| 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..9075189cbdf95e6e060a40bd33c34bcd7fa5ef12 100644
|
| --- a/Source/bindings/v8/custom/V8PromiseCustom.cpp
|
| +++ b/Source/bindings/v8/custom/V8PromiseCustom.cpp
|
| @@ -33,45 +33,240 @@
|
|
|
| #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 {
|
| +
|
| +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(!m_callback.isEmpty());
|
| + ASSERT(!m_receiver.isEmpty());
|
| + ASSERT(!m_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)
|
| +{
|
| + ASSERT(context && context->isDocument());
|
| + if (context->activeDOMObjectsAreStopped())
|
| + return;
|
| + ScriptState* state = mainWorldScriptState(static_cast<Document*>(context)->frame());
|
| + v8::HandleScope handleScope;
|
| + ASSERT(state);
|
| + v8::Handle<v8::Context> v8Context = state->context();
|
| + v8::Context::Scope scope(v8Context);
|
| + v8::Isolate* isolate = v8Context->GetIsolate();
|
| + v8::Handle<v8::Value> args[] = { m_result.newLocal(isolate) };
|
| + V8ScriptRunner::callFunction(m_callback.newLocal(isolate), context, m_receiver.newLocal(isolate), WTF_ARRAY_LENGTH(args), args);
|
| +};
|
| +
|
| +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 callCallbacks(v8::Handle<v8::Array> callbacks, v8::Handle<v8::Value> result, V8PromiseCustom::SynchronousMode mode, v8::Isolate* isolate)
|
| +{
|
| + v8::Local<v8::Object> global = isolate->GetCurrentContext()->Global();
|
| + for (uint32_t i = 0, length = callbacks->Length(); i < length; ++i) {
|
| + v8::Local<v8::Value> value = callbacks->Get(i);
|
| + v8::Local<v8::Function> callback = value.As<v8::Function>();
|
| + V8PromiseCustom::call(callback, global, result, mode, isolate);
|
| + }
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| void V8Promise::constructorCustom(const v8::FunctionCallbackInfo<v8::Value>& args)
|
| {
|
| v8SetReturnValue(args, v8::Local<v8::Value>());
|
| v8::Isolate* isolate = args.GetIsolate();
|
| - if (!args.Length() || args[0].IsEmpty() || !args[0]->IsFunction()) {
|
| + if (!args.Length() || !args[0]->IsFunction()) {
|
| 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;
|
| +}
|
| +
|
| +//
|
| +// -- 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));
|
|
|
| - internal->SetInternalField(V8PromiseCustom::InternalStateIndex, v8::NumberObject::New(V8PromiseCustom::Pending));
|
| - internal->SetInternalField(V8PromiseCustom::InternalResultIndex, v8::Undefined());
|
| + (*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)
|
| +{
|
| + if (isInternalDetached(resolver))
|
| + return;
|
| + v8::Local<v8::Object> internal = getInternal(resolver);
|
| + if (getState(internal) != Pending)
|
| + return;
|
| +
|
| + v8::Local<v8::Array> callbacks = internal->GetInternalField(V8PromiseCustom::InternalFulfillCallbackIndex).As<v8::Array>();
|
| + clearInternal(internal, Fulfilled, result);
|
| + detachInternal(resolver, isolate);
|
| +
|
| + callCallbacks(callbacks, result, mode, isolate);
|
| +}
|
| +
|
| +void V8PromiseCustom::rejectResolver(v8::Handle<v8::Object> resolver, v8::Handle<v8::Value> result, SynchronousMode mode, v8::Isolate* isolate)
|
| +{
|
| + if (isInternalDetached(resolver))
|
| + return;
|
| + v8::Local<v8::Object> internal = getInternal(resolver);
|
| + if (getState(internal) != Pending)
|
| + return;
|
| +
|
| + v8::Local<v8::Array> callbacks = internal->GetInternalField(V8PromiseCustom::InternalRejectCallbackIndex).As<v8::Array>();
|
| + clearInternal(internal, Rejected, result);
|
| + detachInternal(resolver, isolate);
|
| +
|
| + callCallbacks(callbacks, result, mode, isolate);
|
| +}
|
| +
|
| +void V8PromiseCustom::append(v8::Handle<v8::Object> promise, v8::Handle<v8::Function> fulfillCallback, v8::Handle<v8::Function> rejectCallback, v8::Isolate* isolate)
|
| +{
|
| + // fulfillCallback and rejectCallback can be empty.
|
| + v8::Local<v8::Object> internal = getInternal(promise).As<v8::Object>();
|
| +
|
| + 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;
|
| + }
|
| +
|
| + ASSERT(state == Pending);
|
| + if (!fulfillCallback.IsEmpty()) {
|
| + v8::Local<v8::Array> callbacks = internal->GetInternalField(InternalFulfillCallbackIndex).As<v8::Array>();
|
| + callbacks->Set(callbacks->Length(), fulfillCallback);
|
| + }
|
| + if (!rejectCallback.IsEmpty()) {
|
| + v8::Local<v8::Array> callbacks = internal->GetInternalField(InternalRejectCallbackIndex).As<v8::Array>();
|
| + callbacks->Set(callbacks->Length(), rejectCallback);
|
| + }
|
| +}
|
| +
|
| +v8::Local<v8::Object> V8PromiseCustom::getInternal(v8::Handle<v8::Object> promiseOrResolver)
|
| +{
|
| + v8::Local<v8::Value> value = promiseOrResolver->GetInternalField(v8DOMWrapperObjectIndex);
|
| + // This function cannot be called when the internal object is detached, so the value must be an object.
|
| + return value.As<v8::Object>();
|
| +}
|
| +
|
| +bool V8PromiseCustom::isInternalDetached(v8::Handle<v8::Object> resolver)
|
| +{
|
| + v8::Local<v8::Value> value = resolver->GetInternalField(v8DOMWrapperObjectIndex);
|
| + ASSERT(!value.IsEmpty());
|
| + return value->IsUndefined();
|
| +}
|
| +
|
| +void V8PromiseCustom::detachInternal(v8::Handle<v8::Object> resolver, v8::Isolate* isolate)
|
| +{
|
| + resolver->SetInternalField(v8DOMWrapperObjectIndex, v8::Undefined(isolate));
|
| +}
|
| +
|
| +void V8PromiseCustom::clearInternal(v8::Handle<v8::Object> internal, PromiseState state, v8::Handle<v8::Value> value)
|
| +{
|
| + 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)
|
| +{
|
| + v8::Handle<v8::Value> value = internal->GetInternalField(V8PromiseCustom::InternalStateIndex);
|
| + bool ok = false;
|
| + uint32_t number = toInt32(value, ok);
|
| + ASSERT(ok && (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(state == Pending || state == Fulfilled || state == Rejected);
|
| + internal->SetInternalField(V8PromiseCustom::InternalStateIndex, v8::Integer::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)
|
| +{
|
| + if (mode == Synchronous) {
|
| + v8::Context::Scope scope(isolate->GetCurrentContext());
|
| + // If an exception is thrown, catch it and do nothing.
|
| + v8::TryCatch trycatch;
|
| + v8::Handle<v8::Value> args[] = { result };
|
| + V8ScriptRunner::callFunction(function, getScriptExecutionContext(), receiver, WTF_ARRAY_LENGTH(args), args);
|
| + } else {
|
| + ASSERT(mode == Asynchronous);
|
| + postTask(function, receiver, result, isolate);
|
| }
|
| - v8SetReturnValue(args, promise);
|
| - return;
|
| }
|
|
|
| } // namespace WebCore
|
|
|