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