[WTF] Import std::optional reference implementation as WTF::Optional
[WebKit-https.git] / Source / WebCore / bindings / js / JSDOMPromise.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 "ActiveDOMCallback.h"
29 #include "JSDOMBinding.h"
30 #include <heap/StrongInlines.h>
31 #include <runtime/JSPromiseDeferred.h>
32
33 namespace WebCore {
34
35 template<typename DOMClass>
36 struct TypeInspector {
37 private:
38     template<typename T> static constexpr auto testIsVector(int) -> decltype(std::declval<T>().shrinkToFit(), bool()) { return true; }
39     template<typename T> static constexpr bool testIsVector(...) { return false; }
40
41     template<typename T> static constexpr auto testIsRef(int) -> decltype(T::isRef) { return true; }
42     template<typename T> static constexpr bool testIsRef(...) { return false; }
43
44     template<typename T> static constexpr auto testIsRefPtr(int) -> decltype(T::isRefPtr) { return true; }
45     template<typename T> static constexpr bool testIsRefPtr(...) { return false; }
46
47 public:
48     static constexpr bool isRefOrRefPtr = testIsRef<DOMClass>(0) || testIsRefPtr<DOMClass>(0);
49     static constexpr bool isPassByValueType = std::is_pointer<DOMClass>::value
50         || std::is_same<DOMClass, std::nullptr_t>::value
51         || std::is_same<DOMClass, JSC::JSValue>::value
52         || std::is_same<DOMClass, bool>::value;
53     static constexpr bool isPassByConstRefType = testIsVector<DOMClass>(0)
54         || std::is_same<DOMClass, String>::value;
55 };
56
57 template<typename DOMClass, typename Enable = void>
58 struct PromiseResultInspector {
59 public:
60     static constexpr bool passByValue = false;
61     static constexpr bool passByRef = true;
62     static constexpr bool passByURef = false;
63     static constexpr bool passByConstRef = false;
64
65     typedef DOMClass& Type;
66 };
67
68 template<>
69 struct PromiseResultInspector<void> {
70 public:
71     typedef int Type;
72 };
73
74 template<typename DOMClass>
75 struct PromiseResultInspector<DOMClass, typename std::enable_if<TypeInspector<DOMClass>::isPassByValueType>::type> {
76 public:
77     static constexpr bool passByValue = true;
78     static constexpr bool passByRef = false;
79     static constexpr bool passByURef = false;
80     static constexpr bool passByConstRef = false;
81
82     typedef DOMClass Type;
83 };
84
85 template<typename DOMClass>
86 struct PromiseResultInspector<DOMClass, typename std::enable_if<TypeInspector<DOMClass>::isPassByConstRefType>::type> {
87 public:
88     static constexpr bool passByValue = false;
89     static constexpr bool passByRef = false;
90     static constexpr bool passByURef = false;
91     static constexpr bool passByConstRef = true;
92
93     typedef const DOMClass& Type;
94 };
95
96 template<typename DOMClass>
97 struct PromiseResultInspector<DOMClass, typename std::enable_if<TypeInspector<DOMClass>::isRefOrRefPtr>::type> {
98     static constexpr bool passByValue = false;
99     static constexpr bool passByRef = false;
100     static constexpr bool passByURef = true;
101     static constexpr bool passByConstRef = false;
102 };
103
104 class DeferredPromise : public RefCounted<DeferredPromise>, public ActiveDOMCallback {
105 public:
106     static Ref<DeferredPromise> create(JSDOMGlobalObject& globalObject, JSC::JSPromiseDeferred& deferred)
107     {
108         return adoptRef(*new DeferredPromise(globalObject, deferred));
109     }
110
111     ~DeferredPromise();
112
113     template<class ResolveResultType> typename std::enable_if<PromiseResultInspector<ResolveResultType>::passByValue, void>::type
114     resolve(ResolveResultType result) { resolveWithValue(result); }
115     template<class ResolveResultType> typename std::enable_if<PromiseResultInspector<ResolveResultType>::passByRef, void>::type
116     resolve(ResolveResultType& result) { resolveWithValue(result); }
117     template<class ResolveResultType> typename std::enable_if<PromiseResultInspector<ResolveResultType>::passByURef, void>::type
118     resolve(ResolveResultType&& result) { resolveWithValue(std::forward<ResolveResultType>(result)); }
119     template<class ResolveResultType> typename std::enable_if<PromiseResultInspector<ResolveResultType>::passByConstRef, void>::type
120     resolve(const ResolveResultType& result) { resolveWithValue(result); }
121
122     template<class RejectResultType> typename std::enable_if<PromiseResultInspector<RejectResultType>::passByValue, void>::type
123     reject(RejectResultType result) { rejectWithValue(result); }
124     template<class RejectResultType> typename std::enable_if<PromiseResultInspector<RejectResultType>::passByRef, void>::type
125     reject(RejectResultType& result) { rejectWithValue(result); }
126     template<class RejectResultType> typename std::enable_if<PromiseResultInspector<RejectResultType>::passByURef, void>::type
127     reject(RejectResultType&& result) { rejectWithValue(std::forward<RejectResultType>(result)); }
128     template<class RejectResultType> typename std::enable_if<PromiseResultInspector<RejectResultType>::passByConstRef, void>::type
129     reject(const RejectResultType& result) { rejectWithValue(result); }
130
131     template<class ResolveResultType> void resolveWithNewlyCreated(Ref<ResolveResultType>&&);
132
133     void reject(Exception&&);
134     void reject(ExceptionCode, const String& = { });
135
136     JSC::JSValue promise() const;
137
138     bool isSuspended() { return !m_deferred || !canInvokeCallback(); } // The wrapper world has gone away or active DOM objects have been suspended.
139     JSDOMGlobalObject* globalObject() { return m_globalObject.get(); }
140
141     void visitAggregate(JSC::SlotVisitor& visitor) { visitor.appendUnbarrieredWeak(&m_deferred); }
142
143 private:
144     DeferredPromise(JSDOMGlobalObject&, JSC::JSPromiseDeferred&);
145
146     void clear();
147     void contextDestroyed() override;
148
149     void callFunction(JSC::ExecState&, JSC::JSValue function, JSC::JSValue resolution);
150     void resolve(JSC::ExecState& state, JSC::JSValue resolution) { callFunction(state, m_deferred->resolve(), resolution); }
151     void reject(JSC::ExecState& state, JSC::JSValue resolution) { callFunction(state, m_deferred->reject(), resolution); }
152
153     template<class RejectResultType> void rejectWithValue(RejectResultType&&);
154     template<class ResolveResultType> void resolveWithValue(ResolveResultType&&);
155
156     JSC::Weak<JSC::JSPromiseDeferred> m_deferred;
157     JSC::Weak<JSDOMGlobalObject> m_globalObject;
158 };
159
160 Ref<DeferredPromise> createDeferredPromise(JSC::ExecState&, JSDOMWindow&);
161
162 void fulfillPromiseWithJSON(Ref<DeferredPromise>&&, const String&);
163 void fulfillPromiseWithArrayBuffer(Ref<DeferredPromise>&&, ArrayBuffer*);
164 void fulfillPromiseWithArrayBuffer(Ref<DeferredPromise>&&, const void*, size_t);
165 void rejectPromiseWithExceptionIfAny(JSC::ExecState&, JSDOMGlobalObject&, JSC::JSPromiseDeferred&);
166 JSC::EncodedJSValue createRejectedPromiseWithTypeError(JSC::ExecState&, const String&);
167
168 using PromiseFunction = void(JSC::ExecState&, Ref<DeferredPromise>&&);
169
170 enum class PromiseExecutionScope { WindowOnly, WindowOrWorker };
171
172 template<PromiseFunction promiseFunction, PromiseExecutionScope executionScope>
173 inline JSC::JSValue callPromiseFunction(JSC::ExecState& state)
174 {
175     JSC::VM& vm = state.vm();
176     auto scope = DECLARE_CATCH_SCOPE(vm);
177
178     JSDOMGlobalObject& globalObject = *JSC::jsCast<JSDOMGlobalObject*>(state.lexicalGlobalObject());
179     JSC::JSPromiseDeferred* promiseDeferred = JSC::JSPromiseDeferred::create(&state, &globalObject);
180
181     // promiseDeferred can be null when terminating a Worker abruptly.
182     if (executionScope == PromiseExecutionScope::WindowOrWorker && !promiseDeferred)
183         return JSC::jsUndefined();
184
185     promiseFunction(state, DeferredPromise::create(globalObject, *promiseDeferred));
186
187     rejectPromiseWithExceptionIfAny(state, globalObject, *promiseDeferred);
188     ASSERT_UNUSED(scope, !scope.exception());
189     return promiseDeferred->promise();
190 }
191
192 using BindingPromiseFunction = JSC::EncodedJSValue(JSC::ExecState*, Ref<DeferredPromise>&&);
193 template<BindingPromiseFunction bindingFunction>
194 inline void bindingPromiseFunctionAdapter(JSC::ExecState& state, Ref<DeferredPromise>&& promise)
195 {
196     bindingFunction(&state, WTFMove(promise));
197 }
198
199 template<BindingPromiseFunction bindingPromiseFunction, PromiseExecutionScope executionScope>
200 inline JSC::JSValue callPromiseFunction(JSC::ExecState& state)
201 {
202     return callPromiseFunction<bindingPromiseFunctionAdapter<bindingPromiseFunction>, executionScope>(state);
203 }
204
205 // At the moment, Value cannot be a Ref<T> or RefPtr<T>, it should be a DOM class.
206 template<typename Value> class DOMPromise {
207 public:
208     DOMPromise(Ref<DeferredPromise>&& genericPromise) : m_promiseDeferred(WTFMove(genericPromise)) { }
209     DOMPromise(DOMPromise&& promise) : m_promiseDeferred(WTFMove(promise.m_promiseDeferred)) { }
210     DOMPromise(const DOMPromise& other) : m_promiseDeferred(other.m_promiseDeferred.copyRef()) { }
211
212     DOMPromise& operator=(const DOMPromise& other)
213     {
214         m_promiseDeferred = other.copyRef();
215         return *this;
216     }
217
218     DOMPromise& operator=(DOMPromise&& other)
219     {
220         m_promiseDeferred = WTFMove(other.m_promiseDeferred);
221         return *this;
222     }
223
224     template<typename T = Value>
225     typename std::enable_if<!std::is_void<T>::value, void>::type resolve(typename PromiseResultInspector<Value>::Type value) { m_promiseDeferred->resolve(value); }
226
227     template<typename T = Value>
228     typename std::enable_if<std::is_void<T>::value, void>::type resolve() { m_promiseDeferred->resolve(nullptr); }
229
230     template<typename... ErrorType> void reject(ErrorType&&... error) { m_promiseDeferred->reject(std::forward<ErrorType>(error)...); }
231
232     JSC::JSValue promise() const { return m_promiseDeferred->promise(); };
233
234 private:
235     Ref<DeferredPromise> m_promiseDeferred;
236 };
237
238 template<class ResolveResultType>
239 inline void DeferredPromise::resolveWithValue(ResolveResultType&& result)
240 {
241     if (isSuspended())
242         return;
243     ASSERT(m_globalObject);
244     JSC::ExecState* exec = m_globalObject->globalExec();
245     JSC::JSLockHolder locker(exec);
246     resolve(*exec, toJS(exec, m_globalObject.get(), std::forward<ResolveResultType>(result)));
247 }
248
249 template<class ResolveResultType>
250 inline void DeferredPromise::resolveWithNewlyCreated(Ref<ResolveResultType>&& result)
251 {
252     if (isSuspended())
253         return;
254     ASSERT(m_deferred);
255     ASSERT(m_globalObject);
256     JSC::ExecState* exec = m_globalObject->globalExec();
257     JSC::JSLockHolder locker(exec);
258     resolve(*exec, toJSNewlyCreated(exec, m_globalObject.get(), WTFMove(result)));
259 }
260
261 template<class RejectResultType>
262 inline void DeferredPromise::rejectWithValue(RejectResultType&& result)
263 {
264     if (isSuspended())
265         return;
266     ASSERT(m_deferred);
267     ASSERT(m_globalObject);
268     JSC::ExecState* exec = m_globalObject->globalExec();
269     JSC::JSLockHolder locker(exec);
270     reject(*exec, toJS(exec, m_globalObject.get(), std::forward<RejectResultType>(result)));
271 }
272
273 template<>
274 inline void DeferredPromise::resolve(bool result)
275 {
276     if (isSuspended())
277         return;
278     ASSERT(m_deferred);
279     ASSERT(m_globalObject);
280     JSC::ExecState* exec = m_globalObject->globalExec();
281     JSC::JSLockHolder locker(exec);
282     resolve(*exec, JSC::jsBoolean(result));
283 }
284
285 template<>
286 inline void DeferredPromise::resolve(JSC::JSValue value)
287 {
288     if (isSuspended())
289         return;
290     ASSERT(m_deferred);
291     ASSERT(m_globalObject);
292     JSC::ExecState* exec = m_globalObject->globalExec();
293     JSC::JSLockHolder locker(exec);
294     resolve(*exec, value);
295 }
296
297 template<>
298 inline void DeferredPromise::reject(JSC::JSValue value)
299 {
300     if (isSuspended())
301         return;
302     ASSERT(m_deferred);
303     ASSERT(m_globalObject);
304     JSC::ExecState* exec = m_globalObject->globalExec();
305     JSC::JSLockHolder locker(exec);
306     reject(*exec, value);
307 }
308
309 template<>
310 inline void DeferredPromise::resolve(std::nullptr_t)
311 {
312     if (isSuspended())
313         return;
314     ASSERT(m_deferred);
315     ASSERT(m_globalObject);
316     JSC::ExecState* exec = m_globalObject->globalExec();
317     JSC::JSLockHolder locker(exec);
318     resolve(*exec, JSC::jsUndefined());
319 }
320
321 template<>
322 inline void DeferredPromise::reject(std::nullptr_t)
323 {
324     if (isSuspended())
325         return;
326     ASSERT(m_deferred);
327     ASSERT(m_globalObject);
328     JSC::ExecState* exec = m_globalObject->globalExec();
329     JSC::JSLockHolder locker(exec);
330     reject(*exec, JSC::jsNull());
331 }
332
333 template<>
334 inline void DeferredPromise::resolve(const String& result)
335 {
336     if (isSuspended())
337         return;
338     ASSERT(m_deferred);
339     ASSERT(m_globalObject);
340     JSC::ExecState* exec = m_globalObject->globalExec();
341     JSC::JSLockHolder locker(exec);
342     resolve(*exec, jsString(exec, result));
343 }
344
345 template<>
346 inline void DeferredPromise::reject(const String& result)
347 {
348     if (isSuspended())
349         return;
350     ASSERT(m_deferred);
351     ASSERT(m_globalObject);
352     JSC::ExecState* exec = m_globalObject->globalExec();
353     JSC::JSLockHolder locker(exec);
354     reject(*exec, jsString(exec, result));
355 }
356
357 } // namespace WebCore