Bug 1130010: Implement the new AudioNode.disconnect methods. r=dminor,ehsan
authorPaul Adenot <paul@paul.cx>
Fri, 05 Jun 2015 11:17:14 +0200
changeset 345721 178517f7c7360278506a80a44f23e81d53ef9126
parent 345720 d17d4140f8592eeaaf2fc9ca51d7367b50fe04bb
child 345722 162c0295a1203a6f6aab60ed613cd1bb70d10670
push id6389
push userraliiev@mozilla.com
push dateMon, 19 Sep 2016 13:38:22 +0000
treeherdermozilla-beta@01d67bfe6c81 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdminor, ehsan
bugs1130010
milestone50.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 1130010: Implement the new AudioNode.disconnect methods. r=dminor,ehsan Initial patch by Thomas Escalon <tesc.bugzilla@gmail.com>. MozReview-Commit-ID: KDnmKIGWYL
dom/media/webaudio/AudioNode.cpp
dom/media/webaudio/AudioNode.h
dom/media/webaudio/ScriptProcessorNode.h
dom/webidl/AudioNode.webidl
--- a/dom/media/webaudio/AudioNode.cpp
+++ b/dom/media/webaudio/AudioNode.cpp
@@ -286,24 +286,19 @@ void
 AudioNode::SendChannelMixingParametersToStream()
 {
   if (mStream) {
     mStream->SetChannelMixingParameters(mChannelCount, mChannelCountMode,
                                         mChannelInterpretation);
   }
 }
 
