Title: [185407] trunk/Source/WebCore
Revision
185407
Author
[email protected]
Date
2015-06-10 01:10:44 -0700 (Wed, 10 Jun 2015)

Log Message

Refactor AudioContext implementation to enable automatic binding generation of promise-based methods
https://bugs.webkit.org/show_bug.cgi?id=145223

Reviewed by Darin Adler.

Introducing DOMPromise as a typed wrapper above DeferredWrapper.
This DOMPromise allows constraining Promise resolution/rejection to a single resolution type and rejection type.
This might be useful for typed callback usage of promise based APIs, like getUserMedia() or ReadableStreamReader.read().

Applying DOMPromise to AudioContext close, suspend and resume.
Changed binding code to represent what could be automatically generated by binding generator.
Fixing unneeded copies of std::function callbacks.

Disabling DOMPromise copy constructors to lower chances that resolution/rejection is done twice on the same object.

Covered by existing tests.

* Modules/webaudio/AudioContext.cpp:
(WebCore::AudioContext::addReaction):
(WebCore::AudioContext::setState):
(WebCore::AudioContext::suspend):
(WebCore::AudioContext::resume):
(WebCore::AudioContext::close):
* Modules/webaudio/AudioContext.h:
* bindings/js/JSAudioContextCustom.cpp:
(WebCore::JSAudioContext::suspend):
(WebCore::JSAudioContext::resume):
(WebCore::JSAudioContext::close):
* bindings/js/JSDOMPromise.cpp:
(WebCore::DeferredWrapper::callFunction):
* bindings/js/JSDOMPromise.h:
(WebCore::DeferredWrapper::resolve):
(WebCore::DeferredWrapper::reject):
(WebCore::DOMPromise::DOMPromise):
(WebCore::DOMPromise::resolve):
(WebCore::DOMPromise::reject):

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (185406 => 185407)


--- trunk/Source/WebCore/ChangeLog	2015-06-10 08:09:02 UTC (rev 185406)
+++ trunk/Source/WebCore/ChangeLog	2015-06-10 08:10:44 UTC (rev 185407)
@@ -1,3 +1,42 @@
+2015-06-10  Youenn Fablet  <[email protected]>
+
+        Refactor AudioContext implementation to enable automatic binding generation of promise-based methods
+        https://bugs.webkit.org/show_bug.cgi?id=145223
+
+        Reviewed by Darin Adler.
+
+        Introducing DOMPromise as a typed wrapper above DeferredWrapper.
+        This DOMPromise allows constraining Promise resolution/rejection to a single resolution type and rejection type. 
+        This might be useful for typed callback usage of promise based APIs, like getUserMedia() or ReadableStreamReader.read().
+
+        Applying DOMPromise to AudioContext close, suspend and resume.
+        Changed binding code to represent what could be automatically generated by binding generator.
+        Fixing unneeded copies of std::function callbacks.
+
+        Disabling DOMPromise copy constructors to lower chances that resolution/rejection is done twice on the same object.
+
+        Covered by existing tests.
+
+        * Modules/webaudio/AudioContext.cpp:
+        (WebCore::AudioContext::addReaction):
+        (WebCore::AudioContext::setState):
+        (WebCore::AudioContext::suspend):
+        (WebCore::AudioContext::resume):
+        (WebCore::AudioContext::close):
+        * Modules/webaudio/AudioContext.h:
+        * bindings/js/JSAudioContextCustom.cpp:
+        (WebCore::JSAudioContext::suspend):
+        (WebCore::JSAudioContext::resume):
+        (WebCore::JSAudioContext::close):
+        * bindings/js/JSDOMPromise.cpp:
+        (WebCore::DeferredWrapper::callFunction):
+        * bindings/js/JSDOMPromise.h:
+        (WebCore::DeferredWrapper::resolve):
+        (WebCore::DeferredWrapper::reject):
+        (WebCore::DOMPromise::DOMPromise):
+        (WebCore::DOMPromise::resolve):
+        (WebCore::DOMPromise::reject):
+
 2015-06-10  Xabier Rodriguez Calvar  <[email protected]> and Youenn Fablet <[email protected]>
 
         [Streams API] Implement pulling of a source by a ReadableStream

Modified: trunk/Source/WebCore/Modules/webaudio/AudioContext.cpp (185406 => 185407)


