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 "core/inspector/PromiseTracker.h" 7 8 #include "bindings/core/v8/ScopedPersistent.h" 9 #include "bindings/core/v8/ScriptCallStackFactory.h" 10 #include "bindings/core/v8/ScriptState.h" 11 #include "bindings/core/v8/ScriptValue.h" 12 #include "wtf/PassOwnPtr.h" 13 #include "wtf/WeakPtr.h" 14 15 using blink::TypeBuilder::Array; 16 using blink::TypeBuilder::Console::CallFrame; 17 using blink::TypeBuilder::Debugger::PromiseDetails; 18 19 namespace blink { 20 21 class PromiseTracker::PromiseData FINAL : public RefCountedWillBeGarbageCollectedFinalized<PromiseData> { 22 public: 23 static PassRefPtrWillBeRawPtr<PromiseData> create(ScriptState* scriptState, int promiseHash, int promiseId, v8::Handle<v8::Object> promise) 24 { 25 return adoptRefWillBeNoop(new PromiseData(scriptState, promiseHash, promiseId, promise)); 26 } 27 28 int promiseHash() const { return m_promiseHash; } 29 ScopedPersistent<v8::Object>& promise() { return m_promise; } 30 31 #if ENABLE(OILPAN) 32 void dispose() 33 { 34 m_promise.clear(); 35 m_parentPromise.clear(); 36 } 37 #else 38 WeakPtr<PromiseData> createWeakPtr() 39 { 40 return m_weakPtrFactory.createWeakPtr(); 41 } 42 #endif 43 44 void trace(Visitor* visitor) 45 { 46 visitor->trace(m_callStack); 47 } 48 49 private: 50 friend class PromiseTracker; 51 52 PromiseData(ScriptState* scriptState, int promiseHash, int promiseId, v8::Handle<v8::Object> promise) 53 : m_scriptState(scriptState) 54 , m_promiseHash(promiseHash) 55 , m_promiseId(promiseId) 56 , m_promise(scriptState->isolate(), promise) 57 , m_parentPromiseId(0) 58 , m_status(0) 59 #if !ENABLE(OILPAN) 60 , m_weakPtrFactory(this) 61 #endif 62 { 63 } 64 65 RefPtr<ScriptState> m_scriptState; 66 int m_promiseHash; 67 int m_promiseId; 68 ScopedPersistent<v8::Object> m_promise; 69 int m_parentPromiseId; 70 int m_status; 71 RefPtrWillBeMember<ScriptCallStack> m_callStack; 72 ScopedPersistent<v8::Object> m_parentPromise; 73 #if !ENABLE(OILPAN) 74 WeakPtrFactory<PromiseData> m_weakPtrFactory; 75 #endif 76 }; 77 78 static int indexOf(PromiseTracker::PromiseDataVector* vector, const ScopedPersistent<v8::Object>& promise) 79 { 80 for (size_t index = 0; index < vector->size(); ++index) { 81 if (vector->at(index)->promise() == promise) 82 return index; 83 } 84 return -1; 85 } 86 87 namespace { 88 89 class PromiseDataWrapper FINAL : public NoBaseWillBeGarbageCollected<PromiseDataWrapper> { 90 public: 91 static PassOwnPtrWillBeRawPtr<PromiseDataWrapper> create(PromiseTracker::PromiseData* data, PromiseTracker* tracker) 92 { 93 #if ENABLE(OILPAN) 94 return new PromiseDataWrapper(data, tracker); 95 #else 96 return adoptPtr(new PromiseDataWrapper(data->createWeakPtr(), tracker)); 97 #endif 98 } 99 100 #if ENABLE(OILPAN) 101 static void didRemovePromise(const v8::WeakCallbackData<v8::Object, Persistent<PromiseDataWrapper> >& data) 102 #else 103 static void didRemovePromise(const v8::WeakCallbackData<v8::Object, PromiseDataWrapper>& data) 104 #endif 105 { 106 #if ENABLE(OILPAN) 107 OwnPtr<Persistent<PromiseDataWrapper> > persistentWrapper = adoptPtr(data.GetParameter()); 108 RawPtr<PromiseDataWrapper> wrapper = *persistentWrapper; 109 #else 110 OwnPtr<PromiseDataWrapper> wrapper = adoptPtr(data.GetParameter()); 111 #endif 112 WeakPtrWillBeRawPtr<PromiseTracker::PromiseData> promiseData = wrapper->m_data; 113 if (!promiseData || !wrapper->m_tracker) 114 return; 115 116 #if ENABLE(OILPAN) 117 // Oilpan: let go of ScopedPersistent<>s right here (and not wait until the 118 // PromiseDataWrapper is GCed later.) The v8 weak callback handling expects 119 // to see the callback data upon return. 120 promiseData->dispose(); 121 #endif 122 PromiseTracker::PromiseDataMap& map = wrapper->m_tracker->promiseDataMap(); 123 int promiseHash = promiseData->promiseHash(); 124 125 PromiseTracker::PromiseDataMap::iterator it = map.find(promiseHash); 126 // The PromiseTracker may have been disabled (and, possibly, re-enabled later), 127 // leaving the promiseHash as unmapped. 128 if (it == map.end()) 129 return; 130 131 PromiseTracker::PromiseDataVector* vector = &it->value; 132 int index = indexOf(vector, promiseData->promise()); 133 ASSERT(index >= 0); 134 vector->remove(index); 135 if (vector->isEmpty()) 136 map.remove(promiseHash); 137 } 138 139 void trace(Visitor* visitor) 140 { 141 #if ENABLE(OILPAN) 142 visitor->trace(m_data); 143 visitor->trace(m_tracker); 144 #endif 145 } 146 147 private: 148 PromiseDataWrapper(WeakPtrWillBeRawPtr<PromiseTracker::PromiseData> data, PromiseTracker* tracker) 149 : m_data(data) 150 , m_tracker(tracker) 151 { 152 } 153 154 WeakPtrWillBeWeakMember<PromiseTracker::PromiseData> m_data; 155 RawPtrWillBeWeakMember<PromiseTracker> m_tracker; 156 }; 157 158 } 159 160 PromiseTracker::PromiseTracker() 161 : m_circularSequentialId(0) 162 , m_isEnabled(false) 163 { 164 } 165 166 DEFINE_EMPTY_DESTRUCTOR_WILL_BE_REMOVED(PromiseTracker); 167 168 void PromiseTracker::trace(Visitor* visitor) 169 { 170 #if ENABLE(OILPAN) 171 visitor->trace(m_promiseDataMap); 172 #endif 173 } 174 175 void PromiseTracker::setEnabled(bool enabled) 176 { 177 m_isEnabled = enabled; 178 if (!enabled) 179 clear(); 180 } 181 182 void PromiseTracker::clear() 183 { 184 m_promiseDataMap.clear(); 185 } 186 187 int PromiseTracker::circularSequentialId() 188 { 189 ++m_circularSequentialId; 190 if (m_circularSequentialId <= 0) 191 m_circularSequentialId = 1; 192 return m_circularSequentialId; 193 } 194 195 PassRefPtrWillBeRawPtr<PromiseTracker::PromiseData> PromiseTracker::createPromiseDataIfNeeded(ScriptState* scriptState, v8::Handle<v8::Object> promise) 196 { 197 int promiseHash = promise->GetIdentityHash(); 198 RawPtr<PromiseDataVector> vector = nullptr; 199 PromiseDataMap::iterator it = m_promiseDataMap.find(promiseHash); 200 if (it != m_promiseDataMap.end()) 201 vector = &it->value; 202 else 203 vector = &m_promiseDataMap.add(promiseHash, PromiseDataVector()).storedValue->value; 204 205 int index = indexOf(vector, ScopedPersistent<v8::Object>(scriptState->isolate(), promise)); 206 if (index != -1) 207 return vector->at(index); 208 209 // FIXME: Consider using the ScriptState's DOMWrapperWorld instead 210 // to handle the lifetime of PromiseDataWrapper, avoiding all this 211 // manual labor to achieve the same, with and without Oilpan. 212 RefPtrWillBeRawPtr<PromiseData> data = PromiseData::create(scriptState, promiseHash, circularSequentialId(), promise); 213 OwnPtrWillBeRawPtr<PromiseDataWrapper> dataWrapper = PromiseDataWrapper::create(data.get(), this); 214 #if ENABLE(OILPAN) 215 OwnPtr<Persistent<PromiseDataWrapper> > wrapper = adoptPtr(new Persistent<PromiseDataWrapper>(dataWrapper)); 216 #else 217 OwnPtr<PromiseDataWrapper> wrapper = dataWrapper.release(); 218 #endif 219 data->m_promise.setWeak(wrapper.leakPtr(), &PromiseDataWrapper::didRemovePromise); 220 vector->append(data); 221 222 return data.release(); 223 } 224 225 void PromiseTracker::didReceiveV8PromiseEvent(ScriptState* scriptState, v8::Handle<v8::Object> promise, v8::Handle<v8::Value> parentPromise, int status) 226 { 227 ASSERT(isEnabled()); 228 229 RefPtrWillBeRawPtr<PromiseData> data = createPromiseDataIfNeeded(scriptState, promise); 230 if (!parentPromise.IsEmpty() && parentPromise->IsObject()) { 231 v8::Handle<v8::Object> handle = parentPromise->ToObject(); 232 RefPtrWillBeRawPtr<PromiseData> parentData = createPromiseDataIfNeeded(scriptState, handle); 233 data->m_parentPromiseId = parentData->m_promiseId; 234 data->m_parentPromise.set(scriptState->isolate(), handle); 235 } else { 236 data->m_status = status; 237 if (!status && !data->m_callStack) { 238 RefPtrWillBeRawPtr<ScriptCallStack> stack = createScriptCallStack(1, true); 239 if (stack && stack->size()) 240 data->m_callStack = stack; 241 } 242 } 243 } 244 245 PassRefPtr<Array<PromiseDetails> > PromiseTracker::promises() 246 { 247 ASSERT(isEnabled()); 248 249 RefPtr<Array<PromiseDetails> > result = Array<PromiseDetails>::create(); 250 for (PromiseDataMap::iterator it = m_promiseDataMap.begin(); it != m_promiseDataMap.end(); ++it) { 251 PromiseDataVector* vector = &it->value; 252 for (size_t index = 0; index < vector->size(); ++index) { 253 RefPtrWillBeRawPtr<PromiseData> data = vector->at(index); 254 PromiseDetails::Status::Enum status; 255 if (!data->m_status) 256 status = PromiseDetails::Status::Pending; 257 else if (data->m_status == 1) 258 status = PromiseDetails::Status::Resolved; 259 else 260 status = PromiseDetails::Status::Rejected; 261 RefPtr<PromiseDetails> promiseDetails = PromiseDetails::create() 262 .setId(data->m_promiseId) 263 .setStatus(status); 264 if (data->m_parentPromiseId) 265 promiseDetails->setParentId(data->m_parentPromiseId); 266 if (data->m_callStack) 267 promiseDetails->setCallFrame(data->m_callStack->at(0).buildInspectorObject()); 268 result->addItem(promiseDetails); 269 } 270 } 271 272 return result.release(); 273 } 274 275 ScriptValue PromiseTracker::promiseById(int promiseId) const 276 { 277 ASSERT(isEnabled()); 278 279 for (PromiseDataMap::const_iterator it = m_promiseDataMap.begin(); it != m_promiseDataMap.end(); ++it) { 280 const PromiseDataVector* vector = &it->value; 281 for (size_t index = 0; index < vector->size(); ++index) { 282 RefPtrWillBeRawPtr<PromiseData> data = vector->at(index); 283 if (data->m_promiseId == promiseId) { 284 ScriptState* scriptState = data->m_scriptState.get(); 285 return ScriptValue(scriptState, data->m_promise.newLocal(scriptState->isolate())); 286 } 287 } 288 } 289 290 return ScriptValue(); 291 } 292 293 } // namespace blink 294