Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(657)

Unified Diff: Source/core/inspector/AsyncCallStackTracker.cpp

Issue 114033002: DevTools: Capture async stacks for event listeners. (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: addressed Created 7 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « Source/core/inspector/AsyncCallStackTracker.h ('k') | Source/core/inspector/InspectorDOMDebuggerAgent.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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)
« no previous file with comments | « Source/core/inspector/AsyncCallStackTracker.h ('k') | Source/core/inspector/InspectorDOMDebuggerAgent.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698