merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 13 Jul 2015 11:51:14 +0200
changeset 252532 38d03bf4616e7fb1eaddf20cae83c9b8c0f4e7ab
parent 252387 167b744bf171eced6b82c3c17e480dbb2c831cf2 (current diff)
parent 252531 76b77a5ace934d9909d60d70e7e7f1b607d20812 (diff)
child 252534 149e770b65ac588cba337eb0791310929faff478
child 252552 a5051f562117fd50f4e078b4c0ec8efcedc939d3
child 252608 694c698ed1c8d5f89448c87c948dd3879447495c
push id29035
push usercbook@mozilla.com
push dateMon, 13 Jul 2015 09:52:17 +0000
treeherdermozilla-central@38d03bf4616e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone42.0a1
first release with
nightly linux32
38d03bf4616e / 42.0a1 / 20150713030204 / files
nightly linux64
38d03bf4616e / 42.0a1 / 20150713030204 / files
nightly mac
38d03bf4616e / 42.0a1 / 20150713030204 / files
nightly win32
38d03bf4616e / 42.0a1 / 20150713030204 / files
nightly win64
38d03bf4616e / 42.0a1 / 20150713030204 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
dom/audiochannel/AudioChannelCommon.h
dom/audiochannel/AudioChannelServiceChild.cpp
dom/audiochannel/AudioChannelServiceChild.h
dom/audiochannel/tests/AudioChannelChromeScript.js
dom/audiochannel/tests/TestAudioChannelService.cpp
dom/audiochannel/tests/audio.ogg
dom/audiochannel/tests/file_audio.html
dom/audiochannel/tests/file_telephonyPolicy.html
dom/audiochannel/tests/mochitest.ini
dom/audiochannel/tests/moz.build
dom/audiochannel/tests/test_audioChannelChange.html
dom/audiochannel/tests/test_telephonyPolicy.html
dom/media/webaudio/test/browser.ini
dom/media/webaudio/test/browser_mozAudioChannel.html
dom/media/webaudio/test/browser_mozAudioChannel.js
dom/media/webaudio/test/browser_mozAudioChannel_muted.html
dom/media/webaudio/test/browser_mozAudioChannel_muted.js
mobile/android/base/Makefile.in
toolkit/components/addoncompat/remoteTagService.js
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,9 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Bug 1178892 requires clobber for profiler file moves.
+Bug 1178850 requires clobber for Android JNI header changes
--- a/b2g/config/mozconfigs/common.override
+++ b/b2g/config/mozconfigs/common.override
@@ -1,7 +1,8 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 # This file is included at the bottom of all b2g mozconfigs
 
 . "$topsrcdir/build/mozconfig.common.override"
+. "$topsrcdir/build/mozconfig.cache"
--- a/b2g/config/mozconfigs/ics_armv7a_gecko/debug
+++ b/b2g/config/mozconfigs/ics_armv7a_gecko/debug
@@ -7,15 +7,14 @@ ac_add_options --enable-b2g-camera
 
 ac_add_options --target=arm-linux-androideabi
 ac_add_options --with-gonk="$topsrcdir/gonk-toolchain"
 export TOOLCHAIN_HOST=linux-x86
 export GONK_PRODUCT=generic
 ac_add_options --with-gonk-toolchain-prefix="$topsrcdir/gonk-toolchain/prebuilt/$TOOLCHAIN_HOST/toolchain/arm-linux-androideabi-4.4.x/bin/arm-linux-androideabi-"
 ac_add_options --enable-debug-symbols
 ac_add_options --enable-debug
-#. "$topsrcdir/build/mozconfig.cache"
 ENABLE_MARIONETTE=1
 
 # Enable dump() from JS.
 export CXXFLAGS="-DMOZ_ENABLE_JS_DUMP -include $topsrcdir/gonk-toolchain/gonk-misc/Unicode.h -include $topsrcdir/gonk-toolchain/system/vold/ResponseCode.h"
 
 . "$topsrcdir/b2g/config/mozconfigs/common.override"
--- a/b2g/config/mozconfigs/ics_armv7a_gecko/nightly
+++ b/b2g/config/mozconfigs/ics_armv7a_gecko/nightly
@@ -8,15 +8,14 @@ ac_add_options --enable-updater
 
 ac_add_options --target=arm-linux-androideabi
 ac_add_options --with-gonk="$topsrcdir/gonk-toolchain"
 export TOOLCHAIN_HOST=linux-x86
 export GONK_PRODUCT=generic
 ac_add_options --with-gonk-toolchain-prefix="$topsrcdir/gonk-toolchain/prebuilt/$TOOLCHAIN_HOST/toolchain/arm-linux-androideabi-4.4.x/bin/arm-linux-androideabi-"
 ac_add_options --enable-debug-symbols
 # ac_add_options --enable-profiling
-#. "$topsrcdir/build/mozconfig.cache"
 ENABLE_MARIONETTE=1
 
 # Enable dump() from JS.
 export CXXFLAGS="-DMOZ_ENABLE_JS_DUMP -include $topsrcdir/gonk-toolchain/gonk-misc/Unicode.h -include $topsrcdir/gonk-toolchain/system/vold/ResponseCode.h"
 
 . "$topsrcdir/b2g/config/mozconfigs/common.override"
--- a/b2g/config/mozconfigs/linux32_gecko/debug
+++ b/b2g/config/mozconfigs/linux32_gecko/debug
@@ -4,35 +4,31 @@ MOZ_AUTOMATION_UPDATE_PACKAGING=0
 . "$topsrcdir/b2g/config/mozconfigs/common"
 . "$topsrcdir/build/unix/mozconfig.linux32"
 
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 ac_add_options --enable-update-packaging
 ac_add_options --enable-signmar
 ac_add_options --enable-debug
 
-# Nightlies only since this has a cost in performance
-#ac_add_options --enable-js-diagnostics
-
 # This will overwrite the default of stripping everything and keep the symbol table.
 # This is useful for profiling and debugging and only increases the package size
 # by 2 MBs.
 STRIP_FLAGS="--strip-debug"
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 export MOZ_TELEMETRY_REPORTING=1
 
 # Treat warnings as errors in directories with FAIL_ON_WARNINGS.
 ac_add_options --enable-warnings-as-errors
 
 # Use sccache
 no_sccache=
-. "$topsrcdir/build/mozconfig.cache"
 
 #B2G options
 ac_add_options --enable-application=b2g
 ENABLE_MARIONETTE=1
 export CXXFLAGS=-DMOZ_ENABLE_JS_DUMP
 
 GAIADIR=$topsrcdir/gaia
 
--- a/b2g/config/mozconfigs/linux32_gecko/nightly
+++ b/b2g/config/mozconfigs/linux32_gecko/nightly
@@ -4,35 +4,31 @@ MOZ_AUTOMATION_UPDATE_PACKAGING=0
 MOZ_AUTOMATION_SDK=0
 . "$topsrcdir/b2g/config/mozconfigs/common"
 . "$topsrcdir/build/unix/mozconfig.linux32"
 
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 ac_add_options --enable-update-packaging
 ac_add_options --enable-signmar
 
-# Nightlies only since this has a cost in performance
-#ac_add_options --enable-js-diagnostics
-
 # This will overwrite the default of stripping everything and keep the symbol table.
 # This is useful for profiling and debugging and only increases the package size
 # by 2 MBs.
 STRIP_FLAGS="--strip-debug"
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 export MOZ_TELEMETRY_REPORTING=1
 
 # Treat warnings as errors in directories with FAIL_ON_WARNINGS.
 ac_add_options --enable-warnings-as-errors
 
 # Use sccache
 no_sccache=
-. "$topsrcdir/build/mozconfig.cache"
 
 #B2G options
 ac_add_options --enable-application=b2g
 export CXXFLAGS=-DMOZ_ENABLE_JS_DUMP
 
 GAIADIR=$topsrcdir/gaia
 
 # Include Firefox OS fonts.
--- a/b2g/config/mozconfigs/linux64_gecko/debug
+++ b/b2g/config/mozconfigs/linux64_gecko/debug
@@ -4,35 +4,31 @@ MOZ_AUTOMATION_UPDATE_PACKAGING=0
 . "$topsrcdir/b2g/config/mozconfigs/common"
 . "$topsrcdir/build/unix/mozconfig.linux"
 
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 ac_add_options --enable-update-packaging
 ac_add_options --enable-signmar
 ac_add_options --enable-debug
 
-# Nightlies only since this has a cost in performance
-#ac_add_options --enable-js-diagnostics
-
 # This will overwrite the default of stripping everything and keep the symbol table.
 # This is useful for profiling and debugging and only increases the package size
 # by 2 MBs.
 STRIP_FLAGS="--strip-debug"
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 export MOZ_TELEMETRY_REPORTING=1
 
 # Treat warnings as errors in directories with FAIL_ON_WARNINGS.
 ac_add_options --enable-warnings-as-errors
 
 # Use sccache
 no_sccache=
-. "$topsrcdir/build/mozconfig.cache"
 
 #B2G options
 ac_add_options --enable-application=b2g
 ENABLE_MARIONETTE=1
 export CXXFLAGS=-DMOZ_ENABLE_JS_DUMP
 
 GAIADIR=$topsrcdir/gaia
 
--- a/b2g/config/mozconfigs/linux64_gecko/nightly
+++ b/b2g/config/mozconfigs/linux64_gecko/nightly
@@ -4,35 +4,31 @@ MOZ_AUTOMATION_UPDATE_PACKAGING=0
 MOZ_AUTOMATION_SDK=0
 . "$topsrcdir/b2g/config/mozconfigs/common"
 . "$topsrcdir/build/unix/mozconfig.linux"
 
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 ac_add_options --enable-update-packaging
 ac_add_options --enable-signmar
 
-# Nightlies only since this has a cost in performance
-#ac_add_options --enable-js-diagnostics
-
 # This will overwrite the default of stripping everything and keep the symbol table.
 # This is useful for profiling and debugging and only increases the package size
 # by 2 MBs.
 STRIP_FLAGS="--strip-debug"
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 export MOZ_TELEMETRY_REPORTING=1
 
 # Treat warnings as errors in directories with FAIL_ON_WARNINGS.
 ac_add_options --enable-warnings-as-errors
 
 # Use sccache
 no_sccache=
-. "$topsrcdir/build/mozconfig.cache"
 
 #B2G options
 ac_add_options --enable-application=b2g
 export CXXFLAGS=-DMOZ_ENABLE_JS_DUMP
 
 GAIADIR=$topsrcdir/gaia
 
 # Include Firefox OS fonts.
--- a/b2g/config/mozconfigs/macosx64_gecko/debug
+++ b/b2g/config/mozconfigs/macosx64_gecko/debug
@@ -6,19 +6,16 @@ MOZ_AUTOMATION_UPDATE_PACKAGING=0
 no_sccache=
 
 . $topsrcdir/build/macosx/mozconfig.common
 
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 ac_add_options --enable-update-packaging
 ac_add_options --enable-signmar
 
-# Nightlies only since this has a cost in performance
-ac_add_options --enable-js-diagnostics
-
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 export MOZ_TELEMETRY_REPORTING=1
 
 #ac_add_options --with-macbundlename-prefix=Firefox
 
 # Treat warnings as errors in directories with FAIL_ON_WARNINGS.
--- a/b2g/config/mozconfigs/macosx64_gecko/nightly
+++ b/b2g/config/mozconfigs/macosx64_gecko/nightly
@@ -7,19 +7,16 @@ MOZ_AUTOMATION_SDK=0
 no_sccache=
 
 . $topsrcdir/build/macosx/mozconfig.common
 
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 ac_add_options --enable-update-packaging
 ac_add_options --enable-signmar
 
-# Nightlies only since this has a cost in performance
-ac_add_options --enable-js-diagnostics
-
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 export MOZ_TELEMETRY_REPORTING=1
 
 #ac_add_options --with-macbundlename-prefix=Firefox
 
 # Treat warnings as errors in directories with FAIL_ON_WARNINGS.
--- a/b2g/config/mozconfigs/win32_gecko/debug
+++ b/b2g/config/mozconfigs/win32_gecko/debug
@@ -4,19 +4,16 @@ MOZ_AUTOMATION_UPDATE_PACKAGING=0
 . "$topsrcdir/b2g/config/mozconfigs/common"
 
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 ac_add_options --enable-update-packaging
 ac_add_options --enable-jemalloc
 ac_add_options --enable-signmar
 ac_add_options --enable-debug
 
-# Nightlies only since this has a cost in performance
-ac_add_options --enable-js-diagnostics
-
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 export MOZ_TELEMETRY_REPORTING=1
 
 if test "$PROCESSOR_ARCHITECTURE" = "AMD64" -o "$PROCESSOR_ARCHITEW6432" = "AMD64"; then
   . $topsrcdir/build/win32/mozconfig.vs2013-win64
 else
--- a/b2g/config/mozconfigs/win32_gecko/nightly
+++ b/b2g/config/mozconfigs/win32_gecko/nightly
@@ -4,19 +4,16 @@ MOZ_AUTOMATION_UPDATE_PACKAGING=0
 MOZ_AUTOMATION_SDK=0
 . "$topsrcdir/b2g/config/mozconfigs/common"
 
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 ac_add_options --enable-update-packaging
 ac_add_options --enable-jemalloc
 ac_add_options --enable-signmar
 
-# Nightlies only since this has a cost in performance
-ac_add_options --enable-js-diagnostics
-
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 export MOZ_TELEMETRY_REPORTING=1
 
 if test "$PROCESSOR_ARCHITECTURE" = "AMD64" -o "$PROCESSOR_ARCHITEW6432" = "AMD64"; then
   . $topsrcdir/build/win32/mozconfig.vs2013-win64
 else
--- a/b2g/config/tooltool-manifests/linux32/releng.manifest
+++ b/b2g/config/tooltool-manifests/linux32/releng.manifest
@@ -1,28 +1,23 @@
 [
 {
-"size": 51,
-"digest": "c8e40edb314eeabfb92c77cf5ff9a7857033f15dd65a00349bcf9e3e5b75624afc71f733b2ff7e029c20a78313038409c2bd022bf7e5a7e0c487fc2c2d640986",
-"algorithm": "sha512",
-"filename": "setup.sh"
-},
-{
 "size": 80458572,
 "digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad",
 "algorithm": "sha512",
 "filename": "gcc.tar.xz",
-"unpack": "True"
+"unpack": true
 },
 {
 "size": 167175,
 "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
 "algorithm": "sha512",
-"filename": "sccache.tar.bz2"
+"filename": "sccache.tar.bz2",
+"unpack": true
 },
 {
 "size": 31057326,
 "digest": "b844c3e52be493d2cacafa58c4a924b89c9be8d2dcc2a7c71aed58c253d8035fba4d51df309f73e3c4342a1f3c3898a9a25c4815e2112888d1280f43c41c8e51",
 "algorithm": "sha512",
 "filename": "moztt.tar.bz2",
-"unpack": "True"
+"unpack": true
 }
 ]
--- a/b2g/config/tooltool-manifests/linux64/releng.manifest
+++ b/b2g/config/tooltool-manifests/linux64/releng.manifest
@@ -1,28 +1,23 @@
 [
 {
-"size": 51,
-"digest": "c8e40edb314eeabfb92c77cf5ff9a7857033f15dd65a00349bcf9e3e5b75624afc71f733b2ff7e029c20a78313038409c2bd022bf7e5a7e0c487fc2c2d640986",
-"algorithm": "sha512",
-"filename": "setup.sh"
-},
-{
 "size": 80458572,
 "digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad",
 "algorithm": "sha512",
 "filename": "gcc.tar.xz",
-"unpack": "True"
+"unpack": true
 },
 {
 "size": 167175,
 "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
 "algorithm": "sha512",
-"filename": "sccache.tar.bz2"
+"filename": "sccache.tar.bz2",
+"unpack": true
 },
 {
 "size": 31057326,
 "digest": "b844c3e52be493d2cacafa58c4a924b89c9be8d2dcc2a7c71aed58c253d8035fba4d51df309f73e3c4342a1f3c3898a9a25c4815e2112888d1280f43c41c8e51",
 "algorithm": "sha512",
 "filename": "moztt.tar.bz2",
-"unpack": "True"
+"unpack": true
 }
 ]
--- a/b2g/config/tooltool-manifests/macosx64/releng.manifest
+++ b/b2g/config/tooltool-manifests/macosx64/releng.manifest
@@ -1,30 +1,26 @@
 [
 {
 "clang_version": "r183744"
 },
 {
-"size": 88,
-"digest": "0d2ae9bcd7cea34ec0b768270725e98410dbb3bc150c7381e0dcf3eb5dbb3e69ac76dbb0f46b056151d6a6fa8681cab06da68173ae8598f3397b8f7628e67381",
-"algorithm": "sha512",
-"filename": "setup.sh"
-},
-{
 "size": 59602619,
 "digest": "86662ebc0ef650490559005948c4f0cb015dad72c7cac43732c2bf2995247081e30c139cf8008d19670a0009fc302c4eee2676981ee3f9ff4a15c01af22b783b",
 "algorithm": "sha512",
-"filename": "clang.tar.bz2"
+"filename": "clang.tar.bz2",
+"unpack": true
 },
 {
 "size": 167175,
 "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
 "algorithm": "sha512",
-"filename": "sccache.tar.bz2"
+"filename": "sccache.tar.bz2",
+"unpack": true
 },
 {
 "size": 31057326,
 "digest": "b844c3e52be493d2cacafa58c4a924b89c9be8d2dcc2a7c71aed58c253d8035fba4d51df309f73e3c4342a1f3c3898a9a25c4815e2112888d1280f43c41c8e51",
 "algorithm": "sha512",
 "filename": "moztt.tar.bz2",
-"unpack": "True"
+"unpack": true
 }
 ]
--- a/b2g/config/tooltool-manifests/win32/releng.manifest
+++ b/b2g/config/tooltool-manifests/win32/releng.manifest
@@ -5,11 +5,18 @@
 "algorithm": "sha512",
 "filename": "mozmake.exe"
 },
 {
 "size": 31057326,
 "digest": "b844c3e52be493d2cacafa58c4a924b89c9be8d2dcc2a7c71aed58c253d8035fba4d51df309f73e3c4342a1f3c3898a9a25c4815e2112888d1280f43c41c8e51",
 "algorithm": "sha512",
 "filename": "moztt.tar.bz2",
-"unpack": "True"
+"unpack": true
+},
+{
+"size": 167175,
+"digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
+"algorithm": "sha512",
+"filename": "sccache.tar.bz2",
+"unpack": true
 }
 ]
--- a/b2g/dev/config/mozconfigs/macosx64/mulet
+++ b/b2g/dev/config/mozconfigs/macosx64/mulet
@@ -7,26 +7,24 @@ MOZ_AUTOMATION_SDK=0
 
 ac_add_options --enable-application=b2g/dev
 ac_add_options --disable-install-strip
 ac_add_options --enable-signmar
 ac_add_options --enable-profiling
 ac_add_options --enable-instruments
 ac_add_options --enable-dtrace
 
-# Nightlies only since this has a cost in performance
-ac_add_options --enable-js-diagnostics
-
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 ac_add_options --with-macbundlename-prefix=Firefox
 
 # Treat warnings as errors in directories with FAIL_ON_WARNINGS.
 ac_add_options --enable-warnings-as-errors
 
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
 
 # Include Firefox OS fonts.
 MOZTTDIR=$topsrcdir/moztt
 
 . "$topsrcdir/build/mozconfig.common.override"
+. "$topsrcdir/build/mozconfig.cache"
--- a/b2g/dev/config/tooltool-manifests/linux64/releng.manifest
+++ b/b2g/dev/config/tooltool-manifests/linux64/releng.manifest
@@ -1,27 +1,23 @@
 [
 {
-"size": 82,
-"digest": "70a6126249e40aa1da32248bf6bfe45e0d8c87334579ec0cf69403e61b635e27c766d9bf08d530978286552f158ee24c74b0168a57cc6b734dcfed4fc5e09cff",
-"algorithm": "sha512",
-"filename": "setup.sh"
-},
-{
 "size": 80458572,
 "digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad",
 "algorithm": "sha512",
-"filename": "gcc.tar.xz"
+"filename": "gcc.tar.xz",
+"unpack": true
 },
 {
 "size": 167175,
 "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
 "algorithm": "sha512",
-"filename": "sccache.tar.bz2"
+"filename": "sccache.tar.bz2",
+"unpack": true
 },
 {
 "size": 31057326,
 "digest": "b844c3e52be493d2cacafa58c4a924b89c9be8d2dcc2a7c71aed58c253d8035fba4d51df309f73e3c4342a1f3c3898a9a25c4815e2112888d1280f43c41c8e51",
 "algorithm": "sha512",
 "filename": "moztt.tar.bz2",
-"unpack": "True"
+"unpack": true
 }
 ]
--- a/b2g/dev/config/tooltool-manifests/macosx64/releng.manifest
+++ b/b2g/dev/config/tooltool-manifests/macosx64/releng.manifest
@@ -1,30 +1,26 @@
 [
 {
 "clang_version": "r183744"
 },
 {
-"size": 88,
-"digest": "0d2ae9bcd7cea34ec0b768270725e98410dbb3bc150c7381e0dcf3eb5dbb3e69ac76dbb0f46b056151d6a6fa8681cab06da68173ae8598f3397b8f7628e67381",
-"algorithm": "sha512",
-"filename": "setup.sh"
-},
-{
 "size": 59602619,
 "digest": "86662ebc0ef650490559005948c4f0cb015dad72c7cac43732c2bf2995247081e30c139cf8008d19670a0009fc302c4eee2676981ee3f9ff4a15c01af22b783b",
 "algorithm": "sha512",
-"filename": "clang.tar.bz2"
+"filename": "clang.tar.bz2",
+"unpack": true
 },
 {
 "size": 167175,
 "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
 "algorithm": "sha512",
-"filename": "sccache.tar.bz2"
+"filename": "sccache.tar.bz2",
+"unpack": true
 },
 {
 "size": 31057326,
 "digest": "b844c3e52be493d2cacafa58c4a924b89c9be8d2dcc2a7c71aed58c253d8035fba4d51df309f73e3c4342a1f3c3898a9a25c4815e2112888d1280f43c41c8e51",
 "algorithm": "sha512",
 "filename": "moztt.tar.bz2",
-"unpack": "True"
+"unpack": true
 }
 ]
--- a/b2g/dev/config/tooltool-manifests/win32/releng.manifest
+++ b/b2g/dev/config/tooltool-manifests/win32/releng.manifest
@@ -1,27 +1,22 @@
 [
 {
 "size": 266240,
 "digest": "bb345b0e700ffab4d09436981f14b5de84da55a3f18a7f09ebc4364a4488acdeab8d46f447b12ac70f2da1444a68b8ce8b8675f0dae2ccf845e966d1df0f0869",
 "algorithm": "sha512",
 "filename": "mozmake.exe"
 },
 {
-"size": 51,
-"digest": "c8e40edb314eeabfb92c77cf5ff9a7857033f15dd65a00349bcf9e3e5b75624afc71f733b2ff7e029c20a78313038409c2bd022bf7e5a7e0c487fc2c2d640986",
-"algorithm": "sha512",
-"filename": "setup.sh"
-},
-{
 "size": 167175,
 "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
 "algorithm": "sha512",
-"filename": "sccache.tar.bz2"
+"filename": "sccache.tar.bz2",
+"unpack": true
 },
 {
 "size": 31057326,
 "digest": "b844c3e52be493d2cacafa58c4a924b89c9be8d2dcc2a7c71aed58c253d8035fba4d51df309f73e3c4342a1f3c3898a9a25c4815e2112888d1280f43c41c8e51",
 "algorithm": "sha512",
 "filename": "moztt.tar.bz2",
-"unpack": "True"
+"unpack": true
 }
 ]
--- a/browser/config/mozconfigs/linux32/debug
+++ b/browser/config/mozconfigs/linux32/debug
@@ -6,19 +6,19 @@ ac_add_options --with-google-oauth-api-k
 MOZ_AUTOMATION_L10N_CHECK=0
 
 . $topsrcdir/build/unix/mozconfig.linux32
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 #Use ccache
-. "$topsrcdir/build/mozconfig.cache"
 
 # Treat warnings as errors in directories with FAIL_ON_WARNINGS.
 ac_add_options --enable-warnings-as-errors
 
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
 
 ac_add_options --with-branding=browser/branding/nightly
 
 . "$topsrcdir/build/mozconfig.common.override"
+. "$topsrcdir/build/mozconfig.cache"
--- a/browser/config/mozconfigs/linux32/nightly
+++ b/browser/config/mozconfigs/linux32/nightly
@@ -1,19 +1,14 @@
 . "$topsrcdir/browser/config/mozconfigs/linux32/common-opt"
 
 ac_add_options --enable-signmar
 ac_add_options --enable-profiling
 
-# Nightlies only since this has a cost in performance
-ac_add_options --enable-js-diagnostics
-
 # This will overwrite the default of stripping everything and keep the symbol table.
 # This is useful for profiling and debugging and only increases the package size
 # by 2 MBs.
 STRIP_FLAGS="--strip-debug"
 
 ac_add_options --with-branding=browser/branding/nightly
 
-# Use ccache
+. "$topsrcdir/build/mozconfig.common.override"
 . "$topsrcdir/build/mozconfig.cache"
-
-. "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/linux64/debug
+++ b/browser/config/mozconfigs/linux64/debug
@@ -5,20 +5,18 @@ ac_add_options --with-google-oauth-api-k
 
 MOZ_AUTOMATION_L10N_CHECK=0
 
 . $topsrcdir/build/unix/mozconfig.linux
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
-# Use ccache
-. "$topsrcdir/build/mozconfig.cache"
-
 # Treat warnings as errors in directories with FAIL_ON_WARNINGS.
 ac_add_options --enable-warnings-as-errors
 
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
 
 ac_add_options --with-branding=browser/branding/nightly
 
 . "$topsrcdir/build/mozconfig.common.override"
+. "$topsrcdir/build/mozconfig.cache"
--- a/browser/config/mozconfigs/linux64/nightly
+++ b/browser/config/mozconfigs/linux64/nightly
@@ -1,19 +1,14 @@
 . "$topsrcdir/browser/config/mozconfigs/linux64/common-opt"
 
 ac_add_options --enable-signmar
 ac_add_options --enable-profiling
 
-# Nightlies only since this has a cost in performance
-ac_add_options --enable-js-diagnostics
-
 # This will overwrite the default of stripping everything and keep the symbol table.
 # This is useful for profiling and debugging and only increases the package size
 # by 2 MBs.
 STRIP_FLAGS="--strip-debug"
 
 ac_add_options --with-branding=browser/branding/nightly
 
-# Use ccache
+. "$topsrcdir/build/mozconfig.common.override"
 . "$topsrcdir/build/mozconfig.cache"
-
-. "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/macosx-universal/beta
+++ b/browser/config/mozconfigs/macosx-universal/beta
@@ -1,5 +1,6 @@
 . "$topsrcdir/browser/config/mozconfigs/macosx-universal/common-opt"
 
 ac_add_options --enable-official-branding
 
 . "$topsrcdir/build/mozconfig.common.override"
+. "$topsrcdir/build/mozconfig.cache"
--- a/browser/config/mozconfigs/macosx-universal/l10n-mozconfig
+++ b/browser/config/mozconfigs/macosx-universal/l10n-mozconfig
@@ -5,13 +5,12 @@ ac_add_options --with-l10n-base=../../..
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 ac_add_options --enable-update-packaging
 ac_add_options --with-branding=browser/branding/nightly
 
 if test "${MOZ_UPDATE_CHANNEL}" = "nightly"; then
 ac_add_options --with-macbundlename-prefix=Firefox
 fi
 
-. "$topsrcdir/build/mozconfig.cache"
-
 export MOZILLA_OFFICIAL=1
 
 . "$topsrcdir/build/mozconfig.common.override"
+. "$topsrcdir/build/mozconfig.cache"
--- a/browser/config/mozconfigs/macosx-universal/nightly
+++ b/browser/config/mozconfigs/macosx-universal/nightly
@@ -1,18 +1,16 @@
 . "$topsrcdir/browser/config/mozconfigs/macosx-universal/common-opt"
 
 ac_add_options --disable-install-strip
 ac_add_options --enable-signmar
 ac_add_options --enable-profiling
 ac_add_options --enable-instruments
 ac_add_options --enable-dtrace
 
-# Nightlies only since this has a cost in performance
-ac_add_options --enable-js-diagnostics
-
 if test "${MOZ_UPDATE_CHANNEL}" = "nightly"; then
 ac_add_options --with-macbundlename-prefix=Firefox
 fi
 
 ac_add_options --with-branding=browser/branding/nightly
 
 . "$topsrcdir/build/mozconfig.common.override"
+. "$topsrcdir/build/mozconfig.cache"
--- a/browser/config/mozconfigs/macosx-universal/release
+++ b/browser/config/mozconfigs/macosx-universal/release
@@ -4,8 +4,9 @@
 
 ac_add_options --enable-official-branding
 
 # safeguard against someone forgetting to re-set EARLY_BETA_OR_EARLIER in
 # defines.sh during the beta cycle
 export BUILDING_RELEASE=1
 
 . "$topsrcdir/build/mozconfig.common.override"
+. "$topsrcdir/build/mozconfig.cache"
--- a/browser/config/mozconfigs/macosx64/debug
+++ b/browser/config/mozconfigs/macosx64/debug
@@ -16,8 +16,9 @@ fi
 ac_add_options --enable-warnings-as-errors
 
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
 
 ac_add_options --with-branding=browser/branding/nightly
 
 . "$topsrcdir/build/mozconfig.common.override"
+. "$topsrcdir/build/mozconfig.cache"
--- a/browser/config/mozconfigs/macosx64/debug-static-analysis
+++ b/browser/config/mozconfigs/macosx64/debug-static-analysis
@@ -8,8 +8,9 @@ ac_add_options --enable-debug
 ac_add_options --enable-dmd
 
 # Treat warnings as errors in directories with FAIL_ON_WARNINGS.
 ac_add_options --enable-warnings-as-errors
 
 ac_add_options --enable-clang-plugin
 
 . "$topsrcdir/build/mozconfig.common.override"
+. "$topsrcdir/build/mozconfig.cache"
--- a/browser/config/mozconfigs/macosx64/l10n-mozconfig
+++ b/browser/config/mozconfigs/macosx64/l10n-mozconfig
@@ -1,9 +1,9 @@
 . "$topsrcdir/browser/config/mozconfigs/common"
 
 ac_add_options --with-l10n-base=../../l10n
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 ac_add_options --enable-update-packaging
 ac_add_options --with-branding=browser/branding/nightly
-. "$topsrcdir/build/mozconfig.cache"
 
 . "$topsrcdir/build/mozconfig.common.override"
+. "$topsrcdir/build/mozconfig.cache"
--- a/browser/config/mozconfigs/macosx64/nightly
+++ b/browser/config/mozconfigs/macosx64/nightly
@@ -14,8 +14,9 @@ fi
 ac_add_options --enable-warnings-as-errors
 
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
 
 ac_add_options --with-branding=browser/branding/nightly
 
 . "$topsrcdir/build/mozconfig.common.override"
+. "$topsrcdir/build/mozconfig.cache"
--- a/browser/config/mozconfigs/whitelist
+++ b/browser/config/mozconfigs/whitelist
@@ -17,17 +17,16 @@ for platform in all_platforms:
 
 for platform in ['linux32', 'linux64', 'macosx-universal']:
     whitelist['nightly'][platform] += [
         'mk_add_options MOZ_MAKE_FLAGS="-j4"',
     ]
 
 for platform in ['linux32', 'linux64', 'macosx-universal', 'win32', 'win64']:
     whitelist['nightly'][platform] += ['ac_add_options --enable-signmar']
-    whitelist['nightly'][platform] += ['ac_add_options --enable-js-diagnostics']
 
 whitelist['nightly']['linux32'] += [
     'CXX=$REAL_CXX',
     'CXX="ccache $REAL_CXX"',
     'CC="ccache $REAL_CC"',
     'mk_add_options PROFILE_GEN_SCRIPT=@TOPSRCDIR@/build/profile_pageloader.pl',
     'ac_add_options --with-ccache=/usr/bin/ccache',
     '. "$topsrcdir/build/mozconfig.cache"',
--- a/browser/config/mozconfigs/win32/debug
+++ b/browser/config/mozconfigs/win32/debug
@@ -23,11 +23,10 @@ export MOZILLA_OFFICIAL=1
 # Treat warnings as errors in directories with FAIL_ON_WARNINGS.
 ac_add_options --enable-warnings-as-errors
 
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
 
 ac_add_options --with-branding=browser/branding/nightly
 
+. "$topsrcdir/build/mozconfig.common.override"
 . "$topsrcdir/build/mozconfig.cache"
-
-. "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/win32/nightly
+++ b/browser/config/mozconfigs/win32/nightly
@@ -1,14 +1,10 @@
 . "$topsrcdir/build/mozconfig.win-common"
 . "$topsrcdir/browser/config/mozconfigs/win32/common-opt"
 
 ac_add_options --enable-signmar
 ac_add_options --enable-profiling
 
-# Nightlies only since this has a cost in performance
-ac_add_options --enable-js-diagnostics
-
 ac_add_options --with-branding=browser/branding/nightly
 
+. "$topsrcdir/build/mozconfig.common.override"
 . "$topsrcdir/build/mozconfig.cache"
-
-. "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/win64/debug
+++ b/browser/config/mozconfigs/win64/debug
@@ -25,11 +25,10 @@ ac_add_options --enable-warnings-as-erro
 
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
 
 ac_add_options --with-branding=browser/branding/nightly
 
 . $topsrcdir/build/win64/mozconfig.vs2013
 
+. "$topsrcdir/build/mozconfig.common.override"
 . "$topsrcdir/build/mozconfig.cache"
-
-. "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/win64/nightly
+++ b/browser/config/mozconfigs/win64/nightly
@@ -1,15 +1,11 @@
 . "$topsrcdir/build/mozconfig.win-common"
 . "$topsrcdir/browser/config/mozconfigs/win64/common-win64"
 . "$topsrcdir/browser/config/mozconfigs/win64/common-opt"
 
 ac_add_options --enable-signmar
 ac_add_options --enable-profiling
 
-# Nightlies only since this has a cost in performance
-ac_add_options --enable-js-diagnostics
-
 ac_add_options --with-branding=browser/branding/nightly
 
+. "$topsrcdir/build/mozconfig.common.override"
 . "$topsrcdir/build/mozconfig.cache"
-
-. "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/tooltool-manifests/linux32/asan.manifest
+++ b/browser/config/tooltool-manifests/linux32/asan.manifest
@@ -1,17 +1,12 @@
 [
 {
 "clang_version": "r185949"
 }, 
 {
-"size": 47, 
-"digest": "2005a41fe97a5e00997063705f39d42b6a43b1cf7ba306cbc7b1513de34cdcd050fc6326efa2107f19ba0cc67914745dbf13154fa748010a93cf072481ef4aaa", 
-"algorithm": "sha512", 
-"filename": "setup.sh"
-}, 
-{
 "size": 72573411, 
 "digest": "491753968f34d1bd3c58280688349499a92f31a118eb6f28e86746be62615004370394b8e1b10d48dc3fba4bc6d4fbb4ce6c7dbc4fadb39447de9aa55573c58e", 
 "algorithm": "sha512", 
-"filename": "clang.tar.bz2"
+"filename": "clang.tar.bz2",
+"unpack": true
 }
 ]
--- a/browser/config/tooltool-manifests/linux32/clang.manifest
+++ b/browser/config/tooltool-manifests/linux32/clang.manifest
@@ -1,17 +1,12 @@
 [
 {
 "clang_version": "r183744"
 }, 
 {
-"size": 47, 
-"digest": "2005a41fe97a5e00997063705f39d42b6a43b1cf7ba306cbc7b1513de34cdcd050fc6326efa2107f19ba0cc67914745dbf13154fa748010a93cf072481ef4aaa", 
-"algorithm": "sha512", 
-"filename": "setup.sh"
-}, 
-{
 "size": 70206124, 
 "digest": "a6b8046bd9485f9387dcb1c14b8d442822f02b1caa61b653e8b6cfd96906deadfb4b29809f2cd2b71f919b321d97dd2ebec6020c15f6d485f1641c0f710a762f", 
 "algorithm": "sha512", 
-"filename": "clang.tar.bz2"
+"filename": "clang.tar.bz2",
+"unpack": true,
 }
 ]
--- a/browser/config/tooltool-manifests/linux32/releng.manifest
+++ b/browser/config/tooltool-manifests/linux32/releng.manifest
@@ -1,20 +1,16 @@
 [
 {
-"size": 82,
-"digest": "70a6126249e40aa1da32248bf6bfe45e0d8c87334579ec0cf69403e61b635e27c766d9bf08d530978286552f158ee24c74b0168a57cc6b734dcfed4fc5e09cff",
-"algorithm": "sha512", 
-"filename": "setup.sh"
-}, 
-{
 "size": 80458572,
 "digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad",
 "algorithm": "sha512", 
-"filename": "gcc.tar.xz"
+"filename": "gcc.tar.xz",
+"unpack": true
 },
 {
 "size": 167175,
 "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
 "algorithm": "sha512",
-"filename": "sccache.tar.bz2"
+"filename": "sccache.tar.bz2",
+"unpack": true
 }
 ]
--- a/browser/config/tooltool-manifests/linux64/asan.manifest
+++ b/browser/config/tooltool-manifests/linux64/asan.manifest
@@ -1,17 +1,12 @@
 [
 {
 "clang_version": "r200213"
 }, 
 {
-"size": 47, 
-"digest": "2005a41fe97a5e00997063705f39d42b6a43b1cf7ba306cbc7b1513de34cdcd050fc6326efa2107f19ba0cc67914745dbf13154fa748010a93cf072481ef4aaa", 
-"algorithm": "sha512", 
-"filename": "setup.sh"
-}, 
-{
 "size": 71282740, 
 "digest": "ee9edb1ef3afd9ab29e39565145545ad57e8d8d2538be4d822d7dbd64038f4529b0b287cecf48bf83def52a26ac2c6faa331686c3ad5e8b4ba4c22686ee0808f", 
 "algorithm": "sha512", 
-"filename": "clang.tar.bz2"
+"filename": "clang.tar.bz2",
+"unpack": true
 }
 ]
--- a/browser/config/tooltool-manifests/linux64/clang.manifest
+++ b/browser/config/tooltool-manifests/linux64/clang.manifest
@@ -1,17 +1,12 @@
 [
 {
 "clang_version": "r183744"
 }, 
 {
-"size": 47, 
-"digest": "2005a41fe97a5e00997063705f39d42b6a43b1cf7ba306cbc7b1513de34cdcd050fc6326efa2107f19ba0cc67914745dbf13154fa748010a93cf072481ef4aaa", 
-"algorithm": "sha512", 
-"filename": "setup.sh"
-}, 
-{
 "size": 70350828, 
 "digest": "6cd04e8ec44c6fef159349c22bd0476891e4a2d46479f9586283eaf3305e42f79c720d40dfec0e78d8899c1651189b12e285de60862ffd0612b0dac7a0c336c6", 
 "algorithm": "sha512", 
-"filename": "clang.tar.bz2"
+"filename": "clang.tar.bz2",
+"unpack": true
 }
 ]
--- a/browser/config/tooltool-manifests/linux64/releng.manifest
+++ b/browser/config/tooltool-manifests/linux64/releng.manifest
@@ -1,20 +1,16 @@
 [
 {
-"size": 82,
-"digest": "70a6126249e40aa1da32248bf6bfe45e0d8c87334579ec0cf69403e61b635e27c766d9bf08d530978286552f158ee24c74b0168a57cc6b734dcfed4fc5e09cff",
-"algorithm": "sha512", 
-"filename": "setup.sh"
-}, 
-{
 "size": 80458572,
 "digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad",
 "algorithm": "sha512", 
-"filename": "gcc.tar.xz"
+"filename": "gcc.tar.xz",
+"unpack": true
 },
 {
 "size": 167175,
 "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
 "algorithm": "sha512",
-"filename": "sccache.tar.bz2"
+"filename": "sccache.tar.bz2",
+"unpack": true
 }
 ]
--- a/browser/config/tooltool-manifests/macosx64/asan.manifest
+++ b/browser/config/tooltool-manifests/macosx64/asan.manifest
@@ -1,17 +1,12 @@
 [
 {
 "clang_version": "r200213"
 }, 
 {
-"size": 47, 
-"digest": "2005a41fe97a5e00997063705f39d42b6a43b1cf7ba306cbc7b1513de34cdcd050fc6326efa2107f19ba0cc67914745dbf13154fa748010a93cf072481ef4aaa", 
-"algorithm": "sha512", 
-"filename": "setup.sh"
-}, 
-{
 "size": 58997296, 
 "digest": "9757d142142442c881b8d1eb31c2fe80e1979a858e6133473b5574a5a3b9cdaf9abed32b2e246b715c9f0eb0969103337918215fc491feae196219e8fb03f0b1", 
 "algorithm": "sha512", 
-"filename": "clang.tar.bz2"
+"filename": "clang.tar.bz2",
+"unpack": true
 }
 ]
--- a/browser/config/tooltool-manifests/macosx64/clang.manifest
+++ b/browser/config/tooltool-manifests/macosx64/clang.manifest
@@ -1,23 +1,19 @@
 [
 {
 "clang_version": "r183744"
 }, 
 {
-"size": 88,
-"digest": "0d2ae9bcd7cea34ec0b768270725e98410dbb3bc150c7381e0dcf3eb5dbb3e69ac76dbb0f46b056151d6a6fa8681cab06da68173ae8598f3397b8f7628e67381",
-"algorithm": "sha512",
-"filename": "setup.sh"
-}, 
-{
 "size": 59602619, 
 "digest": "86662ebc0ef650490559005948c4f0cb015dad72c7cac43732c2bf2995247081e30c139cf8008d19670a0009fc302c4eee2676981ee3f9ff4a15c01af22b783b", 
 "algorithm": "sha512", 
-"filename": "clang.tar.bz2"
+"filename": "clang.tar.bz2",
+"unpack": true
 },
 {
 "size": 167175,
 "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
 "algorithm": "sha512",
-"filename": "sccache.tar.bz2"
+"filename": "sccache.tar.bz2",
+"unpack": true
 }
 ]
--- a/browser/config/tooltool-manifests/macosx64/releng.manifest
+++ b/browser/config/tooltool-manifests/macosx64/releng.manifest
@@ -1,23 +1,19 @@
 [
 {
 "clang_version": "r183744"
 }, 
 {
-"size": 88,
-"digest": "0d2ae9bcd7cea34ec0b768270725e98410dbb3bc150c7381e0dcf3eb5dbb3e69ac76dbb0f46b056151d6a6fa8681cab06da68173ae8598f3397b8f7628e67381",
-"algorithm": "sha512",
-"filename": "setup.sh"
-}, 
-{
 "size": 59602619, 
 "digest": "86662ebc0ef650490559005948c4f0cb015dad72c7cac43732c2bf2995247081e30c139cf8008d19670a0009fc302c4eee2676981ee3f9ff4a15c01af22b783b", 
 "algorithm": "sha512", 
-"filename": "clang.tar.bz2"
+"filename": "clang.tar.bz2",
+"unpack": true
 },
 {
 "size": 167175,
 "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
 "algorithm": "sha512",
-"filename": "sccache.tar.bz2"
+"filename": "sccache.tar.bz2",
+"unpack": true
 }
 ]
--- a/browser/config/tooltool-manifests/win32/releng.manifest
+++ b/browser/config/tooltool-manifests/win32/releng.manifest
@@ -1,20 +1,15 @@
 [
 {
 "size": 266240,
 "digest": "bb345b0e700ffab4d09436981f14b5de84da55a3f18a7f09ebc4364a4488acdeab8d46f447b12ac70f2da1444a68b8ce8b8675f0dae2ccf845e966d1df0f0869",
 "algorithm": "sha512",
 "filename": "mozmake.exe"
 },
 {
-"size": 176,
-"digest": "2809058907ac5eefdc394113d2e4fe76ba559ac61c2eca2f88e7a12a74bdf44a15d9039fa8aa229f7362a14b67d67395063f68147ae098beac5dfcc78aff98da",
-"algorithm": "sha512",
-"filename": "setup.sh"
-},
-{
 "size": 167175,
 "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
 "algorithm": "sha512",
-"filename": "sccache.tar.bz2"
+"filename": "sccache.tar.bz2",
+"unpack": true
 }
 ]
--- a/browser/config/tooltool-manifests/win64/releng.manifest
+++ b/browser/config/tooltool-manifests/win64/releng.manifest
@@ -1,20 +1,15 @@
 [
 {
 "size": 266240,
 "digest": "bb345b0e700ffab4d09436981f14b5de84da55a3f18a7f09ebc4364a4488acdeab8d46f447b12ac70f2da1444a68b8ce8b8675f0dae2ccf845e966d1df0f0869",
 "algorithm": "sha512",
 "filename": "mozmake.exe"
 },
 {
-"size": 176,
-"digest": "2809058907ac5eefdc394113d2e4fe76ba559ac61c2eca2f88e7a12a74bdf44a15d9039fa8aa229f7362a14b67d67395063f68147ae098beac5dfcc78aff98da",
-"algorithm": "sha512",
-"filename": "setup.sh"
-},
-{
 "size": 167175,
 "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
 "algorithm": "sha512",
-"filename": "sccache.tar.bz2"
+"filename": "sccache.tar.bz2",
+"unpack": true
 }
 ]
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -431,17 +431,18 @@
 @RESPATH@/components/nsUpdateService.js
 @RESPATH@/components/nsUpdateServiceStub.js
 #endif
 @RESPATH@/components/nsUpdateTimerManager.manifest
 @RESPATH@/components/nsUpdateTimerManager.js
 @RESPATH@/components/addoncompat.manifest
 @RESPATH@/components/multiprocessShims.js
 @RESPATH@/components/defaultShims.js
-@RESPATH@/components/remoteTagService.js
+@RESPATH@/components/utils.manifest
+@RESPATH@/components/simpleServices.js
 @RESPATH@/components/pluginGlue.manifest
 @RESPATH@/components/ProcessSingleton.manifest
 @RESPATH@/components/MainProcessSingleton.js
 @RESPATH@/components/ContentProcessSingleton.js
 @RESPATH@/browser/components/nsSessionStore.manifest
 @RESPATH@/browser/components/nsSessionStartup.js
 @RESPATH@/browser/components/nsSessionStore.js
 @RESPATH@/components/nsURLFormatter.manifest
@@ -883,16 +884,18 @@ bin/libfreebl_32int64_3.so
 @RESPATH@/webapprt/modules/RemoteDebugger.jsm
 @RESPATH@/webapprt/modules/WebRTCHandler.jsm
 #endif
 
 @RESPATH@/components/DataStore.manifest
 @RESPATH@/components/DataStoreImpl.js
 @RESPATH@/components/dom_datastore.xpt
 
+@RESPATH@/components/dom_audiochannel.xpt
+
 ; Shutdown Terminator
 @RESPATH@/components/nsTerminatorTelemetry.js
 @RESPATH@/components/terminator.manifest
 
 #if defined(CLANG_CXX)
 #if defined(MOZ_ASAN) || defined(MOZ_TSAN)
 @BINPATH@/llvm-symbolizer
 #endif
--- a/build/annotationProcessors/AnnotationProcessor.java
+++ b/build/annotationProcessors/AnnotationProcessor.java
@@ -10,24 +10,25 @@ import org.mozilla.gecko.annotationProce
 import org.mozilla.gecko.annotationProcessors.utils.GeneratableElementIterator;
 
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.Iterator;
 
 public class AnnotationProcessor {
-    public static final String OUTFILE = "GeneratedJNIWrappers.cpp";
-    public static final String HEADERFILE = "GeneratedJNIWrappers.h";
+    public static final String SOURCE_FILE = "GeneratedJNIWrappers.cpp";
+    public static final String HEADER_FILE = "GeneratedJNIWrappers.h";
+    public static final String NATIVES_FILE = "GeneratedJNINatives.h";
 
     public static final String GENERATED_COMMENT =
             "// GENERATED CODE\n" +
             "// Generated by the Java program at /build/annotationProcessors at compile time\n" +
             "// from annotations on Java methods. To update, change the annotations on the\n" +
-            "// corresponding Javamethods and rerun the build. Manually updating this file\n" +
+            "// corresponding Java methods and rerun the build. Manually updating this file\n" +
             "// will cause your build to fail.\n" +
             "\n";
 
     public static void main(String[] args) {
         // We expect a list of jars on the commandline. If missing, whinge about it.
         if (args.length <= 1) {
             System.err.println("Usage: java AnnotationProcessor jarfiles ...");
             System.exit(1);
@@ -42,34 +43,46 @@ public class AnnotationProcessor {
         // Start the clock!
         long s = System.currentTimeMillis();
 
         // Get an iterator over the classes in the jar files given...
         Iterator<ClassWithOptions> jarClassIterator = IterableJarLoadingURLClassLoader.getIteratorOverJars(args);
 
         StringBuilder headerFile = new StringBuilder(GENERATED_COMMENT);
         headerFile.append(
-                "#ifndef GeneratedJNIWrappers_h__\n" +
-                "#define GeneratedJNIWrappers_h__\n" +
+                "#ifndef " + getHeaderGuardName(HEADER_FILE) + "\n" +
+                "#define " + getHeaderGuardName(HEADER_FILE) + "\n" +
                 "\n" +
                 "#include \"mozilla/jni/Refs.h\"\n" +
                 "\n" +
                 "namespace mozilla {\n" +
                 "namespace widget {\n" +
                 "\n");
 
         StringBuilder implementationFile = new StringBuilder(GENERATED_COMMENT);
         implementationFile.append(
                 "#include \"GeneratedJNIWrappers.h\"\n" +
                 "#include \"mozilla/jni/Accessors.h\"\n" +
                 "\n" +
                 "namespace mozilla {\n" +
                 "namespace widget {\n" +
                 "\n");
 
+        StringBuilder nativesFile = new StringBuilder(GENERATED_COMMENT);
+        nativesFile.append(
+                "#ifndef " + getHeaderGuardName(NATIVES_FILE) + "\n" +
+                "#define " + getHeaderGuardName(NATIVES_FILE) + "\n" +
+                "\n" +
+                "#include \"GeneratedJNIWrappers.h\"\n" +
+                "#include \"mozilla/jni/Natives.h\"\n" +
+                "\n" +
+                "namespace mozilla {\n" +
+                "namespace widget {\n" +
+                "\n");
+
         while (jarClassIterator.hasNext()) {
             ClassWithOptions aClassTuple = jarClassIterator.next();
 
             CodeGenerator generatorInstance;
 
             // Get an iterator over the appropriately generated methods of this class
             Iterator<AnnotatableEntity> methodIterator = new GeneratableElementIterator(aClassTuple.wrappedClass);
 
@@ -80,70 +93,69 @@ public class AnnotationProcessor {
 
             // Iterate all annotated members in this class..
             while (methodIterator.hasNext()) {
                 AnnotatableEntity aElementTuple = methodIterator.next();
                 switch (aElementTuple.mEntityType) {
                     case METHOD:
                         generatorInstance.generateMethod(aElementTuple);
                         break;
+                    case NATIVE:
+                        generatorInstance.generateNative(aElementTuple);
+                        break;
                     case FIELD:
                         generatorInstance.generateField(aElementTuple);
                         break;
                     case CONSTRUCTOR:
                         generatorInstance.generateConstructor(aElementTuple);
                         break;
                 }
             }
 
             headerFile.append(generatorInstance.getHeaderFileContents());
             implementationFile.append(generatorInstance.getWrapperFileContents());
+            nativesFile.append(generatorInstance.getNativesFileContents());
         }
 
         implementationFile.append(
                 "\n" +
                 "} /* widget */\n" +
                 "} /* mozilla */\n");
 
         headerFile.append(
                 "\n" +
                 "} /* widget */\n" +
                 "} /* mozilla */\n" +
-                "#endif // GeneratedJNIWrappers_h__\n");
+                "#endif // " + getHeaderGuardName(HEADER_FILE) + "\n");
 
-        writeOutputFiles(headerFile, implementationFile);
+        nativesFile.append(
+                "\n" +
+                "} /* widget */\n" +
+                "} /* mozilla */\n" +
+                "#endif // " + getHeaderGuardName(NATIVES_FILE) + "\n");
+
+        writeOutputFile(SOURCE_FILE, implementationFile);
+        writeOutputFile(HEADER_FILE, headerFile);
+        writeOutputFile(NATIVES_FILE, nativesFile);
         long e = System.currentTimeMillis();
         System.out.println("Annotation processing complete in " + (e - s) + "ms");
     }
 
-    private static void writeOutputFiles(StringBuilder aHeaderFile, StringBuilder aImplementationFile) {
-        FileOutputStream headerStream = null;
-        try {
-            headerStream = new FileOutputStream(OUTFILE);
-            headerStream.write(aImplementationFile.toString().getBytes());
-        } catch (IOException e) {
-            System.err.println("Unable to write " + OUTFILE + ". Perhaps a permissions issue?");
-            e.printStackTrace(System.err);
-        } finally {
-            if (headerStream != null) {
-                try {
-                    headerStream.close();
-                } catch (IOException e) {
-                    System.err.println("Unable to close headerStream due to "+e);
-                    e.printStackTrace(System.err);
-                }
-            }
-        }
+    private static String getHeaderGuardName(final String name) {
+        return name.replaceAll("\\W", "_");
+    }
 
+    private static void writeOutputFile(final String name,
+                                        final StringBuilder content) {
         FileOutputStream outStream = null;
         try {
-            outStream = new FileOutputStream(HEADERFILE);
-            outStream.write(aHeaderFile.toString().getBytes());
+            outStream = new FileOutputStream(name);
+            outStream.write(content.toString().getBytes());
         } catch (IOException e) {
-            System.err.println("Unable to write " + HEADERFILE + ". Perhaps a permissions issue?");
+            System.err.println("Unable to write " + name + ". Perhaps a permissions issue?");
             e.printStackTrace(System.err);
         } finally {
             if (outStream != null) {
                 try {
                     outStream.close();
                 } catch (IOException e) {
                     System.err.println("Unable to close outStream due to "+e);
                     e.printStackTrace(System.err);
--- a/build/annotationProcessors/CodeGenerator.java
+++ b/build/annotationProcessors/CodeGenerator.java
@@ -17,45 +17,53 @@ import java.lang.reflect.Modifier;
 import java.util.HashSet;
 
 public class CodeGenerator {
     private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0];
 
     // Buffers holding the strings to ultimately be written to the output files.
     private final StringBuilder cpp = new StringBuilder();
     private final StringBuilder header = new StringBuilder();
+    private final StringBuilder natives = new StringBuilder();
+    private final StringBuilder nativesInits = new StringBuilder();
 
     private final Class<?> cls;
     private final String clsName;
 
     private final HashSet<String> takenMethodNames = new HashSet<String>();
 
     public CodeGenerator(ClassWithOptions annotatedClass) {
         this.cls = annotatedClass.wrappedClass;
         this.clsName = annotatedClass.generatedName;
 
         header.append(
-                "class " + clsName + " : public mozilla::jni::Class<" + clsName + "> {\n" +
-                "\n" +
+                "class " + clsName + " : public mozilla::jni::Class<" + clsName + ">\n" +
+                "{\n" +
                 "public:\n" +
                 "    typedef mozilla::jni::Ref<" + clsName + "> Ref;\n" +
                 "    typedef mozilla::jni::LocalRef<" + clsName + "> LocalRef;\n" +
                 "    typedef mozilla::jni::GlobalRef<" + clsName + "> GlobalRef;\n" +
-                "    typedef const typename mozilla::jni::Param<" + clsName + ">::Type& Param;\n" +
+                "    typedef const mozilla::jni::Param<" + clsName + ">& Param;\n" +
                 "\n" +
                 "    static constexpr char name[] =\n" +
                 "            \"" + cls.getName().replace('.', '/') + "\";\n" +
                 "\n" +
                 "protected:\n" +
                 "    " + clsName + "(jobject instance) : Class(instance) {}\n" +
                 "\n");
 
         cpp.append(
                 "constexpr char " + clsName + "::name[];\n" +
                 "\n");
+
+        natives.append(
+                "template<class Impl>\n" +
+                "class " + clsName + "::Natives : " +
+                        "public mozilla::jni::NativeImpl<" + clsName + ", Impl>\n" +
+                "{\n");
     }
 
     private String getTraitsName(String uniqueName, boolean includeScope) {
         return (includeScope ? clsName + "::" : "") + uniqueName + "_t";
     }
 
     private String getNativeParameterType(Class<?> type, AnnotationInfo info) {
         if (type == cls) {
@@ -67,30 +75,40 @@ public class CodeGenerator {
     private String getNativeReturnType(Class<?> type, AnnotationInfo info) {
         if (type == cls) {
             return clsName + "::LocalRef";
         }
         return Utils.getNativeReturnType(type, info);
     }
 
     private void generateMember(AnnotationInfo info, Member member,
-                                String uniqueName, Class<?> type) {
+                                String uniqueName, Class<?> type, Class<?>[] argTypes) {
+        final StringBuilder args = new StringBuilder();
+        for (Class<?> argType : argTypes) {
+            args.append("\n                " + getNativeParameterType(argType, info) + ",");
+        }
+        if (args.length() > 0) {
+            args.setLength(args.length() - 1);
+        }
+
         header.append(
                 "public:\n" +
                 "    struct " + getTraitsName(uniqueName, /* includeScope */ false) + " {\n" +
                 "        typedef " + clsName + " Owner;\n" +
                 "        typedef " + getNativeReturnType(type, info) + " ReturnType;\n" +
                 "        typedef " + getNativeParameterType(type, info) + " SetterType;\n" +
+                "        typedef mozilla::jni::Args<" + args + "> Args;\n" +
                 "        static constexpr char name[] = \"" +
                         Utils.getMemberName(member) + "\";\n" +
                 "        static constexpr char signature[] =\n" +
                 "                \"" + Utils.getSignature(member) + "\";\n" +
                 "        static const bool isStatic = " + Utils.isStatic(member) + ";\n" +
                 "        static const bool isMultithreaded = " + info.isMultithreaded + ";\n" +
-                "        static const mozilla::jni::ExceptionMode exceptionMode = " + (
+                "        static const mozilla::jni::ExceptionMode exceptionMode =\n" +
+                "                " + (
                         info.catchException ? "mozilla::jni::ExceptionMode::NSRESULT" :
                         info.noThrow ?        "mozilla::jni::ExceptionMode::IGNORE" :
                                               "mozilla::jni::ExceptionMode::ABORT") + ";\n" +
                 "    };\n" +
                 "\n");
 
         cpp.append(
                 "constexpr char " + getTraitsName(uniqueName, /* includeScope */ true) +
@@ -243,40 +261,69 @@ public class CodeGenerator {
      *
      * @param annotatedMethod The Java method, plus annotation data.
      */
     public void generateMethod(AnnotatableEntity annotatedMethod) {
         // Unpack the tuple and extract some useful fields from the Method..
         final Method method = annotatedMethod.getMethod();
         final AnnotationInfo info = annotatedMethod.mAnnotationInfo;
         final String uniqueName = getUniqueMethodName(info.wrapperName);
+        final Class<?>[] argTypes = method.getParameterTypes();
         final Class<?> returnType = method.getReturnType();
 
         if (method.isSynthetic()) {
             return;
         }
 
-        generateMember(info, method, uniqueName, returnType);
+        generateMember(info, method, uniqueName, returnType, argTypes);
 
-        final Class<?>[] argTypes = method.getParameterTypes();
         final boolean isStatic = Utils.isStatic(method);
 
         header.append(
                 "    " + generateDeclaration(info.wrapperName, argTypes,
                                              returnType, info, isStatic) + "\n" +
                 "\n");
 
         cpp.append(
                 generateDefinition(
                         "mozilla::jni::Method<" +
                                 getTraitsName(uniqueName, /* includeScope */ false) + ">::Call",
                         info.wrapperName, argTypes, returnType, info, isStatic) + "\n" +
                 "\n");
     }
 
+    /**
+     * Append the appropriate generated code to the buffers for the native method provided.
+     *
+     * @param annotatedMethod The Java native method, plus annotation data.
+     */
+    public void generateNative(AnnotatableEntity annotatedMethod) {
+        // Unpack the tuple and extract some useful fields from the Method..
+        final Method method = annotatedMethod.getMethod();
+        final AnnotationInfo info = annotatedMethod.mAnnotationInfo;
+        final String uniqueName = getUniqueMethodName(info.wrapperName);
+        final Class<?>[] argTypes = method.getParameterTypes();
+        final Class<?> returnType = method.getReturnType();
+
+        generateMember(info, method, uniqueName, returnType, argTypes);
+
+        final String traits = getTraitsName(uniqueName, /* includeScope */ true);
+
+        if (nativesInits.length() > 0) {
+            nativesInits.append(',');
+        }
+
+        nativesInits.append(
+                "\n" +
+                "\n" +
+                "        mozilla::jni::MakeNativeMethod<" + traits + ">(\n" +
+                "                mozilla::jni::NativeStub<" + traits + ", Impl>\n" +
+                "                ::template Wrap<&Impl::" + info.wrapperName + ">)");
+    }
+
     private String getLiteral(Object val, AnnotationInfo info) {
         final Class<?> type = val.getClass();
 
         if (type == char.class || type == Character.class) {
             final char c = (char) val;
             if (c >= 0x20 && c < 0x7F) {
                 return "'" + c + '\'';
             }
@@ -343,17 +390,17 @@ public class CodeGenerator {
                             "[] = " + getLiteral(val, info) + ";\n" +
                     "\n");
                 return;
             }
 
             // Fall back to using accessors if we encounter an exception.
         }
 
-        generateMember(info, field, uniqueName, type);
+        generateMember(info, field, uniqueName, type, EMPTY_CLASS_ARRAY);
 
         final Class<?>[] getterArgs = EMPTY_CLASS_ARRAY;
 
         header.append(
                 "    " + generateDeclaration(info.wrapperName, getterArgs,
                                              type, info, isStatic) + "\n" +
                 "\n");
 
@@ -384,25 +431,24 @@ public class CodeGenerator {
     }
 
     public void generateConstructor(AnnotatableEntity annotatedConstructor) {
         // Unpack the tuple and extract some useful fields from the Method..
         final Constructor<?> method = annotatedConstructor.getConstructor();
         final AnnotationInfo info = annotatedConstructor.mAnnotationInfo;
         final String wrapperName = "New";
         final String uniqueName = getUniqueMethodName(wrapperName);
+        final Class<?>[] argTypes = method.getParameterTypes();
         final Class<?> returnType = cls;
 
         if (method.isSynthetic()) {
             return;
         }
 
-        generateMember(info, method, uniqueName, returnType);
-
-        final Class<?>[] argTypes = method.getParameterTypes();
+        generateMember(info, method, uniqueName, returnType, argTypes);
 
         header.append(
                 "    " + generateDeclaration(wrapperName, argTypes,
                                              returnType, info, /* isStatic */ true) + "\n" +
                 "\n");
 
         cpp.append(
                 generateDefinition(
@@ -449,14 +495,39 @@ public class CodeGenerator {
     }
 
     /**
      * Get the finalised bytes to go into the generated header file.
      *
      * @return The bytes to be written to the header file.
      */
     public String getHeaderFileContents() {
+        if (nativesInits.length() > 0) {
+            header.append(
+                    "public:\n" +
+                    "    template<class Impl> class Natives;\n");
+        }
         header.append(
                 "};\n" +
                 "\n");
         return header.toString();
     }
+
+    /**
+     * Get the finalised bytes to go into the generated natives header file.
+     *
+     * @return The bytes to be written to the header file.
+     */
+    public String getNativesFileContents() {
+        if (nativesInits.length() == 0) {
+            return "";
+        }
+        natives.append(
+                "public:\n" +
+                "    static constexpr JNINativeMethod methods[] = {" + nativesInits + '\n' +
+                "    };\n" +
+                "};\n" +
+                "\n" +
+                "template<class Impl>\n" +
+                "constexpr JNINativeMethod " + clsName + "::Natives<Impl>::methods[];\n");
+        return natives.toString();
+    }
 }
--- a/build/annotationProcessors/classloader/AnnotatableEntity.java
+++ b/build/annotationProcessors/classloader/AnnotatableEntity.java
@@ -5,44 +5,49 @@
 package org.mozilla.gecko.annotationProcessors.classloader;
 
 import org.mozilla.gecko.annotationProcessors.AnnotationInfo;
 
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.lang.reflect.Member;
 import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
 
 /**
  * Union type to hold either a method, field, or ctor. Allows us to iterate "The generatable stuff", despite
  * the fact that such things can be of either flavour.
  */
 public class AnnotatableEntity {
-    public enum ENTITY_TYPE {METHOD, FIELD, CONSTRUCTOR}
+    public enum ENTITY_TYPE {METHOD, NATIVE, FIELD, CONSTRUCTOR}
 
     private final Member mMember;
     public final ENTITY_TYPE mEntityType;
 
     public final AnnotationInfo mAnnotationInfo;
 
     public AnnotatableEntity(Member aObject, AnnotationInfo aAnnotationInfo) {
         mMember = aObject;
         mAnnotationInfo = aAnnotationInfo;
 
         if (aObject instanceof Method) {
-            mEntityType = ENTITY_TYPE.METHOD;
+            if (Modifier.isNative(aObject.getModifiers())) {
+                mEntityType = ENTITY_TYPE.NATIVE;
+            } else {
+                mEntityType = ENTITY_TYPE.METHOD;
+            }
         } else if (aObject instanceof Field) {
             mEntityType = ENTITY_TYPE.FIELD;
         } else {
             mEntityType = ENTITY_TYPE.CONSTRUCTOR;
         }
     }
 
     public Method getMethod() {
-        if (mEntityType != ENTITY_TYPE.METHOD) {
+        if (mEntityType != ENTITY_TYPE.METHOD && mEntityType != ENTITY_TYPE.NATIVE) {
             throw new UnsupportedOperationException("Attempt to cast to unsupported member type.");
         }
         return (Method) mMember;
     }
     public Field getField() {
         if (mEntityType != ENTITY_TYPE.FIELD) {
             throw new UnsupportedOperationException("Attempt to cast to unsupported member type.");
         }
--- a/build/macosx/mozconfig.common
+++ b/build/macosx/mozconfig.common
@@ -29,11 +29,9 @@ if [ -z "$CC" ]; then
     export CC=clang
 fi
 
 # If not set use the system default clang++
 if [ -z "$CXX" ]; then
     export CXX=clang++
 fi
 
-. "$topsrcdir/build/mozconfig.cache"
-
 export SOCORRO_SYMBOL_UPLOAD_TOKEN_FILE=/builds/crash-stats-api.token
--- a/build/mozconfig.cache
+++ b/build/mozconfig.cache
@@ -46,16 +46,20 @@ fi
 
 if test -z "$bucket"; then
     case "$platform" in
     win*) : ;;
     *)
         ac_add_options --with-ccache
     esac
 else
+    if ! test -e $topsrcdir/sccache/sccache.py; then
+        echo "Sccache missing in the tooltool manifest" >&2
+        exit 1
+    fi
     mk_add_options "export SCCACHE_BUCKET=$bucket"
     case "$master" in
     *use1.mozilla.com*|*usw2.mozilla.com*)
         mk_add_options "export SCCACHE_NAMESERVER=169.254.169.253"
         ;;
     esac
     ac_add_options "--with-compiler-wrapper=python2.7 $topsrcdir/sccache/sccache.py"
     mk_add_options MOZ_PREFLIGHT_ALL+=build/sccache.mk
--- a/caps/BasePrincipal.cpp
+++ b/caps/BasePrincipal.cpp
@@ -1,25 +1,27 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 sw=2 et tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/BasePrincipal.h"
 
+#include "nsIAddonPolicyService.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsIObjectInputStream.h"
 #include "nsIObjectOutputStream.h"
 
 #include "nsPrincipal.h"
 #include "nsNetUtil.h"
 #include "nsIURIWithPrincipal.h"
 #include "nsNullPrincipal.h"
 #include "nsScriptSecurityManager.h"
+#include "nsServiceManagerUtils.h"
 
 #include "mozilla/dom/CSPDictionariesBinding.h"
 #include "mozilla/dom/ToJSValue.h"
 #include "mozilla/dom/URLSearchParams.h"
 
 namespace mozilla {
 
 using dom::URLParams;
@@ -36,16 +38,20 @@ OriginAttributes::CreateSuffix(nsACStrin
     value.AppendInt(mAppId);
     params->Set(NS_LITERAL_STRING("appId"), value);
   }
 
   if (mInBrowser) {
     params->Set(NS_LITERAL_STRING("inBrowser"), NS_LITERAL_STRING("1"));
   }
 
+  if (!mAddonId.IsEmpty()) {
+    params->Set(NS_LITERAL_STRING("addonId"), mAddonId);
+  }
+
   aStr.Truncate();
 
   params->Serialize(value);
   if (!value.IsEmpty()) {
     aStr.AppendLiteral("!");
     aStr.Append(NS_ConvertUTF16toUTF8(value));
   }
 }
@@ -83,16 +89,22 @@ public:
       if (!aValue.EqualsLiteral("1")) {
         return false;
       }
 
       mOriginAttributes->mInBrowser = true;
       return true;
     }
 
+    if (aName.EqualsLiteral("addonId")) {
+      MOZ_RELEASE_ASSERT(mOriginAttributes->mAddonId.IsEmpty());
+      mOriginAttributes->mAddonId.Assign(aValue);
+      return true;
+    }
+
     // No other attributes are supported.
     return false;
   }
 
 private:
   OriginAttributes* mOriginAttributes;
 };
 
@@ -341,9 +353,24 @@ BasePrincipal::CreateCodebasePrincipal(n
 
   // Mint a codebase principal.
   nsRefPtr<nsPrincipal> codebase = new nsPrincipal();
   rv = codebase->Init(aURI, aAttrs);
   NS_ENSURE_SUCCESS(rv, nullptr);
   return codebase.forget();
 }
 
+bool
+BasePrincipal::AddonAllowsLoad(nsIURI* aURI)
+{
+  if (mOriginAttributes.mAddonId.IsEmpty()) {
+    return false;
+  }
+
+  nsCOMPtr<nsIAddonPolicyService> aps = do_GetService("@mozilla.org/addons/policy-service;1");
+  NS_ENSURE_TRUE(aps, false);
+
+  bool allowed = false;
+  nsresult rv = aps->AddonMayLoadURI(mOriginAttributes.mAddonId, aURI, &allowed);
+  return NS_SUCCEEDED(rv) && allowed;
+}
+
 } // namespace mozilla
--- a/caps/BasePrincipal.h
+++ b/caps/BasePrincipal.h
@@ -29,17 +29,18 @@ public:
     mInBrowser = aInBrowser;
   }
   explicit OriginAttributes(const OriginAttributesDictionary& aOther)
     : OriginAttributesDictionary(aOther) {}
 
   bool operator==(const OriginAttributes& aOther) const
   {
     return mAppId == aOther.mAppId &&
-           mInBrowser == aOther.mInBrowser;
+           mInBrowser == aOther.mInBrowser &&
+           mAddonId == aOther.mAddonId;
   }
   bool operator!=(const OriginAttributes& aOther) const
   {
     return !(*this == aOther);
   }
 
   // Serializes/Deserializes non-default values into the suffix format, i.e.
   // |!key1=value1&key2=value2|. If there are no non-default attributes, this
@@ -101,15 +102,19 @@ public:
   bool IsInBrowserElement() const { return mOriginAttributes.mInBrowser; }
 
 protected:
   virtual ~BasePrincipal();
 
   virtual nsresult GetOriginInternal(nsACString& aOrigin) = 0;
   virtual bool SubsumesInternal(nsIPrincipal* aOther, DocumentDomainConsideration aConsider) = 0;
 
+  // Helper to check whether this principal is associated with an addon that
+  // allows unprivileged code to load aURI.
+  bool AddonAllowsLoad(nsIURI* aURI);
+
   nsCOMPtr<nsIContentSecurityPolicy> mCSP;
   OriginAttributes mOriginAttributes;
 };
 
 } // namespace mozilla
 
 #endif /* mozilla_BasePrincipal_h */
--- a/caps/moz.build
+++ b/caps/moz.build
@@ -4,16 +4,17 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 MOCHITEST_MANIFESTS += ['tests/mochitest/mochitest.ini']
 MOCHITEST_CHROME_MANIFESTS += ['tests/mochitest/chrome.ini']
 XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
 
 XPIDL_SOURCES += [
+    'nsIAddonPolicyService.idl',
     'nsIDomainPolicy.idl',
     'nsIPrincipal.idl',
     'nsIScriptSecurityManager.idl',
 ]
 
 XPIDL_MODULE = 'caps'
 
 EXPORTS += [
new file mode 100644
--- /dev/null
+++ b/caps/nsIAddonPolicyService.idl
@@ -0,0 +1,22 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+#include "nsIURI.idl"
+
+/**
+ * This interface allows the security manager to query custom per-addon security
+ * policy.
+ */
+[scriptable,uuid(fedf126c-988e-42df-82c9-f2ac99cd65f3)]
+interface nsIAddonPolicyService : nsISupports
+{
+  /**
+   * Returns true if unprivileged code associated with the given addon may load
+   * data from |aURI|.
+   */
+  boolean addonMayLoadURI(in AString aAddonId, in nsIURI aURI);
+};
--- a/caps/nsNullPrincipal.h
+++ b/caps/nsNullPrincipal.h
@@ -18,18 +18,18 @@
 #include "nsCOMPtr.h"
 #include "nsIContentSecurityPolicy.h"
 
 #include "mozilla/BasePrincipal.h"
 
 class nsIURI;
 
 #define NS_NULLPRINCIPAL_CID \
-{ 0xe502ffb8, 0x5d95, 0x48e8, \
-  { 0x82, 0x3c, 0x0d, 0x29, 0xd8, 0x3a, 0x59, 0x33 } }
+{ 0x34a19ab6, 0xca47, 0x4098, \
+  { 0xa7, 0xb8, 0x4a, 0xfc, 0xdd, 0xcd, 0x8f, 0x88 } }
 #define NS_NULLPRINCIPAL_CONTRACTID "@mozilla.org/nullprincipal;1"
 
 #define NS_NULLPRINCIPAL_SCHEME "moz-nullprincipal"
 
 class nsNullPrincipal final : public mozilla::BasePrincipal
 {
 public:
   // This should only be used by deserialization, and the factory constructor.
--- a/caps/nsPrincipal.cpp
+++ b/caps/nsPrincipal.cpp
@@ -231,16 +231,22 @@ nsPrincipal::CheckMayLoad(nsIURI* aURI, 
   nsCOMPtr<nsIPrincipal> uriPrin;
   if (uriWithPrin) {
     uriWithPrin->GetPrincipal(getter_AddRefs(uriPrin));
   }
   if (uriPrin && nsIPrincipal::Subsumes(uriPrin)) {
       return NS_OK;
   }
 
+  // If this principal is associated with an addon, check whether that addon
+  // has been given permission to load from this domain.
+  if (AddonAllowsLoad(aURI)) {
+    return NS_OK;
+  }
+
   if (nsScriptSecurityManager::SecurityCompareURIs(mCodebase, aURI)) {
     return NS_OK;
   }
 
   // If strict file origin policy is in effect, local files will always fail
   // SecurityCompareURIs unless they are identical. Explicitly check file origin
   // policy, in that case.
   if (nsScriptSecurityManager::GetStrictFileOriginPolicy() &&
--- a/caps/nsPrincipal.h
+++ b/caps/nsPrincipal.h
@@ -107,17 +107,17 @@ protected:
   bool SubsumesInternal(nsIPrincipal* aOther, DocumentDomainConsideration aConsideration) override;
 
 private:
   nsTArray< nsCOMPtr<nsIPrincipal> > mPrincipals;
 };
 
 #define NS_PRINCIPAL_CONTRACTID "@mozilla.org/principal;1"
 #define NS_PRINCIPAL_CID \
-  { 0xb7c8505e, 0xc56d, 0x4191, \
-    { 0xa1, 0x5e, 0x5d, 0xcb, 0x88, 0x9b, 0xa0, 0x94 }}
+{ 0xb02c3023, 0x5b37, 0x472a, \
+  { 0xa2, 0xcd, 0x35, 0xaa, 0x5e, 0xe2, 0xa8, 0x19 } }
 
 #define NS_EXPANDEDPRINCIPAL_CONTRACTID "@mozilla.org/expandedprincipal;1"
 #define NS_EXPANDEDPRINCIPAL_CID \
-  { 0x38539471, 0x68cc, 0x4a6f, \
-    { 0x81, 0x20, 0xdb, 0xd5, 0x4a, 0x22, 0x0a, 0x13 }}
+{ 0xe8ee88b0, 0x5571, 0x4086, \
+  { 0xa4, 0x5b, 0x39, 0xa7, 0x16, 0x90, 0x6b, 0xdb } }
 
 #endif // nsPrincipal_h__
--- a/caps/tests/mochitest/chrome.ini
+++ b/caps/tests/mochitest/chrome.ini
@@ -1,10 +1,11 @@
 [DEFAULT]
-skip-if = buildapp == 'b2g'
+skip-if = buildapp == 'b2g' || os == 'android'
 support-files =
   file_disableScript.html
 
 [test_bug995943.xul]
+[test_addonMayLoad.html]
 [test_disableScript.xul]
 [test_principal_jarprefix_origin_appid_appstatus.html]
 # jarPrefix test doesn't work on Windows, see bug 776296.
 skip-if = os == "win"
new file mode 100644
--- /dev/null
+++ b/caps/tests/mochitest/file_data.txt
@@ -0,0 +1,1 @@
+server data fetched over XHR
--- a/caps/tests/mochitest/mochitest.ini
+++ b/caps/tests/mochitest/mochitest.ini
@@ -1,10 +1,11 @@
 [DEFAULT]
 support-files =
+  file_data.txt
   file_disableScript.html
 
 [test_app_principal_equality.html]
 skip-if = e10s
 [test_bug246699.html]
 [test_bug292789.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_bug423375.html]
new file mode 100644
--- /dev/null
+++ b/caps/tests/mochitest/test_addonMayLoad.html
@@ -0,0 +1,97 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1180921
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1180921</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://global/skin"/>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+  <script type="application/javascript;version=1.8">
+
+  /** Test for Bug 1180921 **/
+  const Cc = Components.classes;
+  const Ci = Components.interfaces;
+  const Cu = Components.utils;
+  Cu.import("resource://gre/modules/Services.jsm");
+  let ssm = Services.scriptSecurityManager;
+  let aps = Cc["@mozilla.org/addons/policy-service;1"].getService(Ci.nsIAddonPolicyService).wrappedJSObject;
+
+  SimpleTest.waitForExplicitFinish();
+  SimpleTest.registerCleanupFunction(function() {
+    aps.setAddonLoadURICallback('addonA', null);
+    aps.setAddonLoadURICallback('addonB', null);
+  });
+
+  function tryLoad(sb, uri) {
+    let p = new Promise(function(resolve, reject) {
+      Cu.exportFunction(resolve, sb, { defineAs: "finish" });
+      Cu.exportFunction(reject, sb, { defineAs: "error" });
+      sb.eval("try { (function () { " +
+              "  var xhr = new XMLHttpRequest();" +
+              "  xhr.onreadystatechange = function() { if (xhr.readyState == XMLHttpRequest.DONE) { finish(xhr.status == 200); } };" +
+              "  xhr.open('GET', '" + uri + "', true);" +
+              "  xhr.send();" +
+              "})() } catch (e) { error(e); }");
+    });
+    return p;
+  }
+
+  let exampleCom_addonA = new Cu.Sandbox(ssm.createCodebasePrincipal(Services.io.newURI('http://example.com', null, null), {addonId: 'addonA'}),
+                                         {wantGlobalProperties: ['XMLHttpRequest']});
+  let nullPrin_addonA = new Cu.Sandbox(ssm.createNullPrincipal({addonId: 'addonA'}),
+                                       {wantGlobalProperties: ['XMLHttpRequest']});
+  let exampleCom_addonB = new Cu.Sandbox(ssm.createCodebasePrincipal(Services.io.newURI('http://example.com', null, null), {addonId: 'addonB'}),
+                                         {wantGlobalProperties: ['XMLHttpRequest']});
+
+  function uriForDomain(d) { return d + '/tests/caps/tests/mochitest/file_data.txt' }
+
+  tryLoad(exampleCom_addonA, uriForDomain('http://example.com'))
+  .then(function(success) {
+    ok(success, "same-origin load should succeed for addon A");
+    return tryLoad(nullPrin_addonA, uriForDomain('http://example.com'));
+  }).then(function(success) {
+    ok(!success, "null-principal load should fail for addon A");
+    return tryLoad(exampleCom_addonB, uriForDomain('http://example.com'));
+  }).then(function(success) {
+    ok(success, "same-origin load should succeed for addon B");
+    return tryLoad(exampleCom_addonA, uriForDomain('http://test1.example.org'));
+  }).then(function(success) {
+    ok(!success, "cross-origin load should fail for addon A");
+    aps.setAddonLoadURICallback('addonA', function(uri) { return /test1/.test(uri.host); });
+    aps.setAddonLoadURICallback('addonB', function(uri) { return /test2/.test(uri.host); });
+    return tryLoad(exampleCom_addonA, uriForDomain('http://test1.example.org'));
+  }).then(function(success) {
+    ok(success, "whitelisted cross-origin load of test1 should succeed for addon A");
+    return tryLoad(nullPrin_addonA, uriForDomain('http://test1.example.org'));
+  }).then(function(success) {
+    ok(!success, "whitelisted null principal load of test1 should still fail for addon A");
+    return tryLoad(exampleCom_addonB, uriForDomain('http://test1.example.org'));
+  }).then(function(success) {
+    ok(!success, "non-whitelisted cross-origin load of test1 should fail for addon B");
+    return tryLoad(exampleCom_addonB, uriForDomain('http://test2.example.org'));
+  }).then(function(success) {
+    ok(success, "whitelisted cross-origin load of test2 should succeed for addon B");
+    return tryLoad(exampleCom_addonA, uriForDomain('http://test2.example.org'));
+  }).then(function(success) {
+    ok(!success, "non-whitelisted cross-origin load of test2 should fail for addon A");
+    SimpleTest.finish();
+  }, function(e) {
+    ok(false, "Rejected promise chain: " + e);
+    SimpleTest.finish();
+  });
+
+  </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1180921">Mozilla Bug 1180921</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
--- a/caps/tests/unit/test_origin.js
+++ b/caps/tests/unit/test_origin.js
@@ -20,19 +20,20 @@ function checkCrossOrigin(a, b) {
   do_check_false(a.subsumesConsideringDomain(b));
   do_check_false(b.subsumes(a));
   do_check_false(b.subsumesConsideringDomain(a));
   do_check_eq(a.cookieJar === b.cookieJar,
               a.originAttributes.appId == b.originAttributes.appId &&
               a.originAttributes.inBrowser == b.originAttributes.inBrowser);
 }
 
-function checkOriginAttributes(prin, appId, inBrowser, suffix) {
-  do_check_eq(prin.originAttributes.appId, appId || 0);
-  do_check_eq(prin.originAttributes.inBrowser, inBrowser || false);
+function checkOriginAttributes(prin, attrs, suffix) {
+  attrs = attrs || {};
+  do_check_eq(prin.originAttributes.appId, attrs.appId || 0);
+  do_check_eq(prin.originAttributes.inBrowser, attrs.inBrowser || false);
   do_check_eq(prin.originSuffix, suffix || '');
   if (!prin.isNullPrincipal && !prin.origin.startsWith('[')) {
     do_check_true(BrowserUtils.principalFromOrigin(prin.origin).equals(prin));
   } else {
     checkThrows(() => BrowserUtils.principalFromOrigin(prin.origin));
   }
 }
 
@@ -62,40 +63,46 @@ function run_test() {
 
   //
   // Test origin attributes.
   //
 
   // Just app.
   var exampleOrg_app = ssm.createCodebasePrincipal(makeURI('http://example.org'), {appId: 42});
   var nullPrin_app = ssm.createNullPrincipal({appId: 42});
-  checkOriginAttributes(exampleOrg_app, 42, false, '!appId=42');
-  checkOriginAttributes(nullPrin_app, 42, false, '!appId=42');
+  checkOriginAttributes(exampleOrg_app, {appId: 42}, '!appId=42');
+  checkOriginAttributes(nullPrin_app, {appId: 42}, '!appId=42');
   do_check_eq(exampleOrg_app.origin, 'http://example.org!appId=42');
 
   // Just browser.
   var exampleOrg_browser = ssm.createCodebasePrincipal(makeURI('http://example.org'), {inBrowser: true});
   var nullPrin_browser = ssm.createNullPrincipal({inBrowser: true});
-  checkOriginAttributes(exampleOrg_browser, 0, true, '!inBrowser=1');
-  checkOriginAttributes(nullPrin_browser, 0, true, '!inBrowser=1');
+  checkOriginAttributes(exampleOrg_browser, {inBrowser: true}, '!inBrowser=1');
+  checkOriginAttributes(nullPrin_browser, {inBrowser: true}, '!inBrowser=1');
   do_check_eq(exampleOrg_browser.origin, 'http://example.org!inBrowser=1');
 
   // App and browser.
   var exampleOrg_appBrowser = ssm.createCodebasePrincipal(makeURI('http://example.org'), {inBrowser: true, appId: 42});
   var nullPrin_appBrowser = ssm.createNullPrincipal({inBrowser: true, appId: 42});
-  checkOriginAttributes(exampleOrg_appBrowser, 42, true, '!appId=42&inBrowser=1');
-  checkOriginAttributes(nullPrin_appBrowser, 42, true, '!appId=42&inBrowser=1');
+  checkOriginAttributes(exampleOrg_appBrowser, {appId: 42, inBrowser: true}, '!appId=42&inBrowser=1');
+  checkOriginAttributes(nullPrin_appBrowser, {appId: 42, inBrowser: true}, '!appId=42&inBrowser=1');
   do_check_eq(exampleOrg_appBrowser.origin, 'http://example.org!appId=42&inBrowser=1');
 
   // App and browser, different domain.
   var exampleCom_appBrowser = ssm.createCodebasePrincipal(makeURI('https://www.example.com:123'), {appId: 42, inBrowser: true});
-  checkOriginAttributes(exampleCom_appBrowser, 42, true, '!appId=42&inBrowser=1');
+  checkOriginAttributes(exampleCom_appBrowser, {appId: 42, inBrowser: true}, '!appId=42&inBrowser=1');
   do_check_eq(exampleCom_appBrowser.origin, 'https://www.example.com:123!appId=42&inBrowser=1');
 
+  // Addon.
+  var exampleOrg_addon = ssm.createCodebasePrincipal(makeURI('http://example.org'), {addonId: 'dummy'});
+  checkOriginAttributes(exampleOrg_addon, { addonId: "dummy" }, '!addonId=dummy');
+  do_check_eq(exampleOrg_addon.origin, 'http://example.org!addonId=dummy');
+
   // Check that all of the above are cross-origin.
   checkCrossOrigin(exampleOrg_app, exampleOrg);
   checkCrossOrigin(exampleOrg_app, nullPrin_app);
   checkCrossOrigin(exampleOrg_browser, exampleOrg_app);
   checkCrossOrigin(exampleOrg_browser, nullPrin_browser);
   checkCrossOrigin(exampleOrg_appBrowser, exampleOrg_app);
   checkCrossOrigin(exampleOrg_appBrowser, nullPrin_appBrowser);
   checkCrossOrigin(exampleOrg_appBrowser, exampleCom_appBrowser);
+  checkCrossOrigin(exampleOrg_addon, exampleOrg);
 }
new file mode 100644
--- /dev/null
+++ b/config/external/lgpllibs/lgpllibs.def
@@ -0,0 +1,10 @@
+; 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/.
+
+LIBRARY lgpllibs.dll
+
+EXPORTS
+        av_rdft_init
+        av_rdft_calc
+        av_rdft_end
--- a/config/external/lgpllibs/moz.build
+++ b/config/external/lgpllibs/moz.build
@@ -7,8 +7,12 @@
 # The lgpllibs library stores symbols from third-party LGPL licensed libraries,
 # such as libav and libsoundtouch. It fulfills the requirement of dynamically
 # linking these symbols into gecko.
 #
 # Any library added here should also be reflected in the about:license page.
 
 SharedLibrary('lgpllibs')
 SHARED_LIBRARY_NAME = 'lgpllibs'
+
+if CONFIG['MOZ_LIBAV_FFT']:
+    DIRS += ['/media/libav']
+    DEFFILE = SRCDIR + '/lgpllibs.def'
--- a/config/system-headers
+++ b/config/system-headers
@@ -1253,16 +1253,19 @@ X11/XKBlib.h
 X11/Xlib.h
 X11/Xlibint.h
 X11/Xlocale.h
 X11/Xos.h
 X11/Xutil.h
 zmouse.h
 soundtouch/SoundTouch.h
 soundtouch/SoundTouchFactory.h
+#if MOZ_LIBAV_FFT==1
+libavcodec/avfft.h
+#endif
 #if MOZ_NATIVE_PNG==1
 png.h
 #endif
 #if MOZ_NATIVE_ZLIB==1
 zlib.h
 #endif
 #ifdef MOZ_ENABLE_STARTUP_NOTIFICATION
 libsn/sn.h
--- a/configure.in
+++ b/configure.in
@@ -6270,16 +6270,84 @@ dnl On other platforms, use the C compil
   esac
 elif test -n "$MOZ_LIBJPEG_TURBO"; then
     dnl Warn if we're not building the optimized routines, even though the user
     dnl didn't specify --disable-libjpeg-turbo.
     AC_MSG_WARN([No assembler or assembly support for libjpeg-turbo.  Using unoptimized C routines.])
 fi
 
 dnl ========================================================
+dnl = libav-fft configuration
+dnl ========================================================
+
+MOZ_LIBAV_FFT=
+
+dnl Turn on libav-fft for 32-bit windows, and all 64-bit supported platforms.
+dnl 32-bit linux/os x have text relocation issues.
+
+case "$OS_ARCH:$CPU_ARCH" in
+  WINNT:x86)
+      MOZ_LIBAV_FFT=1
+  ;;
+  *:x86_64)
+      MOZ_LIBAV_FFT=1
+  ;;
+esac
+
+dnl Detect if we can use yasm to compile libav's assembly
+
+if test -n "$MOZ_LIBAV_FFT"; then
+  AC_DEFINE(MOZ_LIBAV_FFT)
+  dnl Do we support libav-fft on this platform?
+  case "$OS_ARCH:$CPU_ARCH" in
+  Darwin:x86_64)
+    LIBAV_FFT_ASFLAGS="-f macho64 -rnasm -pnasm -D__x86_64__ -DPIC -DMACHO"
+  ;;
+  WINNT:x86)
+    LIBAV_FFT_ASFLAGS="-f win32 -rnasm -pnasm -DPIC -DWIN32"
+  ;;
+  WINNT:x86_64)
+    LIBAV_FFT_ASFLAGS="-f win64 -rnasm -pnasm -D__x86_64__ -DPIC -DWIN64 -DMSVC"
+  ;;
+  *:x86_64)
+    if $CC -E -dM -</dev/null | grep -q __ELF__; then
+      LIBAV_FFT_ASFLAGS="-f elf64 -rnasm -pnasm -D__x86_64__ -DPIC -DELF"
+    fi
+  ;;
+  *)
+    AC_MSG_ERROR([libav's FFT routines are only available for 32-bit windows or 64-bit x86 based platforms.])
+  ;;
+  esac
+fi
+
+if test -n "$LIBAV_FFT_ASFLAGS"; then
+  dnl If we're on an x86 or x64 system which supports libav-fft's asm routines
+  dnl check for Yasm, and error out if it doesn't exist or we have too old of a
+  dnl version.
+  LIBAV_FFT_AS=$YASM
+  if test -z "$LIBAV_FFT_AS" ; then
+    AC_MSG_ERROR([Yasm is required to build with libav's optimized FFT routines, but you do not appear to have Yasm installed. See https://developer.mozilla.org/en/YASM for more details.])
+  fi
+  dnl Check that we have the right yasm version.  We require 1.0.1 or newer
+  dnl on Linux and 1.1 or newer everywhere else.
+  if test "$OS_ARCH" = "Linux" ; then
+    if test "$_YASM_MAJOR_VERSION" -lt "1" -o \( "$_YASM_MAJOR_VERSION" -eq "1" -a "$_YASM_MINOR_VERSION" -eq "0" -a "$_YASM_RELEASE" -lt "1" \) ; then
+      AC_MSG_ERROR([Yasm 1.0.1 or greater is required to build with libav's optimized FFT routines, but you do not appear to have Yasm installed.  See https://developer.mozilla.org/en/YASM for more details.])
+    fi
+  else
+    if test "$_YASM_MAJOR_VERSION" -lt "1" -o \( "$_YASM_MAJOR_VERSION" -eq "1" -a "$_YASM_MINOR_VERSION" -lt "1" \) ; then
+      AC_MSG_ERROR([Yasm 1.1 or greater is required to build with libav's optimized FFT routines, but you do not appear to have Yasm installed.  See https://developer.mozilla.org/en/YASM for more details.])
+    fi
+  fi
+elif test -n "$MOZ_LIBAV_FFT" -a "${CPU_ARCH}" != "arm"; then
+  dnl Warn if we're not building either libav or opendl-max optimized routines.
+  AC_MSG_WARN([No assembler or assembly support for libav-fft.  Using unoptimized C routines.])
+fi
+
+dnl ========================================================
 dnl = Enable compilation of specific extension modules
 dnl ========================================================
 
 MOZ_ARG_ENABLE_STRING(extensions,
 [  --enable-extensions     Enable extensions],
 [ for option in `echo $enableval | sed 's/,/ /g'`; do
     if test "$option" = "yes" -o "$option" = "all"; then
         AC_MSG_ERROR([--enable-extensions=$option is no longer supported.])
@@ -8954,16 +9022,19 @@ AC_SUBST(VPX_AS_CONVERSION)
 AC_SUBST(VPX_ASM_SUFFIX)
 AC_SUBST(VPX_X86_ASM)
 AC_SUBST(VPX_ARM_ASM)
 AC_SUBST(VPX_NEED_OBJ_INT_EXTRACT)
 AC_SUBST(MOZ_INSTRUMENT_EVENT_LOOP)
 AC_SUBST(MOZ_CODE_COVERAGE)
 AC_SUBST(LIBJPEG_TURBO_AS)
 AC_SUBST_LIST(LIBJPEG_TURBO_ASFLAGS)
+AC_SUBST(MOZ_LIBAV_FFT)
+AC_SUBST(LIBAV_FFT_AS)
+AC_SUBST_LIST(LIBAV_FFT_ASFLAGS)
 
 AC_SUBST(MOZ_PACKAGE_JSSHELL)
 AC_SUBST(MOZ_FOLD_LIBS)
 AC_SUBST(SOCORRO_SYMBOL_UPLOAD_TOKEN_FILE)
 
 AC_SUBST(MOZ_ENABLE_SZIP)
 AC_SUBST(MOZ_SZIP_FLAGS)
 
--- a/docshell/test/chrome/chrome.ini
+++ b/docshell/test/chrome/chrome.ini
@@ -1,10 +1,10 @@
 [DEFAULT]
-skip-if = buildapp == 'b2g'
+skip-if = buildapp == 'b2g' || os == 'android'
 support-files =
   662200a.html
   662200b.html
   662200c.html
   89419.html
   92598_nostore.html
   bug112564_window.xul
   bug113934_window.xul
--- a/dom/activities/tests/mochi/mochitest.ini
+++ b/dom/activities/tests/mochi/mochitest.ini
@@ -1,10 +1,10 @@
 [DEFAULT]
-skip-if = e10s
+skip-if = e10s || os == 'android'
 support-files =
   common.js
   system.webapp
   system.webapp^headers^
   manifest.webapp
   manifest.webapp^headers^
 
 [test_dev_mode_activity.html]
--- a/dom/animation/AnimationTimeline.h
+++ b/dom/animation/AnimationTimeline.h
@@ -15,16 +15,18 @@
 #include "mozilla/Attributes.h"
 #include "nsHashKeys.h"
 #include "nsIGlobalObject.h"
 #include "nsTHashtable.h"
 
 namespace mozilla {
 namespace dom {
 
+class Animation;
+
 class AnimationTimeline
   : public nsISupports
   , public nsWrapperCache
 {
 public:
   explicit AnimationTimeline(nsIGlobalObject* aWindow)
     : mWindow(aWindow)
   {
--- a/dom/apps/tests/chrome.ini
+++ b/dom/apps/tests/chrome.ini
@@ -1,10 +1,10 @@
 [DEFAULT]
-skip-if = buildapp == 'b2g'
+skip-if = buildapp == 'b2g' || os == 'android'
 support-files =
   asmjs/*
   file_bug_945152.html
   file_bug_945152.sjs
 
 [test_apps_service.xul]
 [test_bug_945152.html]
 skip-if = os != 'linux'
--- a/dom/audiochannel/AudioChannelAgent.cpp
+++ b/dom/audiochannel/AudioChannelAgent.cpp
@@ -1,16 +1,15 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "AudioChannelAgent.h"
-#include "AudioChannelCommon.h"
 #include "AudioChannelService.h"
 #include "nsIDOMWindow.h"
 #include "nsPIDOMWindow.h"
 #include "nsXULAppAPI.h"
 
 using namespace mozilla::dom;
 
 NS_IMPL_CYCLE_COLLECTION(AudioChannelAgent, mWindow, mCallback)
@@ -21,25 +20,23 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(AudioChannelAgent)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(AudioChannelAgent)
 
 AudioChannelAgent::AudioChannelAgent()
   : mAudioChannelType(AUDIO_AGENT_CHANNEL_ERROR)
   , mIsRegToService(false)
-  , mVisible(true)
-  , mWithVideo(false)
 {
 }
 
 AudioChannelAgent::~AudioChannelAgent()
 {
   if (mIsRegToService) {
-    StopPlaying();
+    NotifyStoppedPlaying();
   }
 }
 
 /* readonly attribute long audioChannelType; */
 NS_IMETHODIMP AudioChannelAgent::GetAudioChannelType(int32_t *aAudioChannelType)
 {
   *aAudioChannelType = mAudioChannelType;
   return NS_OK;
@@ -61,31 +58,20 @@ NS_IMETHODIMP
 AudioChannelAgent::InitWithWeakCallback(nsIDOMWindow* aWindow,
                                         int32_t aChannelType,
                                         nsIAudioChannelAgentCallback *aCallback)
 {
   return InitInternal(aWindow, aChannelType, aCallback,
                       /* useWeakRef = */ true);
 }
 
-/* void initWithVideo(in nsIDOMWindow window, in long channelType,
- *                    in nsIAudioChannelAgentCallback callback, in boolean weak); */
-NS_IMETHODIMP
-AudioChannelAgent::InitWithVideo(nsIDOMWindow* aWindow, int32_t aChannelType,
-                                 nsIAudioChannelAgentCallback *aCallback,
-                                 bool aUseWeakRef)
-{
-  return InitInternal(aWindow, aChannelType, aCallback, aUseWeakRef,
-                      /* withVideo = */ true);
-}
-
 nsresult
 AudioChannelAgent::InitInternal(nsIDOMWindow* aWindow, int32_t aChannelType,
                                 nsIAudioChannelAgentCallback *aCallback,
-                                bool aUseWeakRef, bool aWithVideo)
+                                bool aUseWeakRef)
 {
   // We syncd the enum of channel type between nsIAudioChannelAgent.idl and
   // AudioChannelBinding.h the same.
   MOZ_ASSERT(int(AUDIO_AGENT_CHANNEL_NORMAL) == int(AudioChannel::Normal) &&
              int(AUDIO_AGENT_CHANNEL_CONTENT) == int(AudioChannel::Content) &&
              int(AUDIO_AGENT_CHANNEL_NOTIFICATION) == int(AudioChannel::Notification) &&
              int(AUDIO_AGENT_CHANNEL_ALARM) == int(AudioChannel::Alarm) &&
              int(AUDIO_AGENT_CHANNEL_TELEPHONY) == int(AudioChannel::Telephony) &&
@@ -95,91 +81,75 @@ AudioChannelAgent::InitInternal(nsIDOMWi
 
   if (mAudioChannelType != AUDIO_AGENT_CHANNEL_ERROR ||
       aChannelType > AUDIO_AGENT_CHANNEL_PUBLICNOTIFICATION ||
       aChannelType < AUDIO_AGENT_CHANNEL_NORMAL) {
     return NS_ERROR_FAILURE;
   }
 
   if (aWindow) {
-    nsCOMPtr<nsPIDOMWindow> pWindow = do_QueryInterface(aWindow);
-    if (!pWindow->IsInnerWindow()) {
-      pWindow = pWindow->GetCurrentInnerWindow();
+    nsCOMPtr<nsIDOMWindow> topWindow;
+    aWindow->GetScriptableTop(getter_AddRefs(topWindow));
+    MOZ_ASSERT(topWindow);
+
+    mWindow = do_QueryInterface(topWindow);
+    if (!mWindow) {
+      return NS_ERROR_FAILURE;
     }
 
-    mWindow = pWindow.forget();
+    mWindow = mWindow->GetOuterWindow();
   }
 
   mAudioChannelType = aChannelType;
 
   if (aUseWeakRef) {
     mWeakCallback = do_GetWeakReference(aCallback);
   } else {
     mCallback = aCallback;
   }
 
-  mWithVideo = aWithVideo;
-
   return NS_OK;
 }
 
-/* boolean startPlaying (); */
-NS_IMETHODIMP AudioChannelAgent::StartPlaying(int32_t *_retval)
+/* boolean notifyStartedPlaying (); */
+NS_IMETHODIMP AudioChannelAgent::NotifyStartedPlaying(float *aVolume,
+                                                      bool* aMuted)
 {
-  AudioChannelService *service = AudioChannelService::GetOrCreateAudioChannelService();
+  MOZ_ASSERT(aVolume);
+  MOZ_ASSERT(aMuted);
+
+  nsRefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
   if (mAudioChannelType == AUDIO_AGENT_CHANNEL_ERROR ||
       service == nullptr || mIsRegToService) {
     return NS_ERROR_FAILURE;
   }
 
   service->RegisterAudioChannelAgent(this,
-    static_cast<AudioChannel>(mAudioChannelType), mWithVideo);
-  *_retval = service->GetState(this, !mVisible);
+    static_cast<AudioChannel>(mAudioChannelType));
+
+  service->GetState(mWindow, mAudioChannelType, aVolume, aMuted);
+
   mIsRegToService = true;
   return NS_OK;
 }
 
-/* void stopPlaying (); */
-NS_IMETHODIMP AudioChannelAgent::StopPlaying(void)
+/* void notifyStoppedPlaying (); */
+NS_IMETHODIMP AudioChannelAgent::NotifyStoppedPlaying(void)
 {
   if (mAudioChannelType == AUDIO_AGENT_CHANNEL_ERROR ||
       !mIsRegToService) {
     return NS_ERROR_FAILURE;
   }
 
-  AudioChannelService *service = AudioChannelService::GetOrCreateAudioChannelService();
+  nsRefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
   service->UnregisterAudioChannelAgent(this);
   mIsRegToService = false;
   return NS_OK;
 }
 
-/* void setVisibilityState (in boolean visible); */
-NS_IMETHODIMP AudioChannelAgent::SetVisibilityState(bool visible)
-{
-  bool oldVisibility = mVisible;
-
-  nsCOMPtr<nsIAudioChannelAgentCallback> callback = GetCallback();
-
-  mVisible = visible;
-  if (mIsRegToService && oldVisibility != mVisible && callback) {
-    AudioChannelService *service = AudioChannelService::GetOrCreateAudioChannelService();
-    callback->CanPlayChanged(service->GetState(this, !mVisible));
-  }
-  return NS_OK;
-}
-
-void AudioChannelAgent::NotifyAudioChannelStateChanged()
-{
-  nsCOMPtr<nsIAudioChannelAgentCallback> callback = GetCallback();
-  if (callback) {
-    AudioChannelService *service = AudioChannelService::GetOrCreateAudioChannelService();
-    callback->CanPlayChanged(service->GetState(this, !mVisible));
-  }
-}
-
 already_AddRefed<nsIAudioChannelAgentCallback>
 AudioChannelAgent::GetCallback()
 {
   nsCOMPtr<nsIAudioChannelAgentCallback> callback = mCallback;
   if (!callback) {
     callback = do_QueryReferent(mWeakCallback);
   }
   return callback.forget();
@@ -188,25 +158,22 @@ AudioChannelAgent::GetCallback()
 void
 AudioChannelAgent::WindowVolumeChanged()
 {
   nsCOMPtr<nsIAudioChannelAgentCallback> callback = GetCallback();
   if (!callback) {
     return;
   }
 
-  callback->WindowVolumeChanged();
+  float volume = 1.0;
+  bool muted = false;
+
+  nsRefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
+  service->GetState(mWindow, mAudioChannelType, &volume, &muted);
+
+  callback->WindowVolumeChanged(volume, muted);
 }
 
-NS_IMETHODIMP
-AudioChannelAgent::GetWindowVolume(float* aVolume)
+uint64_t
+AudioChannelAgent::WindowID() const
 {
-  NS_ENSURE_ARG_POINTER(aVolume);
-
-  nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(mWindow);
-  if (!win) {
-    *aVolume = 1.0f;
-    return NS_OK;
-  }
-
-  *aVolume = win->GetAudioGlobalVolume();
-  return NS_OK;
+  return mWindow ? mWindow->WindowID() : 0;
 }
--- a/dom/audiochannel/AudioChannelAgent.h
+++ b/dom/audiochannel/AudioChannelAgent.h
@@ -12,56 +12,57 @@
 #include "nsCOMPtr.h"
 #include "nsWeakPtr.h"
 
 #define NS_AUDIOCHANNELAGENT_CONTRACTID "@mozilla.org/audiochannelagent;1"
 // f27688e2-3dd7-11e2-904e-10bf48d64bd4
 #define NS_AUDIOCHANNELAGENT_CID {0xf27688e2, 0x3dd7, 0x11e2, \
       {0x90, 0x4e, 0x10, 0xbf, 0x48, 0xd6, 0x4b, 0xd4}}
 
-class nsIDOMWindow;
+class nsPIDOMWindow;
 
 namespace mozilla {
 namespace dom {
 
 /* Header file */
 class AudioChannelAgent : public nsIAudioChannelAgent
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_NSIAUDIOCHANNELAGENT
 
   NS_DECL_CYCLE_COLLECTION_CLASS(AudioChannelAgent)
 
   AudioChannelAgent();
-  virtual void NotifyAudioChannelStateChanged();
 
   void WindowVolumeChanged();
 
-  nsIDOMWindow* Window() const
+  nsPIDOMWindow* Window() const
   {
     return mWindow;
   }
 
+  uint64_t WindowID() const;
+
 private:
   virtual ~AudioChannelAgent();
 
   // Returns mCallback if that's non-null, or otherwise tries to get an
   // nsIAudioChannelAgentCallback out of mWeakCallback.
   already_AddRefed<nsIAudioChannelAgentCallback> GetCallback();
 
   nsresult InitInternal(nsIDOMWindow* aWindow, int32_t aAudioAgentType,
                         nsIAudioChannelAgentCallback* aCallback,
-                        bool aUseWeakRef, bool aWithVideo=false);
+                        bool aUseWeakRef);
 
-  nsCOMPtr<nsIDOMWindow> mWindow;
+  nsCOMPtr<nsPIDOMWindow> mWindow;
   nsCOMPtr<nsIAudioChannelAgentCallback> mCallback;
+
   nsWeakPtr mWeakCallback;
+
   int32_t mAudioChannelType;
   bool mIsRegToService;
-  bool mVisible;
-  bool mWithVideo;
 };
 
 } // namespace dom
 } // namespace mozilla
+
 #endif
-
deleted file mode 100644
--- a/dom/audiochannel/AudioChannelCommon.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef mozilla_dom_audiochannelcommon_h__
-#define mozilla_dom_audiochannelcommon_h__
-
-namespace mozilla {
-namespace dom {
-
-enum AudioChannelState {
-  AUDIO_CHANNEL_STATE_NORMAL = 0,
-  AUDIO_CHANNEL_STATE_MUTED,
-  AUDIO_CHANNEL_STATE_FADED,
-  AUDIO_CHANNEL_STATE_LAST
-};
-
-} // namespace dom
-} // namespace mozilla
-
-#endif
-
--- a/dom/audiochannel/AudioChannelService.cpp
+++ b/dom/audiochannel/AudioChannelService.cpp
@@ -1,26 +1,27 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "AudioChannelService.h"
-#include "AudioChannelServiceChild.h"
 
 #include "base/basictypes.h"
 
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/unused.h"
 
+#include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/ContentParent.h"
 
 #include "nsContentUtils.h"
+#include "nsIScriptSecurityManager.h"
 #include "nsISupportsPrimitives.h"
 #include "nsThreadUtils.h"
 #include "nsHashPropertyBag.h"
 #include "nsComponentManagerUtils.h"
 #include "nsPIDOMWindow.h"
 #include "nsServiceManagerUtils.h"
 #include "mozilla/dom/SettingChangeNotificationBinding.h"
 
@@ -32,849 +33,446 @@
 #endif
 
 #include "mozilla/Preferences.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::hal;
 
-// When a inner-window is destroyed we have to mute all the related
-// AudioChannelAgents. In order to do this we have to notify them after purging
-// AudioChannelService::mAgents.
-struct MOZ_STACK_CLASS WindowDestroyedEnumeratorData
+namespace {
+
+// If true, any new AudioChannelAgent will be muted when created.
+bool sAudioChannelMutedByDefault = false;
+
+class NotifyChannelActiveRunnable final : public nsRunnable
 {
-  explicit WindowDestroyedEnumeratorData(uint64_t aInnerID)
-    : mInnerID(aInnerID)
+public:
+  NotifyChannelActiveRunnable(uint64_t aWindowID, AudioChannel aAudioChannel,
+                              bool aActive)
+    : mWindowID(aWindowID)
+    , mAudioChannel(aAudioChannel)
+    , mActive(aActive)
   {}
 
-  nsTArray<nsRefPtr<AudioChannelAgent>> mAgents;
-  uint64_t mInnerID;
+  NS_IMETHOD Run() override
+  {
+    nsCOMPtr<nsIObserverService> observerService =
+      services::GetObserverService();
+    if (NS_WARN_IF(!observerService)) {
+      return NS_ERROR_FAILURE;
+    }
+
+    nsCOMPtr<nsISupportsPRUint64> wrapper =
+      do_CreateInstance(NS_SUPPORTS_PRUINT64_CONTRACTID);
+    if (NS_WARN_IF(!wrapper)) {
+       return NS_ERROR_FAILURE;
+    }
+
+    wrapper->SetData(mWindowID);
+
+    nsAutoString name;
+    AudioChannelService::GetAudioChannelString(mAudioChannel, name);
+
+    nsAutoCString topic;
+    topic.Assign("audiochannel-activity-");
+    topic.Append(NS_ConvertUTF16toUTF8(name));
+
+    observerService->NotifyObservers(wrapper, topic.get(),
+                                     mActive
+                                       ? MOZ_UTF16("active")
+                                       : MOZ_UTF16("inactive"));
+    return NS_OK;
+  }
+
+private:
+  const uint64_t mWindowID;
+  const AudioChannel mAudioChannel;
+  const bool mActive;
 };
 
+void
+NotifyChannelActive(uint64_t aWindowID, AudioChannel aAudioChannel,
+                    bool aActive)
+{
+  nsRefPtr<nsRunnable> runnable =
+    new NotifyChannelActiveRunnable(aWindowID, aAudioChannel, aActive);
+  NS_DispatchToCurrentThread(runnable);
+}
+
+already_AddRefed<nsPIDOMWindow>
+GetTopWindow(nsIDOMWindow* aWindow)
+{
+  MOZ_ASSERT(aWindow);
+
+  nsCOMPtr<nsIDOMWindow> topWindow;
+  aWindow->GetScriptableTop(getter_AddRefs(topWindow));
+  MOZ_ASSERT(topWindow);
+
+  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(topWindow);
+  window = window->GetOuterWindow();
+
+  return window.forget();
+}
+
+bool
+IsParentProcess()
+{
+  return XRE_GetProcessType() == GeckoProcessType_Default;
+}
+
+class MediaPlaybackRunnable : public nsRunnable
+{
+public:
+  MediaPlaybackRunnable(nsIDOMWindow* aWindow, bool aActive)
+    : mWindow(aWindow)
+    , mActive(aActive)
+  {}
+
+ NS_IMETHOD Run()
+ {
+    nsCOMPtr<nsIObserverService> observerService =
+      services::GetObserverService();
+    if (observerService) {
+      observerService->NotifyObservers(
+        ToSupports(mWindow),
+        "media-playback",
+        mActive ? NS_LITERAL_STRING("active").get()
+                : NS_LITERAL_STRING("inactive").get());
+    }
+
+    return NS_OK;
+  }
+
+private:
+  nsCOMPtr<nsIDOMWindow> mWindow;
+  bool mActive;
+};
+
+} // anonymous namespace
+
 StaticRefPtr<AudioChannelService> gAudioChannelService;
 
 // Mappings from 'mozaudiochannel' attribute strings to an enumeration.
 static const nsAttrValue::EnumTable kMozAudioChannelAttributeTable[] = {
   { "normal",             (int16_t)AudioChannel::Normal },
   { "content",            (int16_t)AudioChannel::Content },
   { "notification",       (int16_t)AudioChannel::Notification },
   { "alarm",              (int16_t)AudioChannel::Alarm },
   { "telephony",          (int16_t)AudioChannel::Telephony },
   { "ringer",             (int16_t)AudioChannel::Ringer },
   { "publicnotification", (int16_t)AudioChannel::Publicnotification },
   { nullptr }
 };
 
-// static
-AudioChannelService*
-AudioChannelService::GetAudioChannelService()
+/* static */ already_AddRefed<AudioChannelService>
+AudioChannelService::GetOrCreate()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  if (!XRE_IsParentProcess()) {
-    return AudioChannelServiceChild::GetAudioChannelService();
+  if (!gAudioChannelService) {
+    gAudioChannelService = new AudioChannelService();
   }
 
-  return gAudioChannelService;
-
-}
-
-// static
-AudioChannelService*
-AudioChannelService::GetOrCreateAudioChannelService()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  if (!XRE_IsParentProcess()) {
-    return AudioChannelServiceChild::GetOrCreateAudioChannelService();
-  }
-
-  // If we already exist, exit early
-  if (gAudioChannelService) {
-    return gAudioChannelService;
-  }
-
-  // Create new instance, register, return
-  nsRefPtr<AudioChannelService> service = new AudioChannelService();
-  MOZ_ASSERT(service);
-
-  gAudioChannelService = service;
-  return gAudioChannelService;
+  nsRefPtr<AudioChannelService> service = gAudioChannelService.get();
+  return service.forget();
 }
 
 void
 AudioChannelService::Shutdown()
 {
-  if (!XRE_IsParentProcess()) {
-    return AudioChannelServiceChild::Shutdown();
-  }
+  if (gAudioChannelService) {
+    if (IsParentProcess()) {
+      nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+      if (obs) {
+        obs->RemoveObserver(gAudioChannelService, "ipc:content-shutdown");
+        obs->RemoveObserver(gAudioChannelService, "xpcom-shutdown");
+        obs->RemoveObserver(gAudioChannelService, "inner-window-destroyed");
+#ifdef MOZ_WIDGET_GONK
+        // To monitor the volume settings based on audio channel.
+        obs->RemoveObserver(gAudioChannelService, "mozsettings-changed");
+#endif
+      }
+    }
 
-  if (gAudioChannelService) {
     gAudioChannelService = nullptr;
   }
 }
 
-NS_IMPL_ISUPPORTS(AudioChannelService, nsIObserver, nsITimerCallback)
+NS_INTERFACE_MAP_BEGIN(AudioChannelService)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAudioChannelService)
+  NS_INTERFACE_MAP_ENTRY(nsIAudioChannelService)
+  NS_INTERFACE_MAP_ENTRY(nsIObserver)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_ADDREF(AudioChannelService)
+NS_IMPL_RELEASE(AudioChannelService)
 
 AudioChannelService::AudioChannelService()
-: mCurrentHigherChannel(-1)
-, mCurrentVisibleHigherChannel(-1)
-, mPlayableHiddenContentChildID(CONTENT_PROCESS_ID_UNKNOWN)
-, mDisabled(false)
-, mDefChannelChildID(CONTENT_PROCESS_ID_UNKNOWN)
+  : mDisabled(false)
+  , mDefChannelChildID(CONTENT_PROCESS_ID_UNKNOWN)
+  , mTelephonyChannel(false)
+  , mContentOrNormalChannel(false)
+  , mAnyChannel(false)
 {
-  if (XRE_IsParentProcess()) {
+  if (IsParentProcess()) {
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     if (obs) {
       obs->AddObserver(this, "ipc:content-shutdown", false);
       obs->AddObserver(this, "xpcom-shutdown", false);
       obs->AddObserver(this, "inner-window-destroyed", false);
 #ifdef MOZ_WIDGET_GONK
       // To monitor the volume settings based on audio channel.
       obs->AddObserver(this, "mozsettings-changed", false);
 #endif
     }
   }
+
+  Preferences::AddBoolVarCache(&sAudioChannelMutedByDefault,
+                               "dom.audiochannel.mutedByDefault");
 }
 
 AudioChannelService::~AudioChannelService()
 {
 }
 
 void
 AudioChannelService::RegisterAudioChannelAgent(AudioChannelAgent* aAgent,
-                                               AudioChannel aChannel,
-                                               bool aWithVideo)
-{
-  if (mDisabled) {
-    return;
-  }
-
-  AudioChannelAgentData* data = new AudioChannelAgentData(aChannel,
-                                true /* aElementHidden */,
-                                AUDIO_CHANNEL_STATE_MUTED /* aState */,
-                                aWithVideo);
-  mAgents.Put(aAgent, data);
-  RegisterType(aChannel, CONTENT_PROCESS_ID_MAIN, aWithVideo);
-
-  // If this is the first agent for this window, we must notify the observers.
-  uint32_t count = CountWindow(aAgent->Window());
-  if (count == 1) {
-    nsCOMPtr<nsIObserverService> observerService =
-      services::GetObserverService();
-    if (observerService) {
-      observerService->NotifyObservers(ToSupports(aAgent->Window()),
-                                       "media-playback",
-                                       NS_LITERAL_STRING("active").get());
-    }
-  }
-}
-
-void
-AudioChannelService::RegisterType(AudioChannel aChannel, uint64_t aChildID,
-                                  bool aWithVideo)
+                                               AudioChannel aChannel)
 {
   if (mDisabled) {
     return;
   }
 
-  AudioChannelInternalType type = GetInternalType(aChannel, true);
-  mChannelCounters[type].AppendElement(aChildID);
-
-  if (XRE_IsParentProcess()) {
+  uint64_t windowID = aAgent->WindowID();
+  AudioChannelWindow* winData = GetWindowData(windowID);
+  if (!winData) {
+    winData = new AudioChannelWindow(windowID);
+    mWindows.AppendElement(winData);
+  }
 
-    // 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);
-    }
+  MOZ_ASSERT(!winData->mAgents.Contains(aAgent));
+  winData->mAgents.AppendElement(aAgent);
 
-    // 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);
-    }
+  ++winData->mChannels[(uint32_t)aChannel].mNumberOfAgents;
 
-    if (aWithVideo) {
-      mWithVideoChildIDs.AppendElement(aChildID);
-    }
+  // The first one, we must inform the BrowserElementAudioChannel.
+  if (winData->mChannels[(uint32_t)aChannel].mNumberOfAgents == 1) {
+    NotifyChannelActive(aAgent->WindowID(), aChannel, true);
+  }
 
-    // No hidden content channel can be playable if there is a content channel
-    // in foreground (bug 855208), nor if there is a normal channel with video
-    // in foreground (bug 894249).
-    if (type == AUDIO_CHANNEL_INT_CONTENT ||
-        (type == AUDIO_CHANNEL_INT_NORMAL &&
-         mWithVideoChildIDs.Contains(aChildID))) {
-      mPlayableHiddenContentChildID = CONTENT_PROCESS_ID_UNKNOWN;
-    }
-    // One hidden content channel can be playable only when there is no any
-    // content channel in the foreground, and no normal channel with video in
-    // foreground.
-    else if (type == AUDIO_CHANNEL_INT_CONTENT_HIDDEN &&
-        mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].IsEmpty()) {
-      mPlayableHiddenContentChildID = aChildID;
-    }
+  // If this is the first agent for this window, we must notify the observers.
+  if (winData->mAgents.Length() == 1) {
+    nsRefPtr<MediaPlaybackRunnable> runnable =
+      new MediaPlaybackRunnable(aAgent->Window(), true /* active */);
+    NS_DispatchToCurrentThread(runnable);
+  }
 
-    // In order to avoid race conditions, it's safer to notify any existing
-    // agent any time a new one is registered.
-    SendAudioChannelChangedNotification(aChildID);
-    SendNotification();
-  }
+  MaybeSendStatusUpdate();
 }
 
 void
 AudioChannelService::UnregisterAudioChannelAgent(AudioChannelAgent* aAgent)
 {
   if (mDisabled) {
     return;
   }
 
-  nsAutoPtr<AudioChannelAgentData> data;
-  mAgents.RemoveAndForget(aAgent, data);
+  AudioChannelWindow* winData = GetWindowData(aAgent->WindowID());
+  if (!winData) {
+    return;
+  }
+
+  if (winData->mAgents.Contains(aAgent)) {
+    int32_t channel = aAgent->AudioChannelType();
+    uint64_t windowID = aAgent->WindowID();
 
-  if (data) {
-    UnregisterType(data->mChannel, data->mElementHidden,
-                   CONTENT_PROCESS_ID_MAIN, data->mWithVideo);
+    // aAgent can be freed after this call.
+    winData->mAgents.RemoveElement(aAgent);
+
+    MOZ_ASSERT(winData->mChannels[channel].mNumberOfAgents > 0);
+
+    --winData->mChannels[channel].mNumberOfAgents;
+
+    // The last one, we must inform the BrowserElementAudioChannel.
+    if (winData->mChannels[channel].mNumberOfAgents == 0) {
+      NotifyChannelActive(windowID, static_cast<AudioChannel>(channel), false);
+    }
   }
 
 #ifdef MOZ_WIDGET_GONK
   bool active = AnyAudioChannelIsActive();
   for (uint32_t i = 0; i < mSpeakerManager.Length(); i++) {
     mSpeakerManager[i]->SetAudioChannelActive(active);
   }
 #endif
 
   // If this is the last agent for this window, we must notify the observers.
-  uint32_t count = CountWindow(aAgent->Window());
-  if (count == 0) {
-    nsCOMPtr<nsIObserverService> observerService =
-      services::GetObserverService();
-    if (observerService) {
-      observerService->NotifyObservers(ToSupports(aAgent->Window()),
-                                       "media-playback",
-                                       NS_LITERAL_STRING("inactive").get());
-    }
-  }
-}
-
-void
-AudioChannelService::UnregisterType(AudioChannel aChannel,
-                                    bool aElementHidden,
-                                    uint64_t aChildID,
-                                    bool aWithVideo)
-{
-  if (mDisabled) {
-    return;
+  if (winData->mAgents.IsEmpty()) {
+    nsRefPtr<MediaPlaybackRunnable> runnable =
+      new MediaPlaybackRunnable(aAgent->Window(), false /* active */);
+    NS_DispatchToCurrentThread(runnable);
   }
 
-  // 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_IsParentProcess()) {
-
-    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,
-                                            uint64_t aChildID,
-                                            bool aWithVideo)
-{
-  // The array may contain multiple occurrence of this appId but
-  // this should remove only the first one.
-  AudioChannelInternalType type = GetInternalType(aChannel, aElementHidden);
-  MOZ_ASSERT(mChannelCounters[type].Contains(aChildID));
-  mChannelCounters[type].RemoveElement(aChildID);
-
-  // In order to avoid race conditions, it's safer to notify any existing
-  // agent any time a new one is registered.
-  if (XRE_IsParentProcess()) {
-    // No hidden content channel is playable if the original playable hidden
-    // process does not need to play audio from background anymore.
-    if (aChannel == AudioChannel::Content &&
-        mPlayableHiddenContentChildID == aChildID &&
-        !mChannelCounters[AUDIO_CHANNEL_INT_CONTENT_HIDDEN].Contains(aChildID)) {
-      mPlayableHiddenContentChildID = CONTENT_PROCESS_ID_UNKNOWN;
-    }
-
-    if (aWithVideo) {
-      MOZ_ASSERT(mWithVideoChildIDs.Contains(aChildID));
-      mWithVideoChildIDs.RemoveElement(aChildID);
-    }
-
-    SendAudioChannelChangedNotification(aChildID);
-    SendNotification();
-  }
+  MaybeSendStatusUpdate();
 }
 
 void
-AudioChannelService::UpdateChannelType(AudioChannel aChannel,
-                                       uint64_t aChildID,
-                                       bool aElementHidden,
-                                       bool aElementWasHidden)
+AudioChannelService::GetState(nsPIDOMWindow* aWindow, uint32_t aAudioChannel,
+                              float* aVolume, bool* aMuted)
 {
-  // Calculate the new and old internal type and update the hashtable if needed.
-  AudioChannelInternalType newType = GetInternalType(aChannel, aElementHidden);
-  AudioChannelInternalType oldType = GetInternalType(aChannel, aElementWasHidden);
-
-  if (newType != oldType) {
-    mChannelCounters[newType].AppendElement(aChildID);
-    MOZ_ASSERT(mChannelCounters[oldType].Contains(aChildID));
-    mChannelCounters[oldType].RemoveElement(aChildID);
-  }
-
-  // No hidden content channel can be playable if there is a content channel
-  // in foreground (bug 855208), nor if there is a normal channel with video
-  // in foreground (bug 894249).
-  if (newType == AUDIO_CHANNEL_INT_CONTENT ||
-      (newType == AUDIO_CHANNEL_INT_NORMAL &&
-       mWithVideoChildIDs.Contains(aChildID))) {
-    mPlayableHiddenContentChildID = CONTENT_PROCESS_ID_UNKNOWN;
-  }
-  // If there is no content channel in foreground and no normal channel with
-  // video in foreground, the last content channel which goes from foreground
-  // to background can be playable.
-  else if (oldType == AUDIO_CHANNEL_INT_CONTENT &&
-      newType == AUDIO_CHANNEL_INT_CONTENT_HIDDEN &&
-      mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].IsEmpty()) {
-    mPlayableHiddenContentChildID = aChildID;
-  }
-}
-
-AudioChannelState
-AudioChannelService::GetState(AudioChannelAgent* aAgent, bool aElementHidden)
-{
-  AudioChannelAgentData* data;
-  if (!mAgents.Get(aAgent, &data)) {
-    return AUDIO_CHANNEL_STATE_MUTED;
-  }
-
-  bool oldElementHidden = data->mElementHidden;
-  // Update visibility.
-  data->mElementHidden = aElementHidden;
+  MOZ_ASSERT(!aWindow || aWindow->IsOuterWindow());
+  MOZ_ASSERT(aVolume && aMuted);
+  MOZ_ASSERT(aAudioChannel < NUMBER_OF_AUDIO_CHANNELS);
 
-  data->mState = GetStateInternal(data->mChannel, CONTENT_PROCESS_ID_MAIN,
-                                aElementHidden, oldElementHidden);
-  #ifdef MOZ_WIDGET_GONK
-  /** Only modify the speaker status when
-   *  (1) apps in the foreground.
-   *  (2) apps in the backgrund and inactive.
-   *  Notice : check the state when the visible status is stable, because there
-   *  has lantency in passing the visibility events.
-   **/
-  bool active = AnyAudioChannelIsActive();
-  if (aElementHidden == oldElementHidden &&
-      (!aElementHidden || (aElementHidden && !active))) {
-    for (uint32_t i = 0; i < mSpeakerManager.Length(); i++) {
-      mSpeakerManager[i]->SetAudioChannelActive(active);
-    }
-  }
-  #endif
-
-  return data->mState;
-}
+  *aVolume = 1.0;
+  *aMuted = false;
 
-AudioChannelState
-AudioChannelService::GetStateInternal(AudioChannel aChannel, uint64_t aChildID,
-                                      bool aElementHidden,
-                                      bool aElementWasHidden)
-{
-  UpdateChannelType(aChannel, aChildID, aElementHidden, aElementWasHidden);
-
-  // Calculating the new and old type and update the hashtable if needed.
-  AudioChannelInternalType newType = GetInternalType(aChannel, aElementHidden);
-  AudioChannelInternalType oldType = GetInternalType(aChannel,
-                                                     aElementWasHidden);
-
-  if (newType != oldType &&
-      (aChannel == AudioChannel::Content ||
-       (aChannel == AudioChannel::Normal &&
-        mWithVideoChildIDs.Contains(aChildID)))) {
-    SendNotification();
-  }
-
-  SendAudioChannelChangedNotification(aChildID);
-
-  // Let play any visible audio channel.
-  if (!aElementHidden) {
-    if (CheckVolumeFadedCondition(newType, aElementHidden)) {
-      return AUDIO_CHANNEL_STATE_FADED;
-    }
-    return CheckTelephonyPolicy(aChannel, aChildID);
+  if (!aWindow || !aWindow->IsOuterWindow()) {
+    return;
   }
 
-  // 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
-       // channels should remain playable until all of their visibility statuses
-       // have been updated as hidden. After all its content channels have been
-       // updated properly as hidden, mPlayableHiddenContentChildID is used to
-       // check whether this background process is playable or not.
-       !(mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].Contains(aChildID) ||
-         (mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].IsEmpty() &&
-          mPlayableHiddenContentChildID == aChildID)))) {
-    return AUDIO_CHANNEL_STATE_MUTED;
-  }
+  AudioChannelWindow* winData = nullptr;
+  nsCOMPtr<nsPIDOMWindow> window = aWindow;
 
-  // After checking the condition on normal & content channel, if the state
-  // is not on muted then checking other higher channels type here.
-  if (ChannelsActiveWithHigherPriorityThan(newType)) {
-    MOZ_ASSERT(newType != AUDIO_CHANNEL_INT_NORMAL_HIDDEN);
-    if (CheckVolumeFadedCondition(newType, aElementHidden)) {
-      return AUDIO_CHANNEL_STATE_FADED;
+  // The volume must be calculated based on the window hierarchy. Here we go up
+  // to the top window and we calculate the volume and the muted flag.
+  do {
+    winData = GetWindowData(window->WindowID());
+    if (winData) {
+      *aVolume *= winData->mChannels[aAudioChannel].mVolume;
+      *aMuted = *aMuted || winData->mChannels[aAudioChannel].mMuted;
     }
-    return AUDIO_CHANNEL_STATE_MUTED;
-  }
 
-  return CheckTelephonyPolicy(aChannel, aChildID);
-}
+    *aVolume *= window->GetAudioVolume();
+    *aMuted = *aMuted || window->GetAudioMuted();
 
-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;
+    nsCOMPtr<nsIDOMWindow> win;
+    window->GetScriptableParent(getter_AddRefs(win));
+    if (window == win) {
       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) {
-    return false;
-  }
-
-  // Consider that audio from notification is with short duration
-  // so just fade the volume not pause it
-  if (mChannelCounters[AUDIO_CHANNEL_INT_NOTIFICATION].IsEmpty() &&
-      mChannelCounters[AUDIO_CHANNEL_INT_NOTIFICATION_HIDDEN].IsEmpty()) {
-    return false;
-  }
+    window = do_QueryInterface(win);
 
-  // Since this element is on the foreground, it can be allowed to play always.
-  // So return true directly when there is any notification channel alive.
-  if (aElementHidden == false) {
-   return true;
-  }
-
-  // If element is on the background, it is possible paused by channels higher
-  // then notification.
-  for (int i = AUDIO_CHANNEL_INT_LAST - 1;
-    i != AUDIO_CHANNEL_INT_NOTIFICATION_HIDDEN; --i) {
-    if (!mChannelCounters[i].IsEmpty()) {
-      return false;
-    }
-  }
-
-  return true;
-}
-
-bool
-AudioChannelService::ContentOrNormalChannelIsActive()
-{
-  return !mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].IsEmpty() ||
-         !mChannelCounters[AUDIO_CHANNEL_INT_CONTENT_HIDDEN].IsEmpty() ||
-         !mChannelCounters[AUDIO_CHANNEL_INT_NORMAL].IsEmpty();
+    // If there is no parent, or we are the toplevel we don't continue.
+  } while (window && window != aWindow);
 }
 
 bool
 AudioChannelService::TelephonyChannelIsActive()
 {
-  return !mChannelCounters[AUDIO_CHANNEL_INT_TELEPHONY].IsEmpty() ||
-         !mChannelCounters[AUDIO_CHANNEL_INT_TELEPHONY_HIDDEN].IsEmpty();
-}
-
-bool
-AudioChannelService::ProcessContentOrNormalChannelIsActive(uint64_t aChildID)
-{
-  return mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].Contains(aChildID) ||
-         mChannelCounters[AUDIO_CHANNEL_INT_CONTENT_HIDDEN].Contains(aChildID) ||
-         mChannelCounters[AUDIO_CHANNEL_INT_NORMAL].Contains(aChildID);
-}
-
-void
-AudioChannelService::SetDefaultVolumeControlChannel(int32_t aChannel,
-                                                    bool aVisible)
-{
-  SetDefaultVolumeControlChannelInternal(aChannel, aVisible,
-                                         CONTENT_PROCESS_ID_MAIN);
-}
-
-void
-AudioChannelService::SetDefaultVolumeControlChannelInternal(int32_t aChannel,
-                                                            bool aVisible,
-                                                            uint64_t aChildID)
-{
-  if (!XRE_IsParentProcess()) {
-    return;
-  }
-
-  // If this child is in the background and mDefChannelChildID is set to
-  // others then it means other child in the foreground already set it's
-  // own default channel already.
-  if (!aVisible && mDefChannelChildID != aChildID) {
-    return;
-  }
-  // Workaround for the call screen app. The call screen app is running on the
-  // main process, that will results in wrong visible state. Because we use the
-  // docshell's active state as visible state, the main process is always
-  // active. Therefore, we will see the strange situation that the visible
-  // state of the call screen is always true. If the mDefChannelChildID is set
-  // to others then it means other child in the foreground already set it's
-  // own default channel already.
-  // Summary :
-  //   Child process : foreground app always can set type.
-  //   Parent process : check the mDefChannelChildID.
-  else if (aChildID == CONTENT_PROCESS_ID_MAIN &&
-           mDefChannelChildID != CONTENT_PROCESS_ID_UNKNOWN) {
-    return;
-  }
-
-  mDefChannelChildID = aVisible ? aChildID : CONTENT_PROCESS_ID_UNKNOWN;
-  nsAutoString channelName;
-  if (aChannel == -1) {
-    channelName.AssignASCII("unknown");
-  } else {
-    GetAudioChannelString(static_cast<AudioChannel>(aChannel), channelName);
-  }
-
-  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
-  if (obs) {
-    obs->NotifyObservers(nullptr, "default-volume-channel-changed",
-                         channelName.get());
-  }
-}
-
-void
-AudioChannelService::SendAudioChannelChangedNotification(uint64_t aChildID)
-{
-  if (!XRE_IsParentProcess()) {
-    return;
-  }
-
-  nsRefPtr<nsHashPropertyBag> props = new nsHashPropertyBag();
-  props->SetPropertyAsUint64(NS_LITERAL_STRING("childID"), aChildID);
-
-  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
-  if (obs) {
-    obs->NotifyObservers(static_cast<nsIWritablePropertyBag*>(props),
-                         "audio-channel-process-changed", nullptr);
-  }
-
-  // Calculating the most important active channel.
-  int32_t higher = -1;
-
-  // Top-Down in the hierarchy for visible elements
-  if (!mChannelCounters[AUDIO_CHANNEL_INT_PUBLICNOTIFICATION].IsEmpty()) {
-    higher = static_cast<int32_t>(AudioChannel::Publicnotification);
-  }
-
-  else if (!mChannelCounters[AUDIO_CHANNEL_INT_RINGER].IsEmpty()) {
-    higher = static_cast<int32_t>(AudioChannel::Ringer);
-  }
-
-  else if (!mChannelCounters[AUDIO_CHANNEL_INT_TELEPHONY].IsEmpty()) {
-    higher = static_cast<int32_t>(AudioChannel::Telephony);
-  }
-
-  else if (!mChannelCounters[AUDIO_CHANNEL_INT_ALARM].IsEmpty()) {
-    higher = static_cast<int32_t>(AudioChannel::Alarm);
-  }
-
-  else if (!mChannelCounters[AUDIO_CHANNEL_INT_NOTIFICATION].IsEmpty()) {
-    higher = static_cast<int32_t>(AudioChannel::Notification);
-  }
-
-  else if (!mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].IsEmpty()) {
-    higher = static_cast<int32_t>(AudioChannel::Content);
-  }
-
-  else if (!mChannelCounters[AUDIO_CHANNEL_INT_NORMAL].IsEmpty()) {
-    higher = static_cast<int32_t>(AudioChannel::Normal);
-  }
-
-  int32_t visibleHigher = higher;
-
-  // Top-Down in the hierarchy for non-visible elements
-  // And we can ignore normal channel because it can't play in the background.
-  int32_t index;
-  for (index = 0; kMozAudioChannelAttributeTable[index].tag; ++index);
-
-  for (--index;
-       kMozAudioChannelAttributeTable[index].value > higher &&
-       kMozAudioChannelAttributeTable[index].value > (int16_t)AudioChannel::Normal;
-       --index) {
-    // Each channel type will be split to fg and bg for recording the state,
-    // so here need to do a translation.
-    if (mChannelCounters[index * 2 + 1].IsEmpty()) {
-      continue;
-    }
-
-    if (kMozAudioChannelAttributeTable[index].value == (int16_t)AudioChannel::Content) {
-      if (mPlayableHiddenContentChildID != CONTENT_PROCESS_ID_UNKNOWN) {
-        higher = kMozAudioChannelAttributeTable[index].value;
-        break;
-      }
-    } else {
-      higher = kMozAudioChannelAttributeTable[index].value;
-      break;
+  nsTObserverArray<nsAutoPtr<AudioChannelWindow>>::ForwardIterator iter(mWindows);
+  while (iter.HasMore()) {
+    AudioChannelWindow* next = iter.GetNext();
+    if (next->mChannels[(uint32_t)AudioChannel::Telephony].mNumberOfAgents != 0 &&
+        !next->mChannels[(uint32_t)AudioChannel::Telephony].mMuted) {
+      return true;
     }
   }
 
-  if (higher != mCurrentHigherChannel) {
-    mCurrentHigherChannel = higher;
-
-    nsString channelName;
-    if (mCurrentHigherChannel != -1) {
-      GetAudioChannelString(static_cast<AudioChannel>(mCurrentHigherChannel),
-                            channelName);
-    } else {
-      channelName.AssignLiteral("none");
-    }
-
-    if (obs) {
-      obs->NotifyObservers(nullptr, "audio-channel-changed", channelName.get());
-    }
-  }
-
-  if (visibleHigher != mCurrentVisibleHigherChannel) {
-    mCurrentVisibleHigherChannel = visibleHigher;
-
-    nsString channelName;
-    if (mCurrentVisibleHigherChannel != -1) {
-      GetAudioChannelString(static_cast<AudioChannel>(mCurrentVisibleHigherChannel),
-                            channelName);
-    } else {
-      channelName.AssignLiteral("none");
-    }
-
-    if (obs) {
-      obs->NotifyObservers(nullptr, "visible-audio-channel-changed", channelName.get());
-    }
-  }
-}
-
-PLDHashOperator
-AudioChannelService::NotifyEnumerator(AudioChannelAgent* aAgent,
-                                      AudioChannelAgentData* aData, void* aUnused)
-{
-  MOZ_ASSERT(aAgent);
-  aAgent->NotifyAudioChannelStateChanged();
-  return PL_DHASH_NEXT;
-}
-
-class NotifyRunnable : public nsRunnable
-{
-public:
-  explicit NotifyRunnable(AudioChannelService* aService)
-    : mService(aService)
-  {}
-
-  NS_IMETHOD Run()
-  {
-    mService->Notify();
-    return NS_OK;
-  }
-
-private:
-  nsRefPtr<AudioChannelService> mService;
-};
-
-void
-AudioChannelService::SendNotification()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  if (mRunnable) {
-    return;
-  }
-
-  mRunnable = new NotifyRunnable(this);
-  NS_DispatchToCurrentThread(mRunnable);
-}
-
-void
-AudioChannelService::Notify()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  mRunnable = nullptr;
-
-  // Notify any agent for the main process.
-  mAgents.EnumerateRead(NotifyEnumerator, nullptr);
-
-  // Notify for the child processes.
-  nsTArray<ContentParent*> children;
-  ContentParent::GetAll(children);
-  for (uint32_t i = 0; i < children.Length(); i++) {
-    unused << children[i]->SendAudioChannelNotify();
-  }
-}
-
-NS_IMETHODIMP
-AudioChannelService::Notify(nsITimer* aTimer)
-{
-  UnregisterTypeInternal(AudioChannel::Telephony, mTimerElementHidden,
-                         mTimerChildID, false);
-  mDeferTelChannelTimer = nullptr;
-  return NS_OK;
-}
-
-bool
-AudioChannelService::AnyAudioChannelIsActive()
-{
-  for (int i = AUDIO_CHANNEL_INT_LAST - 1;
-       i >= AUDIO_CHANNEL_INT_NORMAL; --i) {
-    if (!mChannelCounters[i].IsEmpty()) {
-      return true;
+  if (IsParentProcess()) {
+    nsTObserverArray<nsAutoPtr<AudioChannelChildStatus>>::ForwardIterator
+      iter(mPlayingChildren);
+    while (iter.HasMore()) {
+      AudioChannelChildStatus* child = iter.GetNext();
+      if (child->mActiveTelephonyChannel) {
+        return true;
+      }
     }
   }
 
   return false;
 }
 
 bool
-AudioChannelService::ChannelsActiveWithHigherPriorityThan(
-  AudioChannelInternalType aType)
+AudioChannelService::ContentOrNormalChannelIsActive()
 {
-  for (int i = AUDIO_CHANNEL_INT_LAST - 1;
-       i != AUDIO_CHANNEL_INT_CONTENT_HIDDEN; --i) {
-    if (i == aType) {
-      return false;
-    }
+  // This method is meant to be used just by the child to send status update.
+  MOZ_ASSERT(!IsParentProcess());
 
-    if (!mChannelCounters[i].IsEmpty()) {
+  nsTObserverArray<nsAutoPtr<AudioChannelWindow>>::ForwardIterator iter(mWindows);
+  while (iter.HasMore()) {
+    AudioChannelWindow* next = iter.GetNext();
+    if (next->mChannels[(uint32_t)AudioChannel::Content].mNumberOfAgents > 0 ||
+        next->mChannels[(uint32_t)AudioChannel::Normal].mNumberOfAgents > 0) {
       return true;
     }
   }
+  return false;
+}
+
+AudioChannelService::AudioChannelChildStatus*
+AudioChannelService::GetChildStatus(uint64_t aChildID) const
+{
+  nsTObserverArray<nsAutoPtr<AudioChannelChildStatus>>::ForwardIterator
+    iter(mPlayingChildren);
+  while (iter.HasMore()) {
+    AudioChannelChildStatus* child = iter.GetNext();
+    if (child->mChildID == aChildID) {
+      return child;
+    }
+  }
+
+  return nullptr;
+}
+
+void
+AudioChannelService::RemoveChildStatus(uint64_t aChildID)
+{
+  nsTObserverArray<nsAutoPtr<AudioChannelChildStatus>>::ForwardIterator
+    iter(mPlayingChildren);
+  while (iter.HasMore()) {
+    nsAutoPtr<AudioChannelChildStatus>& child = iter.GetNext();
+    if (child->mChildID == aChildID) {
+      mPlayingChildren.RemoveElement(child);
+      break;
+    }
+  }
+}
+
+bool
+AudioChannelService::ProcessContentOrNormalChannelIsActive(uint64_t aChildID)
+{
+  AudioChannelChildStatus* child = GetChildStatus(aChildID);
+  if (!child) {
+    return false;
+  }
+
+  return child->mActiveContentOrNormalChannel;
+}
+
+bool
+AudioChannelService::AnyAudioChannelIsActive()
+{
+  nsTObserverArray<nsAutoPtr<AudioChannelWindow>>::ForwardIterator iter(mWindows);
+  while (iter.HasMore()) {
+    AudioChannelWindow* next = iter.GetNext();
+    for (uint32_t i = 0; kMozAudioChannelAttributeTable[i].tag; ++i) {
+      if (next->mChannels[kMozAudioChannelAttributeTable[i].value].mNumberOfAgents
+          != 0) {
+        return true;
+      }
+    }
+  }
+
+  if (IsParentProcess()) {
+    return !mPlayingChildren.IsEmpty();
+  }
 
   return false;
 }
 
-PLDHashOperator
-AudioChannelService::WindowDestroyedEnumerator(AudioChannelAgent* aAgent,
-                                               nsAutoPtr<AudioChannelAgentData>& aData,
-                                               void* aPtr)
-{
-  auto* data = static_cast<WindowDestroyedEnumeratorData*>(aPtr);
-  MOZ_ASSERT(data);
-
-  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aAgent->Window());
-  if (window && !window->IsInnerWindow()) {
-    window = window->GetCurrentInnerWindow();
-  }
-
-  if (!window || window->WindowID() != data->mInnerID) {
-    return PL_DHASH_NEXT;
-  }
-
-  AudioChannelService* service = AudioChannelService::GetAudioChannelService();
-  MOZ_ASSERT(service);
-
-  service->UnregisterType(aData->mChannel, aData->mElementHidden,
-                          CONTENT_PROCESS_ID_MAIN, aData->mWithVideo);
-  data->mAgents.AppendElement(aAgent);
-
-  return PL_DHASH_REMOVE;
-}
-
 NS_IMETHODIMP
-AudioChannelService::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData)
+AudioChannelService::Observe(nsISupports* aSubject, const char* aTopic,
+                             const char16_t* aData)
 {
   if (!strcmp(aTopic, "xpcom-shutdown")) {
     mDisabled = true;
-  }
-
-  if (!strcmp(aTopic, "ipc:content-shutdown")) {
-    nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject);
-    if (!props) {
-      NS_WARNING("ipc:content-shutdown message without property bag as subject");
-      return NS_OK;
-    }
-
-    int32_t index;
-    uint64_t childID = 0;
-    nsresult rv = props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"),
-                                             &childID);
-    if (NS_SUCCEEDED(rv)) {
-      for (int32_t type = AUDIO_CHANNEL_INT_NORMAL;
-           type < AUDIO_CHANNEL_INT_LAST;
-           ++type) {
-
-        while ((index = mChannelCounters[type].IndexOf(childID)) != -1) {
-          mChannelCounters[type].RemoveElementAt(index);
-        }
-      }
-
-      // No hidden content channel is playable if the original playable hidden
-      // process shuts down.
-      if (mPlayableHiddenContentChildID == childID) {
-        mPlayableHiddenContentChildID = CONTENT_PROCESS_ID_UNKNOWN;
-      }
-
-      while ((index = mWithVideoChildIDs.IndexOf(childID)) != -1) {
-        mWithVideoChildIDs.RemoveElementAt(index);
-      }
-
-      // We don't have to remove the agents from the mAgents hashtable because if
-      // that table contains only agents running on the same process.
-
-      SendAudioChannelChangedNotification(childID);
-      SendNotification();
-
-      if (mDefChannelChildID == childID) {
-        SetDefaultVolumeControlChannelInternal(-1, false, childID);
-        mDefChannelChildID = CONTENT_PROCESS_ID_UNKNOWN;
-      }
-    } else {
-      NS_WARNING("ipc:content-shutdown message without childID property");
-    }
+    mWindows.Clear();
   }
 
 #ifdef MOZ_WIDGET_GONK
   // To process the volume control on each audio channel according to
   // change of settings
   else if (!strcmp(aTopic, "mozsettings-changed")) {
     RootedDictionary<SettingChangeNotification> setting(nsContentUtils::RootingCxForThread());
     if (!WrappedJSToDictionary(aSubject, setting)) {
@@ -915,153 +513,97 @@ AudioChannelService::Observe(nsISupports
     NS_ENSURE_TRUE(wrapper, NS_ERROR_FAILURE);
 
     uint64_t innerID;
     nsresult rv = wrapper->GetData(&innerID);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
-    WindowDestroyedEnumeratorData data(innerID);
-    mAgents.Enumerate(WindowDestroyedEnumerator, &data);
-    for (uint32_t i = 0, len = data.mAgents.Length(); i < len; ++i) {
-      data.mAgents[i]->NotifyAudioChannelStateChanged();
+    nsAutoPtr<AudioChannelWindow> winData;
+    {
+      nsTObserverArray<nsAutoPtr<AudioChannelWindow>>::ForwardIterator
+        iter(mWindows);
+      while (iter.HasMore()) {
+        nsAutoPtr<AudioChannelWindow>& next = iter.GetNext();
+        if (next->mWindowID == innerID) {
+          uint32_t pos = mWindows.IndexOf(next);
+          winData = next.forget();
+          mWindows.RemoveElementAt(pos);
+          break;
+        }
+      }
+    }
+
+    if (winData) {
+      nsTObserverArray<AudioChannelAgent*>::ForwardIterator
+        iter(winData->mAgents);
+      while (iter.HasMore()) {
+        iter.GetNext()->WindowVolumeChanged();
+      }
     }
 
 #ifdef MOZ_WIDGET_GONK
     bool active = AnyAudioChannelIsActive();
     for (uint32_t i = 0; i < mSpeakerManager.Length(); i++) {
       mSpeakerManager[i]->SetAudioChannelActive(active);
     }
 #endif
   }
 
-  return NS_OK;
-}
-
-AudioChannelService::AudioChannelInternalType
-AudioChannelService::GetInternalType(AudioChannel aChannel,
-                                     bool aElementHidden)
-{
-  switch (aChannel) {
-    case AudioChannel::Normal:
-      return aElementHidden
-               ? AUDIO_CHANNEL_INT_NORMAL_HIDDEN
-               : AUDIO_CHANNEL_INT_NORMAL;
-
-    case AudioChannel::Content:
-      return aElementHidden
-               ? AUDIO_CHANNEL_INT_CONTENT_HIDDEN
-               : AUDIO_CHANNEL_INT_CONTENT;
-
-    case AudioChannel::Notification:
-      return aElementHidden
-               ? AUDIO_CHANNEL_INT_NOTIFICATION_HIDDEN
-               : AUDIO_CHANNEL_INT_NOTIFICATION;
+  else if (!strcmp(aTopic, "ipc:content-shutdown")) {
+    nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject);
+    if (!props) {
+      NS_WARNING("ipc:content-shutdown message without property bag as subject");
+      return NS_OK;
+    }
 
-    case AudioChannel::Alarm:
-      return aElementHidden
-               ? AUDIO_CHANNEL_INT_ALARM_HIDDEN
-               : AUDIO_CHANNEL_INT_ALARM;
-
-    case AudioChannel::Telephony:
-      return aElementHidden
-               ? AUDIO_CHANNEL_INT_TELEPHONY_HIDDEN
-               : AUDIO_CHANNEL_INT_TELEPHONY;
+    uint64_t childID = 0;
+    nsresult rv = props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"),
+                                             &childID);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
 
-    case AudioChannel::Ringer:
-      return aElementHidden
-               ? AUDIO_CHANNEL_INT_RINGER_HIDDEN
-               : AUDIO_CHANNEL_INT_RINGER;
+    if (mDefChannelChildID == childID) {
+      SetDefaultVolumeControlChannelInternal(-1, false, childID);
+      mDefChannelChildID = CONTENT_PROCESS_ID_UNKNOWN;
+    }
 
-    case AudioChannel::Publicnotification:
-      return aElementHidden
-               ? AUDIO_CHANNEL_INT_PUBLICNOTIFICATION_HIDDEN
-               : AUDIO_CHANNEL_INT_PUBLICNOTIFICATION;
-
-    default:
-      break;
+    RemoveChildStatus(childID);
   }
 
-  MOZ_CRASH("unexpected audio channel");
+  return NS_OK;
 }
 
 struct RefreshAgentsVolumeData
 {
   explicit RefreshAgentsVolumeData(nsPIDOMWindow* aWindow)
     : mWindow(aWindow)
   {}
 
   nsPIDOMWindow* mWindow;
   nsTArray<nsRefPtr<AudioChannelAgent>> mAgents;
 };
 
-PLDHashOperator
-AudioChannelService::RefreshAgentsVolumeEnumerator(AudioChannelAgent* aAgent,
-                                                   AudioChannelAgentData* aUnused,
-                                                   void* aPtr)
-{
-  MOZ_ASSERT(aAgent);
-  RefreshAgentsVolumeData* data = static_cast<RefreshAgentsVolumeData*>(aPtr);
-  MOZ_ASSERT(data);
-
-  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aAgent->Window());
-  if (window && !window->IsInnerWindow()) {
-    window = window->GetCurrentInnerWindow();
-  }
-
-  if (window == data->mWindow) {
-    data->mAgents.AppendElement(aAgent);
-  }
-
-  return PL_DHASH_NEXT;
-}
 void
 AudioChannelService::RefreshAgentsVolume(nsPIDOMWindow* aWindow)
 {
-  RefreshAgentsVolumeData data(aWindow);
-  mAgents.EnumerateRead(RefreshAgentsVolumeEnumerator, &data);
-
-  for (uint32_t i = 0; i < data.mAgents.Length(); ++i) {
-    data.mAgents[i]->WindowVolumeChanged();
-  }
-}
-
-struct CountWindowData
-{
-  explicit CountWindowData(nsIDOMWindow* aWindow)
-    : mWindow(aWindow)
-    , mCount(0)
-  {}
+  MOZ_ASSERT(aWindow);
 
-  nsIDOMWindow* mWindow;
-  uint32_t mCount;
-};
-
-PLDHashOperator
-AudioChannelService::CountWindowEnumerator(AudioChannelAgent* aAgent,
-                                           AudioChannelAgentData* aUnused,
-                                           void* aPtr)
-{
-  CountWindowData* data = static_cast<CountWindowData*>(aPtr);
-  MOZ_ASSERT(aAgent);
-
-  if (aAgent->Window() == data->mWindow) {
-    ++data->mCount;
+  AudioChannelWindow* winData = GetWindowData(aWindow->WindowID());
+  if (!winData) {
+    return;
   }
 
-  return PL_DHASH_NEXT;
-}
-
-uint32_t
-AudioChannelService::CountWindow(nsIDOMWindow* aWindow)
-{
-  CountWindowData data(aWindow);
-  mAgents.EnumerateRead(CountWindowEnumerator, &data);
-  return data.mCount;
+  nsTObserverArray<AudioChannelAgent*>::ForwardIterator
+    iter(winData->mAgents);
+  while (iter.HasMore()) {
+    iter.GetNext()->WindowVolumeChanged();
+  }
 }
 
 /* static */ const nsAttrValue::EnumTable*
 AudioChannelService::GetAudioChannelTable()
 {
   return kMozAudioChannelAttributeTable;
 }
 
@@ -1075,17 +617,17 @@ AudioChannelService::GetAudioChannel(con
   }
 
   return AudioChannel::Normal;
 }
 
 /* static */ AudioChannel
 AudioChannelService::GetDefaultAudioChannel()
 {
-  nsString audioChannel = Preferences::GetString("media.defaultAudioChannel");
+  nsAutoString audioChannel(Preferences::GetString("media.defaultAudioChannel"));
   if (audioChannel.IsEmpty()) {
     return AudioChannel::Normal;
   }
 
   for (uint32_t i = 0; kMozAudioChannelAttributeTable[i].tag; ++i) {
     if (audioChannel.EqualsASCII(kMozAudioChannelAttributeTable[i].tag)) {
       return static_cast<AudioChannel>(kMozAudioChannelAttributeTable[i].value);
     }
@@ -1109,54 +651,277 @@ AudioChannelService::GetAudioChannelStri
   }
 }
 
 /* static */ void
 AudioChannelService::GetDefaultAudioChannelString(nsAString& aString)
 {
   aString.AssignASCII("normal");
 
-  nsString audioChannel = Preferences::GetString("media.defaultAudioChannel");
+  nsAutoString audioChannel(Preferences::GetString("media.defaultAudioChannel"));
   if (!audioChannel.IsEmpty()) {
     for (uint32_t i = 0; kMozAudioChannelAttributeTable[i].tag; ++i) {
       if (audioChannel.EqualsASCII(kMozAudioChannelAttributeTable[i].tag)) {
         aString = audioChannel;
         break;
       }
     }
   }
 }
 
-void
-AudioChannelService::RegisterTelephonyChild(uint64_t aChildID)
+AudioChannelService::AudioChannelWindow*
+AudioChannelService::GetOrCreateWindowData(nsPIDOMWindow* aWindow)
 {
-  for (uint32_t i = 0, len = mTelephonyChildren.Length(); i < len; ++i) {
-    if (mTelephonyChildren[i].mChildID == aChildID) {
-      ++mTelephonyChildren[i].mInstances;
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aWindow);
+  MOZ_ASSERT(aWindow->IsOuterWindow());
+
+  AudioChannelWindow* winData = GetWindowData(aWindow->WindowID());
+  if (!winData) {
+    winData = new AudioChannelWindow(aWindow->WindowID());
+    mWindows.AppendElement(winData);
+  }
 
-      if (i != len - 1) {
-        TelephonyChild child = mTelephonyChildren[i];
-        mTelephonyChildren.RemoveElementAt(i);
-        mTelephonyChildren.AppendElement(child);
-      }
+  return winData;
+}
 
-      return;
+AudioChannelService::AudioChannelWindow*
+AudioChannelService::GetWindowData(uint64_t aWindowID) const
+{
+  nsTObserverArray<nsAutoPtr<AudioChannelWindow>>::ForwardIterator
+    iter(mWindows);
+  while (iter.HasMore()) {
+    AudioChannelWindow* next = iter.GetNext();
+    if (next->mWindowID == aWindowID) {
+      return next;
     }
   }
 
-  mTelephonyChildren.AppendElement(TelephonyChild(aChildID));
+  return nullptr;
+}
+
+float
+AudioChannelService::GetAudioChannelVolume(nsPIDOMWindow* aWindow,
+                                           AudioChannel aAudioChannel)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aWindow);
+  MOZ_ASSERT(aWindow->IsOuterWindow());
+
+  AudioChannelWindow* winData = GetOrCreateWindowData(aWindow);
+  return winData->mChannels[(uint32_t)aAudioChannel].mVolume;
+}
+
+NS_IMETHODIMP
+AudioChannelService::GetAudioChannelVolume(nsIDOMWindow* aWindow,
+                                           unsigned short aAudioChannel,
+                                           float* aVolume)
+{
+  nsCOMPtr<nsPIDOMWindow> window = GetTopWindow(aWindow);
+  *aVolume = GetAudioChannelVolume(window, (AudioChannel)aAudioChannel);
+  return NS_OK;
+}
+
+void
+AudioChannelService::SetAudioChannelVolume(nsPIDOMWindow* aWindow,
+                                           AudioChannel aAudioChannel,
+                                           float aVolume)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aWindow);
+  MOZ_ASSERT(aWindow->IsOuterWindow());
+
+  AudioChannelWindow* winData = GetOrCreateWindowData(aWindow);
+  winData->mChannels[(uint32_t)aAudioChannel].mVolume = aVolume;
+  RefreshAgentsVolume(aWindow);
+}
+
+NS_IMETHODIMP
+AudioChannelService::SetAudioChannelVolume(nsIDOMWindow* aWindow,
+                                           unsigned short aAudioChannel,
+                                           float aVolume)
+{
+  nsCOMPtr<nsPIDOMWindow> window = GetTopWindow(aWindow);
+  SetAudioChannelVolume(window, (AudioChannel)aAudioChannel, aVolume);
+  return NS_OK;
+}
+
+bool
+AudioChannelService::GetAudioChannelMuted(nsPIDOMWindow* aWindow,
+                                          AudioChannel aAudioChannel)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aWindow);
+  MOZ_ASSERT(aWindow->IsOuterWindow());
+
+  AudioChannelWindow* winData = GetOrCreateWindowData(aWindow);
+  return winData->mChannels[(uint32_t)aAudioChannel].mMuted;
+}
+
+NS_IMETHODIMP
+AudioChannelService::GetAudioChannelMuted(nsIDOMWindow* aWindow,
+                                          unsigned short aAudioChannel,
+                                          bool* aMuted)
+{
+  nsCOMPtr<nsPIDOMWindow> window = GetTopWindow(aWindow);
+  *aMuted = GetAudioChannelMuted(window, (AudioChannel)aAudioChannel);
+  return NS_OK;
 }
 
 void
-AudioChannelService::UnregisterTelephonyChild(uint64_t aChildID)
+AudioChannelService::SetAudioChannelMuted(nsPIDOMWindow* aWindow,
+                                          AudioChannel aAudioChannel,
+                                          bool aMuted)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aWindow);
+  MOZ_ASSERT(aWindow->IsOuterWindow());
+
+  AudioChannelWindow* winData = GetOrCreateWindowData(aWindow);
+  winData->mChannels[(uint32_t)aAudioChannel].mMuted = aMuted;
+  RefreshAgentsVolume(aWindow);
+}
+
+NS_IMETHODIMP
+AudioChannelService::SetAudioChannelMuted(nsIDOMWindow* aWindow,
+                                          unsigned short aAudioChannel,
+                                          bool aMuted)
+{
+  nsCOMPtr<nsPIDOMWindow> window = GetTopWindow(aWindow);
+  SetAudioChannelMuted(window, (AudioChannel)aAudioChannel, aMuted);
+  return NS_OK;
+}
+
+bool
+AudioChannelService::IsAudioChannelActive(nsPIDOMWindow* aWindow,
+                                          AudioChannel aAudioChannel)
 {
-  for (uint32_t i = 0, len = mTelephonyChildren.Length(); i < len; ++i) {
-    if (mTelephonyChildren[i].mChildID == aChildID) {
-      if (!--mTelephonyChildren[i].mInstances) {
-        mTelephonyChildren.RemoveElementAt(i);
-      }
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aWindow);
+  MOZ_ASSERT(aWindow->IsOuterWindow());
+
+  AudioChannelWindow* winData = GetOrCreateWindowData(aWindow);
+  return !!winData->mChannels[(uint32_t)aAudioChannel].mNumberOfAgents;
+}
 
-      return;
+NS_IMETHODIMP
+AudioChannelService::IsAudioChannelActive(nsIDOMWindow* aWindow,
+                                          unsigned short aAudioChannel,
+                                          bool* aActive)
+{
+  nsCOMPtr<nsPIDOMWindow> window = GetTopWindow(aWindow);
+  *aActive = IsAudioChannelActive(window, (AudioChannel)aAudioChannel);
+  return NS_OK;
+}
+void
+AudioChannelService::SetDefaultVolumeControlChannel(int32_t aChannel,
+                                                    bool aVisible)
+{
+  SetDefaultVolumeControlChannelInternal(aChannel, aVisible,
+                                         CONTENT_PROCESS_ID_MAIN);
+}
+
+void
+AudioChannelService::SetDefaultVolumeControlChannelInternal(int32_t aChannel,
+                                                            bool aVisible,
+                                                            uint64_t aChildID)
+{
+  if (!IsParentProcess()) {
+    ContentChild* cc = ContentChild::GetSingleton();
+    if (cc) {
+      cc->SendAudioChannelChangeDefVolChannel(aChannel, aVisible);
     }
+
+    return;
+  }
+
+  // If this child is in the background and mDefChannelChildID is set to
+  // others then it means other child in the foreground already set it's
+  // own default channel.
+  if (!aVisible && mDefChannelChildID != aChildID) {
+    return;
   }
 
-  MOZ_ASSERT(false, "This should not happen.");
+  // Workaround for the call screen app. The call screen app is running on the
+  // main process, that will results in wrong visible state. Because we use the
+  // docshell's active state as visible state, the main process is always
+  // active. Therefore, we will see the strange situation that the visible
+  // state of the call screen is always true. If the mDefChannelChildID is set
+  // to others then it means other child in the foreground already set it's
+  // own default channel already.
+  // Summary :
+  //   Child process : foreground app always can set type.
+  //   Parent process : check the mDefChannelChildID.
+  else if (aChildID == CONTENT_PROCESS_ID_MAIN &&
+           mDefChannelChildID != CONTENT_PROCESS_ID_UNKNOWN) {
+    return;
+  }
+
+  mDefChannelChildID = aVisible ? aChildID : CONTENT_PROCESS_ID_UNKNOWN;
+  nsAutoString channelName;
+
+  if (aChannel == -1) {
+    channelName.AssignASCII("unknown");
+  } else {
+    GetAudioChannelString(static_cast<AudioChannel>(aChannel), channelName);
+  }
+
+  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+  if (obs) {
+    obs->NotifyObservers(nullptr, "default-volume-channel-changed",
+                         channelName.get());
+  }
 }
+
+void
+AudioChannelService::MaybeSendStatusUpdate()
+{
+  if (IsParentProcess()) {
+    return;
+  }
+
+  bool telephonyChannel = TelephonyChannelIsActive();
+  bool contentOrNormalChannel = ContentOrNormalChannelIsActive();
+  bool anyChannel = AnyAudioChannelIsActive();
+
+  if (telephonyChannel == mTelephonyChannel &&
+      contentOrNormalChannel == mContentOrNormalChannel &&
+      anyChannel == mAnyChannel) {
+    return;
+  }
+
+  mTelephonyChannel = telephonyChannel;
+  mContentOrNormalChannel = contentOrNormalChannel;
+  mAnyChannel = anyChannel;
+
+  ContentChild* cc = ContentChild::GetSingleton();
+  if (cc) {
+    cc->SendAudioChannelServiceStatus(telephonyChannel, contentOrNormalChannel,
+                                      anyChannel);
+  }
+}
+
+void
+AudioChannelService::ChildStatusReceived(uint64_t aChildID,
+                                         bool aTelephonyChannel,
+                                         bool aContentOrNormalChannel,
+                                         bool aAnyChannel)
+{
+  if (!aAnyChannel) {
+    RemoveChildStatus(aChildID);
+    return;
+  }
+
+  AudioChannelChildStatus* data = GetChildStatus(aChildID);
+  if (!data) {
+    data = new AudioChannelChildStatus(aChildID);
+    mPlayingChildren.AppendElement(data);
+  }
+
+  data->mActiveTelephonyChannel = aTelephonyChannel;
+  data->mActiveContentOrNormalChannel = aContentOrNormalChannel;
+}
+
+/* static */ bool
+AudioChannelService::IsAudioChannelMutedByDefault()
+{
+  return sAudioChannelMutedByDefault;
+}
--- a/dom/audiochannel/AudioChannelService.h
+++ b/dom/audiochannel/AudioChannelService.h
@@ -2,100 +2,102 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_audiochannelservice_h__
 #define mozilla_dom_audiochannelservice_h__
 
+#include "nsIAudioChannelService.h"
 #include "nsAutoPtr.h"
 #include "nsIObserver.h"
+#include "nsTObserverArray.h"
 #include "nsTArray.h"
-#include "nsITimer.h"
 
-#include "AudioChannelCommon.h"
 #include "AudioChannelAgent.h"
 #include "nsAttrValue.h"
-#include "nsClassHashtable.h"
 #include "mozilla/dom/AudioChannelBinding.h"
 
 class nsIRunnable;
 class nsPIDOMWindow;
 
 namespace mozilla {
 namespace dom {
 #ifdef MOZ_WIDGET_GONK
 class SpeakerManagerService;
 #endif
-class AudioChannelService
-: public nsIObserver
-, public nsITimerCallback
+
+#define NUMBER_OF_AUDIO_CHANNELS (uint32_t)AudioChannel::Publicnotification + 1
+
+class AudioChannelService final : public nsIAudioChannelService
+                                , public nsIObserver
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
-  NS_DECL_NSITIMERCALLBACK
-
-  /**
-   * Returns the AudioChannelServce singleton or null if the process havn't create it before.
-   * Only to be called from main thread.
-   */
-  static AudioChannelService* GetAudioChannelService();
+  NS_DECL_NSIAUDIOCHANNELSERVICE
 
   /**
    * Returns the AudioChannelServce singleton.
    * If AudioChannelServce is not exist, create and return new one.
    * Only to be called from main thread.
    */
-  static AudioChannelService* GetOrCreateAudioChannelService();
+  static already_AddRefed<AudioChannelService> GetOrCreate();
 
   /**
    * Shutdown the singleton.
    */
   static void Shutdown();
 
+  static bool IsAudioChannelMutedByDefault();
+
   /**
    * Any audio channel agent that starts playing should register itself to
    * this service, sharing the AudioChannel.
    */
-  virtual void RegisterAudioChannelAgent(AudioChannelAgent* aAgent,
-                                         AudioChannel aChannel,
-                                         bool aWithVideo);
+  void RegisterAudioChannelAgent(AudioChannelAgent* aAgent, AudioChannel aChannel);
 
   /**
    * Any audio channel agent that stops playing should unregister itself to
    * this service.
    */
-  virtual void UnregisterAudioChannelAgent(AudioChannelAgent* aAgent);
+  void UnregisterAudioChannelAgent(AudioChannelAgent* aAgent);
 
   /**
-   * Return the state to indicate this agent should keep playing/
-   * fading volume/muted.
+   * Return the state to indicate this audioChannel for his window should keep
+   * playing/muted.
    */
-  virtual AudioChannelState GetState(AudioChannelAgent* aAgent,
-                                     bool aElementHidden);
+  void GetState(nsPIDOMWindow* aWindow, uint32_t aChannel,
+                float* aVolume, bool* aMuted);
+
+  /* Methods for the BrowserElementAudioChannel */
+  float GetAudioChannelVolume(nsPIDOMWindow* aWindow, AudioChannel aChannel);
 
-  /**
-   * Return true if there is a content channel active in this process
-   * or one of its subprocesses.
-   */
-  virtual bool ContentOrNormalChannelIsActive();
+  void SetAudioChannelVolume(nsPIDOMWindow* aWindow, AudioChannel aChannel,
+                             float aVolume);
+
+  bool GetAudioChannelMuted(nsPIDOMWindow* aWindow, AudioChannel aChannel);
+
+  void SetAudioChannelMuted(nsPIDOMWindow* aWindow, AudioChannel aChannel,
+                            bool aMuted);
+
+  bool IsAudioChannelActive(nsPIDOMWindow* aWindow, AudioChannel aChannel);
 
   /**
    * Return true if there is a telephony channel active in this process
    * or one of its subprocesses.
    */
-  virtual bool TelephonyChannelIsActive();
+  bool TelephonyChannelIsActive();
 
   /**
    * Return true if a normal or content channel is active for the given
    * process ID.
    */
-  virtual bool ProcessContentOrNormalChannelIsActive(uint64_t aChildID);
+  bool ProcessContentOrNormalChannelIsActive(uint64_t aChildID);
 
   /***
    * AudioChannelManager calls this function to notify the default channel used
    * to adjust volume when there is no any active channel. if aChannel is -1,
    * the default audio channel will be used. Otherwise aChannel is casted to
    * AudioChannel enum.
    */
   virtual void SetDefaultVolumeControlChannel(int32_t aChannel,
@@ -120,172 +122,104 @@ public:
 #endif
 
   static const nsAttrValue::EnumTable* GetAudioChannelTable();
   static AudioChannel GetAudioChannel(const nsAString& aString);
   static AudioChannel GetDefaultAudioChannel();
   static void GetAudioChannelString(AudioChannel aChannel, nsAString& aString);
   static void GetDefaultAudioChannelString(nsAString& aString);
 
-  void Notify();
-
-protected:
-  void SendNotification();
+  void Notify(uint64_t aWindowID);
 
-  /**
-   * Send the audio-channel-changed notification for the given process ID if
-   * needed.
-   */
-  void SendAudioChannelChangedNotification(uint64_t aChildID);
+  void ChildStatusReceived(uint64_t aChildID, bool aTelephonyChannel,
+                           bool aContentOrNormalChannel, bool aAnyChannel);
 
-  /* Register/Unregister IPC types: */
-  void RegisterType(AudioChannel aChannel, uint64_t aChildID, bool aWithVideo);
-  void UnregisterType(AudioChannel aChannel, bool aElementHidden,
-                      uint64_t aChildID, bool aWithVideo);
-  void UnregisterTypeInternal(AudioChannel aChannel, bool aElementHidden,
-                              uint64_t aChildID, bool aWithVideo);
+private:
+  AudioChannelService();
+  ~AudioChannelService();
 
-  AudioChannelState GetStateInternal(AudioChannel aChannel, uint64_t aChildID,
-                                     bool aElementHidden,
-                                     bool aElementWasHidden);
+  void MaybeSendStatusUpdate();
 
-  /* Update the internal type value following the visibility changes */
-  void UpdateChannelType(AudioChannel aChannel, uint64_t aChildID,
-                         bool aElementHidden, bool aElementWasHidden);
+  bool ContentOrNormalChannelIsActive();
 
   /* Send the default-volume-channel-changed notification */
   void SetDefaultVolumeControlChannelInternal(int32_t aChannel,
                                               bool aVisible, uint64_t aChildID);
 
-  AudioChannelState CheckTelephonyPolicy(AudioChannel aChannel,
-                                         uint64_t aChildID);
-  void RegisterTelephonyChild(uint64_t aChildID);
-  void UnregisterTelephonyChild(uint64_t aChildID);
+  struct AudioChannelConfig final
+  {
+    AudioChannelConfig()
+      : mVolume(1.0)
+      , mMuted(IsAudioChannelMutedByDefault())
+      , mNumberOfAgents(0)
+    {}
 
-  AudioChannelService();
-  virtual ~AudioChannelService();
+    float mVolume;
+    bool mMuted;
 
-  enum AudioChannelInternalType {
-    AUDIO_CHANNEL_INT_NORMAL = 0,
-    AUDIO_CHANNEL_INT_NORMAL_HIDDEN,
-    AUDIO_CHANNEL_INT_CONTENT,
-    AUDIO_CHANNEL_INT_CONTENT_HIDDEN,
-    AUDIO_CHANNEL_INT_NOTIFICATION,
-    AUDIO_CHANNEL_INT_NOTIFICATION_HIDDEN,
-    AUDIO_CHANNEL_INT_ALARM,
-    AUDIO_CHANNEL_INT_ALARM_HIDDEN,
-    AUDIO_CHANNEL_INT_TELEPHONY,
-    AUDIO_CHANNEL_INT_TELEPHONY_HIDDEN,
-    AUDIO_CHANNEL_INT_RINGER,
-    AUDIO_CHANNEL_INT_RINGER_HIDDEN,
-    AUDIO_CHANNEL_INT_PUBLICNOTIFICATION,
-    AUDIO_CHANNEL_INT_PUBLICNOTIFICATION_HIDDEN,
-    AUDIO_CHANNEL_INT_LAST
+    uint32_t mNumberOfAgents;
+  };
+
+  struct AudioChannelWindow final
+  {
+    explicit AudioChannelWindow(uint64_t aWindowID)
+      : mWindowID(aWindowID)
+    {}
+
+    uint64_t mWindowID;
+    AudioChannelConfig mChannels[NUMBER_OF_AUDIO_CHANNELS];
+
+    // Raw pointer because the AudioChannelAgent must unregister itself.
+    nsTObserverArray<AudioChannelAgent*> mAgents;
   };
 
-  bool ChannelsActiveWithHigherPriorityThan(AudioChannelInternalType aType);
+  AudioChannelWindow*
+  GetOrCreateWindowData(nsPIDOMWindow* aWindow);
 
-  bool CheckVolumeFadedCondition(AudioChannelInternalType aType,
-                                 bool aElementHidden);
-
-  AudioChannelInternalType GetInternalType(AudioChannel aChannel,
-                                           bool aElementHidden);
+  AudioChannelWindow*
+  GetWindowData(uint64_t aWindowID) const;
 
-  class AudioChannelAgentData {
-  public:
-    AudioChannelAgentData(AudioChannel aChannel,
-                          bool aElementHidden,
-                          AudioChannelState aState,
-                          bool aWithVideo)
-    : mChannel(aChannel)
-    , mElementHidden(aElementHidden)
-    , mState(aState)
-    , mWithVideo(aWithVideo)
+  struct AudioChannelChildStatus final
+  {
+    explicit AudioChannelChildStatus(uint64_t aChildID)
+      : mChildID(aChildID)
+      , mActiveTelephonyChannel(false)
+      , mActiveContentOrNormalChannel(false)
     {}
 
-    AudioChannel mChannel;
-    bool mElementHidden;
-    AudioChannelState mState;
-    const bool mWithVideo;
+    uint64_t mChildID;
+    bool mActiveTelephonyChannel;
+    bool mActiveContentOrNormalChannel;
   };
 
-  static PLDHashOperator
-  NotifyEnumerator(AudioChannelAgent* aAgent,
-                   AudioChannelAgentData* aData, void *aUnused);
-
-  static PLDHashOperator
-  RefreshAgentsVolumeEnumerator(AudioChannelAgent* aAgent,
-                                AudioChannelAgentData* aUnused,
-                                void *aPtr);
+  AudioChannelChildStatus*
+  GetChildStatus(uint64_t aChildID) const;
 
-  static PLDHashOperator
-  CountWindowEnumerator(AudioChannelAgent* aAgent,
-                        AudioChannelAgentData* aUnused,
-                        void *aPtr);
+  void
+  RemoveChildStatus(uint64_t aChildID);
 
-  static PLDHashOperator
-  WindowDestroyedEnumerator(AudioChannelAgent* aAgent,
-                            nsAutoPtr<AudioChannelAgentData>& aData,
-                            void *aPtr);
+  nsTObserverArray<nsAutoPtr<AudioChannelWindow>> mWindows;
 
-  // This returns the number of agents from this aWindow.
-  uint32_t CountWindow(nsIDOMWindow* aWindow);
+  nsTObserverArray<nsAutoPtr<AudioChannelChildStatus>> mPlayingChildren;
 
-  nsClassHashtable< nsPtrHashKey<AudioChannelAgent>, AudioChannelAgentData > mAgents;
 #ifdef MOZ_WIDGET_GONK
   nsTArray<SpeakerManagerService*>  mSpeakerManager;
 #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).
-  // A background process contained content channel(s) will become non-playable:
-  //   1. When there is a foreground process registering its content channel(s)
-  //   in AudioChannelService.
-  //   ps. Currently this condition is never satisfied because the default value
-  //   of visibility status of each channel during registering is hidden = true.
-  //   2. When there is a process with registered content channel(s) goes from
-  //   background into foreground.
-  //   3. When this process unregisters all hidden content channels.
-  //   4. When this process shuts down.
-  uint64_t mPlayableHiddenContentChildID;
 
   bool mDisabled;
 
   nsCOMPtr<nsIRunnable> mRunnable;
 
-  nsCOMPtr<nsITimer> mDeferTelChannelTimer;
-  bool mTimerElementHidden;
-  uint64_t mTimerChildID;
+  uint64_t mDefChannelChildID;
 
-  uint64_t mDefChannelChildID;
+  // These boolean are used to know if we have to send an status update to the
+  // service running in the main process.
+  bool mTelephonyChannel;
+  bool mContentOrNormalChannel;
+  bool mAnyChannel;
 
   // This is needed for IPC comunication between
   // AudioChannelServiceChild and this class.
   friend class ContentParent;
   friend class ContentChild;
 };
 
 } // namespace dom
deleted file mode 100644
--- a/dom/audiochannel/AudioChannelServiceChild.cpp
+++ /dev/null
@@ -1,168 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "AudioChannelServiceChild.h"
-
-#include "base/basictypes.h"
-
-#include "mozilla/Services.h"
-#include "mozilla/StaticPtr.h"
-#include "mozilla/unused.h"
-#include "mozilla/dom/ContentChild.h"
-#include "mozilla/dom/ContentParent.h"
-#include "nsIObserverService.h"
-#include "nsThreadUtils.h"
-
-#ifdef MOZ_WIDGET_GONK
-#include "SpeakerManagerService.h"
-#endif
-
-using namespace mozilla;
-using namespace mozilla::dom;
-using namespace mozilla::hal;
-
-StaticRefPtr<AudioChannelServiceChild> gAudioChannelServiceChild;
-
-// static
-AudioChannelService*
-AudioChannelServiceChild::GetAudioChannelService()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  return gAudioChannelServiceChild;
-
-}
-
-// static
-AudioChannelService*
-AudioChannelServiceChild::GetOrCreateAudioChannelService()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  // If we already exist, exit early
-  if (gAudioChannelServiceChild) {
-    return gAudioChannelServiceChild;
-  }
-
-  // Create new instance, register, return
-  nsRefPtr<AudioChannelServiceChild> service = new AudioChannelServiceChild();
-  MOZ_ASSERT(service);
-
-  gAudioChannelServiceChild = service;
-  return gAudioChannelServiceChild;
-}
-
-void
-AudioChannelServiceChild::Shutdown()
-{
-  if (gAudioChannelServiceChild) {
-    gAudioChannelServiceChild = nullptr;
-  }
-}
-
-AudioChannelServiceChild::AudioChannelServiceChild()
-{
-}
-
-AudioChannelServiceChild::~AudioChannelServiceChild()
-{
-}
-
-AudioChannelState
-AudioChannelServiceChild::GetState(AudioChannelAgent* aAgent, bool aElementHidden)
-{
-  AudioChannelAgentData* data;
-  if (!mAgents.Get(aAgent, &data)) {
-    return AUDIO_CHANNEL_STATE_MUTED;
-  }
-
-  AudioChannelState state = AUDIO_CHANNEL_STATE_MUTED;
-  bool oldElementHidden = data->mElementHidden;
-
-  UpdateChannelType(data->mChannel, CONTENT_PROCESS_ID_MAIN, aElementHidden,
-                    oldElementHidden);
-
-  // Update visibility.
-  data->mElementHidden = aElementHidden;
-
-  ContentChild* cc = ContentChild::GetSingleton();
-  cc->SendAudioChannelGetState(data->mChannel, aElementHidden, oldElementHidden,
-                               &state);
-  data->mState = state;
-  cc->SendAudioChannelChangedNotification();
-
-  #ifdef MOZ_WIDGET_GONK
-  /** Only modify the speaker status when
-   *  (1) apps in the foreground.
-   *  (2) apps in the backgrund and inactive.
-   *  Notice : modify only when the visible status is stable, because there
-   *  has lantency in passing the visibility events.
-   **/
-  bool active = AnyAudioChannelIsActive();
-  if (aElementHidden == oldElementHidden &&
-      (!aElementHidden || (aElementHidden && !active))) {
-    for (uint32_t i = 0; i < mSpeakerManager.Length(); i++) {
-      mSpeakerManager[i]->SetAudioChannelActive(active);
-    }
-  }
-  #endif
-
-  return state;
-}
-
-void
-AudioChannelServiceChild::RegisterAudioChannelAgent(AudioChannelAgent* aAgent,
-                                                    AudioChannel aChannel,
-                                                    bool aWithVideo)
-{
-  AudioChannelService::RegisterAudioChannelAgent(aAgent, aChannel, aWithVideo);
-
-  ContentChild::GetSingleton()->SendAudioChannelRegisterType(aChannel, aWithVideo);
-
-  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
-  if (obs) {
-    obs->NotifyObservers(nullptr, "audio-channel-agent-changed", nullptr);
-  }
-}
-
-void
-AudioChannelServiceChild::UnregisterAudioChannelAgent(AudioChannelAgent* aAgent)
-{
-  AudioChannelAgentData *pData;
-  if (!mAgents.Get(aAgent, &pData)) {
-    return;
-  }
-
-  // We need to keep a copy because unregister will remove the
-  // AudioChannelAgentData object from the hashtable.
-  AudioChannelAgentData data(*pData);
-
-  AudioChannelService::UnregisterAudioChannelAgent(aAgent);
-
-  ContentChild::GetSingleton()->SendAudioChannelUnregisterType(
-      data.mChannel, data.mElementHidden, data.mWithVideo);
-
-  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
-  if (obs) {
-    obs->NotifyObservers(nullptr, "audio-channel-agent-changed", nullptr);
-  }
-#ifdef MOZ_WIDGET_GONK
-  bool active = AnyAudioChannelIsActive();
-  for (uint32_t i = 0; i < mSpeakerManager.Length(); i++) {
-    mSpeakerManager[i]->SetAudioChannelActive(active);
-  }
-#endif
-}
-
-void
-AudioChannelServiceChild::SetDefaultVolumeControlChannel(int32_t aChannel,
-                                                         bool aHidden)
-{
-  ContentChild *cc = ContentChild::GetSingleton();
-  if (cc) {
-    cc->SendAudioChannelChangeDefVolChannel(aChannel, aHidden);
-  }
-}
deleted file mode 100644
--- a/dom/audiochannel/AudioChannelServiceChild.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef mozilla_dom_audiochannelservicechild_h__
-#define mozilla_dom_audiochannelservicechild_h__
-
-#include "nsAutoPtr.h"
-#include "nsISupports.h"
-
-#include "AudioChannelService.h"
-#include "AudioChannelCommon.h"
-
-namespace mozilla {
-namespace dom {
-
-class AudioChannelServiceChild : public AudioChannelService
-{
-public:
-
-  /**
-   * Returns the AudioChannelServce singleton or null if the process havn't create it before.
-   * Only to be called from main thread.
-   */
-  static AudioChannelService* GetAudioChannelService();
-
-  /**
-   * Returns the AudioChannelServce singleton.
-   * If AudioChannelServce is not exist, create and return new one.
-   * Only to be called from main thread.
-   */
-  static AudioChannelService* GetOrCreateAudioChannelService();
-
-  static void Shutdown();
-
-  virtual void RegisterAudioChannelAgent(AudioChannelAgent* aAgent,
-                                         AudioChannel aChannel,
-                                         bool aWithVideo);
-  virtual void UnregisterAudioChannelAgent(AudioChannelAgent* aAgent);
-
-  /**
-   * Return the state to indicate this agent should keep playing/
-   * fading volume/muted.
-   */
-  virtual AudioChannelState GetState(AudioChannelAgent* aAgent,
-                                     bool aElementHidden);
-
-  virtual void SetDefaultVolumeControlChannel(int32_t aChannel,
-                                              bool aHidden);
-
-protected:
-  AudioChannelServiceChild();
-  virtual ~AudioChannelServiceChild();
-};
-
-} // namespace dom
-} // namespace mozilla
-
-#endif
-
--- a/dom/audiochannel/moz.build
+++ b/dom/audiochannel/moz.build
@@ -1,32 +1,28 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-TEST_DIRS += ['tests']
-
 XPIDL_SOURCES += [
     'nsIAudioChannelAgent.idl',
+    'nsIAudioChannelService.idl',
 ]
 
 XPIDL_MODULE = 'dom_audiochannel'
 
 EXPORTS += [
     'AudioChannelAgent.h',
-    'AudioChannelCommon.h',
     'AudioChannelService.h',
-    'AudioChannelServiceChild.h',
 ]
 
 UNIFIED_SOURCES += [
     'AudioChannelAgent.cpp',
     'AudioChannelService.cpp',
-    'AudioChannelServiceChild.cpp',
 ]
 
 FAIL_ON_WARNINGS = True
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
--- a/dom/audiochannel/nsIAudioChannelAgent.idl
+++ b/dom/audiochannel/nsIAudioChannelAgent.idl
@@ -1,51 +1,40 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
 interface nsIDOMWindow;
 
-[uuid(194b55d9-39c0-45c6-b8ef-b8049f978ea5)]
+[uuid(4f537c88-3722-4946-9a09-ce559fa0591d)]
 interface nsIAudioChannelAgentCallback : nsISupports
 {
   /**
-   * Notified when the playable status of channel is changed.
-   *
-   * @param canPlay
-   *        Callback from agent to notify component of the playable status
-   *        of the channel. If canPlay is muted state, component SHOULD stop
-   *        playing media associated with this channel as soon as possible. if
-   *        it is faded state then the volume of media should be reduced.
-   */
-  void canPlayChanged(in long canPlay);
-
-  /**
    * Notified when the window volume/mute is changed
    */
-  void windowVolumeChanged();
+  void windowVolumeChanged(in float aVolume, in bool aMuted);
 };
 
 /**
  * This interface provides an agent for gecko components to participate
  * in the audio channel service. Gecko components are responsible for
  *   1. Indicating what channel type they are using (via the init() member
  *      function).
  *   2. Before playing, checking the playable status of the channel.
  *   3. Notifying the agent when they start/stop using this channel.
  *   4. Notifying the agent of changes to the visibility of the component using
  *      this channel.
  *
  * The agent will invoke a callback to notify Gecko components of
  *   1. Changes to the playable status of this channel.
  */
 
-[uuid(2b0222a5-8f7b-49d2-9ab8-cd01b744b23e)]
+[uuid(e28e1569-2a44-4f71-9cd0-216874b05d57)]
 interface nsIAudioChannelAgent : nsISupports
 {
   const long AUDIO_AGENT_CHANNEL_NORMAL             = 0;
   const long AUDIO_AGENT_CHANNEL_CONTENT            = 1;
   const long AUDIO_AGENT_CHANNEL_NOTIFICATION       = 2;
   const long AUDIO_AGENT_CHANNEL_ALARM              = 3;
   const long AUDIO_AGENT_CHANNEL_TELEPHONY          = 4;
   const long AUDIO_AGENT_CHANNEL_RINGER             = 5;
@@ -57,16 +46,23 @@ interface nsIAudioChannelAgent : nsISupp
   const long AUDIO_AGENT_STATE_MUTED                = 1;
   const long AUDIO_AGENT_STATE_FADED                = 2;
 
   /**
    * Before init() is called, this returns AUDIO_AGENT_CHANNEL_ERROR.
    */
   readonly attribute long audioChannelType;
 
+  %{C++
+  inline int32_t AudioChannelType() {
+    int32_t channel;
+    return NS_SUCCEEDED(GetAudioChannelType(&channel)) ? channel : AUDIO_AGENT_CHANNEL_ERROR;
+  }
+  %}
+
   /**
    * Initialize the agent with a channel type.
    * Note: This function should only be called once.
    *
    * @param window
    *    The window
    * @param channelType
    *    Audio Channel Type listed as above
@@ -87,56 +83,33 @@ interface nsIAudioChannelAgent : nsISupp
    *
    * In order for this to work, |callback| must implement
    * nsISupportsWeakReference.
    */
   void initWithWeakCallback(in nsIDOMWindow window, in long channelType,
                             in nsIAudioChannelAgentCallback callback);
 
   /**
-   * This method is just like init(), and specify the channel is associated
-   * with video.
-   *
-   * @param weak
-   *    true if weak reference should be hold.
-   */
-  void initWithVideo(in nsIDOMWindow window, in long channelType,
-                     in nsIAudioChannelAgentCallback callback, in boolean weak);
-
-  /**
    * Notify the agent that we want to start playing.
    * Note: Gecko component SHOULD call this function first then start to
    *          play audio stream only when return value is true.
    *
    *
    * @return
    *    normal state: the agent has registered with audio channel service and
    *          the component should start playback.
    *    muted state: the agent has registered with audio channel service but
    *          the component should not start playback.
    *    faded state: the agent has registered with audio channel service the
    *          component should start playback as well as reducing the volume.
    */
-  long startPlaying();
+  void notifyStartedPlaying(out float volume, out bool muted);
 
   /**
    * Notify the agent we no longer want to play.
    *
-   * Note : even if startPlaying() returned false, the agent would still be
-   *        registered with the audio channel service and receive callbacks for status changes.
-   *        So stopPlaying must still eventually be called to unregister the agent with the
-   *        channel service.
+   * Note : even if notifyStartedPlaying() returned false, the agent would
+   * still be registered with the audio channel service and receive callbacks
+   * for status changes. So notifyStoppedPlaying must still eventually be
+   * called to unregister the agent with the channel service.
    */
-  void stopPlaying();
-
-  /**
-   * Notify the agent of the visibility state of the window using this agent.
-   * @param visible
-   *    True if the window associated with the agent is visible.
-   */
-  void setVisibilityState(in boolean visible);
-
-  /**
-   * Retrieve the volume from the window.
-   */
-  readonly attribute float windowVolume;
+  void notifyStoppedPlaying();
 };
-
new file mode 100644
--- /dev/null
+++ b/dom/audiochannel/nsIAudioChannelService.idl
@@ -0,0 +1,29 @@
+/* -*- 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 "nsISupports.idl"
+
+interface nsIDOMWindow;
+
+[scriptable, builtinclass, uuid(323e5472-b8f4-4288-b1b9-53c7c54bbbe8)]
+interface nsIAudioChannelService : nsISupports
+{
+  float getAudioChannelVolume(in nsIDOMWindow window,
+                              in unsigned short audioChannel);
+
+  void setAudioChannelVolume(in nsIDOMWindow window,
+                             in unsigned short audioChannel,
+                             in float volume);
+
+  boolean getAudioChannelMuted(in nsIDOMWindow window,
+                               in unsigned short audioChannel);
+
+  void setAudioChannelMuted(in nsIDOMWindow window,
+                            in unsigned short audioChannel,
+                            in boolean muted);
+
+  boolean isAudioChannelActive(in nsIDOMWindow window,
+                               in unsigned short audioChannel);
+};
deleted file mode 100644
--- a/dom/audiochannel/tests/AudioChannelChromeScript.js
+++ /dev/null
@@ -1,18 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
-const { Services } = Cu.import('resource://gre/modules/Services.jsm');
-const { SystemAppProxy } = Cu.import('resource://gre/modules/SystemAppProxy.jsm');
-
-addMessageListener('init-chrome-event', function(message) {
-  // listen mozChromeEvent and forward to content process.
-  let type = message.type;
-
-  SystemAppProxy.addEventListener('mozChromeEvent', function(event) {
-    let details = event.detail;
-    if (details.type === type) {
-      sendAsyncMessage('chrome-event', details);
-    }
-  }, true);
-});
deleted file mode 100644
--- a/dom/audiochannel/tests/TestAudioChannelService.cpp
+++ /dev/null
@@ -1,669 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-#ifdef XP_WIN
-#include <windows.h>
-#else
-#include <unistd.h>
-#endif
-
-#include "TestHarness.h"
-
-#include "nsWeakReference.h"
-#include "AudioChannelService.h"
-#include "AudioChannelAgent.h"
-
-#include "nsThreadUtils.h"
-
-#define TEST_ENSURE_BASE(_test, _msg)       \
-  PR_BEGIN_MACRO                            \
-    if (!(_test)) {                         \
-      fail(_msg);                           \
-      return NS_ERROR_FAILURE;              \
-    } else {                                \
-      passed(_msg);                         \
-    }                                       \
-  PR_END_MACRO
-
-using namespace mozilla::dom;
-
-void
-spin_events_loop_until_false(const bool* const aCondition)
-{
-  nsCOMPtr<nsIThread> thread(::do_GetCurrentThread());
-  nsresult rv = NS_OK;
-  bool processed = true;
-  while (*aCondition && NS_SUCCEEDED(rv)) {
-    rv = thread->ProcessNextEvent(true, &processed);
-  }
-}
-
-class Agent : public nsIAudioChannelAgentCallback,
-              public nsSupportsWeakReference
-{
-protected:
-  virtual ~Agent()
-  {
-    if (mRegistered) {
-      StopPlaying();
-    }
-  }
-
-public:
-  NS_DECL_ISUPPORTS
-
-  explicit Agent(AudioChannel aChannel)
-  : mChannel(aChannel)
-  , mWaitCallback(false)
-  , mRegistered(false)
-  , mCanPlay(AUDIO_CHANNEL_STATE_MUTED)
-  {
-    mAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1");
-  }
-
-  nsresult Init(bool video=false)
-  {
-    nsresult rv = NS_OK;
-    if (video) {
-      rv = mAgent->InitWithVideo(nullptr, static_cast<int32_t>(mChannel),
-                                 this, true);
-    }
-    else {
-      rv = mAgent->InitWithWeakCallback(nullptr, static_cast<int32_t>(mChannel),
-                                        this);
-    }
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    return mAgent->SetVisibilityState(false);
-  }
-
-  nsresult StartPlaying(AudioChannelState *_ret)
-  {
-    if (mRegistered) {
-      StopPlaying();
-    }
-
-    nsresult rv = mAgent->StartPlaying((int32_t *)_ret);
-    mRegistered = true;
-    return rv;
-  }
-
-  nsresult StopPlaying()
-  {
-    mRegistered = false;
-    spin_events_loop_until_false(&mWaitCallback);
-    return mAgent->StopPlaying();
-  }
-
-  nsresult SetVisibilityState(bool visible)
-  {
-    if (mRegistered) {
-      mWaitCallback = true;
-    }
-    return mAgent->SetVisibilityState(visible);
-  }
-
-  NS_IMETHODIMP CanPlayChanged(int32_t canPlay) override
-  {
-    mCanPlay = static_cast<AudioChannelState>(canPlay);
-    mWaitCallback = false;
-    return NS_OK;
-  }
-
-  NS_IMETHODIMP WindowVolumeChanged() override
-  {
-    return NS_OK;
-  }
-
-  nsresult GetCanPlay(AudioChannelState *_ret, bool aWaitCallback = false)
-  {
-    if (aWaitCallback) {
-      mWaitCallback = true;
-    }
-
-    spin_events_loop_until_false(&mWaitCallback);
-    *_ret = mCanPlay;
-    return NS_OK;
-  }
-
-  nsCOMPtr<nsIAudioChannelAgent> mAgent;
-  AudioChannel mChannel;
-  bool mWaitCallback;
-  bool mRegistered;
-  AudioChannelState mCanPlay;
-};
-
-NS_IMPL_ISUPPORTS(Agent, nsIAudioChannelAgentCallback,
-                  nsISupportsWeakReference)
-
-nsresult
-TestDoubleStartPlaying()
-{
-  nsRefPtr<Agent> agent = new Agent(AudioChannel::Normal);
-
-  nsresult rv = agent->Init();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  AudioChannelState playable;
-  rv = agent->mAgent->StartPlaying((int32_t *)&playable);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = agent->mAgent->StartPlaying((int32_t *)&playable);
-  TEST_ENSURE_BASE(NS_FAILED(rv),
-    "Test0: StartPlaying calling twice must return error");
-
-  return NS_OK;
-}
-
-nsresult
-TestOneNormalChannel()
-{
-  nsRefPtr<Agent> agent = new Agent(AudioChannel::Normal);
-  nsresult rv = agent->Init();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  AudioChannelState playable;
-  rv = agent->StartPlaying(&playable);
-  NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED,
-    "Test1: A normal channel unvisible agent must be muted");
-
-  rv = agent->SetVisibilityState(true);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = agent->GetCanPlay(&playable);
-  NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
-    "Test1: A normal channel visible agent must be playable");
-
-  return rv;
-}
-
-nsresult
-TestTwoNormalChannels()
-{
-  nsRefPtr<Agent> agent1 = new Agent(AudioChannel::Normal);
-  nsresult rv = agent1->Init();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsRefPtr<Agent> agent2 = new Agent(AudioChannel::Normal);
-  rv = agent2->Init();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  AudioChannelState playable;
-  rv = agent1->StartPlaying(&playable);
-  NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED,
-    "Test2: A normal channel unvisible agent1 must be muted");
-
-  rv = agent2->StartPlaying(&playable);
-  NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED,
-    "Test2: A normal channel unvisible agent2 must be muted");
-
-  rv = agent1->SetVisibilityState(true);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = agent2->SetVisibilityState(true);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = agent1->GetCanPlay(&playable);
-  NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
-    "Test2: A normal channel visible agent1 must be playable");
-
-  rv = agent2->GetCanPlay(&playable);
-  NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
-    "Test2: A normal channel visible agent2 must be playable");
-
-  return rv;
-}
-
-nsresult
-TestContentChannels()
-{
-  nsRefPtr<Agent> agent1 = new Agent(AudioChannel::Content);
-  nsresult rv = agent1->Init();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsRefPtr<Agent> agent2 = new Agent(AudioChannel::Content);
-  rv = agent2->Init();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // All content channels in the foreground can be allowed to play
-  rv = agent1->SetVisibilityState(true);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = agent2->SetVisibilityState(true);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  AudioChannelState playable;
-  rv = agent1->StartPlaying(&playable);
-  NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
-    "Test3: A content channel visible agent1 must be playable");
-
-  rv = agent2->StartPlaying(&playable);
-  NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
-    "Test3: A content channel visible agent2 must be playable");
-
-  // Test the transition state of one content channel tried to set non-visible
-  // state first when app is going to background.
-  rv = agent1->SetVisibilityState(false);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = agent1->GetCanPlay(&playable);
-  NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
-    "Test3: A content channel unvisible agent1 must be playable from "
-    "foreground to background");
-
-  // Test all content channels set non-visible already
-  rv = agent2->SetVisibilityState(false);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = agent2->GetCanPlay(&playable);
-  NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
-    "Test3: A content channel unvisible agent2 must be playable from "
-    "foreground to background");
-
-  // Clear the content channels & mActiveContentChildIDs in AudioChannelService.
-  // If agent stop playable in the background, we will reserve it's childID in
-  // mActiveContentChildIDs, then it can allow to play next song. So we set agents
-  // to foreground first then stopping to play
-  rv = agent1->SetVisibilityState(true);
-  NS_ENSURE_SUCCESS(rv, rv);
-  rv = agent2->SetVisibilityState(true);
-  NS_ENSURE_SUCCESS(rv, rv);
-  rv = agent1->StopPlaying();
-  NS_ENSURE_SUCCESS(rv, rv);
-  rv = agent2->StopPlaying();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // Test that content channels can be allow to play when they starts from
-  // the background state
-  rv = agent1->SetVisibilityState(false);
-  NS_ENSURE_SUCCESS(rv, rv);
-  rv = agent2->SetVisibilityState(false);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = agent1->StartPlaying(&playable);
-  NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
-    "Test3: A content channel unvisible agent1 must be playable "
-    "from background state");
-
-  rv = agent2->StartPlaying(&playable);
-  NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
-    "Test3: A content channel unvisible agent2 must be playable "
-    "from background state");
-
-  return rv;
-}
-
-nsresult
-TestFadedState()
-{
-  nsRefPtr<Agent> normalAgent = new Agent(AudioChannel::Normal);
-  nsresult rv = normalAgent->Init();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsRefPtr<Agent> contentAgent = new Agent(AudioChannel::Content);
-  rv = contentAgent->Init();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsRefPtr<Agent> notificationAgent = new Agent(AudioChannel::Notification);
-  rv = notificationAgent->Init();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = normalAgent->SetVisibilityState(true);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = contentAgent->SetVisibilityState(true);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = notificationAgent->SetVisibilityState(true);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  AudioChannelState playable;
-  rv = normalAgent->StartPlaying(&playable);
-  NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
-    "Test4: A normal channel visible agent must be playable");
-
-  rv = contentAgent->StartPlaying(&playable);
-  NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
-    "Test4: A content channel visible agent must be playable");
-
-  rv = notificationAgent->StartPlaying(&playable);
-  NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
-    "Test4: A notification channel visible agent must be playable");
-
-  rv = contentAgent->GetCanPlay(&playable, true);
-  NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_FADED,
-    "Test4: A content channel unvisible agent must be faded because of "
-    "notification channel is playing");
-
-  rv = contentAgent->SetVisibilityState(false);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = contentAgent->GetCanPlay(&playable);
-  NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_FADED,
-    "Test4: A content channel unvisible agent must be faded because of "
-    "notification channel is playing");
-
-  rv = notificationAgent->SetVisibilityState(false);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = notificationAgent->GetCanPlay(&playable);
-  NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
-    "Test4: A notification channel unvisible agent must be playable from "
-    "foreground to background");
-
-  rv = notificationAgent->StopPlaying();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = contentAgent->GetCanPlay(&playable, true);
-  NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
-    "Test4: A content channel unvisible agent must be playable "
-    "because of notification channel is stopped");
-
-  rv = contentAgent->SetVisibilityState(true);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return rv;
-}
-
-nsresult
-TestPriorities()
-{
-  nsRefPtr<Agent> normalAgent = new Agent(AudioChannel::Normal);
-  nsresult rv = normalAgent->Init();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsRefPtr<Agent> contentAgent = new Agent(AudioChannel::Content);
-  rv = contentAgent->Init();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsRefPtr<Agent> notificationAgent = new Agent(AudioChannel::Notification);
-  rv = notificationAgent->Init();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsRefPtr<Agent> alarmAgent = new Agent(AudioChannel::Alarm);
-  rv = alarmAgent->Init();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsRefPtr<Agent> telephonyAgent = new Agent(AudioChannel::Telephony);
-  rv = telephonyAgent->Init();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsRefPtr<Agent> ringerAgent = new Agent(AudioChannel::Ringer);
-  rv = ringerAgent->Init();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsRefPtr<Agent> pNotificationAgent =
-    new Agent(AudioChannel::Publicnotification);
-  rv = pNotificationAgent->Init();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  AudioChannelState playable;
-
-  rv = normalAgent->StartPlaying(&playable);
-  NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED,
-    "Test5: A normal channel unvisible agent must be muted");
-
-  rv = contentAgent->StartPlaying(&playable);
-  NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
-    "Test5: A content channel unvisible agent must be playable while "
-    "playing from background state");
-
-  rv = notificationAgent->StartPlaying(&playable);
-  NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
-    "Test5: A notification channel unvisible agent must be playable");
-
-  rv = alarmAgent->StartPlaying(&playable);
-  NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
-    "Test5: An alarm channel unvisible agent must be playable");
-
-  rv = notificationAgent->StartPlaying(&playable);
-  NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED,
-    "Test5: A notification channel unvisible agent must be muted when an "
-    "alarm is playing");
-
-  rv = telephonyAgent->StartPlaying(&playable);
-  NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
-    "Test5: A telephony channel unvisible agent must be playable");
-
-  rv = alarmAgent->StartPlaying(&playable);
-  NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED,
-    "Test5: An alarm channel unvisible agent must be muted when a telephony "
-    "is playing");
-
-  rv = ringerAgent->StartPlaying(&playable);
-  NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
-    "Test5: A ringer channel unvisible agent must be playable");
-
-  rv = telephonyAgent->StartPlaying(&playable);
-  NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED,
-    "Test5: A telephony channel unvisible agent must be muted when a ringer "
-    "is playing");
-
-  rv = pNotificationAgent->StartPlaying(&playable);
-  NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
-    "Test5: A pNotification channel unvisible agent must be playable");
-
-  rv = ringerAgent->StartPlaying(&playable);
-  NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED,
-    "Test5: A ringer channel unvisible agent must be muted when a public "
-    "notification is playing");
-
-  // Stop to play notification channel or normal/content will be faded.
-  // Which already be tested on Test 4.
-  rv = notificationAgent->StopPlaying();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // Settings visible the normal channel.
-  rv = normalAgent->SetVisibilityState(true);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = normalAgent->GetCanPlay(&playable);
-  NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
-    "Test5: A normal channel visible agent must be playable");
-
-  // Set the content channel as visible .
-  rv = contentAgent->SetVisibilityState(true);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // Content must be playable because visible.
-  rv = contentAgent->GetCanPlay(&playable);
-  NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
-    "Test5: A content channel visible agent must be playable");
-
-  // Set the alarm channel as visible.
-  rv = alarmAgent->SetVisibilityState(true);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = alarmAgent->GetCanPlay(&playable);
-  NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
-    "Test5: An alarm channel visible agent must be playable");
-
-  // Set the telephony channel as visible.
-  rv = telephonyAgent->SetVisibilityState(true);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = telephonyAgent->GetCanPlay(&playable);
-  NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
-    "Test5: A telephony channel visible agent must be playable");
-
-  // Set the ringer channel as visible.
-  rv = ringerAgent->SetVisibilityState(true);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = ringerAgent->GetCanPlay(&playable);
-  NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
-    "Test5: A ringer channel visible agent must be playable");
-
-  // Set the public notification channel as visible.
-  rv = pNotificationAgent->SetVisibilityState(true);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = pNotificationAgent->GetCanPlay(&playable);
-  NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
-    "Test5: A pNotification channel visible agent must be playable");
-
-  return rv;
-}
-
-nsresult
-TestOneVideoNormalChannel()
-{
-  nsRefPtr<Agent> agent1 = new Agent(AudioChannel::Normal);
-  nsresult rv = agent1->Init(true);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsRefPtr<Agent> agent2 = new Agent(AudioChannel::Content);
-  rv = agent2->Init(false);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  AudioChannelState playable;
-  rv = agent1->StartPlaying(&playable);
-  NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED,
-    "Test6: A video normal channel invisible agent1 must be muted");
-
-  rv = agent2->StartPlaying(&playable);
-  NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
-    "Test6: A content channel invisible agent2 must be playable");
-
-  // one video normal channel in foreground and one content channel in background
-  rv = agent1->SetVisibilityState(true);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = agent1->GetCanPlay(&playable);
-  NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
-    "Test6: A video normal channel visible agent1 must be playable");
-
-  rv = agent2->GetCanPlay(&playable);
-  NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED,
-    "Test6: A content channel invisible agent2 must be muted");
-
-  // both one video normal channel and one content channel in foreground
-  rv = agent2->SetVisibilityState(true);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = agent1->GetCanPlay(&playable);
-  NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
-    "Test6: A video normal channel visible agent1 must be playable");
-
-  rv = agent2->GetCanPlay(&playable);
-  NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
-    "Test6: A content channel visible agent2 must be playable");
-
-  // one video normal channel in background and one content channel in foreground
-  rv = agent1->SetVisibilityState(false);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = agent1->GetCanPlay(&playable);
-  NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED,
-    "Test6: A video normal channel invisible agent1 must be muted");
-
-  rv = agent2->GetCanPlay(&playable);
-  NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
-    "Test6: A content channel visible agent2 must be playable");
-
-  // both one video normal channel and one content channel in background
-  rv = agent2->SetVisibilityState(false);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = agent1->GetCanPlay(&playable);
-  NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED,
-    "Test6: A video normal channel invisible agent1 must be muted");
-
-  rv = agent2->GetCanPlay(&playable);
-  NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
-    "Test6: A content channel invisible agent2 must be playable");
-
-  return rv;
-}
-
-int main(int argc, char** argv)
-{
-  ScopedXPCOM xpcom("AudioChannelService");
-  if (xpcom.failed()) {
-    return 1;
-  }
-
-  if (NS_FAILED(TestDoubleStartPlaying())) {
-    return 1;
-  }
-
-  if (NS_FAILED(TestOneNormalChannel())) {
-    return 1;
-  }
-
-  if (NS_FAILED(TestTwoNormalChannels())) {
-    return 1;
-  }
-
-  if (NS_FAILED(TestContentChannels())) {
-    return 1;
-  }
-
-  if (NS_FAILED(TestFadedState())) {
-    return 1;
-  }
-
-  // Channel type with AudioChannel::Telephony cannot be unregistered until the
-  // main thread has chances to process 1500 millisecond timer. In order to
-  // skip ambiguous return value of ChannelsActiveWithHigherPriorityThan(), new
-  // test cases are added before any test case that registers the channel type
-  // with AudioChannel::Telephony channel.
-  if (NS_FAILED(TestOneVideoNormalChannel())) {
-    return 1;
-  }
-
-  if (NS_FAILED(TestPriorities())) {
-    return 1;
-  }
-
-  return 0;
-}
-
deleted file mode 100644
index d7f6a0ccf47fbc30e936b47e3f4d2cf8f4a90a16..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
--- a/dom/audiochannel/tests/file_audio.html
+++ /dev/null
@@ -1,99 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Test audio-channel-changed & visible-audio-channel-changed mozChromeEvent</title>
-</head>
-<body>
-  <div id="content"></div>
-  <script>
-  var normalAudio;
-  var contentAudio;
-  var notificationAudio;
-  var alarmAudio;
-  var telephonyAudio;
-  var ringerAudio;
-  var publicnotificationAudio;
-
-  function playWithAudioType(audio, type) {
-    audio.mozAudioChannelType = type;
-    audio.src = "test.ogg";
-    audio.loop = true;
-
-    audio.play();
-  }
-
-  function runTest() {
-    // normal channel.
-    normalAudio = new Audio();
-    playWithAudioType(normalAudio, 'normal');
-
-    // content channel.
-    contentAudio = new Audio();
-    playWithAudioType(contentAudio, 'content');
-
-    // notification channel.
-    notificationAudio = new Audio();
-    playWithAudioType(notificationAudio, 'notification');
-
-    // alarm channel.
-    alarmAudio = new Audio();
-    playWithAudioType(alarmAudio, 'alarm');
-
-    // telephony channel.
-    telephonyAudio = new Audio();
-    playWithAudioType(telephonyAudio, 'telephony');
-
-    // ringer channel.
-    ringerAudio = new Audio();
-    playWithAudioType(ringerAudio, 'ringer');
-
-    // publicnotification channel.
-    publicnotificationAudio = new Audio();
-    playWithAudioType(publicnotificationAudio, 'publicnotification');
-
-    window.addEventListener('hashchange', function(event) {
-      if (location.hash == "#pauseAudio") {
-        publicnotificationAudio.pause();
-        ringerAudio.pause();
-        telephonyAudio.pause();
-      }
-
-      if (location.hash == "#pauseAudioFollowing") {
-        alarmAudio.pause();
-        notificationAudio.pause();
-        contentAudio.pause();
-        normalAudio.pause();
-      }
-    }, false);
-  }
-
-  function checkBackgroundStatus() {
-    if (location.hash == "#fg") {
-      runTest();
-      return;
-    }
-
-    if (document.hidden) {
-      runTest();
-      return;
-    }
-
-    document.addEventListener('visibilitychange', function visibilityChange() {
-      if (document.hidden) {
-        runTest();
-      }
-    });
-  }
-
-  SpecialPowers.pushPermissions(
-    [{ "type": "audio-channel-content", "allow": 1, "context": document },
-     { "type": "audio-channel-notification", "allow": 1, "context": document },
-     { "type": "audio-channel-alarm", "allow": 1, "context": document },
-     { "type": "audio-channel-telephony", "allow": 1, "context": document },
-     { "type": "audio-channel-ringer", "allow": 1, "context": document },
-     { "type": "audio-channel-publicnotification", "allow": 1, "context": document }],
-    checkBackgroundStatus);
-
-  </script>
-</body>
-</html>
deleted file mode 100644
--- a/dom/audiochannel/tests/mochitest.ini
+++ /dev/null
@@ -1,11 +0,0 @@
-[DEFAULT]
-support-files =
-  audio.ogg
-  file_audio.html
-  file_telephonyPolicy.html
-  AudioChannelChromeScript.js
-
-[test_telephonyPolicy.html]
-skip-if = buildapp == 'mulet' || (toolkit == 'gonk' || e10s) || os == "android"
-[test_audioChannelChange.html]
-skip-if = (toolkit != 'gonk')
deleted file mode 100644
--- a/dom/audiochannel/tests/moz.build
+++ /dev/null
@@ -1,16 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-GeckoCppUnitTests([
-    'TestAudioChannelService',
-])
-
-if CONFIG['OS_ARCH'] == 'WINNT':
-    DEFINES['NOMINMAX'] = True
-
-MOCHITEST_MANIFESTS += ['mochitest.ini']
-
-FAIL_ON_WARNINGS = True
deleted file mode 100644
--- a/dom/audiochannel/tests/test_audioChannelChange.html
+++ /dev/null
@@ -1,209 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <meta charset="utf-8">
-  <title>Test audio-channel-changed & visible-audio-channel-changed mozChromeEvent</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="content"></div>
-  <script type="application/javascript;version=1.7">
-  var expectedAudioTypes;
-  var expectedVisibleAudioTypes;
-  var expectedVisibleAudioType;
-  var index;
-  var visibleIndex;
-  var iframe1;
-  var normalAudio;
-
-  function playWithAudioType(audio, type) {
-    audio.mozAudioChannelType = type;
-    audio.src = "test.ogg";
-    audio.loop = true;
-
-    audio.play();
-  }
-
-  function fgBgTestListener(message) {
-    var type = message.type;
-    var channel = message.channel;
-
-    if (type == 'audio-channel-changed') {
-      is(channel, expectedAudioTypes[index], channel + " is received and expected " + expectedAudioTypes[index]);
-      index++;
-    }
-
-    if (type == 'visible-audio-channel-changed') {
-      is(channel, expectedVisibleAudioType, channel + " is received and expected " + expectedVisibleAudioType);
-    }
-
-    // All audio types are playing now so ask to pause them.
-    // This call will stop audio from highest to telephony.
-    if ('cmd-pause' == expectedAudioTypes[index]) {
-      iframe1.src = 'file_audio.html#pauseAudio';
-      index++;
-    }
-
-    // According to there is a 1.5 second delay of releasing telephony,
-    // we need to wait for it then continue to pause others.
-    if ('cmd-secondPause' == expectedAudioTypes[index]) {
-      iframe1.src = 'file_audio.html#pauseAudioFollowing';
-      index++;
-    }
-
-    if (index == expectedAudioTypes.length) {
-      document.body.removeChild(iframe1);
-      script.removeMessageListener('chrome-event', fgBgTestListener);
-      normalAudio.pause();
-      SimpleTest.finish();
-    }
-  }
-
-  // Channel of visible-audio-channel-changed event should be always normal.
-  // Audios in background should not effect visible-audio-channel-changed.
-  function runFgBgTest() {
-    expectedAudioTypes = ["normal", "content", "notification",
-                          "alarm", "telephony", "ringer", "publicnotification", "cmd-pause",
-                          "ringer", "telephony", "alarm", "cmd-secondPause", "notification",
-                          "content", "normal"];
-    expectedVisibleAudioType = "normal";
-    index = 0;
-
-    script.addMessageListener('chrome-event', fgBgTestListener);
-
-    // To play a audio with normal channel in the foreground.
-    normalAudio = new Audio();
-    playWithAudioType(normalAudio, 'normal');
-
-    iframe1.src = 'file_audio.html#bg';
-    document.body.appendChild(iframe1);
-    iframe1.setVisible(false);
-  }
-
-  function bgTestListener(message) {
-    var type = message.type;
-    var channel = message.channel;
-
-    if (type == 'audio-channel-changed') {
-      is(channel, expectedAudioTypes[index], channel + " is received and expected " + expectedAudioTypes[index]);
-      index++;
-    }
-
-    if (type == 'visible-audio-channel-changed') {
-      is(channel, expectedVisibleAudioType, channel + " is received and expected " + expectedVisibleAudioType);
-    }
-
-    // All audio types are playing now so ask to pause them.
-    if ('cmd-pause' == expectedAudioTypes[index]) {
-      iframe1.src = 'file_audio.html#pauseAudio';
-      index++;
-    }
-
-    if ('cmd-secondPause' == expectedAudioTypes[index]) {
-      iframe1.src = 'file_audio.html#pauseAudioFollowing';
-      index++;
-    }
-
-    if (index == expectedAudioTypes.length) {
-      document.body.removeChild(iframe1);
-      script.removeMessageListener('chrome-event', bgTestListener);
-      runFgBgTest();
-    }
-  }
-
-  // 1. Channel of visible-audio-channel-changed event should be always none.
-  // 2. normal is not allowed to be played in the background.
-  function runBgTest() {
-    expectedAudioTypes = ["content", "notification",
-                          "alarm", "telephony", "ringer", "publicnotification", "cmd-pause",
-                         "ringer", "telephony", "alarm", "cmd-secondPause", "notification",
-                          "content", "none"];
-    expectedVisibleAudioType = "none";
-    index = 0;
-
-    script.addMessageListener('chrome-event', bgTestListener);
-
-    iframe1.src = 'file_audio.html#bg';
-    document.body.appendChild(iframe1);
-    iframe1.setVisible(false);
-  }
-
-  function fgTestListener(message) {
-    var type = message.type;
-    var channel = message.channel;
-
-    if (type == 'audio-channel-changed') {
-      is(channel, expectedAudioTypes[index], channel + " is received and expected " + expectedAudioTypes[index]);
-      index++;
-    }
-
-    if (type == 'visible-audio-channel-changed') {
-      is(channel, expectedAudioTypes[visibleIndex], channel + " is received and expected " + expectedAudioTypes[visibleIndex]);
-      visibleIndex++;
-    }
-
-    // All audio types are playing now so ask to pause them.
-    if ('cmd-pause' == expectedAudioTypes[visibleIndex] &&
-        'cmd-pause' == expectedAudioTypes[index]) {
-      iframe1.src = 'file_audio.html#pauseAudio';
-      visibleIndex++;
-      index++;
-    }
-
-    if ('cmd-secondPause' == expectedAudioTypes[visibleIndex] &&
-        'cmd-secondPause' == expectedAudioTypes[index]) {
-      iframe1.src = 'file_audio.html#pauseAudioFollowing';
-      visibleIndex++;
-      index++;
-    }
-
-    if (index == expectedAudioTypes.length && visibleIndex == expectedAudioTypes.length) {
-      document.body.removeChild(iframe1);
-      script.removeMessageListener('chrome-event', fgTestListener);
-      runBgTest();
-    }
-  }
-
-  // The foreground audio will effect both of audio-channel-changed and
-  // visible-audio-channel-changed.
-  function runFgTest() {
-    expectedAudioTypes = ["normal", "content", "notification",
-                          "alarm", "telephony", "ringer", "publicnotification",
-                          "cmd-pause", "ringer", "telephony", "alarm",
-                          "cmd-secondPause", "notification", "content",
-                          "normal", "none"];
-
-    index = 0;
-    visibleIndex = 0;
-
-    script.addMessageListener('chrome-event', fgTestListener);
-
-    iframe1 = document.createElement('iframe');
-    iframe1.setAttribute('mozbrowser', true);
-    iframe1.src = 'file_audio.html#fg';
-    document.body.appendChild(iframe1);
-  }
-
-  var url = SimpleTest.getTestFileURL("AudioChannelChromeScript.js")
-  var script = SpecialPowers.loadChromeScript(url);
-  script.sendAsyncMessage("init-chrome-event", {
-    type: 'audio-channel-changed'
-  });
-  script.sendAsyncMessage("init-chrome-event", {
-    type: 'visible-audio-channel-changed'
-  });
-
-  SimpleTest.waitForExplicitFinish();
-
-  SpecialPowers.pushPermissions(
-    [{ "type": "browser", "allow": 1, "context": document },
-     { "type": "embed-apps", "allow": 1, "context": document },
-     { "type": "webapps-manage", "allow": 1, "context": document }], function() {
-    SpecialPowers.pushPrefEnv({"set": [["dom.ipc.browser_frames.oop_by_default", true],
-                                       ["media.useAudioChannelService", true],
-                                       ["dom.mozBrowserFramesEnabled", true]]}, runFgTest);
-  });
-  </script>
-</body>
-</html>
deleted file mode 100644
--- a/dom/audiochannel/tests/test_telephonyPolicy.html
+++ /dev/null
@@ -1,87 +0,0 @@
-<!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.useAudioChannelAPI", 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();
-SimpleTest.requestFlakyTimeout("untriaged");
-runTest();
-
-  </script>
-</body>
-</html>
--- a/dom/base/ResponsiveImageSelector.cpp
+++ b/dom/base/ResponsiveImageSelector.cpp
@@ -698,18 +698,18 @@ ResponsiveImageCandidate::Density(int32_
 
   if (mType == eCandidateType_Default) {
     return 1.0;
   }
 
   if (mType == eCandidateType_Density) {
     return mValue.mDensity;
   } else if (mType == eCandidateType_ComputedFromWidth) {
-    if (aMatchingWidth <= 0) {
-      MOZ_ASSERT(false, "0 or negative matching width is invalid per spec");
+    if (aMatchingWidth < 0) {
+      MOZ_ASSERT(false, "Don't expect to have a negative matching width at this point");
       return 1.0;
     }
     double density = double(mValue.mWidth) / double(aMatchingWidth);
     MOZ_ASSERT(density > 0.0);
     return density;
   }
 
   MOZ_ASSERT(false, "Unknown candidate type");
--- a/dom/base/WebSocket.cpp
+++ b/dom/base/WebSocket.cpp
@@ -8,16 +8,18 @@
 #include "mozilla/dom/WebSocketBinding.h"
 #include "mozilla/net/WebSocketChannel.h"
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/net/WebSocketChannel.h"
 #include "mozilla/dom/File.h"
+#include "mozilla/dom/nsCSPContext.h"
+#include "mozilla/dom/nsCSPUtils.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "mozilla/dom/WorkerRunnable.h"
 #include "mozilla/dom/WorkerScope.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIDOMWindow.h"
 #include "nsIDocument.h"
 #include "nsXPCOM.h"
@@ -1461,16 +1463,77 @@ WebSocketImpl::Init(JSContext* aCx,
     nsresult rv;
     sc = mWebSocket->GetContextForEventHandlers(&rv);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       aRv.Throw(rv);
       return;
     }
   }
 
+  nsCOMPtr<nsIURI> uri;
+  {
+    nsresult rv = NS_NewURI(getter_AddRefs(uri), mURI);
+
+    // We crash here because we are sure that mURI is a valid URI, so either we
+    // are OOM'ing or something else bad is happening.
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      MOZ_CRASH();
+    }
+  }
+
+  // Check content policy.
+  int16_t shouldLoad = nsIContentPolicy::ACCEPT;
+  nsCOMPtr<nsIDocument> originDoc = nsContentUtils::GetDocumentFromScriptContext(sc);
+  mOriginDocument = do_GetWeakReference(originDoc);
+  aRv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_WEBSOCKET,
+                                  uri,
+                                  aPrincipal,
+                                  originDoc,
+                                  EmptyCString(),
+                                  nullptr,
+                                  &shouldLoad,
+                                  nsContentUtils::GetContentPolicy(),
+                                  nsContentUtils::GetSecurityManager());
+  if (NS_WARN_IF(aRv.Failed())) {
+    return;
+  }
+
+  if (NS_CP_REJECTED(shouldLoad)) {
+    // Disallowed by content policy.
+    aRv.Throw(NS_ERROR_CONTENT_BLOCKED);
+    return;
+  }
+
+  // Potentially the page uses the CSP directive 'upgrade-insecure-requests'.
+  // In such a case we have to upgrade ws: to wss: and also update mSecure
+  // to reflect that upgrade. Please note that we can not upgrade from ws:
+  // to wss: before performing content policy checks because CSP needs to
+  // send reports in case the scheme is about to be upgraded.
+  if (!mSecure && originDoc && originDoc->GetUpgradeInsecureRequests()) {
+    // let's use the old specification before the upgrade for logging
+    NS_ConvertUTF8toUTF16 reportSpec(mURI);
+
+    // upgrade the request from ws:// to wss:// and mark as secure
+    mURI.ReplaceSubstring("ws://", "wss://");
+    if (NS_WARN_IF(mURI.Find("wss://") != 0)) {
+      return;
+    }
+    mSecure = true;
+
+    const char16_t* params[] = { reportSpec.get(), NS_LITERAL_STRING("wss").get() };
+    CSP_LogLocalizedStr(NS_LITERAL_STRING("upgradeInsecureRequest").get(),
+                        params, ArrayLength(params),
+                        EmptyString(), // aSourceFile
+                        EmptyString(), // aScriptSample
+                        0, // aLineNumber
+                        0, // aColumnNumber
+                        nsIScriptError::warningFlag, "CSP",
+                        mInnerWindowID);
+  }
+
   // Don't allow https:// to open ws://
   if (!mSecure &&
       !Preferences::GetBool("network.websocket.allowInsecureFromHTTPS",
                             false)) {
     // Confirmed we are opening plain ws:// and want to prevent this from a
     // secure context (e.g. https). Check the principal's uri to determine if
     // we were loaded from https.
     nsCOMPtr<nsIGlobalObject> globalObject(GetEntryGlobal());
@@ -1507,50 +1570,16 @@ WebSocketImpl::Init(JSContext* aCx,
 
     if (!mRequestedProtocolList.IsEmpty()) {
       mRequestedProtocolList.AppendLiteral(", ");
     }
 
     AppendUTF16toUTF8(aProtocolArray[index], mRequestedProtocolList);
   }
 
-  nsCOMPtr<nsIURI> uri;
-  {
-    nsresult rv = NS_NewURI(getter_AddRefs(uri), mURI);
-
-    // We crash here because we are sure that mURI is a valid URI, so either we
-    // are OOM'ing or something else bad is happening.
-    if (NS_FAILED(rv)) {
-      MOZ_CRASH();
-    }
-  }
-
-  // Check content policy.
-  int16_t shouldLoad = nsIContentPolicy::ACCEPT;
-  nsCOMPtr<nsIDocument> originDoc = nsContentUtils::GetDocumentFromScriptContext(sc);
-  mOriginDocument = do_GetWeakReference(originDoc);
-  aRv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_WEBSOCKET,
-                                  uri,
-                                  aPrincipal,
-                                  originDoc,
-                                  EmptyCString(),
-                                  nullptr,
-                                  &shouldLoad,
-                                  nsContentUtils::GetContentPolicy(),
-                                  nsContentUtils::GetSecurityManager());
-  if (NS_WARN_IF(aRv.Failed())) {
-    return;
-  }
-
-  if (NS_CP_REJECTED(shouldLoad)) {
-    // Disallowed by content policy.
-    aRv.Throw(NS_ERROR_CONTENT_BLOCKED);
-    return;
-  }
-
   // the constructor should throw a SYNTAX_ERROR only if it fails to parse the
   // url parameter, so don't throw if InitializeConnection fails, and call
   // onerror/onclose asynchronously
   if (NS_FAILED(InitializeConnection(aPrincipal))) {
     *aConnectionFailed = true;
   } else {
     *aConnectionFailed = false;
   }
--- a/dom/base/nsCCUncollectableMarker.cpp
+++ b/dom/base/nsCCUncollectableMarker.cpp
@@ -25,16 +25,17 @@
 #include "nsAppShellCID.h"
 #include "nsContentUtils.h"
 #include "nsGlobalWindow.h"
 #include "nsJSEnvironment.h"
 #include "nsInProcessTabChildGlobal.h"
 #include "nsFrameLoader.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/dom/Element.h"
+#include "mozilla/dom/ProcessGlobal.h"
 #include "xpcpublic.h"
 #include "nsObserverService.h"
 #include "nsFocusManager.h"
 #include "nsIInterfaceRequestorUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
@@ -135,16 +136,24 @@ MarkChildMessageManagers(nsIMessageBroad
       }
     }
   }
 }
 
 static void
 MarkMessageManagers()
 {
+  if (nsFrameMessageManager::GetChildProcessManager()) {
+    // ProcessGlobal's MarkForCC marks also ChildProcessManager.
+    ProcessGlobal* pg = ProcessGlobal::Get();
+    if (pg) {
+      pg->MarkForCC();
+    }
+  }
+
   // The global message manager only exists in the root process.
   if (!XRE_IsParentProcess()) {
     return;
   }
   nsCOMPtr<nsIMessageBroadcaster> strongGlobalMM =
     do_GetService("@mozilla.org/globalmessagemanager;1");
   if (!strongGlobalMM) {
     return;
@@ -167,19 +176,16 @@ MarkMessageManagers()
       nsIMessageListenerManager* child = childMM;
       childMM = nullptr;
       child->MarkForCC();
     }
   }
   if (nsFrameMessageManager::sSameProcessParentManager) {
     nsFrameMessageManager::sSameProcessParentManager->MarkForCC();
   }
-  if (nsFrameMessageManager::GetChildProcessManager()) {
-    nsFrameMessageManager::GetChildProcessManager()->MarkForCC();
-  }
 }
 
 void
 MarkContentViewer(nsIContentViewer* aViewer, bool aCleanupJS,
                   bool aPrepareForCC)
 {
   if (!aViewer) {
     return;
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -1547,16 +1547,17 @@ static already_AddRefed<mozilla::dom::No
 
 // ==================================================================
 // =
 // ==================================================================
 nsIDocument::nsIDocument()
   : nsINode(nullNodeInfo),
     mReferrerPolicySet(false),
     mReferrerPolicy(mozilla::net::RP_Default),
+    mUpgradeInsecureRequests(false),
     mCharacterSet(NS_LITERAL_CSTRING("ISO-8859-1")),
     mNodeInfoManager(nullptr),
     mCompatMode(eCompatibility_FullStandards),
     mVisibilityState(dom::VisibilityState::Hidden),
     mIsInitialDocumentInWindow(false),
     mMayStartLayout(true),
     mVisible(true),
     mRemovedFromDocShell(false),
@@ -2701,16 +2702,29 @@ nsDocument::StartDocumentLoad(const char
   nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(aContainer);
 
   if (docShell) {
     nsresult rv = docShell->GetSandboxFlags(&mSandboxFlags);
     NS_ENSURE_SUCCESS(rv, rv);
     WarnIfSandboxIneffective(docShell, mSandboxFlags, GetChannel());
   }
 
+  // The CSP directive upgrade-insecure-requests not only applies to the
+  // toplevel document, but also to nested documents. Let's propagate that
+  // flag from the parent to the nested document.
+  nsCOMPtr<nsIDocShellTreeItem> treeItem = this->GetDocShell();
+  if (treeItem) {
+    nsCOMPtr<nsIDocShellTreeItem> sameTypeParent;
+    treeItem->GetSameTypeParent(getter_AddRefs(sameTypeParent));
+    if (sameTypeParent) {
+      mUpgradeInsecureRequests =
+        sameTypeParent->GetDocument()->GetUpgradeInsecureRequests();
+    }
+  }
+
   // If this is not a data document, set CSP.
   if (!mLoadedAsData) {
     nsresult rv = InitCSP(aChannel);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
 }
@@ -2973,16 +2987,23 @@ nsDocument::InitCSP(nsIChannel* aChannel
     mReferrerPolicy = static_cast<ReferrerPolicy>(referrerPolicy);
     mReferrerPolicySet = true;
 
     // Referrer Policy is set separately for the speculative parser in
     // nsHTMLDocument::StartDocumentLoad() so there's nothing to do here for
     // speculative loads.
   }
 
+  // ------ Set flag for 'upgrade-insecure-requests' if not already
+  //        inherited from the parent context.
+  if (!mUpgradeInsecureRequests) {
+    rv = csp->GetUpgradeInsecureRequests(&mUpgradeInsecureRequests);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
   rv = principal->SetCsp(csp);
   NS_ENSURE_SUCCESS(rv, rv);
   MOZ_LOG(gCspPRLog, LogLevel::Debug,
          ("Inserted CSP into principal %p", principal));
 
   return NS_OK;
 }
 
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -2050,16 +2050,17 @@ GK_ATOM(canplaythrough, "canplaythrough"
 GK_ATOM(ratechange, "ratechange")
 GK_ATOM(durationchange, "durationchange")
 GK_ATOM(volumechange, "volumechange")
 GK_ATOM(ondataavailable, "ondataavailable")
 GK_ATOM(onwarning, "onwarning")
 GK_ATOM(onstart, "onstart")
 GK_ATOM(onstop, "onstop")
 GK_ATOM(onphoto, "onphoto")
+GK_ATOM(onactivestatechanged, "onactivestatechanged")
 #ifdef MOZ_GAMEPAD
 GK_ATOM(ongamepadbuttondown, "ongamepadbuttondown")
 GK_ATOM(ongamepadbuttonup, "ongamepadbuttonup")
 GK_ATOM(ongamepadaxismove, "ongamepadaxismove")
 GK_ATOM(ongamepadconnected, "ongamepadconnected")
 GK_ATOM(ongamepaddisconnected, "ongamepaddisconnected")
 #endif
 
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -3730,49 +3730,20 @@ nsPIDOMWindow::SetAudioVolume(float aVol
     return NS_OK;
   }
 
   mAudioVolume = aVolume;
   RefreshMediaElements();
   return NS_OK;
 }
 
-float
-nsPIDOMWindow::GetAudioGlobalVolume()
-{
-  float globalVolume = 1.0;
-  nsCOMPtr<nsPIDOMWindow> window = this;
-
-  do {
-    if (window->GetAudioMuted()) {
-      return 0;
-    }
-
-    globalVolume *= window->GetAudioVolume();
-
-    nsCOMPtr<nsIDOMWindow> win;
-    window->GetParent(getter_AddRefs(win));
-    if (window == win) {
-      break;
-    }
-
-    window = do_QueryInterface(win);
-
-    // If there is not parent, or we are the toplevel or the volume is
-    // already 0.0, we don't continue.
-  } while (window && window != this && globalVolume);
-
-  return globalVolume;
-}
-
 void
 nsPIDOMWindow::RefreshMediaElements()
 {
-  nsRefPtr<AudioChannelService> service =
-    AudioChannelService::GetOrCreateAudioChannelService();
+  nsRefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
   service->RefreshAgentsVolume(GetCurrentInnerWindow());
 }
 
 // nsISpeechSynthesisGetter
 
 #ifdef MOZ_WEBSPEECH
 SpeechSynthesis*
 nsGlobalWindow::GetSpeechSynthesis(ErrorResult& aError)
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -304,16 +304,28 @@ public:
    * GetReferrerPolicy() for Document.webidl.
    */
   uint32_t ReferrerPolicy() const
   {
     return GetReferrerPolicy();
   }
 
   /**
+   * If true, this flag indicates that all subresource loads for this
+   * document need to be upgraded from http to https.
+   * This flag becomes true if the CSP of the document itself, or any
+   * of the document's ancestors up to the toplevel document makes use
+   * of the CSP directive 'upgrade-insecure-requests'.
+   */
+  bool GetUpgradeInsecureRequests() const
+  {
+    return mUpgradeInsecureRequests;
+  }
+
+  /**
    * Set the principal responsible for this document.
    */
   virtual void SetPrincipal(nsIPrincipal *aPrincipal) = 0;
 
   /**
    * Return the LoadGroup for the document. May return null.
    */
   already_AddRefed<nsILoadGroup> GetDocumentLoadGroup() const
@@ -2626,16 +2638,18 @@ protected:
   nsCOMPtr<nsIURI> mDocumentBaseURI;
   nsCOMPtr<nsIURI> mChromeXHRDocBaseURI;
 
   nsWeakPtr mDocumentLoadGroup;
 
   bool mReferrerPolicySet;
   ReferrerPolicyEnum mReferrerPolicy;
 
+  bool mUpgradeInsecureRequests;
+
   mozilla::WeakPtr<nsDocShell> mDocumentContainer;
 
   nsCString mCharacterSet;
   int32_t mCharacterSetSource;
 
   // This is just a weak pointer; the parent document owns its children.
   nsIDocument* mParentDocument;
 
--- a/dom/base/nsINode.cpp
+++ b/dom/base/nsINode.cpp
@@ -417,16 +417,46 @@ nsINode::IsInAnonymousSubtree() const
 {
   if (!IsContent()) {
     return false;
   }
 
   return AsContent()->IsInAnonymousSubtree();
 }
 
+std::ostream&
+operator<<(std::ostream& aStream, const nsINode& aNode)
+{
+  nsAutoString elemDesc;
+  const nsINode* curr = &aNode;
+  while (curr) {
+    const nsString& localName = curr->LocalName();
+    nsString id;
+    if (curr->IsElement()) {
+      curr->AsElement()->GetId(id);
+    }
+
+    if (!elemDesc.IsEmpty()) {
+      elemDesc = elemDesc + NS_LITERAL_STRING(".");
+    }
+
+    elemDesc = elemDesc + localName;
+
+    if (!id.IsEmpty()) {
+      elemDesc = elemDesc + NS_LITERAL_STRING("['") + id +
+                 NS_LITERAL_STRING("']");
+    }
+
+    curr = curr->GetParentNode();
+  }
+
+  NS_ConvertUTF16toUTF8 str(elemDesc);
+  return aStream << str.get();
+}
+
 bool
 nsINode::IsAnonymousContentInSVGUseSubtree() const
 {
   MOZ_ASSERT(IsInAnonymousSubtree());
   nsIContent* parent = AsContent()->GetBindingParent();
   // Watch out for parentless native-anonymous subtrees.
   return parent && parent->IsSVGElement(nsGkAtoms::use);
 }
--- a/dom/base/nsINode.h
+++ b/dom/base/nsINode.h
@@ -17,16 +17,17 @@
 #include "nsPropertyTable.h"        // for typedefs
 #include "nsTObserverArray.h"       // for member
 #include "mozilla/ErrorResult.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/dom/EventTarget.h" // for base class
 #include "js/TypeDecls.h"     // for Handle, Value, JSObject, JSContext
 #include "mozilla/dom/DOMString.h"
 #include "mozilla/dom/BindingDeclarations.h"
+#include <iosfwd>
 
 // Including 'windows.h' will #define GetClassInfo to something else.
 #ifdef XP_WIN
 #ifdef GetClassInfo
 #undef GetClassInfo
 #endif
 #endif
 
@@ -577,16 +578,22 @@ public:
     return mNodeInfo;
   }
 
   inline bool IsInNamespace(int32_t aNamespace) const
   {
     return mNodeInfo->NamespaceID() == aNamespace;
   }
 
+  /**
+   * Print a debugger friendly descriptor of this element. This will describe
+   * the position of this element in the document.
+   */
+  friend std::ostream& operator<<(std::ostream& aStream, const nsINode& aNode);
+
 protected:
   // These 2 methods are useful for the recursive templates IsHTMLElement,
   // IsSVGElement, etc.
   inline bool IsNodeInternal() const
   {
     return false;
   }
 
@@ -1717,17 +1724,17 @@ public:
     mNodeInfo->GetNamespaceURI(aNamespaceURI);
   }
 #ifdef MOZILLA_INTERNAL_API
   void GetPrefix(nsAString& aPrefix)
   {
     mNodeInfo->GetPrefix(aPrefix);
   }
 #endif
-  void GetLocalName(mozilla::dom::DOMString& aLocalName)
+  void GetLocalName(mozilla::dom::DOMString& aLocalName) const
   {
     const nsString& localName = LocalName();
     if (localName.IsVoid()) {
       aLocalName.SetNull();
     } else {
       aLocalName.SetStringBuffer(nsStringBuffer::FromString(localName),
                                  localName.Length());
     }
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -180,18 +180,16 @@ public:
 
   // Audio API
   bool GetAudioMuted() const;
   void SetAudioMuted(bool aMuted);
 
   float GetAudioVolume() const;
   nsresult SetAudioVolume(float aVolume);
 
-  float GetAudioGlobalVolume();
-
   virtual void SetServiceWorkersTestingEnabled(bool aEnabled)
   {
     MOZ_ASSERT(IsOuterWindow());
     mServiceWorkersTestingEnabled = aEnabled;
   }
 
   bool GetServiceWorkersTestingEnabled()
   {
--- a/dom/base/test/chrome.ini
+++ b/dom/base/test/chrome.ini
@@ -1,10 +1,10 @@
 [DEFAULT]
-skip-if = buildapp == 'b2g'
+skip-if = buildapp == 'b2g' || os == 'android'
 support-files =
   file_url.jsm
   file_empty.html
   file_bug945152.jar
   file_bug945152_worker.js
   file_bug1008126_worker.js
 
 [test_anonymousContent_xul_window.xul]
--- a/dom/base/test/chrome/chrome.ini
+++ b/dom/base/test/chrome/chrome.ini
@@ -1,10 +1,10 @@
 [DEFAULT]
-skip-if = buildapp == 'b2g'
+skip-if = buildapp == 'b2g' || os == 'android'
 support-files =
   blockNoPlugins.xml
   blockPluginHard.xml
   bug418986-1.js
   cpows_child.js
   cpows_parent.xul
   file_bug391728.html
   file_bug391728_2.html
new file mode 100644
--- /dev/null
+++ b/dom/base/test/file_audioLoop.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<audio src="audio.ogg" autoplay="true" loop>
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -232,27 +232,30 @@ support-files =
   variable_style_sheet.sjs
   viewport_helpers.js
   w3element_traversal.svg
   wholeTexty-helper.xml
   file_nonascii_blob_url.html
   referrerHelper.js
   test_performance_user_timing.js
   img_referrer_testserver.sjs
+  file_audioLoop.html
   file_webaudioLoop.html
   file_webaudioLoop2.html
 
 [test_anonymousContent_api.html]
 [test_anonymousContent_append_after_reflow.html]
 [test_anonymousContent_insert.html]
 [test_anonymousContent_manipulate_content.html]
 [test_appname_override.html]
 [test_audioWindowUtils.html]
 [test_audioNotification.html]
 skip-if = buildapp == 'mulet'
+[test_audioNotificationStopOnNavigation.html]
+skip-if = buildapp == 'mulet'
 [test_bug1091883.html]
 [test_bug116083.html]
 [test_bug793311.html]
 [test_bug913761.html]
 [test_bug976673.html]
 [test_bug978522.html]
 [test_bug979109.html]
 [test_bug989665.html]
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_audioNotificationStopOnNavigation.html
@@ -0,0 +1,75 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for audio controller in windows</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<pre id="test">
+</pre>
+<iframe></iframe>
+
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+var expectedNotification = null;
+var iframe = null;
+
+var observer = {
+  observe: function(subject, topic, data) {
+    is(topic, "media-playback", "media-playback received");
+    is(data, expectedNotification, "This is the right notification");
+    runTest();
+  }
+};
+
+var observerService = SpecialPowers.Cc["@mozilla.org/observer-service;1"]
+                                   .getService(SpecialPowers.Ci.nsIObserverService);
+
+var tests = [
+  function() {
+    iframe = document.querySelector("iframe");
+    SpecialPowers.pushPrefEnv({"set": [["media.useAudioChannelService", true]]}, runTest);
+  },
+
+  function() {
+    observerService.addObserver(observer, "media-playback", false);
+    ok(true, "Observer set");
+    runTest();
+  },
+
+  function() {
+    expectedNotification = 'active';
+    iframe.src = "file_audioLoop.html";
+  },
+
+  function() {
+    expectedNotification = 'inactive';
+    iframe.src = "data:text/html,page without audio";
+  },
+
+  function() {
+    observerService.removeObserver(observer, "media-playback");
+    ok(true, "Observer removed");
+    runTest();
+  }
+];
+
+function runTest() {
+  if (!tests.length) {
+    SimpleTest.finish();
+    return;
+  }
+
+  var test = tests.shift();
+  test();
+}
+
+onload = runTest;
+
+</script>
+</body>
+</html>
+
--- a/dom/bindings/BindingDeclarations.h
+++ b/dom/bindings/BindingDeclarations.h
@@ -114,16 +114,21 @@ public:
   Optional_base()
   {}
 
   explicit Optional_base(const T& aValue)
   {
     mImpl.emplace(aValue);
   }
 
+  bool operator==(const Optional_base<T, InternalType>& aOther) const
+  {
+    return mImpl == aOther.mImpl;
+  }
+
   template<typename T1, typename T2>
   explicit Optional_base(const T1& aValue1, const T2& aValue2)
   {
     mImpl.emplace(aValue1, aValue2);
   }
 
   bool WasPassed() const
   {
--- a/dom/broadcastchannel/tests/chrome.ini
+++ b/dom/broadcastchannel/tests/chrome.ini
@@ -1,6 +1,6 @@
 [DEFAULT]
-skip-if = e10s || buildapp == 'b2g'
+skip-if = e10s || buildapp == 'b2g' || os == 'android'
 support-files =
   blank.html
 
 [test_broadcastchannel_private_browsing.html]
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/BrowserElementAudioChannel.cpp
@@ -0,0 +1,489 @@
+/* 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 "BrowserElementAudioChannel.h"
+
+#include "mozilla/Services.h"
+#include "mozilla/dom/BrowserElementAudioChannelBinding.h"
+#include "mozilla/dom/DOMRequest.h"
+#include "mozilla/dom/ToJSValue.h"
+#include "AudioChannelService.h"
+#include "nsIBrowserElementAPI.h"
+#include "nsIDocShell.h"
+#include "nsIDOMDOMRequest.h"
+#include "nsIObserverService.h"
+#include "nsISupportsPrimitives.h"
+#include "nsITabParent.h"
+#include "nsPIDOMWindow.h"
+
+namespace {
+
+void
+AssertIsInMainProcess()
+{
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+}
+
+} // anonymous namespace
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_ADDREF_INHERITED(BrowserElementAudioChannel, DOMEventTargetHelper)
+NS_IMPL_RELEASE_INHERITED(BrowserElementAudioChannel, DOMEventTargetHelper)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(BrowserElementAudioChannel)
+  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
+  NS_INTERFACE_MAP_ENTRY(nsIObserver)
+NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED(BrowserElementAudioChannel,
+                                   DOMEventTargetHelper,
+                                   mFrameLoader,
+                                   mFrameWindow,
+                                   mTabParent,
+                                   mBrowserElementAPI)
+
+BrowserElementAudioChannel::BrowserElementAudioChannel(
+                                                   nsPIDOMWindow* aWindow,
+                                                   nsIFrameLoader* aFrameLoader,
+                                                   nsIBrowserElementAPI* aAPI,
+                                                   AudioChannel aAudioChannel)
+  : DOMEventTargetHelper(aWindow)
+  , mFrameLoader(aFrameLoader)
+  , mBrowserElementAPI(aAPI)
+  , mAudioChannel(aAudioChannel)
+  , mState(eStateUnknown)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  AssertIsInMainProcess();
+  MOZ_ASSERT(mFrameLoader);
+
+  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+  if (obs) {
+    nsAutoString name;
+    AudioChannelService::GetAudioChannelString(aAudioChannel, name);
+
+    nsAutoCString topic;
+    topic.Assign("audiochannel-activity-");
+    topic.Append(NS_ConvertUTF16toUTF8(name));
+
+    obs->AddObserver(this, topic.get(), true);
+  }
+}
+
+BrowserElementAudioChannel::~BrowserElementAudioChannel()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  AssertIsInMainProcess();
+
+  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+  if (obs) {
+    nsAutoString name;
+    AudioChannelService::GetAudioChannelString(mAudioChannel, name);
+
+    nsAutoCString topic;
+    topic.Assign("audiochannel-activity-");
+    topic.Append(NS_ConvertUTF16toUTF8(name));
+
+    obs->RemoveObserver(this, topic.get());
+  }
+}
+
+nsresult
+BrowserElementAudioChannel::Initialize()
+{
+  nsCOMPtr<nsIDocShell> docShell;
+  nsresult rv = mFrameLoader->GetDocShell(getter_AddRefs(docShell));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (docShell) {
+    nsCOMPtr<nsPIDOMWindow> window = docShell->GetWindow();
+    if (!window) {
+      return NS_ERROR_FAILURE;
+    }
+
+    nsCOMPtr<nsIDOMWindow> topWindow;
+    window->GetScriptableTop(getter_AddRefs(topWindow));
+
+    mFrameWindow = do_QueryInterface(topWindow);
+    mFrameWindow = mFrameWindow->GetOuterWindow();
+    return NS_OK;
+  }
+
+  rv = mFrameLoader->GetTabParent(getter_AddRefs(mTabParent));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  MOZ_ASSERT(mTabParent);
+  return NS_OK;
+}
+
+JSObject*
+BrowserElementAudioChannel::WrapObject(JSContext *aCx,
+                                       JS::Handle<JSObject*> aGivenProto)
+{
+  return BrowserElementAudioChannelBinding::Wrap(aCx, this, aGivenProto);
+}
+
+AudioChannel
+BrowserElementAudioChannel::Name() const
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  AssertIsInMainProcess();
+
+  return mAudioChannel;
+}
+
+namespace {
+
+class BaseRunnable : public nsRunnable
+{
+protected:
+  nsCOMPtr<nsPIDOMWindow> mParentWindow;
+  nsCOMPtr<nsPIDOMWindow> mFrameWindow;
+  nsRefPtr<DOMRequest> mRequest;
+  AudioChannel mAudioChannel;
+
+  virtual void DoWork(AudioChannelService* aService,
+                      JSContext* aCx) = 0;
+
+public:
+  BaseRunnable(nsPIDOMWindow* aParentWindow, nsPIDOMWindow* aFrameWindow,
+               DOMRequest* aRequest, AudioChannel aAudioChannel)
+    : mParentWindow(aParentWindow)
+    , mFrameWindow(aFrameWindow)
+    , mRequest(aRequest)
+    , mAudioChannel(aAudioChannel)
+  {}
+
+  NS_IMETHODIMP Run() override
+  {
+    nsRefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
+    MOZ_ASSERT(service);
+
+    AutoJSAPI jsapi;
+    if (!jsapi.Init(mParentWindow)) {
+      mRequest->FireError(NS_ERROR_FAILURE);
+      return NS_OK;
+    }
+
+    DoWork(service, jsapi.cx());
+    return NS_OK;
+  }
+};
+
+class GetVolumeRunnable final : public BaseRunnable
+{
+public:
+  GetVolumeRunnable(nsPIDOMWindow* aParentWindow, nsPIDOMWindow* aFrameWindow,
+                    DOMRequest* aRequest, AudioChannel aAudioChannel)
+    : BaseRunnable(aParentWindow, aFrameWindow, aRequest, aAudioChannel)
+  {}
+
+protected:
+  virtual void DoWork(AudioChannelService* aService, JSContext* aCx) override
+  {
+    float volume = aService->GetAudioChannelVolume(mFrameWindow, mAudioChannel);
+
+    JS::Rooted<JS::Value> value(aCx);
+    if (!ToJSValue(aCx, volume, &value)) {
+      mRequest->FireError(NS_ERROR_FAILURE);
+      return;
+    }
+
+    mRequest->FireSuccess(value);
+  }
+};
+
+class GetMutedRunnable final : public BaseRunnable
+{
+public:
+  GetMutedRunnable(nsPIDOMWindow* aParentWindow, nsPIDOMWindow* aFrameWindow,
+                   DOMRequest* aRequest, AudioChannel aAudioChannel)
+    : BaseRunnable(aParentWindow, aFrameWindow, aRequest, aAudioChannel)
+  {}
+
+protected:
+  virtual void DoWork(AudioChannelService* aService, JSContext* aCx) override
+  {
+    bool muted = aService->GetAudioChannelMuted(mFrameWindow, mAudioChannel);
+
+    JS::Rooted<JS::Value> value(aCx);
+    if (!ToJSValue(aCx, muted, &value)) {
+      mRequest->FireError(NS_ERROR_FAILURE);
+      return;
+    }
+
+    mRequest->FireSuccess(value);
+  }
+};
+
+class IsActiveRunnable final : public BaseRunnable
+{
+  bool mActive;
+  bool mValueKnown;
+
+public:
+  IsActiveRunnable(nsPIDOMWindow* aParentWindow, nsPIDOMWindow* aFrameWindow,
+                   DOMRequest* aRequest, AudioChannel aAudioChannel,
+                   bool aActive)
+    : BaseRunnable(aParentWindow, aFrameWindow, aRequest, aAudioChannel)
+    , mActive(aActive)
+    , mValueKnown(true)
+  {}
+
+  IsActiveRunnable(nsPIDOMWindow* aParentWindow, nsPIDOMWindow* aFrameWindow,
+                   DOMRequest* aRequest, AudioChannel aAudioChannel)
+    : BaseRunnable(aParentWindow, aFrameWindow, aRequest, aAudioChannel)
+    , mActive(true)
+    , mValueKnown(false)
+  {}
+
+protected:
+  virtual void DoWork(AudioChannelService* aService, JSContext* aCx) override
+  {
+    if (!mValueKnown) {
+      mActive = aService->IsAudioChannelActive(mFrameWindow, mAudioChannel);
+    }
+
+    JS::Rooted<JS::Value> value(aCx);
+    if (!ToJSValue(aCx, mActive, &value)) {
+      mRequest->FireError(NS_ERROR_FAILURE);
+      return;
+    }
+
+    mRequest->FireSuccess(value);
+  }
+};
+
+class FireSuccessRunnable final : public BaseRunnable
+{
+public:
+  FireSuccessRunnable(nsPIDOMWindow* aParentWindow, nsPIDOMWindow* aFrameWindow,
+                      DOMRequest* aRequest, AudioChannel aAudioChannel)
+    : BaseRunnable(aParentWindow, aFrameWindow, aRequest, aAudioChannel)
+  {}
+
+protected:
+  virtual void DoWork(AudioChannelService* aService, JSContext* aCx) override
+  {
+    JS::Rooted<JS::Value> value(aCx);
+    mRequest->FireSuccess(value);
+  }
+};
+
+} // anonymous namespace
+
+already_AddRefed<dom::DOMRequest>
+BrowserElementAudioChannel::GetVolume(ErrorResult& aRv)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  AssertIsInMainProcess();
+
+  if (!mFrameWindow) {
+    nsCOMPtr<nsIDOMDOMRequest> request;
+    aRv = mBrowserElementAPI->GetAudioChannelVolume((uint32_t)mAudioChannel,
+                                                    getter_AddRefs(request));
+    if (NS_WARN_IF(aRv.Failed())) {
+      return nullptr;
+    }
+
+    return request.forget().downcast<DOMRequest>();
+  }
+
+  nsRefPtr<DOMRequest> domRequest = new DOMRequest(GetOwner());
+
+  nsCOMPtr<nsIRunnable> runnable =
+    new GetVolumeRunnable(GetOwner(), mFrameWindow, domRequest, mAudioChannel);
+  NS_DispatchToMainThread(runnable);
+
+  return domRequest.forget();
+}
+
+already_AddRefed<dom::DOMRequest>
+BrowserElementAudioChannel::SetVolume(float aVolume, ErrorResult& aRv)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  AssertIsInMainProcess();
+
+  if (!mFrameWindow) {
+    nsCOMPtr<nsIDOMDOMRequest> request;
+    aRv = mBrowserElementAPI->SetAudioChannelVolume((uint32_t)mAudioChannel,
+                                                    aVolume,
+                                                    getter_AddRefs(request));
+    if (NS_WARN_IF(aRv.Failed())) {
+      return nullptr;
+    }
+
+    return request.forget().downcast<DOMRequest>();
+  }
+
+  nsRefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
+  MOZ_ASSERT(service);
+
+  service->SetAudioChannelVolume(mFrameWindow, mAudioChannel, aVolume);
+
+  nsRefPtr<DOMRequest> domRequest = new DOMRequest(GetOwner());
+  nsCOMPtr<nsIRunnable> runnable = new FireSuccessRunnable(GetOwner(),
+                                                           mFrameWindow,
+                                                           domRequest,
+                                                           mAudioChannel);
+  NS_DispatchToMainThread(runnable);
+
+  return domRequest.forget();
+}
+
+already_AddRefed<dom::DOMRequest>
+BrowserElementAudioChannel::GetMuted(ErrorResult& aRv)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  AssertIsInMainProcess();
+
+  if (!mFrameWindow) {
+    nsCOMPtr<nsIDOMDOMRequest> request;
+    aRv = mBrowserElementAPI->GetAudioChannelMuted((uint32_t)mAudioChannel,
+                                                   getter_AddRefs(request));
+    if (NS_WARN_IF(aRv.Failed())) {
+      return nullptr;
+    }
+
+    return request.forget().downcast<DOMRequest>();
+  }
+
+  nsRefPtr<DOMRequest> domRequest = new DOMRequest(GetOwner());
+
+  nsCOMPtr<nsIRunnable> runnable =
+    new GetMutedRunnable(GetOwner(), mFrameWindow, domRequest, mAudioChannel);
+  NS_DispatchToMainThread(runnable);
+
+  return domRequest.forget();
+}
+
+already_AddRefed<dom::DOMRequest>
+BrowserElementAudioChannel::SetMuted(bool aMuted, ErrorResult& aRv)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  AssertIsInMainProcess();
+
+  if (!mFrameWindow) {
+    nsCOMPtr<nsIDOMDOMRequest> request;
+    aRv = mBrowserElementAPI->SetAudioChannelMuted((uint32_t)mAudioChannel,
+                                                   aMuted,
+                                                   getter_AddRefs(request));
+    if (NS_WARN_IF(aRv.Failed())) {
+      return nullptr;
+    }
+
+    return request.forget().downcast<DOMRequest>();
+  }
+
+  nsRefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
+  MOZ_ASSERT(service);
+
+  service->SetAudioChannelMuted(mFrameWindow, mAudioChannel, aMuted);
+
+  nsRefPtr<DOMRequest> domRequest = new DOMRequest(GetOwner());
+  nsCOMPtr<nsIRunnable> runnable = new FireSuccessRunnable(GetOwner(),
+                                                           mFrameWindow,
+                                                           domRequest,
+                                                           mAudioChannel);
+  NS_DispatchToMainThread(runnable);
+
+  return domRequest.forget();
+}
+
+already_AddRefed<dom::DOMRequest>
+BrowserElementAudioChannel::IsActive(ErrorResult& aRv)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  AssertIsInMainProcess();
+
+  if (mState != eStateUnknown) {
+    nsRefPtr<DOMRequest> domRequest = new DOMRequest(GetOwner());
+
+    nsCOMPtr<nsIRunnable> runnable =
+      new IsActiveRunnable(GetOwner(), mFrameWindow, domRequest, mAudioChannel,
+                           mState == eStateActive);
+    NS_DispatchToMainThread(runnable);
+
+    return domRequest.forget();
+  }
+
+  if (!mFrameWindow) {
+    nsCOMPtr<nsIDOMDOMRequest> request;
+    aRv = mBrowserElementAPI->IsAudioChannelActive((uint32_t)mAudioChannel,
+                                                   getter_AddRefs(request));
+    if (NS_WARN_IF(aRv.Failed())) {
+      return nullptr;
+    }
+
+    return request.forget().downcast<DOMRequest>();
+  }
+
+  nsRefPtr<DOMRequest> domRequest = new DOMRequest(GetOwner());
+
+  nsCOMPtr<nsIRunnable> runnable =
+    new IsActiveRunnable(GetOwner(), mFrameWindow, domRequest, mAudioChannel);
+  NS_DispatchToMainThread(runnable);
+
+  return domRequest.forget();
+}
+
+NS_IMETHODIMP
+BrowserElementAudioChannel::Observe(nsISupports* aSubject, const char* aTopic,
+                                    const char16_t* aData)
+{
+  nsAutoString name;
+  AudioChannelService::GetAudioChannelString(mAudioChannel, name);
+
+  nsAutoCString topic;
+  topic.Assign("audiochannel-activity-");
+  topic.Append(NS_ConvertUTF16toUTF8(name));
+
+  if (strcmp(topic.get(), aTopic)) {
+    return NS_OK;
+  }
+
+  // Message received from the child.
+  if (!mFrameWindow) {
+    if (mTabParent == aSubject) {
+      ProcessStateChanged(aData);
+    }
+
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject);
+  if (NS_WARN_IF(!wrapper)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  uint64_t windowID;
+  nsresult rv = wrapper->GetData(&windowID);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (windowID != mFrameWindow->WindowID()) {
+    return NS_OK;
+  }
+
+  ProcessStateChanged(aData);
+  return NS_OK;
+}
+
+void
+BrowserElementAudioChannel::ProcessStateChanged(const char16_t* aData)
+{
+  nsAutoString value(aData);
+  mState = value.EqualsASCII("active") ? eStateActive : eStateInactive;
+  DispatchTrustedEvent(NS_LITERAL_STRING("activestatechanged"));
+}
+
+} // dom namespace
+} // mozilla namespace
copy from dom/audiochannel/AudioChannelAgent.h
copy to dom/browser-element/BrowserElementAudioChannel.h
--- a/dom/audiochannel/AudioChannelAgent.h
+++ b/dom/browser-element/BrowserElementAudioChannel.h
@@ -1,67 +1,83 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef mozilla_dom_audio_channel_agent_h__
-#define mozilla_dom_audio_channel_agent_h__
+#ifndef mozilla_dom_BrowserElementAudioChannels_h
+#define mozilla_dom_BrowserElementAudioChannels_h
 
-#include "nsIAudioChannelAgent.h"
+#include "mozilla/dom/AudioChannelBinding.h"
+#include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/DOMEventTargetHelper.h"
+#include "mozilla/ErrorResult.h"
 #include "nsCycleCollectionParticipant.h"
-#include "nsCOMPtr.h"
-#include "nsWeakPtr.h"
+#include "nsIObserver.h"
+#include "nsIFrameLoader.h"
+#include "nsWeakReference.h"
+#include "nsWrapperCache.h"
 
-#define NS_AUDIOCHANNELAGENT_CONTRACTID "@mozilla.org/audiochannelagent;1"
-// f27688e2-3dd7-11e2-904e-10bf48d64bd4
-#define NS_AUDIOCHANNELAGENT_CID {0xf27688e2, 0x3dd7, 0x11e2, \
-      {0x90, 0x4e, 0x10, 0xbf, 0x48, 0xd6, 0x4b, 0xd4}}
-
-class nsIDOMWindow;
+class nsIBrowserElementAPI;
+class nsITabParent;
+class nsPIDOMWindow;
 
 namespace mozilla {
 namespace dom {
 
-/* Header file */
-class AudioChannelAgent : public nsIAudioChannelAgent
+class DOMRequest;
+
+class BrowserElementAudioChannel final : public DOMEventTargetHelper
+                                       , public nsSupportsWeakReference
+                                       , public nsIObserver
 {
 public:
-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_NSIAUDIOCHANNELAGENT
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_NSIOBSERVER
+
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(BrowserElementAudioChannel,
+                                           DOMEventTargetHelper)
 
-  NS_DECL_CYCLE_COLLECTION_CLASS(AudioChannelAgent)
+  BrowserElementAudioChannel(nsPIDOMWindow* aWindow,
+                             nsIFrameLoader* aFrameLoader,
+                             nsIBrowserElementAPI* aAPI,
+                             AudioChannel aAudioChannel);
+
+  nsresult Initialize();
+
+  // WebIDL methods
 
-  AudioChannelAgent();
-  virtual void NotifyAudioChannelStateChanged();
+  virtual JSObject* WrapObject(JSContext *aCx,
+                               JS::Handle<JSObject*> aGivenProto) override;
 
-  void WindowVolumeChanged();
+  AudioChannel Name() const;
 
-  nsIDOMWindow* Window() const
-  {
-    return mWindow;
-  }
+  already_AddRefed<dom::DOMRequest> GetVolume(ErrorResult& aRv);
+  already_AddRefed<dom::DOMRequest> SetVolume(float aVolume, ErrorResult& aRv);
+
+  already_AddRefed<dom::DOMRequest> GetMuted(ErrorResult& aRv);
+  already_AddRefed<dom::DOMRequest> SetMuted(bool aMuted, ErrorResult& aRv);
+
+  already_AddRefed<dom::DOMRequest> IsActive(ErrorResult& aRv);
+
+  IMPL_EVENT_HANDLER(activestatechanged);
 
 private:
-  virtual ~AudioChannelAgent();
+  ~BrowserElementAudioChannel();
 
-  // Returns mCallback if that's non-null, or otherwise tries to get an
-  // nsIAudioChannelAgentCallback out of mWeakCallback.
-  already_AddRefed<nsIAudioChannelAgentCallback> GetCallback();
+  void ProcessStateChanged(const char16_t* aData);
 
-  nsresult InitInternal(nsIDOMWindow* aWindow, int32_t aAudioAgentType,
-                        nsIAudioChannelAgentCallback* aCallback,
-                        bool aUseWeakRef, bool aWithVideo=false);
+  nsCOMPtr<nsIFrameLoader> mFrameLoader;
+  nsCOMPtr<nsIBrowserElementAPI> mBrowserElementAPI;
+  nsCOMPtr<nsITabParent> mTabParent;
+  nsCOMPtr<nsPIDOMWindow> mFrameWindow;
+  AudioChannel mAudioChannel;
 
-  nsCOMPtr<nsIDOMWindow> mWindow;
-  nsCOMPtr<nsIAudioChannelAgentCallback> mCallback;
-  nsWeakPtr mWeakCallback;
-  int32_t mAudioChannelType;
-  bool mIsRegToService;
-  bool mVisible;
-  bool mWithVideo;
+  enum {
+    eStateActive,
+    eStateInactive,
+    eStateUnknown
+  } mState;
 };
 
-} // namespace dom
-} // namespace mozilla
-#endif
+} // dom namespace
+} // mozilla namespace
 
+#endif // mozilla_dom_BrowserElementAudioChannels_h
--- a/dom/browser-element/BrowserElementChildPreload.js
+++ b/dom/browser-element/BrowserElementChildPreload.js
@@ -8,16 +8,20 @@ dump("######################## BrowserEl
 
 var BrowserElementIsReady = false;
 
 let { classes: Cc, interfaces: Ci, results: Cr, utils: Cu }  = Components;
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/BrowserElementPromptService.jsm");
 
+XPCOMUtils.defineLazyServiceGetter(this, "acs",
+                                   "@mozilla.org/audiochannel/service;1",
+                                   "nsIAudioChannelService");
+
 let kLongestReturnedString = 128;
 
 function debug(msg) {
   //dump("BrowserElementChildPreload - " + msg + "\n");
 }
 
 function sendAsyncMsg(msg, data) {
   // Ensure that we don't send any messages before BrowserElementChild.js
@@ -46,16 +50,17 @@ function sendSyncMsg(msg, data) {
   data.msg_name = msg;
   return sendSyncMessage('browser-element-api:call', data);
 }
 
 let CERTIFICATE_ERROR_PAGE_PREF = 'security.alternate_certificate_error_page';
 
 const OBSERVED_EVENTS = [
   'xpcom-shutdown',
+  'media-playback',
   'activity-done'
 ];
 
 const COMMAND_MAP = {
   'cut': 'cmd_cut',
   'copy': 'cmd_copyAndCollapseToEnd',
   'paste': 'cmd_paste',
   'selectall': 'cmd_selectAll'
@@ -228,16 +233,21 @@ BrowserElementChild.prototype = {
       "exit-fullscreen": this._recvExitFullscreen.bind(this),
       "activate-next-paint-listener": this._activateNextPaintListener.bind(this),
       "set-input-method-active": this._recvSetInputMethodActive.bind(this),
       "deactivate-next-paint-listener": this._deactivateNextPaintListener.bind(this),
       "do-command": this._recvDoCommand,
       "find-all": this._recvFindAll.bind(this),
       "find-next": this._recvFindNext.bind(this),
       "clear-match": this._recvClearMatch.bind(this),
+      "get-audio-channel-volume": this._recvGetAudioChannelVolume,
+      "set-audio-channel-volume": this._recvSetAudioChannelVolume,
+      "get-audio-channel-muted": this._recvGetAudioChannelMuted,
+      "set-audio-channel-muted": this._recvSetAudioChannelMuted,
+      "get-is-audio-channel-active": this._recvIsAudioChannelActive
     }
 
     addMessageListener("browser-element-api:call", function(aMessage) {
       if (aMessage.data.msg_name in mmCalls) {
         return mmCalls[aMessage.data.msg_name].apply(self, arguments);
       }
     });
 
@@ -265,24 +275,32 @@ BrowserElementChild.prototype = {
     OBSERVED_EVENTS.forEach((aTopic) => {
       Services.obs.addObserver(this, aTopic, false);
     });
   },
 
   observe: function(subject, topic, data) {
     // Ignore notifications not about our document.  (Note that |content| /can/
     // be null; see bug 874900.)
-    if (topic !== 'activity-done' && (!content || subject != content.document))
+
+    if (topic !== 'activity-done' && topic !== 'media-playback' &&
+        (!content || subject !== content.document)) {
       return;
+    }
     if (topic == 'activity-done' && docShell !== subject)
       return;
     switch (topic) {
       case 'activity-done':
         sendAsyncMsg('activitydone', { success: (data == 'activity-success') });
         break;
+      case 'media-playback':
+        if (subject === content) {
+          sendAsyncMsg('mediaplaybackchange', { _payload_: data });
+        }
+        break;
       case 'xpcom-shutdown':
         this._shuttingDown = true;
         break;
     }
   },
 
   /**
    * Called when our TabChildGlobal starts to die.  This is not called when the
@@ -845,16 +863,20 @@ BrowserElementChild.prototype = {
 
     if (ctxMenuId) {
       var menu = e.target.ownerDocument.getElementById(ctxMenuId);
       if (menu) {
         menuData.contextmenu = this._buildMenuObj(menu, '');
       }
     }
 
+    // Pass along the position where the context menu should be located
+    menuData.clientX = e.clientX;
+    menuData.clientY = e.clientY;
+
     // The value returned by the contextmenu sync call is true if the embedder
     // called preventDefault() on its contextmenu event.
     //
     // We call preventDefault() on our contextmenu event if the embedder called
     // preventDefault() on /its/ contextmenu event.  This way, if the embedder
     // ignored the contextmenu event, TabChild will fire a click.
     if (sendSyncMsg('contextmenu', menuData)[0]) {
       e.preventDefault();
@@ -1228,16 +1250,65 @@ BrowserElementChild.prototype = {
 
   _recvDoCommand: function(data) {
     if (this._isCommandEnabled(data.json.command)) {
       this._selectionStateChangedTarget = null;
       docShell.doCommand(COMMAND_MAP[data.json.command]);
     }
   },
 
+  _recvGetAudioChannelVolume: function(data) {
+    debug("Received getAudioChannelVolume message: (" + data.json.id + ")");
+
+    let volume = acs.getAudioChannelVolume(content,
+                                           data.json.args.audioChannel);
+    sendAsyncMsg('got-audio-channel-volume', {
+      id: data.json.id, successRv: volume
+    });
+  },
+
+  _recvSetAudioChannelVolume: function(data) {
+    debug("Received setAudioChannelVolume message: (" + data.json.id + ")");
+
+    acs.setAudioChannelVolume(content,
+                              data.json.args.audioChannel,
+                              data.json.args.volume);
+    sendAsyncMsg('got-set-audio-channel-volume', {
+      id: data.json.id, successRv: true
+    });
+  },
+
+  _recvGetAudioChannelMuted: function(data) {
+    debug("Received getAudioChannelMuted message: (" + data.json.id + ")");
+
+    let muted = acs.getAudioChannelMuted(content, data.json.args.audioChannel);
+    sendAsyncMsg('got-audio-channel-muted', {
+      id: data.json.id, successRv: muted
+    });
+  },
+
+  _recvSetAudioChannelMuted: function(data) {
+    debug("Received setAudioChannelMuted message: (" + data.json.id + ")");
+
+    acs.setAudioChannelMuted(content, data.json.args.audioChannel,
+                             data.json.args.muted);
+    sendAsyncMsg('got-set-audio-channel-muted', {
+      id: data.json.id, successRv: true
+    });
+  },
+
+  _recvIsAudioChannelActive: function(data) {
+    debug("Received isAudioChannelActive message: (" + data.json.id + ")");
+
+    let active = acs.isAudioChannelActive(content, data.json.args.audioChannel);
+    sendAsyncMsg('got-is-audio-channel-active', {
+      id: data.json.id, successRv: active
+    });
+  },
+
   _initFinder: function() {
     if (!this._finder) {
       try {
         this._findLimit = Services.prefs.getIntPref("accessibility.typeaheadfind.matchesCountLimit");
       } catch (e) {
         // Pref not available, assume 0, no match counting.
         this._findLimit = 0;
       }
--- a/dom/browser-element/BrowserElementParent.cpp
+++ b/dom/browser-element/BrowserElementParent.cpp
@@ -10,16 +10,17 @@
 //   #define CreateEvent CreateEventW
 // That messes up our call to EventDispatcher::CreateEvent below.
 
 #ifdef CreateEvent
 #undef CreateEvent
 #endif
 
 #include "BrowserElementParent.h"
+#include "BrowserElementAudioChannel.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/dom/HTMLIFrameElement.h"
 #include "mozilla/dom/ToJSValue.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsVariant.h"
 #include "mozilla/dom/BrowserElementDictionariesBinding.h"
 #include "mozilla/dom/CustomEvent.h"
 
--- a/dom/browser-element/BrowserElementParent.js
+++ b/dom/browser-element/BrowserElementParent.js
@@ -201,20 +201,26 @@ BrowserElementParent.prototype = {
       "fullscreen-origin-change": this._fullscreenOriginChange,
       "exit-dom-fullscreen": this._exitDomFullscreen,
       "got-visible": this._gotDOMRequestResult,
       "visibilitychange": this._childVisibilityChange,
       "got-set-input-method-active": this._gotDOMRequestResult,
       "selectionstatechanged": this._handleSelectionStateChanged,
       "scrollviewchange": this._handleScrollViewChange,
       "caretstatechanged": this._handleCaretStateChanged,
-      "findchange": this._handleFindChange
+      "findchange": this._handleFindChange,
+      "got-audio-channel-volume": this._gotDOMRequestResult,
+      "got-set-audio-channel-volume": this._gotDOMRequestResult,
+      "got-audio-channel-muted": this._gotDOMRequestResult,
+      "got-set-audio-channel-muted": this._gotDOMRequestResult,
+      "got-is-audio-channel-active": this._gotDOMRequestResult
     };
 
     let mmSecuritySensitiveCalls = {
+      "mediaplaybackchange": this._fireEventFromMsg,
       "showmodalprompt": this._handleShowModalPrompt,
       "contextmenu": this._fireCtxMenuEvent,
       "securitychange": this._fireEventFromMsg,
       "locationchange": this._fireEventFromMsg,
       "iconchange": this._fireEventFromMsg,
       "scrollareachanged": this._fireEventFromMsg,
       "titlechange": this._fireProfiledEventFromMsg,
       "opensearch": this._fireEventFromMsg,
@@ -967,16 +973,43 @@ BrowserElementParent.prototype = {
       let nfcContentHelper =
         Cc["@mozilla.org/nfc/content-helper;1"].getService(Ci.nsINfcBrowserAPI);
       nfcContentHelper.setFocusApp(tabId, isFocus);
     } catch(e) {
       // Not all platforms support NFC
     }
   },
 
+  getAudioChannelVolume: function(aAudioChannel) {
+    return this._sendDOMRequest('get-audio-channel-volume',
+                                {audioChannel: aAudioChannel});
+  },
+
+  setAudioChannelVolume: function(aAudioChannel, aVolume) {
+    return this._sendDOMRequest('set-audio-channel-volume',
+                                {audioChannel: aAudioChannel,
+                                 volume: aVolume});
+  },
+
+  getAudioChannelMuted: function(aAudioChannel) {
+    return this._sendDOMRequest('get-audio-channel-muted',
+                                {audioChannel: aAudioChannel});
+  },
+
+  setAudioChannelMuted: function(aAudioChannel, aMuted) {
+    return this._sendDOMRequest('set-audio-channel-muted',
+                                {audioChannel: aAudioChannel,
+                                 muted: aMuted});
+  },
+
+  isAudioChannelActive: function(aAudioChannel) {
+    return this._sendDOMRequest('get-is-audio-channel-active',
+                                {audioChannel: aAudioChannel});
+  },
+
   /**
    * Called when the visibility of the window which owns this iframe changes.
    */
   _ownerVisibilityChange: function() {
     this._sendAsyncMsg('owner-visibility-change',
                        {visible: !this._window.document.hidden});
   },
 
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/browserElement_AudioChannel.js
@@ -0,0 +1,152 @@
+/* Any copyright is dedicated to the public domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Bug 1113086 - tests for AudioChannel API into BrowserElement
+
+"use strict";
+
+SimpleTest.waitForExplicitFinish();
+browserElementTestHelpers.setEnabledPref(true);
+browserElementTestHelpers.addPermission();
+
+SpecialPowers.setBoolPref("media.useAudioChannelService", true);
+
+function noaudio() {
+  var iframe = document.createElement('iframe');
+  iframe.setAttribute('mozbrowser', 'true');
+  iframe.setAttribute('mozapp', 'http://example.org/manifest.webapp');
+  iframe.src = 'http://example.org/tests/dom/browser-element/mochitest/file_empty.html';
+
+  function noaudio_loadend() {
+    ok("allowedAudioChannels" in iframe, "allowedAudioChannels exist");
+    var channels = iframe.allowedAudioChannels;
+    is(channels.length, 1, "1 audio channel by default");
+
+    var ac = channels[0];
+
+    ok(ac instanceof BrowserElementAudioChannel, "Correct class");
+    ok("getVolume" in ac, "ac.getVolume exists");
+    ok("setVolume" in ac, "ac.setVolume exists");
+    ok("getMuted" in ac, "ac.getMuted exists");
+    ok("setMuted" in ac, "ac.setMuted exists");
+    ok("isActive" in ac, "ac.isActive exists");
+
+    new Promise(function(r, rr) {
+      var req = ac.getVolume();
+      ok(req instanceof DOMRequest, "This is a domRequest.");
+      req.onsuccess = function(e) {
+        is(e.target.result, 1.0, "The default volume should be 1.0");
+        r();
+      }
+    })
+
+    .then(function() {
+      return new Promise(function(r, rr) {
+        ac.getMuted().onsuccess = function(e) {
+          is(e.target.result, false, "The default muted value should be false");
+          r();
+        }
+      });
+    })
+
+    .then(function() {
+      return new Promise(function(r, rr) {
+        ac.setVolume(0.8).onsuccess = function() { r(); }
+      });
+    })
+
+    .then(function() {
+      return new Promise(function(r, rr) {
+        ac.getVolume().onsuccess = function(e) {
+          // the actual value is 0.800000011920929..
+          ok(Math.abs(0.8 - e.target.result) < 0.01, "The new volume should be 0.8: " + e.target.result);
+          r();
+        }
+      });
+    })
+
+    .then(function() {
+      return new Promise(function(r, rr) {
+        ac.setVolume(1.0).onsuccess = function() { r(); }
+      });
+    })
+
+    .then(function() {
+      return new Promise(function(r, rr) {
+        ac.setMuted(true).onsuccess = function() { r(); }
+      });
+    })
+
+    .then(function() {
+      return new Promise(function(r, rr) {
+        ac.getMuted().onsuccess = function(e) {
+          is(e.target.result, true, "The new muted value should be true");
+          r();
+        }
+      });
+    })
+
+    .then(function() {
+      return new Promise(function(r, rr) {
+        ac.isActive().onsuccess = function(e) {
+          is(e.target.result, false, "ac.isActive is false: no audio element active.");
+          r();
+        }
+      });
+    })
+
+    .then(runTests);
+  }
+
+  iframe.addEventListener('mozbrowserloadend', noaudio_loadend);
+  document.body.appendChild(iframe);
+}
+
+function audio() {
+  var iframe = document.createElement('iframe');
+  iframe.setAttribute('mozbrowser', 'true');
+  iframe.setAttribute('mozapp', 'http://example.org/manifest.webapp');
+  iframe.src = 'http://example.org/tests/dom/browser-element/mochitest/iframe_file_audio.html';
+
+  function audio_loadend() {
+    ok("allowedAudioChannels" in iframe, "allowedAudioChannels exist");
+    var channels = iframe.allowedAudioChannels;
+    is(channels.length, 1, "1 audio channel by default");
+
+    var ac = channels[0];
+
+    ok(ac instanceof BrowserElementAudioChannel, "Correct class");
+    ok("getVolume" in ac, "ac.getVolume exists");
+    ok("setVolume" in ac, "ac.setVolume exists");
+    ok("getMuted" in ac, "ac.getMuted exists");
+    ok("setMuted" in ac, "ac.setMuted exists");
+    ok("isActive" in ac, "ac.isActive exists");
+
+    ac.onactivestatechanged = function() {
+      ok("activestatechanged event received.");
+      ac.onactivestatechanged = null;
+      runTests();
+    }
+  }
+
+  iframe.addEventListener('mozbrowserloadend', audio_loadend);
+  document.body.appendChild(iframe);
+}
+
+var tests = [ noaudio, audio ];
+
+function runTests() {
+  if (tests.length == 0) {
+    SimpleTest.finish();
+    return;
+  }
+
+  var test = tests.shift();
+  test();
+}
+
+
+addEventListener('load', function() {
+  SimpleTest.executeSoon(runTests);
+});
+
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/browserElement_MediaPlayback.js
@@ -0,0 +1,75 @@
+/* Any copyright is dedicated to the public domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test the mozbrowsermediaplaybackchange event is fired correctly.
+'use strict';
+
+const { Services } = SpecialPowers.Cu.import('resource://gre/modules/Services.jsm');
+
+SimpleTest.waitForExplicitFinish();
+browserElementTestHelpers.setEnabledPref(true);
+browserElementTestHelpers.addPermission();
+
+/**
+ * Content script passed to the child iframe
+ */
+function playMediaScript() {
+  var audio = new content.Audio();
+  content.document.body.appendChild(audio);
+  audio.oncanplay = function() {
+    audio.play();
+  };
+  audio.src = 'audio.ogg';
+}
+
+/**
+ * Creates a simple mozbrowser frame
+ */
+function createFrame() {
+  let iframe = document.createElement('iframe');
+  iframe.setAttribute('mozbrowser', 'true');
+  document.body.appendChild(iframe);
+  return iframe;
+}
+
+function runTest() {
+  SimpleTest.waitForExplicitFinish();
+
+  let iframe = createFrame();
+  let iframe2 = createFrame();
+
+  // When the first iframe is finished loading inject a script to create
+  // an audio element and play it.
+  iframe.addEventListener('mozbrowserloadend', () => {
+    let mm = SpecialPowers.getBrowserFrameMessageManager(iframe);
+    mm.loadFrameScript('data:,(' + playMediaScript.toString() + ')();', false);
+  });
+
+  // Two events should come in, when the audio starts, and stops playing.
+  // The first one should have a detail of 'active' and the second one
+  // should have a detail of 'inactive'.
+  let expectedNextData = 'active';
+  iframe.addEventListener('mozbrowsermediaplaybackchange', (e) => {
+    is(e.detail, expectedNextData, 'Audio detail should be correct')
+    is(e.target, iframe, 'event target should be the first iframe')
+    if (e.detail === 'inactive') {
+      SimpleTest.finish();
+    }
+    expectedNextData = 'inactive';
+  });
+
+  // Make sure an event only goes to the first iframe.
+  iframe2.addEventListener('mozbrowsermediaplaybackchange', (e) => {
+    ok(false,
+       'mozbrowsermediaplaybackchange should dispatch to the correct browser');
+  });
+
+  // Load a simple page to get the process started.
+  iframe.src = browserElementTestHelpers.emptyPage1;
+}
+
+addEventListener('testready', () => {
+  // Audio channel service is needed for events
+  SpecialPowers.pushPrefEnv({"set": [["media.useAudioChannelService", true]]},
+                            runTest);
+});
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/file_audio.html
@@ -0,0 +1,15 @@
+<html>
+<body>
+<audio src="http://mochi.test:8888/tests/dom/browser-element/mochitest/audio.ogg" id="audio" />
+<script>
+var audio = document.getElementById('audio');
+audio.play();
+audio.onended = function() {
+  setTimeout(function() {
+    audio.play();
+  }, 0);
+}
+</script>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/iframe_file_audio.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+<iframe src="file_audio.html"></iframe>
+</body>
+</html>
--- a/dom/browser-element/mochitest/mochitest-oop.ini
+++ b/dom/browser-element/mochitest/mochitest-oop.ini
@@ -48,16 +48,17 @@ skip-if = (toolkit == 'gonk' && !debug)
 [test_browserElement_oop_FrameWrongURI.html]
 skip-if = (toolkit == 'gonk' && !debug)
 [test_browserElement_oop_GetScreenshot.html]
 [test_browserElement_oop_GetScreenshotDppx.html]
 [test_browserElement_oop_Iconchange.html]
 [test_browserElement_oop_LoadEvents.html]
 [test_browserElement_oop_Manifestchange.html]
 [test_browserElement_oop_Metachange.html]
+[test_browserElement_oop_MediaPlayback.html]
 [test_browserElement_oop_OpenMixedProcess.html]
 skip-if = (toolkit == 'gonk' && !debug)
 [test_browserElement_oop_OpenNamed.html]
 skip-if = (toolkit == 'gonk' && !debug)
 [test_browserElement_oop_OpenWindow.html]
 skip-if = (toolkit == 'gonk' && !debug)
 [test_browserElement_oop_OpenWindowDifferentOrigin.html]
 skip-if = (toolkit == 'gonk' && !debug)
@@ -103,8 +104,9 @@ disabled = bug 930449
 # Disabled until bug 924771 makes them stop timing out
 [test_browserElement_oop_CloseFromOpener.html]
 disabled = bug 924771
 [test_browserElement_oop_CloseApp.html]
 disabled = bug 924771
 [test_browserElement_oop_ExposableURI.html]
 disabled = bug 924771
 [test_browserElement_oop_GetContentDimensions.html]
+[test_browserElement_oop_AudioChannel.html]
--- a/dom/browser-element/mochitest/mochitest.ini
+++ b/dom/browser-element/mochitest/mochitest.ini
@@ -34,16 +34,17 @@ support-files =
   browserElement_ForwardName.js
   browserElement_FrameWrongURI.js
   browserElement_GetScreenshot.js
   browserElement_GetScreenshotDppx.js
   browserElement_Iconchange.js
   browserElement_LoadEvents.js
   browserElement_Manifestchange.js
   browserElement_Metachange.js
+  browserElement_MediaPlayback.js
   browserElement_NextPaint.js
   browserElement_OpenNamed.js
   browserElement_OpenTab.js
   browserElement_OpenWindow.js
   browserElement_OpenWindowDifferentOrigin.js
   browserElement_OpenWindowInFrame.js
   browserElement_OpenWindowRejected.js
   browserElement_Opensearch.js
@@ -69,16 +70,17 @@ support-files =
   browserElement_TopBarrier.js
   browserElement_VisibilityChange.js
   browserElement_XFrameOptions.js
   browserElement_XFrameOptionsAllowFrom.js
   browserElement_XFrameOptionsDeny.js
   browserElement_XFrameOptionsSameOrigin.js
   browserElement_XFrameOptionsSameOrigin.js
   browserElement_GetContentDimensions.js
+  browserElement_AudioChannel.js
   file_browserElement_AlertInFrame.html
   file_browserElement_AlertInFrame_Inner.html
   file_browserElement_AllowEmbedAppsInNestedOOIframe.html
   file_browserElement_AppFramePermission.html
   file_browserElement_AppWindowNamespace.html
   file_browserElement_Viewmode.html
   file_browserElement_ThemeColor.html
   file_browserElement_BrowserWindowNamespace.html
@@ -116,16 +118,18 @@ support-files =
   file_empty.html
   file_empty_script.js
   file_focus.html
   file_http_401_response.sjs
   file_http_407_response.sjs
   file_inputmethod.html
   file_post_request.html
   file_wyciwyg.html
+  file_audio.html
+  iframe_file_audio.html
 
 # 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]
@@ -167,16 +171,17 @@ disabled = bug 1022281
 skip-if = (toolkit == 'gonk' && !debug)
 [test_browserElement_inproc_GetScreenshot.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only
 [test_browserElement_inproc_GetScreenshotDppx.html]
 [test_browserElement_inproc_Iconchange.html]
 [test_browserElement_inproc_LoadEvents.html]
 [test_browserElement_inproc_Manifestchange.html]
 [test_browserElement_inproc_Metachange.html]
+[test_browserElement_inproc_MediaPlayback.html]
 [test_browserElement_inproc_NextPaint.html]
 [test_browserElement_inproc_OpenNamed.html]
 skip-if = (toolkit == 'gonk' && !debug)
 [test_browserElement_inproc_OpenTab.html]
 disabled = won't work as Firefox desktop will intercept ctrl-click
 [test_browserElement_inproc_OpenWindow.html]
 skip-if = (toolkit == 'gonk' && !debug)
 [test_browserElement_inproc_OpenWindowDifferentOrigin.html]
@@ -216,8 +221,9 @@ skip-if = (toolkit == 'gonk' && !debug)
 [test_browserElement_inproc_XFrameOptionsDeny.html]
 [test_browserElement_inproc_XFrameOptionsSameOrigin.html]
 [test_browserElement_oop_NextPaint.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 936226
 # Disabled due to https://bugzilla.mozilla.org/show_bug.cgi?id=774100
 [test_browserElement_inproc_Reload.html]
 disabled = bug 774100
 [test_browserElement_inproc_GetContentDimensions.html]
+[test_browserElement_inproc_AudioChannel.html]
rename from dom/audiochannel/tests/file_telephonyPolicy.html
rename to dom/browser-element/mochitest/test_browserElement_inproc_AudioChannel.html
--- a/dom/audiochannel/tests/file_telephonyPolicy.html
+++ b/dom/browser-element/mochitest/test_browserElement_inproc_AudioChannel.html
@@ -1,18 +1,13 @@
 <!DOCTYPE HTML>
 <html>
 <head>
-  <meta charset="utf-8">
-  <title>Test Telephony Channel Policy</title>
+  <title>Test of browser element audioChannel.</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>
-<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>
+<script type="application/javascript;version=1.7" src="browserElement_AudioChannel.js">
+</script>
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/test_browserElement_inproc_MediaPlayback.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1180824
+-->
+<head>
+  <title>Test for Bug 1180824</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=1180824">Mozilla Bug 1180824</a>
+
+<script type="application/javascript;version=1.7" src="browserElement_MediaPlayback.js">
+</script>
+</body>
+</html>
copy from dom/audiochannel/tests/file_telephonyPolicy.html
copy to dom/browser-element/mochitest/test_browserElement_oop_AudioChannel.html
--- a/dom/audiochannel/tests/file_telephonyPolicy.html
+++ b/dom/browser-element/mochitest/test_browserElement_oop_AudioChannel.html
@@ -1,18 +1,13 @@
 <!DOCTYPE HTML>
 <html>
 <head>
-  <meta charset="utf-8">
-  <title>Test Telephony Channel Policy</title>
+  <title>Test of browser element audioChannel.</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>
-<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>
+<script type="application/javascript;version=1.7" src="browserElement_AudioChannel.js">
+</script>
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/test_browserElement_oop_MediaPlayback.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1180824
+-->
+<head>
+  <title>Test for Bug 1180824</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=1180824">Mozilla Bug 1180824</a>
+
+<script type="application/javascript;version=1.7" src="browserElement_MediaPlayback.js">
+</script>
+</body>
+</html>
--- a/dom/browser-element/moz.build
+++ b/dom/browser-element/moz.build
@@ -3,17 +3,22 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 EXPORTS.mozilla += [
     'BrowserElementParent.h',
 ]
 
+EXPORTS.mozilla.dom += [
+    'BrowserElementAudioChannel.h',
+]
+
 SOURCES += [
+    'BrowserElementAudioChannel.cpp',
     'BrowserElementParent.cpp',
 ]
 
 XPIDL_SOURCES += [
     'nsIBrowserElementAPI.idl',
 ]
 
 XPIDL_MODULE = 'browser-element'
--- a/dom/browser-element/nsIBrowserElementAPI.idl
+++ b/dom/browser-element/nsIBrowserElementAPI.idl
@@ -21,17 +21,17 @@ interface nsIBrowserElementNextPaintList
     { 0x651db7e3, 0x1734, 0x4536,                               \
       { 0xb1, 0x5a, 0x5b, 0x3a, 0xe6, 0x44, 0x13, 0x4c } }
 %}
 
 /**
  * Interface to the BrowserElementParent implementation. All methods
  * but setFrameLoader throw when the remote process is dead.
  */
-[scriptable, uuid(8ecb598c-f886-11e4-9915-778f934fbf93)]
+[scriptable, uuid(daa264b2-54df-4fc7-89b7-c9d02167c5d4)]
 interface nsIBrowserElementAPI : nsISupports
 {
   const long FIND_CASE_SENSITIVE = 0;
   const long FIND_CASE_INSENSITIVE = 1;
 
   const long FIND_FORWARD = 0;
   const long FIND_BACKWARD = 1;
 
@@ -77,10 +77,18 @@ interface nsIBrowserElementAPI : nsISupp
   void findNext(in long direction);
   void clearMatch();
 
   void addNextPaintListener(in nsIBrowserElementNextPaintListener listener);
   void removeNextPaintListener(in nsIBrowserElementNextPaintListener listener);
 
   nsIDOMDOMRequest setInputMethodActive(in boolean isActive);
 
+  nsIDOMDOMRequest getAudioChannelVolume(in uint32_t audioChannel);
+  nsIDOMDOMRequest setAudioChannelVolume(in uint32_t audioChannel, in float volume);
+
+  nsIDOMDOMRequest getAudioChannelMuted(in uint32_t audioChannel);
+  nsIDOMDOMRequest setAudioChannelMuted(in uint32_t audioChannel, in bool muted);
+
+  nsIDOMDOMRequest isAudioChannelActive(in uint32_t audioChannel);
+
   void setNFCFocus(in boolean isFocus);
 };
--- a/dom/cache/Context.cpp
+++ b/dom/cache/Context.cpp
@@ -78,19 +78,19 @@ public:
     MOZ_ASSERT(mConnection);
   }
 
 private:
   ~Data()
   {
     // We could proxy release our data here, but instead just assert.  The
     // Context code should guarantee that we are destroyed on the target
-    // thread.  If we're not, then QuotaManager might race and try to clear the
-    // origin out from under us.
-    MOZ_ASSERT(mTarget == NS_GetCurrentThread());
+    // thread once the connection is initialized.  If we're not, then
+    // QuotaManager might race and try to clear the origin out from under us.
+    MOZ_ASSERT_IF(mConnection, mTarget == NS_GetCurrentThread());
   }
 
   nsCOMPtr<nsIThread> mTarget;
   nsCOMPtr<mozIStorageConnection> mConnection;
 
   // Threadsafe counting because we're created on the PBackground thread
   // and destroyed on the target IO thread.
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Context::Data)
--- a/dom/camera/DOMCameraControl.cpp
+++ b/dom/camera/DOMCameraControl.cpp
@@ -1096,17 +1096,17 @@ nsDOMCameraControl::Shutdown()
   }
 }
 
 void
 nsDOMCameraControl::ReleaseAudioChannelAgent()
 {
 #ifdef MOZ_B2G
   if (mAudioChannelAgent) {
-    mAudioChannelAgent->StopPlaying();
+    mAudioChannelAgent->NotifyStoppedPlaying();
     mAudioChannelAgent = nullptr;
   }
 #endif
 }
 
 nsresult
 nsDOMCameraControl::NotifyRecordingStatusChange(const nsString& aMsg)
 {
@@ -1130,18 +1130,22 @@ nsDOMCameraControl::NotifyRecordingStatu
     mAudioChannelAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1");
     if (!mAudioChannelAgent) {
       return NS_ERROR_UNEXPECTED;
     }
 
     // Camera app will stop recording when it falls to the background, so no callback is necessary.
     mAudioChannelAgent->Init(mWindow, (int32_t)AudioChannel::Content, nullptr);
     // Video recording doesn't output any sound, so it's not necessary to check canPlay.
-    int32_t canPlay;
-    mAudioChannelAgent->StartPlaying(&canPlay);
+    float volume = 0.0;
+    bool muted = true;
+    rv = mAudioChannelAgent->NotifyStartedPlaying(&volume, &muted);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
   }
 #endif
   return rv;
 }
 
 already_AddRefed<Promise>
 nsDOMCameraControl::CreatePromise(ErrorResult& aRv)
 {
--- a/dom/canvas/WebGL1Context.cpp
+++ b/dom/canvas/WebGL1Context.cpp
@@ -2,28 +2,30 @@
 /* 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 "WebGL1Context.h"
 
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "mozilla/Telemetry.h"
+#include "WebGLFormats.h"
 
 namespace mozilla {
 
 /*static*/ WebGL1Context*
 WebGL1Context::Create()
 {
     return new WebGL1Context();
 }
 
 WebGL1Context::WebGL1Context()
     : WebGLContext()
 {
+    mFormatUsage = Move(webgl::FormatUsageAuthority::CreateForWebGL1());
 }
 
 WebGL1Context::~WebGL1Context()
 {
 }
 
 JSObject*
 WebGL1Context::WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto)
--- a/dom/canvas/WebGL2Context.cpp
+++ b/dom/canvas/WebGL2Context.cpp
@@ -1,30 +1,33 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebGL2Context.h"
 
 #include "GLContext.h"
-#include "WebGLBuffer.h"
-#include "WebGLTransformFeedback.h"
 #include "mozilla/dom/WebGL2RenderingContextBinding.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Telemetry.h"
+#include "WebGLBuffer.h"
+#include "WebGLFormats.h"
+#include "WebGLTransformFeedback.h"
 
 namespace mozilla {
 
 WebGL2Context::WebGL2Context()
     : WebGLContext()
 {
     MOZ_ASSERT(IsSupported(), "not supposed to create a WebGL2Context"
                               "context when not supported");
+
+    mFormatUsage = Move(webgl::FormatUsageAuthority::CreateForWebGL2());
 }
 
 WebGL2Context::~WebGL2Context()
 {
 
 }
 
 /*static*/ bool
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -11,16 +11,17 @@
 #include "mozilla/EnumeratedArray.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/WeakPtr.h"
 
 #include "GLDefs.h"
 #include "WebGLActiveInfo.h"
 #include "WebGLContextUnchecked.h"
+#include "WebGLFormats.h"
 #include "WebGLObjectModel.h"
 #include "WebGLRenderbuffer.h"
 #include "WebGLTexture.h"
 #include "WebGLShaderValidator.h"
 #include "WebGLStrongTypes.h"
 #include <stdarg.h>
 
 #include "nsTArray.h"
@@ -1603,16 +1604,19 @@ protected:
 
     nsRefPtr<WebGLObserver> mContextObserver;
 
 public:
     // console logging helpers
     void GenerateWarning(const char* fmt, ...);
     void GenerateWarning(const char* fmt, va_list ap);
 
+    UniquePtr<webgl::FormatUsageAuthority> mFormatUsage;
+
+    // Friend list
     friend class WebGLTexture;
     friend class WebGLFramebuffer;
     friend class WebGLRenderbuffer;
     friend class WebGLProgram;
     friend class WebGLQuery;
     friend class WebGLBuffer;
     friend class WebGLSampler;
     friend class WebGLShader;
new file mode 100644
--- /dev/null
+++ b/dom/canvas/WebGLFormats.cpp
@@ -0,0 +1,794 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WebGLFormats.h"
+
+#include "mozilla/StaticMutex.h"
+
+namespace mozilla {
+namespace webgl {
+
+//////////////////////////////////////////////////////////////////////////////////////////
+
+std::map<EffectiveFormat, const CompressedFormatInfo> gCompressedFormatInfoMap;
+std::map<EffectiveFormat, const FormatInfo> gFormatInfoMap;
+std::map<UnpackTuple, const FormatInfo*> gUnpackTupleMap;
+std::map<GLenum, const FormatInfo*> gSizedFormatMap;
+
+static const CompressedFormatInfo*
+GetCompressedFormatInfo(EffectiveFormat format)
+{
+    MOZ_ASSERT(!gCompressedFormatInfoMap.empty());
+    auto itr = gCompressedFormatInfoMap.find(format);
+    if (itr == gCompressedFormatInfoMap.end())
+        return nullptr;
+
+    return &(itr->second);
+}
+
+static const FormatInfo*
+GetFormatInfo_NoLock(EffectiveFormat format)
+{
+    MOZ_ASSERT(!gFormatInfoMap.empty());
+    auto itr = gFormatInfoMap.find(format);
+    if (itr == gFormatInfoMap.end())
+        return nullptr;
+
+    return &(itr->second);
+}
+
+template<typename K, typename V, typename K2, typename V2>
+static void
+AlwaysInsert(std::map<K,V>& dest, const K2& key, const V2& val)
+{
+    auto res = dest.insert({ key, val });
+    bool didInsert = res.second;
+    MOZ_ALWAYS_TRUE(didInsert);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+
+static void
+AddCompressedFormatInfo(EffectiveFormat format, uint16_t bitsPerBlock, uint8_t blockWidth,
+                        uint8_t blockHeight, bool requirePOT,
+                        SubImageUpdateBehavior subImageUpdateBehavior)
+{
+    MOZ_ASSERT(bitsPerBlock % 8 == 0);
+    uint16_t bytesPerBlock = bitsPerBlock / 8; // The specs always state these in bits,
+                                               // but it's only ever useful to us as
+                                               // bytes.
+    MOZ_ASSERT(bytesPerBlock <= 255);
+
+    const CompressedFormatInfo info = { format, uint8_t(bytesPerBlock), blockWidth,
+                                        blockHeight, requirePOT, subImageUpdateBehavior };
+    AlwaysInsert(gCompressedFormatInfoMap, format, info);
+}
+
+static void
+InitCompressedFormatInfo()
+{
+    // GLES 3.0.4, p147, table 3.19
+    // GLES 3.0.4, p286+, $C.1 "ETC Compressed Texture Image Formats"
+    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGB8_ETC2                     ,  64, 4, 4, false, SubImageUpdateBehavior::BlockAligned);
+    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_SRGB8_ETC2                    ,  64, 4, 4, false, SubImageUpdateBehavior::BlockAligned);
+    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGBA8_ETC2_EAC                , 128, 4, 4, false, SubImageUpdateBehavior::BlockAligned);
+    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_SRGB8_ALPHA8_ETC2_EAC         , 128, 4, 4, false, SubImageUpdateBehavior::BlockAligned);
+    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_R11_EAC                       ,  64, 4, 4, false, SubImageUpdateBehavior::BlockAligned);
+    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RG11_EAC                      , 128, 4, 4, false, SubImageUpdateBehavior::BlockAligned);
+    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_SIGNED_R11_EAC                ,  64, 4, 4, false, SubImageUpdateBehavior::BlockAligned);
+    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_SIGNED_RG11_EAC               , 128, 4, 4, false, SubImageUpdateBehavior::BlockAligned);
+    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 ,  64, 4, 4, false, SubImageUpdateBehavior::BlockAligned);
+    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2,  64, 4, 4, false, SubImageUpdateBehavior::BlockAligned);
+
+    // AMD_compressed_ATC_texture
+    AddCompressedFormatInfo(EffectiveFormat::ATC_RGB_AMD                    ,  64, 4, 4, false, SubImageUpdateBehavior::Forbidden);
+    AddCompressedFormatInfo(EffectiveFormat::ATC_RGBA_EXPLICIT_ALPHA_AMD    , 128, 4, 4, false, SubImageUpdateBehavior::Forbidden);
+    AddCompressedFormatInfo(EffectiveFormat::ATC_RGBA_INTERPOLATED_ALPHA_AMD, 128, 4, 4, false, SubImageUpdateBehavior::Forbidden);
+
+    // EXT_texture_compression_s3tc
+    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGB_S3TC_DXT1 ,  64, 4, 4, false, SubImageUpdateBehavior::BlockAligned);
+    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGBA_S3TC_DXT1,  64, 4, 4, false, SubImageUpdateBehavior::BlockAligned);
+    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGBA_S3TC_DXT3, 128, 4, 4, false, SubImageUpdateBehavior::BlockAligned);
+    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGBA_S3TC_DXT5, 128, 4, 4, false, SubImageUpdateBehavior::BlockAligned);
+
+    // IMG_texture_compression_pvrtc
+    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGB_PVRTC_4BPPV1 , 256,  8, 8, true, SubImageUpdateBehavior::FullOnly);
+    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGBA_PVRTC_4BPPV1, 256,  8, 8, true, SubImageUpdateBehavior::FullOnly);
+    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGB_PVRTC_2BPPV1 , 256, 16, 8, true, SubImageUpdateBehavior::FullOnly);
+    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGBA_PVRTC_2BPPV1, 256, 16, 8, true, SubImageUpdateBehavior::FullOnly);
+
+    // OES_compressed_ETC1_RGB8_texture
+    AddCompressedFormatInfo(EffectiveFormat::ETC1_RGB8, 64, 4, 4, false, SubImageUpdateBehavior::Forbidden);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+
+static void
+AddFormatInfo(EffectiveFormat format, const char* name, uint8_t bytesPerPixel,
+              UnsizedFormat unsizedFormat, ComponentType colorComponentType)
+{
+    bool hasColor = false;
+    bool hasAlpha = false;
+    bool hasDepth = false;
+    bool hasStencil = false;
+
+    switch (unsizedFormat) {
+    case UnsizedFormat::L:
+    case UnsizedFormat::R:
+    case UnsizedFormat::RG:
+    case UnsizedFormat::RGB:
+        hasColor = true;
+        break;
+    case UnsizedFormat::A:
+        hasAlpha = true;
+        break;
+    case UnsizedFormat::LA:
+    case UnsizedFormat::RGBA:
+        hasColor = true;
+        hasAlpha = true;
+        break;
+    case UnsizedFormat::D:
+        hasDepth = true;
+        break;
+    case UnsizedFormat::S:
+        hasStencil = true;
+        break;
+    case UnsizedFormat::DS:
+        hasDepth = true;
+        hasStencil = true;
+        break;
+    default:
+        MOZ_CRASH("Missing UnsizedFormat case.");
+    }
+
+    const CompressedFormatInfo* compressedFormatInfo = GetCompressedFormatInfo(format);
+    MOZ_ASSERT(!bytesPerPixel == bool(compressedFormatInfo));
+
+    const FormatInfo info = { format, name, unsizedFormat, colorComponentType,
+                              bytesPerPixel, hasColor, hasAlpha, hasDepth, hasStencil,
+                              compressedFormatInfo };
+    AlwaysInsert(gFormatInfoMap, format, info);
+}
+
+static void
+InitFormatInfoMap()
+{
+#ifdef FOO
+#error FOO is already defined!
+#endif
+
+#define FOO(x) EffectiveFormat::x, #x
+
+    // GLES 3.0.4, p130-132, table 3.13
+    AddFormatInfo(FOO(R8            ),  1, UnsizedFormat::R   , ComponentType::NormUInt    );
+    AddFormatInfo(FOO(R8_SNORM      ),  1, UnsizedFormat::R   , ComponentType::NormInt     );
+    AddFormatInfo(FOO(RG8           ),  2, UnsizedFormat::RG  , ComponentType::NormUInt    );
+    AddFormatInfo(FOO(RG8_SNORM     ),  2, UnsizedFormat::RG  , ComponentType::NormInt     );
+    AddFormatInfo(FOO(RGB8          ),  3, UnsizedFormat::RGB , ComponentType::NormUInt    );
+    AddFormatInfo(FOO(RGB8_SNORM    ),  3, UnsizedFormat::RGB , ComponentType::NormInt     );
+    AddFormatInfo(FOO(RGB565        ),  2, UnsizedFormat::RGB , ComponentType::NormUInt    );
+    AddFormatInfo(FOO(RGBA4         ),  2, UnsizedFormat::RGBA, ComponentType::NormUInt    );
+    AddFormatInfo(FOO(RGB5_A1       ),  2, UnsizedFormat::RGBA, ComponentType::NormUInt    );
+    AddFormatInfo(FOO(RGBA8         ),  4, UnsizedFormat::RGBA, ComponentType::NormUInt    );
+    AddFormatInfo(FOO(RGBA8_SNORM   ),  4, UnsizedFormat::RGBA, ComponentType::NormInt     );
+    AddFormatInfo(FOO(RGB10_A2      ),  4, UnsizedFormat::RGBA, ComponentType::NormUInt    );
+    AddFormatInfo(FOO(RGB10_A2UI    ),  4, UnsizedFormat::RGBA, ComponentType::UInt        );
+
+    AddFormatInfo(FOO(SRGB8         ),  3, UnsizedFormat::RGB , ComponentType::NormUIntSRGB);
+    AddFormatInfo(FOO(SRGB8_ALPHA8  ),  4, UnsizedFormat::RGBA, ComponentType::NormUIntSRGB);
+
+    AddFormatInfo(FOO(R16F          ),  2, UnsizedFormat::R   , ComponentType::Float       );
+    AddFormatInfo(FOO(RG16F         ),  4, UnsizedFormat::RG  , ComponentType::Float       );
+    AddFormatInfo(FOO(RGB16F        ),  6, UnsizedFormat::RGB , ComponentType::Float       );
+    AddFormatInfo(FOO(RGBA16F       ),  8, UnsizedFormat::RGBA, ComponentType::Float       );
+    AddFormatInfo(FOO(R32F          ),  4, UnsizedFormat::R   , ComponentType::Float       );
+    AddFormatInfo(FOO(RG32F         ),  8, UnsizedFormat::RG  , ComponentType::Float       );
+    AddFormatInfo(FOO(RGB32F        ), 12, UnsizedFormat::RGB , ComponentType::Float       );
+    AddFormatInfo(FOO(RGBA32F       ), 16, UnsizedFormat::RGBA, ComponentType::Float       );
+
+    AddFormatInfo(FOO(R11F_G11F_B10F),  4, UnsizedFormat::RGB , ComponentType::Float       );
+    AddFormatInfo(FOO(RGB9_E5       ),  4, UnsizedFormat::RGB , ComponentType::SharedExp   );
+
+    AddFormatInfo(FOO(R8I           ),  1, UnsizedFormat::R   , ComponentType::Int         );
+    AddFormatInfo(FOO(R8UI          ),  1, UnsizedFormat::R   , ComponentType::UInt        );
+    AddFormatInfo(FOO(R16I          ),  2, UnsizedFormat::R   , ComponentType::Int         );
+    AddFormatInfo(FOO(R16UI         ),  2, UnsizedFormat::R   , ComponentType::UInt        );
+    AddFormatInfo(FOO(R32I          ),  4, UnsizedFormat::R   , ComponentType::Int         );
+    AddFormatInfo(FOO(R32UI         ),  4, UnsizedFormat::R   , ComponentType::UInt        );
+
+    AddFormatInfo(FOO(RG8I          ),  2, UnsizedFormat::RG  , ComponentType::Int         );
+    AddFormatInfo(FOO(RG8UI         ),  2, UnsizedFormat::RG  , ComponentType::UInt        );
+    AddFormatInfo(FOO(RG16I         ),  4, UnsizedFormat::RG  , ComponentType::Int         );
+    AddFormatInfo(FOO(RG16UI        ),  4, UnsizedFormat::RG  , ComponentType::UInt        );
+    AddFormatInfo(FOO(RG32I         ),  8, UnsizedFormat::RG  , ComponentType::Int         );
+    AddFormatInfo(FOO(RG32UI        ),  8, UnsizedFormat::RG  , ComponentType::UInt        );
+
+    AddFormatInfo(FOO(RGB8I         ),  3, UnsizedFormat::RGB , ComponentType::Int         );
+    AddFormatInfo(FOO(RGB8UI        ),  3, UnsizedFormat::RGB , ComponentType::UInt        );
+    AddFormatInfo(FOO(RGB16I        ),  6, UnsizedFormat::RGB , ComponentType::Int         );
+    AddFormatInfo(FOO(RGB16UI       ),  6, UnsizedFormat::RGB , ComponentType::UInt        );
+    AddFormatInfo(FOO(RGB32I        ), 12, UnsizedFormat::RGB , ComponentType::Int         );
+    AddFormatInfo(FOO(RGB32UI       ), 12, UnsizedFormat::RGB , ComponentType::UInt        );
+
+    AddFormatInfo(FOO(RGBA8I        ),  4, UnsizedFormat::RGBA, ComponentType::Int         );
+    AddFormatInfo(FOO(RGBA8UI       ),  4, UnsizedFormat::RGBA, ComponentType::UInt        );
+    AddFormatInfo(FOO(RGBA16I       ),  8, UnsizedFormat::RGBA, ComponentType::Int         );
+    AddFormatInfo(FOO(RGBA16UI      ),  8, UnsizedFormat::RGBA, ComponentType::UInt        );
+    AddFormatInfo(FOO(RGBA32I       ), 16, UnsizedFormat::RGBA, ComponentType::Int         );
+    AddFormatInfo(FOO(RGBA32UI      ), 16, UnsizedFormat::RGBA, ComponentType::UInt        );
+
+    // GLES 3.0.4, p133, table 3.14
+    AddFormatInfo(FOO(DEPTH_COMPONENT16 ), 2, UnsizedFormat::D , ComponentType::None);
+    AddFormatInfo(FOO(DEPTH_COMPONENT24 ), 3, UnsizedFormat::D , ComponentType::None);
+    AddFormatInfo(FOO(DEPTH_COMPONENT32F), 4, UnsizedFormat::D , ComponentType::None);
+    AddFormatInfo(FOO(DEPTH24_STENCIL8  ), 4, UnsizedFormat::DS, ComponentType::None);
+    AddFormatInfo(FOO(DEPTH32F_STENCIL8 ), 5, UnsizedFormat::DS, ComponentType::None);
+
+    // GLES 3.0.4, p205-206, "Required Renderbuffer Formats"
+    AddFormatInfo(FOO(STENCIL_INDEX8), 1, UnsizedFormat::S, ComponentType::None);
+
+    // GLES 3.0.4, p128, table 3.12.
+    AddFormatInfo(FOO(Luminance8Alpha8), 2, UnsizedFormat::LA, ComponentType::NormUInt);
+    AddFormatInfo(FOO(Luminance8      ), 1, UnsizedFormat::L , ComponentType::NormUInt);
+    AddFormatInfo(FOO(Alpha8          ), 1, UnsizedFormat::A , ComponentType::None    );
+
+    // GLES 3.0.4, p147, table 3.19
+    // GLES 3.0.4  p286+  $C.1 "ETC Compressed Texture Image Formats"
+    AddFormatInfo(FOO(COMPRESSED_RGB8_ETC2                     ), 0, UnsizedFormat::RGB , ComponentType::NormUInt    );
+    AddFormatInfo(FOO(COMPRESSED_SRGB8_ETC2                    ), 0, UnsizedFormat::RGB , ComponentType::NormUIntSRGB);
+    AddFormatInfo(FOO(COMPRESSED_RGBA8_ETC2_EAC                ), 0, UnsizedFormat::RGBA, ComponentType::NormUInt    );
+    AddFormatInfo(FOO(COMPRESSED_SRGB8_ALPHA8_ETC2_EAC         ), 0, UnsizedFormat::RGBA, ComponentType::NormUIntSRGB);
+    AddFormatInfo(FOO(COMPRESSED_R11_EAC                       ), 0, UnsizedFormat::R   , ComponentType::NormUInt    );
+    AddFormatInfo(FOO(COMPRESSED_RG11_EAC                      ), 0, UnsizedFormat::RG  , ComponentType::NormUInt    );
+    AddFormatInfo(FOO(COMPRESSED_SIGNED_R11_EAC                ), 0, UnsizedFormat::R   , ComponentType::NormInt     );
+    AddFormatInfo(FOO(COMPRESSED_SIGNED_RG11_EAC               ), 0, UnsizedFormat::RG  , ComponentType::NormInt     );
+    AddFormatInfo(FOO(COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 ), 0, UnsizedFormat::RGBA, ComponentType::NormUInt    );
+    AddFormatInfo(FOO(COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2), 0, UnsizedFormat::RGBA, ComponentType::NormUIntSRGB);
+
+    // AMD_compressed_ATC_texture
+    AddFormatInfo(FOO(ATC_RGB_AMD                    ), 0, UnsizedFormat::RGB , ComponentType::NormUInt);
+    AddFormatInfo(FOO(ATC_RGBA_EXPLICIT_ALPHA_AMD    ), 0, UnsizedFormat::RGBA, ComponentType::NormUInt);
+    AddFormatInfo(FOO(ATC_RGBA_INTERPOLATED_ALPHA_AMD), 0, UnsizedFormat::RGBA, ComponentType::NormUInt);
+
+    // EXT_texture_compression_s3tc
+    AddFormatInfo(FOO(COMPRESSED_RGB_S3TC_DXT1 ), 0, UnsizedFormat::RGB , ComponentType::NormUInt);
+    AddFormatInfo(FOO(COMPRESSED_RGBA_S3TC_DXT1), 0, UnsizedFormat::RGBA, ComponentType::NormUInt);
+    AddFormatInfo(FOO(COMPRESSED_RGBA_S3TC_DXT3), 0, UnsizedFormat::RGBA, ComponentType::NormUInt);
+    AddFormatInfo(FOO(COMPRESSED_RGBA_S3TC_DXT5), 0, UnsizedFormat::RGBA, ComponentType::NormUInt);
+
+    // IMG_texture_compression_pvrtc
+    AddFormatInfo(FOO(COMPRESSED_RGB_PVRTC_4BPPV1 ), 0, UnsizedFormat::RGB , ComponentType::NormUInt);
+    AddFormatInfo(FOO(COMPRESSED_RGBA_PVRTC_4BPPV1), 0, UnsizedFormat::RGBA, ComponentType::NormUInt);
+    AddFormatInfo(FOO(COMPRESSED_RGB_PVRTC_2BPPV1 ), 0, UnsizedFormat::RGB , ComponentType::NormUInt);
+    AddFormatInfo(FOO(COMPRESSED_RGBA_PVRTC_2BPPV1), 0, UnsizedFormat::RGBA, ComponentType::NormUInt);
+
+    // OES_compressed_ETC1_RGB8_texture
+    AddFormatInfo(FOO(ETC1_RGB8), 0, UnsizedFormat::RGB, ComponentType::NormUInt);
+
+#undef FOO
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+
+static void
+AddUnpackTuple(GLenum unpackFormat, GLenum unpackType, EffectiveFormat effectiveFormat)
+{
+    const UnpackTuple unpack = { unpackFormat, unpackType };
+    const FormatInfo* info = GetFormatInfo_NoLock(effectiveFormat);
+    MOZ_ASSERT(info);
+
+    AlwaysInsert(gUnpackTupleMap, unpack, info);
+}
+
+static void
+InitUnpackTupleMap()
+{
+    AddUnpackTuple(LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE         , EffectiveFormat::RGBA8  );
+    AddUnpackTuple(LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_SHORT_4_4_4_4, EffectiveFormat::RGBA4  );
+    AddUnpackTuple(LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_SHORT_5_5_5_1, EffectiveFormat::RGB5_A1);
+    AddUnpackTuple(LOCAL_GL_RGB , LOCAL_GL_UNSIGNED_BYTE         , EffectiveFormat::RGB8   );
+    AddUnpackTuple(LOCAL_GL_RGB , LOCAL_GL_UNSIGNED_SHORT_5_6_5  , EffectiveFormat::RGB565 );
+
+    AddUnpackTuple(LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_UNSIGNED_BYTE, EffectiveFormat::Luminance8Alpha8);
+    AddUnpackTuple(LOCAL_GL_LUMINANCE      , LOCAL_GL_UNSIGNED_BYTE, EffectiveFormat::Luminance8      );
+    AddUnpackTuple(LOCAL_GL_ALPHA          , LOCAL_GL_UNSIGNED_BYTE, EffectiveFormat::Alpha8          );
+
+    AddUnpackTuple(LOCAL_GL_RGB , LOCAL_GL_FLOAT     , EffectiveFormat::RGB32F );
+    AddUnpackTuple(LOCAL_GL_RGBA, LOCAL_GL_FLOAT     , EffectiveFormat::RGBA32F);
+    AddUnpackTuple(LOCAL_GL_RGB , LOCAL_GL_HALF_FLOAT, EffectiveFormat::RGB16F );
+    AddUnpackTuple(LOCAL_GL_RGBA, LOCAL_GL_HALF_FLOAT, EffectiveFormat::RGBA16F);
+
+    // Everyone's favorite problem-child:
+    AddUnpackTuple(LOCAL_GL_RGB , LOCAL_GL_HALF_FLOAT_OES, EffectiveFormat::RGB16F );
+    AddUnpackTuple(LOCAL_GL_RGBA, LOCAL_GL_HALF_FLOAT_OES, EffectiveFormat::RGBA16F);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+
+static void
+AddSizedFormat(GLenum sizedFormat, EffectiveFormat effectiveFormat)
+{
+    const FormatInfo* info = GetFormatInfo_NoLock(effectiveFormat);
+    MOZ_ASSERT(info);
+
+    AlwaysInsert(gSizedFormatMap, sizedFormat, info);
+}
+
+static void
+InitSizedFormatMap()
+{
+    // GLES 3.0.4, p128-129 "Required Texture Formats"
+
+    // "Texture and renderbuffer color formats"
+#ifdef FOO
+#error FOO is already defined!
+#endif
+
+#define FOO(x) AddSizedFormat(LOCAL_GL_ ## x, EffectiveFormat::x)
+
+    FOO(RGBA32I);
+    FOO(RGBA32UI);
+    FOO(RGBA16I);
+    FOO(RGBA16UI);
+    FOO(RGBA8);
+    FOO(RGBA8I);
+    FOO(RGBA8UI);
+    FOO(SRGB8_ALPHA8);
+    FOO(RGB10_A2);
+    FOO(RGB10_A2UI);
+    FOO(RGBA4);
+    FOO(RGB5_A1);
+
+    FOO(RGB8);
+    FOO(RGB565);
+
+    FOO(RG32I);
+    FOO(RG32UI);
+    FOO(RG16I);
+    FOO(RG16UI);
+    FOO(RG8);
+    FOO(RG8I);
+    FOO(RG8UI);
+
+    FOO(R32I);
+    FOO(R32UI);
+    FOO(R16I);
+    FOO(R16UI);
+    FOO(R8);
+    FOO(R8I);
+    FOO(R8UI);
+
+    // "Texture-only color formats"
+    FOO(RGBA32F);
+    FOO(RGBA16F);
+    FOO(RGBA8_SNORM);
+
+    FOO(RGB32F);
+    FOO(RGB32I);
+    FOO(RGB32UI);
+
+    FOO(RGB16F);
+    FOO(RGB16I);
+    FOO(RGB16UI);
+
+    FOO(RGB8_SNORM);
+    FOO(RGB8I);
+    FOO(RGB8UI);
+    FOO(SRGB8);
+
+    FOO(R11F_G11F_B10F);
+    FOO(RGB9_E5);
+
+    FOO(RG32F);
+    FOO(RG16F);
+    FOO(RG8_SNORM);
+
+    FOO(R32F);
+    FOO(R16F);
+    FOO(R8_SNORM);
+
+    // "Depth formats"
+    FOO(DEPTH_COMPONENT32F);
+    FOO(DEPTH_COMPONENT24);
+    FOO(DEPTH_COMPONENT16);
+
+    // "Combined depth+stencil formats"
+    FOO(DEPTH32F_STENCIL8);
+    FOO(DEPTH24_STENCIL8);
+
+    // GLES 3.0.4, p205-206, "Required Renderbuffer Formats"
+    FOO(STENCIL_INDEX8);
+
+#undef FOO
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+
+bool gAreFormatTablesInitialized = false;
+
+static void
+EnsureInitFormatTables()
+{
+    if (MOZ_LIKELY(gAreFormatTablesInitialized))
+        return;
+
+    gAreFormatTablesInitialized = true;
+
+    InitCompressedFormatInfo();
+    InitFormatInfoMap();
+    InitUnpackTupleMap();
+    InitSizedFormatMap();
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// Public funcs
+
+StaticMutex gFormatMapMutex;
+
+const FormatInfo*
+GetFormatInfo(EffectiveFormat format)
+{
+    StaticMutexAutoLock lock(gFormatMapMutex);
+    EnsureInitFormatTables();
+
+    return GetFormatInfo_NoLock(format);
+}
+
+const FormatInfo*
+GetInfoByUnpackTuple(GLenum unpackFormat, GLenum unpackType)
+{
+    StaticMutexAutoLock lock(gFormatMapMutex);
+    EnsureInitFormatTables();
+
+    const UnpackTuple unpack = { unpackFormat, unpackType };
+
+    MOZ_ASSERT(!gUnpackTupleMap.empty());
+    auto itr = gUnpackTupleMap.find(unpack);
+    if (itr == gUnpackTupleMap.end())
+        return nullptr;
+
+    return itr->second;
+}
+
+const FormatInfo*
+GetInfoBySizedFormat(GLenum sizedFormat)
+{
+    StaticMutexAutoLock lock(gFormatMapMutex);
+    EnsureInitFormatTables();
+
+    MOZ_ASSERT(!gSizedFormatMap.empty());
+    auto itr = gSizedFormatMap.find(sizedFormat);
+    if (itr == gSizedFormatMap.end())
+        return nullptr;
+
+    return itr->second;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+
+bool
+FormatUsageInfo::CanUnpackWith(GLenum unpackFormat, GLenum unpackType) const
+{
+    const UnpackTuple key = { unpackFormat, unpackType };
+    auto itr = validUnpacks.find(key);
+    return itr != validUnpacks.end();
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////
+// FormatUsageAuthority
+
+UniquePtr<FormatUsageAuthority>
+FormatUsageAuthority::CreateForWebGL1()
+{
+    UniquePtr<FormatUsageAuthority> ret(new FormatUsageAuthority);
+
+    ////////////////////////////////////////////////////////////////////////////
+
+    // GLES 2.0.25, p117, Table 4.5
+    // RGBA8 is made renderable in WebGL 1.0, "Framebuffer Object Attachments"
+
+    //                                              render       filter
+    //                                        RB    able   Tex   able
+    ret->AddFormat(EffectiveFormat::RGBA8  , false, true , true, true);
+    ret->AddFormat(EffectiveFormat::RGBA4  , true , true , true, true);
+    ret->AddFormat(EffectiveFormat::RGB5_A1, true , true , true, true);
+    ret->AddFormat(EffectiveFormat::RGB8   , false, false, true, true);
+    ret->AddFormat(EffectiveFormat::RGB565 , true , true , true, true);
+
+    ret->AddFormat(EffectiveFormat::Luminance8Alpha8, false, false, true, true);
+    ret->AddFormat(EffectiveFormat::Luminance8      , false, false, true, true);
+    ret->AddFormat(EffectiveFormat::Alpha8          , false, false, true, true);
+
+    ret->AddFormat(EffectiveFormat::DEPTH_COMPONENT16, true, true, false, false);
+    ret->AddFormat(EffectiveFormat::STENCIL_INDEX8   , true, true, false, false);
+
+    // Added in WebGL 1.0 spec:
+    ret->AddFormat(EffectiveFormat::DEPTH24_STENCIL8, true, true, false, false);
+
+    ////////////////////////////////////////////////////////////////////////////
+
+    // GLES 2.0.25, p63, Table 3.4
+
+    ret->AddUnpackOption(LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE         , EffectiveFormat::RGBA8  );
+    ret->AddUnpackOption(LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_SHORT_4_4_4_4, EffectiveFormat::RGBA4  );
+    ret->AddUnpackOption(LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_SHORT_5_5_5_1, EffectiveFormat::RGB5_A1);
+    ret->AddUnpackOption(LOCAL_GL_RGB , LOCAL_GL_UNSIGNED_BYTE         , EffectiveFormat::RGB8   );
+    ret->AddUnpackOption(LOCAL_GL_RGB , LOCAL_GL_UNSIGNED_SHORT_5_6_5  , EffectiveFormat::RGB565 );
+
+    ret->AddUnpackOption(LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_UNSIGNED_BYTE, EffectiveFormat::Luminance8Alpha8);
+    ret->AddUnpackOption(LOCAL_GL_LUMINANCE      , LOCAL_GL_UNSIGNED_BYTE, EffectiveFormat::Luminance8      );
+    ret->AddUnpackOption(LOCAL_GL_ALPHA          , LOCAL_GL_UNSIGNED_BYTE, EffectiveFormat::Alpha8          );
+
+
+    return Move(ret);
+}
+
+static void
+AddES3TexFormat(FormatUsageAuthority* that, EffectiveFormat format, bool isRenderable,
+                bool isFilterable)
+{
+    bool asRenderbuffer = isRenderable;
+    bool asTexture = true;
+
+    that->AddFormat(format, asRenderbuffer, isRenderable, asTexture, isFilterable);
+}
+
+UniquePtr<FormatUsageAuthority>
+FormatUsageAuthority::CreateForWebGL2()
+{
+    UniquePtr<FormatUsageAuthority> ret(new FormatUsageAuthority);
+    FormatUsageAuthority* const ptr = ret.get();
+
+    ////////////////////////////////////////////////////////////////////////////
+
+    // For renderable, see GLES 3.0.4, p212 "Framebuffer Completeness"
+    // For filterable, see GLES 3.0.4, p161 "...a texture is complete unless..."
+
+    // GLES 3.0.4, p128-129 "Required Texture Formats"
+    // GLES 3.0.4, p130-132, table 3.13
+    //                                                render filter
+    //                                                 able   able
+    AddES3TexFormat(ptr, EffectiveFormat::R8         , true , true );
+    AddES3TexFormat(ptr, EffectiveFormat::R8_SNORM   , false, true );
+    AddES3TexFormat(ptr, EffectiveFormat::RG8        , true , true );
+    AddES3TexFormat(ptr, EffectiveFormat::RG8_SNORM  , false, true );
+    AddES3TexFormat(ptr, EffectiveFormat::RGB8       , true , true );
+    AddES3TexFormat(ptr, EffectiveFormat::RGB8_SNORM , false, true );
+    AddES3TexFormat(ptr, EffectiveFormat::RGB565     , true , true );
+    AddES3TexFormat(ptr, EffectiveFormat::RGBA4      , true , true );
+    AddES3TexFormat(ptr, EffectiveFormat::RGB5_A1    , true , true );
+    AddES3TexFormat(ptr, EffectiveFormat::RGBA8      , true , true );
+    AddES3TexFormat(ptr, EffectiveFormat::RGBA8_SNORM, false, true );
+    AddES3TexFormat(ptr, EffectiveFormat::RGB10_A2   , true , true );
+    AddES3TexFormat(ptr, EffectiveFormat::RGB10_A2UI , true , false);
+
+    AddES3TexFormat(ptr, EffectiveFormat::SRGB8       , false, true);
+    AddES3TexFormat(ptr, EffectiveFormat::SRGB8_ALPHA8, true , true);
+
+    AddES3TexFormat(ptr, EffectiveFormat::R16F   , false, true);
+    AddES3TexFormat(ptr, EffectiveFormat::RG16F  , false, true);
+    AddES3TexFormat(ptr, EffectiveFormat::RGB16F , false, true);
+    AddES3TexFormat(ptr, EffectiveFormat::RGBA16F, false, true);
+
+    AddES3TexFormat(ptr, EffectiveFormat::R32F   , false, false);
+    AddES3TexFormat(ptr, EffectiveFormat::RG32F  , false, false);
+    AddES3TexFormat(ptr, EffectiveFormat::RGB32F , false, false);
+    AddES3TexFormat(ptr, EffectiveFormat::RGBA32F, false, false);
+
+    AddES3TexFormat(ptr, EffectiveFormat::R11F_G11F_B10F, false, true);
+    AddES3TexFormat(ptr, EffectiveFormat::RGB9_E5       , false, true);
+
+    AddES3TexFormat(ptr, EffectiveFormat::R8I  , true, false);
+    AddES3TexFormat(ptr, EffectiveFormat::R8UI , true, false);
+    AddES3TexFormat(ptr, EffectiveFormat::R16I , true, false);
+    AddES3TexFormat(ptr, EffectiveFormat::R16UI, true, false);
+    AddES3TexFormat(ptr, EffectiveFormat::R32I , true, false);
+    AddES3TexFormat(ptr, EffectiveFormat::R32UI, true, false);
+
+    AddES3TexFormat(ptr, EffectiveFormat::RG8I  , true, false);
+    AddES3TexFormat(ptr, EffectiveFormat::RG8UI , true, false);
+    AddES3TexFormat(ptr, EffectiveFormat::RG16I , true, false);
+    AddES3TexFormat(ptr, EffectiveFormat::RG16UI, true, false);
+    AddES3TexFormat(ptr, EffectiveFormat::RG32I , true, false);
+    AddES3TexFormat(ptr, EffectiveFormat::RG32UI, true, false);
+
+    AddES3TexFormat(ptr, EffectiveFormat::RGB8I  , false, false);
+    AddES3TexFormat(ptr, EffectiveFormat::RGB8UI , false, false);
+    AddES3TexFormat(ptr, EffectiveFormat::RGB16I , false, false);
+    AddES3TexFormat(ptr, EffectiveFormat::RGB16UI, false, false);
+    AddES3TexFormat(ptr, EffectiveFormat::RGB32I , false, false);
+    AddES3TexFormat(ptr, EffectiveFormat::RGB32UI, false, false);
+
+    AddES3TexFormat(ptr, EffectiveFormat::RGBA8I  , true, false);
+    AddES3TexFormat(ptr, EffectiveFormat::RGBA8UI , true, false);
+    AddES3TexFormat(ptr, EffectiveFormat::RGBA16I , true, false);
+    AddES3TexFormat(ptr, EffectiveFormat::RGBA16UI, true, false);
+    AddES3TexFormat(ptr, EffectiveFormat::RGBA32I , true, false);
+    AddES3TexFormat(ptr, EffectiveFormat::RGBA32UI, true, false);
+
+    // GLES 3.0.4, p133, table 3.14
+    // GLES 3.0.4, p161 "...a texture is complete unless..."
+    AddES3TexFormat(ptr, EffectiveFormat::DEPTH_COMPONENT16 , true, false);
+    AddES3TexFormat(ptr, EffectiveFormat::DEPTH_COMPONENT24 , true, false);
+    AddES3TexFormat(ptr, EffectiveFormat::DEPTH_COMPONENT32F, true, false);
+    AddES3TexFormat(ptr, EffectiveFormat::DEPTH24_STENCIL8  , true, false);
+    AddES3TexFormat(ptr, EffectiveFormat::DEPTH32F_STENCIL8 , true, false);
+
+    // GLES 3.0.4, p205-206, "Required Renderbuffer Formats"
+    AddES3TexFormat(ptr, EffectiveFormat::STENCIL_INDEX8, true, false);
+
+    // GLES 3.0.4, p128, table 3.12.
+    // Unsized RGBA/RGB formats are renderable, other unsized are not.
+    AddES3TexFormat(ptr, EffectiveFormat::Luminance8Alpha8, false, true);
+    AddES3TexFormat(ptr, EffectiveFormat::Luminance8      , false, true);
+    AddES3TexFormat(ptr, EffectiveFormat::Alpha8          , false, true);
+
+    // GLES 3.0.4, p147, table 3.19
+    // GLES 3.0.4, p286+, $C.1 "ETC Compressed Texture Image Formats"
+    // (jgilbert) I can't find where these are established as filterable.
+    AddES3TexFormat(ptr, EffectiveFormat::COMPRESSED_RGB8_ETC2                     , false, true);
+    AddES3TexFormat(ptr, EffectiveFormat::COMPRESSED_SRGB8_ETC2                    , false, true);
+    AddES3TexFormat(ptr, EffectiveFormat::COMPRESSED_RGBA8_ETC2_EAC                , false, true);
+    AddES3TexFormat(ptr, EffectiveFormat::COMPRESSED_SRGB8_ALPHA8_ETC2_EAC         , false, true);
+    AddES3TexFormat(ptr, EffectiveFormat::COMPRESSED_R11_EAC                       , false, true);
+    AddES3TexFormat(ptr, EffectiveFormat::COMPRESSED_RG11_EAC                      , false, true);
+    AddES3TexFormat(ptr, EffectiveFormat::COMPRESSED_SIGNED_R11_EAC                , false, true);
+    AddES3TexFormat(ptr, EffectiveFormat::COMPRESSED_SIGNED_RG11_EAC               , false, true);
+    AddES3TexFormat(ptr, EffectiveFormat::COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 , false, true);
+    AddES3TexFormat(ptr, EffectiveFormat::COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2, false, true);
+
+    ////////////////////////////////////////////////////////////////////////////
+    // GLES 3.0.4 p111-113
+    // RGBA
+    ret->AddUnpackOption(LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE              , EffectiveFormat::RGBA8       );
+    ret->AddUnpackOption(LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE              , EffectiveFormat::RGB5_A1     );
+    ret->AddUnpackOption(LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE              , EffectiveFormat::RGBA4       );
+    ret->AddUnpackOption(LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE              , EffectiveFormat::SRGB8_ALPHA8);
+    ret->AddUnpackOption(LOCAL_GL_RGBA, LOCAL_GL_BYTE                       , EffectiveFormat::RGBA8_SNORM );
+    ret->AddUnpackOption(LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_SHORT_4_4_4_4     , EffectiveFormat::RGBA4       );
+    ret->AddUnpackOption(LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_SHORT_5_5_5_1     , EffectiveFormat::RGB5_A1     );
+    ret->AddUnpackOption(LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV, EffectiveFormat::RGB10_A2    );
+    ret->AddUnpackOption(LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV, EffectiveFormat::RGB5_A1     );
+    ret->AddUnpackOption(LOCAL_GL_RGBA, LOCAL_GL_HALF_FLOAT                 , EffectiveFormat::RGBA16F     );
+    ret->AddUnpackOption(LOCAL_GL_RGBA, LOCAL_GL_FLOAT                      , EffectiveFormat::RGBA32F     );
+    ret->AddUnpackOption(LOCAL_GL_RGBA, LOCAL_GL_FLOAT                      , EffectiveFormat::RGBA16F     );
+    // RGBA_INTEGER
+    ret->AddUnpackOption(LOCAL_GL_RGBA_INTEGER, LOCAL_GL_UNSIGNED_BYTE              , EffectiveFormat::RGBA8UI   );
+    ret->AddUnpackOption(LOCAL_GL_RGBA_INTEGER, LOCAL_GL_BYTE                       , EffectiveFormat::RGBA8I    );
+    ret->AddUnpackOption(LOCAL_GL_RGBA_INTEGER, LOCAL_GL_UNSIGNED_SHORT             , EffectiveFormat::RGBA16UI  );
+    ret->AddUnpackOption(LOCAL_GL_RGBA_INTEGER, LOCAL_GL_SHORT                      , EffectiveFormat::RGBA16I   );
+    ret->AddUnpackOption(LOCAL_GL_RGBA_INTEGER, LOCAL_GL_UNSIGNED_INT               , EffectiveFormat::RGBA32UI  );
+    ret->AddUnpackOption(LOCAL_GL_RGBA_INTEGER, LOCAL_GL_INT                        , EffectiveFormat::RGBA32I   );
+    ret->AddUnpackOption(LOCAL_GL_RGBA_INTEGER, LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV, EffectiveFormat::RGB10_A2UI);
+
+    // RGB
+    ret->AddUnpackOption(LOCAL_GL_RGB, LOCAL_GL_UNSIGNED_BYTE               , EffectiveFormat::RGB8          );
+    ret->AddUnpackOption(LOCAL_GL_RGB, LOCAL_GL_UNSIGNED_BYTE               , EffectiveFormat::RGB565        );
+    ret->AddUnpackOption(LOCAL_GL_RGB, LOCAL_GL_UNSIGNED_BYTE               , EffectiveFormat::SRGB8         );
+    ret->AddUnpackOption(LOCAL_GL_RGB, LOCAL_GL_BYTE                        , EffectiveFormat::RGB8_SNORM    );
+    ret->AddUnpackOption(LOCAL_GL_RGB, LOCAL_GL_UNSIGNED_SHORT_5_6_5        , EffectiveFormat::RGB565        );
+    ret->AddUnpackOption(LOCAL_GL_RGB, LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV, EffectiveFormat::R11F_G11F_B10F);
+    ret->AddUnpackOption(LOCAL_GL_RGB, LOCAL_GL_UNSIGNED_INT_5_9_9_9_REV    , EffectiveFormat::RGB9_E5       );
+    ret->AddUnpackOption(LOCAL_GL_RGB, LOCAL_GL_HALF_FLOAT                  , EffectiveFormat::RGB16F        );
+    ret->AddUnpackOption(LOCAL_GL_RGB, LOCAL_GL_HALF_FLOAT                  , EffectiveFormat::R11F_G11F_B10F);
+    ret->AddUnpackOption(LOCAL_GL_RGB, LOCAL_GL_HALF_FLOAT                  , EffectiveFormat::RGB9_E5       );
+    ret->AddUnpackOption(LOCAL_GL_RGB, LOCAL_GL_FLOAT                       , EffectiveFormat::RGB32F        );
+    ret->AddUnpackOption(LOCAL_GL_RGB, LOCAL_GL_FLOAT                       , EffectiveFormat::RGB16F        );
+    ret->AddUnpackOption(LOCAL_GL_RGB, LOCAL_GL_FLOAT                       , EffectiveFormat::R11F_G11F_B10F);
+    ret->AddUnpackOption(LOCAL_GL_RGB, LOCAL_GL_FLOAT                       , EffectiveFormat::RGB9_E5       );
+
+    // RGB_INTEGER
+    ret->AddUnpackOption(LOCAL_GL_RGB_INTEGER, LOCAL_GL_UNSIGNED_BYTE , EffectiveFormat::RGB8UI );
+    ret->AddUnpackOption(LOCAL_GL_RGB_INTEGER, LOCAL_GL_BYTE          , EffectiveFormat::RGB8I  );
+    ret->AddUnpackOption(LOCAL_GL_RGB_INTEGER, LOCAL_GL_UNSIGNED_SHORT, EffectiveFormat::RGB16UI);
+    ret->AddUnpackOption(LOCAL_GL_RGB_INTEGER, LOCAL_GL_SHORT         , EffectiveFormat::RGB16I );
+    ret->AddUnpackOption(LOCAL_GL_RGB_INTEGER, LOCAL_GL_UNSIGNED_INT  , EffectiveFormat::RGB32UI);
+    ret->AddUnpackOption(LOCAL_GL_RGB_INTEGER, LOCAL_GL_INT           , EffectiveFormat::RGB32I );
+
+
+    // RG
+    ret->AddUnpackOption(LOCAL_GL_RG, LOCAL_GL_UNSIGNED_BYTE, EffectiveFormat::RG8      );
+    ret->AddUnpackOption(LOCAL_GL_RG, LOCAL_GL_BYTE         , EffectiveFormat::RG8_SNORM);
+    ret->AddUnpackOption(LOCAL_GL_RG, LOCAL_GL_HALF_FLOAT   , EffectiveFormat::RG16F    );
+    ret->AddUnpackOption(LOCAL_GL_RG, LOCAL_GL_FLOAT        , EffectiveFormat::RG32F    );
+    ret->AddUnpackOption(LOCAL_GL_RG, LOCAL_GL_FLOAT        , EffectiveFormat::RG16F    );
+
+    // RG_INTEGER
+    ret->AddUnpackOption(LOCAL_GL_RG_INTEGER, LOCAL_GL_UNSIGNED_BYTE , EffectiveFormat::RG8UI );
+    ret->AddUnpackOption(LOCAL_GL_RG_INTEGER, LOCAL_GL_BYTE          , EffectiveFormat::RG8I  );
+    ret->AddUnpackOption(LOCAL_GL_RG_INTEGER, LOCAL_GL_UNSIGNED_SHORT, EffectiveFormat::RG16UI);
+    ret->AddUnpackOption(LOCAL_GL_RG_INTEGER, LOCAL_GL_SHORT         , EffectiveFormat::RG16I );
+    ret->AddUnpackOption(LOCAL_GL_RG_INTEGER, LOCAL_GL_UNSIGNED_INT  , EffectiveFormat::RG32UI);
+    ret->AddUnpackOption(LOCAL_GL_RG_INTEGER, LOCAL_GL_INT           , EffectiveFormat::RG32I );
+
+    // RED
+    ret->AddUnpackOption(LOCAL_GL_RED, LOCAL_GL_UNSIGNED_BYTE, EffectiveFormat::R8      );
+    ret->AddUnpackOption(LOCAL_GL_RED, LOCAL_GL_BYTE         , EffectiveFormat::R8_SNORM);
+    ret->AddUnpackOption(LOCAL_GL_RED, LOCAL_GL_HALF_FLOAT   , EffectiveFormat::R16F    );
+    ret->AddUnpackOption(LOCAL_GL_RED, LOCAL_GL_FLOAT        , EffectiveFormat::R32F    );
+    ret->AddUnpackOption(LOCAL_GL_RED, LOCAL_GL_FLOAT        , EffectiveFormat::R16F    );
+
+    // RED_INTEGER
+    ret->AddUnpackOption(LOCAL_GL_RED_INTEGER, LOCAL_GL_UNSIGNED_BYTE , EffectiveFormat::R8UI );
+    ret->AddUnpackOption(LOCAL_GL_RED_INTEGER, LOCAL_GL_BYTE          , EffectiveFormat::R8I  );
+    ret->AddUnpackOption(LOCAL_GL_RED_INTEGER, LOCAL_GL_UNSIGNED_SHORT, EffectiveFormat::R16UI);
+    ret->AddUnpackOption(LOCAL_GL_RED_INTEGER, LOCAL_GL_SHORT         , EffectiveFormat::R16I );
+    ret->AddUnpackOption(LOCAL_GL_RED_INTEGER, LOCAL_GL_UNSIGNED_INT  , EffectiveFormat::R32UI);
+    ret->AddUnpackOption(LOCAL_GL_RED_INTEGER, LOCAL_GL_INT           , EffectiveFormat::R32I );
+
+    // DEPTH_COMPONENT
+    ret->AddUnpackOption(LOCAL_GL_DEPTH_COMPONENT, LOCAL_GL_UNSIGNED_SHORT, EffectiveFormat::DEPTH_COMPONENT16 );
+    ret->AddUnpackOption(LOCAL_GL_DEPTH_COMPONENT, LOCAL_GL_UNSIGNED_INT  , EffectiveFormat::DEPTH_COMPONENT24 );
+    ret->AddUnpackOption(LOCAL_GL_DEPTH_COMPONENT, LOCAL_GL_UNSIGNED_INT  , EffectiveFormat::DEPTH_COMPONENT16 );
+    ret->AddUnpackOption(LOCAL_GL_DEPTH_COMPONENT, LOCAL_GL_FLOAT         , EffectiveFormat::DEPTH_COMPONENT32F);
+
+    // DEPTH_STENCIL
+    ret->AddUnpackOption(LOCAL_GL_DEPTH_STENCIL, LOCAL_GL_UNSIGNED_INT_24_8             , EffectiveFormat::DEPTH24_STENCIL8 );
+    ret->AddUnpackOption(LOCAL_GL_DEPTH_STENCIL, LOCAL_GL_FLOAT_32_UNSIGNED_INT_24_8_REV, EffectiveFormat::DEPTH32F_STENCIL8);
+
+    // Unsized formats
+    ret->AddUnpackOption(LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_UNSIGNED_BYTE, EffectiveFormat::Luminance8Alpha8);
+    ret->AddUnpackOption(LOCAL_GL_LUMINANCE      , LOCAL_GL_UNSIGNED_BYTE, EffectiveFormat::Luminance8      );
+    ret->AddUnpackOption(LOCAL_GL_ALPHA          , LOCAL_GL_UNSIGNED_BYTE, EffectiveFormat::Alpha8          );
+
+    return Move(ret);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+
+FormatUsageInfo*
+FormatUsageAuthority::GetInfo(EffectiveFormat format)
+{
+    auto itr = mInfoMap.find(format);
+
+    if (itr == mInfoMap.end())
+        return nullptr;
+
+    return &(itr->second);
+}
+
+void
+FormatUsageAuthority::AddFormat(EffectiveFormat format, bool asRenderbuffer,
+                                bool isRenderable, bool asTexture, bool isFilterable)
+{
+    MOZ_ASSERT_IF(asRenderbuffer, isRenderable);
+    MOZ_ASSERT_IF(isFilterable, asTexture);
+
+    const FormatInfo* formatInfo = GetFormatInfo(format);
+    MOZ_RELEASE_ASSERT(formatInfo);
+
+    FormatUsageInfo usage = { formatInfo, asRenderbuffer, isRenderable, asTexture,
+                              isFilterable, std::set<UnpackTuple>() };
+    AlwaysInsert(mInfoMap, format, usage);
+}
+
+void
+FormatUsageAuthority::AddUnpackOption(GLenum unpackFormat, GLenum unpackType,
+                                      EffectiveFormat effectiveFormat)
+{
+    const UnpackTuple unpack = { unpackFormat, unpackType };
+    FormatUsageInfo* usage = GetInfo(effectiveFormat);
+    MOZ_RELEASE_ASSERT(usage);
+    if (!usage)
+        return;
+
+    MOZ_RELEASE_ASSERT(usage->asTexture);
+
+    auto res = usage->validUnpacks.insert(unpack);
+    bool didInsert = res.second;
+    MOZ_ALWAYS_TRUE(didInsert);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+
+} // namespace webgl
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/canvas/WebGLFormats.h
@@ -0,0 +1,257 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef WEBGL_FORMATS_H_
+#define WEBGL_FORMATS_H_
+
+#include <map>
+#include <set>
+
+#include "GLTypes.h"
+#include "mozilla/UniquePtr.h"
+
+namespace mozilla {
+namespace webgl {
+
+typedef uint8_t EffectiveFormatValueT;
+
+enum class EffectiveFormat : EffectiveFormatValueT {
+    // GLES 3.0.4, p128-129, "Required Texture Formats"
+    // "Texture and renderbuffer color formats"
+    RGBA32I,
+    RGBA32UI,
+    RGBA16I,
+    RGBA16UI,
+    RGBA8,
+    RGBA8I,
+    RGBA8UI,
+    SRGB8_ALPHA8,
+    RGB10_A2,
+    RGB10_A2UI,
+    RGBA4,
+    RGB5_A1,
+
+    RGB8,
+    RGB565,
+
+    RG32I,
+    RG32UI,
+    RG16I,
+    RG16UI,
+    RG8,
+    RG8I,
+    RG8UI,
+
+    R32I,
+    R32UI,
+    R16I,
+    R16UI,
+    R8,
+    R8I,
+    R8UI,
+
+    // "Texture-only color formats"
+    RGBA32F,
+    RGBA16F,
+    RGBA8_SNORM,
+
+    RGB32F,
+    RGB32I,
+    RGB32UI,
+
+    RGB16F,
+    RGB16I,
+    RGB16UI,
+
+    RGB8_SNORM,
+    RGB8I,
+    RGB8UI,
+    SRGB8,
+
+    R11F_G11F_B10F,
+    RGB9_E5,
+
+    RG32F,
+    RG16F,
+    RG8_SNORM,
+
+    R32F,
+    R16F,
+    R8_SNORM,
+
+    // "Depth formats"
+    DEPTH_COMPONENT32F,
+    DEPTH_COMPONENT24,
+    DEPTH_COMPONENT16,
+
+    // "Combined depth+stencil formats"
+    DEPTH32F_STENCIL8,
+    DEPTH24_STENCIL8,
+
+    // GLES 3.0.4, p205-206, "Required Renderbuffer Formats"
+    STENCIL_INDEX8,
+
+    // GLES 3.0.4, p128, table 3.12.
+    Luminance8Alpha8,
+    Luminance8,
+    Alpha8,
+
+    // GLES 3.0.4, p147, table 3.19
+    // GLES 3.0.4, p286+, $C.1 "ETC Compressed Texture Image Formats"
+    COMPRESSED_R11_EAC,
+    COMPRESSED_SIGNED_R11_EAC,
+    COMPRESSED_RG11_EAC,
+    COMPRESSED_SIGNED_RG11_EAC,
+    COMPRESSED_RGB8_ETC2,
+    COMPRESSED_SRGB8_ETC2,
+    COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2,
+    COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2,
+    COMPRESSED_RGBA8_ETC2_EAC,
+    COMPRESSED_SRGB8_ALPHA8_ETC2_EAC,
+
+    // AMD_compressed_ATC_texture
+    ATC_RGB_AMD,
+    ATC_RGBA_EXPLICIT_ALPHA_AMD,
+    ATC_RGBA_INTERPOLATED_ALPHA_AMD,
+
+    // EXT_texture_compression_s3tc
+    COMPRESSED_RGB_S3TC_DXT1,
+    COMPRESSED_RGBA_S3TC_DXT1,
+    COMPRESSED_RGBA_S3TC_DXT3,
+    COMPRESSED_RGBA_S3TC_DXT5,
+
+    // IMG_texture_compression_pvrtc
+    COMPRESSED_RGB_PVRTC_4BPPV1,
+    COMPRESSED_RGBA_PVRTC_4BPPV1,
+    COMPRESSED_RGB_PVRTC_2BPPV1,
+    COMPRESSED_RGBA_PVRTC_2BPPV1,
+
+    // OES_compressed_ETC1_RGB8_texture
+    ETC1_RGB8,
+
+    MAX,
+};
+
+enum class UnsizedFormat : uint8_t {
+    R,
+    RG,
+    RGB,
+    RGBA,
+    LA,
+    L,
+    A,
+    D,
+    S,
+    DS,
+};
+
+// GLES 3.0.4 p114 Table 3.4
+enum class ComponentType : uint8_t {
+    None,         // DEPTH_COMPONENT32F
+    Int,          // RGBA32I
+    UInt,         // RGBA32UI
+    NormInt,      // RGBA8_SNORM
+    NormUInt,     // RGBA8
+    NormUIntSRGB, // SRGB8_ALPHA8
+    Float,        // RGBA32F
+    SharedExp,    // RGB9_E5
+};
+
+enum class SubImageUpdateBehavior : uint8_t {
+    Forbidden,
+    FullOnly,
+    BlockAligned,
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+struct CompressedFormatInfo {
+    const EffectiveFormat effectiveFormat;
+    const uint8_t bytesPerBlock;
+    const uint8_t blockWidth;
+    const uint8_t blockHeight;
+    const bool requirePOT;
+    const SubImageUpdateBehavior subImageUpdateBehavior;
+};
+
+struct FormatInfo {
+    const EffectiveFormat effectiveFormat;
+    const char* const name;
+    const UnsizedFormat unsizedFormat;
+    const ComponentType colorComponentType;
+    const uint8_t bytesPerPixel; // 0 iff `!!compression`.
+    const bool hasColor;
+    const bool hasAlpha;
+    const bool hasDepth;
+    const bool hasStencil;
+
+    const CompressedFormatInfo* const compression;
+};
+
+//////////////////////////////////////////////////////////////////////////////////////////
+
+const FormatInfo* GetFormatInfo(EffectiveFormat format);
+const FormatInfo* GetInfoByUnpackTuple(GLenum unpackFormat, GLenum unpackType);
+const FormatInfo* GetInfoBySizedFormat(GLenum sizedFormat);
+
+////////////////////////////////////////
+
+struct UnpackTuple {
+    const GLenum format;
+    const GLenum type;
+
+    bool operator <(const UnpackTuple& x) const
+    {
+        if (format == x.format) {
+            return type < x.type;
+        }
+
+        return format < x.format;
+    }
+};
+
+struct FormatUsageInfo {
+    const FormatInfo* const formatInfo;
+    bool asRenderbuffer;
+    bool isRenderable;
+    bool asTexture;
+    bool isFilterable;
+    std::set<UnpackTuple> validUnpacks;
+
+    bool CanUnpackWith(GLenum unpackFormat, GLenum unpackType) const;
+};
+
+class FormatUsageAuthority
+{
+    std::map<EffectiveFormat, FormatUsageInfo> mInfoMap;
+
+public:
+    static UniquePtr<FormatUsageAuthority> CreateForWebGL1();
+    static UniquePtr<FormatUsageAuthority> CreateForWebGL2();
+
+private:
+    FormatUsageAuthority() { }
+
+public:
+    void AddFormat(EffectiveFormat format, bool asRenderbuffer, bool isRenderable,
+                   bool asTexture, bool isFilterable);
+
+    void AddUnpackOption(GLenum unpackFormat, GLenum unpackType,
+                         EffectiveFormat effectiveFormat);
+
+    FormatUsageInfo* GetInfo(EffectiveFormat format);
+
+    FormatUsageInfo* GetInfo(const FormatInfo* format)
+    {
+        return GetInfo(format->effectiveFormat);
+    }
+};
+
+////////////////////////////////////////
+
+} // namespace webgl
+} // namespace mozilla
+
+#endif // WEBGL_FORMATS_H_
--- a/dom/canvas/moz.build
+++ b/dom/canvas/moz.build
@@ -105,16 +105,17 @@ UNIFIED_SOURCES += [
     'WebGLExtensionSRGB.cpp',
     'WebGLExtensionStandardDerivatives.cpp',
     'WebGLExtensionTextureFilterAnisotropic.cpp',
     'WebGLExtensionTextureFloat.cpp',
     'WebGLExtensionTextureFloatLinear.cpp',
     'WebGLExtensionTextureHalfFloat.cpp',
     'WebGLExtensionTextureHalfFloatLinear.cpp',
     'WebGLExtensionVertexArray.cpp',
+    'WebGLFormats.cpp',
     'WebGLFramebuffer.cpp',
     'WebGLFramebufferAttachable.cpp',
     'WebGLObjectModel.cpp',
     'WebGLProgram.cpp',
     'WebGLQuery.cpp',
     'WebGLRenderbuffer.cpp',
     'WebGLSampler.cpp',
     'WebGLShader.cpp',
--- a/dom/canvas/test/chrome/chrome.ini
+++ b/dom/canvas/test/chrome/chrome.ini
@@ -1,7 +1,7 @@
 [DEFAULT]
-skip-if = buildapp == 'b2g'
+skip-if = buildapp == 'b2g' || os == 'android'
 support-files = nonchrome_webgl_debug_renderer_info.html
 
 [test_webgl_debug_renderer_info.html]
 [test_drawWindow_widget_layers.html]
 support-files = ../file_drawWindow_source.html ../file_drawWindow_common.js
--- a/dom/devicestorage/test/chrome.ini
+++ b/dom/devicestorage/test/chrome.ini
@@ -1,5 +1,5 @@
 [DEFAULT]
-skip-if = (buildapp == 'b2g' || buildapp == 'mulet')
+skip-if = (buildapp == 'b2g' || buildapp == 'mulet' || os == 'android')
 
 [test_app_permissions.html]
 [test_fs_app_permissions.html]
--- a/dom/events/IMEContentObserver.cpp
+++ b/dom/events/IMEContentObserver.cpp
@@ -94,17 +94,17 @@ IMEContentObserver::IMEContentObserver()
   , mIMEHasFocus(false)
   , mIsFocusEventPending(false)
   , mIsSelectionChangeEventPending(false)
   , mSelectionChangeCausedOnlyByComposition(false)
   , mIsPositionChangeEventPending(false)
   , mIsFlushingPendingNotifications(false)
 {
 #ifdef DEBUG
-  TestMergingTextChangeData();
+  mTextChangeData.Test();
 #endif
 }
 
 void
 IMEContentObserver::Init(nsIWidget* aWidget,
                          nsPresContext* aPresContext,
                          nsIContent* aContent,
                          nsIEditor* aEditor)
@@ -519,216 +519,31 @@ IMEContentObserver::OnMouseButtonEvent(n
   }
 
   bool consumed = (rv == NS_SUCCESS_EVENT_CONSUMED);
   aMouseEvent->mFlags.mDefaultPrevented = consumed;
   return consumed;
 }
 
 void
-IMEContentObserver::StoreTextChangeData(const TextChangeData& aTextChangeData)
-{
-  MOZ_ASSERT(aTextChangeData.mStartOffset <= aTextChangeData.mRemovedEndOffset,
-             "end of removed text must be same or larger than start");
-  MOZ_ASSERT(aTextChangeData.mStartOffset <= aTextChangeData.mAddedEndOffset,
-             "end of added text must be same or larger than start");
-
-  if (!mTextChangeData.mStored) {
-    mTextChangeData = aTextChangeData;
-    MOZ_ASSERT(mTextChangeData.mStored, "Why mStored is false?");
-    return;
-  }
-
-  // |mTextChangeData| should represent all modified text ranges and all
-  // inserted text ranges.
-  // |mStartOffset| and |mRemovedEndOffset| represent all replaced or removed
-  // text ranges.  I.e., mStartOffset should be the smallest offset of all
-  // modified text ranges in old text.  |mRemovedEndOffset| should be the
-  // largest end offset in old text of all modified text ranges.
-  // |mAddedEndOffset| represents the end offset of all inserted text ranges.
-  // I.e., only this is an offset in new text.
-  // In other words, between mStartOffset and |mRemovedEndOffset| of the
-  // premodified text was already removed.  And some text whose length is
-  // |mAddedEndOffset - mStartOffset| is inserted to |mStartOffset|.  I.e.,
-  // this allows IME to mark dirty the modified text range with |mStartOffset|
-  // and |mRemovedEndOffset| if IME stores all text of the focused editor and
-  // to compute new text length with |mAddedEndOffset| and |mRemovedEndOffset|.
-  // Additionally, IME can retrieve only the text between |mStartOffset| and
-  // |mAddedEndOffset| for updating stored text.
-
-  // For comparing new and old |mStartOffset|/|mRemovedEndOffset| values, they
-  // should be adjusted to be in same text. The |newData.mStartOffset| and
-  // |newData.mRemovedEndOffset| should be computed as in old text because
-  // |mStartOffset| and |mRemovedEndOffset| represent the modified text range
-  // in the old text but even if some text before the values of the newData
-  // has already been modified, the values don't include the changes.
-
-  // For comparing new and old |mAddedEndOffset| values, they should be
-  // adjusted to be in same text.  The |oldData.mAddedEndOffset| should be
-  // computed as in the new text because |mAddedEndOffset| indicates the end
-  // offset of inserted text in the new text but |oldData.mAddedEndOffset|
-  // doesn't include any changes of the text before |newData.mAddedEndOffset|.
-
-  const TextChangeData& newData = aTextChangeData;
-  const TextChangeData oldData = mTextChangeData;
-
-  mTextChangeData.mCausedOnlyByComposition =
-    newData.mCausedOnlyByComposition && oldData.mCausedOnlyByComposition;
-
-  if (newData.mStartOffset >= oldData.mAddedEndOffset) {
-    // Case 1:
-    // If new start is after old end offset of added text, it means that text
-    // after the modified range is modified.  Like:
-    // added range of old change:             +----------+
-    // removed range of new change:                           +----------+
-    // So, the old start offset is always the smaller offset.
-    mTextChangeData.mStartOffset = oldData.mStartOffset;
-    // The new end offset of removed text is moved by the old change and we
-    // need to cancel the move of the old change for comparing the offsets in
-    // same text because it doesn't make sensce to compare offsets in different
-    // text.
-    uint32_t newRemovedEndOffsetInOldText =
-      newData.mRemovedEndOffset - oldData.Difference();
-    mTextChangeData.mRemovedEndOffset =
-      std::max(newRemovedEndOffsetInOldText, oldData.mRemovedEndOffset);
-    // The new end offset of added text is always the larger offset.
-    mTextChangeData.mAddedEndOffset = newData.mAddedEndOffset;
-    return;
-  }
-
-  if (newData.mStartOffset >= oldData.mStartOffset) {
-    // If new start is in the modified range, it means that new data changes
-    // a part or all of the range.
-    mTextChangeData.mStartOffset = oldData.mStartOffset;
-    if (newData.mRemovedEndOffset >= oldData.mAddedEndOffset) {
-      // Case 2:
-      // If new end of removed text is greater than old end of added text, it
-      // means that all or a part of modified range modified again and text
-      // after the modified range is also modified.  Like:
-      // added range of old change:             +----------+
-      // removed range of new change:                   +----------+
-      // So, the new removed end offset is moved by the old change and we need
-      // to cancel the move of the old change for comparing the offsets in the
-      // same text because it doesn't make sense to compare the offsets in
-      // different text.
-      uint32_t newRemovedEndOffsetInOldText =
-        newData.mRemovedEndOffset - oldData.Difference();
-      mTextChangeData.mRemovedEndOffset =
-        std::max(newRemovedEndOffsetInOldText, oldData.mRemovedEndOffset);
-      // The old end of added text is replaced by new change. So, it should be
-      // same as the new start.  On the other hand, the new added end offset is
-      // always same or larger.  Therefore, the merged end offset of added
-      // text should be the new end offset of added text.
-      mTextChangeData.mAddedEndOffset = newData.mAddedEndOffset;
-      return;
-    }
-
-    // Case 3:
-    // If new end of removed text is less than old end of added text, it means
-    // that only a part of the modified range is modified again.  Like:
-    // added range of old change:             +------------+
-    // removed range of new change:               +-----+
-    // So, the new end offset of removed text should be same as the old end
-    // offset of removed text.  Therefore, the merged end offset of removed
-    // text should be the old text change's |mRemovedEndOffset|.
-    mTextChangeData.mRemovedEndOffset = oldData.mRemovedEndOffset;
-    // The old end of added text is moved by new change.  So, we need to cancel
-    // the move of the new change for comparing the offsets in same text.
-    uint32_t oldAddedEndOffsetInNewText =
-      oldData.mAddedEndOffset + newData.Difference();
-    mTextChangeData.mAddedEndOffset =
-      std::max(newData.mAddedEndOffset, oldAddedEndOffsetInNewText);
-    return;
-  }
-
-  if (newData.mRemovedEndOffset >= oldData.mStartOffset) {
-    // If new end of removed text is greater than old start (and new start is
-    // less than old start), it means that a part of modified range is modified
-    // again and some new text before the modified range is also modified.
-    MOZ_ASSERT(newData.mStartOffset < oldData.mStartOffset,
-      "new start offset should be less than old one here");
-    mTextChangeData.mStartOffset = newData.mStartOffset;
-    if (newData.mRemovedEndOffset >= oldData.mAddedEndOffset) {
-      // Case 4:
-      // If new end of removed text is greater than old end of added text, it
-      // means that all modified text and text after the modified range is
-      // modified.  Like:
-      // added range of old change:             +----------+
-      // removed range of new change:        +------------------+
-      // So, the new end of removed text is moved by the old change.  Therefore,
-      // we need to cancel the move of the old change for comparing the offsets
-      // in same text because it doesn't make sense to compare the offsets in
-      // different text.
-      uint32_t newRemovedEndOffsetInOldText =
-        newData.mRemovedEndOffset - oldData.Difference();
-      mTextChangeData.mRemovedEndOffset =
-        std::max(newRemovedEndOffsetInOldText, oldData.mRemovedEndOffset);
-      // The old end of added text is replaced by new change.  So, the old end
-      // offset of added text is same as new text change's start offset.  Then,
-      // new change's end offset of added text is always same or larger than
-      // it.  Therefore, merged end offset of added text is always the new end
-      // offset of added text.
-      mTextChangeData.mAddedEndOffset = newData.mAddedEndOffset;
-      return;
-    }
-
-    // Case 5:
-    // If new end of removed text is less than old end of added text, it
-    // means that only a part of the modified range is modified again.  Like:
-    // added range of old change:             +----------+
-    // removed range of new change:      +----------+
-    // So, the new end of removed text should be same as old end of removed
-    // text for preventing end of removed text to be modified.  Therefore,
-    // merged end offset of removed text is always the old end offset of removed
-    // text.
-    mTextChangeData.mRemovedEndOffset = oldData.mRemovedEndOffset;
-    // The old end of added text is moved by this change.  So, we need to
-    // cancel the move of the new change for comparing the offsets in same text
-    // because it doesn't make sense to compare the offsets in different text.
-    uint32_t oldAddedEndOffsetInNewText =
-      oldData.mAddedEndOffset + newData.Difference();
-    mTextChangeData.mAddedEndOffset =
-      std::max(newData.mAddedEndOffset, oldAddedEndOffsetInNewText);
-    return;
-  }
-
-  // Case 6:
-  // Otherwise, i.e., both new end of added text and new start are less than
-  // old start, text before the modified range is modified.  Like:
-  // added range of old change:                  +----------+
-  // removed range of new change: +----------+
-  MOZ_ASSERT(newData.mStartOffset < oldData.mStartOffset,
-    "new start offset should be less than old one here");
-  mTextChangeData.mStartOffset = newData.mStartOffset;
-  MOZ_ASSERT(newData.mRemovedEndOffset < oldData.mRemovedEndOffset,
-     "new removed end offset should be less than old one here");
-  mTextChangeData.mRemovedEndOffset = oldData.mRemovedEndOffset;
-  // The end of added text should be adjusted with the new difference.
-  uint32_t oldAddedEndOffsetInNewText =
-    oldData.mAddedEndOffset + newData.Difference();
-  mTextChangeData.mAddedEndOffset =
-    std::max(newData.mAddedEndOffset, oldAddedEndOffsetInNewText);
-}
-
-void
 IMEContentObserver::CharacterDataWillChange(nsIDocument* aDocument,
                                             nsIContent* aContent,
                                             CharacterDataChangeInfo* aInfo)
 {
   NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
                "character data changed for non-text node");
   MOZ_ASSERT(mPreCharacterDataChangeLength < 0,
              "CharacterDataChanged() should've reset "
              "mPreCharacterDataChangeLength");
 
   mEndOfAddedTextCache.Clear();
   mStartOfRemovingTextRangeCache.Clear();
 
   bool causedByComposition = IsEditorHandlingEventForComposition();
-  if (!mTextChangeData.mStored && causedByComposition &&
+  if (!mTextChangeData.IsValid() && causedByComposition &&
       !mUpdatePreference.WantChangesCausedByComposition()) {
     return;
   }
 
   mPreCharacterDataChangeLength =
     ContentEventHandler::GetNativeTextLength(aContent, aInfo->mChangeStart,
                                              aInfo->mChangeEnd);
   MOZ_ASSERT(mPreCharacterDataChangeLength >=
@@ -746,17 +561,17 @@ IMEContentObserver::CharacterDataChanged
 
   mEndOfAddedTextCache.Clear();
   mStartOfRemovingTextRangeCache.Clear();
 
   int64_t removedLength = mPreCharacterDataChangeLength;
   mPreCharacterDataChangeLength = -1;
 
   bool causedByComposition = IsEditorHandlingEventForComposition();
-  if (!mTextChangeData.mStored && causedByComposition &&
+  if (!mTextChangeData.IsValid() && causedByComposition &&
       !mUpdatePreference.WantChangesCausedByComposition()) {
     return;
   }
 
   MOZ_ASSERT(removedLength >= 0,
              "mPreCharacterDataChangeLength should've been set by "
              "CharacterDataWillChange()");
 
@@ -784,17 +599,17 @@ IMEContentObserver::CharacterDataChanged
 void
 IMEContentObserver::NotifyContentAdded(nsINode* aContainer,
                                        int32_t aStartIndex,
                                        int32_t aEndIndex)
 {
   mStartOfRemovingTextRangeCache.Clear();
 
   bool causedByComposition = IsEditorHandlingEventForComposition();
-  if (!mTextChangeData.mStored && causedByComposition &&
+  if (!m