36996cd705c03ed87ecf527c44e2bc263817b5eb
[WebKit-https.git] / Source / WebCore / bindings / js / JSDOMPromiseDeferred.h
1 /*
2  * Copyright (C) 2013 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #pragma once
27
28 #include "JSDOMConvert.h"
29 #include "JSDOMGuardedObject.h"
30 #include <runtime/CatchScope.h>
31 #include <runtime/JSPromiseDeferred.h>
32
33 namespace WebCore {
34
35 class DeferredPromise : public DOMGuarded<JSC::JSPromiseDeferred> {
36 public:
37     enum class Mode {
38         ClearPromiseOnResolve,
39         RetainPromiseOnResolve
40     };
41
42     static RefPtr<DeferredPromise> create(JSC::ExecState& state, JSDOMGlobalObject& globalObject, Mode mode = Mode::ClearPromiseOnResolve)
43     {
44         auto* promiseDeferred = JSC::JSPromiseDeferred::create(&state, &globalObject);
45         if (!promiseDeferred)
46             return nullptr;
47         return adoptRef(new DeferredPromise(globalObject, *promiseDeferred, mode));
48     }
49
50     static Ref<DeferredPromise> create(JSDOMGlobalObject& globalObject, JSC::JSPromiseDeferred& deferred, Mode mode = Mode::ClearPromiseOnResolve)
51     {
52         return adoptRef(*new DeferredPromise(globalObject, deferred, mode));
53     }
54
55     template<class IDLType>
56     void resolve(typename IDLType::ParameterType value)
57     {
58         if (isSuspended())
59             return;
60         ASSERT(deferred());
61         ASSERT(globalObject());
62         JSC::ExecState* exec = globalObject()->globalExec();
63         JSC::JSLockHolder locker(exec);
64         resolve(*exec, toJS<IDLType>(*exec, *globalObject(), std::forward<typename IDLType::ParameterType>(value)));
65     }
66
67     void resolve()
68     {
69         if (isSuspended())
70             return;
71         ASSERT(deferred());
72         ASSERT(globalObject());
73         JSC::ExecState* exec = globalObject()->globalExec();
74         JSC::JSLockHolder locker(exec);
75         resolve(*exec, JSC::jsUndefined());
76     }
77
78     template<class IDLType>
79     void resolveWithNewlyCreated(typename IDLType::ParameterType value)
80     {
81         if (isSuspended())
82             return;
83         ASSERT(deferred());
84         ASSERT(globalObject());
85         JSC::ExecState* exec = globalObject()->globalExec();
86         JSC::JSLockHolder locker(exec);
87         resolve(*exec, toJSNewlyCreated<IDLType>(*exec, *globalObject(), std::forward<typename IDLType::ParameterType>(value)));
88     }
89
90     template<class IDLType>
91     void reject(typename IDLType::ParameterType value)
92     {
93         if (isSuspended())
94             return;
95         ASSERT(deferred());
96         ASSERT(globalObject());
97         JSC::ExecState* exec = globalObject()->globalExec();
98         JSC::JSLockHolder locker(exec);
99         reject(*exec, toJS<IDLType>(*exec, *globalObject(), std::forward<typename IDLType::ParameterType>(value)));
100     }
101
102     void reject();
103     void reject(std::nullptr_t);
104     void reject(Exception);
105     WEBCORE_EXPORT void reject(ExceptionCode, const String& = { });
106     void reject(const JSC::PrivateName&);
107
108     template<typename Callback>
109     void resolveWithCallback(Callback callback)
110     {
111         if (isSuspended())
112             return;
113         ASSERT(deferred());
114         ASSERT(globalObject());
115         JSC::ExecState* exec = globalObject()->globalExec();
116         JSC::JSLockHolder locker(exec);
117         resolve(*exec, callback(*exec, *globalObject()));
118     }
119
120     template<typename Callback>
121     void rejectWithCallback(Callback callback)
122     {
123         if (isSuspended())
124             return;
125         ASSERT(deferred());
126         ASSERT(globalObject());
127         JSC::ExecState* exec = globalObject()->globalExec();
128         JSC::JSLockHolder locker(exec);
129         reject(*exec, callback(*exec, *globalObject()));
130     }
131
132     JSC::JSValue promise() const;
133
134 private:
135     DeferredPromise(JSDOMGlobalObject& globalObject, JSC::JSPromiseDeferred& deferred, Mode mode)
136         : DOMGuarded<JSC::JSPromiseDeferred>(globalObject, deferred)
137         , m_mode(mode)
138     {
139     }
140
141     JSC::JSPromiseDeferred* deferred() const { return guarded(); }
142
143     WEBCORE_EXPORT void callFunction(JSC::ExecState&, JSC::JSValue function, JSC::JSValue resolution);
144     void resolve(JSC::ExecState& state, JSC::JSValue resolution) { callFunction(state, deferred()->resolve(), resolution); }
145     void reject(JSC::ExecState& state, JSC::JSValue resolution) { callFunction(state, deferred()->reject(), resolution); }
146
147     Mode m_mode;
148 };
149
150 class DOMPromiseDeferredBase {
151 public:
152     DOMPromiseDeferredBase(Ref<DeferredPromise>&& genericPromise)
153         : m_promiseDeferred(WTFMove(genericPromise))
154     {
155     }
156
157     DOMPromiseDeferredBase(DOMPromiseDeferredBase&& promise)
158         : m_promiseDeferred(WTFMove(promise.m_promiseDeferred))
159     {
160     }
161
162     DOMPromiseDeferredBase(const DOMPromiseDeferredBase& other)
163         : m_promiseDeferred(other.m_promiseDeferred.copyRef())
164     {
165     }
166
167     DOMPromiseDeferredBase& operator=(const DOMPromiseDeferredBase& other)
168     {
169         m_promiseDeferred = other.m_promiseDeferred.copyRef();
170         return *this;
171     }
172
173     DOMPromiseDeferredBase& operator=(DOMPromiseDeferredBase&& other)
174     {
175         m_promiseDeferred = WTFMove(other.m_promiseDeferred);
176         return *this;
177     }
178
179     void reject()
180     {
181         m_promiseDeferred->reject();
182     }
183
184     template<typename... ErrorType> 
185     void reject(ErrorType&&... error)
186     {
187         m_promiseDeferred->reject(std::forward<ErrorType>(error)...);
188     }
189
190     template<typename IDLType>
191     void rejectType(typename IDLType::ParameterType value)
192     {
193         m_promiseDeferred->reject<IDLType>(std::forward<typename IDLType::ParameterType>(value));
194     }
195
196     JSC::JSValue promise() const { return m_promiseDeferred->promise(); };
197
198 protected:
199     Ref<DeferredPromise> m_promiseDeferred;
200 };
201
202 template<typename IDLType> 
203 class DOMPromiseDeferred : public DOMPromiseDeferredBase {
204 public:
205     using DOMPromiseDeferredBase::DOMPromiseDeferredBase;
206     using DOMPromiseDeferredBase::operator=;
207     using DOMPromiseDeferredBase::promise;
208     using DOMPromiseDeferredBase::reject;
209
210     void resolve(typename IDLType::ParameterType value)
211     { 
212         m_promiseDeferred->resolve<IDLType>(std::forward<typename IDLType::ParameterType>(value));
213     }
214 };
215
216 template<> class DOMPromiseDeferred<void> : public DOMPromiseDeferredBase {
217 public:
218     using DOMPromiseDeferredBase::DOMPromiseDeferredBase;
219     using DOMPromiseDeferredBase::operator=;
220     using DOMPromiseDeferredBase::promise;
221     using DOMPromiseDeferredBase::reject;
222
223     void resolve()
224     { 
225         m_promiseDeferred->resolve();
226     }
227 };
228
229
230 Ref<DeferredPromise> createDeferredPromise(JSC::ExecState&, JSDOMWindow&);
231
232 void fulfillPromiseWithJSON(Ref<DeferredPromise>&&, const String&);
233 void fulfillPromiseWithArrayBuffer(Ref<DeferredPromise>&&, ArrayBuffer*);
234 void fulfillPromiseWithArrayBuffer(Ref<DeferredPromise>&&, const void*, size_t);
235 WEBCORE_EXPORT void rejectPromiseWithExceptionIfAny(JSC::ExecState&, JSDOMGlobalObject&, JSC::JSPromiseDeferred&);
236 JSC::EncodedJSValue createRejectedPromiseWithTypeError(JSC::ExecState&, const String&);
237
238 using PromiseFunction = void(JSC::ExecState&, Ref<DeferredPromise>&&);
239
240 enum class PromiseExecutionScope { WindowOnly, WindowOrWorker };
241
242 template<PromiseFunction promiseFunction, PromiseExecutionScope executionScope>
243 inline JSC::JSValue callPromiseFunction(JSC::ExecState& state)
244 {
245     JSC::VM& vm = state.vm();
246     auto scope = DECLARE_CATCH_SCOPE(vm);
247
248     JSDOMGlobalObject& globalObject = *JSC::jsCast<JSDOMGlobalObject*>(state.lexicalGlobalObject());
249     JSC::JSPromiseDeferred* promiseDeferred = JSC::JSPromiseDeferred::create(&state, &globalObject);
250
251     // promiseDeferred can be null when terminating a Worker abruptly.
252     if (executionScope == PromiseExecutionScope::WindowOrWorker && !promiseDeferred)
253         return JSC::jsUndefined();
254
255     promiseFunction(state, DeferredPromise::create(globalObject, *promiseDeferred));
256
257     rejectPromiseWithExceptionIfAny(state, globalObject, *promiseDeferred);
258     ASSERT_UNUSED(scope, !scope.exception());
259     return promiseDeferred->promise();
260 }
261
262 template<PromiseExecutionScope executionScope, typename PromiseFunctor>
263 inline JSC::JSValue callPromiseFunction(JSC::ExecState& state, PromiseFunctor functor)
264 {
265     JSC::VM& vm = state.vm();
266     auto scope = DECLARE_CATCH_SCOPE(vm);
267
268     JSDOMGlobalObject& globalObject = *JSC::jsCast<JSDOMGlobalObject*>(state.lexicalGlobalObject());
269     JSC::JSPromiseDeferred* promiseDeferred = JSC::JSPromiseDeferred::create(&state, &globalObject);
270
271     // promiseDeferred can be null when terminating a Worker abruptly.
272     if (executionScope == PromiseExecutionScope::WindowOrWorker && !promiseDeferred)
273         return JSC::jsUndefined();
274
275     functor(state, DeferredPromise::create(globalObject, *promiseDeferred));
276
277     rejectPromiseWithExceptionIfAny(state, globalObject, *promiseDeferred);
278     ASSERT_UNUSED(scope, !scope.exception());
279     return promiseDeferred->promise();
280 }
281
282 using BindingPromiseFunction = JSC::EncodedJSValue(JSC::ExecState*, Ref<DeferredPromise>&&);
283 template<BindingPromiseFunction bindingFunction>
284 inline void bindingPromiseFunctionAdapter(JSC::ExecState& state, Ref<DeferredPromise>&& promise)
285 {
286     bindingFunction(&state, WTFMove(promise));
287 }
288
289 template<BindingPromiseFunction bindingPromiseFunction, PromiseExecutionScope executionScope>
290 inline JSC::JSValue callPromiseFunction(JSC::ExecState& state)
291 {
292     return callPromiseFunction<bindingPromiseFunctionAdapter<bindingPromiseFunction>, executionScope>(state);
293 }
294
295 } // namespace WebCore