| 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)
|
|
|