Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/webaudio/AudioNode.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim:set ts=2 sw=2 sts=2 et cindent: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "AudioNode.h"
8
#include "mozilla/ErrorResult.h"
9
#include "AudioNodeStream.h"
10
#include "AudioNodeEngine.h"
11
#include "mozilla/dom/AudioParam.h"
12
#include "mozilla/Services.h"
13
#include "nsIObserverService.h"
14
15
namespace mozilla {
16
namespace dom {
17
18
static const uint32_t INVALID_PORT = 0xffffffff;
19
static uint32_t gId = 0;
20
21
NS_IMPL_CYCLE_COLLECTION_CLASS(AudioNode)
22
23
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(AudioNode, DOMEventTargetHelper)
24
0
  tmp->DisconnectFromGraph();
25
0
  if (tmp->mContext) {
26
0
    tmp->mContext->UnregisterNode(tmp);
27
0
  }
28
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext)
29
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mOutputNodes)
30
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mOutputParams)
31
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
32
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(AudioNode,
33
0
                                                  DOMEventTargetHelper)
34
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext)
35
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOutputNodes)
36
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOutputParams)
37
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
38
39
NS_IMPL_ADDREF_INHERITED(AudioNode, DOMEventTargetHelper)
40
NS_IMPL_RELEASE_INHERITED(AudioNode, DOMEventTargetHelper)
41
42
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AudioNode)
43
0
  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
44
0
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
45
46
AudioNode::AudioNode(AudioContext* aContext,
47
                     uint32_t aChannelCount,
48
                     ChannelCountMode aChannelCountMode,
49
                     ChannelInterpretation aChannelInterpretation)
50
  : DOMEventTargetHelper(aContext->GetParentObject())
51
  , mContext(aContext)
52
  , mChannelCount(aChannelCount)
53
  , mChannelCountMode(aChannelCountMode)
54
  , mChannelInterpretation(aChannelInterpretation)
55
  , mId(gId++)
56
  , mPassThrough(false)
57
  , mAbstractMainThread(aContext->GetOwnerGlobal()->AbstractMainThreadFor(TaskCategory::Other))
58
0
{
59
0
  MOZ_ASSERT(aContext);
60
0
  aContext->RegisterNode(this);
61
0
}
62
63
AudioNode::~AudioNode()
64
0
{
65
0
  MOZ_ASSERT(mInputNodes.IsEmpty());
66
0
  MOZ_ASSERT(mOutputNodes.IsEmpty());
67
0
  MOZ_ASSERT(mOutputParams.IsEmpty());
68
0
  MOZ_ASSERT(!mStream,
69
0
             "The webaudio-node-demise notification must have been sent");
70
0
  if (mContext) {
71
0
    mContext->UnregisterNode(this);
72
0
  }
73
0
}
74
75
void
76
AudioNode::Initialize(const AudioNodeOptions& aOptions, ErrorResult& aRv)
77
0
{
78
0
  if (aOptions.mChannelCount.WasPassed()) {
79
0
    SetChannelCount(aOptions.mChannelCount.Value(), aRv);
80
0
    if (NS_WARN_IF(aRv.Failed())) {
81
0
      return;
82
0
    }
83
0
  }
84
0
85
0
  if (aOptions.mChannelCountMode.WasPassed()) {
86
0
    SetChannelCountModeValue(aOptions.mChannelCountMode.Value(), aRv);
87
0
    if (NS_WARN_IF(aRv.Failed())) {
88
0
      return;
89
0
    }
90
0
  }
91
0
92
0
  if (aOptions.mChannelInterpretation.WasPassed()) {
93
0
    SetChannelInterpretationValue(aOptions.mChannelInterpretation.Value(), aRv);
94
0
    if (NS_WARN_IF(aRv.Failed())) {
95
0
      return;
96
0
    }
97
0
  }
98
0
}
99
100
size_t
101
AudioNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
102
0
{
103
0
  // Not owned:
104
0
  // - mContext
105
0
  // - mStream
106
0
  size_t amount = 0;
107
0
108
0
  amount += mInputNodes.ShallowSizeOfExcludingThis(aMallocSizeOf);
109
0
  for (size_t i = 0; i < mInputNodes.Length(); i++) {
110
0
    amount += mInputNodes[i].SizeOfExcludingThis(aMallocSizeOf);
111
0
  }
112
0
113
0
  // Just measure the array. The entire audio node graph is measured via the
114
0
  // MediaStreamGraph's streams, so we don't want to double-count the elements.
115
0
  amount += mOutputNodes.ShallowSizeOfExcludingThis(aMallocSizeOf);
116
0
117
0
  amount += mOutputParams.ShallowSizeOfExcludingThis(aMallocSizeOf);
118
0
  for (size_t i = 0; i < mOutputParams.Length(); i++) {
119
0
    amount += mOutputParams[i]->SizeOfIncludingThis(aMallocSizeOf);
120
0
  }
121
0
122
0
  return amount;
123
0
}
124
125
size_t
126
AudioNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
127
0
{
128
0
  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
129
0
}
130
131
template <class InputNode>
132
static size_t
133
FindIndexOfNode(const nsTArray<InputNode>& aInputNodes, const AudioNode* aNode)
134
0
{
135
0
  for (size_t i = 0; i < aInputNodes.Length(); ++i) {
136
0
    if (aInputNodes[i].mInputNode == aNode) {
137
0
      return i;
138
0
    }
139
0
  }
140
0
  return nsTArray<InputNode>::NoIndex;
141
0
}
142
143
template <class InputNode>
144
static size_t
145
FindIndexOfNodeWithPorts(const nsTArray<InputNode>& aInputNodes,
146
                         const AudioNode* aNode,
147
                         uint32_t aInputPort, uint32_t aOutputPort)
