Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / inspector / AsyncCallStackTracker.cpp
1 /*
2  * Copyright (C) 2013 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
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
13  * distribution.
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.
17  *
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.
29  */
30
31 #include "config.h"
32 #include "core/inspector/AsyncCallStackTracker.h"
33
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"
43
44 namespace {
45
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";
53
54 }
55
56 namespace WebCore {
57
58 class AsyncCallStackTracker::ExecutionContextData FINAL : public ContextLifecycleObserver {
59     WTF_MAKE_FAST_ALLOCATED;
60 public:
61     typedef std::pair<RegisteredEventListener, RefPtr<AsyncCallChain> > EventListenerAsyncCallChain;
62     typedef Vector<EventListenerAsyncCallChain, 1> EventListenerAsyncCallChainVector;
63     typedef HashMap<AtomicString, EventListenerAsyncCallChainVector> EventListenerAsyncCallChainVectorHashMap;
64
65     ExecutionContextData(AsyncCallStackTracker* tracker, ExecutionContext* executionContext)
66         : ContextLifecycleObserver(executionContext)
67         , m_tracker(tracker)
68     {
69     }
70
71     virtual void contextDestroyed() OVERRIDE
72     {
73         ASSERT(executionContext());
74         ExecutionContextData* self = m_tracker->m_executionContextDataMap.take(executionContext());
75         ASSERT(self == this);
76         ContextLifecycleObserver::contextDestroyed();
77         delete self;
78     }
79
80     void addEventListenerData(EventTarget* eventTarget, const AtomicString& eventType, const EventListenerAsyncCallChain& item)
81     {
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;
86         else
87             mapPtr = &it->value;
88         EventListenerAsyncCallChainVectorHashMap& map = *mapPtr;
89         EventListenerAsyncCallChainVectorHashMap::iterator it2 = map.find(eventType);
90         if (it2 == map.end())
91             map.set(eventType, EventListenerAsyncCallChainVector()).storedValue->value.append(item);
92         else
93             it2->value.append(item);
94     }
95
96     void removeEventListenerData(EventTarget* eventTarget, const AtomicString& eventType, const RegisteredEventListener& item)
97     {
98         findEventListenerData(eventTarget, eventType, item, true);
99     }
100
101     PassRefPtr<AsyncCallChain> findEventListenerData(EventTarget* eventTarget, const AtomicString& eventType, const RegisteredEventListener& item, bool remove = false)
102     {
103         HashMap<EventTarget*, EventListenerAsyncCallChainVectorHashMap>::iterator it = m_eventTargetCallChains.find(eventTarget);
104         if (it == m_eventTargetCallChains.end())
105             return nullptr;
106         EventListenerAsyncCallChainVectorHashMap& map = it->value;
107         EventListenerAsyncCallChainVectorHashMap::iterator it2 = map.find(eventType);
108         if (it2 == map.end())
109             return nullptr;
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;
115                 if (remove) {
116                     vector.remove(i);
117                     if (vector.isEmpty())
118                         map.remove(it2);
119                     if (map.isEmpty())
120                         m_eventTargetCallChains.remove(it);
121                 }
122                 break;
123             }
124         }
125         return result.release();
126     }
127
128 public:
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;
137 };
138
139 static XMLHttpRequest* toXmlHttpRequest(EventTarget* eventTarget)
140 {
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();
146     return 0;
147 }
148
149 AsyncCallStackTracker::AsyncCallStack::AsyncCallStack(const String& description, const ScriptValue& callFrames)
150     : m_description(description)
151     , m_callFrames(callFrames)
152 {
153 }
154
155 AsyncCallStackTracker::AsyncCallStack::~AsyncCallStack()
156 {
157 }
158
159 AsyncCallStackTracker::AsyncCallStackTracker()
160     : m_maxAsyncCallStackDepth(0)
161 {
162 }
163
164 void AsyncCallStackTracker::setAsyncCallStackDepth(int depth)
165 {
166     if (depth <= 0) {
167         m_maxAsyncCallStackDepth = 0;
168         clear();
169     } else {
170         m_maxAsyncCallStackDepth = depth;
171     }
172 }
173
174 const AsyncCallStackTracker::AsyncCallChain* AsyncCallStackTracker::currentAsyncCallChain() const
175 {
176     if (m_currentAsyncCallChain)
177         ensureMaxAsyncCallChainDepth(m_currentAsyncCallChain.get(), m_maxAsyncCallStackDepth);
178     return m_currentAsyncCallChain.get();
179 }
180
181 void AsyncCallStackTracker::didInstallTimer(ExecutionContext* context, int timerId, bool singleShot, const ScriptValue& callFrames)
182 {
183     ASSERT(context);
184     ASSERT(isEnabled());
185     if (!validateCallFrames(callFrames))
186         return;
187     ASSERT(timerId > 0);
188     ExecutionContextData* data = createContextDataIfNeeded(context);
189     data->m_timerCallChains.set(timerId, createAsyncCallChain(singleShot ? setTimeoutName : setIntervalName, callFrames));
190     if (!singleShot)
191         data->m_intervalTimerIds.add(timerId);
192 }
193
194 void AsyncCallStackTracker::didRemoveTimer(ExecutionContext* context, int timerId)
195 {
196     ASSERT(context);
197     ASSERT(isEnabled());
198     if (timerId <= 0)
199         return;
200     ExecutionContextData* data = m_executionContextDataMap.get(context);
201     if (!data)
202         return;
203     data->m_intervalTimerIds.remove(timerId);
204     data->m_timerCallChains.remove(timerId);
205 }
206
207 void AsyncCallStackTracker::willFireTimer(ExecutionContext* context, int timerId)
208 {
209     ASSERT(context);
210     ASSERT(isEnabled());
211     ASSERT(timerId > 0);
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));
216         else
217             setCurrentAsyncCallChain(data->m_timerCallChains.take(timerId));
218     } else {
219         setCurrentAsyncCallChain(nullptr);
220     }
221 }
222
223 void AsyncCallStackTracker::didRequestAnimationFrame(ExecutionContext* context, int callbackId, const ScriptValue& callFrames)
224 {
225     ASSERT(context);
226     ASSERT(isEnabled());
227     if (!validateCallFrames(callFrames))
228         return;
229     ASSERT(callbackId > 0);
230     ExecutionContextData* data = createContextDataIfNeeded(context);
231     data->m_animationFrameCallChains.set(callbackId, createAsyncCallChain(requestAnimationFrameName, callFrames));
232 }
233
234 void AsyncCallStackTracker::didCancelAnimationFrame(ExecutionContext* context, int callbackId)
235 {
236     ASSERT(context);
237     ASSERT(isEnabled());
238     if (callbackId <= 0)
239         return;
240     if (ExecutionContextData* data = m_executionContextDataMap.get(context))
241         data->m_animationFrameCallChains.remove(callbackId);
242 }
243
244 void AsyncCallStackTracker::willFireAnimationFrame(ExecutionContext* context, int callbackId)
245 {
246     ASSERT(context);
247     ASSERT(isEnabled());
248     ASSERT(callbackId > 0);
249     ASSERT(!m_currentAsyncCallChain);
250     if (ExecutionContextData* data = m_executionContextDataMap.get(context))
251         setCurrentAsyncCallChain(data->m_animationFrameCallChains.take(callbackId));
252     else
253         setCurrentAsyncCallChain(nullptr);
254 }
255
256 void AsyncCallStackTracker::didAddEventListener(EventTarget* eventTarget, const AtomicString& eventType, EventListener* listener, bool useCapture, const ScriptValue& callFrames)
257 {
258     ASSERT(eventTarget->executionContext());
259     ASSERT(isEnabled());
260     if (!validateCallFrames(callFrames) || toXmlHttpRequest(eventTarget))
261         return;
262
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);
270     } else {
271         description.append("addEventListener(\"");
272         description.append(eventType);
273         description.append("\")");
274     }
275
276     ExecutionContextData* data = createContextDataIfNeeded(eventTarget->executionContext());
277     data->addEventListenerData(eventTarget, eventType, std::make_pair(RegisteredEventListener(listener, useCapture), createAsyncCallChain(description.toString(), callFrames)));
278 }
279
280 void AsyncCallStackTracker::didRemoveEventListener(EventTarget* eventTarget, const AtomicString& eventType, EventListener* listener, bool useCapture)
281 {
282     ASSERT(eventTarget->executionContext());
283     ASSERT(isEnabled());
284     if (ExecutionContextData* data = m_executionContextDataMap.get(eventTarget->executionContext()))
285         data->removeEventListenerData(eventTarget, eventType, RegisteredEventListener(listener, useCapture));
286 }
287
288 void AsyncCallStackTracker::didRemoveAllEventListeners(EventTarget* eventTarget)
289 {
290     ASSERT(eventTarget->executionContext());
291     ASSERT(isEnabled());
292     if (ExecutionContextData* data = m_executionContextDataMap.get(eventTarget->executionContext()))
293         data->m_eventTargetCallChains.remove(eventTarget);
294 }
295
296 void AsyncCallStackTracker::willHandleEvent(EventTarget* eventTarget, const AtomicString& eventType, EventListener* listener, bool useCapture)
297 {
298     ASSERT(eventTarget->executionContext());
299     ASSERT(isEnabled());
300     if (XMLHttpRequest* xhr = toXmlHttpRequest(eventTarget)) {
301         willHandleXHREvent(xhr, eventTarget, eventType);
302         return;
303     }
304     if (ExecutionContextData* data = m_executionContextDataMap.get(eventTarget->executionContext()))
305         setCurrentAsyncCallChain(data->findEventListenerData(eventTarget, eventType, RegisteredEventListener(listener, useCapture)));
306     else
307         setCurrentAsyncCallChain(nullptr);
308 }
309
310 void AsyncCallStackTracker::willLoadXHR(XMLHttpRequest* xhr, const ScriptValue& callFrames)
311 {
312     ASSERT(xhr->executionContext());
313     ASSERT(isEnabled());
314     if (!validateCallFrames(callFrames))
315         return;
316     ExecutionContextData* data = createContextDataIfNeeded(xhr->executionContext());
317     data->m_xhrCallChains.set(xhr, createAsyncCallChain(xhrSendName, callFrames));
318 }
319
320 void AsyncCallStackTracker::willHandleXHREvent(XMLHttpRequest* xhr, EventTarget* eventTarget, const AtomicString& eventType)
321 {
322     ASSERT(xhr->executionContext());
323     ASSERT(isEnabled());
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));
328         else
329             setCurrentAsyncCallChain(data->m_xhrCallChains.get(xhr));
330     } else {
331         setCurrentAsyncCallChain(nullptr);
332     }
333 }
334
335 void AsyncCallStackTracker::didEnqueueMutationRecord(ExecutionContext* context, MutationObserver* observer, const ScriptValue& callFrames)
336 {
337     ASSERT(context);
338     ASSERT(isEnabled());
339     if (!validateCallFrames(callFrames))
340         return;
341     ExecutionContextData* data = createContextDataIfNeeded(context);
342     data->m_mutationObserverCallChains.set(observer, createAsyncCallChain(enqueueMutationRecordName, callFrames));
343 }
344
345 bool AsyncCallStackTracker::hasEnqueuedMutationRecord(ExecutionContext* context, MutationObserver* observer)
346 {
347     ASSERT(context);
348     ASSERT(isEnabled());
349     if (ExecutionContextData* data = m_executionContextDataMap.get(context))
350         return data->m_mutationObserverCallChains.contains(observer);
351     return false;
352 }
353
354 void AsyncCallStackTracker::didClearAllMutationRecords(ExecutionContext* context, MutationObserver* observer)
355 {
356     ASSERT(context);
357     ASSERT(isEnabled());
358     if (ExecutionContextData* data = m_executionContextDataMap.get(context))
359         data->m_mutationObserverCallChains.remove(observer);
360 }
361
362 void AsyncCallStackTracker::willDeliverMutationRecords(ExecutionContext* context, MutationObserver* observer)
363 {
364     ASSERT(context);
365     ASSERT(isEnabled());
366     if (ExecutionContextData* data = m_executionContextDataMap.get(context))
367         setCurrentAsyncCallChain(data->m_mutationObserverCallChains.take(observer));
368     else
369         setCurrentAsyncCallChain(nullptr);
370 }
371
372 void AsyncCallStackTracker::didPostPromiseTask(ExecutionContext* context, ExecutionContextTask* task, bool isResolved, const ScriptValue& callFrames)
373 {
374     ASSERT(context);
375     ASSERT(isEnabled());
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);
383     }
384 }
385
386 void AsyncCallStackTracker::willPerformPromiseTask(ExecutionContext* context, ExecutionContextTask* task)
387 {
388     ASSERT(context);
389     ASSERT(isEnabled());
390     if (ExecutionContextData* data = m_executionContextDataMap.get(context))
391         setCurrentAsyncCallChain(data->m_promiseTaskCallChains.take(task));
392     else
393         setCurrentAsyncCallChain(nullptr);
394 }
395
396 void AsyncCallStackTracker::didFireAsyncCall()
397 {
398     clearCurrentAsyncCallChain();
399 }
400
401 PassRefPtr<AsyncCallStackTracker::AsyncCallChain> AsyncCallStackTracker::createAsyncCallChain(const String& description, const ScriptValue& callFrames)
402 {
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();
407 }
408
409 void AsyncCallStackTracker::setCurrentAsyncCallChain(PassRefPtr<AsyncCallChain> chain)
410 {
411     if (V8RecursionScope::recursionLevel(v8::Isolate::GetCurrent())) {
412         if (m_currentAsyncCallChain)
413             ++m_nestedAsyncCallCount;
414     } else {
415         // Current AsyncCallChain corresponds to the bottommost JS call frame.
416         m_currentAsyncCallChain = chain;
417         m_nestedAsyncCallCount = m_currentAsyncCallChain ? 1 : 0;
418     }
419 }
420
421 void AsyncCallStackTracker::clearCurrentAsyncCallChain()
422 {
423     if (!m_nestedAsyncCallCount)
424         return;
425     --m_nestedAsyncCallCount;
426     if (!m_nestedAsyncCallCount)
427         m_currentAsyncCallChain.clear();
428 }
429
430 void AsyncCallStackTracker::ensureMaxAsyncCallChainDepth(AsyncCallChain* chain, unsigned maxDepth)
431 {
432     while (chain->m_callStacks.size() > maxDepth)
433         chain->m_callStacks.removeLast();
434 }
435
436 bool AsyncCallStackTracker::validateCallFrames(const ScriptValue& callFrames)
437 {
438     return !callFrames.isEmpty();
439 }
440
441 AsyncCallStackTracker::ExecutionContextData* AsyncCallStackTracker::createContextDataIfNeeded(ExecutionContext* context)
442 {
443     ExecutionContextData* data = m_executionContextDataMap.get(context);
444     if (!data) {
445         data = new AsyncCallStackTracker::ExecutionContextData(this, context);
446         m_executionContextDataMap.set(context, data);
447     }
448     return data;
449 }
450
451 void AsyncCallStackTracker::clear()
452 {
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)
458         delete it->value;
459 }
460
461 } // namespace WebCore