Bug 1130010: Implement the new AudioNode.disconnect methods. r?dminor
Initial patch by Thomas Escalon <tesc.bugzilla@gmail.com>.
MozReview-Commit-ID: KDnmKIGWYL
--- 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;