148
0
{
149
0
  for (size_t i = 0; i < aInputNodes.Length(); ++i) {
150
0
    if (aInputNodes[i].mInputNode == aNode &&
151
0
        aInputNodes[i].mInputPort == aInputPort &&
152
0
        aInputNodes[i].mOutputPort == aOutputPort) {
153
0
      return i;
154
0
    }
155
0
  }
156
0
  return nsTArray<InputNode>::NoIndex;
157
0
}
158
159
void
160
AudioNode::DisconnectFromGraph()
161
0
{
162
0
  MOZ_ASSERT(mRefCnt.get() > mInputNodes.Length(),
163
0
             "Caller should be holding a reference");
164
0
165
0
  // The idea here is that we remove connections one by one, and at each step
166
0
  // the graph is in a valid state.
167
0
168
0
  // Disconnect inputs. We don't need them anymore.
169
0
  while (!mInputNodes.IsEmpty()) {
170
0
    size_t i = mInputNodes.Length() - 1;
171
0
    RefPtr<AudioNode> input = mInputNodes[i].mInputNode;
172
0
    mInputNodes.RemoveElementAt(i);
173
0
    input->mOutputNodes.RemoveElement(this);
174
0
  }
175
0
176
0
  while (!mOutputNodes.IsEmpty()) {
177
0
    size_t i = mOutputNodes.Length() - 1;
178
0
    RefPtr<AudioNode> output = mOutputNodes[i].forget();
179
0
    mOutputNodes.RemoveElementAt(i);
180
0
    size_t inputIndex = FindIndexOfNode(output->mInputNodes, this);
181
0
    // It doesn't matter which one we remove, since we're going to remove all
182
0
    // entries for this node anyway.
183
0
    output->mInputNodes.RemoveElementAt(inputIndex);
184
0
    // This effects of this connection will remain.
185
0
    output->NotifyHasPhantomInput();
186
0
  }
187
0
188
0
  while (!mOutputParams.IsEmpty()) {
189
0
    size_t i = mOutputParams.Length() - 1;
190
0
    RefPtr<AudioParam> output = mOutputParams[i].forget();
191
0
    mOutputParams.RemoveElementAt(i);
192
0
    size_t inputIndex = FindIndexOfNode(output->InputNodes(), this);
193
0
    // It doesn't matter which one we remove, since we're going to remove all
194
0
    // entries for this node anyway.
195
0
    output->RemoveInputNode(inputIndex);
196
0
  }
197
0
198
0
  DestroyMediaStream();
199
0
}
200
201
AudioNode*
202
AudioNode::Connect(AudioNode& aDestination, uint32_t aOutput,
203
                   uint32_t aInput, ErrorResult& aRv)