--- trunk/Source/WebCore/Modules/webaudio/AudioContext.cpp	2015-06-10 08:09:02 UTC (rev 185406)
+++ trunk/Source/WebCore/Modules/webaudio/AudioContext.cpp	2015-06-10 08:10:44 UTC (rev 185407)
@@ -51,6 +51,7 @@
 #include "GenericEventQueue.h"
 #include "HRTFDatabaseLoader.h"
 #include "HRTFPanner.h"
+#include "JSDOMPromise.h"
 #include "OfflineAudioCompletionEvent.h"
 #include "OfflineAudioDestinationNode.h"
 #include "OscillatorNode.h"
@@ -277,13 +278,13 @@
     return m_isInitialized;
 }
 
-void AudioContext::addReaction(State state, std::function<void()> reaction)
+void AudioContext::addReaction(State state, Promise&& promise)
 {
     size_t stateIndex = static_cast<size_t>(state);
     if (stateIndex >= m_stateReactions.size())
         m_stateReactions.resize(stateIndex + 1);
 
-    m_stateReactions[stateIndex].append(reaction);
+    m_stateReactions[stateIndex].append(WTF::move(promise));
 }
 
 void AudioContext::setState(State state)
@@ -298,11 +299,11 @@
     if (stateIndex >= m_stateReactions.size())
         return;
 
-    Vector<std::function<void()>> reactions;
+    Vector<Promise> reactions;
     m_stateReactions[stateIndex].swap(reactions);
 
-    for (auto& reaction : reactions)
-        reaction();
+    for (auto& promise : reactions)
+        promise.resolve(nullptr);
 }
 
 const AtomicString& AudioContext::state() const
@@ -1096,27 +1097,24 @@
     --m_activeSourceCount;
 }
 
-void AudioContext::suspendContext(std::function<void()> successCallback, FailureCallback failureCallback)
+void AudioContext::suspend(Promise&& promise)
 {
-    ASSERT(successCallback);
-    ASSERT(failureCallback);
-
     if (isOfflineContext()) {
-        failureCallback(INVALID_STATE_ERR);
+        promise.reject(INVALID_STATE_ERR);
         return;
     }
 
     if (m_state == State::Suspended) {
-        successCallback();
+        promise.resolve(nullptr);
         return;
     }
 
     if (m_state == State::Closed || m_state == State::Interrupted || !m_destinationNode) {
-        failureCallback(0);
+        promise.reject(0);
         return;
     }
 
-    addReaction(State::Suspended, successCallback);
+    addReaction(State::Suspended, WTF::move(promise));
 
     if (!willPausePlayback())
         return;
@@ -1129,27 +1127,24 @@
     });
 }
 
-void AudioContext::resumeContext(std::function<void()> successCallback, FailureCallback failureCallback)
+void AudioContext::resume(Promise&& promise)
 {
-    ASSERT(successCallback);
-    ASSERT(failureCallback);
-
     if (isOfflineContext()) {
-        failureCallback(INVALID_STATE_ERR);
+        promise.reject(INVALID_STATE_ERR);
         return;
     }
 
     if (m_state == State::Running) {
-        successCallback();
+        promise.resolve(nullptr);
         return;
     }
 
     if (m_state == State::Closed || !m_destinationNode) {
-        failureCallback(0);
+        promise.reject(0);
         return;
     }
 
-    addReaction(State::Running, successCallback);
+    addReaction(State::Running, WTF::move(promise));
 
     if (!willBeginPlayback())
         return;
@@ -1162,27 +1157,24 @@
     });
 }
 
