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.

     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 }

mercurial