OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright (C) 2013 Google Inc. All rights reserved. | 2 * Copyright (C) 2013 Google Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
6 * met: | 6 * met: |
7 * | 7 * |
8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
(...skipping 15 matching lines...) Expand all Loading... | |
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 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. | 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
29 */ | 29 */ |
30 | 30 |
31 #include "config.h" | 31 #include "config.h" |
32 #include "bindings/v8/custom/V8PromiseCustom.h" | 32 #include "bindings/v8/custom/V8PromiseCustom.h" |
33 | 33 |
34 #include "V8Promise.h" | 34 #include "V8Promise.h" |
35 #include "V8PromiseResolver.h" | 35 #include "V8PromiseResolver.h" |
36 #include "bindings/v8/ScopedPersistent.h" | |
37 #include "bindings/v8/ScriptFunctionCall.h" | |
36 #include "bindings/v8/V8Binding.h" | 38 #include "bindings/v8/V8Binding.h" |
39 #include "bindings/v8/V8PerIsolateData.h" | |
37 #include "bindings/v8/V8ScriptRunner.h" | 40 #include "bindings/v8/V8ScriptRunner.h" |
38 #include "bindings/v8/WrapperTypeInfo.h" | 41 #include "bindings/v8/WrapperTypeInfo.h" |
42 #include "core/dom/Document.h" | |
43 #include "core/dom/ExceptionCode.h" | |
44 #include "core/page/DOMWindow.h" | |
45 #include "core/platform/Task.h" | |
46 #include "wtf/Functional.h" | |
47 #include "wtf/PassOwnPtr.h" | |
39 #include <v8.h> | 48 #include <v8.h> |
40 | 49 |
41 namespace WebCore { | 50 namespace WebCore { |
42 | 51 |
52 namespace { | |
53 | |
54 class PromiseTask : public ScriptExecutionContext::Task { | |
55 public: | |
56 PromiseTask(v8::Handle<v8::Function> callback, v8::Handle<v8::Object> receiv er, v8::Handle<v8::Value> result) | |
57 : m_callback(callback) | |
58 , m_receiver(receiver) | |
59 , m_result(result) | |
60 { | |
61 ASSERT(!callback.IsEmpty()); | |
62 ASSERT(!receiver.IsEmpty()); | |
63 ASSERT(!result.IsEmpty()); | |
abarth-chromium
2013/06/27 06:34:01
You can check the m_ versions of these, if you lik
yhirano
2013/06/27 09:29:23
Done.
| |
64 } | |
65 virtual ~PromiseTask() { } | |
66 | |
67 virtual void performTask(ScriptExecutionContext*) OVERRIDE; | |
68 | |
69 private: | |
70 ScopedPersistent<v8::Function> m_callback; | |
71 ScopedPersistent<v8::Object> m_receiver; | |
72 ScopedPersistent<v8::Value> m_result; | |
73 }; | |
74 | |
75 void PromiseTask::performTask(ScriptExecutionContext* context) | |
76 { | |
77 if (context->activeDOMObjectsAreStopped()) | |
78 return; | |
79 ASSERT(context->isDocument()); | |
haraken
2013/06/27 06:55:56
Nit: You can move this check to the head of this m
yhirano
2013/06/27 09:29:23
Done.
| |
80 ScriptState* state = mainWorldScriptState(static_cast<Document*>(context)->f rame()); | |
81 v8::HandleScope handleScope; | |
82 ASSERT(state); | |
83 v8::Handle<v8::Context> v8Context = state->context(); | |
84 ASSERT(!v8Context.IsEmpty()); | |
85 v8::Context::Scope scope(v8Context); | |
86 v8::Isolate* isolate = v8Context->GetIsolate(); | |
87 ASSERT(!m_callback.newLocal(isolate).IsEmpty()); | |
88 ASSERT(!m_receiver.newLocal(isolate).IsEmpty()); | |
89 ASSERT(!m_result.newLocal(isolate).IsEmpty()); | |
abarth-chromium
2013/06/27 06:34:01
No need to call newLocal here. ScopedPersistent h
yhirano
2013/06/27 09:29:23
I deleted the ASSERTs.
| |
90 v8::Handle<v8::Value> result = m_result.newLocal(isolate); | |
91 V8ScriptRunner::callFunction(m_callback.newLocal(isolate), context, m_receiv er.newLocal(isolate), 1, &result); | |
haraken
2013/06/27 06:55:56
Nit: We normally write this as follows:
v8::Han
yhirano
2013/06/27 09:29:23
Done.
| |
92 }; | |
93 | |
94 v8::Handle<v8::Value> postTask(v8::Handle<v8::Function> callback, v8::Handle<v8: :Object> receiver, v8::Handle<v8::Value> value, v8::Isolate* isolate) | |
95 { | |
96 DOMWindow* window = activeDOMWindow(); | |
97 ASSERT(window); | |
98 Document* document = window->document(); | |
99 ASSERT(document); | |
100 document->postTask(adoptPtr(new PromiseTask(callback, receiver, value))); | |
101 return v8::Undefined(isolate); | |
102 } | |
103 | |
104 } // namespace | |
105 | |
43 void V8Promise::constructorCustom(const v8::FunctionCallbackInfo<v8::Value>& arg s) | 106 void V8Promise::constructorCustom(const v8::FunctionCallbackInfo<v8::Value>& arg s) |
44 { | 107 { |
45 v8SetReturnValue(args, v8::Local<v8::Value>()); | 108 v8SetReturnValue(args, v8::Local<v8::Value>()); |
46 v8::Isolate* isolate = args.GetIsolate(); | 109 v8::Isolate* isolate = args.GetIsolate(); |
47 if (!args.Length() || args[0].IsEmpty() || !args[0]->IsFunction()) { | 110 if (!args.Length() || args[0].IsEmpty() || !args[0]->IsFunction()) { |
48 throwTypeError("Promise constructor takes a function argument", isolate) ; | 111 throwTypeError("Promise constructor takes a function argument", isolate) ; |
49 return; | 112 return; |
50 } | 113 } |
51 v8::Handle<v8::Function> init = args[0].As<v8::Function>(); | 114 v8::Local<v8::Function> init = args[0].As<v8::Function>(); |
52 v8::Local<v8::ObjectTemplate> internalTemplate = v8::ObjectTemplate::New(); | 115 v8::Local<v8::Object> promise, resolver; |
53 internalTemplate->SetInternalFieldCount(V8PromiseCustom::InternalFieldCount) ; | 116 V8PromiseCustom::createPromise(args.Holder(), &promise, &resolver, isolate); |
54 v8::Local<v8::Object> internal = internalTemplate->NewInstance(); | |
55 v8::Local<v8::Object> promise = V8DOMWrapper::createWrapper(args.Holder(), & V8Promise::info, 0, isolate); | |
56 v8::Local<v8::Object> promiseResolver = V8DOMWrapper::createWrapper(args.Hol der(), &V8PromiseResolver::info, 0, isolate); | |
57 | |
58 internal->SetInternalField(V8PromiseCustom::InternalStateIndex, v8::NumberOb ject::New(V8PromiseCustom::Pending)); | |
59 internal->SetInternalField(V8PromiseCustom::InternalResultIndex, v8::Undefin ed()); | |
60 internal->SetInternalField(V8PromiseCustom::InternalFulfillCallbackIndex, v8 ::Array::New()); | |
61 internal->SetInternalField(V8PromiseCustom::InternalRejectCallbackIndex, v8: :Array::New()); | |
62 | |
63 promise->SetInternalField(v8DOMWrapperObjectIndex, internal); | |
64 promiseResolver->SetInternalField(v8DOMWrapperObjectIndex, internal); | |
65 | |
66 v8::Handle<v8::Value> argv[] = { | 117 v8::Handle<v8::Value> argv[] = { |
67 promiseResolver, | 118 resolver, |
68 }; | 119 }; |
69 v8::TryCatch trycatch; | 120 v8::TryCatch trycatch; |
70 if (V8ScriptRunner::callFunction(init, getScriptExecutionContext(), promise, WTF_ARRAY_LENGTH(argv), argv).IsEmpty()) { | 121 if (V8ScriptRunner::callFunction(init, getScriptExecutionContext(), promise, WTF_ARRAY_LENGTH(argv), argv).IsEmpty()) { |
71 // FIXME: An exception is thrown. Reject the promise. | 122 // An exception is thrown. Reject the promise. |
123 V8PromiseCustom::rejectResolver(resolver, trycatch.Exception(), V8Promis eCustom::Asynchronous, isolate); | |
72 } | 124 } |
73 v8SetReturnValue(args, promise); | 125 v8SetReturnValue(args, promise); |
74 return; | 126 return; |
75 } | 127 } |
76 | 128 |
129 // | |
130 // -- V8PromiseCustom -- | |
131 void V8PromiseCustom::createPromise(v8::Handle<v8::Object> creationContext, v8:: Local<v8::Object>* promise, v8::Local<v8::Object>* resolver, v8::Isolate* isolat e) | |
132 { | |
133 // FIXME: v8::ObjectTemplate::New should be cached. | |
134 v8::Local<v8::ObjectTemplate> internalTemplate = v8::ObjectTemplate::New(); | |
135 internalTemplate->SetInternalFieldCount(InternalFieldCount); | |
136 v8::Local<v8::Object> internal = internalTemplate->NewInstance(); | |
137 *promise = V8DOMWrapper::createWrapper(creationContext, &V8Promise::info, 0, isolate); | |
138 *resolver = V8DOMWrapper::createWrapper(creationContext, &V8PromiseResolver: :info, 0, isolate); | |
139 | |
140 clearInternal(internal, V8PromiseCustom::Pending, v8::Undefined(isolate)); | |
141 | |
142 (*promise)->SetInternalField(v8DOMWrapperObjectIndex, internal); | |
143 (*resolver)->SetInternalField(v8DOMWrapperObjectIndex, internal); | |
144 } | |
145 | |
146 void V8PromiseCustom::fulfillResolver(v8::Handle<v8::Object> resolver, v8::Handl e<v8::Value> result, SynchronousMode mode, v8::Isolate* isolate) | |
147 { | |
148 ASSERT(!resolver.IsEmpty()); | |
149 ASSERT(!result.IsEmpty()); | |
150 v8::Local<v8::Object> internal = getInternal(resolver); | |
151 if (internal.IsEmpty() || getState(internal) != Pending) { | |
152 // The resolver is already fulfilled or rejected. | |
153 return; | |
154 } | |
155 | |
156 v8::Local<v8::Value> value; | |
157 value = internal->GetInternalField(V8PromiseCustom::InternalFulfillCallbackI ndex); | |
abarth-chromium
2013/06/27 06:34:01
You can combine these lines.
yhirano
2013/06/27 09:29:23
Done.
| |
158 ASSERT(!value.IsEmpty()); | |
159 ASSERT(value->IsArray()); | |
160 v8::Local<v8::Array> callbacks = value.As<v8::Array>(); | |
161 clearInternal(internal, Fulfilled, result); | |
162 // The internal object is no more needed in this PromiseResolver. | |
163 resolver->SetInternalField(v8DOMWrapperObjectIndex, v8::Null(isolate)); | |
164 | |
165 v8::Local<v8::Object> global = isolate->GetCurrentContext()->Global(); | |
166 for (uint32_t i = 0, length = callbacks->Length(); i < length; ++i) { | |
167 value = callbacks->Get(i); | |
168 ASSERT(!value.IsEmpty()); | |
169 ASSERT(value->IsFunction()); | |
170 v8::Local<v8::Function> callback = value.As<v8::Function>(); | |
171 call(callback, global, result, mode, isolate); | |
abarth-chromium
2013/06/27 06:34:01
Do we need to check for exceptions?
yhirano
2013/06/27 09:29:23
V8PromiseCustom::call doesn't throws an exception,
| |
172 } | |
173 } | |
174 | |
175 void V8PromiseCustom::rejectResolver(v8::Handle<v8::Object> resolver, v8::Handle <v8::Value> result, SynchronousMode mode, v8::Isolate* isolate) | |
abarth-chromium
2013/06/27 06:34:01
Can we share more code between this function and f
yhirano
2013/06/27 09:29:23
Done.
| |
176 { | |
177 ASSERT(!resolver.IsEmpty()); | |
178 ASSERT(!result.IsEmpty()); | |
179 v8::Local<v8::Object> internal = getInternal(resolver); | |
180 if (internal.IsEmpty() || getState(internal) != Pending) { | |
181 // The resolver is already fulfilled or rejected. | |
182 return; | |
183 } | |
184 | |
185 v8::Local<v8::Value> value; | |
186 value = internal->GetInternalField(V8PromiseCustom::InternalRejectCallbackIn dex); | |
187 ASSERT(!value.IsEmpty()); | |
188 ASSERT(value->IsArray()); | |
189 v8::Local<v8::Array> callbacks = value.As<v8::Array>(); | |
190 clearInternal(internal, Rejected, result); | |
191 // The internal object is no more needed in this PromiseResolver. | |
192 resolver->SetInternalField(v8DOMWrapperObjectIndex, v8::Null(isolate)); | |
193 | |
194 v8::Local<v8::Object> global = isolate->GetCurrentContext()->Global(); | |
195 for (uint32_t i = 0, length = callbacks->Length(); i < length; ++i) { | |
196 value = callbacks->Get(i); | |
197 ASSERT(!value.IsEmpty()); | |
198 ASSERT(value->IsFunction()); | |
199 v8::Local<v8::Function> callback = value.As<v8::Function>(); | |
200 call(callback, global, result, mode, isolate); | |
201 } | |
202 } | |
203 | |
204 void V8PromiseCustom::append(v8::Handle<v8::Object> promise, v8::Handle<v8::Func tion> fulfillCallback, v8::Handle<v8::Function> rejectCallback, v8::Isolate* iso late) | |
205 { | |
206 v8::Local<v8::Object> internal = getInternal(promise); | |
207 ASSERT(!internal.IsEmpty()); | |
208 | |
209 PromiseState state = getState(internal); | |
210 if (state == Fulfilled) { | |
211 if (!fulfillCallback.IsEmpty()) { | |
212 v8::Local<v8::Value> result = internal->GetInternalField(V8PromiseCu stom::InternalResultIndex); | |
213 v8::Local<v8::Object> global = isolate->GetCurrentContext()->Global( ); | |
214 call(fulfillCallback, global, result, Asynchronous, isolate); | |
215 } | |
216 return; | |
217 } | |
218 if (state == Rejected) { | |
219 if (!rejectCallback.IsEmpty()) { | |
220 v8::Local<v8::Value> result = internal->GetInternalField(V8PromiseCu stom::InternalResultIndex); | |
221 v8::Local<v8::Object> global = isolate->GetCurrentContext()->Global( ); | |
222 call(rejectCallback, global, result, Asynchronous, isolate); | |
223 } | |
224 return; | |
225 } | |
226 | |
227 ASSERT(state == Pending); | |
228 if (!fulfillCallback.IsEmpty()) { | |
229 v8::Local<v8::Value> value = internal->GetInternalField(InternalFulfillC allbackIndex); | |
230 ASSERT(!value.IsEmpty()); | |
231 ASSERT(value->IsArray()); | |
232 v8::Local<v8::Array> callbacks = value.As<v8::Array>(); | |
233 callbacks->Set(callbacks->Length(), fulfillCallback); | |
234 } | |
235 if (!rejectCallback.IsEmpty()) { | |
236 v8::Local<v8::Value> value = internal->GetInternalField(InternalRejectCa llbackIndex); | |
237 ASSERT(!value.IsEmpty()); | |
238 ASSERT(value->IsArray()); | |
239 v8::Local<v8::Array> callbacks = value.As<v8::Array>(); | |
240 callbacks->Set(callbacks->Length(), rejectCallback); | |
241 } | |
242 } | |
243 | |
244 v8::Local<v8::Object> V8PromiseCustom::getInternal(v8::Handle<v8::Object> promis eOrPromiseResolver) | |
245 { | |
246 ASSERT(!promiseOrPromiseResolver.IsEmpty()); | |
247 v8::Local<v8::Value> value = promiseOrPromiseResolver->GetInternalField(v8DO MWrapperObjectIndex); | |
248 ASSERT(!value.IsEmpty()); | |
249 if (value->IsNull()) | |
haraken
2013/06/27 06:55:56
What if value is undefined?
yhirano
2013/06/27 09:29:23
Null was the special value that indicates the inte
| |
250 return v8::Local<v8::Object>(); | |
251 ASSERT(value->IsObject()); | |
252 return value.As<v8::Object>(); | |
253 } | |
254 | |
255 void V8PromiseCustom::clearInternal(v8::Handle<v8::Object> internal, PromiseStat e state, v8::Handle<v8::Value> value) | |
256 { | |
257 ASSERT(!internal.IsEmpty()); | |
258 | |
259 setState(internal, state); | |
260 internal->SetInternalField(V8PromiseCustom::InternalResultIndex, value); | |
261 internal->SetInternalField(V8PromiseCustom::InternalFulfillCallbackIndex, v8 ::Array::New()); | |
262 internal->SetInternalField(V8PromiseCustom::InternalRejectCallbackIndex, v8: :Array::New()); | |
263 } | |
264 | |
265 V8PromiseCustom::PromiseState V8PromiseCustom::getState(v8::Handle<v8::Object> i nternal) | |
266 { | |
267 ASSERT(!internal.IsEmpty()); | |
268 v8::Handle<v8::Value> value = internal->GetInternalField(V8PromiseCustom::In ternalStateIndex); | |
269 ASSERT(!value.IsEmpty()); | |
270 ASSERT(value->IsNumber()); | |
271 double number = value.As<v8::Number>()->Value(); | |
abarth-chromium
2013/06/27 06:34:01
Why not use a v8::Integer ? We shouldn't have to
haraken
2013/06/27 06:55:56
You can use toInt32() or some utility methods in V
yhirano
2013/06/27 09:29:23
Done.
| |
272 ASSERT(number == Pending || number == Fulfilled || number == Rejected); | |
273 return static_cast<PromiseState>(number); | |
274 } | |
275 | |
276 void V8PromiseCustom::setState(v8::Handle<v8::Object> internal, PromiseState sta te) | |
277 { | |
278 ASSERT(!internal.IsEmpty()); | |
279 ASSERT(state == Pending || state == Fulfilled || state == Rejected); | |
280 internal->SetInternalField(V8PromiseCustom::InternalStateIndex, v8::Number:: New(state)); | |
281 } | |
282 | |
283 void V8PromiseCustom::call(v8::Handle<v8::Function> function, v8::Handle<v8::Obj ect> receiver, v8::Handle<v8::Value> result, SynchronousMode mode, v8::Isolate* isolate) | |
284 { | |
285 ASSERT(!function.IsEmpty()); | |
286 ASSERT(!receiver.IsEmpty()); | |
287 if (mode == Synchronous) { | |
288 v8::TryCatch trycatch; | |
abarth-chromium
2013/06/27 06:34:01
I see. We catch the exception and ignore it.
yhirano
2013/06/27 09:29:23
I added a comment.
| |
289 V8ScriptRunner::callFunction(function, getScriptExecutionContext(), rece iver, 1, &result); | |
haraken
2013/06/27 06:55:56
Nit: WTF_ARRAY_LENGTH(args).
yhirano
2013/06/27 09:29:23
Done.
| |
290 } else { | |
291 ASSERT(mode == Asynchronous); | |
292 postTask(function, receiver, result, isolate); | |
293 } | |
294 } | |
295 | |
77 } // namespace WebCore | 296 } // namespace WebCore |
OLD | NEW |