2014-10-15 07:46:00 +00:00
|
|
|
/* 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";
|
|
|
|
|
2015-05-22 20:25:52 +00:00
|
|
|
const { Task } = require("resource://gre/modules/Task.jsm");
|
|
|
|
const { Heritage, ViewHelpers, WidgetMethods } = require("resource:///modules/devtools/ViewHelpers.jsm");
|
2014-10-15 07:46:00 +00:00
|
|
|
|
2015-05-22 20:25:52 +00:00
|
|
|
loader.lazyRequireGetter(this, "Services");
|
|
|
|
loader.lazyRequireGetter(this, "promise");
|
|
|
|
loader.lazyRequireGetter(this, "EventEmitter",
|
2014-10-15 07:46:00 +00:00
|
|
|
"devtools/toolkit/event-emitter");
|
2015-05-22 20:25:52 +00:00
|
|
|
loader.lazyRequireGetter(this, "DevToolsUtils",
|
2014-10-15 07:46:00 +00:00
|
|
|
"devtools/toolkit/DevToolsUtils");
|
2014-12-23 16:50:50 +00:00
|
|
|
|
2015-05-22 20:25:52 +00:00
|
|
|
// Logic modules
|
|
|
|
|
|
|
|
loader.lazyRequireGetter(this, "L10N",
|
|
|
|
"devtools/performance/global", true);
|
|
|
|
loader.lazyRequireGetter(this, "TIMELINE_BLUEPRINT",
|
|
|
|
"devtools/performance/global", true);
|
|
|
|
loader.lazyRequireGetter(this, "RecordingUtils",
|
2015-01-28 19:30:32 +00:00
|
|
|
"devtools/performance/recording-utils", true);
|
2015-05-22 20:25:52 +00:00
|
|
|
loader.lazyRequireGetter(this, "RecordingModel",
|
2015-01-15 19:54:46 +00:00
|
|
|
"devtools/performance/recording-model", true);
|
2015-05-22 20:25:52 +00:00
|
|
|
loader.lazyRequireGetter(this, "GraphsController",
|
2015-04-25 04:04:02 +00:00
|
|
|
"devtools/performance/graphs", true);
|
2015-05-22 20:25:52 +00:00
|
|
|
loader.lazyRequireGetter(this, "Waterfall",
|
|
|
|
"devtools/performance/waterfall", true);
|
|
|
|
loader.lazyRequireGetter(this, "MarkerDetails",
|
|
|
|
"devtools/performance/marker-details", true);
|
|
|
|
loader.lazyRequireGetter(this, "MarkerUtils",
|
|
|
|
"devtools/performance/marker-utils");
|
|
|
|
loader.lazyRequireGetter(this, "CallView",
|
|
|
|
"devtools/performance/tree-view", true);
|
|
|
|
loader.lazyRequireGetter(this, "ThreadNode",
|
|
|
|
"devtools/performance/tree-model", true);
|
|
|
|
loader.lazyRequireGetter(this, "FrameNode",
|
|
|
|
"devtools/performance/tree-model", true);
|
|
|
|
loader.lazyRequireGetter(this, "JITOptimizations",
|
|
|
|
"devtools/performance/jit", true);
|
|
|
|
|
|
|
|
// Widgets modules
|
|
|
|
|
|
|
|
loader.lazyRequireGetter(this, "OptionsView",
|
2015-01-19 09:37:00 +00:00
|
|
|
"devtools/shared/options-view", true);
|
2015-05-22 20:25:52 +00:00
|
|
|
loader.lazyRequireGetter(this, "FlameGraphUtils",
|
2015-04-06 15:53:00 +00:00
|
|
|
"devtools/shared/widgets/FlameGraph", true);
|
2015-05-22 20:25:52 +00:00
|
|
|
loader.lazyRequireGetter(this, "FlameGraph",
|
2015-04-06 15:53:00 +00:00
|
|
|
"devtools/shared/widgets/FlameGraph", true);
|
2015-05-22 20:25:52 +00:00
|
|
|
loader.lazyRequireGetter(this, "TreeWidget",
|
|
|
|
"devtools/shared/widgets/TreeWidget", true);
|
2014-10-15 07:46:00 +00:00
|
|
|
|
2015-05-22 20:25:52 +00:00
|
|
|
loader.lazyImporter(this, "SideMenuWidget",
|
2015-01-15 19:54:46 +00:00
|
|
|
"resource:///modules/devtools/SideMenuWidget.jsm");
|
2015-05-22 20:25:52 +00:00
|
|
|
loader.lazyImporter(this, "setNamedTimeout",
|
|
|
|
"resource:///modules/devtools/ViewHelpers.jsm");
|
|
|
|
loader.lazyImporter(this, "clearNamedTimeout",
|
|
|
|
"resource:///modules/devtools/ViewHelpers.jsm");
|
|
|
|
loader.lazyImporter(this, "PluralForm",
|
2015-04-05 14:05:00 +00:00
|
|
|
"resource://gre/modules/PluralForm.jsm");
|
2015-01-11 16:45:23 +00:00
|
|
|
|
2015-01-19 09:37:00 +00:00
|
|
|
const BRANCH_NAME = "devtools.performance.ui.";
|
|
|
|
|
2014-12-13 03:31:27 +00:00
|
|
|
// Events emitted by various objects in the panel.
|
2014-10-30 11:35:00 +00:00
|
|
|
const EVENTS = {
|
2015-02-25 22:40:20 +00:00
|
|
|
// Fired by the PerformanceController and OptionsView when a pref changes.
|
2015-02-04 23:20:04 +00:00
|
|
|
PREF_CHANGED: "Performance:PrefChanged",
|
2015-01-19 09:37:00 +00:00
|
|
|
|
2015-03-31 08:18:00 +00:00
|
|
|
// Fired by the PerformanceController when the devtools theme changes.
|
|
|
|
THEME_CHANGED: "Performance:ThemeChanged",
|
|
|
|
|
2015-02-25 22:40:20 +00:00
|
|
|
// Emitted by the PerformanceView when the state (display mode) changes,
|
|
|
|
// for example when switching between "empty", "recording" or "recorded".
|
|
|
|
// This causes certain panels to be hidden or visible.
|
2015-02-06 18:20:19 +00:00
|
|
|
UI_STATE_CHANGED: "Performance:UI:StateChanged",
|
2015-01-13 08:26:00 +00:00
|
|
|
|
2015-02-04 21:35:31 +00:00
|
|
|
// Emitted by the PerformanceView on clear button click
|
|
|
|
UI_CLEAR_RECORDINGS: "Performance:UI:ClearRecordings",
|
|
|
|
|
2014-12-23 16:50:50 +00:00
|
|
|
// Emitted by the PerformanceView on record button click
|
|
|
|
UI_START_RECORDING: "Performance:UI:StartRecording",
|
|
|
|
UI_STOP_RECORDING: "Performance:UI:StopRecording",
|
|
|
|
|
2015-01-13 08:26:00 +00:00
|
|
|
// Emitted by the PerformanceView on import button click
|
2014-12-23 16:50:50 +00:00
|
|
|
UI_IMPORT_RECORDING: "Performance:UI:ImportRecording",
|
2015-01-13 08:26:00 +00:00
|
|
|
// Emitted by the RecordingsView on export button click
|
2014-12-23 16:50:50 +00:00
|
|
|
UI_EXPORT_RECORDING: "Performance:UI:ExportRecording",
|
|
|
|
|
2014-12-13 03:31:27 +00:00
|
|
|
// When a recording is started or stopped via the PerformanceController
|
2014-10-30 11:35:00 +00:00
|
|
|
RECORDING_STARTED: "Performance:RecordingStarted",
|
|
|
|
RECORDING_STOPPED: "Performance:RecordingStopped",
|
2015-01-22 17:20:55 +00:00
|
|
|
RECORDING_WILL_START: "Performance:RecordingWillStart",
|
|
|
|
RECORDING_WILL_STOP: "Performance:RecordingWillStop",
|
2014-12-13 03:31:27 +00:00
|
|
|
|
2015-02-06 18:20:19 +00:00
|
|
|
// Emitted by the PerformanceController or RecordingView
|
|
|
|
// when a recording model is selected
|
|
|
|
RECORDING_SELECTED: "Performance:RecordingSelected",
|
|
|
|
|
2015-02-04 21:35:31 +00:00
|
|
|
// When recordings have been cleared out
|
|
|
|
RECORDINGS_CLEARED: "Performance:RecordingsCleared",
|
|
|
|
|
2014-12-23 16:50:50 +00:00
|
|
|
// When a recording is imported or exported via the PerformanceController
|
|
|
|
RECORDING_IMPORTED: "Performance:RecordingImported",
|
|
|
|
RECORDING_EXPORTED: "Performance:RecordingExported",
|
2014-10-30 11:35:00 +00:00
|
|
|
|
2015-05-08 23:19:58 +00:00
|
|
|
// When the front has updated information on the profiler's circular buffer
|
|
|
|
PROFILER_STATUS_UPDATED: "Performance:BufferUpdated",
|
|
|
|
|
|
|
|
// When the PerformanceView updates the display of the buffer status
|
|
|
|
UI_BUFFER_STATUS_UPDATED: "Performance:UI:BufferUpdated",
|
2014-11-18 13:37:00 +00:00
|
|
|
|
2015-03-25 17:08:40 +00:00
|
|
|
// Emitted by the JITOptimizationsView when it renders new optimization
|
|
|
|
// data and clears the optimization data
|
|
|
|
OPTIMIZATIONS_RESET: "Performance:UI:OptimizationsReset",
|
|
|
|
OPTIMIZATIONS_RENDERED: "Performance:UI:OptimizationsRendered",
|
|
|
|
|
2014-11-18 13:37:00 +00:00
|
|
|
// Emitted by the OverviewView when more data has been rendered
|
2014-11-20 12:02:00 +00:00
|
|
|
OVERVIEW_RENDERED: "Performance:UI:OverviewRendered",
|
2014-12-13 03:31:27 +00:00
|
|
|
FRAMERATE_GRAPH_RENDERED: "Performance:UI:OverviewFramerateRendered",
|
|
|
|
MARKERS_GRAPH_RENDERED: "Performance:UI:OverviewMarkersRendered",
|
|
|
|
MEMORY_GRAPH_RENDERED: "Performance:UI:OverviewMemoryRendered",
|
|
|
|
|
2014-11-25 14:01:00 +00:00
|
|
|
// Emitted by the OverviewView when a range has been selected in the graphs
|
|
|
|
OVERVIEW_RANGE_SELECTED: "Performance:UI:OverviewRangeSelected",
|
|
|
|
// Emitted by the OverviewView when a selection range has been removed
|
|
|
|
OVERVIEW_RANGE_CLEARED: "Performance:UI:OverviewRangeCleared",
|
2014-11-20 12:02:00 +00:00
|
|
|
|
2014-12-03 15:36:00 +00:00
|
|
|
// Emitted by the DetailsView when a subview is selected
|
|
|
|
DETAILS_VIEW_SELECTED: "Performance:UI:DetailsViewSelected",
|
|
|
|
|
|
|
|
// Emitted by the WaterfallView when it has been rendered
|
2015-01-11 16:45:23 +00:00
|
|
|
WATERFALL_RENDERED: "Performance:UI:WaterfallRendered",
|
|
|
|
|
2015-01-28 19:30:32 +00:00
|
|
|
// Emitted by the JsCallTreeView when a call tree has been rendered
|
|
|
|
JS_CALL_TREE_RENDERED: "Performance:UI:JsCallTreeRendered",
|
|
|
|
|
|
|
|
// Emitted by the JsFlameGraphView when it has been rendered
|
|
|
|
JS_FLAMEGRAPH_RENDERED: "Performance:UI:JsFlameGraphRendered",
|
|
|
|
|
|
|
|
// Emitted by the MemoryCallTreeView when a call tree has been rendered
|
|
|
|
MEMORY_CALL_TREE_RENDERED: "Performance:UI:MemoryCallTreeRendered",
|
|
|
|
|
|
|
|
// Emitted by the MemoryFlameGraphView when it has been rendered
|
|
|
|
MEMORY_FLAMEGRAPH_RENDERED: "Performance:UI:MemoryFlameGraphRendered",
|
2015-01-22 17:20:55 +00:00
|
|
|
|
|
|
|
// When a source is shown in the JavaScript Debugger at a specific location.
|
|
|
|
SOURCE_SHOWN_IN_JS_DEBUGGER: "Performance:UI:SourceShownInJsDebugger",
|
|
|
|
SOURCE_NOT_FOUND_IN_JS_DEBUGGER: "Performance:UI:SourceNotFoundInJsDebugger"
|
2014-10-30 11:35:00 +00:00
|
|
|
};
|
|
|
|
|
2014-10-15 07:46:00 +00:00
|
|
|
/**
|
|
|
|
* The current target and the profiler connection, set by this tool's host.
|
|
|
|
*/
|
|
|
|
let gToolbox, gTarget, gFront;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Initializes the profiler controller and views.
|
|
|
|
*/
|
|
|
|
let startupPerformance = Task.async(function*() {
|
|
|
|
yield promise.all([
|
2014-10-30 11:35:00 +00:00
|
|
|
PerformanceController.initialize(),
|
2014-11-20 12:02:00 +00:00
|
|
|
PerformanceView.initialize()
|
2014-10-15 07:46:00 +00:00
|
|
|
]);
|
|
|
|
});
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Destroys the profiler controller and views.
|
|
|
|
*/
|
|
|
|
let shutdownPerformance = Task.async(function*() {
|
|
|
|
yield promise.all([
|
2014-10-30 11:35:00 +00:00
|
|
|
PerformanceController.destroy(),
|
2014-11-20 12:02:00 +00:00
|
|
|
PerformanceView.destroy()
|
2014-10-15 07:46:00 +00:00
|
|
|
]);
|
|
|
|
});
|
|
|
|
|
|
|
|
/**
|
2014-10-30 11:35:00 +00:00
|
|
|
* Functions handling target-related lifetime events and
|
|
|
|
* UI interaction.
|
2014-10-15 07:46:00 +00:00
|
|
|
*/
|
2014-10-30 11:35:00 +00:00
|
|
|
let PerformanceController = {
|
2015-01-13 08:26:00 +00:00
|
|
|
_recordings: [],
|
|
|
|
_currentRecording: null,
|
2014-12-13 03:31:27 +00:00
|
|
|
|
2014-10-15 07:46:00 +00:00
|
|
|
/**
|
2014-10-30 11:35:00 +00:00
|
|
|
* Listen for events emitted by the current tab target and
|
|
|
|
* main UI events.
|
2014-10-15 07:46:00 +00:00
|
|
|
*/
|
2015-01-19 09:37:00 +00:00
|
|
|
initialize: Task.async(function* () {
|
2014-10-30 11:35:00 +00:00
|
|
|
this.startRecording = this.startRecording.bind(this);
|
|
|
|
this.stopRecording = this.stopRecording.bind(this);
|
2014-12-23 16:50:50 +00:00
|
|
|
this.importRecording = this.importRecording.bind(this);
|
|
|
|
this.exportRecording = this.exportRecording.bind(this);
|
2015-02-04 21:35:31 +00:00
|
|
|
this.clearRecordings = this.clearRecordings.bind(this);
|
2015-01-13 08:26:00 +00:00
|
|
|
this._onRecordingSelectFromView = this._onRecordingSelectFromView.bind(this);
|
2015-01-19 09:37:00 +00:00
|
|
|
this._onPrefChanged = this._onPrefChanged.bind(this);
|
2015-03-31 08:18:00 +00:00
|
|
|
this._onThemeChanged = this._onThemeChanged.bind(this);
|
2015-05-02 23:47:41 +00:00
|
|
|
this._onRecordingStateChange = this._onRecordingStateChange.bind(this);
|
2015-05-08 23:19:58 +00:00
|
|
|
this._onProfilerStatusUpdated = this._onProfilerStatusUpdated.bind(this);
|
2014-10-30 11:35:00 +00:00
|
|
|
|
2015-05-19 00:44:49 +00:00
|
|
|
// Store data regarding if e10s is enabled.
|
|
|
|
this._e10s = Services.appinfo.browserTabsRemoteAutostart;
|
|
|
|
|
|
|
|
this._setMultiprocessAttributes();
|
|
|
|
|
2015-02-25 22:40:20 +00:00
|
|
|
// All boolean prefs should be handled via the OptionsView in the
|
|
|
|
// ToolbarView, so that they may be accessible via the "gear" menu.
|
|
|
|
// Every other pref should be registered here.
|
|
|
|
this._nonBooleanPrefs = new ViewHelpers.Prefs("devtools.performance", {
|
2015-03-10 17:24:48 +00:00
|
|
|
"hidden-markers": ["Json", "timeline.hidden-markers"],
|
|
|
|
"memory-sample-probability": ["Float", "memory.sample-probability"],
|
2015-04-14 01:00:02 +00:00
|
|
|
"memory-max-log-length": ["Int", "memory.max-log-length"],
|
|
|
|
"profiler-buffer-size": ["Int", "profiler.buffer-size"],
|
|
|
|
"profiler-sample-frequency": ["Int", "profiler.sample-frequency-khz"],
|
2015-02-25 22:40:20 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
this._nonBooleanPrefs.registerObserver();
|
|
|
|
this._nonBooleanPrefs.on("pref-changed", this._onPrefChanged);
|
|
|
|
|
2015-05-02 23:47:41 +00:00
|
|
|
gFront.on("recording-starting", this._onRecordingStateChange);
|
|
|
|
gFront.on("recording-started", this._onRecordingStateChange);
|
|
|
|
gFront.on("recording-stopping", this._onRecordingStateChange);
|
|
|
|
gFront.on("recording-stopped", this._onRecordingStateChange);
|
2015-05-08 23:19:58 +00:00
|
|
|
gFront.on("profiler-status", this._onProfilerStatusUpdated);
|
2015-01-19 09:37:00 +00:00
|
|
|
ToolbarView.on(EVENTS.PREF_CHANGED, this._onPrefChanged);
|
2014-10-30 11:35:00 +00:00
|
|
|
PerformanceView.on(EVENTS.UI_START_RECORDING, this.startRecording);
|
|
|
|
PerformanceView.on(EVENTS.UI_STOP_RECORDING, this.stopRecording);
|
2014-12-23 16:50:50 +00:00
|
|
|
PerformanceView.on(EVENTS.UI_IMPORT_RECORDING, this.importRecording);
|
2015-02-04 21:35:31 +00:00
|
|
|
PerformanceView.on(EVENTS.UI_CLEAR_RECORDINGS, this.clearRecordings);
|
2015-01-13 08:26:00 +00:00
|
|
|
RecordingsView.on(EVENTS.UI_EXPORT_RECORDING, this.exportRecording);
|
|
|
|
RecordingsView.on(EVENTS.RECORDING_SELECTED, this._onRecordingSelectFromView);
|
2014-12-23 16:50:50 +00:00
|
|
|
|
2015-03-31 08:18:00 +00:00
|
|
|
gDevTools.on("pref-changed", this._onThemeChanged);
|
2015-01-19 09:37:00 +00:00
|
|
|
}),
|
2014-10-15 07:46:00 +00:00
|
|
|
|
|
|
|
/**
|
2014-10-30 11:35:00 +00:00
|
|
|
* Remove events handled by the PerformanceController
|
2014-10-15 07:46:00 +00:00
|
|
|
*/
|
|
|
|
destroy: function() {
|
2015-02-25 22:40:20 +00:00
|
|
|
this._nonBooleanPrefs.unregisterObserver();
|
|
|
|
this._nonBooleanPrefs.off("pref-changed", this._onPrefChanged);
|
|
|
|
|
2015-05-02 23:47:41 +00:00
|
|
|
gFront.off("recording-starting", this._onRecordingStateChange);
|
|
|
|
gFront.off("recording-started", this._onRecordingStateChange);
|
|
|
|
gFront.off("recording-stopping", this._onRecordingStateChange);
|
|
|
|
gFront.off("recording-stopped", this._onRecordingStateChange);
|
2015-05-22 20:29:41 +00:00
|
|
|
gFront.off("profiler-status", this._onProfilerStatusUpdated);
|
2015-01-19 09:37:00 +00:00
|
|
|
ToolbarView.off(EVENTS.PREF_CHANGED, this._onPrefChanged);
|
2014-10-30 11:35:00 +00:00
|
|
|
PerformanceView.off(EVENTS.UI_START_RECORDING, this.startRecording);
|
|
|
|
PerformanceView.off(EVENTS.UI_STOP_RECORDING, this.stopRecording);
|
2014-12-23 16:50:50 +00:00
|
|
|
PerformanceView.off(EVENTS.UI_IMPORT_RECORDING, this.importRecording);
|
2015-02-04 21:35:31 +00:00
|
|
|
PerformanceView.off(EVENTS.UI_CLEAR_RECORDINGS, this.clearRecordings);
|
2015-01-13 08:26:00 +00:00
|
|
|
RecordingsView.off(EVENTS.UI_EXPORT_RECORDING, this.exportRecording);
|
|
|
|
RecordingsView.off(EVENTS.RECORDING_SELECTED, this._onRecordingSelectFromView);
|
2014-12-23 16:50:50 +00:00
|
|
|
|
2015-03-31 08:18:00 +00:00
|
|
|
gDevTools.off("pref-changed", this._onThemeChanged);
|
2014-10-30 11:35:00 +00:00
|
|
|
},
|
|
|
|
|
2015-03-31 08:18:00 +00:00
|
|
|
/**
|
|
|
|
* Returns the current devtools theme.
|
|
|
|
*/
|
|
|
|
getTheme: function () {
|
|
|
|
return Services.prefs.getCharPref("devtools.theme");
|
|
|
|
},
|
|
|
|
|
2015-01-19 09:37:00 +00:00
|
|
|
/**
|
2015-02-25 22:40:20 +00:00
|
|
|
* Get a boolean preference setting from `prefName` via the underlying
|
2015-01-19 09:37:00 +00:00
|
|
|
* OptionsView in the ToolbarView.
|
2015-02-25 22:40:20 +00:00
|
|
|
*
|
|
|
|
* @param string prefName
|
|
|
|
* @return boolean
|
2015-01-19 09:37:00 +00:00
|
|
|
*/
|
2015-02-25 22:40:20 +00:00
|
|
|
getOption: function (prefName) {
|
2015-01-19 09:37:00 +00:00
|
|
|
return ToolbarView.optionsView.getPref(prefName);
|
|
|
|
},
|
|
|
|
|
2015-02-25 22:40:20 +00:00
|
|
|
/**
|
|
|
|
* Get a preference setting from `prefName`.
|
|
|
|
* @param string prefName
|
|
|
|
* @return object
|
|
|
|
*/
|
|
|
|
getPref: function (prefName) {
|
|
|
|
return this._nonBooleanPrefs[prefName];
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set a preference setting from `prefName`.
|
|
|
|
* @param string prefName
|
|
|
|
* @param object prefValue
|
|
|
|
*/
|
|
|
|
setPref: function (prefName, prefValue) {
|
|
|
|
this._nonBooleanPrefs[prefName] = prefValue;
|
|
|
|
},
|
|
|
|
|
2014-10-30 11:35:00 +00:00
|
|
|
/**
|
|
|
|
* Starts recording with the PerformanceFront. Emits `EVENTS.RECORDING_STARTED`
|
2014-12-13 03:31:27 +00:00
|
|
|
* when the front has started to record.
|
2014-10-30 11:35:00 +00:00
|
|
|
*/
|
|
|
|
startRecording: Task.async(function *() {
|
2015-04-14 15:58:58 +00:00
|
|
|
let options = {
|
2015-05-15 18:35:26 +00:00
|
|
|
withMarkers: true,
|
|
|
|
withMemory: this.getOption("enable-memory"),
|
2015-03-13 15:53:23 +00:00
|
|
|
withTicks: this.getOption("enable-framerate"),
|
2015-05-15 18:35:26 +00:00
|
|
|
withAllocations: this.getOption("enable-memory"),
|
2015-03-13 15:53:23 +00:00
|
|
|
allocationsSampleProbability: this.getPref("memory-sample-probability"),
|
2015-04-14 01:00:02 +00:00
|
|
|
allocationsMaxLogLength: this.getPref("memory-max-log-length"),
|
|
|
|
bufferSize: this.getPref("profiler-buffer-size"),
|
|
|
|
sampleFrequency: this.getPref("profiler-sample-frequency")
|
2015-04-14 15:58:58 +00:00
|
|
|
};
|
2015-01-21 17:31:33 +00:00
|
|
|
|
2015-05-02 23:47:41 +00:00
|
|
|
yield gFront.startRecording(options);
|
2014-10-30 11:35:00 +00:00
|
|
|
}),
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Stops recording with the PerformanceFront. Emits `EVENTS.RECORDING_STOPPED`
|
2014-12-13 03:31:27 +00:00
|
|
|
* when the front has stopped recording.
|
2014-10-30 11:35:00 +00:00
|
|
|
*/
|
|
|
|
stopRecording: Task.async(function *() {
|
2015-04-14 15:58:58 +00:00
|
|
|
let recording = this.getLatestManualRecording();
|
|
|
|
yield gFront.stopRecording(recording);
|
2014-11-18 13:37:00 +00:00
|
|
|
}),
|
|
|
|
|
2014-12-23 16:50:50 +00:00
|
|
|
/**
|
2015-01-22 17:20:55 +00:00
|
|
|
* Saves the given recording to a file. Emits `EVENTS.RECORDING_EXPORTED`
|
|
|
|
* when the file was saved.
|
2014-12-23 16:50:50 +00:00
|
|
|
*
|
2015-01-13 08:26:00 +00:00
|
|
|
* @param RecordingModel recording
|
|
|
|
* The model that holds the recording data.
|
2014-12-23 16:50:50 +00:00
|
|
|
* @param nsILocalFile file
|
|
|
|
* The file to stream the data into.
|
|
|
|
*/
|
2015-01-13 08:26:00 +00:00
|
|
|
exportRecording: Task.async(function*(_, recording, file) {
|
2015-01-22 17:20:55 +00:00
|
|
|
yield recording.exportRecording(file);
|
|
|
|
this.emit(EVENTS.RECORDING_EXPORTED, recording);
|
2014-12-23 16:50:50 +00:00
|
|
|
}),
|
|
|
|
|
2015-02-04 21:35:31 +00:00
|
|
|
/**
|
|
|
|
* Clears all recordings from the list as well as the current recording.
|
|
|
|
* Emits `EVENTS.RECORDINGS_CLEARED` when complete so other components can clean up.
|
|
|
|
*/
|
|
|
|
clearRecordings: Task.async(function* () {
|
2015-04-14 15:58:58 +00:00
|
|
|
let latest = this.getLatestManualRecording();
|
2015-02-04 21:35:31 +00:00
|
|
|
if (latest && latest.isRecording()) {
|
|
|
|
yield this.stopRecording();
|
|
|
|
}
|
2015-05-02 23:47:41 +00:00
|
|
|
// If last recording is not recording, but finalizing itself,
|
|
|
|
// wait for that to finish
|
|
|
|
if (latest && !latest.isCompleted()) {
|
|
|
|
yield this.once(EVENTS.RECORDING_STOPPED);
|
|
|
|
}
|
2015-02-04 21:35:31 +00:00
|
|
|
|
|
|
|
this._recordings.length = 0;
|
|
|
|
this.setCurrentRecording(null);
|
|
|
|
this.emit(EVENTS.RECORDINGS_CLEARED);
|
|
|
|
}),
|
|
|
|
|
2014-12-23 16:50:50 +00:00
|
|
|
/**
|
2015-01-22 17:20:55 +00:00
|
|
|
* Loads a recording from a file, adding it to the recordings list. Emits
|
|
|
|
* `EVENTS.RECORDING_IMPORTED` when the file was loaded.
|
2014-12-23 16:50:50 +00:00
|
|
|
*
|
|
|
|
* @param nsILocalFile file
|
|
|
|
* The file to import the data from.
|
|
|
|
*/
|
|
|
|
importRecording: Task.async(function*(_, file) {
|
2015-04-14 15:58:58 +00:00
|
|
|
let recording = new RecordingModel();
|
|
|
|
this._recordings.push(recording);
|
2015-01-16 18:44:24 +00:00
|
|
|
yield recording.importRecording(file);
|
2014-12-23 16:50:50 +00:00
|
|
|
|
2015-01-16 18:44:24 +00:00
|
|
|
this.emit(EVENTS.RECORDING_IMPORTED, recording);
|
2015-01-13 08:26:00 +00:00
|
|
|
}),
|
2014-12-23 16:50:50 +00:00
|
|
|
|
2015-01-13 08:26:00 +00:00
|
|
|
/**
|
2015-04-14 15:58:58 +00:00
|
|
|
* Sets the currently active RecordingModel. Should rarely be called directly,
|
|
|
|
* as RecordingsView handles this when manually selected a recording item. Exceptions
|
|
|
|
* are when clearing the view.
|
2015-01-16 18:44:24 +00:00
|
|
|
* @param RecordingModel recording
|
2015-01-13 08:26:00 +00:00
|
|
|
*/
|
|
|
|
setCurrentRecording: function (recording) {
|
|
|
|
if (this._currentRecording !== recording) {
|
|
|
|
this._currentRecording = recording;
|
|
|
|
this.emit(EVENTS.RECORDING_SELECTED, recording);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
2015-01-16 18:44:24 +00:00
|
|
|
* Gets the currently active RecordingModel.
|
|
|
|
* @return RecordingModel
|
2015-01-13 08:26:00 +00:00
|
|
|
*/
|
|
|
|
getCurrentRecording: function () {
|
|
|
|
return this._currentRecording;
|
|
|
|
},
|
2014-12-23 16:50:50 +00:00
|
|
|
|
|
|
|
/**
|
2015-01-16 18:44:24 +00:00
|
|
|
* Get most recently added recording that was triggered manually (via UI).
|
|
|
|
* @return RecordingModel
|
2014-11-18 13:37:00 +00:00
|
|
|
*/
|
2015-04-14 15:58:58 +00:00
|
|
|
getLatestManualRecording: function () {
|
2015-01-13 08:26:00 +00:00
|
|
|
for (let i = this._recordings.length - 1; i >= 0; i--) {
|
2015-04-14 15:58:58 +00:00
|
|
|
let model = this._recordings[i];
|
|
|
|
if (!model.isConsole() && !model.isImported()) {
|
|
|
|
return this._recordings[i];
|
|
|
|
}
|
2014-12-13 03:31:27 +00:00
|
|
|
}
|
2015-01-13 08:26:00 +00:00
|
|
|
return null;
|
|
|
|
},
|
2014-12-13 03:31:27 +00:00
|
|
|
|
2015-02-25 22:40:20 +00:00
|
|
|
/**
|
|
|
|
* Gets the current timeline blueprint without the hidden markers.
|
|
|
|
* @return object
|
|
|
|
*/
|
|
|
|
getTimelineBlueprint: function() {
|
|
|
|
let blueprint = TIMELINE_BLUEPRINT;
|
|
|
|
let hiddenMarkers = this.getPref("hidden-markers");
|
|
|
|
return RecordingUtils.getFilteredBlueprint({ blueprint, hiddenMarkers });
|
|
|
|
},
|
|
|
|
|
2015-01-13 08:26:00 +00:00
|
|
|
/**
|
2015-01-16 18:44:24 +00:00
|
|
|
* Fired from RecordingsView, we listen on the PerformanceController so we can
|
|
|
|
* set it here and re-emit on the controller, where all views can listen.
|
2015-01-13 08:26:00 +00:00
|
|
|
*/
|
|
|
|
_onRecordingSelectFromView: function (_, recording) {
|
|
|
|
this.setCurrentRecording(recording);
|
2015-01-19 09:37:00 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fired when the ToolbarView fires a PREF_CHANGED event.
|
|
|
|
* with the value.
|
|
|
|
*/
|
2015-02-25 22:40:20 +00:00
|
|
|
_onPrefChanged: function (_, prefName, prefValue) {
|
|
|
|
this.emit(EVENTS.PREF_CHANGED, prefName, prefValue);
|
2015-02-07 01:45:32 +00:00
|
|
|
},
|
|
|
|
|
2015-03-31 08:18:00 +00:00
|
|
|
/*
|
|
|
|
* Called when the developer tools theme changes.
|
|
|
|
*/
|
|
|
|
_onThemeChanged: function (_, data) {
|
|
|
|
// Right now, gDevTools only emits `pref-changed` for the theme,
|
|
|
|
// but this could change in the future.
|
|
|
|
if (data.pref !== "devtools.theme") {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.emit(EVENTS.THEME_CHANGED, data.newValue);
|
|
|
|
},
|
|
|
|
|
2015-05-08 23:19:58 +00:00
|
|
|
/**
|
|
|
|
* Emitted when the front updates RecordingModel's buffer status.
|
|
|
|
*/
|
|
|
|
_onProfilerStatusUpdated: function (_, data) {
|
|
|
|
this.emit(EVENTS.PROFILER_STATUS_UPDATED, data);
|
|
|
|
},
|
|
|
|
|
2015-04-14 15:58:58 +00:00
|
|
|
/**
|
2015-05-02 23:47:41 +00:00
|
|
|
* Fired when a recording model changes state.
|
|
|
|
*
|
|
|
|
* @param {string} state
|
|
|
|
* @param {RecordingModel} model
|
2015-04-14 15:58:58 +00:00
|
|
|
*/
|
2015-05-02 23:47:41 +00:00
|
|
|
_onRecordingStateChange: function (state, model) {
|
2015-05-19 22:06:59 +00:00
|
|
|
// If we get a state change for a recording that isn't being tracked in the front,
|
|
|
|
// just ignore it. This can occur when stopping a profile via console that was cleared.
|
|
|
|
if (state !== "recording-starting" && this.getRecordings().indexOf(model) === -1) {
|
|
|
|
return;
|
|
|
|
}
|
2015-05-02 23:47:41 +00:00
|
|
|
switch (state) {
|
|
|
|
// Fired when a RecordingModel was just created from the front
|
|
|
|
case "recording-starting":
|
|
|
|
// When a recording is just starting, store it internally
|
|
|
|
this._recordings.push(model);
|
|
|
|
this.emit(EVENTS.RECORDING_WILL_START, model);
|
|
|
|
break;
|
|
|
|
// Fired when a RecordingModel has started recording
|
|
|
|
case "recording-started":
|
|
|
|
this.emit(EVENTS.RECORDING_STARTED, model);
|
|
|
|
break;
|
|
|
|
// Fired when a RecordingModel is no longer recording, and
|
|
|
|
// starting to fetch all the profiler data
|
|
|
|
case "recording-stopping":
|
|
|
|
this.emit(EVENTS.RECORDING_WILL_STOP, model);
|
|
|
|
break;
|
|
|
|
// Fired when a RecordingModel is finished fetching all of its data
|
|
|
|
case "recording-stopped":
|
|
|
|
this.emit(EVENTS.RECORDING_STOPPED, model);
|
|
|
|
break;
|
|
|
|
}
|
2015-04-14 15:58:58 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the internal store of recording models.
|
|
|
|
*/
|
|
|
|
getRecordings: function () {
|
|
|
|
return this._recordings;
|
|
|
|
},
|
|
|
|
|
2015-04-25 04:04:02 +00:00
|
|
|
/**
|
|
|
|
* 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<string>} features
|
|
|
|
* An array of strings indicating what configuration is needed on the recording
|
|
|
|
* model, like `withTicks`, or `withMemory`.
|
|
|
|
* @option {Array<string>} actors
|
|
|
|
* An array of strings indicating what actors must exist.
|
2015-05-02 23:47:41 +00:00
|
|
|
* @option {boolean} mustBeCompleted
|
|
|
|
* A boolean indicating whether the recording must be either completed or not.
|
|
|
|
* Setting to undefined will allow either state.
|
2015-04-25 04:04:02 +00:00
|
|
|
* @param {RecordingModel} recording
|
|
|
|
* An optional recording model to use instead of the currently selected.
|
|
|
|
*
|
|
|
|
* @return boolean
|
|
|
|
*/
|
2015-05-02 23:47:41 +00:00
|
|
|
isFeatureSupported: function ({ features, actors, mustBeCompleted }, recording) {
|
2015-04-25 04:04:02 +00:00
|
|
|
recording = recording || this.getCurrentRecording();
|
|
|
|
let recordingConfig = recording ? recording.getConfiguration() : {};
|
2015-05-02 23:47:41 +00:00
|
|
|
let currentCompletedState = recording ? recording.isCompleted() : void 0;
|
2015-04-25 04:04:02 +00:00
|
|
|
let actorsSupported = gFront.getActorSupport();
|
|
|
|
|
2015-05-02 23:47:41 +00:00
|
|
|
if (mustBeCompleted != null && mustBeCompleted !== currentCompletedState) {
|
2015-04-25 04:04:02 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (actors && !actors.every(a => actorsSupported[a])) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (features && !features.every(f => recordingConfig[f])) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
|
2015-05-19 00:44:49 +00:00
|
|
|
/**
|
|
|
|
* Returns an object with `supported` and `enabled` properties indicating
|
|
|
|
* whether or not the platform is capable of turning on e10s and whether or not
|
|
|
|
* it's already enabled, respectively.
|
|
|
|
*
|
|
|
|
* @return {object}
|
|
|
|
*/
|
|
|
|
getMultiprocessStatus: function () {
|
|
|
|
// If testing, set both supported and enabled to true so we
|
|
|
|
// have realtime rendering tests in non-e10s. This function is
|
|
|
|
// overridden wholesale in tests when we want to test multiprocess support
|
|
|
|
// specifically.
|
|
|
|
if (gDevTools.testing) {
|
|
|
|
return { supported: true, enabled: true };
|
|
|
|
}
|
|
|
|
let supported = SYSTEM.MULTIPROCESS_SUPPORTED;
|
|
|
|
// This is only checked on tool startup -- requires a restart if
|
|
|
|
// e10s subsequently enabled.
|
|
|
|
let enabled = this._e10s;
|
|
|
|
return { supported, enabled };
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Called on init, sets an `e10s` attribute on the main view container with
|
|
|
|
* "disabled" if e10s is possible on the platform and just not on, or "unsupported"
|
|
|
|
* if e10s is not possible on the platform. If e10s is on, no attribute is set.
|
|
|
|
*/
|
|
|
|
_setMultiprocessAttributes: function () {
|
|
|
|
let { enabled, supported } = this.getMultiprocessStatus();
|
|
|
|
if (!enabled && supported) {
|
|
|
|
$("#performance-view").setAttribute("e10s", "disabled");
|
|
|
|
}
|
|
|
|
// Could be a chance where the directive goes away yet e10s is still on
|
|
|
|
else if (!enabled && !supported) {
|
|
|
|
$("#performance-view").setAttribute("e10s", "unsupported");
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2015-02-07 01:45:32 +00:00
|
|
|
toString: () => "[object PerformanceController]"
|
2014-10-15 07:46:00 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2014-10-30 11:35:00 +00:00
|
|
|
* Convenient way of emitting events from the controller.
|
2014-10-15 07:46:00 +00:00
|
|
|
*/
|
2014-10-30 11:35:00 +00:00
|
|
|
EventEmitter.decorate(PerformanceController);
|
2014-10-15 07:46:00 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* DOM query helpers.
|
|
|
|
*/
|
|
|
|
function $(selector, target = document) {
|
|
|
|
return target.querySelector(selector);
|
|
|
|
}
|
|
|
|
function $$(selector, target = document) {
|
|
|
|
return target.querySelectorAll(selector);
|
|
|
|
}
|