Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / timeline / TimelineFlameChart.js
1 /*
2  * Copyright (C) 2014 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 /**
32  * @constructor
33  * @implements {WebInspector.FlameChartDataProvider}
34  * @param {!WebInspector.TracingTimelineModel} model
35  * @param {!WebInspector.TimelineFrameModelBase} frameModel
36  */
37 WebInspector.TimelineFlameChartDataProvider = function(model, frameModel)
38 {
39     WebInspector.FlameChartDataProvider.call(this);
40     this.reset();
41     this._model = model;
42     this._frameModel = frameModel;
43     this._font = "12px " + WebInspector.fontFamily();
44     this._linkifier = new WebInspector.Linkifier();
45     this._filters = [];
46     this.addFilter(WebInspector.TracingTimelineUIUtils.hiddenEventsFilter());
47     this.addFilter(new WebInspector.TracingTimelineModel.ExclusiveEventNameFilter([WebInspector.TracingTimelineModel.RecordType.Program]));
48 }
49
50 WebInspector.TimelineFlameChartDataProvider.InstantEventVisibleDurationMs = 0.01;
51 WebInspector.TimelineFlameChartDataProvider.JSFrameCoalsceThresholdMs = 1.1;
52
53 WebInspector.TimelineFlameChartDataProvider.prototype = {
54     /**
55      * @return {number}
56      */
57     barHeight: function()
58     {
59         return 20;
60     },
61
62     /**
63      * @return {number}
64      */
65     textBaseline: function()
66     {
67         return 6;
68     },
69
70     /**
71      * @return {number}
72      */
73     textPadding: function()
74     {
75         return 5;
76     },
77
78     /**
79      * @param {number} entryIndex
80      * @return {string}
81      */
82     entryFont: function(entryIndex)
83     {
84         return this._font;
85     },
86
87     /**
88      * @param {number} entryIndex
89      * @return {?string}
90      */
91     entryTitle: function(entryIndex)
92     {
93         var event = this._entryEvents[entryIndex];
94         if (event) {
95             var name = WebInspector.TracingTimelineUIUtils.styleForTraceEvent(event.name).title;
96             // TODO(yurys): support event dividers
97             var details = WebInspector.TracingTimelineUIUtils.buildDetailsNodeForTraceEvent(event, this._linkifier);
98             if (event.name === WebInspector.TracingTimelineModel.RecordType.JSFrame && details)
99                 return details.textContent;
100             return details ? WebInspector.UIString("%s (%s)", name, details.textContent) : name;
101         }
102         var title = this._entryIndexToTitle[entryIndex];
103         if (!title) {
104             title = WebInspector.UIString("Unexpected entryIndex %d", entryIndex);
105             console.error(title);
106         }
107         return title;
108     },
109
110     /**
111      * @param {number} startTime
112      * @param {number} endTime
113      * @return {?Array.<number>}
114      */
115     dividerOffsets: function(startTime, endTime)
116     {
117         return null;
118     },
119
120     /**
121      * @override
122      * @param {number} index
123      * @return {string}
124      */
125     markerColor: function(index)
126     {
127         var event = this._markerEvents[index];
128         return WebInspector.TracingTimelineUIUtils.markerEventColor(event.name);
129     },
130
131     /**
132      * @override
133      * @param {number} index
134      * @return {string}
135      */
136     markerTitle: function(index)
137     {
138         var event = this._markerEvents[index];
139         return WebInspector.TracingTimelineUIUtils.eventTitle(event, this._model);
140     },
141
142     reset: function()
143     {
144         this._timelineData = null;
145         /** @type {!Array.<!WebInspector.TracingModel.Event>} */
146         this._entryEvents = [];
147         this._entryIndexToTitle = {};
148         this._markerEvents = [];
149         this._entryIndexToFrame = {};
150     },
151
152     /**
153      * @return {!WebInspector.FlameChart.TimelineData}
154      */
155     timelineData: function()
156     {
157         if (this._timelineData)
158             return this._timelineData;
159
160         this._timelineData = new WebInspector.FlameChart.TimelineData([], [], []);
161
162         this._minimumBoundary = this._model.minimumRecordTime();
163         this._timeSpan = this._model.isEmpty() ?  1000 : this._model.maximumRecordTime() - this._minimumBoundary;
164         this._currentLevel = 0;
165         this._appendFrameBars(this._frameModel.frames());
166         this._appendThreadTimelineData(WebInspector.UIString("Main Thread"), this._model.mainThreadEvents());
167         var threads = this._model.virtualThreads();
168         for (var i = 0; i < threads.length; i++) {
169             var thread = threads[i];
170             this._appendThreadTimelineData(thread.name, thread.events);
171         }
172         return this._timelineData;
173     },
174
175     /**
176      * @param {string} headerName
177      * @param {!Array.<!WebInspector.TracingModel.Event>} traceEvents
178      */
179     _appendThreadTimelineData: function(headerName, traceEvents)
180     {
181         var maxStackDepth = 0;
182         var openEvents = [];
183         var headerAppended = false;
184         var events = traceEvents;
185         if (WebInspector.experimentsSettings.timelineJSCPUProfile.isEnabled()) {
186             var jsFrameEvents = this._generateJSFrameEvents(traceEvents);
187             events = jsFrameEvents.mergeOrdered(traceEvents, WebInspector.TracingModel.Event.orderedCompareStartTime);
188         }
189         for (var i = 0; i < events.length; ++i) {
190             var e = events[i];
191             // FIXME: clean up once phase name is unified between Blink and Chromium.
192             if (!e.endTime && e.phase !== WebInspector.TracingModel.Phase.Instant && e.phase !== "I")
193                 continue;
194             if (WebInspector.TracingTimelineUIUtils.isMarkerEvent(e)) {
195                 this._markerEvents.push(e);
196                 this._timelineData.markerTimestamps.push(e.startTime);
197             }
198             if (!this._isVisible(e))
199                 continue;
200             while (openEvents.length && openEvents.peekLast().endTime <= e.startTime)
201                 openEvents.pop();
202             if (!headerAppended) {
203                 this._appendHeaderRecord(headerName, this._currentLevel++);
204                 headerAppended = true;
205             }
206             this._appendEvent(e, this._currentLevel + openEvents.length);
207             maxStackDepth = Math.max(maxStackDepth, openEvents.length + 1);
208             if (e.endTime)
209                 openEvents.push(e);
210         }
211         this._currentLevel += maxStackDepth;
212         if (headerAppended)
213             ++this._currentLevel;
214     },
215
216     /**
217      * @param {!Array.<!WebInspector.TimelineFrame>} frames
218      */
219     _appendFrameBars: function(frames)
220     {
221         this._frameBarsLevel = this._currentLevel++;
222         for (var i = 0; i < frames.length; ++i)
223             this._appendFrame(frames[i]);
224     },
225
226     /**
227      * @param {!Array.<!WebInspector.TracingModel.Event>} events
228      * @return {!Array.<!WebInspector.TracingModel.Event>}
229      */
230     _generateJSFrameEvents: function(events)
231     {
232         function equalFrames(frame1, frame2)
233         {
234             return frame1.scriptId === frame2.scriptId && frame1.functionName === frame2.functionName;
235         }
236         function eventEndTime(e)
237         {
238             return e.endTime || e.startTime + WebInspector.TimelineFlameChartDataProvider.InstantEventVisibleDurationMs;
239         }
240         function isJSInvocationEvent(e)
241         {
242             switch (e.name) {
243             case WebInspector.TracingTimelineModel.RecordType.FunctionCall:
244             case WebInspector.TracingTimelineModel.RecordType.EvaluateScript:
245                 return true;
246             }
247             return false;
248         }
249         var jsFrameEvents = [];
250         var stackTraceOpenEvents = [];
251         var currentJSInvocationEndTime = 0;
252         var coalesceThresholdMs = WebInspector.TimelineFlameChartDataProvider.JSFrameCoalsceThresholdMs;
253         for (var i = 0; i < events.length; ++i) {
254             var e = events[i];
255             if (e.startTime >= currentJSInvocationEndTime) {
256                 stackTraceOpenEvents.length = 0;
257                 currentJSInvocationEndTime = 0;
258             }
259             if (isJSInvocationEvent(e))
260                 currentJSInvocationEndTime = e.endTime;
261             if (!currentJSInvocationEndTime)
262                 continue;
263             if (!e.stackTrace)
264                 continue;
265             while (stackTraceOpenEvents.length && eventEndTime(stackTraceOpenEvents.peekLast()) + coalesceThresholdMs <= e.startTime)
266                 stackTraceOpenEvents.pop();
267             var numFrames = e.stackTrace.length;
268             for (var j = 0; j < numFrames && j < stackTraceOpenEvents.length; ++j) {
269                 var frame = e.stackTrace[numFrames - 1 - j];
270                 if (!equalFrames(frame, stackTraceOpenEvents[j].args["data"]))
271                     break;
272                 stackTraceOpenEvents[j].endTime = Math.max(stackTraceOpenEvents[j].endTime, eventEndTime(e));
273                 stackTraceOpenEvents[j].duration = stackTraceOpenEvents[j].endTime - stackTraceOpenEvents[j].startTime;
274             }
275             stackTraceOpenEvents.length = j;
276             var timestampUs = e.startTime * 1000;
277             var durationUs = (e.duration || WebInspector.TimelineFlameChartDataProvider.InstantEventVisibleDurationMs) * 1000;
278             for (; j < numFrames; ++j) {
279                 var frame = e.stackTrace[numFrames - 1 - j];
280                 var payload = /** @type {!WebInspector.TracingModel.EventPayload} */ ({
281                     ph: e.phase,
282                     cat: WebInspector.TracingModel.DevToolsMetadataEventCategory,
283                     name: WebInspector.TracingTimelineModel.RecordType.JSFrame,
284                     ts: timestampUs,
285                     dur: durationUs,
286                     args: {
287                         data: frame
288                     }
289                 });
290                 var jsFrameEvent = new WebInspector.TracingModel.Event(payload, 0, e.thread);
291                 stackTraceOpenEvents.push(jsFrameEvent);
292                 jsFrameEvents.push(jsFrameEvent);
293             }
294         }
295         return jsFrameEvents;
296     },
297
298     /**
299      * @param {!WebInspector.TracingTimelineModel.Filter} filter
300      */
301     addFilter: function(filter)
302     {
303         this._filters.push(filter);
304     },
305
306     /**
307      * @param {!WebInspector.TracingModel.Event} event
308      * @return {boolean}
309      */
310     _isVisible: function(event)
311     {
312         return this._filters.every(function (filter) { return filter.accept(event); });
313     },
314
315     /**
316      * @return {number}
317      */
318     minimumBoundary: function()
319     {
320         return this._minimumBoundary;
321     },
322
323     /**
324      * @return {number}
325      */
326     totalTime: function()
327     {
328         return this._timeSpan;
329     },
330
331     /**
332      * @return {number}
333      */
334     maxStackDepth: function()
335     {
336         return this._currentLevel;
337     },
338
339     /**
340      * @param {number} entryIndex
341      * @return {?Array.<!{title: string, text: string}>}
342      */
343     prepareHighlightedEntryInfo: function(entryIndex)
344     {
345         return null;
346     },
347
348     /**
349      * @param {number} entryIndex
350      * @return {boolean}
351      */
352     canJumpToEntry: function(entryIndex)
353     {
354         return false;
355     },
356
357     /**
358      * @param {number} entryIndex
359      * @return {string}
360      */
361     entryColor: function(entryIndex)
362     {
363         var event = this._entryEvents[entryIndex];
364         if (!event)
365             return this._entryIndexToFrame[entryIndex] ? "white" : "#555";
366         if (event.name === WebInspector.TracingTimelineModel.RecordType.JSFrame)
367             return WebInspector.TimelineFlameChartDataProvider.jsFrameColorGenerator().colorForID(event.args["data"]["functionName"]);
368         var style = WebInspector.TracingTimelineUIUtils.styleForTraceEvent(event.name);
369         return style.category.fillColorStop1;
370     },
371
372     /**
373      * @param {number} entryIndex
374      * @param {!CanvasRenderingContext2D} context
375      * @param {?string} text
376      * @param {number} barX
377      * @param {number} barY
378      * @param {number} barWidth
379      * @param {number} barHeight
380      * @param {function(number):number} offsetToPosition
381      * @return {boolean}
382      */
383     decorateEntry: function(entryIndex, context, text, barX, barY, barWidth, barHeight, offsetToPosition)
384     {
385         var frame = this._entryIndexToFrame[entryIndex];
386         if (frame) {
387             context.save();
388
389             context.translate(0.5, 0.5);
390
391             context.beginPath();
392             context.moveTo(barX, barY);
393             context.lineTo(barX, context.canvas.height);
394             context.strokeStyle = "rgba(100, 100, 100, 0.4)";
395             context.setLineDash([5]);
396             context.stroke();
397             context.setLineDash([]);
398
399
400             var padding = 4 * window.devicePixelRatio;
401             barX += padding;
402             barWidth -= 2 * padding;
403             barY += padding;
404             barHeight -= 2 * padding;
405
406             var cornerRadis = 3;
407             var radiusY = cornerRadis;
408             var radiusX = Math.min(cornerRadis, barWidth / 2);
409
410             context.beginPath();
411             context.moveTo(barX + radiusX, barY);
412             context.lineTo(barX + barWidth - radiusX, barY);
413             context.quadraticCurveTo(barX + barWidth, barY, barX + barWidth, barY + radiusY);
414             context.lineTo(barX + barWidth, barY + barHeight - radiusY);
415             context.quadraticCurveTo(barX + barWidth, barY + barHeight, barX + barWidth - radiusX, barY + barHeight);
416             context.lineTo(barX + radiusX, barY + barHeight);
417             context.quadraticCurveTo(barX, barY + barHeight, barX, barY + barHeight - radiusY);
418             context.lineTo(barX, barY + radiusY);
419             context.quadraticCurveTo(barX, barY, barX + radiusX, barY);
420             context.closePath();
421
422             context.fillStyle = "rgba(200, 200, 200, 0.8)";
423             context.fill();
424             context.strokeStyle = "rgba(150, 150, 150, 0.8)";
425             context.stroke();
426
427             var frameDurationText = Number.millisToString(frame.duration, true);
428             var textWidth = context.measureText(frameDurationText).width;
429             if (barWidth > textWidth) {
430                 context.fillStyle = "#555";
431                 context.fillText(frameDurationText, barX + ((barWidth - textWidth) >> 1), barY + barHeight - 2);
432             }
433             context.restore();
434             return true;
435         }
436         if (barWidth < 5)
437             return false;
438
439         // Paint text using white color on dark background.
440         if (text) {
441             context.save();
442             context.fillStyle = "white";
443             context.shadowColor = "rgba(0, 0, 0, 0.1)";
444             context.shadowOffsetX = 1;
445             context.shadowOffsetY = 1;
446             context.font = this._font;
447             context.fillText(text, barX + this.textPadding(), barY + barHeight - this.textBaseline());
448             context.restore();
449         }
450
451         var event = this._entryEvents[entryIndex];
452         if (event && event.warning) {
453             context.save();
454
455             context.rect(barX, barY, barWidth, this.barHeight());
456             context.clip();
457
458             context.beginPath();
459             context.fillStyle = "red";
460             context.moveTo(barX + barWidth - 15, barY + 1);
461             context.lineTo(barX + barWidth - 1, barY + 1);
462             context.lineTo(barX + barWidth - 1, barY + 15);
463             context.fill();
464
465             context.restore();
466         }
467
468         return true;
469     },
470
471     /**
472      * @param {number} entryIndex
473      * @return {boolean}
474      */
475     forceDecoration: function(entryIndex)
476     {
477         var event = this._entryEvents[entryIndex];
478         if (!event)
479             return !!this._entryIndexToFrame[entryIndex];
480         return !!event.warning;
481     },
482
483    /**
484      * @param {number} entryIndex
485      * @return {?{startTime: number, endTime: number}}
486      */
487     highlightTimeRange: function(entryIndex)
488     {
489         var event = this._entryEvents[entryIndex] || this._entryIndexToFrame[entryIndex];
490         if (!event)
491             return null;
492         return {
493             startTime: event.startTime,
494             endTime: event.endTime || event.startTime + WebInspector.TimelineFlameChartDataProvider.InstantEventVisibleDurationMs
495         }
496     },
497
498     /**
499      * @return {number}
500      */
501     paddingLeft: function()
502     {
503         return 0;
504     },
505
506     /**
507      * @param {number} entryIndex
508      * @return {string}
509      */
510     textColor: function(entryIndex)
511     {
512         return "white";
513     },
514
515     /**
516      * @param {string} title
517      * @param {number} level
518      */
519     _appendHeaderRecord: function(title, level)
520     {
521         var index = this._entryEvents.length;
522         this._entryIndexToTitle[index] = title;
523         this._entryEvents.push(null);
524         this._timelineData.entryLevels[index] = level;
525         this._timelineData.entryTotalTimes[index] = this._timeSpan;
526         this._timelineData.entryStartTimes[index] = this._minimumBoundary;
527     },
528
529     /**
530      * @param {!WebInspector.TracingModel.Event} event
531      * @param {number} level
532      */
533     _appendEvent: function(event, level)
534     {
535         var index = this._entryEvents.length;
536         this._entryEvents.push(event);
537         this._timelineData.entryLevels[index] = level;
538         this._timelineData.entryTotalTimes[index] = event.duration || WebInspector.TimelineFlameChartDataProvider.InstantEventVisibleDurationMs;
539         this._timelineData.entryStartTimes[index] = event.startTime;
540     },
541
542     /**
543      * @param {!WebInspector.TimelineFrame} frame
544      */
545     _appendFrame: function(frame)
546     {
547         var index = this._entryEvents.length;
548         this._entryEvents.push(null);
549         this._entryIndexToFrame[index] = frame;
550         this._entryIndexToTitle[index] = Number.millisToString(frame.duration, true);
551         this._timelineData.entryLevels[index] = this._frameBarsLevel;
552         this._timelineData.entryTotalTimes[index] = frame.duration;
553         this._timelineData.entryStartTimes[index] = frame.startTime;
554     },
555
556     /**
557      * @param {number} entryIndex
558      * @return {?WebInspector.TimelineSelection}
559      */
560     createSelection: function(entryIndex)
561     {
562         var event = this._entryEvents[entryIndex];
563         if (event) {
564             this._lastSelection = new WebInspector.TimelineFlameChart.Selection(WebInspector.TimelineSelection.fromTraceEvent(event), entryIndex);
565             return this._lastSelection.timelineSelection;
566         }
567         var frame = this._entryIndexToFrame[entryIndex];
568         if (frame) {
569             this._lastSelection = new WebInspector.TimelineFlameChart.Selection(WebInspector.TimelineSelection.fromFrame(frame), entryIndex);
570             return this._lastSelection.timelineSelection;
571         }
572         return null;
573     },
574
575     /**
576      * @param {?WebInspector.TimelineSelection} selection
577      * @return {number}
578      */
579     entryIndexForSelection: function(selection)
580     {
581         if (!selection)
582             return -1;
583
584         if (this._lastSelection && this._lastSelection.timelineSelection.object() === selection.object())
585             return this._lastSelection.entryIndex;
586         switch  (selection.type()) {
587         case WebInspector.TimelineSelection.Type.TraceEvent:
588             var event = /** @type{!WebInspector.TracingModel.Event} */ (selection.object());
589             var entryEvents = this._entryEvents;
590             for (var entryIndex = 0; entryIndex < entryEvents.length; ++entryIndex) {
591                 if (entryEvents[entryIndex] === event) {
592                     this._lastSelection = new WebInspector.TimelineFlameChart.Selection(WebInspector.TimelineSelection.fromTraceEvent(event), entryIndex);
593                     return entryIndex;
594                 }
595             }
596             break;
597         case WebInspector.TimelineSelection.Type.Frame:
598             var frame = /** @type {!WebInspector.TimelineFrame} */ (selection.object());
599             for (var frameIndex in this._entryIndexToFrame) {
600                 if (this._entryIndexToFrame[frameIndex] === frame) {
601                     this._lastSelection = new WebInspector.TimelineFlameChart.Selection(WebInspector.TimelineSelection.fromFrame(frame), Number(frameIndex));
602                     return Number(frameIndex);
603                 }
604             }
605             break;
606         }
607         return -1;
608     }
609 }
610
611 /**
612  * @return {!WebInspector.FlameChart.ColorGenerator}
613  */
614 WebInspector.TimelineFlameChartDataProvider.jsFrameColorGenerator = function()
615 {
616     if (!WebInspector.TimelineFlameChartDataProvider._jsFrameColorGenerator) {
617         var hueSpace = { min: 30, max: 55, count: 5 };
618         var satSpace = { min: 70, max: 100, count: 6 };
619         var colorGenerator = new WebInspector.FlameChart.ColorGenerator(hueSpace, satSpace, 50);
620         colorGenerator.setColorForID("(idle)", "hsl(0, 0%, 60%)");
621         colorGenerator.setColorForID("(program)", "hsl(0, 0%, 60%)");
622         colorGenerator.setColorForID("(garbage collector)", "hsl(0, 0%, 60%)");
623         WebInspector.TimelineFlameChartDataProvider._jsFrameColorGenerator = colorGenerator;
624     }
625     return WebInspector.TimelineFlameChartDataProvider._jsFrameColorGenerator;
626 }
627
628 /**
629  * @constructor
630  * @extends {WebInspector.VBox}
631  * @implements {WebInspector.TimelineModeView}
632  * @implements {WebInspector.FlameChartDelegate}
633  * @param {!WebInspector.TimelineModeViewDelegate} delegate
634  * @param {!WebInspector.TracingTimelineModel} tracingModel
635  * @param {!WebInspector.TimelineFrameModelBase} frameModel
636  */
637 WebInspector.TimelineFlameChart = function(delegate, tracingModel, frameModel)
638 {
639     WebInspector.VBox.call(this);
640     this.element.classList.add("timeline-flamechart");
641     this.registerRequiredCSS("flameChart.css");
642     this._delegate = delegate;
643     this._model = tracingModel;
644     this._dataProvider = new WebInspector.TimelineFlameChartDataProvider(tracingModel, frameModel)
645     this._mainView = new WebInspector.FlameChart(this._dataProvider, this, true);
646     this._mainView.show(this.element);
647     this._model.addEventListener(WebInspector.TimelineModel.Events.RecordingStarted, this._onRecordingStarted, this);
648     this._mainView.addEventListener(WebInspector.FlameChart.Events.EntrySelected, this._onEntrySelected, this);
649 }
650
651 WebInspector.TimelineFlameChart.prototype = {
652     dispose: function()
653     {
654         this._model.removeEventListener(WebInspector.TimelineModel.Events.RecordingStarted, this._onRecordingStarted, this);
655         this._mainView.removeEventListener(WebInspector.FlameChart.Events.EntrySelected, this._onEntrySelected, this);
656     },
657
658     /**
659      * @param {number} windowStartTime
660      * @param {number} windowEndTime
661      */
662     requestWindowTimes: function(windowStartTime, windowEndTime)
663     {
664         this._delegate.requestWindowTimes(windowStartTime, windowEndTime);
665     },
666
667     /**
668      * @param {?RegExp} textFilter
669      */
670     refreshRecords: function(textFilter)
671     {
672         this._dataProvider.reset();
673         this._mainView.scheduleUpdate();
674     },
675
676     wasShown: function()
677     {
678         this._mainView.scheduleUpdate();
679     },
680
681     /**
682      * @return {!WebInspector.View}
683      */
684     view: function()
685     {
686         return this;
687     },
688
689     reset: function()
690     {
691         this._automaticallySizeWindow = true;
692         this._dataProvider.reset();
693         this._mainView.reset();
694         this._mainView.setWindowTimes(0, Infinity);
695     },
696
697     _onRecordingStarted: function()
698     {
699         this._automaticallySizeWindow = true;
700         this._mainView.reset();
701     },
702
703     /**
704      * @param {!WebInspector.TimelineModel.Record} record
705      */
706     addRecord: function(record)
707     {
708         this._dataProvider.reset();
709         if (this._automaticallySizeWindow) {
710             var minimumRecordTime = this._model.minimumRecordTime();
711             if (record.startTime() > (minimumRecordTime + 1000)) {
712                 this._automaticallySizeWindow = false;
713                 this._delegate.requestWindowTimes(minimumRecordTime, minimumRecordTime + 1000);
714             }
715             this._mainView.scheduleUpdate();
716         } else {
717             if (!this._pendingUpdateTimer)
718                 this._pendingUpdateTimer = window.setTimeout(this._updateOnAddRecord.bind(this), 300);
719         }
720     },
721
722     _updateOnAddRecord: function()
723     {
724         delete this._pendingUpdateTimer;
725         this._mainView.scheduleUpdate();
726     },
727
728     /**
729      * @param {number} startTime
730      * @param {number} endTime
731      */
732     setWindowTimes: function(startTime, endTime)
733     {
734         this._mainView.setWindowTimes(startTime, endTime);
735         this._delegate.select(null);
736     },
737
738     /**
739      * @param {number} width
740      */
741     setSidebarSize: function(width)
742     {
743     },
744
745     /**
746      * @param {?WebInspector.TimelineModel.Record} record
747      * @param {string=} regex
748      * @param {boolean=} selectRecord
749      */
750     highlightSearchResult: function(record, regex, selectRecord)
751     {
752     },
753
754     /**
755      * @param {?WebInspector.TimelineSelection} selection
756      */
757     setSelection: function(selection)
758     {
759         var index = this._dataProvider.entryIndexForSelection(selection);
760         this._mainView.setSelectedEntry(index);
761     },
762
763     /**
764      * @param {!WebInspector.Event} event
765      */
766     _onEntrySelected: function(event)
767     {
768         var entryIndex = /** @type{number} */ (event.data);
769         var timelineSelection = this._dataProvider.createSelection(entryIndex);
770         if (timelineSelection)
771             this._delegate.select(timelineSelection);
772     },
773
774     __proto__: WebInspector.VBox.prototype
775 }
776
777 /**
778   * @constructor
779   * @param {!WebInspector.TimelineSelection} selection
780   * @param {number} entryIndex
781   */
782 WebInspector.TimelineFlameChart.Selection = function(selection, entryIndex)
783 {
784     this.timelineSelection = selection;
785     this.entryIndex = entryIndex;
786 }