Bug 1113086 - AudioChannel policy in Browser API - patch 1 - BrowserElementAudioChannel, r=ehsan
authorAndrea Marchesini <amarchesini@mozilla.com>
Fri, 10 Jul 2015 17:38:44 +0100
changeset 284142 4120dbd77a5dd1fc8399833752f4d1ce38294485
parent 284141 727b321502a6a9ea22dbcb3cd496d3db9c444a80
child 284143 790dadba73e354ef10bf6dc8ca6335d1f4988375
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersehsan
bugs1113086
milestone42.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1113086 - AudioChannel policy in Browser API - patch 1 - BrowserElementAudioChannel, r=ehsan
browser/installer/package-manifest.in
dom/audiochannel/AudioChannelAgent.cpp
dom/audiochannel/AudioChannelAgent.h
dom/audiochannel/AudioChannelCommon.h
dom/audiochannel/AudioChannelService.cpp
dom/audiochannel/AudioChannelService.h
dom/audiochannel/AudioChannelServiceChild.cpp
dom/audiochannel/AudioChannelServiceChild.h
dom/audiochannel/moz.build
dom/audiochannel/nsIAudioChannelAgent.idl
dom/audiochannel/nsIAudioChannelService.idl
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/base/nsGkAtomList.h
dom/base/nsGlobalWindow.cpp
dom/base/nsPIDOMWindow.h
dom/browser-element/BrowserElementAudioChannel.cpp
dom/browser-element/BrowserElementAudioChannel.h
dom/browser-element/BrowserElementChildPreload.js
dom/browser-element/BrowserElementParent.cpp
dom/browser-element/BrowserElementParent.js
dom/browser-element/mochitest/browserElement_AudioChannel.js
dom/browser-element/mochitest/file_audio.html
dom/browser-element/mochitest/iframe_file_audio.html
dom/browser-element/mochitest/mochitest-oop.ini
dom/browser-element/mochitest/mochitest.ini
dom/browser-element/mochitest/test_browserElement_inproc_AudioChannel.html
dom/browser-element/mochitest/test_browserElement_oop_AudioChannel.html
dom/browser-element/moz.build
dom/browser-element/nsIBrowserElementAPI.idl
dom/camera/DOMCameraControl.cpp
dom/fmradio/FMRadio.cpp
dom/fmradio/FMRadio.h
dom/html/HTMLAudioElement.cpp
dom/html/HTMLMediaElement.cpp
dom/html/HTMLMediaElement.h
dom/html/nsBrowserElement.cpp
dom/html/nsBrowserElement.h
dom/html/nsGenericHTMLFrameElement.cpp
dom/html/nsGenericHTMLFrameElement.h
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PBrowser.ipdl
dom/ipc/PContent.ipdl
dom/ipc/ProcessPriorityManager.cpp
dom/ipc/TabChild.cpp
dom/ipc/TabMessageUtils.h
dom/ipc/TabParent.cpp
dom/ipc/TabParent.h
dom/media/webaudio/AudioDestinationNode.cpp
dom/media/webaudio/AudioDestinationNode.h
dom/media/webaudio/moz.build
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
dom/speakermanager/SpeakerManager.cpp
dom/speakermanager/SpeakerManagerService.cpp
dom/speakermanager/SpeakerManagerServiceChild.cpp
dom/system/gonk/AudioChannelManager.cpp
dom/system/gonk/AudioManager.cpp
dom/webidl/BrowserElementAudioChannel.webidl
dom/webidl/moz.build
layout/build/nsLayoutModule.cpp
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -883,16 +883,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/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,18 +20,16 @@ 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();
   }
@@ -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,70 @@ 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 = pWindow.forget();
+    mWindow = do_QueryInterface(topWindow);
+    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)
+NS_IMETHODIMP AudioChannelAgent::StartPlaying(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)
 {
   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 +153,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
 
--- 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,100 +33,125 @@
 #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 {
+
+void
+NotifyChannelActive(uint64_t aWindowID, AudioChannel aAudioChannel,
+                    bool aActive)
 {
-  explicit WindowDestroyedEnumeratorData(uint64_t aInnerID)
-    : mInnerID(aInnerID)
-  {}
+  nsCOMPtr<nsIObserverService> observerService =
+    services::GetObserverService();
+  if (NS_WARN_IF(!observerService)) {
+    return;
+  }
+
+  nsCOMPtr<nsISupportsPRUint64> wrapper =
+    do_CreateInstance(NS_SUPPORTS_PRUINT64_CONTRACTID);
+  if (!wrapper) {
+     return;
+  }
+
+  wrapper->SetData(aWindowID);
+
+  nsAutoString name;
+  AudioChannelService::GetAudioChannelString(aAudioChannel, name);
+
+  nsAutoCString topic;
+  topic.Assign("audiochannel-activity-");
+  topic.Append(NS_ConvertUTF16toUTF8(name));
 
-  nsTArray<nsRefPtr<AudioChannelAgent>> mAgents;
-  uint64_t mInnerID;
-};
+  observerService->NotifyObservers(wrapper, topic.get(),
+                                   aActive
+                                     ? MOZ_UTF16("active") : MOZ_UTF16("inactive"));
+}
+
+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();
+}
+
+} // 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 (XRE_GetProcessType() == GeckoProcessType_Default) {
+      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)
 {
   if (XRE_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
@@ -137,744 +163,207 @@ AudioChannelService::AudioChannelService
 }
 
 AudioChannelService::~AudioChannelService()
 {
 }
 
 void
 AudioChannelService::RegisterAudioChannelAgent(AudioChannelAgent* aAgent,
-                                               AudioChannel aChannel,
-                                               bool aWithVideo)
+                                               AudioChannel aChannel)
 {
   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);
+  uint64_t windowID = aAgent->WindowID();
+  AudioChannelWindow* winData = mWindows.LookupOrAdd(windowID);
+
+  MOZ_ASSERT(!winData->mAgents.Get(aAgent));
+
+  AudioChannel* audioChannel = new AudioChannel(aChannel);
+  winData->mAgents.Put(aAgent, audioChannel);
+
+  ++winData->mChannels[(uint32_t)aChannel].mNumberOfAgents;
+
+  // The first one, we must inform the BrowserElementAudioChannel.
+  if (winData->mChannels[(uint32_t)aChannel].mNumberOfAgents == 1) {
+    NotifyChannelActive(aAgent->WindowID(), aChannel, true);
+  }
 
   // If this is the first agent for this window, we must notify the observers.
-  uint32_t count = CountWindow(aAgent->Window());
-  if (count == 1) {
+  if (winData->mAgents.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)
-{
-  if (mDisabled) {
-    return;
-  }
-
-  AudioChannelInternalType type = GetInternalType(aChannel, true);
-  mChannelCounters[type].AppendElement(aChildID);
-
-  if (XRE_IsParentProcess()) {
-
-    // We must keep the childIds in order to decide which app is allowed to play
-    // with then telephony channel.
-    if (aChannel == AudioChannel::Telephony) {
-      RegisterTelephonyChild(aChildID);
-    }
-
-    // Since there is another telephony registered, we can unregister old one
-    // immediately.
-    if (mDeferTelChannelTimer && aChannel == AudioChannel::Telephony) {
-      mDeferTelChannelTimer->Cancel();
-      mDeferTelChannelTimer = nullptr;
-      UnregisterTypeInternal(aChannel, mTimerElementHidden, mTimerChildID,
-                             false);
-    }
-
-    if (aWithVideo) {
-      mWithVideoChildIDs.AppendElement(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 (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;
-    }
-
-    // In order to avoid race conditions, it's safer to notify any existing
-    // agent any time a new one is registered.
-    SendAudioChannelChangedNotification(aChildID);
-    SendNotification();
-  }
-}
-
-void
 AudioChannelService::UnregisterAudioChannelAgent(AudioChannelAgent* aAgent)
 {
   if (mDisabled) {
     return;
   }
 
-  nsAutoPtr<AudioChannelAgentData> data;
-  mAgents.RemoveAndForget(aAgent, data);
+  uint64_t windowID = aAgent->WindowID();
+  AudioChannelWindow* winData = nullptr;
+  if (!mWindows.Get(windowID, &winData)) {
+    return;
+  }
 
-  if (data) {
-    UnregisterType(data->mChannel, data->mElementHidden,
-                   CONTENT_PROCESS_ID_MAIN, data->mWithVideo);
+  nsAutoPtr<AudioChannel> audioChannel;
+  winData->mAgents.RemoveAndForget(aAgent, audioChannel);
+  if (audioChannel) {
+    MOZ_ASSERT(winData->mChannels[(uint32_t)*audioChannel].mNumberOfAgents > 0);
+
+    --winData->mChannels[(uint32_t)*audioChannel].mNumberOfAgents;
+
+    // The last one, we must inform the BrowserElementAudioChannel.
+    if (winData->mChannels[(uint32_t)*audioChannel].mNumberOfAgents == 0) {
+      NotifyChannelActive(aAgent->WindowID(), *audioChannel, 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) {
+  if (winData->mAgents.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)
+AudioChannelService::GetState(nsPIDOMWindow* aWindow, uint32_t aAudioChannel,
+                              float* aVolume, bool* aMuted)
 {
-  if (mDisabled) {
+  MOZ_ASSERT(!aWindow || aWindow->IsOuterWindow());
+  MOZ_ASSERT(aVolume && aMuted);
+  MOZ_ASSERT(aAudioChannel < NUMBER_OF_AUDIO_CHANNELS);
+
+  *aVolume = 1.0;
+  *aMuted = false;
+
+  if (!aWindow || !aWindow->IsOuterWindow()) {
     return;
   }
 
-  // There are two reasons to defer the decrease of telephony channel.
-  // 1. User can have time to remove device from his ear before music resuming.
-  // 2. Give BT SCO to be disconnected before starting to connect A2DP.
-  if (XRE_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);
-}
+  AudioChannelWindow* winData = nullptr;
+  nsCOMPtr<nsPIDOMWindow> window = aWindow;
 
-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);
+  // 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 {
+    if (mWindows.Get(window->WindowID(), &winData)) {
+      *aVolume *= winData->mChannels[aAudioChannel].mVolume;
+      *aMuted = *aMuted || winData->mChannels[aAudioChannel].mMuted;
     }
 
-    SendAudioChannelChangedNotification(aChildID);
-    SendNotification();
-  }
-}
-
-void
-AudioChannelService::UpdateChannelType(AudioChannel aChannel,
-                                       uint64_t aChildID,
-                                       bool aElementHidden,
-                                       bool aElementWasHidden)
-{
-  // 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);
-  }
+    *aVolume *= window->GetAudioVolume();
+    *aMuted = *aMuted || window->GetAudioMuted();
 
-  // 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;
-  }
-}
+    nsCOMPtr<nsIDOMWindow> win;
+    window->GetScriptableParent(getter_AddRefs(win));
+    if (window == win) {
+      break;
+    }
 
-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;
+    window = do_QueryInterface(win);
 
-  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;
+    // If there is no parent, or we are the toplevel we don't continue.
+  } while (window && window != aWindow);
 }
 
-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);
-  }
-
-  // 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;
-  }
-
-  // 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;
-    }
-    return AUDIO_CHANNEL_STATE_MUTED;
-  }
-
-  return CheckTelephonyPolicy(aChannel, aChildID);
-}
-
-AudioChannelState
-AudioChannelService::CheckTelephonyPolicy(AudioChannel aChannel,
-                                          uint64_t aChildID)
+PLDHashOperator
+AudioChannelService::TelephonyChannelIsActiveEnumerator(
+                                        const uint64_t& aWindowID,
+                                        nsAutoPtr<AudioChannelWindow>& aWinData,
+                                        void* aPtr)
 {
-  // Only the latest childID is allowed to play with telephony channel.
-  if (aChannel != AudioChannel::Telephony) {
-    return AUDIO_CHANNEL_STATE_NORMAL;
-  }
-
-  MOZ_ASSERT(!mTelephonyChildren.IsEmpty());
-
-#if DEBUG
-  bool found = false;
-  for (uint32_t i = 0, len = mTelephonyChildren.Length(); i < len; ++i) {
-    if (mTelephonyChildren[i].mChildID == aChildID) {
-      found = true;
-      break;
-    }
-  }
-
-  MOZ_ASSERT(found);
-#endif
-
-  return mTelephonyChildren.LastElement().mChildID == aChildID
-           ? AUDIO_CHANNEL_STATE_NORMAL : AUDIO_CHANNEL_STATE_MUTED;
-}
-
-bool
-AudioChannelService::CheckVolumeFadedCondition(AudioChannelInternalType aType,
-                                               bool aElementHidden)
-{
-  // Only normal & content channels are considered
-  if (aType > AUDIO_CHANNEL_INT_CONTENT_HIDDEN) {
-    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;
-  }
-
-  // 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();
+  bool* isActive = static_cast<bool*>(aPtr);
+  *isActive =
+    aWinData->mChannels[(uint32_t)AudioChannel::Telephony].mNumberOfAgents != 0 &&
+   !aWinData->mChannels[(uint32_t)AudioChannel::Telephony].mMuted;
+  return *isActive ? PL_DHASH_STOP : PL_DHASH_NEXT;
 }
 
 bool
 AudioChannelService::TelephonyChannelIsActive()
 {
-  return !mChannelCounters[AUDIO_CHANNEL_INT_TELEPHONY].IsEmpty() ||
-         !mChannelCounters[AUDIO_CHANNEL_INT_TELEPHONY_HIDDEN].IsEmpty();
+  // TODO: no child process check.
+
+  bool active = false;
+  mWindows.Enumerate(TelephonyChannelIsActiveEnumerator, &active);
+  return active;
 }
 
 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;
