Merge mozilla-central to fx-team
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Wed, 30 Jul 2014 14:22:07 +0200
changeset 196819 a2b0f17e7dbaf54772989a964a36d0d5b3c05675
parent 196818 d3d2b622400feda2ac07489f80605c926298ad3f (current diff)
parent 196784 08c23f12a43e8a388c3f0ed28725007dfd948930 (diff)
child 196820 78df4aa16b2114ae72c47af0b8fbaec60de1b708
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
milestone34.0a1
Merge mozilla-central to fx-team
js/src/tests/js1_5/Regress/regress-328897.js
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="0d616942c300d9fb142483210f1dda9096c9a9fc">
     <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="c527178b78a5bc85b76f89d6ba7f0bb464963b79"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="25e998814ba89f30fe44cd2fdfbb44d160a04641"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d61daef8fca7d6f335f659a8967bad423770e634"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="227354333a185180b85471f2cc6abfb029e44718"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="71f5a35e3bc1801847413cff1f14fc3b5cd991ca"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="3aa6abd313f965a84aa86c6b213dc154e4875139">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="c527178b78a5bc85b76f89d6ba7f0bb464963b79"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="25e998814ba89f30fe44cd2fdfbb44d160a04641"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d61daef8fca7d6f335f659a8967bad423770e634"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="71f5a35e3bc1801847413cff1f14fc3b5cd991ca"/>
   <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"/>
@@ -126,12 +126,12 @@
   <!-- Emulator specific things -->
   <project name="android-development" path="development" remote="b2g" revision="dab55669da8f48b6e57df95d5af9f16b4a87b0b1"/>
   <project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="3a9a17613cc685aa232432566ad6cc607eab4ec1"/>
   <project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="197cd9492b9fadaa915c5daf36ff557f8f4a8d1c"/>
   <project name="platform/external/libnfc-nci" path="external/libnfc-nci" revision="7d33aaf740bbf6c7c6e9c34a92b371eda311b66b"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="683623c76338dccd65e698bfb5c4cfee8808d799"/>
   <project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="0e56e450367cd802241b27164a2979188242b95f"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="9f28c4faea3b2f01db227b2467b08aeba96d9bec"/>
-  <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="2cd3c7277621902b3749797eaaab618c4c80a58b"/>
+  <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="1adbb04024c6181f1089f3a12ee46f26663ea1db"/>
   <project name="android-sdk" path="sdk" remote="b2g" revision="8b1365af38c9a653df97349ee53a3f5d64fd590a"/>
   <project name="darwinstreamingserver" path="system/darwinstreamingserver" remote="b2g" revision="cf85968c7f85e0ec36e72c87ceb4837a943b8af6"/>
 </manifest>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="999e945b85c578c503ad445c2285940f16aacdae">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="c527178b78a5bc85b76f89d6ba7f0bb464963b79"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="25e998814ba89f30fe44cd2fdfbb44d160a04641"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d61daef8fca7d6f335f659a8967bad423770e634"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="71f5a35e3bc1801847413cff1f14fc3b5cd991ca"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="0d616942c300d9fb142483210f1dda9096c9a9fc">
     <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="c527178b78a5bc85b76f89d6ba7f0bb464963b79"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="25e998814ba89f30fe44cd2fdfbb44d160a04641"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d61daef8fca7d6f335f659a8967bad423770e634"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="227354333a185180b85471f2cc6abfb029e44718"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="71f5a35e3bc1801847413cff1f14fc3b5cd991ca"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/flame/sources.xml
+++ b/b2g/config/flame/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="3aa6abd313f965a84aa86c6b213dc154e4875139">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="c527178b78a5bc85b76f89d6ba7f0bb464963b79"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="25e998814ba89f30fe44cd2fdfbb44d160a04641"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d61daef8fca7d6f335f659a8967bad423770e634"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="71f5a35e3bc1801847413cff1f14fc3b5cd991ca"/>
   <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"/>
@@ -115,17 +115,17 @@
   <project name="platform/system/security" path="system/security" revision="360f51f7af191316cd739f229db1c5f7233be063"/>
   <project name="platform/system/vold" path="system/vold" revision="153df4d067a4149c7d78f1c92fed2ce2bd6a272e"/>
   <!--original fetch url was git://github.com/t2m-foxfone/-->
   <remote fetch="https://git.mozilla.org/external/t2m-foxfone" name="t2m"/>
   <default remote="caf" revision="jb_3.2" sync-j="4"/>
   <!-- Flame specific things -->
   <project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="e8a318f7690092e639ba88891606f4183e846d3f"/>
   <project name="device/qcom/common" path="device/qcom/common" revision="34ed8345250bb97262d70a052217a92e83444ede"/>
-  <project name="device-flame" path="device/t2m/flame" remote="b2g" revision="a927b19531c293dca1e80e081e0ab5715f4d1bfe"/>
+  <project name="device-flame" path="device/t2m/flame" remote="b2g" revision="d7e5ed0a081a39419932b8b9fdefc9b2d903850d"/>
   <project name="codeaurora_kernel_msm" path="kernel" remote="b2g" revision="30d40a5636ff28a554f1d8e9d975bfd04c2463d3"/>
   <project name="kernel_lk" path="bootable/bootloader/lk" remote="b2g" revision="2b1d8b5b7a760230f4c94c02e733e3929f44253a"/>
   <project name="platform_bootable_recovery" path="bootable/recovery" remote="b2g" revision="e81502511cda303c803e63f049574634bc96f9f2"/>
   <project name="platform/external/bluetooth/bluedroid" path="external/bluetooth/bluedroid" revision="81c4a859d75d413ad688067829d21b7ba9205f81"/>
   <project name="platform/external/bluetooth/bluez" path="external/bluetooth/bluez" revision="f0689ac1914cdbc59e53bdc9edd9013dc157c299"/>
   <project name="platform/external/bluetooth/glib" path="external/bluetooth/glib" revision="dd925f76e4f149c3d5571b80e12f7e24bbe89c59"/>
   <project name="platform/external/dbus" path="external/dbus" revision="ea87119c843116340f5df1d94eaf8275e1055ae8"/>
   <project name="platform_external_libnfc-nci" path="external/libnfc-nci" remote="t2m" revision="4186bdecb4dae911b39a8202252cc2310d91b0be"/>
@@ -138,13 +138,13 @@
   <project name="platform/hardware/qcom/camera" path="hardware/qcom/camera" revision="5e110615212302c5d798a3c223dcee458817651c"/>
   <project name="platform/hardware/qcom/display" path="hardware/qcom/display" revision="fa9ffd47948eb24466de227e48fe9c4a7c5e7711"/>
   <project name="platform/hardware/qcom/gps" path="hardware/qcom/gps" revision="5dc48bd46f9589653f8bf297be5d73676f2e2867"/>
   <project name="platform/hardware/qcom/media" path="hardware/qcom/media" revision="8a0d0b0d9889ef99c4c6317c810db4c09295f15a"/>
   <project name="platform/hardware/qcom/wlan" path="hardware/qcom/wlan" revision="2208fa3537ace873b8f9ec2355055761c79dfd5f"/>
   <project name="platform/hardware/ril" path="hardware/ril" revision="c4e2ac95907a5519a0e09f01a0d8e27fec101af0"/>
   <project name="platform/system/bluetooth" path="system/bluetooth" revision="e1eb226fa3ad3874ea7b63c56a9dc7012d7ff3c2"/>
   <project name="platform/system/core" path="system/core" revision="bbf7212289fc8311e43f9d11e10788e310d36a08"/>
-  <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="2cd3c7277621902b3749797eaaab618c4c80a58b"/>
+  <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="1adbb04024c6181f1089f3a12ee46f26663ea1db"/>
   <project name="platform/system/qcom" path="system/qcom" revision="1cdab258b15258b7f9657da70e6f06ebd5a2fc25"/>
   <project name="platform/vendor/qcom/msm8610" path="device/qcom/msm8610" revision="4ae5df252123591d5b941191790e7abed1bce5a4"/>
   <project name="platform/vendor/qcom-opensource/wlan/prima" path="vendor/qcom/opensource/wlan/prima" revision="ce18b47b4a4f93a581d672bbd5cb6d12fe796ca9"/>
 </manifest>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
         "git_revision": "", 
         "remote": "", 
         "branch": ""
     }, 
-    "revision": "4024e28dbc44b11d3297378484c2474dcee425fa", 
+    "revision": "549d40d59aed92eb33c912e0a8c7ea0faf0e7a18", 
     "repo_path": "/integration/gaia-central"
 }
--- a/b2g/config/hamachi/sources.xml
+++ b/b2g/config/hamachi/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="0d616942c300d9fb142483210f1dda9096c9a9fc">
     <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="c527178b78a5bc85b76f89d6ba7f0bb464963b79"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="25e998814ba89f30fe44cd2fdfbb44d160a04641"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d61daef8fca7d6f335f659a8967bad423770e634"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="71f5a35e3bc1801847413cff1f14fc3b5cd991ca"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/helix/sources.xml
+++ b/b2g/config/helix/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="0d616942c300d9fb142483210f1dda9096c9a9fc">
     <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="c527178b78a5bc85b76f89d6ba7f0bb464963b79"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="25e998814ba89f30fe44cd2fdfbb44d160a04641"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d61daef8fca7d6f335f659a8967bad423770e634"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="3aa6abd313f965a84aa86c6b213dc154e4875139">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="c527178b78a5bc85b76f89d6ba7f0bb464963b79"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="25e998814ba89f30fe44cd2fdfbb44d160a04641"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d61daef8fca7d6f335f659a8967bad423770e634"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="71f5a35e3bc1801847413cff1f14fc3b5cd991ca"/>
   <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"/>
@@ -122,17 +122,17 @@
   <project name="platform/system/netd" path="system/netd" revision="56112dd7b811301b718d0643a82fd5cac9522073"/>
   <project name="platform/system/security" path="system/security" revision="f48ff68fedbcdc12b570b7699745abb6e7574907"/>
   <project name="platform/system/vold" path="system/vold" revision="8de05d4a52b5a91e7336e6baa4592f945a6ddbea"/>
   <default remote="caf" revision="refs/tags/android-4.3_r2.1" sync-j="4"/>
   <!-- Nexus 4 specific things -->
   <project name="device-mako" path="device/lge/mako" remote="b2g" revision="78d17f0c117f0c66dd55ee8d5c5dde8ccc93ecba"/>
   <project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="3a9a17613cc685aa232432566ad6cc607eab4ec1"/>
   <project name="device/lge/mako-kernel" path="device/lge/mako-kernel" revision="d1729e53d71d711c8fde25eab8728ff2b9b4df0e"/>
-  <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="2cd3c7277621902b3749797eaaab618c4c80a58b"/>
+  <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="1adbb04024c6181f1089f3a12ee46f26663ea1db"/>
   <project name="platform/external/libnfc-nci" path="external/libnfc-nci" revision="7d33aaf740bbf6c7c6e9c34a92b371eda311b66b"/>
   <project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="0e56e450367cd802241b27164a2979188242b95f"/>
   <project name="platform/hardware/broadcom/wlan" path="hardware/broadcom/wlan" revision="0e1929fa3aa38bf9d40e9e953d619fab8164c82e"/>
   <project name="platform/hardware/qcom/audio" path="hardware/qcom/audio" revision="b0a528d839cfd9d170d092fe3743b5252b4243a6"/>
   <project name="platform/hardware/qcom/bt" path="hardware/qcom/bt" revision="380945eaa249a2dbdde0daa4c8adb8ca325edba6"/>
   <project name="platform/hardware/qcom/display" path="hardware/qcom/display" revision="6f3b0272cefaffeaed2a7d2bb8f633059f163ddc"/>
   <project name="platform/hardware/qcom/keymaster" path="hardware/qcom/keymaster" revision="16da8262c997a5a0d797885788a64a0771b26910"/>
   <project name="platform/hardware/qcom/media" path="hardware/qcom/media" revision="689b476ba3eb46c34b81343295fe144a0e81a18e"/>
--- a/b2g/config/wasabi/sources.xml
+++ b/b2g/config/wasabi/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="0d616942c300d9fb142483210f1dda9096c9a9fc">
     <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="c527178b78a5bc85b76f89d6ba7f0bb464963b79"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="25e998814ba89f30fe44cd2fdfbb44d160a04641"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d61daef8fca7d6f335f659a8967bad423770e634"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="71f5a35e3bc1801847413cff1f14fc3b5cd991ca"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -18,17 +18,17 @@ browser.jar:
 #ifdef MOZ_SERVICES_SYNC
   skin/classic/browser/aboutSyncTabs.css
 #endif
   skin/classic/browser/aboutTabCrashed.css
   skin/classic/browser/actionicon-tab.png
 * skin/classic/browser/browser.css
 * skin/classic/browser/browser-lightweightTheme.css
   skin/classic/browser/click-to-play-warning-stripes.png
-* skin/classic/browser/content-contextmenu.svg
+  skin/classic/browser/content-contextmenu.svg
 * skin/classic/browser/engineManager.css
   skin/classic/browser/fullscreen-darknoise.png
   skin/classic/browser/Geolocation-16.png
   skin/classic/browser/Geolocation-64.png
   skin/classic/browser/identity.png
   skin/classic/browser/identity-icons-generic.png
   skin/classic/browser/identity-icons-https.png
   skin/classic/browser/identity-icons-https-ev.png
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -18,17 +18,17 @@ browser.jar:
   skin/classic/browser/aboutSyncTabs.css
 #endif
   skin/classic/browser/aboutTabCrashed.css
   skin/classic/browser/actionicon-tab.png
   skin/classic/browser/actionicon-tab@2x.png
 * skin/classic/browser/browser.css                          (browser.css)
 * skin/classic/browser/browser-lightweightTheme.css
   skin/classic/browser/click-to-play-warning-stripes.png
-* skin/classic/browser/content-contextmenu.svg
+  skin/classic/browser/content-contextmenu.svg
 * skin/classic/browser/engineManager.css                    (engineManager.css)
   skin/classic/browser/fullscreen-darknoise.png
   skin/classic/browser/Geolocation-16.png
   skin/classic/browser/Geolocation-16@2x.png
   skin/classic/browser/Geolocation-64.png
   skin/classic/browser/Geolocation-64@2x.png
   skin/classic/browser/identity.png
   skin/classic/browser/identity@2x.png
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -20,17 +20,17 @@ browser.jar:
 #ifdef MOZ_SERVICES_SYNC
         skin/classic/browser/aboutSyncTabs.css
 #endif
         skin/classic/browser/aboutTabCrashed.css
         skin/classic/browser/actionicon-tab.png
 *       skin/classic/browser/browser.css
 *       skin/classic/browser/browser-lightweightTheme.css
         skin/classic/browser/click-to-play-warning-stripes.png
-*       skin/classic/browser/content-contextmenu.svg
+        skin/classic/browser/content-contextmenu.svg
 *       skin/classic/browser/engineManager.css
         skin/classic/browser/fullscreen-darknoise.png
         skin/classic/browser/Geolocation-16.png
         skin/classic/browser/Geolocation-64.png
         skin/classic/browser/Info.png
         skin/classic/browser/identity.png
         skin/classic/browser/identity-icons-generic.png
         skin/classic/browser/identity-icons-https.png
@@ -432,17 +432,17 @@ browser.jar:
         skin/classic/aero/browser/aboutSyncTabs.css
 #endif
         skin/classic/aero/browser/aboutTabCrashed.css
         skin/classic/aero/browser/aboutWelcomeBack.css               (../shared/aboutWelcomeBack.css)
         skin/classic/aero/browser/actionicon-tab.png
 *       skin/classic/aero/browser/browser.css                        (browser-aero.css)
 *       skin/classic/aero/browser/browser-lightweightTheme.css
         skin/classic/aero/browser/click-to-play-warning-stripes.png
-*       skin/classic/aero/browser/content-contextmenu.svg
+        skin/classic/aero/browser/content-contextmenu.svg
 *       skin/classic/aero/browser/engineManager.css
         skin/classic/aero/browser/fullscreen-darknoise.png
         skin/classic/aero/browser/Geolocation-16.png
         skin/classic/aero/browser/Geolocation-64.png
         skin/classic/aero/browser/Info.png                           (Info-aero.png)
         skin/classic/aero/browser/identity.png                       (identity-aero.png)
         skin/classic/aero/browser/identity-icons-generic.png
         skin/classic/aero/browser/identity-icons-https.png
--- a/build/macosx/universal/mozconfig.common
+++ b/build/macosx/universal/mozconfig.common
@@ -7,17 +7,17 @@ mk_add_options MOZ_UNIFY_BDATE=1
 mk_add_options MOZ_POSTFLIGHT_ALL+=build/macosx/universal/flight.mk
 
 DARWIN_VERSION=`uname -r`
 ac_add_app_options i386 --target=i386-apple-darwin$DARWIN_VERSION
 ac_add_app_options x86_64 --target=x86_64-apple-darwin$DARWIN_VERSION
 ac_add_app_options i386 --with-unify-dist=../x86_64/dist
 ac_add_app_options x86_64 --with-unify-dist=../i386/dist
 
-ac_add_options --with-macos-sdk=/Developer/SDKs/MacOSX10.6.sdk
+ac_add_options --with-macos-sdk=/Developer/SDKs/MacOSX10.7.sdk
 
 . $topsrcdir/build/macosx/mozconfig.common
 
 # $MOZ_BUILD_APP is only defined when sourced by configure.  That's not a
 # problem, because the variables it affects only need to be set for
 # configure.
 if test -n "$MOZ_BUILD_APP" ; then
 if test "$MOZ_BUILD_APP" = "i386" -o "$MOZ_BUILD_APP" = "x86_64"; then
--- a/build/mobile/remoteautomation.py
+++ b/build/mobile/remoteautomation.py
@@ -261,17 +261,17 @@ class RemoteAutomation(Automation):
                 return
 
             # We only keep the last (unfinished) line in the buffer
             self.logBuffer = lines[-1]
             del lines[-1]
             messages = []
             for line in lines:
                 # This passes the line to the logger (to be logged or buffered)
-                # and returns a list of structured messages (dict) or None, depending on the log
+                # and returns a list of structured messages (dict)
                 parsed_messages = self.messageLogger.write(line)
                 for message in parsed_messages:
                     if message['action'] == 'test_start':
                         self.lastTestSeen = message['test']
                 messages += parsed_messages
             return messages
 
         @property
--- a/build/mobile/robocop/FennecMochitestAssert.java
+++ b/build/mobile/robocop/FennecMochitestAssert.java
@@ -20,54 +20,63 @@ public class FennecMochitestAssert imple
     private boolean mLogStarted = false;
 
     // Used to write the test-start/test-end log lines
     private String mLogTestName = "";
 
     // Measure the time it takes to run test case
     private long mStartTime = 0;
 
-    public FennecMochitestAssert() {
-    }
+    // Structured logger
+    private StructuredLogger mLogger;
 
     /** Write information to a logfile and logcat */
     public void dumpLog(String message) {
-        FennecNativeDriver.log(FennecNativeDriver.LogLevel.INFO, message);
+        mLogger.info(message);
+    }
+
+    public void dumpLog(String message, Throwable t) {
+        mLogger.error(message + " - " + t.toString());
     }
 
     /** Write information to a logfile and logcat */
-    public void dumpLog(String message, Throwable t) {
-        FennecNativeDriver.log(FennecNativeDriver.LogLevel.INFO, message, t);
+    static class DumpLogCallback implements StructuredLogger.LoggerCallback {
+        public void call(String output) {
+            FennecNativeDriver.log(FennecNativeDriver.LogLevel.INFO, output);
+        }
+    }
+
+
+    public FennecMochitestAssert() {
+        mLogger = new StructuredLogger("robocop", new DumpLogCallback());
     }
 
     /** Set the filename used for dumpLog. */
     public void setLogFile(String filename) {
         FennecNativeDriver.setLogFile(filename);
 
         String message;
         if (!mLogStarted) {
-            dumpLog("SimpleTest START");
+            mLogger.info("SimpleTest START");
             mLogStarted = true;
         }
 
         if (mLogTestName != "") {
             long diff = SystemClock.uptimeMillis() - mStartTime;
-            message = "TEST-END | " + mLogTestName;
-            message += " | finished in " + diff + "ms";
-            dumpLog(message);
+            mLogger.testEnd(mLogTestName, "OK", "finished in " + diff + "ms");
             mLogTestName = "";
         }
     }
 
     public void setTestName(String testName) {
         String[] nameParts = testName.split("\\.");
         mLogTestName = nameParts[nameParts.length - 1];
         mStartTime = SystemClock.uptimeMillis();
 
-        dumpLog("TEST-START | " + mLogTestName);
+        mLogger.testStart(mLogTestName);
     }
 
     class testInfo {
         public boolean mResult;
         public String mName;
         public String mDiag;
         public boolean mTodo;
         public boolean mInfo;
@@ -76,72 +85,68 @@ public class FennecMochitestAssert imple
             mName = n;
             mDiag = d;
             mTodo = t;
             mInfo = i;
         }
 
     }
 
-    private void _logMochitestResult(testInfo test, String passString, String failString) {
+    /** Used to log a subtest's result.
+     * test represents the subtest (an assertion).
+     * passStatus and passExpected are the actual status and the expected status if the assertion is true.
+     * failStatus and failExpected are the actual status and the expected status otherwise.
+     */
+    private void _logMochitestResult(testInfo test, String passStatus, String passExpected, String failStatus, String failExpected) {
         boolean isError = true;
-        String resultString = failString;
         if (test.mResult || test.mTodo) {
             isError = false;
         }
         if (test.mResult)
         {
-            resultString = passString;
+            mLogger.testStatus(mLogTestName, test.mName, passStatus, passExpected, test.mDiag);
+        } else {
+            mLogger.testStatus(mLogTestName, test.mName, failStatus, failExpected, test.mDiag);
         }
-        String diag = test.mName;
-        if (test.mDiag != null) diag += " - " + test.mDiag;
-
-        String message = resultString + " | " + mLogTestName + " | " + diag;
-        dumpLog(message);
 
         if (test.mInfo) {
             // do not count TEST-INFO messages
         } else if (test.mTodo) {
             mTodo++;
         } else if (isError) {
             mFailed++;
         } else {
             mPassed++;
         }
         if (isError) {
+            String message = "TEST-UNEXPECTED-" + failStatus + " | " + mLogTestName + " | "
+                    + test.mName + " - " + test.mDiag;
             junit.framework.Assert.fail(message);
         }
     }
 
     public void endTest() {
         String message;
 
         if (mLogTestName != "") {
             long diff = SystemClock.uptimeMillis() - mStartTime;
-            message = "TEST-END | " + mLogTestName;
-            message += " | finished in " + diff + "ms";
-            dumpLog(message);
+            mLogger.testEnd(mLogTestName, "OK", "finished in " + diff + "ms");
             mLogTestName = "";
         }
 
-        message = "TEST-START | Shutdown";
-        dumpLog(message);
-        message = "Passed: " + Integer.toString(mPassed);
-        dumpLog(message);
-        message = "Failed: " + Integer.toString(mFailed);
-        dumpLog(message);
-        message = "Todo: " + Integer.toString(mTodo);
-        dumpLog(message);
-        message = "SimpleTest FINISHED";
-        dumpLog(message);
+        mLogger.info("TEST-START | Shutdown");
+        mLogger.info("Passed: " + Integer.toString(mPassed));
+        mLogger.info("Failed: " + Integer.toString(mFailed));
+        mLogger.info("Todo: " + Integer.toString(mTodo));
+        mLogger.info("SimpleTest FINISHED");
     }
 
     public void ok(boolean condition, String name, String diag) {
         testInfo test = new testInfo(condition, name, diag, false, false);
-        _logMochitestResult(test, "TEST-PASS", "TEST-UNEXPECTED-FAIL");
+        _logMochitestResult(test, "PASS", "PASS", "FAIL", "PASS");
         mTestList.add(test);
     }
 
     public void is(Object actual, Object expected, String name) {
         boolean pass = checkObjectsEqual(actual, expected);
         ok(pass, name, getEqualString(actual, expected, pass));
     }
 
@@ -188,17 +193,17 @@ public class FennecMochitestAssert imple
             return true;
         } else {
             return false;
         }
     }
 
     public void todo(boolean condition, String name, String diag) {
         testInfo test = new testInfo(condition, name, diag, true, false);
-        _logMochitestResult(test, "TEST-UNEXPECTED-PASS", "TEST-KNOWN-FAIL");
+        _logMochitestResult(test, "PASS", "FAIL", "FAIL", "FAIL");
         mTestList.add(test);
     }
 
     public void todo_is(Object actual, Object expected, String name) {
         boolean pass = checkObjectsEqual(actual, expected);
         todo(pass, name, getEqualString(actual, expected, pass));
     }
 
@@ -240,12 +245,11 @@ public class FennecMochitestAssert imple
     private String getNotEqualString(Object a, Object b, boolean pass) {
         if(pass) {
             return a + " should not equal " + b;
         }
         return "didn't expect " + a + ", but got it";
     }
 
     public void info(String name, String message) {
-        testInfo test = new testInfo(true, name, message, false, true);
-        _logMochitestResult(test, "TEST-INFO", "INFO FAILED?");
+        mLogger.info(name + " | " + message);
     }
 }
--- a/build/mobile/robocop/StructuredLogger.java
+++ b/build/mobile/robocop/StructuredLogger.java
@@ -39,16 +39,20 @@ public class StructuredLogger {
         mComponent = component;
         mCallback = callback;
     }
 
     public StructuredLogger(String name, String component) {
         this(name, component, new StandardLoggerCallback());
     }
 
+    public StructuredLogger(String name,  LoggerCallback callback) {
+        this(name, null, callback);
+    }
+
     public StructuredLogger(String name) {
         this(name, null, new StandardLoggerCallback());
     }
 
     public void suiteStart(List<String> tests, Map<String, Object> runInfo) {
         HashMap<String, Object> data = new HashMap<String, Object>();
         data.put("tests", tests);
         if (runInfo != null) {
--- a/config/system-headers
+++ b/config/system-headers
@@ -1258,17 +1258,16 @@ X11/Xfuncproto.h
 X11/X.h
 X11/XKBlib.h
 X11/Xlib.h
 X11/Xlibint.h
 X11/Xlocale.h
 X11/Xos.h
 X11/Xutil.h
 zmouse.h
-speex/speex_resampler.h
 soundtouch/SoundTouch.h
 #if MOZ_NATIVE_PNG==1
 png.h
 #endif
 #if MOZ_NATIVE_ZLIB==1
 zlib.h
 #endif
 #ifdef MOZ_ENABLE_STARTUP_NOTIFICATION
@@ -1320,16 +1319,17 @@ vpx/vpx_codec.h
 vpx/vpx_decoder.h
 vpx/vpx_encoder.h
 vpx/vp8cx.h
 vpx/vp8dx.h
 vpx_mem/vpx_mem.h
 vorbis/codec.h
 theora/theoradec.h
 tremor/ivorbiscodec.h
+speex/speex_resampler.h
 ogg/ogg.h
 ogg/os_types.h
 nestegg/nestegg.h
 cubeb/cubeb.h
 #endif
 gst/gst.h
 gst/app/gstappsink.h
 gst/app/gstappsrc.h
--- a/configure.in
+++ b/configure.in
@@ -3839,16 +3839,17 @@ MOZ_LOCALE_SWITCHER=
 MOZ_ANDROID_SEARCH_ACTIVITY=
 MOZ_ANDROID_MLS_STUMBLER=
 ACCESSIBILITY=1
 MOZ_TIME_MANAGER=
 MOZ_PAY=
 MOZ_AUDIO_CHANNEL_MANAGER=
 NSS_NO_LIBPKIX=
 MOZ_CONTENT_SANDBOX=
+MOZ_GMP_SANDBOX=
 JSGC_USE_EXACT_ROOTING=1
 JSGC_GENERATIONAL=
 
 case "$target_os" in
     mingw*)
         NS_ENABLE_TSF=1
         AC_DEFINE(NS_ENABLE_TSF)
         ;;
@@ -6401,16 +6402,38 @@ MOZ_ARG_ENABLE_BOOL(content-sandbox,
 
 if test -n "$MOZ_CONTENT_SANDBOX"; then
     AC_DEFINE(MOZ_CONTENT_SANDBOX)
 fi
 
 AC_SUBST(MOZ_CONTENT_SANDBOX)
 
 dnl ========================================================
+dnl = Gecko Media Plugin sandboxing
+dnl ========================================================
+case $OS_TARGET in
+WINNT)
+    MOZ_GMP_SANDBOX=1
+    ;;
+Linux)
+    case $CPU_ARCH in
+    x86_64|x86)
+        MOZ_GMP_SANDBOX=1
+        ;;
+    esac
+    ;;
+esac
+
+if test -n "$MOZ_GMP_SANDBOX"; then
+    AC_DEFINE(MOZ_GMP_SANDBOX)
+fi
+
+AC_SUBST(MOZ_GMP_SANDBOX)
+
+dnl ========================================================
 dnl =
 dnl = Module specific options
 dnl =
 dnl ========================================================
 MOZ_ARG_HEADER(Individual module options)
 
 dnl ========================================================
 dnl = Disable feed handling components
--- a/content/base/src/Element.cpp
+++ b/content/base/src/Element.cpp
@@ -1741,17 +1741,17 @@ Element::DispatchClickEvent(nsPresContex
   }
 
   return DispatchEvent(aPresContext, &event, aTarget, aFullDispatch, aStatus);
 }
 
 nsIFrame*
 Element::GetPrimaryFrame(mozFlushType aType)
 {
-  nsIDocument* doc = GetCurrentDoc();
+  nsIDocument* doc = GetComposedDoc();
   if (!doc) {
     return nullptr;
   }
 
   // Cause a flush, so we get up-to-date frame
   // information
   if (aType != Flush_None) {
     doc->FlushPendingNotifications(aType);
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -1511,17 +1511,17 @@ struct nsIDocument::FrameRequest
   bool operator<(int32_t aHandle) const {
     return mHandle < aHandle;
   }
 
   FrameRequestCallbackHolder mCallback;
   int32_t mHandle;
 };
 
-static already_AddRefed<mozilla::dom::NodeInfo> nullNodeInfo(nullptr);
+static already_AddRefed<mozilla::dom::NodeInfo> nullNodeInfo;
 
 // ==================================================================
 // =
 // ==================================================================
 nsIDocument::nsIDocument()
   : nsINode(nullNodeInfo),
     mCharacterSet(NS_LITERAL_CSTRING("ISO-8859-1")),
     mNodeInfoManager(nullptr),
--- a/content/base/src/nsScriptLoader.cpp
+++ b/content/base/src/nsScriptLoader.cpp
@@ -322,22 +322,29 @@ nsScriptLoader::StartLoad(nsScriptLoadRe
   nsCOMPtr<nsIChannel> channel;
   rv = NS_NewChannel(getter_AddRefs(channel),
                      aRequest->mURI, nullptr, loadGroup, prompter,
                      nsIRequest::LOAD_NORMAL | nsIChannel::LOAD_CLASSIFY_URI,
                      channelPolicy);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsIScriptElement *script = aRequest->mElement;
-  if (aScriptFromHead &&
-      !(script && (script->GetScriptAsync() || script->GetScriptDeferred()))) {
-    nsCOMPtr<nsIHttpChannelInternal>
-      internalHttpChannel(do_QueryInterface(channel));
-    if (internalHttpChannel)
+  nsCOMPtr<nsIHttpChannelInternal>
+    internalHttpChannel(do_QueryInterface(channel));
+
+  if (internalHttpChannel) {
+    if (aScriptFromHead &&
+        !(script && (script->GetScriptAsync() || script->GetScriptDeferred()))) {
+      // synchronous head scripts block lading of most other non js/css
+      // content such as images
       internalHttpChannel->SetLoadAsBlocking(true);
+    } else if (!(script && script->GetScriptDeferred())) {
+      // other scripts are neither blocked nor prioritized unless marked deferred
+      internalHttpChannel->SetLoadUnblocked(true);
+    }
   }
 
   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
   if (httpChannel) {
     // HTTP content negotation has little value in this context.
     httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
                                   NS_LITERAL_CSTRING("*/*"),
                                   false);
--- a/content/base/test/mochitest.ini
+++ b/content/base/test/mochitest.ini
@@ -645,8 +645,9 @@ skip-if = toolkit == 'android'
 disabled = Disabled for now. Mochitest isn't reliable enough for these.
 support-files = bug444546.sjs
 [test_bug503473.html]
 disabled = Disabled due to making the harness time out
 support-files = file_bug503473-frame.sjs
 [test_bug1011748.html]
 skip-if = buildapp == 'b2g' || e10s
 support-files = file_bug1011748_redirect.sjs file_bug1011748_OK.sjs
+[test_bug1025933.html]
--- a/content/base/test/script_bug602838.sjs
+++ b/content/base/test/script_bug602838.sjs
@@ -1,18 +1,37 @@
+function setOurState(data) {
+  x = { data: data, QueryInterface: function(iid) { return this } };
+  x.wrappedJSObject = x;
+  setObjectState("bug602838", x);
+}
+
+function getOurState() {
+  var data;
+  getObjectState("bug602838", function(x) {
+    // x can be null if no one has set any state yet
+    if (x) {
+      data = x.wrappedJSObject.data;
+    }
+  });
+  return data;
+}
+
 function handleRequest(request, response)
 {
   if (request.queryString) {
-    let blockedResponse = null;
-    getObjectState("bug602838", function(x) { blockedResponse = x.wrappedJSObject.r });
-    blockedResponse.finish();
-    setObjectState("bug602838", null);
+    let blockedResponse = getOurState();
+    if (typeof(blockedResponse) == "object") {
+      blockedResponse.finish();
+      setOurState(null);
+    } else {
+      setOurState("unblocked");
+    }
     return;
   }
   response.setHeader("Cache-Control", "no-cache", false);
   response.setHeader("Content-Type", "text/javascript", false);
   response.write("ok(asyncRan, 'Async script should have run first.'); firstRan = true;");
-  response.processAsync();
-
-  x = { r: response, QueryInterface: function(iid) { return this } };
-  x.wrappedJSObject = x;
-  setObjectState("bug602838", x);
+  if (getOurState() != "unblocked") {
+    response.processAsync();
+    setOurState(response);
+  }
 }
new file mode 100644
--- /dev/null
+++ b/content/base/test/test_bug1025933.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1025933
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1025933</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript">
+
+  /** Test for Bug 1025933 **/
+
+  SimpleTest.waitForExplicitFinish();
+
+  function test() {
+    var s = document.getElementById("host").createShadowRoot();
+    s.innerHTML = '<div style="width:100px;height:100px;background:red"></div>';
+    var el = s.firstElementChild;
+    is(el.clientWidth, 100);
+    is(el.clientHeight, 100);
+    SimpleTest.finish();
+  }
+
+  </script>
+</head>
+<body onload="test()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1025933">Mozilla Bug 1025933</a>
+<p id="display"></p>
+<div id="content">
+  <div id="host"></div>
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
--- a/content/html/content/src/HTMLMediaElement.cpp
+++ b/content/html/content/src/HTMLMediaElement.cpp
@@ -2642,22 +2642,25 @@ nsresult HTMLMediaElement::FinishDecoder
   mPendingEvents.Clear();
   // Set mDecoder now so if methods like GetCurrentSrc get called between
   // here and Load(), they work.
   mDecoder = aDecoder;
 
   // Tell the decoder about its MediaResource now so things like principals are
   // available immediately.
   mDecoder->SetResource(aStream);
-  aDecoder->SetAudioChannel(mAudioChannel);
+  mDecoder->SetAudioChannel(mAudioChannel);
   mDecoder->SetAudioCaptured(mAudioCaptured);
   mDecoder->SetVolume(mMuted ? 0.0 : mVolume);
   mDecoder->SetPreservesPitch(mPreservesPitch);
   mDecoder->SetPlaybackRate(mPlaybackRate);
 
+  if (mMediaKeys) {
+    mDecoder->SetCDMProxy(mMediaKeys->GetCDMProxy());
+  }
   if (mPreloadAction == HTMLMediaElement::PRELOAD_METADATA) {
     mDecoder->SetMinimizePrerollUntilPlaybackStarts();
   }
 
   // Update decoder principal before we start decoding, since it
   // can affect how we feed data to MediaStreams
   NotifyDecoderPrincipalChanged();
 
@@ -3969,16 +3972,19 @@ HTMLMediaElement::SetMediaKeys(mozilla::
   // TODO: Need to shutdown existing MediaKeys instance? bug 1016709.
   nsRefPtr<Promise> promise = Promise::Create(global, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
   if (mMediaKeys != aMediaKeys) {
     mMediaKeys = aMediaKeys;
   }
+  if (mDecoder) {
+    mDecoder->SetCDMProxy(mMediaKeys->GetCDMProxy());
+  }
   promise->MaybeResolve(JS::UndefinedHandleValue);
   return promise.forget();
 }
 
 MediaWaitingFor
 HTMLMediaElement::WaitingFor() const
 {
   return mWaitingFor;
--- a/content/media/AbstractMediaDecoder.h
+++ b/content/media/AbstractMediaDecoder.h
@@ -20,16 +20,17 @@ namespace layers
 {
   class ImageContainer;
 }
 class MediaResource;
 class ReentrantMonitor;
 class VideoFrameContainer;
 class TimedMetadata;
 class MediaDecoderOwner;
+class CDMProxy;
 
 typedef nsDataHashtable<nsCStringHashKey, nsCString> MetadataTags;
 
 static inline bool IsCurrentThread(nsIThread* aThread) {
   return NS_GetCurrentThread() == aThread;
 }
 
 /**
@@ -132,16 +133,19 @@ public:
     ~AutoNotifyDecoded() {
       mDecoder->NotifyDecodedFrames(mParsed, mDecoded);
     }
   private:
     AbstractMediaDecoder* mDecoder;
     uint32_t& mParsed;
     uint32_t& mDecoded;
   };
+
+  virtual nsresult SetCDMProxy(CDMProxy* aProxy) { return NS_ERROR_NOT_IMPLEMENTED; }
+  virtual CDMProxy* GetCDMProxy() { return nullptr; }
 };
 
 class MetadataEventRunner : public nsRunnable
 {
   private:
     nsRefPtr<AbstractMediaDecoder> mDecoder;
   public:
     MetadataEventRunner(AbstractMediaDecoder* aDecoder, MediaInfo* aInfo, MetadataTags* aTags)
--- a/content/media/AudioNodeExternalInputStream.cpp
+++ b/content/media/AudioNodeExternalInputStream.cpp
@@ -1,17 +1,16 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "AudioNodeEngine.h"
 #include "AudioNodeExternalInputStream.h"
 #include "AudioChannelFormat.h"
-#include "speex/speex_resampler.h"
 #include "mozilla/dom/MediaStreamAudioSourceNode.h"
 
 using namespace mozilla::dom;
 
 namespace mozilla {
 
 AudioNodeExternalInputStream::AudioNodeExternalInputStream(AudioNodeEngine* aEngine, TrackRate aSampleRate)
   : AudioNodeStream(aEngine, MediaStreamGraph::INTERNAL_STREAM, aSampleRate)
--- a/content/media/AudioSegment.cpp
+++ b/content/media/AudioSegment.cpp
@@ -4,17 +4,17 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "AudioSegment.h"
 
 #include "AudioStream.h"
 #include "AudioMixer.h"
 #include "AudioChannelFormat.h"
 #include "Latency.h"
-#include "speex/speex_resampler.h"
+#include <speex/speex_resampler.h>
 
 namespace mozilla {
 
 template <class SrcT, class DestT>
 static void
 InterleaveAndConvertBuffer(const SrcT** aSourceChannels,
                            int32_t aLength, float aVolume,
                            int32_t aChannels,
--- a/content/media/MediaDecoder.cpp
+++ b/content/media/MediaDecoder.cpp
@@ -1655,16 +1655,35 @@ bool MediaDecoder::CanPlayThrough()
   // we don't suddenly discover that we need to buffer. This is particularly
   // required near the start of the media, when not much data is downloaded.
   int64_t readAheadMargin =
     static_cast<int64_t>(stats.mPlaybackRate * CAN_PLAY_THROUGH_MARGIN);
   return stats.mTotalBytes == stats.mDownloadPosition ||
          stats.mDownloadPosition > stats.mPlaybackPosition + readAheadMargin;
 }
 
+nsresult
+MediaDecoder::SetCDMProxy(CDMProxy* aProxy)
+{
+  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
+  MOZ_ASSERT(NS_IsMainThread());
+  mProxy = aProxy;
+  // Awaken any readers waiting for the proxy.
+  NotifyWaitingForResourcesStatusChanged();
+  return NS_OK;
+}
+
+CDMProxy*
+MediaDecoder::GetCDMProxy()
+{
+  GetReentrantMonitor().AssertCurrentThreadIn();
+  MOZ_ASSERT(OnDecodeThread() || NS_IsMainThread());
+  return mProxy;
+}
+
 #ifdef MOZ_RAW
 bool
 MediaDecoder::IsRawEnabled()
 {
   return Preferences::GetBool("media.raw.enabled");
 }
 #endif
 
--- a/content/media/MediaDecoder.h
+++ b/content/media/MediaDecoder.h
@@ -185,16 +185,17 @@ destroying the MediaDecoder object.
 #include "MediaResource.h"
 #include "mozilla/dom/AudioChannelBinding.h"
 #include "mozilla/gfx/Rect.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "mozilla/TimeStamp.h"
 #include "MediaStreamGraph.h"
 #include "AbstractMediaDecoder.h"
 #include "necko-config.h"
+#include "mozilla/CDMProxy.h"
 
 class nsIStreamListener;
 class nsIPrincipal;
 class nsITimer;
 
 namespace mozilla {
 namespace dom {
 class TimeRanges;
@@ -844,16 +845,22 @@ public:
 
   // Returns true if we're logically playing, that is, if the Play() has
   // been called and Pause() has not or we have not yet reached the end
   // of media. This is irrespective of the seeking state; if the owner
   // calls Play() and then Seek(), we still count as logically playing.
   // The decoder monitor must be held.
   bool IsLogicallyPlaying();
 
+  // This takes the decoder monitor.
+  virtual nsresult SetCDMProxy(CDMProxy* aProxy) MOZ_OVERRIDE;
+
+  // Decoder monitor must be held.
+  virtual CDMProxy* GetCDMProxy() MOZ_OVERRIDE;
+
 #ifdef MOZ_RAW
   static bool IsRawEnabled();
 #endif
 
   static bool IsOggEnabled();
   static bool IsOpusEnabled();
 
 #ifdef MOZ_WAVE
@@ -1094,16 +1101,18 @@ private:
     }
   private:
     ReentrantMonitor mReentrantMonitor;
   };
 
   // The |RestrictedAccessMonitor| member object.
   RestrictedAccessMonitor mReentrantMonitor;
 
+  nsRefPtr<CDMProxy> mProxy;
+
 protected:
   // Data about MediaStreams that are being fed by this decoder.
   nsTArray<OutputStreamData> mOutputStreams;
   // The SourceMediaStream we are using to feed the mOutputStreams. This stream
   // is never exposed outside the decoder.
   // Only written on the main thread while holding the monitor. Therefore it
   // can be read on any thread while holding the monitor, or on the main thread
   // without holding the monitor.
--- a/content/media/MediaDecoderOwner.h
+++ b/content/media/MediaDecoderOwner.h
@@ -132,14 +132,20 @@ public:
 
   // Called by the media decoder and the video frame to get the
   // ImageContainer containing the video data.
   virtual VideoFrameContainer* GetVideoFrameContainer() = 0;
 
   // Called by the media decoder object, on the main thread,
   // when the connection between Rtsp server and client gets lost.
   virtual void ResetConnectionState() = 0;
+
+  // Dispatches a "needkey" event to the HTMLMediaElement, with the
+  // provided init data.
+  // Main thread only.
+  virtual void DispatchNeedKey(const nsTArray<uint8_t>& aInitData,
+                               const nsAString& aInitDataType) = 0;
 };
 
 }
 
 #endif
 
--- a/content/media/MediaDecoderReader.h
+++ b/content/media/MediaDecoderReader.h
@@ -171,17 +171,17 @@ public:
   AudioData* DecodeToFirstAudioData();
   VideoData* DecodeToFirstVideoData();
 
   MediaInfo GetMediaInfo() { return mInfo; }
 
   // Indicates if the media is seekable.
   // ReadMetada should be called before calling this method.
   virtual bool IsMediaSeekable() = 0;
-  
+
 protected:
   virtual ~MediaDecoderReader();
 
   // Overrides of this function should decodes an unspecified amount of
   // audio data, enqueuing the audio data in mAudioQueue. Returns true
   // when there's more audio to decode, false if the audio is finished,
   // end of file has been reached, or an un-recoverable read error has
   // occured. This function blocks until the decode is complete.
--- a/content/media/MediaRecorder.cpp
+++ b/content/media/MediaRecorder.cpp
@@ -449,17 +449,18 @@ private:
         return;
       }
     }
 
     // In case source media stream does not notify track end, recieve
     // shutdown notification and stop Read Thread.
     nsContentUtils::RegisterShutdownObserver(this);
 
-    nsRefPtr<nsIRunnable> event = new ExtractRunnable(this);
+    already_AddRefed<Session> session(this); // addref in MediaRecorder::Start
+    nsRefPtr<nsIRunnable> event = new ExtractRunnable(Move(session));
     if (NS_FAILED(mReadThread->Dispatch(event, NS_DISPATCH_NORMAL))) {
       NS_WARNING("Failed to dispatch ExtractRunnable at beginning");
     }
   }
   // application should get blob and onstop event
   void DoSessionEndTask(nsresult rv)
   {
     MOZ_ASSERT(NS_IsMainThread());
@@ -467,17 +468,18 @@ private:
       mRecorder->NotifyError(rv);
     }
 
     CleanupStreams();
     if (NS_FAILED(NS_DispatchToMainThread(new PushBlobRunnable(this)))) {
       MOZ_ASSERT(false, "NS_DispatchToMainThread PushBlobRunnable failed");
     }
     // Destroy this session object in main thread.
-    if (NS_FAILED(NS_DispatchToMainThread(new DestroyRunnable(this)))) {
+    already_AddRefed<Session> session(this); // addref in MediaRecorder::Start
+    if (NS_FAILED(NS_DispatchToMainThread(new DestroyRunnable(Move(session))))) {
       MOZ_ASSERT(false, "NS_DispatchToMainThread DestroyRunnable failed");
     }
   }
   void CleanupStreams()
   {
     if (mInputPort.get()) {
       mInputPort->Destroy();
       mInputPort = nullptr;
--- a/content/media/MediaStreamGraph.cpp
+++ b/content/media/MediaStreamGraph.cpp
@@ -23,17 +23,16 @@
 #include "AudioChannelService.h"
 #include "AudioNodeEngine.h"
 #include "AudioNodeStream.h"
 #include "AudioNodeExternalInputStream.h"
 #include <algorithm>
 #include "DOMMediaStream.h"
 #include "GeckoProfiler.h"
 #include "mozilla/unused.h"
-#include "speex/speex_resampler.h"
 #ifdef MOZ_WEBRTC
 #include "AudioOutputObserver.h"
 #endif
 
 using namespace mozilla::layers;
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 
--- a/content/media/MediaStreamGraph.h
+++ b/content/media/MediaStreamGraph.h
@@ -12,17 +12,17 @@
 #include "nsTArray.h"
 #include "nsIRunnable.h"
 #include "StreamBuffer.h"
 #include "TimeVarying.h"
 #include "VideoFrameContainer.h"
 #include "VideoSegment.h"
 #include "MainThreadUtils.h"
 #include "nsAutoRef.h"
-#include "speex/speex_resampler.h"
+#include <speex/speex_resampler.h>
 #include "AudioMixer.h"
 #include "mozilla/dom/AudioChannelBinding.h"
 
 class nsIRunnable;
 
 template <>
 class nsAutoRefTraits<SpeexResamplerState> : public nsPointerRefTraits<SpeexResamplerState>
 {
--- a/content/media/MediaTaskQueue.cpp
+++ b/content/media/MediaTaskQueue.cpp
@@ -43,16 +43,56 @@ MediaTaskQueue::Dispatch(TemporaryRef<ns
     NS_WARNING("Failed to dispatch runnable to run MediaTaskQueue");
     return rv;
   }
   mIsRunning = true;
 
   return NS_OK;
 }
 
+class MediaTaskQueueSyncRunnable : public nsRunnable {
+public:
+  MediaTaskQueueSyncRunnable(TemporaryRef<nsIRunnable> aRunnable)
+    : mRunnable(aRunnable)
+    , mMonitor("MediaTaskQueueSyncRunnable")
+    , mDone(false)
+  {
+  }
+
+  NS_IMETHOD Run() {
+    nsresult rv = mRunnable->Run();
+    {
+      MonitorAutoLock mon(mMonitor);
+      mDone = true;
+      mon.NotifyAll();
+    }
+    return rv;
+  }
+
+  nsresult WaitUntilDone() {
+    MonitorAutoLock mon(mMonitor);
+    while (!mDone) {
+      mon.Wait();
+    }
+    return NS_OK;
+  }
+private:
+  RefPtr<nsIRunnable> mRunnable;
+  Monitor mMonitor;
+  bool mDone;
+};
+
+nsresult
+MediaTaskQueue::SyncDispatch(TemporaryRef<nsIRunnable> aRunnable) {
+  RefPtr<MediaTaskQueueSyncRunnable> task(new MediaTaskQueueSyncRunnable(aRunnable));
+  nsresult rv = Dispatch(task);
+  NS_ENSURE_SUCCESS(rv, rv);
+  return task->WaitUntilDone();
+}
+
 void
 MediaTaskQueue::AwaitIdle()
 {
   MonitorAutoLock mon(mQueueMonitor);
   AwaitIdleLocked();
 }
 
 void
--- a/content/media/MediaTaskQueue.h
+++ b/content/media/MediaTaskQueue.h
@@ -28,16 +28,18 @@ class MediaTaskQueue MOZ_FINAL {
 
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaTaskQueue)
 
   MediaTaskQueue(TemporaryRef<SharedThreadPool> aPool);
 
   nsresult Dispatch(TemporaryRef<nsIRunnable> aRunnable);
 
+  nsresult SyncDispatch(TemporaryRef<nsIRunnable> aRunnable);
+
   // Removes all pending tasks from the task queue, and blocks until
   // the currently running task (if any) finishes.
   void Flush();
 
   // Blocks until all tasks finish executing, then shuts down the task queue
   // and exits.
   void Shutdown();
 
new file mode 100644
--- /dev/null
+++ b/content/media/eme/CDMCallbackProxy.cpp
@@ -0,0 +1,284 @@
+/* -*- 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 "mozilla/CDMCallbackProxy.h"
+#include "mozilla/CDMProxy.h"
+#include "nsString.h"
+#include "mozilla/dom/MediaKeys.h"
+#include "mozilla/dom/MediaKeySession.h"
+#include "mozIGeckoMediaPluginService.h"
+#include "nsContentCID.h"
+#include "nsServiceManagerUtils.h"
+#include "MainThreadUtils.h"
+#include "EMELog.h"
+
+namespace mozilla {
+
+CDMCallbackProxy::CDMCallbackProxy(CDMProxy* aProxy)
+  : mProxy(aProxy)
+{
+
+}
+
+class NewSessionTask : public nsRunnable {
+public:
+  NewSessionTask(CDMProxy* aProxy,
+                 uint32_t aPromiseId,
+                 const nsCString& aSessionId)
+    : mProxy(aProxy)
+    , mPid(aPromiseId)
+    , mSid(NS_ConvertUTF8toUTF16(aSessionId))
+  {
+  }
+
+  NS_IMETHOD Run() {
+    mProxy->OnResolveNewSessionPromise(mPid, mSid);
+    return NS_OK;
+  }
+
+  nsRefPtr<CDMProxy> mProxy;
+  dom::PromiseId mPid;
+  nsString mSid;
+};
+
+void
+CDMCallbackProxy::ResolveNewSessionPromise(uint32_t aPromiseId,
+                                           const nsCString& aSessionId)
+{
+  MOZ_ASSERT(mProxy->IsOnGMPThread());
+
+  nsRefPtr<nsIRunnable> task(new NewSessionTask(mProxy,
+                                                aPromiseId,
+                                                aSessionId));
+  NS_DispatchToMainThread(task);
+}
+
+void
+CDMCallbackProxy::ResolvePromise(uint32_t aPromiseId)
+{
+  MOZ_ASSERT(mProxy->IsOnGMPThread());
+
+  // Note: CDMProxy proxies this from non-main threads to main thread.
+  mProxy->ResolvePromise(aPromiseId);
+}
+
+class RejectPromiseTask : public nsRunnable {
+public:
+  RejectPromiseTask(CDMProxy* aProxy,
+                    uint32_t aPromiseId,
+                    nsresult aException,
+                    const nsCString& aMessage)
+    : mProxy(aProxy)
+    , mPid(aPromiseId)
+    , mException(aException)
+    , mMsg(NS_ConvertUTF8toUTF16(aMessage))
+  {
+  }
+
+  NS_IMETHOD Run() {
+    mProxy->OnRejectPromise(mPid, mException, mMsg);
+    return NS_OK;
+  }
+
+  nsRefPtr<CDMProxy> mProxy;
+  dom::PromiseId mPid;
+  nsresult mException;
+  nsString mMsg;
+};
+
+
+void
+CDMCallbackProxy::RejectPromise(uint32_t aPromiseId,
+                                nsresult aException,
+                                const nsCString& aMessage)
+{
+  MOZ_ASSERT(mProxy->IsOnGMPThread());
+
+  nsRefPtr<nsIRunnable> task;
+  task = new RejectPromiseTask(mProxy,
+                               aPromiseId,
+                               aException,
+                               aMessage);
+  NS_DispatchToMainThread(task);
+}
+
+class SessionMessageTask : public nsRunnable {
+public:
+  SessionMessageTask(CDMProxy* aProxy,
+                     const nsCString& aSessionId,
+                     const nsTArray<uint8_t>& aMessage,
+                     const nsCString& aDestinationURL)
+    : mProxy(aProxy)
+    , mSid(NS_ConvertUTF8toUTF16(aSessionId))
+    , mURL(NS_ConvertUTF8toUTF16(aDestinationURL))
+  {
+    mMsg.AppendElements(aMessage);
+  }
+
+  NS_IMETHOD Run() {
+    mProxy->OnSessionMessage(mSid, mMsg, mURL);
+    return NS_OK;
+  }
+
+  nsRefPtr<CDMProxy> mProxy;
+  dom::PromiseId mPid;
+  nsString mSid;
+  nsTArray<uint8_t> mMsg;
+  nsString mURL;
+};
+
+void
+CDMCallbackProxy::SessionMessage(const nsCString& aSessionId,
+                                 const nsTArray<uint8_t>& aMessage,
+                                 const nsCString& aDestinationURL)
+{
+  MOZ_ASSERT(mProxy->IsOnGMPThread());
+
+  nsRefPtr<nsIRunnable> task;
+  task = new SessionMessageTask(mProxy,
+                                aSessionId,
+                                aMessage,
+                                aDestinationURL);
+  NS_DispatchToMainThread(task);
+}
+
+class ExpirationChangeTask : public nsRunnable {
+public:
+  ExpirationChangeTask(CDMProxy* aProxy,
+                       const nsCString& aSessionId,
+                       GMPTimestamp aExpiryTime)
+    : mProxy(aProxy)
+    , mSid(NS_ConvertUTF8toUTF16(aSessionId))
+    , mTimestamp(aExpiryTime)
+  {}
+
+  NS_IMETHOD Run() {
+    mProxy->OnExpirationChange(mSid, mTimestamp);
+    return NS_OK;
+  }
+
+  nsRefPtr<CDMProxy> mProxy;
+  nsString mSid;
+  GMPTimestamp mTimestamp;
+};
+
+void
+CDMCallbackProxy::ExpirationChange(const nsCString& aSessionId,
+                                   GMPTimestamp aExpiryTime)
+{
+  MOZ_ASSERT(mProxy->IsOnGMPThread());
+
+  nsRefPtr<nsIRunnable> task;
+  task = new ExpirationChangeTask(mProxy,
+                                  aSessionId,
+                                  aExpiryTime);
+  NS_DispatchToMainThread(task);
+}
+
+void
+CDMCallbackProxy::SessionClosed(const nsCString& aSessionId)
+{
+  MOZ_ASSERT(mProxy->IsOnGMPThread());
+
+  nsRefPtr<nsIRunnable> task;
+  task = NS_NewRunnableMethodWithArg<nsString>(mProxy,
+                                               &CDMProxy::OnSessionClosed,
+                                               NS_ConvertUTF8toUTF16(aSessionId));
+  NS_DispatchToMainThread(task);
+}
+
+class SessionErrorTask : public nsRunnable {
+public:
+  SessionErrorTask(CDMProxy* aProxy,
+                   const nsCString& aSessionId,
+                   nsresult aException,
+                   uint32_t aSystemCode,
+                   const nsCString& aMessage)
+    : mProxy(aProxy)
+    , mSid(NS_ConvertUTF8toUTF16(aSessionId))
+    , mException(aException)
+    , mSystemCode(aSystemCode)
+    , mMsg(NS_ConvertUTF8toUTF16(aMessage))
+  {}
+
+  NS_IMETHOD Run() {
+    mProxy->OnSessionError(mSid, mException, mSystemCode, mMsg);
+    return NS_OK;
+  }
+
+  nsRefPtr<CDMProxy> mProxy;
+  dom::PromiseId mPid;
+  nsString mSid;
+  nsresult mException;
+  uint32_t mSystemCode;
+  nsString mMsg;
+};
+
+void
+CDMCallbackProxy::SessionError(const nsCString& aSessionId,
+                               nsresult aException,
+                               uint32_t aSystemCode,
+                               const nsCString& aMessage)
+{
+  MOZ_ASSERT(mProxy->IsOnGMPThread());
+
+  nsRefPtr<nsIRunnable> task;
+  task = new SessionErrorTask(mProxy,
+                              aSessionId,
+                              aException,
+                              aSystemCode,
+                              aMessage);
+  NS_DispatchToMainThread(task);
+}
+
+void
+CDMCallbackProxy::KeyIdUsable(const nsCString& aSessionId,
+                              const nsTArray<uint8_t>& aKeyId)
+{
+  MOZ_ASSERT(mProxy->IsOnGMPThread());
+
+  CDMCaps::AutoLock caps(mProxy->Capabilites());
+  caps.SetKeyUsable(aKeyId, NS_ConvertUTF8toUTF16(aSessionId));
+}
+
+void
+CDMCallbackProxy::KeyIdNotUsable(const nsCString& aSessionId,
+                                 const nsTArray<uint8_t>& aKeyId)
+{
+  MOZ_ASSERT(mProxy->IsOnGMPThread());
+
+  CDMCaps::AutoLock caps(mProxy->Capabilites());
+  caps.SetKeyUnusable(aKeyId, NS_ConvertUTF8toUTF16(aSessionId));
+}
+
+void
+CDMCallbackProxy::SetCaps(uint64_t aCaps)
+{
+  MOZ_ASSERT(mProxy->IsOnGMPThread());
+
+  CDMCaps::AutoLock caps(mProxy->Capabilites());
+  caps.SetCaps(aCaps);
+}
+
+void
+CDMCallbackProxy::Decrypted(uint32_t aId,
+                            GMPErr aResult,
+                            const nsTArray<uint8_t>& aDecryptedData)
+{
+  MOZ_ASSERT(mProxy->IsOnGMPThread());
+
+  mProxy->gmp_Decrypted(aId, aResult, aDecryptedData);
+}
+
+void
+CDMCallbackProxy::Terminated()
+{
+  MOZ_ASSERT(mProxy->IsOnGMPThread());
+
+  mProxy->gmp_Terminated();
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/media/eme/CDMCallbackProxy.h
@@ -0,0 +1,69 @@
+/* -*- 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/. */
+
+#ifndef CDMCallbackProxy_h_
+#define CDMCallbackProxy_h_
+
+#include "mozilla/CDMProxy.h"
+#include "gmp-decryption.h"
+#include "GMPDecryptorProxy.h"
+
+namespace mozilla {
+
+// Proxies call backs from the CDM on the GMP thread back to the MediaKeys
+// object on the main thread.
+class CDMCallbackProxy : public GMPDecryptorProxyCallback {
+public:
+  virtual void ResolveNewSessionPromise(uint32_t aPromiseId,
+                                        const nsCString& aSessionId) MOZ_OVERRIDE;
+
+  virtual void ResolvePromise(uint32_t aPromiseId) MOZ_OVERRIDE;
+
+  virtual void RejectPromise(uint32_t aPromiseId,
+                             nsresult aException,
+                             const nsCString& aSessionId) MOZ_OVERRIDE;
+
+  virtual void SessionMessage(const nsCString& aSessionId,
+                              const nsTArray<uint8_t>& aMessage,
+                              const nsCString& aDestinationURL) MOZ_OVERRIDE;
+
+  virtual void ExpirationChange(const nsCString& aSessionId,
+                                GMPTimestamp aExpiryTime) MOZ_OVERRIDE;
+
+  virtual void SessionClosed(const nsCString& aSessionId) MOZ_OVERRIDE;
+
+  virtual void SessionError(const nsCString& aSessionId,
+                            nsresult aException,
+                            uint32_t aSystemCode,
+                            const nsCString& aMessage) MOZ_OVERRIDE;
+
+  virtual void KeyIdUsable(const nsCString& aSessionId,
+                           const nsTArray<uint8_t>& aKeyId) MOZ_OVERRIDE;
+
+  virtual void KeyIdNotUsable(const nsCString& aSessionId,
+                              const nsTArray<uint8_t>& aKeyId) MOZ_OVERRIDE;
+
+  virtual void SetCaps(uint64_t aCaps) MOZ_OVERRIDE;
+
+  virtual void Decrypted(uint32_t aId,
+                         GMPErr aResult,
+                         const nsTArray<uint8_t>& aDecryptedData) MOZ_OVERRIDE;
+
+  virtual void Terminated() MOZ_OVERRIDE;
+
+  ~CDMCallbackProxy() {}
+
+private:
+  friend class CDMProxy;
+  explicit CDMCallbackProxy(CDMProxy* aProxy);
+
+  // Warning: Weak ref.
+  CDMProxy* mProxy;
+};
+
+} // namespace mozilla
+
+#endif // CDMCallbackProxy_h_
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/content/media/eme/CDMCaps.cpp
@@ -0,0 +1,187 @@
+/* -*- 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 "CDMCaps.h"
+#include "gmp-decryption.h"
+#include "EMELog.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+
+CDMCaps::CDMCaps()
+  : mMonitor("CDMCaps")
+  , mCaps(0)
+{
+}
+
+CDMCaps::~CDMCaps()
+{
+}
+
+void
+CDMCaps::Lock()
+{
+  mMonitor.Lock();
+}
+
+void
+CDMCaps::Unlock()
+{
+  mMonitor.Unlock();
+}
+
+bool
+CDMCaps::HasCap(uint64_t aCap)
+{
+  mMonitor.AssertCurrentThreadOwns();
+  return (mCaps & aCap) == aCap;
+}
+
+CDMCaps::AutoLock::AutoLock(CDMCaps& aInstance)
+  : mData(aInstance)
+{
+  mData.Lock();
+}
+
+CDMCaps::AutoLock::~AutoLock()
+{
+  mData.Unlock();
+}
+
+void
+CDMCaps::AutoLock::SetCaps(uint64_t aCaps)
+{
+  EME_LOG("SetCaps()");
+  mData.mMonitor.AssertCurrentThreadOwns();
+  mData.mCaps = aCaps;
+  for (size_t i = 0; i < mData.mWaitForCaps.Length(); i++) {
+    NS_DispatchToMainThread(mData.mWaitForCaps[i], NS_DISPATCH_NORMAL);
+  }
+  mData.mWaitForCaps.Clear();
+}
+
+void
+CDMCaps::AutoLock::CallOnMainThreadWhenCapsAvailable(nsIRunnable* aContinuation)
+{
+  mData.mMonitor.AssertCurrentThreadOwns();
+  if (mData.mCaps) {
+    NS_DispatchToMainThread(aContinuation, NS_DISPATCH_NORMAL);
+    MOZ_ASSERT(mData.mWaitForCaps.IsEmpty());
+  } else {
+    mData.mWaitForCaps.AppendElement(aContinuation);
+  }
+}
+
+bool
+CDMCaps::AutoLock::IsKeyUsable(const CencKeyId& aKeyId)
+{
+  mData.mMonitor.AssertCurrentThreadOwns();
+  const auto& keys = mData.mUsableKeyIds;
+  for (size_t i = 0; i < keys.Length(); i++) {
+    if (keys[i].mId == aKeyId) {
+      return true;
+    }
+  }
+  return false;
+}
+
+void
+CDMCaps::AutoLock::SetKeyUsable(const CencKeyId& aKeyId,
+                                const nsString& aSessionId)
+{
+  mData.mMonitor.AssertCurrentThreadOwns();
+  mData.mUsableKeyIds.AppendElement(UsableKey(aKeyId, aSessionId));
+  auto& waiters = mData.mWaitForKeys;
+  size_t i = 0;
+  while (i < waiters.Length()) {
+    auto& w = waiters[i];
+    if (w.mKeyId == aKeyId) {
+      if (waiters[i].mTarget) {
+        EME_LOG("SetKeyUsable() notified waiter.");
+        w.mTarget->Dispatch(w.mContinuation, NS_DISPATCH_NORMAL);
+      } else {
+        w.mContinuation->Run();
+      }
+      waiters.RemoveElementAt(i);
+    } else {
+      i++;
+    }
+  }
+}
+
+void
+CDMCaps::AutoLock::SetKeyUnusable(const CencKeyId& aKeyId,
+                                  const nsString& aSessionId)
+{
+  mData.mMonitor.AssertCurrentThreadOwns();
+  auto& keys = mData.mUsableKeyIds;
+  for (size_t i = 0; i < keys.Length(); i++) {
+    if (keys[i].mId == aKeyId &&
+        keys[i].mSessionId == aSessionId) {
+      keys.RemoveElementAt(i);
+      break;
+    }
+  }
+}
+
+void
+CDMCaps::AutoLock::CallWhenKeyUsable(const CencKeyId& aKey,
+                                     nsIRunnable* aContinuation,
+                                     nsIThread* aTarget)
+{
+  mData.mMonitor.AssertCurrentThreadOwns();
+  MOZ_ASSERT(!IsKeyUsable(aKey));
+  MOZ_ASSERT(aContinuation);
+  mData.mWaitForKeys.AppendElement(WaitForKeys(aKey, aContinuation, aTarget));
+}
+
+void
+CDMCaps::AutoLock::DropKeysForSession(const nsAString& aSessionId)
+{
+  mData.mMonitor.AssertCurrentThreadOwns();
+  auto& keys = mData.mUsableKeyIds;
+  size_t i = 0;
+  while (i < keys.Length()) {
+    if (keys[i].mSessionId == aSessionId) {
+      keys.RemoveElementAt(i);
+    } else {
+      i++;
+    }
+  }
+}
+
+bool
+CDMCaps::AutoLock::AreCapsKnown()
+{
+  mData.mMonitor.AssertCurrentThreadOwns();
+  return mData.mCaps != 0;
+}
+
+bool
+CDMCaps::AutoLock::CanDecryptAndDecodeAudio()
+{
+  return mData.HasCap(GMP_EME_CAP_DECRYPT_AND_DECODE_AUDIO);
+}
+
+bool
+CDMCaps::AutoLock::CanDecryptAndDecodeVideo()
+{
+  return mData.HasCap(GMP_EME_CAP_DECRYPT_AND_DECODE_VIDEO);
+}
+
+bool
+CDMCaps::AutoLock::CanDecryptAudio()
+{
+  return mData.HasCap(GMP_EME_CAP_DECRYPT_AUDIO);
+}
+
+bool
+CDMCaps::AutoLock::CanDecryptVideo()
+{
+  return mData.HasCap(GMP_EME_CAP_DECRYPT_VIDEO);
+}
+
+} // namespace mozilla
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/content/media/eme/CDMCaps.h
@@ -0,0 +1,117 @@
+/* -*- 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/. */
+
+#ifndef CDMCaps_h_
+#define CDMCaps_h_
+
+#include "nsString.h"
+#include "nsAutoPtr.h"
+#include "mozilla/Monitor.h"
+#include "nsIThread.h"
+#include "nsTArray.h"
+#include "mozilla/Attributes.h"
+
+namespace mozilla {
+
+typedef nsTArray<uint8_t> CencKeyId;
+
+// CDM capabilities; what keys a CDMProxy can use, and whether it can decrypt, or
+// decrypt-and-decode on a per stream basis. Must be locked to access state.
+class CDMCaps {
+public:
+  CDMCaps();
+  ~CDMCaps();
+
+  // Locks the CDMCaps. It must be locked to access its shared state.
+  // Threadsafe when locked.
+  class MOZ_STACK_CLASS AutoLock {
+  public:
+    explicit AutoLock(CDMCaps& aKeyCaps);
+    ~AutoLock();
+
+    // Returns true if the capabilities of the CDM are known, i.e. they have
+    // been reported by the CDM to Gecko.
+    bool AreCapsKnown();
+
+    bool IsKeyUsable(const CencKeyId& aKeyId);
+
+    void SetKeyUsable(const CencKeyId& aKeyId, const nsString& aSessionId);
+    void SetKeyUnusable(const CencKeyId& aKeyId, const nsString& aSessionId);
+
+    void DropKeysForSession(const nsAString& aSessionId);
+
+    // Sets the capabilities of the CDM. aCaps is the logical OR of the
+    // GMP_EME_CAP_* flags from gmp-decryption.h.
+    void SetCaps(uint64_t aCaps);
+
+    bool CanDecryptAndDecodeAudio();
+    bool CanDecryptAndDecodeVideo();
+
+    bool CanDecryptAudio();
+    bool CanDecryptVideo();
+
+    void CallOnMainThreadWhenCapsAvailable(nsIRunnable* aContinuation);
+
+    // Calls aContinuation on aTarget thread when key become usable.
+    // Pass aTarget=nullptr and runnable will be called on the GMP thread
+    // when key becomes usable.
+    void CallWhenKeyUsable(const CencKeyId& aKey,
+                           nsIRunnable* aContinuation,
+                           nsIThread* aTarget = nullptr);
+
+  private:
+    // Not taking a strong ref, since this should be allocated on the stack.
+    CDMCaps& mData;
+  };
+
+private:
+  void Lock();
+  void Unlock();
+  bool HasCap(uint64_t);
+
+  struct WaitForKeys {
+    WaitForKeys(const CencKeyId& aKeyId,
+                nsIRunnable* aContinuation,
+                nsIThread* aTarget)
+      : mKeyId(aKeyId)
+      , mContinuation(aContinuation)
+      , mTarget(aTarget)
+    {}
+    CencKeyId mKeyId;
+    nsRefPtr<nsIRunnable> mContinuation;
+    nsCOMPtr<nsIThread> mTarget;
+  };
+
+  Monitor mMonitor;
+
+  struct UsableKey {
+    UsableKey(const CencKeyId& aId,
+              const nsString& aSessionId)
+      : mId(aId)
+      , mSessionId(aSessionId)
+    {}
+    UsableKey(const UsableKey& aOther)
+      : mId(aOther.mId)
+      , mSessionId(aOther.mSessionId)
+    {}
+    CencKeyId mId;
+    nsString mSessionId;
+  };
+  nsTArray<UsableKey> mUsableKeyIds;
+
+  nsTArray<WaitForKeys> mWaitForKeys;
+
+  nsTArray<nsRefPtr<nsIRunnable>> mWaitForCaps;
+  uint64_t mCaps;
+
+  // It is not safe to copy this object.
+  CDMCaps(const CDMCaps&) MOZ_DELETE;
+  CDMCaps& operator=(const CDMCaps&) MOZ_DELETE;
+};
+
+} // namespace mozilla
+
+#endif
--- a/content/media/eme/CDMProxy.cpp
+++ b/content/media/eme/CDMProxy.cpp
@@ -7,158 +7,299 @@
 #include "mozilla/CDMProxy.h"
 #include "nsString.h"
 #include "mozilla/dom/MediaKeys.h"
 #include "mozilla/dom/MediaKeySession.h"
 #include "mozIGeckoMediaPluginService.h"
 #include "nsContentCID.h"
 #include "nsServiceManagerUtils.h"
 #include "MainThreadUtils.h"
-
-// TODO: Change the functions in this file to do IPC via the Gecko Media
-// Plugins API. In the meantime, the code here merely implements the
-// interface we expect will be required when the IPC is working.
+#include "mozilla/EMELog.h"
+#include "nsIConsoleService.h"
+#include "prenv.h"
+#include "mozilla/PodOperations.h"
+#include "mozilla/CDMCallbackProxy.h"
 
 namespace mozilla {
 
 CDMProxy::CDMProxy(dom::MediaKeys* aKeys, const nsAString& aKeySystem)
   : mKeys(aKeys)
   , mKeySystem(aKeySystem)
+  , mCDM(nullptr)
+  , mDecryptionJobCount(0)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_COUNT_CTOR(CDMProxy);
 }
 
 CDMProxy::~CDMProxy()
 {
   MOZ_COUNT_DTOR(CDMProxy);
 }
 
 void
 CDMProxy::Init(PromiseId aPromiseId)
 {
   MOZ_ASSERT(NS_IsMainThread());
+
+  nsresult rv = mKeys->GetOrigin(mOrigin);
+  if (NS_FAILED(rv)) {
+    RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
+    return;
+  }
+  EME_LOG("Creating CDMProxy for origin='%s'",
+          NS_ConvertUTF16toUTF8(GetOrigin()).get());
+
   if (!mGMPThread) {
     nsCOMPtr<mozIGeckoMediaPluginService> mps =
       do_GetService("@mozilla.org/gecko-media-plugin-service;1");
     if (!mps) {
       RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
       return;
     }
     mps->GetThread(getter_AddRefs(mGMPThread));
     if (!mGMPThread) {
       RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
       return;
     }
   }
 
-  // TODO: Dispatch task to GMPThread to initialize CDM via IPC.
-
-  mKeys->OnCDMCreated(aPromiseId);
+  nsRefPtr<nsIRunnable> task(NS_NewRunnableMethodWithArg<uint32_t>(this, &CDMProxy::gmp_Init, aPromiseId));
+  mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
 }
 
-static int sFakeSessionIdNum = 0;
+#ifdef DEBUG
+bool
+CDMProxy::IsOnGMPThread()
+{
+  return NS_GetCurrentThread() == mGMPThread;
+}
+#endif
+
+void
+CDMProxy::gmp_Init(uint32_t aPromiseId)
+{
+  MOZ_ASSERT(IsOnGMPThread());
+
+  nsCOMPtr<mozIGeckoMediaPluginService> mps =
+    do_GetService("@mozilla.org/gecko-media-plugin-service;1");
+  if (!mps) {
+    RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
+    return;
+  }
+
+  nsTArray<nsCString> tags;
+  tags.AppendElement(NS_ConvertUTF16toUTF8(mKeySystem));
+  nsresult rv = mps->GetGMPDecryptor(&tags, GetOrigin(), &mCDM);
+  if (NS_FAILED(rv) || !mCDM) {
+    RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
+  } else {
+    mCallback = new CDMCallbackProxy(this);
+    mCDM->Init(mCallback);
+    nsRefPtr<nsIRunnable> task(NS_NewRunnableMethodWithArg<uint32_t>(this, &CDMProxy::OnCDMCreated, aPromiseId));
+    NS_DispatchToMainThread(task);
+  }
+}
+
+void
+CDMProxy::OnCDMCreated(uint32_t aPromiseId)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  if (!mKeys.IsNull()) {
+    mKeys->OnCDMCreated(aPromiseId);
+  } else {
+    NS_WARNING("CDMProxy unable to reject promise!");
+  }
+}
 
 void
 CDMProxy::CreateSession(dom::SessionType aSessionType,
                         PromiseId aPromiseId,
                         const nsAString& aInitDataType,
                         const Uint8Array& aInitData)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mGMPThread);
 
-  // TODO: Dispatch task to GMPThread to call CDM CreateSession via IPC.
+  nsAutoPtr<CreateSessionData> data(new CreateSessionData());
+  data->mSessionType = aSessionType;
+  data->mPromiseId = aPromiseId;
+  data->mInitDataType = NS_ConvertUTF16toUTF8(aInitDataType);
+  data->mInitData.AppendElements(aInitData.Data(), aInitData.Length());
+
+  nsRefPtr<nsIRunnable> task(
+    NS_NewRunnableMethodWithArg<nsAutoPtr<CreateSessionData>>(this, &CDMProxy::gmp_CreateSession, data));
+  mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
+}
 
-  // Make a fake session id. We'll get this from the CDM normally.
-  nsAutoString id;
-  id.AppendASCII("FakeSessionId_");
-  id.AppendInt(sFakeSessionIdNum++);
+GMPSessionType
+ToGMPSessionType(dom::SessionType aSessionType) {
+  switch (aSessionType) {
+    case dom::SessionType::Temporary: return kGMPTemporySession;
+    case dom::SessionType::Persistent: return kGMPPersistentSession;
+    default: return kGMPTemporySession;
+  };
+};
 
-  mKeys->OnSessionActivated(aPromiseId, id);
+void
+CDMProxy::gmp_CreateSession(nsAutoPtr<CreateSessionData> aData)
+{
+  MOZ_ASSERT(IsOnGMPThread());
+  if (!mCDM) {
+    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
+    return;
+  }
+  mCDM->CreateSession(aData->mPromiseId,
+                      aData->mInitDataType,
+                      aData->mInitData,
+                      ToGMPSessionType(aData->mSessionType));
 }
 
 void
 CDMProxy::LoadSession(PromiseId aPromiseId,
                       const nsAString& aSessionId)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mGMPThread);
 
-  // TODO: Dispatch task to GMPThread to call CDM LoadSession via IPC.
-  // make MediaKeys::mPendingSessions CC'd
+  nsAutoPtr<SessionOpData> data(new SessionOpData());
+  data->mPromiseId = aPromiseId;
+  data->mSessionId = NS_ConvertUTF16toUTF8(aSessionId);
+  nsRefPtr<nsIRunnable> task(
+    NS_NewRunnableMethodWithArg<nsAutoPtr<SessionOpData>>(this, &CDMProxy::gmp_LoadSession, data));
+  mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
+}
 
-  mKeys->OnSessionActivated(aPromiseId, aSessionId);
+void
+CDMProxy::gmp_LoadSession(nsAutoPtr<SessionOpData> aData)
+{
+  MOZ_ASSERT(IsOnGMPThread());
+
+  if (!mCDM) {
+    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
+    return;
+  }
+  mCDM->LoadSession(aData->mPromiseId, aData->mSessionId);
 }
 
 void
 CDMProxy::SetServerCertificate(PromiseId aPromiseId,
-                               const Uint8Array& aCertData)
+                               const Uint8Array& aCert)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mGMPThread);
 
-  // TODO: Dispatch task to GMPThread to call CDM SetServerCertificate via IPC.
-
-  ResolvePromise(aPromiseId);
+  nsAutoPtr<SetServerCertificateData> data;
+  data->mPromiseId = aPromiseId;
+  data->mCert.AppendElements(aCert.Data(), aCert.Length());
+  nsRefPtr<nsIRunnable> task(
+    NS_NewRunnableMethodWithArg<nsAutoPtr<SetServerCertificateData>>(this, &CDMProxy::gmp_SetServerCertificate, data));
+  mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
 }
 
-static int sUpdateCount = 0;
+void
+CDMProxy::gmp_SetServerCertificate(nsAutoPtr<SetServerCertificateData> aData)
+{
+  MOZ_ASSERT(IsOnGMPThread());
+  if (!mCDM) {
+    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
+    return;
+  }
+  mCDM->SetServerCertificate(aData->mPromiseId, aData->mCert);
+}
 
 void
 CDMProxy::UpdateSession(const nsAString& aSessionId,
                         PromiseId aPromiseId,
                         const Uint8Array& aResponse)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mGMPThread);
   NS_ENSURE_TRUE_VOID(!mKeys.IsNull());
 
-  // TODO: Dispatch task to GMPThread to call CDM UpdateSession via IPC.
+  nsAutoPtr<UpdateSessionData> data(new UpdateSessionData());
+  data->mPromiseId = aPromiseId;
+  data->mSessionId = NS_ConvertUTF16toUTF8(aSessionId);
+  data->mResponse.AppendElements(aResponse.Data(), aResponse.Length());
+  nsRefPtr<nsIRunnable> task(
+    NS_NewRunnableMethodWithArg<nsAutoPtr<UpdateSessionData>>(this, &CDMProxy::gmp_UpdateSession, data));
+  mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
+}
 
-  nsRefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
-  nsAutoCString str(NS_LITERAL_CSTRING("Update_"));
-  str.AppendInt(sUpdateCount++);
-  nsTArray<uint8_t> msg;
-  msg.AppendElements(str.get(), str.Length());
-  session->DispatchKeyMessage(msg, NS_LITERAL_STRING("http://bogus.url"));
-  ResolvePromise(aPromiseId);
+void
+CDMProxy::gmp_UpdateSession(nsAutoPtr<UpdateSessionData> aData)
+{
+  MOZ_ASSERT(IsOnGMPThread());
+  if (!mCDM) {
+    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
+    return;
+  }
+  mCDM->UpdateSession(aData->mPromiseId,
+                      aData->mSessionId,
+                      aData->mResponse);
 }
 
 void
 CDMProxy::CloseSession(const nsAString& aSessionId,
                        PromiseId aPromiseId)
 {
   MOZ_ASSERT(NS_IsMainThread());
   NS_ENSURE_TRUE_VOID(!mKeys.IsNull());
 
-  // TODO: Dispatch task to GMPThread to call CDM CloseSession via IPC.
-
-  nsRefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
+  {
+    CDMCaps::AutoLock caps(Capabilites());
+    caps.DropKeysForSession(aSessionId);
+  }
+  nsAutoPtr<SessionOpData> data(new SessionOpData());
+  data->mPromiseId = aPromiseId;
+  data->mSessionId = NS_ConvertUTF16toUTF8(aSessionId);
+  nsRefPtr<nsIRunnable> task(
+    NS_NewRunnableMethodWithArg<nsAutoPtr<SessionOpData>>(this, &CDMProxy::gmp_CloseSession, data));
+  mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
+}
 
-  // Pretend that the CDM actually does close the session...
-  // "...the [MediaKeySession's] closed attribute promise is resolved
-  // when the session is closed."
-  session->OnClosed();
-
-  // "The promise is resolved when the request has been processed."
-  ResolvePromise(aPromiseId);
+void
+CDMProxy::gmp_CloseSession(nsAutoPtr<SessionOpData> aData)
+{
+  MOZ_ASSERT(IsOnGMPThread());
+  if (!mCDM) {
+    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
+    return;
+  }
+  mCDM->CloseSession(aData->mPromiseId, aData->mSessionId);
 }
 
 void
 CDMProxy::RemoveSession(const nsAString& aSessionId,
                         PromiseId aPromiseId)
 {
   MOZ_ASSERT(NS_IsMainThread());
-
-  // TODO: Dispatch task to GMPThread to call CDM RemoveSession via IPC.
+  NS_ENSURE_TRUE_VOID(!mKeys.IsNull());
 
-  // Assume CDM immediately removes session's data, then close the session
-  // as per the spec.
-  CloseSession(aSessionId, aPromiseId);
+  {
+    CDMCaps::AutoLock caps(Capabilites());
+    caps.DropKeysForSession(aSessionId);
+  }
+  nsAutoPtr<SessionOpData> data(new SessionOpData());
+  data->mPromiseId = aPromiseId;
+  data->mSessionId = NS_ConvertUTF16toUTF8(aSessionId);
+  nsRefPtr<nsIRunnable> task(
+    NS_NewRunnableMethodWithArg<nsAutoPtr<SessionOpData>>(this, &CDMProxy::gmp_RemoveSession, data));
+  mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
+}
+
+void
+CDMProxy::gmp_RemoveSession(nsAutoPtr<SessionOpData> aData)
+{
+  MOZ_ASSERT(IsOnGMPThread());
+  if (!mCDM) {
+    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
+    return;
+  }
+  mCDM->RemoveSession(aData->mPromiseId, aData->mSessionId);
 }
 
 void
 CDMProxy::Shutdown()
 {
   MOZ_ASSERT(NS_IsMainThread());
   mKeys.Clear();
 }
@@ -191,9 +332,163 @@ CDMProxy::ResolvePromise(PromiseId aId)
     nsRefPtr<nsIRunnable> task;
     task = NS_NewRunnableMethodWithArg<PromiseId>(this,
                                                   &CDMProxy::ResolvePromise,
                                                   aId);
     NS_DispatchToMainThread(task);
   }
 }
 
+const nsAString&
+CDMProxy::GetOrigin() const
+{
+  return mOrigin;
+}
+
+void
+CDMProxy::OnResolveNewSessionPromise(uint32_t aPromiseId,
+                                     const nsAString& aSessionId)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  mKeys->OnSessionCreated(aPromiseId, aSessionId);
+}
+
+void
+CDMProxy::OnSessionMessage(const nsAString& aSessionId,
+                           nsTArray<uint8_t>& aMessage,
+                           const nsAString& aDestinationURL)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  nsRefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
+  if (session) {
+    session->DispatchKeyMessage(aMessage, aDestinationURL);
+  }
+}
+
+void
+CDMProxy::OnExpirationChange(const nsAString& aSessionId,
+                             GMPTimestamp aExpiryTime)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  NS_WARNING("CDMProxy::OnExpirationChange() not implemented");
+}
+
+void
+CDMProxy::OnSessionClosed(const nsAString& aSessionId)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  NS_WARNING("CDMProxy::OnSessionClosed() not implemented");
+}
+
+static void
+LogToConsole(const nsAString& aMsg)
+{
+  nsCOMPtr<nsIConsoleService> console(
+    do_GetService("@mozilla.org/consoleservice;1"));
+  if (!console) {
+    NS_WARNING("Failed to log message to console.");
+    return;
+  }
+  nsAutoString msg(aMsg);
+  console->LogStringMessage(msg.get());
+}
+
+void
+CDMProxy::OnSessionError(const nsAString& aSessionId,
+                         nsresult aException,
+                         uint32_t aSystemCode,
+                         const nsAString& aMsg)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  nsRefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
+  if (session) {
+    session->DispatchKeyError(aSystemCode);
+  }
+  LogToConsole(aMsg);
+}
+
+void
+CDMProxy::OnRejectPromise(uint32_t aPromiseId,
+                          nsresult aDOMException,
+                          const nsAString& aMsg)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  RejectPromise(aPromiseId, aDOMException);
+  LogToConsole(aMsg);
+}
+
+const nsString&
+CDMProxy::KeySystem() const
+{
+  return mKeySystem;
+}
+
+CDMCaps&
+CDMProxy::Capabilites() {
+  return mCapabilites;
+}
+
+void
+CDMProxy::Decrypt(mp4_demuxer::MP4Sample* aSample,
+                  DecryptionClient* aClient)
+{
+  nsAutoPtr<DecryptJob> job(new DecryptJob(aSample, aClient));
+  nsRefPtr<nsIRunnable> task(
+    NS_NewRunnableMethodWithArg<nsAutoPtr<DecryptJob>>(this, &CDMProxy::gmp_Decrypt, job));
+  mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
+}
+
+void
+CDMProxy::gmp_Decrypt(nsAutoPtr<DecryptJob> aJob)
+{
+  MOZ_ASSERT(IsOnGMPThread());
+  MOZ_ASSERT(aJob->mClient);
+  MOZ_ASSERT(aJob->mSample);
+
+  if (!mCDM) {
+    aJob->mClient->Decrypted(NS_ERROR_FAILURE, 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);
+  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) {
+        NS_WARNING("CDM returned incorrect number of decrypted bytes");
+      }
+      PodCopy(job->mSample->data,
+              aDecryptedData.Elements(),
+              std::min<size_t>(aDecryptedData.Length(), job->mSample->size));
+      nsresult rv = GMP_SUCCEEDED(aResult) ? NS_OK : NS_ERROR_FAILURE;
+      job->mClient->Decrypted(rv, job->mSample.forget());
+      mDecryptionJobs.RemoveElementAt(i);
+      return;
+    } else {
+      NS_WARNING("GMPDecryptorChild returned incorrect job ID");
+    }
+  }
+}
+
+void
+CDMProxy::gmp_Terminated()
+{
+  MOZ_ASSERT(IsOnGMPThread());
+  EME_LOG("CDM terminated");
+  if (mCDM) {
+    mCDM->Close();
+    mCDM = nullptr;
+  }
+}
+
 } // namespace mozilla
--- a/content/media/eme/CDMProxy.h
+++ b/content/media/eme/CDMProxy.h
@@ -4,32 +4,40 @@
  * 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 CDMProxy_h_
 #define CDMProxy_h_
 
 #include "nsString.h"
 #include "nsAutoPtr.h"
-#include "nsProxyRelease.h"
 #include "mozilla/dom/MediaKeys.h"
 #include "mozilla/dom/TypedArray.h"
-
-class nsIThread;
+#include "mozilla/Monitor.h"
+#include "nsIThread.h"
+#include "GMPDecryptorProxy.h"
+#include "mozilla/CDMCaps.h"
+#include "mp4_demuxer/DecoderData.h"
 
 namespace mozilla {
 
+class CDMCallbackProxy;
+
 namespace dom {
 class MediaKeySession;
 }
 
-// A placeholder proxy to the CDM.
-// TODO: The functions here need to do IPC to talk to the CDM via the
-// Gecko Media Plugin API, which we'll need to extend for H.264 and EME
-// content.
+class DecryptionClient {
+public:
+  virtual ~DecryptionClient() {}
+  virtual void Decrypted(nsresult aResult,
+                         mp4_demuxer::MP4Sample* 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;
   typedef dom::SessionType SessionType;
   typedef dom::Uint8Array Uint8Array;
 public:
@@ -90,18 +98,134 @@ public:
   // Calls MediaKeys->ResolvePromise(aPromiseId) after the CDM has
   // processed the request.
   void RemoveSession(const nsAString& aSessionId,
                      PromiseId aPromiseId);
 
   // Main thread only.
   void Shutdown();
 
+  // Threadsafe.
+  const nsAString& GetOrigin() const;
+
+  // Main thread only.
+  void OnResolveNewSessionPromise(uint32_t aPromiseId,
+                                  const nsAString& aSessionId);
+
+  // Main thread only.
+  void OnSessionMessage(const nsAString& aSessionId,
+                        nsTArray<uint8_t>& aMessage,
+                        const nsAString& aDestinationURL);
+
+  // Main thread only.
+  void OnExpirationChange(const nsAString& aSessionId,
+                          GMPTimestamp aExpiryTime);
+
+  // Main thread only.
+  void OnSessionClosed(const nsAString& aSessionId);
+
+  // Main thread only.
+  void OnSessionError(const nsAString& aSessionId,
+                      nsresult aException,
+                      uint32_t aSystemCode,
+                      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);
+
+  // 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);
+
+  // Threadsafe.
+  const nsString& KeySystem() const;
+
+  // GMP thread only.
+  void gmp_Decrypted(uint32_t aId,
+                     GMPErr aResult,
+                     const nsTArray<uint8_t>& aDecryptedData);
+
+  // GMP thread only.
+  void gmp_Terminated();
+
+  CDMCaps& Capabilites();
+
+#ifdef DEBUG
+  bool IsOnGMPThread();
+#endif
+
 private:
 
+  // GMP thread only.
+  void gmp_Init(uint32_t aPromiseId);
+
+  // Main thread only.
+  void OnCDMCreated(uint32_t aPromiseId);
+
+  struct CreateSessionData {
+    dom::SessionType mSessionType;
+    PromiseId mPromiseId;
+    nsAutoCString mInitDataType;
+    nsTArray<uint8_t> mInitData;
+  };
+  // GMP thread only.
+  void gmp_CreateSession(nsAutoPtr<CreateSessionData> aData);
+
+  struct SessionOpData {
+    PromiseId mPromiseId;
+    nsAutoCString mSessionId;
+  };
+  // GMP thread only.
+  void gmp_LoadSession(nsAutoPtr<SessionOpData> aData);
+
+  struct SetServerCertificateData {
+    PromiseId mPromiseId;
+    nsTArray<uint8_t> mCert;
+  };
+  // GMP thread only.
+  void gmp_SetServerCertificate(nsAutoPtr<SetServerCertificateData> aData);
+
+  struct UpdateSessionData {
+    PromiseId mPromiseId;
+    nsAutoCString mSessionId;
+    nsTArray<uint8_t> mResponse;
+  };
+  // GMP thread only.
+  void gmp_UpdateSession(nsAutoPtr<UpdateSessionData> aData);
+
+  // 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)
+      : mId(0)
+      , mSample(aSample)
+      , mClient(aClient)
+    {}
+    uint32_t mId;
+    nsAutoPtr<mp4_demuxer::MP4Sample> mSample;
+    nsAutoPtr<DecryptionClient> mClient;
+  };
+  // GMP thread only.
+  void gmp_Decrypt(nsAutoPtr<DecryptJob> aJob);
+
   class RejectPromiseTask : public nsRunnable {
   public:
     RejectPromiseTask(CDMProxy* aProxy,
                       PromiseId aId,
                       nsresult aCode)
       : mProxy(aProxy)
       , mId(aId)
       , mCode(aCode)
@@ -112,23 +236,16 @@ private:
       return NS_OK;
     }
   private:
     nsRefPtr<CDMProxy> mProxy;
     PromiseId mId;
     nsresult mCode;
   };
 
-  // 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);
-
   ~CDMProxy();
 
   // Helper to enforce that a raw pointer is only accessed on the main thread.
   template<class Type>
   class MainThreadOnlyRawPtr {
   public:
     MainThreadOnlyRawPtr(Type* aPtr)
       : mPtr(aPtr)
@@ -159,13 +276,31 @@ private:
   // destructor. only use on main thread, and always nullcheck before using!
   MainThreadOnlyRawPtr<dom::MediaKeys> mKeys;
 
   const nsAutoString mKeySystem;
 
   // Gecko Media Plugin thread. All interactions with the out-of-process
   // EME plugin must come from this thread.
   nsRefPtr<nsIThread> mGMPThread;
+
+  nsAutoString mOrigin;
+
+  GMPDecryptorProxy* mCDM;
+  CDMCaps mCapabilites;
+  nsAutoPtr<CDMCallbackProxy> mCallback;
+
+  // Decryption jobs sent to CDM, awaiting result.
+  // GMP thread only.
+  nsTArray<nsAutoPtr<DecryptJob>> mDecryptionJobs;
+
+  // Number of buffers we've decrypted. Used to uniquely identify
+  // decryption jobs sent to CDM. Note we can't just use the length of
+  // mDecryptionJobs as that shrinks as jobs are completed and removed
+  // from it.
+  // GMP thread only.
+  uint32_t mDecryptionJobCount;
 };
 
+
 } // namespace mozilla
 
 #endif // CDMProxy_h_
--- a/content/media/eme/EMELog.h
+++ b/content/media/eme/EMELog.h
@@ -1,35 +1,48 @@
 /* -*- 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/. */
 
+#ifndef EME_LOG_H_
+#define EME_LOG_H_
+
 #include "prlog.h"
 
 namespace mozilla {
 
 #ifdef PR_LOGGING
 
-#ifndef EME_LOG
-PRLogModuleInfo* GetEMELog();
-#define EME_LOG(...) PR_LOG(GetEMELog(), PR_LOG_DEBUG, (__VA_ARGS__))
-#endif
+  #ifndef EME_LOG
+    PRLogModuleInfo* GetEMELog();
+    #define EME_LOG(...) PR_LOG(GetEMELog(), PR_LOG_DEBUG, (__VA_ARGS__))
+  #endif
 
-#ifndef EME_VERBOSE_LOG
-PRLogModuleInfo* GetEMEVerboseLog();
-#define EME_VERBOSE_LOG(...) PR_LOG(GetEMEVerboseLog(), PR_LOG_DEBUG, (__VA_ARGS__))
+  #ifndef EME_VERBOSE_LOG
+    PRLogModuleInfo* GetEMEVerboseLog();
+    #define EME_VERBOSE_LOG(...) PR_LOG(GetEMEVerboseLog(), PR_LOG_DEBUG, (__VA_ARGS__))
+  #else
+    #ifndef EME_LOG
+      #define EME_LOG(...)
+    #endif
+
+    #ifndef EME_VERBOSE_LOG
+      #define EME_VERBOSE_LOG(...)
+    #endif
+  #endif
 
 #else
 
-#ifndef EME_LOG
-#define EME_LOG(...)
-#endif
+  #ifndef EME_LOG
+    #define EME_LOG(...)
+  #endif
 
-#ifndef EME_VERBOSE_LOG
-#define EME_VERBOSE_LOG(...)
-#endif
-
-#endif
+  #ifndef EME_VERBOSE_LOG
+    #define EME_VERBOSE_LOG(...)
+  #endif
 
 #endif // PR_LOGGING
+
 } // namespace mozilla
+
+#endif // EME_LOG_H_
--- a/content/media/eme/MediaKeyError.cpp
+++ b/content/media/eme/MediaKeyError.cpp
@@ -11,16 +11,17 @@
 namespace mozilla {
 namespace dom {
 
 MediaKeyError::MediaKeyError(EventTarget* aOwner, uint32_t aSystemCode)
   : Event(aOwner, nullptr, nullptr)
   , mSystemCode(aSystemCode)
 {
   SetIsDOMBinding();
+  InitEvent(NS_LITERAL_STRING("error"), false, false);
 }
 
 MediaKeyError::~MediaKeyError()
 {
 }
 
 uint32_t
 MediaKeyError::SystemCode() const
--- a/content/media/eme/MediaKeySession.cpp
+++ b/content/media/eme/MediaKeySession.cpp
@@ -62,17 +62,23 @@ void
 MediaKeySession::GetKeySystem(nsString& aKeySystem) const
 {
   aKeySystem = mKeySystem;
 }
 
 void
 MediaKeySession::GetSessionId(nsString& aSessionId) const
 {
-  aSessionId = mSessionId;
+  aSessionId = GetSessionId();
+}
+
+const nsString&
+MediaKeySession::GetSessionId() const
+{
+  return mSessionId;
 }
 
 JSObject*
 MediaKeySession::WrapObject(JSContext* aCx)
 {
   return MediaKeySessionBinding::Wrap(aCx, this);
 }
 
@@ -161,17 +167,17 @@ MediaKeySession::Remove(ErrorResult& aRv
     return promise.forget();
   }
   mKeys->GetCDMProxy()->RemoveSession(mSessionId, mKeys->StorePromise(promise));
   return promise.forget();
 }
 
 void
 MediaKeySession::DispatchKeyMessage(const nsTArray<uint8_t>& aMessage,
-                                    const nsString& aURL)
+                                    const nsAString& aURL)
 {
   nsRefPtr<MediaKeyMessageEvent> event(
     MediaKeyMessageEvent::Constructor(this, aURL, aMessage));
   nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
     new AsyncEventDispatcher(this, event);
   asyncDispatcher->PostDOMEvent();
 }
 
--- a/content/media/eme/MediaKeySession.h
+++ b/content/media/eme/MediaKeySession.h
@@ -48,32 +48,34 @@ public:
 
   // Mark this as resultNotAddRefed to return raw pointers
   MediaKeyError* GetError() const;
 
   void GetKeySystem(nsString& aRetval) const;
 
   void GetSessionId(nsString& aRetval) const;
 
+  const nsString& GetSessionId() const;
+
   // Number of ms since epoch at which expiration occurs, or NaN if unknown.
   // TODO: The type of this attribute is still under contention.
   // https://www.w3.org/Bugs/Public/show_bug.cgi?id=25902
   double Expiration() const;
 
   Promise* Closed() const;
 
   already_AddRefed<Promise> Update(const Uint8Array& response,
                                    ErrorResult& aRv);
 
   already_AddRefed<Promise> Close(ErrorResult& aRv);
 
   already_AddRefed<Promise> Remove(ErrorResult& aRv);
 
   void DispatchKeyMessage(const nsTArray<uint8_t>& aMessage,
-                          const nsString& aURL);
+                          const nsAString& aURL);
 
   void DispatchKeyError(uint32_t system_code);
 
   void OnClosed();
 
   bool IsClosed() const;
 
 private:
--- a/content/media/eme/MediaKeys.cpp
+++ b/content/media/eme/MediaKeys.cpp
@@ -7,18 +7,23 @@
 #include "mozilla/dom/HTMLMediaElement.h"
 #include "mozilla/dom/MediaKeys.h"
 #include "mozilla/dom/MediaKeysBinding.h"
 #include "mozilla/dom/MediaKeyMessageEvent.h"
 #include "mozilla/dom/MediaKeyError.h"
 #include "mozilla/dom/MediaKeySession.h"
 #include "mozilla/dom/DOMException.h"
 #include "mozilla/CDMProxy.h"
+#include "mozilla/EMELog.h"
 #include "nsContentUtils.h"
-#include "EMELog.h"
+#include "nsIScriptObjectPrincipal.h"
+#include "mozilla/Preferences.h"
+#ifdef XP_WIN
+#include "mozilla/WindowsVersion.h"
+#endif
 
 namespace mozilla {
 
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(MediaKeys,
                                       mParent,
                                       mKeySessions,
@@ -27,18 +32,19 @@ NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Me
 NS_IMPL_CYCLE_COLLECTING_ADDREF(MediaKeys)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(MediaKeys)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaKeys)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 MediaKeys::MediaKeys(nsPIDOMWindow* aParent, const nsAString& aKeySystem)
-  : mParent(aParent),
-    mKeySystem(aKeySystem)
+  : mParent(aParent)
+  , mKeySystem(aKeySystem)
+  , mCreatePromiseId(0)
 {
   SetIsDOMBinding();
 }
 
 MediaKeys::~MediaKeys()
 {
   if (mProxy) {
     mProxy->Shutdown();
@@ -71,28 +77,40 @@ MediaKeys::SetServerCertificate(const Ui
   nsRefPtr<Promise> promise(MakePromise(aRv));
   if (aRv.Failed()) {
     return nullptr;
   }
   mProxy->SetServerCertificate(StorePromise(promise), aCert);
   return promise.forget();
 }
 
+static bool
+IsSupportedKeySystem(const nsAString& aKeySystem)
+{
+  return aKeySystem.EqualsASCII("org.w3.clearkey") ||
+#ifdef XP_WIN
+         (aKeySystem.EqualsASCII("com.adobe.access") &&
+          IsVistaOrLater() &&
+          Preferences::GetBool("media.eme.adobe-access.enabled", false)) ||
+#endif
+         false;
+}
+
 /* static */
 IsTypeSupportedResult
 MediaKeys::IsTypeSupported(const GlobalObject& aGlobal,
                            const nsAString& aKeySystem,
                            const Optional<nsAString>& aInitDataType,
                            const Optional<nsAString>& aContentType,
                            const Optional<nsAString>& aCapability)
 {
-  // TODO: Query list of known CDMs and their supported content types.
   // TODO: Should really get spec changed to this is async, so we can wait
   //       for user to consent to running plugin.
-  return IsTypeSupportedResult::Maybe;
+  return IsSupportedKeySystem(aKeySystem) ? IsTypeSupportedResult::Maybe
+                                          : IsTypeSupportedResult::_empty;
 }
 
 already_AddRefed<Promise>
 MediaKeys::MakePromise(ErrorResult& aRv)
 {
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
   if (!global) {
     NS_WARNING("Passed non-global to MediaKeys ctor!");
@@ -110,16 +128,17 @@ MediaKeys::StorePromise(Promise* aPromis
   uint32_t id = sEMEPromiseCount++;
   mPromises.Put(id, aPromise);
   return id;
 }
 
 already_AddRefed<Promise>
 MediaKeys::RetrievePromise(PromiseId aId)
 {
+  MOZ_ASSERT(mPromises.Contains(aId));
   nsRefPtr<Promise> promise;
   mPromises.Remove(aId, getter_AddRefs(promise));
   return promise.forget();
 }
 
 void
 MediaKeys::RejectPromise(PromiseId aId, nsresult aExceptionCode)
 {
@@ -133,30 +152,49 @@ MediaKeys::RejectPromise(PromiseId aId, 
     // so we might have a pending session waiting to be resolved into
     // the promise on success. We've been directed to reject to promise,
     // so we can throw away the corresponding session object.
     mPendingSessions.Remove(aId);
   }
 
   MOZ_ASSERT(NS_FAILED(aExceptionCode));
   promise->MaybeReject(aExceptionCode);
+
+  if (mCreatePromiseId == aId) {
+    // Note: This will probably destroy the MediaKeys object!
+    Release();
+  }
 }
 
 void
 MediaKeys::ResolvePromise(PromiseId aId)
 {
   nsRefPtr<Promise> promise(RetrievePromise(aId));
   if (!promise) {
     NS_WARNING("MediaKeys tried to resolve a non-existent promise");
     return;
   }
-  // We should not resolve CreateSession or LoadSession calls via this path,
-  // OnSessionActivated() should be called instead.
-  MOZ_ASSERT(!mPendingSessions.Contains(aId));
-  promise->MaybeResolve(JS::UndefinedHandleValue);
+  if (mPendingSessions.Contains(aId)) {
+    // We should only resolve LoadSession calls via this path,
+    // not CreateSession() promises.
+    nsRefPtr<MediaKeySession> session;
+    if (!mPendingSessions.Get(aId, getter_AddRefs(session)) ||
+        !session ||
+        session->GetSessionId().IsEmpty()) {
+      NS_WARNING("Received activation for non-existent session!");
+      promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
+      mPendingSessions.Remove(aId);
+      return;
+    }
+    mPendingSessions.Remove(aId);
+    mKeySessions.Put(session->GetSessionId(), session);
+    promise->MaybeResolve(session);
+  } else {
+    promise->MaybeResolve(JS::UndefinedHandleValue);
+  }
 }
 
 /* static */
 already_AddRefed<Promise>
 MediaKeys::Create(const GlobalObject& aGlobal,
                   const nsAString& aKeySystem,
                   ErrorResult& aRv)
 {
@@ -169,37 +207,52 @@ MediaKeys::Create(const GlobalObject& aG
   }
 
   nsRefPtr<MediaKeys> keys = new MediaKeys(window, aKeySystem);
   nsRefPtr<Promise> promise(keys->MakePromise(aRv));
   if (aRv.Failed()) {
     return nullptr;
   }
 
-  if (!aKeySystem.EqualsASCII("org.w3.clearkey")) {
+  if (!IsSupportedKeySystem(aKeySystem)) {
     aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
     return nullptr;
   }
 
   keys->mProxy = new CDMProxy(keys, aKeySystem);
-  keys->mProxy->Init(keys->StorePromise(promise));
+
+  // The CDMProxy's initialization is asynchronous. The MediaKeys is
+  // refcounted, and its instance is returned to JS by promise once
+  // it's been initialized. No external refs exist to the MediaKeys while
+  // we're waiting for the promise to be resolved, so we must hold a
+  // reference to the new MediaKeys object until it's been created,
+  // or its creation has failed. Store the id of the promise returned
+  // here, and hold a self-reference until that promise is resolved or
+  // rejected.
+  MOZ_ASSERT(!keys->mCreatePromiseId, "Should only be created once!");
+  keys->mCreatePromiseId = keys->StorePromise(promise);
+  keys->AddRef();
+  keys->mProxy->Init(keys->mCreatePromiseId);
 
   return promise.forget();
 }
 
 void
 MediaKeys::OnCDMCreated(PromiseId aId)
 {
   nsRefPtr<Promise> promise(RetrievePromise(aId));
   if (!promise) {
     NS_WARNING("MediaKeys tried to resolve a non-existent promise");
     return;
   }
   nsRefPtr<MediaKeys> keys(this);
   promise->MaybeResolve(keys);
+  if (mCreatePromiseId == aId) {
+    Release();
+  }
 }
 
 already_AddRefed<Promise>
 MediaKeys::LoadSession(const nsAString& aSessionId, ErrorResult& aRv)
 {
   nsRefPtr<Promise> promise(MakePromise(aRv));
   if (aRv.Failed()) {
     return nullptr;
@@ -219,19 +272,20 @@ MediaKeys::LoadSession(const nsAString& 
 
   // Create session.
   nsRefPtr<MediaKeySession> session(
     new MediaKeySession(GetParentObject(), this, mKeySystem, SessionType::Persistent, aRv));
   if (aRv.Failed()) {
     return nullptr;
   }
 
-  // Proxy owns session object until resolving promise.
-  mProxy->LoadSession(StorePromise(promise),
-                      aSessionId);
+  session->Init(aSessionId);
+  auto pid = StorePromise(promise);
+  mPendingSessions.Put(pid, session);
+  mProxy->LoadSession(pid, aSessionId);
 
   return promise.forget();
 }
 
 already_AddRefed<Promise>
 MediaKeys::CreateSession(const nsAString& initDataType,
                          const Uint8Array& aInitData,
                          SessionType aSessionType,
@@ -257,36 +311,37 @@ MediaKeys::CreateSession(const nsAString
                         pid,
                         initDataType,
                         aInitData);
 
   return promise.forget();
 }
 
 void
-MediaKeys::OnSessionActivated(PromiseId aId, const nsAString& aSessionId)
+MediaKeys::OnSessionCreated(PromiseId aId, const nsAString& aSessionId)
 {
   nsRefPtr<Promise> promise(RetrievePromise(aId));
   if (!promise) {
     NS_WARNING("MediaKeys tried to resolve a non-existent promise");
     return;
   }
   MOZ_ASSERT(mPendingSessions.Contains(aId));
 
   nsRefPtr<MediaKeySession> session;
-  if (!mPendingSessions.Get(aId, getter_AddRefs(session)) || !session) {
+  bool gotSession = mPendingSessions.Get(aId, getter_AddRefs(session));
+  // Session has completed creation/loading, remove it from mPendingSessions,
+  // and resolve the promise with it. We store it in mKeySessions, so we can
+  // find it again if we need to send messages to it etc.
+  mPendingSessions.Remove(aId);
+  if (!gotSession || !session) {
     NS_WARNING("Received activation for non-existent session!");
     promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
     return;
   }
 
-  // Session has completed creation/loading, remove it from mPendingSessions,
-  // and resolve the promise with it. We store it in mKeySessions, so we can
-  // find it again if we need to send messages to it etc.
-  mPendingSessions.Remove(aId);
   session->Init(aSessionId);
   mKeySessions.Put(aSessionId, session);
   promise->MaybeResolve(session);
 }
 
 void
 MediaKeys::OnSessionClosed(MediaKeySession* aSession)
 {
@@ -298,10 +353,32 @@ MediaKeys::OnSessionClosed(MediaKeySessi
 already_AddRefed<MediaKeySession>
 MediaKeys::GetSession(const nsAString& aSessionId)
 {
   nsRefPtr<MediaKeySession> session;
   mKeySessions.Get(aSessionId, getter_AddRefs(session));
   return session.forget();
 }
 
+nsresult
+MediaKeys::GetOrigin(nsString& aOutOrigin)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  // TODO: Bug 1035637, return a combination of origin and URL bar origin.
+
+  nsIPrincipal* principal = nullptr;
+  nsCOMPtr<nsPIDOMWindow> pWindow = do_QueryInterface(GetParentObject());
+  nsCOMPtr<nsIScriptObjectPrincipal> scriptPrincipal =
+    do_QueryInterface(pWindow);
+  if (scriptPrincipal) {
+    principal = scriptPrincipal->GetPrincipal();
+  }
+  NS_ENSURE_TRUE(principal, NS_ERROR_FAILURE);
+
+  nsresult res = nsContentUtils::GetUTFOrigin(principal, aOutOrigin);
+
+  EME_LOG("EME Origin = '%s'", NS_ConvertUTF16toUTF8(aOutOrigin).get());
+
+  return res;
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/content/media/eme/MediaKeys.h
+++ b/content/media/eme/MediaKeys.h
@@ -78,17 +78,17 @@ public:
                                                const Optional<nsAString>& aContentType,
                                                const Optional<nsAString>& aCapability);
 
   already_AddRefed<MediaKeySession> GetSession(const nsAString& aSessionId);
 
   // Called once a Create() operation succeeds.
   void OnCDMCreated(PromiseId aId);
   // Called once a CreateSession or LoadSession succeeds.
-  void OnSessionActivated(PromiseId aId, const nsAString& aSessionId);
+  void OnSessionCreated(PromiseId aId, const nsAString& aSessionId);
   // Called once a session has closed.
   void OnSessionClosed(MediaKeySession* aSession);
 
   CDMProxy* GetCDMProxy() { return mProxy; }
 
   // Makes a new promise, or nullptr on failure.
   already_AddRefed<Promise> MakePromise(ErrorResult& aRv);
   // Stores promise in mPromises, returning an ID that can be used to retrieve
@@ -96,28 +96,31 @@ public:
   // promises to be resolved.
   PromiseId StorePromise(Promise* aPromise);
 
   // Reject promise with DOMException corresponding to aExceptionCode.
   void RejectPromise(PromiseId aId, nsresult aExceptionCode);
   // Resolves promise with "undefined".
   void ResolvePromise(PromiseId aId);
 
+  nsresult GetOrigin(nsString& aOutOrigin);
+
 private:
 
   // Removes promise from mPromises, and returns it.
   already_AddRefed<Promise> RetrievePromise(PromiseId aId);
 
   // Owning ref to proxy. The proxy has a weak reference back to the MediaKeys,
   // and the MediaKeys destructor clears the proxy's reference to the MediaKeys.
   nsRefPtr<CDMProxy> mProxy;
 
   nsCOMPtr<nsPIDOMWindow> mParent;
   nsString mKeySystem;
   KeySessionHashMap mKeySessions;
   PromiseHashMap mPromises;
   PendingKeySessionsHashMap mPendingSessions;
+  PromiseId mCreatePromiseId;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_mediakeys_h__
--- a/content/media/eme/moz.build
+++ b/content/media/eme/moz.build
@@ -8,20 +8,25 @@ EXPORTS.mozilla.dom += [
     'MediaKeyError.h',
     'MediaKeyMessageEvent.h',
     'MediaKeyNeededEvent.h',
     'MediaKeys.h',
     'MediaKeySession.h',
 ]
 
 EXPORTS.mozilla += [
+    'CDMCallbackProxy.h',
+    'CDMCaps.h',
     'CDMProxy.h',
+    'EMELog.h'
 ]
 
 UNIFIED_SOURCES += [
+    'CDMCallbackProxy.cpp',
+    'CDMCaps.cpp',
     'CDMProxy.cpp',
     'EMELog.cpp',
     'MediaKeyError.cpp',
     'MediaKeyMessageEvent.cpp',
     'MediaKeyNeededEvent.cpp',
     'MediaKeys.cpp',
     'MediaKeySession.cpp',
 ]
--- a/content/media/fmp4/MP4Decoder.cpp
+++ b/content/media/fmp4/MP4Decoder.cpp
@@ -3,31 +3,50 @@
 /* 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 "MP4Decoder.h"
 #include "MP4Reader.h"
 #include "MediaDecoderStateMachine.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/CDMProxy.h"
+#include "prlog.h"
 
 #ifdef XP_WIN
 #include "mozilla/WindowsVersion.h"
 #endif
 #ifdef MOZ_FFMPEG
 #include "FFmpegRuntimeLinker.h"
 #endif
 
 namespace mozilla {
 
 MediaDecoderStateMachine* MP4Decoder::CreateStateMachine()
 {
   return new MediaDecoderStateMachine(this, new MP4Reader(this));
 }
 
+nsresult
+MP4Decoder::SetCDMProxy(CDMProxy* aProxy)
+{
+  nsresult rv = MediaDecoder::SetCDMProxy(aProxy);
+  NS_ENSURE_SUCCESS(rv, rv);
+  {
+    // The MP4Reader can't decrypt EME content until it has a CDMProxy,
+    // and the CDMProxy knows the capabilities of the CDM. The MP4Reader
+    // remains in "waiting for resources" state until then.
+    CDMCaps::AutoLock caps(aProxy->Capabilites());
+    nsRefPtr<nsIRunnable> task(
+      NS_NewRunnableMethod(this, &MediaDecoder::NotifyWaitingForResourcesStatusChanged));
+    caps.CallOnMainThreadWhenCapsAvailable(task);
+  }
+  return NS_OK;
+}
+
 bool
 MP4Decoder::GetSupportedCodecs(const nsACString& aType,
                                char const *const ** aCodecList)
 {
   if (!IsEnabled()) {
     return false;
   }
 
--- a/content/media/fmp4/MP4Decoder.h
+++ b/content/media/fmp4/MP4Decoder.h
@@ -19,16 +19,18 @@ public:
     if (!IsEnabled()) {
       return nullptr;
     }
     return new MP4Decoder();
   }
 
   virtual MediaDecoderStateMachine* CreateStateMachine();
 
+  virtual nsresult SetCDMProxy(CDMProxy* aProxy) MOZ_OVERRIDE;
+
   // Returns true if aType is a MIME type that we can render with the
   // a MP4 platform decoder backend. If aCodecList is non null,
   // it is filled with a (static const) null-terminated list of strings
   // denoting the codecs we'll playback.
   static bool GetSupportedCodecs(const nsACString& aType,
                                  char const *const ** aCodecList);
 
   // Returns true if the MP4 backend is preffed on, and we're running on a
--- a/content/media/fmp4/MP4Reader.cpp
+++ b/content/media/fmp4/MP4Reader.cpp
@@ -100,16 +100,18 @@ private:
 };
 
 MP4Reader::MP4Reader(AbstractMediaDecoder* aDecoder)
   : MediaDecoderReader(aDecoder)
   , mAudio("MP4 audio decoder data", Preferences::GetUint("media.mp4-audio-decode-ahead", 2))
   , mVideo("MP4 video decoder data", Preferences::GetUint("media.mp4-video-decode-ahead", 2))
   , mLastReportedNumDecodedFrames(0)
   , mLayersBackendType(layers::LayersBackend::LAYERS_NONE)
+  , mDemuxerInitialized(false)
+  , mIsEncrypted(false)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
   MOZ_COUNT_CTOR(MP4Reader);
 }
 
 MP4Reader::~MP4Reader()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
@@ -163,16 +165,18 @@ MP4Reader::InitLayersBackendType()
 
   nsRefPtr<LayerManager> layerManager =
     nsContentUtils::LayerManagerForDocument(element->OwnerDoc());
   NS_ENSURE_TRUE_VOID(layerManager);
 
   mLayersBackendType = layerManager->GetCompositorBackendType();
 }
 
+static bool sIsEMEEnabled = false;
+
 nsresult
 MP4Reader::Init(MediaDecoderReader* aCloneDonor)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
   PlatformDecoderModule::Init();
   mDemuxer = new MP4Demuxer(new MP4Stream(mDecoder->GetResource()));
 
   InitLayersBackendType();
@@ -180,56 +184,171 @@ MP4Reader::Init(MediaDecoderReader* aClo
   mAudio.mTaskQueue = new MediaTaskQueue(
     SharedThreadPool::Get(NS_LITERAL_CSTRING("MP4 Audio Decode")));
   NS_ENSURE_TRUE(mAudio.mTaskQueue, NS_ERROR_FAILURE);
 
   mVideo.mTaskQueue = new MediaTaskQueue(
     SharedThreadPool::Get(NS_LITERAL_CSTRING("MP4 Video Decode")));
   NS_ENSURE_TRUE(mVideo.mTaskQueue, NS_ERROR_FAILURE);
 
+  static bool sSetupPrefCache = false;
+  if (!sSetupPrefCache) {
+    sSetupPrefCache = true;
+    Preferences::AddBoolVarCache(&sIsEMEEnabled, "media.eme.enabled", false);
+  }
+
   return NS_OK;
 }
 
+class DispatchKeyNeededEvent : public nsRunnable {
+public:
+  DispatchKeyNeededEvent(AbstractMediaDecoder* aDecoder,
+                         nsTArray<uint8_t>& aInitData,
+                         const nsString& aInitDataType)
+    : mDecoder(aDecoder)
+    , mInitData(aInitData)
+    , mInitDataType(aInitDataType)
+  {
+  }
+  NS_IMETHOD Run() {
+    // Note: Null check the owner, as the decoder could have been shutdown
+    // since this event was dispatched.
+    MediaDecoderOwner* owner = mDecoder->GetOwner();
+    if (owner) {
+      owner->DispatchNeedKey(mInitData, mInitDataType);
+    }
+    mDecoder = nullptr;
+    return NS_OK;
+  }
+private:
+  nsRefPtr<AbstractMediaDecoder> mDecoder;
+  nsTArray<uint8_t> mInitData;
+  nsString mInitDataType;
+};
+
+bool MP4Reader::IsWaitingMediaResources()
+{
+  nsRefPtr<CDMProxy> proxy;
+  {
+    ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
+    if (!mIsEncrypted) {
+      // Not encrypted, no need to wait for CDMProxy.
+      return false;
+    }
+    proxy = mDecoder->GetCDMProxy();
+    if (!proxy) {
+      // We're encrypted, we need a CDMProxy to decrypt file.
+      return true;
+    }
+  }
+  // We'll keep waiting if the CDM hasn't informed Gecko of its capabilities.
+  {
+    CDMCaps::AutoLock caps(proxy->Capabilites());
+    LOG("MP4Reader::IsWaitingMediaResources() capsKnown=%d", caps.AreCapsKnown());
+    return !caps.AreCapsKnown();
+  }
+}
+
+void
+MP4Reader::ExtractCryptoInitData(nsTArray<uint8_t>& aInitData)
+{
+  MOZ_ASSERT(mDemuxer->Crypto().valid);
+  const nsTArray<mp4_demuxer::PsshInfo>& psshs = mDemuxer->Crypto().pssh;
+  for (uint32_t i = 0; i < psshs.Length(); i++) {
+    aInitData.AppendElements(psshs[i].data);
+  }
+}
+
 nsresult
 MP4Reader::ReadMetadata(MediaInfo* aInfo,
                         MetadataTags** aTags)
 {
-  bool ok = mDemuxer->Init();
-  NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
+  if (!mDemuxerInitialized) {
+    bool ok = mDemuxer->Init();
+    NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
+
+    mInfo.mAudio.mHasAudio = mAudio.mActive = mDemuxer->HasValidAudio();
+    const AudioDecoderConfig& audio = mDemuxer->AudioConfig();
+    // If we have audio, we *only* allow AAC to be decoded.
+    if (mInfo.mAudio.mHasAudio && strcmp(audio.mime_type, "audio/mp4a-latm")) {
+      return NS_ERROR_FAILURE;
+    }
+
+    mInfo.mVideo.mHasVideo = mVideo.mActive = mDemuxer->HasValidVideo();
+    const VideoDecoderConfig& video = mDemuxer->VideoConfig();
+    // If we have video, we *only* allow H.264 to be decoded.
+    if (mInfo.mVideo.mHasVideo && strcmp(video.mime_type, "video/avc")) {
+      return NS_ERROR_FAILURE;
+    }
+
+    {
+      ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
+      mIsEncrypted = mDemuxer->Crypto().valid;
+    }
 
-  mInfo.mAudio.mHasAudio = mAudio.mActive = mDemuxer->HasValidAudio();
-  const AudioDecoderConfig& audio = mDemuxer->AudioConfig();
-  // If we have audio, we *only* allow AAC to be decoded.
-  if (mInfo.mAudio.mHasAudio && strcmp(audio.mime_type, "audio/mp4a-latm")) {
-    return NS_ERROR_FAILURE;
+    // Remember that we've initialized the demuxer, so that if we're decoding
+    // an encrypted stream and we need to wait for a CDM to be set, we don't
+    // need to reinit the demuxer.
+    mDemuxerInitialized = true;
+  }
+  if (mDemuxer->Crypto().valid) {
+    if (!sIsEMEEnabled) {
+      // TODO: Need to signal DRM/EME required somehow...
+      return NS_ERROR_FAILURE;
+    }
+
+    // We have encrypted audio or video. We'll need a CDM to decrypt and
+    // possibly decode this. Wait until we've received a CDM from the
+    // JavaScript player app.
+    nsRefPtr<CDMProxy> proxy;
+    nsTArray<uint8_t> initData;
+    ExtractCryptoInitData(initData);
+    if (initData.Length() == 0) {
+      return NS_ERROR_FAILURE;
+    }
+    if (!mInitDataEncountered.Contains(initData)) {
+      mInitDataEncountered.AppendElement(initData);
+      NS_DispatchToMainThread(new DispatchKeyNeededEvent(mDecoder, initData, NS_LITERAL_STRING("cenc")));
+    }
+    if (IsWaitingMediaResources()) {
+      return NS_OK;
+    }
+    MOZ_ASSERT(!IsWaitingMediaResources());
+
+    {
+      ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
+      proxy = mDecoder->GetCDMProxy();
+    }
+    MOZ_ASSERT(proxy);
+
+    mPlatform = PlatformDecoderModule::CreateCDMWrapper(proxy,
+                                                        HasAudio(),
+                                                        HasVideo(),
+                                                        GetTaskQueue());
+    NS_ENSURE_TRUE(mPlatform, NS_ERROR_FAILURE);
+  } else {
+    mPlatform = PlatformDecoderModule::Create();
+    NS_ENSURE_TRUE(mPlatform, NS_ERROR_FAILURE);
   }
 
-  mInfo.mVideo.mHasVideo = mVideo.mActive = mDemuxer->HasValidVideo();
-  const VideoDecoderConfig& video = mDemuxer->VideoConfig();
-  // If we have video, we *only* allow H.264 to be decoded.
-  if (mInfo.mVideo.mHasVideo && strcmp(video.mime_type, "video/avc")) {
-    return NS_ERROR_FAILURE;
-  }
-
-  mPlatform = PlatformDecoderModule::Create();
-  NS_ENSURE_TRUE(mPlatform, NS_ERROR_FAILURE);
-
   if (HasAudio()) {
+    const AudioDecoderConfig& audio = mDemuxer->AudioConfig();
     mInfo.mAudio.mRate = audio.samples_per_second;
     mInfo.mAudio.mChannels = audio.channel_count;
     mAudio.mCallback = new DecoderCallback(this, kAudio);
     mAudio.mDecoder = mPlatform->CreateAACDecoder(audio,
                                                   mAudio.mTaskQueue,
                                                   mAudio.mCallback);
     NS_ENSURE_TRUE(mAudio.mDecoder != nullptr, NS_ERROR_FAILURE);
     nsresult rv = mAudio.mDecoder->Init();
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   if (HasVideo()) {
+    const VideoDecoderConfig& video = mDemuxer->VideoConfig();
     mInfo.mVideo.mDisplay =
       nsIntSize(video.display_width, video.display_height);
     mVideo.mCallback = new  DecoderCallback(this, kVideo);
     mVideo.mDecoder = mPlatform->CreateH264Decoder(video,
                                                    mLayersBackendType,
                                                    mDecoder->GetImageContainer(),
                                                    mVideo.mTaskQueue,
                                                    mVideo.mCallback);
@@ -287,19 +406,19 @@ MP4Reader::Decoder(TrackType aTrack)
 MP4Sample*
 MP4Reader::PopSample(TrackType aTrack)
 {
   switch (aTrack) {
     case kAudio:
       return mDemuxer->DemuxAudioSample();
 
     case kVideo:
-      if (mQueuedVideoSample)
+      if (mQueuedVideoSample) {
         return mQueuedVideoSample.forget();
-
+      }
       return mDemuxer->DemuxVideoSample();
 
     default:
       return nullptr;
   }
 }
 
 // How async decoding works:
--- a/content/media/fmp4/MP4Reader.h
+++ b/content/media/fmp4/MP4Reader.h
@@ -7,16 +7,17 @@
 #if !defined(MP4Reader_h_)
 #define MP4Reader_h_
 
 #include "MediaDecoderReader.h"
 #include "nsAutoPtr.h"
 #include "PlatformDecoderModule.h"
 #include "mp4_demuxer/mp4_demuxer.h"
 #include "MediaTaskQueue.h"
+#include "mozilla/CDMProxy.h"
 
 #include <deque>
 #include "mozilla/Monitor.h"
 
 namespace mozilla {
 
 namespace dom {
 class TimeRanges;
@@ -50,18 +51,22 @@ public:
                         int64_t aEndTime,
                         int64_t aCurrentTime) MOZ_OVERRIDE;
 
   virtual bool IsMediaSeekable() MOZ_OVERRIDE;
 
   virtual nsresult GetBuffered(dom::TimeRanges* aBuffered,
                                int64_t aStartTime) MOZ_OVERRIDE;
 
+  virtual bool IsWaitingMediaResources() MOZ_OVERRIDE;
+
 private:
 
+  void ExtractCryptoInitData(nsTArray<uint8_t>& aInitData);
+
   // Destroys all decoder resources.
   void Shutdown();
 
   // 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.
@@ -136,29 +141,35 @@ private:
     bool mActive;
     bool mInputExhausted;
     bool mError;
     bool mIsFlushing;
     bool mDrainComplete;
   };
   DecoderData mAudio;
   DecoderData mVideo;
-  // Queued frame extracted by the demuxer, but not yet sent to the platform
+  // Queued samples extracted by the demuxer, but not yet sent to the platform
   // decoder.
   nsAutoPtr<mp4_demuxer::MP4Sample> mQueuedVideoSample;
 
   // 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);
-  MP4SampleQueue& SampleQueue(mp4_demuxer::TrackType aTrack);
   MediaDataDecoder* Decoder(mp4_demuxer::TrackType aTrack);
 
   layers::LayersBackend mLayersBackendType;
 
+  nsTArray<nsTArray<uint8_t>> mInitDataEncountered;
+
+  // True if we've read the streams' metadata.
+  bool mDemuxerInitialized;
+
+  // Synchronized by decoder monitor.
+  bool mIsEncrypted;
 };
 
 } // namespace mozilla
 
 #endif
--- a/content/media/fmp4/PlatformDecoderModule.cpp
+++ b/content/media/fmp4/PlatformDecoderModule.cpp
@@ -7,16 +7,20 @@
 #include "PlatformDecoderModule.h"
 #ifdef XP_WIN
 #include "WMFDecoderModule.h"
 #endif
 #ifdef MOZ_FFMPEG
 #include "FFmpegRuntimeLinker.h"
 #endif
 #include "mozilla/Preferences.h"
+#include "EMEDecoderModule.h"
+#include "mozilla/CDMProxy.h"
+#include "SharedThreadPool.h"
+#include "MediaTaskQueue.h"
 
 namespace mozilla {
 
 extern PlatformDecoderModule* CreateBlankDecoderModule();
 
 bool PlatformDecoderModule::sUseBlankDecoder = false;
 bool PlatformDecoderModule::sFFmpegDecoderEnabled = false;
 
@@ -30,22 +34,76 @@ PlatformDecoderModule::Init()
     return;
   }
   alreadyInitialized = true;
 
   Preferences::AddBoolVarCache(&sUseBlankDecoder,
                                "media.fragmented-mp4.use-blank-decoder");
   Preferences::AddBoolVarCache(&sFFmpegDecoderEnabled,
                                "media.fragmented-mp4.ffmpeg.enabled", false);
-
 #ifdef XP_WIN
   WMFDecoderModule::Init();
 #endif
 }
 
+class CreateTaskQueueTask : public nsRunnable {
+public:
+  NS_IMETHOD Run() {
+    MOZ_ASSERT(NS_IsMainThread());
+    mTaskQueue = new MediaTaskQueue(GetMediaDecodeThreadPool());
+    return NS_OK;
+  }
+  nsRefPtr<MediaTaskQueue> mTaskQueue;
+};
+
+static already_AddRefed<MediaTaskQueue>
+CreateTaskQueue()
+{
+  // We must create the MediaTaskQueue/SharedThreadPool on the main thread.
+  nsRefPtr<CreateTaskQueueTask> t(new CreateTaskQueueTask());
+  nsresult rv = NS_DispatchToMainThread(t, NS_DISPATCH_SYNC);
+  NS_ENSURE_SUCCESS(rv, nullptr);
+  return t->mTaskQueue.forget();
+}
+
+/* static */
+PlatformDecoderModule*
+PlatformDecoderModule::CreateCDMWrapper(CDMProxy* aProxy,
+                                        bool aHasAudio,
+                                        bool aHasVideo,
+                                        MediaTaskQueue* aTaskQueue)
+{
+  bool cdmDecodesAudio;
+  bool cdmDecodesVideo;
+  {
+    CDMCaps::AutoLock caps(aProxy->Capabilites());
+    cdmDecodesAudio = caps.CanDecryptAndDecodeAudio();
+    cdmDecodesVideo = caps.CanDecryptAndDecodeVideo();
+  }
+
+  nsAutoPtr<PlatformDecoderModule> pdm;
+  if ((!cdmDecodesAudio && aHasAudio) || (!cdmDecodesVideo && aHasVideo)) {
+    // The CDM itself can't decode. We need to wrap a PDM to decode the
+    // decrypted output of the CDM.
+    pdm = Create();
+    if (!pdm) {
+      return nullptr;
+    }
+  } else {
+    NS_WARNING("CDM that decodes not yet supported!");
+    return nullptr;
+  }
+
+  return new EMEDecoderModule(aProxy,
+                              pdm.forget(),
+                              cdmDecodesAudio,
+                              cdmDecodesVideo,
+                              CreateTaskQueue());
+}
+
 /* static */
 PlatformDecoderModule*
 PlatformDecoderModule::Create()
 {
   if (sUseBlankDecoder) {
     return CreateBlankDecoderModule();
   }
 #ifdef XP_WIN
--- a/content/media/fmp4/PlatformDecoderModule.h
+++ b/content/media/fmp4/PlatformDecoderModule.h
@@ -26,16 +26,17 @@ namespace mozilla {
 namespace layers {
 class ImageContainer;
 }
 
 class MediaDataDecoder;
 class MediaDataDecoderCallback;
 class MediaInputQueue;
 class MediaTaskQueue;
+class CDMProxy;
 typedef int64_t Microseconds;
 
 // The PlatformDecoderModule interface is used by the MP4Reader to abstract
 // access to the H264 and AAC decoders provided by various platforms. It
 // may be extended to support other codecs in future. Each platform (Windows,
 // MacOSX, Linux, B2G etc) must implement a PlatformDecoderModule to provide
 // access to its decoders in order to get decompressed H.264/AAC from the
 // MP4Reader.
@@ -60,16 +61,25 @@ public:
   // Factory method that creates the appropriate PlatformDecoderModule for
   // the platform we're running on. Caller is responsible for deleting this
   // instance. It's expected that there will be multiple
   // PlatformDecoderModules alive at the same time. There is one
   // PlatformDecoderModule created per MP4Reader.
   // This is called on the decode thread.
   static PlatformDecoderModule* Create();
 
+  // Creates a PlatformDecoderModule that uses a CDMProxy to decrypt or
+  // decrypt-and-decode EME encrypted content. If the CDM only decrypts and
+  // does not decode, we create a PDM and use that to create MediaDataDecoders
+  // that we use on on aTaskQueue to decode the decrypted stream.
+  static PlatformDecoderModule* CreateCDMWrapper(CDMProxy* aProxy,
+                                                 bool aHasAudio,
+                                                 bool aHasVideo,
+                                                 MediaTaskQueue* aTaskQueue);
+
   // Called to shutdown the decoder module and cleanup state. This should
   // block until shutdown is complete. This is called after Shutdown() has
   // been called on all MediaDataDecoders created from this
   // PlatformDecoderModule.
   // Called on the main thread only.
   virtual nsresult Shutdown() = 0;
 
   // Creates an H.264 decoder. The layers backend is passed in so that
new file mode 100644
--- /dev/null
+++ b/content/media/fmp4/eme/EMEDecoderModule.cpp
@@ -0,0 +1,246 @@
+/* -*- 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 "EMEDecoderModule.h"
+#include "mtransport/runnable_utils.h"
+#include "mozIGeckoMediaPluginService.h"
+#include "nsServiceManagerUtils.h"
+#include "nsThreadUtils.h"
+#include "ImageContainer.h"
+#include "prsystem.h"
+#include "mp4_demuxer/DecoderData.h"
+#include "gfx2DGlue.h"
+#include "nsContentUtils.h"
+#include "mozilla/CDMProxy.h"
+#include "mozilla/EMELog.h"
+#include "MediaTaskQueue.h"
+#include "SharedThreadPool.h"
+#include "mozilla/EMELog.h"
+#include <string>
+
+namespace mozilla {
+
+class EMEDecryptor : public MediaDataDecoder {
+  typedef mp4_demuxer::MP4Sample MP4Sample;
+
+public:
+
+  EMEDecryptor(MediaDataDecoder* aDecoder,
+               MediaDataDecoderCallback* aCallback,
+               MediaTaskQueue* aTaskQueue,
+               CDMProxy* aProxy)
+    : mDecoder(aDecoder)
+    , mCallback(aCallback)
+    , mTaskQueue(aTaskQueue)
+    , mProxy(aProxy)
+  {
+  }
+
+  virtual nsresult Init() MOZ_OVERRIDE {
+    return mTaskQueue->SyncDispatch(
+      NS_NewRunnableMethod(mDecoder,
+      &MediaDataDecoder::Init));
+  }
+
+  class RedeliverEncryptedInput : public nsRunnable {
+  public:
+    RedeliverEncryptedInput(EMEDecryptor* aDecryptor,
+                            MediaTaskQueue* aTaskQueue,
+                            MP4Sample* aSample)
+      : mDecryptor(aDecryptor)
+      , mTaskQueue(aTaskQueue)
+      , mSample(aSample)
+    {}
+
+    NS_IMETHOD Run() {
+      RefPtr<nsIRunnable> task;
+      task = NS_NewRunnableMethodWithArg<MP4Sample*>(mDecryptor,
+                                                     &EMEDecryptor::Input,
+                                                     mSample.forget());
+      mTaskQueue->Dispatch(task.forget());
+      mTaskQueue = nullptr;
+      mDecryptor = nullptr;
+      return NS_OK;
+    }
+
+  private:
+    nsRefPtr<EMEDecryptor> mDecryptor;
+    nsRefPtr<MediaTaskQueue> mTaskQueue;
+    nsAutoPtr<MP4Sample> mSample;
+  };
+
+  class DeliverDecrypted : public DecryptionClient {
+  public:
+    DeliverDecrypted(EMEDecryptor* aDecryptor, MediaTaskQueue* aTaskQueue)
+      : mDecryptor(aDecryptor)
+      , mTaskQueue(aTaskQueue)
+    {}
+    virtual void Decrypted(nsresult aResult,
+                           mp4_demuxer::MP4Sample* aSample) MOZ_OVERRIDE {
+      if (NS_FAILED(aResult)) {
+        mDecryptor->mCallback->Error();
+        delete aSample;
+      } else {
+        RefPtr<nsIRunnable> task;
+        task = NS_NewRunnableMethodWithArg<MP4Sample*>(mDecryptor,
+                                                       &EMEDecryptor::Decrypted,
+                                                       aSample);
+        mTaskQueue->Dispatch(task.forget());
+        mTaskQueue = nullptr;
+        mDecryptor = nullptr;
+      }
+    }
+  private:
+    nsRefPtr<EMEDecryptor> mDecryptor;
+    nsRefPtr<MediaTaskQueue> mTaskQueue;
+  };
+
+  virtual nsresult Input(MP4Sample* aSample) MOZ_OVERRIDE {
+    // 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.
+    {
+      CDMCaps::AutoLock caps(mProxy->Capabilites());
+      if (!caps.IsKeyUsable(aSample->crypto.key)) {
+        EME_LOG("Encountered a non-usable key, waiting");
+        nsRefPtr<nsIRunnable> task(new RedeliverEncryptedInput(this,
+                                                               mTaskQueue,
+                                                               aSample));
+        caps.CallWhenKeyUsable(aSample->crypto.key, task);
+        return NS_OK;
+      }
+    }
+    mProxy->Decrypt(aSample, new DeliverDecrypted(this, mTaskQueue));
+    return NS_OK;
+  }
+
+  void Decrypted(mp4_demuxer::MP4Sample* aSample) {
+    mTaskQueue->Dispatch(
+      NS_NewRunnableMethodWithArg<mp4_demuxer::MP4Sample*>(
+        mDecoder,
+        &MediaDataDecoder::Input,
+        aSample));
+  }
+
+  virtual nsresult Flush() MOZ_OVERRIDE {
+    mTaskQueue->SyncDispatch(
+      NS_NewRunnableMethod(
+        mDecoder,
+        &MediaDataDecoder::Flush));
+    return NS_OK;
+  }
+
+  virtual nsresult Drain() MOZ_OVERRIDE {
+    mTaskQueue->Dispatch(
+      NS_NewRunnableMethod(
+        mDecoder,
+        &MediaDataDecoder::Drain));
+    return NS_OK;
+  }
+
+  virtual nsresult Shutdown() MOZ_OVERRIDE {
+    mTaskQueue->SyncDispatch(
+      NS_NewRunnableMethod(
+        mDecoder,
+        &MediaDataDecoder::Shutdown));
+    mDecoder = nullptr;
+    mTaskQueue->Shutdown();
+    mTaskQueue = nullptr;
+    mProxy = nullptr;
+    return NS_OK;
+  }
+
+private:
+
+  nsRefPtr<MediaDataDecoder> mDecoder;
+  MediaDataDecoderCallback* mCallback;
+  nsRefPtr<MediaTaskQueue> mTaskQueue;
+  nsRefPtr<CDMProxy> mProxy;
+};
+
+EMEDecoderModule::EMEDecoderModule(CDMProxy* aProxy,
+                                   PlatformDecoderModule* aPDM,
+                                   bool aCDMDecodesAudio,
+                                   bool aCDMDecodesVideo,
+                                   already_AddRefed<MediaTaskQueue> aTaskQueue)
+  : mProxy(aProxy)
+  , mPDM(aPDM)
+  , mTaskQueue(aTaskQueue)
+  , mCDMDecodesAudio(aCDMDecodesAudio)
+  , mCDMDecodesVideo(aCDMDecodesVideo)
+{
+}
+
+EMEDecoderModule::~EMEDecoderModule()
+{
+}
+
+nsresult
+EMEDecoderModule::Shutdown()
+{
+  MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
+  if (mPDM) {
+    return mPDM->Shutdown();
+  }
+  mTaskQueue->Shutdown();
+  return NS_OK;
+}
+
+MediaDataDecoder*
+EMEDecoderModule::CreateH264Decoder(const VideoDecoderConfig& aConfig,
+                                    layers::LayersBackend aLayersBackend,
+                                    layers::ImageContainer* aImageContainer,
+                                    MediaTaskQueue* aVideoTaskQueue,
+                                    MediaDataDecoderCallback* aCallback)
+{
+  if (mCDMDecodesVideo) {
+    NS_WARNING("Support for CDM that decodes video not yet supported");
+    return nullptr;
+  } else {
+    nsRefPtr<MediaDataDecoder> decoder(mPDM->CreateH264Decoder(aConfig,
+                                                               aLayersBackend,
+                                                               aImageContainer,
+                                                               aVideoTaskQueue,
+                                                               aCallback));
+    if (!decoder) {
+      return nullptr;
+    }
+
+    return new EMEDecryptor(decoder,
+                            aCallback,
+                            mTaskQueue,
+                            mProxy);
+  }
+}
+
+MediaDataDecoder*
+EMEDecoderModule::CreateAACDecoder(const AudioDecoderConfig& aConfig,
+                                   MediaTaskQueue* aAudioTaskQueue,
+                                   MediaDataDecoderCallback* aCallback)
+{
+  if (mCDMDecodesAudio) {
+    NS_WARNING("Support for CDM that decodes audio not yet supported");
+    return nullptr;
+  } else {
+    nsRefPtr<MediaDataDecoder> decoder(mPDM->CreateAACDecoder(aConfig,
+                                                              aAudioTaskQueue,
+                                                              aCallback));
+    if (!decoder) {
+      return nullptr;
+    }
+
+    return new EMEDecryptor(decoder,
+                            aCallback,
+                            mTaskQueue,
+                            mProxy);
+  }
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/media/fmp4/eme/EMEDecoderModule.h
@@ -0,0 +1,62 @@
+/* -*- 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(EMEDecoderModule_h_)
+#define EMEDecoderModule_h_
+
+#include "PlatformDecoderModule.h"
+#include "gmp-decryption.h"
+
+namespace mozilla {
+
+class CDMProxy;
+class MediaTaskQueue;
+
+class EMEDecoderModule : public PlatformDecoderModule {
+private:
+  typedef mp4_demuxer::AudioDecoderConfig AudioDecoderConfig;
+  typedef mp4_demuxer::VideoDecoderConfig VideoDecoderConfig;
+
+public:
+  EMEDecoderModule(CDMProxy* aProxy,
+                   PlatformDecoderModule* aPDM,
+                   bool aCDMDecodesAudio,
+                   bool aCDMDecodesVideo,
+                   already_AddRefed<MediaTaskQueue> aDecodeTaskQueue);
+
+  virtual ~EMEDecoderModule();
+
+  // Called when the decoders have shutdown. Main thread only.
+  virtual nsresult Shutdown() MOZ_OVERRIDE;
+
+  // Decode thread.
+  virtual MediaDataDecoder*
+  CreateH264Decoder(const mp4_demuxer::VideoDecoderConfig& aConfig,
+                    layers::LayersBackend aLayersBackend,
+                    layers::ImageContainer* aImageContainer,
+                    MediaTaskQueue* aVideoTaskQueue,
+                    MediaDataDecoderCallback* aCallback) MOZ_OVERRIDE;
+
+  // Decode thread.
+  virtual MediaDataDecoder* CreateAACDecoder(
+    const mp4_demuxer::AudioDecoderConfig& aConfig,
+    MediaTaskQueue* aAudioTaskQueue,
+    MediaDataDecoderCallback* aCallback) MOZ_OVERRIDE;
+
+private:
+  nsRefPtr<CDMProxy> mProxy;
+  // Will be null if CDM has decoding capability.
+  nsAutoPtr<PlatformDecoderModule> mPDM;
+  // We run the PDM on its own task queue.
+  nsRefPtr<MediaTaskQueue> mTaskQueue;
+  bool mCDMDecodesAudio;
+  bool mCDMDecodesVideo;
+
+};
+
+} // namespace mozilla
+
+#endif // EMEDecoderModule_h_
--- a/content/media/fmp4/moz.build
+++ b/content/media/fmp4/moz.build
@@ -1,45 +1,47 @@
 # -*- 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/.
 
 EXPORTS += [
+    'eme/EMEDecoderModule.h',
     'MP4Decoder.h',
     'MP4Reader.h',
     'PlatformDecoderModule.h',
 ]
 
 UNIFIED_SOURCES += [
     'BlankDecoderModule.cpp',
+    'eme/EMEDecoderModule.cpp',
     'PlatformDecoderModule.cpp',
 ]
 
 SOURCES += [
     'MP4Decoder.cpp',
     'MP4Reader.cpp',
 ]
 
 if CONFIG['MOZ_WMF']:
-  DIRS += [ 'wmf' ];
+    DIRS += [ 'wmf' ];
 
 if CONFIG['MOZ_FFMPEG']:
-  EXPORTS += [
-      'ffmpeg/FFmpegRuntimeLinker.h',
-  ]
-  UNIFIED_SOURCES += [
-      'ffmpeg/FFmpegLog.cpp',
-      'ffmpeg/FFmpegRuntimeLinker.cpp',
-  ]
-  DIRS += [
-      'ffmpeg/libav53',
-      'ffmpeg/libav54',
-      'ffmpeg/libav55',
-  ]
-  LOCAL_INCLUDES += [
-      'ffmpeg',
-  ]
+    EXPORTS += [
+        'ffmpeg/FFmpegRuntimeLinker.h',
+    ]
+    UNIFIED_SOURCES += [
+        'ffmpeg/FFmpegLog.cpp',
+        'ffmpeg/FFmpegRuntimeLinker.cpp',
+    ]
+    DIRS += [
+        'ffmpeg/libav53',
+        'ffmpeg/libav54',
+        'ffmpeg/libav55',
+    ]
+    LOCAL_INCLUDES += [
+        'ffmpeg',
+    ]
 
 FINAL_LIBRARY = 'xul'
 
 FAIL_ON_WARNINGS = True
--- a/content/media/gmp/GMPChild.cpp
+++ b/content/media/gmp/GMPChild.cpp
@@ -21,16 +21,18 @@ using mozilla::dom::CrashReporterChild;
 #include <stdlib.h> // for _exit()
 #else
 #include <unistd.h> // for _exit()
 #endif
 
 #if defined(XP_WIN)
 #define TARGET_SANDBOX_EXPORTS
 #include "mozilla/sandboxTarget.h"
+#elif defined(XP_LINUX) && defined(MOZ_GMP_SANDBOX)
+#include "mozilla/Sandbox.h"
 #endif
 
 namespace mozilla {
 namespace gmp {
 
 GMPChild::GMPChild()
   : mLib(nullptr)
   , mGetAPIFunc(nullptr)
@@ -93,16 +95,23 @@ GMPChild::LoadPluginLibrary(const std::s
   nsAutoString binaryName =                            baseName + NS_LITERAL_STRING(".dll");
 #else
 #error not defined
 #endif
   libFile->AppendRelativePath(binaryName);
 
   nsAutoCString nativePath;
   libFile->GetNativePath(nativePath);
+
+#if defined(XP_LINUX) && defined(MOZ_GMP_SANDBOX)
+  // Enable sandboxing here -- we know the plugin file's path, but
+  // this process's execution hasn't been affected by its content yet.
+  mozilla::SetMediaPluginSandbox(nativePath.get());
+#endif
+
   mLib = PR_LoadLibrary(nativePath.get());
   if (!mLib) {
     return false;
   }
 
   GMPInitFunc initFunc = reinterpret_cast<GMPInitFunc>(PR_FindFunctionSymbol(mLib, "GMPInit"));
   if (!initFunc) {
     return false;
--- a/content/media/gmp/GMPDecryptorProxy.h
+++ b/content/media/gmp/GMPDecryptorProxy.h
@@ -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/. */
 
 #ifndef GMPDecryptorProxy_h_
 #define GMPDecryptorProxy_h_
 
 #include "GMPCallbackBase.h"
+#include "gmp-decryption.h"
 
 namespace mp4_demuxer {
 class CryptoSample;
 }
 
 class GMPDecryptorProxyCallback : public GMPCallbackBase {
 public:
   ~GMPDecryptorProxyCallback() {}
new file mode 100644
--- /dev/null
+++ b/content/media/test/crashtests/944851.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<script>
+
+var ac = new AudioContext(1, 1354, 44100);
+var shaper = ac.createWaveShaper();
+var biquad = ac.createBiquadFilter();
+shaper.connect(biquad.frequency);
+biquad.getFrequencyResponse(new Float32Array(55785),
+                            new Float32Array(62876),
+                            new Float32Array(45111));
+
+</script>
+</head>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/media/test/crashtests/966636.html
@@ -0,0 +1,45 @@
+<html class="reftest-wait">
+<head>
+<script>
+function boom() {
+  var Context0= new window.AudioContext();
+  var BufferSource0=Context0.createBufferSource();
+  BufferSource0.start(0);
+  BufferSource0.playbackRate.value = 1.0/128.0;
+
+  setTimeout(
+    function(){
+      BufferSource0.buffer=
+        function(){
+          var length=1;
+          var Buffer=Context0.createBuffer(1,length,Context0.sampleRate);
+          var bufferData= Buffer.getChannelData(0);
+          for (var i = 0; i < length; ++i) {
+            bufferData[i] = Math.sin(i*(626))
+          };return Buffer;
+        }();
+      setTimeout(
+        function(){
+          document.documentElement.removeAttribute("class");
+        }, 0)
+    },4)
+
+  BufferSource0.buffer=
+    function(){
+      const resample_filter_length = 64;
+      // Small enough so that resampler tail latency is triggered immediately,
+      // but large enough that skip_zeros does not consume resampler tail
+      // latency.
+      var length=resample_filter_length/2 + 1;
+      var Buffer=Context0.createBuffer(1,length,Context0.sampleRate);
+      var bufferData= Buffer.getChannelData(0);
+      for (var i = 0; i < length; ++i) {
+        bufferData[i] = Math.sin(i*(311980))
+      };
+      return Buffer;
+    }();
+}
+</script>
+</head>
+<body onload="boom();">
+</body>
new file mode 100644
--- /dev/null
+++ b/content/media/test/crashtests/990794.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<script>
+var ctx = new AudioContext();
+var source = ctx.createOscillator();
+source.start(0);
+
+function appendMerger(src) {
+  const inputCount = 18;
+
+  var merger = ctx.createChannelMerger(32);
+
+  for (var i = 0; i < inputCount; ++i) {
+    src.connect(merger, 0, i);
+  }
+
+  return merger;
+}
+
+for (var i = 0; i < 6; ++i) {
+  source = appendMerger(source);
+}
+</script>
--- a/content/media/test/crashtests/crashtests.list
+++ b/content/media/test/crashtests/crashtests.list
@@ -58,18 +58,21 @@ load 907986-3.html
 load 907986-4.html
 load 910171-1.html
 load 920987.html
 load 925619-1.html
 load 925619-2.html
 load 926619.html
 load 933151.html
 load 933156.html
+load 944851.html
 load 952756.html
+load 966636.html
 load 986901.html
+load 990794.html
 load buffer-source-ended-1.html
 load offline-buffer-source-ended-1.html
 HTTP load media-element-source-seek-1.html
 skip-if(B2G) load oscillator-ended-1.html # intermittent B2G timeouts, bug 920338
 skip-if(B2G) load oscillator-ended-2.html # intermittent B2G timeouts, bug 920338
 load 1015662.html
 include ../../mediasource/test/crashtests/crashtests.list
 test-pref(media.navigator.permission.disabled,true) load 1028458.html
--- a/content/media/webaudio/AudioBufferSourceNode.cpp
+++ b/content/media/webaudio/AudioBufferSourceNode.cpp
@@ -7,17 +7,16 @@
 #include "AudioBufferSourceNode.h"
 #include "mozilla/dom/AudioBufferSourceNodeBinding.h"
 #include "mozilla/dom/AudioParam.h"
 #include "nsMathUtils.h"
 #include "AudioNodeEngine.h"
 #include "AudioNodeStream.h"
 #include "AudioDestinationNode.h"
 #include "AudioParamTimeline.h"
-#include "speex/speex_resampler.h"
 #include <limits>
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(AudioBufferSourceNode)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AudioBufferSourceNode)
--- a/content/media/webaudio/WaveShaperNode.cpp
+++ b/content/media/webaudio/WaveShaperNode.cpp
@@ -5,17 +5,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WaveShaperNode.h"
 #include "mozilla/dom/WaveShaperNodeBinding.h"
 #include "AudioNode.h"
 #include "AudioNodeEngine.h"
 #include "AudioNodeStream.h"
 #include "mozilla/PodOperations.h"
-#include "speex/speex_resampler.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(WaveShaperNode)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WaveShaperNode, AudioNode)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
--- a/content/media/webaudio/WebAudioUtils.cpp
+++ b/content/media/webaudio/WebAudioUtils.cpp
@@ -3,17 +3,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebAudioUtils.h"
 #include "AudioNodeStream.h"
 #include "AudioParamTimeline.h"
 #include "blink/HRTFDatabaseLoader.h"
-#include "speex/speex_resampler.h"
 
 namespace mozilla {
 
 namespace dom {
 
 struct ConvertTimeToTickHelper
 {
   AudioNodeStream* mSourceStream;
--- a/content/media/webaudio/blink/HRTFElevation.cpp
+++ b/content/media/webaudio/blink/HRTFElevation.cpp
@@ -23,17 +23,17 @@
  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #include "HRTFElevation.h"
 
-#include "speex/speex_resampler.h"
+#include <speex/speex_resampler.h>
 #include "mozilla/PodOperations.h"
 #include "AudioSampleFormat.h"
 
 #include "IRC_Composite_C_R0195-incl.cpp"
 
 using namespace std;
 using namespace mozilla;
  
--- a/content/media/webspeech/synth/pico/nsPicoService.cpp
+++ b/content/media/webspeech/synth/pico/nsPicoService.cpp
@@ -300,17 +300,17 @@ PicoCallbackRunnable::Run()
                                          &bytes_sent);
       PICO_ENSURE_SUCCESS("pico_putTextUtf8", status, NS_ERROR_FAILURE);
       // XXX: End speech task on error
       text_remaining -= bytes_sent;
       text_offset += bytes_sent;
     } else {
       // If we already fed all the text to the engine, send a zero length buffer
       // and quit.
-      DispatchSynthDataRunnable(already_AddRefed<SharedBuffer>(nullptr), 0);
+      DispatchSynthDataRunnable(already_AddRefed<SharedBuffer>(), 0);
       break;
     }
 
     do {
       // Run this loop while the result of getData is STEP_BUSY, when it finishes
       // synthesizing audio for the given text, it returns STEP_IDLE. We then
       // break to the outer loop and feed more text, if there is any left.
       if (!IsCurrentTask()) {
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -4366,16 +4366,31 @@ nsGlobalWindow::GetOwnPropertyNames(JSCo
 
 /* static */ bool
 nsGlobalWindow::IsChromeWindow(JSContext* aCx, JSObject* aObj)
 {
   // For now, have to deal with XPConnect objects here.
   return xpc::WindowOrNull(aObj)->IsChromeWindow();
 }
 
+/* static */ bool
+nsGlobalWindow::IsShowModalDialogEnabled(JSContext*, JSObject*)
+{
+  static bool sAddedPrefCache = false;
+  static bool sIsDisabled;
+  static const char sShowModalDialogPref[] = "dom.disable_window_showModalDialog";
+
+  if (!sAddedPrefCache) {
+    Preferences::AddBoolVarCache(&sIsDisabled, sShowModalDialogPref, false);
+    sAddedPrefCache = true;
+  }
+
+  return !sIsDisabled;
+}
+
 nsIDOMOfflineResourceList*
 nsGlobalWindow::GetApplicationCache(ErrorResult& aError)
 {
   FORWARD_TO_INNER_OR_THROW(GetApplicationCache, (aError), aError, nullptr);
 
   if (!mApplicationCache) {
     nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(GetDocShell()));
     if (!webNav) {
@@ -9122,17 +9137,17 @@ nsGlobalWindow::ShowModalDialog(const ns
   if (mDoc) {
     mDoc->WarnOnceAbout(nsIDocument::eShowModalDialog);
   }
 
   FORWARD_TO_OUTER_OR_THROW(ShowModalDialog,
                             (aUrl, aArgument, aOptions, aError), aError,
                             nullptr);
 
-  if (Preferences::GetBool("dom.disable_window_showModalDialog", false)) {
+  if (!IsShowModalDialogEnabled()) {
     aError.Throw(NS_ERROR_NOT_AVAILABLE);
     return nullptr;
   }
 
   nsRefPtr<DialogValueHolder> argHolder =
     new DialogValueHolder(nsContentUtils::SubjectPrincipal(), aArgument);
 
   // Before bringing up the window/dialog, unsuppress painting and flush
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -507,16 +507,19 @@ public:
 
   // WebIDL interface.
   already_AddRefed<nsIDOMWindow> IndexedGetter(uint32_t aIndex, bool& aFound);
 
   void GetSupportedNames(nsTArray<nsString>& aNames);
 
   static bool IsChromeWindow(JSContext* /* unused */, JSObject* aObj);
 
+  static bool IsShowModalDialogEnabled(JSContext* /* unused */ = nullptr,
+                                       JSObject* /* unused */ = nullptr);
+
   bool DoNewResolve(JSContext* aCx, JS::Handle<JSObject*> aObj,
                     JS::Handle<jsid> aId,
                     JS::MutableHandle<JSPropertyDescriptor> aDesc);
 
   void GetOwnPropertyNames(JSContext* aCx, nsTArray<nsString>& aNames,
                            mozilla::ErrorResult& aRv);
 
   // Object Management
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -705,58 +705,31 @@ DumpString(const nsAString &str)
 {
   printf("%s\n", NS_ConvertUTF16toUTF8(str).get());
 }
 #endif
 
 #define JS_OPTIONS_DOT_STR "javascript.options."
 
 static const char js_options_dot_str[]   = JS_OPTIONS_DOT_STR;
-static const char js_strict_option_str[] = JS_OPTIONS_DOT_STR "strict";
-#ifdef DEBUG
-static const char js_strict_debug_option_str[] = JS_OPTIONS_DOT_STR "strict.debug";
-#endif
 #ifdef JS_GC_ZEAL
 static const char js_zeal_option_str[]        = JS_OPTIONS_DOT_STR "gczeal";
 static const char js_zeal_frequency_str[]     = JS_OPTIONS_DOT_STR "gczeal.frequency";
 #endif
 static const char js_memlog_option_str[]      = JS_OPTIONS_DOT_STR "mem.log";
 static const char js_memnotify_option_str[]   = JS_OPTIONS_DOT_STR "mem.notify";
 
 void
 nsJSContext::JSOptionChangedCallback(const char *pref, void *data)
 {
-  nsJSContext *context = reinterpret_cast<nsJSContext *>(data);
-  JSContext *cx = context->mContext;
-
   sPostGCEventsToConsole = Preferences::GetBool(js_memlog_option_str);
   sPostGCEventsToObserver = Preferences::GetBool(js_memnotify_option_str);
 
-  JS::ContextOptionsRef(cx).setExtraWarnings(Preferences::GetBool(js_strict_option_str));
-
-  // The vanilla GetGlobalObject returns null if a global isn't set up on
-  // the context yet. We can sometimes be call midway through context init,
-  // So ask for the member directly instead.
-  nsIScriptGlobalObject *global = context->GetGlobalObjectRef();
-
-  // XXX should we check for sysprin instead of a chrome window, to make
-  // XXX components be covered by the chrome pref instead of the content one?
-  nsCOMPtr<nsIDOMWindow> contentWindow(do_QueryInterface(global));
-  nsCOMPtr<nsIDOMChromeWindow> chromeWindow(do_QueryInterface(global));
-
-#ifdef DEBUG
-  // In debug builds, warnings are enabled in chrome context if
-  // javascript.options.strict.debug is true
-  if (Preferences::GetBool(js_strict_debug_option_str) &&
-      (chromeWindow || !contentWindow)) {
-    JS::ContextOptionsRef(cx).setExtraWarnings(true);
-  }
-#endif
-
 #ifdef JS_GC_ZEAL
+  nsJSContext *context = reinterpret_cast<nsJSContext *>(data);
   int32_t zeal = Preferences::GetInt(js_zeal_option_str, -1);
   int32_t frequency = Preferences::GetInt(js_zeal_frequency_str, JS_DEFAULT_ZEAL_FREQ);
   if (zeal >= 0)
     ::JS_SetGCZeal(context->mContext, (uint8_t)zeal, frequency);
 #endif
 }
 
 nsJSContext::nsJSContext(bool aGCOnDestruction,
--- a/dom/browser-element/mochitest/priority/test_ExpectingSystemMessage.html
+++ b/dom/browser-element/mochitest/priority/test_ExpectingSystemMessage.html
@@ -38,18 +38,18 @@ function runTest() {
     // process times out.
     return expectPriorityChange(childID, 'FOREGROUND');
   }).then(SimpleTest.finish);
 
   document.body.appendChild(iframe);
 }
 
 addEventListener('testready', function() {
-  // Cause the CPU wake lock taken on behalf of this new process to time out
-  // after 1s.
+  // Cause the grace period of priority privilege for this new process to time
+  // out after 1s.
   SpecialPowers.pushPrefEnv(
     {set: [["dom.ipc.systemMessageCPULockTimeoutSec", 1]]},
     runTest);
 });
 
 </script>
 </body>
 </html>
--- a/dom/browser-element/mochitest/priority/test_ExpectingSystemMessage2.html
+++ b/dom/browser-element/mochitest/priority/test_ExpectingSystemMessage2.html
@@ -52,19 +52,19 @@ function runTest() {
     iframe.setVisible(false);
     return p;
   }).then(SimpleTest.finish);
 
   document.body.appendChild(iframe);
 }
 
 addEventListener('testready', function() {
-  // We don't want this wake lock to time out during the test; if it did, then
-  // we might see BACKGROUND priority instead of BACKGROUND_PERCEIVABLE.  So
-  // set the timeout to a large value.
+  // We don't want the grace period of priority privilege to time out during the
+  // test; should it really happen, we would see BACKGROUND priority instead of
+  // BACKGROUND_PERCEIVABLE.  So set the timeout to a large value.
   SpecialPowers.pushPrefEnv(
     {set: [["dom.ipc.systemMessageCPULockTimeoutSec", 99999]]},
     runTest);
 });
 
 </script>
 </body>
 </html>
--- a/dom/browser-element/mochitest/priority/test_HighPriorityDowngrade.html
+++ b/dom/browser-element/mochitest/priority/test_HighPriorityDowngrade.html
@@ -64,18 +64,18 @@ function runTest() {
   }).then(SimpleTest.finish);
 
   document.body.appendChild(iframe);
 }
 
 addEventListener('testready', function() {
   SpecialPowers.pushPrefEnv(
     {set: [
-      /* Cause the CPU wake lock taken on behalf of the high-priority process
-       * to time out after 1s. */
+      /* Cause the grace period of priority privilege for the high-priority
+       * process to time out after 1s. */
        ["dom.ipc.systemMessageCPULockTimeoutSec", 1],
        ["dom.wakelock.enabled", true]
     ]},
     runTest);
 });
 
 </script>
 </body>
--- a/dom/browser-element/mochitest/priority/test_HighPriorityDowngrade2.html
+++ b/dom/browser-element/mochitest/priority/test_HighPriorityDowngrade2.html
@@ -60,18 +60,18 @@ function runTest() {
     document.body.removeChild(highPriorityIframe);
     return p;
   }).then(SimpleTest.finish);
 
   document.body.appendChild(iframe);
 }
 
 addEventListener('testready', function() {
-  // Cause the CPU wake lock taken on behalf of the high-priority process never
-  // to time out during this test.
+  // Cause the grace period of priority privilege for the high-priority process
+  // never to time out during this test.
   SpecialPowers.pushPrefEnv(
     {set: [["dom.ipc.systemMessageCPULockTimeoutSec", 1000]]},
     runTest);
 });
 
 </script>
 </body>
 </html>
--- a/dom/canvas/WebGLExtensionInstancedArrays.cpp
+++ b/dom/canvas/WebGLExtensionInstancedArrays.cpp
@@ -21,37 +21,37 @@ WebGLExtensionInstancedArrays::~WebGLExt
 {
 }
 
 void
 WebGLExtensionInstancedArrays::DrawArraysInstancedANGLE(GLenum mode, GLint first,
                                                         GLsizei count, GLsizei primcount)
 {
     if (mIsLost)
-        return mContext->ErrorInvalidOperation("drawArraysInstancedANGLE: Extension is lost.");
+        return mContext->GenerateWarning("drawArraysInstancedANGLE: Extension is lost.");
 
     mContext->DrawArraysInstanced(mode, first, count, primcount);
 }
 
 void
 WebGLExtensionInstancedArrays::DrawElementsInstancedANGLE(GLenum mode, GLsizei count,
                                                           GLenum type, WebGLintptr offset,
                                                           GLsizei primcount)
 {
     if (mIsLost)
-        return mContext->ErrorInvalidOperation("drawElementsInstancedANGLE: Extension is lost.");
+        return mContext->GenerateWarning("drawElementsInstancedANGLE: Extension is lost.");
 
     mContext->DrawElementsInstanced(mode, count, type, offset, primcount);
 }
 
 void
 WebGLExtensionInstancedArrays::VertexAttribDivisorANGLE(GLuint index, GLuint divisor)
 {
     if (mIsLost)
-        return mContext->ErrorInvalidOperation("vertexAttribDivisorANGLE: Extension is lost.");
+        return mContext->GenerateWarning("vertexAttribDivisorANGLE: Extension is lost.");
 
     mContext->VertexAttribDivisor(index, divisor);
 }
 
 bool
 WebGLExtensionInstancedArrays::IsSupported(const WebGLContext* context)
 {
     gl::GLContext* gl = context->GL();
--- a/dom/canvas/WebGLExtensionVertexArray.cpp
+++ b/dom/canvas/WebGLExtensionVertexArray.cpp
@@ -21,45 +21,45 @@ WebGLExtensionVertexArray::WebGLExtensio
 
 WebGLExtensionVertexArray::~WebGLExtensionVertexArray()
 {
 }
 
 already_AddRefed<WebGLVertexArray> WebGLExtensionVertexArray::CreateVertexArrayOES()
 {
     if (mIsLost) {
-        mContext->ErrorInvalidOperation("createVertexArrayOES: Extension is lost. Returning NULL.");
+        mContext->GenerateWarning("createVertexArrayOES: Extension is lost. Returning null.");
         return nullptr;
     }
 
     return mContext->CreateVertexArray();
 }
 
 void WebGLExtensionVertexArray::DeleteVertexArrayOES(WebGLVertexArray* array)
 {
     if (mIsLost)
-        return mContext->ErrorInvalidOperation("deleteVertexArrayOES: Extension is lost.");
+        return mContext->GenerateWarning("deleteVertexArrayOES: Extension is lost.");
 
     mContext->DeleteVertexArray(array);
 }
 
 bool WebGLExtensionVertexArray::IsVertexArrayOES(WebGLVertexArray* array)
 {
     if (mIsLost) {
-        mContext->ErrorInvalidOperation("isVertexArrayOES: Extension is lost. Returning false.");
+        mContext->GenerateWarning("isVertexArrayOES: Extension is lost. Returning false.");
         return false;
     }
 
     return mContext->IsVertexArray(array);
 }
 
 void WebGLExtensionVertexArray::BindVertexArrayOES(WebGLVertexArray* array)
 {
     if (mIsLost)
-        return mContext->ErrorInvalidOperation("bindVertexArrayOES: Extension is lost.");
+        return mContext->GenerateWarning("bindVertexArrayOES: Extension is lost.");
 
     mContext->BindVertexArray(array);
 }
 
 bool WebGLExtensionVertexArray::IsSupported(const WebGLContext* context)
 {
     // If it is not supported then it's emulated, therefore it's always 'supported'
     // See - WebGLVertexArrayFake.h/cpp for the emulation
--- a/dom/canvas/WebGLTexelConversions.h
+++ b/dom/canvas/WebGLTexelConversions.h
@@ -115,17 +115,17 @@ unpackFromFloat16(uint16_t v)
             f32Bits |= 0x7FFFFFFF;
         } else {
             // this is -inf or +inf
             f32Bits |= 0x7F800000;
         }
         return f32Value;
     }
 
-    f32Bits |= uint32_t(exp + (-15 + 127)) << 10;
+    f32Bits |= uint32_t(exp + (-15 + 127)) << 23;
     f32Bits |= uint32_t(v & 0x03FF) << 13;
 
     return f32Value;
 }
 
 MOZ_BEGIN_ENUM_CLASS(WebGLTexelPremultiplicationOp, int)
     None,
     Premultiply,
--- a/dom/cellbroadcast/tests/marionette/head.js
+++ b/dom/cellbroadcast/tests/marionette/head.js
@@ -53,16 +53,36 @@ const CB_DCS_LANG_GROUP_1 = [
   "de", "en", "it", "fr", "es", "nl", "sv", "da", "pt", "fi",
   "no", "el", "tr", "hu", "pl", null
 ];
 const CB_DCS_LANG_GROUP_2 = [
   "cs", "he", "ar", "ru", "is", null, null, null, null, null,
   null, null, null, null, null, null
 ];
 
+const CB_MAX_CONTENT_PER_PAGE_7BIT = Math.floor((CB_MESSAGE_SIZE_GSM - 6) * 8 / 7);
+const CB_MAX_CONTENT_PER_PAGE_UCS2 = Math.floor((CB_MESSAGE_SIZE_GSM - 6) / 2);
+
+const DUMMY_BODY_7BITS = "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
+                       + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
+                       + "@@@@@@@@@@@@@"; // 93 ascii chars.
+const DUMMY_BODY_7BITS_IND = DUMMY_BODY_7BITS.substr(3);
+const DUMMY_BODY_UCS2 = "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"
+                      + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"
+                      + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"
+                      + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"
+                      + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"
+                      + "\u0000"; // 41 unicode chars.
+const DUMMY_BODY_UCS2_IND = DUMMY_BODY_UCS2.substr(1);
+
+is(DUMMY_BODY_7BITS.length,     CB_MAX_CONTENT_PER_PAGE_7BIT,     "DUMMY_BODY_7BITS.length");
+is(DUMMY_BODY_7BITS_IND.length, CB_MAX_CONTENT_PER_PAGE_7BIT - 3, "DUMMY_BODY_7BITS_IND.length");
+is(DUMMY_BODY_UCS2.length,      CB_MAX_CONTENT_PER_PAGE_UCS2,     "DUMMY_BODY_UCS2.length");
+is(DUMMY_BODY_UCS2_IND.length,  CB_MAX_CONTENT_PER_PAGE_UCS2 - 1, "DUMMY_BODY_UCS2_IND.length");
+
 /**
  * Compose input number into specified number of semi-octets.
  *
  * @param: aNum
  *         The number to be converted.
  * @param: aNumSemiOctets
  *         Number of semi-octects to be composed to.
  *
--- a/dom/cellbroadcast/tests/marionette/manifest.ini
+++ b/dom/cellbroadcast/tests/marionette/manifest.ini
@@ -1,8 +1,9 @@
 [DEFAULT]
 b2g = true
 browser = false
 qemu = true
 
 [test_cellbroadcast_etws.js]
 [test_cellbroadcast_gsm.js]
-[test_cellbroadcast_multi_sim.js]
\ No newline at end of file
+[test_cellbroadcast_multi_sim.js]
+[test_cellbroadcast_umts.js]
\ No newline at end of file
--- a/dom/cellbroadcast/tests/marionette/test_cellbroadcast_gsm.js
+++ b/dom/cellbroadcast/tests/marionette/test_cellbroadcast_gsm.js
@@ -1,34 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
-MARIONETTE_TIMEOUT = 60000;
+MARIONETTE_TIMEOUT = 90000;
 MARIONETTE_HEAD_JS = 'head.js';
 
-const CB_MAX_CONTENT_7BIT = Math.floor((CB_MESSAGE_SIZE_GSM - 6) * 8 / 7);
-const CB_MAX_CONTENT_UCS2 = Math.floor((CB_MESSAGE_SIZE_GSM - 6) / 2);
-
-const BODY_7BITS = "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
-                 + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
-                 + "@@@@@@@@@@@@@"; // 93 ascii chars.
-const BODY_7BITS_IND = BODY_7BITS.substr(3);
-const BODY_UCS2 = "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"
-                + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"
-                + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"
-                + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"
-                + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"
-                + "\u0000"; // 41 unicode chars.
-const BODY_UCS2_IND = BODY_UCS2.substr(1);
-
-is(BODY_7BITS.length,     CB_MAX_CONTENT_7BIT,     "BODY_7BITS.length");
-is(BODY_7BITS_IND.length, CB_MAX_CONTENT_7BIT - 3, "BODY_7BITS_IND.length");
-is(BODY_UCS2.length,      CB_MAX_CONTENT_UCS2,     "BODY_UCS2.length");
-is(BODY_UCS2_IND.length,  CB_MAX_CONTENT_UCS2 - 1, "BODY_UCS2_IND.length");
-
 function testReceiving_GSM_MessageAttributes() {
   log("Test receiving GSM Cell Broadcast - Message Attributes");
 
   let verifyCBMessage = (aMessage) => {
     // Attributes other than `language` and `body` should always be assigned.
     ok(aMessage.gsmGeographicalScope != null, "aMessage.gsmGeographicalScope");
     ok(aMessage.messageCode != null, "aMessage.messageCode");
     ok(aMessage.messageId != null, "aMessage.messageId");
@@ -154,23 +134,23 @@ function testReceiving_GSM_Language_and_
     } else if (aDcsInfo.indicator) {
       is(aMessage.language, "@@", "aMessage.language");
     } else {
       ok(aMessage.language == null, "aMessage.language");
     }
 
     switch (aDcsInfo.encoding) {
       case PDU_DCS_MSG_CODING_7BITS_ALPHABET:
-        is(aMessage.body, aDcsInfo.indicator ? BODY_7BITS_IND : BODY_7BITS, "aMessage.body");
+        is(aMessage.body, aDcsInfo.indicator ? DUMMY_BODY_7BITS_IND : DUMMY_BODY_7BITS, "aMessage.body");
         break;
       case PDU_DCS_MSG_CODING_8BITS_ALPHABET:
         ok(aMessage.body == null, "aMessage.body");
         break;
       case PDU_DCS_MSG_CODING_16BITS_ALPHABET:
-        is(aMessage.body, aDcsInfo.indicator ? BODY_UCS2_IND : BODY_UCS2, "aMessage.body");
+        is(aMessage.body, aDcsInfo.indicator ? DUMMY_BODY_UCS2_IND : DUMMY_BODY_UCS2, "aMessage.body");
         break;
     }
 
     is(aMessage.messageClass, aDcsInfo.messageClass, "aMessage.messageClass");
   };
 
   testDcs.forEach(function(aDcsInfo) {
     let pdu = buildHexStr(0, 8)
@@ -291,17 +271,17 @@ function testReceiving_GSM_Multipart() {
 
   let promise = Promise.resolve();
 
   // According to 9.4.1.2.4 Page Parameter in TS 23.041, the maximal Number of
   // pages per CB message is 15.
   let numParts = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
 
   let verifyCBMessage = (aMessage, aNumParts) => {
-      is(aMessage.body.length, (aNumParts * CB_MAX_CONTENT_7BIT),
+      is(aMessage.body.length, (aNumParts * CB_MAX_CONTENT_PER_PAGE_7BIT),
          "aMessage.body");
   };
 
   numParts.forEach(function(aNumParts) {
     let pdus = [];
     for (let i = 1; i <= aNumParts; i++) {
       let pdu = buildHexStr(0, 10)
               + buildHexStr((i << 4) + aNumParts, 2)
--- a/dom/cellbroadcast/tests/marionette/test_cellbroadcast_multi_sim.js
+++ b/dom/cellbroadcast/tests/marionette/test_cellbroadcast_multi_sim.js
@@ -1,26 +1,22 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 MARIONETTE_TIMEOUT = 10000;
 MARIONETTE_HEAD_JS = 'head.js';
 
-const BODY_7BITS = "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
-                 + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
-                 + "@@@@@@@@@@@@@"; // 93 ascii chars.
-
 function testReceiving_MultiSIM() {
   log("Test receiving GSM Cell Broadcast - Multi-SIM");
 
   let pdu = buildHexStr(0, CB_MESSAGE_SIZE_GSM * 2);
 
   let verifyCBMessage = (aMessage, aServiceId) => {
     log("Verify CB message received from serviceId: " + aServiceId);
-    is(aMessage.body, BODY_7BITS, "Checking message body.");
+    is(aMessage.body, DUMMY_BODY_7BITS, "Checking message body.");
     is(aMessage.serviceId, aServiceId, "Checking serviceId.");
   };
 
   return selectModem(1)
     .then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
     .then((aMessage) => verifyCBMessage(aMessage, 1))
     .then(() => selectModem(0))
     .then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
new file mode 100644
--- /dev/null
+++ b/dom/cellbroadcast/tests/marionette/test_cellbroadcast_umts.js
@@ -0,0 +1,450 @@
+MARIONETTE_TIMEOUT = 90000;
+MARIONETTE_HEAD_JS = 'head.js';
+
+const CB_UMTS_MESSAGE_TYPE_CBS = 1;
+const CB_UMTS_MESSAGE_PAGE_SIZE = 82;
+
+function testReceiving_UMTS_MessageAttributes() {
+  log("Test receiving UMTS Cell Broadcast - Message Attributes");
+
+  let verifyCBMessage = (aMessage) => {
+    // Attributes other than `language` , `body` and `data` should always be assigned.
+    ok(aMessage.gsmGeographicalScope != null, "aMessage.gsmGeographicalScope");
+    ok(aMessage.messageCode != null, "aMessage.messageCode");
+    ok(aMessage.messageId != null, "aMessage.messageId");
+    ok(aMessage.messageClass != null, "aMessage.messageClass");
+    ok(aMessage.timestamp != null, "aMessage.timestamp");
+    ok('etws' in aMessage, "aMessage.etws");
+    if (aMessage.etws) {
+      ok('warningType' in aMessage.etws, "aMessage.etws.warningType");
+      ok(aMessage.etws.emergencyUserAlert != null, "aMessage.etws.emergencyUserAlert");
+      ok(aMessage.etws.popup != null, "aMessage.etws.popup");
+    }
+    ok(aMessage.cdmaServiceCategory != null, "aMessage.cdmaServiceCategory");
+  };
+
+  // Here we use a single UMTS message for test.
+  let pdu = buildHexStr(CB_UMTS_MESSAGE_TYPE_CBS, 2) // msg_type
+          + buildHexStr(0, 10) // skip msg_id, sn, dcs
+          + buildHexStr(1, 2)  // set num_of_pages to 1
+          + buildHexStr(0, CB_UMTS_MESSAGE_PAGE_SIZE * 2)
+          + buildHexStr(CB_UMTS_MESSAGE_PAGE_SIZE, 2); // msg_info_length
+
+  return sendMultipleRawCbsToEmulatorAndWait([pdu])
+    .then((aMessage) => verifyCBMessage(aMessage));
+}
+
+function testReceiving_UMTS_GeographicalScope() {
+  log("Test receiving UMTS Cell Broadcast - Geographical Scope");
+
+  let promise = Promise.resolve();
+
+  let verifyCBMessage = (aMessage, aGsName) => {
+    is(aMessage.gsmGeographicalScope, aGsName,
+       "aMessage.gsmGeographicalScope");
+  };
+
+  CB_GSM_GEOGRAPHICAL_SCOPE_NAMES.forEach(function(aGsName, aIndex) {
+    let pdu = buildHexStr(CB_UMTS_MESSAGE_TYPE_CBS, 2) // msg_type
+            + buildHexStr(0, 4) // skip msg_id
+            + buildHexStr(((aIndex & 0x03) << 14), 4) // set SN
+            + buildHexStr(0, 2) // skip dcs
+            + buildHexStr(1, 2) // set num_of_pages to 1
+            + buildHexStr(0, CB_UMTS_MESSAGE_PAGE_SIZE * 2)
+            + buildHexStr(CB_UMTS_MESSAGE_PAGE_SIZE, 2); // msg_info_length
+    promise = promise
+      .then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
+      .then((aMessage) => verifyCBMessage(aMessage, aGsName));
+  });
+
+  return promise;
+}
+
+function testReceiving_UMTS_MessageCode() {
+  log("Test receiving UMTS Cell Broadcast - Message Code");
+
+  let promise = Promise.resolve();
+
+  // Message Code has 10 bits, and is ORed into a 16 bits 'serial' number. Here
+  // we test every single bit to verify the operation doesn't go wrong.
+  let messageCodes = [
+    0x000, 0x001, 0x002, 0x004, 0x008, 0x010, 0x020, 0x040,
+    0x080, 0x100, 0x200, 0x251
+  ];
+
+  let verifyCBMessage = (aMessage, aMsgCode) => {
+    is(aMessage.messageCode, aMsgCode, "aMessage.messageCode");
+  };
+
+  messageCodes.forEach(function(aMsgCode) {
+    let pdu = buildHexStr(CB_UMTS_MESSAGE_TYPE_CBS, 2) // msg_type
+            + buildHexStr(0, 4) // skip msg_id
+            + buildHexStr(((aMsgCode & 0x3FF) << 4), 4) // set SN
+            + buildHexStr(0, 2) // skip dcs
+            + buildHexStr(1, 2) // set num_of_pages to 1
+            + buildHexStr(0, CB_UMTS_MESSAGE_PAGE_SIZE * 2)
+            + buildHexStr(CB_UMTS_MESSAGE_PAGE_SIZE, 2); // msg_info_length
+    promise = promise
+      .then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
+      .then((aMessage) => verifyCBMessage(aMessage, aMsgCode));
+  });
+
+  return promise;
+}
+
+function testReceiving_UMTS_MessageId() {
+  log("Test receiving UMTS Cell Broadcast - Message Identifier");
+
+  let promise = Promise.resolve();
+
+  // Message Identifier has 16 bits, but no bitwise operation is needed.
+  // Test some selected values only.
+  let messageIds = [
+    0x0000, 0x0001, 0x0010, 0x0100, 0x1000, 0x1111, 0x8888, 0x8811,
+  ];
+
+  let verifyCBMessage = (aMessage, aMessageId) => {
+    is(aMessage.messageId, aMessageId, "aMessage.messageId");
+    ok(aMessage.etws == null, "aMessage.etws");
+  };
+
+  messageIds.forEach(function(aMessageId) {
+    let pdu = buildHexStr(CB_UMTS_MESSAGE_TYPE_CBS, 2) // msg_type
+            + buildHexStr((aMessageId & 0xFFFF), 4) // set msg_id
+            + buildHexStr(0, 4) // skip SN
+            + buildHexStr(0, 2) // skip dcs
+            + buildHexStr(1, 2) // set num_of_pages to 1
+            + buildHexStr(0, CB_UMTS_MESSAGE_PAGE_SIZE * 2)
+            + buildHexStr(CB_UMTS_MESSAGE_PAGE_SIZE, 2);  // msg_info_length
+    promise = promise
+      .then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
+      .then((aMessage) => verifyCBMessage(aMessage, aMessageId));
+  });
+
+  return promise;
+}
+
+function testReceiving_UMTS_Language_and_Body() {
+  log("Test receiving UMTS Cell Broadcast - Language & Body");
+
+  let promise = Promise.resolve();
+
+  let testDcs = [];
+  dcs = 0;
+  while (dcs <= 0xFF) {
+    try {
+      let dcsInfo = { dcs: dcs };
+      [ dcsInfo.encoding, dcsInfo.language,
+        dcsInfo.indicator, dcsInfo.messageClass ] = decodeGsmDataCodingScheme(dcs);
+      testDcs.push(dcsInfo);
+    } catch (e) {
+      // Unsupported coding group, skip.
+      let dcs = (dcs & PDU_DCS_CODING_GROUP_BITS) + 0x10;
+    }
+    dcs++;
+  }
+
+  let verifyCBMessage = (aMessage, aDcsInfo) => {
+    if (aDcsInfo.language) {
+      is(aMessage.language, aDcsInfo.language, "aMessage.language");
+    } else if (aDcsInfo.indicator) {
+      is(aMessage.language, "@@", "aMessage.language");
+    } else {
+      ok(aMessage.language == null, "aMessage.language");
+    }
+
+    switch (aDcsInfo.encoding) {
+      case PDU_DCS_MSG_CODING_7BITS_ALPHABET:
+        is(aMessage.body,
+           aDcsInfo.indicator ? DUMMY_BODY_7BITS_IND : DUMMY_BODY_7BITS,
+           "aMessage.body");
+        break;
+      case PDU_DCS_MSG_CODING_8BITS_ALPHABET:
+        ok(aMessage.body == null, "aMessage.body");
+        break;
+      case PDU_DCS_MSG_CODING_16BITS_ALPHABET:
+        is(aMessage.body,
+           aDcsInfo.indicator ? DUMMY_BODY_UCS2_IND : DUMMY_BODY_UCS2,
+           "aMessage.body");
+        break;
+    }
+
+    is(aMessage.messageClass, aDcsInfo.messageClass, "aMessage.messageClass");
+  };
+
+  testDcs.forEach(function(aDcsInfo) {
+    let pdu = buildHexStr(CB_UMTS_MESSAGE_TYPE_CBS, 2) // msg_type
+            + buildHexStr(0, 4) // skip msg_id
+            + buildHexStr(0, 4) // skip SN
+            + buildHexStr(aDcsInfo.dcs, 2) // set dcs
+            + buildHexStr(1, 2) // set num_of_pages to 1
+            + buildHexStr(0, CB_UMTS_MESSAGE_PAGE_SIZE * 2)
+            + buildHexStr(CB_UMTS_MESSAGE_PAGE_SIZE, 2);  // msg_info_length
+    promise = promise
+      .then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
+      .then((aMessage) => verifyCBMessage(aMessage, aDcsInfo));
+  });
+
+  return promise;
+}
+
+function testReceiving_UMTS_Timestamp() {
+  log("Test receiving UMTS Cell Broadcast - Timestamp");
+
+  let verifyCBMessage = (aMessage) => {
+    // Cell Broadcast messages do not contain a timestamp field (however, ETWS
+    // does). We only check the timestamp doesn't go too far (60 seconds) here.
+    let msMessage = aMessage.timestamp;
+    let msNow = Date.now();
+    ok(Math.abs(msMessage - msNow) < (1000 * 60), "aMessage.timestamp");
+  };
+
+  // Here we use a single UMTS message for test.
+  let pdu = buildHexStr(CB_UMTS_MESSAGE_TYPE_CBS, 2) // msg_type
+          + buildHexStr(0, 10) // skip msg_id, sn, dcs
+          + buildHexStr(1, 2)  // set num_of_pages to 1
+          + buildHexStr(0, CB_UMTS_MESSAGE_PAGE_SIZE * 2)
+          + buildHexStr(CB_UMTS_MESSAGE_PAGE_SIZE, 2); // msg_info_length
+
+  return sendMultipleRawCbsToEmulatorAndWait([pdu])
+    .then((aMessage) => verifyCBMessage(aMessage));
+}
+
+function testReceiving_UMTS_WarningType() {
+  log("Test receiving UMTS Cell Broadcast - Warning Type");
+
+  let promise = Promise.resolve();
+
+  let messageIds = [];
+  for (let i = CB_GSM_MESSAGEID_ETWS_BEGIN; i <= CB_GSM_MESSAGEID_ETWS_END; i++) {
+    messageIds.push(i);
+  }
+
+  let verifyCBMessage = (aMessage, aMessageId) => {
+    is(aMessage.messageId, aMessageId, "aMessage.messageId");
+    ok(aMessage.etws != null, "aMessage.etws");
+
+    let offset = aMessageId - CB_GSM_MESSAGEID_ETWS_BEGIN;
+    if (offset < CB_ETWS_WARNING_TYPE_NAMES.length) {
+      is(aMessage.etws.warningType, CB_ETWS_WARNING_TYPE_NAMES[offset],
+         "aMessage.etws.warningType");
+    } else {
+      ok(aMessage.etws.warningType == null, "aMessage.etws.warningType");
+    }
+  };
+
+  messageIds.forEach(function(aMessageId) {
+    let pdu = buildHexStr(CB_UMTS_MESSAGE_TYPE_CBS, 2) // msg_type
+            + buildHexStr((aMessageId & 0xFFFF), 4) // set msg_id
+            + buildHexStr(0, 4) // skip SN
+            + buildHexStr(0, 2) // skip dcs
+            + buildHexStr(1, 2) // set num_of_pages to 1
+            + buildHexStr(0, CB_UMTS_MESSAGE_PAGE_SIZE * 2)
+            + buildHexStr(CB_UMTS_MESSAGE_PAGE_SIZE, 2); // msg_info_length
+    promise = promise
+      .then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
+      .then((aMessage) => verifyCBMessage(aMessage, aMessageId));
+  });
+
+  return promise;
+}
+
+function testReceiving_UMTS_EmergencyUserAlert() {
+  log("Test receiving UMTS Cell Broadcast - Emergency User Alert");
+
+  let promise = Promise.resolve();
+
+  let emergencyUserAlertMasks = [0x2000, 0x0000];
+
+  let verifyCBMessage = (aMessage, aMask) => {
+    is(aMessage.messageId, CB_GSM_MESSAGEID_ETWS_BEGIN, "aMessage.messageId");
+    ok(aMessage.etws != null, "aMessage.etws");
+    is(aMessage.etws.emergencyUserAlert, aMask != 0, "aMessage.etws.emergencyUserAlert");
+  };
+
+  emergencyUserAlertMasks.forEach(function(aMask) {
+    let pdu = buildHexStr(CB_UMTS_MESSAGE_TYPE_CBS, 2) // msg_type
+            + buildHexStr(CB_GSM_MESSAGEID_ETWS_BEGIN, 4) // set msg_id
+            + buildHexStr(aMask, 4) // set SN
+            + buildHexStr(0, 2) // skip dcs
+            + buildHexStr(1, 2) // set num_of_pages to 1
+            + buildHexStr(0, CB_UMTS_MESSAGE_PAGE_SIZE * 2)
+            + buildHexStr(CB_UMTS_MESSAGE_PAGE_SIZE, 2); // msg_info_length
+    promise = promise
+      .then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
+      .then((aMessage) => verifyCBMessage(aMessage, aMask));
+  });
+
+  return promise;
+}
+
+function testReceiving_UMTS_Popup() {
+  log("Test receiving UMTS Cell Broadcast - Popup");
+
+  let promise = Promise.resolve();
+
+  let popupMasks = [0x1000, 0x0000];
+
+  let verifyCBMessage = (aMessage, aMask) => {
+    is(aMessage.messageId, CB_GSM_MESSAGEID_ETWS_BEGIN, "aMessage.messageId");
+    ok(aMessage.etws != null, "aMessage.etws");
+    is(aMessage.etws.popup, aMask != 0, "aMessage.etws.popup");
+  };
+
+  popupMasks.forEach(function(aMask) {
+    let pdu = buildHexStr(CB_UMTS_MESSAGE_TYPE_CBS, 2) // msg_type
+            + buildHexStr(CB_GSM_MESSAGEID_ETWS_BEGIN, 4) // set msg_id
+            + buildHexStr(aMask, 4) // set SN
+            + buildHexStr(0, 2) // skip dcs
+            + buildHexStr(1, 2) // set num_of_pages to 1
+            + buildHexStr(0, CB_UMTS_MESSAGE_PAGE_SIZE * 2)
+            + buildHexStr(CB_UMTS_MESSAGE_PAGE_SIZE, 2); // msg_info_length
+    promise = promise
+      .then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
+      .then((aMessage) => verifyCBMessage(aMessage, aMask));
+  });
+
+  return promise;
+}
+
+function testReceiving_UMTS_Multipart() {
+  log("Test receiving UMTS Cell Broadcast - Multipart Messages");
+
+  let promise = Promise.resolve();
+
+  // According to 9.4.2.2.5 CB Data in TS 23.041,
+  // Number-of-Pages is equal to or less than 15.
+  // However, the size of out_buff of android modem in emulator is fixed to 1024.
+  // The maximal number of CBS pages we can test is limited to 6.
+  // Another xpc shell test case in ril_worker will address this instead.
+  let numOfPages = [1, 2, 3, 4, 5, 6];
+
+  let verifyCBMessage = (aMessage, aNumOfPages) => {
+      is(aMessage.body.length, (aNumOfPages * CB_MAX_CONTENT_PER_PAGE_7BIT),
+         "aMessage.body");
+  };
+
+  numOfPages.forEach(function(aNumOfPages) {
+    let pdu = buildHexStr(CB_UMTS_MESSAGE_TYPE_CBS, 2) // msg_type
+            + buildHexStr(0, 4) // skip msg_id
+            + buildHexStr(0, 4) // skip SN
+            + buildHexStr(0, 2) // skip dcs
+            + buildHexStr(aNumOfPages, 2); // set num_of_pages
+    for (let i = 1; i <= aNumOfPages; i++) {
+      pdu = pdu + buildHexStr(0, CB_UMTS_MESSAGE_PAGE_SIZE * 2)
+                + buildHexStr(CB_UMTS_MESSAGE_PAGE_SIZE, 2); // msg_info_length
+    }
+    promise = promise
+      .then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
+      .then((aMessage) => verifyCBMessage(aMessage, aNumOfPages));
+  });
+
+  return promise;
+}
+
+function testReceiving_UMTS_PaddingCharacters() {
+  log("Test receiving UMTS Cell Broadcast - Padding Characters <CR>");
+
+  let promise = Promise.resolve();
+
+  let testContents = [
+    { pdu:
+        // CB PDU with GSM 7bit encoded text of
+        // "The quick brown fox jumps over the lazy dog
+        //  \r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r
+        //  \r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r
+        //  \r\r\r\r\r\r\r\r"
+        buildHexStr(CB_UMTS_MESSAGE_TYPE_CBS, 2) + // msg_type
+        buildHexStr(0, 4) + // skip msg_id
+        buildHexStr(0, 4) + // skip SN
+        buildHexStr(0, 2) + // set dcs
+        buildHexStr(1, 2) + // set num_of_pages to 1
+        "54741914AFA7C76B9058FEBEBB41E637" +
+        "1EA4AEB7E173D0DB5E9683E8E832881D" +
+        "D6E741E4F7B9D168341A8D46A3D16834" +
+        "1A8D46A3D168341A8D46A3D168341A8D" +
+        "46A3D168341A8D46A3D168341A8D46A3" +
+        "D100" +
+        buildHexStr(CB_UMTS_MESSAGE_PAGE_SIZE, 2),  // msg_info_length
+      text:
+        "The quick brown fox jumps over the lazy dog"
+    },
+    { pdu:
+        // CB PDU with UCS2 encoded text of
+        // "The quick brown fox jumps over\r\r\r\r\r\r\r\r\r\r\r"
+        buildHexStr(CB_UMTS_MESSAGE_TYPE_CBS, 2) + // msg_type
+        buildHexStr(0, 4) + // skip msg_id
+        buildHexStr(0, 4) + // skip SN
+        buildHexStr(72, 2) + // set dcs
+        buildHexStr(1, 2) + // set num_of_pages to 1
+        "00540068006500200071007500690063" +
+        "006b002000620072006f0077006e0020" +
+        "0066006f00780020006a0075006d0070" +
+        "00730020006f007600650072000D000D" +
+        "000D000D000D000D000D000D000D000D" +
+        "000D" +
+        buildHexStr(CB_UMTS_MESSAGE_PAGE_SIZE, 2),  // msg_info_length
+      text:
+        "The quick brown fox jumps over"
+    }
+  ];
+
+  let verifyCBMessage = (aMessage, aText) => {
+    is(aMessage.body, aText, "aMessage.body");
+  };
+
+  testContents.forEach(function(aTestContent) {
+    promise = promise
+      .then(() => sendMultipleRawCbsToEmulatorAndWait([aTestContent.pdu]))
+      .then((aMessage) => verifyCBMessage(aMessage, aTestContent.text));
+  });
+
+  return promise;
+}
+
+function testReceiving_UMTS_MessageInformationLength() {
+  log("Test receiving UMTS Cell Broadcast - Message Information Length");
+
+  let testText = "The quick brown fox jumps over the lazy dog";
+
+  let verifyCBMessage = (aMessage) => {
+    is(aMessage.body, testText, "aMessage.body");
+  };
+
+  // CB PDU with GSM 7bit encoded text of
+  // "The quick brown fox jumps over the lazy dog
+  //  \r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r
+  //  \r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r
+  //  \r\r\r\r\r\r\r\r"
+  //  We set msg_info_length to the number of used octets.
+  let pdu = buildHexStr(CB_UMTS_MESSAGE_TYPE_CBS, 2) // msg_type
+          + buildHexStr(0, 4) // skip msg_id
+          + buildHexStr(0, 4) // skip SN
+          + buildHexStr(0, 2) // set dcs
+          + buildHexStr(1, 2) // set num_of_pages to 1
+          + "54741914AFA7C76B9058FEBEBB41E637"
+          + "1EA4AEB7E173D0DB5E9683E8E832881D"
+          + "D6E741E4F7B9D168341A8D46A3D16834"
+          + "1A8D46A3D168341A8D46A3D168341A8D"
+          + "46A3D168341A8D46A3D168341A8D46A3"
+          + "D100"
+          + buildHexStr(Math.ceil(testText.length * 7 / 8), 2); // msg_info_length
+
+  return sendMultipleRawCbsToEmulatorAndWait([pdu])
+    .then((aMessage) => verifyCBMessage(aMessage));
+}
+
+startTestCommon(function testCaseMain() {
+  return testReceiving_UMTS_MessageAttributes()
+  .then(() => testReceiving_UMTS_GeographicalScope())
+  .then(() => testReceiving_UMTS_MessageCode())
+  .then(() => testReceiving_UMTS_MessageId())
+  .then(() => testReceiving_UMTS_Language_and_Body())
+  .then(() => testReceiving_UMTS_Timestamp())
+  .then(() => testReceiving_UMTS_WarningType())
+  .then(() => testReceiving_UMTS_EmergencyUserAlert())
+  .then(() => testReceiving_UMTS_Popup())
+  .then(() => testReceiving_UMTS_Multipart())
+  .then(() => testReceiving_UMTS_PaddingCharacters())
+  .then(() => testReceiving_UMTS_MessageInformationLength());
+});
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -2490,17 +2490,17 @@ EventStateManager::DoScrollText(nsIScrol
       break;
     default:
       MOZ_CRASH("Invalid scrollType value comes");
   }
 
   nsIntPoint overflow;
   aScrollableFrame->ScrollBy(actualDevPixelScrollAmount,
                              nsIScrollableFrame::DEVICE_PIXELS,
-                             mode, &overflow, origin);
+                             mode, &overflow, origin, aEvent->isMomentum);
 
   if (!scrollFrameWeak.IsAlive()) {
     // If the scroll causes changing the layout, we can think that the event
     // has been completely consumed by the content.  Then, users probably don't
     // want additional action.
     aEvent->overflowDeltaX = aEvent->overflowDeltaY = 0;
   } else if (isDeltaModePixel) {
     aEvent->overflowDeltaX = overflow.x;
--- a/dom/fetch/Headers.cpp
+++ b/dom/fetch/Headers.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 "mozilla/dom/Headers.h"
 
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/UnionTypes.h"
+#include "mozilla/dom/WorkerPrivate.h"
+#include "mozilla/Preferences.h"
 
 #include "nsCharSeparatedTokenizer.h"
 #include "nsContentUtils.h"
 #include "nsDOMString.h"
 #include "nsNetUtil.h"
 #include "nsPIDOMWindow.h"
 #include "nsReadableUtils.h"
 
@@ -24,16 +26,42 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(Headers
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Headers, mOwner)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Headers)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 // static
+bool
+Headers::PrefEnabled(JSContext* aCx, JSObject* aObj)
+{
+  using mozilla::dom::workers::WorkerPrivate;
+  using mozilla::dom::workers::GetWorkerPrivateFromContext;
+
+  if (NS_IsMainThread()) {
+    static bool sPrefCacheInit = false;
+    static bool sPrefEnabled = false;
+    if (sPrefCacheInit) {
+      return sPrefEnabled;
+    }
+    Preferences::AddBoolVarCache(&sPrefEnabled, "dom.fetch.enabled");
+    sPrefCacheInit = true;
+    return sPrefEnabled;
+  }
+
+  WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
+  if (!workerPrivate) {
+    return false;
+  }
+
+  return workerPrivate->DOMFetchEnabled();
+}
+
+// static
 already_AddRefed<Headers>
 Headers::Constructor(const GlobalObject& aGlobal,
                      const Optional<HeadersOrByteStringSequenceSequenceOrByteStringMozMap>& aInit,
                      ErrorResult& aRv)
 {
   nsRefPtr<Headers> headers = new Headers(aGlobal.GetAsSupports());
 
   if (!aInit.WasPassed()) {
--- a/dom/fetch/Headers.h
+++ b/dom/fetch/Headers.h
@@ -49,16 +49,18 @@ private:
 public:
   explicit Headers(nsISupports* aOwner, HeadersGuardEnum aGuard = HeadersGuardEnum::None)
     : mOwner(aOwner)
     , mGuard(aGuard)
   {
     SetIsDOMBinding();
   }
 
+  static bool PrefEnabled(JSContext* cx, JSObject* obj);
+
   static already_AddRefed<Headers>
   Constructor(const GlobalObject& aGlobal,
               const Optional<HeadersOrByteStringSequenceSequenceOrByteStringMozMap>& aInit,
               ErrorResult& aRv);
 
   void Append(const nsACString& aName, const nsACString& aValue,
               ErrorResult& aRv);
   void Delete(const nsACString& aName, ErrorResult& aRv);
--- a/dom/fetch/moz.build
+++ b/dom/fetch/moz.build
@@ -7,11 +7,15 @@
 EXPORTS.mozilla.dom += [
     'Headers.h',
 ]
 
 UNIFIED_SOURCES += [
     'Headers.cpp',
 ]
 
+LOCAL_INCLUDES += [
+    '../workers',
+]
+
 FAIL_ON_WARNINGS = True
 MSVC_ENABLE_PGO = True
 FINAL_LIBRARY = 'xul'
--- a/dom/imptests/html/html/browsers/the-window-object/test_window-properties.html
+++ b/dom/imptests/html/html/browsers/the-window-object/test_window-properties.html
@@ -1,17 +1,17 @@
 <!doctype html>
 <meta charset=utf-8>
 <title>Properties of the window object</title>
-<link rel="author" title="Ms2ger" href="ms2ger@gmail.com">
+<link rel="author" title="Ms2ger" href="mailto:Ms2ger@gmail.com">
 <link rel="help" href="http://ecma-international.org/ecma-262/5.1/#sec-15.1">
-<link rel="help" href="http://dev.w3.org/2006/webapi/WebIDL/#interface-prototype-object">
-<link rel="help" href="http://dev.w3.org/2006/webapi/WebIDL/#es-attributes">
-<link rel="help" href="http://dev.w3.org/2006/webapi/WebIDL/#es-operations">
-<link rel="help" href="http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#eventtarget">
+<link rel="help" href="http://heycam.github.io/webidl/#interface-prototype-object">
+<link rel="help" href="http://heycam.github.io/webidl/#es-attributes">
+<link rel="help" href="http://heycam.github.io/webidl/#es-operations">
+<link rel="help" href="http://dom.spec.whatwg.org/#eventtarget">
 <link rel="help" href="http://www.whatwg.org/html/#window">
 <link rel="help" href="http://www.whatwg.org/html/#windowtimers">
 <link rel="help" href="http://www.whatwg.org/html/#windowbase64">
 <link rel="help" href="http://www.whatwg.org/html/#windowsessionstorage">
 <link rel="help" href="http://www.whatwg.org/html/#windowlocalstorage">
 <link rel="help" href="https://dvcs.w3.org/hg/editing/raw-file/tip/editing.html#dom-window-getselection">
 <link rel="help" href="http://dev.w3.org/csswg/cssom/#widl-def-Window">
 <link rel="help" href="http://dev.w3.org/csswg/cssom-view/#widl-def-Window">
@@ -59,17 +59,17 @@ var methods = [
   "stop",
   "focus",
   "blur",
   "open",
   "alert",
   "confirm",
   "prompt",
   "print",
-  "showModalDialog",
+  // See below: "showModalDialog",
   "postMessage",
 
   // WindowBase64
   "btoa",
   "atob",
 
   // WindowTimers
   "setTimeout",
@@ -85,16 +85,22 @@ var methods = [
 
   // CSSOM-View
   "matchMedia",
   "scroll",
   "scrollTo",
   "scrollBy"
 ];
 
+// We would like to remove showModalDialog from the platform,
+// see <https://www.w3.org/Bugs/Public/show_bug.cgi?id=26437>.
+if ("showModalDialog" in window) {
+  methods.push("showModalDialog");
+}
+
 var readonlyAttributes = [
   "history",
   "parent",
   "frameElement",
   "navigator",
   "external",
   "applicationCache",
 
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -913,17 +913,17 @@ ContentChild::AllocPBackgroundChild(Tran
 
 bool
 ContentChild::RecvSetProcessSandbox()
 {
   // We may want to move the sandbox initialization somewhere else
   // at some point; see bug 880808.
 #if defined(MOZ_CONTENT_SANDBOX)
 #if defined(XP_LINUX)
-    SetCurrentProcessSandbox();
+    SetContentProcessSandbox();
 #elif defined(XP_WIN)
     mozilla::SandboxTarget::Instance()->StartSandbox();
 #endif
 #endif
     return true;
 }
 
 bool
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -36,17 +36,16 @@
 #include "mozilla/dom/asmjscache/AsmJSCache.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/DataStoreService.h"
 #include "mozilla/dom/ExternalHelperAppParent.h"
 #include "mozilla/dom/PContentBridgeParent.h"
 #include "mozilla/dom/PFileDescriptorSetParent.h"
 #include "mozilla/dom/PCycleCollectWithLogsParent.h"
 #include "mozilla/dom/PMemoryReportRequestParent.h"
-#include "mozilla/dom/power/PowerManagerService.h"
 #include "mozilla/dom/DOMStorageIPC.h"
 #include "mozilla/dom/bluetooth/PBluetoothParent.h"
 #include "mozilla/dom/PFMRadioParent.h"
 #include "mozilla/dom/devicestorage/DeviceStorageRequestParent.h"
 #include "mozilla/dom/FileSystemRequestParent.h"
 #include "mozilla/dom/GeolocationBinding.h"
 #include "mozilla/dom/FileDescriptorSetParent.h"
 #include "mozilla/dom/telephony/TelephonyParent.h"
@@ -79,17 +78,16 @@
 #include "nsDOMFile.h"
 #include "nsFrameMessageManager.h"
 #include "nsHashPropertyBag.h"
 #include "nsIAlertsService.h"
 #include "nsIAppsService.h"
 #include "nsIClipboard.h"
 #include "nsICycleCollectorListener.h"
 #include "nsIDOMGeoGeolocation.h"
-#include "mozilla/dom/WakeLock.h"
 #include "nsIDOMWindow.h"
 #include "nsIExternalProtocolService.h"
 #include "nsIGfxInfo.h"
 #include "nsIIdleService.h"
 #include "nsIJSRuntimeService.h"
 #include "nsIMemoryInfoDumper.h"
 #include "nsIMemoryReporter.h"
 #include "nsIMozBrowserFrame.h"
@@ -180,17 +178,16 @@ using namespace mozilla::system;
 static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID);
 static const char* sClipboardTextFlavors[] = { kUnicodeMime };
 
 using base::ChildPrivileges;
 using base::KillProcess;
 using namespace mozilla::dom::bluetooth;
 using namespace mozilla::dom::devicestorage;
 using namespace mozilla::dom::indexedDB;
-using namespace mozilla::dom::power;
 using namespace mozilla::dom::mobilemessage;
 using namespace mozilla::dom::telephony;
 using namespace mozilla::hal;
 using namespace mozilla::ipc;
 using namespace mozilla::layers;
 using namespace mozilla::net;
 using namespace mozilla::jsipc;
 using namespace mozilla::widget;
@@ -834,16 +831,105 @@ ContentParent::AnswerBridgeToChildProces
         return PContentBridge::Bridge(this, cp);
     } else {
         // You can't bridge to a process you didn't open!
         KillHard();
         return false;
     }
 }
 
+namespace {
+
+class SystemMessageHandledListener MOZ_FINAL
+    : public nsITimerCallback
+    , public LinkedListElement<SystemMessageHandledListener>
+{
+public:
+    NS_DECL_ISUPPORTS
+
+    SystemMessageHandledListener() {}
+
+    static void OnSystemMessageHandled()
+    {
+        if (!sListeners) {
+            return;
+        }
+
+        SystemMessageHandledListener* listener = sListeners->popFirst();
+        if (!listener) {
+            return;
+        }
+
+        // Careful: ShutDown() may delete |this|.
+        listener->ShutDown();
+    }
+
+    void Init(ContentParent* aContentParent)
+    {
+        MOZ_ASSERT(!mContentParent);
+        MOZ_ASSERT(!mTimer);
+
+        // mTimer keeps a strong reference to |this|.  When this object's
+        // destructor runs, it will remove itself from the LinkedList.
+
+        if (!sListeners) {
+            sListeners = new LinkedList<SystemMessageHandledListener>();
+            ClearOnShutdown(&sListeners);
+        }
+        sListeners->insertBack(this);
+
+        mContentParent = aContentParent;
+
+        mTimer = do_CreateInstance("@mozilla.org/timer;1");
+
+        uint32_t timeoutSec =
+            Preferences::GetInt("dom.ipc.systemMessageCPULockTimeoutSec", 30);
+        mTimer->InitWithCallback(this, timeoutSec * 1000,
+                                 nsITimer::TYPE_ONE_SHOT);
+    }
+
+    NS_IMETHOD Notify(nsITimer* aTimer)
+    {
+        // Careful: ShutDown() may delete |this|.
+        ShutDown();
+        return NS_OK;
+    }
+
+private:
+    ~SystemMessageHandledListener() {}
+
+    static StaticAutoPtr<LinkedList<SystemMessageHandledListener> > sListeners;
+
+    void ShutDown()
+    {
+        ProcessPriorityManager::ResetProcessPriority(mContentParent, false);
+
+        nsRefPtr<SystemMessageHandledListener> kungFuDeathGrip = this;
+
+        if (mContentParent) {
+            mContentParent = nullptr;
+        }
+        if (mTimer) {
+            mTimer->Cancel();
+            mTimer = nullptr;
+        }
+    }
+
+    nsRefPtr<ContentParent> mContentParent;
+    nsCOMPtr<nsITimer> mTimer;
+};
+
+StaticAutoPtr<LinkedList<SystemMessageHandledListener> >
+    SystemMessageHandledListener::sListeners;
+
+NS_IMPL_ISUPPORTS(SystemMessageHandledListener,
+                  nsITimerCallback)
+
+} // anonymous namespace
+
 /*static*/ TabParent*
 ContentParent::CreateBrowserOrApp(const TabContext& aContext,
                                   Element* aFrameElement,
                                   ContentParent* aOpenerContentParent)
 {
     if (!sCanLaunchSubprocesses) {
         return nullptr;
     }
@@ -1038,17 +1124,30 @@ ContentParent::CreateBrowserOrApp(const 
                                                    aFrameElement,
                                                    aOpenerContentParent);
         }
 
         // Otherwise just give up.
         return nullptr;
     }
 
-    p->MaybeTakeCPUWakeLock(aFrameElement);
+    // Request a higher priority above BACKGROUND if the child process is
+    // "critical" and probably has system messages coming soon. (A CPU wake lock
+    // may already be controlled by the B2G process in SystemMessageInternal.js
+    // for message handling.) This privilege is revoked once the message is
+    // delivered, or the grace period is up, whichever comes first.
+    nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(aFrameElement);
+    if (browserFrame && browserFrame->GetIsExpectingSystemMessage()) {
+      ProcessPriorityManager::ResetProcessPriority(p, true);
+
+      // This object's Init() function keeps it alive.
+      nsRefPtr<SystemMessageHandledListener> listener =
+          new SystemMessageHandledListener();
+      listener->Init(p);
+    }
 
     return static_cast<TabParent*>(browser);
 }
 
 void
 ContentParent::GetAll(nsTArray<ContentParent*>& aArray)
 {
     aArray.Clear();
@@ -1102,127 +1201,16 @@ ContentParent::Init()
         unused << SendActivateA11y();
     }
 #endif
 
     DebugOnly<FileUpdateDispatcher*> observer = FileUpdateDispatcher::GetSingleton();
     NS_ASSERTION(observer, "FileUpdateDispatcher is null");
 }
 
-namespace {
-
-class SystemMessageHandledListener MOZ_FINAL
-    : public nsITimerCallback
-    , public LinkedListElement<SystemMessageHandledListener>
-{
-public:
-    NS_DECL_ISUPPORTS
-
-    SystemMessageHandledListener() {}
-
-    static void OnSystemMessageHandled()
-    {
-        if (!sListeners) {
-            return;
-        }
-
-        SystemMessageHandledListener* listener = sListeners->popFirst();
-        if (!listener) {
-            return;
-        }
-
-        // Careful: ShutDown() may delete |this|.
-        listener->ShutDown();
-    }
-
-    void Init(WakeLock* aWakeLock)
-    {
-        MOZ_ASSERT(!mWakeLock);
-        MOZ_ASSERT(!mTimer);
-
-        // mTimer keeps a strong reference to |this|.  When this object's
-        // destructor runs, it will remove itself from the LinkedList.
-
-        if (!sListeners) {
-            sListeners = new LinkedList<SystemMessageHandledListener>();
-            ClearOnShutdown(&sListeners);
-        }
-        sListeners->insertBack(this);
-
-        mWakeLock = aWakeLock;
-
-        mTimer = do_CreateInstance("@mozilla.org/timer;1");
-
-        uint32_t timeoutSec =
-            Preferences::GetInt("dom.ipc.systemMessageCPULockTimeoutSec", 30);
-        mTimer->InitWithCallback(this, timeoutSec * 1000,
-                                 nsITimer::TYPE_ONE_SHOT);
-    }
-
-    NS_IMETHOD Notify(nsITimer* aTimer)
-    {
-        // Careful: ShutDown() may delete |this|.
-        ShutDown();
-        return NS_OK;
-    }
-
-private:
-    ~SystemMessageHandledListener() {}
-
-    static StaticAutoPtr<LinkedList<SystemMessageHandledListener> > sListeners;
-
-    void ShutDown()
-    {
-        nsRefPtr<SystemMessageHandledListener> kungFuDeathGrip = this;
-
-        ErrorResult rv;
-        mWakeLock->Unlock(rv);
-
-        if (mTimer) {
-            mTimer->Cancel();
-            mTimer = nullptr;
-        }
-    }
-
-    nsRefPtr<WakeLock> mWakeLock;
-    nsCOMPtr<nsITimer> mTimer;
-};
-
-StaticAutoPtr<LinkedList<SystemMessageHandledListener> >
-    SystemMessageHandledListener::sListeners;
-
-NS_IMPL_ISUPPORTS(SystemMessageHandledListener,
-                  nsITimerCallback)
-
-} // anonymous namespace
-
-void
-ContentParent::MaybeTakeCPUWakeLock(Element* aFrameElement)
-{
-    // Take the CPU wake lock on behalf of this processs if it's expecting a
-    // system message.  We'll release the CPU lock once the message is
-    // delivered, or after some period of time, which ever comes first.
-
-    nsCOMPtr<nsIMozBrowserFrame> browserFrame =
-        do_QueryInterface(aFrameElement);
-    if (!browserFrame ||
-        !browserFrame->GetIsExpectingSystemMessage()) {
-        return;
-    }
-
-    nsRefPtr<PowerManagerService> pms = PowerManagerService::GetInstance();
-    nsRefPtr<WakeLock> lock =
-        pms->NewWakeLockOnBehalfOfProcess(NS_LITERAL_STRING("cpu"), this);
-
-    // This object's Init() function keeps it alive.
-    nsRefPtr<SystemMessageHandledListener> listener =
-        new SystemMessageHandledListener();
-    listener->Init(lock);
-}
-
 bool
 ContentParent::SetPriorityAndCheckIsAlive(ProcessPriority aPriority)
 {
     ProcessPriorityManager::SetProcessPriority(this, aPriority);
 
     // Now that we've set this process's priority, check whether the process is
     // still alive.  Hopefully we've set the priority to FOREGROUND*, so the
     // process won't unexpectedly crash after this point!
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -327,22 +327,16 @@ private:
     void InitInternal(ProcessPriority aPriority,
                       bool aSetupOffMainThreadCompositing,
                       bool aSendRegisteredChrome);
 
     virtual ~ContentParent();
 
     void Init();
 
-    // If the frame element indicates that the child process is "critical" and
-    // has a pending system message, this function acquires the CPU wake lock on
-    // behalf of the child.  We'll release the lock when the system message is
-    // handled or after a timeout, whichever comes first.
-    void MaybeTakeCPUWakeLock(Element* aFrameElement);
-
     // Set the child process's priority and then check whether the child is
     // still alive.  Returns true if the process is still alive, and false
     // otherwise.  If you pass a FOREGROUND* priority here, it's (hopefully)
     // unlikely that the process will be killed after this point.
     bool SetPriorityAndCheckIsAlive(hal::ProcessPriority aPriority);
 
     // Transform a pre-allocated app process into a "real" app
     // process, for the specified manifest URL.
--- a/dom/ipc/ProcessPriorityManager.cpp
+++ b/dom/ipc/ProcessPriorityManager.cpp
@@ -127,16 +127,22 @@ public:
   /**
    * This function implements ProcessPriorityManager::SetProcessPriority.
    */
   void SetProcessPriority(ContentParent* aContentParent,
                           ProcessPriority aPriority,
                           uint32_t aBackgroundLRU = 0);
 
   /**
+   * This function implements ProcessPriorityManager::ResetProcessPriority.
+   */
+  void ResetProcessPriority(ContentParent* aContentParent,
+                            bool aHandleSystemMessage);
+
+  /**
    * If a magic testing-only pref is set, notify the observer service on the
    * given topic with the given data.  This is used for testing
    */
   void FireTestOnlyObserverNotification(const char* aTopic,
                                         const nsACString& aData = EmptyCString());
 
   /**
    * Does some process, other than the one handled by aParticularManager, have
@@ -282,31 +288,34 @@ public:
                       uint32_t aBackgroundLRU = 0);
 
   void SetPriorityNow(ProcessPriority aPriority,
                       ProcessCPUPriority aCPUPriority,
                       uint32_t aBackgroundLRU = 0);
 
   void ShutDown();
 
+  void SetHandlesSystemMessage(bool aHandlesSystemMessage);
+
 private:
   void FireTestOnlyObserverNotification(
     const char* aTopic,
     const nsACString& aData = EmptyCString());
 
   void FireTestOnlyObserverNotification(
     const char* aTopic,
     const char* aData = nullptr);
 
   ContentParent* mContentParent;
   uint64_t mChildID;
   ProcessPriority mPriority;
   ProcessCPUPriority mCPUPriority;
   bool mHoldsCPUWakeLock;
   bool mHoldsHighPriorityWakeLock;
+  bool mHandlesSystemMessage;
 
   /**
    * Used to implement NameWithComma().
    */
   nsAutoCString mNameWithComma;
 
   nsCOMPtr<nsITimer> mResetPriorityTimer;
 };
@@ -492,16 +501,27 @@ ProcessPriorityManagerImpl::SetProcessPr
 {
   MOZ_ASSERT(aContentParent);
   nsRefPtr<ParticularProcessPriorityManager> pppm =
     GetParticularProcessPriorityManager(aContentParent);
   pppm->SetPriorityNow(aPriority, aBackgroundLRU);
 }
 
 void
+ProcessPriorityManagerImpl::ResetProcessPriority(ContentParent* aContentParent,
+                                                 bool aHandleSystemMessage)
+{
+  MOZ_ASSERT(aContentParent);
+  nsRefPtr<ParticularProcessPriorityManager> pppm =
+    GetParticularProcessPriorityManager(aContentParent);
+  pppm->SetHandlesSystemMessage(aHandleSystemMessage);
+  pppm->ResetPriorityNow();
+}
+
+void
 ProcessPriorityManagerImpl::ObserveContentParentCreated(
   nsISupports* aContentParent)
 {
   // Do nothing; it's sufficient to get the PPPM.  But assign to nsRefPtr so we
   // don't leak the already_AddRefed object.
   nsCOMPtr<nsIContentParent> cp = do_QueryInterface(aContentParent);
   nsRefPtr<ParticularProcessPriorityManager> pppm =
     GetParticularProcessPriorityManager(cp->AsContentParent());
@@ -642,16 +662,17 @@ NS_IMPL_ISUPPORTS(ParticularProcessPrior
 ParticularProcessPriorityManager::ParticularProcessPriorityManager(
   ContentParent* aContentParent)
   : mContentParent(aContentParent)
   , mChildID(aContentParent->ChildID())
   , mPriority(PROCESS_PRIORITY_UNKNOWN)
   , mCPUPriority(PROCESS_CPU_PRIORITY_NORMAL)
   , mHoldsCPUWakeLock(false)
   , mHoldsHighPriorityWakeLock(false)
+  , mHandlesSystemMessage(false)
 {
   MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
   LOGP("Creating ParticularProcessPriorityManager.");
 }
 
 void
 ParticularProcessPriorityManager::Init()
 {
@@ -964,17 +985,17 @@ ProcessPriority
 ParticularProcessPriorityManager::CurrentPriority()
 {
   return mPriority;
 }
 
 ProcessPriority
 ParticularProcessPriorityManager::ComputePriority()
 {
-  if ((mHoldsCPUWakeLock || mHoldsHighPriorityWakeLock) &&
+  if ((mHandlesSystemMessage || mHoldsCPUWakeLock || mHoldsHighPriorityWakeLock) &&
       HasAppType("critical")) {
     return PROCESS_PRIORITY_FOREGROUND_HIGH;
   }
 
   bool isVisible = false;
   const InfallibleTArray<PBrowserParent*>& browsers =
     mContentParent->ManagedPBrowserParent();
   for (uint32_t i = 0; i < browsers.Length(); i++) {
@@ -985,17 +1006,17 @@ ParticularProcessPriorityManager::Comput
   }
 
   if (isVisible) {
     return HasAppType("inputmethod") ?
       PROCESS_PRIORITY_FOREGROUND_KEYBOARD :
       PROCESS_PRIORITY_FOREGROUND;
   }
 
-  if ((mHoldsCPUWakeLock || mHoldsHighPriorityWakeLock) &&
+  if ((mHandlesSystemMessage || mHoldsCPUWakeLock || mHoldsHighPriorityWakeLock) &&
       IsExpectingSystemMessage()) {
     return PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE;
   }
 
   AudioChannelService* service = AudioChannelService::GetAudioChannelService();
   if (service->ProcessContentOrNormalChannelIsActive(ChildID())) {
     return PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE;
   }
@@ -1133,16 +1154,22 @@ ParticularProcessPriorityManager::ShutDo
   if (mPriority == PROCESS_PRIORITY_BACKGROUND && !IsPreallocated()) {
     ProcessPriorityManager::RemoveFromBackgroundLRUPool(mContentParent);
   }
 
   mContentParent = nullptr;
 }
 
 void
+ParticularProcessPriorityManager::SetHandlesSystemMessage(bool aHandlesSystemMessage)
+{
+  mHandlesSystemMessage = aHandlesSystemMessage;
+}
+
+void
 ProcessPriorityManagerImpl::FireTestOnlyObserverNotification(
   const char* aTopic,
   const nsACString& aData /* = EmptyCString() */)
 {
   if (!Preferences::GetBool("dom.ipc.processPriorityManager.testMode")) {
     return;
   }
 
@@ -1469,16 +1496,29 @@ ProcessPriorityManager::SetProcessPriori
   ProcessPriorityManagerImpl* singleton =
     ProcessPriorityManagerImpl::GetSingleton();
   if (singleton) {
     singleton->SetProcessPriority(aContentParent, aPriority);
   }
 }
 
 /* static */ void
+ProcessPriorityManager::ResetProcessPriority(ContentParent* aContentParent,
+                                             bool aHandleSystemMessage)
+{
+  MOZ_ASSERT(aContentParent);
+
+  ProcessPriorityManagerImpl* singleton =
+    ProcessPriorityManagerImpl::GetSingleton();
+  if (singleton) {
+    singleton->ResetProcessPriority(aContentParent, aHandleSystemMessage);
+  }
+}
+
+/* static */ void
 ProcessPriorityManager::RemoveFromBackgroundLRUPool(
     ContentParent* aContentParent)
 {
   MOZ_ASSERT(aContentParent);
 
   BackgroundProcessLRUPool* singleton =
     BackgroundProcessLRUPool::Singleton();
   if (singleton) {
--- a/dom/ipc/ProcessPriorityManager.h
+++ b/dom/ipc/ProcessPriorityManager.h
@@ -55,16 +55,30 @@ public:
    *
    * Eventually whatever priority you set here can and probably will be
    * overwritten by the process priority manager.
    */
   static void SetProcessPriority(dom::ContentParent* aContentParent,
                                  hal::ProcessPriority aPriority);
 
   /**
+   * Reset the process priority of a given ContentParent's process in
+   * consideration of system message handling.
+   *
+   * Note that because this method takes a ContentParent*, you can only set the
+   * priority of your subprocesses.  In fact, because we don't support nested
+   * content processes (bug 761935), you can only call this method from the
+   * main process.
+   *
+   * The process priority manager will determine a new appropriate priority.
+   */
+  static void ResetProcessPriority(dom::ContentParent* aContentParent,
+                                   bool aHandleSystemMessage);
+
+  /**
    * Returns true iff this process's priority is FOREGROUND*.
    *
    * Note that because process priorities are set in the main process, it's
    * possible for this method to return a stale value.  So be careful about
    * what you use this for.
    */
   static bool CurrentProcessIsForeground();
 
--- a/dom/media/tests/mochitest/mochitest.ini
+++ b/dom/media/tests/mochitest/mochitest.ini
@@ -69,18 +69,17 @@ skip-if = buildapp == 'b2g' || os == 'an
 [test_peerConnection_bug825703.html]
 [test_peerConnection_bug827843.html]
 skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_bug834153.html]
 [test_peerConnection_bug835370.html]
 [test_peerConnection_bug1013809.html]
 skip-if = (toolkit == 'gonk' && debug) # b2g emulator seems to be too slow (Bug 1016498 and 1008080)
 [test_peerConnection_bug1042791.html]
-skip-if = true # disabled until we can resolve plugin installation issues
-#skip-if = toolkit == 'gonk' || toolkit == 'android' # no openh264 on b2g/android
+skip-if = buildapp == 'b2g' || os == 'android' # bug 1043403
 [test_peerConnection_close.html]
 [test_peerConnection_errorCallbacks.html]
 [test_peerConnection_offerRequiresReceiveAudio.html]
 skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_offerRequiresReceiveVideo.html]
 skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_offerRequiresReceiveVideoAudio.html]
 skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
--- a/dom/media/tests/mochitest/test_peerConnection_bug1042791.html
+++ b/dom/media/tests/mochitest/test_peerConnection_bug1042791.html
@@ -23,20 +23,20 @@
     options.h264 = true;
     test = new PeerConnectionTest(options);
     test.setMediaConstraints([{video: true}], [{video: true}]);
     test.chain.removeAfter("PC_LOCAL_CREATE_OFFER");
 
     test.chain.append([[
       "PC_LOCAL_VERIFY_H264_OFFER",
       function (test) {
-        ok(!test.pcLocal._last_offer.sdp.contains("profile-level-id=0x42e00c"),
-          "H264 offer does not contain profile-level-id=0x42e00c");
-        ok(test.pcLocal._last_offer.sdp.contains("profile-level-id=42e00c"),
-          "H264 offer contains profile-level-id=42e00c");
+        ok(!test.pcLocal._last_offer.sdp.toLowerCase().contains("profile-level-id=0x42e0"),
+          "H264 offer does not contain profile-level-id=0x42e0");
+        ok(test.pcLocal._last_offer.sdp.toLowerCase().contains("profile-level-id=42e0"),
+          "H264 offer contains profile-level-id=42e0");
         test.next();
       }
     ]]);
 
     test.run();
   });
 </script>
 </pre>
--- a/dom/messages/SystemMessageInternal.js
+++ b/dom/messages/SystemMessageInternal.js
@@ -34,16 +34,26 @@ let kMaxPendingMessages;
 try {
   kMaxPendingMessages =
     Services.prefs.getIntPref("dom.messages.maxPendingMessages");
 } catch(e) {
   // getIntPref throws when the pref is not set.
   kMaxPendingMessages = 5;
 }
 
+//Limit the duration to hold the CPU wake lock.
+let kCpuWakeLockTimeoutSec;
+try {
+  kCpuWakeLockTimeoutSec =
+    Services.prefs.getIntPref("dom.ipc.systemMessageCPULockTimeoutSec");
+} catch (e) {
+  // getIntPref throws when the pref is not set.
+  kCpuWakeLockTimeoutSec = 30;
+}
+
 const kMessages =["SystemMessageManager:GetPendingMessages",
                   "SystemMessageManager:HasPendingMessages",
                   "SystemMessageManager:Register",
                   "SystemMessageManager:Unregister",
                   "SystemMessageManager:Message:Return:OK",
                   "SystemMessageManager:AskReadyToRegister",
                   "SystemMessageManager:HandleMessagesDone",
                   "child-process-shutdown"]
@@ -144,17 +154,17 @@ SystemMessageInternal.prototype = {
     // Set a watchdog to avoid locking the CPU wake lock too long,
     // because it'd exhaust the battery quickly which is very bad.
     // This could probably happen if the app failed to launch or
     // handle the system messages due to any unexpected reasons.
     cpuWakeLock.timer.initWithCallback(function timerCb() {
       debug("Releasing the CPU wake lock because the system messages " +
             "were not handled by its registered page before time out.");
       this._cancelCpuWakeLock(aPageKey);
-    }.bind(this), 30000, Ci.nsITimer.TYPE_ONE_SHOT);
+    }.bind(this), kCpuWakeLockTimeoutSec * 1000, Ci.nsITimer.TYPE_ONE_SHOT);
   },
 
   _releaseCpuWakeLock: function _releaseCpuWakeLock(aPageKey, aHandledCount) {
     let cpuWakeLock = this._cpuWakeLocks[aPageKey];
     if (cpuWakeLock) {
       cpuWakeLock.lockCount -= aHandledCount;
       if (cpuWakeLock.lockCount <= 0) {
         debug("Unlocking the CPU wake lock now that the system messages " +
--- a/dom/messages/SystemMessageManager.js
+++ b/dom/messages/SystemMessageManager.js
@@ -103,20 +103,18 @@ SystemMessageManager.prototype = {
                             handledCount: 1 });
 
     aDispatcher.isHandling = false;
 
     if (aDispatcher.messages.length > 0) {
       this._dispatchMessage(aType, aDispatcher, aDispatcher.messages.shift());
     } else {
       // No more messages that need to be handled, we can notify the
-      // ContentChild to release the CPU wake lock grabbed by the ContentParent
-      // (i.e. NewWakeLockOnBehalfOfProcess()) and reset the process's priority.
-      //
-      // TODO: Bug 874353 - Remove SystemMessageHandledListener in ContentParent
+      // ContentChild to propogate the event, so that the ContentParent can
+      // reset the process's priority.
       Services.obs.notifyObservers(/* aSubject */ null,
                                    "handle-system-messages-done",
                                    /* aData */ null);
     }
   },
 
   mozSetMessageHandler: function(aType, aHandler) {
     debug("set message handler for [" + aType + "] " + aHandler);
@@ -244,21 +242,18 @@ SystemMessageManager.prototype = {
       // messages.length|), so the parent can release the CPU wake lock it took
       // on our behalf.
       cpmm.sendAsyncMessage("SystemMessageManager:HandleMessagesDone",
                             { type: msg.type,
                               manifestURL: this._manifestURL,
                               pageURL: this._pageURL,
                               handledCount: messages.length });
 
-      // We also need to notify the ContentChild to release the CPU wake lock
-      // grabbed by the ContentParent (i.e. NewWakeLockOnBehalfOfProcess()) and
-      // reset the process's priority.
-      //
-      // TODO: Bug 874353 - Remove SystemMessageHandledListener in ContentParent
+      // We also need to notify the ContentChild to propogate the event, so that
+      // the ContentParent can reset the process's priority.
       Services.obs.notifyObservers(/* aSubject */ null,
                                    "handle-system-messages-done",
                                    /* aData */ null);
     }
   },
 
   // nsIDOMGlobalPropertyInitializer implementation.
   init: function(aWindow) {
--- a/dom/nfc/nsNfc.js
+++ b/dom/nfc/nsNfc.js
@@ -259,17 +259,26 @@ mozNfc.prototype = {
         eventType = NFC_PEER_EVENT_LOST;
         break;
       default:
         break;
     }
     return eventType;
   },
 
+  hasDeadWrapper: function hasDeadWrapper() {
+    return Cu.isDeadWrapper(this._window) || Cu.isDeadWrapper(this.__DOM_IMPL__);
+  },
+
   firePeerEvent: function firePeerEvent(evt, sessionToken) {
+    if (this.hasDeadWrapper()) {
+      dump("this._window or this.__DOM_IMPL__ is a dead wrapper.");
+      return;
+    }
+
     let peerEvent = (NFC_PEER_EVENT_READY === evt) ? "peerready" : "peerlost";
     let detail = {
       "detail":sessionToken
     };
     let event = new this._window.CustomEvent(peerEvent, this._wrap(detail));
     this.__DOM_IMPL__.dispatchEvent(event);
   },
 
--- a/dom/plugins/base/nsNPAPIPluginInstance.cpp
+++ b/dom/plugins/base/nsNPAPIPluginInstance.cpp
@@ -1807,17 +1807,17 @@ nsNPAPIPluginInstance::CheckJavaC2PJSObj
 
   // Due to the Java version being specified inconsistently across platforms
   // check the version via the mimetype for choosing specific Java versions
   nsCString javaVersion;
   if (!GetJavaVersionFromMimetype(pluginTag, javaVersion)) {
     return;
   }
 
-  mozilla::Version version = javaVersion.get();
+  mozilla::Version version(javaVersion.get());
 
   if (version >= "1.7.0.4") {
     return;
   }
 
   if (!haveCodeParam && version >= "1.6.0.34" && version < "1.7") {
     return;
   }
--- a/dom/promise/Promise.cpp
+++ b/dom/promise/Promise.cpp
@@ -1383,18 +1383,18 @@ PromiseWorkerProxy::GetWorkerPromise() c
   return mWorkerPromise;
 }
 
 void
 PromiseWorkerProxy::StoreISupports(nsISupports* aSupports)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  nsMainThreadPtrHandle<nsISupports> supports =
-    new nsMainThreadPtrHolder<nsISupports>(aSupports);
+  nsMainThreadPtrHandle<nsISupports> supports(
+    new nsMainThreadPtrHolder<nsISupports>(aSupports));
   mSupportsArray.AppendElement(supports);
 }
 
 void
 PromiseWorkerProxy::RunCallback(JSContext* aCx,
                                 JS::Handle<JS::Value> aValue,
                                 RunCallbackFunc aFunc)
 {
--- a/dom/system/gonk/NetworkManager.js
+++ b/dom/system/gonk/NetworkManager.js
@@ -28,17 +28,16 @@ XPCOMUtils.defineLazyGetter(this, "ppmm"
 XPCOMUtils.defineLazyServiceGetter(this, "gDNSService",
                                    "@mozilla.org/network/dns-service;1",
                                    "nsIDNSService");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gNetworkService",
                                    "@mozilla.org/network/service;1",
                                    "nsINetworkService");
 
-const TOPIC_INTERFACE_STATE_CHANGED  = "network-interface-state-changed";
 const TOPIC_INTERFACE_REGISTERED     = "network-interface-registered";
 const TOPIC_INTERFACE_UNREGISTERED   = "network-interface-unregistered";
 const TOPIC_ACTIVE_CHANGED           = "network-active-changed";
 const TOPIC_MOZSETTINGS_CHANGED      = "mozsettings-changed";
 const TOPIC_PREF_CHANGED             = "nsPref:changed";
 const TOPIC_XPCOM_SHUTDOWN           = "xpcom-shutdown";
 const TOPIC_CONNECTION_STATE_CHANGED = "network-connection-state-changed";
 const PREF_MANAGE_OFFLINE_STATUS     = "network.gonk.manage-offline-status";
@@ -131,17 +130,16 @@ function defineLazyRegExp(obj, name, pat
 }
 
 /**
  * This component watches for network interfaces changing state and then
  * adjusts routes etc. accordingly.
  */
 function NetworkManager() {
   this.networkInterfaces = {};
-  Services.obs.addObserver(this, TOPIC_INTERFACE_STATE_CHANGED, true);
   Services.obs.addObserver(this, TOPIC_XPCOM_SHUTDOWN, false);
   Services.obs.addObserver(this, TOPIC_MOZSETTINGS_CHANGED, false);
 
   try {
     this._manageOfflineStatus =
       Services.prefs.getBoolPref(PREF_MANAGE_OFFLINE_STATUS);
   } catch(ex) {
     // Ignore.
@@ -211,115 +209,28 @@ NetworkManager.prototype = {
                                          Ci.nsISupportsWeakReference,
                                          Ci.nsIObserver,
                                          Ci.nsISettingsServiceCallback]),
 
   // nsIObserver
 
   observe: function(subject, topic, data) {
     switch (topic) {
-      case TOPIC_INTERFACE_STATE_CHANGED:
-        let network = subject.QueryInterface(Ci.nsINetworkInterface);
-        debug("Network " + network.type + "/" + network.name +
-              " changed state to " + network.state);
-        switch (network.state) {
-          case Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED:
-#ifdef MOZ_B2G_RIL
-            // Add host route for data calls
-            if (this.isNetworkTypeMobile(network.type)) {
-              gNetworkService.removeHostRoutes(network.name);
-              gNetworkService.addHostRoute(network);
-            }
-            // Dun type is a special case where we add the default route to a
-            // secondary table.
-            if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN) {
-              this.setSecondaryDefaultRoute(network);
-            }
-#endif
-            // Remove pre-created default route and let setAndConfigureActive()
-            // to set default route only on preferred network
-            gNetworkService.removeDefaultRoute(network);
-            this.setAndConfigureActive();
-#ifdef MOZ_B2G_RIL
-            // Resolve and add extra host route. For example, mms proxy or mmsc.
-            // IMPORTANT: The offline state of DNSService will be set implicitly in
-            //            setAndConfigureActive() by modifying Services.io.offline.
-            //            Always setExtraHostRoute() after setAndConfigureActive().
-            this.setExtraHostRoute(network);
-
-            // Update data connection when Wifi connected/disconnected
-            if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_WIFI) {
-              for (let i = 0; i < this.mRil.numRadioInterfaces; i++) {
-                this.mRil.getRadioInterface(i).updateRILNetworkInterface();
-              }
-            }
-#endif
-
-            this.onConnectionChanged(network);
-
-            // Probing the public network accessibility after routing table is ready
-            CaptivePortalDetectionHelper
-              .notify(CaptivePortalDetectionHelper.EVENT_CONNECT, this.active);
-            break;
-          case Ci.nsINetworkInterface.NETWORK_STATE_DISCONNECTED:
-#ifdef MOZ_B2G_RIL
-            // Remove host route for data calls
-            if (this.isNetworkTypeMobile(network.type)) {
-              gNetworkService.removeHostRoute(network);
-            }
-            // Remove extra host route. For example, mms proxy or mmsc.
-            this.removeExtraHostRoute(network);
-            // Remove secondary default route for dun.
-            if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN) {
-              this.removeSecondaryDefaultRoute(network);
-            }
-#endif
-            // Remove routing table in /proc/net/route
-            if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_WIFI) {
-              gNetworkService.resetRoutingTable(network);
-#ifdef MOZ_B2G_RIL
-            } else if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE) {
-              gNetworkService.removeDefaultRoute(network);
-#endif
-            }
-
-            // Abort ongoing captive portal detection on the wifi interface
-            CaptivePortalDetectionHelper
-              .notify(CaptivePortalDetectionHelper.EVENT_DISCONNECT, network);
-            this.setAndConfigureActive();
-#ifdef MOZ_B2G_RIL
-            // Update data connection when Wifi connected/disconnected
-            if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_WIFI) {
-              for (let i = 0; i < this.mRil.numRadioInterfaces; i++) {
-                this.mRil.getRadioInterface(i).updateRILNetworkInterface();
-              }
-            }
-#endif
-            break;
-        }
-#ifdef MOZ_B2G_RIL
-        // Notify outer modules like MmsService to start the transaction after
-        // the configuration of the network interface is done.
-        Services.obs.notifyObservers(network, TOPIC_CONNECTION_STATE_CHANGED,
-                                     this.convertConnectionType(network));
-#endif
-        break;
       case TOPIC_MOZSETTINGS_CHANGED:
         let setting = JSON.parse(data);
         this.handle(setting.key, setting.value);
         break;
       case TOPIC_PREF_CHANGED:
         this._manageOfflineStatus =
           Services.prefs.getBoolPref(PREF_MANAGE_OFFLINE_STATUS);
         debug(PREF_MANAGE_OFFLINE_STATUS + " has changed to " + this._manageOfflineStatus);
         break;
       case TOPIC_XPCOM_SHUTDOWN:
         Services.obs.removeObserver(this, TOPIC_XPCOM_SHUTDOWN);
         Services.obs.removeObserver(this, TOPIC_MOZSETTINGS_CHANGED);
-        Services.obs.removeObserver(this, TOPIC_INTERFACE_STATE_CHANGED);
 #ifdef MOZ_B2G_RIL
         this.dunConnectTimer.cancel();
         this.dunRetryTimer.cancel();
 #endif
         break;
     }
   },
 
@@ -392,16 +303,111 @@ NetworkManager.prototype = {
                                  Cr.NS_ERROR_INVALID_ARG);
     }
     this.networkInterfaces[networkId] = network;
 
     Services.obs.notifyObservers(network, TOPIC_INTERFACE_REGISTERED, null);
     debug("Network '" + networkId + "' registered.");
   },
 
+  updateNetworkInterface: function(network) {
+    if (!(network instanceof Ci.nsINetworkInterface)) {
+      throw Components.Exception("Argument must be nsINetworkInterface.",
+                                 Cr.NS_ERROR_INVALID_ARG);
+    }
+    let networkId = this.getNetworkId(network);
+    if (!(networkId in this.networkInterfaces)) {
+      throw Components.Exception("No network with that type registered.",
+                                 Cr.NS_ERROR_INVALID_ARG);
+    }
+    debug("Network " + network.type + "/" + network.name +
+          " changed state to " + network.state);
+    switch (network.state) {
+      case Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED:
+#ifdef MOZ_B2G_RIL
+        // Add host route for data calls
+        if (this.isNetworkTypeMobile(network.type)) {
+          gNetworkService.removeHostRoutes(network.name);
+          gNetworkService.addHostRoute(network);
+        }
+        // Dun type is a special case where we add the default route to a
+        // secondary table.
+        if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN) {
+          this.setSecondaryDefaultRoute(network);
+        }
+#endif
+        // Remove pre-created default route and let setAndConfigureActive()
+        // to set default route only on preferred network
+        gNetworkService.removeDefaultRoute(network);
+        this.setAndConfigureActive();
+#ifdef MOZ_B2G_RIL
+        // Resolve and add extra host route. For example, mms proxy or mmsc.
+        // IMPORTANT: The offline state of DNSService will be set implicitly in
+        //            setAndConfigureActive() by modifying Services.io.offline.
+        //            Always setExtraHostRoute() after setAndConfigureActive().
+        this.setExtraHostRoute(network);
+
+        // Update data connection when Wifi connected/disconnected
+        if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_WIFI) {
+          for (let i = 0; i < this.mRil.numRadioInterfaces; i++) {
+            this.mRil.getRadioInterface(i).updateRILNetworkInterface();
+          }
+        }
+#endif
+
+        this.onConnectionChanged(network);
+
+        // Probing the public network accessibility after routing table is ready
+        CaptivePortalDetectionHelper
+          .notify(CaptivePortalDetectionHelper.EVENT_CONNECT, this.active);
+        break;
+      case Ci.nsINetworkInterface.NETWORK_STATE_DISCONNECTED:
+#ifdef MOZ_B2G_RIL
+        // Remove host route for data calls
+        if (this.isNetworkTypeMobile(network.type)) {
+          gNetworkService.removeHostRoute(network);
+        }
+        // Remove extra host route. For example, mms proxy or mmsc.
+        this.removeExtraHostRoute(network);
+        // Remove secondary default route for dun.
+        if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN) {
+          this.removeSecondaryDefaultRoute(network);
+        }
+#endif
+        // Remove routing table in /proc/net/route
+        if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_WIFI) {
+          gNetworkService.resetRoutingTable(network);
+#ifdef MOZ_B2G_RIL
+        } else if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE) {
+          gNetworkService.removeDefaultRoute(network);
+#endif
+        }
+
+        // Abort ongoing captive portal detection on the wifi interface
+        CaptivePortalDetectionHelper
+          .notify(CaptivePortalDetectionHelper.EVENT_DISCONNECT, network);
+        this.setAndConfigureActive();
+#ifdef MOZ_B2G_RIL
+        // Update data connection when Wifi connected/disconnected
+        if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_WIFI) {
+          for (let i = 0; i < this.mRil.numRadioInterfaces; i++) {
+            this.mRil.getRadioInterface(i).updateRILNetworkInterface();
+          }
+        }
+#endif
+        break;
+    }
+#ifdef MOZ_B2G_RIL
+    // Notify outer modules like MmsService to start the transaction after
+    // the configuration of the network interface is done.
+    Services.obs.notifyObservers(network, TOPIC_CONNECTION_STATE_CHANGED,
+                                 this.convertConnectionType(network));
+#endif
+  },
+
   unregisterNetworkInterface: function(network) {
     if (!(network instanceof Ci.nsINetworkInterface)) {
       throw Components.Exception("Argument must be nsINetworkInterface.",
                                  Cr.NS_ERROR_INVALID_ARG);
     }
     let networkId = this.getNetworkId(network);
     if (!(networkId in this.networkInterfaces)) {
       throw Components.Exception("No network with that type registered.",
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -64,17 +64,16 @@ const GSMCELLINFO_CID =
 const WCDMACELLINFO_CID =
     Components.ID("{eeaaf307-df6e-4c98-b121-e3302b1fc468}");
 const CDMACELLINFO_CID =
     Components.ID("{b497d6e4-4cb8-4d6e-b673-840c7d5ddf25}");
 const LTECELLINFO_CID =
     Components.ID("{c7e0a78a-4e99-42f5-9251-e6172c5ed8d8}");
 
 const NS_XPCOM_SHUTDOWN_OBSERVER_ID      = "xpcom-shutdown";
-const kNetworkInterfaceStateChangedTopic = "network-interface-state-changed";
 const kNetworkConnStateChangedTopic      = "network-connection-state-changed";
 const kNetworkActiveChangedTopic         = "network-active-changed";
 const kSmsReceivedObserverTopic          = "sms-received";
 const kSilentSmsReceivedObserverTopic    = "silent-sms-received";
 const kSmsSendingObserverTopic           = "sms-sending";
 const kSmsSentObserverTopic              = "sms-sent";
 const kSmsFailedObserverTopic            = "sms-failed";
 const kSmsDeliverySuccessObserverTopic   = "sms-delivery-success";
@@ -5091,19 +5090,17 @@ RILNetworkInterface.prototype = {
   },
 
   notifyRILNetworkInterface: function() {
     if (DEBUG) {
       this.debug("notifyRILNetworkInterface type: " + this.type + ", state: " +
                  this.state);
     }
 
-    Services.obs.notifyObservers(this,
-                                 kNetworkInterfaceStateChangedTopic,
-                                 null);
+    gNetworkManager.updateNetworkInterface(this);
   },
 
   connect: function() {
     this.enabled = true;
 
     this.dataCall.connect(this);
   },
 
--- a/dom/system/gonk/nsINetworkManager.idl
+++ b/dom/system/gonk/nsINetworkManager.idl
@@ -16,18 +16,18 @@ interface nsINetworkInterface : nsISuppo
   const long NETWORK_STATE_CONNECTING = 0;
   const long NETWORK_STATE_CONNECTED = 1;
   const long NETWORK_STATE_DISCONNECTING = 2;
   const long NETWORK_STATE_DISCONNECTED = 3;
 
   /**
    * Current network state, one of the NETWORK_STATE_* constants.
    *
-   * When this changes, network interface implementations notify the
-   * 'network-interface-state-changed' observer notification.
+   * When this changes, network interface implementations notify with
+   * updateNetworkInterface() API.
    */
   readonly attribute long state;
 
   const long NETWORK_TYPE_UNKNOWN     = -1;
   const long NETWORK_TYPE_WIFI        = 0;
   const long NETWORK_TYPE_MOBILE      = 1;
   const long NETWORK_TYPE_MOBILE_MMS  = 2;
   const long NETWORK_TYPE_MOBILE_SUPL = 3;
@@ -92,33 +92,47 @@ interface nsINetworkInterface : nsISuppo
    */
   void getDnses([optional] out unsigned long count,
                 [array, size_is(count), retval] out wstring dnses);
 };
 
 /**
  * Manage network interfaces.
  */
-[scriptable, uuid(3ea50550-4b3c-11e3-8f96-0800200c9a66)]
+[scriptable, uuid(f3193805-c070-4d23-bd5c-a439eb8610c3)]
 interface nsINetworkManager : nsISupports
 {
   /**
    * Register the given network interface with the network manager.
    *
    * Consumers will be notified with the 'network-interface-registered'
    * observer notification.
    *
    * Throws if there's already an interface registered with the same network id.
    *
    * @param network
    *        Network interface to register.
    */
   void registerNetworkInterface(in nsINetworkInterface network);
 
   /**
+   * Update the routes and DNSes according the state of the given network.
+   *
+   * Consumers will be notified with the 'network-connection-state-changed'
+   * observer notification.
+   *
+   * Throws an exception if the specified network interface object isn't
+   * registered.
+   *
+   * @param network
+   *        Network interface to update.
+   */
+  void updateNetworkInterface(in nsINetworkInterface network);
+
+  /**
    * Unregister the given network interface from the network manager.
    *
    * Consumers will be notified with the 'network-interface-unregistered'
    * observer notification.
    *
    * Throws an exception if the specified network interface object isn't
    * registered.
    *
--- a/dom/system/gonk/ril_consts.js
+++ b/dom/system/gonk/ril_consts.js
@@ -1304,18 +1304,25 @@ this.CB_NON_MMI_SETTABLE_RANGES = [
 
 // User Data max length in septets
 this.CB_MAX_CONTENT_7BIT = 93;
 // User Data max length in octets
 this.CB_MAX_CONTENT_8BIT = 82;
 // User Data max length in chars
 this.CB_MAX_CONTENT_UCS2 = 41;
 
+// See 3GPP TS 23.041 v11.6.0 senction 9.3.19
+this.CB_MSG_PAGE_INFO_SIZE = 82;
+
 this.CB_MESSAGE_SIZE_ETWS = 56;
 this.CB_MESSAGE_SIZE_GSM  = 88;
+this.CB_MESSAGE_SIZE_UMTS_MIN = 90;
+this.CB_MESSAGE_SIZE_UMTS_MAX = 1252;
+
+
 
 // GSM Cell Broadcast Geographical Scope
 // See 3GPP TS 23.041 clause 9.4.1.2.1
 this.CB_GSM_GEOGRAPHICAL_SCOPE_CELL_WIDE_IMMEDIATE = 0;
 this.CB_GSM_GEOGRAPHICAL_SCOPE_PLMN_WIDE           = 1;
 this.CB_GSM_GEOGRAPHICAL_SCOPE_LOCATION_AREA_WIDE  = 2;
 this.CB_GSM_GEOGRAPHICAL_SCOPE_CELL_WIDE           = 3;
 
@@ -1338,16 +1345,22 @@ this.CB_GSM_MESSAGEID_ETWS_END   = 0x110
 this.CB_ETWS_WARNING_TYPE_NAMES = [
   "earthquake",
   "tsunami",
   "earthquake-tsunami",
   "test",
   "other"
 ];
 
+// UMTS Message Type
+// see 3GPP TS 25.324 section 11.1
+this.CB_UMTS_MESSAGE_TYPE_CBS      = 1;
+this.CB_UMTS_MESSAGE_TYPE_SCHEDULE = 2;
+this.CB_UMTS_MESSAGE_TYPE_CBS41    = 3;
+
 /**
  * GSM PDU constants
  */
 
 // PDU TYPE-OF-ADDRESS
 this.PDU_TOA_UNKNOWN       = 0x80; // Unknown. This is used when the user or
                                     // network has no a priori information
                                     // about the numbering plan.
--- a/dom/system/gonk/ril_worker.js
+++ b/dom/system/gonk/ril_worker.js
@@ -6953,18 +6953,17 @@ RilObject.prototype[UNSOLICITED_RESPONSE
 };
 RilObject.prototype[UNSOLICITED_RESPONSE_NEW_BROADCAST_SMS] = function UNSOLICITED_RESPONSE_NEW_BROADCAST_SMS(length) {
   let message;
   try {
     message =
       this.context.GsmPDUHelper.readCbMessage(this.context.Buf.readInt32());
   } catch (e) {
     if (DEBUG) {
-      this.context.debug("Failed to parse Cell Broadcast message: " +
-                         JSON.stringify(e));
+      this.context.debug("Failed to parse Cell Broadcast message: " + e);
     }
     return;
   }
 
   message = this._processReceivedSmsCbPage(message);
   if (!message) {
     return;
   }
@@ -8402,17 +8401,26 @@ GsmPDUHelperObject.prototype = {
    * @param msg
    *        message object for output.
    *
    * @see 3GPP TS 23.041 section 9.4.1.2.2
    */
   readCbMessageIdentifier: function(msg) {
     let Buf = this.context.Buf;
     msg.messageId = Buf.readUint8() << 8 | Buf.readUint8();
-
+  },
+
+  /**
+   * Read ETWS information from message identifier and serial Number
+   * @param msg
+   *        message object for output.
+   *
+   * @see 3GPP TS 23.041 section 9.4.1.2.1 & 9.4.1.2.2
+   */
+  readCbEtwsInfo: function(msg) {
     if ((msg.format != CB_FORMAT_ETWS)
         && (msg.messageId >= CB_GSM_MESSAGEID_ETWS_BEGIN)
         && (msg.messageId <= CB_GSM_MESSAGEID_ETWS_END)) {
       // `In the case of transmitting CBS message for ETWS, a part of
       // Message Code can be used to command mobile terminals to activate
       // emergency user alert and message popup in order to alert the users.`
       msg.etws = {
         emergencyUserAlert: msg.messageCode & 0x0200 ? true : false,
@@ -8539,24 +8547,22 @@ GsmPDUHelperObject.prototype = {
     msg.etws = {
       warningType:        (word >>> 9) & 0x7F,
       popup:              word & 0x80 ? true : false,
       emergencyUserAlert: word & 0x100 ? true : false
     };
   },
 
   /**
-   * Read CBS-Message-Information-Page
-   *
-   * @param msg
-   *        message object for output.
-   * @param length
-   *        length of cell broadcast data to read in octets.
-   *
-   * @see 3GPP TS 23.041 section 9.3.19
+   * Read GSM CB Data
+   *
+   * This parameter is a copy of the 'CBS-Message-Information-Page' as sent
+   * from the CBC to the BSC.
+   *
+   * @see  3GPP TS 23.041 section 9.4.1.2.5
    */
   readGsmCbData: function(msg, length) {
     let Buf = this.context.Buf;
     let bufAdapter = {
       context: this.context,
       readHexOctet: function() {
         return Buf.readUint8();
       }
@@ -8586,36 +8592,160 @@ GsmPDUHelperObject.prototype = {
                                                        PDU_NL_IDENTIFIER_DEFAULT,
                                                        PDU_NL_IDENTIFIER_DEFAULT);
           length -= 2;
         }
         msg.body = this.readUCS2String.call(bufAdapter, length);
         break;
     }
 
+    if (msg.data || !msg.body) {
+      return;
+    }
+
     // According to 9.3.19 CBS-Message-Information-Page in TS 23.041:
     // "
     //  This parameter is of a fixed length of 82 octets and carries up to and
     //  including 82 octets of user information. Where the user information is
     //  less than 82 octets, the remaining octets must be filled with padding.
     // "
     // According to 6.2.1.1 GSM 7 bit Default Alphabet and 6.2.3 UCS2 in
     // TS 23.038, the padding character is <CR>.
-    if (!msg.body) {
-      return;
-    }
     for (let i = msg.body.length - 1; i >= 0; i--) {
       if (msg.body.charAt(i) !== '\r') {
         msg.body = msg.body.substring(0, i + 1);
         break;
       }
     }
   },
 
   /**
+   * Read UMTS CB Data
+   *
+   * Octet Number(s)  Parameter
+   *               1  Number-of-Pages
+   *          2 - 83  CBS-Message-Information-Page 1
+   *              84  CBS-Message-Information-Length 1
+   *             ...
+   *                  CBS-Message-Information-Page n
+   *                  CBS-Message-Information-Length n
+   *
+   * @see 3GPP TS 23.041 section 9.4.2.2.5
+   */
+  readUmtsCbData: function(msg) {
+    let Buf = this.context.Buf;
+    let numOfPages = Buf.readUint8();
+    if (numOfPages < 0 || numOfPages > 15) {
+      throw new Error("Invalid numOfPages: " + numOfPages);
+    }
+
+    let bufAdapter = {
+      context: this.context,
+      readHexOctet: function() {
+        return Buf.readUint8();
+      }
+    };
+
+    let removePaddingCharactors = function (text) {
+      for (let i = text.length - 1; i >= 0; i--) {
+        if (text.charAt(i) !== '\r') {
+          return text.substring(0, i + 1);
+        }
+      }
+      return text;
+    }
+
+    let totalLength = 0, length, pageLengths = [];
+    for (let i = 0; i < numOfPages; i++) {
+      Buf.seekIncoming(CB_MSG_PAGE_INFO_SIZE);
+      length = Buf.readUint8();
+      totalLength += length;
+      pageLengths.push(length);
+    }
+
+    // Seek back to beginning of CB Data.
+    Buf.seekIncoming(-numOfPages * (CB_MSG_PAGE_INFO_SIZE + 1));
+
+    switch (msg.encoding) {
+      case PDU_DCS_MSG_CODING_7BITS_ALPHABET: {
+        let body;
+        msg.body = "";
+        for (let i = 0; i < numOfPages; i++) {
+          body = this.readSeptetsToString.call(bufAdapter,
+                                               (pageLengths[i] * 8 / 7),
+                                               0,
+                                               PDU_NL_IDENTIFIER_DEFAULT,
+                                               PDU_NL_IDENTIFIER_DEFAULT);
+          if (msg.hasLanguageIndicator) {
+            if (!msg.language) {
+              msg.language = body.substring(0, 2);
+            }
+            body = body.substring(3);
+          }
+
+          msg.body += removePaddingCharactors(body);
+
+          // Skip padding octets
+          Buf.seekIncoming(CB_MSG_PAGE_INFO_SIZE - pageLengths[i]);
+          // Read the octet of CBS-Message-Information-Length
+          Buf.readUint8();
+        }
+
+        break;
+      }
+
+      case PDU_DCS_MSG_CODING_8BITS_ALPHABET: {
+        msg.data = new Uint8Array(totalLength);
+        for (let i = 0, j = 0; i < numOfPages; i++) {
+          for (let pageLength = pageLengths[i]; pageLength > 0; pageLength--) {
+              msg.data[j++] = Buf.readUint8();
+          }
+
+          // Skip padding octets
+          Buf.seekIncoming(CB_MSG_PAGE_INFO_SIZE - pageLengths[i]);
+          // Read the octet of CBS-Message-Information-Length
+          Buf.readUint8();
+        }
+
+        break;
+      }
+
+      case PDU_DCS_MSG_CODING_16BITS_ALPHABET: {
+        msg.body = "";
+        for (let i = 0; i < numOfPages; i++) {
+          let pageLength = pageLengths[i];
+          if (msg.hasLanguageIndicator) {
+            if (!msg.language) {
+              msg.language = this.readSeptetsToString.call(bufAdapter,
+                                                           2,
+                                                           0,
+                                                           PDU_NL_IDENTIFIER_DEFAULT,
+                                                           PDU_NL_IDENTIFIER_DEFAULT);
+            } else {
+              Buf.readUint16();
+            }
+
+            pageLength -= 2;
+          }
+
+          msg.body += removePaddingCharactors(
+                        this.readUCS2String.call(bufAdapter, pageLength));
+
+          // Skip padding octets
+          Buf.seekIncoming(CB_MSG_PAGE_INFO_SIZE - pageLengths[i]);
+          // Read the octet of CBS-Message-Information-Length
+          Buf.readUint8();
+        }
+
+        break;
+      }
+    }
+  },
+
+  /**
    * Read Cell GSM/ETWS/UMTS Broadcast Message.
    *
    * @param pduLength
    *        total length of the incoming PDU in octets.
    */
   readCbMessage: function(pduLength) {
     // Validity                                                   GSM ETWS UMTS
     let msg = {
@@ -8652,32 +8782,64 @@ GsmPDUHelperObject.prototype = {
       return this.readEtwsCbMessage(msg);
     }
 
     if (pduLength <= CB_MESSAGE_SIZE_GSM) {
       msg.format = CB_FORMAT_GSM;
       return this.readGsmCbMessage(msg, pduLength);
     }
 
-    return null;
+    if (pduLength >= CB_MESSAGE_SIZE_UMTS_MIN &&
+        pduLength <= CB_MESSAGE_SIZE_UMTS_MAX) {
+      msg.format = CB_FORMAT_UMTS;
+      return this.readUmtsCbMessage(msg);
+    }
+
+    throw new Error("Invalid PDU Length: " + pduLength);
+  },
+
+  /**
+   * Read UMTS CBS Message.
+   *
+   * @param msg
+   *        message object for output.
+   *
+   * @see 3GPP TS 23.041 section 9.4.2
+   * @see 3GPP TS 25.324 section 10.2
+   */
+  readUmtsCbMessage: function(msg) {
+    let Buf = this.context.Buf;
+    let type = Buf.readUint8();
+    if (type != CB_UMTS_MESSAGE_TYPE_CBS) {
+      throw new Error("Unsupported UMTS Cell Broadcast message type: " + type);
+    }
+
+    this.readCbMessageIdentifier(msg);
+    this.readCbSerialNumber(msg);
+    this.readCbEtwsInfo(msg);
+    this.readCbDataCodingScheme(msg);
+    this.readUmtsCbData(msg);
+
+    return msg;
   },
 
   /**
    * Read GSM Cell Broadcast Message.
    *
    * @param msg
    *        message object for output.
    * @param pduLength
    *        total length of the incomint PDU in octets.
    *
    * @see 3GPP TS 23.041 clause 9.4.1.2
    */
   readGsmCbMessage: function(msg, pduLength) {
     this.readCbSerialNumber(msg);
     this.readCbMessageIdentifier(msg);
+    this.readCbEtwsInfo(msg);
     this.readCbDataCodingScheme(msg);
     this.readCbPageParameter(msg);
 
     // GSM CB message header takes 6 octets.
     this.readGsmCbData(msg, pduLength - 6);
 
     return msg;
   },
--- a/dom/system/gonk/tests/marionette/head.js
+++ b/dom/system/gonk/tests/marionette/head.js
@@ -2,17 +2,16 @@
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 MARIONETTE_CONTEXT = "chrome";
 
 const SETTINGS_KEY_DATA_ENABLED = "ril.data.enabled";
 const SETTINGS_KEY_DATA_APN_SETTINGS  = "ril.data.apnSettings";
 
 const TOPIC_CONNECTION_STATE_CHANGED = "network-connection-state-changed";
-const TOPIC_INTERFACE_STATE_CHANGED = "network-interface-state-changed";
 const TOPIC_NETWORK_ACTIVE_CHANGED = "network-active-changed";
 
 let Promise = Cu.import("resource://gre/modules/Promise.jsm").Promise;
 
 let ril = Cc["@mozilla.org/ril;1"].getService(Ci.nsIRadioInterfaceLayer);
 ok(ril, "ril.constructor is " + ril.constructor);
 
 let radioInterface = ril.getRadioInterface(0);
--- a/dom/system/gonk/tests/test_ril_worker_cellbroadcast.js
+++ b/dom/system/gonk/tests/test_ril_worker_cellbroadcast.js
@@ -2,16 +2,41 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 subscriptLoader.loadSubScript("resource://gre/modules/ril_consts.js", this);
 
 function run_test() {
   run_next_test();
 }
 
+function buildHexStr(aNum, aNumSemiOctets) {
+  let str = aNum.toString(16);
+  while (str.length < aNumSemiOctets) {
+    str = "0" + str;
+  }
+  return str;
+}
+
+function hexStringToParcelByteArrayData(hexString) {
+  let bytes = [];
+
+  let length = hexString.length / 2;
+
+  bytes.push(length & 0xFF);
+  bytes.push((length >>  8) & 0xFF);
+  bytes.push((length >> 16) & 0xFF);
+  bytes.push((length >> 24) & 0xFF);
+
+  for (let i = 0; i < hexString.length; i += 2) {
+    bytes.push(Number.parseInt(hexString.substr(i, 2), 16));
+  }
+
+  return bytes;
+}
+
 add_test(function test_ril_consts_cellbroadcast_misc() {
   // Must be 16 for indexing.
   do_check_eq(CB_DCS_LANG_GROUP_1.length, 16);
   do_check_eq(CB_DCS_LANG_GROUP_2.length, 16);
 
   // Array length must be even.
   do_check_eq(CB_NON_MMI_SETTABLE_RANGES.length & 0x01, 0);
   for (let i = 0; i < CB_NON_MMI_SETTABLE_RANGES.length;) {
@@ -429,8 +454,97 @@ add_test(function test_ril_worker__merge
   test([10, 13, 16, 19], 13, 16, [10, 19]);
   test([10, 13, 16, 19], 14, 15, [10, 13, 14, 15, 16, 19]);
   test([10, 13, 16, 19], 14, 16, [10, 13, 14, 19]);
   test([10, 13, 16, 19], 15, 16, [10, 13, 15, 19]);
 
   run_next_test();
 });
 
+
+/**
+ * Verify GsmPDUHelper#readUmtsCbMessage with numOfPages from 1 to 15.
+ */
+add_test(function test_GsmPDUHelper_readUmtsCbMessage_MultiParts() {
+  let CB_UMTS_MESSAGE_PAGE_SIZE = 82;
+  let CB_MAX_CONTENT_PER_PAGE_7BIT = 93;
+  let workerHelper = newInterceptWorker(),
+      worker = workerHelper.worker,
+      context = worker.ContextPool._contexts[0],
+      GsmPDUHelper = context.GsmPDUHelper;
+
+  function test_MultiParts(aNumOfPages) {
+    let pdu = buildHexStr(CB_UMTS_MESSAGE_TYPE_CBS, 2) // msg_type
+            + buildHexStr(0, 4) // skip msg_id
+            + buildHexStr(0, 4) // skip SN
+            + buildHexStr(0, 2) // skip dcs
+            + buildHexStr(aNumOfPages, 2); // set num_of_pages
+    for (let i = 1; i <= aNumOfPages; i++) {
+      pdu = pdu + buildHexStr(0, CB_UMTS_MESSAGE_PAGE_SIZE * 2)
+                + buildHexStr(CB_UMTS_MESSAGE_PAGE_SIZE, 2); // msg_info_length
+    }
+
+    worker.onRILMessage(0, newIncomingParcel(-1,
+                           RESPONSE_TYPE_UNSOLICITED,
+                           UNSOLICITED_RESPONSE_NEW_BROADCAST_SMS,
+                           hexStringToParcelByteArrayData(pdu)));
+
+    let postedMessage = workerHelper.postedMessage;
+    do_check_eq("cellbroadcast-received", postedMessage.rilMessageType);
+    do_check_eq(postedMessage.fullBody.length,
+                aNumOfPages * CB_MAX_CONTENT_PER_PAGE_7BIT);
+  }
+
+  [1, 5, 15].forEach(function(i) {
+    test_MultiParts(i);
+  });
+
+  run_next_test();
+});
+
+/**
+ * Verify GsmPDUHelper#readUmtsCbMessage with 8bit encoded.
+ */
+add_test(function test_GsmPDUHelper_readUmtsCbMessage_Binary() {
+  let CB_UMTS_MESSAGE_PAGE_SIZE = 82;
+  let CB_MAX_CONTENT_PER_PAGE_7BIT = 93;
+  let TEXT_BINARY = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+                  + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+                  + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+                  + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+                  + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+                  + "FFFF";
+  let workerHelper = newInterceptWorker(),
+      worker = workerHelper.worker,
+      context = worker.ContextPool._contexts[0],
+      GsmPDUHelper = context.GsmPDUHelper;
+
+  function test_MultiPartsBinary(aNumOfPages) {
+    let pdu = buildHexStr(CB_UMTS_MESSAGE_TYPE_CBS, 2) // msg_type
+            + buildHexStr(0, 4) // skip msg_id
+            + buildHexStr(0, 4) // skip SN
+            + buildHexStr(68, 2) // set DCS to 8bit data
+            + buildHexStr(aNumOfPages, 2); // set num_of_pages
+    for (let i = 1; i <= aNumOfPages; i++) {
+      pdu = pdu + TEXT_BINARY
+                + buildHexStr(CB_UMTS_MESSAGE_PAGE_SIZE, 2); // msg_info_length
+    }
+
+    worker.onRILMessage(0, newIncomingParcel(-1,
+                           RESPONSE_TYPE_UNSOLICITED,
+                           UNSOLICITED_RESPONSE_NEW_BROADCAST_SMS,
+                           hexStringToParcelByteArrayData(pdu)));
+
+    let postedMessage = workerHelper.postedMessage;
+    do_check_eq("cellbroadcast-received", postedMessage.rilMessageType);
+    do_check_eq(postedMessage.fullData.length,
+                aNumOfPages * CB_UMTS_MESSAGE_PAGE_SIZE);
+    for (let i = 0; i < postedMessage.fullData.length; i++) {
+      do_check_eq(postedMessage.fullData[i], 255);
+    }
+  }
+
+  [1, 5, 15].forEach(function(i) {
+    test_MultiPartsBinary(i);
+  });
+
+  run_next_test();
+});
--- a/dom/webidl/Headers.webidl
+++ b/dom/webidl/Headers.webidl
@@ -15,17 +15,17 @@ enum HeadersGuardEnum {
   "request",
   "request-no-cors",
   "response",
   "immutable"
 };
 
 [Constructor(optional HeadersInit init),
  // FIXME: Exposed=Window,Worker,
- Pref="dom.fetch.enabled"]
+ Func="mozilla::dom::Headers::PrefEnabled"]
 interface Headers {
   [Throws] void append(ByteString name, ByteString value);
   [Throws] void delete(ByteString name);
   [Throws] ByteString? get(ByteString name);
   [Throws] sequence<ByteString> getAll(ByteString name);
   [Throws] boolean has(ByteString name);
   [Throws] void set(ByteString name, ByteString value);
 
--- a/dom/webidl/Window.webidl
+++ b/dom/webidl/Window.webidl
@@ -74,17 +74,18 @@ typedef any Transferable;
 
   // user prompts
   [Throws] void alert();
   [Throws] void alert(DOMString message);
   [Throws] boolean confirm(optional DOMString message = "");
   [Throws] DOMString? prompt(optional DOMString message = "", optional DOMString default = "");
   [Throws] void print();
   //[Throws] any showModalDialog(DOMString url, optional any argument);
-  [Throws] any showModalDialog(DOMString url, optional any argument, optional DOMString options = "");
+  [Throws, Func="nsGlobalWindow::IsShowModalDialogEnabled"]
+  any showModalDialog(DOMString url, optional any argument, optional DOMString options = "");
 
   [Throws, CrossOriginCallable] void postMessage(any message, DOMString targetOrigin, optional sequence<Transferable> transfer);
 
   // also has obsolete members
 };
 Window implements GlobalEventHandlers;
 Window implements WindowEventHandlers;
 
--- a/dom/wifi/WifiP2pManager.jsm
+++ b/dom/wifi/WifiP2pManager.jsm
@@ -16,18 +16,16 @@ Cu.import("resource://gre/modules/system
 XPCOMUtils.defineLazyServiceGetter(this, "gSysMsgr",
                                    "@mozilla.org/system-message-internal;1",
                                    "nsISystemMessagesInternal");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gNetworkManager",
                                    "@mozilla.org/network/manager;1",
                                    "nsINetworkManager");
 
-const kNetworkInterfaceStateChangedTopic = "network-interface-state-changed";
-
 this.EXPORTED_SYMBOLS = ["WifiP2pManager"];
 
 const EVENT_IGNORED                      = -1;
 const EVENT_UNKNOWN                      = -2;
 
 // Events from supplicant for p2p.
 const EVENT_P2P_DEVICE_FOUND             = 0;
 const EVENT_P2P_DEVICE_LOST              = 1;
@@ -1471,19 +1469,17 @@ function P2pStateMachine(aP2pCommand, aN
     if (_p2pNetworkInterface.registered) {
       resetP2pNetworkInterface();
       gNetworkManager.unregisterNetworkInterface(_p2pNetworkInterface);
       _p2pNetworkInterface.registered = false;
     }
   }
 
   function handleP2pNetworkInterfaceStateChanged() {
-    Services.obs.notifyObservers(_p2pNetworkInterface,
-                                 kNetworkInterfaceStateChangedTopic,
-                                 null);
+    gNetworkManager.updateNetworkInterface(_p2pNetworkInterface);
   }
 
   // Handle 'P2P_GROUP_STARTED' event.
   //
   // @param aInfo information carried by "P2P_GROUP_REMOVED" event:
   //   .ifname
   //   .role: "GO" or "client".
   //
--- a/dom/wifi/WifiWorker.js
+++ b/dom/wifi/WifiWorker.js
@@ -19,17 +19,16 @@ Cu.import("resource://gre/modules/WifiP2
 
 var DEBUG = false; // set to true to show debug messages.
 
 const WIFIWORKER_CONTRACTID = "@mozilla.org/wifi/worker;1";
 const WIFIWORKER_CID        = Components.ID("{a14e8977-d259-433a-a88d-58dd44657e5b}");
 
 const WIFIWORKER_WORKER     = "resource://gre/modules/wifi_worker.js";
 
-const kNetworkInterfaceStateChangedTopic = "network-interface-state-changed";
 const kMozSettingsChangedObserverTopic   = "mozsettings-changed";
 
 const MAX_RETRIES_ON_AUTHENTICATION_FAILURE = 2;
 const MAX_SUPPLICANT_LOOP_ITERATIONS = 4;
 const MAX_RETRIES_ON_DHCP_FAILURE = 2;
 
 // Settings DB path for wifi
 const SETTINGS_WIFI_ENABLED            = "wifi.enabled";
@@ -974,19 +973,18 @@ var WifiManager = (function() {
         gNetworkManager.registerNetworkInterface(WifiNetworkInterface);
         WifiNetworkInterface.registered = true;
       }
       WifiNetworkInterface.state = Ci.nsINetworkInterface.NETWORK_STATE_DISCONNECTED;
       WifiNetworkInterface.ips = [];
       WifiNetworkInterface.prefixLengths = [];
       WifiNetworkInterface.gateways = [];
       WifiNetworkInterface.dnses = [];
-      Services.obs.notifyObservers(WifiNetworkInterface,
-                                   kNetworkInterfaceStateChangedTopic,
-                                   null);
+      gNetworkManager.updateNetworkInterface(WifiNetworkInterface);
+
       prepareForStartup(function() {
         loadDriver(function (status) {
           if (status < 0) {
             callback(status);
             manager.state = "UNINITIALIZED";
             return;
           }
           // This command is mandatory for Nexus 4. But some devices like
@@ -2180,19 +2178,17 @@ function WifiWorker() {
         });
 
         WifiNetworkInterface.state =
           Ci.nsINetworkInterface.NETWORK_STATE_DISCONNECTED;
         WifiNetworkInterface.ips = [];
         WifiNetworkInterface.prefixLengths = [];
         WifiNetworkInterface.gateways = [];
         WifiNetworkInterface.dnses = [];
-        Services.obs.notifyObservers(WifiNetworkInterface,
-                                     kNetworkInterfaceStateChangedTopic,
-                                     null);
+        gNetworkManager.updateNetworkInterface(WifiNetworkInterface);
 
         break;
       case "WPS_TIMEOUT":
         self._fireEvent("onwpstimeout", {});
         break;
       case "WPS_FAIL":
         self._fireEvent("onwpsfail", {});
         break;
@@ -2230,19 +2226,17 @@ function WifiWorker() {
     if (typeof this.info.dns1_str == "string" &&
         this.info.dns1_str.length) {
       WifiNetworkInterface.dnses.push(this.info.dns1_str);
     }
     if (typeof this.info.dns2_str == "string" &&
         this.info.dns2_str.length) {
       WifiNetworkInterface.dnses.push(this.info.dns2_str);
     }
-    Services.obs.notifyObservers(WifiNetworkInterface,
-                                 kNetworkInterfaceStateChangedTopic,
-                                 null);
+    gNetworkManager.updateNetworkInterface(WifiNetworkInterface);
 
     self.ipAddress = this.info.ipaddr_str;
 
     // We start the connection information timer when we associate, but
     // don't have our IP address until here. Make sure that we fire a new
     // connectionInformation event with the IP address the next time the
     // timer fires.
     self._lastConnectionInfo = null;
--- a/dom/workers/DataStore.cpp
+++ b/dom/workers/DataStore.cpp
@@ -411,18 +411,18 @@ public:
 protected:
   virtual bool
   MainThreadRun() MOZ_OVERRIDE
   {
     AssertIsOnMainThread();
 
     // Point WorkerDataStoreCursor to DataStoreCursor.
     nsRefPtr<DataStoreCursor> cursor = mBackingStore->Sync(mRevisionId, mRv);
-    nsMainThreadPtrHandle<DataStoreCursor> backingCursor =
-      new nsMainThreadPtrHolder<DataStoreCursor>(cursor);
+    nsMainThreadPtrHandle<DataStoreCursor> backingCursor(
+      new nsMainThreadPtrHolder<DataStoreCursor>(cursor));
     mWorkerCursor->SetBackingDataStoreCursor(backingCursor);
 
     return true;
   }
 };
 
 void
 WorkerDataStore::GetName(JSContext* aCx, nsAString& aName, ErrorResult& aRv)
--- a/dom/workers/Navigator.cpp
+++ b/dom/workers/Navigator.cpp
@@ -117,17 +117,17 @@ GetDataStoresStructuredCloneCallbacksRea
     return nullptr;
   }
 
   // Protect workerStoreObj from moving GC during ~nsRefPtr.
   JS::Rooted<JSObject*> workerStoreObj(aCx, nullptr);
   {
     nsRefPtr<WorkerDataStore> workerStore =
       new WorkerDataStore(workerPrivate->GlobalScope());
-    nsMainThreadPtrHandle<DataStore> backingStore = dataStoreholder;
+    nsMainThreadPtrHandle<DataStore> backingStore(dataStoreholder);
 
     // When we're on the worker thread, prepare a DataStoreChangeEventProxy.
     nsRefPtr<DataStoreChangeEventProxy> eventProxy =
       new DataStoreChangeEventProxy(workerPrivate, workerStore);
 
     // Add the DataStoreChangeEventProxy as an event listener on the main thread.
     nsRefPtr<DataStoreAddEventListenerRunnable> runnable =
       new DataStoreAddEventListenerRunnable(workerPrivate,
--- a/dom/workers/RegisterBindings.cpp
+++ b/dom/workers/RegisterBindings.cpp
@@ -59,36 +59,32 @@ WorkerPrivate::RegisterBindings(JSContex
     return false;
   }
 
   // Init other paris-bindings.
   if (!ConsoleBinding::GetConstructorObject(aCx, aGlobal) ||
       !DOMExceptionBinding::GetConstructorObject(aCx, aGlobal) ||
       !EventBinding::GetConstructorObject(aCx, aGlobal) ||
       !FileReaderSyncBinding_workers::GetConstructorObject(aCx, aGlobal) ||
+      (HeadersBinding::ConstructorEnabled(aCx, aGlobal) &&
+       !HeadersBinding::GetConstructorObject(aCx, aGlobal)) ||
       !ImageDataBinding::GetConstructorObject(aCx, aGlobal) ||
       !MessageEventBinding::GetConstructorObject(aCx, aGlobal) ||
       !MessagePortBinding::GetConstructorObject(aCx, aGlobal) ||
       !PromiseBinding::GetConstructorObject(aCx, aGlobal) ||
       !TextDecoderBinding::GetConstructorObject(aCx, aGlobal) ||
       !TextEncoderBinding::GetConstructorObject(aCx, aGlobal) ||
       !XMLHttpRequestBinding_workers::GetConstructorObject(aCx, aGlobal) ||
       !XMLHttpRequestUploadBinding_workers::GetConstructorObject(aCx, aGlobal) ||
       !URLBinding_workers::GetConstructorObject(aCx, aGlobal) ||
       !URLSearchParamsBinding::GetConstructorObject(aCx, aGlobal) ||
       !WorkerBinding::GetConstructorObject(aCx, aGlobal) ||
       !WorkerLocationBinding_workers::GetConstructorObject(aCx, aGlobal) ||
       !WorkerNavigatorBinding_workers::GetConstructorObject(aCx, aGlobal)) {
     return false;
   }
 
-  if (DOMFetchEnabled()) {
-    if (!HeadersBinding::GetConstructorObject(aCx, aGlobal)) {
-      return false;
-    }
-  }
-
   if (!JS_DefineProfilingFunctions(aCx, aGlobal)) {
     return false;
   }
 
   return true;
 }
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -79,16 +79,19 @@ USING_WORKERS_NAMESPACE
 
 using mozilla::MutexAutoLock;
 using mozilla::MutexAutoUnlock;
 using mozilla::Preferences;
 
 // The size of the worker runtime heaps in bytes. May be changed via pref.
 #define WORKER_DEFAULT_RUNTIME_HEAPSIZE 32 * 1024 * 1024
 
+// The size of the generational GC nursery for workers, in bytes.
+#define WORKER_DEFAULT_NURSERY_SIZE 1 * 1024 * 1024
+
 // The size of the worker JS allocation threshold in MB. May be changed via pref.
 #define WORKER_DEFAULT_ALLOCATION_THRESHOLD 30
 
 // The C stack size. We use the same stack size on all platforms for
 // consistency.
 #define WORKER_STACK_SIZE 256 * sizeof(size_t) * 1024
 
 // Half the size of the actual C stack, to be safe.
@@ -309,17 +312,17 @@ GenerateSharedWorkerKey(const nsACString
     }
   }
 
   aKey.Append('|');
   aKey.Append(aScriptSpec);
 }
 
 void
-LoadRuntimeAndContextOptions(const char* aPrefName, void* /* aClosure */)
+LoadRuntimeOptions(const char* aPrefName, void* /* aClosure */)
 {
   AssertIsOnMainThread();
 
   RuntimeService* rts = RuntimeService::GetService();
   if (!rts && !gRuntimeServiceDuringInit) {
     // May be shutting down, just bail.
     return;
   }
@@ -357,39 +360,24 @@ LoadRuntimeAndContextOptions(const char*
   }
   if (GetWorkerPref<bool>(NS_LITERAL_CSTRING("native_regexp"))) {
     runtimeOptions.setNativeRegExp(true);
   }
   if (GetWorkerPref<bool>(NS_LITERAL_CSTRING("werror"))) {
     runtimeOptions.setWerror(true);
   }
 
-  // Common options.
-  JS::ContextOptions commonContextOptions = kRequiredContextOptions;
   if (GetWorkerPref<bool>(NS_LITERAL_CSTRING("strict"))) {
-    commonContextOptions.setExtraWarnings(true);
+    runtimeOptions.setExtraWarnings(true);
   }
 
-  // Content options.
-  JS::ContextOptions contentContextOptions = commonContextOptions;
-
-  // Chrome options.
-  JS::ContextOptions chromeContextOptions = commonContextOptions;
-#ifdef DEBUG
-  if (GetWorkerPref<bool>(NS_LITERAL_CSTRING("strict.debug"))) {
-    chromeContextOptions.setExtraWarnings(true);
-  }
-#endif
-
-  RuntimeService::SetDefaultRuntimeAndContextOptions(runtimeOptions,
-                                                     contentContextOptions,
-                                                     chromeContextOptions);
+  RuntimeService::SetDefaultRuntimeOptions(runtimeOptions);
 
   if (rts) {
-    rts->UpdateAllWorkerRuntimeAndContextOptions();
+    rts->UpdateAllWorkerRuntimeOptions();
   }
 }
 
 #ifdef JS_GC_ZEAL
 void
 LoadGCZealOptions(const char* /* aPrefName */, void* /* aClosure */)
 {
   AssertIsOnMainThread();
@@ -839,35 +827,34 @@ CreateJSContextForWorker(WorkerPrivate* 
   JS_SetRuntimePrivate(aRuntime, rtPrivate);
 
   JS_SetErrorReporter(workerCx, ErrorReporter);
 
   JS_SetInterruptCallback(aRuntime, InterruptCallback);
 
   js::SetCTypesActivityCallback(aRuntime, CTypesActivityCallback);
 
-  JS::ContextOptionsRef(workerCx) =
-    aWorkerPrivate->IsChromeWorker() ? settings.chrome.contextOptions
-                                     : settings.content.contextOptions;
+  JS::ContextOptionsRef(workerCx) = kRequiredContextOptions;
 
 #ifdef JS_GC_ZEAL
   JS_SetGCZeal(workerCx, settings.gcZeal, settings.gcZealFrequency);
 #endif
 
   return workerCx;
 }
 
 class WorkerJSRuntime : public mozilla::CycleCollectedJSRuntime
 {
 public:
   // The heap size passed here doesn't matter, we will change it later in the
   // call to JS_SetGCParameter inside CreateJSContextForWorker.
   WorkerJSRuntime(JSRuntime* aParentRuntime, WorkerPrivate* aWorkerPrivate)
     : CycleCollectedJSRuntime(aParentRuntime,
-                              WORKER_DEFAULT_RUNTIME_HEAPSIZE),
+                              WORKER_DEFAULT_RUNTIME_HEAPSIZE,
+                              WORKER_DEFAULT_NURSERY_SIZE),
     mWorkerPrivate(aWorkerPrivate)
   {
   }
 
   ~WorkerJSRuntime()
   {
     auto rtPrivate = static_cast<WorkerThreadRuntimePrivate*>(JS_GetRuntimePrivate(Runtime()));
     delete rtPrivate;
@@ -1690,20 +1677,18 @@ RuntimeService::Init()
 {
   AssertIsOnMainThread();
 
   nsLayoutStatics::AddRef();
 
   // Initialize JSSettings.
   if (!sDefaultJSSettings.gcSettings[0].IsSet()) {
     sDefaultJSSettings.runtimeOptions = JS::RuntimeOptions();
-    sDefaultJSSettings.chrome.contextOptions = kRequiredContextOptions;
     sDefaultJSSettings.chrome.maxScriptRuntime = -1;
     sDefaultJSSettings.chrome.compartmentOptions.setVersion(JSVERSION_LATEST);
-    sDefaultJSSettings.content.contextOptions = kRequiredContextOptions;
     sDefaultJSSettings.content.maxScriptRuntime = MAX_SCRIPT_RUN_TIME_SEC;
 #ifdef JS_GC_ZEAL
     sDefaultJSSettings.gcZealFrequency = JS_DEFAULT_ZEAL_FREQ;
     sDefaultJSSettings.gcZeal = 0;
 #endif
     SetDefaultJSGCSettings(JSGC_MAX_BYTES, WORKER_DEFAULT_RUNTIME_HEAPSIZE);
     SetDefaultJSGCSettings(JSGC_ALLOCATION_THRESHOLD,
                            WORKER_DEFAULT_ALLOCATION_THRESHOLD);
@@ -1772,21 +1757,21 @@ RuntimeService::Init()
                                   WorkerPrefChanged,
                                   PREF_DOM_WINDOW_DUMP_ENABLED,
                                   reinterpret_cast<void *>(WORKERPREF_DUMP))) ||
 #endif
       NS_FAILED(Preferences::RegisterCallbackAndCall(
                                   WorkerPrefChanged,
                                   PREF_DOM_FETCH_ENABLED,
                                   reinterpret_cast<void *>(WORKERPREF_DOM_FETCH))) ||
-      NS_FAILED(Preferences::RegisterCallback(LoadRuntimeAndContextOptions,
+      NS_FAILED(Preferences::RegisterCallback(LoadRuntimeOptions,
                                               PREF_JS_OPTIONS_PREFIX,
                                               nullptr)) ||
       NS_FAILED(Preferences::RegisterCallbackAndCall(
-                                                   LoadRuntimeAndContextOptions,
+                                                   LoadRuntimeOptions,
                                                    PREF_WORKERS_OPTIONS_PREFIX,
                                                    nullptr)) ||
       NS_FAILED(Preferences::RegisterCallbackAndCall(
                                                  JSVersionChanged,
                                                  PREF_WORKERS_LATEST_JS_VERSION,
                                                  nullptr))) {
     NS_WARNING("Failed to register pref callbacks!");
   }
@@ -1925,20 +1910,20 @@ RuntimeService::Cleanup()
   }
 
   NS_ASSERTION(!mWindowMap.Count(), "All windows should have been released!");
 
   if (mObserved) {
     if (NS_FAILED(Preferences::UnregisterCallback(JSVersionChanged,
                                                   PREF_WORKERS_LATEST_JS_VERSION,
                                                   nullptr)) ||
-        NS_FAILED(Preferences::UnregisterCallback(LoadRuntimeAndContextOptions,
+        NS_FAILED(Preferences::UnregisterCallback(LoadRuntimeOptions,
                                                   PREF_JS_OPTIONS_PREFIX,
                                                   nullptr)) ||
-        NS_FAILED(Preferences::UnregisterCallback(LoadRuntimeAndContextOptions,
+        NS_FAILED(Preferences::UnregisterCallback(LoadRuntimeOptions,
                                                   PREF_WORKERS_OPTIONS_PREFIX,
                                                   nullptr)) ||
         NS_FAILED(Preferences::UnregisterCallback(
                                   WorkerPrefChanged,
                                   PREF_DOM_FETCH_ENABLED,
                                   reinterpret_cast<void *>(WORKERPREF_DOM_FETCH))) ||
 #if DUMP_CONTROLLED_BY_PREF
         NS_FAILED(Preferences::UnregisterCallback(
@@ -2380,22 +2365,19 @@ RuntimeService::NoteIdleThread(WorkerThr
   // Schedule timer.
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mIdleThreadTimer->InitWithFuncCallback(
                                                  ShutdownIdleThreads, nullptr,
                                                  IDLE_THREAD_TIMEOUT_SEC * 1000,
                                                  nsITimer::TYPE_ONE_SHOT)));
 }
 
 void
-RuntimeService::UpdateAllWorkerRuntimeAndContextOptions()
+RuntimeService::UpdateAllWorkerRuntimeOptions()
 {
-  BROADCAST_ALL_WORKERS(UpdateRuntimeAndContextOptions,
-                        sDefaultJSSettings.runtimeOptions,
-                        sDefaultJSSettings.content.contextOptions,
-                        sDefaultJSSettings.chrome.contextOptions);
+  BROADCAST_ALL_WORKERS(UpdateRuntimeOptions, sDefaultJSSettings.runtimeOptions);
 }
 
 void
 RuntimeService::UpdateAllWorkerPreference(WorkerPreference aPref, bool aValue)
 {
   BROADCAST_ALL_WORKERS(UpdatePreference, aPref, aValue);
 }
 
--- a/dom/workers/RuntimeService.h
+++ b/dom/workers/RuntimeService.h
@@ -188,29 +188,24 @@ public:
   static void
   GetDefaultPreferences(bool aPreferences[WORKERPREF_COUNT])
   {
     AssertIsOnMainThread();
     memcpy(aPreferences, sDefaultPreferences, WORKERPREF_COUNT * sizeof(bool));
   }
 
   static void
-  SetDefaultRuntimeAndContextOptions(
-                                    const JS::RuntimeOptions& aRuntimeOptions,
-                                    const JS::ContextOptions& aContentCxOptions,
-                                    const JS::ContextOptions& aChromeCxOptions)
+  SetDefaultRuntimeOptions(const JS::RuntimeOptions& aRuntimeOptions)
   {
     AssertIsOnMainThread();
     sDefaultJSSettings.runtimeOptions = aRuntimeOptions;
-    sDefaultJSSettings.content.contextOptions = aContentCxOptions;
-    sDefaultJSSettings.chrome.contextOptions = aChromeCxOptions;
   }
 
   void
-  UpdateAllWorkerRuntimeAndContextOptions();
+  UpdateAllWorkerRuntimeOptions();
 
   void
   UpdateAllWorkerPreference(WorkerPreference aPref, bool aValue);
 
   static void
   SetDefaultJSGCSettings(JSGCParamKey aKey, uint32_t aValue)
   {
     AssertIsOnMainThread();
--- a/dom/workers/ScriptLoader.cpp
+++ b/dom/workers/ScriptLoader.cpp
@@ -719,16 +719,21 @@ ScriptExecutorRunnable::WorkerRun(JSCont
   // Note that we read a pref that is cached on the main thread. This is benignly
   // racey.
   if (xpc::ShouldDiscardSystemSource()) {
     bool discard = aWorkerPrivate->UsesSystemPrincipal() ||
                    aWorkerPrivate->IsInPrivilegedApp();
     JS::CompartmentOptionsRef(global).setDiscardSource(discard);
   }
 
+  // Similar to the above.
+  if (xpc::ExtraWarningsForSystemJS() && aWorkerPrivate->UsesSystemPrincipal()) {
+      JS::CompartmentOptionsRef(global).extraWarningsOverride().set(true);
+  }
+
   for (uint32_t index = mFirstIndex; index <= mLastIndex; index++) {
     ScriptLoadInfo& loadInfo = loadInfos.ElementAt(index);
 
     NS_ASSERTION(!loadInfo.mChannel, "Should no longer have a channel!");
     NS_ASSERTION(loadInfo.mExecutionScheduled, "Should be scheduled!");
     NS_ASSERTION(!loadInfo.mExecutionResult, "Should not have executed yet!");
 
     if (NS_FAILED(loadInfo.mLoadResult)) {
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -256,18 +256,18 @@ public:
                                                     mScriptSpec,
                                                     mRegistration->mScope,
                                                     getter_AddRefs(serviceWorker));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       swm->RejectUpdatePromiseObservers(mRegistration, rv);
       return;
     }
 
-    nsMainThreadPtrHandle<ServiceWorkerUpdateInstance> handle =
-      new nsMainThreadPtrHolder<ServiceWorkerUpdateInstance>(this);
+    nsMainThreadPtrHandle<ServiceWorkerUpdateInstance> handle(
+      new nsMainThreadPtrHolder<ServiceWorkerUpdateInstance>(this));
     // FIXME(nsm): Deal with error case (worker failed to download, redirect,
     // parse) in error handler patch.
     nsRefPtr<FinishSuccessfulFetchWorkerRunnable> r =
       new FinishSuccessfulFetchWorkerRunnable(serviceWorker->GetWorkerPrivate(), handle);
 
     AutoSafeJSContext cx;
     if (!r->Dispatch(cx)) {
       swm->RejectUpdatePromiseObservers(mRegistration, NS_ERROR_FAILURE);
@@ -1004,18 +1004,18 @@ ServiceWorkerManager::Install(ServiceWor
                               ServiceWorkerInfo* aServiceWorkerInfo)
 {
   AssertIsOnMainThread();
   aRegistration->mInstallingWorker = aServiceWorkerInfo;
   MOZ_ASSERT(aRegistration->mInstallingWorker);
   InvalidateServiceWorkerContainerWorker(aRegistration,
                                          WhichServiceWorker::INSTALLING_WORKER);
 
-  nsMainThreadPtrHandle<ServiceWorkerRegistration> handle =
-    new nsMainThreadPtrHolder<ServiceWorkerRegistration>(aRegistration);
+  nsMainThreadPtrHandle<ServiceWorkerRegistration> handle(
+    new nsMainThreadPtrHolder<ServiceWorkerRegistration>(aRegistration));
 
   nsRefPtr<ServiceWorker> serviceWorker;
   nsresult rv =
     CreateServiceWorker(aServiceWorkerInfo->GetScriptSpec(),
                         aRegistration->mScope,
                         getter_AddRefs(serviceWorker));
 
   if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -1080,18 +1080,18 @@ public:
     nsresult rv =
       swm->CreateServiceWorker(mRegistration->mCurrentWorker->GetScriptSpec(),
                                mRegistration->mScope,
                                getter_AddRefs(serviceWorker));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
-    nsMainThreadPtrHandle<ServiceWorkerRegistration> handle =
-      new nsMainThreadPtrHolder<ServiceWorkerRegistration>(mRegistration);
+    nsMainThreadPtrHandle<ServiceWorkerRegistration> handle(
+      new nsMainThreadPtrHolder<ServiceWorkerRegistration>(mRegistration));
 
     nsRefPtr<ActivateEventRunnable> r =
       new ActivateEventRunnable(serviceWorker->GetWorkerPrivate(), handle);
 
     AutoSafeJSContext cx;
     if (!r->Dispatch(cx)) {
       return NS_ERROR_FAILURE;
     }
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -1542,42 +1542,33 @@ private:
       mTimer->Cancel();
       mTimer = nullptr;
     }
 
     return true;
   }
 };
 
-class UpdateRuntimeAndContextOptionsRunnable MOZ_FINAL : public WorkerControlRunnable
+class UpdateRuntimeOptionsRunnable MOZ_FINAL : public WorkerControlRunnable
 {
   JS::RuntimeOptions mRuntimeOptions;
-  JS::ContextOptions mContentCxOptions;
-  JS::ContextOptions mChromeCxOptions;
 
 public:
-  UpdateRuntimeAndContextOptionsRunnable(
+  UpdateRuntimeOptionsRunnable(
                                     WorkerPrivate* aWorkerPrivate,
-                                    const JS::RuntimeOptions& aRuntimeOptions,
-                                    const JS::ContextOptions& aContentCxOptions,
-                                    const JS::ContextOptions& aChromeCxOptions)
+                                    const JS::RuntimeOptions& aRuntimeOptions)
   : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
-    mRuntimeOptions(aRuntimeOptions),
-    mContentCxOptions(aContentCxOptions),
-    mChromeCxOptions(aChromeCxOptions)
+    mRuntimeOptions(aRuntimeOptions)
   { }
 
 private:
   virtual bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
   {
-    aWorkerPrivate->UpdateRuntimeAndContextOptionsInternal(aCx,
-                                                           mRuntimeOptions,
-                                                           mContentCxOptions,
-                                                           mChromeCxOptions);
+    aWorkerPrivate->UpdateRuntimeOptionsInternal(aCx, mRuntimeOptions);
     return true;
   }
 };
 
 class UpdatePreferenceRunnable MOZ_FINAL : public WorkerControlRunnable
 {
   WorkerPreference mPref;
   bool mValue;
@@ -2876,36 +2867,29 @@ WorkerPrivateParent<Derived>::GetInnerWi
   AssertIsOnMainThread();
   NS_ASSERTION(!mLoadInfo.mWindow || mLoadInfo.mWindow->IsInnerWindow(),
                "Outer window?");
   return mLoadInfo.mWindow ? mLoadInfo.mWindow->WindowID() : 0;
 }
 
 template <class Derived>
 void
-WorkerPrivateParent<Derived>::UpdateRuntimeAndContextOptions(
+WorkerPrivateParent<Derived>::UpdateRuntimeOptions(
                                     JSContext* aCx,
-                                    const JS::RuntimeOptions& aRuntimeOptions,
-                                    const JS::ContextOptions& aContentCxOptions,
-                                    const JS::ContextOptions& aChromeCxOptions)
+                                    const JS::RuntimeOptions& aRuntimeOptions)
 {
   AssertIsOnParentThread();
 
   {
     MutexAutoLock lock(mMutex);
     mJSSettings.runtimeOptions = aRuntimeOptions;
-    mJSSettings.content.contextOptions = aContentCxOptions;
-    mJSSettings.chrome.contextOptions = aChromeCxOptions;
-  }
-
-  nsRefPtr<UpdateRuntimeAndContextOptionsRunnable> runnable =
-    new UpdateRuntimeAndContextOptionsRunnable(ParentAsWorkerPrivate(),
-                                               aRuntimeOptions,
-                                               aContentCxOptions,
-                                               aChromeCxOptions);
+  }
+
+  nsRefPtr<UpdateRuntimeOptionsRunnable> runnable =
+    new UpdateRuntimeOptionsRunnable(ParentAsWorkerPrivate(), aRuntimeOptions);
   if (!runnable->Dispatch(aCx)) {
     NS_WARNING("Failed to update worker context options!");
     JS_ClearPendingException(aCx);
   }
 }
 
 template <class Derived>
 void
@@ -5529,31 +5513,26 @@ WorkerPrivate::RescheduleTimeoutTimer(JS
     JS_ReportError(aCx, "Failed to start timer!");
     return false;
   }
 
   return true;
 }
 
 void
-WorkerPrivate::UpdateRuntimeAndContextOptionsInternal(
+WorkerPrivate::UpdateRuntimeOptionsInternal(
                                     JSContext* aCx,
-                                    const JS::RuntimeOptions& aRuntimeOptions,
-                                    const JS::ContextOptions& aContentCxOptions,
-                                    const JS::ContextOptions& aChromeCxOptions)
+                                    const JS::RuntimeOptions& aRuntimeOptions)
 {
   AssertIsOnWorkerThread();
 
   JS::RuntimeOptionsRef(aCx) = aRuntimeOptions;
-  JS::ContextOptionsRef(aCx) = IsChromeWorker() ? aChromeCxOptions : aContentCxOptions;
 
   for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
-    mChildWorkers[index]->UpdateRuntimeAndContextOptions(aCx, aRuntimeOptions,
-                                                         aContentCxOptions,
-                                                         aChromeCxOptions);
+    mChildWorkers[index]->UpdateRuntimeOptions(aCx, aRuntimeOptions);
   }
 }
 
 void
 WorkerPrivate::UpdatePreferenceInternal(JSContext* aCx, WorkerPreference aPref, bool aValue)
 {
   AssertIsOnWorkerThread();
   MOZ_ASSERT(aPref >= 0 && aPref < WORKERPREF_COUNT);
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -388,20 +388,18 @@ public:
                                uint64_t aMessagePortSerial,
                                JSAutoStructuredCloneBuffer&& aBuffer,
                                nsTArray<nsCOMPtr<nsISupports>>& aClonedObjects);
 
   uint64_t
   GetInnerWindowId();
 
   void
-  UpdateRuntimeAndContextOptions(JSContext* aCx,
-                                 const JS::RuntimeOptions& aRuntimeOptions,
-                                 const JS::ContextOptions& aContentCxOptions,
-                                 const JS::ContextOptions& aChromeCxOptions);
+  UpdateRuntimeOptions(JSContext* aCx,
+                       const JS::RuntimeOptions& aRuntimeOptions);
 
   void
   UpdatePreference(JSContext* aCx, WorkerPreference aPref, bool aValue);
 
   void
   UpdateJSWorkerMemoryParameter(JSContext* aCx, JSGCParamKey key,
                                 uint32_t value);
 
@@ -924,21 +922,17 @@ public:
   void
   CloseHandlerFinished()
   {
     AssertIsOnWorkerThread();
     mCloseHandlerFinished = true;
   }
 
   void
-  UpdateRuntimeAndContextOptionsInternal(
-                                    JSContext* aCx,
-                                    const JS::RuntimeOptions& aRuntimeOptions,
-                                    const JS::ContextOptions& aContentCxOptions,
-                                    const JS::ContextOptions& aChromeCxOptions);
+  UpdateRuntimeOptionsInternal(JSContext* aCx, const JS::RuntimeOptions& aRuntimeOptions);
 
   void
   UpdatePreferenceInternal(JSContext* aCx, WorkerPreference aPref, bool aValue);
 
   void
   UpdateJSWorkerMemoryParameterInternal(JSContext* aCx, JSGCParamKey key, uint32_t aValue);
 
   enum WorkerRanOrNot {
--- a/dom/workers/Workers.h
+++ b/dom/workers/Workers.h
@@ -90,22 +90,21 @@ struct JSSettings
 
   // There are several settings that we know we need so it makes sense to
   // preallocate here.
   typedef JSGCSetting JSGCSettingsArray[kGCSettingsArraySize];
 
   // Settings that change based on chrome/content context.
   struct JSContentChromeSettings
   {
-    JS::ContextOptions contextOptions;
     JS::CompartmentOptions compartmentOptions;
     int32_t maxScriptRuntime;
 
     JSContentChromeSettings()
-    : contextOptions(), compartmentOptions(), maxScriptRuntime(0)
+    : compartmentOptions(), maxScriptRuntime(0)
     { }
   };
 
   JSContentChromeSettings chrome;
   JSContentChromeSettings content;
   JSGCSettingsArray gcSettings;
   JS::RuntimeOptions runtimeOptions;
 
new file mode 100644
--- /dev/null
+++ b/gfx/layers/AxisPhysicsMSDModel.cpp
@@ -0,0 +1,100 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "AxisPhysicsMSDModel.h"
+#include <math.h>                       // for sqrt and fabs
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * Constructs an AxisPhysicsMSDModel with initial values for state.
+ *
+ * @param aInitialPosition sets the initial position of the simulated spring,
+ *        in AppUnits.
+ * @param aInitialDestination sets the resting position of the simulated spring,
+ *        in AppUnits.
+ * @param aInitialVelocity sets the initial velocity of the simulated spring,
+ *        in AppUnits / second.  Critically-damped and over-damped systems are
+ *        guaranteed not to overshoot aInitialDestination if this is set to 0;
+ *        however, it is possible to overshoot and oscillate if not set to 0 or
+ *        the system is under-damped.
+ * @param aSpringConstant sets the strength of the simulated spring.  Greater
+ *        values of mSpringConstant result in a stiffer / stronger spring.
+ * @param aDampingRatio controls the amount of dampening force and determines
+ *        if the system is under-damped, critically-damped, or over-damped.
+ */
+AxisPhysicsMSDModel::AxisPhysicsMSDModel(double aInitialPosition,
+                                         double aInitialDestination,
+                                         double aInitialVelocity,
+                                         double aSpringConstant,
+                                         double aDampingRatio)
+  : AxisPhysicsModel(aInitialPosition, aInitialVelocity)
+  , mDestination(aInitialDestination)
+  , mSpringConstant(aSpringConstant)
+  , mSpringConstantSqrtXTwo(sqrt(mSpringConstant) * 2.0)
+  , mDampingRatio(aDampingRatio)
+{
+}
+
+AxisPhysicsMSDModel::~AxisPhysicsMSDModel()
+{
+}
+
+double
+AxisPhysicsMSDModel::Acceleration(const State &aState)
+{
+  // Simulate a Mass-Damper-Spring Model; assume a unit mass
+
+  // Hooke’s Law: http://en.wikipedia.org/wiki/Hooke%27s_law
+  double spring_force = (mDestination - aState.p) * mSpringConstant;
+  double damp_force = -aState.v * mDampingRatio * mSpringConstantSqrtXTwo;
+
+  return spring_force + damp_force;
+}
+
+
+double
+AxisPhysicsMSDModel::GetDestination()
+{
+  return mDestination;
+}
+
+void
+AxisPhysicsMSDModel::SetDestination(double aDestination)
+{
+  mDestination = aDestination;
+}
+
+bool
+AxisPhysicsMSDModel::IsFinished()
+{
+  // In order to satisfy the condition of reaching the destination, the distance
+  // between the simulation position and the destination must be less than
+  // kFinishDistance while the speed is simultaneously less than
+  // kFinishVelocity.  This enables an under-damped system to overshoot the
+  // destination when desired without prematurely triggering the finished state.
+
+  // As the number of app units per css pixel is 60 and retina / HiDPI displays
+  // may display two pixels for every css pixel, setting kFinishDistance to 30.0
+  // ensures that there will be no perceptable shift in position at the end
+  // of the animation.
+  const double kFinishDistance = 30.0;
+
+  // If kFinishVelocity is set too low, the animation may end long after
+  // oscillation has finished, resulting in unnecessary processing.
+  // If set too high, the animation may prematurely terminate when expected
+  // to overshoot the destination in an under-damped system.
+  // 60.0 was selected through experimentation that revealed that a
+  // critically damped system will terminate within 100ms.
+  const double kFinishVelocity = 60.0;
+
+  return fabs(mDestination - GetPosition ()) < kFinishDistance
+    && fabs(GetVelocity()) <= kFinishVelocity;
+}
+
+}
+}
new file mode 100644
--- /dev/null
+++ b/gfx/layers/AxisPhysicsMSDModel.h
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_AxisPhysicsMSDModel_h
+#define mozilla_layers_AxisPhysicsMSDModel_h
+
+#include "AxisPhysicsModel.h"
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * AxisPhysicsMSDModel encapsulates a 1-dimensional MSD (Mass-Spring-Damper)
+ * model.  A unit mass is assumed.
+ */
+class AxisPhysicsMSDModel : public AxisPhysicsModel {
+public:
+  AxisPhysicsMSDModel(double aInitialPosition, double aInitialDestination,
+                      double aInitialVelocity, double aSpringConstant,
+                      double aDampingRatio);
+
+  ~AxisPhysicsMSDModel();
+
+  /**
+   * Gets the raw destination of this axis at this moment.
+   */
+  double GetDestination();
+
+  /**
+   * Sets the raw destination of this axis at this moment.
+   */
+  void SetDestination(double aDestination);
+
+  /**
+   * Returns true when the position is close to the destination and the
+   * velocity is low.
+   */
+  bool IsFinished();
+
+protected:
+  virtual double Acceleration(const State &aState);
+
+private:
+
+  /**
+   * mDestination represents the target position and the resting position of
+   * the simulated spring.
+   */
+  double mDestination;
+
+  /**
+   * Greater values of mSpringConstant result in a stiffer / stronger spring.
+   */
+  double mSpringConstant;
+
+  /**
+   * mSpringConstantSqrtTimesTwo is calculated from mSpringConstant to reduce
+   * calculations performed in the inner loop.
+   */
+  double mSpringConstantSqrtXTwo;
+
+  /**
+   * Damping Ratio: http://en.wikipedia.org/wiki/Damping_ratio
+   *
+   * When mDampingRatio < 1.0, this is an under damped system.
+   * - Overshoots destination and oscillates with the amplitude gradually
+   *   decreasing to zero.
+   *
+   * When mDampingRatio == 1.0, this is a critically damped system.
+   * - Reaches destination as quickly as possible without oscillating.
+   *
+   * When mDampingRatio > 1.0, this is an over damped system.
+   * - Reaches destination (exponentially decays) without oscillating.
+   */
+  double mDampingRatio;
+
+};
+
+
+}
+}
+
+#endif
new file mode 100644
--- /dev/null
+++ b/gfx/layers/AxisPhysicsModel.cpp
@@ -0,0 +1,121 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "AxisPhysicsModel.h"
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * The simulation is advanced forward in time with a fixed time step to ensure
+ * that it remains deterministic given variable framerates.  To determine the
+ * position at any variable time, two samples are interpolated.
+ *
+ * kFixedtimestep is set to 120hz in order to ensure that every frame in a
+ * common 60hz refresh rate display will have at least one physics simulation
+ * sample.  More accuracy can be obtained by reducing kFixedTimestep to smaller
+ * intervals, such as 240hz or 1000hz, at the cost of more CPU cycles.  If
+ * kFixedTimestep is increased to much longer intervals, interpolation will
+ * become less effective at reducing temporal jitter and the simulation will
+ * lose accuracy.
+ */
+const double AxisPhysicsModel::kFixedTimestep = 1.0 / 120.0; // 120hz
+
+/**
+ * Constructs an AxisPhysicsModel with initial values for state.
+ *
+ * @param aInitialPosition sets the initial position of the simulation,
+ *        in AppUnits.
+ * @param aInitialVelocity sets the initial velocity of the simulation,
+ *        in AppUnits / second.
+ */
+AxisPhysicsModel::AxisPhysicsModel(double aInitialPosition,
+                                   double aInitialVelocity)
+  : mProgress(1.0)
+  , mPrevState(aInitialPosition, aInitialVelocity)
+  , mNextState(aInitialPosition, aInitialVelocity)
+{
+
+}
+
+AxisPhysicsModel::~AxisPhysicsModel()
+{
+
+}
+
+double
+AxisPhysicsModel::GetVelocity()
+{
+  return LinearInterpolate(mPrevState.v, mNextState.v, mProgress);
+}
+
+double
+AxisPhysicsModel::GetPosition()
+{
+  return LinearInterpolate(mPrevState.p, mNextState.p, mProgress);
+}
+
+void
+AxisPhysicsModel::SetVelocity(double aVelocity)
+{
+  mNextState.v = aVelocity;
+  mNextState.p = GetPosition();
+  mProgress = 1.0;
+}
+
+void
+AxisPhysicsModel::SetPosition(double aPosition)
+{
+  mNextState.v = GetVelocity();
+  mNextState.p = aPosition;
+  mProgress = 1.0;
+}
+
+void
+AxisPhysicsModel::Simulate(const TimeDuration& aDeltaTime)
+{
+  for(mProgress += aDeltaTime.ToSeconds() / kFixedTimestep;
+      mProgress > 1.0; mProgress -= 1.0) {
+    Integrate(kFixedTimestep);
+  }
+}
+
+void
+AxisPhysicsModel::Integrate(double aDeltaTime)
+{
+  mPrevState = mNextState;
+
+  // RK4 (Runge-Kutta method) Integration
+  // http://en.wikipedia.org/wiki/Runge%E2%80%93Kutta_methods
+  Derivative a = Evaluate( mNextState, 0.0, Derivative() );
+  Derivative b = Evaluate( mNextState, aDeltaTime * 0.5, a );
+  Derivative c = Evaluate( mNextState, aDeltaTime * 0.5, b );
+  Derivative d = Evaluate( mNextState, aDeltaTime, c );
+
+  double dpdt = 1.0 / 6.0 * (a.dp + 2.0 * (b.dp + c.dp) + d.dp);
+  double dvdt = 1.0 / 6.0 * (a.dv + 2.0 * (b.dv + c.dv) + d.dv);
+
+  mNextState.p += dpdt * aDeltaTime;
+  mNextState.v += dvdt * aDeltaTime;
+}
+
+AxisPhysicsModel::Derivative
+AxisPhysicsModel::Evaluate(const State &aInitState, double aDeltaTime,
+                           const Derivative &aDerivative)
+{
+  State state( aInitState.p + aDerivative.dp*aDeltaTime, aInitState.v + aDerivative.dv*aDeltaTime );
+
+  return Derivative( state.v, Acceleration(state) );
+}
+
+double
+AxisPhysicsModel::LinearInterpolate(double aV1, double aV2, double aBlend)
+{
+  return aV1 * (1.0 - aBlend) + aV2 * aBlend;
+}
+
+}
+}
new file mode 100644
--- /dev/null
+++ b/gfx/layers/AxisPhysicsModel.h
@@ -0,0 +1,131 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_AxisPhysicsModel_h
+#define mozilla_layers_AxisPhysicsModel_h
+
+#include "AxisPhysicsModel.h"
+#include <sys/types.h>                  // for int32_t
+#include "mozilla/TimeStamp.h"          // for TimeDuration
+
+namespace mozilla {
+namespace layers {
+
+
+/**
+ * AxisPhysicsModel encapsulates a generic 1-dimensional physically-based motion
+ * model.
+ *
+ * It performs frame-rate independent interpolation and RK4 integration for
+ * smooth animation with stable, deterministic behavior.
+ * Implementations are expected to subclass and override the Acceleration()
+ * method.
+ */
+class AxisPhysicsModel {
+public:
+  AxisPhysicsModel(double aInitialPosition, double aInitialVelocity);
+  ~AxisPhysicsModel();
+
+  /**
+   * Advance the physics simulation.
+   * |aDelta| is the time since the last sample.
+   */
+  void Simulate(const TimeDuration& aDeltaTime);
+
+  /**
+   * Gets the raw velocity of this axis at this moment.
+   */
+  double GetVelocity();
+
+  /**
+   * Sets the raw velocity of this axis at this moment.
+   */
+  void SetVelocity(double aVelocity);
+
+  /**
+   * Gets the raw position of this axis at this moment.
+   */
+  double GetPosition();
+
+  /**
+   * Sets the raw position of this axis at this moment.
+   */
+  void SetPosition(double aPosition);
+
+protected:
+
+  struct State
+  {
+    State(double ap, double av) : p(ap), v(av) {};
+    double p; // Position
+    double v; // Velocity
+  };
+
+  struct Derivative
+  {
+    Derivative() : dp(0.0), dv(0.0) {};
+    Derivative(double aDp, double aDv) : dp(aDp), dv(aDv) {};
+    double dp; // dp / delta time = Position
+    double dv; // dv / delta time = Velocity
+  };
+
+  /**
+   * Acceleration must be overridden and return the number of
+   * axis-position-units / second that should be added or removed from the
+   * velocity.
+   */
+  virtual double Acceleration(const State &aState) = 0;
+
+private:
+
+  /**
+   * Duration of fixed delta time step (seconds)
+   */
+  static const double kFixedTimestep;
+
+  /**
+   * 0.0 - 1.0 value indicating progress between current and next simulation
+   * sample.  Normalized to units of kFixedTimestep duration.
+   */
+  double mProgress;
+
+  /**
+   * Sample of simulation state as it existed
+   * (1.0 - mProgress) * kFixedTimestep seconds in the past.
+   */
+  State mPrevState;
+
+  /**
+   * Sample of simulation state as it will be in mProgress * kFixedTimestep
+   * seconds in the future.
+   */
+  State mNextState;
+
+  /**
+   * Perform RK4 (Runge-Kutta method) Integration to calculate the next
+   * simulation sample.
+   */
+  void Integrate(double aDeltaTime);
+
+  /**
+   * Apply delta velocity and position represented by aDerivative over
+   * aDeltaTime seconds, calculate new acceleration, and return new deltas.
+   */
+  Derivative Evaluate(const State &aInitState, double aDeltaTime,
+                      const Derivative &aDerivative);
+
+  /**
+   * Helper function for performing linear interpolation (lerp) of double's
+   */
+  static double LinearInterpolate(double aV1, double aV2, double aBlend);
+
+};
+
+
+}
+}
+
+#endif
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -110,16 +110,18 @@ EXPORTS.mozilla.layers += [
     'apz/src/AsyncPanZoomController.h',
     'apz/src/Axis.h',
     'apz/src/GestureEventListener.h',
     'apz/src/TaskThrottler.h',
     'apz/testutil/APZTestData.h',
     'apz/util/ActiveElementManager.h',
     'apz/util/APZCCallbackHelper.h',
     'AtomicRefCountedWithFinalize.h',
+    'AxisPhysicsModel.h',
+    'AxisPhysicsMSDModel.h',
     'basic/BasicCompositor.h',
     'basic/MacIOSurfaceTextureHostBasic.h',
     'basic/TextureHostBasic.h',
     'client/CanvasClient.h',
     'client/CompositableClient.h',
     'client/ContentClient.h',
     'client/ImageClient.h',
     'client/SimpleTextureClientPool.h',
@@ -236,16 +238,18 @@ UNIFIED_SOURCES += [
     'apz/src/AsyncPanZoomController.cpp',
     'apz/src/Axis.cpp',
     'apz/src/GestureEventListener.cpp',
     'apz/src/TaskThrottler.cpp',
     'apz/src/TouchBlockState.cpp',
     'apz/testutil/APZTestData.cpp',
     'apz/util/ActiveElementManager.cpp',
     'apz/util/APZCCallbackHelper.cpp',
+    'AxisPhysicsModel.cpp',
+    'AxisPhysicsMSDModel.cpp',
     'basic/BasicCanvasLayer.cpp',
     'basic/BasicColorLayer.cpp',
     'basic/BasicCompositor.cpp',
     'basic/BasicContainerLayer.cpp',
     'basic/BasicImages.cpp',
     'basic/BasicLayerManager.cpp',
     'basic/BasicLayersImpl.cpp',
     'basic/BasicThebesLayer.cpp',
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -245,16 +245,19 @@ private:
   DECL_GFX_PREF(Once, "layers.use-image-offscreen-surfaces",   UseImageOffscreenSurfaces, bool, false);
   DECL_GFX_PREF(Live, "layers.orientation.sync.timeout",       OrientationSyncMillis, uint32_t, (uint32_t)0);
   DECL_GFX_PREF(Once, "layers.prefer-d3d9",                    LayersPreferD3D9, bool, false);
   DECL_GFX_PREF(Once, "layers.prefer-opengl",                  LayersPreferOpenGL, bool, false);
   DECL_GFX_PREF(Once, "layers.progressive-paint",              UseProgressiveTilePainting, bool, false);
   DECL_GFX_PREF(Once, "layers.scroll-graph",                   LayersScrollGraph, bool, false);
   DECL_GFX_PREF(Once, "layers.uniformity-info",                UniformityInfo, bool, false);
 
+  DECL_GFX_PREF(Live, "layout.css.scroll-behavior.damping-ratio", ScrollBehaviorDampingRatio, float, 1.0f);
+  DECL_GFX_PREF(Live, "layout.css.scroll-behavior.enabled",    ScrollBehaviorEnabled, bool, false);
+  DECL_GFX_PREF(Live, "layout.css.scroll-behavior.spring-constant", ScrollBehaviorSpringConstant, float, 250.0f);
   DECL_GFX_PREF(Once, "layout.css.touch_action.enabled",       TouchActionEnabled, bool, false);
   DECL_GFX_PREF(Once, "layout.frame_rate",                     LayoutFrameRate, int32_t, -1);
   DECL_GFX_PREF(Live, "layout.display-list.dump",              LayoutDumpDisplayList, bool, false);
   DECL_GFX_PREF(Once, "layout.paint_rects_separately",         LayoutPaintRectsSeparately, bool, true);
 
   DECL_GFX_PREF(Live, "nglayout.debug.widget_update_flashing", WidgetUpdateFlashing, bool, false);
 
   DECL_GFX_PREF(Live, "ui.click_hold_context_menus.delay",     UiClickHoldContextMenusDelay, int32_t, 500);
--- a/js/public/HeapAPI.h
+++ b/js/public/HeapAPI.h
@@ -106,57 +106,57 @@ const uint32_t DefaultHeapMaxBytes = 32 
 /*
  * We cannot expose the class hierarchy: the implementation is hidden. Instead
  * we provide cast functions with strong debug-mode assertions.
  */
 static MOZ_ALWAYS_INLINE js::gc::Cell *
 AsCell(JSObject *obj)
 {
     js::gc::Cell *cell = reinterpret_cast<js::gc::Cell *>(obj);
-    AssertGCThingHasType(cell, JSTRACE_OBJECT);
+    js::gc::AssertGCThingHasType(cell, JSTRACE_OBJECT);
     return cell;
 }
 
 static MOZ_ALWAYS_INLINE js::gc::Cell *
 AsCell(JSFunction *fun)
 {
     js::gc::Cell *cell = reinterpret_cast<js::gc::Cell *>(fun);
-    AssertGCThingHasType(cell, JSTRACE_OBJECT);
+    js::gc::AssertGCThingHasType(cell, JSTRACE_OBJECT);
     return cell;
 }
 
 static MOZ_ALWAYS_INLINE js::gc::Cell *
 AsCell(JSString *str)
 {
     js::gc::Cell *cell = reinterpret_cast<js::gc::Cell *>(str);
-    AssertGCThingHasType(cell, JSTRACE_STRING);
+    js::gc::AssertGCThingHasType(cell, JSTRACE_STRING);
     return cell;
 }
 
 static MOZ_ALWAYS_INLINE js::gc::Cell *
 AsCell(JSFlatString *flat)
 {
     js::gc::Cell *cell = reinterpret_cast<js::gc::Cell *>(flat);
-    AssertGCThingHasType(cell, JSTRACE_STRING);
+    js::gc::AssertGCThingHasType(cell, JSTRACE_STRING);
     return cell;
 }
 
 static MOZ_ALWAYS_INLINE js::gc::Cell *
 AsCell(JS::Symbol *sym)
 {
     js::gc::Cell *cell = reinterpret_cast<js::gc::Cell *>(sym);
-    AssertGCThingHasType(cell, JSTRACE_SYMBOL);
+    js::gc::AssertGCThingHasType(cell, JSTRACE_SYMBOL);
     return cell;
 }
 
 static MOZ_ALWAYS_INLINE js::gc::Cell *
 AsCell(JSScript *script)
 {
     js::gc::Cell *cell = reinterpret_cast<js::gc::Cell *>(script);
-    AssertGCThingHasType(cell, JSTRACE_SCRIPT);
+    js::gc::AssertGCThingHasType(cell, JSTRACE_SCRIPT);
     return cell;
 }
 
 namespace shadow {
 
 struct ArenaHeader
 {
     JS::Zone *zone;
@@ -284,16 +284,17 @@ GetTenuredGCThingZone(void *thing)
 }
 
 extern JS_PUBLIC_API(Zone *)
 GetObjectZone(JSObject *obj);
 
 static MOZ_ALWAYS_INLINE bool
 GCThingIsMarkedGray(void *thing)
 {
+    MOZ_ASSERT(thing);
 #ifdef JSGC_GENERATIONAL
     /*
      * GC things residing in the nursery cannot be gray: they have no mark bits.
      * All live objects in the nursery are moved to tenured at the beginning of
      * each GC slice, so the gray marker never sees nursery things.
      */
     if (js::gc::IsInsideNursery((js::gc::Cell *)thing))
         return false;
@@ -301,16 +302,20 @@ GCThingIsMarkedGray(void *thing)
     uintptr_t *word, mask;
     js::gc::GetGCThingMarkWordAndMask(thing, js::gc::GRAY, &word, &mask);
     return *word & mask;
 }
 
 static MOZ_ALWAYS_INLINE bool
 IsIncrementalBarrierNeededOnTenuredGCThing(shadow::Runtime *rt, void *thing, JSGCTraceKind kind)
 {
+    MOZ_ASSERT(thing);
+#ifdef JSGC_GENERATIONAL
+    MOZ_ASSERT(!js::gc::IsInsideNursery((js::gc::Cell *)thing));
+#endif
     if (!rt->needsBarrier_)
         return false;
     JS::Zone *zone = GetTenuredGCThingZone(thing);
     return reinterpret_cast<shadow::Zone *>(zone)->needsBarrier_;
 }
 
 } /* namespace JS */
 
--- a/js/public/RootingAPI.h
+++ b/js/public/RootingAPI.h
@@ -98,18 +98,16 @@
  *     +---> MutableHandle<T>
  *     (via &)
  *
  * All of these types have an implicit conversion to raw pointers.
  */
 
 namespace js {
 
-class ScriptSourceObject;
-
 template <typename T>
 struct GCMethods {};
 
 template <typename T>
 class RootedBase {};
 
 template <typename T>
 class HandleBase {};
--- a/js/public/Value.h
+++ b/js/public/Value.h
@@ -1335,28 +1335,16 @@ class Value
     }
 
     friend jsval_layout (::JSVAL_TO_IMPL)(Value);
     friend Value JS_VALUE_CONSTEXPR (::IMPL_TO_JSVAL)(jsval_layout l);
     friend Value JS_VALUE_CONSTEXPR (JS::UndefinedValue)();
 };
 
 inline bool
-IsPoisonedValue(const Value &v)
-{
-    if (v.isString())
-        return IsPoisonedPtr(v.toString());
-    if (v.isSymbol())
-        return IsPoisonedPtr(v.toSymbol());
-    if (v.isObject())
-        return IsPoisonedPtr(&v.toObject());
-    return false;
-}
-
-inline bool
 IsOptimizedPlaceholderMagicValue(const Value &v)
 {
     if (v.isMagic()) {
         MOZ_ASSERT(v.whyMagic() == JS_OPTIMIZED_ARGUMENTS || v.whyMagic() == JS_OPTIMIZED_OUT);
         return true;
     }
     return false;
 }
@@ -1633,23 +1621,27 @@ JS_PUBLIC_API(void) HeapValueRelocate(Va
 }
 #endif
 
 namespace js {
 
 template <> struct GCMethods<const JS::Value>
 {
     static JS::Value initial() { return JS::UndefinedValue(); }
-    static bool poisoned(const JS::Value &v) { return JS::IsPoisonedValue(v); }
+    static bool poisoned(const JS::Value &v) {
+        return v.isMarkable() && JS::IsPoisonedPtr(v.toGCThing());
+    }
 };
 
 template <> struct GCMethods<JS::Value>
 {
     static JS::Value initial() { return JS::UndefinedValue(); }
-    static bool poisoned(const JS::Value &v) { return JS::IsPoisonedValue(v); }
+    static bool poisoned(const JS::Value &v) {
+        return v.isMarkable() && JS::IsPoisonedPtr(v.toGCThing());
+    }
     static bool needsPostBarrier(const JS::Value &v) {
         return v.isObject() && gc::IsInsideNursery(reinterpret_cast<gc::Cell*>(&v.toObject()));
     }
 #ifdef JSGC_GENERATIONAL
     static void postBarrier(JS::Value *v) { JS::HeapValuePostBarrier(v); }
     static void relocate(JS::Value *v) { JS::HeapValueRelocate(v); }
 #endif
 };
@@ -1859,16 +1851,22 @@ IMPL_TO_JSVAL(jsval_layout l)
     JS::Value v;
     v.data = l;
     return v;
 #endif
 }
 
 namespace JS {
 
+inline bool
+IsPoisonedValue(const Value &v)
+{
+    return js::GCMethods<Value>::poisoned(v);
+}
+
 #ifndef __GNUC__
 /*
  * The default assignment operator for |struct C| has the signature:
  *
  *   C& C::operator=(const C&)
  *
  * And in particular requires implicit conversion of |this| to type |C| for the
  * return value. But |volatile C| cannot thus be converted to |C|, so just
--- a/js/src/assembler/assembler/AssemblerBuffer.h
+++ b/js/src/assembler/assembler/AssemblerBuffer.h
@@ -285,60 +285,50 @@ namespace JSC {
             printer = sp;
         }
 
         void spew(const char *fmt, ...)
 #ifdef __GNUC__
             __attribute__ ((format (printf, 2, 3)))
 #endif
         {
-            if (printer
-#ifdef JS_ION
-                || js::jit::IonSpewEnabled(js::jit::IonSpew_Codegen)
-#endif
-                )
-            {
+            if (printer || js::jit::IonSpewEnabled(js::jit::IonSpew_Codegen)) {
                 // Buffer to hold the formatted string. Note that this may contain
                 // '%' characters, so do not pass it directly to printf functions.
                 char buf[200];
 
                 va_list va;
                 va_start(va, fmt);
                 int i = vsnprintf(buf, sizeof(buf), fmt, va);
                 va_end(va);
 
                 if (i > -1) {
                     if (printer)
                         printer->printf("%s\n", buf);
-
-#ifdef JS_ION
                     js::jit::IonSpew(js::jit::IonSpew_Codegen, "%s", buf);
-#endif
                 }
             }
         }
 
         static void staticSpew(const char *fmt, ...)
 #ifdef __GNUC__
             __attribute__ ((format (printf, 1, 2)))
 #endif
         {
-#ifdef JS_ION
             if (js::jit::IonSpewEnabled(js::jit::IonSpew_Codegen)) {
                 char buf[200];
 
                 va_list va;
                 va_start(va, fmt);
                 int i = vsnprintf(buf, sizeof(buf), fmt, va);
                 va_end(va);
 
                 if (i > -1)
                     js::jit::IonSpew(js::jit::IonSpew_Codegen, "%s", buf);
             }
-#endif
         }
     };
 
 } // namespace JSC
 
 #endif // ENABLE(ASSEMBLER)
 
 #endif /* assembler_assembler_AssemblerBuffer_h */
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -1102,17 +1102,17 @@ DisableSPSProfiling(JSContext *cx, unsig
     args.rval().setUndefined();
     return true;
 }
 
 static bool
 EnableOsiPointRegisterChecks(JSContext *, unsigned argc, jsval *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-#if defined(JS_ION) && defined(CHECK_OSIPOINT_REGISTERS)
+#ifdef CHECK_OSIPOINT_REGISTERS
     jit::js_JitOptions.checkOsiPointRegisters = true;
 #endif
     args.rval().setUndefined();
     return true;
 }
 
 static bool
 DisplayName(JSContext *cx, unsigned argc, jsval *vp)
@@ -1320,19 +1320,17 @@ GetJitCompilerOptions(JSContext *cx, uns
 
     return true;
 }
 
 static bool
 SetIonCheckGraphCoherency(JSContext *cx, unsigned argc, jsval *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-#ifdef JS_ION
     jit::js_JitOptions.checkGraphConsistency = ToBoolean(args.get(0));
-#endif
     args.rval().setUndefined();
     return true;
 }
 
 class CloneBufferObject : public JSObject {
     static const JSPropertySpec props_[2];
     static const size_t DATA_SLOT   = 0;
     static const size_t LENGTH_SLOT = 1;
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -2019,18 +2019,16 @@ esac
 MOZ_ARG_DISABLE_BOOL(ion,
 [  --disable-ion      Disable use of the IonMonkey JIT],
   ENABLE_ION= )
 
 AC_SUBST(ENABLE_METHODJIT_SPEW)
 
 AC_SUBST(ENABLE_ION)
 
-AC_DEFINE(JS_ION)
-
 if test -n "$COMPILE_ENVIRONMENT"; then
     MOZ_COMPILER_OPTS
 fi
 
 if test -z "$SKIP_COMPILER_CHECKS"; then
 dnl Checks for typedefs, structures, and compiler characteristics.
 dnl ========================================================
 AC_HEADER_STDC
--- a/js/src/frontend/BytecodeCompiler.h
+++ b/js/src/frontend/BytecodeCompiler.h
@@ -11,16 +11,17 @@
 
 class JSLinearString;
 
 namespace js {
 
 class AutoNameVector;
 class LazyScript;
 class LifoAlloc;
+class ScriptSourceObject;
 struct SourceCompressionTask;
 
 namespace frontend {
 
 JSScript *
 CompileScript(ExclusiveContext *cx, LifoAlloc *alloc,
               HandleObject scopeChain, HandleScript evalCaller,
               const ReadOnlyCompileOptions &options, SourceBufferHolder &srcBuf,
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -2539,30 +2539,28 @@ Parser<FullParseHandler>::asmJS(Node lis
 
     // If there is no ScriptSource, then we are doing a non-compiling parse and
     // so we shouldn't (and can't, without a ScriptSource) compile.
     if (ss == nullptr)
         return true;
 
     pc->sc->asFunctionBox()->useAsm = true;
 
-#ifdef JS_ION
     // Attempt to validate and compile this asm.js module. On success, the
     // tokenStream has been advanced to the closing }. On failure, the
     // tokenStream is in an indeterminate state and we must reparse the
     // function from the beginning. Reparsing is triggered by marking that a
     // new directive has been encountered and returning 'false'.
     bool validated;
     if (!CompileAsmJS(context, *this, list, &validated))
         return false;
     if (!validated) {
         pc->newDirectives->setAsmJS();
         return false;
     }
-#endif
 
     return true;
 }
 
 /*
  * Recognize Directive Prologue members and directives. Assuming |pn| is a
  * candidate for membership in a directive prologue, recognize directives and
  * set |pc|'s flags accordingly. If |pn| is indeed part of a prologue, set its
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -781,16 +781,17 @@ class ReadBarriered
 
     void set(T v) { value = v; }
 };
 
 class ArrayBufferObject;
 class NestedScopeObject;
 class DebugScopeObject;
 class GlobalObject;
+class ScriptSourceObject;
 class Shape;
 class BaseShape;
 class UnownedBaseShape;
 namespace jit {
 class JitCode;
 }
 namespace types {
 struct TypeObject;
--- a/js/src/gc/ForkJoinNursery.cpp
+++ b/js/src/gc/ForkJoinNursery.cpp
@@ -250,19 +250,17 @@ ForkJoinNursery::pjsCollection(int op)
         if (!initNewspace())
             CrashAtUnhandlableOOM("Cannot expand PJS nursery during GC");
         // newspace must be at least as large as fromSpace
         numActiveChunks_ = currentNumActiveChunks_;
     }
     ForkJoinNurseryCollectionTracer trc(rt, this);
     forwardFromRoots(&trc);
     collectToFixedPoint(&trc);
-#ifdef JS_ION
     jit::UpdateJitActivationsForMinorGC<ForkJoinNursery>(TlsPerThreadData.get(), &trc);
-#endif
     freeFromspace();
 
     size_t live = movedSize_;
     computeNurserySizeAfterGC(live, &msg);
 
     sweepHugeSlots();
     JS_ASSERT(hugeSlots[hugeSlotsFrom].empty());
     JS_ASSERT_IF(isEvacuating_, hugeSlots[hugeSlotsNew].empty());
--- a/js/src/gc/ForkJoinNursery.h
+++ b/js/src/gc/ForkJoinNursery.h
@@ -8,19 +8,16 @@
 #ifndef gc_ForkJoinNursery_h
 #define gc_ForkJoinNursery_h
 
 #ifdef JSGC_FJGENERATIONAL
 
 #ifndef JSGC_GENERATIONAL
 #error "JSGC_GENERATIONAL is required for the ForkJoinNursery"
 #endif
-#ifndef JS_ION
-#error "JS_ION is required for the ForkJoinNursery"
-#endif
 
 #include "jsalloc.h"
 #include "jspubtd.h"
 
 #include "gc/Heap.h"
 #include "gc/Memory.h"
 #include "gc/Nursery.h"
 
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -99,16 +99,136 @@ struct ConservativeGCData
         nativeStackTop = nullptr;
     }
 
     bool hasStackToScan() const {
         return !!nativeStackTop;
     }
 };
 
+/*
+ * Encapsulates all of the GC tunables. These are effectively constant and
+ * should only be modified by setParameter.
+ */
+class GCSchedulingTunables
+{
+    /*
+     * Soft limit on the number of bytes we are allowed to allocate in the GC
+     * heap. Attempts to allocate gcthings over this limit will return null and
+     * subsequently invoke the standard OOM machinery, independent of available
+     * physical memory.
+     */
+    size_t gcMaxBytes_;
+
+    /*
+     * The base value used to compute zone->trigger.gcBytes(). When
+     * usage.gcBytes() surpasses threshold.gcBytes() for a zone, the zone may
+     * be scheduled for a GC, depending on the exact circumstances.
+     */
+    size_t gcZoneAllocThresholdBase_;
+
+    /*
+     * Totally disables |highFrequencyGC|, the HeapGrowthFactor, and other
+     * tunables that make GC non-deterministic.
+     */
+    bool dynamicHeapGrowthEnabled_;
+
+    /*
+     * We enter high-frequency mode if we GC a twice within this many
+     * microseconds. This value is stored directly in microseconds.
+     */
+    uint64_t highFrequencyThresholdUsec_;
+
+    /*
+     * When in the |highFrequencyGC| mode, these parameterize the per-zone
+     * "HeapGrowthFactor" computation.
+     */
+    uint64_t highFrequencyLowLimitBytes_;
+    uint64_t highFrequencyHighLimitBytes_;
+    double highFrequencyHeapGrowthMax_;
+    double highFrequencyHeapGrowthMin_;
+
+    /*
+     * When not in |highFrequencyGC| mode, this is the global (stored per-zone)
+     * "HeapGrowthFactor".
+     */
+    double lowFrequencyHeapGrowth_;
+
+    /*
+     * Doubles the length of IGC slices when in the |highFrequencyGC| mode.
+     */
+    bool dynamicMarkSliceEnabled_;
+
+    /*
+     * Controls the number of empty chunks reserved for future allocation.
+     */
+    unsigned minEmptyChunkCount_;
+    unsigned maxEmptyChunkCount_;
+
+  public:
+    GCSchedulingTunables()
+      : gcMaxBytes_(0),
+        gcZoneAllocThresholdBase_(30 * 1024 * 1024),
+        dynamicHeapGrowthEnabled_(false),
+        highFrequencyThresholdUsec_(1000 * 1000),
+        highFrequencyLowLimitBytes_(100 * 1024 * 1024),
+        highFrequencyHighLimitBytes_(500 * 1024 * 1024),
+        highFrequencyHeapGrowthMax_(3.0),
+        highFrequencyHeapGrowthMin_(1.5),
+        lowFrequencyHeapGrowth_(1.5),
+        dynamicMarkSliceEnabled_(false),
+        minEmptyChunkCount_(1),
+        maxEmptyChunkCount_(30)
+    {}
+
+    size_t gcMaxBytes() const { return gcMaxBytes_; }
+    size_t gcZoneAllocThresholdBase() const { return gcZoneAllocThresholdBase_; }
+    bool isDynamicHeapGrowthEnabled() const { return dynamicHeapGrowthEnabled_; }
+    uint64_t highFrequencyThresholdUsec() const { return highFrequencyThresholdUsec_; }
+    uint64_t highFrequencyLowLimitBytes() const { return highFrequencyLowLimitBytes_; }
+    uint64_t highFrequencyHighLimitBytes() const { return highFrequencyHighLimitBytes_; }
+    double highFrequencyHeapGrowthMax() const { return highFrequencyHeapGrowthMax_; }
+    double highFrequencyHeapGrowthMin() const { return highFrequencyHeapGrowthMin_; }
+    double lowFrequencyHeapGrowth() const { return lowFrequencyHeapGrowth_; }
+    bool isDynamicMarkSliceEnabled() const { return dynamicMarkSliceEnabled_; }
+    unsigned minEmptyChunkCount() const { return minEmptyChunkCount_; }
+    unsigned maxEmptyChunkCount() const { return maxEmptyChunkCount_; }
+
+    void setParameter(JSGCParamKey key, uint32_t value);
+};
+
+/*
+ * Internal values that effect GC scheduling that are not directly exposed
+ * in the GC API.
+ */
+class GCSchedulingState
+{
+    /*
+     * Influences how we schedule and run GC's in several subtle ways. The most
+     * important factor is in how it controls the "HeapGrowthFactor". The
+     * growth factor is a measure of how large (as a percentage of the last GC)
+     * the heap is allowed to grow before we try to schedule another GC.
+     */
+    bool inHighFrequencyGCMode_;
+
+  public:
+    GCSchedulingState()
+      : inHighFrequencyGCMode_(false)
+    {}
+
+    bool inHighFrequencyGCMode() const { return inHighFrequencyGCMode_; }
+
+    void updateHighFrequencyMode(uint64_t lastGCTime, uint64_t currentTime,
+                                 const GCSchedulingTunables &tunables) {
+        inHighFrequencyGCMode_ =
+            tunables.isDynamicHeapGrowthEnabled() && lastGCTime &&
+            lastGCTime + tunables.highFrequencyThresholdUsec() > currentTime;
+    }
+};
+
 template<typename F>
 struct Callback {
     F op;
     void *data;
 
     Callback()
       : op(nullptr), data(nullptr)
     {}
@@ -177,17 +297,16 @@ class GCRuntime
     void verifyPostBarriers();
     void maybeVerifyPreBarriers(bool always);
     void maybeVerifyPostBarriers(bool always);
     bool selectForMarking(JSObject *object);
     void clearSelectedForMarking();
     void setDeterministic(bool enable);
 #endif
 
-    size_t maxBytesAllocated() { return maxBytes; }
     size_t maxMallocBytesAllocated() { return maxMallocBytes; }
 
   public:
     // Internal public interface
     js::gc::State state() { return incrementalState; }
     void recordNativeStackTop();
     void notifyRequestEnd() { conservativeGC.updateForRequestEnd(); }
     bool isBackgroundSweeping() { return helperState.isBackgroundSweeping(); }
@@ -298,17 +417,16 @@ class GCRuntime
 
     bool areGrayBitsValid() { return grayBitsValid; }
     void setGrayBitsInvalid() { grayBitsValid = false; }
 
     bool isGcNeeded() { return isNeeded; }
 
     double computeHeapGrowthFactor(size_t lastBytes);
     size_t computeTriggerBytes(double growthFactor, size_t lastBytes, JSGCInvocationKind gckind);
-    size_t allocationThreshold() { return allocThreshold; }
 
     JSGCMode gcMode() const { return mode; }
     void setGCMode(JSGCMode m) {
         mode = m;
         marker.setGCMode(mode);
     }
 
     inline void updateOnFreeArenaAlloc(const ChunkInfo &info);
@@ -409,16 +527,20 @@ class GCRuntime
 
     js::gcstats::Statistics stats;
 
     js::GCMarker          marker;
 
     /* Track heap usage for this runtime. */
     HeapUsage usage;
 
+    /* GC scheduling state and parameters. */
+    GCSchedulingTunables  tunables;
+    GCSchedulingState     schedulingState;
+
   private:
     /*
      * Set of all GC chunks with at least one allocated thing. The
      * conservative GC uses it to quickly check if a possible GC thing points
      * into an allocated chunk.
      */
     js::GCChunkSet        chunkSet;
 
@@ -430,45 +552,32 @@ class GCRuntime
      * removed from the list and scheduled for release.
      */
     js::gc::Chunk         *systemAvailableChunkListHead;
     js::gc::Chunk         *userAvailableChunkListHead;
     js::gc::ChunkPool     chunkPool;
 
     js::RootedValueMap    rootsHash;
 
-    size_t                maxBytes;
     size_t                maxMallocBytes;
 
     /*
      * Number of the committed arenas in all GC chunks including empty chunks.
      */
     mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire>   numArenasFreeCommitted;
     void                  *verifyPreData;
     void                  *verifyPostData;
     bool                  chunkAllocationSinceLastGC;
     int64_t               nextFullGCTime;
     int64_t               lastGCTime;
     int64_t               jitReleaseTime;
 
     JSGCMode              mode;
 
-    size_t                allocThreshold;
-    bool                  highFrequencyGC;
-    uint64_t              highFrequencyTimeThreshold;
-    uint64_t              highFrequencyLowLimitBytes;
-    uint64_t              highFrequencyHighLimitBytes;
-    double                highFrequencyHeapGrowthMax;
-    double                highFrequencyHeapGrowthMin;
-    double                lowFrequencyHeapGrowth;
-    bool                  dynamicHeapGrowth;
-    bool                  dynamicMarkSlice;
     uint64_t              decommitThreshold;
-    unsigned              minEmptyChunkCount;
-    unsigned              maxEmptyChunkCount;
 
     /* During shutdown, the GC needs to clean up every possible object. */
     bool                  cleanUpEverything;
 
     /*
      * The gray bits can become invalid if UnmarkGray overflows the stack. A
      * full GC will reset this bit, since it fills in all the gray bits.
      */
@@ -593,36 +702,36 @@ class GCRuntime
      * Technically this should be #ifdef JSGC_FJGENERATIONAL but that
      * affects the observed size of JSRuntime in problematic ways, see
      * note in vm/ThreadPool.h.
      */
     mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> fjCollectionCounter;
 
     /*
      * These options control the zealousness of the GC. The fundamental values
-     * are   nextScheduled and gcDebugCompartmentGC. At every allocation,
-     *   nextScheduled is decremented. When it reaches zero, we do either a
-     * full or a compartmental GC, based on   debugCompartmentGC.
+     * are nextScheduled and gcDebugCompartmentGC. At every allocation,
+     * nextScheduled is decremented. When it reaches zero, we do either a full
+     * or a compartmental GC, based on debugCompartmentGC.
      *
-     * At this point, if   zeal_ is one of the types that trigger periodic
-     * collection, then   nextScheduled is reset to the value of
-     *   zealFrequency. Otherwise, no additional GCs take place.
+     * At this point, if zeal_ is one of the types that trigger periodic
+     * collection, then nextScheduled is reset to the value of zealFrequency.
+     * Otherwise, no additional GCs take place.
      *
      * You can control these values in several ways:
      *   - Pass the -Z flag to the shell (see the usage info for details)
      *   - Call   zeal() or schedulegc() from inside shell-executed JS code
      *     (see the help for details)
      *
      * If gzZeal_ == 1 then we perform GCs in select places (during MaybeGC and
      * whenever a GC poke happens). This option is mainly useful to embedders.
      *
-     * We use   zeal_ == 4 to enable write barrier verification. See the comment
+     * We use zeal_ == 4 to enable write barrier verification. See the comment
      * in jsgc.cpp for more information about this.
      *
-     *   zeal_ values from 8 to 10 periodically run different types of
+     * zeal_ values from 8 to 10 periodically run different types of
      * incremental GC.
      */
 #ifdef JS_GC_ZEAL
     int                   zealMode;
     int                   zealFrequency;
     int                   nextScheduled;
     bool                  deterministicOnly;
     int                   incrementalLimit;
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -1413,19 +1413,17 @@ gc::MarkChildren(JSTracer *trc, types::T
 
     if (type->interpretedFunction)
         MarkObject(trc, &type->interpretedFunction, "type_function");
 }
 
 static void
 gc::MarkChildren(JSTracer *trc, jit::JitCode *code)
 {
-#ifdef JS_ION
     code->trace(trc);
-#endif
 }
 
 template<typename T>
 static void
 PushArenaTyped(GCMarker *gcmarker, ArenaHeader *aheader)
 {
     for (ArenaCellIterUnderGC i(aheader); !i.done(); i.next())
         PushMarkStack(gcmarker, i.get<T>());
--- a/js/src/gc/Marking.h
+++ b/js/src/gc/Marking.h
@@ -349,35 +349,31 @@ IsAboutToBeFinalized(BarrieredBase<JSObj
 }
 
 inline bool
 IsAboutToBeFinalized(BarrieredBase<JSScript*> *scriptp)
 {
     return IsScriptAboutToBeFinalized(scriptp);
 }
 
-#ifdef JS_ION
-/* Nonsense to get WeakCache to work with new Marking semantics. */
-
 inline bool
 IsAboutToBeFinalized(const js::jit::VMFunction **vmfunc)
 {
     /*
      * Preserves entries in the WeakCache<VMFunction, JitCode>
      * iff the JitCode has been marked.
      */
     return false;
 }
 
 inline bool
 IsAboutToBeFinalized(ReadBarrieredJitCode code)
 {
     return IsJitCodeAboutToBeFinalized(code.unsafeGet());
 }
-#endif
 
 inline Cell *
 ToMarkable(const Value &v)
 {
     if (v.isMarkable())
         return (Cell *)v.toGCThing();
     return nullptr;
 }
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -4,28 +4,27 @@
  * 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/. */
 
 #ifdef JSGC_GENERATIONAL
 
 #include "gc/Nursery-inl.h"
 
+#include "mozilla/IntegerPrintfMacros.h"
+
 #include "jscompartment.h"
 #include "jsgc.h"
 #include "jsinfer.h"
 #include "jsutil.h"
 #include "prmjtime.h"
 
 #include "gc/GCInternals.h"
 #include "gc/Memory.h"
-#ifdef JS_ION
 #include "jit/IonFrames.h"
-#endif
-#include "mozilla/IntegerPrintfMacros.h"
 #include "vm/ArrayObject.h"
 #include "vm/Debugger.h"
 #if defined(DEBUG)
 #include "vm/ScopeObject.h"
 #endif
 #include "vm/TypedArrayObject.h"
 
 #include "jsgcinlines.h"
@@ -846,19 +845,17 @@ js::Nursery::collect(JSRuntime *rt, JS::
     for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
         if (!c->gcLiveArrayBuffers.empty())
             ArrayBufferObject::sweep(c);
     }
     TIME_END(sweepArrayBufferViewList);
 
     // Update any slot or element pointers whose destination has been tenured.
     TIME_START(updateJitActivations);
-#ifdef JS_ION
     js::jit::UpdateJitActivationsForMinorGC<Nursery>(&rt->mainThread, &trc);
-#endif
     TIME_END(updateJitActivations);
 
     // Resize the nursery.
     TIME_START(resize);
     double promotionRate = trc.tenuredSize / double(allocationEnd() - start());
     if (promotionRate > 0.05)
         growAllocableSpace();
     else if (promotionRate < 0.01)
@@ -890,17 +887,17 @@ js::Nursery::collect(JSRuntime *rt, JS::
 
     TIME_START(clearStoreBuffer);
     rt->gc.storeBuffer.clear();
     TIME_END(clearStoreBuffer);
 
     // We ignore gcMaxBytes when allocating for minor collection. However, if we
     // overflowed, we disable the nursery. The next time we allocate, we'll fail
     // because gcBytes >= gcMaxBytes.
-    if (rt->gc.usage.gcBytes() >= rt->gc.maxBytesAllocated())
+    if (rt->gc.usage.gcBytes() >= rt->gc.tunables.gcMaxBytes())
         disable();
 
     TIME_END(total);
 
     TraceMinorGCEnd();
 
 #ifdef PROFILE_NURSERY
     int64_t totalTime = TIME_TOTAL(total);
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -17,19 +17,17 @@
 #include "jstypes.h"
 #include "jswatchpoint.h"
 
 #include "builtin/MapObject.h"
 #include "frontend/BytecodeCompiler.h"
 #include "gc/ForkJoinNursery.h"
 #include "gc/GCInternals.h"
 #include "gc/Marking.h"
-#ifdef JS_ION
-# include "jit/IonMacroAssembler.h"
-#endif
+#include "jit/IonMacroAssembler.h"
 #include "js/HashTable.h"
 #include "vm/Debugger.h"
 #include "vm/PropDesc.h"
 
 #include "jsgcinlines.h"
 #include "jsobjinlines.h"
 
 using namespace js;
@@ -300,17 +298,17 @@ MarkRangeConservatively(JSTracer *trc, c
         MarkWordConservatively(trc, *i);
 }
 
 static void
 MarkRangeConservativelyAndSkipIon(JSTracer *trc, JSRuntime *rt, const uintptr_t *begin, const uintptr_t *end)
 {
     const uintptr_t *i = begin;
 
-#if JS_STACK_GROWTH_DIRECTION < 0 && defined(JS_ION) && !defined(JS_ARM_SIMULATOR) && !defined(JS_MIPS_SIMULATOR)
+#if JS_STACK_GROWTH_DIRECTION < 0 && !defined(JS_ARM_SIMULATOR) && !defined(JS_MIPS_SIMULATOR)
     // Walk only regions in between JIT activations. Note that non-volatile
     // registers are spilled to the stack before the entry frame, ensuring
     // that the conservative scanner will still see them.
     //
     // If the ARM or MIPS simulator is enabled, JIT activations are not on
     // the native stack but on the simulator stack, so we don't have to skip
     // JIT regions in this case.
     for (jit::JitActivationIterator iter(rt); !iter.done(); ++iter) {
@@ -533,26 +531,22 @@ AutoGCRooter::trace(JSTracer *trc)
 
       case HASHABLEVALUE: {
         AutoHashableValueRooter *rooter = static_cast<AutoHashableValueRooter *>(this);
         rooter->trace(trc);
         return;
       }
 
       case IONMASM: {
-#ifdef JS_ION
         static_cast<js::jit::MacroAssembler::AutoRooter *>(this)->masm()->trace(trc);
-#endif
         return;
       }
 
       case IONALLOC: {
-#ifdef JS_ION
         static_cast<js::jit::AutoTempAllocatorRooter *>(this)->trace(trc);
-#endif
         return;
       }
 
       case WRAPPER: {
         /*
          * We need to use MarkValueUnbarriered here because we mark wrapper
          * roots in every slice. This is because of some rule-breaking in
          * RemapAllWrappersForObject; see comment there.
@@ -765,19 +759,17 @@ js::gc::GCRuntime::markRuntime(JSTracer 
             MarkScriptRoot(trc, &vec[i].script, "scriptAndCountsVector");
     }
 
     if (!rt->isBeingDestroyed() && !trc->runtime()->isHeapMinorCollecting()) {
         if (!IS_GC_MARKING_TRACER(trc) || rt->atomsCompartment()->zone()->isCollecting()) {
             MarkPermanentAtoms(trc);
             MarkAtoms(trc);
             MarkWellKnownSymbols(trc);
-#ifdef JS_ION
             jit::JitRuntime::Mark(trc);
-#endif
         }
     }
 
     for (ContextIter acx(rt); !acx.done(); acx.next())
         acx->mark(trc);
 
     for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
         if (IS_GC_MARKING_TRACER(trc) && !zone->isCollecting())
@@ -811,19 +803,17 @@ js::gc::GCRuntime::markRuntime(JSTracer 
 
         /* Mark debug scopes, if present */
         if (c->debugScopes)
             c->debugScopes->mark(trc);
     }
 
     MarkInterpreterActivations(&rt->mainThread, trc);
 
-#ifdef JS_ION
     jit::MarkJitActivations(&rt->mainThread, trc);
-#endif
 
     if (!isHeapMinorCollecting()) {
         /*
          * All JSCompartment::mark does is mark the globals for compartments
          * which have been entered. Globals aren't nursery allocated so there's
          * no need to do this for minor GCs.
          */
         for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next())
--- a/js/src/gc/Rooting.h
+++ b/js/src/gc/Rooting.h
@@ -10,34 +10,35 @@
 #include "js/RootingAPI.h"
 
 class JSAtom;
 class JSLinearString;
 
 namespace js {
 
 class PropertyName;
+class ScriptSourceObject;
 class Shape;
 
 namespace types { struct TypeObject; }
 
 // These are internal counterparts to the public types such as HandleObject.
 
 typedef JS::Handle<Shape*>             HandleShape;
 typedef JS::Handle<types::TypeObject*> HandleTypeObject;
 typedef JS::Handle<JSAtom*>            HandleAtom;
 typedef JS::Handle<JSLinearString*>    HandleLinearString;
 typedef JS::Handle<PropertyName*>      HandlePropertyName;
-typedef JS::Handle<js::ScriptSourceObject*> HandleScriptSource;
+typedef JS::Handle<ScriptSourceObject*> HandleScriptSource;
 
 typedef JS::MutableHandle<Shape*>      MutableHandleShape;
 typedef JS::MutableHandle<JSAtom*>     MutableHandleAtom;
 
 typedef JS::Rooted<Shape*>             RootedShape;
 typedef JS::Rooted<types::TypeObject*> RootedTypeObject;
 typedef JS::Rooted<JSAtom*>            RootedAtom;
 typedef JS::Rooted<JSLinearString*>    RootedLinearString;
 typedef JS::Rooted<PropertyName*>      RootedPropertyName;
-typedef JS::Rooted<js::ScriptSourceObject*> RootedScriptSource;
+typedef JS::Rooted<ScriptSourceObject*> RootedScriptSource;
 
 } /* namespace js */
 
 #endif /* gc_Rooting_h */
--- a/js/src/gc/StoreBuffer.cpp
+++ b/js/src/gc/StoreBuffer.cpp
@@ -57,22 +57,18 @@ StoreBuffer::WholeCellEdges::mark(JSTrac
     JSGCTraceKind kind = GetGCThingTraceKind(edge);
     if (kind <= JSTRACE_OBJECT) {
         JSObject *object = static_cast<JSObject *>(edge);
         if (object->is<ArgumentsObject>())
             ArgumentsObject::trace(trc, object);
         MarkChildren(trc, object);
         return;
     }
-#ifdef JS_ION
     JS_ASSERT(kind == JSTRACE_JITCODE);
     static_cast<jit::JitCode *>(edge)->trace(trc);
-#else
-    MOZ_CRASH("Only objects can be in the wholeCellBuffer if IonMonkey is disabled.");
-#endif
 }
 
 void
 StoreBuffer::CellPtrEdge::mark(JSTracer *trc)
 {
     if (!*edge)
         return;
 
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -3,84 +3,78 @@
  * 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 "gc/Zone.h"
 
 #include "jsgc.h"
 
-#ifdef JS_ION
 #include "jit/BaselineJIT.h"
 #include "jit/Ion.h"
 #include "jit/JitCompartment.h"
-#endif
 #include "vm/Debugger.h"
 #include "vm/Runtime.h"
 
 #include "jsgcinlines.h"
 
 using namespace js;
 using namespace js::gc;
 
 JS::Zone::Zone(JSRuntime *rt)
   : JS::shadow::Zone(rt, &rt->gc.marker),
     allocator(this),
     types(this),
     compartments(),
     gcGrayRoots(),
-    gcHeapGrowthFactor(3.0),
     gcMallocBytes(0),
     gcMallocGCTriggered(false),
     usage(&rt->gc.usage),
-    gcTriggerBytes(0),
     data(nullptr),
     isSystem(false),
     usedByExclusiveThread(false),
     scheduledForDestruction(false),
     maybeAlive(true),
     active(false),
     jitZone_(nullptr),
     gcState_(NoGC),
     gcScheduled_(false),
     gcPreserveCode_(false),
     jitUsingBarriers_(false)
 {
     /* Ensure that there are no vtables to mess us up here. */
     JS_ASSERT(reinterpret_cast<JS::shadow::Zone *>(this) ==
               static_cast<JS::shadow::Zone *>(this));
 
+    threshold.updateAfterGC(8192, GC_NORMAL, rt->gc.tunables, rt->gc.schedulingState);
     setGCMaxMallocBytes(rt->gc.maxMallocBytesAllocated() * 0.9);
 }
 
 Zone::~Zone()
 {
     JSRuntime *rt = runtimeFromMainThread();
     if (this == rt->gc.systemZone)
         rt->gc.systemZone = nullptr;
 
-#ifdef JS_ION
     js_delete(jitZone_);
-#endif
 }
 
-bool Zone::init()
+bool Zone::init(bool isSystemArg)
 {
+    isSystem = isSystemArg;
     return gcZoneGroupEdges.init();
 }
 
 void
 Zone::setNeedsBarrier(bool needs, ShouldUpdateJit updateJit)
 {
-#ifdef JS_ION
     if (updateJit == UpdateJit && needs != jitUsingBarriers_) {
         jit::ToggleBarriers(this, needs);
         jitUsingBarriers_ = needs;
     }
-#endif
 
     if (needs && runtimeFromMainThread()->isAtomsZone(this))
         JS_ASSERT(!runtimeFromMainThread()->exclusiveThreadsPresent());
 
     JS_ASSERT_IF(needs, canCollect());
     needsBarrier_ = needs;
 }
 
@@ -167,31 +161,30 @@ Zone::sweepBreakpoints(FreeOp *fop)
             }
         }
     }
 }
 
 void
 Zone::discardJitCode(FreeOp *fop)
 {
-#ifdef JS_ION
     if (!jitZone())
         return;
 
     if (isPreservingCode()) {
         PurgeJITCaches(this);
     } else {
 
-# ifdef DEBUG
+#ifdef DEBUG
         /* Assert no baseline scripts are marked as active. */
         for (ZoneCellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
             JSScript *script = i.get<JSScript>();
             JS_ASSERT_IF(script->hasBaselineScript(), !script->baselineScript()->active());
         }
-# endif
+#endif
 
         /* Mark baseline scripts on the stack as active. */
         jit::MarkActiveBaselineScripts(this);
 
         /* Only mark OSI points if code is being discarded. */
         jit::InvalidateAll(fop, this);
 
         for (ZoneCellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
@@ -210,40 +203,37 @@ Zone::discardJitCode(FreeOp *fop)
              * need to let it warm back up to get information such as which
              * opcodes are setting array holes or accessing getter properties.
              */
             script->resetUseCount();
         }
 
         jitZone()->optimizedStubSpace()->free();
     }
-#endif
 }
 
 uint64_t
 Zone::gcNumber()
 {
     // Zones in use by exclusive threads are not collected, and threads using
     // them cannot access the main runtime's gcNumber without racing.
     return usedByExclusiveThread ? 0 : runtimeFromMainThread()->gc.gcNumber();
 }
 
-#ifdef JS_ION
 js::jit::JitZone *
 Zone::createJitZone(JSContext *cx)
 {
     MOZ_ASSERT(!jitZone_);
 
     if (!cx->runtime()->getJitRuntime(cx))
         return nullptr;
 
     jitZone_ = cx->new_<js::jit::JitZone>();
     return jitZone_;
 }
-#endif
 
 JS::Zone *
 js::ZoneOfObjectFromAnyThread(const JSObject &obj)
 {
     return obj.zoneFromAnyThread();
 }
 
 bool
--- a/js/src/gc/Zone.h
+++ b/js/src/gc/Zone.h
@@ -37,16 +37,50 @@ class Allocator
     // Since allocators can be accessed from worker threads, the parent zone_
     // should not be accessed in general. ArenaLists is allowed to actually do
     // the allocation, however.
     friend class gc::ArenaLists;
 
     JS::Zone *zone_;
 };
 
+namespace gc {
+
+// This class encapsulates the data that determines when we need to do a zone GC.
+class ZoneHeapThreshold
+{
+    // The "growth factor" for computing our next thresholds after a GC.
+    double gcHeapGrowthFactor_;
+
+    // GC trigger threshold for allocations on the GC heap.
+    size_t gcTriggerBytes_;
+
+  public:
+    ZoneHeapThreshold()
+      : gcHeapGrowthFactor_(3.0),
+        gcTriggerBytes_(0)
+    {}
+
+    double gcHeapGrowthFactor() const { return gcHeapGrowthFactor_; }
+    size_t gcTriggerBytes() const { return gcTriggerBytes_; }
+
+    void updateAfterGC(size_t lastBytes, JSGCInvocationKind gckind,
+                       const GCSchedulingTunables &tunables, const GCSchedulingState &state);
+    void updateForRemovedArena(const GCSchedulingTunables &tunables);
+
+  private:
+    static double computeZoneHeapGrowthFactorForHeapSize(size_t lastBytes,
+                                                         const GCSchedulingTunables &tunables,
+                                                         const GCSchedulingState &state);
+    static size_t computeZoneTriggerBytes(double growthFactor, size_t lastBytes,
+                                          JSGCInvocationKind gckind,
+                                          const GCSchedulingTunables &tunables);
+};
+
+} // namespace gc
 } // namespace js
 
 namespace JS {
 
 // A zone is a collection of compartments. Every compartment belongs to exactly
 // one zone. In Firefox, there is roughly one zone per tab along with a system
 // zone for everything else. Zones mainly serve as boundaries for garbage
 // collection. Unlike compartments, they have no special security properties.
@@ -90,29 +124,26 @@ namespace JS {
 // example, if the conservative scanner marks a string in an otherwise dead
 // zone.)
 struct Zone : public JS::shadow::Zone,
               public js::gc::GraphNodeBase<JS::Zone>,
               public js::MallocProvider<JS::Zone>
 {
     explicit Zone(JSRuntime *rt);
     ~Zone();
-    bool init();
+    bool init(bool isSystem);
 
     void findOutgoingEdges(js::gc::ComponentFinder<JS::Zone> &finder);
 
     void discardJitCode(js::FreeOp *fop);
 
     void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
                                 size_t *typePool,
                                 size_t *baselineStubsOptimized);
 
-    void setGCLastBytes(size_t lastBytes, js::JSGCInvocationKind gckind);
-    void reduceGCTriggerBytes(size_t amount);
-
     void resetGCMallocBytes();
     void setGCMaxMallocBytes(size_t value);
     void updateMallocCounter(size_t nbytes) {
         // Note: this code may be run from worker threads. We tolerate any
         // thread races when updating gcMallocBytes.
         gcMallocBytes -= ptrdiff_t(nbytes);
         if (MOZ_UNLIKELY(isTooMuchMalloc()))
             onTooMuchMalloc();
@@ -221,19 +252,16 @@ struct Zone : public JS::shadow::Zone,
 
     // A set of edges from this zone to other zones.
     //
     // This is used during GC while calculating zone groups to record edges that
     // can't be determined by examining this zone by itself.
     typedef js::HashSet<Zone *, js::DefaultHasher<Zone *>, js::SystemAllocPolicy> ZoneSet;
     ZoneSet gcZoneGroupEdges;
 
-    // The "growth factor" for computing our next thresholds after a GC.
-    double gcHeapGrowthFactor;
-
     // Malloc counter to measure memory pressure for GC scheduling. It runs from
     // gcMaxMallocBytes down to zero. This counter should be used only when it's
     // not possible to know the size of a free.
     mozilla::Atomic<ptrdiff_t, mozilla::ReleaseAcquire> gcMallocBytes;
 
     // GC trigger threshold for allocations on the C heap.
     size_t gcMaxMallocBytes;
 
@@ -242,18 +270,18 @@ struct Zone : public JS::shadow::Zone,
     //
     // This should be a bool, but Atomic only supports 32-bit and pointer-sized
     // types.
     mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> gcMallocGCTriggered;
 
     // Track heap usage under this Zone.
     js::gc::HeapUsage usage;
 
-    // GC trigger threshold for allocations on the GC heap.
-    size_t gcTriggerBytes;
+    // Thresholds used to trigger GC.
+    js::gc::ZoneHeapThreshold threshold;
 
     // Per-zone data for use by an embedder.
     void *data;
 
     bool isSystem;
 
     bool usedByExclusiveThread;
 
--- a/js/src/irregexp/NativeRegExpMacroAssembler.h
+++ b/js/src/irregexp/NativeRegExpMacroAssembler.h
@@ -26,18 +26,16 @@
 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #ifndef V8_NATIVE_REGEXP_MACRO_ASSEMBLER_H_
 #define V8_NATIVE_REGEXP_MACRO_ASSEMBLER_H_
 
-#ifdef JS_ION
-
 #include "irregexp/RegExpMacroAssembler.h"
 
 namespace js {
 namespace irregexp {
 
 struct InputOutputData
 {
     const void *inputStart;
@@ -215,11 +213,9 @@ class MOZ_STACK_CLASS NativeRegExpMacroA
 
     int32_t register_offset(int register_index) {
         return sizeof(FrameData) + register_index * sizeof(void *);
     }
 };
 
 } }  // namespace js::irregexp
 
-#endif // JS_ION
-
 #endif  // V8_NATIVE_REGEXP_MACRO_ASSEMBLER_H_
--- a/js/src/irregexp/RegExpEngine.cpp
+++ b/js/src/irregexp/RegExpEngine.cpp
@@ -1646,17 +1646,16 @@ irregexp::CompilePattern(JSContext *cx, 
 
     Analysis analysis(cx, ignore_case, is_ascii);
     analysis.EnsureAnalyzed(node);
     if (analysis.has_failed()) {
         JS_ReportError(cx, analysis.errorMessage());
         return RegExpCode();
     }
 
-#ifdef JS_ION
     Maybe<jit::IonContext> ctx;
     Maybe<NativeRegExpMacroAssembler> native_assembler;
     Maybe<InterpretedRegExpMacroAssembler> interpreted_assembler;
 
     RegExpMacroAssembler *assembler;
     if (IsNativeRegExpEnabled(cx)) {
         NativeRegExpMacroAssembler::Mode mode =
             is_ascii ? NativeRegExpMacroAssembler::ASCII
@@ -1664,20 +1663,16 @@ irregexp::CompilePattern(JSContext *cx, 
 
         ctx.construct(cx, (jit::TempAllocator *) nullptr);
         native_assembler.construct(&alloc, shared, cx->runtime(), mode, (data->capture_count + 1) * 2);
         assembler = native_assembler.addr();
     } else {
         interpreted_assembler.construct(&alloc, shared, (data->capture_count + 1) * 2);
         assembler = interpreted_assembler.addr();
     }
-#else // JS_ION
-    InterpretedRegExpMacroAssembler macro_assembler(&alloc, shared, (data->capture_count + 1) * 2);
-    RegExpMacroAssembler *assembler = &macro_assembler;
-#endif // JS_ION
 
     // Inserted here, instead of in Assembler, because it depends on information
     // in the AST that isn't replicated in the Node structure.
     static const int kMaxBacksearchLimit = 1024;
     if (is_end_anchored &&
         !is_start_anchored &&
         max_length < kMaxBacksearchLimit) {
         assembler->SetCurrentPositionFromEnd(max_length);
@@ -1692,32 +1687,28 @@ irregexp::CompilePattern(JSContext *cx, 
     return compiler.Assemble(cx, assembler, node, data->capture_count);
 }
 
 template <typename CharT>
 RegExpRunStatus
 irregexp::ExecuteCode(JSContext *cx, jit::JitCode *codeBlock, const CharT *chars, size_t start,
                       size_t length, MatchPairs *matches)
 {
-#ifdef JS_ION
     typedef void (*RegExpCodeSignature)(InputOutputData *);
 
     InputOutputData data(chars, chars + length, start, matches);
 
     RegExpCodeSignature function = reinterpret_cast<RegExpCodeSignature>(codeBlock->raw());
 
     {
         JS::AutoSuppressGCAnalysis nogc;
         CALL_GENERATED_REGEXP(function, &data);
     }
 
     return (RegExpRunStatus) data.result;
-#else
-    MOZ_CRASH();
-#endif
 }
 
 template RegExpRunStatus
 irregexp::ExecuteCode(JSContext *cx, jit::JitCode *codeBlock, const Latin1Char *chars, size_t start,
                       size_t length, MatchPairs *matches);
 
 template RegExpRunStatus
 irregexp::ExecuteCode(JSContext *cx, jit::JitCode *codeBlock, const jschar *chars, size_t start,
--- a/js/src/irregexp/RegExpEngine.h
+++ b/js/src/irregexp/RegExpEngine.h
@@ -64,46 +64,30 @@ struct RegExpCompileData
     RegExpTree *tree;
     bool simple;
     bool contains_anchor;
     int capture_count;
 };
 
 struct RegExpCode
 {
-#ifdef JS_ION
     jit::JitCode *jitCode;
     uint8_t *byteCode;
 
     RegExpCode()
       : jitCode(nullptr), byteCode(nullptr)
     {}
 
     bool empty() {
         return !jitCode && !byteCode;
     }
 
     void destroy() {
         js_free(byteCode);
     }
-#else
-    uint8_t *byteCode;
-
-    RegExpCode()
-      : byteCode(nullptr)
-    {}
-
-    bool empty() {
-        return !byteCode;
-    }
-
-    void destroy() {
-        js_free(byteCode);
-    }
-#endif
 };
 
 RegExpCode
 CompilePattern(JSContext *cx, RegExpShared *shared, RegExpCompileData *data,
                HandleLinearString sample,  bool is_global, bool ignore_case = false,
                bool is_ascii = false);
 
 // Note: this may return RegExpRunStatus_Error if an interrupt was requested
--- a/js/src/jit/AsmJS.h
+++ b/js/src/jit/AsmJS.h
@@ -68,35 +68,21 @@ static const size_t AsmJSBufferProtected
 //   | waste | ObjectElements | data array | inaccessible reserved memory |
 //                            ^            ^                              ^
 //                            |            \                             /
 //                      obj->elements       required to be page boundaries
 //
 static const size_t AsmJSMappedSize = AsmJSPageSize + AsmJSBufferProtectedSize;
 #endif // JS_CODEGEN_X64
 
-#ifdef JS_ION
-
-// Return whether asm.js optimization is inhibitted by the platform or
+// Return whether asm.js optimization is inhibited by the platform or
 // dynamically disabled:
 extern bool
 IsAsmJSCompilationAvailable(JSContext *cx, unsigned argc, JS::Value *vp);
 
-#else // JS_ION
-
-inline bool
-IsAsmJSCompilationAvailable(JSContext *cx, unsigned argc, Value *vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    args.rval().set(BooleanValue(false));
-    return true;
-}
-
-#endif // JS_ION
-
 // To succesfully link an asm.js module to an ArrayBuffer heap, the
 // ArrayBuffer's byteLength must be:
 //  - greater or equal to 4096
 //  - either a power of 2 OR a multiple of 16MB
 inline bool
 IsValidAsmJSHeapLength(uint32_t length)
 {
     if (length < 4096)
--- a/js/src/jit/AsmJSLink.h
+++ b/js/src/jit/AsmJSLink.h
@@ -6,18 +6,16 @@
 
 #ifndef jit_AsmJSLink_h
 #define jit_AsmJSLink_h
 
 #include "NamespaceImports.h"
 
 namespace js {
 
-#ifdef JS_ION
-
 // Create a new JSFunction to replace originalFun as the representation of the
 // function defining the succesfully-validated module 'moduleObj'.
 extern JSFunction *
 NewAsmJSModuleFunction(ExclusiveContext *cx, JSFunction *originalFun, HandleObject moduleObj);
 
 // Return whether this is the js::Native returned by NewAsmJSModuleFunction.
 extern bool
 IsAsmJSModuleNative(JSNative native);
@@ -42,69 +40,11 @@ IsAsmJSModuleLoadedFromCache(JSContext *
 extern bool
 IsAsmJSFunction(JSContext *cx, unsigned argc, JS::Value *vp);
 extern bool
 IsAsmJSFunction(HandleFunction fun);
 
 extern JSString *
 AsmJSFunctionToString(JSContext *cx, HandleFunction fun);
 
-#else // JS_ION
-
-inline bool
-IsAsmJSModuleNative(JSNative native)
-{
-    return false;
-}
-
-inline bool
-IsAsmJSFunction(JSContext *cx, unsigned argc, Value *vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    args.rval().set(BooleanValue(false));
-    return true;
-}
-
-inline bool
-IsAsmJSFunction(HandleFunction fun)
-{
-    return false;
-}
-
-inline JSString *
-AsmJSFunctionToString(JSContext *cx, HandleFunction fun)
-{
-    return nullptr;
-}
-
-inline bool
-IsAsmJSModule(JSContext *cx, unsigned argc, Value *vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    args.rval().set(BooleanValue(false));
-    return true;
-}
-
-inline bool
-IsAsmJSModule(HandleFunction fun)
-{
-    return false;
-}
-
-inline JSString*
-AsmJSModuleToString(JSContext *cx, HandleFunction fun, bool addParenToLambda)
-{
-    return nullptr;
-}
-
-inline bool
-IsAsmJSModuleLoadedFromCache(JSContext *cx, unsigned argc, Value *vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    args.rval().set(BooleanValue(false));
-    return true;
-}
-
-#endif // JS_ION
-
 } // namespace js
 
 #endif // jit_AsmJS_h
--- a/js/src/jit/AsmJSModule.h
+++ b/js/src/jit/AsmJSModule.h
@@ -2,18 +2,16 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jit_AsmJSModule_h
 #define jit_AsmJSModule_h
 
-#ifdef JS_ION
-
 #include "mozilla/Maybe.h"
 #include "mozilla/Move.h"
 #include "mozilla/PodOperations.h"
 
 #include "jsscript.h"
 
 #include "gc/Marking.h"
 #include "jit/AsmJS.h"
@@ -1220,11 +1218,9 @@ class AsmJSModuleObject : public JSObjec
         module().addSizeOfMisc(mallocSizeOf, asmJSModuleCode, asmJSModuleData);
     }
 
     static const Class class_;
 };
 
 }  // namespace js
 
-#endif  // JS_ION
-
 #endif /* jit_AsmJSModule_h */
--- a/js/src/jit/BaselineCompiler.h
+++ b/js/src/jit/BaselineCompiler.h
@@ -2,18 +2,16 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jit_BaselineCompiler_h
 #define jit_BaselineCompiler_h
 
-#ifdef JS_ION
-
 #include "jit/FixedList.h"
 #if defined(JS_CODEGEN_X86)
 # include "jit/x86/BaselineCompiler-x86.h"
 #elif defined(JS_CODEGEN_X64)
 # include "jit/x64/BaselineCompiler-x64.h"
 #elif defined(JS_CODEGEN_ARM)
 # include "jit/arm/BaselineCompiler-arm.h"
 #elif defined(JS_CODEGEN_MIPS)
@@ -272,11 +270,9 @@ class BaselineCompiler : public Baseline
     void getScopeCoordinateObject(Register reg);
     Address getScopeCoordinateAddressFromObject(Register objReg, Register reg);
     Address getScopeCoordinateAddress(Register reg);
 };
 
 } // namespace jit
 } // namespace js
 
-#endif // JS_ION
-
 #endif /* jit_BaselineCompiler_h */
--- a/js/src/jit/BaselineDebugModeOSR.h
+++ b/js/src/jit/BaselineDebugModeOSR.h
@@ -2,18 +2,16 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jit_BaselineDebugModeOSR_h
 #define jit_BaselineDebugModeOSR_h
 
-#ifdef JS_ION
-
 #include "jit/BaselineFrame.h"
 #include "jit/BaselineIC.h"
 #include "jit/BaselineJIT.h"
 
 namespace js {
 namespace jit {
 
 // Note that this file and the corresponding .cpp implement debug mode
@@ -96,11 +94,9 @@ struct BaselineDebugModeOSRInfo
 };
 
 bool
 RecompileOnStackBaselineScriptsForDebugMode(JSContext *cx, JSCompartment *comp);
 
 } // namespace jit
 } // namespace js
 
-#endif // JS_ION
-
 #endif // jit_BaselineDebugModeOSR_h
--- a/js/src/jit/BaselineFrame-inl.h
+++ b/js/src/jit/BaselineFrame-inl.h
@@ -2,18 +2,16 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jit_BaselineFrame_inl_h
 #define jit_BaselineFrame_inl_h
 
-#ifdef JS_ION
-
 #include "jit/BaselineFrame.h"
 
 #include "jscntxt.h"
 #include "jscompartment.h"
 
 #include "vm/ScopeObject.h"
 
 namespace js {
@@ -74,11 +72,9 @@ BaselineFrame::callObj() const
     while (!obj->is<CallObject>())
         obj = obj->enclosingScope();
     return obj->as<CallObject>();
 }
 
 } // namespace jit
 } // namespace js
 
-#endif // JS_ION
-
 #endif /* jit_BaselineFrame_inl_h */
--- a/js/src/jit/BaselineFrame.h
+++ b/js/src/jit/BaselineFrame.h
@@ -2,18 +2,16 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jit_BaselineFrame_h
 #define jit_BaselineFrame_h
 
-#ifdef JS_ION
-
 #include "jit/IonFrames.h"
 #include "vm/Stack.h"
 
 namespace js {
 namespace jit {
 
 struct BaselineDebugModeOSRInfo;
 
@@ -420,11 +418,9 @@ class BaselineFrame
 };
 
 // Ensure the frame is 8-byte aligned (required on ARM).
 JS_STATIC_ASSERT(((sizeof(BaselineFrame) + BaselineFrame::FramePointerOffset) % 8) == 0);
 
 } // namespace jit
 } // namespace js
 
-#endif // JS_ION
-
 #endif /* jit_BaselineFrame_h */
--- a/js/src/jit/BaselineFrameInfo.h
+++ b/js/src/jit/BaselineFrameInfo.h
@@ -2,18 +2,16 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jit_BaselineFrameInfo_h
 #define jit_BaselineFrameInfo_h
 
-#ifdef JS_ION
-
 #include "mozilla/Alignment.h"
 
 #include "jit/BaselineFrame.h"
 #include "jit/BaselineRegisters.h"
 #include "jit/FixedList.h"
 #include "jit/IonMacroAssembler.h"
 
 namespace js {
@@ -313,11 +311,9 @@ class FrameInfo
 #else
     inline void assertValidState(const BytecodeInfo &info) {}
 #endif
 };
 
 } // namespace jit
 } // namespace js
 
-#endif // JS_ION
-
 #endif /* jit_BaselineFrameInfo_h */
--- a/js/src/jit/BaselineHelpers.h
+++ b/js/src/jit/BaselineHelpers.h
@@ -2,18 +2,16 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jit_BaselineHelpers_h
 #define jit_BaselineHelpers_h
 
-#ifdef JS_ION
-
 #if defined(JS_CODEGEN_X86)
 # include "jit/x86/BaselineHelpers-x86.h"
 #elif defined(JS_CODEGEN_X64)
 # include "jit/x64/BaselineHelpers-x64.h"
 #elif defined(JS_CODEGEN_ARM)
 # include "jit/arm/BaselineHelpers-arm.h"
 #elif defined(JS_CODEGEN_MIPS)
 # include "jit/mips/BaselineHelpers-mips.h"
@@ -24,11 +22,9 @@
 #endif
 
 namespace js {
 namespace jit {
 
 } // namespace jit
 } // namespace js
 
-#endif // JS_ION
-
 #endif /* jit_BaselineHelpers_h */
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -2,18 +2,16 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jit_BaselineIC_h
 #define jit_BaselineIC_h
 
-#ifdef JS_ION
-
 #include "mozilla/Assertions.h"
 
 #include "jscntxt.h"
 #include "jscompartment.h"
 #include "jsgc.h"
 #include "jsopcode.h"
 
 #include "jit/BaselineJIT.h"
@@ -6466,11 +6464,9 @@ IsCacheableDOMProxy(JSObject *obj)
         return false;
 
     return true;
 }
 
 } // namespace jit
 } // namespace js
 
-#endif // JS_ION
-
 #endif /* jit_BaselineIC_h */
--- a/js/src/jit/BaselineInspector.h
+++ b/js/src/jit/BaselineInspector.h
@@ -2,18 +2,16 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jit_BaselineInspector_h
 #define jit_BaselineInspector_h
 
-#ifdef JS_ION
-
 #include "jit/BaselineIC.h"
 #include "jit/BaselineJIT.h"
 #include "jit/MIR.h"
 
 namespace js {
 namespace jit {
 
 class BaselineInspector;
@@ -119,11 +117,9 @@ class BaselineInspector
 
     JSObject *commonGetPropFunction(jsbytecode *pc, Shape **lastProperty, JSFunction **commonGetter);
     JSObject *commonSetPropFunction(jsbytecode *pc, Shape **lastProperty, JSFunction **commonSetter);
 };
 
 } // namespace jit
 } // namespace js
 
-#endif // JS_ION
-
 #endif /* jit_BaselineInspector_h */
--- a/js/src/jit/BaselineJIT.h
+++ b/js/src/jit/BaselineJIT.h
@@ -2,18 +2,16 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jit_BaselineJIT_h
 #define jit_BaselineJIT_h
 
-#ifdef JS_ION
-
 #include "mozilla/MemoryReporting.h"
 
 #include "jscntxt.h"
 #include "jscompartment.h"
 
 #include "ds/LifoAlloc.h"
 #include "jit/Bailouts.h"
 #include "jit/IonCode.h"
@@ -424,11 +422,9 @@ void
 MarkActiveBaselineScripts(Zone *zone);
 
 MethodStatus
 BaselineCompile(JSContext *cx, JSScript *script);
 
 } // namespace jit
 } // namespace js
 
-#endif // JS_ION
-
 #endif /* jit_BaselineJIT_h */
--- a/js/src/jit/BaselineRegisters.h
+++ b/js/src/jit/BaselineRegisters.h
@@ -2,18 +2,16 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jit_BaselineRegisters_h
 #define jit_BaselineRegisters_h
 
-#ifdef JS_ION
-
 #if defined(JS_CODEGEN_X86)
 # include "jit/x86/BaselineRegisters-x86.h"
 #elif defined(JS_CODEGEN_X64)
 # include "jit/x64/BaselineRegisters-x64.h"
 #elif defined(JS_CODEGEN_ARM)
 # include "jit/arm/BaselineRegisters-arm.h"
 #elif defined(JS_CODEGEN_MIPS)
 # include "jit/mips/BaselineRegisters-mips.h"
@@ -24,11 +22,9 @@
 #endif
 
 namespace js {
 namespace jit {
 
 } // namespace jit
 } // namespace js
 
-#endif // JS_ION
-
 #endif /* jit_BaselineRegisters_h */
--- a/js/src/jit/CompileWrappers.h
+++ b/js/src/jit/CompileWrappers.h
@@ -2,18 +2,16 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jit_CompileWrappers_h
 #define jit_CompileWrappers_h
 
-#ifdef JS_ION
-
 #include "jscntxt.h"
 
 namespace js {
 namespace jit {
 
 class JitRuntime;
 
 // During Ion compilation we need access to various bits of the current
@@ -136,15 +134,12 @@ class JitCompileOptions
         return spsSlowAssertionsEnabled_;
     }
 
   private:
     bool cloneSingletons_;
     bool spsSlowAssertionsEnabled_;
 };
 
-
 } // namespace jit
 } // namespace js
 
-#endif // JS_ION
-
 #endif // jit_CompileWrappers_h
--- a/js/src/jit/CompilerRoot.h
+++ b/js/src/jit/CompilerRoot.h
@@ -2,18 +2,16 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jit_CompilerRoot_h
 #define jit_CompilerRoot_h
 
-#ifdef JS_ION
-
 #include "jscntxt.h"
 
 #include "jit/Ion.h"
 #include "jit/IonAllocPolicy.h"
 #include "js/RootingAPI.h"
 
 namespace js {
 namespace jit {
@@ -60,11 +58,9 @@ typedef CompilerRoot<JSFunction*> Compil
 typedef CompilerRoot<JSScript*> CompilerRootScript;
 typedef CompilerRoot<PropertyName*> CompilerRootPropertyName;
 typedef CompilerRoot<Shape*> CompilerRootShape;
 typedef CompilerRoot<Value> CompilerRootValue;
 
 } // namespace jit
 } // namespace js