Chromium Code Reviews| 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 |