-void AudioContext::closeContext(std::function<void()> successCallback, FailureCallback failureCallback)
+void AudioContext::close(Promise&& promise)
 {
-    ASSERT(successCallback);
-    ASSERT(failureCallback);
-
     if (isOfflineContext()) {
-        failureCallback(INVALID_STATE_ERR);
+        promise.reject(INVALID_STATE_ERR);
         return;
     }
 
     if (m_state == State::Closed || !m_destinationNode) {
-        successCallback();
+        promise.resolve(nullptr);
         return;
     }
 
-    addReaction(State::Closed, successCallback);
+    addReaction(State::Closed, WTF::move(promise));
 
     lazyInitialize();
 
     RefPtr<AudioContext> strongThis(this);
-    m_destinationNode->close([strongThis, successCallback] {
+    m_destinationNode->close([strongThis] {
         strongThis->setState(State::Closed);
         strongThis->uninitialize();
     });

Modified: trunk/Source/WebCore/Modules/webaudio/AudioContext.h (185406 => 185407)


--- trunk/Source/WebCore/Modules/webaudio/AudioContext.h	2015-06-10 08:09:02 UTC (rev 185406)
+++ trunk/Source/WebCore/Modules/webaudio/AudioContext.h	2015-06-10 08:10:44 UTC (rev 185407)
@@ -31,6 +31,7 @@
 #include "AudioDestinationNode.h"
 #include "EventListener.h"
 #include "EventTarget.h"
+#include "JSDOMPromise.h"
 #include "MediaCanStartListener.h"
 #include "MediaProducer.h"
 #include "PlatformMediaSession.h"
@@ -106,11 +107,15 @@
 
     AudioListener* listener() { return m_listener.get(); }
 
-    typedef std::function<void(ExceptionCode)> FailureCallback;
+    using ActiveDOMObject::suspend;
+    using ActiveDOMObject::resume;
 
-    void suspendContext(std::function<void()>, FailureCallback);
-    void resumeContext(std::function<void()>, FailureCallback);
-    void closeContext(std::function<void()>, FailureCallback);
+    typedef DOMPromise<std::nullptr_t, ExceptionCode> Promise;
+
+    void suspend(Promise&&);
+    void resume(Promise&&);
+    void close(Promise&&);
+
     const AtomicString& state() const;
 
     // The AudioNode create methods are called on the main thread (from _javascript_).
@@ -324,7 +329,7 @@
     void handleDirtyAudioSummingJunctions();
     void handleDirtyAudioNodeOutputs();
 
-    void addReaction(State, std::function<void()>);
+    void addReaction(State, Promise&&);
     void updateAutomaticPullNodes();
 
     // Only accessed in the audio thread.
@@ -361,7 +366,7 @@
     Vector<AudioNode*> m_renderingAutomaticPullNodes;
     // Only accessed in the audio thread.
     Vector<AudioNode*> m_deferredFinishDerefList;
-    Vector<Vector<std::function<void()>>> m_stateReactions;
+    Vector<Vector<Promise>> m_stateReactions;
 
     std::unique_ptr<PlatformMediaSession> m_mediaSession;
     std::unique_ptr<GenericEventQueue> m_eventQueue;

Modified: trunk/Source/WebCore/bindings/js/JSAudioContextCustom.cpp (185406 => 185407)


--- trunk/Source/WebCore/bindings/js/JSAudioContextCustom.cpp	2015-06-10 08:09:02 UTC (rev 185406)
+++ trunk/Source/WebCore/bindings/js/JSAudioContextCustom.cpp	2015-06-10 08:10:44 UTC (rev 185407)
@@ -111,50 +111,29 @@
     return JSValue::encode(CREATE_DOM_WRAPPER(jsConstructor->globalObject(), AudioContext, audioContext.get()));
 }
 
-JSValue JSAudioContext::suspend(ExecState* exec)
+JSValue JSAudioContext::suspend(ExecState* state)
 {
-    JSPromiseDeferred* promiseDeferred = JSPromiseDeferred::create(exec, globalObject());
-    DeferredWrapper wrapper(exec, globalObject(), promiseDeferred);
-    auto successCallback = [wrapper]() mutable {
-        wrapper.resolve(nullptr);
-    };
-    auto failureCallback = [wrapper](ExceptionCode value) mutable {
-        wrapper.reject(value);
-    };
+    JSPromiseDeferred* promiseDeferred = JSPromiseDeferred::create(state, globalObject());
 
-    impl().suspendContext(WTF::move(successCallback), WTF::move(failureCallback));
+    impl().suspend(DeferredWrapper(state, globalObject(), promiseDeferred));
 
     return promiseDeferred->promise();
 }
 
-JSValue JSAudioContext::resume(ExecState* exec)
+JSValue JSAudioContext::resume(ExecState* state)
 {
-    JSPromiseDeferred* promiseDeferred = JSPromiseDeferred::create(exec, globalObject());
-    DeferredWrapper wrapper(exec, globalObject(), promiseDeferred);
-    auto successCallback = [wrapper]() mutable {
-        wrapper.resolve(nullptr);
-    };
-    auto failureCallback = [wrapper](ExceptionCode value) mutable {
-        wrapper.reject(value);
-    };
+    JSPromiseDeferred* promiseDeferred = JSPromiseDeferred::create(state, globalObject());
 
-    impl().resumeContext(WTF::move(successCallback), WTF::move(failureCallback));
+    impl().resume(DeferredWrapper(state, globalObject(), promiseDeferred));
 
     return promiseDeferred->promise();
 }
 
-JSValue JSAudioContext::close(ExecState* exec)
+JSValue JSAudioContext::close(ExecState* state)
 {
-    JSPromiseDeferred* promiseDeferred = JSPromiseDeferred::create(exec, globalObject());
-    DeferredWrapper wrapper(exec, globalObject(), promiseDeferred);
-    auto successCallback = [wrapper]() mutable {
-        wrapper.resolve(nullptr);
-    };
-    auto failureCallback = [wrapper](ExceptionCode value) mutable {
-        wrapper.reject(value);
-    };
+    JSPromiseDeferred* promiseDeferred = JSPromiseDeferred::create(state, globalObject());
 
-    impl().closeContext(WTF::move(successCallback), WTF::move(failureCallback));
+    impl().close(DeferredWrapper(state, globalObject(), promiseDeferred));
 
     return promiseDeferred->promise();
 }

Modified: trunk/Source/WebCore/bindings/js/JSDOMPromise.cpp (185406 => 185407)


--- trunk/Source/WebCore/bindings/js/JSDOMPromise.cpp	2015-06-10 08:09:02 UTC (rev 185406)
+++ trunk/Source/WebCore/bindings/js/JSDOMPromise.cpp	2015-06-10 08:10:44 UTC (rev 185407)
@@ -28,6 +28,8 @@
 
 #if ENABLE(PROMISES)
 
+#include "ExceptionCode.h"
+
 using namespace JSC;
 
 namespace WebCore {
@@ -45,40 +47,21 @@
     return m_deferred->promise();
 }
 
-void DeferredWrapper::resolve(ExecState* exec, JSValue resolution)
+void DeferredWrapper::callFunction(ExecState& exec, JSValue function, JSValue resolution)
 {
-    JSValue deferredResolve = m_deferred->resolve();
+    CallData callData;
+    CallType callType = getCallData(function, callData);
+    ASSERT(callType != CallTypeNone);
 
-    CallData resolveCallData;
-    CallType resolveCallType = getCallData(deferredResolve, resolveCallData);
-    ASSERT(resolveCallType != CallTypeNone);
-
     MarkedArgumentBuffer arguments;
     arguments.append(resolution);
 
-    call(exec, deferredResolve, resolveCallType, resolveCallData, jsUndefined(), arguments);
+    call(&exec, function, callType, callData, jsUndefined(), arguments);
 
     m_globalObject.clear();
     m_deferred.clear();
 }
 
-void DeferredWrapper::reject(ExecState* exec, JSValue reason)
-{
-    JSValue deferredReject = m_deferred->reject();
-
-    CallData rejectCallData;
-    CallType rejectCallType = getCallData(deferredReject, rejectCallData);
-    ASSERT(rejectCallType != CallTypeNone);
-
-    MarkedArgumentBuffer arguments;
-    arguments.append(reason);
-
-    call(exec, deferredReject, rejectCallType, rejectCallData, jsUndefined(), arguments);
-
-    m_globalObject.clear();
-    m_deferred.clear();
 }
 
-}
-
 #endif // ENABLE(PROMISES)

Modified: trunk/Source/WebCore/bindings/js/JSDOMPromise.h (185406 => 185407)


--- trunk/Source/WebCore/bindings/js/JSDOMPromise.h	2015-06-10 08:09:02 UTC (rev 185406)
+++ trunk/Source/WebCore/bindings/js/JSDOMPromise.h	2015-06-10 08:10:44 UTC (rev 185407)
@@ -49,13 +49,30 @@
     JSC::JSObject* promise() const;
 
 private:
-    void resolve(JSC::ExecState*, JSC::JSValue);
-    void reject(JSC::ExecState*, JSC::JSValue);
+    void callFunction(JSC::ExecState&, JSC::JSValue function, JSC::JSValue resolution);
+    void resolve(JSC::ExecState& state, JSC::JSValue resolution) { callFunction(state, m_deferred->resolve(), resolution); }
+    void reject(JSC::ExecState& state, JSC::JSValue resolution) { callFunction(state, m_deferred->reject(), resolution); }
 
     JSC::Strong<JSDOMGlobalObject> m_globalObject;
     JSC::Strong<JSC::JSPromiseDeferred> m_deferred;
 };
 