-void
-AudioNode::Disconnect(uint32_t aOutput, ErrorResult& aRv)
+bool
+AudioNode::DisconnectFromOutputIfConnected(AudioNode& aDestination, uint32_t aOutputIndex, uint32_t aInputIndex)
 {
-  if (aOutput >= NumberOfOutputs()) {
-    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
-    return;
-  }
-
   WEB_AUDIO_API_LOG("%f: %s %u Disconnect()", Context()->CurrentTime(),
                     NodeType(), Id());
 
   // An upstream node may be starting to play on the graph thread, and the
   // engine for a downstream node may be sending a PlayingRefChangeHandler
   // ADDREF message to this (main) thread.  Wait for a round trip before
   // releasing nodes, to give engines receiving sound now time to keep their
   // nodes alive.
@@ -317,60 +312,233 @@ AudioNode::Disconnect(uint32_t aOutput, 
     {
       mNode = nullptr;
       return NS_OK;
     }
   private:
     RefPtr<AudioNode> mNode;
   };
 
-  for (int32_t i = mOutputNodes.Length() - 1; i >= 0; --i) {
-    AudioNode* dest = mOutputNodes[i];
-    for (int32_t j = dest->mInputNodes.Length() - 1; j >= 0; --j) {
-      InputNode& input = dest->mInputNodes[j];
-      if (input.mInputNode == this && input.mOutputPort == aOutput) {
-        // Destroying the InputNode here sends a message to the graph thread
-        // to disconnect the streams, which should be sent before the
-        // RunAfterPendingUpdates() call below.
-        dest->mInputNodes.RemoveElementAt(j);
-        // Remove one instance of 'dest' from mOutputNodes. There could be
-        // others, and it's not correct to remove them all since some of them
-        // could be for different output ports.
-        RefPtr<AudioNode> output = mOutputNodes[i].forget();
-        mOutputNodes.RemoveElementAt(i);
-        output->NotifyInputsChanged();
-        if (mStream) {
-          RefPtr<nsIRunnable> runnable = new RunnableRelease(output.forget());
-          mStream->RunAfterPendingUpdates(runnable.forget());
-        }
-        break;
+  InputNode& input = aDestination.mInputNodes[aInputIndex];
+  if (input.mInputNode != this) {
+    return false;
+  }
+  // RunAfterPendingUpdates() call below.
+  aDestination.mInputNodes.RemoveElementAt(aInputIndex);
+  // Remove one instance of 'dest' from mOutputNodes. There could be
+  // others, and it's not correct to remove them all since some of them
+  // could be for different output ports.
+  RefPtr<AudioNode> output = mOutputNodes[aOutputIndex].forget();
+  mOutputNodes.RemoveElementAt(aOutputIndex);
+  if (mStream) {
+    nsCOMPtr<nsIRunnable> runnable = new RunnableRelease(output.forget());
+    mStream->RunAfterPendingUpdates(runnable.forget());
+  }
+  return true;
+}
+
+bool
+AudioNode::DisconnectFromParamIfConnected(AudioParam& aDestination, uint32_t aOutputIndex, uint32_t aInputIndex)
+{
+  MOZ_ASSERT(aOutputIndex < mOutputParams.Length());
+  MOZ_ASSERT(aInputIndex < aDestination.InputNodes().Length());
+
+  const InputNode& input = aDestination.InputNodes()[aInputIndex];
+  if (input.mInputNode != this) {
+    return false;
+  }
+  aDestination.RemoveInputNode(aInputIndex);
+  // Remove one instance of 'dest' from mOutputParams. There could be
+  // others, and it's not correct to remove them all since some of them
+  // could be for different output ports.
+  mOutputParams.RemoveElementAt(aOutputIndex);
+  return true;
+}
+
+void
+AudioNode::Disconnect(ErrorResult& aRv)
+{
+  for (int32_t outputIndex = mOutputNodes.Length() - 1; outputIndex >= 0; --outputIndex) {
+    AudioNode* dest = mOutputNodes[outputIndex];
+    for (int32_t inputIndex = dest->mInputNodes.Length() - 1; inputIndex >= 0; --inputIndex) {
+      DisconnectFromOutputIfConnected(*dest, outputIndex, inputIndex);
+    }
+  }
+
+  for (int32_t outputIndex = mOutputParams.Length() - 1; outputIndex >= 0; --outputIndex) {
+    AudioParam* dest = mOutputParams[outputIndex];
+    for (int32_t inputIndex = dest->InputNodes().Length() - 1; inputIndex >= 0; --inputIndex) {
+      DisconnectFromParamIfConnected(*dest, outputIndex, inputIndex);
+    }
+  }
+
+  // This disconnection may have disconnected a panner and a source.
+  Context()->UpdatePannerSource();
+}
+
+void
+AudioNode::Disconnect(uint32_t aOutput, ErrorResult& aRv)
+{
+  if (aOutput >= NumberOfOutputs()) {
+    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
+    return;
+  }
+
+  for (int32_t outputIndex = mOutputNodes.Length() - 1; outputIndex >= 0; --outputIndex) {
+    AudioNode* dest = mOutputNodes[outputIndex];
+    for (int32_t inputIndex = dest->mInputNodes.Length() - 1; inputIndex >= 0; --inputIndex) {
+      InputNode& input = dest->mInputNodes[inputIndex];
+      if (input.mOutputPort == aOutput) {
+        DisconnectFromOutputIfConnected(*dest, outputIndex, inputIndex);
       }
     }
   }
 
-  for (int32_t i = mOutputParams.Length() - 1; i >= 0; --i) {
-    AudioParam* dest = mOutputParams[i];
-    for (int32_t j = dest->InputNodes().Length() - 1; j >= 0; --j) {
-      const InputNode& input = dest->InputNodes()[j];
-      if (input.mInputNode == this && input.mOutputPort == aOutput) {
-        dest->RemoveInputNode(j);
-        // Remove one instance of 'dest' from mOutputParams. There could be
-        // others, and it's not correct to remove them all since some of them
-        // could be for different output ports.
-        mOutputParams.RemoveElementAt(i);
-        break;
+  for (int32_t outputIndex = mOutputParams.Length() - 1; outputIndex >= 0; --outputIndex) {
+    AudioParam* dest = mOutputParams[outputIndex];
+    for (int32_t inputIndex = dest->InputNodes().Length() - 1; inputIndex >= 0; --inputIndex) {
+      const InputNode& input = dest->InputNodes()[inputIndex];
+      if (input.mOutputPort == aOutput) {
+        DisconnectFromParamIfConnected(*dest, outputIndex, inputIndex);
       }
     }
   }
 
   // This disconnection may have disconnected a panner and a source.
   Context()->UpdatePannerSource();
 }
 
 void
+AudioNode::Disconnect(AudioNode& aDestination, ErrorResult& aRv)
+{
+  bool wasConnected = false;
+
+  size_t outputIndex = mOutputNodes.IndexOf(&aDestination);
+  if (outputIndex == nsTArray<InputNode>::NoIndex) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
+    return;
+  }
+  for (int32_t inputIndex = aDestination.mInputNodes.Length() - 1; inputIndex >= 0; --inputIndex) {
+    wasConnected |= DisconnectFromOutputIfConnected(aDestination, outputIndex, inputIndex);
+  }
+
+  if (!wasConnected) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
+    return;
+  }
+
+  // This disconnection may have disconnected a panner and a source.
+  Context()->UpdatePannerSource();
+}
+
+void
+AudioNode::Disconnect(AudioNode& aDestination, uint32_t aOutput, ErrorResult& aRv)
+{
+  if (aOutput >= NumberOfOutputs()) {
+    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
+    return;
+  }
+
+  bool wasConnected = false;
+
+  for (int32_t outputIndex = mOutputNodes.Length() - 1; outputIndex >= 0; --outputIndex) {
+    for (int32_t inputIndex = aDestination.mInputNodes.Length() - 1; inputIndex >= 0; --inputIndex) {
+      InputNode& input = aDestination.mInputNodes[inputIndex];
+      if (input.mOutputPort == aOutput) {
+        wasConnected |= DisconnectFromOutputIfConnected(aDestination, outputIndex, inputIndex);
+      }
+    }
+  }
+
+  if (!wasConnected) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
+    return;
+  }
+
+  // This disconnection may have disconnected a panner and a source.
+  Context()->UpdatePannerSource();
+}
+
+void
+AudioNode::Disconnect(AudioNode& aDestination, uint32_t aOutput, uint32_t aInput, ErrorResult& aRv)
+{
+  if (aOutput >= NumberOfOutputs()) {
+    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
+    return;
+  }
+
+  if (aInput >= aDestination.NumberOfInputs()) {
+    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
+    return;
+  }
+
+  bool wasConnected = false;
+
+  for (int32_t outputIndex = mOutputNodes.Length() - 1; outputIndex >= 0; --outputIndex) {
+    for (int32_t inputIndex = aDestination.mInputNodes.Length() - 1; inputIndex >= 0; --inputIndex) {
+      InputNode& input = aDestination.mInputNodes[inputIndex];
+      if (input.mOutputPort == aOutput && input.mInputPort == aInput) {
+        wasConnected |= DisconnectFromOutputIfConnected(aDestination, outputIndex, inputIndex);
+        break;
+      }
+    }
+  }
+
+  if (!wasConnected) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
+    return;
+  }
+
+  // This disconnection may have disconnected a panner and a source.
+  Context()->UpdatePannerSource();
+}
+
+void
+AudioNode::Disconnect(AudioParam& aDestination, ErrorResult& aRv)
+{
+  bool wasConnected = false;
+
+  for (int32_t outputIndex = mOutputParams.Length() - 1; outputIndex >= 0; --outputIndex) {
+    for (int32_t inputIndex = aDestination.InputNodes().Length() - 1; inputIndex >= 0; --inputIndex) {
+        wasConnected |= DisconnectFromParamIfConnected(aDestination, outputIndex, inputIndex);
+    }
+  }
+
+  if (!wasConnected) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
+    return;
+  }
+}
+
+void
+AudioNode::Disconnect(AudioParam& aDestination, uint32_t aOutput, ErrorResult& aRv)
+{
+  if (aOutput >= NumberOfOutputs()) {
+    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
+    return;
+  }
+
+  bool wasConnected = false;
+
+  for (int32_t outputIndex = mOutputParams.Length() - 1; outputIndex >= 0; --outputIndex) {
+    for (int32_t inputIndex = aDestination.InputNodes().Length() - 1; inputIndex >= 0; --inputIndex) {
+      const InputNode& input = aDestination.InputNodes()[inputIndex];
+      if (input.mOutputPort == aOutput) {
+        wasConnected |= DisconnectFromParamIfConnected(aDestination, outputIndex, inputIndex);
+      }
+    }
+  }
+
+  if (!wasConnected) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
+    return;
+  }
+}
+
+void
 AudioNode::DestroyMediaStream()
 {
   if (mStream) {
     // Remove the node pointer on the engine.
     AudioNodeStream* ns = mStream;
     MOZ_ASSERT(ns, "How come we don't have a stream here?");
     MOZ_ASSERT(ns->Engine()->NodeMainThread() == this,
                "Invalid node reference");
--- a/dom/media/webaudio/AudioNode.h
+++ b/dom/media/webaudio/AudioNode.h
@@ -89,17 +89,27 @@ public:
   }
 
   virtual AudioNode* Connect(AudioNode& aDestination, uint32_t aOutput,
                              uint32_t aInput, ErrorResult& aRv);
 
   virtual void Connect(AudioParam& aDestination, uint32_t aOutput,
                        ErrorResult& aRv);
 
+  virtual void Disconnect(ErrorResult& aRv);
   virtual void Disconnect(uint32_t aOutput, ErrorResult& aRv);
+  virtual void Disconnect(AudioNode& aDestination, ErrorResult& aRv);
+  virtual void Disconnect(AudioNode& aDestination, uint32_t aOutput,
+                          ErrorResult& aRv);
+  virtual void Disconnect(AudioNode& aDestination,
+                          uint32_t aOutput, uint32_t aInput,
+                          ErrorResult& aRv);
+  virtual void Disconnect(AudioParam& aDestination, ErrorResult& aRv);
+  virtual void Disconnect(AudioParam& aDestination, uint32_t aOutput,
+                          ErrorResult& aRv);
 
   // Called after input nodes have been explicitly added or removed through
   // the Connect() or Disconnect() methods.
   virtual void NotifyInputsChanged() {}
   // Indicate that the node should continue indefinitely to behave as if an
   // input is connected, even though there is no longer a corresponding entry
   // in mInputNodes.  Called after an input node has been removed because it
   // is being garbage collected.
@@ -213,16 +223,18 @@ private:
   virtual void LastRelease() override
   {
     // We are about to be deleted, disconnect the object from the graph before
     // the derived type is destroyed.
     DisconnectFromGraph();
   }
   // Callers must hold a reference to 'this'.
   void DisconnectFromGraph();
+  bool DisconnectFromOutputIfConnected(AudioNode& aDestination, uint32_t aOutputIndex, uint32_t aInputIndex);
+  bool DisconnectFromParamIfConnected(AudioParam& aDestination, uint32_t aOutputIndex, uint32_t aInputIndex);
 
 protected:
   // Helpers for sending different value types to streams
   void SendDoubleParameterToStream(uint32_t aIndex, double aValue);
   void SendInt32ParameterToStream(uint32_t aIndex, int32_t aValue);
   void SendThreeDPointParameterToStream(uint32_t aIndex, const ThreeDPoint& aValue);
   void SendChannelMixingParametersToStream();
 
--- a/dom/media/webaudio/ScriptProcessorNode.h
+++ b/dom/media/webaudio/ScriptProcessorNode.h
@@ -45,35 +45,61 @@ public:
   void Connect(AudioParam& aDestination, uint32_t aOutput,
                ErrorResult& aRv) override
   {
     AudioNode::Connect(aDestination, aOutput, aRv);
     if (!aRv.Failed()) {
       UpdateConnectedStatus();
     }
   }
-
+  void Disconnect(ErrorResult& aRv) override
+  {
+    AudioNode::Disconnect(aRv);
+    UpdateConnectedStatus();
+  }
   void Disconnect(uint32_t aOutput, ErrorResult& aRv) override
   {
     AudioNode::Disconnect(aOutput, aRv);
-    if (!aRv.Failed()) {
-      UpdateConnectedStatus();
-    }
+    UpdateConnectedStatus();
   }
   void NotifyInputsChanged() override
   {
     UpdateConnectedStatus();
   }
   void NotifyHasPhantomInput() override
   {
     mHasPhantomInput = true;
     // No need to UpdateConnectedStatus() because there was previously an
     // input in InputNodes().
   }
-
+  void Disconnect(AudioNode& aDestination, ErrorResult& aRv) override
+  {
+    AudioNode::Disconnect(aDestination, aRv);
+    UpdateConnectedStatus();
+  }
+  void Disconnect(AudioNode& aDestination, uint32_t aOutput, ErrorResult& aRv) override
+  {
+    AudioNode::Disconnect(aDestination, aOutput, aRv);
+    UpdateConnectedStatus();
+  }
+  void Disconnect(AudioNode& aDestination, uint32_t aOutput, uint32_t aInput, ErrorResult& aRv) override
+  {
+    AudioNode::Disconnect(aDestination, aOutput, aInput, aRv);
+    UpdateConnectedStatus();
+  }
+  void Disconnect(AudioParam& aDestination, ErrorResult& aRv) override
+  {
+    AudioNode::Disconnect(aDestination, aRv);
+    UpdateConnectedStatus();
+  }
+  void Disconnect(AudioParam& aDestination, uint32_t aOutput, ErrorResult& aRv) override
+  {
+    AudioNode::Disconnect(aDestination, aOutput, aRv);
+    UpdateConnectedStatus();
+  }
   void SetChannelCount(uint32_t aChannelCount, ErrorResult& aRv) override
   {
     if (aChannelCount != ChannelCount()) {
       aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
     }
     return;
   }
   void SetChannelCountModeValue(ChannelCountMode aMode, ErrorResult& aRv) override
--- a/dom/webidl/AudioNode.webidl
+++ b/dom/webidl/AudioNode.webidl
@@ -23,17 +23,29 @@ enum ChannelInterpretation {
 
 interface AudioNode : EventTarget {
 
     [Throws]
     AudioNode connect(AudioNode destination, optional unsigned long output = 0, optional unsigned long input = 0);
     [Throws]
     void connect(AudioParam destination, optional unsigned long output = 0);
     [Throws]
-    void disconnect(optional unsigned long output = 0);
+    void disconnect();
+    [Throws]
+    void disconnect(unsigned long output);
+    [Throws]
+    void disconnect(AudioNode destination);
+    [Throws]
+    void disconnect(AudioNode destination, unsigned long output);
+    [Throws]
+    void disconnect(AudioNode destination, unsigned long output, unsigned long input);
+    [Throws]
+    void disconnect(AudioParam destination);
+    [Throws]
+    void disconnect(AudioParam destination, unsigned long output);
 
     readonly attribute AudioContext context;
     readonly attribute unsigned long numberOfInputs;
     readonly attribute unsigned long numberOfOutputs;
 
     // Channel up-mixing and down-mixing rules for all inputs.
     [SetterThrows]
     attribute unsigned long channelCount;