Bug 868776 - Fix rooting hazards in Web Audio code; r=till
authorEhsan Akhgari <ehsan@mozilla.com>
Sat, 04 May 2013 17:40:20 -0400
changeset 141807 e6d7033aeb748c3246eb9966f2365f562d01639d
parent 141806 a2245b038bcc305c8cdb048e8c3199e0ee6d047b
child 141808 2b63238518a39ffa03c79abddb982244abb3427c
push id2579
push userakeybl@mozilla.com
push dateMon, 24 Jun 2013 18:52:47 +0000
treeherdermozilla-beta@b69b7de8a05a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstill
bugs868776
milestone23.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 868776 - Fix rooting hazards in Web Audio code; r=till
content/media/webaudio/AudioBuffer.cpp
content/media/webaudio/AudioBuffer.h
content/media/webaudio/MediaBufferDecoder.cpp
--- a/content/media/webaudio/AudioBuffer.cpp
+++ b/content/media/webaudio/AudioBuffer.cpp
@@ -68,48 +68,53 @@ AudioBuffer::ClearJSChannels()
 
 bool
 AudioBuffer::InitializeBuffers(uint32_t aNumberOfChannels, JSContext* aJSContext)
 {
   if (!mJSChannels.SetCapacity(aNumberOfChannels)) {
     return false;
   }
   for (uint32_t i = 0; i < aNumberOfChannels; ++i) {
-    JSObject* array = JS_NewFloat32Array(aJSContext, mLength);
+    JS::RootedObject array(aJSContext, JS_NewFloat32Array(aJSContext, mLength));
     if (!array) {
       return false;
     }
     mJSChannels.AppendElement(array);
   }
 
   return true;
 }
 
 JSObject*
 AudioBuffer::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
 {
   return AudioBufferBinding::Wrap(aCx, aScope, this);
 }
 
-void
+bool
 AudioBuffer::RestoreJSChannelData(JSContext* aJSContext)
 {
   if (mSharedChannels) {
     for (uint32_t i = 0; i < mJSChannels.Length(); ++i) {
       const float* data = mSharedChannels->GetData(i);
       // The following code first zeroes the array and then copies our data
       // into it. We could avoid this with additional JS APIs to construct
       // an array (or ArrayBuffer) containing initial data.
-      JSObject* array = JS_NewFloat32Array(aJSContext, mLength);
+      JS::RootedObject array(aJSContext, JS_NewFloat32Array(aJSContext, mLength));
+      if (!array) {
+        return false;
+      }
       memcpy(JS_GetFloat32ArrayData(array), data, sizeof(float)*mLength);
       mJSChannels[i] = array;
     }
 
     mSharedChannels = nullptr;
   }
+
+  return true;
 }
 
 void
 AudioBuffer::SetRawChannelContents(JSContext* aJSContext, uint32_t aChannel,
                                    float* aContents)
 {
   memcpy(JS_GetFloat32ArrayData(mJSChannels[aChannel]), aContents, sizeof(float)*mLength);
 }
@@ -118,43 +123,56 @@ JSObject*
 AudioBuffer::GetChannelData(JSContext* aJSContext, uint32_t aChannel,
                             ErrorResult& aRv)
 {
   if (aChannel >= NumberOfChannels()) {
     aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
     return nullptr;
   }
 
-  RestoreJSChannelData(aJSContext);
+  if (!RestoreJSChannelData(aJSContext)) {
+    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
+    return nullptr;
+  }
 
   return mJSChannels[aChannel];
 }
 
