123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815 |
- /*
- * 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.
- */
- /**
- * @constructor
- * @extends {WebInspector.View}
- * @param {WebInspector.CPUProfileView} cpuProfileView
- */
- WebInspector.FlameChart = function(cpuProfileView)
- {
- WebInspector.View.call(this);
- this.registerRequiredCSS("flameChart.css");
- this.element.className = "fill";
- this.element.id = "cpu-flame-chart";
- this._overviewContainer = this.element.createChild("div", "overview-container");
- this._overviewGrid = new WebInspector.OverviewGrid("flame-chart");
- this._overviewCanvas = this._overviewContainer.createChild("canvas", "flame-chart-overview-canvas");
- this._overviewContainer.appendChild(this._overviewGrid.element);
- this._overviewCalculator = new WebInspector.FlameChart.OverviewCalculator();
- this._overviewGrid.addEventListener(WebInspector.OverviewGrid.Events.WindowChanged, this._onWindowChanged, this);
- this._chartContainer = this.element.createChild("div", "chart-container");
- this._timelineGrid = new WebInspector.TimelineGrid();
- this._chartContainer.appendChild(this._timelineGrid.element);
- this._calculator = new WebInspector.FlameChart.Calculator();
- this._canvas = this._chartContainer.createChild("canvas");
- this._canvas.addEventListener("mousemove", this._onMouseMove.bind(this));
- WebInspector.installDragHandle(this._canvas, this._startCanvasDragging.bind(this), this._canvasDragging.bind(this), this._endCanvasDragging.bind(this), "col-resize");
- this._cpuProfileView = cpuProfileView;
- this._windowLeft = 0.0;
- this._windowRight = 1.0;
- this._barHeight = 15;
- this._minWidth = 2;
- this._paddingLeft = 15;
- this._canvas.addEventListener("mousewheel", this._onMouseWheel.bind(this), false);
- this._canvas.addEventListener("click", this._onClick.bind(this), false);
- this._linkifier = new WebInspector.Linkifier();
- this._highlightedEntryIndex = -1;
- if (!WebInspector.FlameChart._colorGenerator)
- WebInspector.FlameChart._colorGenerator = new WebInspector.FlameChart.ColorGenerator();
- }
- /**
- * @constructor
- * @implements {WebInspector.TimelineGrid.Calculator}
- */
- WebInspector.FlameChart.Calculator = function()
- {
- }
- WebInspector.FlameChart.Calculator.prototype = {
- /**
- * @param {WebInspector.FlameChart} flameChart
- */
- _updateBoundaries: function(flameChart)
- {
- this._minimumBoundaries = flameChart._windowLeft * flameChart._timelineData.totalTime;
- this._maximumBoundaries = flameChart._windowRight * flameChart._timelineData.totalTime;
- this.paddingLeft = flameChart._paddingLeft;
- this._width = flameChart._canvas.width - this.paddingLeft;
- this._timeToPixel = this._width / this.boundarySpan();
- },
- /**
- * @param {number} time
- */
- computePosition: function(time)
- {
- return (time - this._minimumBoundaries) * this._timeToPixel + this.paddingLeft;
- },
- formatTime: function(value)
- {
- return WebInspector.UIString("%s\u2009ms", Number.withThousandsSeparator(Math.round(value + this._minimumBoundaries)));
- },
- maximumBoundary: function()
- {
- return this._maximumBoundaries;
- },
- minimumBoundary: function()
- {
- return this._minimumBoundaries;
- },
- zeroTime: function()
- {
- return 0;
- },
- boundarySpan: function()
- {
- return this._maximumBoundaries - this._minimumBoundaries;
- }
- }
- /**
- * @constructor
- * @implements {WebInspector.TimelineGrid.Calculator}
- */
- WebInspector.FlameChart.OverviewCalculator = function()
- {
- }
- WebInspector.FlameChart.OverviewCalculator.prototype = {
- /**
- * @param {WebInspector.FlameChart} flameChart
- */
- _updateBoundaries: function(flameChart)
- {
- this._minimumBoundaries = 0;
- this._maximumBoundaries = flameChart._timelineData.totalTime;
- this._xScaleFactor = flameChart._canvas.width / flameChart._timelineData.totalTime;
- },
- /**
- * @param {number} time
- */
- computePosition: function(time)
- {
- return (time - this._minimumBoundaries) * this._xScaleFactor;
- },
- formatTime: function(value)
- {
- return Number.secondsToString((value + this._minimumBoundaries) / 1000);
- },
- maximumBoundary: function()
- {
- return this._maximumBoundaries;
- },
- minimumBoundary: function()
- {
- return this._minimumBoundaries;
- },
- zeroTime: function()
- {
- return this._minimumBoundaries;
- },
- boundarySpan: function()
- {
- return this._maximumBoundaries - this._minimumBoundaries;
- }
- }
- WebInspector.FlameChart.Events = {
- SelectedNode: "SelectedNode"
- }
- /**
- * @constructor
- */
- WebInspector.FlameChart.ColorGenerator = function()
- {
- this._colorPairs = {};
- this._currentColorIndex = 0;
- this._colorPairs["(idle)::0"] = this._createPair(0, 50);
- this._colorPairs["(program)::0"] = this._createPair(5, 50);
- this._colorPairs["(garbage collector)::0"] = this._createPair(10, 50);
- }
- WebInspector.FlameChart.ColorGenerator.prototype = {
- /**
- * @param {!string} id
- */
- _colorPairForID: function(id)
- {
- var colorPairs = this._colorPairs;
- var colorPair = colorPairs[id];
- if (!colorPair)
- colorPairs[id] = colorPair = this._createPair(++this._currentColorIndex);
- return colorPair;
- },
- /**
- * @param {!number} index
- * @param {number=} sat
- */
- _createPair: function(index, sat)
- {
- var hue = (index * 7 + 12 * (index % 2)) % 360;
- if (typeof sat !== "number")
- sat = 100;
- return {highlighted: "hsla(" + hue + ", " + sat + "%, 33%, 0.7)", normal: "hsla(" + hue + ", " + sat + "%, 66%, 0.7)"}
- }
- }
- /**
- * @constructor
- * @param {!Object} colorPair
- * @param {!number} depth
- * @param {!number} duration
- * @param {!number} startTime
- * @param {Object} node
- */
- WebInspector.FlameChart.Entry = function(colorPair, depth, duration, startTime, node)
- {
- this.colorPair = colorPair;
- this.depth = depth;
- this.duration = duration;
- this.startTime = startTime;
- this.node = node;
- this.selfTime = 0;
- }
- WebInspector.FlameChart.prototype = {
- /**
- * @param {!number} timeLeft
- * @param {!number} timeRight
- */
- selectRange: function(timeLeft, timeRight)
- {
- this._overviewGrid.setWindow(timeLeft / this._totalTime, timeRight / this._totalTime);
- },
- _onWindowChanged: function(event)
- {
- this._scheduleUpdate();
- },
- _startCanvasDragging: function(event)
- {
- if (!this._timelineData)
- return false;
- this._isDragging = true;
- this._wasDragged = false;
- this._dragStartPoint = event.pageX;
- this._dragStartWindowLeft = this._windowLeft;
- this._dragStartWindowRight = this._windowRight;
- return true;
- },
- _canvasDragging: function(event)
- {
- var pixelShift = this._dragStartPoint - event.pageX;
- var windowShift = pixelShift / this._totalPixels;
- var windowLeft = Math.max(0, this._dragStartWindowLeft + windowShift);
- if (windowLeft === this._windowLeft)
- return;
- windowShift = windowLeft - this._dragStartWindowLeft;
-
- var windowRight = Math.min(1, this._dragStartWindowRight + windowShift);
- if (windowRight === this._windowRight)
- return;
- windowShift = windowRight - this._dragStartWindowRight;
- this._overviewGrid.setWindow(this._dragStartWindowLeft + windowShift, this._dragStartWindowRight + windowShift);
- this._wasDragged = true;
- },
- _endCanvasDragging: function()
- {
- this._isDragging = false;
- },
- _calculateTimelineData: function()
- {
- if (this._cpuProfileView.samples)
- return this._calculateTimelineDataForSamples();
- if (this._timelineData)
- return this._timelineData;
- if (!this._cpuProfileView.profileHead)
- return null;
- var index = 0;
- var entries = [];
- function appendReversedArray(toArray, fromArray)
- {
- for (var i = fromArray.length - 1; i >= 0; --i)
- toArray.push(fromArray[i]);
- }
- var stack = [];
- appendReversedArray(stack, this._cpuProfileView.profileHead.children);
- var levelOffsets = /** @type {Array.<!number>} */ ([0]);
- var levelExitIndexes = /** @type {Array.<!number>} */ ([0]);
- var colorGenerator = WebInspector.FlameChart._colorGenerator;
- while (stack.length) {
- var level = levelOffsets.length - 1;
- var node = stack.pop();
- var offset = levelOffsets[level];
- var colorPair = colorGenerator._colorPairForID(node.functionName + ":" + node.url + ":" + node.lineNumber);
- entries.push(new WebInspector.FlameChart.Entry(colorPair, level, node.totalTime, offset, node));
- ++index;
- levelOffsets[level] += node.totalTime;
- if (node.children.length) {
- levelExitIndexes.push(stack.length);
- levelOffsets.push(offset + node.selfTime / 2);
- appendReversedArray(stack, node.children);
- }
- while (stack.length === levelExitIndexes[levelExitIndexes.length - 1]) {
- levelOffsets.pop();
- levelExitIndexes.pop();
- }
- }
- this._timelineData = {
- entries: entries,
- totalTime: this._cpuProfileView.profileHead.totalTime,
- }
- return this._timelineData;
- },
- _calculateTimelineDataForSamples: function()
- {
- if (this._timelineData)
- return this._timelineData;
- if (!this._cpuProfileView.profileHead)
- return null;
- var samples = this._cpuProfileView.samples;
- var idToNode = this._cpuProfileView._idToNode;
- var gcNode = this._cpuProfileView._gcNode;
- var samplesCount = samples.length;
- var index = 0;
- var entries = /** @type {Array.<!WebInspector.FlameChart.Entry>} */ ([]);
- var openIntervals = [];
- var stackTrace = [];
- var colorGenerator = WebInspector.FlameChart._colorGenerator;
- for (var sampleIndex = 0; sampleIndex < samplesCount; sampleIndex++) {
- var node = idToNode[samples[sampleIndex]];
- stackTrace.length = 0;
- while (node) {
- stackTrace.push(node);
- node = node.parent;
- }
- stackTrace.pop(); // Remove (root) node
- var depth = 0;
- node = stackTrace.pop();
- var intervalIndex;
- // GC samples have no stack, so we just put GC node on top of the last recoreded sample.
- if (node === gcNode) {
- while (depth < openIntervals.length) {
- intervalIndex = openIntervals[depth].index;
- entries[intervalIndex].duration += 1;
- ++depth;
- }
- // If previous stack is also GC then just continue.
- if (openIntervals.length > 0 && openIntervals.peekLast().node === node) {
- entries[intervalIndex].selfTime += 1;
- continue;
- }
- }
- while (node && depth < openIntervals.length && node === openIntervals[depth].node) {
- intervalIndex = openIntervals[depth].index;
- entries[intervalIndex].duration += 1;
- node = stackTrace.pop();
- ++depth;
- }
- if (depth < openIntervals.length)
- openIntervals.length = depth;
- if (!node) {
- entries[intervalIndex].selfTime += 1;
- continue;
- }
- while (node) {
- var colorPair = colorGenerator._colorPairForID(node.functionName + ":" + node.url + ":" + node.lineNumber);
- entries.push(new WebInspector.FlameChart.Entry(colorPair, depth, 1, sampleIndex, node));
- openIntervals.push({node: node, index: index});
- ++index;
- node = stackTrace.pop();
- ++depth;
- }
- entries[entries.length - 1].selfTime += 1;
- }
- this._timelineData = {
- entries: entries,
- totalTime: samplesCount,
- };
- return this._timelineData;
- },
- _onMouseMove: function(event)
- {
- if (this._isDragging)
- return;
- var entryIndex = this._coordinatesToEntryIndex(event.offsetX, event.offsetY);
- if (this._highlightedEntryIndex === entryIndex)
- return;
- if (entryIndex === -1 || this._timelineData.entries[entryIndex].node.scriptId === "0")
- this._canvas.style.cursor = "default";
- else
- this._canvas.style.cursor = "pointer";
- this._highlightedEntryIndex = entryIndex;
- this._scheduleUpdate();
- },
- _millisecondsToString: function(ms)
- {
- if (ms === 0)
- return "0";
- if (ms < 1000)
- return WebInspector.UIString("%.1f\u2009ms", ms);
- return Number.secondsToString(ms / 1000, true);
- },
- _prepareHighlightedEntryInfo: function()
- {
- if (this._isDragging)
- return null;
- var entry = this._timelineData.entries[this._highlightedEntryIndex];
- if (!entry)
- return null;
- var node = entry.node;
- if (!node)
- return null;
- var entryInfo = [];
- function pushEntryInfoRow(title, text)
- {
- var row = {};
- row.title = title;
- row.text = text;
- entryInfo.push(row);
- }
- pushEntryInfoRow(WebInspector.UIString("Name"), node.functionName);
- if (this._cpuProfileView.samples) {
- var rate = this._cpuProfileView.samplesPerMs;
- var selfTime = this._millisecondsToString(entry.selfTime / rate);
- var totalTime = this._millisecondsToString(entry.duration / rate);
- pushEntryInfoRow(WebInspector.UIString("Self time"), selfTime);
- pushEntryInfoRow(WebInspector.UIString("Total time"), totalTime);
- }
- if (node.url)
- pushEntryInfoRow(WebInspector.UIString("URL"), node.url + ":" + node.lineNumber);
- pushEntryInfoRow(WebInspector.UIString("Aggregated self time"), Number.secondsToString(node.selfTime / 1000, true));
- pushEntryInfoRow(WebInspector.UIString("Aggregated total time"), Number.secondsToString(node.totalTime / 1000, true));
- return entryInfo;
- },
- _onClick: function(e)
- {
- // 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.
- if (this._wasDragged)
- return;
- if (this._highlightedEntryIndex === -1)
- return;
- var node = this._timelineData.entries[this._highlightedEntryIndex].node;
- this.dispatchEventToListeners(WebInspector.FlameChart.Events.SelectedNode, node);
- },
- _onMouseWheel: function(e)
- {
- if (e.wheelDeltaY) {
- const zoomFactor = 1.1;
- const mouseWheelZoomSpeed = 1 / 120;
- var zoom = Math.pow(zoomFactor, -e.wheelDeltaY * mouseWheelZoomSpeed);
- var overviewReference = (this._pixelWindowLeft + e.offsetX - this._paddingLeft) / this._totalPixels;
- this._overviewGrid.zoom(zoom, overviewReference);
- } else {
- var shift = Number.constrain(-1 * this._windowWidth / 4 * e.wheelDeltaX / 120, -this._windowLeft, 1 - this._windowRight);
- this._overviewGrid.setWindow(this._windowLeft + shift, this._windowRight + shift);
- }
- },
- /**
- * @param {!number} x
- * @param {!number} y
- */
- _coordinatesToEntryIndex: function(x, y)
- {
- var timelineData = this._timelineData;
- if (!timelineData)
- return -1;
- var timelineEntries = timelineData.entries;
- var cursorTime = (x + this._pixelWindowLeft - this._paddingLeft) * this._pixelToTime;
- var cursorLevel = Math.floor((this._canvas.height / window.devicePixelRatio - y) / this._barHeight);
- for (var i = 0; i < timelineEntries.length; ++i) {
- if (cursorTime < timelineEntries[i].startTime)
- return -1;
- if (cursorTime < (timelineEntries[i].startTime + timelineEntries[i].duration)
- && cursorLevel === timelineEntries[i].depth)
- return i;
- }
- return -1;
- },
- onResize: function()
- {
- this._updateOverviewCanvas = true;
- this._scheduleUpdate();
- },
- _drawOverviewCanvas: function(width, height)
- {
- if (!this._timelineData)
- return;
- var timelineEntries = this._timelineData.entries;
- var drawData = new Uint8Array(width);
- var scaleFactor = width / this._totalTime;
- var maxStackDepth = 5; // minimum stack depth for the case when we see no activity.
- for (var entryIndex = 0; entryIndex < timelineEntries.length; ++entryIndex) {
- var entry = timelineEntries[entryIndex];
- var start = Math.floor(entry.startTime * scaleFactor);
- var finish = Math.floor((entry.startTime + entry.duration) * scaleFactor);
- for (var x = start; x < finish; ++x) {
- drawData[x] = Math.max(drawData[x], entry.depth + 1);
- maxStackDepth = Math.max(maxStackDepth, entry.depth + 1);
- }
- }
- var ratio = window.devicePixelRatio;
- var canvasWidth = width * ratio;
- var canvasHeight = height * ratio;
- this._overviewCanvas.width = canvasWidth;
- this._overviewCanvas.height = canvasHeight;
- this._overviewCanvas.style.width = width + "px";
- this._overviewCanvas.style.height = height + "px";
- var context = this._overviewCanvas.getContext("2d");
- var yScaleFactor = canvasHeight / (maxStackDepth * 1.1);
- context.lineWidth = 1;
- context.translate(0.5, 0.5);
- context.strokeStyle = "rgba(20,0,0,0.4)";
- context.fillStyle = "rgba(214,225,254,0.8)";
- context.moveTo(-1, canvasHeight - 1);
- if (drawData)
- context.lineTo(-1, Math.round(height - drawData[0] * yScaleFactor - 1));
- var value;
- for (var x = 0; x < width; ++x) {
- value = Math.round(canvasHeight - drawData[x] * yScaleFactor - 1);
- context.lineTo(x * ratio, value);
- }
- context.lineTo(canvasWidth + 1, value);
- context.lineTo(canvasWidth + 1, canvasHeight - 1);
- context.fill();
- context.stroke();
- context.closePath();
- },
- /**
- * @param {WebInspector.FlameChart.Entry} entry
- * @param {AnchorBox} anchorBox
- */
- _entryToAnchorBox: function(entry, anchorBox)
- {
- anchorBox.x = Math.floor(entry.startTime * this._timeToPixel) - this._pixelWindowLeft + this._paddingLeft;
- anchorBox.y = this._canvas.height / window.devicePixelRatio - (entry.depth + 1) * this._barHeight;
- anchorBox.width = Math.max(Math.ceil(entry.duration * this._timeToPixel), this._minWidth);
- anchorBox.height = this._barHeight;
- if (anchorBox.x < 0) {
- anchorBox.width += anchorBox.x;
- anchorBox.x = 0;
- }
- anchorBox.width = Number.constrain(anchorBox.width, 0, this._canvas.width - anchorBox.x);
- },
- /**
- * @param {!number} height
- * @param {!number} width
- */
- draw: function(width, height)
- {
- var timelineData = this._calculateTimelineData();
- if (!timelineData)
- return;
- var timelineEntries = timelineData.entries;
- var ratio = window.devicePixelRatio;
- var canvasWidth = width * ratio;
- var canvasHeight = height * ratio;
- this._canvas.width = canvasWidth;
- this._canvas.height = canvasHeight;
- this._canvas.style.width = width + "px";
- this._canvas.style.height = height + "px";
- var barHeight = this._barHeight;
- var context = this._canvas.getContext("2d");
- context.scale(ratio, ratio);
- var visibleTimeLeft = this._timeWindowLeft - this._paddingLeftTime;
- var timeWindowRight = this._timeWindowRight;
- function forEachEntry(flameChart, callback)
- {
- var anchorBox = new AnchorBox();
- for (var i = 0; i < timelineEntries.length; ++i) {
- var entry = timelineEntries[i];
- var startTime = entry.startTime;
- if (startTime > timeWindowRight)
- break;
- if ((startTime + entry.duration) < visibleTimeLeft)
- continue;
- flameChart._entryToAnchorBox(entry, anchorBox);
- callback(flameChart, context, entry, anchorBox, flameChart._highlightedEntryIndex === i);
- }
- }
- function drawBar(flameChart, context, entry, anchorBox, highlighted)
- {
- context.beginPath();
- context.rect(anchorBox.x, anchorBox.y, anchorBox.width - 1, anchorBox.height - 1);
- var colorPair = entry.colorPair;
- context.fillStyle = highlighted ? colorPair.highlighted : colorPair.normal;
- context.fill();
- }
- forEachEntry(this, drawBar);
- context.font = (barHeight - 4) + "px " + window.getComputedStyle(this.element, null).getPropertyValue("font-family");
- context.textBaseline = "alphabetic";
- context.fillStyle = "#333";
- this._dotsWidth = context.measureText("\u2026").width;
- var textPaddingLeft = 2;
- function drawText(flameChart, context, entry, anchorBox, highlighted)
- {
- var xText = Math.max(0, anchorBox.x);
- var widthText = anchorBox.width - textPaddingLeft + anchorBox.x - xText;
- var title = flameChart._prepareText(context, entry.node.functionName, widthText);
- if (title)
- context.fillText(title, xText + textPaddingLeft, anchorBox.y + barHeight - 4);
- }
- forEachEntry(this, drawText);
- var entryInfo = this._prepareHighlightedEntryInfo();
- if (entryInfo)
- this._printEntryInfo(context, entryInfo, 0, 25, width);
- },
- _printEntryInfo: function(context, entryInfo, x, y, width)
- {
- const lineHeight = 18;
- const paddingLeft = 10;
- const paddingTop = 5;
- var maxTitleWidth = 0;
- var basicFont = "100% " + window.getComputedStyle(this.element, null).getPropertyValue("font-family");
- context.font = "bold " + basicFont;
- context.textBaseline = "top";
- for (var i = 0; i < entryInfo.length; ++i)
- maxTitleWidth = Math.max(maxTitleWidth, context.measureText(entryInfo[i].title).width);
- var maxTextWidth = 0;
- for (var i = 0; i < entryInfo.length; ++i)
- maxTextWidth = Math.max(maxTextWidth, context.measureText(entryInfo[i].text).width);
- maxTextWidth = Math.min(maxTextWidth, width - 2 * paddingLeft - maxTitleWidth);
- context.beginPath();
- context.rect(x, y, maxTitleWidth + maxTextWidth + 5, lineHeight * entryInfo.length + 5);
- context.strokeStyle = "rgba(0,0,0,0)";
- context.fillStyle = "rgba(254,254,254,0.8)";
- context.fill();
- context.stroke();
- context.fillStyle = "#333";
- for (var i = 0; i < entryInfo.length; ++i)
- context.fillText(entryInfo[i].title, x + paddingLeft, y + lineHeight * i);
- context.font = basicFont;
- for (var i = 0; i < entryInfo.length; ++i) {
- var text = this._prepareText(context, entryInfo[i].text, maxTextWidth);
- context.fillText(text, x + paddingLeft + maxTitleWidth + paddingLeft, y + lineHeight * i);
- }
- },
- _prepareText: function(context, title, maxSize)
- {
- if (maxSize < this._dotsWidth)
- return null;
- var titleWidth = context.measureText(title).width;
- if (maxSize > titleWidth)
- return title;
- maxSize -= this._dotsWidth;
- var dotRegExp=/[\.\$]/g;
- var match = dotRegExp.exec(title);
- if (!match) {
- var visiblePartSize = maxSize / titleWidth;
- var newTextLength = Math.floor(title.length * visiblePartSize) + 1;
- var minTextLength = 4;
- if (newTextLength < minTextLength)
- return null;
- var substring;
- do {
- --newTextLength;
- substring = title.substring(0, newTextLength);
- } while (context.measureText(substring).width > maxSize);
- return title.substring(0, newTextLength) + "\u2026";
- }
- while (match) {
- var substring = title.substring(match.index + 1);
- var width = context.measureText(substring).width;
- if (maxSize > width)
- return "\u2026" + substring;
- match = dotRegExp.exec(title);
- }
- var i = 0;
- do {
- ++i;
- } while (context.measureText(title.substring(0, i)).width < maxSize);
- return title.substring(0, i - 1) + "\u2026";
- },
- _scheduleUpdate: function()
- {
- if (this._updateTimerId)
- return;
- this._updateTimerId = setTimeout(this.update.bind(this), 10);
- },
-
- _updateBoundaries: function()
- {
- this._windowLeft = this._overviewGrid.windowLeft();
- this._windowRight = this._overviewGrid.windowRight();
- this._windowWidth = this._windowRight - this._windowLeft;
- this._totalTime = this._timelineData.totalTime;
- this._timeWindowLeft = this._windowLeft * this._totalTime;
- this._timeWindowRight = this._windowRight * this._totalTime;
- this._pixelWindowWidth = this._chartContainer.clientWidth;
- 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;
- },
- update: function()
- {
- this._updateTimerId = 0;
- if (!this._timelineData)
- this._calculateTimelineData();
- if (!this._timelineData)
- return;
- this._updateBoundaries();
- this.draw(this._chartContainer.clientWidth, this._chartContainer.clientHeight);
- this._calculator._updateBoundaries(this);
- this._overviewCalculator._updateBoundaries(this);
- this._timelineGrid.element.style.width = this.element.clientWidth;
- this._timelineGrid.updateDividers(this._calculator);
- this._overviewGrid.updateDividers(this._overviewCalculator);
- if (this._updateOverviewCanvas) {
- this._drawOverviewCanvas(this._overviewContainer.clientWidth, this._overviewContainer.clientHeight - 20);
- this._updateOverviewCanvas = false;
- }
- },
- __proto__: WebInspector.View.prototype
- };
|