Chromium Code Reviews| 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 |
| 40 namespace WebCore { | 43 namespace WebCore { |
| 41 | 44 |
| 42 class AsyncCallStackTracker::ExecutionContextData : public ContextLifecycleObser ver { | 45 class AsyncCallStackTracker::ExecutionContextData : public ContextLifecycleObser ver { |
| 43 WTF_MAKE_FAST_ALLOCATED; | 46 WTF_MAKE_FAST_ALLOCATED; |
| 44 public: | 47 public: |
| 45 typedef std::pair<RegisteredEventListener, RefPtr<AsyncCallChain> > EventLis tenerAsyncCallChain; | 48 typedef std::pair<RegisteredEventListener, RefPtr<AsyncCallChain> > EventLis tenerAsyncCallChain; |
| 46 typedef Vector<EventListenerAsyncCallChain, 1> EventListenerAsyncCallChainVe ctor; | 49 typedef Vector<EventListenerAsyncCallChain, 1> EventListenerAsyncCallChainVe ctor; |
| 47 typedef HashMap<AtomicString, EventListenerAsyncCallChainVector> EventListen erAsyncCallChainVectorHashMap; | 50 typedef HashMap<AtomicString, EventListenerAsyncCallChainVector> EventListen erAsyncCallChainVectorHashMap; |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 104 } | 107 } |
| 105 return result.release(); | 108 return result.release(); |
| 106 } | 109 } |
| 107 | 110 |
| 108 public: | 111 public: |
| 109 AsyncCallStackTracker* m_tracker; | 112 AsyncCallStackTracker* m_tracker; |
| 110 HashSet<int> m_intervalTimerIds; | 113 HashSet<int> m_intervalTimerIds; |
| 111 HashMap<int, RefPtr<AsyncCallChain> > m_timerCallChains; | 114 HashMap<int, RefPtr<AsyncCallChain> > m_timerCallChains; |
| 112 HashMap<int, RefPtr<AsyncCallChain> > m_animationFrameCallChains; | 115 HashMap<int, RefPtr<AsyncCallChain> > m_animationFrameCallChains; |
| 113 HashMap<EventTarget*, EventListenerAsyncCallChainVectorHashMap> m_eventTarge tCallChains; | 116 HashMap<EventTarget*, EventListenerAsyncCallChainVectorHashMap> m_eventTarge tCallChains; |
| 117 HashMap<EventTarget*, RefPtr<AsyncCallChain> > m_xhrCallChains; | |
| 114 }; | 118 }; |
| 115 | 119 |
| 120 static XMLHttpRequest* xmlHttpRequestFor(EventTarget* eventTarget) | |
|
yurys
2013/12/23 09:11:57
toXmlHttpRequest ?
aandrey
2013/12/23 12:09:38
Done.
| |
| 121 { | |
| 122 const AtomicString& interfaceName = eventTarget->interfaceName(); | |
| 123 if (interfaceName == EventTargetNames::XMLHttpRequest) | |
| 124 return static_cast<XMLHttpRequest*>(eventTarget); | |
| 125 if (interfaceName == EventTargetNames::XMLHttpRequestUpload) | |
| 126 return static_cast<XMLHttpRequestUpload*>(eventTarget)->xmlHttpRequest() ; | |
| 127 return 0; | |
| 128 } | |
| 129 | |
| 116 AsyncCallStackTracker::AsyncCallStack::AsyncCallStack(const String& description, const ScriptValue& callFrames) | 130 AsyncCallStackTracker::AsyncCallStack::AsyncCallStack(const String& description, const ScriptValue& callFrames) |
| 117 : m_description(description) | 131 : m_description(description) |
| 118 , m_callFrames(callFrames) | 132 , m_callFrames(callFrames) |
| 119 { | 133 { |
| 120 } | 134 } |
| 121 | 135 |
| 122 AsyncCallStackTracker::AsyncCallStack::~AsyncCallStack() | 136 AsyncCallStackTracker::AsyncCallStack::~AsyncCallStack() |
| 123 { | 137 { |
| 124 } | 138 } |
| 125 | 139 |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 173 data->m_intervalTimerIds.remove(timerId); | 187 data->m_intervalTimerIds.remove(timerId); |
| 174 data->m_timerCallChains.remove(timerId); | 188 data->m_timerCallChains.remove(timerId); |
| 175 } | 189 } |
| 176 | 190 |
| 177 void AsyncCallStackTracker::willFireTimer(ExecutionContext* context, int timerId ) | 191 void AsyncCallStackTracker::willFireTimer(ExecutionContext* context, int timerId ) |
| 178 { | 192 { |
| 179 ASSERT(context); | 193 ASSERT(context); |
| 180 ASSERT(isEnabled()); | 194 ASSERT(isEnabled()); |
| 181 ASSERT(timerId > 0); | 195 ASSERT(timerId > 0); |
| 182 ASSERT(!m_currentAsyncCallChain); | 196 ASSERT(!m_currentAsyncCallChain); |
| 183 ExecutionContextData* data = m_executionContextDataMap.get(context); | 197 if (ExecutionContextData* data = m_executionContextDataMap.get(context)) { |
| 184 if (!data) | 198 if (data->m_intervalTimerIds.contains(timerId)) |
| 185 return; | 199 setCurrentAsyncCallChain(data->m_timerCallChains.get(timerId)); |
| 186 if (data->m_intervalTimerIds.contains(timerId)) | 200 else |
| 187 setCurrentAsyncCallChain(data->m_timerCallChains.get(timerId)); | 201 setCurrentAsyncCallChain(data->m_timerCallChains.take(timerId)); |
| 188 else | 202 } else { |
| 189 setCurrentAsyncCallChain(data->m_timerCallChains.take(timerId)); | 203 setCurrentAsyncCallChain(0); |
|
yurys
2013/12/23 09:11:57
Can we test this branch?
aandrey
2013/12/23 12:09:38
Added new test: LayoutTests/inspector/debugger/asy
yurys
2013/12/23 12:40:31
Thank you!
| |
| 204 } | |
| 190 } | 205 } |
| 191 | 206 |
| 192 void AsyncCallStackTracker::didRequestAnimationFrame(ExecutionContext* context, int callbackId, const ScriptValue& callFrames) | 207 void AsyncCallStackTracker::didRequestAnimationFrame(ExecutionContext* context, int callbackId, const ScriptValue& callFrames) |
| 193 { | 208 { |
| 194 DEFINE_STATIC_LOCAL(String, requestAnimationFrameName, ("requestAnimationFra me")); | 209 DEFINE_STATIC_LOCAL(String, requestAnimationFrameName, ("requestAnimationFra me")); |
| 195 | 210 |
| 196 ASSERT(context); | 211 ASSERT(context); |
| 197 ASSERT(isEnabled()); | 212 ASSERT(isEnabled()); |
| 198 if (!validateCallFrames(callFrames)) | 213 if (!validateCallFrames(callFrames)) |
| 199 return; | 214 return; |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 213 } | 228 } |
| 214 | 229 |
| 215 void AsyncCallStackTracker::willFireAnimationFrame(ExecutionContext* context, in t callbackId) | 230 void AsyncCallStackTracker::willFireAnimationFrame(ExecutionContext* context, in t callbackId) |
| 216 { | 231 { |
| 217 ASSERT(context); | 232 ASSERT(context); |
| 218 ASSERT(isEnabled()); | 233 ASSERT(isEnabled()); |
| 219 ASSERT(callbackId > 0); | 234 ASSERT(callbackId > 0); |
| 220 ASSERT(!m_currentAsyncCallChain); | 235 ASSERT(!m_currentAsyncCallChain); |
| 221 if (ExecutionContextData* data = m_executionContextDataMap.get(context)) | 236 if (ExecutionContextData* data = m_executionContextDataMap.get(context)) |
| 222 setCurrentAsyncCallChain(data->m_animationFrameCallChains.take(callbackI d)); | 237 setCurrentAsyncCallChain(data->m_animationFrameCallChains.take(callbackI d)); |
| 238 else | |
| 239 setCurrentAsyncCallChain(0); | |
| 223 } | 240 } |
| 224 | 241 |
| 225 void AsyncCallStackTracker::didAddEventListener(EventTarget* eventTarget, const AtomicString& eventType, EventListener* listener, bool useCapture, const ScriptV alue& callFrames) | 242 void AsyncCallStackTracker::didAddEventListener(EventTarget* eventTarget, const AtomicString& eventType, EventListener* listener, bool useCapture, const ScriptV alue& callFrames) |
| 226 { | 243 { |
| 227 ASSERT(eventTarget->executionContext()); | 244 ASSERT(eventTarget->executionContext()); |
| 228 ASSERT(isEnabled()); | 245 ASSERT(isEnabled()); |
| 229 if (!validateCallFrames(callFrames)) | 246 if (!validateCallFrames(callFrames) || xmlHttpRequestFor(eventTarget)) |
| 230 return; | 247 return; |
| 231 | 248 |
| 232 StringBuilder description; | 249 StringBuilder description; |
| 233 description.append(eventTarget->interfaceName()); | 250 description.append(eventTarget->interfaceName()); |
| 234 if (!description.isEmpty()) | 251 if (!description.isEmpty()) |
| 235 description.append("."); | 252 description.append("."); |
| 236 if (listener->isAttribute()) { | 253 if (listener->isAttribute()) { |
| 237 description.append("on"); | 254 description.append("on"); |
| 238 description.append(eventType); | 255 description.append(eventType); |
| 239 } else { | 256 } else { |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 259 ASSERT(eventTarget->executionContext()); | 276 ASSERT(eventTarget->executionContext()); |
| 260 ASSERT(isEnabled()); | 277 ASSERT(isEnabled()); |
| 261 if (ExecutionContextData* data = m_executionContextDataMap.get(eventTarget-> executionContext())) | 278 if (ExecutionContextData* data = m_executionContextDataMap.get(eventTarget-> executionContext())) |
| 262 data->m_eventTargetCallChains.remove(eventTarget); | 279 data->m_eventTargetCallChains.remove(eventTarget); |
| 263 } | 280 } |
| 264 | 281 |
| 265 void AsyncCallStackTracker::willHandleEvent(EventTarget* eventTarget, const Atom icString& eventType, EventListener* listener, bool useCapture) | 282 void AsyncCallStackTracker::willHandleEvent(EventTarget* eventTarget, const Atom icString& eventType, EventListener* listener, bool useCapture) |
| 266 { | 283 { |
| 267 ASSERT(eventTarget->executionContext()); | 284 ASSERT(eventTarget->executionContext()); |
| 268 ASSERT(isEnabled()); | 285 ASSERT(isEnabled()); |
| 286 if (XMLHttpRequest* xhr = xmlHttpRequestFor(eventTarget)) { | |
| 287 willHandleXHREvent(xhr, eventTarget, eventType); | |
| 288 return; | |
| 289 } | |
| 269 if (ExecutionContextData* data = m_executionContextDataMap.get(eventTarget-> executionContext())) | 290 if (ExecutionContextData* data = m_executionContextDataMap.get(eventTarget-> executionContext())) |
| 270 setCurrentAsyncCallChain(data->findEventListenerData(eventTarget, eventT ype, RegisteredEventListener(listener, useCapture))); | 291 setCurrentAsyncCallChain(data->findEventListenerData(eventTarget, eventT ype, RegisteredEventListener(listener, useCapture))); |
| 292 else | |
| 293 setCurrentAsyncCallChain(0); | |
| 294 } | |
| 295 | |
| 296 void AsyncCallStackTracker::willLoadXHR(XMLHttpRequest* xhr, const ScriptValue& callFrames) | |
| 297 { | |
| 298 DEFINE_STATIC_LOCAL(String, xhrSendName, ("XMLHttpRequest.send")); | |
|
yurys
2013/12/23 09:11:57
This is not thread-safe and may be called on Worke
aandrey
2013/12/23 12:09:38
Done. I also removed other DEFINE_STATIC_LOCAL's
| |
| 299 | |
| 300 ASSERT(xhr->executionContext()); | |
| 301 ASSERT(isEnabled()); | |
| 302 if (!validateCallFrames(callFrames)) | |
| 303 return; | |
| 304 ExecutionContextData* data = createContextDataIfNeeded(xhr->executionContext ()); | |
| 305 data->m_xhrCallChains.set(xhr, createAsyncCallChain(xhrSendName, callFrames) ); | |
| 306 } | |
| 307 | |
| 308 void AsyncCallStackTracker::willHandleXHREvent(XMLHttpRequest* xhr, EventTarget* eventTarget, const AtomicString& eventType) | |
| 309 { | |
| 310 ASSERT(xhr->executionContext()); | |
| 311 ASSERT(isEnabled()); | |
| 312 if (ExecutionContextData* data = m_executionContextDataMap.get(xhr->executio nContext())) { | |
| 313 if (xhr == eventTarget && eventType == EventTypeNames::loadend) | |
|
yurys
2013/12/23 09:11:57
What is xhr == eventTarget? Is it check for upload
aandrey
2013/12/23 12:09:38
Yes, essentially.
The "loadend" on XHR is the corr
yurys
2013/12/23 12:40:31
Consider assigning it to a named variable like isU
aandrey
2013/12/23 13:01:29
Assigned to isXHRDownload.
| |
| 314 setCurrentAsyncCallChain(data->m_xhrCallChains.take(xhr)); | |
| 315 else | |
| 316 setCurrentAsyncCallChain(data->m_xhrCallChains.get(xhr)); | |
| 317 } else { | |
| 318 setCurrentAsyncCallChain(0); | |
| 319 } | |
| 271 } | 320 } |
| 272 | 321 |
| 273 void AsyncCallStackTracker::didFireAsyncCall() | 322 void AsyncCallStackTracker::didFireAsyncCall() |
| 274 { | 323 { |
| 275 setCurrentAsyncCallChain(0); | 324 clearCurrentAsyncCallChain(); |
| 276 } | 325 } |
| 277 | 326 |
| 278 PassRefPtr<AsyncCallStackTracker::AsyncCallChain> AsyncCallStackTracker::createA syncCallChain(const String& description, const ScriptValue& callFrames) | 327 PassRefPtr<AsyncCallStackTracker::AsyncCallChain> AsyncCallStackTracker::createA syncCallChain(const String& description, const ScriptValue& callFrames) |
| 279 { | 328 { |
| 280 RefPtr<AsyncCallChain> chain = adoptRef(m_currentAsyncCallChain ? new AsyncC allStackTracker::AsyncCallChain(*m_currentAsyncCallChain) : new AsyncCallStackTr acker::AsyncCallChain()); | 329 RefPtr<AsyncCallChain> chain = adoptRef(m_currentAsyncCallChain ? new AsyncC allStackTracker::AsyncCallChain(*m_currentAsyncCallChain) : new AsyncCallStackTr acker::AsyncCallChain()); |
| 281 ensureMaxAsyncCallChainDepth(chain.get(), m_maxAsyncCallStackDepth - 1); | 330 ensureMaxAsyncCallChainDepth(chain.get(), m_maxAsyncCallStackDepth - 1); |
| 282 chain->m_callStacks.prepend(adoptRef(new AsyncCallStackTracker::AsyncCallSta ck(description, callFrames))); | 331 chain->m_callStacks.prepend(adoptRef(new AsyncCallStackTracker::AsyncCallSta ck(description, callFrames))); |
| 283 return chain.release(); | 332 return chain.release(); |
| 284 } | 333 } |
| 285 | 334 |
| 286 void AsyncCallStackTracker::setCurrentAsyncCallChain(PassRefPtr<AsyncCallChain> chain) | 335 void AsyncCallStackTracker::setCurrentAsyncCallChain(PassRefPtr<AsyncCallChain> chain) |
| 287 { | 336 { |
| 288 if (m_currentAsyncCallChain) { | 337 if (m_currentAsyncCallChain) { |
| 289 m_nestedAsyncCallCount += chain ? 1 : -1; | 338 ++m_nestedAsyncCallCount; |
| 290 if (!m_nestedAsyncCallCount) | |
| 291 m_currentAsyncCallChain = 0; | |
| 292 } else if (chain) { | 339 } else if (chain) { |
| 293 m_currentAsyncCallChain = chain; | 340 m_currentAsyncCallChain = chain; |
| 294 m_nestedAsyncCallCount = 1; | 341 m_nestedAsyncCallCount = 1; |
| 295 } | 342 } |
| 296 } | 343 } |
| 297 | 344 |
| 345 void AsyncCallStackTracker::clearCurrentAsyncCallChain() | |
| 346 { | |
| 347 if (!m_nestedAsyncCallCount) | |
| 348 return; | |
| 349 --m_nestedAsyncCallCount; | |
| 350 if (!m_nestedAsyncCallCount) | |
| 351 m_currentAsyncCallChain.clear(); | |
| 352 } | |
| 353 | |
| 298 void AsyncCallStackTracker::ensureMaxAsyncCallChainDepth(AsyncCallChain* chain, unsigned maxDepth) | 354 void AsyncCallStackTracker::ensureMaxAsyncCallChainDepth(AsyncCallChain* chain, unsigned maxDepth) |
| 299 { | 355 { |
| 300 while (chain->m_callStacks.size() > maxDepth) | 356 while (chain->m_callStacks.size() > maxDepth) |
| 301 chain->m_callStacks.removeLast(); | 357 chain->m_callStacks.removeLast(); |
| 302 } | 358 } |
| 303 | 359 |
| 304 bool AsyncCallStackTracker::validateCallFrames(const ScriptValue& callFrames) | 360 bool AsyncCallStackTracker::validateCallFrames(const ScriptValue& callFrames) |
| 305 { | 361 { |
| 306 return !callFrames.hasNoValue(); | 362 return !callFrames.hasNoValue(); |
| 307 } | 363 } |
| 308 | 364 |
| 309 AsyncCallStackTracker::ExecutionContextData* AsyncCallStackTracker::createContex tDataIfNeeded(ExecutionContext* context) | 365 AsyncCallStackTracker::ExecutionContextData* AsyncCallStackTracker::createContex tDataIfNeeded(ExecutionContext* context) |
| 310 { | 366 { |
| 311 ExecutionContextData* data = m_executionContextDataMap.get(context); | 367 ExecutionContextData* data = m_executionContextDataMap.get(context); |
| 312 if (!data) { | 368 if (!data) { |
| 313 data = new AsyncCallStackTracker::ExecutionContextData(this, context); | 369 data = new AsyncCallStackTracker::ExecutionContextData(this, context); |
| 314 m_executionContextDataMap.set(context, data); | 370 m_executionContextDataMap.set(context, data); |
| 315 } | 371 } |
| 316 return data; | 372 return data; |
| 317 } | 373 } |
| 318 | 374 |
| 319 void AsyncCallStackTracker::clear() | 375 void AsyncCallStackTracker::clear() |
| 320 { | 376 { |
| 321 m_currentAsyncCallChain = 0; | 377 m_currentAsyncCallChain.clear(); |
| 322 m_nestedAsyncCallCount = 0; | 378 m_nestedAsyncCallCount = 0; |
| 323 ExecutionContextDataMap copy; | 379 ExecutionContextDataMap copy; |
| 324 m_executionContextDataMap.swap(copy); | 380 m_executionContextDataMap.swap(copy); |
| 325 for (ExecutionContextDataMap::const_iterator it = copy.begin(); it != copy.e nd(); ++it) | 381 for (ExecutionContextDataMap::const_iterator it = copy.begin(); it != copy.e nd(); ++it) |
| 326 delete it->value; | 382 delete it->value; |
| 327 } | 383 } |
| 328 | 384 |
| 329 } // namespace WebCore | 385 } // namespace WebCore |
| OLD | NEW |