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.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
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
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.
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.
34 #include "InspectorTimelineAgent.h"
38 #include "FrameView.h"
39 #include "InspectorClient.h"
40 #include "InspectorInstrumentation.h"
41 #include "InspectorPageAgent.h"
42 #include "InstrumentingAgents.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>
59 #include "RuntimeApplicationChecksIOS.h"
60 #include <WebCore/WebCoreThread.h>
64 #include <WebCore/RunLoopObserver.h>
67 using namespace Inspector;
72 static const CFIndex frameStopRunLoopOrder = (CFIndex)RunLoopObserver::WellKnownRunLoopOrders::CoreAnimationCommit + 1;
74 static CFRunLoopRef currentRunLoop()
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();
85 return CFRunLoopGetCurrent();
89 InspectorTimelineAgent::~InspectorTimelineAgent()
93 void InspectorTimelineAgent::didCreateFrontendAndBackend(Inspector::FrontendRouter*, Inspector::BackendDispatcher*)
95 m_instrumentingAgents.setPersistentInspectorTimelineAgent(this);
97 if (m_scriptDebugServer)
98 m_scriptDebugServer->recompileAllJSFunctions();
101 void InspectorTimelineAgent::willDestroyFrontendAndBackend(Inspector::DisconnectReason reason)
103 m_instrumentingAgents.setPersistentInspectorTimelineAgent(nullptr);
105 if (reason != Inspector::DisconnectReason::InspectedTargetDestroyed) {
106 if (m_scriptDebugServer)
107 m_scriptDebugServer->recompileAllJSFunctions();
114 void InspectorTimelineAgent::start(ErrorString&, const int* maxCallStackDepth)
116 m_enabledFromFrontend = true;
118 internalStart(maxCallStackDepth);
121 void InspectorTimelineAgent::stop(ErrorString&)
125 m_enabledFromFrontend = false;
128 void InspectorTimelineAgent::internalStart(const int* maxCallStackDepth)
133 if (maxCallStackDepth && *maxCallStackDepth > 0)
134 m_maxCallStackDepth = *maxCallStackDepth;
136 m_maxCallStackDepth = 5;
138 m_instrumentingAgents.setInspectorTimelineAgent(this);
140 if (m_scriptDebugServer)
141 m_scriptDebugServer->addListener(this);
145 // FIXME: Abstract away platform-specific code once https://bugs.webkit.org/show_bug.cgi?id=142748 is fixed.
148 m_frameStartObserver = RunLoopObserver::create(0, [this]() {
149 if (!m_enabled || m_scriptDebugServer->isPaused())
152 if (!m_runLoopNestingLevel)
153 pushCurrentRecord(InspectorObject::create(), TimelineRecordType::RenderingFrame, false, nullptr);
154 m_runLoopNestingLevel++;
157 m_frameStopObserver = RunLoopObserver::create(frameStopRunLoopOrder, [this]() {
158 if (!m_enabled || m_scriptDebugServer->isPaused())
161 ASSERT(m_runLoopNestingLevel > 0);
162 m_runLoopNestingLevel--;
163 if (m_runLoopNestingLevel)
166 if (m_startedComposite)
169 didCompleteCurrentRecord(TimelineRecordType::RenderingFrame);
172 m_frameStartObserver->schedule(currentRunLoop(), kCFRunLoopEntry | kCFRunLoopAfterWaiting);
173 m_frameStopObserver->schedule(currentRunLoop(), kCFRunLoopExit | kCFRunLoopBeforeWaiting);
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);
179 m_runLoopNestingLevel = 1;
182 m_frontendDispatcher->recordingStarted(timestamp());
185 void InspectorTimelineAgent::internalStop()
190 m_instrumentingAgents.setInspectorTimelineAgent(nullptr);
192 if (m_scriptDebugServer)
193 m_scriptDebugServer->removeListener(this, true);
196 m_frameStartObserver = nullptr;
197 m_frameStopObserver = nullptr;
198 m_runLoopNestingLevel = 0;
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);
208 m_startedComposite = false;
210 m_frontendDispatcher->recordingStopped(timestamp());
213 double InspectorTimelineAgent::timestamp()
215 return m_instrumentingAgents.inspectorEnvironment().executionStopwatch()->elapsedTime();
218 void InspectorTimelineAgent::setPageScriptDebugServer(PageScriptDebugServer* scriptDebugServer)
221 ASSERT(!m_scriptDebugServer);
223 m_scriptDebugServer = scriptDebugServer;
226 static inline void startProfiling(JSC::ExecState* exec, const String& title, PassRefPtr<Stopwatch> stopwatch)
228 JSC::LegacyProfiler::profiler()->startProfiling(exec, title, stopwatch);
231 static inline PassRefPtr<JSC::Profile> stopProfiling(JSC::ExecState* exec, const String& title)
233 return JSC::LegacyProfiler::profiler()->stopProfiling(exec, title);
236 static inline void startProfiling(Frame* frame, const String& title, PassRefPtr<Stopwatch> stopwatch)
238 startProfiling(toJSDOMWindow(frame, debuggerWorld())->globalExec(), title, stopwatch);
241 static inline PassRefPtr<JSC::Profile> stopProfiling(Frame* frame, const String& title)
243 return stopProfiling(toJSDOMWindow(frame, debuggerWorld())->globalExec(), title);
246 void InspectorTimelineAgent::startFromConsole(JSC::ExecState* exec, const String &title)
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) {
253 record.data->getString(ASCIILiteral("title"), recordTitle);
254 if (recordTitle == title)
259 if (!m_enabled && m_pendingConsoleProfileRecords.isEmpty())
262 startProfiling(exec, title, m_instrumentingAgents.inspectorEnvironment().executionStopwatch());
264 m_pendingConsoleProfileRecords.append(createRecordEntry(TimelineRecordFactory::createConsoleProfileData(title), TimelineRecordType::ConsoleProfile, true, frameFromExecState(exec)));
267 PassRefPtr<JSC::Profile> InspectorTimelineAgent::stopFromConsole(JSC::ExecState* exec, const String& title)
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];
275 record.data->getString(ASCIILiteral("title"), recordTitle);
277 if (title.isEmpty() || recordTitle == title) {
278 RefPtr<JSC::Profile> profile = stopProfiling(exec, title);
280 TimelineRecordFactory::appendProfile(record.data.get(), profile.copyRef());
282 didCompleteRecordEntry(record);
284 m_pendingConsoleProfileRecords.remove(i);
286 if (!m_enabledFromFrontend && m_pendingConsoleProfileRecords.isEmpty())
289 return WTF::move(profile);
296 void InspectorTimelineAgent::willCallFunction(const String& scriptName, int scriptLine, Frame* frame)
298 pushCurrentRecord(TimelineRecordFactory::createFunctionCallData(scriptName, scriptLine), TimelineRecordType::FunctionCall, true, frame);
300 if (frame && !m_callStackDepth)
301 startProfiling(frame, ASCIILiteral("Timeline FunctionCall"), m_instrumentingAgents.inspectorEnvironment().executionStopwatch());
306 void InspectorTimelineAgent::didCallFunction(Frame* frame)
308 if (frame && m_callStackDepth) {
310 ASSERT(m_callStackDepth >= 0);
312 if (!m_callStackDepth) {
313 if (m_recordStack.isEmpty())
316 TimelineRecordEntry& entry = m_recordStack.last();
317 ASSERT(entry.type == TimelineRecordType::FunctionCall);
319 RefPtr<JSC::Profile> profile = stopProfiling(frame, ASCIILiteral("Timeline FunctionCall"));
321 TimelineRecordFactory::appendProfile(entry.data.get(), profile.release());
325 didCompleteCurrentRecord(TimelineRecordType::FunctionCall);
328 void InspectorTimelineAgent::willDispatchEvent(const Event& event, Frame* frame)
330 pushCurrentRecord(TimelineRecordFactory::createEventDispatchData(event), TimelineRecordType::EventDispatch, false, frame);
333 void InspectorTimelineAgent::didDispatchEvent()
335 didCompleteCurrentRecord(TimelineRecordType::EventDispatch);
338 void InspectorTimelineAgent::didInvalidateLayout(Frame& frame)
340 appendRecord(InspectorObject::create(), TimelineRecordType::InvalidateLayout, true, &frame);
343 void InspectorTimelineAgent::willLayout(Frame& frame)
345 auto* root = frame.view()->layoutRoot();
346 bool partialLayout = !!root;
349 root = frame.contentRenderer();
351 unsigned dirtyObjects = 0;
352 unsigned totalObjects = 0;
353 for (RenderObject* o = root; o; o = o->nextInPreOrder(root)) {
355 if (o->needsLayout())
358 pushCurrentRecord(TimelineRecordFactory::createLayoutData(dirtyObjects, totalObjects, partialLayout), TimelineRecordType::Layout, true, &frame);
361 void InspectorTimelineAgent::didLayout(RenderObject* root)
363 if (m_recordStack.isEmpty())
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]);
372 ASSERT_NOT_REACHED();
373 didCompleteCurrentRecord(TimelineRecordType::Layout);
376 void InspectorTimelineAgent::didScheduleStyleRecalculation(Frame* frame)
378 appendRecord(InspectorObject::create(), TimelineRecordType::ScheduleStyleRecalculation, true, frame);
381 void InspectorTimelineAgent::willRecalculateStyle(Frame* frame)
383 pushCurrentRecord(InspectorObject::create(), TimelineRecordType::RecalculateStyles, true, frame);
386 void InspectorTimelineAgent::didRecalculateStyle()
388 didCompleteCurrentRecord(TimelineRecordType::RecalculateStyles);
391 void InspectorTimelineAgent::willComposite(Frame& frame)
393 ASSERT(!m_startedComposite);
394 pushCurrentRecord(InspectorObject::create(), TimelineRecordType::Composite, true, &frame);
395 m_startedComposite = true;
398 void InspectorTimelineAgent::didComposite()
400 ASSERT(m_startedComposite);
401 didCompleteCurrentRecord(TimelineRecordType::Composite);
402 m_startedComposite = false;
405 void InspectorTimelineAgent::willPaint(Frame& frame)
407 pushCurrentRecord(InspectorObject::create(), TimelineRecordType::Paint, true, &frame);
410 void InspectorTimelineAgent::didPaint(RenderObject* renderer, const LayoutRect& clipRect)
412 TimelineRecordEntry& entry = m_recordStack.last();
413 ASSERT(entry.type == TimelineRecordType::Paint);
415 localToPageQuad(*renderer, clipRect, &quad);
416 entry.data = TimelineRecordFactory::createPaintData(quad);
417 didCompleteCurrentRecord(TimelineRecordType::Paint);
420 void InspectorTimelineAgent::willScroll(Frame& frame)
422 pushCurrentRecord(InspectorObject::create(), TimelineRecordType::ScrollLayer, false, &frame);
425 void InspectorTimelineAgent::didScroll()
427 didCompleteCurrentRecord(TimelineRecordType::ScrollLayer);
430 void InspectorTimelineAgent::willWriteHTML(unsigned startLine, Frame* frame)
432 pushCurrentRecord(TimelineRecordFactory::createParseHTMLData(startLine), TimelineRecordType::ParseHTML, true, frame);
435 void InspectorTimelineAgent::didWriteHTML(unsigned endLine)
437 if (!m_recordStack.isEmpty()) {
438 const TimelineRecordEntry& entry = m_recordStack.last();
439 entry.data->setInteger("endLine", endLine);
440 didCompleteCurrentRecord(TimelineRecordType::ParseHTML);
444 void InspectorTimelineAgent::didInstallTimer(int timerId, int timeout, bool singleShot, Frame* frame)
446 appendRecord(TimelineRecordFactory::createTimerInstallData(timerId, timeout, singleShot), TimelineRecordType::TimerInstall, true, frame);
449 void InspectorTimelineAgent::didRemoveTimer(int timerId, Frame* frame)
451 appendRecord(TimelineRecordFactory::createGenericTimerData(timerId), TimelineRecordType::TimerRemove, true, frame);
454 void InspectorTimelineAgent::willFireTimer(int timerId, Frame* frame)
456 pushCurrentRecord(TimelineRecordFactory::createGenericTimerData(timerId), TimelineRecordType::TimerFire, false, frame);
459 void InspectorTimelineAgent::didFireTimer()
461 didCompleteCurrentRecord(TimelineRecordType::TimerFire);
464 void InspectorTimelineAgent::willDispatchXHRReadyStateChangeEvent(const String& url, int readyState, Frame* frame)
466 pushCurrentRecord(TimelineRecordFactory::createXHRReadyStateChangeData(url, readyState), TimelineRecordType::XHRReadyStateChange, false, frame);
469 void InspectorTimelineAgent::didDispatchXHRReadyStateChangeEvent()
471 didCompleteCurrentRecord(TimelineRecordType::XHRReadyStateChange);
474 void InspectorTimelineAgent::willDispatchXHRLoadEvent(const String& url, Frame* frame)
476 pushCurrentRecord(TimelineRecordFactory::createXHRLoadData(url), TimelineRecordType::XHRLoad, true, frame);
479 void InspectorTimelineAgent::didDispatchXHRLoadEvent()
481 didCompleteCurrentRecord(TimelineRecordType::XHRLoad);
484 void InspectorTimelineAgent::willEvaluateScript(const String& url, int lineNumber, Frame& frame)
486 pushCurrentRecord(TimelineRecordFactory::createEvaluateScriptData(url, lineNumber), TimelineRecordType::EvaluateScript, true, &frame);
488 if (!m_callStackDepth) {
490 startProfiling(&frame, ASCIILiteral("Timeline EvaluateScript"), m_instrumentingAgents.inspectorEnvironment().executionStopwatch());
494 void InspectorTimelineAgent::didEvaluateScript(Frame& frame)
496 if (m_callStackDepth) {
498 ASSERT(m_callStackDepth >= 0);
500 if (!m_callStackDepth) {
501 if (m_recordStack.isEmpty())
504 TimelineRecordEntry& entry = m_recordStack.last();
505 ASSERT(entry.type == TimelineRecordType::EvaluateScript);
507 RefPtr<JSC::Profile> profile = stopProfiling(&frame, ASCIILiteral("Timeline EvaluateScript"));
509 TimelineRecordFactory::appendProfile(entry.data.get(), profile.release());
513 didCompleteCurrentRecord(TimelineRecordType::EvaluateScript);
516 void InspectorTimelineAgent::didTimeStamp(Frame& frame, const String& message)
518 appendRecord(TimelineRecordFactory::createTimeStampData(message), TimelineRecordType::TimeStamp, true, &frame);
521 void InspectorTimelineAgent::time(Frame& frame, const String& message)
523 appendRecord(TimelineRecordFactory::createTimeStampData(message), TimelineRecordType::Time, true, &frame);
526 void InspectorTimelineAgent::timeEnd(Frame& frame, const String& message)
528 appendRecord(TimelineRecordFactory::createTimeStampData(message), TimelineRecordType::TimeEnd, true, &frame);
531 void InspectorTimelineAgent::didMarkDOMContentEvent(Frame& frame)
533 appendRecord(TimelineRecordFactory::createMarkData(frame.isMainFrame()), TimelineRecordType::MarkDOMContent, false, &frame);
536 void InspectorTimelineAgent::didMarkLoadEvent(Frame& frame)
538 appendRecord(TimelineRecordFactory::createMarkData(frame.isMainFrame()), TimelineRecordType::MarkLoad, false, &frame);
541 void InspectorTimelineAgent::didCommitLoad()
546 void InspectorTimelineAgent::didRequestAnimationFrame(int callbackId, Frame* frame)
548 appendRecord(TimelineRecordFactory::createAnimationFrameData(callbackId), TimelineRecordType::RequestAnimationFrame, true, frame);
551 void InspectorTimelineAgent::didCancelAnimationFrame(int callbackId, Frame* frame)
553 appendRecord(TimelineRecordFactory::createAnimationFrameData(callbackId), TimelineRecordType::CancelAnimationFrame, true, frame);
556 void InspectorTimelineAgent::willFireAnimationFrame(int callbackId, Frame* frame)
558 pushCurrentRecord(TimelineRecordFactory::createAnimationFrameData(callbackId), TimelineRecordType::FireAnimationFrame, false, frame);
561 void InspectorTimelineAgent::didFireAnimationFrame()
563 didCompleteCurrentRecord(TimelineRecordType::FireAnimationFrame);
566 #if ENABLE(WEB_SOCKETS)
567 void InspectorTimelineAgent::didCreateWebSocket(unsigned long identifier, const URL& url, const String& protocol, Frame* frame)
569 appendRecord(TimelineRecordFactory::createWebSocketCreateData(identifier, url, protocol), TimelineRecordType::WebSocketCreate, true, frame);
572 void InspectorTimelineAgent::willSendWebSocketHandshakeRequest(unsigned long identifier, Frame* frame)
574 appendRecord(TimelineRecordFactory::createGenericWebSocketData(identifier), TimelineRecordType::WebSocketSendHandshakeRequest, true, frame);
577 void InspectorTimelineAgent::didReceiveWebSocketHandshakeResponse(unsigned long identifier, Frame* frame)
579 appendRecord(TimelineRecordFactory::createGenericWebSocketData(identifier), TimelineRecordType::WebSocketReceiveHandshakeResponse, false, frame);
582 void InspectorTimelineAgent::didDestroyWebSocket(unsigned long identifier, Frame* frame)
584 appendRecord(TimelineRecordFactory::createGenericWebSocketData(identifier), TimelineRecordType::WebSocketDestroy, true, frame);
586 #endif // ENABLE(WEB_SOCKETS)
588 // ScriptDebugListener
590 void InspectorTimelineAgent::breakpointActionProbe(JSC::ExecState* exec, const Inspector::ScriptBreakpointAction& action, unsigned batchId, unsigned sampleId, const Deprecated::ScriptValue&)
592 UNUSED_PARAM(batchId);
595 appendRecord(TimelineRecordFactory::createProbeSampleData(action, sampleId), TimelineRecordType::ProbeSample, false, frameFromExecState(exec));
598 static Inspector::Protocol::Timeline::EventType toProtocol(TimelineRecordType 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;
620 case TimelineRecordType::ParseHTML:
621 return Inspector::Protocol::Timeline::EventType::ParseHTML;
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;
630 case TimelineRecordType::EvaluateScript:
631 return Inspector::Protocol::Timeline::EventType::EvaluateScript;
633 case TimelineRecordType::MarkLoad:
634 return Inspector::Protocol::Timeline::EventType::MarkLoad;
635 case TimelineRecordType::MarkDOMContent:
636 return Inspector::Protocol::Timeline::EventType::MarkDOMContent;
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;
645 case TimelineRecordType::XHRReadyStateChange:
646 return Inspector::Protocol::Timeline::EventType::XHRReadyStateChange;
647 case TimelineRecordType::XHRLoad:
648 return Inspector::Protocol::Timeline::EventType::XHRLoad;
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;
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;
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;
674 return Inspector::Protocol::Timeline::EventType::TimeStamp;
677 void InspectorTimelineAgent::addRecordToTimeline(RefPtr<InspectorObject>&& record, TimelineRecordType type)
679 ASSERT_ARG(record, record);
680 record->setString("type", Inspector::Protocol::getEnumConstantValue(toProtocol(type)));
682 if (m_recordStack.isEmpty()) {
683 auto recordObject = BindingTraits<Inspector::Protocol::Timeline::TimelineEvent>::runtimeCast(WTF::move(record));
684 sendEvent(WTF::move(recordObject));
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)
691 parent.children->pushObject(WTF::move(record));
695 void InspectorTimelineAgent::setFrameIdentifier(InspectorObject* record, Frame* frame)
697 if (!frame || !m_pageAgent)
700 if (frame && m_pageAgent)
701 frameId = m_pageAgent->frameId(frame);
702 record->setString("frameId", frameId);
705 void InspectorTimelineAgent::didCompleteRecordEntry(const TimelineRecordEntry& entry)
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);
713 void InspectorTimelineAgent::didCompleteCurrentRecord(TimelineRecordType type)
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);
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)
731 , m_inspectorType(type)
735 void InspectorTimelineAgent::appendRecord(RefPtr<InspectorObject>&& data, TimelineRecordType type, bool captureCallStack, Frame* frame)
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);
743 void InspectorTimelineAgent::sendEvent(RefPtr<InspectorObject>&& event)
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));
750 InspectorTimelineAgent::TimelineRecordEntry InspectorTimelineAgent::createRecordEntry(RefPtr<InspectorObject>&& data, TimelineRecordType type, bool captureCallStack, Frame* frame)
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);
757 void InspectorTimelineAgent::pushCurrentRecord(RefPtr<InspectorObject>&& data, TimelineRecordType type, bool captureCallStack, Frame* frame)
759 pushCurrentRecord(createRecordEntry(WTF::move(data), type, captureCallStack, frame));
762 void InspectorTimelineAgent::clearRecordStack()
764 m_recordStack.clear();
768 void InspectorTimelineAgent::localToPageQuad(const RenderObject& renderer, const LayoutRect& rect, FloatQuad* quad)
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())));
778 } // namespace WebCore