Bug 1087944 - Implement the promise version of OfflineAudioContext. r=ehsan,smaug
authorPaul Adenot <paul@paul.cx>
Wed, 19 Nov 2014 11:38:39 +0100
changeset 243911 c6be963121ae1120ffc952bc9c5c2de3e0ecbd3d
parent 243910 8a0c83fe66d18b738c5c822c47eed723201d708d
child 243912 c213f97e0eec2b0ded9fa2ccb14877b8705f95cc
push id4489
push userraliiev@mozilla.com
push dateMon, 23 Feb 2015 15:17:55 +0000
treeherdermozilla-beta@fd7c3dc24146 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersehsan, smaug
bugs1087944
milestone37.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 1087944 - Implement the promise version of OfflineAudioContext. r=ehsan,smaug
dom/media/webaudio/AudioContext.cpp
dom/media/webaudio/AudioContext.h
dom/media/webaudio/AudioDestinationNode.cpp
dom/media/webaudio/AudioDestinationNode.h
dom/webidl/OfflineAudioContext.webidl
--- a/dom/media/webaudio/AudioContext.cpp
+++ b/dom/media/webaudio/AudioContext.cpp
@@ -627,27 +627,31 @@ AudioContext::GetGlobalJSObject() const
   if (!parentObject) {
     return nullptr;
   }
 
   // This can also return null.
   return parentObject->GetGlobalJSObject();
 }
 
-void
+already_AddRefed<Promise>
 AudioContext::StartRendering(ErrorResult& aRv)
 {
+  nsCOMPtr<nsIGlobalObject> parentObject = do_QueryInterface(GetParentObject());
+
   MOZ_ASSERT(mIsOffline, "This should only be called on OfflineAudioContext");
   if (mIsStarted) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
-    return;
+    return nullptr;
   }
 
   mIsStarted = true;
-  mDestination->StartRendering();
+  nsRefPtr<Promise> promise = Promise::Create(parentObject, aRv);
+  mDestination->StartRendering(promise);
+  return promise.forget();
 }
 
 void
 AudioContext::Mute() const
 {
   MOZ_ASSERT(!mIsOffline);
   if (mDestination) {
     mDestination->Mute();
--- a/dom/media/webaudio/AudioContext.h
+++ b/dom/media/webaudio/AudioContext.h
@@ -186,17 +186,17 @@ public:
                      ErrorResult& aRv);
 
   already_AddRefed<Promise>
   DecodeAudioData(const ArrayBuffer& aBuffer,
                   const Optional<OwningNonNull<DecodeSuccessCallback> >& aSuccessCallback,
                   const Optional<OwningNonNull<DecodeErrorCallback> >& aFailureCallback);
 
   // OfflineAudioContext methods
-  void StartRendering(ErrorResult& aRv);
+  already_AddRefed<Promise> StartRendering(ErrorResult& aRv);
   IMPL_EVENT_HANDLER(complete)
 
   bool IsOffline() const { return mIsOffline; }
 
   MediaStreamGraph* Graph() const;
   MediaStream* DestinationStream() const;
 
   // Nodes register here if they will produce sound even if they have silent
--- a/dom/media/webaudio/AudioDestinationNode.cpp
+++ b/dom/media/webaudio/AudioDestinationNode.cpp
@@ -17,16 +17,17 @@
 #include "OfflineAudioCompletionEvent.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIDocShell.h"
 #include "nsIPermissionManager.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIAppShell.h"
 #include "nsWidgetsCID.h"
+#include "mozilla/dom/Promise.h"
 
 namespace mozilla {
 namespace dom {
 
 static uint8_t gWebAudioOutputKey;
 
 class OfflineDestinationNodeEngine : public AudioNodeEngine
 {
@@ -121,16 +122,38 @@ public:
       NS_ASSERTION(mWriteIndex == mLength, "Overshot length");
       // Go to finished state. When the graph's current time eventually reaches
       // the end of the stream, then the main thread will be notified and we'll
       // shut down the AudioContext.
       *aFinished = true;
     }
   }
 
+  class OnCompleteTask MOZ_FINAL : public nsRunnable
+  {
+  public:
+    OnCompleteTask(AudioContext* aAudioContext, AudioBuffer* aRenderedBuffer)
+      : mAudioContext(aAudioContext)
+      , mRenderedBuffer(aRenderedBuffer)
+    {}
+
+    NS_IMETHOD Run()
+    {
+      nsRefPtr<OfflineAudioCompletionEvent> event =
+          new OfflineAudioCompletionEvent(mAudioContext, nullptr, nullptr);
+      event->InitEvent(mRenderedBuffer);
+      mAudioContext->DispatchTrustedEvent(event);
+
+      return NS_OK;
+    }
+  private:
+    nsRefPtr<AudioContext> mAudioContext;
+    nsRefPtr<AudioBuffer> mRenderedBuffer;
+  };
+
   void FireOfflineCompletionEvent(AudioDestinationNode* aNode)
   {
     AudioContext* context = aNode->Context();
     context->Shutdown();
     // Shutdown drops self reference, but the context is still referenced by aNode,
     // which is strongly referenced by the runnable that called
     // AudioDestinationNode::FireOfflineCompletionEvent.
 
@@ -147,20 +170,21 @@ public:
                           mLength, mSampleRate, cx, rv);
     if (rv.Failed()) {
       return;
     }
     for (uint32_t i = 0; i < mInputChannels.Length(); ++i) {
       renderedBuffer->SetRawChannelContents(i, mInputChannels[i]);
     }
 
-    nsRefPtr<OfflineAudioCompletionEvent> event =
-        new OfflineAudioCompletionEvent(context, nullptr, nullptr);
-    event->InitEvent(renderedBuffer);
-    context->DispatchTrustedEvent(event);
+    aNode->ResolvePromise(renderedBuffer);
+
+    nsRefPtr<OnCompleteTask> task =
+      new OnCompleteTask(context, renderedBuffer);
+    NS_DispatchToMainThread(task);
   }
 
   virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
   {
     size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
     amount += mInputChannels.SizeOfExcludingThis(aMallocSizeOf);
     return amount;
   }
@@ -296,17 +320,18 @@ private:
   { }
 
   nsWeakPtr mWeakNode;
 };
 
 NS_IMPL_ISUPPORTS(EventProxyHandler, nsIDOMEventListener)
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(AudioDestinationNode, AudioNode,
-                                   mAudioChannelAgent, mEventProxyHelper)
+                                   mAudioChannelAgent, mEventProxyHelper,
+                                   mOfflineRenderingPromise)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(AudioDestinationNode)
   NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
   NS_INTERFACE_MAP_ENTRY(nsIAudioChannelAgentCallback)
 NS_INTERFACE_MAP_END_INHERITING(AudioNode)
 
 NS_IMPL_ADDREF_INHERITED(AudioDestinationNode, AudioNode)
 NS_IMPL_RELEASE_INHERITED(AudioDestinationNode, AudioNode)
