Backed out changesets 961f205d340d, 14a4637e9d96 and 14a4637e9d96 (bug 1228564) for failing Android M(4) test_browserElement_inproc_AudioChannel.html. r=backout
authorSebastian Hengst <archaeopteryx@coole-files.de>
Sun, 27 Dec 2015 22:28:08 +0100
changeset 662942 f96142e950e08c82b0d6f0f87b95ec58fcd06ea7
parent 662851 526f309b5ba4a93cf4e72405dbbf5f645d755ea4
child 662943 f26fb489885c01f4e99bfef51c3e3d875f5257cb
push id102302
push usercpeterson@mozilla.com
push dateMon, 28 Dec 2015 06:05:08 +0000
treeherdertry@b38a648cdebe [default view] [failures only]
reviewersbackout
bugs1228564
milestone46.0a1
backs out961f205d340d1ad9eb152da90fa8443df7a497b8
14a4637e9d96dd7d9ae9012bb44f53354bd5a602
14a4637e9d96dd7d9ae9012bb44f53354bd5a602
Backed out changesets 961f205d340d, 14a4637e9d96 and 14a4637e9d96 (bug 1228564) for failing Android M(4) test_browserElement_inproc_AudioChannel.html. r=backout
dom/audiochannel/AudioChannelAgent.cpp
dom/audiochannel/AudioChannelAgent.h
dom/audiochannel/AudioChannelService.cpp
dom/audiochannel/AudioChannelService.h
dom/audiochannel/nsIAudioChannelAgent.idl
dom/base/nsGlobalWindow.cpp
dom/camera/DOMCameraControl.cpp
dom/fmradio/FMRadio.cpp
dom/html/HTMLMediaElement.cpp
dom/media/webaudio/AudioDestinationNode.cpp
dom/media/webspeech/synth/nsSpeechTask.cpp
dom/plugins/base/nsNPAPIPlugin.cpp
dom/plugins/base/nsNPAPIPluginInstance.cpp
dom/telephony/Telephony.cpp
--- a/dom/audiochannel/AudioChannelAgent.cpp
+++ b/dom/audiochannel/AudioChannelAgent.cpp
@@ -36,16 +36,17 @@ NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(AudioChannelAgent)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(AudioChannelAgent)
 
 AudioChannelAgent::AudioChannelAgent()
   : mAudioChannelType(AUDIO_AGENT_CHANNEL_ERROR)
   , mInnerWindowID(0)
   , mIsRegToService(false)
+  , mNotifyPlayback(false)
 {
 }
 
 AudioChannelAgent::~AudioChannelAgent()
 {
   Shutdown();
 }
 
@@ -202,17 +203,18 @@ AudioChannelAgent::InitInternal(nsIDOMWi
   MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
          ("AudioChannelAgent, InitInternal, this = %p, type = %d, "
           "owner = %p, hasCallback = %d\n", this, mAudioChannelType,
           mWindow.get(), (!!mCallback || !!mWeakCallback)));
 
   return NS_OK;
 }
 
-NS_IMETHODIMP AudioChannelAgent::NotifyStartedPlaying(float *aVolume,
+NS_IMETHODIMP AudioChannelAgent::NotifyStartedPlaying(uint32_t aNotifyPlayback,
+                                                      float *aVolume,
                                                       bool* aMuted)
 {
   MOZ_ASSERT(aVolume);
   MOZ_ASSERT(aMuted);
 
   // Window-less AudioChannelAgents are muted by default.
   if (!mWindow) {
     *aVolume = 0;
@@ -221,42 +223,43 @@ NS_IMETHODIMP AudioChannelAgent::NotifyS
   }
 
   RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
   if (mAudioChannelType == AUDIO_AGENT_CHANNEL_ERROR ||
       service == nullptr || mIsRegToService) {
     return NS_ERROR_FAILURE;
   }
 
-  service->RegisterAudioChannelAgent(this,
+  service->RegisterAudioChannelAgent(this, aNotifyPlayback,
     static_cast<AudioChannel>(mAudioChannelType));
 
   service->GetState(mWindow, mAudioChannelType, aVolume, aMuted);
 
   MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
          ("AudioChannelAgent, NotifyStartedPlaying, this = %p, mute = %d, "
           "volume = %f\n", this, *aMuted, *aVolume));
 
+  mNotifyPlayback = aNotifyPlayback;
   mIsRegToService = true;
   return NS_OK;
 }
 
 NS_IMETHODIMP AudioChannelAgent::NotifyStoppedPlaying()
 {
   if (mAudioChannelType == AUDIO_AGENT_CHANNEL_ERROR ||
       !mIsRegToService) {
     return NS_ERROR_FAILURE;
   }
 
   MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
          ("AudioChannelAgent, NotifyStoppedPlaying, this = %p\n", this));
 
   RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
   if (service) {
-    service->UnregisterAudioChannelAgent(this);
+    service->UnregisterAudioChannelAgent(this, mNotifyPlayback);
   }
 
   mIsRegToService = false;
   return NS_OK;
 }
 
 already_AddRefed<nsIAudioChannelAgentCallback>
 AudioChannelAgent::GetCallback()
