b4886de28f09224f781131997e98cd6a5fae6a3f
[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 using namespace Inspector;
58
59 namespace WebCore {
60
61 InspectorTimelineAgent::~InspectorTimelineAgent()
62 {
63 }
64
65 void InspectorTimelineAgent::didCreateFrontendAndBackend(Inspector::FrontendChannel* frontendChannel, Inspector::BackendDispatcher* backendDispatcher)
66 {
67     m_frontendDispatcher = std::make_unique<Inspector::TimelineFrontendDispatcher>(frontendChannel);
68     m_backendDispatcher = Inspector::TimelineBackendDispatcher::create(backendDispatcher, this);
69
70     m_instrumentingAgents->setPersistentInspectorTimelineAgent(this);
71
72     if (m_scriptDebugServer)
73         m_scriptDebugServer->recompileAllJSFunctions();
74 }
75
76 void InspectorTimelineAgent::willDestroyFrontendAndBackend(Inspector::DisconnectReason reason)
77 {
78     m_frontendDispatcher = nullptr;
79     m_backendDispatcher.clear();
80
81     m_instrumentingAgents->setPersistentInspectorTimelineAgent(nullptr);
82
83     if (reason != Inspector::DisconnectReason::InspectedTargetDestroyed) {
84         if (m_scriptDebugServer)
85             m_scriptDebugServer->recompileAllJSFunctions();
86     }
87
88     ErrorString unused;
89     stop(unused);
90 }
91
92 void InspectorTimelineAgent::start(ErrorString&, const int* maxCallStackDepth)
93 {
94     m_enabledFromFrontend = true;
95
96     internalStart(maxCallStackDepth);
97 }
98
99 void InspectorTimelineAgent::stop(ErrorString&)
100 {
101     internalStop();
102
103     m_enabledFromFrontend = false;
104 }
105
106 void InspectorTimelineAgent::internalStart(const int* maxCallStackDepth)
107 {
108     if (m_enabled)
109         return;
110
111     if (maxCallStackDepth && *maxCallStackDepth > 0)
112         m_maxCallStackDepth = *maxCallStackDepth;
113     else
114         m_maxCallStackDepth = 5;
115
116     m_instrumentingAgents->inspectorEnvironment().executionStopwatch()->start();
117
118     m_instrumentingAgents->setInspectorTimelineAgent(this);
119
120     if (m_scriptDebugServer)
121         m_scriptDebugServer->addListener(this);
122
123     m_enabled = true;
124
125     if (m_frontendDispatcher)
126         m_frontendDispatcher->recordingStarted();
127 }
128
129 void InspectorTimelineAgent::internalStop()
130 {
131     if (!m_enabled)
132         return;
133
134     // The environment's stopwatch could be already stopped if the debugger has paused.
135     auto stopwatch = m_instrumentingAgents->inspectorEnvironment().executionStopwatch();
136     if (stopwatch->isActive())
137         stopwatch->stop();
138
139     m_instrumentingAgents->setInspectorTimelineAgent(nullptr);
140
141     if (m_scriptDebugServer)
142         m_scriptDebugServer->removeListener(this, true);
143
144     clearRecordStack();
145
146     m_enabled = false;
147
148     if (m_frontendDispatcher)
149         m_frontendDispatcher->recordingStopped();
150 }
151
152 double InspectorTimelineAgent::timestamp()
153 {
154     return m_instrumentingAgents->inspectorEnvironment().executionStopwatch()->elapsedTime();
155 }
156
157 void InspectorTimelineAgent::setPageScriptDebugServer(PageScriptDebugServer* scriptDebugServer)
158 {
159     ASSERT(!m_enabled);
160     ASSERT(!m_scriptDebugServer);
161
162     m_scriptDebugServer = scriptDebugServer;
163 }
164
165 static inline void startProfiling(JSC::ExecState* exec, const String& title, PassRefPtr<Stopwatch> stopwatch)
166 {
167     JSC::LegacyProfiler::profiler()->startProfiling(exec, title, stopwatch);
168 }
169
170 static inline PassRefPtr<JSC::Profile> stopProfiling(JSC::ExecState* exec, const String& title)
171 {
172     return JSC::LegacyProfiler::profiler()->stopProfiling(exec, title);
173 }
174
175 static inline void startProfiling(Frame* frame, const String& title, PassRefPtr<Stopwatch> stopwatch)
176 {
177     startProfiling(toJSDOMWindow(frame, debuggerWorld())->globalExec(), title, stopwatch);
178 }
179
180 static inline PassRefPtr<JSC::Profile> stopProfiling(Frame* frame, const String& title)
181 {
182     return stopProfiling(toJSDOMWindow(frame, debuggerWorld())->globalExec(), title);
183 }
184
185 void InspectorTimelineAgent::startFromConsole(JSC::ExecState* exec, const String &title)
186 {
187     // Only allow recording of a profile if it is anonymous (empty title) or does not match
188     // the title of an already recording profile.
189     if (!title.isEmpty()) {
190         for (const TimelineRecordEntry& record : m_pendingConsoleProfileRecords) {
191             String recordTitle;
192             record.data->getString(ASCIILiteral("title"), recordTitle);
193             if (recordTitle == title)
194                 return;
195         }
196     }
197
198     if (!m_enabled && m_pendingConsoleProfileRecords.isEmpty())
199         internalStart();
200
201     // Use an independent stopwatch for console-initiated profiling, since the user will expect it
202     // to be relative to when their command was issued.
203     Ref<Stopwatch> profilerStopwatch = Stopwatch::create();
204     profilerStopwatch->start();
205     startProfiling(exec, title, WTF::move(profilerStopwatch));
206
207     m_pendingConsoleProfileRecords.append(createRecordEntry(TimelineRecordFactory::createConsoleProfileData(title), TimelineRecordType::ConsoleProfile, true, frameFromExecState(exec)));
208 }
209
210 PassRefPtr<JSC::Profile> InspectorTimelineAgent::stopFromConsole(JSC::ExecState* exec, const String& title)
211 {
212     // Stop profiles in reverse order. If the title is empty, then stop the last profile.
213     // Otherwise, match the title of the profile to stop.
214     for (ptrdiff_t i = m_pendingConsoleProfileRecords.size() - 1; i >= 0; --i) {
215         const TimelineRecordEntry& record = m_pendingConsoleProfileRecords[i];
216
217         String recordTitle;
218         record.data->getString(ASCIILiteral("title"), recordTitle);
219
220         if (title.isEmpty() || recordTitle == title) {
221             RefPtr<JSC::Profile> profile = stopProfiling(exec, title);
222             if (profile)
223                 TimelineRecordFactory::appendProfile(record.data.get(), profile.copyRef());
224
225             didCompleteRecordEntry(record);
226
227             m_pendingConsoleProfileRecords.remove(i);
228
229             if (!m_enabledFromFrontend && m_pendingConsoleProfileRecords.isEmpty())
230                 internalStop();
231
232             return WTF::move(profile);
233         }
234     }
235
236     return nullptr;
237 }
238
239 void InspectorTimelineAgent::willCallFunction(const String& scriptName, int scriptLine, Frame* frame)
240 {
241     pushCurrentRecord(TimelineRecordFactory::createFunctionCallData(scriptName, scriptLine), TimelineRecordType::FunctionCall, true, frame);
242
243     if (frame && !m_callStackDepth)
244         startProfiling(frame, ASCIILiteral("Timeline FunctionCall"), m_instrumentingAgents->inspectorEnvironment().executionStopwatch());
245
246     ++m_callStackDepth;
247 }
248
249 void InspectorTimelineAgent::didCallFunction(Frame* frame)
250 {
251     if (frame && m_callStackDepth) {
252         --m_callStackDepth;
253         ASSERT(m_callStackDepth >= 0);
254
255         if (!m_callStackDepth) {
256             if (m_recordStack.isEmpty())
257                 return;
258
259             TimelineRecordEntry& entry = m_recordStack.last();
260             ASSERT(entry.type == TimelineRecordType::FunctionCall);
261
262             RefPtr<JSC::Profile> profile = stopProfiling(frame, ASCIILiteral("Timeline FunctionCall"));
263             if (profile)
264                 TimelineRecordFactory::appendProfile(entry.data.get(), profile.release());
265         }
266     }
267
268     didCompleteCurrentRecord(TimelineRecordType::FunctionCall);
269 }
270
271 void InspectorTimelineAgent::willDispatchEvent(const Event& event, Frame* frame)
272 {
273     pushCurrentRecord(TimelineRecordFactory::createEventDispatchData(event), TimelineRecordType::EventDispatch, false, frame);
274 }
275
276 void InspectorTimelineAgent::didDispatchEvent()
277 {
278     didCompleteCurrentRecord(TimelineRecordType::EventDispatch);
279 }
280
281 void InspectorTimelineAgent::didInvalidateLayout(Frame& frame)
282 {
283     appendRecord(InspectorObject::create(), TimelineRecordType::InvalidateLayout, true, &frame);
284 }
285
286 void InspectorTimelineAgent::willLayout(Frame& frame)
287 {
288     RenderObject* root = frame.view()->layoutRoot();
289     bool partialLayout = !!root;
290
291     if (!partialLayout)
292         root = frame.contentRenderer();
293
294     unsigned dirtyObjects = 0;
295     unsigned totalObjects = 0;
296     for (RenderObject* o = root; o; o = o->nextInPreOrder(root)) {
297         ++totalObjects;
298         if (o->needsLayout())
299             ++dirtyObjects;
300     }
301     pushCurrentRecord(TimelineRecordFactory::createLayoutData(dirtyObjects, totalObjects, partialLayout), TimelineRecordType::Layout, true, &frame);
302 }
303
304 void InspectorTimelineAgent::didLayout(RenderObject* root)
305 {
306     if (m_recordStack.isEmpty())
307         return;
308     TimelineRecordEntry& entry = m_recordStack.last();
309     ASSERT(entry.type == TimelineRecordType::Layout);
310     Vector<FloatQuad> quads;
311     root->absoluteQuads(quads);
312     if (quads.size() >= 1)
313         TimelineRecordFactory::appendLayoutRoot(entry.data.get(), quads[0]);
314     else
315         ASSERT_NOT_REACHED();
316     didCompleteCurrentRecord(TimelineRecordType::Layout);
317 }
318
319 void InspectorTimelineAgent::didScheduleStyleRecalculation(Frame* frame)
320 {
321     appendRecord(InspectorObject::create(), TimelineRecordType::ScheduleStyleRecalculation, true, frame);
322 }
323
324 void InspectorTimelineAgent::willRecalculateStyle(Frame* frame)
325 {
326     pushCurrentRecord(InspectorObject::create(), TimelineRecordType::RecalculateStyles, true, frame);
327 }
328
329 void InspectorTimelineAgent::didRecalculateStyle()
330 {
331     didCompleteCurrentRecord(TimelineRecordType::RecalculateStyles);
332 }
333
334 void InspectorTimelineAgent::willPaint(Frame& frame)
335 {
336     pushCurrentRecord(InspectorObject::create(), TimelineRecordType::Paint, true, &frame);
337 }
338
339 void InspectorTimelineAgent::didPaint(RenderObject* renderer, const LayoutRect& clipRect)
340 {
341     TimelineRecordEntry& entry = m_recordStack.last();
342     ASSERT(entry.type == TimelineRecordType::Paint);
343     FloatQuad quad;
344     localToPageQuad(*renderer, clipRect, &quad);
345     entry.data = TimelineRecordFactory::createPaintData(quad);
346     didCompleteCurrentRecord(TimelineRecordType::Paint);
347 }
348
349 void InspectorTimelineAgent::willScroll(Frame& frame)
350 {
351     pushCurrentRecord(InspectorObject::create(), TimelineRecordType::ScrollLayer, false, &frame);
352 }
353
354 void InspectorTimelineAgent::didScroll()
355 {
356     didCompleteCurrentRecord(TimelineRecordType::ScrollLayer);
357 }
358
359 void InspectorTimelineAgent::willWriteHTML(unsigned startLine, Frame* frame)
360 {
361     pushCurrentRecord(TimelineRecordFactory::createParseHTMLData(startLine), TimelineRecordType::ParseHTML, true, frame);
362 }
363
364 void InspectorTimelineAgent::didWriteHTML(unsigned endLine)
365 {
366     if (!m_recordStack.isEmpty()) {
367         const TimelineRecordEntry& entry = m_recordStack.last();
368         entry.data->setInteger("endLine", endLine);
369         didCompleteCurrentRecord(TimelineRecordType::ParseHTML);
370     }
371 }
372
373 void InspectorTimelineAgent::didInstallTimer(int timerId, int timeout, bool singleShot, Frame* frame)
374 {
375     appendRecord(TimelineRecordFactory::createTimerInstallData(timerId, timeout, singleShot), TimelineRecordType::TimerInstall, true, frame);
376 }
377
378 void InspectorTimelineAgent::didRemoveTimer(int timerId, Frame* frame)
379 {
380     appendRecord(TimelineRecordFactory::createGenericTimerData(timerId), TimelineRecordType::TimerRemove, true, frame);
381 }
382
383 void InspectorTimelineAgent::willFireTimer(int timerId, Frame* frame)
384 {
385     pushCurrentRecord(TimelineRecordFactory::createGenericTimerData(timerId), TimelineRecordType::TimerFire, false, frame);
386 }
387
388 void InspectorTimelineAgent::didFireTimer()
389 {
390     didCompleteCurrentRecord(TimelineRecordType::TimerFire);
391 }
392
393 void InspectorTimelineAgent::willDispatchXHRReadyStateChangeEvent(const String& url, int readyState, Frame* frame)
394 {
395     pushCurrentRecord(TimelineRecordFactory::createXHRReadyStateChangeData(url, readyState), TimelineRecordType::XHRReadyStateChange, false, frame);
396 }
397
398 void InspectorTimelineAgent::didDispatchXHRReadyStateChangeEvent()
399 {
400     didCompleteCurrentRecord(TimelineRecordType::XHRReadyStateChange);
401 }
402
403 void InspectorTimelineAgent::willDispatchXHRLoadEvent(const String& url, Frame* frame)
404 {
405     pushCurrentRecord(TimelineRecordFactory::createXHRLoadData(url), TimelineRecordType::XHRLoad, true, frame);
406 }
407
408 void InspectorTimelineAgent::didDispatchXHRLoadEvent()
409 {
410     didCompleteCurrentRecord(TimelineRecordType::XHRLoad);
411 }
412
413 void InspectorTimelineAgent::willEvaluateScript(const String& url, int lineNumber, Frame& frame)
414 {
415     pushCurrentRecord(TimelineRecordFactory::createEvaluateScriptData(url, lineNumber), TimelineRecordType::EvaluateScript, true, &frame);
416
417     if (!m_callStackDepth) {
418         ++m_callStackDepth;
419         startProfiling(&frame, ASCIILiteral("Timeline EvaluateScript"), m_instrumentingAgents->inspectorEnvironment().executionStopwatch());
420     }
421 }
422
423 void InspectorTimelineAgent::didEvaluateScript(Frame& frame)
424 {
425     if (m_callStackDepth) {
426         --m_callStackDepth;
427         ASSERT(m_callStackDepth >= 0);
428
429         if (!m_callStackDepth) {
430             if (m_recordStack.isEmpty())
431                 return;
432
433             TimelineRecordEntry& entry = m_recordStack.last();
434             ASSERT(entry.type == TimelineRecordType::EvaluateScript);
435
436             RefPtr<JSC::Profile> profile = stopProfiling(&frame, ASCIILiteral("Timeline EvaluateScript"));
437             if (profile)
438                 TimelineRecordFactory::appendProfile(entry.data.get(), profile.release());
439         }
440     }
441
442     didCompleteCurrentRecord(TimelineRecordType::EvaluateScript);
443 }
444
445 void InspectorTimelineAgent::didTimeStamp(Frame& frame, const String& message)
446 {
447     appendRecord(TimelineRecordFactory::createTimeStampData(message), TimelineRecordType::TimeStamp, true, &frame);
448 }
449
450 void InspectorTimelineAgent::time(Frame& frame, const String& message)
451 {
452     appendRecord(TimelineRecordFactory::createTimeStampData(message), TimelineRecordType::Time, true, &frame);
453 }
454
455 void InspectorTimelineAgent::timeEnd(Frame& frame, const String& message)
456 {
457     appendRecord(TimelineRecordFactory::createTimeStampData(message), TimelineRecordType::TimeEnd, true, &frame);
458 }
459
460 void InspectorTimelineAgent::didMarkDOMContentEvent(Frame& frame)
461 {
462     appendRecord(TimelineRecordFactory::createMarkData(frame.isMainFrame()), TimelineRecordType::MarkDOMContent, false, &frame);
463 }
464
465 void InspectorTimelineAgent::didMarkLoadEvent(Frame& frame)
466 {
467     appendRecord(TimelineRecordFactory::createMarkData(frame.isMainFrame()), TimelineRecordType::MarkLoad, false, &frame);
468 }
469
470 void InspectorTimelineAgent::didCommitLoad()
471 {
472     clearRecordStack();
473 }
474
475 void InspectorTimelineAgent::didRequestAnimationFrame(int callbackId, Frame* frame)
476 {
477     appendRecord(TimelineRecordFactory::createAnimationFrameData(callbackId), TimelineRecordType::RequestAnimationFrame, true, frame);
478 }
479
480 void InspectorTimelineAgent::didCancelAnimationFrame(int callbackId, Frame* frame)
481 {
482     appendRecord(TimelineRecordFactory::createAnimationFrameData(callbackId), TimelineRecordType::CancelAnimationFrame, true, frame);
483 }
484
485 void InspectorTimelineAgent::willFireAnimationFrame(int callbackId, Frame* frame)
486 {
487     pushCurrentRecord(TimelineRecordFactory::createAnimationFrameData(callbackId), TimelineRecordType::FireAnimationFrame, false, frame);
488 }
489
490 void InspectorTimelineAgent::didFireAnimationFrame()
491 {
492     didCompleteCurrentRecord(TimelineRecordType::FireAnimationFrame);
493 }
494
495 #if ENABLE(WEB_SOCKETS)
496 void InspectorTimelineAgent::didCreateWebSocket(unsigned long identifier, const URL& url, const String& protocol, Frame* frame)
497 {
498     appendRecord(TimelineRecordFactory::createWebSocketCreateData(identifier, url, protocol), TimelineRecordType::WebSocketCreate, true, frame);
499 }
500
501 void InspectorTimelineAgent::willSendWebSocketHandshakeRequest(unsigned long identifier, Frame* frame)
502 {
503     appendRecord(TimelineRecordFactory::createGenericWebSocketData(identifier), TimelineRecordType::WebSocketSendHandshakeRequest, true, frame);
504 }
505
506 void InspectorTimelineAgent::didReceiveWebSocketHandshakeResponse(unsigned long identifier, Frame* frame)
507 {
508     appendRecord(TimelineRecordFactory::createGenericWebSocketData(identifier), TimelineRecordType::WebSocketReceiveHandshakeResponse, false, frame);
509 }
510
511 void InspectorTimelineAgent::didDestroyWebSocket(unsigned long identifier, Frame* frame)
512 {
513     appendRecord(TimelineRecordFactory::createGenericWebSocketData(identifier), TimelineRecordType::WebSocketDestroy, true, frame);
514 }
515 #endif // ENABLE(WEB_SOCKETS)
516
517 // ScriptDebugListener
518
519 void InspectorTimelineAgent::breakpointActionProbe(JSC::ExecState* exec, const Inspector::ScriptBreakpointAction& action, unsigned batchId, unsigned sampleId, const Deprecated::ScriptValue&)
520 {
521     UNUSED_PARAM(batchId);
522     ASSERT(exec);
523
524     appendRecord(TimelineRecordFactory::createProbeSampleData(action, sampleId), TimelineRecordType::ProbeSample, false, frameFromExecState(exec));
525 }
526
527 static Inspector::Protocol::Timeline::EventType toProtocol(TimelineRecordType type)
528 {
529     switch (type) {
530     case TimelineRecordType::EventDispatch:
531         return Inspector::Protocol::Timeline::EventType::EventDispatch;
532     case TimelineRecordType::ScheduleStyleRecalculation:
533         return Inspector::Protocol::Timeline::EventType::ScheduleStyleRecalculation;
534     case TimelineRecordType::RecalculateStyles:
535         return Inspector::Protocol::Timeline::EventType::RecalculateStyles;
536     case TimelineRecordType::InvalidateLayout:
537         return Inspector::Protocol::Timeline::EventType::InvalidateLayout;
538     case TimelineRecordType::Layout:
539         return Inspector::Protocol::Timeline::EventType::Layout;
540     case TimelineRecordType::Paint:
541         return Inspector::Protocol::Timeline::EventType::Paint;
542     case TimelineRecordType::ScrollLayer:
543         return Inspector::Protocol::Timeline::EventType::ScrollLayer;
544
545     case TimelineRecordType::ParseHTML:
546         return Inspector::Protocol::Timeline::EventType::ParseHTML;
547
548     case TimelineRecordType::TimerInstall:
549         return Inspector::Protocol::Timeline::EventType::TimerInstall;
550     case TimelineRecordType::TimerRemove:
551         return Inspector::Protocol::Timeline::EventType::TimerRemove;
552     case TimelineRecordType::TimerFire:
553         return Inspector::Protocol::Timeline::EventType::TimerFire;
554
555     case TimelineRecordType::EvaluateScript:
556         return Inspector::Protocol::Timeline::EventType::EvaluateScript;
557
558     case TimelineRecordType::MarkLoad:
559         return Inspector::Protocol::Timeline::EventType::MarkLoad;
560     case TimelineRecordType::MarkDOMContent:
561         return Inspector::Protocol::Timeline::EventType::MarkDOMContent;
562
563     case TimelineRecordType::TimeStamp:
564         return Inspector::Protocol::Timeline::EventType::TimeStamp;
565     case TimelineRecordType::Time:
566         return Inspector::Protocol::Timeline::EventType::Time;
567     case TimelineRecordType::TimeEnd:
568         return Inspector::Protocol::Timeline::EventType::TimeEnd;
569
570     case TimelineRecordType::XHRReadyStateChange:
571         return Inspector::Protocol::Timeline::EventType::XHRReadyStateChange;
572     case TimelineRecordType::XHRLoad:
573         return Inspector::Protocol::Timeline::EventType::XHRLoad;
574
575     case TimelineRecordType::FunctionCall:
576         return Inspector::Protocol::Timeline::EventType::FunctionCall;
577     case TimelineRecordType::ProbeSample:
578         return Inspector::Protocol::Timeline::EventType::ProbeSample;
579     case TimelineRecordType::ConsoleProfile:
580         return Inspector::Protocol::Timeline::EventType::ConsoleProfile;
581
582     case TimelineRecordType::RequestAnimationFrame:
583         return Inspector::Protocol::Timeline::EventType::RequestAnimationFrame;
584     case TimelineRecordType::CancelAnimationFrame:
585         return Inspector::Protocol::Timeline::EventType::CancelAnimationFrame;
586     case TimelineRecordType::FireAnimationFrame:
587         return Inspector::Protocol::Timeline::EventType::FireAnimationFrame;
588
589     case TimelineRecordType::WebSocketCreate:
590         return Inspector::Protocol::Timeline::EventType::WebSocketCreate;
591     case TimelineRecordType::WebSocketSendHandshakeRequest:
592         return Inspector::Protocol::Timeline::EventType::WebSocketSendHandshakeRequest;
593     case TimelineRecordType::WebSocketReceiveHandshakeResponse:
594         return Inspector::Protocol::Timeline::EventType::WebSocketReceiveHandshakeResponse;
595     case TimelineRecordType::WebSocketDestroy:
596         return Inspector::Protocol::Timeline::EventType::WebSocketDestroy;
597     }
598
599     return Inspector::Protocol::Timeline::EventType::TimeStamp;
600 }
601
602 void InspectorTimelineAgent::addRecordToTimeline(RefPtr<InspectorObject>&& record, TimelineRecordType type)
603 {
604     ASSERT_ARG(record, record);
605     record->setString("type", Inspector::Protocol::getEnumConstantValue(toProtocol(type)));
606
607     if (m_recordStack.isEmpty()) {
608         auto recordObject = BindingTraits<Inspector::Protocol::Timeline::TimelineEvent>::runtimeCast(WTF::move(record));
609         sendEvent(WTF::move(recordObject));
610     } else {
611         const TimelineRecordEntry& parent = m_recordStack.last();
612         parent.children->pushObject(WTF::move(record));
613     }
614 }
615
616 void InspectorTimelineAgent::setFrameIdentifier(InspectorObject* record, Frame* frame)
617 {
618     if (!frame || !m_pageAgent)
619         return;
620     String frameId;
621     if (frame && m_pageAgent)
622         frameId = m_pageAgent->frameId(frame);
623     record->setString("frameId", frameId);
624 }
625
626 void InspectorTimelineAgent::didCompleteRecordEntry(const TimelineRecordEntry& entry)
627 {
628     entry.record->setObject(ASCIILiteral("data"), entry.data);
629     entry.record->setArray(ASCIILiteral("children"), entry.children);
630     entry.record->setDouble(ASCIILiteral("endTime"), timestamp());
631     addRecordToTimeline(entry.record.copyRef(), entry.type);
632 }
633
634 void InspectorTimelineAgent::didCompleteCurrentRecord(TimelineRecordType type)
635 {
636     // An empty stack could merely mean that the timeline agent was turned on in the middle of
637     // an event.  Don't treat as an error.
638     if (!m_recordStack.isEmpty()) {
639         TimelineRecordEntry entry = m_recordStack.last();
640         m_recordStack.removeLast();
641         ASSERT_UNUSED(type, entry.type == type);
642         didCompleteRecordEntry(entry);
643     }
644 }
645
646 InspectorTimelineAgent::InspectorTimelineAgent(InstrumentingAgents* instrumentingAgents, InspectorPageAgent* pageAgent, InspectorType type, InspectorClient* client)
647     : InspectorAgentBase(ASCIILiteral("Timeline"), instrumentingAgents)
648     , m_pageAgent(pageAgent)
649     , m_scriptDebugServer(nullptr)
650     , m_id(1)
651     , m_callStackDepth(0)
652     , m_maxCallStackDepth(5)
653     , m_inspectorType(type)
654     , m_client(client)
655     , m_enabled(false)
656     , m_enabledFromFrontend(false)
657 {
658 }
659
660 void InspectorTimelineAgent::appendRecord(RefPtr<InspectorObject>&& data, TimelineRecordType type, bool captureCallStack, Frame* frame)
661 {
662     Ref<InspectorObject> record = TimelineRecordFactory::createGenericRecord(timestamp(), captureCallStack ? m_maxCallStackDepth : 0);
663     record->setObject("data", WTF::move(data));
664     setFrameIdentifier(&record.get(), frame);
665     addRecordToTimeline(WTF::move(record), type);
666 }
667
668 void InspectorTimelineAgent::sendEvent(RefPtr<InspectorObject>&& event)
669 {
670     if (!m_frontendDispatcher)
671         return;
672
673     // FIXME: runtimeCast is a hack. We do it because we can't build TimelineEvent directly now.
674     auto recordChecked = BindingTraits<Inspector::Protocol::Timeline::TimelineEvent>::runtimeCast(WTF::move(event));
675     m_frontendDispatcher->eventRecorded(WTF::move(recordChecked));
676 }
677
678 InspectorTimelineAgent::TimelineRecordEntry InspectorTimelineAgent::createRecordEntry(RefPtr<InspectorObject>&& data, TimelineRecordType type, bool captureCallStack, Frame* frame)
679 {
680     Ref<InspectorObject> record = TimelineRecordFactory::createGenericRecord(timestamp(), captureCallStack ? m_maxCallStackDepth : 0);
681     setFrameIdentifier(&record.get(), frame);
682     return TimelineRecordEntry(WTF::move(record), WTF::move(data), InspectorArray::create(), type);
683 }
684
685 void InspectorTimelineAgent::pushCurrentRecord(RefPtr<InspectorObject>&& data, TimelineRecordType type, bool captureCallStack, Frame* frame)
686 {
687     pushCurrentRecord(createRecordEntry(WTF::move(data), type, captureCallStack, frame));
688 }
689
690 void InspectorTimelineAgent::clearRecordStack()
691 {
692     m_recordStack.clear();
693     m_id++;
694 }
695
696 void InspectorTimelineAgent::localToPageQuad(const RenderObject& renderer, const LayoutRect& rect, FloatQuad* quad)
697 {
698     const FrameView& frameView = renderer.view().frameView();
699     FloatQuad absolute = renderer.localToAbsoluteQuad(FloatQuad(rect));
700     quad->setP1(frameView.contentsToRootView(roundedIntPoint(absolute.p1())));
701     quad->setP2(frameView.contentsToRootView(roundedIntPoint(absolute.p2())));
702     quad->setP3(frameView.contentsToRootView(roundedIntPoint(absolute.p3())));
703     quad->setP4(frameView.contentsToRootView(roundedIntPoint(absolute.p4())));
704 }
705
706 Page* InspectorTimelineAgent::page()
707 {
708     return m_pageAgent ? m_pageAgent->page() : nullptr;
709 }
710
711 } // namespace WebCore