Use "= default" to denote default constructor or destructor
[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 * Copyright (C) 2015 Apple Inc. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 *     * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 *     * Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following disclaimer
14 * in the documentation and/or other materials provided with the
15 * distribution.
16 *     * Neither the name of Google Inc. nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #include "config.h"
34 #include "InspectorTimelineAgent.h"
35
36 #include "DOMWindow.h"
37 #include "Event.h"
38 #include "Frame.h"
39 #include "InspectorMemoryAgent.h"
40 #include "InspectorPageAgent.h"
41 #include "InstrumentingAgents.h"
42 #include "JSDOMWindow.h"
43 #include "PageScriptDebugServer.h"
44 #include "RenderView.h"
45 #include "ScriptState.h"
46 #include "TimelineRecordFactory.h"
47 #include "WebConsoleAgent.h"
48 #include <inspector/ConsoleMessage.h>
49 #include <inspector/ScriptBreakpoint.h>
50 #include <inspector/agents/InspectorDebuggerAgent.h>
51 #include <inspector/agents/InspectorHeapAgent.h>
52 #include <inspector/agents/InspectorScriptProfilerAgent.h>
53 #include <wtf/Stopwatch.h>
54
55 #if PLATFORM(IOS)
56 #include "RuntimeApplicationChecks.h"
57 #include "WebCoreThreadInternal.h"
58 #endif
59
60 #if PLATFORM(COCOA)
61 #include "RunLoopObserver.h"
62 #endif
63
64
65 namespace WebCore {
66 using namespace Inspector;
67
68 #if PLATFORM(COCOA)
69 static const CFIndex frameStopRunLoopOrder = (CFIndex)RunLoopObserver::WellKnownRunLoopOrders::CoreAnimationCommit + 1;
70
71 static CFRunLoopRef currentRunLoop()
72 {
73 #if PLATFORM(IOS)
74     // A race condition during WebView deallocation can lead to a crash if the layer sync run loop
75     // observer is added to the main run loop <rdar://problem/9798550>. However, for responsiveness,
76     // we still allow this, see <rdar://problem/7403328>. Since the race condition and subsequent
77     // crash are especially troublesome for iBooks, we never allow the observer to be added to the
78     // main run loop in iBooks.
79     if (IOSApplication::isIBooks())
80         return WebThreadRunLoop();
81 #endif
82     return CFRunLoopGetCurrent();
83 }
84 #endif
85
86 InspectorTimelineAgent::InspectorTimelineAgent(WebAgentContext& context, InspectorScriptProfilerAgent* scriptProfileAgent, InspectorHeapAgent* heapAgent, InspectorPageAgent* pageAgent)
87     : InspectorAgentBase(ASCIILiteral("Timeline"), context)
88     , m_frontendDispatcher(std::make_unique<Inspector::TimelineFrontendDispatcher>(context.frontendRouter))
89     , m_backendDispatcher(Inspector::TimelineBackendDispatcher::create(context.backendDispatcher, this))
90     , m_scriptProfilerAgent(scriptProfileAgent)
91     , m_heapAgent(heapAgent)
92     , m_pageAgent(pageAgent)
93 {
94 }
95
96 InspectorTimelineAgent::~InspectorTimelineAgent() = default;
97
98 void InspectorTimelineAgent::didCreateFrontendAndBackend(Inspector::FrontendRouter*, Inspector::BackendDispatcher*)
99 {
100     m_instrumentingAgents.setPersistentInspectorTimelineAgent(this);
101 }
102
103 void InspectorTimelineAgent::willDestroyFrontendAndBackend(Inspector::DisconnectReason)
104 {
105     m_instrumentingAgents.setPersistentInspectorTimelineAgent(nullptr);
106
107     ErrorString unused;
108     stop(unused);
109
110     m_autoCaptureEnabled = false;
111     m_instruments.clear();
112 }
113
114 void InspectorTimelineAgent::start(ErrorString&, const int* maxCallStackDepth)
115 {
116     m_enabledFromFrontend = true;
117
118     internalStart(maxCallStackDepth);
119 }
120
121 void InspectorTimelineAgent::stop(ErrorString&)
122 {
123     internalStop();
124
125     m_enabledFromFrontend = false;
126 }
127
128 void InspectorTimelineAgent::setAutoCaptureEnabled(ErrorString&, bool enabled)
129 {
130     m_autoCaptureEnabled = enabled;
131 }
132
133 void InspectorTimelineAgent::setInstruments(ErrorString& errorString, const InspectorArray& instruments)
134 {
135     Vector<Protocol::Timeline::Instrument> newInstruments;
136     newInstruments.reserveCapacity(instruments.length());
137
138     for (auto instrumentValue : instruments) {
139         String enumValueString;
140         if (!instrumentValue->asString(enumValueString)) {
141             errorString = ASCIILiteral("Unexpected type in instruments list, should be string");
142             return;
143         }
144
145         std::optional<Protocol::Timeline::Instrument> instrumentType = Protocol::InspectorHelpers::parseEnumValueFromString<Protocol::Timeline::Instrument>(enumValueString);
146         if (!instrumentType) {
147             errorString = makeString("Unexpected enum value: ", enumValueString);
148             return;
149         }
150
151         newInstruments.uncheckedAppend(*instrumentType);
152     }
153
154     m_instruments.swap(newInstruments);
155 }
156
157 void InspectorTimelineAgent::internalStart(const int* maxCallStackDepth)
158 {
159     if (m_enabled)
160         return;
161
162     if (maxCallStackDepth && *maxCallStackDepth > 0)
163         m_maxCallStackDepth = *maxCallStackDepth;
164     else
165         m_maxCallStackDepth = 5;
166
167     m_instrumentingAgents.setInspectorTimelineAgent(this);
168
169     m_environment.scriptDebugServer().addListener(this);
170
171     m_enabled = true;
172
173     // FIXME: Abstract away platform-specific code once https://bugs.webkit.org/show_bug.cgi?id=142748 is fixed.
174
175 #if PLATFORM(COCOA)
176     m_frameStartObserver = std::make_unique<RunLoopObserver>(0, [this]() {
177         if (!m_enabled || m_environment.scriptDebugServer().isPaused())
178             return;
179
180         if (!m_runLoopNestingLevel)
181             pushCurrentRecord(InspectorObject::create(), TimelineRecordType::RenderingFrame, false, nullptr);
182         m_runLoopNestingLevel++;
183     });
184
185     m_frameStopObserver = std::make_unique<RunLoopObserver>(frameStopRunLoopOrder, [this]() {
186         if (!m_enabled || m_environment.scriptDebugServer().isPaused())
187             return;
188
189         ASSERT(m_runLoopNestingLevel > 0);
190         m_runLoopNestingLevel--;
191         if (m_runLoopNestingLevel)
192             return;
193
194         if (m_startedComposite)
195             didComposite();
196
197         didCompleteCurrentRecord(TimelineRecordType::RenderingFrame);
198     });
199
200     m_frameStartObserver->schedule(currentRunLoop(), kCFRunLoopEntry | kCFRunLoopAfterWaiting);
201     m_frameStopObserver->schedule(currentRunLoop(), kCFRunLoopExit | kCFRunLoopBeforeWaiting);
202
203     // Create a runloop record and increment the runloop nesting level, to capture the current turn of the main runloop
204     // (which is the outer runloop if recording started while paused in the debugger).
205     pushCurrentRecord(InspectorObject::create(), TimelineRecordType::RenderingFrame, false, nullptr);
206
207     m_runLoopNestingLevel = 1;
208 #endif
209
210     m_frontendDispatcher->recordingStarted(timestamp());
211 }
212
213 void InspectorTimelineAgent::internalStop()
214 {
215     if (!m_enabled)
216         return;
217
218     m_instrumentingAgents.setInspectorTimelineAgent(nullptr);
219
220     m_environment.scriptDebugServer().removeListener(this, true);
221
222 #if PLATFORM(COCOA)
223     m_frameStartObserver = nullptr;
224     m_frameStopObserver = nullptr;
225     m_runLoopNestingLevel = 0;
226
227     // Complete all pending records to prevent discarding events that are currently in progress.
228     while (!m_recordStack.isEmpty())
229         didCompleteCurrentRecord(m_recordStack.last().type);
230 #endif
231
232     clearRecordStack();
233
234     m_enabled = false;
235     m_startedComposite = false;
236     m_autoCapturePhase = AutoCapturePhase::None;
237
238     m_frontendDispatcher->recordingStopped(timestamp());
239 }
240
241 double InspectorTimelineAgent::timestamp()
242 {
243     return m_environment.executionStopwatch()->elapsedTime();
244 }
245
246 void InspectorTimelineAgent::startFromConsole(JSC::ExecState* exec, const String& title)
247 {
248     // Allow duplicate unnamed profiles. Disallow duplicate named profiles.
249     if (!title.isEmpty()) {
250         for (const TimelineRecordEntry& record : m_pendingConsoleProfileRecords) {
251             String recordTitle;
252             record.data->getString(ASCIILiteral("title"), recordTitle);
253             if (recordTitle == title) {
254                 if (WebConsoleAgent* consoleAgent = m_instrumentingAgents.webConsoleAgent()) {
255                     // FIXME: Send an enum to the frontend for localization?
256                     String warning = title.isEmpty() ? ASCIILiteral("Unnamed Profile already exists") : makeString("Profile \"", title, "\" already exists");
257                     consoleAgent->addMessageToConsole(std::make_unique<ConsoleMessage>(MessageSource::ConsoleAPI, MessageType::Profile, MessageLevel::Warning, warning));
258                 }
259                 return;
260             }
261         }
262     }
263
264     if (!m_enabled && m_pendingConsoleProfileRecords.isEmpty())
265         startProgrammaticCapture();
266
267     m_pendingConsoleProfileRecords.append(createRecordEntry(TimelineRecordFactory::createConsoleProfileData(title), TimelineRecordType::ConsoleProfile, true, frameFromExecState(exec)));
268 }
269
270 void InspectorTimelineAgent::stopFromConsole(JSC::ExecState*, const String& title)
271 {
272     // Stop profiles in reverse order. If the title is empty, then stop the last profile.
273     // Otherwise, match the title of the profile to stop.
274     for (int i = m_pendingConsoleProfileRecords.size() - 1; i >= 0; --i) {
275         const TimelineRecordEntry& record = m_pendingConsoleProfileRecords[i];
276
277         String recordTitle;
278         record.data->getString(ASCIILiteral("title"), recordTitle);
279         if (title.isEmpty() || recordTitle == title) {
280             didCompleteRecordEntry(record);
281             m_pendingConsoleProfileRecords.remove(i);
282
283             if (!m_enabledFromFrontend && m_pendingConsoleProfileRecords.isEmpty())
284                 stopProgrammaticCapture();
285
286             return;
287         }
288     }
289
290     if (WebConsoleAgent* consoleAgent = m_instrumentingAgents.webConsoleAgent()) {
291         // FIXME: Send an enum to the frontend for localization?
292         String warning = title.isEmpty() ? ASCIILiteral("No profiles exist") : makeString("Profile \"", title, "\" does not exist");
293         consoleAgent->addMessageToConsole(std::make_unique<ConsoleMessage>(MessageSource::ConsoleAPI, MessageType::ProfileEnd, MessageLevel::Warning, warning));
294     }
295 }
296
297 void InspectorTimelineAgent::willCallFunction(const String& scriptName, int scriptLine, Frame* frame)
298 {
299     pushCurrentRecord(TimelineRecordFactory::createFunctionCallData(scriptName, scriptLine), TimelineRecordType::FunctionCall, true, frame);
300 }
301
302 void InspectorTimelineAgent::didCallFunction(Frame*)
303 {
304     didCompleteCurrentRecord(TimelineRecordType::FunctionCall);
305 }
306
307 void InspectorTimelineAgent::willDispatchEvent(const Event& event, Frame* frame)
308 {
309     pushCurrentRecord(TimelineRecordFactory::createEventDispatchData(event), TimelineRecordType::EventDispatch, false, frame);
310 }
311
312 void InspectorTimelineAgent::didDispatchEvent()
313 {
314     didCompleteCurrentRecord(TimelineRecordType::EventDispatch);
315 }
316
317 void InspectorTimelineAgent::didInvalidateLayout(Frame& frame)
318 {
319     appendRecord(InspectorObject::create(), TimelineRecordType::InvalidateLayout, true, &frame);
320 }
321
322 void InspectorTimelineAgent::willLayout(Frame& frame)
323 {
324     pushCurrentRecord(InspectorObject::create(), TimelineRecordType::Layout, true, &frame);
325 }
326
327 void InspectorTimelineAgent::didLayout(RenderObject& root)
328 {
329     if (m_recordStack.isEmpty())
330         return;
331     TimelineRecordEntry& entry = m_recordStack.last();
332     ASSERT(entry.type == TimelineRecordType::Layout);
333     Vector<FloatQuad> quads;
334     root.absoluteQuads(quads);
335     if (quads.size() >= 1)
336         TimelineRecordFactory::appendLayoutRoot(entry.data.get(), quads[0]);
337     else
338         ASSERT_NOT_REACHED();
339     didCompleteCurrentRecord(TimelineRecordType::Layout);
340 }
341
342 void InspectorTimelineAgent::didScheduleStyleRecalculation(Frame* frame)
343 {
344     appendRecord(InspectorObject::create(), TimelineRecordType::ScheduleStyleRecalculation, true, frame);
345 }
346
347 void InspectorTimelineAgent::willRecalculateStyle(Frame* frame)
348 {
349     pushCurrentRecord(InspectorObject::create(), TimelineRecordType::RecalculateStyles, true, frame);
350 }
351
352 void InspectorTimelineAgent::didRecalculateStyle()
353 {
354     didCompleteCurrentRecord(TimelineRecordType::RecalculateStyles);
355 }
356
357 void InspectorTimelineAgent::willComposite(Frame& frame)
358 {
359     ASSERT(!m_startedComposite);
360     pushCurrentRecord(InspectorObject::create(), TimelineRecordType::Composite, true, &frame);
361     m_startedComposite = true;
362 }
363
364 void InspectorTimelineAgent::didComposite()
365 {
366     ASSERT(m_startedComposite);
367     didCompleteCurrentRecord(TimelineRecordType::Composite);
368     m_startedComposite = false;
369 }
370
371 void InspectorTimelineAgent::willPaint(Frame& frame)
372 {
373     pushCurrentRecord(InspectorObject::create(), TimelineRecordType::Paint, true, &frame);
374 }
375
376 void InspectorTimelineAgent::didPaint(RenderObject& renderer, const LayoutRect& clipRect)
377 {
378     TimelineRecordEntry& entry = m_recordStack.last();
379     ASSERT(entry.type == TimelineRecordType::Paint);
380     FloatQuad quad;
381     localToPageQuad(renderer, clipRect, &quad);
382     entry.data = TimelineRecordFactory::createPaintData(quad);
383     didCompleteCurrentRecord(TimelineRecordType::Paint);
384 }
385
386 void InspectorTimelineAgent::didInstallTimer(int timerId, Seconds timeout, bool singleShot, Frame* frame)
387 {
388     appendRecord(TimelineRecordFactory::createTimerInstallData(timerId, timeout, singleShot), TimelineRecordType::TimerInstall, true, frame);
389 }
390
391 void InspectorTimelineAgent::didRemoveTimer(int timerId, Frame* frame)
392 {
393     appendRecord(TimelineRecordFactory::createGenericTimerData(timerId), TimelineRecordType::TimerRemove, true, frame);
394 }
395
396 void InspectorTimelineAgent::willFireTimer(int timerId, Frame* frame)
397 {
398     pushCurrentRecord(TimelineRecordFactory::createGenericTimerData(timerId), TimelineRecordType::TimerFire, false, frame);
399 }
400
401 void InspectorTimelineAgent::didFireTimer()
402 {
403     didCompleteCurrentRecord(TimelineRecordType::TimerFire);
404 }
405
406 void InspectorTimelineAgent::willEvaluateScript(const String& url, int lineNumber, Frame& frame)
407 {
408     pushCurrentRecord(TimelineRecordFactory::createEvaluateScriptData(url, lineNumber), TimelineRecordType::EvaluateScript, true, &frame);
409 }
410
411 void InspectorTimelineAgent::didEvaluateScript(Frame&)
412 {
413     didCompleteCurrentRecord(TimelineRecordType::EvaluateScript);
414 }
415
416 void InspectorTimelineAgent::didTimeStamp(Frame& frame, const String& message)
417 {
418     appendRecord(TimelineRecordFactory::createTimeStampData(message), TimelineRecordType::TimeStamp, true, &frame);
419 }
420
421 void InspectorTimelineAgent::time(Frame& frame, const String& message)
422 {
423     appendRecord(TimelineRecordFactory::createTimeStampData(message), TimelineRecordType::Time, true, &frame);
424 }
425
426 void InspectorTimelineAgent::timeEnd(Frame& frame, const String& message)
427 {
428     appendRecord(TimelineRecordFactory::createTimeStampData(message), TimelineRecordType::TimeEnd, true, &frame);
429 }
430
431 void InspectorTimelineAgent::mainFrameStartedLoading()
432 {
433     if (m_enabled)
434         return;
435
436     if (!m_autoCaptureEnabled)
437         return;
438
439     if (m_instruments.isEmpty())
440         return;
441
442     m_autoCapturePhase = AutoCapturePhase::BeforeLoad;
443
444     // Pre-emptively disable breakpoints. The frontend must re-enable them.
445     if (InspectorDebuggerAgent* debuggerAgent = m_instrumentingAgents.inspectorDebuggerAgent()) {
446         ErrorString unused;
447         debuggerAgent->setBreakpointsActive(unused, false);
448     }
449
450     // Inform the frontend we started an auto capture. The frontend must stop capture.
451     m_frontendDispatcher->autoCaptureStarted();
452
453     toggleInstruments(InstrumentState::Start);
454 }
455
456 void InspectorTimelineAgent::mainFrameNavigated()
457 {
458     if (m_autoCapturePhase == AutoCapturePhase::BeforeLoad) {
459         m_autoCapturePhase = AutoCapturePhase::FirstNavigation;
460         toggleInstruments(InstrumentState::Start);
461         m_autoCapturePhase = AutoCapturePhase::AfterFirstNavigation;
462     }
463 }
464
465 void InspectorTimelineAgent::startProgrammaticCapture()
466 {
467     ASSERT(!m_enabled);
468
469     // Disable breakpoints during programmatic capture.
470     if (InspectorDebuggerAgent* debuggerAgent = m_instrumentingAgents.inspectorDebuggerAgent()) {
471         m_programmaticCaptureRestoreBreakpointActiveValue = debuggerAgent->breakpointsActive();
472         if (m_programmaticCaptureRestoreBreakpointActiveValue) {
473             ErrorString unused;
474             debuggerAgent->setBreakpointsActive(unused, false);
475         }
476     } else
477         m_programmaticCaptureRestoreBreakpointActiveValue = false;
478
479     m_frontendDispatcher->programmaticCaptureStarted();
480
481     toggleScriptProfilerInstrument(InstrumentState::Start); // Ensure JavaScript samping data.
482     toggleTimelineInstrument(InstrumentState::Start); // Ensure Console Profile event records.
483     toggleInstruments(InstrumentState::Start); // Any other instruments the frontend wants us to record.
484 }
485
486 void InspectorTimelineAgent::stopProgrammaticCapture()
487 {
488     ASSERT(m_enabled);
489     ASSERT(!m_enabledFromFrontend);
490
491     toggleInstruments(InstrumentState::Stop);
492     toggleTimelineInstrument(InstrumentState::Stop);
493     toggleScriptProfilerInstrument(InstrumentState::Stop);
494
495     // Re-enable breakpoints if they were enabled.
496     if (m_programmaticCaptureRestoreBreakpointActiveValue) {
497         if (InspectorDebuggerAgent* debuggerAgent = m_instrumentingAgents.inspectorDebuggerAgent()) {
498             ErrorString unused;
499             debuggerAgent->setBreakpointsActive(unused, true);
500         }
501     }
502
503     m_frontendDispatcher->programmaticCaptureStopped();
504 }
505
506 void InspectorTimelineAgent::toggleInstruments(InstrumentState state)
507 {
508     for (auto instrumentType : m_instruments) {
509         switch (instrumentType) {
510         case Inspector::Protocol::Timeline::Instrument::ScriptProfiler: {
511             toggleScriptProfilerInstrument(state);
512             break;
513         }
514         case Inspector::Protocol::Timeline::Instrument::Heap: {
515             toggleHeapInstrument(state);
516             break;
517         }
518         case Inspector::Protocol::Timeline::Instrument::Memory: {
519             toggleMemoryInstrument(state);
520             break;
521         }
522         case Inspector::Protocol::Timeline::Instrument::Timeline:
523             toggleTimelineInstrument(state);
524             break;
525         }
526     }
527 }
528
529 void InspectorTimelineAgent::toggleScriptProfilerInstrument(InstrumentState state)
530 {
531     if (m_scriptProfilerAgent) {
532         ErrorString unused;
533         if (state == InstrumentState::Start) {
534             const bool includeSamples = true;
535             m_scriptProfilerAgent->startTracking(unused, &includeSamples);
536         } else
537             m_scriptProfilerAgent->stopTracking(unused);
538     }
539 }
540
541 void InspectorTimelineAgent::toggleHeapInstrument(InstrumentState state)
542 {
543     if (m_heapAgent) {
544         ErrorString unused;
545         if (state == InstrumentState::Start) {
546             if (m_autoCapturePhase == AutoCapturePhase::None || m_autoCapturePhase == AutoCapturePhase::FirstNavigation)
547                 m_heapAgent->startTracking(unused);
548         } else
549             m_heapAgent->stopTracking(unused);
550     }
551 }
552
553 void InspectorTimelineAgent::toggleMemoryInstrument(InstrumentState state)
554 {
555 #if ENABLE(RESOURCE_USAGE)
556     if (InspectorMemoryAgent* memoryAgent = m_instrumentingAgents.inspectorMemoryAgent()) {
557         ErrorString unused;
558         if (state == InstrumentState::Start)
559             memoryAgent->startTracking(unused);
560         else
561             memoryAgent->stopTracking(unused);
562     }
563 #else
564     UNUSED_PARAM(state);
565 #endif
566 }
567
568 void InspectorTimelineAgent::toggleTimelineInstrument(InstrumentState state)
569 {
570     if (state == InstrumentState::Start)
571         internalStart();
572     else
573         internalStop();
574 }
575
576 void InspectorTimelineAgent::didCommitLoad()
577 {
578     clearRecordStack();
579 }
580
581 void InspectorTimelineAgent::didRequestAnimationFrame(int callbackId, Frame* frame)
582 {
583     appendRecord(TimelineRecordFactory::createAnimationFrameData(callbackId), TimelineRecordType::RequestAnimationFrame, true, frame);
584 }
585
586 void InspectorTimelineAgent::didCancelAnimationFrame(int callbackId, Frame* frame)
587 {
588     appendRecord(TimelineRecordFactory::createAnimationFrameData(callbackId), TimelineRecordType::CancelAnimationFrame, true, frame);
589 }
590
591 void InspectorTimelineAgent::willFireAnimationFrame(int callbackId, Frame* frame)
592 {
593     pushCurrentRecord(TimelineRecordFactory::createAnimationFrameData(callbackId), TimelineRecordType::FireAnimationFrame, false, frame);
594 }
595
596 void InspectorTimelineAgent::didFireAnimationFrame()
597 {
598     didCompleteCurrentRecord(TimelineRecordType::FireAnimationFrame);
599 }
600
601 // ScriptDebugListener
602
603 void InspectorTimelineAgent::breakpointActionProbe(JSC::ExecState& state, const Inspector::ScriptBreakpointAction& action, unsigned /*batchId*/, unsigned sampleId, JSC::JSValue)
604 {
605     appendRecord(TimelineRecordFactory::createProbeSampleData(action, sampleId), TimelineRecordType::ProbeSample, false, frameFromExecState(&state));
606 }
607
608 static Inspector::Protocol::Timeline::EventType toProtocol(TimelineRecordType type)
609 {
610     switch (type) {
611     case TimelineRecordType::EventDispatch:
612         return Inspector::Protocol::Timeline::EventType::EventDispatch;
613     case TimelineRecordType::ScheduleStyleRecalculation:
614         return Inspector::Protocol::Timeline::EventType::ScheduleStyleRecalculation;
615     case TimelineRecordType::RecalculateStyles:
616         return Inspector::Protocol::Timeline::EventType::RecalculateStyles;
617     case TimelineRecordType::InvalidateLayout:
618         return Inspector::Protocol::Timeline::EventType::InvalidateLayout;
619     case TimelineRecordType::Layout:
620         return Inspector::Protocol::Timeline::EventType::Layout;
621     case TimelineRecordType::Paint:
622         return Inspector::Protocol::Timeline::EventType::Paint;
623     case TimelineRecordType::Composite:
624         return Inspector::Protocol::Timeline::EventType::Composite;
625     case TimelineRecordType::RenderingFrame:
626         return Inspector::Protocol::Timeline::EventType::RenderingFrame;
627
628     case TimelineRecordType::TimerInstall:
629         return Inspector::Protocol::Timeline::EventType::TimerInstall;
630     case TimelineRecordType::TimerRemove:
631         return Inspector::Protocol::Timeline::EventType::TimerRemove;
632     case TimelineRecordType::TimerFire:
633         return Inspector::Protocol::Timeline::EventType::TimerFire;
634
635     case TimelineRecordType::EvaluateScript:
636         return Inspector::Protocol::Timeline::EventType::EvaluateScript;
637
638     case TimelineRecordType::TimeStamp:
639         return Inspector::Protocol::Timeline::EventType::TimeStamp;
640     case TimelineRecordType::Time:
641         return Inspector::Protocol::Timeline::EventType::Time;
642     case TimelineRecordType::TimeEnd:
643         return Inspector::Protocol::Timeline::EventType::TimeEnd;
644
645     case TimelineRecordType::FunctionCall:
646         return Inspector::Protocol::Timeline::EventType::FunctionCall;
647     case TimelineRecordType::ProbeSample:
648         return Inspector::Protocol::Timeline::EventType::ProbeSample;
649     case TimelineRecordType::ConsoleProfile:
650         return Inspector::Protocol::Timeline::EventType::ConsoleProfile;
651
652     case TimelineRecordType::RequestAnimationFrame:
653         return Inspector::Protocol::Timeline::EventType::RequestAnimationFrame;
654     case TimelineRecordType::CancelAnimationFrame:
655         return Inspector::Protocol::Timeline::EventType::CancelAnimationFrame;
656     case TimelineRecordType::FireAnimationFrame:
657         return Inspector::Protocol::Timeline::EventType::FireAnimationFrame;
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::InspectorHelpers::getEnumConstantValue(toProtocol(type)));
667
668     if (m_recordStack.isEmpty()) {
669         auto recordObject = BindingTraits<Inspector::Protocol::Timeline::TimelineEvent>::runtimeCast(WTFMove(record));
670         sendEvent(WTFMove(recordObject));
671     } else {
672         const TimelineRecordEntry& parent = m_recordStack.last();
673         // Nested paint records are an implementation detail and add no information not already contained in the parent.
674         if (type == TimelineRecordType::Paint && parent.type == type)
675             return;
676
677         parent.children->pushObject(WTFMove(record));
678     }
679 }
680
681 void InspectorTimelineAgent::setFrameIdentifier(InspectorObject* record, Frame* frame)
682 {
683     if (!frame || !m_pageAgent)
684         return;
685     String frameId;
686     if (frame && m_pageAgent)
687         frameId = m_pageAgent->frameId(frame);
688     record->setString("frameId", frameId);
689 }
690
691 void InspectorTimelineAgent::didCompleteRecordEntry(const TimelineRecordEntry& entry)
692 {
693     entry.record->setObject(ASCIILiteral("data"), entry.data);
694     entry.record->setArray(ASCIILiteral("children"), entry.children);
695     entry.record->setDouble(ASCIILiteral("endTime"), timestamp());
696     addRecordToTimeline(entry.record.copyRef(), entry.type);
697 }
698
699 void InspectorTimelineAgent::didCompleteCurrentRecord(TimelineRecordType type)
700 {
701     // An empty stack could merely mean that the timeline agent was turned on in the middle of
702     // an event.  Don't treat as an error.
703     if (!m_recordStack.isEmpty()) {
704         TimelineRecordEntry entry = m_recordStack.last();
705         m_recordStack.removeLast();
706         ASSERT_UNUSED(type, entry.type == type);
707
708         // Don't send RenderingFrame records that have no children to reduce noise.
709         if (entry.type == TimelineRecordType::RenderingFrame && !entry.children->length())
710             return;
711
712         didCompleteRecordEntry(entry);
713     }
714 }
715
716 void InspectorTimelineAgent::appendRecord(RefPtr<InspectorObject>&& data, TimelineRecordType type, bool captureCallStack, Frame* frame)
717 {
718     Ref<InspectorObject> record = TimelineRecordFactory::createGenericRecord(timestamp(), captureCallStack ? m_maxCallStackDepth : 0);
719     record->setObject("data", WTFMove(data));
720     setFrameIdentifier(&record.get(), frame);
721     addRecordToTimeline(WTFMove(record), type);
722 }
723
724 void InspectorTimelineAgent::sendEvent(RefPtr<InspectorObject>&& event)
725 {
726     // FIXME: runtimeCast is a hack. We do it because we can't build TimelineEvent directly now.
727     auto recordChecked = BindingTraits<Inspector::Protocol::Timeline::TimelineEvent>::runtimeCast(WTFMove(event));
728     m_frontendDispatcher->eventRecorded(WTFMove(recordChecked));
729 }
730
731 InspectorTimelineAgent::TimelineRecordEntry InspectorTimelineAgent::createRecordEntry(RefPtr<InspectorObject>&& data, TimelineRecordType type, bool captureCallStack, Frame* frame)
732 {
733     Ref<InspectorObject> record = TimelineRecordFactory::createGenericRecord(timestamp(), captureCallStack ? m_maxCallStackDepth : 0);
734     setFrameIdentifier(&record.get(), frame);
735     return TimelineRecordEntry(WTFMove(record), WTFMove(data), InspectorArray::create(), type);
736 }
737
738 void InspectorTimelineAgent::pushCurrentRecord(RefPtr<InspectorObject>&& data, TimelineRecordType type, bool captureCallStack, Frame* frame)
739 {
740     pushCurrentRecord(createRecordEntry(WTFMove(data), type, captureCallStack, frame));
741 }
742
743 void InspectorTimelineAgent::clearRecordStack()
744 {
745     m_recordStack.clear();
746     m_id++;
747 }
748
749 void InspectorTimelineAgent::localToPageQuad(const RenderObject& renderer, const LayoutRect& rect, FloatQuad* quad)
750 {
751     const FrameView& frameView = renderer.view().frameView();
752     FloatQuad absolute = renderer.localToAbsoluteQuad(FloatQuad(rect));
753     quad->setP1(frameView.contentsToRootView(roundedIntPoint(absolute.p1())));
754     quad->setP2(frameView.contentsToRootView(roundedIntPoint(absolute.p2())));
755     quad->setP3(frameView.contentsToRootView(roundedIntPoint(absolute.p3())));
756     quad->setP4(frameView.contentsToRootView(roundedIntPoint(absolute.p4())));
757 }
758
759 } // namespace WebCore