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