OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2013 Google Inc. All rights reserved. | 2 * Copyright (C) 2013 Google Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
6 * met: | 6 * met: |
7 * | 7 * |
8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
(...skipping 15 matching lines...) Expand all Loading... |
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
29 */ | 29 */ |
30 | 30 |
31 #include "config.h" | 31 #include "config.h" |
32 #include "bindings/v8/custom/V8PromiseCustom.h" | 32 #include "bindings/v8/custom/V8PromiseCustom.h" |
33 | 33 |
34 #include "V8Promise.h" | 34 #include "V8Promise.h" |
35 #include "V8PromiseResolver.h" | 35 #include "V8PromiseResolver.h" |
| 36 #include "bindings/v8/ScopedPersistent.h" |
| 37 #include "bindings/v8/ScriptFunctionCall.h" |
36 #include "bindings/v8/V8Binding.h" | 38 #include "bindings/v8/V8Binding.h" |
| 39 #include "bindings/v8/V8PerIsolateData.h" |
37 #include "bindings/v8/V8ScriptRunner.h" | 40 #include "bindings/v8/V8ScriptRunner.h" |
38 #include "bindings/v8/WrapperTypeInfo.h" | 41 #include "bindings/v8/WrapperTypeInfo.h" |
| 42 #include "core/dom/Document.h" |
| 43 #include "core/dom/ExceptionCode.h" |
| 44 #include "core/page/DOMWindow.h" |
| 45 #include "core/platform/Task.h" |
| 46 #include "wtf/Functional.h" |
| 47 #include "wtf/PassOwnPtr.h" |
39 #include <v8.h> | 48 #include <v8.h> |
40 | 49 |
41 namespace WebCore { | 50 namespace WebCore { |
42 | 51 |
| 52 namespace { |
| 53 |
| 54 class PromiseTask : public ScriptExecutionContext::Task { |
| 55 public: |
| 56 PromiseTask(v8::Handle<v8::Function> callback, v8::Handle<v8::Object> receiv
er, v8::Handle<v8::Value> result) |
| 57 : m_callback(callback) |
| 58 , m_receiver(receiver) |
| 59 , m_result(result) |
| 60 { |
| 61 ASSERT(!m_callback.isEmpty()); |
| 62 ASSERT(!m_receiver.isEmpty()); |
| 63 ASSERT(!m_result.isEmpty()); |
| 64 } |
| 65 virtual ~PromiseTask() { } |
| 66 |
| 67 virtual void performTask(ScriptExecutionContext*) OVERRIDE; |
| 68 |
| 69 private: |
| 70 ScopedPersistent<v8::Function> m_callback; |
| 71 ScopedPersistent<v8::Object> m_receiver; |
| 72 ScopedPersistent<v8::Value> m_result; |
| 73 }; |
| 74 |
| 75 void PromiseTask::performTask(ScriptExecutionContext* context) |
| 76 { |
| 77 ASSERT(context && context->isDocument()); |
| 78 if (context->activeDOMObjectsAreStopped()) |
| 79 return; |
| 80 ScriptState* state = mainWorldScriptState(static_cast<Document*>(context)->f
rame()); |
| 81 v8::HandleScope handleScope; |
| 82 ASSERT(state); |
| 83 v8::Handle<v8::Context> v8Context = state->context(); |
| 84 v8::Context::Scope scope(v8Context); |
| 85 v8::Isolate* isolate = v8Context->GetIsolate(); |
| 86 v8::Handle<v8::Value> args[] = { m_result.newLocal(isolate) }; |
| 87 V8ScriptRunner::callFunction(m_callback.newLocal(isolate), context, m_receiv
er.newLocal(isolate), WTF_ARRAY_LENGTH(args), args); |
| 88 }; |
| 89 |
| 90 v8::Handle<v8::Value> postTask(v8::Handle<v8::Function> callback, v8::Handle<v8:
:Object> receiver, v8::Handle<v8::Value> value, v8::Isolate* isolate) |
| 91 { |
| 92 DOMWindow* window = activeDOMWindow(); |
| 93 ASSERT(window); |
| 94 Document* document = window->document(); |
| 95 ASSERT(document); |
| 96 document->postTask(adoptPtr(new PromiseTask(callback, receiver, value))); |
| 97 return v8::Undefined(isolate); |
| 98 } |
| 99 |
| 100 void callCallbacks(v8::Handle<v8::Array> callbacks, v8::Handle<v8::Value> result
, V8PromiseCustom::SynchronousMode mode, v8::Isolate* isolate) |
| 101 { |
| 102 v8::Local<v8::Object> global = isolate->GetCurrentContext()->Global(); |
| 103 for (uint32_t i = 0, length = callbacks->Length(); i < length; ++i) { |
| 104 v8::Local<v8::Value> value = callbacks->Get(i); |
| 105 v8::Local<v8::Function> callback = value.As<v8::Function>(); |
| 106 V8PromiseCustom::call(callback, global, result, mode, isolate); |
| 107 } |
| 108 } |
| 109 |
| 110 } // namespace |
| 111 |
43 void V8Promise::constructorCustom(const v8::FunctionCallbackInfo<v8::Value>& arg
s) | 112 void V8Promise::constructorCustom(const v8::FunctionCallbackInfo<v8::Value>& arg
s) |
44 { | 113 { |
45 v8SetReturnValue(args, v8::Local<v8::Value>()); | 114 v8SetReturnValue(args, v8::Local<v8::Value>()); |
46 v8::Isolate* isolate = args.GetIsolate(); | 115 v8::Isolate* isolate = args.GetIsolate(); |
47 if (!args.Length() || args[0].IsEmpty() || !args[0]->IsFunction()) { | 116 if (!args.Length() || !args[0]->IsFunction()) { |
48 throwTypeError("Promise constructor takes a function argument", isolate)
; | 117 throwTypeError("Promise constructor takes a function argument", isolate)
; |
49 return; | 118 return; |
50 } | 119 } |
51 v8::Handle<v8::Function> init = args[0].As<v8::Function>(); | 120 v8::Local<v8::Function> init = args[0].As<v8::Function>(); |
52 v8::Local<v8::ObjectTemplate> internalTemplate = v8::ObjectTemplate::New(); | 121 v8::Local<v8::Object> promise, resolver; |
53 internalTemplate->SetInternalFieldCount(V8PromiseCustom::InternalFieldCount)
; | 122 V8PromiseCustom::createPromise(args.Holder(), &promise, &resolver, isolate); |
54 v8::Local<v8::Object> internal = internalTemplate->NewInstance(); | |
55 v8::Local<v8::Object> promise = V8DOMWrapper::createWrapper(args.Holder(), &
V8Promise::info, 0, isolate); | |
56 v8::Local<v8::Object> promiseResolver = V8DOMWrapper::createWrapper(args.Hol
der(), &V8PromiseResolver::info, 0, isolate); | |
57 | |
58 internal->SetInternalField(V8PromiseCustom::InternalStateIndex, v8::NumberOb
ject::New(V8PromiseCustom::Pending)); | |
59 internal->SetInternalField(V8PromiseCustom::InternalResultIndex, v8::Undefin
ed()); | |
60 internal->SetInternalField(V8PromiseCustom::InternalFulfillCallbackIndex, v8
::Array::New()); | |
61 internal->SetInternalField(V8PromiseCustom::InternalRejectCallbackIndex, v8:
:Array::New()); | |
62 | |
63 promise->SetInternalField(v8DOMWrapperObjectIndex, internal); | |
64 promiseResolver->SetInternalField(v8DOMWrapperObjectIndex, internal); | |
65 | |
66 v8::Handle<v8::Value> argv[] = { | 123 v8::Handle<v8::Value> argv[] = { |
67 promiseResolver, | 124 resolver, |
68 }; | 125 }; |
69 v8::TryCatch trycatch; | 126 v8::TryCatch trycatch; |
70 if (V8ScriptRunner::callFunction(init, getScriptExecutionContext(), promise,
WTF_ARRAY_LENGTH(argv), argv).IsEmpty()) { | 127 if (V8ScriptRunner::callFunction(init, getScriptExecutionContext(), promise,
WTF_ARRAY_LENGTH(argv), argv).IsEmpty()) { |
71 // FIXME: An exception is thrown. Reject the promise. | 128 // An exception is thrown. Reject the promise. |
| 129 V8PromiseCustom::rejectResolver(resolver, trycatch.Exception(), V8Promis
eCustom::Asynchronous, isolate); |
72 } | 130 } |
73 v8SetReturnValue(args, promise); | 131 v8SetReturnValue(args, promise); |
74 return; | 132 return; |
75 } | 133 } |
76 | 134 |
| 135 // |
| 136 // -- V8PromiseCustom -- |
| 137 void V8PromiseCustom::createPromise(v8::Handle<v8::Object> creationContext, v8::
Local<v8::Object>* promise, v8::Local<v8::Object>* resolver, v8::Isolate* isolat
e) |
| 138 { |
| 139 // FIXME: v8::ObjectTemplate::New should be cached. |
| 140 v8::Local<v8::ObjectTemplate> internalTemplate = v8::ObjectTemplate::New(); |
| 141 internalTemplate->SetInternalFieldCount(InternalFieldCount); |
| 142 v8::Local<v8::Object> internal = internalTemplate->NewInstance(); |
| 143 *promise = V8DOMWrapper::createWrapper(creationContext, &V8Promise::info, 0,
isolate); |
| 144 *resolver = V8DOMWrapper::createWrapper(creationContext, &V8PromiseResolver:
:info, 0, isolate); |
| 145 |
| 146 clearInternal(internal, V8PromiseCustom::Pending, v8::Undefined(isolate)); |
| 147 |
| 148 (*promise)->SetInternalField(v8DOMWrapperObjectIndex, internal); |
| 149 (*resolver)->SetInternalField(v8DOMWrapperObjectIndex, internal); |
| 150 } |
| 151 |
| 152 void V8PromiseCustom::fulfillResolver(v8::Handle<v8::Object> resolver, v8::Handl
e<v8::Value> result, SynchronousMode mode, v8::Isolate* isolate) |
| 153 { |
| 154 if (isInternalDetached(resolver)) |
| 155 return; |
| 156 v8::Local<v8::Object> internal = getInternal(resolver); |
| 157 if (getState(internal) != Pending) |
| 158 return; |
| 159 |
| 160 v8::Local<v8::Array> callbacks = internal->GetInternalField(V8PromiseCustom:
:InternalFulfillCallbackIndex).As<v8::Array>(); |
| 161 clearInternal(internal, Fulfilled, result); |
| 162 detachInternal(resolver, isolate); |
| 163 |
| 164 callCallbacks(callbacks, result, mode, isolate); |
| 165 } |
| 166 |
| 167 void V8PromiseCustom::rejectResolver(v8::Handle<v8::Object> resolver, v8::Handle
<v8::Value> result, SynchronousMode mode, v8::Isolate* isolate) |
| 168 { |
| 169 if (isInternalDetached(resolver)) |
| 170 return; |
| 171 v8::Local<v8::Object> internal = getInternal(resolver); |
| 172 if (getState(internal) != Pending) |
| 173 return; |
| 174 |
| 175 v8::Local<v8::Array> callbacks = internal->GetInternalField(V8PromiseCustom:
:InternalRejectCallbackIndex).As<v8::Array>(); |
| 176 clearInternal(internal, Rejected, result); |
| 177 detachInternal(resolver, isolate); |
| 178 |
| 179 callCallbacks(callbacks, result, mode, isolate); |
| 180 } |
| 181 |
| 182 void V8PromiseCustom::append(v8::Handle<v8::Object> promise, v8::Handle<v8::Func
tion> fulfillCallback, v8::Handle<v8::Function> rejectCallback, v8::Isolate* iso
late) |
| 183 { |
| 184 // fulfillCallback and rejectCallback can be empty. |
| 185 v8::Local<v8::Object> internal = getInternal(promise).As<v8::Object>(); |
| 186 |
| 187 PromiseState state = getState(internal); |
| 188 if (state == Fulfilled) { |
| 189 if (!fulfillCallback.IsEmpty()) { |
| 190 v8::Local<v8::Value> result = internal->GetInternalField(V8PromiseCu
stom::InternalResultIndex); |
| 191 v8::Local<v8::Object> global = isolate->GetCurrentContext()->Global(
); |
| 192 call(fulfillCallback, global, result, Asynchronous, isolate); |
| 193 } |
| 194 return; |
| 195 } |
| 196 if (state == Rejected) { |
| 197 if (!rejectCallback.IsEmpty()) { |
| 198 v8::Local<v8::Value> result = internal->GetInternalField(V8PromiseCu
stom::InternalResultIndex); |
| 199 v8::Local<v8::Object> global = isolate->GetCurrentContext()->Global(
); |
| 200 call(rejectCallback, global, result, Asynchronous, isolate); |
| 201 } |
| 202 return; |
| 203 } |
| 204 |
| 205 ASSERT(state == Pending); |
| 206 if (!fulfillCallback.IsEmpty()) { |
| 207 v8::Local<v8::Array> callbacks = internal->GetInternalField(InternalFulf
illCallbackIndex).As<v8::Array>(); |
| 208 callbacks->Set(callbacks->Length(), fulfillCallback); |
| 209 } |
| 210 if (!rejectCallback.IsEmpty()) { |
| 211 v8::Local<v8::Array> callbacks = internal->GetInternalField(InternalReje
ctCallbackIndex).As<v8::Array>(); |
| 212 callbacks->Set(callbacks->Length(), rejectCallback); |
| 213 } |
| 214 } |
| 215 |
| 216 v8::Local<v8::Object> V8PromiseCustom::getInternal(v8::Handle<v8::Object> promis
eOrResolver) |
| 217 { |
| 218 v8::Local<v8::Value> value = promiseOrResolver->GetInternalField(v8DOMWrappe
rObjectIndex); |
| 219 // This function cannot be called when the internal object is detached, so t
he value must be an object. |
| 220 return value.As<v8::Object>(); |
| 221 } |
| 222 |
| 223 bool V8PromiseCustom::isInternalDetached(v8::Handle<v8::Object> resolver) |
| 224 { |
| 225 v8::Local<v8::Value> value = resolver->GetInternalField(v8DOMWrapperObjectIn
dex); |
| 226 ASSERT(!value.IsEmpty()); |
| 227 return value->IsUndefined(); |
| 228 } |
| 229 |
| 230 void V8PromiseCustom::detachInternal(v8::Handle<v8::Object> resolver, v8::Isolat
e* isolate) |
| 231 { |
| 232 resolver->SetInternalField(v8DOMWrapperObjectIndex, v8::Undefined(isolate)); |
| 233 } |
| 234 |
| 235 void V8PromiseCustom::clearInternal(v8::Handle<v8::Object> internal, PromiseStat
e state, v8::Handle<v8::Value> value) |
| 236 { |
| 237 setState(internal, state); |
| 238 internal->SetInternalField(V8PromiseCustom::InternalResultIndex, value); |
| 239 internal->SetInternalField(V8PromiseCustom::InternalFulfillCallbackIndex, v8
::Array::New()); |
| 240 internal->SetInternalField(V8PromiseCustom::InternalRejectCallbackIndex, v8:
:Array::New()); |
| 241 } |
| 242 |
| 243 V8PromiseCustom::PromiseState V8PromiseCustom::getState(v8::Handle<v8::Object> i
nternal) |
| 244 { |
| 245 v8::Handle<v8::Value> value = internal->GetInternalField(V8PromiseCustom::In
ternalStateIndex); |
| 246 bool ok = false; |
| 247 uint32_t number = toInt32(value, ok); |
| 248 ASSERT(ok && (number == Pending || number == Fulfilled || number == Rejected
)); |
| 249 return static_cast<PromiseState>(number); |
| 250 } |
| 251 |
| 252 void V8PromiseCustom::setState(v8::Handle<v8::Object> internal, PromiseState sta
te) |
| 253 { |
| 254 ASSERT(state == Pending || state == Fulfilled || state == Rejected); |
| 255 internal->SetInternalField(V8PromiseCustom::InternalStateIndex, v8::Integer:
:New(state)); |
| 256 } |
| 257 |
| 258 void V8PromiseCustom::call(v8::Handle<v8::Function> function, v8::Handle<v8::Obj
ect> receiver, v8::Handle<v8::Value> result, SynchronousMode mode, v8::Isolate*
isolate) |
| 259 { |
| 260 if (mode == Synchronous) { |
| 261 v8::Context::Scope scope(isolate->GetCurrentContext()); |
| 262 // If an exception is thrown, catch it and do nothing. |
| 263 v8::TryCatch trycatch; |
| 264 v8::Handle<v8::Value> args[] = { result }; |
| 265 V8ScriptRunner::callFunction(function, getScriptExecutionContext(), rece
iver, WTF_ARRAY_LENGTH(args), args); |
| 266 } else { |
| 267 ASSERT(mode == Asynchronous); |
| 268 postTask(function, receiver, result, isolate); |
| 269 } |
| 270 } |
| 271 |
77 } // namespace WebCore | 272 } // namespace WebCore |
OLD | NEW |