mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-05 15:28:39 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
514
devtools/client/performance/modules/widgets/graphs.js
Normal file
514
devtools/client/performance/modules/widgets/graphs.js
Normal file
|
|
@ -0,0 +1,514 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* This file contains the base line graph that all Performance line graphs use.
|
||||
*/
|
||||
|
||||
const { Task } = require("devtools/shared/task");
|
||||
const { Heritage } = require("devtools/client/shared/widgets/view-helpers");
|
||||
const LineGraphWidget = require("devtools/client/shared/widgets/LineGraphWidget");
|
||||
const MountainGraphWidget = require("devtools/client/shared/widgets/MountainGraphWidget");
|
||||
const { CanvasGraphUtils } = require("devtools/client/shared/widgets/Graphs");
|
||||
|
||||
const promise = require("promise");
|
||||
const EventEmitter = require("devtools/shared/event-emitter");
|
||||
|
||||
const { colorUtils } = require("devtools/shared/css/color");
|
||||
const { getColor } = require("devtools/client/shared/theme");
|
||||
const ProfilerGlobal = require("devtools/client/performance/modules/global");
|
||||
const { MarkersOverview } = require("devtools/client/performance/modules/widgets/markers-overview");
|
||||
const { createTierGraphDataFromFrameNode } = require("devtools/client/performance/modules/logic/jit");
|
||||
|
||||
/**
|
||||
* For line graphs
|
||||
*/
|
||||
// px
|
||||
const HEIGHT = 35;
|
||||
// px
|
||||
const STROKE_WIDTH = 1;
|
||||
const DAMPEN_VALUES = 0.95;
|
||||
const CLIPHEAD_LINE_COLOR = "#666";
|
||||
const SELECTION_LINE_COLOR = "#555";
|
||||
const SELECTION_BACKGROUND_COLOR_NAME = "graphs-blue";
|
||||
const FRAMERATE_GRAPH_COLOR_NAME = "graphs-green";
|
||||
const MEMORY_GRAPH_COLOR_NAME = "graphs-blue";
|
||||
|
||||
/**
|
||||
* For timeline overview
|
||||
*/
|
||||
// px
|
||||
const MARKERS_GRAPH_HEADER_HEIGHT = 14;
|
||||
// px
|
||||
const MARKERS_GRAPH_ROW_HEIGHT = 10;
|
||||
// px
|
||||
const MARKERS_GROUP_VERTICAL_PADDING = 4;
|
||||
|
||||
/**
|
||||
* For optimization graph
|
||||
*/
|
||||
const OPTIMIZATIONS_GRAPH_RESOLUTION = 100;
|
||||
|
||||
/**
|
||||
* A base class for performance graphs to inherit from.
|
||||
*
|
||||
* @param nsIDOMNode parent
|
||||
* The parent node holding the overview.
|
||||
* @param string metric
|
||||
* The unit of measurement for this graph.
|
||||
*/
|
||||
function PerformanceGraph(parent, metric) {
|
||||
LineGraphWidget.call(this, parent, { metric });
|
||||
this.setTheme();
|
||||
}
|
||||
|
||||
PerformanceGraph.prototype = Heritage.extend(LineGraphWidget.prototype, {
|
||||
strokeWidth: STROKE_WIDTH,
|
||||
dampenValuesFactor: DAMPEN_VALUES,
|
||||
fixedHeight: HEIGHT,
|
||||
clipheadLineColor: CLIPHEAD_LINE_COLOR,
|
||||
selectionLineColor: SELECTION_LINE_COLOR,
|
||||
withTooltipArrows: false,
|
||||
withFixedTooltipPositions: true,
|
||||
|
||||
/**
|
||||
* Disables selection and empties this graph.
|
||||
*/
|
||||
clearView: function () {
|
||||
this.selectionEnabled = false;
|
||||
this.dropSelection();
|
||||
this.setData([]);
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the theme via `theme` to either "light" or "dark",
|
||||
* and updates the internal styling to match. Requires a redraw
|
||||
* to see the effects.
|
||||
*/
|
||||
setTheme: function (theme) {
|
||||
theme = theme || "light";
|
||||
let mainColor = getColor(this.mainColor || "graphs-blue", theme);
|
||||
this.backgroundColor = getColor("body-background", theme);
|
||||
this.strokeColor = mainColor;
|
||||
this.backgroundGradientStart = colorUtils.setAlpha(mainColor, 0.2);
|
||||
this.backgroundGradientEnd = colorUtils.setAlpha(mainColor, 0.2);
|
||||
this.selectionBackgroundColor = colorUtils.setAlpha(
|
||||
getColor(SELECTION_BACKGROUND_COLOR_NAME, theme), 0.25);
|
||||
this.selectionStripesColor = "rgba(255, 255, 255, 0.1)";
|
||||
this.maximumLineColor = colorUtils.setAlpha(mainColor, 0.4);
|
||||
this.averageLineColor = colorUtils.setAlpha(mainColor, 0.7);
|
||||
this.minimumLineColor = colorUtils.setAlpha(mainColor, 0.9);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Constructor for the framerate graph. Inherits from PerformanceGraph.
|
||||
*
|
||||
* @param nsIDOMNode parent
|
||||
* The parent node holding the overview.
|
||||
*/
|
||||
function FramerateGraph(parent) {
|
||||
PerformanceGraph.call(this, parent, ProfilerGlobal.L10N.getStr("graphs.fps"));
|
||||
}
|
||||
|
||||
FramerateGraph.prototype = Heritage.extend(PerformanceGraph.prototype, {
|
||||
mainColor: FRAMERATE_GRAPH_COLOR_NAME,
|
||||
setPerformanceData: function ({ duration, ticks }, resolution) {
|
||||
this.dataDuration = duration;
|
||||
return this.setDataFromTimestamps(ticks, resolution, duration);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Constructor for the memory graph. Inherits from PerformanceGraph.
|
||||
*
|
||||
* @param nsIDOMNode parent
|
||||
* The parent node holding the overview.
|
||||
*/
|
||||
function MemoryGraph(parent) {
|
||||
PerformanceGraph.call(this, parent, ProfilerGlobal.L10N.getStr("graphs.memory"));
|
||||
}
|
||||
|
||||
MemoryGraph.prototype = Heritage.extend(PerformanceGraph.prototype, {
|
||||
mainColor: MEMORY_GRAPH_COLOR_NAME,
|
||||
setPerformanceData: function ({ duration, memory }) {
|
||||
this.dataDuration = duration;
|
||||
return this.setData(memory);
|
||||
}
|
||||
});
|
||||
|
||||
function TimelineGraph(parent, filter) {
|
||||
MarkersOverview.call(this, parent, filter);
|
||||
}
|
||||
|
||||
TimelineGraph.prototype = Heritage.extend(MarkersOverview.prototype, {
|
||||
headerHeight: MARKERS_GRAPH_HEADER_HEIGHT,
|
||||
rowHeight: MARKERS_GRAPH_ROW_HEIGHT,
|
||||
groupPadding: MARKERS_GROUP_VERTICAL_PADDING,
|
||||
setPerformanceData: MarkersOverview.prototype.setData
|
||||
});
|
||||
|
||||
/**
|
||||
* Definitions file for GraphsController, indicating the constructor,
|
||||
* selector and other meta for each of the graphs controller by
|
||||
* GraphsController.
|
||||
*/
|
||||
const GRAPH_DEFINITIONS = {
|
||||
memory: {
|
||||
constructor: MemoryGraph,
|
||||
selector: "#memory-overview",
|
||||
},
|
||||
framerate: {
|
||||
constructor: FramerateGraph,
|
||||
selector: "#time-framerate",
|
||||
},
|
||||
timeline: {
|
||||
constructor: TimelineGraph,
|
||||
selector: "#markers-overview",
|
||||
primaryLink: true
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A controller for orchestrating the performance's tool overview graphs. Constructs,
|
||||
* syncs, toggles displays and defines the memory, framerate and timeline view.
|
||||
*
|
||||
* @param {object} definition
|
||||
* @param {DOMElement} root
|
||||
* @param {function} getFilter
|
||||
* @param {function} getTheme
|
||||
*/
|
||||
function GraphsController({ definition, root, getFilter, getTheme }) {
|
||||
this._graphs = {};
|
||||
this._enabled = new Set();
|
||||
this._definition = definition || GRAPH_DEFINITIONS;
|
||||
this._root = root;
|
||||
this._getFilter = getFilter;
|
||||
this._getTheme = getTheme;
|
||||
this._primaryLink = Object.keys(this._definition)
|
||||
.filter(name => this._definition[name].primaryLink)[0];
|
||||
this.$ = root.ownerDocument.querySelector.bind(root.ownerDocument);
|
||||
|
||||
EventEmitter.decorate(this);
|
||||
this._onSelecting = this._onSelecting.bind(this);
|
||||
}
|
||||
|
||||
GraphsController.prototype = {
|
||||
|
||||
/**
|
||||
* Returns the corresponding graph by `graphName`.
|
||||
*/
|
||||
get: function (graphName) {
|
||||
return this._graphs[graphName];
|
||||
},
|
||||
|
||||
/**
|
||||
* Iterates through all graphs and renders the data
|
||||
* from a RecordingModel. Takes a resolution value used in
|
||||
* some graphs.
|
||||
* Saves rendering progress as a promise to be consumed by `destroy`,
|
||||
* to wait for cleaning up rendering during destruction.
|
||||
*/
|
||||
render: Task.async(function* (recordingData, resolution) {
|
||||
// Get the previous render promise so we don't start rendering
|
||||
// until the previous render cycle completes, which can occur
|
||||
// especially when a recording is finished, and triggers a
|
||||
// fresh rendering at a higher rate
|
||||
yield (this._rendering && this._rendering.promise);
|
||||
|
||||
// Check after yielding to ensure we're not tearing down,
|
||||
// as this can create a race condition in tests
|
||||
if (this._destroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._rendering = promise.defer();
|
||||
for (let graph of (yield this._getEnabled())) {
|
||||
yield graph.setPerformanceData(recordingData, resolution);
|
||||
this.emit("rendered", graph.graphName);
|
||||
}
|
||||
this._rendering.resolve();
|
||||
}),
|
||||
|
||||
/**
|
||||
* Destroys the underlying graphs.
|
||||
*/
|
||||
destroy: Task.async(function* () {
|
||||
let primary = this._getPrimaryLink();
|
||||
|
||||
this._destroyed = true;
|
||||
|
||||
if (primary) {
|
||||
primary.off("selecting", this._onSelecting);
|
||||
}
|
||||
|
||||
// If there was rendering, wait until the most recent render cycle
|
||||
// has finished
|
||||
if (this._rendering) {
|
||||
yield this._rendering.promise;
|
||||
}
|
||||
|
||||
for (let graph of this.getWidgets()) {
|
||||
yield graph.destroy();
|
||||
}
|
||||
}),
|
||||
|
||||
/**
|
||||
* Applies the theme to the underlying graphs. Optionally takes
|
||||
* a `redraw` boolean in the options to force redraw.
|
||||
*/
|
||||
setTheme: function (options = {}) {
|
||||
let theme = options.theme || this._getTheme();
|
||||
for (let graph of this.getWidgets()) {
|
||||
graph.setTheme(theme);
|
||||
graph.refresh({ force: options.redraw });
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets up the graph, if needed. Returns a promise resolving
|
||||
* to the graph if it is enabled once it's ready, or otherwise returns
|
||||
* null if disabled.
|
||||
*/
|
||||
isAvailable: Task.async(function* (graphName) {
|
||||
if (!this._enabled.has(graphName)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let graph = this.get(graphName);
|
||||
|
||||
if (!graph) {
|
||||
graph = yield this._construct(graphName);
|
||||
}
|
||||
|
||||
yield graph.ready();
|
||||
return graph;
|
||||
}),
|
||||
|
||||
/**
|
||||
* Enable or disable a subgraph controlled by GraphsController.
|
||||
* This determines what graphs are visible and get rendered.
|
||||
*/
|
||||
enable: function (graphName, isEnabled) {
|
||||
let el = this.$(this._definition[graphName].selector);
|
||||
el.classList[isEnabled ? "remove" : "add"]("hidden");
|
||||
|
||||
// If no status change, just return
|
||||
if (this._enabled.has(graphName) === isEnabled) {
|
||||
return;
|
||||
}
|
||||
if (isEnabled) {
|
||||
this._enabled.add(graphName);
|
||||
} else {
|
||||
this._enabled.delete(graphName);
|
||||
}
|
||||
|
||||
// Invalidate our cache of ready-to-go graphs
|
||||
this._enabledGraphs = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Disables all graphs controller by the GraphsController, and
|
||||
* also hides the root element. This is a one way switch, and used
|
||||
* when older platforms do not have any timeline data.
|
||||
*/
|
||||
disableAll: function () {
|
||||
this._root.classList.add("hidden");
|
||||
// Hide all the subelements
|
||||
Object.keys(this._definition).forEach(graphName => this.enable(graphName, false));
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets a mapped selection on the graph that is the main controller
|
||||
* for keeping the graphs' selections in sync.
|
||||
*/
|
||||
setMappedSelection: function (selection, { mapStart, mapEnd }) {
|
||||
return this._getPrimaryLink().setMappedSelection(selection, { mapStart, mapEnd });
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetches the currently mapped selection. If graphs are not yet rendered,
|
||||
* (which throws in Graphs.js), return null.
|
||||
*/
|
||||
getMappedSelection: function ({ mapStart, mapEnd }) {
|
||||
let primary = this._getPrimaryLink();
|
||||
if (primary && primary.hasData()) {
|
||||
return primary.getMappedSelection({ mapStart, mapEnd });
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns an array of graphs that have been created, not necessarily
|
||||
* enabled currently.
|
||||
*/
|
||||
getWidgets: function () {
|
||||
return Object.keys(this._graphs).map(name => this._graphs[name]);
|
||||
},
|
||||
|
||||
/**
|
||||
* Drops the selection.
|
||||
*/
|
||||
dropSelection: function () {
|
||||
if (this._getPrimaryLink()) {
|
||||
return this._getPrimaryLink().dropSelection();
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Makes sure the selection is enabled or disabled in all the graphs.
|
||||
*/
|
||||
selectionEnabled: Task.async(function* (enabled) {
|
||||
for (let graph of (yield this._getEnabled())) {
|
||||
graph.selectionEnabled = enabled;
|
||||
}
|
||||
}),
|
||||
|
||||
/**
|
||||
* Creates the graph `graphName` and initializes it.
|
||||
*/
|
||||
_construct: Task.async(function* (graphName) {
|
||||
let def = this._definition[graphName];
|
||||
let el = this.$(def.selector);
|
||||
let filter = this._getFilter();
|
||||
let graph = this._graphs[graphName] = new def.constructor(el, filter);
|
||||
graph.graphName = graphName;
|
||||
|
||||
yield graph.ready();
|
||||
|
||||
// Sync the graphs' animations and selections together
|
||||
if (def.primaryLink) {
|
||||
graph.on("selecting", this._onSelecting);
|
||||
} else {
|
||||
CanvasGraphUtils.linkAnimation(this._getPrimaryLink(), graph);
|
||||
CanvasGraphUtils.linkSelection(this._getPrimaryLink(), graph);
|
||||
}
|
||||
|
||||
// Sets the container element's visibility based off of enabled status
|
||||
el.classList[this._enabled.has(graphName) ? "remove" : "add"]("hidden");
|
||||
|
||||
this.setTheme();
|
||||
return graph;
|
||||
}),
|
||||
|
||||
/**
|
||||
* Returns the main graph for this collection, that all graphs
|
||||
* are bound to for syncing and selection.
|
||||
*/
|
||||
_getPrimaryLink: function () {
|
||||
return this.get(this._primaryLink);
|
||||
},
|
||||
|
||||
/**
|
||||
* Emitted when a selection occurs.
|
||||
*/
|
||||
_onSelecting: function () {
|
||||
this.emit("selecting");
|
||||
},
|
||||
|
||||
/**
|
||||
* Resolves to an array with all graphs that are enabled, and
|
||||
* creates them if needed. Different than just iterating over `this._graphs`,
|
||||
* as those could be enabled. Uses caching, as rendering happens many times per second,
|
||||
* compared to how often which graphs/features are changed (rarely).
|
||||
*/
|
||||
_getEnabled: Task.async(function* () {
|
||||
if (this._enabledGraphs) {
|
||||
return this._enabledGraphs;
|
||||
}
|
||||
let enabled = [];
|
||||
for (let graphName of this._enabled) {
|
||||
let graph = yield this.isAvailable(graphName);
|
||||
if (graph) {
|
||||
enabled.push(graph);
|
||||
}
|
||||
}
|
||||
this._enabledGraphs = enabled;
|
||||
return this._enabledGraphs;
|
||||
}),
|
||||
};
|
||||
|
||||
/**
|
||||
* A base class for performance graphs to inherit from.
|
||||
*
|
||||
* @param nsIDOMNode parent
|
||||
* The parent node holding the overview.
|
||||
* @param string metric
|
||||
* The unit of measurement for this graph.
|
||||
*/
|
||||
function OptimizationsGraph(parent) {
|
||||
MountainGraphWidget.call(this, parent);
|
||||
this.setTheme();
|
||||
}
|
||||
|
||||
OptimizationsGraph.prototype = Heritage.extend(MountainGraphWidget.prototype, {
|
||||
|
||||
render: Task.async(function* (threadNode, frameNode) {
|
||||
// Regardless if we draw or clear the graph, wait
|
||||
// until it's ready.
|
||||
yield this.ready();
|
||||
|
||||
if (!threadNode || !frameNode) {
|
||||
this.setData([]);
|
||||
return;
|
||||
}
|
||||
|
||||
let { sampleTimes } = threadNode;
|
||||
|
||||
if (!sampleTimes.length) {
|
||||
this.setData([]);
|
||||
return;
|
||||
}
|
||||
|
||||
// Take startTime/endTime from samples recorded, rather than
|
||||
// using duration directly from threadNode, as the first sample that
|
||||
// equals the startTime does not get recorded.
|
||||
let startTime = sampleTimes[0];
|
||||
let endTime = sampleTimes[sampleTimes.length - 1];
|
||||
|
||||
let bucketSize = (endTime - startTime) / OPTIMIZATIONS_GRAPH_RESOLUTION;
|
||||
let data = createTierGraphDataFromFrameNode(frameNode, sampleTimes, bucketSize);
|
||||
|
||||
// If for some reason we don't have data (like the frameNode doesn't
|
||||
// have optimizations, but it shouldn't be at this point if it doesn't),
|
||||
// log an error.
|
||||
if (!data) {
|
||||
console.error(
|
||||
`FrameNode#${frameNode.location} does not have optimizations data to render.`);
|
||||
return;
|
||||
}
|
||||
|
||||
this.dataOffsetX = startTime;
|
||||
yield this.setData(data);
|
||||
}),
|
||||
|
||||
/**
|
||||
* Sets the theme via `theme` to either "light" or "dark",
|
||||
* and updates the internal styling to match. Requires a redraw
|
||||
* to see the effects.
|
||||
*/
|
||||
setTheme: function (theme) {
|
||||
theme = theme || "light";
|
||||
|
||||
let interpreterColor = getColor("graphs-red", theme);
|
||||
let baselineColor = getColor("graphs-blue", theme);
|
||||
let ionColor = getColor("graphs-green", theme);
|
||||
|
||||
this.format = [
|
||||
{ color: interpreterColor },
|
||||
{ color: baselineColor },
|
||||
{ color: ionColor },
|
||||
];
|
||||
|
||||
this.backgroundColor = getColor("sidebar-background", theme);
|
||||
}
|
||||
});
|
||||
|
||||
exports.OptimizationsGraph = OptimizationsGraph;
|
||||
exports.FramerateGraph = FramerateGraph;
|
||||
exports.MemoryGraph = MemoryGraph;
|
||||
exports.TimelineGraph = TimelineGraph;
|
||||
exports.GraphsController = GraphsController;
|
||||
Loading…
Add table
Add a link
Reference in a new issue