+/* TODO
+  AudioChannelChildData* data;
+  if (!mData.Get(aChildID, &data)) {
+    return false;
   }
 
-  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);
-  }
+  return data->mChannels[(uint32_t)AudioChannel::Content].mNumberOfAgents != 0 ||
+         data->mChannels[(uint32_t)AudioChannel::Normal].mNumberOfAgents != 0;
+*/
+  return true;
+}
 
-  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;
+PLDHashOperator
+AudioChannelService::AnyAudioChannelIsActiveEnumerator(
+                                        const uint64_t& aWindowID,
+                                        nsAutoPtr<AudioChannelWindow>& aWinData,
+                                        void* aPtr)
+{
+  bool* isActive = static_cast<bool*>(aPtr);
+  for (uint32_t i = 0; kMozAudioChannelAttributeTable[i].tag; ++i) {
+    if (aWinData->mChannels[kMozAudioChannelAttributeTable[i].value].mNumberOfAgents
+        != 0) {
+      *isActive = true;
       break;
     }
   }
 
-  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;
+  return *isActive ? PL_DHASH_STOP : PL_DHASH_NEXT;
 }
 
 bool
 AudioChannelService::AnyAudioChannelIsActive()
 {
-  for (int i = AUDIO_CHANNEL_INT_LAST - 1;
-       i >= AUDIO_CHANNEL_INT_NORMAL; --i) {
-    if (!mChannelCounters[i].IsEmpty()) {
-      return true;
-    }
-  }
-
-  return false;
-}
-
-bool
-AudioChannelService::ChannelsActiveWithHigherPriorityThan(
-  AudioChannelInternalType aType)
-{
-  for (int i = AUDIO_CHANNEL_INT_LAST - 1;
-       i != AUDIO_CHANNEL_INT_CONTENT_HIDDEN; --i) {
-    if (i == aType) {
-      return false;
-    }
-
-    if (!mChannelCounters[i].IsEmpty()) {
-      return true;
-    }
-  }
-
-  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;
+  // TODO: no child process check.
+  bool active = false;
+  mWindows.Enumerate(AnyAudioChannelIsActiveEnumerator, &active);
+  return active;
 }
 
 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 +404,82 @@ 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> window;
+    mWindows.RemoveAndForget(innerID, window);
+    if (window) {
+      window->mAgents.EnumerateRead(NotifyEnumerator, nullptr);
     }
 
 #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;
-
-    case AudioChannel::Publicnotification:
-      return aElementHidden
-               ? AUDIO_CHANNEL_INT_PUBLICNOTIFICATION_HIDDEN
-               : AUDIO_CHANNEL_INT_PUBLICNOTIFICATION;
-
-    default:
-      break;
+    if (mDefChannelChildID == childID) {
+      SetDefaultVolumeControlChannelInternal(-1, false, childID);
+      mDefChannelChildID = CONTENT_PROCESS_ID_UNKNOWN;
+    }
   }
 
-  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)
+AudioChannelService::RefreshAgentsVolumeEnumerator(
+                                                 AudioChannelAgent* aAgent,
+                                                 AudioChannel* 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);
-  }
-
+  aAgent->WindowVolumeChanged();
   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)
-  {}
-
-  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 = mWindows.Get(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;
+  winData->mAgents.EnumerateRead(RefreshAgentsVolumeEnumerator, nullptr);
 }
 
 /* static */ const nsAttrValue::EnumTable*
 AudioChannelService::GetAudioChannelTable()
 {
   return kMozAudioChannelAttributeTable;
 }
 
@@ -1075,17 +493,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 +527,212 @@ 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)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aWindow);
+  MOZ_ASSERT(aWindow->IsOuterWindow());
+
+  AudioChannelWindow* winData = mWindows.LookupOrAdd(aWindow->WindowID());
+  return *winData;
+}
+
+float
+AudioChannelService::GetAudioChannelVolume(nsPIDOMWindow* aWindow,
+                                           AudioChannel aAudioChannel)
 {
-  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 = 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;
+}
 
-      if (i != len - 1) {
-        TelephonyChild child = mTelephonyChildren[i];
-        mTelephonyChildren.RemoveElementAt(i);
-        mTelephonyChildren.AppendElement(child);
-      }
+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);
+}
 
-      return;
-    }
-  }
+NS_IMETHODIMP
+AudioChannelService::SetAudioChannelVolume(nsIDOMWindow* aWindow,
+                                           unsigned short aAudioChannel,
+                                           float aVolume)
+{
+  nsCOMPtr<nsPIDOMWindow> window = GetTopWindow(aWindow);
+  SetAudioChannelVolume(window, (AudioChannel)aAudioChannel, aVolume);
+  return NS_OK;
+}
 
-  mTelephonyChildren.AppendElement(TelephonyChild(aChildID));
+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 (XRE_GetProcessType() != GeckoProcessType_Default) {
+    ContentChild* cc = ContentChild::GetSingleton();
+    if (cc) {
+      cc->SendAudioChannelChangeDefVolChannel(aChannel, aVisible);
     }
+
+    return;
   }
 
-  MOZ_ASSERT(false, "This should not happen.");
+  // 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;
+  }
+
+  // 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());
+  }
 }
+
+/* static */ PLDHashOperator
+AudioChannelService::NotifyEnumerator(AudioChannelAgent* aAgent,
+                                      AudioChannel* aAudioChannel,
+                                      void* aUnused)
+{
+  aAgent->WindowVolumeChanged();
+  return PL_DHASH_NEXT;
+}
+
--- a/dom/audiochannel/AudioChannelService.h
+++ b/dom/audiochannel/AudioChannelService.h
@@ -2,100 +2,100 @@
 /* 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 "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();
 
   /**
    * 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,171 +120,79 @@ 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();
-
-  /**
-   * Send the audio-channel-changed notification for the given process ID if
-   * needed.
-   */
-  void SendAudioChannelChangedNotification(uint64_t aChildID);
+  void Notify(uint64_t aWindowID);
 
-  /* 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);
-
-  AudioChannelState GetStateInternal(AudioChannel aChannel, uint64_t aChildID,
-                                     bool aElementHidden,
-                                     bool aElementWasHidden);
-
-  /* Update the internal type value following the visibility changes */
-  void UpdateChannelType(AudioChannel aChannel, uint64_t aChildID,
-                         bool aElementHidden, bool aElementWasHidden);
+private:
+  AudioChannelService();
+  ~AudioChannelService();
 
   /* 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);
-
-  AudioChannelService();
-  virtual ~AudioChannelService();
+  struct AudioChannelConfig final
+  {
+    AudioChannelConfig()
+      : mVolume(1.0)
+      , mMuted(false)
+      , mNumberOfAgents(0)
+    {}
 
-  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
+    float mVolume;
+    bool mMuted;
+
+    uint32_t mNumberOfAgents;
   };
 
-  bool ChannelsActiveWithHigherPriorityThan(AudioChannelInternalType aType);
-
-  bool CheckVolumeFadedCondition(AudioChannelInternalType aType,
-                                 bool aElementHidden);
-
-  AudioChannelInternalType GetInternalType(AudioChannel aChannel,
-                                           bool aElementHidden);
-
-  class AudioChannelAgentData {
-  public:
-    AudioChannelAgentData(AudioChannel aChannel,
-                          bool aElementHidden,
-                          AudioChannelState aState,
-                          bool aWithVideo)
-    : mChannel(aChannel)
-    , mElementHidden(aElementHidden)
-    , mState(aState)
-    , mWithVideo(aWithVideo)
-    {}
-
-    AudioChannel mChannel;
-    bool mElementHidden;
-    AudioChannelState mState;
-    const bool mWithVideo;
+  struct AudioChannelWindow final
+  {
+    AudioChannelConfig mChannels[NUMBER_OF_AUDIO_CHANNELS];
+    nsClassHashtable<nsPtrHashKey<AudioChannelAgent>, AudioChannel> mAgents;
   };
 
+  AudioChannelWindow&
+  GetOrCreateWindowData(nsPIDOMWindow* aWindow);
+
   static PLDHashOperator
-  NotifyEnumerator(AudioChannelAgent* aAgent,
-                   AudioChannelAgentData* aData, void *aUnused);
+  TelephonyChannelIsActiveEnumerator(const uint64_t& aWindowID,
+                                     nsAutoPtr<AudioChannelWindow>& aWinData,
+                                     void *aPtr);
+
+  static PLDHashOperator
+  AnyAudioChannelIsActiveEnumerator(const uint64_t& aWindowID,
+                                    nsAutoPtr<AudioChannelWindow>& aWinData,
+                                    void *aPtr);
 
   static PLDHashOperator
   RefreshAgentsVolumeEnumerator(AudioChannelAgent* aAgent,
-                                AudioChannelAgentData* aUnused,
+                                AudioChannel* aUnused,
                                 void *aPtr);
 
   static PLDHashOperator
-  CountWindowEnumerator(AudioChannelAgent* aAgent,
-                        AudioChannelAgentData* aUnused,
-                        void *aPtr);
+  NotifyEnumerator(AudioChannelAgent* aAgent,
+                   AudioChannel* aAudioChannel,
+                   void* aUnused);
 
-  static PLDHashOperator
-  WindowDestroyedEnumerator(AudioChannelAgent* aAgent,
-                            nsAutoPtr<AudioChannelAgentData>& aData,
-                            void *aPtr);
+  nsClassHashtable<nsUint64HashKey, AudioChannelWindow> mWindows;
 
-  // This returns the number of agents from this aWindow.
-  uint32_t CountWindow(nsIDOMWindow* aWindow);
-
-  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;
 
   // This is needed for IPC comunication between
   // AudioChannelServiceChild and this class.
   friend class ContentParent;
   friend class ContentChild;
 };
 
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(363ff8d3-5bd2-485a-84ac-125062cbdc19)]
 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;
@@ -87,56 +76,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 startPlaying(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.
    */
   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;
 };
-
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/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/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()
   {
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
@@ -229,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);
       }
     });
 
@@ -1241,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,17 +201,22 @@ 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,
@@ -968,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,153 @@
+/* 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("dom.testing.browserElementAudioChannel.noapp", true);
+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/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
@@ -104,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
@@ -70,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
@@ -117,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]
@@ -218,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>
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>
--- 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/camera/DOMCameraControl.cpp
+++ b/dom/camera/DOMCameraControl.cpp
@@ -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->StartPlaying(&volume, &muted);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
   }
 #endif
   return rv;
 }
 
 already_AddRefed<Promise>
 nsDOMCameraControl::CreatePromise(ErrorResult& aRv)
 {
--- a/dom/fmradio/FMRadio.cpp
+++ b/dom/fmradio/FMRadio.cpp
@@ -130,62 +130,43 @@ FMRadio::Init(nsPIDOMWindow *aWindow)
                                              /* default = */ false);
   if (mHasInternalAntenna) {
     LOG("We have an internal antenna.");
   } else {
     mHeadphoneState = GetCurrentSwitchState(SWITCH_HEADPHONES);
     RegisterSwitchObserver(SWITCH_HEADPHONES, this);
   }
 
-  nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(GetOwner());
-  NS_ENSURE_TRUE_VOID(target);
-  target->AddSystemEventListener(NS_LITERAL_STRING("visibilitychange"), this,
-                                 /* useCapture = */ true,
-                                 /* wantsUntrusted = */ false);
-
-
   // All of the codes below are for AudioChannel. We can directly return here
   // if preferences doesn't enable AudioChannelService.
   NS_ENSURE_TRUE_VOID(Preferences::GetBool("media.useAudioChannelService"));
 
   nsCOMPtr<nsIAudioChannelAgent> audioChannelAgent =
     do_CreateInstance("@mozilla.org/audiochannelagent;1");
   NS_ENSURE_TRUE_VOID(audioChannelAgent);
 
   audioChannelAgent->InitWithWeakCallback(
     GetOwner(),
     nsIAudioChannelAgent::AUDIO_AGENT_CHANNEL_CONTENT,
     this);
 
