Get rid of HeapRootVisitor and make SlotVisitor less painful to use
[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 "JSDOMConvert.h"
30 #include <heap/StrongInlines.h>
31 #include <runtime/JSPromiseDeferred.h>
32
33 namespace WebCore {
34
35 class DeferredPromise : public RefCounted<DeferredPromise>, public ActiveDOMCallback {
36 public:
37     static Ref<DeferredPromise> create(JSDOMGlobalObject& globalObject, JSC::JSPromiseDeferred& deferred)
38     {
39         return adoptRef(*new DeferredPromise(globalObject, deferred));
40     }
41
42     ~DeferredPromise();
43
44     template<class IDLType> 
45     void resolve(typename IDLType::ParameterType value)
46     {
47         if (isSuspended())
48             return;
49         ASSERT(m_deferred);
50         ASSERT(m_globalObject);
51         JSC::ExecState* exec = m_globalObject->globalExec();
52         JSC::JSLockHolder locker(exec);
53         resolve(*exec, toJS<IDLType>(*exec, *m_globalObject.get(), std::forward<typename IDLType::ParameterType>(value)));
54     }
55
56     void resolve()
57     {
58         if (isSuspended())
59             return;
60         ASSERT(m_deferred);
61         ASSERT(m_globalObject);
62         JSC::ExecState* exec = m_globalObject->globalExec();
63         JSC::JSLockHolder locker(exec);
64         resolve(*exec, JSC::jsUndefined());
65     }
66
67     template<class IDLType>
68     void resolveWithNewlyCreated(typename IDLType::ParameterType value)
69     {
70         if (isSuspended())
71             return;
72         ASSERT(m_deferred);
73         ASSERT(m_globalObject);
74         JSC::ExecState* exec = m_globalObject->globalExec();
75         JSC::JSLockHolder locker(exec);
76         resolve(*exec, toJSNewlyCreated<IDLType>(*exec, *m_globalObject.get(), std::forward<typename IDLType::ParameterType>(value)));
77     }
78
79     template<class IDLType> 
80     void reject(typename IDLType::ParameterType value)
81     {
82         if (isSuspended())
83             return;
84         ASSERT(m_deferred);
85         ASSERT(m_globalObject);
86         JSC::ExecState* exec = m_globalObject->globalExec();
87         JSC::JSLockHolder locker(exec);
88         reject(*exec, toJS<IDLType>(*exec, *m_globalObject.get(), std::forward<typename IDLType::ParameterType>(value)));
89     }
90
91     void reject();
92     void reject(std::nullptr_t);
93     void reject(Exception&&);
94     void reject(ExceptionCode, const String& = { });
95     void reject(const JSC::PrivateName&);
96
97     JSC::JSValue promise() const;
98
99     bool isSuspended() { return !m_deferred || !canInvokeCallback(); } // The wrapper world has gone away or active DOM objects have been suspended.
100     JSDOMGlobalObject* globalObject() { return m_globalObject.get(); }
101
102     void visitAggregate(JSC::SlotVisitor& visitor) { visitor.append(m_deferred); }
103
104 private:
105     DeferredPromise(JSDOMGlobalObject&, JSC::JSPromiseDeferred&);
106
107     void clear();
108     void contextDestroyed() override;
109
110     void callFunction(JSC::ExecState&, JSC::JSValue function, JSC::JSValue resolution);
111     void resolve(JSC::ExecState& state, JSC::JSValue resolution) { callFunction(state, m_deferred->resolve(), resolution); }
112     void reject(JSC::ExecState& state, JSC::JSValue resolution) { callFunction(state, m_deferred->reject(), resolution); }
113
114     JSC::Weak<JSC::JSPromiseDeferred> m_deferred;
115     JSC::Weak<JSDOMGlobalObject> m_globalObject;
116 };
117
118 class DOMPromiseBase {
119 public:
120     DOMPromiseBase(Ref<DeferredPromise>&& genericPromise)
121         : m_promiseDeferred(WTFMove(genericPromise))
122     {
123     }
124
125     DOMPromiseBase(DOMPromiseBase&& promise)
126         : m_promiseDeferred(WTFMove(promise.m_promiseDeferred))
127     {
128     }
129
130     DOMPromiseBase(const DOMPromiseBase& other)
131         : m_promiseDeferred(other.m_promiseDeferred.copyRef())
132     {
133     }
134
135     DOMPromiseBase& operator=(const DOMPromiseBase& other)
136     {
137         m_promiseDeferred = other.m_promiseDeferred.copyRef();
138         return *this;
139     }
140
141     DOMPromiseBase& operator=(DOMPromiseBase&& other)
142     {
143         m_promiseDeferred = WTFMove(other.m_promiseDeferred);
144         return *this;
145     }
146
147     void reject()
148     {
149         m_promiseDeferred->reject();
150     }
151
152     template<typename... ErrorType> 
153     void reject(ErrorType&&... error)
154     {
155         m_promiseDeferred->reject(std::forward<ErrorType>(error)...);
156     }
157
158     template<typename IDLType>
159     void rejectType(typename IDLType::ParameterType value)
160     {
161         m_promiseDeferred->reject<IDLType>(std::forward<typename IDLType::ParameterType>(value));
162     }
163
164     JSC::JSValue promise() const { return m_promiseDeferred->promise(); };
165
166 protected:
167     Ref<DeferredPromise> m_promiseDeferred;
168 };
169
170 template<typename IDLType> 
171 class DOMPromise : public DOMPromiseBase {
172 public:
173     using DOMPromiseBase::DOMPromiseBase;
174     using DOMPromiseBase::operator=;
175     using DOMPromiseBase::promise;
176     using DOMPromiseBase::reject;
177
178     void resolve(typename IDLType::ParameterType value)
179     { 
180         m_promiseDeferred->resolve<IDLType>(std::forward<typename IDLType::ParameterType>(value));
181     }
182 };
183
184 template<> class DOMPromise<void> : public DOMPromiseBase {
185 public:
186     using DOMPromiseBase::DOMPromiseBase;
187     using DOMPromiseBase::operator=;
188     using DOMPromiseBase::promise;
189     using DOMPromiseBase::reject;
190
191     void resolve()
192     { 
193         m_promiseDeferred->resolve();
194     }
195 };
196
197
198 Ref<DeferredPromise> createDeferredPromise(JSC::ExecState&, JSDOMWindow&);
199
200 void fulfillPromiseWithJSON(Ref<DeferredPromise>&&, const String&);
201 void fulfillPromiseWithArrayBuffer(Ref<DeferredPromise>&&, ArrayBuffer*);
202 void fulfillPromiseWithArrayBuffer(Ref<DeferredPromise>&&, const void*, size_t);
203 void rejectPromiseWithExceptionIfAny(JSC::ExecState&, JSDOMGlobalObject&, JSC::JSPromiseDeferred&);
204 JSC::EncodedJSValue createRejectedPromiseWithTypeError(JSC::ExecState&, const String&);
205
206 using PromiseFunction = void(JSC::ExecState&, Ref<DeferredPromise>&&);
207
208 enum class PromiseExecutionScope { WindowOnly, WindowOrWorker };
209
210 template<PromiseFunction promiseFunction, PromiseExecutionScope executionScope>
211 inline JSC::JSValue callPromiseFunction(JSC::ExecState& state)
212 {
213     JSC::VM& vm = state.vm();
214     auto scope = DECLARE_CATCH_SCOPE(vm);
215
216     JSDOMGlobalObject& globalObject = *JSC::jsCast<JSDOMGlobalObject*>(state.lexicalGlobalObject());
217     JSC::JSPromiseDeferred* promiseDeferred = JSC::JSPromiseDeferred::create(&state, &globalObject);
218
219     // promiseDeferred can be null when terminating a Worker abruptly.
220     if (executionScope == PromiseExecutionScope::WindowOrWorker && !promiseDeferred)
221         return JSC::jsUndefined();
222
223     promiseFunction(state, DeferredPromise::create(globalObject, *promiseDeferred));
224
225     rejectPromiseWithExceptionIfAny(state, globalObject, *promiseDeferred);
226     ASSERT_UNUSED(scope, !scope.exception());
227     return promiseDeferred->promise();
228 }
229
230 using BindingPromiseFunction = JSC::EncodedJSValue(JSC::ExecState*, Ref<DeferredPromise>&&);
231 template<BindingPromiseFunction bindingFunction>
232 inline void bindingPromiseFunctionAdapter(JSC::ExecState& state, Ref<DeferredPromise>&& promise)
233 {
234     bindingFunction(&state, WTFMove(promise));
235 }
236
237 template<BindingPromiseFunction bindingPromiseFunction, PromiseExecutionScope executionScope>
238 inline JSC::JSValue callPromiseFunction(JSC::ExecState& state)
239 {
240     return callPromiseFunction<bindingPromiseFunctionAdapter<bindingPromiseFunction>, executionScope>(state);
241 }
242
243 } // namespace WebCore