Index: Source/core/inspector/AsyncCallStackTracker.cpp |
diff --git a/Source/core/inspector/AsyncCallStackTracker.cpp b/Source/core/inspector/AsyncCallStackTracker.cpp |
index 89d21b2452a3474eed3556fee4a124f9f464094d..841c94667395ff8770fe72ba997c60f5fc033a48 100644 |
--- a/Source/core/inspector/AsyncCallStackTracker.cpp |
+++ b/Source/core/inspector/AsyncCallStackTracker.cpp |
@@ -33,12 +33,19 @@ |
#include "core/dom/ContextLifecycleObserver.h" |
#include "core/dom/ExecutionContext.h" |
+#include "core/events/EventTarget.h" |
+#include "core/events/RegisteredEventListener.h" |
+#include "wtf/text/StringBuilder.h" |
namespace WebCore { |
class AsyncCallStackTracker::ExecutionContextData : public ContextLifecycleObserver { |
WTF_MAKE_FAST_ALLOCATED; |
public: |
+ typedef std::pair<RegisteredEventListener, RefPtr<AsyncCallChain> > EventListenerAsyncCallChain; |
+ typedef Vector<EventListenerAsyncCallChain, 1> EventListenerAsyncCallChainVector; |
+ typedef HashMap<AtomicString, EventListenerAsyncCallChainVector> EventListenerAsyncCallChainVectorHashMap; |
+ |
ExecutionContextData(AsyncCallStackTracker* tracker, ExecutionContext* executionContext) |
: ContextLifecycleObserver(executionContext) |
, m_tracker(tracker) |
@@ -54,12 +61,56 @@ public: |
delete self; |
} |
-private: |
- friend class AsyncCallStackTracker; |
+ void addEventListenerData(EventTarget* eventTarget, const AtomicString& eventType, const EventListenerAsyncCallChain& item) |
+ { |
+ HashMap<EventTarget*, EventListenerAsyncCallChainVectorHashMap>::iterator it = m_eventTargetCallChains.find(eventTarget); |
+ if (it == m_eventTargetCallChains.end()) |
+ it = m_eventTargetCallChains.set(eventTarget, EventListenerAsyncCallChainVectorHashMap()).iterator; |
+ EventListenerAsyncCallChainVectorHashMap& map = it->value; |
+ EventListenerAsyncCallChainVectorHashMap::iterator it2 = map.find(eventType); |
+ if (it2 == map.end()) |
+ it2 = map.set(eventType, EventListenerAsyncCallChainVector()).iterator; |
+ it2->value.append(item); |
+ } |
+ |
+ void removeEventListenerData(EventTarget* eventTarget, const AtomicString& eventType, const RegisteredEventListener& item) |
+ { |
+ findEventListenerData(eventTarget, eventType, item, true); |
+ } |
+ |
+ PassRefPtr<AsyncCallChain> findEventListenerData(EventTarget* eventTarget, const AtomicString& eventType, const RegisteredEventListener& item, bool remove = false) |
+ { |
+ HashMap<EventTarget*, EventListenerAsyncCallChainVectorHashMap>::iterator it = m_eventTargetCallChains.find(eventTarget); |
+ if (it == m_eventTargetCallChains.end()) |
+ return 0; |
+ EventListenerAsyncCallChainVectorHashMap& map = it->value; |
+ EventListenerAsyncCallChainVectorHashMap::iterator it2 = map.find(eventType); |
+ if (it2 == map.end()) |
+ return 0; |
+ RefPtr<AsyncCallChain> result; |
+ EventListenerAsyncCallChainVector& vector = it2->value; |
+ for (size_t i = 0; i < vector.size(); ++i) { |
+ if (vector[i].first == item) { |
+ result = vector[i].second; |
+ if (remove) { |
+ vector.remove(i); |
+ if (vector.isEmpty()) |
+ map.remove(it2); |
+ if (map.isEmpty()) |
+ m_eventTargetCallChains.remove(it); |
+ } |
+ break; |
+ } |
+ } |
+ return result.release(); |
+ } |
+ |
+public: |
AsyncCallStackTracker* m_tracker; |
HashSet<int> m_intervalTimerIds; |
HashMap<int, RefPtr<AsyncCallChain> > m_timerCallChains; |
HashMap<int, RefPtr<AsyncCallChain> > m_animationFrameCallChains; |
+ HashMap<EventTarget*, EventListenerAsyncCallChainVectorHashMap> m_eventTargetCallChains; |
}; |
AsyncCallStackTracker::AsyncCallStack::AsyncCallStack(const String& description, const ScriptValue& callFrames) |
@@ -113,7 +164,8 @@ void AsyncCallStackTracker::didInstallTimer(ExecutionContext* context, int timer |
void AsyncCallStackTracker::didRemoveTimer(ExecutionContext* context, int timerId) |
{ |
ASSERT(context); |
- if (!isEnabled() || timerId <= 0) |
+ ASSERT(isEnabled()); |
+ if (timerId <= 0) |
return; |
ExecutionContextData* data = m_executionContextDataMap.get(context); |
if (!data) |
@@ -125,17 +177,16 @@ void AsyncCallStackTracker::didRemoveTimer(ExecutionContext* context, int timerI |
void AsyncCallStackTracker::willFireTimer(ExecutionContext* context, int timerId) |
{ |
ASSERT(context); |
- if (!isEnabled()) |
- return; |
+ ASSERT(isEnabled()); |
ASSERT(timerId > 0); |
ASSERT(!m_currentAsyncCallChain); |
ExecutionContextData* data = m_executionContextDataMap.get(context); |
if (!data) |
return; |
if (data->m_intervalTimerIds.contains(timerId)) |
- m_currentAsyncCallChain = data->m_timerCallChains.get(timerId); |
+ setCurrentAsyncCallChain(data->m_timerCallChains.get(timerId)); |
else |
- m_currentAsyncCallChain = data->m_timerCallChains.take(timerId); |
+ setCurrentAsyncCallChain(data->m_timerCallChains.take(timerId)); |
} |
void AsyncCallStackTracker::didRequestAnimationFrame(ExecutionContext* context, int callbackId, const ScriptValue& callFrames) |
@@ -154,7 +205,8 @@ void AsyncCallStackTracker::didRequestAnimationFrame(ExecutionContext* context, |
void AsyncCallStackTracker::didCancelAnimationFrame(ExecutionContext* context, int callbackId) |
{ |
ASSERT(context); |
- if (!isEnabled() || callbackId <= 0) |
+ ASSERT(isEnabled()); |
+ if (callbackId <= 0) |
return; |
if (ExecutionContextData* data = m_executionContextDataMap.get(context)) |
data->m_animationFrameCallChains.remove(callbackId); |
@@ -163,28 +215,86 @@ void AsyncCallStackTracker::didCancelAnimationFrame(ExecutionContext* context, i |
void AsyncCallStackTracker::willFireAnimationFrame(ExecutionContext* context, int callbackId) |
{ |
ASSERT(context); |
- if (!isEnabled()) |
- return; |
+ ASSERT(isEnabled()); |
ASSERT(callbackId > 0); |
ASSERT(!m_currentAsyncCallChain); |
if (ExecutionContextData* data = m_executionContextDataMap.get(context)) |
- m_currentAsyncCallChain = data->m_animationFrameCallChains.take(callbackId); |
+ setCurrentAsyncCallChain(data->m_animationFrameCallChains.take(callbackId)); |
+} |
+ |
+void AsyncCallStackTracker::didAddEventListener(EventTarget* eventTarget, const AtomicString& eventType, EventListener* listener, bool useCapture, const ScriptValue& callFrames) |
+{ |
+ ASSERT(eventTarget->executionContext()); |
+ ASSERT(isEnabled()); |
+ if (!validateCallFrames(callFrames)) |
+ return; |
+ |
+ StringBuilder description; |
+ description.append(eventTarget->interfaceName()); |
+ if (!description.isEmpty()) |
+ description.append("."); |
+ if (listener->isAttribute()) { |
+ description.append("on"); |
+ description.append(eventType); |
+ } else { |
+ description.append("addEventListener(\""); |
+ description.append(eventType); |
+ description.append("\")"); |
+ } |
+ |
+ ExecutionContextData* data = createContextDataIfNeeded(eventTarget->executionContext()); |
+ data->addEventListenerData(eventTarget, eventType, std::make_pair(RegisteredEventListener(listener, useCapture), createAsyncCallChain(description.toString(), callFrames))); |
+} |
+ |
+void AsyncCallStackTracker::didRemoveEventListener(EventTarget* eventTarget, const AtomicString& eventType, EventListener* listener, bool useCapture) |
+{ |
+ ASSERT(eventTarget->executionContext()); |
+ ASSERT(isEnabled()); |
+ if (ExecutionContextData* data = m_executionContextDataMap.get(eventTarget->executionContext())) |
+ data->removeEventListenerData(eventTarget, eventType, RegisteredEventListener(listener, useCapture)); |
+} |
+ |
+void AsyncCallStackTracker::didRemoveAllEventListeners(EventTarget* eventTarget) |
+{ |
+ ASSERT(eventTarget->executionContext()); |
+ ASSERT(isEnabled()); |
+ if (ExecutionContextData* data = m_executionContextDataMap.get(eventTarget->executionContext())) |
+ data->m_eventTargetCallChains.remove(eventTarget); |
+} |
+ |
+void AsyncCallStackTracker::willHandleEvent(EventTarget* eventTarget, const AtomicString& eventType, EventListener* listener, bool useCapture) |
+{ |
+ ASSERT(eventTarget->executionContext()); |
+ ASSERT(isEnabled()); |
+ if (ExecutionContextData* data = m_executionContextDataMap.get(eventTarget->executionContext())) |
+ setCurrentAsyncCallChain(data->findEventListenerData(eventTarget, eventType, RegisteredEventListener(listener, useCapture))); |
} |
void AsyncCallStackTracker::didFireAsyncCall() |
{ |
- m_currentAsyncCallChain = 0; |
+ setCurrentAsyncCallChain(0); |
} |
PassRefPtr<AsyncCallStackTracker::AsyncCallChain> AsyncCallStackTracker::createAsyncCallChain(const String& description, const ScriptValue& callFrames) |
{ |
- ASSERT(isEnabled()); |
RefPtr<AsyncCallChain> chain = adoptRef(m_currentAsyncCallChain ? new AsyncCallStackTracker::AsyncCallChain(*m_currentAsyncCallChain) : new AsyncCallStackTracker::AsyncCallChain()); |
ensureMaxAsyncCallChainDepth(chain.get(), m_maxAsyncCallStackDepth - 1); |
chain->m_callStacks.prepend(adoptRef(new AsyncCallStackTracker::AsyncCallStack(description, callFrames))); |
return chain.release(); |
} |
+void AsyncCallStackTracker::setCurrentAsyncCallChain(PassRefPtr<AsyncCallChain> chain) |
+{ |
+ if (m_currentAsyncCallChain) { |
+ m_nestedAsyncCallCount += chain ? 1 : -1; |
+ if (!m_nestedAsyncCallCount) |
+ m_currentAsyncCallChain = 0; |
+ } else if (chain) { |
+ m_currentAsyncCallChain = chain; |
+ m_nestedAsyncCallCount = 1; |
+ } |
+} |
+ |
void AsyncCallStackTracker::ensureMaxAsyncCallChainDepth(AsyncCallChain* chain, unsigned maxDepth) |
{ |
while (chain->m_callStacks.size() > maxDepth) |
@@ -209,6 +319,7 @@ AsyncCallStackTracker::ExecutionContextData* AsyncCallStackTracker::createContex |
void AsyncCallStackTracker::clear() |
{ |
m_currentAsyncCallChain = 0; |
+ m_nestedAsyncCallCount = 0; |
ExecutionContextDataMap copy; |
m_executionContextDataMap.swap(copy); |
for (ExecutionContextDataMap::const_iterator it = copy.begin(); it != copy.end(); ++it) |