@@ -292,33 +295,22 @@ AudioChannelAgent::WindowVolumeChanged()
 }
 
 uint64_t
 AudioChannelAgent::WindowID() const
 {
   return mWindow ? mWindow->WindowID() : 0;
 }
 
-uint64_t
-AudioChannelAgent::InnerWindowID() const
-{
-  return mInnerWindowID;
-}
-
 void
-AudioChannelAgent::WindowAudioCaptureChanged(uint64_t aInnerWindowID,
-                                             bool aCapture)
+AudioChannelAgent::WindowAudioCaptureChanged(uint64_t aInnerWindowID)
 {
   if (aInnerWindowID != mInnerWindowID) {
     return;
   }
 
   nsCOMPtr<nsIAudioChannelAgentCallback> callback = GetCallback();
   if (!callback) {
     return;
   }
 
-  MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
-         ("AudioChannelAgent, WindowAudioCaptureChanged, this = %p, "
-          "capture = %d\n", this, aCapture));
-
-  callback->WindowAudioCaptureChanged(aCapture);
+  callback->WindowAudioCaptureChanged();
 }
--- a/dom/audiochannel/AudioChannelAgent.h
+++ b/dom/audiochannel/AudioChannelAgent.h
@@ -29,25 +29,24 @@ public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_NSIAUDIOCHANNELAGENT
 
   NS_DECL_CYCLE_COLLECTION_CLASS(AudioChannelAgent)
 
   AudioChannelAgent();
 
   void WindowVolumeChanged();
-  void WindowAudioCaptureChanged(uint64_t aInnerWindowID, bool aCapture);
+  void WindowAudioCaptureChanged(uint64_t aInnerWindowID);
 
   nsPIDOMWindow* Window() const
   {
     return mWindow;
   }
 
   uint64_t WindowID() const;
-  uint64_t InnerWindowID() 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();
 
@@ -62,15 +61,16 @@ private:
   nsCOMPtr<nsPIDOMWindow> mWindow;
   nsCOMPtr<nsIAudioChannelAgentCallback> mCallback;
 
   nsWeakPtr mWeakCallback;
 
   int32_t mAudioChannelType;
   uint64_t mInnerWindowID;
   bool mIsRegToService;
+  bool mNotifyPlayback;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 
 #endif
