OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2013 Google Inc. All rights reserved. | 2 * Copyright (C) 2013 Google Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
6 * met: | 6 * met: |
7 * | 7 * |
8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
(...skipping 17 matching lines...) Expand all Loading... |
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
29 */ | 29 */ |
30 | 30 |
31 #include "config.h" | 31 #include "config.h" |
32 #include "core/inspector/AsyncCallStackTracker.h" | 32 #include "core/inspector/AsyncCallStackTracker.h" |
33 | 33 |
34 #include "core/dom/ContextLifecycleObserver.h" | 34 #include "core/dom/ContextLifecycleObserver.h" |
35 #include "core/dom/ExecutionContext.h" | 35 #include "core/dom/ExecutionContext.h" |
36 #include "core/events/EventTarget.h" | 36 #include "core/events/EventTarget.h" |
37 #include "core/events/RegisteredEventListener.h" | 37 #include "core/events/RegisteredEventListener.h" |
| 38 #include "core/xml/XMLHttpRequest.h" |
| 39 #include "core/xml/XMLHttpRequestUpload.h" |
| 40 #include "wtf/text/AtomicStringHash.h" |
38 #include "wtf/text/StringBuilder.h" | 41 #include "wtf/text/StringBuilder.h" |
39 | 42 |
| 43 namespace { |
| 44 |
| 45 static const char setTimeoutName[] = "setTimeout"; |
| 46 static const char setIntervalName[] = "setInterval"; |
| 47 static const char requestAnimationFrameName[] = "requestAnimationFrame"; |
| 48 static const char xhrSendName[] = "XMLHttpRequest.send"; |
| 49 |
| 50 } |
| 51 |
40 namespace WebCore { | 52 namespace WebCore { |
41 | 53 |
42 class AsyncCallStackTracker::ExecutionContextData : public ContextLifecycleObser
ver { | 54 class AsyncCallStackTracker::ExecutionContextData : public ContextLifecycleObser
ver { |
43 WTF_MAKE_FAST_ALLOCATED; | 55 WTF_MAKE_FAST_ALLOCATED; |
44 public: | 56 public: |
45 typedef std::pair<RegisteredEventListener, RefPtr<AsyncCallChain> > EventLis
tenerAsyncCallChain; | 57 typedef std::pair<RegisteredEventListener, RefPtr<AsyncCallChain> > EventLis
tenerAsyncCallChain; |
46 typedef Vector<EventListenerAsyncCallChain, 1> EventListenerAsyncCallChainVe
ctor; | 58 typedef Vector<EventListenerAsyncCallChain, 1> EventListenerAsyncCallChainVe
ctor; |
47 typedef HashMap<AtomicString, EventListenerAsyncCallChainVector> EventListen
erAsyncCallChainVectorHashMap; | 59 typedef HashMap<AtomicString, EventListenerAsyncCallChainVector> EventListen
erAsyncCallChainVectorHashMap; |
48 | 60 |
49 ExecutionContextData(AsyncCallStackTracker* tracker, ExecutionContext* execu
tionContext) | 61 ExecutionContextData(AsyncCallStackTracker* tracker, ExecutionContext* execu
tionContext) |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
104 } | 116 } |
105 return result.release(); | 117 return result.release(); |
106 } | 118 } |
107 | 119 |
108 public: | 120 public: |
109 AsyncCallStackTracker* m_tracker; | 121 AsyncCallStackTracker* m_tracker; |
110 HashSet<int> m_intervalTimerIds; | 122 HashSet<int> m_intervalTimerIds; |
111 HashMap<int, RefPtr<AsyncCallChain> > m_timerCallChains; | 123 HashMap<int, RefPtr<AsyncCallChain> > m_timerCallChains; |
112 HashMap<int, RefPtr<AsyncCallChain> > m_animationFrameCallChains; | 124 HashMap<int, RefPtr<AsyncCallChain> > m_animationFrameCallChains; |
113 HashMap<EventTarget*, EventListenerAsyncCallChainVectorHashMap> m_eventTarge
tCallChains; | 125 HashMap<EventTarget*, EventListenerAsyncCallChainVectorHashMap> m_eventTarge
tCallChains; |
| 126 HashMap<EventTarget*, RefPtr<AsyncCallChain> > m_xhrCallChains; |
114 }; | 127 }; |
115 | 128 |
| 129 static XMLHttpRequest* toXmlHttpRequest(EventTarget* eventTarget) |
| 130 { |
| 131 const AtomicString& interfaceName = eventTarget->interfaceName(); |
| 132 if (interfaceName == EventTargetNames::XMLHttpRequest) |
| 133 return static_cast<XMLHttpRequest*>(eventTarget); |
| 134 if (interfaceName == EventTargetNames::XMLHttpRequestUpload) |
| 135 return static_cast<XMLHttpRequestUpload*>(eventTarget)->xmlHttpRequest()
; |
| 136 return 0; |
| 137 } |
| 138 |
116 AsyncCallStackTracker::AsyncCallStack::AsyncCallStack(const String& description,
const ScriptValue& callFrames) | 139 AsyncCallStackTracker::AsyncCallStack::AsyncCallStack(const String& description,
const ScriptValue& callFrames) |
117 : m_description(description) | 140 : m_description(description) |
118 , m_callFrames(callFrames) | 141 , m_callFrames(callFrames) |
119 { | 142 { |
120 } | 143 } |
121 | 144 |
122 AsyncCallStackTracker::AsyncCallStack::~AsyncCallStack() | 145 AsyncCallStackTracker::AsyncCallStack::~AsyncCallStack() |
123 { | 146 { |
124 } | 147 } |
125 | 148 |
(...skipping 14 matching lines...) Expand all Loading... |
140 | 163 |
141 const AsyncCallStackTracker::AsyncCallChain* AsyncCallStackTracker::currentAsync
CallChain() const | 164 const AsyncCallStackTracker::AsyncCallChain* AsyncCallStackTracker::currentAsync
CallChain() const |
142 { | 165 { |
143 if (m_currentAsyncCallChain) | 166 if (m_currentAsyncCallChain) |
144 ensureMaxAsyncCallChainDepth(m_currentAsyncCallChain.get(), m_maxAsyncCa
llStackDepth); | 167 ensureMaxAsyncCallChainDepth(m_currentAsyncCallChain.get(), m_maxAsyncCa
llStackDepth); |
145 return m_currentAsyncCallChain.get(); | 168 return m_currentAsyncCallChain.get(); |
146 } | 169 } |
147 | 170 |
148 void AsyncCallStackTracker::didInstallTimer(ExecutionContext* context, int timer
Id, bool singleShot, const ScriptValue& callFrames) | 171 void AsyncCallStackTracker::didInstallTimer(ExecutionContext* context, int timer
Id, bool singleShot, const ScriptValue& callFrames) |
149 { | 172 { |
150 DEFINE_STATIC_LOCAL(String, setTimeoutName, ("setTimeout")); | |
151 DEFINE_STATIC_LOCAL(String, setIntervalName, ("setInterval")); | |
152 | |
153 ASSERT(context); | 173 ASSERT(context); |
154 ASSERT(isEnabled()); | 174 ASSERT(isEnabled()); |
155 if (!validateCallFrames(callFrames)) | 175 if (!validateCallFrames(callFrames)) |
156 return; | 176 return; |
157 ASSERT(timerId > 0); | 177 ASSERT(timerId > 0); |
158 ExecutionContextData* data = createContextDataIfNeeded(context); | 178 ExecutionContextData* data = createContextDataIfNeeded(context); |
159 data->m_timerCallChains.set(timerId, createAsyncCallChain(singleShot ? setTi
meoutName : setIntervalName, callFrames)); | 179 data->m_timerCallChains.set(timerId, createAsyncCallChain(singleShot ? setTi
meoutName : setIntervalName, callFrames)); |
160 if (!singleShot) | 180 if (!singleShot) |
161 data->m_intervalTimerIds.add(timerId); | 181 data->m_intervalTimerIds.add(timerId); |
162 } | 182 } |
(...skipping 10 matching lines...) Expand all Loading... |
173 data->m_intervalTimerIds.remove(timerId); | 193 data->m_intervalTimerIds.remove(timerId); |
174 data->m_timerCallChains.remove(timerId); | 194 data->m_timerCallChains.remove(timerId); |
175 } | 195 } |
176 | 196 |
177 void AsyncCallStackTracker::willFireTimer(ExecutionContext* context, int timerId
) | 197 void AsyncCallStackTracker::willFireTimer(ExecutionContext* context, int timerId
) |
178 { | 198 { |
179 ASSERT(context); | 199 ASSERT(context); |
180 ASSERT(isEnabled()); | 200 ASSERT(isEnabled()); |
181 ASSERT(timerId > 0); | 201 ASSERT(timerId > 0); |
182 ASSERT(!m_currentAsyncCallChain); | 202 ASSERT(!m_currentAsyncCallChain); |
183 ExecutionContextData* data = m_executionContextDataMap.get(context); | 203 if (ExecutionContextData* data = m_executionContextDataMap.get(context)) { |
184 if (!data) | 204 if (data->m_intervalTimerIds.contains(timerId)) |
185 return; | 205 setCurrentAsyncCallChain(data->m_timerCallChains.get(timerId)); |
186 if (data->m_intervalTimerIds.contains(timerId)) | 206 else |
187 setCurrentAsyncCallChain(data->m_timerCallChains.get(timerId)); | 207 setCurrentAsyncCallChain(data->m_timerCallChains.take(timerId)); |
188 else | 208 } else { |
189 setCurrentAsyncCallChain(data->m_timerCallChains.take(timerId)); | 209 setCurrentAsyncCallChain(0); |
| 210 } |
190 } | 211 } |
191 | 212 |
192 void AsyncCallStackTracker::didRequestAnimationFrame(ExecutionContext* context,
int callbackId, const ScriptValue& callFrames) | 213 void AsyncCallStackTracker::didRequestAnimationFrame(ExecutionContext* context,
int callbackId, const ScriptValue& callFrames) |
193 { | 214 { |
194 DEFINE_STATIC_LOCAL(String, requestAnimationFrameName, ("requestAnimationFra
me")); | |
195 | |
196 ASSERT(context); | 215 ASSERT(context); |
197 ASSERT(isEnabled()); | 216 ASSERT(isEnabled()); |
198 if (!validateCallFrames(callFrames)) | 217 if (!validateCallFrames(callFrames)) |
199 return; | 218 return; |
200 ASSERT(callbackId > 0); | 219 ASSERT(callbackId > 0); |
201 ExecutionContextData* data = createContextDataIfNeeded(context); | 220 ExecutionContextData* data = createContextDataIfNeeded(context); |
202 data->m_animationFrameCallChains.set(callbackId, createAsyncCallChain(reques
tAnimationFrameName, callFrames)); | 221 data->m_animationFrameCallChains.set(callbackId, createAsyncCallChain(reques
tAnimationFrameName, callFrames)); |
203 } | 222 } |
204 | 223 |
205 void AsyncCallStackTracker::didCancelAnimationFrame(ExecutionContext* context, i
nt callbackId) | 224 void AsyncCallStackTracker::didCancelAnimationFrame(ExecutionContext* context, i
nt callbackId) |
206 { | 225 { |
207 ASSERT(context); | 226 ASSERT(context); |
208 ASSERT(isEnabled()); | 227 ASSERT(isEnabled()); |
209 if (callbackId <= 0) | 228 if (callbackId <= 0) |
210 return; | 229 return; |
211 if (ExecutionContextData* data = m_executionContextDataMap.get(context)) | 230 if (ExecutionContextData* data = m_executionContextDataMap.get(context)) |
212 data->m_animationFrameCallChains.remove(callbackId); | 231 data->m_animationFrameCallChains.remove(callbackId); |
213 } | 232 } |
214 | 233 |
215 void AsyncCallStackTracker::willFireAnimationFrame(ExecutionContext* context, in
t callbackId) | 234 void AsyncCallStackTracker::willFireAnimationFrame(ExecutionContext* context, in
t callbackId) |
216 { | 235 { |
217 ASSERT(context); | 236 ASSERT(context); |
218 ASSERT(isEnabled()); | 237 ASSERT(isEnabled()); |
219 ASSERT(callbackId > 0); | 238 ASSERT(callbackId > 0); |
220 ASSERT(!m_currentAsyncCallChain); | 239 ASSERT(!m_currentAsyncCallChain); |
221 if (ExecutionContextData* data = m_executionContextDataMap.get(context)) | 240 if (ExecutionContextData* data = m_executionContextDataMap.get(context)) |
222 setCurrentAsyncCallChain(data->m_animationFrameCallChains.take(callbackI
d)); | 241 setCurrentAsyncCallChain(data->m_animationFrameCallChains.take(callbackI
d)); |
| 242 else |
| 243 setCurrentAsyncCallChain(0); |
223 } | 244 } |
224 | 245 |
225 void AsyncCallStackTracker::didAddEventListener(EventTarget* eventTarget, const
AtomicString& eventType, EventListener* listener, bool useCapture, const ScriptV
alue& callFrames) | 246 void AsyncCallStackTracker::didAddEventListener(EventTarget* eventTarget, const
AtomicString& eventType, EventListener* listener, bool useCapture, const ScriptV
alue& callFrames) |
226 { | 247 { |
227 ASSERT(eventTarget->executionContext()); | 248 ASSERT(eventTarget->executionContext()); |
228 ASSERT(isEnabled()); | 249 ASSERT(isEnabled()); |
229 if (!validateCallFrames(callFrames)) | 250 if (!validateCallFrames(callFrames) || toXmlHttpRequest(eventTarget)) |
230 return; | 251 return; |
231 | 252 |
232 StringBuilder description; | 253 StringBuilder description; |
233 description.append(eventTarget->interfaceName()); | 254 description.append(eventTarget->interfaceName()); |
234 if (!description.isEmpty()) | 255 if (!description.isEmpty()) |
235 description.append("."); | 256 description.append("."); |
236 if (listener->isAttribute()) { | 257 if (listener->isAttribute()) { |
237 description.append("on"); | 258 description.append("on"); |
238 description.append(eventType); | 259 description.append(eventType); |
239 } else { | 260 } else { |
(...skipping 19 matching lines...) Expand all Loading... |
259 ASSERT(eventTarget->executionContext()); | 280 ASSERT(eventTarget->executionContext()); |
260 ASSERT(isEnabled()); | 281 ASSERT(isEnabled()); |
261 if (ExecutionContextData* data = m_executionContextDataMap.get(eventTarget->
executionContext())) | 282 if (ExecutionContextData* data = m_executionContextDataMap.get(eventTarget->
executionContext())) |
262 data->m_eventTargetCallChains.remove(eventTarget); | 283 data->m_eventTargetCallChains.remove(eventTarget); |
263 } | 284 } |
264 | 285 |
265 void AsyncCallStackTracker::willHandleEvent(EventTarget* eventTarget, const Atom
icString& eventType, EventListener* listener, bool useCapture) | 286 void AsyncCallStackTracker::willHandleEvent(EventTarget* eventTarget, const Atom
icString& eventType, EventListener* listener, bool useCapture) |
266 { | 287 { |
267 ASSERT(eventTarget->executionContext()); | 288 ASSERT(eventTarget->executionContext()); |
268 ASSERT(isEnabled()); | 289 ASSERT(isEnabled()); |
| 290 if (XMLHttpRequest* xhr = toXmlHttpRequest(eventTarget)) { |
| 291 willHandleXHREvent(xhr, eventTarget, eventType); |
| 292 return; |
| 293 } |
269 if (ExecutionContextData* data = m_executionContextDataMap.get(eventTarget->
executionContext())) | 294 if (ExecutionContextData* data = m_executionContextDataMap.get(eventTarget->
executionContext())) |
270 setCurrentAsyncCallChain(data->findEventListenerData(eventTarget, eventT
ype, RegisteredEventListener(listener, useCapture))); | 295 setCurrentAsyncCallChain(data->findEventListenerData(eventTarget, eventT
ype, RegisteredEventListener(listener, useCapture))); |
| 296 else |
| 297 setCurrentAsyncCallChain(0); |
| 298 } |
| 299 |
| 300 void AsyncCallStackTracker::willLoadXHR(XMLHttpRequest* xhr, const ScriptValue&
callFrames) |
| 301 { |
| 302 ASSERT(xhr->executionContext()); |
| 303 ASSERT(isEnabled()); |
| 304 if (!validateCallFrames(callFrames)) |
| 305 return; |
| 306 ExecutionContextData* data = createContextDataIfNeeded(xhr->executionContext
()); |
| 307 data->m_xhrCallChains.set(xhr, createAsyncCallChain(xhrSendName, callFrames)
); |
| 308 } |
| 309 |
| 310 void AsyncCallStackTracker::willHandleXHREvent(XMLHttpRequest* xhr, EventTarget*
eventTarget, const AtomicString& eventType) |
| 311 { |
| 312 ASSERT(xhr->executionContext()); |
| 313 ASSERT(isEnabled()); |
| 314 if (ExecutionContextData* data = m_executionContextDataMap.get(xhr->executio
nContext())) { |
| 315 bool isXHRDownload = (xhr == eventTarget); |
| 316 if (isXHRDownload && eventType == EventTypeNames::loadend) |
| 317 setCurrentAsyncCallChain(data->m_xhrCallChains.take(xhr)); |
| 318 else |
| 319 setCurrentAsyncCallChain(data->m_xhrCallChains.get(xhr)); |
| 320 } else { |
| 321 setCurrentAsyncCallChain(0); |
| 322 } |
271 } | 323 } |
272 | 324 |
273 void AsyncCallStackTracker::didFireAsyncCall() | 325 void AsyncCallStackTracker::didFireAsyncCall() |
274 { | 326 { |
275 setCurrentAsyncCallChain(0); | 327 clearCurrentAsyncCallChain(); |
276 } | 328 } |
277 | 329 |
278 PassRefPtr<AsyncCallStackTracker::AsyncCallChain> AsyncCallStackTracker::createA
syncCallChain(const String& description, const ScriptValue& callFrames) | 330 PassRefPtr<AsyncCallStackTracker::AsyncCallChain> AsyncCallStackTracker::createA
syncCallChain(const String& description, const ScriptValue& callFrames) |
279 { | 331 { |
280 RefPtr<AsyncCallChain> chain = adoptRef(m_currentAsyncCallChain ? new AsyncC
allStackTracker::AsyncCallChain(*m_currentAsyncCallChain) : new AsyncCallStackTr
acker::AsyncCallChain()); | 332 RefPtr<AsyncCallChain> chain = adoptRef(m_currentAsyncCallChain ? new AsyncC
allStackTracker::AsyncCallChain(*m_currentAsyncCallChain) : new AsyncCallStackTr
acker::AsyncCallChain()); |
281 ensureMaxAsyncCallChainDepth(chain.get(), m_maxAsyncCallStackDepth - 1); | 333 ensureMaxAsyncCallChainDepth(chain.get(), m_maxAsyncCallStackDepth - 1); |
282 chain->m_callStacks.prepend(adoptRef(new AsyncCallStackTracker::AsyncCallSta
ck(description, callFrames))); | 334 chain->m_callStacks.prepend(adoptRef(new AsyncCallStackTracker::AsyncCallSta
ck(description, callFrames))); |
283 return chain.release(); | 335 return chain.release(); |
284 } | 336 } |
285 | 337 |
286 void AsyncCallStackTracker::setCurrentAsyncCallChain(PassRefPtr<AsyncCallChain>
chain) | 338 void AsyncCallStackTracker::setCurrentAsyncCallChain(PassRefPtr<AsyncCallChain>
chain) |
287 { | 339 { |
288 if (m_currentAsyncCallChain) { | 340 if (m_currentAsyncCallChain) { |
289 m_nestedAsyncCallCount += chain ? 1 : -1; | 341 ++m_nestedAsyncCallCount; |
290 if (!m_nestedAsyncCallCount) | |
291 m_currentAsyncCallChain = 0; | |
292 } else if (chain) { | 342 } else if (chain) { |
293 m_currentAsyncCallChain = chain; | 343 m_currentAsyncCallChain = chain; |
294 m_nestedAsyncCallCount = 1; | 344 m_nestedAsyncCallCount = 1; |
295 } | 345 } |
296 } | 346 } |
297 | 347 |
| 348 void AsyncCallStackTracker::clearCurrentAsyncCallChain() |
| 349 { |
| 350 if (!m_nestedAsyncCallCount) |
| 351 return; |
| 352 --m_nestedAsyncCallCount; |
| 353 if (!m_nestedAsyncCallCount) |
| 354 m_currentAsyncCallChain.clear(); |
| 355 } |
| 356 |
298 void AsyncCallStackTracker::ensureMaxAsyncCallChainDepth(AsyncCallChain* chain,
unsigned maxDepth) | 357 void AsyncCallStackTracker::ensureMaxAsyncCallChainDepth(AsyncCallChain* chain,
unsigned maxDepth) |
299 { | 358 { |
300 while (chain->m_callStacks.size() > maxDepth) | 359 while (chain->m_callStacks.size() > maxDepth) |
301 chain->m_callStacks.removeLast(); | 360 chain->m_callStacks.removeLast(); |
302 } | 361 } |
303 | 362 |
304 bool AsyncCallStackTracker::validateCallFrames(const ScriptValue& callFrames) | 363 bool AsyncCallStackTracker::validateCallFrames(const ScriptValue& callFrames) |
305 { | 364 { |
306 return !callFrames.hasNoValue(); | 365 return !callFrames.hasNoValue(); |
307 } | 366 } |
308 | 367 |
309 AsyncCallStackTracker::ExecutionContextData* AsyncCallStackTracker::createContex
tDataIfNeeded(ExecutionContext* context) | 368 AsyncCallStackTracker::ExecutionContextData* AsyncCallStackTracker::createContex
tDataIfNeeded(ExecutionContext* context) |
310 { | 369 { |
311 ExecutionContextData* data = m_executionContextDataMap.get(context); | 370 ExecutionContextData* data = m_executionContextDataMap.get(context); |
312 if (!data) { | 371 if (!data) { |
313 data = new AsyncCallStackTracker::ExecutionContextData(this, context); | 372 data = new AsyncCallStackTracker::ExecutionContextData(this, context); |
314 m_executionContextDataMap.set(context, data); | 373 m_executionContextDataMap.set(context, data); |
315 } | 374 } |
316 return data; | 375 return data; |
317 } | 376 } |
318 | 377 |
319 void AsyncCallStackTracker::clear() | 378 void AsyncCallStackTracker::clear() |
320 { | 379 { |
321 m_currentAsyncCallChain = 0; | 380 m_currentAsyncCallChain.clear(); |
322 m_nestedAsyncCallCount = 0; | 381 m_nestedAsyncCallCount = 0; |
323 ExecutionContextDataMap copy; | 382 ExecutionContextDataMap copy; |
324 m_executionContextDataMap.swap(copy); | 383 m_executionContextDataMap.swap(copy); |
325 for (ExecutionContextDataMap::const_iterator it = copy.begin(); it != copy.e
nd(); ++it) | 384 for (ExecutionContextDataMap::const_iterator it = copy.begin(); it != copy.e
nd(); ++it) |
326 delete it->value; | 385 delete it->value; |
327 } | 386 } |
328 | 387 |
329 } // namespace WebCore | 388 } // namespace WebCore |
OLD | NEW |