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