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
--- 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