Upstream version 9.38.198.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/core/v8/V8Binding.h"
35 #include "bindings/core/v8/V8RecursionScope.h"
36 #include "core/dom/ExecutionContext.h"
37 #include "core/dom/ExecutionContextTask.h"
38 #include "core/events/Event.h"
39 #include "core/events/EventTarget.h"
40 #include "core/xml/XMLHttpRequest.h"
41 #include "core/xml/XMLHttpRequestUpload.h"
42 #include "wtf/text/StringBuilder.h"
43 #include "wtf/text/StringHash.h"
44 #include <v8.h>
45
46 namespace {
47
48 static const char setTimeoutName[] = "setTimeout";
49 static const char setIntervalName[] = "setInterval";
50 static const char requestAnimationFrameName[] = "requestAnimationFrame";
51 static const char xhrSendName[] = "XMLHttpRequest.send";
52 static const char enqueueMutationRecordName[] = "Mutation";
53
54 }
55
56 namespace blink {
57
58 void AsyncCallStackTracker::ExecutionContextData::contextDestroyed()
59 {
60     ASSERT(executionContext());
61     OwnPtrWillBeRawPtr<ExecutionContextData> self = m_tracker->m_executionContextDataMap.take(executionContext());
62     ASSERT_UNUSED(self, self == this);
63     ContextLifecycleObserver::contextDestroyed();
64 }
65
66 int AsyncCallStackTracker::ExecutionContextData::circularSequentialID()
67 {
68     ++m_circularSequentialID;
69     if (m_circularSequentialID <= 0)
70         m_circularSequentialID = 1;
71     return m_circularSequentialID;
72 }
73
74 void AsyncCallStackTracker::ExecutionContextData::trace(Visitor* visitor)
75 {
76     visitor->trace(m_tracker);
77 #if ENABLE(OILPAN)
78     visitor->trace(m_timerCallChains);
79     visitor->trace(m_animationFrameCallChains);
80     visitor->trace(m_eventCallChains);
81     visitor->trace(m_xhrCallChains);
82     visitor->trace(m_mutationObserverCallChains);
83     visitor->trace(m_executionContextTaskCallChains);
84     visitor->trace(m_v8AsyncTaskCallChains);
85     visitor->trace(m_asyncOperationCallChains);
86 #endif
87 }
88
89 static XMLHttpRequest* toXmlHttpRequest(EventTarget* eventTarget)
90 {
91     const AtomicString& interfaceName = eventTarget->interfaceName();
92     if (interfaceName == EventTargetNames::XMLHttpRequest)
93         return static_cast<XMLHttpRequest*>(eventTarget);
94     if (interfaceName == EventTargetNames::XMLHttpRequestUpload)
95         return static_cast<XMLHttpRequestUpload*>(eventTarget)->xmlHttpRequest();
96     return 0;
97 }
98
99 void AsyncCallStackTracker::AsyncCallChain::trace(Visitor* visitor)
100 {
101     visitor->trace(m_callStacks);
102 }
103
104 AsyncCallStackTracker::AsyncCallStack::AsyncCallStack(const String& description, const ScriptValue& callFrames)
105     : m_description(description)
106     , m_callFrames(callFrames)
107 {
108 }
109
110 AsyncCallStackTracker::AsyncCallStack::~AsyncCallStack()
111 {
112 }
113
114 AsyncCallStackTracker::AsyncCallStackTracker()
115     : m_maxAsyncCallStackDepth(0)
116     , m_nestedAsyncCallCount(0)
117 {
118 }
119
120 void AsyncCallStackTracker::setAsyncCallStackDepth(int depth)
121 {
122     if (depth <= 0) {
123         m_maxAsyncCallStackDepth = 0;
124         clear();
125     } else {
126         m_maxAsyncCallStackDepth = depth;
127     }
128 }
129
130 const AsyncCallStackTracker::AsyncCallChain* AsyncCallStackTracker::currentAsyncCallChain() const
131 {
132     if (m_currentAsyncCallChain)
133         ensureMaxAsyncCallChainDepth(m_currentAsyncCallChain.get(), m_maxAsyncCallStackDepth);
134     return m_currentAsyncCallChain.get();
135 }
136
137 void AsyncCallStackTracker::didInstallTimer(ExecutionContext* context, int timerId, bool singleShot, const ScriptValue& callFrames)
138 {
139     ASSERT(context);
140     ASSERT(isEnabled());
141     if (!validateCallFrames(callFrames))
142         return;
143     ASSERT(timerId > 0);
144     ExecutionContextData* data = createContextDataIfNeeded(context);
145     data->m_timerCallChains.set(timerId, createAsyncCallChain(singleShot ? setTimeoutName : setIntervalName, callFrames));
146     if (!singleShot)
147         data->m_intervalTimerIds.add(timerId);
148 }
149
150 void AsyncCallStackTracker::didRemoveTimer(ExecutionContext* context, int timerId)
151 {
152     ASSERT(context);
153     ASSERT(isEnabled());
154     if (timerId <= 0)
155         return;
156     ExecutionContextData* data = m_executionContextDataMap.get(context);
157     if (!data)
158         return;
159     data->m_intervalTimerIds.remove(timerId);
160     data->m_timerCallChains.remove(timerId);
161 }
162
163 void AsyncCallStackTracker::willFireTimer(ExecutionContext* context, int timerId)
164 {
165     ASSERT(context);
166     ASSERT(isEnabled());
167     ASSERT(timerId > 0);
168     ASSERT(!m_currentAsyncCallChain);
169     if (ExecutionContextData* data = m_executionContextDataMap.get(context)) {
170         if (data->m_intervalTimerIds.contains(timerId))
171             setCurrentAsyncCallChain(context, data->m_timerCallChains.get(timerId));
172         else
173             setCurrentAsyncCallChain(context, data->m_timerCallChains.take(timerId));
174     } else {
175         setCurrentAsyncCallChain(context, nullptr);
176     }
177 }
178
179 void AsyncCallStackTracker::didRequestAnimationFrame(ExecutionContext* context, int callbackId, const ScriptValue& callFrames)
180 {
181     ASSERT(context);
182     ASSERT(isEnabled());
183     if (!validateCallFrames(callFrames))
184         return;
185     ASSERT(callbackId > 0);
186     ExecutionContextData* data = createContextDataIfNeeded(context);
187     data->m_animationFrameCallChains.set(callbackId, createAsyncCallChain(requestAnimationFrameName, callFrames));
188 }
189
190 void AsyncCallStackTracker::didCancelAnimationFrame(ExecutionContext* context, int callbackId)
191 {
192     ASSERT(context);
193     ASSERT(isEnabled());
194     if (callbackId <= 0)
195         return;
196     if (ExecutionContextData* data = m_executionContextDataMap.get(context))
197         data->m_animationFrameCallChains.remove(callbackId);
198 }
199
200 void AsyncCallStackTracker::willFireAnimationFrame(ExecutionContext* context, int callbackId)
201 {
202     ASSERT(context);
203     ASSERT(isEnabled());
204     ASSERT(callbackId > 0);
205     ASSERT(!m_currentAsyncCallChain);
206     if (ExecutionContextData* data = m_executionContextDataMap.get(context))
207         setCurrentAsyncCallChain(context, data->m_animationFrameCallChains.take(callbackId));
208     else
209         setCurrentAsyncCallChain(context, nullptr);
210 }
211
212 void AsyncCallStackTracker::didEnqueueEvent(EventTarget* eventTarget, Event* event, const ScriptValue& callFrames)
213 {
214     ASSERT(eventTarget->executionContext());
215     ASSERT(isEnabled());
216     if (!validateCallFrames(callFrames))
217         return;
218     ExecutionContextData* data = createContextDataIfNeeded(eventTarget->executionContext());
219     data->m_eventCallChains.set(event, createAsyncCallChain(event->type(), callFrames));
220 }
221
222 void AsyncCallStackTracker::didRemoveEvent(EventTarget* eventTarget, Event* event)
223 {
224     ASSERT(eventTarget->executionContext());
225     ASSERT(isEnabled());
226     if (ExecutionContextData* data = m_executionContextDataMap.get(eventTarget->executionContext()))
227         data->m_eventCallChains.remove(event);
228 }
229
230 void AsyncCallStackTracker::willHandleEvent(EventTarget* eventTarget, Event* event, EventListener* listener, bool useCapture)
231 {
232     ASSERT(eventTarget->executionContext());
233     ASSERT(isEnabled());
234     if (XMLHttpRequest* xhr = toXmlHttpRequest(eventTarget)) {
235         willHandleXHREvent(xhr, event);
236     } else {
237         ExecutionContext* context = eventTarget->executionContext();
238         if (ExecutionContextData* data = m_executionContextDataMap.get(context))
239             setCurrentAsyncCallChain(context, data->m_eventCallChains.get(event));
240         else
241             setCurrentAsyncCallChain(context, nullptr);
242     }
243 }
244
245 void AsyncCallStackTracker::willLoadXHR(XMLHttpRequest* xhr, const ScriptValue& callFrames)
246 {
247     ASSERT(xhr->executionContext());
248     ASSERT(isEnabled());
249     if (!validateCallFrames(callFrames))
250         return;
251     ExecutionContextData* data = createContextDataIfNeeded(xhr->executionContext());
252     data->m_xhrCallChains.set(xhr, createAsyncCallChain(xhrSendName, callFrames));
253 }
254
255 void AsyncCallStackTracker::didLoadXHR(XMLHttpRequest* xhr)
256 {
257     ASSERT(xhr->executionContext());
258     ASSERT(isEnabled());
259     if (ExecutionContextData* data = m_executionContextDataMap.get(xhr->executionContext()))
260         data->m_xhrCallChains.remove(xhr);
261 }
262
263 void AsyncCallStackTracker::willHandleXHREvent(XMLHttpRequest* xhr, Event* event)
264 {
265     ExecutionContext* context = xhr->executionContext();
266     ASSERT(context);
267     ASSERT(isEnabled());
268     if (ExecutionContextData* data = m_executionContextDataMap.get(context))
269         setCurrentAsyncCallChain(context, data->m_xhrCallChains.get(xhr));
270     else
271         setCurrentAsyncCallChain(context, nullptr);
272 }
273
274 void AsyncCallStackTracker::didEnqueueMutationRecord(ExecutionContext* context, MutationObserver* observer, const ScriptValue& callFrames)
275 {
276     ASSERT(context);
277     ASSERT(isEnabled());
278     if (!validateCallFrames(callFrames))
279         return;
280     ExecutionContextData* data = createContextDataIfNeeded(context);
281     data->m_mutationObserverCallChains.set(observer, createAsyncCallChain(enqueueMutationRecordName, callFrames));
282 }
283
284 bool AsyncCallStackTracker::hasEnqueuedMutationRecord(ExecutionContext* context, MutationObserver* observer)
285 {
286     ASSERT(context);
287     ASSERT(isEnabled());
288     if (ExecutionContextData* data = m_executionContextDataMap.get(context))
289         return data->m_mutationObserverCallChains.contains(observer);
290     return false;
291 }
292
293 void AsyncCallStackTracker::didClearAllMutationRecords(ExecutionContext* context, MutationObserver* observer)
294 {
295     ASSERT(context);
296     ASSERT(isEnabled());
297     if (ExecutionContextData* data = m_executionContextDataMap.get(context))
298         data->m_mutationObserverCallChains.remove(observer);
299 }
300
301 void AsyncCallStackTracker::willDeliverMutationRecords(ExecutionContext* context, MutationObserver* observer)
302 {
303     ASSERT(context);
304     ASSERT(isEnabled());
305     if (ExecutionContextData* data = m_executionContextDataMap.get(context))
306         setCurrentAsyncCallChain(context, data->m_mutationObserverCallChains.take(observer));
307     else
308         setCurrentAsyncCallChain(context, nullptr);
309 }
310
311 void AsyncCallStackTracker::didPostExecutionContextTask(ExecutionContext* context, ExecutionContextTask* task, const ScriptValue& callFrames)
312 {
313     ASSERT(context);
314     ASSERT(isEnabled());
315     if (!validateCallFrames(callFrames))
316         return;
317     ExecutionContextData* data = createContextDataIfNeeded(context);
318     data->m_executionContextTaskCallChains.set(task, createAsyncCallChain(task->taskNameForInstrumentation(), callFrames));
319 }
320
321 void AsyncCallStackTracker::didKillAllExecutionContextTasks(ExecutionContext* context)
322 {
323     ASSERT(context);
324     ASSERT(isEnabled());
325     if (ExecutionContextData* data = m_executionContextDataMap.get(context))
326         data->m_executionContextTaskCallChains.clear();
327 }
328
329 void AsyncCallStackTracker::willPerformExecutionContextTask(ExecutionContext* context, ExecutionContextTask* task)
330 {
331     ASSERT(context);
332     ASSERT(isEnabled());
333     if (ExecutionContextData* data = m_executionContextDataMap.get(context))
334         setCurrentAsyncCallChain(context, data->m_executionContextTaskCallChains.take(task));
335     else
336         setCurrentAsyncCallChain(context, nullptr);
337 }
338
339 static String makeV8AsyncTaskUniqueId(const String& eventName, int id)
340 {
341     StringBuilder builder;
342     builder.append(eventName);
343     builder.appendNumber(id);
344     return builder.toString();
345 }
346
347 void AsyncCallStackTracker::didEnqueueV8AsyncTask(ExecutionContext* context, const String& eventName, int id, const ScriptValue& callFrames)
348 {
349     ASSERT(context);
350     ASSERT(isEnabled());
351     if (!validateCallFrames(callFrames))
352         return;
353     ExecutionContextData* data = createContextDataIfNeeded(context);
354     data->m_v8AsyncTaskCallChains.set(makeV8AsyncTaskUniqueId(eventName, id), createAsyncCallChain(eventName, callFrames));
355 }
356
357 void AsyncCallStackTracker::willHandleV8AsyncTask(ExecutionContext* context, const String& eventName, int id)
358 {
359     ASSERT(context);
360     ASSERT(isEnabled());
361     if (ExecutionContextData* data = m_executionContextDataMap.get(context))
362         setCurrentAsyncCallChain(context, data->m_v8AsyncTaskCallChains.take(makeV8AsyncTaskUniqueId(eventName, id)));
363     else
364         setCurrentAsyncCallChain(context, nullptr);
365 }
366
367 int AsyncCallStackTracker::traceAsyncOperationStarting(ExecutionContext* context, const String& operationName, const ScriptValue& callFrames)
368 {
369     ASSERT(context);
370     ASSERT(isEnabled());
371     if (!validateCallFrames(callFrames))
372         return 0;
373     ExecutionContextData* data = createContextDataIfNeeded(context);
374     int id = data->circularSequentialID();
375     while (data->m_asyncOperationCallChains.contains(id))
376         id = data->circularSequentialID();
377     data->m_asyncOperationCallChains.set(id, createAsyncCallChain(operationName, callFrames));
378     return id;
379 }
380
381 void AsyncCallStackTracker::traceAsyncOperationCompleted(ExecutionContext* context, int operationId)
382 {
383     ASSERT(context);
384     ASSERT(isEnabled());
385     if (operationId <= 0)
386         return;
387     if (ExecutionContextData* data = m_executionContextDataMap.get(context))
388         data->m_asyncOperationCallChains.remove(operationId);
389 }
390
391 void AsyncCallStackTracker::traceAsyncCallbackStarting(ExecutionContext* context, int operationId)
392 {
393     ASSERT(context);
394     ASSERT(isEnabled());
395     if (ExecutionContextData* data = m_executionContextDataMap.get(context))
396         setCurrentAsyncCallChain(context, operationId > 0 ? data->m_asyncOperationCallChains.get(operationId) : nullptr);
397     else
398         setCurrentAsyncCallChain(context, nullptr);
399 }
400
401 void AsyncCallStackTracker::didFireAsyncCall()
402 {
403     clearCurrentAsyncCallChain();
404 }
405
406 PassRefPtrWillBeRawPtr<AsyncCallStackTracker::AsyncCallChain> AsyncCallStackTracker::createAsyncCallChain(const String& description, const ScriptValue& callFrames)
407 {
408     if (callFrames.isEmpty()) {
409         ASSERT(m_currentAsyncCallChain);
410         return m_currentAsyncCallChain; // Propogate async call stack chain.
411     }
412     RefPtrWillBeRawPtr<AsyncCallChain> chain = adoptRefWillBeNoop(m_currentAsyncCallChain ? new AsyncCallStackTracker::AsyncCallChain(*m_currentAsyncCallChain) : new AsyncCallStackTracker::AsyncCallChain());
413     ensureMaxAsyncCallChainDepth(chain.get(), m_maxAsyncCallStackDepth - 1);
414     chain->m_callStacks.prepend(adoptRefWillBeNoop(new AsyncCallStackTracker::AsyncCallStack(description, callFrames)));
415     return chain.release();
416 }
417
418 void AsyncCallStackTracker::setCurrentAsyncCallChain(ExecutionContext* context, PassRefPtrWillBeRawPtr<AsyncCallChain> chain)
419 {
420     if (chain && !V8RecursionScope::recursionLevel(toIsolate(context))) {
421         // Current AsyncCallChain corresponds to the bottommost JS call frame.
422         m_currentAsyncCallChain = chain;
423         m_nestedAsyncCallCount = 1;
424     } else {
425         if (m_currentAsyncCallChain)
426             ++m_nestedAsyncCallCount;
427     }
428 }
429
430 void AsyncCallStackTracker::clearCurrentAsyncCallChain()
431 {
432     if (!m_nestedAsyncCallCount)
433         return;
434     --m_nestedAsyncCallCount;
435     if (!m_nestedAsyncCallCount)
436         m_currentAsyncCallChain.clear();
437 }
438
439 void AsyncCallStackTracker::ensureMaxAsyncCallChainDepth(AsyncCallChain* chain, unsigned maxDepth)
440 {
441     while (chain->m_callStacks.size() > maxDepth)
442         chain->m_callStacks.removeLast();
443 }
444
445 bool AsyncCallStackTracker::validateCallFrames(const ScriptValue& callFrames)
446 {
447     return !callFrames.isEmpty() || m_currentAsyncCallChain;
448 }
449
450 AsyncCallStackTracker::ExecutionContextData* AsyncCallStackTracker::createContextDataIfNeeded(ExecutionContext* context)
451 {
452     ExecutionContextData* data = m_executionContextDataMap.get(context);
453     if (!data) {
454         data = m_executionContextDataMap.set(context, adoptPtrWillBeNoop(new AsyncCallStackTracker::ExecutionContextData(this, context)))
455             .storedValue->value.get();
456     }
457     return data;
458 }
459
460 void AsyncCallStackTracker::clear()
461 {
462     m_currentAsyncCallChain.clear();
463     m_nestedAsyncCallCount = 0;
464     m_executionContextDataMap.clear();
465 }
466
467 void AsyncCallStackTracker::trace(Visitor* visitor)
468 {
469     visitor->trace(m_currentAsyncCallChain);
470 #if ENABLE(OILPAN)
471     visitor->trace(m_executionContextDataMap);
472 #endif
473 }
474
475 } // namespace blink