-void
+bool
 AudioBuffer::SetChannelDataFromArrayBufferContents(JSContext* aJSContext,
                                                    uint32_t aChannel,
                                                    void* aContents)
 {
-  RestoreJSChannelData(aJSContext);
+  if (!RestoreJSChannelData(aJSContext)) {
+    return false;
+  }
 
   MOZ_ASSERT(aChannel < NumberOfChannels());
-  JSObject* arrayBuffer = JS_NewArrayBufferWithContents(aJSContext, aContents);
+  JS::RootedObject arrayBuffer(aJSContext, JS_NewArrayBufferWithContents(aJSContext, aContents));
+  if (!arrayBuffer) {
+    return false;
+  }
   mJSChannels[aChannel] = JS_NewFloat32ArrayWithBuffer(aJSContext, arrayBuffer,
                                                        0, -1);
+  if (!mJSChannels[aChannel]) {
+    return false;
+  }
   MOZ_ASSERT(mLength == JS_GetTypedArrayLength(mJSChannels[aChannel]));
+
+  return true;
 }
 
 static already_AddRefed<ThreadSharedFloatArrayBufferList>
 StealJSArrayDataIntoThreadSharedFloatArrayBufferList(JSContext* aJSContext,
                                                      const nsTArray<JSObject*>& aJSArrays)
 {
   nsRefPtr<ThreadSharedFloatArrayBufferList> result =
     new ThreadSharedFloatArrayBufferList(aJSArrays.Length());
   for (uint32_t i = 0; i < aJSArrays.Length(); ++i) {
-    JSObject* arrayBuffer = JS_GetArrayBufferViewBuffer(aJSArrays[i]);
+    JS::RootedObject arrayBuffer(aJSContext, JS_GetArrayBufferViewBuffer(aJSArrays[i]));
     void* dataToFree = nullptr;
     uint8_t* stolenData = nullptr;
     if (arrayBuffer &&
         JS_StealArrayBufferContents(aJSContext, arrayBuffer, &dataToFree,
                                     &stolenData)) {
       result->SetData(i, dataToFree, reinterpret_cast<float*>(stolenData));
     } else {
       result->Clear();
--- a/content/media/webaudio/AudioBuffer.h
+++ b/content/media/webaudio/AudioBuffer.h
@@ -91,30 +91,30 @@ public:
 
   /**
    * Returns a ThreadSharedFloatArrayBufferList containing the sample data.
    */
   ThreadSharedFloatArrayBufferList* GetThreadSharedChannelsForRate(JSContext* aContext);
 
   // aContents should either come from JS_AllocateArrayBufferContents or
   // JS_StealArrayBufferContents.
-  void SetChannelDataFromArrayBufferContents(JSContext* aJSContext,
+  bool SetChannelDataFromArrayBufferContents(JSContext* aJSContext,
                                              uint32_t aChannel,
                                              void* aContents);
 
   // This replaces the contents of the JS array for the given channel.
   // This function needs to be called on an AudioBuffer which has not been
   // handed off to the content yet, and right after the object has been
   // initialized.
   void SetRawChannelContents(JSContext* aJSContext,
                              uint32_t aChannel,
                              float* aContents);
 
 protected:
-  void RestoreJSChannelData(JSContext* aJSContext);
+  bool RestoreJSChannelData(JSContext* aJSContext);
   void ClearJSChannels();
 
   nsRefPtr<AudioContext> mContext;
   // Float32Arrays
   AutoFallibleTArray<JSObject*,2> mJSChannels;
 
   // mSharedChannels aggregates the data from mJSChannels. This is non-null
   // if and only if the mJSChannels are neutered.
--- a/content/media/webaudio/MediaBufferDecoder.cpp
+++ b/content/media/webaudio/MediaBufferDecoder.cpp
@@ -635,17 +635,19 @@ WebAudioDecodeJob::FinalizeBufferData()
   MOZ_ASSERT(mChannels == mChannelBuffers.Length());
 
   AutoPushJSContext cx(mContext->GetJSContext());
   if (!cx) {
     return false;
   }
 
   for (uint32_t i = 0; i < mChannels; ++i) {
-    mOutput->SetChannelDataFromArrayBufferContents(cx, i, mChannelBuffers[i].first);
+    if (!mOutput->SetChannelDataFromArrayBufferContents(cx, i, mChannelBuffers[i].first)) {
+      return false;
+    }
   }
 
   return true;
 }
 
 bool
 WebAudioDecodeJob::AllocateBuffer()
 {
@@ -663,18 +665,21 @@ WebAudioDecodeJob::AllocateBuffer()
   if (!mOutput->InitializeBuffers(mChannels, cx)) {
     return false;
   }
 
   if (!mChannelBuffers.SetCapacity(mChannels)) {
     return false;
   }
   for (uint32_t i = 0; i < mChannels; ++i) {
-    JSObject* channelObj = mOutput->GetChannelData(i);
-    JSObject* arrayBuffer = JS_GetArrayBufferViewBuffer(channelObj);
+    JS::RootedObject channelObj(cx, mOutput->GetChannelData(i));
+    JS::RootedObject arrayBuffer(cx, JS_GetArrayBufferViewBuffer(channelObj));
+    if (!arrayBuffer) {
+      return false;
+    }
     void* contents;
     uint8_t* data;
     if (JS_FALSE == JS_StealArrayBufferContents(cx, arrayBuffer, &contents, &data)) {
       return false;
     }
     mChannelBuffers.AppendElement(
       std::make_pair(contents, reinterpret_cast<float*>(data)));
   }