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
35namespace WebCore {
36
37template<typename IDLType>
38class DOMPromiseProxy {
39 WTF_MAKE_FAST_ALLOCATED;
40public:
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
56private:
57 Optional<ExceptionOr<Value>> m_valueOrException;
58 Vector<Ref<DeferredPromise>, 1> m_deferredPromises;
59};
60
61template<>
62class DOMPromiseProxy<IDLVoid> {
63public:
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
76private:
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.
85template<typename IDLType>
86class DOMPromiseProxyWithResolveCallback {
87public:
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
105private:
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
114template<typename IDLType>
115inline 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
139template<typename IDLType>
140inline void DOMPromiseProxy<IDLType>::clear()
141{
142 m_valueOrException = WTF::nullopt;
143 m_deferredPromises.clear();
144}
145
146template<typename IDLType>
147inline bool DOMPromiseProxy<IDLType>::isFulfilled() const
148{
149 return m_valueOrException.hasValue();
150}
151
152template<typename IDLType>
153inline 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
162template<typename IDLType>
163inline 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
172template<typename IDLType>
173inline 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
185inline 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
209inline void DOMPromiseProxy<IDLVoid>::clear()
210{
211 m_valueOrException = WTF::nullopt;
212 m_deferredPromises.clear();
213}
214
215inline bool DOMPromiseProxy<IDLVoid>::isFulfilled() const
216{
217 return m_valueOrException.hasValue();
218}
219
220inline 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
228inline 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
238template<typename IDLType>
239template <typename Class, typename BaseClass>
240inline DOMPromiseProxyWithResolveCallback<IDLType>::DOMPromiseProxyWithResolveCallback(Class& object, typename IDLType::ParameterType (BaseClass::*function)())
241 : m_resolveCallback(std::bind(function, &object))
242{
243}
244
245template<typename IDLType>
246inline DOMPromiseProxyWithResolveCallback<IDLType>::DOMPromiseProxyWithResolveCallback(ResolveCallback&& function)
247 : m_resolveCallback(WTFMove(function))
248{
249}
250
251template<typename IDLType>
252inline 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
276template<typename IDLType>
277inline void DOMPromiseProxyWithResolveCallback<IDLType>::clear()
278{
279 m_valueOrException = WTF::nullopt;
280 m_deferredPromises.clear();
281}
282
283template<typename IDLType>
284inline bool DOMPromiseProxyWithResolveCallback<IDLType>::isFulfilled() const
285{
286 return m_valueOrException.hasValue();
287}
288
289template<typename IDLType>
290inline 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
299template<typename IDLType>
300inline 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
309template<typename IDLType>
310inline 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