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(!m_callback.isEmpty()); | |
62 ASSERT(!m_receiver.isEmpty()); | |
63 ASSERT(!m_result.isEmpty()); | |
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 ASSERT(context && context->isDocument()); | |
78 if (context->activeDOMObjectsAreStopped()) | |
79 return; | |
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 v8::Context::Scope scope(v8Context); | |
85 v8::Isolate* isolate = v8Context->GetIsolate(); | |
86 v8::Handle<v8::Value> args[] = { m_result.newLocal(isolate) }; | |
87 V8ScriptRunner::callFunction(m_callback.newLocal(isolate), context, m_receiv er.newLocal(isolate), WTF_ARRAY_LENGTH(args), args); | |
88 }; | |
89 | |
90 v8::Handle<v8::Value> postTask(v8::Handle<v8::Function> callback, v8::Handle<v8: :Object> receiver, v8::Handle<v8::Value> value, v8::Isolate* isolate) | |
91 { | |
92 DOMWindow* window = activeDOMWindow(); | |
93 ASSERT(window); | |
94 Document* document = window->document(); | |
95 ASSERT(document); | |
96 document->postTask(adoptPtr(new PromiseTask(callback, receiver, value))); | |
97 return v8::Undefined(isolate); | |
98 } | |
99 | |
100 void callCallbacks(v8::Handle<v8::Array> callbacks, v8::Handle<v8::Value> result , V8PromiseCustom::SynchronousMode mode, v8::Isolate* isolate) | |
101 { | |
102 v8::Local<v8::Object> global = isolate->GetCurrentContext()->Global(); | |
103 for (uint32_t i = 0, length = callbacks->Length(); i < length; ++i) { | |
104 v8::Local<v8::Value> value = callbacks->Get(i); | |
105 v8::Local<v8::Function> callback = value.As<v8::Function>(); | |
106 V8PromiseCustom::call(callback, global, result, mode, isolate); | |
107 } | |
108 } | |
109 | |
110 } // namespace | |
111 | |
43 void V8Promise::constructorCustom(const v8::FunctionCallbackInfo<v8::Value>& arg s) | 112 void V8Promise::constructorCustom(const v8::FunctionCallbackInfo<v8::Value>& arg s) |
44 { | 113 { |
45 v8SetReturnValue(args, v8::Local<v8::Value>()); | 114 v8SetReturnValue(args, v8::Local<v8::Value>()); |
46 v8::Isolate* isolate = args.GetIsolate(); | 115 v8::Isolate* isolate = args.GetIsolate(); |
47 if (!args.Length() || args[0].IsEmpty() || !args[0]->IsFunction()) { | 116 if (!args.Length() || !args[0]->IsFunction()) { |
48 throwTypeError("Promise constructor takes a function argument", isolate) ; | 117 throwTypeError("Promise constructor takes a function argument", isolate) ; |
49 return; | 118 return; |
50 } | 119 } |
51 v8::Handle<v8::Function> init = args[0].As<v8::Function>(); | 120 v8::Local<v8::Function> init = args[0].As<v8::Function>(); |
52 v8::Local<v8::ObjectTemplate> internalTemplate = v8::ObjectTemplate::New(); | 121 v8::Local<v8::Object> promise, resolver; |
53 internalTemplate->SetInternalFieldCount(V8PromiseCustom::InternalFieldCount) ; | 122 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[] = { | 123 v8::Handle<v8::Value> argv[] = { |
67 promiseResolver, | 124 resolver, |
68 }; | 125 }; |
69 v8::TryCatch trycatch; | 126 v8::TryCatch trycatch; |
70 if (V8ScriptRunner::callFunction(init, getScriptExecutionContext(), promise, WTF_ARRAY_LENGTH(argv), argv).IsEmpty()) { | 127 if (V8ScriptRunner::callFunction(init, getScriptExecutionContext(), promise, WTF_ARRAY_LENGTH(argv), argv).IsEmpty()) { |
71 // FIXME: An exception is thrown. Reject the promise. | 128 // An exception is thrown. Reject the promise. |
129 V8PromiseCustom::rejectResolver(resolver, trycatch.Exception(), V8Promis eCustom::Asynchronous, isolate); | |
72 } | 130 } |
73 v8SetReturnValue(args, promise); | 131 v8SetReturnValue(args, promise); |
74 return; | 132 return; |
75 } | 133 } |
76 | 134 |
135 // | |
136 // -- V8PromiseCustom -- | |
137 void V8PromiseCustom::createPromise(v8::Handle<v8::Object> creationContext, v8:: Local<v8::Object>* promise, v8::Local<v8::Object>* resolver, v8::Isolate* isolat e) | |
138 { | |
139 // FIXME: v8::ObjectTemplate::New should be cached. | |
140 v8::Local<v8::ObjectTemplate> internalTemplate = v8::ObjectTemplate::New(); | |
141 internalTemplate->SetInternalFieldCount(InternalFieldCount); | |
142 v8::Local<v8::Object> internal = internalTemplate->NewInstance(); | |
143 *promise = V8DOMWrapper::createWrapper(creationContext, &V8Promise::info, 0, isolate); | |
144 *resolver = V8DOMWrapper::createWrapper(creationContext, &V8PromiseResolver: :info, 0, isolate); | |
145 | |
146 clearInternal(internal, V8PromiseCustom::Pending, v8::Undefined(isolate)); | |
147 | |
148 (*promise)->SetInternalField(v8DOMWrapperObjectIndex, internal); | |
149 (*resolver)->SetInternalField(v8DOMWrapperObjectIndex, internal); | |
150 } | |
151 | |
152 void V8PromiseCustom::fulfillResolver(v8::Handle<v8::Object> resolver, v8::Handl e<v8::Value> result, SynchronousMode mode, v8::Isolate* isolate) | |
153 { | |
154 if (isInternalDetached(resolver)) | |
155 return; | |
156 v8::Local<v8::Object> internal = getInternal(resolver); | |
157 if (getState(internal) != Pending) | |
158 return; | |
159 | |
160 v8::Local<v8::Array> callbacks = internal->GetInternalField(V8PromiseCustom: :InternalFulfillCallbackIndex).As<v8::Array>(); | |
161 clearInternal(internal, Fulfilled, result); | |
162 detachInternal(resolver, isolate); | |
163 | |
164 callCallbacks(callbacks, result, mode, isolate); | |
165 } | |
166 | |
167 void V8PromiseCustom::rejectResolver(v8::Handle<v8::Object> resolver, v8::Handle <v8::Value> result, SynchronousMode mode, v8::Isolate* isolate) | |
168 { | |
169 if (isInternalDetached(resolver)) | |
170 return; | |
171 v8::Local<v8::Object> internal = getInternal(resolver); | |
172 if (getState(internal) != Pending) | |
173 return; | |
174 | |
175 v8::Local<v8::Array> callbacks = internal->GetInternalField(V8PromiseCustom: :InternalRejectCallbackIndex).As<v8::Array>(); | |
176 clearInternal(internal, Rejected, result); | |
177 detachInternal(resolver, isolate); | |
178 | |
179 callCallbacks(callbacks, result, mode, isolate); | |
180 } | |
181 | |
182 void V8PromiseCustom::append(v8::Handle<v8::Object> promise, v8::Handle<v8::Func tion> fulfillCallback, v8::Handle<v8::Function> rejectCallback, v8::Isolate* iso late) | |
183 { | |
184 // fulfillCallback and rejectCallback can be empty. | |
185 v8::Local<v8::Object> internal = getInternal(promise).As<v8::Object>(); | |
186 | |
187 PromiseState state = getState(internal); | |
188 if (state == Fulfilled) { | |
189 if (!fulfillCallback.IsEmpty()) { | |
190 v8::Local<v8::Value> result = internal->GetInternalField(V8PromiseCu stom::InternalResultIndex); | |
191 v8::Local<v8::Object> global = isolate->GetCurrentContext()->Global( ); | |
192 call(fulfillCallback, global, result, Asynchronous, isolate); | |
193 } | |
194 return; | |
195 } | |
196 if (state == Rejected) { | |
197 if (!rejectCallback.IsEmpty()) { | |
198 v8::Local<v8::Value> result = internal->GetInternalField(V8PromiseCu stom::InternalResultIndex); | |
199 v8::Local<v8::Object> global = isolate->GetCurrentContext()->Global( ); | |
200 call(rejectCallback, global, result, Asynchronous, isolate); | |
201 } | |
202 return; | |
203 } | |
204 | |
205 ASSERT(state == Pending); | |
206 if (!fulfillCallback.IsEmpty()) { | |
207 v8::Local<v8::Array> callbacks = internal->GetInternalField(InternalFulf illCallbackIndex).As<v8::Array>(); | |
208 callbacks->Set(callbacks->Length(), fulfillCallback); | |
209 } | |
210 if (!rejectCallback.IsEmpty()) { | |
211 v8::Local<v8::Array> callbacks = internal->GetInternalField(InternalReje ctCallbackIndex).As<v8::Array>(); | |
212 callbacks->Set(callbacks->Length(), rejectCallback); | |
213 } | |
214 } | |
215 | |
216 v8::Local<v8::Object> V8PromiseCustom::getInternal(v8::Handle<v8::Object> promis eOrResolver) | |
217 { | |
218 v8::Local<v8::Value> value = promiseOrResolver->GetInternalField(v8DOMWrappe rObjectIndex); | |
219 // This function cannot be called when the internal object is detached, so t he value must be an object. | |
220 return value.As<v8::Object>(); | |
221 } | |
222 | |
223 bool V8PromiseCustom::isInternalDetached(v8::Handle<v8::Object> resolver) | |
224 { | |
225 v8::Local<v8::Value> value = resolver->GetInternalField(v8DOMWrapperObjectIn dex); | |
226 return value->IsUndefined(); | |
haraken
2013/06/27 10:36:29
You might want to add ASSERT(!value.IsEmpty()) jus
yhirano
2013/06/28 00:47:18
Done.
| |
227 } | |
228 | |
229 void V8PromiseCustom::detachInternal(v8::Handle<v8::Object> resolver, v8::Isolat e* isolate) | |
230 { | |
231 resolver->SetInternalField(v8DOMWrapperObjectIndex, v8::Undefined(isolate)); | |
232 } | |
233 | |
234 void V8PromiseCustom::clearInternal(v8::Handle<v8::Object> internal, PromiseStat e state, v8::Handle<v8::Value> value) | |
235 { | |
236 setState(internal, state); | |
237 internal->SetInternalField(V8PromiseCustom::InternalResultIndex, value); | |
238 internal->SetInternalField(V8PromiseCustom::InternalFulfillCallbackIndex, v8 ::Array::New()); | |
239 internal->SetInternalField(V8PromiseCustom::InternalRejectCallbackIndex, v8: :Array::New()); | |
240 } | |
241 | |
242 V8PromiseCustom::PromiseState V8PromiseCustom::getState(v8::Handle<v8::Object> i nternal) | |
243 { | |
244 v8::Handle<v8::Value> value = internal->GetInternalField(V8PromiseCustom::In ternalStateIndex); | |
245 bool ok = false; | |
246 uint32_t number = toInt32(value, ok); | |
247 ASSERT(ok && (number == Pending || number == Fulfilled || number == Rejected )); | |
248 return static_cast<PromiseState>(number); | |
249 } | |
250 | |
251 void V8PromiseCustom::setState(v8::Handle<v8::Object> internal, PromiseState sta te) | |
252 { | |
253 ASSERT(state == Pending || state == Fulfilled || state == Rejected); | |
254 internal->SetInternalField(V8PromiseCustom::InternalStateIndex, v8::Integer: :New(state)); | |
255 } | |
256 | |
257 void V8PromiseCustom::call(v8::Handle<v8::Function> function, v8::Handle<v8::Obj ect> receiver, v8::Handle<v8::Value> result, SynchronousMode mode, v8::Isolate* isolate) | |
258 { | |
259 if (mode == Synchronous) { | |
260 // If an exception is thrown, catch it and do nothing. | |
261 v8::TryCatch trycatch; | |
262 v8::Handle<v8::Value> args[] = { result }; | |
263 V8ScriptRunner::callFunction(function, getScriptExecutionContext(), rece iver, WTF_ARRAY_LENGTH(args), args); | |
haraken
2013/06/27 10:36:29
Are you sure that you've entered the context? If y
yhirano
2013/06/28 00:47:18
Done.
| |
264 } else { | |
265 ASSERT(mode == Asynchronous); | |
266 postTask(function, receiver, result, isolate); | |
267 } | |
268 } | |
269 | |
77 } // namespace WebCore | 270 } // namespace WebCore |
OLD | NEW |