204
0
{
205
0
  if (aOutput >= NumberOfOutputs() ||
206
0
      aInput >= aDestination.NumberOfInputs()) {
207
0
    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
208
0
    return nullptr;
209
0
  }
210
0
211
0
  if (Context() != aDestination.Context()) {
212
0
    aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
213
0
    return nullptr;
214
0
  }
215
0
216
0
  if (FindIndexOfNodeWithPorts(aDestination.mInputNodes,
217
0
                               this, aInput, aOutput) !=
218
0
      nsTArray<AudioNode::InputNode>::NoIndex) {
219
0
    // connection already exists.
220
0
    return &aDestination;
221
0
  }
222
0
223
0
  WEB_AUDIO_API_LOG("%f: %s %u Connect() to %s %u",
224
0
                    Context()->CurrentTime(), NodeType(), Id(),
225
0
                    aDestination.NodeType(), aDestination.Id());
226
0
227
0
  // The MediaStreamGraph will handle cycle detection. We don't need to do it
228
0
  // here.
229
0
230
0
  mOutputNodes.AppendElement(&aDestination);
231
0
  InputNode* input = aDestination.mInputNodes.AppendElement();
232
0
  input->mInputNode = this;
233
0
  input->mInputPort = aInput;
234
0
  input->mOutputPort = aOutput;
235
0
  AudioNodeStream* destinationStream = aDestination.mStream;
236
0
  if (mStream && destinationStream) {
237
0
    // Connect streams in the MediaStreamGraph
238
0
    MOZ_ASSERT(aInput <= UINT16_MAX, "Unexpected large input port number");
239
0
    MOZ_ASSERT(aOutput <= UINT16_MAX, "Unexpected large output port number");
240
0
    input->mStreamPort = destinationStream->
241
0
      AllocateInputPort(mStream, AudioNodeStream::AUDIO_TRACK, TRACK_ANY,
242
0
                        static_cast<uint16_t>(aInput),
243
0
                        static_cast<uint16_t>(aOutput));
244
0
  }
245
0
  aDestination.NotifyInputsChanged();
246
0
247
0
  return &aDestination;
248
0
}
249
250
void
251
AudioNode::Connect(AudioParam& aDestination, uint32_t aOutput,
252
                   ErrorResult& aRv)
