Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
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/. */
7 #include "AudioNode.h"
8 #include "mozilla/ErrorResult.h"
9 #include "AudioNodeStream.h"
10 #include "AudioNodeEngine.h"
11 #include "mozilla/dom/AudioParam.h"
13 namespace mozilla {
14 namespace dom {
16 static const uint32_t INVALID_PORT = 0xffffffff;
18 NS_IMPL_CYCLE_COLLECTION_CLASS(AudioNode)
20 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(AudioNode, DOMEventTargetHelper)
21 tmp->DisconnectFromGraph();
22 if (tmp->mContext) {
23 tmp->mContext->UpdateNodeCount(-1);
24 }
25 NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext)
26 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOutputNodes)
27 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOutputParams)
28 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
29 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(AudioNode,
30 DOMEventTargetHelper)
31 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext)
32 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOutputNodes)
33 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOutputParams)
34 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
36 NS_IMPL_ADDREF_INHERITED(AudioNode, DOMEventTargetHelper)
38 NS_IMETHODIMP_(MozExternalRefCountType)
39 AudioNode::Release()
40 {
41 if (mRefCnt.get() == 1) {
42 // We are about to be deleted, disconnect the object from the graph before
43 // the derived type is destroyed.
44 DisconnectFromGraph();
45 }
46 nsrefcnt r = DOMEventTargetHelper::Release();
47 NS_LOG_RELEASE(this, r, "AudioNode");
48 return r;
49 }
51 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(AudioNode)
52 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
54 AudioNode::AudioNode(AudioContext* aContext,
55 uint32_t aChannelCount,
56 ChannelCountMode aChannelCountMode,
57 ChannelInterpretation aChannelInterpretation)
58 : DOMEventTargetHelper(aContext->GetParentObject())
59 , mContext(aContext)
60 , mChannelCount(aChannelCount)
61 , mChannelCountMode(aChannelCountMode)
62 , mChannelInterpretation(aChannelInterpretation)
63 {
64 MOZ_ASSERT(aContext);
65 DOMEventTargetHelper::BindToOwner(aContext->GetParentObject());
66 SetIsDOMBinding();
67 aContext->UpdateNodeCount(1);
68 }
70 AudioNode::~AudioNode()
71 {
72 MOZ_ASSERT(mInputNodes.IsEmpty());
73 MOZ_ASSERT(mOutputNodes.IsEmpty());
74 MOZ_ASSERT(mOutputParams.IsEmpty());
75 if (mContext) {
76 mContext->UpdateNodeCount(-1);
77 }
78 }
80 size_t
81 AudioNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
82 {
83 // Not owned:
84 // - mContext
85 // - mStream
86 size_t amount = 0;
88 amount += mInputNodes.SizeOfExcludingThis(aMallocSizeOf);
89 for (size_t i = 0; i < mInputNodes.Length(); i++) {
90 amount += mInputNodes[i].SizeOfExcludingThis(aMallocSizeOf);
91 }
93 // Just measure the array. The entire audio node graph is measured via the
94 // MediaStreamGraph's streams, so we don't want to double-count the elements.
95 amount += mOutputNodes.SizeOfExcludingThis(aMallocSizeOf);
97 amount += mOutputParams.SizeOfExcludingThis(aMallocSizeOf);
98 for (size_t i = 0; i < mOutputParams.Length(); i++) {
99 amount += mOutputParams[i]->SizeOfIncludingThis(aMallocSizeOf);
100 }
102 return amount;
103 }
105 size_t
106 AudioNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
107 {
108 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
109 }
111 template <class InputNode>
112 static uint32_t
113 FindIndexOfNode(const nsTArray<InputNode>& aInputNodes, const AudioNode* aNode)
114 {
115 for (uint32_t i = 0; i < aInputNodes.Length(); ++i) {
116 if (aInputNodes[i].mInputNode == aNode) {
117 return i;
118 }
119 }
120 return nsTArray<InputNode>::NoIndex;
121 }
123 template <class InputNode>
124 static uint32_t
125 FindIndexOfNodeWithPorts(const nsTArray<InputNode>& aInputNodes, const AudioNode* aNode,
126 uint32_t aInputPort, uint32_t aOutputPort)
127 {
128 for (uint32_t i = 0; i < aInputNodes.Length(); ++i) {
129 if (aInputNodes[i].mInputNode == aNode &&
130 aInputNodes[i].mInputPort == aInputPort &&
131 aInputNodes[i].mOutputPort == aOutputPort) {
132 return i;
133 }
134 }
135 return nsTArray<InputNode>::NoIndex;
136 }
138 void
139 AudioNode::DisconnectFromGraph()
140 {
141 // Addref this temporarily so the refcount bumping below doesn't destroy us
142 // prematurely
143 nsRefPtr<AudioNode> kungFuDeathGrip = this;
145 // The idea here is that we remove connections one by one, and at each step
146 // the graph is in a valid state.
148 // Disconnect inputs. We don't need them anymore.
149 while (!mInputNodes.IsEmpty()) {
150 uint32_t i = mInputNodes.Length() - 1;
151 nsRefPtr<AudioNode> input = mInputNodes[i].mInputNode;
152 mInputNodes.RemoveElementAt(i);
153 input->mOutputNodes.RemoveElement(this);
154 }
156 while (!mOutputNodes.IsEmpty()) {
157 uint32_t i = mOutputNodes.Length() - 1;
158 nsRefPtr<AudioNode> output = mOutputNodes[i].forget();
159 mOutputNodes.RemoveElementAt(i);
160 uint32_t inputIndex = FindIndexOfNode(output->mInputNodes, this);
161 // It doesn't matter which one we remove, since we're going to remove all
162 // entries for this node anyway.
163 output->mInputNodes.RemoveElementAt(inputIndex);
164 }
166 while (!mOutputParams.IsEmpty()) {
167 uint32_t i = mOutputParams.Length() - 1;
168 nsRefPtr<AudioParam> output = mOutputParams[i].forget();
169 mOutputParams.RemoveElementAt(i);
170 uint32_t inputIndex = FindIndexOfNode(output->InputNodes(), this);
171 // It doesn't matter which one we remove, since we're going to remove all
172 // entries for this node anyway.
173 output->RemoveInputNode(inputIndex);
174 }
176 DestroyMediaStream();
177 }
179 void
180 AudioNode::Connect(AudioNode& aDestination, uint32_t aOutput,
181 uint32_t aInput, ErrorResult& aRv)
182 {
183 if (aOutput >= NumberOfOutputs() ||
184 aInput >= aDestination.NumberOfInputs()) {
185 aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
186 return;
187 }
189 if (Context() != aDestination.Context()) {
190 aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
191 return;
192 }
194 if (FindIndexOfNodeWithPorts(aDestination.mInputNodes, this, aInput, aOutput) !=
195 nsTArray<AudioNode::InputNode>::NoIndex) {
196 // connection already exists.
197 return;
198 }
200 // The MediaStreamGraph will handle cycle detection. We don't need to do it
201 // here.
203 mOutputNodes.AppendElement(&aDestination);
204 InputNode* input = aDestination.mInputNodes.AppendElement();
205 input->mInputNode = this;
206 input->mInputPort = aInput;
207 input->mOutputPort = aOutput;
208 if (aDestination.mStream) {
209 // Connect streams in the MediaStreamGraph
210 MOZ_ASSERT(aDestination.mStream->AsProcessedStream());
211 ProcessedMediaStream* ps =
212 static_cast<ProcessedMediaStream*>(aDestination.mStream.get());
213 MOZ_ASSERT(aInput <= UINT16_MAX, "Unexpected large input port number");
214 MOZ_ASSERT(aOutput <= UINT16_MAX, "Unexpected large output port number");
215 input->mStreamPort =
216 ps->AllocateInputPort(mStream, MediaInputPort::FLAG_BLOCK_INPUT,
217 static_cast<uint16_t>(aInput),
218 static_cast<uint16_t>(aOutput));
219 }
221 // This connection may have connected a panner and a source.
222 Context()->UpdatePannerSource();
223 }
225 void
226 AudioNode::Connect(AudioParam& aDestination, uint32_t aOutput,
227 ErrorResult& aRv)
228 {
229 if (aOutput >= NumberOfOutputs()) {
230 aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
231 return;
232 }
234 if (Context() != aDestination.GetParentObject()) {
235 aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
236 return;
237 }
239 if (FindIndexOfNodeWithPorts(aDestination.InputNodes(), this, INVALID_PORT, aOutput) !=
240 nsTArray<AudioNode::InputNode>::NoIndex) {
241 // connection already exists.
242 return;
243 }
245 mOutputParams.AppendElement(&aDestination);
246 InputNode* input = aDestination.AppendInputNode();
247 input->mInputNode = this;
248 input->mInputPort = INVALID_PORT;
249 input->mOutputPort = aOutput;
251 MediaStream* stream = aDestination.Stream();
252 MOZ_ASSERT(stream->AsProcessedStream());
253 ProcessedMediaStream* ps = static_cast<ProcessedMediaStream*>(stream);
255 // Setup our stream as an input to the AudioParam's stream
256 MOZ_ASSERT(aOutput <= UINT16_MAX, "Unexpected large output port number");
257 input->mStreamPort = ps->AllocateInputPort(mStream, MediaInputPort::FLAG_BLOCK_INPUT,
258 0, static_cast<uint16_t>(aOutput));
259 }
261 void
262 AudioNode::SendDoubleParameterToStream(uint32_t aIndex, double aValue)
263 {
264 AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
265 MOZ_ASSERT(ns, "How come we don't have a stream here?");
266 ns->SetDoubleParameter(aIndex, aValue);
267 }
269 void
270 AudioNode::SendInt32ParameterToStream(uint32_t aIndex, int32_t aValue)
271 {
272 AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
273 MOZ_ASSERT(ns, "How come we don't have a stream here?");
274 ns->SetInt32Parameter(aIndex, aValue);
275 }
277 void
278 AudioNode::SendThreeDPointParameterToStream(uint32_t aIndex, const ThreeDPoint& aValue)
279 {
280 AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
281 MOZ_ASSERT(ns, "How come we don't have a stream here?");
282 ns->SetThreeDPointParameter(aIndex, aValue);
283 }
285 void
286 AudioNode::SendChannelMixingParametersToStream()
287 {
288 AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
289 MOZ_ASSERT(ns, "How come we don't have a stream here?");
290 ns->SetChannelMixingParameters(mChannelCount, mChannelCountMode,
291 mChannelInterpretation);
292 }
294 void
295 AudioNode::SendTimelineParameterToStream(AudioNode* aNode, uint32_t aIndex,
296 const AudioParamTimeline& aValue)
297 {
298 AudioNodeStream* ns = static_cast<AudioNodeStream*>(aNode->mStream.get());
299 MOZ_ASSERT(ns, "How come we don't have a stream here?");
300 ns->SetTimelineParameter(aIndex, aValue);
301 }
303 void
304 AudioNode::Disconnect(uint32_t aOutput, ErrorResult& aRv)
305 {
306 if (aOutput >= NumberOfOutputs()) {
307 aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
308 return;
309 }
311 // An upstream node may be starting to play on the graph thread, and the
312 // engine for a downstream node may be sending a PlayingRefChangeHandler
313 // ADDREF message to this (main) thread. Wait for a round trip before
314 // releasing nodes, to give engines receiving sound now time to keep their
315 // nodes alive.
316 class RunnableRelease : public nsRunnable {
317 public:
318 explicit RunnableRelease(already_AddRefed<AudioNode> aNode)
319 : mNode(aNode) {}
321 NS_IMETHODIMP Run() MOZ_OVERRIDE
322 {
323 mNode = nullptr;
324 return NS_OK;
325 }
326 private:
327 nsRefPtr<AudioNode> mNode;
328 };
330 for (int32_t i = mOutputNodes.Length() - 1; i >= 0; --i) {
331 AudioNode* dest = mOutputNodes[i];
332 for (int32_t j = dest->mInputNodes.Length() - 1; j >= 0; --j) {
333 InputNode& input = dest->mInputNodes[j];
334 if (input.mInputNode == this && input.mOutputPort == aOutput) {
335 // Destroying the InputNode here sends a message to the graph thread
336 // to disconnect the streams, which should be sent before the
337 // RunAfterPendingUpdates() call below.
338 dest->mInputNodes.RemoveElementAt(j);
339 // Remove one instance of 'dest' from mOutputNodes. There could be
340 // others, and it's not correct to remove them all since some of them
341 // could be for different output ports.
342 nsRefPtr<nsIRunnable> runnable =
343 new RunnableRelease(mOutputNodes[i].forget());
344 mOutputNodes.RemoveElementAt(i);
345 mStream->RunAfterPendingUpdates(runnable.forget());
346 break;
347 }
348 }
349 }
351 for (int32_t i = mOutputParams.Length() - 1; i >= 0; --i) {
352 AudioParam* dest = mOutputParams[i];
353 for (int32_t j = dest->InputNodes().Length() - 1; j >= 0; --j) {
354 const InputNode& input = dest->InputNodes()[j];
355 if (input.mInputNode == this && input.mOutputPort == aOutput) {
356 dest->RemoveInputNode(j);
357 // Remove one instance of 'dest' from mOutputParams. There could be
358 // others, and it's not correct to remove them all since some of them
359 // could be for different output ports.
360 mOutputParams.RemoveElementAt(i);
361 break;
362 }
363 }
364 }
366 // This disconnection may have disconnected a panner and a source.
367 Context()->UpdatePannerSource();
368 }
370 void
371 AudioNode::DestroyMediaStream()
372 {
373 if (mStream) {
374 {
375 // Remove the node reference on the engine, and take care to not
376 // hold the lock when the stream gets destroyed, because that will
377 // cause the engine to be destroyed as well, and we don't want to
378 // be holding the lock as we're trying to destroy it!
379 AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
380 MutexAutoLock lock(ns->Engine()->NodeMutex());
381 MOZ_ASSERT(ns, "How come we don't have a stream here?");
382 MOZ_ASSERT(ns->Engine()->Node() == this, "Invalid node reference");
383 ns->Engine()->ClearNode();
384 }
386 mStream->Destroy();
387 mStream = nullptr;
388 }
389 }
391 void
392 AudioNode::RemoveOutputParam(AudioParam* aParam)
393 {
394 mOutputParams.RemoveElement(aParam);
395 }
397 }
398 }