Bug 1156472 - Part 1 - Allow to capture all HTMLMediaElements and AudioContexts for a document. r=baku,padenot
☠☠ backed out by a1829935d87e ☠ ☠
authorPaul Adenot <paul@paul.cx>
Thu, 09 Jul 2015 16:40:08 +0200
changeset 254484 05acb71cf98148393803b13c041066aea85b56b8
parent 254483 129fd95e3d41b63e879b3ccbc4ff6f4a1d133de1
child 254485 13730e7c5997a0365b4d03318bb44c9192712d42
push id62772
push userpaul@paul.cx
push dateFri, 24 Jul 2015 14:43:18 +0000
treeherdermozilla-inbound@2ddbf85a42c0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku, padenot
bugs1156472
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 1156472 - Part 1 - Allow to capture all HTMLMediaElements and AudioContexts for a document. r=baku,padenot This is built on top of the AudioChannel infrastructure. This patch does not actually implement the capture, it just does the plumbing to be able to notify all HTMLMediaElement/AudioContext for a document.
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/base/nsPIDOMWindow.h
dom/fmradio/FMRadio.cpp
dom/html/HTMLMediaElement.cpp
dom/media/webaudio/AudioDestinationNode.cpp
--- a/dom/audiochannel/AudioChannelAgent.cpp
+++ b/dom/audiochannel/AudioChannelAgent.cpp
@@ -30,16 +30,17 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 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)
 {
 }
 
 AudioChannelAgent::~AudioChannelAgent()
 {
   Shutdown();
 }
@@ -99,16 +100,20 @@ 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> pInnerWindow = do_QueryInterface(aWindow);
+    MOZ_ASSERT(pInnerWindow->IsInnerWindow());
+    mInnerWindowID = pInnerWindow->WindowID();
+
     nsCOMPtr<nsIDOMWindow> topWindow;
     aWindow->GetScriptableTop(getter_AddRefs(topWindow));
     mWindow = do_QueryInterface(topWindow);
     if (mWindow) {
       mWindow = mWindow->GetOuterWindow();
     }
   }
 
@@ -186,8 +191,23 @@ AudioChannelAgent::WindowVolumeChanged()
   callback->WindowVolumeChanged(volume, muted);
 }
 
 uint64_t
 AudioChannelAgent::WindowID() const
 {
   return mWindow ? mWindow->WindowID() : 0;
 }
+
+void
+AudioChannelAgent::WindowAudioCaptureChanged(uint64_t aInnerWindowID)
+{
+  if (aInnerWindowID != mInnerWindowID) {
+    return;
+  }
+
+  nsCOMPtr<nsIAudioChannelAgentCallback> callback = GetCallback();
+  if (!callback) {
+    return;
+  }
+
+  callback->WindowAudioCaptureChanged();
+}
--- a/dom/audiochannel/AudioChannelAgent.h
+++ b/dom/audiochannel/AudioChannelAgent.h
@@ -29,16 +29,17 @@ public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_NSIAUDIOCHANNELAGENT
 
   NS_DECL_CYCLE_COLLECTION_CLASS(AudioChannelAgent)
 
   AudioChannelAgent();
 
   void WindowVolumeChanged();
+  void WindowAudioCaptureChanged(uint64_t aInnerWindowID);
 
   nsPIDOMWindow* Window() const
   {
     return mWindow;
   }
 
   uint64_t WindowID() const;
 
@@ -56,16 +57,17 @@ private:
   void Shutdown();
 
   nsCOMPtr<nsPIDOMWindow> mWindow;
   nsCOMPtr<nsIAudioChannelAgentCallback> mCallback;
 
   nsWeakPtr mWeakCallback;
 
   int32_t mAudioChannelType;
+  uint64_t mInnerWindowID;
   bool mIsRegToService;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 
 #endif
--- a/dom/audiochannel/AudioChannelService.cpp
+++ b/dom/audiochannel/AudioChannelService.cpp
@@ -541,16 +541,39 @@ AudioChannelService::RefreshAgentsVolume
 
   nsTObserverArray<AudioChannelAgent*>::ForwardIterator
     iter(winData->mAgents);
   while (iter.HasMore()) {
     iter.GetNext()->WindowVolumeChanged();
   }
 }
 
