Bug 879111 - Mute Web Audio when allowMedia is false on the docshell. r=ehsan, a=bajaj
authorDrew Willcoxon <adw@mozilla.com>
Wed, 03 Jul 2013 17:44:32 -0700
changeset 147883 856a1ecc4b024706eede5007df8ac583777bb609
parent 147882 d78e01f88bbee1f4cc87c77e0c4c330b2239dd7d
child 147884 78abb9bcd93eadd4c7492db000123cc4ab809f67
push id2697
push userbbajaj@mozilla.com
push dateMon, 05 Aug 2013 18:49:53 +0000
treeherdermozilla-beta@dfec938c7b63 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersehsan, bajaj
bugs879111
milestone24.0a2
Bug 879111 - Mute Web Audio when allowMedia is false on the docshell. r=ehsan, a=bajaj
content/media/webaudio/AudioContext.cpp
content/media/webaudio/AudioContext.h
content/media/webaudio/AudioDestinationNode.cpp
content/media/webaudio/AudioDestinationNode.h
docshell/base/nsDocShell.cpp
dom/base/nsGlobalWindow.cpp
dom/base/nsPIDOMWindow.h
--- a/content/media/webaudio/AudioContext.cpp
+++ b/content/media/webaudio/AudioContext.cpp
@@ -531,10 +531,24 @@ AudioContext::GetJSContext() const
 void
 AudioContext::StartRendering()
 {
   MOZ_ASSERT(mIsOffline, "This should only be called on OfflineAudioContext");
 
   mDestination->StartRendering();
 }
 
+void
+AudioContext::Mute() const
+{
+  MOZ_ASSERT(!mIsOffline);
+  mDestination->Mute();
+}
+
+void
+AudioContext::Unmute() const
+{
+  MOZ_ASSERT(!mIsOffline);
+  mDestination->Unmute();
+}
+
 }
 }
--- a/content/media/webaudio/AudioContext.h
+++ b/content/media/webaudio/AudioContext.h
@@ -205,16 +205,19 @@ public:
   MediaStream* DestinationStream() const;
   void UnregisterAudioBufferSourceNode(AudioBufferSourceNode* aNode);
   void UnregisterPannerNode(PannerNode* aNode);
   void UnregisterScriptProcessorNode(ScriptProcessorNode* aNode);
   void UpdatePannerSource();
 
   uint32_t MaxChannelCount() const;
 
+  void Mute() const;
+  void Unmute() const;
+
   JSContext* GetJSContext() const;
 
 private:
   void RemoveFromDecodeQueue(WebAudioDecodeJob* aDecodeJob);
 
   friend struct ::mozilla::WebAudioDecodeJob;
 
 private:
--- a/content/media/webaudio/AudioDestinationNode.cpp
+++ b/content/media/webaudio/AudioDestinationNode.cpp
@@ -173,16 +173,49 @@ private:
   InputChannels mInputChannels;
   // An index representing the next offset in mInputChannels to be written to.
   uint32_t mWriteIndex;
   // How many frames the OfflineAudioContext intends to produce.
   uint32_t mLength;
   float mSampleRate;
 };
 
+class DestinationNodeEngine : public AudioNodeEngine
+{
+public:
+  explicit DestinationNodeEngine(AudioDestinationNode* aNode)
+    : AudioNodeEngine(aNode)
+    , mVolume(1.0f)
+  {
+  }
+
+  virtual void ProduceAudioBlock(AudioNodeStream* aStream,
+                                 const AudioChunk& aInput,
+                                 AudioChunk* aOutput,
+                                 bool* aFinished) MOZ_OVERRIDE
+  {
+    *aOutput = aInput;
+    aOutput->mVolume *= mVolume;
+  }
+
+  virtual void SetDoubleParameter(uint32_t aIndex, double aParam) MOZ_OVERRIDE
+  {
+    if (aIndex == VOLUME) {
+      mVolume = aParam;
+    }
+  }
+
+  enum Parameters {
+    VOLUME,
+  };
+
+private:
+  float mVolume;
+};
+
 NS_IMPL_ISUPPORTS_INHERITED0(AudioDestinationNode, AudioNode)
 
 AudioDestinationNode::AudioDestinationNode(AudioContext* aContext,
                                            bool aIsOffline,
                                            uint32_t aNumberOfChannels,
                                            uint32_t aLength,
                                            float aSampleRate)
   : AudioNode(aContext,
@@ -192,17 +225,18 @@ AudioDestinationNode::AudioDestinationNo
   , mFramesToProduce(aLength)
 {
   MediaStreamGraph* graph = aIsOffline ?
                             MediaStreamGraph::CreateNonRealtimeInstance() :
                             MediaStreamGraph::GetInstance();
   AudioNodeEngine* engine = aIsOffline ?
                             new OfflineDestinationNodeEngine(this, aNumberOfChannels,
                                                              aLength, aSampleRate) :
-                            new AudioNodeEngine(this);
+                            static_cast<AudioNodeEngine*>(new DestinationNodeEngine(this));
+
   mStream = graph->CreateAudioNodeStream(engine, MediaStreamGraph::EXTERNAL_STREAM);
 }
 
 uint32_t
 AudioDestinationNode::MaxChannelCount() const
 {
   return Context()->MaxChannelCount();
 }
@@ -214,16 +248,30 @@ AudioDestinationNode::SetChannelCount(ui
     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     return;
   }
 
   AudioNode::SetChannelCount(aChannelCount, aRv);
 }
 
 void
