Merge mozilla-central to fx-team
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 14 Jul 2014 15:24:04 +0200
changeset 214592 827f88a7eb65f8557c7f05748139bb69691f51fa
parent 214591 c517a17ebe6bba7ac80b87228055a0a7df066c5a (current diff)
parent 214586 340b19c14d3d388b7ae1a15bff9695cb0141ac0a (diff)
child 214593 4565c08e0027753473e542cec142949f6cbb9c79
push id3857
push userraliiev@mozilla.com
push dateTue, 02 Sep 2014 16:39:23 +0000
treeherdermozilla-beta@5638b907b505 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone33.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to fx-team
dom/interfaces/html/nsIDOMHTMLAudioElement.idl
dom/interfaces/html/nsIDOMHTMLVideoElement.idl
gfx/gl/SurfaceFactory.cpp
gfx/gl/SurfaceFactory.h
gfx/thebes/gfxPath.cpp
gfx/thebes/gfxPath.h
mfbt/lz4_encoder.h
--- a/b2g/components/test/mochitest/mochitest.ini
+++ b/b2g/components/test/mochitest/mochitest.ini
@@ -2,13 +2,12 @@
 run-if = toolkit == "gonk"
 support-files =
   permission_handler_chrome.js
   SandboxPromptTest.html
   filepicker_path_handler_chrome.js
   systemapp_helper.js
 
 [test_sandbox_permission.html]
-skip-if = true # bug 984274 - frequent timeouts
 [test_filepicker_path.html]
 [test_permission_deny.html]
 [test_permission_gum_remember.html]
 [test_systemapp.html]
--- 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="40cac290f0a3253d31242d7f50b1d2ddd2f47cda"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="88e0a972280bb35847c010b8c3f1481fa80f3847"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
   <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="bf9aaf39dd5a6491925a022db167c460f8207d34"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="aebf432f334ec0b48eb358569b9dfbfbead48017"/>
   <!-- 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="cc67f31dc638c0b7edba3cf7e3d87cadf0ed52bf">
     <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="40cac290f0a3253d31242d7f50b1d2ddd2f47cda"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="88e0a972280bb35847c010b8c3f1481fa80f3847"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="aebf432f334ec0b48eb358569b9dfbfbead48017"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="276ce45e78b09c4a4ee643646f691d22804754c1">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="40cac290f0a3253d31242d7f50b1d2ddd2f47cda"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="88e0a972280bb35847c010b8c3f1481fa80f3847"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
   <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="aebf432f334ec0b48eb358569b9dfbfbead48017"/>
--- 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="40cac290f0a3253d31242d7f50b1d2ddd2f47cda"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="88e0a972280bb35847c010b8c3f1481fa80f3847"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
   <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="bf9aaf39dd5a6491925a022db167c460f8207d34"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="aebf432f334ec0b48eb358569b9dfbfbead48017"/>
   <!-- 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="cc67f31dc638c0b7edba3cf7e3d87cadf0ed52bf">
     <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="40cac290f0a3253d31242d7f50b1d2ddd2f47cda"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="88e0a972280bb35847c010b8c3f1481fa80f3847"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="aebf432f334ec0b48eb358569b9dfbfbead48017"/>
   <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="a928d2ae29244dfec3753ea695e6b98e38e8849a"/>
+  <project name="device-flame" path="device/t2m/flame" remote="b2g" revision="a927b19531c293dca1e80e081e0ab5715f4d1bfe"/>
   <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"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
         "git_revision": "", 
         "remote": "", 
         "branch": ""
     }, 
-    "revision": "f705a3f96020c7d7aa5ec63bf3417db29e1ab2a2", 
+    "revision": "168c5ba3a072b6c6b30bcc93586f833cc97fff4a", 
     "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="40cac290f0a3253d31242d7f50b1d2ddd2f47cda"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="88e0a972280bb35847c010b8c3f1481fa80f3847"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
   <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="aebf432f334ec0b48eb358569b9dfbfbead48017"/>
   <!-- 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="40cac290f0a3253d31242d7f50b1d2ddd2f47cda"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="88e0a972280bb35847c010b8c3f1481fa80f3847"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
   <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="cc67f31dc638c0b7edba3cf7e3d87cadf0ed52bf">
     <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="40cac290f0a3253d31242d7f50b1d2ddd2f47cda"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="88e0a972280bb35847c010b8c3f1481fa80f3847"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="aebf432f334ec0b48eb358569b9dfbfbead48017"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/wasabi/sources.xml
+++ b/b2g/config/wasabi/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="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="40cac290f0a3253d31242d7f50b1d2ddd2f47cda"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="88e0a972280bb35847c010b8c3f1481fa80f3847"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
   <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="aebf432f334ec0b48eb358569b9dfbfbead48017"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
--- a/configure.in
+++ b/configure.in
@@ -7793,16 +7793,35 @@ if test "$SOLARIS_SUNPRO_CC"; then
   PROFILE_USE_LDFLAGS="-xprofile=use:$_objdir/$enable_application"
 fi
 
 AC_SUBST(PROFILE_GEN_CFLAGS)
 AC_SUBST(PROFILE_GEN_LDFLAGS)
 AC_SUBST(PROFILE_USE_CFLAGS)
 AC_SUBST(PROFILE_USE_LDFLAGS)
 
+dnl =============================================
+dnl Support for -fno-integrated-as (recent clang)
+dnl =============================================
+dnl Under clang 3.4+, use -fno-integrated-as to
+dnl build libvpx's vp8_asm_enc_offsets.c
+
+_SAVE_CFLAGS="$CFLAGS"
+CFLAGS="$CFLAGS -fno-integrated-as"
+
+AC_MSG_CHECKING([whether C compiler supports -fno-integrated-as])
+AC_TRY_COMPILE([], [return 0;],
+               [ NO_INTEGRATED_AS_CFLAGS="-fno-integrated-as"
+                 result="yes" ], result="no")
+AC_MSG_RESULT([$result])
+
+CFLAGS="$_SAVE_CFLAGS"
+
+AC_SUBST(NO_INTEGRATED_AS_CFLAGS)
+
 fi # ! SKIP_COMPILER_CHECKS
 
 AC_DEFINE(CPP_THROW_NEW, [throw()])
 AC_LANG_C
 
 if test "$COMPILE_ENVIRONMENT"; then
 MOZ_EXPAND_LIBS
 fi # COMPILE_ENVIRONMENT
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -61,16 +61,17 @@ class nsIPresShell;
 class nsIPrincipal;
 class nsIRequest;
 class nsIStreamListener;
 class nsIStructuredCloneContainer;
 class nsIStyleRule;
 class nsIStyleSheet;
 class nsIURI;
 class nsIVariant;
+class nsLocation;
 class nsViewManager;
 class nsPresContext;
 class nsRange;
 class nsScriptLoader;
 class nsSMILAnimationController;
 class nsStyleSet;
 class nsTextNode;
 class nsWindowSizes;
@@ -2179,17 +2180,17 @@ public:
     CreateCDATASection(const nsAString& aData, mozilla::ErrorResult& rv);
   already_AddRefed<mozilla::dom::Attr>
     CreateAttribute(const nsAString& aName, mozilla::ErrorResult& rv);
   already_AddRefed<mozilla::dom::Attr>
     CreateAttributeNS(const nsAString& aNamespaceURI,
                       const nsAString& aQualifiedName,
                       mozilla::ErrorResult& rv);
   void GetInputEncoding(nsAString& aInputEncoding);
-  already_AddRefed<nsIDOMLocation> GetLocation() const;
+  already_AddRefed<nsLocation> GetLocation() const;
   void GetReferrer(nsAString& aReferrer) const;
   void GetLastModified(nsAString& aLastModified) const;
   void GetReadyState(nsAString& aReadyState) const;
   // Not const because otherwise the compiler can't figure out whether to call
   // this GetTitle or the nsAString version from non-const methods, since
   // neither is an exact match.
   virtual void GetTitle(nsString& aTitle) = 0;
   virtual void SetTitle(const nsAString& aTitle, mozilla::ErrorResult& rv) = 0;
--- a/content/base/src/Link.cpp
+++ b/content/base/src/Link.cpp
@@ -123,17 +123,17 @@ Link::GetURI() const
   Link *self = const_cast<Link *>(this);
   Element *element = self->mElement;
   mCachedURI = element->GetHrefURI();
 
   return mCachedURI;
 }
 
 void
-Link::SetProtocol(const nsAString &aProtocol)
+Link::SetProtocol(const nsAString &aProtocol, ErrorResult& aError)
 {
   nsCOMPtr<nsIURI> uri(GetURIToMutate());
   if (!uri) {
     // Ignore failures to be compatible with NS4.
     return;
   }
 
   nsAString::const_iterator start, end;
@@ -142,83 +142,83 @@ Link::SetProtocol(const nsAString &aProt
   nsAString::const_iterator iter(start);
   (void)FindCharInReadable(':', iter, end);
   (void)uri->SetScheme(NS_ConvertUTF16toUTF8(Substring(start, iter)));
 
   SetHrefAttribute(uri);
 }
 
 void
-Link::SetPassword(const nsAString &aPassword)
+Link::SetPassword(const nsAString &aPassword, ErrorResult& aError)
 {
   nsCOMPtr<nsIURI> uri(GetURIToMutate());
   if (!uri) {
     // Ignore failures to be compatible with NS4.
     return;
   }
 
   uri->SetPassword(NS_ConvertUTF16toUTF8(aPassword));
   SetHrefAttribute(uri);
 }
 
 void
-Link::SetUsername(const nsAString &aUsername)
+Link::SetUsername(const nsAString &aUsername, ErrorResult& aError)
 {
   nsCOMPtr<nsIURI> uri(GetURIToMutate());
   if (!uri) {
     // Ignore failures to be compatible with NS4.
     return;
   }
 
   uri->SetUsername(NS_ConvertUTF16toUTF8(aUsername));
   SetHrefAttribute(uri);
 }
 
 void
-Link::SetHost(const nsAString &aHost)
+Link::SetHost(const nsAString &aHost, ErrorResult& aError)
 {
   nsCOMPtr<nsIURI> uri(GetURIToMutate());
   if (!uri) {
     // Ignore failures to be compatible with NS4.
     return;
   }
 
   (void)uri->SetHostPort(NS_ConvertUTF16toUTF8(aHost));
   SetHrefAttribute(uri);
 }
 
 void
-Link::SetHostname(const nsAString &aHostname)
+Link::SetHostname(const nsAString &aHostname, ErrorResult& aError)
 {
   nsCOMPtr<nsIURI> uri(GetURIToMutate());
   if (!uri) {
     // Ignore failures to be compatible with NS4.
     return;
   }
 
   (void)uri->SetHost(NS_ConvertUTF16toUTF8(aHostname));
   SetHrefAttribute(uri);
 }
 
 void
-Link::SetPathname(const nsAString &aPathname)
+Link::SetPathname(const nsAString &aPathname, ErrorResult& aError)
 {
   nsCOMPtr<nsIURI> uri(GetURIToMutate());
   nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
   if (!url) {
     // Ignore failures to be compatible with NS4.
     return;
   }
 
   (void)url->SetFilePath(NS_ConvertUTF16toUTF8(aPathname));
   SetHrefAttribute(uri);
 }
 
 void
-Link::SetSearch(const nsAString& aSearch)
+Link::SetSearch(const nsAString& aSearch, ErrorResult& aError)
 {
   SetSearchInternal(aSearch);
   UpdateURLSearchParams();
 }
 
 void
 Link::SetSearchInternal(const nsAString& aSearch)
 {
@@ -229,17 +229,17 @@ Link::SetSearchInternal(const nsAString&
     return;
   }
 
   (void)url->SetQuery(NS_ConvertUTF16toUTF8(aSearch));
   SetHrefAttribute(uri);
 }
 
 void
-Link::SetPort(const nsAString &aPort)
+Link::SetPort(const nsAString &aPort, ErrorResult& aError)
 {
   nsCOMPtr<nsIURI> uri(GetURIToMutate());
   if (!uri) {
     // Ignore failures to be compatible with NS4.
     return;
   }
 
   nsresult rv;
@@ -254,91 +254,91 @@ Link::SetPort(const nsAString &aPort)
     }
   }
 
   (void)uri->SetPort(port);
   SetHrefAttribute(uri);
 }
 
 void
-Link::SetHash(const nsAString &aHash)
+Link::SetHash(const nsAString &aHash, ErrorResult& aError)
 {
   nsCOMPtr<nsIURI> uri(GetURIToMutate());
   if (!uri) {
     // Ignore failures to be compatible with NS4.
     return;
   }
 
   (void)uri->SetRef(NS_ConvertUTF16toUTF8(aHash));
   SetHrefAttribute(uri);
 }
 
 void
-Link::GetOrigin(nsAString &aOrigin)
+Link::GetOrigin(nsAString &aOrigin, ErrorResult& aError)
 {
   aOrigin.Truncate();
 
   nsCOMPtr<nsIURI> uri(GetURI());
   if (!uri) {
     return;
   }
 
   nsString origin;
   nsContentUtils::GetUTFNonNullOrigin(uri, origin);
   aOrigin.Assign(origin);
 }
 
 void
-Link::GetProtocol(nsAString &_protocol)
+Link::GetProtocol(nsAString &_protocol, ErrorResult& aError)
 {
   nsCOMPtr<nsIURI> uri(GetURI());
   if (!uri) {
     _protocol.AssignLiteral("http");
   }
   else {
     nsAutoCString scheme;
     (void)uri->GetScheme(scheme);
     CopyASCIItoUTF16(scheme, _protocol);
   }
   _protocol.Append(char16_t(':'));
   return;
 }
 
 void
-Link::GetUsername(nsAString& aUsername)
+Link::GetUsername(nsAString& aUsername, ErrorResult& aError)
 {
   aUsername.Truncate();
 
   nsCOMPtr<nsIURI> uri(GetURI());
   if (!uri) {
     return;
   }
 
   nsAutoCString username;
   uri->GetUsername(username);
   CopyASCIItoUTF16(username, aUsername);
 }
 
 void
-Link::GetPassword(nsAString &aPassword)
+Link::GetPassword(nsAString &aPassword, ErrorResult& aError)
 {
   aPassword.Truncate();
 
   nsCOMPtr<nsIURI> uri(GetURI());
   if (!uri) {
     return;
   }
 
   nsAutoCString password;
   uri->GetPassword(password);
   CopyASCIItoUTF16(password, aPassword);
 }
 
 void
-Link::GetHost(nsAString &_host)
+Link::GetHost(nsAString &_host, ErrorResult& aError)
 {
   _host.Truncate();
 
   nsCOMPtr<nsIURI> uri(GetURI());
   if (!uri) {
     // Do not throw!  Not having a valid URI should result in an empty string.
     return;
   }
@@ -346,17 +346,17 @@ Link::GetHost(nsAString &_host)
   nsAutoCString hostport;
   nsresult rv = uri->GetHostPort(hostport);
   if (NS_SUCCEEDED(rv)) {
     CopyUTF8toUTF16(hostport, _host);
   }
 }
 
 void
-Link::GetHostname(nsAString &_hostname)
+Link::GetHostname(nsAString &_hostname, ErrorResult& aError)
 {
   _hostname.Truncate();
 
   nsCOMPtr<nsIURI> uri(GetURI());
   if (!uri) {
     // Do not throw!  Not having a valid URI should result in an empty string.
     return;
   }
@@ -366,17 +366,17 @@ Link::GetHostname(nsAString &_hostname)
   // Note that failure to get the host from the URI is not necessarily a bad
   // thing.  Some URIs do not have a host.
   if (NS_SUCCEEDED(rv)) {
     CopyUTF8toUTF16(host, _hostname);
   }
 }
 
 void
-Link::GetPathname(nsAString &_pathname)
+Link::GetPathname(nsAString &_pathname, ErrorResult& aError)
 {
   _pathname.Truncate();
 
   nsCOMPtr<nsIURI> uri(GetURI());
   nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
   if (!url) {
     // Do not throw!  Not having a valid URI or URL should result in an empty
     // string.
@@ -386,17 +386,17 @@ Link::GetPathname(nsAString &_pathname)
   nsAutoCString file;
   nsresult rv = url->GetFilePath(file);
   if (NS_SUCCEEDED(rv)) {
     CopyUTF8toUTF16(file, _pathname);
   }
 }
 
 void
-Link::GetSearch(nsAString &_search)
+Link::GetSearch(nsAString &_search, ErrorResult& aError)
 {
   _search.Truncate();
 
   nsCOMPtr<nsIURI> uri(GetURI());
   nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
   if (!url) {
     // Do not throw!  Not having a valid URI or URL should result in an empty
     // string.
@@ -406,17 +406,17 @@ Link::GetSearch(nsAString &_search)
   nsAutoCString search;
   nsresult rv = url->GetQuery(search);
   if (NS_SUCCEEDED(rv) && !search.IsEmpty()) {
     CopyUTF8toUTF16(NS_LITERAL_CSTRING("?") + search, _search);
   }
 }
 
 void
-Link::GetPort(nsAString &_port)
+Link::GetPort(nsAString &_port, ErrorResult& aError)
 {
   _port.Truncate();
 
   nsCOMPtr<nsIURI> uri(GetURI());
   if (!uri) {
     // Do not throw!  Not having a valid URI should result in an empty string.
     return;
   }
@@ -428,17 +428,17 @@ Link::GetPort(nsAString &_port)
   if (NS_SUCCEEDED(rv) && port != -1) {
     nsAutoString portStr;
     portStr.AppendInt(port, 10);
     _port.Assign(portStr);
   }
 }
 
 void
-Link::GetHash(nsAString &_hash)
+Link::GetHash(nsAString &_hash, ErrorResult& aError)
 {
   _hash.Truncate();
 
   nsCOMPtr<nsIURI> uri(GetURI());
   if (!uri) {
     // Do not throw!  Not having a valid URI should result in an empty
     // string.
     return;
--- a/content/base/src/Link.h
+++ b/content/base/src/Link.h
@@ -52,37 +52,37 @@ public:
   nsIURI* GetURI() const;
   virtual nsIURI* GetURIExternal() const {
     return GetURI();
   }
 
   /**
    * Helper methods for modifying and obtaining parts of the URI of the Link.
    */
-  void SetProtocol(const nsAString &aProtocol);
-  void SetUsername(const nsAString &aUsername);
-  void SetPassword(const nsAString &aPassword);
-  void SetHost(const nsAString &aHost);
-  void SetHostname(const nsAString &aHostname);
-  void SetPathname(const nsAString &aPathname);
-  void SetSearch(const nsAString &aSearch);
+  void SetProtocol(const nsAString &aProtocol, ErrorResult& aError);
+  void SetUsername(const nsAString &aUsername, ErrorResult& aError);
+  void SetPassword(const nsAString &aPassword, ErrorResult& aError);
+  void SetHost(const nsAString &aHost, ErrorResult& aError);
+  void SetHostname(const nsAString &aHostname, ErrorResult& aError);
+  void SetPathname(const nsAString &aPathname, ErrorResult& aError);
+  void SetSearch(const nsAString &aSearch, ErrorResult& aError);
   void SetSearchParams(mozilla::dom::URLSearchParams& aSearchParams);
-  void SetPort(const nsAString &aPort);
-  void SetHash(const nsAString &aHash);
-  void GetOrigin(nsAString &aOrigin);
-  void GetProtocol(nsAString &_protocol);
-  void GetUsername(nsAString &aUsername);
-  void GetPassword(nsAString &aPassword);
-  void GetHost(nsAString &_host);
-  void GetHostname(nsAString &_hostname);
-  void GetPathname(nsAString &_pathname);
-  void GetSearch(nsAString &_search);
+  void SetPort(const nsAString &aPort, ErrorResult& aError);
+  void SetHash(const nsAString &aHash, ErrorResult& aError);
+  void GetOrigin(nsAString &aOrigin, ErrorResult& aError);
+  void GetProtocol(nsAString &_protocol, ErrorResult& aError);
+  void GetUsername(nsAString &aUsername, ErrorResult& aError);
+  void GetPassword(nsAString &aPassword, ErrorResult& aError);
+  void GetHost(nsAString &_host, ErrorResult& aError);
+  void GetHostname(nsAString &_hostname, ErrorResult& aError);
+  void GetPathname(nsAString &_pathname, ErrorResult& aError);
+  void GetSearch(nsAString &_search, ErrorResult& aError);
   URLSearchParams* SearchParams();
-  void GetPort(nsAString &_port);
-  void GetHash(nsAString &_hash);
+  void GetPort(nsAString &_port, ErrorResult& aError);
+  void GetHash(nsAString &_hash, ErrorResult& aError);
 
   /**
    * Invalidates any link caching, and resets the state to the default.
    *
    * @param aNotify
    *        true if ResetLinkState should notify the owning document about style
    *        changes or false if it should not.
    */
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -218,16 +218,17 @@
 #include "mozilla/dom/XPathResult.h"
 #include "nsIDocumentEncoder.h"
 #include "nsIDocumentActivity.h"
 #include "nsIStructuredCloneContainer.h"
 #include "nsIMutableArray.h"
 #include "nsContentPermissionHelper.h"
 #include "mozilla/dom/DOMStringList.h"
 #include "nsWindowMemoryReporter.h"
+#include "nsLocation.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 typedef nsTArray<Link*> LinkArray;
 
 #ifdef PR_LOGGING
 static PRLogModuleInfo* gDocumentLeakPRLog;
@@ -6541,28 +6542,28 @@ nsDocument::GetDefaultView(nsIDOMWindow*
 
 NS_IMETHODIMP
 nsDocument::GetLocation(nsIDOMLocation **_retval)
 {
   *_retval = nsIDocument::GetLocation().take();
   return NS_OK;
 }
 
-already_AddRefed<nsIDOMLocation>
+already_AddRefed<nsLocation>
 nsIDocument::GetLocation() const
 {
   nsCOMPtr<nsIDOMWindow> w = do_QueryInterface(mScriptGlobalObject);
 
   if (!w) {
     return nullptr;
   }
 
   nsCOMPtr<nsIDOMLocation> loc;
   w->GetLocation(getter_AddRefs(loc));
-  return loc.forget();
+  return loc.forget().downcast<nsLocation>();
 }
 
 Element*
 nsIDocument::GetHtmlElement() const
 {
   Element* rootElement = GetRootElement();
   if (rootElement && rootElement->IsHTML(nsGkAtoms::html))
     return rootElement;
--- a/content/canvas/src/CanvasRenderingContext2D.cpp
+++ b/content/canvas/src/CanvasRenderingContext2D.cpp
@@ -362,17 +362,17 @@ public:
     RefPtr<SourceSurface> snapshot = mTarget->Snapshot();
 
     mCtx->mTarget->DrawSurfaceWithShadow(snapshot, mTempRect.TopLeft(),
                                          Color::FromABGR(mCtx->CurrentState().shadowColor),
                                          mCtx->CurrentState().shadowOffset, mSigma,
                                          mCtx->CurrentState().op);
   }
 
-  operator DrawTarget*() 
+  operator DrawTarget*()
   {
     return mTarget;
   }
 
   DrawTarget* operator->()
   {
     return mTarget;
   }
@@ -939,17 +939,18 @@ CanvasRenderingContext2D::EnsureTarget()
         DemoteOldestContextIfNecessary();
 
         SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue();
 
 #if USE_SKIA
         if (glue && glue->GetGrContext() && glue->GetGLContext()) {
           mTarget = Factory::CreateDrawTargetSkiaWithGrContext(glue->GetGrContext(), size, format);
           if (mTarget) {
-            mStream = gfx::SurfaceStream::CreateForType(SurfaceStreamType::TripleBuffer, glue->GetGLContext());
+            mStream = gl::SurfaceStream::CreateForType(gl::SurfaceStreamType::TripleBuffer,
+                                                       glue->GetGLContext());
             AddDemotableContext(this);
           } else {
             printf_stderr("Failed to create a SkiaGL DrawTarget, falling back to software\n");
           }
         }
 #endif
         if (!mTarget) {
           mTarget = layerManager->CreateDrawTarget(size, format);
@@ -2521,17 +2522,17 @@ CanvasRenderingContext2D::AddHitRegion(c
         break;
       }
     }
 #ifdef ACCESSIBILITY
   options.mControl->SetProperty(nsGkAtoms::hitregion, new bool(true),
                                 nsINode::DeleteProperty<bool>);
 #endif
   }
-  
+
   // finally, add the region to the list
   RegionInfo info;
   info.mId = options.mId;
   info.mElement = options.mControl;
   RefPtr<PathBuilder> pathBuilder = mPath->TransformedCopyToBuilder(mTarget->GetTransform());
   info.mPath = pathBuilder->Finish();
 
   mHitRegionsOptions.InsertElementAt(0, info);
@@ -3463,17 +3464,17 @@ CanvasRenderingContext2D::DrawDirectlyTo
   transformMatrix.Translate(gfxPoint(sx, sy));
   if (dw > 0 && dh > 0) {
     transformMatrix.Scale(sw/dw, sh/dh);
   }
   transformMatrix.Translate(gfxPoint(-dx, -dy));
 
   nsRefPtr<gfxContext> context = new gfxContext(tempTarget);
   context->SetMatrix(contextMatrix);
-  
+
   // FLAG_CLAMP is added for increased performance
   uint32_t modifiedFlags = image.mDrawingFlags | imgIContainer::FLAG_CLAMP;
 
   nsresult rv = image.mImgContainer->
     Draw(context, GraphicsFilter::FILTER_GOOD, transformMatrix,
          gfxRect(gfxPoint(dx, dy), gfxIntSize(dw, dh)),
          nsIntRect(nsIntPoint(0, 0), gfxIntSize(imgSize.width, imgSize.height)),
          gfxIntSize(imgSize.width, imgSize.height), nullptr, image.mWhichFrame,
--- a/content/canvas/src/CanvasRenderingContext2D.h
+++ b/content/canvas/src/CanvasRenderingContext2D.h
@@ -25,17 +25,17 @@
 #include "imgIEncoder.h"
 #include "nsLayoutUtils.h"
 #include "mozilla/EnumeratedArray.h"
 
 class nsGlobalWindow;
 class nsXULElement;
 
 namespace mozilla {
-namespace gfx {
+namespace gl {
 class SourceSurface;
 class SurfaceStream;
 }
 
 namespace dom {
 class HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement;
 class ImageData;
 class StringOrCanvasGradientOrCanvasPattern;
@@ -388,31 +388,31 @@ public:
 
   void ArcTo(double x1, double y1, double x2, double y2, double radius,
              mozilla::ErrorResult& error);
   void Rect(double x, double y, double w, double h);
   void Arc(double x, double y, double radius, double startAngle,
            double endAngle, bool anticlockwise, mozilla::ErrorResult& error);
 
   void GetMozCurrentTransform(JSContext* cx,
-			      JS::MutableHandle<JSObject*> result,
-			      mozilla::ErrorResult& error) const;
+                              JS::MutableHandle<JSObject*> result,
+                              mozilla::ErrorResult& error) const;
   void SetMozCurrentTransform(JSContext* cx,
                               JS::Handle<JSObject*> currentTransform,
                               mozilla::ErrorResult& error);
   void GetMozCurrentTransformInverse(JSContext* cx,
-				     JS::MutableHandle<JSObject*> result,
-				     mozilla::ErrorResult& error) const;
+                                     JS::MutableHandle<JSObject*> result,
+                                     mozilla::ErrorResult& error) const;
   void SetMozCurrentTransformInverse(JSContext* cx,
                                      JS::Handle<JSObject*> currentTransform,
                                      mozilla::ErrorResult& error);
   void GetFillRule(nsAString& fillRule);
   void SetFillRule(const nsAString& fillRule);
   void GetMozDash(JSContext* cx, JS::MutableHandle<JS::Value> retval,
-		  mozilla::ErrorResult& error);
+                  mozilla::ErrorResult& error);
   void SetMozDash(JSContext* cx, const JS::Value& mozDash,
                   mozilla::ErrorResult& error);
 
   void SetLineDash(const Sequence<double>& mSegments);
   void GetLineDash(nsTArray<double>& mSegments) const;
 
   void SetLineDashOffset(double mOffset);
   double LineDashOffset() const;
@@ -656,17 +656,17 @@ protected:
   /**
     * Returns the surface format this canvas should be allocated using. Takes
     * into account mOpaque, platform requirements, etc.
     */
   mozilla::gfx::SurfaceFormat GetSurfaceFormat() const;
 
   void DrawImage(const HTMLImageOrCanvasOrVideoElement &imgElt,
                  double sx, double sy, double sw, double sh,
-                 double dx, double dy, double dw, double dh, 
+                 double dx, double dy, double dw, double dh,
                  uint8_t optional_argc, mozilla::ErrorResult& error);
 
   void DrawDirectlyToCanvas(const nsLayoutUtils::DirectDrawInfo& image,
                             mozilla::gfx::Rect* bounds, double dx, double dy,
                             double dw, double dh, double sx, double sy,
                             double sw, double sh, gfxIntSize imgSize);
 
   nsString& GetFont()
@@ -706,17 +706,17 @@ protected:
   // If mCanvasElement is not provided, then a docshell is
   nsCOMPtr<nsIDocShell> mDocShell;
 
   // This is created lazily so it is necessary to call EnsureTarget before
   // accessing it. In the event of an error it will be equal to
   // sErrorTarget.
   mozilla::RefPtr<mozilla::gfx::DrawTarget> mTarget;
 
-  RefPtr<gfx::SurfaceStream> mStream;
+  RefPtr<gl::SurfaceStream> mStream;
 
   /**
     * Flag to avoid duplicate calls to InvalidateFrame. Set to true whenever
     * Redraw is called, reset to false when Render is called.
     */
   bool mIsEntireFrameInvalid;
   /**
     * When this is set, the first call to Redraw(gfxRect) should set
@@ -784,17 +784,17 @@ protected:
     * drawing operation.
     */
   bool NeedToDrawShadow()
   {
     const ContextState& state = CurrentState();
 
     // The spec says we should not draw shadows if the operator is OVER.
     // If it's over and the alpha value is zero, nothing needs to be drawn.
-    return NS_GET_A(state.shadowColor) != 0 && 
+    return NS_GET_A(state.shadowColor) != 0 &&
       (state.shadowBlur != 0 || state.shadowOffset.x != 0 || state.shadowOffset.y != 0);
   }
 
   mozilla::gfx::CompositionOp UsedOperation()
   {
     if (NeedToDrawShadow()) {
       // In this case the shadow rendering will use the operator.
       return mozilla::gfx::CompositionOp::OP_OVER;
--- a/content/canvas/src/WebGLContextDraw.cpp
+++ b/content/canvas/src/WebGLContextDraw.cpp
@@ -445,17 +445,27 @@ WebGLContext::ValidateBufferFetching(con
             ErrorInvalidOperation("%s: integer overflow occured while checking vertex attrib %d", info, i);
             return false;
         }
 
         if (vd.divisor == 0) {
             maxVertices = std::min(maxVertices, checked_maxAllowedCount.value());
             hasPerVertex = true;
         } else {
-            maxInstances = std::min(maxInstances, checked_maxAllowedCount.value() / vd.divisor);
+            CheckedUint32 checked_curMaxInstances = checked_maxAllowedCount * vd.divisor;
+
+            uint32_t curMaxInstances = UINT32_MAX;
+            // If this isn't valid, it's because we overflowed our
+            // uint32 above. Just leave this as UINT32_MAX, since
+            // sizeof(uint32) becomes our limiting factor.
+            if (checked_curMaxInstances.isValid()) {
+                curMaxInstances = checked_curMaxInstances.value();
+            }
+
+            maxInstances = std::min(maxInstances, curMaxInstances);
         }
     }
 
     mBufferFetchingIsVerified = true;
     mBufferFetchingHasPerVertex = hasPerVertex;
     mMaxFetchedVertices = maxVertices;
     mMaxFetchedInstances = maxInstances;
 
--- a/content/html/content/public/HTMLAudioElement.h
+++ b/content/html/content/public/HTMLAudioElement.h
@@ -2,41 +2,37 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #ifndef mozilla_dom_HTMLAudioElement_h
 #define mozilla_dom_HTMLAudioElement_h
 
 #include "mozilla/Attributes.h"
-#include "nsIDOMHTMLAudioElement.h"
 #include "mozilla/dom/HTMLMediaElement.h"
 #include "mozilla/dom/TypedArray.h"
 
 typedef uint16_t nsMediaNetworkState;
 typedef uint16_t nsMediaReadyState;
 
 namespace mozilla {
 namespace dom {
 
-class HTMLAudioElement MOZ_FINAL : public HTMLMediaElement,
-                                   public nsIDOMHTMLAudioElement
+class HTMLAudioElement MOZ_FINAL : public HTMLMediaElement
 {
 public:
-  HTMLAudioElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
+  typedef mozilla::dom::NodeInfo NodeInfo;
 
-  // nsISupports
-  NS_DECL_ISUPPORTS_INHERITED
+  HTMLAudioElement(already_AddRefed<NodeInfo>& aNodeInfo);
 
   // nsIDOMHTMLMediaElement
   using HTMLMediaElement::GetPaused;
-  NS_FORWARD_NSIDOMHTMLMEDIAELEMENT(HTMLMediaElement::)
 
-  virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const;
-  virtual nsresult SetAcceptHeader(nsIHttpChannel* aChannel);
+  virtual nsresult Clone(NodeInfo *aNodeInfo, nsINode **aResult) const MOZ_OVERRIDE;
+  virtual nsresult SetAcceptHeader(nsIHttpChannel* aChannel) MOZ_OVERRIDE;
 
   virtual nsIDOMNode* AsDOMNode() MOZ_OVERRIDE { return this; }
 
   // WebIDL
 
   static already_AddRefed<HTMLAudioElement>
   Audio(const GlobalObject& aGlobal,
         const Optional<nsAString>& aSrc, ErrorResult& aRv);
--- a/content/html/content/public/HTMLMediaElement.h
+++ b/content/html/content/public/HTMLMediaElement.h
@@ -67,16 +67,17 @@ namespace dom {
 
 class MediaError;
 class MediaSource;
 class TextTrackList;
 class AudioTrackList;
 class VideoTrackList;
 
 class HTMLMediaElement : public nsGenericHTMLElement,
+                         public nsIDOMHTMLMediaElement,
                          public nsIObserver,
                          public MediaDecoderOwner,
                          public nsIAudioChannelAgentCallback
 {
 public:
   typedef mozilla::TimeStamp TimeStamp;
   typedef mozilla::layers::ImageContainer ImageContainer;
   typedef mozilla::VideoFrameContainer VideoFrameContainer;
--- a/content/html/content/public/HTMLVideoElement.h
+++ b/content/html/content/public/HTMLVideoElement.h
@@ -3,60 +3,56 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_HTMLVideoElement_h
 #define mozilla_dom_HTMLVideoElement_h
 
 #include "mozilla/Attributes.h"
-#include "nsIDOMHTMLVideoElement.h"
 #include "mozilla/dom/HTMLMediaElement.h"
 
 namespace mozilla {
 namespace dom {
 
 class WakeLock;
 class VideoPlaybackQuality;
 
-class HTMLVideoElement MOZ_FINAL : public HTMLMediaElement,
-                                   public nsIDOMHTMLVideoElement
+class HTMLVideoElement MOZ_FINAL : public HTMLMediaElement
 {
 public:
-  HTMLVideoElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
+  typedef mozilla::dom::NodeInfo NodeInfo;
+
+  HTMLVideoElement(already_AddRefed<NodeInfo>& aNodeInfo);
 
   NS_IMPL_FROMCONTENT_HTML_WITH_TAG(HTMLVideoElement, video)
 
-  // nsISupports
-  NS_DECL_ISUPPORTS_INHERITED
+  using HTMLMediaElement::GetPaused;
 
-  // nsIDOMHTMLMediaElement
-  using HTMLMediaElement::GetPaused;
-  NS_FORWARD_NSIDOMHTMLMEDIAELEMENT(HTMLMediaElement::)
-
-  // nsIDOMHTMLVideoElement
-  NS_DECL_NSIDOMHTMLVIDEOELEMENT
+  NS_IMETHOD_(bool) IsVideo() MOZ_OVERRIDE {
+    return true;
+  }
 
   virtual bool ParseAttribute(int32_t aNamespaceID,
-                                nsIAtom* aAttribute,
-                                const nsAString& aValue,
-                                nsAttrValue& aResult);
+                              nsIAtom* aAttribute,
+                              const nsAString& aValue,
+                              nsAttrValue& aResult) MOZ_OVERRIDE;
   NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const MOZ_OVERRIDE;
 
   static void Init();
 
   virtual nsMapRuleToAttributesFunc GetAttributeMappingFunction() const MOZ_OVERRIDE;
 
-  virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const MOZ_OVERRIDE;
+  virtual nsresult Clone(NodeInfo *aNodeInfo, nsINode **aResult) const MOZ_OVERRIDE;
 
   // Set size with the current video frame's height and width.
   // If there is no video frame, returns NS_ERROR_FAILURE.
   nsresult GetVideoSize(nsIntSize* size);
 
-  virtual nsresult SetAcceptHeader(nsIHttpChannel* aChannel);
+  virtual nsresult SetAcceptHeader(nsIHttpChannel* aChannel) MOZ_OVERRIDE;
 
   // WebIDL
 
   uint32_t Width() const
   {
     return GetIntAttr(nsGkAtoms::width, 0);
   }
 
@@ -80,17 +76,20 @@ public:
     return mMediaSize.width == -1 ? 0 : mMediaSize.width;
   }
 
   uint32_t VideoHeight() const
   {
     return mMediaSize.height == -1 ? 0 : mMediaSize.height;
   }
 
-  // XPCOM GetPoster is OK
+  void GetPoster(nsAString& aValue)
+  {
+    GetURIAttr(nsGkAtoms::poster, nullptr, aValue);
+  }
   void SetPoster(const nsAString& aValue, ErrorResult& aRv)
   {
     SetHTMLAttr(nsGkAtoms::poster, aValue, aRv);
   }
 
   uint32_t MozParsedFrames() const;
 
   uint32_t MozDecodedFrames() const;
--- a/content/html/content/src/HTMLAnchorElement.cpp
+++ b/content/html/content/src/HTMLAnchorElement.cpp
@@ -295,23 +295,27 @@ HTMLAnchorElement::RelList()
   }
   return mRelList;
 }
 
 #define IMPL_URI_PART(_part)                                 \
   NS_IMETHODIMP                                              \
   HTMLAnchorElement::Get##_part(nsAString& a##_part)         \
   {                                                          \
-    Link::Get##_part(a##_part);                              \
+    ErrorResult rv;                                          \
+    Link::Get##_part(a##_part, rv);                          \
+    MOZ_ASSERT(!rv.Failed());                                \
     return NS_OK;                                            \
   }                                                          \
   NS_IMETHODIMP                                              \
   HTMLAnchorElement::Set##_part(const nsAString& a##_part)   \
   {                                                          \
-    Link::Set##_part(a##_part);                              \
+    ErrorResult rv;                                          \
+    Link::Set##_part(a##_part, rv);                          \
+    MOZ_ASSERT(!rv.Failed());                                \
     return NS_OK;                                            \
   }
 
 IMPL_URI_PART(Protocol)
 IMPL_URI_PART(Host)
 IMPL_URI_PART(Hostname)
 IMPL_URI_PART(Pathname)
 IMPL_URI_PART(Search)
--- a/content/html/content/src/HTMLAnchorElement.h
+++ b/content/html/content/src/HTMLAnchorElement.h
@@ -81,17 +81,17 @@ public:
 
   virtual EventStates IntrinsicState() const MOZ_OVERRIDE;
 
   virtual void OnDNSPrefetchDeferred();
   virtual void OnDNSPrefetchRequested();
   virtual bool HasDeferredDNSPrefetchRequest();
 
   // WebIDL API
-  void GetHref(nsString& aValue)
+  void GetHref(nsAString& aValue, ErrorResult& rv)
   {
     GetHTMLURIAttr(nsGkAtoms::href, aValue);
   }
   void SetHref(const nsAString& aValue, mozilla::ErrorResult& rv)
   {
     SetHTMLAttr(nsGkAtoms::href, aValue, rv);
   }
   // The XPCOM GetTarget is OK for us
@@ -140,21 +140,42 @@ public:
   // The XPCOM GetText is OK for us
   void SetText(const nsAString& aValue, mozilla::ErrorResult& rv)
   {
     rv = SetText(aValue);
   }
 
   // Link::GetOrigin is OK for us
 
+  using Link::GetProtocol;
+  using Link::SetProtocol;
+
   // Link::GetUsername is OK for us
   // Link::SetUsername is OK for us
 
-  // Link::Getpassword is OK for us
-  // Link::Setpassword is OK for us
+  // Link::GetPassword is OK for us
+  // Link::SetPassword is OK for us
+
+  using Link::GetHost;
+  using Link::SetHost;
+
+  using Link::GetHostname;
+  using Link::SetHostname;
+
+  using Link::GetPort;
+  using Link::SetPort;
+
+  using Link::GetPathname;
+  using Link::SetPathname;
+
+  using Link::GetSearch;
+  using Link::SetSearch;
+
+  using Link::GetHash;
+  using Link::SetHash;
 
   // The XPCOM URI decomposition attributes are fine for us
   void GetCoords(nsString& aValue)
   {
     GetHTMLAttr(nsGkAtoms::coords, aValue);
   }
   void SetCoords(const nsAString& aValue, mozilla::ErrorResult& rv)
   {
@@ -187,19 +208,19 @@ public:
   void GetShape(nsString& aValue)
   {
     GetHTMLAttr(nsGkAtoms::shape, aValue);
   }
   void SetShape(const nsAString& aValue, mozilla::ErrorResult& rv)
   {
     SetHTMLAttr(nsGkAtoms::shape, aValue, rv);
   }
-  void Stringify(nsAString& aResult)
+  void Stringify(nsAString& aResult, ErrorResult& aError)
   {
-    GetHref(aResult);
+    GetHref(aResult, aError);
   }
 
 protected:
   virtual ~HTMLAnchorElement();
 
   virtual void GetItemValueText(nsAString& text) MOZ_OVERRIDE;
   virtual void SetItemValueText(const nsAString& text) MOZ_OVERRIDE;
   virtual JSObject* WrapNode(JSContext *aCx) MOZ_OVERRIDE;
--- a/content/html/content/src/HTMLAreaElement.cpp
+++ b/content/html/content/src/HTMLAreaElement.cpp
@@ -197,23 +197,27 @@ HTMLAreaElement::UnsetAttr(int32_t aName
 
   return rv;
 }
 
 #define IMPL_URI_PART(_part)                                 \
   NS_IMETHODIMP                                              \
   HTMLAreaElement::Get##_part(nsAString& a##_part)           \
   {                                                          \
-    Link::Get##_part(a##_part);                              \
+    ErrorResult rv;                                          \
+    Link::Get##_part(a##_part, rv);                          \
+    MOZ_ASSERT(!rv.Failed());                                \
     return NS_OK;                                            \
   }                                                          \
   NS_IMETHODIMP                                              \
   HTMLAreaElement::Set##_part(const nsAString& a##_part)     \
   {                                                          \
-    Link::Set##_part(a##_part);                              \
+    ErrorResult rv;                                          \
+    Link::Set##_part(a##_part, rv);                          \
+    MOZ_ASSERT(!rv.Failed());                                \
     return NS_OK;                                            \
   }
 
 IMPL_URI_PART(Protocol)
 IMPL_URI_PART(Host)
 IMPL_URI_PART(Hostname)
 IMPL_URI_PART(Pathname)
 IMPL_URI_PART(Search)
--- a/content/html/content/src/HTMLAreaElement.h
+++ b/content/html/content/src/HTMLAreaElement.h
@@ -85,17 +85,20 @@ public:
   }
 
   // The XPCOM GetShape is OK for us
   void SetShape(const nsAString& aShape, ErrorResult& aError)
   {
     SetHTMLAttr(nsGkAtoms::shape, aShape, aError);
   }
 
-  // The XPCOM GetHref is OK for us
+  void GetHref(nsAString& aHref, ErrorResult& aError)
+  {
+    aError = GetHref(aHref);
+  }
   void SetHref(const nsAString& aHref, ErrorResult& aError)
   {
     aError = SetHref(aHref);
   }
 
   // The XPCOM GetTarget is OK for us
   void SetTarget(const nsAString& aTarget, ErrorResult& aError)
   {
@@ -119,61 +122,62 @@ public:
     GetHTMLAttr(nsGkAtoms::rel, aValue);
   }
 
   void SetRel(const nsAString& aRel, ErrorResult& aError)
   {
     SetHTMLAttr(nsGkAtoms::rel, aRel, aError);
   } 
   nsDOMTokenList* RelList();
+
   // The Link::GetOrigin is OK for us
 
-  // The XPCOM GetProtocol is OK for us
-  // The XPCOM SetProtocol is OK for us
+  using Link::GetProtocol;
+  using Link::SetProtocol;
 
   // The Link::GetUsername is OK for us
   // The Link::SetUsername is OK for us
 
   // The Link::GetPassword is OK for us
   // The Link::SetPassword is OK for us
 
-  // The XPCOM GetHost is OK for us
-  // The XPCOM SetHost is OK for us
+  using Link::GetHost;
+  using Link::SetHost;
 
-  // The XPCOM GetHostname is OK for us
-  // The XPCOM SetHostname is OK for us
+  using Link::GetHostname;
+  using Link::SetHostname;
 
-  // The XPCOM GetPort is OK for us
-  // The XPCOM SetPort is OK for us
+  using Link::GetPort;
+  using Link::SetPort;
 
-  // The XPCOM GetPathname is OK for us
-  // The XPCOM SetPathname is OK for us
+  using Link::GetPathname;
+  using Link::SetPathname;
 
-  // The XPCOM GetSearch is OK for us
-  // The XPCOM SetSearch is OK for us
+  using Link::GetSearch;
+  using Link::SetSearch;
 
-  // The XPCOM GetHash is OK for us
-  // The XPCOM SetHash is OK for us
+  using Link::GetHash;
+  using Link::SetHash;
 
   // The Link::GetSearchParams is OK for us
   // The Link::SetSearchParams is OK for us
 
   bool NoHref() const
   {
     return GetBoolAttr(nsGkAtoms::nohref);
   }
 
   void SetNoHref(bool aValue, ErrorResult& aError)
   {
     SetHTMLBoolAttr(nsGkAtoms::nohref, aValue, aError);
   }
 
-  void Stringify(nsAString& aResult)
+  void Stringify(nsAString& aResult, ErrorResult& aError)
   {
-    GetHref(aResult);
+    GetHref(aResult, aError);
   }
 
 protected:
   virtual ~HTMLAreaElement();
 
   virtual JSObject* WrapNode(JSContext* aCx) MOZ_OVERRIDE;
 
   virtual void GetItemValueText(nsAString& text) MOZ_OVERRIDE;
--- a/content/html/content/src/HTMLAudioElement.cpp
+++ b/content/html/content/src/HTMLAudioElement.cpp
@@ -2,17 +2,16 @@
 /* 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 "mozilla/dom/HTMLAudioElement.h"
 #include "mozilla/dom/HTMLAudioElementBinding.h"
 #include "nsError.h"
-#include "nsIDOMHTMLAudioElement.h"
 #include "nsGenericHTMLElement.h"
 #include "nsGkAtoms.h"
 #include "nsIDocument.h"
 #include "jsfriendapi.h"
 #include "nsContentUtils.h"
 #include "nsJSUtils.h"
 #include "AudioSampleFormat.h"
 #include "AudioChannelCommon.h"
@@ -24,32 +23,28 @@
 
 NS_IMPL_NS_NEW_HTML_ELEMENT(Audio)
 
 namespace mozilla {
 namespace dom {
 
 extern bool IsAudioAPIEnabled();
 
-NS_IMPL_ISUPPORTS_INHERITED(HTMLAudioElement, HTMLMediaElement,
-                            nsIDOMHTMLMediaElement, nsIDOMHTMLAudioElement)
-
 NS_IMPL_ELEMENT_CLONE(HTMLAudioElement)
 
 
-HTMLAudioElement::HTMLAudioElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
+HTMLAudioElement::HTMLAudioElement(already_AddRefed<NodeInfo>& aNodeInfo)
   : HTMLMediaElement(aNodeInfo)
 {
 }
 
 HTMLAudioElement::~HTMLAudioElement()
 {
 }
 
-
 already_AddRefed<HTMLAudioElement>
 HTMLAudioElement::Audio(const GlobalObject& aGlobal,
                         const Optional<nsAString>& aSrc,
                         ErrorResult& aRv)
 {
   nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(aGlobal.GetAsSupports());
   nsIDocument* doc;
   if (!win || !(doc = win->GetExtantDoc())) {
--- a/content/html/content/src/HTMLMediaElement.cpp
+++ b/content/html/content/src/HTMLMediaElement.cpp
@@ -43,17 +43,16 @@
 
 #include "nsITimer.h"
 
 #include "MediaError.h"
 #include "MediaDecoder.h"
 #include "nsICategoryManager.h"
 #include "MediaResource.h"
 
-#include "nsIDOMHTMLVideoElement.h"
 #include "nsIContentPolicy.h"
 #include "nsContentPolicyUtils.h"
 #include "nsCrossSiteListenerProxy.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsICachingChannel.h"
 #include "nsLayoutUtils.h"
 #include "nsVideoFrame.h"
 #include "Layers.h"
@@ -453,16 +452,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_IN
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPlayed)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mTextTrackManager)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mAudioTrackList)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mVideoTrackList)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mMediaKeys)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(HTMLMediaElement)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMHTMLMediaElement)
   NS_INTERFACE_MAP_ENTRY(nsIObserver)
   NS_INTERFACE_MAP_ENTRY(nsIAudioChannelAgentCallback)
 NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElement)
 
 // nsIDOMHTMLMediaElement
 NS_IMPL_URI_ATTR(HTMLMediaElement, Src, src)
 NS_IMPL_STRING_ATTR(HTMLMediaElement, CrossOrigin, crossorigin)
 NS_IMPL_BOOL_ATTR(HTMLMediaElement, Controls, controls)
@@ -483,16 +483,22 @@ HTMLMediaElement::GetMozAudioChannelType
 }
 
 NS_IMETHODIMP
 HTMLMediaElement::SetMozAudioChannelType(const nsAString& aValue)
 {
   return SetAttrHelper(nsGkAtoms::mozaudiochannel, aValue);
 }
 
+NS_IMETHODIMP_(bool)
+HTMLMediaElement::IsVideo()
+{
+  return false;
+}
+
 already_AddRefed<DOMMediaStream>
 HTMLMediaElement::GetMozSrcObject() const
 {
   NS_ASSERTION(!mSrcAttrStream || mSrcAttrStream->GetStream(),
                "MediaStream should have been set up properly");
   nsRefPtr<DOMMediaStream> stream = mSrcAttrStream;
   return stream.forget();
 }
@@ -1815,18 +1821,17 @@ HTMLMediaElement::CaptureStreamInternal(
   // to behave appropriately when media streams generated
   // via mozCaptureStream*() are added to the Peer Connection.
   // This functionality is planned to be used as part of Audio
   // Quality Performance testing for WebRTC.
   // Bug932845: Revisit this once hints mechanism is dealt with
   // holistically.
   uint8_t hints = 0;
   if (Preferences::GetBool("media.capturestream_hints.enabled")) {
-    nsCOMPtr<nsIDOMHTMLVideoElement> video = do_QueryObject(this);
-    if (video && GetVideoFrameContainer()) {
+    if (IsVideo() && GetVideoFrameContainer()) {
       hints = DOMMediaStream::HINT_CONTENTS_VIDEO | DOMMediaStream::HINT_CONTENTS_AUDIO;
     } else {
       hints = DOMMediaStream::HINT_CONTENTS_AUDIO;
     }
   }
   out->mStream = DOMMediaStream::CreateTrackUnionStream(window, hints);
 #else
   out->mStream = DOMMediaStream::CreateTrackUnionStream(window);
@@ -2758,21 +2763,24 @@ public:
     nsCOMPtr<nsIRunnable> event;
     if (aBlocked == BLOCKED) {
       event = NS_NewRunnableMethod(this, &StreamListener::DoNotifyBlocked);
     } else {
       event = NS_NewRunnableMethod(this, &StreamListener::DoNotifyUnblocked);
     }
     aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
   }
-  virtual void NotifyFinished(MediaStreamGraph* aGraph) MOZ_OVERRIDE
+  virtual void NotifyEvent(MediaStreamGraph* aGraph,
+                           MediaStreamListener::MediaStreamGraphEvent event) MOZ_OVERRIDE
   {
-    nsCOMPtr<nsIRunnable> event =
-      NS_NewRunnableMethod(this, &StreamListener::DoNotifyFinished);
-    aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
+    if (event == EVENT_FINISHED) {
+      nsCOMPtr<nsIRunnable> event =
+        NS_NewRunnableMethod(this, &StreamListener::DoNotifyFinished);
+      aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
+    }
   }
   virtual void NotifyHasCurrentData(MediaStreamGraph* aGraph) MOZ_OVERRIDE
   {
     MutexAutoLock lock(mMutex);
     nsCOMPtr<nsIRunnable> event =
       NS_NewRunnableMethod(this, &StreamListener::DoNotifyHaveCurrentData);
     aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
   }
@@ -3270,19 +3278,19 @@ VideoFrameContainer* HTMLMediaElement::G
       mMediaSize == nsIntSize(-1, -1)) {
     return nullptr;
   }
 
   if (mVideoFrameContainer)
     return mVideoFrameContainer;
 
   // Only video frames need an image container.
-  nsCOMPtr<nsIDOMHTMLVideoElement> video = do_QueryObject(this);
-  if (!video)
+  if (!IsVideo()) {
     return nullptr;
+  }
 
   mVideoFrameContainer =
     new VideoFrameContainer(this, LayerManager::CreateAsynchronousImageContainer());
 
   return mVideoFrameContainer;
 }
 
 nsresult HTMLMediaElement::DispatchEvent(const nsAString& aName)
@@ -3879,19 +3887,18 @@ void HTMLMediaElement::UpdateAudioChanne
     mPlayingThroughTheAudioChannel = playingThroughTheAudioChannel;
 
     if (!mAudioChannelAgent) {
       nsresult rv;
       mAudioChannelAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1", &rv);
       if (!mAudioChannelAgent) {
         return;
       }
-      nsCOMPtr<nsIDOMHTMLVideoElement> video = do_QueryObject(this);
       // Use a weak ref so the audio channel agent can't leak |this|.
-      if (AudioChannel::Normal == mAudioChannel && video) {
+      if (AudioChannel::Normal == mAudioChannel && IsVideo()) {
         mAudioChannelAgent->InitWithVideo(OwnerDoc()->GetWindow(),
                                           static_cast<int32_t>(mAudioChannel),
                                           this, true);
       } else {
         mAudioChannelAgent->InitWithWeakCallback(OwnerDoc()->GetWindow(),
                                                  static_cast<int32_t>(mAudioChannel),
                                                  this);
       }
--- a/content/html/content/src/HTMLMetaElement.cpp
+++ b/content/html/content/src/HTMLMetaElement.cpp
@@ -44,16 +44,31 @@ HTMLMetaElement::GetItemValueText(nsAStr
 void
 HTMLMetaElement::SetItemValueText(const nsAString& aValue)
 {
   SetContent(aValue);
 }
 
 
 nsresult
+HTMLMetaElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
+                              const nsAttrValue* aValue, bool aNotify)
+{
+  if (aNameSpaceID == kNameSpaceID_None) {
+    if (aName == nsGkAtoms::content) {
+      nsIDocument *document = GetCurrentDoc();
+      CreateAndDispatchEvent(document, NS_LITERAL_STRING("DOMMetaChanged"));
+    }
+  }
+
+  return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, aValue,
+                                            aNotify);
+}
+
+nsresult
 HTMLMetaElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                             nsIContent* aBindingParent,
                             bool aCompileEventHandlers)
 {
   nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
                                                  aBindingParent,
                                                  aCompileEventHandlers);
   NS_ENSURE_SUCCESS(rv, rv);
--- a/content/html/content/src/HTMLMetaElement.h
+++ b/content/html/content/src/HTMLMetaElement.h
@@ -25,16 +25,20 @@ public:
   // nsIDOMHTMLMetaElement
   NS_DECL_NSIDOMHTMLMETAELEMENT
 
   virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                               nsIContent* aBindingParent,
                               bool aCompileEventHandlers) MOZ_OVERRIDE;
   virtual void UnbindFromTree(bool aDeep = true,
                               bool aNullParent = true) MOZ_OVERRIDE;
+
+  virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
+                                const nsAttrValue* aValue, bool aNotify) MOZ_OVERRIDE;
+
   void CreateAndDispatchEvent(nsIDocument* aDoc, const nsAString& aEventName);
 
   virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const MOZ_OVERRIDE;
 
   // XPCOM GetName is fine.
   void SetName(const nsAString& aName, ErrorResult& aRv)
   {
     SetHTMLAttr(nsGkAtoms::name, aName, aRv);
--- a/content/html/content/src/HTMLVideoElement.cpp
+++ b/content/html/content/src/HTMLVideoElement.cpp
@@ -1,15 +1,14 @@
 /* -*- 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 "nsIDOMHTMLVideoElement.h"
 #include "nsIDOMHTMLSourceElement.h"
 #include "mozilla/dom/HTMLVideoElement.h"
 #include "mozilla/dom/HTMLVideoElementBinding.h"
 #include "nsGenericHTMLElement.h"
 #include "nsGkAtoms.h"
 #include "nsSize.h"
 #include "nsError.h"
 #include "nsNodeInfoManager.h"
@@ -35,41 +34,19 @@
 
 NS_IMPL_NS_NEW_HTML_ELEMENT(Video)
 
 namespace mozilla {
 namespace dom {
 
 static bool sVideoStatsEnabled;
 
-NS_IMPL_ISUPPORTS_INHERITED(HTMLVideoElement, HTMLMediaElement,
-                            nsIDOMHTMLMediaElement, nsIDOMHTMLVideoElement)
-
 NS_IMPL_ELEMENT_CLONE(HTMLVideoElement)
 
-// nsIDOMHTMLVideoElement
-NS_IMPL_INT_ATTR(HTMLVideoElement, Width, width)
-NS_IMPL_INT_ATTR(HTMLVideoElement, Height, height)
-
-// nsIDOMHTMLVideoElement
-/* readonly attribute unsigned long videoWidth; */
-NS_IMETHODIMP HTMLVideoElement::GetVideoWidth(uint32_t *aVideoWidth)
-{
-  *aVideoWidth = VideoWidth();
-  return NS_OK;
-}
-
-/* readonly attribute unsigned long videoHeight; */
-NS_IMETHODIMP HTMLVideoElement::GetVideoHeight(uint32_t *aVideoHeight)
-{
-  *aVideoHeight = VideoHeight();
-  return NS_OK;
-}
-
-HTMLVideoElement::HTMLVideoElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
+HTMLVideoElement::HTMLVideoElement(already_AddRefed<NodeInfo>& aNodeInfo)
   : HTMLMediaElement(aNodeInfo)
 {
 }
 
 HTMLVideoElement::~HTMLVideoElement()
 {
 }
 
@@ -144,103 +121,66 @@ nsresult HTMLVideoElement::SetAcceptHead
       "application/ogg;q=0.7,"
       "audio/*;q=0.6,*/*;q=0.5");
 
   return aChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
                                     value,
                                     false);
 }
 
-NS_IMPL_URI_ATTR(HTMLVideoElement, Poster, poster)
-
 uint32_t HTMLVideoElement::MozParsedFrames() const
 {
   MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread.");
   if (!sVideoStatsEnabled) {
     return 0;
   }
   return mDecoder ? mDecoder->GetFrameStatistics().GetParsedFrames() : 0;
 }
 
-NS_IMETHODIMP HTMLVideoElement::GetMozParsedFrames(uint32_t *aMozParsedFrames)
-{
-  *aMozParsedFrames = MozParsedFrames();
-  return NS_OK;
-}
-
 uint32_t HTMLVideoElement::MozDecodedFrames() const
 {
   MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread.");
   if (!sVideoStatsEnabled) {
     return 0;
   }
   return mDecoder ? mDecoder->GetFrameStatistics().GetDecodedFrames() : 0;
 }
 
-NS_IMETHODIMP HTMLVideoElement::GetMozDecodedFrames(uint32_t *aMozDecodedFrames)
-{
-  *aMozDecodedFrames = MozDecodedFrames();
-  return NS_OK;
-}
-
 uint32_t HTMLVideoElement::MozPresentedFrames() const
 {
   MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread.");
   if (!sVideoStatsEnabled) {
     return 0;
   }
   return mDecoder ? mDecoder->GetFrameStatistics().GetPresentedFrames() : 0;
 }
 
-NS_IMETHODIMP HTMLVideoElement::GetMozPresentedFrames(uint32_t *aMozPresentedFrames)
-{
-  *aMozPresentedFrames = MozPresentedFrames();
-  return NS_OK;
-}
-
 uint32_t HTMLVideoElement::MozPaintedFrames()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread.");
   if (!sVideoStatsEnabled) {
     return 0;
   }
   layers::ImageContainer* container = GetImageContainer();
   return container ? container->GetPaintCount() : 0;
 }
 
-NS_IMETHODIMP HTMLVideoElement::GetMozPaintedFrames(uint32_t *aMozPaintedFrames)
-{
-  *aMozPaintedFrames = MozPaintedFrames();
-  return NS_OK;
-}
-
 double HTMLVideoElement::MozFrameDelay()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread.");
   VideoFrameContainer* container = GetVideoFrameContainer();
   return container ?  container->GetFrameDelay() : 0;
 }
 
-NS_IMETHODIMP HTMLVideoElement::GetMozFrameDelay(double *aMozFrameDelay) {
-  *aMozFrameDelay = MozFrameDelay();
-  return NS_OK;
-}
-
-/* readonly attribute bool mozHasAudio */
 bool HTMLVideoElement::MozHasAudio() const
 {
   MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread.");
   return mHasAudio;
 }
 
-NS_IMETHODIMP HTMLVideoElement::GetMozHasAudio(bool *aHasAudio) {
-  *aHasAudio = MozHasAudio();
-  return NS_OK;
-}
-
 JSObject*
 HTMLVideoElement::WrapNode(JSContext* aCx)
 {
   return HTMLVideoElementBinding::Wrap(aCx, this);
 }
 
 void
 HTMLVideoElement::NotifyOwnerDocumentActivityChanged()
@@ -319,10 +259,11 @@ HTMLVideoElement::UpdateScreenWakeLock()
   }
 }
 
 void
 HTMLVideoElement::Init()
 {
   Preferences::AddBoolVarCache(&sVideoStatsEnabled, "media.video_stats.enabled");
 }
+
 } // namespace dom
 } // namespace mozilla
--- a/content/html/content/src/nsHTMLDNSPrefetch.cpp
+++ b/content/html/content/src/nsHTMLDNSPrefetch.cpp
@@ -170,17 +170,18 @@ nsresult
 nsHTMLDNSPrefetch::CancelPrefetch(Link *aElement,
                                   uint16_t flags,
                                   nsresult aReason)
 {
   if (!(sInitialized && sPrefetches && sDNSService && sDNSListener))
     return NS_ERROR_NOT_AVAILABLE;
 
   nsAutoString hostname;
-  aElement->GetHostname(hostname);
+  ErrorResult rv;
+  aElement->GetHostname(hostname, rv);
   return CancelPrefetch(hostname, flags, aReason);
 }
 
 nsresult
 nsHTMLDNSPrefetch::CancelPrefetch(const nsAString &hostname,
                                   uint16_t flags,
                                   nsresult aReason)
 {
--- a/content/html/document/src/nsHTMLDocument.h
+++ b/content/html/document/src/nsHTMLDocument.h
@@ -235,17 +235,17 @@ public:
   void Clear() const
   {
     // Deprecated
   }
   mozilla::dom::Selection* GetSelection(mozilla::ErrorResult& aRv);
   // The XPCOM CaptureEvents works fine for us.
   // The XPCOM ReleaseEvents works fine for us.
   // We're picking up GetLocation from Document
-  already_AddRefed<nsIDOMLocation> GetLocation() const {
+  already_AddRefed<nsLocation> GetLocation() const {
     return nsIDocument::GetLocation();
   }
 
   virtual nsHTMLDocument* AsHTMLDocument() MOZ_OVERRIDE { return this; }
 
 protected:
   ~nsHTMLDocument();
 
--- a/content/media/MediaDecoder.cpp
+++ b/content/media/MediaDecoder.cpp
@@ -256,23 +256,25 @@ MediaDecoder::DecodedStreamGraphListener
     }
   }
 
   MutexAutoLock lock(mMutex);
   mStreamFinishedOnMainThread = true;
 }
 
 void
-MediaDecoder::DecodedStreamGraphListener::NotifyFinished(MediaStreamGraph* aGraph)
+MediaDecoder::DecodedStreamGraphListener::NotifyEvent(MediaStreamGraph* aGraph,
+  MediaStreamListener::MediaStreamGraphEvent event)
 {
-  nsCOMPtr<nsIRunnable> event =
-    NS_NewRunnableMethod(this, &DecodedStreamGraphListener::DoNotifyFinished);
-  aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
+  if (event == EVENT_FINISHED) {
+    nsCOMPtr<nsIRunnable> event =
+      NS_NewRunnableMethod(this, &DecodedStreamGraphListener::DoNotifyFinished);
+    aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
+  }
 }
-
 void MediaDecoder::DestroyDecodedStream()
 {
   MOZ_ASSERT(NS_IsMainThread());
   GetReentrantMonitor().AssertCurrentThreadIn();
 
   // All streams are having their SourceMediaStream disconnected, so they
   // need to be explicitly blocked again.
   for (int32_t i = mOutputStreams.Length() - 1; i >= 0; --i) {
--- a/content/media/MediaDecoder.h
+++ b/content/media/MediaDecoder.h
@@ -435,17 +435,18 @@ public:
     // mDecoderStateMachine is non-null and MediaDecoderStateMachine is false.
     bool mHaveBlockedForStateMachineNotPlaying;
   };
 
   class DecodedStreamGraphListener : public MediaStreamListener {
   public:
     DecodedStreamGraphListener(MediaStream* aStream, DecodedStreamData* aData);
     virtual void NotifyOutput(MediaStreamGraph* aGraph, GraphTime aCurrentTime) MOZ_OVERRIDE;
-    virtual void NotifyFinished(MediaStreamGraph* aGraph) MOZ_OVERRIDE;
+    virtual void NotifyEvent(MediaStreamGraph* aGraph,
+                             MediaStreamListener::MediaStreamGraphEvent event) MOZ_OVERRIDE;
 
     void DoNotifyFinished();
 
     int64_t GetLastOutputTime() // microseconds
     {
       MutexAutoLock lock(mMutex);
       return mLastOutputTime;
     }
--- a/content/media/MediaStreamGraph.cpp
+++ b/content/media/MediaStreamGraph.cpp
@@ -392,17 +392,17 @@ MediaStreamGraphImpl::UpdateCurrentTime(
   streamHasOutput.SetLength(mStreams.Length());
   for (uint32_t i = 0; i < mStreams.Length(); ++i) {
     MediaStream* stream = mStreams[i];
 
     // Calculate blocked time and fire Blocked/Unblocked events
     GraphTime blockedTime = 0;
     GraphTime t = prevCurrentTime;
     // include |nextCurrentTime| to ensure NotifyBlockingChanged() is called
-    // before NotifyFinished() when |nextCurrentTime == stream end time|
+    // before NotifyEvent(this, EVENT_FINISHED) when |nextCurrentTime == stream end time|
     while (t <= nextCurrentTime) {
       GraphTime end;
       bool blocked = stream->mBlocked.GetAt(t, &end);
       if (blocked) {
         blockedTime += std::min(end, nextCurrentTime) - t;
       }
       if (blocked != stream->mNotifiedBlocked) {
         for (uint32_t j = 0; j < stream->mListeners.Length(); ++j) {
@@ -455,17 +455,17 @@ MediaStreamGraphImpl::UpdateCurrentTime(
           stream->StreamTimeToGraphTime(stream->GetStreamBuffer().GetAllTracksEnd()))  {
       NS_WARN_IF_FALSE(stream->mNotifiedBlocked,
         "Should've notified blocked=true for a fully finished stream");
       stream->mNotifiedFinished = true;
       stream->mLastPlayedVideoFrame.SetNull();
       SetStreamOrderDirty();
       for (uint32_t j = 0; j < stream->mListeners.Length(); ++j) {
         MediaStreamListener* l = stream->mListeners[j];
-        l->NotifyFinished(this);
+        l->NotifyEvent(this, MediaStreamListener::EVENT_FINISHED);
       }
     }
   }
 }
 
 bool
 MediaStreamGraphImpl::WillUnderrun(MediaStream* aStream, GraphTime aTime,
                                    GraphTime aEndBlockingDecisions, GraphTime* aEnd)
@@ -1928,17 +1928,17 @@ MediaStream::EnsureTrack(TrackID aTrackI
   return track;
 }
 
 void
 MediaStream::RemoveAllListenersImpl()
 {
   for (int32_t i = mListeners.Length() - 1; i >= 0; --i) {
     nsRefPtr<MediaStreamListener> listener = mListeners[i].forget();
-    listener->NotifyRemoved(GraphImpl());
+    listener->NotifyEvent(GraphImpl(), MediaStreamListener::EVENT_REMOVED);
   }
   mListeners.Clear();
 }
 
 void
 MediaStream::DestroyImpl()
 {
   RemoveAllListenersImpl();
@@ -2106,17 +2106,17 @@ MediaStream::ChangeExplicitBlockerCount(
 
 void
 MediaStream::AddListenerImpl(already_AddRefed<MediaStreamListener> aListener)
 {
   MediaStreamListener* listener = *mListeners.AppendElement() = aListener;
   listener->NotifyBlockingChanged(GraphImpl(),
     mNotifiedBlocked ? MediaStreamListener::BLOCKED : MediaStreamListener::UNBLOCKED);
   if (mNotifiedFinished) {
-    listener->NotifyFinished(GraphImpl());
+    listener->NotifyEvent(GraphImpl(), MediaStreamListener::EVENT_FINISHED);
   }
   if (mNotifiedHasCurrentData) {
     listener->NotifyHasCurrentData(GraphImpl());
   }
 }
 
 void
 MediaStream::AddListener(MediaStreamListener* aListener)
@@ -2135,17 +2135,17 @@ MediaStream::AddListener(MediaStreamList
 }
 
 void
 MediaStream::RemoveListenerImpl(MediaStreamListener* aListener)
 {
   // wouldn't need this if we could do it in the opposite order
   nsRefPtr<MediaStreamListener> listener(aListener);
   mListeners.RemoveElement(aListener);
-  listener->NotifyRemoved(GraphImpl());
+  listener->NotifyEvent(GraphImpl(), MediaStreamListener::EVENT_REMOVED);
 }
 
 void
 MediaStream::RemoveListener(MediaStreamListener* aListener)
 {
   class Message : public ControlMessage {
   public:
     Message(MediaStream* aStream, MediaStreamListener* aListener) :
@@ -2362,25 +2362,47 @@ SourceMediaStream::NotifyDirectConsumers
     l->NotifyRealtimeData(static_cast<MediaStreamGraph*>(GraphImpl()), aTrack->mID, aTrack->mOutputRate,
                           offset, aTrack->mCommands, *aSegment);
   }
 }
 
 void
 SourceMediaStream::AddDirectListener(MediaStreamDirectListener* aListener)
 {
-  MutexAutoLock lock(mMutex);
-  mDirectListeners.AppendElement(aListener);
+  bool wasEmpty;
+  {
+    MutexAutoLock lock(mMutex);
+    wasEmpty = mDirectListeners.IsEmpty();
+    mDirectListeners.AppendElement(aListener);
+  }
+
+  if (wasEmpty) {
+    for (uint32_t j = 0; j < mListeners.Length(); ++j) {
+      MediaStreamListener* l = mListeners[j];
+      l->NotifyEvent(GraphImpl(), MediaStreamListener::EVENT_HAS_DIRECT_LISTENERS);
+    }
+  }
 }
 
 void
 SourceMediaStream::RemoveDirectListener(MediaStreamDirectListener* aListener)
 {
-  MutexAutoLock lock(mMutex);
-  mDirectListeners.RemoveElement(aListener);
+  bool isEmpty;
+  {
+    MutexAutoLock lock(mMutex);
+    mDirectListeners.RemoveElement(aListener);
+    isEmpty = mDirectListeners.IsEmpty();
+  }
+
+  if (isEmpty) {
+    for (uint32_t j = 0; j < mListeners.Length(); ++j) {
+      MediaStreamListener* l = mListeners[j];
+      l->NotifyEvent(GraphImpl(), MediaStreamListener::EVENT_HAS_NO_DIRECT_LISTENERS);
+    }
+  }
 }
 
 bool
 SourceMediaStream::HaveEnoughBuffered(TrackID aID)
 {
   MutexAutoLock lock(mMutex);
   TrackData *track = FindDataForTrack(aID);
   if (track) {
@@ -2450,17 +2472,17 @@ void
 SourceMediaStream::EndAllTrackAndFinish()
 {
   MutexAutoLock lock(mMutex);
   for (uint32_t i = 0; i < mUpdateTracks.Length(); ++i) {
     SourceMediaStream::TrackData* data = &mUpdateTracks[i];
     data->mCommands |= TRACK_END;
   }
   FinishWithLockHeld();
-  // we will call NotifyFinished() to let GetUserMedia know
+  // we will call NotifyEvent() to let GetUserMedia know
 }
 
 TrackTicks
 SourceMediaStream::GetBufferedTicks(TrackID aID)
 {
   StreamBuffer::Track* track  = mBuffer.FindTrack(aID);
   if (track) {
     MediaSegment* segment = track->GetSegment();
--- a/content/media/MediaStreamGraph.h
+++ b/content/media/MediaStreamGraph.h
@@ -106,16 +106,17 @@ protected:
 
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaStreamListener)
 
   enum Consumption {
     CONSUMED,
     NOT_CONSUMED
   };
+
   /**
    * Notify that the stream is hooked up and we'd like to start or stop receiving
    * data on it. Only fires on SourceMediaStreams.
    * The initial state is assumed to be NOT_CONSUMED.
    */
   virtual void NotifyConsumptionChanged(MediaStreamGraph* aGraph, Consumption aConsuming) {}
 
   /**
@@ -152,26 +153,27 @@ public:
 
   /**
    * Notify that the stream output is advancing. aCurrentTime is the graph's
    * current time. MediaStream::GraphTimeToStreamTime can be used to get the
    * stream time.
    */
   virtual void NotifyOutput(MediaStreamGraph* aGraph, GraphTime aCurrentTime) {}
 
-  /**
-   * Notify that the stream finished.
-   */
-  virtual void NotifyFinished(MediaStreamGraph* aGraph) {}
+  enum MediaStreamGraphEvent {
+    EVENT_FINISHED,
+    EVENT_REMOVED,
+    EVENT_HAS_DIRECT_LISTENERS, // transition from no direct listeners
+    EVENT_HAS_NO_DIRECT_LISTENERS,  // transition to no direct listeners
+  };
 
   /**
-   * Notify that your listener has been removed, either due to RemoveListener(),
-   * or due to the stream being destroyed.  You will get no further notifications.
+   * Notify that an event has occurred on the Stream
    */
-  virtual void NotifyRemoved(MediaStreamGraph* aGraph) {}
+  virtual void NotifyEvent(MediaStreamGraph* aGraph, MediaStreamGraphEvent aEvent) {}
 
   enum {
     TRACK_EVENT_CREATED = 0x01,
     TRACK_EVENT_ENDED = 0x02
   };
   /**
    * Notify that changes to one of the stream tracks have been queued.
    * aTrackEvents can be any combination of TRACK_EVENT_CREATED and
--- a/content/media/encoder/MediaEncoder.cpp
+++ b/content/media/encoder/MediaEncoder.cpp
@@ -59,27 +59,27 @@ MediaEncoder::NotifyQueuedTrackChanges(M
   } else if (mVideoEncoder && aQueuedMedia.GetType() == MediaSegment::VIDEO) {
       mVideoEncoder->NotifyQueuedTrackChanges(aGraph, aID, aTrackRate,
                                               aTrackOffset, aTrackEvents,
                                               aQueuedMedia);
   }
 }
 
 void
-MediaEncoder::NotifyRemoved(MediaStreamGraph* aGraph)
+MediaEncoder::NotifyEvent(MediaStreamGraph* aGraph,
+                          MediaStreamListener::MediaStreamGraphEvent event)
 {
   // In case that MediaEncoder does not receive a TRACK_EVENT_ENDED event.
   LOG(PR_LOG_DEBUG, ("NotifyRemoved in [MediaEncoder]."));
   if (mAudioEncoder) {
-    mAudioEncoder->NotifyRemoved(aGraph);
+    mAudioEncoder->NotifyEvent(aGraph, event);
   }
   if (mVideoEncoder) {
-    mVideoEncoder->NotifyRemoved(aGraph);
+    mVideoEncoder->NotifyEvent(aGraph, event);
   }
-
 }
 
 /* static */
 already_AddRefed<MediaEncoder>
 MediaEncoder::CreateEncoder(const nsAString& aMIMEType, uint8_t aTrackTypes)
 {
 #ifdef PR_LOGGING
   if (!gMediaEncoderLog) {
--- a/content/media/encoder/MediaEncoder.h
+++ b/content/media/encoder/MediaEncoder.h
@@ -75,22 +75,23 @@ public :
   /**
    * Notified by the control loop of MediaStreamGraph; aQueueMedia is the raw
    * track data in form of MediaSegment.
    */
   virtual void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
                                         TrackRate aTrackRate,
                                         TrackTicks aTrackOffset,
                                         uint32_t aTrackEvents,
-                                        const MediaSegment& aQueuedMedia);
+                                        const MediaSegment& aQueuedMedia) MOZ_OVERRIDE;
 
   /**
    * Notified the stream is being removed.
    */
-  virtual void NotifyRemoved(MediaStreamGraph* aGraph);
+  virtual void NotifyEvent(MediaStreamGraph* aGraph,
+                           MediaStreamListener::MediaStreamGraphEvent event) MOZ_OVERRIDE;
 
   /**
    * Creates an encoder with a given MIME type. Returns null if we are unable
    * to create the encoder. For now, default aMIMEType to "audio/ogg" and use
    * Ogg+Opus if it is empty.
    */
   static already_AddRefed<MediaEncoder> CreateEncoder(const nsAString& aMIMEType,
                                                       uint8_t aTrackTypes = ContainerWriter::CREATE_AUDIO_TRACK);
--- a/content/media/encoder/TrackEncoder.h
+++ b/content/media/encoder/TrackEncoder.h
@@ -8,21 +8,20 @@
 
 #include "mozilla/ReentrantMonitor.h"
 
 #include "AudioSegment.h"
 #include "EncodedFrameContainer.h"
 #include "StreamBuffer.h"
 #include "TrackMetadataBase.h"
 #include "VideoSegment.h"
+#include "MediaStreamGraph.h"
 
 namespace mozilla {
 
-class MediaStreamGraph;
-
 /**
  * Base class of AudioTrackEncoder and VideoTrackEncoder. Lifetimes managed by
  * MediaEncoder. Most methods can only be called on the MediaEncoder's thread,
  * but some subclass methods can be called on other threads when noted.
  *
  * NotifyQueuedTrackChanges is called on subclasses of this class from the
  * MediaStreamGraph thread, and AppendAudioSegment/AppendVideoSegment is then
  * called to store media data in the TrackEncoder. Later on, GetEncodedTrack is
@@ -44,17 +43,23 @@ public:
                                         TrackTicks aTrackOffset,
                                         uint32_t aTrackEvents,
                                         const MediaSegment& aQueuedMedia) = 0;
 
   /**
    * Notified by the same callback of MediaEncoder when it has been removed from
    * MediaStreamGraph. Called on the MediaStreamGraph thread.
    */
-  void NotifyRemoved(MediaStreamGraph* aGraph) { NotifyEndOfStream(); }
+  void NotifyEvent(MediaStreamGraph* aGraph,
+                   MediaStreamListener::MediaStreamGraphEvent event)
+  {
+    if (event == MediaStreamListener::MediaStreamGraphEvent::EVENT_REMOVED) {
+      NotifyEndOfStream();
+    }
+  }
 
   /**
    * Creates and sets up meta data for a specific codec, called on the worker
    * thread.
    */
   virtual already_AddRefed<TrackMetadataBase> GetMetadata() = 0;
 
   /**
--- a/content/media/gmp/GMPParent.cpp
+++ b/content/media/gmp/GMPParent.cpp
@@ -8,16 +8,17 @@
 #include "nsComponentManagerUtils.h"
 #include "nsIInputStream.h"
 #include "nsILineInputStream.h"
 #include "nsNetUtil.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsThreadUtils.h"
 #include "nsIRunnable.h"
 #include "mozIGeckoMediaPluginService.h"
+#include "mozilla/unused.h"
 
 namespace mozilla {
 namespace gmp {
 
 GMPParent::GMPParent()
   : mState(GMPStateNotLoaded)
   , mProcess(nullptr)
 {
@@ -121,30 +122,32 @@ GMPParent::UnloadProcess()
   }
 }
 
 void
 GMPParent::VideoDecoderDestroyed(GMPVideoDecoderParent* aDecoder)
 {
   MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
 
-  MOZ_ALWAYS_TRUE(mVideoDecoders.RemoveElement(aDecoder));
+  // If the constructor fails, we'll get called before it's added
+  unused << NS_WARN_IF(!mVideoDecoders.RemoveElement(aDecoder));
 
   // Recv__delete__ is on the stack, don't potentially destroy the top-level actor
   // until after this has completed.
   nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &GMPParent::MaybeUnloadProcess);
   NS_DispatchToCurrentThread(event);
 }
 
 void
 GMPParent::VideoEncoderDestroyed(GMPVideoEncoderParent* aEncoder)
 {
   MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
 
-  MOZ_ALWAYS_TRUE(mVideoEncoders.RemoveElement(aEncoder));
+  // If the constructor fails, we'll get called before it's added
+  unused << NS_WARN_IF(!mVideoEncoders.RemoveElement(aEncoder));
 
   // Recv__delete__ is on the stack, don't potentially destroy the top-level actor
   // until after this has completed.
   nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &GMPParent::MaybeUnloadProcess);
   NS_DispatchToCurrentThread(event);
 }
 
 GMPState
new file mode 100644
--- /dev/null
+++ b/content/media/omx/MediaCodecProxy.cpp
@@ -0,0 +1,392 @@
+/* -*- 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 "MediaCodecProxy.h"
+
+#include <string.h>
+
+#include <binder/IPCThreadState.h>
+
+namespace android {
+
+sp<MediaCodecProxy>
+MediaCodecProxy::CreateByType(sp<ALooper> aLooper,
+                              const char *aMime,
+                              bool aEncoder,
+                              bool aAsync,
+                              wp<CodecResourceListener> aListener)
+{
+  sp<MediaCodecProxy> codec = new MediaCodecProxy(aLooper, aMime, aEncoder, aAsync, aListener);
+  if ((!aAsync && codec->allocated()) || codec->requestResource()) {
+    return codec;
+  }
+  return nullptr;
+}
+
+MediaCodecProxy::MediaCodecProxy(sp<ALooper> aLooper,
+                                 const char *aMime,
+                                 bool aEncoder,
+                                 bool aAsync,
+                                 wp<CodecResourceListener> aListener)
+  : mCodecLooper(aLooper)
+  , mCodecMime(aMime)
+  , mCodecEncoder(aEncoder)
+  , mListener(aListener)
+{
+  MOZ_ASSERT(mCodecLooper != nullptr, "ALooper should not be nullptr.");
+  if (aAsync) {
+    mResourceHandler = new MediaResourceHandler(this);
+  } else {
+    allocateCodec();
+  }
+}
+
+MediaCodecProxy::~MediaCodecProxy()
+{
+  releaseCodec();
+
+  // Complete all pending Binder ipc transactions
+  IPCThreadState::self()->flushCommands();
+
+  cancelResource();
+}
+
+bool
+MediaCodecProxy::requestResource()
+{
+  if (mResourceHandler == nullptr) {
+    return false;
+  }
+
+  if (strncasecmp(mCodecMime.get(), "video/", 6) == 0) {
+    mResourceHandler->requestResource(mCodecEncoder
+        ? IMediaResourceManagerService::HW_VIDEO_ENCODER
+        : IMediaResourceManagerService::HW_VIDEO_DECODER);
+  } else if (strncasecmp(mCodecMime.get(), "audio/", 6) == 0) {
+    mResourceHandler->requestResource(mCodecEncoder
+        ? IMediaResourceManagerService::HW_AUDIO_ENCODER
+        : IMediaResourceManagerService::HW_AUDIO_DECODER);
+  } else {
+    return false;
+  }
+
+  return true;
+}
+
+void
+MediaCodecProxy::cancelResource()
+{
+  if (mResourceHandler == nullptr) {
+    return;
+  }
+
+  mResourceHandler->cancelResource();
+}
+
+bool
+MediaCodecProxy::allocateCodec()
+{
+  if (mCodecLooper == nullptr) {
+    return false;
+  }
+
+  // Write Lock for mCodec
+  RWLock::AutoWLock awl(mCodecLock);
+
+  // Create MediaCodec
+  mCodec = MediaCodec::CreateByType(mCodecLooper, mCodecMime.get(), mCodecEncoder);
+  if (mCodec == nullptr) {
+    return false;
+  }
+
+  return true;
+}
+
+void
+MediaCodecProxy::releaseCodec()
+{
+  wp<MediaCodec> codec;
+
+  {
+    // Write Lock for mCodec
+    RWLock::AutoWLock awl(mCodecLock);
+
+    codec = mCodec;
+
+    // Release MediaCodec
+    if (mCodec != nullptr) {
+      mCodec->release();
+      mCodec = nullptr;
+    }
+  }
+
+  while (codec.promote() != nullptr) {
+    // this value come from stagefright's AwesomePlayer.
+    usleep(1000);
+  }
+}
+
+bool
+MediaCodecProxy::allocated() const
+{
+  // Read Lock for mCodec
+  RWLock::AutoRLock arl(mCodecLock);
+
+  return mCodec != nullptr;
+}
+
+status_t
+MediaCodecProxy::configure(const sp<AMessage> &aFormat,
+                           const sp<Surface> &aNativeWindow,
+                           const sp<ICrypto> &aCrypto,
+                           uint32_t aFlags)
+{
+  // Read Lock for mCodec
+  RWLock::AutoRLock arl(mCodecLock);
+
+  if (mCodec == nullptr) {
+    return NO_INIT;
+  }
+  return mCodec->configure(aFormat, aNativeWindow, aCrypto, aFlags);
+}
+
+status_t
+MediaCodecProxy::start()
+{
+  // Read Lock for mCodec
+  RWLock::AutoRLock arl(mCodecLock);
+
+  if (mCodec == nullptr) {
+    return NO_INIT;
+  }
+  return mCodec->start();
+}
+
+status_t
+MediaCodecProxy::stop()
+{
+  // Read Lock for mCodec
+  RWLock::AutoRLock arl(mCodecLock);
+
+  if (mCodec == nullptr) {
+    return NO_INIT;
+  }
+  return mCodec->stop();
+}
+
+status_t
+MediaCodecProxy::release()
+{
+  // Read Lock for mCodec
+  RWLock::AutoRLock arl(mCodecLock);
+
+  if (mCodec == nullptr) {
+    return NO_INIT;
+  }
+  return mCodec->release();
+}
+
+status_t
+MediaCodecProxy::flush()
+{
+  // Read Lock for mCodec
+  RWLock::AutoRLock arl(mCodecLock);
+
+  if (mCodec == nullptr) {
+    return NO_INIT;
+  }
+  return mCodec->flush();
+}
+
+status_t
+MediaCodecProxy::queueInputBuffer(size_t aIndex,
+                                  size_t aOffset,
+                                  size_t aSize,
+                                  int64_t aPresentationTimeUs,
+                                  uint32_t aFlags,
+                                  AString *aErrorDetailMessage)
+{
+  // Read Lock for mCodec
+  RWLock::AutoRLock arl(mCodecLock);
+
+  if (mCodec == nullptr) {
+    return NO_INIT;
+  }
+  return mCodec->queueInputBuffer(aIndex, aOffset, aSize,
+      aPresentationTimeUs, aFlags, aErrorDetailMessage);
+}
+
+status_t
+MediaCodecProxy::queueSecureInputBuffer(size_t aIndex,
+                                        size_t aOffset,
+                                        const CryptoPlugin::SubSample *aSubSamples,
+                                        size_t aNumSubSamples,
+                                        const uint8_t aKey[16],
+                                        const uint8_t aIV[16],
+                                        CryptoPlugin::Mode aMode,
+                                        int64_t aPresentationTimeUs,
+                                        uint32_t aFlags,
+                                        AString *aErrorDetailMessage)
+{
+  // Read Lock for mCodec
+  RWLock::AutoRLock arl(mCodecLock);
+
+  if (mCodec == nullptr) {
+    return NO_INIT;
+  }
+  return mCodec->queueSecureInputBuffer(aIndex, aOffset,
+      aSubSamples, aNumSubSamples, aKey, aIV, aMode,
+      aPresentationTimeUs, aFlags, aErrorDetailMessage);
+}
+
+status_t
+MediaCodecProxy::dequeueInputBuffer(size_t *aIndex,
+                                    int64_t aTimeoutUs)
+{
+  // Read Lock for mCodec
+  RWLock::AutoRLock arl(mCodecLock);
+
+  if (mCodec == nullptr) {
+    return NO_INIT;
+  }
+  return mCodec->dequeueInputBuffer(aIndex, aTimeoutUs);
+}
+
+status_t
+MediaCodecProxy::dequeueOutputBuffer(size_t *aIndex,
+                                     size_t *aOffset,
+                                     size_t *aSize,
+                                     int64_t *aPresentationTimeUs,
+                                     uint32_t *aFlags,
+                                     int64_t aTimeoutUs)
+{
+  // Read Lock for mCodec
+  RWLock::AutoRLock arl(mCodecLock);
+
+  if (mCodec == nullptr) {
+    return NO_INIT;
+  }
+  return mCodec->dequeueOutputBuffer(aIndex, aOffset, aSize,
+      aPresentationTimeUs, aFlags, aTimeoutUs);
+}
+
+status_t
+MediaCodecProxy::renderOutputBufferAndRelease(size_t aIndex)
+{
+  // Read Lock for mCodec
+  RWLock::AutoRLock arl(mCodecLock);
+
+  if (mCodec == nullptr) {
+    return NO_INIT;
+  }
+  return mCodec->renderOutputBufferAndRelease(aIndex);
+}
+
+status_t
+MediaCodecProxy::releaseOutputBuffer(size_t aIndex)
+{
+  // Read Lock for mCodec
+  RWLock::AutoRLock arl(mCodecLock);
+
+  if (mCodec == nullptr) {
+    return NO_INIT;
+  }
+  return mCodec->releaseOutputBuffer(aIndex);
+}
+
+status_t
+MediaCodecProxy::signalEndOfInputStream()
+{
+  // Read Lock for mCodec
+  RWLock::AutoRLock arl(mCodecLock);
+
+  if (mCodec == nullptr) {
+    return NO_INIT;
+  }
+  return mCodec->signalEndOfInputStream();
+}
+
+status_t
+MediaCodecProxy::getOutputFormat(sp<AMessage> *aFormat) const
+{
+  // Read Lock for mCodec
+  RWLock::AutoRLock arl(mCodecLock);
+
+  if (mCodec == nullptr) {
+    return NO_INIT;
+  }
+  return mCodec->getOutputFormat(aFormat);
+}
+
+status_t
+MediaCodecProxy::getInputBuffers(Vector<sp<ABuffer>> *aBuffers) const
+{
+  // Read Lock for mCodec
+  RWLock::AutoRLock arl(mCodecLock);
+
+  if (mCodec == nullptr) {
+    return NO_INIT;
+  }
+  return mCodec->getInputBuffers(aBuffers);
+}
+
+status_t
+MediaCodecProxy::getOutputBuffers(Vector<sp<ABuffer>> *aBuffers) const
+{
+  // Read Lock for mCodec
+  RWLock::AutoRLock arl(mCodecLock);
+
+  if (mCodec == nullptr) {
+    return NO_INIT;
+  }
+  return mCodec->getOutputBuffers(aBuffers);
+}
+
+void
+MediaCodecProxy::requestActivityNotification(const sp<AMessage> &aNotify)
+{
+  // Read Lock for mCodec
+  RWLock::AutoRLock arl(mCodecLock);
+
+  if (mCodec == nullptr) {
+    return;
+  }
+  mCodec->requestActivityNotification(aNotify);
+}
+
+// Called on a Binder thread
+void
+MediaCodecProxy::resourceReserved()
+{
+  // Create MediaCodec
+  releaseCodec();
+  if (!allocateCodec()) {
+    cancelResource();
+    return;
+  }
+
+  // Notification
+  sp<CodecResourceListener> listener = mListener.promote();
+  if (listener != nullptr) {
+    listener->codecReserved();
+  }
+}
+
+// Called on a Binder thread
+void
+MediaCodecProxy::resourceCanceled()
+{
+  // Release MediaCodec
+  releaseCodec();
+
+  // Notification
+  sp<CodecResourceListener> listener = mListener.promote();
+  if (listener != nullptr) {
+    listener->codecCanceled();
+  }
+}
+
+} // namespace android
new file mode 100644
--- /dev/null
+++ b/content/media/omx/MediaCodecProxy.h
@@ -0,0 +1,156 @@
+/* -*- 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/. */
+
+#ifndef MEDIA_CODEC_PROXY_H
+#define MEDIA_CODEC_PROXY_H
+
+#include <nsString.h>
+
+#include <stagefright/MediaCodec.h>
+#include <utils/threads.h>
+
+#include "MediaResourceHandler.h"
+
+namespace android {
+
+class MediaCodecProxy : public MediaResourceHandler::ResourceListener
+{
+public:
+  /* Codec resource notification listener.
+   * All functions are called on the Binder thread.
+   */
+  struct CodecResourceListener : public virtual RefBase {
+    /* The codec resource is reserved and can be granted.
+     * The client can allocate the requested resource.
+     */
+    virtual void codecReserved() = 0;
+    /* The codec resource is not reserved any more.
+     * The client should release the resource as soon as possible if the
+     * resource is still being held.
+     */
+    virtual void codecCanceled() = 0;
+  };
+
+  // Check whether MediaCodec has been allocated.
+  bool allocated() const;
+
+  // Static MediaCodec methods
+  // Only support MediaCodec::CreateByType()
+  static sp<MediaCodecProxy> CreateByType(sp<ALooper> aLooper,
+                                          const char *aMime,
+                                          bool aEncoder,
+                                          bool aAsync=false,
+                                          wp<CodecResourceListener> aListener=nullptr);
+
+  // MediaCodec methods
+  status_t configure(const sp<AMessage> &aFormat,
+                     const sp<Surface> &aNativeWindow,
+                     const sp<ICrypto> &aCrypto,
+                     uint32_t aFlags);
+
+  status_t start();
+
+  status_t stop();
+
+  status_t release();
+
+  status_t flush();
+
+  status_t queueInputBuffer(size_t aIndex,
+                            size_t aOffset,
+                            size_t aSize,
+                            int64_t aPresentationTimeUs,
+                            uint32_t aFlags,
+                            AString *aErrorDetailMessage=nullptr);
+
+  status_t queueSecureInputBuffer(size_t aIndex,
+                                  size_t aOffset,
+                                  const CryptoPlugin::SubSample *aSubSamples,
+                                  size_t aNumSubSamples,
+                                  const uint8_t aKey[16],
+                                  const uint8_t aIV[16],
+                                  CryptoPlugin::Mode aMode,
+                                  int64_t aPresentationTimeUs,
+                                  uint32_t aFlags,
+                                  AString *aErrorDetailMessage=nullptr);
+
+  status_t dequeueInputBuffer(size_t *aIndex,
+                              int64_t aTimeoutUs=INT64_C(0));
+
+  status_t dequeueOutputBuffer(size_t *aIndex,
+                               size_t *aOffset,
+                               size_t *aSize,
+                               int64_t *aPresentationTimeUs,
+                               uint32_t *aFlags,
+                               int64_t aTimeoutUs=INT64_C(0));
+
+  status_t renderOutputBufferAndRelease(size_t aIndex);
+
+  status_t releaseOutputBuffer(size_t aIndex);
+
+  status_t signalEndOfInputStream();
+
+  status_t getOutputFormat(sp<AMessage> *aFormat) const;
+
+  status_t getInputBuffers(Vector<sp<ABuffer>> *aBuffers) const;
+
+  status_t getOutputBuffers(Vector<sp<ABuffer>> *aBuffers) const;
+
+  // Notification will be posted once there "is something to do", i.e.
+  // an input/output buffer has become available, a format change is
+  // pending, an error is pending.
+  void requestActivityNotification(const sp<AMessage> &aNotify);
+
+protected:
+  virtual ~MediaCodecProxy();
+
+  // MediaResourceHandler::EventListener::resourceReserved()
+  virtual void resourceReserved();
+  // MediaResourceHandler::EventListener::resourceCanceled()
+  virtual void resourceCanceled();
+
+private:
+  // Forbidden
+  MediaCodecProxy() MOZ_DELETE;
+  MediaCodecProxy(const MediaCodecProxy &) MOZ_DELETE;
+  const MediaCodecProxy &operator=(const MediaCodecProxy &) MOZ_DELETE;
+
+  // Constructor for MediaCodecProxy::CreateByType
+  MediaCodecProxy(sp<ALooper> aLooper,
+                  const char *aMime,
+                  bool aEncoder,
+                  bool aAsync,
+                  wp<CodecResourceListener> aListener);
+
+  // Request Resource
+  bool requestResource();
+  // Cancel Resource
+  void cancelResource();
+
+  // Allocate Codec Resource
+  bool allocateCodec();
+  // Release Codec Resource
+  void releaseCodec();
+
+  // MediaCodec Parameter
+  sp<ALooper> mCodecLooper;
+  nsCString mCodecMime;
+  bool mCodecEncoder;
+
+  // Codec Resource Notification Listener
+  wp<CodecResourceListener> mListener;
+
+  // Media Resource Management
+  sp<MediaResourceHandler> mResourceHandler;
+
+  // MediaCodec instance
+  mutable RWLock mCodecLock;
+  sp<MediaCodec> mCodec;
+};
+
+} // namespace android
+
+#endif // MEDIA_CODEC_PROXY_H
--- a/content/media/omx/mediaresourcemanager/IMediaResourceManagerService.h
+++ b/content/media/omx/mediaresourcemanager/IMediaResourceManagerService.h
@@ -22,16 +22,17 @@ class IMediaResourceManagerService : pub
 public:
     DECLARE_META_INTERFACE(MediaResourceManagerService);
 
     // Enumeration for the resource types
     enum ResourceType {
       HW_VIDEO_DECODER = 0,
       HW_AUDIO_DECODER,  // Not supported currently.
       HW_VIDEO_ENCODER,
+      HW_AUDIO_ENCODER,  // Not supported currently.
       HW_CAMERA,          // Not supported currently.
       NUM_OF_RESOURCE_TYPES,
       INVALID_RESOURCE_TYPE = -1
     };
 
     enum ErrorCode {
         RESOURCE_NOT_AVAILABLE = -EAGAIN
     };
new file mode 100644
--- /dev/null
+++ b/content/media/omx/mediaresourcemanager/MediaResourceHandler.cpp
@@ -0,0 +1,92 @@
+/* -*- 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 "MediaResourceHandler.h"
+
+#include "mozilla/NullPtr.h"
+
+namespace android {
+
+MediaResourceHandler::MediaResourceHandler(const wp<ResourceListener> &aListener)
+  : mListener(aListener)
+  , mState(MediaResourceManagerClient::CLIENT_STATE_WAIT_FOR_RESOURCE)
+  , mType(IMediaResourceManagerService::INVALID_RESOURCE_TYPE)
+{
+}
+
+MediaResourceHandler::~MediaResourceHandler()
+{
+  cancelResource();
+}
+
+bool
+MediaResourceHandler::requestResource(IMediaResourceManagerService::ResourceType aType)
+{
+  Mutex::Autolock al(mLock);
+
+  if (mClient != nullptr && mService != nullptr) {
+    return false;
+  }
+
+  sp<MediaResourceManagerClient> client = new MediaResourceManagerClient(this);
+  sp<IMediaResourceManagerService> service = client->getMediaResourceManagerService();
+
+  if (service == nullptr) {
+    return false;
+  }
+
+  if (service->requestMediaResource(client, (int)aType, true) != OK) {
+    return false;
+  }
+
+  mClient = client;
+  mService = service;
+  mType = aType;
+
+  return true;
+}
+
+void
+MediaResourceHandler::cancelResource()
+{
+  Mutex::Autolock al(mLock);
+
+  if (mClient != nullptr && mService != nullptr) {
+    mService->cancelClient(mClient, (int)mType);
+  }
+
+  mClient = nullptr;
+  mService = nullptr;
+}
+
+// Called on a Binder thread
+void
+MediaResourceHandler::statusChanged(int aEvent)
+{
+  sp<ResourceListener> listener;
+
+  Mutex::Autolock autoLock(mLock);
+
+  MediaResourceManagerClient::State state = (MediaResourceManagerClient::State)aEvent;
+  if (state == mState) {
+    return;
+  }
+
+  mState = state;
+
+  listener = mListener.promote();
+  if (listener == nullptr) {
+    return;
+  }
+
+  if (mState == MediaResourceManagerClient::CLIENT_STATE_RESOURCE_ASSIGNED) {
+    listener->resourceReserved();
+  } else {
+    listener->resourceCanceled();
+  }
+}
+
+} // namespace android
new file mode 100644
--- /dev/null
+++ b/content/media/omx/mediaresourcemanager/MediaResourceHandler.h
@@ -0,0 +1,68 @@
+/* -*- 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/. */
+
+#ifndef MEDIA_RESOURCE_HANDLER_H
+#define MEDIA_RESOURCE_HANDLER_H
+
+#include <utils/threads.h>
+
+#include <mozilla/Attributes.h>
+
+#include "MediaResourceManagerClient.h"
+
+namespace android {
+
+class MediaResourceHandler : public MediaResourceManagerClient::EventListener
+{
+public:
+  /* Resource notification listener.
+   * All functions are called on the Binder thread.
+   */
+  struct ResourceListener : public virtual RefBase {
+    /* The resource is reserved and can be granted.
+     * The client can allocate the requested resource.
+     */
+    virtual void resourceReserved() = 0;
+    /* The resource is not reserved any more.
+     * The client should release the resource as soon as possible if the
+     * resource is still being held.
+     */
+    virtual void resourceCanceled() = 0;
+  };
+
+  MediaResourceHandler(const wp<ResourceListener> &aListener);
+
+  virtual ~MediaResourceHandler();
+
+  // Request Resource
+  bool requestResource(IMediaResourceManagerService::ResourceType aType);
+  // Cancel Resource
+  void cancelResource();
+
+protected:
+  // MediaResourceManagerClient::EventListener::statusChanged()
+  virtual void statusChanged(int event);
+
+private:
+  // Forbidden
+  MediaResourceHandler() MOZ_DELETE;
+  MediaResourceHandler(const MediaResourceHandler &) MOZ_DELETE;
+  const MediaResourceHandler &operator=(const MediaResourceHandler &) MOZ_DELETE;
+
+  // Resource Notification Listener
+  wp<ResourceListener> mListener;
+
+  // Resource Management
+  Mutex mLock;
+  MediaResourceManagerClient::State mState;
+  sp<IMediaResourceManagerClient> mClient;
+  sp<IMediaResourceManagerService> mService;
+  IMediaResourceManagerService::ResourceType mType;
+};
+
+} // namespace android
+
+#endif // MEDIA_RESOURCE_HANDLER_H
--- a/content/media/omx/mediaresourcemanager/moz.build
+++ b/content/media/omx/mediaresourcemanager/moz.build
@@ -11,16 +11,17 @@ EXPORTS += [
     'MediaResourceManagerClient.h',
     'MediaResourceManagerService.h',
 ]
 
 SOURCES += [
     'IMediaResourceManagerClient.cpp',
     'IMediaResourceManagerDeathNotifier.cpp',
     'IMediaResourceManagerService.cpp',
+    'MediaResourceHandler.cpp',
     'MediaResourceManagerClient.cpp',
     'MediaResourceManagerService.cpp',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 CXXFLAGS += [
     '-I%s/%s' % (CONFIG['ANDROID_SOURCE'], d) for d in [
--- a/content/media/omx/moz.build
+++ b/content/media/omx/moz.build
@@ -42,16 +42,24 @@ if 'rtsp' in CONFIG['NECKO_PROTOCOLS']:
         'RtspOmxDecoder.h',
         'RtspOmxReader.h',
     ]
     SOURCES += [
         'RtspOmxDecoder.cpp',
         'RtspOmxReader.cpp',
     ]
 
+if int(CONFIG['ANDROID_VERSION']) >= 16:
+    EXPORTS += [
+        'MediaCodecProxy.h',
+    ]
+    SOURCES += [
+        'MediaCodecProxy.cpp',
+    ]
+
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'gklayout'
 LOCAL_INCLUDES += [
     '/content/base/src',
     '/content/html/content/src',
     '/ipc/chromium/src',
     'mediaresourcemanager',
--- a/content/media/webrtc/MediaEngine.h
+++ b/content/media/webrtc/MediaEngine.h
@@ -81,16 +81,19 @@ public:
   /* Release the device back to the system. */
   virtual nsresult Deallocate() = 0;
 
   /* Start the device and add the track to the provided SourceMediaStream, with
    * the provided TrackID. You may start appending data to the track
    * immediately after. */
   virtual nsresult Start(SourceMediaStream*, TrackID) = 0;
 
+  /* tell the source if there are any direct listeners attached */
+  virtual void SetDirectListeners(bool) = 0;
+
   /* Take a snapshot from this source. In the case of video this is a single
    * image, and for audio, it is a snippet lasting aDuration milliseconds. The
    * duration argument is ignored for a MediaEngineVideoSource.
    */
   virtual nsresult Snapshot(uint32_t aDuration, nsIDOMFile** aFile) = 0;
 
   /* Called when the stream wants more data */
   virtual void NotifyPull(MediaStreamGraph* aGraph,
--- a/content/media/webrtc/MediaEngineDefault.h
+++ b/content/media/webrtc/MediaEngineDefault.h
@@ -40,16 +40,17 @@ public:
   virtual void GetName(nsAString&);
   virtual void GetUUID(nsAString&);
 
   virtual nsresult Allocate(const VideoTrackConstraintsN &aConstraints,
                             const MediaEnginePrefs &aPrefs);
   virtual nsresult Deallocate();
   virtual nsresult Start(SourceMediaStream*, TrackID);
   virtual nsresult Stop(SourceMediaStream*, TrackID);
+  virtual void SetDirectListeners(bool aHasDirectListeners) {};
   virtual nsresult Snapshot(uint32_t aDuration, nsIDOMFile** aFile);
   virtual nsresult Config(bool aEchoOn, uint32_t aEcho,
                           bool aAgcOn, uint32_t aAGC,
                           bool aNoiseOn, uint32_t aNoise,
                           int32_t aPlayoutDelay) { return NS_OK; };
   virtual void NotifyPull(MediaStreamGraph* aGraph,
                           SourceMediaStream *aSource,
                           TrackID aId,
@@ -95,16 +96,17 @@ public:
   virtual void GetName(nsAString&);
   virtual void GetUUID(nsAString&);
 
   virtual nsresult Allocate(const AudioTrackConstraintsN &aConstraints,
                             const MediaEnginePrefs &aPrefs);
   virtual nsresult Deallocate();
   virtual nsresult Start(SourceMediaStream*, TrackID);
   virtual nsresult Stop(SourceMediaStream*, TrackID);
+  virtual void SetDirectListeners(bool aHasDirectListeners) {};
   virtual nsresult Snapshot(uint32_t aDuration, nsIDOMFile** aFile);
   virtual nsresult Config(bool aEchoOn, uint32_t aEcho,
                           bool aAgcOn, uint32_t aAGC,
                           bool aNoiseOn, uint32_t aNoise,
                           int32_t aPlayoutDelay) { return NS_OK; };
   virtual void NotifyPull(MediaStreamGraph* aGraph,
                           SourceMediaStream *aSource,
                           TrackID aId,
--- a/content/media/webrtc/MediaEngineTabVideoSource.h
+++ b/content/media/webrtc/MediaEngineTabVideoSource.h
@@ -19,16 +19,17 @@ class MediaEngineTabVideoSource : public
     MediaEngineTabVideoSource();
 
     virtual void GetName(nsAString_internal&);
     virtual void GetUUID(nsAString_internal&);
     virtual nsresult Allocate(const VideoTrackConstraintsN &,
                               const mozilla::MediaEnginePrefs&);
     virtual nsresult Deallocate();
     virtual nsresult Start(mozilla::SourceMediaStream*, mozilla::TrackID);
+    virtual void SetDirectListeners(bool aHasDirectListeners) {};
     virtual nsresult Snapshot(uint32_t, nsIDOMFile**);
     virtual void NotifyPull(mozilla::MediaStreamGraph*, mozilla::SourceMediaStream*, mozilla::TrackID, mozilla::StreamTime, mozilla::TrackTicks&);
     virtual nsresult Stop(mozilla::SourceMediaStream*, mozilla::TrackID);
     virtual nsresult Config(bool, uint32_t, bool, uint32_t, bool, uint32_t, int32_t);
     virtual bool IsFake();
     void Draw();
 
     class StartRunnable : public nsRunnable {
--- a/content/media/webrtc/MediaEngineWebRTC.h
+++ b/content/media/webrtc/MediaEngineWebRTC.h
@@ -99,16 +99,17 @@ public:
     : mCameraControl(nullptr)
     , mCallbackMonitor("WebRTCCamera.CallbackMonitor")
     , mRotation(0)
     , mBackCamera(false)
     , mCaptureIndex(aIndex)
     , mMonitor("WebRTCCamera.Monitor")
     , mWidth(0)
     , mHeight(0)
+    , mHasDirectListeners(false)
     , mInitDone(false)
     , mInSnapshotMode(false)
     , mSnapshotPath(nullptr)
   {
     mState = kReleased;
     Init();
   }
 #else
@@ -126,32 +127,34 @@ public:
   MediaEngineWebRTCVideoSource(webrtc::VideoEngine* aVideoEnginePtr, int aIndex)
     : mVideoEngine(aVideoEnginePtr)
     , mCaptureIndex(aIndex)
     , mFps(-1)
     , mMinFps(-1)
     , mMonitor("WebRTCCamera.Monitor")
     , mWidth(0)
     , mHeight(0)
+    , mHasDirectListeners(false)
     , mInitDone(false)
     , mInSnapshotMode(false)
     , mSnapshotPath(nullptr) {
     MOZ_ASSERT(aVideoEnginePtr);
     mState = kReleased;
     Init();
   }
 #endif
 
   virtual void GetName(nsAString&);
   virtual void GetUUID(nsAString&);
   virtual nsresult Allocate(const VideoTrackConstraintsN &aConstraints,
                             const MediaEnginePrefs &aPrefs);
   virtual nsresult Deallocate();
   virtual nsresult Start(SourceMediaStream*, TrackID);
   virtual nsresult Stop(SourceMediaStream*, TrackID);
+  virtual void SetDirectListeners(bool aHasListeners);
   virtual nsresult Snapshot(uint32_t aDuration, nsIDOMFile** aFile);
   virtual nsresult Config(bool aEchoOn, uint32_t aEcho,
                           bool aAgcOn, uint32_t aAGC,
                           bool aNoiseOn, uint32_t aNoise,
                           int32_t aPlayoutDelay) { return NS_OK; };
   virtual void NotifyPull(MediaStreamGraph* aGraph,
                           SourceMediaStream *aSource,
                           TrackID aId,
@@ -240,16 +243,17 @@ private:
   // mMonitor protects mImage access/changes, and transitions of mState
   // from kStarted to kStopped (which are combined with EndTrack() and
   // image changes).  Note that mSources is not accessed from other threads
   // for video and is not protected.
   Monitor mMonitor; // Monitor for processing WebRTC frames.
   int mWidth, mHeight;
   nsRefPtr<layers::Image> mImage;
   nsRefPtr<layers::ImageContainer> mImageContainer;
+  bool mHasDirectListeners;
 
   nsTArray<SourceMediaStream *> mSources; // When this goes empty, we shut down HW
 
   bool mInitDone;
   bool mInSnapshotMode;
   nsString* mSnapshotPath;
 
   nsString mDeviceName;
@@ -292,16 +296,17 @@ public:
   virtual void GetName(nsAString&);
   virtual void GetUUID(nsAString&);
 
   virtual nsresult Allocate(const AudioTrackConstraintsN &aConstraints,
                             const MediaEnginePrefs &aPrefs);
   virtual nsresult Deallocate();
   virtual nsresult Start(SourceMediaStream*, TrackID);
   virtual nsresult Stop(SourceMediaStream*, TrackID);
+  virtual void SetDirectListeners(bool aHasDirectListeners) {};
   virtual nsresult Snapshot(uint32_t aDuration, nsIDOMFile** aFile);
   virtual nsresult Config(bool aEchoOn, uint32_t aEcho,
                           bool aAgcOn, uint32_t aAGC,
                           bool aNoiseOn, uint32_t aNoise,
                           int32_t aPlayoutDelay);
 
   virtual void NotifyPull(MediaStreamGraph* aGraph,
                           SourceMediaStream *aSource,
--- a/content/media/webrtc/MediaEngineWebRTCVideo.cpp
+++ b/content/media/webrtc/MediaEngineWebRTCVideo.cpp
@@ -579,16 +579,23 @@ MediaEngineWebRTCVideoSource::Stop(Sourc
   mViERender->StopRender(mCaptureIndex);
   mViERender->RemoveRenderer(mCaptureIndex);
   mViECapture->StopCapture(mCaptureIndex);
 #endif
 
   return NS_OK;
 }
 
+void
+MediaEngineWebRTCVideoSource::SetDirectListeners(bool aHasDirectListeners)
+{
+  LOG((__FUNCTION__));
+  mHasDirectListeners = aHasDirectListeners;
+}
+
 nsresult
 MediaEngineWebRTCVideoSource::Snapshot(uint32_t aDuration, nsIDOMFile** aFile)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 /**
  * Initialization and Shutdown functions for the video source, called by the
@@ -729,26 +736,29 @@ GetRotateAmount(ScreenOrientation aScree
   } else {
     //front camera
     result = (aCameraMountAngle + screenAngle) % 360;
   }
   return result;
 }
 
 // undefine to remove on-the-fly rotation support
-// #define DYNAMIC_GUM_ROTATION
+#define DYNAMIC_GUM_ROTATION
 
 void
 MediaEngineWebRTCVideoSource::Notify(const hal::ScreenConfiguration& aConfiguration) {
 #ifdef DYNAMIC_GUM_ROTATION
-  MonitorAutoLock enter(mMonitor);
-  mRotation = GetRotateAmount(aConfiguration.orientation(), mCameraAngle, mBackCamera);
+  if (mHasDirectListeners) {
+    // aka hooked to PeerConnection
+    MonitorAutoLock enter(mMonitor);
+    mRotation = GetRotateAmount(aConfiguration.orientation(), mCameraAngle, mBackCamera);
 
-  LOG(("*** New orientation: %d (Camera %d Back %d MountAngle: %d)",
-       mRotation, mCaptureIndex, mBackCamera, mCameraAngle));
+    LOG(("*** New orientation: %d (Camera %d Back %d MountAngle: %d)",
+         mRotation, mCaptureIndex, mBackCamera, mCameraAngle));
+  }
 #endif
 }
 
 void
 MediaEngineWebRTCVideoSource::StartImpl(webrtc::CaptureCapability aCapability) {
   MOZ_ASSERT(NS_IsMainThread());
 
   ICameraControl::Configuration config;
--- a/content/media/webspeech/recognition/SpeechStreamListener.cpp
+++ b/content/media/webspeech/recognition/SpeechStreamListener.cpp
@@ -79,15 +79,16 @@ SpeechStreamListener::ConvertAndDispatch
 
   int16_t* to = static_cast<int16_t*>(samples->Data());
   ConvertAudioSamplesWithScale(aData, to, aDuration, aVolume);
 
   mRecognition->FeedAudioData(samples.forget(), aDuration, this);
 }
 
 void
-SpeechStreamListener::NotifyFinished(MediaStreamGraph* aGraph)
+SpeechStreamListener::NotifyEvent(MediaStreamGraph* aGraph,
+                                  MediaStreamListener::MediaStreamGraphEvent event)
 {
   // TODO dispatch SpeechEnd event so services can be informed
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/content/media/webspeech/recognition/SpeechStreamListener.h
+++ b/content/media/webspeech/recognition/SpeechStreamListener.h
@@ -25,17 +25,18 @@ public:
   ~SpeechStreamListener();
 
   void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
                                 TrackRate aTrackRate,
                                 TrackTicks aTrackOffset,
                                 uint32_t aTrackEvents,
                                 const MediaSegment& aQueuedMedia) MOZ_OVERRIDE;
 
-  void NotifyFinished(MediaStreamGraph* aGraph) MOZ_OVERRIDE;
+  void NotifyEvent(MediaStreamGraph* aGraph,
+                   MediaStreamListener::MediaStreamGraphEvent event) MOZ_OVERRIDE;
 
 private:
   template<typename SampleFormatType>
   void ConvertAndDispatchAudioChunk(int aDuration, float aVolume, SampleFormatType* aData);
   nsRefPtr<SpeechRecognition> mRecognition;
 };
 
 } // namespace dom
--- a/content/media/webspeech/synth/nsSpeechTask.cpp
+++ b/content/media/webspeech/synth/nsSpeechTask.cpp
@@ -44,38 +44,45 @@ public:
   void DoNotifyFinished()
   {
     if (mSpeechTask) {
       mSpeechTask->DispatchEndImpl(mSpeechTask->GetCurrentTime(),
                                    mSpeechTask->GetCurrentCharOffset());
     }
   }
 
-  virtual void NotifyFinished(MediaStreamGraph* aGraph)
+  virtual void NotifyEvent(MediaStreamGraph* aGraph,
+                           MediaStreamListener::MediaStreamGraphEvent event) MOZ_OVERRIDE
   {
-    nsCOMPtr<nsIRunnable> event =
-      NS_NewRunnableMethod(this, &SynthStreamListener::DoNotifyFinished);
-    aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
+    switch (event) {
+      case EVENT_FINISHED:
+        {
+          nsCOMPtr<nsIRunnable> runnable =
+            NS_NewRunnableMethod(this, &SynthStreamListener::DoNotifyFinished);
+          aGraph->DispatchToMainThreadAfterStreamStateUpdate(runnable.forget());
+        }
+        break;
+      case EVENT_REMOVED:
+        mSpeechTask = nullptr;
+        break;
+      default:
+        break;
+    }
   }
 
   virtual void NotifyBlockingChanged(MediaStreamGraph* aGraph, Blocking aBlocked)
   {
     if (aBlocked == MediaStreamListener::UNBLOCKED && !mStarted) {
       mStarted = true;
       nsCOMPtr<nsIRunnable> event =
         NS_NewRunnableMethod(this, &SynthStreamListener::DoNotifyStarted);
       aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
     }
   }
 
-  virtual void NotifyRemoved(MediaStreamGraph* aGraph)
-  {
-    mSpeechTask = nullptr;
-  }
-
 private:
   // Raw pointer; if we exist, the stream exists,
   // and 'mSpeechTask' exclusively owns it and therefor exists as well.
   nsSpeechTask* mSpeechTask;
 
   bool mStarted;
 };
 
--- a/content/svg/content/src/SVGEllipseElement.cpp
+++ b/content/svg/content/src/SVGEllipseElement.cpp
@@ -98,18 +98,17 @@ SVGEllipseElement::ConstructPath(gfxCont
 {
   RefPtr<DrawTarget> dt = aCtx->GetDrawTarget();
   FillRule fillRule =
     aCtx->CurrentFillRule() == gfxContext::FILL_RULE_WINDING ?
       FillRule::FILL_WINDING : FillRule::FILL_EVEN_ODD;
   RefPtr<PathBuilder> builder = dt->CreatePathBuilder(fillRule);
   RefPtr<Path> path = BuildPath(builder);
   if (path) {
-    nsRefPtr<gfxPath> gfxpath = new gfxPath(path);
-    aCtx->SetPath(gfxpath);
+    aCtx->SetPath(path);
   }
 }
 
 TemporaryRef<Path>
 SVGEllipseElement::BuildPath(PathBuilder* aBuilder)
 {
   float x, y, rx, ry;
   GetAnimatedLengthValues(&x, &y, &rx, &ry, nullptr);
--- a/docshell/base/nsIContentViewer.idl
+++ b/docshell/base/nsIContentViewer.idl
@@ -22,17 +22,17 @@ class nsDOMNavigationTiming;
 
 [ptr] native nsIWidgetPtr(nsIWidget);
 [ref] native nsIntRectRef(nsIntRect);
 [ptr] native nsIPresShellPtr(nsIPresShell);
 [ptr] native nsPresContextPtr(nsPresContext);
 [ptr] native nsViewPtr(nsView);
 [ptr] native nsDOMNavigationTimingPtr(nsDOMNavigationTiming);
 
-[scriptable, builtinclass, uuid(0cb321bd-5b38-4586-8fcd-d43b366886fb)]
+[scriptable, builtinclass, uuid(f92298b8-4fe3-40d1-aad7-44e704fffd0d)]
 interface nsIContentViewer : nsISupports
 {
 
   [noscript] void init(in nsIWidgetPtr aParentWidget,
                        [const] in nsIntRectRef aBounds);
 
   attribute nsIDocShell container;
 
@@ -171,18 +171,20 @@ interface nsIContentViewer : nsISupports
    * we're part way through some operation (eg beforeunload) that shouldn't be
    * rentrant if the user closes the tab while the prompt is showing.
    * See bug 613800.
    */
   readonly attribute boolean isTabModalPromptAllowed;
 
   /**
    * Returns whether this content viewer is in a hidden state.
+   *
+   * @note Only Gecko internal code should set the attribute!
    */
-  readonly attribute boolean isHidden;
+  attribute boolean isHidden;
 
   [noscript] readonly attribute nsIPresShellPtr presShell;
   [noscript] readonly attribute nsPresContextPtr presContext;
   // aDocument must not be null.
   [noscript] void setDocumentInternal(in nsIDocument aDocument,
                                       in boolean aForceReuseInnerWindow);
   /**
    * Find the view to use as the container view for MakeWindow. Returns
--- a/dom/apps/tests/file_packaged_app.sjs
+++ b/dom/apps/tests/file_packaged_app.sjs
@@ -15,33 +15,45 @@ var gMiniManifestTemplate = "file_packag
 var gAppTemplate = "file_packaged_app.template.html";
 var gAppName = "appname";
 var gDevName = "devname";
 var gDevUrl = "http://dev.url";
 
 function handleRequest(request, response) {
   var query = getQuery(request);
 
-  response.setHeader("Access-Control-Allow-Origin", "*", false);
-
   var packageSize = ("packageSize" in query) ? query.packageSize : 0;
   var appName = ("appName" in query) ? query.appName : gAppName;
   var devName = ("devName" in query) ? query.devName : gDevName;
   var devUrl = ("devUrl" in query) ? query.devUrl : gDevUrl;
+  // allowCancel just means deliver the file slowly so we have time to cancel it
+  var allowCancel = "allowCancel" in query;
+  var getPackage = "getPackage" in query;
+  var alreadyDeferred = Number(getState("alreadyDeferred"));
+
+  if (allowCancel && getPackage && !alreadyDeferred) {
+    // Only do this for the actual package delivery.
+    response.processAsync();
+    // And to avoid timer problems, only do this once.
+    setState("alreadyDeferred", "1");
+  }
+
+  response.setHeader("Access-Control-Allow-Origin", "*", false);
 
   // If this is a version update, update state, prepare the manifest,
   // the application package and return.
   if ("setVersion" in query) {
     var version = query.setVersion;
     setState("version", version);
     var packageVersion = ("dontUpdatePackage" in query) ? version - 1 : version;
     var packageName = "test_packaged_app_" + packageVersion + ".zip";
 
     setState("packageName", packageName);
-    var packagePath = "/" + gBasePath + "file_packaged_app.sjs?getPackage=" +
+    var packagePath = "/" + gBasePath + "file_packaged_app.sjs?" +
+                      (allowCancel?"allowCancel&": "") + "getPackage=" +
                       packageName;
     setState("packagePath", packagePath);
 
     if (version == packageVersion) {
       // Create the application package.
       var zipWriter = Cc["@mozilla.org/zipwriter;1"]
                         .createInstance(Ci.nsIZipWriter);
       var zipFile = FileUtils.getFile("TmpD", [packageName]);
@@ -79,21 +91,29 @@ function handleRequest(request, response
     dump("Etags Match. Sending 304\n");
     response.setStatusLine(request.httpVersion, "304", "Not modified");
     return;
   }
 
   response.setHeader("Etag", etag, false);
 
   // Serve the application package corresponding to the requested app version.
-  if ("getPackage" in query) {
+  if (getPackage) {
     var resource = readFile(packageName, true);
     response.setHeader("Content-Type",
                        "Content-Type: application/java-archive", false);
-    response.write(resource);
+    if (allowCancel && !alreadyDeferred) {
+      var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+      timer.initWithCallback(function (aTimer) {
+          response.write(resource);
+          response.finish();
+      }, 3000, Ci.nsITimer.TYPE_ONE_SHOT);
+    } else {
+      response.write(resource);
+    }
     return;
   }
 
   // Serve the mini-manifest corresponding to the requested app version.
   if ("getManifest" in query) {
     var template = gBasePath + gMiniManifestTemplate;
     if (!("noManifestContentType" in query)) {
       response.setHeader("Content-Type",
--- a/dom/apps/tests/test_packaged_app_common.js
+++ b/dom/apps/tests/test_packaged_app_common.js
@@ -52,23 +52,27 @@ var PackagedTestHelper = (function Packa
     finish();
   }
 
   function xhrAbort(url) {
     ok(false, "XHR abort loading " + url);
     finish();
   }
 
-  function setAppVersion(aVersion, aCb, aDontUpdatePackage) {
+  function setAppVersion(aVersion, aCb, aDontUpdatePackage, aAllowCancel) {
     var xhr = new XMLHttpRequest();
     var dontUpdate = "";
+    var allowCancel = "";
     if (aDontUpdatePackage) {
       dontUpdate = "&dontUpdatePackage=1";
     }
-    var url = gSJS + "?setVersion=" + aVersion + dontUpdate;
+    if (aAllowCancel) {
+      allowCancel= "&allowCancel=1";
+    }
+    var url = gSJS + "?setVersion=" + aVersion + dontUpdate + allowCancel;
     xhr.addEventListener("load", function() {
                            is(xhr.responseText, "OK", "setAppVersion OK");
                            aCb();
                          });
     xhr.addEventListener("error", event => xhrError(event, url));
     xhr.addEventListener("abort", event => xhrAbort(url));
     xhr.open("GET", url, true);
     xhr.send();
--- a/dom/apps/tests/test_packaged_app_install.html
+++ b/dom/apps/tests/test_packaged_app_install.html
@@ -29,29 +29,29 @@ function checkAppInstallError(aMiniManif
   var req = navigator.mozApps.installPackage(aMiniManifestURL);
   req.onsuccess = function() {
     ok(false, "We are supposed to throw " + aExpectedError);
     PackagedTestHelper.finish();
   };
   req.onerror = function(evt) {
     var error = evt.target.error.name;
     if (error == aExpectedError) {
-      ok(true, "Got expected " + aExpectedError);
+      info("Got expected " + aExpectedError);
       PackagedTestHelper.next();
     } else {
       ok(false, "Got unexpected " + error);
       PackagedTestHelper.finish();
     }
   };
 }
 
 function checkUninstallApp(aApp) {
   var req = navigator.mozApps.mgmt.uninstall(aApp);
   req.onsuccess = function() {
-    ok(true, "App uninstalled");
+    info("App uninstalled");
     aApp.ondownloadsuccess = null;
     aApp.ondownloaderror = null;
     aApp.onprogress = null;
     PackagedTestHelper.next();
   };
   req.onerror = function(evt) {
     ok(false, "Got unexpected " + evt.target.error.name);
     PackagedTestHelper.finish();
@@ -78,84 +78,84 @@ function checkInstalledApp(aMiniManifest
 
 SimpleTest.waitForExplicitFinish();
 
 var steps = [
   function() {
     // Set up
     SpecialPowers.setAllAppsLaunchable(true);
     SpecialPowers.addPermission("webapps-manage", true, document);
-    ok(true, "Set up");
+    info("Set up");
     PackagedTestHelper.next();
   },
   function() {
-    ok(true, "autoConfirmAppInstall");
+    info("autoConfirmAppInstall");
     SpecialPowers.autoConfirmAppInstall(PackagedTestHelper.next);
   },
   function() {
     PackagedTestHelper.setAppVersion(0, PackagedTestHelper.next);
   },
   function() {
     // Bug 927699 - navigator.mozApps.install(url) lets NS_ERROR_FAILURE onto
     //              the web.
-    ok(true, "== TEST == INVALID_URL");
+    info("== TEST == INVALID_URL");
     checkAppInstallError("", "INVALID_URL");
   },
   function() {
     // Test network error.
-    ok(true, "== TEST == Network error");
+    info("== TEST == Network error");
     checkAppInstallError("http://notvalidurl", "NETWORK_ERROR");
   },
   function() {
     // Test wrong mini-manifest content type.
-    ok(true, "== TEST == Not valid mini-manifest content type");
+    info("== TEST == Not valid mini-manifest content type");
     var miniManifestURL = PackagedTestHelper.gSJS +
                           "?getManifest=true" +
                           "&noManifestContentType=true";
     checkAppInstallError(miniManifestURL, "INVALID_MANIFEST_CONTENT_TYPE");
   },
   function() {
     // Test mini-manifest 'size' value is not number. Bug 839435.
-    ok(true, "== TEST == Size value is not a number");
+    info("== TEST == Size value is not a number");
     var miniManifestURL = PackagedTestHelper.gSJS +
                           "?getManifest=true" +
                           "&packageSize=\"NotANumber\"";
     checkAppInstallError(miniManifestURL, "INVALID_MANIFEST");
   },
   function() {
     // Test mini-manifest  negative 'size' value. Bug 839435.
-    ok(true, "== TEST == Negative size value");
+    info("== TEST == Negative size value");
     var miniManifestURL = PackagedTestHelper.gSJS +
                           "?getManifest=true" +
                           "&packageSize=-1";
     checkAppInstallError(miniManifestURL, "INVALID_MANIFEST");
   },
   function() {
     // Test wrong package path
-    ok(true, "== TEST == Installing app with wrong package path");
+    info("== TEST == Installing app with wrong package path");
     var miniManifestURL = PackagedTestHelper.gSJS +
                           "?getManifest=true" +
                           "&wrongPackagePath=true";
     checkAppInstallError(miniManifestURL, "INVALID_MANIFEST");
   },
   function() {
     // Test no manifest in zip file.
-    ok(true, "== TEST == No manifest in the zip file");
+    info("== TEST == No manifest in the zip file");
     var miniManifestURL = PackagedTestHelper.gSJS + "?getManifest=true";
     PackagedTestHelper.checkAppDownloadError(miniManifestURL,
                                             "MISSING_MANIFEST", 0, true, true,
                                              PackagedTestHelper.gAppName);
   },
   function() {
       PackagedTestHelper.setAppVersion(1, PackagedTestHelper.next);
   },
   function() {
     // Test mini-manifest app name is different from the webapp manifest name.
     // Bug 844243.
-    ok(true, "== TEST == Mini-manifest app name is different from webapp " +
+    info("== TEST == Mini-manifest app name is different from webapp " +
              "manifest name");
     var miniManifestURL = PackagedTestHelper.gSJS +
                           "?getManifest=true" +
                           "&appName=arandomname";
     PackagedTestHelper.checkAppDownloadError(miniManifestURL,
                                              "MANIFEST_MISMATCH", 1, true, true,
                                              "arandomname");
   },
@@ -182,29 +182,29 @@ var steps = [
     PackagedTestHelper.checkAppDownloadError(miniManifestURL,
                                              "MANIFEST_MISMATCH", 1, true, true,
                                              PackagedTestHelper.gAppName);
   },
   function() {
     PackagedTestHelper.setAppVersion(2, PackagedTestHelper.next);
   },
   function() {
-    ok(true, "== TEST == Install packaged app");
+    info("== TEST == Install packaged app");
     var miniManifestURL = PackagedTestHelper.gSJS +
                           "?getManifest=true";
     navigator.mozApps.mgmt.oninstall = function(evt) {
-      ok(true, "Got oninstall event");
+      info("Got oninstall event");
       PackagedTestHelper.gApp = evt.application;
       PackagedTestHelper.gApp.ondownloaderror = function() {
         ok(false, "Download error " +
                   PackagedTestHelper.gApp.downloadError.name);
         PackagedTestHelper.finish();
       };
       PackagedTestHelper.gApp.ondownloadsuccess = function() {
-        ok(true, "App downloaded");
+        info("App downloaded");
         var expected = {
           name: PackagedTestHelper.gAppName,
           manifestURL: miniManifestURL,
           installOrigin: PackagedTestHelper.gInstallOrigin,
           progress: 0,
           installState: "installed",
           downloadAvailable: false,
           downloading: false,
@@ -215,21 +215,95 @@ var steps = [
         PackagedTestHelper.checkAppState(PackagedTestHelper.gApp, 2, expected,
                                          true, false, PackagedTestHelper.next);
       };
     };
 
     var request = navigator.mozApps.installPackage(miniManifestURL);
     request.onerror = PackagedTestHelper.mozAppsError;
     request.onsuccess = function() {
-      ok(true, "Application installed");
+      info("Application installed");
     };
   },
   function() {
-    ok(true, "all done!\n");
+    PackagedTestHelper.setAppVersion(3, PackagedTestHelper.next, false, true);
+  },
+  function() {
+    info("== TEST == Install packaged app with a cancel/resume");
+    var miniManifestURL = PackagedTestHelper.gSJS +
+                          "?getManifest=true&allowCancel";
+    navigator.mozApps.mgmt.oninstall = function(evt) {
+      info("Got oninstall event");
+      PackagedTestHelper.gApp = evt.application;
+
+      PackagedTestHelper.gApp.onprogress = function() {
+        // Let's try cancelling and resuming the download later on.
+        PackagedTestHelper.gApp.cancelDownload();
+        // And only do this once.
+        PackagedTestHelper.gApp.onprogress = null;
+      };
+
+      var alreadyCancelled = false;
+      PackagedTestHelper.gApp.ondownloaderror = function() {
+        ok(!alreadyCancelled, "The download should be cancelled only once!");
+        is(PackagedTestHelper.gApp.downloadError.name, "DOWNLOAD_CANCELED",
+           "Download error " + PackagedTestHelper.gApp.downloadError.name);
+        if (!alreadyCancelled) {
+          PackagedTestHelper.gApp.download();
+          alreadyCancelled = true;
+        }
+      };
+
+      PackagedTestHelper.gApp.ondownloadsuccess = function() {
+        info("App downloaded");
+        // We could try also applying the download we just made.
+        var expected = {
+          name: PackagedTestHelper.gAppName,
+          manifestURL: miniManifestURL,
+          installOrigin: PackagedTestHelper.gInstallOrigin,
+          progress: 0,
+          installState: "pending",
+          downloadAvailable: false,
+          downloading: false,
+          downloadSize: 0,
+          size: 0,
+          readyToApplyDownload: true
+        };
+        PackagedTestHelper.checkAppState(PackagedTestHelper.gApp, 3, expected,
+                                         true, false, function() {});
+      };
+
+      PackagedTestHelper.gApp.ondownloadapplied = function() {
+        info("App download applied.");
+        var expected = {
+          name: PackagedTestHelper.gAppName,
+          manifestURL: miniManifestURL,
+          installOrigin: PackagedTestHelper.gInstallOrigin,
+          progress: 0,
+          installState: "installed",
+          downloadAvailable: false,
+          downloading: false,
+          downloadSize: 0,
+          size: 0,
+          readyToApplyDownload: false
+        };
+        PackagedTestHelper.checkAppState(PackagedTestHelper.gApp, 3, expected,
+                                         true, false, PackagedTestHelper.next);
+      }
+
+    };
+
+    var request = navigator.mozApps.installPackage(miniManifestURL);
+    request.onerror = PackagedTestHelper.mozAppsError;
+    request.onsuccess = function() {
+      info("Application installed");
+    };
+  },
+  function() {
+    info("all done!\n");
     PackagedTestHelper.finish();
   }
 ];
 
 PackagedTestHelper.setSteps(steps);
 
 addLoadEvent(PackagedTestHelper.start);
 
--- a/dom/audiochannel/AudioChannelService.cpp
+++ b/dom/audiochannel/AudioChannelService.cpp
@@ -146,16 +146,23 @@ AudioChannelService::RegisterType(AudioC
   if (mDisabled) {
     return;
   }
 
   AudioChannelInternalType type = GetInternalType(aChannel, true);
   mChannelCounters[type].AppendElement(aChildID);
 
   if (XRE_GetProcessType() == GeckoProcessType_Default) {
+
+    // We must keep the childIds in order to decide which app is allowed to play
+    // with then telephony channel.
+    if (aChannel == AudioChannel::Telephony) {
+      RegisterTelephonyChild(aChildID);
+    }
+
     // Since there is another telephony registered, we can unregister old one
     // immediately.
     if (mDeferTelChannelTimer && aChannel == AudioChannel::Telephony) {
       mDeferTelChannelTimer->Cancel();
       mDeferTelChannelTimer = nullptr;
       UnregisterTypeInternal(aChannel, mTimerElementHidden, mTimerChildID,
                              false);
     }
@@ -229,25 +236,31 @@ AudioChannelService::UnregisterType(Audi
 {
   if (mDisabled) {
     return;
   }
 
   // There are two reasons to defer the decrease of telephony channel.
   // 1. User can have time to remove device from his ear before music resuming.
   // 2. Give BT SCO to be disconnected before starting to connect A2DP.
-  if (XRE_GetProcessType() == GeckoProcessType_Default &&
-      aChannel == AudioChannel::Telephony &&
-      (mChannelCounters[AUDIO_CHANNEL_INT_TELEPHONY_HIDDEN].Length() +
-       mChannelCounters[AUDIO_CHANNEL_INT_TELEPHONY].Length()) == 1) {
-    mTimerElementHidden = aElementHidden;
-    mTimerChildID = aChildID;
-    mDeferTelChannelTimer = do_CreateInstance("@mozilla.org/timer;1");
-    mDeferTelChannelTimer->InitWithCallback(this, 1500, nsITimer::TYPE_ONE_SHOT);
-    return;
+  if (XRE_GetProcessType() == GeckoProcessType_Default) {
+
+    if (aChannel == AudioChannel::Telephony) {
+      UnregisterTelephonyChild(aChildID);
+    }
+
+    if (aChannel == AudioChannel::Telephony &&
+        (mChannelCounters[AUDIO_CHANNEL_INT_TELEPHONY_HIDDEN].Length() +
+         mChannelCounters[AUDIO_CHANNEL_INT_TELEPHONY].Length()) == 1) {
+      mTimerElementHidden = aElementHidden;
+      mTimerChildID = aChildID;
+      mDeferTelChannelTimer = do_CreateInstance("@mozilla.org/timer;1");
+      mDeferTelChannelTimer->InitWithCallback(this, 1500, nsITimer::TYPE_ONE_SHOT);
+      return;
+    }
   }
 
   UnregisterTypeInternal(aChannel, aElementHidden, aChildID, aWithVideo);
 }
 
 void
 AudioChannelService::UnregisterTypeInternal(AudioChannel aChannel,
                                             bool aElementHidden,
@@ -353,17 +366,17 @@ AudioChannelService::GetStateInternal(Au
 
   SendAudioChannelChangedNotification(aChildID);
 
   // Let play any visible audio channel.
   if (!aElementHidden) {
     if (CheckVolumeFadedCondition(newType, aElementHidden)) {
       return AUDIO_CHANNEL_STATE_FADED;
     }
-    return AUDIO_CHANNEL_STATE_NORMAL;
+    return CheckTelephonyPolicy(aChannel, aChildID);
   }
 
   // We are not visible, maybe we have to mute.
   if (newType == AUDIO_CHANNEL_INT_NORMAL_HIDDEN ||
       (newType == AUDIO_CHANNEL_INT_CONTENT_HIDDEN &&
        // One process can have multiple content channels; and during the
        // transition from foreground to background, its content channels will be
        // updated with correct visibility status one by one. All its content
@@ -382,17 +395,44 @@ AudioChannelService::GetStateInternal(Au
   if (ChannelsActiveWithHigherPriorityThan(newType)) {
     MOZ_ASSERT(newType != AUDIO_CHANNEL_INT_NORMAL_HIDDEN);
     if (CheckVolumeFadedCondition(newType, aElementHidden)) {
       return AUDIO_CHANNEL_STATE_FADED;
     }
     return AUDIO_CHANNEL_STATE_MUTED;
   }
 
-  return AUDIO_CHANNEL_STATE_NORMAL;
+  return CheckTelephonyPolicy(aChannel, aChildID);
+}
+
+AudioChannelState
+AudioChannelService::CheckTelephonyPolicy(AudioChannel aChannel,
+                                          uint64_t aChildID)
+{
+  // Only the latest childID is allowed to play with telephony channel.
+  if (aChannel != AudioChannel::Telephony) {
+    return AUDIO_CHANNEL_STATE_NORMAL;
+  }
+
+  MOZ_ASSERT(!mTelephonyChildren.IsEmpty());
+
+#if DEBUG
+  bool found = false;
+  for (uint32_t i = 0, len = mTelephonyChildren.Length(); i < len; ++i) {
+    if (mTelephonyChildren[i].mChildID == aChildID) {
+      found = true;
+      break;
+    }
+  }
+
+  MOZ_ASSERT(found);
+#endif
+
+  return mTelephonyChildren.LastElement().mChildID == aChildID
+           ? AUDIO_CHANNEL_STATE_NORMAL : AUDIO_CHANNEL_STATE_MUTED;
 }
 
 bool
 AudioChannelService::CheckVolumeFadedCondition(AudioChannelInternalType aType,
                                                bool aElementHidden)
 {
   // Only normal & content channels are considered
   if (aType > AUDIO_CHANNEL_INT_CONTENT_HIDDEN) {
@@ -981,8 +1021,44 @@ AudioChannelService::GetDefaultAudioChan
     for (uint32_t i = 0; kMozAudioChannelAttributeTable[i].tag; ++i) {
       if (audioChannel.EqualsASCII(kMozAudioChannelAttributeTable[i].tag)) {
         aString = audioChannel;
         break;
       }
     }
   }
 }
+
+void
+AudioChannelService::RegisterTelephonyChild(uint64_t aChildID)
+{
+  for (uint32_t i = 0, len = mTelephonyChildren.Length(); i < len; ++i) {
+    if (mTelephonyChildren[i].mChildID == aChildID) {
+      ++mTelephonyChildren[i].mInstances;
+
+      if (i != len - 1) {
+        TelephonyChild child = mTelephonyChildren[i];
+        mTelephonyChildren.RemoveElementAt(i);
+        mTelephonyChildren.AppendElement(child);
+      }
+
+      return;
+    }
+  }
+
+  mTelephonyChildren.AppendElement(TelephonyChild(aChildID));
+}
+
+void
+AudioChannelService::UnregisterTelephonyChild(uint64_t aChildID)
+{
+  for (uint32_t i = 0, len = mTelephonyChildren.Length(); i < len; ++i) {
+    if (mTelephonyChildren[i].mChildID == aChildID) {
+      if (!--mTelephonyChildren[i].mInstances) {
+        mTelephonyChildren.RemoveElementAt(i);
+      }
+
+      return;
+    }
+  }
+
+  MOZ_ASSERT(false, "This should not happen.");
+}
--- a/dom/audiochannel/AudioChannelService.h
+++ b/dom/audiochannel/AudioChannelService.h
@@ -145,16 +145,21 @@ protected:
   /* Update the internal type value following the visibility changes */
   void UpdateChannelType(AudioChannel aChannel, uint64_t aChildID,
                          bool aElementHidden, bool aElementWasHidden);
 
   /* Send the default-volume-channel-changed notification */
   void SetDefaultVolumeControlChannelInternal(int32_t aChannel,
                                               bool aHidden, uint64_t aChildID);
 
+  AudioChannelState CheckTelephonyPolicy(AudioChannel aChannel,
+                                         uint64_t aChildID);
+  void RegisterTelephonyChild(uint64_t aChildID);
+  void UnregisterTelephonyChild(uint64_t aChildID);
+
   AudioChannelService();
   virtual ~AudioChannelService();
 
   enum AudioChannelInternalType {
     AUDIO_CHANNEL_INT_NORMAL = 0,
     AUDIO_CHANNEL_INT_NORMAL_HIDDEN,
     AUDIO_CHANNEL_INT_CONTENT,
     AUDIO_CHANNEL_INT_CONTENT_HIDDEN,
@@ -220,16 +225,29 @@ protected:
 #endif
   nsTArray<uint64_t> mChannelCounters[AUDIO_CHANNEL_INT_LAST];
 
   int32_t mCurrentHigherChannel;
   int32_t mCurrentVisibleHigherChannel;
 
   nsTArray<uint64_t> mWithVideoChildIDs;
 
+  // Telephony Channel policy is "LIFO", the last app to require the resource is
+  // allowed to play. The others are muted.
+  struct TelephonyChild {
+    uint64_t mChildID;
+    uint32_t mInstances;
+
+    explicit TelephonyChild(uint64_t aChildID)
+      : mChildID(aChildID)
+      , mInstances(1)
+    {}
+  };
+  nsTArray<TelephonyChild> mTelephonyChildren;
+
   // mPlayableHiddenContentChildID stores the ChildID of the process which can
   // play content channel(s) in the background.
   // A background process contained content channel(s) will become playable:
   //   1. When this background process registers its content channel(s) in
   //   AudioChannelService and there is no foreground process with registered
   //   content channel(s).
   //   2. When this process goes from foreground into background and there is
   //   no foreground process with registered content channel(s).
copy from dom/base/test/audio.ogg
copy to dom/audiochannel/tests/audio.ogg
new file mode 100644
--- /dev/null
+++ b/dom/audiochannel/tests/file_telephonyPolicy.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test Telephony Channel Policy</title>
+</head>
+<body>
+<div id="container"></div>
+  <script type="application/javascript;version=1.7">
+
+  var audio = new Audio();
+  audio.mozAudioChannelType = 'telephony';
+  audio.src = "audio.ogg";
+  audio.play();
+
+  </script>
+</body>
+</html>
--- a/dom/audiochannel/tests/mochitest.ini
+++ b/dom/audiochannel/tests/mochitest.ini
@@ -1,7 +1,11 @@
 [DEFAULT]
 support-files =
+  audio.ogg
   file_audio.html
+  file_telephonyPolicy.html
   AudioChannelChromeScript.js
 
+[test_telephonyPolicy.html]
+skip-if = (toolkit == 'gonk' || e10s)
 [test_audioChannelChange.html]
 skip-if = (toolkit != 'gonk')
new file mode 100644
--- /dev/null
+++ b/dom/audiochannel/tests/test_telephonyPolicy.html
@@ -0,0 +1,85 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test the Telephony Channel Policy</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<div id="container"></div>
+  <script type="application/javascript;version=1.7">
+
+function mainApp() {
+  var audio = new Audio();
+  audio.mozAudioChannelType = 'telephony';
+  audio.src = "audio.ogg";
+  audio.loop = true;
+  audio.play();
+
+  audio.addEventListener('mozinterruptbegin', function() {
+    ok(true, "This element has been muted!");
+  }, false);
+
+  audio.addEventListener('mozinterruptend', function() {
+    ok(true, "This element has been unmuted!");
+    audio.pause();
+    runTest();
+  }, false);
+
+  setTimeout(runTest, 600);
+}
+
+function newApp() {
+  var iframe = document.createElement('iframe');
+  iframe.setAttribute('mozbrowser', true);
+  // That needs to be an app.
+  iframe.setAttribute('mozapp', 'https://acertified.com/manifest.webapp');
+  iframe.src = "file_telephonyPolicy.html";
+  document.body.appendChild(iframe);
+}
+
+var tests = [
+  // Permissions
+  function() {
+    SpecialPowers.pushPermissions(
+      [{ "type": "browser", "allow": 1, "context": document },
+       { "type": "embed-apps", "allow": 1, "context": document },
+       { "type": "webapps-manage", "allow": 1, "context": document },
+       { "type": "audio-channel-telephony", "allow": 1, "context": document }], runTest);
+  },
+
+  // Preferences
+  function() {
+    SpecialPowers.pushPrefEnv({"set": [["dom.ipc.browser_frames.oop_by_default", true],
+                                       ["media.useAudioChannelService", true],
+                                       ["media.defaultAudioChannel", "telephony"],
+                                       ["dom.mozBrowserFramesEnabled", true],
+                                       ["network.disable.ipc.security", true]]}, runTest);
+  },
+
+  // Run 2 apps
+  mainApp,
+  newApp,
+];
+
+function runTest() {
+  if (!tests.length) {
+    finish();
+    return;
+  }
+
+  var test = tests.shift();
+  test();
+}
+
+function finish() {
+  SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+runTest();
+
+  </script>
+</body>
+</html>
--- a/dom/base/URL.cpp
+++ b/dom/base/URL.cpp
@@ -199,17 +199,17 @@ URL::RevokeObjectURL(const GlobalObject&
     if (window && window->GetExtantDoc()) {
       window->GetExtantDoc()->UnregisterHostObjectUri(asciiurl);
     }
     nsHostObjectProtocolHandler::RemoveDataEntry(asciiurl);
   }
 }
 
 void
-URL::GetHref(nsString& aHref) const
+URL::GetHref(nsString& aHref, ErrorResult& aRv) const
 {
   aHref.Truncate();
 
   nsAutoCString href;
   nsresult rv = mURI->GetSpec(href);
   if (NS_SUCCEEDED(rv)) {
     CopyUTF8toUTF16(href, aHref);
   }
@@ -235,35 +235,35 @@ URL::SetHref(const nsAString& aHref, Err
     return;
   }
 
   mURI = uri;
   UpdateURLSearchParams();
 }
 
 void
-URL::GetOrigin(nsString& aOrigin) const
+URL::GetOrigin(nsString& aOrigin, ErrorResult& aRv) const
 {
   nsContentUtils::GetUTFNonNullOrigin(mURI, aOrigin);
 }
 
 void
-URL::GetProtocol(nsString& aProtocol) const
+URL::GetProtocol(nsString& aProtocol, ErrorResult& aRv) const
 {
   nsCString protocol;
   if (NS_SUCCEEDED(mURI->GetScheme(protocol))) {
     aProtocol.Truncate();
   }
 
   CopyASCIItoUTF16(protocol, aProtocol);
   aProtocol.Append(char16_t(':'));
 }
 
 void
-URL::SetProtocol(const nsAString& aProtocol)
+URL::SetProtocol(const nsAString& aProtocol, ErrorResult& aRv)
 {
   nsAString::const_iterator start, end;
   aProtocol.BeginReading(start);
   aProtocol.EndReading(end);
   nsAString::const_iterator iter(start);
 
   FindCharInReadable(':', iter, end);
 
@@ -300,47 +300,47 @@ URL::SetProtocol(const nsAString& aProto
   value.Truncate();               \
   nsAutoCString tmp;              \
   nsresult rv = mURI->func(tmp);  \
   if (NS_SUCCEEDED(rv)) {         \
     CopyUTF8toUTF16(tmp, value);  \
   }
 
 void
-URL::GetUsername(nsString& aUsername) const
+URL::GetUsername(nsString& aUsername, ErrorResult& aRv) const
 {
   URL_GETTER(aUsername, GetUsername);
 }
 
 void
-URL::SetUsername(const nsAString& aUsername)
+URL::SetUsername(const nsAString& aUsername, ErrorResult& aRv)
 {
   mURI->SetUsername(NS_ConvertUTF16toUTF8(aUsername));
 }
 
 void
-URL::GetPassword(nsString& aPassword) const
+URL::GetPassword(nsString& aPassword, ErrorResult& aRv) const
 {
   URL_GETTER(aPassword, GetPassword);
 }
 
 void
-URL::SetPassword(const nsAString& aPassword)
+URL::SetPassword(const nsAString& aPassword, ErrorResult& aRv)
 {
   mURI->SetPassword(NS_ConvertUTF16toUTF8(aPassword));
 }
 
 void
-URL::GetHost(nsString& aHost) const
+URL::GetHost(nsString& aHost, ErrorResult& aRv) const
 {
   URL_GETTER(aHost, GetHostPort);
 }
 
 void
-URL::SetHost(const nsAString& aHost)
+URL::SetHost(const nsAString& aHost, ErrorResult& aRv)
 {
   mURI->SetHostPort(NS_ConvertUTF16toUTF8(aHost));
 }
 
 void
 URL::URLSearchParamsUpdated()
 {
   MOZ_ASSERT(mSearchParams);
@@ -365,56 +365,56 @@ URL::UpdateURLSearchParams()
       NS_WARNING("Failed to get the query from a nsIURL.");
     }
   }
 
   mSearchParams->ParseInput(search, this);
 }
 
 void
-URL::GetHostname(nsString& aHostname) const
+URL::GetHostname(nsString& aHostname, ErrorResult& aRv) const
 {
   aHostname.Truncate();
   nsAutoCString tmp;
   nsresult rv = mURI->GetHost(tmp);
   if (NS_SUCCEEDED(rv)) {
     if (tmp.FindChar(':') != -1) { // Escape IPv6 address
       MOZ_ASSERT(!tmp.Length() ||
         (tmp[0] !='[' && tmp[tmp.Length() - 1] != ']'));
       tmp.Insert('[', 0);
       tmp.Append(']');
     }
     CopyUTF8toUTF16(tmp, aHostname);
   }
 }
 
 void
-URL::SetHostname(const nsAString& aHostname)
+URL::SetHostname(const nsAString& aHostname, ErrorResult& aRv)
 {
   // nsStandardURL returns NS_ERROR_UNEXPECTED for an empty hostname
   // The return code is silently ignored
   mURI->SetHost(NS_ConvertUTF16toUTF8(aHostname));
 }
 
 void
-URL::GetPort(nsString& aPort) const
+URL::GetPort(nsString& aPort, ErrorResult& aRv) const
 {
   aPort.Truncate();
 
   int32_t port;
   nsresult rv = mURI->GetPort(&port);
   if (NS_SUCCEEDED(rv) && port != -1) {
     nsAutoString portStr;
     portStr.AppendInt(port, 10);
     aPort.Assign(portStr);
   }
 }
 
 void
-URL::SetPort(const nsAString& aPort)
+URL::SetPort(const nsAString& aPort, ErrorResult& aRv)
 {
   nsresult rv;
   nsAutoString portStr(aPort);
   int32_t port = -1;
 
   // nsIURI uses -1 as default value.
   if (!portStr.IsEmpty()) {
     port = portStr.ToInteger(&rv);
@@ -422,17 +422,17 @@ URL::SetPort(const nsAString& aPort)
       return;
     }
   }
 
   mURI->SetPort(port);
 }
 
 void
-URL::GetPathname(nsString& aPathname) const
+URL::GetPathname(nsString& aPathname, ErrorResult& aRv) const
 {
   aPathname.Truncate();
 
   nsCOMPtr<nsIURL> url(do_QueryInterface(mURI));
   if (!url) {
     // Do not throw!  Not having a valid URI or URL should result in an empty
     // string.
     return;
@@ -441,29 +441,29 @@ URL::GetPathname(nsString& aPathname) co
   nsAutoCString file;
   nsresult rv = url->GetFilePath(file);
   if (NS_SUCCEEDED(rv)) {
     CopyUTF8toUTF16(file, aPathname);
   }
 }
 
 void
-URL::SetPathname(const nsAString& aPathname)
+URL::SetPathname(const nsAString& aPathname, ErrorResult& aRv)
 {
   nsCOMPtr<nsIURL> url(do_QueryInterface(mURI));
   if (!url) {
     // Ignore failures to be compatible with NS4.
     return;
   }
 
   url->SetFilePath(NS_ConvertUTF16toUTF8(aPathname));
 }
 
 void
-URL::GetSearch(nsString& aSearch) const
+URL::GetSearch(nsString& aSearch, ErrorResult& aRv) const
 {
   aSearch.Truncate();
 
   nsCOMPtr<nsIURL> url(do_QueryInterface(mURI));
   if (!url) {
     // Do not throw!  Not having a valid URI or URL should result in an empty
     // string.
     return;
@@ -472,17 +472,17 @@ URL::GetSearch(nsString& aSearch) const
   nsAutoCString search;
   nsresult rv = url->GetQuery(search);
   if (NS_SUCCEEDED(rv) && !search.IsEmpty()) {
     CopyUTF8toUTF16(NS_LITERAL_CSTRING("?") + search, aSearch);
   }
 }
 
 void
-URL::SetSearch(const nsAString& aSearch)
+URL::SetSearch(const nsAString& aSearch, ErrorResult& aRv)
 {
   SetSearchInternal(aSearch);
   UpdateURLSearchParams();
 }
 
 void
 URL::SetSearchInternal(const nsAString& aSearch)
 {
@@ -514,31 +514,31 @@ URL::SetSearchParams(URLSearchParams& aS
   mSearchParams->AddObserver(this);
 
   nsAutoString search;
   mSearchParams->Serialize(search);
   SetSearchInternal(search);
 }
 
 void
-URL::GetHash(nsString& aHash) const
+URL::GetHash(nsString& aHash, ErrorResult& aRv) const
 {
   aHash.Truncate();
 
   nsAutoCString ref;
   nsresult rv = mURI->GetRef(ref);
   if (NS_SUCCEEDED(rv) && !ref.IsEmpty()) {
     NS_UnescapeURL(ref); // XXX may result in random non-ASCII bytes!
     aHash.Assign(char16_t('#'));
     AppendUTF8toUTF16(ref, aHash);
   }
 }
 
 void
-URL::SetHash(const nsAString& aHash)
+URL::SetHash(const nsAString& aHash, ErrorResult& aRv)
 {
   mURI->SetRef(NS_ConvertUTF16toUTF8(aHash));
 }
 
 bool IsChromeURI(nsIURI* aURI)
 {
   bool isChrome = false;
   if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &isChrome)))
--- a/dom/base/URL.h
+++ b/dom/base/URL.h
@@ -64,65 +64,65 @@ public:
   static void CreateObjectURL(const GlobalObject& aGlobal,
                               MediaSource& aSource,
                               const objectURLOptions& aOptions,
                               nsString& aResult,
                               ErrorResult& aError);
   static void RevokeObjectURL(const GlobalObject& aGlobal,
                               const nsAString& aURL);
 
-  void GetHref(nsString& aHref) const;
+  void GetHref(nsString& aHref, ErrorResult& aRv) const;
 
   void SetHref(const nsAString& aHref, ErrorResult& aRv);
 
-  void GetOrigin(nsString& aOrigin) const;
+  void GetOrigin(nsString& aOrigin, ErrorResult& aRv) const;
 
-  void GetProtocol(nsString& aProtocol) const;
+  void GetProtocol(nsString& aProtocol, ErrorResult& aRv) const;
 
-  void SetProtocol(const nsAString& aProtocol);
+  void SetProtocol(const nsAString& aProtocol, ErrorResult& aRv);
 
-  void GetUsername(nsString& aUsername) const;
+  void GetUsername(nsString& aUsername, ErrorResult& aRv) const;
 
-  void SetUsername(const nsAString& aUsername);
+  void SetUsername(const nsAString& aUsername, ErrorResult& aRv);
 
-  void GetPassword(nsString& aPassword) const;
+  void GetPassword(nsString& aPassword, ErrorResult& aRv) const;
 
-  void SetPassword(const nsAString& aPassword);
+  void SetPassword(const nsAString& aPassword, ErrorResult& aRv);
 
-  void GetHost(nsString& aHost) const;
+  void GetHost(nsString& aHost, ErrorResult& aRv) const;
 
-  void SetHost(const nsAString& aHost);
+  void SetHost(const nsAString& aHost, ErrorResult& aRv);
 
-  void GetHostname(nsString& aHostname) const;
+  void GetHostname(nsString& aHostname, ErrorResult& aRv) const;
 
-  void SetHostname(const nsAString& aHostname);
+  void SetHostname(const nsAString& aHostname, ErrorResult& aRv);
 
-  void GetPort(nsString& aPort) const;
+  void GetPort(nsString& aPort, ErrorResult& aRv) const;
 
-  void SetPort(const nsAString& aPort);
+  void SetPort(const nsAString& aPort, ErrorResult& aRv);
 
-  void GetPathname(nsString& aPathname) const;
+  void GetPathname(nsString& aPathname, ErrorResult& aRv) const;
 
-  void SetPathname(const nsAString& aPathname);
+  void SetPathname(const nsAString& aPathname, ErrorResult& aRv);
 
-  void GetSearch(nsString& aRetval) const;
+  void GetSearch(nsString& aRetval, ErrorResult& aRv) const;
 
-  void SetSearch(const nsAString& aArg);
+  void SetSearch(const nsAString& aArg, ErrorResult& aRv);
 
   URLSearchParams* SearchParams();
 
   void SetSearchParams(URLSearchParams& aSearchParams);
 
-  void GetHash(nsString& aRetval) const;
+  void GetHash(nsString& aRetval, ErrorResult& aRv) const;
 
-  void SetHash(const nsAString& aArg);
+  void SetHash(const nsAString& aArg, ErrorResult& aRv);
 
-  void Stringify(nsString& aRetval) const
+  void Stringify(nsString& aRetval, ErrorResult& aRv) const
   {
-    GetHref(aRetval);
+    GetHref(aRetval, aRv);
   }
 
   // URLSearchParamsObserver
   void URLSearchParamsUpdated() MOZ_OVERRIDE;
 
 private:
   nsIURI* GetURI() const
   {
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -9644,24 +9644,24 @@ nsGlobalWindow::GetPrivateRoot()
       }
     }
   }
 
   return static_cast<nsGlobalWindow*>(top.get());
 }
 
 
-nsIDOMLocation*
+nsLocation*
 nsGlobalWindow::GetLocation(ErrorResult& aError)
 {
   FORWARD_TO_INNER_OR_THROW(GetLocation, (aError), aError, nullptr);
 
   nsIDocShell *docShell = GetDocShell();
   if (!mLocation && docShell) {
-    mLocation = new nsLocation(docShell);
+    mLocation = new nsLocation(this, docShell);
   }
   return mLocation;
 }
 
 NS_IMETHODIMP
 nsGlobalWindow::GetLocation(nsIDOMLocation ** aLocation)
 {
   ErrorResult rv;
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -822,17 +822,17 @@ public:
   nsIDOMWindow* GetWindow(mozilla::ErrorResult& aError);
   nsIDOMWindow* GetSelf(mozilla::ErrorResult& aError);
   nsIDocument* GetDocument()
   {
     return GetDoc();
   }
   void GetName(nsAString& aName, mozilla::ErrorResult& aError);
   void SetName(const nsAString& aName, mozilla::ErrorResult& aError);
-  nsIDOMLocation* GetLocation(mozilla::ErrorResult& aError);
+  nsLocation* GetLocation(mozilla::ErrorResult& aError);
   nsHistory* GetHistory(mozilla::ErrorResult& aError);
   mozilla::dom::BarProp* GetLocationbar(mozilla::ErrorResult& aError);
   mozilla::dom::BarProp* GetMenubar(mozilla::ErrorResult& aError);
   mozilla::dom::BarProp* GetPersonalbar(mozilla::ErrorResult& aError);
   mozilla::dom::BarProp* GetScrollbars(mozilla::ErrorResult& aError);
   mozilla::dom::BarProp* GetStatusbar(mozilla::ErrorResult& aError);
   mozilla::dom::BarProp* GetToolbar(mozilla::ErrorResult& aError);
   void GetStatus(nsAString& aStatus, mozilla::ErrorResult& aError);
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -3074,51 +3074,42 @@ mozilla::dom::ShutdownJSEnvironment()
     NS_IF_RELEASE(sRuntimeService);
     NS_IF_RELEASE(sSecurityManager);
   }
 
   sShuttingDown = true;
   sDidShutdown = true;
 }
 
-class nsJSArgArray;
-
-namespace mozilla {
-template<>
-struct HasDangerousPublicDestructor<nsJSArgArray>
-{
-  static const bool value = true;
-};
-}
-
 // A fast-array class for JS.  This class supports both nsIJSScriptArray and
 // nsIArray.  If it is JS itself providing and consuming this class, all work
 // can be done via nsIJSScriptArray, and avoid the conversion of elements
 // to/from nsISupports.
 // When consumed by non-JS (eg, another script language), conversion is done
 // on-the-fly.
 class nsJSArgArray MOZ_FINAL : public nsIJSArgArray {
 public:
   nsJSArgArray(JSContext *aContext, uint32_t argc, JS::Value *argv,
                nsresult *prv);
-  ~nsJSArgArray();
+
   // nsISupports
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsJSArgArray,
                                                          nsIJSArgArray)
 
   // nsIArray
   NS_DECL_NSIARRAY
 
   // nsIJSArgArray
   nsresult GetArgs(uint32_t *argc, void **argv);
 
   void ReleaseJSObjects();
 
 protected:
+  ~nsJSArgArray();
   JSContext *mContext;
   JS::Heap<JS::Value> *mArgv;
   uint32_t mArgc;
 };
 
 nsJSArgArray::nsJSArgArray(JSContext *aContext, uint32_t argc, JS::Value *argv,
                            nsresult *prv) :
     mContext(aContext),
@@ -3238,18 +3229,16 @@ NS_IMETHODIMP nsJSArgArray::Enumerate(ns
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 // The factory function
 nsresult NS_CreateJSArgv(JSContext *aContext, uint32_t argc, void *argv,
                          nsIJSArgArray **aArray)
 {
   nsresult rv;
-  nsJSArgArray *ret = new nsJSArgArray(aContext, argc,
-                                       static_cast<JS::Value *>(argv), &rv);
-  if (ret == nullptr)
-    return NS_ERROR_OUT_OF_MEMORY;
+  nsCOMPtr<nsIJSArgArray> ret = new nsJSArgArray(aContext, argc,
+                                                static_cast<JS::Value *>(argv), &rv);
   if (NS_FAILED(rv)) {
-    delete ret;
     return rv;
   }
-  return ret->QueryInterface(NS_GET_IID(nsIArray), (void **)aArray);
+  ret.forget(aArray);
+  return NS_OK;
 }
--- a/dom/base/nsJSTimeoutHandler.cpp
+++ b/dom/base/nsJSTimeoutHandler.cpp
@@ -344,18 +344,18 @@ nsJSScriptTimeoutHandler::Init(nsGlobalW
     // if CSP is enabled, and setTimeout/setInterval was called with a string,
     // disable the registration and log an error
     ErrorResult error;
     *aAllowEval = CheckCSPForEval(cx, aWindow, error);
     if (error.Failed() || !*aAllowEval) {
       return error.ErrorCode();
     }
 
-    mExpr.Append(JS_GetFlatStringChars(expr),
-                 JS_GetStringLength(JS_FORGET_STRING_FLATNESS(expr)));
+    MOZ_ASSERT(mExpr.IsEmpty());
+    AssignJSFlatString(mExpr, expr);
 
     // Get the calling location.
     const char *filename;
     if (nsJSUtils::GetCallingLocation(cx, &filename, &mLineNo)) {
       mFileName.Assign(filename);
     }
   } else if (funobj) {
     *aAllowEval = true;
--- a/dom/base/nsJSUtils.h
+++ b/dom/base/nsJSUtils.h
@@ -152,16 +152,28 @@ AssignJSString(JSContext *cx, T &dest, J
   if (MOZ_UNLIKELY(!js::CopyStringChars(cx, dest.BeginWriting(), s, len))) {
     return false;
   }
   dest.BeginWriting()[len] = '\0';
   dest.SetLength(len);
   return true;
 }
 
+inline void
+AssignJSFlatString(nsAString &dest, JSFlatString *s)
+{
+  size_t len = js::GetFlatStringLength(s);
+  static_assert(js::MaxStringLength < (1 << 28),
+                "Shouldn't overflow here or in SetCapacity");
+  dest.SetCapacity(len + 1);
+  js::CopyFlatStringChars(dest.BeginWriting(), s, len);
+  dest.BeginWriting()[len] = '\0';
+  dest.SetLength(len);
+}
+
 class nsAutoJSString : public nsAutoString
 {
 public:
 
   /**
    * nsAutoJSString should be default constructed, which leaves it empty
    * (this->IsEmpty()), and initialized with one of the init() methods below.
    */
--- a/dom/base/nsLocation.cpp
+++ b/dom/base/nsLocation.cpp
@@ -27,16 +27,20 @@
 #include "nsReadableUtils.h"
 #include "nsITextToSubURI.h"
 #include "nsJSUtils.h"
 #include "nsContentUtils.h"
 #include "mozilla/Likely.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsNullPrincipal.h"
 #include "ScriptSettings.h"
+#include "mozilla/dom/LocationBinding.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
 
 static nsresult
 GetDocumentCharacterSetForURI(const nsAString& aHref, nsACString& aCharset)
 {
   aCharset.Truncate();
 
   JSContext *cx = nsContentUtils::GetCurrentJSContext();
   if (cx) {
@@ -47,40 +51,41 @@ GetDocumentCharacterSetForURI(const nsAS
     if (nsIDocument* doc = window->GetDoc()) {
       aCharset = doc->GetDocumentCharacterSet();
     }
   }
 
   return NS_OK;
 }
 
-nsLocation::nsLocation(nsIDocShell *aDocShell)
+nsLocation::nsLocation(nsPIDOMWindow* aWindow, nsIDocShell *aDocShell)
+  : mInnerWindow(aWindow)
 {
   MOZ_ASSERT(aDocShell);
+  MOZ_ASSERT(mInnerWindow->IsInnerWindow());
+  SetIsDOMBinding();
 
   mDocShell = do_GetWeakReference(aDocShell);
-  nsCOMPtr<nsIDOMWindow> outer = aDocShell->GetWindow();
-  mOuter = do_GetWeakReference(outer);
 }
 
 nsLocation::~nsLocation()
 {
 }
 
 DOMCI_DATA(Location, nsLocation)
 
 // QueryInterface implementation for nsLocation
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsLocation)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsIDOMLocation)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Location)
 NS_INTERFACE_MAP_END
 
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(nsLocation)
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsLocation, mInnerWindow)
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsLocation)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsLocation)
 
 void
 nsLocation::SetDocShell(nsIDocShell *aDocShell)
 {
    mDocShell = do_GetWeakReference(aDocShell);
 }
@@ -724,16 +729,112 @@ nsLocation::SetProtocol(const nsAString&
   rv = uri->SetScheme(NS_ConvertUTF16toUTF8(aProtocol));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return SetURI(uri);
 }
 
+void
+nsLocation::GetUsername(nsAString& aUsername, ErrorResult& aError)
+{
+  if (!CallerSubsumes()) {
+    aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
+    return;
+  }
+
+  aUsername.Truncate();
+  nsCOMPtr<nsIURI> uri;
+  nsresult result = GetURI(getter_AddRefs(uri));
+  if (uri) {
+    nsAutoCString username;
+    result = uri->GetUsername(username);
+    if (NS_SUCCEEDED(result)) {
+      CopyUTF8toUTF16(username, aUsername);
+    }
+  }
+}
+
+void
+nsLocation::SetUsername(const nsAString& aUsername, ErrorResult& aError)
+{
+  if (!CallerSubsumes()) {
+    aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
+    return;
+  }
+
+  nsCOMPtr<nsIURI> uri;
+  nsresult rv = GetWritableURI(getter_AddRefs(uri));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    aError.Throw(rv);
+    return;
+  }
+
+  if (!uri) {
+    return;
+  }
+
+  rv = uri->SetUsername(NS_ConvertUTF16toUTF8(aUsername));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    aError.Throw(rv);
+    return;
+  }
+
+  rv = SetURI(uri);
+}
+
+void
+nsLocation::GetPassword(nsAString& aPassword, ErrorResult& aError)
+{
+  if (!CallerSubsumes()) {
+    aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
+    return;
+  }
+
+  aPassword.Truncate();
+  nsCOMPtr<nsIURI> uri;
+  nsresult result = GetURI(getter_AddRefs(uri));
+  if (uri) {
+    nsAutoCString password;
+    result = uri->GetPassword(password);
+    if (NS_SUCCEEDED(result)) {
+      CopyUTF8toUTF16(password, aPassword);
+    }
+  }
+}
+
+void
+nsLocation::SetPassword(const nsAString& aPassword, ErrorResult& aError)
+{
+  if (!CallerSubsumes()) {
+    aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
+    return;
+  }
+
+  nsCOMPtr<nsIURI> uri;
+  nsresult rv = GetWritableURI(getter_AddRefs(uri));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    aError.Throw(rv);
+    return;
+  }
+
+  if (!uri) {
+    return;
+  }
+
+  rv = uri->SetPassword(NS_ConvertUTF16toUTF8(aPassword));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    aError.Throw(rv);
+    return;
+  }
+
+  rv = SetURI(uri);
+}
+
 NS_IMETHODIMP
 nsLocation::GetSearch(nsAString& aSearch)
 {
   if (!CallerSubsumes())
     return NS_ERROR_DOM_SECURITY_ERR;
 
   aSearch.SetLength(0);
 
@@ -913,18 +1014,28 @@ nsLocation::GetSourceBaseURL(JSContext* 
   NS_ENSURE_TRUE(doc, NS_OK);
   *sourceURL = doc->GetBaseURI().take();
   return NS_OK;
 }
 
 bool
 nsLocation::CallerSubsumes()
 {
-  // Get the principal associated with the location object.
-  nsCOMPtr<nsIDOMWindow> outer = do_QueryReferent(mOuter);
+  // Get the principal associated with the location object.  Note that this is
+  // the principal of the page which will actually be navigated, not the
+  // principal of the Location object itself.  This is why we need this check
+  // even though we only allow limited cross-origin access to Location objects
+  // in general.
+  nsCOMPtr<nsIDOMWindow> outer = mInnerWindow->GetOuterWindow();
   if (MOZ_UNLIKELY(!outer))
     return false;
   nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(outer);
   bool subsumes = false;
   nsresult rv = nsContentUtils::SubjectPrincipal()->SubsumesConsideringDomain(sop->GetPrincipal(), &subsumes);
   NS_ENSURE_SUCCESS(rv, false);
   return subsumes;
 }
+
+JSObject*
+nsLocation::WrapObject(JSContext* aCx)
+{
+  return LocationBinding::Wrap(aCx, this);
+}
--- a/dom/base/nsLocation.h
+++ b/dom/base/nsLocation.h
@@ -8,40 +8,139 @@
 #define nsLocation_h__
 
 #include "nsIDOMLocation.h"
 #include "nsString.h"
 #include "nsIWeakReferenceUtils.h"
 #include "nsWrapperCache.h"
 #include "nsCycleCollectionParticipant.h"
 #include "js/TypeDecls.h"
+#include "mozilla/ErrorResult.h"
+#include "nsPIDOMWindow.h"
 
 class nsIURI;
 class nsIDocShell;
 class nsIDocShellLoadInfo;
 
 //*****************************************************************************
 // nsLocation: Script "location" object
 //*****************************************************************************
 
-class nsLocation : public nsIDOMLocation
-                 , public nsWrapperCache
+class nsLocation MOZ_FINAL : public nsIDOMLocation
+                           , public nsWrapperCache
 {
+  typedef mozilla::ErrorResult ErrorResult;
+
 public:
-  nsLocation(nsIDocShell *aDocShell);
+  nsLocation(nsPIDOMWindow* aWindow, nsIDocShell *aDocShell);
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsLocation)
 
   void SetDocShell(nsIDocShell *aDocShell);
   nsIDocShell *GetDocShell();
 
   // nsIDOMLocation
   NS_DECL_NSIDOMLOCATION
 
+  // WebIDL API:
+  void Assign(const nsAString& aUrl, ErrorResult& aError)
+  {
+    aError = Assign(aUrl);
+  }
+  void Replace(const nsAString& aUrl, ErrorResult& aError)
+  {
+    aError = Replace(aUrl);
+  }
+  void Reload(bool aForceget, ErrorResult& aError)
+  {
+    aError = Reload(aForceget);
+  }
+  void GetHref(nsAString& aHref, ErrorResult& aError)
+  {
+    aError = GetHref(aHref);
+  }
+  void SetHref(const nsAString& aHref, ErrorResult& aError)
+  {
+    aError = SetHref(aHref);
+  }
+  void GetOrigin(nsAString& aOrigin, ErrorResult& aError)
+  {
+    aError = GetOrigin(aOrigin);
+  }
+  void GetProtocol(nsAString& aProtocol, ErrorResult& aError)
+  {
+    aError = GetProtocol(aProtocol);
+  }
+  void SetProtocol(const nsAString& aProtocol, ErrorResult& aError)
+  {
+    aError = SetProtocol(aProtocol);
+  }
+  void GetUsername(nsAString& aUsername, ErrorResult& aError);
+  void SetUsername(const nsAString& aUsername, ErrorResult& aError);
+  void GetPassword(nsAString& aPassword, ErrorResult& aError);
+  void SetPassword(const nsAString& aPassword, ErrorResult& aError);
+  void GetHost(nsAString& aHost, ErrorResult& aError)
+  {
+    aError = GetHost(aHost);
+  }
+  void SetHost(const nsAString& aHost, ErrorResult& aError)
+  {
+    aError = SetHost(aHost);
+  }
+  void GetHostname(nsAString& aHostname, ErrorResult& aError)
+  {
+    aError = GetHostname(aHostname);
+  }
+  void SetHostname(const nsAString& aHostname, ErrorResult& aError)
+  {
+    aError = SetHostname(aHostname);
+  }
+  void GetPort(nsAString& aPort, ErrorResult& aError)
+  {
+    aError = GetPort(aPort);
+  }
+  void SetPort(const nsAString& aPort, ErrorResult& aError)
+  {
+    aError = SetPort(aPort);
+  }
+  void GetPathname(nsAString& aPathname, ErrorResult& aError)
+  {
+    aError = GetPathname(aPathname);
+  }
+  void SetPathname(const nsAString& aPathname, ErrorResult& aError)
+  {
+    aError = SetPathname(aPathname);
+  }
+  void GetSearch(nsAString& aSeach, ErrorResult& aError)
+  {
+    aError = GetSearch(aSeach);
+  }
+  void SetSearch(const nsAString& aSeach, ErrorResult& aError)
+  {
+    aError = SetSearch(aSeach);
+  }
+  void GetHash(nsAString& aHash, ErrorResult& aError)
+  {
+    aError = GetHash(aHash);
+  }
+  void SetHash(const nsAString& aHash, ErrorResult& aError)
+  {
+    aError = SetHash(aHash);
+  }
+  void Stringify(nsAString& aRetval, ErrorResult& aError)
+  {
+    GetHref(aRetval, aError);
+  }
+  nsPIDOMWindow* GetParentObject() const
+  {
+    return mInnerWindow;
+  }
+  virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
+
 protected:
   virtual ~nsLocation();
 
   // In the case of jar: uris, we sometimes want the place the jar was
   // fetched from as the URI instead of the jar: uri itself.  Pass in
   // true for aGetInnermostURI when that's the case.
   nsresult GetURI(nsIURI** aURL, bool aGetInnermostURI = false);
   nsresult GetWritableURI(nsIURI** aURL);
@@ -51,14 +150,14 @@ protected:
   nsresult SetHrefWithContext(JSContext* cx, const nsAString& aHref,
                               bool aReplace);
 
   nsresult GetSourceBaseURL(JSContext* cx, nsIURI** sourceURL);
   nsresult CheckURL(nsIURI *url, nsIDocShellLoadInfo** aLoadInfo);
   bool CallerSubsumes();
 
   nsString mCachedHash;
+  nsCOMPtr<nsPIDOMWindow> mInnerWindow;
   nsWeakPtr mDocShell;
-  nsWeakPtr mOuter;
 };
 
 #endif // nsLocation_h__
 
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -81,20 +81,23 @@ ThrowInvalidThis(JSContext* aCx, const J
   // This should only be called for DOM methods/getters/setters, which
   // are JSNative-backed functions, so we can assume that
   // JS_ValueToFunction and JS_GetFunctionDisplayId will both return
   // non-null and that JS_GetStringCharsZ returns non-null.
   JS::Rooted<JSFunction*> func(aCx, JS_ValueToFunction(aCx, aArgs.calleev()));
   MOZ_ASSERT(func);
   JS::Rooted<JSString*> funcName(aCx, JS_GetFunctionDisplayId(func));
   MOZ_ASSERT(funcName);
+  nsAutoJSString funcNameStr;
+  if (!funcNameStr.init(aCx, funcName)) {
+    return false;
+  }
   JS_ReportErrorNumberUC(aCx, GetErrorMessage, nullptr,
                          static_cast<const unsigned>(aErrorNumber),
-                         JS_GetStringCharsZ(aCx, funcName),
-                         ifaceName.get());
+                         funcNameStr.get(), ifaceName.get());
   return false;
 }
 
 bool
 ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs,
                  const ErrNum aErrorNumber,
                  prototypes::ID aProtoId)
 {
@@ -905,16 +908,24 @@ GetInterfaceImpl(JSContext* aCx, nsIInte
   }
 
   if (!WrapObject(aCx, result, iid, aRetval)) {
     aError.Throw(NS_ERROR_FAILURE);
   }
 }
 
 bool
+UnforgeableValueOf(JSContext* cx, unsigned argc, JS::Value* vp)
+{
+  JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+  args.rval().set(args.thisv());
+  return true;
+}
+
+bool
 ThrowingConstructor(JSContext* cx, unsigned argc, JS::Value* vp)
 {
   return ThrowErrorMessage(cx, MSG_ILLEGAL_CONSTRUCTOR);
 }
 
 bool
 ThrowConstructorWithoutNew(JSContext* cx, const char* name)
 {
@@ -1048,28 +1059,101 @@ XrayResolveAttribute(JSContext* cx, JS::
           return true;
         }
       }
     }
   }
   return true;
 }
 
+static bool
+XrayResolveMethod(JSContext* cx, JS::Handle<JSObject*> wrapper,
+                  JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
+                  const Prefable<const JSFunctionSpec>* methods,
+                  jsid* methodIds,
+                  const JSFunctionSpec* methodsSpecs,
+                  JS::MutableHandle<JSPropertyDescriptor> desc)
+{
+  const Prefable<const JSFunctionSpec>* method;
+  for (method = methods; method->specs; ++method) {
+    if (method->isEnabled(cx, obj)) {
+      // Set i to be the index into our full list of ids/specs that we're
+      // looking at now.
+      size_t i = method->specs - methodsSpecs;
+      for ( ; methodIds[i] != JSID_VOID; ++i) {
+        if (id == methodIds[i]) {
+          const JSFunctionSpec& methodSpec = methodsSpecs[i];
+          JSFunction *fun;
+          if (methodSpec.selfHostedName) {
+            fun = JS::GetSelfHostedFunction(cx, methodSpec.selfHostedName, id, methodSpec.nargs);
+            if (!fun) {
+              return false;
+            }
+            MOZ_ASSERT(!methodSpec.call.op, "Bad FunctionSpec declaration: non-null native");
+            MOZ_ASSERT(!methodSpec.call.info, "Bad FunctionSpec declaration: non-null jitinfo");
+          } else {
+            fun = JS_NewFunctionById(cx, methodSpec.call.op, methodSpec.nargs, 0, wrapper, id);
+            if (!fun) {
+              return false;
+            }
+            SET_JITINFO(fun, methodSpec.call.info);
+          }
+          JSObject *funobj = JS_GetFunctionObject(fun);
+          desc.value().setObject(*funobj);
+          desc.setAttributes(methodSpec.flags);
+          desc.object().set(wrapper);
+          desc.setSetter(nullptr);
+          desc.setGetter(nullptr);
+          return true;
+        }
+      }
+    }
+  }
+  return true;
+}
+
 /* static */ bool
 XrayResolveUnforgeableProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
                                JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
                                JS::MutableHandle<JSPropertyDescriptor> desc,
                                const NativeProperties* nativeProperties)
 {
-  return !nativeProperties || !nativeProperties->unforgeableAttributes ||
-         XrayResolveAttribute(cx, wrapper, obj, id,
+  if (!nativeProperties) {
+    return true;
+  }
+
+  if (nativeProperties->unforgeableAttributes) {
+    if (!XrayResolveAttribute(cx, wrapper, obj, id,
                               nativeProperties->unforgeableAttributes,
                               nativeProperties->unforgeableAttributeIds,
                               nativeProperties->unforgeableAttributeSpecs,
-                              desc);
+                              desc)) {
+      return false;
+    }
+
+    if (desc.object()) {
+      return true;
+    }
+  }
+
+  if (nativeProperties->unforgeableMethods) {
+    if (!XrayResolveMethod(cx, wrapper, obj, id,
+                           nativeProperties->unforgeableMethods,
+                           nativeProperties->unforgeableMethodIds,
+                           nativeProperties->unforgeableMethodSpecs,
+                           desc)) {
+      return false;
+    }
+
+    if (desc.object()) {
+      return true;
+    }
+  }
+
+  return true;
 }
 
 static bool
 XrayResolveProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
                     JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
                     JS::MutableHandle<JSPropertyDescriptor> desc, DOMObjectType type,
                     const NativeProperties* nativeProperties)
 {
@@ -1081,50 +1165,22 @@ XrayResolveProperty(JSContext* cx, JS::H
     methodIds = nativeProperties->staticMethodIds;
     methodsSpecs = nativeProperties->staticMethodsSpecs;
   } else {
     methods = nativeProperties->methods;
     methodIds = nativeProperties->methodIds;
     methodsSpecs = nativeProperties->methodsSpecs;
   }
   if (methods) {
-    const Prefable<const JSFunctionSpec>* method;
-    for (method = methods; method->specs; ++method) {
-      if (method->isEnabled(cx, obj)) {
-        // Set i to be the index into our full list of ids/specs that we're
-        // looking at now.
-        size_t i = method->specs - methodsSpecs;
-        for ( ; methodIds[i] != JSID_VOID; ++i) {
-          if (id == methodIds[i]) {
-            const JSFunctionSpec& methodSpec = methodsSpecs[i];
-            JSFunction *fun;
-            if (methodSpec.selfHostedName) {
-              fun = JS::GetSelfHostedFunction(cx, methodSpec.selfHostedName, id, methodSpec.nargs);
-              if (!fun) {
-                return false;
-              }
-              MOZ_ASSERT(!methodSpec.call.op, "Bad FunctionSpec declaration: non-null native");
-              MOZ_ASSERT(!methodSpec.call.info, "Bad FunctionSpec declaration: non-null jitinfo");
-            } else {
-              fun = JS_NewFunctionById(cx, methodSpec.call.op, methodSpec.nargs, 0, wrapper, id);
-              if (!fun) {
-                return false;
-              }
-              SET_JITINFO(fun, methodSpec.call.info);
-            }
-            JSObject *funobj = JS_GetFunctionObject(fun);
-            desc.value().setObject(*funobj);
-            desc.setAttributes(methodSpec.flags);
-            desc.object().set(wrapper);
-            desc.setSetter(nullptr);
-            desc.setGetter(nullptr);
-           return true;
-          }
-        }
-      }
+    if (!XrayResolveMethod(cx, wrapper, obj, id, methods, methodIds,
+                           methodsSpecs, desc)) {
+      return false;
+    }
+    if (desc.object()) {
+      return true;
     }
   }
 
   if (type == eInterface) {
     if (nativeProperties->staticAttributes) {
       if (!XrayResolveAttribute(cx, wrapper, obj, id,
                                 nativeProperties->staticAttributes,
                                 nativeProperties->staticAttributeIds,
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -1696,16 +1696,19 @@ template<class T>
 void
 GetInterface(JSContext* aCx, T* aThis, nsIJSID* aIID,
              JS::MutableHandle<JS::Value> aRetval, ErrorResult& aError)
 {
   GetInterfaceImpl(aCx, aThis, aThis, aIID, aRetval, aError);
 }
 
 bool
+UnforgeableValueOf(JSContext* cx, unsigned argc, JS::Value* vp);
+
+bool
 ThrowingConstructor(JSContext* cx, unsigned argc, JS::Value* vp);
 
 bool
 ThrowConstructorWithoutNew(JSContext* cx, const char* name);
 
 // vp is allowed to be null; in that case no get will be attempted,
 // and *found will simply indicate whether the property exists.
 bool
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -737,21 +737,17 @@ DOMInterfaces = {
 },
 
 'LocalMediaStream': {
     'headerFile': 'DOMMediaStream.h',
     'nativeType': 'mozilla::DOMLocalMediaStream'
 },
 
 'Location': {
-    # NOTE: Before you turn on codegen for Location, make sure all the
-    # Unforgeable stuff is dealt with.
-    'nativeType': 'nsIDOMLocation',
-    'skipGen': True,
-    'register': False
+    'nativeType': 'nsLocation',
 },
 
 'MediaList': {
     'nativeType': 'nsMediaList',
     'headerFile': 'nsIMediaList.h',
 },
 
 'MediaSource': [{
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -484,20 +484,32 @@ def PrototypeIDAndDepth(descriptor):
             prototypeID += "_workers"
         depth = "PrototypeTraits<%s>::Depth" % prototypeID
     else:
         prototypeID += "_ID_Count"
         depth = "0"
     return (prototypeID, depth)
 
 
+def MemberIsUnforgeable(member, descriptor):
+    # Note: "or" and "and" return either their LHS or RHS, not
+    # necessarily booleans.  Make sure to return a boolean from this
+    # method, because callers will compare its return value to
+    # booleans.
+    return bool((member.isAttr() or member.isMethod()) and
+                not member.isStatic() and
+                (member.isUnforgeable() or
+                 descriptor.interface.getExtendedAttribute("Unforgeable")))
+
+
 def UseHolderForUnforgeable(descriptor):
     return (descriptor.concrete and
             descriptor.proxy and
-            any(m for m in descriptor.interface.members if (m.isAttr() or m.isMethod()) and m.isUnforgeable()))
+            any(m for m in descriptor.interface.members
+                if MemberIsUnforgeable(m, descriptor)))
 
 
 def CallOnUnforgeableHolder(descriptor, code, isXrayCheck=None,
                             useSharedRoot=False):
     """
     Generate the code to execute the code in "code" on an unforgeable holder if
     needed. code should be a string containing the code to execute. If it
     contains a ${holder} string parameter it will be replaced with the
@@ -1994,17 +2006,17 @@ class MethodDefiner(PropertyDefiner):
         # FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=772822
         #       We should be able to check for special operations without an
         #       identifier. For now we check if the name starts with __
 
         # Ignore non-static methods for interfaces without a proto object
         if descriptor.interface.hasInterfacePrototypeObject() or static:
             methods = [m for m in descriptor.interface.members if
                        m.isMethod() and m.isStatic() == static and
-                       m.isUnforgeable() == unforgeable and
+                       MemberIsUnforgeable(m, descriptor) == unforgeable and
                        not m.isIdentifierLess()]
         else:
             methods = []
         self.chrome = []
         self.regular = []
         for m in methods:
             if m.identifier.name == 'queryInterface':
                 if self.descriptor.workers:
@@ -2067,17 +2079,18 @@ class MethodDefiner(PropertyDefiner):
                 "selfHostedName": "ArrayValues",
                 "length": 0,
                 "flags": "JSPROP_ENUMERATE",
                 "condition": MemberCondition(None, None)
             })
 
         if not static:
             stringifier = descriptor.operations['Stringifier']
-            if stringifier:
+            if (stringifier and
+                unforgeable == MemberIsUnforgeable(stringifier, descriptor)):
                 toStringDesc = {
                     "name": "toString",
                     "nativeName": stringifier.identifier.name,
                     "length": 0,
                     "flags": "JSPROP_ENUMERATE",
                     "condition": PropertyDefiner.getControllingCondition(stringifier, descriptor)
                 }
                 if isChromeOnly(stringifier):
@@ -2092,16 +2105,28 @@ class MethodDefiner(PropertyDefiner):
                     "length": 0,
                     "flags": "JSPROP_ENUMERATE",
                     "condition": PropertyDefiner.getControllingCondition(jsonifier, descriptor)
                 }
                 if isChromeOnly(jsonifier):
                     self.chrome.append(toJSONDesc)
                 else:
                     self.regular.append(toJSONDesc)
+            if (unforgeable and
+                descriptor.interface.getExtendedAttribute("Unforgeable")):
+                # Synthesize our valueOf method
+                self.regular.append({
+                    "name": 'valueOf',
+                    "nativeName": "UnforgeableValueOf",
+                    "methodInfo": False,
+                    "length": 0,
+                    "flags": "JSPROP_ENUMERATE", # readonly/permanent added
+                                                 # automatically.
+                    "condition": MemberCondition(None, None)
+                })
         elif (descriptor.interface.isJSImplemented() and
               descriptor.interface.hasInterfaceObject()):
             self.chrome.append({
                 "name": '_create',
                 "nativeName": ("%s::_Create" % descriptor.name),
                 "methodInfo": False,
                 "length": 2,
                 "flags": "0",
@@ -2122,17 +2147,17 @@ class MethodDefiner(PropertyDefiner):
     def generateArray(self, array, name, doIdArrays):
         if len(array) == 0:
             return ""
 
         def condition(m, d):
             return m["condition"]
 
         def flags(m):
-            unforgeable = " | JSPROP_PERMANENT" if self.unforgeable else ""
+            unforgeable = " | JSPROP_PERMANENT | JSPROP_READONLY" if self.unforgeable else ""
             return m["flags"] + unforgeable
 
         def specData(m):
             if "selfHostedName" in m:
                 selfHostedName = '"%s"' % m["selfHostedName"]
                 assert not m.get("methodInfo", True)
                 accessor = "nullptr"
                 jitinfo = "nullptr"
@@ -2170,26 +2195,43 @@ class MethodDefiner(PropertyDefiner):
         return self.generatePrefableArray(
             array, name,
             '  JS_FNSPEC("%s", %s, %s, %s, %s, %s)',
             '  JS_FS_END',
             'JSFunctionSpec',
             condition, specData, doIdArrays)
 
 
+def IsCrossOriginWritable(attr, descriptor):
+    """
+    Return whether the IDLAttribute in question is cross-origin writable on the
+    interface represented by descriptor.  This is needed to handle the fact that
+    some, but not all, interfaces implementing URLUtils want a cross-origin
+    writable .href.
+    """
+    crossOriginWritable = attr.getExtendedAttribute("CrossOriginWritable")
+    if not crossOriginWritable:
+        return False
+    if crossOriginWritable == True:
+        return True
+    assert (isinstance(crossOriginWritable, list) and
+            len(crossOriginWritable) == 1)
+    return crossOriginWritable[0] == descriptor.interface.identifier.name
+
+
 class AttrDefiner(PropertyDefiner):
     def __init__(self, descriptor, name, static, unforgeable=False):
         assert not (static and unforgeable)
         PropertyDefiner.__init__(self, descriptor, name)
         self.name = name
         # Ignore non-static attributes for interfaces without a proto object
         if descriptor.interface.hasInterfacePrototypeObject() or static:
             attributes = [m for m in descriptor.interface.members if
                           m.isAttr() and m.isStatic() == static and
-                          m.isUnforgeable() == unforgeable]
+                          MemberIsUnforgeable(m, descriptor) == unforgeable]
         else:
             attributes = []
         self.chrome = [m for m in attributes if isChromeOnly(m)]
         self.regular = [m for m in attributes if not isChromeOnly(m)]
         self.static = static
         self.unforgeable = unforgeable
 
         if static:
@@ -2233,17 +2275,17 @@ class AttrDefiner(PropertyDefiner):
                 attr.getExtendedAttribute("Replaceable") is None):
                 return "JSOP_NULLWRAPPER"
             if self.static:
                 accessor = 'set_' + attr.identifier.name
                 jitinfo = "nullptr"
             else:
                 if attr.hasLenientThis():
                     accessor = "genericLenientSetter"
-                elif attr.getExtendedAttribute("CrossOriginWritable"):
+                elif IsCrossOriginWritable(attr, self.descriptor):
                     accessor = "genericCrossOriginSetter"
                 elif self.descriptor.needsSpecialGenericOps():
                     accessor = "genericSetter"
                 else:
                     accessor = "GenericBindingSetter"
                 jitinfo = "&%s_setterinfo" % attr.identifier.name
             return "{ { JS_CAST_NATIVE_TO(%s, JSStrictPropertyOp), %s } }" % \
                    (accessor, jitinfo)
@@ -2781,45 +2823,63 @@ def CreateBindingJSObject(descriptor, pr
 
 
 def InitUnforgeablePropertiesOnObject(descriptor, obj, properties, failureReturnValue=""):
     """
     properties is a PropertyArrays instance
     """
     unforgeables = []
 
+    if failureReturnValue:
+        failureReturnValue = " " + failureReturnValue
+    else:
+        failureReturnValue = ""
+
     defineUnforgeableAttrs = fill(
         """
         if (!DefineUnforgeableAttributes(aCx, ${obj}, %s)) {
           return${rv};
         }
         """,
         obj=obj,
-        rv=" " + failureReturnValue if failureReturnValue else "")
+        rv=failureReturnValue)
     defineUnforgeableMethods = fill(
         """
         if (!DefineUnforgeableMethods(aCx, ${obj}, %s)) {
           return${rv};
         }
         """,
         obj=obj,
-        rv=" " + failureReturnValue if failureReturnValue else "")
+        rv=failureReturnValue)
 
     unforgeableMembers = [
         (defineUnforgeableAttrs, properties.unforgeableAttrs),
         (defineUnforgeableMethods, properties.unforgeableMethods)
     ]
     for (template, array) in unforgeableMembers:
         if array.hasNonChromeOnly():
             unforgeables.append(CGGeneric(template % array.variableName(False)))
         if array.hasChromeOnly():
             unforgeables.append(
                 CGIfWrapper(CGGeneric(template % array.variableName(True)),
                             "nsContentUtils::ThreadsafeIsCallerChrome()"))
 
+    if descriptor.interface.getExtendedAttribute("Unforgeable"):
+        # We do our undefined toJSON here, not as a regular property
+        # because we don't have a concept of value props anywhere.
+        unforgeables.append(CGGeneric(fill(
+            """
+            if (!JS_DefineProperty(aCx, ${obj}, "toJSON", JS::UndefinedHandleValue,
+                                   JSPROP_READONLY | JSPROP_ENUMERATE | JSPROP_PERMANENT)) {
+              return${rv};
+            }
+            """,
+            obj=obj,
+            rv=failureReturnValue)))
+
     return CGList(unforgeables)
 
 
 def InitUnforgeableProperties(descriptor, properties):
     """
     properties is a PropertyArrays instance
     """
     unforgeableAttrs = properties.unforgeableAttrs
@@ -10348,23 +10408,23 @@ class CGDescriptor(CGThing):
                                             (extAttr, m.location))
                     if m.isStatic():
                         assert descriptor.interface.hasInterfaceObject()
                         cgThings.append(CGStaticSetter(descriptor, m))
                     elif descriptor.interface.hasInterfacePrototypeObject():
                         cgThings.append(CGSpecializedSetter(descriptor, m))
                         if m.hasLenientThis():
                             hasLenientSetter = True
-                        elif m.getExtendedAttribute("CrossOriginWritable"):
+                        elif IsCrossOriginWritable(m, descriptor):
                             crossOriginSetters.add(m.identifier.name)
                         elif descriptor.needsSpecialGenericOps():
                             hasSetter = True
                 elif m.getExtendedAttribute("PutForwards"):
                     cgThings.append(CGSpecializedForwardingSetter(descriptor, m))
-                    if m.getExtendedAttribute("CrossOriginWritable"):
+                    if IsCrossOriginWritable(m, descriptor):
                         crossOriginSetters.add(m.identifier.name)
                     elif descriptor.needsSpecialGenericOps():
                         hasSetter = True
                 elif m.getExtendedAttribute("Replaceable"):
                     cgThings.append(CGSpecializedReplaceableSetter(descriptor, m))
                     if descriptor.needsSpecialGenericOps():
                         hasSetter = True
                 if (not m.isStatic() and
--- a/dom/bindings/Exceptions.cpp
+++ b/dom/bindings/Exceptions.cpp
@@ -389,19 +389,17 @@ NS_IMETHODIMP JSStackFrame::GetFilename(
 }
 
 /* readonly attribute AString name; */
 NS_IMETHODIMP JSStackFrame::GetName(nsAString& aFunction)
 {
   if (!mFunnameInitialized) {
     JS::FrameDescription& desc = mStackDescription->FrameAt(mIndex);
     if (JSFlatString *name = desc.funDisplayName()) {
-      mFunname.Assign(JS_GetFlatStringChars(name),
-                      // XXXbz Can't JS_GetStringLength on JSFlatString!
-                      JS_GetStringLength(JS_FORGET_STRING_FLATNESS(name)));
+      AssignJSFlatString(mFunname, name);
     }
     mFunnameInitialized = true;
   }
 
   // The function name must be set to null if empty.
   if (mFunname.IsEmpty()) {
     aFunction.SetIsVoid(true);
   } else {
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -664,18 +664,46 @@ class IDLInterface(IDLObjectWithScope):
             self.members.extend(additionalMembers)
             iface.interfacesImplementingSelf.add(self)
 
         for ancestor in self.getInheritedInterfaces():
             ancestor.interfacesBasedOnSelf.add(self)
             for ancestorConsequential in ancestor.getConsequentialInterfaces():
                 ancestorConsequential.interfacesBasedOnSelf.add(self)
 
+        # Deal with interfaces marked [Unforgeable], now that we have our full
+        # member list, except unforgeables pulled in from parents.  We want to
+        # do this before we set "originatingInterface" on our unforgeable
+        # members.
+        if self.getExtendedAttribute("Unforgeable"):
+            # Check that the interface already has all the things the
+            # spec would otherwise require us to synthesize and is
+            # missing the ones we plan to synthesize.
+            if not any(m.isMethod() and m.isStringifier() for m in self.members):
+                raise WebIDLError("Unforgeable interface %s does not have a "
+                                  "stringifier" % self.identifier.name,
+                                  [self.location])
+
+            for m in self.members:
+                if ((m.isMethod() and m.isJsonifier()) or
+                    m.identifier.name == "toJSON"):
+                    raise WebIDLError("Unforgeable interface %s has a "
+                                      "jsonifier so we won't be able to add "
+                                      "one ourselves" % self.identifier.name,
+                                      [self.location, m.location])
+
+                if m.identifier.name == "valueOf" and not m.isStatic():
+                    raise WebIDLError("Unforgeable interface %s has a valueOf "
+                                      "member so we won't be able to add one "
+                                      "ourselves" % self.identifier.name,
+                                      [self.location, m.location])
+
         for member in self.members:
-            if (member.isAttr() and member.isUnforgeable() and
+            if ((member.isAttr() or member.isMethod()) and
+                member.isUnforgeable() and
                 not hasattr(member, "originatingInterface")):
                 member.originatingInterface = self
 
         # Compute slot indices for our members before we pull in
         # unforgeable members from our parent.
         for member in self.members:
             if (member.isAttr() and
                 (member.getExtendedAttribute("StoreInSlot") or
@@ -688,38 +716,38 @@ class IDLInterface(IDLObjectWithScope):
         if self.parent:
             # Make sure we don't shadow any of the [Unforgeable] attributes on
             # our ancestor interfaces.  We don't have to worry about
             # consequential interfaces here, because those have already been
             # imported into the relevant .members lists.  And we don't have to
             # worry about anything other than our parent, because it has already
             # imported its ancestors unforgeable attributes into its member
             # list.
-            for unforgeableAttr in (attr for attr in self.parent.members if
-                                    attr.isAttr() and not attr.isStatic() and
-                                    attr.isUnforgeable()):
+            for unforgeableMember in (member for member in self.parent.members if
+                                      (member.isAttr() or member.isMethod()) and
+                                      member.isUnforgeable()):
                 shadows = [ m for m in self.members if
                             (m.isAttr() or m.isMethod()) and
                             not m.isStatic() and
-                            m.identifier.name == unforgeableAttr.identifier.name ]
+                            m.identifier.name == unforgeableMember.identifier.name ]
                 if len(shadows) != 0:
-                    locs = [unforgeableAttr.location] + [ s.location for s
-                                                          in shadows ]
+                    locs = [unforgeableMember.location] + [ s.location for s
+                                                            in shadows ]
                     raise WebIDLError("Interface %s shadows [Unforgeable] "
                                       "members of %s" %
                                       (self.identifier.name,
                                        ancestor.identifier.name),
                                       locs)
                 # And now just stick it in our members, since we won't be
                 # inheriting this down the proto chain.  If we really cared we
                 # could try to do something where we set up the unforgeable
-                # attributes of ancestor interfaces, with their corresponding
-                # getters, on our interface, but that gets pretty complicated
-                # and seems unnecessary.
-                self.members.append(unforgeableAttr)
+                # attributes/methods of ancestor interfaces, with their
+                # corresponding getters, on our interface, but that gets pretty
+                # complicated and seems unnecessary.
+                self.members.append(unforgeableMember)
 
         # Ensure that there's at most one of each {named,indexed}
         # {getter,setter,creator,deleter}, at most one stringifier,
         # and at most one legacycaller.  Note that this last is not
         # quite per spec, but in practice no one overloads
         # legacycallers.
         specialMembersSeen = {}
         for member in self.members:
@@ -780,16 +808,36 @@ class IDLInterface(IDLObjectWithScope):
                 if parent.getExtendedAttribute("OverrideBuiltins"):
                     raise WebIDLError("Interface with [Global] inherits from "
                                       "interface with [OverrideBuiltins]",
                                       [self.location, parent.location])
                 parent._isOnGlobalProtoChain = True
                 parent = parent.parent
 
     def validate(self):
+        # We don't support consequential unforgeable interfaces.  Need to check
+        # this here, becaue in finish() an interface might not know yet that
+        # it's consequential.
+        if self.getExtendedAttribute("Unforgeable") and self.isConsequential():
+            raise WebIDLError(
+                "%s is an unforgeable consequential interface" %
+                self.identifier.name,
+                [self.location] +
+                list(i.location for i in
+                     (self.interfacesBasedOnSelf - { self }) ))
+
+        # We also don't support inheriting from unforgeable interfaces.
+        if self.getExtendedAttribute("Unforgeable") and self.hasChildInterfaces():
+            raise WebIDLError("%s is an unforgeable ancestor interface" %
+                self.identifier.name,
+                [self.location] +
+                list(i.location for i in
+                     self.interfacesBasedOnSelf if i.parent == self))
+
+
         for member in self.members:
             member.validate()
 
             # Check that PutForwards refers to another attribute and that no
             # cycles exist in forwarded assignments.
             if member.isAttr():
                 iface = self
                 attr = member
@@ -987,16 +1035,17 @@ class IDLInterface(IDLObjectWithScope):
             elif identifier == "Global":
                 if not attr.noArguments():
                     raise WebIDLError("[Global] must take no arguments",
                                       [attr.location])
                 self._isOnGlobalProtoChain = True
             elif (identifier == "NeedNewResolve" or
                   identifier == "OverrideBuiltins" or
                   identifier == "ChromeOnly" or
+                  identifier == "Unforgeable" or
                   identifier == "LegacyEventInit"):
                 # Known extended attributes that do not take values
                 if not attr.noArguments():
                     raise WebIDLError("[%s] must take no arguments" % identifier,
                                       [attr.location])
             elif (identifier == "Pref" or
                   identifier == "JSImplementation" or
                   identifier == "HeaderFile" or
@@ -2884,19 +2933,16 @@ class IDLAttribute(IDLInterfaceMember):
                                   "with [CrossOriginReadable]",
                                   [attr.location, self.location])
             if self.getExtendedAttribute("CrossOriginWritable"):
                 raise WebIDLError("[LenientThis] is not allowed in combination "
                                   "with [CrossOriginWritable]",
                                   [attr.location, self.location])
             self.lenientThis = True
         elif identifier == "Unforgeable":
-            if not self.readonly:
-                raise WebIDLError("[Unforgeable] is only allowed on readonly "
-                                  "attributes", [attr.location, self.location])
             if self.isStatic():
                 raise WebIDLError("[Unforgeable] is only allowed on non-static "
                                   "attributes", [attr.location, self.location])
             self._unforgeable = True
         elif identifier == "SameObject" and not self.readonly:
             raise WebIDLError("[SameObject] only allowed on readonly attributes",
                               [attr.location, self.location])
         elif identifier == "Constant" and not self.readonly:
@@ -2946,17 +2992,17 @@ class IDLAttribute(IDLInterfaceMember):
                                   [attr.location, self.location])
         elif identifier == "Cached":
             if self.getExtendedAttribute("StoreInSlot"):
                 raise WebIDLError("[Cached] and [StoreInSlot] must not be "
                                   "specified on the same attribute",
                                   [attr.location, self.location])
         elif (identifier == "CrossOriginReadable" or
               identifier == "CrossOriginWritable"):
-            if not attr.noArguments():
+            if not attr.noArguments() and identifier == "CrossOriginReadable":
                 raise WebIDLError("[%s] must take no arguments" % identifier,
                                   [attr.location])
             if self.isStatic():
                 raise WebIDLError("[%s] is only allowed on non-static "
                                   "attributes" % identifier,
                                   [attr.location, self.location])
             if self.getExtendedAttribute("LenientThis"):
                 raise WebIDLError("[LenientThis] is not allowed in combination "
@@ -3540,25 +3586,29 @@ class IDLMethod(IDLInterfaceMember, IDLS
             sig = self.signatures()[0]
             if not sig[0].isVoid():
                 raise WebIDLError("[LenientFloat] used on a non-void method",
                                   [attr.location, self.location])
             if not any(arg.type.includesRestrictedFloat() for arg in sig[1]):
                 raise WebIDLError("[LenientFloat] used on an operation with no "
                                   "restricted float type arguments",
                                   [attr.location, self.location])
+        elif (identifier == "Pure" or
+              identifier == "CrossOriginCallable" or
+              identifier == "WebGLHandlesContextLoss"):
+            # Known no-argument attributes.
+            if not attr.noArguments():
+                raise WebIDLError("[%s] must take no arguments" % identifier,
+                                  [attr.location])
         elif (identifier == "Throws" or
               identifier == "NewObject" or
               identifier == "ChromeOnly" or
               identifier == "Pref" or
               identifier == "Func" or
               identifier == "AvailableIn" or
-              identifier == "Pure" or
-              identifier == "CrossOriginCallable" or
-              identifier == "WebGLHandlesContextLoss" or
               identifier == "CheckPermissions"):
             # Known attributes that we don't need to do anything with here
             pass
         else:
             raise WebIDLError("Unknown extended attribute %s on method" % identifier,
                               [attr.location])
         IDLInterfaceMember.handleExtendedAttribute(self, attr)
 
--- a/dom/bindings/parser/tests/test_unforgeable.py
+++ b/dom/bindings/parser/tests/test_unforgeable.py
@@ -79,31 +79,69 @@ def WebIDLTest(parser, harness):
                "Should have thrown when shadowing unforgeable attribute on "
                "parent with operation.")
 
     parser = parser.reset();
     threw = False
     try:
         parser.parse("""
             interface Child : Parent {
+              void foo();
+            };
+            interface Parent {
+              [Unforgeable] void foo();
+            };
+        """)
+
+        results = parser.finish()
+    except:
+        threw = True
+    harness.ok(threw,
+               "Should have thrown when shadowing unforgeable operation on "
+               "parent with operation.")
+
+    parser = parser.reset();
+    threw = False
+    try:
+        parser.parse("""
+            interface Child : Parent {
               attribute short foo;
             };
             interface Parent {
               [Unforgeable] readonly attribute long foo;
             };
         """)
 
         results = parser.finish()
     except Exception,x:
         threw = True
     harness.ok(threw,
                "Should have thrown when shadowing unforgeable attribute on "
                "parent with attribute.")
 
     parser = parser.reset();
+    threw = False
+    try:
+        parser.parse("""
+            interface Child : Parent {
+              attribute short foo;
+            };
+            interface Parent {
+              [Unforgeable] void foo();
+            };
+        """)
+
+        results = parser.finish()
+    except Exception,x:
+        threw = True
+    harness.ok(threw,
+               "Should have thrown when shadowing unforgeable operation on "
+               "parent with attribute.")
+
+    parser = parser.reset();
     parser.parse("""
             interface Child : Parent {
             };
             interface Parent {};
             interface Consequential {
               [Unforgeable] readonly attribute long foo;
             };
             Parent implements Consequential;
@@ -161,26 +199,48 @@ def WebIDLTest(parser, harness):
     harness.ok(threw,
                "Should have thrown when our consequential interface shadows unforgeable attribute "
                "of ancestor's consequential interface.")
 
     parser = parser.reset();
     threw = False
     try:
         parser.parse("""
-            interface iface {
-              [Unforgeable] attribute long foo;
+            interface Child : Parent {
             };
+            interface Parent : GrandParent {};
+            interface GrandParent {};
+            interface Consequential {
+              [Unforgeable] void foo();
+            };
+            GrandParent implements Consequential;
+            interface ChildConsequential {
+              void foo();
+            };
+            Child implements ChildConsequential;
         """)
 
         results = parser.finish()
     except:
         threw = True
 
-    harness.ok(threw, "Should have thrown for writable [Unforgeable] attribute.")
+    harness.ok(threw,
+               "Should have thrown when our consequential interface shadows unforgeable operation "
+               "of ancestor's consequential interface.")
+
+    parser = parser.reset();
+    parser.parse("""
+        interface iface {
+          [Unforgeable] attribute long foo;
+        };
+    """)
+
+    results = parser.finish()
+    harness.check(len(results), 1,
+                  "Should allow writable [Unforgeable] attribute.")
 
     parser = parser.reset();
     threw = False
     try:
         parser.parse("""
             interface iface {
               [Unforgeable] static readonly attribute long foo;
             };
--- a/dom/bluetooth2/BluetoothAdapter.cpp
+++ b/dom/bluetooth2/BluetoothAdapter.cpp
@@ -15,16 +15,17 @@
 #include "mozilla/dom/BluetoothAttributeEvent.h"
 #include "mozilla/dom/BluetoothDeviceEvent.h"
 #include "mozilla/dom/BluetoothStatusChangedEvent.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/LazyIdleThread.h"
 
 #include "BluetoothAdapter.h"
+#include "BluetoothClassOfDevice.h"
 #include "BluetoothDevice.h"
 #include "BluetoothDiscoveryHandle.h"
 #include "BluetoothReplyRunnable.h"
 #include "BluetoothService.h"
 #include "BluetoothUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
@@ -59,37 +60,42 @@ NS_IMPL_RELEASE_INHERITED(BluetoothAdapt
 class StartDiscoveryTask : public BluetoothReplyRunnable
 {
  public:
   StartDiscoveryTask(BluetoothAdapter* aAdapter, Promise* aPromise)
     : BluetoothReplyRunnable(nullptr, aPromise,
                              NS_LITERAL_STRING("StartDiscovery"))
     , mAdapter(aAdapter)
   {
-    MOZ_ASSERT(aPromise && aAdapter);
+    MOZ_ASSERT(aPromise);
+    MOZ_ASSERT(aAdapter);
   }
 
   bool
   ParseSuccessfulReply(JS::MutableHandle<JS::Value> aValue)
   {
-    BT_API2_LOGR();
     aValue.setUndefined();
 
     AutoJSAPI jsapi;
     NS_ENSURE_TRUE(jsapi.Init(mAdapter->GetParentObject()), false);
+    JSContext* cx = jsapi.cx();
 
-    // Wrap BluetoothDiscoveryHandle to return
-    JSContext* cx = jsapi.cx();
+    /**
+     * Create a new discovery handle and wrap it to return. Each
+     * discovery handle is one-time-use only.
+     */
     nsRefPtr<BluetoothDiscoveryHandle> discoveryHandle =
       BluetoothDiscoveryHandle::Create(mAdapter->GetParentObject());
     if (!ToJSValue(cx, discoveryHandle, aValue)) {
       JS_ClearPendingException(cx);
       return false;
     }
 
+    // Set the created discovery handle as the one in use.
+    mAdapter->SetDiscoveryHandleInUse(discoveryHandle);
     return true;
   }
 
   virtual void
   ReleaseMembers() MOZ_OVERRIDE
   {
     BluetoothReplyRunnable::ReleaseMembers();
     mAdapter = nullptr;
@@ -200,16 +206,17 @@ public:
 
 static int kCreatePairedDeviceTimeout = 50000; // unit: msec
 
 BluetoothAdapter::BluetoothAdapter(nsPIDOMWindow* aWindow,
                                    const BluetoothValue& aValue)
   : DOMEventTargetHelper(aWindow)
   , mJsUuids(nullptr)
   , mJsDeviceAddresses(nullptr)
+  , mDiscoveryHandleInUse(nullptr)
   , mState(BluetoothAdapterState::Disabled)
   , mDiscoverable(false)
   , mDiscovering(false)
   , mPairable(false)
   , mPowered(false)
   , mIsRooted(false)
 {
   MOZ_ASSERT(aWindow);
@@ -268,27 +275,30 @@ BluetoothAdapter::Root()
 }
 
 void
 BluetoothAdapter::SetPropertyByValue(const BluetoothNamedValue& aValue)
 {
   const nsString& name = aValue.name();
   const BluetoothValue& value = aValue.value();
   if (name.EqualsLiteral("State")) {
-    bool isEnabled = value.get_bool();
-    mState = isEnabled ? BluetoothAdapterState::Enabled
-                       : BluetoothAdapterState::Disabled;
+    mState = value.get_bool() ? BluetoothAdapterState::Enabled
+                              : BluetoothAdapterState::Disabled;
   } else if (name.EqualsLiteral("Name")) {
     mName = value.get_nsString();
   } else if (name.EqualsLiteral("Address")) {
     mAddress = value.get_nsString();
   } else if (name.EqualsLiteral("Discoverable")) {
     mDiscoverable = value.get_bool();
   } else if (name.EqualsLiteral("Discovering")) {
     mDiscovering = value.get_bool();
+    if (!mDiscovering) {
+      // Reset discovery handle in use to nullptr
+      SetDiscoveryHandleInUse(nullptr);
+    }
   } else if (name.EqualsLiteral("Pairable")) {
     mPairable = value.get_bool();
   } else if (name.EqualsLiteral("Powered")) {
     mPowered = value.get_bool();
   } else if (name.EqualsLiteral("PairableTimeout")) {
     mPairableTimeout = value.get_uint32_t();
   } else if (name.EqualsLiteral("DiscoverableTimeout")) {
     mDiscoverableTimeout = value.get_uint32_t();
@@ -385,33 +395,48 @@ BluetoothAdapter::Notify(const Bluetooth
 
     DispatchTrustedEvent(event);
   } else {
     BT_WARNING("Not handling adapter signal: %s",
                NS_ConvertUTF16toUTF8(aData.name()).get());
   }
 }
 
+void
+BluetoothAdapter::SetDiscoveryHandleInUse(
+  BluetoothDiscoveryHandle* aDiscoveryHandle)
+{
+  // Stop discovery handle in use from listening to "DeviceFound" signal
+  if (mDiscoveryHandleInUse) {
+    mDiscoveryHandleInUse->DisconnectFromOwner();
+  }
+
+  mDiscoveryHandleInUse = aDiscoveryHandle;
+}
+
 already_AddRefed<Promise>
 BluetoothAdapter::StartStopDiscovery(bool aStart, ErrorResult& aRv)
 {
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
   if (!global) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
   nsRefPtr<Promise> promise = new Promise(global);
 
   /**
    * Ensure
    * - adapter does not already start/stop discovering,
+   *   (note we reject here to ensure each resolved promise of startDiscovery
+   *    returns a BluetoothDiscoveryHandle)
    * - adapter is already enabled, and
    * - BluetoothService is available
    */
-  BT_ENSURE_TRUE_RESOLVE(mDiscovering != aStart, JS::UndefinedHandleValue);
+  BT_ENSURE_TRUE_REJECT(mDiscovering != aStart,
+                        NS_ERROR_DOM_INVALID_STATE_ERR);
   BT_ENSURE_TRUE_REJECT(mState == BluetoothAdapterState::Enabled,
                         NS_ERROR_DOM_INVALID_STATE_ERR);
   BluetoothService* bs = BluetoothService::Get();
   BT_ENSURE_TRUE_REJECT(bs, NS_ERROR_NOT_AVAILABLE);
 
   BT_API2_LOGR("aStart %d", aStart);
   nsresult rv;
   if (aStart) {
@@ -819,22 +844,20 @@ BluetoothAdapter::ConvertStringToAdapter
   return BluetoothAdapterAttribute::Unknown;
 }
 
 bool
 BluetoothAdapter::IsAdapterAttributeChanged(BluetoothAdapterAttribute aType,
                                             const BluetoothValue& aValue)
 {
   switch(aType) {
-    case BluetoothAdapterAttribute::State: {
+    case BluetoothAdapterAttribute::State:
       MOZ_ASSERT(aValue.type() == BluetoothValue::Tbool);
-      bool isEnabled = aValue.get_bool();
-      return isEnabled ? mState != BluetoothAdapterState::Enabled
-                       : mState != BluetoothAdapterState::Disabled;
-    }
+      return aValue.get_bool() ? mState != BluetoothAdapterState::Enabled
+                               : mState != BluetoothAdapterState::Disabled;
     case BluetoothAdapterAttribute::Name:
       MOZ_ASSERT(aValue.type() == BluetoothValue::TnsString);
       return !mName.Equals(aValue.get_nsString());
     case BluetoothAdapterAttribute::Address:
       MOZ_ASSERT(aValue.type() == BluetoothValue::TnsString);
       return !mAddress.Equals(aValue.get_nsString());
     case BluetoothAdapterAttribute::Discoverable:
       MOZ_ASSERT(aValue.type() == BluetoothValue::Tbool);
@@ -923,17 +946,17 @@ BluetoothAdapter::Connect(BluetoothDevic
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(win);
   nsRefPtr<BluetoothVoidReplyRunnable> results =
     new BluetoothVoidReplyRunnable(request);
 
   nsAutoString address;
   aDevice.GetAddress(address);
-  uint32_t deviceClass = aDevice.Class();
+  uint32_t deviceClass = aDevice.Cod()->ToUint32();
   uint16_t serviceUuid = 0;
   if (aServiceUuid.WasPassed()) {
     serviceUuid = aServiceUuid.Value();
   }
 
   BluetoothService* bs = BluetoothService::Get();
   if (!bs) {
     aRv.Throw(NS_ERROR_FAILURE);
--- a/dom/bluetooth2/BluetoothAdapter.h
+++ b/dom/bluetooth2/BluetoothAdapter.h
@@ -20,16 +20,17 @@ class DOMRequest;
 struct MediaMetaData;
 struct MediaPlayStatus;
 }
 }
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothDevice;
+class BluetoothDiscoveryHandle;
 class BluetoothSignal;
 class BluetoothNamedValue;
 class BluetoothValue;
 
 class BluetoothAdapter : public DOMEventTargetHelper
                        , public BluetoothSignalObserver
 {
 public:
@@ -88,16 +89,27 @@ public:
     return mDiscoverableTimeout;
   }
 
   void GetDevices(JSContext* aContext, JS::MutableHandle<JS::Value> aDevices,
                   ErrorResult& aRv);
   void GetUuids(JSContext* aContext, JS::MutableHandle<JS::Value> aUuids,
                 ErrorResult& aRv);
 
+  /**
+   * Update this adapter's discovery handle in use (mDiscoveryHandleInUse).
+   *
+   * |mDiscoveryHandleInUse| is set to the latest discovery handle when adapter
+   * just starts discovery, and is reset to nullptr when discovery is stopped
+   * by some adapter.
+   *
+   * @param aDiscoveryHandle [in] the discovery handle to set.
+   */
+  void SetDiscoveryHandleInUse(BluetoothDiscoveryHandle* aDiscoveryHandle);
+
   already_AddRefed<Promise> SetName(const nsAString& aName, ErrorResult& aRv);
   already_AddRefed<Promise>
     SetDiscoverable(bool aDiscoverable, ErrorResult& aRv);
   already_AddRefed<Promise> StartDiscovery(ErrorResult& aRv);
   already_AddRefed<Promise> StopDiscovery(ErrorResult& aRv);
 
   already_AddRefed<DOMRequest>
     Pair(const nsAString& aDeviceAddress, ErrorResult& aRv);
@@ -184,16 +196,17 @@ private:
                                  const BluetoothValue& aValue);
   void HandlePropertyChanged(const BluetoothValue& aValue);
   void DispatchAttributeEvent(const nsTArray<nsString>& aTypes);
   BluetoothAdapterAttribute
     ConvertStringToAdapterAttribute(const nsAString& aString);
 
   JS::Heap<JSObject*> mJsUuids;
   JS::Heap<JSObject*> mJsDeviceAddresses;
+  nsRefPtr<BluetoothDiscoveryHandle> mDiscoveryHandleInUse;
   BluetoothAdapterState mState;
   nsString mAddress;
   nsString mName;
   bool mDiscoverable;
   bool mDiscovering;
   bool mPairable;
   bool mPowered;
   uint32_t mPairableTimeout;
--- a/dom/bluetooth2/BluetoothDevice.cpp
+++ b/dom/bluetooth2/BluetoothDevice.cpp
@@ -1,65 +1,90 @@
 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "base/basictypes.h"
+#include "BluetoothClassOfDevice.h"
 #include "BluetoothDevice.h"
 #include "BluetoothReplyRunnable.h"
 #include "BluetoothService.h"
 #include "BluetoothUtils.h"
 
-#include "nsDOMClassInfo.h"
-#include "nsTArrayHelpers.h"
+#include "mozilla/dom/bluetooth/BluetoothTypes.h"
+#include "mozilla/dom/BluetoothAttributeEvent.h"
+#include "mozilla/dom/BluetoothDevice2Binding.h"
+#include "mozilla/dom/Promise.h"
 
-#include "mozilla/dom/bluetooth/BluetoothTypes.h"
-#include "mozilla/dom/BluetoothDevice2Binding.h"
-#include "mozilla/dom/ScriptSettings.h"
+using namespace mozilla;
+using namespace mozilla::dom;
 
 USING_BLUETOOTH_NAMESPACE
 
-DOMCI_DATA(BluetoothDevice, BluetoothDevice)
-
-NS_IMPL_CYCLE_COLLECTION_CLASS(BluetoothDevice)
-
-NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(BluetoothDevice,
-                                               DOMEventTargetHelper)
-  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mJsUuids)
-  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mJsServices)
-NS_IMPL_CYCLE_COLLECTION_TRACE_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(BluetoothDevice,
-                                                  DOMEventTargetHelper)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(BluetoothDevice,
-                                                DOMEventTargetHelper)
-  tmp->Unroot();
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
+NS_IMPL_CYCLE_COLLECTION_INHERITED(BluetoothDevice, DOMEventTargetHelper, mCod)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(BluetoothDevice)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(BluetoothDevice, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(BluetoothDevice, DOMEventTargetHelper)
 
+class FetchUuidsTask : public BluetoothReplyRunnable
+{
+public:
+  FetchUuidsTask(Promise* aPromise,
+                 const nsAString& aName,
+                 BluetoothDevice* aDevice)
+    : BluetoothReplyRunnable(nullptr /* DOMRequest */, aPromise, aName)
+    , mDevice(aDevice)
+  {
+    MOZ_ASSERT(aPromise);
+    MOZ_ASSERT(aDevice);
+  }
+
+  bool ParseSuccessfulReply(JS::MutableHandle<JS::Value> aValue)
+  {
+    aValue.setUndefined();
+
+    const BluetoothValue& v = mReply->get_BluetoothReplySuccess().value();
+    NS_ENSURE_TRUE(v.type() == BluetoothValue::TArrayOfnsString, false);
+    const InfallibleTArray<nsString>& uuids = v.get_ArrayOfnsString();
+
+    AutoJSAPI jsapi;
+    NS_ENSURE_TRUE(jsapi.Init(mDevice->GetParentObject()), false);
+
+    JSContext* cx = jsapi.cx();
+    if (!ToJSValue(cx, uuids, aValue)) {
+      BT_WARNING("Cannot create JS array!");
+      JS_ClearPendingException(cx);
+      return false;
+    }
+
+    return true;
+  }
+
+  virtual void ReleaseMembers() MOZ_OVERRIDE
+  {
+    BluetoothReplyRunnable::ReleaseMembers();
+    mDevice = nullptr;
+  }
+
+private:
+  nsRefPtr<BluetoothDevice> mDevice;
+};
+
 BluetoothDevice::BluetoothDevice(nsPIDOMWindow* aWindow,
                                  const BluetoothValue& aValue)
   : DOMEventTargetHelper(aWindow)
-  , mJsUuids(nullptr)
-  , mJsServices(nullptr)
-  , mIsRooted(false)
 {
   MOZ_ASSERT(aWindow);
   MOZ_ASSERT(IsDOMBinding());
 
+  mCod = BluetoothClassOfDevice::Create(aWindow);
+
   const InfallibleTArray<BluetoothNamedValue>& values =
     aValue.get_ArrayOfBluetoothNamedValue();
   for (uint32_t i = 0; i < values.Length(); ++i) {
     SetPropertyByValue(values[i]);
   }
 
   BluetoothService* bs = BluetoothService::Get();
   NS_ENSURE_TRUE_VOID(bs);
@@ -67,104 +92,76 @@ BluetoothDevice::BluetoothDevice(nsPIDOM
 }
 
 BluetoothDevice::~BluetoothDevice()
 {
   BluetoothService* bs = BluetoothService::Get();
   // bs can be null on shutdown, where destruction might happen.
   NS_ENSURE_TRUE_VOID(bs);
   bs->UnregisterBluetoothSignalHandler(mAddress, this);
-  Unroot();
 }
 
 void
 BluetoothDevice::DisconnectFromOwner()
 {
   DOMEventTargetHelper::DisconnectFromOwner();
 
   BluetoothService* bs = BluetoothService::Get();
   NS_ENSURE_TRUE_VOID(bs);
   bs->UnregisterBluetoothSignalHandler(mAddress, this);
 }
 
 void
-BluetoothDevice::Root()
-{
-  if (!mIsRooted) {
-    mozilla::HoldJSObjects(this);
-    mIsRooted = true;
-  }
-}
-
-void
-BluetoothDevice::Unroot()
-{
-  if (mIsRooted) {
-    mJsUuids = nullptr;
-    mJsServices = nullptr;
-    mozilla::DropJSObjects(this);
-    mIsRooted = false;
-  }
-}
-
-void
 BluetoothDevice::SetPropertyByValue(const BluetoothNamedValue& aValue)
 {
   const nsString& name = aValue.name();
   const BluetoothValue& value = aValue.value();
   if (name.EqualsLiteral("Name")) {
     mName = value.get_nsString();
   } else if (name.EqualsLiteral("Address")) {
     mAddress = value.get_nsString();
-  } else if (name.EqualsLiteral("Class")) {
-    mClass = value.get_uint32_t();
-  } else if (name.EqualsLiteral("Icon")) {
-    mIcon = value.get_nsString();
-  } else if (name.EqualsLiteral("Connected")) {
-    mConnected = value.get_bool();
+  } else if (name.EqualsLiteral("Cod")) {
+    mCod->Update(value.get_uint32_t());
   } else if (name.EqualsLiteral("Paired")) {
     mPaired = value.get_bool();
   } else if (name.EqualsLiteral("UUIDs")) {
+    // We assume the received uuids array is sorted without duplicate items.
+    // If it's not, we require additional processing before assigning it
+    // directly.
     mUuids = value.get_ArrayOfnsString();
-
-    AutoJSAPI jsapi;
-    if (!jsapi.Init(GetOwner())) {
-      BT_WARNING("Failed to initialise AutoJSAPI!");
-      return;
-    }
-    JSContext* cx = jsapi.cx();
-    JS::Rooted<JSObject*> uuids(cx);
-    if (NS_FAILED(nsTArrayToJSArray(cx, mUuids, &uuids))) {
-      BT_WARNING("Cannot set JS UUIDs object!");
-      return;
-    }
-    mJsUuids = uuids;
-    Root();
-  } else if (name.EqualsLiteral("Services")) {
-    mServices = value.get_ArrayOfnsString();
+    BluetoothDeviceBinding::ClearCachedUuidsValue(this);
+  } else {
+    BT_WARNING("Not handling device property: %s",
+               NS_ConvertUTF16toUTF8(name).get());
+  }
+}
 
-    AutoJSAPI jsapi;
-    if (!jsapi.Init(GetOwner())) {
-      BT_WARNING("Failed to initialise AutoJSAPI!");
-      return;
-    }
-    JSContext* cx = jsapi.cx();
-    JS::Rooted<JSObject*> services(cx);
-    if (NS_FAILED(nsTArrayToJSArray(cx, mServices, &services))) {
-      BT_WARNING("Cannot set JS Services object!");
-      return;
-    }
-    mJsServices = services;
-    Root();
-  } else {
-    nsCString warningMsg;
-    warningMsg.AssignLiteral("Not handling device property: ");
-    warningMsg.Append(NS_ConvertUTF16toUTF8(name));
-    BT_WARNING(warningMsg.get());
+already_AddRefed<Promise>
+BluetoothDevice::FetchUuids(ErrorResult& aRv)
+{
+  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
+  if (!global) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
   }
+
+  nsRefPtr<Promise> promise = new Promise(global);
+
+  BluetoothService* bs = BluetoothService::Get();
+  BT_ENSURE_TRUE_REJECT(bs, NS_ERROR_NOT_AVAILABLE);
+
+  nsRefPtr<BluetoothReplyRunnable> result =
+    new FetchUuidsTask(promise,
+                       NS_LITERAL_STRING("FetchUuids"),
+                       this);
+
+  nsresult rv = bs->FetchUuidsInternal(mAddress, result);
+  BT_ENSURE_TRUE_REJECT(NS_SUCCEEDED(rv), NS_ERROR_DOM_OPERATION_ERR);
+
+  return promise.forget();
 }
 
 // static
 already_AddRefed<BluetoothDevice>
 BluetoothDevice::Create(nsPIDOMWindow* aWindow,
                         const BluetoothValue& aValue)
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -172,65 +169,121 @@ BluetoothDevice::Create(nsPIDOMWindow* a
 
   nsRefPtr<BluetoothDevice> device = new BluetoothDevice(aWindow, aValue);
   return device.forget();
 }
 
 void
 BluetoothDevice::Notify(const BluetoothSignal& aData)
 {
-  BT_LOGD("[D] %s: %s", __FUNCTION__, NS_ConvertUTF16toUTF8(aData.name()).get());
+  BT_LOGD("[D] %s", NS_ConvertUTF16toUTF8(aData.name()).get());
 
   BluetoothValue v = aData.value();
   if (aData.name().EqualsLiteral("PropertyChanged")) {
-    MOZ_ASSERT(v.type() == BluetoothValue::TArrayOfBluetoothNamedValue);
+    HandlePropertyChanged(v);
+  } else {
+    BT_WARNING("Not handling device signal: %s",
+               NS_ConvertUTF16toUTF8(aData.name()).get());
+  }
+}
 
-    const InfallibleTArray<BluetoothNamedValue>& arr =
-      v.get_ArrayOfBluetoothNamedValue();
+BluetoothDeviceAttribute
+BluetoothDevice::ConvertStringToDeviceAttribute(const nsAString& aString)
+{
+  using namespace
+    mozilla::dom::BluetoothDeviceAttributeValues;
 
-    for (uint32_t i = 0, propCount = arr.Length(); i < propCount; ++i) {
-      SetPropertyByValue(arr[i]);
+  for (size_t index = 0; index < ArrayLength(strings) - 1; index++) {
+    if (aString.LowerCaseEqualsASCII(strings[index].value,
+                                     strings[index].length)) {
+      return static_cast<BluetoothDeviceAttribute>(index);
     }
-  } else {
-#ifdef DEBUG
-    nsCString warningMsg;
-    warningMsg.AssignLiteral("Not handling device signal: ");
-    warningMsg.Append(NS_ConvertUTF16toUTF8(aData.name()));
-    BT_WARNING(warningMsg.get());
-#endif
+  }
+
+  return BluetoothDeviceAttribute::Unknown;
+}
+
+bool
+BluetoothDevice::IsDeviceAttributeChanged(BluetoothDeviceAttribute aType,
+                                          const BluetoothValue& aValue)
+{
+  switch (aType) {
+    case BluetoothDeviceAttribute::Cod:
+      MOZ_ASSERT(aValue.type() == BluetoothValue::Tuint32_t);
+      return !mCod->Equals(aValue.get_uint32_t());
+    case BluetoothDeviceAttribute::Name:
+      MOZ_ASSERT(aValue.type() == BluetoothValue::TnsString);
+      return !mName.Equals(aValue.get_nsString());
+    case BluetoothDeviceAttribute::Paired:
+      MOZ_ASSERT(aValue.type() == BluetoothValue::Tbool);
+      return mPaired != aValue.get_bool();
+    case BluetoothDeviceAttribute::Uuids: {
+      MOZ_ASSERT(aValue.type() == BluetoothValue::TArrayOfnsString);
+      const InfallibleTArray<nsString>& uuids = aValue.get_ArrayOfnsString();
+      // We assume the received uuids array is sorted without duplicate items.
+      // If it's not, we require additional processing before comparing it
+      // directly.
+      return mUuids != uuids;
+    }
+    default:
+      BT_WARNING("Type %d is not handled", uint32_t(aType));
+      return false;
   }
 }
 
 void
-BluetoothDevice::GetUuids(JSContext* aContext,
-                           JS::MutableHandle<JS::Value> aUuids,
-                           ErrorResult& aRv)
+BluetoothDevice::HandlePropertyChanged(const BluetoothValue& aValue)
 {
-  if (!mJsUuids) {
-    BT_WARNING("UUIDs not yet set!");
-    aRv.Throw(NS_ERROR_FAILURE);
+  MOZ_ASSERT(aValue.type() == BluetoothValue::TArrayOfBluetoothNamedValue);
+
+  const InfallibleTArray<BluetoothNamedValue>& arr =
+    aValue.get_ArrayOfBluetoothNamedValue();
+
+  nsTArray<nsString> types;
+  for (uint32_t i = 0, propCount = arr.Length(); i < propCount; ++i) {
+    BluetoothDeviceAttribute type =
+      ConvertStringToDeviceAttribute(arr[i].name());
+
+    // Non-BluetoothDeviceAttribute properties
+    if (type == BluetoothDeviceAttribute::Unknown) {
+      SetPropertyByValue(arr[i]);
+      continue;
+    }
+
+    // BluetoothDeviceAttribute properties
+    if (IsDeviceAttributeChanged(type, arr[i].value())) {
+      SetPropertyByValue(arr[i]);
+      BT_APPEND_ENUM_STRING(types, BluetoothDeviceAttribute, type);
+    }
+  }
+
+  DispatchAttributeEvent(types);
+}
+
+void
+BluetoothDevice::DispatchAttributeEvent(const nsTArray<nsString>& aTypes)
+{
+  NS_ENSURE_TRUE_VOID(aTypes.Length());
+
+  AutoJSAPI jsapi;
+  NS_ENSURE_TRUE_VOID(jsapi.Init(GetOwner()));
+  JSContext* cx = jsapi.cx();
+  JS::Rooted<JS::Value> value(cx);
+
+  if (!ToJSValue(cx, aTypes, &value)) {
+    JS_ClearPendingException(cx);
     return;
   }
 
-  JS::ExposeObjectToActiveJS(mJsUuids);
-  aUuids.setObject(*mJsUuids);
-}
-
-void
-BluetoothDevice::GetServices(JSContext* aCx,
-                             JS::MutableHandle<JS::Value> aServices,
-                             ErrorResult& aRv)
-{
-  if (!mJsServices) {
-    BT_WARNING("Services not yet set!");
-    aRv.Throw(NS_ERROR_FAILURE);
-    return;
-  }
-
-  JS::ExposeObjectToActiveJS(mJsServices);
-  aServices.setObject(*mJsServices);
+  RootedDictionary<BluetoothAttributeEventInit> init(cx);
+  init.mAttrs = value;
+  nsRefPtr<BluetoothAttributeEvent> event =
+    BluetoothAttributeEvent::Constructor(this,
+                                         NS_LITERAL_STRING("attributechanged"),
+                                         init);
+  DispatchTrustedEvent(event);
 }
 
 JSObject*
 BluetoothDevice::WrapObject(JSContext* aContext)
 {
   return BluetoothDeviceBinding::Wrap(aContext, this);
 }
--- a/dom/bluetooth2/BluetoothDevice.h
+++ b/dom/bluetooth2/BluetoothDevice.h
@@ -4,110 +4,105 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_bluetooth_bluetoothdevice_h__
 #define mozilla_dom_bluetooth_bluetoothdevice_h__
 
 #include "mozilla/Attributes.h"
 #include "mozilla/DOMEventTargetHelper.h"
+#include "mozilla/dom/BluetoothDevice2Binding.h"
 #include "BluetoothCommon.h"
 #include "nsString.h"
+#include "nsCOMPtr.h"
+
+namespace mozilla {
+namespace dom {
+  class Promise;
+}
+}
 
 BEGIN_BLUETOOTH_NAMESPACE
 
+class BluetoothClassOfDevice;
 class BluetoothNamedValue;
 class BluetoothValue;
 class BluetoothSignal;
 class BluetoothSocket;
 
-class BluetoothDevice : public DOMEventTargetHelper
-                      , public BluetoothSignalObserver
+class BluetoothDevice MOZ_FINAL : public DOMEventTargetHelper
+                                , public BluetoothSignalObserver
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
 
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(BluetoothDevice,
-                                                         DOMEventTargetHelper)
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(BluetoothDevice,
+                                           DOMEventTargetHelper)
 
   static already_AddRefed<BluetoothDevice>
   Create(nsPIDOMWindow* aOwner, const BluetoothValue& aValue);
 
   void Notify(const BluetoothSignal& aParam);
 
   void GetAddress(nsString& aAddress) const
   {
     aAddress = mAddress;
   }
 
+  BluetoothClassOfDevice* Cod() const
+  {
+    return mCod;
+  }
+
   void GetName(nsString& aName) const
   {
     aName = mName;
   }
 
-  void GetIcon(nsString& aIcon) const
-  {
-    aIcon = mIcon;
-  }
-
-  uint32_t Class() const
-  {
-    return mClass;
-  }
-
   bool Paired() const
   {
     return mPaired;
   }
 
-  bool Connected() const
-  {
-    return mConnected;
+  void GetUuids(nsTArray<nsString>& aUuids) {
+    aUuids = mUuids;
   }
 
-  void GetUuids(JSContext* aContext, JS::MutableHandle<JS::Value> aUuids,
-                ErrorResult& aRv);
-  void GetServices(JSContext* aContext, JS::MutableHandle<JS::Value> aServices,
-                   ErrorResult& aRv);
-
-  nsISupports*
-  ToISupports()
-  {
-    return static_cast<EventTarget*>(this);
-  }
+  already_AddRefed<Promise> FetchUuids(ErrorResult& aRv);
 
   void SetPropertyByValue(const BluetoothNamedValue& aValue);
 
-  void Unroot();
+  BluetoothDeviceAttribute
+  ConvertStringToDeviceAttribute(const nsAString& aString);
+
+  bool
+  IsDeviceAttributeChanged(BluetoothDeviceAttribute aType,
+                           const BluetoothValue& aValue);
+
+  void HandlePropertyChanged(const BluetoothValue& aValue);
+
+  void DispatchAttributeEvent(const nsTArray<nsString>& aTypes);
+
+  IMPL_EVENT_HANDLER(attributechanged);
 
   nsPIDOMWindow* GetParentObject() const
   {
      return GetOwner();
   }
 
-  virtual JSObject*
-    WrapObject(JSContext* aCx) MOZ_OVERRIDE;
-
+  virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
   virtual void DisconnectFromOwner() MOZ_OVERRIDE;
 
 private:
   BluetoothDevice(nsPIDOMWindow* aOwner, const BluetoothValue& aValue);
   ~BluetoothDevice();
-  void Root();
-
-  JS::Heap<JSObject*> mJsUuids;
-  JS::Heap<JSObject*> mJsServices;
 
   nsString mAddress;
+  nsRefPtr<BluetoothClassOfDevice> mCod;
   nsString mName;
-  nsString mIcon;
-  uint32_t mClass;
-  bool mConnected;
   bool mPaired;
-  bool mIsRooted;
   nsTArray<nsString> mUuids;
-  nsTArray<nsString> mServices;
 
 };
 
 END_BLUETOOTH_NAMESPACE
 
 #endif
--- a/dom/bluetooth2/BluetoothDiscoveryHandle.cpp
+++ b/dom/bluetooth2/BluetoothDiscoveryHandle.cpp
@@ -86,13 +86,20 @@ BluetoothDiscoveryHandle::Notify(const B
   if (aData.name().EqualsLiteral("DeviceFound")) {
     DispatchDeviceEvent(aData.value());
   } else {
     BT_WARNING("Not handling discovery handle signal: %s",
                NS_ConvertUTF16toUTF8(aData.name()).get());
   }
 }
 
+void
+BluetoothDiscoveryHandle::DisconnectFromOwner()
+{
+  DOMEventTargetHelper::DisconnectFromOwner();
+  ListenToBluetoothSignal(false);
+}
+
 JSObject*
 BluetoothDiscoveryHandle::WrapObject(JSContext* aCx)
 {
   return BluetoothDiscoveryHandleBinding::Wrap(aCx, this);
 }
--- a/dom/bluetooth2/BluetoothDiscoveryHandle.h
+++ b/dom/bluetooth2/BluetoothDiscoveryHandle.h
@@ -25,16 +25,18 @@ public:
   NS_DECL_ISUPPORTS_INHERITED
 
   static already_AddRefed<BluetoothDiscoveryHandle>
     Create(nsPIDOMWindow* aWindow);
 
   IMPL_EVENT_HANDLER(devicefound);
 
   void Notify(const BluetoothSignal& aData); // BluetoothSignalObserver
+
+  virtual void DisconnectFromOwner() MOZ_OVERRIDE;
   virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
 private:
   BluetoothDiscoveryHandle(nsPIDOMWindow* aWindow);
   ~BluetoothDiscoveryHandle();
 
   /**
    * Start/Stop listening to bluetooth signal.
--- a/dom/bluetooth2/BluetoothService.h
+++ b/dom/bluetooth2/BluetoothService.h
@@ -147,16 +147,26 @@ public:
    *
    * @return NS_OK on success, NS_ERROR_FAILURE otherwise
    */
   virtual nsresult
   GetConnectedDevicePropertiesInternal(uint16_t aServiceUuid,
                                        BluetoothReplyRunnable* aRunnable) = 0;
 
   /**
+   * Returns up-to-date uuids of given device address,
+   * implemented via a platform specific methood.
+   *
+   * @return NS_OK on success, NS_ERROR_FAILURE otherwise
+   */
+  virtual nsresult
+  FetchUuidsInternal(const nsAString& aDeviceAddress,
+                     BluetoothReplyRunnable* aRunnable) = 0;
+
+  /**
    * Stop device discovery (platform specific implementation)
    *
    * @return NS_OK if discovery stopped correctly, false otherwise
    */
   virtual nsresult
   StopDiscoveryInternal(BluetoothReplyRunnable* aRunnable) = 0;
 
   /**
--- a/dom/bluetooth2/bluedroid/BluetoothServiceBluedroid.cpp
+++ b/dom/bluetooth2/bluedroid/BluetoothServiceBluedroid.cpp
@@ -58,16 +58,17 @@ static InfallibleTArray<nsString> sAdapt
 static BluetoothInterface* sBtInterface;
 static nsTArray<nsRefPtr<BluetoothProfileController> > sControllerArray;
 static InfallibleTArray<BluetoothNamedValue> sRemoteDevicesPack;
 static nsTArray<int> sRequestedDeviceCountArray;
 static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sChangeAdapterStateRunnableArray;
 static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sChangeDiscoveryRunnableArray;
 static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sSetPropertyRunnableArray;
 static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sGetDeviceRunnableArray;
+static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sFetchUuidsRunnableArray;
 static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sBondingRunnableArray;
 static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sUnbondingRunnableArray;
 
 // Static variables below should only be used on *callback thread*
 
 
 // Atomic static variables
 static Atomic<bool> sAdapterDiscoverable(false);
@@ -118,20 +119,21 @@ public:
 
   NS_IMETHOD
   Run()
   {
     MOZ_ASSERT(NS_IsMainThread());
 
     // Bluetooth just enabled, clear profile controllers and runnable arrays.
     sControllerArray.Clear();
+    sChangeDiscoveryRunnableArray.Clear();
+    sSetPropertyRunnableArray.Clear();
+    sGetDeviceRunnableArray.Clear();
+    sFetchUuidsRunnableArray.Clear();
     sBondingRunnableArray.Clear();
-    sChangeDiscoveryRunnableArray.Clear();
-    sGetDeviceRunnableArray.Clear();
-    sSetPropertyRunnableArray.Clear();
     sUnbondingRunnableArray.Clear();
 
     // Bluetooth scan mode is NONE by default
     bt_scan_mode_t mode = BT_SCAN_MODE_CONNECTABLE;
     bt_property_t prop;
     prop.type = BT_PROPERTY_ADAPTER_SCAN_MODE;
     prop.val = (void*)&mode;
     prop.len = sizeof(mode);
@@ -189,107 +191,16 @@ public:
 
     return NS_OK;
   }
 };
 
 /**
  *  Static callback functions
  */
-static void
-ClassToIcon(uint32_t aClass, nsAString& aRetIcon)
-{
-  switch ((aClass & 0x1f00) >> 8) {
-    case 0x01:
-      aRetIcon.AssignLiteral("computer");
-      break;
-    case 0x02:
-      switch ((aClass & 0xfc) >> 2) {
-        case 0x01:
-        case 0x02:
-        case 0x03:
-        case 0x05:
-          aRetIcon.AssignLiteral("phone");
-          break;
-        case 0x04:
-          aRetIcon.AssignLiteral("modem");
-          break;
-      }
-      break;
-    case 0x03:
-      aRetIcon.AssignLiteral("network-wireless");
-      break;
-    case 0x04:
-      switch ((aClass & 0xfc) >> 2) {
-        case 0x01:
-        case 0x02:
-        case 0x06:
-          aRetIcon.AssignLiteral("audio-card");
-          break;
-        case 0x0b:
-        case 0x0c:
-        case 0x0d:
-          aRetIcon.AssignLiteral("camera-video");
-          break;
-        default:
-          aRetIcon.AssignLiteral("audio-card");
-          break;
-      }
-      break;
-    case 0x05:
-      switch ((aClass & 0xc0) >> 6) {
-        case 0x00:
-          switch ((aClass && 0x1e) >> 2) {
-            case 0x01:
-            case 0x02:
-              aRetIcon.AssignLiteral("input-gaming");
-              break;
-          }
-          break;
-        case 0x01:
-          aRetIcon.AssignLiteral("input-keyboard");
-          break;
-        case 0x02:
-          switch ((aClass && 0x1e) >> 2) {
-            case 0x05:
-              aRetIcon.AssignLiteral("input-tablet");
-              break;
-            default:
-              aRetIcon.AssignLiteral("input-mouse");
-              break;
-          }
-      }
-      break;
-    case 0x06:
-      if (aClass & 0x80) {
-        aRetIcon.AssignLiteral("printer");
-        break;
-      }
-      if (aClass & 0x20) {
-        aRetIcon.AssignLiteral("camera-photo");
-        break;
-      }
-      break;
-  }
-
-  if (aRetIcon.IsEmpty()) {
-    if (HAS_AUDIO(aClass)) {
-      /**
-       * Property 'Icon' may be missed due to CoD of major class is TOY(0x08).
-       * But we need to assign Icon as audio-card if service class is 'Audio'.
-       * This is for PTS test case TC_AG_COD_BV_02_I. As HFP specification
-       * defines that service class is 'Audio' can be considered as HFP HF.
-       */
-      aRetIcon.AssignLiteral("audio-card");
-    } else {
-      BT_LOGR("No icon to match class: %x", aClass);
-    }
-  }
-}
-
 static ControlPlayStatus
 PlayStatusStringToControlPlayStatus(const nsAString& aPlayStatus)
 {
   ControlPlayStatus playStatus = ControlPlayStatus::PLAYSTATUS_UNKNOWN;
   if (aPlayStatus.EqualsLiteral("STOPPED")) {
     playStatus = ControlPlayStatus::PLAYSTATUS_STOPPED;
   } else if (aPlayStatus.EqualsLiteral("PLAYING")) {
     playStatus = ControlPlayStatus::PLAYSTATUS_PLAYING;
@@ -471,32 +382,45 @@ public:
   , mRemoteDeviceBdAddress(aRemoteDeviceBdAddress)
   { }
 
   NS_IMETHOD
   Run()
   {
     MOZ_ASSERT(NS_IsMainThread());
 
-    if (sRequestedDeviceCountArray.IsEmpty()) {
-      // This is possible because the callback would be called after turning
-      // Bluetooth on.
-      return NS_OK;
-    }
 
     // Update to registered BluetoothDevice objects
     BluetoothSignal signal(NS_LITERAL_STRING("PropertyChanged"),
                            mRemoteDeviceBdAddress, mProps);
     nsRefPtr<DistributeBluetoothSignalTask>
       t = new DistributeBluetoothSignalTask(signal);
     if (NS_FAILED(NS_DispatchToMainThread(t))) {
       BT_WARNING("Failed to dispatch to main thread!");
       return NS_OK;
     }
 
+    // FetchUuids task
+    if (!sFetchUuidsRunnableArray.IsEmpty()) {
+      // mProps contains Address and Uuids only
+      DispatchBluetoothReply(sFetchUuidsRunnableArray[0],
+                             mProps[1].value() /* Uuids */,
+                             EmptyString());
+      sFetchUuidsRunnableArray.RemoveElementAt(0);
+
+      return NS_OK;
+    }
+
+    // GetDevices task
+    if (sRequestedDeviceCountArray.IsEmpty()) {
+      // This is possible because the callback would be called after turning
+      // Bluetooth on.
+      return NS_OK;
+    }
+
     // Use address as the index
     sRemoteDevicesPack.AppendElement(
       BluetoothNamedValue(mRemoteDeviceBdAddress, mProps));
 
     if (--sRequestedDeviceCountArray[0] == 0) {
       if (!sGetDeviceRunnableArray.IsEmpty()) {
         DispatchBluetoothReply(sGetDeviceRunnableArray[0],
                                sRemoteDevicesPack, EmptyString());
@@ -510,16 +434,17 @@ public:
     return NS_OK;
   }
 };
 
 /**
  * RemoteDevicePropertiesCallback will be called, as the following conditions:
  * 1. When BT is turning on, bluedroid automatically execute this callback
  * 2. When get_remote_device_properties()
+ * 3. When get_remote_services()
  */
 static void
 RemoteDevicePropertiesCallback(bt_status_t aStatus, bt_bdaddr_t *aBdAddress,
                                int aNumProperties, bt_property_t *aProperties)
 {
   MOZ_ASSERT(!NS_IsMainThread());
 
   InfallibleTArray<BluetoothNamedValue> props;
@@ -531,21 +456,32 @@ RemoteDevicePropertiesCallback(bt_status
   for (int i = 0; i < aNumProperties; ++i) {
     bt_property_t p = aProperties[i];
 
     if (p.type == BT_PROPERTY_BDNAME) {
       BluetoothValue propertyValue = NS_ConvertUTF8toUTF16((char*)p.val);
       BT_APPEND_NAMED_VALUE(props, "Name", propertyValue);
     } else if (p.type == BT_PROPERTY_CLASS_OF_DEVICE) {
       uint32_t cod = *(uint32_t*)p.val;
-      BT_APPEND_NAMED_VALUE(props, "Class", cod);
+      BT_APPEND_NAMED_VALUE(props, "Cod", cod);
+    } else if (p.type == BT_PROPERTY_UUIDS) {
+      nsTArray<nsString> uuids;
 
-      nsString icon;
-      ClassToIcon(cod, icon);
-      BT_APPEND_NAMED_VALUE(props, "Icon", icon);
+      // Construct a sorted uuid set
+      for (uint32_t j = 0; j < p.len / sizeof(bt_uuid_t); j++) {
+        nsAutoString uuid;
+        bt_uuid_t* pUuid = (bt_uuid_t*)p.val + j;
+        UuidToString(pUuid, uuid);
+
+        if (!uuids.Contains(uuid)) { // filter out duplicate uuids
+          uuids.InsertElementSorted(uuid);
+        }
+      }
+
+      BT_APPEND_NAMED_VALUE(props, "UUIDs", uuids);
     } else {
       BT_LOGD("Other non-handled device properties. Type: %d", p.type);
     }
   }
 
   // Redirect to main thread to avoid racing problem
   NS_DispatchToMainThread(
     new RemoteDevicePropertiesCallbackTask(props, remoteDeviceBdAddress));
@@ -569,22 +505,17 @@ DeviceFoundCallback(int aNumProperties, 
 
       BT_APPEND_NAMED_VALUE(propertiesArray, "Address", propertyValue);
     } else if (p.type == BT_PROPERTY_BDNAME) {
       propertyValue = NS_ConvertUTF8toUTF16((char*)p.val);
       BT_APPEND_NAMED_VALUE(propertiesArray, "Name", propertyValue);
     } else if (p.type == BT_PROPERTY_CLASS_OF_DEVICE) {
       uint32_t cod = *(uint32_t*)p.val;
       propertyValue = cod;
-      BT_APPEND_NAMED_VALUE(propertiesArray, "Class", propertyValue);
-
-      nsString icon;
-      ClassToIcon(cod, icon);
-      propertyValue = icon;
-      BT_APPEND_NAMED_VALUE(propertiesArray, "Icon", propertyValue);
+      BT_APPEND_NAMED_VALUE(propertiesArray, "Cod", cod);
     } else {
       BT_LOGD("Not handled remote device property: %d", p.type);
     }
   }
 
   BluetoothValue value = propertiesArray;
   BluetoothSignal signal(NS_LITERAL_STRING("DeviceFound"),
                          NS_LITERAL_STRING(KEY_DISCOVERY_HANDLE), value);
@@ -1246,16 +1177,61 @@ BluetoothServiceBluedroid::StopDiscovery
   ENSURE_BLUETOOTH_IS_READY(aRunnable, NS_OK);
 
   sChangeDiscoveryRunnableArray.AppendElement(aRunnable);
   sBtInterface->CancelDiscovery(new CancelDiscoveryResultHandler(aRunnable));
 
   return NS_OK;
 }
 
+class GetRemoteServicesResultHandler MOZ_FINAL : public BluetoothResultHandler
+{
+public:
+  GetRemoteServicesResultHandler(BluetoothReplyRunnable* aRunnable)
+  : mRunnable(aRunnable)
+  { }
+
+  void OnError(int aStatus) MOZ_OVERRIDE
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    sFetchUuidsRunnableArray.RemoveElement(mRunnable);
+    ReplyStatusError(mRunnable, aStatus, NS_LITERAL_STRING("FetchUuids"));
+  }
+
+private:
+  BluetoothReplyRunnable* mRunnable;
+};
+
+nsresult
+BluetoothServiceBluedroid::FetchUuidsInternal(
+  const nsAString& aDeviceAddress, BluetoothReplyRunnable* aRunnable)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  ENSURE_BLUETOOTH_IS_READY(aRunnable, NS_OK);
+
+  /*
+   * get_remote_services request will not be performed by bluedroid
+   * if it is currently discovering nearby remote devices.
+   */
+  if (sAdapterDiscovering) {
+    sBtInterface->CancelDiscovery(new CancelDiscoveryResultHandler(aRunnable));
+  }
+
+  bt_bdaddr_t addressType;
+  StringToBdAddressType(aDeviceAddress, &addressType);
+
+  sFetchUuidsRunnableArray.AppendElement(aRunnable);
+
+  sBtInterface->GetRemoteServices(&addressType,
+    new GetRemoteServicesResultHandler(aRunnable));
+
+  return NS_OK;
+}
+
 class SetAdapterPropertyResultHandler MOZ_FINAL : public BluetoothResultHandler
 {
 public:
   SetAdapterPropertyResultHandler(BluetoothReplyRunnable* aRunnable)
   : mRunnable(aRunnable)
   { }
 
   void OnError(int aStatus) MOZ_OVERRIDE
@@ -1761,9 +1737,8 @@ void
 BluetoothServiceBluedroid::IgnoreWaitingCall(BluetoothReplyRunnable* aRunnable)
 {
 }
 
 void
 BluetoothServiceBluedroid::ToggleCalls(BluetoothReplyRunnable* aRunnable)
 {
 }
-
--- a/dom/bluetooth2/bluedroid/BluetoothServiceBluedroid.h
+++ b/dom/bluetooth2/bluedroid/BluetoothServiceBluedroid.h
@@ -31,16 +31,20 @@ public:
   virtual nsresult
   GetConnectedDevicePropertiesInternal(uint16_t aProfileId,
                                        BluetoothReplyRunnable* aRunnable);
 
   virtual nsresult
   GetPairedDevicePropertiesInternal(const nsTArray<nsString>& aDeviceAddress,
                                     BluetoothReplyRunnable* aRunnable);
 
+  virtual nsresult
+  FetchUuidsInternal(const nsAString& aDeviceAddress,
+                     BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
+
   virtual nsresult StartDiscoveryInternal(BluetoothReplyRunnable* aRunnable);
   virtual nsresult StopDiscoveryInternal(BluetoothReplyRunnable* aRunnable);
 
   virtual nsresult
   SetProperty(BluetoothObjectType aType,
               const BluetoothNamedValue& aValue,
               BluetoothReplyRunnable* aRunnable);
 
--- a/dom/bluetooth2/bluedroid/BluetoothUtils.cpp
+++ b/dom/bluetooth2/bluedroid/BluetoothUtils.cpp
@@ -44,16 +44,39 @@ BdAddressTypeToString(bt_bdaddr_t* aBdAd
 
   sprintf(bdstr, "%02x:%02x:%02x:%02x:%02x:%02x",
           (int)addr[0],(int)addr[1],(int)addr[2],
           (int)addr[3],(int)addr[4],(int)addr[5]);
 
   aRetBdAddress = NS_ConvertUTF8toUTF16(bdstr);
 }
 
+void
+UuidToString(bt_uuid_t* aUuid, nsAString& aString) {
+  char uuidStr[37];
+
+  uint32_t uuid0, uuid4;
+  uint16_t uuid1, uuid2, uuid3, uuid5;
+
+  memcpy(&uuid0, &(aUuid->uu[0]), sizeof(uint32_t));
+  memcpy(&uuid1, &(aUuid->uu[4]), sizeof(uint16_t));
+  memcpy(&uuid2, &(aUuid->uu[6]), sizeof(uint16_t));
+  memcpy(&uuid3, &(aUuid->uu[8]), sizeof(uint16_t));
+  memcpy(&uuid4, &(aUuid->uu[10]), sizeof(uint32_t));
+  memcpy(&uuid5, &(aUuid->uu[14]), sizeof(uint16_t));
+
+  sprintf(uuidStr, "%.8x-%.4x-%.4x-%.4x-%.8x%.4x",
+          ntohl(uuid0), ntohs(uuid1),
+          ntohs(uuid2), ntohs(uuid3),
+          ntohl(uuid4), ntohs(uuid5));
+
+  aString.Truncate();
+  aString.AssignLiteral(uuidStr);
+}
+
 bool
 SetJsObject(JSContext* aContext,
             const BluetoothValue& aValue,
             JS::Handle<JSObject*> aObj)
 {
   MOZ_ASSERT(aContext && aObj);
 
   if (aValue.type() != BluetoothValue::TArrayOfBluetoothNamedValue) {
--- a/dom/bluetooth2/bluedroid/BluetoothUtils.h
+++ b/dom/bluetooth2/bluedroid/BluetoothUtils.h
@@ -21,16 +21,19 @@ class BluetoothReplyRunnable;
 void
 StringToBdAddressType(const nsAString& aBdAddress,
                       bt_bdaddr_t *aRetBdAddressType);
 
 void
 BdAddressTypeToString(bt_bdaddr_t* aBdAddressType,
                       nsAString& aRetBdAddress);
 
+void
+UuidToString(bt_uuid_t* aUuid, nsAString& aString);
+
 bool
 SetJsObject(JSContext* aContext,
             const BluetoothValue& aValue,
             JS::Handle<JSObject*> aObj);
 
 bool
 BroadcastSystemMessage(const nsAString& aType,
                        const BluetoothValue& aData);
--- a/dom/bluetooth2/bluez/BluetoothDBusService.cpp
+++ b/dom/bluetooth2/bluez/BluetoothDBusService.cpp
@@ -2809,16 +2809,23 @@ BluetoothDBusService::GetPairedDevicePro
                                                      GetPairedDevicesFilter,
                                                      aRunnable);
   Task* task = new ProcessRemainingDeviceAddressesTask(handler, aRunnable);
   DispatchToDBusThread(task);
 
   return NS_OK;
 }
 
+nsresult
+FetchUuidsInternal(const nsAString& aDeviceAddress,
+                   BluetoothReplyRunnable* aRunnable)
+{
+  return NS_OK;
+}
+
 class SetPropertyTask : public Task
 {
 public:
   SetPropertyTask(BluetoothObjectType aType,
                   const nsACString& aName,
                   BluetoothReplyRunnable* aRunnable)
     : mType(aType)
     , mName(aName)
--- a/dom/bluetooth2/bluez/BluetoothDBusService.h
+++ b/dom/bluetooth2/bluez/BluetoothDBusService.h
@@ -57,16 +57,20 @@ public:
   virtual nsresult
   GetConnectedDevicePropertiesInternal(uint16_t aServiceUuid,
                                        BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
 
   virtual nsresult
   GetPairedDevicePropertiesInternal(const nsTArray<nsString>& aDeviceAddresses,
                                     BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
 
+  virtual nsresult
+  FetchUuidsInternal(const nsAString& aDeviceAddress,
+                     BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
+
   virtual nsresult StartDiscoveryInternal(BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
 
   virtual nsresult StopDiscoveryInternal(BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
 
   virtual nsresult
   SetProperty(BluetoothObjectType aType,
               const BluetoothNamedValue& aValue,
               BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
--- a/dom/bluetooth2/ipc/BluetoothParent.cpp
+++ b/dom/bluetooth2/ipc/BluetoothParent.cpp
@@ -205,16 +205,18 @@ BluetoothParent::RecvPBluetoothRequestCo
     case Request::TPairRequest:
       return actor->DoRequest(aRequest.get_PairRequest());
     case Request::TUnpairRequest:
       return actor->DoRequest(aRequest.get_UnpairRequest());
     case Request::TPairedDevicePropertiesRequest:
       return actor->DoRequest(aRequest.get_PairedDevicePropertiesRequest());
     case Request::TConnectedDevicePropertiesRequest:
       return actor->DoRequest(aRequest.get_ConnectedDevicePropertiesRequest());
+    case Request::TFetchUuidsRequest:
+      return actor->DoRequest(aRequest.get_FetchUuidsRequest());
     case Request::TSetPinCodeRequest:
       return actor->DoRequest(aRequest.get_SetPinCodeRequest());
     case Request::TSetPasskeyRequest:
       return actor->DoRequest(aRequest.get_SetPasskeyRequest());
     case Request::TConfirmPairingConfirmationRequest:
       return actor->DoRequest(aRequest.get_ConfirmPairingConfirmationRequest());
     case Request::TDenyPairingConfirmationRequest:
       return actor->DoRequest(aRequest.get_DenyPairingConfirmationRequest());
@@ -427,29 +429,43 @@ BluetoothRequestParent::DoRequest(const 
   nsresult rv =
     mService->GetPairedDevicePropertiesInternal(aRequest.addresses(),
                                                 mReplyRunnable.get());
   NS_ENSURE_SUCCESS(rv, false);
   return true;
 }
 
 bool
-BluetoothRequestParent::DoRequest(const ConnectedDevicePropertiesRequest& aRequest)
+BluetoothRequestParent::DoRequest(
+    const ConnectedDevicePropertiesRequest& aRequest)
 {
   MOZ_ASSERT(mService);
   MOZ_ASSERT(mRequestType == Request::TConnectedDevicePropertiesRequest);
   nsresult rv =
     mService->GetConnectedDevicePropertiesInternal(aRequest.serviceUuid(),
                                                    mReplyRunnable.get());
   NS_ENSURE_SUCCESS(rv, false);
 
   return true;
 }
 
 bool
+BluetoothRequestParent::DoRequest(const FetchUuidsRequest& aRequest)
+{
+  MOZ_ASSERT(mService);
+  MOZ_ASSERT(mRequestType == Request::TFetchUuidsRequest);
+  nsresult rv =
+    mService->FetchUuidsInternal(aRequest.address(), mReplyRunnable.get());
+
+  NS_ENSURE_SUCCESS(rv, false);
+
+  return true;
+}
+
+bool
 BluetoothRequestParent::DoRequest(const SetPinCodeRequest& aRequest)
 {
   MOZ_ASSERT(mService);
   MOZ_ASSERT(mRequestType == Request::TSetPinCodeRequest);
 
   bool result =
     mService->SetPinCodeInternal(aRequest.path(),
                                  aRequest.pincode(),
--- a/dom/bluetooth2/ipc/BluetoothParent.h
+++ b/dom/bluetooth2/ipc/BluetoothParent.h
@@ -149,20 +149,24 @@ protected:
   bool
   DoRequest(const PairRequest& aRequest);
 
   bool
   DoRequest(const UnpairRequest& aRequest);
 
   bool
   DoRequest(const PairedDevicePropertiesRequest& aRequest);
+
   bool
   DoRequest(const ConnectedDevicePropertiesRequest& aRequest);
 
   bool
+  DoRequest(const FetchUuidsRequest& aRequest);
+
+  bool
   DoRequest(const SetPinCodeRequest& aRequest);
 
   bool
   DoRequest(const SetPasskeyRequest& aRequest);
 
   bool
   DoRequest(const ConfirmPairingConfirmationRequest& aRequest);
 
--- a/dom/bluetooth2/ipc/BluetoothServiceChildProcess.cpp
+++ b/dom/bluetooth2/ipc/BluetoothServiceChildProcess.cpp
@@ -120,29 +120,38 @@ BluetoothServiceChildProcess::StopIntern
 nsresult
 BluetoothServiceChildProcess::GetConnectedDevicePropertiesInternal(
                                               uint16_t aServiceUuid,
                                               BluetoothReplyRunnable* aRunnable)
 {
   SendRequest(aRunnable, ConnectedDevicePropertiesRequest(aServiceUuid));
   return NS_OK;
 }
+
 nsresult
 BluetoothServiceChildProcess::GetPairedDevicePropertiesInternal(
                                      const nsTArray<nsString>& aDeviceAddresses,
                                      BluetoothReplyRunnable* aRunnable)
 {
   PairedDevicePropertiesRequest request;
   request.addresses().AppendElements(aDeviceAddresses);
 
   SendRequest(aRunnable, request);
   return NS_OK;
 }
 
 nsresult
+BluetoothServiceChildProcess::FetchUuidsInternal(
+    const nsAString& aDeviceAddress, BluetoothReplyRunnable* aRunnable)
+{
+  SendRequest(aRunnable, FetchUuidsRequest(nsString(aDeviceAddress)));
+  return NS_OK;
+}
+
+nsresult
 BluetoothServiceChildProcess::StopDiscoveryInternal(
                                               BluetoothReplyRunnable* aRunnable)
 {
   SendRequest(aRunnable, StopDiscoveryRequest());
   return NS_OK;
 }
 
 nsresult
--- a/dom/bluetooth2/ipc/BluetoothServiceChildProcess.h
+++ b/dom/bluetooth2/ipc/BluetoothServiceChildProcess.h
@@ -57,16 +57,20 @@ public:
                                     BluetoothReplyRunnable* aRunnable)
                                     MOZ_OVERRIDE;
 
   virtual nsresult
   GetConnectedDevicePropertiesInternal(uint16_t aServiceUuid,
                                        BluetoothReplyRunnable* aRunnable)
                                        MOZ_OVERRIDE;
   virtual nsresult
+  FetchUuidsInternal(const nsAString& aDeviceAddress,
+                     BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
+
+  virtual nsresult
   StopDiscoveryInternal(BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
 
   virtual nsresult
   StartDiscoveryInternal(BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
 
   virtual nsresult
   SetProperty(BluetoothObjectType aType,
               const BluetoothNamedValue& aValue,
--- a/dom/bluetooth2/ipc/PBluetooth.ipdl
+++ b/dom/bluetooth2/ipc/PBluetooth.ipdl
@@ -91,16 +91,21 @@ struct PairedDevicePropertiesRequest
   nsString[] addresses;
 };
 
 struct ConnectedDevicePropertiesRequest
 {
   uint16_t serviceUuid;
 };
 
+struct FetchUuidsRequest
+{
+  nsString address;
+};
+
 struct ConnectRequest
 {
   nsString address;
   uint32_t cod;
   uint16_t serviceUuid;
 };
 
 struct DisconnectRequest
@@ -183,16 +188,17 @@ union Request
   PairRequest;
   UnpairRequest;
   SetPinCodeRequest;
   SetPasskeyRequest;
   ConfirmPairingConfirmationRequest;
   DenyPairingConfirmationRequest;
   ConnectedDevicePropertiesRequest;
   PairedDevicePropertiesRequest;
+  FetchUuidsRequest;
   ConnectRequest;
   DisconnectRequest;
   SendFileRequest;
   StopSendingFileRequest;
   ConfirmReceivingFileRequest;
   DenyReceivingFileRequest;
   ConnectScoRequest;
   DisconnectScoRequest;
--- a/dom/browser-element/BrowserElementChildPreload.js
+++ b/dom/browser-element/BrowserElementChildPreload.js
@@ -181,20 +181,31 @@ BrowserElementChild.prototype = {
                      /* wantsUntrusted = */ false);
 
     addEventListener('DOMLinkAdded',
                      this._linkAddedHandler.bind(this),
                      /* useCapture = */ true,
                      /* wantsUntrusted = */ false);
 
     addEventListener('DOMMetaAdded',
-                     this._metaAddedHandler.bind(this),
+                     this._metaChangedHandler.bind(this),
                      /* useCapture = */ true,
                      /* wantsUntrusted = */ false);
 
+    addEventListener('DOMMetaChanged',
+                     this._metaChangedHandler.bind(this),
+                     /* useCapture = */ true,
+                     /* wantsUntrusted = */ false);
+
+    addEventListener('DOMMetaRemoved',
+                     this._metaChangedHandler.bind(this),
+                     /* useCapture = */ true,
+                     /* wantsUntrusted = */ false);
+
+
     // This listens to unload events from our message manager, but /not/ from
     // the |content| window.  That's because the window's unload event doesn't
     // bubble, and we're not using a capturing listener.  If we'd used
     // useCapture == true, we /would/ hear unload events from the window, which
     // is not what we want!
     addEventListener('unload',
                      this._unloadHandler.bind(this),
                      /* useCapture = */ false,
@@ -508,62 +519,88 @@ BrowserElementChild.prototype = {
     e.target.rel.split(' ').forEach(function(x) {
       let token = x.toLowerCase();
       if (handlers[token]) {
         handlers[token](e);
       }
     }, this);
   },
 
-  _metaAddedHandler: function(e) {
+  _metaChangedHandler: function(e) {
     let win = e.target.ownerDocument.defaultView;
     // Ignore metas which don't come from the top-level
     // <iframe mozbrowser> window.
     if (win != content) {
       debug('Not top level!');
       return;
     }
 
     if (!e.target.name) {
       return;
     }
 
-    debug('Got metaAdded: (' + e.target.name + ') ' + e.target.content);
-    if (e.target.name == 'application-name') {
-      let meta = { name: e.target.name,
-                   content: e.target.content };
+    debug('Got metaChanged: (' + e.target.name + ') ' + e.target.content);
 
-      let lang;
-      let elm;
+    let handlers = {
+      'theme-color': this._themeColorChangedHandler,
+      'application-name': this._applicationNameChangedHandler
+    };
+
+    let handler = handlers[e.target.name];
+    if (handler) {
+      handler(e.type, e.target);
+    }
+  },
 
-      for (elm = e.target;
-           !lang && elm && elm.nodeType == e.target.ELEMENT_NODE;
-           elm = elm.parentNode) {
-        if (elm.hasAttribute('lang')) {
-          lang = elm.getAttribute('lang');
-          continue;
-        }
+  _applicationNameChangedHandler: function(eventType, target) {
+    if (eventType !== 'DOMMetaAdded') {
+      // Bug 1037448 - Decide what to do when <meta name="application-name">
+      // changes
+      return;
+    }
+
+    let meta = { name: 'application-name',
+                 content: target.content };
 
-        if (elm.hasAttributeNS('http://www.w3.org/XML/1998/namespace', 'lang')) {
-          lang = elm.getAttributeNS('http://www.w3.org/XML/1998/namespace', 'lang');
-          continue;
-        }
+    let lang;
+    let elm;
+
+    for (elm = target;
+         !lang && elm && elm.nodeType == target.ELEMENT_NODE;
+         elm = elm.parentNode) {
+      if (elm.hasAttribute('lang')) {
+        lang = elm.getAttribute('lang');
+        continue;
       }
 
-      // No lang has been detected.
-      if (!lang && elm.nodeType == e.target.DOCUMENT_NODE) {
-        lang = elm.contentLanguage;
+      if (elm.hasAttributeNS('http://www.w3.org/XML/1998/namespace', 'lang')) {
+        lang = elm.getAttributeNS('http://www.w3.org/XML/1998/namespace', 'lang');
+        continue;
       }
+    }
+
+    // No lang has been detected.
+    if (!lang && elm.nodeType == target.DOCUMENT_NODE) {
+      lang = elm.contentLanguage;
+    }
 
-      if (lang) {
-        meta.lang = lang;
-      }
+    if (lang) {
+      meta.lang = lang;
+    }
+
+    sendAsyncMsg('metachange', meta);
+  },
 
-      sendAsyncMsg('metachange', meta);
-    }
+  _themeColorChangedHandler: function(eventType, target) {
+    let meta = {
+      name: 'theme-color',
+      content: target.content,
+      type: eventType.replace('DOMMeta', '').toLowerCase()
+    };
+    sendAsyncMsg('metachange', meta);
   },
 
   _addMozAfterPaintHandler: function(callback) {
     function onMozAfterPaint() {
       let uri = docShell.QueryInterface(Ci.nsIWebNavigation).currentURI;
       if (uri.spec != "about:blank") {
         debug("Got afterpaint event: " + uri.spec);
         removeEventListener('MozAfterPaint', onMozAfterPaint,
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/browserElement_ThemeColor.js
@@ -0,0 +1,71 @@
+/* Any copyright is dedicated to the public domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that the onmozbrowsermetachange event for theme-color works.
+"use strict";
+
+SimpleTest.waitForExplicitFinish();
+browserElementTestHelpers.setEnabledPref(true);
+browserElementTestHelpers.addPermission();
+
+function runTest() {
+  function loadFrameScript(script) {
+    SpecialPowers.getBrowserFrameMessageManager(iframe1)
+                 .loadFrameScript("data:," + script,
+                                  /* allowDelayedLoad = */ false);
+  }
+
+  let iframe1 = document.createElement('iframe');
+  SpecialPowers.wrap(iframe1).mozbrowser = true;
+  iframe1.src = "http://test/tests/dom/browser-element/mochitest/file_browserElement_ThemeColor.html";
+  iframe1.addEventListener('mozbrowsermetachange', tests);
+  document.body.appendChild(iframe1);
+
+  let numMetaChanges = 0;
+  function tests(e) {
+    let detail = e.detail;
+
+    switch (numMetaChanges++) {
+      case 0: {
+        is(detail.name, 'theme-color', 'name matches');
+        is(detail.content, 'pink', 'content matches');
+        is(detail.type, 'added', 'type matches');
+
+        let script =
+          "var meta = content.document.head.querySelector('meta');" +
+          "meta.content = 'green';";
+        loadFrameScript(script);
+        break;
+      }
+
+      case 1: {
+        is(detail.name, 'theme-color', 'name matches');
+        is(detail.content, 'green', 'content matches');
+        is(detail.type, 'changed', 'type matches');
+
+        let script =
+          "var meta = content.document.head.querySelector('meta');" +
+          "meta.parentNode.removeChild(meta);";
+        loadFrameScript(script);
+        break;
+      }
+
+      case 2: {
+        is(detail.name, 'theme-color', 'name matches');
+        is(detail.content, 'green', 'content matches');
+        is(detail.type, 'removed', 'type matches');
+
+        SimpleTest.finish();
+        break;
+      }
+
+      default: {
+        ok(false, 'Too many metachange events.');
+        break;
+      }
+    }
+  };
+}
+
+window.addEventListener('testready', runTest);
+
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/file_browserElement_ThemeColor.html
@@ -0,0 +1,8 @@
+<html>
+  <head>
+    <meta name="theme-color" content="pink">
+  </head>
+
+  <body>
+  </body>
+</html>
--- a/dom/browser-element/mochitest/mochitest-oop.ini
+++ b/dom/browser-element/mochitest/mochitest-oop.ini
@@ -3,16 +3,17 @@
 # so we don't run that test on platforms which don't support OOP tests.
 # OOP tests don't work on native-fennec (bug 774939).
 # Bug 960345 - Disabled on OSX debug for frequent crashes.
 skip-if = os == "android" || (toolkit == "cocoa" && debug) || (buildapp == 'b2g' && (toolkit != 'gonk' || debug)) || e10s
 support-files =
   browserElement_OpenMixedProcess.js
   file_browserElement_OpenMixedProcess.html
 
+[test_browserElement_oop_ThemeColor.html]
 [test_browserElement_inproc_ErrorSecurity.html]
 skip-if = toolkit=='gonk'
 [test_browserElement_inproc_OpenMixedProcess.html]
 skip-if = toolkit=='gonk' || (toolkit == 'gonk' && !debug)
 [test_browserElement_oop_Alert.html]
 [test_browserElement_oop_AlertInFrame.html]
 [test_browserElement_oop_AppFramePermission.html]
 skip-if = (toolkit == 'gonk' && !debug)
--- a/dom/browser-element/mochitest/mochitest.ini
+++ b/dom/browser-element/mochitest/mochitest.ini
@@ -6,16 +6,17 @@ support-files =
   browserElementTestHelpers.js
   browserElement_Alert.js
   browserElement_AlertInFrame.js
   browserElement_AppFramePermission.js
   browserElement_AppWindowNamespace.js
   browserElement_Auth.js
   browserElement_BackForward.js
   browserElement_BadScreenshot.js
+  browserElement_ThemeColor.js
   browserElement_BrowserWindowNamespace.js
   browserElement_BrowserWindowResize.js
   browserElement_Close.js
   browserElement_CloseApp.js
   browserElement_CloseFromOpener.js
   browserElement_ContextmenuEvents.js
   browserElement_CookiesNotThirdParty.js
   browserElement_DOMRequestError.js
@@ -63,16 +64,17 @@ support-files =
   browserElement_XFrameOptions.js
   browserElement_XFrameOptionsAllowFrom.js
   browserElement_XFrameOptionsDeny.js
   browserElement_XFrameOptionsSameOrigin.js
   file_browserElement_AlertInFrame.html
   file_browserElement_AlertInFrame_Inner.html
   file_browserElement_AppFramePermission.html
   file_browserElement_AppWindowNamespace.html
+  file_browserElement_ThemeColor.html
   file_browserElement_BrowserWindowNamespace.html
   file_browserElement_CloseApp.html
   file_browserElement_CloseFromOpener.html
   file_browserElement_CookiesNotThirdParty.html
   file_browserElement_ForwardName.html
   file_browserElement_FrameWrongURI.html
   file_browserElement_LoadEvents.html
   file_browserElement_Metachange.sjs
@@ -108,16 +110,17 @@ support-files =
 
 # Note: browserElementTestHelpers.js looks at the test's filename to determine
 # whether the test should be OOP.  "_oop_" signals OOP, "_inproc_" signals in
 # process.  Default is OOP.
 [test_browserElement_NoAttr.html]
 [test_browserElement_NoPref.html]
 [test_browserElement_NoPermission.html]
 [test_browserElement_inproc_Alert.html]
+[test_browserElement_inproc_ThemeColor.html]
 skip-if = buildapp == 'b2g'
 [test_browserElement_inproc_AlertInFrame.html]
 [test_browserElement_inproc_AppFramePermission.html]
 skip-if = toolkit == 'android' || buildapp == 'b2g'
 [test_browserElement_inproc_AppWindowNamespace.html]
 skip-if = toolkit == 'android' || buildapp == 'b2g' # android(TIMED_OUT, bug 783509) androidx86(TIMED_OUT, bug 783509)
 [test_browserElement_inproc_Auth.html]
 skip-if = buildapp == 'b2g'
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/test_browserElement_inproc_ThemeColor.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1013913
+-->
+<head>
+  <title>Test for Bug 1013913</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="browserElementTestHelpers.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1013913">Mozilla Bug 1013913</a>
+
+<script type="application/javascript;version=1.7" src="browserElement_ThemeColor.js">
+</script>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/test_browserElement_oop_ThemeColor.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1013913
+-->
+<head>
+  <title>Test for Bug 1013913</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="browserElementTestHelpers.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1013913">Mozilla Bug 1013913</a>
+
+<script type="application/javascript;version=1.7" src="browserElement_ThemeColor.js">
+</script>
+
+</body>
+</html>
--- a/dom/camera/CameraPreferences.cpp
+++ b/dom/camera/CameraPreferences.cpp
@@ -17,41 +17,71 @@ static StaticAutoPtr<Monitor> sPrefMonit
 
 StaticAutoPtr<nsCString> CameraPreferences::sPrefTestEnabled;
 StaticAutoPtr<nsCString> CameraPreferences::sPrefHardwareTest;
 StaticAutoPtr<nsCString> CameraPreferences::sPrefGonkParameters;
 
 nsresult CameraPreferences::sPrefCameraControlMethodErrorOverride = NS_OK;
 nsresult CameraPreferences::sPrefCameraControlAsyncErrorOverride = NS_OK;
 
+uint32_t CameraPreferences::sPrefCameraControlLowMemoryThresholdMB = 0;
+
+bool CameraPreferences::sPrefCameraParametersIsLowMemory = false;
+
+#ifdef CAMERAPREFERENCES_HAVE_SEPARATE_UINT32_AND_NSRESULT
 /* static */
 nsresult
 CameraPreferences::UpdatePref(const char* aPref, nsresult& aVal)
 {
   uint32_t val;
   nsresult rv = Preferences::GetUint(aPref, &val);
   if (NS_SUCCEEDED(rv)) {
     aVal = static_cast<nsresult>(val);
   }
   return rv;
 }
+#endif
+
+/* static */
+nsresult
+CameraPreferences::UpdatePref(const char* aPref, uint32_t& aVal)
+{
+  uint32_t val;
+  nsresult rv = Preferences::GetUint(aPref, &val);
+  if (NS_SUCCEEDED(rv)) {
+    aVal = val;
+  }
+  return rv;
+}
 
 /* static */
 nsresult
 CameraPreferences::UpdatePref(const char* aPref, nsACString& aVal)
 {
   nsCString val;
   nsresult rv = Preferences::GetCString(aPref, &val);
   if (NS_SUCCEEDED(rv)) {
     aVal = val;
   }
   return rv;
 }
 
 /* static */
+nsresult
+CameraPreferences::UpdatePref(const char* aPref, bool& aVal)
+{
+  bool val;
+  nsresult rv = Preferences::GetBool(aPref, &val);
+  if (NS_SUCCEEDED(rv)) {
+    aVal = val;
+  }
+  return rv;
+}
+
+/* static */
 CameraPreferences::Pref CameraPreferences::sPrefs[] = {
   {
     "camera.control.test.enabled",
     kPrefValueIsCString,
     { &sPrefTestEnabled }
   },
   {
     "camera.control.test.hardware",
@@ -62,24 +92,34 @@ CameraPreferences::Pref CameraPreference
   {
     "camera.control.test.hardware.gonk.parameters",
     kPrefValueIsCString,
     { &sPrefGonkParameters }
   },
 #endif
   {
     "camera.control.test.method.error",
-    kPrefValueIsNSResult,
+    kPrefValueIsNsResult,
     { &sPrefCameraControlMethodErrorOverride }
   },
   {
     "camera.control.test.async.error",
-    kPrefValueIsNSResult,
+    kPrefValueIsNsResult,
     { &sPrefCameraControlAsyncErrorOverride }
   },
+  {
+    "camera.control.test.is_low_memory",
+    kPrefValueIsBoolean,
+    { &sPrefCameraParametersIsLowMemory }
+  },
+  {
+    "camera.control.low_memory_thresholdMB",
+    kPrefValueIsUint32,
+    { &sPrefCameraControlLowMemoryThresholdMB }
+  },
 };
 
 /* static */
 uint32_t
 CameraPreferences::PrefToIndex(const char* aPref)
 {
   for (uint32_t i = 0; i < ArrayLength(sPrefs); ++i) {
     if (strcmp(aPref, sPrefs[i].mPref) == 0) {
@@ -99,36 +139,59 @@ CameraPreferences::PreferenceChanged(con
   if (i == kPrefNotFound) {
     DOM_CAMERA_LOGE("Preference '%s' is not tracked by CameraPreferences\n", aPref);
     return;
   }
 
   Pref& p = sPrefs[i];
   nsresult rv;
   switch (p.mValueType) {
-    case kPrefValueIsNSResult:
+    case kPrefValueIsNsResult:
+    #ifdef CAMERAPREFERENCES_HAVE_SEPARATE_UINT32_AND_NSRESULT
       {
         nsresult& v = *p.mValue.mAsNsResult;
         rv = UpdatePref(aPref, v);
         if (NS_SUCCEEDED(rv)) {
           DOM_CAMERA_LOGI("Preference '%s' has changed, 0x%x\n", aPref, v);
         }
       }
       break;
+    #endif
+
+    case kPrefValueIsUint32:
+      {
+        uint32_t& v = *p.mValue.mAsUint32;
+        rv = UpdatePref(aPref, v);
+        if (NS_SUCCEEDED(rv)) {
+          DOM_CAMERA_LOGI("Preference '%s' has changed, %u\n", aPref, v);
+        }
+      }
+      break;
 
     case kPrefValueIsCString:
       {
         nsCString& v = **p.mValue.mAsCString;
         rv = UpdatePref(aPref, v);
         if (NS_SUCCEEDED(rv)) {
           DOM_CAMERA_LOGI("Preference '%s' has changed, '%s'\n", aPref, v.get());
         }
       }
       break;
 
+    case kPrefValueIsBoolean:
+      {
+        bool& v = *p.mValue.mAsBoolean;
+        rv = UpdatePref(aPref, v);
+        if (NS_SUCCEEDED(rv)) {
+          DOM_CAMERA_LOGI("Preference '%s' has changed, %s\n",
+            aPref, v ? "true" : "false");
+        }
+      }
+      break;
+
     default:
       MOZ_ASSERT_UNREACHABLE("Unhandled preference value type!");
       return;
   }
 
   if (NS_FAILED(rv)) {
     DOM_CAMERA_LOGE("Failed to get pref '%s' (0x%x)\n", aPref, rv);
   }
@@ -206,35 +269,83 @@ CameraPreferences::GetPref(const char* a
     return false;
   }
 
   DOM_CAMERA_LOGI("Preference '%s', got '%s'\n", aPref, (*s)->get());
   aVal = **s;
   return true;
 }
 
+#ifdef CAMERAPREFERENCES_HAVE_SEPARATE_UINT32_AND_NSRESULT
 /* static */
 bool
 CameraPreferences::GetPref(const char* aPref, nsresult& aVal)
 {
   MOZ_ASSERT(sPrefMonitor, "sPrefMonitor missing in CameraPreferences::GetPref()");
   MonitorAutoLock mon(*sPrefMonitor);
 
   uint32_t i = PrefToIndex(aPref);
   if (i == kPrefNotFound || i >= ArrayLength(sPrefs)) {
     DOM_CAMERA_LOGW("Preference '%s' is not tracked by CameraPreferences\n", aPref);
     return false;
   }
-  if (sPrefs[i].mValueType != kPrefValueIsNSResult) {
+  if (sPrefs[i].mValueType != kPrefValueIsNsResult) {
     DOM_CAMERA_LOGW("Preference '%s' is not an nsresult type\n", aPref);
     return false;
   }
 
   nsresult v = *sPrefs[i].mValue.mAsNsResult;
   if (v == NS_OK) {
-    DOM_CAMERA_LOGI("Preference '%s' is not set\n", aPref);
+    DOM_CAMERA_LOGW("Preference '%s' is not set\n", aPref);
     return false;
   }
 
   DOM_CAMERA_LOGI("Preference '%s', got 0x%x\n", aPref, v);
   aVal = v;
   return true;
 }
+#endif
+
+/* static */
+bool
+CameraPreferences::GetPref(const char* aPref, uint32_t& aVal)
+{
+  MOZ_ASSERT(sPrefMonitor, "sPrefMonitor missing in CameraPreferences::GetPref()");
+  MonitorAutoLock mon(*sPrefMonitor);
+
+  uint32_t i = PrefToIndex(aPref);
+  if (i == kPrefNotFound || i >= ArrayLength(sPrefs)) {
+    DOM_CAMERA_LOGW("Preference '%s' is not tracked by CameraPreferences\n", aPref);
+    return false;
+  }
+  if (sPrefs[i].mValueType != kPrefValueIsUint32) {
+    DOM_CAMERA_LOGW("Preference '%s' is not a uint32_t type\n", aPref);
+    return false;
+  }
+
+  uint32_t v = *sPrefs[i].mValue.mAsUint32;
+  DOM_CAMERA_LOGI("Preference '%s', got %u\n", aPref, v);
+  aVal = v;
+  return true;
+}
+
+/* static */
+bool
+CameraPreferences::GetPref(const char* aPref, bool& aVal)
+{
+  MOZ_ASSERT(sPrefMonitor, "sPrefMonitor missing in CameraPreferences::GetPref()");
+  MonitorAutoLock mon(*sPrefMonitor);
+
+  uint32_t i = PrefToIndex(aPref);
+  if (i == kPrefNotFound || i >= ArrayLength(sPrefs)) {
+    DOM_CAMERA_LOGW("Preference '%s' is not tracked by CameraPreferences\n", aPref);
+    return false;
+  }
+  if (sPrefs[i].mValueType != kPrefValueIsBoolean) {
+    DOM_CAMERA_LOGW("Preference '%s' is not a boolean type\n", aPref);
+    return false;
+  }
+
+  bool v = *sPrefs[i].mValue.mAsBoolean;
+  DOM_CAMERA_LOGI("Preference '%s', got %s\n", aPref, v ? "true" : "false");
+  aVal = v;
+  return true;
+}
--- a/dom/camera/CameraPreferences.h
+++ b/dom/camera/CameraPreferences.h
@@ -3,64 +3,87 @@
  * 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 DOM_CAMERA_CAMERAPREFERENCES_H
 #define DOM_CAMERA_CAMERAPREFERENCES_H
 
 #include "nsString.h"
 
+#if defined(MOZ_HAVE_CXX11_STRONG_ENUMS) || defined(MOZ_HAVE_CXX11_ENUM_TYPE)
+// Older compilers that don't support strongly-typed enums
+// just typedef uint32_t to nsresult, which results in conflicting
+// overloaded members in CameraPreferences.
+#define CAMERAPREFERENCES_HAVE_SEPARATE_UINT32_AND_NSRESULT
+#endif
+
 namespace mozilla {
 
 template<class T> class StaticAutoPtr;
 
 class CameraPreferences
 {
 public:
   static bool Initialize();
   static void Shutdown();
 
   static bool GetPref(const char* aPref, nsACString& aVal);
+#ifdef CAMERAPREFERENCES_HAVE_SEPARATE_UINT32_AND_NSRESULT
   static bool GetPref(const char* aPref, nsresult& aVal);
+#endif
+  static bool GetPref(const char* aPref, uint32_t& aVal);
+  static bool GetPref(const char* aPref, bool& aVal);
 
 protected:
   static const uint32_t kPrefNotFound = UINT32_MAX;
   static uint32_t PrefToIndex(const char* aPref);
 
   static void PreferenceChanged(const char* aPref, void* aClosure);
+#ifdef CAMERAPREFERENCES_HAVE_SEPARATE_UINT32_AND_NSRESULT
   static nsresult UpdatePref(const char* aPref, nsresult& aVar);
+#endif
+  static nsresult UpdatePref(const char* aPref, uint32_t& aVar);
   static nsresult UpdatePref(const char* aPref, nsACString& aVar);
+  static nsresult UpdatePref(const char* aPref, bool& aVar);
 
   enum PrefValueType {
-    kPrefValueIsNSResult,
-    kPrefValueIsCString
+    kPrefValueIsNsResult,
+    kPrefValueIsUint32,
+    kPrefValueIsCString,
+    kPrefValueIsBoolean
   };
   struct Pref {
     const char* const           mPref;
     PrefValueType               mValueType;
     union {
       // The 'mAsVoid' member must be first and is required to allow 'mValue'
       // to be initialized with any pointer type, as not all of our platforms
       // support the use of designated initializers; in their absence, only
       // the first element of a union can be statically initialized, and
       // 'void*' lets us stuff any pointer type into it.
       void*                     mAsVoid;
       StaticAutoPtr<nsCString>* mAsCString;
       nsresult*                 mAsNsResult;
+      uint32_t*                 mAsUint32;
+      bool*                     mAsBoolean;
     } mValue;
   };
   static Pref sPrefs[];
 
   static StaticAutoPtr<nsCString> sPrefTestEnabled;
   static StaticAutoPtr<nsCString> sPrefHardwareTest;
   static StaticAutoPtr<nsCString> sPrefGonkParameters;
 
   static nsresult sPrefCameraControlMethodErrorOverride;
   static nsresult sPrefCameraControlAsyncErrorOverride;
 
+  static uint32_t sPrefCameraControlLowMemoryThresholdMB;
+
+  static bool sPrefCameraParametersIsLowMemory;
+
 private:
   // static class only
   CameraPreferences();
   ~CameraPreferences();
 };
 
 } // namespace mozilla
 
--- a/dom/camera/CameraPreviewMediaStream.cpp
+++ b/dom/camera/CameraPreviewMediaStream.cpp
@@ -98,17 +98,17 @@ CameraPreviewMediaStream::AddListener(Me
 void
 CameraPreviewMediaStream::RemoveListener(MediaStreamListener* aListener)
 {
   MutexAutoLock lock(mMutex);
 
   MediaStreamGraph* gm = MediaStreamGraph::GetInstance();
   nsRefPtr<MediaStreamListener> listener(aListener);
   mListeners.RemoveElement(aListener);
-  listener->NotifyRemoved(gm);
+  listener->NotifyEvent(gm, MediaStreamListener::EVENT_REMOVED);
 }
 
 void
 CameraPreviewMediaStream::Destroy()
 {
   MutexAutoLock lock(mMutex);
   DestroyImpl();
 }
--- a/dom/camera/GonkCameraParameters.cpp
+++ b/dom/camera/GonkCameraParameters.cpp
@@ -11,22 +11,50 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #include "GonkCameraParameters.h"
 #include "camera/CameraParameters.h"
+#include "CameraPreferences.h"
 #include "ICameraControl.h"
 #include "CameraCommon.h"
+#include "mozilla/Hal.h"
 
 using namespace mozilla;
 using namespace android;
 
+/* static */ bool
+GonkCameraParameters::IsLowMemoryPlatform()
+{
+  bool testIsLowMem = false;
+  CameraPreferences::GetPref("camera.control.test.is_low_memory", testIsLowMem);
+  if (testIsLowMem) {
+    NS_WARNING("Forcing low-memory platform camera preferences");
+    return true;
+  }
+
+  uint32_t lowMemoryThresholdBytes = 0;
+  CameraPreferences::GetPref("camera.control.low_memory_thresholdMB",
+                             lowMemoryThresholdBytes);
+  lowMemoryThresholdBytes *= 1024 * 1024;
+  if (lowMemoryThresholdBytes) {
+    uint32_t totalMemoryBytes = hal::GetTotalSystemMemory();
+    if (totalMemoryBytes < lowMemoryThresholdBytes) {
+      DOM_CAMERA_LOGI("Low-memory platform with %d bytes of RAM (threshold: <%d bytes)\n",
+        totalMemoryBytes, lowMemoryThresholdBytes);
+      return true;
+    }
+  }
+
+  return false;
+}
+
 /* static */ const char*
 GonkCameraParameters::Parameters::GetTextKey(uint32_t aKey)
 {
   switch (aKey) {
     case CAMERA_PARAM_PREVIEWSIZE:
       return KEY_PREVIEW_SIZE;
     case CAMERA_PARAM_PREVIEWFORMAT:
       return KEY_PREVIEW_FORMAT;
@@ -241,44 +269,65 @@ GonkCameraParameters::Initialize()
   }
 
   // The return code from GetListAsArray() doesn't matter. If it fails,
   // the isoModes array will be empty, and the subsequent loop won't
   // execute.
   nsString s;
   nsTArray<nsCString> isoModes;
   GetListAsArray(CAMERA_PARAM_SUPPORTED_ISOMODES, isoModes);
-  for (uint32_t i = 0; i < isoModes.Length(); ++i) {
+  for (nsTArray<nsCString>::size_type i = 0; i < isoModes.Length(); ++i) {
     rv = MapIsoFromGonk(isoModes[i].get(), s);
     if (NS_FAILED(rv)) {
       DOM_CAMERA_LOGW("Unrecognized ISO mode value '%s'\n", isoModes[i].get());
       continue;
     }
     *mIsoModes.AppendElement() = s;
     mIsoModeMap.Put(s, new nsCString(isoModes[i]));
   }
 
+  GetListAsArray(CAMERA_PARAM_SUPPORTED_SCENEMODES, mSceneModes);
+  if (IsLowMemoryPlatform()) {
+    bool hdrRemoved = false;
+    while (mSceneModes.RemoveElement(NS_LITERAL_STRING("hdr"))) {
+      hdrRemoved = true;
+    }
+    if (hdrRemoved) {
+      DOM_CAMERA_LOGI("Disabling HDR support due to low memory\n");
+    }
+  }
+
   mInitialized = true;
   return NS_OK;
 }
 
 // Handle nsAStrings
 nsresult
 GonkCameraParameters::SetTranslated(uint32_t aKey, const nsAString& aValue)
 {
-  if (aKey == CAMERA_PARAM_ISOMODE) {
-    nsAutoCString v;
-    nsresult rv = MapIsoToGonk(aValue, v);
-    if (NS_FAILED(rv)) {
-      return rv;
-    }
-    return SetImpl(aKey, v.get());
+  switch (aKey) {
+    case CAMERA_PARAM_ISOMODE:
+      {
+        nsAutoCString v;
+        nsresult rv = MapIsoToGonk(aValue, v);
+        if (NS_FAILED(rv)) {
+          return rv;
+        }
+        return SetImpl(aKey, v.get());
+      }
+
+    case CAMERA_PARAM_SCENEMODE:
+      if (mSceneModes.IndexOf(aValue) == nsTArray<nsString>::NoIndex) {
+        return NS_ERROR_INVALID_ARG;
+      }
+      // fallthrough
+
+    default:
+      return SetImpl(aKey, NS_ConvertUTF16toUTF8(aValue).get());
   }
-
-  return SetImpl(aKey, NS_ConvertUTF16toUTF8(aValue).get());
 }
 
 nsresult
 GonkCameraParameters::GetTranslated(uint32_t aKey, nsAString& aValue)
 {
   const char* val;
   nsresult rv = GetImpl(aKey, val);
   if (NS_FAILED(rv)) {
@@ -615,17 +664,17 @@ GonkCameraParameters::SetTranslated(uint
   }
 
   return SetImpl(aKey, aValue);
 }
 
 nsresult
 GonkCameraParameters::GetTranslated(uint32_t aKey, double& aValue)
 {
-  double val;
+  double val = 0.0; // initialize to keep the compiler happy [-Wmaybe-uninitialized]
   int index = 0;
   double focusDistance[3];
   const char* s;
   nsresult rv;
 
   switch (aKey) {
     case CAMERA_PARAM_ZOOM:
       rv = GetImpl(aKey, index);
@@ -835,22 +884,28 @@ GonkCameraParameters::GetListAsArray(uin
   }
 
   return NS_OK;
 }
 
 nsresult
 GonkCameraParameters::GetTranslated(uint32_t aKey, nsTArray<nsString>& aValues)
 {
-  if (aKey == CAMERA_PARAM_SUPPORTED_ISOMODES) {
-    aValues = mIsoModes;
-    return NS_OK;
+  switch (aKey) {
+    case CAMERA_PARAM_SUPPORTED_ISOMODES:
+      aValues = mIsoModes;
+      return NS_OK;
+
+    case CAMERA_PARAM_SUPPORTED_SCENEMODES:
+      aValues = mSceneModes;
+      return NS_OK;
+
+    default:
+      return GetListAsArray(aKey, aValues);
   }
-
-  return GetListAsArray(aKey, aValues);
 }
 
 nsresult
 GonkCameraParameters::GetTranslated(uint32_t aKey, nsTArray<double>& aValues)
 {
   if (aKey == CAMERA_PARAM_SUPPORTED_ZOOMRATIOS) {
     aValues.Clear();
     for (uint32_t i = 0; i < mZoomRatios.Length(); ++i) {
--- a/dom/camera/GonkCameraParameters.h
+++ b/dom/camera/GonkCameraParameters.h
@@ -96,16 +96,17 @@ protected:
   bool mInitialized;
 
   // Required internal properties
   double mExposureCompensationStep;
   int32_t mExposureCompensationMinIndex;
   int32_t mExposureCompensationMaxIndex;
   nsTArray<int> mZoomRatios;
   nsTArray<nsString> mIsoModes;
+  nsTArray<nsString> mSceneModes;
   nsClassHashtable<nsStringHashKey, nsCString> mIsoModeMap;
 
   // This subclass of android::CameraParameters just gives
   // all of the AOSP getters and setters the same signature.
   class Parameters : public android::CameraParameters
   {
   public:
     using android::CameraParameters::set;
@@ -219,13 +220,17 @@ protected:
   //  - NS_OK on success;
   //  - NS_ERROR_INVALID_ARG if the 'aIso' argument is not a valid form.
   nsresult MapIsoToGonk(const nsAString& aIso, nsACString& aIsoOut);
   nsresult MapIsoFromGonk(const char* aIso, nsAString& aIsoOut);
 
   // Call once to initialize local cached values used in translating other
   // arguments between Gecko and Gonk. Always returns NS_OK.
   nsresult Initialize();
+
+  // Returns true if we're a memory-constrained platform that requires
+  // certain features to be disabled; returns false otherwise.
+  static bool IsLowMemoryPlatform();
 };
 
 } // namespace mozilla
 
 #endif // DOM_CAMERA_GONKCAMERAPARAMETERS_H
--- a/dom/camera/test/camera_common.js
+++ b/dom/camera/test/camera_common.js
@@ -30,16 +30,17 @@ var CameraTest = (function() {
    *
    * This means (of course) that neither the key not the value tokens can
    * contain either equals signs or semicolons. The test shim doesn't enforce
    * this so that we can test getting junk from the camera library as well.
    */
   const PREF_TEST_ENABLED = "camera.control.test.enabled";
   const PREF_TEST_HARDWARE = "camera.control.test.hardware";
   const PREF_TEST_EXTRA_PARAMETERS = "camera.control.test.hardware.gonk.parameters";
+  const PREF_TEST_FAKE_LOW_MEMORY = "camera.control.test.is_low_memory";
   var oldTestEnabled;
   var oldTestHw;
   var testMode;
 
   function testHardwareSetFakeParameters(parameters, callback) {
     SpecialPowers.pushPrefEnv({'set': [[PREF_TEST_EXTRA_PARAMETERS, parameters]]}, function() {
       var setParams = SpecialPowers.getCharPref(PREF_TEST_EXTRA_PARAMETERS);
       ise(setParams, parameters, "Extra test parameters '" + setParams + "'");
@@ -48,16 +49,30 @@ var CameraTest = (function() {
       }
     });
   }
 
   function testHardwareClearFakeParameters(callback) {
     SpecialPowers.pushPrefEnv({'clear': [[PREF_TEST_EXTRA_PARAMETERS]]}, callback);
   }
 
+  function testHardwareSetFakeLowMemoryPlatform(callback) {
+    SpecialPowers.pushPrefEnv({'set': [[PREF_TEST_FAKE_LOW_MEMORY, true]]}, function() {
+      var setParams = SpecialPowers.getBoolPref(PREF_TEST_FAKE_LOW_MEMORY);
+      ise(setParams, true, "Fake low memory platform");
+      if (callback) {
+        callback(setParams);
+      }
+    });
+  }
+
+  function testHardwareClearFakeLowMemoryPlatform(callback) {
+    SpecialPowers.pushPrefEnv({'clear': [[PREF_TEST_FAKE_LOW_MEMORY]]}, callback);
+  }
+
   function testHardwareSet(test, callback) {
     SpecialPowers.pushPrefEnv({'set': [[PREF_TEST_HARDWARE, test]]}, function() {
       var setTest = SpecialPowers.getCharPref(PREF_TEST_HARDWARE);
       ise(setTest, test, "Test subtype set to " + setTest);
       if (callback) {
         callback(setTest);
       }
     });
@@ -83,16 +98,18 @@ var CameraTest = (function() {
       if (setMode === "hardware") {
         try {
           oldTestHw = SpecialPowers.getCharPref(PREF_TEST_HARDWARE);
         } catch(e) { }
         testMode = {
           set: testHardwareSet,
           setFakeParameters: testHardwareSetFakeParameters,
           clearFakeParameters: testHardwareClearFakeParameters,
+          setFakeLowMemoryPlatform: testHardwareSetFakeLowMemoryPlatform,
+          clearFakeLowMemoryPlatform: testHardwareClearFakeLowMemoryPlatform,
           done: testHardwareDone
         };
         if (callback) {
           callback(testMode);
         }
       }
     });
   }
@@ -117,18 +134,26 @@ var CameraTest = (function() {
       var next = cleanUpTestEnabled;
       if (testMode) {
         testMode.done(next);
         testMode = null;
       } else {
         next();
       }
     }
+    function cleanUpLowMemoryPlatform() {
+      var next = cleanUpTest;
+      if (testMode) {
+        testMode.clearFakeLowMemoryPlatform(next);
+      } else {
+        next();
+      }
+    }
     function cleanUpExtraParameters() {
-      var next = cleanUpTest;
+      var next = cleanUpLowMemoryPlatform;
       if (testMode) {
         testMode.clearFakeParameters(next);
       } else {
         next();
       }
     }
 
     cleanUpExtraParameters();
--- a/dom/camera/test/test_camera_fake_parameters.html
+++ b/dom/camera/test/test_camera_fake_parameters.html
@@ -110,44 +110,93 @@ var tests = [
     },
     test: function testFakeZoomOutOfOrder(cam, cap) {
       ok(cap.zoomRatios.length == 1, "zoom ratios length = " + cap.zoomRatios.length);
       ok(cap.zoomRatios[0] == 1.0, "only supported zoom = " + cap.zoomRatios[0] + "x");
       next();
     }
   },
   {
+    key: "fake-high-memory-platform",
+    prep: function setupFakeHighMemoryPlatform(test) {
+      test.setFakeParameters("scene-mode-values=none,snow,beach,hdr,nothdr", function () {
+        run();
+      });
+    },
+    test: function testFakeHighMemoryPlatform(cam, cap) {
+      ok(cap.sceneModes.length == 5, "scene modes length = " + cap.zoomRatios.length);
+
+      // make sure expected values are present and can be set
+      [ "none", "snow", "beach", "hdr", "nothdr" ].forEach(function(mode) {
+        ok(cap.sceneModes.indexOf(mode) != -1, "Scene mode '" + mode + "' is present");
+        cam.sceneMode = mode;
+        ok(cam.sceneMode == mode, "Scene mode '" + cam.sceneMode + "' is set");
+      });
+
+      next();
+    }
+  },
+  {
+    key: "fake-low-memory-platform",
+    prep: function setupFakeLowMemoryPlatform(test) {
+      test.setFakeLowMemoryPlatform(function() {
+        test.setFakeParameters("scene-mode-values=none,hdr,snow,beach,hdr,nothdr", function () {
+          run();
+        });
+      });
+    },
+    test: function testFakeLowMemoryPlatform(cam, cap) {
+      ok(cap.sceneModes.length == 4, "scene modes length = " + cap.zoomRatios.length);
+
+      // make sure expected values are present and can be set
+      [ "none", "snow", "beach", "nothdr" ].forEach(function(mode) {
+        ok(cap.sceneModes.indexOf(mode) != -1, "Scene mode '" + mode + "' is present");
+        cam.sceneMode = mode;
+        ok(cam.sceneMode == mode, "Scene mode '" + cam.sceneMode + "' is set");
+      });
+
+      // make sure unsupported values have been removed, and can't be set
+      var sceneMode = cam.sceneMode;
+      [ "hdr" ].forEach(function(mode) {
+        ok(cap.sceneModes.indexOf(mode) == -1, "Scene mode '" + mode + "' is not present");
+        try {
+          cam.sceneMode = mode;
+        } catch(e) {
+        }
+        ok(cam.sceneMode != mode, "Scene mode '" + cam.sceneMode + "' is still set, '"
+          + mode + "' rejected");
+      });
+      ok(cam.sceneMode == sceneMode, "Scene mode '" + cam.sceneMode + "' is still set");
+
+      next();
+    }
+  },
+  {
     key: "fake-iso",
     prep: function setupFakeIso(test) {
       // we should recognize 'auto', 'hjr', and numeric modes; anything else
       // from the driver is ignored, which this test also verifies.
       test.setFakeParameters(
         "iso=auto;iso-values=auto,ISO_HJR,ISO100,foo,ISObar,ISO150moz,ISO200,400,ISO800,1600",
         function () {
         run();
       });
     },
     test: function testFakeIso(cam, cap) {
       ok(cap.isoModes.length == 7, "ISO modes length = " + cap.isoModes.length);
 
       // make sure we're not leaking any unexpected values formats
-      ok(cap.isoModes.indexOf("ISO_HJR") == -1, "ISO mode 'ISO_HJR' does not appear");
-      ok(cap.isoModes.indexOf("_HJR") == -1, "ISO mode '_HJR' does not appear");
-      ok(cap.isoModes.indexOf("HJR") == -1, "ISO mode 'HJR' does not appear");
-      ok(cap.isoModes.indexOf("ISO100") == -1, "ISO mode 'ISO100' does not appear");
-      ok(cap.isoModes.indexOf("ISO200") == -1, "ISO mode 'ISO200' does not appear");
-      ok(cap.isoModes.indexOf("ISO800") == -1, "ISO mode 'ISO800' does not appear");
+      [ "ISO_HJR", "_HJR", "HJR", "ISO100", "ISO200", "ISO800" ].forEach(function(iso) {
+        ok(cap.isoModes.indexOf(iso) == -1, "ISO mode '" + iso + "' does not appear");
+      });
 
       // make sure any weird values are dropped entirely
-      ok(cap.isoModes.indexOf("foo") == -1, "Unknown ISO mode 'foo' is ignored");
-      ok(cap.isoModes.indexOf("ISObar") == -1, "Unknown ISO mode 'ISObar' is ignored");
-      ok(cap.isoModes.indexOf("bar") == -1, "Unknown ISO mode 'bar' is ignored");
-      ok(cap.isoModes.indexOf("ISO150moz") == -1, "Unknown ISO mode 'ISO150moz' is ignored");
-      ok(cap.isoModes.indexOf("150moz") == -1, "Unknown ISO mode '150moz' is ignored");
-      ok(cap.isoModes.indexOf("150") == -1, "Unknown ISO mode '150' is ignored");
+      [ "foo", "ISObar", "bar", "ISO150moz", "150moz", "150" ].forEach(function(iso) {
+        ok(cap.isoModes.indexOf(iso) == -1, "Unknown ISO mode '" + iso + "' is ignored");
+      });
 
       // make sure expected values are present
       [ "auto", "hjr", "100", "200", "400", "800", "1600" ].forEach(function(iso) {
         ok(cap.isoModes.indexOf(iso) != -1, "ISO mode '" + iso + "' is present");
       });
 
       // test setters/getters for individual ISO modes
       cap.isoModes.forEach(function(iso, index) {
--- a/dom/crypto/AesKeyAlgorithm.h
+++ b/dom/crypto/AesKeyAlgorithm.h
@@ -2,44 +2,35 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_AesKeyAlgorithm_h
 #define mozilla_dom_AesKeyAlgorithm_h
 
-#include "mozilla/dom/KeyAlgorithm.h"
+#include "mozilla/dom/BasicSymmetricKeyAlgorithm.h"
 #include "js/TypeDecls.h"
 
 namespace mozilla {
 namespace dom {
 
-class AesKeyAlgorithm MOZ_FINAL : public KeyAlgorithm
+class AesKeyAlgorithm MOZ_FINAL : public BasicSymmetricKeyAlgorithm
 {
 public:
   AesKeyAlgorithm(nsIGlobalObject* aGlobal, const nsString& aName, uint16_t aLength)
-    : KeyAlgorithm(aGlobal, aName)
-    , mLength(aLength)
+    : BasicSymmetricKeyAlgorithm(aGlobal, aName, aLength)
   {}
 
   ~AesKeyAlgorithm()
   {}
 
   virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
-  uint16_t Length() const
-  {
-    return mLength;
-  }
-
   virtual bool WriteStructuredClone(JSStructuredCloneWriter* aWriter) const MOZ_OVERRIDE;
   static KeyAlgorithm* Create(nsIGlobalObject* aGlobal,
                               JSStructuredCloneReader* aReader);
-
-protected:
-  uint16_t mLength;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_AesKeyAlgorithm_h
new file mode 100644
--- /dev/null
+++ b/dom/crypto/BasicSymmetricKeyAlgorithm.h
@@ -0,0 +1,38 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_BasicSymmetricKeyAlgorithm_h
+#define mozilla_dom_BasicSymmetricKeyAlgorithm_h
+
+#include "mozilla/dom/KeyAlgorithm.h"
+
+namespace mozilla {
+namespace dom {
+
+class BasicSymmetricKeyAlgorithm : public KeyAlgorithm
+{
+public:
+  BasicSymmetricKeyAlgorithm(nsIGlobalObject* aGlobal, const nsString& aName, uint16_t aLength)
+    : KeyAlgorithm(aGlobal, aName)
+    , mLength(aLength)
+  {}
+
+  ~BasicSymmetricKeyAlgorithm()
+  {}
+
+  uint16_t Length() const
+  {
+    return mLength;
+  }
+
+protected:
+  uint16_t mLength;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_BasicSymmetricKeyAlgorithm_h
--- a/dom/crypto/KeyAlgorithm.cpp
+++ b/dom/crypto/KeyAlgorithm.cpp
@@ -27,37 +27,18 @@ NS_INTERFACE_MAP_END
 
 KeyAlgorithm::KeyAlgorithm(nsIGlobalObject* aGlobal, const nsString& aName)
   : mGlobal(aGlobal)
   , mName(aName)
 {
   SetIsDOMBinding();
 
   // Set mechanism based on algorithm name
-  if (mName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC)) {
-    mMechanism = CKM_AES_CBC_PAD;
-  } else if (mName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR)) {
-    mMechanism = CKM_AES_CTR;
-  } else if (mName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
-    mMechanism = CKM_AES_GCM;
-  } else if (mName.EqualsLiteral(WEBCRYPTO_ALG_SHA1)) {
-    mMechanism = CKM_SHA_1;
-  } else if (mName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) {
-    mMechanism = CKM_SHA256;
-  } else if (mName.EqualsLiteral(WEBCRYPTO_ALG_SHA384)) {
-    mMechanism = CKM_SHA384;
-  } else if (mName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
-    mMechanism = CKM_SHA512;
-  } else if (mName.EqualsLiteral(WEBCRYPTO_ALG_RSAES_PKCS1)) {
-    mMechanism = CKM_RSA_PKCS;
-  } else if (mName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) {
-    mMechanism = CKM_RSA_PKCS;
-  } else {
-    mMechanism = UNKNOWN_CK_MECHANISM;
-  }
+  mMechanism = MapAlgorithmNameToMechanism(aName);
+
   // HMAC not handled here, since it requires extra info
 }
 
 KeyAlgorithm::~KeyAlgorithm()
 {
 }
 
 JSObject*
--- a/dom/crypto/WebCryptoCommon.h
+++ b/dom/crypto/WebCryptoCommon.h
@@ -16,16 +16,17 @@
 #define WEBCRYPTO_ALG_AES_CBC       "AES-CBC"
 #define WEBCRYPTO_ALG_AES_CTR       "AES-CTR"
 #define WEBCRYPTO_ALG_AES_GCM       "AES-GCM"
 #define WEBCRYPTO_ALG_SHA1          "SHA-1"
 #define WEBCRYPTO_ALG_SHA256        "SHA-256"
 #define WEBCRYPTO_ALG_SHA384        "SHA-384"
 #define WEBCRYPTO_ALG_SHA512        "SHA-512"
 #define WEBCRYPTO_ALG_HMAC          "HMAC"
+#define WEBCRYPTO_ALG_PBKDF2        "PBKDF2"
 #define WEBCRYPTO_ALG_RSAES_PKCS1   "RSAES-PKCS1-v1_5"
 #define WEBCRYPTO_ALG_RSASSA_PKCS1  "RSASSA-PKCS1-v1_5"
 
 // WebCrypto key formats
 #define WEBCRYPTO_KEY_FORMAT_RAW    "raw"
 #define WEBCRYPTO_KEY_FORMAT_PKCS8  "pkcs8"
 #define WEBCRYPTO_KEY_FORMAT_SPKI   "spki"
 #define WEBCRYPTO_KEY_FORMAT_JWK    "jwk"
@@ -114,12 +115,43 @@ WriteBuffer(JSStructuredCloneWriter* aWr
 {
   bool ret = JS_WriteUint32Pair(aWriter, aBuffer.Length(), 0);
   if (ret && aBuffer.Length() > 0) {
     ret = JS_WriteBytes(aWriter, aBuffer.Elements(), aBuffer.Length());
   }
   return ret;
 }
 
+inline CK_MECHANISM_TYPE
+MapAlgorithmNameToMechanism(const nsString& aName)
+{
+  CK_MECHANISM_TYPE mechanism(UNKNOWN_CK_MECHANISM);
+
+  // Set mechanism based on algorithm name
+  if (aName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC)) {
+    mechanism = CKM_AES_CBC_PAD;
+  } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR)) {
+    mechanism = CKM_AES_CTR;
+  } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
+    mechanism = CKM_AES_GCM;
+  } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA1)) {
+    mechanism = CKM_SHA_1;
+  } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) {
+    mechanism = CKM_SHA256;
+  } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA384)) {
+    mechanism = CKM_SHA384;
+  } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
+    mechanism = CKM_SHA512;
+  } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2)) {
+    mechanism = CKM_PKCS5_PBKD2;
+  } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_RSAES_PKCS1)) {
+    mechanism = CKM_RSA_PKCS;
+  } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) {
+    mechanism = CKM_RSA_PKCS;
+  }
+
+  return mechanism;
+}
+
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_WebCryptoCommon_h
--- a/dom/crypto/WebCryptoTask.cpp
+++ b/dom/crypto/WebCryptoTask.cpp
@@ -143,16 +143,88 @@ Coerce(JSContext* aCx, T& aTarget, const
   JS::RootedValue value(aCx, JS::ObjectValue(*aAlgorithm.GetAsObject()));
   if (!aTarget.Init(aCx, value)) {
     return NS_ERROR_DOM_SYNTAX_ERR;
   }
 
   return NS_OK;
 }
 
+inline size_t
+MapHashAlgorithmNameToBlockSize(const nsString& aName)
+{
+  if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA1) ||
+      aName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) {
+    return 512;
+  }
+
+  if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA384) ||
+      aName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
+    return 1024;
+  }
+
+  return 0;
+}
+
+inline nsresult
+GetKeySizeForAlgorithm(JSContext* aCx, const ObjectOrString& aAlgorithm,
+                       size_t& aLength)
+{
+  aLength = 0;
+
+  // Extract algorithm name
+  nsString algName;
+  if (NS_FAILED(GetAlgorithmName(aCx, aAlgorithm, algName))) {
+    return NS_ERROR_DOM_SYNTAX_ERR;
+  }
+
+  // Read AES key length from given algorithm object.
+  if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
+      algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
+      algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
+    RootedDictionary<AesKeyGenParams> params(aCx);
+    if (NS_FAILED(Coerce(aCx, params, aAlgorithm)) ||
+        !params.mLength.WasPassed()) {
+      return NS_ERROR_DOM_SYNTAX_ERR;
+    }
+
+    size_t length = params.mLength.Value();
+    if (length != 128 && length != 192 && length != 256) {
+      return NS_ERROR_DOM_DATA_ERR;
+    }
+
+    aLength = length;
+    return NS_OK;
+  }
+
+  // Determine HMAC key length as the block size of the given hash.
+  if (algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
+    RootedDictionary<HmacImportParams> params(aCx);
+    if (NS_FAILED(Coerce(aCx, params, aAlgorithm)) ||
+        !params.mHash.WasPassed()) {
+      return NS_ERROR_DOM_SYNTAX_ERR;
+    }
+
+    nsString hashName;
+    if (NS_FAILED(GetAlgorithmName(aCx, params.mHash.Value(), hashName))) {
+      return NS_ERROR_DOM_SYNTAX_ERR;
+    }
+
+    size_t length = MapHashAlgorithmNameToBlockSize(hashName);
+    if (length == 0) {
+      return NS_ERROR_DOM_SYNTAX_ERR;
+    }
+
+    aLength = length;
+    return NS_OK;
+  }
+
+  return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+}
+
 // Implementation of WebCryptoTask methods
 
 void
 WebCryptoTask::FailWithError(nsresult aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
   Telemetry::Accumulate(Telemetry::WEBCRYPTO_RESOLVED, false);
 
@@ -823,20 +895,22 @@ public:
     }
 
     // Import the key data
     if (aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) {
       if (aKeyData.IsArrayBufferView()) {
         mKeyData.Assign(aKeyData.GetAsArrayBufferView());
       } else if (aKeyData.IsArrayBuffer()) {
         mKeyData.Assign(aKeyData.GetAsArrayBuffer());
-      } else {
-        mEarlyRv = NS_ERROR_DOM_DATA_ERR;
-        return;
       }
+      // We would normally fail here if the key data is not an ArrayBuffer or
+      // an ArrayBufferView but let's wait for BeforeCrypto() to be called in
+      // case PBKDF2's deriveKey() operation passed dummy key data. When that
+      // happens DerivePbkdfKeyTask is responsible for calling SetKeyData()
+      // itself before this task is actually run.
     } else if (aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
       mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
       return;
     } else {
       // Invalid key format
       mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
       return;
     }
@@ -854,32 +928,42 @@ public:
         mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
         return;
       }
     }
   }
 
   virtual nsresult BeforeCrypto() MOZ_OVERRIDE
   {
+    // Check that we have valid key data.
+    if (mKeyData.Length() == 0) {
+      return NS_ERROR_DOM_DATA_ERR;
+    }
+
     // Construct an appropriate KeyAlorithm,
     // and verify that usages are appropriate
     nsRefPtr<KeyAlgorithm> algorithm;
     nsIGlobalObject* global = mKey->GetParentObject();
     uint32_t length = 8 * mKeyData.Length(); // bytes to bits
     if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
         mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
         mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
       if (mKey->HasUsageOtherThan(CryptoKey::ENCRYPT | CryptoKey::DECRYPT)) {
         return NS_ERROR_DOM_DATA_ERR;
       }
 
       if ( (length != 128) && (length != 192) && (length != 256) ) {
         return NS_ERROR_DOM_DATA_ERR;
       }
       algorithm = new AesKeyAlgorithm(global, mAlgName, length);
+    } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2)) {
+      if (mKey->HasUsageOtherThan(CryptoKey::DERIVEKEY)) {
+        return NS_ERROR_DOM_DATA_ERR;
+      }
+      algorithm = new BasicSymmetricKeyAlgorithm(global, mAlgName, length);
     } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
       if (mKey->HasUsageOtherThan(CryptoKey::SIGN | CryptoKey::VERIFY)) {
         return NS_ERROR_DOM_DATA_ERR;
       }
 
       algorithm = new HmacKeyAlgorithm(global, mAlgName, length, mHashName);
       if (algorithm->Mechanism() == UNKNOWN_CK_MECHANISM) {
         return NS_ERROR_DOM_SYNTAX_ERR;
@@ -890,16 +974,22 @@ public:
 
     mKey->SetAlgorithm(algorithm);
     mKey->SetSymKey(mKeyData);
     mKey->SetType(CryptoKey::SECRET);
     mEarlyComplete = true;
     return NS_OK;
   }
 
+  void SetKeyData(const CryptoBuffer& aKeyData)
+  {
+    // An OOM will just result in an error in BeforeCrypto
+    mKeyData = aKeyData;
+  }
+
 private:
   CryptoBuffer mKeyData;
   nsString mHashName;
 };
 
 class ImportRsaKeyTask : public ImportKeyTask
 {
 public:
@@ -1124,26 +1214,18 @@ public:
     }
 
     // Construct an appropriate KeyAlorithm
     nsRefPtr<KeyAlgorithm> algorithm;
     uint32_t allowedUsages = 0;
     if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
         algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
         algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
-      RootedDictionary<AesKeyGenParams> params(aCx);
-      mEarlyRv = Coerce(aCx, params, aAlgorithm);
-      if (NS_FAILED(mEarlyRv) || !params.mLength.WasPassed()) {
-        mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
-        return;
-      }
-
-      mLength = params.mLength.Value();
-      if (mLength != 128 && mLength != 192 && mLength != 256) {
-        mEarlyRv = NS_ERROR_DOM_DATA_ERR;
+      mEarlyRv = GetKeySizeForAlgorithm(aCx, aAlgorithm, mLength);
+      if (NS_FAILED(mEarlyRv)) {
         return;
       }
       algorithm = new AesKeyAlgorithm(global, algName, mLength);
       allowedUsages = CryptoKey::ENCRYPT | CryptoKey::DECRYPT;
     } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
       RootedDictionary<HmacKeyGenParams> params(aCx);
       mEarlyRv = Coerce(aCx, params, aAlgorithm);
       if (NS_FAILED(mEarlyRv) || !params.mHash.WasPassed()) {
@@ -1162,30 +1244,17 @@ public:
           return;
         }
         hashName.Assign(hashAlg.mName.Value());
       }
 
       if (params.mLength.WasPassed()) {
         mLength = params.mLength.Value();
       } else {
-        nsRefPtr<KeyAlgorithm> hashAlg = new KeyAlgorithm(global, hashName);
-        switch (hashAlg->Mechanism()) {
-          case CKM_SHA_1:
-          case CKM_SHA256:
-            mLength = 512;
-            break;
-          case CKM_SHA384:
-          case CKM_SHA512:
-            mLength = 1024;
-            break;
-          default:
-            mLength = 0;
-            break;
-        }
+        mLength = MapHashAlgorithmNameToBlockSize(hashName);
       }
 
       if (mLength == 0) {
         mEarlyRv = NS_ERROR_DOM_DATA_ERR;
         return;
       }
 
       algorithm = new HmacKeyAlgorithm(global, algName, mLength, hashName);
@@ -1416,16 +1485,170 @@ private:
   }
 
   virtual void Cleanup() MOZ_OVERRIDE
   {
     mKeyPair = nullptr;
   }
 };
 
+class DerivePbkdfBitsTask : public ReturnArrayBufferViewTask
+{
+public:
+  DerivePbkdfBitsTask(JSContext* aCx,
+      const ObjectOrString& aAlgorithm, CryptoKey& aKey, uint32_t aLength)
+    : mSymKey(aKey.GetSymKey())
+  {
+    Init(aCx, aAlgorithm, aKey, aLength);
+  }
+
+  DerivePbkdfBitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
+                      CryptoKey& aKey, const ObjectOrString& aTargetAlgorithm)
+    : mSymKey(aKey.GetSymKey())
+  {
+    size_t length;
+    mEarlyRv = GetKeySizeForAlgorithm(aCx, aTargetAlgorithm, length);
+
+    if (NS_SUCCEEDED(mEarlyRv)) {
+      Init(aCx, aAlgorithm, aKey, length);
+    }
+  }
+
+  void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
+            uint32_t aLength)
+  {
+    // Check that we got a symmetric key
+    if (mSymKey.Length() == 0) {
+      mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
+      return;
+    }
+
+    RootedDictionary<Pbkdf2Params> params(aCx);
+    mEarlyRv = Coerce(aCx, params, aAlgorithm);
+    if (NS_FAILED(mEarlyRv) || !params.mHash.WasPassed() ||
+        !params.mIterations.WasPassed() || !params.mSalt.WasPassed()) {
+      mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
+      return;
+    }
+
+    // length must be a multiple of 8 bigger than zero.
+    if (aLength == 0 || aLength % 8) {
+      mEarlyRv = NS_ERROR_DOM_DATA_ERR;
+      return;
+    }
+
+    // Extract the hash algorithm.
+    nsString hashName;
+    mEarlyRv = GetAlgorithmName(aCx, params.mHash.Value(), hashName);
+    if (NS_FAILED(mEarlyRv)) {
+      return;
+    }
+
+    // Check the given hash algorithm.
+    switch (MapAlgorithmNameToMechanism(hashName)) {
+      case CKM_SHA_1: mHashOidTag = SEC_OID_HMAC_SHA1; break;
+      case CKM_SHA256: mHashOidTag = SEC_OID_HMAC_SHA256; break;
+      case CKM_SHA384: mHashOidTag = SEC_OID_HMAC_SHA384; break;
+      case CKM_SHA512: mHashOidTag = SEC_OID_HMAC_SHA512; break;
+      default: {
+        mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+        return;
+      }
+    }
+
+    ATTEMPT_BUFFER_INIT(mSalt, params.mSalt.Value())
+    mLength = aLength >> 3; // bits to bytes
+    mIterations = params.mIterations.Value();
+  }
+
+private:
+  size_t mLength;
+  size_t mIterations;
+  CryptoBuffer mSalt;
+  CryptoBuffer mSymKey;
+  SECOidTag mHashOidTag;
+
+  virtual nsresult DoCrypto() MOZ_OVERRIDE
+  {
+    ScopedSECItem salt;
+    ATTEMPT_BUFFER_TO_SECITEM(salt, mSalt);
+
+    // Always pass in cipherAlg=SEC_OID_HMAC_SHA1 (i.e. PBMAC1) as this
+    // parameter is unused for key generation. It is currently only used
+    // for PBKDF2 authentication or key (un)wrapping when specifying an
+    // encryption algorithm (PBES2).
+    ScopedSECAlgorithmID alg_id(PK11_CreatePBEV2AlgorithmID(
+      SEC_OID_PKCS5_PBKDF2, SEC_OID_HMAC_SHA1, mHashOidTag,
+      mLength, mIterations, salt));
+
+    if (!alg_id.get()) {
+      return NS_ERROR_DOM_OPERATION_ERR;
+    }
+
+    ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
+    if (!slot.get()) {
+      return NS_ERROR_DOM_OPERATION_ERR;
+    }
+
+    ScopedSECItem keyItem;
+    ATTEMPT_BUFFER_TO_SECITEM(keyItem, mSymKey);
+
+    ScopedPK11SymKey symKey(PK11_PBEKeyGen(slot, alg_id, keyItem, false, nullptr));
+    if (!symKey.get()) {
+      return NS_ERROR_DOM_OPERATION_ERR;
+    }
+
+    nsresult rv = MapSECStatus(PK11_ExtractKeyValue(symKey));
+    if (NS_FAILED(rv)) {
+      return NS_ERROR_DOM_OPERATION_ERR;
+    }
+
+    // This doesn't leak, because the SECItem* returned by PK11_GetKeyData
+    // just refers to a buffer managed by symKey. The assignment copies the
+    // data, so mResult manages one copy, while symKey manages another.
+    ATTEMPT_BUFFER_ASSIGN(mResult, PK11_GetKeyData(symKey));
+    return NS_OK;
+  }
+};
+
+class DerivePbkdfKeyTask : public DerivePbkdfBitsTask
+{
+public:
+  DerivePbkdfKeyTask(JSContext* aCx,
+                     const ObjectOrString& aAlgorithm, CryptoKey& aBaseKey,
+                     const ObjectOrString& aDerivedKeyType, bool aExtractable,
+                     const Sequence<nsString>& aKeyUsages)
+    : DerivePbkdfBitsTask(aCx, aAlgorithm, aBaseKey, aDerivedKeyType)
+  {
+    if (NS_FAILED(mEarlyRv)) {
+      return;
+    }
+
+    CryptoOperationData dummy;
+    NS_NAMED_LITERAL_STRING(format, WEBCRYPTO_KEY_FORMAT_RAW);
+
+    mTask = new ImportSymmetricKeyTask(aCx, format, dummy, aDerivedKeyType,
+                                       aExtractable, aKeyUsages);
+  }
+
+protected:
+  nsRefPtr<ImportSymmetricKeyTask> mTask;
+
+private:
+  virtual void Resolve() MOZ_OVERRIDE {
+    mTask->SetKeyData(mResult);
+    mTask->DispatchWithPromise(mResultPromise);
+  }
+
+  virtual void Cleanup() MOZ_OVERRIDE
+  {
+    mTask = nullptr;
+  }
+};
+
 
 // Task creation methods for WebCryptoTask
 
 WebCryptoTask*
 WebCryptoTask::EncryptDecryptTask(JSContext* aCx,
                                   const ObjectOrString& aAlgorithm,
                                   CryptoKey& aKey,
                                   const CryptoOperationData& aData,
@@ -1515,16 +1738,17 @@ WebCryptoTask::ImportKeyTask(JSContext* 
   nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
   if (NS_FAILED(rv)) {
     return new FailureTask(rv);
   }
 
   if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
       algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
       algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM) ||
+      algName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2) ||
       algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
     return new ImportSymmetricKeyTask(aCx, aFormat, aKeyData, aAlgorithm,
                                       aExtractable, aKeyUsages);
   } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSAES_PKCS1) ||
              algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) {
     return new ImportRsaKeyTask(aCx, aFormat, aKeyData, aAlgorithm,
                                 aExtractable, aKeyUsages);
   } else {
@@ -1577,24 +1801,47 @@ WebCryptoTask*
 WebCryptoTask::DeriveKeyTask(JSContext* aCx,
                              const ObjectOrString& aAlgorithm,
                              CryptoKey& aBaseKey,
                              const ObjectOrString& aDerivedKeyType,
                              bool aExtractable,
                              const Sequence<nsString>& aKeyUsages)
 {
   Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_DERIVEKEY);
+
+  nsString algName;
+  nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
+  if (NS_FAILED(rv)) {
+    return new FailureTask(rv);
+  }
+
+  if (algName.EqualsASCII(WEBCRYPTO_ALG_PBKDF2)) {
+    return new DerivePbkdfKeyTask(aCx, aAlgorithm, aBaseKey, aDerivedKeyType,
+                                  aExtractable, aKeyUsages);
+  }
+
   return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
 }
 
 WebCryptoTask*
 WebCryptoTask::DeriveBitsTask(JSContext* aCx,
                               const ObjectOrString& aAlgorithm,
                               CryptoKey& aKey,
                               uint32_t aLength)
 {
   Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_DERIVEBITS);
+
+  nsString algName;
+  nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
+  if (NS_FAILED(rv)) {
+    return new FailureTask(rv);
+  }
+
+  if (algName.EqualsASCII(WEBCRYPTO_ALG_PBKDF2)) {
+    return new DerivePbkdfBitsTask(aCx, aAlgorithm, aKey, aLength);
+  }
+
   return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
 }
 
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/crypto/moz.build
+++ b/dom/crypto/moz.build
@@ -3,16 +3,17 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 TEST_DIRS += ['test']
 
 EXPORTS.mozilla.dom += [
     'AesKeyAlgorithm.h',
+    'BasicSymmetricKeyAlgorithm.h',
     'CryptoBuffer.h',
     'CryptoKey.h',
     'CryptoKeyPair.h',
     'HmacKeyAlgorithm.h',
     'KeyAlgorithm.h',
     'RsaHashedKeyAlgorithm.h',
     'RsaKeyAlgorithm.h',
     'WebCryptoCommon.h',
--- a/dom/crypto/test/test-vectors.js
+++ b/dom/crypto/test/test-vectors.js
@@ -305,9 +305,34 @@ tv = {
     ),
     sig_fail: util.hex2abv(
       "8000000080e5c7b4b5e672929f664c4896e50c35134b6de4d5a934252a3a245f" +
       "f48340920e1034b7d5a5b524eb0e1cf12befef49b27b732d2c19e1c43217d6e1" +
       "417381111a1d36de6375cf455b3c9812639dbc27600c751994fb61799ecf7da6" +
       "bcf51540afd0174db4033188556675b1d763360af46feeca5b60f882829ee7b2"
     ),
   },
+
+  // RFC 6070 <http://tools.ietf.org/html/rfc6070>
+  pbkdf2_sha1: {
+    password: new TextEncoder("utf-8").encode("passwordPASSWORDpassword"),
+    salt: new TextEncoder("utf-8").encode("saltSALTsaltSALTsaltSALTsaltSALTsalt"),
+    iterations: 4096,
+    length: 25 * 8,
+
+    derived: util.hex2abv(
+      "3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038"
+    )
+  },
+
+  // https://stackoverflow.com/questions/5130513/pbkdf2-hmac-sha2-test-vectors
+  pbkdf2_sha256: {
+    password: new TextEncoder("utf-8").encode("passwordPASSWORDpassword"),
+    salt: new TextEncoder("utf-8").encode("saltSALTsaltSALTsaltSALTsaltSALTsalt"),
+    iterations: 4096,
+    length: 40 * 8,
+
+    derived: util.hex2abv(
+      "348c89dbcbd32b2f32d814b8116e84cf2b17347ebc1800181c4e2a1fb8dd53e1" +
+      "c635518c7dac47e9"
+    )
+  },
 }
--- a/dom/crypto/test/tests.js
+++ b/dom/crypto/test/tests.js
@@ -1013,9 +1013,140 @@ TestArray.addTest(
       .then( doVerify, fail )
       .then(
         complete(that, function(x) { return !x; }),
         fail
       );
   }
 );
 
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+  "Import raw PBKDF2 key",
+  function() {
+    var that = this;
+    var alg = "PBKDF2";
+    var key = new TextEncoder("utf-8").encode("password");
 
+    crypto.subtle.importKey("raw", key, alg, false, ["deriveKey"]).then(
+      complete(that, hasKeyFields),
+      error(that)
+    );
+  }
+);
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+  "Import raw PBKDF2 key and derive bits using HMAC-SHA-1",
+  function() {
+    var that = this;
+    var alg = "PBKDF2";
+    var key = tv.pbkdf2_sha1.password;
+
+    function doDerive(x) {
+      console.log("deriving");
+      if (!hasKeyFields(x)) {
+        throw "Invalid key; missing field(s)";
+      }
+
+      var alg = {
+        name: "PBKDF2",
+        hash: "SHA-1",
+        salt: tv.pbkdf2_sha1.salt,
+        iterations: tv.pbkdf2_sha1.iterations
+      };
+      return crypto.subtle.deriveBits(alg, x, tv.pbkdf2_sha1.length);
+    }
+    function fail(x) { console.log("failing"); error(that)(x); }
+
+    crypto.subtle.importKey("raw", key, alg, false, ["deriveKey"])
+      .then( doDerive, fail )
+      .then( memcmp_complete(that, tv.pbkdf2_sha1.derived), fail );
+  }
+);
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+  "Import raw PBKDF2 key and derive a new key using HMAC-SHA-1",
+  function() {
+    var that = this;
+    var alg = "PBKDF2";
+    var key = tv.pbkdf2_sha1.password;
+
+    function doDerive(x) {
+      console.log("deriving");
+      if (!hasKeyFields(x)) {
+        throw "Invalid key; missing field(s)";
+      }
+
+      var alg = {
+        name: "PBKDF2",
+        hash: "SHA-1",
+        salt: tv.pbkdf2_sha1.salt,
+        iterations: tv.pbkdf2_sha1.iterations
+      };
+
+      var algDerived = {
+        name: "HMAC",
+        hash: {name: "SHA-1"}
+      };
+
+      return crypto.subtle.deriveKey(alg, x, algDerived, false, ["sign", "verify"])
+        .then(function (x) {
+          if (!hasKeyFields(x)) {
+            throw "Invalid key; missing field(s)";
+          }
+
+          if (x.algorithm.length != 512) {
+            throw "Invalid key; incorrect length";
+          }
+
+          return x;
+        });
+    }
+
+    function doSignAndVerify(x) {
+      var data = new Uint8Array(1024);
+
+      return crypto.subtle.sign("HMAC", x, data)
+        .then(function (sig) {
+          return crypto.subtle.verify("HMAC", x, sig, data);
+        });
+    }
+
+    function fail(x) { console.log("failing"); error(that)(x); }
+
+    crypto.subtle.importKey("raw", key, alg, false, ["deriveKey"])
+      .then( doDerive, fail )
+      .then( doSignAndVerify, fail )
+      .then( complete(that), fail );
+  }
+);
+
+// -----------------------------------------------------------------------------
+/*TestArray.addTest(
+  "Import raw PBKDF2 key and derive bits using HMAC-SHA-256",
+  function() {
+    var that = this;
+    var alg = "PBKDF2";
+    var key = tv.pbkdf2_sha256.password;
+
+    function doDerive(x) {
+      console.log("deriving");
+      if (!hasKeyFields(x)) {
+        throw "Invalid key; missing field(s)";
+      }
+
+      var alg = {
+        name: "PBKDF2",
+        hash: "SHA-256",
+        salt: tv.pbkdf2_sha256.salt,
+        iterations: tv.pbkdf2_sha256.iterations
+      };
+      return crypto.subtle.deriveBits(alg, x, tv.pbkdf2_sha256.length);
+    }
+    function fail(x) { console.log("failing"); error(that)(x); }
+
+    crypto.subtle.importKey("raw", key, alg, false, ["deriveKey"])
+      .then( doDerive, fail )
+      .then( memcmp_complete(that, tv.pbkdf2_sha256.derived), fail );
+  }
+);*/
--- a/dom/interfaces/base/nsIServiceWorkerManager.idl
+++ b/dom/interfaces/base/nsIServiceWorkerManager.idl
@@ -1,21 +1,23 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "domstubs.idl"
 
-[uuid(d9539ecb-6665-452c-bae7-4e42f25d23aa)]
+[uuid(44ef0b7e-92c0-48a7-a092-5a49f2533792)]
 interface nsIServiceWorkerManager : nsISupports
 {
   // Returns a Promise
   nsISupports register(in nsIDOMWindow aWindow, in DOMString aScope, in DOMString aScriptURI);
 
   // Returns a Promise
   nsISupports unregister(in nsIDOMWindow aWindow, in DOMString aScope);
 
+  // Testing
+  DOMString getScopeForUrl(in DOMString path);
 };
 
 %{ C++
 #define SERVICEWORKERMANAGER_CONTRACTID "@mozilla.org/serviceworkers/manager;1"
 %}
--- a/dom/interfaces/html/moz.build
+++ b/dom/interfaces/html/moz.build
@@ -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/.
 
 XPIDL_SOURCES += [
     'nsIDOMHTMLAnchorElement.idl',
     'nsIDOMHTMLAppletElement.idl',
     'nsIDOMHTMLAreaElement.idl',
-    'nsIDOMHTMLAudioElement.idl',
     'nsIDOMHTMLBaseElement.idl',
     'nsIDOMHTMLBodyElement.idl',
     'nsIDOMHTMLBRElement.idl',
     'nsIDOMHTMLButtonElement.idl',
     'nsIDOMHTMLCanvasElement.idl',
     'nsIDOMHTMLCollection.idl',
     'nsIDOMHTMLDirectoryElement.idl',
     'nsIDOMHTMLDivElement.idl',
@@ -53,17 +52,16 @@ XPIDL_SOURCES += [
     'nsIDOMHTMLSourceElement.idl',
     'nsIDOMHTMLStyleElement.idl',
     'nsIDOMHTMLTableCaptionElem.idl',
     'nsIDOMHTMLTableCellElement.idl',
     'nsIDOMHTMLTableElement.idl',
     'nsIDOMHTMLTextAreaElement.idl',
     'nsIDOMHTMLTitleElement.idl',
     'nsIDOMHTMLUListElement.idl',
-    'nsIDOMHTMLVideoElement.idl',
     'nsIDOMMediaError.idl',
     'nsIDOMMozBrowserFrame.idl',
     'nsIDOMTimeRanges.idl',
     'nsIDOMValidityState.idl',
     'nsIMozBrowserFrame.idl',
 ]
 
 XPIDL_MODULE = 'dom_html'
deleted file mode 100644
--- a/dom/interfaces/html/nsIDOMHTMLAudioElement.idl
+++ /dev/null
@@ -1,22 +0,0 @@
-/* -*- 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 "nsIDOMHTMLMediaElement.idl"
-
-/**
- * The nsIDOMHTMLAudioElement interface is the interface to a HTML
- * <audio> element.
- *
- * For more information on this interface, please see
- * http://www.whatwg.org/specs/web-apps/current-work/#audio
- *
- * @status UNDER_DEVELOPMENT
- */
-
-[uuid(75a7f3ca-0761-4b63-863b-6fd6a87ed51c)]
-interface nsIDOMHTMLAudioElement : nsIDOMHTMLMediaElement
-{
-};
--- a/dom/interfaces/html/nsIDOMHTMLMediaElement.idl
+++ b/dom/interfaces/html/nsIDOMHTMLMediaElement.idl
@@ -22,17 +22,17 @@ interface nsIDOMMediaStream;
 
 // undef the GetCurrentTime macro defined in WinBase.h from the MS Platform SDK
 %{C++
 #ifdef GetCurrentTime
 #undef GetCurrentTime
 #endif
 %}
 
-[uuid(1f9393e8-2df0-4072-87b9-c26999b09acc)]
+[uuid(0e14e6ad-2074-48b7-a247-e545a3a15131)]
 interface nsIDOMHTMLMediaElement : nsISupports
 {
   // error state
   readonly attribute nsIDOMMediaError error;
 
   // network state
            attribute DOMString src;
            attribute nsIDOMMediaStream mozSrcObject;
@@ -128,9 +128,11 @@ interface nsIDOMHTMLMediaElement : nsISu
    //   Always plays in speaker, even when headphones are plugged in.
    //   Use case: Camera shutter sound.
    attribute DOMString mozAudioChannelType;
 
   // In addition the media element has this new events:
   // * onmozinterruptbegin - called when the media element is interrupted
   //   because of the audiochannel manager.
   // * onmozinterruptend - called when the interruption is concluded
+
+  [notxpcom] boolean isVideo();
 };
deleted file mode 100644
--- a/dom/interfaces/html/nsIDOMHTMLVideoElement.idl
+++ /dev/null
@@ -1,50 +0,0 @@
-/* -*- 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 "nsIDOMHTMLMediaElement.idl"
-
-/**
- * The nsIDOMHTMLVideoElement interface is the interface to a HTML
- * <video> element.
- *
- * For more information on this interface, please see
- * http://www.whatwg.org/specs/web-apps/current-work/#video
- *
- * @status UNDER_DEVELOPMENT
- */
-
-[uuid(185a3e8f-56a7-4bda-8dc7-2cff6ed07d1d)]
-interface nsIDOMHTMLVideoElement : nsIDOMHTMLMediaElement
-{
-           attribute long width; 
-           attribute long height;
-  readonly attribute unsigned long videoWidth;
-  readonly attribute unsigned long videoHeight;
-           attribute DOMString poster;
-           
-  // A count of the number of video frames that have demuxed from the media
-  // resource. If we were playing perfectly, we'd be able to paint this many
-  // frames.
-  readonly attribute unsigned long mozParsedFrames;
-
-  // A count of the number of frames that have been decoded. We may drop
-  // frames if the decode is taking too much time.
-  readonly attribute unsigned long mozDecodedFrames;
-
-  // A count of the number of frames that have been presented to the rendering
-  // pipeline. We may drop frames if they arrive late at the renderer.
-  readonly attribute unsigned long mozPresentedFrames;
-
-  // Number of presented frames which were painted on screen.
-  readonly attribute unsigned long mozPaintedFrames;
-
-  // Time which the last painted video frame was late by, in seconds.
-  readonly attribute double mozFrameDelay;
-
-  // True if the video has an audio track available.
-  readonly attribute bool mozHasAudio;
-};
-
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -2033,16 +2033,29 @@ void
 GetUserMediaCallbackMediaStreamListener::NotifyFinished(MediaStreamGraph* aGraph)
 {
   mFinished = true;
   Invalidate(); // we know it's been activated
   NS_DispatchToMainThread(new GetUserMediaListenerRemove(mWindowID, this));
 }
 
 // Called from the MediaStreamGraph thread
+void
+GetUserMediaCallbackMediaStreamListener::NotifyDirectListeners(MediaStreamGraph* aGraph,
+                                                               bool aHasListeners)
+{
+  nsRefPtr<MediaOperationRunnable> runnable;
+  runnable = new MediaOperationRunnable(MEDIA_DIRECT_LISTENERS,
+                                        this, nullptr, nullptr,
+                                        mAudioSource, mVideoSource,
+                                        aHasListeners, mWindowID, nullptr);
+  mMediaThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
+}
+
+// Called from the MediaStreamGraph thread
 // this can be in response to our own RemoveListener() (via ::Remove()), or
 // because the DOM GC'd the DOMLocalMediaStream/etc we're attached to.
 void
 GetUserMediaCallbackMediaStreamListener::NotifyRemoved(MediaStreamGraph* aGraph)
 {
   {
     MutexAutoLock lock(mLock); // protect access to mRemoved
     MM_LOG(("Listener removed by DOM Destroy(), mFinished = %d", (int) mFinished));
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -170,20 +170,45 @@ public:
       mAudioSource->NotifyPull(aGraph, mStream, kAudioTrack, aDesiredTime, mLastEndTimeAudio);
     }
     if (mVideoSource) {
       mVideoSource->NotifyPull(aGraph, mStream, kVideoTrack, aDesiredTime, mLastEndTimeVideo);
     }
   }
 
   virtual void
-  NotifyFinished(MediaStreamGraph* aGraph) MOZ_OVERRIDE;
+  NotifyEvent(MediaStreamGraph* aGraph,
+              MediaStreamListener::MediaStreamGraphEvent aEvent) MOZ_OVERRIDE
+  {
+    switch (aEvent) {
+      case EVENT_FINISHED:
+        NotifyFinished(aGraph);
+        break;
+      case EVENT_REMOVED:
+        NotifyRemoved(aGraph);
+        break;
+      case EVENT_HAS_DIRECT_LISTENERS:
+        NotifyDirectListeners(aGraph, true);
+        break;
+      case EVENT_HAS_NO_DIRECT_LISTENERS:
+        NotifyDirectListeners(aGraph, false);
+        break;
+      default:
+        break;
+    }
+  }
 
   virtual void
-  NotifyRemoved(MediaStreamGraph* aGraph) MOZ_OVERRIDE;
+  NotifyFinished(MediaStreamGraph* aGraph);
+
+  virtual void
+  NotifyRemoved(MediaStreamGraph* aGraph);
+
+  virtual void
+  NotifyDirectListeners(MediaStreamGraph* aGraph, bool aHasListeners);
 
 private:
   // Set at construction
   nsCOMPtr<nsIThread> mMediaThread;
   uint64_t mWindowID;
 
   bool mStopped; // MainThread only
 
@@ -239,17 +264,18 @@ class GetUserMediaNotificationEvent: pub
     bool mIsAudio;
     bool mIsVideo;
     uint64_t mWindowID;
     nsRefPtr<nsIDOMGetUserMediaErrorCallback> mError;
 };
 
 typedef enum {
   MEDIA_START,
-  MEDIA_STOP
+  MEDIA_STOP,
+  MEDIA_DIRECT_LISTENERS
 } MediaOperation;
 
 class MediaManager;
 class GetUserMediaRunnable;
 
 /**
  * Send an error back to content. The error is the form a string.
  * Do this only on the main thread. The success callback is also passed here
@@ -294,26 +320,26 @@ class MediaOperationRunnable : public ns
 public:
   // so we can send Stop without AddRef()ing from the MSG thread
   MediaOperationRunnable(MediaOperation aType,
     GetUserMediaCallbackMediaStreamListener* aListener,
     DOMMediaStream* aStream,
     DOMMediaStream::OnTracksAvailableCallback* aOnTracksAvailableCallback,
     MediaEngineSource* aAudioSource,
     MediaEngineSource* aVideoSource,
-    bool aNeedsFinish,
+    bool aBool,
     uint64_t aWindowID,
     already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError)
     : mType(aType)
     , mStream(aStream)
     , mOnTracksAvailableCallback(aOnTracksAvailableCallback)
     , mAudioSource(aAudioSource)
     , mVideoSource(aVideoSource)
     , mListener(aListener)
-    , mFinish(aNeedsFinish)
+    , mBool(aBool)
     , mWindowID(aWindowID)
     , mError(aError)
   {}
 
   ~MediaOperationRunnable()
   {
     // MediaStreams can be released on any thread.
   }
@@ -393,46 +419,55 @@ public:
             mAudioSource->Stop(source, kAudioTrack);
             mAudioSource->Deallocate();
           }
           if (mVideoSource) {
             mVideoSource->Stop(source, kVideoTrack);
             mVideoSource->Deallocate();
           }
           // Do this after stopping all tracks with EndTrack()
-          if (mFinish) {
+          if (mBool) {
             source->Finish();
           }
           nsIRunnable *event =
             new GetUserMediaNotificationEvent(mListener,
                                               GetUserMediaNotificationEvent::STOPPING,
                                               mAudioSource != nullptr,
                                               mVideoSource != nullptr,
                                               mWindowID);
           // event must always be released on mainthread due to the JS callbacks
           // in the TracksAvailableCallback
           NS_DispatchToMainThread(event);
         }
         break;
 
+      case MEDIA_DIRECT_LISTENERS:
+        {
+          NS_ASSERTION(!NS_IsMainThread(), "Never call on main thread");
+          if (mVideoSource) {
+            mVideoSource->SetDirectListeners(mBool);
+          }
+        }
+        break;
+
       default:
         MOZ_ASSERT(false,"invalid MediaManager operation");
         break;
     }
     return NS_OK;
   }
 
 private:
   MediaOperation mType;
   nsRefPtr<DOMMediaStream> mStream;
   nsAutoPtr<DOMMediaStream::OnTracksAvailableCallback> mOnTracksAvailableCallback;
   nsRefPtr<MediaEngineSource> mAudioSource; // threadsafe
   nsRefPtr<MediaEngineSource> mVideoSource; // threadsafe
   nsRefPtr<GetUserMediaCallbackMediaStreamListener> mListener; // threadsafe
-  bool mFinish;
+  bool mBool;
   uint64_t mWindowID;
   nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mError;
 };
 
 typedef nsTArray<nsRefPtr<GetUserMediaCallbackMediaStreamListener> > StreamListeners;
 typedef nsClassHashtable<nsUint64HashKey, StreamListeners> WindowTable;
 
 class MediaDevice : public nsIMediaDevice
--- a/dom/plugins/base/nsJSNPRuntime.cpp
+++ b/dom/plugins/base/nsJSNPRuntime.cpp
@@ -10,16 +10,17 @@
 
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsJSNPRuntime.h"
 #include "nsNPAPIPlugin.h"
 #include "nsNPAPIPluginInstance.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIScriptContext.h"
 #include "nsDOMJSUtils.h"
+#include "nsJSUtils.h"
 #include "nsCxPusher.h"
 #include "nsIDocument.h"
 #include "nsIJSRuntimeService.h"
 #include "nsIXPConnect.h"
 #include "nsIDOMElement.h"
 #include "prmem.h"
 #include "nsIContent.h"
 #include "nsPluginInstanceOwner.h"
@@ -400,24 +401,22 @@ JSValToNPVariant(NPP npp, JSContext *cx,
       int i;
       if (JS_DoubleIsInt32(d, &i)) {
         INT32_TO_NPVARIANT(i, *variant);
       } else {
         DOUBLE_TO_NPVARIANT(d, *variant);
       }
     } else if (val.isString()) {
       JSString *jsstr = val.toString();
-      size_t length;
-      const jschar *chars = ::JS_GetStringCharsZAndLength(cx, jsstr, &length);
-      if (!chars) {
-          return false;
+
+      nsAutoJSString str;
+      if (!str.init(cx, jsstr)) {
+        return false;
       }
 
-      nsDependentString str(chars, length);
-
       uint32_t len;
       char *p = ToNewUTF8String(str, &len);
 
       if (!p) {
         return false;
       }
 
       STRINGN_TO_NPVARIANT(p, len, *variant);
--- a/dom/plugins/base/nsNPAPIPlugin.cpp
+++ b/dom/plugins/base/nsNPAPIPlugin.cpp
@@ -1298,20 +1298,20 @@ NPUTF8*
   if (!id)
     return nullptr;
 
   if (!NPIdentifierIsString(id)) {
     return nullptr;
   }
 
   JSString *str = NPIdentifierToString(id);
-
-  return
-    ToNewUTF8String(nsDependentString(::JS_GetInternedStringChars(str),
-                                      ::JS_GetStringLength(str)));
+  nsAutoString autoStr;
+  AssignJSFlatString(autoStr, JS_ASSERT_STRING_IS_FLAT(str));
+
+  return ToNewUTF8String(autoStr);
 }
 
 int32_t
 _intfromidentifier(NPIdentifier id)
 {
   if (!NS_IsMainThread()) {
     NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_intfromidentifier called from the wrong thread\n"));
   }
--- a/dom/plugins/base/nsNPAPIPluginInstance.cpp
+++ b/dom/plugins/base/nsNPAPIPluginInstance.cpp
@@ -50,16 +50,17 @@ using namespace mozilla;
 #include "mozilla/CondVar.h"
 #include "AndroidBridge.h"
 #include "mozilla/dom/ScreenOrientation.h"
 #include "mozilla/Hal.h"
 #include "GLContextProvider.h"
 #include "GLContext.h"
 #include "TexturePoolOGL.h"
 #include "GLSharedHandleHelpers.h"
+#include "SurfaceTypes.h"
 
 using namespace mozilla::gl;
 
 typedef nsNPAPIPluginInstance::VideoInfo VideoInfo;
 
 class PluginEventRunnable : public nsRunnable
 {
 public:
@@ -84,17 +85,17 @@ private:
 
 static nsRefPtr<GLContext> sPluginContext = nullptr;
 
 static bool EnsureGLContext()
 {
   if (!sPluginContext) {
     gfxIntSize dummySize(16, 16);
     sPluginContext = GLContextProvider::CreateOffscreen(dummySize,
-                                                        GLContext::SurfaceCaps::Any());
+                                                        SurfaceCaps::Any());
   }
 
   return sPluginContext != nullptr;
 }
 
 class SharedPluginTexture MOZ_FINAL {
 public:
   NS_INLINE_DECL_REFCOUNTING(SharedPluginTexture)
--- a/dom/plugins/base/nsPluginInstanceOwner.cpp
+++ b/dom/plugins/base/nsPluginInstanceOwner.cpp
@@ -160,17 +160,17 @@ nsPluginInstanceOwner::GetImageContainer
 
   LayoutDeviceRect r = GetPluginRect();
 
   // NotifySize() causes Flash to do a bunch of stuff like ask for surfaces to render
   // into, set y-flip flags, etc, so we do this at the beginning.
   gfxSize resolution = mObjectFrame->PresContext()->PresShell()->GetCumulativeResolution();
   ScreenSize screenSize = (r * LayoutDeviceToScreenScale(resolution.width, resolution.height)).Size();
   mInstance->NotifySize(nsIntSize(screenSize.width, screenSize.height));
-  
+
   container = LayerManager::CreateImageContainer();
 
   nsRefPtr<Image> img = container->CreateImage(ImageFormat::SHARED_TEXTURE);
 
   SharedTextureImage::Data data;
   data.mSize = gfx::IntSize(r.width, r.height);
   data.mHandle = mInstance->CreateSharedHandle();
   data.mShareType = mozilla::gl::SharedTextureShareType::SameProcess;
@@ -255,17 +255,17 @@ nsPluginInstanceOwner::GetCurrentImageSi
   return size;
 }
 
 nsPluginInstanceOwner::nsPluginInstanceOwner()
 {
   // create nsPluginNativeWindow object, it is derived from NPWindow
   // struct and allows to manipulate native window procedure
   nsCOMPtr<nsIPluginHost> pluginHostCOM = do_GetService(MOZ_PLUGIN_HOST_CONTRACTID);
-  mPluginHost = static_cast<nsPluginHost*>(pluginHostCOM.get());  
+  mPluginHost = static_cast<nsPluginHost*>(pluginHostCOM.get());
   if (mPluginHost)
     mPluginHost->NewPluginNativeWindow(&mPluginWindow);
   else
     mPluginWindow = nullptr;
 
   mObjectFrame = nullptr;
   mContent = nullptr;
   mWidgetCreationComplete = false;
@@ -424,17 +424,17 @@ NS_IMETHODIMP nsPluginInstanceOwner::Get
 
   return rv;
 }
 
 NS_IMETHODIMP nsPluginInstanceOwner::GetAttribute(const char* name, const char* *result)
 {
   NS_ENSURE_ARG_POINTER(name);
   NS_ENSURE_ARG_POINTER(result);
-  
+
   nsresult rv = EnsureCachedAttrParamArrays();
   NS_ENSURE_SUCCESS(rv, rv);
 
   *result = nullptr;
 
   for (int i = 0; i < mNumCachedAttrs; i++) {
     if (0 == PL_strcasecmp(mCachedAttrParamNames[i], name)) {
       *result = mCachedAttrParamValues[i];
@@ -525,19 +525,19 @@ NS_IMETHODIMP nsPluginInstanceOwner::Get
                        aPostStream, headersDataStream, true);
 
   return rv;
 }
 
 NS_IMETHODIMP nsPluginInstanceOwner::ShowStatus(const char *aStatusMsg)
 {
   nsresult  rv = NS_ERROR_FAILURE;
-  
+
   rv = this->ShowStatus(NS_ConvertUTF8toUTF16(aStatusMsg).get());
-  
+
   return rv;
 }
 
 NS_IMETHODIMP nsPluginInstanceOwner::ShowStatus(const char16_t *aStatusMsg)
 {
   nsresult  rv = NS_ERROR_FAILURE;
 
   if (!mObjectFrame) {
@@ -553,17 +553,17 @@ NS_IMETHODIMP nsPluginInstanceOwner::Sho
   if (NS_FAILED(rv) || !treeOwner) {
     return rv;
   }
 
   nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(treeOwner, &rv));
   if (NS_FAILED(rv) || !browserChrome) {
     return rv;
   }
-  rv = browserChrome->SetStatus(nsIWebBrowserChrome::STATUS_SCRIPT, 
+  rv = browserChrome->SetStatus(nsIWebBrowserChrome::STATUS_SCRIPT,
                                 aStatusMsg);
 
   return rv;
 }
 
 NS_IMETHODIMP nsPluginInstanceOwner::GetDocument(nsIDocument* *aDocument)
 {
   if (!aDocument)
@@ -621,73 +621,73 @@ NS_IMETHODIMP nsPluginInstanceOwner::Inv
   mObjectFrame->InvalidateLayer(nsDisplayItem::TYPE_PLUGIN, &rect);
   return NS_OK;
 }
 
 NS_IMETHODIMP nsPluginInstanceOwner::InvalidateRegion(NPRegion invalidRegion)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
- 
+
 NS_IMETHODIMP
 nsPluginInstanceOwner::RedrawPlugin()
 {
   if (mObjectFrame) {
     mObjectFrame->InvalidateLayer(nsDisplayItem::TYPE_PLUGIN);
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP nsPluginInstanceOwner::GetNetscapeWindow(void *value)
 {
   if (!mObjectFrame) {
     NS_WARNING("plugin owner has no owner in getting doc's window handle");
     return NS_ERROR_FAILURE;
   }
-  
+
 #if defined(XP_WIN)
   void** pvalue = (void**)value;
   nsViewManager* vm = mObjectFrame->PresContext()->GetPresShell()->GetViewManager();
   if (!vm)
     return NS_ERROR_FAILURE;
 #if defined(XP_WIN)
   // This property is provided to allow a "windowless" plugin to determine the window it is drawing
   // in, so it can translate mouse coordinates it receives directly from the operating system
   // to coordinates relative to itself.
-  
+
   // The original code (outside this #if) returns the document's window, which is OK if the window the "windowless" plugin
   // is drawing into has the same origin as the document's window, but this is not the case for "windowless" plugins inside of scrolling DIVs etc
-  
+
   // To make sure "windowless" plugins always get the right origin for translating mouse coordinates, this code
   // determines the window handle of the mozilla window containing the "windowless" plugin.
-  
+
   // Given that this HWND may not be that of the document's window, there is a slight risk
   // of confusing a plugin that is using this HWND for illicit purposes, but since the documentation
   // does not suggest this HWND IS that of the document window, rather that of the window
   // the plugin is drawn in, this seems like a safe fix.
-  
+
   // we only attempt to get the nearest window if this really is a "windowless" plugin so as not
   // to change any behaviour for the much more common windowed plugins,
   // though why this method would even be being called for a windowed plugin escapes me.
   if (mPluginWindow && mPluginWindow->type == NPWindowTypeDrawable) {
     // it turns out that flash also uses this window for determining focus, and is currently
     // unable to show a caret correctly if we return the enclosing window. Therefore for
     // now we only return the enclosing window when there is an actual offset which
     // would otherwise cause coordinates to be offset incorrectly. (i.e.
     // if the enclosing window if offset from the document window)
     //
     // fixing both the caret and ability to interact issues for a windowless control in a non document aligned windw
     // does not seem to be possible without a change to the flash plugin
-    
+
     nsIWidget* win = mObjectFrame->GetNearestWidget();
     if (win) {
       nsView *view = nsView::GetViewFor(win);
       NS_ASSERTION(view, "No view for widget");
       nsPoint offset = view->GetOffsetTo(nullptr);
-      
+
       if (offset.x || offset.y) {
         // in the case the two windows are offset from eachother, we do go ahead and return the correct enclosing window
         // so that mouse co-ordinates are not messed up.
         *pvalue = (void*)win->GetNativeData(NS_NATIVE_WINDOW);
         if (*pvalue)
           return NS_OK;
       }
     }
@@ -1175,17 +1175,17 @@ void nsPluginInstanceOwner::CARefresh(ns
     instanceOwner->GetWindow(window);
     if (!window) {
       continue;
     }
     NPRect r;
     r.left = 0;
     r.top = 0;
     r.right = window->width;
-    r.bottom = window->height; 
+    r.bottom = window->height;
     instanceOwner->InvalidateRect(&r);
   }
 }
 
 void nsPluginInstanceOwner::AddToCARefreshTimer() {
   if (!mInstance) {
     return;
   }
@@ -1215,17 +1215,17 @@ void nsPluginInstanceOwner::AddToCARefre
     sCATimer = new nsCOMPtr<nsITimer>();
     if (!sCATimer) {
       return;
     }
   }
 
   if (sCARefreshListeners->Length() == 1) {
     *sCATimer = do_CreateInstance("@mozilla.org/timer;1");
-    (*sCATimer)->InitWithFuncCallback(CARefresh, nullptr, 
+    (*sCATimer)->InitWithFuncCallback(CARefresh, nullptr,
                    DEFAULT_REFRESH_RATE, nsITimer::TYPE_REPEATING_SLACK);
   }
 }
 
 void nsPluginInstanceOwner::RemoveFromCARefreshTimer() {
   if (!sCARefreshListeners || sCARefreshListeners->Contains(this) == false) {
     return;
   }
@@ -1316,23 +1316,23 @@ void nsPluginInstanceOwner::RenderCoreAn
     ::CGContextDrawImage(aCGContext, CGRectMake(0,0,aWidth,aHeight), caImage);
   } else {
     NS_NOTREACHED("nsCARenderer::Render failure");
   }
 }
 
 void* nsPluginInstanceOwner::GetPluginPortCopy()
 {
-  if (GetDrawingModel() == NPDrawingModelCoreGraphics || 
+  if (GetDrawingModel() == NPDrawingModelCoreGraphics ||
       GetDrawingModel() == NPDrawingModelCoreAnimation ||
       GetDrawingModel() == NPDrawingModelInvalidatingCoreAnimation)
     return &mCGPluginPortCopy;
   return nullptr;
 }
-  
+
 // Currently (on OS X in Cocoa widgets) any changes made as a result of
 // calling GetPluginPortFromWidget() are immediately reflected in the NPWindow
 // structure that has been passed to the plugin via SetWindow().  This is
 // because calls to nsChildView::GetNativeData(NS_NATIVE_PLUGIN_PORT_CG)
 // always return a pointer to the same internal (private) object, but may
 // make changes inside that object.  All calls to GetPluginPortFromWidget() made while
 // the plugin is active (i.e. excluding those made at our initialization)
 // need to take this into account.  The easiest way to do so is to replace
@@ -1441,17 +1441,17 @@ LayoutDeviceRect nsPluginInstanceOwner::
   LayoutDeviceIntRect rect = LayoutDeviceIntRect::FromAppUnitsToNearest(bounds, mObjectFrame->PresContext()->AppUnitsPerDevPixel());
   return LayoutDeviceRect(rect);
 }
 
 bool nsPluginInstanceOwner::AddPluginView(const LayoutDeviceRect& aRect /* = LayoutDeviceRect(0, 0, 0, 0) */)
 {
   if (!mJavaView) {
     mJavaView = mInstance->GetJavaSurface();
-  
+
     if (!mJavaView)
       return false;
 
     mJavaView = (void*)AndroidBridge::GetJNIEnv()->NewGlobalRef((jobject)mJavaView);
   }
 
   if (AndroidBridge::Bridge())
     AndroidBridge::Bridge()->AddPluginView((jobject)mJavaView, aRect, mFullScreen);
@@ -1532,17 +1532,17 @@ void nsPluginInstanceOwner::RequestFullS
 
 void nsPluginInstanceOwner::ExitFullScreen() {
   if (!mFullScreen)
     return;
 
   RemovePluginView();
 
   mFullScreen = false;
-  
+
   int32_t model = mInstance->GetANPDrawingModel();
 
   if (model == kSurface_ANPDrawingModel) {
     // We need to do this immediately, otherwise Flash
     // sometimes causes a deadlock (bug 762407)
     AddPluginView(GetPluginRect());
   }
 
@@ -1576,17 +1576,17 @@ nsresult nsPluginInstanceOwner::Dispatch
     aFocusEvent->GetType(eventType);
     if (eventType.EqualsLiteral("focus")) {
       event.data.lifecycle.action = kGainFocus_ANPLifecycleAction;
     }
     else if (eventType.EqualsLiteral("blur")) {
       event.data.lifecycle.action = kLoseFocus_ANPLifecycleAction;
     }
     else {
-      NS_ASSERTION(false, "nsPluginInstanceOwner::DispatchFocusToPlugin, wierd eventType");   
+      NS_ASSERTION(false, "nsPluginInstanceOwner::DispatchFocusToPlugin, wierd eventType");
     }
     mInstance->HandleEvent(&event, nullptr);
   }
 #endif
 
 #ifndef XP_MACOSX
   if (!mPluginWindow || (mPluginWindow->type == NPWindowTypeWindow)) {
     // continue only for cases without child window
@@ -1598,21 +1598,21 @@ nsresult nsPluginInstanceOwner::Dispatch
   if (theEvent) {
     // we only care about the message in ProcessEvent
     WidgetGUIEvent focusEvent(theEvent->mFlags.mIsTrusted, theEvent->message,
                               nullptr);
     nsEventStatus rv = ProcessEvent(focusEvent);
     if (nsEventStatus_eConsumeNoDefault == rv) {
       aFocusEvent->PreventDefault();
       aFocusEvent->StopPropagation();
-    }   
+    }
   }
-  
+
   return NS_OK;
-}    
+}
 
 nsresult nsPluginInstanceOwner::ProcessKeyPress(nsIDOMEvent* aKeyEvent)
 {
 #ifdef XP_MACOSX
   return DispatchKeyToPlugin(aKeyEvent);
 #else
   if (SendNativeEvents())
     DispatchKeyToPlugin(aKeyEvent);
@@ -1638,54 +1638,54 @@ nsresult nsPluginInstanceOwner::Dispatch
   if (mInstance) {
     WidgetKeyboardEvent* keyEvent =
       aKeyEvent->GetInternalNSEvent()->AsKeyboardEvent();
     if (keyEvent && keyEvent->eventStructType == NS_KEY_EVENT) {
       nsEventStatus rv = ProcessEvent(*keyEvent);
       if (nsEventStatus_eConsumeNoDefault == rv) {
         aKeyEvent->PreventDefault();
         aKeyEvent->StopPropagation();
-      }   
+      }
     }
   }
 
   return NS_OK;
-}    
+}
 
 nsresult
 nsPluginInstanceOwner::ProcessMouseDown(nsIDOMEvent* aMouseEvent)
 {
 #if !defined(XP_MACOSX)
   if (!mPluginWindow || (mPluginWindow->type == NPWindowTypeWindow))
     return aMouseEvent->PreventDefault(); // consume event
   // continue only for cases without child window
 #endif
 
   // if the plugin is windowless, we need to set focus ourselves
   // otherwise, we might not get key events
   if (mObjectFrame && mPluginWindow &&
       mPluginWindow->type == NPWindowTypeDrawable) {
-    
+
     nsIFocusManager* fm = nsFocusManager::GetFocusManager();
     if (fm) {
       nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(mContent);
       fm->SetFocus(elem, 0);
     }
   }
 
   WidgetMouseEvent* mouseEvent =
     aMouseEvent->GetInternalNSEvent()->AsMouseEvent();
   if (mouseEvent && mouseEvent->eventStructType == NS_MOUSE_EVENT) {
     mLastMouseDownButtonType = mouseEvent->button;
     nsEventStatus rv = ProcessEvent(*mouseEvent);
     if (nsEventStatus_eConsumeNoDefault == rv) {
       return aMouseEvent->PreventDefault(); // consume event
     }
   }
-  
+
   return NS_OK;
 }
 
 nsresult nsPluginInstanceOwner::DispatchMouseToPlugin(nsIDOMEvent* aMouseEvent,
                                                       bool aAllowPropagate)
 {
 #if !defined(XP_MACOSX)
   if (!mPluginWindow || (mPluginWindow->type == NPWindowTypeWindow))
@@ -2110,17 +2110,17 @@ nsEventStatus nsPluginInstanceOwner::Pro
               event.same_screen = True;
             }
             break;
           }
       }
       break;
 
    //XXX case NS_MOUSE_SCROLL_EVENT: not received.
- 
+
    case NS_KEY_EVENT:
       if (anEvent.pluginEvent)
         {
           XKeyEvent &event = pluginEvent.xkey;
 #ifdef MOZ_WIDGET_GTK
           event.root = GDK_ROOT_WINDOW();
           event.time = anEvent.time;
           const GdkEventKey* gdkEvent =
@@ -2158,17 +2158,17 @@ nsEventStatus nsPluginInstanceOwner::Pro
           // If we need to send synthesized key events, then
           // DOMKeyCodeToGdkKeyCode(keyEvent.keyCode) and
           // gdk_keymap_get_entries_for_keyval will be useful, but the
           // mappings will not be unique.
           NS_WARNING("Synthesized key event not sent to plugin");
         }
       break;
 
-    default: 
+    default:
       switch (anEvent.message)
         {
         case NS_FOCUS_CONTENT:
         case NS_BLUR_CONTENT:
           {
             XFocusChangeEvent &event = pluginEvent.xfocus;
             event.type =
               anEvent.message == NS_FOCUS_CONTENT ? FocusIn : FocusOut;
@@ -2278,17 +2278,17 @@ nsEventStatus nsPluginInstanceOwner::Pro
      }
      break;
 
     default:
       break;
     }
     rv = nsEventStatus_eConsumeNoDefault;
 #endif
- 
+
   return rv;
 }
 
 nsresult
 nsPluginInstanceOwner::Destroy()
 {
   SetFrame(nullptr);
 
@@ -2349,17 +2349,17 @@ nsPluginInstanceOwner::Destroy()
 // Paints are handled differently, so we just simulate an update event.
 
 #ifdef XP_MACOSX
 void nsPluginInstanceOwner::Paint(const gfxRect& aDirtyRect, CGContextRef cgContext)
 {
   if (!mInstance || !mObjectFrame)
     return;
 
-  gfxRect dirtyRectCopy = aDirtyRect; 
+  gfxRect dirtyRectCopy = aDirtyRect;
   double scaleFactor = 1.0;
   GetContentsScaleFactor(&scaleFactor);
   if (scaleFactor != 1.0) {
     ::CGContextScaleCTM(cgContext, scaleFactor, scaleFactor);
     // Convert aDirtyRect from device pixels to "display pixels"
     // for HiDPI modes
     dirtyRectCopy.ScaleRoundOut(1.0 / scaleFactor);
   }
@@ -2370,17 +2370,17 @@ void nsPluginInstanceOwner::Paint(const 
     pluginWidget->EndDrawPlugin();
   }
 }
 
 void nsPluginInstanceOwner::DoCocoaEventDrawRect(const gfxRect& aDrawRect, CGContextRef cgContext)
 {
   if (!mInstance || !mObjectFrame)
     return;
- 
+
   // The context given here is only valid during the HandleEvent call.
   NPCocoaEvent updateEvent;
   InitializeNPCocoaEvent(&updateEvent);
   updateEvent.type = NPCocoaEventDrawRect;
   updateEvent.data.draw.context = cgContext;
   updateEvent.data.draw.x = aDrawRect.X();
   updateEvent.data.draw.y = aDrawRect.Y();
   updateEvent.data.draw.width = aDrawRect.Width();
@@ -2427,44 +2427,44 @@ void nsPluginInstanceOwner::Paint(gfxCon
 
 #ifdef ANP_BITMAP_DRAWING_MODEL
   static nsRefPtr<gfxImageSurface> pluginSurface;
 
   if (pluginSurface == nullptr ||
       aFrameRect.width  != pluginSurface->Width() ||
       aFrameRect.height != pluginSurface->Height()) {
 
-    pluginSurface = new gfxImageSurface(gfxIntSize(aFrameRect.width, aFrameRect.height), 
+    pluginSurface = new gfxImageSurface(gfxIntSize(aFrameRect.width, aFrameRect.height),
                                         gfxImageFormat::ARGB32);
     if (!pluginSurface)
       return;
   }
 
   // Clears buffer.  I think this is needed.
   gfxUtils::ClearThebesSurface(pluginSurface);
-  
+
   ANPEvent event;
   event.inSize = sizeof(ANPEvent);
   event.eventType = 4;
   event.data.draw.model = 1;
-  
+
   event.data.draw.clip.top     = 0;
   event.data.draw.clip.left    = 0;
   event.data.draw.clip.bottom  = aFrameRect.width;
   event.data.draw.clip.right   = aFrameRect.height;
-  
+
   event.data.draw.data.bitmap.format   = kRGBA_8888_ANPBitmapFormat;
   event.data.draw.data.bitmap.width    = aFrameRect.width;
   event.data.draw.data.bitmap.height   = aFrameRect.height;
   event.data.draw.data.bitmap.baseAddr = pluginSurface->Data();
   event.data.draw.data.bitmap.rowBytes = aFrameRect.width * 4;
-  
+
   if (!mInstance)
     return;
-    
+
   mInstance->HandleEvent(&event, nullptr);
 
   aContext->SetOperator(gfxContext::OPERATOR_SOURCE);
   aContext->SetSource(pluginSurface, gfxPoint(aFrameRect.x, aFrameRect.y));
   aContext->Clip(aFrameRect);
   aContext->Paint();
 #endif
 }
@@ -2538,17 +2538,17 @@ void nsPluginInstanceOwner::Paint(gfxCon
   Visual* visual = DefaultVisualOfScreen(screen);
 
   renderer.Draw(aContext, nsIntSize(window->width, window->height),
                 rendererFlags, screen, visual);
 }
 nsresult
 nsPluginInstanceOwner::Renderer::DrawWithXlib(cairo_surface_t* xsurface,
                                               nsIntPoint offset,
-                                              nsIntRect *clipRects, 
+                                              nsIntRect *clipRects,
                                               uint32_t numClipRects)
 {
   Screen *screen = cairo_xlib_surface_get_screen(xsurface);
   Colormap colormap;
   Visual* visual;
   if (!gfxXlibSurface::GetColormapAndVisual(xsurface, &colormap, &visual)) {
     NS_ERROR("Failed to get visual and colormap");
     return NS_ERROR_UNEXPECTED;
@@ -2607,17 +2607,17 @@ nsPluginInstanceOwner::Renderer::DrawWit
   if (mWindow->clipRect.left    != newClipRect.left   ||
       mWindow->clipRect.top     != newClipRect.top    ||
       mWindow->clipRect.right   != newClipRect.right  ||
       mWindow->clipRect.bottom  != newClipRect.bottom) {
     mWindow->clipRect = newClipRect;
     doupdatewindow = true;
   }
 
-  NPSetWindowCallbackStruct* ws_info = 
+  NPSetWindowCallbackStruct* ws_info =
     static_cast<NPSetWindowCallbackStruct*>(mWindow->ws_info);
 #ifdef MOZ_X11
   if (ws_info->visual != visual || ws_info->colormap != colormap) {
     ws_info->visual = visual;
     ws_info->colormap = colormap;
     ws_info->depth = gfxXlibSurface::DepthOfVisual(screen, visual);
     doupdatewindow = true;
   }
@@ -2720,32 +2720,32 @@ nsresult nsPluginInstanceOwner::Init(nsI
   mContent->AddEventListener(NS_LITERAL_STRING("dragenter"), this, true);
   mContent->AddEventListener(NS_LITERAL_STRING("dragover"), this, true);
   mContent->AddEventListener(NS_LITERAL_STRING("dragleave"), this, true);
   mContent->AddEventListener(NS_LITERAL_STRING("dragexit"), this, true);
   mContent->AddEventListener(NS_LITERAL_STRING("dragstart"), this, true);
   mContent->AddEventListener(NS_LITERAL_STRING("draggesture"), this, true);
   mContent->AddEventListener(NS_LITERAL_STRING("dragend"), this, true);
 
-  return NS_OK; 
+  return NS_OK;
 }
 
 void* nsPluginInstanceOwner::GetPluginPortFromWidget()
 {
 //!!! Port must be released for windowless plugins on Windows, because it is HDC !!!
 
   void* result = nullptr;
   if (mWidget) {
 #ifdef XP_WIN
     if (mPluginWindow && (mPluginWindow->type == NPWindowTypeDrawable))
       result = mWidget->GetNativeData(NS_NATIVE_GRAPHIC);
     else
 #endif
 #ifdef XP_MACOSX
-    if (GetDrawingModel() == NPDrawingModelCoreGraphics || 
+    if (GetDrawingModel() == NPDrawingModelCoreGraphics ||
         GetDrawingModel() == NPDrawingModelCoreAnimation ||
         GetDrawingModel() == NPDrawingModelInvalidatingCoreAnimation)
       result = mWidget->GetNativeData(NS_NATIVE_PLUGIN_PORT_CG);
     else
 #endif
       result = mWidget->GetNativeData(NS_NATIVE_PLUGIN_PORT);
   }
   return result;
@@ -2761,23 +2761,23 @@ void nsPluginInstanceOwner::ReleasePlugi
 #endif
 }
 
 NS_IMETHODIMP nsPluginInstanceOwner::CreateWidget(void)
 {
   NS_ENSURE_TRUE(mPluginWindow, NS_ERROR_NULL_POINTER);
 
   nsresult rv = NS_ERROR_FAILURE;
-  
+
   // Can't call this twice!
   if (mWidget) {
     NS_WARNING("Trying to create a plugin widget twice!");
     return NS_ERROR_FAILURE;
   }
-  
+
   bool windowless = false;
   mInstance->IsWindowless(&windowless);
   if (!windowless
 // Mac plugins use the widget for setting up the plugin window and event handling.
 // We want to use the puppet widgets there with e10s
 #ifndef XP_MACOSX
       && !nsIWidget::UsePuppetWidgets()
 #endif
@@ -2839,33 +2839,33 @@ NS_IMETHODIMP nsPluginInstanceOwner::Cre
 
     // this needs to be a HDC according to the spec, but I do
     // not see the right way to release it so let's postpone
     // passing HDC till paint event when it is really
     // needed. Change spec?
     mPluginWindow->window = nullptr;
 #ifdef MOZ_X11
     // Fill in the display field.
-    NPSetWindowCallbackStruct* ws_info = 
+    NPSetWindowCallbackStruct* ws_info =
     static_cast<NPSetWindowCallbackStruct*>(mPluginWindow->ws_info);
     ws_info->display = DefaultXDisplay();
-    
+
     nsAutoCString description;
     GetPluginDescription(description);
     NS_NAMED_LITERAL_CSTRING(flash10Head, "Shockwave Flash 10.");
     mFlash10Quirks = StringBeginsWith(description, flash10Head);
 #endif
   } else if (mWidget) {
     // mPluginWindow->type is used in |GetPluginPort| so it must
     // be initialized first
     mPluginWindow->type = NPWindowTypeWindow;
     mPluginWindow->window = GetPluginPortFromWidget();
     // tell the plugin window about the widget
     mPluginWindow->SetPluginWidget(mWidget);
-    
+
     // tell the widget about the current plugin instance owner.
     nsCOMPtr<nsIPluginWidget> pluginWidget = do_QueryInterface(mWidget);
     if (pluginWidget) {
       pluginWidget->SetPluginInstanceOwner(this);
     }
   }
 
   mWidgetCreationComplete = true;
--- a/dom/plugins/ipc/PluginInstanceParent.cpp
+++ b/dom/plugins/ipc/PluginInstanceParent.cpp
@@ -578,17 +578,17 @@ PluginInstanceParent::RecvShow(const NPR
         RefPtr<MacIOSurface> newIOSurface =
           MacIOSurface::LookupSurface(iodesc.surfaceId(),
                                       iodesc.contentsScaleFactor());
 
         if (!newIOSurface) {
             NS_WARNING("Got bad IOSurfaceDescriptor in RecvShow");
             return false;
         }
-      
+
         if (mFrontIOSurface)
             *prevSurface = IOSurfaceDescriptor(mFrontIOSurface->GetIOSurfaceID(),
                                                mFrontIOSurface->GetContentsScaleFactor());
         else
             *prevSurface = null_t();
 
         mFrontIOSurface = newIOSurface;
 
@@ -619,17 +619,17 @@ PluginInstanceParent::RecvShow(const NPR
         // referencing it.