672f13caf159b87aa71fedd5da2d43525dda1358
[WebKit-https.git] / Source / WebCore / bindings / js / DOMPromiseProxy.h
1 /*
2  * Copyright (C) 2017 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 "JSDOMGlobalObject.h"
30 #include "JSDOMPromiseDeferred.h"
31 #include <wtf/Function.h>
32 #include <wtf/Optional.h>
33 #include <wtf/Vector.h>
34
35 namespace WebCore {
36
37 template<typename IDLType>
38 class DOMPromiseProxy {
39     WTF_MAKE_FAST_ALLOCATED;
40 public:
41     using Value = typename IDLType::StorageType;
42
43     DOMPromiseProxy() = default;
44     ~DOMPromiseProxy() = default;
45
46     JSC::JSValue promise(JSC::JSGlobalObject&, JSDOMGlobalObject&);
47
48     void clear();
49
50     bool isFulfilled() const;
51
52     void resolve(typename IDLType::ParameterType);
53     void resolveWithNewlyCreated(typename IDLType::ParameterType);
54     void reject(Exception);
55     
56 private:
57     Optional<ExceptionOr<Value>> m_valueOrException;
58     Vector<Ref<DeferredPromise>, 1> m_deferredPromises;
59 };
60
61 template<>
62 class DOMPromiseProxy<IDLVoid> {
63     WTF_MAKE_FAST_ALLOCATED;
64 public:
65     DOMPromiseProxy() = default;
66     ~DOMPromiseProxy() = default;
67
68     JSC::JSValue promise(JSC::JSGlobalObject&, JSDOMGlobalObject&);
69
70     void clear();
71
72     bool isFulfilled() const;
73
74     void resolve();
75     void reject(Exception);
76
77 private:
78     Optional<ExceptionOr<void>> m_valueOrException;
79     Vector<Ref<DeferredPromise>, 1> m_deferredPromises;
80 };
81
82 // Instead of storing the value of the resolution directly, DOMPromiseProxyWithResolveCallback
83 // allows the owner to specify callback to be called when the resolved value is needed. This is
84 // needed to avoid reference cycles when the resolved value is the owner, such as is the case with
85 // FontFace and FontFaceSet.
86 template<typename IDLType>
87 class DOMPromiseProxyWithResolveCallback {
88     WTF_MAKE_FAST_ALLOCATED;
89 public:
90     using ResolveCallback = WTF::Function<typename IDLType::ParameterType ()>;
91
92     template <typename Class, typename BaseClass>
93     DOMPromiseProxyWithResolveCallback(Class&, typename IDLType::ParameterType (BaseClass::*)());
94     DOMPromiseProxyWithResolveCallback(ResolveCallback&&);
95     ~DOMPromiseProxyWithResolveCallback() = default;
96
97     JSC::JSValue promise(JSC::JSGlobalObject&, JSDOMGlobalObject&);
98
99     void clear();
100
101     bool isFulfilled() const;
102
103     void resolve(typename IDLType::ParameterType);
104     void resolveWithNewlyCreated(typename IDLType::ParameterType);
105     void reject(Exception);
106     
107 private:
108     ResolveCallback m_resolveCallback;
109     Optional<ExceptionOr<void>> m_valueOrException;
110     Vector<Ref<DeferredPromise>, 1> m_deferredPromises;
111 };
112
113
114 // MARK: - DOMPromiseProxy<IDLType> generic implementation
115
116 template<typename IDLType>
117 inline JSC::JSValue DOMPromiseProxy<IDLType>::promise(JSC::JSGlobalObject& lexicalGlobalObject, JSDOMGlobalObject& globalObject)
118 {
119     UNUSED_PARAM(lexicalGlobalObject);
120     for (auto& deferredPromise : m_deferredPromises) {
121         if (deferredPromise->globalObject() == &globalObject)
122             return deferredPromise->promise();
123     }
124
125     // DeferredPromise can fail construction during worker abrupt termination.
126     auto deferredPromise = DeferredPromise::create(globalObject, DeferredPromise::Mode::RetainPromiseOnResolve);
127     if (!deferredPromise)
128         return JSC::jsUndefined();
129
130     if (m_valueOrException) {
131         if (m_valueOrException->hasException())
132             deferredPromise->reject(m_valueOrException->exception());
133         else
134             deferredPromise->template resolve<IDLType>(m_valueOrException->returnValue());
135     }
136
137     auto result = deferredPromise->promise();
138     m_deferredPromises.append(deferredPromise.releaseNonNull());
139     return result;
140 }
141
142 template<typename IDLType>
143 inline void DOMPromiseProxy<IDLType>::clear()
144 {
145     m_valueOrException = WTF::nullopt;
146     m_deferredPromises.clear();
147 }
148
149 template<typename IDLType>
150 inline bool DOMPromiseProxy<IDLType>::isFulfilled() const
151 {
152     return m_valueOrException.hasValue();
153 }
154
155 template<typename IDLType>
156 inline void DOMPromiseProxy<IDLType>::resolve(typename IDLType::ParameterType value)
157 {
158     ASSERT(!m_valueOrException);
159
160     m_valueOrException = ExceptionOr<Value> { std::forward<typename IDLType::ParameterType>(value) };
161     for (auto& deferredPromise : m_deferredPromises)
162         deferredPromise->template resolve<IDLType>(m_valueOrException->returnValue());
163 }
164
165 template<typename IDLType>
166 inline void DOMPromiseProxy<IDLType>::resolveWithNewlyCreated(typename IDLType::ParameterType value)
167 {
168     ASSERT(!m_valueOrException);
169
170     m_valueOrException = ExceptionOr<Value> { std::forward<typename IDLType::ParameterType>(value) };
171     for (auto& deferredPromise : m_deferredPromises)
172         deferredPromise->template resolveWithNewlyCreated<IDLType>(m_valueOrException->returnValue());
173 }
174
175 template<typename IDLType>
176 inline void DOMPromiseProxy<IDLType>::reject(Exception exception)
177 {
178     ASSERT(!m_valueOrException);
179
180     m_valueOrException = ExceptionOr<Value> { WTFMove(exception) };
181     for (auto& deferredPromise : m_deferredPromises)
182         deferredPromise->reject(m_valueOrException->exception());
183 }
184
185
186 // MARK: - DOMPromiseProxy<IDLVoid> specialization
187
188 inline JSC::JSValue DOMPromiseProxy<IDLVoid>::promise(JSC::JSGlobalObject& lexicalGlobalObject, JSDOMGlobalObject& globalObject)
189 {
190     UNUSED_PARAM(lexicalGlobalObject);
191     for (auto& deferredPromise : m_deferredPromises) {
192         if (deferredPromise->globalObject() == &globalObject)
193             return deferredPromise->promise();
194     }
195
196     // DeferredPromise can fail construction during worker abrupt termination.
197     auto deferredPromise = DeferredPromise::create(globalObject, DeferredPromise::Mode::RetainPromiseOnResolve);
198     if (!deferredPromise)
199         return JSC::jsUndefined();
200
201     if (m_valueOrException) {
202         if (m_valueOrException->hasException())
203             deferredPromise->reject(m_valueOrException->exception());
204         else
205             deferredPromise->resolve();
206     }
207
208     auto result = deferredPromise->promise();
209     m_deferredPromises.append(deferredPromise.releaseNonNull());
210     return result;
211 }
212
213 inline void DOMPromiseProxy<IDLVoid>::clear()
214 {
215     m_valueOrException = WTF::nullopt;
216     m_deferredPromises.clear();
217 }
218
219 inline bool DOMPromiseProxy<IDLVoid>::isFulfilled() const
220 {
221     return m_valueOrException.hasValue();
222 }
223
224 inline void DOMPromiseProxy<IDLVoid>::resolve()
225 {
226     ASSERT(!m_valueOrException);
227     m_valueOrException = ExceptionOr<void> { };
228     for (auto& deferredPromise : m_deferredPromises)
229         deferredPromise->resolve();
230 }
231
232 inline void DOMPromiseProxy<IDLVoid>::reject(Exception exception)
233 {
234     ASSERT(!m_valueOrException);
235     m_valueOrException = ExceptionOr<void> { WTFMove(exception) };
236     for (auto& deferredPromise : m_deferredPromises)
237         deferredPromise->reject(m_valueOrException->exception());
238 }
239
240 // MARK: - DOMPromiseProxyWithResolveCallback<IDLType> implementation
241
242 template<typename IDLType>
243 template <typename Class, typename BaseClass>
244 inline DOMPromiseProxyWithResolveCallback<IDLType>::DOMPromiseProxyWithResolveCallback(Class& object, typename IDLType::ParameterType (BaseClass::*function)())
245     : m_resolveCallback(std::bind(function, &object))
246 {
247 }
248
249 template<typename IDLType>
250 inline DOMPromiseProxyWithResolveCallback<IDLType>::DOMPromiseProxyWithResolveCallback(ResolveCallback&& function)
251     : m_resolveCallback(WTFMove(function))
252 {
253 }
254
255 template<typename IDLType>
256 inline JSC::JSValue DOMPromiseProxyWithResolveCallback<IDLType>::promise(JSC::JSGlobalObject& lexicalGlobalObject, JSDOMGlobalObject& globalObject)
257 {
258     UNUSED_PARAM(lexicalGlobalObject);
259     for (auto& deferredPromise : m_deferredPromises) {
260         if (deferredPromise->globalObject() == &globalObject)
261             return deferredPromise->promise();
262     }
263
264     // DeferredPromise can fail construction during worker abrupt termination.
265     auto deferredPromise = DeferredPromise::create(globalObject, DeferredPromise::Mode::RetainPromiseOnResolve);
266     if (!deferredPromise)
267         return JSC::jsUndefined();
268
269     if (m_valueOrException) {
270         if (m_valueOrException->hasException())
271             deferredPromise->reject(m_valueOrException->exception());
272         else
273             deferredPromise->template resolve<IDLType>(m_resolveCallback());
274     }
275
276     auto result = deferredPromise->promise();
277     m_deferredPromises.append(deferredPromise.releaseNonNull());
278     return result;
279 }
280
281 template<typename IDLType>
282 inline void DOMPromiseProxyWithResolveCallback<IDLType>::clear()
283 {
284     m_valueOrException = WTF::nullopt;
285     m_deferredPromises.clear();
286 }
287
288 template<typename IDLType>
289 inline bool DOMPromiseProxyWithResolveCallback<IDLType>::isFulfilled() const
290 {
291     return m_valueOrException.hasValue();
292 }
293
294 template<typename IDLType>
295 inline void DOMPromiseProxyWithResolveCallback<IDLType>::resolve(typename IDLType::ParameterType value)
296 {
297     ASSERT(!m_valueOrException);
298
299     m_valueOrException = ExceptionOr<void> { };
300     for (auto& deferredPromise : m_deferredPromises)
301         deferredPromise->template resolve<IDLType>(value);
302 }
303
304 template<typename IDLType>
305 inline void DOMPromiseProxyWithResolveCallback<IDLType>::resolveWithNewlyCreated(typename IDLType::ParameterType value)
306 {
307     ASSERT(!m_valueOrException);
308
309     m_valueOrException = ExceptionOr<void> { };
310     for (auto& deferredPromise : m_deferredPromises)
311         deferredPromise->template resolveWithNewlyCreated<IDLType>(value);
312 }
313
314 template<typename IDLType>
315 inline void DOMPromiseProxyWithResolveCallback<IDLType>::reject(Exception exception)
316 {
317     ASSERT(!m_valueOrException);
318
319     m_valueOrException = ExceptionOr<void> { WTFMove(exception) };
320     for (auto& deferredPromise : m_deferredPromises)
321         deferredPromise->reject(m_valueOrException->exception());
322 }
323
324 }