+AudioDestinationNode::Mute()
+{
+  MOZ_ASSERT(Context() && !Context()->IsOffline());
+  SendDoubleParameterToStream(DestinationNodeEngine::VOLUME, 0.0f);
+}
+
+void
+AudioDestinationNode::Unmute()
+{
+  MOZ_ASSERT(Context() && !Context()->IsOffline());
+  SendDoubleParameterToStream(DestinationNodeEngine::VOLUME, 1.0f);
+}
+
+void
 AudioDestinationNode::DestroyGraph()
 {
   MOZ_ASSERT(Context() && Context()->IsOffline(),
              "Should only be called on a valid OfflineAudioContext");
   MediaStreamGraph::DestroyNonRealtimeInstance(mStream->Graph());
 }
 
 JSObject*
--- a/content/media/webaudio/AudioDestinationNode.h
+++ b/content/media/webaudio/AudioDestinationNode.h
@@ -34,16 +34,19 @@ public:
   {
     return 0;
   }
 
   uint32_t MaxChannelCount() const;
   virtual void SetChannelCount(uint32_t aChannelCount,
                                ErrorResult& aRv) MOZ_OVERRIDE;
 
+  void Mute();
+  void Unmute();
+
   void StartRendering();
 
   void DestroyGraph();
 
 private:
   uint32_t mFramesToProduce;
 };
 
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -2291,16 +2291,29 @@ NS_IMETHODIMP nsDocShell::GetAllowMedia(
 {
     *aAllowMedia = mAllowMedia;
     return NS_OK;
 }
 
 NS_IMETHODIMP nsDocShell::SetAllowMedia(bool aAllowMedia)
 {
     mAllowMedia = aAllowMedia;
+
+    // Mute or unmute audio contexts attached to the inner window.
+    if (mScriptGlobal) {
+        nsPIDOMWindow* innerWin = mScriptGlobal->GetCurrentInnerWindow();
+        if (innerWin) {
+            if (aAllowMedia) {
+                innerWin->UnmuteAudioContexts();
+            } else {
+                innerWin->MuteAudioContexts();
+            }
+        }
+    }
+
     return NS_OK;
 }
 
 NS_IMETHODIMP nsDocShell::GetAllowDNSPrefetch(bool * aAllowDNSPrefetch)
 {
     *aAllowDNSPrefetch = mAllowDNSPrefetch;
     return NS_OK;
 }
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -3269,16 +3269,37 @@ nsPIDOMWindow::MaybeCreateDoc()
     nsCOMPtr<nsIDocument> document = do_GetInterface(docShell);
   }
 }
 
 void
 nsPIDOMWindow::AddAudioContext(AudioContext* aAudioContext)
 {
   mAudioContexts.AppendElement(aAudioContext);
+
+  nsIDocShell* docShell = GetDocShell();
+  if (docShell && !docShell->GetAllowMedia()) {
+    aAudioContext->Mute();
+  }
+}
+
+void
+nsPIDOMWindow::MuteAudioContexts()
+{
+  for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
+    mAudioContexts[i]->Mute();
+  }
+}
+
+void
+nsPIDOMWindow::UnmuteAudioContexts()
+{
+  for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
+    mAudioContexts[i]->Unmute();
+  }
 }
 
 NS_IMETHODIMP
 nsGlobalWindow::GetDocument(nsIDOMDocument** aDocument)
 {
   nsCOMPtr<nsIDOMDocument> document = do_QueryInterface(GetDoc());
   document.forget(aDocument);
   return NS_OK;
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -639,16 +639,18 @@ public:
   /**
    * Like nsIDOMWindow::Open, except that we don't navigate to the given URL.
    */
   virtual nsresult
   OpenNoNavigate(const nsAString& aUrl, const nsAString& aName,
                  const nsAString& aOptions, nsIDOMWindow **_retval) = 0;
 
   void AddAudioContext(mozilla::dom::AudioContext* aAudioContext);
+  void MuteAudioContexts();
+  void UnmuteAudioContexts();
 
   // Given an inner window, return its outer if the inner is the current inner.
   // Otherwise (argument null or not an inner or not current) return null.
   static nsPIDOMWindow* GetOuterFromCurrentInner(nsPIDOMWindow* aInner)
   {
     if (!aInner) {
       return nullptr;
     }