• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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