blob: 82061cd8c32584fe223786513dc0212baf57edb2 [file] [log] [blame]
/*
* Copyright (C) 2014 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @constructor
* @implements {WebInspector.FlameChartDataProvider}
* @param {!WebInspector.TracingTimelineModel} model
* @param {!WebInspector.TimelineFrameModelBase} frameModel
*/
WebInspector.TimelineFlameChartDataProvider = function(model, frameModel)
{
WebInspector.FlameChartDataProvider.call(this);
this.reset();
this._model = model;
this._frameModel = frameModel;
this._font = "12px " + WebInspector.fontFamily();
this._linkifier = new WebInspector.Linkifier();
this._filters = [];
this.addFilter(WebInspector.TracingTimelineUIUtils.hiddenEventsFilter());
this.addFilter(new WebInspector.TracingTimelineModel.ExclusiveEventNameFilter([WebInspector.TracingTimelineModel.RecordType.Program]));
}
WebInspector.TimelineFlameChartDataProvider.InstantEventVisibleDurationMs = 0.01;
WebInspector.TimelineFlameChartDataProvider.JSFrameCoalesceThresholdMs = 1.1;
WebInspector.TimelineFlameChartDataProvider.prototype = {
/**
* @return {number}
*/
barHeight: function()
{
return 20;
},
/**
* @return {number}
*/
textBaseline: function()
{
return 6;
},
/**
* @return {number}
*/
textPadding: function()
{
return 5;
},
/**
* @param {number} entryIndex
* @return {string}
*/
entryFont: function(entryIndex)
{
return this._font;
},
/**
* @param {number} entryIndex
* @return {?string}
*/
entryTitle: function(entryIndex)
{
var event = this._entryEvents[entryIndex];
if (event) {
if (event.phase === WebInspector.TracingModel.Phase.AsyncStepInto || event.phase === WebInspector.TracingModel.Phase.AsyncStepPast)
return event.name + ":" + event.args["step"];
var name = WebInspector.TracingTimelineUIUtils.eventStyle(event).title;
// TODO(yurys): support event dividers
var details = WebInspector.TracingTimelineUIUtils.buildDetailsNodeForTraceEvent(event, this._linkifier);
if (event.name === WebInspector.TracingTimelineModel.RecordType.JSFrame && details)
return details.textContent;
return details ? WebInspector.UIString("%s (%s)", name, details.textContent) : name;
}
var title = this._entryIndexToTitle[entryIndex];
if (!title) {
title = WebInspector.UIString("Unexpected entryIndex %d", entryIndex);
console.error(title);
}
return title;
},
/**
* @param {number} startTime
* @param {number} endTime
* @return {?Array.<number>}
*/
dividerOffsets: function(startTime, endTime)
{
return null;
},
/**
* @override
* @param {number} index
* @return {string}
*/
markerColor: function(index)
{
var event = this._markerEvents[index];
return WebInspector.TracingTimelineUIUtils.markerEventColor(event);
},
/**
* @override
* @param {number} index
* @return {string}
*/
markerTitle: function(index)
{
var event = this._markerEvents[index];
return WebInspector.TracingTimelineUIUtils.eventTitle(event, this._model);
},
reset: function()
{
this._timelineData = null;
/** @type {!Array.<!WebInspector.TracingModel.Event>} */
this._entryEvents = [];
this._entryIndexToTitle = {};
this._markerEvents = [];
this._entryIndexToFrame = {};
this._asyncColorByCategory = {};
},
/**
* @return {!WebInspector.FlameChart.TimelineData}
*/
timelineData: function()
{
if (this._timelineData)
return this._timelineData;
this._timelineData = new WebInspector.FlameChart.TimelineData([], [], []);
this._minimumBoundary = this._model.minimumRecordTime();
this._timeSpan = this._model.isEmpty() ? 1000 : this._model.maximumRecordTime() - this._minimumBoundary;
this._currentLevel = 0;
this._appendFrameBars(this._frameModel.frames());
this._appendThreadTimelineData(WebInspector.UIString("Main Thread"), this._model.mainThreadEvents(), this._model.mainThreadAsyncEvents());
var threads = this._model.virtualThreads();
for (var i = 0; i < threads.length; i++)
this._appendThreadTimelineData(threads[i].name, threads[i].events, threads[i].asyncEvents);
return this._timelineData;
},
/**
* @param {string} threadTitle
* @param {!Array.<!WebInspector.TracingModel.Event>} syncEvents
* @param {!Array.<!Array.<!WebInspector.TracingModel.Event>>} asyncEvents
*/
_appendThreadTimelineData: function(threadTitle, syncEvents, asyncEvents)
{
var levelCount = this._appendAsyncEvents(threadTitle, asyncEvents);
if (WebInspector.experimentsSettings.timelineJSCPUProfile.isEnabled()) {
var jsFrameEvents = this._generateJSFrameEvents(syncEvents);
syncEvents = jsFrameEvents.mergeOrdered(syncEvents, WebInspector.TracingModel.Event.orderedCompareStartTime);
}
levelCount += this._appendSyncEvents(levelCount ? null : threadTitle, syncEvents);
if (levelCount)
++this._currentLevel;
},
/**
* @param {?string} headerName
* @param {!Array.<!WebInspector.TracingModel.Event>} events
* @return {boolean}
*/
_appendSyncEvents: function(headerName, events)
{
var openEvents = [];
var headerAppended = false;
var maxStackDepth = 0;
for (var i = 0; i < events.length; ++i) {
var e = events[i];
if (WebInspector.TracingTimelineUIUtils.isMarkerEvent(e)) {
this._markerEvents.push(e);
this._timelineData.markerTimestamps.push(e.startTime);
}
if (!e.endTime && e.phase !== WebInspector.TracingModel.Phase.Instant)
continue;
if (!this._isVisible(e))
continue;
while (openEvents.length && openEvents.peekLast().endTime <= e.startTime)
openEvents.pop();
if (!headerAppended && headerName) {
this._appendHeaderRecord(headerName, this._currentLevel++);
headerAppended = true;
}
this._appendEvent(e, this._currentLevel + openEvents.length);
maxStackDepth = Math.max(maxStackDepth, openEvents.length + 1);
if (e.endTime)
openEvents.push(e);
}
this._currentLevel += maxStackDepth;
return !!maxStackDepth;
},
/**
* @param {string} header
* @param {!Array.<!Array.<!WebInspector.TracingModel.Event>>} eventSteps
*/
_appendAsyncEvents: function(header, eventSteps)
{
var lastUsedTimeByLevel = [];
var headerAppended = false;
var maxStackDepth = 0;
for (var i = 0; i < eventSteps.length; ++i) {
var e = eventSteps[i][0];
if (!this._isVisible(e))
continue;
if (!headerAppended && header) {
this._appendHeaderRecord(header, this._currentLevel++);
headerAppended = true;
}
var level;
for (level = 0; level < lastUsedTimeByLevel.length && lastUsedTimeByLevel[level] > e.startTime; ++level) {}
this._appendAsyncEventSteps(eventSteps[i], this._currentLevel + level);
var lastStep = eventSteps[i].peekLast();
lastUsedTimeByLevel[level] = lastStep.phase === WebInspector.TracingModel.Phase.AsyncEnd ? lastStep.startTime : Infinity;
}
this._currentLevel += lastUsedTimeByLevel.length;
return lastUsedTimeByLevel.length;
},
/**
* @param {!Array.<!WebInspector.TimelineFrame>} frames
*/
_appendFrameBars: function(frames)
{
this._frameBarsLevel = this._currentLevel++;
for (var i = 0; i < frames.length; ++i)
this._appendFrame(frames[i]);
},
/**
* @param {!Array.<!WebInspector.TracingModel.Event>} events
* @return {!Array.<!WebInspector.TracingModel.Event>}
*/
_generateJSFrameEvents: function(events)
{
function equalFrames(frame1, frame2)
{
return frame1.scriptId === frame2.scriptId && frame1.functionName === frame2.functionName;
}
function eventEndTime(e)
{
return e.endTime || e.startTime;
}
function isJSInvocationEvent(e)
{
switch (e.name) {
case WebInspector.TracingTimelineModel.RecordType.FunctionCall:
case WebInspector.TracingTimelineModel.RecordType.EvaluateScript:
return true;
}
return false;
}
var jsFrameEvents = [];
var jsFramesStack = [];
var coalesceThresholdMs = WebInspector.TimelineFlameChartDataProvider.JSFrameCoalesceThresholdMs;
function onStartEvent(e)
{
extractStackTrace(e);
}
function onInstantEvent(e, top)
{
if (e.name === WebInspector.TracingTimelineModel.RecordType.JSSample && (!top || !isJSInvocationEvent(top)))
return;
extractStackTrace(e);
}
function onEndEvent(e)
{
if (isJSInvocationEvent(e))
jsFramesStack.length = 0;
}
function extractStackTrace(e)
{
if (!e.stackTrace)
return;
while (jsFramesStack.length && eventEndTime(jsFramesStack.peekLast()) + coalesceThresholdMs <= e.startTime)
jsFramesStack.pop();
var endTime = eventEndTime(e);
var numFrames = e.stackTrace.length;
var minFrames = Math.min(numFrames, jsFramesStack.length);
var j;
for (j = 0; j < minFrames; ++j) {
var newFrame = e.stackTrace[numFrames - 1 - j];
var oldFrame = jsFramesStack[j].args["data"];
if (!equalFrames(newFrame, oldFrame))
break;
jsFramesStack[j].setEndTime(Math.max(jsFramesStack[j].endTime, endTime));
}
jsFramesStack.length = j;
for (; j < numFrames; ++j) {
var frame = e.stackTrace[numFrames - 1 - j];
var jsFrameEvent = new WebInspector.TracingModel.Event(WebInspector.TracingModel.DevToolsMetadataEventCategory, WebInspector.TracingTimelineModel.RecordType.JSFrame,
WebInspector.TracingModel.Phase.Complete, e.startTime, e.thread);
jsFrameEvent.addArgs({ data: frame });
jsFrameEvent.setEndTime(endTime);
jsFramesStack.push(jsFrameEvent);
jsFrameEvents.push(jsFrameEvent);
}
}
var stack = [];
for (var i = 0; i < events.length; ++i) {
var e = events[i];
var top = stack.peekLast();
if (top && top.endTime <= e.startTime)
onEndEvent(stack.pop());
if (e.duration) {
onStartEvent(e);
stack.push(e);
} else {
onInstantEvent(e, stack.peekLast());
}
}
while (stack.length)
onEndEvent(stack.pop());
return jsFrameEvents;
},
/**
* @param {!WebInspector.TracingTimelineModel.Filter} filter
*/
addFilter: function(filter)
{
this._filters.push(filter);
},
/**
* @param {!WebInspector.TracingModel.Event} event
* @return {boolean}
*/
_isVisible: function(event)
{
return this._filters.every(function (filter) { return filter.accept(event); });
},
/**
* @return {number}
*/
minimumBoundary: function()
{
return this._minimumBoundary;
},
/**
* @return {number}
*/
totalTime: function()
{
return this._timeSpan;
},
/**
* @return {number}
*/
maxStackDepth: function()
{
return this._currentLevel;
},
/**
* @param {number} entryIndex
* @return {?Array.<!{title: string, text: string}>}
*/
prepareHighlightedEntryInfo: function(entryIndex)
{
return null;
},
/**
* @param {number} entryIndex
* @return {boolean}
*/
canJumpToEntry: function(entryIndex)
{
return false;
},
/**
* @param {number} entryIndex
* @return {string}
*/
entryColor: function(entryIndex)
{
var event = this._entryEvents[entryIndex];
if (!event)
return this._entryIndexToFrame[entryIndex] ? "white" : "#555";
if (event.name === WebInspector.TracingTimelineModel.RecordType.JSFrame)
return WebInspector.TimelineFlameChartDataProvider.jsFrameColorGenerator().colorForID(event.args["data"]["functionName"]);
var style = WebInspector.TracingTimelineUIUtils.eventStyle(event);
if (event.phase === WebInspector.TracingModel.Phase.AsyncBegin || event.phase === WebInspector.TracingModel.Phase.AsyncStepInto || event.phase === WebInspector.TracingModel.Phase.AsyncStepPast) {
var color = this._asyncColorByCategory[style.category.name];
if (color)
return color;
var parsedColor = WebInspector.Color.parse(style.category.fillColorStop1);
color = parsedColor.setAlpha(0.7).toString(WebInspector.Color.Format.RGBA) || "";
this._asyncColorByCategory[style.category.name] = color;
return color;
}
return style.category.fillColorStop1;
},
/**
* @param {number} entryIndex
* @param {!CanvasRenderingContext2D} context
* @param {?string} text
* @param {number} barX
* @param {number} barY
* @param {number} barWidth
* @param {number} barHeight
* @param {function(number):number} offsetToPosition
* @return {boolean}
*/
decorateEntry: function(entryIndex, context, text, barX, barY, barWidth, barHeight, offsetToPosition)
{
var frame = this._entryIndexToFrame[entryIndex];
if (frame) {
context.save();
context.translate(0.5, 0.5);
context.beginPath();
context.moveTo(barX, barY);
context.lineTo(barX, context.canvas.height);
context.strokeStyle = "rgba(100, 100, 100, 0.4)";
context.setLineDash([5]);
context.stroke();
context.setLineDash([]);
var padding = 4 * window.devicePixelRatio;
barX += padding;
barWidth -= 2 * padding;
barY += padding;
barHeight -= 2 * padding;
var cornerRadis = 3;
var radiusY = cornerRadis;
var radiusX = Math.min(cornerRadis, barWidth / 2);
context.beginPath();
context.moveTo(barX + radiusX, barY);
context.lineTo(barX + barWidth - radiusX, barY);
context.quadraticCurveTo(barX + barWidth, barY, barX + barWidth, barY + radiusY);
context.lineTo(barX + barWidth, barY + barHeight - radiusY);
context.quadraticCurveTo(barX + barWidth, barY + barHeight, barX + barWidth - radiusX, barY + barHeight);
context.lineTo(barX + radiusX, barY + barHeight);
context.quadraticCurveTo(barX, barY + barHeight, barX, barY + barHeight - radiusY);
context.lineTo(barX, barY + radiusY);
context.quadraticCurveTo(barX, barY, barX + radiusX, barY);
context.closePath();
context.fillStyle = "rgba(200, 200, 200, 0.8)";
context.fill();
context.strokeStyle = "rgba(150, 150, 150, 0.8)";
context.stroke();
var frameDurationText = Number.millisToString(frame.duration, true);
var textWidth = context.measureText(frameDurationText).width;
if (barWidth > textWidth) {
context.fillStyle = "#555";
context.fillText(frameDurationText, barX + ((barWidth - textWidth) >> 1), barY + barHeight - 2);
}
context.restore();
return true;
}
if (barWidth < 5)
return false;
// Paint text using white color on dark background.
if (text) {
context.save();
context.fillStyle = "white";
context.shadowColor = "rgba(0, 0, 0, 0.1)";
context.shadowOffsetX = 1;
context.shadowOffsetY = 1;
context.font = this._font;
context.fillText(text, barX + this.textPadding(), barY + barHeight - this.textBaseline());
context.restore();
}
var event = this._entryEvents[entryIndex];
if (event && event.warning) {
context.save();
context.rect(barX, barY, barWidth, this.barHeight());
context.clip();
context.beginPath();
context.fillStyle = "red";
context.moveTo(barX + barWidth - 15, barY + 1);
context.lineTo(barX + barWidth - 1, barY + 1);
context.lineTo(barX + barWidth - 1, barY + 15);
context.fill();
context.restore();
}
return true;
},
/**
* @param {number} entryIndex
* @return {boolean}
*/
forceDecoration: function(entryIndex)
{
var event = this._entryEvents[entryIndex];
if (!event)
return !!this._entryIndexToFrame[entryIndex];
return !!event.warning;
},
/**
* @param {number} entryIndex
* @return {?{startTime: number, endTime: number}}
*/
highlightTimeRange: function(entryIndex)
{
var startTime = this._timelineData.entryStartTimes[entryIndex];
if (!startTime)
return null;
return {
startTime: startTime,
endTime: startTime + this._timelineData.entryTotalTimes[entryIndex]
}
},
/**
* @return {number}
*/
paddingLeft: function()
{
return 0;
},
/**
* @param {number} entryIndex
* @return {string}
*/
textColor: function(entryIndex)
{
return "white";
},
/**
* @param {string} title
* @param {number} level
*/
_appendHeaderRecord: function(title, level)
{
var index = this._entryEvents.length;
this._entryIndexToTitle[index] = title;
this._entryEvents.push(null);
this._timelineData.entryLevels[index] = level;
this._timelineData.entryTotalTimes[index] = this._timeSpan;
this._timelineData.entryStartTimes[index] = this._minimumBoundary;
},
/**
* @param {!WebInspector.TracingModel.Event} event
* @param {number} level
*/
_appendEvent: function(event, level)
{
var index = this._entryEvents.length;
this._entryEvents.push(event);
this._timelineData.entryLevels[index] = level;
this._timelineData.entryTotalTimes[index] = event.duration || WebInspector.TimelineFlameChartDataProvider.InstantEventVisibleDurationMs;
this._timelineData.entryStartTimes[index] = event.startTime;
},
/**
* @param {!Array.<!WebInspector.TracingModel.Event>} steps
* @param {number} level
*/
_appendAsyncEventSteps: function(steps, level)
{
// If we have past steps, put the end event for each range rather than start one.
var eventOffset = steps[1].phase === WebInspector.TracingModel.Phase.AsyncStepPast ? 1 : 0;
for (var i = 0; i < steps.length - 1; ++i) {
var index = this._entryEvents.length;
this._entryEvents.push(steps[i + eventOffset]);
var startTime = steps[i].startTime;
this._timelineData.entryLevels[index] = level;
this._timelineData.entryTotalTimes[index] = steps[i + 1].startTime - startTime;
this._timelineData.entryStartTimes[index] = startTime;
}
},
/**
* @param {!WebInspector.TimelineFrame} frame
*/
_appendFrame: function(frame)
{
var index = this._entryEvents.length;
this._entryEvents.push(null);
this._entryIndexToFrame[index] = frame;
this._entryIndexToTitle[index] = Number.millisToString(frame.duration, true);
this._timelineData.entryLevels[index] = this._frameBarsLevel;
this._timelineData.entryTotalTimes[index] = frame.duration;
this._timelineData.entryStartTimes[index] = frame.startTime;
},
/**
* @param {number} entryIndex
* @return {?WebInspector.TimelineSelection}
*/
createSelection: function(entryIndex)
{
var event = this._entryEvents[entryIndex];
if (event) {
this._lastSelection = new WebInspector.TimelineFlameChart.Selection(WebInspector.TimelineSelection.fromTraceEvent(event), entryIndex);
return this._lastSelection.timelineSelection;
}
var frame = this._entryIndexToFrame[entryIndex];
if (frame) {
this._lastSelection = new WebInspector.TimelineFlameChart.Selection(WebInspector.TimelineSelection.fromFrame(frame), entryIndex);
return this._lastSelection.timelineSelection;
}
return null;
},
/**
* @param {?WebInspector.TimelineSelection} selection
* @return {number}
*/
entryIndexForSelection: function(selection)
{
if (!selection)
return -1;
if (this._lastSelection && this._lastSelection.timelineSelection.object() === selection.object())
return this._lastSelection.entryIndex;
switch (selection.type()) {
case WebInspector.TimelineSelection.Type.TraceEvent:
var event = /** @type{!WebInspector.TracingModel.Event} */ (selection.object());
var entryEvents = this._entryEvents;
for (var entryIndex = 0; entryIndex < entryEvents.length; ++entryIndex) {
if (entryEvents[entryIndex] === event) {
this._lastSelection = new WebInspector.TimelineFlameChart.Selection(WebInspector.TimelineSelection.fromTraceEvent(event), entryIndex);
return entryIndex;
}
}
break;
case WebInspector.TimelineSelection.Type.Frame:
var frame = /** @type {!WebInspector.TimelineFrame} */ (selection.object());
for (var frameIndex in this._entryIndexToFrame) {
if (this._entryIndexToFrame[frameIndex] === frame) {
this._lastSelection = new WebInspector.TimelineFlameChart.Selection(WebInspector.TimelineSelection.fromFrame(frame), Number(frameIndex));
return Number(frameIndex);
}
}
break;
}
return -1;
}
}
/**
* @return {!WebInspector.FlameChart.ColorGenerator}
*/
WebInspector.TimelineFlameChartDataProvider.jsFrameColorGenerator = function()
{
if (!WebInspector.TimelineFlameChartDataProvider._jsFrameColorGenerator) {
var hueSpace = { min: 30, max: 55, count: 5 };
var satSpace = { min: 70, max: 100, count: 6 };
var colorGenerator = new WebInspector.FlameChart.ColorGenerator(hueSpace, satSpace, 50);
colorGenerator.setColorForID("(idle)", "hsl(0, 0%, 60%)");
colorGenerator.setColorForID("(program)", "hsl(0, 0%, 60%)");
colorGenerator.setColorForID("(garbage collector)", "hsl(0, 0%, 60%)");
WebInspector.TimelineFlameChartDataProvider._jsFrameColorGenerator = colorGenerator;
}
return WebInspector.TimelineFlameChartDataProvider._jsFrameColorGenerator;
}
/**
* @constructor
* @extends {WebInspector.VBox}
* @implements {WebInspector.TimelineModeView}
* @implements {WebInspector.FlameChartDelegate}
* @param {!WebInspector.TimelineModeViewDelegate} delegate
* @param {!WebInspector.TracingTimelineModel} tracingModel
* @param {!WebInspector.TimelineFrameModelBase} frameModel
*/
WebInspector.TimelineFlameChart = function(delegate, tracingModel, frameModel)
{
WebInspector.VBox.call(this);
this.element.classList.add("timeline-flamechart");
this.registerRequiredCSS("flameChart.css");
this._delegate = delegate;
this._model = tracingModel;
this._dataProvider = new WebInspector.TimelineFlameChartDataProvider(tracingModel, frameModel)
this._mainView = new WebInspector.FlameChart(this._dataProvider, this, true);
this._mainView.show(this.element);
this._model.addEventListener(WebInspector.TimelineModel.Events.RecordingStarted, this._onRecordingStarted, this);
this._mainView.addEventListener(WebInspector.FlameChart.Events.EntrySelected, this._onEntrySelected, this);
}
WebInspector.TimelineFlameChart.prototype = {
dispose: function()
{
this._model.removeEventListener(WebInspector.TimelineModel.Events.RecordingStarted, this._onRecordingStarted, this);
this._mainView.removeEventListener(WebInspector.FlameChart.Events.EntrySelected, this._onEntrySelected, this);
},
/**
* @param {number} windowStartTime
* @param {number} windowEndTime
*/
requestWindowTimes: function(windowStartTime, windowEndTime)
{
this._delegate.requestWindowTimes(windowStartTime, windowEndTime);
},
/**
* @param {?RegExp} textFilter
*/
refreshRecords: function(textFilter)
{
this._dataProvider.reset();
this._mainView.scheduleUpdate();
},
wasShown: function()
{
this._mainView.scheduleUpdate();
},
/**
* @return {!WebInspector.View}
*/
view: function()
{
return this;
},
reset: function()
{
this._automaticallySizeWindow = true;
this._dataProvider.reset();
this._mainView.reset();
this._mainView.setWindowTimes(0, Infinity);
},
_onRecordingStarted: function()
{
this._automaticallySizeWindow = true;
this._mainView.reset();
},
/**
* @param {!WebInspector.TimelineModel.Record} record
*/
addRecord: function(record)
{
this._dataProvider.reset();
if (this._automaticallySizeWindow) {
var minimumRecordTime = this._model.minimumRecordTime();
if (record.startTime() > (minimumRecordTime + 1000)) {
this._automaticallySizeWindow = false;
this._delegate.requestWindowTimes(minimumRecordTime, minimumRecordTime + 1000);
}
this._mainView.scheduleUpdate();
} else {
if (!this._pendingUpdateTimer)
this._pendingUpdateTimer = window.setTimeout(this._updateOnAddRecord.bind(this), 300);
}
},
_updateOnAddRecord: function()
{
delete this._pendingUpdateTimer;
this._mainView.scheduleUpdate();
},
/**
* @param {number} startTime
* @param {number} endTime
*/
setWindowTimes: function(startTime, endTime)
{
this._mainView.setWindowTimes(startTime, endTime);
this._delegate.select(null);
},
/**
* @param {number} width
*/
setSidebarSize: function(width)
{
},
/**
* @param {?WebInspector.TimelineModel.Record} record
* @param {string=} regex
* @param {boolean=} selectRecord
*/
highlightSearchResult: function(record, regex, selectRecord)
{
},
/**
* @param {?WebInspector.TimelineSelection} selection
*/
setSelection: function(selection)
{
var index = this._dataProvider.entryIndexForSelection(selection);
this._mainView.setSelectedEntry(index);
},
/**
* @param {!WebInspector.Event} event
*/
_onEntrySelected: function(event)
{
var entryIndex = /** @type{number} */ (event.data);
var timelineSelection = this._dataProvider.createSelection(entryIndex);
if (timelineSelection)
this._delegate.select(timelineSelection);
},
__proto__: WebInspector.VBox.prototype
}
/**
* @constructor
* @param {!WebInspector.TimelineSelection} selection
* @param {number} entryIndex
*/
WebInspector.TimelineFlameChart.Selection = function(selection, entryIndex)
{
this.timelineSelection = selection;
this.entryIndex = entryIndex;
}