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