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 15using blink::TypeBuilder::Array; 16using blink::TypeBuilder::Console::CallFrame; 17using blink::TypeBuilder::Debugger::PromiseDetails; 18 19namespace blink { 20 21class PromiseTracker::PromiseData FINAL : public RefCountedWillBeGarbageCollectedFinalized<PromiseData> { 22public: 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 49private: 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 78static 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 87namespace { 88 89class PromiseDataWrapper FINAL : public NoBaseWillBeGarbageCollected<PromiseDataWrapper> { 90public: 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 147private: 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 160PromiseTracker::PromiseTracker() 161 : m_circularSequentialId(0) 162 , m_isEnabled(false) 163{ 164} 165 166DEFINE_EMPTY_DESTRUCTOR_WILL_BE_REMOVED(PromiseTracker); 167 168void PromiseTracker::trace(Visitor* visitor) 169{ 170#if ENABLE(OILPAN) 171 visitor->trace(m_promiseDataMap); 172#endif 173} 174 175void PromiseTracker::setEnabled(bool enabled) 176{ 177 m_isEnabled = enabled; 178 if (!enabled) 179 clear(); 180} 181 182void PromiseTracker::clear() 183{ 184 m_promiseDataMap.clear(); 185} 186 187int PromiseTracker::circularSequentialId() 188{ 189 ++m_circularSequentialId; 190 if (m_circularSequentialId <= 0) 191 m_circularSequentialId = 1; 192 return m_circularSequentialId; 193} 194 195PassRefPtrWillBeRawPtr<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 225void 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 245PassRefPtr<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 275ScriptValue 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