| 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..9f4f0eef4794b643897035ce1717c5480b4b3e3b 100644
|
| --- a/Source/bindings/v8/custom/V8PromiseCustom.cpp
|
| +++ b/Source/bindings/v8/custom/V8PromiseCustom.cpp
|
| @@ -33,13 +33,249 @@
|
|
|
| #include "V8Promise.h"
|
| #include "V8PromiseResolver.h"
|
| +#include "bindings/v8/ScopedPersistent.h"
|
| +#include "bindings/v8/ScriptFunctionCall.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> functionTemplate = v8::FunctionTemplate::New(callback);
|
| + data->templateMap(worldType).add(tag, UnsafePersistent<v8::FunctionTemplate>(isolate, functionTemplate));
|
| + return handleScope.Close(functionTemplate->GetFunction());
|
| +}
|
| +
|
| +class PromiseTask : public ScriptExecutionContext::Task {
|
| +public:
|
| + PromiseTask(v8::Handle<v8::Function> callback, v8::Handle<v8::Object> receiver, v8::Handle<v8::Value> result)
|
| + : m_callback(callback)
|
| + , m_receiver(receiver)
|
| + , m_result(result)
|
| + {
|
| + ASSERT(!callback.IsEmpty());
|
| + ASSERT(!receiver.IsEmpty());
|
| + ASSERT(!result.IsEmpty());
|
| + }
|
| + virtual ~PromiseTask() { }
|
| +
|
| + virtual void performTask(ScriptExecutionContext*) OVERRIDE;
|
| +
|
| +private:
|
| + ScopedPersistent<v8::Function> m_callback;
|
| + ScopedPersistent<v8::Object> m_receiver;
|
| + ScopedPersistent<v8::Value> m_result;
|
| +};
|
| +
|
| +void PromiseTask::performTask(ScriptExecutionContext* context)
|
| +{
|
| + if (context->activeDOMObjectsAreStopped())
|
| + return;
|
| + ASSERT(context->isDocument());
|
| + ScriptState* state = mainWorldScriptState(static_cast<Document*>(context)->frame());
|
| + v8::HandleScope handleScope;
|
| + ASSERT(state);
|
| + v8::Handle<v8::Context> v8Context = state->context();
|
| + ASSERT(!v8Context.IsEmpty());
|
| + v8::Context::Scope scope(v8Context);
|
| + v8::Isolate* isolate = v8Context->GetIsolate();
|
| + ASSERT(!m_callback.newLocal(isolate).IsEmpty());
|
| + ASSERT(!m_receiver.newLocal(isolate).IsEmpty());
|
| + ASSERT(!m_result.newLocal(isolate).IsEmpty());
|
| + v8::Handle<v8::Value> result = m_result.newLocal(isolate);
|
| + V8ScriptRunner::callFunction(m_callback.newLocal(isolate), context, m_receiver.newLocal(isolate), 1, &result);
|
| +};
|
| +
|
| +v8::Handle<v8::Value> postTask(v8::Handle<v8::Function> callback, v8::Handle<v8::Object> receiver, v8::Handle<v8::Value> value, v8::Isolate* isolate)
|
| +{
|
| + DOMWindow* window = activeDOMWindow();
|
| + ASSERT(window);
|
| + Document* document = window->document();
|
| + ASSERT(document);
|
| + document->postTask(adoptPtr(new PromiseTask(callback, receiver, value)));
|
| + return v8::Undefined(isolate);
|
| +}
|
| +
|
| +void wrapperCallbackRaw(const v8::FunctionCallbackInfo<v8::Value>& args)
|
| +{
|
| + v8::Isolate* isolate = args.GetIsolate();
|
| + v8SetReturnValue(args, v8::Undefined(args.GetIsolate()));
|
| + v8::Local<v8::Object> environment;
|
| + v8::Local<v8::Value> result = v8::Undefined(args.GetIsolate());
|
| + ASSERT(args.Length() > 0);
|
| + ASSERT(!args[0].IsEmpty());
|
| + ASSERT(args[0]->IsObject());
|
| + environment = args[0].As<v8::Object>();
|
| + if (args.Length() > 1 && !args[1].IsEmpty())
|
| + result = args[1];
|
| +
|
| + 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(), V8PromiseCustom::Synchronous, isolate);
|
| + return;
|
| + }
|
| + V8PromiseCustom::resolveResolver(resolver, result, V8PromiseCustom::Synchronous, 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> objectTemplate = v8::ObjectTemplate::New();
|
| + objectTemplate->SetInternalFieldCount(V8PromiseCustom::WrapperCallbackEnvironmentFieldCount);
|
| + v8::Local<v8::Object> environment = objectTemplate->NewInstance();
|
| + environment->SetInternalField(V8PromiseCustom::WrapperCallbackEnvironmentPromiseIndex, promise);
|
| + environment->SetInternalField(V8PromiseCustom::WrapperCallbackEnvironmentPromiseResolverIndex, resolver);
|
| + environment->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);
|
| + ASSERT(!value.IsEmpty());
|
| + ASSERT(value->IsFunction());
|
| +
|
| + v8::Local<v8::Value> argv[] = {
|
| + getFunction(wrapperCallbackRaw, &wrapperCallbackTag, isolate),
|
| + environment,
|
| + };
|
| + v8::Local<v8::Object> global = isolate->GetCurrentContext()->Global();
|
| +
|
| + 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(args.GetIsolate());
|
| + ASSERT(args.Length() > 0);
|
| + ASSERT(!args[0].IsEmpty());
|
| + ASSERT(args[0]->IsObject());
|
| + resolver = args[0].As<v8::Object>();
|
| + if (args.Length() > 1 && !args[1].IsEmpty())
|
| + result = args[1];
|
| +
|
| + V8PromiseCustom::fulfillResolver(resolver, result, V8PromiseCustom::Synchronous, args.GetIsolate());
|
| +}
|
| +
|
| +void promiseResolveCallback(const v8::FunctionCallbackInfo<v8::Value>& args)
|
| +{
|
| + v8::Local<v8::Object> resolver;
|
| + v8::Local<v8::Value> result = v8::Undefined(args.GetIsolate());
|
| + ASSERT(args.Length() > 0);
|
| + ASSERT(!args[0].IsEmpty());
|
| + ASSERT(args[0]->IsObject());
|
| + resolver = args[0].As<v8::Object>();
|
| + if (args.Length() > 1 && !args[1].IsEmpty())
|
| + result = args[1];
|
| +
|
| + V8PromiseCustom::resolveResolver(resolver, result, V8PromiseCustom::Synchronous, args.GetIsolate());
|
| +}
|
| +
|
| +void promiseRejectCallback(const v8::FunctionCallbackInfo<v8::Value>& args)
|
| +{
|
| + v8::Local<v8::Object> resolver;
|
| + v8::Local<v8::Value> result = v8::Undefined(args.GetIsolate());
|
| + ASSERT(args.Length() > 0);
|
| + ASSERT(!args[0].IsEmpty());
|
| + ASSERT(args[0]->IsObject());
|
| + resolver = args[0].As<v8::Object>();
|
| + if (args.Length() > 1 && !args[1].IsEmpty())
|
| + result = args[1];
|
| +
|
| + V8PromiseCustom::rejectResolver(resolver, result, V8PromiseCustom::Synchronous, 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 +284,272 @@ 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(), V8PromiseCustom::Asynchronous, 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(isolate));
|
| +
|
| + (*promise)->SetInternalField(v8DOMWrapperObjectIndex, internal);
|
| + (*resolver)->SetInternalField(v8DOMWrapperObjectIndex, internal);
|
| +}
|
| +
|
| +void V8PromiseCustom::fulfillResolver(v8::Handle<v8::Object> resolver, v8::Handle<v8::Value> result, SynchronousMode mode, 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(isolate));
|
| +
|
| + 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, mode, isolate);
|
| + }
|
| +}
|
| +
|
| +void V8PromiseCustom::resolveResolver(v8::Handle<v8::Object> resolver, v8::Handle<v8::Value> result, SynchronousMode mode, 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(), mode, isolate);
|
| + return;
|
| + }
|
| + }
|
| +
|
| + 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(), mode, isolate);
|
| + return;
|
| + }
|
| + fulfillResolver(resolver, result, mode, isolate);
|
| +}
|
| +
|
| +void V8PromiseCustom::rejectResolver(v8::Handle<v8::Object> resolver, v8::Handle<v8::Value> result, SynchronousMode mode, 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(isolate));
|
| +
|
| + 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, mode, 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, Asynchronous, 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, Asynchronous, isolate);
|
| + }
|
| + return;
|
| + }
|
|
|
| - internal->SetInternalField(V8PromiseCustom::InternalStateIndex, v8::NumberObject::New(V8PromiseCustom::Pending));
|
| - internal->SetInternalField(V8PromiseCustom::InternalResultIndex, v8::Undefined());
|
| + 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, SynchronousMode mode, v8::Isolate* isolate)
|
| +{
|
| + ASSERT(!function.IsEmpty());
|
| + ASSERT(!receiver.IsEmpty());
|
| + if (mode == Synchronous) {
|
| + v8::TryCatch trycatch;
|
| + V8ScriptRunner::callFunction(function, getScriptExecutionContext(), receiver, 1, &result);
|
| + } else {
|
| + ASSERT(mode == Asynchronous);
|
| + postTask(function, receiver, result, isolate);
|
| }
|
| - v8SetReturnValue(args, promise);
|
| - return;
|
| }
|
|
|
| } // namespace WebCore
|
|
|