WebInspector.TimelinePanel = function()
{
WebInspector.Panel.call(this, "timeline");
this.registerRequiredCSS("timelinePanel.css");
this._model = new WebInspector.TimelineModel();
this._presentationModel = new WebInspector.TimelinePresentationModel();
this._overviewPane = new WebInspector.TimelineOverviewPane(this._model);
this._overviewPane.addEventListener(WebInspector.TimelineOverviewPane.Events.WindowChanged, this._scheduleRefresh.bind(this, false));
this._overviewPane.addEventListener(WebInspector.TimelineOverviewPane.Events.ModeChanged, this._timelinesOverviewModeChanged, this);
this._overviewPane.show(this.element);
this.element.addEventListener("contextmenu", this._contextMenu.bind(this), true);
this.element.tabIndex = 0;
this._sidebarBackgroundElement = document.createElement("div");
this._sidebarBackgroundElement.className = "sidebar split-view-sidebar-left timeline-sidebar-background";
this.element.appendChild(this._sidebarBackgroundElement);
this.createSplitViewWithSidebarTree();
this._containerElement = this.splitView.element;
this._containerElement.id = "timeline-container";
this._containerElement.addEventListener("scroll", this._onScroll.bind(this), false);
this._timelineMemorySplitter = this.element.createChild("div");
this._timelineMemorySplitter.id = "timeline-memory-splitter";
this._timelineMemorySplitter.addEventListener("mousedown", this._startSplitterDragging.bind(this), false);
this._timelineMemorySplitter.addStyleClass("hidden");
this._memoryStatistics = new WebInspector.MemoryStatistics(this, this.splitView.preferredSidebarWidth());
WebInspector.settings.memoryCounterGraphsHeight = WebInspector.settings.createSetting("memoryCounterGraphsHeight", 150);
var itemsTreeElement = new WebInspector.SidebarSectionTreeElement(WebInspector.UIString("RECORDS"), {}, true);
this.sidebarTree.appendChild(itemsTreeElement);
this._sidebarListElement = document.createElement("div");
this.sidebarElement.appendChild(this._sidebarListElement);
this._containerContentElement = this.splitView.mainElement;
this._containerContentElement.id = "resources-container-content";
this._timelineGrid = new WebInspector.TimelineGrid();
this._itemsGraphsElement = this._timelineGrid.itemsGraphsElement;
this._itemsGraphsElement.id = "timeline-graphs";
this._containerContentElement.appendChild(this._timelineGrid.element);
this._memoryStatistics.setMainTimelineGrid(this._timelineGrid);
this._topGapElement = document.createElement("div");
this._topGapElement.className = "timeline-gap";
this._itemsGraphsElement.appendChild(this._topGapElement);
this._graphRowsElement = document.createElement("div");
this._itemsGraphsElement.appendChild(this._graphRowsElement);
this._bottomGapElement = document.createElement("div");
this._bottomGapElement.className = "timeline-gap";
this._itemsGraphsElement.appendChild(this._bottomGapElement);
this._expandElements = document.createElement("div");
this._expandElements.id = "orphan-expand-elements";
this._itemsGraphsElement.appendChild(this._expandElements);
this._calculator = new WebInspector.TimelineCalculator(this._model);
var shortRecordThresholdTitle = Number.secondsToString(WebInspector.TimelinePresentationModel.shortRecordThreshold);
this._showShortRecordsTitleText = WebInspector.UIString("Show the records that are shorter than %s", shortRecordThresholdTitle);
this._hideShortRecordsTitleText = WebInspector.UIString("Hide the records that are shorter than %s", shortRecordThresholdTitle);
this._createStatusbarButtons();
this._verticalOverview = false;
this._boundariesAreValid = true;
this._scrollTop = 0;
this._popoverHelper = new WebInspector.PopoverHelper(this._containerElement, this._getPopoverAnchor.bind(this), this._showPopover.bind(this));
this._containerElement.addEventListener("mousemove", this._mouseMove.bind(this), false);
this._containerElement.addEventListener("mouseout", this._mouseOut.bind(this), false);
this.toggleFilterButton.toggled = true;
this._showShortEvents = this.toggleFilterButton.toggled;
this._overviewPane.setShowShortEvents(this._showShortEvents);
this._timeStampRecords = [];
this._expandOffset = 15;
this._createFileSelector();
this._model.addEventListener(WebInspector.TimelineModel.Events.RecordAdded, this._onTimelineEventRecorded, this);
this._model.addEventListener(WebInspector.TimelineModel.Events.RecordsCleared, this._onRecordsCleared, this);
this._registerShortcuts();
this._allRecordsCount = 0;
this._presentationModel.addFilter(this._overviewPane);
this._presentationModel.addFilter(new WebInspector.TimelineCategoryFilter());
this._presentationModel.addFilter(new WebInspector.TimelineIsLongFilter(this));
}
WebInspector.TimelinePanel.rowHeight = 18;
WebInspector.TimelinePanel.prototype = {
_startSplitterDragging: function(event)
{
this._dragOffset = this._timelineMemorySplitter.offsetTop + 2 - event.pageY;
WebInspector.elementDragStart(this._timelineMemorySplitter, this._splitterDragging.bind(this), this._endSplitterDragging.bind(this), event, "ns-resize");
},
_splitterDragging: function(event)
{
var top = event.pageY + this._dragOffset
this._setSplitterPosition(top);
event.preventDefault();
this._refresh();
},
_endSplitterDragging: function(event)
{
delete this._dragOffset;
WebInspector.elementDragEnd(event);
this._memoryStatistics.show();
WebInspector.settings.memoryCounterGraphsHeight.set(this.splitView.element.offsetHeight);
},
_setSplitterPosition: function(top)
{
const overviewHeight = 90;
const sectionMinHeight = 100;
top = Number.constrain(top, overviewHeight + sectionMinHeight, this.element.offsetHeight - sectionMinHeight);
this.splitView.element.style.height = (top - overviewHeight) + "px";
this._timelineMemorySplitter.style.top = (top - 2) + "px";
this._memoryStatistics.setTopPosition(top);
},
get calculator()
{
return this._calculator;
},
get toolbarItemLabel()
{
return WebInspector.UIString("Timeline");
},
get statusBarItems()
{
var statusBarItems = [ this.toggleFilterButton.element, this.toggleTimelineButton.element, this.clearButton.element, this.garbageCollectButton.element, this._glueParentButton.element, this.statusBarFilters ];
return statusBarItems;
},
defaultFocusedElement: function()
{
return this.element;
},
_createStatusbarButtons: function()
{
this.toggleTimelineButton = new WebInspector.StatusBarButton(WebInspector.UIString("Record"), "record-profile-status-bar-item");
this.toggleTimelineButton.addEventListener("click", this._toggleTimelineButtonClicked, this);
this.clearButton = new WebInspector.StatusBarButton(WebInspector.UIString("Clear"), "clear-status-bar-item");
this.clearButton.addEventListener("click", this._clearPanel, this);
this.toggleFilterButton = new WebInspector.StatusBarButton(this._hideShortRecordsTitleText, "timeline-filter-status-bar-item");
this.toggleFilterButton.addEventListener("click", this._toggleFilterButtonClicked, this);
this.garbageCollectButton = new WebInspector.StatusBarButton(WebInspector.UIString("Collect Garbage"), "garbage-collect-status-bar-item");
this.garbageCollectButton.addEventListener("click", this._garbageCollectButtonClicked, this);
this.recordsCounter = document.createElement("span");
this.recordsCounter.className = "timeline-records-counter";
this._glueParentButton = new WebInspector.StatusBarButton(WebInspector.UIString("Glue asynchronous events to causes"), "glue-async-status-bar-item");
this._glueParentButton.toggled = true;
this._presentationModel.setGlueRecords(true);
this._glueParentButton.addEventListener("click", this._glueParentButtonClicked, this);
this.statusBarFilters = document.createElement("div");
this.statusBarFilters.className = "status-bar-items";
var categories = WebInspector.TimelinePresentationModel.categories();
for (var categoryName in categories) {
var category = categories[categoryName];
this.statusBarFilters.appendChild(this._createTimelineCategoryStatusBarCheckbox(category, this._onCategoryCheckboxClicked.bind(this, category)));
}
},
_createTimelineCategoryStatusBarCheckbox: function(category, onCheckboxClicked)
{
var labelContainer = document.createElement("div");
labelContainer.addStyleClass("timeline-category-statusbar-item");
labelContainer.addStyleClass("timeline-category-" + category.name);
labelContainer.addStyleClass("status-bar-item");
var label = document.createElement("label");
var checkElement = document.createElement("input");
checkElement.type = "checkbox";
checkElement.className = "timeline-category-checkbox";
checkElement.checked = true;
checkElement.addEventListener("click", onCheckboxClicked, false);
label.appendChild(checkElement);
var typeElement = document.createElement("span");
typeElement.className = "type";
typeElement.textContent = category.title;
label.appendChild(typeElement);
labelContainer.appendChild(label);
return labelContainer;
},
_onCategoryCheckboxClicked: function(category, event)
{
category.hidden = !event.target.checked;
this._scheduleRefresh(true);
},
_registerShortcuts: function()
{
var shortcut = WebInspector.KeyboardShortcut;
var modifiers = shortcut.Modifiers;
var section = WebInspector.shortcutsScreen.section(WebInspector.UIString("Timeline Panel"));
this._shortcuts[shortcut.makeKey("e", modifiers.CtrlOrMeta)] = this._toggleTimelineButtonClicked.bind(this);
section.addKey(shortcut.shortcutToString("e", modifiers.CtrlOrMeta), WebInspector.UIString("Start/stop recording"));
if (InspectorFrontendHost.canSave()) {
this._shortcuts[shortcut.makeKey("s", modifiers.CtrlOrMeta)] = this._saveToFile.bind(this);
section.addKey(shortcut.shortcutToString("s", modifiers.CtrlOrMeta), WebInspector.UIString("Save timeline data"));
}
this._shortcuts[shortcut.makeKey("o", modifiers.CtrlOrMeta)] = this._fileSelectorElement.click.bind(this._fileSelectorElement);
section.addKey(shortcut.shortcutToString("o", modifiers.CtrlOrMeta), WebInspector.UIString("Load timeline data"));
},
_createFileSelector: function()
{
if (this._fileSelectorElement)
this.element.removeChild(this._fileSelectorElement);
var fileSelectorElement = document.createElement("input");
fileSelectorElement.type = "file";
fileSelectorElement.style.zIndex = -1;
fileSelectorElement.style.position = "absolute";
fileSelectorElement.onchange = this._loadFromFile.bind(this);
this.element.appendChild(fileSelectorElement);
this._fileSelectorElement = fileSelectorElement;
},
_contextMenu: function(event)
{
var contextMenu = new WebInspector.ContextMenu();
if (InspectorFrontendHost.canSave())
contextMenu.appendItem(WebInspector.UIString("Save Timeline data\u2026"), this._saveToFile.bind(this));
contextMenu.appendItem(WebInspector.UIString("Load Timeline data\u2026"), this._fileSelectorElement.click.bind(this._fileSelectorElement));
contextMenu.show(event);
},
_saveToFile: function()
{
this._model.saveToFile();
},
_loadFromFile: function()
{
if (this.toggleTimelineButton.toggled) {
this.toggleTimelineButton.toggled = false;
this._model.stopRecord();
}
this._model.loadFromFile(this._fileSelectorElement.files[0]);
this._createFileSelector();
},
_rootRecord: function()
{
return this._presentationModel.rootRecord();
},
_updateRecordsCounter: function(recordsInWindowCount)
{
this.recordsCounter.textContent = WebInspector.UIString("%d of %d captured records are visible", recordsInWindowCount, this._allRecordsCount);
},
_updateEventDividers: function()
{
this._timelineGrid.removeEventDividers();
var clientWidth = this._graphRowsElement.offsetWidth;
var dividers = [];
for (var i = 0; i < this._timeStampRecords.length; ++i) {
var record = this._timeStampRecords[i];
var positions = this._calculator.computeBarGraphWindowPosition(record);
var dividerPosition = Math.round(positions.left);
if (dividerPosition < 0 || dividerPosition >= clientWidth || dividers[dividerPosition])
continue;
var divider = WebInspector.TimelinePresentationModel.createEventDivider(record.type, record.title);
divider.style.left = dividerPosition + "px";
dividers[dividerPosition] = divider;
}
this._timelineGrid.addEventDividers(dividers);
},
_shouldShowFrames: function()
{
return this._verticalOverview && this._presentationModel.frames().length > 0 && this.calculator.boundarySpan < 1.0;
},
_updateFrames: function()
{
var frames = this._presentationModel.frames();
var clientWidth = this._graphRowsElement.offsetWidth;
if (this._frameContainer)
this._frameContainer.removeChildren();
else {
this._frameContainer = document.createElement("div");
this._frameContainer.addStyleClass("fill");
this._frameContainer.addStyleClass("timeline-frame-container");
this._frameContainer.addEventListener("dblclick", this._onFrameDoubleClicked.bind(this), false);
}
var dividers = [ this._frameContainer ];
for (var i = 0; i < frames.length; ++i) {
var frame = frames[i];
var frameStart = this._calculator.computePosition(frame.startTime);
var frameEnd = this._calculator.computePosition(frame.endTime);
if (frameEnd <= 0 || frameStart >= clientWidth)
continue;
var frameStrip = document.createElement("div");
frameStrip.className = "timeline-frame-strip";
var actualStart = Math.max(frameStart, 0);
var width = frameEnd - actualStart;
frameStrip.style.left = actualStart + "px";
frameStrip.style.width = width + "px";
frameStrip._frame = frame;
const minWidthForFrameInfo = 60;
if (width > minWidthForFrameInfo)
frameStrip.textContent = Number.secondsToString(frame.endTime - frame.startTime, true);
this._frameContainer.appendChild(frameStrip);
if (actualStart > 0) {
var frameMarker = WebInspector.TimelinePresentationModel.createEventDivider(WebInspector.TimelineModel.RecordType.BeginFrame);
frameMarker.style.left = frameStart + "px";
dividers.push(frameMarker);
}
}
this._timelineGrid.addEventDividers(dividers);
},
_onFrameDoubleClicked: function(event)
{
var frameBar = event.target.enclosingNodeOrSelfWithClass("timeline-frame-strip");
if (!frameBar)
return;
this._overviewPane.zoomToFrame(frameBar._frame);
},
_timelinesOverviewModeChanged: function(event)
{
var shouldShowMemory = event.data === WebInspector.TimelineOverviewPane.Mode.Memory;
var verticalOverview = event.data === WebInspector.TimelineOverviewPane.Mode.EventsVertical;
if (verticalOverview !== this._verticalOverview) {
this._verticalOverview = verticalOverview;
this._glueParentButton.disabled = verticalOverview;
this._presentationModel.setGlueRecords(this._glueParentButton.toggled && !verticalOverview);
this._repopulateRecords();
if (verticalOverview) {
this.element.addStyleClass("timeline-vertical-overview");
this._frameController = new WebInspector.TimelineFrameController(this._model, this._overviewPane, this._presentationModel);
} else {
this._frameController.dispose();
this._frameController = null;
this.element.removeStyleClass("timeline-vertical-overview");
}
}
if (shouldShowMemory === this._memoryStatistics.visible())
return;
if (!shouldShowMemory) {
this._timelineMemorySplitter.addStyleClass("hidden");
this._memoryStatistics.hide();
this.splitView.element.style.height = "auto";
this.splitView.element.style.bottom = "0";
} else {
this._timelineMemorySplitter.removeStyleClass("hidden");
this._memoryStatistics.show();
this.splitView.element.style.bottom = "auto";
this._setSplitterPosition(WebInspector.settings.memoryCounterGraphsHeight.get());
}
this._refresh();
},
_toggleTimelineButtonClicked: function()
{
if (this.toggleTimelineButton.toggled)
this._model.stopRecord();
else {
this._model.startRecord();
WebInspector.userMetrics.TimelineStarted.record();
}
this.toggleTimelineButton.toggled = !this.toggleTimelineButton.toggled;
},
_toggleFilterButtonClicked: function()
{
this.toggleFilterButton.toggled = !this.toggleFilterButton.toggled;
this._showShortEvents = this.toggleFilterButton.toggled;
this._overviewPane.setShowShortEvents(this._showShortEvents);
this.toggleFilterButton.element.title = this._showShortEvents ? this._hideShortRecordsTitleText : this._showShortRecordsTitleText;
this._scheduleRefresh(true);
},
_garbageCollectButtonClicked: function()
{
ProfilerAgent.collectGarbage();
},
_glueParentButtonClicked: function()
{
this._glueParentButton.toggled = !this._glueParentButton.toggled;
this._presentationModel.setGlueRecords(this._glueParentButton.toggled);
this._repopulateRecords();
},
_repopulateRecords: function()
{
this._resetPanel();
var records = this._model.records;
for (var i = 0; i < records.length; ++i)
this._innerAddRecordToTimeline(records[i], this._rootRecord());
this._scheduleRefresh(false);
},
_onTimelineEventRecorded: function(event)
{
this._innerAddRecordToTimeline(event.data, this._rootRecord());
this._scheduleRefresh(false);
if (event.data["counters"])
this._memoryStatistics.addTimlineEvent(event);
},
_innerAddRecordToTimeline: function(record, parentRecord)
{
var formattedRecord = this._presentationModel.addRecord(record, parentRecord);
++this._allRecordsCount;
var recordTypes = WebInspector.TimelineModel.RecordType;
var timeStampRecords = this._timeStampRecords;
function addTimestampRecords(record)
{
if (WebInspector.TimelinePresentationModel.isEventDivider(record))
timeStampRecords.push(record);
}
WebInspector.TimelinePresentationModel.forAllRecords([ formattedRecord ], addTimestampRecords);
},
sidebarResized: function(event)
{
var width = event.data;
this._sidebarBackgroundElement.style.width = width + "px";
this._scheduleRefresh(false);
this._overviewPane.sidebarResized(width);
this.statusBarFilters.style.left = Math.max((this.statusBarItems.length + 2) * 31, width) + "px";
this._memoryStatistics.setSidebarWidth(width);
},
onResize: function()
{
this._closeRecordDetails();
this._scheduleRefresh(false);
},
_clearPanel: function()
{
this._model.reset();
},
_onRecordsCleared: function()
{
this._resetPanel();
this._refresh();
},
_resetPanel: function()
{
this._presentationModel.reset();
this._timeStampRecords = [];
this._boundariesAreValid = false;
this._adjustScrollPosition(0);
this._closeRecordDetails();
this._allRecordsCount = 0;
this._memoryStatistics.reset();
},
elementsToRestoreScrollPositionsFor: function()
{
return [this._containerElement];
},
wasShown: function()
{
WebInspector.Panel.prototype.wasShown.call(this);
this._refresh();
WebInspector.drawer.currentPanelCounters = this.recordsCounter;
},
willHide: function()
{
this._closeRecordDetails();
WebInspector.drawer.currentPanelCounters = null;
WebInspector.Panel.prototype.willHide.call(this);
},
_onScroll: function(event)
{
this._closeRecordDetails();
var scrollTop = this._containerElement.scrollTop;
var dividersTop = Math.max(0, scrollTop);
this._timelineGrid.setScrollAndDividerTop(scrollTop, dividersTop);
this._scheduleRefresh(true);
},
_scheduleRefresh: function(preserveBoundaries)
{
this._closeRecordDetails();
this._boundariesAreValid &= preserveBoundaries;
if (!this.isShowing())
return;
if (preserveBoundaries)
this._refresh();
else {
if (!this._refreshTimeout)
this._refreshTimeout = setTimeout(this._refresh.bind(this), 100);
}
},
_refresh: function()
{
if (this._refreshTimeout) {
clearTimeout(this._refreshTimeout);
delete this._refreshTimeout;
}
this._calculator.setWindow(this._overviewPane.windowStartTime(), this._overviewPane.windowEndTime());
this._calculator.setDisplayWindow(!this._overviewPane.windowLeft() ? this._expandOffset : 0, this._graphRowsElement.clientWidth);
var recordsInWindowCount = this._refreshRecords();
this._updateRecordsCounter(recordsInWindowCount);
if(!this._boundariesAreValid) {
this._updateEventDividers();
if (this._shouldShowFrames()) {
this._timelineGrid.removeDividers();
this._updateFrames();
} else {
this._timelineGrid.updateDividers(this._calculator);
}
}
if (this._memoryStatistics.visible())
this._memoryStatistics.refresh();
this._boundariesAreValid = true;
},
revealRecordAt: function(time)
{
if (this._verticalOverview)
return;
var recordsInWindow = this._presentationModel.filteredRecords();
var recordToReveal;
for (var i = 0; i < recordsInWindow.length; ++i) {
var record = recordsInWindow[i];
if (record.containsTime(time)) {
recordToReveal = record;
break;
}
if (!recordToReveal || record.endTime < time && recordToReveal.endTime < record.endTime)
recordToReveal = record;
}
if (!recordToReveal) {
this._containerElement.scrollTop = 0;
return;
}
for (var parent = recordToReveal.parent; parent !== this._rootRecord(); parent = parent.parent)
parent.collapsed = false;
var index = recordsInWindow.indexOf(recordToReveal);
this._containerElement.scrollTop = index * WebInspector.TimelinePanel.rowHeight;
},
_refreshRecords: function()
{
var recordsInWindow = this._presentationModel.filteredRecords();
this._scrollTop = this._containerElement.scrollTop;
var visibleTop = this._scrollTop;
var visibleBottom = visibleTop + this._containerElement.clientHeight;
const rowHeight = WebInspector.TimelinePanel.rowHeight;
var startIndex = Math.max(0, Math.min(Math.floor(visibleTop / rowHeight) - 1, recordsInWindow.length - 1));
var endIndex = Math.min(recordsInWindow.length, Math.ceil(visibleBottom / rowHeight));
const top = (startIndex * rowHeight) + "px";
this._topGapElement.style.height = top;
this.sidebarElement.style.top = top;
this.splitView.sidebarResizerElement.style.top = top;
this._bottomGapElement.style.height = (recordsInWindow.length - endIndex) * rowHeight + "px";
var listRowElement = this._sidebarListElement.firstChild;
var width = this._graphRowsElement.offsetWidth;
this._itemsGraphsElement.removeChild(this._graphRowsElement);
var graphRowElement = this._graphRowsElement.firstChild;
var scheduleRefreshCallback = this._scheduleRefresh.bind(this, true);
this._itemsGraphsElement.removeChild(this._expandElements);
this._expandElements.removeChildren();
for (var i = 0; i < endIndex; ++i) {
var record = recordsInWindow[i];
var isEven = !(i % 2);
if (i < startIndex) {
var lastChildIndex = i + record.visibleChildrenCount;
if (lastChildIndex >= startIndex && lastChildIndex < endIndex) {
var expandElement = new WebInspector.TimelineExpandableElement(this._expandElements);
var positions = this._calculator.computeBarGraphWindowPosition(record);
expandElement._update(record, i, positions.left - this._expandOffset, positions.width);
}
} else {
if (!listRowElement) {
listRowElement = new WebInspector.TimelineRecordListRow().element;
this._sidebarListElement.appendChild(listRowElement);
}
if (!graphRowElement) {
graphRowElement = new WebInspector.TimelineRecordGraphRow(this._itemsGraphsElement, scheduleRefreshCallback).element;
this._graphRowsElement.appendChild(graphRowElement);
}
listRowElement.row.update(record, isEven, visibleTop);
graphRowElement.row.update(record, isEven, this._calculator, this._expandOffset, i);
listRowElement = listRowElement.nextSibling;
graphRowElement = graphRowElement.nextSibling;
}
}
while (listRowElement) {
var nextElement = listRowElement.nextSibling;
listRowElement.row.dispose();
listRowElement = nextElement;
}
while (graphRowElement) {
var nextElement = graphRowElement.nextSibling;
graphRowElement.row.dispose();
graphRowElement = nextElement;
}
this._itemsGraphsElement.insertBefore(this._graphRowsElement, this._bottomGapElement);
this._itemsGraphsElement.appendChild(this._expandElements);
this.splitView.sidebarResizerElement.style.height = this.sidebarElement.clientHeight + "px";
this._adjustScrollPosition((recordsInWindow.length + 1) * rowHeight);
return recordsInWindow.length;
},
_adjustScrollPosition: function(totalHeight)
{
if ((this._containerElement.scrollTop + this._containerElement.offsetHeight) > totalHeight + 1)
this._containerElement.scrollTop = (totalHeight - this._containerElement.offsetHeight);
},
_getPopoverAnchor: function(element)
{
return element.enclosingNodeOrSelfWithClass("timeline-graph-bar") ||
element.enclosingNodeOrSelfWithClass("timeline-tree-item") ||
element.enclosingNodeOrSelfWithClass("timeline-frame-strip");
},
_mouseOut: function(e)
{
this._hideRectHighlight();
},
_mouseMove: function(e)
{
var anchor = this._getPopoverAnchor(e.target);
if (anchor && anchor.row && anchor.row._record.type === "Paint")
this._highlightRect(anchor.row._record);
else
this._hideRectHighlight();
},
_highlightRect: function(record)
{
if (this._highlightedRect === record.data)
return;
this._highlightedRect = record.data;
DOMAgent.highlightRect(this._highlightedRect.x, this._highlightedRect.y, this._highlightedRect.width, this._highlightedRect.height, WebInspector.Color.PageHighlight.Content.toProtocolRGBA(), WebInspector.Color.PageHighlight.ContentOutline.toProtocolRGBA());
},
_hideRectHighlight: function()
{
if (this._highlightedRect) {
delete this._highlightedRect;
DOMAgent.hideHighlight();
}
},
_showPopover: function(anchor, popover)
{
if (anchor.hasStyleClass("timeline-frame-strip")) {
var frame = anchor._frame;
popover.show(WebInspector.TimelinePresentationModel.generatePopupContentForFrame(frame), anchor);
} else {
var record = anchor.row._record;
popover.show(record.generatePopupContent(), anchor);
}
},
_closeRecordDetails: function()
{
this._popoverHelper.hidePopover();
}
}
WebInspector.TimelinePanel.prototype.__proto__ = WebInspector.Panel.prototype;
WebInspector.TimelineCalculator = function(model)
{
this._model = model;
}
WebInspector.TimelineCalculator._minWidth = 5;
WebInspector.TimelineCalculator._borderWidth = 4;
WebInspector.TimelineCalculator.prototype = {
computePosition: function(time)
{
return (time - this.minimumBoundary) / this.boundarySpan * this._workingArea + this.paddingLeft;
},
computeBarGraphPercentages: function(record)
{
var start = (record.startTime - this.minimumBoundary) / this.boundarySpan * 100;
var end = (record.startTime + record.selfTime - this.minimumBoundary) / this.boundarySpan * 100;
var endWithChildren = (record.lastChildEndTime - this.minimumBoundary) / this.boundarySpan * 100;
var cpuWidth = record.cpuTime / this.boundarySpan * 100;
return {start: start, end: end, endWithChildren: endWithChildren, cpuWidth: cpuWidth};
},
computeBarGraphWindowPosition: function(record)
{
var percentages = this.computeBarGraphPercentages(record);
var left = this.computePosition(record.startTime);
var width = (percentages.end - percentages.start) / 100 * this._workingArea + WebInspector.TimelineCalculator._minWidth;
var widthWithChildren = (percentages.endWithChildren - percentages.start) / 100 * this._workingArea;
var cpuWidth = percentages.cpuWidth / 100 * this._workingArea + WebInspector.TimelineCalculator._minWidth;
if (percentages.endWithChildren > percentages.end)
widthWithChildren += WebInspector.TimelineCalculator._borderWidth + WebInspector.TimelineCalculator._minWidth;
return {left: left, width: width, widthWithChildren: widthWithChildren, cpuWidth: cpuWidth};
},
setWindow: function(minimumBoundary, maximumBoundary)
{
this.minimumBoundary = minimumBoundary;
this.maximumBoundary = maximumBoundary;
this.boundarySpan = this.maximumBoundary - this.minimumBoundary;
},
setDisplayWindow: function(paddingLeft, clientWidth)
{
this._workingArea = clientWidth - WebInspector.TimelineCalculator._minWidth - WebInspector.TimelineCalculator._borderWidth - paddingLeft;
this.paddingLeft = paddingLeft;
},
formatTime: function(value)
{
return Number.secondsToString(value + this.minimumBoundary - this._model.minimumRecordTime());
}
}
WebInspector.TimelineRecordListRow = function()
{
this.element = document.createElement("div");
this.element.row = this;
this.element.style.cursor = "pointer";
var iconElement = document.createElement("span");
iconElement.className = "timeline-tree-icon";
this.element.appendChild(iconElement);
this._typeElement = document.createElement("span");
this._typeElement.className = "type";
this.element.appendChild(this._typeElement);
var separatorElement = document.createElement("span");
separatorElement.className = "separator";
separatorElement.textContent = " ";
this._dataElement = document.createElement("span");
this._dataElement.className = "data dimmed";
this.element.appendChild(separatorElement);
this.element.appendChild(this._dataElement);
}
WebInspector.TimelineRecordListRow.prototype = {
update: function(record, isEven, offset)
{
this._record = record;
this._offset = offset;
this.element.className = "timeline-tree-item timeline-category-" + record.category.name + (isEven ? " even" : "");
this._typeElement.textContent = record.title;
if (this._dataElement.firstChild)
this._dataElement.removeChildren();
if (record.details) {
var detailsContainer = document.createElement("span");
if (typeof record.details === "object") {
detailsContainer.appendChild(document.createTextNode("("));
detailsContainer.appendChild(record.details);
detailsContainer.appendChild(document.createTextNode(")"));
} else
detailsContainer.textContent = "(" + record.details + ")";
this._dataElement.appendChild(detailsContainer);
}
},
dispose: function()
{
this.element.parentElement.removeChild(this.element);
}
}
WebInspector.TimelineRecordGraphRow = function(graphContainer, scheduleRefresh)
{
this.element = document.createElement("div");
this.element.row = this;
this._barAreaElement = document.createElement("div");
this._barAreaElement.className = "timeline-graph-bar-area";
this.element.appendChild(this._barAreaElement);
this._barWithChildrenElement = document.createElement("div");
this._barWithChildrenElement.className = "timeline-graph-bar with-children";
this._barWithChildrenElement.row = this;
this._barAreaElement.appendChild(this._barWithChildrenElement);
this._barCpuElement = document.createElement("div");
this._barCpuElement.className = "timeline-graph-bar cpu"
this._barCpuElement.row = this;
this._barAreaElement.appendChild(this._barCpuElement);
this._barElement = document.createElement("div");
this._barElement.className = "timeline-graph-bar";
this._barElement.row = this;
this._barAreaElement.appendChild(this._barElement);
this._expandElement = new WebInspector.TimelineExpandableElement(graphContainer);
this._expandElement._element.addEventListener("click", this._onClick.bind(this));
this._scheduleRefresh = scheduleRefresh;
}
WebInspector.TimelineRecordGraphRow.prototype = {
update: function(record, isEven, calculator, expandOffset, index)
{
this._record = record;
this.element.className = "timeline-graph-side timeline-category-" + record.category.name + (isEven ? " even" : "");
var barPosition = calculator.computeBarGraphWindowPosition(record);
this._barWithChildrenElement.style.left = barPosition.left + "px";
this._barWithChildrenElement.style.width = barPosition.widthWithChildren + "px";
this._barElement.style.left = barPosition.left + "px";
this._barElement.style.width = barPosition.width + "px";
this._barCpuElement.style.left = barPosition.left + "px";
this._barCpuElement.style.width = barPosition.cpuWidth + "px";
this._expandElement._update(record, index, barPosition.left - expandOffset, barPosition.width);
},
_onClick: function(event)
{
this._record.collapsed = !this._record.collapsed;
this._scheduleRefresh(false);
},
dispose: function()
{
this.element.parentElement.removeChild(this.element);
this._expandElement._dispose();
}
}
WebInspector.TimelineExpandableElement = function(container)
{
this._element = document.createElement("div");
this._element.className = "timeline-expandable";
var leftBorder = document.createElement("div");
leftBorder.className = "timeline-expandable-left";
this._element.appendChild(leftBorder);
container.appendChild(this._element);
}
WebInspector.TimelineExpandableElement.prototype = {
_update: function(record, index, left, width)
{
const rowHeight = WebInspector.TimelinePanel.rowHeight;
if (record.visibleChildrenCount || record.invisibleChildrenCount) {
this._element.style.top = index * rowHeight + "px";
this._element.style.left = left + "px";
this._element.style.width = Math.max(12, width + 25) + "px";
if (!record.collapsed) {
this._element.style.height = (record.visibleChildrenCount + 1) * rowHeight + "px";
this._element.addStyleClass("timeline-expandable-expanded");
this._element.removeStyleClass("timeline-expandable-collapsed");
} else {
this._element.style.height = rowHeight + "px";
this._element.addStyleClass("timeline-expandable-collapsed");
this._element.removeStyleClass("timeline-expandable-expanded");
}
this._element.removeStyleClass("hidden");
} else
this._element.addStyleClass("hidden");
},
_dispose: function()
{
this._element.parentElement.removeChild(this._element);
}
}
WebInspector.TimelineCategoryFilter = function()
{
}
WebInspector.TimelineCategoryFilter.prototype = {
accept: function(record)
{
return !record.category.hidden && record.type !== WebInspector.TimelineModel.RecordType.BeginFrame;
}
}
WebInspector.TimelineIsLongFilter = function(panel)
{
this._panel = panel;
}
WebInspector.TimelineIsLongFilter.prototype = {
accept: function(record)
{
return this._panel._showShortEvents || record.isLong();
}
}