content/media/webaudio/AudioBuffer.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 "AudioBuffer.h"
     8 #include "mozilla/dom/AudioBufferBinding.h"
     9 #include "jsfriendapi.h"
    10 #include "mozilla/ErrorResult.h"
    11 #include "AudioSegment.h"
    12 #include "AudioChannelFormat.h"
    13 #include "mozilla/PodOperations.h"
    14 #include "mozilla/CheckedInt.h"
    15 #include "AudioNodeEngine.h"
    17 namespace mozilla {
    18 namespace dom {
    20 NS_IMPL_CYCLE_COLLECTION_CLASS(AudioBuffer)
    22 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AudioBuffer)
    23   NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext)
    24   NS_IMPL_CYCLE_COLLECTION_UNLINK(mJSChannels)
    25   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
    26   tmp->ClearJSChannels();
    27 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    29 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AudioBuffer)
    30   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext)
    31   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
    32 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    34 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(AudioBuffer)
    35   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
    36   for (uint32_t i = 0; i < tmp->mJSChannels.Length(); ++i) {
    37     NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mJSChannels[i])
    38   }
    39 NS_IMPL_CYCLE_COLLECTION_TRACE_END
    41 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(AudioBuffer, AddRef)
    42 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(AudioBuffer, Release)
    44 AudioBuffer::AudioBuffer(AudioContext* aContext, uint32_t aNumberOfChannels,
    45                          uint32_t aLength, float aSampleRate)
    46   : mContext(aContext),
    47     mLength(aLength),
    48     mSampleRate(aSampleRate)
    49 {
    50   mJSChannels.SetCapacity(aNumberOfChannels);
    51   SetIsDOMBinding();
    52   mozilla::HoldJSObjects(this);
    53 }
    55 AudioBuffer::~AudioBuffer()
    56 {
    57   ClearJSChannels();
    58 }
    60 void
    61 AudioBuffer::ClearJSChannels()
    62 {
    63   mJSChannels.Clear();
    64   mozilla::DropJSObjects(this);
    65 }
    67 /* static */ already_AddRefed<AudioBuffer>
    68 AudioBuffer::Create(AudioContext* aContext, uint32_t aNumberOfChannels,
    69                     uint32_t aLength, float aSampleRate,
    70                     JSContext* aJSContext, ErrorResult& aRv)
    71 {
    72   // Note that a buffer with zero channels is permitted here for the sake of
    73   // AudioProcessingEvent, where channel counts must match parameters passed
    74   // to createScriptProcessor(), one of which may be zero.
    75   if (aSampleRate < WebAudioUtils::MinSampleRate ||
    76       aSampleRate > WebAudioUtils::MaxSampleRate ||
    77       aNumberOfChannels > WebAudioUtils::MaxChannelCount ||
    78       !aLength || aLength > INT32_MAX) {
    79     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
    80     return nullptr;
    81   }
    83   nsRefPtr<AudioBuffer> buffer =
    84     new AudioBuffer(aContext, aNumberOfChannels, aLength, aSampleRate);
    86   for (uint32_t i = 0; i < aNumberOfChannels; ++i) {
    87     JS::Rooted<JSObject*> array(aJSContext,
    88                                 JS_NewFloat32Array(aJSContext, aLength));
    89     if (!array) {
    90       aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
    91       return nullptr;
    92     }
    93     buffer->mJSChannels.AppendElement(array.get());
    94   }
    96   return buffer.forget();
    97 }
    99 JSObject*
   100 AudioBuffer::WrapObject(JSContext* aCx)
   101 {
   102   return AudioBufferBinding::Wrap(aCx, this);
   103 }
   105 bool
   106 AudioBuffer::RestoreJSChannelData(JSContext* aJSContext)
   107 {
   108   if (mSharedChannels) {
   109     for (uint32_t i = 0; i < mJSChannels.Length(); ++i) {
   110       const float* data = mSharedChannels->GetData(i);
   111       // The following code first zeroes the array and then copies our data
   112       // into it. We could avoid this with additional JS APIs to construct
   113       // an array (or ArrayBuffer) containing initial data.
   114       JS::Rooted<JSObject*> array(aJSContext,
   115                                   JS_NewFloat32Array(aJSContext, mLength));
   116       if (!array) {
   117         return false;
   118       }
   119       memcpy(JS_GetFloat32ArrayData(array), data, sizeof(float)*mLength);
   120       mJSChannels[i] = array;
   121     }
   123     mSharedChannels = nullptr;
   124   }
   126   return true;
   127 }
   129 void
   130 AudioBuffer::CopyFromChannel(const Float32Array& aDestination, uint32_t aChannelNumber,
   131                              uint32_t aStartInChannel, ErrorResult& aRv)
   132 {
   133   aDestination.ComputeLengthAndData();
   135   uint32_t length = aDestination.Length();
   136   CheckedInt<uint32_t> end = aStartInChannel;
   137   end += length;
   138   if (aChannelNumber >= NumberOfChannels() ||
   139       !end.isValid() || end.value() > mLength) {
   140     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
   141     return;
   142   }
   144   if (!mSharedChannels && JS_GetTypedArrayLength(mJSChannels[aChannelNumber]) != mLength) {
   145     // The array was probably neutered
   146     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
   147     return;
   148   }
   150   const float* sourceData = mSharedChannels ?
   151     mSharedChannels->GetData(aChannelNumber) :
   152     JS_GetFloat32ArrayData(mJSChannels[aChannelNumber]);
   153   PodMove(aDestination.Data(), sourceData + aStartInChannel, length);
   154 }
   156 void
   157 AudioBuffer::CopyToChannel(JSContext* aJSContext, const Float32Array& aSource,
   158                            uint32_t aChannelNumber, uint32_t aStartInChannel,
   159                            ErrorResult& aRv)
   160 {
   161   aSource.ComputeLengthAndData();
   163   uint32_t length = aSource.Length();
   164   CheckedInt<uint32_t> end = aStartInChannel;
   165   end += length;
   166   if (aChannelNumber >= NumberOfChannels() ||
   167       !end.isValid() || end.value() > mLength) {
   168     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
   169     return;
   170   }
   172   if (!mSharedChannels && JS_GetTypedArrayLength(mJSChannels[aChannelNumber]) != mLength) {
   173     // The array was probably neutered
   174     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
   175     return;
   176   }
   178   if (!RestoreJSChannelData(aJSContext)) {
   179     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
   180     return;
   181   }
   183   PodMove(JS_GetFloat32ArrayData(mJSChannels[aChannelNumber]) + aStartInChannel,
   184           aSource.Data(), length);
   185 }
   187 void
   188 AudioBuffer::SetRawChannelContents(JSContext* aJSContext, uint32_t aChannel,
   189                                    float* aContents)
   190 {
   191   PodCopy(JS_GetFloat32ArrayData(mJSChannels[aChannel]), aContents, mLength);
   192 }
   194 void
   195 AudioBuffer::GetChannelData(JSContext* aJSContext, uint32_t aChannel,
   196                             JS::MutableHandle<JSObject*> aRetval,
   197                             ErrorResult& aRv)
   198 {
   199   if (aChannel >= NumberOfChannels()) {
   200     aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
   201     return;
   202   }
   204   if (!RestoreJSChannelData(aJSContext)) {
   205     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
   206     return;
   207   }
   209   if (mJSChannels[aChannel]) {
   210     JS::ExposeObjectToActiveJS(mJSChannels[aChannel]);
   211   }
   212   aRetval.set(mJSChannels[aChannel]);
   213 }
   215 static already_AddRefed<ThreadSharedFloatArrayBufferList>
   216 StealJSArrayDataIntoThreadSharedFloatArrayBufferList(JSContext* aJSContext,
   217                                                      const nsTArray<JSObject*>& aJSArrays)
   218 {
   219   nsRefPtr<ThreadSharedFloatArrayBufferList> result =
   220     new ThreadSharedFloatArrayBufferList(aJSArrays.Length());
   221   for (uint32_t i = 0; i < aJSArrays.Length(); ++i) {
   222     JS::Rooted<JSObject*> arrayBuffer(aJSContext,
   223                                       JS_GetArrayBufferViewBuffer(aJSContext, aJSArrays[i]));
   224     uint8_t* stolenData = arrayBuffer
   225                           ? (uint8_t*) JS_StealArrayBufferContents(aJSContext, arrayBuffer)
   226                           : nullptr;
   227     if (stolenData) {
   228       result->SetData(i, stolenData, reinterpret_cast<float*>(stolenData));
   229     } else {
   230       return nullptr;
   231     }
   232   }
   233   return result.forget();
   234 }
   236 ThreadSharedFloatArrayBufferList*
   237 AudioBuffer::GetThreadSharedChannelsForRate(JSContext* aJSContext)
   238 {
   239   if (!mSharedChannels) {
   240     for (uint32_t i = 0; i < mJSChannels.Length(); ++i) {
   241       if (mLength != JS_GetTypedArrayLength(mJSChannels[i])) {
   242         // Probably one of the arrays was neutered
   243         return nullptr;
   244       }
   245     }
   247     mSharedChannels =
   248       StealJSArrayDataIntoThreadSharedFloatArrayBufferList(aJSContext, mJSChannels);
   249   }
   251   return mSharedChannels;
   252 }
   254 size_t
   255 AudioBuffer::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
   256 {
   257   size_t amount = aMallocSizeOf(this);
   258   amount += mJSChannels.SizeOfExcludingThis(aMallocSizeOf);
   259   if (mSharedChannels) {
   260     amount += mSharedChannels->SizeOfExcludingThis(aMallocSizeOf);
   261   }
   262   return amount;
   263 }
   265 }
   266 }

mercurial