-  nsCOMPtr<nsIDocShell> docshell = do_GetInterface(GetOwner());
-  NS_ENSURE_TRUE_VOID(docshell);
-
-  bool isActive = false;
-  docshell->GetIsActive(&isActive);
-  audioChannelAgent->SetVisibilityState(isActive);
-
   // Once all necessary resources are got successfully, we just enabled
   // mAudioChannelAgent.
   mAudioChannelAgent = audioChannelAgent;
 }
 
 void
 FMRadio::Shutdown()
 {
   IFMRadioService::Singleton()->RemoveObserver(this);
 
   if (!mHasInternalAntenna) {
     UnregisterSwitchObserver(SWITCH_HEADPHONES, this);
   }
 
-  nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(GetOwner());
-  NS_ENSURE_TRUE_VOID(target);
-  target->RemoveSystemEventListener(NS_LITERAL_STRING("visibilitychange"), this,
-                                    /* useCapture = */ true);
-
   mIsShutdown = true;
 }
 
 JSObject*
 FMRadio::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return FMRadioBinding::Wrap(aCx, this, aGivenProto);
 }
@@ -464,70 +445,39 @@ FMRadio::DisableRDS()
     return nullptr;
   }
 
   nsRefPtr<FMRadioRequest> r = new FMRadioRequest(win, this);
   FMRadioService::Singleton()->DisableRDS(r);
   return r.forget();
 }
 
-NS_IMETHODIMP
-FMRadio::HandleEvent(nsIDOMEvent* aEvent)
-{
-  nsAutoString type;
-  aEvent->GetType(type);
-
-  if (!type.EqualsLiteral("visibilitychange")) {
-    return NS_ERROR_FAILURE;
-  }
-
-  nsCOMPtr<nsIDocShell> docshell = do_GetInterface(GetOwner());
-  NS_ENSURE_TRUE(docshell, NS_ERROR_FAILURE);
-
-  bool isActive = false;
-  docshell->GetIsActive(&isActive);
-
-  mAudioChannelAgent->SetVisibilityState(isActive);
-  return NS_OK;
-}
-
 void
 FMRadio::EnableAudioChannelAgent()
 {
   NS_ENSURE_TRUE_VOID(mAudioChannelAgent);
 
-  int32_t playingState = 0;
-  mAudioChannelAgent->StartPlaying(&playingState);
-  SetCanPlay(playingState == AudioChannelState::AUDIO_CHANNEL_STATE_NORMAL);
+  float volume = 0.0;
+  bool muted = true;
+  mAudioChannelAgent->StartPlaying(&volume, &muted);
+  WindowVolumeChanged(volume, muted);
 
   mAudioChannelAgentEnabled = true;
 }
 
 NS_IMETHODIMP
-FMRadio::CanPlayChanged(int32_t aCanPlay)
+FMRadio::WindowVolumeChanged(float aVolume, bool aMuted)
 {
-  SetCanPlay(!(aCanPlay == AudioChannelState::AUDIO_CHANNEL_STATE_MUTED));
+  IFMRadioService::Singleton()->EnableAudio(!aMuted);
+  // TODO: what about the volume?
   return NS_OK;
 }
 
-NS_IMETHODIMP
-FMRadio::WindowVolumeChanged()
-{
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-void
-FMRadio::SetCanPlay(bool aCanPlay)
-{
-  IFMRadioService::Singleton()->EnableAudio(aCanPlay);
-}
-
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FMRadio)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
   NS_INTERFACE_MAP_ENTRY(nsIAudioChannelAgentCallback)
-  NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(FMRadio, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(FMRadio, DOMEventTargetHelper)
 
 END_FMRADIO_NAMESPACE
 
--- a/dom/fmradio/FMRadio.h
+++ b/dom/fmradio/FMRadio.h
@@ -20,17 +20,16 @@ BEGIN_FMRADIO_NAMESPACE
 
 class DOMRequest;
 
 class FMRadio final : public DOMEventTargetHelper
                     , public hal::SwitchObserver
                     , public FMRadioEventObserver
                     , public nsSupportsWeakReference
                     , public nsIAudioChannelAgentCallback
-                    , public nsIDOMEventListener
 
 {
   friend class FMRadioRequest;
 
 public:
   FMRadio();
 
   NS_DECL_ISUPPORTS_INHERITED
@@ -104,23 +103,19 @@ public:
   IMPL_EVENT_HANDLER(antennaavailablechange);
   IMPL_EVENT_HANDLER(frequencychange);
   IMPL_EVENT_HANDLER(pichange);
   IMPL_EVENT_HANDLER(ptychange);
   IMPL_EVENT_HANDLER(pschange);
   IMPL_EVENT_HANDLER(rtchange);
   IMPL_EVENT_HANDLER(newrdsgroup);
 
-  // nsIDOMEventListener
-  NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent);
-
 private:
   ~FMRadio();
 
-  void SetCanPlay(bool aCanPlay);
   void EnableAudioChannelAgent();
 
   hal::SwitchState mHeadphoneState;
   uint32_t mRdsGroupMask;
   bool mAudioChannelAgentEnabled;
   bool mHasInternalAntenna;
   bool mIsShutdown;
 
--- a/dom/html/HTMLAudioElement.cpp
+++ b/dom/html/HTMLAudioElement.cpp
@@ -9,17 +9,16 @@
 #include "nsError.h"
 #include "nsGenericHTMLElement.h"
 #include "nsGkAtoms.h"
 #include "nsIDocument.h"
 #include "jsfriendapi.h"
 #include "nsContentUtils.h"
 #include "nsJSUtils.h"
 #include "AudioSampleFormat.h"
-#include "AudioChannelCommon.h"
 #include <algorithm>
 #include "nsComponentManagerUtils.h"
 #include "nsIHttpChannel.h"
 #include "mozilla/dom/TimeRanges.h"
 #include "AudioStream.h"
 
 NS_IMPL_NS_NEW_HTML_ELEMENT(Audio)
 
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -1826,26 +1826,17 @@ void HTMLMediaElement::SetMutedInternal(
     return;
   }
 
   SetVolumeInternal();
 }
 
 void HTMLMediaElement::SetVolumeInternal()
 {
-  float effectiveVolume = mMuted ? 0.0f :
-    mAudioChannelFaded ? float(mVolume) * FADED_VOLUME_RATIO : float(mVolume);
-
-  if (mAudioChannelAgent) {
-    float volume;
-    nsresult rv = mAudioChannelAgent->GetWindowVolume(&volume);
-    if (NS_SUCCEEDED(rv)) {
-      effectiveVolume *= volume;
-    }
-  }
+  float effectiveVolume = mMuted ? 0.0f : float(mVolume * mAudioChannelVolume);
 
   if (mDecoder) {
     mDecoder->SetVolume(effectiveVolume);
   } else if (mSrcStream) {
     GetSrcMediaStream()->SetAudioOutputVolume(this, effectiveVolume);
   }
 }
 
