| Index: third_party/WebKit/Source/devtools/front_end/ui_lazy/FlameChart.js
|
| diff --git a/third_party/WebKit/Source/devtools/front_end/ui_lazy/FlameChart.js b/third_party/WebKit/Source/devtools/front_end/ui_lazy/FlameChart.js
|
| deleted file mode 100644
|
| index 41b52e00e41334871aa3d140447a4e83fab12756..0000000000000000000000000000000000000000
|
| --- a/third_party/WebKit/Source/devtools/front_end/ui_lazy/FlameChart.js
|
| +++ /dev/null
|
| @@ -1,1524 +0,0 @@
|
| -/**
|
| - * Copyright (C) 2013 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.
|
| - */
|
| -
|
| -/**
|
| - * @interface
|
| - */
|
| -UI.FlameChartDelegate = function() {};
|
| -
|
| -UI.FlameChartDelegate.prototype = {
|
| - /**
|
| - * @param {number} startTime
|
| - * @param {number} endTime
|
| - */
|
| - requestWindowTimes(startTime, endTime) {},
|
| -
|
| - /**
|
| - * @param {number} startTime
|
| - * @param {number} endTime
|
| - */
|
| - updateRangeSelection(startTime, endTime) {},
|
| -};
|
| -
|
| -/**
|
| - * @unrestricted
|
| - */
|
| -UI.FlameChart = class extends UI.ChartViewport {
|
| - /**
|
| - * @param {!UI.FlameChartDataProvider} dataProvider
|
| - * @param {!UI.FlameChartDelegate} flameChartDelegate
|
| - * @param {!Common.Setting=} groupExpansionSetting
|
| - */
|
| - constructor(dataProvider, flameChartDelegate, groupExpansionSetting) {
|
| - super();
|
| - this.registerRequiredCSS('ui_lazy/flameChart.css');
|
| - this.contentElement.classList.add('flame-chart-main-pane');
|
| - this._flameChartDelegate = flameChartDelegate;
|
| - this._groupExpansionSetting = groupExpansionSetting;
|
| - this._groupExpansionState = groupExpansionSetting && groupExpansionSetting.get() || {};
|
| -
|
| - this._dataProvider = dataProvider;
|
| - this._calculator = new UI.FlameChart.Calculator(dataProvider);
|
| -
|
| - this._canvas = /** @type {!HTMLCanvasElement} */ (this.viewportElement.createChild('canvas'));
|
| - this._canvas.tabIndex = 1;
|
| - this.setDefaultFocusedElement(this._canvas);
|
| - this._canvas.addEventListener('mousemove', this._onMouseMove.bind(this), false);
|
| - this._canvas.addEventListener('mouseout', this._onMouseOut.bind(this), false);
|
| - this._canvas.addEventListener('click', this._onClick.bind(this), false);
|
| - this._canvas.addEventListener('keydown', this._onKeyDown.bind(this), false);
|
| -
|
| - this._entryInfo = this.viewportElement.createChild('div', 'flame-chart-entry-info');
|
| - this._markerHighlighElement = this.viewportElement.createChild('div', 'flame-chart-marker-highlight-element');
|
| - this._highlightElement = this.viewportElement.createChild('div', 'flame-chart-highlight-element');
|
| - this._selectedElement = this.viewportElement.createChild('div', 'flame-chart-selected-element');
|
| -
|
| - this._rulerEnabled = true;
|
| - this._windowLeft = 0.0;
|
| - this._windowRight = 1.0;
|
| - this._timeWindowLeft = 0;
|
| - this._timeWindowRight = Infinity;
|
| - this._rangeSelectionStart = 0;
|
| - this._rangeSelectionEnd = 0;
|
| - this._barHeight = 17;
|
| - this._textBaseline = 5;
|
| - this._textPadding = 5;
|
| - this._paddingLeft = 0;
|
| - var markerPadding = 2;
|
| - this._markerRadius = this._barHeight / 2 - markerPadding;
|
| -
|
| - /** @const */
|
| - this._headerLeftPadding = 6;
|
| - /** @const */
|
| - this._arrowSide = 8;
|
| - /** @const */
|
| - this._expansionArrowIndent = this._headerLeftPadding + this._arrowSide / 2;
|
| - /** @const */
|
| - this._headerLabelXPadding = 3;
|
| - /** @const */
|
| - this._headerLabelYPadding = 2;
|
| -
|
| - this._highlightedMarkerIndex = -1;
|
| - this._highlightedEntryIndex = -1;
|
| - this._selectedEntryIndex = -1;
|
| - this._rawTimelineDataLength = 0;
|
| - /** @type {!Map<string,!Map<string,number>>} */
|
| - this._textWidth = new Map();
|
| -
|
| - this._lastMouseOffsetX = 0;
|
| - }
|
| -
|
| - /**
|
| - * @override
|
| - */
|
| - willHide() {
|
| - this.hideHighlight();
|
| - }
|
| -
|
| - /**
|
| - * @param {number} value
|
| - */
|
| - setBarHeight(value) {
|
| - this._barHeight = value;
|
| - }
|
| -
|
| - /**
|
| - * @param {number} value
|
| - */
|
| - setTextBaseline(value) {
|
| - this._textBaseline = value;
|
| - }
|
| -
|
| - /**
|
| - * @param {number} value
|
| - */
|
| - setTextPadding(value) {
|
| - this._textPadding = value;
|
| - }
|
| -
|
| - /**
|
| - * @param {number} value
|
| - */
|
| - setPaddingLeft(value) {
|
| - this._paddingLeft = value;
|
| - }
|
| -
|
| - /**
|
| - * @param {boolean} enable
|
| - */
|
| - enableRuler(enable) {
|
| - this._rulerEnabled = enable;
|
| - }
|
| -
|
| - /**
|
| - * @param {number} entryIndex
|
| - */
|
| - highlightEntry(entryIndex) {
|
| - if (this._highlightedEntryIndex === entryIndex)
|
| - return;
|
| - this._highlightedEntryIndex = entryIndex;
|
| - this._updateElementPosition(this._highlightElement, this._highlightedEntryIndex);
|
| - }
|
| -
|
| - hideHighlight() {
|
| - this._entryInfo.removeChildren();
|
| - this._highlightedEntryIndex = -1;
|
| - this._updateElementPosition(this._highlightElement, this._highlightedEntryIndex);
|
| - }
|
| -
|
| - _resetCanvas() {
|
| - var ratio = window.devicePixelRatio;
|
| - this._canvas.width = this._offsetWidth * ratio;
|
| - this._canvas.height = this._offsetHeight * ratio;
|
| - this._canvas.style.width = this._offsetWidth + 'px';
|
| - this._canvas.style.height = this._offsetHeight + 'px';
|
| - }
|
| -
|
| - /**
|
| - * @return {?UI.FlameChart.TimelineData}
|
| - */
|
| - _timelineData() {
|
| - if (!this._dataProvider)
|
| - return null;
|
| - var timelineData = this._dataProvider.timelineData();
|
| - if (timelineData !== this._rawTimelineData || timelineData.entryStartTimes.length !== this._rawTimelineDataLength)
|
| - this._processTimelineData(timelineData);
|
| - return this._rawTimelineData;
|
| - }
|
| -
|
| - /**
|
| - * @param {number} entryIndex
|
| - */
|
| - _revealEntry(entryIndex) {
|
| - var timelineData = this._timelineData();
|
| - if (!timelineData)
|
| - return;
|
| - // Think in terms of not where we are, but where we'll be after animation (if present)
|
| - var timeLeft = this._cancelWindowTimesAnimation ? this._pendingAnimationTimeLeft : this._timeWindowLeft;
|
| - var timeRight = this._cancelWindowTimesAnimation ? this._pendingAnimationTimeRight : this._timeWindowRight;
|
| - var entryStartTime = timelineData.entryStartTimes[entryIndex];
|
| - var entryTotalTime = timelineData.entryTotalTimes[entryIndex];
|
| - var entryEndTime = entryStartTime + entryTotalTime;
|
| - var minEntryTimeWindow = Math.min(entryTotalTime, timeRight - timeLeft);
|
| -
|
| - var y = this._levelToHeight(timelineData.entryLevels[entryIndex]);
|
| - this.setScrollOffset(y, this._barHeight);
|
| -
|
| - if (timeLeft > entryEndTime) {
|
| - var delta = timeLeft - entryEndTime + minEntryTimeWindow;
|
| - this._flameChartDelegate.requestWindowTimes(timeLeft - delta, timeRight - delta);
|
| - } else if (timeRight < entryStartTime) {
|
| - var delta = entryStartTime - timeRight + minEntryTimeWindow;
|
| - this._flameChartDelegate.requestWindowTimes(timeLeft + delta, timeRight + delta);
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * @override
|
| - * @param {number} startTime
|
| - * @param {number} endTime
|
| - */
|
| - setWindowTimes(startTime, endTime) {
|
| - super.setWindowTimes(startTime, endTime);
|
| - this._updateHighlight();
|
| - }
|
| -
|
| - /**
|
| - * @param {!Event} event
|
| - */
|
| - _onMouseMove(event) {
|
| - this._lastMouseOffsetX = event.offsetX;
|
| - this._lastMouseOffsetY = event.offsetY;
|
| - if (!this._enabled())
|
| - return;
|
| - if (this.isDragging())
|
| - return;
|
| - if (this._coordinatesToGroupIndex(event.offsetX, event.offsetY) >= 0) {
|
| - this.hideHighlight();
|
| - this.viewportElement.style.cursor = 'pointer';
|
| - return;
|
| - }
|
| - this._updateHighlight();
|
| - }
|
| -
|
| - _updateHighlight() {
|
| - const inDividersBar = this._lastMouseOffsetY < UI.FlameChart.HeaderHeight;
|
| - this._highlightedMarkerIndex = inDividersBar ? this._markerIndexAtPosition(this._lastMouseOffsetX) : -1;
|
| - this._updateMarkerHighlight();
|
| -
|
| - const entryIndex = this._highlightedMarkerIndex === -1 ?
|
| - this._coordinatesToEntryIndex(this._lastMouseOffsetX, this._lastMouseOffsetY) : -1;
|
| - if (entryIndex === -1) {
|
| - this.hideHighlight();
|
| - return;
|
| - }
|
| - if (this.isDragging())
|
| - return;
|
| - this._updatePopover(entryIndex);
|
| - this.viewportElement.style.cursor = this._dataProvider.canJumpToEntry(entryIndex) ? 'pointer' : 'default';
|
| - this.highlightEntry(entryIndex);
|
| - }
|
| -
|
| - _onMouseOut() {
|
| - this._lastMouseOffsetX = -1;
|
| - this._lastMouseOffsetY = -1;
|
| - this.hideHighlight();
|
| - }
|
| -
|
| - /**
|
| - * @param {number} entryIndex
|
| - */
|
| - _updatePopover(entryIndex) {
|
| - if (entryIndex === this._highlightedEntryIndex) {
|
| - this._updatePopoverOffset();
|
| - return;
|
| - }
|
| - this._entryInfo.removeChildren();
|
| - var popoverElement = this._dataProvider.prepareHighlightedEntryInfo(entryIndex);
|
| - if (popoverElement) {
|
| - this._entryInfo.appendChild(popoverElement);
|
| - this._updatePopoverOffset();
|
| - }
|
| - }
|
| -
|
| - _updatePopoverOffset() {
|
| - var mouseX = this._lastMouseOffsetX;
|
| - var mouseY = this._lastMouseOffsetY;
|
| - var parentWidth = this._entryInfo.parentElement.clientWidth;
|
| - var parentHeight = this._entryInfo.parentElement.clientHeight;
|
| - var infoWidth = this._entryInfo.clientWidth;
|
| - var infoHeight = this._entryInfo.clientHeight;
|
| - var /** @const */ offsetX = 10;
|
| - var /** @const */ offsetY = 6;
|
| - var x;
|
| - var y;
|
| - for (var quadrant = 0; quadrant < 4; ++quadrant) {
|
| - var dx = quadrant & 2 ? -offsetX - infoWidth : offsetX;
|
| - var dy = quadrant & 1 ? -offsetY - infoHeight : offsetY;
|
| - x = Number.constrain(mouseX + dx, 0, parentWidth - infoWidth);
|
| - y = Number.constrain(mouseY + dy, 0, parentHeight - infoHeight);
|
| - if (x >= mouseX || mouseX >= x + infoWidth || y >= mouseY || mouseY >= y + infoHeight)
|
| - break;
|
| - }
|
| - this._entryInfo.style.left = x + 'px';
|
| - this._entryInfo.style.top = y + 'px';
|
| - }
|
| -
|
| - /**
|
| - * @param {!Event} event
|
| - */
|
| - _onClick(event) {
|
| - this.focus();
|
| - // onClick comes after dragStart and dragEnd events.
|
| - // So if there was drag (mouse move) in the middle of that events
|
| - // we skip the click. Otherwise we jump to the sources.
|
| - const clickThreshold = 5;
|
| - if (this.maxDragOffset() > clickThreshold)
|
| - return;
|
| - var groupIndex = this._coordinatesToGroupIndex(event.offsetX, event.offsetY);
|
| - if (groupIndex >= 0) {
|
| - this._toggleGroupVisibility(groupIndex);
|
| - return;
|
| - }
|
| - this.hideRangeSelection();
|
| - this.dispatchEventToListeners(UI.FlameChart.Events.EntrySelected, this._highlightedEntryIndex);
|
| - }
|
| -
|
| - /**
|
| - * @param {number} groupIndex
|
| - */
|
| - _toggleGroupVisibility(groupIndex) {
|
| - if (!this._isGroupCollapsible(groupIndex))
|
| - return;
|
| - var groups = this._rawTimelineData.groups;
|
| - var group = groups[groupIndex];
|
| - group.expanded = !group.expanded;
|
| - this._groupExpansionState[group.name] = group.expanded;
|
| - if (this._groupExpansionSetting)
|
| - this._groupExpansionSetting.set(this._groupExpansionState);
|
| - this._updateLevelPositions();
|
| -
|
| - this._updateHighlight();
|
| - if (!group.expanded) {
|
| - var timelineData = this._timelineData();
|
| - var level = timelineData.entryLevels[this._selectedEntryIndex];
|
| - if (this._selectedEntryIndex >= 0 && level >= group.startLevel &&
|
| - (groupIndex === groups.length || groups[groupIndex + 1].startLevel > level))
|
| - this._selectedEntryIndex = -1;
|
| - }
|
| -
|
| - this._updateHeight();
|
| - this._resetCanvas();
|
| - this._draw(this._offsetWidth, this._offsetHeight);
|
| - }
|
| -
|
| - /**
|
| - * @param {!Event} e
|
| - */
|
| - _onKeyDown(e) {
|
| - this._handleSelectionNavigation(e);
|
| - }
|
| -
|
| - /**
|
| - * @param {!Event} e
|
| - */
|
| - _handleSelectionNavigation(e) {
|
| - if (!UI.KeyboardShortcut.hasNoModifiers(e))
|
| - return;
|
| - if (this._selectedEntryIndex === -1)
|
| - return;
|
| - var timelineData = this._timelineData();
|
| - if (!timelineData)
|
| - return;
|
| -
|
| - /**
|
| - * @param {number} time
|
| - * @param {number} entryIndex
|
| - * @return {number}
|
| - */
|
| - function timeComparator(time, entryIndex) {
|
| - return time - timelineData.entryStartTimes[entryIndex];
|
| - }
|
| -
|
| - /**
|
| - * @param {number} entry1
|
| - * @param {number} entry2
|
| - * @return {boolean}
|
| - */
|
| - function entriesIntersect(entry1, entry2) {
|
| - var start1 = timelineData.entryStartTimes[entry1];
|
| - var start2 = timelineData.entryStartTimes[entry2];
|
| - var end1 = start1 + timelineData.entryTotalTimes[entry1];
|
| - var end2 = start2 + timelineData.entryTotalTimes[entry2];
|
| - return start1 < end2 && start2 < end1;
|
| - }
|
| -
|
| - var keys = UI.KeyboardShortcut.Keys;
|
| - if (e.keyCode === keys.Left.code || e.keyCode === keys.Right.code) {
|
| - var level = timelineData.entryLevels[this._selectedEntryIndex];
|
| - var levelIndexes = this._timelineLevels[level];
|
| - var indexOnLevel = levelIndexes.lowerBound(this._selectedEntryIndex);
|
| - indexOnLevel += e.keyCode === keys.Left.code ? -1 : 1;
|
| - e.consume(true);
|
| - if (indexOnLevel >= 0 && indexOnLevel < levelIndexes.length)
|
| - this.dispatchEventToListeners(UI.FlameChart.Events.EntrySelected, levelIndexes[indexOnLevel]);
|
| - return;
|
| - }
|
| - if (e.keyCode === keys.Up.code || e.keyCode === keys.Down.code) {
|
| - e.consume(true);
|
| - var level = timelineData.entryLevels[this._selectedEntryIndex];
|
| - level += e.keyCode === keys.Up.code ? -1 : 1;
|
| - if (level < 0 || level >= this._timelineLevels.length)
|
| - return;
|
| - var entryTime = timelineData.entryStartTimes[this._selectedEntryIndex] +
|
| - timelineData.entryTotalTimes[this._selectedEntryIndex] / 2;
|
| - var levelIndexes = this._timelineLevels[level];
|
| - var indexOnLevel = levelIndexes.upperBound(entryTime, timeComparator) - 1;
|
| - if (!entriesIntersect(this._selectedEntryIndex, levelIndexes[indexOnLevel])) {
|
| - ++indexOnLevel;
|
| - if (indexOnLevel >= levelIndexes.length ||
|
| - !entriesIntersect(this._selectedEntryIndex, levelIndexes[indexOnLevel]))
|
| - return;
|
| - }
|
| - this.dispatchEventToListeners(UI.FlameChart.Events.EntrySelected, levelIndexes[indexOnLevel]);
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * @param {number} x
|
| - * @return {number}
|
| - */
|
| - _cursorTime(x) {
|
| - return (x + this._pixelWindowLeft - this._paddingLeft) * this._pixelToTime + this._minimumBoundary;
|
| - }
|
| -
|
| - /**
|
| - * @param {number} x
|
| - * @param {number} y
|
| - * @return {number}
|
| - */
|
| - _coordinatesToEntryIndex(x, y) {
|
| - if (x < 0 || y < 0)
|
| - return -1;
|
| - y += this.getScrollOffset();
|
| - var timelineData = this._timelineData();
|
| - if (!timelineData)
|
| - return -1;
|
| - var cursorTime = this._cursorTime(x);
|
| - var cursorLevel = this._visibleLevelOffsets.upperBound(y) - 1;
|
| - if (cursorLevel < 0 || !this._visibleLevels[cursorLevel])
|
| - return -1;
|
| - var offsetFromLevel = y - this._visibleLevelOffsets[cursorLevel];
|
| - if (offsetFromLevel > this._barHeight)
|
| - return -1;
|
| - var entryStartTimes = timelineData.entryStartTimes;
|
| - var entryTotalTimes = timelineData.entryTotalTimes;
|
| - var entryIndexes = this._timelineLevels[cursorLevel];
|
| - if (!entryIndexes || !entryIndexes.length)
|
| - return -1;
|
| -
|
| - /**
|
| - * @param {number} time
|
| - * @param {number} entryIndex
|
| - * @return {number}
|
| - */
|
| - function comparator(time, entryIndex) {
|
| - return time - entryStartTimes[entryIndex];
|
| - }
|
| - var indexOnLevel = Math.max(entryIndexes.upperBound(cursorTime, comparator) - 1, 0);
|
| -
|
| - /**
|
| - * @this {UI.FlameChart}
|
| - * @param {number} entryIndex
|
| - * @return {boolean}
|
| - */
|
| - function checkEntryHit(entryIndex) {
|
| - if (entryIndex === undefined)
|
| - return false;
|
| - var startTime = entryStartTimes[entryIndex];
|
| - var duration = entryTotalTimes[entryIndex];
|
| - if (isNaN(duration)) {
|
| - var dx = (startTime - cursorTime) / this._pixelToTime;
|
| - var dy = this._barHeight / 2 - offsetFromLevel;
|
| - return dx * dx + dy * dy < this._markerRadius * this._markerRadius;
|
| - }
|
| - var endTime = startTime + duration;
|
| - var barThreshold = 3 * this._pixelToTime;
|
| - return startTime - barThreshold < cursorTime && cursorTime < endTime + barThreshold;
|
| - }
|
| -
|
| - var entryIndex = entryIndexes[indexOnLevel];
|
| - if (checkEntryHit.call(this, entryIndex))
|
| - return entryIndex;
|
| - entryIndex = entryIndexes[indexOnLevel + 1];
|
| - if (checkEntryHit.call(this, entryIndex))
|
| - return entryIndex;
|
| - return -1;
|
| - }
|
| -
|
| - /**
|
| - * @param {number} x
|
| - * @param {number} y
|
| - * @return {number}
|
| - */
|
| - _coordinatesToGroupIndex(x, y) {
|
| - if (x < 0 || y < 0)
|
| - return -1;
|
| - y += this.getScrollOffset();
|
| - var groups = this._rawTimelineData.groups || [];
|
| - var group = this._groupOffsets.upperBound(y) - 1;
|
| -
|
| - if (group < 0 || group >= groups.length || y - this._groupOffsets[group] >= groups[group].style.height)
|
| - return -1;
|
| - var context = /** @type {!CanvasRenderingContext2D} */ (this._canvas.getContext('2d'));
|
| - context.save();
|
| - context.font = groups[group].style.font;
|
| - var right = this._headerLeftPadding + this._labelWidthForGroup(context, groups[group]);
|
| - context.restore();
|
| - if (x > right)
|
| - return -1;
|
| -
|
| - return group;
|
| - }
|
| -
|
| - /**
|
| - * @param {number} x
|
| - * @return {number}
|
| - */
|
| - _markerIndexAtPosition(x) {
|
| - const markers = this._timelineData().markers;
|
| - if (!markers)
|
| - return -1;
|
| - const accurracyOffsetPx = 4;
|
| - const time = this._cursorTime(x);
|
| - const leftTime = this._cursorTime(x - accurracyOffsetPx);
|
| - const rightTime = this._cursorTime(x + accurracyOffsetPx);
|
| - const left = this._markerIndexBeforeTime(leftTime);
|
| - var markerIndex = -1;
|
| - var distance = Infinity;
|
| - for (var i = left; i < markers.length && markers[i].startTime() < rightTime; i++) {
|
| - const nextDistance = Math.abs(markers[i].startTime() - time);
|
| - if (nextDistance < distance) {
|
| - markerIndex = i;
|
| - distance = nextDistance;
|
| - }
|
| - }
|
| - return markerIndex;
|
| - }
|
| -
|
| - /**
|
| - * @param {number} time
|
| - * @return {number}
|
| - */
|
| - _markerIndexBeforeTime(time) {
|
| - return this._timelineData().markers.lowerBound(
|
| - time, (markerTimestamp, marker) => markerTimestamp - marker.startTime());
|
| - }
|
| -
|
| - /**
|
| - * @param {number} height
|
| - * @param {number} width
|
| - */
|
| - _draw(width, height) {
|
| - var timelineData = this._timelineData();
|
| - if (!timelineData)
|
| - return;
|
| -
|
| - var context = /** @type {!CanvasRenderingContext2D} */ (this._canvas.getContext('2d'));
|
| - context.save();
|
| - var ratio = window.devicePixelRatio;
|
| - var top = this.getScrollOffset();
|
| - context.scale(ratio, ratio);
|
| - context.translate(0, -top);
|
| - var defaultFont = '11px ' + Host.fontFamily();
|
| - context.font = defaultFont;
|
| -
|
| - var timeWindowRight = this._timeWindowRight;
|
| - var timeWindowLeft = this._timeWindowLeft - this._paddingLeft / this._timeToPixel;
|
| - var entryTotalTimes = timelineData.entryTotalTimes;
|
| - var entryStartTimes = timelineData.entryStartTimes;
|
| - var entryLevels = timelineData.entryLevels;
|
| -
|
| - var titleIndices = [];
|
| - var markerIndices = [];
|
| - var textPadding = this._textPadding;
|
| - var minTextWidth = 2 * textPadding + UI.measureTextWidth(context, '\u2026');
|
| - var barHeight = this._barHeight;
|
| - var minVisibleBarLevel = Math.max(this._visibleLevelOffsets.upperBound(top) - 1, 0);
|
| -
|
| - /** @type {!Map<string, !Array<number>>} */
|
| - var colorBuckets = new Map();
|
| - for (var level = minVisibleBarLevel; level < this._dataProvider.maxStackDepth(); ++level) {
|
| - if (this._levelToHeight(level) > top + height)
|
| - break;
|
| - if (!this._visibleLevels[level])
|
| - continue;
|
| -
|
| - // Entries are ordered by start time within a level, so find the last visible entry.
|
| - var levelIndexes = this._timelineLevels[level];
|
| - var rightIndexOnLevel =
|
| - levelIndexes.lowerBound(timeWindowRight, (time, entryIndex) => time - entryStartTimes[entryIndex]) - 1;
|
| - var lastDrawOffset = Infinity;
|
| - for (var entryIndexOnLevel = rightIndexOnLevel; entryIndexOnLevel >= 0; --entryIndexOnLevel) {
|
| - var entryIndex = levelIndexes[entryIndexOnLevel];
|
| - var entryStartTime = entryStartTimes[entryIndex];
|
| - var entryOffsetRight = entryStartTime + (entryTotalTimes[entryIndex] || 0);
|
| - if (entryOffsetRight <= timeWindowLeft)
|
| - break;
|
| -
|
| - var barX = this._timeToPositionClipped(entryStartTime);
|
| - // Check if the entry entirely fits into an already drawn pixel, we can just skip drawing it.
|
| - if (barX >= lastDrawOffset)
|
| - continue;
|
| - lastDrawOffset = barX;
|
| -
|
| - var color = this._dataProvider.entryColor(entryIndex);
|
| - var bucket = colorBuckets.get(color);
|
| - if (!bucket) {
|
| - bucket = [];
|
| - colorBuckets.set(color, bucket);
|
| - }
|
| - bucket.push(entryIndex);
|
| - }
|
| - }
|
| -
|
| - var colors = colorBuckets.keysArray();
|
| - // We don't use for-of here because it's slow.
|
| - for (var c = 0; c < colors.length; ++c) {
|
| - var color = colors[c];
|
| - var indexes = colorBuckets.get(color);
|
| - context.beginPath();
|
| - context.fillStyle = color;
|
| - for (var i = 0; i < indexes.length; ++i) {
|
| - var entryIndex = indexes[i];
|
| - var entryStartTime = entryStartTimes[entryIndex];
|
| - var barX = this._timeToPositionClipped(entryStartTime);
|
| - var duration = entryTotalTimes[entryIndex];
|
| - var barLevel = entryLevels[entryIndex];
|
| - var barY = this._levelToHeight(barLevel);
|
| - if (isNaN(duration)) {
|
| - context.moveTo(barX + this._markerRadius, barY + barHeight / 2);
|
| - context.arc(barX, barY + barHeight / 2, this._markerRadius, 0, Math.PI * 2);
|
| - markerIndices.push(entryIndex);
|
| - continue;
|
| - }
|
| - var barRight = this._timeToPositionClipped(entryStartTime + duration);
|
| - var barWidth = Math.max(barRight - barX, 1);
|
| - context.rect(barX, barY, barWidth - 0.4, barHeight - 1);
|
| - if (barWidth > minTextWidth || this._dataProvider.forceDecoration(entryIndex))
|
| - titleIndices.push(entryIndex);
|
| - }
|
| - context.fill();
|
| - }
|
| -
|
| - context.strokeStyle = 'rgba(0, 0, 0, 0.2)';
|
| - context.beginPath();
|
| - for (var m = 0; m < markerIndices.length; ++m) {
|
| - var entryIndex = markerIndices[m];
|
| - var entryStartTime = entryStartTimes[entryIndex];
|
| - var barX = this._timeToPositionClipped(entryStartTime);
|
| - var barLevel = entryLevels[entryIndex];
|
| - var barY = this._levelToHeight(barLevel);
|
| - context.moveTo(barX + this._markerRadius, barY + barHeight / 2);
|
| - context.arc(barX, barY + barHeight / 2, this._markerRadius, 0, Math.PI * 2);
|
| - }
|
| - context.stroke();
|
| -
|
| - context.textBaseline = 'alphabetic';
|
| - var textBaseHeight = this._barHeight - this._textBaseline;
|
| -
|
| - for (var i = 0; i < titleIndices.length; ++i) {
|
| - var entryIndex = titleIndices[i];
|
| - var entryStartTime = entryStartTimes[entryIndex];
|
| - var barX = this._timeToPositionClipped(entryStartTime);
|
| - var barRight = Math.min(this._timeToPositionClipped(entryStartTime + entryTotalTimes[entryIndex]), width) + 1;
|
| - var barWidth = barRight - barX;
|
| - var barLevel = entryLevels[entryIndex];
|
| - var barY = this._levelToHeight(barLevel);
|
| - var text = this._dataProvider.entryTitle(entryIndex);
|
| - if (text && text.length) {
|
| - context.font = this._dataProvider.entryFont(entryIndex) || defaultFont;
|
| - text = UI.trimTextMiddle(context, text, barWidth - 2 * textPadding);
|
| - }
|
| - var unclippedBarX = this._timeToPosition(entryStartTime);
|
| - if (this._dataProvider.decorateEntry(
|
| - entryIndex, context, text, barX, barY, barWidth, barHeight, unclippedBarX, this._timeToPixel))
|
| - continue;
|
| - if (!text || !text.length)
|
| - continue;
|
| - context.fillStyle = this._dataProvider.textColor(entryIndex);
|
| - context.fillText(text, barX + textPadding, barY + textBaseHeight);
|
| - }
|
| -
|
| - context.restore();
|
| -
|
| - this._drawGroupHeaders(width, height);
|
| - this._drawMarkers();
|
| - const headerHeight = this._rulerEnabled ? UI.FlameChart.HeaderHeight : 0;
|
| - UI.TimelineGrid.drawCanvasGrid(context, this._calculator, 3, headerHeight);
|
| -
|
| - this._updateElementPosition(this._highlightElement, this._highlightedEntryIndex);
|
| - this._updateElementPosition(this._selectedElement, this._selectedEntryIndex);
|
| - this._updateMarkerHighlight();
|
| - }
|
| -
|
| - /**
|
| - * @param {number} width
|
| - * @param {number} height
|
| - */
|
| - _drawGroupHeaders(width, height) {
|
| - var context = /** @type {!CanvasRenderingContext2D} */ (this._canvas.getContext('2d'));
|
| - var top = this.getScrollOffset();
|
| - var ratio = window.devicePixelRatio;
|
| - var barHeight = this._barHeight;
|
| - var textBaseHeight = barHeight - this._textBaseline;
|
| - var groups = this._rawTimelineData.groups || [];
|
| - if (!groups.length)
|
| - return;
|
| -
|
| - var groupOffsets = this._groupOffsets;
|
| - var lastGroupOffset = Array.prototype.peekLast.call(groupOffsets);
|
| - var colorUsage = UI.ThemeSupport.ColorUsage;
|
| -
|
| - context.save();
|
| - context.scale(ratio, ratio);
|
| - context.translate(0, -top);
|
| -
|
| - context.fillStyle = UI.themeSupport.patchColor('#fff', colorUsage.Background);
|
| - forEachGroup.call(this, (offset, index, group) => {
|
| - var paddingHeight = group.style.padding;
|
| - if (paddingHeight < 5)
|
| - return;
|
| - context.fillRect(0, offset - paddingHeight + 2, width, paddingHeight - 4);
|
| - });
|
| - if (groups.length && lastGroupOffset < top + height)
|
| - context.fillRect(0, lastGroupOffset + 2, width, top + height - lastGroupOffset);
|
| -
|
| - context.strokeStyle = UI.themeSupport.patchColor('#eee', colorUsage.Background);
|
| - context.beginPath();
|
| - forEachGroup.call(this, (offset, index, group, isFirst) => {
|
| - if (isFirst || group.style.padding < 4)
|
| - return;
|
| - hLine(offset - 2.5);
|
| - });
|
| - hLine(lastGroupOffset + 1.5);
|
| - context.stroke();
|
| -
|
| - forEachGroup.call(this, (offset, index, group) => {
|
| - if (group.style.useFirstLineForOverview)
|
| - return;
|
| - if (!this._isGroupCollapsible(index) || group.expanded) {
|
| - if (!group.style.shareHeaderLine) {
|
| - context.fillStyle = group.style.backgroundColor;
|
| - context.fillRect(0, offset, width, group.style.height);
|
| - }
|
| - return;
|
| - }
|
| - var nextGroup = index + 1;
|
| - while (nextGroup < groups.length && groups[nextGroup].style.nestingLevel > group.style.nestingLevel)
|
| - nextGroup++;
|
| - var endLevel = nextGroup < groups.length ? groups[nextGroup].startLevel : this._dataProvider.maxStackDepth();
|
| - this._drawCollapsedOverviewForGroup(offset + 1, group.startLevel, endLevel);
|
| - });
|
| -
|
| - context.save();
|
| - forEachGroup.call(this, (offset, index, group) => {
|
| - context.font = group.style.font;
|
| - if (this._isGroupCollapsible(index) && !group.expanded || group.style.shareHeaderLine) {
|
| - const width = this._labelWidthForGroup(context, group) + 2;
|
| - context.fillStyle = Common.Color.parse(group.style.backgroundColor).setAlpha(0.8).asString(null);
|
| - context.fillRect(
|
| - this._headerLeftPadding - this._headerLabelXPadding, offset + this._headerLabelYPadding, width,
|
| - barHeight - 2 * this._headerLabelYPadding);
|
| - }
|
| - context.fillStyle = group.style.color;
|
| - context.fillText(
|
| - group.name, Math.floor(this._expansionArrowIndent * (group.style.nestingLevel + 1) + this._arrowSide),
|
| - offset + textBaseHeight);
|
| - });
|
| - context.restore();
|
| -
|
| - context.fillStyle = UI.themeSupport.patchColor('#6e6e6e', colorUsage.Foreground);
|
| - context.beginPath();
|
| - forEachGroup.call(this, (offset, index, group) => {
|
| - if (this._isGroupCollapsible(index)) {
|
| - drawExpansionArrow.call(
|
| - this, this._expansionArrowIndent * (group.style.nestingLevel + 1),
|
| - offset + textBaseHeight - this._arrowSide / 2, !!group.expanded);
|
| - }
|
| - });
|
| - context.fill();
|
| -
|
| - context.strokeStyle = UI.themeSupport.patchColor('#ddd', colorUsage.Background);
|
| - context.beginPath();
|
| - context.stroke();
|
| -
|
| - context.restore();
|
| -
|
| - /**
|
| - * @param {number} y
|
| - */
|
| - function hLine(y) {
|
| - context.moveTo(0, y);
|
| - context.lineTo(width, y);
|
| - }
|
| -
|
| - /**
|
| - * @param {number} x
|
| - * @param {number} y
|
| - * @param {boolean} expanded
|
| - * @this {UI.FlameChart}
|
| - */
|
| - function drawExpansionArrow(x, y, expanded) {
|
| - var arrowHeight = this._arrowSide * Math.sqrt(3) / 2;
|
| - var arrowCenterOffset = Math.round(arrowHeight / 2);
|
| - context.save();
|
| - context.translate(x, y);
|
| - context.rotate(expanded ? Math.PI / 2 : 0);
|
| - context.moveTo(-arrowCenterOffset, -this._arrowSide / 2);
|
| - context.lineTo(-arrowCenterOffset, this._arrowSide / 2);
|
| - context.lineTo(arrowHeight - arrowCenterOffset, 0);
|
| - context.restore();
|
| - }
|
| -
|
| - /**
|
| - * @param {function(number, number, !UI.FlameChart.Group, boolean)} callback
|
| - * @this {UI.FlameChart}
|
| - */
|
| - function forEachGroup(callback) {
|
| - /** @type !Array<{nestingLevel: number, visible: boolean}> */
|
| - var groupStack = [{nestingLevel: -1, visible: true}];
|
| - for (var i = 0; i < groups.length; ++i) {
|
| - var groupTop = groupOffsets[i];
|
| - var group = groups[i];
|
| - if (groupTop - group.style.padding > top + height)
|
| - break;
|
| - var firstGroup = true;
|
| - while (groupStack.peekLast().nestingLevel >= group.style.nestingLevel) {
|
| - groupStack.pop();
|
| - firstGroup = false;
|
| - }
|
| - var parentGroupVisible = groupStack.peekLast().visible;
|
| - var thisGroupVisible = parentGroupVisible && (!this._isGroupCollapsible(i) || group.expanded);
|
| - groupStack.push({nestingLevel: group.style.nestingLevel, visible: thisGroupVisible});
|
| - if (!parentGroupVisible || groupTop + group.style.height < top)
|
| - continue;
|
| - callback(groupTop, i, group, firstGroup);
|
| - }
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * @param {!CanvasRenderingContext2D} context
|
| - * @param {!UI.FlameChart.Group} group
|
| - * @return {number}
|
| - */
|
| - _labelWidthForGroup(context, group) {
|
| - return UI.measureTextWidth(context, group.name) + this._expansionArrowIndent * (group.style.nestingLevel + 1) +
|
| - 2 * this._headerLabelXPadding;
|
| - }
|
| -
|
| - /**
|
| - * @param {number} y
|
| - * @param {number} startLevel
|
| - * @param {number} endLevel
|
| - */
|
| - _drawCollapsedOverviewForGroup(y, startLevel, endLevel) {
|
| - var range = new Common.SegmentedRange(mergeCallback);
|
| - var timeWindowRight = this._timeWindowRight;
|
| - var timeWindowLeft = this._timeWindowLeft - this._paddingLeft / this._timeToPixel;
|
| - var context = /** @type {!CanvasRenderingContext2D} */ (this._canvas.getContext('2d'));
|
| - var barHeight = this._barHeight - 2;
|
| - var entryStartTimes = this._rawTimelineData.entryStartTimes;
|
| - var entryTotalTimes = this._rawTimelineData.entryTotalTimes;
|
| -
|
| - for (var level = startLevel; level < endLevel; ++level) {
|
| - var levelIndexes = this._timelineLevels[level];
|
| - var rightIndexOnLevel =
|
| - levelIndexes.lowerBound(timeWindowRight, (time, entryIndex) => time - entryStartTimes[entryIndex]) - 1;
|
| - var lastDrawOffset = Infinity;
|
| -
|
| - for (var entryIndexOnLevel = rightIndexOnLevel; entryIndexOnLevel >= 0; --entryIndexOnLevel) {
|
| - var entryIndex = levelIndexes[entryIndexOnLevel];
|
| - var entryStartTime = entryStartTimes[entryIndex];
|
| - var startPosition = this._timeToPositionClipped(entryStartTime);
|
| - var entryEndTime = entryStartTime + entryTotalTimes[entryIndex];
|
| - if (isNaN(entryEndTime) || startPosition >= lastDrawOffset)
|
| - continue;
|
| - if (entryEndTime <= timeWindowLeft)
|
| - break;
|
| - lastDrawOffset = startPosition;
|
| - var color = this._dataProvider.entryColor(entryIndex);
|
| - range.append(new Common.Segment(startPosition, this._timeToPositionClipped(entryEndTime), color));
|
| - }
|
| - }
|
| -
|
| - var segments = range.segments().slice().sort((a, b) => a.data.localeCompare(b.data));
|
| - var lastColor;
|
| - context.beginPath();
|
| - for (var i = 0; i < segments.length; ++i) {
|
| - var segment = segments[i];
|
| - if (lastColor !== segments[i].data) {
|
| - context.fill();
|
| - context.beginPath();
|
| - lastColor = segments[i].data;
|
| - context.fillStyle = lastColor;
|
| - }
|
| - context.rect(segment.begin, y, segment.end - segment.begin, barHeight);
|
| - }
|
| - context.fill();
|
| -
|
| - /**
|
| - * @param {!Common.Segment} a
|
| - * @param {!Common.Segment} b
|
| - * @return {?Common.Segment}
|
| - */
|
| - function mergeCallback(a, b) {
|
| - return a.data === b.data && a.end + 0.4 > b.end ? a : null;
|
| - }
|
| - }
|
| -
|
| - _drawMarkers() {
|
| - var markers = this._timelineData().markers;
|
| - var left = this._markerIndexBeforeTime(this._calculator.minimumBoundary());
|
| - var rightBoundary = this._calculator.maximumBoundary();
|
| -
|
| - var context = /** @type {!CanvasRenderingContext2D} */ (this._canvas.getContext('2d'));
|
| - context.save();
|
| - var ratio = window.devicePixelRatio;
|
| - context.scale(ratio, ratio);
|
| - context.translate(0, 3);
|
| - var height = UI.FlameChart.HeaderHeight - 1;
|
| - for (var i = left; i < markers.length; i++) {
|
| - var timestamp = markers[i].startTime();
|
| - if (timestamp > rightBoundary)
|
| - break;
|
| - markers[i].draw(context, this._calculator.computePosition(timestamp), height, this._timeToPixel);
|
| - }
|
| - context.restore();
|
| - }
|
| -
|
| - _updateMarkerHighlight() {
|
| - var element = this._markerHighlighElement;
|
| - if (element.parentElement)
|
| - element.remove();
|
| - var markerIndex = this._highlightedMarkerIndex;
|
| - if (markerIndex === -1)
|
| - return;
|
| - var marker = this._timelineData().markers[markerIndex];
|
| - var barX = this._timeToPositionClipped(marker.startTime());
|
| - element.title = marker.title();
|
| - var style = element.style;
|
| - style.left = barX + 'px';
|
| - style.backgroundColor = marker.color();
|
| - this.viewportElement.appendChild(element);
|
| - }
|
| -
|
| - /**
|
| - * @param {?UI.FlameChart.TimelineData} timelineData
|
| - */
|
| - _processTimelineData(timelineData) {
|
| - if (!timelineData) {
|
| - this._timelineLevels = null;
|
| - this._visibleLevelOffsets = null;
|
| - this._visibleLevels = null;
|
| - this._groupOffsets = null;
|
| - this._rawTimelineData = null;
|
| - this._rawTimelineDataLength = 0;
|
| - return;
|
| - }
|
| -
|
| - this._rawTimelineData = timelineData;
|
| - this._rawTimelineDataLength = timelineData.entryStartTimes.length;
|
| -
|
| - var entryCounters = new Uint32Array(this._dataProvider.maxStackDepth() + 1);
|
| - for (var i = 0; i < timelineData.entryLevels.length; ++i)
|
| - ++entryCounters[timelineData.entryLevels[i]];
|
| - var levelIndexes = new Array(entryCounters.length);
|
| - for (var i = 0; i < levelIndexes.length; ++i) {
|
| - levelIndexes[i] = new Uint32Array(entryCounters[i]);
|
| - entryCounters[i] = 0;
|
| - }
|
| - for (var i = 0; i < timelineData.entryLevels.length; ++i) {
|
| - var level = timelineData.entryLevels[i];
|
| - levelIndexes[level][entryCounters[level]++] = i;
|
| - }
|
| - this._timelineLevels = levelIndexes;
|
| - var groups = this._rawTimelineData.groups || [];
|
| - for (var i = 0; i < groups.length; ++i) {
|
| - var expanded = this._groupExpansionState[groups[i].name];
|
| - if (expanded !== undefined)
|
| - groups[i].expanded = expanded;
|
| - }
|
| - this._updateLevelPositions();
|
| - this._updateHeight();
|
| - }
|
| -
|
| - _updateLevelPositions() {
|
| - var levelCount = this._dataProvider.maxStackDepth();
|
| - var groups = this._rawTimelineData.groups || [];
|
| - this._visibleLevelOffsets = new Uint32Array(levelCount + 1);
|
| - this._visibleLevels = new Uint16Array(levelCount);
|
| - this._groupOffsets = new Uint32Array(groups.length + 1);
|
| -
|
| - var groupIndex = -1;
|
| - var currentOffset = this._rulerEnabled ? UI.FlameChart.HeaderHeight : 2;
|
| - var visible = true;
|
| - /** @type !Array<{nestingLevel: number, visible: boolean}> */
|
| - var groupStack = [{nestingLevel: -1, visible: true}];
|
| - var lastGroupLevel = Math.max(levelCount, groups.peekLast().startLevel + 1);
|
| - for (var level = 0; level < lastGroupLevel; ++level) {
|
| - while (groupIndex < groups.length - 1 && level === groups[groupIndex + 1].startLevel) {
|
| - ++groupIndex;
|
| - var style = groups[groupIndex].style;
|
| - var nextLevel = true;
|
| - while (groupStack.peekLast().nestingLevel >= style.nestingLevel) {
|
| - groupStack.pop();
|
| - nextLevel = false;
|
| - }
|
| - var thisGroupIsVisible =
|
| - groupIndex >= 0 && this._isGroupCollapsible(groupIndex) ? groups[groupIndex].expanded : true;
|
| - var parentGroupIsVisible = groupStack.peekLast().visible;
|
| - visible = thisGroupIsVisible && parentGroupIsVisible;
|
| - groupStack.push({nestingLevel: style.nestingLevel, visible: visible});
|
| - if (parentGroupIsVisible)
|
| - currentOffset += nextLevel ? 0 : style.padding;
|
| - this._groupOffsets[groupIndex] = currentOffset;
|
| - if (parentGroupIsVisible && !style.shareHeaderLine)
|
| - currentOffset += style.height;
|
| - }
|
| - var isFirstOnLevel = groupIndex >= 0 && level === groups[groupIndex].startLevel;
|
| - var thisLevelIsVisible = visible || isFirstOnLevel && groups[groupIndex].style.useFirstLineForOverview;
|
| - if (level < levelCount) {
|
| - this._visibleLevels[level] = thisLevelIsVisible;
|
| - this._visibleLevelOffsets[level] = currentOffset;
|
| - }
|
| - if (thisLevelIsVisible || (parentGroupIsVisible && style.shareHeaderLine && isFirstOnLevel))
|
| - currentOffset += this._barHeight;
|
| - }
|
| - if (groupIndex >= 0)
|
| - this._groupOffsets[groupIndex + 1] = currentOffset;
|
| - this._visibleLevelOffsets[level] = currentOffset;
|
| - }
|
| -
|
| - /**
|
| - * @param {number} index
|
| - */
|
| - _isGroupCollapsible(index) {
|
| - var groups = this._rawTimelineData.groups || [];
|
| - var style = groups[index].style;
|
| - if (!style.shareHeaderLine || !style.collapsible)
|
| - return !!style.collapsible;
|
| - var isLastGroup = index + 1 >= groups.length;
|
| - if (!isLastGroup && groups[index + 1].style.nestingLevel > style.nestingLevel)
|
| - return true;
|
| - var nextGroupLevel = isLastGroup ? this._dataProvider.maxStackDepth() : groups[index + 1].startLevel;
|
| - // For groups that only have one line and share header line, pretend these are not collapsible.
|
| - return nextGroupLevel !== groups[index].startLevel + 1;
|
| - }
|
| -
|
| - /**
|
| - * @param {number} entryIndex
|
| - */
|
| - setSelectedEntry(entryIndex) {
|
| - if (entryIndex === -1 && !this.isDragging())
|
| - this.hideRangeSelection();
|
| - if (this._selectedEntryIndex === entryIndex)
|
| - return;
|
| - this._selectedEntryIndex = entryIndex;
|
| - this._revealEntry(entryIndex);
|
| - this._updateElementPosition(this._selectedElement, this._selectedEntryIndex);
|
| - }
|
| -
|
| - /**
|
| - * @param {!Element} element
|
| - * @param {number} entryIndex
|
| - */
|
| - _updateElementPosition(element, entryIndex) {
|
| - const elementMinWidthPx = 2;
|
| - if (element.parentElement)
|
| - element.remove();
|
| - if (entryIndex === -1)
|
| - return;
|
| - var timelineData = this._timelineData();
|
| - var startTime = timelineData.entryStartTimes[entryIndex];
|
| - var endTime = startTime + (timelineData.entryTotalTimes[entryIndex] || 0);
|
| - var barX = this._timeToPositionClipped(startTime);
|
| - var barRight = this._timeToPositionClipped(endTime);
|
| - if (barRight === 0 || barX === this._offsetWidth)
|
| - return;
|
| - var barWidth = barRight - barX;
|
| - var barCenter = barX + barWidth / 2;
|
| - barWidth = Math.max(barWidth, elementMinWidthPx);
|
| - barX = barCenter - barWidth / 2;
|
| - var barY = this._levelToHeight(timelineData.entryLevels[entryIndex]) - this.getScrollOffset();
|
| - var style = element.style;
|
| - style.left = barX + 'px';
|
| - style.top = barY + 'px';
|
| - style.width = barWidth + 'px';
|
| - style.height = this._barHeight - 1 + 'px';
|
| - this.viewportElement.appendChild(element);
|
| - }
|
| -
|
| - /**
|
| - * @param {number} time
|
| - * @return {number}
|
| - */
|
| - _timeToPositionClipped(time) {
|
| - return Number.constrain(this._timeToPosition(time), 0, this._offsetWidth);
|
| - }
|
| -
|
| - /**
|
| - * @param {number} time
|
| - * @return {number}
|
| - */
|
| - _timeToPosition(time) {
|
| - return Math.floor((time - this._minimumBoundary) * this._timeToPixel) - this._pixelWindowLeft + this._paddingLeft;
|
| - }
|
| -
|
| - /**
|
| - * @param {number} level
|
| - * @return {number}
|
| - */
|
| - _levelToHeight(level) {
|
| - return this._visibleLevelOffsets[level];
|
| - }
|
| -
|
| - _updateBoundaries() {
|
| - this._totalTime = this._dataProvider.totalTime();
|
| - this._minimumBoundary = this._dataProvider.minimumBoundary();
|
| -
|
| - var windowWidth = 1;
|
| - if (this._timeWindowRight !== Infinity) {
|
| - this._windowLeft = (this._timeWindowLeft - this._minimumBoundary) / this._totalTime;
|
| - this._windowRight = (this._timeWindowRight - this._minimumBoundary) / this._totalTime;
|
| - windowWidth = this._windowRight - this._windowLeft;
|
| - } else if (this._timeWindowLeft === Infinity) {
|
| - this._windowLeft = Infinity;
|
| - this._windowRight = Infinity;
|
| - } else {
|
| - this._windowLeft = 0;
|
| - this._windowRight = 1;
|
| - }
|
| -
|
| - var totalPixels = Math.floor((this._offsetWidth - this._paddingLeft) / windowWidth);
|
| - this._pixelWindowLeft = Math.floor(totalPixels * this._windowLeft);
|
| -
|
| - this._timeToPixel = totalPixels / this._totalTime;
|
| - this._pixelToTime = this._totalTime / totalPixels;
|
| - }
|
| -
|
| - _updateHeight() {
|
| - var height = this._levelToHeight(this._dataProvider.maxStackDepth());
|
| - this.setContentHeight(height);
|
| - }
|
| -
|
| - /**
|
| - * @override
|
| - */
|
| - onResize() {
|
| - super.onResize();
|
| - this.scheduleUpdate();
|
| - }
|
| -
|
| - /**
|
| - * @override
|
| - */
|
| - update() {
|
| - if (!this._timelineData())
|
| - return;
|
| - this._resetCanvas();
|
| - this._updateHeight();
|
| - this._updateBoundaries();
|
| - this._calculator._updateBoundaries(this);
|
| - this._draw(this._offsetWidth, this._offsetHeight);
|
| - if (!this.isDragging())
|
| - this._updateHighlight();
|
| - }
|
| -
|
| - /**
|
| - * @override
|
| - */
|
| - reset() {
|
| - super.reset();
|
| - this._highlightedMarkerIndex = -1;
|
| - this._highlightedEntryIndex = -1;
|
| - this._selectedEntryIndex = -1;
|
| - /** @type {!Map<string,!Map<string,number>>} */
|
| - this._textWidth = new Map();
|
| - this.update();
|
| - }
|
| -
|
| - _enabled() {
|
| - return this._rawTimelineDataLength !== 0;
|
| - }
|
| -};
|
| -
|
| -UI.FlameChart.HeaderHeight = 15;
|
| -
|
| -UI.FlameChart.MinimalTimeWindowMs = 0.5;
|
| -
|
| -/**
|
| - * @interface
|
| - */
|
| -UI.FlameChartDataProvider = function() {};
|
| -
|
| -/**
|
| - * @typedef {!{name: string, startLevel: number, expanded: (boolean|undefined), style: !UI.FlameChart.GroupStyle}}
|
| - */
|
| -UI.FlameChart.Group;
|
| -
|
| -/**
|
| - * @typedef {!{
|
| - * height: number,
|
| - * padding: number,
|
| - * collapsible: boolean,
|
| - * font: string,
|
| - * color: string,
|
| - * backgroundColor: string,
|
| - * nestingLevel: number,
|
| - * shareHeaderLine: (boolean|undefined),
|
| - * useFirstLineForOverview: (boolean|undefined)
|
| - * }}
|
| - */
|
| -UI.FlameChart.GroupStyle;
|
| -
|
| -/**
|
| - * @unrestricted
|
| - */
|
| -UI.FlameChart.TimelineData = class {
|
| - /**
|
| - * @param {!Array<number>|!Uint16Array} entryLevels
|
| - * @param {!Array<number>|!Float32Array} entryTotalTimes
|
| - * @param {!Array<number>|!Float64Array} entryStartTimes
|
| - * @param {?Array<!UI.FlameChart.Group>} groups
|
| - */
|
| - constructor(entryLevels, entryTotalTimes, entryStartTimes, groups) {
|
| - this.entryLevels = entryLevels;
|
| - this.entryTotalTimes = entryTotalTimes;
|
| - this.entryStartTimes = entryStartTimes;
|
| - this.groups = groups;
|
| - /** @type {!Array.<!UI.FlameChartMarker>} */
|
| - this.markers = [];
|
| - }
|
| -};
|
| -
|
| -UI.FlameChartDataProvider.prototype = {
|
| - /**
|
| - * @return {number}
|
| - */
|
| - minimumBoundary() {},
|
| -
|
| - /**
|
| - * @return {number}
|
| - */
|
| - totalTime() {},
|
| -
|
| - /**
|
| - * @param {number} value
|
| - * @param {number=} precision
|
| - * @return {string}
|
| - */
|
| - formatValue(value, precision) {},
|
| -
|
| - /**
|
| - * @return {number}
|
| - */
|
| - maxStackDepth() {},
|
| -
|
| - /**
|
| - * @return {?UI.FlameChart.TimelineData}
|
| - */
|
| - timelineData() {},
|
| -
|
| - /**
|
| - * @param {number} entryIndex
|
| - * @return {?Element}
|
| - */
|
| - prepareHighlightedEntryInfo(entryIndex) {},
|
| -
|
| - /**
|
| - * @param {number} entryIndex
|
| - * @return {boolean}
|
| - */
|
| - canJumpToEntry(entryIndex) {},
|
| -
|
| - /**
|
| - * @param {number} entryIndex
|
| - * @return {?string}
|
| - */
|
| - entryTitle(entryIndex) {},
|
| -
|
| - /**
|
| - * @param {number} entryIndex
|
| - * @return {?string}
|
| - */
|
| - entryFont(entryIndex) {},
|
| -
|
| - /**
|
| - * @param {number} entryIndex
|
| - * @return {string}
|
| - */
|
| - entryColor(entryIndex) {},
|
| -
|
| - /**
|
| - * @param {number} entryIndex
|
| - * @param {!CanvasRenderingContext2D} context
|
| - * @param {?string} text
|
| - * @param {number} barX
|
| - * @param {number} barY
|
| - * @param {number} barWidth
|
| - * @param {number} barHeight
|
| - * @param {number} unclippedBarX
|
| - * @param {number} timeToPixels
|
| - * @return {boolean}
|
| - */
|
| - decorateEntry(entryIndex, context, text, barX, barY, barWidth, barHeight, unclippedBarX, timeToPixels) {},
|
| -
|
| - /**
|
| - * @param {number} entryIndex
|
| - * @return {boolean}
|
| - */
|
| - forceDecoration(entryIndex) {},
|
| -
|
| - /**
|
| - * @param {number} entryIndex
|
| - * @return {string}
|
| - */
|
| - textColor(entryIndex) {},
|
| -};
|
| -
|
| -/**
|
| - * @interface
|
| - */
|
| -UI.FlameChartMarker = function() {};
|
| -
|
| -UI.FlameChartMarker.prototype = {
|
| - /**
|
| - * @return {number}
|
| - */
|
| - startTime() {},
|
| -
|
| - /**
|
| - * @return {string}
|
| - */
|
| - color() {},
|
| -
|
| - /**
|
| - * @return {string}
|
| - */
|
| - title() {},
|
| -
|
| - /**
|
| - * @param {!CanvasRenderingContext2D} context
|
| - * @param {number} x
|
| - * @param {number} height
|
| - * @param {number} pixelsPerMillisecond
|
| - */
|
| - draw(context, x, height, pixelsPerMillisecond) {},
|
| -};
|
| -
|
| -/** @enum {symbol} */
|
| -UI.FlameChart.Events = {
|
| - EntrySelected: Symbol('EntrySelected')
|
| -};
|
| -
|
| -/**
|
| - * @unrestricted
|
| - */
|
| -UI.FlameChart.ColorGenerator = class {
|
| - /**
|
| - * @param {!{min: number, max: number}|number=} hueSpace
|
| - * @param {!{min: number, max: number, count: (number|undefined)}|number=} satSpace
|
| - * @param {!{min: number, max: number, count: (number|undefined)}|number=} lightnessSpace
|
| - * @param {!{min: number, max: number, count: (number|undefined)}|number=} alphaSpace
|
| - */
|
| - constructor(hueSpace, satSpace, lightnessSpace, alphaSpace) {
|
| - this._hueSpace = hueSpace || {min: 0, max: 360};
|
| - this._satSpace = satSpace || 67;
|
| - this._lightnessSpace = lightnessSpace || 80;
|
| - this._alphaSpace = alphaSpace || 1;
|
| - /** @type {!Map<string, string>} */
|
| - this._colors = new Map();
|
| - }
|
| -
|
| - /**
|
| - * @param {string} id
|
| - * @param {string} color
|
| - */
|
| - setColorForID(id, color) {
|
| - this._colors.set(id, color);
|
| - }
|
| -
|
| - /**
|
| - * @param {string} id
|
| - * @return {string}
|
| - */
|
| - colorForID(id) {
|
| - var color = this._colors.get(id);
|
| - if (!color) {
|
| - color = this._generateColorForID(id);
|
| - this._colors.set(id, color);
|
| - }
|
| - return color;
|
| - }
|
| -
|
| - /**
|
| - * @param {string} id
|
| - * @return {string}
|
| - */
|
| - _generateColorForID(id) {
|
| - var hash = String.hashCode(id);
|
| - var h = this._indexToValueInSpace(hash, this._hueSpace);
|
| - var s = this._indexToValueInSpace(hash >> 8, this._satSpace);
|
| - var l = this._indexToValueInSpace(hash >> 16, this._lightnessSpace);
|
| - var a = this._indexToValueInSpace(hash >> 24, this._alphaSpace);
|
| - return 'hsla(' + h + ', ' + s + '%, ' + l + '%, ' + a + ')';
|
| - }
|
| -
|
| - /**
|
| - * @param {number} index
|
| - * @param {!{min: number, max: number, count: (number|undefined)}|number} space
|
| - * @return {number}
|
| - */
|
| - _indexToValueInSpace(index, space) {
|
| - if (typeof space === 'number')
|
| - return space;
|
| - var count = space.count || space.max - space.min;
|
| - index %= count;
|
| - return space.min + Math.floor(index / (count - 1) * (space.max - space.min));
|
| - }
|
| -};
|
| -
|
| -/**
|
| - * @implements {UI.TimelineGrid.Calculator}
|
| - * @unrestricted
|
| - */
|
| -UI.FlameChart.Calculator = class {
|
| - /**
|
| - * @param {!UI.FlameChartDataProvider} dataProvider
|
| - */
|
| - constructor(dataProvider) {
|
| - this._dataProvider = dataProvider;
|
| - this._paddingLeft = 0;
|
| - }
|
| -
|
| - /**
|
| - * @override
|
| - * @return {number}
|
| - */
|
| - paddingLeft() {
|
| - return this._paddingLeft;
|
| - }
|
| -
|
| - /**
|
| - * @param {!UI.FlameChart} mainPane
|
| - */
|
| - _updateBoundaries(mainPane) {
|
| - this._totalTime = mainPane._dataProvider.totalTime();
|
| - this._zeroTime = mainPane._dataProvider.minimumBoundary();
|
| - this._minimumBoundaries = this._zeroTime + mainPane._windowLeft * this._totalTime;
|
| - this._maximumBoundaries = this._zeroTime + mainPane._windowRight * this._totalTime;
|
| - this._paddingLeft = mainPane._paddingLeft;
|
| - this._width = mainPane._offsetWidth - this._paddingLeft;
|
| - this._timeToPixel = this._width / this.boundarySpan();
|
| - }
|
| -
|
| - /**
|
| - * @override
|
| - * @param {number} time
|
| - * @return {number}
|
| - */
|
| - computePosition(time) {
|
| - return Math.round((time - this._minimumBoundaries) * this._timeToPixel + this._paddingLeft);
|
| - }
|
| -
|
| - /**
|
| - * @override
|
| - * @param {number} value
|
| - * @param {number=} precision
|
| - * @return {string}
|
| - */
|
| - formatValue(value, precision) {
|
| - return this._dataProvider.formatValue(value - this._zeroTime, precision);
|
| - }
|
| -
|
| - /**
|
| - * @override
|
| - * @return {number}
|
| - */
|
| - maximumBoundary() {
|
| - return this._maximumBoundaries;
|
| - }
|
| -
|
| - /**
|
| - * @override
|
| - * @return {number}
|
| - */
|
| - minimumBoundary() {
|
| - return this._minimumBoundaries;
|
| - }
|
| -
|
| - /**
|
| - * @override
|
| - * @return {number}
|
| - */
|
| - zeroTime() {
|
| - return this._zeroTime;
|
| - }
|
| -
|
| - /**
|
| - * @override
|
| - * @return {number}
|
| - */
|
| - boundarySpan() {
|
| - return this._maximumBoundaries - this._minimumBoundaries;
|
| - }
|
| -};
|
|
|