d96028bc2fd3d106b0e44d687dd0193e21ca0f42
[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 "ExceptionOr.h"
29 #include "JSDOMConvert.h"
30 #include "JSDOMGuardedObject.h"
31 #include <JavaScriptCore/CatchScope.h>
32 #include <JavaScriptCore/JSPromiseDeferred.h>
33
34 namespace WebCore {
35
36 class JSDOMWindow;
37
38 class DeferredPromise : public DOMGuarded<JSC::JSPromiseDeferred> {
39 public:
40     enum class Mode {
41         ClearPromiseOnResolve,
42         RetainPromiseOnResolve
43     };
44
45     static RefPtr<DeferredPromise> create(JSC::ExecState& state, JSDOMGlobalObject& globalObject, Mode mode = Mode::ClearPromiseOnResolve)
46     {
47         auto* promiseDeferred = JSC::JSPromiseDeferred::tryCreate(&state, &globalObject);
48         if (!promiseDeferred)
49             return nullptr;
50         return adoptRef(new DeferredPromise(globalObject, *promiseDeferred, mode));
51     }
52
53     static Ref<DeferredPromise> create(JSDOMGlobalObject& globalObject, JSC::JSPromiseDeferred& deferred, Mode mode = Mode::ClearPromiseOnResolve)
54     {
55         return adoptRef(*new DeferredPromise(globalObject, deferred, mode));
56     }
57
58     template<class IDLType>
59     void resolve(typename IDLType::ParameterType value)
60     {
61         if (isSuspended())
62             return;
63         ASSERT(deferred());
64         ASSERT(globalObject());
65         JSC::ExecState* exec = globalObject()->globalExec();
66         JSC::JSLockHolder locker(exec);
67         resolve(*exec, toJS<IDLType>(*exec, *globalObject(), std::forward<typename IDLType::ParameterType>(value)));
68     }
69
70     void resolve()
71     {
72         if (isSuspended())
73             return;
74         ASSERT(deferred());
75         ASSERT(globalObject());
76         JSC::ExecState* exec = globalObject()->globalExec();
77         JSC::JSLockHolder locker(exec);
78         resolve(*exec, JSC::jsUndefined());
79     }
80
81     template<class IDLType>
82     void resolveWithNewlyCreated(typename IDLType::ParameterType value)
83     {
84         if (isSuspended())
85             return;
86         ASSERT(deferred());
87         ASSERT(globalObject());
88         JSC::ExecState* exec = globalObject()->globalExec();
89         JSC::JSLockHolder locker(exec);
90         resolve(*exec, toJSNewlyCreated<IDLType>(*exec, *globalObject(), std::forward<typename IDLType::ParameterType>(value)));
91     }
92
93     template<class IDLType>
94     void resolveCallbackValueWithNewlyCreated(const Function<typename IDLType::InnerParameterType(ScriptExecutionContext&)>& createValue)
95     {
96         if (isSuspended())
97             return;
98         ASSERT(deferred());
99         ASSERT(globalObject());
100         auto* exec = globalObject()->globalExec();
101         JSC::JSLockHolder locker(exec);
102         resolve(*exec, toJSNewlyCreated<IDLType>(*exec, *globalObject(), createValue(*globalObject()->scriptExecutionContext())));
103     }
104
105     template<class IDLType>
106     void reject(typename IDLType::ParameterType value)
107     {
108         if (isSuspended())
109             return;
110         ASSERT(deferred());
111         ASSERT(globalObject());
112         JSC::ExecState* exec = globalObject()->globalExec();
113         JSC::JSLockHolder locker(exec);
114         reject(*exec, toJS<IDLType>(*exec, *globalObject(), std::forward<typename IDLType::ParameterType>(value)));
115     }
116
117     void reject();
118     void reject(std::nullptr_t);
119     void reject(Exception);
120     WEBCORE_EXPORT void reject(ExceptionCode, const String& = { });
121     void reject(const JSC::PrivateName&);
122
123     template<typename Callback>
124     void resolveWithCallback(Callback callback)
125     {
126         if (isSuspended())
127             return;
128         ASSERT(deferred());
129         ASSERT(globalObject());
130         JSC::ExecState* exec = globalObject()->globalExec();
131         JSC::JSLockHolder locker(exec);
132         resolve(*exec, callback(*exec, *globalObject()));
133     }
134
135     template<typename Callback>
136     void rejectWithCallback(Callback callback)
137     {
138         if (isSuspended())
139             return;
140         ASSERT(deferred());
141         ASSERT(globalObject());
142         JSC::ExecState* exec = globalObject()->globalExec();
143         JSC::JSLockHolder locker(exec);
144         reject(*exec, callback(*exec, *globalObject()));
145     }
146
147     JSC::JSValue promise() const;
148
149     void whenSettled(Function<void()>&&);
150
151 private:
152     DeferredPromise(JSDOMGlobalObject& globalObject, JSC::JSPromiseDeferred& deferred, Mode mode)
153         : DOMGuarded<JSC::JSPromiseDeferred>(globalObject, deferred)
154         , m_mode(mode)
155     {
156     }
157
158     JSC::JSPromiseDeferred* deferred() const { return guarded(); }
159
160     WEBCORE_EXPORT void callFunction(JSC::ExecState&, JSC::JSValue function, JSC::JSValue resolution);
161
162     void resolve(JSC::ExecState& state, JSC::JSValue resolution) { callFunction(state, deferred()->resolve(), resolution); }
163     void reject(JSC::ExecState& state, JSC::JSValue resolution) { callFunction(state, deferred()->reject(), resolution); }
164
165     Mode m_mode;
166 };
167
168 class DOMPromiseDeferredBase {
169 public:
170     DOMPromiseDeferredBase(Ref<DeferredPromise>&& genericPromise)
171         : m_promiseDeferred(WTFMove(genericPromise))
172     {
173     }
174
175     DOMPromiseDeferredBase(DOMPromiseDeferredBase&& promise)
176         : m_promiseDeferred(WTFMove(promise.m_promiseDeferred))
177     {
178     }
179
180     DOMPromiseDeferredBase(const DOMPromiseDeferredBase& other)
181         : m_promiseDeferred(other.m_promiseDeferred.copyRef())
182     {
183     }
184
185     DOMPromiseDeferredBase& operator=(const DOMPromiseDeferredBase& other)
186     {
187         m_promiseDeferred = other.m_promiseDeferred.copyRef();
188         return *this;
189     }
190
191     DOMPromiseDeferredBase& operator=(DOMPromiseDeferredBase&& other)
192     {
193         m_promiseDeferred = WTFMove(other.m_promiseDeferred);
194         return *this;
195     }
196
197     void reject()
198     {
199         m_promiseDeferred->reject();
200     }
201
202     template<typename... ErrorType> 
203     void reject(ErrorType&&... error)
204     {
205         m_promiseDeferred->reject(std::forward<ErrorType>(error)...);
206     }
207
208     template<typename IDLType>
209     void rejectType(typename IDLType::ParameterType value)
210     {
211         m_promiseDeferred->reject<IDLType>(std::forward<typename IDLType::ParameterType>(value));
212     }
213
214     JSC::JSValue promise() const { return m_promiseDeferred->promise(); };
215
216     void whenSettled(Function<void()>&& function)
217     {
218         m_promiseDeferred->whenSettled(WTFMove(function));
219     }
220
221 protected:
222     Ref<DeferredPromise> m_promiseDeferred;
223 };
224
225 template<typename IDLType> 
226 class DOMPromiseDeferred : public DOMPromiseDeferredBase {
227 public:
228     using DOMPromiseDeferredBase::DOMPromiseDeferredBase;
229     using DOMPromiseDeferredBase::operator=;
230     using DOMPromiseDeferredBase::promise;
231     using DOMPromiseDeferredBase::reject;
232
233     void resolve(typename IDLType::ParameterType value)
234     { 
235         m_promiseDeferred->resolve<IDLType>(std::forward<typename IDLType::ParameterType>(value));
236     }
237
238     void settle(ExceptionOr<typename IDLType::ParameterType>&& result)
239     {
240         if (result.hasException()) {
241             reject(result.releaseException());
242             return;
243         }
244         resolve(result.releaseReturnValue());
245     }
246 };
247
248 template<> class DOMPromiseDeferred<void> : public DOMPromiseDeferredBase {
249 public:
250     using DOMPromiseDeferredBase::DOMPromiseDeferredBase;
251     using DOMPromiseDeferredBase::operator=;
252     using DOMPromiseDeferredBase::promise;
253     using DOMPromiseDeferredBase::reject;
254
255     void resolve()
256     { 
257         m_promiseDeferred->resolve();
258     }
259
260     void settle(ExceptionOr<void>&& result)
261     {
262         if (result.hasException()) {
263             reject(result.releaseException());
264             return;
265         }
266         resolve();
267     }
268 };
269
270
271 Ref<DeferredPromise> createDeferredPromise(JSC::ExecState&, JSDOMWindow&);
272
273 void fulfillPromiseWithJSON(Ref<DeferredPromise>&&, const String&);
274 void fulfillPromiseWithArrayBuffer(Ref<DeferredPromise>&&, ArrayBuffer*);
275 void fulfillPromiseWithArrayBuffer(Ref<DeferredPromise>&&, const void*, size_t);
276 WEBCORE_EXPORT void rejectPromiseWithExceptionIfAny(JSC::ExecState&, JSDOMGlobalObject&, JSC::JSPromiseDeferred&);
277
278 enum class RejectedPromiseWithTypeErrorCause { NativeGetter, InvalidThis };
279 JSC::EncodedJSValue createRejectedPromiseWithTypeError(JSC::ExecState&, const String&, RejectedPromiseWithTypeErrorCause);
280
281 using PromiseFunction = void(JSC::ExecState&, Ref<DeferredPromise>&&);
282
283 enum class PromiseExecutionScope { WindowOnly, WindowOrWorker };
284
285 template<PromiseFunction promiseFunction, PromiseExecutionScope executionScope>
286 inline JSC::JSValue callPromiseFunction(JSC::ExecState& state)
287 {
288     JSC::VM& vm = state.vm();
289     auto scope = DECLARE_CATCH_SCOPE(vm);
290
291     auto& globalObject = callerGlobalObject(state);
292     JSC::JSPromiseDeferred* promiseDeferred = JSC::JSPromiseDeferred::tryCreate(&state, &globalObject);
293
294     // promiseDeferred can be null when terminating a Worker abruptly.
295     if (executionScope == PromiseExecutionScope::WindowOrWorker && !promiseDeferred)
296         return JSC::jsUndefined();
297
298     promiseFunction(state, DeferredPromise::create(globalObject, *promiseDeferred));
299
300     rejectPromiseWithExceptionIfAny(state, globalObject, *promiseDeferred);
301     EXCEPTION_ASSERT_UNUSED(scope, !scope.exception());
302     return promiseDeferred->promise();
303 }
304
305 template<PromiseExecutionScope executionScope, typename PromiseFunctor>
306 inline JSC::JSValue callPromiseFunction(JSC::ExecState& state, PromiseFunctor functor)
307 {
308     JSC::VM& vm = state.vm();
309     auto scope = DECLARE_CATCH_SCOPE(vm);
310
311     auto& globalObject = callerGlobalObject(state);
312     JSC::JSPromiseDeferred* promiseDeferred = JSC::JSPromiseDeferred::tryCreate(&state, &globalObject);
313
314     // promiseDeferred can be null when terminating a Worker abruptly.
315     if (executionScope == PromiseExecutionScope::WindowOrWorker && !promiseDeferred)
316         return JSC::jsUndefined();
317
318     functor(state, DeferredPromise::create(globalObject, *promiseDeferred));
319
320     rejectPromiseWithExceptionIfAny(state, globalObject, *promiseDeferred);
321     EXCEPTION_ASSERT_UNUSED(scope, !scope.exception());
322     return promiseDeferred->promise();
323 }
324
325 using BindingPromiseFunction = JSC::EncodedJSValue(JSC::ExecState*, Ref<DeferredPromise>&&);
326 template<BindingPromiseFunction bindingFunction>
327 inline void bindingPromiseFunctionAdapter(JSC::ExecState& state, Ref<DeferredPromise>&& promise)
328 {
329     bindingFunction(&state, WTFMove(promise));
330 }
331
332 template<BindingPromiseFunction bindingPromiseFunction, PromiseExecutionScope executionScope>
333 inline JSC::JSValue callPromiseFunction(JSC::ExecState& state)
334 {
335     return callPromiseFunction<bindingPromiseFunctionAdapter<bindingPromiseFunction>, executionScope>(state);
336 }
337
338 } // namespace WebCore