Chromium Code Reviews| Index: Source/core/inspector/AsyncCallStackTracker.cpp |
| diff --git a/Source/core/inspector/AsyncCallStackTracker.cpp b/Source/core/inspector/AsyncCallStackTracker.cpp |
| index d3a1982af2b16a22b3dffa3f6248143237aee4e9..04ab4a8a7097e180105da6511ce91c2ddf2e512b 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) |
|
yurys
2013/12/17 13:01:59
addEventListenerData?
aandrey
2013/12/17 13:24:03
Done.
|
| + { |
| + 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; |
|
yurys
2013/12/17 13:01:59
All these field should be public since ExecutionCo
aandrey
2013/12/17 13:24:03
Done.
|
| 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) |
| @@ -125,14 +177,13 @@ void AsyncCallStackTracker::willFireTimer(ExecutionContext* context, int timerId |
| if (!isEnabled()) |
| return; |
| ASSERT(timerId > 0); |
| - ASSERT(!m_currentAsyncCallChain); |
|
yurys
2013/12/17 13:01:59
Why did you remove this assert? Can it fail in a w
aandrey
2013/12/17 13:07:48
We can receive nested willHandleEvent() callbacks,
aandrey
2013/12/17 13:24:03
Done.
yurys
2013/12/18 09:22:15
I agree that it make sense for willHandleEvent whi
aandrey
2013/12/18 09:32:09
I think they can not. Added those ASSERT's back.
|
| 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) |
| @@ -163,14 +214,64 @@ void AsyncCallStackTracker::willFireAnimationFrame(ExecutionContext* context, in |
| if (!isEnabled()) |
| return; |
| 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->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; |
| + if (ExecutionContextData* data = m_executionContextDataMap.get(eventTarget->executionContext())) |
| + setCurrentAsyncCallChain(data->get(eventTarget, eventType, RegisteredEventListener(listener, useCapture))); |
| } |
| void AsyncCallStackTracker::didFireAsyncCall() |
| { |
| - m_currentAsyncCallChain = 0; |
| + setCurrentAsyncCallChain(0); |
| } |
| PassRefPtr<AsyncCallStackTracker::AsyncCallChain> AsyncCallStackTracker::createAsyncCallChain(const String& description, const ScriptValue& callFrames) |
| @@ -182,6 +283,18 @@ PassRefPtr<AsyncCallStackTracker::AsyncCallChain> AsyncCallStackTracker::createA |
| 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) |
| @@ -213,6 +326,7 @@ AsyncCallStackTracker::ExecutionContextData* AsyncCallStackTracker::createContex |
| void AsyncCallStackTracker::clear() |
| { |
| m_currentAsyncCallChain = 0; |
| + m_nestedAsyncCallCount = 0; |
| Vector<ExecutionContextData*> contextsData; |
| copyValuesToVector(m_executionContextDataMap, contextsData); |
| m_executionContextDataMap.clear(); |