Merge m-c to fx-team
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 06 Oct 2014 15:52:04 +0200
changeset 209045 23348a6110bdee34dfd03d8f95f8428ce1ba934c
parent 209044 bc32afc1d6c977472c89e10298d8095c06b95602 (current diff)
parent 208943 e0d9ad4be6065c9d49f4712a4a06250f4b15cbee (diff)
child 209046 e06807007c5cfe939f8ccf727f9a96790f501ca9
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
milestone35.0a1
Merge m-c to fx-team
memory/replace/dmd/test-expected.dmd
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="3a2947df41a480de1457a6dcdbf46ad0af70d8e0">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="83f495a1c12687970f7f2840c2729795c4b88177"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="470826d13ae130a5c3d572d1029e595105485fb0"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="693382a5630079f9debfe55c59f8197d432f47ff"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="9eec93462df6d01fe33a737bb4a185e9ad6cee15"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="8b880805d454664b3eed11d0f053cdeafa1ff06e"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="a1e239a0bb5cd1d69680bf1075883aa9a7bf2429"/>
   <project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="c7931763d41be602407ed9d71e2c0292c6597e00"/>
   <project groups="linux,x86" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="83760d213fb3bec7b4117d266fcfbf6fe2ba14ab"/>
   <project name="device/common" path="device/common" revision="6a2995683de147791e516aae2ccb31fdfbe2ad30"/>
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,23 +14,23 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="84923f1940625c47ff4c1fdf01b10fde3b7d909e">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="83f495a1c12687970f7f2840c2729795c4b88177"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="470826d13ae130a5c3d572d1029e595105485fb0"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="c058843242068d0df7c107e09da31b53d2e08fa6"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="693382a5630079f9debfe55c59f8197d432f47ff"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="9eec93462df6d01fe33a737bb4a185e9ad6cee15"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
   <project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="425f8b5fadf5889834c5acd27d23c9e0b2129c28"/>
   <project name="device/common" path="device/common" revision="42b808b7e93d0619286ae8e59110b176b7732389"/>
   <project name="device/sample" path="device/sample" revision="237bd668d0f114d801a8d6455ef5e02cc3577587"/>
   <project name="platform_external_apriori" path="external/apriori" remote="b2g" revision="11816ad0406744f963537b23d68ed9c2afb412bd"/>
   <project name="platform/external/bluetooth/bluez" path="external/bluetooth/bluez" revision="52a1a862a8bac319652b8f82d9541ba40bfa45ce"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,20 +12,20 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="8986df0f82e15ac2798df0b6c2ee3435400677ac">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="83f495a1c12687970f7f2840c2729795c4b88177"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="470826d13ae130a5c3d572d1029e595105485fb0"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="693382a5630079f9debfe55c59f8197d432f47ff"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="9eec93462df6d01fe33a737bb4a185e9ad6cee15"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="9025e50b9d29b3cabbbb21e1dd94d0d13121a17e"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="b89fda71fcd0fa0cf969310e75be3ea33e048b44"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="2e7d5348f35575870b3c7e567a9a9f6d66f8d6c5"/>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="3a2947df41a480de1457a6dcdbf46ad0af70d8e0">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="83f495a1c12687970f7f2840c2729795c4b88177"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="470826d13ae130a5c3d572d1029e595105485fb0"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="693382a5630079f9debfe55c59f8197d432f47ff"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="9eec93462df6d01fe33a737bb4a185e9ad6cee15"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="f92a936f2aa97526d4593386754bdbf02db07a12"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="6e47ff2790f5656b5b074407829ceecf3e6188c4"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="1950e4760fa14688b83cdbb5acaa1af9f82ef434"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="ac6eb97a37035c09fb5ede0852f0881e9aadf9ad"/>
   <project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="737f591c5f95477148d26602c7be56cbea0cdeb9"/>
   <project groups="linux,x86" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="51da9b1981be481b92a59a826d4d78dc73d0989a"/>
   <project name="device/common" path="device/common" revision="798a3664597e6041985feab9aef42e98d458bc3d"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,23 +14,23 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="84923f1940625c47ff4c1fdf01b10fde3b7d909e">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="83f495a1c12687970f7f2840c2729795c4b88177"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="470826d13ae130a5c3d572d1029e595105485fb0"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="c058843242068d0df7c107e09da31b53d2e08fa6"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="693382a5630079f9debfe55c59f8197d432f47ff"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="9eec93462df6d01fe33a737bb4a185e9ad6cee15"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
   <project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="425f8b5fadf5889834c5acd27d23c9e0b2129c28"/>
   <project name="device/common" path="device/common" revision="42b808b7e93d0619286ae8e59110b176b7732389"/>
   <project name="device/sample" path="device/sample" revision="237bd668d0f114d801a8d6455ef5e02cc3577587"/>
   <project name="platform_external_apriori" path="external/apriori" remote="b2g" revision="11816ad0406744f963537b23d68ed9c2afb412bd"/>
   <project name="platform/external/bluetooth/bluez" path="external/bluetooth/bluez" revision="52a1a862a8bac319652b8f82d9541ba40bfa45ce"/>
--- a/b2g/config/flame-kk/sources.xml
+++ b/b2g/config/flame-kk/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="3a2947df41a480de1457a6dcdbf46ad0af70d8e0">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="83f495a1c12687970f7f2840c2729795c4b88177"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="470826d13ae130a5c3d572d1029e595105485fb0"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="693382a5630079f9debfe55c59f8197d432f47ff"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="9eec93462df6d01fe33a737bb4a185e9ad6cee15"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="8b880805d454664b3eed11d0f053cdeafa1ff06e"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="a1e239a0bb5cd1d69680bf1075883aa9a7bf2429"/>
   <project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="c7931763d41be602407ed9d71e2c0292c6597e00"/>
   <project groups="linux,x86" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="a32003194f707f66a2d8cdb913ed1869f1926c5d"/>
   <project name="device/common" path="device/common" revision="96d4d2006c4fcb2f19a3fa47ab10cb409faa017b"/>
--- a/b2g/config/flame/sources.xml
+++ b/b2g/config/flame/sources.xml
@@ -12,20 +12,20 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="8986df0f82e15ac2798df0b6c2ee3435400677ac">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="83f495a1c12687970f7f2840c2729795c4b88177"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="470826d13ae130a5c3d572d1029e595105485fb0"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="693382a5630079f9debfe55c59f8197d432f47ff"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="9eec93462df6d01fe33a737bb4a185e9ad6cee15"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="e95b4ce22c825da44d14299e1190ea39a5260bde"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="471afab478649078ad7c75ec6b252481a59e19b8"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="8b880805d454664b3eed11d0f053cdeafa1ff06e"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
         "git_revision": "", 
         "remote": "", 
         "branch": ""
     }, 
-    "revision": "665042c75b7d6b0a1f4b8f07f79cd998abce68b0", 
+    "revision": "842fd7448790cfaeaf0bf22164e599c74e9be0e9", 
     "repo_path": "/integration/gaia-central"
 }
--- a/b2g/config/hamachi/sources.xml
+++ b/b2g/config/hamachi/sources.xml
@@ -12,22 +12,22 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="84923f1940625c47ff4c1fdf01b10fde3b7d909e">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="83f495a1c12687970f7f2840c2729795c4b88177"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="470826d13ae130a5c3d572d1029e595105485fb0"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="693382a5630079f9debfe55c59f8197d432f47ff"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="9eec93462df6d01fe33a737bb4a185e9ad6cee15"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="746bc48f34f5060f90801925dcdd964030c1ab6d"/>
   <project name="platform/development" path="development" revision="2460485184bc8535440bb63876d4e63ec1b4770c"/>
   <project name="device/common" path="device/common" revision="0dcc1e03659db33b77392529466f9eb685cdd3c7"/>
   <project name="device/sample" path="device/sample" revision="68b1cb978a20806176123b959cb05d4fa8adaea4"/>
   <project name="platform_external_apriori" path="external/apriori" remote="b2g" revision="11816ad0406744f963537b23d68ed9c2afb412bd"/>
--- a/b2g/config/helix/sources.xml
+++ b/b2g/config/helix/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="84923f1940625c47ff4c1fdf01b10fde3b7d909e">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="83f495a1c12687970f7f2840c2729795c4b88177"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="470826d13ae130a5c3d572d1029e595105485fb0"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -12,20 +12,20 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="8986df0f82e15ac2798df0b6c2ee3435400677ac">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="83f495a1c12687970f7f2840c2729795c4b88177"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="470826d13ae130a5c3d572d1029e595105485fb0"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="693382a5630079f9debfe55c59f8197d432f47ff"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="9eec93462df6d01fe33a737bb4a185e9ad6cee15"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="9025e50b9d29b3cabbbb21e1dd94d0d13121a17e"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="b89fda71fcd0fa0cf969310e75be3ea33e048b44"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="2e7d5348f35575870b3c7e567a9a9f6d66f8d6c5"/>
--- a/b2g/config/wasabi/sources.xml
+++ b/b2g/config/wasabi/sources.xml
@@ -12,22 +12,22 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="84923f1940625c47ff4c1fdf01b10fde3b7d909e">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="83f495a1c12687970f7f2840c2729795c4b88177"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="470826d13ae130a5c3d572d1029e595105485fb0"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="693382a5630079f9debfe55c59f8197d432f47ff"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="9eec93462df6d01fe33a737bb4a185e9ad6cee15"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="cd5dfce80bc3f0139a56b58aca633202ccaee7f8"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="e0a9ac010df3afaa47ba107192c05ac8b5516435"/>
   <project name="platform/development" path="development" revision="a384622f5fcb1d2bebb9102591ff7ae91fe8ed2d"/>
   <project name="device/common" path="device/common" revision="7c65ea240157763b8ded6154a17d3c033167afb7"/>
   <project name="device/sample" path="device/sample" revision="c328f3d4409db801628861baa8d279fb8855892f"/>
--- a/build/Makefile.in
+++ b/build/Makefile.in
@@ -93,25 +93,32 @@ GARBAGE_DIRS += $(_VALGRIND_DIR)
 		$(topsrcdir)/build/valgrind/i386-redhat-linux-gnu.sup \
 		$(topsrcdir)/build/valgrind/x86_64-redhat-linux-gnu.sup \
 		$(NULL)
 
 libs:: $(_VALGRIND_FILES)
 	$(INSTALL) $^ $(_VALGRIND_DIR)
 endif
 
-ifdef ENABLE_TESTS
+ifneq (,$(ENABLE_TESTS)$(MOZ_DMD))
 libs:: $(topsrcdir)/tools/rb/fix_stack_using_bpsyms.py
 	$(INSTALL) $< $(DIST)/bin
 
 ifeq ($(OS_ARCH),Darwin)
 libs:: $(topsrcdir)/tools/rb/fix_macosx_stack.py
 	$(INSTALL) $< $(DIST)/bin
 endif
 
 ifeq ($(OS_ARCH),Linux)
 libs:: $(topsrcdir)/tools/rb/fix_linux_stack.py
 	$(INSTALL) $< $(DIST)/bin
 endif
+endif # ENABLE_TESTS or MOZ_DMD
 
+ifdef ENABLE_TESTS
 GARBAGE += $(srcdir)/automationutils.pyc
+endif # ENABLE_TESTS
 
-endif # ENABLE_TESTS
+ifdef MOZ_DMD
+libs:: $(topsrcdir)/memory/replace/dmd/dmd.py
+	$(INSTALL) $< $(DIST)/bin
+endif
+
--- a/config/external/nss/nss.def
+++ b/config/external/nss/nss.def
@@ -511,20 +511,22 @@ SECKEY_DecodeDERSubjectPublicKeyInfo
 SECKEY_DestroyPrivateKey
 SECKEY_DestroyPrivateKeyList
 SECKEY_DestroyPublicKey
 SECKEY_DestroySubjectPublicKeyInfo
 SECKEY_ECParamsToBasePointOrderLen
 SECKEY_ECParamsToKeySize
 SECKEY_EncodeDERSubjectPublicKeyInfo
 SECKEY_ExtractPublicKey
+SECKEY_GetPublicKeyType
 SECKEY_ImportDERPublicKey
 SECKEY_PublicKeyStrength
 SECKEY_PublicKeyStrengthInBits
 SECKEY_RSAPSSParamsTemplate DATA
+SECKEY_SignatureLen
 SECMIME_DecryptionAllowed
 SECMOD_AddNewModule
 SECMOD_AddNewModuleEx
 SECMOD_CancelWait
 SECMOD_CanDeleteInternalModule
 SECMOD_CloseUserDB
 SECMOD_CreateModule
 SECMOD_DeleteInternalModule
--- a/content/html/content/src/HTMLMediaElement.cpp
+++ b/content/html/content/src/HTMLMediaElement.cpp
@@ -1057,23 +1057,16 @@ void HTMLMediaElement::UpdatePreloadActi
       }
     } else {
       // Use the suggested "missing value default" of "metadata", or the value
       // specified by the media.preload.default, if present.
       nextAction = static_cast<PreloadAction>(preloadDefault);
     }
   }
 
-  if ((mBegun || mIsRunningSelectResource) && nextAction < mPreloadAction) {
-    // We've started a load or are already downloading, and the preload was
-    // changed to a state where we buffer less. We don't support this case,
-    // so don't change the preload behaviour.
-    return;
-  }
-
   mPreloadAction = nextAction;
   if (nextAction == HTMLMediaElement::PRELOAD_ENOUGH) {
     if (mSuspendedForPreloadNone) {
       // Our load was previouly suspended due to the media having preload
       // value "none". The preload value has changed to preload:auto, so
       // resume the load.
       ResumeLoad(PRELOAD_ENOUGH);
     } else {
--- a/content/media/MediaDecoderReader.h
+++ b/content/media/MediaDecoderReader.h
@@ -83,16 +83,19 @@ public:
   // If aSkipToKeyframe is true, the decode should skip ahead to the
   // the next keyframe at or after aTimeThreshold microseconds.
   virtual void RequestVideoData(bool aSkipToNextKeyframe,
                                 int64_t aTimeThreshold);
 
   virtual bool HasAudio() = 0;
   virtual bool HasVideo() = 0;
 
+  // A function that is called before ReadMetadata() call.
+  virtual void PreReadMetadata() {};
+
   // Read header data for all bitstreams in the file. Fills aInfo with
   // the data required to present the media, and optionally fills *aTags
   // with tag metadata from the file.
   // Returns NS_OK on success, or NS_ERROR_FAILURE on failure.
   virtual nsresult ReadMetadata(MediaInfo* aInfo,
                                 MetadataTags** aTags) = 0;
 
   // Moves the decode head to aTime microseconds. aStartTime and aEndTime
--- a/content/media/MediaDecoderStateMachine.cpp
+++ b/content/media/MediaDecoderStateMachine.cpp
@@ -1416,21 +1416,31 @@ void MediaDecoderStateMachine::StartWait
   AssertCurrentThreadInMonitor();
   SetState(DECODER_STATE_WAIT_FOR_RESOURCES);
   DECODER_LOG("StartWaitForResources");
 }
 
 void MediaDecoderStateMachine::NotifyWaitingForResourcesStatusChanged()
 {
   AssertCurrentThreadInMonitor();
-  if (mState != DECODER_STATE_WAIT_FOR_RESOURCES ||
-      mReader->IsWaitingMediaResources()) {
+  DECODER_LOG("NotifyWaitingForResourcesStatusChanged");
+  RefPtr<nsIRunnable> task(
+    NS_NewRunnableMethod(this,
+      &MediaDecoderStateMachine::DoNotifyWaitingForResourcesStatusChanged));
+  mDecodeTaskQueue->Dispatch(task);
+}
+
+void MediaDecoderStateMachine::DoNotifyWaitingForResourcesStatusChanged()
+{
+  NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
+  ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
+  if (mState != DECODER_STATE_WAIT_FOR_RESOURCES) {
     return;
   }
-  DECODER_LOG("NotifyWaitingForResourcesStatusChanged");
+  DECODER_LOG("DoNotifyWaitingForResourcesStatusChanged");
   // The reader is no longer waiting for resources (say a hardware decoder),
   // we can now proceed to decode metadata.
   SetState(DECODER_STATE_DECODING_NONE);
   ScheduleStateMachine();
 }
 
 void MediaDecoderStateMachine::Play()
 {
@@ -1905,16 +1915,18 @@ MediaDecoderStateMachine::CallDecodeMeta
 
 nsresult MediaDecoderStateMachine::DecodeMetadata()
 {
   AssertCurrentThreadInMonitor();
   NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
   MOZ_ASSERT(mState == DECODER_STATE_DECODING_METADATA);
   DECODER_LOG("Decoding Media Headers");
 
+  mReader->PreReadMetadata();
+
   if (mReader->IsWaitingMediaResources()) {
     StartWaitForResources();
     return NS_OK;
   }
 
   nsresult res;
   MediaInfo info;
   {
--- a/content/media/MediaDecoderStateMachine.h
+++ b/content/media/MediaDecoderStateMachine.h
@@ -322,19 +322,19 @@ public:
   bool IsShutdown();
 
   void QueueMetadata(int64_t aPublishTime, MediaInfo* aInfo, MetadataTags* aTags);
 
   // Returns true if we're currently playing. The decoder monitor must
   // be held.
   bool IsPlaying();
 
+  // Dispatch DoNotifyWaitingForResourcesStatusChanged task to mDecodeTaskQueue.
   // Called when the reader may have acquired the hardware resources required
-  // to begin decoding. The state machine may move into DECODING_METADATA if
-  // appropriate. The decoder monitor must be held while calling this.
+  // to begin decoding. The decoder monitor must be held while calling this.
   void NotifyWaitingForResourcesStatusChanged();
 
   // Notifies the state machine that should minimize the number of samples
   // decoded we preroll, until playback starts. The first time playback starts
   // the state machine is free to return to prerolling normally. Note
   // "prerolling" in this context refers to when we decode and buffer decoded
   // samples in advance of when they're needed for playback.
   void SetMinimizePrerollUntilPlaybackStarts();
@@ -634,16 +634,20 @@ protected:
 
   // Called by the AudioSink to signal that all outstanding work is complete
   // and the sink is shutting down.
   void OnAudioSinkComplete();
 
   // Called by the AudioSink to signal errors.
   void OnAudioSinkError();
 
+  // The state machine may move into DECODING_METADATA if we are in
+  // DECODER_STATE_WAIT_FOR_RESOURCES.
+  void DoNotifyWaitingForResourcesStatusChanged();
+
   // The decoder object that created this state machine. The state machine
   // holds a strong reference to the decoder to ensure that the decoder stays
   // alive once media element has started the decoder shutdown process, and has
   // dropped its reference to the decoder. This enables the state machine to
   // keep using the decoder's monitor until the state machine has finished
   // shutting down, without fear of the monitor being destroyed. After
   // shutting down, the state machine will then release this reference,
   // causing the decoder to be destroyed. This is accessed on the decode,
--- a/content/media/MediaResource.cpp
+++ b/content/media/MediaResource.cpp
@@ -78,17 +78,16 @@ ChannelMediaResource::ChannelMediaResour
                                            nsIURI* aURI,
                                            const nsACString& aContentType)
   : BaseMediaResource(aDecoder, aChannel, aURI, aContentType),
     mOffset(0), mSuspendCount(0),
     mReopenOnError(false), mIgnoreClose(false),
     mCacheStream(MOZ_THIS_IN_INITIALIZER_LIST()),
     mLock("ChannelMediaResource.mLock"),
     mIgnoreResume(false),
-    mSeekingForMetadata(false),
     mIsTransportSeekable(true)
 {
 #ifdef PR_LOGGING
   if (!gMediaResourceLog) {
     gMediaResourceLog = PR_NewLogModule("MediaResource");
   }
 #endif
 }
@@ -353,21 +352,17 @@ ChannelMediaResource::OnStartRequest(nsI
 
   {
     MutexAutoLock lock(mLock);
     mIsTransportSeekable = seekable;
     mChannelStatistics->Start();
   }
 
   mReopenOnError = false;
-  // If we are seeking to get metadata, because we are playing an OGG file,
-  // ignore if the channel gets closed without us suspending it explicitly. We
-  // don't want to tell the element that the download has finished whereas we
-  // just happended to have reached the end of the media while seeking.
-  mIgnoreClose = mSeekingForMetadata;
+  mIgnoreClose = false;
 
   if (mSuspendCount > 0) {
     // Re-suspend the channel if it needs to be suspended
     // No need to call PossiblySuspend here since the channel is
     // definitely in the right state for us in OnStartRequest.
     mChannel->Suspend();
     mIgnoreResume = false;
   }
@@ -803,26 +798,16 @@ nsresult ChannelMediaResource::Seek(int3
 {
   NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
 
   CMLOG("Seek requested for aOffset [%lld] for decoder [%p]",
         aOffset, mDecoder);
   return mCacheStream.Seek(aWhence, aOffset);
 }
 
-void ChannelMediaResource::StartSeekingForMetadata()
-{
-  mSeekingForMetadata = true;
-}
-
-void ChannelMediaResource::EndSeekingForMetadata()
-{
-  mSeekingForMetadata = false;
-}
-
 int64_t ChannelMediaResource::Tell()
 {
   NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
 
   return mCacheStream.Tell();
 }
 
 nsresult ChannelMediaResource::GetCachedRanges(nsTArray<MediaByteRange>& aRanges)
@@ -1226,18 +1211,16 @@ public:
 
   // Other thread
   virtual void     SetReadMode(MediaCacheStream::ReadMode aMode) {}
   virtual void     SetPlaybackRate(uint32_t aBytesPerSecond) {}
   virtual nsresult Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes);
   virtual nsresult ReadAt(int64_t aOffset, char* aBuffer,
                           uint32_t aCount, uint32_t* aBytes);
   virtual nsresult Seek(int32_t aWhence, int64_t aOffset);
-  virtual void     StartSeekingForMetadata() {};
-  virtual void     EndSeekingForMetadata() {};
   virtual int64_t  Tell();
 
   // Any thread
   virtual void    Pin() {}
   virtual void    Unpin() {}
   virtual double  GetDownloadRate(bool* aIsReliable)
   {
     // The data's all already here
--- a/content/media/MediaResource.h
+++ b/content/media/MediaResource.h
@@ -306,18 +306,16 @@ public:
   //
   // The default strategy does not do any seeking - the only issue is
   // a blocked read which it handles by causing the listener to close
   // the pipe, as per the http case.
   //
   // The file strategy doesn't block for any great length of time so
   // is fine for a no-op cancel.
   virtual nsresult Seek(int32_t aWhence, int64_t aOffset) = 0;
-  virtual void StartSeekingForMetadata() = 0;
-  virtual void EndSeekingForMetadata() = 0;
   // Report the current offset in bytes from the start of the stream.
   virtual int64_t Tell() = 0;
   // Moves any existing channel loads into the background, so that they don't
   // block the load event. Any new loads initiated (for example to seek)
   // will also be in the background.
   virtual void MoveLoadsToBackground() {}
   // Ensures that the value returned by IsSuspendedByCache below is up to date
   // (i.e. the cache has examined this stream at least once).
@@ -583,18 +581,16 @@ public:
 
   // Other thread
   virtual void     SetReadMode(MediaCacheStream::ReadMode aMode);
   virtual void     SetPlaybackRate(uint32_t aBytesPerSecond);
   virtual nsresult Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes);
   virtual nsresult ReadAt(int64_t offset, char* aBuffer,
                           uint32_t aCount, uint32_t* aBytes);
   virtual nsresult Seek(int32_t aWhence, int64_t aOffset);
-  virtual void     StartSeekingForMetadata();
-  virtual void     EndSeekingForMetadata();
   virtual int64_t  Tell();
 
   // Any thread
   virtual void    Pin();
   virtual void    Unpin();
   virtual double  GetDownloadRate(bool* aIsReliable);
   virtual int64_t GetLength();
   virtual int64_t GetNextCachedData(int64_t aOffset);
@@ -709,19 +705,16 @@ protected:
   Mutex               mLock;
   nsRefPtr<MediaChannelStatistics> mChannelStatistics;
 
   // True if we couldn't suspend the stream and we therefore don't want
   // to resume later. This is usually due to the channel not being in the
   // isPending state at the time of the suspend request.
   bool mIgnoreResume;
 
-  // True if we are seeking to get the real duration of the file.
-  bool mSeekingForMetadata;
-
   // Start and end offset of the bytes to be requested.
   MediaByteRange mByteRange;
 
   // True if the stream can seek into unbuffered ranged, i.e. if the
   // connection supports byte range requests.
   bool mIsTransportSeekable;
 };
 
--- a/content/media/RtspMediaResource.h
+++ b/content/media/RtspMediaResource.h
@@ -126,20 +126,16 @@ public:
   MOZ_OVERRIDE {
     return NS_OK;
   }
   // dummy
   virtual nsresult Seek(int32_t aWhence, int64_t aOffset) MOZ_OVERRIDE {
     return NS_OK;
   }
   // dummy
-  virtual void     StartSeekingForMetadata() MOZ_OVERRIDE {}
-  // dummy
-  virtual void     EndSeekingForMetadata() MOZ_OVERRIDE {}
-  // dummy
   virtual int64_t  Tell() MOZ_OVERRIDE { return 0; }
 
   // Any thread
   virtual void    Pin() MOZ_OVERRIDE {}
   virtual void    Unpin() MOZ_OVERRIDE {}
 
   virtual bool    IsSuspendedByCache() MOZ_OVERRIDE { return mIsSuspend; }
 
--- a/content/media/mediasource/MediaSourceResource.h
+++ b/content/media/mediasource/MediaSourceResource.h
@@ -34,18 +34,16 @@ public:
   virtual already_AddRefed<nsIPrincipal> GetCurrentPrincipal() MOZ_OVERRIDE { UNIMPLEMENTED(); return nullptr; }
   virtual bool CanClone() MOZ_OVERRIDE { UNIMPLEMENTED(); return false; }
   virtual already_AddRefed<MediaResource> CloneData(MediaDecoder* aDecoder) MOZ_OVERRIDE { UNIMPLEMENTED(); return nullptr; }
   virtual void SetReadMode(MediaCacheStream::ReadMode aMode) MOZ_OVERRIDE { UNIMPLEMENTED(); }
   virtual void SetPlaybackRate(uint32_t aBytesPerSecond) MOZ_OVERRIDE  { UNIMPLEMENTED(); }
   virtual nsresult Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes) MOZ_OVERRIDE { UNIMPLEMENTED(); return NS_ERROR_FAILURE; }
   virtual nsresult ReadAt(int64_t aOffset, char* aBuffer, uint32_t aCount, uint32_t* aBytes) MOZ_OVERRIDE { UNIMPLEMENTED(); return NS_ERROR_FAILURE; }
   virtual nsresult Seek(int32_t aWhence, int64_t aOffset) MOZ_OVERRIDE { UNIMPLEMENTED(); return NS_ERROR_FAILURE; }
-  virtual void StartSeekingForMetadata() MOZ_OVERRIDE { UNIMPLEMENTED(); }
-  virtual void EndSeekingForMetadata() MOZ_OVERRIDE { UNIMPLEMENTED(); }
   virtual int64_t Tell() MOZ_OVERRIDE { UNIMPLEMENTED(); return -1; }
   virtual void Pin() MOZ_OVERRIDE { UNIMPLEMENTED(); }
   virtual void Unpin() MOZ_OVERRIDE { UNIMPLEMENTED(); }
   virtual double GetDownloadRate(bool* aIsReliable) MOZ_OVERRIDE { UNIMPLEMENTED(); *aIsReliable = false; return 0; }
   virtual int64_t GetLength() MOZ_OVERRIDE { UNIMPLEMENTED(); return -1; }
   virtual int64_t GetNextCachedData(int64_t aOffset) MOZ_OVERRIDE { UNIMPLEMENTED(); return -1; }
   virtual int64_t GetCachedDataEnd(int64_t aOffset) MOZ_OVERRIDE { UNIMPLEMENTED(); return -1; }
   virtual bool IsDataCachedToEndOfResource(int64_t aOffset) MOZ_OVERRIDE { UNIMPLEMENTED(); return false; }
--- a/content/media/mediasource/SourceBufferResource.h
+++ b/content/media/mediasource/SourceBufferResource.h
@@ -52,18 +52,16 @@ public:
   virtual void Resume() MOZ_OVERRIDE { UNIMPLEMENTED(); }
   virtual already_AddRefed<nsIPrincipal> GetCurrentPrincipal() MOZ_OVERRIDE { UNIMPLEMENTED(); return nullptr; }
   virtual already_AddRefed<MediaResource> CloneData(MediaDecoder* aDecoder) MOZ_OVERRIDE { UNIMPLEMENTED(); return nullptr; }
   virtual void SetReadMode(MediaCacheStream::ReadMode aMode) MOZ_OVERRIDE { UNIMPLEMENTED(); }
   virtual void SetPlaybackRate(uint32_t aBytesPerSecond) MOZ_OVERRIDE { UNIMPLEMENTED(); }
   virtual nsresult Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes) MOZ_OVERRIDE;
   virtual nsresult ReadAt(int64_t aOffset, char* aBuffer, uint32_t aCount, uint32_t* aBytes) MOZ_OVERRIDE;
   virtual nsresult Seek(int32_t aWhence, int64_t aOffset) MOZ_OVERRIDE;
-  virtual void StartSeekingForMetadata() MOZ_OVERRIDE { UNIMPLEMENTED(); }
-  virtual void EndSeekingForMetadata() MOZ_OVERRIDE { UNIMPLEMENTED(); }
   virtual int64_t Tell() MOZ_OVERRIDE { return mOffset; }
   virtual void Pin() MOZ_OVERRIDE { UNIMPLEMENTED(); }
   virtual void Unpin() MOZ_OVERRIDE { UNIMPLEMENTED(); }
   virtual double GetDownloadRate(bool* aIsReliable) MOZ_OVERRIDE { UNIMPLEMENTED(); *aIsReliable = false; return 0; }
   virtual int64_t GetLength() MOZ_OVERRIDE { return mInputBuffer.GetLength(); }
   virtual int64_t GetNextCachedData(int64_t aOffset) MOZ_OVERRIDE {
     ReentrantMonitorAutoEnter mon(mMonitor);
     MOZ_ASSERT(aOffset >= 0);
--- a/content/media/ogg/OggReader.cpp
+++ b/content/media/ogg/OggReader.cpp
@@ -484,31 +484,29 @@ nsresult OggReader::ReadMetadata(MediaIn
     MediaResource* resource = mDecoder->GetResource();
     if (mDecoder->GetMediaDuration() == -1 &&
         !mDecoder->IsShutdown() &&
         resource->GetLength() >= 0 &&
         mDecoder->IsMediaSeekable())
     {
       // We didn't get a duration from the index or a Content-Duration header.
       // Seek to the end of file to find the end time.
-      mDecoder->GetResource()->StartSeekingForMetadata();
       int64_t length = resource->GetLength();
 
       NS_ASSERTION(length > 0, "Must have a content length to get end time");
 
       int64_t endTime = 0;
       {
         ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
         endTime = RangeEndTime(length);
       }
       if (endTime != -1) {
         mDecoder->SetMediaEndTime(endTime);
         LOG(PR_LOG_DEBUG, ("Got Ogg duration from seeking to end %lld", endTime));
       }
-      mDecoder->GetResource()->EndSeekingForMetadata();
     }
   } else {
     return NS_ERROR_FAILURE;
   }
   *aInfo = mInfo;
 
   return NS_OK;
 }
--- a/content/media/omx/MediaCodecReader.cpp
+++ b/content/media/omx/MediaCodecReader.cpp
@@ -280,16 +280,17 @@ MediaCodecReader::ProcessCachedDataTask:
 MediaCodecReader::MediaCodecReader(AbstractMediaDecoder* aDecoder)
   : MediaOmxCommonReader(aDecoder)
   , mColorConverterBufferSize(0)
   , mExtractor(nullptr)
   , mParserMonitor("MediaCodecReader::mParserMonitor")
   , mParseDataFromCache(true)
   , mNextParserPosition(INT64_C(0))
   , mParsedDataLength(INT64_C(0))
+  , mIsWaitingResources(false)
 {
   mHandler = new MessageHandler(this);
   mVideoListener = new VideoResourceListener(this);
 }
 
 MediaCodecReader::~MediaCodecReader()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread.");
@@ -299,17 +300,24 @@ nsresult
 MediaCodecReader::Init(MediaDecoderReader* aCloneDonor)
 {
   return NS_OK;
 }
 
 bool
 MediaCodecReader::IsWaitingMediaResources()
 {
-  return mVideoTrack.mCodec != nullptr && !mVideoTrack.mCodec->allocated();
+  return mIsWaitingResources;
+}
+
+void
+MediaCodecReader::UpdateIsWaitingMediaResources()
+{
+  mIsWaitingResources = (mVideoTrack.mCodec != nullptr) &&
+                        (!mVideoTrack.mCodec->allocated());
 }
 
 bool
 MediaCodecReader::IsDormantNeeded()
 {
   return mVideoTrack.mSource != nullptr;
 }
 
@@ -642,30 +650,42 @@ MediaCodecReader::ParseDataSegment(const
     MOZ_ASSERT(mDecoder);
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
     mDecoder->UpdateEstimatedMediaDuration(duration);
   }
 
   return true;
 }
 
+void
+MediaCodecReader::PreReadMetadata()
+{
+  UpdateIsWaitingMediaResources();
+}
+
+
 nsresult
 MediaCodecReader::ReadMetadata(MediaInfo* aInfo,
                                MetadataTags** aTags)
 {
   MOZ_ASSERT(mDecoder->OnDecodeThread(), "Should be on decode thread.");
 
   if (!ReallocateResources()) {
     return NS_ERROR_FAILURE;
   }
 
   if (!TriggerIncrementalParser()) {
     return NS_ERROR_FAILURE;
   }
 
+  // Bug 1050667, both MediaDecoderStateMachine and MediaCodecReader
+  // relies on IsWaitingMediaResources() function. And the waiting state will be
+  // changed by binder thread, so we store the waiting state in a cache value to
+  // make them in the same waiting state.
+  UpdateIsWaitingMediaResources();
   if (IsWaitingMediaResources()) {
     return NS_OK;
   }
 
   // TODO: start streaming
 
   if (!UpdateDuration()) {
     return NS_ERROR_FAILURE;
--- a/content/media/omx/MediaCodecReader.h
+++ b/content/media/omx/MediaCodecReader.h
@@ -73,16 +73,17 @@ public:
                                 int64_t aTimeThreshold) MOZ_OVERRIDE;
 
   // Disptach a DecodeAduioDataTask to decode video data.
   virtual void RequestAudioData() MOZ_OVERRIDE;
 
   virtual bool HasAudio();
   virtual bool HasVideo();
 
+  virtual void PreReadMetadata() MOZ_OVERRIDE;
   // Read header data for all bitstreams in the file. Fills aInfo with
   // the data required to present the media, and optionally fills *aTags
   // with tag metadata from the file.
   // Returns NS_OK on success, or NS_ERROR_FAILURE on failure.
   virtual nsresult ReadMetadata(MediaInfo* aInfo,
                                 MetadataTags** aTags);
 
   // Moves the decode head to aTime microseconds. aStartTime and aEndTime
@@ -147,17 +148,24 @@ protected:
 
   // Receive a notify from ResourceListener.
   // Called on Binder thread.
   virtual void codecReserved(Track& aTrack);
   virtual void codecCanceled(Track& aTrack);
 
   virtual bool CreateExtractor();
 
+  // Check the underlying HW resource is available and store the result in
+  // mIsWaitingResources.
+  void UpdateIsWaitingMediaResources();
+
   android::sp<android::MediaExtractor> mExtractor;
+  // A cache value updated by UpdateIsWaitingMediaResources(), makes the
+  // "waiting resources state" is synchronous to StateMachine.
+  bool mIsWaitingResources;
 
 private:
   // An intermediary class that can be managed by android::sp<T>.
   // Redirect onMessageReceived() to MediaCodecReader.
   class MessageHandler : public android::AHandler
   {
   public:
     MessageHandler(MediaCodecReader* aReader);
--- a/content/media/omx/MediaOmxReader.cpp
+++ b/content/media/omx/MediaOmxReader.cpp
@@ -142,16 +142,17 @@ MediaOmxReader::MediaOmxReader(AbstractM
   , mHasVideo(false)
   , mHasAudio(false)
   , mVideoSeekTimeUs(-1)
   , mAudioSeekTimeUs(-1)
   , mSkipCount(0)
   , mUseParserDuration(false)
   , mLastParserDuration(-1)
   , mIsShutdown(false)
+  , mIsWaitingResources(false)
 {
 #ifdef PR_LOGGING
   if (!gMediaDecoderLog) {
     gMediaDecoderLog = PR_NewLogModule("MediaDecoder");
   }
 #endif
 
   mAudioChannel = dom::AudioChannelService::GetDefaultAudioChannel();
@@ -183,20 +184,26 @@ void MediaOmxReader::Shutdown()
   ReleaseMediaResources();
   nsCOMPtr<nsIRunnable> event =
     NS_NewRunnableMethod(this, &MediaOmxReader::ReleaseDecoder);
   NS_DispatchToMainThread(event);
 }
 
 bool MediaOmxReader::IsWaitingMediaResources()
 {
-  if (!mOmxDecoder.get()) {
-    return false;
+  return mIsWaitingResources;
+}
+
+void MediaOmxReader::UpdateIsWaitingMediaResources()
+{
+  if (mOmxDecoder.get()) {
+    mIsWaitingResources = mOmxDecoder->IsWaitingMediaResources();
+  } else {
+    mIsWaitingResources = false;
   }
-  return mOmxDecoder->IsWaitingMediaResources();
 }
 
 bool MediaOmxReader::IsDormantNeeded()
 {
   if (!mOmxDecoder.get()) {
     return false;
   }
   return mOmxDecoder->IsDormantNeeded();
@@ -233,16 +240,21 @@ nsresult MediaOmxReader::InitOmxDecoder(
     mOmxDecoder = new OmxDecoder(mDecoder->GetResource(), mDecoder);
     if (!mOmxDecoder->Init(mExtractor)) {
       return NS_ERROR_FAILURE;
     }
   }
   return NS_OK;
 }
 
+void MediaOmxReader::PreReadMetadata()
+{
+  UpdateIsWaitingMediaResources();
+}
+
 nsresult MediaOmxReader::ReadMetadata(MediaInfo* aInfo,
                                       MetadataTags** aTags)
 {
   NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
   EnsureActive();
 
   *aTags = nullptr;
 
@@ -256,23 +268,31 @@ nsresult MediaOmxReader::ReadMetadata(Me
   if (isMP3) {
     // When read sdcard's file on b2g platform at constructor,
     // the mDecoder->GetResource()->GetLength() would return -1.
     // Delay set the total duration on this function.
     mMP3FrameParser.SetLength(mDecoder->GetResource()->GetLength());
     ProcessCachedData(0, true);
   }
 
-  if (!mOmxDecoder->TryLoad()) {
+  if (!mOmxDecoder->AllocateMediaResources()) {
     return NS_ERROR_FAILURE;
   }
-
+  // Bug 1050667, both MediaDecoderStateMachine and MediaOmxReader
+  // relies on IsWaitingMediaResources() function. And the waiting state will be
+  // changed by binder thread, so we store the waiting state in a cache value to
+  // make them in consistent state.
+  UpdateIsWaitingMediaResources();
   if (IsWaitingMediaResources()) {
     return NS_OK;
   }
+  // After resources are available, set the metadata.
+  if (!mOmxDecoder->EnsureMetadata()) {
+    return NS_ERROR_FAILURE;
+  }
 
   if (isMP3 && mMP3FrameParser.IsMP3()) {
     int64_t duration = mMP3FrameParser.GetDuration();
     // The MP3FrameParser may reported a duration;
     // return -1 if no frame has been parsed.
     if (duration >= 0) {
       ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
       mUseParserDuration = true;
--- a/content/media/omx/MediaOmxReader.h
+++ b/content/media/omx/MediaOmxReader.h
@@ -42,26 +42,36 @@ class MediaOmxReader : public MediaOmxCo
   int64_t mLastParserDuration;
   int32_t mSkipCount;
   bool mUseParserDuration;
   bool mIsShutdown;
 protected:
   android::sp<android::OmxDecoder> mOmxDecoder;
   android::sp<android::MediaExtractor> mExtractor;
   MP3FrameParser mMP3FrameParser;
+
+  // A cache value updated by UpdateIsWaitingMediaResources(), makes the
+  // "waiting resources state" is synchronous to StateMachine.
+  bool mIsWaitingResources;
+
   // Called by ReadMetadata() during MediaDecoderStateMachine::DecodeMetadata()
   // on decode thread. It create and initialize the OMX decoder including
   // setting up custom extractor. The extractor provide the essential
   // information used for creating OMX decoder such as video/audio codec.
   virtual nsresult InitOmxDecoder();
 
   // Called inside DecodeVideoFrame, DecodeAudioData, ReadMetadata and Seek
   // to activate the decoder automatically.
   virtual void EnsureActive();
 
+  // Check the underlying HW resources are available and store the result in
+  // mIsWaitingResources. The result might be changed by binder thread,
+  // Can only called by ReadMetadata.
+  void UpdateIsWaitingMediaResources();
+
 public:
   MediaOmxReader(AbstractMediaDecoder* aDecoder);
   ~MediaOmxReader();
 
   virtual nsresult Init(MediaDecoderReader* aCloneDonor);
 
   virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset);
 
@@ -74,21 +84,23 @@ public:
     return mHasAudio;
   }
 
   virtual bool HasVideo()
   {
     return mHasVideo;
   }
 
-  virtual bool IsWaitingMediaResources();
+  // Return mIsWaitingResources.
+  virtual bool IsWaitingMediaResources() MOZ_OVERRIDE;
 
   virtual bool IsDormantNeeded();
   virtual void ReleaseMediaResources();
 
+  virtual void PreReadMetadata() MOZ_OVERRIDE;
   virtual nsresult ReadMetadata(MediaInfo* aInfo,
                                 MetadataTags** aTags);
   virtual nsresult Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime, int64_t aCurrentTime);
 
   virtual bool IsMediaSeekable() MOZ_OVERRIDE;
 
   virtual void SetIdle() MOZ_OVERRIDE;
 
--- a/content/media/omx/OmxDecoder.cpp
+++ b/content/media/omx/OmxDecoder.cpp
@@ -157,29 +157,17 @@ bool OmxDecoder::Init(sp<MediaExtractor>
     // mAudioTrack is be used by OMXCodec. For offloaded audio track, using same
     // object gives undetermined behavior. So get a new track
     mAudioOffloadTrack = extractor->getTrack(audioTrackIndex);
 #endif
   }
   return true;
 }
 
-bool OmxDecoder::TryLoad() {
-
-  if (!AllocateMediaResources()) {
-    return false;
-  }
-
-  //check if video is waiting resources
-  if (mVideoSource.get()) {
-    if (mVideoSource->IsWaitingResources()) {
-      return true;
-    }
-  }
-
+bool OmxDecoder::EnsureMetadata() {
   // calculate duration
   int64_t totalDurationUs = 0;
   int64_t durationUs = 0;
   if (mVideoTrack.get() && mVideoTrack->getFormat()->findInt64(kKeyDuration, &durationUs)) {
     if (durationUs > totalDurationUs)
       totalDurationUs = durationUs;
   }
   if (mAudioTrack.get()) {
--- a/content/media/omx/OmxDecoder.h
+++ b/content/media/omx/OmxDecoder.h
@@ -147,19 +147,26 @@ public:
   // MediaExtractor::getTrackMetaData().
   // In general cases, the extractor is created by a sp<DataSource> which
   // connect to a MediaResource like ChannelMediaResource.
   // Data is read from the MediaResource to create a suitable extractor which
   // extracts data from a container.
   // Note: RTSP requires a custom extractor because it doesn't have a container.
   bool Init(sp<MediaExtractor>& extractor);
 
-  bool TryLoad();
   bool IsDormantNeeded();
+
+  // Called after resources(video/audio codec) are allocated, set the
+  // mDurationUs and video/audio metadata.
+  bool EnsureMetadata();
+
+  // Only called by MediaOmxDecoder, do not call this function arbitrarily.
+  // See bug 1050667.
   bool IsWaitingMediaResources();
+
   bool AllocateMediaResources();
   void ReleaseMediaResources();
   bool SetVideoFormat();
   bool SetAudioFormat();
 
   void ReleaseDecoder();
 
   void GetDuration(int64_t *durationUs) {
--- a/content/media/test/test_preload_actions.html
+++ b/content/media/test/test_preload_actions.html
@@ -381,44 +381,16 @@ var tests = [
       v.addEventListener("loadedmetadata", function(e){v._gotLoadedMetaData = true;}, false);
       v.addEventListener("loadeddata", this.loadeddata, false);
       v.src = test.name; // Causes implicit load.
       document.body.appendChild(v);
     },
 
     name: "test11",
   },
-  /*{
-    // 12. Change preload value from auto to metadata after load started,
-    // should still do full load, should not halt after metadata only.
-    // disable this test since the spec is no longer found in the document
-    // http://dev.w3.org/html5/spec-preview/media-elements.html
-    canplaythrough:
-    function(e) {
-      var v = e.target;
-      is(v._gotLoadedMetaData, true, "(12) Must get loadedmetadata.");
-      is(v._gotLoadStart, true, "(12) Must get loadstart.");
-      maybeFinish(v, 12);
-    },
-
-    setup:
-    function(v) {
-      v._gotLoadStart = false;
-      v._gotLoadedMetaData = false;
-      v.preload = "auto";
-      v.addEventListener("loadstart", function(e){v._gotLoadStart = true;}, false);
-      v.addEventListener("loadedmetadata", function(e){v._gotLoadedMetaData = true;}, false);
-      v.addEventListener("canplaythrough", this.canplaythrough, false);
-      v.src = test.name; // Causes implicit load.
-      document.body.appendChild(v);
-      v.preload = "metadata";
-    },
-
-    name: "test12",
-  },*/
   {
     // 13. Change preload value from auto to none after specifying a src
     // should load according to preload none, no buffering should have taken place
     suspend:
     function(e) {
       var v = e.target;
       is(v._gotLoadStart, true, "(13) Must get loadstart.");
       is(v._gotLoadedMetaData, false, "(13) Must not get loadedmetadata.");
@@ -563,38 +535,46 @@ var tests = [
       v.play(); // Should cause preload:none to be overridden.
     },
 
     name: "test18",
   },
   {
     // 19. Set preload='auto' on first video source then switching preload='none' and swapping the video source to another.
     // The second video should not start playing as it's preload state has been changed to 'none' from 'auto'
-    loadedmetadata: function(e) {
-      var v = e.target;
-      is(v.preload === "auto", true, "(19) preload is initially auto");
-      setTimeout(function() {
+    setup:
+    function(v) {
+      v.preload = "auto";
+      v.src = test.name;
+      // add a listener for when the video has loaded, so we know preload auto has worked
+      v.onloadedmetadata = function() {
+        is(v.preload, "auto", "(19) preload is initially auto");
         // set preload state to none and switch video sources
         v.preload="none";
         v.src = test.name + "?asdf";
-        setTimeout(function() {
-          is(v.readyState === 0, true, "(19) no buffering has taken place");
-          maybeFinish(v, 19);
-        }, 2000);
-      }, 2000);
 
-    },
+        v.onloadedmetadata = function() {
+          ok(false, "(19) 'loadedmetadata' shouldn't fire when preload is none");
+        }
 
-    setup:
-    function(v) {
-      var that = this;
-      v.preload = "auto";
-      v.src = test.name;
-      // add a listener for when the video has loaded, so we know preload auto has worked
-      v.addEventListener( "loadedmetadata", this.loadedmetadata, false);
+        var ontimeout = function() {
+          v.removeEventListener("suspend", onsuspend, false);
+          ok(false, "(19) 'suspend' should've fired");
+          maybeFinish(v, 19);
+        }
+        var cancel = setTimeout(ontimeout, 10000);
+
+        var onsuspend = function() {
+          v.removeEventListener("suspend", onsuspend, false);
+          clearTimeout(cancel);
+          is(v.readyState, 0, "(19) no buffering has taken place");
+          maybeFinish(v, 19);
+        }
+        v.addEventListener("suspend", onsuspend, false);
+      }
       document.body.appendChild(v);
     },
 
     name: "test19",
   }
 ];
 
 var iterationCount = 0;
--- a/content/media/webrtc/MediaEngineWebRTCVideo.cpp
+++ b/content/media/webrtc/MediaEngineWebRTCVideo.cpp
@@ -969,16 +969,18 @@ MediaEngineWebRTCVideoSource::OnTakePict
       new GenerateBlobRunnable(mPhotoCallbacks, aData, aLength, aMimeType));
   }
 }
 
 uint32_t
 MediaEngineWebRTCVideoSource::ConvertPixelFormatToFOURCC(int aFormat)
 {
   switch (aFormat) {
+  case HAL_PIXEL_FORMAT_RGBA_8888:
+    return libyuv::FOURCC_BGRA;
   case HAL_PIXEL_FORMAT_YCrCb_420_SP:
     return libyuv::FOURCC_NV21;
   case HAL_PIXEL_FORMAT_YV12:
     return libyuv::FOURCC_YV12;
   default: {
     LOG((" xxxxx Unknown pixel format %d", aFormat));
     MOZ_ASSERT(false, "Unknown pixel format.");
     return libyuv::FOURCC_ANY;
--- a/content/media/webspeech/recognition/SpeechGrammarList.cpp
+++ b/content/media/webspeech/recognition/SpeechGrammarList.cpp
@@ -3,43 +3,56 @@
 /* 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 "SpeechGrammarList.h"
 
 #include "mozilla/dom/SpeechGrammarListBinding.h"
 #include "mozilla/ErrorResult.h"
+#include "nsCOMPtr.h"
+#include "nsXPCOMStrings.h"
+#include "SpeechRecognition.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(SpeechGrammarList, mParent)
 NS_IMPL_CYCLE_COLLECTING_ADDREF(SpeechGrammarList)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(SpeechGrammarList)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SpeechGrammarList)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
-SpeechGrammarList::SpeechGrammarList(nsISupports* aParent)
+SpeechGrammarList::SpeechGrammarList(nsISupports* aParent, nsISpeechRecognitionService* aRecognitionService)
   : mParent(aParent)
 {
+  this->mRecognitionService = aRecognitionService;
   SetIsDOMBinding();
 }
 
 SpeechGrammarList::~SpeechGrammarList()
 {
 }
 
-SpeechGrammarList*
+already_AddRefed<SpeechGrammarList>
 SpeechGrammarList::Constructor(const GlobalObject& aGlobal,
                                ErrorResult& aRv)
 {
-  return new SpeechGrammarList(aGlobal.GetAsSupports());
+  nsCOMPtr<nsISpeechRecognitionService> recognitionService;
+  recognitionService = GetSpeechRecognitionService();
+  if (!recognitionService) {
+    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
+    return nullptr;
+  } else {
+    nsRefPtr<SpeechGrammarList> speechGrammarList =
+      new SpeechGrammarList(aGlobal.GetAsSupports(), recognitionService);
+    return speechGrammarList.forget();
+  }
 }
 
 JSObject*
 SpeechGrammarList::WrapObject(JSContext* aCx)
 {
   return SpeechGrammarListBinding::Wrap(aCx, this);
 }
 
@@ -71,17 +84,17 @@ SpeechGrammarList::AddFromURI(const nsAS
   return;
 }
 
 void
 SpeechGrammarList::AddFromString(const nsAString& aString,
                                  const Optional<float>& aWeight,
                                  ErrorResult& aRv)
 {
-  aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
+  mRecognitionService->ValidateAndSetGrammarList(this, nullptr);
   return;
 }
 
 already_AddRefed<SpeechGrammar>
 SpeechGrammarList::IndexedGetter(uint32_t aIndex, bool& aPresent,
                                  ErrorResult& aRv)
 {
   aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
--- a/content/media/webspeech/recognition/SpeechGrammarList.h
+++ b/content/media/webspeech/recognition/SpeechGrammarList.h
@@ -6,16 +6,17 @@
 
 #ifndef mozilla_dom_SpeechGrammarList_h
 #define mozilla_dom_SpeechGrammarList_h
 
 #include "mozilla/Attributes.h"
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsWrapperCache.h"
+#include "nsISpeechRecognitionService.h"
 
 struct JSContext;
 
 namespace mozilla {
 
 class ErrorResult;
 
 namespace dom {
@@ -23,38 +24,39 @@ namespace dom {
 class GlobalObject;
 class SpeechGrammar;
 template<typename> class Optional;
 
 class SpeechGrammarList MOZ_FINAL : public nsISupports,
                                     public nsWrapperCache
 {
 public:
-  explicit SpeechGrammarList(nsISupports* aParent);
+  explicit SpeechGrammarList(nsISupports* aParent, nsISpeechRecognitionService* aRecognitionService);
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(SpeechGrammarList)
 
-  SpeechGrammarList* Constructor(const GlobalObject& aGlobal,
-                                 ErrorResult& aRv);
+  static already_AddRefed<SpeechGrammarList> Constructor(const GlobalObject& aGlobal, ErrorResult& aRv);
 
   nsISupports* GetParentObject() const;
 
   virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
   uint32_t Length() const;
 
   already_AddRefed<SpeechGrammar> Item(uint32_t aIndex, ErrorResult& aRv);
 
   void AddFromURI(const nsAString& aSrc, const Optional<float>& aWeight, ErrorResult& aRv);
 
   void AddFromString(const nsAString& aString, const Optional<float>& aWeight, ErrorResult& aRv);
 
   already_AddRefed<SpeechGrammar> IndexedGetter(uint32_t aIndex, bool& aPresent, ErrorResult& aRv);
 
+  nsCOMPtr<nsISpeechRecognitionService> mRecognitionService;
+
 private:
   ~SpeechGrammarList();
 
   nsCOMPtr<nsISupports> mParent;
 };
 
 } // namespace dom
 } // namespace mozilla
--- a/content/media/webspeech/recognition/SpeechRecognition.cpp
+++ b/content/media/webspeech/recognition/SpeechRecognition.cpp
@@ -52,16 +52,46 @@ GetSpeechRecognitionLog()
 
   return sLog;
 }
 #define SR_LOG(...) PR_LOG(GetSpeechRecognitionLog(), PR_LOG_DEBUG, (__VA_ARGS__))
 #else
 #define SR_LOG(...)
 #endif
 
+already_AddRefed<nsISpeechRecognitionService>
+GetSpeechRecognitionService()
+{
+  nsAutoCString speechRecognitionServiceCID;
+
+  nsAdoptingCString prefValue =
+  Preferences::GetCString(PREFERENCE_DEFAULT_RECOGNITION_SERVICE);
+  nsAutoCString speechRecognitionService;
+
+  if (!prefValue.get() || prefValue.IsEmpty()) {
+    speechRecognitionService = DEFAULT_RECOGNITION_SERVICE;
+  } else {
+    speechRecognitionService = prefValue;
+  }
+
+  if (!SpeechRecognition::mTestConfig.mFakeRecognitionService){
+    speechRecognitionServiceCID =
+      NS_LITERAL_CSTRING(NS_SPEECH_RECOGNITION_SERVICE_CONTRACTID_PREFIX) +
+      speechRecognitionService;
+  } else {
+    speechRecognitionServiceCID =
+      NS_SPEECH_RECOGNITION_SERVICE_CONTRACTID_PREFIX "fake";
+  }
+
+  nsresult aRv;
+  nsCOMPtr<nsISpeechRecognitionService> recognitionService;
+  recognitionService = do_GetService(speechRecognitionServiceCID.get(), &aRv);
+  return recognitionService.forget();
+}
+
 NS_INTERFACE_MAP_BEGIN(SpeechRecognition)
   NS_INTERFACE_MAP_ENTRY(nsIObserver)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(SpeechRecognition, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(SpeechRecognition, DOMEventTargetHelper)
 
 struct SpeechRecognition::TestConfig SpeechRecognition::mTestConfig;
@@ -323,43 +353,16 @@ SpeechRecognition::ProcessAudioSegment(A
     samples += iterator->GetDuration();
     iterator.Next();
   }
 
   mRecognitionService->ProcessAudioSegment(aSegment, aTrackRate);
   return samples;
 }
 
-void
-SpeechRecognition::GetRecognitionServiceCID(nsACString& aResultCID)
-{
-  if (mTestConfig.mFakeRecognitionService) {
-    aResultCID =
-      NS_SPEECH_RECOGNITION_SERVICE_CONTRACTID_PREFIX "fake";
-
-    return;
-  }
-
-  nsAdoptingCString prefValue =
-    Preferences::GetCString(PREFERENCE_DEFAULT_RECOGNITION_SERVICE);
-
-  nsAutoCString speechRecognitionService;
-  if (!prefValue.get() || prefValue.IsEmpty()) {
-    speechRecognitionService = DEFAULT_RECOGNITION_SERVICE;
-  } else {
-    speechRecognitionService = prefValue;
-  }
-
-  aResultCID =
-    NS_LITERAL_CSTRING(NS_SPEECH_RECOGNITION_SERVICE_CONTRACTID_PREFIX) +
-    speechRecognitionService;
-
-  return;
-}
-
 /****************************************************************************
  * FSM Transition functions
  *
  * If a transition function may cause a DOM event to be fired,
  * it may also be re-entered, since the event handler may cause the
  * event loop to spin and new SpeechEvents to be processed.
  *
  * Rules:
@@ -686,23 +689,20 @@ SpeechRecognition::SetServiceURI(const n
 void
 SpeechRecognition::Start(const Optional<NonNull<DOMMediaStream>>& aStream, ErrorResult& aRv)
 {
   if (mCurrentState != STATE_IDLE) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
-  nsAutoCString speechRecognitionServiceCID;
-  GetRecognitionServiceCID(speechRecognitionServiceCID);
+  mRecognitionService = GetSpeechRecognitionService();
+  NS_ENSURE_TRUE_VOID(mRecognitionService);
 
   nsresult rv;
-  mRecognitionService = do_GetService(speechRecognitionServiceCID.get(), &rv);
-  NS_ENSURE_SUCCESS_VOID(rv);
-
   rv = mRecognitionService->Initialize(this);
   NS_ENSURE_SUCCESS_VOID(rv);
 
   MediaStreamConstraints constraints;
   constraints.mAudio.SetAsBoolean() = true;
 
   if (aStream.WasPassed()) {
     StartRecording(&aStream.Value());
--- a/content/media/webspeech/recognition/SpeechRecognition.h
+++ b/content/media/webspeech/recognition/SpeechRecognition.h
@@ -48,16 +48,18 @@ class SpeechEvent;
 
 #ifdef PR_LOGGING
 PRLogModuleInfo* GetSpeechRecognitionLog();
 #define SR_LOG(...) PR_LOG(GetSpeechRecognitionLog(), PR_LOG_DEBUG, (__VA_ARGS__))
 #else
 #define SR_LOG(...)
 #endif
 
+already_AddRefed<nsISpeechRecognitionService> GetSpeechRecognitionService();
+
 class SpeechRecognition MOZ_FINAL : public DOMEventTargetHelper,
                                     public nsIObserver,
                                     public SupportsWeakPtr<SpeechRecognition>
 {
 public:
   MOZ_DECLARE_REFCOUNTED_TYPENAME(SpeechRecognition)
   explicit SpeechRecognition(nsPIDOMWindow* aOwnerWindow);
 
@@ -229,18 +231,16 @@ private:
   void DoNothing(SpeechEvent* aEvent);
   void AbortSilently(SpeechEvent* aEvent);
   void AbortError(SpeechEvent* aEvent);
 
   nsRefPtr<DOMMediaStream> mDOMStream;
   nsRefPtr<SpeechStreamListener> mSpeechListener;
   nsCOMPtr<nsISpeechRecognitionService> mRecognitionService;
 
-  void GetRecognitionServiceCID(nsACString& aResultCID);
-
   FSMState mCurrentState;
 
   Endpointer mEndpointer;
   uint32_t mEstimationSamples;
 
   uint32_t mAudioSamplesPerChunk;
 
   // buffer holds one chunk of mAudioSamplesPerChunk
--- a/content/media/webspeech/recognition/nsISpeechRecognitionService.idl
+++ b/content/media/webspeech/recognition/nsISpeechRecognitionService.idl
@@ -2,24 +2,42 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
 %{C++
 #include "mozilla/WeakPtr.h"
+
+namespace mozilla {
+class AudioSegment;
+namespace dom {
+class SpeechRecognition;
+class SpeechRecognitionResultList;
+class SpeechGrammarList;
+class SpeechGrammar;
+}
+}
 %}
 
 native SpeechRecognitionWeakPtr(mozilla::WeakPtr<mozilla::dom::SpeechRecognition>);
 [ptr] native AudioSegmentPtr(mozilla::AudioSegment);
+[ptr] native SpeechGrammarListPtr(mozilla::dom::SpeechGrammarList);
+[ptr] native SpeechGrammarPtr(mozilla::dom::SpeechGrammar);
+
+[uuid(374583f0-4507-11e4-a183-164230d1df67)]
+interface nsISpeechGrammarCompilationCallback : nsISupports {
+    void grammarCompilationEnd(in SpeechGrammarPtr grammarObject, in boolean success);
+};
 
 [uuid(857f3fa2-a980-4d3e-a959-a2f53af74232)]
 interface nsISpeechRecognitionService : nsISupports {
     void initialize(in SpeechRecognitionWeakPtr aSpeechRecognition);
     void processAudioSegment(in AudioSegmentPtr aAudioSegment, in long aSampleRate);
+    void validateAndSetGrammarList(in SpeechGrammarListPtr aSpeechGramarList, in nsISpeechGrammarCompilationCallback aCallback);
     void soundEnd();
     void abort();
 };
 
 %{C++
 #define NS_SPEECH_RECOGNITION_SERVICE_CONTRACTID_PREFIX "@mozilla.org/webspeech/service;1?name="
 %}
--- a/content/media/webspeech/recognition/test/FakeSpeechRecognitionService.cpp
+++ b/content/media/webspeech/recognition/test/FakeSpeechRecognitionService.cpp
@@ -47,16 +47,22 @@ FakeSpeechRecognitionService::ProcessAud
 
 NS_IMETHODIMP
 FakeSpeechRecognitionService::SoundEnd()
 {
   return NS_OK;
 }
 
 NS_IMETHODIMP
+FakeSpeechRecognitionService::ValidateAndSetGrammarList(mozilla::dom::SpeechGrammarList*, nsISpeechGrammarCompilationCallback*)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 FakeSpeechRecognitionService::Abort()
 {
   return NS_OK;
 }
 
 NS_IMETHODIMP
 FakeSpeechRecognitionService::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData)
 {
--- a/content/media/webspeech/recognition/test/FakeSpeechRecognitionService.h
+++ b/content/media/webspeech/recognition/test/FakeSpeechRecognitionService.h
@@ -4,27 +4,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_FakeSpeechRecognitionService_h
 #define mozilla_dom_FakeSpeechRecognitionService_h
 
 #include "nsCOMPtr.h"
 #include "nsIObserver.h"
-
-// nsISpeechRecognitionService needs these declarations
-namespace mozilla {
-  class AudioSegment;
-
-  namespace dom {
-    class SpeechRecognition;
-    class SpeechRecognitionResultList;
-  }
-}
-
 #include "nsISpeechRecognitionService.h"
 
 #define NS_FAKE_SPEECH_RECOGNITION_SERVICE_CID \
   {0x48c345e7, 0x9929, 0x4f9a, {0xa5, 0x63, 0xf4, 0x78, 0x22, 0x2d, 0xab, 0xcd}};
 
 namespace mozilla {
 
 class FakeSpeechRecognitionService : public nsISpeechRecognitionService,
--- a/dom/canvas/WebGL2Context.h
+++ b/dom/canvas/WebGL2Context.h
@@ -238,13 +238,19 @@ public:
     void DeleteVertexArray(WebGLVertexArrayObject* vertexArray);
     bool IsVertexArray(WebGLVertexArrayObject* vertexArray);
     void BindVertexArray(WebGLVertexArrayObject* vertexArray);
 */
 
 private:
 
     WebGL2Context();
+
+    bool ValidateSizedInternalFormat(GLenum internalFormat, const char* info);
+    bool ValidateTexStorage(GLenum target, GLsizei levels, GLenum internalformat,
+                                GLsizei width, GLsizei height, GLsizei depth,
+                                const char* info);
+
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/canvas/WebGL2ContextTextures.cpp
+++ b/dom/canvas/WebGL2ContextTextures.cpp
@@ -4,23 +4,177 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebGL2Context.h"
 #include "GLContext.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
+bool
+WebGL2Context::ValidateSizedInternalFormat(GLenum internalformat, const char* info)
+{
+    switch (internalformat) {
+        // Sized Internal Formats
+        // https://www.khronos.org/opengles/sdk/docs/man3/html/glTexStorage2D.xhtml
+    case LOCAL_GL_R8:
+    case LOCAL_GL_R8_SNORM:
+    case LOCAL_GL_R16F:
+    case LOCAL_GL_R32F:
+    case LOCAL_GL_R8UI:
+    case LOCAL_GL_R8I:
+    case LOCAL_GL_R16UI:
+    case LOCAL_GL_R16I:
+    case LOCAL_GL_R32UI:
+    case LOCAL_GL_R32I:
+    case LOCAL_GL_RG8:
+    case LOCAL_GL_RG8_SNORM:
+    case LOCAL_GL_RG16F:
+    case LOCAL_GL_RG32F:
+    case LOCAL_GL_RG8UI:
+    case LOCAL_GL_RG8I:
+    case LOCAL_GL_RG16UI:
+    case LOCAL_GL_RG16I:
+    case LOCAL_GL_RG32UI:
+    case LOCAL_GL_RG32I:
+    case LOCAL_GL_RGB8:
+    case LOCAL_GL_SRGB8:
+    case LOCAL_GL_RGB565:
+    case LOCAL_GL_RGB8_SNORM:
+    case LOCAL_GL_R11F_G11F_B10F:
+    case LOCAL_GL_RGB9_E5:
+    case LOCAL_GL_RGB16F:
+    case LOCAL_GL_RGB32F:
+    case LOCAL_GL_RGB8UI:
+    case LOCAL_GL_RGB8I:
+    case LOCAL_GL_RGB16UI:
+    case LOCAL_GL_RGB16I:
+    case LOCAL_GL_RGB32UI:
+    case LOCAL_GL_RGB32I:
+    case LOCAL_GL_RGBA8:
+    case LOCAL_GL_SRGB8_ALPHA8:
+    case LOCAL_GL_RGBA8_SNORM:
+    case LOCAL_GL_RGB5_A1:
+    case LOCAL_GL_RGBA4:
+    case LOCAL_GL_RGB10_A2:
+    case LOCAL_GL_RGBA16F:
+    case LOCAL_GL_RGBA32F:
+    case LOCAL_GL_RGBA8UI:
+    case LOCAL_GL_RGBA8I:
+    case LOCAL_GL_RGB10_A2UI:
+    case LOCAL_GL_RGBA16UI:
+    case LOCAL_GL_RGBA16I:
+    case LOCAL_GL_RGBA32I:
+    case LOCAL_GL_RGBA32UI:
+    case LOCAL_GL_DEPTH_COMPONENT16:
+    case LOCAL_GL_DEPTH_COMPONENT24:
+    case LOCAL_GL_DEPTH_COMPONENT32F:
+    case LOCAL_GL_DEPTH24_STENCIL8:
+    case LOCAL_GL_DEPTH32F_STENCIL8:
+        return true;
+    }
+
+    if (IsCompressedTextureFormat(internalformat)) {
+        return true;
+    }
+
+    const char* name = EnumName(internalformat);
+    if (name && name[0] != '[')
+        ErrorInvalidEnum("%s: invalid internal format %s", info, name);
+    else
+        ErrorInvalidEnum("%s: invalid internal format 0x%04X", info, internalformat);
+
+    return false;
+}
+
+/** Validates parameters to texStorage{2D,3D} */
+bool
+WebGL2Context::ValidateTexStorage(GLenum target, GLsizei levels, GLenum internalformat,
+                                      GLsizei width, GLsizei height, GLsizei depth,
+                                      const char* info)
+{
+    // GL_INVALID_OPERATION is generated if the default texture object is curently bound to target.
+    WebGLTexture* tex = activeBoundTextureForTarget(target);
+    if (!tex) {
+        ErrorInvalidOperation("%s: no texture is bound to target %s", info, EnumName(target));
+        return false;
+    }
+
+    // GL_INVALID_OPERATION is generated if the texture object currently bound to target already has
+    // GL_TEXTURE_IMMUTABLE_FORMAT set to GL_TRUE.
+    if (tex->IsImmutable()) {
+        ErrorInvalidOperation("%s: texture bound to target %s is already immutable", info, EnumName(target));
+        return false;
+    }
+
+    // GL_INVALID_ENUM is generated if internalformat is not a valid sized internal format.
+    if (!ValidateSizedInternalFormat(internalformat, info))
+        return false;
+
+    // GL_INVALID_VALUE is generated if width, height or levels are less than 1.
+    if (width < 1)  { ErrorInvalidValue("%s: width is < 1", info);  return false; }
+    if (height < 1) { ErrorInvalidValue("%s: height is < 1", info); return false; }
+    if (depth < 1)  { ErrorInvalidValue("%s: depth is < 1", info);  return false; }
+    if (levels < 1) { ErrorInvalidValue("%s: levels is < 1", info); return false; }
+
+    // The following check via FloorLog2 only requires a depth value if target is TEXTURE_3D.
+    bool is3D = (target != LOCAL_GL_TEXTURE_3D);
+    if (!is3D)
+        depth = 1;
+
+    // GL_INVALID_OPERATION is generated if levels is greater than floor(log2(max(width, height, depth)))+1.
+    if (FloorLog2(std::max(std::max(width, height), depth))+1 < levels) {
+        ErrorInvalidOperation("%s: levels > floor(log2(max(width, height%s)))+1", info, is3D ? ", depth" : "");
+        return false;
+    }
+
+    return true;
+}
+
 // -------------------------------------------------------------------------
 // Texture objects
 
 void
 WebGL2Context::TexStorage2D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height)
 {
-    MOZ_CRASH("Not Implemented.");
+    if (IsContextLost())
+        return;
+
+    // GL_INVALID_ENUM is generated if target is not one of the accepted target enumerants.
+    if (target != LOCAL_GL_TEXTURE_2D && target != LOCAL_GL_TEXTURE_CUBE_MAP)
+        return ErrorInvalidEnum("texStorage2D: target is not TEXTURE_2D or TEXTURE_CUBE_MAP.");
+
+    if (!ValidateTexStorage(target, levels, internalformat, width, height, 1, "texStorage2D"))
+        return;
+
+    WebGLTexture* tex = activeBoundTextureForTarget(target);
+    tex->SetImmutable();
+
+    const size_t facesCount = (target == LOCAL_GL_TEXTURE_2D) ? 1 : 6;
+    GLsizei w = width;
+    GLsizei h = height;
+    for (size_t l = 0; l < size_t(levels); l++) {
+        for (size_t f = 0; f < facesCount; f++) {
+            TexImageTarget imageTarget = TexImageTargetForTargetAndFace(target, f);
+            // FIXME: SetImageInfo wants a type, to go with the internalformat that it stores.
+            // 'type' is deprecated by sized internalformats, which are how TexStorage works.
+            // We must fix WebGLTexture::ImageInfo to store an "effective internalformat",
+            // which in the present case is just the sized internalformat, and drop 'types'
+            // altogether. For now, we just pass LOCAL_GL_UNSIGNED_BYTE, which works For
+            // the most commonly used formats.
+            const GLenum type = LOCAL_GL_UNSIGNED_BYTE;
+            tex->SetImageInfo(imageTarget, l, w, h,
+                              internalformat, type,
+                              WebGLImageDataStatus::UninitializedImageData);
+        }
+        w = std::max(1, w/2);
+        h = std::max(1, h/2);
+    }
+
+    gl->fTexStorage2D(target, levels, internalformat, width, height);
 }
 
 void
 WebGL2Context::TexStorage3D(GLenum target, GLsizei levels, GLenum internalformat,
                             GLsizei width, GLsizei height, GLsizei depth)
 {
     MOZ_CRASH("Not Implemented.");
 }
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -390,16 +390,22 @@ WebGLContext::CopyTexSubImage2D_base(Tex
 
     MakeContextCurrent();
 
     WebGLTexture *tex = activeBoundTextureForTexImageTarget(texImageTarget);
 
     if (!tex)
         return ErrorInvalidOperation("%s: no texture is bound to this target");
 
+    if (tex->IsImmutable()) {
+        if (!sub) {
+            return ErrorInvalidOperation("copyTexImage2D: disallowed because the texture bound to this target has already been made immutable by texStorage2D");
+        }
+    }
+
     if (CanvasUtils::CheckSaneSubrectSize(x, y, width, height, framebufferWidth, framebufferHeight)) {
         if (sub)
             gl->fCopyTexSubImage2D(texImageTarget.get(), level, xoffset, yoffset, x, y, width, height);
         else
             gl->fCopyTexImage2D(texImageTarget.get(), level, internalformat, x, y, width, height, 0);
     } else {
 
         // the rect doesn't fit in the framebuffer
@@ -3331,20 +3337,27 @@ WebGLContext::CompressedTexImage2D(GLenu
 
     if (!ValidateCompTexImageSize(level, internalformat, 0, 0, width, height, width, height, func))
     {
         return;
     }
 
     const TexImageTarget texImageTarget(rawTexImgTarget);
 
+    WebGLTexture* tex = activeBoundTextureForTexImageTarget(texImageTarget);
+    MOZ_ASSERT(tex);
+    if (tex->IsImmutable()) {
+        return ErrorInvalidOperation(
+            "compressedTexImage2D: disallowed because the texture bound to "
+            "this target has already been made immutable by texStorage2D");
+    }
+
     MakeContextCurrent();
     gl->fCompressedTexImage2D(texImageTarget.get(), level, internalformat, width, height, border, byteLength, view.Data());
-    WebGLTexture* tex = activeBoundTextureForTexImageTarget(texImageTarget);
-    MOZ_ASSERT(tex);
+
     tex->SetImageInfo(texImageTarget, level, width, height, internalformat, LOCAL_GL_UNSIGNED_BYTE,
                       WebGLImageDataStatus::InitializedImageData);
 }
 
 void
 WebGLContext::CompressedTexSubImage2D(GLenum rawTexImgTarget, GLint level, GLint xoffset,
                                       GLint yoffset, GLsizei width, GLsizei height,
                                       GLenum internalformat,
@@ -3682,16 +3695,21 @@ WebGLContext::TexImage2D_base(TexImageTa
         return ErrorInvalidOperation("texImage2D: not enough data for operation (need %d, have %d)",
                                  bytesNeeded, byteLength);
 
     WebGLTexture *tex = activeBoundTextureForTexImageTarget(texImageTarget);
 
     if (!tex)
         return ErrorInvalidOperation("texImage2D: no texture is bound to this target");
 
+    if (tex->IsImmutable()) {
+        return ErrorInvalidOperation(
+            "texImage2D: disallowed because the texture "
+            "bound to this target has already been made immutable by texStorage2D");
+    }
     MakeContextCurrent();
 
     nsAutoArrayPtr<uint8_t> convertedData;
     void* pixels = nullptr;
     WebGLImageDataStatus imageInfoStatusIfSuccess = WebGLImageDataStatus::UninitializedImageData;
 
     if (byteLength) {
         size_t   srcStride = srcStrideOrZero ? srcStrideOrZero : checked_alignedRowSize.value();
--- a/dom/canvas/WebGLContextUtils.cpp
+++ b/dom/canvas/WebGLContextUtils.cpp
@@ -501,16 +501,26 @@ WebGLContext::IsCompressedTextureFormat(
         case LOCAL_GL_ATC_RGB:
         case LOCAL_GL_ATC_RGBA_EXPLICIT_ALPHA:
         case LOCAL_GL_ATC_RGBA_INTERPOLATED_ALPHA:
         case LOCAL_GL_COMPRESSED_RGB_PVRTC_4BPPV1:
         case LOCAL_GL_COMPRESSED_RGB_PVRTC_2BPPV1:
         case LOCAL_GL_COMPRESSED_RGBA_PVRTC_4BPPV1:
         case LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1:
         case LOCAL_GL_ETC1_RGB8_OES:
+        case LOCAL_GL_COMPRESSED_R11_EAC:
+        case LOCAL_GL_COMPRESSED_SIGNED_R11_EAC:
+        case LOCAL_GL_COMPRESSED_RG11_EAC:
+        case LOCAL_GL_COMPRESSED_SIGNED_RG11_EAC:
+        case LOCAL_GL_COMPRESSED_RGB8_ETC2:
+        case LOCAL_GL_COMPRESSED_SRGB8_ETC2:
+        case LOCAL_GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:
+        case LOCAL_GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:
+        case LOCAL_GL_COMPRESSED_RGBA8_ETC2_EAC:
+        case LOCAL_GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:
             return true;
         default:
             return false;
     }
 }
 
 
 bool
--- a/dom/canvas/WebGLTexture.cpp
+++ b/dom/canvas/WebGLTexture.cpp
@@ -27,16 +27,17 @@ WebGLTexture::WebGLTexture(WebGLContext 
     , WebGLContextBoundObject(context)
     , mMinFilter(LOCAL_GL_NEAREST_MIPMAP_LINEAR)
     , mMagFilter(LOCAL_GL_LINEAR)
     , mWrapS(LOCAL_GL_REPEAT)
     , mWrapT(LOCAL_GL_REPEAT)
     , mFacesCount(0)
     , mMaxLevelWithCustomImages(0)
     , mHaveGeneratedMipmap(false)
+    , mImmutable(false)
     , mFakeBlackStatus(WebGLTextureFakeBlackStatus::IncompleteTexture)
 {
     SetIsDOMBinding();
     mContext->MakeContextCurrent();
     mContext->gl->fGenTextures(1, &mGLName);
     mContext->mTextures.insertBack(this);
 }
 
@@ -222,29 +223,22 @@ WebGLTexture::IsCubeComplete() const {
     if (mTarget != LOCAL_GL_TEXTURE_CUBE_MAP)
         return false;
     const ImageInfo &first = ImageInfoAt(LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0);
     if (!first.IsPositive() || !first.IsSquare())
         return false;
     return AreAllLevel0ImageInfosEqual();
 }
 
-static TexImageTarget
-GLCubeMapFaceById(int id)
-{
-    // Correctness is checked by the constructor for TexImageTarget
-    return TexImageTarget(LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X + id);
-}
-
 bool
 WebGLTexture::IsMipmapCubeComplete() const {
     if (!IsCubeComplete()) // in particular, this checks that this is a cube map
         return false;
     for (int i = 0; i < 6; i++) {
-        const TexImageTarget face = GLCubeMapFaceById(i);
+        const TexImageTarget face = TexImageTargetForTargetAndFace(LOCAL_GL_TEXTURE_CUBE_MAP, i);
         if (!DoesTexture2DMipmapHaveAllLevelsConsistentlyDefined(face))
             return false;
     }
     return true;
 }
 
 WebGLTextureFakeBlackStatus
 WebGLTexture::ResolvedFakeBlackStatus() {
@@ -404,19 +398,17 @@ WebGLTexture::ResolvedFakeBlackStatus() 
         if (hasAnyInitializedImageData) {
             // The texture contains some initialized image data, and some uninitialized image data.
             // In this case, we have no choice but to initialize all image data now. Fortunately,
             // in this case we know that we can't be dealing with a depth texture per WEBGL_depth_texture
             // and ANGLE_depth_texture (which allow only one image per texture) so we can assume that
             // glTexImage2D is able to upload data to images.
             for (size_t level = 0; level <= mMaxLevelWithCustomImages; ++level) {
                 for (size_t face = 0; face < mFacesCount; ++face) {
-                    TexImageTarget imageTarget = mTarget == LOCAL_GL_TEXTURE_2D
-                                                 ? LOCAL_GL_TEXTURE_2D
-                                                 : LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X + face;
+                    TexImageTarget imageTarget = TexImageTargetForTargetAndFace(mTarget, face);
                     const ImageInfo& imageInfo = ImageInfoAt(imageTarget, level);
                     if (imageInfo.mImageDataStatus == WebGLImageDataStatus::UninitializedImageData) {
                         DoDeferredImageInitialization(imageTarget, level);
                     }
                 }
             }
             mFakeBlackStatus = WebGLTextureFakeBlackStatus::NotNeeded;
         } else {
--- a/dom/canvas/WebGLTexture.h
+++ b/dom/canvas/WebGLTexture.h
@@ -197,17 +197,19 @@ protected:
 
     TexMinFilter mMinFilter;
     TexMagFilter mMagFilter;
     TexWrap mWrapS, mWrapT;
 
     size_t mFacesCount, mMaxLevelWithCustomImages;
     nsTArray<ImageInfo> mImageInfos;
 
-    bool mHaveGeneratedMipmap;
+    bool mHaveGeneratedMipmap; // set by generateMipmap
+    bool mImmutable; // set by texStorage*
+
     WebGLTextureFakeBlackStatus mFakeBlackStatus;
 
     void EnsureMaxLevelWithCustomImagesAtLeast(size_t aMaxLevelWithCustomImages) {
         mMaxLevelWithCustomImages = std::max(mMaxLevelWithCustomImages, aMaxLevelWithCustomImages);
         mImageInfos.EnsureLengthAtLeast((mMaxLevelWithCustomImages + 1) * mFacesCount);
     }
 
     bool CheckFloatTextureFilterParams() const {
@@ -266,16 +268,29 @@ public:
     bool IsMipmapTexture2DComplete() const;
 
     bool IsCubeComplete() const;
 
     bool IsMipmapCubeComplete() const;
 
     void SetFakeBlackStatus(WebGLTextureFakeBlackStatus x);
 
+    bool IsImmutable() const { return mImmutable; }
+    void SetImmutable() { mImmutable = true; }
+
+    size_t MaxLevelWithCustomImages() const { return mMaxLevelWithCustomImages; }
+
     // Returns the current fake-black-status, except if it was Unknown,
     // in which case this function resolves it first, so it never returns Unknown.
     WebGLTextureFakeBlackStatus ResolvedFakeBlackStatus();
 };
 
+inline TexImageTarget
+TexImageTargetForTargetAndFace(TexTarget target, size_t face)
+{
+    return target == LOCAL_GL_TEXTURE_2D
+           ? LOCAL_GL_TEXTURE_2D
+           : LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X + face;
+}
+
 } // namespace mozilla
 
 #endif
--- a/dom/identity/Identity.manifest
+++ b/dom/identity/Identity.manifest
@@ -1,9 +1,8 @@
 # nsDOMIdentity.js
 component {210853d9-2c97-4669-9761-b1ab9cbf57ef} nsDOMIdentity.js
-contract @mozilla.org/dom/identity;1 {210853d9-2c97-4669-9761-b1ab9cbf57ef}
-category JavaScript-navigator-property mozId @mozilla.org/dom/identity;1
+contract @mozilla.org/identity/manager;1 {210853d9-2c97-4669-9761-b1ab9cbf57ef}
 
 # nsIDService.js (initialization on startup)
 component {4e0a0e98-b1d3-4745-a1eb-f815199dd06b} nsIDService.js
 contract @mozilla.org/dom/identity/service;1 {4e0a0e98-b1d3-4745-a1eb-f815199dd06b}
 category app-startup IDService @mozilla.org/dom/identity/service;1
--- a/dom/identity/nsDOMIdentity.js
+++ b/dom/identity/nsDOMIdentity.js
@@ -41,39 +41,20 @@ XPCOMUtils.defineLazyServiceGetter(this,
 
 const ERRORS = {
   "ERROR_INVALID_ASSERTION_AUDIENCE":
     "Assertion audience may not differ from origin",
   "ERROR_REQUEST_WHILE_NOT_HANDLING_USER_INPUT":
     "The request() method may only be invoked when handling user input",
 };
 
-function nsDOMIdentity(aIdentityInternal) {
-  this._identityInternal = aIdentityInternal;
+function nsDOMIdentity() {
 }
-nsDOMIdentity.prototype = {
-  __exposedProps__: {
-    // Relying Party (RP)
-    watch: 'r',
-    request: 'r',
-    logout: 'r',
-    get: 'r',
-    getVerifiedEmail: 'r',
 
-    // Provisioning
-    beginProvisioning: 'r',
-    genKeyPair: 'r',
-    registerCertificate: 'r',
-    raiseProvisioningFailure: 'r',
-
-    // Authentication
-    beginAuthentication: 'r',
-    completeAuthentication: 'r',
-    raiseAuthenticationFailure: 'r'
-  },
+nsDOMIdentity.prototype = {
 
   // require native events unless syntheticEventsOk is set
   get nativeEventsRequired() {
     if (Services.prefs.prefHasUserValue(PREF_SYNTHETIC_EVENTS_OK) &&
         (Services.prefs.getPrefType(PREF_SYNTHETIC_EVENTS_OK) ===
          Ci.nsIPrefBranch.PREF_BOOL)) {
       return !Services.prefs.getBoolPref(PREF_SYNTHETIC_EVENTS_OK);
     }
@@ -143,17 +124,17 @@ nsDOMIdentity.prototype = {
     this._rpWatcher.audience = message.audience;
 
     if (message.errors.length) {
       this.reportErrors(message);
       // We don't delete the rpWatcher object, because we don't want the
       // broken client to be able to call watch() any more.  It's broken.
       return;
     }
-    this._identityInternal._mm.sendAsyncMessage(
+    this._mm.sendAsyncMessage(
       "Identity:RP:Watch",
       message,
       null,
       this._window.document.nodePrincipal
     );
   },
 
   request: function nsDOMIdentity_request(aOptions = {}) {
@@ -219,17 +200,17 @@ nsDOMIdentity.prototype = {
         throw new Error("oncancel is not a function");
       } else {
         // Store optional cancel callback for later.
         this._onCancelRequestCallback = aOptions.oncancel;
       }
     }
 
     this._rpCalls++;
-    this._identityInternal._mm.sendAsyncMessage(
+    this._mm.sendAsyncMessage(
       "Identity:RP:Request",
       message,
       null,
       this._window.document.nodePrincipal
     );
   },
 
   logout: function nsDOMIdentity_logout() {
@@ -244,17 +225,17 @@ nsDOMIdentity.prototype = {
     let message = this.DOMIdentityMessage();
 
     // Report and fail hard on any errors.
     if (message.errors.length) {
       this.reportErrors(message);
       return;
     }
 
-    this._identityInternal._mm.sendAsyncMessage(
+    this._mm.sendAsyncMessage(
       "Identity:RP:Logout",
       message,
       null,
       this._window.document.nodePrincipal
     );
   },
 
   /*
@@ -332,17 +313,17 @@ nsDOMIdentity.prototype = {
     if (this._beginProvisioningCallback) {
       throw new Error("navigator.id.beginProvisioning already called.");
     }
     if (!aCallback || typeof(aCallback) !== "function") {
       throw new Error("beginProvisioning callback is required.");
     }
 
     this._beginProvisioningCallback = aCallback;
-    this._identityInternal._mm.sendAsyncMessage(
+    this._mm.sendAsyncMessage(
       "Identity:IDP:BeginProvisioning",
       this.DOMIdentityMessage(),
       null,
       this._window.document.nodePrincipal
     );
   },
 
   genKeyPair: function nsDOMIdentity_genKeyPair(aCallback) {
@@ -353,17 +334,17 @@ nsDOMIdentity.prototype = {
     if (this._genKeyPairCallback) {
       throw new Error("navigator.id.genKeyPair already called.");
     }
     if (!aCallback || typeof(aCallback) !== "function") {
       throw new Error("genKeyPair callback is required.");
     }
 
     this._genKeyPairCallback = aCallback;
-    this._identityInternal._mm.sendAsyncMessage(
+    this._mm.sendAsyncMessage(
       "Identity:IDP:GenKeyPair",
       this.DOMIdentityMessage(),
       null,
       this._window.document.nodePrincipal
     );
   },
 
   registerCertificate: function nsDOMIdentity_registerCertificate(aCertificate) {
@@ -373,17 +354,17 @@ nsDOMIdentity.prototype = {
     }
     if (this._provisioningEnded) {
       throw new Error("Provisioning already ended");
     }
     this._provisioningEnded = true;
 
     let message = this.DOMIdentityMessage();
     message.cert = aCertificate;
-    this._identityInternal._mm.sendAsyncMessage(
+    this._mm.sendAsyncMessage(
       "Identity:IDP:RegisterCertificate",
       message,
       null,
       this._window.document.nodePrincipal
     );
   },
 
   raiseProvisioningFailure: function nsDOMIdentity_raiseProvisioningFailure(aReason) {
@@ -393,17 +374,17 @@ nsDOMIdentity.prototype = {
     }
     if (!aReason || typeof(aReason) != "string") {
       throw new Error("raiseProvisioningFailure reason is required");
     }
     this._provisioningEnded = true;
 
     let message = this.DOMIdentityMessage();
     message.reason = aReason;
-    this._identityInternal._mm.sendAsyncMessage(
+    this._mm.sendAsyncMessage(
       "Identity:IDP:ProvisioningFailure",
       message,
       null,
       this._window.document.nodePrincipal
     );
   },
 
   /**
@@ -418,34 +399,34 @@ nsDOMIdentity.prototype = {
     if (typeof(aCallback) !== "function") {
       throw new Error("beginAuthentication callback is required.");
     }
     if (!aCallback || typeof(aCallback) !== "function") {
       throw new Error("beginAuthentication callback is required.");
     }
 
     this._beginAuthenticationCallback = aCallback;
-    this._identityInternal._mm.sendAsyncMessage(
+    this._mm.sendAsyncMessage(
       "Identity:IDP:BeginAuthentication",
       this.DOMIdentityMessage(),
       null,
       this._window.document.nodePrincipal
     );
   },
 
   completeAuthentication: function nsDOMIdentity_completeAuthentication() {
     if (this._authenticationEnded) {
       throw new Error("Authentication already ended");
     }
     if (!this._beginAuthenticationCallback) {
       throw new Error("navigator.id.completeAuthentication called outside of authentication");
     }
     this._authenticationEnded = true;
 
-    this._identityInternal._mm.sendAsyncMessage(
+    this._mm.sendAsyncMessage(
       "Identity:IDP:CompleteAuthentication",
       this.DOMIdentityMessage(),
       null,
       this._window.document.nodePrincipal
     );
   },
 
   raiseAuthenticationFailure: function nsDOMIdentity_raiseAuthenticationFailure(aReason) {
@@ -453,44 +434,24 @@ nsDOMIdentity.prototype = {
       throw new Error("Authentication already ended");
     }
     if (!aReason || typeof(aReason) != "string") {
       throw new Error("raiseProvisioningFailure reason is required");
     }
 
     let message = this.DOMIdentityMessage();
     message.reason = aReason;
-    this._identityInternal._mm.sendAsyncMessage(
+    this._mm.sendAsyncMessage(
       "Identity:IDP:AuthenticationFailure",
       message,
       null,
       this._window.document.nodePrincipal
     );
   },
 
-  // Private.
-  _init: function nsDOMIdentity__init(aWindow) {
-
-    this._initializeState();
-
-    // Store window and origin URI.
-    this._window = aWindow;
-    this._origin = aWindow.document.nodePrincipal.origin;
-    this._appStatus = aWindow.document.nodePrincipal.appStatus;
-    this._appId = aWindow.document.nodePrincipal.appId;
-
-    // Setup identifiers for current window.
-    let util = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                      .getInterface(Ci.nsIDOMWindowUtils);
-
-    // We need to inherit the id from the internalIdentity service.
-    // See comments below in that service's init.
-    this._id = this._identityInternal._id;
-  },
-
   /**
    * Called during init and shutdown.
    */
   _initializeState: function nsDOMIdentity__initializeState() {
     // Some state to prevent abuse
     // Limit the number of calls to .request
     this._rpCalls = 0;
     this._provisioningEnded = false;
@@ -498,22 +459,28 @@ nsDOMIdentity.prototype = {
 
     this._rpWatcher = null;
     this._onCancelRequestCallback = null;
     this._beginProvisioningCallback = null;
     this._genKeyPairCallback = null;
     this._beginAuthenticationCallback = null;
   },
 
-  _receiveMessage: function nsDOMIdentity_receiveMessage(aMessage) {
+  // nsIMessageListener
+  receiveMessage: function nsDOMIdentity_receiveMessage(aMessage) {
     let msg = aMessage.json;
 
+    // Is this message intended for this window?
+    if (msg.id != this._id) {
+      return;
+    }
+
     switch (aMessage.name) {
       case "Identity:ResetState":
-        if (!this._identityInternal._debug) {
+        if (!this._debug) {
           return;
         }
         this._initializeState();
         Services.obs.notifyObservers(null, "identity-DOM-state-reset", this._id);
         break;
       case "Identity:RP:Watch:OnLogin":
         // Do we have a watcher?
         if (!this._rpWatcher) {
@@ -581,20 +548,16 @@ nsDOMIdentity.prototype = {
         this._callGenKeyPairCallback(msg);
         break;
       case "Identity:IDP:CallBeginAuthenticationCallback":
         this._callBeginAuthenticationCallback(msg);
         break;
     }
   },
 
-  _log: function nsDOMIdentity__log(msg) {
-    this._identityInternal._log(msg);
-  },
-
   _callGenKeyPairCallback: function nsDOMIdentity__callGenKeyPairCallback(message) {
     // create a pubkey object that works
     let chrome_pubkey = JSON.parse(message.publicKey);
 
     // bunch of stuff to create a proper object in window context
     function genPropDesc(value) {
       return {
         enumerable: true, configurable: true, writable: true, value: value
@@ -671,98 +634,79 @@ nsDOMIdentity.prototype = {
     // Replace any audience supplied by the RP with one that has been sanitised
     message.audience = _audience;
 
     this._log("DOMIdentityMessage: " + JSON.stringify(message));
 
     return message;
   },
 
-  uninit: function DOMIdentity_uninit() {
-    this._log("nsDOMIdentity uninit() " + this._id);
-    this._identityInternal._mm.sendAsyncMessage(
-      "Identity:RP:Unwatch",
-      { id: this._id },
-      null,
-      this._window.document.nodePrincipal
-    );
-  }
-
-};
-
-/**
- * Internal functions that shouldn't be exposed to content.
- */
-function nsDOMIdentityInternal() {
-}
-nsDOMIdentityInternal.prototype = {
-
-  // nsIMessageListener
-  receiveMessage: function nsDOMIdentityInternal_receiveMessage(aMessage) {
-    let msg = aMessage.json;
-    // Is this message intended for this window?
-    if (msg.id != this._id) {
-      return;
-    }
-    this._identity._receiveMessage(aMessage);
-  },
-
+ /**
+  * Internal methods that are not exposed to content.
+  * See dom/webidl/Identity.webidl for the public interface.
+  */
   // nsIObserver
   observe: function nsDOMIdentityInternal_observe(aSubject, aTopic, aData) {
     let window = aSubject.QueryInterface(Ci.nsIDOMWindow);
     if (window != this._window) {
       return;
     }
 
-    this._identity.uninit();
+    this.uninit();
 
     Services.obs.removeObserver(this, "dom-window-destroyed");
-    this._identity._initializeState();
-    this._identity = null;
+    this._initializeState();
 
     // TODO: Also send message to DOMIdentity notifiying window is no longer valid
     // ie. in the case that the user closes the auth. window and we need to know.
 
     try {
       for (let msgName of this._messages) {
         this._mm.removeMessageListener(msgName, this);
       }
     } catch (ex) {
       // Avoid errors when removing more than once.
     }
 
     this._mm = null;
   },
 
-  // nsIDOMGlobalPropertyInitializer
+  //  Because we implement nsIDOMGlobalPropertyInitializer, our init() method
+  //  is invoked with content window as its single argument.
   init: function nsDOMIdentityInternal_init(aWindow) {
     if (Services.prefs.getPrefType(PREF_ENABLED) != Ci.nsIPrefBranch.PREF_BOOL
         || !Services.prefs.getBoolPref(PREF_ENABLED)) {
       return null;
     }
 
     this._debug =
       Services.prefs.getPrefType(PREF_DEBUG) == Ci.nsIPrefBranch.PREF_BOOL
       && Services.prefs.getBoolPref(PREF_DEBUG);
 
+    // Setup identifiers for current window.
     let util = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                       .getInterface(Ci.nsIDOMWindowUtils);
 
     // To avoid cross-process windowId collisions, use a uuid as an
     // almost certainly unique identifier.
     //
     // XXX Bug 869182 - use a combination of child process id and
     // innerwindow id to construct the unique id.
     this._id = uuidgen.generateUUID().toString();
     this._window = aWindow;
 
     // nsDOMIdentity needs to know our _id, so this goes after
     // its creation.
-    this._identity = new nsDOMIdentity(this);
-    this._identity._init(aWindow);
+    this._initializeState();
+
+    // Store window and origin URI.
+    this._window = aWindow;
+    this._origin = aWindow.document.nodePrincipal.origin;
+    this._appStatus = aWindow.document.nodePrincipal.appStatus;
+    this._appId = aWindow.document.nodePrincipal.appId;
 
     this._log("init was called from " + aWindow.document.location);
 
     this._mm = cpmm;
 
     // Setup listeners for messages from parent process.
     this._messages = [
       "Identity:ResetState",
@@ -776,34 +720,42 @@ nsDOMIdentityInternal.prototype = {
       "Identity:IDP:CallBeginAuthenticationCallback"
     ];
     this._messages.forEach(function(msgName) {
       this._mm.addMessageListener(msgName, this);
     }, this);
 
     // Setup observers so we can remove message listeners.
     Services.obs.addObserver(this, "dom-window-destroyed", false);
+  },
 
-    return this._identity;
-  },
+  uninit: function DOMIdentity_uninit() {
+    this._log("nsDOMIdentity uninit() " + this._id);
+    this._mm.sendAsyncMessage(
+      "Identity:RP:Unwatch",
+      { id: this._id }
+    );
+   },
 
   // Private.
   _log: function nsDOMIdentityInternal__log(msg) {
     if (!this._debug) {
       return;
     }
     dump("nsDOMIdentity (" + this._id + "): " + msg + "\n");
   },
 
   // Component setup.
   classID: Components.ID("{210853d9-2c97-4669-9761-b1ab9cbf57ef}"),
 
-  QueryInterface: XPCOMUtils.generateQI(
-    [Ci.nsIDOMGlobalPropertyInitializer, Ci.nsIMessageListener]
-  ),
+  QueryInterface: XPCOMUtils.generateQI([
+      Ci.nsIMessageListener,
+      Ci.nsIObserver,
+      Ci.nsIDOMGlobalPropertyInitializer
+  ]),
 
   classInfo: XPCOMUtils.generateCI({
     classID: Components.ID("{210853d9-2c97-4669-9761-b1ab9cbf57ef}"),
     contractID: "@mozilla.org/dom/identity;1",
     interfaces: [],
     classDescription: "Identity DOM Implementation"
   })
 };
@@ -835,9 +787,9 @@ function assertCorrectCallbacks(aOptions
 
   for (let cbName of optionalCallbacks) {
     if (aOptions[cbName] && typeof(aOptions[cbName]) != "function") {
       throw new Error(cbName + " must be a function");
     }
   }
 }
 
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([nsDOMIdentityInternal]);
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([nsDOMIdentity]);
--- a/dom/tests/mochitest/geolocation/test_mozsettings.html
+++ b/dom/tests/mochitest/geolocation/test_mozsettings.html
@@ -9,23 +9,25 @@ https://bugzilla.mozilla.org/show_bug.cg
   <script type="text/javascript" src="geolocation_common.js"></script>
 
 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=777594">Mozilla Bug 777594</a>
 <p id="display"></p>
 <div id="content" style="display: none">
-  
+
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 SimpleTest.waitForExplicitFinish();
 
+var timeToWaitMs = 1000;
+
 resume_geolocationProvider(function() {
   force_prompt(true, test1);
 });
 
 if (SpecialPowers.isMainProcess()) {
   SpecialPowers.Cu.import("resource://gre/modules/SettingsRequestManager.jsm");
 }
 
@@ -41,41 +43,41 @@ function test1() {
 function test2() {
   ok(navigator.geolocation, "get geolocation object");
 
   toggleGeolocationSetting(false, function() {
       ok(true, "turned off geolocation via mozSettings");
       setTimeout(function() {
 	  navigator.geolocation.getCurrentPosition(successCallbackAfterMozsettingOff,
                                                    failureCallbackAfterMozsettingOff);
-        }, 500); // need to wait a bit for all of these async callbacks to finish
+        }, timeToWaitMs); // need to wait a bit for all of these async callbacks to finish
   });
 }
 
 function successCallbackAfterMozsettingOff(position) {
   ok(false, "Success callback should not have been called after setting geolocation.enabled to false.");
 
   toggleGeolocationSetting(true, function() {
       ok(true, "turned on geolocation via mozSettings");
       setTimeout(function() {
          navigator.geolocation.getCurrentPosition(successCallbackAfterMozsettingOn,
                                                   failureCallbackAfterMozsettingOn);
-        }, 500); // need to wait a bit for all of these async callbacks to finish
+        }, timeToWaitMs); // need to wait a bit for all of these async callbacks to finish
     });
 }
 
 function failureCallbackAfterMozsettingOff(error) {
   ok(true, "Geolocation didn't work after setting geolocation.enabled to false.");
 
   toggleGeolocationSetting(true, function() {
       ok(true, "turned on geolocation via mozSettings");
       setTimeout(function() {
          navigator.geolocation.getCurrentPosition(successCallbackAfterMozsettingOn,
                                                   failureCallbackAfterMozsettingOn);
-        }, 500); // need to wait a bit for all of these async callbacks to finish
+        }, timeToWaitMs); // need to wait a bit for all of these async callbacks to finish
     });
 }
 
 function successCallbackAfterMozsettingOn(position) {
   ok(true, "Geolocation worked after setting geolocation.enabled to true.");
   SimpleTest.finish();
 }
 
new file mode 100644
--- /dev/null
+++ b/dom/webidl/Identity.webidl
@@ -0,0 +1,70 @@
+/* -*- Mode: IDL; 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/.
+ */
+
+callback IdentityOnReadyCallback = void();
+callback IdentityOnLoginCallback = void(DOMString identityAssertion);
+callback IdentityOnLogoutCallback = void();
+callback IdentityOnCancelCallback = void(DOMString? error);
+callback IdentityOnErrorCallback = void(DOMString error);
+
+dictionary IdentityWatchOptions {
+  // Required callback
+  IdentityOnLoginCallback onlogin;
+
+  // Optional parameters
+  DOMString wantIssuer;
+  DOMString loggedInUser;
+
+  // Optional callbacks
+  IdentityOnReadyCallback onready;
+  IdentityOnLogoutCallback onlogout;
+  IdentityOnErrorCallback onerror;
+
+  // Certified apps can specify this
+  DOMString audience;
+};
+
+dictionary IdentityRequestOptions {
+  // Optional parameters
+  long refreshAuthentication;
+  DOMString termsOfService;
+  DOMString privacyPolicy;
+  DOMString backgroundColor;
+  DOMString siteLogo;
+  DOMString siteName;
+  DOMString returnTo;
+
+  IdentityOnCancelCallback oncancel;
+
+  // Certified apps can specify this
+  DOMString origin;
+};
+
+dictionary IdentityGetOptions {
+  DOMString privacyPolicy;
+  DOMString termsOfService;
+  DOMString privacyURL;
+  DOMString tosURL;
+  DOMString siteName;
+  DOMString siteLogo;
+};
+
+[JSImplementation="@mozilla.org/identity/manager;1",
+ NoInterfaceObject,
+ NavigatorProperty="mozId",
+ Pref="dom.identity.enabled"]
+interface IdentityManager {
+  void watch(optional IdentityWatchOptions options);
+  void request(optional IdentityRequestOptions options);
+  void logout();
+
+  [Pref="dom.identity.exposeLegacyGetAPI"]
+  void get(IdentityOnLoginCallback callback, optional IdentityGetOptions options);
+
+  [Pref="dom.identity.exposeLegacyGetVerifiedEmailAPI"]
+  void getVerifiedEmail(IdentityOnLoginCallback callback);
+};
+
--- a/dom/webidl/SpeechGrammarList.webidl
+++ b/dom/webidl/SpeechGrammarList.webidl
@@ -5,17 +5,17 @@
  *
  * The origin of this IDL file is
  * http://dvcs.w3.org/hg/speech-api/raw-file/tip/speechapi.html
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
-[Pref="media.webspeech.recognition.enable"]
+[Constructor, Pref="media.webspeech.recognition.enable"]
 interface SpeechGrammarList {
     readonly attribute unsigned long length;
     [Throws]
     getter SpeechGrammar item(unsigned long index);
     [Throws]
     void addFromURI(DOMString src, optional float weight);
     [Throws]
     void addFromString(DOMString string, optional float weight);
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -227,16 +227,17 @@ WEBIDL_FILES = [
     'IDBIndex.webidl',
     'IDBKeyRange.webidl',
     'IDBMutableFile.webidl',
     'IDBObjectStore.webidl',
     'IDBOpenDBRequest.webidl',
     'IDBRequest.webidl',
     'IDBTransaction.webidl',
     'IDBVersionChangeEvent.webidl',
+    'Identity.webidl',
     'ImageCapture.webidl',
     'ImageData.webidl',
     'ImageDocument.webidl',
     'InputEvent.webidl',
     'InputMethod.webidl',
     'InspectorUtils.webidl',
     'InstallEvent.webidl',
     'InstallPhaseEvent.webidl',
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -121,16 +121,17 @@ static const char *sExtensionNames[] = {
     "GL_EXT_sRGB",
     "GL_EXT_shader_texture_lod",
     "GL_EXT_texture3D",
     "GL_EXT_texture_compression_dxt1",
     "GL_EXT_texture_compression_s3tc",
     "GL_EXT_texture_filter_anisotropic",
     "GL_EXT_texture_format_BGRA8888",
     "GL_EXT_texture_sRGB",
+    "GL_EXT_texture_storage",
     "GL_EXT_transform_feedback",
     "GL_EXT_unpack_subimage",
     "GL_IMG_read_format",
     "GL_IMG_texture_compression_pvrtc",
     "GL_IMG_texture_npot",
     "GL_KHR_debug",
     "GL_NV_draw_instanced",
     "GL_NV_fence",
@@ -904,16 +905,39 @@ GLContext::InitWithPrefix(const char *pr
             if (!LoadSymbols(useCore ? coreSymbols : extSymbols, trygl, prefix)) {
                 NS_ERROR("GL supports array instanced without supplying it function.");
 
                 MarkUnsupported(GLFeature::instanced_arrays);
                 ClearSymbols(coreSymbols);
             }
         }
 
+        if (IsSupported(GLFeature::texture_storage)) {
+            SymLoadStruct coreSymbols[] = {
+                { (PRFuncPtr*) &mSymbols.fTexStorage2D, { "TexStorage2D", nullptr } },
+                { (PRFuncPtr*) &mSymbols.fTexStorage3D, { "TexStorage3D", nullptr } },
+                END_SYMBOLS
+            };
+
+            SymLoadStruct extSymbols[] = {
+                { (PRFuncPtr*) &mSymbols.fTexStorage2D, { "TexStorage2DEXT", nullptr } },
+                { (PRFuncPtr*) &mSymbols.fTexStorage3D, { "TexStorage3DEXT", nullptr } },
+                END_SYMBOLS
+            };
+
+            bool useCore = IsFeatureProvidedByCoreSymbols(GLFeature::texture_storage);
+            if (!LoadSymbols(useCore ? coreSymbols : extSymbols, trygl, prefix)) {
+                NS_ERROR("GL supports texture storage without supplying its functions.");
+
+                MarkUnsupported(GLFeature::texture_storage);
+                MarkExtensionSupported(useCore ? ARB_texture_storage : EXT_texture_storage);
+                ClearSymbols(coreSymbols);
+            }
+        }
+
         if (IsSupported(GLFeature::sampler_objects)) {
             SymLoadStruct samplerObjectsSymbols[] = {
                 { (PRFuncPtr*) &mSymbols.fGenSamplers, { "GenSamplers", nullptr } },
                 { (PRFuncPtr*) &mSymbols.fDeleteSamplers, { "DeleteSamplers", nullptr } },
                 { (PRFuncPtr*) &mSymbols.fIsSampler, { "IsSampler", nullptr } },
                 { (PRFuncPtr*) &mSymbols.fBindSampler, { "BindSampler", nullptr } },
                 { (PRFuncPtr*) &mSymbols.fSamplerParameteri, { "SamplerParameteri", nullptr } },
                 { (PRFuncPtr*) &mSymbols.fSamplerParameteriv, { "SamplerParameteriv", nullptr } },
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -403,16 +403,17 @@ public:
         EXT_sRGB,
         EXT_shader_texture_lod,
         EXT_texture3D,
         EXT_texture_compression_dxt1,
         EXT_texture_compression_s3tc,
         EXT_texture_filter_anisotropic,
         EXT_texture_format_BGRA8888,
         EXT_texture_sRGB,
+        EXT_texture_storage,
         EXT_transform_feedback,
         EXT_unpack_subimage,
         IMG_read_format,
         IMG_texture_compression_pvrtc,
         IMG_texture_npot,
         KHR_debug,
         NV_draw_instanced,
         NV_fence,
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -2454,16 +2454,17 @@ bool AsyncPanZoomController::UpdateAnima
     }
     UpdateSharedCompositorFrameMetrics();
     return true;
   }
   return false;
 }
 
 Matrix4x4 AsyncPanZoomController::GetOverscrollTransform() const {
+  ReentrantMonitorAutoEnter lock(mMonitor);
   if (!IsOverscrolled()) {
     return Matrix4x4();
   }
 
   // The overscroll effect is a uniform stretch along the overscrolled axis,
   // with the edge of the content where we have reached the end of the
   // scrollable area pinned into place.
 
--- a/gfx/layers/ipc/CompositorParent.cpp
+++ b/gfx/layers/ipc/CompositorParent.cpp
@@ -55,16 +55,19 @@
 #include "mozilla/layers/CompositorD3D9.h"
 #endif
 #include "GeckoProfiler.h"
 #include "mozilla/ipc/ProtocolTypes.h"
 #include "mozilla/unused.h"
 #include "mozilla/Hal.h"
 #include "mozilla/HalTypes.h"
 #include "mozilla/StaticPtr.h"
+#ifdef MOZ_ENABLE_PROFILER_SPS
+#include "ProfilerMarkers.h"
+#endif
 
 namespace mozilla {
 namespace layers {
 
 using namespace base;
 using namespace mozilla::ipc;
 using namespace mozilla::gfx;
 using namespace std;
@@ -1112,16 +1115,35 @@ CompositorParent::ComputeRenderIntegrity
 {
   if (mLayerManager) {
     return mLayerManager->ComputeRenderIntegrity();
   }
 
   return 1.0f;
 }
 
+static void
+InsertVsyncProfilerMarker(TimeStamp aVsyncTimestamp)
+{
+#ifdef MOZ_ENABLE_PROFILER_SPS
+  MOZ_ASSERT(CompositorParent::IsInCompositorThread());
+  MOZ_ASSERT(profiler_is_active());
+  VsyncPayload* payload = new VsyncPayload(aVsyncTimestamp);
+  PROFILER_MARKER_PAYLOAD("VsyncTimestamp", payload);
+#endif
+}
+
+/*static */ void
+CompositorParent::PostInsertVsyncProfilerMarker(TimeStamp aVsyncTimestamp)
+{
+  if (profiler_is_active()) {
+    CompositorLoop()->PostTask(FROM_HERE,
+      NewRunnableFunction(InsertVsyncProfilerMarker, aVsyncTimestamp));
+  }
+}
 
 /**
  * This class handles layer updates pushed directly from child
  * processes to the compositor thread.  It's associated with a
  * CompositorParent on the compositor thread.  While it uses the
  * PCompositor protocol to manage these updates, it doesn't actually
  * drive compositing itself.  For that it hands off work to the
  * CompositorParent it's associated with.
--- a/gfx/layers/ipc/CompositorParent.h
+++ b/gfx/layers/ipc/CompositorParent.h
@@ -256,16 +256,21 @@ public:
 
   /**
    * Lookup the indirect shadow tree for |aId| and return it if it
    * exists.  Otherwise null is returned.  This must only be called on
    * the compositor thread.
    */
   static LayerTreeState* GetIndirectShadowTree(uint64_t aId);
 
+  /**
+   * Used by the profiler to denote when a vsync occured
+   */
+  static void PostInsertVsyncProfilerMarker(mozilla::TimeStamp aVsyncTimestamp);
+
   float ComputeRenderIntegrity();
 
   /**
    * Returns true if the calling thread is the compositor thread.
    */
   static bool IsInCompositorThread();
 
 protected:
--- a/gfx/layers/ipc/LayerTransactionParent.cpp
+++ b/gfx/layers/ipc/LayerTransactionParent.cpp
@@ -198,17 +198,17 @@ LayerTransactionParent::RecvUpdate(const
                                    const TargetConfig& targetConfig,
                                    const bool& isFirstPaint,
                                    const bool& scheduleComposite,
                                    const uint32_t& paintSequenceNumber,
                                    const bool& isRepeatTransaction,
                                    const mozilla::TimeStamp& aTransactionStart,
                                    InfallibleTArray<EditReply>* reply)
 {
-  profiler_tracing("Paint", "Composite", TRACING_INTERVAL_START);
+  profiler_tracing("Paint", "LayerTransaction", TRACING_INTERVAL_START);
   PROFILER_LABEL("LayerTransactionParent", "RecvUpdate",
     js::ProfileEntry::Category::GRAPHICS);
 
 #ifdef COMPOSITOR_PERFORMANCE_WARNING
   TimeStamp updateStart = TimeStamp::Now();
 #endif
 
   MOZ_LAYERS_LOG(("[ParentSide] received txn with %d edits", cset.Length()));
@@ -576,16 +576,17 @@ LayerTransactionParent::RecvUpdate(const
     float severity = (latency - TimeDuration::FromMilliseconds(kVisualWarningTrigger)).ToMilliseconds() /
                        (kVisualWarningMax - kVisualWarningTrigger);
     if (severity > 1.f) {
       severity = 1.f;
     }
     mLayerManager->VisualFrameWarning(severity);
   }
 
+  profiler_tracing("Paint", "LayerTransaction", TRACING_INTERVAL_END);
   return true;
 }
 
 bool
 LayerTransactionParent::RecvSetTestSampleTime(const TimeStamp& aTime)
 {
   return mShadowLayersManager->SetTestSampleTime(this, aTime);
 }
--- a/hal/gonk/GonkHal.cpp
+++ b/hal/gonk/GonkHal.cpp
@@ -1479,16 +1479,64 @@ SetNiceForPid(int aPid, int aNice)
   }
 
   HAL_LOG("Changed nice for pid %d from %d to %d.",
           aPid, origProcPriority, aNice);
 
   closedir(tasksDir);
 }
 
+/*
+ * Used to store the nice value adjustments and oom_adj values for the various
+ * process priority levels.
+ */
+struct ProcessPriorityPrefs {
+  bool initialized;
+  int lowPriorityNice;
+  struct {
+    int nice;
+    int oomScoreAdj;
+  } priorities[NUM_PROCESS_PRIORITY];
+};
+
+/*
+ * Reads the preferences for the various process priority levels and sets up
+ * watchers so that if they're dynamically changed the change is reflected on
+ * the appropriate variables.
+ */
+void
+EnsureProcessPriorityPrefs(ProcessPriorityPrefs* prefs)
+{
+  if (prefs->initialized) {
+    return;
+  }
+
+  // Read the preferences for process priority levels
+  for (int i = PROCESS_PRIORITY_BACKGROUND; i < NUM_PROCESS_PRIORITY; i++) {
+    ProcessPriority priority = static_cast<ProcessPriority>(i);
+
+    // Read the nice values
+    const char* processPriorityStr = ProcessPriorityToString(priority);
+    nsPrintfCString niceStr("hal.processPriorityManager.gonk.%s.Nice",
+                            processPriorityStr);
+    Preferences::AddIntVarCache(&prefs->priorities[i].nice, niceStr.get());
+
+    // Read the oom_adj scores
+    nsPrintfCString oomStr("hal.processPriorityManager.gonk.%s.OomScoreAdjust",
+                           processPriorityStr);
+    Preferences::AddIntVarCache(&prefs->priorities[i].oomScoreAdj,
+                                oomStr.get());
+  }
+
+  Preferences::AddIntVarCache(&prefs->lowPriorityNice,
+                              "hal.processPriorityManager.gonk.LowCPUNice");
+
+  prefs->initialized = true;
+}
+
 void
 SetProcessPriority(int aPid,
                    ProcessPriority aPriority,
                    ProcessCPUPriority aCPUPriority,
                    uint32_t aBackgroundLRU)
 {
   HAL_LOG("SetProcessPriority(pid=%d, priority=%d, cpuPriority=%d, LRU=%u)",
           aPid, aPriority, aCPUPriority, aBackgroundLRU);
@@ -1497,72 +1545,59 @@ SetProcessPriority(int aPid,
   // OOM parameters according to our prefs.
   //
   // We could/should do this on startup instead of waiting for the first
   // SetProcessPriorityCall.  But in practice, the master process needs to set
   // its priority early in the game, so we can reasonably rely on
   // SetProcessPriority being called early in startup.
   EnsureKernelLowMemKillerParamsSet();
 
-  int32_t oomScoreAdj = 0;
-  nsresult rv = Preferences::GetInt(nsPrintfCString(
-    "hal.processPriorityManager.gonk.%s.OomScoreAdjust",
-    ProcessPriorityToString(aPriority)).get(), &oomScoreAdj);
+  static ProcessPriorityPrefs prefs = { 0 };
+  EnsureProcessPriorityPrefs(&prefs);
+
+  int oomScoreAdj = prefs.priorities[aPriority].oomScoreAdj;
 
   RoundOomScoreAdjUpWithBackroundLRU(oomScoreAdj, aBackgroundLRU);
 
-  if (NS_SUCCEEDED(rv)) {
-    int clampedOomScoreAdj = clamped<int>(oomScoreAdj, OOM_SCORE_ADJ_MIN,
-                                                       OOM_SCORE_ADJ_MAX);
-    if(clampedOomScoreAdj != oomScoreAdj) {
-      HAL_LOG("Clamping OOM adjustment for pid %d to %d", aPid,
-              clampedOomScoreAdj);
-    } else {
-      HAL_LOG("Setting OOM adjustment for pid %d to %d", aPid,
-              clampedOomScoreAdj);
-    }
-
-    // We try the newer interface first, and fall back to the older interface
-    // on failure.
-
-    if (!WriteToFile(nsPrintfCString("/proc/%d/oom_score_adj", aPid).get(),
-                     nsPrintfCString("%d", clampedOomScoreAdj).get()))
-    {
-      int oomAdj = OomAdjOfOomScoreAdj(clampedOomScoreAdj);
-
-      WriteToFile(nsPrintfCString("/proc/%d/oom_adj", aPid).get(),
-                  nsPrintfCString("%d", oomAdj).get());
-    }
+  int clampedOomScoreAdj = clamped<int>(oomScoreAdj, OOM_SCORE_ADJ_MIN,
+                                                     OOM_SCORE_ADJ_MAX);
+  if (clampedOomScoreAdj != oomScoreAdj) {
+    HAL_LOG("Clamping OOM adjustment for pid %d to %d", aPid,
+            clampedOomScoreAdj);
   } else {
-    HAL_ERR("Unable to read oom_score_adj pref for priority %s; "
-            "are the prefs messed up?", ProcessPriorityToString(aPriority));
-    MOZ_ASSERT(false);
+    HAL_LOG("Setting OOM adjustment for pid %d to %d", aPid,
+            clampedOomScoreAdj);
   }
 
-  int32_t nice = 0;
+  // We try the newer interface first, and fall back to the older interface
+  // on failure.
+
+  if (!WriteToFile(nsPrintfCString("/proc/%d/oom_score_adj", aPid).get(),
+                   nsPrintfCString("%d", clampedOomScoreAdj).get()))
+  {
+    int oomAdj = OomAdjOfOomScoreAdj(clampedOomScoreAdj);
+
+    WriteToFile(nsPrintfCString("/proc/%d/oom_adj", aPid).get(),
+                nsPrintfCString("%d", oomAdj).get());
+  }
+
+  int nice = 0;
 
   if (aCPUPriority == PROCESS_CPU_PRIORITY_NORMAL) {
-    rv = Preferences::GetInt(
-      nsPrintfCString("hal.processPriorityManager.gonk.%s.Nice",
-                      ProcessPriorityToString(aPriority)).get(),
-      &nice);
+    nice = prefs.priorities[aPriority].nice;
   } else if (aCPUPriority == PROCESS_CPU_PRIORITY_LOW) {
-    rv = Preferences::GetInt("hal.processPriorityManager.gonk.LowCPUNice",
-                             &nice);
+    nice = prefs.lowPriorityNice;
   } else {
-    HAL_ERR("Unable to read niceness pref for priority %s; "
-            "are the prefs messed up?", ProcessPriorityToString(aPriority));
+    HAL_ERR("Unknown aCPUPriority value %d", aCPUPriority);
     MOZ_ASSERT(false);
-    rv = NS_ERROR_FAILURE;
+    return;
   }
 
-  if (NS_SUCCEEDED(rv)) {
-    HAL_LOG("Setting nice for pid %d to %d", aPid, nice);
-    SetNiceForPid(aPid, nice);
-  }
+  HAL_LOG("Setting nice for pid %d to %d", aPid, nice);
+  SetNiceForPid(aPid, nice);
 }
 
 static bool
 IsValidRealTimePriority(int aValue, int aSchedulePolicy)
 {
   return (aValue >= sched_get_priority_min(aSchedulePolicy)) &&
          (aValue <= sched_get_priority_max(aSchedulePolicy));
 }
@@ -1603,47 +1638,86 @@ SetRealTimeThreadPriority(pid_t aTid,
   int rv = sched_setscheduler(aTid, policy, &schedParam);
 
   if (rv) {
     HAL_LOG("Failed to set thread %d to real time priority level %s; error %s",
             aTid, ThreadPriorityToString(aThreadPriority), strerror(errno));
   }
 }
 
+/*
+ * Used to store the nice value adjustments and real time priorities for the
+ * various thread priority levels.
+ */
+struct ThreadPriorityPrefs {
+  bool initialized;
+  struct {
+    int nice;
+    int realTime;
+  } priorities[NUM_THREAD_PRIORITY];
+};
+
+/*
+ * Reads the preferences for the various process priority levels and sets up
+ * watchers so that if they're dynamically changed the change is reflected on
+ * the appropriate variables.
+ */
+void
+EnsureThreadPriorityPrefs(ThreadPriorityPrefs* prefs)
+{
+  if (prefs->initialized) {
+    return;
+  }
+
+  for (int i = THREAD_PRIORITY_COMPOSITOR; i < NUM_THREAD_PRIORITY; i++) {
+    ThreadPriority priority = static_cast<ThreadPriority>(i);
+
+    // Read the nice values
+    const char* threadPriorityStr = ThreadPriorityToString(priority);
+    nsPrintfCString niceStr("hal.gonk.%s.nice", threadPriorityStr);
+    Preferences::AddIntVarCache(&prefs->priorities[i].nice, niceStr.get());
+
+    // Read the real-time priorities
+    nsPrintfCString realTimeStr("hal.gonk.%s.rt_priority", threadPriorityStr);
+    Preferences::AddIntVarCache(&prefs->priorities[i].realTime,
+                                realTimeStr.get());
+  }
+
+  prefs->initialized = true;
+}
+
 static void
 SetThreadPriority(pid_t aTid, hal::ThreadPriority aThreadPriority)
 {
   // See bug 999115, we can only read preferences on the main thread otherwise
   // we create a race condition in HAL
   MOZ_ASSERT(NS_IsMainThread(), "Can only set thread priorities on main thread");
   MOZ_ASSERT(aThreadPriority >= 0);
 
-  const char* threadPriorityStr;
+  static ThreadPriorityPrefs prefs = { 0 };
+  EnsureThreadPriorityPrefs(&prefs);
+
   switch (aThreadPriority) {
     case THREAD_PRIORITY_COMPOSITOR:
-      threadPriorityStr = ThreadPriorityToString(aThreadPriority);
       break;
     default:
       HAL_ERR("Unrecognized thread priority %d; Doing nothing",
               aThreadPriority);
       return;
   }
 
-  int realTimePriority = Preferences::GetInt(
-    nsPrintfCString("hal.gonk.%s.rt_priority", threadPriorityStr).get());
+  int realTimePriority = prefs.priorities[aThreadPriority].realTime;
 
   if (IsValidRealTimePriority(realTimePriority, SCHED_FIFO)) {
     SetRealTimeThreadPriority(aTid, aThreadPriority, realTimePriority);
     return;
   }
 
-  int niceValue = Preferences::GetInt(
-    nsPrintfCString("hal.gonk.%s.nice", threadPriorityStr).get());
-
-  SetThreadNiceValue(aTid, aThreadPriority, niceValue);
+  SetThreadNiceValue(aTid, aThreadPriority,
+                     prefs.priorities[aThreadPriority].nice);
 }
 
 namespace {
 
 /**
  * This class sets the priority of threads given the kernel thread's id and a
  * value taken from hal::ThreadPriority.
  *
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -834,58 +834,22 @@ js::obj_getOwnPropertyDescriptor(JSConte
     if (!GetFirstArgumentAsObject(cx, args, "Object.getOwnPropertyDescriptor", &obj))
         return false;
     RootedId id(cx);
     if (!ValueToId<CanGC>(cx, args.get(1), &id))
         return false;
     return GetOwnPropertyDescriptor(cx, obj, id, args.rval());
 }
 
-// ES6 draft rev25 (2014/05/22) 19.1.2.14 Object.keys(O)
+// ES6 draft rev27 (2014/08/24) 19.1.2.14 Object.keys(O)
 static bool
 obj_keys(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-
-    // Steps 1-2.
-    RootedObject obj(cx);
-    if (!GetFirstArgumentAsObject(cx, args, "Object.keys", &obj))
-        return false;
-
-    // Steps 3-10. Since JSITER_SYMBOLS and JSITER_HIDDEN are not passed,
-    // GetPropertyNames performs the type check in step 10.c. and the
-    // [[Enumerable]] check specified in step 10.c.iii.
-    AutoIdVector props(cx);
-    if (!GetPropertyNames(cx, obj, JSITER_OWNONLY, &props))
-        return false;
-
-    AutoValueVector namelist(cx);
-    if (!namelist.reserve(props.length()))
-        return false;
-    for (size_t i = 0, len = props.length(); i < len; i++) {
-        jsid id = props[i];
-        JSString *str;
-        if (JSID_IS_STRING(id)) {
-            str = JSID_TO_STRING(id);
-        } else {
-            str = Int32ToString<CanGC>(cx, JSID_TO_INT(id));
-            if (!str)
-                return false;
-        }
-        namelist.infallibleAppend(StringValue(str));
-    }
-
-    // Step 11.
-    MOZ_ASSERT(props.length() <= UINT32_MAX);
-    JSObject *aobj = NewDenseCopiedArray(cx, uint32_t(namelist.length()), namelist.begin());
-    if (!aobj)
-        return false;
-
-    args.rval().setObject(*aobj);
-    return true;
+    return GetOwnPropertyKeys(cx, args, JSITER_OWNONLY);
 }
 
 /* ES6 draft 15.2.3.16 */
 static bool
 obj_is(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/strict-compare-same-operands.js
@@ -0,0 +1,49 @@
+function f(l, m) {
+    var a = NaN;
+    var b = 13;
+    var c = "test";
+    var d = undefined;
+    var e = null;
+    var f = 15.7;
+    var g = Math.fround(189777.111);
+    var h = "ABC";
+    var i = String.fromCharCode(65, 65, 65);
+    var j = {};
+    var k = Math.fround("".charCodeAt(15));
+
+    // Special case rigt here:
+    assertEq(a === a, false);
+    assertEq(a !== a, true);
+    assertEq(k === k, false);
+    assertEq(k !== k, true);
+    assertEq(l === l, false);
+    assertEq(l !== l, true);
+
+    assertEq(b === b, true);
+    assertEq(b !== b, false);
+    assertEq(c === c, true);
+    assertEq(c !== c, false);
+    assertEq(d === d, true);
+    assertEq(d !== d, false);
+    assertEq(e === e, true);
+    assertEq(e !== e, false);
+    assertEq(f === f, true);
+    assertEq(f !== f, false);
+    assertEq(g === g, true);
+    assertEq(g !== g, false);
+    assertEq(h === h, true);
+    assertEq(h !== h, false);
+    assertEq(i === i, true);
+    assertEq(i !== i, false);
+    assertEq(j === j, true);
+    assertEq(j !== j, false);
+    assertEq(m === m, true);
+    assertEq(m !== m, false);
+}
+
+function test() {
+    for (var i = 0; i < 100; i++)
+        f("".charCodeAt(15), 42);
+}
+
+test();
--- a/js/src/jit/IonTypes.h
+++ b/js/src/jit/IonTypes.h
@@ -528,16 +528,26 @@ IsNullOrUndefined(MIRType type)
 }
 
 static inline bool
 IsSimdType(MIRType type)
 {
     return type == MIRType_Int32x4 || type == MIRType_Float32x4;
 };
 
+static inline bool
+IsMagicType(MIRType type)
+{
+    return type == MIRType_MagicHole ||
+           type == MIRType_MagicOptimizedOut ||
+           type == MIRType_MagicIsConstructing ||
+           type == MIRType_MagicOptimizedArguments ||
+           type == MIRType_MagicUninitializedLexical;
+}
+
 // Returns the number of vector elements (hereby called "length") for a given
 // SIMD kind. It is the Y part of the name "Foo x Y".
 static inline unsigned
 SimdTypeToLength(MIRType type)
 {
     MOZ_ASSERT(IsSimdType(type));
     switch (type) {
       case MIRType_Int32x4:
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -2068,21 +2068,30 @@ IonBuilder::inlineToObject(CallInfo &cal
 }
 
 IonBuilder::InliningStatus
 IonBuilder::inlineToInteger(CallInfo &callInfo)
 {
     if (callInfo.argc() != 1 || callInfo.constructing())
         return InliningStatus_NotInlined;
 
-    MIRType type = callInfo.getArg(0)->type();
+    MDefinition *input = callInfo.getArg(0);
 
-    // Only optimize cases where input is number, null, or boolean
-    if (!IsNumberType(type) && type != MIRType_Null && type != MIRType_Boolean)
+    // Only optimize cases where input contains only number, null or boolean
+    if (input->mightBeType(MIRType_Object) ||
+        input->mightBeType(MIRType_String) ||
+        input->mightBeType(MIRType_Symbol) ||
+        input->mightBeType(MIRType_Undefined) ||
+        input->mightBeMagicType())
+    {
         return InliningStatus_NotInlined;
+    }
+
+    MOZ_ASSERT(input->type() == MIRType_Value || input->type() == MIRType_Null ||
+               input->type() == MIRType_Boolean || IsNumberType(input->type()));
 
     // Only optimize cases where output is int32
     if (getInlineReturnType() != MIRType_Int32)
         return InliningStatus_NotInlined;
 
     callInfo.setImplicitlyUsedUnchecked();
 
     MToInt32 *toInt32 = MToInt32::New(alloc(), callInfo.getArg(0));
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -2834,20 +2834,59 @@ MClampToUint8::foldsTo(TempAllocator &al
             int32_t clamped = ClampIntForUint8Array(v.toInt32());
             return MConstant::New(alloc, Int32Value(clamped));
         }
     }
     return this;
 }
 
 bool
+MCompare::tryFoldEqualOperands(bool *result)
+{
+    if (lhs() != rhs())
+        return false;
+
+    // Intuitively somebody would think that if lhs == rhs,
+    // then we can just return true. (Or false for !==)
+    // However NaN !== NaN is true! So we spend some time trying
+    // to eliminate this case.
+
+    if (jsop() != JSOP_STRICTEQ && jsop() != JSOP_STRICTNE)
+        return false;
+
+    if (compareType_ == Compare_Unknown)
+        return false;
+
+    MOZ_ASSERT(compareType_ == Compare_Undefined || compareType_ == Compare_Null ||
+               compareType_ == Compare_Boolean || compareType_ == Compare_Int32 ||
+               compareType_ == Compare_Int32MaybeCoerceBoth ||
+               compareType_ == Compare_Int32MaybeCoerceLHS ||
+               compareType_ == Compare_Int32MaybeCoerceRHS || compareType_ == Compare_UInt32 ||
+               compareType_ == Compare_Double || compareType_ == Compare_DoubleMaybeCoerceLHS ||
+               compareType_ == Compare_DoubleMaybeCoerceRHS || compareType_ == Compare_Float32 ||
+               compareType_ == Compare_String || compareType_ == Compare_StrictString ||
+               compareType_ == Compare_Object || compareType_ == Compare_Value);
+
+    if (isDoubleComparison() || isFloat32Comparison()) {
+        if (!operandsAreNeverNaN())
+            return false;
+    }
+
+    *result = (jsop() == JSOP_STRICTEQ);
+    return true;
+}
+
+bool
 MCompare::tryFold(bool *result)
 {
     JSOp op = jsop();
 
+    if (tryFoldEqualOperands(result))
+        return true;
+
     if (compareType_ == Compare_Null || compareType_ == Compare_Undefined) {
         MOZ_ASSERT(op == JSOP_EQ || op == JSOP_STRICTEQ ||
                    op == JSOP_NE || op == JSOP_STRICTNE);
 
         // The LHS is the value we want to test against null or undefined.
         switch (lhs()->type()) {
           case MIRType_Value:
             return false;
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -591,16 +591,26 @@ class MDefinition : public MNode
             return true;
 
         if (MIRType_Value != this->type())
             return false;
 
         return !resultTypeSet() || resultTypeSet()->mightBeMIRType(type);
     }
 
+    bool mightBeMagicType() const {
+        if (IsMagicType(type()))
+            return true;
+
+        if (MIRType_Value != type())
+            return false;
+
+        return !resultTypeSet() || resultTypeSet()->hasType(types::Type::MagicArgType());
+    }
+
     // Float32 specialization operations (see big comment in IonAnalysis before the Float32
     // specialization algorithm).
     virtual bool isFloat32Commutative() const { return false; }
     virtual bool canProduceFloat32() const { return false; }
     virtual bool canConsumeFloat32(MUse *use) const { return false; }
     virtual void trySpecializeFloat32(TempAllocator &alloc) {}
 #ifdef DEBUG
     // Used during the pass that checks that Float32 flow into valid MDefinitions
@@ -3468,16 +3478,18 @@ class MCompare
         // Both sides of the compare can be Float32
         return compareType_ == Compare_Float32;
     }
 # endif
 
     ALLOW_CLONE(MCompare)
 
   protected:
+    bool tryFoldEqualOperands(bool *result);
+
     bool congruentTo(const MDefinition *ins) const {
         if (!binaryCongruentTo(ins))
             return false;
         return compareType() == ins->toCompare()->compareType() &&
                jsop() == ins->toCompare()->jsop();
     }
 };
 
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Object/keys.js
@@ -0,0 +1,23 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+var BUGNUMBER = 1038545;
+var summary = "Coerce the argument passed to Object.keys using ToObject";
+print(BUGNUMBER + ": " + summary);
+
+assertThrowsInstanceOf(() => Object.keys(), TypeError);
+assertThrowsInstanceOf(() => Object.keys(undefined), TypeError);
+assertThrowsInstanceOf(() => Object.keys(null), TypeError);
+
+assertDeepEq(Object.keys(1), []);
+assertDeepEq(Object.keys(true), []);
+if (typeof Symbol === "function") {
+    assertDeepEq(Object.keys(Symbol("foo")), []);
+}
+
+assertDeepEq(Object.keys("foo"), ["0", "1", "2"]);
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
--- a/layout/style/FontFaceSet.cpp
+++ b/layout/style/FontFaceSet.cpp
@@ -350,24 +350,32 @@ FontFaceSet::Length()
 static PLDHashOperator DestroyIterator(nsPtrHashKey<nsFontFaceLoader>* aKey,
                                        void* aUserArg)
 {
   aKey->GetKey()->Cancel();
   return PL_DHASH_REMOVE;
 }
 
 void
+FontFaceSet::DisconnectFromRule(FontFace* aFontFace)
+{
+  nsCSSFontFaceRule* rule = aFontFace->GetRule();
+  aFontFace->DisconnectFromRule();
+  mRuleFaceMap.Remove(rule);
+}
+
+void
 FontFaceSet::DestroyUserFontSet()
 {
   Disconnect();
   mDocument = nullptr;
   mPresContext = nullptr;
   mLoaders.EnumerateEntries(DestroyIterator, nullptr);
   for (size_t i = 0; i < mRuleFaces.Length(); i++) {
-    mRuleFaces[i].mFontFace->DisconnectFromRule();
+    DisconnectFromRule(mRuleFaces[i].mFontFace);
     mRuleFaces[i].mFontFace->SetUserFontEntry(nullptr);
   }
   for (size_t i = 0; i < mNonRuleFaces.Length(); i++) {
     mNonRuleFaces[i]->SetUserFontEntry(nullptr);
   }
   for (size_t i = 0; i < mUnavailableFaces.Length(); i++) {
     mUnavailableFaces[i]->SetUserFontEntry(nullptr);
   }
@@ -582,17 +590,17 @@ FontFaceSet::UpdateRules(const nsTArray<
         }
       }
 
       // Any left over FontFace objects should also cease being rule backed.
       MOZ_ASSERT(!mUnavailableFaces.Contains(f),
                  "FontFace should not occur in mUnavailableFaces twice");
 
       mUnavailableFaces.AppendElement(f);
-      f->DisconnectFromRule();
+      DisconnectFromRule(f);
     }
   }
 
   if (modified) {
     IncrementGeneration(true);
     mHasLoadingFontFacesIsDirty = true;
     CheckLoadingStarted();
     CheckLoadingFinished();
@@ -978,17 +986,17 @@ FontFaceSet::FindRuleForUserFontEntry(gf
     }
   }
   return nullptr;
 }
 
 gfxUserFontEntry*
 FontFaceSet::FindUserFontEntryForRule(nsCSSFontFaceRule* aRule)
 {
-  FontFace* f = aRule->GetFontFace();
+  FontFace* f = mRuleFaceMap.Get(aRule);
   if (f) {
     return f->GetUserFontEntry();
   }
   return nullptr;
 }
 
 nsresult
 FontFaceSet::LogMessage(gfxUserFontEntry* aUserFontEntry,
@@ -1263,27 +1271,33 @@ FontFaceSet::DoRebuildUserFontSet()
   }
 
   mPresContext->RebuildUserFontSet();
 }
 
 FontFace*
 FontFaceSet::FontFaceForRule(nsCSSFontFaceRule* aRule)
 {
-  FontFace* f = aRule->GetFontFace();
+  MOZ_ASSERT(aRule);
+
+  FontFace* f = mRuleFaceMap.Get(aRule);
   if (f) {
+    MOZ_ASSERT(f->GetFontFaceSet() == this,
+               "existing FontFace is from another FontFaceSet?");
     return f;
   }
 
   // We might be creating a FontFace object for an @font-face rule that we are
   // just about to create a user font entry for, so entry might be null.
   gfxUserFontEntry* entry = FindUserFontEntryForRule(aRule);
   nsRefPtr<FontFace> newFontFace =
     FontFace::CreateForRule(GetParentObject(), mPresContext, aRule, entry);
-  aRule->SetFontFace(newFontFace);
+  MOZ_ASSERT(newFontFace->GetFontFaceSet() == this,
+             "new FontFace is from another FontFaceSet?");
+  mRuleFaceMap.Put(aRule, newFontFace);
   return newFontFace;
 }
 
 void
 FontFaceSet::AddUnavailableFontFace(FontFace* aFontFace)
 {
   MOZ_ASSERT(!aFontFace->HasRule());
   MOZ_ASSERT(!aFontFace->IsInFontFaceSet());
--- a/layout/style/FontFaceSet.h
+++ b/layout/style/FontFaceSet.h
@@ -192,16 +192,22 @@ private:
   bool HasAvailableFontFace(FontFace* aFontFace);
 
   /**
    * Removes any listeners and observers.
    */
   void Disconnect();
 
   /**
+   * Calls DisconnectFromRule on the given FontFace and removes its entry from
+   * mRuleFaceMap.
+   */
+  void DisconnectFromRule(FontFace* aFontFace);
+
+  /**
    * Returns whether there might be any pending font loads, which should cause
    * the mReady Promise not to be resolved yet.
    */
   bool MightHavePendingFontLoads();
 
   /**
    * Checks to see whether it is time to replace mReady and dispatch a
    * "loading" event.
@@ -300,16 +306,23 @@ private:
   // The non rule backed FontFace objects that have been added to this
   // FontFaceSet and their corresponding user font entries.
   nsTArray<nsRefPtr<FontFace>> mNonRuleFaces;
 
   // The non rule backed FontFace objects that have not been added to
   // this FontFaceSet.
   nsTArray<FontFace*> mUnavailableFaces;
 
+  // Map of nsCSSFontFaceRule objects to FontFace objects.  We hold a weak
+  // reference to both; for actively used FontFaces, mRuleFaces will hold
+  // a strong reference to the FontFace and the FontFace will hold on to
+  // the nsCSSFontFaceRule.  FontFaceSet::DisconnectFromRule will ensure its
+  // entry in this array will be removed.
+  nsDataHashtable<nsPtrHashKey<nsCSSFontFaceRule>, FontFace*> mRuleFaceMap;
+
   // The overall status of the loading or loaded fonts in the FontFaceSet.
   mozilla::dom::FontFaceSetLoadStatus mStatus;
 
   // Whether mNonRuleFaces has changed since last time UpdateRules ran.
   bool mNonRuleFacesDirty;
 
   // Whether we have called MaybeResolve() on mReady.
   bool mReadyIsResolved;
--- a/layout/style/test/test_font_loading_api.html
+++ b/layout/style/test/test_font_loading_api.html
@@ -300,16 +300,19 @@ function runTest() {
           is(aError.name, "SyntaxError", "FontFace.loaded with invalid family name " + aFamilyName + " should be rejected with a SyntaxError (TEST 17)");
         });
       });
     });
     return familyTests;
 
   }).then(function() {
 
+    // XXX Disabled this sub-test due to intermittent failures (bug 1076803).
+    return;
+
     // (TEST 18) Test passing valid url() source strings to the FontFace
     // constructor.
     var srcTests = Promise.resolve();
     gCSSFontFaceDescriptors.src.values.forEach(function(aSrc) {
       srcTests = srcTests.then(function() {
         var face = new FontFace("test", aSrc);
         return face.load().then(function() {
           ok(true, "FontFace should load with valid url() src " + aSrc + " (TEST 18)");
--- a/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp
@@ -3563,36 +3563,39 @@ status_t MPEG4Source::fragmentedRead(
     bool isSyncSample = false;
     bool newBuffer = false;
     if (mBuffer == NULL) {
         newBuffer = true;
 
         if (mCurrentSampleIndex >= mCurrentSamples.size()) {
             // move to next fragment
             off64_t nextMoof = mNextMoofOffset; // lastSample.offset + lastSample.size;
-
-            // If we're pointing to a sidx box then we skip it.
-            uint32_t hdr[2];
-            if (mDataSource->readAt(nextMoof, hdr, 8) < 8) {
-                return ERROR_END_OF_STREAM;
-            }
-            uint64_t chunk_size = ntohl(hdr[0]);
-            uint32_t chunk_type = ntohl(hdr[1]);
-            if (chunk_type == FOURCC('s', 'i', 'd', 'x')) {
-                nextMoof += chunk_size;
-            }
-
-            mCurrentMoofOffset = nextMoof;
             mCurrentSamples.clear();
             mCurrentSampleIndex = 0;
             mTrackFragmentData.mPresent = false;
-            parseChunk(&nextMoof);
-            if (mCurrentSampleIndex >= mCurrentSamples.size()) {
-                return ERROR_END_OF_STREAM;
-            }
+            uint32_t hdr[2];
+            do {
+                if (mDataSource->readAt(nextMoof, hdr, 8) < 8) {
+                    return ERROR_END_OF_STREAM;
+                }
+                uint64_t chunk_size = ntohl(hdr[0]);
+                uint32_t chunk_type = ntohl(hdr[1]);
+
+                // If we're pointing to a segment type or sidx box then we skip them.
+                if (chunk_type == FOURCC('s', 't', 'y', 'p') ||
+                    chunk_type == FOURCC('s', 'i', 'd', 'x')) {
+                    nextMoof += chunk_size;
+                    continue;
+                }
+                mCurrentMoofOffset = nextMoof;
+                status_t ret = parseChunk(&nextMoof);
+                if (ret != OK) {
+                    return ret;
+                }
+            } while (mCurrentSamples.size() == 0);
 
             if (mTrackFragmentData.mPresent) {
                 mCurrentTime = mTrackFragmentData.mBaseMediaDecodeTime;
             }
         }
 
         const Sample *smpl = &mCurrentSamples[mCurrentSampleIndex];
         offset = smpl->offset;
--- a/media/webrtc/signaling/src/sipcc/core/sdp/sdp_token.c
+++ b/media/webrtc/signaling/src/sipcc/core/sdp/sdp_token.c
@@ -1370,49 +1370,49 @@ sdp_result_e sdp_parse_media (sdp_t *sdp
      * transport/profile types per line, so these are handled differently. */
     if ((mca_p->transport == SDP_TRANSPORT_AAL2_ITU) ||
         (mca_p->transport == SDP_TRANSPORT_AAL2_ATMF) ||
         (mca_p->transport == SDP_TRANSPORT_AAL2_CUSTOM)) {
 
         if (sdp_parse_multiple_profile_payload_types(sdp_p, mca_p, ptr) !=
             SDP_SUCCESS) {
             sdp_p->conf_p->num_invalid_param++;
-	    SDP_FREE(mca_p);
+            SDP_FREE(mca_p);
             return (SDP_INVALID_PARAMETER);
         }
-    } else {
-        /* Transport is a non-AAL2 type.  Parse payloads normally. */
-        sdp_parse_payload_types(sdp_p, mca_p, ptr);
-    }
-
     /* Parse DTLS/SCTP port */
-    if (mca_p->transport == SDP_TRANSPORT_DTLSSCTP) {
+    } else if (mca_p->transport == SDP_TRANSPORT_DTLSSCTP) {
         ptr = sdp_getnextstrtok(ptr, port, sizeof(port), " \t", &result);
         if (result != SDP_SUCCESS) {
             sdp_parse_error(sdp_p->peerconnection,
                 "%s No sctp port specified in m= media line, "
                 "parse failed.", sdp_p->debug_str);
             SDP_FREE(mca_p);
             sdp_p->conf_p->num_invalid_param++;
             return (SDP_INVALID_PARAMETER);
         }
         port_ptr = port;
 
         if (sdp_getchoosetok(port_ptr, &port_ptr, "/ \t", &result)) {
-        	sctp_port = SDP_CHOOSE_PARAM;
+            sctp_port = SDP_CHOOSE_PARAM;
         } else {
-        	sctp_port = sdp_getnextnumtok(port_ptr, (const char **)&port_ptr,
+            sctp_port = sdp_getnextnumtok(port_ptr, (const char **)&port_ptr,
                                            "/ \t", &result);
             if (result != SDP_SUCCESS) {
-            	return (SDP_INVALID_PARAMETER);
+                return (SDP_INVALID_PARAMETER);
             }
             mca_p->sctpport = sctp_port;
         }
+    } else {
+        /* Transport is a non-AAL2 type and not SCTP.  Parse payloads
+           normally. */
+        sdp_parse_payload_types(sdp_p, mca_p, ptr);
     }
 
+
     /* Media line params are valid.  Add it into the SDP. */
     sdp_p->mca_count++;
     if (sdp_p->mca_p == NULL) {
         sdp_p->mca_p = mca_p;
     } else {
         for (next_mca_p = sdp_p->mca_p; next_mca_p->next_p != NULL;
              next_mca_p = next_mca_p->next_p) {
             ; // Empty For
--- a/memory/replace/dmd/DMD.cpp
+++ b/memory/replace/dmd/DMD.cpp
@@ -31,21 +31,23 @@
 #include "nscore.h"
 #include "nsStackWalk.h"
 
 #include "js/HashTable.h"
 #include "js/Vector.h"
 
 #include "mozilla/Assertions.h"
 #include "mozilla/HashFunctions.h"
+#include "mozilla/IntegerPrintfMacros.h"
+#include "mozilla/JSONWriter.h"
 #include "mozilla/Likely.h"
 #include "mozilla/MemoryReporting.h"
 
-// CodeAddressService is defined entirely in the header, so this does not make DMD
-// depend on XPCOM's object file.
+// CodeAddressService is defined entirely in the header, so this does not make
+// DMD depend on XPCOM's object file.
 #include "CodeAddressService.h"
 
 // MOZ_REPLACE_ONLY_MEMALIGN saves us from having to define
 // replace_{posix_memalign,aligned_alloc,valloc}.  It requires defining
 // PAGE_SIZE.  Nb: sysconf() is expensive, but it's only used for (the obsolete
 // and rarely used) valloc.
 #define MOZ_REPLACE_ONLY_MEMALIGN 1
 
@@ -216,53 +218,41 @@ StatusMsg(const char* aFmt, ...)
 #endif
   va_end(ap);
 }
 
 /* static */ void
 InfallibleAllocPolicy::ExitOnFailure(const void* aP)
 {
   if (!aP) {
-    StatusMsg("out of memory;  aborting\n");
-    MOZ_CRASH();
+    MOZ_CRASH("DMD out of memory; aborting");
   }
 }
 
-void
-Writer::Write(const char* aFmt, ...) const
+class FpWriteFunc : public JSONWriteFunc
 {
-  va_list ap;
-  va_start(ap, aFmt);
-  mWriterFun(mWriteState, aFmt, ap);
-  va_end(ap);
-}
+public:
+  explicit FpWriteFunc(FILE* aFp) : mFp(aFp) {}
+  ~FpWriteFunc() { fclose(mFp); }
 
-#define W(...) aWriter.Write(__VA_ARGS__);
-
-#define WriteSeparator(...) \
-  W("#-----------------------------------------------------------------\n\n");
+  void Write(const char* aStr) { fputs(aStr, mFp); }
 
-MOZ_EXPORT void
-FpWrite(void* aWriteState, const char* aFmt, va_list aAp)
-{
-  FILE* fp = static_cast<FILE*>(aWriteState);
-  vfprintf(fp, aFmt, aAp);
-}
+private:
+  FILE* mFp;
+};
 
 static double
 Percent(size_t part, size_t whole)
 {
   return (whole == 0) ? 0 : 100 * (double)part / whole;
 }
 
-// Commifies the number and prepends a '~' if requested.  Best used with
-// |kBufLen| and |gBuf[1234]|, because they should be big enough for any number
-// we'll see.
+// Commifies the number.
 static char*
-Show(size_t n, char* buf, size_t buflen, bool addTilde = false)
+Show(size_t n, char* buf, size_t buflen)
 {
   int nc = 0, i = 0, lasti = buflen - 2;
   buf[lasti + 1] = '\0';
   if (n == 0) {
     buf[lasti - i] = '0';
     i++;
   } else {
     while (n > 0) {
@@ -273,37 +263,20 @@ Show(size_t n, char* buf, size_t buflen,
       }
       buf[lasti - i] = static_cast<char>((n % 10) + '0');
       i++;
       n /= 10;
     }
   }
   int firstCharIndex = lasti - i + 1;
 
-  if (addTilde) {
-    firstCharIndex--;
-    buf[firstCharIndex] = '~';
-  }
-
   MOZ_ASSERT(firstCharIndex >= 0);
   return &buf[firstCharIndex];
 }
 
-static const char*
-Plural(size_t aN)
-{
-  return aN == 1 ? "" : "s";
-}
-
-// Used by calls to Show().
-static const size_t kBufLen = 64;
-static char gBuf1[kBufLen];
-static char gBuf2[kBufLen];
-static char gBuf3[kBufLen];
-
 //---------------------------------------------------------------------------
 // Options (Part 1)
 //---------------------------------------------------------------------------
 
 class Options
 {
   template <typename T>
   struct NumOption
@@ -321,32 +294,33 @@ class Options
     Test,     // do some basic correctness tests
     Stress    // do some performance stress tests
   };
 
   char* mDMDEnvVar;   // a saved copy, for later printing
 
   NumOption<size_t>   mSampleBelowSize;
   NumOption<uint32_t> mMaxFrames;
-  NumOption<uint32_t> mMaxRecords;
+  bool mShowDumpStats;
   Mode mMode;
 
   void BadArg(const char* aArg);
   static const char* ValueIfMatch(const char* aArg, const char* aOptionName);
   static bool GetLong(const char* aArg, const char* aOptionName,
-                      long aMin, long aMax, long* aN);
+                      long aMin, long aMax, long* aValue);
+  static bool GetBool(const char* aArg, const char* aOptionName, bool* aValue);
 
 public:
   explicit Options(const char* aDMDEnvVar);
 
   const char* DMDEnvVar() const { return mDMDEnvVar; }
 
   size_t SampleBelowSize() const { return mSampleBelowSize.mActual; }
   size_t MaxFrames()       const { return mMaxFrames.mActual; }
-  size_t MaxRecords()      const { return mMaxRecords.mActual; }
+  size_t ShowDumpStats()   const { return mShowDumpStats; }
 
   void SetSampleBelowSize(size_t aN) { mSampleBelowSize.mActual = aN; }
 
   bool IsTestMode()   const { return mMode == Test; }
   bool IsStressMode() const { return mMode == Stress; }
 };
 
 static Options *gOptions;
@@ -673,40 +647,37 @@ typedef CodeAddressService<StringTable, 
 //---------------------------------------------------------------------------
 
 class StackTrace
 {
 public:
   static const uint32_t MaxFrames = 24;
 
 private:
-  uint32_t mLength;         // The number of PCs.
-  void* mPcs[MaxFrames];    // The PCs themselves.  If --max-frames is less
-                            // than 24, this array is bigger than necessary,
-                            // but that case is unusual.
+  uint32_t mLength;             // The number of PCs.
+  const void* mPcs[MaxFrames];  // The PCs themselves.  If --max-frames is less
+                                // than 24, this array is bigger than
+                                // necessary, but that case is unusual.
 
 public:
   StackTrace() : mLength(0) {}
 
   uint32_t Length() const { return mLength; }
-  void* Pc(uint32_t i) const { MOZ_ASSERT(i < mLength); return mPcs[i]; }
+  const void* Pc(uint32_t i) const
+  {
+    MOZ_ASSERT(i < mLength);
+    return mPcs[i];
+  }
 
   uint32_t Size() const { return mLength * sizeof(mPcs[0]); }
 
   // The stack trace returned by this function is interned in gStackTraceTable,
   // and so is immortal and unmovable.
   static const StackTrace* Get(Thread* aT);
 
-  void Sort()
-  {
-    qsort(mPcs, mLength, sizeof(mPcs[0]), StackTrace::Cmp);
-  }
-
-  void Print(const Writer& aWriter, CodeAddressService* aLocService) const;
-
   // Hash policy.
 
   typedef StackTrace* Lookup;
 
   static uint32_t hash(const StackTrace* const& aSt)
   {
     return mozilla::HashBytes(aSt->mPcs, aSt->Size());
   }
@@ -714,57 +685,45 @@ public:
   static bool match(const StackTrace* const& aA,
                     const StackTrace* const& aB)
   {
     return aA->mLength == aB->mLength &&
            memcmp(aA->mPcs, aB->mPcs, aA->Size()) == 0;
   }
 
 private:
-  static void StackWalkCallback(void* aPc, void* aSp, void* aClosure)
+  static void StackWalkCallback(uint32_t aFrameNumber, void* aPc, void* aSp,
+                                void* aClosure)
   {
     StackTrace* st = (StackTrace*) aClosure;
     MOZ_ASSERT(st->mLength < MaxFrames);
     st->mPcs[st->mLength] = aPc;
     st->mLength++;
-  }
-
-  static int Cmp(const void* aA, const void* aB)
-  {
-    const void* const a = *static_cast<const void* const*>(aA);
-    const void* const b = *static_cast<const void* const*>(aB);
-    if (a < b) return -1;
-    if (a > b) return  1;
-    return 0;
+    MOZ_ASSERT(st->mLength == aFrameNumber);
   }
 };
 
 typedef js::HashSet<StackTrace*, StackTrace, InfallibleAllocPolicy>
         StackTraceTable;
 static StackTraceTable* gStackTraceTable = nullptr;
 
+typedef js::HashSet<const StackTrace*, js::DefaultHasher<const StackTrace*>,
+                    InfallibleAllocPolicy>
+        StackTraceSet;
+
+typedef js::HashSet<const void*, js::DefaultHasher<const void*>,
+                    InfallibleAllocPolicy>
+        PointerSet;
+typedef js::HashMap<const void*, uint32_t, js::DefaultHasher<const void*>,
+                    InfallibleAllocPolicy>
+        PointerIdMap;
+
 // We won't GC the stack trace table until it this many elements.
 static uint32_t gGCStackTraceTableWhenSizeExceeds = 4 * 1024;
 
-void
-StackTrace::Print(const Writer& aWriter, CodeAddressService* aLocService) const
-{
-  if (mLength == 0) {
-    W("    (empty)\n");  // StackTrace::Get() must have failed
-    return;
-  }
-
-  static const size_t buflen = 1024;
-  char buf[buflen];
-  for (uint32_t i = 0; i < mLength; i++) {
-    aLocService->GetLocation(Pc(i), buf, buflen);
-    aWriter.Write("    %s\n", buf);
-  }
-}
-
 /* static */ const StackTrace*
 StackTrace::Get(Thread* aT)
 {
   MOZ_ASSERT(gStateLock->IsLocked());
   MOZ_ASSERT(aT->InterceptsAreBlocked());
 
   // On Windows, NS_StackWalk can acquire a lock from the shared library
   // loader.  Another thread might call malloc while holding that lock (when
@@ -792,19 +751,19 @@ StackTrace::Get(Thread* aT)
     // attempt to create a semaphore (which can happen in printf) could
     // deadlock.
     //
     // However, the most complex thing DMD does after Get() returns is to put
     // something in a hash table, which might call
     // InfallibleAllocPolicy::malloc_.  I'm not yet sure if this needs special
     // handling, hence the forced abort.  Sorry.  If you hit this, please file
     // a bug and CC nnethercote.
-    MOZ_CRASH();
+    MOZ_CRASH("unexpected case in StackTrace::Get()");
   } else {
-    MOZ_CRASH();  // should be impossible
+    MOZ_CRASH("impossible case in StackTrace::Get()");
   }
 
   StackTraceTable::AddPtr p = gStackTraceTable->lookupForAdd(&tmp);
   if (!p) {
     StackTrace* stnew = InfallibleAllocPolicy::new_<StackTrace>(tmp);
     (void)gStackTraceTable->add(p, stnew);
   }
   return *p;
@@ -893,16 +852,18 @@ public:
     : mPtr(aPtr),
       mReqSize(aReqSize),
       mAllocStackTrace_mSampled(aAllocStackTrace, aSampled),
       mReportStackTrace_mReportedOnAlloc()     // all fields get zeroed
   {
     MOZ_ASSERT(aAllocStackTrace);
   }
 
+  const void* Address() const { return mPtr; }
+
   size_t ReqSize() const { return mReqSize; }
 
   // Sampled blocks always have zero slop.
   size_t SlopSize() const
   {
     return IsSampled() ? 0 : MallocSizeOf(mPtr) - mReqSize;
   }
 
@@ -916,33 +877,50 @@ public:
     return mAllocStackTrace_mSampled.Tag();
   }
 
   const StackTrace* AllocStackTrace() const
   {
     return mAllocStackTrace_mSampled.Ptr();
   }
 
-  const StackTrace* ReportStackTrace1() const {
+  const StackTrace* ReportStackTrace1() const
+  {
     return mReportStackTrace_mReportedOnAlloc[0].Ptr();
   }
 
-  const StackTrace* ReportStackTrace2() const {
+  const StackTrace* ReportStackTrace2() const
+  {
     return mReportStackTrace_mReportedOnAlloc[1].Ptr();
   }
 
-  bool ReportedOnAlloc1() const {
+  bool ReportedOnAlloc1() const
+  {
     return mReportStackTrace_mReportedOnAlloc[0].Tag();
   }
 
-  bool ReportedOnAlloc2() const {
+  bool ReportedOnAlloc2() const
+  {
     return mReportStackTrace_mReportedOnAlloc[1].Tag();
   }
 
-  uint32_t NumReports() const {
+  void AddStackTracesToTable(StackTraceSet& aStackTraces) const
+  {
+    aStackTraces.put(AllocStackTrace());  // never null
+    const StackTrace* st;
+    if ((st = ReportStackTrace1())) {     // may be null
+      aStackTraces.put(st);
+    }
+    if ((st = ReportStackTrace2())) {     // may be null
+      aStackTraces.put(st);
+    }
+  }
+
+  uint32_t NumReports() const
+  {
     if (ReportStackTrace2()) {
       MOZ_ASSERT(ReportStackTrace1());
       return 2;
     }
     if (ReportStackTrace1()) {
       return 1;
     }
     return 0;
@@ -989,60 +967,47 @@ public:
   {
     return aB.mPtr == aPtr;
   }
 };
 
 typedef js::HashSet<Block, Block, InfallibleAllocPolicy> BlockTable;
 static BlockTable* gBlockTable = nullptr;
 
-typedef js::HashSet<const StackTrace*, js::DefaultHasher<const StackTrace*>,
-                    InfallibleAllocPolicy>
-        StackTraceSet;
-
 // Add a pointer to each live stack trace into the given StackTraceSet.  (A
 // stack trace is live if it's used by one of the live blocks.)
 static void
 GatherUsedStackTraces(StackTraceSet& aStackTraces)
 {
   MOZ_ASSERT(gStateLock->IsLocked());
   MOZ_ASSERT(Thread::Fetch()->InterceptsAreBlocked());
 
   aStackTraces.finish();
-  aStackTraces.init(1024);
+  aStackTraces.init(512);
 
   for (BlockTable::Range r = gBlockTable->all(); !r.empty(); r.popFront()) {
     const Block& b = r.front();
-    aStackTraces.put(b.AllocStackTrace());
-    aStackTraces.put(b.ReportStackTrace1());
-    aStackTraces.put(b.ReportStackTrace2());
+    b.AddStackTracesToTable(aStackTraces);
   }
-
-  // Any of the stack traces added above may have been null.  For the sake of
-  // cleanliness, don't leave the null pointer in the set.
-  aStackTraces.remove(nullptr);
 }
 
 // Delete stack traces that we aren't using, and compact our hashtable.
 static void
 GCStackTraces()
 {
   MOZ_ASSERT(gStateLock->IsLocked());
   MOZ_ASSERT(Thread::Fetch()->InterceptsAreBlocked());
 
   StackTraceSet usedStackTraces;
   GatherUsedStackTraces(usedStackTraces);
 
   // Delete all unused stack traces from gStackTraceTable.  The Enum destructor
   // will automatically rehash and compact the table.
-  for (StackTraceTable::Enum e(*gStackTraceTable);
-       !e.empty();
-       e.popFront()) {
+  for (StackTraceTable::Enum e(*gStackTraceTable); !e.empty(); e.popFront()) {
     StackTrace* const& st = e.front();
-
     if (!usedStackTraces.has(st)) {
       e.removeFront();
       InfallibleAllocPolicy::delete_(st);
     }
   }
 
   // Schedule a GC when we have twice as many stack traces as we had right after
   // this GC finished.
@@ -1245,203 +1210,16 @@ replace_free(void* aPtr)
   FreeCallback(aPtr, t);
   gMallocTable->free(aPtr);
 }
 
 namespace mozilla {
 namespace dmd {
 
 //---------------------------------------------------------------------------
-// Heap block records
-//---------------------------------------------------------------------------
-
-class RecordKey
-{
-public:
-  const StackTrace* const mAllocStackTrace;   // never null
-protected:
-  const StackTrace* const mReportStackTrace1; // nullptr if unreported
-  const StackTrace* const mReportStackTrace2; // nullptr if not 2x-reported
-
-public:
-  explicit RecordKey(const Block& aB)
-    : mAllocStackTrace(aB.AllocStackTrace()),
-      mReportStackTrace1(aB.ReportStackTrace1()),
-      mReportStackTrace2(aB.ReportStackTrace2())
-  {
-    MOZ_ASSERT(mAllocStackTrace);
-  }
-
-  // Hash policy.
-
-  typedef RecordKey Lookup;
-
-  static uint32_t hash(const RecordKey& aKey)
-  {
-    return mozilla::HashGeneric(aKey.mAllocStackTrace,
-                                aKey.mReportStackTrace1,
-                                aKey.mReportStackTrace2);
-  }
-
-  static bool match(const RecordKey& aA, const RecordKey& aB)
-  {
-    return aA.mAllocStackTrace   == aB.mAllocStackTrace &&
-           aA.mReportStackTrace1 == aB.mReportStackTrace1 &&
-           aA.mReportStackTrace2 == aB.mReportStackTrace2;
-  }
-};
-
-class RecordSize
-{
-  static const size_t kReqBits = sizeof(size_t) * 8 - 1;  // 31 or 63
-
-  size_t mReq;              // size requested
-  size_t mSlop:kReqBits;    // slop bytes
-  size_t mSampled:1;        // were one or more blocks contributing to this
-                            //   RecordSize sampled?
-public:
-  RecordSize()
-    : mReq(0),
-      mSlop(0),
-      mSampled(false)
-  {}
-
-  size_t Req()    const { return mReq; }
-  size_t Slop()   const { return mSlop; }
-  size_t Usable() const { return mReq + mSlop; }
-
-  bool IsSampled() const { return mSampled; }
-
-  void Add(const Block& aB)
-  {
-    mReq  += aB.ReqSize();
-    mSlop += aB.SlopSize();
-    mSampled = mSampled || aB.IsSampled();
-  }
-
-  void Add(const RecordSize& aRecordSize)
-  {
-    mReq  += aRecordSize.Req();
-    mSlop += aRecordSize.Slop();
-    mSampled = mSampled || aRecordSize.IsSampled();
-  }
-
-  static int CmpByUsable(const RecordSize& aA, const RecordSize& aB)
-  {
-    // Primary sort: put bigger usable sizes first.
-    if (aA.Usable() > aB.Usable()) return -1;
-    if (aA.Usable() < aB.Usable()) return  1;
-
-    // Secondary sort: put bigger requested sizes first.
-    if (aA.Req() > aB.Req()) return -1;
-    if (aA.Req() < aB.Req()) return  1;
-
-    // Tertiary sort: put non-sampled records before sampled records.
-    if (!aA.mSampled &&  aB.mSampled) return -1;
-    if ( aA.mSampled && !aB.mSampled) return  1;
-
-    return 0;
-  }
-};
-
-// A collection of one or more heap blocks with a common RecordKey.
-class Record : public RecordKey
-{
-  // The RecordKey base class serves as the key in RecordTables.  These two
-  // fields constitute the value, so it's ok for them to be |mutable|.
-  mutable uint32_t    mNumBlocks; // number of blocks with this RecordKey
-  mutable RecordSize mRecordSize; // combined size of those blocks
-
-public:
-  explicit Record(const RecordKey& aKey)
-    : RecordKey(aKey),
-      mNumBlocks(0),
-      mRecordSize()
-  {}
-
-  uint32_t NumBlocks() const { return mNumBlocks; }
-
-  const RecordSize& GetRecordSize() const { return mRecordSize; }
-
-  // This is |const| thanks to the |mutable| fields above.
-  void Add(const Block& aB) const
-  {
-    mNumBlocks++;
-    mRecordSize.Add(aB);
-  }
-
-  void Print(const Writer& aWriter, CodeAddressService* aLocService,
-             uint32_t aM, uint32_t aN, const char* aStr, const char* astr,
-             size_t aCategoryUsableSize, size_t aCumulativeUsableSize,
-             size_t aTotalUsableSize, bool aShowCategoryPercentage,
-             bool aShowReportedAt) const;
-
-  static int CmpByUsable(const void* aA, const void* aB)
-  {
-    const Record* const a = *static_cast<const Record* const*>(aA);
-    const Record* const b = *static_cast<const Record* const*>(aB);
-
-    return RecordSize::CmpByUsable(a->mRecordSize, b->mRecordSize);
-  }
-};
-
-typedef js::HashSet<Record, Record, InfallibleAllocPolicy> RecordTable;
-
-void
-Record::Print(const Writer& aWriter, CodeAddressService* aLocService,
-              uint32_t aM, uint32_t aN, const char* aStr, const char* astr,
-              size_t aCategoryUsableSize, size_t aCumulativeUsableSize,
-              size_t aTotalUsableSize, bool aShowCategoryPercentage,
-              bool aShowReportedAt) const
-{
-  bool showTilde = mRecordSize.IsSampled();
-
-  W("%s {\n", aStr);
-  W("  %s block%s in heap block record %s of %s\n",
-    Show(mNumBlocks, gBuf1, kBufLen, showTilde), Plural(mNumBlocks),
-    Show(aM, gBuf2, kBufLen),
-    Show(aN, gBuf3, kBufLen));
-
-  W("  %s bytes (%s requested / %s slop)\n",
-    Show(mRecordSize.Usable(), gBuf1, kBufLen, showTilde),
-    Show(mRecordSize.Req(),    gBuf2, kBufLen, showTilde),
-    Show(mRecordSize.Slop(),   gBuf3, kBufLen, showTilde));
-
-  W("  %4.2f%% of the heap (%4.2f%% cumulative)\n",
-    Percent(mRecordSize.Usable(), aTotalUsableSize),
-    Percent(aCumulativeUsableSize, aTotalUsableSize));
-
-  if (aShowCategoryPercentage) {
-    W("  %4.2f%% of %s (%4.2f%% cumulative)\n",
-      Percent(mRecordSize.Usable(), aCategoryUsableSize),
-      astr,
-      Percent(aCumulativeUsableSize, aCategoryUsableSize));
-  }
-
-  W("  Allocated at {\n");
-  mAllocStackTrace->Print(aWriter, aLocService);
-  W("  }\n");
-
-  if (aShowReportedAt) {
-    if (mReportStackTrace1) {
-      W("  Reported at {\n");
-      mReportStackTrace1->Print(aWriter, aLocService);
-      W("  }\n");
-    }
-    if (mReportStackTrace2) {
-      W("  Reported again at {\n");
-      mReportStackTrace2->Print(aWriter, aLocService);
-      W("  }\n");
-    }
-  }
-
-  W("}\n\n");
-}
-
-//---------------------------------------------------------------------------
 // Options (Part 2)
 //---------------------------------------------------------------------------
 
 // Given an |aOptionName| like "foo", succeed if |aArg| has the form "foo=blah"
 // (where "blah" is non-empty) and return the pointer to "blah".  |aArg| can
 // have leading space chars (but not other whitespace).
 const char*
 Options::ValueIfMatch(const char* aArg, const char* aOptionName)
@@ -1454,23 +1232,41 @@ Options::ValueIfMatch(const char* aArg, 
   }
   return nullptr;
 }
 
 // Extracts a |long| value for an option from an argument.  It must be within
 // the range |aMin..aMax| (inclusive).
 bool
 Options::GetLong(const char* aArg, const char* aOptionName,
-                 long aMin, long aMax, long* aN)
+                 long aMin, long aMax, long* aValue)
 {
   if (const char* optionValue = ValueIfMatch(aArg, aOptionName)) {
     char* endPtr;
-    *aN = strtol(optionValue, &endPtr, /* base */ 10);
-    if (!*endPtr && aMin <= *aN && *aN <= aMax &&
-        *aN != LONG_MIN && *aN != LONG_MAX) {
+    *aValue = strtol(optionValue, &endPtr, /* base */ 10);
+    if (!*endPtr && aMin <= *aValue && *aValue <= aMax &&
+        *aValue != LONG_MIN && *aValue != LONG_MAX) {
+      return true;
+    }
+  }
+  return false;
+}
+
+// Extracts a |bool| value for an option -- encoded as "yes" or "no" -- from an
+// argument.
+bool
+Options::GetBool(const char* aArg, const char* aOptionName, bool* aValue)
+{
+  if (const char* optionValue = ValueIfMatch(aArg, aOptionName)) {
+    if (strcmp(optionValue, "yes") == 0) {
+      *aValue = true;
+      return true;
+    }
+    if (strcmp(optionValue, "no") == 0) {
+      *aValue = false;
       return true;
     }
   }
   return false;
 }
 
 // The sample-below default is a prime number close to 4096.
 // - Why that size?  Because it's *much* faster but only moderately less precise
@@ -1479,17 +1275,17 @@ Options::GetLong(const char* aArg, const
 //   of 4096, for example, then our alloc counter would only take on even
 //   values, because jemalloc always rounds up requests sizes.  In contrast, a
 //   prime size will explore all possible values of the alloc counter.
 //
 Options::Options(const char* aDMDEnvVar)
   : mDMDEnvVar(InfallibleAllocPolicy::strdup_(aDMDEnvVar)),
     mSampleBelowSize(4093, 100 * 100 * 1000),
     mMaxFrames(StackTrace::MaxFrames, StackTrace::MaxFrames),
-    mMaxRecords(1000, 1000000),
+    mShowDumpStats(false),
     mMode(Normal)
 {
   char* e = mDMDEnvVar;
   if (strcmp(e, "1") != 0) {
     bool isEnd = false;
     while (!isEnd) {
       // Consume leading whitespace.
       while (isspace(*e)) {
@@ -1505,24 +1301,25 @@ Options::Options(const char* aDMDEnvVar)
         e++;
       }
       char replacedChar = *e;
       isEnd = replacedChar == '\0';
       *e = '\0';
 
       // Handle arg
       long myLong;
+      bool myBool;
       if (GetLong(arg, "--sample-below", 1, mSampleBelowSize.mMax, &myLong)) {
         mSampleBelowSize.mActual = myLong;
 
       } else if (GetLong(arg, "--max-frames", 1, mMaxFrames.mMax, &myLong)) {
         mMaxFrames.mActual = myLong;
 
-      } else if (GetLong(arg, "--max-records", 1, mMaxRecords.mMax, &myLong)) {
-        mMaxRecords.mActual = myLong;
+      } else if (GetBool(arg, "--show-dump-stats", &myBool)) {
+        mShowDumpStats = myBool;
 
       } else if (strcmp(arg, "--mode=normal") == 0) {
         mMode = Options::Normal;
       } else if (strcmp(arg, "--mode=test")   == 0) {
         mMode = Options::Test;
       } else if (strcmp(arg, "--mode=stress") == 0) {
         mMode = Options::Stress;
 
@@ -1555,49 +1352,49 @@ Options::BadArg(const char* aArg)
   StatusMsg("The following options are allowed;  defaults are shown in [].\n");
   StatusMsg("  --sample-below=<1..%d> Sample blocks smaller than this [%d]\n",
             int(mSampleBelowSize.mMax),
             int(mSampleBelowSize.mDefault));
   StatusMsg("                               (prime numbers are recommended)\n");
   StatusMsg("  --max-frames=<1..%d>         Max. depth of stack traces [%d]\n",
             int(mMaxFrames.mMax),
             int(mMaxFrames.mDefault));
-  StatusMsg("  --max-records=<1..%u>   Max. number of records printed [%u]\n",
-            mMaxRecords.mMax,
-            mMaxRecords.mDefault);
+  StatusMsg("  --show-dump-stats=<yes|no>   Show stats about dumps? [no]\n");
   StatusMsg("  --mode=<normal|test|stress>  Mode of operation [normal]\n");
   StatusMsg("\n");
   exit(1);
 }
 
 //---------------------------------------------------------------------------
 // DMD start-up
 //---------------------------------------------------------------------------
 
 #ifdef XP_MACOSX
 static void
-NopStackWalkCallback(void* aPc, void* aSp, void* aClosure)
+NopStackWalkCallback(uint32_t aFrameNumber, void* aPc, void* aSp,
+                     void* aClosure)
 {
 }
 #endif
 
 // Note that fopen() can allocate.
 static FILE*
 OpenOutputFile(const char* aFilename)
 {
   FILE* fp = fopen(aFilename, "w");
   if (!fp) {
     StatusMsg("can't create %s file: %s\n", aFilename, strerror(errno));
     exit(1);
   }
   return fp;
 }
 
-static void RunTestMode(FILE* fp);
-static void RunStressMode(FILE* fp);
+static void RunTestMode(UniquePtr<FpWriteFunc> aF1, UniquePtr<FpWriteFunc> aF2,
+                        UniquePtr<FpWriteFunc> aF3, UniquePtr<FpWriteFunc> aF4);
+static void RunStressMode(UniquePtr<FpWriteFunc> aF);
 
 // WARNING: this function runs *very* early -- before all static initializers
 // have run.  For this reason, non-scalar globals such as gStateLock and
 // gStackTraceTable are allocated dynamically (so we can guarantee their
 // construction in this function) rather than statically.
 static void
 Init(const malloc_table_t* aMallocTable)
 {
@@ -1645,37 +1442,38 @@ Init(const malloc_table_t* aMallocTable)
     gStackTraceTable = InfallibleAllocPolicy::new_<StackTraceTable>();
     gStackTraceTable->init(8192);
 
     gBlockTable = InfallibleAllocPolicy::new_<BlockTable>();
     gBlockTable->init(8192);
   }
 
   if (gOptions->IsTestMode()) {
-    // OpenOutputFile() can allocate.  So do this before setting
-    // gIsDMDRunning so those allocations don't show up in our results.  Once
-    // gIsDMDRunning is set we are intercepting malloc et al. in earnest.
-    FILE* fp = OpenOutputFile("test.dmd");
+    // Do all necessary allocations before setting gIsDMDRunning so those
+    // allocations don't show up in our results.  Once gIsDMDRunning is set we
+    // are intercepting malloc et al. in earnest.
+    auto f1 = MakeUnique<FpWriteFunc>(OpenOutputFile("full1.json"));
+    auto f2 = MakeUnique<FpWriteFunc>(OpenOutputFile("full2.json"));
+    auto f3 = MakeUnique<FpWriteFunc>(OpenOutputFile("full3.json"));
+    auto f4 = MakeUnique<FpWriteFunc>(OpenOutputFile("full4.json"));
     gIsDMDRunning = true;
 
     StatusMsg("running test mode...\n");
-    RunTestMode(fp);
+    RunTestMode(Move(f1), Move(f2), Move(f3), Move(f4));
     StatusMsg("finished test mode\n");
-    fclose(fp);
     exit(0);
   }
 
   if (gOptions->IsStressMode()) {
-    FILE* fp = OpenOutputFile("stress.dmd");
+    auto f = MakeUnique<FpWriteFunc>(OpenOutputFile("stress.json"));
     gIsDMDRunning = true;
 
     StatusMsg("running stress mode...\n");
-    RunStressMode(fp);
+    RunStressMode(Move(f));
     StatusMsg("finished stress mode\n");
-    fclose(fp);
     exit(0);
   }
 
   gIsDMDRunning = true;
 }
 
 //---------------------------------------------------------------------------
 // DMD reporting and unreporting
@@ -1714,66 +1512,23 @@ ReportOnAlloc(const void* aPtr)
 {
   ReportHelper(aPtr, /* onAlloc */ true);
 }
 
 //---------------------------------------------------------------------------
 // DMD output
 //---------------------------------------------------------------------------
 
-static void
-PrintSortedRecords(const Writer& aWriter, CodeAddressService* aLocService,
-                   int (*aCmp)(const void*, const void*),
-                   const char* aStr, const char* astr,
-                   const RecordTable& aRecordTable,
-                   size_t aCategoryUsableSize, size_t aTotalUsableSize,
-                   bool aShowCategoryPercentage, bool aShowReportedAt)
-{
-  StatusMsg("  creating and sorting %s heap block record array...\n", astr);
-
-  // Convert the table into a sorted array.
-  js::Vector<const Record*, 0, InfallibleAllocPolicy> recordArray;
-  recordArray.reserve(aRecordTable.count());
-  for (RecordTable::Range r = aRecordTable.all();
-       !r.empty();
-       r.popFront()) {
-    recordArray.infallibleAppend(&r.front());
-  }
-  qsort(recordArray.begin(), recordArray.length(), sizeof(recordArray[0]),
-        aCmp);
-
-  WriteSeparator();
-
-  if (recordArray.length() == 0) {
-    W("# no %s heap blocks\n\n", astr);
-    return;
-  }
-
-  StatusMsg("  printing %s heap block record array...\n", astr);
-  size_t cumulativeUsableSize = 0;
-
-  // Limit the number of records printed, because fix_linux_stack.py is too
-  // damn slow.  Note that we don't break out of this loop because we need to
-  // keep adding to |cumulativeUsableSize|.
-  uint32_t numRecords = recordArray.length();
-  uint32_t maxRecords = gOptions->MaxRecords();
-  for (uint32_t i = 0; i < numRecords; i++) {
-    const Record* r = recordArray[i];
-    cumulativeUsableSize += r->GetRecordSize().Usable();
-    if (i < maxRecords) {
-      r->Print(aWriter, aLocService, i+1, numRecords, aStr, astr,
-               aCategoryUsableSize, cumulativeUsableSize, aTotalUsableSize,
-               aShowCategoryPercentage, aShowReportedAt);
-    } else if (i == maxRecords) {
-      W("# %s: stopping after %s heap block records\n\n", aStr,
-        Show(maxRecords, gBuf1, kBufLen));
-    }
-  }
-  MOZ_ASSERT(aCategoryUsableSize == cumulativeUsableSize);
-}
+// The version number of the output format. Increment this if you make
+// backwards-incompatible changes to the format.
+//
+// Version history:
+// - 1: The original format (bug 1044709).
+//
+static const int kOutputVersionNumber = 1;
 
 // Note that, unlike most SizeOf* functions, this function does not take a
 // |mozilla::MallocSizeOf| argument.  That's because those arguments are
 // primarily to aid DMD track heap blocks... but DMD deliberately doesn't track
 // heap blocks it allocated for itself!
 //
 // SizeOfInternal should be called while you're holding the state lock and
 // while intercepts are blocked; SizeOf acquires the lock and blocks
@@ -1844,335 +1599,254 @@ ClearReports()
 }
 
 MOZ_EXPORT bool
 IsRunning()
 {
   return gIsDMDRunning;
 }
 
-// AnalyzeReports() and AnalyzeHeap() have a lot in common. This abstract class
-// encapsulates the operations that are not shared.
-class Analyzer
-{
-public:
-  virtual const char* AnalyzeFunctionName() const = 0;
-
-  virtual RecordTable* ProcessBlock(const Block& aBlock) = 0;
-
-  virtual void PrintRecords(const Writer& aWriter,
-                            CodeAddressService* aLocService) const = 0;
-  virtual void PrintSummary(const Writer& aWriter, bool aShowTilde) const = 0;
-  virtual void PrintStats(const Writer& aWriter) const = 0;
-
-  struct RecordKindData
-  {
-    RecordTable mRecordTable;
-    size_t mUsableSize;
-    size_t mNumBlocks;
-
-    explicit RecordKindData(size_t aN)
-      : mUsableSize(0), mNumBlocks(0)
-    {
-      mRecordTable.init(aN);
-    }
-
-    void processBlock(const Block& aBlock)
-    {
-      mUsableSize += aBlock.UsableSize();
-      mNumBlocks++;
-    }
-  };
-};
-
-class ReportsAnalyzer MOZ_FINAL : public Analyzer
+// This function converts an integer to base-32. |aBuf| must have space for at
+// least eight chars, which is the space needed to hold 'Dffffff' (including
+// the terminating null char), which is the base-32 representation of
+// 0xffffffff.
+//
+// We use base-32 values for indexing into the traceTable and the frameTable,
+// for the following reasons.
+//
+// - Base-32 gives more compact indices than base-16.
+//
+// - 32 is a power-of-two, which makes the necessary div/mod calculations fast.
+//
+// - We can (and do) choose non-numeric digits for base-32. When
+//   inspecting/debugging the JSON output, non-numeric indices are easier to
+//   search for than numeric indices.
+//
+char* Base32(uint32_t aN, char* aBuf, size_t aBufLen)
 {
-  RecordKindData mUnreported;
-  RecordKindData mOnceReported;
-  RecordKindData mTwiceReported;
-
-  size_t mTotalUsableSize;
-  size_t mTotalNumBlocks;
-
-public:
-  ReportsAnalyzer()
-    : mUnreported(1024), mOnceReported(1024), mTwiceReported(0),
-      mTotalUsableSize(0), mTotalNumBlocks(0)
-  {}
-
-  ~ReportsAnalyzer()
-  {
-    ClearReports();
-  }
-
-  virtual const char* AnalyzeFunctionName() const { return "AnalyzeReports"; }
-
-  virtual RecordTable* ProcessBlock(const Block& aBlock)
-  {
-    RecordKindData* data;
-    uint32_t numReports = aBlock.NumReports();
-    if (numReports == 0) {
-      data = &mUnreported;
-    } else if (numReports == 1) {
-      data = &mOnceReported;
-    } else {
-      MOZ_ASSERT(numReports == 2);
-      data = &mTwiceReported;
-    }
-    data->processBlock(aBlock);
-
-    mTotalUsableSize += aBlock.UsableSize();
-    mTotalNumBlocks++;
-
-    return &data->mRecordTable;
-  }
-
-  virtual void PrintRecords(const Writer& aWriter,
-                            CodeAddressService* aLocService) const
-  {
-    PrintSortedRecords(aWriter, aLocService, Record::CmpByUsable,
-                       "Twice-reported", "twice-reported",
-                       mTwiceReported.mRecordTable,
-                       mTwiceReported.mUsableSize, mTotalUsableSize,
-                       /* showCategoryPercentage = */ true,
-                       /* showReportedAt = */ true);
-
-    PrintSortedRecords(aWriter, aLocService, Record::CmpByUsable,
-                       "Unreported", "unreported",
-                       mUnreported.mRecordTable,
-                       mUnreported.mUsableSize, mTotalUsableSize,
-                       /* showCategoryPercentage = */ true,
-                       /* showReportedAt = */ true);
+  static const char digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef";
 
-    PrintSortedRecords(aWriter, aLocService, Record::CmpByUsable,
-                       "Once-reported", "once-reported",
-                       mOnceReported.mRecordTable,
-                       mOnceReported.mUsableSize, mTotalUsableSize,
-                       /* showCategoryPercentage = */ true,
-                       /* showReportedAt = */ true);
-  }
-
-  virtual void PrintSummary(const Writer& aWriter, bool aShowTilde) const
-  {
-    W("  Total:          %12s bytes (%6.2f%%) in %7s blocks (%6.2f%%)\n",
-      Show(mTotalUsableSize, gBuf1, kBufLen, aShowTilde),
-      100.0,
-      Show(mTotalNumBlocks,  gBuf2, kBufLen, aShowTilde),
-      100.0);
-
-    W("  Unreported:     %12s bytes (%6.2f%%) in %7s blocks (%6.2f%%)\n",
-      Show(mUnreported.mUsableSize, gBuf1, kBufLen, aShowTilde),
-      Percent(mUnreported.mUsableSize, mTotalUsableSize),
-      Show(mUnreported.mNumBlocks, gBuf2, kBufLen, aShowTilde),
-      Percent(mUnreported.mNumBlocks, mTotalNumBlocks));
+  char* b = aBuf + aBufLen - 1;
+  *b = '\0';
+  do {
+    b--;
+    if (b == aBuf) {
+      MOZ_CRASH("Base32 buffer too small");
+    }
+    *b = digits[aN % 32];
+    aN /= 32;
+  } while (aN);
 
-    W("  Once-reported:  %12s bytes (%6.2f%%) in %7s blocks (%6.2f%%)\n",
-      Show(mOnceReported.mUsableSize, gBuf1, kBufLen, aShowTilde),
-      Percent(mOnceReported.mUsableSize, mTotalUsableSize),
-      Show(mOnceReported.mNumBlocks, gBuf2, kBufLen, aShowTilde),
-      Percent(mOnceReported.mNumBlocks, mTotalNumBlocks));
-
-    W("  Twice-reported: %12s bytes (%6.2f%%) in %7s blocks (%6.2f%%)\n",
-      Show(mTwiceReported.mUsableSize, gBuf1, kBufLen, aShowTilde),
-      Percent(mTwiceReported.mUsableSize, mTotalUsableSize),
-      Show(mTwiceReported.mNumBlocks, gBuf2, kBufLen, aShowTilde),
-      Percent(mTwiceReported.mNumBlocks, mTotalNumBlocks));
-  }
-
-  virtual void PrintStats(const Writer& aWriter) const
-  {
-    size_t unreportedSize =
-      mUnreported.mRecordTable.sizeOfIncludingThis(MallocSizeOf);
-    W("    Unreported table:     %10s bytes (%s entries, %s used)\n",
-      Show(unreportedSize,                      gBuf1, kBufLen),
-      Show(mUnreported.mRecordTable.capacity(), gBuf2, kBufLen),
-      Show(mUnreported.mRecordTable.count(),    gBuf3, kBufLen));
-
-    size_t onceReportedSize =
-      mOnceReported.mRecordTable.sizeOfIncludingThis(MallocSizeOf);
-    W("    Once-reported table:  %10s bytes (%s entries, %s used)\n",
-      Show(onceReportedSize,                      gBuf1, kBufLen),
-      Show(mOnceReported.mRecordTable.capacity(), gBuf2, kBufLen),
-      Show(mOnceReported.mRecordTable.count(),    gBuf3, kBufLen));
+  return b;
+}
 
-    size_t twiceReportedSize =
-      mTwiceReported.mRecordTable.sizeOfIncludingThis(MallocSizeOf);
-    W("    Twice-reported table: %10s bytes (%s entries, %s used)\n",
-      Show(twiceReportedSize,                      gBuf1, kBufLen),
-      Show(mTwiceReported.mRecordTable.capacity(), gBuf2, kBufLen),
-      Show(mTwiceReported.mRecordTable.count(),    gBuf3, kBufLen));
-  }
-};
-
-class HeapAnalyzer MOZ_FINAL : public Analyzer
+// Converts a pointer to a unique ID. Reuses the existing ID for the pointer if
+// it's been seen before.
+static const char* Id(PointerIdMap& aIdMap, uint32_t& aNextId,
+                      const void* aPtr, char* aBuf, size_t aBufLen)
 {
-  RecordKindData mLive;
-
-public:
-  HeapAnalyzer() : mLive(1024) {}
-
-  virtual const char* AnalyzeFunctionName() const { return "AnalyzeHeap"; }
-
-  virtual RecordTable* ProcessBlock(const Block& aBlock)
-  {
-    mLive.processBlock(aBlock);
-
-    return &mLive.mRecordTable;
+  uint32_t id;
+  PointerIdMap::AddPtr p = aIdMap.lookupForAdd(aPtr);
+  if (!p) {
+    id = aNextId++;
+    (void)aIdMap.add(p, aPtr, id);
+  } else {
+    id = p->value();
   }
-
-  virtual void PrintRecords(const Writer& aWriter,
-                            CodeAddressService* aLocService) const
-  {
-    size_t totalUsableSize = mLive.mUsableSize;
-    PrintSortedRecords(aWriter, aLocService, Record::CmpByUsable,
-                       "Live", "live", mLive.mRecordTable, totalUsableSize,
-                       mLive.mUsableSize,
-                       /* showReportedAt = */ false,
-                       /* showCategoryPercentage = */ false);
-  }
-
-  virtual void PrintSummary(const Writer& aWriter, bool aShowTilde) const
-  {
-    W("  Total: %s bytes in %s blocks\n",
-      Show(mLive.mUsableSize, gBuf1, kBufLen, aShowTilde),
-      Show(mLive.mNumBlocks,  gBuf2, kBufLen, aShowTilde));
-  }
-
-  virtual void PrintStats(const Writer& aWriter) const
-  {
-    size_t liveSize = mLive.mRecordTable.sizeOfIncludingThis(MallocSizeOf);
-    W("    Live table:           %10s bytes (%s entries, %s used)\n",
-      Show(liveSize,                      gBuf1, kBufLen),
-      Show(mLive.mRecordTable.capacity(), gBuf2, kBufLen),
-      Show(mLive.mRecordTable.count(),    gBuf3, kBufLen));
-  }
-};
+  return Base32(id, aBuf, aBufLen);
+}
 
 static void
-AnalyzeImpl(Analyzer *aAnalyzer, const Writer& aWriter)
+AnalyzeReportsImpl(JSONWriter& aWriter)
 {
   if (!gIsDMDRunning) {
     return;
   }
 
   AutoBlockIntercepts block(Thread::Fetch());
   AutoLockState lock;
 
-  static int analysisCount = 1;
-  StatusMsg("%s %d {\n", aAnalyzer->AnalyzeFunctionName(), analysisCount++);
+  // Allocate this on the heap instead of the stack because it's fairly large.
+  auto locService = InfallibleAllocPolicy::new_<CodeAddressService>();
 
-  StatusMsg("  gathering heap block records...\n");
+  StackTraceSet usedStackTraces;
+  usedStackTraces.init(512);
+
+  PointerSet usedPcs;
+  usedPcs.init(512);
 
-  bool anyBlocksSampled = false;
+  PointerIdMap idMap;
+  idMap.init(512);
 
-  for (BlockTable::Range r = gBlockTable->all(); !r.empty(); r.popFront()) {
-    const Block& b = r.front();
-    RecordTable* table = aAnalyzer->ProcessBlock(b);
+  static int analysisCount = 1;
+  StatusMsg("Dump %d {\n", analysisCount++);
+
+  aWriter.Start();
+  {
+    #define ID(p) Id(idMap, id, p, idBuf, idBufLen)
 
-    RecordKey key(b);
-    RecordTable::AddPtr p = table->lookupForAdd(key);
-    if (!p) {
-      Record tr(key);
-      (void)table->add(p, tr);
+    aWriter.IntProperty("version", kOutputVersionNumber);
+
+    aWriter.StartObjectProperty("invocation");
+    {
+      aWriter.StringProperty("dmdEnvVar", gOptions->DMDEnvVar());
+      aWriter.IntProperty("sampleBelowSize", gOptions->SampleBelowSize());
     }
-    p->Add(b);
+    aWriter.EndObject();
+
+    StatusMsg("  Constructing the heap block list...\n");
 
-    anyBlocksSampled = anyBlocksSampled || b.IsSampled();
-  }
+    static const size_t idBufLen = 16;
+    char idBuf[idBufLen];
+    uint32_t id = 0;
+
+    aWriter.StartArrayProperty("blockList");
+    {
+      for (BlockTable::Range r = gBlockTable->all(); !r.empty(); r.popFront()) {
+        const Block& b = r.front();
+        b.AddStackTracesToTable(usedStackTraces);
 
-  WriteSeparator();
-  W("Invocation {\n");
-  W("  $DMD = '%s'\n", gOptions->DMDEnvVar());
-  W("  Function = %s\n", aAnalyzer->AnalyzeFunctionName());
-  W("  Sample-below size = %lld\n", (long long)(gOptions->SampleBelowSize()));
-  W("}\n\n");
+        aWriter.StartObjectElement(aWriter.SingleLineStyle);
+        {
+          if (!b.IsSampled()) {
+            aWriter.IntProperty("req", b.ReqSize());
+            if (b.SlopSize() > 0) {
+              aWriter.IntProperty("slop", b.SlopSize());
+            }
+          }
+          aWriter.StringProperty("alloc", ID(b.AllocStackTrace()));
+          if (b.NumReports() > 0) {
+            aWriter.StartArrayProperty("reps");
+            {
+              if (b.ReportStackTrace1()) {
+                aWriter.StringElement(ID(b.ReportStackTrace1()));
+              }
+              if (b.ReportStackTrace2()) {
+                aWriter.StringElement(ID(b.ReportStackTrace2()));
+              }
+            }
+            aWriter.EndArray();
+          }
+        }
+        aWriter.EndObject();
+      }
+    }
+    aWriter.EndArray();
 
-  // Allocate this on the heap instead of the stack because it's fairly large.
-  CodeAddressService* locService = InfallibleAllocPolicy::new_<CodeAddressService>();
+    StatusMsg("  Constructing the stack trace table...\n");
 
-  aAnalyzer->PrintRecords(aWriter, locService);
+    aWriter.StartObjectProperty("traceTable");
+    {
+      for (StackTraceSet::Enum e(usedStackTraces); !e.empty(); e.popFront()) {
+        const StackTrace* const st = e.front();
+        aWriter.StartArrayProperty(ID(st), aWriter.SingleLineStyle);
+        {
+          for (uint32_t i = 0; i < st->Length(); i++) {
+            const void* pc = st->Pc(i);
+            aWriter.StringElement(ID(pc));
+            usedPcs.put(pc);
+          }
+        }
+        aWriter.EndArray();
+      }
+    }
+    aWriter.EndObject();
 
-  WriteSeparator();
-  W("Summary {\n");
+    StatusMsg("  Constructing the stack frame table...\n");
 
-  bool showTilde = anyBlocksSampled;
-  aAnalyzer->PrintSummary(aWriter, showTilde);
+    aWriter.StartObjectProperty("frameTable");
+    {
+      static const size_t locBufLen = 1024;
+      char locBuf[locBufLen];
+
+      for (PointerSet::Enum e(usedPcs); !e.empty(); e.popFront()) {
+        const void* const pc = e.front();
 
-  W("}\n\n");
+        // Use 0 for the frame number. See the JSON format description comment
+        // in DMD.h to understand why.
+        locService->GetLocation(0, pc, locBuf, locBufLen);
+        aWriter.StringProperty(ID(pc), locBuf);
+      }
+    }
+    aWriter.EndObject();
 
-  // Stats are non-deterministic, so don't show them in test mode.
-  if (!gOptions->IsTestMode()) {
+    #undef ID
+  }
+  aWriter.End();
+
+  if (gOptions->ShowDumpStats()) {
     Sizes sizes;
     SizeOfInternal(&sizes);
 
-    WriteSeparator();
-    W("Execution measurements {\n");
+    static const size_t kBufLen = 64;
+    char buf1[kBufLen];
+    char buf2[kBufLen];
+    char buf3[kBufLen];
 
-    W("  Data structures that persist after Dump() ends {\n");
+    StatusMsg("  Execution measurements {\n");
 
-    W("    Used stack traces:    %10s bytes\n",
-      Show(sizes.mStackTracesUsed, gBuf1, kBufLen));
+    StatusMsg("    Data structures that persist after Dump() ends {\n");
 
-    W("    Unused stack traces:  %10s bytes\n",
-      Show(sizes.mStackTracesUnused, gBuf1, kBufLen));
+    StatusMsg("      Used stack traces:    %10s bytes\n",
+      Show(sizes.mStackTracesUsed, buf1, kBufLen));
 
-    W("    Stack trace table:    %10s bytes (%s entries, %s used)\n",
-      Show(sizes.mStackTraceTable,       gBuf1, kBufLen),
-      Show(gStackTraceTable->capacity(), gBuf2, kBufLen),
-      Show(gStackTraceTable->count(),    gBuf3, kBufLen));
+    StatusMsg("      Unused stack traces:  %10s bytes\n",
+      Show(sizes.mStackTracesUnused, buf1, kBufLen));
+
+    StatusMsg("      Stack trace table:    %10s bytes (%s entries, %s used)\n",
+      Show(sizes.mStackTraceTable,       buf1, kBufLen),
+      Show(gStackTraceTable->capacity(), buf2, kBufLen),
+      Show(gStackTraceTable->count(),    buf3, kBufLen));
 
-    W("    Block table:          %10s bytes (%s entries, %s used)\n",
-      Show(sizes.mBlockTable,       gBuf1, kBufLen),
-      Show(gBlockTable->capacity(), gBuf2, kBufLen),
-      Show(gBlockTable->count(),    gBuf3, kBufLen));
+    StatusMsg("      Block table:          %10s bytes (%s entries, %s used)\n",
+      Show(sizes.mBlockTable,       buf1, kBufLen),
+      Show(gBlockTable->capacity(), buf2, kBufLen),
+      Show(gBlockTable->count(),    buf3, kBufLen));
 
-    W("  }\n");
-    W("  Data structures that are destroyed after Dump() ends {\n");
+    StatusMsg("    }\n");
+    StatusMsg("    Data structures that are destroyed after Dump() ends {\n");
 
-    aAnalyzer->PrintStats(aWriter);
+    StatusMsg("      Location service:      %10s bytes\n",
+      Show(locService->SizeOfIncludingThis(MallocSizeOf), buf1, kBufLen));
+    StatusMsg("      Used stack traces set: %10s bytes\n",
+      Show(usedStackTraces.sizeOfExcludingThis(MallocSizeOf), buf1, kBufLen));
+    StatusMsg("      Used PCs set:          %10s bytes\n",
+      Show(usedPcs.sizeOfExcludingThis(MallocSizeOf), buf1, kBufLen));
+    StatusMsg("      Pointer ID map:        %10s bytes\n",
+      Show(idMap.sizeOfExcludingThis(MallocSizeOf), buf1, kBufLen));
 
-    W("    Location service:     %10s bytes\n",
-      Show(locService->SizeOfIncludingThis(MallocSizeOf), gBuf1, kBufLen));
-
-    W("  }\n");
-    W("  Counts {\n");
+    StatusMsg("    }\n");
+    StatusMsg("    Counts {\n");
 
     size_t hits   = locService->NumCacheHits();
     size_t misses = locService->NumCacheMisses();
     size_t requests = hits + misses;
-    W("    Location service:    %10s requests\n",
-      Show(requests, gBuf1, kBufLen));
+    StatusMsg("      Location service:    %10s requests\n",
+      Show(requests, buf1, kBufLen));
 
     size_t count    = locService->CacheCount();
     size_t capacity = locService->CacheCapacity();
-    W("    Location service cache:  "
+    StatusMsg("      Location service cache:  "
       "%4.1f%% hit rate, %.1f%% occupancy at end\n",
       Percent(hits, requests), Percent(count, capacity));
 
-    W("  }\n");
-    W("}\n\n");
+    StatusMsg("    }\n");
+    StatusMsg("  }\n");
   }
 
   InfallibleAllocPolicy::delete_(locService);
 
   StatusMsg("}\n");
 }
 
 MOZ_EXPORT void
-AnalyzeReports(const Writer& aWriter)
+AnalyzeReports(JSONWriter& aWriter)
 {
-  ReportsAnalyzer aAnalyzer;
-  AnalyzeImpl(&aAnalyzer, aWriter);
-}
-
-MOZ_EXPORT void
-AnalyzeHeap(const Writer& aWriter)
-{
-  HeapAnalyzer analyzer;
-  AnalyzeImpl(&analyzer, aWriter);
+  AnalyzeReportsImpl(aWriter);
+  ClearReports();
 }
 
 //---------------------------------------------------------------------------
 // Testing
 //---------------------------------------------------------------------------
 
 // This function checks that heap blocks that have the same stack trace but
 // different (or no) reporters get aggregated separately.
@@ -2194,30 +1868,33 @@ void foo()
 static void
 UseItOrLoseIt(void* a)
 {
   char buf[64];
   sprintf(buf, "%p\n", a);
   fwrite(buf, 1, strlen(buf) + 1, stderr);
 }
 
-// The output from this should be compared against test-expected.dmd.  It's
-// been tested on Linux64, and probably will give different results on other
+// The output from this should be tested with check_test_output.py.  It's been
+// tested on Linux64, and probably will give different results on other
 // platforms.
 static void
-RunTestMode(FILE* fp)
+RunTestMode(UniquePtr<FpWriteFunc> aF1, UniquePtr<FpWriteFunc> aF2,
+            UniquePtr<FpWriteFunc> aF3, UniquePtr<FpWriteFunc> aF4)
 {
-  Writer writer(FpWrite, fp);
-
   // The first part of this test requires sampling to be disabled.
   gOptions->SetSampleBelowSize(1);
 
+  //---------
+
   // AnalyzeReports 1.  Zero for everything.
-  AnalyzeReports(writer);
-  AnalyzeHeap(writer);
+  JSONWriter writer1(Move(aF1));
+  AnalyzeReports(writer1);
+
+  //---------
 
   // AnalyzeReports 2: 1 freed, 9 out of 10 unreported.
   // AnalyzeReports 3: still present and unreported.
   int i;
   char* a;
   for (i = 0; i < 10; i++) {
       a = (char*) malloc(100);
       UseItOrLoseIt(a);
@@ -2321,34 +1998,34 @@ RunTestMode(FILE* fp)
 //posix_memalign(&y, 128, 129);         // rounds up to 256
 //UseItOrLoseIt(y);
   // XXX: valloc doesn't work on Windows.
 //void* z = valloc(1);                  // rounds up to 4096
 //UseItOrLoseIt(z);
 //aligned_alloc(64, 256);               // XXX: C11 only
 
   // AnalyzeReports 2.
-  AnalyzeReports(writer);
-  AnalyzeHeap(writer);
+  JSONWriter writer2(Move(aF2));
+  AnalyzeReports(writer2);
 
   //---------
 
   Report(a2);
   Report(a2);
   free(c);
   free(e);
   Report(e2);
   free(e3);
 //free(x);
 //free(y);
 //free(z);
 
   // AnalyzeReports 3.
-  AnalyzeReports(writer);
-  AnalyzeHeap(writer);
+  JSONWriter writer3(Move(aF3));
+  AnalyzeReports(writer3);
 
   //---------
 
   // Clear all knowledge of existing blocks to give us a clean slate.
   gBlockTable->clear();
 
   gOptions->SetSampleBelowSize(128);
 
@@ -2402,18 +2079,18 @@ RunTestMode(FILE* fp)
     UseItOrLoseIt(s);
   }
   MOZ_ASSERT(gSmallBlockActualSizeCounter == 64);
 
   // At the end we're 64 bytes into the current sample so we report ~1,424
   // bytes of allocation overall, which is 64 less than the real value 1,488.
 
   // AnalyzeReports 4.
-  AnalyzeReports(writer);
-  AnalyzeHeap(writer);
+  JSONWriter writer4(Move(aF4));
+  AnalyzeReports(writer4);
 }
 
 //---------------------------------------------------------------------------
 // Stress testing microbenchmark
 //---------------------------------------------------------------------------
 
 // This stops otherwise-unused variables from being optimized away.
 static void
@@ -2469,19 +2146,19 @@ stress1()
 // This stress test does lots of allocations and frees, which is where most of
 // DMD's overhead occurs.  It allocates 1,000,000 64-byte blocks, spread evenly
 // across 1,000 distinct stack traces.  It frees every second one immediately
 // after allocating it.
 //
 // It's highly artificial, but it's deterministic and easy to run.  It can be
 // timed under different conditions to glean performance data.
 static void
-RunStressMode(FILE* fp)
+RunStressMode(UniquePtr<FpWriteFunc> aF)
 {
-  Writer writer(FpWrite, fp);
+  JSONWriter writer(Move(aF));
 
   // Disable sampling for maximum stress.
   gOptions->SetSampleBelowSize(1);
 
   stress1(); stress1(); stress1(); stress1(); stress1();
   stress1(); stress1(); stress1(); stress1(); stress1();
 
   AnalyzeReports(writer);
--- a/memory/replace/dmd/DMD.h
+++ b/memory/replace/dmd/DMD.h
@@ -2,76 +2,131 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef DMD_h___
 #define DMD_h___
 
-#include <stdarg.h>
 #include <string.h>
 
 #include "mozilla/Types.h"
 
 namespace mozilla {
+
+class JSONWriter;
+
 namespace dmd {
 
 // Mark a heap block as reported by a memory reporter.
 MOZ_EXPORT void
 Report(const void* aPtr);
 
 // Mark a heap block as reported immediately on allocation.
 MOZ_EXPORT void
 ReportOnAlloc(const void* aPtr);
 
-class Writer
-{
-public:
-  typedef void (*WriterFun)(void* aWriteState, const char* aFmt, va_list aAp);
-
-  Writer(WriterFun aWriterFun, void* aWriteState)
-    : mWriterFun(aWriterFun), mWriteState(aWriteState)
-  {}
-
-  void Write(const char* aFmt, ...) const;
-
-private:
-  WriterFun mWriterFun;
-  void*     mWriteState;
-};
-
 // Clears existing reportedness data from any prior runs of the memory
 // reporters.  The following sequence should be used.
 // - ClearReports()
 // - run the memory reporters
 // - AnalyzeReports()
 // This sequence avoids spurious twice-reported warnings.
 MOZ_EXPORT void
 ClearReports();
 
-// Determines which heap blocks have been reported, and dumps a human-readable
-// summary (via |aWrite|).  If |aWrite| is nullptr it will dump to stderr.
-// Beware: this output may have very long lines.
-MOZ_EXPORT void
-AnalyzeReports(const Writer& aWriter);
-
-// Measures all heap blocks, and dumps a human-readable summary (via |aWrite|).
-// If |aWrite| is nullptr it will dump to stderr.  Beware: this output may
-// have very long lines.
+// Determines which heap blocks have been reported, and dumps JSON output
+// (via |aWriter|) describing the heap.
+//
+// The following sample output contains comments that explain the format and
+// design choices. The output files can be quite large, so a number of
+// decisions were made to minimize size, such as using short property names and
+// omitting properties whenever possible.
+//
+// {
+//   // The version number of the format, which will be incremented each time
+//   // backwards-incompatible changes are made. A mandatory integer.
+//   "version": 1,
+//
+//   // Information about how DMD was invoked. A mandatory object.
+//   "invocation": {
+//     // The contents of the $DMD environment variable. A mandatory string.
+//     "dmdEnvVar": "1",
+//
+//     // The value of the --sample-below-size option. A mandatory integer.
+//     "sampleBelowSize": 4093
+//   },
+//
+//   // Details of all analyzed heap blocks. A mandatory array.
+//   "blockList": [
+//     // An example of a non-sampled heap block.
+//     {
+//       // Requested size, in bytes. In non-sampled blocks this is a
+//       // mandatory integer. In sampled blocks this is not present, and the
+//       // requested size is equal to the "sampleBelowSize" value. Therefore,
+//       // the block is sampled if and only if this property is absent.
+//       "req": 3584,
+//
+//       // Requested slop size, in bytes. This is mandatory if it is non-zero,
+//       // but omitted otherwise. Because sampled blocks never have slop, this
+//       // property is never present for non-sampled blocks.
+//       "slop": 512,
+//
+//       // The stack trace at which the block was allocated. A mandatory
+//       // string which indexes into the "traceTable" object.
+//       "alloc": "A"
+//     },
+//
+//     // An example of a sampled heap block.
+//     {
+//       "alloc": "B",
+//
+//       // One or more stack traces at which this heap block was reported by a
+//       // memory reporter. An optional array. The elements are strings that
+//       // index into the "traceTable" object.
+//       "reps": ["C"]
+//     }
+//   ],
+//
+//   // The stack traces referenced by elements of the "blockList" array. This
+//   // could be an array, but making it an object makes it easier to see
+//   // which stacks correspond to which references in the "blockList" array.
+//   "traceTable": {
+//     // Each property corresponds to a stack trace mentioned in the "blocks"
+//     // object. Each element is an index into the "frameTable" object.
+//     "A": ["D", "E"],
+//     "B": ["D", "F"],
+//     "C": ["G", "H"]
+//   },
+//
+//   // The stack frames referenced by the "traceTable" object. The
+//   // descriptions can be quite long, so they are stored separately from the
+//   // "traceTable" object so that each one only has to be written once.
+//   // This could also be an array, but again, making it an object makes it
+//   // easier to see which frames correspond to which references in the
+//   // "traceTable" object.
+//   "frameTable": {
+//     // Each property key is a frame key mentioned in the "traceTable" object.
+//     // Each property value is a string containing a frame description. Each
+//     // frame description must be in a format recognized by the stack-fixing
+//     // scripts (e.g. fix_linux_stack.py), which require a frame number at
+//     // the start. Because each stack frame description in this table can
+//     // be shared between multiple stack traces, we use a dummy value of
+//     // #00. The proper frame number can be reconstructed later by scripts
+//     // that output stack traces in a conventional non-shared format.
+//     "D": "#00: foo (Foo.cpp:123)",
+//     "E": "#00: bar (Bar.cpp:234)",
+//     "F": "#00: baz (Baz.cpp:345)",
+//     "G": "#00: quux (Quux.cpp:456)",
+//     "H": "#00: quuux (Quux.cpp:567)"
+//   }
+// }
 MOZ_EXPORT void
-AnalyzeHeap(const Writer& aWriter);
-
-// A useful |WriterFun|.  For example, if |fp| is a FILE* you want
-// |AnalyzeReports|'s output to be written to, call:
-//
-//   dmd::Writer writer(FpWrite, fp);
-//   dmd::AnalyzeReports(writer);
-MOZ_EXPORT void
-FpWrite(void* aFp, const char* aFmt, va_list aAp);
+AnalyzeReports(mozilla::JSONWriter& aWriter);
 
 struct Sizes
 {
   size_t mStackTracesUsed;
   size_t mStackTracesUnused;
   size_t mStackTraceTable;
   size_t mBlockTable;
 
--- a/memory/replace/dmd/check_test_output.py
+++ b/memory/replace/dmd/check_test_output.py
@@ -1,14 +1,18 @@
 #! /usr/bin/python
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 """This script takes the file produced by DMD's test mode and checks its
 correctness.
 
-It produces the following output files: $TMP/test-{fixed,filtered,diff}.dmd.
+It produces the following output files: $TMP/full-{fixed,filtered,diff}.dmd.
 
 It runs the appropriate fix* script to get nice stack traces.  It also
 filters out platform-specific details from the test output file.
 
 Note: you must run this from the same directory that you invoked DMD's test
 mode, otherwise the fix* script will not work properly, because some of the
 paths in the test output are relative.
 
@@ -18,63 +22,55 @@ from __future__ import print_function
 
 import os
 import platform
 import re
 import subprocess
 import sys
 import tempfile
 
-
-def main():
-
-    # Arguments
-
-    if (len(sys.argv) != 3):
-        print("usage:", sys.argv[0], "<topsrcdir> <test.dmd>")
-        sys.exit(1)
-
-    srcdir = sys.argv[1]
-
+def test(src_dir, kind, options, i):
     # Filenames
-
-    tempdir = tempfile.gettempdir()
-    in_name       = sys.argv[2]
-    fixed_name    = tempdir + os.sep + "test-fixed.dmd"
-    filtered_name = tempdir + os.sep + "test-filtered.dmd"
-    diff_name     = tempdir + os.sep + "test-diff.dmd"
-    expected_name = srcdir + os.sep + \
-                    "memory/replace/dmd/test-expected.dmd"
+    tmp_dir = tempfile.gettempdir()
+    in_name        = os.path.join(src_dir, "full{:d}.json".format(i))
+    fixed_name     = os.path.join(tmp_dir, "full-{:}-fixed{:d}.json".format(kind, i))
+    converted_name = os.path.join(tmp_dir, "full-{:}-converted{:d}.txt".format(kind, i))
+    filtered_name  = os.path.join(tmp_dir, "full-{:}-filtered{:d}.txt".format(kind, i))
+    diff_name      = os.path.join(tmp_dir, "full-{:}-diff{:d}.txt".format(kind, i))
+    expected_name  = os.path.join(src_dir, "memory", "replace", "dmd", "test", "full-{:}-expected{:d}.txt".format(kind, i))
 
     # Fix stack traces
 
-    print("fixing output to", fixed_name)
-
-    sysname = platform.system()
-    if sysname == "Linux":
-        fix = srcdir + os.sep + "tools/rb/fix_linux_stack.py"
-    elif sysname == "Darwin":
-        fix = srcdir + os.sep + "tools/rb/fix_macosx_stack.py"
+    sys_name = platform.system()
+    fix = os.path.join(src_dir, "tools", "rb")
+    if sys_name == "Linux":
+        fix = os.path.join(fix, "fix_linux_stack.py")
+    elif sys_name == "Darwin":
+        fix = os.path.join(fix, "fix_macosx_stack.py")
     else:
-        print("unhandled platform: " + sysname, file=sys.stderr)
+        print("unhandled platform: " + sys_name, file=sys.stderr)
         sys.exit(1)
 
     subprocess.call(fix, stdin=open(in_name, "r"),
                          stdout=open(fixed_name, "w"))
 
+    # Convert from JSON
+
+    convert = [os.path.join(src_dir, "memory", "replace", "dmd", "dmd.py")] + \
+               options + ['--no-fix-stacks', fixed_name]
+    subprocess.call(convert, stdout=open(converted_name, "w"))
+
     # Filter output
 
     # In heap block records we filter out most stack frames.  The only thing
     # we leave behind is a "DMD.cpp" entry if we see one or more frames that
     # have DMD.cpp in them.  There is simply too much variation to do anything
     # better than that.
 
-    print("filtering output to", filtered_name)
-
-    with open(fixed_name, "r") as fin, \
+    with open(converted_name, "r") as fin, \
          open(filtered_name, "w") as fout:
 
         test_frame_re = re.compile(r".*(DMD.cpp)")
 
         for line in fin:
             if re.match(r"  (Allocated at {|Reported( again)? at {)", line):
                 # It's a heap block record.
                 print(line, end='', file=fout)
@@ -95,22 +91,37 @@ def main():
                         break
 
             else:
                 # A line that needs no special handling.  Copy it through.
                 print(line, end='', file=fout)
 
     # Compare with expected output
 
-    print("diffing output to", diff_name)
-
     ret = subprocess.call(["diff", "-u", expected_name, filtered_name],
                           stdout=open(diff_name, "w"))
 
     if ret == 0:
-        print("test PASSED")
+        print("TEST-PASS | {:} {:d} | ok".format(kind, i))
     else:
-        print("test FAILED (did you remember to run this script and Firefox "
-              "in the same directory?)")
+        print("TEST-UNEXPECTED-FAIL | {:} {:d} | mismatch".format(kind, i))
+        print("Output files:")
+        print("- " + fixed_name);
+        print("- " + converted_name);
+        print("- " + filtered_name);
+        print("- " + diff_name);
+
+
+def main():
+    if (len(sys.argv) != 2):
+        print("usage:", sys.argv[0], "<topsrcdir>")
+        sys.exit(1)
+
+    src_dir = sys.argv[1]
+
+    ntests = 4
+    for i in range(1, ntests+1):
+        test(src_dir, "reports", [], i)
+        test(src_dir, "heap", ["--ignore-reports"], i)
 
 
 if __name__ == "__main__":
     main()
new file mode 100755
--- /dev/null
+++ b/memory/replace/dmd/dmd.py
@@ -0,0 +1,451 @@
+#! /usr/bin/python
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+'''This script analyzes a JSON file emitted by DMD.'''
+
+from __future__ import print_function, division
+
+import argparse
+import collections
+import json
+import os
+import platform
+import re
+import shutil
+import sys
+import tempfile
+
+# The DMD output version this script handles.
+outputVersion = 1
+
+# If --ignore-alloc-fns is specified, stack frames containing functions that
+# match these strings will be removed.
+allocatorFns = [
+    'replace_malloc',
+    'replace_calloc',
+    'replace_realloc',
+    'replace_memalign',
+    'replace_posix_memalign',
+    'moz_xmalloc',
+    'moz_xcalloc',
+    'moz_xrealloc',
+    'operator new(',
+    'operator new[](',
+    'g_malloc',
+    'g_slice_alloc',
+    'callocCanGC',
+    'reallocCanGC',
+    'vpx_malloc',
+    'vpx_calloc',
+    'vpx_realloc',
+    'vpx_memalign',
+    'js_malloc',
+    'js_calloc',
+    'js_realloc',
+    'pod_malloc',
+    'pod_calloc',
+    'pod_realloc',
+]
+
+class Record(object):
+    def __init__(self):
+        self.numBlocks = 0
+        self.reqSize = 0
+        self.slopSize = 0
+        self.usableSize = 0
+        self.isSampled = False
+        self.usableSizes = collections.defaultdict(int)
+
+    @staticmethod
+    def cmpByIsSampled(r1, r2):
+        # Treat sampled as smaller than non-sampled.
+        return cmp(r2.isSampled, r1.isSampled)
+
+    @staticmethod
+    def cmpByUsableSize(r1, r2):
+        # Sort by usable size, then req size, then by isSampled.
+        return cmp(r1.usableSize, r2.usableSize) or Record.cmpByReqSize(r1, r2)
+
+    @staticmethod
+    def cmpByReqSize(r1, r2):
+        # Sort by req size, then by isSampled.
+        return cmp(r1.reqSize, r2.reqSize) or Record.cmpByIsSampled(r1, r2)
+
+    @staticmethod
+    def cmpBySlopSize(r1, r2):
+        # Sort by slop size, then by isSampled.
+        return cmp(r1.slopSize, r2.slopSize) or Record.cmpByIsSampled(r1, r2)
+
+
+sortByChoices = {
+    'usable': Record.cmpByUsableSize,   # the default
+    'req':    Record.cmpByReqSize,
+    'slop':   Record.cmpBySlopSize,
+}
+
+
+def parseCommandLine():
+    # 24 is the maximum number of frames that DMD will produce.
+    def range_1_24(string):
+        value = int(string)
+        if value < 1 or value > 24:
+            msg = '{:s} is not in the range 1..24'.format(string)
+            raise argparse.ArgumentTypeError(msg)
+        return value
+
+    description = '''
+Analyze heap data produced by DMD.
+If no files are specified, read from stdin.
+Write to stdout unless -o/--output is specified.
+Stack traces are fixed to show function names, filenames and line numbers
+unless --no-fix-stacks is specified; stack fixing modifies the original file
+and may take some time.
+'''
+    p = argparse.ArgumentParser(description=description)
+
+    p.add_argument('-o', '--output', type=argparse.FileType('w'),
+                   help='output file; stdout if unspecified')
+
+    p.add_argument('-f', '--max-frames', type=range_1_24,
+                   help='maximum number of frames to consider in each trace')
+
+    p.add_argument('-r', '--ignore-reports', action='store_true',
+                   help='ignore memory reports data; useful if you just ' +
+                        'want basic heap profiling')
+
+    p.add_argument('-s', '--sort-by', choices=sortByChoices.keys(),
+                   default=sortByChoices.keys()[0],
+                   help='sort the records by a particular metric')
+
+    p.add_argument('-a', '--ignore-alloc-fns', action='store_true',
+                   help='ignore allocation functions at the start of traces')
+
+    p.add_argument('-b', '--show-all-block-sizes', action='store_true',
+                   help='show individual block sizes for each record')
+
+    p.add_argument('--no-fix-stacks', action='store_true',
+                   help='do not fix stacks')
+
+    p.add_argument('input_file', type=argparse.FileType('r'))
+
+    return p.parse_args(sys.argv[1:])
+
+
+# Fix stacks if necessary: first write the output to a tempfile, then replace
+# the original file with it.
+def fixStackTraces(args):
+    # This append() call is needed to make the import statements work when this
+    # script is installed as a symlink.
+    sys.path.append(os.path.dirname(__file__))
+
+    # XXX: should incorporate fix_stack_using_bpsyms.py here as well, like in
+    #      testing/mochitests/runtests.py
+    sysname = platform.system()
+    if sysname == 'Linux':
+        import fix_linux_stack as fixModule
+        fix = lambda line: fixModule.fixSymbols(line)
+    elif sysname == 'Darwin':
+        import fix_macosx_stack as fixModule
+        fix = lambda line: fixModule.fixSymbols(line)
+    else:
+        fix = None  # there is no fix script for Windows
+
+    if fix:
+        # Fix stacks, writing output to a temporary file, and then
+        # overwrite the original file.
+        with tempfile.NamedTemporaryFile(delete=False) as tmp:
+            for line in args.input_file:
+                tmp.write(fix(line))
+            shutil.move(tmp.name, args.input_file.name)
+
+        args.input_file = open(args.input_file.name)
+
+
+def main():
+    args = parseCommandLine()
+
+    # Fix stack traces unless otherwise instructed.
+    if not args.no_fix_stacks:
+        fixStackTraces(args)
+
+    j = json.load(args.input_file)
+
+    if j['version'] != outputVersion:
+        raise Exception("'version' property isn't '{:d}'".format(outputVersion))
+
+    # Extract the main parts of the JSON object.
+    invocation = j['invocation']
+    dmdEnvVar = invocation['dmdEnvVar']
+    sampleBelowSize = invocation['sampleBelowSize']
+    blockList = j['blockList']
+    traceTable = j['traceTable']
+    frameTable = j['frameTable']
+
+    heapIsSampled = sampleBelowSize > 1     # is sampling present?
+
+    # Remove allocation functions at the start of traces.
+    if args.ignore_alloc_fns:
+        # Build a regexp that matches every function in allocatorFns.
+        escapedAllocatorFns = map(re.escape, allocatorFns)
+        fn_re = re.compile('|'.join(escapedAllocatorFns))
+
+        # Remove allocator fns from each stack trace.
+        for traceKey, frameKeys in traceTable.items():
+            numSkippedFrames = 0
+            for frameKey in frameKeys:
+                frameDesc = frameTable[frameKey]
+                if re.search(fn_re, frameDesc):
+                    numSkippedFrames += 1
+                else:
+                    break
+            if numSkippedFrames > 0:
+                traceTable[traceKey] = frameKeys[numSkippedFrames:]
+
+    # Trim the number of frames.
+    for traceKey, frameKeys in traceTable.items():
+        if len(frameKeys) > args.max_frames:
+            traceTable[traceKey] = frameKeys[:args.max_frames]
+
+    # Aggregate blocks into records. All sufficiently similar blocks go into a
+    # single record.
+
+    if args.ignore_reports:
+        liveRecords = collections.defaultdict(Record)
+    else:
+        unreportedRecords    = collections.defaultdict(Record)
+        onceReportedRecords  = collections.defaultdict(Record)
+        twiceReportedRecords = collections.defaultdict(Record)
+
+    heapUsableSize = 0
+    heapBlocks = 0
+
+    for block in blockList:
+        # For each block we compute a |recordKey|, and all blocks with the same
+        # |recordKey| are aggregated into a single record. The |recordKey| is
+        # derived from the block's 'alloc' and 'reps' (if present) stack
+        # traces.
+        #
+        # Each stack trace has a key in the JSON file. But we don't use that
+        # key to construct |recordKey|; instead we use the frame keys.
+        # This is because the stack trimming done for --max-frames can cause
+        # stack traces with distinct trace keys to end up with the same frame
+        # keys, and these should be considered equivalent. E.g. if we have
+        # distinct traces T1:[A,B,C] and T2:[A,B,D] and we trim the final frame
+        # of each they should be considered equivalent.
+        allocatedAt = block['alloc']
+        if args.ignore_reports:
+            recordKey = str(traceTable[allocatedAt])
+            records = liveRecords
+        else:
+            recordKey = str(traceTable[allocatedAt])
+            if 'reps' in block:
+                reportedAts = block['reps']
+                for reportedAt in reportedAts:
+                    recordKey += str(traceTable[reportedAt])
+                if len(reportedAts) == 1:
+                    records = onceReportedRecords
+                else:
+                    records = twiceReportedRecords
+            else:
+                records = unreportedRecords
+
+        record = records[recordKey]
+
+        if 'req' in block:
+            # not sampled
+            reqSize = block['req']
+            slopSize = block.get('slop', 0)
+            isSampled = False
+        else:
+            # sampled
+            reqSize = sampleBelowSize
+            if 'slop' in block:
+                raise Exception("'slop' property in sampled block'")
+            slopSize = 0
+            isSampled = True
+
+        usableSize = reqSize + slopSize
+        heapUsableSize += usableSize
+        heapBlocks += 1
+
+        record.numBlocks  += 1
+        record.reqSize    += reqSize
+        record.slopSize   += slopSize
+        record.usableSize += usableSize
+        record.isSampled   = record.isSampled or isSampled
+        record.allocatedAt = block['alloc']
+        if args.ignore_reports:
+            pass
+        else:
+            if 'reps' in block:
+                record.reportedAts = block['reps']
+        record.usableSizes[(usableSize, isSampled)] += 1
+
+    # Print records.
+
+    separator = '#' + '-' * 65 + '\n'
+
+    def number(n, isSampled):
+        '''Format a number, with comma as a separator and a '~' prefix if it's
+        sampled.'''
+        return '{:}{:,d}'.format('~' if isSampled else '', n)
+
+    def perc(m, n):
+        return 0 if n == 0 else (100 * m / n)
+
+    def plural(n):
+        return '' if n == 1 else 's'
+
+    # Prints to stdout, or to file if -o/--output was specified.
+    def out(*arguments, **kwargs):
+        print(*arguments, file=args.output, **kwargs)
+
+    def printStack(traceTable, frameTable, traceKey):
+        # The frame number is always '#00' (see DMD.h for why), so we have to
+        # replace that with the correct frame number.
+        for n, frameKey in enumerate(traceTable[traceKey], start=1):
+            out('    #{:02d}{:}'.format(n, frameTable[frameKey][3:]))
+
+    def printRecords(recordKind, records, heapUsableSize):
+        RecordKind = recordKind.capitalize()
+        out(separator)
+        numRecords = len(records)
+        cmpRecords = sortByChoices[args.sort_by]
+        sortedRecords = sorted(records.values(), cmp=cmpRecords, reverse=True)
+        kindBlocks = 0
+        kindUsableSize = 0
+        maxRecord = 1000
+
+        # First iteration: get totals, etc.
+        for record in sortedRecords:
+            kindBlocks     += record.numBlocks
+            kindUsableSize += record.usableSize
+
+        # Second iteration: print.
+        if numRecords == 0:
+            out('# no {:} heap blocks\n'.format(recordKind))
+
+        kindCumulativeUsableSize = 0
+        for i, record in enumerate(sortedRecords, start=1):
+            # Stop printing at the |maxRecord|th record.
+            if i == maxRecord:
+                out('# {:}: stopping after {:,d} heap block records\n'.
+                    format(RecordKind, i))
+                break
+
+            kindCumulativeUsableSize += record.usableSize
+
+            isSampled = record.isSampled
+
+            out(RecordKind + ' {')
+            out('  {:} block{:} in heap block record {:,d} of {:,d}'.
+                format(number(record.numBlocks, isSampled),
+                       plural(record.numBlocks), i, numRecords))
+            out('  {:} bytes ({:} requested / {:} slop)'.
+                format(number(record.usableSize, isSampled),
+                       number(record.reqSize, isSampled),
+                       number(record.slopSize, isSampled)))
+            out('  {:4.2f}% of the heap ({:4.2f}% cumulative)'.
+                format(perc(record.usableSize, heapUsableSize),
+                       perc(kindCumulativeUsableSize, heapUsableSize)))
+            if args.ignore_reports:
+                pass
+            else:
+                out('  {:4.2f}% of {:} ({:4.2f}% cumulative)'.
+                    format(perc(record.usableSize, kindUsableSize),
+                           recordKind,
+                           perc(kindCumulativeUsableSize, kindUsableSize)))
+
+            if args.show_all_block_sizes:
+                usableSizes = sorted(record.usableSizes.items(), reverse=True)
+
+                out('  Individual block sizes: ', end='')
+                isFirst = True
+                for (usableSize, isSampled), count in usableSizes:
+                    if not isFirst:
+                        out('; ', end='')
+                    out('{:}'.format(number(usableSize, isSampled)), end='')
+                    if count > 1:
+                        out(' x {:,d}'.format(count), end='')
+                    isFirst = False
+                out()
+
+            out('  Allocated at {')
+            printStack(traceTable, frameTable, record.allocatedAt)
+            out('  }')
+            if args.ignore_reports:
+                pass
+            else:
+                if hasattr(record, 'reportedAts'):
+                    for n, reportedAt in enumerate(record.reportedAts):
+                        again = 'again ' if n > 0 else ''
+                        out('  Reported {:}at {{'.format(again))
+                        printStack(traceTable, frameTable, reportedAt)
+                        out('  }')
+            out('}\n')
+
+        return (kindUsableSize, kindBlocks)
+
+
+    # Print header.
+    out(separator)
+    out('Invocation {')
+    out('  $DMD = \'' + dmdEnvVar + '\'')
+    out('  Sample-below size = ' + str(sampleBelowSize))
+    out('}\n')
+
+    # Print records.
+    if args.ignore_reports:
+        liveUsableSize, liveBlocks = \
+            printRecords('live', liveRecords, heapUsableSize)
+    else:
+        twiceReportedUsableSize, twiceReportedBlocks = \
+            printRecords('twice-reported', twiceReportedRecords, heapUsableSize)
+
+        unreportedUsableSize, unreportedBlocks = \
+            printRecords('unreported',     unreportedRecords, heapUsableSize)
+
+        onceReportedUsableSize, onceReportedBlocks = \
+            printRecords('once-reported',  onceReportedRecords, heapUsableSize)
+
+    # Print summary.
+    out(separator)
+    out('Summary {')
+    if args.ignore_reports:
+        out('  Total: {:} bytes in {:} blocks'.
+            format(number(liveUsableSize, heapIsSampled),
+                   number(liveBlocks, heapIsSampled)))
+    else:
+        fmt = '  {:15} {:>12} bytes ({:6.2f}%) in {:>7} blocks ({:6.2f}%)'
+        out(fmt.
+            format('Total:',
+                   number(heapUsableSize, heapIsSampled),
+                   100,
+                   number(heapBlocks, heapIsSampled),
+                   100))
+        out(fmt.
+            format('Unreported:',
+                   number(unreportedUsableSize, heapIsSampled),
+                   perc(unreportedUsableSize, heapUsableSize),
+                   number(unreportedBlocks, heapIsSampled),
+                   perc(unreportedBlocks, heapBlocks)))
+        out(fmt.
+            format('Once-reported:',
+                   number(onceReportedUsableSize, heapIsSampled),
+                   perc(onceReportedUsableSize, heapUsableSize),
+                   number(onceReportedBlocks, heapIsSampled),
+                   perc(onceReportedBlocks, heapBlocks)))
+        out(fmt.
+            format('Twice-reported:',
+                   number(twiceReportedUsableSize, heapIsSampled),
+                   perc(twiceReportedUsableSize, heapUsableSize),
+                   number(twiceReportedBlocks, heapIsSampled),
+                   perc(twiceReportedBlocks, heapBlocks)))
+    out('}\n')
+
+
+if __name__ == '__main__':
+    main()
deleted file mode 100644
--- a/memory/replace/dmd/test-expected.dmd
+++ /dev/null
@@ -1,841 +0,0 @@
-#-----------------------------------------------------------------
-
-Invocation {
-  $DMD = '--mode=test'
-  Function = AnalyzeReports
-  Sample-below size = 1
-}
-
-#-----------------------------------------------------------------
-
-# no twice-reported heap blocks
-
-#-----------------------------------------------------------------
-
-# no unreported heap blocks
-
-#-----------------------------------------------------------------
-
-# no once-reported heap blocks
-
-#-----------------------------------------------------------------
-
-Summary {
-  Total:                     0 bytes (100.00%) in       0 blocks (100.00%)
-  Unreported:                0 bytes (  0.00%) in       0 blocks (  0.00%)
-  Once-reported:             0 bytes (  0.00%) in       0 blocks (  0.00%)
-  Twice-reported:            0 bytes (  0.00%) in       0 blocks (  0.00%)
-}
-
-#-----------------------------------------------------------------
-
-Invocation {
-  $DMD = '--mode=test'
-  Function = AnalyzeHeap
-  Sample-below size = 1
-}
-
-#-----------------------------------------------------------------
-
-# no live heap blocks
-
-#-----------------------------------------------------------------
-
-Summary {
-  Total: 0 bytes in 0 blocks
-}
-
-#-----------------------------------------------------------------
-
-Invocation {
-  $DMD = '--mode=test'
-  Function = AnalyzeReports
-  Sample-below size = 1
-}
-
-#-----------------------------------------------------------------
-
-Twice-reported {
-  1 block in heap block record 1 of 4
-  80 bytes (79 requested / 1 slop)
-  0.66% of the heap (0.66% cumulative)
-  29.41% of twice-reported (29.41% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-  Reported at {
-    ... DMD.cpp
-  }
-  Reported again at {
-    ... DMD.cpp
-  }
-}
-
-Twice-reported {
-  1 block in heap block record 2 of 4
-  80 bytes (78 requested / 2 slop)
-  0.66% of the heap (1.32% cumulative)
-  29.41% of twice-reported (58.82% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-  Reported at {
-    ... DMD.cpp
-  }
-  Reported again at {
-    ... DMD.cpp
-  }
-}
-
-Twice-reported {
-  1 block in heap block record 3 of 4
-  80 bytes (77 requested / 3 slop)
-  0.66% of the heap (1.99% cumulative)
-  29.41% of twice-reported (88.24% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-  Reported at {
-    ... DMD.cpp
-  }
-  Reported again at {
-    ... DMD.cpp
-  }
-}
-
-Twice-reported {
-  1 block in heap block record 4 of 4
-  32 bytes (30 requested / 2 slop)
-  0.26% of the heap (2.25% cumulative)
-  11.76% of twice-reported (100.00% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-  Reported at {
-    ... DMD.cpp
-  }
-  Reported again at {
-    ... DMD.cpp
-  }
-}
-
-#-----------------------------------------------------------------
-
-Unreported {
-  9 blocks in heap block record 1 of 3
-  1,008 bytes (900 requested / 108 slop)
-  8.34% of the heap (8.34% cumulative)
-  81.82% of unreported (81.82% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-}
-
-Unreported {
-  2 blocks in heap block record 2 of 3
-  112 bytes (112 requested / 0 slop)
-  0.93% of the heap (9.27% cumulative)
-  9.09% of unreported (90.91% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-}
-
-Unreported {
-  2 blocks in heap block record 3 of 3
-  112 bytes (112 requested / 0 slop)
-  0.93% of the heap (10.19% cumulative)
-  9.09% of unreported (100.00% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-}
-
-#-----------------------------------------------------------------
-
-Once-reported {
-  1 block in heap block record 1 of 11
-  8,192 bytes (4,097 requested / 4,095 slop)
-  67.77% of the heap (67.77% cumulative)
-  77.40% of once-reported (77.40% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-  Reported at {
-    ... DMD.cpp
-  }
-}
-
-Once-reported {
-  1 block in heap block record 2 of 11
-  1,024 bytes (1,023 requested / 1 slop)
-  8.47% of the heap (76.24% cumulative)
-  9.67% of once-reported (87.07% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-  Reported at {
-    ... DMD.cpp
-  }
-}
-
-Once-reported {
-  1 block in heap block record 3 of 11
-  512 bytes (512 requested / 0 slop)
-  4.24% of the heap (80.48% cumulative)
-  4.84% of once-reported (91.91% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-  Reported at {
-    ... DMD.cpp
-  }
-}
-
-Once-reported {
-  2 blocks in heap block record 4 of 11
-  240 bytes (240 requested / 0 slop)
-  1.99% of the heap (82.46% cumulative)
-  2.27% of once-reported (94.18% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-  Reported at {
-    ... DMD.cpp
-  }
-}
-
-Once-reported {
-  2 blocks in heap block record 5 of 11
-  240 bytes (240 requested / 0 slop)
-  1.99% of the heap (84.45% cumulative)
-  2.27% of once-reported (96.45% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-  Reported at {
-    ... DMD.cpp
-  }
-}
-
-Once-reported {
-  1 block in heap block record 6 of 11
-  96 bytes (96 requested / 0 slop)
-  0.79% of the heap (85.24% cumulative)
-  0.91% of once-reported (97.35% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-  Reported at {
-    ... DMD.cpp
-  }
-}
-
-Once-reported {
-  1 block in heap block record 7 of 11
-  96 bytes (96 requested / 0 slop)
-  0.79% of the heap (86.04% cumulative)
-  0.91% of once-reported (98.26% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-  Reported at {
-    ... DMD.cpp
-  }
-}
-
-Once-reported {
-  1 block in heap block record 8 of 11
-  80 bytes (80 requested / 0 slop)
-  0.66% of the heap (86.70% cumulative)
-  0.76% of once-reported (99.02% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-  Reported at {
-    ... DMD.cpp
-  }
-}
-
-Once-reported {
-  1 block in heap block record 9 of 11
-  80 bytes (80 requested / 0 slop)
-  0.66% of the heap (87.36% cumulative)
-  0.76% of once-reported (99.77% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-  Reported at {
-    ... DMD.cpp
-  }
-}
-
-Once-reported {
-  1 block in heap block record 10 of 11
-  16 bytes (10 requested / 6 slop)
-  0.13% of the heap (87.49% cumulative)
-  0.15% of once-reported (99.92% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-  Reported at {
-    ... DMD.cpp
-  }
-}
-
-Once-reported {
-  1 block in heap block record 11 of 11
-  8 bytes (0 requested / 8 slop)
-  0.07% of the heap (87.56% cumulative)
-  0.08% of once-reported (100.00% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-  Reported at {
-    ... DMD.cpp
-  }
-}
-
-#-----------------------------------------------------------------
-
-Summary {
-  Total:                12,088 bytes (100.00%) in      30 blocks (100.00%)
-  Unreported:            1,232 bytes ( 10.19%) in      13 blocks ( 43.33%)
-  Once-reported:        10,584 bytes ( 87.56%) in      13 blocks ( 43.33%)
-  Twice-reported:          272 bytes (  2.25%) in       4 blocks ( 13.33%)
-}
-
-#-----------------------------------------------------------------
-
-Invocation {
-  $DMD = '--mode=test'
-  Function = AnalyzeHeap
-  Sample-below size = 1
-}
-
-#-----------------------------------------------------------------
-
-Live {
-  1 block in heap block record 1 of 12
-  8,192 bytes (4,097 requested / 4,095 slop)
-  67.77% of the heap (67.77% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-}
-
-Live {
-  1 block in heap block record 2 of 12
-  1,024 bytes (1,023 requested / 1 slop)
-  8.47% of the heap (76.24% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-}
-
-Live {
-  9 blocks in heap block record 3 of 12
-  1,008 bytes (900 requested / 108 slop)
-  8.34% of the heap (84.58% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-}
-
-Live {
-  6 blocks in heap block record 4 of 12
-  528 bytes (528 requested / 0 slop)
-  4.37% of the heap (88.95% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-}
-
-Live {
-  6 blocks in heap block record 5 of 12
-  528 bytes (528 requested / 0 slop)
-  4.37% of the heap (93.32% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-}
-
-Live {
-  1 block in heap block record 6 of 12
-  512 bytes (512 requested / 0 slop)
-  4.24% of the heap (97.55% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-}
-
-Live {
-  1 block in heap block record 7 of 12
-  80 bytes (79 requested / 1 slop)
-  0.66% of the heap (98.21% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-}
-
-Live {
-  1 block in heap block record 8 of 12
-  80 bytes (78 requested / 2 slop)
-  0.66% of the heap (98.87% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-}
-
-Live {
-  1 block in heap block record 9 of 12
-  80 bytes (77 requested / 3 slop)
-  0.66% of the heap (99.54% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-}
-
-Live {
-  1 block in heap block record 10 of 12
-  32 bytes (30 requested / 2 slop)
-  0.26% of the heap (99.80% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-}
-
-Live {
-  1 block in heap block record 11 of 12
-  16 bytes (10 requested / 6 slop)
-  0.13% of the heap (99.93% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-}
-
-Live {
-  1 block in heap block record 12 of 12
-  8 bytes (0 requested / 8 slop)
-  0.07% of the heap (100.00% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-}
-
-#-----------------------------------------------------------------
-
-Summary {
-  Total: 12,088 bytes in 30 blocks
-}
-
-#-----------------------------------------------------------------
-
-Invocation {
-  $DMD = '--mode=test'
-  Function = AnalyzeReports
-  Sample-below size = 1
-}
-
-#-----------------------------------------------------------------
-
-Twice-reported {
-  1 block in heap block record 1 of 2
-  80 bytes (77 requested / 3 slop)
-  2.82% of the heap (2.82% cumulative)
-  90.91% of twice-reported (90.91% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-  Reported at {
-    ... DMD.cpp
-  }
-  Reported again at {
-    ... DMD.cpp
-  }
-}
-
-Twice-reported {
-  1 block in heap block record 2 of 2
-  8 bytes (0 requested / 8 slop)
-  0.28% of the heap (3.10% cumulative)
-  9.09% of twice-reported (100.00% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-  Reported at {
-    ... DMD.cpp
-  }
-  Reported again at {
-    ... DMD.cpp
-  }
-}
-
-#-----------------------------------------------------------------
-
-Unreported {
-  9 blocks in heap block record 1 of 3
-  1,008 bytes (900 requested / 108 slop)
-  35.49% of the heap (35.49% cumulative)
-  48.84% of unreported (48.84% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-}
-
-Unreported {
-  6 blocks in heap block record 2 of 3
-  528 bytes (528 requested / 0 slop)
-  18.59% of the heap (54.08% cumulative)
-  25.58% of unreported (74.42% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-}
-
-Unreported {
-  6 blocks in heap block record 3 of 3
-  528 bytes (528 requested / 0 slop)
-  18.59% of the heap (72.68% cumulative)
-  25.58% of unreported (100.00% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-}
-
-#-----------------------------------------------------------------
-
-Once-reported {
-  1 block in heap block record 1 of 4
-  512 bytes (512 requested / 0 slop)
-  18.03% of the heap (18.03% cumulative)
-  74.42% of once-reported (74.42% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-  Reported at {
-    ... DMD.cpp
-  }
-}
-
-Once-reported {
-  1 block in heap block record 2 of 4
-  80 bytes (79 requested / 1 slop)
-  2.82% of the heap (20.85% cumulative)
-  11.63% of once-reported (86.05% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-  Reported at {
-    ... DMD.cpp
-  }
-}
-
-Once-reported {
-  1 block in heap block record 3 of 4
-  80 bytes (78 requested / 2 slop)
-  2.82% of the heap (23.66% cumulative)
-  11.63% of once-reported (97.67% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-  Reported at {
-    ... DMD.cpp
-  }
-}
-
-Once-reported {
-  1 block in heap block record 4 of 4
-  16 bytes (10 requested / 6 slop)
-  0.56% of the heap (24.23% cumulative)
-  2.33% of once-reported (100.00% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-  Reported at {
-    ... DMD.cpp
-  }
-}
-
-#-----------------------------------------------------------------
-
-Summary {
-  Total:                 2,840 bytes (100.00%) in      27 blocks (100.00%)
-  Unreported:            2,064 bytes ( 72.68%) in      21 blocks ( 77.78%)
-  Once-reported:           688 bytes ( 24.23%) in       4 blocks ( 14.81%)
-  Twice-reported:           88 bytes (  3.10%) in       2 blocks (  7.41%)
-}
-
-#-----------------------------------------------------------------
-
-Invocation {
-  $DMD = '--mode=test'
-  Function = AnalyzeHeap
-  Sample-below size = 1
-}
-
-#-----------------------------------------------------------------
-
-Live {
-  9 blocks in heap block record 1 of 9
-  1,008 bytes (900 requested / 108 slop)
-  35.49% of the heap (35.49% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-}
-
-Live {
-  6 blocks in heap block record 2 of 9
-  528 bytes (528 requested / 0 slop)
-  18.59% of the heap (54.08% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-}
-
-Live {
-  6 blocks in heap block record 3 of 9
-  528 bytes (528 requested / 0 slop)
-  18.59% of the heap (72.68% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-}
-
-Live {
-  1 block in heap block record 4 of 9
-  512 bytes (512 requested / 0 slop)
-  18.03% of the heap (90.70% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-}
-
-Live {
-  1 block in heap block record 5 of 9
-  80 bytes (79 requested / 1 slop)
-  2.82% of the heap (93.52% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-}
-
-Live {
-  1 block in heap block record 6 of 9
-  80 bytes (78 requested / 2 slop)
-  2.82% of the heap (96.34% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-}
-
-Live {
-  1 block in heap block record 7 of 9
-  80 bytes (77 requested / 3 slop)
-  2.82% of the heap (99.15% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-}
-
-Live {
-  1 block in heap block record 8 of 9
-  16 bytes (10 requested / 6 slop)
-  0.56% of the heap (99.72% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-}
-
-Live {
-  1 block in heap block record 9 of 9
-  8 bytes (0 requested / 8 slop)
-  0.28% of the heap (100.00% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-}
-
-#-----------------------------------------------------------------
-
-Summary {
-  Total: 2,840 bytes in 27 blocks
-}
-
-#-----------------------------------------------------------------
-
-Invocation {
-  $DMD = '--mode=test'
-  Function = AnalyzeReports
-  Sample-below size = 128
-}
-
-#-----------------------------------------------------------------
-
-# no twice-reported heap blocks
-
-#-----------------------------------------------------------------
-
-Unreported {
-  ~4 blocks in heap block record 1 of 7
-  ~512 bytes (~512 requested / ~0 slop)
-  35.96% of the heap (35.96% cumulative)
-  35.96% of unreported (35.96% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-}
-
-Unreported {
-  1 block in heap block record 2 of 7
-  256 bytes (256 requested / 0 slop)
-  17.98% of the heap (53.93% cumulative)
-  17.98% of unreported (53.93% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-}
-
-Unreported {
-  1 block in heap block record 3 of 7
-  144 bytes (144 requested / 0 slop)
-  10.11% of the heap (64.04% cumulative)
-  10.11% of unreported (64.04% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-}
-
-Unreported {
-  1 block in heap block record 4 of 7
-  128 bytes (128 requested / 0 slop)
-  8.99% of the heap (73.03% cumulative)
-  8.99% of unreported (73.03% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-}
-
-Unreported {
-  ~1 block in heap block record 5 of 7
-  ~128 bytes (~128 requested / ~0 slop)
-  8.99% of the heap (82.02% cumulative)
-  8.99% of unreported (82.02% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-}
-
-Unreported {
-  ~1 block in heap block record 6 of 7
-  ~128 bytes (~128 requested / ~0 slop)
-  8.99% of the heap (91.01% cumulative)
-  8.99% of unreported (91.01% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-}
-
-Unreported {
-  ~1 block in heap block record 7 of 7
-  ~128 bytes (~128 requested / ~0 slop)
-  8.99% of the heap (100.00% cumulative)
-  8.99% of unreported (100.00% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-}
-
-#-----------------------------------------------------------------
-
-# no once-reported heap blocks
-
-#-----------------------------------------------------------------
-
-Summary {
-  Total:                ~1,424 bytes (100.00%) in     ~10 blocks (100.00%)
-  Unreported:           ~1,424 bytes (100.00%) in     ~10 blocks (100.00%)
-  Once-reported:            ~0 bytes (  0.00%) in      ~0 blocks (  0.00%)
-  Twice-reported:           ~0 bytes (  0.00%) in      ~0 blocks (  0.00%)
-}
-
-#-----------------------------------------------------------------
-
-Invocation {
-  $DMD = '--mode=test'
-  Function = AnalyzeHeap
-  Sample-below size = 128
-}
-
-#-----------------------------------------------------------------
-
-Live {
-  ~4 blocks in heap block record 1 of 7
-  ~512 bytes (~512 requested / ~0 slop)
-  35.96% of the heap (35.96% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-}
-
-Live {
-  1 block in heap block record 2 of 7
-  256 bytes (256 requested / 0 slop)
-  17.98% of the heap (53.93% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-}
-
-Live {
-  1 block in heap block record 3 of 7
-  144 bytes (144 requested / 0 slop)
-  10.11% of the heap (64.04% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-}
-
-Live {
-  1 block in heap block record 4 of 7
-  128 bytes (128 requested / 0 slop)
-  8.99% of the heap (73.03% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-}
-
-Live {
-  ~1 block in heap block record 5 of 7
-  ~128 bytes (~128 requested / ~0 slop)
-  8.99% of the heap (82.02% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-}
-
-Live {
-  ~1 block in heap block record 6 of 7
-  ~128 bytes (~128 requested / ~0 slop)
-  8.99% of the heap (91.01% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-}
-
-Live {
-  ~1 block in heap block record 7 of 7
-  ~128 bytes (~128 requested / ~0 slop)
-  8.99% of the heap (100.00% cumulative)
-  Allocated at {
-    ... DMD.cpp
-  }
-}
-
-#-----------------------------------------------------------------
-
-Summary {
-  Total: ~1,424 bytes in ~10 blocks
-}
-
new file mode 100644
--- /dev/null
+++ b/memory/replace/dmd/test/full-heap-expected1.txt
@@ -0,0 +1,17 @@
+#-----------------------------------------------------------------
+
+Invocation {
+  $DMD = '--mode=test'
+  Sample-below size = 1
+}
+
+#-----------------------------------------------------------------
+
+# no live heap blocks
+
+#-----------------------------------------------------------------
+
+Summary {
+  Total: 0 bytes in 0 blocks
+}
+
new file mode 100644
--- /dev/null
+++ b/memory/replace/dmd/test/full-heap-expected2.txt
@@ -0,0 +1,123 @@
+#-----------------------------------------------------------------
+
+Invocation {
+  $DMD = '--mode=test'
+  Sample-below size = 1
+}
+
+#-----------------------------------------------------------------
+
+Live {
+  1 block in heap block record 1 of 12
+  8,192 bytes (4,097 requested / 4,095 slop)
+  67.77% of the heap (67.77% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
+
+Live {
+  1 block in heap block record 2 of 12
+  1,024 bytes (1,023 requested / 1 slop)
+  8.47% of the heap (76.24% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
+
+Live {
+  9 blocks in heap block record 3 of 12
+  1,008 bytes (900 requested / 108 slop)
+  8.34% of the heap (84.58% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
+
+Live {
+  6 blocks in heap block record 4 of 12
+  528 bytes (528 requested / 0 slop)
+  4.37% of the heap (88.95% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
+
+Live {
+  6 blocks in heap block record 5 of 12
+  528 bytes (528 requested / 0 slop)
+  4.37% of the heap (93.32% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
+
+Live {
+  1 block in heap block record 6 of 12
+  512 bytes (512 requested / 0 slop)
+  4.24% of the heap (97.55% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
+
+Live {
+  1 block in heap block record 7 of 12
+  80 bytes (79 requested / 1 slop)
+  0.66% of the heap (98.21% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
+
+Live {
+  1 block in heap block record 8 of 12
+  80 bytes (78 requested / 2 slop)
+  0.66% of the heap (98.87% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
+
+Live {
+  1 block in heap block record 9 of 12
+  80 bytes (77 requested / 3 slop)
+  0.66% of the heap (99.54% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
+
+Live {
+  1 block in heap block record 10 of 12
+  32 bytes (30 requested / 2 slop)
+  0.26% of the heap (99.80% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
+
+Live {
+  1 block in heap block record 11 of 12
+  16 bytes (10 requested / 6 slop)
+  0.13% of the heap (99.93% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
+
+Live {
+  1 block in heap block record 12 of 12
+  8 bytes (0 requested / 8 slop)
+  0.07% of the heap (100.00% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
+
+#-----------------------------------------------------------------
+
+Summary {
+  Total: 12,088 bytes in 30 blocks
+}
+
new file mode 100644
--- /dev/null
+++ b/memory/replace/dmd/test/full-heap-expected3.txt
@@ -0,0 +1,96 @@
+#-----------------------------------------------------------------
+
+Invocation {
+  $DMD = '--mode=test'
+  Sample-below size = 1
+}
+
+#-----------------------------------------------------------------
+
+Live {
+  9 blocks in heap block record 1 of 9
+  1,008 bytes (900 requested / 108 slop)
+  35.49% of the heap (35.49% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
+
+Live {
+  6 blocks in heap block record 2 of 9
+  528 bytes (528 requested / 0 slop)
+  18.59% of the heap (54.08% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
+
+Live {
+  6 blocks in heap block record 3 of 9
+  528 bytes (528 requested / 0 slop)
+  18.59% of the heap (72.68% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
+
+Live {
+  1 block in heap block record 4 of 9
+  512 bytes (512 requested / 0 slop)
+  18.03% of the heap (90.70% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
+
+Live {
+  1 block in heap block record 5 of 9
+  80 bytes (79 requested / 1 slop)
+  2.82% of the heap (93.52% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
+
+Live {
+  1 block in heap block record 6 of 9
+  80 bytes (78 requested / 2 slop)
+  2.82% of the heap (96.34% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
+
+Live {
+  1 block in heap block record 7 of 9
+  80 bytes (77 requested / 3 slop)
+  2.82% of the heap (99.15% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
+
+Live {
+  1 block in heap block record 8 of 9
+  16 bytes (10 requested / 6 slop)
+  0.56% of the heap (99.72% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
+
+Live {
+  1 block in heap block record 9 of 9
+  8 bytes (0 requested / 8 slop)
+  0.28% of the heap (100.00% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
+
+#-----------------------------------------------------------------
+
+Summary {
+  Total: 2,840 bytes in 27 blocks
+}
+
new file mode 100644
--- /dev/null
+++ b/memory/replace/dmd/test/full-heap-expected4.txt
@@ -0,0 +1,78 @@
+#-----------------------------------------------------------------
+
+Invocation {
+  $DMD = '--mode=test'
+  Sample-below size = 128
+}
+
+#-----------------------------------------------------------------
+
+Live {
+  ~4 blocks in heap block record 1 of 7
+  ~512 bytes (~512 requested / ~0 slop)
+  35.96% of the heap (35.96% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
+
+Live {
+  1 block in heap block record 2 of 7
+  256 bytes (256 requested / 0 slop)
+  17.98% of the heap (53.93% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
+
+Live {
+  1 block in heap block record 3 of 7
+  144 bytes (144 requested / 0 slop)
+  10.11% of the heap (64.04% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
+
+Live {
+  1 block in heap block record 4 of 7
+  128 bytes (128 requested / 0 slop)
+  8.99% of the heap (73.03% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
+
+Live {
+  ~1 block in heap block record 5 of 7
+  ~128 bytes (~128 requested / ~0 slop)
+  8.99% of the heap (82.02% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
+
+Live {
+  ~1 block in heap block record 6 of 7
+  ~128 bytes (~128 requested / ~0 slop)
+  8.99% of the heap (91.01% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
+
+Live {
+  ~1 block in heap block record 7 of 7
+  ~128 bytes (~128 requested / ~0 slop)
+  8.99% of the heap (100.00% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
+
+#-----------------------------------------------------------------
+
+Summary {
+  Total: ~1,424 bytes in ~10 blocks
+}
+
new file mode 100644
--- /dev/null
+++ b/memory/replace/dmd/test/full-reports-expected1.txt
@@ -0,0 +1,28 @@
+#-----------------------------------------------------------------
+
+Invocation {
+  $DMD = '--mode=test'
+  Sample-below size = 1
+}
+
+#-----------------------------------------------------------------
+
+# no twice-reported heap blocks
+
+#-----------------------------------------------------------------
+
+# no unreported heap blocks
+
+#-----------------------------------------------------------------
+
+# no once-reported heap blocks
+
+#-----------------------------------------------------------------
+
+Summary {
+  Total:                     0 bytes (100.00%) in       0 blocks (100.00%)
+  Unreported:                0 bytes (  0.00%) in       0 blocks (  0.00%)
+  Once-reported:             0 bytes (  0.00%) in       0 blocks (  0.00%)
+  Twice-reported:            0 bytes (  0.00%) in       0 blocks (  0.00%)
+}
+
new file mode 100644
--- /dev/null
+++ b/memory/replace/dmd/test/full-reports-expected2.txt
@@ -0,0 +1,259 @@
+#-----------------------------------------------------------------
+
+Invocation {
+  $DMD = '--mode=test'
+  Sample-below size = 1
+}
+
+#-----------------------------------------------------------------
+
+Twice-reported {
+  1 block in heap block record 1 of 4
+  80 bytes (79 requested / 1 slop)
+  0.66% of the heap (0.66% cumulative)
+  29.41% of twice-reported (29.41% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+  Reported at {
+    ... DMD.cpp
+  }
+  Reported again at {
+    ... DMD.cpp
+  }
+}
+
+Twice-reported {
+  1 block in heap block record 2 of 4
+  80 bytes (78 requested / 2 slop)
+  0.66% of the heap (1.32% cumulative)
+  29.41% of twice-reported (58.82% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+  Reported at {
+    ... DMD.cpp
+  }
+  Reported again at {
+    ... DMD.cpp
+  }
+}
+
+Twice-reported {
+  1 block in heap block record 3 of 4
+  80 bytes (77 requested / 3 slop)
+  0.66% of the heap (1.99% cumulative)
+  29.41% of twice-reported (88.24% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+  Reported at {
+    ... DMD.cpp
+  }
+  Reported again at {
+    ... DMD.cpp
+  }
+}
+
+Twice-reported {
+  1 block in heap block record 4 of 4
+  32 bytes (30 requested / 2 slop)
+  0.26% of the heap (2.25% cumulative)
+  11.76% of twice-reported (100.00% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+  Reported at {
+    ... DMD.cpp
+  }
+  Reported again at {
+    ... DMD.cpp
+  }
+}
+
+#-----------------------------------------------------------------
+
+Unreported {
+  9 blocks in heap block record 1 of 3
+  1,008 bytes (900 requested / 108 slop)
+  8.34% of the heap (8.34% cumulative)
+  81.82% of unreported (81.82% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
+
+Unreported {
+  2 blocks in heap block record 2 of 3
+  112 bytes (112 requested / 0 slop)
+  0.93% of the heap (9.27% cumulative)
+  9.09% of unreported (90.91% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
+
+Unreported {
+  2 blocks in heap block record 3 of 3
+  112 bytes (112 requested / 0 slop)
+  0.93% of the heap (10.19% cumulative)
+  9.09% of unreported (100.00% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
+
+#-----------------------------------------------------------------
+
+Once-reported {
+  1 block in heap block record 1 of 11
+  8,192 bytes (4,097 requested / 4,095 slop)
+  67.77% of the heap (67.77% cumulative)
+  77.40% of once-reported (77.40% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+  Reported at {
+    ... DMD.cpp
+  }
+}
+
+Once-reported {
+  1 block in heap block record 2 of 11
+  1,024 bytes (1,023 requested / 1 slop)
+  8.47% of the heap (76.24% cumulative)
+  9.67% of once-reported (87.07% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+  Reported at {
+    ... DMD.cpp
+  }
+}
+
+Once-reported {
+  1 block in heap block record 3 of 11
+  512 bytes (512 requested / 0 slop)
+  4.24% of the heap (80.48% cumulative)
+  4.84% of once-reported (91.91% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+  Reported at {
+    ... DMD.cpp
+  }
+}
+
+Once-reported {
+  2 blocks in heap block record 4 of 11
+  240 bytes (240 requested / 0 slop)
+  1.99% of the heap (82.46% cumulative)
+  2.27% of once-reported (94.18% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+  Reported at {
+    ... DMD.cpp
+  }
+}
+
+Once-reported {
+  2 blocks in heap block record 5 of 11
+  240 bytes (240 requested / 0 slop)
+  1.99% of the heap (84.45% cumulative)
+  2.27% of once-reported (96.45% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+  Reported at {
+    ... DMD.cpp
+  }
+}
+
+Once-reported {
+  1 block in heap block record 6 of 11
+  96 bytes (96 requested / 0 slop)
+  0.79% of the heap (85.24% cumulative)
+  0.91% of once-reported (97.35% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+  Reported at {
+    ... DMD.cpp
+  }
+}
+
+Once-reported {
+  1 block in heap block record 7 of 11
+  96 bytes (96 requested / 0 slop)
+  0.79% of the heap (86.04% cumulative)
+  0.91% of once-reported (98.26% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+  Reported at {
+    ... DMD.cpp
+  }
+}
+
+Once-reported {
+  1 block in heap block record 8 of 11
+  80 bytes (80 requested / 0 slop)
+  0.66% of the heap (86.70% cumulative)
+  0.76% of once-reported (99.02% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+  Reported at {
+    ... DMD.cpp
+  }
+}
+
+Once-reported {
+  1 block in heap block record 9 of 11
+  80 bytes (80 requested / 0 slop)
+  0.66% of the heap (87.36% cumulative)
+  0.76% of once-reported (99.77% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+  Reported at {
+    ... DMD.cpp
+  }
+}
+
+Once-reported {
+  1 block in heap block record 10 of 11
+  16 bytes (10 requested / 6 slop)
+  0.13% of the heap (87.49% cumulative)
+  0.15% of once-reported (99.92% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+  Reported at {
+    ... DMD.cpp
+  }
+}
+
+Once-reported {
+  1 block in heap block record 11 of 11
+  8 bytes (0 requested / 8 slop)
+  0.07% of the heap (87.56% cumulative)
+  0.08% of once-reported (100.00% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+  Reported at {
+    ... DMD.cpp
+  }
+}
+
+#-----------------------------------------------------------------
+
+Summary {
+  Total:                12,088 bytes (100.00%) in      30 blocks (100.00%)
+  Unreported:            1,232 bytes ( 10.19%) in      13 blocks ( 43.33%)
+  Once-reported:        10,584 bytes ( 87.56%) in      13 blocks ( 43.33%)
+  Twice-reported:          272 bytes (  2.25%) in       4 blocks ( 13.33%)
+}
+
new file mode 100644
--- /dev/null
+++ b/memory/replace/dmd/test/full-reports-expected3.txt
@@ -0,0 +1,136 @@
+#-----------------------------------------------------------------
+
+Invocation {
+  $DMD = '--mode=test'
+  Sample-below size = 1
+}
+
+#-----------------------------------------------------------------
+
+Twice-reported {
+  1 block in heap block record 1 of 2
+  80 bytes (77 requested / 3 slop)
+  2.82% of the heap (2.82% cumulative)
+  90.91% of twice-reported (90.91% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+  Reported at {
+    ... DMD.cpp
+  }
+  Reported again at {
+    ... DMD.cpp
+  }
+}
+
+Twice-reported {
+  1 block in heap block record 2 of 2
+  8 bytes (0 requested / 8 slop)
+  0.28% of the heap (3.10% cumulative)
+  9.09% of twice-reported (100.00% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+  Reported at {
+    ... DMD.cpp
+  }
+  Reported again at {
+    ... DMD.cpp
+  }
+}
+
+#-----------------------------------------------------------------
+
+Unreported {
+  9 blocks in heap block record 1 of 3
+  1,008 bytes (900 requested / 108 slop)
+  35.49% of the heap (35.49% cumulative)
+  48.84% of unreported (48.84% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
+
+Unreported {
+  6 blocks in heap block record 2 of 3
+  528 bytes (528 requested / 0 slop)
+  18.59% of the heap (54.08% cumulative)
+  25.58% of unreported (74.42% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
+
+Unreported {
+  6 blocks in heap block record 3 of 3
+  528 bytes (528 requested / 0 slop)
+  18.59% of the heap (72.68% cumulative)
+  25.58% of unreported (100.00% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
+
+#-----------------------------------------------------------------
+
+Once-reported {
+  1 block in heap block record 1 of 4
+  512 bytes (512 requested / 0 slop)
+  18.03% of the heap (18.03% cumulative)
+  74.42% of once-reported (74.42% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+  Reported at {
+    ... DMD.cpp
+  }
+}
+
+Once-reported {
+  1 block in heap block record 2 of 4
+  80 bytes (79 requested / 1 slop)
+  2.82% of the heap (20.85% cumulative)
+  11.63% of once-reported (86.05% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+  Reported at {
+    ... DMD.cpp
+  }
+}
+
+Once-reported {
+  1 block in heap block record 3 of 4
+  80 bytes (78 requested / 2 slop)
+  2.82% of the heap (23.66% cumulative)
+  11.63% of once-reported (97.67% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+  Reported at {
+    ... DMD.cpp
+  }
+}
+
+Once-reported {
+  1 block in heap block record 4 of 4
+  16 bytes (10 requested / 6 slop)
+  0.56% of the heap (24.23% cumulative)
+  2.33% of once-reported (100.00% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+  Reported at {
+    ... DMD.cpp
+  }
+}
+
+#-----------------------------------------------------------------
+
+Summary {
+  Total:                 2,840 bytes (100.00%) in      27 blocks (100.00%)
+  Unreported:            2,064 bytes ( 72.68%) in      21 blocks ( 77.78%)
+  Once-reported:           688 bytes ( 24.23%) in       4 blocks ( 14.81%)
+  Twice-reported:           88 bytes (  3.10%) in       2 blocks (  7.41%)
+}
+
new file mode 100644
--- /dev/null
+++ b/memory/replace/dmd/test/full-reports-expected4.txt
@@ -0,0 +1,96 @@
+#-----------------------------------------------------------------
+
+Invocation {
+  $DMD = '--mode=test'
+  Sample-below size = 128
+}
+
+#-----------------------------------------------------------------
+
+# no twice-reported heap blocks
+
+#-----------------------------------------------------------------
+
+Unreported {
+  ~4 blocks in heap block record 1 of 7
+  ~512 bytes (~512 requested / ~0 slop)
+  35.96% of the heap (35.96% cumulative)
+  35.96% of unreported (35.96% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
+
+Unreported {
+  1 block in heap block record 2 of 7
+  256 bytes (256 requested / 0 slop)
+  17.98% of the heap (53.93% cumulative)
+  17.98% of unreported (53.93% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
+
+Unreported {
+  1 block in heap block record 3 of 7
+  144 bytes (144 requested / 0 slop)
+  10.11% of the heap (64.04% cumulative)
+  10.11% of unreported (64.04% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
+
+Unreported {
+  1 block in heap block record 4 of 7
+  128 bytes (128 requested / 0 slop)
+  8.99% of the heap (73.03% cumulative)
+  8.99% of unreported (73.03% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
+
+Unreported {
+  ~1 block in heap block record 5 of 7
+  ~128 bytes (~128 requested / ~0 slop)
+  8.99% of the heap (82.02% cumulative)
+  8.99% of unreported (82.02% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
+
+Unreported {
+  ~1 block in heap block record 6 of 7
+  ~128 bytes (~128 requested / ~0 slop)
+  8.99% of the heap (91.01% cumulative)
+  8.99% of unreported (91.01% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
+
+Unreported {
+  ~1 block in heap block record 7 of 7
+  ~128 bytes (~128 requested / ~0 slop)
+  8.99% of the heap (100.00% cumulative)
+  8.99% of unreported (100.00% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
+
+#-----------------------------------------------------------------
+
+# no once-reported heap blocks
+
+#-----------------------------------------------------------------
+
+Summary {
+  Total:                ~1,424 bytes (100.00%) in     ~10 blocks (100.00%)
+  Unreported:           ~1,424 bytes (100.00%) in     ~10 blocks (100.00%)
+  Once-reported:            ~0 bytes (  0.00%) in      ~0 blocks (  0.00%)
+  Twice-reported:           ~0 bytes (  0.00%) in      ~0 blocks (  0.00%)
+}
+
--- a/python/mozbuild/mozbuild/mach_commands.py
+++ b/python/mozbuild/mozbuild/mach_commands.py
@@ -929,42 +929,44 @@ class RunDmd(MachCommandBase):
         help=('Command-line arguments to be passed through to the program. '
               'Not specifying a -profile or -P option will result in a '
               'temporary profile being used. If passing -params use a "--" to '
               'indicate the start of params to pass to firefox.'))
     @CommandArgument('--remote', '-r', action='store_true',
         help='Do not pass the -no-remote argument by default.')
     @CommandArgument('--background', '-b', action='store_true',
         help='Do not pass the -foreground argument by default on Mac')
+    @CommandArgument('--noprofile', '-n', action='store_true',
+        help='Do not pass the -profile argument by default.')
     @CommandArgument('--sample-below', default=None, type=str,
         help='The sample size to use, [1..n]. Default is 4093.')
     @CommandArgument('--max-frames', default=None, type=str,
         help='The max number of stack frames to capture in allocation traces, [1..24] Default is 24.')
-    @CommandArgument('--max-records', default=None, type=str,
-        help='Number of stack trace records to print of each kind, [1..1000000]. Default is 1000.')
-    def dmd(self, params, remote, background, sample_below, max_frames, max_records):
-        args = get_run_args(self, params, remote, background)
+    @CommandArgument('--show-dump-stats', action='store_true',
+        help='Show stats when doing dumps.')
+    def dmd(self, params, remote, background, noprofile, sample_below, max_frames, show_dump_stats):
+        args = get_run_args(self, params, remote, background, noprofile)
         if not args:
             return 1
 
         lib_dir = os.path.join(self.distdir, 'lib')
         lib_name = self.substs['DLL_PREFIX'] + 'dmd' + self.substs['DLL_SUFFIX']
         dmd_lib = os.path.join(lib_dir, lib_name)
         if not os.path.exists(dmd_lib):
             print("You need to build with |--enable-dmd| to use dmd.")
             return 1
 
         dmd_params = []
 
         if sample_below:
             dmd_params.append('--sample-below=' + sample_below)
         if max_frames:
             dmd_params.append('--max-frames=' + max_frames)
-        if max_records:
-            dmd_params.append('--max-records=' + max_records)
+        if show_dump_stats:
+            dmd_params.append('--show-dump-stats=yes')
 
         if dmd_params:
             dmd_str = " ".join(dmd_params)
         else:
             dmd_str = "1"
 
         env_vars = {
             "Darwin": {
--- a/security/manager/ssl/src/SSLServerCertVerification.cpp
+++ b/security/manager/ssl/src/SSLServerCertVerification.cpp
@@ -398,18 +398,17 @@ CertErrorRunnable::CheckCertOverrides()
     NS_ERROR("CertErrorRunnable::CheckCertOverrides called off main thread");
     return new SSLServerCertVerificationResult(mInfoObject,
                                                mDefaultErrorCodeToReport);
   }
 
   int32_t port;
   mInfoObject->GetPort(&port);
 
-  nsCString hostWithPortString;
-  hostWithPortString.AppendASCII(mInfoObject->GetHostNameRaw());
+  nsAutoCString hostWithPortString(mInfoObject->GetHostName());
   hostWithPortString.Append(':');
   hostWithPortString.AppendInt(port);
 
   uint32_t remaining_display_errors = mCollectedErrors;
 
   nsresult nsrv;
 
   // Enforce Strict-Transport-Security for hosts that are "STS" hosts:
@@ -435,17 +434,17 @@ CertErrorRunnable::CheckCertOverrides()
     // it is fine to continue without the nsICertOverrideService
 
     uint32_t overrideBits = 0;
 
     if (overrideService)
     {
       bool haveOverride;
       bool isTemporaryOverride; // we don't care
-      nsCString hostString(mInfoObject->GetHostName());
+      const nsACString& hostString(mInfoObject->GetHostName());
       nsrv = overrideService->HasMatchingOverride(hostString, port,
                                                   mCert,
                                                   &overrideBits,
                                                   &isTemporaryOverride,
                                                   &haveOverride);
       if (NS_SUCCEEDED(nsrv) && haveOverride)
       {
        // remove the errors that are already overriden
@@ -762,35 +761,20 @@ AccumulateSubjectCommonNameTelemetry(con
   }
 }
 
 // Returns true if and only if commonName ends with altName (minus its leading
 // "*"). altName has already been checked to be of the form "*.<something>".
 // commonName may be NULL.
 static bool
 TryMatchingWildcardSubjectAltName(const char* commonName,
-                                  nsDependentCString altName)
+                                  const nsACString& altName)
 {
-  if (!commonName) {
-    return false;
-  }
-  // altNameSubstr is now ".<something>"
-  nsDependentCString altNameSubstr(altName.get() + 1, altName.Length() - 1);
-  nsDependentCString commonNameStr(commonName, strlen(commonName));
-  int32_t altNameIndex = commonNameStr.Find(altNameSubstr);
-  // This only matches if the end of commonNameStr is the altName without
-  // the '*'.
-  // Consider this.example.com and *.example.com:
-  // "this.example.com".Find(".example.com") is 4
-  // 4 + ".example.com".Length() == 4 + 12 == 16 == "this.example.com".Length()
-  // Now this.example.com and *.example:
-  // "this.example.com".Find(".example") is 4
-  // 4 + ".example".Length() == 4 + 8 == 12 != "this.example.com".Length()
-  return altNameIndex >= 0 &&
-         altNameIndex + altNameSubstr.Length() == commonNameStr.Length();
+  return commonName &&
+         StringEndsWith(nsDependentCString(commonName), Substring(altName, 1));
 }
 
 // Gathers telemetry on Baseline Requirements 9.2.1 (Subject Alternative
 // Names Extension) and 9.2.2 (Subject Common Name Field).
 // Specifically:
 //  - whether or not the subject common name field is present
 //  - whether or not the subject alternative names extension is present
 //  - if there is a malformed entry in the subject alt. names extension
@@ -854,23 +838,23 @@ GatherBaselineRequirementsTelemetry(cons
   }
 
   CERTGeneralName* currentName = subjectAltNames;
   bool commonNameInSubjectAltNames = false;
   bool nonDNSNameOrIPAddressPresent = false;
   bool malformedDNSNameOrIPAddressPresent = false;
   bool nonFQDNPresent = false;
   do {
-    nsDependentCString altName;
+    nsAutoCString altName;
     if (currentName->type == certDNSName) {
       altName.Assign(reinterpret_cast<char*>(currentName->name.other.data),
                      currentName->name.other.len);
-      nsDependentCString altNameWithoutWildcard(altName);
-      if (altNameWithoutWildcard.Find("*.") == 0) {
-        altNameWithoutWildcard.Assign(altName.get() + 2, altName.Length() - 2);
+      nsDependentCString altNameWithoutWildcard(altName, 0);
+      if (StringBeginsWith(altNameWithoutWildcard, NS_LITERAL_CSTRING("*."))) {
+        altNameWithoutWildcard.Rebind(altName, 2);
         commonNameInSubjectAltNames |=
           TryMatchingWildcardSubjectAltName(commonName.get(), altName);
       }
       // net_IsValidHostName appears to return true for valid IP addresses,
       // which would be invalid for a DNS name.
       // Note that the net_IsValidHostName check will catch things like
       // "a.*.example.com".
       if (!net_IsValidHostName(altNameWithoutWildcard) ||
@@ -893,29 +877,29 @@ GatherBaselineRequirementsTelemetry(cons
         memcpy(&addr.inet.ip, currentName->name.other.data,
                currentName->name.other.len);
         if (PR_NetAddrToString(&addr, buf, sizeof(buf) - 1) != PR_SUCCESS) {
         PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
                ("BR telemetry: IPAddress (v4) not valid (for '%s')\n",
                 commonName.get()));
           malformedDNSNameOrIPAddressPresent = true;
         } else {
-          altName.Assign(buf, strlen(buf));
+          altName.Assign(buf);
         }
       } else if (currentName->name.other.len == 16) {
         addr.inet.family = PR_AF_INET6;
         memcpy(&addr.ipv6.ip, currentName->name.other.data,
                currentName->name.other.len);
         if (PR_NetAddrToString(&addr, buf, sizeof(buf) - 1) != PR_SUCCESS) {
         PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
                ("BR telemetry: IPAddress (v6) not valid (for '%s')\n",
                 commonName.get()));
           malformedDNSNameOrIPAddressPresent = true;
         } else {
-          altName.Assign(buf, strlen(buf));
+          altName.Assign(buf);
         }
       } else {
         PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
                ("BR telemetry: IPAddress not valid (for '%s')\n",
                 commonName.get()));
         malformedDNSNameOrIPAddressPresent = true;
       }
     } else {
--- a/security/sandbox/win/src/warnonlysandbox/wosCallbacks.h
+++ b/security/sandbox/win/src/warnonlysandbox/wosCallbacks.h
@@ -54,24 +54,26 @@ SetProvideLogFunctionCb(ProvideLogFuncti
   sProvideLogFunctionCb = aProvideLogFunctionCb;
 }
 
 #ifdef MOZ_STACKWALKING
 static uint32_t sStackTraceDepth = 0;
 
 // NS_WalkStackCallback to write a formatted stack frame to an ostringstream.
 static void
-StackFrameToOStringStream(void* aPC, void* aSP, void* aClosure)
+StackFrameToOStringStream(uint32_t aFrameNumber, void* aPC, void* aSP,
+                          void* aClosure)
 {
   std::ostringstream* stream = static_cast<std::ostringstream*>(aClosure);
   nsCodeAddressDetails details;
   char buf[1024];
   NS_DescribeCodeAddress(aPC, &details);
-  NS_FormatCodeAddressDetails(aPC, &details, buf, sizeof(buf));
-  *stream << "--" << buf;
+  NS_FormatCodeAddressDetails(buf, sizeof(buf), aFrameNumber, aPC, &details);
+  *stream << "--" << buf << '\n';
+  stream->flush();
 }
 #endif
 
 // Log to the browser console and, if DEBUG build, stderr.
 static void
 Log(const char* aMessageType,
     const char* aFunctionName,
     const char* aContext,
--- a/toolkit/content/tests/chrome/findbar_events_window.xul
+++ b/toolkit/content/tests/chrome/findbar_events_window.xul
@@ -16,17 +16,17 @@
   <script type="application/javascript"><![CDATA[
     const Ci = Components.interfaces;
     const Cc = Components.classes;
     const Cr = Components.results;
 
     var gFindBar = null;
     var gBrowser;
 
-    var imports = ["SimpleTest", "ok"];
+    var imports = ["SimpleTest", "ok", "is"];
     for each (var name in imports) {
       window[name] = window.opener.wrappedJSObject[name];
     }
 
     function finish() {
       window.close();
       SimpleTest.finish();
     }
@@ -66,31 +66,38 @@
       gFindBar.open();
       gFindBar.onFindCommand();
       nextTest();
     }
 
     function checkSelection(done) {
       SimpleTest.executeSoon(function() {
         var selected = gBrowser.contentWindow.getSelection();
-        ok(selected == "", "No text is selected");
+        is(selected, "", "No text is selected");
 
         var controller = gFindBar.browser.docShell.QueryInterface(Ci.nsIInterfaceRequestor)
                                  .getInterface(Ci.nsISelectionDisplay)
                                  .QueryInterface(Ci.nsISelectionController);
         var selection = controller.getSelection(controller.SELECTION_FIND);
-        ok(selection.rangeCount == 0, "No text is highlighted");
+        is(selection.rangeCount, 0, "No text is highlighted");
         done();
       });
     }
 
+    function once(node, eventName, callback) {
+      node.addEventListener(eventName, function clb(e) {
+        node.removeEventListener(eventName, clb);
+        callback(e);
+      })
+    }
+
     function testFind(done) {
-      var findTriggered = false;
+      var eventTriggered = false;
       var query = "t";
-      gFindBar.addEventListener("find", function(e) {
+      once(gFindBar, "find", function(e) {
         eventTriggered = true;
         ok(e.detail.query === query, "find event query should match '" + query + "'");
         e.preventDefault();
         // Since we're preventing the default make sure nothing was selected.
         checkSelection(done);
       });
 
       // Put some text in the find box.
@@ -98,44 +105,48 @@
       event.initKeyEvent("keypress", true, true, null, false, false,
                          false, false, 0, query.charCodeAt(0));
       gFindBar._findField.inputField.dispatchEvent(event);
       ok(eventTriggered, "find event should be triggered");
     }
 
     function testFindAgain(done) {
       var eventTriggered = false;
-      gFindBar.addEventListener("findagain", function(e) {
+      once(gFindBar, "findagain", function(e) {
         eventTriggered = true;
         e.preventDefault();
         // Since we're preventing the default make sure nothing was selected.
         checkSelection(done);
       });
 
       gFindBar.onFindAgainCommand();
       ok(eventTriggered, "findagain event should be triggered");
     }
 
     function testCaseSensitivity() {
       var eventTriggered = false;
-      gFindBar.addEventListener("findcasesensitivitychange", function(e) {
+      once(gFindBar, "findcasesensitivitychange", function(e) {
         eventTriggered = true;
         ok(e.detail.caseSensitive, "find should be case sensitive");
       });
 
       var matchCaseCheckbox = gFindBar.getElement("find-case-sensitive");
       matchCaseCheckbox.click();
       ok(eventTriggered, "findcasesensitivitychange should be triggered");
+
+      // Changing case sensitivity does the search so clear the selected text
+      // before the next test.
+      gBrowser.contentWindow.getSelection().removeAllRanges();
     }
 
     function testHighlight(done) {
       // Update the find state so the highlight button is clickable.
       gFindBar.updateControlState(Ci.nsITypeAheadFind.FIND_FOUND, false);
       var eventTriggered = false;
-      gFindBar.addEventListener("findhighlightallchange", function(e) {
+      once(gFindBar, "findhighlightallchange", function(e) {
         eventTriggered = true;
         ok(e.detail.highlightAll, "find event should have highlight all set");
         e.preventDefault();
         // Since we're preventing the default make sure nothing was highlighted.
         SimpleTest.executeSoon(function() {
           checkSelection(done);
         });
       });
--- a/toolkit/content/tests/chrome/findbar_window.xul
+++ b/toolkit/content/tests/chrome/findbar_window.xul
@@ -17,16 +17,19 @@
           src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/ChromeUtils.js"/>
 
   <script type="application/javascript"><![CDATA[
     const Ci = Components.interfaces;
     const Cc = Components.classes;
     const Cr = Components.results;
+    const Cu = Components.utils;
+    const { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
+    var { Promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
 
     const SAMPLE_URL = "http://www.mozilla.org/";
     const SAMPLE_TEXT = "Some text in a text field.";
     const SEARCH_TEXT = "Text Test";
 
     var gFindBar = null;
     var gBrowser;
 
@@ -50,16 +53,19 @@
       },
 
       onBeforeLinkTraversal: function() { }
     };
 
     function ok(condition, message) {
       window.opener.wrappedJSObject.SimpleTest.ok(condition, message);
     }
+    function is(a, b, message) {
+      window.opener.wrappedJSObject.SimpleTest.is(a, b, message);
+    }
     function finish() {
       window.close();
       window.opener.wrappedJSObject.SimpleTest.finish();
     }
 
     function onLoad() {
       window.QueryInterface(Ci.nsIInterfaceRequestor)
             .getInterface(Ci.nsIWebNavigation)
@@ -78,46 +84,46 @@
       setTimeout(_delayedOnLoad, 1000);
     }
 
     function _delayedOnPageShow() {
       // setTimeout to the test runs after painting suppression ends
       setTimeout(onPageShow, 0);
     }
 
-    function onPageShow() {
+    let onPageShow = Task.async(function* () {
       testNormalFind();
       gFindBar.close();
       ok(gFindBar.hidden, "Failed to close findbar after testNormalFind");
       testNormalFindWithComposition();
       gFindBar.close();
       ok(gFindBar.hidden, "findbar should be hidden after testNormalFindWithComposition");
       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();
       testDrop();
       testQuickFindLink();
-      if (gHasFindClipboard)
-        testStatusText(afterStatusText);
-      else
-        afterStatusText();
-
-      function afterStatusText() {
-        testFindCountUI(function() {
-          gFindBar.close();
-          ok(gFindBar.hidden, "Failed to close findbar after testFindCountUI");
-          testQuickFindClose();
-        });
+      if (gHasFindClipboard) {
+        yield testStatusText();
       }
-    }
+      yield testFindCountUI();
+      gFindBar.close();
+      ok(gFindBar.hidden, "Failed to close findbar after testFindCountUI");
+      yield testFindAfterCaseChanged();
+      gFindBar.close();
+      yield testFailedStringReset();
+      gFindBar.close();
+      yield testQuickFindClose();
+      finish();
+    });
 
     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) {
@@ -153,36 +159,40 @@
     }
 
     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");
       synthesizeDrop(img, gFindBar._findField, [[ {type: "text/plain", data: "Rabbits" } ]], "copy", window);
-      window.opener.wrappedJSObject.SimpleTest.is(gFindBar._findField.inputField.value, "Rabbits", "drop on findbar");
+      is(gFindBar._findField.inputField.value, "Rabbits", "drop on findbar");
       gFindBar.close();
     }
 
     function testQuickFindClose() {
+      let deferred = Promise.defer();
       var _isClosedCallback = function() {
         ok(gFindBar.hidden,
            "_isClosedCallback: Failed to auto-close quick find bar after " +
            gFindBar._quickFindTimeoutLength + "ms");
-        finish();
+        deferred.resolve();
       };
       setTimeout(_isClosedCallback, gFindBar._quickFindTimeoutLength + 100);
+      return deferred.promise;
     }
 
-    function testStatusText(aCallback) {
+    function testStatusText() {
+      let deferred = Promise.defer();
       var _delayedCheckStatusText = function() {
         ok(gStatusText == SAMPLE_URL, "testStatusText: Failed to set status text of found link");
-        aCallback();
+        deferred.resolve();
       };
       setTimeout(_delayedCheckStatusText, 100);
+      return deferred.promise;
     }
 
     function enterStringIntoFindField(aString) {
       for (var i=0; i < aString.length; i++) {
         var event = document.createEvent("KeyEvents");
         event.initKeyEvent("keypress", true, true, null, false, false,
                            false, false, 0, aString.charCodeAt(i));
         gFindBar._findField.inputField.dispatchEvent(event);
@@ -387,16 +397,18 @@
 
       enterStringIntoFindField(SEARCH_TEXT);
       ok(gBrowser.contentWindow.getSelection() == SEARCH_TEXT,
          "testQuickFindText: failed to find '" + SEARCH_TEXT + "'");
       testClipboardSearchString(SEARCH_TEXT);
     }
 
     function testFindCountUI(callback) {
+      let deferred = Promise.defer();
+
       clearFocus();
       document.getElementById("cmd_find").doCommand();
 
       ok(!gFindBar.hidden, "testFindCountUI: failed to open findbar");
       ok(document.commandDispatcher.focusedElement == gFindBar._findField.inputField,
          "testFindCountUI: find field is not focused");
 
       let matchCase = gFindBar.getElement("find-case-sensitive");
@@ -420,19 +432,19 @@
         text: "texxx",
         current: 0,
         total: 0
       }];
       let regex = /([\d]*)\sof\s([\d]*)/;
       let timeout = gFindBar._matchesCountTimeoutLength + 20;
 
       function assertMatches(aTest, aMatches) {
-        window.opener.wrappedJSObject.SimpleTest.is(aMatches[1], aTest.current,
+        is(aMatches[1], aTest.current,
           "Currently highlighted match should be at " + aTest.current);
-        window.opener.wrappedJSObject.SimpleTest.is(aMatches[2], aTest.total,
+        is(aMatches[2], aTest.total,
           "Total amount of matches should be " + aTest.total);
       }
 
       function* generatorTest() {
         for (let test of tests) {
           gFindBar.clear();
           yield;
           enterStringIntoFindField(test.text);
@@ -449,26 +461,74 @@
               let current = (test.current + i - 1) % test.total + 1;
               assertMatches({
                 current: current,
                 total: test.total
               }, foundMatches.value.match(regex));
             }
           }
         }
-        callback();
+        deferred.resolve();
       }
       let test = generatorTest();
       let resultListener = {
         onMatchesCountResult: function() {
           test.next();
         }
       };
       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);
+      prefsvc.setIntPref("accessibility.typeaheadfind.casesensitive", 0);
+
+      enterStringIntoFindField("t");
+      is(gBrowser.contentWindow.getSelection(), "T", "First T should be selected.");
+
+      prefsvc.setIntPref("accessibility.typeaheadfind.casesensitive", 1);
+      setTimeout(function() {
+        is(gBrowser.contentWindow.getSelection(), "t", "First t should be selected.");
+        deferred.resolve();
+      }, 0);
+      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.");
+
+      prefsvc.setIntPref("accessibility.typeaheadfind.casesensitive", 0);
+      setTimeout(function() {
+        is(gBrowser.contentWindow.getSelection(), SEARCH_TEXT, "Search text should be selected.");
+        deferred.resolve();
+      }, 0);
+      return deferred.resolve();
     }
 
     function testClipboardSearchString(aExpected) {
       if (!gHasFindClipboard)
         return;
 
       if (!aExpected)
         aExpected = "";
--- a/toolkit/content/widgets/findbar.xml
+++ b/toolkit/content/widgets/findbar.xml
@@ -533,16 +533,18 @@
         -   2 - auto = case sensitive iff match string contains upper case letters
         -   @see _shouldBeCaseSensitive
         -->
       <method name="_setCaseSensitivity">
         <parameter name="aCaseSensitivity"/>
         <body><![CDATA[
           this._typeAheadCaseSensitive = aCaseSensitivity;
           this._updateCaseSensitivity();
+          this._findFailedString = null;
+          this._find();
           if (this.getElement("highlight").checked)
             this._setHighlightTimeout();
 
           this._dispatchFindEvent("casesensitivitychange");
         ]]></body>
       </method>
 
       <field name="_strBundle">null</field>
--- a/toolkit/content/widgets/radio.xml
+++ b/toolkit/content/widgets/radio.xml
@@ -448,20 +448,20 @@
           // Just clear out the parent's cached list of radio children
           var control = this.control;
           if (control)
             control._radioChildren = null;
         ]]>
       </constructor>
       <destructor>
         <![CDATA[
-          if (!this.radioGroup)
+          if (!this.control)
             return;
 
-          var radioList = this.radioGroup.mRadioChildren;
+          var radioList = this.control._radioChildren;
           if (!radioList)
             return;
           for (var i = 0; i < radioList.length; ++i) {
             if (radioList[i] == this) {
               radioList.splice(i, 1);
               return;
             }
           }
--- a/toolkit/content/widgets/videocontrols.xml
+++ b/toolkit/content/widgets/videocontrols.xml
@@ -1508,16 +1508,20 @@
                     addListener(this.controlsSpacer, "click", this.clickToPlayClickHandler);
                     addListener(this.controlsSpacer, "dblclick", this.toggleFullscreen);
 
                     addListener(this.videocontrols, "resizevideocontrols", this.adjustControlSize);
                     addListener(this.videocontrols, "transitionend", this.onTransitionEnd);
                     addListener(this.video.ownerDocument, "mozfullscreenchange", this.onFullscreenChange);
                     addListener(this.video, "keypress", this.keyHandler);
 
+                    addListener(this.videocontrols, "dragstart", function(event) {
+                        event.preventDefault(); //prevent dragging of controls image (bug 517114)
+                    });
+
                     this.log("--- videocontrols initialized ---");
                 }
             };
             this.Utils.init(this);
             ]]>
         </constructor>
         <destructor>
             <![CDATA[
--- a/toolkit/xre/nsSigHandlers.cpp
+++ b/toolkit/xre/nsSigHandlers.cpp
@@ -51,24 +51,26 @@ static unsigned int _gdb_sleep_duration 
 #include "nsStackWalk.h"
 
 // NB: keep me up to date with the same variable in
 // ipc/chromium/chrome/common/ipc_channel_posix.cc
 static const int kClientChannelFd = 3;
 
 extern "C" {
 
-static void PrintStackFrame(void *aPC, void *aSP, void *aClosure)
+static void PrintStackFrame(uint32_t aFrameNumber, void *aPC, void *aSP,
+                            void *aClosure)
 {
   char buf[1024];
   nsCodeAddressDetails details;
 
   NS_DescribeCodeAddress(aPC, &details);
-  NS_FormatCodeAddressDetails(aPC, &details, buf, sizeof(buf));
-  fputs(buf, stdout);
+  NS_FormatCodeAddressDetails(buf, sizeof(buf), aFrameNumber, aPC, &details);
+  fprintf(stdout, "%s\n", buf);
+  fflush(stdout);
 }
 
 }
 
 void
 ah_crap_handler(int signum)
 {
   printf("\nProgram %s (pid = %d) received signal %d.\n",
--- a/tools/profiler/ProfilerMarkers.cpp
+++ b/tools/profiler/ProfilerMarkers.cpp
@@ -164,8 +164,23 @@ TouchDataPayload::TouchDataPayload(const
 void
 TouchDataPayload::streamPayloadImpl(JSStreamWriter& b)
 {
   b.BeginObject();
   b.NameValue("x", mPoint.x);
   b.NameValue("y", mPoint.y);
   b.EndObject();
 }
+
+VsyncPayload::VsyncPayload(mozilla::TimeStamp aVsyncTimestamp)
+  : ProfilerMarkerPayload(aVsyncTimestamp, aVsyncTimestamp, nullptr)
+  , mVsyncTimestamp(aVsyncTimestamp)
+{
+}
+
+void
+VsyncPayload::streamPayloadImpl(JSStreamWriter& b)
+{
+  b.BeginObject();
+  b.NameValue("vsync", profiler_time(mVsyncTimestamp));
+  b.NameValue("category", "VsyncTimestamp");
+  b.EndObject();
+}
--- a/tools/profiler/ProfilerMarkers.h
+++ b/tools/profiler/ProfilerMarkers.h
@@ -163,9 +163,27 @@ protected:
   virtual void
   streamPayload(JSStreamWriter& b) { return streamPayloadImpl(b); }
 
 private:
   void streamPayloadImpl(JSStreamWriter& b);
   mozilla::ScreenIntPoint mPoint;
 };
 
+/**
+ * Tracks when a vsync occurs according to the HardwareComposer.
+ */
+class VsyncPayload : public ProfilerMarkerPayload
+{
+public:
+  explicit VsyncPayload(mozilla::TimeStamp aVsyncTimestamp);
+  virtual ~VsyncPayload() {}
+
+protected:
+  virtual void
+  streamPayload(JSStreamWriter& b) { return streamPayloadImpl(b); }
+
+private:
+  void streamPayloadImpl(JSStreamWriter& b);
+  mozilla::TimeStamp mVsyncTimestamp;
+};
+
 #endif // PROFILER_MARKERS_H
--- a/tools/profiler/TableTicker.cpp
+++ b/tools/profiler/TableTicker.cpp
@@ -538,17 +538,18 @@ void mergeStacksIntoProfile(ThreadProfil
     MOZ_ASSERT(nativeIndex >= 0);
     aProfile.addTag(ProfileEntry('l', (void*)aNativeStack.pc_array[nativeIndex]));
     nativeIndex--;
   }
 }
 
 #ifdef USE_NS_STACKWALK
 static
-void StackWalkCallback(void* aPC, void* aSP, void* aClosure)
+void StackWalkCallback(uint32_t aFrameNumber, void* aPC, void* aSP,
+                       void* aClosure)
 {
   NativeStack* nativeStack = static_cast<NativeStack*>(aClosure);
   MOZ_ASSERT(nativeStack->count < nativeStack->size);
   nativeStack->sp_array[nativeStack->count] = aSP;
   nativeStack->pc_array[nativeStack->count] = aPC;
   nativeStack->count++;
 }
 
@@ -562,18 +563,21 @@ void TableTicker::doNativeBacktrace(Thre
   void* sp_array[1000];
   NativeStack nativeStack = {
     pc_array,
     sp_array,
     mozilla::ArrayLength(pc_array),
     0
   };
 
-  // Start with the current function.
-  StackWalkCallback(aSample->pc, aSample->sp, &nativeStack);
+  // Start with the current function. We use 0 as the frame number here because
+  // the FramePointerStackWalk() and NS_StackWalk() calls below will use 1..N.
+  // This is a bit weird but it doesn't matter because StackWalkCallback()
+  // doesn't use the frame number argument.
+  StackWalkCallback(/* frameNumber */ 0, aSample->pc, aSample->sp, &nativeStack);
 
   uint32_t maxFrames = uint32_t(nativeStack.size - nativeStack.count);
 #ifdef XP_MACOSX
   pthread_t pt = GetProfiledThread(aSample->threadProfile->GetPlatformData());
   void *stackEnd = reinterpret_cast<void*>(-1);
   if (pt)
     stackEnd = static_cast<char*>(pthread_get_stackaddr_np(pt));
   nsresult rv = NS_OK;
--- a/tools/rb/fix_linux_stack.py
+++ b/tools/rb/fix_linux_stack.py
@@ -1,26 +1,17 @@
 #!/usr/bin/python
 # vim:sw=4:ts=4:et:
 # 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 script uses addr2line (part of binutils) to process the output of
-# nsTraceRefcnt's Linux stack walking code.  This is useful for two
-# things:
-#  (1) Getting line number information out of
-#      |nsTraceRefcnt::WalkTheStack|'s output in debug builds.
-#  (2) Getting function names out of |nsTraceRefcnt::WalkTheStack|'s
-#      output on optimized builds (where it mostly prints UNKNOWN
-#      because only a handful of symbols are exported from component
-#      libraries).
-#
-# Use the script by piping output containing stacks (such as raw stacks
-# or make-tree.pl balance trees) through this script.
+# This script uses addr2line (part of binutils) to post-process the entries
+# produced by NS_FormatCodeAddress(), which on Linux often lack a function
+# name, a file name and a line number.
 
 import subprocess
 import sys
 import re
 import os
 import pty
 import termios
 from StringIO import StringIO
@@ -291,35 +282,30 @@ def addressToSymbol(file, address):
     else:
         (converter, address_adjustment, cache) = addr2lines[file]
     if address in cache:
         return cache[address]
     result = converter.convert(hex(int(address, 16) + address_adjustment))
     cache[address] = result
     return result
 
-line_re = re.compile("^(.*) ?\[([^ ]*) \+(0x[0-9A-F]{1,8})\](.*)$")
-balance_tree_re = re.compile("^([ \|0-9-]*)(.*)$")
+# Matches lines produced by NS_FormatCodeAddress().
+line_re = re.compile("^(.*#\d+: )(.+)\[(.+) \+(0x[0-9A-Fa-f]+)\](.*)$")
 
 def fixSymbols(line):
     result = line_re.match(line)
     if result is not None:
-        # before allows preservation of balance trees
-        # after allows preservation of counts
-        (before, file, address, after) = result.groups()
+        (before, fn, file, address, after) = result.groups()
 
         if os.path.exists(file) and os.path.isfile(file):
-            # throw away the bad symbol, but keep balance tree structure
-            (before, badsymbol) = balance_tree_re.match(before).groups()
-
             (name, fileline) = addressToSymbol(file, address)
 
             # If addr2line gave us something useless, keep what we had before.
             if name == "??":
-                name = badsymbol
+                name = fn
             if fileline == "??:0" or fileline == "??:?":
                 fileline = file
 
             nl = '\n' if line[-1] == '\n' else ''
             return "%s%s (%s)%s%s" % (before, name, fileline, after, nl)
         else:
             sys.stderr.write("Warning: File \"" + file + "\" does not exist.\n")
             return line
--- a/tools/rb/fix_macosx_stack.py
+++ b/tools/rb/fix_macosx_stack.py
@@ -1,24 +1,17 @@
 #!/usr/bin/python
 # vim:sw=4:ts=4:et:
 # 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 script uses atos to process the output of nsTraceRefcnt's Mac OS
-# X stack walking code.  This is useful for two things:
-#  (1) Getting line number information out of
-#      |nsTraceRefcnt::WalkTheStack|'s output in debug builds.
-#  (2) Getting function names out of |nsTraceRefcnt::WalkTheStack|'s
-#      output on all builds (where it mostly prints UNKNOWN because only
-#      a handful of symbols are exported from component libraries).
-#
-# Use the script by piping output containing stacks (such as raw stacks
-# or make-tree.pl balance trees) through this script.
+# This script uses |atos| to post-process the entries produced by
+# NS_FormatCodeAddress(), which on Mac often lack a file name and a line
+# number.
 
 import subprocess
 import sys
 import re
 import os
 import pty
 import termios
 
@@ -94,26 +87,24 @@ def cxxfilt(sym):
         globals()["cxxfilt_proc"] = subprocess.Popen(['c++filt',
                                                       '--no-strip-underscores',
                                                       '--format', 'gnu-v3'],
                                                      stdin=subprocess.PIPE,
                                                      stdout=subprocess.PIPE)
     cxxfilt_proc.stdin.write(sym + "\n")
     return cxxfilt_proc.stdout.readline().rstrip("\n")
 
-line_re = re.compile("^(.*) ?\[([^ ]*) \+(0x[0-9a-fA-F]{1,8})\](.*)$")
-balance_tree_re = re.compile("^([ \|0-9-]*)")
+# Matches lines produced by NS_FormatCodeAddress().
+line_re = re.compile("^(.*#\d+: )(.+)\[(.+) \+(0x[0-9A-Fa-f]+)\](.*)$")
 atos_name_re = re.compile("^(.+) \(in ([^)]+)\) \((.+)\)$")
 
 def fixSymbols(line):
     result = line_re.match(line)
     if result is not None:
-        # before allows preservation of balance trees
-        # after allows preservation of counts
-        (before, file, address, after) = result.groups()
+        (before, fn, file, address, after) = result.groups()
         address = int(address, 16)
 
         if os.path.exists(file) and os.path.isfile(file):
             address += address_adjustment(file)
             info = addressToSymbol(file, address)
 
             # atos output seems to have three forms:
             #   address
@@ -124,19 +115,16 @@ def fixSymbols(line):
                 # Print the first two forms as-is, and transform the third
                 (name, library, fileline) = name_result.groups()
                 # atos demangles, but occasionally it fails.  cxxfilt can mop
                 # up the remaining cases(!), which will begin with '_Z'.
                 if (name.startswith("_Z")):
                     name = cxxfilt(name)
                 info = "%s (%s, in %s)" % (name, fileline, library)
 
-            # throw away the bad symbol, but keep balance tree structure
-            before = balance_tree_re.match(before).groups()[0]
-
             nl = '\n' if line[-1] == '\n' else ''
             return before + info + after + nl
         else:
             sys.stderr.write("Warning: File \"" + file + "\" does not exist.\n")
             return line
     else:
         return line
 
--- a/tools/rb/fix_stack_using_bpsyms.py
+++ b/tools/rb/fix_stack_using_bpsyms.py
@@ -1,14 +1,18 @@
 #!/usr/bin/env python
 
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+# This script uses breakpad symbols to post-process the entries produced by
+# NS_FormatCodeAddress(), which on TBPL builds often lack a file name and a
+# line number (and on Linux even the symbol is often bad).
+
 from __future__ import with_statement
 
 import sys
 import os
 import re
 import bisect
 
 def prettyFileName(name):
@@ -107,28 +111,24 @@ def getSymbolFile(file, symbolsDir):
 
 def addressToSymbol(file, address, symbolsDir):
   p = getSymbolFile(file, symbolsDir)
   if p:
     return p.addrToSymbol(address)
   else:
     return ""
 
-line_re = re.compile("^(.*) ?\[([^ ]*) \+(0x[0-9A-F]{1,16})\](.*)$")
-balance_tree_re = re.compile("^([ \|0-9-]*)")
+# Matches lines produced by NS_FormatCodeAddress().
+line_re = re.compile("^(.*#\d+: )(.+)\[(.+) \+(0x[0-9A-Fa-f]+)\](.*)$")
 
 def fixSymbols(line, symbolsDir):
   result = line_re.match(line)
   if result is not None:
-    # before allows preservation of balance trees
-    # after allows preservation of counts
-    (before, file, address, after) = result.groups()
+    (before, fn, file, address, after) = result.groups()
     address = int(address, 16)
-    # throw away the bad symbol, but keep balance tree structure
-    before = balance_tree_re.match(before).groups()[0]
     symbol = addressToSymbol(file, address, symbolsDir)
     if not symbol:
       symbol = "%s + 0x%x" % (os.path.basename(file), address)
     return before + symbol + after + "\n"
   else:
     return line
 
 if __name__ == "__main__":
--- a/tools/rb/make-tree.pl
+++ b/tools/rb/make-tree.pl
@@ -92,21 +92,26 @@ sub read_data($$$) {
           my $sno   = shift(@fields);
           next LINE unless ($obj eq $::opt_object);
      
           my $op  = shift(@fields);
           next LINE unless ($op eq $plus || $op eq $minus);
      
           my $cnt = shift(@fields);
      
-          # Collect the remaining lines to create a stack trace.
+          # Collect the remaining lines to create a stack trace. We need to
+          # filter out the frame numbers so that frames that differ only in
+          # their frame number are considered equivalent. However, we need to
+          # keep a frame number on each line so that the fix*.py scripts can
+          # parse the output. So we set the frame number to 0 for every frame.
           my @stack;
           CALLSITE: while (<$INFILE>) {
               chomp;
               last CALLSITE if (/^$/);
+              $_ =~ s/#\d+: /#00: /;    # replace frame number with 0
               $stack[++$#stack] = $_;
           }
      
           # Reverse the remaining fields to produce the call stack, with the
           # oldest frame at the front of the array.
           if (! $::opt_reverse) {
               @stack = reverse(@stack);
           }
--- a/tools/trace-malloc/lib/nsTraceMalloc.c
+++ b/tools/trace-malloc/lib/nsTraceMalloc.c
@@ -888,17 +888,17 @@ calltree(void **stack, size_t num_stack_
     return NULL;
 }
 
 /*
  * Buffer the stack from top at low index to bottom at high, so that we can
  * reverse it in calltree.
  */
 static void
-stack_callback(void *pc, void *sp, void *closure)
+stack_callback(uint32_t frameNumber, void *pc, void *sp, void *closure)
 {
     stack_buffer_info *info = (stack_buffer_info*) closure;
 
     /*
      * If we run out of buffer, keep incrementing entries so that
      * backtrace can call us again with a bigger buffer.
      */
     if (info->entries < info->size)
--- a/widget/gonk/HwcComposer2D.cpp
+++ b/widget/gonk/HwcComposer2D.cpp
@@ -26,16 +26,21 @@
 #include "mozilla/layers/PLayerTransaction.h"
 #include "mozilla/layers/ShadowLayerUtilsGralloc.h"
 #include "mozilla/layers/TextureHostOGL.h"  // for TextureHostOGL
 #include "mozilla/StaticPtr.h"
 #include "cutils/properties.h"
 #include "gfx2DGlue.h"
 #include "GeckoTouchDispatcher.h"
 
+#ifdef MOZ_ENABLE_PROFILER_SPS
+#include "GeckoProfiler.h"
+#include "ProfilerMarkers.h"
+#endif
+
 #if ANDROID_VERSION >= 17
 #include "libdisplay/FramebufferSurface.h"
 #include "gfxPrefs.h"
 #include "nsThreadUtils.h"
 
 #ifndef HWC_BLIT
 #define HWC_BLIT (HWC_FRAMEBUFFER_TARGET + 1)
 #endif
@@ -65,16 +70,18 @@
 
 using namespace android;
 using namespace mozilla::gfx;
 using namespace mozilla::layers;
 
 namespace mozilla {
 
 #if ANDROID_VERSION >= 17
+nsecs_t sAndroidInitTime = 0;
+mozilla::TimeStamp sMozInitTime;
 static void
 HookInvalidate(const struct hwc_procs* aProcs)
 {
     HwcComposer2D::GetInstance()->Invalidate();
 }
 
 static void
 HookVsync(const struct hwc_procs* aProcs, int aDisplay,
@@ -149,16 +156,18 @@ HwcComposer2D::Init(hwc_display_t dpy, h
             mRBSwapSupport = !!supported;
         }
     } else {
         mColorFill = false;
         mRBSwapSupport = false;
     }
 
     if (RegisterHwcEventCallback()) {
+        sAndroidInitTime = systemTime(SYSTEM_TIME_MONOTONIC);
+        sMozInitTime = TimeStamp::Now();
         EnableVsync(true);
     }
 #else
     char propValue[PROPERTY_VALUE_MAX];
     property_get("ro.display.colorfill", propValue, "0");
     mColorFill = (atoi(propValue) == 1) ? true : false;
     mRBSwapSupport = true;
 #endif
@@ -224,19 +233,27 @@ HwcComposer2D::RunVsyncEventControl(bool
         HwcDevice* device = (HwcDevice*)GetGonkDisplay()->GetHWCDevice();
         if (device && device->eventControl) {
             device->eventControl(device, HWC_DISPLAY_PRIMARY, HWC_EVENT_VSYNC, aEnable);
         }
     }
 }
 
 void
-HwcComposer2D::Vsync(int aDisplay, int64_t aTimestamp)
+HwcComposer2D::Vsync(int aDisplay, nsecs_t aVsyncTimestamp)
 {
-    GeckoTouchDispatcher::NotifyVsync(aTimestamp);
+#ifdef MOZ_ENABLE_PROFILER_SPS
+    if (profiler_is_active()) {
+      nsecs_t timeSinceInit = aVsyncTimestamp - sAndroidInitTime;
+      TimeStamp vsyncTime = sMozInitTime + TimeDuration::FromMicroseconds(timeSinceInit / 1000);
+      CompositorParent::PostInsertVsyncProfilerMarker(vsyncTime);
+    }
+#endif
+
+    GeckoTouchDispatcher::NotifyVsync(aVsyncTimestamp);
 }
 
 // Called on the "invalidator" thread (run from HAL).
 void
 HwcComposer2D::Invalidate()
 {
     if (!Initialized()) {
         LOGE("HwcComposer2D::Invalidate failed!");
--- a/xpcom/base/CodeAddressService.h
+++ b/xpcom/base/CodeAddressService.h
@@ -84,22 +84,19 @@ class CodeAddressService
     void Replace(const void* aPc, const char* aFunction,
                  const char* aLibrary, ptrdiff_t aLOffset,
                  const char* aFileName, unsigned long aLineNo)
     {
       mPc = aPc;
 
       // Convert "" to nullptr.  Otherwise, make a copy of the name.
       StringAlloc::free(mFunction);
-      mFunction =
-        !aFunction[0] ? nullptr : StringAlloc::copy(aFunction);
+      mFunction = !aFunction[0] ? nullptr : StringAlloc::copy(aFunction);
       StringAlloc::free(mFileName);
-      mFileName =
-        !aFileName[0] ? nullptr : StringAlloc::copy(aFileName);
-
+      mFileName = !aFileName[0] ? nullptr : StringAlloc::copy(aFileName);
 
       mLibrary = aLibrary;
       mLOffset = aLOffset;
       mLineNo = aLineNo;
 
       mInUse = 1;
     }
 
@@ -127,17 +124,18 @@ class CodeAddressService
   size_t mNumCacheMisses;
 
 public:
   CodeAddressService()
     : mEntries(), mNumCacheHits(0), mNumCacheMisses(0)
   {
   }
 
-  void GetLocation(const void* aPc, char* aBuf, size_t aBufLen)
+  void GetLocation(uint32_t aFrameNumber, const void* aPc, char* aBuf,
+                   size_t aBufLen)
   {
     MOZ_ASSERT(DescribeCodeAddressLock::IsLocked());
 
     uint32_t index = HashGeneric(aPc) & kMask;
     MOZ_ASSERT(index < kNumEntries);
     Entry& entry = mEntries[index];
 
     if (!entry.mInUse || entry.mPc != aPc) {
@@ -160,36 +158,19 @@ public:
                     details.filename, details.lineno);
 
     } else {
       mNumCacheHits++;
     }
 
     MOZ_ASSERT(entry.mPc == aPc);
 
-    uintptr_t entryPc = (uintptr_t)(entry.mPc);
-    // Sometimes we get nothing useful.  Just print "???" for the entire entry
-    // so that fix_linux_stack.py doesn't complain about an empty filename.
-    if (!entry.mFunction && !entry.mLibrary[0] && entry.mLOffset == 0) {
-      snprintf(aBuf, aBufLen, "??? 0x%" PRIxPTR, entryPc);
-    } else {
-      // Use "???" for unknown functions.
-      const char* entryFunction = entry.mFunction ? entry.mFunction : "???";
-      if (entry.mFileName) {
-        // On Windows we can get the filename and line number at runtime.
-        snprintf(aBuf, aBufLen, "%s (%s:%u) 0x%" PRIxPTR,
-                 entryFunction, entry.mFileName, entry.mLineNo, entryPc);
-      } else {
-        // On Linux and Mac we cannot get the filename and line number at
-        // runtime, so we print the offset in a form that fix_linux_stack.py and
-        // fix_macosx_stack.py can post-process.
-        snprintf(aBuf, aBufLen, "%s[%s +0x%" PRIXPTR "] 0x%" PRIxPTR,
-                 entryFunction, entry.mLibrary, entry.mLOffset, entryPc);
-      }
-    }
+    NS_FormatCodeAddress(aBuf, aBufLen, aFrameNumber, entry.mPc,
+                         entry.mFunction, entry.mLibrary, entry.mLOffset,
+                         entry.mFileName, entry.mLineNo);
   }
 
   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
   {
     size_t n = aMallocSizeOf(this);
     for (uint32_t i = 0; i < kNumEntries; i++) {
       n += mEntries[i].SizeOfExcludingThis(aMallocSizeOf);
     }
--- a/xpcom/base/nsIMemoryInfoDumper.idl
+++ b/xpcom/base/nsIMemoryInfoDumper.idl
@@ -54,89 +54,39 @@ interface nsIMemoryInfoDumper : nsISuppo
    * @param aFilename The output file.
    *
    * @param aFinishDumping The callback called on completion.
    *
    * @param aFinishDumpingData The environment for the callback.
    *
    * @param aAnonymize Should the reports be anonymized?
    *
-   * Sample output:
+   * Sample output, annotated with comments for explanatory purposes.
    *
    * {
+   *   // The version number of the format, which will be incremented each time
+   *   // backwards-incompatible changes are made. A mandatory integer.
    *   "version": 1
+   *
+   *   // Equal to nsIMemoryReporterManager::hasMozMallocUsableSize. A
+   *   // mandatory boolean.
    *   "hasMozMallocUsableSize": true,
+   *
+   *   // The memory reports. A mandatory array.
    *   "reports": [
+   *     // The properties correspond to the arguments of
+   *     // nsIHandleReportCallback::callback. Every one is mandatory.
    *     {"process":"Main Process (pid 12345)", "path":"explicit/foo/bar",
    *      "kind":1, "units":0, "amount":2000000, "description":"Foo bar."},
    *     {"process":"Main Process (pid 12345)", "path":"heap-allocated",
    *      "kind":1, "units":0, "amount":3000000, "description":"Heap allocated."},
    *     {"process":"Main Process (pid 12345)", "path":"vsize",
    *      "kind":1, "units":0, "amount":10000000, "description":"Vsize."}
    *   ]
    * }
-   *
-   * JSON schema for the output.
-   *
-   * {
-   *   "properties": {
-   *     "version": {
-   *       "type": "integer",
-   *       "description": "Version number of this schema.",
-   *       "required": true
-   *     },
-   *     "hasMozMallocUsableSize": {
-   *       "type": "boolean",
-   *       "description": "nsIMemoryReporterManager::hasMozMallocUsableSize",
-   *       "required": true
-   *     },
-   *     "reports": {
-   *       "type": "array",
-   *       "description": "The memory reports.",
-   *       "required": true
-   *       "minItems": 1,
-   *       "items": {
-   *         "type": "object",
-   *         "properties": {
-   *           "process": {
-   *             "type": "string",
-   *             "description": "nsIMemoryReporter::process",
-   *             "required": true
-   *           },
-   *           "path": {
-   *             "type": "string",
-   *             "description": "nsIMemoryReporter::path",
-   *             "required": true,
-   *             "minLength": 1
-   *           },
-   *           "kind": {
-   *             "type": "integer",
-   *             "description": "nsIMemoryReporter::kind",
-   *             "required": true
-   *           },
-   *           "units": {
-   *             "type": "integer",
-   *             "description": "nsIMemoryReporter::units",
-   *             "required": true
-   *           },
-   *           "amount": {
-   *             "type": "integer",
-   *             "description": "nsIMemoryReporter::amount",
-   *             "required": true
-   *           },
-   *           "description": {
-   *             "type": "string",
-   *             "description": "nsIMemoryReporter::description",
-   *             "required": true
-   *           }
-   *         }
-   *       }
-   *     }
-   *   }
-   * }
    */
   void dumpMemoryReportsToNamedFile(in AString aFilename,
                                     in nsIFinishDumpingCallback aFinishDumping,
                                     in nsISupports aFinishDumpingData,
                                     in boolean aAnonymize);
 
   /**
    * Similar to dumpMemoryReportsToNamedFile, this method dumps gzipped memory
--- a/xpcom/base/nsMemoryInfoDumper.cpp
+++ b/xpcom/base/nsMemoryInfoDumper.cpp
@@ -425,38 +425,16 @@ MakeFilename(const char* aPrefix, const 
              int aPid, const char* aSuffix, nsACString& aResult)
 {
   aResult = nsPrintfCString("%s-%s-%d.%s",
                             aPrefix,
                             NS_ConvertUTF16toUTF8(aIdentifier).get(),
                             aPid, aSuffix);
 }
 
-#ifdef MOZ_DMD
-struct DMDWriteState
-{
-  static const size_t kBufSize = 4096;
-  char mBuf[kBufSize];
-  nsRefPtr<nsGZFileWriter> mGZWriter;
-
-  DMDWriteState(nsGZFileWriter* aGZWriter)
-    : mGZWriter(aGZWriter)
-  {
-  }
-};
-
-static void
-DMDWrite(void* aState, const char* aFmt, va_list ap)
-{
-  DMDWriteState* state = (DMDWriteState*)aState;
-  vsnprintf(state->mBuf, state->kBufSize, aFmt, ap);
-  unused << state->mGZWriter->Write(state->mBuf);
-}
-#endif
-
 // This class wraps GZFileWriter so it can be used with JSONWriter, overcoming
 // the following two problems:
 // - It provides a JSONWriterFunc::Write() that calls nsGZFileWriter::Write().
 // - It can be stored as a UniquePtr, whereas nsGZFileWriter is refcounted.
 class GZWriterWrapper : public JSONWriteFunc
 {
 public:
   explicit GZWriterWrapper(nsGZFileWriter* aGZWriter)
@@ -786,20 +764,20 @@ nsresult
 nsMemoryInfoDumper::OpenDMDFile(const nsAString& aIdentifier, int aPid,
                                 FILE** aOutFile)
 {
   if (!dmd::IsRunning()) {
     *aOutFile = nullptr;
     return NS_OK;
   }
 
-  // Create a filename like dmd-<identifier>-<pid>.txt.gz, which will be used
+  // Create a filename like dmd-<identifier>-<pid>.json.gz, which will be used
   // if DMD is enabled.
   nsCString dmdFilename;
-  MakeFilename("dmd", aIdentifier, aPid, "txt.gz", dmdFilename);
+  MakeFilename("dmd", aIdentifier, aPid, "json.gz", dmdFilename);
 
   // Open a new DMD file named |dmdFilename| in NS_OS_TEMP_DIR for writing,
   // and dump DMD output to it.  This must occur after the memory reporters
   // have been run (above), but before the memory-reports file has been
   // renamed (so scripts can detect the DMD file, if present).
 
   nsresult rv;
   nsCOMPtr<nsIFile> dmdFile;
@@ -821,25 +799,24 @@ nsMemoryInfoDumper::OpenDMDFile(const ns
   dmd::StatusMsg("opened %s for writing\n", path.get());
 
   return rv;
 }
 
 nsresult
 nsMemoryInfoDumper::DumpDMDToFile(FILE* aFile)
 {
-  nsRefPtr<nsGZFileWriter> dmdWriter = new nsGZFileWriter();
-  nsresult rv = dmdWriter->InitANSIFileDesc(aFile);
+  nsRefPtr<nsGZFileWriter> gzWriter = new nsGZFileWriter();
+  nsresult rv = gzWriter->InitANSIFileDesc(aFile);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   // Dump DMD's memory reports analysis to the file.
-  DMDWriteState state(dmdWriter);
-  dmd::Writer w(DMDWrite, &state);
-  dmd::AnalyzeReports(w);
+  JSONWriter jsonWriter(MakeUnique<GZWriterWrapper>(gzWriter));
+  dmd::AnalyzeReports(jsonWriter);
 
-  rv = dmdWriter->Finish();
+  rv = gzWriter->Finish();
   NS_WARN_IF(NS_FAILED(rv));
   return rv;
 }
 #endif  // MOZ_DMD
 
--- a/xpcom/base/nsStackWalk.cpp
+++ b/xpcom/base/nsStackWalk.cpp
@@ -8,16 +8,20 @@
 
 #include "mozilla/Assertions.h"
 #include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/StackWalk.h"
 #include "nsStackWalkPrivate.h"
 
 #include "nsStackWalk.h"
 
+#ifdef XP_WIN
+#define snprintf _snprintf
+#endif
+
 using namespace mozilla;
 
 // The presence of this address is the stack must stop the stack walk. If
 // there is no such address, the structure will be {nullptr, true}.
 struct CriticalAddress
 {
   void* mAddr;
   bool mInit;
@@ -49,17 +53,17 @@ static CriticalAddress gCriticalAddress;
 
 typedef void
 malloc_logger_t(uint32_t aType,
                 uintptr_t aArg1, uintptr_t aArg2, uintptr_t aArg3,
                 uintptr_t aResult, uint32_t aNumHotFramesToSkip);
 extern malloc_logger_t* malloc_logger;
 
 static void
-stack_callback(void* aPc, void* aSp, void* aClosure)
+stack_callback(uint32_t aFrameNumber, void* aPc, void* aSp, void* aClosure)
 {
   const char* name = static_cast<char*>(aClosure);
   Dl_info info;
 
   // On Leopard dladdr returns the wrong value for "new_sem_from_pool". The
   // stack shows up as having two pthread_cond_wait$UNIX2003 frames. The
   // correct one is the first that we find on our way up, so the
   // following check for gCriticalAddress.mAddr is critical.
@@ -607,17 +611,17 @@ NS_StackWalk(NS_WalkStackCallback aCallb
 
     ::CloseHandle(data.eventStart);
     ::CloseHandle(data.eventEnd);
   }
 
   ::CloseHandle(myThread);
 
   for (uint32_t i = 0; i < data.pc_count; ++i) {
-    (*aCallback)(data.pcs[i], data.sps[i], aClosure);
+    (*aCallback)(i + 1, data.pcs[i], data.sps[i], aClosure);
   }
 
   return data.pc_count == 0 ? NS_ERROR_FAILURE : NS_OK;
 }
 
 
 static BOOL CALLBACK
 callbackEspecial64(
@@ -822,48 +826,16 @@ NS_DescribeCodeAddress(void* aPC, nsCode
                 sizeof(aDetails->function));
     aDetails->foffset = static_cast<ptrdiff_t>(displacement);
   }
 
   LeaveCriticalSection(&gDbgHelpCS); // release our lock
   return NS_OK;
 }
 
-EXPORT_XPCOM_API(nsresult)
-NS_FormatCodeAddressDetails(void* aPC, const nsCodeAddressDetails* aDetails,
-                            char* aBuffer, uint32_t aBufferSize)
-{
-  if (aDetails->function[0]) {
-    _snprintf(aBuffer, aBufferSize, "%s+0x%08lX [%s +0x%016lX]",
-              aDetails->function, aDetails->foffset,
-              aDetails->library, aDetails->loffset);
-  } else if (aDetails->library[0]) {
-    _snprintf(aBuffer, aBufferSize, "UNKNOWN [%s +0x%016lX]",
-              aDetails->library, aDetails->loffset);
-  } else {
-    _snprintf(aBuffer, aBufferSize, "UNKNOWN 0x%016lX", aPC);
-  }
-
-  aBuffer[aBufferSize - 1] = '\0';
-
-  uint32_t len = strlen(aBuffer);
-  if (aDetails->filename[0]) {
-    _snprintf(aBuffer + len, aBufferSize - len, " (%s, line %d)\n",
-              aDetails->filename, aDetails->lineno);
-  } else {
-    aBuffer[len] = '\n';
-    if (++len != aBufferSize) {
-      aBuffer[len] = '\0';
-    }
-  }
-  aBuffer[aBufferSize - 2] = '\n';
-  aBuffer[aBufferSize - 1] = '\0';
-  return NS_OK;
-}
-
 // i386 or PPC Linux stackwalking code
 #elif HAVE_DLADDR && (HAVE__UNWIND_BACKTRACE || NSSTACKWALK_SUPPORTS_LINUX || NSSTACKWALK_SUPPORTS_MACOSX)
 
 #include <stdlib.h>
 #include <string.h>
 #include "nscore.h"
 #include <stdio.h>
 #include "plstr.h"
@@ -942,18 +914,18 @@ FramePointerStackWalk(NS_WalkStackCallba
     if (IsCriticalAddress(pc)) {
       return NS_ERROR_UNEXPECTED;
     }
     if (--skip < 0) {
       // Assume that the SP points to the BP of the function
       // it called. We can't know the exact location of the SP
       // but this should be sufficient for our use the SP
       // to order elements on the stack.
-      (*aCallback)(pc, bp, aClosure);
       numFrames++;
+      (*aCallback)(numFrames, pc, bp, aClosure);
       if (aMaxFrames != 0 && numFrames == aMaxFrames) {
         break;
       }
     }
     bp = next;
   }
   return numFrames == 0 ? NS_ERROR_FAILURE : NS_OK;
 }
@@ -986,17 +958,16 @@ NS_StackWalk(NS_WalkStackCallback aCallb
   void* stackEnd;
 #if HAVE___LIBC_STACK_END
   stackEnd = __libc_stack_end;
 #else
   stackEnd = reinterpret_cast<void*>(-1);
 #endif
   return FramePointerStackWalk(aCallback, aSkipFrames, aMaxFrames,
                                aClosure, bp, stackEnd);
-
 }
 
 #elif defined(HAVE__UNWIND_BACKTRACE)
 
 // libgcc_s.so symbols _Unwind_Backtrace@@GCC_3.3 and _Unwind_GetIP@@GCC_3.0
 #include <unwind.h>
 
 struct unwind_info
@@ -1018,18 +989,18 @@ unwind_callback(struct _Unwind_Context* 
   if (IsCriticalAddress(pc)) {
     info->isCriticalAbort = true;
     // We just want to stop the walk, so any error code will do.  Using
     // _URC_NORMAL_STOP would probably be the most accurate, but it is not
     // defined on Android for ARM.
     return _URC_FOREIGN_EXCEPTION_CAUGHT;
   }
   if (--info->skip < 0) {
-    (*info->callback)(pc, nullptr, info->closure);
     info->numFrames++;
+    (*info->callback)(info->numFrames, pc, nullptr, info->closure);
     if (info->maxFrames != 0 && info->numFrames == info->maxFrames) {
       // Again, any error code that stops the walk will do.
       return _URC_FOREIGN_EXCEPTION_CAUGHT;
     }
   }
   return _URC_NO_REASON;
 }
 
@@ -1097,34 +1068,16 @@ NS_DescribeCodeAddress(void* aPC, nsCode
     // Just use the mangled symbol if demangling failed.
     PL_strncpyz(aDetails->function, symbol, sizeof(aDetails->function));
   }
 
   aDetails->foffset = (char*)aPC - (char*)info.dli_saddr;
   return NS_OK;
 }
 
-EXPORT_XPCOM_API(nsresult)
-NS_FormatCodeAddressDetails(void* aPC, const nsCodeAddressDetails* aDetails,
-                            char* aBuffer, uint32_t aBufferSize)
-{
-  if (!aDetails->library[0]) {
-    snprintf(aBuffer, aBufferSize, "UNKNOWN %p\n", aPC);
-  } else if (!aDetails->function[0]) {
-    snprintf(aBuffer, aBufferSize, "UNKNOWN [%s +0x%08" PRIXPTR "]\n",
-             aDetails->library, aDetails->loffset);
-  } else {
-    snprintf(aBuffer, aBufferSize, "%s+0x%08" PRIXPTR
-             " [%s +0x%08" PRIXPTR "]\n",
-             aDetails->function, aDetails->foffset,
-             aDetails->library, aDetails->loffset);
-  }
-  return NS_OK;
-}
-
 #else // unsupported platform.
 
 EXPORT_XPCOM_API(nsresult)
 NS_StackWalk(NS_WalkStackCallback aCallback, uint32_t aSkipFrames,
              uint32_t aMaxFrames, void* aClosure, uintptr_t aThread,
              void* aPlatformData)
 {
   MOZ_ASSERT(!aThread);
@@ -1148,17 +1101,49 @@ NS_DescribeCodeAddress(void* aPC, nsCode
   aDetails->loffset = 0;
   aDetails->filename[0] = '\0';
   aDetails->lineno = 0;
   aDetails->function[0] = '\0';
   aDetails->foffset = 0;
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
-EXPORT_XPCOM_API(nsresult)
-NS_FormatCodeAddressDetails(void* aPC, const nsCodeAddressDetails* aDetails,
-                            char* aBuffer, uint32_t aBufferSize)
+#endif
+
+EXPORT_XPCOM_API(void)
+NS_FormatCodeAddressDetails(char* aBuffer, uint32_t aBufferSize,
+                            uint32_t aFrameNumber, void* aPC,
+                            const nsCodeAddressDetails* aDetails)
 {
-  aBuffer[0] = '\0';
-  return NS_ERROR_NOT_IMPLEMENTED;
+  NS_FormatCodeAddress(aBuffer, aBufferSize,
+                       aFrameNumber, aPC, aDetails->function,
+                       aDetails->library, aDetails->loffset,
+                       aDetails->filename, aDetails->lineno);
 }
 
-#endif
+EXPORT_XPCOM_API(void)
+NS_FormatCodeAddress(char* aBuffer, uint32_t aBufferSize, uint32_t aFrameNumber,
+                     const void* aPC, const char* aFunction,
+                     const char* aLibrary, ptrdiff_t aLOffset,
+                     const char* aFileName, uint32_t aLineNo)
+{
+  const char* function = aFunction && aFunction[0] ? aFunction : "???";
+  if (aFileName && aFileName[0]) {
+    // We have a filename and (presumably) a line number. Use them.
+    snprintf(aBuffer, aBufferSize,
+             "#%02u: %s (%s:%u)",
+             aFrameNumber, function, aFileName, aLineNo);
+  } else if (aLibrary && aLibrary[0]) {
+    // We have no filename, but we do have a library name. Use it and the
+    // library offset, and print them in a way that scripts like
+    // fix_{linux,macosx}_stacks.py can easily post-process.
+    snprintf(aBuffer, aBufferSize,
+             "#%02u: %s[%s +0x%" PRIxPTR "]",
+             aFrameNumber, function, aLibrary, aLOffset);
+  } else {
+    // We have nothing useful to go on. (The format string is split because
+    // '??)' is a trigraph and causes a warning, sigh.)
+    snprintf(aBuffer, aBufferSize,
+             "#%02u: ??? (???:???" ")",
+             aFrameNumber);
+  }
+}
+
--- a/xpcom/base/nsStackWalk.h
+++ b/xpcom/base/nsStackWalk.h
@@ -13,21 +13,30 @@
 
 #include "nscore.h"
 #include <stdint.h>
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-// aSP will be the best approximation possible of what the stack pointer will be
-// pointing to when the execution returns to executing that at that PC.
-// If no approximation can be made it will be nullptr.
+/**
+ * The callback for NS_StackWalk.
+ *
+ * @param aFrameNumber  The frame number (starts at 1, not 0).
+ * @param aPC           The program counter value.
+ * @param aSP           The best approximation possible of what the stack
+ *                      pointer will be pointing to when the execution returns
+ *                      to executing that at aPC. If no approximation can
+ *                      be made it will be nullptr.
+ * @param aClosure      Extra data passed in via NS_StackWalk().
+ */
 typedef void
-(*NS_WalkStackCallback)(void* aPC, void* aSP, void* aClosure);
+(*NS_WalkStackCallback)(uint32_t aFrameNumber, void* aPC, void* aSP,
+                        void* aClosure);
 
 /**
  * Call aCallback for the C/C++ stack frames on the current thread, from
  * the caller of NS_StackWalk to main (or above).
  *
  * @param aCallback    Callback function, called once per frame.
  * @param aSkipFrames  Number of initial frames to skip.  0 means that
  *                     the first callback will be for the caller of
@@ -104,27 +113,58 @@ NS_DescribeCodeAddress(void* aPC, nsCode
 
 /**
  * Format the information about a code address in a format suitable for
  * stack traces on the current platform.  When available, this string
  * should contain the function name, source file, and line number.  When
  * these are not available, library and offset should be reported, if
  * possible.
  *
- * @param aPC         The code address.
- * @param aDetails    The value filled in by NS_DescribeCodeAddress(aPC).
- * @param aBuffer     A string to be filled in with the description.
- *                    The string will always be null-terminated.
- * @param aBufferSize The size, in bytes, of aBuffer, including
- *                    room for the terminating null.  If the information
- *                    to be printed would be larger than aBuffer, it
- *                    will be truncated so that aBuffer[aBufferSize-1]
- *                    is the terminating null.
+ * Note that this output is parsed by several scripts including the fix*.py and
+ * make-tree.pl scripts in tools/rb/. It should only be change with care, and
+ * in conjunction with those scripts.
+ *
+ * @param aBuffer      A string to be filled in with the description.
+ *                     The string will always be null-terminated.
+ * @param aBufferSize  The size, in bytes, of aBuffer, including
+ *                     room for the terminating null.  If the information
+ *                     to be printed would be larger than aBuffer, it
+ *                     will be truncated so that aBuffer[aBufferSize-1]
+ *                     is the terminating null.
+ * @param aFrameNumber The frame number.
+ * @param aPC          The code address.
+ * @param aFunction    The function name. Possibly null or the empty string.
+ * @param aLibrary     The library name. Possibly null or the empty string.
+ * @param aLOffset     The library offset.
+ * @param aFileName    The filename. Possibly null or the empty string.
+ * @param aLineNo      The line number. Possibly zero.
  */
-XPCOM_API(nsresult)
-NS_FormatCodeAddressDetails(void* aPC, const nsCodeAddressDetails* aDetails,
-                            char* aBuffer, uint32_t aBufferSize);
+XPCOM_API(void)
+NS_FormatCodeAddress(char* aBuffer, uint32_t aBufferSize, uint32_t aFrameNumber,
+                     const void* aPC, const char* aFunction,
+                     const char* aLibrary, ptrdiff_t aLOffset,
+                     const char* aFileName, uint32_t aLineNo);
+
+/**
+ * Format the information about a code address in the same fashion as
+ * NS_FormatCodeAddress.
+ *
+ * @param aBuffer      A string to be filled in with the description.
+ *                     The string will always be null-terminated.
+ * @param aBufferSize  The size, in bytes, of aBuffer, including
+ *                     room for the terminating null.  If the information
+ *                     to be printed would be larger than aBuffer, it
+ *                     will be truncated so that aBuffer[aBufferSize-1]
+ *                     is the terminating null.
+ * @param aFrameNumber The frame number.
+ * @param aPC          The code address.
+ * @param aDetails     The value filled in by NS_DescribeCodeAddress(aPC).
+ */
+XPCOM_API(void)
+NS_FormatCodeAddressDetails(char* aBuffer, uint32_t aBufferSize,
+                            uint32_t aFrameNumber, void* aPC,
+                            const nsCodeAddressDetails* aDetails);
 
 #ifdef __cplusplus
 }
 #endif
 
 #endif /* !defined(nsStackWalk_h_) */
--- a/xpcom/base/nsTraceRefcnt.cpp
+++ b/xpcom/base/nsTraceRefcnt.cpp
@@ -924,35 +924,38 @@ InitTraceLog()
 }
 
 #endif
 
 extern "C" {
 
 #ifdef MOZ_STACKWALKING
 static void
-PrintStackFrame(void* aPC, void* aSP, void* aClosure)
+PrintStackFrame(uint32_t aFrameNumber, void* aPC, void* aSP, void* aClosure)
 {
   FILE* stream = (FILE*)aClosure;
   nsCodeAddressDetails details;
   char buf[1024];
 
   NS_DescribeCodeAddress(aPC, &details);
-  NS_FormatCodeAddressDetails(aPC, &details, buf, sizeof(buf));
-  fputs(buf, stream);
+  NS_FormatCodeAddressDetails(buf, sizeof(buf), aFrameNumber, aPC, &details);
+  fprintf(stream, "%s\n", buf);
+  fflush(stream);
 }
 
 static void
-PrintStackFrameCached(void* aPC, void* aSP, void* aClosure)
+PrintStackFrameCached(uint32_t aFrameNumber, void* aPC, void* aSP,
+                      void* aClosure)
 {
   auto stream = static_cast<FILE*>(aClosure);
   static const size_t buflen = 1024;
   char buf[buflen];
-  gCodeAddressService->GetLocation(aPC, buf, buflen);
+  gCodeAddressService->GetLocation(aFrameNumber, aPC, buf, buflen);
   fprintf(stream, "    %s\n", buf);
+  fflush(stream);
 }
 #endif
 
 }
 
 void
 nsTraceRefcnt::WalkTheStack(FILE* aStream)
 {
--- a/xpcom/build/LateWriteChecks.cpp
+++ b/xpcom/build/LateWriteChecks.cpp
@@ -74,17 +74,17 @@ public:
     mFile = nullptr;
   }
 private:
   FILE* mFile;
   SHA1Sum mSHA1;
 };
 
 static void
-RecordStackWalker(void* aPC, void* aSP, void* aClosure)
+RecordStackWalker(uint32_t aFrameNumber, void* aPC, void* aSP, void* aClosure)
 {
   std::vector<uintptr_t>* stack =
     static_cast<std::vector<uintptr_t>*>(aClosure);
   stack->push_back(reinterpret_cast<uintptr_t>(aPC));
 }
 
 /**************************** Late-Write Observer  ****************************/
 
--- a/xpcom/glue/BlockingResourceBase.cpp
+++ b/xpcom/glue/BlockingResourceBase.cpp
@@ -43,17 +43,18 @@ const char* const BlockingResourceBase::
 #ifdef DEBUG
 
 PRCallOnceType BlockingResourceBase::sCallOnce;
 unsigned BlockingResourceBase::sResourceAcqnChainFrontTPI = (unsigned)-1;
 BlockingResourceBase::DDT* BlockingResourceBase::sDeadlockDetector;
 
 
 void
-BlockingResourceBase::StackWalkCallback(void* aPc, void* aSp, void* aClosure)
+BlockingResourceBase::StackWalkCallback(uint32_t aFrameNumber, void* aPc,
+                                        void* aSp, void* aClosure)
 {
 #ifndef MOZ_CALLSTACK_DISABLED
   AcquisitionState* state = (AcquisitionState*)aClosure;
   state->AppendElement(aPc);
 #endif
 }
 
 void
@@ -62,18 +63,17 @@ BlockingResourceBase::GetStackTrace(Acqu
 #ifndef MOZ_CALLSTACK_DISABLED
   // Skip this function and the calling function.
   const uint32_t kSkipFrames = 2;
 
   aState.Clear();
 
   // NB: Ignore the return value, there's nothing useful we can do if this
   //     this fails.
-  NS_StackWalk(StackWalkCallback, kSkipFrames,
-               24, &aState, 0, nullptr);
+  NS_StackWalk(StackWalkCallback, kSkipFrames, 24, &aState, 0, nullptr);
 #endif
 }
 
 /**
  * PrintCycle
  * Append to |aOut| detailed information about the circular
  * dependency in |aCycle|.  Returns true if it *appears* that this
  * cycle may represent an imminent deadlock, but this is merely a
@@ -180,17 +180,17 @@ BlockingResourceBase::Print(nsACString& 
 #ifdef MOZ_CALLSTACK_DISABLED
   fputs("  [stack trace unavailable]\n", stderr);
 #else
   const AcquisitionState& state = acquired ? mAcquired : mFirstSeen;
 
   WalkTheStackCodeAddressService addressService;
 
   for (uint32_t i = 0; i < state.Length(); i++) {
-    const size_t kMaxLength = 4096;
+    const size_t kMaxLength = 1024;
     char buffer[kMaxLength];
     addressService.GetLocation(state[i], buffer, kMaxLength);
     const char* fmt = "    %s\n";
     aOut += nsPrintfCString(fmt, buffer);
     fprintf(stderr, fmt, buffer);
   }
 
 #endif
--- a/xpcom/glue/BlockingResourceBase.h
+++ b/xpcom/glue/BlockingResourceBase.h
@@ -314,17 +314,18 @@ private:
   /**
    * Shutdown
    * Free static members.
    *
    * *NOT* thread safe.
    */
   static void Shutdown();
 
-  static void StackWalkCallback(void* aPc, void* aSp, void* aClosure);
+  static void StackWalkCallback(uint32_t aFrameNumber, void* aPc,
+                                void* aSp, void* aClosure);
   static void GetStackTrace(AcquisitionState& aState);
 
 #  ifdef MOZILLA_INTERNAL_API
   // so it can call BlockingResourceBase::Shutdown()
   friend void LogTerm();
 #  endif  // ifdef MOZILLA_INTERNAL_API
 
 #else  // non-DEBUG implementation
--- a/xpcom/threads/HangMonitor.cpp
+++ b/xpcom/threads/HangMonitor.cpp
@@ -109,17 +109,17 @@ Crash()
                                      NS_LITERAL_CSTRING("1"));
 #endif
 
   NS_RUNTIMEABORT("HangMonitor triggered");
 }
 
 #ifdef REPORT_CHROME_HANGS
 static void
-ChromeStackWalker(void* aPC, void* aSP, void* aClosure)
+ChromeStackWalker(uint32_t aFrameNumber, void* aPC, void* aSP, void* aClosure)
 {
   MOZ_ASSERT(aClosure);
   std::vector<uintptr_t>* stack =
     static_cast<std::vector<uintptr_t>*>(aClosure);
   if (stack->size() == MAX_CALL_STACK_PCS) {
     return;
   }
   MOZ_ASSERT(stack->size() < MAX_CALL_STACK_PCS);