Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/webaudio/MediaStreamAudioSourceNode.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 "MediaStreamAudioSourceNode.h"
8
#include "mozilla/dom/MediaStreamAudioSourceNodeBinding.h"
9
#include "AudioNodeEngine.h"
10
#include "AudioNodeExternalInputStream.h"
11
#include "AudioStreamTrack.h"
12
#include "nsIDocument.h"
13
#include "mozilla/CORSMode.h"
14
#include "nsContentUtils.h"
15
#include "nsIScriptError.h"
16
17
namespace mozilla {
18
namespace dom {
19
20
NS_IMPL_CYCLE_COLLECTION_CLASS(MediaStreamAudioSourceNode)
21
22
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(MediaStreamAudioSourceNode)
23
0
  tmp->Destroy();
24
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mInputStream)
25
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mInputTrack)
26
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(AudioNode)
27
28
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MediaStreamAudioSourceNode, AudioNode)
29
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInputStream)
30
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInputTrack)
31
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
32
33
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaStreamAudioSourceNode)
34
0
NS_INTERFACE_MAP_END_INHERITING(AudioNode)
35
36
NS_IMPL_ADDREF_INHERITED(MediaStreamAudioSourceNode, AudioNode)
37
NS_IMPL_RELEASE_INHERITED(MediaStreamAudioSourceNode, AudioNode)
38
39
MediaStreamAudioSourceNode::MediaStreamAudioSourceNode(AudioContext* aContext)
40
  : AudioNode(aContext,
41
              2,
42
              ChannelCountMode::Max,
43
              ChannelInterpretation::Speakers)
44
0
{
45
0
}
46
47
/* static */ already_AddRefed<MediaStreamAudioSourceNode>
48
MediaStreamAudioSourceNode::Create(AudioContext& aAudioContext,
49
                                   const MediaStreamAudioSourceOptions& aOptions,
50
                                   ErrorResult& aRv)
51
0
{
52
0
  if (aAudioContext.IsOffline()) {
53
0
    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
54
0
    return nullptr;
55
0
  }
56
0
57
0
  if (aAudioContext.CheckClosed(aRv)) {
58
0
    return nullptr;
59
0
  }
60
0
61
0
  if (aAudioContext.Graph() != aOptions.mMediaStream->GetPlaybackStream()->Graph()) {
62
0
    nsCOMPtr<nsPIDOMWindowInner> pWindow = aAudioContext.GetParentObject();
63
0
    nsIDocument* document = pWindow ? pWindow->GetExtantDoc() : nullptr;
64
0
    nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
65
0
                                    NS_LITERAL_CSTRING("Web Audio"),
66
0
                                    document,
67
0
                                    nsContentUtils::eDOM_PROPERTIES,
68
0
                                    "MediaStreamAudioSourceNodeDifferentRate");
69
0
    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
70
0
    return nullptr;
71
0
  }
72
0
73
0
  RefPtr<MediaStreamAudioSourceNode> node =
74
0
    new MediaStreamAudioSourceNode(&aAudioContext);
75
0
76
0
  node->Init(aOptions.mMediaStream, aRv);
77
0
  if (aRv.Failed()) {
78
0
    return nullptr;
79
0
  }
80
0
81
0
  return node.forget();