253
0
{
254
0
  if (aOutput >= NumberOfOutputs()) {
255
0
    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
256
0
    return;
257
0
  }
258
0
259
0
  if (Context() != aDestination.GetParentObject()) {
260
0
    aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
261
0
    return;
262
0
  }
263
0
264
0
  if (FindIndexOfNodeWithPorts(aDestination.InputNodes(),
265
0
                               this, INVALID_PORT, aOutput) !=
266
0
      nsTArray<AudioNode::InputNode>::NoIndex) {
267
0
    // connection already exists.
268
0
    return;
269
0
  }
270
0
271
0
  mOutputParams.AppendElement(&aDestination);
272
0
  InputNode* input = aDestination.AppendInputNode();
273
0
  input->mInputNode = this;
274
0
  input->mInputPort = INVALID_PORT;
275
0
  input->mOutputPort = aOutput;
276
0
277
0
  MediaStream* stream = aDestination.Stream();
278
0
  MOZ_ASSERT(stream->AsProcessedStream());
279
0
  ProcessedMediaStream* ps = static_cast<ProcessedMediaStream*>(stream);
280
0
  if (mStream) {
281
0
    // Setup our stream as an input to the AudioParam's stream
282
0
    MOZ_ASSERT(aOutput <= UINT16_MAX, "Unexpected large output port number");
283
0
    input->mStreamPort =
284
0
      ps->AllocateInputPort(mStream, AudioNodeStream::AUDIO_TRACK, TRACK_ANY,
285
0
                            0, static_cast<uint16_t>(aOutput));
286
0
  }
287
0
}
288
289
void
290
AudioNode::SendDoubleParameterToStream(uint32_t aIndex, double aValue)
291
0
{
292
0
  MOZ_ASSERT(mStream, "How come we don't have a stream here?");
293
0
  mStream->SetDoubleParameter(aIndex, aValue);
294
0
}
295
296
void
297
AudioNode::SendInt32ParameterToStream(uint32_t aIndex, int32_t aValue)
298
0
{
299
0
  MOZ_ASSERT(mStream, "How come we don't have a stream here?");
300
0
  mStream->SetInt32Parameter(aIndex, aValue);
301
0
}
302
303
void
304
AudioNode::SendThreeDPointParameterToStream(uint32_t aIndex,
305
                                            const ThreeDPoint& aValue)
306
0
{
307
0
  MOZ_ASSERT(mStream, "How come we don't have a stream here?");
308
0
  mStream->SetThreeDPointParameter(aIndex, aValue);
309
0
}
310
311
void
312
AudioNode::SendChannelMixingParametersToStream()
313
0
{
314
0
  if (mStream) {
315
0
    mStream->SetChannelMixingParameters(mChannelCount, mChannelCountMode,
316
0
                                        mChannelInterpretation);
317
0
  }
318
0
}
319
320
template<>
321
bool
322
AudioNode::DisconnectFromOutputIfConnected<AudioNode>(uint32_t aOutputNodeIndex,
323
                                                      uint32_t aInputIndex)
324
0
{
325
0
  WEB_AUDIO_API_LOG("%f: %s %u Disconnect()", Context()->CurrentTime(),
326
0
                    NodeType(), Id());
327
0
328
0
  AudioNode* destination = mOutputNodes[aOutputNodeIndex];
329
0
330
0
  MOZ_ASSERT(aOutputNodeIndex < mOutputNodes.Length());
331
0
  MOZ_ASSERT(aInputIndex < destination->InputNodes().Length());
332
0
333
0
  // An upstream node may be starting to play on the graph thread, and the
334
0
  // engine for a downstream node may be sending a PlayingRefChangeHandler
335
0
  // ADDREF message to this (main) thread.  Wait for a round trip before
336
0
  // releasing nodes, to give engines receiving sound now time to keep their
337
0
  // nodes alive.
338
0
  class RunnableRelease final : public Runnable
339
0
  {
340
0
  public:
341
0
    explicit RunnableRelease(already_AddRefed<AudioNode> aNode)
342
0
      : mozilla::Runnable("RunnableRelease")
343
0
      , mNode(aNode)
344
0
    {
345
0
    }
346
0
347
0
    NS_IMETHOD Run() override
348
0
    {
349
0
      mNode = nullptr;
350
0
      return NS_OK;
351
0
    }
352
0
  private:
353
0
    RefPtr<AudioNode> mNode;
354
0
  };
355
0
356
0
  InputNode& input = destination->mInputNodes[aInputIndex];
357
0
  if (input.mInputNode != this) {
358
0
    return false;
359
0
  }
360
0
361
0
  // Remove one instance of 'dest' from mOutputNodes. There could be
362
0
  // others, and it's not correct to remove them all since some of them
363
0
  // could be for different output ports.
364
0
  RefPtr<AudioNode> output = mOutputNodes[aOutputNodeIndex].forget();
365
0
  mOutputNodes.RemoveElementAt(aOutputNodeIndex);
366
0
  // Destroying the InputNode here sends a message to the graph thread
367
0
  // to disconnect the streams, which should be sent before the
368
0
  // RunAfterPendingUpdates() call below.
369
0
  destination->mInputNodes.RemoveElementAt(aInputIndex);
370
0
  output->NotifyInputsChanged();
371
0
  if (mStream) {
372
0
    nsCOMPtr<nsIRunnable> runnable = new RunnableRelease(output.forget());
373
0
    mStream->RunAfterPendingUpdates(runnable.forget());
374
0
  }
375
0
  return true;
376
0
}
377
378
template<>
379
bool
380
AudioNode::DisconnectFromOutputIfConnected<AudioParam>(uint32_t aOutputParamIndex,
381
                                                       uint32_t aInputIndex)
