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/ScriptFunctionCall.h" | |
37 #include "bindings/v8/ScriptValue.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 int wrapperCallbackTag = 0; | |
55 int promiseFulfillCallbackTag = 0; | |
56 int promiseResolveCallbackTag = 0; | |
57 int promiseRejectCallbackTag = 0; | |
58 | |
59 // tag must be a pointer of one of the above tags. | |
60 v8::Local<v8::Function> getFunction(v8::FunctionCallback callback, int* tag, v8: :Isolate* isolate) | |
61 { | |
62 WrapperWorldType worldType = WebCore::worldType(isolate); | |
63 V8PerIsolateData* data = V8PerIsolateData::from(isolate); | |
64 ASSERT(data); | |
65 V8PerIsolateData::TemplateMap::iterator result = data->templateMap(worldType ).find(tag); | |
66 if (result != data->templateMap(worldType).end()) | |
67 return result->value.newLocal(isolate)->GetFunction(); | |
68 | |
69 v8::HandleScope handleScope(isolate); | |
70 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(callback) ; | |
71 data->templateMap(worldType).add(tag, UnsafePersistent<v8::FunctionTemplate> (isolate, templ)); | |
72 return handleScope.Close(templ)->GetFunction(); | |
abarth-chromium
2013/06/27 03:25:16
Don't you mean:
return handleScope.Close(templ->G
yhirano
2013/06/27 05:40:34
Done.
| |
73 } | |
74 | |
75 class PromiseTask : public ScriptExecutionContext::Task { | |
76 public: | |
77 PromiseTask(v8::Handle<v8::Function> callback, v8::Handle<v8::Value> receive r, v8::Handle<v8::Value> result) | |
78 : m_callback(callback), m_receiver(receiver), m_result(result) | |
abarth-chromium
2013/06/27 03:25:16
Please split these up on to separate lines.
yhirano
2013/06/27 05:40:34
Done.
| |
79 { | |
80 ASSERT(!callback.IsEmpty()); | |
81 ASSERT(!receiver.IsEmpty()); | |
82 ASSERT(!result.IsEmpty()); | |
83 } | |
84 virtual ~PromiseTask() { } | |
85 | |
86 virtual void performTask(ScriptExecutionContext*) OVERRIDE; | |
87 | |
88 private: | |
89 ScriptValue m_callback; | |
90 ScriptValue m_receiver; | |
91 ScriptValue m_result; | |
abarth-chromium
2013/06/27 03:25:16
You can use ScopedPersistent rather than ScriptVal
yhirano
2013/06/27 05:40:34
Done.
| |
92 }; | |
93 | |
94 void PromiseTask::performTask(ScriptExecutionContext* context) | |
95 { | |
96 ASSERT(context->isDocument()); | |
97 ScriptState* state = mainWorldScriptState(static_cast<Document*>(context)->f rame()); | |
abarth-chromium
2013/06/27 03:25:16
We should ask the Document whether ActiveDOMObject
yhirano
2013/06/27 05:40:34
Done.
| |
98 v8::HandleScope handleScope; | |
99 if (!state) { | |
100 // We can't execute the task. Ignore it. | |
101 return; | |
102 } | |
abarth-chromium
2013/06/27 03:25:16
Can this occur? If not we shouldn't handle this c
yhirano
2013/06/27 05:40:34
Done.
| |
103 v8::Handle<v8::Context> v8Context = state->context(); | |
104 if (v8Context.IsEmpty()) { | |
105 // We can't execute the task. Ignore it. | |
106 return; | |
107 } | |
abarth-chromium
2013/06/27 03:25:16
Can this occur? If not we shouldn't handle this c
yhirano
2013/06/27 05:40:34
Done.
| |
108 v8::Context::Scope scope(v8Context); | |
109 v8::Handle<v8::Value> result = m_result.v8Value(); | |
110 ASSERT(!m_callback.v8Value().IsEmpty()); | |
111 ASSERT(m_callback.v8Value()->IsFunction()); | |
112 v8::Local<v8::Function> callback = m_callback.v8Value().As<v8::Function>(); | |
113 ASSERT(!m_receiver.v8Value().IsEmpty()); | |
114 ASSERT(m_receiver.v8Value()->IsObject()); | |
115 v8::Local<v8::Object> receiver =m_receiver.v8Value().As<v8::Object>(); | |
abarth-chromium
2013/06/27 03:25:16
You're missing a = before m_receiver.
yhirano
2013/06/27 05:40:34
Done.
| |
116 ASSERT(!result.IsEmpty()); | |
117 v8::Local<v8::Value> v = V8ScriptRunner::callFunction(callback, context, rec eiver, 1, &result); | |
abarth-chromium
2013/06/27 03:25:16
You don't need |v| here. You can just ignore the
yhirano
2013/06/27 05:40:34
Done.
| |
118 }; | |
119 | |
120 v8::Handle<v8::Value> postTask(v8::Handle<v8::Function> callback, v8::Handle<v8: :Value> receiver, v8::Handle<v8::Value> value, v8::Isolate* isolate) | |
121 { | |
122 DOMWindow* window = activeDOMWindow(); | |
123 if (!window) | |
124 return setDOMException(INVALID_STATE_ERR, isolate); | |
abarth-chromium
2013/06/27 03:25:16
Can this occur? If not, we shouldn't handle this
yhirano
2013/06/27 05:40:34
Done.
| |
125 Document* document = window->document(); | |
126 if (!document) | |
127 return setDOMException(INVALID_STATE_ERR, isolate); | |
abarth-chromium
2013/06/27 03:25:16
This cannot occur.
yhirano
2013/06/27 05:40:34
Done.
| |
128 document->postTask(adoptPtr(new PromiseTask(callback, receiver, value))); | |
129 return v8::Undefined(); | |
abarth-chromium
2013/06/27 03:25:16
v8::Undefined(isolate) ?
yhirano
2013/06/27 05:40:34
Done.
| |
130 } | |
131 | |
132 void wrapperCallbackRaw(const v8::FunctionCallbackInfo<v8::Value>& args) | |
133 { | |
134 v8::Isolate* isolate = args.GetIsolate(); | |
135 v8SetReturnValue(args, v8::Undefined()); | |
abarth-chromium
2013/06/27 03:25:16
v8::Undefined(isolate) ?
yhirano
2013/06/27 05:40:34
Done.
| |
136 v8::Local<v8::Object> environment; | |
137 v8::Local<v8::Value> result = v8::Undefined(); | |
abarth-chromium
2013/06/27 03:25:16
v8::Undefined(isolate) ?
yhirano
2013/06/27 05:40:34
Done.
| |
138 if (args.Length() > 0 && !args[0].IsEmpty() && args[0]->IsObject()) | |
139 environment = args[0].As<v8::Object>(); | |
140 if (args.Length() > 1 && !args[1].IsEmpty()) | |
141 result = args[1]; | |
142 ASSERT(!environment.IsEmpty()); | |
abarth-chromium
2013/06/27 03:25:16
Why is this ASSERT valid? If we don't execute lin
yhirano
2013/06/27 05:40:34
Done.
| |
143 | |
144 v8::Local<v8::Value> value; | |
145 | |
146 value = environment->GetInternalField(V8PromiseCustom::WrapperCallbackEnviro nmentPromiseIndex); | |
147 ASSERT(!value.IsEmpty()); | |
148 ASSERT(value->IsObject()); | |
149 v8::Local<v8::Object> promise = value.As<v8::Object>(); | |
150 | |
151 value = environment->GetInternalField(V8PromiseCustom::WrapperCallbackEnviro nmentPromiseResolverIndex); | |
152 ASSERT(!value.IsEmpty()); | |
153 ASSERT(value->IsObject()); | |
154 v8::Local<v8::Object> resolver = value.As<v8::Object>(); | |
155 | |
156 value = environment->GetInternalField(V8PromiseCustom::WrapperCallbackEnviro nmentCallbackIndex); | |
157 ASSERT(!value.IsEmpty()); | |
158 ASSERT(value->IsFunction()); | |
159 v8::Local<v8::Function> callback = value.As<v8::Function>(); | |
160 | |
161 v8::TryCatch trycatch; | |
162 result = V8ScriptRunner::callFunction(callback, getScriptExecutionContext(), promise, 1, &result); | |
163 if (result.IsEmpty()) { | |
164 V8PromiseCustom::rejectResolver(resolver, trycatch.Exception(), true, is olate); | |
165 return; | |
166 } | |
167 V8PromiseCustom::resolveResolver(resolver, result, true, isolate); | |
168 } | |
169 | |
170 v8::Handle<v8::Function> wrapperCallback(v8::Handle<v8::Object> promise, v8::Han dle<v8::Object> resolver, v8::Handle<v8::Function> callback, v8::Isolate* isolat e) | |
171 { | |
172 ASSERT(!promise.IsEmpty()); | |
173 ASSERT(!resolver.IsEmpty()); | |
174 ASSERT(!callback.IsEmpty()); | |
175 // FIXME: v8::ObjectTemplate::New should be cached. | |
176 v8::Local<v8::ObjectTemplate> tmpl = v8::ObjectTemplate::New(); | |
abarth-chromium
2013/06/27 03:25:16
tmpl -> template (please use complete words in va
yhirano
2013/06/27 05:40:34
Done.
| |
177 tmpl->SetInternalFieldCount(V8PromiseCustom::WrapperCallbackEnvironmentField Count); | |
178 v8::Local<v8::Object> env = tmpl->NewInstance(); | |
abarth-chromium
2013/06/27 03:25:16
env -> ??? (please use complete words in variabl
yhirano
2013/06/27 05:40:34
Done.
| |
179 env->SetInternalField(V8PromiseCustom::WrapperCallbackEnvironmentPromiseInde x, promise); | |
180 env->SetInternalField(V8PromiseCustom::WrapperCallbackEnvironmentPromiseReso lverIndex, resolver); | |
181 env->SetInternalField(V8PromiseCustom::WrapperCallbackEnvironmentCallbackInd ex, callback); | |
182 | |
183 // FIXME: If there is a way to bind an object to a function other than evalu ate a JavaScript, it will be preferable. | |
184 // FIXME: script compilation should be cached. | |
185 // We should not depend on the global context that user can change, such as accessing a property, calling a method or so. | |
186 v8::Local<v8::String> script = v8::String::New("(function(f, v1) { return fu nction(v2) { return f(v1, v2); }; })"); | |
187 v8::Local<v8::Value> value = V8ScriptRunner::compileAndRunInternalScript(scr ipt, isolate); | |
abarth-chromium
2013/06/27 03:25:16
There's got to be a better way to do this.
| |
188 ASSERT(!value.IsEmpty()); | |
189 ASSERT(value->IsFunction()); | |
190 | |
191 v8::Local<v8::Value> argv[] = { | |
192 getFunction(wrapperCallbackRaw, &wrapperCallbackTag, isolate), | |
193 env, | |
194 }; | |
195 v8::Local<v8::Object> global = isolate->GetCurrentContext()->Global(); | |
abarth-chromium
2013/06/27 03:25:16
Why do you need to get the global? Doesn't callFu
| |
196 | |
197 value = V8ScriptRunner::callFunction(value.As<v8::Function>(), getScriptExec utionContext(), global, WTF_ARRAY_LENGTH(argv), argv); | |
198 ASSERT(!value.IsEmpty()); | |
199 ASSERT(value->IsFunction()); | |
200 return value.As<v8::Function>(); | |
201 } | |
202 | |
203 void promiseFulfillCallback(const v8::FunctionCallbackInfo<v8::Value>& args) | |
204 { | |
205 v8::Local<v8::Object> resolver; | |
206 v8::Local<v8::Value> result = v8::Undefined(); | |
207 if (args.Length() > 0 && !args[0].IsEmpty() && args[0]->IsObject()) | |
208 resolver = args[0].As<v8::Object>(); | |
209 if (args.Length() > 1 && !args[1].IsEmpty()) | |
210 result = args[1]; | |
211 | |
212 ASSERT(!resolver.IsEmpty()); | |
abarth-chromium
2013/06/27 03:25:16
Again, why do we have this ASSERT and the branch o
yhirano
2013/06/27 05:40:34
Done.
| |
213 V8PromiseCustom::fulfillResolver(resolver, result, true, args.GetIsolate()); | |
214 } | |
215 | |
216 void promiseResolveCallback(const v8::FunctionCallbackInfo<v8::Value>& args) | |
217 { | |
218 v8::Local<v8::Object> resolver; | |
219 v8::Local<v8::Value> result = v8::Undefined(); | |
220 if (args.Length() > 0 && !args[0].IsEmpty() && args[0]->IsObject()) | |
221 resolver = args[0].As<v8::Object>(); | |
222 if (args.Length() > 1 && !args[1].IsEmpty()) | |
223 result = args[1]; | |
224 | |
225 ASSERT(!resolver.IsEmpty()); | |
226 V8PromiseCustom::resolveResolver(resolver, result, true, args.GetIsolate()); | |
227 } | |
228 | |
229 void promiseRejectCallback(const v8::FunctionCallbackInfo<v8::Value>& args) | |
230 { | |
231 v8::Local<v8::Object> resolver; | |
232 v8::Local<v8::Value> result = v8::Undefined(); | |
233 if (args.Length() > 0 && !args[0].IsEmpty() && args[0]->IsObject()) | |
234 resolver = args[0].As<v8::Object>(); | |
235 if (args.Length() > 1 && !args[1].IsEmpty()) | |
236 result = args[1]; | |
237 | |
238 ASSERT(!resolver.IsEmpty()); | |
239 V8PromiseCustom::rejectResolver(resolver, result, true, args.GetIsolate()); | |
240 } | |
241 | |
242 v8::Local<v8::Function> promiseCallback(v8::Handle<v8::Object> resolver, V8Promi seCustom::PromiseAlgorithm algorithm, v8::Isolate* isolate) | |
243 { | |
244 ASSERT(!resolver.IsEmpty()); | |
245 v8::Local<v8::Function> callback; | |
246 switch (algorithm) { | |
247 case V8PromiseCustom::FulfillAlgorithm: | |
248 callback = getFunction(promiseFulfillCallback, &promiseFulfillCallbackTa g, isolate); | |
249 break; | |
250 case V8PromiseCustom::ResolveAlgorithm: | |
251 callback = getFunction(promiseResolveCallback, &promiseResolveCallbackTa g, isolate); | |
252 break; | |
253 case V8PromiseCustom::RejectAlgorithm: | |
254 callback = getFunction(promiseRejectCallback, &promiseRejectCallbackTag, isolate); | |
255 break; | |
256 default: | |
257 ASSERT(0); | |
258 } | |
259 | |
260 // FIXME: If there is a way to bind an object to a function other than evalu ate a JavaScript, it will be preferable. | |
261 // FIXME: script compilation should be cached. | |
262 // We should not depend on the global context that user can change, such as accessing a property, calling a method or so. | |
263 v8::Local<v8::String> script = v8::String::New("(function(f, v1) { return fu nction(v2) { return f(v1, v2); }; })"); | |
264 v8::Local<v8::Value> value = V8ScriptRunner::compileAndRunInternalScript(scr ipt, isolate); | |
265 ASSERT(!value.IsEmpty()); | |
266 ASSERT(value->IsFunction()); | |
267 | |
268 v8::Local<v8::Value> argv[] = { | |
269 callback, | |
270 resolver, | |
271 }; | |
272 v8::Local<v8::Object> receiver = isolate->GetCurrentContext()->Global(); | |
273 | |
274 value = V8ScriptRunner::callFunction(value.As<v8::Function>(), getScriptExec utionContext(), receiver, WTF_ARRAY_LENGTH(argv), argv); | |
275 ASSERT(!value.IsEmpty()); | |
276 ASSERT(value->IsFunction()); | |
277 return value.As<v8::Function>(); | |
278 } | |
279 | |
280 } // namespace | |
281 | |
43 void V8Promise::constructorCustom(const v8::FunctionCallbackInfo<v8::Value>& arg s) | 282 void V8Promise::constructorCustom(const v8::FunctionCallbackInfo<v8::Value>& arg s) |
44 { | 283 { |
45 v8SetReturnValue(args, v8::Local<v8::Value>()); | 284 v8SetReturnValue(args, v8::Local<v8::Value>()); |
46 v8::Isolate* isolate = args.GetIsolate(); | 285 v8::Isolate* isolate = args.GetIsolate(); |
47 if (!args.Length() || args[0].IsEmpty() || !args[0]->IsFunction()) { | 286 if (!args.Length() || args[0].IsEmpty() || !args[0]->IsFunction()) { |
48 throwTypeError("Promise constructor takes a function argument", isolate) ; | 287 throwTypeError("Promise constructor takes a function argument", isolate) ; |
49 return; | 288 return; |
50 } | 289 } |
51 v8::Handle<v8::Function> init = args[0].As<v8::Function>(); | 290 v8::Local<v8::Function> init = args[0].As<v8::Function>(); |
52 v8::Local<v8::ObjectTemplate> internalTemplate = v8::ObjectTemplate::New(); | 291 v8::Local<v8::Object> promise, resolver; |
53 internalTemplate->SetInternalFieldCount(V8PromiseCustom::InternalFieldCount) ; | 292 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[] = { | 293 v8::Handle<v8::Value> argv[] = { |
67 promiseResolver, | 294 resolver, |
68 }; | 295 }; |
69 v8::TryCatch trycatch; | 296 v8::TryCatch trycatch; |
70 if (V8ScriptRunner::callFunction(init, getScriptExecutionContext(), promise, WTF_ARRAY_LENGTH(argv), argv).IsEmpty()) { | 297 if (V8ScriptRunner::callFunction(init, getScriptExecutionContext(), promise, WTF_ARRAY_LENGTH(argv), argv).IsEmpty()) { |
71 // FIXME: An exception is thrown. Reject the promise. | 298 // An exception is thrown. Reject the promise. |
299 V8PromiseCustom::rejectResolver(resolver, trycatch.Exception(), false, i solate); | |
72 } | 300 } |
73 v8SetReturnValue(args, promise); | 301 v8SetReturnValue(args, promise); |
74 return; | 302 return; |
75 } | 303 } |
76 | 304 |
305 void V8Promise::thenMethodCustom(const v8::FunctionCallbackInfo<v8::Value>& args ) | |
306 { | |
307 v8::Isolate* isolate = args.GetIsolate(); | |
308 v8SetReturnValue(args, v8::Local<v8::Value>()); | |
309 v8::Local<v8::Function> fulfillWrapper, rejectWrapper; | |
310 v8::Local<v8::Object> promise, resolver; | |
311 V8PromiseCustom::createPromise(args.Holder(), &promise, &resolver, isolate); | |
312 if (args.Length() > 0 && !args[0].IsEmpty() && !args[0]->IsUndefined()) { | |
313 if (!args[0]->IsFunction()) { | |
314 throwTypeError("fulfillCallback must be a function or undefined", is olate); | |
315 return; | |
316 } | |
317 fulfillWrapper = wrapperCallback(promise, resolver, args[0].As<v8::Funct ion>(), isolate); | |
318 } else { | |
319 fulfillWrapper = promiseCallback(resolver, V8PromiseCustom::FulfillAlgor ithm, isolate); | |
320 } | |
321 if (args.Length() > 1 && !args[1].IsEmpty() && !args[1]->IsUndefined()) { | |
322 if (!args[1]->IsFunction()) { | |
323 throwTypeError("rejectCallback must be a function or undefined", iso late); | |
324 return; | |
325 } | |
326 rejectWrapper = wrapperCallback(promise, resolver, args[1].As<v8::Functi on>(), isolate); | |
327 } else { | |
328 rejectWrapper = promiseCallback(resolver, V8PromiseCustom::RejectAlgorit hm, isolate); | |
329 } | |
330 V8PromiseCustom::append(args.Holder(), fulfillWrapper, rejectWrapper, isolat e); | |
331 v8SetReturnValue(args, promise); | |
332 } | |
333 | |
334 void V8Promise::catchMethodCustom(const v8::FunctionCallbackInfo<v8::Value>& arg s) | |
335 { | |
336 v8::Isolate* isolate = args.GetIsolate(); | |
337 v8SetReturnValue(args, v8::Local<v8::Value>()); | |
338 v8::Local<v8::Function> fulfillWrapper, rejectWrapper; | |
339 v8::Local<v8::Object> promise, resolver; | |
340 V8PromiseCustom::createPromise(args.Holder(), &promise, &resolver, isolate); | |
341 | |
342 if (args.Length() > 0 && !args[0].IsEmpty() && !args[0]->IsUndefined()) { | |
343 if (!args[0]->IsFunction()) { | |
344 throwTypeError("rejectCallback must be a function or undefined", iso late); | |
345 return; | |
346 } | |
347 rejectWrapper = wrapperCallback(promise, resolver, args[0].As<v8::Functi on>(), isolate); | |
348 } else { | |
349 rejectWrapper = promiseCallback(resolver, V8PromiseCustom::RejectAlgorit hm, isolate); | |
350 } | |
351 fulfillWrapper = promiseCallback(resolver, V8PromiseCustom::FulfillAlgorithm , isolate); | |
352 V8PromiseCustom::append(args.Holder(), fulfillWrapper, rejectWrapper, isolat e); | |
353 v8SetReturnValue(args, promise); | |
354 } | |
355 | |
356 // | |
357 // -- V8PromiseCustom -- | |
358 void V8PromiseCustom::createPromise(v8::Handle<v8::Object> creationContext, v8:: Local<v8::Object>* promise, v8::Local<v8::Object>* resolver, v8::Isolate* isolat e) | |
359 { | |
360 // FIXME: v8::ObjectTemplate::New should be cached. | |
361 v8::Local<v8::ObjectTemplate> internalTemplate = v8::ObjectTemplate::New(); | |
362 internalTemplate->SetInternalFieldCount(InternalFieldCount); | |
363 v8::Local<v8::Object> internal = internalTemplate->NewInstance(); | |
364 *promise = V8DOMWrapper::createWrapper(creationContext, &V8Promise::info, 0, isolate); | |
365 *resolver = V8DOMWrapper::createWrapper(creationContext, &V8PromiseResolver: :info, 0, isolate); | |
366 | |
367 clearInternal(internal, V8PromiseCustom::Pending, v8::Undefined()); | |
368 | |
369 (*promise)->SetInternalField(v8DOMWrapperObjectIndex, internal); | |
370 (*resolver)->SetInternalField(v8DOMWrapperObjectIndex, internal); | |
371 } | |
372 | |
373 void V8PromiseCustom::fulfillResolver(v8::Handle<v8::Object> resolver, v8::Handl e<v8::Value> result, bool synchronous, v8::Isolate* isolate) | |
374 { | |
375 ASSERT(!resolver.IsEmpty()); | |
376 ASSERT(!result.IsEmpty()); | |
377 v8::Local<v8::Object> internal = getInternal(resolver); | |
378 if (internal.IsEmpty() || getState(internal) != Pending) { | |
379 // The resolver is already fulfilled or rejected. | |
380 return; | |
381 } | |
382 | |
383 v8::Local<v8::Value> value; | |
384 value = internal->GetInternalField(V8PromiseCustom::InternalFulfillCallbackI ndex); | |
385 ASSERT(!value.IsEmpty()); | |
386 ASSERT(value->IsArray()); | |
387 v8::Local<v8::Array> callbacks = value.As<v8::Array>(); | |
388 clearInternal(internal, Fulfilled, result); | |
389 // The internal object is no more needed in this PromiseResolver. | |
390 resolver->SetInternalField(v8DOMWrapperObjectIndex, v8::Null()); | |
391 | |
392 v8::Local<v8::Object> global = isolate->GetCurrentContext()->Global(); | |
393 for (uint32_t i = 0, length = callbacks->Length(); i < length; ++i) { | |
394 value = callbacks->Get(i); | |
395 ASSERT(!value.IsEmpty()); | |
396 ASSERT(value->IsFunction()); | |
397 v8::Local<v8::Function> callback = value.As<v8::Function>(); | |
398 call(callback, global, result, synchronous, isolate); | |
399 } | |
400 } | |
401 | |
402 void V8PromiseCustom::resolveResolver(v8::Handle<v8::Object> resolver, v8::Handl e<v8::Value> result, bool synchronous, v8::Isolate* isolate) | |
403 { | |
404 ASSERT(!resolver.IsEmpty()); | |
405 ASSERT(!result.IsEmpty()); | |
406 v8::Local<v8::Object> internal = getInternal(resolver); | |
407 if (internal.IsEmpty() || getState(internal) != Pending) { | |
408 // The resolver is already fulfilled or rejected. | |
409 return; | |
410 } | |
411 | |
412 v8::Local<v8::Value> then; | |
413 if (result->IsObject()) { | |
414 v8::TryCatch trycatch; | |
415 then = result.As<v8::Object>()->Get(v8::String::New("then")); | |
416 if (then.IsEmpty()) { | |
417 // If calling the [[Get]] internal method threw an exception, catch it and run reject. | |
418 rejectResolver(resolver, trycatch.Exception(), synchronous, isolate) ; | |
419 return; | |
420 } | |
421 } | |
422 | |
423 if (!then.IsEmpty() && then->IsFunction()) { | |
424 ASSERT(result->IsObject()); | |
425 v8::TryCatch trycatch; | |
426 v8::Handle<v8::Value> argv[] = { | |
427 promiseCallback(resolver, ResolveAlgorithm, isolate), | |
428 promiseCallback(resolver, RejectAlgorithm, isolate), | |
429 }; | |
430 if (V8ScriptRunner::callFunction(then.As<v8::Function>(), getScriptExecu tionContext(), result.As<v8::Object>(), WTF_ARRAY_LENGTH(argv), argv).IsEmpty()) | |
431 rejectResolver(resolver, trycatch.Exception(), synchronous, isolate) ; | |
432 return; | |
433 } | |
434 fulfillResolver(resolver, result, synchronous, isolate); | |
435 } | |
436 | |
437 void V8PromiseCustom::rejectResolver(v8::Handle<v8::Object> resolver, v8::Handle <v8::Value> result, bool synchronous, v8::Isolate* isolate) | |
438 { | |
439 ASSERT(!resolver.IsEmpty()); | |
440 ASSERT(!result.IsEmpty()); | |
441 v8::Local<v8::Object> internal = getInternal(resolver); | |
442 if (internal.IsEmpty() || getState(internal) != Pending) { | |
443 // The resolver is already fulfilled or rejected. | |
444 return; | |
445 } | |
446 | |
447 v8::Local<v8::Value> value; | |
448 value = internal->GetInternalField(V8PromiseCustom::InternalRejectCallbackIn dex); | |
449 ASSERT(!value.IsEmpty()); | |
450 ASSERT(value->IsArray()); | |
451 v8::Local<v8::Array> callbacks = value.As<v8::Array>(); | |
452 clearInternal(internal, Rejected, result); | |
453 // The internal object is no more needed from this PromiseResolver. | |
454 resolver->SetInternalField(v8DOMWrapperObjectIndex, v8::Null()); | |
455 | |
456 v8::Local<v8::Object> global = isolate->GetCurrentContext()->Global(); | |
457 for (uint32_t i = 0, length = callbacks->Length(); i < length; ++i) { | |
458 value = callbacks->Get(i); | |
459 ASSERT(!value.IsEmpty()); | |
460 ASSERT(value->IsFunction()); | |
461 v8::Local<v8::Function> callback = value.As<v8::Function>(); | |
462 call(callback, global, result, synchronous, isolate); | |
463 } | |
464 } | |
465 | |
466 void V8PromiseCustom::append(v8::Handle<v8::Object> promise, v8::Handle<v8::Func tion> fulfillCallback, v8::Handle<v8::Function> rejectCallback, v8::Isolate* iso late) | |
467 { | |
468 v8::Local<v8::Object> internal = getInternal(promise); | |
469 ASSERT(!internal.IsEmpty()); | |
470 | |
471 PromiseState state = getState(internal); | |
472 if (state == Fulfilled) { | |
473 if (!fulfillCallback.IsEmpty()) { | |
474 v8::Local<v8::Value> result = internal->GetInternalField(V8PromiseCu stom::InternalResultIndex); | |
475 v8::Local<v8::Object> global = isolate->GetCurrentContext()->Global( ); | |
476 call(fulfillCallback, global, result, false, isolate); | |
477 } | |
478 return; | |
479 } | |
480 if (state == Rejected) { | |
481 if (!rejectCallback.IsEmpty()) { | |
482 v8::Local<v8::Value> result = internal->GetInternalField(V8PromiseCu stom::InternalResultIndex); | |
483 v8::Local<v8::Object> global = isolate->GetCurrentContext()->Global( ); | |
484 call(rejectCallback, global, result, false, isolate); | |
485 } | |
486 return; | |
487 } | |
488 | |
489 ASSERT(state == Pending); | |
490 if (!fulfillCallback.IsEmpty()) { | |
491 v8::Local<v8::Value> value = internal->GetInternalField(InternalFulfillC allbackIndex); | |
492 ASSERT(!value.IsEmpty()); | |
493 ASSERT(value->IsArray()); | |
494 v8::Local<v8::Array> callbacks = value.As<v8::Array>(); | |
495 callbacks->Set(callbacks->Length(), fulfillCallback); | |
496 } | |
497 if (!rejectCallback.IsEmpty()) { | |
498 v8::Local<v8::Value> value = internal->GetInternalField(InternalRejectCa llbackIndex); | |
499 ASSERT(!value.IsEmpty()); | |
500 ASSERT(value->IsArray()); | |
501 v8::Local<v8::Array> callbacks = value.As<v8::Array>(); | |
502 callbacks->Set(callbacks->Length(), rejectCallback); | |
503 } | |
504 } | |
505 | |
506 v8::Local<v8::Object> V8PromiseCustom::getInternal(v8::Handle<v8::Object> promis eOrPromiseResolver) | |
507 { | |
508 ASSERT(!promiseOrPromiseResolver.IsEmpty()); | |
509 v8::Local<v8::Value> value = promiseOrPromiseResolver->GetInternalField(v8DO MWrapperObjectIndex); | |
510 ASSERT(!value.IsEmpty()); | |
511 if (value->IsNull()) | |
512 return v8::Local<v8::Object>(); | |
513 ASSERT(value->IsObject()); | |
514 return value.As<v8::Object>(); | |
515 } | |
516 | |
517 void V8PromiseCustom::clearInternal(v8::Handle<v8::Object> internal, PromiseStat e state, v8::Handle<v8::Value> value) | |
518 { | |
519 ASSERT(!internal.IsEmpty()); | |
520 | |
521 setState(internal, state); | |
522 internal->SetInternalField(V8PromiseCustom::InternalResultIndex, value); | |
523 internal->SetInternalField(V8PromiseCustom::InternalFulfillCallbackIndex, v8 ::Array::New()); | |
524 internal->SetInternalField(V8PromiseCustom::InternalRejectCallbackIndex, v8: :Array::New()); | |
525 } | |
526 | |
527 V8PromiseCustom::PromiseState V8PromiseCustom::getState(v8::Handle<v8::Object> i nternal) | |
528 { | |
529 ASSERT(!internal.IsEmpty()); | |
530 v8::Handle<v8::Value> value = internal->GetInternalField(V8PromiseCustom::In ternalStateIndex); | |
531 ASSERT(!value.IsEmpty()); | |
532 ASSERT(value->IsNumber()); | |
533 double number = value.As<v8::Number>()->Value(); | |
534 ASSERT(number == Pending || number == Fulfilled || number == Rejected); | |
535 return static_cast<PromiseState>(number); | |
536 } | |
537 | |
538 void V8PromiseCustom::setState(v8::Handle<v8::Object> internal, PromiseState sta te) | |
539 { | |
540 ASSERT(!internal.IsEmpty()); | |
541 ASSERT(state == Pending || state == Fulfilled || state == Rejected); | |
542 internal->SetInternalField(V8PromiseCustom::InternalStateIndex, v8::Number:: New(state)); | |
543 } | |
544 | |
545 void V8PromiseCustom::call(v8::Handle<v8::Function> function, v8::Handle<v8::Obj ect> receiver, v8::Handle<v8::Value> result, bool synchronous, v8::Isolate* isol ate) | |
546 { | |
547 ASSERT(!function.IsEmpty()); | |
548 ASSERT(!receiver.IsEmpty()); | |
549 if (synchronous) { | |
550 v8::TryCatch trycatch; | |
551 V8ScriptRunner::callFunction(function, getScriptExecutionContext(), rece iver, 1, &result); | |
552 } else { | |
553 postTask(function, receiver, result, isolate); | |
554 } | |
555 } | |
556 | |
77 } // namespace WebCore | 557 } // namespace WebCore |
OLD | NEW |