content/media/webaudio/AudioNode.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

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.

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

mercurial