@@ -2087,17 +2078,17 @@ HTMLMediaElement::HTMLMediaElement(alrea
     mHasPlayedOrSeeked(false),
     mHasSelfReference(false),
     mShuttingDown(false),
     mSuspendedForPreloadNone(false),
     mMediaSecurityVerified(false),
     mCORSMode(CORS_NONE),
     mIsEncrypted(false),
     mDownloadSuspendedByCache(false, "HTMLMediaElement::mDownloadSuspendedByCache"),
-    mAudioChannelFaded(false),
+    mAudioChannelVolume(1.0),
     mPlayingThroughTheAudioChannel(false),
     mDisableVideo(false),
     mPlayBlockedBecauseHidden(false),
     mElementInTreeState(ELEMENT_NOT_INTREE)
 {
   if (!gMediaElementLog) {
     gMediaElementLog = PR_NewLogModule("nsMediaElement");
   }
@@ -4038,23 +4029,16 @@ bool HTMLMediaElement::IsBeingDestroyed(
 void HTMLMediaElement::NotifyOwnerDocumentActivityChanged()
 {
   nsIDocument* ownerDoc = OwnerDoc();
   if (mDecoder && !IsBeingDestroyed()) {
     mDecoder->SetElementVisibility(!ownerDoc->Hidden());
     mDecoder->NotifyOwnerActivityChanged();
   }
 
-  // SetVisibilityState will update mMuted with MUTED_BY_AUDIO_CHANNEL via the
-  // CanPlayChanged callback.
-  if (UseAudioChannelService() && mPlayingThroughTheAudioChannel &&
-      mAudioChannelAgent) {
-    AutoNoJSAPI nojsapi;
-    mAudioChannelAgent->SetVisibilityState(!ownerDoc->Hidden());
-  }
   bool pauseElement = !IsActive() || (mMuted & MUTED_BY_AUDIO_CHANNEL);
 
   SuspendOrResumeElement(pauseElement, !IsActive());
 
   if (!mPausedForInactiveDocumentOrChannel &&
       mPlayBlockedBecauseHidden &&
       !OwnerDoc()->Hidden()) {
     LOG(LogLevel::Debug, ("%p Resuming playback now that owner doc is visble.", this));
@@ -4461,36 +4445,34 @@ NS_IMETHODIMP HTMLMediaElement::SetMozPr
 }
 
 ImageContainer* HTMLMediaElement::GetImageContainer()
 {
   VideoFrameContainer* container = GetVideoFrameContainer();
   return container ? container->GetImageContainer() : nullptr;
 }
 
-nsresult HTMLMediaElement::UpdateChannelMuteState(AudioChannelState aCanPlay)
+nsresult HTMLMediaElement::UpdateChannelMuteState(float aVolume, bool aMuted)
 {
   if (!UseAudioChannelService()) {
     return NS_OK;
   }
 
-  if ((aCanPlay == AUDIO_CHANNEL_STATE_FADED && !mAudioChannelFaded) ||
-      (aCanPlay != AUDIO_CHANNEL_STATE_FADED && mAudioChannelFaded)) {
-    mAudioChannelFaded = !mAudioChannelFaded;
+  if (mAudioChannelVolume != aVolume) {
+    mAudioChannelVolume = aVolume;
     SetVolumeInternal();
   }
 
   // We have to mute this channel.
-  if (aCanPlay == AUDIO_CHANNEL_STATE_MUTED && !(mMuted & MUTED_BY_AUDIO_CHANNEL)) {
+  if (aMuted && !(mMuted & MUTED_BY_AUDIO_CHANNEL)) {
     SetMutedInternal(mMuted | MUTED_BY_AUDIO_CHANNEL);
     if (UseAudioChannelAPI()) {
       DispatchAsyncEvent(NS_LITERAL_STRING("mozinterruptbegin"));
     }
-  } else if (aCanPlay != AUDIO_CHANNEL_STATE_MUTED &&
-             (mMuted & MUTED_BY_AUDIO_CHANNEL)) {
+  } else if (!aMuted && (mMuted & MUTED_BY_AUDIO_CHANNEL)) {
     SetMutedInternal(mMuted & ~MUTED_BY_AUDIO_CHANNEL);
     if (UseAudioChannelAPI()) {
       DispatchAsyncEvent(NS_LITERAL_STRING("mozinterruptend"));
     }
   }
 
   SuspendOrResumeElement(mMuted & MUTED_BY_AUDIO_CHANNEL, false);
   return NS_OK;
@@ -4518,64 +4500,44 @@ void HTMLMediaElement::UpdateAudioChanne
     }
 
     if (!mAudioChannelAgent) {
       nsresult rv;
       mAudioChannelAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1", &rv);
       if (!mAudioChannelAgent) {
         return;
       }
-      // Use a weak ref so the audio channel agent can't leak |this|.
-      if (AudioChannel::Normal == mAudioChannel && IsVideo()) {
-        mAudioChannelAgent->InitWithVideo(OwnerDoc()->GetWindow(),
-                                          static_cast<int32_t>(mAudioChannel),
-                                          this, true);
-      } else {
-        mAudioChannelAgent->InitWithWeakCallback(OwnerDoc()->GetWindow(),
-                                                 static_cast<int32_t>(mAudioChannel),
-                                                 this);
-      }
-      mAudioChannelAgent->SetVisibilityState(!OwnerDoc()->Hidden());
+      mAudioChannelAgent->InitWithWeakCallback(OwnerDoc()->GetWindow(),
+                                               static_cast<int32_t>(mAudioChannel),
+                                               this);
     }
 
     // This is needed to pass nsContentUtils::IsCallerChrome().
     // AudioChannel API should not called from content but it can happen that
     // this method has some content JS in its stack.
     AutoNoJSAPI nojsapi;
 
     if (mPlayingThroughTheAudioChannel) {
-      int32_t canPlay;
-      mAudioChannelAgent->StartPlaying(&canPlay);
-      CanPlayChanged(canPlay);
+      float volume = 0.0;
+      bool muted = true;
+      mAudioChannelAgent->StartPlaying(&volume, &muted);
+      WindowVolumeChanged(volume, muted);
     } else {
       mAudioChannelAgent->StopPlaying();
       mAudioChannelAgent = nullptr;
     }
   }
 }
 
-/* void canPlayChanged (in boolean canPlay); */
-NS_IMETHODIMP HTMLMediaElement::CanPlayChanged(int32_t canPlay)
-{
-  static_assert(static_cast<AudioChannelState>(
-                nsIAudioChannelAgent::AUDIO_AGENT_STATE_NORMAL) ==
-                AUDIO_CHANNEL_STATE_NORMAL &&
-                static_cast<AudioChannelState>(
-                nsIAudioChannelAgent::AUDIO_AGENT_STATE_MUTED) ==
-                AUDIO_CHANNEL_STATE_MUTED &&
-                static_cast<AudioChannelState>(
-                nsIAudioChannelAgent::AUDIO_AGENT_STATE_FADED) ==
-                AUDIO_CHANNEL_STATE_FADED,
-                "Enum of channel state on nsIAudioChannelAgent.idl should be "
-                "the same with AudioChannelCommon.h");
-
+NS_IMETHODIMP HTMLMediaElement::WindowVolumeChanged(float aVolume, bool aMuted)
+{
   NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
 
-  UpdateChannelMuteState(static_cast<AudioChannelState>(canPlay));
-  mPaused.SetCanPlay(canPlay != AUDIO_CHANNEL_STATE_MUTED);
+  UpdateChannelMuteState(aVolume, aMuted);
+  mPaused.SetCanPlay(!aMuted);
   return NS_OK;
 }
 
 #ifdef MOZ_EME
 MediaKeys*
 HTMLMediaElement::GetMediaKeys() const
 {
   return mMediaKeys;
@@ -4710,22 +4672,16 @@ HTMLMediaElement::GetTopLevelPrincipal()
   if (!doc) {
     return nullptr;
   }
   principal = doc->NodePrincipal();
   return principal.forget();
 }
 #endif // MOZ_EME
 
-NS_IMETHODIMP HTMLMediaElement::WindowVolumeChanged()
-{
-  SetVolumeInternal();
-  return NS_OK;
-}
-
 AudioTrackList*
 HTMLMediaElement::AudioTracks()
 {
   if (!mAudioTrackList) {
     nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(OwnerDoc()->GetParentObject());
     mAudioTrackList = new AudioTrackList(window, this);
   }
   return mAudioTrackList;
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -8,17 +8,16 @@
 
 #include "nsIDOMHTMLMediaElement.h"
 #include "nsGenericHTMLElement.h"
 #include "MediaDecoderOwner.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsIObserver.h"
 #include "mozilla/CORSMode.h"
 #include "DOMMediaStream.h"
-#include "AudioChannelCommon.h"
 #include "DecoderTraits.h"
 #include "nsIAudioChannelAgent.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/TextTrackManager.h"
 #include "MediaDecoder.h"
 #ifdef MOZ_EME
 #include "mozilla/dom/MediaKeys.h"
@@ -1003,18 +1002,18 @@ protected:
 #ifdef MOZ_EME
   void ReportEMETelemetry();
 #endif
   void ReportMSETelemetry();
 
   // Check the permissions for audiochannel.
   bool CheckAudioChannelPermissions(const nsAString& aType);
 
-  // This method does the check for muting/fading/unmuting the audio channel.
-  nsresult UpdateChannelMuteState(mozilla::dom::AudioChannelState aCanPlay);
+  // This method does the check for muting/nmuting the audio channel.
+  nsresult UpdateChannelMuteState(float aVolume, bool aMuted);
 
   // Seeks to aTime seconds. aSeekType can be Exact to seek to exactly the
   // seek target, or PrevSyncPoint if a quicker but less precise seek is
   // desired, and we'll seek to the sync point (keyframe and/or start of the
   // next block of audio samples) preceeding seek target.
   void Seek(double aTime, SeekTarget::Type aSeekType, ErrorResult& aRv);
 
   // Update the audio channel playing state
@@ -1358,18 +1357,18 @@ protected:
 #endif // MOZ_EME
 
   // True if the media's channel's download has been suspended.
   Watchable<bool> mDownloadSuspendedByCache;
 
   // Audio Channel.
   AudioChannel mAudioChannel;
 
-  // The audio channel has been faded.
-  bool mAudioChannelFaded;
+  // The audio channel volume
+  float mAudioChannelVolume;
 
   // Is this media element playing?
   bool mPlayingThroughTheAudioChannel;
 
   // Disable the video playback by track selection. This flag might not be
   // enough if we ever expand the ability of supporting multi-tracks video
   // playback.
   bool mDisableVideo;
--- a/dom/html/nsBrowserElement.cpp
+++ b/dom/html/nsBrowserElement.cpp
@@ -4,25 +4,31 @@
  * 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 "nsBrowserElement.h"
 
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "mozilla/dom/BrowserElementBinding.h"
+#include "mozilla/dom/BrowserElementAudioChannel.h"
 #include "mozilla/dom/DOMRequest.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/ToJSValue.h"
 
+#include "AudioChannelService.h"
+
+#include "mozIApplication.h"
 #include "nsComponentManagerUtils.h"
 #include "nsContentUtils.h"
 #include "nsFrameLoader.h"
+#include "nsIAppsService.h"
 #include "nsIDOMDOMRequest.h"
 #include "nsIDOMElement.h"
+#include "nsIMozBrowserFrame.h"
 #include "nsINode.h"
 #include "nsIPrincipal.h"
 
 using namespace mozilla::dom;
 
 namespace mozilla {
 
 bool
@@ -503,16 +509,151 @@ nsBrowserElement::SetInputMethodActive(b
     }
     return nullptr;
   }
 
   return req.forget().downcast<DOMRequest>();
 }
 
 void
+nsBrowserElement::GetAllowedAudioChannels(
+                 nsTArray<nsRefPtr<BrowserElementAudioChannel>>& aAudioChannels,
+                 ErrorResult& aRv)
+{
+  aAudioChannels.Clear();
+
+  NS_ENSURE_TRUE_VOID(IsBrowserElementOrThrow(aRv));
+  NS_ENSURE_TRUE_VOID(IsNotWidgetOrThrow(aRv));
+
+  // If empty, it means that this is the first call of this method.
+  if (mBrowserElementAudioChannels.IsEmpty()) {
+    nsCOMPtr<nsIFrameLoader> frameLoader = GetFrameLoader();
+    if (!frameLoader) {
+      aRv.Throw(NS_ERROR_FAILURE);
+      return;
+    }
+
+    nsCOMPtr<nsIDOMElement> frameElement;
+    aRv = frameLoader->GetOwnerElement(getter_AddRefs(frameElement));
+    if (NS_WARN_IF(aRv.Failed())) {
+      return;
+    }
+
+    MOZ_ASSERT(frameElement);
+
+    nsCOMPtr<nsIDOMDocument> doc;
+    aRv = frameElement->GetOwnerDocument(getter_AddRefs(doc));
+    if (NS_WARN_IF(aRv.Failed())) {
+      return;
+    }
+
+    MOZ_ASSERT(doc);
+
+    nsCOMPtr<nsIDOMWindow> win;
+    aRv = doc->GetDefaultView(getter_AddRefs(win));
+    if (NS_WARN_IF(aRv.Failed())) {
+      return;
+    }
+
+    MOZ_ASSERT(win);
+
+    nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(win);
+
+    if (!window->IsInnerWindow()) {
+      window = window->GetCurrentInnerWindow();
+    }
+
+    nsCOMPtr<nsIMozBrowserFrame> mozBrowserFrame =
+      do_QueryInterface(frameElement);
+    if (NS_WARN_IF(!mozBrowserFrame)) {
+      aRv.Throw(NS_ERROR_FAILURE);
+      return;
+    }
+
+    nsAutoString manifestURL;
+    aRv = mozBrowserFrame->GetAppManifestURL(manifestURL);
+    if (NS_WARN_IF(aRv.Failed())) {
+      return;
+    }
+
+    nsCOMPtr<nsIAppsService> appsService =
+      do_GetService("@mozilla.org/AppsService;1");
+    if (NS_WARN_IF(!appsService)) {
+      aRv.Throw(NS_ERROR_FAILURE);
+      return;
+    }
+
+    nsCOMPtr<mozIApplication> app;
+    aRv = appsService->GetAppByManifestURL(manifestURL, getter_AddRefs(app));
+    if (NS_WARN_IF(aRv.Failed())) {
+      return;
+    }
+
+    bool noapp = false;
+    Preferences::GetBool("dom.testing.browserElementAudioChannel.noapp", &noapp);
+
+    if (!noapp && !app) {
+      aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
+      return;
+    }
+
+    // Normal is always allowed.
+    nsTArray<nsRefPtr<BrowserElementAudioChannel>> channels;
+
+    nsRefPtr<BrowserElementAudioChannel> ac =
+      new BrowserElementAudioChannel(window, frameLoader, mBrowserElementAPI,
+                                     AudioChannel::Normal);
+
+    aRv = ac->Initialize();
+    if (NS_WARN_IF(aRv.Failed())) {
+      return;
+    }
+
+    channels.AppendElement(ac);
+
+    // app can be null in case we are in a test.
+    if (app) {
+      const nsAttrValue::EnumTable* audioChannelTable =
+        AudioChannelService::GetAudioChannelTable();
+
+      bool allowed;
+      nsAutoCString permissionName;
+
+      for (uint32_t i = 0; audioChannelTable && audioChannelTable[i].tag; ++i) {
+        permissionName.AssignASCII("audio-channel-");
+        permissionName.AppendASCII(audioChannelTable[i].tag);
+
+        aRv = app->HasPermission(permissionName.get(), &allowed);
+        if (NS_WARN_IF(aRv.Failed())) {
+          return;
+        }
+
+        if (allowed) {
+          nsRefPtr<BrowserElementAudioChannel> ac =
+            new BrowserElementAudioChannel(window, frameLoader,
+                                           mBrowserElementAPI,
+                                           (AudioChannel)audioChannelTable[i].value);
+
+          aRv = ac->Initialize();
+          if (NS_WARN_IF(aRv.Failed())) {
+            return;
+          }
+
+          channels.AppendElement(ac);
+        }
+      }
+    }
+
+    mBrowserElementAudioChannels.AppendElements(channels);
+  }
+
+  aAudioChannels.AppendElements(mBrowserElementAudioChannels);
+}
+
+void
 nsBrowserElement::SetNFCFocus(bool aIsFocus,
                               ErrorResult& aRv)
 {
   NS_ENSURE_TRUE_VOID(IsBrowserElementOrThrow(aRv));
 
   nsRefPtr<nsFrameLoader> frameLoader = GetFrameLoader();
   if (!frameLoader) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
--- a/dom/html/nsBrowserElement.h
+++ b/dom/html/nsBrowserElement.h
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsBrowserElement_h
 #define nsBrowserElement_h
 
 #include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/dom/BrowserElementAudioChannel.h"
 
 #include "nsCOMPtr.h"
 #include "nsIBrowserElementAPI.h"
 
 class nsFrameLoader;
 
 namespace mozilla {
 
@@ -65,16 +66,20 @@ public:
 
   already_AddRefed<dom::DOMRequest>
   Download(const nsAString& aUrl,
            const dom::BrowserElementDownloadOptions& options,
            ErrorResult& aRv);
 
   already_AddRefed<dom::DOMRequest> PurgeHistory(ErrorResult& aRv);
 
+  void GetAllowedAudioChannels(
+            nsTArray<nsRefPtr<dom::BrowserElementAudioChannel>>& aAudioChannels,
+            ErrorResult& aRv);
+
   already_AddRefed<dom::DOMRequest>
   GetScreenshot(uint32_t aWidth,
                 uint32_t aHeight,
                 const nsAString& aMimeType,
                 ErrorResult& aRv);
 
   void Zoom(float aZoom, ErrorResult& aRv);
 
@@ -97,16 +102,17 @@ public:
 
   void SetNFCFocus(bool isFocus,
                    ErrorResult& aRv);
 
 protected:
   NS_IMETHOD_(already_AddRefed<nsFrameLoader>) GetFrameLoader() = 0;
   void InitBrowserElementAPI();
   nsCOMPtr<nsIBrowserElementAPI> mBrowserElementAPI;
+  nsTArray<nsRefPtr<dom::BrowserElementAudioChannel>> mBrowserElementAudioChannels;
 
 private:
   bool IsBrowserElementOrThrow(ErrorResult& aRv);
   bool IsNotWidgetOrThrow(ErrorResult& aRv);
   bool mOwnerIsWidget;
 };
 
 } // namespace mozilla
--- a/dom/html/nsGenericHTMLFrameElement.cpp
+++ b/dom/html/nsGenericHTMLFrameElement.cpp
@@ -1,16 +1,17 @@
 /* -*- 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 "nsGenericHTMLFrameElement.h"
 
+#include "mozilla/dom/BrowserElementAudioChannel.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ErrorResult.h"
 #include "GeckoProfiler.h"
 #include "mozIApplication.h"
 #include "nsAttrValueInlines.h"
 #include "nsContentUtils.h"
 #include "nsIAppsService.h"
@@ -29,18 +30,30 @@ using namespace mozilla;
 using namespace mozilla::dom;
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsGenericHTMLFrameElement)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsGenericHTMLFrameElement,
                                                   nsGenericHTMLElement)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameLoader)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowserElementAPI)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowserElementAudioChannels)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsGenericHTMLFrameElement,
+                                                nsGenericHTMLElement)
+  if (tmp->mFrameLoader) {
+    tmp->mFrameLoader->Destroy();
+  }
+
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameLoader)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowserElementAPI)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowserElementAudioChannels)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
 NS_IMPL_ADDREF_INHERITED(nsGenericHTMLFrameElement, nsGenericHTMLElement)
 NS_IMPL_RELEASE_INHERITED(nsGenericHTMLFrameElement, nsGenericHTMLElement)
 
 NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(nsGenericHTMLFrameElement)
   NS_INTERFACE_TABLE_INHERITED(nsGenericHTMLFrameElement,
                                nsIFrameLoaderOwner,
                                nsIDOMMozBrowserFrame,
                                nsIMozBrowserFrame)
--- a/dom/html/nsGenericHTMLFrameElement.h
+++ b/dom/html/nsGenericHTMLFrameElement.h
@@ -66,18 +66,18 @@ public:
                                 const nsAttrValue* aValue,
                                 bool aNotify) override;
   virtual void DestroyContent() override;
 
   nsresult CopyInnerTo(mozilla::dom::Element* aDest);
 
   virtual int32_t TabIndexDefault() override;
 
-  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED_NO_UNLINK(nsGenericHTMLFrameElement,
-                                                     nsGenericHTMLElement)
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsGenericHTMLFrameElement,
+                                           nsGenericHTMLElement)
 
   void SwapFrameLoaders(nsXULElement& aOtherOwner, mozilla::ErrorResult& aError);
 
   static bool BrowserFramesEnabled();
 
   /**
    * Helper method to map a HTML 'scrolling' attribute value to a nsIScrollable
    * enum value.  scrolling="no" (and its synonyms) maps to
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -177,17 +177,16 @@
 #endif
 
 #include "ProcessUtils.h"
 #include "StructuredCloneUtils.h"
 #include "URIUtils.h"
 #include "nsContentUtils.h"
 #include "nsIPrincipal.h"
 #include "nsDeviceStorage.h"
-#include "AudioChannelService.h"
 #include "DomainPolicy.h"
 #include "mozilla/dom/DataStoreService.h"
 #include "mozilla/dom/telephony/PTelephonyChild.h"
 #include "mozilla/dom/time/DateCacheCleaner.h"
 #include "mozilla/dom/voicemail/VoicemailIPCService.h"
 #include "mozilla/net/NeckoMessageUtils.h"
 #include "mozilla/widget/PuppetBidiKeyboard.h"
 #include "mozilla/RemoteSpellCheckEngineChild.h"
@@ -939,27 +938,16 @@ NS_IMETHODIMP MemoryReportRequestChild::
     mgr->GetReportsForThisProcessExtended(cb, nullptr, mAnonymize,
                                           FileDescriptorToFILE(mDMDFile, "wb"));
 
     bool sent = Send__delete__(this);
     return sent ? NS_OK : NS_ERROR_FAILURE;
 }
 
 bool
-ContentChild::RecvAudioChannelNotify()
-{
-    nsRefPtr<AudioChannelService> service =
-        AudioChannelService::GetAudioChannelService();
-    if (service) {
-        service->Notify();
-    }
-    return true;
-}
-
-bool
 ContentChild::RecvDataStoreNotify(const uint32_t& aAppId,
                                   const nsString& aName,
                                   const nsString& aManifestURL)
 {
   nsRefPtr<DataStoreService> service = DataStoreService::GetOrCreate();
   if (NS_WARN_IF(!service)) {
     return false;
   }
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -196,19 +196,16 @@ public:
     DeallocPCycleCollectWithLogsChild(PCycleCollectWithLogsChild* aActor) override;
     virtual bool
     RecvPCycleCollectWithLogsConstructor(PCycleCollectWithLogsChild* aChild,
                                          const bool& aDumpAllTraces,
                                          const FileDescriptor& aGCLog,
                                          const FileDescriptor& aCCLog) override;
 
     virtual bool
-    RecvAudioChannelNotify() override;
-
-    virtual bool
     RecvDataStoreNotify(const uint32_t& aAppId, const nsString& aName,
                         const nsString& aManifestURL) override;
 
     virtual PTestShellChild* AllocPTestShellChild() override;
     virtual bool DeallocPTestShellChild(PTestShellChild*) override;
     virtual bool RecvPTestShellConstructor(PTestShellChild*) override;
     jsipc::CPOWManager* GetCPOWManager() override;
 
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -2787,73 +2787,20 @@ ContentParent::RecvFirstIdle()
     // which we use as a good time to prelaunch another process. If we
     // prelaunch any sooner than this, then we'll be competing with the
     // child process and slowing it down.
     PreallocatedProcessManager::AllocateAfterDelay();
     return true;
 }
 
 bool
-ContentParent::RecvAudioChannelGetState(const AudioChannel& aChannel,
-                                        const bool& aElementHidden,
-                                        const bool& aElementWasHidden,
-                                        AudioChannelState* aState)
-{
-    nsRefPtr<AudioChannelService> service =
-        AudioChannelService::GetOrCreateAudioChannelService();
-    *aState = AUDIO_CHANNEL_STATE_NORMAL;
-    MOZ_ASSERT(service);
-    *aState = service->GetStateInternal(aChannel, mChildID,
-                                        aElementHidden, aElementWasHidden);
-
-    return true;
-}
-
-bool
-ContentParent::RecvAudioChannelRegisterType(const AudioChannel& aChannel,
-                                            const bool& aWithVideo)
-{
-    nsRefPtr<AudioChannelService> service =
-        AudioChannelService::GetOrCreateAudioChannelService();
-    MOZ_ASSERT(service);
-    service->RegisterType(aChannel, mChildID, aWithVideo);
-
-    return true;
-}
-
-bool
-ContentParent::RecvAudioChannelUnregisterType(const AudioChannel& aChannel,
-                                              const bool& aElementHidden,
-                                              const bool& aWithVideo)
-{
-    nsRefPtr<AudioChannelService> service =
-        AudioChannelService::GetOrCreateAudioChannelService();
-    MOZ_ASSERT(service);
-    service->UnregisterType(aChannel, aElementHidden, mChildID, aWithVideo);
-
-    return true;
-}
-
-bool
-ContentParent::RecvAudioChannelChangedNotification()
-{
-    nsRefPtr<AudioChannelService> service =
-        AudioChannelService::GetOrCreateAudioChannelService();
-    MOZ_ASSERT(service);
-    service->SendAudioChannelChangedNotification(ChildID());
-
-    return true;
-}
-
-bool
 ContentParent::RecvAudioChannelChangeDefVolChannel(const int32_t& aChannel,
                                                    const bool& aHidden)
 {
-    nsRefPtr<AudioChannelService> service =
-        AudioChannelService::GetOrCreateAudioChannelService();
+    nsRefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
     MOZ_ASSERT(service);
     service->SetDefaultVolumeControlChannelInternal(aChannel,
                                                     aHidden, mChildID);
     return true;
 }
 
 bool
 ContentParent::RecvDataStoreGetStores(
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -747,29 +747,16 @@ private:
                                  const uint32_t& aColNumber,
                                  const uint32_t& aFlags,
                                  const nsCString& aCategory) override;
 
     virtual bool RecvPrivateDocShellsExist(const bool& aExist) override;
 
     virtual bool RecvFirstIdle() override;
 
-    virtual bool RecvAudioChannelGetState(const AudioChannel& aChannel,
-                                          const bool& aElementHidden,
-                                          const bool& aElementWasHidden,
-                                          AudioChannelState* aValue) override;
-
-    virtual bool RecvAudioChannelRegisterType(const AudioChannel& aChannel,
-                                              const bool& aWithVideo) override;
-    virtual bool RecvAudioChannelUnregisterType(const AudioChannel& aChannel,
-                                                const bool& aElementHidden,
-                                                const bool& aWithVideo) override;
-
-    virtual bool RecvAudioChannelChangedNotification() override;
-
     virtual bool RecvAudioChannelChangeDefVolChannel(const int32_t& aChannel,
                                                      const bool& aHidden) override;
     virtual bool RecvGetSystemMemory(const uint64_t& getterId) override;
 
     virtual bool RecvGetLookAndFeelCache(nsTArray<LookAndFeelInt>* aLookAndFeelIntCache) override;
 
     virtual bool RecvDataStoreGetStores(
                        const nsString& aName,
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -543,16 +543,20 @@ parent:
 
     prio(high) sync DispatchWheelEvent(WidgetWheelEvent event);
     prio(high) sync DispatchMouseEvent(WidgetMouseEvent event);
     prio(high) sync DispatchKeyboardEvent(WidgetKeyboardEvent event);
 
     InvokeDragSession(IPCDataTransfer[] transfers, uint32_t action,
                       nsCString visualData, uint32_t width, uint32_t height,
                       uint32_t stride, uint8_t format, int32_t dragAreaX, int32_t dragAreaY);
+
+    async AudioChannelActivityNotification(uint32_t aAudioChannel,
+                                           bool aActive);
+
 child:
     /**
      * Notify the remote browser that it has been Show()n on this
      * side, with the given |visibleRect|.  This message is expected
      * to trigger creation of the remote browser's "widget".
      *
      * |Show()| and |Move()| take IntSizes rather than Rects because
      * content processes always render to a virtual <0, 0> top-left
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -76,18 +76,16 @@ using struct OverrideMapping from "mozil
 using base::ChildPrivileges from "base/process_util.h";
 using base::ProcessId from "base/process.h";
 using struct IPC::Permission from "mozilla/net/NeckoMessageUtils.h";
 using class IPC::Principal from "mozilla/dom/PermissionMessageUtils.h";
 using struct mozilla::null_t from "ipc/IPCMessageUtils.h";
 using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
 using mozilla::dom::asmjscache::OpenMode from "mozilla/dom/asmjscache/AsmJSCache.h";
 using mozilla::dom::asmjscache::WriteParams from "mozilla/dom/asmjscache/AsmJSCache.h";
-using mozilla::dom::AudioChannel from "mozilla/dom/AudioChannelBinding.h";
-using mozilla::dom::AudioChannelState from "AudioChannelCommon.h";
 using mozilla::dom::NativeThreadId from "mozilla/dom/TabMessageUtils.h";
 using mozilla::dom::quota::PersistenceType from "mozilla/dom/quota/PersistenceType.h";
 using mozilla::hal::ProcessPriority from "mozilla/HalTypes.h";
 using gfxIntSize from "nsSize.h";
 using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h";
 using mozilla::dom::ContentParentId from "mozilla/dom/ipc/IdType.h";
 using struct LookAndFeelInt from "mozilla/widget/WidgetMessageUtils.h";
 using struct mozilla::OwningSerializedStructuredCloneBuffer from "ipc/IPCMessageUtils.h";
@@ -493,21 +491,16 @@ child:
      * usually only be performed zero or one times.  The child may
      * abnormally exit if this fails; the details are OS-specific.
      */
     async SetProcessSandbox();
 
     PMemoryReportRequest(uint32_t generation, bool anonymize,
                          bool minimizeMemoryUsage, MaybeFileDesc DMDFile);
 
-    /**
-     * Notify the AudioChannelService in the child processes.
-     */
-    async AudioChannelNotify();
-
     async SpeakerManagerNotify();
 
     /**
      * Communication between the PuppetBidiKeyboard and the actual
      * BidiKeyboard hosted by the parent
      */
     async BidiKeyboardNotify(bool isLangRTL);
 
@@ -866,27 +859,16 @@ parent:
         returns (bool showPassword);
 
     // Notify the parent of the presence or absence of private docshells
     PrivateDocShellsExist(bool aExist);
 
     // Tell the parent that the child has gone idle for the first time
     async FirstIdle();
 
-    // Get Muted from the main AudioChannelService.
-    sync AudioChannelGetState(AudioChannel aChannel, bool aElementHidden,
-                              bool aElementWasHidden)
-        returns (AudioChannelState value);
-
-    sync AudioChannelRegisterType(AudioChannel aChannel, bool aWithVideo);
-    sync AudioChannelUnregisterType(AudioChannel aChannel,
-                                    bool aElementHidden,
-                                    bool aWithVideo);
-
-    async AudioChannelChangedNotification();
     async AudioChannelChangeDefVolChannel(int32_t aChannel, bool aHidden);
 
     sync DataStoreGetStores(nsString aName, nsString aOwner, Principal aPrincipal)
         returns (DataStoreSetting[] dataStores);
 
     async FilePathUpdateNotify(nsString aType,
                                nsString aStorageName,
                                nsString aFilepath,
--- a/dom/ipc/ProcessPriorityManager.cpp
+++ b/dom/ipc/ProcessPriorityManager.cpp
@@ -1036,17 +1036,17 @@ ParticularProcessPriorityManager::Comput
       PROCESS_PRIORITY_FOREGROUND;
   }
 
   if ((mHoldsCPUWakeLock || mHoldsHighPriorityWakeLock) &&
       IsExpectingSystemMessage()) {
     return PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE;
   }
 
-  AudioChannelService* service = AudioChannelService::GetOrCreateAudioChannelService();
+  nsRefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
   if (service->ProcessContentOrNormalChannelIsActive(ChildID())) {
     return PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE;
   }
 
   return mIsActivityOpener ? PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE
                            : PROCESS_PRIORITY_BACKGROUND;
 }
 
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -885,16 +885,32 @@ TabChild::TabChild(nsIContentChild* aMan
   // check the other conditions necessary for enabling APZ.
   mAsyncPanZoomEnabled = gfxPlatform::AsyncPanZoomEnabled();
 
   // preloaded TabChild should not be added to child map
   if (mUniqueId) {
     MOZ_ASSERT(NestedTabChildMap().find(mUniqueId) == NestedTabChildMap().end());
     NestedTabChildMap()[mUniqueId] = this;
   }
+
+  nsCOMPtr<nsIObserverService> observerService =
+    mozilla::services::GetObserverService();
+
+  if (observerService) {
+    const nsAttrValue::EnumTable* table =
+      AudioChannelService::GetAudioChannelTable();
+
+    nsAutoCString topic;
+    for (uint32_t i = 0; table[i].tag; ++i) {
+      topic.Assign("audiochannel-activity-");
+      topic.Append(table[i].tag);
+
+      observerService->AddObserver(this, topic.get(), false);
+    }
+  }
 }
 
 NS_IMETHODIMP
 TabChild::HandleEvent(nsIDOMEvent* aEvent)
 {
   nsAutoString eventType;
   aEvent->GetType(eventType);
   if (eventType.EqualsLiteral("DOMMetaAdded")) {
@@ -952,16 +968,37 @@ TabChild::Observe(nsISupports *aSubject,
             nsLayoutUtils::SetResolutionAndScaleTo(shell, mLastRootMetrics.GetPresShellResolution());
           }
           HandlePossibleViewportChange(GetInnerSize());
         }
       }
     }
   }
 
+  const nsAttrValue::EnumTable* table =
+    AudioChannelService::GetAudioChannelTable();
+
+  nsAutoCString topic;
+  int16_t audioChannel = -1;
+  for (uint32_t i = 0; table[i].tag; ++i) {
+    topic.Assign("audiochannel-activity-");
+    topic.Append(table[i].tag);
+
+    if (topic.Equals(aTopic)) {
+      audioChannel = table[i].value;
+      break;
+    }
+  }
+
+  if (audioChannel != -1) {
+    nsAutoString active(aData);
+    unused << SendAudioChannelActivityNotification(audioChannel,
+                                                   active.Equals(NS_LITERAL_STRING("active")));
+  }
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 TabChild::OnStateChange(nsIWebProgress* aWebProgress,
                         nsIRequest* aRequest,
                         uint32_t aStateFlags,
                         nsresult aStatus)
@@ -2814,16 +2851,27 @@ TabChild::RecvDestroy()
   }
 
   nsCOMPtr<nsIObserverService> observerService =
     mozilla::services::GetObserverService();
 
   observerService->RemoveObserver(this, BROWSER_ZOOM_TO_RECT);
   observerService->RemoveObserver(this, BEFORE_FIRST_PAINT);
 
+  const nsAttrValue::EnumTable* table =
+    AudioChannelService::GetAudioChannelTable();
+
+  nsAutoCString topic;
+  for (uint32_t i = 0; table[i].tag; ++i) {
+    topic.Assign("audiochannel-activity-");
+    topic.Append(table[i].tag);
+
+    observerService->RemoveObserver(this, topic.get());
+  }
+
   // XXX what other code in ~TabChild() should we be running here?
   DestroyWindow();
 
   // Bounce through the event loop once to allow any delayed teardown runnables
   // that were just generated to have a chance to run.
   nsCOMPtr<nsIRunnable> deleteRunnable = new DelayedDeleteRunnable(this);
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread(deleteRunnable)));
 
