Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / timeline / TracingTimelineModel.js
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 /**
6  * @constructor
7  * @param {!WebInspector.TracingManager} tracingManager
8  * @param {!WebInspector.TracingModel} tracingModel
9  * @param {!WebInspector.TimelineModel.Filter} recordFilter
10  * @extends {WebInspector.TimelineModel}
11  */
12 WebInspector.TracingTimelineModel = function(tracingManager, tracingModel, recordFilter)
13 {
14     WebInspector.TimelineModel.call(this);
15
16     this._tracingManager = tracingManager;
17     this._tracingModel = tracingModel;
18     this._recordFilter = recordFilter;
19     this._tracingManager.addEventListener(WebInspector.TracingManager.Events.TracingStarted, this._onTracingStarted, this);
20     this._tracingManager.addEventListener(WebInspector.TracingManager.Events.EventsCollected, this._onEventsCollected, this);
21     this._tracingManager.addEventListener(WebInspector.TracingManager.Events.TracingComplete, this._onTracingComplete, this);
22     this.reset();
23 }
24
25 WebInspector.TracingTimelineModel.RecordType = {
26     Program: "Program",
27     EventDispatch: "EventDispatch",
28
29     GPUTask: "GPUTask",
30
31     RequestMainThreadFrame: "RequestMainThreadFrame",
32     BeginFrame: "BeginFrame",
33     BeginMainThreadFrame: "BeginMainThreadFrame",
34     ActivateLayerTree: "ActivateLayerTree",
35     DrawFrame: "DrawFrame",
36     ScheduleStyleRecalculation: "ScheduleStyleRecalculation",
37     RecalculateStyles: "RecalculateStyles",
38     InvalidateLayout: "InvalidateLayout",
39     Layout: "Layout",
40     UpdateLayer: "UpdateLayer",
41     UpdateLayerTree: "UpdateLayerTree",
42     PaintSetup: "PaintSetup",
43     Paint: "Paint",
44     PaintImage: "PaintImage",
45     Rasterize: "Rasterize",
46     RasterTask: "RasterTask",
47     ScrollLayer: "ScrollLayer",
48     CompositeLayers: "CompositeLayers",
49
50     StyleRecalcInvalidationTracking: "StyleRecalcInvalidationTracking",
51     LayoutInvalidationTracking: "LayoutInvalidationTracking",
52     LayerInvalidationTracking: "LayerInvalidationTracking",
53     PaintInvalidationTracking: "PaintInvalidationTracking",
54
55     ParseHTML: "ParseHTML",
56
57     TimerInstall: "TimerInstall",
58     TimerRemove: "TimerRemove",
59     TimerFire: "TimerFire",
60
61     XHRReadyStateChange: "XHRReadyStateChange",
62     XHRLoad: "XHRLoad",
63     EvaluateScript: "EvaluateScript",
64
65     MarkLoad: "MarkLoad",
66     MarkDOMContent: "MarkDOMContent",
67     MarkFirstPaint: "MarkFirstPaint",
68
69     TimeStamp: "TimeStamp",
70     ConsoleTime: "ConsoleTime",
71
72     ResourceSendRequest: "ResourceSendRequest",
73     ResourceReceiveResponse: "ResourceReceiveResponse",
74     ResourceReceivedData: "ResourceReceivedData",
75     ResourceFinish: "ResourceFinish",
76
77     FunctionCall: "FunctionCall",
78     GCEvent: "GCEvent",
79     JSFrame: "JSFrame",
80     JSSample: "JSSample",
81
82     UpdateCounters: "UpdateCounters",
83
84     RequestAnimationFrame: "RequestAnimationFrame",
85     CancelAnimationFrame: "CancelAnimationFrame",
86     FireAnimationFrame: "FireAnimationFrame",
87
88     WebSocketCreate : "WebSocketCreate",
89     WebSocketSendHandshakeRequest : "WebSocketSendHandshakeRequest",
90     WebSocketReceiveHandshakeResponse : "WebSocketReceiveHandshakeResponse",
91     WebSocketDestroy : "WebSocketDestroy",
92
93     EmbedderCallback : "EmbedderCallback",
94
95     CallStack: "CallStack",
96     SetLayerTreeId: "SetLayerTreeId",
97     TracingStartedInPage: "TracingStartedInPage",
98     TracingSessionIdForWorker: "TracingSessionIdForWorker",
99
100     DecodeImage: "Decode Image",
101     ResizeImage: "Resize Image",
102     DrawLazyPixelRef: "Draw LazyPixelRef",
103     DecodeLazyPixelRef: "Decode LazyPixelRef",
104
105     LazyPixelRef: "LazyPixelRef",
106     LayerTreeHostImplSnapshot: "cc::LayerTreeHostImpl",
107     PictureSnapshot: "cc::Picture",
108
109     // CpuProfile is a virtual event created on frontend to support
110     // serialization of CPU Profiles within tracing timeline data.
111     CpuProfile: "CpuProfile"
112 };
113
114 /**
115  * @constructor
116  * @param {string} name
117  */
118 WebInspector.TracingTimelineModel.VirtualThread = function(name)
119 {
120     this.name = name;
121     /** @type {!Array.<!WebInspector.TracingModel.Event>} */
122     this.events = [];
123     /** @type {!Array.<!Array.<!WebInspector.TracingModel.Event>>} */
124     this.asyncEvents = [];
125 }
126
127 WebInspector.TracingTimelineModel.prototype = {
128     /**
129      * @param {boolean} captureCauses
130      * @param {boolean} enableJSSampling
131      * @param {boolean} captureMemory
132      * @param {boolean} capturePictures
133      */
134     startRecording: function(captureCauses, enableJSSampling, captureMemory, capturePictures)
135     {
136         function disabledByDefault(category)
137         {
138             return "disabled-by-default-" + category;
139         }
140         var categoriesArray = [
141             "-*",
142             disabledByDefault("devtools.timeline"),
143             disabledByDefault("devtools.timeline.frame"),
144             WebInspector.TracingModel.ConsoleEventCategory
145         ];
146         if (captureCauses || enableJSSampling)
147             categoriesArray.push(disabledByDefault("devtools.timeline.stack"));
148         if (enableJSSampling) {
149             this._jsProfilerStarted = true;
150             this._currentTarget = WebInspector.context.flavor(WebInspector.Target);
151             this._configureCpuProfilerSamplingInterval();
152             this._currentTarget.profilerAgent().start();
153         }
154         if (captureCauses && Runtime.experiments.isEnabled("timelineInvalidationTracking"))
155             categoriesArray.push(disabledByDefault("devtools.timeline.invalidationTracking"));
156         if (capturePictures) {
157             categoriesArray = categoriesArray.concat([
158                 disabledByDefault("devtools.timeline.layers"),
159                 disabledByDefault("devtools.timeline.picture"),
160                 disabledByDefault("blink.graphics_context_annotations")]);
161         }
162         var categories = categoriesArray.join(",");
163         this._startRecordingWithCategories(categories);
164     },
165
166     stopRecording: function()
167     {
168         if (this._jsProfilerStarted) {
169             this._stopCallbackBarrier = new CallbackBarrier();
170             this._currentTarget.profilerAgent().stop(this._stopCallbackBarrier.createCallback(this._didStopRecordingJSSamples.bind(this)));
171             this._jsProfilerStarted = false;
172         }
173         this._tracingManager.stop();
174     },
175
176     /**
177      * @param {!Array.<!WebInspector.TracingManager.EventPayload>} events
178      */
179     setEventsForTest: function(events)
180     {
181         this._startCollectingTraceEvents(false);
182         this._tracingModel.addEvents(events);
183         this._onTracingComplete();
184     },
185
186     _configureCpuProfilerSamplingInterval: function()
187     {
188         var intervalUs = WebInspector.settings.highResolutionCpuProfiling.get() ? 100 : 1000;
189         this._currentTarget.profilerAgent().setSamplingInterval(intervalUs, didChangeInterval);
190
191         function didChangeInterval(error)
192         {
193             if (error)
194                 WebInspector.console.error(error);
195         }
196     },
197
198     /**
199      * @param {string} categories
200      */
201     _startRecordingWithCategories: function(categories)
202     {
203         this._tracingManager.start(categories, "");
204     },
205
206     _onTracingStarted: function()
207     {
208         this._startCollectingTraceEvents(false);
209     },
210
211     /**
212      * @param {boolean} fromFile
213      */
214     _startCollectingTraceEvents: function(fromFile)
215     {
216         this.reset();
217         this._tracingModel.reset();
218         this.dispatchEventToListeners(WebInspector.TimelineModel.Events.RecordingStarted, { fromFile: fromFile });
219     },
220
221     /**
222      * @param {!WebInspector.Event} event
223      */
224     _onEventsCollected: function(event)
225     {
226         var traceEvents = /** @type {!Array.<!WebInspector.TracingManager.EventPayload>} */ (event.data);
227         this._tracingModel.addEvents(traceEvents);
228     },
229
230     _onTracingComplete: function()
231     {
232         if (this._stopCallbackBarrier)
233             this._stopCallbackBarrier.callWhenDone(this._didStopRecordingTraceEvents.bind(this));
234         else
235             this._didStopRecordingTraceEvents();
236     },
237
238     /**
239      * @param {?Protocol.Error} error
240      * @param {?ProfilerAgent.CPUProfile} cpuProfile
241      */
242     _didStopRecordingJSSamples: function(error, cpuProfile)
243     {
244         if (error)
245             WebInspector.console.error(error);
246         this._recordedCpuProfile = cpuProfile;
247     },
248
249     _didStopRecordingTraceEvents: function()
250     {
251         this._stopCallbackBarrier = null;
252
253         if (this._recordedCpuProfile) {
254             this._injectCpuProfileEvent(this._recordedCpuProfile);
255             this._recordedCpuProfile = null;
256         }
257         this._tracingModel.tracingComplete();
258
259         var events = this._tracingModel.devtoolsPageMetadataEvents();
260         var workerMetadataEvents = this._tracingModel.devtoolsWorkerMetadataEvents();
261
262         this._resetProcessingState();
263         for (var i = 0, length = events.length; i < length; i++) {
264             var event = events[i];
265             var process = event.thread.process();
266             var startTime = event.startTime;
267
268             var endTime = Infinity;
269             if (i + 1 < length)
270                 endTime = events[i + 1].startTime;
271
272             var threads = process.sortedThreads();
273             for (var j = 0; j < threads.length; j++) {
274                 var thread = threads[j];
275                 if (thread.name() === "WebCore: Worker" && !workerMetadataEvents.some(function(e) { return e.args["data"]["workerThreadId"] === thread.id(); }))
276                     continue;
277                 this._processThreadEvents(startTime, endTime, event.thread, thread);
278             }
279         }
280         this._resetProcessingState();
281
282         this._inspectedTargetEvents.sort(WebInspector.TracingModel.Event.compareStartTime);
283
284         if (this._cpuProfile) {
285             this._processCpuProfile(this._cpuProfile);
286             this._cpuProfile = null;
287         }
288         this._buildTimelineRecords();
289         this.dispatchEventToListeners(WebInspector.TimelineModel.Events.RecordingStopped);
290     },
291
292     /**
293      * @param {!ProfilerAgent.CPUProfile} cpuProfile
294      */
295     _injectCpuProfileEvent: function(cpuProfile)
296     {
297         var metaEvent = this._tracingModel.devtoolsPageMetadataEvents().peekLast();
298         if (!metaEvent)
299             return;
300         var cpuProfileEvent = /** @type {!WebInspector.TracingManager.EventPayload} */ ({
301             cat: WebInspector.TracingModel.DevToolsMetadataEventCategory,
302             ph: WebInspector.TracingModel.Phase.Instant,
303             ts: this._tracingModel.maximumRecordTime() * 1000,
304             pid: metaEvent.thread.process().id(),
305             tid: metaEvent.thread.id(),
306             name: WebInspector.TracingTimelineModel.RecordType.CpuProfile,
307             args: { data: { cpuProfile: cpuProfile } }
308         });
309         this._tracingModel.addEvents([cpuProfileEvent]);
310     },
311
312     /**
313      * @return {boolean}
314      */
315     containsJSSamples: function()
316     {
317         return this._containsJSSamples;
318     },
319
320     /**
321      * @param {!ProfilerAgent.CPUProfile} cpuProfile
322      */
323     _processCpuProfile: function(cpuProfile)
324     {
325         this._containsJSSamples = true;
326         var jsSamples = WebInspector.TimelineJSProfileProcessor.generateTracingEventsFromCpuProfile(this, cpuProfile);
327         this._inspectedTargetEvents = this._inspectedTargetEvents.mergeOrdered(jsSamples, WebInspector.TracingModel.Event.orderedCompareStartTime);
328         this._setMainThreadEvents(this.mainThreadEvents().mergeOrdered(jsSamples, WebInspector.TracingModel.Event.orderedCompareStartTime));
329     },
330
331     /**
332      * @return {number}
333      */
334     minimumRecordTime: function()
335     {
336         return this._tracingModel.minimumRecordTime();
337     },
338
339     /**
340      * @return {number}
341      */
342     maximumRecordTime: function()
343     {
344         return this._tracingModel.maximumRecordTime();
345     },
346
347     /**
348      * @return {!Array.<!WebInspector.TracingModel.Event>}
349      */
350     inspectedTargetEvents: function()
351     {
352         return this._inspectedTargetEvents;
353     },
354
355     /**
356      * @return {!Array.<!WebInspector.TracingModel.Event>}
357      */
358     mainThreadEvents: function()
359     {
360         return this._mainThreadEvents;
361     },
362
363     /**
364      * @param {!Array.<!WebInspector.TracingModel.Event>} events
365      */
366     _setMainThreadEvents: function(events)
367     {
368         this._mainThreadEvents = events;
369     },
370
371     /**
372      * @return {!Array.<!Array.<!WebInspector.TracingModel.Event>>}
373      */
374     mainThreadAsyncEvents: function()
375     {
376         return this._mainThreadAsyncEvents;
377     },
378
379     /**
380      * @return {!Array.<!WebInspector.TracingTimelineModel.VirtualThread>}
381      */
382     virtualThreads: function()
383     {
384         return this._virtualThreads;
385     },
386
387     /**
388      * @param {!WebInspector.ChunkedFileReader} fileReader
389      * @param {!WebInspector.Progress} progress
390      * @return {!WebInspector.OutputStream}
391      */
392     createLoader: function(fileReader, progress)
393     {
394         return new WebInspector.TracingModelLoader(this, fileReader, progress);
395     },
396
397     /**
398      * @param {!WebInspector.OutputStream} stream
399      */
400     writeToStream: function(stream)
401     {
402         var saver = new WebInspector.TracingTimelineSaver(stream);
403         this._tracingModel.writeToStream(stream, saver);
404     },
405
406     reset: function()
407     {
408         this._virtualThreads = [];
409         this._mainThreadEvents = [];
410         this._mainThreadAsyncEvents = [];
411         this._inspectedTargetEvents = [];
412         this._containsJSSamples = false;
413         WebInspector.TimelineModel.prototype.reset.call(this);
414     },
415
416     _buildTimelineRecords: function()
417     {
418         var topLevelRecords = this._buildTimelineRecordsForThread(this.mainThreadEvents());
419
420         /**
421          * @param {!WebInspector.TracingTimelineModel.TraceEventRecord} a
422          * @param {!WebInspector.TracingTimelineModel.TraceEventRecord} b
423          * @return {number}
424          */
425         function compareRecordStartTime(a, b)
426         {
427             // Never return 0 as otherwise equal records would be merged.
428             return (a.startTime() <= b.startTime()) ? -1 : +1;
429         }
430
431         /**
432          * @param {!WebInspector.TracingTimelineModel.VirtualThread} virtualThread
433          * @this {!WebInspector.TracingTimelineModel}
434          */
435         function processVirtualThreadEvents(virtualThread)
436         {
437             var threadRecords = this._buildTimelineRecordsForThread(virtualThread.events);
438             topLevelRecords = topLevelRecords.mergeOrdered(threadRecords, compareRecordStartTime);
439         }
440         this.virtualThreads().forEach(processVirtualThreadEvents.bind(this));
441
442         for (var i = 0; i < topLevelRecords.length; i++)
443             this._addTopLevelRecord(topLevelRecords[i]);
444     },
445
446     /**
447      * @param {!Array.<!WebInspector.TracingModel.Event>} threadEvents
448      * @return {!Array.<!WebInspector.TracingTimelineModel.TraceEventRecord>}
449      */
450     _buildTimelineRecordsForThread: function(threadEvents)
451     {
452         var recordStack = [];
453         var topLevelRecords = [];
454
455         for (var i = 0, size = threadEvents.length; i < size; ++i) {
456             var event = threadEvents[i];
457             for (var top = recordStack.peekLast(); top && top._event.endTime <= event.startTime; top = recordStack.peekLast()) {
458                 recordStack.pop();
459                 if (!recordStack.length)
460                     topLevelRecords.push(top);
461             }
462             if (event.phase === WebInspector.TracingModel.Phase.AsyncEnd || event.phase === WebInspector.TracingModel.Phase.NestableAsyncEnd)
463                 continue;
464             var parentRecord = recordStack.peekLast();
465             // Maintain the back-end logic of old timeline, skip console.time() / console.timeEnd() that are not properly nested.
466             if (WebInspector.TracingModel.isAsyncBeginPhase(event.phase) && parentRecord && event.endTime > parentRecord._event.endTime)
467                 continue;
468             var record = new WebInspector.TracingTimelineModel.TraceEventRecord(this, event);
469             if (WebInspector.TracingTimelineUIUtils.isMarkerEvent(event))
470                 this._eventDividerRecords.push(record);
471             if (!this._recordFilter.accept(record))
472                 continue;
473             if (parentRecord)
474                 parentRecord._addChild(record);
475             if (event.endTime)
476                 recordStack.push(record);
477         }
478
479         if (recordStack.length)
480             topLevelRecords.push(recordStack[0]);
481
482         return topLevelRecords;
483     },
484
485     /**
486      * @param {!WebInspector.TracingTimelineModel.TraceEventRecord} record
487      */
488     _addTopLevelRecord: function(record)
489     {
490         this._records.push(record);
491         if (record.type() === WebInspector.TracingTimelineModel.RecordType.Program)
492             this._mainThreadTasks.push(record);
493         if (record.type() === WebInspector.TracingTimelineModel.RecordType.GPUTask)
494             this._gpuThreadTasks.push(record);
495         this.dispatchEventToListeners(WebInspector.TimelineModel.Events.RecordAdded, record);
496     },
497
498     _resetProcessingState: function()
499     {
500         this._sendRequestEvents = {};
501         this._timerEvents = {};
502         this._requestAnimationFrameEvents = {};
503         this._invalidationTracker = new WebInspector.InvalidationTracker();
504         this._layoutInvalidate = {};
505         this._lastScheduleStyleRecalculation = {};
506         this._webSocketCreateEvents = {};
507         this._paintImageEventByPixelRefId = {};
508         this._lastPaintForLayer = {};
509         this._lastRecalculateStylesEvent = null;
510         this._currentScriptEvent = null;
511         this._eventStack = [];
512     },
513
514     /**
515      * @param {number} startTime
516      * @param {?number} endTime
517      * @param {!WebInspector.TracingModel.Thread} mainThread
518      * @param {!WebInspector.TracingModel.Thread} thread
519      */
520     _processThreadEvents: function(startTime, endTime, mainThread, thread)
521     {
522         var events = thread.events();
523         var length = events.length;
524         var i = events.lowerBound(startTime, function (time, event) { return time - event.startTime });
525
526         var threadEvents;
527         if (thread === mainThread) {
528             threadEvents = this._mainThreadEvents;
529             this._mainThreadAsyncEvents = this._mainThreadAsyncEvents.concat(thread.asyncEvents());
530         } else {
531             var virtualThread = new WebInspector.TracingTimelineModel.VirtualThread(thread.name());
532             threadEvents = virtualThread.events;
533             virtualThread.asyncEvents = virtualThread.asyncEvents.concat(thread.asyncEvents());
534             this._virtualThreads.push(virtualThread);
535         }
536
537         this._eventStack = [];
538         for (; i < length; i++) {
539             var event = events[i];
540             if (endTime && event.startTime >= endTime)
541                 break;
542             this._processEvent(event);
543             threadEvents.push(event);
544             this._inspectedTargetEvents.push(event);
545         }
546     },
547
548     /**
549      * @param {!WebInspector.TracingModel.Event} event
550      */
551     _processEvent: function(event)
552     {
553         var recordTypes = WebInspector.TracingTimelineModel.RecordType;
554
555         var eventStack = this._eventStack;
556         while (eventStack.length && eventStack.peekLast().endTime < event.startTime)
557             eventStack.pop();
558         var duration = event.duration;
559         if (duration) {
560             if (eventStack.length) {
561                 var parent = eventStack.peekLast();
562                 parent.selfTime -= duration;
563             }
564             event.selfTime = duration;
565             eventStack.push(event);
566         }
567
568         if (this._currentScriptEvent && event.startTime > this._currentScriptEvent.endTime)
569             this._currentScriptEvent = null;
570
571         switch (event.name) {
572         case recordTypes.CallStack:
573             var lastMainThreadEvent = this.mainThreadEvents().peekLast();
574             if (lastMainThreadEvent && event.args["stack"] && event.args["stack"].length)
575                 lastMainThreadEvent.stackTrace = event.args["stack"];
576             break;
577
578         case recordTypes.CpuProfile:
579             this._cpuProfile = event.args["data"]["cpuProfile"];
580             break;
581
582         case recordTypes.ResourceSendRequest:
583             this._sendRequestEvents[event.args["data"]["requestId"]] = event;
584             event.imageURL = event.args["data"]["url"];
585             break;
586
587         case recordTypes.ResourceReceiveResponse:
588         case recordTypes.ResourceReceivedData:
589         case recordTypes.ResourceFinish:
590             event.initiator = this._sendRequestEvents[event.args["data"]["requestId"]];
591             if (event.initiator)
592                 event.imageURL = event.initiator.imageURL;
593             break;
594
595         case recordTypes.TimerInstall:
596             this._timerEvents[event.args["data"]["timerId"]] = event;
597             break;
598
599         case recordTypes.TimerFire:
600             event.initiator = this._timerEvents[event.args["data"]["timerId"]];
601             break;
602
603         case recordTypes.RequestAnimationFrame:
604             this._requestAnimationFrameEvents[event.args["data"]["id"]] = event;
605             break;
606
607         case recordTypes.FireAnimationFrame:
608             event.initiator = this._requestAnimationFrameEvents[event.args["data"]["id"]];
609             break;
610
611         case recordTypes.ScheduleStyleRecalculation:
612             this._lastScheduleStyleRecalculation[event.args["frame"]] = event;
613             break;
614
615         case recordTypes.RecalculateStyles:
616             this._invalidationTracker.didRecalcStyle(event);
617             event.initiator = this._lastScheduleStyleRecalculation[event.args["frame"]];
618             this._lastRecalculateStylesEvent = event;
619             break;
620
621         case recordTypes.StyleRecalcInvalidationTracking:
622         case recordTypes.LayoutInvalidationTracking:
623         case recordTypes.LayerInvalidationTracking:
624         case recordTypes.PaintInvalidationTracking:
625             this._invalidationTracker.addInvalidation(event);
626             break;
627
628         case recordTypes.InvalidateLayout:
629             // Consider style recalculation as a reason for layout invalidation,
630             // but only if we had no earlier layout invalidation records.
631             var layoutInitator = event;
632             var frameId = event.args["frame"];
633             if (!this._layoutInvalidate[frameId] && this._lastRecalculateStylesEvent && this._lastRecalculateStylesEvent.endTime >  event.startTime)
634                 layoutInitator = this._lastRecalculateStylesEvent.initiator;
635             this._layoutInvalidate[frameId] = layoutInitator;
636             break;
637
638         case recordTypes.Layout:
639             this._invalidationTracker.didLayout(event);
640             var frameId = event.args["beginData"]["frame"];
641             event.initiator = this._layoutInvalidate[frameId];
642             // In case we have no closing Layout event, endData is not available.
643             if (event.args["endData"]) {
644                 event.backendNodeId = event.args["endData"]["rootNode"];
645                 event.highlightQuad =  event.args["endData"]["root"];
646             }
647             this._layoutInvalidate[frameId] = null;
648             if (this._currentScriptEvent)
649                 event.warning = WebInspector.UIString("Forced synchronous layout is a possible performance bottleneck.");
650             break;
651
652         case recordTypes.WebSocketCreate:
653             this._webSocketCreateEvents[event.args["data"]["identifier"]] = event;
654             break;
655
656         case recordTypes.WebSocketSendHandshakeRequest:
657         case recordTypes.WebSocketReceiveHandshakeResponse:
658         case recordTypes.WebSocketDestroy:
659             event.initiator = this._webSocketCreateEvents[event.args["data"]["identifier"]];
660             break;
661
662         case recordTypes.EvaluateScript:
663         case recordTypes.FunctionCall:
664             if (!this._currentScriptEvent)
665                 this._currentScriptEvent = event;
666             break;
667
668         case recordTypes.SetLayerTreeId:
669             this._inspectedTargetLayerTreeId = event.args["layerTreeId"];
670             break;
671
672         case recordTypes.Paint:
673             this._invalidationTracker.didPaint(event);
674             event.highlightQuad = event.args["data"]["clip"];
675             event.backendNodeId = event.args["data"]["nodeId"];
676             var layerUpdateEvent = this._findAncestorEvent(recordTypes.UpdateLayer);
677             if (!layerUpdateEvent || layerUpdateEvent.args["layerTreeId"] !== this._inspectedTargetLayerTreeId)
678                 break;
679             // Only keep layer paint events, skip paints for subframes that get painted to the same layer as parent.
680             if (!event.args["data"]["layerId"])
681                 break;
682             this._lastPaintForLayer[layerUpdateEvent.args["layerId"]] = event;
683             break;
684
685         case recordTypes.PictureSnapshot:
686             var layerUpdateEvent = this._findAncestorEvent(recordTypes.UpdateLayer);
687             if (!layerUpdateEvent || layerUpdateEvent.args["layerTreeId"] !== this._inspectedTargetLayerTreeId)
688                 break;
689             var paintEvent = this._lastPaintForLayer[layerUpdateEvent.args["layerId"]];
690             if (paintEvent)
691                 paintEvent.picture = event;
692             break;
693
694         case recordTypes.ScrollLayer:
695             event.backendNodeId = event.args["data"]["nodeId"];
696             break;
697
698         case recordTypes.PaintImage:
699             event.backendNodeId = event.args["data"]["nodeId"];
700             event.imageURL = event.args["data"]["url"];
701             break;
702
703         case recordTypes.DecodeImage:
704         case recordTypes.ResizeImage:
705             var paintImageEvent = this._findAncestorEvent(recordTypes.PaintImage);
706             if (!paintImageEvent) {
707                 var decodeLazyPixelRefEvent = this._findAncestorEvent(recordTypes.DecodeLazyPixelRef);
708                 paintImageEvent = decodeLazyPixelRefEvent && this._paintImageEventByPixelRefId[decodeLazyPixelRefEvent.args["LazyPixelRef"]];
709             }
710             if (!paintImageEvent)
711                 break;
712             event.backendNodeId = paintImageEvent.backendNodeId;
713             event.imageURL = paintImageEvent.imageURL;
714             break;
715
716         case recordTypes.DrawLazyPixelRef:
717             var paintImageEvent = this._findAncestorEvent(recordTypes.PaintImage);
718             if (!paintImageEvent)
719                 break;
720             this._paintImageEventByPixelRefId[event.args["LazyPixelRef"]] = paintImageEvent;
721             event.backendNodeId = paintImageEvent.backendNodeId;
722             event.imageURL = paintImageEvent.imageURL;
723             break;
724         }
725     },
726
727     /**
728      * @param {string} name
729      * @return {?WebInspector.TracingModel.Event}
730      */
731     _findAncestorEvent: function(name)
732     {
733         for (var i = this._eventStack.length - 1; i >= 0; --i) {
734             var event = this._eventStack[i];
735             if (event.name === name)
736                 return event;
737         }
738         return null;
739     },
740
741     __proto__: WebInspector.TimelineModel.prototype
742 }
743
744 /**
745  * @interface
746  */
747 WebInspector.TracingTimelineModel.Filter = function() { }
748
749 WebInspector.TracingTimelineModel.Filter.prototype = {
750     /**
751      * @param {!WebInspector.TracingModel.Event} event
752      * @return {boolean}
753      */
754     accept: function(event) { }
755 }
756
757 /**
758  * @constructor
759  * @implements {WebInspector.TracingTimelineModel.Filter}
760  * @param {!Array.<string>} eventNames
761  */
762 WebInspector.TracingTimelineModel.EventNameFilter = function(eventNames)
763 {
764     this._eventNames = eventNames.keySet();
765 }
766
767 WebInspector.TracingTimelineModel.EventNameFilter.prototype = {
768     /**
769      * @param {!WebInspector.TracingModel.Event} event
770      * @return {boolean}
771      */
772     accept: function(event)
773     {
774         throw new Error("Not implemented.");
775     }
776 }
777
778 /**
779  * @constructor
780  * @extends {WebInspector.TracingTimelineModel.EventNameFilter}
781  * @param {!Array.<string>} includeNames
782  */
783 WebInspector.TracingTimelineModel.InclusiveEventNameFilter = function(includeNames)
784 {
785     WebInspector.TracingTimelineModel.EventNameFilter.call(this, includeNames)
786 }
787
788 WebInspector.TracingTimelineModel.InclusiveEventNameFilter.prototype = {
789     /**
790      * @override
791      * @param {!WebInspector.TracingModel.Event} event
792      * @return {boolean}
793      */
794     accept: function(event)
795     {
796         return event.category === WebInspector.TracingModel.ConsoleEventCategory || !!this._eventNames[event.name];
797     },
798     __proto__: WebInspector.TracingTimelineModel.EventNameFilter.prototype
799 }
800
801 /**
802  * @constructor
803  * @extends {WebInspector.TracingTimelineModel.EventNameFilter}
804  * @param {!Array.<string>} excludeNames
805  */
806 WebInspector.TracingTimelineModel.ExclusiveEventNameFilter = function(excludeNames)
807 {
808     WebInspector.TracingTimelineModel.EventNameFilter.call(this, excludeNames)
809 }
810
811 WebInspector.TracingTimelineModel.ExclusiveEventNameFilter.prototype = {
812     /**
813      * @override
814      * @param {!WebInspector.TracingModel.Event} event
815      * @return {boolean}
816      */
817     accept: function(event)
818     {
819         return !this._eventNames[event.name];
820     },
821     __proto__: WebInspector.TracingTimelineModel.EventNameFilter.prototype
822 }
823
824 /**
825  * @constructor
826  * @implements {WebInspector.TimelineModel.Record}
827  * @param {!WebInspector.TimelineModel} model
828  * @param {!WebInspector.TracingModel.Event} traceEvent
829  */
830 WebInspector.TracingTimelineModel.TraceEventRecord = function(model, traceEvent)
831 {
832     this._model = model;
833     this._event = traceEvent;
834     traceEvent._timelineRecord = this;
835     this._children = [];
836 }
837
838 WebInspector.TracingTimelineModel.TraceEventRecord.prototype = {
839     /**
840      * @return {?Array.<!ConsoleAgent.CallFrame>}
841      */
842     callSiteStackTrace: function()
843     {
844         var initiator = this._event.initiator;
845         return initiator ? initiator.stackTrace : null;
846     },
847
848     /**
849      * @return {?WebInspector.TimelineModel.Record}
850      */
851     initiator: function()
852     {
853         var initiator = this._event.initiator;
854         return initiator ? initiator._timelineRecord : null;
855     },
856
857     /**
858      * @return {?WebInspector.Target}
859      */
860     target: function()
861     {
862         return this._event.thread.target();
863     },
864
865     /**
866      * @return {number}
867      */
868     selfTime: function()
869     {
870         return this._event.selfTime;
871     },
872
873     /**
874      * @return {!Array.<!WebInspector.TimelineModel.Record>}
875      */
876     children: function()
877     {
878         return this._children;
879     },
880
881     /**
882      * @return {number}
883      */
884     startTime: function()
885     {
886         return this._event.startTime;
887     },
888
889     /**
890      * @return {string}
891      */
892     thread: function()
893     {
894         if (this._event.thread.name() === "CrRendererMain")
895             return WebInspector.TimelineModel.MainThreadName;
896         return this._event.thread.name();
897     },
898
899     /**
900      * @return {number}
901      */
902     endTime: function()
903     {
904         return this._endTime || this._event.endTime || this._event.startTime;
905     },
906
907     /**
908      * @param {number} endTime
909      */
910     setEndTime: function(endTime)
911     {
912         this._endTime = endTime;
913     },
914
915     /**
916      * @return {!Object}
917      */
918     data: function()
919     {
920         return this._event.args["data"];
921     },
922
923     /**
924      * @return {string}
925      */
926     type: function()
927     {
928         if (this._event.category === WebInspector.TracingModel.ConsoleEventCategory)
929             return WebInspector.TracingTimelineModel.RecordType.ConsoleTime;
930         return this._event.name;
931     },
932
933     /**
934      * @return {string}
935      */
936     frameId: function()
937     {
938         switch (this._event.name) {
939         case WebInspector.TracingTimelineModel.RecordType.ScheduleStyleRecalculation:
940         case WebInspector.TracingTimelineModel.RecordType.RecalculateStyles:
941         case WebInspector.TracingTimelineModel.RecordType.InvalidateLayout:
942             return this._event.args["frameId"];
943         case WebInspector.TracingTimelineModel.RecordType.Layout:
944             return this._event.args["beginData"]["frameId"];
945         default:
946             var data = this._event.args["data"];
947             return (data && data["frame"]) || "";
948         }
949     },
950
951     /**
952      * @return {?Array.<!ConsoleAgent.CallFrame>}
953      */
954     stackTrace: function()
955     {
956         return this._event.stackTrace;
957     },
958
959     /**
960      * @param {string} key
961      * @return {?Object}
962      */
963     getUserObject: function(key)
964     {
965         if (key === "TimelineUIUtils::preview-element")
966             return this._event.previewElement;
967         throw new Error("Unexpected key: " + key);
968     },
969
970     /**
971      * @param {string} key
972      * @param {?Object|undefined} value
973      */
974     setUserObject: function(key, value)
975     {
976         if (key !== "TimelineUIUtils::preview-element")
977             throw new Error("Unexpected key: " + key);
978         this._event.previewElement = /** @type {?Element} */ (value);
979     },
980
981     /**
982      * @return {?Array.<string>}
983      */
984     warnings: function()
985     {
986         if (this._event.warning)
987             return [this._event.warning];
988         return null;
989     },
990
991     /**
992      * @return {!WebInspector.TracingModel.Event}
993      */
994     traceEvent: function()
995     {
996         return this._event;
997     },
998
999     /**
1000      * @param {!WebInspector.TracingTimelineModel.TraceEventRecord} child
1001      */
1002     _addChild: function(child)
1003     {
1004         this._children.push(child);
1005         child.parent = this;
1006     },
1007
1008     /**
1009      * @return {!WebInspector.TimelineModel}
1010      */
1011     timelineModel: function()
1012     {
1013         return this._model;
1014     }
1015 }
1016
1017
1018
1019 /**
1020  * @constructor
1021  * @implements {WebInspector.OutputStream}
1022  * @param {!WebInspector.TracingTimelineModel} model
1023  * @param {!{cancel: function()}} reader
1024  * @param {!WebInspector.Progress} progress
1025  */
1026 WebInspector.TracingModelLoader = function(model, reader, progress)
1027 {
1028     this._model = model;
1029     this._reader = reader;
1030     this._progress = progress;
1031     this._buffer = "";
1032     this._firstChunk = true;
1033     this._loader = new WebInspector.TracingModel.Loader(model._tracingModel);
1034 }
1035
1036 WebInspector.TracingModelLoader.prototype = {
1037     /**
1038      * @param {string} chunk
1039      */
1040     write: function(chunk)
1041     {
1042         var data = this._buffer + chunk;
1043         var lastIndex = 0;
1044         var index;
1045         do {
1046             index = lastIndex;
1047             lastIndex = WebInspector.TextUtils.findBalancedCurlyBrackets(data, index);
1048         } while (lastIndex !== -1)
1049
1050         var json = data.slice(0, index) + "]";
1051         this._buffer = data.slice(index);
1052
1053         if (!index)
1054             return;
1055
1056         if (this._firstChunk) {
1057             this._model._startCollectingTraceEvents(true);
1058         } else {
1059             var commaIndex = json.indexOf(",");
1060             if (commaIndex !== -1)
1061                 json = json.slice(commaIndex + 1);
1062             json = "[" + json;
1063         }
1064
1065         var items;
1066         try {
1067             items = /** @type {!Array.<!WebInspector.TracingManager.EventPayload>} */ (JSON.parse(json));
1068         } catch (e) {
1069             this._reportErrorAndCancelLoading("Malformed timeline data: " + e);
1070             return;
1071         }
1072
1073         if (this._firstChunk) {
1074             this._firstChunk = false;
1075             if (this._looksLikeAppVersion(items[0])) {
1076                 this._reportErrorAndCancelLoading("Old Timeline format is not supported.");
1077                 return;
1078             }
1079         }
1080
1081         try {
1082             this._loader.loadNextChunk(items);
1083         } catch(e) {
1084             this._reportErrorAndCancelLoading("Malformed timeline data: " + e);
1085             return;
1086         }
1087     },
1088
1089     _reportErrorAndCancelLoading: function(messsage)
1090     {
1091         WebInspector.console.error(messsage);
1092         this._model._onTracingComplete();
1093         this._model.reset();
1094         this._reader.cancel();
1095         this._progress.done();
1096     },
1097
1098     _looksLikeAppVersion: function(item)
1099     {
1100         return typeof item === "string" && item.indexOf("Chrome") !== -1;
1101     },
1102
1103     close: function()
1104     {
1105         this._loader.finish();
1106         this._model._onTracingComplete();
1107     }
1108 }
1109
1110 /**
1111  * @constructor
1112  * @param {!WebInspector.OutputStream} stream
1113  * @implements {WebInspector.OutputStreamDelegate}
1114  */
1115 WebInspector.TracingTimelineSaver = function(stream)
1116 {
1117     this._stream = stream;
1118 }
1119
1120 WebInspector.TracingTimelineSaver.prototype = {
1121     onTransferStarted: function()
1122     {
1123         this._stream.write("[");
1124     },
1125
1126     onTransferFinished: function()
1127     {
1128         this._stream.write("]");
1129     },
1130
1131     /**
1132      * @param {!WebInspector.ChunkedReader} reader
1133      */
1134     onChunkTransferred: function(reader) { },
1135
1136     /**
1137      * @param {!WebInspector.ChunkedReader} reader
1138      * @param {!Event} event
1139      */
1140     onError: function(reader, event) { },
1141 }
1142
1143 /**
1144  * @constructor
1145  * @param {!WebInspector.TracingModel.Event} event
1146  */
1147 WebInspector.InvalidationTrackingEvent = function(event)
1148 {
1149     this.type = event.name;
1150     this.frameId = event.args["data"]["frame"];
1151     this.nodeId = event.args["data"]["nodeId"];
1152     this.nodeName = event.args["data"]["nodeName"];
1153     this.paintId = event.args["data"]["paintId"];
1154     this.reason = event.args["data"]["reason"];
1155     this.stackTrace = event.args["data"]["stackTrace"];
1156 }
1157
1158 /**
1159  * @constructor
1160  */
1161 WebInspector.InvalidationTracker = function()
1162 {
1163     this._initializePerFrameState();
1164 }
1165
1166 WebInspector.InvalidationTracker.prototype = {
1167     /**
1168      * @param {!WebInspector.TracingModel.Event} event
1169      */
1170     addInvalidation: function(event)
1171     {
1172         var invalidation = new WebInspector.InvalidationTrackingEvent(event);
1173
1174         this._startNewFrameIfNeeded();
1175         if (!invalidation.nodeId && !invalidation.paintId) {
1176             console.error("Invalidation lacks node information.");
1177             console.error(invalidation);
1178         }
1179
1180         // Record the paintIds for style recalc or layout invalidations.
1181         // FIXME: This O(n^2) loop could be optimized with a map.
1182         var recordTypes = WebInspector.TracingTimelineModel.RecordType;
1183         if (invalidation.type == recordTypes.PaintInvalidationTracking)
1184             this._invalidationEvents.forEach(updatePaintId);
1185         else
1186             this._invalidationEvents.push(invalidation);
1187
1188         function updatePaintId(invalidationToUpdate)
1189         {
1190             if (invalidationToUpdate.nodeId !== invalidation.nodeId)
1191                 return;
1192             if (invalidationToUpdate.type === recordTypes.StyleRecalcInvalidationTracking
1193                     || invalidationToUpdate.type === recordTypes.LayoutInvalidationTracking) {
1194                 invalidationToUpdate.paintId = invalidation.paintId;
1195             }
1196         }
1197     },
1198
1199     /**
1200      * @param {!WebInspector.TracingModel.Event} styleRecalcEvent
1201      */
1202     didRecalcStyle: function(styleRecalcEvent)
1203     {
1204         var recalcFrameId = styleRecalcEvent.args["frame"];
1205         var index = this._lastStyleRecalcEventIndex;
1206         var invalidationCount = this._invalidationEvents.length;
1207         for (; index < invalidationCount; index++) {
1208             var invalidation = this._invalidationEvents[index];
1209             if (invalidation.type !== WebInspector.TracingTimelineModel.RecordType.StyleRecalcInvalidationTracking)
1210                 continue;
1211             if (invalidation.frameId === recalcFrameId)
1212                 this._addInvalidationTrackingEvent(styleRecalcEvent, invalidation);
1213         }
1214
1215         this._lastStyleRecalcEventIndex = invalidationCount;
1216     },
1217
1218     /**
1219      * @param {!WebInspector.TracingModel.Event} layoutEvent
1220      */
1221     didLayout: function(layoutEvent)
1222     {
1223         var layoutFrameId = layoutEvent.args["beginData"]["frame"];
1224         var index = this._lastLayoutEventIndex;
1225         var invalidationCount = this._invalidationEvents.length;
1226         for (; index < invalidationCount; index++) {
1227             var invalidation = this._invalidationEvents[index];
1228             if (invalidation.type !== WebInspector.TracingTimelineModel.RecordType.LayoutInvalidationTracking)
1229                 continue;
1230             if (invalidation.frameId === layoutFrameId)
1231                 this._addInvalidationTrackingEvent(layoutEvent, invalidation);
1232         }
1233
1234         this._lastLayoutEventIndex = invalidationCount;
1235     },
1236
1237     /**
1238      * @param {!WebInspector.TracingModel.Event} paintEvent
1239      */
1240     didPaint: function(paintEvent)
1241     {
1242         this._didPaint = true;
1243
1244         // If a paint doesn't have a corresponding graphics layer id, it paints
1245         // into its parent so add an effectivePaintId to these events.
1246         var layerId = paintEvent.args["data"]["layerId"];
1247         if (layerId)
1248             this._lastPaintWithLayer = paintEvent;
1249         if (!this._lastPaintWithLayer) {
1250             console.error("Failed to find the paint container for a paint event.");
1251             return;
1252         }
1253
1254         var effectivePaintId = this._lastPaintWithLayer.args["data"]["nodeId"];
1255         var frameId = paintEvent.args["data"]["frame"];
1256         this._invalidationEvents.forEach(recordInvalidationForPaint.bind(this));
1257
1258         /**
1259          * @param {!WebInspector.InvalidationTrackingEvent} invalidation
1260          * @this {WebInspector.InvalidationTracker}
1261          */
1262         function recordInvalidationForPaint(invalidation)
1263         {
1264             if (invalidation.paintId === effectivePaintId && invalidation.frameId === frameId)
1265                 this._addInvalidationTrackingEvent(paintEvent, invalidation);
1266         }
1267     },
1268
1269     /**
1270      * @param {!WebInspector.TracingModel.Event} event
1271      * @param {!WebInspector.InvalidationTrackingEvent} invalidation
1272      */
1273     _addInvalidationTrackingEvent: function(event, invalidation)
1274     {
1275         if (!event.invalidationTrackingEvents)
1276             event.invalidationTrackingEvents = [ invalidation ];
1277         else
1278             event.invalidationTrackingEvents.push(invalidation);
1279     },
1280
1281     _startNewFrameIfNeeded: function()
1282     {
1283         if (!this._didPaint)
1284             return;
1285
1286         this._initializePerFrameState();
1287     },
1288
1289     _initializePerFrameState: function()
1290     {
1291         /** @type {!Array.<!WebInspector.InvalidationTrackingEvent>} */
1292         this._invalidationEvents = [];
1293         this._lastStyleRecalcEventIndex = 0;
1294         this._lastLayoutEventIndex = 0;
1295         this._lastPaintWithLayer = undefined;
1296         this._didPaint = false;
1297     }
1298 }