382
0
{
383
0
  MOZ_ASSERT(aOutputParamIndex < mOutputParams.Length());
384
0
385
0
  AudioParam* destination = mOutputParams[aOutputParamIndex];
386
0
387
0
  MOZ_ASSERT(aInputIndex < destination->InputNodes().Length());
388
0
389
0
  const InputNode& input = destination->InputNodes()[aInputIndex];
390
0
  if (input.mInputNode != this) {
391
0
    return false;
392
0
  }
393
0
  destination->RemoveInputNode(aInputIndex);
394
0
  // Remove one instance of 'dest' from mOutputParams. There could be
395
0
  // others, and it's not correct to remove them all since some of them
396
0
  // could be for different output ports.
397
0
  mOutputParams.RemoveElementAt(aOutputParamIndex);
398
0
  return true;
399
0
}
400
401
template<>
402
const nsTArray<AudioNode::InputNode>&
403
0
AudioNode::InputsForDestination<AudioNode>(uint32_t aOutputNodeIndex) const {
404
0
  return mOutputNodes[aOutputNodeIndex]->InputNodes();
405
0
}
406
407
template<>
408
const nsTArray<AudioNode::InputNode>&
409
0
AudioNode::InputsForDestination<AudioParam>(uint32_t aOutputNodeIndex) const {
410
0
  return mOutputParams[aOutputNodeIndex]->InputNodes();
411
0
}
412
413
template<typename DestinationType, typename Predicate>
414
bool
415
AudioNode::DisconnectMatchingDestinationInputs(uint32_t aDestinationIndex,
416
                                               Predicate aPredicate)
