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 | |
37 | namespace WebCore { |
38 | using namespace JSC; |
39 | |
40 | JSC::JSValue DeferredPromise::promise() const |
41 | { |
42 | ASSERT(deferred()); |
43 | return deferred()->promise(); |
44 | } |
45 | |
46 | void DeferredPromise::callFunction(ExecState& exec, JSValue function, JSValue resolution) |
47 | { |
48 | if (!canInvokeCallback()) |
49 | return; |
50 | |
51 | VM& vm = exec.vm(); |
52 | auto scope = DECLARE_THROW_SCOPE(vm); |
53 | |
54 | CallData callData; |
55 | CallType callType = getCallData(vm, function, callData); |
56 | ASSERT(callType != CallType::None); |
57 | |
58 | MarkedArgumentBuffer arguments; |
59 | arguments.append(resolution); |
60 | ASSERT(!arguments.hasOverflowed()); |
61 | |
62 | call(&exec, function, callType, callData, jsUndefined(), arguments); |
63 | |
64 | // DeferredPromise should only be used by internal implementations that are well behaved. |
65 | // In practice, the only exception we should ever see here is the TerminatedExecutionException. |
66 | EXCEPTION_ASSERT_UNUSED(scope, !scope.exception() || isTerminatedExecutionException(vm, scope.exception())); |
67 | |
68 | if (m_mode == Mode::ClearPromiseOnResolve) |
69 | clear(); |
70 | } |
71 | |
72 | void DeferredPromise::whenSettled(std::function<void()>&& callback) |
73 | { |
74 | DOMPromise::whenPromiseIsSettled(globalObject(), deferred()->promise(), WTFMove(callback)); |
75 | } |
76 | |
77 | void DeferredPromise::reject() |
78 | { |
79 | if (isSuspended()) |
80 | return; |
81 | |
82 | ASSERT(deferred()); |
83 | ASSERT(m_globalObject); |
84 | auto& state = *m_globalObject->globalExec(); |
85 | JSC::JSLockHolder locker(&state); |
86 | reject(state, JSC::jsUndefined()); |
87 | } |
88 | |
89 | void DeferredPromise::reject(std::nullptr_t) |
90 | { |
91 | if (isSuspended()) |
92 | return; |
93 | |
94 | ASSERT(deferred()); |
95 | ASSERT(m_globalObject); |
96 | auto& state = *m_globalObject->globalExec(); |
97 | JSC::JSLockHolder locker(&state); |
98 | reject(state, JSC::jsNull()); |
99 | } |
100 | |
101 | void DeferredPromise::reject(Exception exception) |
102 | { |
103 | if (isSuspended()) |
104 | return; |
105 | |
106 | ASSERT(deferred()); |
107 | ASSERT(m_globalObject); |
108 | auto& state = *m_globalObject->globalExec(); |
109 | JSC::JSLockHolder locker(&state); |
110 | |
111 | if (exception.code() == ExistingExceptionError) { |
112 | auto scope = DECLARE_CATCH_SCOPE(state.vm()); |
113 | |
114 | EXCEPTION_ASSERT(scope.exception()); |
115 | |
116 | auto error = scope.exception()->value(); |
117 | scope.clearException(); |
118 | |
119 | reject<IDLAny>(error); |
120 | return; |
121 | } |
122 | |
123 | auto scope = DECLARE_THROW_SCOPE(state.vm()); |
124 | auto error = createDOMException(state, WTFMove(exception)); |
125 | if (UNLIKELY(scope.exception())) { |
126 | ASSERT(isTerminatedExecutionException(state.vm(), scope.exception())); |
127 | return; |
128 | } |
129 | |
130 | reject(state, error); |
131 | } |
132 | |
133 | void DeferredPromise::reject(ExceptionCode ec, const String& message) |
134 | { |
135 | if (isSuspended()) |
136 | return; |
137 | |
138 | ASSERT(deferred()); |
139 | ASSERT(m_globalObject); |
140 | auto& state = *m_globalObject->globalExec(); |
141 | JSC::JSLockHolder locker(&state); |
142 | |
143 | if (ec == ExistingExceptionError) { |
144 | auto scope = DECLARE_CATCH_SCOPE(state.vm()); |
145 | |
146 | EXCEPTION_ASSERT(scope.exception()); |
147 | |
148 | auto error = scope.exception()->value(); |
149 | scope.clearException(); |
150 | |
151 | reject<IDLAny>(error); |
152 | return; |
153 | } |
154 | |
155 | auto scope = DECLARE_THROW_SCOPE(state.vm()); |
156 | auto error = createDOMException(&state, ec, message); |
157 | if (UNLIKELY(scope.exception())) { |
158 | ASSERT(isTerminatedExecutionException(state.vm(), scope.exception())); |
159 | return; |
160 | } |
161 | |
162 | |
163 | reject(state, error); |
164 | } |
165 | |
166 | void DeferredPromise::reject(const JSC::PrivateName& privateName) |
167 | { |
168 | if (isSuspended()) |
169 | return; |
170 | |
171 | ASSERT(deferred()); |
172 | ASSERT(m_globalObject); |
173 | JSC::ExecState* state = m_globalObject->globalExec(); |
174 | JSC::JSLockHolder locker(state); |
175 | reject(*state, JSC::Symbol::create(state->vm(), privateName.uid())); |
176 | } |
177 | |
178 | void rejectPromiseWithExceptionIfAny(JSC::ExecState& state, JSDOMGlobalObject& globalObject, JSPromiseDeferred& promiseDeferred) |
179 | { |
180 | VM& vm = state.vm(); |
181 | auto scope = DECLARE_CATCH_SCOPE(vm); |
182 | |
183 | if (LIKELY(!scope.exception())) |
184 | return; |
185 | |
186 | JSValue error = scope.exception()->value(); |
187 | scope.clearException(); |
188 | |
189 | DeferredPromise::create(globalObject, promiseDeferred)->reject<IDLAny>(error); |
190 | } |
191 | |
192 | Ref<DeferredPromise> createDeferredPromise(JSC::ExecState& state, JSDOMWindow& domWindow) |
193 | { |
194 | JSC::JSPromiseDeferred* deferred = JSC::JSPromiseDeferred::tryCreate(&state, &domWindow); |
195 | // deferred can only be null in workers. |
196 | RELEASE_ASSERT(deferred); |
197 | return DeferredPromise::create(domWindow, *deferred); |
198 | } |
199 | |
200 | JSC::EncodedJSValue createRejectedPromiseWithTypeError(JSC::ExecState& state, const String& errorMessage, RejectedPromiseWithTypeErrorCause cause) |
201 | { |
202 | ASSERT(state.lexicalGlobalObject()); |
203 | auto& globalObject = *state.lexicalGlobalObject(); |
204 | |
205 | auto promiseConstructor = globalObject.promiseConstructor(); |
206 | auto rejectFunction = promiseConstructor->get(&state, state.vm().propertyNames->builtinNames().rejectPrivateName()); |
207 | auto* rejectionValue = static_cast<ErrorInstance*>(createTypeError(&state, errorMessage)); |
208 | if (cause == RejectedPromiseWithTypeErrorCause::NativeGetter) |
209 | rejectionValue->setNativeGetterTypeError(); |
210 | |
211 | CallData callData; |
212 | auto callType = getCallData(state.vm(), rejectFunction, callData); |
213 | ASSERT(callType != CallType::None); |
214 | |
215 | MarkedArgumentBuffer arguments; |
216 | arguments.append(rejectionValue); |
217 | ASSERT(!arguments.hasOverflowed()); |
218 | |
219 | return JSValue::encode(call(&state, rejectFunction, callType, callData, promiseConstructor, arguments)); |
220 | } |
221 | |
222 | static inline JSC::JSValue parseAsJSON(JSC::ExecState* state, const String& data) |
223 | { |
224 | JSC::JSLockHolder lock(state); |
225 | return JSC::JSONParse(state, data); |
226 | } |
227 | |
228 | void fulfillPromiseWithJSON(Ref<DeferredPromise>&& promise, const String& data) |
229 | { |
230 | JSC::JSValue value = parseAsJSON(promise->globalObject()->globalExec(), data); |
231 | if (!value) |
232 | promise->reject(SyntaxError); |
233 | else |
234 | promise->resolve<IDLAny>(value); |
235 | } |
236 | |
237 | void fulfillPromiseWithArrayBuffer(Ref<DeferredPromise>&& promise, ArrayBuffer* arrayBuffer) |
238 | { |
239 | if (!arrayBuffer) { |
240 | promise->reject<IDLAny>(createOutOfMemoryError(promise->globalObject()->globalExec())); |
241 | return; |
242 | } |
243 | promise->resolve<IDLInterface<ArrayBuffer>>(*arrayBuffer); |
244 | } |
245 | |
246 | void fulfillPromiseWithArrayBuffer(Ref<DeferredPromise>&& promise, const void* data, size_t length) |
247 | { |
248 | fulfillPromiseWithArrayBuffer(WTFMove(promise), ArrayBuffer::tryCreate(data, length).get()); |
249 | } |
250 | |
251 | } |
252 | |