1/*
2* Copyright (C) 2013 Google Inc. All rights reserved.
3* Copyright (C) 2014 University of Washington.
4*
5* Redistribution and use in source and binary forms, with or without
6* modification, are permitted provided that the following conditions are
7* met:
8*
9*     * Redistributions of source code must retain the above copyright
10* notice, this list of conditions and the following disclaimer.
11*     * Redistributions in binary form must reproduce the above
12* copyright notice, this list of conditions and the following disclaimer
13* in the documentation and/or other materials provided with the
14* distribution.
15*     * Neither the name of Google Inc. nor the names of its
16* contributors may be used to endorse or promote products derived from
17* this software without specific prior written permission.
18*
19* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30*/
31
32#include "config.h"
33
34#if ENABLE(INSPECTOR)
35
36#include "InspectorTimelineAgent.h"
37
38#include "Event.h"
39#include "Frame.h"
40#include "FrameView.h"
41#include "InspectorClient.h"
42#include "InspectorInstrumentation.h"
43#include "InspectorPageAgent.h"
44#include "InspectorWebFrontendDispatchers.h"
45#include "InstrumentingAgents.h"
46#include "IntRect.h"
47#include "JSDOMWindow.h"
48#include "PageScriptDebugServer.h"
49#include "RenderElement.h"
50#include "RenderView.h"
51#include "ResourceRequest.h"
52#include "ResourceResponse.h"
53#include "ScriptState.h"
54#include "TimelineRecordFactory.h"
55#include <inspector/IdentifiersFactory.h>
56#include <inspector/ScriptBreakpoint.h>
57#include <profiler/LegacyProfiler.h>
58#include <wtf/CurrentTime.h>
59
60using namespace Inspector;
61
62namespace WebCore {
63
64void TimelineTimeConverter::reset()
65{
66    m_startOffset = monotonicallyIncreasingTime() - currentTime();
67}
68
69InspectorTimelineAgent::~InspectorTimelineAgent()
70{
71}
72
73void InspectorTimelineAgent::didCreateFrontendAndBackend(Inspector::InspectorFrontendChannel* frontendChannel, InspectorBackendDispatcher* backendDispatcher)
74{
75    m_frontendDispatcher = std::make_unique<InspectorTimelineFrontendDispatcher>(frontendChannel);
76    m_backendDispatcher = InspectorTimelineBackendDispatcher::create(backendDispatcher, this);
77
78    m_instrumentingAgents->setPersistentInspectorTimelineAgent(this);
79
80    if (m_scriptDebugServer)
81        m_scriptDebugServer->recompileAllJSFunctions();
82}
83
84void InspectorTimelineAgent::willDestroyFrontendAndBackend(InspectorDisconnectReason reason)
85{
86    m_frontendDispatcher = nullptr;
87    m_backendDispatcher.clear();
88
89    m_instrumentingAgents->setPersistentInspectorTimelineAgent(nullptr);
90
91    if (reason != InspectorDisconnectReason::InspectedTargetDestroyed) {
92        if (m_scriptDebugServer)
93            m_scriptDebugServer->recompileAllJSFunctions();
94    }
95
96    ErrorString error;
97    stop(&error);
98}
99
100void InspectorTimelineAgent::start(ErrorString*, const int* maxCallStackDepth)
101{
102    m_enabledFromFrontend = true;
103
104    internalStart(maxCallStackDepth);
105}
106
107void InspectorTimelineAgent::stop(ErrorString*)
108{
109    internalStop();
110
111    m_enabledFromFrontend = false;
112}
113
114void InspectorTimelineAgent::internalStart(const int* maxCallStackDepth)
115{
116    if (maxCallStackDepth && *maxCallStackDepth > 0)
117        m_maxCallStackDepth = *maxCallStackDepth;
118    else
119        m_maxCallStackDepth = 5;
120
121    m_timeConverter.reset();
122
123    m_instrumentingAgents->setInspectorTimelineAgent(this);
124
125    if (m_scriptDebugServer)
126        m_scriptDebugServer->addListener(this);
127
128    m_enabled = true;
129
130    if (m_frontendDispatcher)
131        m_frontendDispatcher->recordingStarted();
132}
133
134void InspectorTimelineAgent::internalStop()
135{
136    if (!m_enabled)
137        return;
138
139    m_instrumentingAgents->setInspectorTimelineAgent(nullptr);
140
141    if (m_scriptDebugServer)
142        m_scriptDebugServer->removeListener(this, true);
143
144    clearRecordStack();
145
146    m_enabled = false;
147
148    if (m_frontendDispatcher)
149        m_frontendDispatcher->recordingStopped();
150}
151
152void InspectorTimelineAgent::setPageScriptDebugServer(PageScriptDebugServer* scriptDebugServer)
153{
154    ASSERT(!m_enabled);
155    ASSERT(!m_scriptDebugServer);
156
157    m_scriptDebugServer = scriptDebugServer;
158}
159
160static inline void startProfiling(JSC::ExecState* exec, const String& title)
161{
162    JSC::LegacyProfiler::profiler()->startProfiling(exec, title);
163}
164
165static inline PassRefPtr<JSC::Profile> stopProfiling(JSC::ExecState* exec, const String& title)
166{
167    return JSC::LegacyProfiler::profiler()->stopProfiling(exec, title);
168}
169
170static inline void startProfiling(Frame* frame, const String& title)
171{
172    startProfiling(toJSDOMWindow(frame, debuggerWorld())->globalExec(), title);
173}
174
175static inline PassRefPtr<JSC::Profile> stopProfiling(Frame* frame, const String& title)
176{
177    return stopProfiling(toJSDOMWindow(frame, debuggerWorld())->globalExec(), title);
178}
179
180void InspectorTimelineAgent::startFromConsole(JSC::ExecState* exec, const String &title)
181{
182    // Only allow recording of a profile if it is anonymous (empty title) or does not match
183    // the title of an already recording profile.
184    if (!title.isEmpty()) {
185        for (const TimelineRecordEntry& record : m_pendingConsoleProfileRecords) {
186            String recordTitle;
187            record.data->getString(ASCIILiteral("title"), &recordTitle);
188            if (recordTitle == title)
189                return;
190        }
191    }
192
193    if (!m_enabled && m_pendingConsoleProfileRecords.isEmpty())
194        internalStart();
195
196    startProfiling(exec, title);
197
198    m_pendingConsoleProfileRecords.append(createRecordEntry(TimelineRecordFactory::createConsoleProfileData(title), TimelineRecordType::ConsoleProfile, true, frameFromExecState(exec)));
199}
200
201PassRefPtr<JSC::Profile> InspectorTimelineAgent::stopFromConsole(JSC::ExecState* exec, const String& title)
202{
203    // Stop profiles in reverse order. If the title is empty, then stop the last profile.
204    // Otherwise, match the title of the profile to stop.
205    for (ptrdiff_t i = m_pendingConsoleProfileRecords.size() - 1; i >= 0; --i) {
206        const TimelineRecordEntry& record = m_pendingConsoleProfileRecords[i];
207
208        String recordTitle;
209        record.data->getString(ASCIILiteral("title"), &recordTitle);
210
211        if (title.isEmpty() || recordTitle == title) {
212            RefPtr<JSC::Profile> profile = stopProfiling(exec, title);
213            if (profile)
214                TimelineRecordFactory::appendProfile(record.data.get(), profile);
215
216            didCompleteRecordEntry(record);
217
218            m_pendingConsoleProfileRecords.remove(i);
219
220            if (!m_enabledFromFrontend && m_pendingConsoleProfileRecords.isEmpty())
221                internalStop();
222
223            return profile.release();
224        }
225    }
226
227    return nullptr;
228}
229
230void InspectorTimelineAgent::willCallFunction(const String& scriptName, int scriptLine, Frame* frame)
231{
232    pushCurrentRecord(TimelineRecordFactory::createFunctionCallData(scriptName, scriptLine), TimelineRecordType::FunctionCall, true, frame);
233
234    if (frame && !m_recordingProfileDepth) {
235        ++m_recordingProfileDepth;
236        startProfiling(frame, ASCIILiteral("Timeline FunctionCall"));
237    }
238}
239
240void InspectorTimelineAgent::didCallFunction(Frame* frame)
241{
242    if (frame && m_recordingProfileDepth) {
243        --m_recordingProfileDepth;
244        ASSERT(m_recordingProfileDepth >= 0);
245
246        if (!m_recordingProfileDepth) {
247            if (m_recordStack.isEmpty())
248                return;
249
250            TimelineRecordEntry& entry = m_recordStack.last();
251            ASSERT(entry.type == TimelineRecordType::FunctionCall);
252
253            RefPtr<JSC::Profile> profile = stopProfiling(frame, ASCIILiteral("Timeline FunctionCall"));
254            if (profile)
255                TimelineRecordFactory::appendProfile(entry.data.get(), profile.release());
256        }
257    }
258
259    didCompleteCurrentRecord(TimelineRecordType::FunctionCall);
260}
261
262void InspectorTimelineAgent::willDispatchEvent(const Event& event, Frame* frame)
263{
264    pushCurrentRecord(TimelineRecordFactory::createEventDispatchData(event), TimelineRecordType::EventDispatch, false, frame);
265}
266
267void InspectorTimelineAgent::didDispatchEvent()
268{
269    didCompleteCurrentRecord(TimelineRecordType::EventDispatch);
270}
271
272void InspectorTimelineAgent::didInvalidateLayout(Frame* frame)
273{
274    appendRecord(InspectorObject::create(), TimelineRecordType::InvalidateLayout, true, frame);
275}
276
277void InspectorTimelineAgent::willLayout(Frame* frame)
278{
279    RenderObject* root = frame->view()->layoutRoot();
280    bool partialLayout = !!root;
281
282    if (!partialLayout)
283        root = frame->contentRenderer();
284
285    unsigned dirtyObjects = 0;
286    unsigned totalObjects = 0;
287    for (RenderObject* o = root; o; o = o->nextInPreOrder(root)) {
288        ++totalObjects;
289        if (o->needsLayout())
290            ++dirtyObjects;
291    }
292    pushCurrentRecord(TimelineRecordFactory::createLayoutData(dirtyObjects, totalObjects, partialLayout), TimelineRecordType::Layout, true, frame);
293}
294
295void InspectorTimelineAgent::didLayout(RenderObject* root)
296{
297    if (m_recordStack.isEmpty())
298        return;
299    TimelineRecordEntry& entry = m_recordStack.last();
300    ASSERT(entry.type == TimelineRecordType::Layout);
301    Vector<FloatQuad> quads;
302    root->absoluteQuads(quads);
303    if (quads.size() >= 1)
304        TimelineRecordFactory::appendLayoutRoot(entry.data.get(), quads[0]);
305    else
306        ASSERT_NOT_REACHED();
307    didCompleteCurrentRecord(TimelineRecordType::Layout);
308}
309
310void InspectorTimelineAgent::didScheduleStyleRecalculation(Frame* frame)
311{
312    appendRecord(InspectorObject::create(), TimelineRecordType::ScheduleStyleRecalculation, true, frame);
313}
314
315void InspectorTimelineAgent::willRecalculateStyle(Frame* frame)
316{
317    pushCurrentRecord(InspectorObject::create(), TimelineRecordType::RecalculateStyles, true, frame);
318}
319
320void InspectorTimelineAgent::didRecalculateStyle()
321{
322    didCompleteCurrentRecord(TimelineRecordType::RecalculateStyles);
323}
324
325void InspectorTimelineAgent::willPaint(Frame* frame)
326{
327    pushCurrentRecord(InspectorObject::create(), TimelineRecordType::Paint, true, frame);
328}
329
330void InspectorTimelineAgent::didPaint(RenderObject* renderer, const LayoutRect& clipRect)
331{
332    TimelineRecordEntry& entry = m_recordStack.last();
333    ASSERT(entry.type == TimelineRecordType::Paint);
334    FloatQuad quad;
335    localToPageQuad(*renderer, clipRect, &quad);
336    entry.data = TimelineRecordFactory::createPaintData(quad);
337    didCompleteCurrentRecord(TimelineRecordType::Paint);
338}
339
340void InspectorTimelineAgent::willScroll(Frame* frame)
341{
342    pushCurrentRecord(InspectorObject::create(), TimelineRecordType::ScrollLayer, false, frame);
343}
344
345void InspectorTimelineAgent::didScroll()
346{
347    didCompleteCurrentRecord(TimelineRecordType::ScrollLayer);
348}
349
350void InspectorTimelineAgent::willWriteHTML(unsigned startLine, Frame* frame)
351{
352    pushCurrentRecord(TimelineRecordFactory::createParseHTMLData(startLine), TimelineRecordType::ParseHTML, true, frame);
353}
354
355void InspectorTimelineAgent::didWriteHTML(unsigned endLine)
356{
357    if (!m_recordStack.isEmpty()) {
358        const TimelineRecordEntry& entry = m_recordStack.last();
359        entry.data->setNumber("endLine", endLine);
360        didCompleteCurrentRecord(TimelineRecordType::ParseHTML);
361    }
362}
363
364void InspectorTimelineAgent::didInstallTimer(int timerId, int timeout, bool singleShot, Frame* frame)
365{
366    appendRecord(TimelineRecordFactory::createTimerInstallData(timerId, timeout, singleShot), TimelineRecordType::TimerInstall, true, frame);
367}
368
369void InspectorTimelineAgent::didRemoveTimer(int timerId, Frame* frame)
370{
371    appendRecord(TimelineRecordFactory::createGenericTimerData(timerId), TimelineRecordType::TimerRemove, true, frame);
372}
373
374void InspectorTimelineAgent::willFireTimer(int timerId, Frame* frame)
375{
376    pushCurrentRecord(TimelineRecordFactory::createGenericTimerData(timerId), TimelineRecordType::TimerFire, false, frame);
377}
378
379void InspectorTimelineAgent::didFireTimer()
380{
381    didCompleteCurrentRecord(TimelineRecordType::TimerFire);
382}
383
384void InspectorTimelineAgent::willDispatchXHRReadyStateChangeEvent(const String& url, int readyState, Frame* frame)
385{
386    pushCurrentRecord(TimelineRecordFactory::createXHRReadyStateChangeData(url, readyState), TimelineRecordType::XHRReadyStateChange, false, frame);
387}
388
389void InspectorTimelineAgent::didDispatchXHRReadyStateChangeEvent()
390{
391    didCompleteCurrentRecord(TimelineRecordType::XHRReadyStateChange);
392}
393
394void InspectorTimelineAgent::willDispatchXHRLoadEvent(const String& url, Frame* frame)
395{
396    pushCurrentRecord(TimelineRecordFactory::createXHRLoadData(url), TimelineRecordType::XHRLoad, true, frame);
397}
398
399void InspectorTimelineAgent::didDispatchXHRLoadEvent()
400{
401    didCompleteCurrentRecord(TimelineRecordType::XHRLoad);
402}
403
404void InspectorTimelineAgent::willEvaluateScript(const String& url, int lineNumber, Frame* frame)
405{
406    pushCurrentRecord(TimelineRecordFactory::createEvaluateScriptData(url, lineNumber), TimelineRecordType::EvaluateScript, true, frame);
407
408    if (frame && !m_recordingProfileDepth) {
409        ++m_recordingProfileDepth;
410        startProfiling(frame, ASCIILiteral("Timeline EvaluateScript"));
411    }
412}
413
414void InspectorTimelineAgent::didEvaluateScript(Frame* frame)
415{
416    if (frame && m_recordingProfileDepth) {
417        --m_recordingProfileDepth;
418        ASSERT(m_recordingProfileDepth >= 0);
419
420        if (!m_recordingProfileDepth) {
421            if (m_recordStack.isEmpty())
422                return;
423
424            TimelineRecordEntry& entry = m_recordStack.last();
425            ASSERT(entry.type == TimelineRecordType::EvaluateScript);
426
427            RefPtr<JSC::Profile> profile = stopProfiling(frame, ASCIILiteral("Timeline EvaluateScript"));
428            if (profile)
429                TimelineRecordFactory::appendProfile(entry.data.get(), profile.release());
430        }
431    }
432
433    didCompleteCurrentRecord(TimelineRecordType::EvaluateScript);
434}
435
436void InspectorTimelineAgent::didScheduleResourceRequest(const String& url, Frame* frame)
437{
438    appendRecord(TimelineRecordFactory::createScheduleResourceRequestData(url), TimelineRecordType::ScheduleResourceRequest, true, frame);
439}
440
441void InspectorTimelineAgent::willSendResourceRequest(unsigned long identifier, const ResourceRequest& request, Frame* frame)
442{
443    String requestId = IdentifiersFactory::requestId(identifier);
444    appendRecord(TimelineRecordFactory::createResourceSendRequestData(requestId, request), TimelineRecordType::ResourceSendRequest, true, frame);
445}
446
447void InspectorTimelineAgent::willReceiveResourceData(unsigned long identifier, Frame* frame, int length)
448{
449    String requestId = IdentifiersFactory::requestId(identifier);
450    pushCurrentRecord(TimelineRecordFactory::createReceiveResourceData(requestId, length), TimelineRecordType::ResourceReceivedData, false, frame);
451}
452
453void InspectorTimelineAgent::didReceiveResourceData()
454{
455    didCompleteCurrentRecord(TimelineRecordType::ResourceReceivedData);
456}
457
458void InspectorTimelineAgent::willReceiveResourceResponse(unsigned long identifier, const ResourceResponse& response, Frame* frame)
459{
460    String requestId = IdentifiersFactory::requestId(identifier);
461    pushCurrentRecord(TimelineRecordFactory::createResourceReceiveResponseData(requestId, response), TimelineRecordType::ResourceReceiveResponse, false, frame);
462}
463
464void InspectorTimelineAgent::didReceiveResourceResponse()
465{
466    didCompleteCurrentRecord(TimelineRecordType::ResourceReceiveResponse);
467}
468
469void InspectorTimelineAgent::didFinishLoadingResource(unsigned long identifier, bool didFail, double finishTime, Frame* frame)
470{
471    appendRecord(TimelineRecordFactory::createResourceFinishData(IdentifiersFactory::requestId(identifier), didFail, finishTime * 1000), TimelineRecordType::ResourceFinish, false, frame);
472}
473
474void InspectorTimelineAgent::didTimeStamp(Frame* frame, const String& message)
475{
476    appendRecord(TimelineRecordFactory::createTimeStampData(message), TimelineRecordType::TimeStamp, true, frame);
477}
478
479void InspectorTimelineAgent::time(Frame* frame, const String& message)
480{
481    appendRecord(TimelineRecordFactory::createTimeStampData(message), TimelineRecordType::Time, true, frame);
482}
483
484void InspectorTimelineAgent::timeEnd(Frame* frame, const String& message)
485{
486    appendRecord(TimelineRecordFactory::createTimeStampData(message), TimelineRecordType::TimeEnd, true, frame);
487}
488
489void InspectorTimelineAgent::didMarkDOMContentEvent(Frame* frame)
490{
491    bool isMainFrame = frame && m_pageAgent && (frame == m_pageAgent->mainFrame());
492    appendRecord(TimelineRecordFactory::createMarkData(isMainFrame), TimelineRecordType::MarkDOMContent, false, frame);
493}
494
495void InspectorTimelineAgent::didMarkLoadEvent(Frame* frame)
496{
497    bool isMainFrame = frame && m_pageAgent && (frame == m_pageAgent->mainFrame());
498    appendRecord(TimelineRecordFactory::createMarkData(isMainFrame), TimelineRecordType::MarkLoad, false, frame);
499}
500
501void InspectorTimelineAgent::didCommitLoad()
502{
503    clearRecordStack();
504}
505
506void InspectorTimelineAgent::didRequestAnimationFrame(int callbackId, Frame* frame)
507{
508    appendRecord(TimelineRecordFactory::createAnimationFrameData(callbackId), TimelineRecordType::RequestAnimationFrame, true, frame);
509}
510
511void InspectorTimelineAgent::didCancelAnimationFrame(int callbackId, Frame* frame)
512{
513    appendRecord(TimelineRecordFactory::createAnimationFrameData(callbackId), TimelineRecordType::CancelAnimationFrame, true, frame);
514}
515
516void InspectorTimelineAgent::willFireAnimationFrame(int callbackId, Frame* frame)
517{
518    pushCurrentRecord(TimelineRecordFactory::createAnimationFrameData(callbackId), TimelineRecordType::FireAnimationFrame, false, frame);
519}
520
521void InspectorTimelineAgent::didFireAnimationFrame()
522{
523    didCompleteCurrentRecord(TimelineRecordType::FireAnimationFrame);
524}
525
526#if ENABLE(WEB_SOCKETS)
527void InspectorTimelineAgent::didCreateWebSocket(unsigned long identifier, const URL& url, const String& protocol, Frame* frame)
528{
529    appendRecord(TimelineRecordFactory::createWebSocketCreateData(identifier, url, protocol), TimelineRecordType::WebSocketCreate, true, frame);
530}
531
532void InspectorTimelineAgent::willSendWebSocketHandshakeRequest(unsigned long identifier, Frame* frame)
533{
534    appendRecord(TimelineRecordFactory::createGenericWebSocketData(identifier), TimelineRecordType::WebSocketSendHandshakeRequest, true, frame);
535}
536
537void InspectorTimelineAgent::didReceiveWebSocketHandshakeResponse(unsigned long identifier, Frame* frame)
538{
539    appendRecord(TimelineRecordFactory::createGenericWebSocketData(identifier), TimelineRecordType::WebSocketReceiveHandshakeResponse, false, frame);
540}
541
542void InspectorTimelineAgent::didDestroyWebSocket(unsigned long identifier, Frame* frame)
543{
544    appendRecord(TimelineRecordFactory::createGenericWebSocketData(identifier), TimelineRecordType::WebSocketDestroy, true, frame);
545}
546#endif // ENABLE(WEB_SOCKETS)
547
548// ScriptDebugListener
549
550void InspectorTimelineAgent::breakpointActionProbe(JSC::ExecState* exec, const Inspector::ScriptBreakpointAction& action, int hitCount, const Deprecated::ScriptValue&)
551{
552    ASSERT(exec);
553
554    appendRecord(TimelineRecordFactory::createProbeSampleData(action, hitCount), TimelineRecordType::ProbeSample, false, frameFromExecState(exec));
555}
556
557static Inspector::TypeBuilder::Timeline::EventType::Enum toProtocol(TimelineRecordType type)
558{
559    switch (type) {
560    case TimelineRecordType::EventDispatch:
561        return Inspector::TypeBuilder::Timeline::EventType::EventDispatch;
562    case TimelineRecordType::ScheduleStyleRecalculation:
563        return Inspector::TypeBuilder::Timeline::EventType::ScheduleStyleRecalculation;
564    case TimelineRecordType::RecalculateStyles:
565        return Inspector::TypeBuilder::Timeline::EventType::RecalculateStyles;
566    case TimelineRecordType::InvalidateLayout:
567        return Inspector::TypeBuilder::Timeline::EventType::InvalidateLayout;
568    case TimelineRecordType::Layout:
569        return Inspector::TypeBuilder::Timeline::EventType::Layout;
570    case TimelineRecordType::Paint:
571        return Inspector::TypeBuilder::Timeline::EventType::Paint;
572    case TimelineRecordType::ScrollLayer:
573        return Inspector::TypeBuilder::Timeline::EventType::ScrollLayer;
574    case TimelineRecordType::ResizeImage:
575        return Inspector::TypeBuilder::Timeline::EventType::ResizeImage;
576
577    case TimelineRecordType::ParseHTML:
578        return Inspector::TypeBuilder::Timeline::EventType::ParseHTML;
579
580    case TimelineRecordType::TimerInstall:
581        return Inspector::TypeBuilder::Timeline::EventType::TimerInstall;
582    case TimelineRecordType::TimerRemove:
583        return Inspector::TypeBuilder::Timeline::EventType::TimerRemove;
584    case TimelineRecordType::TimerFire:
585        return Inspector::TypeBuilder::Timeline::EventType::TimerFire;
586
587    case TimelineRecordType::EvaluateScript:
588        return Inspector::TypeBuilder::Timeline::EventType::EvaluateScript;
589
590    case TimelineRecordType::MarkLoad:
591        return Inspector::TypeBuilder::Timeline::EventType::MarkLoad;
592    case TimelineRecordType::MarkDOMContent:
593        return Inspector::TypeBuilder::Timeline::EventType::MarkDOMContent;
594
595    case TimelineRecordType::TimeStamp:
596        return Inspector::TypeBuilder::Timeline::EventType::TimeStamp;
597    case TimelineRecordType::Time:
598        return Inspector::TypeBuilder::Timeline::EventType::Time;
599    case TimelineRecordType::TimeEnd:
600        return Inspector::TypeBuilder::Timeline::EventType::TimeEnd;
601
602    case TimelineRecordType::ScheduleResourceRequest:
603        return Inspector::TypeBuilder::Timeline::EventType::ScheduleResourceRequest;
604    case TimelineRecordType::ResourceSendRequest:
605        return Inspector::TypeBuilder::Timeline::EventType::ResourceSendRequest;
606    case TimelineRecordType::ResourceReceiveResponse:
607        return Inspector::TypeBuilder::Timeline::EventType::ResourceReceiveResponse;
608    case TimelineRecordType::ResourceReceivedData:
609        return Inspector::TypeBuilder::Timeline::EventType::ResourceReceivedData;
610    case TimelineRecordType::ResourceFinish:
611        return Inspector::TypeBuilder::Timeline::EventType::ResourceFinish;
612
613    case TimelineRecordType::XHRReadyStateChange:
614        return Inspector::TypeBuilder::Timeline::EventType::XHRReadyStateChange;
615    case TimelineRecordType::XHRLoad:
616        return Inspector::TypeBuilder::Timeline::EventType::XHRLoad;
617
618    case TimelineRecordType::FunctionCall:
619        return Inspector::TypeBuilder::Timeline::EventType::FunctionCall;
620    case TimelineRecordType::ProbeSample:
621        return Inspector::TypeBuilder::Timeline::EventType::ProbeSample;
622    case TimelineRecordType::ConsoleProfile:
623        return Inspector::TypeBuilder::Timeline::EventType::ConsoleProfile;
624
625    case TimelineRecordType::RequestAnimationFrame:
626        return Inspector::TypeBuilder::Timeline::EventType::RequestAnimationFrame;
627    case TimelineRecordType::CancelAnimationFrame:
628        return Inspector::TypeBuilder::Timeline::EventType::CancelAnimationFrame;
629    case TimelineRecordType::FireAnimationFrame:
630        return Inspector::TypeBuilder::Timeline::EventType::FireAnimationFrame;
631
632    case TimelineRecordType::WebSocketCreate:
633        return Inspector::TypeBuilder::Timeline::EventType::WebSocketCreate;
634    case TimelineRecordType::WebSocketSendHandshakeRequest:
635        return Inspector::TypeBuilder::Timeline::EventType::WebSocketSendHandshakeRequest;
636    case TimelineRecordType::WebSocketReceiveHandshakeResponse:
637        return Inspector::TypeBuilder::Timeline::EventType::WebSocketReceiveHandshakeResponse;
638    case TimelineRecordType::WebSocketDestroy:
639        return Inspector::TypeBuilder::Timeline::EventType::WebSocketDestroy;
640    }
641
642    return Inspector::TypeBuilder::Timeline::EventType::TimeStamp;
643}
644
645void InspectorTimelineAgent::addRecordToTimeline(PassRefPtr<InspectorObject> prpRecord, TimelineRecordType type)
646{
647    prpRecord->setString("type", Inspector::TypeBuilder::getWebEnumConstantValue(toProtocol(type)));
648
649    RefPtr<Inspector::TypeBuilder::Timeline::TimelineEvent> record = Inspector::TypeBuilder::Timeline::TimelineEvent::runtimeCast(prpRecord);
650
651    if (m_recordStack.isEmpty())
652        sendEvent(record.release());
653    else {
654        const TimelineRecordEntry& parent = m_recordStack.last();
655        parent.children->pushObject(record.release());
656    }
657}
658
659void InspectorTimelineAgent::setFrameIdentifier(InspectorObject* record, Frame* frame)
660{
661    if (!frame || !m_pageAgent)
662        return;
663    String frameId;
664    if (frame && m_pageAgent)
665        frameId = m_pageAgent->frameId(frame);
666    record->setString("frameId", frameId);
667}
668
669void InspectorTimelineAgent::didCompleteRecordEntry(const TimelineRecordEntry& entry)
670{
671    entry.record->setObject(ASCIILiteral("data"), entry.data);
672    entry.record->setArray(ASCIILiteral("children"), entry.children);
673    entry.record->setNumber(ASCIILiteral("endTime"), timestamp());
674    addRecordToTimeline(entry.record, entry.type);
675}
676
677void InspectorTimelineAgent::didCompleteCurrentRecord(TimelineRecordType type)
678{
679    // An empty stack could merely mean that the timeline agent was turned on in the middle of
680    // an event.  Don't treat as an error.
681    if (!m_recordStack.isEmpty()) {
682        TimelineRecordEntry entry = m_recordStack.last();
683        m_recordStack.removeLast();
684        ASSERT_UNUSED(type, entry.type == type);
685        didCompleteRecordEntry(entry);
686    }
687}
688
689InspectorTimelineAgent::InspectorTimelineAgent(InstrumentingAgents* instrumentingAgents, InspectorPageAgent* pageAgent, InspectorType type, InspectorClient* client)
690    : InspectorAgentBase(ASCIILiteral("Timeline"), instrumentingAgents)
691    , m_pageAgent(pageAgent)
692    , m_scriptDebugServer(nullptr)
693    , m_id(1)
694    , m_maxCallStackDepth(5)
695    , m_inspectorType(type)
696    , m_client(client)
697    , m_recordingProfileDepth(0)
698    , m_enabled(false)
699    , m_enabledFromFrontend(false)
700{
701}
702
703void InspectorTimelineAgent::appendRecord(PassRefPtr<InspectorObject> data, TimelineRecordType type, bool captureCallStack, Frame* frame)
704{
705    RefPtr<InspectorObject> record = TimelineRecordFactory::createGenericRecord(timestamp(), captureCallStack ? m_maxCallStackDepth : 0);
706    record->setObject("data", data);
707    setFrameIdentifier(record.get(), frame);
708    addRecordToTimeline(record.release(), type);
709}
710
711void InspectorTimelineAgent::sendEvent(PassRefPtr<InspectorObject> event)
712{
713    if (!m_frontendDispatcher)
714        return;
715
716    // FIXME: runtimeCast is a hack. We do it because we can't build TimelineEvent directly now.
717    RefPtr<Inspector::TypeBuilder::Timeline::TimelineEvent> recordChecked = Inspector::TypeBuilder::Timeline::TimelineEvent::runtimeCast(event);
718    m_frontendDispatcher->eventRecorded(recordChecked.release());
719}
720
721InspectorTimelineAgent::TimelineRecordEntry InspectorTimelineAgent::createRecordEntry(PassRefPtr<InspectorObject> data, TimelineRecordType type, bool captureCallStack, Frame* frame)
722{
723    RefPtr<InspectorObject> record = TimelineRecordFactory::createGenericRecord(timestamp(), captureCallStack ? m_maxCallStackDepth : 0);
724    setFrameIdentifier(record.get(), frame);
725    return TimelineRecordEntry(record.release(), data, InspectorArray::create(), type);
726}
727
728void InspectorTimelineAgent::pushCurrentRecord(PassRefPtr<InspectorObject> data, TimelineRecordType type, bool captureCallStack, Frame* frame)
729{
730    pushCurrentRecord(createRecordEntry(data, type, captureCallStack, frame));
731}
732
733void InspectorTimelineAgent::clearRecordStack()
734{
735    m_recordStack.clear();
736    m_id++;
737}
738
739void InspectorTimelineAgent::localToPageQuad(const RenderObject& renderer, const LayoutRect& rect, FloatQuad* quad)
740{
741    const FrameView& frameView = renderer.view().frameView();
742    FloatQuad absolute = renderer.localToAbsoluteQuad(FloatQuad(rect));
743    quad->setP1(frameView.contentsToRootView(roundedIntPoint(absolute.p1())));
744    quad->setP2(frameView.contentsToRootView(roundedIntPoint(absolute.p2())));
745    quad->setP3(frameView.contentsToRootView(roundedIntPoint(absolute.p3())));
746    quad->setP4(frameView.contentsToRootView(roundedIntPoint(absolute.p4())));
747}
748
749double InspectorTimelineAgent::timestamp()
750{
751    return m_timeConverter.fromMonotonicallyIncreasingTime(monotonicallyIncreasingTime());
752}
753
754Page* InspectorTimelineAgent::page()
755{
756    return m_pageAgent ? m_pageAgent->page() : nullptr;
757}
758
759} // namespace WebCore
760
761#endif // ENABLE(INSPECTOR)
762