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