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