Index: Source/modules/webaudio/AudioContext.cpp |
diff --git a/Source/modules/webaudio/AudioContext.cpp b/Source/modules/webaudio/AudioContext.cpp |
index ebab3b8851a59fb698968cac7c46e373c5966ed7..1f44deec437dcaae2bccd8abe43658aec57bcac9 100644 |
--- a/Source/modules/webaudio/AudioContext.cpp |
+++ b/Source/modules/webaudio/AudioContext.cpp |
@@ -112,6 +112,7 @@ AudioContext::AudioContext(Document* document) |
, m_isOfflineContext(false) |
, m_contextState(Suspended) |
, m_cachedSampleFrame(0) |
+ , m_closedContextSampleRate(-1) |
tkent
2015/04/07 23:24:47
This member isn't initialized in another construct
|
{ |
m_didInitializeContextGraphMutex = true; |
m_destinationNode = DefaultAudioDestinationNode::create(this); |
@@ -150,6 +151,14 @@ AudioContext::~AudioContext() |
fprintf(stderr, "%p: AudioContext::~AudioContext(): %u\n", this, m_contextId); |
#endif |
// AudioNodes keep a reference to their context, so there should be no way to be in the destructor if there are still AudioNodes around. |
+ |
+ // Context is being destroyed. Reject any decodeAudioData promises that haven't been fulfilled |
+ // yet. |
+ for (auto& resolver : m_audioDecoderResolvers) { |
haraken
2015/04/07 23:39:14
This is unsafe. You're not allowed to touch other
yhirano
2015/04/08 03:19:27
Rejection is needed when deleting a resolver if
Raymond Toy
2015/04/08 16:47:11
uninitialize should work.
Raymond Toy
2015/04/08 16:47:11
If this is moved to uninitialize(), then the execu
|
+ resolver->reject(DOMException::create(InvalidStateError, "Audio context is going away")); |
+ } |
+ m_audioDecoderResolvers.clear(); |
+ |
ASSERT(!m_isInitialized); |
ASSERT(!m_referencedNodes.size()); |
ASSERT(!m_finishedNodes.size()); |
@@ -160,6 +169,7 @@ AudioContext::~AudioContext() |
ASSERT(!m_suspendResolvers.size()); |
ASSERT(!m_isResolvingResumePromises); |
ASSERT(!m_resumeResolvers.size()); |
+ ASSERT(!m_audioDecoderResolvers.size()); |
} |
void AudioContext::initialize() |
@@ -272,20 +282,30 @@ AudioBuffer* AudioContext::createBuffer(unsigned numberOfChannels, size_t number |
return AudioBuffer::create(numberOfChannels, numberOfFrames, sampleRate, exceptionState); |
} |
-void AudioContext::decodeAudioData(DOMArrayBuffer* audioData, AudioBufferCallback* successCallback, AudioBufferCallback* errorCallback, ExceptionState& exceptionState) |
+ScriptPromise AudioContext::decodeAudioData(ScriptState* scriptState, DOMArrayBuffer* audioData, AudioBufferCallback* successCallback, AudioBufferCallback* errorCallback, ExceptionState& exceptionState) |
{ |
- if (isContextClosed()) { |
- throwExceptionForClosedState(exceptionState); |
- return; |
- } |
- |
if (!audioData) { |
- exceptionState.throwDOMException( |
- SyntaxError, |
+ RefPtr<DOMException> error = DOMException::create( |
+ NotSupportedError, |
"invalid ArrayBuffer for audioData."); |
- return; |
+ if (errorCallback) { |
+ errorCallback->handleEvent(error.get()); |
+ } |
+ return ScriptPromise::rejectWithDOMException(scriptState, error); |
} |
- m_audioDecoder.decodeAsync(audioData, sampleRate(), successCallback, errorCallback); |
+ |
+ RefPtrWillBeRawPtr<ScriptPromiseResolver> resolver = ScriptPromiseResolver::create(scriptState); |
+ ScriptPromise promise = resolver->promise(); |
+ |
+ m_audioDecoderResolvers.append(resolver); |
+ |
+ float rate = isContextClosed() ? m_closedContextSampleRate : sampleRate(); |
+ |
+ ASSERT(rate > 0); |
+ |
+ m_audioDecoder.decodeAsync(audioData, rate, successCallback, errorCallback, resolver.get(), this); |
yhirano
2015/04/08 03:19:27
Just to confirm: Is this |resolver.get()| safe in
Raymond Toy
2015/04/08 16:47:11
I believe so. As long as uninitialize() has not b
|
+ |
+ return promise; |
} |
AudioBufferSourceNode* AudioContext::createBufferSource(ExceptionState& exceptionState) |
@@ -948,6 +968,19 @@ void AudioContext::handleStoppableSourceNodes() |
} |
} |
} |
+ |
+void AudioContext::removeAudioDecoderResolver(ScriptPromiseResolver* resolver) |
+{ |
+ ASSERT(isMainThread()); |
+ |
+ for (size_t k = 0; k < m_audioDecoderResolvers.size(); ++k) { |
+ if (resolver == m_audioDecoderResolvers.at(k)) { |
+ m_audioDecoderResolvers.remove(k); |
+ break; |
+ } |
+ } |
+} |
+ |
void AudioContext::handlePreRenderTasks() |
{ |
ASSERT(isAudioThread()); |
@@ -1198,8 +1231,7 @@ void AudioContext::rejectPendingResolvers() |
{ |
ASSERT(isMainThread()); |
- // Audio context is closing down so reject any suspend or resume promises that are still |
- // pending. |
+ // Audio context is closing down so reject any promises that are still pending. |
for (auto& resolver : m_suspendResolvers) { |
resolver->reject(DOMException::create(InvalidStateError, "Audio context is going away")); |
@@ -1273,6 +1305,7 @@ void AudioContext::fireCompletionEvent() |
DEFINE_TRACE(AudioContext) |
{ |
+ visitor->trace(m_audioDecoderResolvers); |
visitor->trace(m_closeResolver); |
visitor->trace(m_offlineResolver); |
visitor->trace(m_renderTarget); |
@@ -1342,6 +1375,9 @@ ScriptPromise AudioContext::closeContext(ScriptState* scriptState) |
"Cannot close a context that is being closed or has already been closed.")); |
} |
+ // Save the current sample rate for any subsequent decodeAudioData calls. |
+ m_closedContextSampleRate = sampleRate(); |
+ |
m_closeResolver = ScriptPromiseResolver::create(scriptState); |
ScriptPromise promise = m_closeResolver->promise(); |