Bug 1343550 - Prevent creating AudioNodes on an AudioContext that has been disconnected from its owner. r=baku draft
authorPaul Adenot <paul@paul.cx>
Wed, 15 Mar 2017 17:36:40 +0100
changeset 551669 73a90954cc3a0a8aeedb996a7de49c3026fe8b5d
parent 499512 ff04d410e74b69acfab17ef7e73e7397602d5a68
child 551670 c461f31670c8f8a295843a2532cda9580767aac6
child 551671 2d45fa52f4f82a81e9356acb866a8fbf0d4143c7
push id51127
push userpaul@paul.cx
push dateMon, 27 Mar 2017 08:17:35 +0000
reviewersbaku
bugs1343550
milestone55.0a1
Bug 1343550 - Prevent creating AudioNodes on an AudioContext that has been disconnected from its owner. r=baku Spec (being written): https://github.com/WebAudio/web-audio-api/issues/1139 Bug 1343550 - Prevent touching promises when shutting down an AudioContext, when the global is going away soon. r=baku MozReview-Commit-ID: F6en9KEbNNf
dom/media/webaudio/AudioContext.cpp
dom/media/webaudio/AudioContext.h
--- a/dom/media/webaudio/AudioContext.cpp
+++ b/dom/media/webaudio/AudioContext.cpp
@@ -135,16 +135,17 @@ AudioContext::AudioContext(nsPIDOMWindow
   , mSampleRate(GetSampleRateForAudioContext(aIsOffline, aSampleRate))
   , mAudioContextState(AudioContextState::Suspended)
   , mNumberOfChannels(aNumberOfChannels)
   , mIsOffline(aIsOffline)
   , mIsStarted(!aIsOffline)
   , mIsShutDown(false)
   , mCloseCalled(false)
   , mSuspendCalled(false)
+  , mIsDisconnecting(false)
 {
   bool mute = aWindow->AddAudioContext(this);
 
   // Note: AudioDestinationNode needs an AudioContext that must already be
   // bound to the window.
   mDestination = new AudioDestinationNode(this, aIsOffline, aChannel,
                                           aNumberOfChannels, aLength, aSampleRate);
 
@@ -255,17 +256,19 @@ AudioContext::Constructor(const GlobalOb
 
   RegisterWeakMemoryReporter(object);
 
   return object.forget();
 }
 
 bool AudioContext::CheckClosed(ErrorResult& aRv)
 {
-  if (mAudioContextState == AudioContextState::Closed) {
+  if (mAudioContextState == AudioContextState::Closed ||
+      mIsShutDown ||
+      mIsDisconnecting) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return true;
   }
   return false;
 }
 
 already_AddRefed<AudioBufferSourceNode>
 AudioContext::CreateBufferSource(ErrorResult& aRv)
@@ -635,32 +638,42 @@ AudioContext::DestinationStream() const
 
 double
 AudioContext::CurrentTime() const
 {
   MediaStream* stream = Destination()->Stream();
   return stream->StreamTimeToSeconds(stream->GetCurrentTime());
 }
 
+void AudioContext::DisconnectFromOwner()
+{
+  mIsDisconnecting = true;
+  Shutdown();
+  DOMEventTargetHelper::DisconnectFromOwner();
+}
+
 void
 AudioContext::Shutdown()
 {
   mIsShutDown = true;
 
-  if (!mIsOffline) {
-    ErrorResult dummy;
-    RefPtr<Promise> ignored = Close(dummy);
-  }
+  // We don't want to touch promises if the global is going away soon.
+  if (!mIsDisconnecting) {
+    if (!mIsOffline) {
+      IgnoredErrorResult dummy;
+      RefPtr<Promise> ignored = Close(dummy);
+    }
 
-  for (auto p : mPromiseGripArray) {
-    p->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
+    for (auto p : mPromiseGripArray) {
+      p->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
+    }
+
+    mPromiseGripArray.Clear();
   }
 
-  mPromiseGripArray.Clear();
-
   // Release references to active nodes.
   // Active AudioNodes don't unregister in destructors, at which point the
   // Node is already unregistered.
   mActiveNodes.Clear();
 
   // For offline contexts, we can destroy the MediaStreamGraph at this point.
   if (mIsOffline && mDestination) {
     mDestination->OfflineShutdown();
--- a/dom/media/webaudio/AudioContext.h
+++ b/dom/media/webaudio/AudioContext.h
@@ -137,16 +137,18 @@ public:
                                            DOMEventTargetHelper)
   MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
 
   nsPIDOMWindowInner* GetParentObject() const
   {
     return GetOwner();
   }
 
+  virtual void DisconnectFromOwner() override;
+
   void Shutdown(); // idempotent
 
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   using DOMEventTargetHelper::DispatchTrustedEvent;
 
   // Constructor for regular AudioContext
   static already_AddRefed<AudioContext>
@@ -367,16 +369,17 @@ private:
   uint32_t mNumberOfChannels;
   bool mIsOffline;
   bool mIsStarted;
   bool mIsShutDown;
   // Close has been called, reject suspend and resume call.
   bool mCloseCalled;
   // Suspend has been called with no following resume.
   bool mSuspendCalled;
+  bool mIsDisconnecting;
 };
 
 static const dom::AudioContext::AudioContextId NO_AUDIO_CONTEXT = 0;
 
 } // namespace dom
 } // namespace mozilla
 
 #endif