| /* |
| * Copyright (C) 2013 Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| #include "bindings/v8/custom/V8PromiseCustom.h" |
| |
| #include "V8Promise.h" |
| #include "V8PromiseResolver.h" |
| #include "bindings/v8/ScopedPersistent.h" |
| #include "bindings/v8/ScriptFunctionCall.h" |
| #include "bindings/v8/ScriptState.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/page/DOMWindow.h" |
| #include "core/platform/Task.h" |
| #include "core/workers/WorkerGlobalScope.h" |
| #include "wtf/Functional.h" |
| #include "wtf/PassOwnPtr.h" |
| #include <v8.h> |
| |
| namespace WebCore { |
| |
| namespace { |
| |
| v8::Local<v8::ObjectTemplate> cachedObjectTemplate(void* privateTemplateUniqueKey, int internalFieldCount, v8::Isolate* isolate) |
| { |
| V8PerIsolateData* data = V8PerIsolateData::from(isolate); |
| WrapperWorldType currentWorldType = worldType(isolate); |
| v8::Handle<v8::FunctionTemplate> functionDescriptor = data->privateTemplateIfExists(currentWorldType, privateTemplateUniqueKey); |
| if (!functionDescriptor.IsEmpty()) |
| return functionDescriptor->InstanceTemplate(); |
| |
| functionDescriptor = v8::FunctionTemplate::New(); |
| v8::Local<v8::ObjectTemplate> instanceTemplate = functionDescriptor->InstanceTemplate(); |
| instanceTemplate->SetInternalFieldCount(internalFieldCount); |
| data->setPrivateTemplate(currentWorldType, privateTemplateUniqueKey, functionDescriptor); |
| return instanceTemplate; |
| } |
| |
| v8::Local<v8::ObjectTemplate> wrapperCallbackEnvironmentObjectTemplate(v8::Isolate* isolate) |
| { |
| // This is only for getting a unique pointer which we can pass to privateTemplate. |
| static int privateTemplateUniqueKey = 0; |
| return cachedObjectTemplate(&privateTemplateUniqueKey, V8PromiseCustom::WrapperCallbackEnvironmentFieldCount, isolate); |
| } |
| |
| v8::Local<v8::ObjectTemplate> promiseEveryEnvironmentObjectTemplate(v8::Isolate* isolate) |
| { |
| // This is only for getting a unique pointer which we can pass to privateTemplate. |
| static int privateTemplateUniqueKey = 0; |
| return cachedObjectTemplate(&privateTemplateUniqueKey, V8PromiseCustom::PromiseEveryEnvironmentFieldCount, isolate); |
| } |
| |
| v8::Local<v8::ObjectTemplate> primitiveWrapperObjectTemplate(v8::Isolate* isolate) |
| { |
| // This is only for getting a unique pointer which we can pass to privateTemplate. |
| static int privateTemplateUniqueKey = 0; |
| return cachedObjectTemplate(&privateTemplateUniqueKey, V8PromiseCustom::PrimitiveWrapperFieldCount, isolate); |
| } |
| |
| v8::Local<v8::ObjectTemplate> internalObjectTemplate(v8::Isolate* isolate) |
| { |
| // This is only for getting a unique pointer which we can pass to privateTemplate. |
| static int privateTemplateUniqueKey = 0; |
| return cachedObjectTemplate(&privateTemplateUniqueKey, V8PromiseCustom::InternalFieldCount, isolate); |
| } |
| |
| 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); |
| if (context->activeDOMObjectsAreStopped()) |
| return; |
| |
| ScriptState* state = 0; |
| if (context->isDocument()) { |
| state = mainWorldScriptState(static_cast<Document*>(context)->frame()); |
| } else { |
| ASSERT(context->isWorkerGlobalScope()); |
| state = scriptStateFromWorkerGlobalScope(toWorkerGlobalScope(context)); |
| } |
| ASSERT(state); |
| |
| v8::HandleScope handleScope; |
| 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) |
| { |
| ScriptExecutionContext* scriptExecutionContext = getScriptExecutionContext(); |
| ASSERT(scriptExecutionContext && scriptExecutionContext->isContextThread()); |
| scriptExecutionContext->postTask(adoptPtr(new PromiseTask(callback, receiver, value))); |
| return v8::Undefined(isolate); |
| } |
| |
| void wrapperCallback(const v8::FunctionCallbackInfo<v8::Value>& args) |
| { |
| v8::Isolate* isolate = args.GetIsolate(); |
| ASSERT(!args.Data().IsEmpty()); |
| v8::Local<v8::Object> environment = args.Data().As<v8::Object>(); |
| v8::Local<v8::Value> result = v8::Undefined(isolate); |
| if (args.Length() > 0) |
| result = args[0]; |
| |
| v8::Local<v8::Object> promise = environment->GetInternalField(V8PromiseCustom::WrapperCallbackEnvironmentPromiseIndex).As<v8::Object>(); |
| v8::Local<v8::Object> resolver = environment->GetInternalField(V8PromiseCustom::WrapperCallbackEnvironmentPromiseResolverIndex).As<v8::Object>(); |
| v8::Local<v8::Function> callback = environment->GetInternalField(V8PromiseCustom::WrapperCallbackEnvironmentCallbackIndex).As<v8::Function>(); |
| |
| v8::Local<v8::Value> argv[] = { |
| result, |
| }; |
| v8::TryCatch trycatch; |
| result = V8ScriptRunner::callFunction(callback, getScriptExecutionContext(), promise, WTF_ARRAY_LENGTH(argv), argv); |
| if (result.IsEmpty()) { |
| V8PromiseCustom::rejectResolver(resolver, trycatch.Exception(), V8PromiseCustom::Synchronous, isolate); |
| return; |
| } |
| V8PromiseCustom::resolveResolver(resolver, result, V8PromiseCustom::Synchronous, isolate); |
| } |
| |
| v8::Local<v8::Object> wrapperCallbackEnvironment(v8::Handle<v8::Object> promise, v8::Handle<v8::Object> resolver, v8::Handle<v8::Function> callback, v8::Isolate* isolate) |
| { |
| v8::Local<v8::ObjectTemplate> objectTemplate = wrapperCallbackEnvironmentObjectTemplate(isolate); |
| v8::Local<v8::Object> environment = objectTemplate->NewInstance(); |
| environment->SetInternalField(V8PromiseCustom::WrapperCallbackEnvironmentPromiseIndex, promise); |
| environment->SetInternalField(V8PromiseCustom::WrapperCallbackEnvironmentPromiseResolverIndex, resolver); |
| environment->SetInternalField(V8PromiseCustom::WrapperCallbackEnvironmentCallbackIndex, callback); |
| return environment; |
| } |
| |
| void promiseFulfillCallback(const v8::FunctionCallbackInfo<v8::Value>& args) |
| { |
| ASSERT(!args.Data().IsEmpty()); |
| v8::Local<v8::Object> resolver = args.Data().As<v8::Object>(); |
| v8::Local<v8::Value> result = v8::Undefined(args.GetIsolate()); |
| if (args.Length() > 0) |
| result = args[0]; |
| |
| V8PromiseCustom::fulfillResolver(resolver, result, V8PromiseCustom::Synchronous, args.GetIsolate()); |
| } |
| |
| void promiseResolveCallback(const v8::FunctionCallbackInfo<v8::Value>& args) |
| { |
| ASSERT(!args.Data().IsEmpty()); |
| v8::Local<v8::Object> resolver = args.Data().As<v8::Object>(); |
| v8::Local<v8::Value> result = v8::Undefined(args.GetIsolate()); |
| if (args.Length() > 0) |
| result = args[0]; |
| |
| V8PromiseCustom::resolveResolver(resolver, result, V8PromiseCustom::Synchronous, args.GetIsolate()); |
| } |
| |
| void promiseRejectCallback(const v8::FunctionCallbackInfo<v8::Value>& args) |
| { |
| ASSERT(!args.Data().IsEmpty()); |
| v8::Local<v8::Object> resolver = args.Data().As<v8::Object>(); |
| v8::Local<v8::Value> result = v8::Undefined(args.GetIsolate()); |
| if (args.Length() > 0) |
| result = args[0]; |
| |
| V8PromiseCustom::rejectResolver(resolver, result, V8PromiseCustom::Synchronous, args.GetIsolate()); |
| } |
| |
| 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); |
| } |
| } |
| |
| void promiseEveryFulfillCallback(const v8::FunctionCallbackInfo<v8::Value>& args) |
| { |
| v8::Isolate* isolate = args.GetIsolate(); |
| ASSERT(!args.Data().IsEmpty()); |
| v8::Local<v8::Object> environment = args.Data().As<v8::Object>(); |
| v8::Local<v8::Value> result = v8::Undefined(isolate); |
| if (args.Length() > 0) |
| result = args[0]; |
| |
| v8::Local<v8::Object> resolver = environment->GetInternalField(V8PromiseCustom::PromiseEveryEnvironmentPromiseResolverIndex).As<v8::Object>(); |
| v8::Local<v8::Object> countdownWrapper = environment->GetInternalField(V8PromiseCustom::PromiseEveryEnvironmentCountdownIndex).As<v8::Object>(); |
| v8::Local<v8::Integer> index = environment->GetInternalField(V8PromiseCustom::PromiseEveryEnvironmentIndexIndex).As<v8::Integer>(); |
| v8::Local<v8::Array> results = environment->GetInternalField(V8PromiseCustom::PromiseEveryEnvironmentResultsIndex).As<v8::Array>(); |
| |
| results->Set(index->Value(), result); |
| |
| v8::Local<v8::Integer> countdown = countdownWrapper->GetInternalField(V8PromiseCustom::PrimitiveWrapperPrimitiveIndex).As<v8::Integer>(); |
| ASSERT(countdown->Value() >= 1); |
| if (countdown->Value() == 1) { |
| V8PromiseCustom::resolveResolver(resolver, results, V8PromiseCustom::Synchronous, isolate); |
| return; |
| } |
| countdownWrapper->SetInternalField(V8PromiseCustom::PrimitiveWrapperPrimitiveIndex, v8::Integer::New(countdown->Value() - 1)); |
| } |
| |
| void promiseSomeRejectCallback(const v8::FunctionCallbackInfo<v8::Value>& args) |
| { |
| v8::Isolate* isolate = args.GetIsolate(); |
| ASSERT(!args.Data().IsEmpty()); |
| v8::Local<v8::Object> environment = args.Data().As<v8::Object>(); |
| v8::Local<v8::Value> result = v8::Undefined(isolate); |
| if (args.Length() > 0) |
| result = args[0]; |
| |
| v8::Local<v8::Object> resolver = environment->GetInternalField(V8PromiseCustom::PromiseEveryEnvironmentPromiseResolverIndex).As<v8::Object>(); |
| v8::Local<v8::Object> countdownWrapper = environment->GetInternalField(V8PromiseCustom::PromiseEveryEnvironmentCountdownIndex).As<v8::Object>(); |
| v8::Local<v8::Integer> index = environment->GetInternalField(V8PromiseCustom::PromiseEveryEnvironmentIndexIndex).As<v8::Integer>(); |
| v8::Local<v8::Array> results = environment->GetInternalField(V8PromiseCustom::PromiseEveryEnvironmentResultsIndex).As<v8::Array>(); |
| |
| results->Set(index->Value(), result); |
| |
| v8::Local<v8::Integer> countdown = countdownWrapper->GetInternalField(V8PromiseCustom::PrimitiveWrapperPrimitiveIndex).As<v8::Integer>(); |
| ASSERT(countdown->Value() >= 1); |
| if (countdown->Value() == 1) { |
| V8PromiseCustom::rejectResolver(resolver, results, V8PromiseCustom::Synchronous, isolate); |
| return; |
| } |
| countdownWrapper->SetInternalField(V8PromiseCustom::PrimitiveWrapperPrimitiveIndex, v8::Integer::New(countdown->Value() - 1)); |
| } |
| |
| v8::Local<v8::Object> promiseEveryEnvironment(v8::Handle<v8::Object> resolver, v8::Handle<v8::Object> countdownWrapper, int index, v8::Handle<v8::Array> results, v8::Isolate* isolate) |
| { |
| v8::Local<v8::ObjectTemplate> objectTemplate = promiseEveryEnvironmentObjectTemplate(isolate); |
| v8::Local<v8::Object> environment = objectTemplate->NewInstance(); |
| |
| environment->SetInternalField(V8PromiseCustom::PromiseEveryEnvironmentPromiseResolverIndex, resolver); |
| environment->SetInternalField(V8PromiseCustom::PromiseEveryEnvironmentCountdownIndex, countdownWrapper); |
| environment->SetInternalField(V8PromiseCustom::PromiseEveryEnvironmentIndexIndex, v8::Integer::New(index)); |
| environment->SetInternalField(V8PromiseCustom::PromiseEveryEnvironmentResultsIndex, results); |
| return environment; |
| } |
| |
| } // 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]->IsFunction()) { |
| throwTypeError("Promise constructor takes a function argument", isolate); |
| return; |
| } |
| 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 if its resolved flag is unset. |
| if (!V8PromiseCustom::isInternalDetached(resolver) && V8PromiseCustom::getState(V8PromiseCustom::getInternal(resolver)) == V8PromiseCustom::Pending) |
| 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(); |
| 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]->IsUndefined()) { |
| if (!args[0]->IsFunction()) { |
| v8SetReturnValue(args, throwTypeError("fulfillCallback must be a function or undefined", isolate)); |
| return; |
| } |
| fulfillWrapper = createClosure(wrapperCallback, wrapperCallbackEnvironment(promise, resolver, args[0].As<v8::Function>(), isolate)); |
| } else { |
| fulfillWrapper = createClosure(promiseFulfillCallback, resolver); |
| } |
| if (args.Length() > 1 && !args[1]->IsUndefined()) { |
| if (!args[1]->IsFunction()) { |
| v8SetReturnValue(args, throwTypeError("rejectCallback must be a function or undefined", isolate)); |
| return; |
| } |
| rejectWrapper = createClosure(wrapperCallback, wrapperCallbackEnvironment(promise, resolver, args[1].As<v8::Function>(), isolate)); |
| } else { |
| rejectWrapper = createClosure(promiseRejectCallback, resolver); |
| } |
| V8PromiseCustom::append(args.Holder(), fulfillWrapper, rejectWrapper, isolate); |
| v8SetReturnValue(args, promise); |
| } |
| |
| void V8Promise::catchMethodCustom(const v8::FunctionCallbackInfo<v8::Value>& args) |
| { |
| v8::Isolate* isolate = args.GetIsolate(); |
| 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]->IsUndefined()) { |
| if (!args[0]->IsFunction()) { |
| v8SetReturnValue(args, throwTypeError("rejectCallback must be a function or undefined", isolate)); |
| return; |
| } |
| rejectWrapper = createClosure(wrapperCallback, wrapperCallbackEnvironment(promise, resolver, args[0].As<v8::Function>(), isolate)); |
| } else { |
| rejectWrapper = createClosure(promiseRejectCallback, resolver); |
| } |
| fulfillWrapper = createClosure(promiseFulfillCallback, resolver); |
| V8PromiseCustom::append(args.Holder(), fulfillWrapper, rejectWrapper, isolate); |
| v8SetReturnValue(args, promise); |
| } |
| |
| void V8Promise::fulfillMethodCustom(const v8::FunctionCallbackInfo<v8::Value>& args) |
| { |
| v8::Isolate* isolate = args.GetIsolate(); |
| v8::Local<v8::Value> result = v8::Undefined(isolate); |
| if (args.Length() > 0) |
| result = args[0]; |
| |
| v8::Local<v8::Object> promise, resolver; |
| V8PromiseCustom::createPromise(args.Holder(), &promise, &resolver, isolate); |
| V8PromiseCustom::fulfillResolver(resolver, result, V8PromiseCustom::Asynchronous, isolate); |
| v8SetReturnValue(args, promise); |
| } |
| |
| void V8Promise::resolveMethodCustom(const v8::FunctionCallbackInfo<v8::Value>& args) |
| { |
| v8::Isolate* isolate = args.GetIsolate(); |
| v8::Local<v8::Value> result = v8::Undefined(isolate); |
| if (args.Length() > 0) |
| result = args[0]; |
| |
| v8::Local<v8::Object> promise, resolver; |
| V8PromiseCustom::createPromise(args.Holder(), &promise, &resolver, isolate); |
| V8PromiseCustom::resolveResolver(resolver, result, V8PromiseCustom::Asynchronous, isolate); |
| v8SetReturnValue(args, promise); |
| } |
| |
| void V8Promise::rejectMethodCustom(const v8::FunctionCallbackInfo<v8::Value>& args) |
| { |
| v8::Isolate* isolate = args.GetIsolate(); |
| v8::Local<v8::Value> result = v8::Undefined(isolate); |
| if (args.Length() > 0) |
| result = args[0]; |
| |
| v8::Local<v8::Object> promise, resolver; |
| V8PromiseCustom::createPromise(args.Holder(), &promise, &resolver, isolate); |
| V8PromiseCustom::rejectResolver(resolver, result, V8PromiseCustom::Asynchronous, isolate); |
| v8SetReturnValue(args, promise); |
| } |
| |
| void V8Promise::anyMethodCustom(const v8::FunctionCallbackInfo<v8::Value>& args) |
| { |
| v8::Isolate* isolate = args.GetIsolate(); |
| v8::Local<v8::Object> promise, resolver; |
| V8PromiseCustom::createPromise(args.Holder(), &promise, &resolver, isolate); |
| |
| if (!args.Length()) { |
| V8PromiseCustom::resolveResolver(resolver, v8::Undefined(isolate), V8PromiseCustom::Asynchronous, isolate); |
| v8SetReturnValue(args, promise); |
| return; |
| } |
| |
| v8::Local<v8::Function> fulfillCallback = createClosure(promiseResolveCallback, resolver); |
| v8::Local<v8::Function> rejectCallback = createClosure(promiseRejectCallback, resolver); |
| |
| for (int i = 0; i < args.Length(); ++i) { |
| v8::Local<v8::Object> eachPromise, eachResolver; |
| V8PromiseCustom::createPromise(args.Holder(), &eachPromise, &eachResolver, isolate); |
| V8PromiseCustom::resolveResolver(eachResolver, args[i], V8PromiseCustom::Asynchronous, isolate); |
| V8PromiseCustom::append(eachPromise, fulfillCallback, rejectCallback, isolate); |
| } |
| v8SetReturnValue(args, promise); |
| } |
| |
| void V8Promise::everyMethodCustom(const v8::FunctionCallbackInfo<v8::Value>& args) |
| { |
| v8::Isolate* isolate = args.GetIsolate(); |
| v8::Local<v8::Object> promise, resolver; |
| V8PromiseCustom::createPromise(args.Holder(), &promise, &resolver, isolate); |
| |
| if (!args.Length()) { |
| V8PromiseCustom::resolveResolver(resolver, v8::Undefined(isolate), V8PromiseCustom::Asynchronous, isolate); |
| v8SetReturnValue(args, promise); |
| return; |
| } |
| |
| v8::Local<v8::ObjectTemplate> objectTemplate = primitiveWrapperObjectTemplate(isolate); |
| v8::Local<v8::Object> countdownWrapper = objectTemplate->NewInstance(); |
| countdownWrapper->SetInternalField(V8PromiseCustom::PrimitiveWrapperPrimitiveIndex, v8::Integer::New(args.Length())); |
| v8::Local<v8::Array> results = v8::Array::New(); |
| |
| v8::Local<v8::Function> rejectCallback = createClosure(promiseRejectCallback, resolver); |
| for (int i = 0; i < args.Length(); ++i) { |
| v8::Local<v8::Object> environment = promiseEveryEnvironment(resolver, countdownWrapper, i, results, isolate); |
| v8::Local<v8::Function> fulfillCallback = v8::FunctionTemplate::New(promiseEveryFulfillCallback, environment)->GetFunction(); |
| v8::Local<v8::Object> eachPromise, eachResolver; |
| V8PromiseCustom::createPromise(args.Holder(), &eachPromise, &eachResolver, isolate); |
| V8PromiseCustom::resolveResolver(eachResolver, args[i], V8PromiseCustom::Asynchronous, isolate); |
| V8PromiseCustom::append(eachPromise, fulfillCallback, rejectCallback, isolate); |
| } |
| v8SetReturnValue(args, promise); |
| } |
| |
| void V8Promise::someMethodCustom(const v8::FunctionCallbackInfo<v8::Value>& args) |
| { |
| v8::Isolate* isolate = args.GetIsolate(); |
| v8::Local<v8::Object> promise, resolver; |
| V8PromiseCustom::createPromise(args.Holder(), &promise, &resolver, isolate); |
| |
| if (!args.Length()) { |
| V8PromiseCustom::resolveResolver(resolver, v8::Undefined(isolate), V8PromiseCustom::Asynchronous, isolate); |
| v8SetReturnValue(args, promise); |
| return; |
| } |
| |
| // Promise.some also uses PromiseEveryEnvironment. |
| v8::Local<v8::ObjectTemplate> objectTemplate = primitiveWrapperObjectTemplate(isolate); |
| v8::Local<v8::Object> countdownWrapper = objectTemplate->NewInstance(); |
| countdownWrapper->SetInternalField(V8PromiseCustom::PrimitiveWrapperPrimitiveIndex, v8::Integer::New(args.Length())); |
| v8::Local<v8::Array> results = v8::Array::New(); |
| |
| v8::Local<v8::Function> fulfillCallback = createClosure(promiseResolveCallback, resolver); |
| for (int i = 0; i < args.Length(); ++i) { |
| v8::Local<v8::Object> environment = promiseEveryEnvironment(resolver, countdownWrapper, i, results, isolate); |
| v8::Local<v8::Object> eachPromise, eachResolver; |
| v8::Local<v8::Function> rejectCallback = v8::FunctionTemplate::New(promiseSomeRejectCallback, environment)->GetFunction(); |
| V8PromiseCustom::createPromise(args.Holder(), &eachPromise, &eachResolver, isolate); |
| V8PromiseCustom::resolveResolver(eachResolver, args[i], V8PromiseCustom::Asynchronous, isolate); |
| V8PromiseCustom::append(eachPromise, fulfillCallback, rejectCallback, 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) |
| { |
| v8::Local<v8::ObjectTemplate> internalTemplate = internalObjectTemplate(isolate); |
| v8::Local<v8::Object> internal = internalTemplate->NewInstance(); |
| *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) |
| { |
| if (isInternalDetached(resolver)) |
| return; |
| v8::Local<v8::Object> internal = getInternal(resolver); |
| ASSERT(getState(internal) == Pending || getState(internal) == PendingWithResolvedFlagSet); |
| 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::resolveResolver(v8::Handle<v8::Object> resolver, v8::Handle<v8::Value> result, SynchronousMode mode, v8::Isolate* isolate) |
| { |
| ASSERT(!result.IsEmpty()); |
| v8::Local<v8::Value> then; |
| if (result->IsObject()) { |
| v8::TryCatch trycatch; |
| then = result.As<v8::Object>()->Get(v8::String::NewSymbol("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[] = { |
| createClosure(promiseResolveCallback, resolver), |
| createClosure(promiseRejectCallback, resolver), |
| }; |
| 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) |
| { |
| if (isInternalDetached(resolver)) |
| return; |
| v8::Local<v8::Object> internal = getInternal(resolver); |
| ASSERT(getState(internal) == Pending || getState(internal) == PendingWithResolvedFlagSet); |
| 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); |
| |
| 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 || state == PendingWithResolvedFlagSet); |
| 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()); |
| } |
| |
| 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 || number == PendingWithResolvedFlagSet)); |
| return static_cast<PromiseState>(number); |
| } |
| |
| void V8PromiseCustom::setState(v8::Handle<v8::Object> internal, PromiseState state) |
| { |
| ASSERT(state == Pending || state == Fulfilled || state == Rejected || state == PendingWithResolvedFlagSet); |
| 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); |
| } |
| } |
| |
| } // namespace WebCore |