1 /*
2 * Copyright (C) 2013 Google 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 are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
32
33 #include "base/macros.h"
34 #include "third_party/blink/renderer/bindings/core/v8/script_function.h"
35 #include "third_party/blink/renderer/bindings/core/v8/to_v8_for_core.h"
36 #include "third_party/blink/renderer/core/dom/dom_exception.h"
37 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
38 #include "third_party/blink/renderer/platform/bindings/v8_throw_exception.h"
39 #include "v8/include/v8.h"
40
41 namespace blink {
42
43 namespace {
44
45 class PromiseAllHandler final : public GarbageCollected<PromiseAllHandler> {
46 public:
All(ScriptState * script_state,const HeapVector<ScriptPromise> & promises)47 static ScriptPromise All(ScriptState* script_state,
48 const HeapVector<ScriptPromise>& promises) {
49 if (promises.IsEmpty())
50 return ScriptPromise::Cast(script_state,
51 v8::Array::New(script_state->GetIsolate()));
52 return (MakeGarbageCollected<PromiseAllHandler>(script_state, promises))
53 ->resolver_.Promise();
54 }
55
PromiseAllHandler(ScriptState * script_state,HeapVector<ScriptPromise> promises)56 PromiseAllHandler(ScriptState* script_state,
57 HeapVector<ScriptPromise> promises)
58 : number_of_pending_promises_(promises.size()), resolver_(script_state) {
59 DCHECK(!promises.IsEmpty());
60 values_.resize(promises.size());
61 for (wtf_size_t i = 0; i < promises.size(); ++i) {
62 promises[i].Then(CreateFulfillFunction(script_state, i),
63 CreateRejectFunction(script_state));
64 }
65 }
66
Trace(Visitor * visitor) const67 virtual void Trace(Visitor* visitor) const {
68 visitor->Trace(resolver_);
69 visitor->Trace(values_);
70 }
71
72 private:
73 class AdapterFunction : public ScriptFunction {
74 public:
75 enum ResolveType {
76 kFulfilled,
77 kRejected,
78 };
79
Create(ScriptState * script_state,ResolveType resolve_type,wtf_size_t index,PromiseAllHandler * handler)80 static v8::Local<v8::Function> Create(ScriptState* script_state,
81 ResolveType resolve_type,
82 wtf_size_t index,
83 PromiseAllHandler* handler) {
84 AdapterFunction* self = MakeGarbageCollected<AdapterFunction>(
85 script_state, resolve_type, index, handler);
86 return self->BindToV8Function();
87 }
88
AdapterFunction(ScriptState * script_state,ResolveType resolve_type,wtf_size_t index,PromiseAllHandler * handler)89 AdapterFunction(ScriptState* script_state,
90 ResolveType resolve_type,
91 wtf_size_t index,
92 PromiseAllHandler* handler)
93 : ScriptFunction(script_state),
94 resolve_type_(resolve_type),
95 index_(index),
96 handler_(handler) {}
97
Trace(Visitor * visitor) const98 void Trace(Visitor* visitor) const override {
99 visitor->Trace(handler_);
100 ScriptFunction::Trace(visitor);
101 }
102
103 private:
Call(ScriptValue value)104 ScriptValue Call(ScriptValue value) override {
105 if (resolve_type_ == kFulfilled)
106 handler_->OnFulfilled(index_, value);
107 else
108 handler_->OnRejected(value);
109 // This return value is never used.
110 return ScriptValue();
111 }
112
113 const ResolveType resolve_type_;
114 const wtf_size_t index_;
115 Member<PromiseAllHandler> handler_;
116 };
117
CreateFulfillFunction(ScriptState * script_state,wtf_size_t index)118 v8::Local<v8::Function> CreateFulfillFunction(ScriptState* script_state,
119 wtf_size_t index) {
120 return AdapterFunction::Create(script_state, AdapterFunction::kFulfilled,
121 index, this);
122 }
123
CreateRejectFunction(ScriptState * script_state)124 v8::Local<v8::Function> CreateRejectFunction(ScriptState* script_state) {
125 return AdapterFunction::Create(script_state, AdapterFunction::kRejected, 0,
126 this);
127 }
128
OnFulfilled(wtf_size_t index,const ScriptValue & value)129 void OnFulfilled(wtf_size_t index, const ScriptValue& value) {
130 if (is_settled_)
131 return;
132
133 DCHECK_LT(index, values_.size());
134 values_[index] = value;
135 if (--number_of_pending_promises_ > 0)
136 return;
137
138 v8::Local<v8::Value> values = ToV8(values_, resolver_.GetScriptState());
139 MarkPromiseSettled();
140 resolver_.Resolve(values);
141 }
142
OnRejected(const ScriptValue & value)143 void OnRejected(const ScriptValue& value) {
144 if (is_settled_)
145 return;
146 MarkPromiseSettled();
147 resolver_.Reject(value.V8Value());
148 }
149
MarkPromiseSettled()150 void MarkPromiseSettled() {
151 DCHECK(!is_settled_);
152 is_settled_ = true;
153 values_.clear();
154 }
155
156 size_t number_of_pending_promises_;
157 ScriptPromise::InternalResolver resolver_;
158 bool is_settled_ = false;
159
160 // This is cleared when owners of this handler, that is, given promises are
161 // settled.
162 HeapVector<ScriptValue> values_;
163
164 DISALLOW_COPY_AND_ASSIGN(PromiseAllHandler);
165 };
166
167 } // namespace
168
InternalResolver(ScriptState * script_state)169 ScriptPromise::InternalResolver::InternalResolver(ScriptState* script_state)
170 : script_state_(script_state),
171 resolver_(script_state->GetIsolate(),
172 v8::Promise::Resolver::New(script_state->GetContext())) {
173 // |resolver| can be empty when the thread is being terminated. We ignore such
174 // errors.
175 }
176
V8Promise() const177 v8::Local<v8::Promise> ScriptPromise::InternalResolver::V8Promise() const {
178 if (resolver_.IsEmpty())
179 return v8::Local<v8::Promise>();
180 return resolver_.V8Value().As<v8::Promise::Resolver>()->GetPromise();
181 }
182
Promise() const183 ScriptPromise ScriptPromise::InternalResolver::Promise() const {
184 if (resolver_.IsEmpty())
185 return ScriptPromise();
186 return ScriptPromise(script_state_, V8Promise());
187 }
188
Resolve(v8::Local<v8::Value> value)189 void ScriptPromise::InternalResolver::Resolve(v8::Local<v8::Value> value) {
190 if (resolver_.IsEmpty())
191 return;
192 v8::Maybe<bool> result =
193 resolver_.V8Value().As<v8::Promise::Resolver>()->Resolve(
194 script_state_->GetContext(), value);
195 // |result| can be empty when the thread is being terminated. We ignore such
196 // errors.
197 ALLOW_UNUSED_LOCAL(result);
198
199 Clear();
200 }
201
Reject(v8::Local<v8::Value> value)202 void ScriptPromise::InternalResolver::Reject(v8::Local<v8::Value> value) {
203 if (resolver_.IsEmpty())
204 return;
205 v8::Maybe<bool> result =
206 resolver_.V8Value().As<v8::Promise::Resolver>()->Reject(
207 script_state_->GetContext(), value);
208 // |result| can be empty when the thread is being terminated. We ignore such
209 // errors.
210 ALLOW_UNUSED_LOCAL(result);
211
212 Clear();
213 }
214
ScriptPromise(ScriptState * script_state,v8::Local<v8::Value> value)215 ScriptPromise::ScriptPromise(ScriptState* script_state,
216 v8::Local<v8::Value> value)
217 : script_state_(script_state) {
218 if (value.IsEmpty())
219 return;
220
221 if (!value->IsPromise()) {
222 promise_ = ScriptValue();
223 V8ThrowException::ThrowTypeError(script_state->GetIsolate(),
224 "the given value is not a Promise");
225 return;
226 }
227 promise_ = ScriptValue(script_state->GetIsolate(), value);
228 }
229
ScriptPromise(const ScriptPromise & other)230 ScriptPromise::ScriptPromise(const ScriptPromise& other) {
231 this->script_state_ = other.script_state_;
232 this->promise_ = other.promise_;
233 }
234
Then(v8::Local<v8::Function> on_fulfilled,v8::Local<v8::Function> on_rejected)235 ScriptPromise ScriptPromise::Then(v8::Local<v8::Function> on_fulfilled,
236 v8::Local<v8::Function> on_rejected) {
237 if (promise_.IsEmpty())
238 return ScriptPromise();
239
240 v8::Local<v8::Promise> promise = promise_.V8Value().As<v8::Promise>();
241
242 if (on_fulfilled.IsEmpty() && on_rejected.IsEmpty())
243 return *this;
244
245 v8::Local<v8::Promise> result_promise;
246 if (on_rejected.IsEmpty()) {
247 if (!promise->Then(script_state_->GetContext(), on_fulfilled)
248 .ToLocal(&result_promise)) {
249 return ScriptPromise();
250 }
251 return ScriptPromise(script_state_, result_promise);
252 }
253
254 if (on_fulfilled.IsEmpty()) {
255 if (!promise->Catch(script_state_->GetContext(), on_rejected)
256 .ToLocal(&result_promise)) {
257 return ScriptPromise();
258 }
259 return ScriptPromise(script_state_, result_promise);
260 }
261
262 if (!promise->Then(script_state_->GetContext(), on_fulfilled, on_rejected)
263 .ToLocal(&result_promise)) {
264 return ScriptPromise();
265 }
266 return ScriptPromise(script_state_, result_promise);
267 }
268
CastUndefined(ScriptState * script_state)269 ScriptPromise ScriptPromise::CastUndefined(ScriptState* script_state) {
270 return ScriptPromise::Cast(script_state,
271 v8::Undefined(script_state->GetIsolate()));
272 }
273
Cast(ScriptState * script_state,const ScriptValue & value)274 ScriptPromise ScriptPromise::Cast(ScriptState* script_state,
275 const ScriptValue& value) {
276 return ScriptPromise::Cast(script_state, value.V8Value());
277 }
278
Cast(ScriptState * script_state,v8::Local<v8::Value> value)279 ScriptPromise ScriptPromise::Cast(ScriptState* script_state,
280 v8::Local<v8::Value> value) {
281 if (value.IsEmpty())
282 return ScriptPromise();
283 if (value->IsPromise()) {
284 return ScriptPromise(script_state, value);
285 }
286 InternalResolver resolver(script_state);
287 ScriptPromise promise = resolver.Promise();
288 resolver.Resolve(value);
289 return promise;
290 }
291
Reject(ScriptState * script_state,const ScriptValue & value)292 ScriptPromise ScriptPromise::Reject(ScriptState* script_state,
293 const ScriptValue& value) {
294 return ScriptPromise::Reject(script_state, value.V8Value());
295 }
296
Reject(ScriptState * script_state,v8::Local<v8::Value> value)297 ScriptPromise ScriptPromise::Reject(ScriptState* script_state,
298 v8::Local<v8::Value> value) {
299 if (value.IsEmpty())
300 return ScriptPromise();
301 InternalResolver resolver(script_state);
302 ScriptPromise promise = resolver.Promise();
303 resolver.Reject(value);
304 return promise;
305 }
306
Reject(ScriptState * script_state,ExceptionState & exception_state)307 ScriptPromise ScriptPromise::Reject(ScriptState* script_state,
308 ExceptionState& exception_state) {
309 DCHECK(exception_state.HadException());
310 ScriptPromise promise = Reject(script_state, exception_state.GetException());
311 exception_state.ClearException();
312 return promise;
313 }
314
RejectWithDOMException(ScriptState * script_state,DOMException * exception)315 ScriptPromise ScriptPromise::RejectWithDOMException(ScriptState* script_state,
316 DOMException* exception) {
317 DCHECK(script_state->GetIsolate()->InContext());
318 return Reject(script_state,
319 ToV8(exception, script_state->GetContext()->Global(),
320 script_state->GetIsolate()));
321 }
322
RejectRaw(ScriptState * script_state,v8::Local<v8::Value> value)323 v8::Local<v8::Promise> ScriptPromise::RejectRaw(ScriptState* script_state,
324 v8::Local<v8::Value> value) {
325 if (value.IsEmpty())
326 return v8::Local<v8::Promise>();
327 v8::Local<v8::Promise::Resolver> resolver;
328 if (!v8::Promise::Resolver::New(script_state->GetContext())
329 .ToLocal(&resolver))
330 return v8::Local<v8::Promise>();
331 v8::Local<v8::Promise> promise = resolver->GetPromise();
332 resolver->Reject(script_state->GetContext(), value).ToChecked();
333 return promise;
334 }
335
MarkAsHandled()336 void ScriptPromise::MarkAsHandled() {
337 if (promise_.IsEmpty())
338 return;
339 promise_.V8Value().As<v8::Promise>()->MarkAsHandled();
340 }
341
All(ScriptState * script_state,const HeapVector<ScriptPromise> & promises)342 ScriptPromise ScriptPromise::All(ScriptState* script_state,
343 const HeapVector<ScriptPromise>& promises) {
344 return PromiseAllHandler::All(script_state, promises);
345 }
346
347 } // namespace blink
348