OLD | NEW |
(Empty) | |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "config.h" |
| 6 #include "bindings/v8/ScriptPromisePropertyBase.h" |
| 7 |
| 8 #include "bindings/v8/V8Binding.h" |
| 9 #include "bindings/v8/V8HiddenValue.h" |
| 10 #include "core/dom/ExecutionContext.h" |
| 11 #include "wtf/text/StringBuilder.h" |
| 12 |
| 13 namespace WebCore { |
| 14 |
| 15 class ScriptPromisePropertyBase::PendingAction { |
| 16 public: |
| 17 PendingAction(v8::Isolate* isolate, v8::Handle<v8::Promise::Resolver> resolv
er, v8::Handle<v8::Value> value, State state) |
| 18 : m_resolver(isolate, resolver) |
| 19 , m_value(isolate, value) |
| 20 , m_state(state) { } |
| 21 ~PendingAction() { } |
| 22 |
| 23 void execute(v8::Isolate*); |
| 24 |
| 25 protected: |
| 26 ScopedPersistent<v8::Promise::Resolver> m_resolver; |
| 27 ScopedPersistent<v8::Value> m_value; |
| 28 State m_state; |
| 29 }; |
| 30 |
| 31 void ScriptPromisePropertyBase::PendingAction::execute(v8::Isolate* isolate) |
| 32 { |
| 33 v8::HandleScope handleScope(isolate); |
| 34 ScriptState::Scope scope(ScriptState::from(m_resolver.newLocal(isolate)->Cre
ationContext())); |
| 35 v8::Handle<v8::Promise::Resolver> resolver = m_resolver.newLocal(isolate); |
| 36 m_resolver.clear(); |
| 37 v8::Handle<v8::Value> value = m_value.newLocal(isolate); |
| 38 m_value.clear(); |
| 39 |
| 40 switch (m_state) { |
| 41 case Pending: |
| 42 ASSERT_NOT_REACHED(); |
| 43 break; |
| 44 case Resolved: |
| 45 resolver->Resolve(value); |
| 46 break; |
| 47 case Rejected: |
| 48 resolver->Reject(value); |
| 49 break; |
| 50 } |
| 51 } |
| 52 |
| 53 static v8::Handle<v8::String> makeName(v8::Isolate* isolate, const char* name, c
onst char* suffix) |
| 54 { |
| 55 StringBuilder builder; |
| 56 builder.append(name); |
| 57 builder.append(suffix); |
| 58 return v8String(isolate, builder.toString()); |
| 59 } |
| 60 |
| 61 ScriptPromisePropertyBase::ScriptPromisePropertyBase(ExecutionContext* execution
Context, const char* name) |
| 62 : ActiveDOMObject(executionContext) |
| 63 , m_isolate(toIsolate(executionContext)) |
| 64 , m_state(Pending) |
| 65 , m_inResume(false) |
| 66 { |
| 67 v8::HandleScope handleScope(m_isolate); |
| 68 m_resolverName.set(m_isolate, makeName(m_isolate, name, "/Resolver")); |
| 69 m_promiseName.set(m_isolate, makeName(m_isolate, name, "/Promise")); |
| 70 } |
| 71 |
| 72 ScriptPromisePropertyBase::~ScriptPromisePropertyBase() |
| 73 { |
| 74 v8::HandleScope handleScope(m_isolate); |
| 75 v8::Handle<v8::Object> wrapper = m_mainWorldWrapper.newLocal(m_isolate); |
| 76 m_mainWorldWrapper.clear(); |
| 77 if (!wrapper.IsEmpty()) { |
| 78 v8::Handle<v8::String> resolverName = m_resolverName.newLocal(m_isolate)
; |
| 79 wrapper->DeleteHiddenValue(resolverName); |
| 80 v8::Handle<v8::String> promiseName = m_promiseName.newLocal(m_isolate); |
| 81 wrapper->DeleteHiddenValue(promiseName); |
| 82 } |
| 83 } |
| 84 |
| 85 static void clearHandle(const v8::WeakCallbackData<v8::Object, ScopedPersistent<
v8::Object> >& data) |
| 86 { |
| 87 data.GetParameter()->clear(); |
| 88 } |
| 89 |
| 90 ScriptPromise ScriptPromisePropertyBase::promise(DOMWrapperWorld& world) |
| 91 { |
| 92 ASSERT(executionContext() && !executionContext()->activeDOMObjectsAreStopped
()); |
| 93 |
| 94 if (!world.isMainWorld()) { |
| 95 // FIXME: Support isolated worlds. |
| 96 return ScriptPromise(); |
| 97 } |
| 98 |
| 99 v8::HandleScope handleScope(m_isolate); |
| 100 ASSERT(m_isolate == toIsolate(executionContext())); |
| 101 v8::Handle<v8::Context> context = toV8Context(executionContext(), world); |
| 102 if (context.IsEmpty()) |
| 103 return ScriptPromise(); |
| 104 ScriptState* scriptState = ScriptState::from(context); |
| 105 ScriptState::Scope scope(scriptState); |
| 106 |
| 107 v8::Handle<v8::String> resolverName = m_resolverName.newLocal(m_isolate); |
| 108 v8::Handle<v8::String> promiseName = m_promiseName.newLocal(m_isolate); |
| 109 |
| 110 v8::Handle<v8::Object> wrapper = m_mainWorldWrapper.newLocal(m_isolate); |
| 111 if (wrapper.IsEmpty()) { |
| 112 wrapper = holder(context->Global(), m_isolate); |
| 113 ASSERT(!wrapper.IsEmpty()); |
| 114 ASSERT(V8HiddenValue::getHiddenValue(m_isolate, wrapper, resolverName).I
sEmpty()); |
| 115 ASSERT(V8HiddenValue::getHiddenValue(m_isolate, wrapper, promiseName).Is
Empty()); |
| 116 m_mainWorldWrapper.set(m_isolate, wrapper); |
| 117 m_mainWorldWrapper.setWeak(&m_mainWorldWrapper, &clearHandle); |
| 118 } |
| 119 ASSERT(wrapper->CreationContext() == context); |
| 120 |
| 121 v8::Handle<v8::Promise> promise = V8HiddenValue::getHiddenValue(m_isolate, w
rapper, promiseName).As<v8::Promise>(); |
| 122 if (!promise.IsEmpty()) { |
| 123 // Return cached Promise |
| 124 return ScriptPromise(scriptState, promise); |
| 125 } |
| 126 |
| 127 // Create and cache the Promise |
| 128 v8::Handle<v8::Promise::Resolver> resolver = v8::Promise::Resolver::New(m_is
olate); |
| 129 promise = resolver->GetPromise(); |
| 130 V8HiddenValue::setHiddenValue(m_isolate, wrapper, promiseName, promise); |
| 131 V8HiddenValue::setHiddenValue(m_isolate, promise, promiseName, wrapper); |
| 132 |
| 133 switch (m_state) { |
| 134 case Pending: |
| 135 // Cache the resolver too |
| 136 V8HiddenValue::setHiddenValue(m_isolate, wrapper, resolverName, resolver
); |
| 137 break; |
| 138 case Resolved: |
| 139 case Rejected: |
| 140 resolveOrReject(resolver); |
| 141 break; |
| 142 } |
| 143 |
| 144 return ScriptPromise(scriptState, promise); |
| 145 } |
| 146 |
| 147 void ScriptPromisePropertyBase::settle(State targetState) |
| 148 { |
| 149 ASSERT(executionContext() && !executionContext()->activeDOMObjectsAreStopped
()); |
| 150 ASSERT(m_state == Pending); |
| 151 ASSERT(targetState == Resolved || targetState == Rejected); |
| 152 |
| 153 m_state = targetState; |
| 154 |
| 155 v8::HandleScope handleScope(m_isolate); |
| 156 v8::Handle<v8::Object> wrapper = m_mainWorldWrapper.newLocal(m_isolate); |
| 157 if (wrapper.IsEmpty()) |
| 158 return; // wrapper has died or was never populated |
| 159 ScriptState::Scope scope(ScriptState::from(wrapper->CreationContext())); |
| 160 |
| 161 v8::Handle<v8::String> resolverName = m_resolverName.newLocal(m_isolate); |
| 162 v8::Handle<v8::Promise::Resolver> resolver = V8HiddenValue::getHiddenValue(m
_isolate, wrapper, resolverName).As<v8::Promise::Resolver>(); |
| 163 |
| 164 V8HiddenValue::deleteHiddenValue(m_isolate, wrapper, resolverName); |
| 165 resolveOrReject(resolver); |
| 166 } |
| 167 |
| 168 void ScriptPromisePropertyBase::resolveOrReject(v8::Handle<v8::Promise::Resolver
> resolver) |
| 169 { |
| 170 ASSERT(m_state == Resolved || m_state == Rejected); |
| 171 |
| 172 v8::Handle<v8::Value> value; |
| 173 switch (m_state) { |
| 174 case Pending: |
| 175 ASSERT_NOT_REACHED(); |
| 176 break; |
| 177 case Resolved: |
| 178 value = resolvedValue(resolver->CreationContext()->Global(), m_isolate); |
| 179 break; |
| 180 case Rejected: |
| 181 value = rejectedValue(resolver->CreationContext()->Global(), m_isolate); |
| 182 break; |
| 183 } |
| 184 |
| 185 if (executionContext()->activeDOMObjectsAreSuspended()) { |
| 186 enqueuePendingAction(resolver, value, m_state); |
| 187 return; |
| 188 } |
| 189 |
| 190 switch (m_state) { |
| 191 case Pending: |
| 192 ASSERT_NOT_REACHED(); |
| 193 break; |
| 194 case Resolved: |
| 195 resolver->Resolve(value); |
| 196 break; |
| 197 case Rejected: |
| 198 resolver->Reject(value); |
| 199 break; |
| 200 } |
| 201 } |
| 202 |
| 203 void ScriptPromisePropertyBase::enqueuePendingAction(v8::Handle<v8::Promise::Res
olver> resolver, v8::Handle<v8::Value> value, State state) |
| 204 { |
| 205 if (m_pending.isEmpty()) |
| 206 setPendingActivity(this); |
| 207 m_pending.append(adoptPtr(new PendingAction(m_isolate, resolver, value, stat
e))); |
| 208 } |
| 209 |
| 210 void ScriptPromisePropertyBase::resume() |
| 211 { |
| 212 ASSERT_WITH_SECURITY_IMPLICATION(!m_inResume); |
| 213 if (!hasPendingActivity()) |
| 214 return; |
| 215 ASSERT(m_pending.size()); |
| 216 m_inResume = true; |
| 217 size_t i = 0; |
| 218 while (i < m_pending.size() && executionContext() && !executionContext()->ac
tiveDOMObjectsAreSuspended() && !executionContext()->activeDOMObjectsAreStopped(
)) |
| 219 m_pending[i++]->execute(m_isolate); |
| 220 if (m_pending.size()) |
| 221 m_pending.remove(0, i); |
| 222 if (m_pending.isEmpty()) |
| 223 unsetPendingActivity(this); |
| 224 } |
| 225 |
| 226 void ScriptPromisePropertyBase::stop() |
| 227 { |
| 228 if (m_pending.size()) |
| 229 unsetPendingActivity(this); |
| 230 m_pending.clear(); |
| 231 } |
| 232 |
| 233 } // namespace WebCore |
OLD | NEW |