+void
+AudioChannelService::RefreshAgentsCapture(nsPIDOMWindow* aWindow,
+                                          uint64_t aInnerWindowID)
+{
+  MOZ_ASSERT(aWindow);
+  MOZ_ASSERT(aWindow->IsOuterWindow());
+
+  nsCOMPtr<nsIDOMWindow> topWindow;
+  aWindow->GetScriptableTop(getter_AddRefs(topWindow));
+  nsCOMPtr<nsPIDOMWindow> pTopWindow = do_QueryInterface(topWindow);
+  if (!pTopWindow) {
+    return;
+  }
+
+  AudioChannelWindow* winData = GetWindowData(pTopWindow->WindowID());
+
+  nsTObserverArray<AudioChannelAgent*>::ForwardIterator
+    iter(winData->mAgents);
+  while (iter.HasMore()) {
+    iter.GetNext()->WindowAudioCaptureChanged(aInnerWindowID);
+  }
+}
+
 /* static */ const nsAttrValue::EnumTable*
 AudioChannelService::GetAudioChannelTable()
 {
   return kMozAudioChannelAttributeTable;
 }
 
 /* static */ AudioChannel
 AudioChannelService::GetAudioChannel(const nsAString& aChannel)
--- a/dom/audiochannel/AudioChannelService.h
+++ b/dom/audiochannel/AudioChannelService.h
@@ -97,16 +97,24 @@ public:
    */
   virtual void SetDefaultVolumeControlChannel(int32_t aChannel,
                                               bool aVisible);
 
   bool AnyAudioChannelIsActive();
 
   void RefreshAgentsVolume(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 RefreshAgentsCapture(nsPIDOMWindow* aWindow,
+                            uint64_t aInnerWindowID);
+
+
 #ifdef MOZ_WIDGET_GONK
   void RegisterSpeakerManager(SpeakerManagerService* aSpeakerManager)
   {
     if (!mSpeakerManager.Contains(aSpeakerManager)) {
       mSpeakerManager.AppendElement(aSpeakerManager);
     }
   }
 
--- a/dom/audiochannel/nsIAudioChannelAgent.idl
+++ b/dom/audiochannel/nsIAudioChannelAgent.idl
@@ -1,23 +1,28 @@
 /* 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(4f537c88-3722-4946-9a09-ce559fa0591d)]
+[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();
 };
 
 /**
  * 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.
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -559,17 +559,17 @@ nsPIDOMWindow::nsPIDOMWindow(nsPIDOMWind
 : mFrameElement(nullptr), mDocShell(nullptr), mModalStateDepth(0),
   mRunningTimeout(nullptr), mMutationBits(0), mIsDocumentLoaded(false),
   mIsHandlingResizeEvent(false), mIsInnerWindow(aOuterWindow != nullptr),
   mMayHavePaintEventListener(false), mMayHaveTouchEventListener(false),
   mMayHaveMouseEnterLeaveEventListener(false),
   mMayHavePointerEnterLeaveEventListener(false),
   mIsModalContentWindow(false),
   mIsActive(false), mIsBackground(false),
-  mAudioMuted(false), mAudioVolume(1.0),
+  mAudioMuted(false), mAudioVolume(1.0), mAudioCaptured(false),
   mDesktopModeViewport(false), mInnerWindow(nullptr),
   mOuterWindow(aOuterWindow),
   // Make sure no actual window ends up with mWindowID == 0
   mWindowID(NextWindowID()), mHasNotifiedGlobalCreated(false),
   mMarkedCCGeneration(0), mServiceWorkersTestingEnabled(false)
  {}
 
 nsPIDOMWindow::~nsPIDOMWindow() {}
@@ -3740,16 +3740,36 @@ nsPIDOMWindow::SetAudioVolume(float aVol
 
 void
 nsPIDOMWindow::RefreshMediaElements()
 {
   nsRefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
   service->RefreshAgentsVolume(GetOuterWindow());
 }
 
+bool
+nsPIDOMWindow::GetAudioCaptured() const
+{
+  MOZ_ASSERT(IsInnerWindow());
+  return mAudioCaptured;
+}
+
+nsresult
+nsPIDOMWindow::SetAudioCapture(bool aCapture)
+{
+  MOZ_ASSERT(IsInnerWindow());
+
+  mAudioCaptured = aCapture;
+
+  nsRefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
+  service->RefreshAgentsCapture(GetOuterWindow(), mWindowID);
+
+  return NS_OK;
+}
+
 // nsISpeechSynthesisGetter
 
 #ifdef MOZ_WEBSPEECH
 SpeechSynthesis*
 nsGlobalWindow::GetSpeechSynthesis(ErrorResult& aError)
 {
   MOZ_RELEASE_ASSERT(IsInnerWindow());
 
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -180,16 +180,19 @@ public:
 
   // Audio API
   bool GetAudioMuted() const;
   void SetAudioMuted(bool aMuted);
 
   float GetAudioVolume() const;
   nsresult SetAudioVolume(float aVolume);
 
+  bool GetAudioCaptured() const;
+  nsresult SetAudioCapture(bool aCapture);
+
   virtual void SetServiceWorkersTestingEnabled(bool aEnabled)
   {
     MOZ_ASSERT(IsOuterWindow());
     mServiceWorkersTestingEnabled = aEnabled;
   }
 
   bool GetServiceWorkersTestingEnabled()
   {
@@ -817,16 +820,18 @@ protected:
   // Tracks whether our docshell is active.  If it is, mIsBackground
   // is false.  Too bad we have so many different concepts of
   // "active".  Only used on outer windows.
   bool                   mIsBackground;
 
   bool                   mAudioMuted;
   float                  mAudioVolume;
 
+  bool                   mAudioCaptured;
+
   // current desktop mode flag.
   bool                   mDesktopModeViewport;
 
   // And these are the references between inner and outer windows.
   nsPIDOMWindow* MOZ_NON_OWNING_REF mInnerWindow;
   nsCOMPtr<nsPIDOMWindow> mOuterWindow;
 
   // the element within the document that is currently focused when this
--- a/dom/fmradio/FMRadio.cpp
+++ b/dom/fmradio/FMRadio.cpp
@@ -466,16 +466,22 @@ FMRadio::EnableAudioChannelAgent()
 NS_IMETHODIMP
 FMRadio::WindowVolumeChanged(float aVolume, bool aMuted)
 {
   IFMRadioService::Singleton()->EnableAudio(!aMuted);
   // TODO: what about the volume?
   return NS_OK;
 }
 
+NS_IMETHODIMP
+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)
 
 NS_IMPL_ADDREF_INHERITED(FMRadio, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(FMRadio, DOMEventTargetHelper)
 
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -4487,28 +4487,32 @@ void HTMLMediaElement::UpdateAudioChanne
     }
 
     if (!mAudioChannelAgent) {
       nsresult rv;
       mAudioChannelAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1", &rv);
       if (!mAudioChannelAgent) {
         return;
       }
-      mAudioChannelAgent->InitWithWeakCallback(OwnerDoc()->GetWindow(),
+      mAudioChannelAgent->InitWithWeakCallback(OwnerDoc()->GetInnerWindow(),
                                                static_cast<int32_t>(mAudioChannel),
                                                this);
     }
 
     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) {
     float volume = 0.0;
     bool muted = true;
@@ -4669,16 +4673,25 @@ HTMLMediaElement::GetTopLevelPrincipal()
   if (!doc) {
     return nullptr;
   }
   principal = doc->NodePrincipal();
   return principal.forget();
 }
 #endif // MOZ_EME
 
+NS_IMETHODIMP HTMLMediaElement::WindowAudioCaptureChanged()
+{
+  MOZ_ASSERT(mAudioChannelAgent);
+  DebugOnly<bool> captured = OwnerDoc()->GetInnerWindow()->GetAudioCaptured();
+
+  // Something is going to happen here!!
+  return NS_OK;
+}
+
 AudioTrackList*
 HTMLMediaElement::AudioTracks()
 {
   if (!mAudioTrackList) {
     nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(OwnerDoc()->GetParentObject());
     mAudioTrackList = new AudioTrackList(window, this);
   }
   return mAudioTrackList;
--- a/dom/media/webaudio/AudioDestinationNode.cpp
+++ b/dom/media/webaudio/AudioDestinationNode.cpp
@@ -500,16 +500,31 @@ AudioDestinationNode::WindowVolumeChange
                 : NS_LITERAL_STRING("mozinterruptbegin"));
     }
   }
 
   SetCanPlay(aVolume, aMuted);
   return NS_OK;
 }
 
+NS_IMETHODIMP
+AudioDestinationNode::WindowAudioCaptureChanged()
+{
+  MOZ_ASSERT(mAudioChannelAgent);
+
+  if (!mStream) {
+    return NS_OK;
+  }
+
+  DebugOnly<bool> captured = GetOwner()->GetAudioCaptured();
+
+  // XXXtodopadenot actually capture
+  return NS_OK;
+}
+
 AudioChannel
 AudioDestinationNode::MozAudioChannelType() const
 {
   return mAudioChannel;
 }
 
 void
 AudioDestinationNode::SetMozAudioChannelType(AudioChannel aValue, ErrorResult& aRv)
@@ -586,16 +601,18 @@ AudioDestinationNode::CreateAudioChannel
   mAudioChannelAgent = new AudioChannelAgent();
   mAudioChannelAgent->InitWithWeakCallback(GetOwner(),
                                            static_cast<int32_t>(mAudioChannel),
                                            this);
 
   // The AudioChannelAgent must start playing immediately in order to avoid
   // race conditions with mozinterruptbegin/end events.
   InputMuted(false);
+
+  WindowAudioCaptureChanged();
 }
 
 void
 AudioDestinationNode::NotifyStableState()
 {
   mExtraCurrentTimeUpdatedSinceLastStableState = false;
 }