417
0
{
418
0
  bool wasConnected = false;
419
0
  uint32_t inputCount =
420
0
    InputsForDestination<DestinationType>(aDestinationIndex).Length();
421
0
422
0
  for (int32_t inputIndex = inputCount - 1; inputIndex >= 0; --inputIndex) {
423
0
    const InputNode& input =
424
0
      InputsForDestination<DestinationType>(aDestinationIndex)[inputIndex];
425
0
    if (aPredicate(input)) {
426
0
      if (DisconnectFromOutputIfConnected<DestinationType>(aDestinationIndex,
427
0
                                                           inputIndex)) {
428
0
        wasConnected = true;
429
0
        break;
430
0
      }
431
0
    }
432
0
  }
433
0
  return wasConnected;
434
0
}
Unexecuted instantiation: Unified_cpp_dom_media_webaudio0.cpp:bool mozilla::dom::AudioNode::DisconnectMatchingDestinationInputs<mozilla::dom::AudioNode, mozilla::dom::AudioNode::Disconnect(mozilla::ErrorResult&)::$_5>(unsigned int, mozilla::dom::AudioNode::Disconnect(mozilla::ErrorResult&)::$_5)
Unexecuted instantiation: Unified_cpp_dom_media_webaudio0.cpp:bool mozilla::dom::AudioNode::DisconnectMatchingDestinationInputs<mozilla::dom::AudioParam, mozilla::dom::AudioNode::Disconnect(mozilla::ErrorResult&)::$_6>(unsigned int, mozilla::dom::AudioNode::Disconnect(mozilla::ErrorResult&)::$_6)
Unexecuted instantiation: Unified_cpp_dom_media_webaudio0.cpp:bool mozilla::dom::AudioNode::DisconnectMatchingDestinationInputs<mozilla::dom::AudioNode, mozilla::dom::AudioNode::Disconnect(unsigned int, mozilla::ErrorResult&)::$_7>(unsigned int, mozilla::dom::AudioNode::Disconnect(unsigned int, mozilla::ErrorResult&)::$_7)
Unexecuted instantiation: Unified_cpp_dom_media_webaudio0.cpp:bool mozilla::dom::AudioNode::DisconnectMatchingDestinationInputs<mozilla::dom::AudioParam, mozilla::dom::AudioNode::Disconnect(unsigned int, mozilla::ErrorResult&)::$_8>(unsigned int, mozilla::dom::AudioNode::Disconnect(unsigned int, mozilla::ErrorResult&)::$_8)
Unexecuted instantiation: Unified_cpp_dom_media_webaudio0.cpp:bool mozilla::dom::AudioNode::DisconnectMatchingDestinationInputs<mozilla::dom::AudioNode, mozilla::dom::AudioNode::Disconnect(mozilla::dom::AudioNode&, mozilla::ErrorResult&)::$_9>(unsigned int, mozilla::dom::AudioNode::Disconnect(mozilla::dom::AudioNode&, mozilla::ErrorResult&)::$_9)
Unexecuted instantiation: Unified_cpp_dom_media_webaudio0.cpp:bool mozilla::dom::AudioNode::DisconnectMatchingDestinationInputs<mozilla::dom::AudioNode, mozilla::dom::AudioNode::Disconnect(mozilla::dom::AudioNode&, unsigned int, mozilla::ErrorResult&)::$_10>(unsigned int, mozilla::dom::AudioNode::Disconnect(mozilla::dom::AudioNode&, unsigned int, mozilla::ErrorResult&)::$_10)
Unexecuted instantiation: Unified_cpp_dom_media_webaudio0.cpp:bool mozilla::dom::AudioNode::DisconnectMatchingDestinationInputs<mozilla::dom::AudioNode, mozilla::dom::AudioNode::Disconnect(mozilla::dom::AudioNode&, unsigned int, unsigned int, mozilla::ErrorResult&)::$_11>(unsigned int, mozilla::dom::AudioNode::Disconnect(mozilla::dom::AudioNode&, unsigned int, unsigned int, mozilla::ErrorResult&)::$_11)
Unexecuted instantiation: Unified_cpp_dom_media_webaudio0.cpp:bool mozilla::dom::AudioNode::DisconnectMatchingDestinationInputs<mozilla::dom::AudioParam, mozilla::dom::AudioNode::Disconnect(mozilla::dom::AudioParam&, mozilla::ErrorResult&)::$_12>(unsigned int, mozilla::dom::AudioNode::Disconnect(mozilla::dom::AudioParam&, mozilla::ErrorResult&)::$_12)
Unexecuted instantiation: Unified_cpp_dom_media_webaudio0.cpp:bool mozilla::dom::AudioNode::DisconnectMatchingDestinationInputs<mozilla::dom::AudioParam, mozilla::dom::AudioNode::Disconnect(mozilla::dom::AudioParam&, unsigned int, mozilla::ErrorResult&)::$_13>(unsigned int, mozilla::dom::AudioNode::Disconnect(mozilla::dom::AudioParam&, unsigned int, mozilla::ErrorResult&)::$_13)
435
436
void
437
AudioNode::Disconnect(ErrorResult& aRv)
438
0
{
439
0
  for (int32_t outputIndex = mOutputNodes.Length() - 1;
440
0
       outputIndex >= 0; --outputIndex) {
441
0
    DisconnectMatchingDestinationInputs<AudioNode>(outputIndex,
442
0
                                                   [](const InputNode&) {
443
0
                                                     return true;
444
0
                                                   });
445
0
  }
446
0
447
0
  for (int32_t outputIndex = mOutputParams.Length() - 1;
448
0
       outputIndex >= 0; --outputIndex) {
449
0
    DisconnectMatchingDestinationInputs<AudioParam>(outputIndex,
450
0
                                                    [](const InputNode&) {
451
0
                                                      return true;
452
0
                                                    });
453
0
  }
454
0
}
455
456
void
457
AudioNode::Disconnect(uint32_t aOutput, ErrorResult& aRv)
458
0
{
459
0
  if (aOutput >= NumberOfOutputs()) {
460
0
    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
461
0
    return;
462
0
  }
463
0
464
0
  for (int32_t outputIndex = mOutputNodes.Length() - 1;
465
0
       outputIndex >= 0; --outputIndex) {
466
0
    DisconnectMatchingDestinationInputs<AudioNode>(
467
0
        outputIndex,
468
0
        [aOutput](const InputNode& aInputNode) {
469
0
          return aInputNode.mOutputPort == aOutput;
470
0
        });
471
0
  }
472
0
473
0
  for (int32_t outputIndex = mOutputParams.Length() - 1;
474
0
       outputIndex >= 0; --outputIndex) {
475
0
    DisconnectMatchingDestinationInputs<AudioParam>(
476
0
        outputIndex,
477
0
        [aOutput](const InputNode& aInputNode) {
478
0
          return aInputNode.mOutputPort == aOutput;
479
0
        });
480
0
  }
481
0
}
482
483
void
484
AudioNode::Disconnect(AudioNode& aDestination, ErrorResult& aRv)
485
0
{
486
0
  bool wasConnected = false;
487
0
488
0
  for (int32_t outputIndex = mOutputNodes.Length() - 1;
489
0
       outputIndex >= 0; --outputIndex) {
490
0
    if (mOutputNodes[outputIndex] != &aDestination) {
491
0
      continue;
492
0
    }
493
0
    wasConnected |=
494
0
      DisconnectMatchingDestinationInputs<AudioNode>(outputIndex,
495
0
                                                     [](const InputNode&) {
496
0
                                                       return true;
497
0
                                                     });
498
0
  }
499
0
500
0
  if (!wasConnected) {
501
0
    aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
502
0
    return;
503
0
  }
504
0
}
505
506
void
507
AudioNode::Disconnect(AudioNode& aDestination,
508
                      uint32_t aOutput,
509
                      ErrorResult& aRv)
