ecc120a8d1022ffd5e85547bf4fb9509d9aa11ea
[WebKit-https.git] / Source / WebCore / inspector / InspectorTimelineAgent.cpp
1 /*
2 * Copyright (C) 2013 Google Inc. All rights reserved.
3 * Copyright (C) 2014 University of Washington.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 *     * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *     * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 *     * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include "config.h"
33 #include "InspectorTimelineAgent.h"
34
35 #include "Event.h"
36 #include "Frame.h"
37 #include "FrameView.h"
38 #include "InspectorClient.h"
39 #include "InspectorInstrumentation.h"
40 #include "InspectorPageAgent.h"
41 #include "InstrumentingAgents.h"
42 #include "IntRect.h"
43 #include "JSDOMWindow.h"
44 #include "MainFrame.h"
45 #include "PageScriptDebugServer.h"
46 #include "RenderElement.h"
47 #include "RenderView.h"
48 #include "ResourceRequest.h"
49 #include "ResourceResponse.h"
50 #include "ScriptState.h"
51 #include "TimelineRecordFactory.h"
52 #include <inspector/IdentifiersFactory.h>
53 #include <inspector/ScriptBreakpoint.h>
54 #include <profiler/LegacyProfiler.h>
55 #include <wtf/CurrentTime.h>
56
57 #if PLATFORM(IOS)
58 #include "RuntimeApplicationChecksIOS.h"
59 #include <WebCore/WebCoreThread.h>
60 #endif
61
62 #if PLATFORM(COCOA)
63 #include <WebCore/RunLoopObserver.h>
64 #endif
65
66 using namespace Inspector;
67
68 namespace WebCore {
69
70 #if PLATFORM(COCOA)
71 static const CFIndex frameStopRunLoopOrder = (CFIndex)RunLoopObserver::WellKnownRunLoopOrders::CoreAnimationCommit + 1;
72
73 static CFRunLoopRef currentRunLoop()
74 {
75 #if PLATFORM(IOS)
76     // A race condition during WebView deallocation can lead to a crash if the layer sync run loop
77     // observer is added to the main run loop <rdar://problem/9798550>. However, for responsiveness,
78     // we still allow this, see <rdar://problem/7403328>. Since the race condition and subsequent
79     // crash are especially troublesome for iBooks, we never allow the observer to be added to the
80     // main run loop in iBooks.
81     if (applicationIsIBooksOnIOS())
82         return WebThreadRunLoop();
83 #endif
84     return CFRunLoopGetCurrent();
85 }
86 #endif
87
88 InspectorTimelineAgent::~InspectorTimelineAgent()
89 {
90 }
91
92 void InspectorTimelineAgent::didCreateFrontendAndBackend(Inspector::FrontendChannel* frontendChannel, Inspector::BackendDispatcher* backendDispatcher)
93 {
94     m_frontendDispatcher = std::make_unique<Inspector::TimelineFrontendDispatcher>(frontendChannel);
95     m_backendDispatcher = Inspector::TimelineBackendDispatcher::create(backendDispatcher, this);
96
97     m_instrumentingAgents->setPersistentInspectorTimelineAgent(this);
98
99     if (m_scriptDebugServer)
100         m_scriptDebugServer->recompileAllJSFunctions();
101 }
102
103 void InspectorTimelineAgent::willDestroyFrontendAndBackend(Inspector::DisconnectReason reason)
104 {
105     m_frontendDispatcher = nullptr;
106     m_backendDispatcher.clear();
107
108     m_instrumentingAgents->setPersistentInspectorTimelineAgent(nullptr);
109
110     if (reason != Inspector::DisconnectReason::InspectedTargetDestroyed) {
111         if (m_scriptDebugServer)
112             m_scriptDebugServer->recompileAllJSFunctions();
113     }
114
115     ErrorString unused;
116     stop(unused);
117 }
118
119 void InspectorTimelineAgent::start(ErrorString&, const int* maxCallStackDepth)
120 {
121     m_enabledFromFrontend = true;
122
123     internalStart(maxCallStackDepth);
124 }
125
126 void InspectorTimelineAgent::stop(ErrorString&)
127 {
128     internalStop();
129
130     m_enabledFromFrontend = false;
131 }
132
133 void InspectorTimelineAgent::internalStart(const int* maxCallStackDepth)
134 {
135     if (m_enabled)
136         return;
137
138     if (maxCallStackDepth && *maxCallStackDepth > 0)
139         m_maxCallStackDepth = *maxCallStackDepth;
140     else
141         m_maxCallStackDepth = 5;
142
143     m_instrumentingAgents->inspectorEnvironment().executionStopwatch()->start();
144
145     m_instrumentingAgents->setInspectorTimelineAgent(this);
146
147     if (m_scriptDebugServer)
148         m_scriptDebugServer->addListener(this);
149
150     m_enabled = true;
151
152     // FIXME: Abstract away platform-specific code once https://bugs.webkit.org/show_bug.cgi?id=142748 is fixed.
153
154 #if PLATFORM(COCOA)
155     m_frameStartObserver = RunLoopObserver::create(0, [this]() {
156         if (!m_enabled || m_didStartRecordingRunLoop)
157             return;
158
159         pushCurrentRecord(InspectorObject::create(), TimelineRecordType::RunLoop, false, nullptr);
160         m_didStartRecordingRunLoop = true;
161     });
162
163     m_frameStopObserver = RunLoopObserver::create(frameStopRunLoopOrder, [this]() {
164         if (!m_enabled || !m_didStartRecordingRunLoop)
165             return;
166
167         didCompleteCurrentRecord(TimelineRecordType::RunLoop);
168         m_didStartRecordingRunLoop = false;
169     });
170
171     m_frameStartObserver->schedule(currentRunLoop(), kCFRunLoopAfterWaiting | kCFRunLoopBeforeTimers);
172     m_frameStopObserver->schedule(currentRunLoop(), kCFRunLoopBeforeWaiting | kCFRunLoopExit);
173
174     // Create a runloop record immediately in order to capture the rest of the current runloop.
175     pushCurrentRecord(InspectorObject::create(), TimelineRecordType::RunLoop, false, nullptr);
176     m_didStartRecordingRunLoop = true;
177 #endif
178
179     if (m_frontendDispatcher)
180         m_frontendDispatcher->recordingStarted();
181 }
182
183 void InspectorTimelineAgent::internalStop()
184 {
185     if (!m_enabled)
186         return;
187
188     // The environment's stopwatch could be already stopped if the debugger has paused.
189     auto stopwatch = m_instrumentingAgents->inspectorEnvironment().executionStopwatch();
190     if (stopwatch->isActive())
191         stopwatch->stop();
192
193     m_instrumentingAgents->setInspectorTimelineAgent(nullptr);
194
195     if (m_scriptDebugServer)
196         m_scriptDebugServer->removeListener(this, true);
197
198 #if PLATFORM(COCOA)
199     m_frameStartObserver = nullptr;
200     m_frameStopObserver = nullptr;
201 #endif
202
203     clearRecordStack();
204
205     m_enabled = false;
206
207     if (m_frontendDispatcher)
208         m_frontendDispatcher->recordingStopped();
209 }
210
211 double InspectorTimelineAgent::timestamp()
212 {
213     return m_instrumentingAgents->inspectorEnvironment().executionStopwatch()->elapsedTime();
214 }
215
216 void InspectorTimelineAgent::setPageScriptDebugServer(PageScriptDebugServer* scriptDebugServer)
217 {
218     ASSERT(!m_enabled);
219     ASSERT(!m_scriptDebugServer);
220
221     m_scriptDebugServer = scriptDebugServer;
222 }
223
224 static inline void startProfiling(JSC::ExecState* exec, const String& title, PassRefPtr<Stopwatch> stopwatch)
225 {
226     JSC::LegacyProfiler::profiler()->startProfiling(exec, title, stopwatch);
227 }
228
229 static inline PassRefPtr<JSC::Profile> stopProfiling(JSC::ExecState* exec, const String& title)
230 {
231     return JSC::LegacyProfiler::profiler()->stopProfiling(exec, title);
232 }
233
234 static inline void startProfiling(Frame* frame, const String& title, PassRefPtr<Stopwatch> stopwatch)
235 {
236     startProfiling(toJSDOMWindow(frame, debuggerWorld())->globalExec(), title, stopwatch);
237 }
238
239 static inline PassRefPtr<JSC::Profile> stopProfiling(Frame* frame, const String& title)
240 {
241     return stopProfiling(toJSDOMWindow(frame, debuggerWorld())->globalExec(), title);
242 }
243
244 void InspectorTimelineAgent::startFromConsole(JSC::ExecState* exec, const String &title)
245 {
246     // Only allow recording of a profile if it is anonymous (empty title) or does not match
247     // the title of an already recording profile.
248     if (!title.isEmpty()) {
249         for (const TimelineRecordEntry& record : m_pendingConsoleProfileRecords) {
250             String recordTitle;
251             record.data->getString(ASCIILiteral("title"), recordTitle);
252             if (recordTitle == title)
253                 return;
254         }
255     }
256
257     if (!m_enabled && m_pendingConsoleProfileRecords.isEmpty())
258         internalStart();
259
260     // Use an independent stopwatch for console-initiated profiling, since the user will expect it
261     // to be relative to when their command was issued.
262     Ref<Stopwatch> profilerStopwatch = Stopwatch::create();
263     profilerStopwatch->start();
264     startProfiling(exec, title, WTF::move(profilerStopwatch));
265
266     m_pendingConsoleProfileRecords.append(createRecordEntry(TimelineRecordFactory::createConsoleProfileData(title), TimelineRecordType::ConsoleProfile, true, frameFromExecState(exec)));
267 }
268
269 PassRefPtr<JSC::Profile> InspectorTimelineAgent::stopFromConsole(JSC::ExecState* exec, const String& title)
270 {
271     // Stop profiles in reverse order. If the title is empty, then stop the last profile.
272     // Otherwise, match the title of the profile to stop.
273     for (ptrdiff_t i = m_pendingConsoleProfileRecords.size() - 1; i >= 0; --i) {
274         const TimelineRecordEntry& record = m_pendingConsoleProfileRecords[i];
275
276         String recordTitle;
277         record.data->getString(ASCIILiteral("title"), recordTitle);
278
279         if (title.isEmpty() || recordTitle == title) {
280             RefPtr<JSC::Profile> profile = stopProfiling(exec, title);
281             if (profile)
282                 TimelineRecordFactory::appendProfile(record.data.get(), profile.copyRef());
283
284             didCompleteRecordEntry(record);
285
286             m_pendingConsoleProfileRecords.remove(i);
287
288             if (!m_enabledFromFrontend && m_pendingConsoleProfileRecords.isEmpty())
289                 internalStop();
290
291             return WTF::move(profile);
292         }
293     }
294
295     return nullptr;
296 }
297
298 void InspectorTimelineAgent::willCallFunction(const String& scriptName, int scriptLine, Frame* frame)
299 {
300     pushCurrentRecord(TimelineRecordFactory::createFunctionCallData(scriptName, scriptLine), TimelineRecordType::FunctionCall, true, frame);
301
302     if (frame && !m_callStackDepth)
303         startProfiling(frame, ASCIILiteral("Timeline FunctionCall"), m_instrumentingAgents->inspectorEnvironment().executionStopwatch());
304
305     ++m_callStackDepth;
306 }
307
308 void InspectorTimelineAgent::didCallFunction(Frame* frame)
309 {
310     if (frame && m_callStackDepth) {
311         --m_callStackDepth;
312         ASSERT(m_callStackDepth >= 0);
313
314         if (!m_callStackDepth) {
315             if (m_recordStack.isEmpty())
316                 return;
317
318             TimelineRecordEntry& entry = m_recordStack.last();
319             ASSERT(entry.type == TimelineRecordType::FunctionCall);
320
321             RefPtr<JSC::Profile> profile = stopProfiling(frame, ASCIILiteral("Timeline FunctionCall"));
322             if (profile)
323                 TimelineRecordFactory::appendProfile(entry.data.get(), profile.release());
324         }
325     }
326
327     didCompleteCurrentRecord(TimelineRecordType::FunctionCall);
328 }
329
330 void InspectorTimelineAgent::willDispatchEvent(const Event& event, Frame* frame)
331 {
332     pushCurrentRecord(TimelineRecordFactory::createEventDispatchData(event), TimelineRecordType::EventDispatch, false, frame);
333 }
334
335 void InspectorTimelineAgent::didDispatchEvent()
336 {
337     didCompleteCurrentRecord(TimelineRecordType::EventDispatch);
338 }
339
340 void InspectorTimelineAgent::didInvalidateLayout(Frame& frame)
341 {
342     appendRecord(InspectorObject::create(), TimelineRecordType::InvalidateLayout, true, &frame);
343 }
344
345 void InspectorTimelineAgent::willLayout(Frame& frame)
346 {
347     RenderObject* root = frame.view()->layoutRoot();
348     bool partialLayout = !!root;
349
350     if (!partialLayout)
351         root = frame.contentRenderer();
352
353     unsigned dirtyObjects = 0;
354     unsigned totalObjects = 0;
355     for (RenderObject* o = root; o; o = o->nextInPreOrder(root)) {
356         ++totalObjects;
357         if (o->needsLayout())
358             ++dirtyObjects;
359     }
360     pushCurrentRecord(TimelineRecordFactory::createLayoutData(dirtyObjects, totalObjects, partialLayout), TimelineRecordType::Layout, true, &frame);
361 }
362
363 void InspectorTimelineAgent::didLayout(RenderObject* root)
364 {
365     if (m_recordStack.isEmpty())
366         return;
367     TimelineRecordEntry& entry = m_recordStack.last();
368     ASSERT(entry.type == TimelineRecordType::Layout);
369     Vector<FloatQuad> quads;
370     root->absoluteQuads(quads);
371     if (quads.size() >= 1)
372         TimelineRecordFactory::appendLayoutRoot(entry.data.get(), quads[0]);
373     else
374         ASSERT_NOT_REACHED();
375     didCompleteCurrentRecord(TimelineRecordType::Layout);
376 }
377
378 void InspectorTimelineAgent::didScheduleStyleRecalculation(Frame* frame)
379 {
380     appendRecord(InspectorObject::create(), TimelineRecordType::ScheduleStyleRecalculation, true, frame);
381 }
382
383 void InspectorTimelineAgent::willRecalculateStyle(Frame* frame)
384 {
385     pushCurrentRecord(InspectorObject::create(), TimelineRecordType::RecalculateStyles, true, frame);
386 }
387
388 void InspectorTimelineAgent::didRecalculateStyle()
389 {
390     didCompleteCurrentRecord(TimelineRecordType::RecalculateStyles);
391 }
392
393 void InspectorTimelineAgent::willPaint(Frame& frame)
394 {
395     pushCurrentRecord(InspectorObject::create(), TimelineRecordType::Paint, true, &frame);
396 }
397
398 void InspectorTimelineAgent::didPaint(RenderObject* renderer, const LayoutRect& clipRect)
399 {
400     TimelineRecordEntry& entry = m_recordStack.last();
401     ASSERT(entry.type == TimelineRecordType::Paint);
402     FloatQuad quad;
403     localToPageQuad(*renderer, clipRect, &quad);
404     entry.data = TimelineRecordFactory::createPaintData(quad);
405     didCompleteCurrentRecord(TimelineRecordType::Paint);
406 }
407
408 void InspectorTimelineAgent::willScroll(Frame& frame)
409 {
410     pushCurrentRecord(InspectorObject::create(), TimelineRecordType::ScrollLayer, false, &frame);
411 }
412
413 void InspectorTimelineAgent::didScroll()
414 {
415     didCompleteCurrentRecord(TimelineRecordType::ScrollLayer);
416 }
417
418 void InspectorTimelineAgent::willWriteHTML(unsigned startLine, Frame* frame)
419 {
420     pushCurrentRecord(TimelineRecordFactory::createParseHTMLData(startLine), TimelineRecordType::ParseHTML, true, frame);
421 }
422
423 void InspectorTimelineAgent::didWriteHTML(unsigned endLine)
424 {
425     if (!m_recordStack.isEmpty()) {
426         const TimelineRecordEntry& entry = m_recordStack.last();
427         entry.data->setInteger("endLine", endLine);
428         didCompleteCurrentRecord(TimelineRecordType::ParseHTML);
429     }
430 }
431
432 void InspectorTimelineAgent::didInstallTimer(int timerId, int timeout, bool singleShot, Frame* frame)
433 {
434     appendRecord(TimelineRecordFactory::createTimerInstallData(timerId, timeout, singleShot), TimelineRecordType::TimerInstall, true, frame);
435 }
436
437 void InspectorTimelineAgent::didRemoveTimer(int timerId, Frame* frame)
438 {
439     appendRecord(TimelineRecordFactory::createGenericTimerData(timerId), TimelineRecordType::TimerRemove, true, frame);
440 }
441
442 void InspectorTimelineAgent::willFireTimer(int timerId, Frame* frame)
443 {
444     pushCurrentRecord(TimelineRecordFactory::createGenericTimerData(timerId), TimelineRecordType::TimerFire, false, frame);
445 }
446
447 void InspectorTimelineAgent::didFireTimer()
448 {
449     didCompleteCurrentRecord(TimelineRecordType::TimerFire);
450 }
451
452 void InspectorTimelineAgent::willDispatchXHRReadyStateChangeEvent(const String& url, int readyState, Frame* frame)
453 {
454     pushCurrentRecord(TimelineRecordFactory::createXHRReadyStateChangeData(url, readyState), TimelineRecordType::XHRReadyStateChange, false, frame);
455 }
456
457 void InspectorTimelineAgent::didDispatchXHRReadyStateChangeEvent()
458 {
459     didCompleteCurrentRecord(TimelineRecordType::XHRReadyStateChange);
460 }
461
462 void InspectorTimelineAgent::willDispatchXHRLoadEvent(const String& url, Frame* frame)
463 {
464     pushCurrentRecord(TimelineRecordFactory::createXHRLoadData(url), TimelineRecordType::XHRLoad, true, frame);
465 }
466
467 void InspectorTimelineAgent::didDispatchXHRLoadEvent()
468 {
469     didCompleteCurrentRecord(TimelineRecordType::XHRLoad);
470 }
471
472 void InspectorTimelineAgent::willEvaluateScript(const String& url, int lineNumber, Frame& frame)
473 {
474     pushCurrentRecord(TimelineRecordFactory::createEvaluateScriptData(url, lineNumber), TimelineRecordType::EvaluateScript, true, &frame);
475
476     if (!m_callStackDepth) {
477         ++m_callStackDepth;
478         startProfiling(&frame, ASCIILiteral("Timeline EvaluateScript"), m_instrumentingAgents->inspectorEnvironment().executionStopwatch());
479     }
480 }
481
482 void InspectorTimelineAgent::didEvaluateScript(Frame& frame)
483 {
484     if (m_callStackDepth) {
485         --m_callStackDepth;
486         ASSERT(m_callStackDepth >= 0);
487
488         if (!m_callStackDepth) {
489             if (m_recordStack.isEmpty())
490                 return;
491
492             TimelineRecordEntry& entry = m_recordStack.last();
493             ASSERT(entry.type == TimelineRecordType::EvaluateScript);
494
495             RefPtr<JSC::Profile> profile = stopProfiling(&frame, ASCIILiteral("Timeline EvaluateScript"));
496             if (profile)
497                 TimelineRecordFactory::appendProfile(entry.data.get(), profile.release());
498         }
499     }
500
501     didCompleteCurrentRecord(TimelineRecordType::EvaluateScript);
502 }
503
504 void InspectorTimelineAgent::didTimeStamp(Frame& frame, const String& message)
505 {
506     appendRecord(TimelineRecordFactory::createTimeStampData(message), TimelineRecordType::TimeStamp, true, &frame);
507 }
508
509 void InspectorTimelineAgent::time(Frame& frame, const String& message)
510 {
511     appendRecord(TimelineRecordFactory::createTimeStampData(message), TimelineRecordType::Time, true, &frame);
512 }
513
514 void InspectorTimelineAgent::timeEnd(Frame& frame, const String& message)
515 {
516     appendRecord(TimelineRecordFactory::createTimeStampData(message), TimelineRecordType::TimeEnd, true, &frame);
517 }
518
519 void InspectorTimelineAgent::didMarkDOMContentEvent(Frame& frame)
520 {
521     appendRecord(TimelineRecordFactory::createMarkData(frame.isMainFrame()), TimelineRecordType::MarkDOMContent, false, &frame);
522 }
523
524 void InspectorTimelineAgent::didMarkLoadEvent(Frame& frame)
525 {
526     appendRecord(TimelineRecordFactory::createMarkData(frame.isMainFrame()), TimelineRecordType::MarkLoad, false, &frame);
527 }
528
529 void InspectorTimelineAgent::didCommitLoad()
530 {
531     clearRecordStack();
532 }
533
534 void InspectorTimelineAgent::didRequestAnimationFrame(int callbackId, Frame* frame)
535 {
536     appendRecord(TimelineRecordFactory::createAnimationFrameData(callbackId), TimelineRecordType::RequestAnimationFrame, true, frame);
537 }
538
539 void InspectorTimelineAgent::didCancelAnimationFrame(int callbackId, Frame* frame)
540 {
541     appendRecord(TimelineRecordFactory::createAnimationFrameData(callbackId), TimelineRecordType::CancelAnimationFrame, true, frame);
542 }
543
544 void InspectorTimelineAgent::willFireAnimationFrame(int callbackId, Frame* frame)
545 {
546     pushCurrentRecord(TimelineRecordFactory::createAnimationFrameData(callbackId), TimelineRecordType::FireAnimationFrame, false, frame);
547 }
548
549 void InspectorTimelineAgent::didFireAnimationFrame()
550 {
551     didCompleteCurrentRecord(TimelineRecordType::FireAnimationFrame);
552 }
553
554 #if ENABLE(WEB_SOCKETS)
555 void InspectorTimelineAgent::didCreateWebSocket(unsigned long identifier, const URL& url, const String& protocol, Frame* frame)
556 {
557     appendRecord(TimelineRecordFactory::createWebSocketCreateData(identifier, url, protocol), TimelineRecordType::WebSocketCreate, true, frame);
558 }
559
560 void InspectorTimelineAgent::willSendWebSocketHandshakeRequest(unsigned long identifier, Frame* frame)
561 {
562     appendRecord(TimelineRecordFactory::createGenericWebSocketData(identifier), TimelineRecordType::WebSocketSendHandshakeRequest, true, frame);
563 }
564
565 void InspectorTimelineAgent::didReceiveWebSocketHandshakeResponse(unsigned long identifier, Frame* frame)
566 {
567     appendRecord(TimelineRecordFactory::createGenericWebSocketData(identifier), TimelineRecordType::WebSocketReceiveHandshakeResponse, false, frame);
568 }
569
570 void InspectorTimelineAgent::didDestroyWebSocket(unsigned long identifier, Frame* frame)
571 {
572     appendRecord(TimelineRecordFactory::createGenericWebSocketData(identifier), TimelineRecordType::WebSocketDestroy, true, frame);
573 }
574 #endif // ENABLE(WEB_SOCKETS)
575
576 // ScriptDebugListener
577
578 void InspectorTimelineAgent::breakpointActionProbe(JSC::ExecState* exec, const Inspector::ScriptBreakpointAction& action, unsigned batchId, unsigned sampleId, const Deprecated::ScriptValue&)
579 {
580     UNUSED_PARAM(batchId);
581     ASSERT(exec);
582
583     appendRecord(TimelineRecordFactory::createProbeSampleData(action, sampleId), TimelineRecordType::ProbeSample, false, frameFromExecState(exec));
584 }
585
586 static Inspector::Protocol::Timeline::EventType toProtocol(TimelineRecordType type)
587 {
588     switch (type) {
589     case TimelineRecordType::EventDispatch:
590         return Inspector::Protocol::Timeline::EventType::EventDispatch;
591     case TimelineRecordType::ScheduleStyleRecalculation:
592         return Inspector::Protocol::Timeline::EventType::ScheduleStyleRecalculation;
593     case TimelineRecordType::RecalculateStyles:
594         return Inspector::Protocol::Timeline::EventType::RecalculateStyles;
595     case TimelineRecordType::InvalidateLayout:
596         return Inspector::Protocol::Timeline::EventType::InvalidateLayout;
597     case TimelineRecordType::Layout:
598         return Inspector::Protocol::Timeline::EventType::Layout;
599     case TimelineRecordType::Paint:
600         return Inspector::Protocol::Timeline::EventType::Paint;
601     case TimelineRecordType::RunLoop:
602         return Inspector::Protocol::Timeline::EventType::RunLoop;
603     case TimelineRecordType::ScrollLayer:
604         return Inspector::Protocol::Timeline::EventType::ScrollLayer;
605
606     case TimelineRecordType::ParseHTML:
607         return Inspector::Protocol::Timeline::EventType::ParseHTML;
608
609     case TimelineRecordType::TimerInstall:
610         return Inspector::Protocol::Timeline::EventType::TimerInstall;
611     case TimelineRecordType::TimerRemove:
612         return Inspector::Protocol::Timeline::EventType::TimerRemove;
613     case TimelineRecordType::TimerFire:
614         return Inspector::Protocol::Timeline::EventType::TimerFire;
615
616     case TimelineRecordType::EvaluateScript:
617         return Inspector::Protocol::Timeline::EventType::EvaluateScript;
618
619     case TimelineRecordType::MarkLoad:
620         return Inspector::Protocol::Timeline::EventType::MarkLoad;
621     case TimelineRecordType::MarkDOMContent:
622         return Inspector::Protocol::Timeline::EventType::MarkDOMContent;
623
624     case TimelineRecordType::TimeStamp:
625         return Inspector::Protocol::Timeline::EventType::TimeStamp;
626     case TimelineRecordType::Time:
627         return Inspector::Protocol::Timeline::EventType::Time;
628     case TimelineRecordType::TimeEnd:
629         return Inspector::Protocol::Timeline::EventType::TimeEnd;
630
631     case TimelineRecordType::XHRReadyStateChange:
632         return Inspector::Protocol::Timeline::EventType::XHRReadyStateChange;
633     case TimelineRecordType::XHRLoad:
634         return Inspector::Protocol::Timeline::EventType::XHRLoad;
635
636     case TimelineRecordType::FunctionCall:
637         return Inspector::Protocol::Timeline::EventType::FunctionCall;
638     case TimelineRecordType::ProbeSample:
639         return Inspector::Protocol::Timeline::EventType::ProbeSample;
640     case TimelineRecordType::ConsoleProfile:
641         return Inspector::Protocol::Timeline::EventType::ConsoleProfile;
642
643     case TimelineRecordType::RequestAnimationFrame:
644         return Inspector::Protocol::Timeline::EventType::RequestAnimationFrame;
645     case TimelineRecordType::CancelAnimationFrame:
646         return Inspector::Protocol::Timeline::EventType::CancelAnimationFrame;
647     case TimelineRecordType::FireAnimationFrame:
648         return Inspector::Protocol::Timeline::EventType::FireAnimationFrame;
649
650     case TimelineRecordType::WebSocketCreate:
651         return Inspector::Protocol::Timeline::EventType::WebSocketCreate;
652     case TimelineRecordType::WebSocketSendHandshakeRequest:
653         return Inspector::Protocol::Timeline::EventType::WebSocketSendHandshakeRequest;
654     case TimelineRecordType::WebSocketReceiveHandshakeResponse:
655         return Inspector::Protocol::Timeline::EventType::WebSocketReceiveHandshakeResponse;
656     case TimelineRecordType::WebSocketDestroy:
657         return Inspector::Protocol::Timeline::EventType::WebSocketDestroy;
658     }
659
660     return Inspector::Protocol::Timeline::EventType::TimeStamp;
661 }
662
663 void InspectorTimelineAgent::addRecordToTimeline(RefPtr<InspectorObject>&& record, TimelineRecordType type)
664 {
665     ASSERT_ARG(record, record);
666     record->setString("type", Inspector::Protocol::getEnumConstantValue(toProtocol(type)));
667
668     if (m_recordStack.isEmpty()) {
669         auto recordObject = BindingTraits<Inspector::Protocol::Timeline::TimelineEvent>::runtimeCast(WTF::move(record));
670         sendEvent(WTF::move(recordObject));
671     } else {
672         const TimelineRecordEntry& parent = m_recordStack.last();
673         parent.children->pushObject(WTF::move(record));
674     }
675 }
676
677 void InspectorTimelineAgent::setFrameIdentifier(InspectorObject* record, Frame* frame)
678 {
679     if (!frame || !m_pageAgent)
680         return;
681     String frameId;
682     if (frame && m_pageAgent)
683         frameId = m_pageAgent->frameId(frame);
684     record->setString("frameId", frameId);
685 }
686
687 void InspectorTimelineAgent::didCompleteRecordEntry(const TimelineRecordEntry& entry)
688 {
689     entry.record->setObject(ASCIILiteral("data"), entry.data);
690     entry.record->setArray(ASCIILiteral("children"), entry.children);
691     entry.record->setDouble(ASCIILiteral("endTime"), timestamp());
692     addRecordToTimeline(entry.record.copyRef(), entry.type);
693 }
694
695 void InspectorTimelineAgent::didCompleteCurrentRecord(TimelineRecordType type)
696 {
697     // An empty stack could merely mean that the timeline agent was turned on in the middle of
698     // an event.  Don't treat as an error.
699     if (!m_recordStack.isEmpty()) {
700         TimelineRecordEntry entry = m_recordStack.last();
701         m_recordStack.removeLast();
702         ASSERT_UNUSED(type, entry.type == type);
703         didCompleteRecordEntry(entry);
704     }
705 }
706
707 InspectorTimelineAgent::InspectorTimelineAgent(InstrumentingAgents* instrumentingAgents, InspectorPageAgent* pageAgent, InspectorType type, InspectorClient* client)
708     : InspectorAgentBase(ASCIILiteral("Timeline"), instrumentingAgents)
709     , m_pageAgent(pageAgent)
710     , m_scriptDebugServer(nullptr)
711     , m_id(1)
712     , m_callStackDepth(0)
713     , m_maxCallStackDepth(5)
714     , m_inspectorType(type)
715     , m_client(client)
716     , m_enabled(false)
717     , m_enabledFromFrontend(false)
718     , m_didStartRecordingRunLoop(false)
719 {
720 }
721
722 void InspectorTimelineAgent::appendRecord(RefPtr<InspectorObject>&& data, TimelineRecordType type, bool captureCallStack, Frame* frame)
723 {
724     Ref<InspectorObject> record = TimelineRecordFactory::createGenericRecord(timestamp(), captureCallStack ? m_maxCallStackDepth : 0);
725     record->setObject("data", WTF::move(data));
726     setFrameIdentifier(&record.get(), frame);
727     addRecordToTimeline(WTF::move(record), type);
728 }
729
730 void InspectorTimelineAgent::sendEvent(RefPtr<InspectorObject>&& event)
731 {
732     if (!m_frontendDispatcher)
733         return;
734
735     // FIXME: runtimeCast is a hack. We do it because we can't build TimelineEvent directly now.
736     auto recordChecked = BindingTraits<Inspector::Protocol::Timeline::TimelineEvent>::runtimeCast(WTF::move(event));
737     m_frontendDispatcher->eventRecorded(WTF::move(recordChecked));
738 }
739
740 InspectorTimelineAgent::TimelineRecordEntry InspectorTimelineAgent::createRecordEntry(RefPtr<InspectorObject>&& data, TimelineRecordType type, bool captureCallStack, Frame* frame)
741 {
742     Ref<InspectorObject> record = TimelineRecordFactory::createGenericRecord(timestamp(), captureCallStack ? m_maxCallStackDepth : 0);
743     setFrameIdentifier(&record.get(), frame);
744     return TimelineRecordEntry(WTF::move(record), WTF::move(data), InspectorArray::create(), type);
745 }
746
747 void InspectorTimelineAgent::pushCurrentRecord(RefPtr<InspectorObject>&& data, TimelineRecordType type, bool captureCallStack, Frame* frame)
748 {
749     pushCurrentRecord(createRecordEntry(WTF::move(data), type, captureCallStack, frame));
750 }
751
752 void InspectorTimelineAgent::clearRecordStack()
753 {
754     m_recordStack.clear();
755     m_id++;
756 }
757
758 void InspectorTimelineAgent::localToPageQuad(const RenderObject& renderer, const LayoutRect& rect, FloatQuad* quad)
759 {
760     const FrameView& frameView = renderer.view().frameView();
761     FloatQuad absolute = renderer.localToAbsoluteQuad(FloatQuad(rect));
762     quad->setP1(frameView.contentsToRootView(roundedIntPoint(absolute.p1())));
763     quad->setP2(frameView.contentsToRootView(roundedIntPoint(absolute.p2())));
764     quad->setP3(frameView.contentsToRootView(roundedIntPoint(absolute.p3())));
765     quad->setP4(frameView.contentsToRootView(roundedIntPoint(absolute.p4())));
766 }
767
768 Page* InspectorTimelineAgent::page()
769 {
770     return m_pageAgent ? m_pageAgent->page() : nullptr;
771 }
772
773 } // namespace WebCore