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/core/v8/ScriptPromisePropertyBase.h" 7 8 #include "bindings/core/v8/ScopedPersistent.h" 9 #include "bindings/core/v8/ScriptState.h" 10 #include "bindings/core/v8/V8Binding.h" 11 #include "bindings/core/v8/V8HiddenValue.h" 12 #include "core/dom/ExecutionContext.h" 13 14 namespace blink { 15 16 ScriptPromisePropertyBase::ScriptPromisePropertyBase(ExecutionContext* executionContext, Name name) 17 : ContextLifecycleObserver(executionContext) 18 , m_isolate(toIsolate(executionContext)) 19 , m_name(name) 20 , m_state(Pending) 21 { 22 } 23 24 ScriptPromisePropertyBase::~ScriptPromisePropertyBase() 25 { 26 clearWrappers(); 27 } 28 29 static void clearHandle(const v8::WeakCallbackData<v8::Object, ScopedPersistent<v8::Object> >& data) 30 { 31 data.GetParameter()->clear(); 32 } 33 34 ScriptPromise ScriptPromisePropertyBase::promise(DOMWrapperWorld& world) 35 { 36 if (!executionContext()) 37 return ScriptPromise(); 38 39 v8::HandleScope handleScope(m_isolate); 40 v8::Handle<v8::Context> context = toV8Context(executionContext(), world); 41 if (context.IsEmpty()) 42 return ScriptPromise(); 43 ScriptState* scriptState = ScriptState::from(context); 44 ScriptState::Scope scope(scriptState); 45 46 v8::Handle<v8::Object> wrapper = ensureHolderWrapper(scriptState); 47 ASSERT(wrapper->CreationContext() == context); 48 49 v8::Handle<v8::Value> cachedPromise = V8HiddenValue::getHiddenValue(m_isolate, wrapper, promiseName()); 50 if (!cachedPromise.IsEmpty()) 51 return ScriptPromise(scriptState, cachedPromise); 52 53 // Create and cache the Promise 54 v8::Handle<v8::Promise::Resolver> resolver = v8::Promise::Resolver::New(m_isolate); 55 v8::Handle<v8::Promise> promise = resolver->GetPromise(); 56 V8HiddenValue::setHiddenValue(m_isolate, wrapper, promiseName(), promise); 57 58 switch (m_state) { 59 case Pending: 60 // Cache the resolver too 61 V8HiddenValue::setHiddenValue(m_isolate, wrapper, resolverName(), resolver); 62 break; 63 case Resolved: 64 case Rejected: 65 resolveOrRejectInternal(resolver); 66 break; 67 } 68 69 return ScriptPromise(scriptState, promise); 70 } 71 72 void ScriptPromisePropertyBase::resolveOrReject(State targetState) 73 { 74 ASSERT(executionContext()); 75 ASSERT(m_state == Pending); 76 ASSERT(targetState == Resolved || targetState == Rejected); 77 78 m_state = targetState; 79 80 v8::HandleScope handleScope(m_isolate); 81 size_t i = 0; 82 while (i < m_wrappers.size()) { 83 const OwnPtr<ScopedPersistent<v8::Object> >& persistent = m_wrappers[i]; 84 if (persistent->isEmpty()) { 85 // wrapper has died. 86 // Since v8 GC can run during the iteration and clear the reference, 87 // we can't move this check out of the loop. 88 m_wrappers.remove(i); 89 continue; 90 } 91 v8::Local<v8::Object> wrapper = persistent->newLocal(m_isolate); 92 ScriptState::Scope scope(ScriptState::from(wrapper->CreationContext())); 93 94 v8::Local<v8::Promise::Resolver> resolver = V8HiddenValue::getHiddenValue(m_isolate, wrapper, resolverName()).As<v8::Promise::Resolver>(); 95 96 V8HiddenValue::deleteHiddenValue(m_isolate, wrapper, resolverName()); 97 resolveOrRejectInternal(resolver); 98 ++i; 99 } 100 } 101 102 void ScriptPromisePropertyBase::resetBase() 103 { 104 clearWrappers(); 105 m_state = Pending; 106 } 107 108 void ScriptPromisePropertyBase::resolveOrRejectInternal(v8::Handle<v8::Promise::Resolver> resolver) 109 { 110 switch (m_state) { 111 case Pending: 112 ASSERT_NOT_REACHED(); 113 break; 114 case Resolved: 115 resolver->Resolve(resolvedValue(resolver->CreationContext()->Global(), m_isolate)); 116 break; 117 case Rejected: 118 resolver->Reject(rejectedValue(resolver->CreationContext()->Global(), m_isolate)); 119 break; 120 } 121 } 122 123 v8::Local<v8::Object> ScriptPromisePropertyBase::ensureHolderWrapper(ScriptState* scriptState) 124 { 125 v8::Local<v8::Context> context = scriptState->context(); 126 size_t i = 0; 127 while (i < m_wrappers.size()) { 128 const OwnPtr<ScopedPersistent<v8::Object> >& persistent = m_wrappers[i]; 129 if (persistent->isEmpty()) { 130 // wrapper has died. 131 // Since v8 GC can run during the iteration and clear the reference, 132 // we can't move this check out of the loop. 133 m_wrappers.remove(i); 134 continue; 135 } 136 137 v8::Local<v8::Object> wrapper = persistent->newLocal(m_isolate); 138 if (wrapper->CreationContext() == context) 139 return wrapper; 140 ++i; 141 } 142 v8::Local<v8::Object> wrapper = holder(context->Global(), m_isolate); 143 OwnPtr<ScopedPersistent<v8::Object> > weakPersistent = adoptPtr(new ScopedPersistent<v8::Object>); 144 weakPersistent->set(m_isolate, wrapper); 145 weakPersistent->setWeak(weakPersistent.get(), &clearHandle); 146 m_wrappers.append(weakPersistent.release()); 147 return wrapper; 148 } 149 150 void ScriptPromisePropertyBase::clearWrappers() 151 { 152 v8::HandleScope handleScope(m_isolate); 153 for (WeakPersistentSet::iterator i = m_wrappers.begin(); i != m_wrappers.end(); ++i) { 154 v8::Local<v8::Object> wrapper = (*i)->newLocal(m_isolate); 155 if (!wrapper.IsEmpty()) { 156 wrapper->DeleteHiddenValue(resolverName()); 157 wrapper->DeleteHiddenValue(promiseName()); 158 } 159 } 160 m_wrappers.clear(); 161 } 162 163 v8::Handle<v8::String> ScriptPromisePropertyBase::promiseName() 164 { 165 switch (m_name) { 166 #define P(Name) \ 167 case Name: \ 168 return V8HiddenValue::Name ## Promise(m_isolate); 169 170 SCRIPT_PROMISE_PROPERTIES(P) 171 172 #undef P 173 } 174 ASSERT_NOT_REACHED(); 175 return v8::Handle<v8::String>(); 176 } 177 178 v8::Handle<v8::String> ScriptPromisePropertyBase::resolverName() 179 { 180 switch (m_name) { 181 #define P(Name) \ 182 case Name: \ 183 return V8HiddenValue::Name ## Resolver(m_isolate); 184 185 SCRIPT_PROMISE_PROPERTIES(P) 186 187 #undef P 188 } 189 ASSERT_NOT_REACHED(); 190 return v8::Handle<v8::String>(); 191 } 192 193 } // namespace blink 194