510
0
{
511
0
  if (aOutput >= NumberOfOutputs()) {
512
0
    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
513
0
    return;
514
0
  }
515
0
516
0
  bool wasConnected = false;
517
0
518
0
  for (int32_t outputIndex = mOutputNodes.Length() - 1;
519
0
       outputIndex >= 0; --outputIndex) {
520
0
    if (mOutputNodes[outputIndex] != &aDestination) {
521
0
      continue;
522
0
    }
523
0
    wasConnected |=
524
0
      DisconnectMatchingDestinationInputs<AudioNode>(
525
0
          outputIndex,
526
0
          [aOutput](const InputNode& aInputNode) {
527
0
            return aInputNode.mOutputPort == aOutput;
528
0
          });
529
0
  }
530
0
531
0
  if (!wasConnected) {
532
0
    aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
533
0
    return;
534
0
  }
535
0
}
536
537
void
538
AudioNode::Disconnect(AudioNode& aDestination,
539
                      uint32_t aOutput,
540
                      uint32_t aInput,
541
                      ErrorResult& aRv)
542
0
{
543
0
  if (aOutput >= NumberOfOutputs()) {
544
0
    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
545
0
    return;
546
0
  }
547
0
548
0
  if (aInput >= aDestination.NumberOfInputs()) {
549
0
    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
550
0
    return;
551
0
  }
552
0
553
0
  bool wasConnected = false;
554
0
555
0
  for (int32_t outputIndex = mOutputNodes.Length() - 1;
556
0
       outputIndex >= 0; --outputIndex) {
557
0
    if (mOutputNodes[outputIndex] != &aDestination) {
558
0
      continue;
559
0
    }
560
0
    wasConnected |=
561
0
      DisconnectMatchingDestinationInputs<AudioNode>(
562
0
          outputIndex,
563
0
          [aOutput, aInput](const InputNode& aInputNode) {
564
0
            return aInputNode.mOutputPort == aOutput &&
565
0
                   aInputNode.mInputPort == aInput;
566
0
          });
567
0
  }
568
0
569
0
  if (!wasConnected) {
570
0
    aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
571
0
    return;
572
0
  }
573
0
}
574
575
void
576
AudioNode::Disconnect(AudioParam& aDestination, ErrorResult& aRv)
577
0
{
578
0
  bool wasConnected = false;
579
0
580
0
  for (int32_t outputIndex = mOutputParams.Length() - 1;
581
0
       outputIndex >= 0; --outputIndex) {
582
0
    if (mOutputParams[outputIndex] != &aDestination) {
583
0
      continue;
584
0
    }
585
0
    wasConnected |=
586
0
      DisconnectMatchingDestinationInputs<AudioParam>(outputIndex,
587
0
                                                      [](const InputNode&) {
588
0
                                                        return true;
589
0
                                                      });
590
0
  }
591
0
592
0
  if (!wasConnected) {
593
0
    aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
594
0
    return;
595
0
  }
596
0
}
597
598
void
599
AudioNode::Disconnect(AudioParam& aDestination,
600
                      uint32_t aOutput,
601
                      ErrorResult& aRv)
