diff --git a/browser/devtools/performance/modules/front.js b/browser/devtools/performance/modules/front.js index 42ae275df990..69648ac5746c 100644 --- a/browser/devtools/performance/modules/front.js +++ b/browser/devtools/performance/modules/front.js @@ -100,9 +100,9 @@ function PerformanceActorsConnection(target) { PerformanceActorsConnection.prototype = { - // Properties set when mocks are being used - _usingMockMemory: false, - _usingMockTimeline: false, + // Properties set based off of server actor support + _memorySupported: true, + _timelineSupported: true, /** * Initializes a connection to the profiler and other miscellaneous actors. @@ -174,9 +174,9 @@ PerformanceActorsConnection.prototype = { if (supported) { this._timeline = new TimelineFront(this._target.client, this._target.form); } else { - this._usingMockTimeline = true; this._timeline = new compatibility.MockTimelineFront(); } + this._timelineSupported = supported; }, /** @@ -187,9 +187,9 @@ PerformanceActorsConnection.prototype = { if (supported) { this._memory = new MemoryFront(this._target.client, this._target.form); } else { - this._usingMockMemory = true; this._memory = new compatibility.MockMemoryFront(); } + this._memorySupported = supported; }), /** @@ -620,8 +620,8 @@ function PerformanceFront(connection) { this._request = connection._request; // Set when mocks are being used - this._usingMockMemory = connection._usingMockMemory; - this._usingMockTimeline = connection._usingMockTimeline; + this._memorySupported = connection._memorySupported; + this._timelineSupported = connection._timelineSupported; // Pipe the console profile events from the connection // to the front so that the UI can listen. @@ -636,7 +636,8 @@ PerformanceFront.prototype = { * * @param object options * An options object to pass to the actors. Supported properties are - * `withTicks`, `withMemory` and `withAllocations`, `probability` and `maxLogLength`. + * `withTicks`, `withMemory` and `withAllocations`, + * `probability` and `maxLogLength`. * @return object * A promise that is resolved once recording has started. */ @@ -658,12 +659,14 @@ PerformanceFront.prototype = { }, /** - * Returns an object indicating if mock actors are being used or not. + * Returns an object indicating what server actors are available and + * initialized. A falsy value indicates that the server does not support + * that feature, or that mock actors were explicitly requested (tests). */ - getMocksInUse: function () { + getActorSupport: function () { return { - memory: this._usingMockMemory, - timeline: this._usingMockTimeline + memory: this._memorySupported, + timeline: this._timelineSupported }; } }; @@ -673,6 +676,7 @@ PerformanceFront.prototype = { */ function getRecordingModelPrefs () { return { + withMarkers: true, withMemory: Services.prefs.getBoolPref("devtools.performance.ui.enable-memory"), withTicks: Services.prefs.getBoolPref("devtools.performance.ui.enable-framerate"), withAllocations: Services.prefs.getBoolPref("devtools.performance.ui.enable-memory"), diff --git a/browser/devtools/performance/modules/graphs.js b/browser/devtools/performance/modules/graphs.js new file mode 100644 index 000000000000..da9cac416ebf --- /dev/null +++ b/browser/devtools/performance/modules/graphs.js @@ -0,0 +1,409 @@ +/* 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"); + +Cu.import("resource:///modules/devtools/Graphs.jsm"); +Cu.import("resource:///modules/devtools/ViewHelpers.jsm"); +Cu.import("resource://gre/modules/Task.jsm"); +Cu.import("resource://gre/modules/Promise.jsm"); + +const { colorUtils: { setAlpha }} = require("devtools/css-color"); +const { getColor } = require("devtools/shared/theme"); + +loader.lazyRequireGetter(this, "ProfilerGlobal", + "devtools/shared/profiler/global"); +loader.lazyRequireGetter(this, "TimelineGlobal", + "devtools/shared/timeline/global"); +loader.lazyRequireGetter(this, "MarkersOverview", + "devtools/shared/timeline/markers-overview", true); +loader.lazyRequireGetter(this, "EventEmitter", + "devtools/toolkit/event-emitter"); + +/** + * 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 = setAlpha(mainColor, 0.2); + this.backgroundGradientEnd = setAlpha(mainColor, 0.2); + this.selectionBackgroundColor = setAlpha(getColor(SELECTION_BACKGROUND_COLOR_NAME, theme), 0.25); + this.selectionStripesColor = "rgba(255, 255, 255, 0.1)"; + this.maximumLineColor = setAlpha(mainColor, 0.4); + this.averageLineColor = setAlpha(mainColor, 0.7); + this.minimumLineColor = 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 graphName in this._graphs) { + yield this._graphs[graphName].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 in this._graphs) { + this._graphs[graph].setTheme(theme); + this._graphs[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 }) { + return this._getPrimaryLink().getMappedSelection({ mapStart, mapEnd }); + }, + + /** + * 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; diff --git a/browser/devtools/performance/modules/io.js b/browser/devtools/performance/modules/io.js index ef5dd1a924e9..6a27056c6e8e 100644 --- a/browser/devtools/performance/modules/io.js +++ b/browser/devtools/performance/modules/io.js @@ -148,7 +148,15 @@ function convertLegacyData (legacyData) { memory: [], ticks: ticksData, allocations: { sites: [], timestamps: [], frames: [], counts: [] }, - profile: profilerData.profile + profile: profilerData.profile, + // Fake a configuration object here if there's tick data, + // so that it can be rendered + configuration: { + withTicks: !!ticksData.length, + withMarkers: false, + withMemory: false, + withAllocations: false + } }; return data; diff --git a/browser/devtools/performance/modules/performance-graphs.js b/browser/devtools/performance/modules/performance-graphs.js deleted file mode 100644 index 0178bfec7832..000000000000 --- a/browser/devtools/performance/modules/performance-graphs.js +++ /dev/null @@ -1,113 +0,0 @@ -/* 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"); - -Cu.import("resource:///modules/devtools/Graphs.jsm"); -Cu.import("resource:///modules/devtools/ViewHelpers.jsm"); - -const { colorUtils: { setAlpha }} = require("devtools/css-color"); -const { getColor } = require("devtools/shared/theme"); - -loader.lazyRequireGetter(this, "ProfilerGlobal", - "devtools/shared/profiler/global"); -loader.lazyRequireGetter(this, "TimelineGlobal", - "devtools/shared/timeline/global"); - -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"; - -/** - * 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 = setAlpha(mainColor, 0.2); - this.backgroundGradientEnd = setAlpha(mainColor, 0.2); - this.selectionBackgroundColor = setAlpha(getColor(SELECTION_BACKGROUND_COLOR_NAME, theme), 0.25); - this.selectionStripesColor = "rgba(255, 255, 255, 0.1)"; - this.maximumLineColor = setAlpha(mainColor, 0.4); - this.averageLineColor = setAlpha(mainColor, 0.7); - this.minimumLineColor = 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 -}); - -exports.FramerateGraph = FramerateGraph; - -/** - * 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 -}); - -exports.MemoryGraph = MemoryGraph; diff --git a/browser/devtools/performance/modules/recording-model.js b/browser/devtools/performance/modules/recording-model.js index 36be95babb98..6d92bc901be6 100644 --- a/browser/devtools/performance/modules/recording-model.js +++ b/browser/devtools/performance/modules/recording-model.js @@ -22,6 +22,7 @@ const RecordingModel = function (options={}) { this._console = options.console || false; this._configuration = { + withMarkers: options.withMarkers || false, withTicks: options.withTicks || false, withMemory: options.withMemory || false, withAllocations: options.withAllocations || false, @@ -70,6 +71,7 @@ RecordingModel.prototype = { this._ticks = recordingData.ticks; this._allocations = recordingData.allocations; this._profile = recordingData.profile; + this._configuration = recordingData.configuration || {}; }), /** @@ -226,7 +228,8 @@ RecordingModel.prototype = { let ticks = this.getTicks(); let allocations = this.getAllocations(); let profile = this.getProfile(); - return { label, duration, markers, frames, memory, ticks, allocations, profile }; + let configuration = this.getConfiguration(); + return { label, duration, markers, frames, memory, ticks, allocations, profile, configuration }; }, /** @@ -263,10 +266,13 @@ RecordingModel.prototype = { return; } + let config = this.getConfiguration(); + switch (eventName) { // Accumulate timeline markers into an array. Furthermore, the timestamps // do not have a zero epoch, so offset all of them by the start time. case "markers": { + if (!config.withMarkers) { break; } let [markers] = data; RecordingUtils.offsetMarkerTimes(markers, this._timelineStartTime); Array.prototype.push.apply(this._markers, markers); @@ -274,6 +280,7 @@ RecordingModel.prototype = { } // Accumulate stack frames into an array. case "frames": { + if (!config.withMarkers) { break; } let [, frames] = data; Array.prototype.push.apply(this._frames, frames); break; @@ -281,6 +288,7 @@ RecordingModel.prototype = { // Accumulate memory measurements into an array. Furthermore, the timestamp // does not have a zero epoch, so offset it by the actor's start time. case "memory": { + if (!config.withMemory) { break; } let [currentTime, measurement] = data; this._memory.push({ delta: currentTime - this._timelineStartTime, @@ -290,6 +298,7 @@ RecordingModel.prototype = { } // Save the accumulated refresh driver ticks. case "ticks": { + if (!config.withTicks) { break; } let [, timestamps] = data; this._ticks = timestamps; break; @@ -298,6 +307,7 @@ RecordingModel.prototype = { // do not have a zero epoch, and are microseconds instead of milliseconds, // so offset all of them by the start time, also converting from µs to ms. case "allocations": { + if (!config.withAllocations) { break; } let [{ sites, timestamps, frames, counts }] = data; let timeOffset = this._memoryStartTime * 1000; let timeScale = 1000; diff --git a/browser/devtools/performance/moz.build b/browser/devtools/performance/moz.build index 301fa6d85c45..7b83784b013c 100644 --- a/browser/devtools/performance/moz.build +++ b/browser/devtools/performance/moz.build @@ -6,8 +6,8 @@ EXTRA_JS_MODULES.devtools.performance += [ 'modules/compatibility.js', 'modules/front.js', + 'modules/graphs.js', 'modules/io.js', - 'modules/performance-graphs.js', 'modules/recording-model.js', 'modules/recording-utils.js', 'panel.js' diff --git a/browser/devtools/performance/performance-controller.js b/browser/devtools/performance/performance-controller.js index 8ae2d7c84a87..b3a6629e6619 100644 --- a/browser/devtools/performance/performance-controller.js +++ b/browser/devtools/performance/performance-controller.js @@ -27,12 +27,8 @@ devtools.lazyRequireGetter(this, "RecordingUtils", "devtools/performance/recording-utils", true); devtools.lazyRequireGetter(this, "RecordingModel", "devtools/performance/recording-model", true); -devtools.lazyRequireGetter(this, "FramerateGraph", - "devtools/performance/performance-graphs", true); -devtools.lazyRequireGetter(this, "MemoryGraph", - "devtools/performance/performance-graphs", true); -devtools.lazyRequireGetter(this, "MarkersOverview", - "devtools/shared/timeline/markers-overview", true); +devtools.lazyRequireGetter(this, "GraphsController", + "devtools/performance/graphs", true); devtools.lazyRequireGetter(this, "Waterfall", "devtools/shared/timeline/waterfall", true); devtools.lazyRequireGetter(this, "MarkerDetails", @@ -52,8 +48,6 @@ devtools.lazyRequireGetter(this, "FlameGraphUtils", devtools.lazyRequireGetter(this, "FlameGraph", "devtools/shared/widgets/FlameGraph", true); -devtools.lazyImporter(this, "CanvasGraphUtils", - "resource:///modules/devtools/Graphs.jsm"); devtools.lazyImporter(this, "SideMenuWidget", "resource:///modules/devtools/SideMenuWidget.jsm"); devtools.lazyImporter(this, "PluralForm", @@ -291,6 +285,7 @@ let PerformanceController = { */ startRecording: Task.async(function *() { let options = { + withMarkers: true, withMemory: this.getOption("enable-memory"), withTicks: this.getOption("enable-framerate"), withAllocations: this.getOption("enable-memory"), @@ -470,6 +465,42 @@ let PerformanceController = { return this._recordings; }, + /** + * Utility method taking the currently selected recording item's features, or optionally passed + * in recording item, as well as the actor support on the server, returning a boolean + * indicating if the requirements pass or not. Used to toggle features' visibility mostly. + * + * @option {Array} features + * An array of strings indicating what configuration is needed on the recording + * model, like `withTicks`, or `withMemory`. + * @option {Array} actors + * An array of strings indicating what actors must exist. + * @option {boolean} isRecording + * A boolean indicating whether the recording must be either recording or not + * recording. Setting to undefined will allow either state. + * @param {RecordingModel} recording + * An optional recording model to use instead of the currently selected. + * + * @return boolean + */ + isFeatureSupported: function ({ features, actors, isRecording: shouldBeRecording }, recording) { + recording = recording || this.getCurrentRecording(); + let recordingConfig = recording ? recording.getConfiguration() : {}; + let currentRecordingState = recording ? recording.isRecording() : void 0; + let actorsSupported = gFront.getActorSupport(); + + if (shouldBeRecording != null && shouldBeRecording !== currentRecordingState) { + return false; + } + if (actors && !actors.every(a => actorsSupported[a])) { + return false; + } + if (features && !features.every(f => recordingConfig[f])) { + return false; + } + return true; + }, + toString: () => "[object PerformanceController]" }; diff --git a/browser/devtools/performance/performance.xul b/browser/devtools/performance/performance.xul index 6cda61cd69c3..6b6c72968c37 100644 --- a/browser/devtools/performance/performance.xul +++ b/browser/devtools/performance/performance.xul @@ -102,22 +102,27 @@