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:
create(ScriptState * scriptState,int promiseHash,int promiseId,v8::Handle<v8::Object> promise)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
promiseHash() const28 int promiseHash() const { return m_promiseHash; }
promise()29 ScopedPersistent<v8::Object>& promise() { return m_promise; }
30
31 #if ENABLE(OILPAN)
dispose()32 void dispose()
33 {
34 m_promise.clear();
35 m_parentPromise.clear();
36 }
37 #else
createWeakPtr()38 WeakPtr<PromiseData> createWeakPtr()
39 {
40 return m_weakPtrFactory.createWeakPtr();
41 }
42 #endif
43
trace(Visitor * visitor)44 void trace(Visitor* visitor)
45 {
46 visitor->trace(m_callStack);
47 }
48
49 private:
50 friend class PromiseTracker;
51
PromiseData(ScriptState * scriptState,int promiseHash,int promiseId,v8::Handle<v8::Object> promise)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
indexOf(PromiseTracker::PromiseDataVector * vector,const ScopedPersistent<v8::Object> & promise)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:
create(PromiseTracker::PromiseData * data,PromiseTracker * tracker)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)
didRemovePromise(const v8::WeakCallbackData<v8::Object,Persistent<PromiseDataWrapper>> & data)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
trace(Visitor * visitor)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:
PromiseDataWrapper(WeakPtrWillBeRawPtr<PromiseTracker::PromiseData> data,PromiseTracker * tracker)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
PromiseTracker()160 PromiseTracker::PromiseTracker()
161 : m_circularSequentialId(0)
162 , m_isEnabled(false)
163 {
164 }
165
166 DEFINE_EMPTY_DESTRUCTOR_WILL_BE_REMOVED(PromiseTracker);
167
trace(Visitor * visitor)168 void PromiseTracker::trace(Visitor* visitor)
169 {
170 #if ENABLE(OILPAN)
171 visitor->trace(m_promiseDataMap);
172 #endif
173 }
174
setEnabled(bool enabled)175 void PromiseTracker::setEnabled(bool enabled)
176 {
177 m_isEnabled = enabled;
178 if (!enabled)
179 clear();
180 }
181
clear()182 void PromiseTracker::clear()
183 {
184 m_promiseDataMap.clear();
185 }
186
circularSequentialId()187 int PromiseTracker::circularSequentialId()
188 {
189 ++m_circularSequentialId;
190 if (m_circularSequentialId <= 0)
191 m_circularSequentialId = 1;
192 return m_circularSequentialId;
193 }
194
createPromiseDataIfNeeded(ScriptState * scriptState,v8::Handle<v8::Object> promise)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
didReceiveV8PromiseEvent(ScriptState * scriptState,v8::Handle<v8::Object> promise,v8::Handle<v8::Value> parentPromise,int status)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
promises()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
promiseById(int promiseId) const275 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