--- a/dom/ipc/TabMessageUtils.h
+++ b/dom/ipc/TabMessageUtils.h
@@ -2,17 +2,16 @@
 /* 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 TABMESSAGE_UTILS_H
 #define TABMESSAGE_UTILS_H
 
-#include "AudioChannelCommon.h"
 #include "ipc/IPCMessageUtils.h"
 #include "mozilla/dom/AudioChannelBinding.h"
 #include "nsIDOMEvent.h"
 #include "nsCOMPtr.h"
 
 #ifdef MOZ_CRASHREPORTER
 #include "nsExceptionHandler.h"
 #endif
@@ -85,24 +84,16 @@ struct ParamTraits<mozilla::dom::AudioCh
   }
 
   static void Log(const paramType& aParam, std::wstring* aLog)
   {
   }
 };
 
 template <>
-struct ParamTraits<mozilla::dom::AudioChannelState>
-  : public ContiguousEnumSerializer<mozilla::dom::AudioChannelState,
-                                    mozilla::dom::AUDIO_CHANNEL_STATE_NORMAL,
-                                    mozilla::dom::AUDIO_CHANNEL_STATE_LAST>
-{ };
-
-
-template <>
 struct ParamTraits<nsEventStatus>
   : public ContiguousEnumSerializer<nsEventStatus,
                                     nsEventStatus_eIgnore,
                                     nsEventStatus_eSentinel>
 { };
 
 }
 
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "base/basictypes.h"
 
 #include "TabParent.h"
 
+#include "AudioChannelService.h"
 #include "AppProcessChecker.h"
 #include "mozIApplication.h"
 #ifdef ACCESSIBILITY
 #include "mozilla/a11y/DocAccessibleParent.h"
 #include "nsAccessibilityService.h"
 #endif
 #include "mozilla/BrowserElementParent.h"
 #include "mozilla/dom/ContentParent.h"
@@ -2641,16 +2642,39 @@ TabParent::RecvGetRenderFrameInfo(PRende
   if (mNeedLayerTreeReadyNotification) {
     RequestNotifyLayerTreeReady();
     mNeedLayerTreeReadyNotification = false;
   }
 
   return true;
 }
 
+bool
+TabParent::RecvAudioChannelActivityNotification(const uint32_t& aAudioChannel,
+                                                const bool& aActive)
+{
+  if (aAudioChannel >= NUMBER_OF_AUDIO_CHANNELS) {
+    return false;
+  }
+
+  nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+  if (os) {
+    nsRefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
+    nsAutoCString topic;
+    topic.Assign("audiochannel-activity-");
+    topic.Append(AudioChannelService::GetAudioChannelTable()[aAudioChannel].tag);
+
+    os->NotifyObservers(NS_ISUPPORTS_CAST(nsITabParent*, this),
+                        topic.get(),
+                        aActive ? MOZ_UTF16("active") : MOZ_UTF16("inactive"));
+  }
+
+  return true;
+}
+
 already_AddRefed<nsFrameLoader>
 TabParent::GetFrameLoader(bool aUseCachedFrameLoaderAfterDestroy) const
 {
   if (mIsDestroyed && !aUseCachedFrameLoaderAfterDestroy) {
     return nullptr;
   }
 
   if (mFrameLoader) {
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -457,16 +457,19 @@ protected:
     virtual bool RecvGetRenderFrameInfo(PRenderFrameParent* aRenderFrame,
                                         TextureFactoryIdentifier* aTextureFactoryIdentifier,
                                         uint64_t* aLayersId) override;
 
     virtual bool RecvSetDimensions(const uint32_t& aFlags,
                                    const int32_t& aX, const int32_t& aY,
                                    const int32_t& aCx, const int32_t& aCy) override;
 
+    virtual bool RecvAudioChannelActivityNotification(const uint32_t& aAudioChannel,
+                                                      const bool& aActive) override;
+
     bool InitBrowserConfiguration(const nsCString& aURI,
                                   BrowserConfiguration& aConfiguration);
 
     void SetHasContentOpener(bool aHasContentOpener);
 
     ContentCacheInParent mContentCache;
 
     nsIntRect mRect;
--- a/dom/media/webaudio/AudioDestinationNode.cpp
+++ b/dom/media/webaudio/AudioDestinationNode.cpp
@@ -293,54 +293,21 @@ static bool UseAudioChannelService()
   return Preferences::GetBool("media.useAudioChannelService");
 }
 
 static bool UseAudioChannelAPI()
 {
   return Preferences::GetBool("media.useAudioChannelAPI");
 }
 
-class EventProxyHandler final : public nsIDOMEventListener
-{
-public:
-  NS_DECL_ISUPPORTS
-
-  explicit EventProxyHandler(nsIDOMEventListener* aNode)
-  {
-    MOZ_ASSERT(aNode);
-    mWeakNode = do_GetWeakReference(aNode);
-  }
-
-  // nsIDOMEventListener
-  NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent) override
-  {
-    nsCOMPtr<nsIDOMEventListener> listener = do_QueryReferent(mWeakNode);
-    if (!listener) {
-      return NS_OK;
-    }
-
-    auto node = static_cast<AudioDestinationNode*>(listener.get());
-    return node->HandleEvent(aEvent);
-  }
-
-private:
-  ~EventProxyHandler()
-  { }
-
-  nsWeakPtr mWeakNode;
-};
-
-NS_IMPL_ISUPPORTS(EventProxyHandler, nsIDOMEventListener)
-
 NS_IMPL_CYCLE_COLLECTION_INHERITED(AudioDestinationNode, AudioNode,
-                                   mAudioChannelAgent, mEventProxyHelper,
+                                   mAudioChannelAgent,
                                    mOfflineRenderingPromise)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(AudioDestinationNode)
-  NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
   NS_INTERFACE_MAP_ENTRY(nsIAudioChannelAgentCallback)
 NS_INTERFACE_MAP_END_INHERITING(AudioNode)
 
 NS_IMPL_ADDREF_INHERITED(AudioDestinationNode, AudioNode)
 NS_IMPL_RELEASE_INHERITED(AudioDestinationNode, AudioNode)
 
 AudioDestinationNode::AudioDestinationNode(AudioContext* aContext,
                                            bool aIsOffline,
@@ -403,23 +370,16 @@ AudioDestinationNode::SizeOfIncludingThi
 }
 
 void
 AudioDestinationNode::DestroyAudioChannelAgent()
 {
   if (mAudioChannelAgent && !Context()->IsOffline()) {
     mAudioChannelAgent->StopPlaying();
     mAudioChannelAgent = nullptr;
-
-    nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(GetOwner());
-    NS_ENSURE_TRUE_VOID(target);
-
-    target->RemoveSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
-                                      mEventProxyHelper,
-                                      /* useCapture = */ true);
   }
 }
 
 void
 AudioDestinationNode::DestroyMediaStream()
 {
   DestroyAudioChannelAgent();
 
@@ -513,75 +473,40 @@ void
 AudioDestinationNode::StartRendering(Promise* aPromise)
 {
   mOfflineRenderingPromise = aPromise;
   mOfflineRenderingRef.Take(this);
   mStream->Graph()->StartNonRealtimeProcessing(mFramesToProduce);
 }
 
 void
-AudioDestinationNode::SetCanPlay(bool aCanPlay)
+AudioDestinationNode::SetCanPlay(float aVolume, bool aMuted)
 {
-  mStream->SetTrackEnabled(AudioNodeStream::AUDIO_TRACK, aCanPlay);
-}
-
-NS_IMETHODIMP
-AudioDestinationNode::HandleEvent(nsIDOMEvent* aEvent)
-{
-  nsAutoString type;
-  aEvent->GetType(type);
-
-  if (!type.EqualsLiteral("visibilitychange")) {
-    return NS_ERROR_FAILURE;
+  if (!mStream) {
+    return;
   }
 
-  nsCOMPtr<nsIDocShell> docshell = do_GetInterface(GetOwner());
-  NS_ENSURE_TRUE(docshell, NS_ERROR_FAILURE);
-
-  bool isActive = false;
-  docshell->GetIsActive(&isActive);
-
-  mAudioChannelAgent->SetVisibilityState(isActive);
-  return NS_OK;
+  mStream->SetTrackEnabled(AudioNodeStream::AUDIO_TRACK, !aMuted);
+  mStream->SetAudioOutputVolume(&gWebAudioOutputKey, aVolume);
 }
 
 NS_IMETHODIMP
-AudioDestinationNode::CanPlayChanged(int32_t aCanPlay)
+AudioDestinationNode::WindowVolumeChanged(float aVolume, bool aMuted)
 {
-  bool playing = aCanPlay == AudioChannelState::AUDIO_CHANNEL_STATE_NORMAL;
-  if (playing == mAudioChannelAgentPlaying) {
-    return NS_OK;
-  }
+  if (aMuted != mAudioChannelAgentPlaying) {
+    mAudioChannelAgentPlaying = aMuted;
 
-  mAudioChannelAgentPlaying = playing;
-  SetCanPlay(playing);
-
-  if (UseAudioChannelAPI()) {
-    Context()->DispatchTrustedEvent(
-      playing ? NS_LITERAL_STRING("mozinterruptend")
-              : NS_LITERAL_STRING("mozinterruptbegin"));
+    if (UseAudioChannelAPI()) {
+      Context()->DispatchTrustedEvent(
+        !aMuted ? NS_LITERAL_STRING("mozinterruptend")
+                : NS_LITERAL_STRING("mozinterruptbegin"));
+    }
   }
 
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-AudioDestinationNode::WindowVolumeChanged()
-{
-  MOZ_ASSERT(mAudioChannelAgent);
-
-  if (!mStream) {
-    return NS_OK;
-  }
-
-  float volume;
-  nsresult rv = mAudioChannelAgent->GetWindowVolume(&volume);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  mStream->SetAudioOutputVolume(&gWebAudioOutputKey, volume);
+  SetCanPlay(aVolume, aMuted);
   return NS_OK;
 }
 
 AudioChannel
 AudioDestinationNode::MozAudioChannelType() const
 {
   return mAudioChannel;
 }
@@ -649,49 +574,28 @@ AudioDestinationNode::CheckAudioChannelP
 
 void
 AudioDestinationNode::CreateAudioChannelAgent()
 {
   if (mIsOffline || !UseAudioChannelService()) {
     return;
   }
 
-  if (!mEventProxyHelper) {
-    nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(GetOwner());
-    if (target) {
-      // We use a proxy because otherwise the event listerner would hold a
-      // reference of the destination node, and by extension, everything
-      // connected to it.
-      mEventProxyHelper = new EventProxyHandler(this);
-      target->AddSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
-                                     mEventProxyHelper,
-                                     /* useCapture = */ true,
-                                     /* wantsUntrusted = */ false);
-    }
-  }
-
   if (mAudioChannelAgent) {
     mAudioChannelAgent->StopPlaying();
   }
 
   mAudioChannelAgent = new AudioChannelAgent();
   mAudioChannelAgent->InitWithWeakCallback(GetOwner(),
                                            static_cast<int32_t>(mAudioChannel),
                                            this);
 
-  nsCOMPtr<nsIDocShell> docshell = do_GetInterface(GetOwner());
-  if (docshell) {
-    bool isActive = false;
-    docshell->GetIsActive(&isActive);
-    mAudioChannelAgent->SetVisibilityState(isActive);
-
-    // The AudioChannelAgent must start playing immediately in order to avoid
-    // race conditions with mozinterruptbegin/end events.
-    InputMuted(false);
-  }
+  // The AudioChannelAgent must start playing immediately in order to avoid
+  // race conditions with mozinterruptbegin/end events.
+  InputMuted(false);
 }
 
 void
 AudioDestinationNode::NotifyStableState()
 {
   mExtraCurrentTimeUpdatedSinceLastStableState = false;
 }
 
@@ -766,19 +670,20 @@ AudioDestinationNode::InputMuted(bool aM
     return;
   }
 
   if (aMuted) {
     mAudioChannelAgent->StopPlaying();
     return;
   }
 
-  int32_t state = 0;
-  nsresult rv = mAudioChannelAgent->StartPlaying(&state);
+  float volume = 0.0;
+  bool muted = true;
+  nsresult rv = mAudioChannelAgent->StartPlaying(&volume, &muted);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
   }
 
-  CanPlayChanged(state);
+  WindowVolumeChanged(volume, muted);
 }
 
 } // dom namespace
 } // mozilla namespace