602
0
{
603
0
  if (aOutput >= NumberOfOutputs()) {
604
0
    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
605
0
    return;
606
0
  }
607
0
608
0
  bool wasConnected = false;
609
0
610
0
  for (int32_t outputIndex = mOutputParams.Length() - 1;
611
0
       outputIndex >= 0; --outputIndex) {
612
0
    if (mOutputParams[outputIndex] != &aDestination) {
613
0
      continue;
614
0
    }
615
0
    wasConnected |=
616
0
      DisconnectMatchingDestinationInputs<AudioParam>(
617
0
          outputIndex,
618
0
          [aOutput](const InputNode& aInputNode) {
619
0
            return aInputNode.mOutputPort == aOutput;
620
0
          });
621
0
  }
622
0
623
0
  if (!wasConnected) {
624
0
    aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
625
0
    return;
626
0
  }
627
0
}
628
629
void
630
AudioNode::DestroyMediaStream()
631
0
{
632
0
  if (mStream) {
633
0
    // Remove the node pointer on the engine.
634
0
    AudioNodeStream* ns = mStream;
635
0
    MOZ_ASSERT(ns, "How come we don't have a stream here?");
636
0
    MOZ_ASSERT(ns->Engine()->NodeMainThread() == this,
637
0
               "Invalid node reference");
638
0
    ns->Engine()->ClearNode();
639
0
640
0
    mStream->Destroy();
641
0
    mStream = nullptr;
642
0
643
0
    nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
644
0
    if (obs) {
645
0
      nsAutoString id;
646
0
      id.AppendPrintf("%u", mId);
647
0
      obs->NotifyObservers(nullptr, "webaudio-node-demise", id.get());
648
0
    }
649
0
  }
650
0
}
651
652
void
653
AudioNode::RemoveOutputParam(AudioParam* aParam)
654
0
{
655
0
  mOutputParams.RemoveElement(aParam);
656
0
}
657
658
bool
659
AudioNode::PassThrough() const
660
0
{
661
0
  MOZ_ASSERT(NumberOfInputs() <= 1 && NumberOfOutputs() == 1);
662
0
  return mPassThrough;
663
0
}
664
665
void
666
AudioNode::SetPassThrough(bool aPassThrough)
667
0
{
668
0
  MOZ_ASSERT(NumberOfInputs() <= 1 && NumberOfOutputs() == 1);
669
0
  mPassThrough = aPassThrough;
670
0
  if (mStream) {
671
0
    mStream->SetPassThrough(mPassThrough);
672
0
  }
673
0
}
674
675
} // namespace dom
676
} // namespace mozilla