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