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