+template <typename Value, typename Error>
+class DOMPromise {
+public:
+    DOMPromise(DeferredWrapper&& wrapper) : m_wrapper(WTF::move(wrapper)) { }
+    DOMPromise(DOMPromise&& promise) : m_wrapper(WTF::move(promise.m_wrapper)) { }
+
+    DOMPromise(const DOMPromise&)= delete;
+    DOMPromise& operator=(DOMPromise const&) = delete;
+
+    void resolve(const Value& value) { m_wrapper.resolve<Value>(value); }
+    void reject(const Error& error) { m_wrapper.reject<Error>(error); }
+
+private:
+    DeferredWrapper m_wrapper;
+};
+
 template<class ResolveResultType>
 inline void DeferredWrapper::resolve(const ResolveResultType& result)
 {
@@ -63,7 +80,7 @@
     ASSERT(m_globalObject);
     JSC::ExecState* exec = m_globalObject->globalExec();
     JSC::JSLockHolder locker(exec);
-    resolve(exec, toJS(exec, m_globalObject.get(), result));
+    resolve(*exec, toJS(exec, m_globalObject.get(), result));
 }
 
 template<class RejectResultType>
@@ -73,7 +90,7 @@
     ASSERT(m_globalObject);
     JSC::ExecState* exec = m_globalObject->globalExec();
     JSC::JSLockHolder locker(exec);