--- a/dom/media/webaudio/AudioDestinationNode.h
+++ b/dom/media/webaudio/AudioDestinationNode.h
@@ -4,28 +4,24 @@
  * 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 AudioDestinationNode_h_
 #define AudioDestinationNode_h_
 
 #include "mozilla/dom/AudioChannelBinding.h"
 #include "AudioNode.h"
-#include "nsIDOMEventListener.h"
 #include "nsIAudioChannelAgent.h"
-#include "AudioChannelCommon.h"
 
 namespace mozilla {
 namespace dom {
 
 class AudioContext;
-class EventProxyHandler;
 
 class AudioDestinationNode final : public AudioNode
-                                 , public nsIDOMEventListener
                                  , public nsIAudioChannelAgentCallback
                                  , public MainThreadMediaStreamListener
 {
 public:
   // This node type knows what MediaStreamGraph to use based on
   // whether it's in offline mode.
   AudioDestinationNode(AudioContext* aContext,
                        bool aIsOffline,
@@ -56,19 +52,16 @@ public:
 
   void Mute();
   void Unmute();
 
   void StartRendering(Promise* aPromise);
 
   void OfflineShutdown();
 
-  // nsIDOMEventListener - by proxy
-  NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent) override;
-
   AudioChannel MozAudioChannelType() const;
   void SetMozAudioChannelType(AudioChannel aValue, ErrorResult& aRv);
 
   virtual void NotifyMainThreadStreamFinished() override;
   void FireOfflineCompletionEvent();
 
   // An amount that should be added to the MediaStream's current time to
   // get the AudioContext.currentTime.
@@ -92,27 +85,26 @@ public:
   void ResolvePromise(AudioBuffer* aRenderedBuffer);
 
 protected:
   virtual ~AudioDestinationNode();
 
 private:
   bool CheckAudioChannelPermissions(AudioChannel aValue);
 
-  void SetCanPlay(bool aCanPlay);
+  void SetCanPlay(float aVolume, bool aMuted);
 
   void NotifyStableState();
   void ScheduleStableStateNotification();
 
   SelfReference<AudioDestinationNode> mOfflineRenderingRef;
   uint32_t mFramesToProduce;
 
   nsCOMPtr<nsIAudioChannelAgent> mAudioChannelAgent;
 
-  nsRefPtr<EventProxyHandler> mEventProxyHelper;
   nsRefPtr<Promise> mOfflineRenderingPromise;
 
   // Audio Channel Type.
   AudioChannel mAudioChannel;
   bool mIsOffline;
   bool mAudioChannelAgentPlaying;
 
   TimeStamp mStartedBlockingDueToBeingOnlyNode;
--- a/dom/media/webaudio/moz.build
+++ b/dom/media/webaudio/moz.build
@@ -13,18 +13,16 @@ TEST_DIRS += ['compiledtest']
 
 MOCHITEST_MANIFESTS += [
     'test/blink/mochitest.ini',
     'test/mochitest.ini',
 ]
 
 MOCHITEST_CHROME_MANIFESTS += ['test/chrome.ini']
 
-BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
-
 EXPORTS += [
     'AlignedTArray.h',
     'AudioContext.h',
     'AudioEventTimeline.h',
     'AudioNodeEngine.h',
     'AudioNodeExternalInputStream.h',
     'AudioNodeStream.h',
     'AudioParamTimeline.h',
deleted file mode 100644
--- a/dom/media/webaudio/test/browser.ini
+++ /dev/null
@@ -1,8 +0,0 @@
-[DEFAULT]
-support-files =
-  browser_mozAudioChannel.html
-  browser_mozAudioChannel_muted.html
-
-[browser_mozAudioChannel.js]
-[browser_mozAudioChannel_muted.js]
-skip-if = e10s
deleted file mode 100644
--- a/dom/media/webaudio/test/browser_mozAudioChannel.html
+++ /dev/null
@@ -1,33 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
-<meta charset="utf-8">
-<title>Test for mozinterruptbegin/end in AudioContext</title>
-
-<script type="application/javascript">
-
-  var ac = new AudioContext();
-
-  function createEvent(msg) {
-    var event = document.createEvent('CustomEvent');
-    event.initCustomEvent('testmozchannel', true, true, { msg: msg });
-    dispatchEvent(event);
-  }
-
-  ac.onmozinterruptbegin = function(evt) {
-    createEvent('mozinterruptbegin');
-  }
-
-  ac.addEventListener('mozinterruptend', function() {
-    createEvent('mozinterruptend');
-  }, false);
-
-  var buffer = ac.createBuffer(1, 2048, ac.sampleRate);
-  for (var i = 0; i < 2048; ++i) {
-    buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / ac.sampleRate);
-  }
-
-  var source = ac.createBufferSource();
-  source.buffer = buffer;
-  source.connect(ac.destination);
-  source.loop = true;
-  source.start(0);
-</script>
deleted file mode 100644
--- a/dom/media/webaudio/test/browser_mozAudioChannel.js
+++ /dev/null
@@ -1,80 +0,0 @@
-/* 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/. */
-
-function whenBrowserLoaded(aBrowser, aCallback) {
-  aBrowser.addEventListener("load", function onLoad(event) {
-    if (event.target == aBrowser.contentDocument) {
-      aBrowser.removeEventListener("load", onLoad, true);
-      executeSoon(aCallback);
-    }
-  }, true);
-}
-
-function whenBrowserUnloaded(aBrowser, aCallback) {
-  aBrowser.addEventListener("unload", function onUnload() {
-    aBrowser.removeEventListener("unload", onUnload, true);
-    executeSoon(aCallback);
-  }, true);
-}
-
-var event;
-var next = function() {}
-
-function eventListener(evt) {
-  info("Event has been received!");
-  is(evt.detail.msg, event, "AudioContext has been received the right event: " + event);
-  next();
-}
-
-function test() {
-
-  waitForExplicitFinish();
-
-  let testURL = "http://mochi.test:8888/browser/" +
-    "dom/media/webaudio/test/browser_mozAudioChannel.html";
-
-  SpecialPowers.pushPrefEnv({"set": [["media.defaultAudioChannel", "content" ],
-                                     ["media.useAudioChannelAPI", true ],
-                                     ["media.useAudioChannelService", true ]]},
-    function() {
-      let tab1 = gBrowser.addTab(testURL);
-      gBrowser.selectedTab = tab1;
-
-      whenBrowserLoaded(tab1.linkedBrowser, function() {
-        let doc = tab1.linkedBrowser.contentDocument;
-        tab1.linkedBrowser.contentWindow.addEventListener('testmozchannel', eventListener, false);
-
-        SpecialPowers.pushPrefEnv({"set": [["media.defaultAudioChannel", "telephony" ]]},
-          function() {
-            event = 'mozinterruptbegin';
-            next = function() {
-              info("Next is called.");
-              event = 'mozinterruptend';
-              next =  function() {
-                info("Next is called again.");
-                tab1.linkedBrowser.contentWindow.removeEventListener('testmozchannel', eventListener);
-                gBrowser.removeTab(tab1);
-                finish();
-              }
-
-              info("Unloading a tab...");
-              whenBrowserUnloaded(tab2.linkedBrowser, function() { info("Tab unloaded."); });
-
-              executeSoon(function() {
-                gBrowser.removeTab(tab2);
-                gBrowser.selectedTab = tab1;
-              });
-            }
-
-            let tab2 = gBrowser.addTab(testURL);
-            gBrowser.selectedTab = tab2;
-
-            info("Loading the tab...");
-            whenBrowserLoaded(tab2.linkedBrowser, function() { info("Tab restored."); });
-          }
-        );
-      });
-    }
-  );
-}
deleted file mode 100644
--- a/dom/media/webaudio/test/browser_mozAudioChannel_muted.html
+++ /dev/null
@@ -1,21 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
-<meta charset="utf-8">
-<title>Test for mozinterruptbegin/end in AudioContext</title>
-
-mozAudioChannelTest = <span id="mozAudioChannelTest">FAIL</span>
-
-<script type="application/javascript">
-
-  var ac = new AudioContext();
-
-  ac.onmozinterruptbegin = function(evt) {
-    document.getElementById("mozAudioChannelTest").innerHTML = "mozinterruptbegin";
-  }
-
-  ac.addEventListener('mozinterruptend', function() {
-    document.getElementById("mozAudioChannelTest").innerHTML = "mozinterruptend";
-  }, false);
-
-  document.getElementById("mozAudioChannelTest").innerHTML = "READY";
-
-</script>
deleted file mode 100644
--- a/dom/media/webaudio/test/browser_mozAudioChannel_muted.js
+++ /dev/null
@@ -1,72 +0,0 @@
-/* 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/. */
-
-function whenBrowserLoaded(aBrowser, aCallback) {
-  aBrowser.addEventListener("load", function onLoad(event) {
-    if (event.target == aBrowser.contentDocument) {
-      aBrowser.removeEventListener("load", onLoad, true);
-      executeSoon(aCallback);
-    }
-  }, true);
-}
-
-function whenTabRestored(aTab, aCallback) {
-  aTab.addEventListener("SSTabRestored", function onRestored(aEvent) {
-    aTab.removeEventListener("SSTabRestored", onRestored, true);
-    executeSoon(function executeWhenTabRestored() {
-      aCallback();
-    });
-  }, true);
-}
-
-function whenBrowserUnloaded(aBrowser, aCallback) {
-  aBrowser.addEventListener("unload", function onUnload() {
-    aBrowser.removeEventListener("unload", onUnload, true);
-    executeSoon(aCallback);
-  }, true);
-}
-
-function test() {
-
-  waitForExplicitFinish();
-
-  let testURL = "http://mochi.test:8888/browser/" +
-    "dom/media/webaudio/test/browser_mozAudioChannel_muted.html";
-
-  SpecialPowers.pushPrefEnv({"set": [["media.defaultAudioChannel", "content" ],
-                                     ["media.useAudioChannelAPI", true ],
-                                     ["media.useAudioChannelService", true ]]},
-    function() {
-      let tab1 = gBrowser.addTab(testURL);
-      gBrowser.selectedTab = tab1;
-
-      whenBrowserLoaded(tab1.linkedBrowser, function() {
-        let doc = tab1.linkedBrowser.contentDocument;
-        is(doc.getElementById("mozAudioChannelTest").textContent, "READY",
-           "Test is ready to run");
-
-        SpecialPowers.pushPrefEnv({"set": [["media.defaultAudioChannel", "telephony" ]]},
-          function() {
-            let tab2 = gBrowser.duplicateTab(tab1);
-            gBrowser.selectedTab = tab2;
-            whenTabRestored(tab2, function() {
-              is(doc.getElementById("mozAudioChannelTest").textContent, "mozinterruptbegin",
-                 "AudioContext should be muted by the second tab.");
-
-              whenBrowserUnloaded(tab2.linkedBrowser, function() {
-                is(doc.getElementById("mozAudioChannelTest").textContent, "mozinterruptend",
-                   "AudioContext should be muted by the second tab.");
-                gBrowser.removeTab(tab1);
-                finish();
-              });
-
-              gBrowser.removeTab(tab2);
-              gBrowser.selectedTab = tab1;
-            });
-          }
-        );
-      });
-    }
-  );
-}
--- a/dom/speakermanager/SpeakerManager.cpp
+++ b/dom/speakermanager/SpeakerManager.cpp
@@ -196,18 +196,18 @@ SpeakerManager::HandleEvent(nsIDOMEvent*
 
   if (mVisible && mForcespeaker) {
     service->ForceSpeaker(mForcespeaker, mVisible);
   }
   // If an application that has called forcespeaker=true, but no audio is
   // currently playing in the app itself, if application switch to
   // the background, we switch 'speakerforced' to false.
   if (!mVisible && mForcespeaker) {
-    AudioChannelService* audioChannelService =
-      AudioChannelService::GetOrCreateAudioChannelService();
+    nsRefPtr<AudioChannelService> audioChannelService =
+      AudioChannelService::GetOrCreate();
     if (audioChannelService && !audioChannelService->AnyAudioChannelIsActive()) {
       service->ForceSpeaker(false, mVisible);
     }
   }
   return NS_OK;
 }
 
 void
--- a/dom/speakermanager/SpeakerManagerService.cpp
+++ b/dom/speakermanager/SpeakerManagerService.cpp
@@ -189,23 +189,20 @@ SpeakerManagerService::SpeakerManagerSer
 {
   MOZ_COUNT_CTOR(SpeakerManagerService);
   if (XRE_IsParentProcess()) {
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     if (obs) {
       obs->AddObserver(this, "ipc:content-shutdown", false);
     }
   }
-  AudioChannelService* audioChannelService =
-    AudioChannelService::GetOrCreateAudioChannelService();
-  if (audioChannelService) {
-    audioChannelService->RegisterSpeakerManager(this);
-  }
+  nsRefPtr<AudioChannelService> audioChannelService =
+    AudioChannelService::GetOrCreate();
+  audioChannelService->RegisterSpeakerManager(this);
 }
 
 SpeakerManagerService::~SpeakerManagerService()
 {
   MOZ_COUNT_DTOR(SpeakerManagerService);
-  AudioChannelService* audioChannelService =
-    AudioChannelService::GetOrCreateAudioChannelService();
-  if (audioChannelService)
-    audioChannelService->UnregisterSpeakerManager(this);
+  nsRefPtr<AudioChannelService> audioChannelService =
+    AudioChannelService::GetOrCreate();
+  audioChannelService->UnregisterSpeakerManager(this);
 }
--- a/dom/speakermanager/SpeakerManagerServiceChild.cpp
+++ b/dom/speakermanager/SpeakerManagerServiceChild.cpp
@@ -91,26 +91,26 @@ SpeakerManagerServiceChild::SetAudioChan
   for (uint32_t i = 0; i < mRegisteredSpeakerManagers.Length(); i++) {
     mRegisteredSpeakerManagers[i]->SetAudioChannelActive(aIsActive);
   }
 }
 
 SpeakerManagerServiceChild::SpeakerManagerServiceChild()
 {
   MOZ_ASSERT(NS_IsMainThread());
-  AudioChannelService* audioChannelService = AudioChannelService::GetOrCreateAudioChannelService();
+  nsRefPtr<AudioChannelService> audioChannelService = AudioChannelService::GetOrCreate();
   if (audioChannelService) {
     audioChannelService->RegisterSpeakerManager(this);
   }
   MOZ_COUNT_CTOR(SpeakerManagerServiceChild);
 }
 
 SpeakerManagerServiceChild::~SpeakerManagerServiceChild()
 {
-  AudioChannelService* audioChannelService = AudioChannelService::GetOrCreateAudioChannelService();
+  nsRefPtr<AudioChannelService> audioChannelService = AudioChannelService::GetOrCreate();
   if (audioChannelService) {
     audioChannelService->UnregisterSpeakerManager(this);
   }
   MOZ_COUNT_DTOR(SpeakerManagerServiceChild);
 }
 
 void
 SpeakerManagerServiceChild::Notify()
--- a/dom/system/gonk/AudioChannelManager.cpp
+++ b/dom/system/gonk/AudioChannelManager.cpp
@@ -126,17 +126,17 @@ void
 AudioChannelManager::NotifyVolumeControlChannelChanged()
 {
   nsCOMPtr<nsIDocShell> docshell = do_GetInterface(GetOwner());
   NS_ENSURE_TRUE_VOID(docshell);
 
   bool isActive = false;
   docshell->GetIsActive(&isActive);
 
-  AudioChannelService* service = AudioChannelService::GetOrCreateAudioChannelService();
+  nsRefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
   if (isActive) {
     service->SetDefaultVolumeControlChannel(mVolumeChannel, isActive);
   } else {
     service->SetDefaultVolumeControlChannel(-1, isActive);
   }
 }
 
 NS_IMETHODIMP
--- a/dom/system/gonk/AudioManager.cpp
+++ b/dom/system/gonk/AudioManager.cpp
@@ -351,17 +351,17 @@ AudioManager::HandleAudioChannelProcessC
   // be called again and therefore the audio manager sets the
   // PHONE_STATE_IN_COMMUNICATION audio state.
 
   if ((mPhoneState == PHONE_STATE_IN_CALL) ||
       (mPhoneState == PHONE_STATE_RINGTONE)) {
     return;
   }
 
-  AudioChannelService *service = AudioChannelService::GetOrCreateAudioChannelService();
+  nsRefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
   MOZ_ASSERT(service);
 
   bool telephonyChannelIsActive = service->TelephonyChannelIsActive();
   telephonyChannelIsActive ? SetPhoneState(PHONE_STATE_IN_COMMUNICATION) :
                              SetPhoneState(PHONE_STATE_NORMAL);
 }
 
 nsresult
@@ -646,18 +646,23 @@ AudioManager::SetPhoneState(int32_t aSta
     if (aState == PHONE_STATE_IN_CALL) {
       // Telephony doesn't be paused by any other channels.
       mPhoneAudioAgent->Init(nullptr, (int32_t)AudioChannel::Telephony, nullptr);
     } else {
       mPhoneAudioAgent->Init(nullptr, (int32_t)AudioChannel::Ringer, nullptr);
     }
 
     // Telephony can always play.
-    int32_t canPlay;
-    mPhoneAudioAgent->StartPlaying(&canPlay);
+    float volume = 0.0;
+    bool muted = true;
+
+    nsresult rv = mPhoneAudioAgent->StartPlaying(&volume, &muted);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 AudioManager::SetForceForUse(int32_t aUsage, int32_t aForce)
 {
rename from dom/audiochannel/AudioChannelCommon.h
rename to dom/webidl/BrowserElementAudioChannel.webidl
--- a/dom/audiochannel/AudioChannelCommon.h
+++ b/dom/webidl/BrowserElementAudioChannel.webidl
@@ -1,24 +1,37 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* -*- 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/. */
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
 
-#ifndef mozilla_dom_audiochannelcommon_h__
-#define mozilla_dom_audiochannelcommon_h__
+[Pref="dom.mozBrowserFramesEnabled",
+ CheckPermissions="browser"]
+interface BrowserElementAudioChannel : EventTarget {
+  readonly attribute AudioChannel name;
+
+  // This event is dispatched when this audiochannel is actually in used by the
+  // app or one of the sub-iframes.
+  attribute EventHandler onactivestatechanged;
 
-namespace mozilla {
-namespace dom {
+  [Throws]
+  DOMRequest getVolume();
+
+  [Throws]
+  DOMRequest setVolume(float aVolume);
 
-enum AudioChannelState {
-  AUDIO_CHANNEL_STATE_NORMAL = 0,
-  AUDIO_CHANNEL_STATE_MUTED,
-  AUDIO_CHANNEL_STATE_FADED,
-  AUDIO_CHANNEL_STATE_LAST
+  [Throws]
+  DOMRequest getMuted();
+
+  [Throws]
+  DOMRequest setMuted(boolean aMuted);
+
+  [Throws]
+  DOMRequest isActive();
 };
 
-} // namespace dom
-} // namespace mozilla
-
-#endif
-
+partial interface BrowserElementPrivileged {
+  [Constant, Cached, Throws,
+   Pref="dom.mozBrowserFramesEnabled",
+   CheckPermissions="browser"]
+  readonly attribute sequence<BrowserElementAudioChannel> allowedAudioChannels;
+};
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -52,16 +52,17 @@ WEBIDL_FILES = [
     'BatteryManager.webidl',
     'BeforeAfterKeyboardEvent.webidl',
     'BeforeUnloadEvent.webidl',
     'BiquadFilterNode.webidl',
     'Blob.webidl',
     'BoxObject.webidl',
     'BroadcastChannel.webidl',
     'BrowserElement.webidl',
+    'BrowserElementAudioChannel.webidl',
     'BrowserElementDictionaries.webidl',
     'Cache.webidl',
     'CacheStorage.webidl',
     'CallsList.webidl',
     'CameraCapabilities.webidl',
     'CameraControl.webidl',
     'CameraManager.webidl',
     'CameraUtil.webidl',
--- a/layout/build/nsLayoutModule.cpp
+++ b/layout/build/nsLayoutModule.cpp
@@ -626,17 +626,17 @@ NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(Geol
 #define NS_AUDIOCHANNEL_SERVICE_CID \
   { 0xf712e983, 0x048a, 0x443f, { 0x88, 0x02, 0xfc, 0xc3, 0xd9, 0x27, 0xce, 0xac }}
 
 #define NS_DATASTORE_SERVICE_CID \
   { 0x0d4285fe, 0xf1b3, 0x49fa, { 0xbc, 0x51, 0xa4, 0xa8, 0x3f, 0x0a, 0xaf, 0x85 }}
 
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsGeolocationService, nsGeolocationService::GetGeolocationService)
 
-NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(AudioChannelService, AudioChannelService::GetOrCreateAudioChannelService)
+NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(AudioChannelService, AudioChannelService::GetOrCreate)
 
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(DataStoreService, DataStoreService::GetOrCreate)
 
 #ifdef MOZ_WEBSPEECH_TEST_BACKEND
 NS_GENERIC_FACTORY_CONSTRUCTOR(FakeSpeechRecognitionService)
 #endif
 #ifdef MOZ_WEBSPEECH_POCKETSPHINX
 NS_GENERIC_FACTORY_CONSTRUCTOR(PocketSphinxSpeechRecognitionService)