Chromium Code Reviews| Index: Source/bindings/v8/custom/V8PromiseCustom.cpp |
| diff --git a/Source/bindings/v8/custom/V8PromiseCustom.cpp b/Source/bindings/v8/custom/V8PromiseCustom.cpp |
| index c9da71b9e89383867155271fdcf99798551f24dd..3a3d1b4c7e1a41620d237f1971c5eddec11005c2 100644 |
| --- a/Source/bindings/v8/custom/V8PromiseCustom.cpp |
| +++ b/Source/bindings/v8/custom/V8PromiseCustom.cpp |
| @@ -33,13 +33,252 @@ |
| #include "V8Promise.h" |
| #include "V8PromiseResolver.h" |
| +#include "bindings/v8/ScriptFunctionCall.h" |
| +#include "bindings/v8/ScriptValue.h" |
| #include "bindings/v8/V8Binding.h" |
| +#include "bindings/v8/V8PerIsolateData.h" |
| #include "bindings/v8/V8ScriptRunner.h" |
| #include "bindings/v8/WrapperTypeInfo.h" |
| +#include "core/dom/Document.h" |
| +#include "core/dom/ExceptionCode.h" |
| +#include "core/page/DOMWindow.h" |
| +#include "core/platform/Task.h" |
| +#include "wtf/Functional.h" |
| +#include "wtf/PassOwnPtr.h" |
| #include <v8.h> |
| namespace WebCore { |
| +namespace { |
| + |
| +int wrapperCallbackTag = 0; |
| +int promiseFulfillCallbackTag = 0; |
| +int promiseResolveCallbackTag = 0; |
| +int promiseRejectCallbackTag = 0; |
| + |
| +// tag must be a pointer of one of the above tags. |
| +v8::Local<v8::Function> getFunction(v8::FunctionCallback callback, int* tag, v8::Isolate* isolate) |
| +{ |
| + WrapperWorldType worldType = WebCore::worldType(isolate); |
| + V8PerIsolateData* data = V8PerIsolateData::from(isolate); |
| + ASSERT(data); |
| + V8PerIsolateData::TemplateMap::iterator result = data->templateMap(worldType).find(tag); |
| + if (result != data->templateMap(worldType).end()) |
| + return result->value.newLocal(isolate)->GetFunction(); |
| + |
| + v8::HandleScope handleScope(isolate); |
| + v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(callback); |
| + data->templateMap(worldType).add(tag, UnsafePersistent<v8::FunctionTemplate>(isolate, templ)); |
| + 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.
|
| +} |
| + |
| +class PromiseTask : public ScriptExecutionContext::Task { |
| +public: |
| + PromiseTask(v8::Handle<v8::Function> callback, v8::Handle<v8::Value> receiver, v8::Handle<v8::Value> result) |
| + : 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.
|
| + { |
| + ASSERT(!callback.IsEmpty()); |
| + ASSERT(!receiver.IsEmpty()); |
| + ASSERT(!result.IsEmpty()); |
| + } |
| + virtual ~PromiseTask() { } |
| + |
| + virtual void performTask(ScriptExecutionContext*) OVERRIDE; |
| + |
| +private: |
| + ScriptValue m_callback; |
| + ScriptValue m_receiver; |
| + 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.
|
| +}; |
| + |
| +void PromiseTask::performTask(ScriptExecutionContext* context) |
| +{ |
| + ASSERT(context->isDocument()); |
| + ScriptState* state = mainWorldScriptState(static_cast<Document*>(context)->frame()); |
|
abarth-chromium
2013/06/27 03:25:16
We should ask the Document whether ActiveDOMObject
yhirano
2013/06/27 05:40:34
Done.
|
| + v8::HandleScope handleScope; |
| + if (!state) { |
| + // We can't execute the task. Ignore it. |
| + return; |
| + } |
|
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.
|
| + v8::Handle<v8::Context> v8Context = state->context(); |
| + if (v8Context.IsEmpty()) { |
| + // We can't execute the task. Ignore it. |
| + return; |
| + } |
|
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.
|
| + v8::Context::Scope scope(v8Context); |
| + v8::Handle<v8::Value> result = m_result.v8Value(); |
| + ASSERT(!m_callback.v8Value().IsEmpty()); |
| + ASSERT(m_callback.v8Value()->IsFunction()); |
| + v8::Local<v8::Function> callback = m_callback.v8Value().As<v8::Function>(); |
| + ASSERT(!m_receiver.v8Value().IsEmpty()); |
| + ASSERT(m_receiver.v8Value()->IsObject()); |
| + 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.
|
| + ASSERT(!result.IsEmpty()); |
| + v8::Local<v8::Value> v = V8ScriptRunner::callFunction(callback, context, receiver, 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.
|
| +}; |
| + |
| +v8::Handle<v8::Value> postTask(v8::Handle<v8::Function> callback, v8::Handle<v8::Value> receiver, v8::Handle<v8::Value> value, v8::Isolate* isolate) |
| +{ |
| + DOMWindow* window = activeDOMWindow(); |
| + if (!window) |
| + 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.
|
| + Document* document = window->document(); |
| + if (!document) |
| + 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.
|
| + document->postTask(adoptPtr(new PromiseTask(callback, receiver, value))); |
| + return v8::Undefined(); |
|
abarth-chromium
2013/06/27 03:25:16
v8::Undefined(isolate) ?
yhirano
2013/06/27 05:40:34
Done.
|
| +} |
| + |
| +void wrapperCallbackRaw(const v8::FunctionCallbackInfo<v8::Value>& args) |
| +{ |
| + v8::Isolate* isolate = args.GetIsolate(); |
| + v8SetReturnValue(args, v8::Undefined()); |
|
abarth-chromium
2013/06/27 03:25:16
v8::Undefined(isolate) ?
yhirano
2013/06/27 05:40:34
Done.
|
| + v8::Local<v8::Object> environment; |
| + 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.
|
| + if (args.Length() > 0 && !args[0].IsEmpty() && args[0]->IsObject()) |
| + environment = args[0].As<v8::Object>(); |
| + if (args.Length() > 1 && !args[1].IsEmpty()) |
| + result = args[1]; |
| + 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.
|
| + |
| + v8::Local<v8::Value> value; |
| + |
| + value = environment->GetInternalField(V8PromiseCustom::WrapperCallbackEnvironmentPromiseIndex); |
| + ASSERT(!value.IsEmpty()); |
| + ASSERT(value->IsObject()); |
| + v8::Local<v8::Object> promise = value.As<v8::Object>(); |
| + |
| + value = environment->GetInternalField(V8PromiseCustom::WrapperCallbackEnvironmentPromiseResolverIndex); |
| + ASSERT(!value.IsEmpty()); |
| + ASSERT(value->IsObject()); |
| + v8::Local<v8::Object> resolver = value.As<v8::Object>(); |
| + |
| + value = environment->GetInternalField(V8PromiseCustom::WrapperCallbackEnvironmentCallbackIndex); |
| + ASSERT(!value.IsEmpty()); |
| + ASSERT(value->IsFunction()); |
| + v8::Local<v8::Function> callback = value.As<v8::Function>(); |
| + |
| + v8::TryCatch trycatch; |
| + result = V8ScriptRunner::callFunction(callback, getScriptExecutionContext(), promise, 1, &result); |
| + if (result.IsEmpty()) { |
| + V8PromiseCustom::rejectResolver(resolver, trycatch.Exception(), true, isolate); |
| + return; |
| + } |
| + V8PromiseCustom::resolveResolver(resolver, result, true, isolate); |
| +} |
| + |
| +v8::Handle<v8::Function> wrapperCallback(v8::Handle<v8::Object> promise, v8::Handle<v8::Object> resolver, v8::Handle<v8::Function> callback, v8::Isolate* isolate) |
| +{ |
| + ASSERT(!promise.IsEmpty()); |
| + ASSERT(!resolver.IsEmpty()); |
| + ASSERT(!callback.IsEmpty()); |
| + // FIXME: v8::ObjectTemplate::New should be cached. |
| + 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.
|
| + tmpl->SetInternalFieldCount(V8PromiseCustom::WrapperCallbackEnvironmentFieldCount); |
| + 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.
|
| + env->SetInternalField(V8PromiseCustom::WrapperCallbackEnvironmentPromiseIndex, promise); |
| + env->SetInternalField(V8PromiseCustom::WrapperCallbackEnvironmentPromiseResolverIndex, resolver); |
| + env->SetInternalField(V8PromiseCustom::WrapperCallbackEnvironmentCallbackIndex, callback); |
| + |
| + // FIXME: If there is a way to bind an object to a function other than evaluate a JavaScript, it will be preferable. |
| + // FIXME: script compilation should be cached. |
| + // We should not depend on the global context that user can change, such as accessing a property, calling a method or so. |
| + v8::Local<v8::String> script = v8::String::New("(function(f, v1) { return function(v2) { return f(v1, v2); }; })"); |
| + v8::Local<v8::Value> value = V8ScriptRunner::compileAndRunInternalScript(script, isolate); |
|
abarth-chromium
2013/06/27 03:25:16
There's got to be a better way to do this.
|
| + ASSERT(!value.IsEmpty()); |
| + ASSERT(value->IsFunction()); |
| + |
| + v8::Local<v8::Value> argv[] = { |
| + getFunction(wrapperCallbackRaw, &wrapperCallbackTag, isolate), |
| + env, |
| + }; |
| + 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
|
| + |
| + value = V8ScriptRunner::callFunction(value.As<v8::Function>(), getScriptExecutionContext(), global, WTF_ARRAY_LENGTH(argv), argv); |
| + ASSERT(!value.IsEmpty()); |
| + ASSERT(value->IsFunction()); |
| + return value.As<v8::Function>(); |
| +} |
| + |
| +void promiseFulfillCallback(const v8::FunctionCallbackInfo<v8::Value>& args) |
| +{ |
| + v8::Local<v8::Object> resolver; |
| + v8::Local<v8::Value> result = v8::Undefined(); |
| + if (args.Length() > 0 && !args[0].IsEmpty() && args[0]->IsObject()) |
| + resolver = args[0].As<v8::Object>(); |
| + if (args.Length() > 1 && !args[1].IsEmpty()) |
| + result = args[1]; |
| + |
| + 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.
|
| + V8PromiseCustom::fulfillResolver(resolver, result, true, args.GetIsolate()); |
| +} |
| + |
| +void promiseResolveCallback(const v8::FunctionCallbackInfo<v8::Value>& args) |
| +{ |
| + v8::Local<v8::Object> resolver; |
| + v8::Local<v8::Value> result = v8::Undefined(); |
| + if (args.Length() > 0 && !args[0].IsEmpty() && args[0]->IsObject()) |
| + resolver = args[0].As<v8::Object>(); |
| + if (args.Length() > 1 && !args[1].IsEmpty()) |
| + result = args[1]; |
| + |
| + ASSERT(!resolver.IsEmpty()); |
| + V8PromiseCustom::resolveResolver(resolver, result, true, args.GetIsolate()); |
| +} |
| + |
| +void promiseRejectCallback(const v8::FunctionCallbackInfo<v8::Value>& args) |
| +{ |
| + v8::Local<v8::Object> resolver; |
| + v8::Local<v8::Value> result = v8::Undefined(); |
| + if (args.Length() > 0 && !args[0].IsEmpty() && args[0]->IsObject()) |
| + resolver = args[0].As<v8::Object>(); |
| + if (args.Length() > 1 && !args[1].IsEmpty()) |
| + result = args[1]; |
| + |
| + ASSERT(!resolver.IsEmpty()); |
| + V8PromiseCustom::rejectResolver(resolver, result, true, args.GetIsolate()); |
| +} |
| + |
| +v8::Local<v8::Function> promiseCallback(v8::Handle<v8::Object> resolver, V8PromiseCustom::PromiseAlgorithm algorithm, v8::Isolate* isolate) |
| +{ |
| + ASSERT(!resolver.IsEmpty()); |
| + v8::Local<v8::Function> callback; |
| + switch (algorithm) { |
| + case V8PromiseCustom::FulfillAlgorithm: |
| + callback = getFunction(promiseFulfillCallback, &promiseFulfillCallbackTag, isolate); |
| + break; |
| + case V8PromiseCustom::ResolveAlgorithm: |
| + callback = getFunction(promiseResolveCallback, &promiseResolveCallbackTag, isolate); |
| + break; |
| + case V8PromiseCustom::RejectAlgorithm: |
| + callback = getFunction(promiseRejectCallback, &promiseRejectCallbackTag, isolate); |
| + break; |
| + default: |
| + ASSERT(0); |
| + } |
| + |
| + // FIXME: If there is a way to bind an object to a function other than evaluate a JavaScript, it will be preferable. |
| + // FIXME: script compilation should be cached. |
| + // We should not depend on the global context that user can change, such as accessing a property, calling a method or so. |
| + v8::Local<v8::String> script = v8::String::New("(function(f, v1) { return function(v2) { return f(v1, v2); }; })"); |
| + v8::Local<v8::Value> value = V8ScriptRunner::compileAndRunInternalScript(script, isolate); |
| + ASSERT(!value.IsEmpty()); |
| + ASSERT(value->IsFunction()); |
| + |
| + v8::Local<v8::Value> argv[] = { |
| + callback, |
| + resolver, |
| + }; |
| + v8::Local<v8::Object> receiver = isolate->GetCurrentContext()->Global(); |
| + |
| + value = V8ScriptRunner::callFunction(value.As<v8::Function>(), getScriptExecutionContext(), receiver, WTF_ARRAY_LENGTH(argv), argv); |
| + ASSERT(!value.IsEmpty()); |
| + ASSERT(value->IsFunction()); |
| + return value.As<v8::Function>(); |
| +} |
| + |
| +} // namespace |
| + |
| void V8Promise::constructorCustom(const v8::FunctionCallbackInfo<v8::Value>& args) |
| { |
| v8SetReturnValue(args, v8::Local<v8::Value>()); |
| @@ -48,30 +287,271 @@ void V8Promise::constructorCustom(const v8::FunctionCallbackInfo<v8::Value>& arg |
| throwTypeError("Promise constructor takes a function argument", isolate); |
| return; |
| } |
| - v8::Handle<v8::Function> init = args[0].As<v8::Function>(); |
| + v8::Local<v8::Function> init = args[0].As<v8::Function>(); |
| + v8::Local<v8::Object> promise, resolver; |
| + V8PromiseCustom::createPromise(args.Holder(), &promise, &resolver, isolate); |
| + v8::Handle<v8::Value> argv[] = { |
| + resolver, |
| + }; |
| + v8::TryCatch trycatch; |
| + if (V8ScriptRunner::callFunction(init, getScriptExecutionContext(), promise, WTF_ARRAY_LENGTH(argv), argv).IsEmpty()) { |
| + // An exception is thrown. Reject the promise. |
| + V8PromiseCustom::rejectResolver(resolver, trycatch.Exception(), false, isolate); |
| + } |
| + v8SetReturnValue(args, promise); |
| + return; |
| +} |
| + |
| +void V8Promise::thenMethodCustom(const v8::FunctionCallbackInfo<v8::Value>& args) |
| +{ |
| + v8::Isolate* isolate = args.GetIsolate(); |
| + v8SetReturnValue(args, v8::Local<v8::Value>()); |
| + v8::Local<v8::Function> fulfillWrapper, rejectWrapper; |
| + v8::Local<v8::Object> promise, resolver; |
| + V8PromiseCustom::createPromise(args.Holder(), &promise, &resolver, isolate); |
| + if (args.Length() > 0 && !args[0].IsEmpty() && !args[0]->IsUndefined()) { |
| + if (!args[0]->IsFunction()) { |
| + throwTypeError("fulfillCallback must be a function or undefined", isolate); |
| + return; |
| + } |
| + fulfillWrapper = wrapperCallback(promise, resolver, args[0].As<v8::Function>(), isolate); |
| + } else { |
| + fulfillWrapper = promiseCallback(resolver, V8PromiseCustom::FulfillAlgorithm, isolate); |
| + } |
| + if (args.Length() > 1 && !args[1].IsEmpty() && !args[1]->IsUndefined()) { |
| + if (!args[1]->IsFunction()) { |
| + throwTypeError("rejectCallback must be a function or undefined", isolate); |
| + return; |
| + } |
| + rejectWrapper = wrapperCallback(promise, resolver, args[1].As<v8::Function>(), isolate); |
| + } else { |
| + rejectWrapper = promiseCallback(resolver, V8PromiseCustom::RejectAlgorithm, isolate); |
| + } |
| + V8PromiseCustom::append(args.Holder(), fulfillWrapper, rejectWrapper, isolate); |
| + v8SetReturnValue(args, promise); |
| +} |
| + |
| +void V8Promise::catchMethodCustom(const v8::FunctionCallbackInfo<v8::Value>& args) |
| +{ |
| + v8::Isolate* isolate = args.GetIsolate(); |
| + v8SetReturnValue(args, v8::Local<v8::Value>()); |
| + v8::Local<v8::Function> fulfillWrapper, rejectWrapper; |
| + v8::Local<v8::Object> promise, resolver; |
| + V8PromiseCustom::createPromise(args.Holder(), &promise, &resolver, isolate); |
| + |
| + if (args.Length() > 0 && !args[0].IsEmpty() && !args[0]->IsUndefined()) { |
| + if (!args[0]->IsFunction()) { |
| + throwTypeError("rejectCallback must be a function or undefined", isolate); |
| + return; |
| + } |
| + rejectWrapper = wrapperCallback(promise, resolver, args[0].As<v8::Function>(), isolate); |
| + } else { |
| + rejectWrapper = promiseCallback(resolver, V8PromiseCustom::RejectAlgorithm, isolate); |
| + } |
| + fulfillWrapper = promiseCallback(resolver, V8PromiseCustom::FulfillAlgorithm, isolate); |
| + V8PromiseCustom::append(args.Holder(), fulfillWrapper, rejectWrapper, isolate); |
| + v8SetReturnValue(args, promise); |
| +} |
| + |
| +// |
| +// -- V8PromiseCustom -- |
| +void V8PromiseCustom::createPromise(v8::Handle<v8::Object> creationContext, v8::Local<v8::Object>* promise, v8::Local<v8::Object>* resolver, v8::Isolate* isolate) |
| +{ |
| + // FIXME: v8::ObjectTemplate::New should be cached. |
| v8::Local<v8::ObjectTemplate> internalTemplate = v8::ObjectTemplate::New(); |
| - internalTemplate->SetInternalFieldCount(V8PromiseCustom::InternalFieldCount); |
| + internalTemplate->SetInternalFieldCount(InternalFieldCount); |
| v8::Local<v8::Object> internal = internalTemplate->NewInstance(); |
| - v8::Local<v8::Object> promise = V8DOMWrapper::createWrapper(args.Holder(), &V8Promise::info, 0, isolate); |
| - v8::Local<v8::Object> promiseResolver = V8DOMWrapper::createWrapper(args.Holder(), &V8PromiseResolver::info, 0, isolate); |
| + *promise = V8DOMWrapper::createWrapper(creationContext, &V8Promise::info, 0, isolate); |
| + *resolver = V8DOMWrapper::createWrapper(creationContext, &V8PromiseResolver::info, 0, isolate); |
| + |
| + clearInternal(internal, V8PromiseCustom::Pending, v8::Undefined()); |
| + |
| + (*promise)->SetInternalField(v8DOMWrapperObjectIndex, internal); |
| + (*resolver)->SetInternalField(v8DOMWrapperObjectIndex, internal); |
| +} |
| + |
| +void V8PromiseCustom::fulfillResolver(v8::Handle<v8::Object> resolver, v8::Handle<v8::Value> result, bool synchronous, v8::Isolate* isolate) |
| +{ |
| + ASSERT(!resolver.IsEmpty()); |
| + ASSERT(!result.IsEmpty()); |
| + v8::Local<v8::Object> internal = getInternal(resolver); |
| + if (internal.IsEmpty() || getState(internal) != Pending) { |
| + // The resolver is already fulfilled or rejected. |
| + return; |
| + } |
| + |
| + v8::Local<v8::Value> value; |
| + value = internal->GetInternalField(V8PromiseCustom::InternalFulfillCallbackIndex); |
| + ASSERT(!value.IsEmpty()); |
| + ASSERT(value->IsArray()); |
| + v8::Local<v8::Array> callbacks = value.As<v8::Array>(); |
| + clearInternal(internal, Fulfilled, result); |
| + // The internal object is no more needed in this PromiseResolver. |
| + resolver->SetInternalField(v8DOMWrapperObjectIndex, v8::Null()); |
| + |
| + v8::Local<v8::Object> global = isolate->GetCurrentContext()->Global(); |
| + for (uint32_t i = 0, length = callbacks->Length(); i < length; ++i) { |
| + value = callbacks->Get(i); |
| + ASSERT(!value.IsEmpty()); |
| + ASSERT(value->IsFunction()); |
| + v8::Local<v8::Function> callback = value.As<v8::Function>(); |
| + call(callback, global, result, synchronous, isolate); |
| + } |
| +} |
| + |
| +void V8PromiseCustom::resolveResolver(v8::Handle<v8::Object> resolver, v8::Handle<v8::Value> result, bool synchronous, v8::Isolate* isolate) |
| +{ |
| + ASSERT(!resolver.IsEmpty()); |
| + ASSERT(!result.IsEmpty()); |
| + v8::Local<v8::Object> internal = getInternal(resolver); |
| + if (internal.IsEmpty() || getState(internal) != Pending) { |
| + // The resolver is already fulfilled or rejected. |
| + return; |
| + } |
| + |
| + v8::Local<v8::Value> then; |
| + if (result->IsObject()) { |
| + v8::TryCatch trycatch; |
| + then = result.As<v8::Object>()->Get(v8::String::New("then")); |
| + if (then.IsEmpty()) { |
| + // If calling the [[Get]] internal method threw an exception, catch it and run reject. |
| + rejectResolver(resolver, trycatch.Exception(), synchronous, isolate); |
| + return; |
| + } |
| + } |
| - internal->SetInternalField(V8PromiseCustom::InternalStateIndex, v8::NumberObject::New(V8PromiseCustom::Pending)); |
| - internal->SetInternalField(V8PromiseCustom::InternalResultIndex, v8::Undefined()); |
| + if (!then.IsEmpty() && then->IsFunction()) { |
| + ASSERT(result->IsObject()); |
| + v8::TryCatch trycatch; |
| + v8::Handle<v8::Value> argv[] = { |
| + promiseCallback(resolver, ResolveAlgorithm, isolate), |
| + promiseCallback(resolver, RejectAlgorithm, isolate), |
| + }; |
| + if (V8ScriptRunner::callFunction(then.As<v8::Function>(), getScriptExecutionContext(), result.As<v8::Object>(), WTF_ARRAY_LENGTH(argv), argv).IsEmpty()) |
| + rejectResolver(resolver, trycatch.Exception(), synchronous, isolate); |
| + return; |
| + } |
| + fulfillResolver(resolver, result, synchronous, isolate); |
| +} |
| + |
| +void V8PromiseCustom::rejectResolver(v8::Handle<v8::Object> resolver, v8::Handle<v8::Value> result, bool synchronous, v8::Isolate* isolate) |
| +{ |
| + ASSERT(!resolver.IsEmpty()); |
| + ASSERT(!result.IsEmpty()); |
| + v8::Local<v8::Object> internal = getInternal(resolver); |
| + if (internal.IsEmpty() || getState(internal) != Pending) { |
| + // The resolver is already fulfilled or rejected. |
| + return; |
| + } |
| + |
| + v8::Local<v8::Value> value; |
| + value = internal->GetInternalField(V8PromiseCustom::InternalRejectCallbackIndex); |
| + ASSERT(!value.IsEmpty()); |
| + ASSERT(value->IsArray()); |
| + v8::Local<v8::Array> callbacks = value.As<v8::Array>(); |
| + clearInternal(internal, Rejected, result); |
| + // The internal object is no more needed from this PromiseResolver. |
| + resolver->SetInternalField(v8DOMWrapperObjectIndex, v8::Null()); |
| + |
| + v8::Local<v8::Object> global = isolate->GetCurrentContext()->Global(); |
| + for (uint32_t i = 0, length = callbacks->Length(); i < length; ++i) { |
| + value = callbacks->Get(i); |
| + ASSERT(!value.IsEmpty()); |
| + ASSERT(value->IsFunction()); |
| + v8::Local<v8::Function> callback = value.As<v8::Function>(); |
| + call(callback, global, result, synchronous, isolate); |
| + } |
| +} |
| + |
| +void V8PromiseCustom::append(v8::Handle<v8::Object> promise, v8::Handle<v8::Function> fulfillCallback, v8::Handle<v8::Function> rejectCallback, v8::Isolate* isolate) |
| +{ |
| + v8::Local<v8::Object> internal = getInternal(promise); |
| + ASSERT(!internal.IsEmpty()); |
| + |
| + PromiseState state = getState(internal); |
| + if (state == Fulfilled) { |
| + if (!fulfillCallback.IsEmpty()) { |
| + v8::Local<v8::Value> result = internal->GetInternalField(V8PromiseCustom::InternalResultIndex); |
| + v8::Local<v8::Object> global = isolate->GetCurrentContext()->Global(); |
| + call(fulfillCallback, global, result, false, isolate); |
| + } |
| + return; |
| + } |
| + if (state == Rejected) { |
| + if (!rejectCallback.IsEmpty()) { |
| + v8::Local<v8::Value> result = internal->GetInternalField(V8PromiseCustom::InternalResultIndex); |
| + v8::Local<v8::Object> global = isolate->GetCurrentContext()->Global(); |
| + call(rejectCallback, global, result, false, isolate); |
| + } |
| + return; |
| + } |
| + |
| + ASSERT(state == Pending); |
| + if (!fulfillCallback.IsEmpty()) { |
| + v8::Local<v8::Value> value = internal->GetInternalField(InternalFulfillCallbackIndex); |
| + ASSERT(!value.IsEmpty()); |
| + ASSERT(value->IsArray()); |
| + v8::Local<v8::Array> callbacks = value.As<v8::Array>(); |
| + callbacks->Set(callbacks->Length(), fulfillCallback); |
| + } |
| + if (!rejectCallback.IsEmpty()) { |
| + v8::Local<v8::Value> value = internal->GetInternalField(InternalRejectCallbackIndex); |
| + ASSERT(!value.IsEmpty()); |
| + ASSERT(value->IsArray()); |
| + v8::Local<v8::Array> callbacks = value.As<v8::Array>(); |
| + callbacks->Set(callbacks->Length(), rejectCallback); |
| + } |
| +} |
| + |
| +v8::Local<v8::Object> V8PromiseCustom::getInternal(v8::Handle<v8::Object> promiseOrPromiseResolver) |
| +{ |
| + ASSERT(!promiseOrPromiseResolver.IsEmpty()); |
| + v8::Local<v8::Value> value = promiseOrPromiseResolver->GetInternalField(v8DOMWrapperObjectIndex); |
| + ASSERT(!value.IsEmpty()); |
| + if (value->IsNull()) |
| + return v8::Local<v8::Object>(); |
| + ASSERT(value->IsObject()); |
| + return value.As<v8::Object>(); |
| +} |
| + |
| +void V8PromiseCustom::clearInternal(v8::Handle<v8::Object> internal, PromiseState state, v8::Handle<v8::Value> value) |
| +{ |
| + ASSERT(!internal.IsEmpty()); |
| + |
| + setState(internal, state); |
| + internal->SetInternalField(V8PromiseCustom::InternalResultIndex, value); |
| internal->SetInternalField(V8PromiseCustom::InternalFulfillCallbackIndex, v8::Array::New()); |
| internal->SetInternalField(V8PromiseCustom::InternalRejectCallbackIndex, v8::Array::New()); |
| +} |
| - promise->SetInternalField(v8DOMWrapperObjectIndex, internal); |
| - promiseResolver->SetInternalField(v8DOMWrapperObjectIndex, internal); |
| +V8PromiseCustom::PromiseState V8PromiseCustom::getState(v8::Handle<v8::Object> internal) |
| +{ |
| + ASSERT(!internal.IsEmpty()); |
| + v8::Handle<v8::Value> value = internal->GetInternalField(V8PromiseCustom::InternalStateIndex); |
| + ASSERT(!value.IsEmpty()); |
| + ASSERT(value->IsNumber()); |
| + double number = value.As<v8::Number>()->Value(); |
| + ASSERT(number == Pending || number == Fulfilled || number == Rejected); |
| + return static_cast<PromiseState>(number); |
| +} |
| - v8::Handle<v8::Value> argv[] = { |
| - promiseResolver, |
| - }; |
| - v8::TryCatch trycatch; |
| - if (V8ScriptRunner::callFunction(init, getScriptExecutionContext(), promise, WTF_ARRAY_LENGTH(argv), argv).IsEmpty()) { |
| - // FIXME: An exception is thrown. Reject the promise. |
| +void V8PromiseCustom::setState(v8::Handle<v8::Object> internal, PromiseState state) |
| +{ |
| + ASSERT(!internal.IsEmpty()); |
| + ASSERT(state == Pending || state == Fulfilled || state == Rejected); |
| + internal->SetInternalField(V8PromiseCustom::InternalStateIndex, v8::Number::New(state)); |
| +} |
| + |
| +void V8PromiseCustom::call(v8::Handle<v8::Function> function, v8::Handle<v8::Object> receiver, v8::Handle<v8::Value> result, bool synchronous, v8::Isolate* isolate) |
| +{ |
| + ASSERT(!function.IsEmpty()); |
| + ASSERT(!receiver.IsEmpty()); |
| + if (synchronous) { |
| + v8::TryCatch trycatch; |
| + V8ScriptRunner::callFunction(function, getScriptExecutionContext(), receiver, 1, &result); |
| + } else { |
| + postTask(function, receiver, result, isolate); |
| } |
| - v8SetReturnValue(args, promise); |
| - return; |
| } |
| } // namespace WebCore |