mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-27 08:49:26 +00:00
98894236c9
- Bug 1171200 - Add means of checking if a document links to a manifest. r=billm (066ddad20) - Bug 1167300 - Consolidate the performance tool directory, r=jsantell (c7dd7dc34) - Bug 1167300 - Create a way to get strings from multiple localization files, r=jsantell (0973b8d3e) - modules not in gre (914e4080e) - Bug 1153011 - Remove zoom button from call tree. r=vporof (797b8f91d) - Bug 1151973 - Inverted call tree should be ordered by 'self cost', not 'total cost', r=jsantell (f2800b272) - more gre removal (27aed87a0) - Bug 1144034 - Flamegraph text is barely readable on non-retina display, r=jsantell (cb19fd9f2) - Bug 1151973 - Inverted call tree should be ordered by 'self cost', not 'total cost', r=jsantell (9c579599e) - Bug 1167300 - Fix all performance tool imports to work with the new file locations, r=jsantell (70b2995c4) - Bug 1167298 - Remove the ordinal property on categories, r=jsantell (00b3f5830) - Bug 1167733 - Consolidate prefs access and usage in the new performance tool, r=jsantell (4dab15e7f) - Bug 1167006 - part 3 fully revert merge from 780e1f999f54. (8aaa33c9c) - Bug 1167961 - Task is incorrectly used in compatibility.js, r=jsantell (7291f68d1) - Bug 1138641 - Updated remaining callsites to use newChannel2 in browser/devtools (r=vporof) (60ac4b2c8) - Bug 1164130 - Correctly include RecordingUtils when importing older version 2 profiler data. r=vp (8169d0398) - Bug 1167962 - Keep exports at bottom of modules, r=jsantell (7426919db) - Bug 1167962 - Fix import in synthesizeProfileForTest, r=orange (cc7fab771) - fix merge of later patch Bug 1167006 (c0b57b0e2) - Bug 1157523 - Fix intermittent where markers are selected in the waterfall views when there is no recording selected. r=vp (35cec0bd1) - Bug 1196253 - update in-tree psutil to 3.1.1. r=gps (80f243738)
425 lines
12 KiB
JavaScript
425 lines
12 KiB
JavaScript
/* 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 { Cc, Ci, Cu, Cr } = require("chrome");
|
|
const { Task } = require("resource://gre/modules/Task.jsm");
|
|
const { LineGraphWidget } = require("resource:///modules/devtools/Graphs.jsm");
|
|
const { BarGraphWidget } = require("resource:///modules/devtools/Graphs.jsm");
|
|
const { CanvasGraphUtils } = require("resource:///modules/devtools/Graphs.jsm");
|
|
const { Heritage } = require("resource:///modules/devtools/ViewHelpers.jsm");
|
|
|
|
loader.lazyRequireGetter(this, "promise");
|
|
loader.lazyRequireGetter(this, "EventEmitter",
|
|
"devtools/toolkit/event-emitter");
|
|
|
|
loader.lazyRequireGetter(this, "colorUtils",
|
|
"devtools/css-color", true);
|
|
loader.lazyRequireGetter(this, "getColor",
|
|
"devtools/shared/theme", true);
|
|
loader.lazyRequireGetter(this, "ProfilerGlobal",
|
|
"devtools/performance/global");
|
|
loader.lazyRequireGetter(this, "TimelineGlobal",
|
|
"devtools/performance/global");
|
|
loader.lazyRequireGetter(this, "MarkersOverview",
|
|
"devtools/performance/markers-overview", true);
|
|
|
|
/**
|
|
* For line graphs
|
|
*/
|
|
const HEIGHT = 35; // px
|
|
const STROKE_WIDTH = 1; // px
|
|
const DAMPEN_VALUES = 0.95;
|
|
const CLIPHEAD_LINE_COLOR = "#666";
|
|
const SELECTION_LINE_COLOR = "#555";
|
|
const SELECTION_BACKGROUND_COLOR_NAME = "highlight-blue";
|
|
const FRAMERATE_GRAPH_COLOR_NAME = "highlight-green";
|
|
const MEMORY_GRAPH_COLOR_NAME = "highlight-blue";
|
|
|
|
/**
|
|
* For timeline overview
|
|
*/
|
|
const MARKERS_GRAPH_HEADER_HEIGHT = 14; // px
|
|
const MARKERS_GRAPH_ROW_HEIGHT = 10; // px
|
|
const MARKERS_GROUP_VERTICAL_PADDING = 4; // px
|
|
|
|
/**
|
|
* 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 || "highlight-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);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Constructor for the memory graph. Inherits from PerformanceGraph.
|
|
*
|
|
* @param nsIDOMNode parent
|
|
* The parent node holding the overview.
|
|
*/
|
|
function MemoryGraph(parent) {
|
|
PerformanceGraph.call(this, parent, TimelineGlobal.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, blueprint) {
|
|
MarkersOverview.call(this, parent, blueprint);
|
|
}
|
|
|
|
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",
|
|
needsBlueprints: true,
|
|
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} getBlueprint
|
|
* @param {function} getTheme
|
|
*/
|
|
function GraphsController ({ definition, root, getBlueprint, getTheme }) {
|
|
this._graphs = {};
|
|
this._enabled = new Set();
|
|
this._definition = definition || GRAPH_DEFINITIONS;
|
|
this._root = root;
|
|
this._getBlueprint = getBlueprint;
|
|
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.hidden = !isEnabled;
|
|
|
|
// 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.hidden = true;
|
|
// 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 });
|
|
},
|
|
|
|
getMappedSelection: function ({ mapStart, mapEnd }) {
|
|
if (this._getPrimaryLink()) {
|
|
return this._getPrimaryLink().getMappedSelection({ mapStart, mapEnd });
|
|
} else {
|
|
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();
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 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 blueprint = def.needsBlueprints ? this._getBlueprint() : void 0;
|
|
let graph = this._graphs[graphName] = new def.constructor(el, blueprint);
|
|
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);
|
|
}
|
|
|
|
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;
|
|
if (graph = yield this.isAvailable(graphName)) {
|
|
enabled.push(graph);
|
|
}
|
|
}
|
|
return this._enabledGraphs = enabled;
|
|
}),
|
|
};
|
|
|
|
exports.FramerateGraph = FramerateGraph;
|
|
exports.MemoryGraph = MemoryGraph;
|
|
exports.TimelineGraph = TimelineGraph;
|
|
exports.GraphsController = GraphsController;
|