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