ServiceWorkers API should reject promises when calling objects inside detached frames
[WebKit-https.git] / Source / WebCore / bindings / js / JSDOMPromiseDeferred.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 "ExceptionOr.h"
29 #include "JSDOMConvert.h"
30 #include "JSDOMGuardedObject.h"
31 #include <runtime/CatchScope.h>
32 #include <runtime/JSPromiseDeferred.h>
33
34 namespace WebCore {
35
36 class DeferredPromise : public DOMGuarded<JSC::JSPromiseDeferred> {
37 public:
38     enum class Mode {
39         ClearPromiseOnResolve,
40         RetainPromiseOnResolve
41     };
42
43     static RefPtr<DeferredPromise> create(JSC::ExecState& state, JSDOMGlobalObject& globalObject, Mode mode = Mode::ClearPromiseOnResolve)
44     {
45         auto* promiseDeferred = JSC::JSPromiseDeferred::create(&state, &globalObject);
46         if (!promiseDeferred)
47             return nullptr;
48         return adoptRef(new DeferredPromise(globalObject, *promiseDeferred, mode));
49     }
50
51     static Ref<DeferredPromise> create(JSDOMGlobalObject& globalObject, JSC::JSPromiseDeferred& deferred, Mode mode = Mode::ClearPromiseOnResolve)
52     {
53         return adoptRef(*new DeferredPromise(globalObject, deferred, mode));
54     }
55
56     template<class IDLType>
57     void resolve(typename IDLType::ParameterType value)
58     {
59         if (isSuspended())
60             return;
61         ASSERT(deferred());
62         ASSERT(globalObject());
63         JSC::ExecState* exec = globalObject()->globalExec();
64         JSC::JSLockHolder locker(exec);
65         resolve(*exec, toJS<IDLType>(*exec, *globalObject(), std::forward<typename IDLType::ParameterType>(value)));
66     }
67
68     void resolve()
69     {
70         if (isSuspended())
71             return;
72         ASSERT(deferred());
73         ASSERT(globalObject());
74         JSC::ExecState* exec = globalObject()->globalExec();
75         JSC::JSLockHolder locker(exec);
76         resolve(*exec, JSC::jsUndefined());
77     }
78
79     template<class IDLType>
80     void resolveWithNewlyCreated(typename IDLType::ParameterType value)
81     {
82         if (isSuspended())
83             return;
84         ASSERT(deferred());
85         ASSERT(globalObject());
86         JSC::ExecState* exec = globalObject()->globalExec();
87         JSC::JSLockHolder locker(exec);
88         resolve(*exec, toJSNewlyCreated<IDLType>(*exec, *globalObject(), std::forward<typename IDLType::ParameterType>(value)));
89     }
90
91     template<class IDLType>
92     void reject(typename IDLType::ParameterType value)
93     {
94         if (isSuspended())
95             return;
96         ASSERT(deferred());
97         ASSERT(globalObject());
98         JSC::ExecState* exec = globalObject()->globalExec();
99         JSC::JSLockHolder locker(exec);
100         reject(*exec, toJS<IDLType>(*exec, *globalObject(), std::forward<typename IDLType::ParameterType>(value)));
101     }
102
103     void reject();
104     void reject(std::nullptr_t);
105     void reject(Exception);
106     WEBCORE_EXPORT void reject(ExceptionCode, const String& = { });
107     void reject(const JSC::PrivateName&);
108
109     template<typename Callback>
110     void resolveWithCallback(Callback callback)
111     {
112         if (isSuspended())
113             return;
114         ASSERT(deferred());
115         ASSERT(globalObject());
116         JSC::ExecState* exec = globalObject()->globalExec();
117         JSC::JSLockHolder locker(exec);
118         resolve(*exec, callback(*exec, *globalObject()));
119     }
120
121     template<typename Callback>
122     void rejectWithCallback(Callback callback)
123     {
124         if (isSuspended())
125             return;
126         ASSERT(deferred());
127         ASSERT(globalObject());
128         JSC::ExecState* exec = globalObject()->globalExec();
129         JSC::JSLockHolder locker(exec);
130         reject(*exec, callback(*exec, *globalObject()));
131     }
132
133     JSC::JSValue promise() const;
134
135 private:
136     DeferredPromise(JSDOMGlobalObject& globalObject, JSC::JSPromiseDeferred& deferred, Mode mode)
137         : DOMGuarded<JSC::JSPromiseDeferred>(globalObject, deferred)
138         , m_mode(mode)
139     {
140     }
141
142     JSC::JSPromiseDeferred* deferred() const { return guarded(); }
143
144     WEBCORE_EXPORT void callFunction(JSC::ExecState&, JSC::JSValue function, JSC::JSValue resolution);
145     void resolve(JSC::ExecState& state, JSC::JSValue resolution) { callFunction(state, deferred()->resolve(), resolution); }
146     void reject(JSC::ExecState& state, JSC::JSValue resolution) { callFunction(state, deferred()->reject(), resolution); }
147
148     Mode m_mode;
149 };
150
151 class DOMPromiseDeferredBase {
152 public:
153     DOMPromiseDeferredBase(Ref<DeferredPromise>&& genericPromise)
154         : m_promiseDeferred(WTFMove(genericPromise))
155     {
156     }
157
158     DOMPromiseDeferredBase(DOMPromiseDeferredBase&& promise)
159         : m_promiseDeferred(WTFMove(promise.m_promiseDeferred))
160     {
161     }
162
163     DOMPromiseDeferredBase(const DOMPromiseDeferredBase& other)
164         : m_promiseDeferred(other.m_promiseDeferred.copyRef())
165     {
166     }
167
168     DOMPromiseDeferredBase& operator=(const DOMPromiseDeferredBase& other)
169     {
170         m_promiseDeferred = other.m_promiseDeferred.copyRef();
171         return *this;
172     }
173
174     DOMPromiseDeferredBase& operator=(DOMPromiseDeferredBase&& other)
175     {
176         m_promiseDeferred = WTFMove(other.m_promiseDeferred);
177         return *this;
178     }
179
180     void reject()
181     {
182         m_promiseDeferred->reject();
183     }
184
185     template<typename... ErrorType> 
186     void reject(ErrorType&&... error)
187     {
188         m_promiseDeferred->reject(std::forward<ErrorType>(error)...);
189     }
190
191     template<typename IDLType>
192     void rejectType(typename IDLType::ParameterType value)
193     {
194         m_promiseDeferred->reject<IDLType>(std::forward<typename IDLType::ParameterType>(value));
195     }
196
197     JSC::JSValue promise() const { return m_promiseDeferred->promise(); };
198
199 protected:
200     Ref<DeferredPromise> m_promiseDeferred;
201 };
202
203 template<typename IDLType> 
204 class DOMPromiseDeferred : public DOMPromiseDeferredBase {
205 public:
206     using DOMPromiseDeferredBase::DOMPromiseDeferredBase;
207     using DOMPromiseDeferredBase::operator=;
208     using DOMPromiseDeferredBase::promise;
209     using DOMPromiseDeferredBase::reject;
210
211     void resolve(typename IDLType::ParameterType value)
212     { 
213         m_promiseDeferred->resolve<IDLType>(std::forward<typename IDLType::ParameterType>(value));
214     }
215
216     void settle(ExceptionOr<typename IDLType::ParameterType>&& result)
217     {
218         if (result.hasException()) {
219             reject(result.releaseException());
220             return;
221         }
222         resolve(result.releaseReturnValue());
223     }
224 };
225
226 template<> class DOMPromiseDeferred<void> : public DOMPromiseDeferredBase {
227 public:
228     using DOMPromiseDeferredBase::DOMPromiseDeferredBase;
229     using DOMPromiseDeferredBase::operator=;
230     using DOMPromiseDeferredBase::promise;
231     using DOMPromiseDeferredBase::reject;
232
233     void resolve()
234     { 
235         m_promiseDeferred->resolve();
236     }
237
238     void settle(ExceptionOr<void>&& result)
239     {
240         if (result.hasException()) {
241             reject(result.releaseException());
242             return;
243         }
244         resolve();
245     }
246 };
247
248
249 Ref<DeferredPromise> createDeferredPromise(JSC::ExecState&, JSDOMWindow&);
250
251 void fulfillPromiseWithJSON(Ref<DeferredPromise>&&, const String&);
252 void fulfillPromiseWithArrayBuffer(Ref<DeferredPromise>&&, ArrayBuffer*);
253 void fulfillPromiseWithArrayBuffer(Ref<DeferredPromise>&&, const void*, size_t);
254 WEBCORE_EXPORT void rejectPromiseWithExceptionIfAny(JSC::ExecState&, JSDOMGlobalObject&, JSC::JSPromiseDeferred&);
255 JSC::EncodedJSValue createRejectedPromiseWithTypeError(JSC::ExecState&, const String&);
256
257 using PromiseFunction = void(JSC::ExecState&, Ref<DeferredPromise>&&);
258
259 enum class PromiseExecutionScope { WindowOnly, WindowOrWorker };
260
261 template<PromiseFunction promiseFunction, PromiseExecutionScope executionScope>
262 inline JSC::JSValue callPromiseFunction(JSC::ExecState& state)
263 {
264     JSC::VM& vm = state.vm();
265     auto scope = DECLARE_CATCH_SCOPE(vm);
266
267     auto& globalObject = callerGlobalObject(state);
268     JSC::JSPromiseDeferred* promiseDeferred = JSC::JSPromiseDeferred::create(&state, &globalObject);
269
270     // promiseDeferred can be null when terminating a Worker abruptly.
271     if (executionScope == PromiseExecutionScope::WindowOrWorker && !promiseDeferred)
272         return JSC::jsUndefined();
273
274     promiseFunction(state, DeferredPromise::create(globalObject, *promiseDeferred));
275
276     rejectPromiseWithExceptionIfAny(state, globalObject, *promiseDeferred);
277     EXCEPTION_ASSERT_UNUSED(scope, !scope.exception());
278     return promiseDeferred->promise();
279 }
280
281 template<PromiseExecutionScope executionScope, typename PromiseFunctor>
282 inline JSC::JSValue callPromiseFunction(JSC::ExecState& state, PromiseFunctor functor)
283 {
284     JSC::VM& vm = state.vm();
285     auto scope = DECLARE_CATCH_SCOPE(vm);
286
287     auto& globalObject = callerGlobalObject(state);
288     JSC::JSPromiseDeferred* promiseDeferred = JSC::JSPromiseDeferred::create(&state, &globalObject);
289
290     // promiseDeferred can be null when terminating a Worker abruptly.
291     if (executionScope == PromiseExecutionScope::WindowOrWorker && !promiseDeferred)
292         return JSC::jsUndefined();
293
294     functor(state, DeferredPromise::create(globalObject, *promiseDeferred));
295
296     rejectPromiseWithExceptionIfAny(state, globalObject, *promiseDeferred);
297     EXCEPTION_ASSERT_UNUSED(scope, !scope.exception());
298     return promiseDeferred->promise();
299 }
300
301 using BindingPromiseFunction = JSC::EncodedJSValue(JSC::ExecState*, Ref<DeferredPromise>&&);
302 template<BindingPromiseFunction bindingFunction>
303 inline void bindingPromiseFunctionAdapter(JSC::ExecState& state, Ref<DeferredPromise>&& promise)
304 {
305     bindingFunction(&state, WTFMove(promise));
306 }
307
308 template<BindingPromiseFunction bindingPromiseFunction, PromiseExecutionScope executionScope>
309 inline JSC::JSValue callPromiseFunction(JSC::ExecState& state)
310 {
311     return callPromiseFunction<bindingPromiseFunctionAdapter<bindingPromiseFunction>, executionScope>(state);
312 }
313
314 } // namespace WebCore