Index: Source/devtools/front_end/components/FlameChart.js |
diff --git a/Source/devtools/front_end/components/FlameChart.js b/Source/devtools/front_end/components/FlameChart.js |
deleted file mode 100644 |
index 15cd67d576ee0de431f622e800e0ec4aa246bd05..0000000000000000000000000000000000000000 |
--- a/Source/devtools/front_end/components/FlameChart.js |
+++ /dev/null |
@@ -1,1319 +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 |
- */ |
-WebInspector.FlameChartDelegate = function() { } |
- |
-WebInspector.FlameChartDelegate.prototype = { |
- /** |
- * @param {number} startTime |
- * @param {number} endTime |
- */ |
- requestWindowTimes: function(startTime, endTime) { }, |
- |
- /** |
- * @param {number} startTime |
- * @param {number} endTime |
- */ |
- updateBoxSelection: function(startTime, endTime) { } |
-} |
- |
-/** |
- * @constructor |
- * @extends {WebInspector.HBox} |
- * @param {!WebInspector.FlameChartDataProvider} dataProvider |
- * @param {!WebInspector.FlameChartDelegate} flameChartDelegate |
- * @param {boolean} isTopDown |
- */ |
-WebInspector.FlameChart = function(dataProvider, flameChartDelegate, isTopDown) |
-{ |
- WebInspector.HBox.call(this, true); |
- this.contentElement.appendChild(WebInspector.View.createStyleElement("components/flameChart.css")); |
- this.contentElement.classList.add("flame-chart-main-pane"); |
- this._flameChartDelegate = flameChartDelegate; |
- this._isTopDown = isTopDown; |
- |
- this._calculator = new WebInspector.FlameChart.Calculator(); |
- |
- this._canvas = this.contentElement.createChild("canvas"); |
- this._canvas.tabIndex = 1; |
- this.setDefaultFocusedElement(this._canvas); |
- this._canvas.addEventListener("mousemove", this._onMouseMove.bind(this), false); |
- this._canvas.addEventListener("mousewheel", this._onMouseWheel.bind(this), false); |
- this._canvas.addEventListener("click", this._onClick.bind(this), false); |
- this._canvas.addEventListener("keydown", this._onKeyDown.bind(this), false); |
- WebInspector.installDragHandle(this._canvas, this._startCanvasDragging.bind(this), this._canvasDragging.bind(this), this._endCanvasDragging.bind(this), "move", null); |
- |
- this._vScrollElement = this.contentElement.createChild("div", "flame-chart-v-scroll"); |
- this._vScrollContent = this._vScrollElement.createChild("div"); |
- this._vScrollElement.addEventListener("scroll", this.scheduleUpdate.bind(this), false); |
- |
- this._entryInfo = this.contentElement.createChild("div", "flame-chart-entry-info"); |
- this._markerHighlighElement = this.contentElement.createChild("div", "flame-chart-marker-highlight-element"); |
- this._highlightElement = this.contentElement.createChild("div", "flame-chart-highlight-element"); |
- this._selectedElement = this.contentElement.createChild("div", "flame-chart-selected-element"); |
- this._selectionOverlay = this.contentElement.createChild("div", "flame-chart-selection-overlay hidden"); |
- this._selectedTimeSpanLabel = this._selectionOverlay.createChild("div", "time-span"); |
- |
- this._dataProvider = dataProvider; |
- |
- this._windowLeft = 0.0; |
- this._windowRight = 1.0; |
- this._windowWidth = 1.0; |
- this._timeWindowLeft = 0; |
- this._timeWindowRight = Infinity; |
- this._barHeight = dataProvider.barHeight(); |
- this._barHeightDelta = this._isTopDown ? -this._barHeight : this._barHeight; |
- this._minWidth = 1; |
- this._paddingLeft = this._dataProvider.paddingLeft(); |
- this._markerPadding = 2; |
- this._markerRadius = this._barHeight / 2 - this._markerPadding; |
- this._highlightedMarkerIndex = -1; |
- this._highlightedEntryIndex = -1; |
- this._selectedEntryIndex = -1; |
- this._rawTimelineDataLength = 0; |
- this._textWidth = {}; |
-} |
- |
-WebInspector.FlameChart.DividersBarHeight = 20; |
- |
-WebInspector.FlameChart.MinimalTimeWindowMs = 0.01; |
- |
-/** |
- * @interface |
- */ |
-WebInspector.FlameChartDataProvider = function() |
-{ |
-} |
- |
-/** |
- * @constructor |
- * @param {!Array.<number>|!Uint8Array} entryLevels |
- * @param {!Array.<number>|!Float32Array} entryTotalTimes |
- * @param {!Array.<number>|!Float64Array} entryStartTimes |
- */ |
-WebInspector.FlameChart.TimelineData = function(entryLevels, entryTotalTimes, entryStartTimes) |
-{ |
- this.entryLevels = entryLevels; |
- this.entryTotalTimes = entryTotalTimes; |
- this.entryStartTimes = entryStartTimes; |
- /** @type {!Array.<number>} */ |
- this.markerTimestamps = []; |
-} |
- |
-WebInspector.FlameChartDataProvider.prototype = { |
- /** |
- * @return {number} |
- */ |
- barHeight: function() { }, |
- |
- /** |
- * @param {number} startTime |
- * @param {number} endTime |
- * @return {?Array.<number>} |
- */ |
- dividerOffsets: function(startTime, endTime) { }, |
- |
- /** |
- * @param {number} index |
- * @return {string} |
- */ |
- markerColor: function(index) { }, |
- |
- /** |
- * @param {number} index |
- * @return {string} |
- */ |
- markerTitle: function(index) { }, |
- |
- /** |
- * @param {number} index |
- * @return {boolean} |
- */ |
- isTallMarker: function(index) { }, |
- |
- /** |
- * @return {number} |
- */ |
- minimumBoundary: function() { }, |
- |
- /** |
- * @return {number} |
- */ |
- totalTime: function() { }, |
- |
- /** |
- * @return {number} |
- */ |
- maxStackDepth: function() { }, |
- |
- /** |
- * @return {?WebInspector.FlameChart.TimelineData} |
- */ |
- timelineData: function() { }, |
- |
- /** |
- * @param {number} entryIndex |
- * @return {?Array.<!{title: string, text: string}>} |
- */ |
- prepareHighlightedEntryInfo: function(entryIndex) { }, |
- |
- /** |
- * @param {number} entryIndex |
- * @return {boolean} |
- */ |
- canJumpToEntry: function(entryIndex) { }, |
- |
- /** |
- * @param {number} entryIndex |
- * @return {?string} |
- */ |
- entryTitle: function(entryIndex) { }, |
- |
- /** |
- * @param {number} entryIndex |
- * @return {?string} |
- */ |
- entryFont: function(entryIndex) { }, |
- |
- /** |
- * @param {number} entryIndex |
- * @return {string} |
- */ |
- entryColor: function(entryIndex) { }, |
- |
- /** |
- * @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} timeToPosition |
- * @return {boolean} |
- */ |
- decorateEntry: function(entryIndex, context, text, barX, barY, barWidth, barHeight, timeToPosition) { }, |
- |
- /** |
- * @param {number} entryIndex |
- * @return {boolean} |
- */ |
- forceDecoration: function(entryIndex) { }, |
- |
- /** |
- * @param {number} entryIndex |
- * @return {string} |
- */ |
- textColor: function(entryIndex) { }, |
- |
- /** |
- * @return {number} |
- */ |
- textBaseline: function() { }, |
- |
- /** |
- * @return {number} |
- */ |
- textPadding: function() { }, |
- |
- /** |
- * @return {?{startTime: number, endTime: number}} |
- */ |
- highlightTimeRange: function(entryIndex) { }, |
- |
- /** |
- * @return {number} |
- */ |
- paddingLeft: function() { }, |
-} |
- |
-WebInspector.FlameChart.Events = { |
- EntrySelected: "EntrySelected" |
-} |
- |
- |
-/** |
- * @constructor |
- * @param {!{min: number, max: number, count: number}|number=} hueSpace |
- * @param {!{min: number, max: number, count: number}|number=} satSpace |
- * @param {!{min: number, max: number, count: number}|number=} lightnessSpace |
- * @param {!{min: number, max: number, count: number}|number=} alphaSpace |
- */ |
-WebInspector.FlameChart.ColorGenerator = function(hueSpace, satSpace, lightnessSpace, alphaSpace) |
-{ |
- this._hueSpace = hueSpace || { min: 0, max: 360, count: 20 }; |
- this._satSpace = satSpace || 67; |
- this._lightnessSpace = lightnessSpace || 80; |
- this._alphaSpace = alphaSpace || 1; |
- this._colors = {}; |
-} |
- |
-WebInspector.FlameChart.ColorGenerator.prototype = { |
- /** |
- * @param {string} id |
- * @param {string|!CanvasGradient} color |
- */ |
- setColorForID: function(id, color) |
- { |
- this._colors[id] = color; |
- }, |
- |
- /** |
- * @param {string} id |
- * @return {string} |
- */ |
- colorForID: function(id) |
- { |
- var color = this._colors[id]; |
- if (!color) { |
- color = this._generateColorForID(id); |
- this._colors[id] = color; |
- } |
- return color; |
- }, |
- |
- /** |
- * @param {string} id |
- * @return {string} |
- */ |
- _generateColorForID: function(id) |
- { |
- var hash = id.hashCode(); |
- var h = this._indexToValueInSpace(hash, this._hueSpace); |
- var s = this._indexToValueInSpace(hash, this._satSpace); |
- var l = this._indexToValueInSpace(hash, this._lightnessSpace); |
- var a = this._indexToValueInSpace(hash, this._alphaSpace); |
- return "hsla(" + h + ", " + s + "%, " + l + "%, " + a + ")"; |
- }, |
- |
- /** |
- * @param {number} index |
- * @param {!{min: number, max: number, count: number}|number} space |
- * @return {number} |
- */ |
- _indexToValueInSpace: function(index, space) |
- { |
- if (typeof space === "number") |
- return space; |
- index %= space.count; |
- return space.min + Math.floor(index / space.count * (space.max - space.min)); |
- } |
-} |
- |
- |
-/** |
- * @constructor |
- * @implements {WebInspector.TimelineGrid.Calculator} |
- */ |
-WebInspector.FlameChart.Calculator = function() |
-{ |
- this._paddingLeft = 0; |
-} |
- |
-WebInspector.FlameChart.Calculator.prototype = { |
- /** |
- * @return {number} |
- */ |
- paddingLeft: function() |
- { |
- return this._paddingLeft; |
- }, |
- |
- /** |
- * @param {!WebInspector.FlameChart} mainPane |
- */ |
- _updateBoundaries: function(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._canvas.width / window.devicePixelRatio - this._paddingLeft; |
- this._timeToPixel = this._width / this.boundarySpan(); |
- }, |
- |
- /** |
- * @param {number} time |
- * @return {number} |
- */ |
- computePosition: function(time) |
- { |
- return Math.round((time - this._minimumBoundaries) * this._timeToPixel + this._paddingLeft); |
- }, |
- |
- /** |
- * @param {number} value |
- * @param {number=} precision |
- * @return {string} |
- */ |
- formatTime: function(value, precision) |
- { |
- return Number.preciseMillisToString(value - this._zeroTime, precision); |
- }, |
- |
- /** |
- * @return {number} |
- */ |
- maximumBoundary: function() |
- { |
- return this._maximumBoundaries; |
- }, |
- |
- /** |
- * @return {number} |
- */ |
- minimumBoundary: function() |
- { |
- return this._minimumBoundaries; |
- }, |
- |
- /** |
- * @return {number} |
- */ |
- zeroTime: function() |
- { |
- return this._zeroTime; |
- }, |
- |
- /** |
- * @return {number} |
- */ |
- boundarySpan: function() |
- { |
- return this._maximumBoundaries - this._minimumBoundaries; |
- } |
-} |
- |
-WebInspector.FlameChart.prototype = { |
- _resetCanvas: function() |
- { |
- 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 {?WebInspector.FlameChart.TimelineData} |
- */ |
- _timelineData: function() |
- { |
- var timelineData = this._dataProvider.timelineData(); |
- if (timelineData !== this._rawTimelineData || timelineData.entryStartTimes.length !== this._rawTimelineDataLength) |
- this._processTimelineData(timelineData); |
- return this._rawTimelineData; |
- }, |
- |
- _cancelAnimation: function() |
- { |
- if (this._cancelWindowTimesAnimation) { |
- this._timeWindowLeft = this._pendingAnimationTimeLeft; |
- this._timeWindowRight = this._pendingAnimationTimeRight; |
- this._cancelWindowTimesAnimation(); |
- delete this._cancelWindowTimesAnimation; |
- } |
- }, |
- |
- _revealEntry: function(entryIndex) |
- { |
- var timelineData = this._timelineData(); |
- if (!timelineData) |
- return; |
- var entryStartTime = timelineData.entryStartTimes[entryIndex]; |
- var entryTotalTime = timelineData.entryTotalTimes[entryIndex]; |
- var entryEndTime = entryStartTime + entryTotalTime; |
- var minEntryTimeWindow = Math.min(entryTotalTime, this._timeWindowRight - this._timeWindowLeft); |
- |
- var y = this._levelToHeight(timelineData.entryLevels[entryIndex]); |
- if (y < this._vScrollElement.scrollTop) |
- this._vScrollElement.scrollTop = y; |
- else if (y > this._vScrollElement.scrollTop + this._offsetHeight + this._barHeightDelta) |
- this._vScrollElement.scrollTop = y - this._offsetHeight - this._barHeightDelta; |
- |
- if (this._timeWindowLeft > entryEndTime) { |
- var delta = this._timeWindowLeft - entryEndTime + minEntryTimeWindow; |
- this._flameChartDelegate.requestWindowTimes(this._timeWindowLeft - delta, this._timeWindowRight - delta); |
- } else if (this._timeWindowRight < entryStartTime) { |
- var delta = entryStartTime - this._timeWindowRight + minEntryTimeWindow; |
- this._flameChartDelegate.requestWindowTimes(this._timeWindowLeft + delta, this._timeWindowRight + delta); |
- } |
- }, |
- |
- /** |
- * @param {number} startTime |
- * @param {number} endTime |
- */ |
- setWindowTimes: function(startTime, endTime) |
- { |
- if (this._muteAnimation || this._timeWindowLeft === 0 || this._timeWindowRight === Infinity || (startTime === 0 && endTime === Infinity)) { |
- // Initial setup. |
- this._timeWindowLeft = startTime; |
- this._timeWindowRight = endTime; |
- this.scheduleUpdate(); |
- return; |
- } |
- |
- this._cancelAnimation(); |
- this._cancelWindowTimesAnimation = WebInspector.animateFunction(this._animateWindowTimes.bind(this), |
- [{from: this._timeWindowLeft, to: startTime}, {from: this._timeWindowRight, to: endTime}], 5, |
- this._animationCompleted.bind(this)); |
- this._pendingAnimationTimeLeft = startTime; |
- this._pendingAnimationTimeRight = endTime; |
- }, |
- |
- /** |
- * @param {number} startTime |
- * @param {number} endTime |
- */ |
- _animateWindowTimes: function(startTime, endTime) |
- { |
- this._timeWindowLeft = startTime; |
- this._timeWindowRight = endTime; |
- this.update(); |
- }, |
- |
- _animationCompleted: function() |
- { |
- delete this._cancelWindowTimesAnimation; |
- }, |
- |
- /** |
- * @param {!MouseEvent} event |
- */ |
- _startCanvasDragging: function(event) |
- { |
- if (event.shiftKey) { |
- this._startBoxSelection(event); |
- this._isDragging = true; |
- return true; |
- } |
- if (!this._timelineData() || this._timeWindowRight === Infinity) |
- return false; |
- this._isDragging = true; |
- this._maxDragOffset = 0; |
- this._dragStartPointX = event.pageX; |
- this._dragStartPointY = event.pageY; |
- this._dragStartScrollTop = this._vScrollElement.scrollTop; |
- this._dragStartWindowLeft = this._timeWindowLeft; |
- this._dragStartWindowRight = this._timeWindowRight; |
- this._canvas.style.cursor = ""; |
- |
- return true; |
- }, |
- |
- /** |
- * @param {!MouseEvent} event |
- */ |
- _canvasDragging: function(event) |
- { |
- if (this._isSelecting) { |
- this._updateBoxSelection(event); |
- return; |
- } |
- var pixelShift = this._dragStartPointX - event.pageX; |
- this._dragStartPointX = event.pageX; |
- this._muteAnimation = true; |
- this._handlePanGesture(pixelShift * this._pixelToTime); |
- this._muteAnimation = false; |
- |
- var pixelScroll = this._dragStartPointY - event.pageY; |
- this._vScrollElement.scrollTop = this._dragStartScrollTop + pixelScroll; |
- this._maxDragOffset = Math.max(this._maxDragOffset, Math.abs(pixelShift)); |
- }, |
- |
- _endCanvasDragging: function() |
- { |
- this._hideBoxSelection(); |
- this._isDragging = false; |
- }, |
- |
- /** |
- * @param {!MouseEvent} event |
- */ |
- _startBoxSelection: function(event) |
- { |
- this._selectionOffsetShiftX = event.offsetX - event.pageX; |
- this._selectionOffsetShiftY = event.offsetY - event.pageY; |
- this._selectionStartX = event.offsetX; |
- this._selectionStartY = event.offsetY; |
- this._isSelecting = true; |
- var style = this._selectionOverlay.style; |
- style.left = this._selectionStartX + "px"; |
- style.top = this._selectionStartY + "px"; |
- style.width = "1px"; |
- style.height = "1px"; |
- this._selectedTimeSpanLabel.textContent = ""; |
- this._selectionOverlay.classList.remove("hidden"); |
- }, |
- |
- _hideBoxSelection: function() |
- { |
- this._selectionOffsetShiftX = null; |
- this._selectionOffsetShiftY = null; |
- this._selectionStartX = null; |
- this._selectionStartY = null; |
- this._isSelecting = false; |
- this._selectionOverlay.classList.add("hidden"); |
- }, |
- |
- /** |
- * @param {!MouseEvent} event |
- */ |
- _updateBoxSelection: function(event) |
- { |
- var x = event.pageX + this._selectionOffsetShiftX; |
- var y = event.pageY + this._selectionOffsetShiftY; |
- x = Number.constrain(x, 0, this._offsetWidth); |
- y = Number.constrain(y, 0, this._offsetHeight); |
- var style = this._selectionOverlay.style; |
- style.left = Math.min(x, this._selectionStartX) + "px"; |
- style.top = Math.min(y, this._selectionStartY) + "px"; |
- var selectionWidth = Math.abs(x - this._selectionStartX) |
- style.width = selectionWidth + "px"; |
- style.height = Math.abs(y - this._selectionStartY) + "px"; |
- |
- var timeSpan = selectionWidth * this._pixelToTime; |
- this._selectedTimeSpanLabel.textContent = Number.preciseMillisToString(timeSpan, 2); |
- var start = this._cursorTime(this._selectionStartX); |
- var end = this._cursorTime(x); |
- if (end > start) |
- this._flameChartDelegate.updateBoxSelection(start, end); |
- else |
- this._flameChartDelegate.updateBoxSelection(end, start); |
- }, |
- |
- /** |
- * @param {!Event} event |
- */ |
- _onMouseMove: function(event) |
- { |
- this._lastMouseOffsetX = event.offsetX; |
- |
- if (!this._enabled()) |
- return; |
- |
- if (this._isDragging) |
- return; |
- |
- var inDividersBar = event.offsetY < WebInspector.FlameChart.DividersBarHeight; |
- this._highlightedMarkerIndex = inDividersBar ? this._markerIndexAtPosition(event.offsetX) : -1; |
- this._updateMarkerHighlight(); |
- if (inDividersBar) |
- return; |
- |
- var entryIndex = this._coordinatesToEntryIndex(event.offsetX, event.offsetY); |
- |
- if (this._highlightedEntryIndex === entryIndex) |
- return; |
- |
- if (entryIndex === -1 || !this._dataProvider.canJumpToEntry(entryIndex)) |
- this._canvas.style.cursor = "default"; |
- else |
- this._canvas.style.cursor = "pointer"; |
- |
- this._highlightedEntryIndex = entryIndex; |
- |
- this._updateElementPosition(this._highlightElement, this._highlightedEntryIndex); |
- this._entryInfo.removeChildren(); |
- |
- if (this._highlightedEntryIndex === -1) |
- return; |
- |
- if (!this._isDragging) { |
- var entryInfo = this._dataProvider.prepareHighlightedEntryInfo(this._highlightedEntryIndex); |
- if (entryInfo) |
- this._entryInfo.appendChild(this._buildEntryInfo(entryInfo)); |
- } |
- }, |
- |
- _onClick: function() |
- { |
- 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; |
- if (this._highlightedEntryIndex === -1) |
- return; |
- this.dispatchEventToListeners(WebInspector.FlameChart.Events.EntrySelected, this._highlightedEntryIndex); |
- }, |
- |
- /** |
- * @param {!Event} e |
- */ |
- _onMouseWheel: function(e) |
- { |
- if (!this._enabled()) |
- return; |
- // Pan vertically when shift down only. |
- var panVertically = e.shiftKey && (e.wheelDeltaY || Math.abs(e.wheelDeltaX) === 120); |
- var panHorizontally = Math.abs(e.wheelDeltaX) > Math.abs(e.wheelDeltaY) && !e.shiftKey; |
- if (panVertically) { |
- this._vScrollElement.scrollTop -= (e.wheelDeltaY || e.wheelDeltaX) / 120 * this._offsetHeight / 8; |
- } else if (panHorizontally) { |
- var shift = -e.wheelDeltaX * this._pixelToTime; |
- this._muteAnimation = true; |
- this._handlePanGesture(shift); |
- this._muteAnimation = false; |
- } else { // Zoom. |
- const mouseWheelZoomSpeed = 1 / 120; |
- this._handleZoomGesture(Math.pow(1.2, -(e.wheelDeltaY || e.wheelDeltaX) * mouseWheelZoomSpeed) - 1); |
- } |
- |
- // Block swipe gesture. |
- e.consume(true); |
- }, |
- |
- /** |
- * @param {!Event} e |
- */ |
- _onKeyDown: function(e) |
- { |
- if (e.altKey || e.ctrlKey || e.metaKey) |
- return; |
- var zoomMultiplier = e.shiftKey ? 0.8 : 0.3; |
- var panMultiplier = e.shiftKey ? 320 : 80; |
- if (e.keyCode === "A".charCodeAt(0)) { |
- this._handlePanGesture(-panMultiplier * this._pixelToTime); |
- e.consume(true); |
- } else if (e.keyCode === "D".charCodeAt(0)) { |
- this._handlePanGesture(panMultiplier * this._pixelToTime); |
- e.consume(true); |
- } else if (e.keyCode === "W".charCodeAt(0)) { |
- this._handleZoomGesture(-zoomMultiplier); |
- e.consume(true); |
- } else if (e.keyCode === "S".charCodeAt(0)) { |
- this._handleZoomGesture(zoomMultiplier); |
- e.consume(true); |
- } |
- }, |
- |
- /** |
- * @param {number} zoom |
- */ |
- _handleZoomGesture: function(zoom) |
- { |
- this._cancelAnimation(); |
- var bounds = this._windowForGesture(); |
- var cursorTime = this._cursorTime(this._lastMouseOffsetX); |
- bounds.left += (bounds.left - cursorTime) * zoom; |
- bounds.right += (bounds.right - cursorTime) * zoom; |
- this._requestWindowTimes(bounds); |
- }, |
- |
- /** |
- * @param {number} shift |
- */ |
- _handlePanGesture: function(shift) |
- { |
- this._cancelAnimation(); |
- var bounds = this._windowForGesture(); |
- shift = Number.constrain(shift, this._minimumBoundary - bounds.left, this._totalTime + this._minimumBoundary - bounds.right); |
- bounds.left += shift; |
- bounds.right += shift; |
- this._requestWindowTimes(bounds); |
- }, |
- |
- /** |
- * @return {{left: number, right: number}} |
- */ |
- _windowForGesture: function() |
- { |
- var windowLeft = this._timeWindowLeft ? this._timeWindowLeft : this._dataProvider.minimumBoundary(); |
- var windowRight = this._timeWindowRight !== Infinity ? this._timeWindowRight : this._dataProvider.minimumBoundary() + this._dataProvider.totalTime(); |
- return {left: windowLeft, right: windowRight}; |
- }, |
- |
- /** |
- * @param {{left: number, right: number}} bounds |
- */ |
- _requestWindowTimes: function(bounds) |
- { |
- bounds.left = Number.constrain(bounds.left, this._minimumBoundary, this._totalTime + this._minimumBoundary); |
- bounds.right = Number.constrain(bounds.right, this._minimumBoundary, this._totalTime + this._minimumBoundary); |
- if (bounds.right - bounds.left < WebInspector.FlameChart.MinimalTimeWindowMs) |
- return; |
- this._flameChartDelegate.requestWindowTimes(bounds.left, bounds.right); |
- }, |
- |
- /** |
- * @param {number} x |
- * @return {number} |
- */ |
- _cursorTime: function(x) |
- { |
- return (x + this._pixelWindowLeft - this._paddingLeft) * this._pixelToTime + this._minimumBoundary; |
- }, |
- |
- /** |
- * @param {number} x |
- * @param {number} y |
- * @return {number} |
- */ |
- _coordinatesToEntryIndex: function(x, y) |
- { |
- y += this._scrollTop; |
- var timelineData = this._timelineData(); |
- if (!timelineData) |
- return -1; |
- var cursorTime = this._cursorTime(x); |
- var cursorLevel; |
- var offsetFromLevel; |
- if (this._isTopDown) { |
- cursorLevel = Math.floor((y - WebInspector.FlameChart.DividersBarHeight) / this._barHeight); |
- offsetFromLevel = y - WebInspector.FlameChart.DividersBarHeight - cursorLevel * this._barHeight; |
- } else { |
- cursorLevel = Math.floor((this._canvas.height / window.devicePixelRatio - y) / this._barHeight); |
- offsetFromLevel = this._canvas.height / window.devicePixelRatio - cursorLevel * this._barHeight; |
- } |
- 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 {WebInspector.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 |
- * @return {number} |
- */ |
- _markerIndexAtPosition: function(x) |
- { |
- var markers = this._timelineData().markerTimestamps; |
- if (!markers) |
- return -1; |
- var accurracyOffsetPx = 1; |
- var time = this._cursorTime(x); |
- var leftTime = this._cursorTime(x - accurracyOffsetPx); |
- var rightTime = this._cursorTime(x + accurracyOffsetPx); |
- |
- /** |
- * @param {number} time |
- * @param {number} markerTimestamp |
- * @return {number} |
- */ |
- function comparator(time, markerTimestamp) |
- { |
- return time - markerTimestamp; |
- } |
- var left = markers.lowerBound(leftTime, comparator); |
- var markerIndex = -1; |
- var distance = Infinity; |
- for (var i = left; i < markers.length && markers[i] < rightTime; i++) { |
- var nextDistance = Math.abs(markers[i] - time); |
- if (nextDistance < distance) { |
- markerIndex = i; |
- distance = nextDistance; |
- } |
- } |
- return markerIndex; |
- }, |
- |
- /** |
- * @param {number} height |
- * @param {number} width |
- */ |
- _draw: function(width, height) |
- { |
- var timelineData = this._timelineData(); |
- if (!timelineData) |
- return; |
- |
- var context = this._canvas.getContext("2d"); |
- context.save(); |
- var ratio = window.devicePixelRatio; |
- context.scale(ratio, ratio); |
- |
- var timeWindowRight = this._timeWindowRight; |
- var timeWindowLeft = this._timeWindowLeft; |
- var timeToPixel = this._timeToPixel; |
- var pixelWindowLeft = this._pixelWindowLeft; |
- var paddingLeft = this._paddingLeft; |
- var minWidth = this._minWidth; |
- var entryTotalTimes = timelineData.entryTotalTimes; |
- var entryStartTimes = timelineData.entryStartTimes; |
- var entryLevels = timelineData.entryLevels; |
- |
- var titleIndices = new Uint32Array(timelineData.entryTotalTimes); |
- var nextTitleIndex = 0; |
- var markerIndices = new Uint32Array(timelineData.entryTotalTimes); |
- var nextMarkerIndex = 0; |
- var textPadding = this._dataProvider.textPadding(); |
- this._minTextWidth = 2 * textPadding + this._measureWidth(context, "\u2026"); |
- var minTextWidth = this._minTextWidth; |
- |
- var barHeight = this._barHeight; |
- |
- var timeToPosition = this._timeToPosition.bind(this); |
- var textBaseHeight = this._baseHeight + barHeight - this._dataProvider.textBaseline(); |
- var colorBuckets = {}; |
- var minVisibleBarLevel = Math.max(Math.floor((this._scrollTop - this._baseHeight) / barHeight), 0); |
- var maxVisibleBarLevel = Math.min(Math.floor((this._scrollTop - this._baseHeight + height) / barHeight), this._dataProvider.maxStackDepth()); |
- |
- context.translate(0, -this._scrollTop); |
- |
- function comparator(time, entryIndex) |
- { |
- return time - entryStartTimes[entryIndex]; |
- } |
- |
- for (var level = minVisibleBarLevel; level <= maxVisibleBarLevel; ++level) { |
- // 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, comparator) - 1; |
- var lastDrawOffset = Infinity; |
- for (var entryIndexOnLevel = rightIndexOnLevel; entryIndexOnLevel >= 0; --entryIndexOnLevel) { |
- var entryIndex = levelIndexes[entryIndexOnLevel]; |
- var entryStartTime = entryStartTimes[entryIndex]; |
- var entryOffsetRight = entryStartTime + (isNaN(entryTotalTimes[entryIndex]) ? 0 : entryTotalTimes[entryIndex]); |
- if (entryOffsetRight <= timeWindowLeft) |
- break; |
- |
- var barX = this._timeToPosition(entryStartTime); |
- if (barX >= lastDrawOffset) |
- continue; |
- var barRight = Math.min(this._timeToPosition(entryOffsetRight), lastDrawOffset); |
- lastDrawOffset = barX; |
- |
- var color = this._dataProvider.entryColor(entryIndex); |
- var bucket = colorBuckets[color]; |
- if (!bucket) { |
- bucket = []; |
- colorBuckets[color] = bucket; |
- } |
- bucket.push(entryIndex); |
- } |
- } |
- |
- var colors = Object.keys(colorBuckets); |
- // We don't use for-in here because it couldn't be optimized. |
- for (var c = 0; c < colors.length; ++c) { |
- var color = colors[c]; |
- context.fillStyle = color; |
- context.strokeStyle = color; |
- var indexes = colorBuckets[color]; |
- |
- // First fill the boxes. |
- context.beginPath(); |
- for (var i = 0; i < indexes.length; ++i) { |
- var entryIndex = indexes[i]; |
- var entryStartTime = entryStartTimes[entryIndex]; |
- var barX = this._timeToPosition(entryStartTime); |
- var barRight = this._timeToPosition(entryStartTime + entryTotalTimes[entryIndex]); |
- var barWidth = Math.max(barRight - barX, minWidth); |
- var barLevel = entryLevels[entryIndex]; |
- var barY = this._levelToHeight(barLevel); |
- if (isNaN(entryTotalTimes[entryIndex])) { |
- context.moveTo(barX + this._markerRadius, barY + barHeight / 2); |
- context.arc(barX, barY + barHeight / 2, this._markerRadius, 0, Math.PI * 2); |
- markerIndices[nextMarkerIndex++] = entryIndex; |
- } else { |
- context.rect(barX, barY, barWidth, barHeight); |
- if (barWidth > minTextWidth || this._dataProvider.forceDecoration(entryIndex)) |
- titleIndices[nextTitleIndex++] = entryIndex; |
- } |
- } |
- context.fill(); |
- } |
- |
- context.strokeStyle = "rgb(0, 0, 0)"; |
- context.beginPath(); |
- for (var m = 0; m < nextMarkerIndex; ++m) { |
- var entryIndex = markerIndices[m]; |
- var entryStartTime = entryStartTimes[entryIndex]; |
- var barX = this._timeToPosition(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"; |
- |
- for (var i = 0; i < nextTitleIndex; ++i) { |
- var entryIndex = titleIndices[i]; |
- var entryStartTime = entryStartTimes[entryIndex]; |
- var barX = this._timeToPosition(entryStartTime); |
- var barRight = this._timeToPosition(entryStartTime + entryTotalTimes[entryIndex]); |
- var barWidth = Math.max(barRight - barX, minWidth); |
- 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); |
- text = this._prepareText(context, text, barWidth - 2 * textPadding); |
- } |
- |
- if (this._dataProvider.decorateEntry(entryIndex, context, text, barX, barY, barWidth, barHeight, timeToPosition)) |
- continue; |
- if (!text || !text.length) |
- continue; |
- |
- context.fillStyle = this._dataProvider.textColor(entryIndex); |
- context.fillText(text, barX + textPadding, textBaseHeight - barLevel * this._barHeightDelta); |
- } |
- context.restore(); |
- |
- var offsets = this._dataProvider.dividerOffsets(this._calculator.minimumBoundary(), this._calculator.maximumBoundary()); |
- WebInspector.TimelineGrid.drawCanvasGrid(this._canvas, this._calculator, offsets); |
- this._drawMarkers(); |
- |
- this._updateElementPosition(this._highlightElement, this._highlightedEntryIndex); |
- this._updateElementPosition(this._selectedElement, this._selectedEntryIndex); |
- this._updateMarkerHighlight(); |
- }, |
- |
- _drawMarkers: function() |
- { |
- var markerTimestamps = this._timelineData().markerTimestamps; |
- /** |
- * @param {number} time |
- * @param {number} markerTimestamp |
- * @return {number} |
- */ |
- function compare(time, markerTimestamp) |
- { |
- return time - markerTimestamp; |
- } |
- var left = markerTimestamps.lowerBound(this._calculator.minimumBoundary(), compare); |
- var rightBoundary = this._calculator.maximumBoundary(); |
- |
- var context = this._canvas.getContext("2d"); |
- context.save(); |
- var ratio = window.devicePixelRatio; |
- context.scale(ratio, ratio); |
- var height = WebInspector.FlameChart.DividersBarHeight - 1; |
- context.lineWidth = 2; |
- for (var i = left; i < markerTimestamps.length; i++) { |
- var timestamp = markerTimestamps[i]; |
- if (timestamp > rightBoundary) |
- break; |
- var position = this._calculator.computePosition(timestamp); |
- context.strokeStyle = this._dataProvider.markerColor(i); |
- context.beginPath(); |
- context.moveTo(position, 0); |
- context.lineTo(position, height); |
- context.stroke(); |
- if (this._dataProvider.isTallMarker(i)) { |
- context.save() |
- context.lineWidth = 0.5; |
- context.translate(0.5, 0.5); |
- context.beginPath(); |
- context.moveTo(position, height); |
- context.setLineDash([10, 5]); |
- context.lineTo(position, this._canvas.height); |
- context.stroke(); |
- context.restore(); |
- } |
- } |
- context.restore(); |
- }, |
- |
- _updateMarkerHighlight: function() |
- { |
- var element = this._markerHighlighElement; |
- if (element.parentElement) |
- element.remove(); |
- var markerIndex = this._highlightedMarkerIndex; |
- if (markerIndex === -1) |
- return; |
- var barX = this._timeToPosition(this._timelineData().markerTimestamps[markerIndex]); |
- element.title = this._dataProvider.markerTitle(markerIndex); |
- var style = element.style; |
- style.left = barX + "px"; |
- style.backgroundColor = this._dataProvider.markerColor(markerIndex); |
- this.contentElement.appendChild(element); |
- }, |
- |
- /** |
- * @param {?WebInspector.FlameChart.TimelineData} timelineData |
- */ |
- _processTimelineData: function(timelineData) |
- { |
- if (!timelineData) { |
- this._timelineLevels = null; |
- this._rawTimelineData = null; |
- this._rawTimelineDataLength = 0; |
- return; |
- } |
- |
- 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; |
- this._rawTimelineData = timelineData; |
- this._rawTimelineDataLength = timelineData.entryStartTimes.length; |
- }, |
- |
- /** |
- * @param {number} entryIndex |
- */ |
- setSelectedEntry: function(entryIndex) |
- { |
- this._selectedEntryIndex = entryIndex; |
- this._revealEntry(entryIndex); |
- this._updateElementPosition(this._selectedElement, this._selectedEntryIndex); |
- }, |
- |
- _updateElementPosition: function(element, entryIndex) |
- { |
- if (element.parentElement) |
- element.remove(); |
- if (entryIndex === -1) |
- return; |
- var timeRange = this._dataProvider.highlightTimeRange(entryIndex); |
- if (!timeRange) |
- return; |
- var timelineData = this._timelineData(); |
- var barX = this._timeToPosition(timeRange.startTime); |
- var barRight = this._timeToPosition(timeRange.endTime); |
- if (barRight === 0 || barX === this._canvas.width) |
- return; |
- var barWidth = Math.max(barRight - barX, this._minWidth); |
- var barY = this._levelToHeight(timelineData.entryLevels[entryIndex]) - this._scrollTop; |
- var style = element.style; |
- style.left = barX + "px"; |
- style.top = barY + "px"; |
- style.width = barWidth + "px"; |
- style.height = this._barHeight + "px"; |
- this.contentElement.appendChild(element); |
- }, |
- |
- /** |
- * @param {number} time |
- */ |
- _timeToPosition: function(time) |
- { |
- var value = Math.floor((time - this._minimumBoundary) * this._timeToPixel) - this._pixelWindowLeft + this._paddingLeft; |
- return Math.min(this._canvas.width, Math.max(0, value)); |
- }, |
- |
- _levelToHeight: function(level) |
- { |
- return this._baseHeight - level * this._barHeightDelta; |
- }, |
- |
- _buildEntryInfo: function(entryInfo) |
- { |
- var infoTable = createElementWithClass("table", "info-table"); |
- for (var i = 0; i < entryInfo.length; ++i) { |
- var row = infoTable.createChild("tr"); |
- row.createChild("td", "title").textContent = entryInfo[i].title; |
- row.createChild("td").textContent = entryInfo[i].text; |
- } |
- return infoTable; |
- }, |
- |
- /** |
- * @param {!CanvasRenderingContext2D} context |
- * @param {string} title |
- * @param {number} maxSize |
- * @return {string} |
- */ |
- _prepareText: function(context, title, maxSize) |
- { |
- var titleWidth = this._measureWidth(context, title); |
- if (maxSize >= titleWidth) |
- return title; |
- |
- var l = 2; |
- var r = title.length; |
- while (l < r) { |
- var m = (l + r) >> 1; |
- if (this._measureWidth(context, title.trimMiddle(m)) <= maxSize) |
- l = m + 1; |
- else |
- r = m; |
- } |
- title = title.trimMiddle(r - 1); |
- return title !== "\u2026" ? title : ""; |
- }, |
- |
- /** |
- * @param {!CanvasRenderingContext2D} context |
- * @param {string} text |
- * @return {number} |
- */ |
- _measureWidth: function(context, text) |
- { |
- if (text.length > 20) |
- return context.measureText(text).width; |
- |
- var font = context.font; |
- var textWidths = this._textWidth[font]; |
- if (!textWidths) { |
- textWidths = {}; |
- this._textWidth[font] = textWidths; |
- } |
- var width = textWidths[text]; |
- if (!width) { |
- width = context.measureText(text).width; |
- textWidths[text] = width; |
- } |
- return width; |
- }, |
- |
- _updateBoundaries: function() |
- { |
- this._totalTime = this._dataProvider.totalTime(); |
- this._minimumBoundary = this._dataProvider.minimumBoundary(); |
- |
- if (this._timeWindowRight !== Infinity) { |
- this._windowLeft = (this._timeWindowLeft - this._minimumBoundary) / this._totalTime; |
- this._windowRight = (this._timeWindowRight - this._minimumBoundary) / this._totalTime; |
- this._windowWidth = this._windowRight - this._windowLeft; |
- } else { |
- this._windowLeft = 0; |
- this._windowRight = 1; |
- this._windowWidth = 1; |
- } |
- |
- this._pixelWindowWidth = this._offsetWidth - this._paddingLeft; |
- this._totalPixels = Math.floor(this._pixelWindowWidth / this._windowWidth); |
- this._pixelWindowLeft = Math.floor(this._totalPixels * this._windowLeft); |
- this._pixelWindowRight = Math.floor(this._totalPixels * this._windowRight); |
- |
- this._timeToPixel = this._totalPixels / this._totalTime; |
- this._pixelToTime = this._totalTime / this._totalPixels; |
- this._paddingLeftTime = this._paddingLeft / this._timeToPixel; |
- |
- this._baseHeight = this._isTopDown ? WebInspector.FlameChart.DividersBarHeight : this._offsetHeight - this._barHeight; |
- |
- this._totalHeight = this._levelToHeight(this._dataProvider.maxStackDepth() + 1); |
- this._vScrollContent.style.height = this._totalHeight + "px"; |
- this._scrollTop = this._vScrollElement.scrollTop; |
- this._updateScrollBar(); |
- }, |
- |
- onResize: function() |
- { |
- this._updateScrollBar(); |
- this.scheduleUpdate(); |
- }, |
- |
- _updateScrollBar: function() |
- { |
- var showScroll = this._totalHeight > this._offsetHeight; |
- this._vScrollElement.classList.toggle("hidden", !showScroll); |
- this._offsetWidth = this.contentElement.offsetWidth - (WebInspector.isMac() ? 0 : this._vScrollElement.offsetWidth); |
- this._offsetHeight = this.contentElement.offsetHeight; |
- }, |
- |
- scheduleUpdate: function() |
- { |
- if (this._updateTimerId || this._cancelWindowTimesAnimation) |
- return; |
- this._updateTimerId = requestAnimationFrame(this.update.bind(this)); |
- }, |
- |
- update: function() |
- { |
- this._updateTimerId = 0; |
- if (!this._timelineData()) |
- return; |
- this._resetCanvas(); |
- this._updateBoundaries(); |
- this._calculator._updateBoundaries(this); |
- this._draw(this._offsetWidth, this._offsetHeight); |
- }, |
- |
- reset: function() |
- { |
- this._highlightedMarkerIndex = -1; |
- this._highlightedEntryIndex = -1; |
- this._selectedEntryIndex = -1; |
- this._textWidth = {}; |
- this.update(); |
- }, |
- |
- _enabled: function() |
- { |
- return this._rawTimelineDataLength !== 0; |
- }, |
- |
- __proto__: WebInspector.HBox.prototype |
-} |