Bug 1130010: Implement the new AudioNode.disconnect methods. r?dminor draft
authorPaul Adenot <paul@paul.cx>
Fri, 05 Jun 2015 11:17:14 +0200
changeset 386259 656a3fea62421b48ead8d2d1399742e7bf5d7d75
parent 385002 4764b9f8e6d4ef9823237f01ca3901759ce8daeb
child 386260 c263054f766ed826819e17b7ded8c4b15bbbecbb
push id22654
push userpaul@paul.cx
push dateMon, 11 Jul 2016 13:45:44 +0000
reviewersdminor
bugs1130010
milestone50.0a1
Bug 1130010: Implement the new AudioNode.disconnect methods. r?dminor 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,229 @@ 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 isConnected = 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) {
+    isConnected |= DisconnectFromOutputIfConnected(aDestination, outputIndex, inputIndex);
+  }
+  MOZ_ASSERT(isConnected);
+
+  // 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 isConnected = 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) {
+        isConnected |= DisconnectFromOutputIfConnected(aDestination, outputIndex, inputIndex);
+      }
+    }
+  }
+
+  if (!isConnected) {
+    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 isConnected = 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) {
+        isConnected |= DisconnectFromOutputIfConnected(aDestination, outputIndex, inputIndex);
+        break;
+      }
+    }
+  }
+
+  if (!isConnected) {
+    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 isConnected = false;
+
+  for (int32_t outputIndex = mOutputParams.Length() - 1; outputIndex >= 0; --outputIndex) {
+    for (int32_t inputIndex = aDestination.InputNodes().Length() - 1; inputIndex >= 0; --inputIndex) {
+        isConnected |= DisconnectFromParamIfConnected(aDestination, outputIndex, inputIndex);
+    }
+  }
+
+  if (!isConnected) {
+    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 isConnected = 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) {
+        isConnected |= DisconnectFromParamIfConnected(aDestination, outputIndex, inputIndex);
+      }
+    }
+  }
+
+  if (!isConnected) {
+    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,64 @@ public:
   void Connect(AudioParam& aDestination, uint32_t aOutput,
                ErrorResult& aRv) override
   {
     AudioNode::Connect(aDestination, aOutput, aRv);
     if (!aRv.Failed()) {
       UpdateConnectedStatus();
     }
   }
-
+  void Disconnect(ErrorResult& aRv)
+  {
+    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();
+  }
+virtual void Disconnect(AudioParam& aDestination, ErrorResult& aRv) override
+  {
+    AudioNode::Disconnect(aDestination, aRv);
+    UpdateConnectedStatus();
+  }
+
+  virtual 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;