5570f4ea3acdf7de1f09a861b607a0647931b6b9
[WebKit-https.git] / Source / WebCore / bindings / js / JSDOMPromiseDeferred.cpp
1 /*
2  * Copyright (C) 2013-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 #include "config.h"
27 #include "JSDOMPromiseDeferred.h"
28
29 #include "DOMWindow.h"
30 #include "JSDOMPromise.h"
31 #include "JSDOMWindow.h"
32 #include <JavaScriptCore/BuiltinNames.h>
33 #include <JavaScriptCore/Exception.h>
34 #include <JavaScriptCore/JSONObject.h>
35 #include <JavaScriptCore/JSPromiseConstructor.h>
36 #include <JavaScriptCore/Strong.h>
37
38 namespace WebCore {
39 using namespace JSC;
40
41 JSC::JSValue DeferredPromise::promise() const
42 {
43     ASSERT(deferred());
44     return deferred();
45 }
46
47 void DeferredPromise::callFunction(JSGlobalObject& lexicalGlobalObject, ResolveMode mode, JSValue resolution)
48 {
49     if (shouldIgnoreRequestToFulfill())
50         return;
51
52     if (activeDOMObjectsAreSuspended()) {
53         JSC::Strong<JSC::Unknown, ShouldStrongDestructorGrabLock::Yes> strongResolution(lexicalGlobalObject.vm(), resolution);
54         scriptExecutionContext()->eventLoop().queueTask(TaskSource::Networking, [this, protectedThis = makeRef(*this), mode, strongResolution = WTFMove(strongResolution)]() mutable {
55             if (shouldIgnoreRequestToFulfill())
56                 return;
57
58             JSC::JSGlobalObject* lexicalGlobalObject = globalObject();
59             JSC::JSLockHolder locker(lexicalGlobalObject);
60             callFunction(*globalObject(), mode, strongResolution.get());
61         });
62         return;
63     }
64
65     // FIXME: We could have error since any JS call can throw stack-overflow errors.
66     // https://bugs.webkit.org/show_bug.cgi?id=203402
67     switch (mode) {
68     case ResolveMode::Resolve:
69         deferred()->resolve(&lexicalGlobalObject, resolution);
70         break;
71     case ResolveMode::Reject:
72         deferred()->reject(&lexicalGlobalObject, resolution);
73         break;
74     }
75
76     if (m_mode == Mode::ClearPromiseOnResolve)
77         clear();
78 }
79
80 void DeferredPromise::whenSettled(Function<void()>&& callback)
81 {
82     if (shouldIgnoreRequestToFulfill())
83         return;
84
85     if (activeDOMObjectsAreSuspended()) {
86         scriptExecutionContext()->eventLoop().queueTask(TaskSource::Networking, [this, protectedThis = makeRef(*this), callback = WTFMove(callback)]() mutable {
87             whenSettled(WTFMove(callback));
88         });
89         return;
90     }
91
92     DOMPromise::whenPromiseIsSettled(globalObject(), deferred(), WTFMove(callback));
93 }
94
95 void DeferredPromise::reject()
96 {
97     if (shouldIgnoreRequestToFulfill())
98         return;
99
100     ASSERT(deferred());
101     ASSERT(m_globalObject);
102     auto& lexicalGlobalObject = *m_globalObject;
103     JSC::JSLockHolder locker(&lexicalGlobalObject);
104     reject(lexicalGlobalObject, JSC::jsUndefined());
105 }
106
107 void DeferredPromise::reject(std::nullptr_t)
108 {
109     if (shouldIgnoreRequestToFulfill())
110         return;
111
112     ASSERT(deferred());
113     ASSERT(m_globalObject);
114     auto& lexicalGlobalObject = *m_globalObject;
115     JSC::JSLockHolder locker(&lexicalGlobalObject);
116     reject(lexicalGlobalObject, JSC::jsNull());
117 }
118
119 void DeferredPromise::reject(Exception exception)
120 {
121     if (shouldIgnoreRequestToFulfill())
122         return;
123
124     ASSERT(deferred());
125     ASSERT(m_globalObject);
126     auto& lexicalGlobalObject = *m_globalObject;
127     JSC::JSLockHolder locker(&lexicalGlobalObject);
128
129     if (exception.code() == ExistingExceptionError) {
130         auto scope = DECLARE_CATCH_SCOPE(lexicalGlobalObject.vm());
131
132         EXCEPTION_ASSERT(scope.exception());
133
134         auto error = scope.exception()->value();
135         scope.clearException();
136
137         reject<IDLAny>(error);
138         return;
139     }
140
141     auto scope = DECLARE_THROW_SCOPE(lexicalGlobalObject.vm());
142     auto error = createDOMException(lexicalGlobalObject, WTFMove(exception));
143     if (UNLIKELY(scope.exception())) {
144         ASSERT(isTerminatedExecutionException(lexicalGlobalObject.vm(), scope.exception()));
145         return;
146     }
147
148     reject(lexicalGlobalObject, error);
149 }
150
151 void DeferredPromise::reject(ExceptionCode ec, const String& message)
152 {
153     if (shouldIgnoreRequestToFulfill())
154         return;
155
156     ASSERT(deferred());
157     ASSERT(m_globalObject);
158     auto& lexicalGlobalObject = *m_globalObject;
159     JSC::JSLockHolder locker(&lexicalGlobalObject);
160
161     if (ec == ExistingExceptionError) {
162         auto scope = DECLARE_CATCH_SCOPE(lexicalGlobalObject.vm());
163
164         EXCEPTION_ASSERT(scope.exception());
165
166         auto error = scope.exception()->value();
167         scope.clearException();
168
169         reject<IDLAny>(error);
170         return;
171     }
172
173     auto scope = DECLARE_THROW_SCOPE(lexicalGlobalObject.vm());
174     auto error = createDOMException(&lexicalGlobalObject, ec, message);
175     if (UNLIKELY(scope.exception())) {
176         ASSERT(isTerminatedExecutionException(lexicalGlobalObject.vm(), scope.exception()));
177         return;
178     }
179
180
181     reject(lexicalGlobalObject, error);
182 }
183
184 void DeferredPromise::reject(const JSC::PrivateName& privateName)
185 {
186     if (shouldIgnoreRequestToFulfill())
187         return;
188
189     ASSERT(deferred());
190     ASSERT(m_globalObject);
191     JSC::JSGlobalObject* lexicalGlobalObject = m_globalObject.get();
192     JSC::JSLockHolder locker(lexicalGlobalObject);
193     reject(*lexicalGlobalObject, JSC::Symbol::create(lexicalGlobalObject->vm(), privateName.uid()));
194 }
195
196 void rejectPromiseWithExceptionIfAny(JSC::JSGlobalObject& lexicalGlobalObject, JSDOMGlobalObject& globalObject, JSPromise& promise)
197 {
198     VM& vm = lexicalGlobalObject.vm();
199     auto scope = DECLARE_CATCH_SCOPE(vm);
200
201     if (LIKELY(!scope.exception()))
202         return;
203
204     JSValue error = scope.exception()->value();
205     scope.clearException();
206
207     DeferredPromise::create(globalObject, promise)->reject<IDLAny>(error);
208 }
209
210 Ref<DeferredPromise> createDeferredPromise(JSC::JSGlobalObject&, JSDOMWindow& domWindow)
211 {
212     auto* promise = JSPromise::create(domWindow.vm(), domWindow.promiseStructure());
213     RELEASE_ASSERT(promise);
214     return DeferredPromise::create(domWindow, *promise);
215 }
216
217 JSC::EncodedJSValue createRejectedPromiseWithTypeError(JSC::JSGlobalObject& lexicalGlobalObject, const String& errorMessage, RejectedPromiseWithTypeErrorCause cause)
218 {
219     auto& globalObject = lexicalGlobalObject;
220
221     auto promiseConstructor = globalObject.promiseConstructor();
222     auto rejectFunction = promiseConstructor->get(&lexicalGlobalObject, lexicalGlobalObject.vm().propertyNames->builtinNames().rejectPrivateName());
223     auto* rejectionValue = static_cast<ErrorInstance*>(createTypeError(&lexicalGlobalObject, errorMessage));
224     if (cause == RejectedPromiseWithTypeErrorCause::NativeGetter)
225         rejectionValue->setNativeGetterTypeError();
226
227     CallData callData;
228     auto callType = getCallData(lexicalGlobalObject.vm(), rejectFunction, callData);
229     ASSERT(callType != CallType::None);
230
231     MarkedArgumentBuffer arguments;
232     arguments.append(rejectionValue);
233     ASSERT(!arguments.hasOverflowed());
234
235     return JSValue::encode(call(&lexicalGlobalObject, rejectFunction, callType, callData, promiseConstructor, arguments));
236 }
237
238 static inline JSC::JSValue parseAsJSON(JSC::JSGlobalObject* lexicalGlobalObject, const String& data)
239 {
240     JSC::JSLockHolder lock(lexicalGlobalObject);
241     return JSC::JSONParse(lexicalGlobalObject, data);
242 }
243
244 void fulfillPromiseWithJSON(Ref<DeferredPromise>&& promise, const String& data)
245 {
246     JSC::JSValue value = parseAsJSON(promise->globalObject(), data);
247     if (!value)
248         promise->reject(SyntaxError);
249     else
250         promise->resolve<IDLAny>(value);
251 }
252
253 void fulfillPromiseWithArrayBuffer(Ref<DeferredPromise>&& promise, ArrayBuffer* arrayBuffer)
254 {
255     if (!arrayBuffer) {
256         promise->reject<IDLAny>(createOutOfMemoryError(promise->globalObject()));
257         return;
258     }
259     promise->resolve<IDLInterface<ArrayBuffer>>(*arrayBuffer);
260 }
261
262 void fulfillPromiseWithArrayBuffer(Ref<DeferredPromise>&& promise, const void* data, size_t length)
263 {
264     fulfillPromiseWithArrayBuffer(WTFMove(promise), ArrayBuffer::tryCreate(data, length).get());
265 }
266
267 }