82
0
}
83
84
void
85
MediaStreamAudioSourceNode::Init(DOMMediaStream* aMediaStream, ErrorResult& aRv)
86
0
{
87
0
  if (!aMediaStream) {
88
0
    aRv.Throw(NS_ERROR_FAILURE);
89
0
    return;
90
0
  }
91
0
92
0
  MediaStream* inputStream = aMediaStream->GetPlaybackStream();
93
0
  MediaStreamGraph* graph = Context()->Graph();
94
0
  if (NS_WARN_IF(graph != inputStream->Graph())) {
95
0
    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
96
0
    return;
97
0
  }
98
0
99
0
  mInputStream = aMediaStream;
100
0
  AudioNodeEngine* engine = new MediaStreamAudioSourceNodeEngine(this);
101
0
  mStream = AudioNodeExternalInputStream::Create(graph, engine);
102
0
  mInputStream->AddConsumerToKeepAlive(ToSupports(this));
103
0
104
0
  mInputStream->RegisterTrackListener(this);
105
0
  AttachToFirstTrack(mInputStream);
106
0
}
107
108
void
109
MediaStreamAudioSourceNode::Destroy()
110
0
{
111
0
  if (mInputStream) {
112
0
    mInputStream->UnregisterTrackListener(this);
113
0
    mInputStream = nullptr;
114
0
  }
115
0
  DetachFromTrack();
116
0
}
117
118
MediaStreamAudioSourceNode::~MediaStreamAudioSourceNode()
119
0
{
120
0
  Destroy();
121
0
}
122
123
void
124
MediaStreamAudioSourceNode::AttachToTrack(const RefPtr<MediaStreamTrack>& aTrack)
125
0
{
126
0
  MOZ_ASSERT(!mInputTrack);
127
0
  MOZ_ASSERT(aTrack->AsAudioStreamTrack());
128
0
129
0
  if (!mStream) {
130
0
    return;
131
0
  }
132
0
133
0
  mInputTrack = aTrack;
134
0
  ProcessedMediaStream* outputStream =
135
0
    static_cast<ProcessedMediaStream*>(mStream.get());
136
0
  mInputPort = mInputTrack->ForwardTrackContentsTo(outputStream);
137
0
  PrincipalChanged(mInputTrack); // trigger enabling/disabling of the connector
138
0
  mInputTrack->AddPrincipalChangeObserver(this);
139
0
}
140
141
void
142
MediaStreamAudioSourceNode::DetachFromTrack()
143
0
{
144
0
  if (mInputTrack) {
145
0
    mInputTrack->RemovePrincipalChangeObserver(this);
146
0
    mInputTrack = nullptr;
147
0
  }
148
0
  if (mInputPort) {
149
0
    mInputPort->Destroy();
150
0
    mInputPort = nullptr;
151
0
  }
152
0
}
153
154
void
155
MediaStreamAudioSourceNode::AttachToFirstTrack(const RefPtr<DOMMediaStream>& aMediaStream)
156
0
{
157
0
  nsTArray<RefPtr<AudioStreamTrack>> tracks;
158
0
  aMediaStream->GetAudioTracks(tracks);
159
0
160
0
  for (const RefPtr<AudioStreamTrack>& track : tracks) {
161
0
    if (track->Ended()) {
162
0
      continue;
163
0
    }
164
0
165
0
    AttachToTrack(track);
166
0
    MarkActive();
167
0
    return;
168
0
  }
169
0
170
0
  // There was no track available. We'll allow the node to be garbage collected.
171
0
  MarkInactive();
172
0
}
173
174
void
175
MediaStreamAudioSourceNode::NotifyTrackAdded(const RefPtr<MediaStreamTrack>& aTrack)
176
0
{
177
0
  if (mInputTrack) {
178
0
    return;
179
0
  }
180
0
181
0
  if (!aTrack->AsAudioStreamTrack()) {
182
0
    return;
183
0
  }
184
0
185
0
  AttachToTrack(aTrack);
186
0
}
187
188
void
189
MediaStreamAudioSourceNode::NotifyTrackRemoved(const RefPtr<MediaStreamTrack>& aTrack)
190
0
{
191
0
  if (aTrack != mInputTrack) {
192
0
    return;
193
0
  }
194
0
195
0
  DetachFromTrack();
196
0
  AttachToFirstTrack(mInputStream);
197
0
}
198
199
/**
200
 * Changes the principal. Note that this will be called on the main thread, but
201
 * changes will be enacted on the MediaStreamGraph thread. If the principal
202
 * change results in the document principal losing access to the stream, then
203
 * there needs to be other measures in place to ensure that any media that is
204
 * governed by the new stream principal is not available to the MediaStreamGraph
205
 * before this change completes. Otherwise, a site could get access to
206
 * media that they are not authorized to receive.
207
 *
208
 * One solution is to block the altered content, call this method, then dispatch
209
 * another change request to the MediaStreamGraph thread that allows the content
210
 * under the new principal to flow. This might be unnecessary if the principal
211
 * change is changing to be the document principal.
212
 */
213
void
214
MediaStreamAudioSourceNode::PrincipalChanged(MediaStreamTrack* aMediaStreamTrack)
215
0
{
216
0
  MOZ_ASSERT(aMediaStreamTrack == mInputTrack);
217
0
218
0
  bool subsumes = false;
219
0
  nsIDocument* doc = nullptr;
220
0
  if (nsPIDOMWindowInner* parent = Context()->GetParentObject()) {
221
0
    doc = parent->GetExtantDoc();
222
0
    if (doc) {
223
0
      nsIPrincipal* docPrincipal = doc->NodePrincipal();
224
0
      nsIPrincipal* trackPrincipal = aMediaStreamTrack->GetPrincipal();
225
0
      if (!trackPrincipal || NS_FAILED(docPrincipal->Subsumes(trackPrincipal, &subsumes))) {
226
0
        subsumes = false;
227
0
      }
228
0
    }
229
0
  }
230
0
  auto stream = static_cast<AudioNodeExternalInputStream*>(mStream.get());
231
0
  bool enabled = subsumes || aMediaStreamTrack->GetCORSMode() != CORS_NONE;
232
0
  stream->SetInt32Parameter(MediaStreamAudioSourceNodeEngine::ENABLE, enabled);
233
0
234
0
  if (!enabled && doc) {
235
0
    nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
236
0
                                    NS_LITERAL_CSTRING("Web Audio"),
237
0
                                    doc,
238
0
                                    nsContentUtils::eDOM_PROPERTIES,
239
0
                                    CrossOriginErrorString());
240
0
  }
241
0
}
242
243
size_t
244
MediaStreamAudioSourceNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
245
0
{
246
0
  // Future:
247
0
  // - mInputStream
248
0
  size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
249
0
  if (mInputPort) {
250
0
    amount += mInputPort->SizeOfIncludingThis(aMallocSizeOf);
251
0
  }
252
0
  return amount;
253
0
}
254
255
size_t
256
MediaStreamAudioSourceNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
257
0
{
258
0
  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
259
0
}
260
261
void
262
MediaStreamAudioSourceNode::DestroyMediaStream()
263
0
{
264
0
  if (mInputPort) {
265
0
    mInputPort->Destroy();
266
0
    mInputPort = nullptr;
267
0
  }
268
0
  AudioNode::DestroyMediaStream();
269
0
}
270
271
JSObject*
272
MediaStreamAudioSourceNode::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
273
0
{
274
0
  return MediaStreamAudioSourceNode_Binding::Wrap(aCx, this, aGivenProto);
275
0
}
276
277
} // namespace dom
278
} // namespace mozilla