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