Merge m-c to fx-team. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Thu, 09 Apr 2015 12:00:29 -0400
changeset 257370 7dc1acf6f01e8b760b442ff281b9fdfb5fd63f92
parent 257369 c061b1170d6a5bae8ac3a8d2a5f5a7710990adbe (current diff)
parent 257257 dd32e3ff37179e1213f7c8e0dd0a6bf0a826e6b2 (diff)
child 257371 f0f90206af121f493eb4bca2d8ee3e03ffe4b46f
push id8007
push userraliiev@mozilla.com
push dateMon, 11 May 2015 19:23:16 +0000
treeherdermozilla-aurora@e2ce1aac996e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone40.0a1
Merge m-c to fx-team. a=merge
browser/app/profile/firefox.js
testing/web-platform/meta/html/rendering/replaced-elements/svg-inline-sizing/svg-inline.html.ini
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -1121,28 +1121,13 @@ pref("dom.mozSettings.SettingsService.ve
 // Controlling whether we want to allow forcing some Settings
 // IndexedDB transactions to be opened as readonly or keep everything as
 // readwrite.
 pref("dom.mozSettings.allowForceReadOnly", false);
 
 // RequestSync API is enabled by default on B2G.
 pref("dom.requestSync.enabled", true);
 
-// Only enable for kit kat and above devices
-// kit kat == 19, L = 21, 20 is kit-kat for wearables
-// 15 is for the ICS emulators which will fallback to software vsync
-#if ANDROID_VERSION == 19 || ANDROID_VERSION == 21 || ANDROID_VERSION == 15
+// Use vsync aligned rendering
 pref("gfx.vsync.hw-vsync.enabled", true);
 pref("gfx.vsync.compositor", true);
 pref("gfx.touch.resample", true);
-#else
-pref("gfx.vsync.hw-vsync.enabled", false);
-pref("gfx.vsync.compositor", false);
-pref("gfx.touch.resample", false);
-#endif
-
-// Bug 1147753 - Weird issues with vsync refresh driver on L devices
-// so disable them on L, but enable on KK and ICS
-#if ANDROID_VERSION == 19 || ANDROID_VERSION == 15
 pref("gfx.vsync.refreshdriver", true);
-#else
-pref("gfx.vsync.refreshdriver", false);
-#endif
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/sources.xml
@@ -10,19 +10,19 @@
   <!--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="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="5dfd0460eb6e616205154b0d219aa5123bf1abb3"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="eaf69e651e19b98c096f8e63b9829fb31df50927"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d36455f7defa176797ef5aaf894fe99307081542"/>
+  <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="805e2a69ad399bc181b308dd4f4fb4eddbd557ca"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="1b1d86462d3150dceacff927536ded9fcc168419"/>
   <!-- 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"/>
@@ -97,17 +97,17 @@
   <project name="platform/external/tinyxml" path="external/tinyxml" revision="494e448824844d866e805831d1d5f5acb654065c"/>
   <project name="platform/external/tinyxml2" path="external/tinyxml2" revision="c74b546f5af36968ffa56d7fd4529f4273b96f48"/>
   <project name="platform/external/tremolo" path="external/tremolo" revision="0499204aa97d3e2978ee77d869dd033acb2196e2"/>
   <project name="platform/external/webp" path="external/webp" revision="513e97bd307573e2adc776eb5368bd129aceaa4a"/>
   <project name="platform/external/webrtc" path="external/webrtc" revision="446452f84e9cc4c75d8e80f6f05e24793397a19d"/>
   <project name="platform/external/yaffs2" path="external/yaffs2" revision="a2cff2275e1b501ff478b03757d6e4f05fddc2db"/>
   <project name="platform/external/zlib" path="external/zlib" revision="a5c7131da47c991585a6c6ac0c063b6d7d56e3fc"/>
   <project name="platform/frameworks/base" path="frameworks/base" revision="2d12cb68a6c680e1ef50c6fbd19f782f67aec9de"/>
-  <project name="platform/frameworks/native" path="frameworks/native" revision="a1a6f7f8ca11eb722f363a05fb60c2e66c8fbf0c"/>
+  <project name="platform/frameworks/native" path="frameworks/native" revision="8d54940d9bdad8fec3208ff58ddab590be9fe0d4"/>
   <project name="platform/frameworks/opt/emoji" path="frameworks/opt/emoji" revision="dbbe673145107e99883f62bafd70c5f43f11065c"/>
   <project name="platform/frameworks/wilhelm" path="frameworks/wilhelm" revision="aac6c4bb59a6577c97cbda68699829b507b7490d"/>
   <project name="platform/hardware/libhardware" path="hardware/libhardware" revision="fbeca55f4695dd07c0291213403533b8fbca4885"/>
   <project name="platform/hardware/libhardware_legacy" path="hardware/libhardware_legacy" revision="92605aa35361ae4ae1e473781e40c1f6929f4ec4"/>
   <project name="platform/libcore" path="libcore" revision="e195beab082c09217318fc19250caeaf4c1bd800"/>
   <project name="platform/libnativehelper" path="libnativehelper" revision="feeb36c2bd4adfe285f98f5de92e0f3771b2c115"/>
   <project name="platform/ndk" path="ndk" revision="e58ef003be4306bb53a8c11331146f39e4eab31f"/>
   <project name="platform_prebuilts_misc" path="prebuilts/misc" remote="b2g" revision="0e7c060db684b409616fe67ea433ef19f5634c60"/>
@@ -124,21 +124,21 @@
   <project name="platform/system/vold" path="system/vold" revision="bb33b1ce8ad9cd3fc4311801b4d56db1d5c8175b"/>
   <!--original fetch url was http://sprdsource.spreadtrum.com:8085/b2g/android-->
   <remote fetch="https://git.mozilla.org/external/sprd-aosp" name="sprd-aosp"/>
   <default remote="sprd-aosp" revision="sprdb2g_gonk4.4" sync-j="4"/>
   <!-- Stock Android things -->
   <project name="platform/external/icu4c" path="external/icu4c" revision="2bb01561780583cc37bc667f0ea79f48a122d8a2"/>
   <!-- dolphin specific things -->
   <project name="device/sprd" path="device/sprd" revision="a26ba0ab998133ad590102be1e5950818b86ce82"/>
-  <project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="2ea7f18a7bc45e16cb97a835dc86ee64d2d6b0b3"/>
-  <project name="platform/frameworks/av" path="frameworks/av" revision="fd359e3a74a658d9eaab1c683440ba5412535777"/>
+  <project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="39a5b5bcadad745df3e6882316fce426f98b1669"/>
+  <project name="platform/frameworks/av" path="frameworks/av" revision="8bb69db127112fc66da75f8ca7a1158614b919f6"/>
   <project name="platform/hardware/akm" path="hardware/akm" revision="6d3be412647b0eab0adff8a2768736cf4eb68039"/>
   <project groups="invensense" name="platform/hardware/invensense" path="hardware/invensense" revision="e6d9ab28b4f4e7684f6c07874ee819c9ea0002a2"/>
   <project name="platform/hardware/ril" path="hardware/ril" revision="865ce3b4a2ba0b3a31421ca671f4d6c5595f8690"/>
   <project name="kernel/common" path="kernel" revision="df636d2c31ad4434a7de2565359ad982b3767118"/>
-  <project name="platform/system/core" path="system/core" revision="a626f6c0ef9e88586569331bd7387b569eaa5ed2"/>
+  <project name="platform/system/core" path="system/core" revision="3747764c63d23de586c56e1cef6dca2cdd90b2ea"/>
   <project name="u-boot" path="u-boot" revision="f1502910977ac88f43da7bf9277c3523ad4b0b2f"/>
   <project name="vendor/sprd/gps" path="vendor/sprd/gps" revision="4c59900937dc2e978b7b14b7f1ea617e3d5d550e"/>
   <project name="vendor/sprd/open-source" path="vendor/sprd/open-source" revision="e503b1d14d7fdee532b8f391407299da193c1b2d"/>
   <project name="vendor/sprd/partner" path="vendor/sprd/partner" revision="8649c7145972251af11b0639997edfecabfc7c2e"/>
   <project name="vendor/sprd/proprietories" path="vendor/sprd/proprietories" revision="d2466593022f7078aaaf69026adf3367c2adb7bb"/>
 </manifest>
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,18 +14,18 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="173b3104bfcbd23fc9dccd4b0035fc49aae3d444">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="5dfd0460eb6e616205154b0d219aa5123bf1abb3"/>
-  <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d36455f7defa176797ef5aaf894fe99307081542"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="eaf69e651e19b98c096f8e63b9829fb31df50927"/>
+  <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="805e2a69ad399bc181b308dd4f4fb4eddbd557ca"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="93f9ba577f68d772093987c2f1c0a4ae293e1802"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="a89cebcccc1e067ebdb71a93194f4ee79d71bd69"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
   <project name="platform_bionic" path="bionic" remote="b2g" revision="e2b3733ba3fa5e3f404e983d2e4142b1f6b1b846"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,18 +12,18 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="4efd19d199ae52656604f794c5a77518400220fd">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="5dfd0460eb6e616205154b0d219aa5123bf1abb3"/>
-  <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d36455f7defa176797ef5aaf894fe99307081542"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="eaf69e651e19b98c096f8e63b9829fb31df50927"/>
+  <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="805e2a69ad399bc181b308dd4f4fb4eddbd557ca"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="1b1d86462d3150dceacff927536ded9fcc168419"/>
   <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"/>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,19 +10,19 @@
   <!--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="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="5dfd0460eb6e616205154b0d219aa5123bf1abb3"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="eaf69e651e19b98c096f8e63b9829fb31df50927"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d36455f7defa176797ef5aaf894fe99307081542"/>
+  <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="805e2a69ad399bc181b308dd4f4fb4eddbd557ca"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="1b1d86462d3150dceacff927536ded9fcc168419"/>
   <!-- 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"/>
--- a/b2g/config/emulator-l/sources.xml
+++ b/b2g/config/emulator-l/sources.xml
@@ -10,19 +10,19 @@
   <!--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="52775e03a2d8532429dff579cb2cd56718e488c3">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="5dfd0460eb6e616205154b0d219aa5123bf1abb3"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="eaf69e651e19b98c096f8e63b9829fb31df50927"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d36455f7defa176797ef5aaf894fe99307081542"/>
+  <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="805e2a69ad399bc181b308dd4f4fb4eddbd557ca"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="1b1d86462d3150dceacff927536ded9fcc168419"/>
   <!-- Stock Android things -->
   <project groups="pdk,linux" name="platform/prebuilts/clang/linux-x86/host/3.5" path="prebuilts/clang/linux-x86/host/3.5" revision="50d1ca4ab8add54523b7bc692860d57e8ee4c0d1"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,18 +14,18 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="173b3104bfcbd23fc9dccd4b0035fc49aae3d444">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="5dfd0460eb6e616205154b0d219aa5123bf1abb3"/>
-  <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d36455f7defa176797ef5aaf894fe99307081542"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="eaf69e651e19b98c096f8e63b9829fb31df50927"/>
+  <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="805e2a69ad399bc181b308dd4f4fb4eddbd557ca"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="93f9ba577f68d772093987c2f1c0a4ae293e1802"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="a89cebcccc1e067ebdb71a93194f4ee79d71bd69"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
   <project name="platform_bionic" path="bionic" remote="b2g" revision="e2b3733ba3fa5e3f404e983d2e4142b1f6b1b846"/>
--- a/b2g/config/flame-kk/sources.xml
+++ b/b2g/config/flame-kk/sources.xml
@@ -10,19 +10,19 @@
   <!--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="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="5dfd0460eb6e616205154b0d219aa5123bf1abb3"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="eaf69e651e19b98c096f8e63b9829fb31df50927"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d36455f7defa176797ef5aaf894fe99307081542"/>
+  <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="805e2a69ad399bc181b308dd4f4fb4eddbd557ca"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="1b1d86462d3150dceacff927536ded9fcc168419"/>
   <!-- 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"/>
--- a/b2g/config/flame/sources.xml
+++ b/b2g/config/flame/sources.xml
@@ -12,18 +12,18 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="4efd19d199ae52656604f794c5a77518400220fd">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="5dfd0460eb6e616205154b0d219aa5123bf1abb3"/>
-  <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d36455f7defa176797ef5aaf894fe99307081542"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="eaf69e651e19b98c096f8e63b9829fb31df50927"/>
+  <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="805e2a69ad399bc181b308dd4f4fb4eddbd557ca"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="1b1d86462d3150dceacff927536ded9fcc168419"/>
   <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"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "5dfd0460eb6e616205154b0d219aa5123bf1abb3", 
+        "git_revision": "eaf69e651e19b98c096f8e63b9829fb31df50927", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "d8cd51229d334a6e9ae46951a7e0555a01d0cb1c", 
+    "revision": "354091e8f9d9c3a76dbc94130a433415079db215", 
     "repo_path": "integration/gaia-central"
 }
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -12,18 +12,18 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="4efd19d199ae52656604f794c5a77518400220fd">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="5dfd0460eb6e616205154b0d219aa5123bf1abb3"/>
-  <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d36455f7defa176797ef5aaf894fe99307081542"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="eaf69e651e19b98c096f8e63b9829fb31df50927"/>
+  <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="805e2a69ad399bc181b308dd4f4fb4eddbd557ca"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="1b1d86462d3150dceacff927536ded9fcc168419"/>
   <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"/>
--- a/b2g/config/nexus-5-l/sources.xml
+++ b/b2g/config/nexus-5-l/sources.xml
@@ -10,19 +10,19 @@
   <!--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="52775e03a2d8532429dff579cb2cd56718e488c3">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="5dfd0460eb6e616205154b0d219aa5123bf1abb3"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="eaf69e651e19b98c096f8e63b9829fb31df50927"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d36455f7defa176797ef5aaf894fe99307081542"/>
+  <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="805e2a69ad399bc181b308dd4f4fb4eddbd557ca"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="1b1d86462d3150dceacff927536ded9fcc168419"/>
   <!-- Stock Android things -->
   <project groups="pdk,linux" name="platform/prebuilts/clang/linux-x86/host/3.5" path="prebuilts/clang/linux-x86/host/3.5" revision="50d1ca4ab8add54523b7bc692860d57e8ee4c0d1"/>
--- a/browser/devtools/performance/views/jit-optimizations.js
+++ b/browser/devtools/performance/views/jit-optimizations.js
@@ -2,16 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const URL_LABEL_TOOLTIP = L10N.getStr("table.url.tooltiptext");
 const OPTIMIZATION_FAILURE = L10N.getStr("jit.optimizationFailure");
 const JIT_SAMPLES = L10N.getStr("jit.samples2");
 const JIT_EMPTY_TEXT = L10N.getStr("jit.empty");
+const PROPNAME_MAX_LENGTH = 4;
 
 /**
  * View for rendering JIT Optimization data. The terminology and types
  * used here can be referenced:
  * @see browser/devtools/shared/profiler/jit.js
  */
 
 let JITOptimizationsView = {
@@ -202,26 +203,36 @@ let JITOptimizationsView = {
     let desc = document.createElement("span");
     let line = document.createElement("span");
     let column = document.createElement("span");
     let urlNode = this._createDebuggerLinkNode(frameData.url, site.data.line);
 
     let attempts = site.getAttempts();
     let lastStrategy = attempts[attempts.length - 1].strategy;
 
+    let propString = "";
+    if (site.data.propertyName) {
+      if (site.data.propertyName.length > PROPNAME_MAX_LENGTH) {
+        propString = ` (.${site.data.propertyName.substr(0, PROPNAME_MAX_LENGTH)}…)`;
+        desc.setAttribute("tooltiptext", site.data.propertyName);
+      } else {
+        propString = ` (.${site.data.propertyName})`;
+      }
+    }
+
     if (!site.hasSuccessfulOutcome()) {
       let icon = document.createElement("span");
       icon.setAttribute("tooltiptext", OPTIMIZATION_FAILURE);
       icon.setAttribute("severity", "warning");
       icon.className = "opt-icon";
       node.appendChild(icon);
     }
 
     let sampleString = PluralForm.get(site.samples, JIT_SAMPLES).replace("#1", site.samples);
-    desc.textContent = `${lastStrategy} – (${sampleString})`;
+    desc.textContent = `${lastStrategy}${propString} – (${sampleString})`;
     line.textContent = site.data.line;
     line.className = "opt-line";
     column.textContent = site.data.column;
     column.className = "opt-line";
     node.appendChild(desc);
     node.appendChild(urlNode);
     node.appendChild(line);
     node.appendChild(column);
--- a/build/win32/mozconfig.vs2010-win64
+++ b/build/win32/mozconfig.vs2010-win64
@@ -4,19 +4,16 @@ if [ -d "/c/PROGRA~2/MICROS~2.0" ]; then
   _VSPATH="/c/PROGRA~2/MICROS~2.0"
 else
   _VSPATH="/c/tools/msvs10"
 fi
 
 ## SDK redist ##
 export WIN32_REDIST_DIR=${_VSPATH}/VC/redist/x86/Microsoft.VC100.CRT
 
-## moz tools location for 64-bit builders ##
-export MOZ_TOOLS=C:/mozilla-build/moztools
-
 ## includes: win8 sdk includes, winrt headers for metro, msvc 10 std library, directx sdk for d3d9 ##
 export INCLUDE=/c/Program\ Files\ \(x86\)/Windows\ Kits/8.0/include/shared:/c/Program\ Files\ \(x86\)/Windows\ Kits/8.0/include/um:/c/Program\ Files\ \(x86\)/Windows\ Kits/8.0/include/winrt:/c/Program\ Files\ \(x86\)/Windows\ Kits/8.0/include/winrt/wrl:/c/Program\ Files\ \(x86\)/Windows\ Kits/8.0/include/winrt/wrl/wrappers:${_VSPATH}/vc/include:${_VSPATH}/vc/atlmfc/include:/c/tools/sdks/dx10/include
 
 ## libs: win8 sdk x86 (32-bit) libs, msvc 10 (32-bit) std library, msvc 10 atl libs, directx sdk (32-bit) for d3d9  ##
 export LIBPATH=/c/Program\ Files\ \(x86\)/Windows\ Kits/8.0/Lib/win8/um/x86:${_VSPATH}/vc/lib:${_VSPATH}/vc/atlmfc/lib:/c/tools/sdks/dx10/lib
 export LIB=/c/Program\ Files\ \(x86\)/Windows\ Kits/8.0/Lib/win8/um/x86:${_VSPATH}/vc/lib:${_VSPATH}/vc/atlmfc/lib:/c/tools/sdks/dx10/lib
 
 ## paths: win8 sdk x86 (32-bit) tools, msvc 10 (32-bit) build toolchain, moz tools  ##
@@ -27,10 +24,8 @@ export WINDOWSSDKDIR="/c/Program Files (
 
 . $topsrcdir/build/mozconfig.vs-common
 
 mk_export_correct_style LIB
 mk_export_correct_style LIBPATH
 mk_export_correct_style PATH
 mk_export_correct_style INCLUDE
 mk_export_correct_style WIN32_REDIST_DIR
-
-mk_add_options "export MOZ_TOOLS=$MOZ_TOOLS"
--- a/build/win32/mozconfig.vs2013-win64
+++ b/build/win32/mozconfig.vs2013-win64
@@ -1,14 +1,11 @@
 _VSPATH="/c/tools/vs2013"
 export WIN32_REDIST_DIR=${_VSPATH}/VC/redist/x86/Microsoft.VC120.CRT
 
-## moz tools location for 64-bit builders ##
-export MOZ_TOOLS=C:/mozilla-build/moztools
-
 ## includes: win8.1 sdk includes, winrt headers for metro, msvc std library, directx sdk for d3d9 ##
 export INCLUDE=/c/Program\ Files\ \(x86\)/Windows\ Kits/8.1/include/shared:/c/Program\ Files\ \(x86\)/Windows\ Kits/8.1/include/um:/c/Program\ Files\ \(x86\)/Windows\ Kits/8.1/include/winrt:/c/Program\ Files\ \(x86\)/Windows\ Kits/8.1/include/winrt/wrl:/c/Program\ Files\ \(x86\)/Windows\ Kits/8.1/include/winrt/wrl/wrappers:${_VSPATH}/vc/include:${_VSPATH}/vc/atlmfc/include:/c/tools/sdks/dx10/include
 
 ## libs: win8.1 sdk x86 (32-bit) libs, msvc (32-bit) std library, msvc atl libs, directx sdk (32-bit) for d3d9  ##
 export LIBPATH=/c/Program\ Files\ \(x86\)/Windows\ Kits/8.1/Lib/winv6.3/um/x86:${_VSPATH}/vc/lib:${_VSPATH}/vc/atlmfc/lib:/c/tools/sdks/dx10/lib
 export LIB=/c/Program\ Files\ \(x86\)/Windows\ Kits/8.1/Lib/winv6.3/um/x86:${_VSPATH}/vc/lib:${_VSPATH}/vc/atlmfc/lib:/c/tools/sdks/dx10/lib
 
 ## paths: win8.1 sdk x86 (32-bit) tools, msvc (64-bit compiling 32-bit) build toolchain, moz tools  ##
@@ -19,10 +16,8 @@ export WINDOWSSDKDIR="/c/Program Files (
 
 . $topsrcdir/build/mozconfig.vs-common
 
 mk_export_correct_style LIB
 mk_export_correct_style LIBPATH
 mk_export_correct_style PATH
 mk_export_correct_style INCLUDE
 mk_export_correct_style WIN32_REDIST_DIR
-
-mk_add_options "export MOZ_TOOLS=$MOZ_TOOLS"
--- a/configure.in
+++ b/configure.in
@@ -2342,32 +2342,16 @@ ia64*-hpux*)
     case "$host" in
     *-mingw*)
         if test -n "$L10NBASEDIR"; then
             L10NBASEDIR=`cd $L10NBASEDIR && pwd -W`
         fi
         ;;
     esac
 
-    case "$host" in
-    *-mingw*)
-        if test -z "$MOZ_TOOLS"; then
-            AC_MSG_ERROR([MOZ_TOOLS is not set])
-        fi
-        MOZ_TOOLS_DIR=`cd $MOZ_TOOLS && pwd -W`
-        if test "$?" != "0" -o -z "$MOZ_TOOLS_DIR"; then
-            AC_MSG_ERROR([cd \$MOZ_TOOLS failed. MOZ_TOOLS ==? $MOZ_TOOLS])
-        fi
-        MOZ_TOOLS_BIN_DIR="$(cd "$MOZ_TOOLS_DIR/bin" && pwd)"
-        if test `echo ${PATH}: | grep -ic "$MOZ_TOOLS_BINDIR:"` = 0; then
-            AC_MSG_ERROR([\$MOZ_TOOLS\\bin must be in your path.])
-        fi
-        ;;
-    esac
-
     case "$host_os" in
     cygwin*|msvc*|mks*)
         AC_MSG_ERROR([Using a Cygwin build environment is unsupported. Configure cannot check for presence of necessary headers. Please upgrade to MozillaBuild; see https://developer.mozilla.org/en/Windows_Build_Prerequisites.])
         ;;
     esac
 
     case "$target" in
     i*86-*)
@@ -8682,17 +8666,16 @@ dnl We need SUBST for build system and D
 if test -n "$MOZ_TELEMETRY_REPORTING" || test -n "$MOZ_SERVICES_HEALTHREPORT" || test -n "$MOZ_CRASHREPORTER"; then
   MOZ_DATA_REPORTING=1
   AC_DEFINE(MOZ_DATA_REPORTING)
   AC_SUBST(MOZ_DATA_REPORTING)
 fi
 
 dnl win32 options
 AC_SUBST(MOZ_BROWSE_INFO)
-AC_SUBST(MOZ_TOOLS_DIR)
 AC_SUBST(WIN32_REDIST_DIR)
 AC_SUBST(MAKENSISU)
 
 dnl Echo the CFLAGS to remove extra whitespace.
 CFLAGS=`echo \
     $_WARNINGS_CFLAGS \
     $CFLAGS`
 
--- a/dom/base/nsContentAreaDragDrop.cpp
+++ b/dom/base/nsContentAreaDragDrop.cpp
@@ -27,16 +27,17 @@
 #include "nsIDOMHTMLAnchorElement.h"
 #include "nsITransferable.h"
 #include "nsComponentManagerUtils.h"
 #include "nsXPCOM.h"
 #include "nsISupportsPrimitives.h"
 #include "nsServiceManagerUtils.h"
 #include "nsNetUtil.h"
 #include "nsIFile.h"
+#include "nsFrameLoader.h"
 #include "nsIWebNavigation.h"
 #include "nsIDocShell.h"
 #include "nsIContent.h"
 #include "nsIImageLoadingContent.h"
 #include "nsITextControlElement.h"
 #include "nsUnicharUtils.h"
 #include "nsIURL.h"
 #include "nsIDocument.h"
@@ -47,16 +48,17 @@
 #include "nsEscape.h"
 #include "nsContentUtils.h"
 #include "nsIMIMEService.h"
 #include "imgIContainer.h"
 #include "imgIRequest.h"
 #include "mozilla/dom/DataTransfer.h"
 #include "nsIMIMEInfo.h"
 #include "nsRange.h"
+#include "TabParent.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/HTMLAreaElement.h"
 
 using namespace mozilla::dom;
 
 class MOZ_STACK_CLASS DragDataProducer
 {
 public:
@@ -409,18 +411,31 @@ DragDataProducer::Produce(DataTransfer* 
   // if set, serialize the content under this node
   nsCOMPtr<nsIContent> nodeToSerialize;
 
   nsCOMPtr<nsIDocShellTreeItem> dsti = mWindow->GetDocShell();
   const bool isChromeShell =
     dsti && dsti->ItemType() == nsIDocShellTreeItem::typeChrome;
 
   // In chrome shells, only allow dragging inside editable areas.
-  if (isChromeShell && !editingElement)
+  if (isChromeShell && !editingElement) {
+    nsCOMPtr<nsIFrameLoaderOwner> flo = do_QueryInterface(mTarget);
+    if (flo) {
+      nsRefPtr<nsFrameLoader> fl = flo->GetFrameLoader();
+      if (fl) {
+        TabParent* tp = static_cast<TabParent*>(fl->GetRemoteBrowser());
+        if (tp) {
+          // We have a TabParent, so it may have data for dnd in case the child
+          // process started a dnd session.
+          tp->AddInitialDnDDataTo(aDataTransfer);
+        }
+      }
+    }
     return NS_OK;
+  }
 
   if (isChromeShell && textControl) {
     // Only use the selection if the target node is in the selection.
     bool selectionContainsTarget = false;
     nsCOMPtr<nsIDOMNode> targetNode = do_QueryInterface(mSelectionTargetNode);
     selection->ContainsNode(targetNode, false, &selectionContainsTarget);
     if (!selectionContainsTarget)
       return NS_OK;
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -31,21 +31,24 @@
 #include "mozilla/Attributes.h"
 #include "mozilla/AutoRestore.h"
 #include "mozilla/AutoTimelineMarker.h"
 #include "mozilla/Base64.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/LoadInfo.h"
 #include "mozilla/dom/DocumentFragment.h"
+#include "mozilla/dom/DOMTypes.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/HTMLMediaElement.h"
 #include "mozilla/dom/HTMLTemplateElement.h"
 #include "mozilla/dom/HTMLContentElement.h"
 #include "mozilla/dom/HTMLShadowElement.h"
+#include "mozilla/dom/ipc/BlobChild.h"
+#include "mozilla/dom/ipc/BlobParent.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/TabParent.h"
 #include "mozilla/dom/TextDecoder.h"
 #include "mozilla/dom/TouchEvent.h"
 #include "mozilla/dom/ShadowRoot.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventListenerManager.h"
@@ -7199,8 +7202,99 @@ nsContentUtils::CallOnAllRemoteChildren(
     nsCOMPtr<nsIMessageBroadcaster> windowMM;
     chromeWindow->GetMessageManager(getter_AddRefs(windowMM));
     if (windowMM) {
       CallOnAllRemoteChildren(windowMM, aCallback, aArg);
     }
   }
 }
 
+void
+nsContentUtils::TransferablesToIPCTransferables(nsISupportsArray* aTransferables,
+                                                nsTArray<IPCDataTransfer>& aIPC,
+                                                mozilla::dom::nsIContentChild* aChild,
+                                                mozilla::dom::nsIContentParent* aParent)
+{
+  aIPC.Clear();
+  MOZ_ASSERT((aChild && !aParent) || (!aChild && aParent));
+  if (aTransferables) {
+    uint32_t transferableCount = 0;
+    aTransferables->Count(&transferableCount);
+    for (uint32_t i = 0; i < transferableCount; ++i) {
+      IPCDataTransfer* dt = aIPC.AppendElement();
+      nsCOMPtr<nsISupports> genericItem;
+      aTransferables->GetElementAt(i, getter_AddRefs(genericItem));
+      nsCOMPtr<nsITransferable> item(do_QueryInterface(genericItem));
+      if (item) {
+        nsCOMPtr<nsISupportsArray> flavorList;
+        item->FlavorsTransferableCanExport(getter_AddRefs(flavorList));
+        if (flavorList) {
+          uint32_t flavorCount = 0;
+          flavorList->Count(&flavorCount);
+          for (uint32_t j = 0; j < flavorCount; ++j) {
+            nsCOMPtr<nsISupportsCString> flavor = do_QueryElementAt(flavorList, j);
+            if (!flavor) {
+              continue;
+            }
+
+            nsAutoCString flavorStr;
+            flavor->GetData(flavorStr);
+            if (!flavorStr.Length()) {
+              continue;
+            }
+
+            nsCOMPtr<nsISupports> data;
+            uint32_t dataLen = 0;
+            item->GetTransferData(flavorStr.get(), getter_AddRefs(data), &dataLen);
+
+            nsCOMPtr<nsISupportsString> text = do_QueryInterface(data);
+            if (text) {
+              nsAutoString dataAsString;
+              text->GetData(dataAsString);
+              IPCDataTransferItem* item = dt->items().AppendElement();
+              item->flavor() = nsCString(flavorStr);
+              item->data() = nsString(dataAsString);
+            } else {
+              nsCOMPtr<nsISupportsInterfacePointer> sip =
+                do_QueryInterface(data);
+              if (sip) {
+                sip->GetData(getter_AddRefs(data));
+              }
+              nsCOMPtr<FileImpl> fileImpl;
+              nsCOMPtr<nsIFile> file = do_QueryInterface(data);
+              if (file) {
+                fileImpl = new FileImplFile(file, false);
+                ErrorResult rv;
+                fileImpl->GetSize(rv);
+                fileImpl->GetLastModified(rv);
+              } else {
+                fileImpl = do_QueryInterface(data);
+              }
+              if (fileImpl) {
+                IPCDataTransferItem* item = dt->items().AppendElement();
+                item->flavor() = nsCString(flavorStr);
+                if (aChild) {
+                  item->data() =
+                    mozilla::dom::BlobChild::GetOrCreate(aChild,
+                      static_cast<FileImpl*>(fileImpl.get()));
+                } else if (aParent) {
+                  item->data() =
+                    mozilla::dom::BlobParent::GetOrCreate(aParent,
+                      static_cast<FileImpl*>(fileImpl.get()));
+                }
+              } else {
+                // This is a hack to support kFilePromiseMime.
+                // On Windows there just needs to be an entry for it, 
+                // and for OSX we need to create
+                // nsContentAreaDragDropDataProvider as nsIFlavorDataProvider.
+                if (flavorStr.EqualsLiteral(kFilePromiseMime)) {
+                  IPCDataTransferItem* item = dt->items().AppendElement();
+                  item->flavor() = nsCString(flavorStr);
+                  item->data() = NS_ConvertUTF8toUTF16(flavorStr);
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+}
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -82,16 +82,17 @@ class nsIPresShell;
 class nsIPrincipal;
 class nsIRequest;
 class nsIRunnable;
 class nsIScriptContext;
 class nsIScriptGlobalObject;
 class nsIScriptSecurityManager;
 class nsIStringBundle;
 class nsIStringBundleService;
+class nsISupportsArray;
 class nsISupportsHashKey;
 class nsIURI;
 class nsIWidget;
 class nsIWordBreaker;
 class nsIXPConnect;
 class nsNodeInfoManager;
 class nsPIDOMWindow;
 class nsPresContext;
@@ -115,17 +116,20 @@ template<class T> class nsReadingIterato
 namespace mozilla {
 class ErrorResult;
 class EventListenerManager;
 
 namespace dom {
 class DocumentFragment;
 class Element;
 class EventTarget;
+class IPCDataTransfer;
 class NodeInfo;
+class nsIContentChild;
+class nsIContentParent;
 class Selection;
 class TabParent;
 } // namespace dom
 
 namespace layers {
 class LayerManager;
 } // namespace layers
 
@@ -2282,16 +2286,20 @@ public:
   /*
    * Call the given callback on all remote children of the given top-level
    * window.
    */
   static void CallOnAllRemoteChildren(nsIDOMWindow* aWindow,
                                       CallOnRemoteChildFunction aCallback,
                                       void* aArg);
 
+  static void TransferablesToIPCTransferables(nsISupportsArray* aTransferables,
+                                              nsTArray<mozilla::dom::IPCDataTransfer>& aIPC,
+                                              mozilla::dom::nsIContentChild* aChild,
+                                              mozilla::dom::nsIContentParent* aParent);
 private:
   static bool InitializeEventTable();
 
   static nsresult EnsureStringBundle(PropertiesFile aFile);
 
   static bool CanCallerAccess(nsIPrincipal* aSubjectPrincipal,
                                 nsIPrincipal* aPrincipal);
 
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -210,16 +210,17 @@
 #include "mozilla/dom/cache/CacheStorage.h"
 #include "mozilla/dom/Console.h"
 #include "mozilla/dom/Fetch.h"
 #include "mozilla/dom/FunctionBinding.h"
 #include "mozilla/dom/HashChangeEvent.h"
 #include "mozilla/dom/MozSelfSupportBinding.h"
 #include "mozilla/dom/PopStateEvent.h"
 #include "mozilla/dom/PopupBlockedEvent.h"
+#include "mozilla/dom/PrimitiveConversions.h"
 #include "mozilla/dom/WindowBinding.h"
 #include "nsITabChild.h"
 #include "mozilla/dom/MediaQueryList.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/NavigatorBinding.h"
 #ifdef HAVE_SIDEBAR
 #include "mozilla/dom/ExternalBinding.h"
 #endif
@@ -4536,28 +4537,17 @@ nsGlobalWindow::SetOpener(JSContext* aCx
 {
   // Check if we were called from a privileged chrome script.  If not, and if
   // aOpener is not null, just define aOpener on our inner window's JS object,
   // wrapped into the current compartment so that for Xrays we define on the
   // Xray expando object, but don't set it on the outer window, so that it'll
   // get reset on navigation.  This is just like replaceable properties, but
   // we're not quite readonly.
   if (!aOpener.isNull() && !nsContentUtils::IsCallerChrome()) {
-    JS::Rooted<JSObject*> thisObj(aCx, GetWrapperPreserveColor());
-    if (!thisObj) {
-      aError.Throw(NS_ERROR_UNEXPECTED);
-      return;
-    }
-
-    if (!JS_WrapObject(aCx, &thisObj) ||
-        !JS_DefineProperty(aCx, thisObj, "opener", aOpener, JSPROP_ENUMERATE,
-                           JS_STUBGETTER, JS_STUBSETTER)) {
-      aError.Throw(NS_ERROR_FAILURE);
-    }
-
+    RedefineProperty(aCx, "opener", aOpener, aError);
     return;
   }
 
   if (!aOpener.isObjectOrNull()) {
     // Chrome code trying to set some random value as opener
     aError.Throw(NS_ERROR_INVALID_ARG);
     return;
   }
@@ -4799,16 +4789,25 @@ nsGlobalWindow::GetInnerWidth(ErrorResul
 {
   FORWARD_TO_OUTER_OR_THROW(GetInnerWidth, (aError), aError, 0);
 
   CSSIntSize size;
   aError = GetInnerSize(size);
   return size.width;
 }
 
+void
+nsGlobalWindow::GetInnerWidth(JSContext* aCx,
+                              JS::MutableHandle<JS::Value> aValue,
+                              ErrorResult& aError)
+{
+  GetReplaceableWindowCoord(aCx, &nsGlobalWindow::GetInnerWidth, aValue,
+                            aError);
+}
+
 NS_IMETHODIMP
 nsGlobalWindow::GetInnerWidth(int32_t* aInnerWidth)
 {
   ErrorResult rv;
   *aInnerWidth = GetInnerWidth(rv);
 
   return rv.ErrorCode();
 }
@@ -4818,24 +4817,16 @@ nsGlobalWindow::SetInnerWidth(int32_t aI
 {
   FORWARD_TO_OUTER_OR_THROW(SetInnerWidth, (aInnerWidth, aError), aError, );
 
   if (!mDocShell) {
     aError.Throw(NS_ERROR_UNEXPECTED);
     return;
   }
 
-  /*
-   * If caller is not chrome and the user has not explicitly exempted the site,
-   * prevent setting window.innerWidth by exiting early
-   */
-  if (!CanMoveResizeWindows() || IsFrame()) {
-    return;
-  }
-
   CheckSecurityWidthAndHeight(&aInnerWidth, nullptr);
 
   nsRefPtr<nsIPresShell> presShell = mDocShell->GetPresShell();
 
   if (presShell && presShell->GetIsViewportOverridden())
   {
     nscoord height = 0;
 
@@ -4852,35 +4843,56 @@ nsGlobalWindow::SetInnerWidth(int32_t aI
   int32_t height = 0;
   int32_t unused  = 0;
 
   nsCOMPtr<nsIBaseWindow> docShellAsWin(do_QueryInterface(mDocShell));
   docShellAsWin->GetSize(&unused, &height);
   aError = SetDocShellWidthAndHeight(CSSToDevIntPixels(aInnerWidth), height);
 }
 
+void
+nsGlobalWindow::SetInnerWidth(JSContext* aCx, JS::Handle<JS::Value> aValue,
+                              ErrorResult& aError)
+{
+  SetReplaceableWindowCoord(aCx, &nsGlobalWindow::SetInnerWidth,
+                            aValue, "innerWidth", aError);
+}
+
 NS_IMETHODIMP
 nsGlobalWindow::SetInnerWidth(int32_t aInnerWidth)
 {
+  if (IsFrame()) {
+    return NS_OK;
+  }
+
   ErrorResult rv;
   SetInnerWidth(aInnerWidth, rv);
 
   return rv.ErrorCode();
 }
 
 int32_t
 nsGlobalWindow::GetInnerHeight(ErrorResult& aError)
 {
   FORWARD_TO_OUTER_OR_THROW(GetInnerHeight, (aError), aError, 0);
 
   CSSIntSize size;
   aError = GetInnerSize(size);
   return size.height;
 }
 
+void
+nsGlobalWindow::GetInnerHeight(JSContext* aCx,
+                              JS::MutableHandle<JS::Value> aValue,
+                              ErrorResult& aError)
+{
+  GetReplaceableWindowCoord(aCx, &nsGlobalWindow::GetInnerHeight, aValue,
+                            aError);
+}
+
 NS_IMETHODIMP
 nsGlobalWindow::GetInnerHeight(int32_t* aInnerHeight)
 {
   ErrorResult rv;
   *aInnerHeight = GetInnerHeight(rv);
 
   return rv.ErrorCode();
 }
@@ -4890,24 +4902,16 @@ nsGlobalWindow::SetInnerHeight(int32_t a
 {
   FORWARD_TO_OUTER_OR_THROW(SetInnerHeight, (aInnerHeight, aError), aError, );
 
   if (!mDocShell) {
     aError.Throw(NS_ERROR_UNEXPECTED);
     return;
   }
 
-  /*
-   * If caller is not chrome and the user has not explicitly exempted the site,
-   * prevent setting window.innerHeight by exiting early
-   */
-  if (!CanMoveResizeWindows() || IsFrame()) {
-    return;
-  }
-
   nsRefPtr<nsIPresShell> presShell = mDocShell->GetPresShell();
 
   if (presShell && presShell->GetIsViewportOverridden())
   {
     nsRefPtr<nsPresContext> presContext;
     presContext = presShell->GetPresContext();
 
     nsRect shellArea = presContext->GetVisibleArea();
@@ -4923,19 +4927,31 @@ nsGlobalWindow::SetInnerHeight(int32_t a
   int32_t width  = 0;
 
   nsCOMPtr<nsIBaseWindow> docShellAsWin(do_QueryInterface(mDocShell));
   docShellAsWin->GetSize(&width, &height);
   CheckSecurityWidthAndHeight(nullptr, &aInnerHeight);
   aError = SetDocShellWidthAndHeight(width, CSSToDevIntPixels(aInnerHeight));
 }
 
+void
+nsGlobalWindow::SetInnerHeight(JSContext* aCx, JS::Handle<JS::Value> aValue,
+                               ErrorResult& aError)
+{
+  SetReplaceableWindowCoord(aCx, &nsGlobalWindow::SetInnerHeight,
+                            aValue, "innerHeight", aError);
+}
+
 NS_IMETHODIMP
 nsGlobalWindow::SetInnerHeight(int32_t aInnerHeight)
 {
+  if (IsFrame()) {
+    return NS_OK;
+  }
+
   ErrorResult rv;
   SetInnerHeight(aInnerHeight, rv);
 
   return rv.ErrorCode();
 }
 
 nsIntSize
 nsGlobalWindow::GetOuterSize(ErrorResult& aError)
@@ -4965,56 +4981,65 @@ nsGlobalWindow::GetOuterSize(ErrorResult
 
 int32_t
 nsGlobalWindow::GetOuterWidth(ErrorResult& aError)
 {
   FORWARD_TO_OUTER_OR_THROW(GetOuterWidth, (aError), aError, 0);
   return GetOuterSize(aError).width;
 }
 
+void
+nsGlobalWindow::GetOuterWidth(JSContext* aCx,
+                              JS::MutableHandle<JS::Value> aValue,
+                              ErrorResult& aError)
+{
+  GetReplaceableWindowCoord(aCx, &nsGlobalWindow::GetOuterWidth, aValue,
+                            aError);
+}
+
 NS_IMETHODIMP
 nsGlobalWindow::GetOuterWidth(int32_t* aOuterWidth)
 {
   ErrorResult rv;
   *aOuterWidth = GetOuterWidth(rv);
 
   return rv.ErrorCode();
 }
 
 int32_t
 nsGlobalWindow::GetOuterHeight(ErrorResult& aError)
 {
   FORWARD_TO_OUTER_OR_THROW(GetOuterHeight, (aError), aError, 0);
   return GetOuterSize(aError).height;
 }
 
+void
+nsGlobalWindow::GetOuterHeight(JSContext* aCx,
+                               JS::MutableHandle<JS::Value> aValue,
+                               ErrorResult& aError)
+{
+  GetReplaceableWindowCoord(aCx, &nsGlobalWindow::GetOuterHeight, aValue,
+                            aError);
+}
+
 NS_IMETHODIMP
 nsGlobalWindow::GetOuterHeight(int32_t* aOuterHeight)
 {
   ErrorResult rv;
   *aOuterHeight = GetOuterHeight(rv);
 
   return rv.ErrorCode();
 }
 
 void
 nsGlobalWindow::SetOuterSize(int32_t aLengthCSSPixels, bool aIsWidth,
                              ErrorResult& aError)
 {
   MOZ_ASSERT(IsOuterWindow());
 
-  /*
-   * If caller is not chrome and the user has not explicitly exempted the site,
-   * prevent setting window.outerWidth by exiting early
-   */
-
-  if (!CanMoveResizeWindows() || IsFrame()) {
-    return;
-  }
-
   nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
   if (!treeOwnerAsWin) {
     aError.Throw(NS_ERROR_FAILURE);
     return;
   }
 
   CheckSecurityWidthAndHeight(aIsWidth ? &aLengthCSSPixels : nullptr,
                               aIsWidth ? nullptr : &aLengthCSSPixels);
@@ -5037,36 +5062,60 @@ nsGlobalWindow::SetOuterSize(int32_t aLe
 void
 nsGlobalWindow::SetOuterWidth(int32_t aOuterWidth, ErrorResult& aError)
 {
   FORWARD_TO_OUTER_OR_THROW(SetOuterWidth, (aOuterWidth, aError), aError, );
 
   SetOuterSize(aOuterWidth, true, aError);
 }
 
+void
+nsGlobalWindow::SetOuterWidth(JSContext* aCx, JS::Handle<JS::Value> aValue,
+                              ErrorResult& aError)
+{
+  SetReplaceableWindowCoord(aCx, &nsGlobalWindow::SetOuterWidth,
+                            aValue, "outerWidth", aError);
+}
+
 NS_IMETHODIMP
 nsGlobalWindow::SetOuterWidth(int32_t aOuterWidth)
 {
+  if (IsFrame()) {
+    return NS_OK;
+  }
+
   ErrorResult rv;
   SetOuterWidth(aOuterWidth, rv);
 
   return rv.ErrorCode();
 }
 
 void
 nsGlobalWindow::SetOuterHeight(int32_t aOuterHeight, ErrorResult& aError)
 {
   FORWARD_TO_OUTER_OR_THROW(SetOuterHeight, (aOuterHeight, aError), aError, );
 
   SetOuterSize(aOuterHeight, false, aError);
 }
 
+void
+nsGlobalWindow::SetOuterHeight(JSContext* aCx, JS::Handle<JS::Value> aValue,
+                               ErrorResult& aError)
+{
+  SetReplaceableWindowCoord(aCx, &nsGlobalWindow::SetOuterHeight,
+                            aValue, "outerHeight", aError);
+}
+
 NS_IMETHODIMP
 nsGlobalWindow::SetOuterHeight(int32_t aOuterHeight)
 {
+  if (IsFrame()) {
+    return NS_OK;
+  }
+
   ErrorResult rv;
   SetOuterHeight(aOuterHeight, rv);
 
   return rv.ErrorCode();
 }
 
 nsIntPoint
 nsGlobalWindow::GetScreenXY(ErrorResult& aError)
@@ -5087,16 +5136,25 @@ nsGlobalWindow::GetScreenXY(ErrorResult&
 int32_t
 nsGlobalWindow::GetScreenX(ErrorResult& aError)
 {
   FORWARD_TO_OUTER_OR_THROW(GetScreenX, (aError), aError, 0);
 
   return DevToCSSIntPixels(GetScreenXY(aError).x);
 }
 
+void
+nsGlobalWindow::GetScreenX(JSContext* aCx,
+                           JS::MutableHandle<JS::Value> aValue,
+                           ErrorResult& aError)
+{
+  GetReplaceableWindowCoord(aCx, &nsGlobalWindow::GetScreenX, aValue,
+                            aError);
+}
+
 NS_IMETHODIMP
 nsGlobalWindow::GetScreenX(int32_t* aScreenX)
 {
   ErrorResult rv;
   *aScreenX = GetScreenX(rv);
 
   return rv.ErrorCode();
 }
@@ -5379,25 +5437,16 @@ nsGlobalWindow::MatchMedia(const nsAStri
   return rv.ErrorCode();
 }
 
 void
 nsGlobalWindow::SetScreenX(int32_t aScreenX, ErrorResult& aError)
 {
   FORWARD_TO_OUTER_OR_THROW(SetScreenX, (aScreenX, aError), aError, );
 
-  /*
-   * If caller is not chrome and the user has not explicitly exempted the site,
-   * prevent setting window.screenX by exiting early
-   */
-
-  if (!CanMoveResizeWindows() || IsFrame()) {
-    return;
-  }
-
   nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
   if (!treeOwnerAsWin) {
     aError.Throw(NS_ERROR_FAILURE);
     return;
   }
 
   int32_t x, y;
   aError = treeOwnerAsWin->GetPosition(&x, &y);
@@ -5406,19 +5455,31 @@ nsGlobalWindow::SetScreenX(int32_t aScre
   }
 
   CheckSecurityLeftAndTop(&aScreenX, nullptr);
   x = CSSToDevIntPixels(aScreenX);
 
   aError = treeOwnerAsWin->SetPosition(x, y);
 }
 
+void
+nsGlobalWindow::SetScreenX(JSContext* aCx, JS::Handle<JS::Value> aValue,
+                           ErrorResult& aError)
+{
+  SetReplaceableWindowCoord(aCx, &nsGlobalWindow::SetScreenX,
+                            aValue, "screenX", aError);
+}
+
 NS_IMETHODIMP
 nsGlobalWindow::SetScreenX(int32_t aScreenX)
 {
+  if (IsFrame()) {
+    return NS_OK;
+  }
+
   ErrorResult rv;
   SetScreenX(aScreenX, rv);
 
   return rv.ErrorCode();
 }
 
 int32_t
 nsGlobalWindow::GetScreenY(ErrorResult& aError)
@@ -5433,29 +5494,29 @@ nsGlobalWindow::GetScreenY(int32_t* aScr
 {
   ErrorResult rv;
   *aScreenY = GetScreenY(rv);
 
   return rv.ErrorCode();
 }
 
 void
+nsGlobalWindow::GetScreenY(JSContext* aCx,
+                           JS::MutableHandle<JS::Value> aValue,
+                           ErrorResult& aError)
+{
+  GetReplaceableWindowCoord(aCx, &nsGlobalWindow::GetScreenY, aValue,
+                            aError);
+}
+
+void
 nsGlobalWindow::SetScreenY(int32_t aScreenY, ErrorResult& aError)
 {
   FORWARD_TO_OUTER_OR_THROW(SetScreenY, (aScreenY, aError), aError, );
 
-  /*
-   * If caller is not chrome and the user has not explicitly exempted the site,
-   * prevent setting window.screenY by exiting early
-   */
-
-  if (!CanMoveResizeWindows() || IsFrame()) {
-    return;
-  }
-
   nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
   if (!treeOwnerAsWin) {
     aError.Throw(NS_ERROR_FAILURE);
     return;
   }
 
   int32_t x, y;
   aError = treeOwnerAsWin->GetPosition(&x, &y);
@@ -5464,19 +5525,31 @@ nsGlobalWindow::SetScreenY(int32_t aScre
   }
 
   CheckSecurityLeftAndTop(nullptr, &aScreenY);
   y = CSSToDevIntPixels(aScreenY);
 
   aError = treeOwnerAsWin->SetPosition(x, y);
 }
 
+void
+nsGlobalWindow::SetScreenY(JSContext* aCx, JS::Handle<JS::Value> aValue,
+                           ErrorResult& aError)
+{
+  SetReplaceableWindowCoord(aCx, &nsGlobalWindow::SetScreenY,
+                            aValue, "screenY", aError);
+}
+
 NS_IMETHODIMP
 nsGlobalWindow::SetScreenY(int32_t aScreenY)
 {
+  if (IsFrame()) {
+    return NS_OK;
+  }
+
   ErrorResult rv;
   SetScreenY(aScreenY, rv);
 
   return rv.ErrorCode();
 }
 
 // NOTE: Arguments to this function should have values scaled to
 // CSS pixels, not device pixels.
@@ -13969,28 +14042,19 @@ nsGlobalWindow::GetConsole(JSContext* aC
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsGlobalWindow::SetConsole(JSContext* aCx, JS::Handle<JS::Value> aValue)
 {
-  JS::Rooted<JSObject*> thisObj(aCx, GetWrapper());
-  if (!thisObj) {
-    return NS_ERROR_UNEXPECTED;
-  }
-
-  if (!JS_WrapObject(aCx, &thisObj) ||
-      !JS_DefineProperty(aCx, thisObj, "console", aValue, JSPROP_ENUMERATE,
-                         JS_STUBGETTER, JS_STUBSETTER)) {
-    return NS_ERROR_FAILURE;
-  }
-
-  return NS_OK;
+  ErrorResult rv;
+  RedefineProperty(aCx, "console", aValue, rv);
+  return rv.ErrorCode();
 }
 
 Console*
 nsGlobalWindow::GetConsole(ErrorResult& aRv)
 {
   FORWARD_TO_INNER_OR_THROW(GetConsole, (aRv), aRv, nullptr);
 
   if (!mConsole) {
@@ -14140,11 +14204,68 @@ nsGlobalWindow::DisableNetworkEvent(uint
         mNetworkDownloadObserverEnabled = false;
         os->RemoveObserver(mObserver, NS_NETWORK_ACTIVITY_BLIP_DOWNLOAD_TOPIC);
       }
       break;
   }
 }
 #endif // MOZ_B2G
 
+void
+nsGlobalWindow::RedefineProperty(JSContext* aCx, const char* aPropName,
+                                 JS::Handle<JS::Value> aValue,
+                                 ErrorResult& aError)
+{
+  JS::Rooted<JSObject*> thisObj(aCx, GetWrapperPreserveColor());
+  if (!thisObj) {
+    aError.Throw(NS_ERROR_UNEXPECTED);
+    return;
+  }
+
+  if (!JS_WrapObject(aCx, &thisObj) ||
+      !JS_DefineProperty(aCx, thisObj, aPropName, aValue, JSPROP_ENUMERATE,
+                         JS_STUBGETTER, JS_STUBSETTER)) {
+    aError.Throw(NS_ERROR_FAILURE);
+  }
+}
+
+void
+nsGlobalWindow::GetReplaceableWindowCoord(JSContext* aCx,
+                                          nsGlobalWindow::WindowCoordGetter aGetter,
+                                          JS::MutableHandle<JS::Value> aRetval,
+                                          ErrorResult& aError)
+{
+  int32_t coord = (this->*aGetter)(aError);
+  if (!aError.Failed() &&
+      !ToJSValue(aCx, coord, aRetval)) {
+    aError.Throw(NS_ERROR_FAILURE);
+  }
+}
+
+void
+nsGlobalWindow::SetReplaceableWindowCoord(JSContext* aCx,
+                                          nsGlobalWindow::WindowCoordSetter aSetter,
+                                          JS::Handle<JS::Value> aValue,
+                                          const char* aPropName,
+                                          ErrorResult& aError)
+{
+  /*
+   * If caller is not chrome and the user has not explicitly exempted the site,
+   * just treat this the way we would an IDL replaceable property.
+   */
+  nsGlobalWindow* outer = GetOuterWindowInternal();
+  if (!outer || !outer->CanMoveResizeWindows() || outer->IsFrame()) {
+    RedefineProperty(aCx, aPropName, aValue, aError);
+    return;
+  }
+
+  int32_t value;
+  if (!ValueToPrimitive<int32_t, eDefault>(aCx, aValue, &value)) {
+    aError.Throw(NS_ERROR_UNEXPECTED);
+    return;
+  }
+
+  (this->*aSetter)(value, aError);
+}
+
 #ifdef _WINDOWS_
 #error "Never include windows.h in this file!"
 #endif
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -961,39 +961,51 @@ public:
   void ScrollTo(const mozilla::dom::ScrollToOptions& aOptions);
   void ScrollBy(double aXScrollDif, double aYScrollDif);
   void ScrollBy(const mozilla::dom::ScrollToOptions& aOptions);
   void ScrollByLines(int32_t numLines,
                      const mozilla::dom::ScrollOptions& aOptions);
   void ScrollByPages(int32_t numPages,
                      const mozilla::dom::ScrollOptions& aOptions);
   void MozScrollSnap();
-  int32_t GetInnerWidth(mozilla::ErrorResult& aError);
-  void SetInnerWidth(int32_t aInnerWidth, mozilla::ErrorResult& aError);
-  int32_t GetInnerHeight(mozilla::ErrorResult& aError);
-  void SetInnerHeight(int32_t aInnerHeight, mozilla::ErrorResult& aError);
+  void GetInnerWidth(JSContext* aCx, JS::MutableHandle<JS::Value> aValue,
+                     mozilla::ErrorResult& aError);
+  void SetInnerWidth(JSContext* aCx, JS::Handle<JS::Value> aValue,
+                     mozilla::ErrorResult& aError);
+  void GetInnerHeight(JSContext* aCx, JS::MutableHandle<JS::Value> aValue,
+                     mozilla::ErrorResult& aError);
+  void SetInnerHeight(JSContext* aCx, JS::Handle<JS::Value> aValue,
+                      mozilla::ErrorResult& aError);
   int32_t GetScrollX(mozilla::ErrorResult& aError);
   int32_t GetPageXOffset(mozilla::ErrorResult& aError)
   {
     return GetScrollX(aError);
   }
   int32_t GetScrollY(mozilla::ErrorResult& aError);
   int32_t GetPageYOffset(mozilla::ErrorResult& aError)
   {
     return GetScrollY(aError);
   }
   void MozRequestOverfill(mozilla::dom::OverfillCallback& aCallback, mozilla::ErrorResult& aError);
-  int32_t GetScreenX(mozilla::ErrorResult& aError);
-  void SetScreenX(int32_t aScreenX, mozilla::ErrorResult& aError);
-  int32_t GetScreenY(mozilla::ErrorResult& aError);
-  void SetScreenY(int32_t aScreenY, mozilla::ErrorResult& aError);
-  int32_t GetOuterWidth(mozilla::ErrorResult& aError);
-  void SetOuterWidth(int32_t aOuterWidth, mozilla::ErrorResult& aError);
-  int32_t GetOuterHeight(mozilla::ErrorResult& aError);
-  void SetOuterHeight(int32_t aOuterHeight, mozilla::ErrorResult& aError);
+  void GetScreenX(JSContext* aCx, JS::MutableHandle<JS::Value> aValue,
+                  mozilla::ErrorResult& aError);
+  void SetScreenX(JSContext* aCx, JS::Handle<JS::Value> aValue,
+                  mozilla::ErrorResult& aError);
+  void GetScreenY(JSContext* aCx, JS::MutableHandle<JS::Value> aValue,
+                  mozilla::ErrorResult& aError);
+  void SetScreenY(JSContext* aCx, JS::Handle<JS::Value> aValue,
+                  mozilla::ErrorResult& aError);
+  void GetOuterWidth(JSContext* aCx, JS::MutableHandle<JS::Value> aValue,
+                     mozilla::ErrorResult& aError);
+  void SetOuterWidth(JSContext* aCx, JS::Handle<JS::Value> aValue,
+                     mozilla::ErrorResult& aError);
+  void GetOuterHeight(JSContext* aCx, JS::MutableHandle<JS::Value> aValue,
+                      mozilla::ErrorResult& aError);
+  void SetOuterHeight(JSContext* aCx, JS::Handle<JS::Value> aValue,
+                      mozilla::ErrorResult& aError);
   int32_t RequestAnimationFrame(mozilla::dom::FrameRequestCallback& aCallback,
                                 mozilla::ErrorResult& aError);
   void CancelAnimationFrame(int32_t aHandle, mozilla::ErrorResult& aError);
   nsPerformance* GetPerformance();
 #ifdef MOZ_WEBSPEECH
   mozilla::dom::SpeechSynthesis*
     GetSpeechSynthesis(mozilla::ErrorResult& aError);
 #endif
@@ -1082,16 +1094,51 @@ public:
   void SetReturnValue(JSContext* aCx, JS::Handle<JS::Value> aReturnValue,
                       mozilla::ErrorResult& aError);
 
   void GetInterface(JSContext* aCx, nsIJSID* aIID,
                     JS::MutableHandle<JS::Value> aRetval,
                     mozilla::ErrorResult& aError);
 
 protected:
+  // Web IDL helpers
+
+  // Redefine the property called aPropName on this window object to be a value
+  // property with the value aValue, much like we would do for a [Replaceable]
+  // property in IDL.
+  void RedefineProperty(JSContext* aCx, const char* aPropName,
+                        JS::Handle<JS::Value> aValue,
+                        mozilla::ErrorResult& aError);
+
+  // Implementation guts for our writable IDL attributes that are really
+  // supposed to be readonly replaceable.
+  typedef int32_t (nsGlobalWindow::*WindowCoordGetter)(mozilla::ErrorResult&);
+  typedef void (nsGlobalWindow::*WindowCoordSetter)(int32_t,
+                                                    mozilla::ErrorResult&);
+  void GetReplaceableWindowCoord(JSContext* aCx, WindowCoordGetter aGetter,
+                                 JS::MutableHandle<JS::Value> aRetval,
+                                 mozilla::ErrorResult& aError);
+  void SetReplaceableWindowCoord(JSContext* aCx, WindowCoordSetter aSetter,
+                                 JS::Handle<JS::Value> aValue,
+                                 const char* aPropName,
+                                 mozilla::ErrorResult& aError);
+  // And the implementations of WindowCoordGetter/WindowCoordSetter.
+  int32_t GetInnerWidth(mozilla::ErrorResult& aError);
+  void SetInnerWidth(int32_t aInnerWidth, mozilla::ErrorResult& aError);
+  int32_t GetInnerHeight(mozilla::ErrorResult& aError);
+  void SetInnerHeight(int32_t aInnerHeight, mozilla::ErrorResult& aError);
+  int32_t GetScreenX(mozilla::ErrorResult& aError);
+  void SetScreenX(int32_t aScreenX, mozilla::ErrorResult& aError);
+  int32_t GetScreenY(mozilla::ErrorResult& aError);
+  void SetScreenY(int32_t aScreenY, mozilla::ErrorResult& aError);
+  int32_t GetOuterWidth(mozilla::ErrorResult& aError);
+  void SetOuterWidth(int32_t aOuterWidth, mozilla::ErrorResult& aError);
+  int32_t GetOuterHeight(mozilla::ErrorResult& aError);
+  void SetOuterHeight(int32_t aOuterHeight, mozilla::ErrorResult& aError);
+
   // Array of idle observers that are notified of idle events.
   nsTObserverArray<IdleObserverHolder> mIdleObservers;
 
   // Idle timer used for function callbacks to notify idle observers.
   nsCOMPtr<nsITimer> mIdleTimer;
 
   // Idle fuzz time added to idle timer callbacks.
   uint32_t mIdleFuzzFactor;
--- a/dom/base/nsScriptLoader.cpp
+++ b/dom/base/nsScriptLoader.cpp
@@ -46,91 +46,60 @@
 #include "nsCORSListenerProxy.h"
 #include "nsProxyRelease.h"
 #include "nsSandboxFlags.h"
 #include "nsContentTypeParser.h"
 #include "nsINetworkPredictor.h"
 #include "ImportManager.h"
 #include "mozilla/dom/EncodingUtils.h"
 
-#include "mozilla/CORSMode.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/unused.h"
 
 #ifdef PR_LOGGING
 static PRLogModuleInfo* gCspPRLog;
 #endif
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
-//////////////////////////////////////////////////////////////
-// Per-request data structure
-//////////////////////////////////////////////////////////////
-
-class nsScriptLoadRequest final : public nsISupports {
-  ~nsScriptLoadRequest()
-  {
-    js_free(mScriptTextBuf);
-  }
-
-public:
-  nsScriptLoadRequest(nsIScriptElement* aElement,
-                      uint32_t aVersion,
-                      CORSMode aCORSMode)
-    : mElement(aElement),
-      mLoading(true),
-      mIsInline(true),
-      mHasSourceMapURL(false),
-      mScriptTextBuf(nullptr),
-      mScriptTextLength(0),
-      mJSVersion(aVersion),
-      mLineNo(1),
-      mCORSMode(aCORSMode),
-      mReferrerPolicy(mozilla::net::RP_Default)
-  {
-  }
-
-  NS_DECL_THREADSAFE_ISUPPORTS
-
-  void FireScriptAvailable(nsresult aResult)
-  {
-    mElement->ScriptAvailable(aResult, mElement, mIsInline, mURI, mLineNo);
-  }
-  void FireScriptEvaluated(nsresult aResult)
-  {
-    mElement->ScriptEvaluated(aResult, mElement, mIsInline);
-  }
-
-  bool IsPreload()
-  {
-    return mElement == nullptr;
-  }
-
-  nsCOMPtr<nsIScriptElement> mElement;
-  bool mLoading;          // Are we still waiting for a load to complete?
-  bool mIsInline;         // Is the script inline or loaded?
-  bool mHasSourceMapURL;  // Does the HTTP header have a source map url?
-  nsString mSourceMapURL; // Holds source map url for loaded scripts
-  char16_t* mScriptTextBuf; // Holds script text for non-inline scripts. Don't
-  size_t mScriptTextLength; // use nsString so we can give ownership to jsapi.
-  uint32_t mJSVersion;
-  nsCOMPtr<nsIURI> mURI;
-  nsCOMPtr<nsIPrincipal> mOriginPrincipal;
-  nsAutoCString mURL;   // Keep the URI's filename alive during off thread parsing.
-  int32_t mLineNo;
-  const CORSMode mCORSMode;
-  mozilla::net::ReferrerPolicy mReferrerPolicy;
-};
-
 // The nsScriptLoadRequest is passed as the context to necko, and thus
 // it needs to be threadsafe. Necko won't do anything with this
 // context, but it will AddRef and Release it on other threads.
 NS_IMPL_ISUPPORTS0(nsScriptLoadRequest)
 
+nsScriptLoadRequestList::~nsScriptLoadRequestList()
+{
+  Clear();
+}
+
+void
+nsScriptLoadRequestList::Clear()
+{
+  while (!isEmpty()) {
+    nsRefPtr<nsScriptLoadRequest> first = StealFirst();
+    // And just let it go out of scope and die.
+  }
+}
+
+#ifdef DEBUG
+bool
+nsScriptLoadRequestList::Contains(nsScriptLoadRequest* aElem)
+{
+  for (nsScriptLoadRequest* req = getFirst();
+       req; req = req->getNext()) {
+    if (req == aElem) {
+      return true;
+    }
+  }
+
+  return false;
+}
+#endif // DEBUG
+
 //////////////////////////////////////////////////////////////
 //
 //////////////////////////////////////////////////////////////
 
 nsScriptLoader::nsScriptLoader(nsIDocument *aDocument)
   : mDocument(aDocument),
     mBlockerCount(0),
     mEnabled(true),
@@ -148,30 +117,40 @@ nsScriptLoader::nsScriptLoader(nsIDocume
 nsScriptLoader::~nsScriptLoader()
 {
   mObservers.Clear();
 
   if (mParserBlockingRequest) {
     mParserBlockingRequest->FireScriptAvailable(NS_ERROR_ABORT);
   }
 
-  for (uint32_t i = 0; i < mXSLTRequests.Length(); i++) {
-    mXSLTRequests[i]->FireScriptAvailable(NS_ERROR_ABORT);
+  for (nsScriptLoadRequest* req = mXSLTRequests.getFirst(); req;
+       req = req->getNext()) {
+    req->FireScriptAvailable(NS_ERROR_ABORT);
+  }
+
+  for (nsScriptLoadRequest* req = mDeferRequests.getFirst(); req;
+       req = req->getNext()) {
+    req->FireScriptAvailable(NS_ERROR_ABORT);
   }
 
-  for (uint32_t i = 0; i < mDeferRequests.Length(); i++) {
-    mDeferRequests[i]->FireScriptAvailable(NS_ERROR_ABORT);
+  for (nsScriptLoadRequest* req = mLoadingAsyncRequests.getFirst(); req;
+       req = req->getNext()) {
+    req->FireScriptAvailable(NS_ERROR_ABORT);
   }
 
-  for (uint32_t i = 0; i < mAsyncRequests.Length(); i++) {
-    mAsyncRequests[i]->FireScriptAvailable(NS_ERROR_ABORT);
+  for (nsScriptLoadRequest* req = mLoadedAsyncRequests.getFirst(); req;
+       req = req->getNext()) {
+    req->FireScriptAvailable(NS_ERROR_ABORT);
   }
 
-  for (uint32_t i = 0; i < mNonAsyncExternalScriptInsertedRequests.Length(); i++) {
-    mNonAsyncExternalScriptInsertedRequests[i]->FireScriptAvailable(NS_ERROR_ABORT);
+  for(nsScriptLoadRequest* req = mNonAsyncExternalScriptInsertedRequests.getFirst();
+      req;
+      req = req->getNext()) {
+    req->FireScriptAvailable(NS_ERROR_ABORT);
   }
 
   // Unblock the kids, in case any of them moved to a different document
   // subtree in the meantime and therefore aren't actually going away.
   for (uint32_t j = 0; j < mPendingChildLoaders.Length(); ++j) {
     mPendingChildLoaders[j]->RemoveExecuteBlocker();
   }  
 }
@@ -646,28 +625,32 @@ nsScriptLoader::ProcessScriptElement(nsI
                                &nsIScriptElement::FireErrorEvent));
         return false;
       }
     }
 
     request->mJSVersion = version;
 
     if (aElement->GetScriptAsync()) {
-      mAsyncRequests.AppendElement(request);
+      request->mIsAsync = true;
       if (!request->mLoading) {
+        mLoadedAsyncRequests.AppendElement(request);
         // The script is available already. Run it ASAP when the event
         // loop gets a chance to spin.
         ProcessPendingRequestsAsync();
+      } else {
+        mLoadingAsyncRequests.AppendElement(request);
       }
       return false;
     }
     if (!aElement->GetParserCreated()) {
       // Violate the HTML5 spec in order to make LABjs and the "order" plug-in
       // for RequireJS work with their Gecko-sniffed code path. See
       // http://lists.w3.org/Archives/Public/public-html/2010Oct/0088.html
+      request->mIsNonAsyncScriptInserted = true;
       mNonAsyncExternalScriptInsertedRequests.AppendElement(request);
       if (!request->mLoading) {
         // The script is available already. Run it ASAP when the event
         // loop gets a chance to spin.
         ProcessPendingRequestsAsync();
       }
       return false;
     }
@@ -686,16 +669,17 @@ nsScriptLoader::ProcessScriptElement(nsI
       AddDeferRequest(request);
       return false;
     }
 
     if (aElement->GetParserCreated() == FROM_PARSER_XSLT) {
       // Need to maintain order for XSLT-inserted scripts
       NS_ASSERTION(!mParserBlockingRequest,
           "Parser-blocking scripts and XSLT scripts in the same doc!");
+      request->mIsXSLT = true;
       mXSLTRequests.AppendElement(request);
       if (!request->mLoading) {
         // The script is available already. Run it ASAP when the event
         // loop gets a chance to spin.
         ProcessPendingRequestsAsync();
       }
       return true;
     }
@@ -706,27 +690,27 @@ nsScriptLoader::ProcessScriptElement(nsI
       if (aElement->GetParserCreated() == FROM_PARSER_NETWORK) {
         return ProcessRequest(request) == NS_ERROR_HTMLPARSER_BLOCK;
       }
       // Otherwise, we've got a document.written script, make a trip through
       // the event loop to hide the preload effects from the scripts on the
       // Web page.
       NS_ASSERTION(!mParserBlockingRequest,
           "There can be only one parser-blocking script at a time");
-      NS_ASSERTION(mXSLTRequests.IsEmpty(),
+      NS_ASSERTION(mXSLTRequests.isEmpty(),
           "Parser-blocking scripts and XSLT scripts in the same doc!");
       mParserBlockingRequest = request;
       ProcessPendingRequestsAsync();
       return true;
     }
     // The script hasn't loaded yet or there's a style sheet blocking it.
     // The script will be run when it loads or the style sheet loads.
     NS_ASSERTION(!mParserBlockingRequest,
         "There can be only one parser-blocking script at a time");
-    NS_ASSERTION(mXSLTRequests.IsEmpty(),
+    NS_ASSERTION(mXSLTRequests.isEmpty(),
         "Parser-blocking scripts and XSLT scripts in the same doc!");
     mParserBlockingRequest = request;
     return true;
   }
 
   // inline script
   // Is this document sandboxed without 'allow-scripts'?
   if (mDocument->GetSandboxFlags() & SANDBOXED_SCRIPTS) {
@@ -742,17 +726,17 @@ nsScriptLoader::ProcessScriptElement(nsI
   request = new nsScriptLoadRequest(aElement, version, CORS_NONE);
   request->mJSVersion = version;
   request->mLoading = false;
   request->mIsInline = true;
   request->mURI = mDocument->GetDocumentURI();
   request->mLineNo = aElement->GetScriptLineNumber();
 
   if (aElement->GetParserCreated() == FROM_PARSER_XSLT &&
-      (!ReadyToExecuteScripts() || !mXSLTRequests.IsEmpty())) {
+      (!ReadyToExecuteScripts() || !mXSLTRequests.isEmpty())) {
     // Need to maintain order for XSLT-inserted scripts
     NS_ASSERTION(!mParserBlockingRequest,
         "Parser-blocking scripts and XSLT scripts in the same doc!");
     mXSLTRequests.AppendElement(request);
     return true;
   }
   if (aElement->GetParserCreated() == NOT_FROM_PARSER) {
     NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
@@ -761,17 +745,17 @@ nsScriptLoader::ProcessScriptElement(nsI
                                                                  request));
     return false;
   }
   if (aElement->GetParserCreated() == FROM_PARSER_NETWORK &&
       !ReadyToExecuteScripts()) {
     NS_ASSERTION(!mParserBlockingRequest,
         "There can be only one parser-blocking script at a time");
     mParserBlockingRequest = request;
-    NS_ASSERTION(mXSLTRequests.IsEmpty(),
+    NS_ASSERTION(mXSLTRequests.isEmpty(),
         "Parser-blocking scripts and XSLT scripts in the same doc!");
     return true;
   }
   // We now have a document.written inline script or we have an inline script
   // from the network but there is no style sheet that is blocking scripts.
   // Don't check for style sheets blocking scripts in the document.write
   // case to avoid style sheet network activity affecting when
   // document.write returns. It's not really necessary to do this if
@@ -1189,63 +1173,55 @@ nsScriptLoader::ProcessPendingRequests()
       ReadyToExecuteScripts()) {
     request.swap(mParserBlockingRequest);
     UnblockParser(request);
     ProcessRequest(request);
     ContinueParserAsync(request);
   }
 
   while (ReadyToExecuteScripts() && 
-         !mXSLTRequests.IsEmpty() && 
-         !mXSLTRequests[0]->mLoading) {
-    request.swap(mXSLTRequests[0]);
-    mXSLTRequests.RemoveElementAt(0);
+         !mXSLTRequests.isEmpty() &&
+         !mXSLTRequests.getFirst()->mLoading) {
+    request = mXSLTRequests.StealFirst();
     ProcessRequest(request);
   }
 
-  uint32_t i = 0;
-  while (mEnabled && i < mAsyncRequests.Length()) {
-    if (!mAsyncRequests[i]->mLoading) {
-      request.swap(mAsyncRequests[i]);
-      mAsyncRequests.RemoveElementAt(i);
-      ProcessRequest(request);
-      continue;
-    }
-    ++i;
+  while (mEnabled && !mLoadedAsyncRequests.isEmpty()) {
+    request = mLoadedAsyncRequests.StealFirst();
+    ProcessRequest(request);
   }
 
-  while (mEnabled && !mNonAsyncExternalScriptInsertedRequests.IsEmpty() &&
-         !mNonAsyncExternalScriptInsertedRequests[0]->mLoading) {
+  while (mEnabled && !mNonAsyncExternalScriptInsertedRequests.isEmpty() &&
+         !mNonAsyncExternalScriptInsertedRequests.getFirst()->mLoading) {
     // Violate the HTML5 spec and execute these in the insertion order in
     // order to make LABjs and the "order" plug-in for RequireJS work with
     // their Gecko-sniffed code path. See
     // http://lists.w3.org/Archives/Public/public-html/2010Oct/0088.html
-    request.swap(mNonAsyncExternalScriptInsertedRequests[0]);
-    mNonAsyncExternalScriptInsertedRequests.RemoveElementAt(0);
+    request = mNonAsyncExternalScriptInsertedRequests.StealFirst();
     ProcessRequest(request);
   }
 
-  if (mDocumentParsingDone && mXSLTRequests.IsEmpty()) {
-    while (!mDeferRequests.IsEmpty() && !mDeferRequests[0]->mLoading) {
-      request.swap(mDeferRequests[0]);
-      mDeferRequests.RemoveElementAt(0);
+  if (mDocumentParsingDone && mXSLTRequests.isEmpty()) {
+    while (!mDeferRequests.isEmpty() && !mDeferRequests.getFirst()->mLoading) {
+      request = mDeferRequests.StealFirst();
       ProcessRequest(request);
     }
   }
 
   while (!mPendingChildLoaders.IsEmpty() && ReadyToExecuteScripts()) {
     nsRefPtr<nsScriptLoader> child = mPendingChildLoaders[0];
     mPendingChildLoaders.RemoveElementAt(0);
     child->RemoveExecuteBlocker();
   }
 
   if (mDocumentParsingDone && mDocument &&
-      !mParserBlockingRequest && mAsyncRequests.IsEmpty() &&
-      mNonAsyncExternalScriptInsertedRequests.IsEmpty() &&
-      mXSLTRequests.IsEmpty() && mDeferRequests.IsEmpty()) {
+      !mParserBlockingRequest && mLoadingAsyncRequests.isEmpty() &&
+      mLoadedAsyncRequests.isEmpty() &&
+      mNonAsyncExternalScriptInsertedRequests.isEmpty() &&
+      mXSLTRequests.isEmpty() && mDeferRequests.isEmpty()) {
     if (MaybeRemovedDeferRequests()) {
       return ProcessPendingRequests();
     }
     // No more pending scripts; time to unblock onload.
     // OK to unblock onload synchronously here, since callers must be
     // prepared for the world changing anyway.
     mDocumentParsingDone = false;
     mDocument->UnblockOnload(true);
@@ -1441,21 +1417,37 @@ nsScriptLoader::OnStreamComplete(nsIStre
      * We make a note of this script node by including it in a dedicated
      * array of blocked tracking nodes under its parent document.
      */
     if (rv == NS_ERROR_TRACKING_URI) {
       nsCOMPtr<nsIContent> cont = do_QueryInterface(request->mElement);
       mDocument->AddBlockedTrackingNode(cont);
     }
 
-    if (mDeferRequests.RemoveElement(request) ||
-        mAsyncRequests.RemoveElement(request) ||
-        mNonAsyncExternalScriptInsertedRequests.RemoveElement(request) ||
-        mXSLTRequests.RemoveElement(request)) {
-      FireScriptAvailable(rv, request);
+    if (request->mIsDefer) {
+      if (request->isInList()) {
+        nsRefPtr<nsScriptLoadRequest> req = mDeferRequests.Steal(request);
+        FireScriptAvailable(rv, req);
+      }
+    } else if (request->mIsAsync) {
+      if (request->isInList()) {
+        nsRefPtr<nsScriptLoadRequest> req = mLoadingAsyncRequests.Steal(request);
+        FireScriptAvailable(rv, req);
+      }
+    } else if (request->mIsNonAsyncScriptInserted) {
+      if (request->isInList()) {
+        nsRefPtr<nsScriptLoadRequest> req =
+          mNonAsyncExternalScriptInsertedRequests.Steal(request);
+        FireScriptAvailable(rv, req);
+      }
+    } else if (request->mIsXSLT) {
+      if (request->isInList()) {
+        nsRefPtr<nsScriptLoadRequest> req = mXSLTRequests.Steal(request);
+        FireScriptAvailable(rv, req);
+      }
     } else if (mParserBlockingRequest == request) {
       mParserBlockingRequest = nullptr;
       UnblockParser(request);
       FireScriptAvailable(rv, request);
       ContinueParserAsync(request);
     } else {
       mPreloads.RemoveElement(request, PreloadRequestComparator());
     }
@@ -1549,41 +1541,48 @@ nsScriptLoader::PrepareLoadedRequest(nsS
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // This assertion could fire errorously if we ran out of memory when
   // inserting the request in the array. However it's an unlikely case
   // so if you see this assertion it is likely something else that is
   // wrong, especially if you see it more than once.
   NS_ASSERTION(mDeferRequests.Contains(aRequest) ||
-               mAsyncRequests.Contains(aRequest) ||
+               mLoadingAsyncRequests.Contains(aRequest) ||
                mNonAsyncExternalScriptInsertedRequests.Contains(aRequest) ||
                mXSLTRequests.Contains(aRequest)  ||
                mPreloads.Contains(aRequest, PreloadRequestComparator()) ||
                mParserBlockingRequest,
                "aRequest should be pending!");
 
   // Mark this as loaded
   aRequest->mLoading = false;
 
+  // And if it's async, move it to the loaded list.
+  if (aRequest->mIsAsync) {
+    nsRefPtr<nsScriptLoadRequest> req = mLoadingAsyncRequests.Steal(aRequest);
+    mLoadedAsyncRequests.AppendElement(req);
+  }
+
   return NS_OK;
 }
 
 void
 nsScriptLoader::ParsingComplete(bool aTerminated)
 {
   if (mDeferEnabled) {
     // Have to check because we apparently get ParsingComplete
     // without BeginDeferringScripts in some cases
     mDocumentParsingDone = true;
   }
   mDeferEnabled = false;
   if (aTerminated) {
     mDeferRequests.Clear();
-    mAsyncRequests.Clear();
+    mLoadingAsyncRequests.Clear();
+    mLoadedAsyncRequests.Clear();
     mNonAsyncExternalScriptInsertedRequests.Clear();
     mXSLTRequests.Clear();
     mParserBlockingRequest = nullptr;
   }
 
   // Have to call this even if aTerminated so we'll correctly unblock
   // onload and all.
   ProcessPendingRequests();
@@ -1617,28 +1616,29 @@ nsScriptLoader::PreloadURI(nsIURI *aURI,
   PreloadInfo *pi = mPreloads.AppendElement();
   pi->mRequest = request;
   pi->mCharset = aCharset;
 }
 
 void
 nsScriptLoader::AddDeferRequest(nsScriptLoadRequest* aRequest)
 {
+  aRequest->mIsDefer = true;
   mDeferRequests.AppendElement(aRequest);
-  if (mDeferEnabled && mDeferRequests.Length() == 1 && mDocument &&
-      !mBlockingDOMContentLoaded) {
+  if (mDeferEnabled && aRequest == mDeferRequests.getFirst() &&
+      mDocument && !mBlockingDOMContentLoaded) {
     MOZ_ASSERT(mDocument->GetReadyStateEnum() == nsIDocument::READYSTATE_LOADING);
     mBlockingDOMContentLoaded = true;
     mDocument->BlockDOMContentLoaded();
   }
 }
 
 bool
 nsScriptLoader::MaybeRemovedDeferRequests()
 {
-  if (mDeferRequests.Length() == 0 && mDocument &&
+  if (mDeferRequests.isEmpty() && mDocument &&
       mBlockingDOMContentLoaded) {
     mBlockingDOMContentLoaded = false;
     mDocument->UnblockDOMContentLoaded();
     return true;
   }
   return false;
 }
--- a/dom/base/nsScriptLoader.h
+++ b/dom/base/nsScriptLoader.h
@@ -12,32 +12,150 @@
 
 #include "nsCOMPtr.h"
 #include "nsIScriptElement.h"
 #include "nsCOMArray.h"
 #include "nsTArray.h"
 #include "nsAutoPtr.h"
 #include "nsIDocument.h"
 #include "nsIStreamLoader.h"
+#include "mozilla/CORSMode.h"
+#include "mozilla/LinkedList.h"
 #include "mozilla/net/ReferrerPolicy.h"
 
-class nsScriptLoadRequest;
+class nsScriptLoadRequestList;
 class nsIURI;
 
 namespace JS {
   class SourceBufferHolder;
 }
 
 namespace mozilla {
 namespace dom {
 class AutoJSAPI;
 }
 }
 
 //////////////////////////////////////////////////////////////
+// Per-request data structure
+//////////////////////////////////////////////////////////////
+
+class nsScriptLoadRequest final : public nsISupports,
+                                  private mozilla::LinkedListElement<nsScriptLoadRequest>
+{
+  ~nsScriptLoadRequest()
+  {
+    js_free(mScriptTextBuf);
+  }
+
+  typedef LinkedListElement<nsScriptLoadRequest> super;
+
+  // Allow LinkedListElement<nsScriptLoadRequest> to cast us to itself as needed.
+  friend class mozilla::LinkedListElement<nsScriptLoadRequest>;
+  friend class nsScriptLoadRequestList;
+
+public:
+  nsScriptLoadRequest(nsIScriptElement* aElement,
+                      uint32_t aVersion,
+                      mozilla::CORSMode aCORSMode)
+    : mElement(aElement),
+      mLoading(true),
+      mIsInline(true),
+      mHasSourceMapURL(false),
+      mIsDefer(false),
+      mIsAsync(false),
+      mIsNonAsyncScriptInserted(false),
+      mIsXSLT(false),
+      mScriptTextBuf(nullptr),
+      mScriptTextLength(0),
+      mJSVersion(aVersion),
+      mLineNo(1),
+      mCORSMode(aCORSMode),
+      mReferrerPolicy(mozilla::net::RP_Default)
+  {
+  }
+
+  NS_DECL_THREADSAFE_ISUPPORTS
+
+  void FireScriptAvailable(nsresult aResult)
+  {
+    mElement->ScriptAvailable(aResult, mElement, mIsInline, mURI, mLineNo);
+  }
+  void FireScriptEvaluated(nsresult aResult)
+  {
+    mElement->ScriptEvaluated(aResult, mElement, mIsInline);
+  }
+
+  bool IsPreload()
+  {
+    return mElement == nullptr;
+  }
+
+  using super::getNext;
+  using super::isInList;
+
+  nsCOMPtr<nsIScriptElement> mElement;
+  bool mLoading;          // Are we still waiting for a load to complete?
+  bool mIsInline;         // Is the script inline or loaded?
+  bool mHasSourceMapURL;  // Does the HTTP header have a source map url?
+  bool mIsDefer;          // True if we live in mDeferRequests.
+  bool mIsAsync;          // True if we live in mLoadingAsyncRequests or mLoadedAsyncRequests.
+  bool mIsNonAsyncScriptInserted; // True if we live in mNonAsyncExternalScriptInsertedRequests
+  bool mIsXSLT;           // True if we live in mXSLTRequests.
+  nsString mSourceMapURL; // Holds source map url for loaded scripts
+  char16_t* mScriptTextBuf; // Holds script text for non-inline scripts. Don't
+  size_t mScriptTextLength; // use nsString so we can give ownership to jsapi.
+  uint32_t mJSVersion;
+  nsCOMPtr<nsIURI> mURI;
+  nsCOMPtr<nsIPrincipal> mOriginPrincipal;
+  nsAutoCString mURL;   // Keep the URI's filename alive during off thread parsing.
+  int32_t mLineNo;
+  const mozilla::CORSMode mCORSMode;
+  mozilla::net::ReferrerPolicy mReferrerPolicy;
+};
+
+class nsScriptLoadRequestList : private mozilla::LinkedList<nsScriptLoadRequest>
+{
+  typedef mozilla::LinkedList<nsScriptLoadRequest> super;
+
+public:
+  ~nsScriptLoadRequestList();
+
+  void Clear();
+
+#ifdef DEBUG
+  bool Contains(nsScriptLoadRequest* aElem);
+#endif // DEBUG
+
+  using super::getFirst;
+  using super::isEmpty;
+
+  void AppendElement(nsScriptLoadRequest* aElem)
+  {
+    MOZ_ASSERT(!aElem->isInList());
+    NS_ADDREF(aElem);
+    insertBack(aElem);
+  }
+
+  MOZ_WARN_UNUSED_RESULT
+  already_AddRefed<nsScriptLoadRequest> Steal(nsScriptLoadRequest* aElem)
+  {
+    aElem->removeFrom(*this);
+    return dont_AddRef(aElem);
+  }
+
+  MOZ_WARN_UNUSED_RESULT
+  already_AddRefed<nsScriptLoadRequest> StealFirst()
+  {
+    MOZ_ASSERT(!isEmpty());
+    return Steal(getFirst());
+  }
+};
+
+//////////////////////////////////////////////////////////////
 // Script loader implementation
 //////////////////////////////////////////////////////////////
 
 class nsScriptLoader final : public nsIStreamLoaderObserver
 {
   class MOZ_STACK_CLASS AutoCurrentScriptUpdater
   {
   public:
@@ -331,20 +449,23 @@ private:
                                 uint32_t aStringLen,
                                 const uint8_t* aString);
 
   void AddDeferRequest(nsScriptLoadRequest* aRequest);
   bool MaybeRemovedDeferRequests();
 
   nsIDocument* mDocument;                   // [WEAK]
   nsCOMArray<nsIScriptLoaderObserver> mObservers;
-  nsTArray<nsRefPtr<nsScriptLoadRequest> > mNonAsyncExternalScriptInsertedRequests;
-  nsTArray<nsRefPtr<nsScriptLoadRequest> > mAsyncRequests;
-  nsTArray<nsRefPtr<nsScriptLoadRequest> > mDeferRequests;
-  nsTArray<nsRefPtr<nsScriptLoadRequest> > mXSLTRequests;
+  nsScriptLoadRequestList mNonAsyncExternalScriptInsertedRequests;
+  // mLoadingAsyncRequests holds async requests while they're loading; when they
+  // have been loaded they are moved to mLoadedAsyncRequests.
+  nsScriptLoadRequestList mLoadingAsyncRequests;
+  nsScriptLoadRequestList mLoadedAsyncRequests;
+  nsScriptLoadRequestList mDeferRequests;
+  nsScriptLoadRequestList mXSLTRequests;
   nsRefPtr<nsScriptLoadRequest> mParserBlockingRequest;
 
   // In mRequests, the additional information here is stored by the element.
   struct PreloadInfo {
     nsRefPtr<nsScriptLoadRequest> mRequest;
     nsString mCharset;
   };
 
--- a/dom/base/test/test_innersize_scrollport.html
+++ b/dom/base/test/test_innersize_scrollport.html
@@ -22,20 +22,25 @@ function run()
   var newWidth = oldWidth / 2;
   var newHeight = oldHeight / 2;
 
   var utils = SpecialPowers.getDOMWindowUtils(window);
   utils.setScrollPositionClampingScrollPortSize(newWidth, newHeight);
   is(window.innerWidth, newWidth, "innerWidth not updated to scroll port width");
   is(window.innerHeight, newHeight, "innerHeight not updated to scroll port height");
 
+  var innerWidthGetter = Object.getOwnPropertyDescriptor(window, "innerWidth").get;
+  var innerHeightGetter = Object.getOwnPropertyDescriptor(window, "innerHeight").get;
+
   window.innerWidth = oldWidth;
   window.innerHeight = oldHeight;
-  is(window.innerWidth, newWidth, "innerWidth clobbered by direct set");
-  is(window.innerHeight, newHeight, "innerHeight clobbered by direct set");
+  is(window.innerWidth, oldWidth, "Should have redefined innerWidth");
+  is(window.innerHeight, oldHeight, "Should have redefined innerWidth");
+  is(innerWidthGetter.call(window), newWidth, "innerWidth clobbered by direct set");
+  is(innerHeightGetter.call(window), newHeight, "innerHeight clobbered by direct set");
 
   SimpleTest.finish();
 }
 
 window.addEventListener("load", run, false);
 
 </script>
 </pre>
--- a/dom/base/test/test_writable-replaceable.html
+++ b/dom/base/test/test_writable-replaceable.html
@@ -9,39 +9,40 @@
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=823283">Mozilla Bug 823283</a>
 <p id="display"></p>
 <div id="content" style="display: none"></div>
 <pre id="test">
 <script type="application/javascript">
 /** Test for Bug 823283 **/
 
-function createTest(prop, typeStr, valCode)
+function createTest(prop, typeStr, valCode, replaceable)
 {
+  var newType = replaceable ? typeof(valCode) : typeStr;
   var code =
     'is(typeof ' + prop + ', "' + typeStr + '", "' + prop + ': bad unqualified before-state");\n' +
     'is(typeof window.' + prop + ', "' + typeStr + '", "' + prop + ': bad qualified before-state");\n' +
     '\n' +
     prop + ' = ' + valCode + ';\n' +
     '\n' +
-    'is(typeof ' + prop + ', "' + typeStr + '", "' + prop + ': bad unqualified after-state");\n' +
-    'is(typeof window.' + prop + ', "' + typeStr + '", "' + prop + ': bad qualified after-state");';
+    'is(typeof ' + prop + ', "' + newType + '", "' + prop + ': bad unqualified after-state");\n' +
+    'is(typeof window.' + prop + ', "' + newType + '", "' + prop + ': bad qualified after-state");';
 
   return Function(code);
 }
 
 [
-  ["innerHeight", "number", '"123"'],
-  ["innerWidth", "number", '"456"'],
-  ["outerHeight", "number", '"654"'],
-  ["outerWidth", "number", '"321"'],
-  ["screenX", "number", '"17"'],
-  ["screenY", "number", '"42"'],
-  ["status", "string", '{}'],
-  ["name", "string", '{}'],
+  ["innerHeight", "number", '"123"', true],
+  ["innerWidth", "number", '"456"', true],
+  ["outerHeight", "number", '"654"', true],
+  ["outerWidth", "number", '"321"', true],
+  ["screenX", "number", '"17"', true],
+  ["screenY", "number", '"42"', true],
+  ["status", "string", '{}', false],
+  ["name", "string", '{}', false],
 ].forEach(function(args)
 {
   createTest.apply(null, args)();
 });
 
 </script>
 </pre>
 </body>
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -8,16 +8,17 @@
 
 #include <algorithm>
 #include <stdarg.h>
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/unused.h"
 
 #include "AccessCheck.h"
 #include "jsfriendapi.h"
 #include "nsContentUtils.h"
 #include "nsGlobalWindow.h"
 #include "nsIDocShell.h"
 #include "nsIDOMGlobalPropertyInitializer.h"
 #include "nsIPermissionManager.h"
@@ -41,16 +42,17 @@
 #include "mozilla/dom/HTMLSharedObjectElement.h"
 #include "mozilla/dom/HTMLEmbedElementBinding.h"
 #include "mozilla/dom/HTMLAppletElementBinding.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ResolveSystemBinding.h"
 #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
 #include "WorkerPrivate.h"
 #include "nsDOMClassInfo.h"
+#include "ipc/ErrorIPCUtils.h"
 
 namespace mozilla {
 namespace dom {
 
 JSErrorFormatString ErrorFormatString[] = {
 #define MSG_DEF(_name, _argc, _exn, _str) \
   { _str, _argc, _exn },
 #include "mozilla/dom/Errors.msg"
@@ -145,16 +147,41 @@ ErrorResult::ThrowErrorWithMessage(va_li
   argCount = std::min<uint16_t>(argCount, 10);
   while (argCount--) {
     message->mArgs.AppendElement(*va_arg(ap, nsString*));
   }
   mMessage = message;
 }
 
 void
+ErrorResult::SerializeMessage(IPC::Message* aMsg) const
+{
+  using namespace IPC;
+  MOZ_ASSERT(mMessage);
+  WriteParam(aMsg, mMessage->mArgs);
+  WriteParam(aMsg, mMessage->mErrorNumber);
+}
+
+bool
+ErrorResult::DeserializeMessage(const IPC::Message* aMsg, void** aIter)
+{
+  using namespace IPC;
+  nsAutoPtr<Message> readMessage(new Message());
+  if (!ReadParam(aMsg, aIter, &readMessage->mArgs) ||
+      !ReadParam(aMsg, aIter, &readMessage->mErrorNumber)) {
+    return false;
+  }
+  if (mMessage) {
+    delete mMessage;
+  }
+  mMessage = readMessage.forget();
+  return true;
+}
+
+void
 ErrorResult::ThrowTypeError(const dom::ErrNum errorNumber, ...)
 {
   va_list ap;
   va_start(ap, errorNumber);
   ThrowErrorWithMessage(ap, errorNumber, NS_ERROR_TYPE_ERR);
   va_end(ap);
 }
 
@@ -296,16 +323,47 @@ ErrorResult::ReportNotEnoughArgsError(JS
                                       const char* memberName)
 {
   MOZ_ASSERT(ErrorCode() == NS_ERROR_XPC_NOT_ENOUGH_ARGS);
 
   nsPrintfCString errorMessage("%s.%s", ifaceName, memberName);
   ThrowErrorMessage(cx, dom::MSG_MISSING_ARGUMENTS, errorMessage.get());
 }
 
+ErrorResult&
+ErrorResult::operator=(ErrorResult&& aRHS)
+{
+#ifdef DEBUG
+  mMightHaveUnreportedJSException = aRHS.mMightHaveUnreportedJSException;
+  aRHS.mMightHaveUnreportedJSException = false;
+#endif
+  if (aRHS.IsErrorWithMessage()) {
+    mMessage = aRHS.mMessage;
+    aRHS.mMessage = nullptr;
+  } else if (aRHS.IsJSException()) {
+    JSContext* cx = nsContentUtils::GetDefaultJSContextForThread();
+    MOZ_ASSERT(cx);
+    mJSException.setUndefined();
+    if (!js::AddRawValueRoot(cx, &mJSException, "ErrorResult::mJSException")) {
+      MOZ_CRASH("Could not root mJSException, we're about to OOM");
+    }
+    mJSException = aRHS.mJSException;
+    aRHS.mJSException.setUndefined();
+    js::RemoveRawValueRoot(cx, &aRHS.mJSException);
+  } else {
+    // Null out the union on both sides for hygiene purposes.
+    mMessage = aRHS.mMessage = nullptr;
+  }
+  // Note: It's important to do this last, since this affects the condition
+  // checks above!
+  mResult = aRHS.mResult;
+  aRHS.mResult = NS_OK;
+  return *this;
+}
+
 namespace dom {
 
 bool
 DefineConstants(JSContext* cx, JS::Handle<JSObject*> obj,
                 const ConstantSpec* cs)
 {
   JS::Rooted<JS::Value> value(cx);
   for (; cs->name; ++cs) {
new file mode 100644
--- /dev/null
+++ b/dom/bindings/ErrorIPCUtils.h
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
+/* vim: set ts=2 sw=2 et tw=79: */
+/* 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 "ipc/IPCMessageUtils.h"
+#include "mozilla/ErrorResult.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Move.h"
+
+#ifndef IPC_ErrorIPCUtils_h
+#define IPC_ErrorIPCUtils_h
+
+namespace IPC {
+
+template<>
+struct ParamTraits<mozilla::dom::ErrNum> :
+  public ContiguousEnumSerializer<mozilla::dom::ErrNum,
+                                  mozilla::dom::ErrNum(0),
+                                  mozilla::dom::ErrNum(mozilla::dom::Err_Limit)> {};
+
+template<>
+struct ParamTraits<mozilla::ErrorResult>
+{
+  typedef mozilla::ErrorResult paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    // It should be the case that mMightHaveUnreportedJSException can only be
+    // true when we're expecting a JS exception.  We cannot send such messages
+    // over the IPC channel since there is no sane way of transferring the JS
+    // value over to the other side.  Callers should never do that.
+    MOZ_ASSERT_IF(aParam.IsJSException(), aParam.mMightHaveUnreportedJSException);
+    if (aParam.IsJSException()
+#ifdef DEBUG
+        || aParam.mMightHaveUnreportedJSException
+#endif
+        ) {
+      MOZ_CRASH("Cannot encode an ErrorResult representing a Javascript exception");
+    }
+
+    WriteParam(aMsg, aParam.mResult);
+    WriteParam(aMsg, aParam.IsErrorWithMessage());
+    if (aParam.IsErrorWithMessage()) {
+      aParam.SerializeMessage(aMsg);
+    }
+  }
+
+  static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
+  {
+    paramType readValue;
+    if (!ReadParam(aMsg, aIter, &readValue.mResult)) {
+      return false;
+    }
+    bool hasMessage = false;
+    if (!ReadParam(aMsg, aIter, &hasMessage)) {
+      return false;
+    }
+    if (hasMessage && !readValue.DeserializeMessage(aMsg, aIter)) {
+      return false;
+    }
+    *aResult = Move(readValue);
+    return true;
+  }
+};
+
+}
+
+#endif
--- a/dom/bindings/ErrorResult.h
+++ b/dom/bindings/ErrorResult.h
@@ -12,16 +12,22 @@
 #define mozilla_ErrorResult_h
 
 #include <stdarg.h>
 
 #include "js/Value.h"
 #include "nscore.h"
 #include "nsStringGlue.h"
 #include "mozilla/Assertions.h"
+#include "mozilla/Move.h"
+
+namespace IPC {
+class Message;
+template <typename> struct ParamTraits;
+}
 
 namespace mozilla {
 
 namespace dom {
 
 enum ErrNum {
 #define MSG_DEF(_name, _argc, _exn, _str) \
   _name,
@@ -51,16 +57,22 @@ public:
 
 #ifdef DEBUG
   ~ErrorResult() {
     MOZ_ASSERT_IF(IsErrorWithMessage(), !mMessage);
     MOZ_ASSERT(!mMightHaveUnreportedJSException);
   }
 #endif
 
+  ErrorResult(ErrorResult&& aRHS)
+  {
+    *this = Move(aRHS);
+  }
+  ErrorResult& operator=(ErrorResult&& aRHS);
+
   void Throw(nsresult rv) {
     MOZ_ASSERT(NS_FAILED(rv), "Please don't try throwing success");
     MOZ_ASSERT(rv != NS_ERROR_TYPE_ERR, "Use ThrowTypeError()");
     MOZ_ASSERT(rv != NS_ERROR_RANGE_ERR, "Use ThrowRangeError()");
     MOZ_ASSERT(!IsErrorWithMessage(), "Don't overwrite errors with message");
     MOZ_ASSERT(rv != NS_ERROR_DOM_JS_EXCEPTION, "Use ThrowJSException()");
     MOZ_ASSERT(!IsJSException(), "Don't overwrite JS exceptions");
     MOZ_ASSERT(rv != NS_ERROR_XPC_NOT_ENOUGH_ARGS, "Use ThrowNotEnoughArgsError()");
@@ -156,25 +168,30 @@ private:
   // ReportErrorWithMessage.
   // mJSException is set (and rooted) by ThrowJSException and unrooted
   // by ReportJSException.
   union {
     Message* mMessage; // valid when IsErrorWithMessage()
     JS::Value mJSException; // valid when IsJSException()
   };
 
+  friend struct IPC::ParamTraits<ErrorResult>;
+  void SerializeMessage(IPC::Message* aMsg) const;
+  bool DeserializeMessage(const IPC::Message* aMsg, void** aIter);
+
 #ifdef DEBUG
   // Used to keep track of codepaths that might throw JS exceptions,
   // for assertion purposes.
   bool mMightHaveUnreportedJSException;
 #endif
 
   // Not to be implemented, to make sure people always pass this by
   // reference, not by value.
   ErrorResult(const ErrorResult&) = delete;
+  void operator=(const ErrorResult&) = delete;
   void ThrowErrorWithMessage(va_list ap, const dom::ErrNum errorNumber,
                              nsresult errorType);
 };
 
 /******************************************************************************
  ** Macros for checking results
  ******************************************************************************/
 
--- a/dom/bindings/ToJSValue.h
+++ b/dom/bindings/ToJSValue.h
@@ -55,36 +55,27 @@ ToJSValue(JSContext* aCx,
 {
   // Make sure we're called in a compartment
   MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
 
   aValue.setInt32(aArgument);
   return true;
 }
 
-// The uint32_t version is disabled for now because on the super-old b2g
-// compiler nsresult and uint32_t are the same type.  If someone needs this at
-// some point we'll need to figure out how to make it work (e.g. by switching to
-// traits structs and using the trick IPC's ParamTraits uses, where a traits
-// struct templated on the type inherits from a base traits struct of some sort,
-// templated on the same type, or something).  Maybe b2g will update to a modern
-// compiler before that happens....
-#if 0
 inline bool
 ToJSValue(JSContext* aCx,
           uint32_t aArgument,
           JS::MutableHandle<JS::Value> aValue)
 {
   // Make sure we're called in a compartment
   MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
 
   aValue.setNumber(aArgument);
   return true;
 }
-#endif
 
 inline bool
 ToJSValue(JSContext* aCx,
           int64_t aArgument,
           JS::MutableHandle<JS::Value> aValue)
 {
   // Make sure we're called in a compartment
   MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
--- a/dom/bindings/moz.build
+++ b/dom/bindings/moz.build
@@ -1,16 +1,20 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 TEST_DIRS += ['test']
 
+EXPORTS.ipc += [
+    'ErrorIPCUtils.h',
+]
+
 EXPORTS.mozilla += [
     'ErrorResult.h',
 ]
 
 EXPORTS.mozilla.dom += [
     'AtomList.h',
     'BindingDeclarations.h',
     'BindingUtils.h',
--- a/dom/canvas/WebGLFramebuffer.cpp
+++ b/dom/canvas/WebGLFramebuffer.cpp
@@ -824,23 +824,22 @@ WebGLFramebuffer::CheckAndInitializeAtta
 
 void WebGLFramebuffer::EnsureColorAttachPoints(size_t colorAttachmentId)
 {
     MOZ_ASSERT(colorAttachmentId < WebGLContext::kMaxColorAttachments);
 
     if (colorAttachmentId < ColorAttachmentCount())
         return;
 
-    size_t colorAttachmentCount = ColorAttachmentCount();
-    while (colorAttachmentCount < WebGLContext::kMaxColorAttachments) {
-        GLenum nextAttachPoint = LOCAL_GL_COLOR_ATTACHMENT0 + colorAttachmentCount;
+    while (ColorAttachmentCount() < WebGLContext::kMaxColorAttachments) {
+        GLenum nextAttachPoint = LOCAL_GL_COLOR_ATTACHMENT0 + ColorAttachmentCount();
         mMoreColorAttachments.AppendElement(AttachPoint(this, nextAttachPoint));
     }
 
-    MOZ_ASSERT(colorAttachmentCount == ColorAttachmentCount());
+    MOZ_ASSERT(ColorAttachmentCount() == WebGLContext::kMaxColorAttachments);
 }
 
 static void
 FinalizeDrawAndReadBuffers(gl::GLContext* gl, bool isColorBufferDefined)
 {
     MOZ_ASSERT(gl, "Expected a valid GLContext ptr.");
     // GLES don't support DrawBuffer()/ReadBuffer.
     // According to http://www.opengl.org/wiki/Framebuffer_Object
--- a/dom/crypto/test/test-array.js
+++ b/dom/crypto/test/test-array.js
@@ -170,19 +170,17 @@ if (window.addEventListener) {
 function start() {
   TestArray.run();
 }
 
 MOCHITEST = ("SimpleTest" in window);
 if (MOCHITEST) {
   SimpleTest.waitForExplicitFinish();
   window.addEventListener("load", function() {
-    SimpleTest.waitForFocus(function() {
-      SpecialPowers.pushPrefEnv({'set': [["dom.webcrypto.enabled", true]]}, start);
-    });
+    SimpleTest.waitForFocus(start);
   });
 }
 
 function error(test) {
   return function(x) {
     console.log("ERROR :: " + x);
     test.complete(false);
     throw x;
--- a/dom/events/DataTransfer.cpp
+++ b/dom/events/DataTransfer.cpp
@@ -19,16 +19,17 @@
 #include "nsIClipboard.h"
 #include "nsContentUtils.h"
 #include "nsIContent.h"
 #include "nsCRT.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsIScriptContext.h"
 #include "nsIDocument.h"
 #include "nsIScriptGlobalObject.h"
+#include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/DataTransferBinding.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/BindingUtils.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(DataTransfer)
@@ -77,17 +78,16 @@ DataTransfer::DataTransfer(nsISupports* 
     mReadOnly(true),
     mIsExternal(aIsExternal),
     mUserCancelled(false),
     mIsCrossDomainSubFrameDrop(false),
     mClipboardType(aClipboardType),
     mDragImageX(0),
     mDragImageY(0)
 {
-  MOZ_ASSERT(mParent);
   // For these events, we want to be able to add data to the data transfer, so
   // clear the readonly state. Otherwise, the data is already present. For
   // external usage, cache the data from the native clipboard or drag.
   if (aEventType == NS_CUT ||
       aEventType == NS_COPY ||
       aEventType == NS_DRAGDROP_START ||
       aEventType == NS_DRAGDROP_GESTURE) {
     mReadOnly = false;
@@ -295,20 +295,26 @@ DataTransfer::GetFiles(ErrorResult& aRv)
       nsCOMPtr<nsISupports> supports;
       nsresult rv = variant->GetAsISupports(getter_AddRefs(supports));
 
       if (NS_FAILED(rv))
         continue;
 
       nsCOMPtr<nsIFile> file = do_QueryInterface(supports);
 
-      if (!file)
-        continue;
-
-      nsRefPtr<File> domFile = File::CreateFromFile(GetParentObject(), file);
+      nsRefPtr<File> domFile;
+      if (file) {
+        domFile = File::CreateFromFile(GetParentObject(), file);
+      } else {
+        nsCOMPtr<FileImpl> fileImpl = do_QueryInterface(supports);
+        if (!fileImpl) {
+          continue;
+        }
+        domFile = new File(GetParentObject(), static_cast<FileImpl*>(fileImpl.get()));
+      }
 
       if (!mFiles->Append(domFile)) {
         aRv.Throw(NS_ERROR_FAILURE);
         return nullptr;
       }
     }
   }
 
@@ -850,38 +856,42 @@ DataTransfer::Clone(nsISupports* aParent
   return NS_OK;
 }
 
 already_AddRefed<nsISupportsArray>
 DataTransfer::GetTransferables(nsIDOMNode* aDragTarget)
 {
   MOZ_ASSERT(aDragTarget);
 
-  nsCOMPtr<nsISupportsArray> transArray =
-    do_CreateInstance("@mozilla.org/supports-array;1");
-  if (!transArray) {
-    return nullptr;
-  }
-    
-
   nsCOMPtr<nsINode> dragNode = do_QueryInterface(aDragTarget);
   if (!dragNode) {
     return nullptr;
   }
     
   nsIDocument* doc = dragNode->GetCurrentDoc();
   if (!doc) {
     return nullptr;
   }
-    
-  nsILoadContext* loadContext = doc->GetLoadContext();
+
+  return GetTransferables(doc->GetLoadContext());
+}
+
+already_AddRefed<nsISupportsArray>
+DataTransfer::GetTransferables(nsILoadContext* aLoadContext)
+{
+
+  nsCOMPtr<nsISupportsArray> transArray =
+    do_CreateInstance("@mozilla.org/supports-array;1");
+  if (!transArray) {
+    return nullptr;
+  }
 
   uint32_t count = mItems.Length();
   for (uint32_t i = 0; i < count; i++) {
-    nsCOMPtr<nsITransferable> transferable = GetTransferable(i, loadContext);
+    nsCOMPtr<nsITransferable> transferable = GetTransferable(i, aLoadContext);
     if (transferable) {
       transArray->AppendElement(transferable);
     }
   }
 
   return transArray.forget();
 }
 
@@ -1248,10 +1258,25 @@ DataTransfer::FillInExternalData(Transfe
     }
     else {
       variant->SetAsISupports(data);
     }
 
     aItem.mData = variant;
   }
 
+void
+DataTransfer::FillAllExternalData()
+{
+  if (mIsExternal) {
+    for (uint32_t i = 0; i < mItems.Length(); ++i) {
+      nsTArray<TransferItem>& itemArray = mItems[i];
+      for (uint32_t j = 0; j < itemArray.Length(); ++j) {
+        if (!itemArray[j].mData) {
+          FillInExternalData(itemArray[j], i);
+        }
+      }
+    }
+  }
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/events/DataTransfer.h
+++ b/dom/events/DataTransfer.h
@@ -180,16 +180,17 @@ public:
 
   // a readonly dataTransfer cannot have new data added or existing data removed.
   // Only the dropEffect and effectAllowed may be modified.
   void SetReadOnly() { mReadOnly = true; }
 
   // converts the data into an array of nsITransferable objects to be used for
   // drag and drop or clipboard operations.
   already_AddRefed<nsISupportsArray> GetTransferables(nsIDOMNode* aDragTarget);
+  already_AddRefed<nsISupportsArray> GetTransferables(nsILoadContext* aLoadContext);
 
   // converts the data for a single item at aIndex into an nsITransferable object.
   already_AddRefed<nsITransferable> GetTransferable(uint32_t aIndex,
                                                     nsILoadContext* aLoadContext);
 
   // converts the data in the variant to an nsISupportString if possible or
   // an nsISupports or null otherwise.
   bool ConvertFromVariant(nsIVariant* aVariant,
@@ -234,16 +235,19 @@ protected:
 
   // caches the formats that exist in the clipboard
   void CacheExternalClipboardFormats();
 
   // fills in the data field of aItem with the data from the drag service or
   // clipboard for a given index.
   void FillInExternalData(TransferItem& aItem, uint32_t aIndex);
 
+  friend class ContentParent;
+  void FillAllExternalData();
+
   void MozClearDataAtHelper(const nsAString& aFormat, uint32_t aIndex,
                             mozilla::ErrorResult& aRv);
 
   nsCOMPtr<nsISupports> mParent;
 
   // the event type this data transfer is for. This will correspond to an
   // event->message value.
   uint32_t mEventType;
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -10,16 +10,17 @@
 #include "mozilla/EventStates.h"
 #include "mozilla/IMEStateManager.h"
 #include "mozilla/MiscEvents.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/TextComposition.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/TouchEvents.h"
+#include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/Event.h"
 #include "mozilla/dom/TabParent.h"
 #include "mozilla/dom/UIEvent.h"
 
 #include "ContentEventHandler.h"
 #include "IMEContentObserver.h"
 #include "WheelHandlingHelper.h"
 
@@ -76,16 +77,17 @@
 #include "mozilla/dom/DataTransfer.h"
 #include "nsContentAreaDragDrop.h"
 #ifdef MOZ_XUL
 #include "nsTreeBodyFrame.h"
 #endif
 #include "nsIController.h"
 #include "nsICommandParams.h"
 #include "mozilla/Services.h"
+#include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/HTMLLabelElement.h"
 
 #include "mozilla/Preferences.h"
 #include "mozilla/LookAndFeel.h"
 #include "GeckoProfiler.h"
 #include "Units.h"
 #include "mozilla/layers/APZCTreeManager.h"
 
@@ -1104,16 +1106,39 @@ EventStateManager::DispatchCrossProcessE
     return remote->SendMouseWheelEvent(*aEvent->AsWheelEvent());
   }
   case eTouchEventClass: {
     // Let the child process synthesize a mouse event if needed, and
     // ensure we don't synthesize one in this process.
     *aStatus = nsEventStatus_eConsumeNoDefault;
     return remote->SendRealTouchEvent(*aEvent->AsTouchEvent());
   }
+  case eDragEventClass: {
+    if (remote->Manager()->IsContentParent()) {
+      remote->Manager()->AsContentParent()->MaybeInvokeDragSession(remote);
+    }
+
+    nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
+    uint32_t dropEffect = nsIDragService::DRAGDROP_ACTION_NONE;
+    uint32_t action = nsIDragService::DRAGDROP_ACTION_NONE;
+    if (dragSession) {
+      dragSession->DragEventDispatchedToChildProcess();
+      dragSession->GetDragAction(&action);
+      nsCOMPtr<nsIDOMDataTransfer> initialDataTransfer;
+      dragSession->GetDataTransfer(getter_AddRefs(initialDataTransfer));
+      if (initialDataTransfer) {
+        initialDataTransfer->GetDropEffectInt(&dropEffect);
+      }
+    }
+
+    bool retval = remote->SendRealDragEvent(*aEvent->AsDragEvent(),
+                                            action, dropEffect);
+
+    return retval;
+  }
   default: {
     MOZ_CRASH("Attempt to send non-whitelisted event?");
   }
   }
 }
 
 bool
 EventStateManager::IsRemoteTarget(nsIContent* target) {
@@ -1160,16 +1185,23 @@ CrossProcessSafeEvent(const WidgetEvent&
     case NS_TOUCH_START:
     case NS_TOUCH_MOVE:
     case NS_TOUCH_END:
     case NS_TOUCH_CANCEL:
       return true;
     default:
       return false;
     }
+  case eDragEventClass:
+    switch (aEvent.message) {
+    case NS_DRAGDROP_OVER:
+    case NS_DRAGDROP_EXIT:
+    case NS_DRAGDROP_DROP:
+      return true;
+    }
   default:
     return false;
   }
 }
 
 bool
 EventStateManager::HandleCrossProcessEvent(WidgetEvent* aEvent,
                                            nsEventStatus *aStatus) {
@@ -1491,16 +1523,22 @@ EventStateManager::BeginTrackingDragGest
   mGestureDownButtons = inDownEvent->buttons;
 
   if (Prefs::ClickHoldContextMenu()) {
     // fire off a timer to track click-hold
     CreateClickHoldTimer(aPresContext, inDownFrame, inDownEvent);
   }
 }
 
+void
+EventStateManager::BeginTrackingRemoteDragGesture(nsIContent* aContent)
+{
+  mGestureDownContent = aContent;
+  mGestureDownFrameOwner = aContent;
+}
 
 //
 // StopTrackingDragGesture
 //
 // Record that the mouse has gone back up so that we should leave the TRACKING
 // state of d&d gesture tracker and return to the START state.
 //
 void
@@ -1592,18 +1630,19 @@ EventStateManager::GenerateDragGesture(n
 
       nsRefPtr<DataTransfer> dataTransfer =
         new DataTransfer(window, NS_DRAGDROP_START, false, -1);
 
       nsCOMPtr<nsISelection> selection;
       nsCOMPtr<nsIContent> eventContent, targetContent;
       mCurrentTarget->GetContentForEvent(aEvent, getter_AddRefs(eventContent));
       if (eventContent)
-        DetermineDragTarget(window, eventContent, dataTransfer,
-                            getter_AddRefs(selection), getter_AddRefs(targetContent));
+        DetermineDragTargetAndDefaultData(window, eventContent, dataTransfer,
+                                          getter_AddRefs(selection),
+                                          getter_AddRefs(targetContent));
 
       // Stop tracking the drag gesture now. This should stop us from
       // reentering GenerateDragGesture inside DOM event processing.
       StopTrackingDragGesture();
 
       if (!targetContent)
         return;
 
@@ -1689,21 +1728,21 @@ EventStateManager::GenerateDragGesture(n
 
     // Now flush all pending notifications, for better responsiveness
     // while dragging.
     FlushPendingEvents(aPresContext);
   }
 } // GenerateDragGesture
 
 void
-EventStateManager::DetermineDragTarget(nsPIDOMWindow* aWindow,
-                                       nsIContent* aSelectionTarget,
-                                       DataTransfer* aDataTransfer,
-                                       nsISelection** aSelection,
-                                       nsIContent** aTargetNode)
+EventStateManager::DetermineDragTargetAndDefaultData(nsPIDOMWindow* aWindow,
+                                                     nsIContent* aSelectionTarget,
+                                                     DataTransfer* aDataTransfer,
+                                                     nsISelection** aSelection,
+                                                     nsIContent** aTargetNode)
 {
   *aTargetNode = nullptr;
 
   // GetDragData determines if a selection, link or image in the content
   // should be dragged, and places the data associated with the drag in the
   // data transfer.
   // mGestureDownContent is the node where the mousedown event for the drag
   // occurred, and aSelectionTarget is the node to use when a selection is used
@@ -3146,16 +3185,17 @@ EventStateManager::PostHandleEvent(nsPre
       // that a drag is allowed. If the event isn't cancelled, a drop won't be
       // allowed. Essentially, to allow a drop somewhere, specify the effects
       // using the effectAllowed and dropEffect properties in a dragenter or
       // dragover event and cancel the event. To not allow a drop somewhere,
       // don't cancel the event or set the effectAllowed or dropEffect to
       // "none". This way, if the event is just ignored, no drop will be
       // allowed.
       uint32_t dropEffect = nsIDragService::DRAGDROP_ACTION_NONE;
+      uint32_t action = nsIDragService::DRAGDROP_ACTION_NONE;
       if (nsEventStatus_eConsumeNoDefault == *aStatus) {
         // if the event has a dataTransfer set, use it.
         if (dragEvent->dataTransfer) {
           // get the dataTransfer and the dropEffect that was set on it
           dataTransfer = do_QueryInterface(dragEvent->dataTransfer);
           dataTransfer->GetDropEffectInt(&dropEffect);
         }
         else {
@@ -3163,17 +3203,16 @@ EventStateManager::PostHandleEvent(nsPre
           // made to access the dataTransfer during the event, yet the event
           // was cancelled. Instead, use the initial data transfer available
           // from the drag session. The drop effect would not have been
           // initialized (which is done in DragEvent::GetDataTransfer),
           // so set it from the drag action. We'll still want to filter it
           // based on the effectAllowed below.
           dataTransfer = initialDataTransfer;
 
-          uint32_t action;
           dragSession->GetDragAction(&action);
 
           // filter the drop effect based on the action. Use UNINITIALIZED as
           // any effect is allowed.
           dropEffect = nsContentUtils::FilterDropEffect(action,
                          nsIDragService::DRAGDROP_ACTION_UNINITIALIZED);
         }
 
@@ -3185,17 +3224,16 @@ EventStateManager::PostHandleEvent(nsPre
           dataTransfer->GetEffectAllowedInt(&effectAllowed);
 
         // set the drag action based on the drop effect and effect allowed.
         // The drop effect field on the drag transfer object specifies the
         // desired current drop effect. However, it cannot be used if the
         // effectAllowed state doesn't include that type of action. If the
         // dropEffect is "none", then the action will be 'none' so a drop will
         // not be allowed.
-        uint32_t action = nsIDragService::DRAGDROP_ACTION_NONE;
         if (effectAllowed == nsIDragService::DRAGDROP_ACTION_UNINITIALIZED ||
             dropEffect & effectAllowed)
           action = dropEffect;
 
         if (action == nsIDragService::DRAGDROP_ACTION_NONE)
           dropEffect = nsIDragService::DRAGDROP_ACTION_NONE;
 
         // inform the drag session that a drop is allowed on this node.
@@ -3209,16 +3247,22 @@ EventStateManager::PostHandleEvent(nsPre
           // content or chrome.
           dragSession->SetOnlyChromeDrop(
             !dragEvent->mDefaultPreventedOnContent);
         }
       } else if (aEvent->message == NS_DRAGDROP_OVER && !isChromeDoc) {
         // No one called preventDefault(), so handle drop only in chrome.
         dragSession->SetOnlyChromeDrop(true);
       }
+      if (ContentChild* child = ContentChild::GetSingleton()) {
+        child->SendUpdateDropEffect(action, dropEffect);
+      }
+      if (dispatchedToContentProcess) {
+        dragSession->SetCanDrop(true);
+      }
 
       // now set the drop effect in the initial dataTransfer. This ensures
       // that we can get the desired drop effect in the drop event.
       if (initialDataTransfer)
         initialDataTransfer->SetDropEffectInt(dropEffect);
     }
     break;
 
@@ -4559,43 +4603,43 @@ static nsIContent* FindCommonAncestor(ns
   // Find closest common ancestor
   if (aNode1 && aNode2) {
     // Find the nearest common ancestor by counting the distance to the
     // root and then walking up again, in pairs.
     int32_t offset = 0;
     nsIContent *anc1 = aNode1;
     for (;;) {
       ++offset;
-      nsIContent* parent = anc1->GetParent();
+      nsIContent* parent = anc1->GetFlattenedTreeParent();
       if (!parent)
         break;
       anc1 = parent;
     }
     nsIContent *anc2 = aNode2;
     for (;;) {
       --offset;
-      nsIContent* parent = anc2->GetParent();
+      nsIContent* parent = anc2->GetFlattenedTreeParent();
       if (!parent)
         break;
       anc2 = parent;
     }
     if (anc1 == anc2) {
       anc1 = aNode1;
       anc2 = aNode2;
       while (offset > 0) {
-        anc1 = anc1->GetParent();
+        anc1 = anc1->GetFlattenedTreeParent();
         --offset;
       }
       while (offset < 0) {
-        anc2 = anc2->GetParent();
+        anc2 = anc2->GetFlattenedTreeParent();
         ++offset;
       }
       while (anc1 != anc2) {
-        anc1 = anc1->GetParent();
-        anc2 = anc2->GetParent();
+        anc1 = anc1->GetFlattenedTreeParent();
+        anc2 = anc2->GetFlattenedTreeParent();
       }
       return anc1;
     }
   }
   return nullptr;
 }
 
 static Element*
@@ -4641,17 +4685,17 @@ EventStateManager::DoStateChange(nsICont
 /* static */
 void
 EventStateManager::UpdateAncestorState(nsIContent* aStartNode,
                                        nsIContent* aStopBefore,
                                        EventStates aState,
                                        bool aAddState)
 {
   for (; aStartNode && aStartNode != aStopBefore;
-       aStartNode = aStartNode->GetParentElementCrossingShadowRoot()) {
+       aStartNode = aStartNode->GetFlattenedTreeParent()) {
     // We might be starting with a non-element (e.g. a text node) and
     // if someone is doing something weird might be ending with a
     // non-element too (e.g. a document fragment)
     if (!aStartNode->IsElement()) {
       continue;
     }
     Element* element = aStartNode->AsElement();
     DoStateChange(element, aState, aAddState);
@@ -4669,17 +4713,17 @@ EventStateManager::UpdateAncestorState(n
     // same node, and while one is no longer hovered the other still
     // is.  In that situation, the label that's still hovered will be
     // aStopBefore or some ancestor of it, and the call we just made
     // to UpdateAncestorState with aAddState = false would have
     // removed the hover state from the node.  But the node should
     // still be in hover state.  To handle this situation we need to
     // keep walking up the tree and any time we find a label mark its
     // corresponding node as still in our state.
-    for ( ; aStartNode; aStartNode = aStartNode->GetParentElementCrossingShadowRoot()) {
+    for ( ; aStartNode; aStartNode = aStartNode->GetFlattenedTreeParent()) {
       if (!aStartNode->IsElement()) {
         continue;
       }
 
       Element* labelTarget = GetLabelTarget(aStartNode->AsElement());
       if (labelTarget && !labelTarget->State().HasState(aState)) {
         DoStateChange(labelTarget, aState, true);
       }
--- a/dom/events/EventStateManager.h
+++ b/dom/events/EventStateManager.h
@@ -750,35 +750,38 @@ protected:
    */
   void DecideGestureEvent(WidgetGestureNotifyEvent* aEvent,
                           nsIFrame* targetFrame);
 
   // routines for the d&d gesture tracking state machine
   void BeginTrackingDragGesture(nsPresContext* aPresContext,
                                 WidgetMouseEvent* aDownEvent,
                                 nsIFrame* aDownFrame);
+
+  friend class mozilla::dom::TabParent;
+  void BeginTrackingRemoteDragGesture(nsIContent* aContent);
   void StopTrackingDragGesture();
   void GenerateDragGesture(nsPresContext* aPresContext,
                            WidgetMouseEvent* aEvent);
 
   /**
    * Determine which node the drag should be targeted at.
    * This is either the node clicked when there is a selection, or, for HTML,
    * the element with a draggable property set to true.
    *
    * aSelectionTarget - target to check for selection
    * aDataTransfer - data transfer object that will contain the data to drag
    * aSelection - [out] set to the selection to be dragged
    * aTargetNode - [out] the draggable node, or null if there isn't one
    */
-  void DetermineDragTarget(nsPIDOMWindow* aWindow,
-                           nsIContent* aSelectionTarget,
-                           dom::DataTransfer* aDataTransfer,
-                           nsISelection** aSelection,
-                           nsIContent** aTargetNode);
+  void DetermineDragTargetAndDefaultData(nsPIDOMWindow* aWindow,
+                                         nsIContent* aSelectionTarget,
+                                         dom::DataTransfer* aDataTransfer,
+                                         nsISelection** aSelection,
+                                         nsIContent** aTargetNode);
 
   /*
    * Perform the default handling for the dragstart/draggesture event and set up a
    * drag for aDataTransfer if it contains any data. Returns true if a drag has
    * started.
    *
    * aDragEvent - the dragstart/draggesture event
    * aDataTransfer - the data transfer that holds the data to be dragged
--- a/dom/events/PhysicalKeyCodeNameList.h
+++ b/dom/events/PhysicalKeyCodeNameList.h
@@ -170,17 +170,17 @@ DEFINE_PHYSICAL_KEY_CODE_NAME_WITH_SAME_
 DEFINE_PHYSICAL_KEY_CODE_NAME_WITH_SAME_NAME(F18)
 DEFINE_PHYSICAL_KEY_CODE_NAME_WITH_SAME_NAME(F19)
 DEFINE_PHYSICAL_KEY_CODE_NAME_WITH_SAME_NAME(F20)
 DEFINE_PHYSICAL_KEY_CODE_NAME_WITH_SAME_NAME(F21)
 DEFINE_PHYSICAL_KEY_CODE_NAME_WITH_SAME_NAME(F22)
 DEFINE_PHYSICAL_KEY_CODE_NAME_WITH_SAME_NAME(F23)
 DEFINE_PHYSICAL_KEY_CODE_NAME_WITH_SAME_NAME(F24)
 DEFINE_PHYSICAL_KEY_CODE_NAME_WITH_SAME_NAME(Fn)
-// DEFINE_PHYSICAL_KEY_CODE_NAME_WITH_SAME_NAME(FLock)
+DEFINE_PHYSICAL_KEY_CODE_NAME_WITH_SAME_NAME(FnLock)
 DEFINE_PHYSICAL_KEY_CODE_NAME_WITH_SAME_NAME(PrintScreen)
 DEFINE_PHYSICAL_KEY_CODE_NAME_WITH_SAME_NAME(ScrollLock)
 DEFINE_PHYSICAL_KEY_CODE_NAME_WITH_SAME_NAME(Pause)
 
 // Media keys
 DEFINE_PHYSICAL_KEY_CODE_NAME_WITH_SAME_NAME(BrowserBack)
 DEFINE_PHYSICAL_KEY_CODE_NAME_WITH_SAME_NAME(BrowserFavorites)
 DEFINE_PHYSICAL_KEY_CODE_NAME_WITH_SAME_NAME(BrowserForward)
--- a/dom/events/test/mochitest.ini
+++ b/dom/events/test/mochitest.ini
@@ -137,16 +137,17 @@ skip-if = toolkit == "gonk" || e10s
 [test_bug985988.html]
 [test_bug998809.html]
 [test_bug1017086_disable.html]
 support-files = bug1017086_inner.html
 [test_bug1017086_enable.html]
 support-files = bug1017086_inner.html
 [test_bug1079236.html]
 [test_bug1145910.html]
+[test_bug1150308.html]
 [test_clickevent_on_input.html]
 skip-if = toolkit == 'android' #CRASH_DUMP, RANDOM
 [test_continuous_wheel_events.html]
 skip-if = buildapp == 'b2g' || e10s # b2g(5535 passed, 108 failed - more tests running than desktop) b2g-debug(5535 passed, 108 failed - more tests running than desktop) b2g-desktop(5535 passed, 108 failed - more tests running than desktop)
 [test_dblclick_explicit_original_target.html]
 [test_dom_keyboard_event.html]
 skip-if = toolkit == 'android' #CRASH_DUMP, RANDOM
 [test_dom_mouse_event.html]
new file mode 100644
--- /dev/null
+++ b/dom/events/test/test_bug1150308.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1150308
+-->
+<head>
+  <title>Test for Bug 1150308</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<div id="host"><span id="distributeme">Foo</span></div>
+<script type="application/javascript">
+
+/** Test for Bug 1150308 **/
+SimpleTest.waitForExplicitFinish();
+
+SimpleTest.waitForFocus(function() {
+  var host = document.getElementById("host");
+  var shadow = host.createShadowRoot();
+  shadow.innerHTML = '<style>.bar:active { color: rgb(0, 255, 0); }</style><div class="bar" id="inner"><content></content></div>';
+  var inner = shadow.getElementById("inner");
+  var distributed = document.getElementById("distributeme");
+
+  is(window.getComputedStyle(inner).color, "rgb(0, 0, 0)", "The div inside the shadow root should not be active.");
+
+  synthesizeMouseAtCenter(distributed, { type: "mousedown" });
+
+  is(window.getComputedStyle(inner).color, "rgb(0, 255, 0)", "Div inside shadow root should be active.");
+
+  synthesizeMouseAtCenter(distributed, { type: "mouseup" });
+
+  is(window.getComputedStyle(inner).color, "rgb(0, 0, 0)", "Div inside shadow root should no longer be active.");
+
+  SimpleTest.finish();
+});
+
+</script>
+</body>
+</html>
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -2040,17 +2040,16 @@ HTMLMediaElement::LookupMediaElementURIT
       }
     }
   }
   return nullptr;
 }
 
 HTMLMediaElement::HTMLMediaElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
   : nsGenericHTMLElement(aNodeInfo),
-    mSrcStreamListener(nullptr),
     mCurrentLoadID(0),
     mNetworkState(nsIDOMHTMLMediaElement::NETWORK_EMPTY),
     mReadyState(nsIDOMHTMLMediaElement::HAVE_NOTHING),
     mLastNextFrameStatus(NEXT_FRAME_UNINITIALIZED),
     mLoadWaitStatus(NOT_WAITING),
     mVolume(1.0),
     mPreloadAction(PRELOAD_UNDEFINED),
     mMediaSize(-1,-1),
@@ -2950,16 +2949,71 @@ private:
   bool mHaveCurrentData;
   bool mBlocked;
 
   // mMutex protects the fields below; they can be accessed on any thread
   Mutex mMutex;
   bool mPendingNotifyOutput;
 };
 
+/**
+ * This listener observes the first video frame to arrive with a non-empty size,
+ * and calls HTMLMediaElement::ReceivedMediaStreamInitialSize() with that size.
+ */
+class HTMLMediaElement::StreamSizeListener : public MediaStreamListener {
+public:
+  explicit StreamSizeListener(HTMLMediaElement* aElement) :
+    mElement(aElement),
+    mMutex("HTMLMediaElement::StreamSizeListener")
+  {}
+  void Forget() { mElement = nullptr; }
+
+  void ReceivedSize()
+  {
+    if (!mElement) {
+      return;
+    }
+    gfxIntSize size;
+    {
+      MutexAutoLock lock(mMutex);
+      size = mInitialSize;
+    }
+    nsRefPtr<HTMLMediaElement> deathGrip = mElement;
+    mElement->UpdateInitialMediaSize(size);
+  }
+  virtual void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
+                                        StreamTime aTrackOffset,
+                                        uint32_t aTrackEvents,
+                                        const MediaSegment& aQueuedMedia) override
+  {
+    MutexAutoLock lock(mMutex);
+    if (mInitialSize != gfxIntSize(0,0) ||
+        aQueuedMedia.GetType() != MediaSegment::VIDEO) {
+      return;
+    }
+    const VideoSegment& video = static_cast<const VideoSegment&>(aQueuedMedia);
+    for (VideoSegment::ConstChunkIterator c(video); !c.IsEnded(); c.Next()) {
+      if (c->mFrame.GetIntrinsicSize() != gfxIntSize(0,0)) {
+        mInitialSize = c->mFrame.GetIntrinsicSize();
+        nsCOMPtr<nsIRunnable> event =
+          NS_NewRunnableMethod(this, &StreamSizeListener::ReceivedSize);
+        aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
+      }
+    }
+  }
+
+private:
+  // These fields may only be accessed on the main thread
+  HTMLMediaElement* mElement;
+
+  // mMutex protects the fields below; they can be accessed on any thread
+  Mutex mMutex;
+  gfxIntSize mInitialSize;
+};
+
 class HTMLMediaElement::MediaStreamTracksAvailableCallback:
     public DOMMediaStream::OnTracksAvailableCallback
 {
 public:
   explicit MediaStreamTracksAvailableCallback(HTMLMediaElement* aElement):
       DOMMediaStream::OnTracksAvailableCallback(),
       mElement(aElement)
     {}
@@ -2970,17 +3024,18 @@ public:
     mElement->NotifyMediaStreamTracksAvailable(aStream);
   }
 private:
   HTMLMediaElement* mElement;
 };
 
 void HTMLMediaElement::SetupSrcMediaStreamPlayback(DOMMediaStream* aStream)
 {
-  NS_ASSERTION(!mSrcStream && !mSrcStreamListener, "Should have been ended already");
+  NS_ASSERTION(!mSrcStream && !mMediaStreamListener && !mMediaStreamSizeListener,
+               "Should have been ended already");
 
   mSrcStream = aStream;
 
   nsIDOMWindow* window = OwnerDoc()->GetInnerWindow();
   if (!window) {
     return;
   }
 
@@ -3003,18 +3058,23 @@ void HTMLMediaElement::SetupSrcMediaStre
 
   nsRefPtr<MediaStream> stream = mSrcStream->GetStream();
   if (stream) {
     stream->SetAudioChannelType(mAudioChannel);
   }
 
   // XXX if we ever support capturing the output of a media element which is
   // playing a stream, we'll need to add a CombineWithPrincipal call here.
-  mSrcStreamListener = new StreamListener(this);
-  GetSrcMediaStream()->AddListener(mSrcStreamListener);
+  mMediaStreamListener = new StreamListener(this);
+  mMediaStreamSizeListener = new StreamSizeListener(this);
+
+  GetSrcMediaStream()->AddListener(mMediaStreamListener);
+  // Listen for an initial image size on mSrcStream so we can get results even
+  // if we block the mPlaybackStream.
+  stream->AddListener(mMediaStreamSizeListener);
   if (mPaused) {
     GetSrcMediaStream()->ChangeExplicitBlockerCount(1);
   }
   if (mPausedForInactiveDocumentOrChannel) {
     GetSrcMediaStream()->ChangeExplicitBlockerCount(1);
   }
 
   ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_IDLE);
@@ -3037,27 +3097,32 @@ void HTMLMediaElement::SetupSrcMediaStre
 
   // FirstFrameLoaded() will be called when the stream has current data.
 }
 
 void HTMLMediaElement::EndSrcMediaStreamPlayback()
 {
   MediaStream* stream = GetSrcMediaStream();
   if (stream) {
-    stream->RemoveListener(mSrcStreamListener);
+    stream->RemoveListener(mMediaStreamListener);
+  }
+  if (mSrcStream->GetStream()) {
+    mSrcStream->GetStream()->RemoveListener(mMediaStreamSizeListener);
   }
   mSrcStream->DisconnectTrackListListeners(AudioTracks(), VideoTracks());
 
   if (mPlaybackStreamInputPort) {
     mPlaybackStreamInputPort->Destroy();
   }
 
   // Kill its reference to this element
-  mSrcStreamListener->Forget();
-  mSrcStreamListener = nullptr;
+  mMediaStreamListener->Forget();
+  mMediaStreamListener = nullptr;
+  mMediaStreamSizeListener->Forget();
+  mMediaStreamSizeListener = nullptr;
   if (stream) {
     stream->RemoveAudioOutput(this);
   }
   VideoFrameContainer* container = GetVideoFrameContainer();
   if (container) {
     if (stream) {
       stream->RemoveVideoOutput(container);
     }
@@ -3796,26 +3861,33 @@ void HTMLMediaElement::NotifyDecoderPrin
 
   for (uint32_t i = 0; i < mOutputStreams.Length(); ++i) {
     OutputMediaStream* ms = &mOutputStreams[i];
     ms->mStream->SetCORSMode(mCORSMode);
     ms->mStream->CombineWithPrincipal(principal);
   }
 }
 
-void HTMLMediaElement::UpdateMediaSize(nsIntSize size)
+void HTMLMediaElement::UpdateMediaSize(const nsIntSize& aSize)
 {
-  if (IsVideo() && mReadyState != HAVE_NOTHING && mMediaSize != size) {
+  if (IsVideo() && mReadyState != HAVE_NOTHING && mMediaSize != aSize) {
     DispatchAsyncEvent(NS_LITERAL_STRING("resize"));
   }
 
-  mMediaSize = size;
+  mMediaSize = aSize;
   UpdateReadyStateForData(mLastNextFrameStatus);
 }
 
+void HTMLMediaElement::UpdateInitialMediaSize(const nsIntSize& aSize)
+{
+  if (mMediaSize == nsIntSize(-1, -1)) {
+    UpdateMediaSize(aSize);
+  }
+}
+
 void HTMLMediaElement::SuspendOrResumeElement(bool aPauseElement, bool aSuspendEvents)
 {
   if (aPauseElement != mPausedForInactiveDocumentOrChannel) {
     mPausedForInactiveDocumentOrChannel = aPauseElement;
     if (aPauseElement) {
       if (mMediaSource) {
         ReportMSETelemetry();
 #ifdef MOZ_EME
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -255,17 +255,20 @@ public:
   // Returns null if nothing is playing.
   already_AddRefed<nsIPrincipal> GetCurrentPrincipal();
 
   // called to notify that the principal of the decoder's media resource has changed.
   virtual void NotifyDecoderPrincipalChanged() final override;
 
   // Update the visual size of the media. Called from the decoder on the
   // main thread when/if the size changes.
-  void UpdateMediaSize(nsIntSize size);
+  void UpdateMediaSize(const nsIntSize& aSize);
+  // Like UpdateMediaSize, but only updates the size if no size has yet
+  // been set.
+  void UpdateInitialMediaSize(const nsIntSize& aSize);
 
   // Returns the CanPlayStatus indicating if we can handle the
   // full MIME type including the optional codecs parameter.
   static CanPlayStatus GetCanPlay(const nsAString& aType);
 
   /**
    * Called when a child source element is added to this media element. This
    * may queue a task to run the select resource algorithm if appropriate.
@@ -637,16 +640,17 @@ public:
   bool IsBeingDestroyed();
 
 protected:
   virtual ~HTMLMediaElement();
 
   class MediaLoadListener;
   class MediaStreamTracksAvailableCallback;
   class StreamListener;
+  class StreamSizeListener;
 
   virtual void GetItemValueText(DOMString& text) override;
   virtual void SetItemValueText(const nsAString& text) override;
 
   class WakeLockBoolWrapper {
   public:
     explicit WakeLockBoolWrapper(bool val = false)
       : mValue(val), mCanPlay(true), mOuter(nullptr) {}
@@ -1042,18 +1046,22 @@ protected:
   // Holds references to the DOM wrappers for the MediaStreams that we're
   // writing to.
   struct OutputMediaStream {
     nsRefPtr<DOMMediaStream> mStream;
     bool mFinishWhenEnded;
   };
   nsTArray<OutputMediaStream> mOutputStreams;
 
-  // Holds a reference to the MediaStreamListener attached to mSrcStream.
-  nsRefPtr<StreamListener> mSrcStreamListener;
+  // Holds a reference to the MediaStreamListener attached to mPlaybackStream
+  // (or mSrcStream if mPlaybackStream is null).
+  nsRefPtr<StreamListener> mMediaStreamListener;
+  // Holds a reference to the size-getting MediaStreamListener attached to
+  // mSrcStream.
+  nsRefPtr<StreamSizeListener> mMediaStreamSizeListener;
 
   // Holds a reference to the MediaSource supplying data for playback.
   nsRefPtr<MediaSource> mMediaSource;
 
   // Holds a reference to the first channel we open to the media resource.
   // Once the decoder is created, control over the channel passes to the
   // decoder, and we null out this reference. We must store this in case
   // we need to cancel the channel before control of it passes to the decoder.
--- a/dom/indexedDB/IDBFactory.cpp
+++ b/dom/indexedDB/IDBFactory.cpp
@@ -304,16 +304,17 @@ IDBFactory::CreateForJSInternal(JSContex
   MOZ_ASSERT(aPrincipalInfo->type() != PrincipalInfo::T__None);
   MOZ_ASSERT(aFactory);
   MOZ_ASSERT(JS_GetGlobalForObject(aCx, aOwningObject) == aOwningObject,
              "Not a global object!");
 
   if (aPrincipalInfo->type() != PrincipalInfo::TContentPrincipalInfo &&
       aPrincipalInfo->type() != PrincipalInfo::TSystemPrincipalInfo) {
     NS_WARNING("IndexedDB not allowed for this principal!");
+    aPrincipalInfo = nullptr;
     *aFactory = nullptr;
     return NS_OK;
   }
 
   nsRefPtr<IDBFactory> factory = new IDBFactory();
   factory->mPrincipalInfo = aPrincipalInfo.forget();
   factory->mOwningObject = aOwningObject;
   mozilla::HoldJSObjects(factory.get());
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -24,16 +24,17 @@
 #include "mozilla/a11y/DocAccessibleChild.h"
 #endif
 #include "mozilla/Preferences.h"
 #include "mozilla/ProcessHangMonitorIPC.h"
 #include "mozilla/docshell/OfflineCacheUpdateChild.h"
 #include "mozilla/dom/ContentBridgeChild.h"
 #include "mozilla/dom/ContentBridgeParent.h"
 #include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/DataTransfer.h"
 #include "mozilla/dom/DOMStorageIPC.h"
 #include "mozilla/dom/ExternalHelperAppChild.h"
 #include "mozilla/dom/PCrashReporterChild.h"
 #include "mozilla/dom/ProcessGlobal.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/asmjscache/AsmJSCache.h"
 #include "mozilla/dom/asmjscache/PAsmJSCacheEntryChild.h"
 #include "mozilla/dom/nsIContentChild.h"
@@ -64,16 +65,17 @@
 #endif
 #endif
 
 #include "mozilla/unused.h"
 
 #include "mozInlineSpellChecker.h"
 #include "nsIConsoleListener.h"
 #include "nsICycleCollectorListener.h"
+#include "nsIDragService.h"
 #include "nsIIPCBackgroundChildCreateCallback.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIMemoryReporter.h"
 #include "nsIMemoryInfoDumper.h"
 #include "nsIMutable.h"
 #include "nsIObserverService.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsScreenManagerProxy.h"
@@ -1850,16 +1852,24 @@ ContentChild::ActorDestroy(ActorDestroyR
 
     nsCOMPtr<nsIConsoleService> svc(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
     if (svc) {
         svc->UnregisterListener(mConsoleListener);
         mConsoleListener->mChild = nullptr;
     }
     mIsAlive = false;
 
+#ifdef MOZ_NUWA_PROCESS
+    if (IsNuwaProcess()) {
+        // The Nuwa cannot go through the full XPCOM shutdown path or deadlock
+        // will result.
+        QuickExit();
+    }
+#endif
+
     XRE_ShutdownChildProcess();
 }
 
 void
 ContentChild::ProcessingError(Result aCode, const char* aReason)
 {
     switch (aCode) {
         case MsgDropped:
@@ -2723,16 +2733,74 @@ NextWindowID()
   uint64_t windowID = ++gNextWindowID;
 
   MOZ_RELEASE_ASSERT(windowID < (uint64_t(1) << kWindowIDWindowBits));
   uint64_t windowBits = windowID & ((uint64_t(1) << kWindowIDWindowBits) - 1);
 
   return (processBits << kWindowIDWindowBits) | windowBits;
 }
 
+bool
+ContentChild::RecvInvokeDragSession(nsTArray<IPCDataTransfer>&& aTransfers,
+                                    const uint32_t& aAction)
+{
+  nsCOMPtr<nsIDragService> dragService =
+    do_GetService("@mozilla.org/widget/dragservice;1");
+  if (dragService) {
+    dragService->StartDragSession();
+    nsCOMPtr<nsIDragSession> session;
+    dragService->GetCurrentSession(getter_AddRefs(session));
+    if (session) {
+      session->SetDragAction(aAction);
+      nsCOMPtr<DataTransfer> dataTransfer =
+        new DataTransfer(nullptr, NS_DRAGDROP_START, false, -1);
+      for (uint32_t i = 0; i < aTransfers.Length(); ++i) {
+        auto& items = aTransfers[i].items();
+        for (uint32_t j = 0; j < items.Length(); ++j) {
+          const IPCDataTransferItem& item = items[j];
+          nsCOMPtr<nsIWritableVariant> variant =
+             do_CreateInstance(NS_VARIANT_CONTRACTID);
+          NS_ENSURE_TRUE(variant, false);
+          if (item.data().type() == IPCDataTransferData::TnsString) {
+            const nsString& data = item.data().get_nsString();
+            variant->SetAsAString(data);
+          } else if (item.data().type() == IPCDataTransferData::TPBlobChild) {
+            BlobChild* blob = static_cast<BlobChild*>(item.data().get_PBlobChild());
+            nsRefPtr<FileImpl> fileImpl = blob->GetBlobImpl();
+            variant->SetAsISupports(fileImpl);
+          }
+          dataTransfer->SetDataWithPrincipal(NS_ConvertUTF8toUTF16(item.flavor()),
+                                             variant, i,
+                                             nsContentUtils::GetSystemPrincipal());
+        }
+      }
+      session->SetDataTransfer(dataTransfer);
+    }
+  }
+  return true;
+}
+
+bool
+ContentChild::RecvEndDragSession(const bool& aDoneDrag,
+                                 const bool& aUserCancelled)
+{
+  nsCOMPtr<nsIDragService> dragService =
+    do_GetService("@mozilla.org/widget/dragservice;1");
+  if (dragService) {
+    if (aUserCancelled) {
+      nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
+      if (dragSession) {
+        dragSession->UserCancelled();
+      }
+    }
+    dragService->EndDragSession(aDoneDrag);
+  }
+  return true;
+}
+
 } // namespace dom
 } // namespace mozilla
 
 extern "C" {
 
 #if defined(MOZ_NUWA_PROCESS)
 NS_EXPORT void
 GetProtoFdInfos(NuwaProtoFdInfo* aInfoList,
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -391,16 +391,21 @@ public:
                                    nsTArray<nsCString>&& aFeatures,
                                    nsTArray<nsCString>&& aThreadNameFilters) override;
     virtual bool RecvStopProfiler() override;
     virtual bool RecvGetProfile(nsCString* aProfile) override;
     virtual bool RecvDomainSetChanged(const uint32_t& aSetType, const uint32_t& aChangeType,
                                       const OptionalURIParams& aDomain) override;
     virtual bool RecvShutdown() override;
 
+    virtual bool
+    RecvInvokeDragSession(nsTArray<IPCDataTransfer>&& aTransfers,
+                          const uint32_t& aAction) override;
+    virtual bool RecvEndDragSession(const bool& aDoneDrag,
+                                    const bool& aUserCancelled) override;
 #ifdef ANDROID
     gfxIntSize GetScreenSize() { return mScreenSize; }
 #endif
 
     // Get the directory for IndexedDB files. We query the parent for this and
     // cache the value
     nsString &GetIndexedDBPath();
 
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -33,16 +33,17 @@
 #include "mozIApplication.h"
 #ifdef ACCESSIBILITY
 #include "mozilla/a11y/DocAccessibleParent.h"
 #include "nsAccessibilityService.h"
 #endif
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/docshell/OfflineCacheUpdateParent.h"
 #include "mozilla/dom/DataStoreService.h"
+#include "mozilla/dom/DataTransfer.h"
 #include "mozilla/dom/DOMStorageIPC.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/ExternalHelperAppParent.h"
 #include "mozilla/dom/FileSystemRequestParent.h"
 #include "mozilla/dom/GeolocationBinding.h"
 #include "mozilla/dom/PContentBridgeParent.h"
 #include "mozilla/dom/PCycleCollectWithLogsParent.h"
@@ -98,16 +99,17 @@
 #include "nsHashPropertyBag.h"
 #include "nsIAlertsService.h"
 #include "nsIAppsService.h"
 #include "nsIClipboard.h"
 #include "nsICycleCollectorListener.h"
 #include "nsIDocument.h"
 #include "nsIDOMGeoGeolocation.h"
 #include "nsIDOMGeoPositionError.h"
+#include "nsIDragService.h"
 #include "mozilla/dom/WakeLock.h"
 #include "nsIDOMWindow.h"
 #include "nsIExternalProtocolService.h"
 #include "nsIFormProcessor.h"
 #include "nsIGfxInfo.h"
 #include "nsIIdleService.h"
 #include "nsIJSRuntimeService.h"
 #include "nsIMemoryInfoDumper.h"
@@ -4802,16 +4804,72 @@ ContentParent::DeallocPOfflineCacheUpdat
 bool
 ContentParent::RecvSetOfflinePermission(const Principal& aPrincipal)
 {
     nsIPrincipal* principal = aPrincipal;
     nsContentUtils::MaybeAllowOfflineAppByDefault(principal, nullptr);
     return true;
 }
 
+void
+ContentParent::MaybeInvokeDragSession(TabParent* aParent)
+{
+  nsCOMPtr<nsIDragService> dragService =
+    do_GetService("@mozilla.org/widget/dragservice;1");
+  if (dragService && dragService->MaybeAddChildProcess(this)) {
+    // We need to send transferable data to child process.
+    nsCOMPtr<nsIDragSession> session;
+    dragService->GetCurrentSession(getter_AddRefs(session));
+    if (session) {
+      nsTArray<IPCDataTransfer> dataTransfers;
+      nsCOMPtr<nsIDOMDataTransfer> domTransfer;
+      session->GetDataTransfer(getter_AddRefs(domTransfer));
+      nsCOMPtr<DataTransfer> transfer = do_QueryInterface(domTransfer);
+      if (!transfer) {
+        // Pass NS_DRAGDROP_DROP to get DataTransfer with external
+        // drag formats cached.
+        transfer = new DataTransfer(nullptr, NS_DRAGDROP_DROP, true, -1);
+        session->SetDataTransfer(transfer);
+      }
+      // Note, even though this fills the DataTransfer object with
+      // external data, the data is usually transfered over IPC lazily when
+      // needed.
+      transfer->FillAllExternalData();
+      nsCOMPtr<nsILoadContext> lc = aParent ?
+                                     aParent->GetLoadContext() : nullptr;
+      nsCOMPtr<nsISupportsArray> transferables =
+        transfer->GetTransferables(lc);
+      nsContentUtils::TransferablesToIPCTransferables(transferables,
+                                                      dataTransfers,
+                                                      nullptr,
+                                                      this);
+      uint32_t action;
+      session->GetDragAction(&action);
+      mozilla::unused << SendInvokeDragSession(dataTransfers, action);
+    }
+  }
+}
+
+bool
+ContentParent::RecvUpdateDropEffect(const uint32_t& aDragAction,
+                                    const uint32_t& aDropEffect)
+{
+  nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
+  if (dragSession) {
+    dragSession->SetDragAction(aDragAction);
+    nsCOMPtr<nsIDOMDataTransfer> dt;
+    dragSession->GetDataTransfer(getter_AddRefs(dt));
+    if (dt) {
+      dt->SetDropEffectInt(aDropEffect);
+    }
+    dragSession->UpdateDragEffect();
+  }
+  return true;
+}
+
 } // namespace dom
 } // namespace mozilla
 
 NS_IMPL_ISUPPORTS(ParentIdleListener, nsIObserver)
 
 NS_IMETHODIMP
 ParentIdleListener::Observe(nsISupports*, const char* aTopic, const char16_t* aData) {
     mozilla::unused << mParent->SendNotifyIdleObserver(mObserver,
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -366,16 +366,17 @@ public:
                                        const TabId& aTabId) override;
     virtual bool
     DeallocPOfflineCacheUpdateParent(POfflineCacheUpdateParent* aActor) override;
 
     virtual bool RecvSetOfflinePermission(const IPC::Principal& principal) override;
 
     virtual bool RecvFinishShutdown() override;
 
+    void MaybeInvokeDragSession(TabParent* aParent);
 protected:
     void OnChannelConnected(int32_t pid) override;
     virtual void ActorDestroy(ActorDestroyReason why) override;
     void OnNuwaForkTimeout();
 
     bool ShouldContinueFromReplyTimeout() override;
 
 private:
@@ -817,16 +818,18 @@ private:
                           int32_t* aSliceRefCnt,
                           bool* aResult) override;
 
     virtual PDocAccessibleParent* AllocPDocAccessibleParent(PDocAccessibleParent*, const uint64_t&) override;
     virtual bool DeallocPDocAccessibleParent(PDocAccessibleParent*) override;
     virtual bool RecvPDocAccessibleConstructor(PDocAccessibleParent* aDoc,
                                                PDocAccessibleParent* aParentDoc, const uint64_t& aParentID) override;
 
+    virtual bool RecvUpdateDropEffect(const uint32_t& aDragAction,
+                                      const uint32_t& aDropEffect) override;
     // If you add strong pointers to cycle collected objects here, be sure to
     // release these objects in ShutDownProcess.  See the comment there for more
     // details.
 
     GeckoChildProcessHost* mSubprocess;
     ContentParent* mOpener;
 
     ContentParentId mChildID;
--- a/dom/ipc/DOMTypes.ipdlh
+++ b/dom/ipc/DOMTypes.ipdlh
@@ -130,10 +130,27 @@ struct ParentBlobConstructorParams
 };
 
 union BlobConstructorParams
 {
   ChildBlobConstructorParams;
   ParentBlobConstructorParams;
 };
 
+union IPCDataTransferData
+{
+  nsString;
+  PBlob;
+};
+
+struct IPCDataTransferItem
+{
+  nsCString flavor;
+  IPCDataTransferData data;
+};
+
+struct IPCDataTransfer
+{
+  IPCDataTransferItem[] items;
+};
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -40,16 +40,17 @@ using class mozilla::WidgetCompositionEv
 using struct mozilla::widget::IMENotification from "nsIWidget.h";
 using struct nsIMEUpdatePreference from "nsIWidget.h";
 using struct nsIntPoint from "nsPoint.h";
 using struct nsIntRect from "nsRect.h";
 using mozilla::gfx::IntSize from "mozilla/gfx/Point.h";
 using class mozilla::WidgetKeyboardEvent from "ipc/nsGUIEventIPC.h";
 using class mozilla::WidgetMouseEvent from "ipc/nsGUIEventIPC.h";
 using class mozilla::WidgetWheelEvent from "ipc/nsGUIEventIPC.h";
+using class mozilla::WidgetDragEvent from "ipc/nsGUIEventIPC.h";
 using struct nsRect from "nsRect.h";
 using class mozilla::WidgetSelectionEvent from "ipc/nsGUIEventIPC.h";
 using class mozilla::WidgetTouchEvent from "ipc/nsGUIEventIPC.h";
 using struct mozilla::dom::RemoteDOMEvent from "mozilla/dom/TabMessageUtils.h";
 using mozilla::dom::ScreenOrientation from "mozilla/dom/ScreenOrientation.h";
 using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.h";
 using mozilla::CSSPoint from "Units.h";
 using mozilla::CSSToScreenScale from "Units.h";
@@ -508,16 +509,19 @@ parent:
      * dimensions has been requested, likely through win.moveTo or resizeTo
      */
     async SetDimensions(uint32_t aFlags, int32_t aX, int32_t aY, int32_t aCx, int32_t aCy);
 
     prio(high) sync DispatchWheelEvent(WidgetWheelEvent event);
     prio(high) sync DispatchMouseEvent(WidgetMouseEvent event);
     prio(high) sync DispatchKeyboardEvent(WidgetKeyboardEvent event);
 
+    InvokeDragSession(IPCDataTransfer[] transfers, uint32_t action,
+                      nsCString visualData, uint32_t width, uint32_t height,
+                      uint32_t stride, uint8_t format, int32_t dragAreaX, int32_t dragAreaY);
 child:
     /**
      * Notify the remote browser that it has been Show()n on this
      * side, with the given |visibleRect|.  This message is expected
      * to trigger creation of the remote browser's "widget".
      *
      * |Show()| and |Move()| take IntSizes rather than Rects because
      * content processes always render to a virtual <0, 0> top-left
@@ -575,16 +579,17 @@ child:
      * they are 'compressed' by dumping the oldest one.
      */
     RealMouseMoveEvent(WidgetMouseEvent event) compress;
     RealMouseButtonEvent(WidgetMouseEvent event);
     RealKeyEvent(WidgetKeyboardEvent event, MaybeNativeKeyBinding keyBinding);
     MouseWheelEvent(WidgetWheelEvent event, ScrollableLayerGuid aGuid, uint64_t aInputBlockId);
     RealTouchEvent(WidgetTouchEvent aEvent, ScrollableLayerGuid aGuid, uint64_t aInputBlockId);
     RealTouchMoveEvent(WidgetTouchEvent aEvent, ScrollableLayerGuid aGuid, uint64_t aInputBlockId);
+    RealDragEvent(WidgetDragEvent aEvent, uint32_t aDragAction, uint32_t aDropEffect);
 
     /**
      * @see nsIDOMWindowUtils sendKeyEvent.
      */
     KeyEvent(nsString aType,
              int32_t aKeyCode,
              int32_t aCharCode,
              int32_t aModifiers,
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -565,16 +565,19 @@ child:
      * Control the Gecko Profiler in the child process.
      */
     async StartProfiler(uint32_t aEntries, double aInterval, nsCString[] aFeatures,
                         nsCString[] aThreadNameFilters);
     async StopProfiler();
     prio(high) sync GetProfile()
       returns (nsCString aProfile);
 
+    InvokeDragSession(IPCDataTransfer[] transfers, uint32_t action);
+
+    EndDragSession(bool aDoneDrag, bool aUserCancelled);
     NuwaFreeze();
 
     async DomainSetChanged(uint32_t aSetType, uint32_t aChangeType, OptionalURIParams aDomain);
 
     /**
      * Notify the child to shutdown. The child will in turn call FinishShutdown
      * and let the parent close the channel.
      */
@@ -936,15 +939,16 @@ parent:
     SetOfflinePermission(Principal principal);
 
     /**
      * Notifies the parent to continue shutting down after the child performs
      * its shutdown tasks.
      */
     async FinishShutdown();
 
+    UpdateDropEffect(uint32_t aDragAction, uint32_t aDropEffect);
 both:
      AsyncMessage(nsString aMessage, ClonedMessageData aData,
                   CpowEntry[] aCpows, Principal aPrincipal);
 };
 
 }
 }
--- a/dom/ipc/PreallocatedProcessManager.cpp
+++ b/dom/ipc/PreallocatedProcessManager.cpp
@@ -286,17 +286,17 @@ PreallocatedProcessManagerImpl::GetSpare
 /**
  * Publish a ContentParent to spare process list.
  */
 void
 PreallocatedProcessManagerImpl::PublishSpareProcess(ContentParent* aContent)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  if (Preferences::GetBool("dom.ipc.processPriorityManager.testMode")) {
+  if (Preferences::GetBool("dom.ipc.preallocatedProcessManager.testMode")) {
     AutoJSContext cx;
     nsCOMPtr<nsIMessageBroadcaster> ppmm =
       do_GetService("@mozilla.org/parentprocessmessagemanager;1");
     mozilla::unused << ppmm->BroadcastAsyncMessage(
       NS_LITERAL_STRING("TEST-ONLY:nuwa-add-new-process"),
       JS::NullHandleValue, JS::NullHandleValue, cx, 1);
   }
 
@@ -332,17 +332,17 @@ PreallocatedProcessManagerImpl::IsNuwaRe
 
 void
 PreallocatedProcessManagerImpl::OnNuwaReady()
 {
   NS_ASSERTION(!mIsNuwaReady, "Multiple Nuwa processes created!");
   ProcessPriorityManager::SetProcessPriority(mPreallocatedAppProcess,
                                              hal::PROCESS_PRIORITY_MASTER);
   mIsNuwaReady = true;
-  if (Preferences::GetBool("dom.ipc.processPriorityManager.testMode")) {
+  if (Preferences::GetBool("dom.ipc.preallocatedProcessManager.testMode")) {
     AutoJSContext cx;
     nsCOMPtr<nsIMessageBroadcaster> ppmm =
       do_GetService("@mozilla.org/parentprocessmessagemanager;1");
     mozilla::unused << ppmm->BroadcastAsyncMessage(
       NS_LITERAL_STRING("TEST-ONLY:nuwa-ready"),
       JS::NullHandleValue, JS::NullHandleValue, cx, 1);
   }
   NuwaFork();
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -2404,16 +2404,54 @@ TabChild::RecvRealTouchEvent(const Widge
 bool
 TabChild::RecvRealTouchMoveEvent(const WidgetTouchEvent& aEvent,
                                  const ScrollableLayerGuid& aGuid,
                                  const uint64_t& aInputBlockId)
 {
   return RecvRealTouchEvent(aEvent, aGuid, aInputBlockId);
 }
 
+bool
+TabChild::RecvRealDragEvent(const WidgetDragEvent& aEvent,
+                            const uint32_t& aDragAction,
+                            const uint32_t& aDropEffect)
+{
+  WidgetDragEvent localEvent(aEvent);
+  localEvent.widget = mWidget;
+
+  nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
+  if (dragSession) {
+    dragSession->SetDragAction(aDragAction);
+    nsCOMPtr<nsIDOMDataTransfer> initialDataTransfer;
+    dragSession->GetDataTransfer(getter_AddRefs(initialDataTransfer));
+    if (initialDataTransfer) {
+      initialDataTransfer->SetDropEffectInt(aDropEffect);
+    }
+  }
+
+  if (aEvent.message == NS_DRAGDROP_DROP) {
+    bool canDrop = true;
+    if (!dragSession || NS_FAILED(dragSession->GetCanDrop(&canDrop)) ||
+        !canDrop) {
+      localEvent.message = NS_DRAGDROP_EXIT;
+    }
+  } else if (aEvent.message == NS_DRAGDROP_OVER) {
+    nsCOMPtr<nsIDragService> dragService =
+      do_GetService("@mozilla.org/widget/dragservice;1");
+    if (dragService) {
+      // This will dispatch 'drag' event at the source if the
+      // drag transaction started in this process.
+      dragService->FireDragEventAtSource(NS_DRAGDROP_DRAG);
+    }
+  }
+
+  APZCCallbackHelper::DispatchWidgetEvent(localEvent);
+  return true;
+}
+
 void
 TabChild::RequestNativeKeyBindings(AutoCacheNativeKeyCommands* aAutoCache,
                                    WidgetKeyboardEvent* aEvent)
 {
   MaybeNativeKeyBinding maybeBindings;
   if (!SendRequestNativeKeyBindings(*aEvent, &maybeBindings)) {
     return;
   }
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -345,16 +345,19 @@ public:
                                 const float&    aX,
                                 const float&    aY,
                                 const int32_t&  aButton,
                                 const int32_t&  aClickCount,
                                 const int32_t&  aModifiers,
                                 const bool&     aIgnoreRootScrollFrame) override;
     virtual bool RecvRealMouseMoveEvent(const mozilla::WidgetMouseEvent& event) override;
     virtual bool RecvRealMouseButtonEvent(const mozilla::WidgetMouseEvent& event) override;
+    virtual bool RecvRealDragEvent(const WidgetDragEvent& aEvent,
+                                   const uint32_t& aDragAction,
+                                   const uint32_t& aDropEffect) override;
     virtual bool RecvRealKeyEvent(const mozilla::WidgetKeyboardEvent& event,
                                   const MaybeNativeKeyBinding& aBindings) override;
     virtual bool RecvMouseWheelEvent(const mozilla::WidgetWheelEvent& event,
                                      const ScrollableLayerGuid& aGuid,
                                      const uint64_t& aInputBlockId) override;
     virtual bool RecvRealTouchEvent(const WidgetTouchEvent& aEvent,
                                     const ScrollableLayerGuid& aGuid,
                                     const uint64_t& aInputBlockId) override;
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -7,34 +7,38 @@
 #include "base/basictypes.h"
 
 #include "TabParent.h"
 
 #include "AppProcessChecker.h"
 #include "mozIApplication.h"
 #include "mozilla/BrowserElementParent.h"
 #include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/DataTransfer.h"
 #include "mozilla/dom/PContentPermissionRequestParent.h"
 #include "mozilla/dom/ServiceWorkerRegistrar.h"
 #include "mozilla/dom/indexedDB/ActorsParent.h"
 #include "mozilla/plugins/PluginWidgetParent.h"
 #include "mozilla/EventStateManager.h"
+#include "mozilla/gfx/2D.h"
 #include "mozilla/Hal.h"
 #include "mozilla/ipc/DocumentRendererParent.h"
 #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
 #include "mozilla/layers/CompositorParent.h"
 #include "mozilla/layers/InputAPZContext.h"
 #include "mozilla/layout/RenderFrameParent.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/net/NeckoChild.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/TouchEvents.h"
 #include "mozilla/unused.h"
+#include "BlobParent.h"
 #include "nsCOMPtr.h"
+#include "nsContentAreaDragDrop.h"
 #include "nsContentPermissionHelper.h"
 #include "nsContentUtils.h"
 #include "nsDebug.h"
 #include "nsFocusManager.h"
 #include "nsFrameLoader.h"
 #include "nsIBaseWindow.h"
 #include "nsIContent.h"
 #include "nsIDocShell.h"
@@ -71,16 +75,17 @@
 #include "StructuredCloneUtils.h"
 #include "ColorPickerParent.h"
 #include "FilePickerParent.h"
 #include "TabChild.h"
 #include "LoadContext.h"
 #include "nsNetCID.h"
 #include "nsIAuthInformation.h"
 #include "nsIAuthPromptCallback.h"
+#include "SourceSurfaceRawData.h"
 #include "nsAuthInformationHolder.h"
 #include "nsICancelable.h"
 #include "gfxPrefs.h"
 #include "nsILoginManagerPrompter.h"
 #include "nsPIWindowRoot.h"
 #include <algorithm>
 
 using namespace mozilla::dom;
@@ -269,16 +274,18 @@ TabParent::TabParent(nsIContentParent* a
   , mUpdatedDimensions(false)
   , mChromeOffset(0, 0)
   , mManager(aManager)
   , mMarkedDestroying(false)
   , mIsDestroyed(false)
   , mAppPackageFileDescriptorSent(false)
   , mSendOfflineStatus(true)
   , mChromeFlags(aChromeFlags)
+  , mDragAreaX(0)
+  , mDragAreaY(0)
   , mInitedByParent(false)
   , mTabId(aTabId)
   , mCreatingWindow(false)
   , mNeedLayerTreeReadyNotification(false)
 {
   MOZ_ASSERT(aManager);
 }
 
@@ -1197,19 +1204,30 @@ TabParent::GetLayoutDeviceToCSSScale()
   nsIDocument* doc = (content ? content->OwnerDoc() : nullptr);
   nsIPresShell* shell = (doc ? doc->GetShell() : nullptr);
   nsPresContext* ctx = (shell ? shell->GetPresContext() : nullptr);
   return LayoutDeviceToCSSScale(ctx
     ? (float)ctx->AppUnitsPerDevPixel() / nsPresContext::AppUnitsPerCSSPixel()
     : 0.0f);
 }
 
+bool
+TabParent::SendRealDragEvent(WidgetDragEvent& event, uint32_t aDragAction,
+                             uint32_t aDropEffect)
+{
+  if (mIsDestroyed) {
+    return false;
+  }
+  event.refPoint += GetChildProcessOffset();
+  return PBrowserParent::SendRealDragEvent(event, aDragAction, aDropEffect);
+}
+
 CSSPoint TabParent::AdjustTapToChildWidget(const CSSPoint& aPoint)
 {
-  return aPoint + (LayoutDevicePoint(mChildProcessOffsetAtTouchStart) * GetLayoutDeviceToCSSScale());
+  return aPoint + (LayoutDevicePoint(GetChildProcessOffset()) * GetLayoutDeviceToCSSScale());
 }
 
 bool TabParent::SendHandleSingleTap(const CSSPoint& aPoint, const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid)
 {
   if (mIsDestroyed) {
     return false;
   }
 
@@ -1366,19 +1384,16 @@ bool TabParent::SendRealKeyEvent(WidgetK
   return PBrowserParent::SendRealKeyEvent(event, bindings);
 }
 
 bool TabParent::SendRealTouchEvent(WidgetTouchEvent& event)
 {
   if (mIsDestroyed) {
     return false;
   }
-  if (event.message == NS_TOUCH_START) {
-    mChildProcessOffsetAtTouchStart = GetChildProcessOffset();
-  }
 
   // PresShell::HandleEventInternal adds touches on touch end/cancel.  This
   // confuses remote content and the panning and zooming logic into thinking
   // that the added touches are part of the touchend/cancel, when actually
   // they're not.
   if (event.message == NS_TOUCH_END || event.message == NS_TOUCH_CANCEL) {
     for (int i = event.touches.Length() - 1; i >= 0; i--) {
       if (!event.touches[i]->mChanged) {
@@ -2930,16 +2945,119 @@ TabParent::RecvAsyncAuthPrompt(const nsC
   nsCOMPtr<nsICancelable> dummy;
   nsresult rv =
     authPrompt->AsyncPromptAuth(channel, channel, nullptr,
                                 level, holder, getter_AddRefs(dummy));
 
   return rv == NS_OK;
 }
 
+bool
+TabParent::RecvInvokeDragSession(nsTArray<IPCDataTransfer>&& aTransfers,
+                                 const uint32_t& aAction,
+                                 const nsCString& aVisualDnDData,
+                                 const uint32_t& aWidth, const uint32_t& aHeight,
+                                 const uint32_t& aStride, const uint8_t& aFormat,
+                                 const int32_t& aDragAreaX, const int32_t& aDragAreaY)
+{
+  mInitialDataTransferItems.Clear();
+  nsPresContext* pc = mFrameElement->OwnerDoc()->GetShell()->GetPresContext();
+  EventStateManager* esm = pc->EventStateManager();
+
+  for (uint32_t i = 0; i < aTransfers.Length(); ++i) {
+    auto& items = aTransfers[i].items();
+    nsTArray<DataTransferItem>* itemArray = mInitialDataTransferItems.AppendElement();
+    for (uint32_t j = 0; j < items.Length(); ++j) {
+      const IPCDataTransferItem& item = items[j];
+      DataTransferItem* localItem = itemArray->AppendElement();
+      localItem->mFlavor = item.flavor();
+      if (item.data().type() == IPCDataTransferData::TnsString) {
+        localItem->mType = DataTransferItem::DataType::eString;
+        localItem->mStringData = item.data().get_nsString();
+      } else {
+        localItem->mType = DataTransferItem::DataType::eBlob;
+        BlobParent* blobParent =
+          static_cast<BlobParent*>(item.data().get_PBlobParent());
+        if (blobParent) {
+          localItem->mBlobData = blobParent->GetBlobImpl();
+        }
+      }
+    }
+  }
+  if (Manager()->IsContentParent()) {
+    nsCOMPtr<nsIDragService> dragService =
+      do_GetService("@mozilla.org/widget/dragservice;1");
+    if (dragService) {
+      dragService->MaybeAddChildProcess(Manager()->AsContentParent());
+    }
+  }
+
+  if (aVisualDnDData.IsEmpty()) {
+    mDnDVisualization = nullptr;
+  } else {
+    mDnDVisualization =
+      new mozilla::gfx::SourceSurfaceRawData();
+    mozilla::gfx::SourceSurfaceRawData* raw =
+      static_cast<mozilla::gfx::SourceSurfaceRawData*>(mDnDVisualization.get());
+    raw->InitWrappingData(
+      reinterpret_cast<uint8_t*>(const_cast<nsCString&>(aVisualDnDData).BeginWriting()),
+      mozilla::gfx::IntSize(aWidth, aHeight), aStride,
+      static_cast<mozilla::gfx::SurfaceFormat>(aFormat), false);
+    raw->GuaranteePersistance();
+  }
+  mDragAreaX = aDragAreaX;
+  mDragAreaY = aDragAreaY;
+  
+  esm->BeginTrackingRemoteDragGesture(mFrameElement);
+
+  return true;
+}
+
+void
+TabParent::AddInitialDnDDataTo(DataTransfer* aDataTransfer)
+{
+  for (uint32_t i = 0; i < mInitialDataTransferItems.Length(); ++i) {
+    nsTArray<DataTransferItem>& itemArray = mInitialDataTransferItems[i];
+    for (uint32_t j = 0; j < itemArray.Length(); ++j) {
+      DataTransferItem& item = itemArray[j];
+      nsCOMPtr<nsIWritableVariant> variant =
+        do_CreateInstance(NS_VARIANT_CONTRACTID);
+      if (!variant) {
+        break;
+      }
+      // Special case kFilePromiseMime so that we get the right
+      // nsIFlavorDataProvider for it.
+      if (item.mFlavor.EqualsLiteral(kFilePromiseMime)) {
+        nsRefPtr<nsISupports> flavorDataProvider =
+          new nsContentAreaDragDropDataProvider();
+        variant->SetAsISupports(flavorDataProvider);
+      } else if (item.mType == DataTransferItem::DataType::eString) {
+        variant->SetAsAString(item.mStringData);
+      } else if (item.mType == DataTransferItem::DataType::eBlob) {
+        variant->SetAsISupports(item.mBlobData);
+      }
+      // Using system principal here, since once the data is on parent process
+      // side, it can be handled as being from browser chrome or OS.
+      aDataTransfer->SetDataWithPrincipal(NS_ConvertUTF8toUTF16(item.mFlavor),
+                                          variant, i,
+                                          nsContentUtils::GetSystemPrincipal());
+    }
+  }
+  mInitialDataTransferItems.Clear();
+}
+
+void
+TabParent::TakeDragVisualization(RefPtr<mozilla::gfx::SourceSurface>& aSurface,
+                                 int32_t& aDragAreaX, int32_t& aDragAreaY)
+{
+  aSurface = mDnDVisualization.forget();
+  aDragAreaX = mDragAreaX;
+  aDragAreaY = mDragAreaY;
+}
+
 NS_IMETHODIMP
 FakeChannel::OnAuthAvailable(nsISupports *aContext, nsIAuthInformation *aAuthInfo)
 {
   nsAuthInformationHolder* holder =
     static_cast<nsAuthInformationHolder*>(aAuthInfo);
 
   if (!net::gNeckoChild->SendOnAuthAvailable(mCallbackId,
                                              holder->User(),
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -8,17 +8,19 @@
 #define mozilla_tabs_TabParent_h
 
 #include "js/TypeDecls.h"
 #include "mozilla/dom/ipc/IdType.h"
 #include "mozilla/dom/PBrowserParent.h"
 #include "mozilla/dom/PFilePickerParent.h"
 #include "mozilla/dom/TabContext.h"
 #include "mozilla/EventForwards.h"
+#include "mozilla/dom/File.h"
 #include "mozilla/WritingModes.h"
+#include "mozilla/RefPtr.h"
 #include "nsCOMPtr.h"
 #include "nsIAuthPromptProvider.h"
 #include "nsIBrowserDOMWindow.h"
 #include "nsIDOMEventListener.h"
 #include "nsISecureBrowserUI.h"
 #include "nsITabParent.h"
 #include "nsIXULBrowserWindow.h"
 #include "nsWeakReference.h"
@@ -47,21 +49,27 @@ struct TextureFactoryIdentifier;
 namespace layout {
 class RenderFrameParent;
 }
 
 namespace widget {
 struct IMENotification;
 }
 
+namespace gfx {
+class SourceSurface;
+class DataSourceSurface;
+}
+
 namespace dom {
 
 class ClonedMessageData;
 class nsIContentParent;
 class Element;
+class DataTransfer;
 struct StructuredCloneData;
 
 class TabParent final : public PBrowserParent
                       , public nsIDOMEventListener
                       , public nsITabParent
                       , public nsIAuthPromptProvider
                       , public nsISecureBrowserUI
                       , public nsSupportsWeakReference
@@ -265,16 +273,18 @@ public:
 
     void SendMouseEvent(const nsAString& aType, float aX, float aY,
                         int32_t aButton, int32_t aClickCount,
                         int32_t aModifiers, bool aIgnoreRootScrollFrame);
     void SendKeyEvent(const nsAString& aType, int32_t aKeyCode,
                       int32_t aCharCode, int32_t aModifiers,
                       bool aPreventDefault);
     bool SendRealMouseEvent(mozilla::WidgetMouseEvent& event);
+    bool SendRealDragEvent(mozilla::WidgetDragEvent& aEvent, uint32_t aDragAction,
+                           uint32_t aDropEffect);
     bool SendMouseWheelEvent(mozilla::WidgetWheelEvent& event);
     bool SendRealKeyEvent(mozilla::WidgetKeyboardEvent& event);
     bool SendRealTouchEvent(WidgetTouchEvent& event);
     bool SendHandleSingleTap(const CSSPoint& aPoint, const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid);
     bool SendHandleLongTap(const CSSPoint& aPoint, const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid, const uint64_t& aInputBlockId);
     bool SendHandleDoubleTap(const CSSPoint& aPoint, const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid);
 
     virtual PDocumentRendererParent*
@@ -361,16 +371,28 @@ public:
     bool SendLoadRemoteScript(const nsString& aURL,
                               const bool& aRunInGlobalScope);
 
     // See nsIFrameLoader requestNotifyLayerTreeReady.
     bool RequestNotifyLayerTreeReady();
     bool RequestNotifyLayerTreeCleared();
     bool LayerTreeUpdate(bool aActive);
 
+    virtual bool
+    RecvInvokeDragSession(nsTArray<IPCDataTransfer>&& aTransfers,
+                          const uint32_t& aAction,
+                          const nsCString& aVisualDnDData,
+                          const uint32_t& aWidth, const uint32_t& aHeight,
+                          const uint32_t& aStride, const uint8_t& aFormat,
+                          const int32_t& aDragAreaX, const int32_t& aDragAreaY) override;
+
+    void AddInitialDnDDataTo(DataTransfer* aDataTransfer);
+
+    void TakeDragVisualization(RefPtr<mozilla::gfx::SourceSurface>& aSurface,
+                               int32_t& aDragAreaX, int32_t& aDragAreaY);
 protected:
     bool ReceiveMessage(const nsString& aMessage,
                         bool aSync,
                         const StructuredCloneData* aCloneData,
                         mozilla::jsipc::CpowHolder* aCpows,
                         nsIPrincipal* aPrincipal,
                         InfallibleTArray<nsString>* aJSONRetVal = nullptr);
 
@@ -446,36 +468,49 @@ private:
     // |aOutTargetGuid| will contain the identifier
     // of the APZC instance that handled the event. aOutTargetGuid may be
     // null.
     // |aOutInputBlockId| will contain the identifier of the input block
     // that this event was added to, if there was one. aOutInputBlockId may
     // be null.
     void ApzAwareEventRoutingToChild(ScrollableLayerGuid* aOutTargetGuid,
                                      uint64_t* aOutInputBlockId);
-    // The offset for the child process which is sampled at touch start. This
-    // means that the touch events are relative to where the frame was at the
-    // start of the touch. We need to look for a better solution to this
-    // problem see bug 872911.
-    LayoutDeviceIntPoint mChildProcessOffsetAtTouchStart;
     // When true, we've initiated normal shutdown and notified our
     // managing PContent.
     bool mMarkedDestroying;
     // When true, the TabParent is invalid and we should not send IPC messages
     // anymore.
     bool mIsDestroyed;
     // Whether we have already sent a FileDescriptor for the app package.
     bool mAppPackageFileDescriptorSent;
 
     // Whether we need to send the offline status to the TabChild
     // This is true, until the first call of LoadURL
     bool mSendOfflineStatus;
 
     uint32_t mChromeFlags;
 
+    struct DataTransferItem
+    {
+      nsCString mFlavor;
+      nsString mStringData;
+      nsRefPtr<mozilla::dom::FileImpl> mBlobData;
+      enum DataType
+      {
+        eString,
+        eBlob
+      };
+      DataType mType;
+    };
+    nsTArray<nsTArray<DataTransferItem>> mInitialDataTransferItems;
+
+    mozilla::RefPtr<gfx::DataSourceSurface> mDnDVisualization;
+    int32_t mDragAreaX;
+    int32_t mDragAreaY;
+
     // When true, the TabParent is initialized without child side's request.
     // When false, the TabParent is initialized by window.open() from child side.
     bool mInitedByParent;
 
     nsCOMPtr<nsILoadContext> mLoadContext;
 
     // We keep a strong reference to the frameloader after we've sent the
     // Destroy message and before we've received __delete__. This allows us to
--- a/dom/ipc/moz.build
+++ b/dom/ipc/moz.build
@@ -131,16 +131,17 @@ LOCAL_INCLUDES += [
     '/dom/media/webspeech/synth/ipc',
     '/dom/mobilemessage/ipc',
     '/dom/storage',
     '/dom/workers',
     '/editor/libeditor',
     '/embedding/components/printingui/ipc',
     '/extensions/cookie',
     '/extensions/spellcheck/src',
+    '/gfx/2d',
     '/hal/sandbox',
     '/layout/base',
     '/netwerk/base',
     '/toolkit/xre',
     '/uriloader/exthandler',
     '/widget',
     '/xpcom/base',
     '/xpcom/threads',
--- a/dom/ipc/tests/test_NuwaProcessCreation.html
+++ b/dom/ipc/tests/test_NuwaProcessCreation.html
@@ -2,99 +2,66 @@
 <html>
 <!--
 Test if Nuwa process created successfully.
 -->
 <head>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
-<body>
+<body onload="setup()">
 
 <script type="application/javascript;version=1.7">
 "use strict";
 
-SimpleTest.waitForExplicitFinish();
-SimpleTest.requestFlakyTimeout("untriaged");
-
-function TestLoader() {}
-
-TestLoader.prototype = {
-  _waitingTask: 0,
-  onTestReady: null,
-  unlockTestReady: function() {
-    this._waitingTask--;
-    this._maybeLoadTest();
-  },
-  lockTestReady: function() {
-    this._waitingTask++;
-  },
-  _maybeLoadTest: function() {
-    if (this._waitingTask == 0) {
-      this.onTestReady();
-    }
-  }
-}
-
-var testLoader = new TestLoader();
-testLoader.lockTestReady();
-window.addEventListener('load', function() {
-  testLoader.unlockTestReady();
-});
-
-function setPref(pref, value) {
-  testLoader.lockTestReady();
-  if (value !== undefined && value !== null) {
-    SpecialPowers.pushPrefEnv({'set': [[pref, value]]}, function() { testLoader.unlockTestReady(); });
-  } else {
-    SpecialPowers.pushPrefEnv({'clear': [[pref]]}, function() { testLoader.unlockTestReady(); });
-  }
-}
-
-setPref('dom.ipc.processPriorityManager.testMode', true);
-setPref('dom.ipc.processPriorityManager.enabled', true);
-setPref('dom.ipc.processPriorityManager.BACKGROUND.LRUPoolLevels', 2);
-
 function runTest()
 {
-  // Shutdown preallocated process.
-  SpecialPowers.setBoolPref('dom.ipc.processPrelaunch.enabled', false);
+  info("Launch the Nuwa process");
   let cpmm = SpecialPowers.Cc["@mozilla.org/childprocessmessagemanager;1"]
                           .getService(SpecialPowers.Ci.nsISyncMessageSender);
   let seenNuwaReady = false;
   let msgHandler = {
     receiveMessage: function receiveMessage(msg) {
       msg = SpecialPowers.wrap(msg);
       if (msg.name == 'TEST-ONLY:nuwa-ready') {
         ok(true, "Got nuwa-ready");
         is(seenNuwaReady, false, "Already received nuwa ready");
         seenNuwaReady = true;
       } else if (msg.name == 'TEST-ONLY:nuwa-add-new-process') {
         ok(true, "Got nuwa-add-new-process");
         is(seenNuwaReady, true, "Receive nuwa-add-new-process before nuwa-ready");
-        testEnd();
+        shutdown();
       }
     }
   };
-  let timeout = setTimeout(function() {
-    ok(false, "Nuwa process is not launched");
-    testEnd();
-  }, 240000);
 
-  function testEnd() {
+  function shutdown() {
+    info("Shut down the test case");
     cpmm.removeMessageListener("TEST-ONLY:nuwa-ready", msgHandler);
     cpmm.removeMessageListener("TEST-ONLY:nuwa-add-new-process", msgHandler);
-    clearTimeout(timeout);
+
     SimpleTest.finish();
   }
 
   cpmm.addMessageListener("TEST-ONLY:nuwa-ready", msgHandler);
   cpmm.addMessageListener("TEST-ONLY:nuwa-add-new-process", msgHandler);
 
 
   // Setting this pref to true should cause us to prelaunch a process.
   SpecialPowers.setBoolPref('dom.ipc.processPrelaunch.enabled', true);
 }
 
-testLoader.onTestReady = runTest;
+function setup()
+{
+  info("Set up preferences for testing the Nuwa process.");
+  SimpleTest.waitForExplicitFinish();
+
+  SpecialPowers.pushPrefEnv({
+    'set': [
+      ['dom.ipc.processPrelaunch.enabled', false],
+      ['dom.ipc.preallocatedProcessManager.testMode', true]
+    ]
+  }, runTest);
+}
+
 </script>
 </body>
 </html>
--- a/dom/ipc/tests/test_NuwaProcessDeadlock.html
+++ b/dom/ipc/tests/test_NuwaProcessDeadlock.html
@@ -2,101 +2,67 @@
 <html>
 <!--
 Test if Nuwa process created successfully.
 -->
 <head>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
-<body>
+<body onload="setup()">
 
 <script type="application/javascript;version=1.7">
 "use strict";
 
-SimpleTest.waitForExplicitFinish();
-SimpleTest.requestFlakyTimeout("untriaged");
-
-function TestLoader() {}
-
-TestLoader.prototype = {
-  _waitingTask: 0,
-  onTestReady: null,
-  unlockTestReady: function() {
-    this._waitingTask--;
-    this._maybeLoadTest();
-  },
-  lockTestReady: function() {
-    this._waitingTask++;
-  },
-  _maybeLoadTest: function() {
-    if (this._waitingTask == 0) {
-      this.onTestReady();
-    }
-  }
-}
-
-var testLoader = new TestLoader();
-testLoader.lockTestReady();
-window.addEventListener('load', function() {
-  testLoader.unlockTestReady();
-});
-
-function setPref(pref, value) {
-  testLoader.lockTestReady();
-  if (value !== undefined && value !== null) {
-    SpecialPowers.pushPrefEnv({'set': [[pref, value]]}, function() { testLoader.unlockTestReady(); });
-  } else {
-    SpecialPowers.pushPrefEnv({'clear': [[pref]]}, function() { testLoader.unlockTestReady(); });
-  }
-}
-
-setPref('dom.ipc.processPriorityManager.testMode', true);
-setPref('dom.ipc.processPriorityManager.enabled', true);
-setPref('dom.ipc.processPriorityManager.BACKGROUND.LRUPoolLevels', 2);
-setPref('dom.ipc.processPrelaunch.testMode', true);  // For testing deadlock.
-
 function runTest()
 {
-  // Shutdown preallocated process.
-  SpecialPowers.setBoolPref('dom.ipc.processPrelaunch.enabled', false);
+  info("Launch the Nuwa process");
   let cpmm = SpecialPowers.Cc["@mozilla.org/childprocessmessagemanager;1"]
                           .getService(SpecialPowers.Ci.nsISyncMessageSender);
   let seenNuwaReady = false;
   let msgHandler = {
     receiveMessage: function receiveMessage(msg) {
       msg = SpecialPowers.wrap(msg);
       if (msg.name == 'TEST-ONLY:nuwa-ready') {
         ok(true, "Got nuwa-ready");
         is(seenNuwaReady, false, "Already received nuwa ready");
         seenNuwaReady = true;
       } else if (msg.name == 'TEST-ONLY:nuwa-add-new-process') {
         ok(true, "Got nuwa-add-new-process");
         is(seenNuwaReady, true, "Receive nuwa-add-new-process before nuwa-ready");
-        testEnd();
+        shutdown();
       }
     }
   };
-  let timeout = setTimeout(function() {
-    ok(false, "Nuwa process is not launched");
-    testEnd();
-  }, 240000);
 
-  function testEnd() {
+  function shutdown() {
+    info("Shut down the test case");
     cpmm.removeMessageListener("TEST-ONLY:nuwa-ready", msgHandler);
     cpmm.removeMessageListener("TEST-ONLY:nuwa-add-new-process", msgHandler);
-    clearTimeout(timeout);
-    setPref('dom.ipc.processPrelaunch.testMode', false);
+
     SimpleTest.finish();
   }
 
   cpmm.addMessageListener("TEST-ONLY:nuwa-ready", msgHandler);
   cpmm.addMessageListener("TEST-ONLY:nuwa-add-new-process", msgHandler);
 
 
   // Setting this pref to true should cause us to prelaunch a process.
   SpecialPowers.setBoolPref('dom.ipc.processPrelaunch.enabled', true);
 }
 
-testLoader.onTestReady = runTest;
+function setup()
+{
+  info("Set up preferences for testing deadlock in the Nuwa process.");
+  SimpleTest.waitForExplicitFinish();
+
+  SpecialPowers.pushPrefEnv({
+    'set': [
+      ['dom.ipc.processPrelaunch.enabled', false],
+      ['dom.ipc.preallocatedProcessManager.testMode', true],
+      ['dom.ipc.processPrelaunch.testMode', true]  // For testing deadlock
+    ]
+  }, runTest);
+}
+
 </script>
 </body>
 </html>
--- a/dom/media/AbstractThread.cpp
+++ b/dom/media/AbstractThread.cpp
@@ -1,53 +1,71 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "AbstractThread.h"
 
+#include "MediaTaskQueue.h"
 #include "nsThreadUtils.h"
 
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/StaticPtr.h"
 
 namespace mozilla {
 
 StaticRefPtr<AbstractThread> sMainThread;
 
 template<>
 nsresult
 AbstractThreadImpl<nsIThread>::Dispatch(already_AddRefed<nsIRunnable> aRunnable)
 {
+  MediaTaskQueue::AssertInTailDispatchIfNeeded();
   nsCOMPtr<nsIRunnable> r = aRunnable;
   return mTarget->Dispatch(r, NS_DISPATCH_NORMAL);
 }
 
 template<>
 bool
 AbstractThreadImpl<nsIThread>::IsCurrentThreadIn()
 {
-  return NS_GetCurrentThread() == mTarget;
+  bool in = NS_GetCurrentThread() == mTarget;
+  MOZ_ASSERT_IF(in, MediaTaskQueue::GetCurrentQueue() == nullptr);
+  return in;
 }
 
+void
+AbstractThread::MaybeTailDispatch(already_AddRefed<nsIRunnable> aRunnable,
+                                  bool aAssertDispatchSuccess)
+{
+  MediaTaskQueue* currentQueue = MediaTaskQueue::GetCurrentQueue();
+  if (currentQueue && currentQueue->RequiresTailDispatch()) {
+    currentQueue->TailDispatcher().AddTask(this, Move(aRunnable), aAssertDispatchSuccess);
+  } else {
+    nsresult rv = Dispatch(Move(aRunnable));
+    MOZ_DIAGNOSTIC_ASSERT(!aAssertDispatchSuccess || NS_SUCCEEDED(rv));
+    unused << rv;
+  }
+}
+
+
 AbstractThread*
 AbstractThread::MainThread()
 {
   MOZ_ASSERT(sMainThread);
   return sMainThread;
 }
 
 void
-AbstractThread::EnsureMainThreadSingleton()
+AbstractThread::InitStatics()
 {
   MOZ_ASSERT(NS_IsMainThread());
-  if (!sMainThread) {
-    nsCOMPtr<nsIThread> mainThread;
-    NS_GetMainThread(getter_AddRefs(mainThread));
-    MOZ_DIAGNOSTIC_ASSERT(mainThread);
-    sMainThread = AbstractThread::Create(mainThread.get());
-    ClearOnShutdown(&sMainThread);
-  }
+  MOZ_ASSERT(!sMainThread);
+  nsCOMPtr<nsIThread> mainThread;
+  NS_GetMainThread(getter_AddRefs(mainThread));
+  MOZ_DIAGNOSTIC_ASSERT(mainThread);
+  sMainThread = AbstractThread::Create(mainThread.get());
+  ClearOnShutdown(&sMainThread);
 }
 
 } // namespace mozilla
--- a/dom/media/AbstractThread.h
+++ b/dom/media/AbstractThread.h
@@ -30,24 +30,28 @@ namespace mozilla {
  */
 class AbstractThread
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AbstractThread);
   virtual nsresult Dispatch(already_AddRefed<nsIRunnable> aRunnable) = 0;
   virtual bool IsCurrentThreadIn() = 0;
 
+  // Convenience method for dispatching a runnable when we may be running on
+  // a thread that requires runnables to be dispatched with tail dispatch.
+  void MaybeTailDispatch(already_AddRefed<nsIRunnable> aRunnable,
+                         bool aAssertDispatchSuccess = true);
+
   template<typename TargetType> static AbstractThread* Create(TargetType* aTarget);
 
   // Convenience method for getting an AbstractThread for the main thread.
-  //
-  // EnsureMainThreadSingleton must be called on the main thread before any
-  // other threads that might use MainThread() are spawned.
   static AbstractThread* MainThread();
-  static void EnsureMainThreadSingleton();
+
+  // Must be called exactly once during startup.
+  static void InitStatics();
 
 protected:
   virtual ~AbstractThread() {}
 };
 
 template<typename TargetType>
 class AbstractThreadImpl : public AbstractThread
 {
--- a/dom/media/AudioSink.cpp
+++ b/dom/media/AudioSink.cpp
@@ -197,17 +197,17 @@ AudioSink::AudioLoop()
       // time.
       missingFrames = std::min<int64_t>(UINT32_MAX, missingFrames.value());
       mWritten += PlaySilence(static_cast<uint32_t>(missingFrames.value()));
     } else {
       mWritten += PlayFromAudioQueue();
     }
     int64_t endTime = GetEndTime();
     if (endTime != -1) {
-      mStateMachine->OnAudioEndTimeUpdate(endTime);
+      mStateMachine->DispatchOnAudioEndTimeUpdate(endTime);
     }
   }
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   MOZ_ASSERT(mStopAudioThread || AudioQueue().AtEndOfStream());
   if (!mStopAudioThread && mPlaying) {
     Drain();
   }
   SINK_LOG("AudioLoop complete");
@@ -251,17 +251,17 @@ AudioSink::Drain()
 }
 
 void
 AudioSink::Cleanup()
 {
   AssertCurrentThreadInMonitor();
   nsRefPtr<AudioStream> audioStream;
   audioStream.swap(mAudioStream);
-  mStateMachine->OnAudioSinkComplete();
+  mStateMachine->DispatchOnAudioSinkComplete();
 
   ReentrantMonitorAutoExit exit(GetReentrantMonitor());
   audioStream->Shutdown();
 }
 
 bool
 AudioSink::ExpectMoreAudioData()
 {
@@ -329,17 +329,17 @@ AudioSink::PlayFromAudioQueue()
 
   SINK_LOG_V("playing %u frames of audio at time %lld",
              audio->mFrames, audio->mTime);
   mAudioStream->Write(audio->mAudioData, audio->mFrames);
 
   StartAudioStreamPlaybackIfNeeded();
 
   if (audio->mOffset != -1) {
-    mStateMachine->OnPlaybackOffsetUpdate(audio->mOffset);
+    mStateMachine->DispatchOnPlaybackOffsetUpdate(audio->mOffset);
   }
   return audio->mFrames;
 }
 
 void
 AudioSink::UpdateStreamSettings()
 {
   AssertCurrentThreadInMonitor();
--- a/dom/media/MediaData.cpp
+++ b/dom/media/MediaData.cpp
@@ -10,16 +10,17 @@
 #include "mozilla/layers/TextureClient.h"
 #endif
 #include "VideoUtils.h"
 #include "ImageContainer.h"
 
 #ifdef MOZ_WIDGET_GONK
 #include <cutils/properties.h>
 #endif
+#include <stdint.h>
 
 namespace mozilla {
 
 using namespace mozilla::gfx;
 using layers::ImageContainer;
 using layers::PlanarYCbCrImage;
 using layers::PlanarYCbCrData;
 
@@ -98,38 +99,40 @@ IsInEmulator()
 {
   char propQemu[PROPERTY_VALUE_MAX];
   property_get("ro.kernel.qemu", propQemu, "");
   return !strncmp(propQemu, "1", 1);
 }
 
 #endif
 
-VideoData::VideoData(int64_t aOffset, int64_t aTime, int64_t aDuration, int64_t aTimecode)
-  : MediaData(VIDEO_DATA, aOffset, aTime, aDuration),
-    mTimecode(aTimecode),
-    mDuplicate(true),
-    mKeyframe(false)
+VideoData::VideoData(int64_t aOffset,
+                     int64_t aTime,
+                     int64_t aDuration,
+                     int64_t aTimecode)
+  : MediaData(VIDEO_DATA, aOffset, aTime, aDuration)
+  , mDuplicate(true)
 {
   NS_ASSERTION(mDuration >= 0, "Frame must have non-negative duration.");
+  mTimecode = aTimecode;
 }
 
 VideoData::VideoData(int64_t aOffset,
                      int64_t aTime,
                      int64_t aDuration,
                      bool aKeyframe,
                      int64_t aTimecode,
                      IntSize aDisplay)
-  : MediaData(VIDEO_DATA, aOffset, aTime, aDuration),
-    mDisplay(aDisplay),
-    mTimecode(aTimecode),
-    mDuplicate(false),
-    mKeyframe(aKeyframe)
+  : MediaData(VIDEO_DATA, aOffset, aTime, aDuration)
+  , mDisplay(aDisplay)
+  , mDuplicate(false)
 {
   NS_ASSERTION(mDuration >= 0, "Frame must have non-negative duration.");
+  mKeyframe = aKeyframe;
+  mTimecode = aTimecode;
 }
 
 VideoData::~VideoData()
 {
 }
 
 size_t
 VideoData::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
@@ -470,9 +473,180 @@ VideoData::Create(VideoInfo& aInfo,
   data.mGraphicBuffer = aBuffer;
 
   videoImage->SetData(data);
 
   return v.forget();
 }
 #endif  // MOZ_OMX_DECODER
 
+// Alignment value - 1. 0 means that data isn't aligned.
+// For 32-bytes aligned, use 31U.
+#define RAW_DATA_ALIGNMENT 31U
+
+#define RAW_DATA_DEFAULT_SIZE 4096
+
+MediaRawData::MediaRawData()
+  : MediaData(RAW_DATA)
+  , mData(nullptr)
+  , mSize(0)
+  , mBuffer(new LargeDataBuffer(RAW_DATA_DEFAULT_SIZE))
+  , mPadding(0)
+{
+}
+
+MediaRawData::MediaRawData(const uint8_t* aData, size_t aSize)
+  : MediaData(RAW_DATA)
+  , mData(nullptr)
+  , mSize(0)
+  , mBuffer(new LargeDataBuffer(RAW_DATA_DEFAULT_SIZE))
+  , mPadding(0)
+{
+  if (!EnsureCapacity(aSize)) {
+    return;
+  }
+  mBuffer->AppendElements(aData, aSize);
+  mBuffer->AppendElements(RAW_DATA_ALIGNMENT);
+  mSize = aSize;
+}
+
+already_AddRefed<MediaRawData>
+MediaRawData::Clone() const
+{
+  nsRefPtr<MediaRawData> s = new MediaRawData;
+  s->mTimecode = mTimecode;
+  s->mTime = mTime;
+  s->mDuration = mDuration;
+  s->mOffset = mOffset;
+  s->mKeyframe = mKeyframe;
+  s->mExtraData = mExtraData;
+  if (mSize) {
+    if (!s->EnsureCapacity(mSize)) {
+      return nullptr;
+    }
+    s->mBuffer->AppendElements(mData, mSize);
+    s->mBuffer->AppendElements(RAW_DATA_ALIGNMENT);
+    s->mSize = mSize;
+  }
+  return s.forget();
+}
+
+bool
+MediaRawData::EnsureCapacity(size_t aSize)
+{
+  if (mData && mBuffer->Capacity() >= aSize + RAW_DATA_ALIGNMENT * 2) {
+    return true;
+  }
+  if (!mBuffer->SetCapacity(aSize + RAW_DATA_ALIGNMENT * 2)) {
+    return false;
+  }
+  // Find alignment address.
+  const uintptr_t alignmask = RAW_DATA_ALIGNMENT;
+  mData = reinterpret_cast<uint8_t*>(
+    (reinterpret_cast<uintptr_t>(mBuffer->Elements()) + alignmask) & ~alignmask);
+  MOZ_ASSERT(uintptr_t(mData) % (RAW_DATA_ALIGNMENT+1) == 0);
+
+  // Shift old data according to new padding.
+  uint32_t oldpadding = int32_t(mPadding);
+  mPadding = mData - mBuffer->Elements();
+  int32_t shift = int32_t(mPadding) - int32_t(oldpadding);
+
+  if (shift == 0) {
+    // Nothing to do.
+  } else if (shift > 0) {
+    mBuffer->InsertElementsAt(oldpadding, shift);
+  } else {
+    mBuffer->RemoveElementsAt(mPadding, -shift);
+  }
+  return true;
+}
+
+MediaRawData::~MediaRawData()
+{
+}
+
+size_t
+MediaRawData::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  size_t size = aMallocSizeOf(this);
+
+  if (mExtraData) {
+    size += mExtraData->SizeOfIncludingThis(aMallocSizeOf);
+  }
+  size += mBuffer->SizeOfIncludingThis(aMallocSizeOf);
+  return size;
+}
+
+MediaRawDataWriter*
+MediaRawData::CreateWriter()
+{
+  return new MediaRawDataWriter(this);
+}
+
+MediaRawDataWriter::MediaRawDataWriter(MediaRawData* aMediaRawData)
+  : mData(nullptr)
+  , mSize(0)
+  , mTarget(aMediaRawData)
+  , mBuffer(aMediaRawData->mBuffer.get())
+{
+  if (aMediaRawData->mData) {
+    mData = mBuffer->Elements() + mTarget->mPadding;
+    mSize = mTarget->mSize;
+  }
+}
+
+bool
+MediaRawDataWriter::EnsureSize(size_t aSize)
+{
+  if (aSize <= mSize) {
+    return true;
+  }
+  if (!mTarget->EnsureCapacity(aSize)) {
+    return false;
+  }
+  mData = mBuffer->Elements() + mTarget->mPadding;
+  return true;
+}
+
+bool
+MediaRawDataWriter::SetSize(size_t aSize)
+{
+  if (aSize > mTarget->mSize && !EnsureSize(aSize)) {
+    return false;
+  }
+  // Pad our buffer.
+  mBuffer->SetLength(aSize + mTarget->mPadding + RAW_DATA_ALIGNMENT);
+  mTarget->mSize = mSize = aSize;
+  return true;
+}
+
+bool
+MediaRawDataWriter::Prepend(const uint8_t* aData, size_t aSize)
+{
+  if (!EnsureSize(aSize + mTarget->mSize)) {
+    return false;
+  }
+  mBuffer->InsertElementsAt(mTarget->mPadding, aData, aSize);
+  mTarget->mSize += aSize;
+  mSize = mTarget->mSize;
+  return true;
+}
+
+bool
+MediaRawDataWriter::Replace(const uint8_t* aData, size_t aSize)
+{
+  if (!EnsureSize(aSize)) {
+    return false;
+  }
+  mBuffer->ReplaceElementsAt(mTarget->mPadding, mTarget->mSize, aData, aSize);
+  mTarget->mSize = mSize = aSize;
+  return true;
+}
+
+void
+MediaRawDataWriter::Clear()
+{
+  mBuffer->RemoveElementsAt(mTarget->mPadding, mTarget->mSize);
+  mTarget->mSize = mSize = 0;
+  mTarget->mData = mData = nullptr;
+}
+
 } // namespace mozilla
--- a/dom/media/MediaData.h
+++ b/dom/media/MediaData.h
@@ -17,57 +17,79 @@
 
 namespace mozilla {
 
 namespace layers {
 class Image;
 class ImageContainer;
 }
 
+class LargeDataBuffer;
+class DataBuffer;
+
 // Container that holds media samples.
 class MediaData {
 public:
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaData)
 
   enum Type {
     AUDIO_DATA = 0,
-    VIDEO_DATA
+    VIDEO_DATA,
+    RAW_DATA
   };
 
   MediaData(Type aType,
             int64_t aOffset,
             int64_t aTimestamp,
             int64_t aDuration)
     : mType(aType)
     , mOffset(aOffset)
     , mTime(aTimestamp)
+    , mTimecode(aTimestamp)
     , mDuration(aDuration)
+    , mKeyframe(false)
     , mDiscontinuity(false)
   {}
 
   // Type of contained data.
   const Type mType;
 
   // Approximate byte offset where this data was demuxed from its media.
-  const int64_t mOffset;
+  int64_t mOffset;
 
   // Start time of sample, in microseconds.
-  const int64_t mTime;
+  int64_t mTime;
+
+  // Codec specific internal time code. For Ogg based codecs this is the
+  // granulepos.
+  int64_t mTimecode;
 
   // Duration of sample, in microseconds.
-  const int64_t mDuration;
+  int64_t mDuration;
+
+  bool mKeyframe;
 
   // True if this is the first sample after a gap or discontinuity in
   // the stream. This is true for the first sample in a stream after a seek.
   bool mDiscontinuity;
 
   int64_t GetEndTime() const { return mTime + mDuration; }
 
 protected:
+  explicit MediaData(Type aType)
+    : mType(aType)
+    , mOffset(0)
+    , mTime(0)
+    , mTimecode(0)
+    , mDuration(0)
+    , mKeyframe(false)
+    , mDiscontinuity(false)
+  {}
+
   virtual ~MediaData() {}
 
 };
 
 // Holds chunk a decoded audio frames.
 class AudioData : public MediaData {
 public:
 
@@ -250,27 +272,22 @@ public:
 
   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
 
   // Dimensions at which to display the video frame. The picture region
   // will be scaled to this size. This is should be the picture region's
   // dimensions scaled with respect to its aspect ratio.
   const IntSize mDisplay;
 
-  // Codec specific internal time code. For Ogg based codecs this is the
-  // granulepos.
-  const int64_t mTimecode;
-
   // This frame's image.
   nsRefPtr<Image> mImage;
 
   // When true, denotes that this frame is identical to the frame that
   // came before; it's a duplicate. mBuffer will be empty.
   const bool mDuplicate;
-  const bool mKeyframe;
 
   VideoData(int64_t aOffset,
             int64_t aTime,
             int64_t aDuration,
             int64_t aTimecode);
 
   VideoData(int64_t aOffset,
             int64_t aTime,
@@ -278,20 +295,136 @@ public:
             bool aKeyframe,
             int64_t aTimecode,
             IntSize aDisplay);
 
 protected:
   ~VideoData();
 };
 
+class CryptoTrack
+{
+public:
+  CryptoTrack() : mValid(false) {}
+  bool mValid;
+  int32_t mMode;
+  int32_t mIVSize;
+  nsTArray<uint8_t> mKeyId;
+};
+
+class CryptoSample : public CryptoTrack
+{
+public:
+  nsTArray<uint16_t> mPlainSizes;
+  nsTArray<uint32_t> mEncryptedSizes;
+  nsTArray<uint8_t> mIV;
+  nsTArray<nsCString> mSessionIds;
+};
+
+
+// MediaRawData is a MediaData container used to store demuxed, still compressed
+// samples.
+// Use MediaRawData::CreateWriter() to obtain a MediaRawDataWriter object that
+// provides methods to modify and manipulate the data.
+// Memory allocations are fallibles. Methods return a boolean indicating if
+// memory allocations were successful. Return values should always be checked.
+// MediaRawData::mData will be nullptr if no memory has been allocated or if
+// an error occurred during construction.
+// Existing data is only ever modified if new memory allocation has succeeded
+// and preserved if not.
+//
+// The memory referenced by mData will always be 32 bytes aligned and the
+// underlying buffer will always have a size such that 32 bytes blocks can be
+// used to read the content, regardless of the mSize value. Buffer is zeroed
+// on creation.
+//
+// Typical usage: create new MediaRawData; create the associated
+// MediaRawDataWriter, call SetSize() to allocate memory, write to mData,
+// up to mSize bytes.
+
+class MediaRawData;
+
+class MediaRawDataWriter
+{
+public:
+  // Pointer to data or null if not-yet allocated
+  uint8_t* mData;
+  // Writeable size of buffer.
+  size_t mSize;
+
+  // Data manipulation methods. mData and mSize may be updated accordingly.
+
+  // Set size of buffer, allocating memory as required.
+  // If size is increased, new buffer area is filled with 0.
+  bool SetSize(size_t aSize);
+  // Add aData at the beginning of buffer.
+  bool Prepend(const uint8_t* aData, size_t aSize);
+  // Replace current content with aData.
+  bool Replace(const uint8_t* aData, size_t aSize);
+  // Clear the memory buffer. Will set mData and mSize to 0.
+  void Clear();
+
+private:
+  friend class MediaRawData;
+  explicit MediaRawDataWriter(MediaRawData* aMediaRawData);
+  bool EnsureSize(size_t aSize);
+  MediaRawData* mTarget;
+  nsRefPtr<LargeDataBuffer> mBuffer;
+};
+
+class MediaRawData : public MediaData {
+public:
+  MediaRawData();
+  MediaRawData(const uint8_t* aData, size_t mSize);
+
+  // Pointer to data or null if not-yet allocated
+  const uint8_t* mData;
+  // Size of buffer.
+  size_t mSize;
+
+  CryptoSample mCrypto;
+  nsRefPtr<DataBuffer> mExtraData;
+
+  // Return a deep copy or nullptr if out of memory.
+  virtual already_AddRefed<MediaRawData> Clone() const;
+  // Create a MediaRawDataWriter for this MediaRawData. The caller must
+  // delete the writer once done. The writer is not thread-safe.
+  virtual MediaRawDataWriter* CreateWriter();
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
+
+protected:
+  ~MediaRawData();
+
+private:
+  friend class MediaRawDataWriter;
+  // Ensure that the backend buffer can hold aSize data. Will update mData.
+  // Will enforce that the start of allocated data is always 32 bytes
+  // aligned and that it has sufficient end padding to allow for 32 bytes block
+  // read as required by some data decoders.
+  // Returns false if memory couldn't be allocated.
+  bool EnsureCapacity(size_t aSize);
+  nsRefPtr<LargeDataBuffer> mBuffer;
+  uint32_t mPadding;
+  MediaRawData(const MediaRawData&); // Not implemented
+};
+
   // LargeDataBuffer is a ref counted fallible TArray.
   // It is designed to share potentially big byte arrays.
 class LargeDataBuffer : public FallibleTArray<uint8_t> {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(LargeDataBuffer);
+  LargeDataBuffer() = default;
+  explicit LargeDataBuffer(size_t aCapacity) : FallibleTArray<uint8_t>(aCapacity) {}
 
 private:
   ~LargeDataBuffer() {}
 };
 
+  // DataBuffer is a ref counted infallible TArray.
+class DataBuffer : public nsTArray<uint8_t> {
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DataBuffer);
+
+private:
+  ~DataBuffer() {}
+};
+
 } // namespace mozilla
 
 #endif // MediaData_h
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -112,16 +112,24 @@ public:
     if (decoders.IsEmpty()) {
       sUniqueInstance = nullptr;
     }
   }
 };
 
 StaticRefPtr<MediaMemoryTracker> MediaMemoryTracker::sUniqueInstance;
 
+void
+MediaDecoder::InitStatics()
+{
+  AbstractThread::InitStatics();
+
+  MediaTaskQueue::InitStatics();
+}
+
 NS_IMPL_ISUPPORTS(MediaMemoryTracker, nsIMemoryReporter)
 
 NS_IMPL_ISUPPORTS(MediaDecoder, nsIObserver)
 
 void MediaDecoder::NotifyOwnerActivityChanged()
 {
   MOZ_ASSERT(NS_IsMainThread());
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
@@ -607,17 +615,16 @@ MediaDecoder::MediaDecoder() :
   mHeuristicDormantTimeout(
     Preferences::GetInt("media.decoder.heuristic.dormant.timeout",
                         DEFAULT_HEURISTIC_DORMANT_TIMEOUT_MSECS)),
   mIsHeuristicDormant(false)
 {
   MOZ_COUNT_CTOR(MediaDecoder);
   MOZ_ASSERT(NS_IsMainThread());
   MediaMemoryTracker::AddMediaDecoder(this);
-  AbstractThread::EnsureMainThreadSingleton();
 #ifdef PR_LOGGING
   if (!gMediaDecoderLog) {
     gMediaDecoderLog = PR_NewLogModule("MediaDecoder");
   }
 #endif
   mAudioChannel = AudioChannelService::GetDefaultAudioChannel();
 }
 
@@ -1534,17 +1541,17 @@ void MediaDecoder::SetLoadInBackground(b
   MOZ_ASSERT(NS_IsMainThread());
   if (mResource) {
     mResource->SetLoadInBackground(aLoadInBackground);
   }
 }
 
 void MediaDecoder::UpdatePlaybackOffset(int64_t aOffset)
 {
-  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
+  GetReentrantMonitor().AssertCurrentThreadIn();
   mPlaybackPosition = aOffset;
 }
 
 bool MediaDecoder::OnStateMachineTaskQueue() const
 {
   return mDecoderStateMachine->OnTaskQueue();
 }
 
--- a/dom/media/MediaDecoder.h
+++ b/dom/media/MediaDecoder.h
@@ -293,16 +293,19 @@ public:
     PLAY_STATE_LOADING,
     PLAY_STATE_PAUSED,
     PLAY_STATE_PLAYING,
     PLAY_STATE_SEEKING,
     PLAY_STATE_ENDED,
     PLAY_STATE_SHUTDOWN
   };
 
+  // Must be called exactly once, on the main thread, during startup.
+  static void InitStatics();
+
   MediaDecoder();
 
   // Reset the decoder and notify the media element that
   // server connection is closed.
   virtual void ResetConnectionState();
   // Create a new decoder of the same type as this one.
   // Subclasses must implement this.
   virtual MediaDecoder* Clone() = 0;
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -42,16 +42,18 @@
 #include <algorithm>
 
 namespace mozilla {
 
 using namespace mozilla::layers;
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 
+#define NS_DispatchToMainThread(...) CompileError_UseTailDispatchInstead
+
 // avoid redefined macro in unified build
 #undef DECODER_LOG
 #undef VERBOSE_LOG
 
 #ifdef PR_LOGGING
 extern PRLogModuleInfo* gMediaDecoderLog;
 #define DECODER_LOG(x, ...) \
   PR_LOG(gMediaDecoderLog, PR_LOG_DEBUG, ("Decoder=%p " x, mDecoder.get(), ##__VA_ARGS__))
@@ -238,25 +240,26 @@ MediaDecoderStateMachine::MediaDecoderSt
   mDropVideoUntilNextDiscontinuity(false),
   mDecodeToSeekTarget(false),
   mCurrentTimeBeforeSeek(0),
   mLastFrameStatus(MediaDecoderOwner::NEXT_FRAME_UNINITIALIZED),
   mCorruptFrames(30),
   mDisabledHardwareAcceleration(false),
   mDecodingFrozenAtStateDecoding(false),
   mSentLoadedMetadataEvent(false),
-  mSentFirstFrameLoadedEvent(false)
+  mSentFirstFrameLoadedEvent(false),
+  mSentPlaybackEndedEvent(false)
 {
   MOZ_COUNT_CTOR(MediaDecoderStateMachine);
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
 
   // Set up our task queue.
   RefPtr<SharedThreadPool> pool(GetMediaThreadPool());
   MOZ_DIAGNOSTIC_ASSERT(pool);
-  mTaskQueue = new MediaTaskQueue(pool.forget());
+  mTaskQueue = new MediaTaskQueue(pool.forget(), /* aAssertTailDispatch = */ true);
 
   static bool sPrefCacheInit = false;
   if (!sPrefCacheInit) {
     sPrefCacheInit = true;
     Preferences::AddUintVarCache(&sVideoQueueDefaultSize,
                                  "media.video-queue.default-size",
                                  MAX_VIDEO_QUEUE_SIZE);
     Preferences::AddUintVarCache(&sVideoQueueHWAccelSize,
@@ -791,16 +794,17 @@ MediaDecoderStateMachine::OnAudioDecoded
       return;
     }
   }
 }
 
 void
 MediaDecoderStateMachine::Push(AudioData* aSample)
 {
+  MOZ_ASSERT(OnTaskQueue());
   MOZ_ASSERT(aSample);
   // TODO: Send aSample to MSG and recalculate readystate before pushing,
   // otherwise AdvanceFrame may pop the sample before we have a chance
   // to reach playing.
   AudioQueue().Push(aSample);
   if (mState > DECODER_STATE_DECODING_FIRSTFRAME) {
     // The ready state can change when we've decoded data, so update the
     // ready state, so that DOM events can fire.
@@ -808,16 +812,17 @@ MediaDecoderStateMachine::Push(AudioData
     DispatchDecodeTasksIfNeeded();
     mDecoder->GetReentrantMonitor().NotifyAll();
   }
 }
 
 void
 MediaDecoderStateMachine::Push(VideoData* aSample)
 {
+  MOZ_ASSERT(OnTaskQueue());
   MOZ_ASSERT(aSample);
   // TODO: Send aSample to MSG and recalculate readystate before pushing,
   // otherwise AdvanceFrame may pop the sample before we have a chance
   // to reach playing.
   VideoQueue().Push(aSample);
   if (mState > DECODER_STATE_DECODING_FIRSTFRAME) {
     // The ready state can change when we've decoded data, so update the
     // ready state, so that DOM events can fire.
@@ -854,20 +859,22 @@ MediaDecoderStateMachine::OnNotDecoded(M
   }
 
   // If the decoder is waiting for data, we tell it to call us back when the
   // data arrives.
   if (aReason == MediaDecoderReader::WAITING_FOR_DATA) {
     MOZ_ASSERT(mReader->IsWaitForDataSupported(),
                "Readers that send WAITING_FOR_DATA need to implement WaitForData");
     WaitRequestRef(aType).Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(), __func__,
-                                               &MediaDecoderReader::WaitForData, aType)
+                                               &MediaDecoderReader::WaitForData, aType,
+                                               TailDispatcher())
       ->RefableThen(TaskQueue(), __func__, this,
                     &MediaDecoderStateMachine::OnWaitForDataResolved,
-                    &MediaDecoderStateMachine::OnWaitForDataRejected));
+                    &MediaDecoderStateMachine::OnWaitForDataRejected,
+                    TailDispatcher()));
     return;
   }
 
   if (aReason == MediaDecoderReader::CANCELED) {
     DispatchDecodeTasksIfNeeded();
     return;
   }
 
@@ -1075,22 +1082,19 @@ MediaDecoderStateMachine::CheckIfSeekCom
     }
   }
 
   SAMPLE_LOG("CheckIfSeekComplete() audioSeekComplete=%d videoSeekComplete=%d",
              audioSeekComplete, videoSeekComplete);
 
   if (audioSeekComplete && videoSeekComplete) {
     mDecodeToSeekTarget = false;
-    RefPtr<nsIRunnable> task(
+    nsCOMPtr<nsIRunnable> task(
       NS_NewRunnableMethod(this, &MediaDecoderStateMachine::SeekCompleted));
-    nsresult rv = TaskQueue()->Dispatch(task);
-    if (NS_FAILED(rv)) {
-      DecodeError();
-    }
+    TailDispatch(TaskQueue(), task.forget());
   }
 }
 
 bool
 MediaDecoderStateMachine::IsAudioDecoding()
 {
   AssertCurrentThreadInMonitor();
   return HasAudio() && !AudioQueue().IsFinished();
@@ -1170,16 +1174,17 @@ void MediaDecoderStateMachine::StopPlayb
   NS_ASSERTION(!IsPlaying(), "Should report not playing at end of StopPlayback()");
   mDecoder->UpdateStreamBlockingForStateMachinePlaying();
 
   DispatchDecodeTasksIfNeeded();
 }
 
 void MediaDecoderStateMachine::MaybeStartPlayback()
 {
+  MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
   if (IsPlaying()) {
     // Logging this case is really spammy - don't do it.
     return;
   }
 
   bool playStatePermits = mDecoder->GetState() == MediaDecoder::PLAY_STATE_PLAYING;
   bool decodeStatePermits = mState == DECODER_STATE_DECODING || mState == DECODER_STATE_COMPLETED;
@@ -1220,33 +1225,34 @@ void MediaDecoderStateMachine::UpdatePla
   NS_ASSERTION(mCurrentFrameTime >= 0, "CurrentTime should be positive!");
   if (aTime > mEndTime) {
     NS_ASSERTION(mCurrentFrameTime > GetDuration(),
                  "CurrentTime must be after duration if aTime > endTime!");
     DECODER_LOG("Setting new end time to %lld", aTime);
     mEndTime = aTime;
     nsCOMPtr<nsIRunnable> event =
       NS_NewRunnableMethod(mDecoder, &MediaDecoder::DurationChanged);
-    NS_DispatchToMainThread(event);
+    TailDispatch(AbstractThread::MainThread(), event.forget());
   }
 }
 
 void MediaDecoderStateMachine::UpdatePlaybackPosition(int64_t aTime)
 {
+  MOZ_ASSERT(OnTaskQueue());
   UpdatePlaybackPositionInternal(aTime);
 
   bool fragmentEnded = mFragmentEndTime >= 0 && GetMediaTime() >= mFragmentEndTime;
   if (!mPositionChangeQueued || fragmentEnded) {
     mPositionChangeQueued = true;
     nsCOMPtr<nsIRunnable> event =
       NS_NewRunnableMethodWithArg<MediaDecoderEventVisibility>(
         mDecoder,
         &MediaDecoder::PlaybackPositionChanged,
         MediaDecoderEventVisibility::Observable);
-    NS_DispatchToMainThread(event);
+    TailDispatch(AbstractThread::MainThread(), event.forget());
   }
 
   mMetadataManager.DispatchMetadataIfNeeded(mDecoder, aTime);
 
   if (fragmentEnded) {
     StopPlayback();
   }
 }
@@ -1293,16 +1299,19 @@ void MediaDecoderStateMachine::SetState(
   AssertCurrentThreadInMonitor();
   if (mState == aState) {
     return;
   }
   DECODER_LOG("Change machine state from %s to %s",
               gMachineStateStr[mState], gMachineStateStr[aState]);
 
   mState = aState;
+
+  // Clear state-scoped state.
+  mSentPlaybackEndedEvent = false;
 }
 
 void MediaDecoderStateMachine::SetVolume(double volume)
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   mVolume = volume;
   if (mAudioSink) {
@@ -1396,31 +1405,31 @@ void MediaDecoderStateMachine::SetDurati
     // resource.
     if (NS_IsMainThread()) {
       // Seek synchronously.
       mDecoder->Seek(double(mEndTime) / USECS_PER_S, SeekTarget::Accurate);
     } else {
       // Queue seek to new end position.
       nsCOMPtr<nsIRunnable> task =
         new SeekRunnable(mDecoder, double(mEndTime) / USECS_PER_S);
-      NS_DispatchToMainThread(task);
+      AbstractThread::MainThread()->MaybeTailDispatch(task.forget());
     }
   }
 }
 
 void MediaDecoderStateMachine::UpdateEstimatedDuration(int64_t aDuration)
 {
   AssertCurrentThreadInMonitor();
   int64_t duration = GetDuration();
   if (aDuration != duration &&
       mozilla::Abs(aDuration - duration) > ESTIMATED_DURATION_FUZZ_FACTOR_USECS) {
     SetDuration(aDuration);
     nsCOMPtr<nsIRunnable> event =
       NS_NewRunnableMethod(mDecoder, &MediaDecoder::DurationChanged);
-    NS_DispatchToMainThread(event);
+    AbstractThread::MainThread()->MaybeTailDispatch(event.forget());
   }
 }
 
 void MediaDecoderStateMachine::SetMediaEndTime(int64_t aEndTime)
 {
   MOZ_ASSERT(OnDecodeTaskQueue());
   AssertCurrentThreadInMonitor();
 
@@ -1473,34 +1482,33 @@ void MediaDecoderStateMachine::SetDorman
     } else {
       mQueuedSeek.mTarget = SeekTarget(mCurrentFrameTime,
                                        SeekTarget::Accurate,
                                        MediaDecoderEventVisibility::Suppressed);
       // XXXbholley - Nobody is listening to this promise. Do we need to pass it
       // back to MediaDecoder when we come out of dormant?
       nsRefPtr<MediaDecoder::SeekPromise> unused = mQueuedSeek.mPromise.Ensure(__func__);
     }
-    mPendingSeek.RejectIfExists(__func__);
-    mCurrentSeek.RejectIfExists(__func__);
+    mPendingSeek.RejectIfExists(__func__, TailDispatcher());
+    mCurrentSeek.RejectIfExists(__func__, TailDispatcher());
     SetState(DECODER_STATE_DORMANT);
     if (IsPlaying()) {
       StopPlayback();
     }
 
     Reset();
 
     // Note that we do not wait for the decode task queue to go idle before
     // queuing the ReleaseMediaResources task - instead, we disconnect promises,
     // reset state, and put a ResetDecode in the decode task queue. Any tasks
     // that run after ResetDecode are supposed to run with a clean slate. We rely
     // on that in other places (i.e. seeking), so it seems reasonable to rely on
     // it here as well.
-    DebugOnly<nsresult> rv = DecodeTaskQueue()->Dispatch(
-    NS_NewRunnableMethod(mReader, &MediaDecoderReader::ReleaseMediaResources));
-    MOZ_ASSERT(NS_SUCCEEDED(rv));
+    nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(mReader, &MediaDecoderReader::ReleaseMediaResources);
+    TailDispatch(DecodeTaskQueue(), r.forget());
     // There's now no possibility of mPendingWakeDecoder being needed again. Revoke it.
     mPendingWakeDecoder = nullptr;
     mDecoder->GetReentrantMonitor().NotifyAll();
   } else if ((aDormant != true) && (mState == DECODER_STATE_DORMANT)) {
     mDecodingFrozenAtStateDecoding = true;
     ScheduleStateMachine();
     mCurrentFrameTime = 0;
     SetState(DECODER_STATE_DECODING_NONE);
@@ -1659,22 +1667,22 @@ MediaDecoderStateMachine::Seek(SeekTarge
     return MediaDecoder::SeekPromise::CreateAndReject(/* aIgnored = */ true, __func__);
   }
 
   NS_ASSERTION(mState > DECODER_STATE_DECODING_METADATA,
                "We should have got duration already");
 
   if (mState < DECODER_STATE_DECODING) {
     DECODER_LOG("Seek() Not Enough Data to continue at this stage, queuing seek");
-    mQueuedSeek.RejectIfExists(__func__);
+    mQueuedSeek.RejectIfExists(__func__, TailDispatcher());
     mQueuedSeek.mTarget = aTarget;
     return mQueuedSeek.mPromise.Ensure(__func__);
   }
-  mQueuedSeek.RejectIfExists(__func__);
-  mPendingSeek.RejectIfExists(__func__);
+  mQueuedSeek.RejectIfExists(__func__, TailDispatcher());
+  mPendingSeek.RejectIfExists(__func__, TailDispatcher());
   mPendingSeek.mTarget = aTarget;
 
   DECODER_LOG("Changed state to SEEKING (to %lld)", mPendingSeek.mTarget.mTime);
   SetState(DECODER_STATE_SEEKING);
   ScheduleStateMachine();
 
   return mPendingSeek.mPromise.Ensure(__func__);
 }
@@ -1709,34 +1717,34 @@ void MediaDecoderStateMachine::StopAudio
 }
 
 nsresult
 MediaDecoderStateMachine::EnqueueDecodeFirstFrameTask()
 {
   AssertCurrentThreadInMonitor();
   MOZ_ASSERT(mState == DECODER_STATE_DECODING_FIRSTFRAME);
 
-  RefPtr<nsIRunnable> task(
+  nsCOMPtr<nsIRunnable> task(
     NS_NewRunnableMethod(this, &MediaDecoderStateMachine::CallDecodeFirstFrame));
-  nsresult rv = TaskQueue()->Dispatch(task);
-  NS_ENSURE_SUCCESS(rv, rv);
+  TailDispatch(TaskQueue(), task.forget());
   return NS_OK;
 }
 
 void
 MediaDecoderStateMachine::SetReaderIdle()
 {
   MOZ_ASSERT(OnDecodeTaskQueue());
   DECODER_LOG("Invoking SetReaderIdle()");
   mReader->SetIdle();
 }
 
 void
 MediaDecoderStateMachine::DispatchDecodeTasksIfNeeded()
 {
+  MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
 
   if (mState != DECODER_STATE_DECODING &&
       mState != DECODER_STATE_DECODING_FIRSTFRAME &&
       mState != DECODER_STATE_BUFFERING &&
       mState != DECODER_STATE_SEEKING) {
     return;
   }
@@ -1783,32 +1791,29 @@ MediaDecoderStateMachine::DispatchDecode
   if (needToDecodeVideo) {
     EnsureVideoDecodeTaskQueued();
   }
 
   if (needIdle) {
     DECODER_LOG("Dispatching SetReaderIdle() audioQueue=%lld videoQueue=%lld",
                 GetDecodedAudioDuration(),
                 VideoQueue().Duration());
-    RefPtr<nsIRunnable> event = NS_NewRunnableMethod(
+    nsCOMPtr<nsIRunnable> task = NS_NewRunnableMethod(
         this, &MediaDecoderStateMachine::SetReaderIdle);
-    nsresult rv = DecodeTaskQueue()->Dispatch(event.forget());
-    if (NS_FAILED(rv) && !IsShutdown()) {
-      DECODER_WARN("Failed to dispatch event to set decoder idle state");
-    }
+    TailDispatch(DecodeTaskQueue(), task.forget());
   }
 }
 
 void
 MediaDecoderStateMachine::InitiateSeek()
 {
   MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
 
-  mCurrentSeek.RejectIfExists(__func__);
+  mCurrentSeek.RejectIfExists(__func__, TailDispatcher());
   mCurrentSeek.Steal(mPendingSeek);
 
   // Bound the seek time to be inside the media range.
   int64_t end = GetEndTime();
   NS_ASSERTION(mStartTime != -1, "Should know start time by now");
   NS_ASSERTION(end != -1, "Should know end time by now");
   int64_t seekTime = mCurrentSeek.mTarget.mTime + mStartTime;
   seekTime = std::min(seekTime, end);
@@ -1822,17 +1827,17 @@ MediaDecoderStateMachine::InitiateSeek()
     // for audio thread since it is until then we know which position we seek to
     // as far as fast-seek is concerned. It also fix the problem where stream
     // clock seems to go backwards during seeking.
     nsCOMPtr<nsIRunnable> event =
       NS_NewRunnableMethodWithArgs<int64_t, MediaStreamGraph*>(mDecoder,
                                                                &MediaDecoder::RecreateDecodedStream,
                                                                seekTime - mStartTime,
                                                                nullptr);
-    NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
+    TailDispatch(AbstractThread::MainThread(), event.forget());
   }
 
   mDropAudioUntilNextDiscontinuity = HasAudio();
   mDropVideoUntilNextDiscontinuity = HasVideo();
 
   mDecoder->StopProgressUpdates();
   mCurrentTimeBeforeSeek = GetMediaTime();
 
@@ -1845,28 +1850,29 @@ MediaDecoderStateMachine::InitiateSeek()
   // SeekingStarted will do a UpdateReadyStateForData which will
   // inform the element and its users that we have no frames
   // to display
   nsCOMPtr<nsIRunnable> startEvent =
       NS_NewRunnableMethodWithArg<MediaDecoderEventVisibility>(
         mDecoder,
         &MediaDecoder::SeekingStarted,
         mCurrentSeek.mTarget.mEventVisibility);
-  NS_DispatchToMainThread(startEvent, NS_DISPATCH_NORMAL);
+  TailDispatch(AbstractThread::MainThread(), startEvent.forget());
 
   // Reset our state machine and decoding pipeline before seeking.
   Reset();
 
   // Do the seek.
   mSeekRequest.Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(), __func__,
                                     &MediaDecoderReader::Seek, mCurrentSeek.mTarget.mTime,
-                                    GetEndTime())
+                                    GetEndTime(), TailDispatcher())
     ->RefableThen(TaskQueue(), __func__, this,
                   &MediaDecoderStateMachine::OnSeekCompleted,
-                  &MediaDecoderStateMachine::OnSeekFailed));
+                  &MediaDecoderStateMachine::OnSeekFailed,
+                  TailDispatcher()));
 }
 
 nsresult
 MediaDecoderStateMachine::DispatchAudioDecodeTaskIfNeeded()
 {
   MOZ_ASSERT(OnTaskQueue());
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
 
@@ -1901,20 +1907,22 @@ MediaDecoderStateMachine::EnsureAudioDec
       mAudioWaitRequest.Exists() || mSeekRequest.Exists()) {
     return NS_OK;
   }
 
   SAMPLE_LOG("Queueing audio task - queued=%i, decoder-queued=%o",
              AudioQueue().GetSize(), mReader->SizeOfAudioQueueInFrames());
 
   mAudioDataRequest.Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(),
-                                         __func__, &MediaDecoderReader::RequestAudioData)
+                                         __func__, &MediaDecoderReader::RequestAudioData,
+                                         TailDispatcher())
     ->RefableThen(TaskQueue(), __func__, this,
                   &MediaDecoderStateMachine::OnAudioDecoded,
-                  &MediaDecoderStateMachine::OnAudioNotDecoded));
+                  &MediaDecoderStateMachine::OnAudioNotDecoded,
+                  TailDispatcher()));
 
   return NS_OK;
 }
 
 nsresult
 MediaDecoderStateMachine::DispatchVideoDecodeTaskIfNeeded()
 {
   MOZ_ASSERT(OnTaskQueue());
@@ -1961,20 +1969,21 @@ MediaDecoderStateMachine::EnsureVideoDec
   mVideoDecodeStartTime = TimeStamp::Now();
 
   SAMPLE_LOG("Queueing video task - queued=%i, decoder-queued=%o, skip=%i, time=%lld",
              VideoQueue().GetSize(), mReader->SizeOfVideoQueueInFrames(), skipToNextKeyFrame,
              currentTime);
 
   mVideoDataRequest.Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(), __func__,
                                          &MediaDecoderReader::RequestVideoData,
-                                         skipToNextKeyFrame, currentTime)
+                                         skipToNextKeyFrame, currentTime, TailDispatcher())
     ->RefableThen(TaskQueue(), __func__, this,
                   &MediaDecoderStateMachine::OnVideoDecoded,
-                  &MediaDecoderStateMachine::OnVideoNotDecoded));
+                  &MediaDecoderStateMachine::OnVideoNotDecoded,
+                  TailDispatcher()));
   return NS_OK;
 }
 
 nsresult
 MediaDecoderStateMachine::StartAudioThread()
 {
   MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
@@ -2099,17 +2108,17 @@ MediaDecoderStateMachine::DecodeError()
   // XXXbholley - Is anybody actually waiting on this monitor, or is it just
   // a leftover from when we used to do sync dispatch for the below?
   mDecoder->GetReentrantMonitor().NotifyAll();
 
   // MediaDecoder::DecodeError notifies the owner, and then shuts down the state
   // machine.
   nsCOMPtr<nsIRunnable> event =
     NS_NewRunnableMethod(mDecoder, &MediaDecoder::DecodeError);
-  NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
+  TailDispatch(AbstractThread::MainThread(), event.forget());
 }
 
 void
 MediaDecoderStateMachine::OnMetadataRead(MetadataHolder* aMetadata)
 {
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   MOZ_ASSERT(OnTaskQueue());
   MOZ_ASSERT(mState == DECODER_STATE_DECODING_METADATA);
@@ -2162,38 +2171,40 @@ MediaDecoderStateMachine::OnMetadataNotR
     DECODER_WARN("Decode metadata failed, shutting down decoder");
     DecodeError();
   }
 }
 
 void
 MediaDecoderStateMachine::EnqueueLoadedMetadataEvent()
 {
+  MOZ_ASSERT(OnTaskQueue());
   nsAutoPtr<MediaInfo> info(new MediaInfo());
   *info = mInfo;
   MediaDecoderEventVisibility visibility = mSentLoadedMetadataEvent?
                                     MediaDecoderEventVisibility::Suppressed :
                                     MediaDecoderEventVisibility::Observable;
   nsCOMPtr<nsIRunnable> metadataLoadedEvent =
     new MetadataEventRunner(mDecoder, info, mMetadataTags, visibility);
-  NS_DispatchToMainThread(metadataLoadedEvent, NS_DISPATCH_NORMAL);
+  TailDispatch(AbstractThread::MainThread(), metadataLoadedEvent.forget());
   mSentLoadedMetadataEvent = true;
 }
 
 void
 MediaDecoderStateMachine::EnqueueFirstFrameLoadedEvent()
 {
+  MOZ_ASSERT(OnTaskQueue());
   nsAutoPtr<MediaInfo> info(new MediaInfo());
   *info = mInfo;
   MediaDecoderEventVisibility visibility = mSentFirstFrameLoadedEvent?
                                     MediaDecoderEventVisibility::Suppressed :
                                     MediaDecoderEventVisibility::Observable;
   nsCOMPtr<nsIRunnable> event =
     new FirstFrameLoadedEventRunner(mDecoder, info, visibility);
-  NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
+  TailDispatch(AbstractThread::MainThread(), event.forget());
   mSentFirstFrameLoadedEvent = true;
 }
 
 void
 MediaDecoderStateMachine::CallDecodeFirstFrame()
 {
   MOZ_ASSERT(OnTaskQueue());
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
@@ -2234,28 +2245,32 @@ MediaDecoderStateMachine::DecodeFirstFra
     // the first samples in order to determine the media start time,
     // we have the start time from last time we loaded.
     SetStartTime(mStartTime);
     nsresult res = FinishDecodeFirstFrame();
     NS_ENSURE_SUCCESS(res, res);
   } else {
     if (HasAudio()) {
       mAudioDataRequest.Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(),
-                                             __func__, &MediaDecoderReader::RequestAudioData)
+                                             __func__, &MediaDecoderReader::RequestAudioData,
+                                             TailDispatcher())
         ->RefableThen(TaskQueue(), __func__, this,
                       &MediaDecoderStateMachine::OnAudioDecoded,
-                      &MediaDecoderStateMachine::OnAudioNotDecoded));
+                      &MediaDecoderStateMachine::OnAudioNotDecoded,
+                      TailDispatcher()));
     }
     if (HasVideo()) {
       mVideoDecodeStartTime = TimeStamp::Now();
       mVideoDataRequest.Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(),
-                                             __func__, &MediaDecoderReader::RequestVideoData, false, int64_t(0))
+                                             __func__, &MediaDecoderReader::RequestVideoData, false,
+                                             int64_t(0), TailDispatcher())
         ->RefableThen(TaskQueue(), __func__, this,
                       &MediaDecoderStateMachine::OnVideoDecoded,
-                      &MediaDecoderStateMachine::OnVideoNotDecoded));
+                      &MediaDecoderStateMachine::OnVideoNotDecoded,
+                      TailDispatcher()));
     }
   }
 
   return NS_OK;
 }
 
 nsresult
 MediaDecoderStateMachine::FinishDecodeFirstFrame()
@@ -2427,25 +2442,25 @@ MediaDecoderStateMachine::SeekCompleted(
   // Try to decode another frame to detect if we're at the end...
   DECODER_LOG("Seek completed, mCurrentFrameTime=%lld", mCurrentFrameTime);
 
   // Reset quick buffering status. This ensures that if we began the
   // seek while quick-buffering, we won't bypass quick buffering mode
   // if we need to buffer after the seek.
   mQuickBuffering = false;
 
-  mCurrentSeek.Resolve(mState == DECODER_STATE_COMPLETED, __func__);
+  mCurrentSeek.Resolve(mState == DECODER_STATE_COMPLETED, __func__, TailDispatcher());
   ScheduleStateMachine();
 
   if (video) {
     ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
     RenderVideoFrame(video, TimeStamp::Now());
     nsCOMPtr<nsIRunnable> event =
       NS_NewRunnableMethod(mDecoder, &MediaDecoder::Invalidate);
-    NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
+    TailDispatch(AbstractThread::MainThread(), event.forget());
   }
 }
 
 class DecoderDisposer
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DecoderDisposer)
   DecoderDisposer(MediaDecoder* aDecoder, MediaDecoderStateMachine* aStateMachine)
@@ -2508,17 +2523,18 @@ MediaDecoderStateMachine::FinishShutdown
   // finished and released its monitor/references. That event then will
   // dispatch an event to the main thread to release the decoder and
   // state machine.
   DECODER_LOG("Shutting down state machine task queue");
   RefPtr<DecoderDisposer> disposer = new DecoderDisposer(mDecoder, this);
   TaskQueue()->BeginShutdown()->Then(AbstractThread::MainThread(), __func__,
                                      disposer.get(),
                                      &DecoderDisposer::OnTaskQueueShutdown,
-                                     &DecoderDisposer::OnTaskQueueShutdown);
+                                     &DecoderDisposer::OnTaskQueueShutdown,
+                                     TailDispatcher());
 }
 
 nsresult MediaDecoderStateMachine::RunStateMachine()
 {
   MOZ_ASSERT(OnTaskQueue());
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
 
   mDelayedScheduler.Reset(); // Must happen on state machine task queue.
@@ -2534,32 +2550,31 @@ nsresult MediaDecoderStateMachine::RunSt
 
   switch (mState) {
     case DECODER_STATE_ERROR: {
       // Just wait for MediaDecoder::DecodeError to shut us down.
       return NS_OK;
     }
 
     case DECODER_STATE_SHUTDOWN: {
-      mQueuedSeek.RejectIfExists(__func__);
-      mPendingSeek.RejectIfExists(__func__);
-      mCurrentSeek.RejectIfExists(__func__);
+      mQueuedSeek.RejectIfExists(__func__, TailDispatcher());
+      mPendingSeek.RejectIfExists(__func__, TailDispatcher());
+      mCurrentSeek.RejectIfExists(__func__, TailDispatcher());
 
       if (IsPlaying()) {
         StopPlayback();
       }
 
       Reset();
 
       // Put a task in the decode queue to shutdown the reader.
       // the queue to spin down.
-      RefPtr<nsIRunnable> task;
-      task = NS_NewRunnableMethod(this, &MediaDecoderStateMachine::ShutdownReader);
-      DebugOnly<nsresult> rv = DecodeTaskQueue()->Dispatch(task);
-      MOZ_ASSERT(NS_SUCCEEDED(rv));
+      nsCOMPtr<nsIRunnable> task
+        = NS_NewRunnableMethod(this, &MediaDecoderStateMachine::ShutdownReader);
+      TailDispatch(DecodeTaskQueue(), task.forget());
 
       DECODER_LOG("Shutdown started");
       return NS_OK;
     }
 
     case DECODER_STATE_DORMANT: {
       return NS_OK;
     }
@@ -2574,20 +2589,21 @@ nsresult MediaDecoderStateMachine::RunSt
       ScheduleStateMachine();
       return NS_OK;
     }
 
     case DECODER_STATE_DECODING_METADATA: {
       if (!mMetadataRequest.Exists()) {
         DECODER_LOG("Dispatching CallReadMetadata");
         mMetadataRequest.Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(), __func__,
-                                              &MediaDecoderReader::CallReadMetadata)
+                                              &MediaDecoderReader::CallReadMetadata, TailDispatcher())
           ->RefableThen(TaskQueue(), __func__, this,
                         &MediaDecoderStateMachine::OnMetadataRead,
-                        &MediaDecoderStateMachine::OnMetadataNotRead));
+                        &MediaDecoderStateMachine::OnMetadataNotRead,
+                        TailDispatcher()));
 
       }
       return NS_OK;
     }
 
     case DECODER_STATE_DECODING_FIRSTFRAME: {
       // DECODER_STATE_DECODING_FIRSTFRAME will be started by OnMetadataRead.
       return NS_OK;
@@ -2690,31 +2706,28 @@ nsresult MediaDecoderStateMachine::RunSt
         // While we're presenting a frame we can change state. Whatever changed
         // our state should have scheduled another state machine run.
         NS_ASSERTION(IsStateMachineScheduled(), "Must have timer scheduled");
         return NS_OK;
       }
 
       StopAudioThread();
 
-      if (mDecoder->GetState() == MediaDecoder::PLAY_STATE_PLAYING) {
+      if (mDecoder->GetState() == MediaDecoder::PLAY_STATE_PLAYING &&
+          !mSentPlaybackEndedEvent)
+      {
         int64_t clockTime = std::max(mAudioEndTime, mVideoFrameEndTime);
         clockTime = std::max(int64_t(0), std::max(clockTime, mEndTime));
         UpdatePlaybackPosition(clockTime);
 
-        {
-          // Wait for the state change is completed in the main thread,
-          // otherwise we might see |mDecoder->GetState() == MediaDecoder::PLAY_STATE_PLAYING|
-          // in next loop and send |MediaDecoder::PlaybackEnded| again to trigger 'ended'
-          // event twice in the media element.
-          ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
-          nsCOMPtr<nsIRunnable> event =
-            NS_NewRunnableMethod(mDecoder, &MediaDecoder::PlaybackEnded);
-          NS_DispatchToMainThread(event, NS_DISPATCH_SYNC);
-        }
+        nsCOMPtr<nsIRunnable> event =
+          NS_NewRunnableMethod(mDecoder, &MediaDecoder::PlaybackEnded);
+        TailDispatch(AbstractThread::MainThread(), event.forget());
+
+        mSentPlaybackEndedEvent = true;
       }
       return NS_OK;
     }
   }
 
   return NS_OK;
 }
 
@@ -2754,19 +2767,19 @@ MediaDecoderStateMachine::Reset()
 
   mMetadataRequest.DisconnectIfExists();
   mAudioDataRequest.DisconnectIfExists();
   mAudioWaitRequest.DisconnectIfExists();
   mVideoDataRequest.DisconnectIfExists();
   mVideoWaitRequest.DisconnectIfExists();
   mSeekRequest.DisconnectIfExists();
 
-  RefPtr<nsRunnable> resetTask =
+  nsCOMPtr<nsIRunnable> resetTask =
     NS_NewRunnableMethod(mReader, &MediaDecoderReader::ResetDecode);
-  DecodeTaskQueue()->Dispatch(resetTask);
+  TailDispatch(DecodeTaskQueue(), resetTask.forget());
 }
 
 void MediaDecoderStateMachine::RenderVideoFrame(VideoData* aData,
                                                 TimeStamp aTarget)
 {
   MOZ_ASSERT(OnTaskQueue());
   mDecoder->GetReentrantMonitor().AssertNotCurrentThreadIn();
 
@@ -2786,17 +2799,19 @@ void MediaDecoderStateMachine::RenderVid
       // If more than 10% of the last 30 frames have been corrupted, then try disabling
       // hardware acceleration. We use 10 as the corrupt value because RollingMean<>
       // only supports integer types.
       mCorruptFrames.insert(10);
       if (!mDisabledHardwareAcceleration &&
           mReader->VideoIsHardwareAccelerated() &&
           frameStats.GetPresentedFrames() > 30 &&
           mCorruptFrames.mean() >= 1 /* 10% */) {
-        DecodeTaskQueue()->Dispatch(NS_NewRunnableMethod(mReader, &MediaDecoderReader::DisableHardwareAcceleration));
+        nsCOMPtr<nsIRunnable> task =
+          NS_NewRunnableMethod(mReader, &MediaDecoderReader::DisableHardwareAcceleration);
+        TailDispatch(DecodeTaskQueue(), task.forget());
         mDisabledHardwareAcceleration = true;
       }
     } else {
       mCorruptFrames.insert(0);
     }
     container->SetCurrentFrame(aData->mDisplay, aData->mImage, aTarget);
     MOZ_ASSERT(container->GetFrameDelay() >= 0 || IsRealTime());
   }
@@ -3189,17 +3204,17 @@ void MediaDecoderStateMachine::UpdateRea
    * HTMLMediaElement::UpdateReadyStateForData. It doesn't use the value of
    * GetNextFrameStatus we computed here, because what we're computing here
    * could be stale by the time MediaDecoder::UpdateReadyStateForData runs.
    * We only compute GetNextFrameStatus here to avoid posting runnables to the main
    * thread unnecessarily.
    */
   nsCOMPtr<nsIRunnable> event;
   event = NS_NewRunnableMethod(mDecoder, &MediaDecoder::UpdateReadyStateForData);
-  NS_DispatchToMainThread(event);
+  AbstractThread::MainThread()->MaybeTailDispatch(event.forget());
 }
 
 bool MediaDecoderStateMachine::JustExitedQuickBuffering()
 {
   return !mDecodeStartTime.IsNull() &&
     mQuickBuffering &&
     (TimeStamp::Now() - mDecodeStartTime) < TimeDuration::FromMicroseconds(QUICK_BUFFER_THRESHOLD_USECS);
 }
@@ -3280,21 +3295,19 @@ MediaDecoderStateMachine::ScheduleStateM
     return;
   }
 
   if (mDispatchedStateMachine) {
     return;
   }
   mDispatchedStateMachine = true;
 
-  RefPtr<nsIRunnable> task =
+  nsCOMPtr<nsIRunnable> task =
     NS_NewRunnableMethod(this, &MediaDecoderStateMachine::RunStateMachine);
-  nsresult rv = TaskQueue()->Dispatch(task);
-  MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
-  (void) rv;
+  TaskQueue()->MaybeTailDispatch(task.forget());
 }
 
 void
 MediaDecoderStateMachine::ScheduleStateMachineIn(int64_t aMicroseconds)
 {
   AssertCurrentThreadInMonitor();
   MOZ_ASSERT(OnTaskQueue());          // mDelayedScheduler.Ensure() may Disconnect()
                                       // the promise, which must happen on the state
@@ -3399,29 +3412,33 @@ void MediaDecoderStateMachine::QueueMeta
   metadata->mPublishTime = aPublishTime;
   metadata->mInfo = aInfo.forget();
   metadata->mTags = aTags.forget();
   mMetadataManager.QueueMetadata(metadata);
 }
 
 void MediaDecoderStateMachine::OnAudioEndTimeUpdate(int64_t aAudioEndTime)
 {
+  MOZ_ASSERT(OnTaskQueue());
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   MOZ_ASSERT(aAudioEndTime >= mAudioEndTime);
   mAudioEndTime = aAudioEndTime;
 }
 
 void MediaDecoderStateMachine::OnPlaybackOffsetUpdate(int64_t aPlaybackOffset)
 {
+  MOZ_ASSERT(OnTaskQueue());
+  ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   mDecoder->UpdatePlaybackOffset(aPlaybackOffset);
 }
 
 void MediaDecoderStateMachine::OnAudioSinkComplete()
 {
-  AssertCurrentThreadInMonitor();
+  MOZ_ASSERT(OnTaskQueue());
+  ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   if (mAudioCaptured) {
     return;
   }
   ResyncAudioClock();
   mAudioCompleted = true;
   UpdateReadyState();
   // Kick the decode thread; it may be sleeping waiting for this to finish.
   mDecoder->GetReentrantMonitor().NotifyAll();
@@ -3459,8 +3476,10 @@ uint32_t MediaDecoderStateMachine::GetAm
 
 } // namespace mozilla
 
 // avoid redefined macro in unified build
 #undef DECODER_LOG
 #undef VERBOSE_LOG
 #undef DECODER_WARN
 #undef DECODER_WARN_HELPER
+
+#undef NS_DispatchToMainThread
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -326,16 +326,32 @@ public:
     return 0;
   }
 
   void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset);
 
   // Returns the state machine task queue.
   MediaTaskQueue* TaskQueue() const { return mTaskQueue; }
 
+  // Returns the tail dispatcher associated with TaskQueue(), which will fire
+  // its tasks when the current task completes. May only be called when running
+  // in TaskQueue().
+  TaskDispatcher& TailDispatcher()
+  {
+    MOZ_ASSERT(OnTaskQueue());
+    return TaskQueue()->TailDispatcher();
+  }
+
+  // Convenience method to perform a tail dispatch.
+  void TailDispatch(AbstractThread* aThread,
+                    already_AddRefed<nsIRunnable> aTask)
+  {
+    TailDispatcher().AddTask(aThread, Move(aTask));
+  }
+
   // Calls ScheduleStateMachine() after taking the decoder lock. Also
   // notifies the decoder thread in case it's waiting on the decoder lock.
   void ScheduleStateMachineWithLockAndWakeDecoder();
 
   // Schedules the shared state machine thread to run the state machine.
   void ScheduleStateMachine();
 
   // Invokes ScheduleStateMachine to run in |aMicroseconds| microseconds,
@@ -713,25 +729,47 @@ protected:
   // the end of stream.
   bool IsAudioDecoding();
   bool IsVideoDecoding();
 
   // Set the time that playback started from the system clock.
   // Can only be called on the state machine thread.
   void SetPlayStartTime(const TimeStamp& aTimeStamp);
 
+private:
   // Update mAudioEndTime.
   void OnAudioEndTimeUpdate(int64_t aAudioEndTime);
+public:
+  void DispatchOnAudioEndTimeUpdate(int64_t aAudioEndTime)
+  {
+    RefPtr<nsRunnable> r =
+      NS_NewRunnableMethodWithArg<int64_t>(this, &MediaDecoderStateMachine::OnAudioEndTimeUpdate, aAudioEndTime);
+    TaskQueue()->Dispatch(r.forget());
+  }
 
+private:
   // Update mDecoder's playback offset.
   void OnPlaybackOffsetUpdate(int64_t aPlaybackOffset);
+public:
+  void DispatchOnPlaybackOffsetUpdate(int64_t aPlaybackOffset)
+  {
+    RefPtr<nsRunnable> r =
+      NS_NewRunnableMethodWithArg<int64_t>(this, &MediaDecoderStateMachine::OnPlaybackOffsetUpdate, aPlaybackOffset);
+    TaskQueue()->Dispatch(r.forget());
+  }
 
+private:
   // Called by the AudioSink to signal that all outstanding work is complete
   // and the sink is shutting down.
   void OnAudioSinkComplete();
+public:
+  void DispatchOnAudioSinkComplete()
+  {
+    TaskQueue()->Dispatch(NS_NewRunnableMethod(this, &MediaDecoderStateMachine::OnAudioSinkComplete));
+  }
 
   // Called by the AudioSink to signal errors.
   void OnAudioSinkError();
 
   void DispatchOnAudioSinkError()
   {
     TaskQueue()->Dispatch(NS_NewRunnableMethod(this, &MediaDecoderStateMachine::OnAudioSinkError));
   }
@@ -775,25 +813,26 @@ protected:
       if (IsScheduled()) {
         mRequest.Disconnect();
         mTarget = TimeStamp();
       }
     }
 
     void Ensure(mozilla::TimeStamp& aTarget)
     {
+      MOZ_ASSERT(mSelf->OnTaskQueue());
       if (IsScheduled() && mTarget <= aTarget) {
         return;
       }
       Reset();
       mTarget = aTarget;
       mRequest.Begin(mMediaTimer->WaitUntil(mTarget, __func__)->RefableThen(
         mSelf->TaskQueue(), __func__, mSelf,
         &MediaDecoderStateMachine::OnDelayedSchedule,
-        &MediaDecoderStateMachine::NotReached));
+        &MediaDecoderStateMachine::NotReached, mSelf->TailDispatcher()));
     }
 
     void CompleteRequest()
     {
       mRequest.Complete();
       mTarget = TimeStamp();
     }
 
@@ -873,27 +912,27 @@ protected:
     }
 
     bool Exists()
     {
       MOZ_ASSERT(mTarget.IsValid() == !mPromise.IsEmpty());
       return mTarget.IsValid();
     }
 
-    void Resolve(bool aAtEnd, const char* aCallSite)
+    void Resolve(bool aAtEnd, const char* aCallSite, TaskDispatcher& aDispatcher)
     {
       mTarget.Reset();
       MediaDecoder::SeekResolveValue val(aAtEnd, mTarget.mEventVisibility);
-      mPromise.Resolve(val, aCallSite);
+      mPromise.Resolve(val, aCallSite, aDispatcher);
     }
 
-    void RejectIfExists(const char* aCallSite)
+    void RejectIfExists(const char* aCallSite, TaskDispatcher& aDispatcher)
     {
       mTarget.Reset();
-      mPromise.RejectIfExists(true, aCallSite);
+      mPromise.RejectIfExists(true, aCallSite, aDispatcher);
     }
 
     ~SeekJob()
     {
       MOZ_DIAGNOSTIC_ASSERT(!mTarget.IsValid());
       MOZ_DIAGNOSTIC_ASSERT(mPromise.IsEmpty());
     }
 
@@ -1209,12 +1248,14 @@ protected:
   // LoadedMetadataEvent was already sent.
   bool mSentLoadedMetadataEvent;
   // True if we are back from DECODER_STATE_DORMANT state and
   // FirstFrameLoadedEvent was already sent, then we can skip
   // SetStartTime because the mStartTime already set before. Also we don't need
   // to decode any audio/video since the MediaDecoder will trigger a seek
   // operation soon.
   bool mSentFirstFrameLoadedEvent;
+
+  bool mSentPlaybackEndedEvent;
 };
 
 } // namespace mozilla;
 #endif
--- a/dom/media/MediaPromise.h
+++ b/dom/media/MediaPromise.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #if !defined(MediaPromise_h_)
 #define MediaPromise_h_
 
 #include "prlog.h"
 
 #include "AbstractThread.h"
+#include "TaskDispatcher.h"
 
 #include "nsTArray.h"
 #include "nsThreadUtils.h"
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/Monitor.h"
@@ -69,28 +70,30 @@ public:
   // interface (upon which the creator of the promise may invoke Resolve() or
   // Reject()). APIs should create and store a MediaPromise::Private (usually
   // via a MediaPromiseHolder), and return a MediaPromise to consumers.
   //
   // NB: We can include the definition of this class inline once B2G ICS is gone.
   class Private;
 
   static nsRefPtr<MediaPromise>
-  CreateAndResolve(ResolveValueType aResolveValue, const char* aResolveSite)
+  CreateAndResolve(ResolveValueType aResolveValue, const char* aResolveSite,
+                   TaskDispatcher& aDispatcher = PassByRef<AutoTaskDispatcher>())
   {
     nsRefPtr<typename MediaPromise::Private> p = new MediaPromise::Private(aResolveSite);
-    p->Resolve(aResolveValue, aResolveSite);
+    p->Resolve(aResolveValue, aResolveSite, aDispatcher);
     return Move(p);
   }
 
   static nsRefPtr<MediaPromise>
-  CreateAndReject(RejectValueType aRejectValue, const char* aRejectSite)
+  CreateAndReject(RejectValueType aRejectValue, const char* aRejectSite,
+                  TaskDispatcher& aDispatcher = PassByRef<AutoTaskDispatcher>())
   {
     nsRefPtr<typename MediaPromise::Private> p = new MediaPromise::Private(aRejectSite);
-    p->Reject(aRejectValue, aRejectSite);
+    p->Reject(aRejectValue, aRejectSite, aDispatcher);
     return Move(p);
   }
 
   class Consumer
   {
   public:
     NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Consumer)
 
@@ -166,17 +169,18 @@ protected:
 
     private:
       nsRefPtr<ThenValueBase> mThenValue;
       RejectValueType mRejectValue;
     };
 
     explicit ThenValueBase(const char* aCallSite) : mCallSite(aCallSite) {}
 
-    virtual void Dispatch(MediaPromise *aPromise) = 0;
+    virtual void Dispatch(MediaPromise *aPromise,
+                          TaskDispatcher& aDispatcher = PassByRef<AutoTaskDispatcher>()) = 0;
 
   protected:
     virtual void DoResolve(ResolveValueType aResolveValue) = 0;
     virtual void DoReject(RejectValueType aRejectValue) = 0;
 
     const char* mCallSite;
   };
 
@@ -214,37 +218,33 @@ protected:
               ResolveMethodType aResolveMethod, RejectMethodType aRejectMethod,
               const char* aCallSite)
       : ThenValueBase(aCallSite)
       , mResponseTarget(aResponseTarget)
       , mThisVal(aThisVal)
       , mResolveMethod(aResolveMethod)
       , mRejectMethod(aRejectMethod) {}
 
-    void Dispatch(MediaPromise *aPromise) override
+    void Dispatch(MediaPromise *aPromise, TaskDispatcher& aDispatcher) override
     {
       aPromise->mMutex.AssertCurrentThreadOwns();
       MOZ_ASSERT(!aPromise->IsPending());
       bool resolved = aPromise->mResolveValue.isSome();
       nsRefPtr<nsRunnable> runnable =
         resolved ? static_cast<nsRunnable*>(new (typename ThenValueBase::ResolveRunnable)(this, aPromise->mResolveValue.ref()))
                  : static_cast<nsRunnable*>(new (typename ThenValueBase::RejectRunnable)(this, aPromise->mRejectValue.ref()));
       PROMISE_LOG("%s Then() call made from %s [Runnable=%p, Promise=%p, ThenValue=%p]",
                   resolved ? "Resolving" : "Rejecting", ThenValueBase::mCallSite,
                   runnable.get(), aPromise, this);
-      nsresult rv = mResponseTarget->Dispatch(runnable.forget());
 
-      // NB: mDisconnected is only supposed to be accessed on the dispatch
-      // thread. However, we require the consumer to have disconnected any
-      // oustanding promise requests _before_ initiating shutdown on the
-      // thread or task queue. So the only non-buggy scenario for dispatch
-      // failing involves the target thread being unable to manipulate the
-      // ThenValue (since it's been disconnected), so it's safe to read here.
-      MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv) || Consumer::mDisconnected);
-      unused << rv;
+      // Promise consumers are allowed to disconnect the Consumer object and
+      // then shut down the thread or task queue that the promise result would
+      // be dispatched on. So we unfortunately can't assert that promise
+      // dispatch succeeds. :-(
+      aDispatcher.AddTask(mResponseTarget, runnable.forget(), /* aAssertDispatchSuccess = */ false);
     }
 
 #ifdef DEBUG
   void AssertOnDispatchThread()
   {
     MOZ_ASSERT(mResponseTarget->IsCurrentThreadIn());
   }
 #else
@@ -303,81 +303,94 @@ protected:
     nsRefPtr<ThisType> mThisVal; // Only accessed and refcounted on dispatch thread.
     ResolveMethodType mResolveMethod;
     RejectMethodType mRejectMethod;
   };
 public:
 
   template<typename ThisType, typename ResolveMethodType, typename RejectMethodType>
   already_AddRefed<Consumer> RefableThen(AbstractThread* aResponseThread, const char* aCallSite, ThisType* aThisVal,
-                                         ResolveMethodType aResolveMethod, RejectMethodType aRejectMethod)
+                                         ResolveMethodType aResolveMethod, RejectMethodType aRejectMethod,
+                                         TaskDispatcher& aDispatcher = PassByRef<AutoTaskDispatcher>())
   {
     MutexAutoLock lock(mMutex);
+
+    // {Refable,}Then() rarely dispatch directly - they do so only in the case
+    // where the promise has already been resolved by the time {Refable,}Then()
+    // is invoked. This case is rare, but it _can_ happen, which makes it a ripe
+    // target for race bugs. So we do an extra assertion here to make sure our
+    // caller is using tail dispatch correctly no matter what, rather than
+    // relying on the assertion in Dispatch(), which may be called extremely
+    // infrequently.
+    aDispatcher.AssertIsTailDispatcherIfRequired();
+
     MOZ_DIAGNOSTIC_ASSERT(!IsExclusive || !mHaveConsumer);
     mHaveConsumer = true;
     nsRefPtr<ThenValueBase> thenValue = new ThenValue<ThisType, ResolveMethodType, RejectMethodType>(
                                               aResponseThread, aThisVal, aResolveMethod, aRejectMethod, aCallSite);
     PROMISE_LOG("%s invoking Then() [this=%p, thenValue=%p, aThisVal=%p, isPending=%d]",
                 aCallSite, this, thenValue.get(), aThisVal, (int) IsPending());
     if (!IsPending()) {
-      thenValue->Dispatch(this);
+      thenValue->Dispatch(this, aDispatcher);
     } else {
       mThenValues.AppendElement(thenValue);
     }
 
     return thenValue.forget();
   }
 
   template<typename ThisType, typename ResolveMethodType, typename RejectMethodType>
   void Then(AbstractThread* aResponseThread, const char* aCallSite, ThisType* aThisVal,
-            ResolveMethodType aResolveMethod, RejectMethodType aRejectMethod)
+            ResolveMethodType aResolveMethod, RejectMethodType aRejectMethod,
+            TaskDispatcher& aDispatcher = PassByRef<AutoTaskDispatcher>())
   {
     nsRefPtr<Consumer> c =
-      RefableThen(aResponseThread, aCallSite, aThisVal, aResolveMethod, aRejectMethod);
+      RefableThen(aResponseThread, aCallSite, aThisVal, aResolveMethod, aRejectMethod, aDispatcher);
     return;
   }
 
-  void ChainTo(already_AddRefed<Private> aChainedPromise, const char* aCallSite)
+  void ChainTo(already_AddRefed<Private> aChainedPromise, const char* aCallSite,
+               TaskDispatcher& aDispatcher = PassByRef<AutoTaskDispatcher>())
   {
     MutexAutoLock lock(mMutex);
     MOZ_DIAGNOSTIC_ASSERT(!IsExclusive || !mHaveConsumer);
     mHaveConsumer = true;
     nsRefPtr<Private> chainedPromise = aChainedPromise;
     PROMISE_LOG("%s invoking Chain() [this=%p, chainedPromise=%p, isPending=%d]",
                 aCallSite, this, chainedPromise.get(), (int) IsPending());
     if (!IsPending()) {
-      ForwardTo(chainedPromise);
+      ForwardTo(chainedPromise, aDispatcher);
     } else {
       mChainedPromises.AppendElement(chainedPromise);
     }
   }
 
 protected:
   bool IsPending() { return mResolveValue.isNothing() && mRejectValue.isNothing(); }
-  void DispatchAll()
+  void DispatchAll(TaskDispatcher& aDispatcher)
   {
     mMutex.AssertCurrentThreadOwns();
     for (size_t i = 0; i < mThenValues.Length(); ++i) {
-      mThenValues[i]->Dispatch(this);
+      mThenValues[i]->Dispatch(this, aDispatcher);
     }
     mThenValues.Clear();
 
     for (size_t i = 0; i < mChainedPromises.Length(); ++i) {
-      ForwardTo(mChainedPromises[i]);
+      ForwardTo(mChainedPromises[i], aDispatcher);
     }
     mChainedPromises.Clear();
   }
 
-  void ForwardTo(Private* aOther)
+  void ForwardTo(Private* aOther, TaskDispatcher& aDispatcher)
   {
     MOZ_ASSERT(!IsPending());
     if (mResolveValue.isSome()) {
-      aOther->Resolve(mResolveValue.ref(), "<chained promise>");
+      aOther->Resolve(mResolveValue.ref(), "<chained promise>", aDispatcher);
     } else {
-      aOther->Reject(mRejectValue.ref(), "<chained promise>");
+      aOther->Reject(mRejectValue.ref(), "<chained promise>", aDispatcher);
     }
   }
 
   ~MediaPromise()
   {
     PROMISE_LOG("MediaPromise::~MediaPromise [this=%p]", this);
     MOZ_ASSERT(!IsPending());
     MOZ_ASSERT(mThenValues.IsEmpty());
@@ -395,32 +408,34 @@ protected:
 
 template<typename ResolveValueT, typename RejectValueT, bool IsExclusive>
 class MediaPromise<ResolveValueT, RejectValueT, IsExclusive>::Private
   : public MediaPromise<ResolveValueT, RejectValueT, IsExclusive>
 {
 public:
   explicit Private(const char* aCreationSite) : MediaPromise(aCreationSite) {}
 
-  void Resolve(ResolveValueT aResolveValue, const char* aResolveSite)
+  void Resolve(ResolveValueT aResolveValue, const char* aResolveSite,
+               TaskDispatcher& aDispatcher = PassByRef<AutoTaskDispatcher>())
   {
     MutexAutoLock lock(mMutex);
     MOZ_ASSERT(IsPending());
     PROMISE_LOG("%s resolving MediaPromise (%p created at %s)", aResolveSite, this, mCreationSite);
     mResolveValue.emplace(aResolveValue);
-    DispatchAll();
+    DispatchAll(aDispatcher);
   }
 
-  void Reject(RejectValueT aRejectValue, const char* aRejectSite)
+  void Reject(RejectValueT aRejectValue, const char* aRejectSite,
+              TaskDispatcher& aDispatcher = PassByRef<AutoTaskDispatcher>())
   {
     MutexAutoLock lock(mMutex);
     MOZ_ASSERT(IsPending());
     PROMISE_LOG("%s rejecting MediaPromise (%p created at %s)", aRejectSite, this, mCreationSite);
     mRejectValue.emplace(aRejectValue);
-    DispatchAll();
+    DispatchAll(aDispatcher);
   }
 };
 
 /*
  * Class to encapsulate a promise for a particular role. Use this as the member
  * variable for a class whose method returns a promise.
  */
 template<typename PromiseType>
@@ -471,50 +486,56 @@ public:
     }
 
     nsRefPtr<typename PromiseType::Private> p = mPromise;
     mPromise = nullptr;
     return p.forget();
   }
 
   void Resolve(typename PromiseType::ResolveValueType aResolveValue,
-               const char* aMethodName)
+               const char* aMethodName,
+               TaskDispatcher& aDispatcher = PassByRef<AutoTaskDispatcher>())
   {
     if (mMonitor) {
       mMonitor->AssertCurrentThreadOwns();
     }
     MOZ_ASSERT(mPromise);
-    mPromise->Resolve(aResolveValue, aMethodName);
+    mPromise->Resolve(aResolveValue, aMethodName, aDispatcher);
     mPromise = nullptr;
   }
 
+
   void ResolveIfExists(typename PromiseType::ResolveValueType aResolveValue,
-                       const char* aMethodName)
+                       const char* aMethodName,
+                       TaskDispatcher& aDispatcher = PassByRef<AutoTaskDispatcher>())
   {
     if (!IsEmpty()) {
-      Resolve(aResolveValue, aMethodName);
+      Resolve(aResolveValue, aMethodName, aDispatcher);
     }
   }
 
   void Reject(typename PromiseType::RejectValueType aRejectValue,
-              const char* aMethodName)
+              const char* aMethodName,
+              TaskDispatcher& aDispatcher = PassByRef<AutoTaskDispatcher>())
   {
     if (mMonitor) {
       mMonitor->AssertCurrentThreadOwns();
     }
     MOZ_ASSERT(mPromise);
-    mPromise->Reject(aRejectValue, aMethodName);
+    mPromise->Reject(aRejectValue, aMethodName, aDispatcher);
     mPromise = nullptr;
   }
 
+
   void RejectIfExists(typename PromiseType::RejectValueType aRejectValue,
-                      const char* aMethodName)
+                      const char* aMethodName,
+                      TaskDispatcher& aDispatcher = PassByRef<AutoTaskDispatcher>())
   {
     if (!IsEmpty()) {
-      Reject(aRejectValue, aMethodName);
+      Reject(aRejectValue, aMethodName, aDispatcher);
     }
   }
 
 private:
   Monitor* mMonitor;
   nsRefPtr<typename PromiseType::Private> mPromise;
 };
 
@@ -640,55 +661,57 @@ public:
 
 private:
   nsRefPtr<typename PromiseType::Private> mProxyPromise;
   nsAutoPtr<MethodCallBase<PromiseType>> mMethodCall;
 };
 
 template<typename PromiseType>
 static nsRefPtr<PromiseType>
-ProxyInternal(AbstractThread* aTarget, MethodCallBase<PromiseType>* aMethodCall, const char* aCallerName)
+ProxyInternal(AbstractThread* aTarget, MethodCallBase<PromiseType>* aMethodCall, const char* aCallerName,
+              TaskDispatcher& aDispatcher)
 {
   nsRefPtr<typename PromiseType::Private> p = new (typename PromiseType::Private)(aCallerName);
   nsRefPtr<ProxyRunnable<PromiseType>> r = new ProxyRunnable<PromiseType>(p, aMethodCall);
-  nsresult rv = aTarget->Dispatch(r.forget());
-  MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
-  unused << rv;
+  aDispatcher.AddTask(aTarget, r.forget());
   return Move(p);
 }
 
 } // namespace detail
 
 template<typename PromiseType, typename ThisType>
 static nsRefPtr<PromiseType>
 ProxyMediaCall(AbstractThread* aTarget, ThisType* aThisVal, const char* aCallerName,
-               nsRefPtr<PromiseType>(ThisType::*aMethod)())
+               nsRefPtr<PromiseType>(ThisType::*aMethod)(),
+               TaskDispatcher& aDispatcher = PassByRef<AutoTaskDispatcher>())
 {
   typedef detail::MethodCallWithNoArgs<PromiseType, ThisType> MethodCallType;
   MethodCallType* methodCall = new MethodCallType(aThisVal, aMethod);
-  return detail::ProxyInternal(aTarget, methodCall, aCallerName);
+  return detail::ProxyInternal(aTarget, methodCall, aCallerName, aDispatcher);
 }
 
 template<typename PromiseType, typename ThisType, typename Arg1Type>
 static nsRefPtr<PromiseType>
 ProxyMediaCall(AbstractThread* aTarget, ThisType* aThisVal, const char* aCallerName,
-               nsRefPtr<PromiseType>(ThisType::*aMethod)(Arg1Type), Arg1Type aArg1)
+               nsRefPtr<PromiseType>(ThisType::*aMethod)(Arg1Type), Arg1Type aArg1,
+               TaskDispatcher& aDispatcher = PassByRef<AutoTaskDispatcher>())
 {
   typedef detail::MethodCallWithOneArg<PromiseType, ThisType, Arg1Type> MethodCallType;
   MethodCallType* methodCall = new MethodCallType(aThisVal, aMethod, aArg1);
-  return detail::ProxyInternal(aTarget, methodCall, aCallerName);
+  return detail::ProxyInternal(aTarget, methodCall, aCallerName, aDispatcher);
 }
 
 template<typename PromiseType, typename ThisType, typename Arg1Type, typename Arg2Type>
 static nsRefPtr<PromiseType>
 ProxyMediaCall(AbstractThread* aTarget, ThisType* aThisVal, const char* aCallerName,
-               nsRefPtr<PromiseType>(ThisType::*aMethod)(Arg1Type, Arg2Type), Arg1Type aArg1, Arg2Type aArg2)
+               nsRefPtr<PromiseType>(ThisType::*aMethod)(Arg1Type, Arg2Type), Arg1Type aArg1, Arg2Type aArg2,
+               TaskDispatcher& aDispatcher = PassByRef<AutoTaskDispatcher>())
 {
   typedef detail::MethodCallWithTwoArgs<PromiseType, ThisType, Arg1Type, Arg2Type> MethodCallType;
   MethodCallType* methodCall = new MethodCallType(aThisVal, aMethod, aArg1, aArg2);
-  return detail::ProxyInternal(aTarget, methodCall, aCallerName);
+  return detail::ProxyInternal(aTarget, methodCall, aCallerName, aDispatcher);
 }
 
 #undef PROMISE_LOG
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/MediaQueue.h
+++ b/dom/media/MediaQueue.h
@@ -171,26 +171,27 @@ private:
       , mTarget(aTarget)
     {
     }
     Listener(const Listener& aOther)
       : mRunnable(aOther.mRunnable)
       , mTarget(aOther.mTarget)
     {
     }
-    RefPtr<nsIRunnable> mRunnable;
+    nsCOMPtr<nsIRunnable> mRunnable;
     RefPtr<MediaTaskQueue> mTarget;
   };
 
   nsTArray<Listener> mPopListeners;
 
   void NotifyPopListeners() {
     for (uint32_t i = 0; i < mPopListeners.Length(); i++) {
       Listener& l = mPopListeners[i];
-      l.mTarget->Dispatch(l.mRunnable);
+      nsCOMPtr<nsIRunnable> r = l.mRunnable;
+      l.mTarget->MaybeTailDispatch(r.forget());
     }
   }
 
   // True when we've decoded the last frame of data in the
   // bitstream for which we're queueing frame data.
   bool mEndOfStream;
 };
 
--- a/dom/media/MediaSegment.h
+++ b/dom/media/MediaSegment.h
@@ -260,16 +260,28 @@ public:
     bool IsEnded() { return mIndex >= mSegment.mChunks.Length(); }
     void Next() { ++mIndex; }
     Chunk& operator*() { return mSegment.mChunks[mIndex]; }
     Chunk* operator->() { return &mSegment.mChunks[mIndex]; }
   private:
     MediaSegmentBase<C, Chunk>& mSegment;
     uint32_t mIndex;
   };
+  class ConstChunkIterator {
+  public:
+    explicit ConstChunkIterator(const MediaSegmentBase<C, Chunk>& aSegment)
+      : mSegment(aSegment), mIndex(0) {}
+    bool IsEnded() { return mIndex >= mSegment.mChunks.Length(); }
+    void Next() { ++mIndex; }
+    const Chunk& operator*() { return mSegment.mChunks[mIndex]; }
+    const Chunk* operator->() { return &mSegment.mChunks[mIndex]; }
+  private:
+    const MediaSegmentBase<C, Chunk>& mSegment;
+    uint32_t mIndex;
+  };
 
   void RemoveLeading(StreamTime aDuration)
   {
     RemoveLeading(aDuration, 0);
   }
 
 #ifdef MOZILLA_INTERNAL_API
   void GetStartTime(TimeStamp &aTime) {
--- a/dom/media/MediaStreamGraph.cpp
+++ b/dom/media/MediaStreamGraph.cpp
@@ -273,17 +273,21 @@ MediaStreamGraphImpl::UpdateBufferSuffic
       data->mHaveEnough = track->GetEnd() >= desiredEnd;
       if (!data->mHaveEnough) {
         runnables.MoveElementsFrom(data->mDispatchWhenNotEnough);
       }
     }
   }
 
   for (uint32_t i = 0; i < runnables.Length(); ++i) {
-    runnables[i].mTarget->Dispatch(runnables[i].mRunnable);
+    // This dispatch was observed to fail in test_video_dimensions.html on
+    // win8 64 debug when invoked from noop_resampler::fill on the cubeb audio
+    // thread.
+    nsCOMPtr<nsIRunnable> r = runnables[i].mRunnable;
+    runnables[i].mTarget->MaybeTailDispatch(r.forget(), /* aAssertDispatchSuccess = */ false);
   }
 }
 
 StreamTime
 MediaStreamGraphImpl::GraphTimeToStreamTime(MediaStream* aStream,
                                             GraphTime aTime)
 {
   MOZ_ASSERT(aTime <= CurrentDriver()->StateComputedTime(),
@@ -2496,26 +2500,28 @@ SourceMediaStream::GetEndOfAppendedData(
 
 void
 SourceMediaStream::DispatchWhenNotEnoughBuffered(TrackID aID,
     MediaTaskQueue* aSignalQueue, nsIRunnable* aSignalRunnable)
 {
   MutexAutoLock lock(mMutex);
   TrackData* data = FindDataForTrack(aID);
   if (!data) {
-    aSignalQueue->Dispatch(aSignalRunnable);
+    nsCOMPtr<nsIRunnable> r = aSignalRunnable;
+    aSignalQueue->MaybeTailDispatch(r.forget());
     return;
   }
 
   if (data->mHaveEnough) {
     if (data->mDispatchWhenNotEnough.IsEmpty()) {
       data->mDispatchWhenNotEnough.AppendElement()->Init(aSignalQueue, aSignalRunnable);
     }
   } else {
-    aSignalQueue->Dispatch(aSignalRunnable);
+    nsCOMPtr<nsIRunnable> r = aSignalRunnable;
+    aSignalQueue->MaybeTailDispatch(r.forget());
   }
 }
 
 void
 SourceMediaStream::EndTrack(TrackID aID)
 {
   MutexAutoLock lock(mMutex);
   TrackData *track = FindDataForTrack(aID);
--- a/dom/media/MediaStreamGraph.h
+++ b/dom/media/MediaStreamGraph.h
@@ -850,17 +850,17 @@ protected:
   struct ThreadAndRunnable {
     void Init(MediaTaskQueue* aTarget, nsIRunnable* aRunnable)
     {
       mTarget = aTarget;
       mRunnable = aRunnable;
     }
 
     nsRefPtr<MediaTaskQueue> mTarget;
-    RefPtr<nsIRunnable> mRunnable;
+    nsCOMPtr<nsIRunnable> mRunnable;
   };
   enum TrackCommands {
     TRACK_CREATE = MediaStreamListener::TRACK_EVENT_CREATED,
     TRACK_END = MediaStreamListener::TRACK_EVENT_ENDED
   };
   /**
    * Data for each track that hasn't ended.
    */
--- a/dom/media/MediaTaskQueue.cpp
+++ b/dom/media/MediaTaskQueue.cpp
@@ -5,43 +5,66 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "MediaTaskQueue.h"
 #include "nsThreadUtils.h"
 #include "SharedThreadPool.h"
 
 namespace mozilla {
 
-MediaTaskQueue::MediaTaskQueue(TemporaryRef<SharedThreadPool> aPool)
+ThreadLocal<MediaTaskQueue*> MediaTaskQueue::sCurrentQueueTLS;
+
+/* static */ void
+MediaTaskQueue::InitStatics()
+{
+  if (!sCurrentQueueTLS.init()) {
+    MOZ_CRASH();
+  }
+}
+
+MediaTaskQueue::MediaTaskQueue(TemporaryRef<SharedThreadPool> aPool,
+                               bool aRequireTailDispatch)
   : mPool(aPool)
   , mQueueMonitor("MediaTaskQueue::Queue")
+  , mTailDispatcher(nullptr)
   , mIsRunning(false)
   , mIsShutdown(false)
   , mIsFlushing(false)
+  , mRequireTailDispatch(aRequireTailDispatch)
 {
   MOZ_COUNT_CTOR(MediaTaskQueue);
 }
 
 MediaTaskQueue::~MediaTaskQueue()
 {
   MonitorAutoLock mon(mQueueMonitor);
   MOZ_ASSERT(mIsShutdown);
   MOZ_COUNT_DTOR(MediaTaskQueue);
 }
 
 nsresult
 MediaTaskQueue::Dispatch(TemporaryRef<nsIRunnable> aRunnable)
 {
+  AssertInTailDispatchIfNeeded(); // Do this before acquiring the monitor.
   MonitorAutoLock mon(mQueueMonitor);
   return DispatchLocked(aRunnable, AbortIfFlushing);
 }
 
+TaskDispatcher&
+MediaTaskQueue::TailDispatcher()
+{
+  MOZ_ASSERT(IsCurrentThreadIn());
+  MOZ_ASSERT(mTailDispatcher);
+  return *mTailDispatcher;
+}
+
 nsresult
 MediaTaskQueue::ForceDispatch(TemporaryRef<nsIRunnable> aRunnable)
 {
+  AssertInTailDispatchIfNeeded(); // Do this before acquiring the monitor.
   MonitorAutoLock mon(mQueueMonitor);
   return DispatchLocked(aRunnable, Forced);
 }
 
 nsresult
 MediaTaskQueue::DispatchLocked(TemporaryRef<nsIRunnable> aRunnable,
                                DispatchMode aMode)
 {
@@ -154,16 +177,17 @@ FlushableMediaTaskQueue::Flush()
   AutoSetFlushing autoFlush(this);
   FlushLocked();
   AwaitIdleLocked();
 }
 
 nsresult
 FlushableMediaTaskQueue::FlushAndDispatch(TemporaryRef<nsIRunnable> aRunnable)
 {
+  AssertInTailDispatchIfNeeded(); // Do this before acquiring the monitor.
   MonitorAutoLock mon(mQueueMonitor);
   AutoSetFlushing autoFlush(this);
   FlushLocked();
   nsresult rv = DispatchLocked(aRunnable, IgnoreFlushing);
   NS_ENSURE_SUCCESS(rv, rv);
   AwaitIdleLocked();
   return NS_OK;
 }
@@ -191,17 +215,19 @@ MediaTaskQueue::IsEmpty()
   MonitorAutoLock mon(mQueueMonitor);
   return mTasks.empty();
 }
 
 bool
 MediaTaskQueue::IsCurrentThreadIn()
 {
   MonitorAutoLock mon(mQueueMonitor);
-  return NS_GetCurrentThread() == mRunningThread;
+  bool in = NS_GetCurrentThread() == mRunningThread;
+  MOZ_ASSERT_IF(in, GetCurrentQueue() == this);
+  return in;
 }
 
 nsresult
 MediaTaskQueue::Runner::Run()
 {
   RefPtr<nsIRunnable> event;
   {
     MonitorAutoLock mon(mQueue->mQueueMonitor);
@@ -218,17 +244,20 @@ MediaTaskQueue::Runner::Run()
   }
   MOZ_ASSERT(event);
 
   // Note that dropping the queue monitor before running the task, and
   // taking the monitor again after the task has run ensures we have memory
   // fences enforced. This means that if the object we're calling wasn't
   // designed to be threadsafe, it will be, provided we're only calling it
   // in this task queue.
-  event->Run();
+  {
+    AutoTaskGuard g(mQueue);
+    event->Run();
+  }
 
   // Drop the reference to event. The event will hold a reference to the
   // object it's calling, and we don't want to keep it alive, it may be
   // making assumptions what holds references to it. This is especially
   // the case if the object is waiting for us to shutdown, so that it
   // can shutdown (like in the MediaDecoderStateMachine's SHUTDOWN case).
   event = nullptr;
 
@@ -263,9 +292,24 @@ MediaTaskQueue::Runner::Run()
       mon.NotifyAll();
     }
     mQueue->mRunningThread = nullptr;
   }
 
   return NS_OK;
 }
 
+#ifdef DEBUG
+void
+TaskDispatcher::AssertIsTailDispatcherIfRequired()
+{
+  MediaTaskQueue* currentQueue = MediaTaskQueue::GetCurrentQueue();
+
+  // NB: Make sure not to use the TailDispatcher() accessor, since that
+  // asserts IsCurrentThreadIn(), which acquires the queue monitor, which
+  // triggers a deadlock during shutdown between the queue monitor and the
+  // MediaPromise monitor.
+  MOZ_ASSERT_IF(currentQueue && currentQueue->RequiresTailDispatch(),
+                this == currentQueue->mTailDispatcher);
+}
+#endif
+
 } // namespace mozilla
--- a/dom/media/MediaTaskQueue.h
+++ b/dom/media/MediaTaskQueue.h
@@ -5,39 +5,79 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MediaTaskQueue_h_
 #define MediaTaskQueue_h_
 
 #include <queue>
 #include "mozilla/RefPtr.h"
 #include "mozilla/Monitor.h"
+#include "mozilla/ThreadLocal.h"
 #include "SharedThreadPool.h"
 #include "nsThreadUtils.h"
 #include "MediaPromise.h"
+#include "TaskDispatcher.h"
 
 class nsIRunnable;
 
 namespace mozilla {
 
 class SharedThreadPool;
 
 typedef MediaPromise<bool, bool, false> ShutdownPromise;
 
 // Abstracts executing runnables in order in a thread pool. The runnables
 // dispatched to the MediaTaskQueue will be executed in the order in which
 // they're received, and are guaranteed to not be executed concurrently.
 // They may be executed on different threads, and a memory barrier is used
 // to make this threadsafe for objects that aren't already threadsafe.
 class MediaTaskQueue : public AbstractThread {
+  static ThreadLocal<MediaTaskQueue*> sCurrentQueueTLS;
 public:
-  explicit MediaTaskQueue(TemporaryRef<SharedThreadPool> aPool);
+  static void InitStatics();
+
+  // Returns the task queue that the caller is currently running in, or null
+  // if the caller is not running in a MediaTaskQueue.
+  static MediaTaskQueue* GetCurrentQueue() { return sCurrentQueueTLS.get(); }
+
+  explicit MediaTaskQueue(TemporaryRef<SharedThreadPool> aPool, bool aRequireTailDispatch = false);
 
   nsresult Dispatch(TemporaryRef<nsIRunnable> aRunnable);
 
+  // Returns a TaskDispatcher that will dispatch its tasks when the currently-
+  // running tasks pops off the stack.
+  //
+  // May only be called when running within the task queue it is invoked up.
+  TaskDispatcher& TailDispatcher();
+
+  // Returns true if this task queue requires all dispatches performed by its
+  // tasks to go through the tail dispatcher.
+  bool RequiresTailDispatch() { return mRequireTailDispatch; }
+
+#ifdef DEBUG
+  static void AssertInTailDispatchIfNeeded()
+  {
+    // See if we're currently running in a task queue that asserts tail
+    // dispatch.
+    MediaTaskQueue* currentQueue = MediaTaskQueue::GetCurrentQueue();
+    if (!currentQueue || !currentQueue->RequiresTailDispatch()) {
+      return;
+    }
+
+    // This is a bit tricky. The only moment when we're running in a task queue
+    // but don't have mTailDispatcher set is precisely the moment that we're
+    // doing tail dispatch (i.e. when AutoTaskGuard's destructor has already
+    // run and AutoTaskDispatcher's destructor is currently running).
+    MOZ_ASSERT(!currentQueue->mTailDispatcher,
+               "Not allowed to dispatch tasks directly from this task queue - use TailDispatcher()");
+  }
+#else
+  static void AssertInTailDispatchIfNeeded() {}
+#endif
+
   // For AbstractThread.
   nsresult Dispatch(already_AddRefed<nsIRunnable> aRunnable) override
   {
     RefPtr<nsIRunnable> r(aRunnable);
     return ForceDispatch(r);
   }
 
   // This should only be used for things that absolutely can't afford to be
@@ -98,27 +138,60 @@ protected:
   // Queue of tasks to run.
   std::queue<TaskQueueEntry> mTasks;
 
   // The thread currently running the task queue. We store a reference
   // to this so that IsCurrentThreadIn() can tell if the current thread
   // is the thread currently running in the task queue.
   RefPtr<nsIThread> mRunningThread;
 
+  // RAII class that gets instantiated for each dispatched task.
+  class AutoTaskGuard : public AutoTaskDispatcher
+  {
+  public:
+    explicit AutoTaskGuard(MediaTaskQueue* aQueue) : mQueue(aQueue)
+    {
+      // NB: We don't hold the lock to aQueue here. Don't do anything that
+      // might require it.
+      MOZ_ASSERT(!mQueue->mTailDispatcher);
+      mQueue->mTailDispatcher = this;
+
+      MOZ_ASSERT(sCurrentQueueTLS.get() == nullptr);
+      sCurrentQueueTLS.set(aQueue);
+
+    }
+
+    ~AutoTaskGuard()
+    {
+      sCurrentQueueTLS.set(nullptr);
+      mQueue->mTailDispatcher = nullptr;
+    }
+
+  private:
+  MediaTaskQueue* mQueue;
+  };
+
+  friend class TaskDispatcher;
+  TaskDispatcher* mTailDispatcher;
+
   // True if we've dispatched an event to the pool to execute events from
   // the queue.
   bool mIsRunning;
 
   // True if we've started our shutdown process.
   bool mIsShutdown;
   MediaPromiseHolder<ShutdownPromise> mShutdownPromise;
 
   // True if we're flushing; we reject new tasks if we're flushing.
   bool mIsFlushing;
 
+  // True if we want to require that every task dispatched from tasks running in
+  // this queue go through our queue's tail dispatcher.
+  bool mRequireTailDispatch;
+
   class Runner : public nsRunnable {
   public:
     explicit Runner(MediaTaskQueue* aQueue)
       : mQueue(aQueue)
     {
     }
     NS_METHOD Run() override;
   private:
new file mode 100644
--- /dev/null
+++ b/dom/media/TaskDispatcher.h
@@ -0,0 +1,165 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/. */
+
+#if !defined(TaskDispatcher_h_)
+#define TaskDispatcher_h_
+
+#include "AbstractThread.h"
+
+#include "mozilla/UniquePtr.h"
+#include "mozilla/unused.h"
+
+#include "nsISupportsImpl.h"
+#include "nsTArray.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+
+/*
+ * A classic approach to cross-thread communication is to dispatch asynchronous
+ * runnables to perform updates on other threads. This generally works well, but
+ * there are sometimes reasons why we might want to delay the actual dispatch of
+ * these tasks until a specified moment. At present, this is primarily useful to
+ * ensure that mirrored state gets updated atomically - but there may be other
+ * applications as well.
+ *
+ * TaskDispatcher is a general abstract class that accepts tasks and dispatches
+ * them at some later point. These groups of tasks are per-target-thread, and
+ * contain separate queues for two kinds of tasks - "state change tasks" (which
+ * run first, and are intended to be used to update the value held by mirrors),
+ * and regular tasks, which are other arbitrary operations that the are gated
+ * to run after all the state changes have completed.
+ */
+class TaskDispatcher
+{
+public:
+  TaskDispatcher() {}
+  virtual ~TaskDispatcher() {}
+
+  virtual void AddStateChangeTask(AbstractThread* aThread,
+                                  already_AddRefed<nsIRunnable> aRunnable) = 0;
+  virtual void AddTask(AbstractThread* aThread,
+                       already_AddRefed<nsIRunnable> aRunnable,
+                       bool aAssertDispatchSuccess = true) = 0;
+
+#ifdef DEBUG
+  void AssertIsTailDispatcherIfRequired();
+#else
+  void AssertIsTailDispatcherIfRequired() {}
+#endif
+};
+
+/*
+ * AutoTaskDispatcher is a stack-scoped TaskDispatcher implementation that fires
+ * its queued tasks when it is popped off the stack.
+ */
+class MOZ_STACK_CLASS AutoTaskDispatcher : public TaskDispatcher
+{
+public:
+  AutoTaskDispatcher() {}
+  ~AutoTaskDispatcher()
+  {
+    for (size_t i = 0; i < mTaskGroups.Length(); ++i) {
+      UniquePtr<PerThreadTaskGroup> group(Move(mTaskGroups[i]));
+      nsRefPtr<AbstractThread> thread = group->mThread;
+      bool assertDispatchSuccess = group->mAssertDispatchSuccess;
+      nsCOMPtr<nsIRunnable> r = new TaskGroupRunnable(Move(group));
+      nsresult rv = thread->Dispatch(r.forget());
+      MOZ_DIAGNOSTIC_ASSERT(!assertDispatchSuccess || NS_SUCCEEDED(rv));
+      unused << rv;
+    }
+  }
+
+  void AddStateChangeTask(AbstractThread* aThread,
+                          already_AddRefed<nsIRunnable> aRunnable) override
+  {
+    EnsureTaskGroup(aThread).mStateChangeTasks.AppendElement(aRunnable);
+  }
+
+  void AddTask(AbstractThread* aThread,
+               already_AddRefed<nsIRunnable> aRunnable,
+               bool aAssertDispatchSuccess) override
+  {
+    PerThreadTaskGroup& group = EnsureTaskGroup(aThread);
+    group.mRegularTasks.AppendElement(aRunnable);
+
+    // The task group needs to assert dispatch success if any of the runnables
+    // it's dispatching want to assert it.
+    group.mAssertDispatchSuccess = group.mAssertDispatchSuccess || aAssertDispatchSuccess;
+  }
+
+private:
+
+  struct PerThreadTaskGroup
+  {
+  public:
+    explicit PerThreadTaskGroup(AbstractThread* aThread)
+      : mThread(aThread), mAssertDispatchSuccess(false)
+    {
+      MOZ_COUNT_CTOR(PerThreadTaskGroup);
+    }
+
+    ~PerThreadTaskGroup() { MOZ_COUNT_DTOR(PerThreadTaskGroup); }
+
+    nsRefPtr<AbstractThread> mThread;
+    nsTArray<nsCOMPtr<nsIRunnable>> mStateChangeTasks;
+    nsTArray<nsCOMPtr<nsIRunnable>> mRegularTasks;
+    bool mAssertDispatchSuccess;
+  };
+
+  class TaskGroupRunnable : public nsRunnable
+  {
+    public:
+      explicit TaskGroupRunnable(UniquePtr<PerThreadTaskGroup>&& aTasks) : mTasks(Move(aTasks)) {}
+
+      NS_IMETHODIMP Run()
+      {
+        for (size_t i = 0; i < mTasks->mStateChangeTasks.Length(); ++i) {
+          mTasks->mStateChangeTasks[i]->Run();
+        }
+
+        for (size_t i = 0; i < mTasks->mRegularTasks.Length(); ++i) {
+          mTasks->mRegularTasks[i]->Run();
+        }
+
+        return NS_OK;
+      }
+
+    private:
+      UniquePtr<PerThreadTaskGroup> mTasks;
+  };
+
+  PerThreadTaskGroup& EnsureTaskGroup(AbstractThread* aThread)
+  {
+    for (size_t i = 0; i < mTaskGroups.Length(); ++i) {
+      if (mTaskGroups[i]->mThread == aThread) {
+        return *mTaskGroups[i];
+      }
+    }
+
+    mTaskGroups.AppendElement(new PerThreadTaskGroup(aThread));
+    return *mTaskGroups.LastElement();
+  }
+
+  // Task groups, organized by thread.
+  nsTArray<UniquePtr<PerThreadTaskGroup>> mTaskGroups;
+};
+
+// Little utility class to allow declaring AutoTaskDispatcher as a default
+// parameter for methods that take a TaskDispatcher&.
+template<typename T>
+class PassByRef
+{
+public:
+  PassByRef() {}
+  operator T&() { return mVal; }
+private:
+  T mVal;
+};
+
+} // namespace mozilla
+
+#endif
--- a/dom/media/eme/CDMProxy.cpp
+++ b/dom/media/eme/CDMProxy.cpp
@@ -12,16 +12,17 @@
 #include "nsContentCID.h"
 #include "nsServiceManagerUtils.h"
 #include "MainThreadUtils.h"
 #include "mozilla/EMEUtils.h"
 #include "nsIConsoleService.h"
 #include "prenv.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/CDMCallbackProxy.h"
+#include "MediaData.h"
 
 namespace mozilla {
 
 CDMProxy::CDMProxy(dom::MediaKeys* aKeys, const nsAString& aKeySystem)
   : mKeys(aKeys)
   , mKeySystem(aKeySystem)
   , mCDM(nullptr)
   , mDecryptionJobCount(0)
@@ -608,17 +609,17 @@ CDMProxy::KeySystem() const
 }
 
 CDMCaps&
 CDMProxy::Capabilites() {
   return mCapabilites;
 }
 
 void
-CDMProxy::Decrypt(mp4_demuxer::MP4Sample* aSample,
+CDMProxy::Decrypt(MediaRawData* aSample,
                   DecryptionClient* aClient)
 {
   nsAutoPtr<DecryptJob> job(new DecryptJob(aSample, aClient));
   nsCOMPtr<nsIRunnable> task(
     NS_NewRunnableMethodWithArg<nsAutoPtr<DecryptJob>>(this, &CDMProxy::gmp_Decrypt, job));
   mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
 }
 
@@ -631,43 +632,44 @@ CDMProxy::gmp_Decrypt(nsAutoPtr<DecryptJ
 
   if (!mCDM) {
     aJob->mClient->Decrypted(GMPAbortedErr, nullptr);
     return;
   }
 
   aJob->mId = ++mDecryptionJobCount;
   nsTArray<uint8_t> data;
-  data.AppendElements(aJob->mSample->data, aJob->mSample->size);
-  mCDM->Decrypt(aJob->mId, aJob->mSample->crypto, data);
+  data.AppendElements(aJob->mSample->mData, aJob->mSample->mSize);
+  mCDM->Decrypt(aJob->mId, aJob->mSample->mCrypto, data);
   mDecryptionJobs.AppendElement(aJob.forget());
 }
 
 void
 CDMProxy::gmp_Decrypted(uint32_t aId,
                         GMPErr aResult,
                         const nsTArray<uint8_t>& aDecryptedData)
 {
   MOZ_ASSERT(IsOnGMPThread());
   for (size_t i = 0; i < mDecryptionJobs.Length(); i++) {
     DecryptJob* job = mDecryptionJobs[i];
     if (job->mId == aId) {
-      if (aDecryptedData.Length() != job->mSample->size) {
+      if (aDecryptedData.Length() != job->mSample->mSize) {
         NS_WARNING("CDM returned incorrect number of decrypted bytes");
       }
       if (GMP_SUCCEEDED(aResult)) {
-        PodCopy(job->mSample->data,
+        nsAutoPtr<MediaRawDataWriter> writer(job->mSample->CreateWriter());
+        PodCopy(writer->mData,
                 aDecryptedData.Elements(),
-                std::min<size_t>(aDecryptedData.Length(), job->mSample->size));
-        job->mClient->Decrypted(GMPNoErr, job->mSample.forget());
+                std::min<size_t>(aDecryptedData.Length(), job->mSample->mSize));
+        job->mClient->Decrypted(GMPNoErr, job->mSample);
       } else if (aResult == GMPNoKeyErr) {
         NS_WARNING("CDM returned GMPNoKeyErr");
         // We still have the encrypted sample, so we can re-enqueue it to be
         // decrypted again once the key is usable again.
-        job->mClient->Decrypted(GMPNoKeyErr, job->mSample.forget());
+        job->mClient->Decrypted(GMPNoKeyErr, job->mSample);
       } else {
         nsAutoCString str("CDM returned decode failure GMPErr=");
         str.AppendInt(aResult);
         NS_WARNING(str.get());
         job->mClient->Decrypted(aResult, nullptr);
       }
       mDecryptionJobs.RemoveElementAt(i);
       return;
--- a/dom/media/eme/CDMProxy.h
+++ b/dom/media/eme/CDMProxy.h
@@ -9,31 +9,30 @@
 
 #include "nsString.h"
 #include "nsAutoPtr.h"
 #include "mozilla/dom/MediaKeys.h"
 #include "mozilla/Monitor.h"
 #include "nsIThread.h"
 #include "GMPDecryptorProxy.h"
 #include "mozilla/CDMCaps.h"
-#include "mp4_demuxer/DecoderData.h"
 
 namespace mozilla {
-
+class MediaRawData;
 class CDMCallbackProxy;
 
 namespace dom {
 class MediaKeySession;
 }
 
 class DecryptionClient {
 public:
   virtual ~DecryptionClient() {}
   virtual void Decrypted(GMPErr aResult,
-                         mp4_demuxer::MP4Sample* aSample) = 0;
+                         MediaRawData* aSample) = 0;
 };
 
 // Proxies calls GMP/CDM, and proxies calls back.
 // Note: Promises are passed in via a PromiseId, so that the ID can be
 // passed via IPC to the CDM, which can then signal when to reject or
 // resolve the promise using its PromiseId.
 class CDMProxy {
   typedef dom::PromiseId PromiseId;
@@ -137,18 +136,17 @@ public:
                       const nsAString& aMsg);
 
   // Main thread only.
   void OnRejectPromise(uint32_t aPromiseId,
                        nsresult aDOMException,
                        const nsAString& aMsg);
 
   // Threadsafe.
-  void Decrypt(mp4_demuxer::MP4Sample* aSample,
-               DecryptionClient* aSink);
+  void Decrypt(MediaRawData* aSample, DecryptionClient* aSink);
 
   // Reject promise with DOMException corresponding to aExceptionCode.
   // Can be called from any thread.
   void RejectPromise(PromiseId aId, nsresult aExceptionCode);
 
   // Resolves promise with "undefined".
   // Can be called from any thread.
   void ResolvePromise(PromiseId aId);
@@ -231,24 +229,23 @@ private:
 
   // GMP thread only.
   void gmp_CloseSession(nsAutoPtr<SessionOpData> aData);
 
   // GMP thread only.
   void gmp_RemoveSession(nsAutoPtr<SessionOpData> aData);
 
   struct DecryptJob {
-    DecryptJob(mp4_demuxer::MP4Sample* aSample,
-               DecryptionClient* aClient)
+    DecryptJob(MediaRawData* aSample, DecryptionClient* aClient)
       : mId(0)
       , mSample(aSample)
       , mClient(aClient)
     {}
     uint32_t mId;
-    nsAutoPtr<mp4_demuxer::MP4Sample> mSample;
+    nsRefPtr<MediaRawData> mSample;
     nsAutoPtr<DecryptionClient> mClient;
   };
   // GMP thread only.
   void gmp_Decrypt(nsAutoPtr<DecryptJob> aJob);
 
   class RejectPromiseTask : public nsRunnable {
   public:
     RejectPromiseTask(CDMProxy* aProxy,
--- a/dom/media/fmp4/BlankDecoderModule.cpp
+++ b/dom/media/fmp4/BlankDecoderModule.cpp
@@ -36,39 +36,39 @@ public:
   }
 
   virtual nsresult Shutdown() override {
     return NS_OK;
   }
 
   class OutputEvent : public nsRunnable {
   public:
-    OutputEvent(mp4_demuxer::MP4Sample* aSample,
+    OutputEvent(MediaRawData* aSample,
                 MediaDataDecoderCallback* aCallback,
                 BlankMediaDataCreator* aCreator)
       : mSample(aSample)
       , mCreator(aCreator)
       , mCallback(aCallback)
     {
     }
     NS_IMETHOD Run() override
     {
-      nsRefPtr<MediaData> data = mCreator->Create(mSample->composition_timestamp,
-                                                  mSample->duration,
-                                                  mSample->byte_offset);
+      nsRefPtr<MediaData> data = mCreator->Create(mSample->mTime,
+                                                  mSample->mDuration,
+                                                  mSample->mOffset);
       mCallback->Output(data);
       return NS_OK;
     }
   private:
-    nsAutoPtr<mp4_demuxer::MP4Sample> mSample;
+    nsRefPtr<MediaRawData> mSample;
     BlankMediaDataCreator* mCreator;
     MediaDataDecoderCallback* mCallback;
   };
 
-  virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) override
+  virtual nsresult Input(MediaRawData* aSample) override
   {
     // The MediaDataDecoder must delete the sample when we're finished
     // with it, so the OutputEvent stores it in an nsAutoPtr and deletes
     // it once it's run.
     RefPtr<nsIRunnable> r(new OutputEvent(aSample, mCallback, mCreator));
     mTaskQueue->Dispatch(r);
     return NS_OK;
   }
--- a/dom/media/fmp4/MP4Reader.cpp
+++ b/dom/media/fmp4/MP4Reader.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 "MP4Reader.h"
 #include "MP4Stream.h"
+#include "MediaData.h"
 #include "MediaResource.h"
 #include "nsPrintfCString.h"
 #include "nsSize.h"
 #include "VideoUtils.h"
 #include "mozilla/dom/HTMLMediaElement.h"
 #include "ImageContainer.h"
 #include "Layers.h"
 #include "SharedThreadPool.h"
@@ -65,17 +66,17 @@ TrackTypeToStr(TrackType aTrack)
     return "Video";
   default:
     return "Unknown";
   }
 }
 #endif
 
 bool
-AccumulateSPSTelemetry(const ByteBuffer* aExtradata)
+AccumulateSPSTelemetry(const DataBuffer* aExtradata)
 {
   SPSData spsdata;
   if (H264::DecodeSPSFromExtraData(aExtradata, spsdata)) {
    uint8_t constraints = (spsdata.constraint_set0_flag ? (1 << 0) : 0) |
                          (spsdata.constraint_set1_flag ? (1 << 1) : 0) |
                          (spsdata.constraint_set2_flag ? (1 << 2) : 0) |
                          (spsdata.constraint_set3_flag ? (1 << 3) : 0) |
                          (spsdata.constraint_set4_flag ? (1 << 4) : 0) |
@@ -773,26 +774,26 @@ MP4Reader::Update(TrackType aTrack)
   VLOG("Update(%s) ni=%d no=%d iex=%d fl=%d",
        TrackTypeToStr(aTrack),
        needInput,
        needOutput,
        decoder.mInputExhausted,
        decoder.mIsFlushing);
 
   if (needInput) {
-    nsAutoPtr<MediaSample> sample(PopSample(aTrack));
+    nsRefPtr<MediaRawData> sample(PopSample(aTrack));
 
     // Collect telemetry from h264 Annex B SPS.
-    if (!mFoundSPSForTelemetry && sample && AnnexB::HasSPS(sample->mMp4Sample)) {
-      nsRefPtr<ByteBuffer> extradata = AnnexB::ExtractExtraData(sample->mMp4Sample);
+    if (!mFoundSPSForTelemetry && sample && AnnexB::HasSPS(sample)) {
+      nsRefPtr<DataBuffer> extradata = AnnexB::ExtractExtraData(sample);
       mFoundSPSForTelemetry = AccumulateSPSTelemetry(extradata);
     }
 
     if (sample) {
-      decoder.mDecoder->Input(sample->mMp4Sample.forget());
+      decoder.mDecoder->Input(sample);
       if (aTrack == kVideo) {
         a.mParsed++;
       }
     } else {
       {
         MonitorAutoLock lock(decoder.mMonitor);
         MOZ_ASSERT(!decoder.mDemuxEOS);
         decoder.mDemuxEOS = true;
@@ -826,41 +827,59 @@ MP4Reader::ReturnOutput(MediaData* aData
     }
 
     mAudio.mPromise.Resolve(audioData, __func__);
   } else if (aTrack == kVideo) {
     mVideo.mPromise.Resolve(static_cast<VideoData*>(aData), __func__);
   }
 }
 
-MediaSample*
+already_AddRefed<MediaRawData>
 MP4Reader::PopSample(TrackType aTrack)
 {
   MonitorAutoLock mon(mDemuxerMonitor);
   return PopSampleLocked(aTrack);
 }
 
-MediaSample*
+already_AddRefed<MediaRawData>
 MP4Reader::PopSampleLocked(TrackType aTrack)
 {
   mDemuxerMonitor.AssertCurrentThreadOwns();
+  nsRefPtr<MediaRawData> sample;
   switch (aTrack) {
     case kAudio:
-      return InvokeAndRetry(mAudio.mTrackDemuxer.get(), &TrackDemuxer::DemuxSample, mStream, &mDemuxerMonitor);
+      sample =
+        InvokeAndRetry(this, &MP4Reader::DemuxAudioSample, mStream, &mDemuxerMonitor);
+      return sample.forget();
     case kVideo:
       if (mQueuedVideoSample) {
         return mQueuedVideoSample.forget();
       }
-      return InvokeAndRetry(mVideo.mTrackDemuxer.get(), &TrackDemuxer::DemuxSample, mStream, &mDemuxerMonitor);
-
+      sample =
+        InvokeAndRetry(this, &MP4Reader::DemuxVideoSample, mStream, &mDemuxerMonitor);
+      return sample.forget();
     default:
       return nullptr;
   }
 }
 
+nsRefPtr<MediaRawData>
+MP4Reader::DemuxAudioSample()
+{
+  nsRefPtr<MediaRawData> sample = mAudio.mTrackDemuxer->DemuxSample();
+  return sample;
+}
+
+nsRefPtr<MediaRawData>
+MP4Reader::DemuxVideoSample()
+{
+  nsRefPtr<MediaRawData> sample = mVideo.mTrackDemuxer->DemuxSample();
+  return sample;
+}
+
 size_t
 MP4Reader::SizeOfVideoQueueInFrames()
 {
   return SizeOfQueue(kVideo);
 }
 
 size_t
 MP4Reader::SizeOfAudioQueueInFrames()
@@ -1002,26 +1021,26 @@ MP4Reader::SkipVideoDemuxToNextKeyFrame(
   MOZ_ASSERT(GetTaskQueue()->IsCurrentThreadIn());
 
   MOZ_ASSERT(mVideo.mDecoder);
 
   Flush(kVideo);
 
   // Loop until we reach the next keyframe after the threshold.
   while (true) {
-    nsAutoPtr<MediaSample> compressed(PopSample(kVideo));
+    nsRefPtr<MediaRawData> compressed(PopSample(kVideo));
     if (!compressed) {
       // EOS, or error. This code assumes EOS, which may or may not be right.
       MonitorAutoLock mon(mVideo.mMonitor);
       mVideo.mDemuxEOS = true;
       return false;
     }
     parsed++;
-    if (!compressed->mMp4Sample->is_sync_point ||
-        compressed->mMp4Sample->composition_timestamp < aTimeThreshold) {
+    if (!compressed->mKeyframe ||
+        compressed->mTime < aTimeThreshold) {
       continue;
     }
     mQueuedVideoSample = compressed;
     break;
   }
 
   return true;
 }
@@ -1038,17 +1057,17 @@ MP4Reader::Seek(int64_t aTime, int64_t a
   }
 
   int64_t seekTime = aTime;
   mQueuedVideoSample = nullptr;
   if (mDemuxer->HasValidVideo()) {
     mVideo.mTrackDemuxer->Seek(seekTime);
     mQueuedVideoSample = PopSampleLocked(kVideo);
     if (mQueuedVideoSample) {
-      seekTime = mQueuedVideoSample->mMp4Sample->composition_timestamp;
+      seekTime = mQueuedVideoSample->mTime;
     }
   }
   if (mDemuxer->HasValidAudio()) {
     mAudio.mTrackDemuxer->Seek(seekTime);
   }
   LOG("aTime=%lld exit", aTime);
   return SeekPromise::CreateAndResolve(seekTime, __func__);
 }
--- a/dom/media/fmp4/MP4Reader.h
+++ b/dom/media/fmp4/MP4Reader.h
@@ -18,17 +18,17 @@
 #include "mozilla/Monitor.h"
 
 namespace mozilla {
 
 namespace dom {
 class TimeRanges;
 }
 
-typedef std::deque<MediaSample*> MediaSampleQueue;
+typedef std::deque<nsRefPtr<MediaRawData>> MediaSampleQueue;
 
 class MP4Stream;
 
 #if defined(MOZ_GONK_MEDIACODEC) || defined(XP_WIN) || defined(MOZ_APPLEMEDIA) || defined(MOZ_FFMPEG)
 #define MP4_READER_DORMANT
 #else
 #undef MP4_READER_DORMANT
 #endif
@@ -116,18 +116,18 @@ private:
 
   void ExtractCryptoInitData(nsTArray<uint8_t>& aInitData);
 
   // Initializes mLayersBackendType if possible.
   void InitLayersBackendType();
 
   // Blocks until the demuxer produces an sample of specified type.
   // Returns nullptr on error on EOS. Caller must delete sample.
-  MediaSample* PopSample(mp4_demuxer::TrackType aTrack);
-  MediaSample* PopSampleLocked(mp4_demuxer::TrackType aTrack);
+  already_AddRefed<MediaRawData> PopSample(mp4_demuxer::TrackType aTrack);
+  already_AddRefed<MediaRawData> PopSampleLocked(mp4_demuxer::TrackType aTrack);
 
   bool SkipVideoDemuxToNextKeyFrame(int64_t aTimeThreshold, uint32_t& parsed);
 
   // DecoderCallback proxies the MediaDataDecoderCallback calls to these
   // functions.
   void Output(mp4_demuxer::TrackType aType, MediaData* aSample);
   void InputExhausted(mp4_demuxer::TrackType aTrack);
   void Error(mp4_demuxer::TrackType aTrack);
@@ -255,32 +255,36 @@ private:
     }
   };
 
   DecoderDataWithPromise<AudioDataPromise> mAudio;
   DecoderDataWithPromise<VideoDataPromise> mVideo;
 
   // Queued samples extracted by the demuxer, but not yet sent to the platform
   // decoder.
-  nsAutoPtr<MediaSample> mQueuedVideoSample;
+  nsRefPtr<MediaRawData> mQueuedVideoSample;
 
   // Returns true when the decoder for this track needs input.
   // aDecoder.mMonitor must be locked.
   bool NeedInput(DecoderData& aDecoder);
 
   // The last number of decoded output frames that we've reported to
   // MediaDecoder::NotifyDecoded(). We diff the number of output video
   // frames every time that DecodeVideoData() is called, and report the
   // delta there.
   uint64_t mLastReportedNumDecodedFrames;
 
   DecoderData& GetDecoderData(mp4_demuxer::TrackType aTrack);
 
   layers::LayersBackend mLayersBackendType;
 
+  // For use with InvokeAndRetry as an already_refed can't be converted to bool
+  nsRefPtr<MediaRawData> DemuxVideoSample();
+  nsRefPtr<MediaRawData> DemuxAudioSample();
+
   // True if we've read the streams' metadata.
   bool mDemuxerInitialized;
 
   // True if we've gathered telemetry from an SPS.
   bool mFoundSPSForTelemetry;
 
   // Synchronized by decoder monitor.
   bool mIsEncrypted;
--- a/dom/media/fmp4/PlatformDecoderModule.h
+++ b/dom/media/fmp4/PlatformDecoderModule.h
@@ -12,22 +12,23 @@
 #include "nsTArray.h"
 #include "mozilla/RefPtr.h"
 #include <queue>
 
 namespace mp4_demuxer {
 class TrackConfig;
 class VideoDecoderConfig;
 class AudioDecoderConfig;
-class MP4Sample;
 }
 
 class nsIThreadPool;
 
 namespace mozilla {
+class MediaRawData;
+class DataBuffer;
 
 namespace layers {
 class ImageContainer;
 }
 
 class MediaDataDecoder;
 class MediaDataDecoderCallback;
 class MediaInputQueue;
@@ -214,20 +215,18 @@ public:
   // this returns. The decoder should do any initialization here, rather
   // than in its constructor or PlatformDecoderModule::Create*Decoder(),
   // so that if the MP4Reader needs to shutdown during initialization,
   // it can call Shutdown() to cancel this operation. Any initialization
   // that requires blocking the calling thread in this function *must*
   // be done here so that it can be canceled by calling Shutdown()!
   virtual nsresult Init() = 0;
 
-  // Inserts a sample into the decoder's decode pipeline. The decoder must
-  // delete the sample once its been decoded. If Input() returns an error,
-  // aSample will be deleted by the caller.
-  virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) = 0;
+  // Inserts a sample into the decoder's decode pipeline.
+  virtual nsresult Input(MediaRawData* aSample) = 0;
 
   // Causes all samples in the decoding pipeline to be discarded. When
   // this function returns, the decoder must be ready to accept new input
   // for decoding. This function is called when the demuxer seeks, before
   // decoding resumes after the seek.
   // While the reader calls Flush(), it ignores all output sent to it;
   // it is safe (but pointless) to send output while Flush is called.
   // The MP4Reader will not call Input() while it's calling Flush().
--- a/dom/media/fmp4/SharedDecoderManager.cpp
+++ b/dom/media/fmp4/SharedDecoderManager.cpp
@@ -213,17 +213,17 @@ SharedDecoderProxy::~SharedDecoderProxy(
 
 nsresult
 SharedDecoderProxy::Init()
 {
   return NS_OK;
 }
 
 nsresult
-SharedDecoderProxy::Input(mp4_demuxer::MP4Sample* aSample)
+SharedDecoderProxy::Input(MediaRawData* aSample)
 {
   if (mManager->mActiveProxy != this) {
     mManager->Select(this);
   }
   return mManager->mDecoder->Input(aSample);
 }
 
 nsresult
--- a/dom/media/fmp4/SharedDecoderManager.h
+++ b/dom/media/fmp4/SharedDecoderManager.h
@@ -64,17 +64,17 @@ private:
 class SharedDecoderProxy : public MediaDataDecoder
 {
 public:
   SharedDecoderProxy(SharedDecoderManager* aManager,
                      MediaDataDecoderCallback* aCallback);
   virtual ~SharedDecoderProxy();
 
   virtual nsresult Init() override;
-  virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) override;
+  virtual nsresult Input(MediaRawData* aSample) override;
   virtual nsresult Flush() override;
   virtual nsresult Drain() override;
   virtual nsresult Shutdown() override;
   virtual bool IsWaitingMediaResources() override;
   virtual bool IsDormantNeeded() override;
   virtual void ReleaseMediaResources() override;
   virtual bool IsHardwareAccelerated() const override;
 
--- a/dom/media/fmp4/android/AndroidDecoderModule.cpp
+++ b/dom/media/fmp4/android/AndroidDecoderModule.cpp
@@ -57,17 +57,17 @@ public:
 
     return InitDecoder(mSurfaceTexture->JavaSurface());
   }
 
   void Cleanup() override {
     mGLContext = nullptr;
   }
 
-  virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) override {
+  virtual nsresult Input(MediaRawData* aSample) override {
     return MediaCodecDataDecoder::Input(aSample);
   }
 
   bool WantCopy() {
     // Allocating a texture is incredibly slow on PowerVR
     return mGLContext->Vendor() != GLVendor::Imagination;
   }
 
@@ -392,17 +392,17 @@ nsresult MediaCodecDataDecoder::GetInput
 void MediaCodecDataDecoder::DecoderLoop()
 {
   bool outputDone = false;
 
   bool draining = false;
   bool waitingEOF = false;
 
   AutoLocalJNIFrame frame(GetJNIForThread(), 1);
-  mp4_demuxer::MP4Sample* sample = nullptr;
+  nsRefPtr<MediaRawData> sample;
 
   MediaFormat::LocalRef outputFormat(frame.GetEnv());
   nsresult res;
 
   for (;;) {
     {
       MonitorAutoLock lock(mMonitor);
       while (!mStopping && !mDraining && !mFlushing && mQueue.empty()) {
@@ -459,33 +459,32 @@ void MediaCodecDataDecoder::DecoderLoop(
 
       if (inputIndex >= 0) {
         jni::Object::LocalRef buffer(frame.GetEnv());
         res = GetInputBuffer(frame.GetEnv(), inputIndex, &buffer);
         HANDLE_DECODER_ERROR();
 
         void* directBuffer = frame.GetEnv()->GetDirectBufferAddress(buffer.Get());
 
-        MOZ_ASSERT(frame.GetEnv()->GetDirectBufferCapacity(buffer.Get()) >= sample->size,
+        MOZ_ASSERT(frame.GetEnv()->GetDirectBufferCapacity(buffer.Get()) >= sample->mSize,
           "Decoder buffer is not large enough for sample");
 
         {
           // We're feeding this to the decoder, so remove it from the queue
           MonitorAutoLock lock(mMonitor);
           mQueue.pop();
         }
 
-        PodCopy((uint8_t*)directBuffer, sample->data, sample->size);
+        PodCopy((uint8_t*)directBuffer, sample->mData, sample->mSize);
 
-        res = mDecoder->QueueInputBuffer(inputIndex, 0, sample->size,
-                                         sample->composition_timestamp, 0);
+        res = mDecoder->QueueInputBuffer(inputIndex, 0, sample->mSize,
+                                         sample->mTime, 0);
         HANDLE_DECODER_ERROR();
 
-        mDurations.push(sample->duration);
-        delete sample;
+        mDurations.push(sample->mDuration);
         sample = nullptr;
         outputDone = false;
       }
     }
 
     if (!outputDone) {
       BufferInfo::LocalRef bufferInfo;
       res = BufferInfo::New(&bufferInfo);
@@ -567,25 +566,24 @@ void MediaCodecDataDecoder::DecoderLoop(
   mStopping = false;
   mMonitor.Notify();
 }
 
 void MediaCodecDataDecoder::ClearQueue()
 {
   mMonitor.AssertCurrentThreadOwns();
   while (!mQueue.empty()) {
-    delete mQueue.front();
     mQueue.pop();
   }
   while (!mDurations.empty()) {
     mDurations.pop();
   }
 }
 
-nsresult MediaCodecDataDecoder::Input(mp4_demuxer::MP4Sample* aSample) {
+nsresult MediaCodecDataDecoder::Input(MediaRawData* aSample) {
   MonitorAutoLock lock(mMonitor);
   mQueue.push(aSample);
   lock.NotifyAll();
 
   return NS_OK;
 }
 
 nsresult MediaCodecDataDecoder::ResetInputBuffers()
--- a/dom/media/fmp4/android/AndroidDecoderModule.h
+++ b/dom/media/fmp4/android/AndroidDecoderModule.h
@@ -10,17 +10,17 @@
 
 #include "MediaCodec.h"
 #include "mozilla/Monitor.h"
 
 #include <queue>
 
 namespace mozilla {
 
-typedef std::queue<mp4_demuxer::MP4Sample*> SampleQueue;
+typedef std::queue<nsRefPtr<MediaRawData>> SampleQueue;
 
 class AndroidDecoderModule : public PlatformDecoderModule {
 public:
   virtual already_AddRefed<MediaDataDecoder>
   CreateVideoDecoder(const mp4_demuxer::VideoDecoderConfig& aConfig,
                      layers::LayersBackend aLayersBackend,
                      layers::ImageContainer* aImageContainer,
                      FlushableMediaTaskQueue* aVideoTaskQueue,
@@ -50,17 +50,17 @@ public:
                         MediaDataDecoderCallback* aCallback);
 
   virtual ~MediaCodecDataDecoder();
 
   virtual nsresult Init() override;
   virtual nsresult Flush() override;
   virtual nsresult Drain() override;
   virtual nsresult Shutdown() override;
-  virtual nsresult Input(mp4_demuxer::MP4Sample* aSample);
+  virtual nsresult Input(MediaRawData* aSample);
 
 protected:
   friend class AndroidDecoderModule;
 
   MediaData::Type mType;
 
   nsAutoCString mMimeType;
   widget::sdk::MediaFormat::GlobalRef mFormat;
--- a/dom/media/fmp4/apple/AppleATDecoder.cpp
+++ b/dom/media/fmp4/apple/AppleATDecoder.cpp
@@ -61,31 +61,31 @@ AppleATDecoder::Init()
   if (!mFormatID) {
     NS_ERROR("Non recognised format");
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
 
 nsresult
-AppleATDecoder::Input(mp4_demuxer::MP4Sample* aSample)
+AppleATDecoder::Input(MediaRawData* aSample)
 {
   LOG("mp4 input sample %p %lld us %lld pts%s %llu bytes audio",
       aSample,
-      aSample->duration,
-      aSample->composition_timestamp,
-      aSample->is_sync_point ? " keyframe" : "",
-      (unsigned long long)aSample->size);
+      aSample->mDuration,
+      aSample->mTime,
+      aSample->mKeyframe ? " keyframe" : "",
+      (unsigned long long)aSample->mSize);
 
   // Queue a task to perform the actual decoding on a separate thread.
   mTaskQueue->Dispatch(
-      NS_NewRunnableMethodWithArg<nsAutoPtr<mp4_demuxer::MP4Sample>>(
+      NS_NewRunnableMethodWithArg<nsRefPtr<MediaRawData>>(
         this,
         &AppleATDecoder::SubmitSample,
-        nsAutoPtr<mp4_demuxer::MP4Sample>(aSample)));
+        nsRefPtr<MediaRawData>(aSample)));
 
   return NS_OK;
 }
 
 nsresult
 AppleATDecoder::Flush()
 {
   LOG("Flushing AudioToolbox AAC decoder");
@@ -168,17 +168,17 @@ static OSStatus
 
   // No more data to provide following this run.
   userData->mDataSize = 0;
 
   return noErr;
 }
 
 void
-AppleATDecoder::SubmitSample(nsAutoPtr<mp4_demuxer::MP4Sample> aSample)
+AppleATDecoder::SubmitSample(MediaRawData* aSample)
 {
   nsresult rv = NS_OK;
   if (!mConverter) {
     rv = SetupDecoder(aSample);
     if (rv != NS_OK && rv != NS_ERROR_NOT_INITIALIZED) {
       mCallback->Error();
       return;
     }
@@ -198,34 +198,34 @@ AppleATDecoder::SubmitSample(nsAutoPtr<m
   }
 
   if (mTaskQueue->IsEmpty()) {
     mCallback->InputExhausted();
   }
 }
 
 nsresult
-AppleATDecoder::DecodeSample(mp4_demuxer::MP4Sample* aSample)
+AppleATDecoder::DecodeSample(MediaRawData* aSample)
 {
   // Array containing the queued decoded audio frames, about to be output.
   nsTArray<AudioDataValue> outputData;
   UInt32 channels = mOutputFormat.mChannelsPerFrame;
   // Pick a multiple of the frame size close to a power of two
   // for efficient allocation.
   const uint32_t MAX_AUDIO_FRAMES = 128;
   const uint32_t maxDecodedSamples = MAX_AUDIO_FRAMES * channels;
 
   // Descriptions for _decompressed_ audio packets. ignored.
   nsAutoArrayPtr<AudioStreamPacketDescription>
     packets(new AudioStreamPacketDescription[MAX_AUDIO_FRAMES]);
 
   // This API insists on having packets spoon-fed to it from a callback.
   // This structure exists only to pass our state.
   PassthroughUserData userData =
-    { channels, (UInt32)aSample->size, aSample->data };
+    { channels, (UInt32)aSample->mSize, aSample->mData };
 
   // Decompressed audio buffer
   nsAutoArrayPtr<AudioDataValue> decoded(new AudioDataValue[maxDecodedSamples]);
 
   do {
     AudioBufferList decBuffer;
     decBuffer.mNumberBuffers = 1;
     decBuffer.mBuffers[0].mNumberChannels = channels;
@@ -267,24 +267,24 @@ AppleATDecoder::DecodeSample(mp4_demuxer
   CheckedInt<Microseconds> duration = FramesToUsecs(numFrames, rate);
   if (!duration.isValid()) {
     NS_WARNING("Invalid count of accumulated audio samples");
     return NS_ERROR_FAILURE;
   }
 
 #ifdef LOG_SAMPLE_DECODE
   LOG("pushed audio at time %lfs; duration %lfs\n",
-      (double)aSample->composition_timestamp / USECS_PER_S,
+      (double)aSample->mTime / USECS_PER_S,
       (double)duration.value() / USECS_PER_S);
 #endif
 
   nsAutoArrayPtr<AudioDataValue> data(new AudioDataValue[outputData.Length()]);
   PodCopy(data.get(), &outputData[0], outputData.Length());
-  nsRefPtr<AudioData> audio = new AudioData(aSample->byte_offset,
-                                            aSample->composition_timestamp,
+  nsRefPtr<AudioData> audio = new AudioData(aSample->mOffset,
+                                            aSample->mTime,
                                             duration.value(),
                                             numFrames,
                                             data.forget(),
                                             channels,
                                             rate);
   mCallback->Output(audio);
   return NS_OK;
 }
@@ -359,17 +359,17 @@ AppleATDecoder::GetInputAudioDescription
   }
 
   aDesc = formatList[itemIndex].mASBD;
 
   return NS_OK;
 }
 
 nsresult
-AppleATDecoder::SetupDecoder(mp4_demuxer::MP4Sample* aSample)
+AppleATDecoder::SetupDecoder(MediaRawData* aSample)
 {
   if (mFormatID == kAudioFormatMPEG4AAC &&
       mConfig.extended_profile == 2) {
     // Check for implicit SBR signalling if stream is AAC-LC
     // This will provide us with an updated magic cookie for use with
     // GetInputAudioDescription.
     if (NS_SUCCEEDED(GetImplicitAACMagicCookie(aSample)) &&
         !mMagicCookie.Length()) {
@@ -456,20 +456,20 @@ static void
                 UInt32 aNumBytes,
                 UInt32 aNumPackets,
                 const void* aData,
                 AudioStreamPacketDescription* aPackets)
 {
 }
 
 nsresult
-AppleATDecoder::GetImplicitAACMagicCookie(const mp4_demuxer::MP4Sample* aSample)
+AppleATDecoder::GetImplicitAACMagicCookie(const MediaRawData* aSample)
 {
   // Prepend ADTS header to AAC audio.
-  nsAutoPtr<mp4_demuxer::MP4Sample> adtssample(aSample->Clone());
+  nsRefPtr<MediaRawData> adtssample(aSample->Clone());
   if (!adtssample) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   bool rv = mp4_demuxer::Adts::ConvertSample(mConfig.channel_count,
                                              mConfig.frequency_index,
                                              mConfig.aac_profile,
                                              adtssample);
@@ -485,18 +485,18 @@ AppleATDecoder::GetImplicitAACMagicCooki
                                       &mStream);
     if (rv) {
       NS_WARNING("Couldn't open AudioFileStream");
       return NS_ERROR_FAILURE;
     }
   }
 
   OSStatus status = AudioFileStreamParseBytes(mStream,
-                                              adtssample->size,
-                                              adtssample->data,
+                                              adtssample->mSize,
+                                              adtssample->mData,
                                               0 /* discontinuity */);
   if (status) {
     NS_WARNING("Couldn't parse sample");
   }
 
   if (status || mFileStreamError || mMagicCookie.Length()) {
     // We have decoded a magic cookie or an error occurred as such
     // we won't need the stream any longer.
--- a/dom/media/fmp4/apple/AppleATDecoder.h
+++ b/dom/media/fmp4/apple/AppleATDecoder.h
@@ -21,17 +21,17 @@ class MediaDataDecoderCallback;
 class AppleATDecoder : public MediaDataDecoder {
 public:
   AppleATDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
                  FlushableMediaTaskQueue* aVideoTaskQueue,
                  MediaDataDecoderCallback* aCallback);
   virtual ~AppleATDecoder();
 
   virtual nsresult Init() override;
-  virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) override;
+  virtual nsresult Input(MediaRawData* aSample) override;
   virtual nsresult Flush() override;
   virtual nsresult Drain() override;
   virtual nsresult Shutdown() override;
 
   // Callbacks also need access to the config.
   const mp4_demuxer::AudioDecoderConfig& mConfig;
 
   // Use to extract magic cookie for HE-AAC detection.
@@ -42,23 +42,23 @@ public:
 
 private:
   nsRefPtr<FlushableMediaTaskQueue> mTaskQueue;
   MediaDataDecoderCallback* mCallback;
   AudioConverterRef mConverter;
   AudioStreamBasicDescription mOutputFormat;
   UInt32 mFormatID;
   AudioFileStreamID mStream;
-  nsTArray<nsAutoPtr<mp4_demuxer::MP4Sample>> mQueuedSamples;
+  nsTArray<nsRefPtr<MediaRawData>> mQueuedSamples;
 
-  void SubmitSample(nsAutoPtr<mp4_demuxer::MP4Sample> aSample);
-  nsresult DecodeSample(mp4_demuxer::MP4Sample* aSample);
+  void SubmitSample(MediaRawData* aSample);
+  nsresult DecodeSample(MediaRawData* aSample);
   nsresult GetInputAudioDescription(AudioStreamBasicDescription& aDesc,
                                     const nsTArray<uint8_t>& aExtraData);
   // Setup AudioConverter once all information required has been gathered.
   // Will return NS_ERROR_NOT_INITIALIZED if more data is required.
-  nsresult SetupDecoder(mp4_demuxer::MP4Sample* aSample);
-  nsresult GetImplicitAACMagicCookie(const mp4_demuxer::MP4Sample* aSample);
+  nsresult SetupDecoder(MediaRawData* aSample);
+  nsresult GetImplicitAACMagicCookie(const MediaRawData* aSample);
 };
 
 } // namespace mozilla
 
 #endif // mozilla_AppleATDecoder_h
--- a/dom/media/fmp4/apple/AppleVDADecoder.cpp
+++ b/dom/media/fmp4/apple/AppleVDADecoder.cpp
@@ -99,30 +99,30 @@ AppleVDADecoder::Shutdown()
     LOG("%s: cleaning up decoder %p", __func__, mDecoder);
     VDADecoderDestroy(mDecoder);
     mDecoder = nullptr;
   }
   return NS_OK;
 }
 
 nsresult
-AppleVDADecoder::Input(mp4_demuxer::MP4Sample* aSample)
+AppleVDADecoder::Input(MediaRawData* aSample)
 {
   LOG("mp4 input sample %p pts %lld duration %lld us%s %d bytes",
       aSample,
-      aSample->composition_timestamp,
-      aSample->duration,
-      aSample->is_sync_point ? " keyframe" : "",
-      aSample->size);
+      aSample->mTime,
+      aSample->mDuration,
+      aSample->mKeyframe ? " keyframe" : "",
+      aSample->mSize);
 
   mTaskQueue->Dispatch(
-      NS_NewRunnableMethodWithArg<nsAutoPtr<mp4_demuxer::MP4Sample>>(
+      NS_NewRunnableMethodWithArg<nsRefPtr<MediaRawData>>(
           this,
           &AppleVDADecoder::SubmitFrame,
-          nsAutoPtr<mp4_demuxer::MP4Sample>(aSample)));
+          nsRefPtr<MediaRawData>(aSample)));
   return NS_OK;
 }
 
 nsresult
 AppleVDADecoder::Flush()
 {
   mTaskQueue->Flush();
   OSStatus rv = VDADecoderFlush(mDecoder, 0 /*dont emit*/);
@@ -219,17 +219,17 @@ PlatformCallback(void* decompressionOutp
     is_sync_point == 1));
 
   // Forward the data back to an object method which can access
   // the correct MP4Reader callback.
   decoder->OutputFrame(image, frameRef);
 }
 
 AppleVDADecoder::AppleFrameRef*
-AppleVDADecoder::CreateAppleFrameRef(const mp4_demuxer::MP4Sample* aSample)
+AppleVDADecoder::CreateAppleFrameRef(const MediaRawData* aSample)
 {
   MOZ_ASSERT(aSample);
   return new AppleFrameRef(*aSample);
 }
 
 void
 AppleVDADecoder::DrainReorderedFrames()
 {
@@ -302,42 +302,42 @@ AppleVDADecoder::OutputFrame(CVPixelBuff
   }
   LOG("%llu decoded frames queued",
       static_cast<unsigned long long>(mReorderQueue.Length()));
 
   return NS_OK;
 }
 
 nsresult
-AppleVDADecoder::SubmitFrame(mp4_demuxer::MP4Sample* aSample)
+AppleVDADecoder::SubmitFrame(MediaRawData* aSample)
 {
   AutoCFRelease<CFDataRef> block =
-    CFDataCreate(kCFAllocatorDefault, aSample->data, aSample->size);
+    CFDataCreate(kCFAllocatorDefault, aSample->mData, aSample->mSize);
   if (!block) {
     NS_ERROR("Couldn't create CFData");
     return NS_ERROR_FAILURE;
   }
 
   AutoCFRelease<CFNumberRef> pts =
     CFNumberCreate(kCFAllocatorDefault,
                    kCFNumberSInt64Type,
-                   &aSample->composition_timestamp);
+                   &aSample->mTime);
   AutoCFRelease<CFNumberRef> dts =
     CFNumberCreate(kCFAllocatorDefault,
                    kCFNumberSInt64Type,
-                   &aSample->decode_timestamp);
+                   &aSample->mTimecode);
   AutoCFRelease<CFNumberRef> duration =
     CFNumberCreate(kCFAllocatorDefault,
                    kCFNumberSInt64Type,
-                   &aSample->duration);
+                   &aSample->mDuration);
   AutoCFRelease<CFNumberRef> byte_offset =
     CFNumberCreate(kCFAllocatorDefault,
                    kCFNumberSInt64Type,
-                   &aSample->byte_offset);
-  char keyframe = aSample->is_sync_point ? 1 : 0;
+                   &aSample->mOffset);
+  char keyframe = aSample->mKeyframe ? 1 : 0;
   AutoCFRelease<CFNumberRef> cfkeyframe =
     CFNumberCreate(kCFAllocatorDefault,
                    kCFNumberSInt8Type,
                    &keyframe);
 
   const void* keys[] = { CFSTR("FRAME_PTS"),
                          CFSTR("FRAME_DTS"),
                          CFSTR("FRAME_DURATION"),
--- a/dom/media/fmp4/apple/AppleVDADecoder.h
+++ b/dom/media/fmp4/apple/AppleVDADecoder.h
@@ -29,35 +29,35 @@ public:
   class AppleFrameRef {
   public:
     Microseconds decode_timestamp;
     Microseconds composition_timestamp;
     Microseconds duration;
     int64_t byte_offset;
     bool is_sync_point;
 
-    explicit AppleFrameRef(const mp4_demuxer::MP4Sample& aSample)
-    : decode_timestamp(aSample.decode_timestamp)
-    , composition_timestamp(aSample.composition_timestamp)
-    , duration(aSample.duration)
-    , byte_offset(aSample.byte_offset)
-    , is_sync_point(aSample.is_sync_point)
+    explicit AppleFrameRef(const MediaRawData& aSample)
+      : decode_timestamp(aSample.mTimecode)
+      , composition_timestamp(aSample.mTime)
+      , duration(aSample.mDuration)
+      , byte_offset(aSample.mOffset)
+      , is_sync_point(aSample.mKeyframe)
     {
     }
 
     AppleFrameRef(Microseconds aDts,
                   Microseconds aPts,
                   Microseconds aDuration,
                   int64_t aByte_offset,
                   bool aIs_sync_point)
-    : decode_timestamp(aDts)
-    , composition_timestamp(aPts)
-    , duration(aDuration)
-    , byte_offset(aByte_offset)
-    , is_sync_point(aIs_sync_point)
+      : decode_timestamp(aDts)
+      , composition_timestamp(aPts)
+      , duration(aDuration)
+      , byte_offset(aByte_offset)
+      , is_sync_point(aIs_sync_point)
     {
     }
   };
 
   // Return a new created AppleVDADecoder or nullptr if media or hardware is
   // not supported by current configuration.
   static already_AddRefed<AppleVDADecoder> CreateVDADecoder(
     const mp4_demuxer::VideoDecoderConfig& aConfig,
@@ -66,51 +66,51 @@ public:
     layers::ImageContainer* aImageContainer);
 
   AppleVDADecoder(const mp4_demuxer::VideoDecoderConfig& aConfig,
                   FlushableMediaTaskQueue* aVideoTaskQueue,
                   MediaDataDecoderCallback* aCallback,
                   layers::ImageContainer* aImageContainer);
   virtual ~AppleVDADecoder();
   virtual nsresult Init() override;
-  virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) override;
+  virtual nsresult Input(MediaRawData* aSample) override;
   virtual nsresult Flush() override;
   virtual nsresult Drain() override;
   virtual nsresult Shutdown() override;
   virtual bool IsHardwareAccelerated() const override
   {
     return true;
   }
 
   nsresult OutputFrame(CVPixelBufferRef aImage,
                        nsAutoPtr<AppleFrameRef> aFrameRef);
 
  protected:
-  AppleFrameRef* CreateAppleFrameRef(const mp4_demuxer::MP4Sample* aSample);
+  AppleFrameRef* CreateAppleFrameRef(const MediaRawData* aSample);
   void DrainReorderedFrames();
   void ClearReorderedFrames();
   CFDictionaryRef CreateOutputConfiguration();
 
-  nsRefPtr<mp4_demuxer::ByteBuffer> mExtraData;
+  nsRefPtr<DataBuffer> mExtraData;
   nsRefPtr<FlushableMediaTaskQueue> mTaskQueue;
   MediaDataDecoderCallback* mCallback;
   nsRefPtr<layers::ImageContainer> mImageContainer;
   ReorderQueue mReorderQueue;
   uint32_t mPictureWidth;
   uint32_t mPictureHeight;
   uint32_t mDisplayWidth;
   uint32_t mDisplayHeight;
   uint32_t mMaxRefFrames;
 
 private:
   VDADecoder mDecoder;
   bool mIs106;
 
   // Method to pass a frame to VideoToolbox for decoding.
-  nsresult SubmitFrame(mp4_demuxer::MP4Sample* aSample);
+  nsresult SubmitFrame(MediaRawData* aSample);
   // Method to set up the decompression session.
   nsresult InitializeSession();
   CFDictionaryRef CreateDecoderSpecification();
 };
 
 } // namespace mozilla
 
 #endif // mozilla_AppleVDADecoder_h
--- a/dom/media/fmp4/apple/AppleVTDecoder.cpp
+++ b/dom/media/fmp4/apple/AppleVTDecoder.cpp
@@ -5,17 +5,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include <CoreFoundation/CFString.h>
 
 #include "AppleCMLinker.h"
 #include "AppleUtils.h"
 #include "AppleVTDecoder.h"
 #include "AppleVTLinker.h"
-#include "mp4_demuxer/DecoderData.h"
 #include "mp4_demuxer/H264.h"
 #include "MediaData.h"
 #include "MacIOSurfaceImage.h"
 #include "mozilla/ArrayUtils.h"
 #include "nsAutoPtr.h"
 #include "nsThreadUtils.h"
 #include "prlog.h"
 #include "VideoUtils.h"
@@ -77,42 +76,42 @@ AppleVTDecoder::Shutdown()
     LOG("%s: releasing format %p", __func__, mFormat);
     CFRelease(mFormat);
     mFormat = nullptr;
   }
   return NS_OK;
 }
 
 nsresult
-AppleVTDecoder::Input(mp4_demuxer::MP4Sample* aSample)
+AppleVTDecoder::Input(MediaRawData* aSample)
 {
   LOG("mp4 input sample %p pts %lld duration %lld us%s %d bytes",
       aSample,
-      aSample->composition_timestamp,
-      aSample->duration,
-      aSample->is_sync_point ? " keyframe" : "",
-      aSample->size);
+      aSample->mTime,
+      aSample->mDuration,
+      aSample->mKeyframe ? " keyframe" : "",
+      aSample->mSize);
 
 #ifdef LOG_MEDIA_SHA1
   SHA1Sum hash;
   hash.update(aSample->data, aSample->size);
   uint8_t digest_buf[SHA1Sum::kHashSize];
   hash.finish(digest_buf);
   nsAutoCString digest;
   for (size_t i = 0; i < sizeof(digest_buf); i++) {
     digest.AppendPrintf("%02x", digest_buf[i]);
   }
   LOG("    sha1 %s", digest.get());
 #endif // LOG_MEDIA_SHA1
 
   mTaskQueue->Dispatch(
-      NS_NewRunnableMethodWithArg<nsAutoPtr<mp4_demuxer::MP4Sample>>(
+      NS_NewRunnableMethodWithArg<nsRefPtr<MediaRawData>>(
           this,
           &AppleVTDecoder::SubmitFrame,
-          nsAutoPtr<mp4_demuxer::MP4Sample>(aSample)));
+          nsRefPtr<MediaRawData>(aSample)));
   return NS_OK;
 }
 
 nsresult
 AppleVTDecoder::Flush()
 {
   mTaskQueue->Flush();
   nsresult rv = WaitForAsynchronousFrames();
@@ -188,51 +187,51 @@ AppleVTDecoder::WaitForAsynchronousFrame
     LOG("AppleVTDecoder: Error %d waiting for asynchronous frames", rv);
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
 
 // Helper to fill in a timestamp structure.
 static CMSampleTimingInfo
-TimingInfoFromSample(mp4_demuxer::MP4Sample* aSample)
+TimingInfoFromSample(MediaRawData* aSample)
 {
   CMSampleTimingInfo timestamp;
 
-  timestamp.duration = CMTimeMake(aSample->duration, USECS_PER_S);
+  timestamp.duration = CMTimeMake(aSample->mDuration, USECS_PER_S);
   timestamp.presentationTimeStamp =
-    CMTimeMake(aSample->composition_timestamp, USECS_PER_S);
+    CMTimeMake(aSample->mTime, USECS_PER_S);
   timestamp.decodeTimeStamp =
-    CMTimeMake(aSample->decode_timestamp, USECS_PER_S);
+    CMTimeMake(aSample->mTimecode, USECS_PER_S);
 
   return timestamp;
 }
 
 nsresult
-AppleVTDecoder::SubmitFrame(mp4_demuxer::MP4Sample* aSample)
+AppleVTDecoder::SubmitFrame(MediaRawData* aSample)
 {
   // For some reason this gives me a double-free error with stagefright.
   AutoCFRelease<CMBlockBufferRef> block = nullptr;
   AutoCFRelease<CMSampleBufferRef> sample = nullptr;
   VTDecodeInfoFlags infoFlags;
   OSStatus rv;
 
   // FIXME: This copies the sample data. I think we can provide
   // a custom block source which reuses the aSample buffer.
   // But note that there may be a problem keeping the samples
   // alive over multiple frames.
-  rv = CMBlockBufferCreateWithMemoryBlock(kCFAllocatorDefault // Struct allocator.
-                                         ,aSample->data
-                                         ,aSample->size
-                                         ,kCFAllocatorNull // Block allocator.
-                                         ,NULL // Block source.
-                                         ,0    // Data offset.
-                                         ,aSample->size
-                                         ,false
-                                         ,block.receive());
+  rv = CMBlockBufferCreateWithMemoryBlock(kCFAllocatorDefault, // Struct allocator.
+                                          const_cast<uint8_t*>(aSample->mData),
+                                          aSample->mSize,
+                                          kCFAllocatorNull, // Block allocator.
+                                          NULL, // Block source.
+                                          0,    // Data offset.
+                                          aSample->mSize,
+                                          false,
+                                          block.receive());
   if (rv != noErr) {
     NS_ERROR("Couldn't create CMBlockBuffer");
     return NS_ERROR_FAILURE;
   }
   CMSampleTimingInfo timestamp = TimingInfoFromSample(aSample);
   rv = CMSampleBufferCreate(kCFAllocatorDefault, block, true, 0, 0, mFormat, 1, 1, &timestamp, 0, NULL, sample.receive());
   if (rv != noErr) {
     NS_ERROR("Couldn't create CMSampleBuffer");
--- a/dom/media/fmp4/apple/AppleVTDecoder.h
+++ b/dom/media/fmp4/apple/AppleVTDecoder.h
@@ -16,31 +16,31 @@ namespace mozilla {
 class AppleVTDecoder : public AppleVDADecoder {
 public:
   AppleVTDecoder(const mp4_demuxer::VideoDecoderConfig& aConfig,
                  FlushableMediaTaskQueue* aVideoTaskQueue,
                  MediaDataDecoderCallback* aCallback,
                  layers::ImageContainer* aImageContainer);
   virtual ~AppleVTDecoder();
   virtual nsresult Init() override;
-  virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) override;
+  virtual nsresult Input(MediaRawData* aSample) override;
   virtual nsresult Flush() override;
   virtual nsresult Drain() override;
   virtual nsresult Shutdown() override;
   virtual bool IsHardwareAccelerated() const override
   {
     return mIsHardwareAccelerated;
   }
 
 private:
   CMVideoFormatDescriptionRef mFormat;
   VTDecompressionSessionRef mSession;
 
   // Method to pass a frame to VideoToolbox for decoding.
-  nsresult SubmitFrame(mp4_demuxer::MP4Sample* aSample);
+  nsresult SubmitFrame(MediaRawData* aSample);
   // Method to set up the decompression session.
   nsresult InitializeSession();
   nsresult WaitForAsynchronousFrames();
   CFDictionaryRef CreateDecoderSpecification();
   CFDictionaryRef CreateDecoderExtensions();
   bool mIsHardwareAccelerated;
 };
 
--- a/dom/media/fmp4/eme/EMEDecoderModule.cpp
+++ b/dom/media/fmp4/eme/EMEDecoderModule.cpp
@@ -7,21 +7,21 @@
 #include "EMEDecoderModule.h"
 #include "EMEAudioDecoder.h"
 #include "EMEVideoDecoder.h"
 #include "MediaDataDecoderProxy.h"
 #include "mozIGeckoMediaPluginService.h"
 #include "mozilla/CDMProxy.h"
 #include "mozilla/unused.h"
 #include "nsServiceManagerUtils.h"
+#include "mp4_demuxer/DecoderData.h"
 
 namespace mozilla {
 
 class EMEDecryptor : public MediaDataDecoder {
-  typedef mp4_demuxer::MP4Sample MP4Sample;
 
 public:
 
   EMEDecryptor(MediaDataDecoder* aDecoder,
                MediaDataDecoderCallback* aCallback,
                CDMProxy* aProxy)
     : mDecoder(aDecoder)
     , mCallback(aCallback)
@@ -44,70 +44,72 @@ public:
 
   class DeliverDecrypted : public DecryptionClient {
   public:
     DeliverDecrypted(EMEDecryptor* aDecryptor, FlushableMediaTaskQueue* aTaskQueue)
       : mDecryptor(aDecryptor)
       , mTaskQueue(aTaskQueue)
     {}
     virtual void Decrypted(GMPErr aResult,
-                           mp4_demuxer::MP4Sample* aSample) override {
+                           MediaRawData* aSample) override {
       if (aResult == GMPNoKeyErr) {
         RefPtr<nsIRunnable> task;
-        task = NS_NewRunnableMethodWithArg<MP4Sample*>(mDecryptor,
-                                                       &EMEDecryptor::Input,
-                                                       aSample);
+        task =  NS_NewRunnableMethodWithArg<nsRefPtr<MediaRawData>>(
+          mDecryptor,
+          &EMEDecryptor::Input,
+          nsRefPtr<MediaRawData>(aSample));
         mTaskQueue->Dispatch(task.forget());
       } else if (GMP_FAILED(aResult)) {
         if (mDecryptor->mCallback) {
           mDecryptor->mCallback->Error();
         }
         MOZ_ASSERT(!aSample);
       } else {
         RefPtr<nsIRunnable> task;
-        task = NS_NewRunnableMethodWithArg<MP4Sample*>(mDecryptor,
-                                                       &EMEDecryptor::Decrypted,
-                                                       aSample);
+        task = NS_NewRunnableMethodWithArg<nsRefPtr<MediaRawData>>(
+          mDecryptor,
+          &EMEDecryptor::Decrypted,
+          nsRefPtr<MediaRawData>(aSample));
         mTaskQueue->Dispatch(task.forget());
       }
       mTaskQueue = nullptr;
       mDecryptor = nullptr;
     }
   private:
     nsRefPtr<EMEDecryptor> mDecryptor;
     nsRefPtr<FlushableMediaTaskQueue> mTaskQueue;
   };
 
-  virtual nsresult Input(MP4Sample* aSample) override {
+  virtual nsresult Input(MediaRawData* aSample) override {
     MOZ_ASSERT(!mIsShutdown);
     // We run the PDM on its own task queue. We can't run it on the decode
     // task queue, because that calls into Input() in a loop and waits until
     // output is delivered. We need to defer some Input() calls while we wait
     // for keys to become usable, and once they do we need to dispatch an event
     // to run the PDM on the same task queue, but since the decode task queue
     // is waiting in MP4Reader::Decode() for output our task would never run.
     // So we dispatch tasks to make all calls into the wrapped decoder.
     if (mSamplesWaitingForKey->WaitIfKeyNotUsable(aSample)) {
       return NS_OK;
     }
 
-    mProxy->GetSessionIdsForKeyId(aSample->crypto.key,
-                                  aSample->crypto.session_ids);
+    mProxy->GetSessionIdsForKeyId(aSample->mCrypto.mKeyId,
+                                  aSample->mCrypto.mSessionIds);
 
     mProxy->Decrypt(aSample, new DeliverDecrypted(this, mTaskQueue));
     return NS_OK;
   }
 
-  void Decrypted(mp4_demuxer::MP4Sample* aSample) {
+  void Decrypted(MediaRawData* aSample) {
     MOZ_ASSERT(!mIsShutdown);
     nsresult rv = mTaskQueue->Dispatch(
-      NS_NewRunnableMethodWithArg<mp4_demuxer::MP4Sample*>(
+      NS_NewRunnableMethodWithArg<nsRefPtr<MediaRawData>>(
         mDecoder,
         &MediaDataDecoder::Input,
-        aSample));
+        nsRefPtr<MediaRawData>(aSample)));
     unused << NS_WARN_IF(NS_FAILED(rv));
   }
 
   virtual nsresult Flush() override {
     MOZ_ASSERT(!mIsShutdown);
     nsresult rv = mTaskQueue->SyncDispatch(
       NS_NewRunnableMethod(
         mDecoder,
@@ -164,33 +166,33 @@ class EMEMediaDataDecoderProxy : public 
 public:
   EMEMediaDataDecoderProxy(nsIThread* aProxyThread, MediaDataDecoderCallback* aCallback, CDMProxy* aProxy, FlushableMediaTaskQueue* aTaskQueue)
    : MediaDataDecoderProxy(aProxyThread, aCallback)
    , mSamplesWaitingForKey(new SamplesWaitingForKey(this, aTaskQueue, aProxy))
    , mProxy(aProxy)
   {
   }
 
-  virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) override;
+  virtual nsresult Input(MediaRawData* aSample) override;
   virtual nsresult Shutdown() override;
 
 private:
   nsRefPtr<SamplesWaitingForKey> mSamplesWaitingForKey;
   nsRefPtr<CDMProxy> mProxy;
 };
 
 nsresult
-EMEMediaDataDecoderProxy::Input(mp4_demuxer::MP4Sample* aSample)
+EMEMediaDataDecoderProxy::Input(MediaRawData* aSample)
 {
   if (mSamplesWaitingForKey->WaitIfKeyNotUsable(aSample)) {
     return NS_OK;
   }
 
-  mProxy->GetSessionIdsForKeyId(aSample->crypto.key,
-                                aSample->crypto.session_ids);
+  mProxy->GetSessionIdsForKeyId(aSample->mCrypto.mKeyId,
+                                aSample->mCrypto.mSessionIds);
 
   return MediaDataDecoderProxy::Input(aSample);
 }
 
 nsresult
 EMEMediaDataDecoderProxy::Shutdown()
 {
   nsresult rv = MediaDataDecoderProxy::Shutdown();
@@ -237,17 +239,17 @@ CreateDecoderWrapper(MediaDataDecoderCal
 
 already_AddRefed<MediaDataDecoder>
 EMEDecoderModule::CreateVideoDecoder(const VideoDecoderConfig& aConfig,
                                      layers::LayersBackend aLayersBackend,
                                      layers::ImageContainer* aImageContainer,
                                      FlushableMediaTaskQueue* aVideoTaskQueue,
                                      MediaDataDecoderCallback* aCallback)
 {
-  if (mCDMDecodesVideo && aConfig.crypto.valid) {
+  if (mCDMDecodesVideo && aConfig.crypto.mValid) {
     nsRefPtr<MediaDataDecoderProxy> wrapper = CreateDecoderWrapper(aCallback, mProxy, aVideoTaskQueue);
     wrapper->SetProxyTarget(new EMEVideoDecoder(mProxy,
                                                 aConfig,
                                                 aLayersBackend,
                                                 aImageContainer,
                                                 aVideoTaskQueue,
                                                 wrapper->Callback()));
     return wrapper.forget();
@@ -258,47 +260,47 @@ EMEDecoderModule::CreateVideoDecoder(con
                         aVideoTaskQueue,
                         aCallback,
                         aLayersBackend,
                         aImageContainer));
   if (!decoder) {
     return nullptr;
   }
 
-  if (!aConfig.crypto.valid) {
+  if (!aConfig.crypto.mValid) {
     return decoder.forget();
   }
 
   nsRefPtr<MediaDataDecoder> emeDecoder(new EMEDecryptor(decoder,
                                                          aCallback,
                                                          mProxy));
   return emeDecoder.forget();
 }
 
 already_AddRefed<MediaDataDecoder>
 EMEDecoderModule::CreateAudioDecoder(const AudioDecoderConfig& aConfig,
                                      FlushableMediaTaskQueue* aAudioTaskQueue,
                                      MediaDataDecoderCallback* aCallback)
 {
-  if (mCDMDecodesAudio && aConfig.crypto.valid) {
+  if (mCDMDecodesAudio && aConfig.crypto.mValid) {
     nsRefPtr<MediaDataDecoderProxy> wrapper = CreateDecoderWrapper(aCallback, mProxy, aAudioTaskQueue);
     wrapper->SetProxyTarget(new EMEAudioDecoder(mProxy,
                                                 aConfig,
                                                 aAudioTaskQueue,
                                                 wrapper->Callback()));
     return wrapper.forget();
   }
 
   nsRefPtr<MediaDataDecoder> decoder(
     mPDM->CreateDecoder(aConfig, aAudioTaskQueue, aCallback));
   if (!decoder) {
     return nullptr;
   }
 
-  if (!aConfig.crypto.valid) {
+  if (!aConfig.crypto.mValid) {
     return decoder.forget();
   }
 
   nsRefPtr<MediaDataDecoder> emeDecoder(new EMEDecryptor(decoder,
                                                          aCallback,
                                                          mProxy));
   return emeDecoder.forget();
 }
--- a/dom/media/fmp4/eme/EMEVideoDecoder.cpp
+++ b/dom/media/fmp4/eme/EMEVideoDecoder.cpp
@@ -2,16 +2,17 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 "EMEVideoDecoder.h"
 #include "GMPVideoEncodedFrameImpl.h"
 #include "mozilla/CDMProxy.h"
+#include "MediaData.h"
 
 namespace mozilla {
 
 void
 EMEVideoCallbackAdapter::Error(GMPErr aErr)
 {
   if (aErr == GMPNoKeyErr) {
     // The GMP failed to decrypt a frame due to not having a key. This can
@@ -31,18 +32,18 @@ EMEVideoDecoder::InitTags(nsTArray<nsCSt
 
 nsCString
 EMEVideoDecoder::GetNodeId()
 {
   return mProxy->GetNodeId();
 }
 
 GMPUnique<GMPVideoEncodedFrame>::Ptr
-EMEVideoDecoder::CreateFrame(mp4_demuxer::MP4Sample* aSample)
+EMEVideoDecoder::CreateFrame(MediaRawData* aSample)
 {
   GMPUnique<GMPVideoEncodedFrame>::Ptr frame = GMPVideoDecoder::CreateFrame(aSample);
-  if (frame && aSample->crypto.valid) {
-    static_cast<gmp::GMPVideoEncodedFrameImpl*>(frame.get())->InitCrypto(aSample->crypto);
+  if (frame && aSample->mCrypto.mValid) {
+    static_cast<gmp::GMPVideoEncodedFrameImpl*>(frame.get())->InitCrypto(aSample->mCrypto);
   }
   return frame;
 }
 
 } // namespace mozilla
--- a/dom/media/fmp4/eme/EMEVideoDecoder.h
+++ b/dom/media/fmp4/eme/EMEVideoDecoder.h
@@ -39,16 +39,16 @@ public:
                                                                       aConfig.display_height), aImageContainer))
    , mProxy(aProxy)
   {
   }
 
 private:
   virtual void InitTags(nsTArray<nsCString>& aTags) override;
   virtual nsCString GetNodeId() override;
-  virtual GMPUnique<GMPVideoEncodedFrame>::Ptr CreateFrame(mp4_demuxer::MP4Sample* aSample) override;
+  virtual GMPUnique<GMPVideoEncodedFrame>::Ptr CreateFrame(MediaRawData* aSample) override;
 
   nsRefPtr<CDMProxy> mProxy;
 };
 
 }
 
 #endif // EMEVideoDecoder_h_
--- a/dom/media/fmp4/eme/SamplesWaitingForKey.cpp
+++ b/dom/media/fmp4/eme/SamplesWaitingForKey.cpp
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "SamplesWaitingForKey.h"
 #include "mozilla/CDMProxy.h"
 #include "mozilla/CDMCaps.h"
+#include "MediaData.h"
 
 namespace mozilla {
 
 SamplesWaitingForKey::SamplesWaitingForKey(MediaDataDecoder* aDecoder,
                                            MediaTaskQueue* aTaskQueue,
                                            CDMProxy* aProxy)
   : mMutex("SamplesWaitingForKey")
   , mDecoder(aDecoder)
@@ -20,45 +21,45 @@ SamplesWaitingForKey::SamplesWaitingForK
 {
 }
 
 SamplesWaitingForKey::~SamplesWaitingForKey()
 {
 }
 
 bool
-SamplesWaitingForKey::WaitIfKeyNotUsable(MP4Sample* aSample)
+SamplesWaitingForKey::WaitIfKeyNotUsable(MediaRawData* aSample)
 {
-  if (!aSample || !aSample->crypto.valid || !mProxy) {
+  if (!aSample || !aSample->mCrypto.mValid || !mProxy) {
     return false;
   }
   CDMCaps::AutoLock caps(mProxy->Capabilites());
-  const auto& keyid = aSample->crypto.key;
+  const auto& keyid = aSample->mCrypto.mKeyId;
   if (!caps.IsKeyUsable(keyid)) {
     {
       MutexAutoLock lock(mMutex);
       mSamples.AppendElement(aSample);
     }
-    caps.NotifyWhenKeyIdUsable(aSample->crypto.key, this);
+    caps.NotifyWhenKeyIdUsable(aSample->mCrypto.mKeyId, this);
     return true;
   }
   return false;
 }
 
 void
 SamplesWaitingForKey::NotifyUsable(const CencKeyId& aKeyId)
 {
   MutexAutoLock lock(mMutex);
   size_t i = 0;
   while (i < mSamples.Length()) {
-    if (aKeyId == mSamples[i]->crypto.key) {
+    if (aKeyId == mSamples[i]->mCrypto.mKeyId) {
       RefPtr<nsIRunnable> task;
-      task = NS_NewRunnableMethodWithArg<MP4Sample*>(mDecoder,
+      task = NS_NewRunnableMethodWithArg<nsRefPtr<MediaRawData>>(mDecoder,
                                                      &MediaDataDecoder::Input,
-                                                     mSamples[i].forget());
+                                                     nsRefPtr<MediaRawData>(mSamples[i]));
       mSamples.RemoveElementAt(i);
       mTaskQueue->Dispatch(task.forget());
     } else {
       i++;
     }
   }
 }
 
--- a/dom/media/fmp4/eme/SamplesWaitingForKey.h
+++ b/dom/media/fmp4/eme/SamplesWaitingForKey.h
@@ -2,56 +2,54 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 SamplesWaitingForKey_h_
 #define SamplesWaitingForKey_h_
 
-#include "mp4_demuxer/DecoderData.h"
 #include "MediaTaskQueue.h"
 #include "PlatformDecoderModule.h"
 
 namespace mozilla {
 
 typedef nsTArray<uint8_t> CencKeyId;
 
 class CDMProxy;
 
 // Encapsulates the task of waiting for the CDMProxy to have the necessary
 // keys to decypt a given sample.
 class SamplesWaitingForKey {
-  typedef mp4_demuxer::MP4Sample MP4Sample;
 public:
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SamplesWaitingForKey)
 
   explicit SamplesWaitingForKey(MediaDataDecoder* aDecoder,
                                 MediaTaskQueue* aTaskQueue,
                                 CDMProxy* aProxy);
 
   // Returns true if we need to wait for a key to become usable.
   // Will callback MediaDataDecoder::Input(aSample) on mDecoder once the
   // sample is ready to be decrypted. The order of input samples is
   // preserved.
-  bool WaitIfKeyNotUsable(MP4Sample* aSample);
+  bool WaitIfKeyNotUsable(MediaRawData* aSample);
 
   void NotifyUsable(const CencKeyId& aKeyId);
 
   void Flush();
 
   void BreakCycles();
 
 protected:
   ~SamplesWaitingForKey();
 
 private:
   Mutex mMutex;
   nsRefPtr<MediaDataDecoder> mDecoder;
   nsRefPtr<MediaTaskQueue> mTaskQueue;
   nsRefPtr<CDMProxy> mProxy;
-  nsTArray<nsAutoPtr<MP4Sample>> mSamples;
+  nsTArray<nsRefPtr<MediaRawData>> mSamples;
 };
 
 } // namespace mozilla
 
 #endif //  SamplesWaitingForKey_h_
--- a/dom/media/fmp4/ffmpeg/FFmpegAudioDecoder.cpp
+++ b/dom/media/fmp4/ffmpeg/FFmpegAudioDecoder.cpp
@@ -7,29 +7,29 @@
 #include "MediaTaskQueue.h"
 #include "FFmpegRuntimeLinker.h"
 
 #include "FFmpegAudioDecoder.h"
 #include "mp4_demuxer/Adts.h"
 
 #define MAX_CHANNELS 16
 
-typedef mp4_demuxer::MP4Sample MP4Sample;
-
 namespace mozilla
 {
 
 FFmpegAudioDecoder<LIBAV_VER>::FFmpegAudioDecoder(
   FlushableMediaTaskQueue* aTaskQueue, MediaDataDecoderCallback* aCallback,
   const mp4_demuxer::AudioDecoderConfig& aConfig)
   : FFmpegDataDecoder(aTaskQueue, GetCodecId(aConfig.mime_type))
   , mCallback(aCallback)
 {
   MOZ_COUNT_CTOR(FFmpegAudioDecoder);
-  mExtraData = aConfig.audio_specific_config;
+  // Use a new DataBuffer as the object will be modified during initialization.
+  mExtraData = new DataBuffer;
+  mExtraData->AppendElements(*aConfig.audio_specific_config);
 }
 
 nsresult
 FFmpegAudioDecoder<LIBAV_VER>::Init()
 {
   nsresult rv = FFmpegDataDecoder::Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -78,38 +78,32 @@ CopyAndPackAudio(AVFrame* aFrame, uint32
       }
     }
   }
 
   return audio.forget();
 }
 
 void
-FFmpegAudioDecoder<LIBAV_VER>::DecodePacket(MP4Sample* aSample)
+FFmpegAudioDecoder<LIBAV_VER>::DecodePacket(MediaRawData* aSample)
 {
   AVPacket packet;
   av_init_packet(&packet);
 
-  if (!aSample->Pad(FF_INPUT_BUFFER_PADDING_SIZE)) {
-    NS_WARNING("FFmpeg audio decoder failed to allocate sample.");
-    mCallback->Error();
-    return;
-  }
-
-  packet.data = aSample->data;
-  packet.size = aSample->size;
+  packet.data = const_cast<uint8_t*>(aSample->mData);
+  packet.size = aSample->mSize;
 
   if (!PrepareFrame()) {
     NS_WARNING("FFmpeg audio decoder failed to allocate frame.");
     mCallback->Error();
     return;
   }
 
-  int64_t samplePosition = aSample->byte_offset;
-  Microseconds pts = aSample->composition_timestamp;
+  int64_t samplePosition = aSample->mOffset;
+  Microseconds pts = aSample->mTime;
 
   while (packet.size > 0) {
     int decoded;
     int bytesConsumed =
       avcodec_decode_audio4(mCodecContext, mFrame, &decoded, &packet);
 
     if (bytesConsumed < 0) {
       NS_WARNING("FFmpeg audio decoder error.");
@@ -148,20 +142,20 @@ FFmpegAudioDecoder<LIBAV_VER>::DecodePac
   }
 
   if (mTaskQueue->IsEmpty()) {
     mCallback->InputExhausted();
   }
 }
 
 nsresult
-FFmpegAudioDecoder<LIBAV_VER>::Input(MP4Sample* aSample)
+FFmpegAudioDecoder<LIBAV_VER>::Input(MediaRawData* aSample)
 {
-  mTaskQueue->Dispatch(NS_NewRunnableMethodWithArg<nsAutoPtr<MP4Sample> >(
-    this, &FFmpegAudioDecoder::DecodePacket, nsAutoPtr<MP4Sample>(aSample)));
+  mTaskQueue->Dispatch(NS_NewRunnableMethodWithArg<nsRefPtr<MediaRawData> >(
+    this, &FFmpegAudioDecoder::DecodePacket, nsRefPtr<MediaRawData>(aSample)));
 
   return NS_OK;
 }
 
 nsresult
 FFmpegAudioDecoder<LIBAV_VER>::Drain()
 {
   mTaskQueue->AwaitIdle();
--- a/dom/media/fmp4/ffmpeg/FFmpegAudioDecoder.h
+++ b/dom/media/fmp4/ffmpeg/FFmpegAudioDecoder.h
@@ -21,21 +21,21 @@ class FFmpegAudioDecoder<LIBAV_VER> : pu
 {
 public:
   FFmpegAudioDecoder(FlushableMediaTaskQueue* aTaskQueue,
                      MediaDataDecoderCallback* aCallback,
                      const mp4_demuxer::AudioDecoderConfig& aConfig);
   virtual ~FFmpegAudioDecoder();
 
   virtual nsresult Init() override;
-  virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) override;
+  virtual nsresult Input(MediaRawData* aSample) override;
   virtual nsresult Drain() override;
   static AVCodecID GetCodecId(const nsACString& aMimeType);
 
 private:
-  void DecodePacket(mp4_demuxer::MP4Sample* aSample);
+  void DecodePacket(MediaRawData* aSample);
 
   MediaDataDecoderCallback* mCallback;
 };
 
 } // namespace mozilla
 
 #endif // __FFmpegAACDecoder_h__
--- a/dom/media/fmp4/ffmpeg/FFmpegDataDecoder.cpp
+++ b/dom/media/fmp4/ffmpeg/FFmpegDataDecoder.cpp
@@ -91,19 +91,19 @@ FFmpegDataDecoder<LIBAV_VER>::Init()
   mCodecContext->get_format = ChoosePixelFormat;
 
   mCodecContext->thread_count = PR_GetNumberOfProcessors();
   mCodecContext->thread_type = FF_THREAD_SLICE | FF_THREAD_FRAME;
   mCodecContext->thread_safe_callbacks = false;
 
   if (mExtraData) {
     mCodecContext->extradata_size = mExtraData->Length();
-    for (int i = 0; i < FF_INPUT_BUFFER_PADDING_SIZE; i++) {
-      mExtraData->AppendElement(0);
-    }
+    // FFmpeg may use SIMD instructions to access the data which reads the
+    // data in 32 bytes block. Must ensure we have enough data to read.
+    mExtraData->AppendElements(FF_INPUT_BUFFER_PADDING_SIZE);
     mCodecContext->extradata = mExtraData->Elements();
   } else {
     mCodecContext->extradata_size = 0;
   }
 
   if (codec->capabilities & CODEC_CAP_DR1) {
     mCodecContext->flags |= CODEC_FLAG_EMU_EDGE;
   }
--- a/dom/media/fmp4/ffmpeg/FFmpegDataDecoder.h
+++ b/dom/media/fmp4/ffmpeg/FFmpegDataDecoder.h
@@ -25,28 +25,28 @@ class FFmpegDataDecoder<LIBAV_VER> : pub
 {
 public:
   FFmpegDataDecoder(FlushableMediaTaskQueue* aTaskQueue, AVCodecID aCodecID);
   virtual ~FFmpegDataDecoder();
 
   static bool Link();
 
   virtual nsresult Init() override;
-  virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) override = 0;
+  virtual nsresult Input(MediaRawData* aSample) override = 0;
   virtual nsresult Flush() override;
   virtual nsresult Drain() override = 0;
   virtual nsresult Shutdown() override;
 
 protected:
   AVFrame*        PrepareFrame();
 
   FlushableMediaTaskQueue* mTaskQueue;
   AVCodecContext* mCodecContext;
   AVFrame*        mFrame;
-  nsRefPtr<mp4_demuxer::ByteBuffer> mExtraData;
+  nsRefPtr<DataBuffer> mExtraData;
 
 private:
   static bool sFFmpegInitDone;
   static StaticMutex sMonitor;
 
   AVCodecID mCodecID;
 };
 
--- a/dom/media/fmp4/ffmpeg/FFmpegH264Decoder.cpp
+++ b/dom/media/fmp4/ffmpeg/FFmpegH264Decoder.cpp
@@ -13,65 +13,59 @@
 
 #include "FFmpegH264Decoder.h"
 
 #define GECKO_FRAME_TYPE 0x00093CC0
 
 typedef mozilla::layers::Image Image;
 typedef mozilla::layers::PlanarYCbCrImage PlanarYCbCrImage;
 
-typedef mp4_demuxer::MP4Sample MP4Sample;
-
 namespace mozilla
 {
 
 FFmpegH264Decoder<LIBAV_VER>::FFmpegH264Decoder(
   FlushableMediaTaskQueue* aTaskQueue, MediaDataDecoderCallback* aCallback,
   const mp4_demuxer::VideoDecoderConfig& aConfig,
   ImageContainer* aImageContainer)
   : FFmpegDataDecoder(aTaskQueue, GetCodecId(aConfig.mime_type))
   , mCallback(aCallback)
   , mImageContainer(aImageContainer)
   , mDisplayWidth(aConfig.display_width)
   , mDisplayHeight(aConfig.display_height)
 {
   MOZ_COUNT_CTOR(FFmpegH264Decoder);
-  mExtraData = aConfig.extra_data;
+  // Use a new DataBuffer as the object will be modified during initialization.
+  mExtraData = new DataBuffer;
+  mExtraData->AppendElements(*aConfig.extra_data);
 }
 
 nsresult
 FFmpegH264Decoder<LIBAV_VER>::Init()
 {
   nsresult rv = FFmpegDataDecoder::Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
   mCodecContext->get_buffer = AllocateBufferCb;
   mCodecContext->release_buffer = ReleaseBufferCb;
 
   return NS_OK;
 }
 
 FFmpegH264Decoder<LIBAV_VER>::DecodeResult
-FFmpegH264Decoder<LIBAV_VER>::DoDecodeFrame(mp4_demuxer::MP4Sample* aSample)
+FFmpegH264Decoder<LIBAV_VER>::DoDecodeFrame(MediaRawData* aSample)
 {
   AVPacket packet;
   av_init_packet(&packet);
 
-  if (!aSample->Pad(FF_INPUT_BUFFER_PADDING_SIZE)) {
-    NS_WARNING("FFmpeg h264 decoder failed to allocate sample.");
-    mCallback->Error();
-    return DecodeResult::DECODE_ERROR;
-  }
-
-  packet.data = aSample->data;
-  packet.size = aSample->size;
-  packet.dts = aSample->decode_timestamp;
-  packet.pts = aSample->composition_timestamp;
-  packet.flags = aSample->is_sync_point ? AV_PKT_FLAG_KEY : 0;
-  packet.pos = aSample->byte_offset;
+  packet.data = const_cast<uint8_t*>(aSample->mData);
+  packet.size = aSample->mSize;
+  packet.dts = aSample->mTimecode;
+  packet.pts = aSample->mTime;
+  packet.flags = aSample->mKeyframe ? AV_PKT_FLAG_KEY : 0;
+  packet.pos = aSample->mOffset;
 
   if (!PrepareFrame()) {
     NS_WARNING("FFmpeg h264 decoder failed to allocate frame.");
     mCallback->Error();
     return DecodeResult::DECODE_ERROR;
   }
 
   int decoded;
@@ -107,36 +101,36 @@ FFmpegH264Decoder<LIBAV_VER>::DoDecodeFr
     b.mPlanes[2].mData = mFrame->data[2];
     b.mPlanes[2].mStride = mFrame->linesize[2];
     b.mPlanes[2].mHeight = (mFrame->height + 1) >> 1;
     b.mPlanes[2].mWidth = (mFrame->width + 1) >> 1;
     b.mPlanes[2].mOffset = b.mPlanes[2].mSkip = 0;
 
     nsRefPtr<VideoData> v = VideoData::Create(info,
                                               mImageContainer,
-                                              aSample->byte_offset,
+                                              aSample->mOffset,
                                               mFrame->pkt_pts,
-                                              aSample->duration,
+                                              aSample->mDuration,
                                               b,
-                                              aSample->is_sync_point,
+                                              aSample->mKeyframe,
                                               -1,
                                               gfx::IntRect(0, 0, mCodecContext->width, mCodecContext->height));
     if (!v) {
       NS_WARNING("image allocation error.");
       mCallback->Error();
       return DecodeResult::DECODE_ERROR;
     }
     mCallback->Output(v);
     return DecodeResult::DECODE_FRAME;
   }
   return DecodeResult::DECODE_NO_FRAME;
 }
 
 void
-FFmpegH264Decoder<LIBAV_VER>::DecodeFrame(mp4_demuxer::MP4Sample* aSample)
+FFmpegH264Decoder<LIBAV_VER>::DecodeFrame(MediaRawData* aSample)
 {
   if (DoDecodeFrame(aSample) != DecodeResult::DECODE_ERROR &&
       mTaskQueue->IsEmpty()) {
     mCallback->InputExhausted();
   }
 }
 
 /* static */ int
@@ -240,30 +234,30 @@ FFmpegH264Decoder<LIBAV_VER>::AllocateYU
   aFrame->height = aCodecContext->height;
 
   aFrame->opaque = static_cast<void*>(image.forget().take());
 
   return 0;
 }
 
 nsresult
-FFmpegH264Decoder<LIBAV_VER>::Input(mp4_demuxer::MP4Sample* aSample)
+FFmpegH264Decoder<LIBAV_VER>::Input(MediaRawData* aSample)
 {
   mTaskQueue->Dispatch(
-    NS_NewRunnableMethodWithArg<nsAutoPtr<mp4_demuxer::MP4Sample>>(
+    NS_NewRunnableMethodWithArg<nsRefPtr<MediaRawData>>(
       this, &FFmpegH264Decoder<LIBAV_VER>::DecodeFrame,
-      nsAutoPtr<mp4_demuxer::MP4Sample>(aSample)));
+      nsRefPtr<MediaRawData>(aSample)));
 
   return NS_OK;
 }
 
 void
 FFmpegH264Decoder<LIBAV_VER>::DoDrain()
 {
-  nsAutoPtr<MP4Sample> empty(new MP4Sample());
+  nsRefPtr<MediaRawData> empty(new MediaRawData());
   while (DoDecodeFrame(empty) == DecodeResult::DECODE_FRAME) {
   }
   mCallback->DrainComplete();
 }
 
 nsresult
 FFmpegH264Decoder<LIBAV_VER>::Drain()
 {
--- a/dom/media/fmp4/ffmpeg/FFmpegH264Decoder.h
+++ b/dom/media/fmp4/ffmpeg/FFmpegH264Decoder.h
@@ -32,24 +32,24 @@ class FFmpegH264Decoder<LIBAV_VER> : pub
 public:
   FFmpegH264Decoder(FlushableMediaTaskQueue* aTaskQueue,
                     MediaDataDecoderCallback* aCallback,
                     const mp4_demuxer::VideoDecoderConfig& aConfig,
                     ImageContainer* aImageContainer);
   virtual ~FFmpegH264Decoder();
 
   virtual nsresult Init() override;
-  virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) override;
+  virtual nsresult Input(MediaRawData* aSample) override;
   virtual nsresult Drain() override;
   virtual nsresult Flush() override;
   static AVCodecID GetCodecId(const nsACString& aMimeType);
 
 private:
-  void DecodeFrame(mp4_demuxer::MP4Sample* aSample);
-  DecodeResult DoDecodeFrame(mp4_demuxer::MP4Sample* aSample);
+  void DecodeFrame(MediaRawData* aSample);
+  DecodeResult DoDecodeFrame(MediaRawData* aSample);
   void DoDrain();
   void OutputDelayedFrames();
 
   /**
    * This method allocates a buffer for FFmpeg's decoder, wrapped in an Image.
    * Currently it only supports Planar YUV420, which appears to be the only
    * non-hardware accelerated image format that FFmpeg's H264 decoder is
    * capable of outputting.
--- a/dom/media/fmp4/gmp/GMPAudioDecoder.cpp
+++ b/dom/media/fmp4/gmp/GMPAudioDecoder.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 "GMPAudioDecoder.h"
 #include "nsServiceManagerUtils.h"
+#include "mp4_demuxer/DecoderData.h"
 
 namespace mozilla {
 
 #if defined(DEBUG)
 bool IsOnGMPThread()
 {
   nsCOMPtr<mozIGeckoMediaPluginService> mps = do_GetService("@mozilla.org/gecko-media-plugin-service;1");
   MOZ_ASSERT(mps);
@@ -186,27 +187,27 @@ GMPAudioDecoder::Init()
   while (!initDone->IsDone()) {
     NS_ProcessNextEvent(gmpThread, true);
   }
 
   return mGMP ? NS_OK : NS_ERROR_FAILURE;
 }
 
 nsresult
-GMPAudioDecoder::Input(mp4_demuxer::MP4Sample* aSample)
+GMPAudioDecoder::Input(MediaRawData* aSample)
 {
   MOZ_ASSERT(IsOnGMPThread());
 
-  nsAutoPtr<mp4_demuxer::MP4Sample> sample(aSample);
+  nsRefPtr<MediaRawData> sample(aSample);
   if (!mGMP) {
     mCallback->Error();
     return NS_ERROR_FAILURE;
   }
 
-  mAdapter->SetLastStreamOffset(sample->byte_offset);
+  mAdapter->SetLastStreamOffset(sample->mOffset);
 
   gmp::GMPAudioSamplesImpl samples(sample, mConfig.channel_count, mConfig.samples_per_second);
   nsresult rv = mGMP->Decode(samples);
   if (NS_FAILED(rv)) {
     mCallback->Error();
     return rv;
   }
 
--- a/dom/media/fmp4/gmp/GMPAudioDecoder.h
+++ b/dom/media/fmp4/gmp/GMPAudioDecoder.h
@@ -65,17 +65,17 @@ public:
    : mConfig(aConfig)
    , mCallback(aCallback)
    , mGMP(nullptr)
    , mAdapter(new AudioCallbackAdapter(aCallback))
   {
   }
 
   virtual nsresult Init() override;
-  virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) override;
+  virtual nsresult Input(MediaRawData* aSample) override;
   virtual nsresult Flush() override;
   virtual nsresult Drain() override;
   virtual nsresult Shutdown() override;
 
 protected:
   virtual void InitTags(nsTArray<nsCString>& aTags);
   virtual nsCString GetNodeId();
 
--- a/dom/media/fmp4/gmp/GMPVideoDecoder.cpp
+++ b/dom/media/fmp4/gmp/GMPVideoDecoder.cpp
@@ -3,16 +3,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "GMPVideoDecoder.h"
 #include "GMPVideoHost.h"
 #include "mozilla/Endian.h"
 #include "prsystem.h"
+#include "MediaData.h"
+#include "mp4_demuxer/DecoderData.h"
 
 namespace mozilla {
 
 #if defined(DEBUG)
 extern bool IsOnGMPThread();
 #endif
 
 void
@@ -110,33 +112,33 @@ GMPVideoDecoder::InitTags(nsTArray<nsCSt
 
 nsCString
 GMPVideoDecoder::GetNodeId()
 {
   return NS_LITERAL_CSTRING("");
 }
 
 GMPUnique<GMPVideoEncodedFrame>::Ptr
-GMPVideoDecoder::CreateFrame(mp4_demuxer::MP4Sample* aSample)
+GMPVideoDecoder::CreateFrame(MediaRawData* aSample)
 {
   GMPVideoFrame* ftmp = nullptr;
   GMPErr err = mHost->CreateFrame(kGMPEncodedVideoFrame, &ftmp);
   if (GMP_FAILED(err)) {
     mCallback->Error();
     return nullptr;
   }
 
   GMPUnique<GMPVideoEncodedFrame>::Ptr frame(static_cast<GMPVideoEncodedFrame*>(ftmp));
-  err = frame->CreateEmptyFrame(aSample->size);
+  err = frame->CreateEmptyFrame(aSample->mSize);
   if (GMP_FAILED(err)) {
     mCallback->Error();
     return nullptr;
   }
 
-  memcpy(frame->Buffer(), aSample->data, frame->Size());
+  memcpy(frame->Buffer(), aSample->mData, frame->Size());
 
   // Convert 4-byte NAL unit lengths to host-endian 4-byte buffer lengths to
   // suit the GMP API.
   if (mConvertNALUnitLengths) {
     const int kNALLengthSize = 4;
     uint8_t* buf = frame->Buffer();
     while (buf < frame->Buffer() + frame->Size() - kNALLengthSize) {
       uint32_t length = BigEndian::readUint32(buf) + kNALLengthSize;
@@ -144,20 +146,20 @@ GMPVideoDecoder::CreateFrame(mp4_demuxer
       buf += length;
     }
   }
 
   frame->SetBufferType(GMP_BufferLength32);
 
   frame->SetEncodedWidth(mConfig.display_width);
   frame->SetEncodedHeight(mConfig.display_height);
-  frame->SetTimeStamp(aSample->composition_timestamp);
+  frame->SetTimeStamp(aSample->mTime);
   frame->SetCompleteFrame(true);
-  frame->SetDuration(aSample->duration);
-  frame->SetFrameType(aSample->is_sync_point ? kGMPKeyFrame : kGMPDeltaFrame);
+  frame->SetDuration(aSample->mDuration);
+  frame->SetFrameType(aSample->mKeyframe ? kGMPKeyFrame : kGMPDeltaFrame);
 
   return frame;
 }
 
 void
 GMPVideoDecoder::GetGMPAPI(GMPInitDoneRunnable* aInitDone)
 {
   MOZ_ASSERT(IsOnGMPThread());
@@ -230,27 +232,27 @@ GMPVideoDecoder::Init()
   while (!initDone->IsDone()) {
     NS_ProcessNextEvent(gmpThread, true);
   }
 
   return mGMP ? NS_OK : NS_ERROR_FAILURE;
 }
 
 nsresult
-GMPVideoDecoder::Input(mp4_demuxer::MP4Sample* aSample)
+GMPVideoDecoder::Input(MediaRawData* aSample)
 {
   MOZ_ASSERT(IsOnGMPThread());
 
-  nsAutoPtr<mp4_demuxer::MP4Sample> sample(aSample);
+  nsRefPtr<MediaRawData> sample(aSample);
   if (!mGMP) {
     mCallback->Error();
     return NS_ERROR_FAILURE;
   }
 
-  mAdapter->SetLastStreamOffset(sample->byte_offset);
+  mAdapter->SetLastStreamOffset(sample->mOffset);
 
   GMPUnique<GMPVideoEncodedFrame>::Ptr frame = CreateFrame(sample);
   nsTArray<uint8_t> info; // No codec specific per-frame info to pass.
   nsresult rv = mGMP->Decode(Move(frame), false, info, 0);
   if (NS_FAILED(rv)) {
     mCallback->Error();
     return rv;
   }
--- a/dom/media/fmp4/gmp/GMPVideoDecoder.h
+++ b/dom/media/fmp4/gmp/GMPVideoDecoder.h
@@ -80,25 +80,25 @@ public:
                                        VideoInfo(aConfig.display_width,
                                                  aConfig.display_height),
                                        aImageContainer))
    , mConvertNALUnitLengths(false)
   {
   }
 
   virtual nsresult Init() override;
-  virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) override;
+  virtual nsresult Input(MediaRawData* aSample) override;
   virtual nsresult Flush() override;
   virtual nsresult Drain() override;
   virtual nsresult Shutdown() override;
 
 protected:
   virtual void InitTags(nsTArray<nsCString>& aTags);
   virtual nsCString GetNodeId();
-  virtual GMPUnique<GMPVideoEncodedFrame>::Ptr CreateFrame(mp4_demuxer::MP4Sample* aSample);
+  virtual GMPUnique<GMPVideoEncodedFrame>::Ptr CreateFrame(MediaRawData* aSample);
 
 private:
   class GMPInitDoneRunnable : public nsRunnable
   {
   public:
     GMPInitDoneRunnable()
       : mInitDone(false),
         mThread(do_GetCurrentThread())
--- a/dom/media/fmp4/gmp/MediaDataDecoderProxy.cpp
+++ b/dom/media/fmp4/gmp/MediaDataDecoderProxy.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 "MediaDataDecoderProxy.h"
+#include "MediaData.h"
 
 namespace mozilla {
 
 void
 MediaDataDecoderCallbackProxy::Error()
 {
   mProxyCallback->Error();
 }
@@ -28,17 +29,17 @@ MediaDataDecoderProxy::Init()
   nsresult rv = mProxyThread->Dispatch(task, NS_DISPATCH_SYNC);
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_SUCCESS(task->Result(), task->Result());
 
   return NS_OK;
 }
 
 nsresult
-MediaDataDecoderProxy::Input(mp4_demuxer::MP4Sample* aSample)
+MediaDataDecoderProxy::Input(MediaRawData* aSample)
 {
   MOZ_ASSERT(!IsOnProxyThread());
   MOZ_ASSERT(!mIsShutdown);
 
   nsCOMPtr<nsIRunnable> task(new InputTask(mProxyDecoder, aSample));
   nsresult rv = mProxyThread->Dispatch(task, NS_DISPATCH_NORMAL);
   NS_ENSURE_SUCCESS(rv, rv);
 
--- a/dom/media/fmp4/gmp/MediaDataDecoderProxy.h
+++ b/dom/media/fmp4/gmp/MediaDataDecoderProxy.h
@@ -3,40 +3,38 @@
 /* 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/. */
 
 #if !defined(MediaDataDecoderProxy_h_)
 #define MediaDataDecoderProxy_h_
 
 #include "PlatformDecoderModule.h"
-#include "mp4_demuxer/DecoderData.h"
-#include "nsAutoPtr.h"
 #include "nsRefPtr.h"
 #include "nsThreadUtils.h"
 #include "nscore.h"
 
 namespace mozilla {
 
 class InputTask : public nsRunnable {
 public:
   InputTask(MediaDataDecoder* aDecoder,
-            mp4_demuxer::MP4Sample* aSample)
+            MediaRawData* aSample)
    : mDecoder(aDecoder)
    , mSample(aSample)
   {}
 
   NS_IMETHOD Run() {
-    mDecoder->Input(mSample.forget());
+    mDecoder->Input(mSample);
     return NS_OK;
   }
 
 private:
   nsRefPtr<MediaDataDecoder> mDecoder;
-  nsAutoPtr<mp4_demuxer::MP4Sample> mSample;
+  nsRefPtr<MediaRawData> mSample;
 };
 
 class InitTask : public nsRunnable {
 public:
   explicit InitTask(MediaDataDecoder* aDecoder)
    : mDecoder(aDecoder)
    , mResultValid(false)
   {}
@@ -83,17 +81,17 @@ private:
   Monitor mMonitor;
   T mCondition;
 };
 
 class MediaDataDecoderProxy;
 
 class MediaDataDecoderCallbackProxy : public MediaDataDecoderCallback {
 public:
-  explicit MediaDataDecoderCallbackProxy(MediaDataDecoderProxy* aProxyDecoder, MediaDataDecoderCallback* aCallback)
+  MediaDataDecoderCallbackProxy(MediaDataDecoderProxy* aProxyDecoder, MediaDataDecoderCallback* aCallback)
    : mProxyDecoder(aProxyDecoder)
    , mProxyCallback(aCallback)
   {
   }
 
   virtual void Output(MediaData* aData) override {
     mProxyCallback->Output(aData);
   }
@@ -152,17 +150,17 @@ public:
     mProxyDecoder = aProxyDecoder;
   }
 
   // These are called from the decoder thread pool.
   // Init and Shutdown run synchronously on the proxy thread, all others are
   // asynchronously and responded to via the MediaDataDecoderCallback.
   // Note: the nsresults returned by the proxied decoder are lost.
   virtual nsresult Init() override;
-  virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) override;
+  virtual nsresult Input(MediaRawData* aSample) override;
   virtual nsresult Flush() override;
   virtual nsresult Drain() override;
   virtual nsresult Shutdown() override;
 
   // Called by MediaDataDecoderCallbackProxy.
   void FlushComplete();
 
 private:
--- a/dom/media/fmp4/gonk/GonkAudioDecoderManager.cpp
+++ b/dom/media/fmp4/gonk/GonkAudioDecoderManager.cpp
@@ -14,16 +14,17 @@
 #include "nsTArray.h"
 #include "prlog.h"
 #include "stagefright/MediaBuffer.h"
 #include "stagefright/MetaData.h"
 #include "stagefright/MediaErrors.h"
 #include <stagefright/foundation/AMessage.h>
 #include <stagefright/foundation/ALooper.h>
 #include "media/openmax/OMX_Audio.h"
+#include "MediaData.h"
 
 #include <android/log.h>
 #define GADM_LOG(...) __android_log_print(ANDROID_LOG_DEBUG, "GonkAudioDecoderManager", __VA_ARGS__)
 
 #ifdef PR_LOGGING
 PRLogModuleInfo* GetDemuxerLog();
 #define LOG(...) PR_LOG(GetDemuxerLog(), PR_LOG_DEBUG, (__VA_ARGS__))
 #else
@@ -95,26 +96,26 @@ GonkAudioDecoderManager::Init(MediaDataD
     return mDecoder;
   } else {
     GADM_LOG("Failed to input codec specific data!");
     return nullptr;
   }
 }
 
 status_t
-GonkAudioDecoderManager::SendSampleToOMX(mp4_demuxer::MP4Sample* aSample)
+GonkAudioDecoderManager::SendSampleToOMX(MediaRawData* aSample)
 {
-  return mDecoder->Input(reinterpret_cast<const uint8_t*>(aSample->data),
-                         aSample->size,
-                         aSample->composition_timestamp,
+  return mDecoder->Input(reinterpret_cast<const uint8_t*>(aSample->mData),
+                         aSample->mSize,
+                         aSample->mTime,
                          0);
 }
 
 bool
-GonkAudioDecoderManager::PerformFormatSpecificProcess(mp4_demuxer::MP4Sample* aSample)
+GonkAudioDecoderManager::PerformFormatSpecificProcess(MediaRawData* aSample)
 {
   if (aSample && mUseAdts) {
     int8_t frequency_index =
         mp4_demuxer::Adts::GetFrequencyIndex(mAudioRate);
     bool rv = mp4_demuxer::Adts::ConvertSample(mAudioChannels,
                                                frequency_index,
                                                mAudioProfile,
                                                aSample);
--- a/dom/media/fmp4/gonk/GonkAudioDecoderManager.h
+++ b/dom/media/fmp4/gonk/GonkAudioDecoderManager.h
@@ -30,19 +30,19 @@ public:
   virtual android::sp<MediaCodecProxy> Init(MediaDataDecoderCallback* aCallback) override;
 
   virtual nsresult Output(int64_t aStreamOffset,
                           nsRefPtr<MediaData>& aOutput) override;
 
   virtual nsresult Flush() override;
 
 protected:
-  virtual bool PerformFormatSpecificProcess(mp4_demuxer::MP4Sample* aSample) override;
+  virtual bool PerformFormatSpecificProcess(MediaRawData* aSample) override;
 
-  virtual status_t SendSampleToOMX(mp4_demuxer::MP4Sample* aSample) override;
+  virtual status_t SendSampleToOMX(MediaRawData* aSample) override;
 
 private:
 
   nsresult CreateAudioData(int64_t aStreamOffset,
                               AudioData** aOutData);
 
   void ReleaseAudioBuffer();
   // MediaCodedc's wrapper that performs the decoding.
--- a/dom/media/fmp4/gonk/GonkDecoderModule.cpp
+++ b/dom/media/fmp4/gonk/GonkDecoderModule.cpp
@@ -50,15 +50,15 @@ GonkDecoderModule::CreateAudioDecoder(co
                            aAudioTaskQueue, aCallback);
   return decoder.forget();
 }
 
 PlatformDecoderModule::ConversionRequired
 GonkDecoderModule::DecoderNeedsConversion(const mp4_demuxer::TrackConfig& aConfig) const
 {
   if (aConfig.IsVideoConfig()) {
-    return kNeedAVCC;
+    return kNeedAnnexB;
   } else {
     return kNeedNone;
   }
 }
 
 } // namespace mozilla
--- a/dom/media/fmp4/gonk/GonkMediaDataDecoder.cpp
+++ b/dom/media/fmp4/gonk/GonkMediaDataDecoder.cpp
@@ -3,16 +3,17 @@
 /* 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 "mp4_demuxer/mp4_demuxer.h"
 #include "GonkMediaDataDecoder.h"
 #include "VideoUtils.h"
 #include "nsTArray.h"
 #include "MediaCodecProxy.h"
+#include "MediaData.h"
 
 #include "prlog.h"
 #include <android/log.h>
 #define GMDD_LOG(...) __android_log_print(ANDROID_LOG_DEBUG, "GonkMediaDataDecoder", __VA_ARGS__)
 
 #ifdef PR_LOGGING
 PRLogModuleInfo* GetDemuxerLog();
 #define LOG(...) PR_LOG(GetDemuxerLog(), PR_LOG_DEBUG, (__VA_ARGS__))
@@ -25,17 +26,17 @@ using namespace android;
 namespace mozilla {
 
 GonkDecoderManager::GonkDecoderManager(MediaTaskQueue* aTaskQueue)
   : mTaskQueue(aTaskQueue)
 {
 }
 
 nsresult
-GonkDecoderManager::Input(mp4_demuxer::MP4Sample* aSample)
+GonkDecoderManager::Input(MediaRawData* aSample)
 {
   MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
 
   // To maintain the order of the MP4Sample, it needs to send the queued samples
   // to OMX first. And then the current input aSample.
   // If it fails to input sample to OMX, it needs to add current into queue
   // for next round.
   uint32_t len = mQueueSample.Length();
@@ -44,28 +45,28 @@ GonkDecoderManager::Input(mp4_demuxer::M
   for (uint32_t i = 0; i < len; i++) {
     rv = SendSampleToOMX(mQueueSample.ElementAt(0));
     if (rv != OK) {
       break;
     }
     mQueueSample.RemoveElementAt(0);
   }
 
-  // When EOS, aSample will be null and sends this empty MP4Sample to nofity
+  // When EOS, aSample will be null and sends this empty MediaRawData to nofity
   // OMX it reachs EOS.
-  nsAutoPtr<mp4_demuxer::MP4Sample> sample;
+  nsRefPtr<MediaRawData> sample;
   if (!aSample) {
-    sample = new mp4_demuxer::MP4Sample();
+    sample = new MediaRawData();
   }
 
   // If rv is OK, that means mQueueSample is empty, now try to queue current input
   // aSample.
   if (rv == OK) {
     MOZ_ASSERT(!mQueueSample.Length());
-    mp4_demuxer::MP4Sample* tmp;
+    MediaRawData* tmp;
     if (aSample) {
       tmp = aSample;
       if (!PerformFormatSpecificProcess(aSample)) {
         return NS_ERROR_FAILURE;
       }
     } else {
       tmp = sample;
     }
@@ -146,38 +147,38 @@ GonkMediaDataDecoder::Shutdown()
 {
   mDecoder->stop();
   mDecoder = nullptr;
   return NS_OK;
 }
 
 // Inserts data into the decoder's pipeline.
 nsresult
-GonkMediaDataDecoder::Input(mp4_demuxer::MP4Sample* aSample)
+GonkMediaDataDecoder::Input(MediaRawData* aSample)
 {
   mTaskQueue->Dispatch(
-    NS_NewRunnableMethodWithArg<nsAutoPtr<mp4_demuxer::MP4Sample>>(
+    NS_NewRunnableMethodWithArg<nsRefPtr<MediaRawData>>(
       this,
       &GonkMediaDataDecoder::ProcessDecode,
-      nsAutoPtr<mp4_demuxer::MP4Sample>(aSample)));
+      nsRefPtr<MediaRawData>(aSample)));
   return NS_OK;
 }
 
 void
-GonkMediaDataDecoder::ProcessDecode(mp4_demuxer::MP4Sample* aSample)
+GonkMediaDataDecoder::ProcessDecode(MediaRawData* aSample)
 {
   nsresult rv = mManager->Input(aSample);
   if (rv != NS_OK) {
     NS_WARNING("GonkAudioDecoder failed to input data");
     GMDD_LOG("Failed to input data err: %d",rv);
     mCallback->Error();
     return;
   }
   if (aSample) {
-    mLastStreamOffset = aSample->byte_offset;
+    mLastStreamOffset = aSample->mOffset;
   }
   ProcessOutput();
 }
 
 void
 GonkMediaDataDecoder::ProcessOutput()
 {
   nsRefPtr<MediaData> output;
--- a/dom/media/fmp4/gonk/GonkMediaDataDecoder.h
+++ b/dom/media/fmp4/gonk/GonkMediaDataDecoder.h
@@ -1,39 +1,39 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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/. */
 
 #if !defined(GonkMediaDataDecoder_h_)
 #define GonkMediaDataDecoder_h_
-#include "mp4_demuxer/mp4_demuxer.h"
 #include "mozilla/RefPtr.h"
 #include "MP4Reader.h"
 
 namespace android {
 class MediaCodecProxy;
 } // namespace android
 
 namespace mozilla {
+class MediaRawData;
 
 // Manage the data flow from inputting encoded data and outputting decode data.
 class GonkDecoderManager {
 public:
   GonkDecoderManager(MediaTaskQueue* aTaskQueue);
 
   virtual ~GonkDecoderManager() {}
 
   // Creates and initializs the GonkDecoder.
   // Returns nullptr on failure.
   virtual android::sp<android::MediaCodecProxy> Init(MediaDataDecoderCallback* aCallback) = 0;
 
   // Add samples into OMX decoder or queue them if decoder is out of input buffer.
-  virtual nsresult Input(mp4_demuxer::MP4Sample* aSample);
+  virtual nsresult Input(MediaRawData* aSample);
 
   // Produces decoded output, it blocks until output can be produced or a timeout
   // is expired or until EOS. Returns NS_OK on success, or NS_ERROR_NOT_AVAILABLE
   // if there's not enough data to produce more output. If this returns a failure
   // code other than NS_ERROR_NOT_AVAILABLE, an error will be reported to the
   // MP4Reader.
   // The overrided class should follow the same behaviour.
   virtual nsresult Output(int64_t aStreamOffset,
@@ -57,25 +57,25 @@ public:
   void ClearQueuedSample() {
     MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
     mQueueSample.Clear();
   }
 
 protected:
   // It performs special operation to MP4 sample, the real action is depended on
   // the codec type.
-  virtual bool PerformFormatSpecificProcess(mp4_demuxer::MP4Sample* aSample) { return true; }
+  virtual bool PerformFormatSpecificProcess(MediaRawData* aSample) { return true; }
 
   // It sends MP4Sample to OMX layer. It must be overrided by subclass.
-  virtual android::status_t SendSampleToOMX(mp4_demuxer::MP4Sample* aSample) = 0;
+  virtual android::status_t SendSampleToOMX(MediaRawData* aSample) = 0;
 
   // An queue with the MP4 samples which are waiting to be sent into OMX.
   // If an element is an empty MP4Sample, that menas EOS. There should not
   // any sample be queued after EOS.
-  nsTArray<nsAutoPtr<mp4_demuxer::MP4Sample>> mQueueSample;
+  nsTArray<nsRefPtr<MediaRawData>> mQueueSample;
 
   RefPtr<MediaTaskQueue> mTaskQueue;
 };
 
 // Samples are decoded using the GonkDecoder (MediaCodec)
 // created by the GonkDecoderManager. This class implements
 // the higher-level logic that drives mapping the Gonk to the async
 // MediaDataDecoder interface. The specifics of decoding the exact stream
@@ -85,17 +85,17 @@ public:
   GonkMediaDataDecoder(GonkDecoderManager* aDecoderManager,
                        FlushableMediaTaskQueue* aTaskQueue,
                        MediaDataDecoderCallback* aCallback);
 
   ~GonkMediaDataDecoder();
 
   virtual nsresult Init() override;
 
-  virtual nsresult Input(mp4_demuxer::MP4Sample* aSample);
+  virtual nsresult Input(MediaRawData* aSample);
 
   virtual nsresult Flush() override;
 
   virtual nsresult Drain() override;
 
   virtual nsresult Shutdown() override;
 
   virtual bool IsWaitingMediaResources() override;
@@ -107,17 +107,17 @@ public:
   virtual void ReleaseMediaResources() override;
 
 private:
 
   // Called on the task queue. Inserts the sample into the decoder, and
   // extracts output if available, if aSample is null, it means there is
   // no data from source, it will notify the decoder EOS and flush all the
   // decoded frames.
-  void ProcessDecode(mp4_demuxer::MP4Sample* aSample);
+  void ProcessDecode(MediaRawData* aSample);
 
   // Called on the task queue. Extracts output if available, and delivers
   // it to the reader. Called after ProcessDecode() and ProcessDrain().
   void ProcessOutput();
 
   // Called on the task queue. Orders the Gonk to drain, and then extracts
   // all available output.
   void ProcessDrain();
--- a/dom/media/fmp4/gonk/GonkVideoDecoderManager.cpp
+++ b/dom/media/fmp4/gonk/GonkVideoDecoderManager.cpp
@@ -433,27 +433,27 @@ GonkVideoDecoderManager::Output(int64_t 
 void GonkVideoDecoderManager::ReleaseVideoBuffer() {
   if (mVideoBuffer) {
     mDecoder->ReleaseMediaBuffer(mVideoBuffer);
     mVideoBuffer = nullptr;
   }
 }
 
 status_t
-GonkVideoDecoderManager::SendSampleToOMX(mp4_demuxer::MP4Sample* aSample)
+GonkVideoDecoderManager::SendSampleToOMX(MediaRawData* aSample)
 {
-  // An empty MP4Sample is going to notify EOS to decoder. It doesn't need
+  // An empty MediaRawData is going to notify EOS to decoder. It doesn't need
   // to keep PTS and duration.
-  if (aSample->data && aSample->duration && aSample->composition_timestamp) {
-    QueueFrameTimeIn(aSample->composition_timestamp, aSample->duration);
+  if (aSample->mData && aSample->mDuration && aSample->mTime) {
+    QueueFrameTimeIn(aSample->mTime, aSample->mDuration);
   }
 
-  return mDecoder->Input(reinterpret_cast<const uint8_t*>(aSample->data),
-                         aSample->size,
-                         aSample->composition_timestamp,
+  return mDecoder->Input(reinterpret_cast<const uint8_t*>(aSample->mData),
+                         aSample->mSize,
+                         aSample->mTime,
                          0);
 }
 
 void
 GonkVideoDecoderManager::ClearQueueFrameTime()
 {
   MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
   mFrameTimeInfo.Clear();
--- a/dom/media/fmp4/gonk/GonkVideoDecoderManager.h
+++ b/dom/media/fmp4/gonk/GonkVideoDecoderManager.h
@@ -53,17 +53,17 @@ public:
 
   virtual void AllocateMediaResources();
 
   virtual void ReleaseMediaResources();
 
   static void RecycleCallback(TextureClient* aClient, void* aClosure);
 
 protected:
-  virtual android::status_t SendSampleToOMX(mp4_demuxer::MP4Sample* aSample) override;
+  virtual android::status_t SendSampleToOMX(MediaRawData* aSample) override;
 
 private:
   struct FrameInfo
   {
     int32_t mWidth = 0;
     int32_t mHeight = 0;
     int32_t mStride = 0;
     int32_t mSliceHeight = 0;
--- a/dom/media/fmp4/wmf/WMFAudioMFTManager.cpp
+++ b/dom/media/fmp4/wmf/WMFAudioMFTManager.cpp
@@ -172,21 +172,21 @@ WMFAudioMFTManager::Init()
   NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
 
   mDecoder = decoder;
 
   return decoder.forget();
 }
 
 HRESULT
-WMFAudioMFTManager::Input(mp4_demuxer::MP4Sample* aSample)
+WMFAudioMFTManager::Input(MediaRawData* aSample)
 {
-  const uint8_t* data = reinterpret_cast<const uint8_t*>(aSample->data);
-  uint32_t length = aSample->size;
-  return mDecoder->Input(data, length, aSample->composition_timestamp);
+  return mDecoder->Input(aSample->mData,
+                         uint32_t(aSample->mSize),
+                         aSample->mTime);
 }
 
 HRESULT
 WMFAudioMFTManager::UpdateOutputType()
 {
   HRESULT hr;
 
   RefPtr<IMFMediaType> type;
--- a/dom/media/fmp4/wmf/WMFAudioMFTManager.h
+++ b/dom/media/fmp4/wmf/WMFAudioMFTManager.h
@@ -17,17 +17,17 @@ namespace mozilla {
 
 class WMFAudioMFTManager : public MFTManager {
 public:
   WMFAudioMFTManager(const mp4_demuxer::AudioDecoderConfig& aConfig);
   ~WMFAudioMFTManager();
 
   virtual TemporaryRef<MFTDecoder> Init() override;
 
-  virtual HRESULT Input(mp4_demuxer::MP4Sample* aSample) override;
+  virtual HRESULT Input(MediaRawData* aSample) override;
 
   // Note WMF's AAC decoder sometimes output negatively timestamped samples,
   // presumably they're the preroll samples, and we strip them. We may return
   // a null aOutput in this case.
   virtual HRESULT Output(int64_t aStreamOffset,
                          nsRefPtr<MediaData>& aOutput) override;
 
   virtual void Shutdown() override;
--- a/dom/media/fmp4/wmf/WMFMediaDataDecoder.cpp
+++ b/dom/media/fmp4/wmf/WMFMediaDataDecoder.cpp
@@ -70,37 +70,37 @@ void
 WMFMediaDataDecoder::ProcessReleaseDecoder()
 {
   mMFTManager->Shutdown();
   mDecoder = nullptr;
 }
 
 // Inserts data into the decoder's pipeline.
 nsresult
-WMFMediaDataDecoder::Input(mp4_demuxer::MP4Sample* aSample)
+WMFMediaDataDecoder::Input(MediaRawData* aSample)
 {
   mTaskQueue->Dispatch(
-    NS_NewRunnableMethodWithArg<nsAutoPtr<mp4_demuxer::MP4Sample>>(
+    NS_NewRunnableMethodWithArg<nsRefPtr<MediaRawData>>(
       this,
       &WMFMediaDataDecoder::ProcessDecode,
-      nsAutoPtr<mp4_demuxer::MP4Sample>(aSample)));
+      nsRefPtr<MediaRawData>(aSample)));
   return NS_OK;
 }
 
 void
-WMFMediaDataDecoder::ProcessDecode(mp4_demuxer::MP4Sample* aSample)
+WMFMediaDataDecoder::ProcessDecode(MediaRawData* aSample)
 {
   HRESULT hr = mMFTManager->Input(aSample);
   if (FAILED(hr)) {
     NS_WARNING("MFTManager rejected sample");
     mCallback->Error();
     return;
   }
 
-  mLastStreamOffset = aSample->byte_offset;
+  mLastStreamOffset = aSample->mOffset;
 
   ProcessOutput();
 }
 
 void
 WMFMediaDataDecoder::ProcessOutput()
 {
   nsRefPtr<MediaData> output;
--- a/dom/media/fmp4/wmf/WMFMediaDataDecoder.h
+++ b/dom/media/fmp4/wmf/WMFMediaDataDecoder.h
@@ -8,37 +8,33 @@
 #define WMFMediaDataDecoder_h_
 
 
 #include "WMF.h"
 #include "MP4Reader.h"
 #include "MFTDecoder.h"
 #include "mozilla/RefPtr.h"
 
-namespace mp4_demuxer {
-class MP4Sample;
-}
-
 namespace mozilla {
 
 // Encapsulates the initialization of the MFTDecoder appropriate for decoding
 // a given stream, and the process of converting the IMFSample produced
 // by the MFT into a MediaData object.
 class MFTManager {
 public:
   virtual ~MFTManager() {}
 
   // Creates an initializs the MFTDecoder.
   // Returns nullptr on failure.
   virtual TemporaryRef<MFTDecoder> Init() = 0;
 
   // Submit a compressed sample for decoding.
   // This should forward to the MFTDecoder after performing
   // any required sample formatting.
-  virtual HRESULT Input(mp4_demuxer::MP4Sample* aSample) = 0;
+  virtual HRESULT Input(MediaRawData* aSample) = 0;
 
   // Produces decoded output, if possible. Blocks until output can be produced,
   // or until no more is able to be produced.
   // Returns S_OK on success, or MF_E_TRANSFORM_NEED_MORE_INPUT if there's not
   // enough data to produce more output. If this returns a failure code other
   // than MF_E_TRANSFORM_NEED_MORE_INPUT, an error will be reported to the
   // MP4Reader.
   virtual HRESULT Output(int64_t aStreamOffset,
@@ -60,17 +56,17 @@ class WMFMediaDataDecoder : public Media
 public:
   WMFMediaDataDecoder(MFTManager* aOutputSource,
                       FlushableMediaTaskQueue* aAudioTaskQueue,
                       MediaDataDecoderCallback* aCallback);
   ~WMFMediaDataDecoder();
 
   virtual nsresult Init() override;
 
-  virtual nsresult Input(mp4_demuxer::MP4Sample* aSample);
+  virtual nsresult Input(MediaRawData* aSample);
 
   virtual nsresult Flush() override;
 
   virtual nsresult Drain() override;
 
   virtual nsresult Shutdown() override;
 
   virtual bool IsWaitingMediaResources() { return false; };
@@ -78,17 +74,17 @@ public:
   virtual void AllocateMediaResources() override;
   virtual void ReleaseMediaResources() override;
   virtual bool IsHardwareAccelerated() const override;
 
 private:
 
   // Called on the task queue. Inserts the sample into the decoder, and
   // extracts output if available.
-  void ProcessDecode(mp4_demuxer::MP4Sample* aSample);
+  void ProcessDecode(MediaRawData* aSample);
 
   // Called on the task queue. Extracts output if available, and delivers
   // it to the reader. Called after ProcessDecode() and ProcessDrain().
   void ProcessOutput();
 
   // Called on the task queue. Orders the MFT to drain, and then extracts
   // all available output.
   void ProcessDrain();
--- a/dom/media/fmp4/wmf/WMFVideoMFTManager.cpp
+++ b/dom/media/fmp4/wmf/WMFVideoMFTManager.cpp
@@ -230,26 +230,26 @@ WMFVideoMFTManager::Init()
   mVideoWidth = 0;
   mVideoHeight = 0;
   mPictureRegion.SetEmpty();
 
   return decoder.forget();
 }
 
 HRESULT
-WMFVideoMFTManager::Input(mp4_demuxer::MP4Sample* aSample)
+WMFVideoMFTManager::Input(MediaRawData* aSample)
 {
   if (!mDecoder) {
     // This can happen during shutdown.
     return E_FAIL;
   }
   // Forward sample data to the decoder.
-  const uint8_t* data = reinterpret_cast<const uint8_t*>(aSample->data);
-  uint32_t length = aSample->size;
-  return mDecoder->Input(data, length, aSample->composition_timestamp);
+  return mDecoder->Input(aSample->mData,
+                         uint32_t(aSample->mSize),
+                         aSample->mTime);
 }
 
 HRESULT
 WMFVideoMFTManager::ConfigureVideoFrameGeometry()
 {
   RefPtr<IMFMediaType> mediaType;
   HRESULT hr = mDecoder->GetOutputMediaType(mediaType);
   NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
--- a/dom/media/fmp4/wmf/WMFVideoMFTManager.h
+++ b/dom/media/fmp4/wmf/WMFVideoMFTManager.h
@@ -23,17 +23,17 @@ public:
   WMFVideoMFTManager(const mp4_demuxer::VideoDecoderConfig& aConfig,
                      mozilla::layers::LayersBackend aLayersBackend,
                      mozilla::layers::ImageContainer* aImageContainer,
                      bool aDXVAEnabled);
   ~WMFVideoMFTManager();
 
   virtual TemporaryRef<MFTDecoder> Init() override;
 
-  virtual HRESULT Input(mp4_demuxer::MP4Sample* aSample) override;
+  virtual HRESULT Input(MediaRawData* aSample) override;
 
   virtual HRESULT Output(int64_t aStreamOffset,
                          nsRefPtr<MediaData>& aOutput) override;
 
   virtual void Shutdown() override;
 
   virtual bool IsHardwareAccelerated() const override;
 
--- a/dom/media/fmp4/wrappers/H264Converter.cpp
+++ b/dom/media/fmp4/wrappers/H264Converter.cpp
@@ -47,17 +47,17 @@ H264Converter::Init()
 {
   if (mDecoder) {
     return mDecoder->Init();
   }
   return mLastError;
 }
 
 nsresult
-H264Converter::Input(mp4_demuxer::MP4Sample* aSample)
+H264Converter::Input(MediaRawData* aSample)
 {
   if (!mNeedAVCC) {
     if (!mp4_demuxer::AnnexB::ConvertSampleToAnnexB(aSample)) {
       return NS_ERROR_FAILURE;
     }
   } else {
     if (!mp4_demuxer::AnnexB::ConvertSampleToAVCC(aSample)) {
       return NS_ERROR_FAILURE;
@@ -66,25 +66,25 @@ H264Converter::Input(mp4_demuxer::MP4Sam
   nsresult rv;
   if (!mDecoder) {
     // It is not possible to create an AVCC H264 decoder without SPS.
     // As such, creation will fail if the extra_data just extracted doesn't
     // contain a SPS.
     rv = CreateDecoderAndInit(aSample);
     if (rv == NS_ERROR_NOT_INITIALIZED) {
       // We are missing the required SPS to create the decoder.
-      // Ignore for the time being, the MP4Sample will be dropped.
+      // Ignore for the time being, the MediaRawData will be dropped.
       return NS_OK;
     }
   } else {
     rv = CheckForSPSChange(aSample);
   }
   NS_ENSURE_SUCCESS(rv, rv);
 
-  aSample->extra_data = mCurrentConfig.extra_data;
+  aSample->mExtraData = mCurrentConfig.extra_data;
 
   return mDecoder->Input(aSample);
 }
 
 nsresult
 H264Converter::Flush()
 {
   if (mDecoder) {
@@ -182,34 +182,34 @@ H264Converter::CreateDecoder()
   if (!mDecoder) {
     mLastError = NS_ERROR_FAILURE;
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
 
 nsresult
-H264Converter::CreateDecoderAndInit(mp4_demuxer::MP4Sample* aSample)
+H264Converter::CreateDecoderAndInit(MediaRawData* aSample)
 {
-  nsRefPtr<mp4_demuxer::ByteBuffer> extra_data =
+  nsRefPtr<DataBuffer> extra_data =
     mp4_demuxer::AnnexB::ExtractExtraData(aSample);
   if (!mp4_demuxer::AnnexB::HasSPS(extra_data)) {
     return NS_ERROR_NOT_INITIALIZED;
   }
   UpdateConfigFromExtraData(extra_data);
 
   nsresult rv = CreateDecoder();
   NS_ENSURE_SUCCESS(rv, rv);
   return Init();
 }
 
 nsresult
-H264Converter::CheckForSPSChange(mp4_demuxer::MP4Sample* aSample)
+H264Converter::CheckForSPSChange(MediaRawData* aSample)
 {
-  nsRefPtr<mp4_demuxer::ByteBuffer> extra_data =
+  nsRefPtr<DataBuffer> extra_data =
     mp4_demuxer::AnnexB::ExtractExtraData(aSample);
   if (!mp4_demuxer::AnnexB::HasSPS(extra_data) ||
       mp4_demuxer::AnnexB::CompareExtraData(extra_data,
                                             mCurrentConfig.extra_data)) {
         return NS_OK;
       }
   if (!mNeedAVCC) {
     UpdateConfigFromExtraData(extra_data);
@@ -219,17 +219,17 @@ H264Converter::CheckForSPSChange(mp4_dem
   // The SPS has changed, signal to flush the current decoder and create a
   // new one.
   mDecoder->Flush();
   ReleaseMediaResources();
   return CreateDecoderAndInit(aSample);
 }
 
 void
-H264Converter::UpdateConfigFromExtraData(mp4_demuxer::ByteBuffer* aExtraData)
+H264Converter::UpdateConfigFromExtraData(DataBuffer* aExtraData)
 {
   mp4_demuxer::SPSData spsdata;
   if (mp4_demuxer::H264::DecodeSPSFromExtraData(aExtraData, spsdata) &&
       spsdata.pic_width > 0 && spsdata.pic_height > 0) {
     mp4_demuxer::H264::EnsureSPSIsSane(spsdata);
     mCurrentConfig.image_width = spsdata.pic_width;
     mCurrentConfig.image_height = spsdata.pic_height;
     mCurrentConfig.display_width = spsdata.display_width;
--- a/dom/media/fmp4/wrappers/H264Converter.h
+++ b/dom/media/fmp4/wrappers/H264Converter.h
@@ -25,17 +25,17 @@ public:
                 const mp4_demuxer::VideoDecoderConfig& aConfig,
                 layers::LayersBackend aLayersBackend,
                 layers::ImageContainer* aImageContainer,
                 FlushableMediaTaskQueue* aVideoTaskQueue,
                 MediaDataDecoderCallback* aCallback);
   virtual ~H264Converter();
 
   virtual nsresult Init() override;
-  virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) override;
+  virtual nsresult Input(MediaRawData* aSample) override;
   virtual nsresult Flush() override;
   virtual nsresult Drain() override;
   virtual nsresult Shutdown() override;
   virtual bool IsWaitingMediaResources() override;
   virtual bool IsDormantNeeded() override;
   virtual void AllocateMediaResources() override;
   virtual void ReleaseMediaResources() override;
   virtual bool IsHardwareAccelerated() const override;
@@ -43,19 +43,19 @@ public:
   // Return true if mimetype is H.264.
   static bool IsH264(const mp4_demuxer::TrackConfig& aConfig);
 
 private:
   // Will create the required MediaDataDecoder if need AVCC and we have a SPS NAL.
   // Returns NS_ERROR_FAILURE if error is permanent and can't be recovered and
   // will set mError accordingly.
   nsresult CreateDecoder();
-  nsresult CreateDecoderAndInit(mp4_demuxer::MP4Sample* aSample);
-  nsresult CheckForSPSChange(mp4_demuxer::MP4Sample* aSample);
-  void UpdateConfigFromExtraData(mp4_demuxer::ByteBuffer* aExtraData);
+  nsresult CreateDecoderAndInit(MediaRawData* aSample);
+  nsresult CheckForSPSChange(MediaRawData* aSample);
+  void UpdateConfigFromExtraData(DataBuffer* aExtraData);
 
   nsRefPtr<PlatformDecoderModule> mPDM;
   mp4_demuxer::VideoDecoderConfig mCurrentConfig;
   layers::LayersBackend mLayersBackend;
   nsRefPtr<layers::ImageContainer> mImageContainer;
   nsRefPtr<FlushableMediaTaskQueue> mVideoTaskQueue;
   MediaDataDecoderCallback* mCallback;
   nsRefPtr<MediaDataDecoder> mDecoder;
--- a/dom/media/gmp/GMPAudioHost.cpp
+++ b/dom/media/gmp/GMPAudioHost.cpp
@@ -2,16 +2,17 @@
 /* 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 "GMPAudioHost.h"
 #include "gmp-audio-samples.h"
 #include "gmp-errors.h"
 #include "GMPEncryptedBufferDataImpl.h"
+#include "MediaData.h"
 
 namespace mozilla {
 namespace gmp {
 
 GMPAudioSamplesImpl::GMPAudioSamplesImpl(GMPAudioFormat aFormat)
   : mFormat(aFormat)
   , mTimeStamp(0)
   , mChannels(0)
@@ -26,27 +27,27 @@ GMPAudioSamplesImpl::GMPAudioSamplesImpl
   , mChannels(aData.mChannelCount())
   , mRate(aData.mSamplesPerSecond())
 {
   if (aData.mDecryptionData().mKeyId().Length() > 0) {
     mCrypto = new GMPEncryptedBufferDataImpl(aData.mDecryptionData());
   }
 }
 
-GMPAudioSamplesImpl::GMPAudioSamplesImpl(mp4_demuxer::MP4Sample* aSample,
+GMPAudioSamplesImpl::GMPAudioSamplesImpl(MediaRawData* aSample,
                                          uint32_t aChannels,
                                          uint32_t aRate)
  : mFormat(kGMPAudioEncodedSamples)
- , mTimeStamp(aSample->composition_timestamp)
+ , mTimeStamp(aSample->mTime)
  , mChannels(aChannels)
  , mRate(aRate)
 {
-  mBuffer.AppendElements(aSample->data, aSample->size);
-  if (aSample->crypto.valid) {
-    mCrypto = new GMPEncryptedBufferDataImpl(aSample->crypto);
+  mBuffer.AppendElements(aSample->mData, aSample->mSize);
+  if (aSample->mCrypto.mValid) {
+    mCrypto = new GMPEncryptedBufferDataImpl(aSample->mCrypto);
   }
 }
 
 GMPAudioSamplesImpl::~GMPAudioSamplesImpl()
 {
 }
 
 GMPAudioFormat
@@ -100,19 +101,19 @@ GMPAudioSamplesImpl::Buffer()
 
 const GMPEncryptedBufferMetadata*
 GMPAudioSamplesImpl::GetDecryptionData() const
 {
   return mCrypto;
 }
 
 void
-GMPAudioSamplesImpl::InitCrypto(const mp4_demuxer::CryptoSample& aCrypto)
+GMPAudioSamplesImpl::InitCrypto(const CryptoSample& aCrypto)
 {
-  if (!aCrypto.valid) {
+  if (!aCrypto.mValid) {
     return;
   }
   mCrypto = new GMPEncryptedBufferDataImpl(aCrypto);
 }
 
 void
 GMPAudioSamplesImpl::RelinquishData(GMPAudioEncodedSampleData& aData)
 {
--- a/dom/media/gmp/GMPAudioHost.h
+++ b/dom/media/gmp/GMPAudioHost.h
@@ -5,44 +5,46 @@
 
 #ifndef GMPAudioHost_h_
 #define GMPAudioHost_h_
 
 #include "gmp-audio-host.h"
 #include "gmp-audio-samples.h"
 #include "nsTArray.h"
 #include "gmp-decryption.h"
-#include "mp4_demuxer/DecoderData.h"
 #include "nsAutoPtr.h"
 #include "GMPEncryptedBufferDataImpl.h"
 #include "mozilla/gmp/GMPTypes.h"
 
 namespace mozilla {
+class CryptoSample;
+class MediaRawData;
+
 namespace gmp {
 
 class GMPAudioSamplesImpl : public GMPAudioSamples {
 public:
   explicit GMPAudioSamplesImpl(GMPAudioFormat aFormat);
   explicit GMPAudioSamplesImpl(const GMPAudioEncodedSampleData& aData);
-  GMPAudioSamplesImpl(mp4_demuxer::MP4Sample* aSample,
+  GMPAudioSamplesImpl(MediaRawData* aSample,
                       uint32_t aChannels,