-    reject(exec, toJS(exec, m_globalObject.get(), result));
+    reject(*exec, toJS(exec, m_globalObject.get(), result));
 }
 
 template<>
@@ -83,7 +100,7 @@
     ASSERT(m_globalObject);
     JSC::ExecState* exec = m_globalObject->globalExec();
     JSC::JSLockHolder locker(exec);
-    reject(exec, JSC::jsNull());
+    reject(*exec, JSC::jsNull());
 }
 
 template<>
@@ -93,7 +110,7 @@
     ASSERT(m_globalObject);
     JSC::ExecState* exec = m_globalObject->globalExec();
     JSC::JSLockHolder locker(exec);
-    reject(exec, value);
+    reject(*exec, value);
 }
 
 template<>
@@ -103,7 +120,7 @@
     ASSERT(m_globalObject);
     JSC::ExecState* exec = m_globalObject->globalExec();
     JSC::JSLockHolder locker(exec);
-    reject(exec, createDOMException(exec, ec));
+    reject(*exec, createDOMException(exec, ec));
 }
 
 template<>
@@ -113,7 +130,7 @@
     ASSERT(m_globalObject);
     JSC::ExecState* exec = m_globalObject->globalExec();
     JSC::JSLockHolder locker(exec);
-    resolve(exec, jsString(exec, result));
+    resolve(*exec, jsString(exec, result));
 }
 
 template<>
@@ -123,7 +140,7 @@
     ASSERT(m_globalObject);
     JSC::ExecState* exec = m_globalObject->globalExec();
     JSC::JSLockHolder locker(exec);
-    resolve(exec, JSC::jsBoolean(result));
+    resolve(*exec, JSC::jsBoolean(result));
 }
 
 template<>
@@ -133,7 +150,7 @@
     ASSERT(m_globalObject);
     JSC::ExecState* exec = m_globalObject->globalExec();
     JSC::JSLockHolder locker(exec);
-    resolve(exec, value);
+    resolve(*exec, value);
 }
 template<>
 inline void DeferredWrapper::resolve<Vector<unsigned char>>(const Vector<unsigned char>& result)
@@ -143,7 +160,7 @@
     JSC::ExecState* exec = m_globalObject->globalExec();
     JSC::JSLockHolder locker(exec);
     RefPtr<ArrayBuffer> buffer = ArrayBuffer::create(result.data(), result.size());
-    resolve(exec, toJS(exec, m_globalObject.get(), buffer.get()));
+    resolve(*exec, toJS(exec, m_globalObject.get(), buffer.get()));
 }
 
 template<>
@@ -153,7 +170,7 @@
     ASSERT(m_globalObject);
     JSC::ExecState* exec = m_globalObject->globalExec();
     JSC::JSLockHolder locker(exec);
-    resolve(exec, JSC::jsUndefined());
+    resolve(*exec, JSC::jsUndefined());
 }
 
 template<>
@@ -163,7 +180,7 @@
     ASSERT(m_globalObject);
     JSC::ExecState* exec = m_globalObject->globalExec();
     JSC::JSLockHolder locker(exec);
-    reject(exec, jsString(exec, result));
+    reject(*exec, jsString(exec, result));
 }
 
 }
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to