@@ -410,16 +435,24 @@ void
 AudioDestinationNode::FireOfflineCompletionEvent()
 {
   AudioNodeStream* stream = static_cast<AudioNodeStream*>(Stream());
   OfflineDestinationNodeEngine* engine =
     static_cast<OfflineDestinationNodeEngine*>(stream->Engine());
   engine->FireOfflineCompletionEvent(this);
 }
 
+void
+AudioDestinationNode::ResolvePromise(AudioBuffer* aRenderedBuffer)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mIsOffline);
+  mOfflineRenderingPromise->MaybeResolve(aRenderedBuffer);
+}
+
 uint32_t
 AudioDestinationNode::MaxChannelCount() const
 {
   return Context()->MaxChannelCount();
 }
 
 void
 AudioDestinationNode::SetChannelCount(uint32_t aChannelCount, ErrorResult& aRv)
@@ -458,18 +491,19 @@ AudioDestinationNode::OfflineShutdown()
 
 JSObject*
 AudioDestinationNode::WrapObject(JSContext* aCx)
 {
   return AudioDestinationNodeBinding::Wrap(aCx, this);
 }
 
 void
-AudioDestinationNode::StartRendering()
+AudioDestinationNode::StartRendering(Promise* aPromise)
 {
+  mOfflineRenderingPromise = aPromise;
   mOfflineRenderingRef.Take(this);
   mStream->Graph()->StartNonRealtimeProcessing(mFramesToProduce);
 }
 
 void
 AudioDestinationNode::SetCanPlay(bool aCanPlay)
 {
   mStream->SetTrackEnabled(AudioNodeStream::AUDIO_TRACK, aCanPlay);
--- a/dom/media/webaudio/AudioDestinationNode.h
+++ b/dom/media/webaudio/AudioDestinationNode.h
@@ -49,17 +49,17 @@ public:
 
   uint32_t MaxChannelCount() const;
   virtual void SetChannelCount(uint32_t aChannelCount,
                                ErrorResult& aRv) MOZ_OVERRIDE;
 
   void Mute();
   void Unmute();
 
-  void StartRendering();
+  void StartRendering(Promise* aPromise);
 
   void OfflineShutdown();
 
   // nsIDOMEventListener - by proxy
   NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent);
 
   AudioChannel MozAudioChannelType() const;
   void SetMozAudioChannelType(AudioChannel aValue, ErrorResult& aRv);
@@ -80,16 +80,17 @@ public:
   {
     return "AudioDestinationNode";
   }
 
   virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
   virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
 
   void InputMuted(bool aInputMuted);
+  void ResolvePromise(AudioBuffer* aRenderedBuffer);
 
 protected:
   virtual ~AudioDestinationNode();
 
 private:
   bool CheckAudioChannelPermissions(AudioChannel aValue);
 
   void SetCanPlay(bool aCanPlay);
@@ -98,16 +99,17 @@ private:
   void ScheduleStableStateNotification();
 
   SelfReference<AudioDestinationNode> mOfflineRenderingRef;
   uint32_t mFramesToProduce;
 
   nsCOMPtr<nsIAudioChannelAgent> mAudioChannelAgent;
 
   nsRefPtr<EventProxyHandler> mEventProxyHelper;
+  nsRefPtr<Promise> mOfflineRenderingPromise;
 
   // Audio Channel Type.
   AudioChannel mAudioChannel;
   bool mIsOffline;
   bool mHasFinished;
   bool mAudioChannelAgentPlaying;
 
   TimeStamp mStartedBlockingDueToBeingOnlyNode;
--- a/dom/webidl/OfflineAudioContext.webidl
+++ b/dom/webidl/OfflineAudioContext.webidl
@@ -11,13 +11,13 @@
  */
 
 callback OfflineRenderSuccessCallback = void (AudioBuffer renderedData);
 
 [Constructor(unsigned long numberOfChannels, unsigned long length, float sampleRate)]
 interface OfflineAudioContext : AudioContext {
 
     [Throws]
-    void startRendering();
+    Promise<AudioBuffer> startRendering();
 
     attribute EventHandler oncomplete;
 
 };