| Index: Source/core/inspector/AsyncCallStackTracker.cpp
|
| diff --git a/Source/core/inspector/AsyncCallStackTracker.cpp b/Source/core/inspector/AsyncCallStackTracker.cpp
|
| index d3a1982af2b16a22b3dffa3f6248143237aee4e9..75216e946344490b5b8695fad7f825dfbad29019 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)
|
| @@ -51,12 +58,57 @@ public:
|
| ContextLifecycleObserver::contextDestroyed();
|
| }
|
|
|
| + void add(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 remove(EventTarget* eventTarget, const AtomicString& eventType, const RegisteredEventListener& item)
|
| + {
|
| + get(eventTarget, eventType, item, true);
|
| + }
|
| +
|
| + PassRefPtr<AsyncCallChain> get(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();
|
| + }
|
| +
|
| private:
|
| friend class AsyncCallStackTracker;
|
| 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)
|
| @@ -168,6 +220,59 @@ void AsyncCallStackTracker::willFireAnimationFrame(ExecutionContext* context, in
|
| m_currentAsyncCallChain = 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->add(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());
|
| + if (!isEnabled())
|
| + return;
|
| + if (ExecutionContextData* data = m_executionContextDataMap.get(eventTarget->executionContext()))
|
| + data->remove(eventTarget, eventType, RegisteredEventListener(listener, useCapture));
|
| +}
|
| +
|
| +void AsyncCallStackTracker::didRemoveAllEventListeners(EventTarget* eventTarget)
|
| +{
|
| + ASSERT(eventTarget->executionContext());
|
| + if (!isEnabled())
|
| + return;
|
| + 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());
|
| + if (!isEnabled())
|
| + return;
|
| + // FIXME: Handle events being fired synchronously right after they are added.
|
| + // ASSERT(!m_currentAsyncCallChain);
|
| + if (ExecutionContextData* data = m_executionContextDataMap.get(eventTarget->executionContext()))
|
| + m_currentAsyncCallChain = data->get(eventTarget, eventType, RegisteredEventListener(listener, useCapture));
|
| +}
|
| +
|
| void AsyncCallStackTracker::didFireAsyncCall()
|
| {
|
| m_currentAsyncCallChain = 0;
|
|
|