--- a/dom/audiochannel/AudioChannelService.cpp
+++ b/dom/audiochannel/AudioChannelService.cpp
@@ -263,16 +263,17 @@ AudioChannelService::AudioChannelService
 }
 
 AudioChannelService::~AudioChannelService()
 {
 }
 
 void
 AudioChannelService::RegisterAudioChannelAgent(AudioChannelAgent* aAgent,
+                                               uint32_t aNotifyPlayback,
                                                AudioChannel aChannel)
 {
   uint64_t windowID = aAgent->WindowID();
   AudioChannelWindow* winData = GetWindowData(windowID);
   if (!winData) {
     winData = new AudioChannelWindow(windowID);
     mWindows.AppendElement(winData);
   }
@@ -283,34 +284,29 @@ AudioChannelService::RegisterAudioChanne
   ++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.
-  if (winData->mAgents.Length() == 1) {
+  if (aNotifyPlayback == nsIAudioChannelAgent::AUDIO_AGENT_NOTIFY &&
+      winData->mAgents.Length() == 1) {
     RefPtr<MediaPlaybackRunnable> runnable =
       new MediaPlaybackRunnable(aAgent->Window(), true /* active */);
     NS_DispatchToCurrentThread(runnable);
   }
 
-  // If the window has already been captured, the agent of that window should
-  // also be captured.
-  if (winData->mIsAudioCaptured) {
-    aAgent->WindowAudioCaptureChanged(aAgent->InnerWindowID(),
-                                      winData->mIsAudioCaptured);
-  }
-
   MaybeSendStatusUpdate();
 }
 
 void
-AudioChannelService::UnregisterAudioChannelAgent(AudioChannelAgent* aAgent)
+AudioChannelService::UnregisterAudioChannelAgent(AudioChannelAgent* aAgent,
+                                                 uint32_t aNotifyPlayback)
 {
   AudioChannelWindow* winData = GetWindowData(aAgent->WindowID());
   if (!winData) {
     return;
   }
 
   if (winData->mAgents.Contains(aAgent)) {
     int32_t channel = aAgent->AudioChannelType();
@@ -332,27 +328,23 @@ AudioChannelService::UnregisterAudioChan
 #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.
-  if (winData->mAgents.IsEmpty()) {
+  if (aNotifyPlayback == nsIAudioChannelAgent::AUDIO_AGENT_NOTIFY &&
+      winData->mAgents.IsEmpty()) {
     RefPtr<MediaPlaybackRunnable> runnable =
       new MediaPlaybackRunnable(aAgent->Window(), false /* active */);
     NS_DispatchToCurrentThread(runnable);
   }
 
-  // No need to capture non-audible object.
-  if (winData->mIsAudioCaptured) {
-    aAgent->WindowAudioCaptureChanged(aAgent->InnerWindowID(), false);
-  }
-
   MaybeSendStatusUpdate();
 }
 
 void
 AudioChannelService::RegisterTabParent(TabParent* aTabParent)
 {
   MOZ_ASSERT(aTabParent);
   MOZ_ASSERT(!mTabParents.Contains(aTabParent));
@@ -630,51 +622,42 @@ AudioChannelService::RefreshAgentsVolume
   nsTObserverArray<AudioChannelAgent*>::ForwardIterator
     iter(winData->mAgents);
   while (iter.HasMore()) {
     iter.GetNext()->WindowVolumeChanged();
   }
 }
 
 void
-AudioChannelService::SetWindowAudioCaptured(nsPIDOMWindow* aWindow,
-                                            uint64_t aInnerWindowID,
-                                            bool aCapture)
+AudioChannelService::RefreshAgentsCapture(nsPIDOMWindow* aWindow,
+                                          uint64_t aInnerWindowID)
 {
-  MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aWindow);
   MOZ_ASSERT(aWindow->IsOuterWindow());
 
-  MOZ_LOG(GetAudioChannelLog(), LogLevel::Debug,
-         ("AudioChannelService, SetWindowAudioCaptured, window = %p, "
-          "aCapture = %d\n", aWindow, aCapture));
-
   nsCOMPtr<nsPIDOMWindow> topWindow = aWindow->GetScriptableTop();
   if (!topWindow) {
     return;
   }
 
   AudioChannelWindow* winData = GetWindowData(topWindow->WindowID());
 
   // This can happen, but only during shutdown, because the the outer window
   // changes ScriptableTop, so that its ID is different.
   // In this case either we are capturing, and it's too late because the window
   // has been closed anyways, or we are un-capturing, and everything has already
   // been cleaned up by the HTMLMediaElements or the AudioContexts.
   if (!winData) {
     return;
   }
 
-  if (aCapture != winData->mIsAudioCaptured) {
-    winData->mIsAudioCaptured = aCapture;
-    nsTObserverArray<AudioChannelAgent*>::ForwardIterator
-      iter(winData->mAgents);
-    while (iter.HasMore()) {
-      iter.GetNext()->WindowAudioCaptureChanged(aInnerWindowID, aCapture);
-    }
+  nsTObserverArray<AudioChannelAgent*>::ForwardIterator
+    iter(winData->mAgents);
+  while (iter.HasMore()) {
+    iter.GetNext()->WindowAudioCaptureChanged(aInnerWindowID);
   }
 }
 
 /* static */ const nsAttrValue::EnumTable*
 AudioChannelService::GetAudioChannelTable()
 {
   return kMozAudioChannelAttributeTable;
 }
@@ -802,17 +785,17 @@ AudioChannelService::SetAudioChannelVolu
                                            float aVolume)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aWindow);
   MOZ_ASSERT(aWindow->IsOuterWindow());
 
   MOZ_LOG(GetAudioChannelLog(), LogLevel::Debug,
          ("AudioChannelService, SetAudioChannelVolume, window = %p, type = %d, "
-          "volume = %f\n", aWindow, aAudioChannel, aVolume));
+          "volume = %d\n", aWindow, aAudioChannel, aVolume));
 
   AudioChannelWindow* winData = GetOrCreateWindowData(aWindow);
   winData->mChannels[(uint32_t)aAudioChannel].mVolume = aVolume;
   RefreshAgentsVolumeAndPropagate(aAudioChannel, aWindow);
 }
 
 NS_IMETHODIMP
 AudioChannelService::SetAudioChannelVolume(nsIDOMWindow* aWindow,
--- a/dom/audiochannel/AudioChannelService.h
+++ b/dom/audiochannel/AudioChannelService.h
@@ -51,23 +51,25 @@ public:
 
   static PRLogModuleInfo* GetAudioChannelLog();
 
   /**
    * Any audio channel agent that starts playing should register itself to
    * this service, sharing the AudioChannel.
    */
   void RegisterAudioChannelAgent(AudioChannelAgent* aAgent,
+                                 uint32_t aNotifyPlayback,
                                  AudioChannel aChannel);
 
   /**
    * Any audio channel agent that stops playing should unregister itself to
    * this service.
    */
-  void UnregisterAudioChannelAgent(AudioChannelAgent* aAgent);
+  void UnregisterAudioChannelAgent(AudioChannelAgent* aAgent,
+                                   uint32_t aNotifyPlayback);
 
   /**
    * For nested iframes.
    */
   void RegisterTabParent(TabParent* aTabParent);
   void UnregisterTabParent(TabParent* aTabParent);
 
   /**
@@ -117,19 +119,18 @@ public:
 
   void RefreshAgentsVolumeAndPropagate(AudioChannel aAudioChannel,
                                        nsPIDOMWindow* aWindow);
 
   // This method needs to know the inner window that wants to capture audio. We
   // group agents per top outer window, but we can have multiple innerWindow per
   // top outerWindow (subiframes, etc.) and we have to identify all the agents
   // just for a particular innerWindow.
-  void SetWindowAudioCaptured(nsPIDOMWindow* aWindow,
-                              uint64_t aInnerWindowID,
-                              bool aCapture);
+  void RefreshAgentsCapture(nsPIDOMWindow* aWindow,
+                            uint64_t aInnerWindowID);
 
 
 #ifdef MOZ_WIDGET_GONK
   void RegisterSpeakerManager(SpeakerManagerService* aSpeakerManager)
   {
     if (!mSpeakerManager.Contains(aSpeakerManager)) {
       mSpeakerManager.AppendElement(aSpeakerManager);
     }
@@ -183,25 +184,23 @@ private:
     bool mMuted;
 
     uint32_t mNumberOfAgents;
   };
 
   struct AudioChannelWindow final
   {
     explicit AudioChannelWindow(uint64_t aWindowID)
-      : mWindowID(aWindowID),
-        mIsAudioCaptured(false)
+      : mWindowID(aWindowID)
     {
       // Workaround for bug1183033, system channel type can always playback.
       mChannels[(int16_t)AudioChannel::System].mMuted = false;
     }
 
     uint64_t mWindowID;
-    bool mIsAudioCaptured;
     AudioChannelConfig mChannels[NUMBER_OF_AUDIO_CHANNELS];
 
     // Raw pointer because the AudioChannelAgent must unregister itself.
     nsTObserverArray<AudioChannelAgent*> mAgents;
   };
 
   AudioChannelWindow*
   GetOrCreateWindowData(nsPIDOMWindow* aWindow);
--- a/dom/audiochannel/nsIAudioChannelAgent.idl
+++ b/dom/audiochannel/nsIAudioChannelAgent.idl
@@ -1,45 +1,45 @@
 /* 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(0a451ee0-972e-11e5-a837-0800200c9a66)]
+[uuid(5fe83b24-38b9-4901-a4a1-d1bd57d3fe18)]
 interface nsIAudioChannelAgentCallback : nsISupports
 {
   /**
    * Notified when the window volume/mute is changed
    */
   void windowVolumeChanged(in float aVolume, in bool aMuted);
 
   /**
    * Notified when the capture state is changed.
    */
-  void windowAudioCaptureChanged(in bool aCapture);
+  void windowAudioCaptureChanged();
 };
 
 /**
  * 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(ab7e21c0-970c-11e5-a837-0800200c9a66)]
+[uuid(18222148-1b32-463d-b050-b741f43a07ba)]
 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;
@@ -47,16 +47,19 @@ interface nsIAudioChannelAgent : nsISupp
   const long AUDIO_AGENT_CHANNEL_SYSTEM             = 7;
 
   const long AUDIO_AGENT_CHANNEL_ERROR              = 1000;
 
   const long AUDIO_AGENT_STATE_NORMAL               = 0;
   const long AUDIO_AGENT_STATE_MUTED                = 1;
   const long AUDIO_AGENT_STATE_FADED                = 2;
 
+  const long AUDIO_AGENT_DONT_NOTIFY                = 0;
+  const long AUDIO_AGENT_NOTIFY                     = 1;
+
   /**
    * Before init() is called, this returns AUDIO_AGENT_CHANNEL_ERROR.
    */
   readonly attribute long audioChannelType;
 
   %{C++
   inline int32_t AudioChannelType() {
     int32_t channel;
@@ -93,25 +96,29 @@ interface nsIAudioChannelAgent : nsISupp
   void initWithWeakCallback(in nsIDOMWindow window, in long channelType,
                             in nsIAudioChannelAgentCallback callback);
 
   /**
    * 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.
    *
+   * @param notifyPlaying
+   *   Whether to send audio-playback notifications, one of AUDIO_CHANNEL_NOTIFY
+   *   or AUDIO_CHANNEL_DONT_NOTIFY.
+   *
    * @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.
    */
-  void notifyStartedPlaying(out float volume, out bool muted);
+  void notifyStartedPlaying(in unsigned long notifyPlayback, out float volume, out bool muted);
 
   /**
    * Notify the agent we no longer want to play.
    *
    * Note : even if notifyStartedPlaying() returned false, the agent would
    * still be registered with the audio channel service and receive callbacks
    * for status changes. So notifyStoppedPlaying must still eventually be
    * called to unregister the agent with the channel service.
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -3738,17 +3738,17 @@ nsresult
 nsPIDOMWindow::SetAudioCapture(bool aCapture)
 {
   MOZ_ASSERT(IsInnerWindow());
 
   mAudioCaptured = aCapture;
 
   RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
   if (service) {
-    service->SetWindowAudioCaptured(GetOuterWindow(), mWindowID, aCapture);
+    service->RefreshAgentsCapture(GetOuterWindow(), mWindowID);
   }
 
   return NS_OK;
 }
 
 // nsISpeechSynthesisGetter
 
 #ifdef MOZ_WEBSPEECH
--- a/dom/camera/DOMCameraControl.cpp
+++ b/dom/camera/DOMCameraControl.cpp
@@ -1182,17 +1182,18 @@ nsDOMCameraControl::NotifyRecordingStatu
       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.
     float volume = 0.0;
     bool muted = true;
-    rv = mAudioChannelAgent->NotifyStartedPlaying(&volume, &muted);
+    rv = mAudioChannelAgent->NotifyStartedPlaying(nsIAudioChannelAgent::AUDIO_AGENT_DONT_NOTIFY,
+                                                  &volume, &muted);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 #endif
   return rv;
 }
 
--- a/dom/fmradio/FMRadio.cpp
+++ b/dom/fmradio/FMRadio.cpp
@@ -449,32 +449,33 @@ FMRadio::DisableRDS()
 
 void
 FMRadio::EnableAudioChannelAgent()
 {
   NS_ENSURE_TRUE_VOID(mAudioChannelAgent);
 
   float volume = 0.0;
   bool muted = true;
-  mAudioChannelAgent->NotifyStartedPlaying(&volume, &muted);
+  mAudioChannelAgent->NotifyStartedPlaying(nsIAudioChannelAgent::AUDIO_AGENT_NOTIFY,
+                                           &volume, &muted);
   WindowVolumeChanged(volume, muted);
 
   mAudioChannelAgentEnabled = true;
 }
 
 NS_IMETHODIMP
 FMRadio::WindowVolumeChanged(float aVolume, bool aMuted)
 {
   IFMRadioService::Singleton()->EnableAudio(!aMuted);
   // TODO: what about the volume?
   return NS_OK;
 }
 
 NS_IMETHODIMP
-FMRadio::WindowAudioCaptureChanged(bool aCapture)
+FMRadio::WindowAudioCaptureChanged()
 {
   return NS_OK;
 }
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FMRadio)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
   NS_INTERFACE_MAP_ENTRY(nsIAudioChannelAgentCallback)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -109,28 +109,41 @@ static mozilla::LazyLogModule gMediaElem
 #include "mozilla/EventStateManager.h"
 
 using namespace mozilla::layers;
 using mozilla::net::nsMediaFragmentURIParser;
 
 class MOZ_STACK_CLASS AutoNotifyAudioChannelAgent
 {
   RefPtr<mozilla::dom::HTMLMediaElement> mElement;
+  bool mShouldNotify;
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER;
 public:
-  explicit AutoNotifyAudioChannelAgent(mozilla::dom::HTMLMediaElement* aElement
-                                       MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+  AutoNotifyAudioChannelAgent(mozilla::dom::HTMLMediaElement* aElement,
+                              bool aNotify
+                              MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
     : mElement(aElement)
+    , mShouldNotify(aNotify)
   {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-  }
-
+    if (mShouldNotify) {
+      // The audio channel agent may not exist now.
+      if (mElement->MaybeCreateAudioChannelAgent()) {
+        mElement->NotifyAudioChannelAgent(false);
+      }
+    }
+  }
   ~AutoNotifyAudioChannelAgent()
   {
-    mElement->UpdateAudioChannelPlayingState();
+    if (mShouldNotify) {
+      // The audio channel agent is destroyed at this point.
+      if (mElement->MaybeCreateAudioChannelAgent()) {
+        mElement->NotifyAudioChannelAgent(true);
+      }
+    }
   }
 };
 
 namespace mozilla {
 namespace dom {
 
 // Number of milliseconds between progress events as defined by spec
 static const uint32_t PROGRESS_MS = 350;
@@ -3381,17 +3394,20 @@ void HTMLMediaElement::ProcessMediaFragm
 void HTMLMediaElement::MetadataLoaded(const MediaInfo* aInfo,
                                       nsAutoPtr<const MetadataTags> aTags)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // If the element is gaining or losing an audio track, we need to notify
   // the audio channel agent so that the correct audio-playback events will
   // get dispatched.
-  AutoNotifyAudioChannelAgent autoNotify(this);
+  bool audioTrackChanging = mMediaInfo.HasAudio() != aInfo->HasAudio();
+  AutoNotifyAudioChannelAgent autoNotify(this,
+                                         audioTrackChanging &&
+                                         mPlayingThroughTheAudioChannel);
 
   mMediaInfo = *aInfo;
   mIsEncrypted = aInfo->IsEncrypted()
 #ifdef MOZ_EME
                  || mPendingEncryptedInitData.IsEncrypted()
 #endif // MOZ_EME
                  ;
   mTags = aTags.forget();
@@ -4740,21 +4756,16 @@ HTMLMediaElement::MaybeCreateAudioChanne
 bool
 HTMLMediaElement::IsPlayingThroughTheAudioChannel() const
 {
   // Are we paused or muted
   if (mPaused || Muted()) {
     return false;
   }
 
-  // If this element doesn't have any audio tracks.
-  if (!HasAudio()) {
-    return false;
-  }
-
   // The volume should not be ~0
   if (std::fabs(Volume()) <= 1e-7) {
     return false;
   }
 
   // We should consider any bfcached page or inactive document as non-playing.
   if (!IsActive()) {
     return false;
@@ -4800,25 +4811,33 @@ HTMLMediaElement::UpdateAudioChannelPlay
       NotifyAudioChannelAgent(mPlayingThroughTheAudioChannel);
     }
   }
 }
 
 void
 HTMLMediaElement::NotifyAudioChannelAgent(bool aPlaying)
 {
+  // Immediately check if this should go to the MSG instead of the normal
+  // media playback route.
+  WindowAudioCaptureChanged();
+
   // 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 (aPlaying) {
+    // Don't notify playback if this element doesn't have any audio tracks.
+    uint32_t notify = HasAudio() ? nsIAudioChannelAgent::AUDIO_AGENT_NOTIFY :
+                                   nsIAudioChannelAgent::AUDIO_AGENT_DONT_NOTIFY;
+
     float volume = 0.0;
     bool muted = true;
-    mAudioChannelAgent->NotifyStartedPlaying(&volume, &muted);
+    mAudioChannelAgent->NotifyStartedPlaying(notify, &volume, &muted);
     WindowVolumeChanged(volume, muted);
   } else {
     mAudioChannelAgent->NotifyStoppedPlaying();
     mAudioChannelAgent = nullptr;
   }
 }
 
 NS_IMETHODIMP HTMLMediaElement::WindowVolumeChanged(float aVolume, bool aMuted)
@@ -4970,27 +4989,27 @@ HTMLMediaElement::GetTopLevelPrincipal()
   if (!doc) {
     return nullptr;
   }
   principal = doc->NodePrincipal();
   return principal.forget();
 }
 #endif // MOZ_EME
 
-NS_IMETHODIMP HTMLMediaElement::WindowAudioCaptureChanged(bool aCapture)
+NS_IMETHODIMP HTMLMediaElement::WindowAudioCaptureChanged()
 {
   MOZ_ASSERT(mAudioChannelAgent);
-  MOZ_ASSERT(HasAudio());
 
   if (!OwnerDoc()->GetInnerWindow()) {
     return NS_OK;
   }
-
-  if (aCapture != mAudioCapturedByWindow) {
-    if (aCapture) {
+  bool captured = OwnerDoc()->GetInnerWindow()->GetAudioCaptured();
+
+  if (captured != mAudioCapturedByWindow) {
+    if (captured) {
       mAudioCapturedByWindow = true;
       nsCOMPtr<nsPIDOMWindow> window =
         do_QueryInterface(OwnerDoc()->GetParentObject());
       uint64_t id = window->WindowID();
       MediaStreamGraph* msg =
         MediaStreamGraph::GetInstance(MediaStreamGraph::AUDIO_THREAD_DRIVER,
                                       mAudioChannel);
 
@@ -5016,17 +5035,17 @@ NS_IMETHODIMP HTMLMediaElement::WindowAu
 
         mDecoder->RemoveOutputStream(ps);
       }
       mCaptureStreamPort->Destroy();
       mCaptureStreamPort = nullptr;
     }
   }
 
-  return NS_OK;
+   return NS_OK;
 }
 
 AudioTrackList*
 HTMLMediaElement::AudioTracks()
 {
   if (!mAudioTrackList) {
     nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(OwnerDoc()->GetParentObject());
     mAudioTrackList = new AudioTrackList(window, this);
--- a/dom/media/webaudio/AudioDestinationNode.cpp
+++ b/dom/media/webaudio/AudioDestinationNode.cpp
@@ -532,39 +532,41 @@ AudioDestinationNode::WindowVolumeChange
     }
   }
 
   SetCanPlay(aVolume, aMuted);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-AudioDestinationNode::WindowAudioCaptureChanged(bool aCapture)
+AudioDestinationNode::WindowAudioCaptureChanged()
 {
   MOZ_ASSERT(mAudioChannelAgent);
 
   if (!mStream || Context()->IsOffline()) {
     return NS_OK;
   }
 
   nsCOMPtr<nsPIDOMWindow> ownerWindow = GetOwner();
   if (!ownerWindow) {
     return NS_OK;
   }
 
-  if (aCapture != mCaptured) {
-    if (aCapture) {
+  bool captured = ownerWindow->GetAudioCaptured();
+
+  if (captured != mCaptured) {
+    if (captured) {
       nsCOMPtr<nsPIDOMWindow> window = Context()->GetParentObject();
       uint64_t id = window->WindowID();
       mCaptureStreamPort =
         mStream->Graph()->ConnectToCaptureStream(id, mStream);
     } else {
       mCaptureStreamPort->Destroy();
     }
-    mCaptured = aCapture;
+    mCaptured = captured;
   }
 
   return NS_OK;
 }
 
 AudioChannel
 AudioDestinationNode::MozAudioChannelType() const
 {
@@ -646,17 +648,20 @@ AudioDestinationNode::CreateAudioChannel
   mAudioChannelAgent = new AudioChannelAgent();
   rv = mAudioChannelAgent->InitWithWeakCallback(GetOwner(),
                                            static_cast<int32_t>(mAudioChannel),
                                            this);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  return NS_OK;
+  rv = WindowAudioCaptureChanged();
+  NS_WARN_IF(NS_FAILED(rv));
+  return rv;
+
 }
 
 void
 AudioDestinationNode::NotifyStableState()
 {
   mExtraCurrentTimeUpdatedSinceLastStableState = false;
 }
 
@@ -736,18 +741,20 @@ AudioDestinationNode::InputMuted(bool aM
 
   if (aMuted) {
     mAudioChannelAgent->NotifyStoppedPlaying();
     return;
   }
 
   float volume = 0.0;
   bool muted = true;
-  nsresult rv = mAudioChannelAgent->NotifyStartedPlaying(&volume, &muted);
+  nsresult rv = mAudioChannelAgent->NotifyStartedPlaying(nsIAudioChannelAgent::AUDIO_AGENT_NOTIFY,
+                                                         &volume, &muted);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
   }
 
+  WindowAudioCaptureChanged();
   WindowVolumeChanged(volume, muted);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/media/webspeech/synth/nsSpeechTask.cpp
+++ b/dom/media/webspeech/synth/nsSpeechTask.cpp
@@ -691,17 +691,17 @@ nsSpeechTask::CreateAudioChannelAgent()
   }
 
   mAudioChannelAgent = new AudioChannelAgent();
   mAudioChannelAgent->InitWithWeakCallback(mUtterance->GetOwner(),
                                            static_cast<int32_t>(AudioChannelService::GetDefaultAudioChannel()),
                                            this);
   float volume = 0.0f;
   bool muted = true;
-  mAudioChannelAgent->NotifyStartedPlaying(&volume, &muted);
+  mAudioChannelAgent->NotifyStartedPlaying(nsIAudioChannelAgent::AUDIO_AGENT_NOTIFY, &volume, &muted);
 }
 
 void
 nsSpeechTask::DestroyAudioChannelAgent()
 {
   if (mAudioChannelAgent) {
     mAudioChannelAgent->NotifyStoppedPlaying();
     mAudioChannelAgent = nullptr;
@@ -711,17 +711,17 @@ nsSpeechTask::DestroyAudioChannelAgent()
 NS_IMETHODIMP
 nsSpeechTask::WindowVolumeChanged(float aVolume, bool aMuted)
 {
   SetAudioOutputVolume(aMuted ? 0.0 : mVolume * aVolume);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsSpeechTask::WindowAudioCaptureChanged(bool aCapture)
+nsSpeechTask::WindowAudioCaptureChanged()
 {
   // This is not supported yet.
   return NS_OK;
 }
 
 void
 nsSpeechTask::SetAudioOutputVolume(float aVolume)
 {
--- a/dom/plugins/base/nsNPAPIPlugin.cpp
+++ b/dom/plugins/base/nsNPAPIPlugin.cpp
@@ -2291,17 +2291,18 @@ NPError
       if (isMuted) {
         rv = agent->NotifyStoppedPlaying();
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return NPERR_NO_ERROR;
         }
       } else {
         float volume = 0.0;
         bool muted = true;
-        rv = agent->NotifyStartedPlaying(&volume, &muted);
+        rv = agent->NotifyStartedPlaying(nsIAudioChannelAgent::AUDIO_AGENT_NOTIFY,
+                                         &volume, &muted);
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return NPERR_NO_ERROR;
         }
 
         rv = inst->WindowVolumeChanged(volume, muted);
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return NPERR_NO_ERROR;
         }
--- a/dom/plugins/base/nsNPAPIPluginInstance.cpp
+++ b/dom/plugins/base/nsNPAPIPluginInstance.cpp
@@ -1855,17 +1855,17 @@ nsNPAPIPluginInstance::WindowVolumeChang
 {
   // We just support mute/unmute
   nsresult rv = SetMuted(aMuted);
   NS_WARN_IF(NS_FAILED(rv));
   return rv;
 }
 
 NS_IMETHODIMP
-nsNPAPIPluginInstance::WindowAudioCaptureChanged(bool aCapture)
+nsNPAPIPluginInstance::WindowAudioCaptureChanged()
 {
   return NS_OK;
 }
 
 nsresult
 nsNPAPIPluginInstance::SetMuted(bool aIsMuted)
 {
   if (RUNNING != mRunning)
--- a/dom/telephony/Telephony.cpp
+++ b/dom/telephony/Telephony.cpp
@@ -557,17 +557,18 @@ Telephony::HandleAudioAgentState()
     mAudioAgent = nullptr;
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   } else if (!activeCall.IsNull() && !mIsAudioStartPlaying) {
     mIsAudioStartPlaying = true;
     float volume;
     bool muted;
-    rv = mAudioAgent->NotifyStartedPlaying(&volume, &muted);
+    rv = mAudioAgent->NotifyStartedPlaying(nsIAudioChannelAgent::AUDIO_AGENT_NOTIFY,
+                                           &volume, &muted);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     // In B2G, the system app manages audio playback policy. If there is a new
     // sound want to be playback, it must wait for the permission from the
     // system app. It means that the sound would be muted first, and then be
     // unmuted. For telephony, the behaviors are hold() first, then resume().
@@ -706,17 +707,17 @@ Telephony::WindowVolumeChanged(float aVo
       mHaveDispatchedInterruptBeginEvent = mMuted;
     }
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-Telephony::WindowAudioCaptureChanged(bool aCapture)
+Telephony::WindowAudioCaptureChanged()
 {
   // Do nothing, it's useless for the telephony object.
   return NS_OK;
 }
 
 // nsITelephonyListener
 
 NS_IMETHODIMP