2 * Copyright (C) 2013 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 #include "core/inspector/AsyncCallStackTracker.h"
34 #include "bindings/v8/V8RecursionScope.h"
35 #include "core/dom/ContextLifecycleObserver.h"
36 #include "core/dom/ExecutionContext.h"
37 #include "core/events/EventTarget.h"
38 #include "core/events/RegisteredEventListener.h"
39 #include "core/xml/XMLHttpRequest.h"
40 #include "core/xml/XMLHttpRequestUpload.h"
41 #include "wtf/text/AtomicStringHash.h"
42 #include "wtf/text/StringBuilder.h"
46 static const char setTimeoutName[] = "setTimeout";
47 static const char setIntervalName[] = "setInterval";
48 static const char requestAnimationFrameName[] = "requestAnimationFrame";
49 static const char xhrSendName[] = "XMLHttpRequest.send";
50 static const char enqueueMutationRecordName[] = "Mutation";
51 static const char promiseResolved[] = "Promise.resolve";
52 static const char promiseRejected[] = "Promise.reject";
58 class AsyncCallStackTracker::ExecutionContextData FINAL : public ContextLifecycleObserver {
59 WTF_MAKE_FAST_ALLOCATED;
61 typedef std::pair<RegisteredEventListener, RefPtr<AsyncCallChain> > EventListenerAsyncCallChain;
62 typedef Vector<EventListenerAsyncCallChain, 1> EventListenerAsyncCallChainVector;
63 typedef HashMap<AtomicString, EventListenerAsyncCallChainVector> EventListenerAsyncCallChainVectorHashMap;
65 ExecutionContextData(AsyncCallStackTracker* tracker, ExecutionContext* executionContext)
66 : ContextLifecycleObserver(executionContext)
71 virtual void contextDestroyed() OVERRIDE
73 ASSERT(executionContext());
74 ExecutionContextData* self = m_tracker->m_executionContextDataMap.take(executionContext());
76 ContextLifecycleObserver::contextDestroyed();
80 void addEventListenerData(EventTarget* eventTarget, const AtomicString& eventType, const EventListenerAsyncCallChain& item)
82 HashMap<EventTarget*, EventListenerAsyncCallChainVectorHashMap>::iterator it = m_eventTargetCallChains.find(eventTarget);
83 EventListenerAsyncCallChainVectorHashMap* mapPtr;
84 if (it == m_eventTargetCallChains.end())
85 mapPtr = &m_eventTargetCallChains.set(eventTarget, EventListenerAsyncCallChainVectorHashMap()).storedValue->value;
88 EventListenerAsyncCallChainVectorHashMap& map = *mapPtr;
89 EventListenerAsyncCallChainVectorHashMap::iterator it2 = map.find(eventType);
91 map.set(eventType, EventListenerAsyncCallChainVector()).storedValue->value.append(item);
93 it2->value.append(item);
96 void removeEventListenerData(EventTarget* eventTarget, const AtomicString& eventType, const RegisteredEventListener& item)
98 findEventListenerData(eventTarget, eventType, item, true);
101 PassRefPtr<AsyncCallChain> findEventListenerData(EventTarget* eventTarget, const AtomicString& eventType, const RegisteredEventListener& item, bool remove = false)
103 HashMap<EventTarget*, EventListenerAsyncCallChainVectorHashMap>::iterator it = m_eventTargetCallChains.find(eventTarget);
104 if (it == m_eventTargetCallChains.end())
106 EventListenerAsyncCallChainVectorHashMap& map = it->value;
107 EventListenerAsyncCallChainVectorHashMap::iterator it2 = map.find(eventType);
108 if (it2 == map.end())
110 RefPtr<AsyncCallChain> result;
111 EventListenerAsyncCallChainVector& vector = it2->value;
112 for (size_t i = 0; i < vector.size(); ++i) {
113 if (vector[i].first == item) {
114 result = vector[i].second;
117 if (vector.isEmpty())
120 m_eventTargetCallChains.remove(it);
125 return result.release();
129 AsyncCallStackTracker* m_tracker;
130 HashSet<int> m_intervalTimerIds;
131 HashMap<int, RefPtr<AsyncCallChain> > m_timerCallChains;
132 HashMap<int, RefPtr<AsyncCallChain> > m_animationFrameCallChains;
133 HashMap<EventTarget*, EventListenerAsyncCallChainVectorHashMap> m_eventTargetCallChains;
134 HashMap<EventTarget*, RefPtr<AsyncCallChain> > m_xhrCallChains;
135 HashMap<MutationObserver*, RefPtr<AsyncCallChain> > m_mutationObserverCallChains;
136 HashMap<ExecutionContextTask*, RefPtr<AsyncCallChain> > m_promiseTaskCallChains;
139 static XMLHttpRequest* toXmlHttpRequest(EventTarget* eventTarget)
141 const AtomicString& interfaceName = eventTarget->interfaceName();
142 if (interfaceName == EventTargetNames::XMLHttpRequest)
143 return static_cast<XMLHttpRequest*>(eventTarget);
144 if (interfaceName == EventTargetNames::XMLHttpRequestUpload)
145 return static_cast<XMLHttpRequestUpload*>(eventTarget)->xmlHttpRequest();
149 AsyncCallStackTracker::AsyncCallStack::AsyncCallStack(const String& description, const ScriptValue& callFrames)
150 : m_description(description)
151 , m_callFrames(callFrames)
155 AsyncCallStackTracker::AsyncCallStack::~AsyncCallStack()
159 AsyncCallStackTracker::AsyncCallStackTracker()
160 : m_maxAsyncCallStackDepth(0)
164 void AsyncCallStackTracker::setAsyncCallStackDepth(int depth)
167 m_maxAsyncCallStackDepth = 0;
170 m_maxAsyncCallStackDepth = depth;
174 const AsyncCallStackTracker::AsyncCallChain* AsyncCallStackTracker::currentAsyncCallChain() const
176 if (m_currentAsyncCallChain)
177 ensureMaxAsyncCallChainDepth(m_currentAsyncCallChain.get(), m_maxAsyncCallStackDepth);
178 return m_currentAsyncCallChain.get();
181 void AsyncCallStackTracker::didInstallTimer(ExecutionContext* context, int timerId, bool singleShot, const ScriptValue& callFrames)
185 if (!validateCallFrames(callFrames))
188 ExecutionContextData* data = createContextDataIfNeeded(context);
189 data->m_timerCallChains.set(timerId, createAsyncCallChain(singleShot ? setTimeoutName : setIntervalName, callFrames));
191 data->m_intervalTimerIds.add(timerId);
194 void AsyncCallStackTracker::didRemoveTimer(ExecutionContext* context, int timerId)
200 ExecutionContextData* data = m_executionContextDataMap.get(context);
203 data->m_intervalTimerIds.remove(timerId);
204 data->m_timerCallChains.remove(timerId);
207 void AsyncCallStackTracker::willFireTimer(ExecutionContext* context, int timerId)
212 ASSERT(!m_currentAsyncCallChain);
213 if (ExecutionContextData* data = m_executionContextDataMap.get(context)) {
214 if (data->m_intervalTimerIds.contains(timerId))
215 setCurrentAsyncCallChain(data->m_timerCallChains.get(timerId));
217 setCurrentAsyncCallChain(data->m_timerCallChains.take(timerId));
219 setCurrentAsyncCallChain(nullptr);
223 void AsyncCallStackTracker::didRequestAnimationFrame(ExecutionContext* context, int callbackId, const ScriptValue& callFrames)
227 if (!validateCallFrames(callFrames))
229 ASSERT(callbackId > 0);
230 ExecutionContextData* data = createContextDataIfNeeded(context);
231 data->m_animationFrameCallChains.set(callbackId, createAsyncCallChain(requestAnimationFrameName, callFrames));
234 void AsyncCallStackTracker::didCancelAnimationFrame(ExecutionContext* context, int callbackId)
240 if (ExecutionContextData* data = m_executionContextDataMap.get(context))
241 data->m_animationFrameCallChains.remove(callbackId);
244 void AsyncCallStackTracker::willFireAnimationFrame(ExecutionContext* context, int callbackId)
248 ASSERT(callbackId > 0);
249 ASSERT(!m_currentAsyncCallChain);
250 if (ExecutionContextData* data = m_executionContextDataMap.get(context))
251 setCurrentAsyncCallChain(data->m_animationFrameCallChains.take(callbackId));
253 setCurrentAsyncCallChain(nullptr);
256 void AsyncCallStackTracker::didAddEventListener(EventTarget* eventTarget, const AtomicString& eventType, EventListener* listener, bool useCapture, const ScriptValue& callFrames)
258 ASSERT(eventTarget->executionContext());
260 if (!validateCallFrames(callFrames) || toXmlHttpRequest(eventTarget))
263 StringBuilder description;
264 description.append(eventTarget->interfaceName());
265 if (!description.isEmpty())
266 description.append(".");
267 if (listener->isAttribute()) {
268 description.append("on");
269 description.append(eventType);
271 description.append("addEventListener(\"");
272 description.append(eventType);
273 description.append("\")");
276 ExecutionContextData* data = createContextDataIfNeeded(eventTarget->executionContext());
277 data->addEventListenerData(eventTarget, eventType, std::make_pair(RegisteredEventListener(listener, useCapture), createAsyncCallChain(description.toString(), callFrames)));
280 void AsyncCallStackTracker::didRemoveEventListener(EventTarget* eventTarget, const AtomicString& eventType, EventListener* listener, bool useCapture)
282 ASSERT(eventTarget->executionContext());
284 if (ExecutionContextData* data = m_executionContextDataMap.get(eventTarget->executionContext()))
285 data->removeEventListenerData(eventTarget, eventType, RegisteredEventListener(listener, useCapture));
288 void AsyncCallStackTracker::didRemoveAllEventListeners(EventTarget* eventTarget)
290 ASSERT(eventTarget->executionContext());
292 if (ExecutionContextData* data = m_executionContextDataMap.get(eventTarget->executionContext()))
293 data->m_eventTargetCallChains.remove(eventTarget);
296 void AsyncCallStackTracker::willHandleEvent(EventTarget* eventTarget, const AtomicString& eventType, EventListener* listener, bool useCapture)
298 ASSERT(eventTarget->executionContext());
300 if (XMLHttpRequest* xhr = toXmlHttpRequest(eventTarget)) {
301 willHandleXHREvent(xhr, eventTarget, eventType);
304 if (ExecutionContextData* data = m_executionContextDataMap.get(eventTarget->executionContext()))
305 setCurrentAsyncCallChain(data->findEventListenerData(eventTarget, eventType, RegisteredEventListener(listener, useCapture)));
307 setCurrentAsyncCallChain(nullptr);
310 void AsyncCallStackTracker::willLoadXHR(XMLHttpRequest* xhr, const ScriptValue& callFrames)
312 ASSERT(xhr->executionContext());
314 if (!validateCallFrames(callFrames))
316 ExecutionContextData* data = createContextDataIfNeeded(xhr->executionContext());
317 data->m_xhrCallChains.set(xhr, createAsyncCallChain(xhrSendName, callFrames));
320 void AsyncCallStackTracker::willHandleXHREvent(XMLHttpRequest* xhr, EventTarget* eventTarget, const AtomicString& eventType)
322 ASSERT(xhr->executionContext());
324 if (ExecutionContextData* data = m_executionContextDataMap.get(xhr->executionContext())) {
325 bool isXHRDownload = (xhr == eventTarget);
326 if (isXHRDownload && eventType == EventTypeNames::loadend)
327 setCurrentAsyncCallChain(data->m_xhrCallChains.take(xhr));
329 setCurrentAsyncCallChain(data->m_xhrCallChains.get(xhr));
331 setCurrentAsyncCallChain(nullptr);
335 void AsyncCallStackTracker::didEnqueueMutationRecord(ExecutionContext* context, MutationObserver* observer, const ScriptValue& callFrames)
339 if (!validateCallFrames(callFrames))
341 ExecutionContextData* data = createContextDataIfNeeded(context);
342 data->m_mutationObserverCallChains.set(observer, createAsyncCallChain(enqueueMutationRecordName, callFrames));
345 bool AsyncCallStackTracker::hasEnqueuedMutationRecord(ExecutionContext* context, MutationObserver* observer)
349 if (ExecutionContextData* data = m_executionContextDataMap.get(context))
350 return data->m_mutationObserverCallChains.contains(observer);
354 void AsyncCallStackTracker::didClearAllMutationRecords(ExecutionContext* context, MutationObserver* observer)
358 if (ExecutionContextData* data = m_executionContextDataMap.get(context))
359 data->m_mutationObserverCallChains.remove(observer);
362 void AsyncCallStackTracker::willDeliverMutationRecords(ExecutionContext* context, MutationObserver* observer)
366 if (ExecutionContextData* data = m_executionContextDataMap.get(context))
367 setCurrentAsyncCallChain(data->m_mutationObserverCallChains.take(observer));
369 setCurrentAsyncCallChain(nullptr);
372 void AsyncCallStackTracker::didPostPromiseTask(ExecutionContext* context, ExecutionContextTask* task, bool isResolved, const ScriptValue& callFrames)
376 if (validateCallFrames(callFrames)) {
377 ExecutionContextData* data = createContextDataIfNeeded(context);
378 data->m_promiseTaskCallChains.set(task, createAsyncCallChain(isResolved ? promiseResolved : promiseRejected, callFrames));
379 } else if (m_currentAsyncCallChain) {
380 // Propagate async call stack to the re-posted task to update a derived Promise.
381 ExecutionContextData* data = createContextDataIfNeeded(context);
382 data->m_promiseTaskCallChains.set(task, m_currentAsyncCallChain);
386 void AsyncCallStackTracker::willPerformPromiseTask(ExecutionContext* context, ExecutionContextTask* task)
390 if (ExecutionContextData* data = m_executionContextDataMap.get(context))
391 setCurrentAsyncCallChain(data->m_promiseTaskCallChains.take(task));
393 setCurrentAsyncCallChain(nullptr);
396 void AsyncCallStackTracker::didFireAsyncCall()
398 clearCurrentAsyncCallChain();
401 PassRefPtr<AsyncCallStackTracker::AsyncCallChain> AsyncCallStackTracker::createAsyncCallChain(const String& description, const ScriptValue& callFrames)
403 RefPtr<AsyncCallChain> chain = adoptRef(m_currentAsyncCallChain ? new AsyncCallStackTracker::AsyncCallChain(*m_currentAsyncCallChain) : new AsyncCallStackTracker::AsyncCallChain());
404 ensureMaxAsyncCallChainDepth(chain.get(), m_maxAsyncCallStackDepth - 1);
405 chain->m_callStacks.prepend(adoptRef(new AsyncCallStackTracker::AsyncCallStack(description, callFrames)));
406 return chain.release();
409 void AsyncCallStackTracker::setCurrentAsyncCallChain(PassRefPtr<AsyncCallChain> chain)
411 if (V8RecursionScope::recursionLevel(v8::Isolate::GetCurrent())) {
412 if (m_currentAsyncCallChain)
413 ++m_nestedAsyncCallCount;
415 // Current AsyncCallChain corresponds to the bottommost JS call frame.
416 m_currentAsyncCallChain = chain;
417 m_nestedAsyncCallCount = m_currentAsyncCallChain ? 1 : 0;
421 void AsyncCallStackTracker::clearCurrentAsyncCallChain()
423 if (!m_nestedAsyncCallCount)
425 --m_nestedAsyncCallCount;
426 if (!m_nestedAsyncCallCount)
427 m_currentAsyncCallChain.clear();
430 void AsyncCallStackTracker::ensureMaxAsyncCallChainDepth(AsyncCallChain* chain, unsigned maxDepth)
432 while (chain->m_callStacks.size() > maxDepth)
433 chain->m_callStacks.removeLast();
436 bool AsyncCallStackTracker::validateCallFrames(const ScriptValue& callFrames)
438 return !callFrames.isEmpty();
441 AsyncCallStackTracker::ExecutionContextData* AsyncCallStackTracker::createContextDataIfNeeded(ExecutionContext* context)
443 ExecutionContextData* data = m_executionContextDataMap.get(context);
445 data = new AsyncCallStackTracker::ExecutionContextData(this, context);
446 m_executionContextDataMap.set(context, data);
451 void AsyncCallStackTracker::clear()
453 m_currentAsyncCallChain.clear();
454 m_nestedAsyncCallCount = 0;
455 ExecutionContextDataMap copy;
456 m_executionContextDataMap.swap(copy);
457 for (ExecutionContextDataMap::const_iterator it = copy.begin(); it != copy.end(); ++it)
461 } // namespace WebCore