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