mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1121194 - Support vertical panning for the flamegraph in the new performance tool, r=jsantell
This commit is contained in:
parent
b89ba90a7b
commit
c6241e074b
@ -19,6 +19,7 @@ support-files =
|
|||||||
[browser_flame-graph-02.js]
|
[browser_flame-graph-02.js]
|
||||||
[browser_flame-graph-03a.js]
|
[browser_flame-graph-03a.js]
|
||||||
[browser_flame-graph-03b.js]
|
[browser_flame-graph-03b.js]
|
||||||
|
[browser_flame-graph-03c.js]
|
||||||
[browser_flame-graph-04.js]
|
[browser_flame-graph-04.js]
|
||||||
[browser_flame-graph-utils-01.js]
|
[browser_flame-graph-utils-01.js]
|
||||||
[browser_flame-graph-utils-02.js]
|
[browser_flame-graph-utils-02.js]
|
||||||
|
@ -24,6 +24,8 @@ function* performTest() {
|
|||||||
let graph = new FlameGraph(doc.body, 1);
|
let graph = new FlameGraph(doc.body, 1);
|
||||||
graph.fixedWidth = TEST_WIDTH;
|
graph.fixedWidth = TEST_WIDTH;
|
||||||
graph.fixedHeight = TEST_HEIGHT;
|
graph.fixedHeight = TEST_HEIGHT;
|
||||||
|
graph.horizontalPanThreshold = 0;
|
||||||
|
graph.verticalPanThreshold = 0;
|
||||||
|
|
||||||
yield graph.ready();
|
yield graph.ready();
|
||||||
|
|
||||||
|
139
browser/devtools/shared/test/browser_flame-graph-03c.js
Normal file
139
browser/devtools/shared/test/browser_flame-graph-03c.js
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
// Tests that vertical panning in the flame graph widget works properly.
|
||||||
|
|
||||||
|
let TEST_DATA = [{ color: "#f00", blocks: [{ x: 0, y: 0, width: 50, height: 20, text: "FOO" }, { x: 50, y: 0, width: 100, height: 20, text: "BAR" }] }, { color: "#00f", blocks: [{ x: 0, y: 30, width: 30, height: 20, text: "BAZ" }] }];
|
||||||
|
let TEST_BOUNDS = { startTime: 0, endTime: 150 };
|
||||||
|
let TEST_WIDTH = 200;
|
||||||
|
let TEST_HEIGHT = 100;
|
||||||
|
let TEST_DPI_DENSITIY = 2;
|
||||||
|
|
||||||
|
let {FlameGraph} = Cu.import("resource:///modules/devtools/FlameGraph.jsm", {});
|
||||||
|
let {Promise} = devtools.require("resource://gre/modules/Promise.jsm");
|
||||||
|
|
||||||
|
add_task(function*() {
|
||||||
|
yield promiseTab("about:blank");
|
||||||
|
yield performTest();
|
||||||
|
gBrowser.removeCurrentTab();
|
||||||
|
});
|
||||||
|
|
||||||
|
function* performTest() {
|
||||||
|
let [host, win, doc] = yield createHost();
|
||||||
|
doc.body.setAttribute("style", "position: fixed; width: 100%; height: 100%; margin: 0;");
|
||||||
|
|
||||||
|
let graph = new FlameGraph(doc.body, TEST_DPI_DENSITIY);
|
||||||
|
graph.fixedWidth = TEST_WIDTH;
|
||||||
|
graph.fixedHeight = TEST_HEIGHT;
|
||||||
|
|
||||||
|
yield graph.ready();
|
||||||
|
|
||||||
|
testGraph(graph);
|
||||||
|
|
||||||
|
yield graph.destroy();
|
||||||
|
host.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
function testGraph(graph) {
|
||||||
|
graph.setData({ data: TEST_DATA, bounds: TEST_BOUNDS });
|
||||||
|
|
||||||
|
// Drag up vertically only.
|
||||||
|
|
||||||
|
dragStart(graph, TEST_WIDTH / 2, TEST_HEIGHT / 2);
|
||||||
|
is(graph.getViewRange().startTime | 0, 0,
|
||||||
|
"The selection start boundary is correct (1).");
|
||||||
|
is(graph.getViewRange().endTime | 0, 150,
|
||||||
|
"The selection end boundary is correct (1).");
|
||||||
|
is(graph.getViewRange().verticalOffset | 0, 0,
|
||||||
|
"The vertical offset is correct (1).");
|
||||||
|
|
||||||
|
hover(graph, TEST_WIDTH / 2, TEST_HEIGHT / 2 - 50);
|
||||||
|
is(graph.getViewRange().startTime | 0, 0,
|
||||||
|
"The selection start boundary is correct (2).");
|
||||||
|
is(graph.getViewRange().endTime | 0, 150,
|
||||||
|
"The selection end boundary is correct (2).");
|
||||||
|
is(graph.getViewRange().verticalOffset | 0, 17,
|
||||||
|
"The vertical offset is correct (2).");
|
||||||
|
|
||||||
|
dragStop(graph, TEST_WIDTH / 2, TEST_HEIGHT / 2 - 100);
|
||||||
|
is(graph.getViewRange().startTime | 0, 0,
|
||||||
|
"The selection start boundary is correct (3).");
|
||||||
|
is(graph.getViewRange().endTime | 0, 150,
|
||||||
|
"The selection end boundary is correct (3).");
|
||||||
|
is(graph.getViewRange().verticalOffset | 0, 42,
|
||||||
|
"The vertical offset is correct (3).");
|
||||||
|
|
||||||
|
// Drag down strongly vertically and slightly horizontally.
|
||||||
|
|
||||||
|
dragStart(graph, TEST_WIDTH / 2, TEST_HEIGHT / 2);
|
||||||
|
is(graph.getViewRange().startTime | 0, 0,
|
||||||
|
"The selection start boundary is correct (4).");
|
||||||
|
is(graph.getViewRange().endTime | 0, 150,
|
||||||
|
"The selection end boundary is correct (4).");
|
||||||
|
is(graph.getViewRange().verticalOffset | 0, 42,
|
||||||
|
"The vertical offset is correct (4).");
|
||||||
|
|
||||||
|
hover(graph, TEST_WIDTH / 2, TEST_HEIGHT / 2 + 50);
|
||||||
|
is(graph.getViewRange().startTime | 0, 0,
|
||||||
|
"The selection start boundary is correct (5).");
|
||||||
|
is(graph.getViewRange().endTime | 0, 150,
|
||||||
|
"The selection end boundary is correct (5).");
|
||||||
|
is(graph.getViewRange().verticalOffset | 0, 25,
|
||||||
|
"The vertical offset is correct (5).");
|
||||||
|
|
||||||
|
dragStop(graph, TEST_WIDTH / 2 + 100, TEST_HEIGHT / 2 + 500);
|
||||||
|
is(graph.getViewRange().startTime | 0, 0,
|
||||||
|
"The selection start boundary is correct (6).");
|
||||||
|
is(graph.getViewRange().endTime | 0, 150,
|
||||||
|
"The selection end boundary is correct (6).");
|
||||||
|
is(graph.getViewRange().verticalOffset | 0, 0,
|
||||||
|
"The vertical offset is correct (6).");
|
||||||
|
|
||||||
|
// Drag up slightly vertically and strongly horizontally.
|
||||||
|
|
||||||
|
dragStart(graph, TEST_WIDTH / 2, TEST_HEIGHT / 2);
|
||||||
|
is(graph.getViewRange().startTime | 0, 0,
|
||||||
|
"The selection start boundary is correct (7).");
|
||||||
|
is(graph.getViewRange().endTime | 0, 150,
|
||||||
|
"The selection end boundary is correct (7).");
|
||||||
|
is(graph.getViewRange().verticalOffset | 0, 0,
|
||||||
|
"The vertical offset is correct (7).");
|
||||||
|
|
||||||
|
hover(graph, TEST_WIDTH / 2 + 50, TEST_HEIGHT / 2);
|
||||||
|
is(graph.getViewRange().startTime | 0, 0,
|
||||||
|
"The selection start boundary is correct (8).");
|
||||||
|
is(graph.getViewRange().endTime | 0, 116,
|
||||||
|
"The selection end boundary is correct (8).");
|
||||||
|
is(graph.getViewRange().verticalOffset | 0, 0,
|
||||||
|
"The vertical offset is correct (8).");
|
||||||
|
|
||||||
|
dragStop(graph, TEST_WIDTH / 2 + 500, TEST_HEIGHT / 2 + 100);
|
||||||
|
is(graph.getViewRange().startTime | 0, 0,
|
||||||
|
"The selection start boundary is correct (9).");
|
||||||
|
is(graph.getViewRange().endTime | 0, 0,
|
||||||
|
"The selection end boundary is correct (9).");
|
||||||
|
is(graph.getViewRange().verticalOffset | 0, 0,
|
||||||
|
"The vertical offset is correct (9).");
|
||||||
|
}
|
||||||
|
|
||||||
|
// EventUtils just doesn't work!
|
||||||
|
|
||||||
|
function hover(graph, x, y = 1) {
|
||||||
|
x /= window.devicePixelRatio;
|
||||||
|
y /= window.devicePixelRatio;
|
||||||
|
graph._onMouseMove({ clientX: x, clientY: y });
|
||||||
|
}
|
||||||
|
|
||||||
|
function dragStart(graph, x, y = 1) {
|
||||||
|
x /= window.devicePixelRatio;
|
||||||
|
y /= window.devicePixelRatio;
|
||||||
|
graph._onMouseMove({ clientX: x, clientY: y });
|
||||||
|
graph._onMouseDown({ clientX: x, clientY: y });
|
||||||
|
}
|
||||||
|
|
||||||
|
function dragStop(graph, x, y = 1) {
|
||||||
|
x /= window.devicePixelRatio;
|
||||||
|
y /= window.devicePixelRatio;
|
||||||
|
graph._onMouseMove({ clientX: x, clientY: y });
|
||||||
|
graph._onMouseUp({ clientX: x, clientY: y });
|
||||||
|
}
|
@ -26,11 +26,15 @@ const GRAPH_WHEEL_ZOOM_SENSITIVITY = 0.00035;
|
|||||||
const GRAPH_WHEEL_SCROLL_SENSITIVITY = 0.5;
|
const GRAPH_WHEEL_SCROLL_SENSITIVITY = 0.5;
|
||||||
const GRAPH_MIN_SELECTION_WIDTH = 0.001; // ms
|
const GRAPH_MIN_SELECTION_WIDTH = 0.001; // ms
|
||||||
|
|
||||||
|
const GRAPH_HORIZONTAL_PAN_THRESHOLD = 10; // px
|
||||||
|
const GRAPH_VERTICAL_PAN_THRESHOLD = 30; // px
|
||||||
|
|
||||||
const FIND_OPTIMAL_TICK_INTERVAL_MAX_ITERS = 100;
|
const FIND_OPTIMAL_TICK_INTERVAL_MAX_ITERS = 100;
|
||||||
const TIMELINE_TICKS_MULTIPLE = 5; // ms
|
const TIMELINE_TICKS_MULTIPLE = 5; // ms
|
||||||
const TIMELINE_TICKS_SPACING_MIN = 75; // px
|
const TIMELINE_TICKS_SPACING_MIN = 75; // px
|
||||||
|
|
||||||
const OVERVIEW_HEADER_HEIGHT = 16; // px
|
const OVERVIEW_HEADER_HEIGHT = 16; // px
|
||||||
|
const OVERVIEW_HEADER_BACKGROUND = "rgba(255,255,255,0.7)";
|
||||||
const OVERVIEW_HEADER_TEXT_COLOR = "#18191a";
|
const OVERVIEW_HEADER_TEXT_COLOR = "#18191a";
|
||||||
const OVERVIEW_HEADER_TEXT_FONT_SIZE = 9; // px
|
const OVERVIEW_HEADER_TEXT_FONT_SIZE = 9; // px
|
||||||
const OVERVIEW_HEADER_TEXT_FONT_FAMILY = "sans-serif";
|
const OVERVIEW_HEADER_TEXT_FONT_FAMILY = "sans-serif";
|
||||||
@ -121,9 +125,11 @@ function FlameGraph(parent, sharpness) {
|
|||||||
this._height = canvas.height = bounds.height * this._pixelRatio;
|
this._height = canvas.height = bounds.height * this._pixelRatio;
|
||||||
this._ctx = canvas.getContext("2d");
|
this._ctx = canvas.getContext("2d");
|
||||||
|
|
||||||
this._bounds = new GraphSelection();
|
this._bounds = new GraphArea();
|
||||||
this._selection = new GraphSelection();
|
this._selection = new GraphArea();
|
||||||
this._selectionDragger = new GraphSelectionDragger();
|
this._selectionDragger = new GraphAreaDragger();
|
||||||
|
this._verticalOffset = 0;
|
||||||
|
this._verticalOffsetDragger = new GraphAreaDragger(0);
|
||||||
|
|
||||||
// Calculating text widths is necessary to trim the text inside the blocks
|
// Calculating text widths is necessary to trim the text inside the blocks
|
||||||
// while the scaling changes (e.g. via scrolling). This is very expensive,
|
// while the scaling changes (e.g. via scrolling). This is very expensive,
|
||||||
@ -198,6 +204,8 @@ FlameGraph.prototype = {
|
|||||||
this._bounds = null;
|
this._bounds = null;
|
||||||
this._selection = null;
|
this._selection = null;
|
||||||
this._selectionDragger = null;
|
this._selectionDragger = null;
|
||||||
|
this._verticalOffset = null;
|
||||||
|
this._verticalOffsetDragger = null;
|
||||||
this._textWidthsCache = null;
|
this._textWidthsCache = null;
|
||||||
|
|
||||||
this._data = null;
|
this._data = null;
|
||||||
@ -208,6 +216,7 @@ FlameGraph.prototype = {
|
|||||||
/**
|
/**
|
||||||
* Rendering options. Subclasses should override these.
|
* Rendering options. Subclasses should override these.
|
||||||
*/
|
*/
|
||||||
|
overviewHeaderBackgroundColor: OVERVIEW_HEADER_BACKGROUND,
|
||||||
overviewHeaderTextColor: OVERVIEW_HEADER_TEXT_COLOR,
|
overviewHeaderTextColor: OVERVIEW_HEADER_TEXT_COLOR,
|
||||||
overviewTimelineStrokes: OVERVIEW_TIMELINE_STROKES,
|
overviewTimelineStrokes: OVERVIEW_TIMELINE_STROKES,
|
||||||
blockTextColor: FLAME_GRAPH_BLOCK_TEXT_COLOR,
|
blockTextColor: FLAME_GRAPH_BLOCK_TEXT_COLOR,
|
||||||
@ -219,6 +228,12 @@ FlameGraph.prototype = {
|
|||||||
fixedWidth: null,
|
fixedWidth: null,
|
||||||
fixedHeight: null,
|
fixedHeight: null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How much preliminar drag is necessary to determine the panning direction.
|
||||||
|
*/
|
||||||
|
horizontalPanThreshold: GRAPH_HORIZONTAL_PAN_THRESHOLD,
|
||||||
|
verticalPanThreshold: GRAPH_VERTICAL_PAN_THRESHOLD,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The units used in the overhead ticks. Could be "ms", for example.
|
* The units used in the overhead ticks. Could be "ms", for example.
|
||||||
* Overwrite this with your own localized format.
|
* Overwrite this with your own localized format.
|
||||||
@ -278,12 +293,13 @@ FlameGraph.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the selection (i.e. the 'view range') bounds.
|
* Sets the selection and vertical offset (i.e. the 'view range').
|
||||||
* @return number
|
* @return number
|
||||||
*/
|
*/
|
||||||
setViewRange: function({ startTime, endTime }) {
|
setViewRange: function({ startTime, endTime }, verticalOffset = 0) {
|
||||||
this._selection.start = startTime * this._pixelRatio;
|
this._selection.start = startTime * this._pixelRatio;
|
||||||
this._selection.end = endTime * this._pixelRatio;
|
this._selection.end = endTime * this._pixelRatio;
|
||||||
|
this._verticalOffset = verticalOffset * this._pixelRatio;
|
||||||
this._shouldRedraw = true;
|
this._shouldRedraw = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -299,13 +315,14 @@ FlameGraph.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the current selection (i.e. the 'view range').
|
* Gets the current selection and vertical offset (i.e. the 'view range').
|
||||||
* @return number
|
* @return number
|
||||||
*/
|
*/
|
||||||
getViewRange: function() {
|
getViewRange: function() {
|
||||||
return {
|
return {
|
||||||
startTime: this._selection.start / this._pixelRatio,
|
startTime: this._selection.start / this._pixelRatio,
|
||||||
endTime: this._selection.end / this._pixelRatio
|
endTime: this._selection.end / this._pixelRatio,
|
||||||
|
verticalOffset: this._verticalOffset / this._pixelRatio
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -366,8 +383,8 @@ FlameGraph.prototype = {
|
|||||||
let selection = this._selection;
|
let selection = this._selection;
|
||||||
let selectionWidth = selection.end - selection.start;
|
let selectionWidth = selection.end - selection.start;
|
||||||
let selectionScale = canvasWidth / selectionWidth;
|
let selectionScale = canvasWidth / selectionWidth;
|
||||||
|
this._drawPyramid(this._data, this._verticalOffset, selection.start, selectionScale);
|
||||||
this._drawTicks(selection.start, selectionScale);
|
this._drawTicks(selection.start, selectionScale);
|
||||||
this._drawPyramid(this._data, selection.start, selectionScale);
|
|
||||||
|
|
||||||
this._shouldRedraw = false;
|
this._shouldRedraw = false;
|
||||||
},
|
},
|
||||||
@ -385,6 +402,9 @@ FlameGraph.prototype = {
|
|||||||
let canvasHeight = this._height;
|
let canvasHeight = this._height;
|
||||||
let scaledOffset = dataOffset * dataScale;
|
let scaledOffset = dataOffset * dataScale;
|
||||||
|
|
||||||
|
ctx.fillStyle = this.overviewHeaderBackgroundColor;
|
||||||
|
ctx.fillRect(0, 0, canvasWidth, OVERVIEW_HEADER_HEIGHT * this._pixelRatio);
|
||||||
|
|
||||||
let fontSize = OVERVIEW_HEADER_TEXT_FONT_SIZE * this._pixelRatio;
|
let fontSize = OVERVIEW_HEADER_TEXT_FONT_SIZE * this._pixelRatio;
|
||||||
let fontFamily = OVERVIEW_HEADER_TEXT_FONT_FAMILY;
|
let fontFamily = OVERVIEW_HEADER_TEXT_FONT_FAMILY;
|
||||||
let textPaddingLeft = OVERVIEW_HEADER_TEXT_PADDING_LEFT * this._pixelRatio;
|
let textPaddingLeft = OVERVIEW_HEADER_TEXT_PADDING_LEFT * this._pixelRatio;
|
||||||
@ -415,48 +435,50 @@ FlameGraph.prototype = {
|
|||||||
*
|
*
|
||||||
* @param object dataSource
|
* @param object dataSource
|
||||||
* The data source. See the constructor for more information.
|
* The data source. See the constructor for more information.
|
||||||
|
* @param number verticalOffset
|
||||||
|
* Offsets the drawing vertically by the specified amount.
|
||||||
* @param number dataOffset, dataScale
|
* @param number dataOffset, dataScale
|
||||||
* Offsets and scales the data source by the specified amount.
|
* Offsets and scales the data source by the specified amount.
|
||||||
* This is used for scrolling the visualization.
|
* This is used for scrolling the visualization.
|
||||||
*/
|
*/
|
||||||
_drawPyramid: function(dataSource, dataOffset, dataScale) {
|
_drawPyramid: function(dataSource, verticalOffset, dataOffset, dataScale) {
|
||||||
let ctx = this._ctx;
|
let ctx = this._ctx;
|
||||||
|
|
||||||
let fontSize = FLAME_GRAPH_BLOCK_TEXT_FONT_SIZE * this._pixelRatio;
|
let fontSize = FLAME_GRAPH_BLOCK_TEXT_FONT_SIZE * this._pixelRatio;
|
||||||
let fontFamily = FLAME_GRAPH_BLOCK_TEXT_FONT_FAMILY;
|
let fontFamily = FLAME_GRAPH_BLOCK_TEXT_FONT_FAMILY;
|
||||||
let visibleBlocks = this._drawPyramidFill(dataSource, dataOffset, dataScale);
|
let visibleBlocksInfo = this._drawPyramidFill(dataSource, verticalOffset, dataOffset, dataScale);
|
||||||
|
|
||||||
ctx.textBaseline = "middle";
|
ctx.textBaseline = "middle";
|
||||||
ctx.font = fontSize + "px " + fontFamily;
|
ctx.font = fontSize + "px " + fontFamily;
|
||||||
ctx.fillStyle = this.blockTextColor;
|
ctx.fillStyle = this.blockTextColor;
|
||||||
|
|
||||||
this._drawPyramidText(visibleBlocks, dataOffset, dataScale);
|
this._drawPyramidText(visibleBlocksInfo, verticalOffset, dataOffset, dataScale);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fills all block inside this graph's pyramid.
|
* Fills all block inside this graph's pyramid.
|
||||||
* @see FlameGraph.prototype._drawPyramid
|
* @see FlameGraph.prototype._drawPyramid
|
||||||
*/
|
*/
|
||||||
_drawPyramidFill: function(dataSource, dataOffset, dataScale) {
|
_drawPyramidFill: function(dataSource, verticalOffset, dataOffset, dataScale) {
|
||||||
let visibleBlocksStore = [];
|
let visibleBlocksInfoStore = [];
|
||||||
let minVisibleBlockWidth = this._overflowCharWidth;
|
let minVisibleBlockWidth = this._overflowCharWidth;
|
||||||
|
|
||||||
for (let { color, blocks } of dataSource) {
|
for (let { color, blocks } of dataSource) {
|
||||||
this._drawBlocksFill(
|
this._drawBlocksFill(
|
||||||
color, blocks, dataOffset, dataScale,
|
color, blocks, verticalOffset, dataOffset, dataScale,
|
||||||
visibleBlocksStore, minVisibleBlockWidth);
|
visibleBlocksInfoStore, minVisibleBlockWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
return visibleBlocksStore;
|
return visibleBlocksInfoStore;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds the text for all block inside this graph's pyramid.
|
* Adds the text for all block inside this graph's pyramid.
|
||||||
* @see FlameGraph.prototype._drawPyramid
|
* @see FlameGraph.prototype._drawPyramid
|
||||||
*/
|
*/
|
||||||
_drawPyramidText: function(blocks, dataOffset, dataScale) {
|
_drawPyramidText: function(blocksInfo, verticalOffset, dataOffset, dataScale) {
|
||||||
for (let block of blocks) {
|
for (let { block, rect } of blocksInfo) {
|
||||||
this._drawBlockText(block, dataOffset, dataScale);
|
this._drawBlockText(block, rect, verticalOffset, dataOffset, dataScale);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -468,19 +490,22 @@ FlameGraph.prototype = {
|
|||||||
* @param array blocks
|
* @param array blocks
|
||||||
* A list of { x, y, width, height } objects visually representing
|
* A list of { x, y, width, height } objects visually representing
|
||||||
* all the blocks sharing this particular style.
|
* all the blocks sharing this particular style.
|
||||||
|
* @param number verticalOffset
|
||||||
|
* Offsets the drawing vertically by the specified amount.
|
||||||
* @param number dataOffset, dataScale
|
* @param number dataOffset, dataScale
|
||||||
* Offsets and scales the data source by the specified amount.
|
* Offsets and scales the data source by the specified amount.
|
||||||
* This is used for scrolling the visualization.
|
* This is used for scrolling the visualization.
|
||||||
* @param array visibleBlocksStore
|
* @param array visibleBlocksInfoStore
|
||||||
* An array to store all the visible blocks into, after drawing them.
|
* An array to store all the visible blocks into, along with the
|
||||||
|
* final baked coordinates and dimensions, after drawing them.
|
||||||
* The provided array will be populated.
|
* The provided array will be populated.
|
||||||
* @param number minVisibleBlockWidth
|
* @param number minVisibleBlockWidth
|
||||||
* The minimum width of the blocks that will be added into
|
* The minimum width of the blocks that will be added into
|
||||||
* the `visibleBlocksStore`.
|
* the `visibleBlocksInfoStore`.
|
||||||
*/
|
*/
|
||||||
_drawBlocksFill: function(
|
_drawBlocksFill: function(
|
||||||
color, blocks, dataOffset, dataScale,
|
color, blocks, verticalOffset, dataOffset, dataScale,
|
||||||
visibleBlocksStore, minVisibleBlockWidth)
|
visibleBlocksInfoStore, minVisibleBlockWidth)
|
||||||
{
|
{
|
||||||
let ctx = this._ctx;
|
let ctx = this._ctx;
|
||||||
let canvasWidth = this._width;
|
let canvasWidth = this._width;
|
||||||
@ -493,13 +518,14 @@ FlameGraph.prototype = {
|
|||||||
for (let block of blocks) {
|
for (let block of blocks) {
|
||||||
let { x, y, width, height } = block;
|
let { x, y, width, height } = block;
|
||||||
let rectLeft = x * this._pixelRatio * dataScale - scaledOffset;
|
let rectLeft = x * this._pixelRatio * dataScale - scaledOffset;
|
||||||
let rectTop = (y + OVERVIEW_HEADER_HEIGHT) * this._pixelRatio;
|
let rectTop = (y - verticalOffset + OVERVIEW_HEADER_HEIGHT) * this._pixelRatio;
|
||||||
let rectWidth = width * this._pixelRatio * dataScale;
|
let rectWidth = width * this._pixelRatio * dataScale;
|
||||||
let rectHeight = height * this._pixelRatio;
|
let rectHeight = height * this._pixelRatio;
|
||||||
|
|
||||||
if (rectLeft > canvasWidth || // Too far right.
|
if (rectLeft > canvasWidth || // Too far right.
|
||||||
rectLeft < -rectWidth || // Too far left.
|
rectLeft < -rectWidth || // Too far left.
|
||||||
rectTop > canvasHeight) { // Too far bottom.
|
rectTop > canvasHeight || // Too far bottom.
|
||||||
|
rectTop < -rectHeight) { // Too far top.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -524,7 +550,10 @@ FlameGraph.prototype = {
|
|||||||
// Populate the visible blocks store with this block if the width
|
// Populate the visible blocks store with this block if the width
|
||||||
// is longer than a given threshold.
|
// is longer than a given threshold.
|
||||||
if (rectWidth > minVisibleBlockWidth) {
|
if (rectWidth > minVisibleBlockWidth) {
|
||||||
visibleBlocksStore.push(block);
|
visibleBlocksInfoStore.push({
|
||||||
|
block: block,
|
||||||
|
rect: { rectLeft, rectTop, rectWidth, rectHeight }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -537,24 +566,29 @@ FlameGraph.prototype = {
|
|||||||
* @param object block
|
* @param object block
|
||||||
* A single { x, y, width, height, text } object visually representing
|
* A single { x, y, width, height, text } object visually representing
|
||||||
* the block containing the text.
|
* the block containing the text.
|
||||||
|
* @param object rect
|
||||||
|
* A single { rectLeft, rectTop, rectWidth, rectHeight } object
|
||||||
|
* representing the final baked coordinates of the drawn rectangle.
|
||||||
|
* Think of them as screen-space values, vs. object-space values. These
|
||||||
|
* differ from the scalars in `block` when the graph is scaled/panned.
|
||||||
|
* @param number verticalOffset
|
||||||
|
* Offsets the drawing vertically by the specified amount.
|
||||||
* @param number dataOffset, dataScale
|
* @param number dataOffset, dataScale
|
||||||
* Offsets and scales the data source by the specified amount.
|
* Offsets and scales the data source by the specified amount.
|
||||||
* This is used for scrolling the visualization.
|
* This is used for scrolling the visualization.
|
||||||
*/
|
*/
|
||||||
_drawBlockText: function(block, dataOffset, dataScale) {
|
_drawBlockText: function(block, rect, verticalOffset, dataOffset, dataScale) {
|
||||||
let ctx = this._ctx;
|
let ctx = this._ctx;
|
||||||
let scaledOffset = dataOffset * dataScale;
|
let scaledOffset = dataOffset * dataScale;
|
||||||
|
|
||||||
let { x, y, width, height, text } = block;
|
let { x, y, width, height, text } = block;
|
||||||
|
let { rectLeft, rectTop, rectWidth, rectHeight } = rect;
|
||||||
|
|
||||||
let paddingTop = FLAME_GRAPH_BLOCK_TEXT_PADDING_TOP * this._pixelRatio;
|
let paddingTop = FLAME_GRAPH_BLOCK_TEXT_PADDING_TOP * this._pixelRatio;
|
||||||
let paddingLeft = FLAME_GRAPH_BLOCK_TEXT_PADDING_LEFT * this._pixelRatio;
|
let paddingLeft = FLAME_GRAPH_BLOCK_TEXT_PADDING_LEFT * this._pixelRatio;
|
||||||
let paddingRight = FLAME_GRAPH_BLOCK_TEXT_PADDING_RIGHT * this._pixelRatio;
|
let paddingRight = FLAME_GRAPH_BLOCK_TEXT_PADDING_RIGHT * this._pixelRatio;
|
||||||
let totalHorizontalPadding = paddingLeft + paddingRight;
|
let totalHorizontalPadding = paddingLeft + paddingRight;
|
||||||
|
|
||||||
let rectLeft = x * this._pixelRatio * dataScale - scaledOffset;
|
|
||||||
let rectWidth = width * this._pixelRatio * dataScale;
|
|
||||||
|
|
||||||
// Clamp the blocks position to start at 0. Avoid negative X coords,
|
// Clamp the blocks position to start at 0. Avoid negative X coords,
|
||||||
// to properly place the text inside the blocks.
|
// to properly place the text inside the blocks.
|
||||||
if (rectLeft < 0) {
|
if (rectLeft < 0) {
|
||||||
@ -563,7 +597,7 @@ FlameGraph.prototype = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let textLeft = rectLeft + paddingLeft;
|
let textLeft = rectLeft + paddingLeft;
|
||||||
let textTop = (y + height / 2 + OVERVIEW_HEADER_HEIGHT) * this._pixelRatio + paddingTop;
|
let textTop = rectTop + rectHeight / 2 + paddingTop;
|
||||||
let textAvailableWidth = rectWidth - totalHorizontalPadding;
|
let textAvailableWidth = rectWidth - totalHorizontalPadding;
|
||||||
|
|
||||||
// Massage the text to fit inside a given width. This clamps the string
|
// Massage the text to fit inside a given width. This clamps the string
|
||||||
@ -672,6 +706,7 @@ FlameGraph.prototype = {
|
|||||||
_onMouseMove: function(e) {
|
_onMouseMove: function(e) {
|
||||||
let offset = this._getContainerOffset();
|
let offset = this._getContainerOffset();
|
||||||
let mouseX = (e.clientX - offset.left) * this._pixelRatio;
|
let mouseX = (e.clientX - offset.left) * this._pixelRatio;
|
||||||
|
let mouseY = (e.clientY - offset.top) * this._pixelRatio;
|
||||||
|
|
||||||
let canvasWidth = this._width;
|
let canvasWidth = this._width;
|
||||||
let canvasHeight = this._height;
|
let canvasHeight = this._height;
|
||||||
@ -680,14 +715,41 @@ FlameGraph.prototype = {
|
|||||||
let selectionWidth = selection.end - selection.start;
|
let selectionWidth = selection.end - selection.start;
|
||||||
let selectionScale = canvasWidth / selectionWidth;
|
let selectionScale = canvasWidth / selectionWidth;
|
||||||
|
|
||||||
let dragger = this._selectionDragger;
|
let horizDrag = this._selectionDragger;
|
||||||
if (dragger.origin != null) {
|
let vertDrag = this._verticalOffsetDragger;
|
||||||
selection.start = dragger.anchor.start + (dragger.origin - mouseX) / selectionScale;
|
|
||||||
selection.end = dragger.anchor.end + (dragger.origin - mouseX) / selectionScale;
|
// Avoid dragging both horizontally and vertically at the same time,
|
||||||
|
// as this doesn't feel natural. Based on a minimum distance, enable either
|
||||||
|
// one, and remember the drag direction to offset the mouse coords later.
|
||||||
|
if (!this._horizontalDragEnabled && !this._verticalDragEnabled) {
|
||||||
|
let horizDiff = Math.abs(horizDrag.origin - mouseX);
|
||||||
|
if (horizDiff > this.horizontalPanThreshold) {
|
||||||
|
this._horizontalDragDirection = Math.sign(horizDrag.origin - mouseX);
|
||||||
|
this._horizontalDragEnabled = true;
|
||||||
|
}
|
||||||
|
let vertDiff = Math.abs(vertDrag.origin - mouseY);
|
||||||
|
if (vertDiff > this.verticalPanThreshold) {
|
||||||
|
this._verticalDragDirection = Math.sign(vertDrag.origin - mouseY);
|
||||||
|
this._verticalDragEnabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (horizDrag.origin != null && this._horizontalDragEnabled) {
|
||||||
|
let relativeX = mouseX + this._horizontalDragDirection * this.horizontalPanThreshold;
|
||||||
|
selection.start = horizDrag.anchor.start + (horizDrag.origin - relativeX) / selectionScale;
|
||||||
|
selection.end = horizDrag.anchor.end + (horizDrag.origin - relativeX) / selectionScale;
|
||||||
this._normalizeSelectionBounds();
|
this._normalizeSelectionBounds();
|
||||||
this._shouldRedraw = true;
|
this._shouldRedraw = true;
|
||||||
this.emit("selecting");
|
this.emit("selecting");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (vertDrag.origin != null && this._verticalDragEnabled) {
|
||||||
|
let relativeY = mouseY + this._verticalDragDirection * this.verticalPanThreshold;
|
||||||
|
this._verticalOffset = vertDrag.anchor + (vertDrag.origin - relativeY) / this._pixelRatio;
|
||||||
|
this._normalizeVerticalOffset();
|
||||||
|
this._shouldRedraw = true;
|
||||||
|
this.emit("panning-vertically");
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -696,11 +758,19 @@ FlameGraph.prototype = {
|
|||||||
_onMouseDown: function(e) {
|
_onMouseDown: function(e) {
|
||||||
let offset = this._getContainerOffset();
|
let offset = this._getContainerOffset();
|
||||||
let mouseX = (e.clientX - offset.left) * this._pixelRatio;
|
let mouseX = (e.clientX - offset.left) * this._pixelRatio;
|
||||||
|
let mouseY = (e.clientY - offset.top) * this._pixelRatio;
|
||||||
|
|
||||||
this._selectionDragger.origin = mouseX;
|
this._selectionDragger.origin = mouseX;
|
||||||
this._selectionDragger.anchor.start = this._selection.start;
|
this._selectionDragger.anchor.start = this._selection.start;
|
||||||
this._selectionDragger.anchor.end = this._selection.end;
|
this._selectionDragger.anchor.end = this._selection.end;
|
||||||
this._canvas.setAttribute("input", "adjusting-selection-boundary");
|
|
||||||
|
this._verticalOffsetDragger.origin = mouseY;
|
||||||
|
this._verticalOffsetDragger.anchor = this._verticalOffset;
|
||||||
|
|
||||||
|
this._horizontalDragEnabled = false;
|
||||||
|
this._verticalDragEnabled = false;
|
||||||
|
|
||||||
|
this._canvas.setAttribute("input", "adjusting-view-area");
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -708,6 +778,11 @@ FlameGraph.prototype = {
|
|||||||
*/
|
*/
|
||||||
_onMouseUp: function() {
|
_onMouseUp: function() {
|
||||||
this._selectionDragger.origin = null;
|
this._selectionDragger.origin = null;
|
||||||
|
this._verticalOffsetDragger.origin = null;
|
||||||
|
this._horizontalDragEnabled = false;
|
||||||
|
this._horizontalDragDirection = 0;
|
||||||
|
this._verticalDragEnabled = false;
|
||||||
|
this._verticalDragDirection = 0;
|
||||||
this._canvas.removeAttribute("input");
|
this._canvas.removeAttribute("input");
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -782,6 +857,14 @@ FlameGraph.prototype = {
|
|||||||
this._selection.end = selectionEnd;
|
this._selection.end = selectionEnd;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes sure that the current vertical offset is within the allowed
|
||||||
|
* panning range.
|
||||||
|
*/
|
||||||
|
_normalizeVerticalOffset: function() {
|
||||||
|
this._verticalOffset = Math.max(this._verticalOffset, 0);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Finds the optimal tick interval between time markers in this graph.
|
* Finds the optimal tick interval between time markers in this graph.
|
||||||
|
@ -12,9 +12,9 @@ const {EventEmitter} = Cu.import("resource://gre/modules/devtools/event-emitter.
|
|||||||
|
|
||||||
this.EXPORTED_SYMBOLS = [
|
this.EXPORTED_SYMBOLS = [
|
||||||
"GraphCursor",
|
"GraphCursor",
|
||||||
"GraphSelection",
|
"GraphArea",
|
||||||
"GraphSelectionDragger",
|
"GraphAreaDragger",
|
||||||
"GraphSelectionResizer",
|
"GraphAreaResizer",
|
||||||
"AbstractCanvasGraph",
|
"AbstractCanvasGraph",
|
||||||
"LineGraphWidget",
|
"LineGraphWidget",
|
||||||
"BarGraphWidget",
|
"BarGraphWidget",
|
||||||
@ -101,17 +101,17 @@ this.GraphCursor = function() {
|
|||||||
this.y = null;
|
this.y = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.GraphSelection = function() {
|
this.GraphArea = function() {
|
||||||
this.start = null;
|
this.start = null;
|
||||||
this.end = null;
|
this.end = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.GraphSelectionDragger = function() {
|
this.GraphAreaDragger = function(anchor = new GraphArea()) {
|
||||||
this.origin = null;
|
this.origin = null;
|
||||||
this.anchor = new GraphSelection();
|
this.anchor = anchor;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.GraphSelectionResizer = function() {
|
this.GraphAreaResizer = function() {
|
||||||
this.margin = null;
|
this.margin = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -178,9 +178,9 @@ this.AbstractCanvasGraph = function(parent, name, sharpness) {
|
|||||||
this._ctx.mozImageSmoothingEnabled = false;
|
this._ctx.mozImageSmoothingEnabled = false;
|
||||||
|
|
||||||
this._cursor = new GraphCursor();
|
this._cursor = new GraphCursor();
|
||||||
this._selection = new GraphSelection();
|
this._selection = new GraphArea();
|
||||||
this._selectionDragger = new GraphSelectionDragger();
|
this._selectionDragger = new GraphAreaDragger();
|
||||||
this._selectionResizer = new GraphSelectionResizer();
|
this._selectionResizer = new GraphAreaResizer();
|
||||||
|
|
||||||
this._onAnimationFrame = this._onAnimationFrame.bind(this);
|
this._onAnimationFrame = this._onAnimationFrame.bind(this);
|
||||||
this._onMouseMove = this._onMouseMove.bind(this);
|
this._onMouseMove = this._onMouseMove.bind(this);
|
||||||
|
@ -864,6 +864,10 @@
|
|||||||
cursor: col-resize;
|
cursor: col-resize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.graph-widget-canvas[input=adjusting-view-area] {
|
||||||
|
cursor: grabbing;
|
||||||
|
}
|
||||||
|
|
||||||
.graph-widget-canvas[input=hovering-selection-contents] {
|
.graph-widget-canvas[input=hovering-selection-contents] {
|
||||||
cursor: grab;
|
cursor: grab;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user