Bug 1408124 - Create a new perf actor and recording panel; r=julienw

This patch adds a new performance recording panel that interfaes with
perf.html. It is enabled through the new preferences:

"devtools.performance.new-panel-enabled"

MozReview-Commit-ID: 1HBLsbREDPk

--HG--
extra : rebase_source : 22cc2826138c4891024c34947f145574f55b4541
This commit is contained in:
Greg Tatum 2017-11-08 10:36:43 -06:00
Родитель c86e3d1c91
Коммит 5912015212
34 изменённых файлов: 1496 добавлений и 22 удалений

Просмотреть файл

@ -19,6 +19,7 @@ loader.lazyGetter(this, "CanvasDebuggerPanel", () => require("devtools/client/ca
loader.lazyGetter(this, "WebAudioEditorPanel", () => require("devtools/client/webaudioeditor/panel").WebAudioEditorPanel);
loader.lazyGetter(this, "MemoryPanel", () => require("devtools/client/memory/panel").MemoryPanel);
loader.lazyGetter(this, "PerformancePanel", () => require("devtools/client/performance/panel").PerformancePanel);
loader.lazyGetter(this, "NewPerformancePanel", () => require("devtools/client/performance-new/panel").PerformancePanel);
loader.lazyGetter(this, "NetMonitorPanel", () => require("devtools/client/netmonitor/panel").NetMonitorPanel);
loader.lazyGetter(this, "StoragePanel", () => require("devtools/client/storage/panel").StoragePanel);
loader.lazyGetter(this, "ScratchpadPanel", () => require("devtools/client/scratchpad/scratchpad-panel").ScratchpadPanel);
@ -253,29 +254,49 @@ Tools.canvasDebugger = {
};
Tools.performance = {
id: "performance",
ordinal: 7,
icon: "chrome://devtools/skin/images/tool-profiler.svg",
url: "chrome://devtools/content/performance/performance.xul",
visibilityswitch: "devtools.performance.enabled",
label: l10n("performance.label"),
panelLabel: l10n("performance.panelLabel"),
get tooltip() {
return l10n("performance.tooltip", "Shift+" +
functionkey(l10n("performance.commandkey")));
},
accesskey: l10n("performance.accesskey"),
inMenu: true,
isTargetSupported: function (target) {
return target.hasActor("performance");
},
build: function (frame, target) {
return new PerformancePanel(frame, target);
}
id: "performance",
ordinal: 7,
icon: "chrome://devtools/skin/images/tool-profiler.svg",
visibilityswitch: "devtools.performance.enabled",
label: l10n("performance.label"),
panelLabel: l10n("performance.panelLabel"),
get tooltip() {
return l10n("performance.tooltip", "Shift+" +
functionkey(l10n("performance.commandkey")));
},
accesskey: l10n("performance.accesskey"),
inMenu: true,
};
function switchPerformancePanel() {
if (Services.prefs.getBoolPref("devtools.performance.new-panel-enabled", false)) {
Tools.performance.url = "chrome://devtools/content/performance-new/perf.xhtml";
Tools.performance.build = function (frame, target) {
return new NewPerformancePanel(frame, target);
};
Tools.performance.isTargetSupported = function (target) {
// Root actors are lazily initialized, so we can't check if the target has
// the perf actor yet. Also this function is not async, so we can't initialize
// the actor yet.
return true;
};
} else {
Tools.performance.url = "chrome://devtools/content/performance/performance.xul";
Tools.performance.build = function (frame, target) {
return new PerformancePanel(frame, target);
};
Tools.performance.isTargetSupported = function (target) {
return target.hasActor("performance");
};
}
}
switchPerformancePanel();
Services.prefs.addObserver(
"devtools.performance.new-panel-enabled",
{ observe: switchPerformancePanel }
);
Tools.memory = {
id: "memory",
ordinal: 8,

Просмотреть файл

@ -328,6 +328,11 @@ OptionsPanel.prototype = {
label: L10N.getStr("toolbox.options.enableNewDebugger.label"),
id: "devtools-new-debugger",
parentId: "debugger-options"
}, {
pref: "devtools.performance.new-panel-enabled",
label: "Enable new performance recorder (then re-open DevTools)",
id: "devtools-new-performance",
parentId: "context-options"
}];
let createPreferenceOption = ({pref, label, id}) => {

Просмотреть файл

@ -77,6 +77,7 @@ function setPrefDefaults() {
Services.prefs.setBoolPref("devtools.debugger.source-maps-enabled", false);
Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", true);
Services.prefs.setBoolPref("devtools.webconsole.new-frontend-enabled", false);
Services.prefs.setBoolPref("devtools.preference.new-panel-enabled", false);
}
window.addEventListener("load", function () {
let cmdClose = document.getElementById("toolbox-cmd-close");

Просмотреть файл

@ -63,6 +63,9 @@ devtools.jar:
content/performance/performance.xul (performance/performance.xul)
content/performance/performance-controller.js (performance/performance-controller.js)
content/performance/performance-view.js (performance/performance-view.js)
content/performance-new/perf.xhtml (performance-new/perf.xhtml)
content/performance-new/frame-script.js (performance-new/frame-script.js)
content/performance-new/initializer.js (performance-new/initializer.js)
content/performance/views/overview.js (performance/views/overview.js)
content/performance/views/toolbar.js (performance/views/toolbar.js)
content/performance/views/details.js (performance/views/details.js)
@ -155,6 +158,7 @@ devtools.jar:
skin/animationinspector.css (themes/animationinspector.css)
skin/canvasdebugger.css (themes/canvasdebugger.css)
skin/debugger.css (themes/debugger.css)
skin/perf.css (themes/perf.css)
skin/performance.css (themes/performance.css)
skin/memory.css (themes/memory.css)
skin/scratchpad.css (themes/scratchpad.css)

Просмотреть файл

@ -20,6 +20,7 @@ DIRS += [
'memory',
'netmonitor',
'performance',
'performance-new',
'preferences',
'responsive.html',
'scratchpad',

Просмотреть файл

@ -0,0 +1,356 @@
/* 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";
const { PureComponent } = require("devtools/client/shared/vendor/react");
const { div, button } = require("devtools/client/shared/vendor/react-dom-factories");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
/**
* The recordingState is one of the following:
**/
// The initial state before we've queried the PerfActor
const NOT_YET_KNOWN = "not-yet-known";
// The profiler is available, we haven't started recording yet.
const AVAILABLE_TO_RECORD = "available-to-record";
// An async request has been sent to start the profiler.
const REQUEST_TO_START_RECORDING = "request-to-start-recording";
// An async request has been sent to get the profile and stop the profiler.
const REQUEST_TO_GET_PROFILE_AND_STOP_PROFILER =
"request-to-get-profile-and-stop-profiler";
// An async request has been sent to stop the profiler.
const REQUEST_TO_STOP_PROFILER = "request-to-stop-profiler";
// The profiler notified us that our request to start it actually started it.
const RECORDING = "recording";
// Some other code with access to the profiler started it.
const OTHER_IS_RECORDING = "other-is-recording";
// Profiling is not available when in private browsing mode.
const LOCKED_BY_PRIVATE_BROWSING = "locked-by-private-browsing";
class Perf extends PureComponent {
static get propTypes() {
return {
perfFront: PropTypes.object.isRequired,
receiveProfile: PropTypes.func.isRequired
};
}
constructor(props) {
super(props);
this.state = {
recordingState: NOT_YET_KNOWN,
recordingUnexpectedlyStopped: false,
// The following is either "null" for unknown, or a boolean value.
isSupportedPlatform: null
};
this.startRecording = this.startRecording.bind(this);
this.getProfileAndStopProfiler = this.getProfileAndStopProfiler.bind(this);
this.stopProfilerAndDiscardProfile = this.stopProfilerAndDiscardProfile.bind(this);
this.handleProfilerStarting = this.handleProfilerStarting.bind(this);
this.handleProfilerStopping = this.handleProfilerStopping.bind(this);
this.handlePrivateBrowsingStarting = this.handlePrivateBrowsingStarting.bind(this);
this.handlePrivateBrowsingEnding = this.handlePrivateBrowsingEnding.bind(this);
}
componentDidMount() {
const { perfFront } = this.props;
// Ask for the initial state of the profiler.
Promise.all([
perfFront.isActive(),
perfFront.isSupportedPlatform(),
perfFront.isLockedForPrivateBrowsing(),
]).then((results) => {
const [
isActive,
isSupportedPlatform,
isLockedForPrivateBrowsing
] = results;
let recordingState = this.state.recordingState;
// It's theoretically possible we got an event that already let us know about
// the current state of the profiler.
if (recordingState === NOT_YET_KNOWN && isSupportedPlatform) {
if (isLockedForPrivateBrowsing) {
recordingState = LOCKED_BY_PRIVATE_BROWSING;
} else {
recordingState = isActive
? OTHER_IS_RECORDING
: AVAILABLE_TO_RECORD;
}
}
this.setState({ isSupportedPlatform, recordingState });
});
// Handle when the profiler changes state. It might be us, it might be someone else.
this.props.perfFront.on("profiler-started", this.handleProfilerStarting);
this.props.perfFront.on("profiler-stopped", this.handleProfilerStopping);
this.props.perfFront.on("profile-locked-by-private-browsing",
this.handlePrivateBrowsingStarting);
this.props.perfFront.on("profile-unlocked-from-private-browsing",
this.handlePrivateBrowsingEnding);
}
componentWillUnmount() {
switch (this.state.recordingState) {
case NOT_YET_KNOWN:
case AVAILABLE_TO_RECORD:
case REQUEST_TO_STOP_PROFILER:
case REQUEST_TO_GET_PROFILE_AND_STOP_PROFILER:
case LOCKED_BY_PRIVATE_BROWSING:
case OTHER_IS_RECORDING:
// Do nothing for these states.
break;
case RECORDING:
case REQUEST_TO_START_RECORDING:
this.props.perfFront.stopProfilerAndDiscardProfile();
break;
default:
throw new Error("Unhandled recording state.");
}
}
getRecordingStateForTesting() {
return this.state.recordingState;
}
handleProfilerStarting() {
switch (this.state.recordingState) {
case NOT_YET_KNOWN:
// We couldn't have started it yet, so it must have been someone
// else. (fallthrough)
case AVAILABLE_TO_RECORD:
// We aren't recording, someone else started it up. (fallthrough)
case REQUEST_TO_STOP_PROFILER:
// We requested to stop the profiler, but someone else already started
// it up. (fallthrough)
case REQUEST_TO_GET_PROFILE_AND_STOP_PROFILER:
// Someone re-started the profiler while we were asking for the completed
// profile.
this.setState({
recordingState: OTHER_IS_RECORDING,
recordingUnexpectedlyStopped: false
});
break;
case REQUEST_TO_START_RECORDING:
// Wait for the profiler to tell us that it has started.
this.setState({
recordingState: RECORDING,
recordingUnexpectedlyStopped: false
});
break;
case LOCKED_BY_PRIVATE_BROWSING:
case OTHER_IS_RECORDING:
case RECORDING:
// These state cases don't make sense to happen, and means we have a logical
// fallacy somewhere.
throw new Error(
"The profiler started recording, when it shouldn't have " +
`been able to. Current state: "${this.state.recordingState}"`);
default:
throw new Error("Unhandled recording state");
}
}
handleProfilerStopping() {
switch (this.state.recordingState) {
case NOT_YET_KNOWN:
case REQUEST_TO_GET_PROFILE_AND_STOP_PROFILER:
case REQUEST_TO_STOP_PROFILER:
case OTHER_IS_RECORDING:
this.setState({
recordingState: AVAILABLE_TO_RECORD,
recordingUnexpectedlyStopped: false
});
break;
case REQUEST_TO_START_RECORDING:
// Highly unlikely, but someone stopped the recorder, this is fine.
// Do nothing (fallthrough).
case LOCKED_BY_PRIVATE_BROWSING:
// The profiler is already locked, so we know about this already.
break;
case RECORDING:
this.setState({
recordingState: AVAILABLE_TO_RECORD,
recordingUnexpectedlyStopped: true
});
break;
case AVAILABLE_TO_RECORD:
throw new Error(
"The profiler stopped recording, when it shouldn't have been able to.");
default:
throw new Error("Unhandled recording state");
}
}
handlePrivateBrowsingStarting() {
switch (this.state.recordingState) {
case REQUEST_TO_GET_PROFILE_AND_STOP_PROFILER:
// This one is a tricky case. Go ahead and act like nothing went wrong, maybe
// it will resolve correctly? (fallthrough)
case REQUEST_TO_STOP_PROFILER:
case AVAILABLE_TO_RECORD:
case OTHER_IS_RECORDING:
case NOT_YET_KNOWN:
this.setState({
recordingState: LOCKED_BY_PRIVATE_BROWSING,
recordingUnexpectedlyStopped: false
});
break;
case REQUEST_TO_START_RECORDING:
case RECORDING:
this.setState({
recordingState: LOCKED_BY_PRIVATE_BROWSING,
recordingUnexpectedlyStopped: true
});
break;
case LOCKED_BY_PRIVATE_BROWSING:
// Do nothing
break;
default:
throw new Error("Unhandled recording state");
}
}
handlePrivateBrowsingEnding() {
// No matter the state, go ahead and set this as ready to record. This should
// be the only logical state to go into.
this.setState({
recordingState: AVAILABLE_TO_RECORD,
recordingUnexpectedlyStopped: false
});
}
startRecording() {
this.setState({
recordingState: REQUEST_TO_START_RECORDING,
// Reset this error state since it's no longer valid.
recordingUnexpectedlyStopped: false,
});
this.props.perfFront.startProfiler();
}
async getProfileAndStopProfiler() {
this.setState({ recordingState: REQUEST_TO_GET_PROFILE_AND_STOP_PROFILER });
const profile = await this.props.perfFront.getProfileAndStopProfiler();
this.setState({ recordingState: AVAILABLE_TO_RECORD });
console.log("getProfileAndStopProfiler");
this.props.receiveProfile(profile);
}
stopProfilerAndDiscardProfile() {
this.setState({ recordingState: REQUEST_TO_STOP_PROFILER });
this.props.perfFront.stopProfilerAndDiscardProfile();
}
render() {
const { recordingState, isSupportedPlatform } = this.state;
// Handle the cases of platform support.
switch (isSupportedPlatform) {
case null:
// We don't know yet if this is a supported platform, wait for a response.
return null;
case false:
return renderButton({
label: "Start recording",
disabled: true,
additionalMessage: "Your platform is not supported. The Gecko Profiler only " +
"supports Tier-1 platforms."
});
case true:
// Continue on and render the panel.
break;
}
// TODO - L10N all of the messages. Bug 1418056
switch (recordingState) {
case NOT_YET_KNOWN:
return null;
case AVAILABLE_TO_RECORD:
return renderButton({
onClick: this.startRecording,
label: "Start recording",
additionalMessage: this.state.recordingUnexpectedlyStopped
? div(null, "The recording was stopped by another tool.")
: null
});
case REQUEST_TO_STOP_PROFILER:
return renderButton({
label: "Stopping the recording",
disabled: true
});
case REQUEST_TO_GET_PROFILE_AND_STOP_PROFILER:
return renderButton({
label: "Stopping the recording, and capturing the profile",
disabled: true
});
case REQUEST_TO_START_RECORDING:
case RECORDING:
return renderButton({
label: "Stop and grab the recording",
onClick: this.getProfileAndStopProfiler,
disabled: this.state.recordingState === REQUEST_TO_START_RECORDING
});
case OTHER_IS_RECORDING:
return renderButton({
label: "Stop and discard the other recording",
onClick: this.stopProfilerAndDiscardProfile,
additionalMessage: "Another tool is currently recording."
});
case LOCKED_BY_PRIVATE_BROWSING:
return renderButton({
label: "Start recording",
disabled: true,
additionalMessage: `The profiler is disabled when Private Browsing is enabled.
Close all Private Windows to re-enable the profiler`
});
default:
throw new Error("Unhandled recording state");
}
}
}
module.exports = Perf;
function renderButton(props) {
const { disabled, label, onClick, additionalMessage } = props;
const nbsp = "\u00A0";
return div(
{ className: "perf" },
div({ className: "perf-additional-message" }, additionalMessage || nbsp),
div(
null,
button(
{
className: "devtools-button perf-button",
"data-standalone": true,
disabled,
onClick
},
label
)
)
);
}

Просмотреть файл

@ -0,0 +1,8 @@
# vim: set filetype=python:
# 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/.
DevToolsModules(
'Perf.js',
)

Просмотреть файл

@ -0,0 +1,106 @@
/* 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";
/* global addMessageListener, addEventListener, content */
/**
* This frame script injects itself into perf-html.io and injects the profile
* into the page. It is mostly taken from the Gecko Profiler Addon implementation.
*/
const TRANSFER_EVENT = "devtools:perf-html-transfer-profile";
let gProfile = null;
addMessageListener(TRANSFER_EVENT, e => {
gProfile = e.data;
// Eagerly try and see if the framescript was evaluated after perf loaded its scripts.
connectToPage();
// If not try again at DOMContentLoaded which should be called after the script
// tag was synchronously loaded in.
addEventListener("DOMContentLoaded", connectToPage);
});
function connectToPage() {
const unsafeWindow = content.wrappedJSObject;
if (unsafeWindow.connectToGeckoProfiler) {
unsafeWindow.connectToGeckoProfiler(makeAccessibleToPage({
getProfile: () => Promise.resolve(gProfile),
getSymbolTable: (debugName, breakpadId) => getSymbolTable(debugName, breakpadId),
}, unsafeWindow));
}
}
/**
* For now, do not try to symbolicate. Reject any attempt.
*/
function getSymbolTable(debugName, breakpadId) {
// Errors will not properly clone into the content page as they bring privileged
// stacks information into the page. In this case provide a mock object to maintain
// the Error type object shape.
const error = {
message: `The DevTools' "perf" actor does not support symbolication.`
};
return Promise.reject(error);
}
// The following functions handle the security of cloning the object into the page.
// The code was taken from the original Gecko Profiler Add-on to maintain
// compatibility with the existing profile importing mechanism:
// See: https://github.com/devtools-html/Gecko-Profiler-Addon/blob/78138190b42565f54ce4022a5b28583406489ed2/data/tab-framescript.js
/**
* Create a promise that can be used in the page.
*/
function createPromiseInPage(fun, contentGlobal) {
function funThatClonesObjects(resolve, reject) {
return fun(result => resolve(Components.utils.cloneInto(result, contentGlobal)),
error => reject(Components.utils.cloneInto(error, contentGlobal)));
}
return new contentGlobal.Promise(Components.utils.exportFunction(funThatClonesObjects,
contentGlobal));
}
/**
* Returns a function that calls the original function and tries to make the
* return value available to the page.
*/
function wrapFunction(fun, contentGlobal) {
return function () {
let result = fun.apply(this, arguments);
if (typeof result === "object") {
if (("then" in result) && (typeof result.then === "function")) {
// fun returned a promise.
return createPromiseInPage((resolve, reject) =>
result.then(resolve, reject), contentGlobal);
}
return Components.utils.cloneInto(result, contentGlobal);
}
return result;
};
}
/**
* Pass a simple object containing values that are objects or functions.
* The objects or functions are wrapped in such a way that they can be
* consumed by the page.
*/
function makeAccessibleToPage(obj, contentGlobal) {
let result = Components.utils.createObjectIn(contentGlobal);
for (let field in obj) {
switch (typeof obj[field]) {
case "function":
Components.utils.exportFunction(
wrapFunction(obj[field], contentGlobal), result, { defineAs: field });
break;
case "object":
Components.utils.cloneInto(obj[field], result, { defineAs: field });
break;
default:
result[field] = obj[field];
break;
}
}
return result;
}

Просмотреть файл

@ -0,0 +1,44 @@
/* 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";
/* exported gInit, gDestroy */
const BrowserLoaderModule = {};
Components.utils.import("resource://devtools/client/shared/browser-loader.js", BrowserLoaderModule);
const { require } = BrowserLoaderModule.BrowserLoader({
baseURI: "resource://devtools/client/memory/",
window
});
const Perf = require("devtools/client/performance-new/components/Perf");
const { render, unmountComponentAtNode } = require("devtools/client/shared/vendor/react-dom");
const { createElement } = require("devtools/client/shared/vendor/react");
/**
* Perform a simple initialization on the panel. Hook up event listeners.
*
* @param perfFront - The Perf actor's front. Used to start and stop recordings.
*/
function gInit(perfFront) {
const props = {
perfFront,
receiveProfile: profile => {
// Open up a new tab and send a message with the profile.
const browser = top.gBrowser;
const tab = browser.addTab("https://perf-html.io/from-addon");
browser.selectedTab = tab;
const mm = tab.linkedBrowser.messageManager;
mm.loadFrameScript(
"chrome://devtools/content/performance-new/frame-script.js",
false
);
mm.sendAsyncMessage("devtools:perf-html-transfer-profile", profile);
}
};
render(createElement(Perf, props), document.querySelector("#root"));
}
function gDestroy() {
unmountComponentAtNode(document.querySelector("#root"));
}

Просмотреть файл

@ -0,0 +1,17 @@
# vim: set filetype=python:
# 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/.
DIRS += [
'components',
]
DevToolsModules(
'panel.js',
)
MOCHITEST_CHROME_MANIFESTS += ['test/chrome/chrome.ini']
with Files('**'):
BUG_COMPONENT = ('Firefox', 'Developer Tools: Performance Tools (Profiler/Timeline)')

Просмотреть файл

@ -0,0 +1,59 @@
/* 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";
const { PerfFront } = require("devtools/shared/fronts/perf");
loader.lazyRequireGetter(this, "EventEmitter",
"devtools/shared/old-event-emitter");
class PerformancePanel {
constructor(iframeWindow, toolbox) {
this.panelWin = iframeWindow;
this.toolbox = toolbox;
EventEmitter.decorate(this);
}
/**
* Open is effectively an asynchronous constructor.
* @return {Promise} Resolves when the Perf tool completes opening.
*/
open() {
if (!this._opening) {
this._opening = this._doOpen();
}
return this._opening;
}
async _doOpen() {
this.panelWin.gToolbox = this.toolbox;
this.panelWin.gTarget = this.target;
const rootForm = await this.target.root;
const perfFront = new PerfFront(this.target.client, rootForm);
this.isReady = true;
this.emit("ready");
this.panelWin.gInit(perfFront);
return this;
}
// DevToolPanel API:
get target() {
return this.toolbox.target;
}
async destroy() {
// Make sure this panel is not already destroyed.
if (this._destroyed) {
return;
}
this.panelWin.gDestroy();
this.emit("destroyed");
this._destroyed = true;
}
}
exports.PerformancePanel = PerformancePanel;

Просмотреть файл

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html [
<!ENTITY % htmlDTD
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"DTD/xhtml1-strict.dtd">
%htmlDTD;
]>
<!-- 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/. -->
<html xmlns="http://www.w3.org/1999/xhtml" dir="">
<head>
<link rel="stylesheet" href="chrome://devtools/skin/widgets.css" type="text/css"/>
<link rel="stylesheet" href="chrome://devtools/skin/perf.css" type="text/css"/>
</head>
<body class="theme-body">
<div id="root"></div>
<script type="application/javascript" src="initializer.js"></script>
<script type="application/javascript"
src="chrome://devtools/content/shared/theme-switching.js"
defer="true">
</script>
</body>
</html>

Просмотреть файл

@ -0,0 +1,6 @@
"use strict";
module.exports = {
// Extend from the shared list of defined globals for mochitests.
"extends": "../../../.eslintrc.mochitests.js"
};

Просмотреть файл

@ -0,0 +1,8 @@
[DEFAULT]
support-files =
head.js
[test_perf-state-01.html]
[test_perf-state-02.html]
[test_perf-state-03.html]
[test_perf-state-04.html]

Просмотреть файл

@ -0,0 +1,133 @@
/* 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";
/* exported addPerfTest, MockPerfFront */
/* globals URL_ROOT */
const { BrowserLoader } = Components.utils.import("resource://devtools/client/shared/browser-loader.js", {});
var { require } = BrowserLoader({
baseURI: "resource://devtools/client/performance-new/",
window
});
const EventEmitter = require("devtools/shared/event-emitter");
const { perfDescription } = require("devtools/shared/specs/perf");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const flags = require("devtools/shared/flags");
flags.testing = true;
let EXPECTED_DTU_ASSERT_FAILURE_COUNT = 0;
SimpleTest.registerCleanupFunction(function () {
if (DevToolsUtils.assertionFailureCount !== EXPECTED_DTU_ASSERT_FAILURE_COUNT) {
ok(false, "Should have had the expected number of DevToolsUtils.assert() failures." +
"Expected " + EXPECTED_DTU_ASSERT_FAILURE_COUNT +
", got " + DevToolsUtils.assertionFailureCount);
}
});
/**
* Handle test setup and teardown while catching errors.
*/
function addPerfTest(asyncTest) {
window.onload = async () => {
try {
await asyncTest();
} catch (e) {
ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
} finally {
SimpleTest.finish();
}
};
}
/**
* The Gecko Profiler is a rather heavy-handed component that uses a lot of resources.
* In order to get around that, and have quick component tests we provide a mock of
* the performance front. It also has a method called flushAsyncQueue() that will
* flush any queued async calls to deterministically run our tests.
*/
class MockPerfFront extends EventEmitter {
constructor() {
super();
this._isActive = false;
this._asyncQueue = [];
// Tests can update these two values directly as needed.
this.mockIsSupported = true;
this.mockIsLocked = false;
// Wrap all async methods in a flushable queue, so that tests can control
// when the responses come back.
this.isActive = this._wrapInAsyncQueue(this.isActive);
this.startProfiler = this._wrapInAsyncQueue(this.startProfiler);
this.stopProfilerAndDiscardProfile = this._wrapInAsyncQueue(
this.stopProfilerAndDiscardProfile);
this.getProfileAndStopProfiler = this._wrapInAsyncQueue(
this.getProfileAndStopProfiler);
}
/**
* Provide a flushable queue mechanism for all async work. The work piles up
* and then is evaluated at once when _flushPendingQueue is called.
*/
_wrapInAsyncQueue(fn) {
if (typeof fn !== "function") {
throw new Error("_wrapInAsyncQueue requires a function");
}
return (...args) => {
return new Promise(resolve => {
this._asyncQueue.push(() => {
resolve(fn.apply(this, args));
});
});
};
}
flushAsyncQueue() {
const pending = this._asyncQueue;
this._asyncQueue = [];
pending.forEach(fn => fn());
// Ensure this is async.
return new Promise(resolve => setTimeout(resolve, 0));
}
startProfiler() {
this._isActive = true;
this.emit("profiler-started");
}
getProfileAndStopProfiler() {
this._isActive = false;
this.emit("profiler-stopped");
// Return a fake profile.
return {};
}
stopProfilerAndDiscardProfile() {
this._isActive = false;
this.emit("profiler-stopped");
}
isActive() {
return this._isActive;
}
isSupportedPlatform() {
return this.mockIsSupported;
}
isLockedForPrivateBrowsing() {
return this.mockIsLocked;
}
}
// Do a quick validation to make sure that our Mock has the same methods as a spec.
const mockKeys = Object.getOwnPropertyNames(MockPerfFront.prototype);
Object.getOwnPropertyNames(perfDescription.methods).forEach(methodName => {
if (!mockKeys.includes(methodName)) {
throw new Error(`The MockPerfFront is missing the method "${methodName}" from the ` +
"actor's spec. It should be added to the mock.");
}
});

Просмотреть файл

@ -0,0 +1,70 @@
<!DOCTYPE HTML>
<html>
<!-- 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/. -->
<head>
<meta charset="utf-8">
<title>Perf component test</title>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
</head>
<body>
<div id="container"></div>
<pre id="test">
<script src="head.js" type="application/javascript"></script>
<script type="application/javascript">
"use strict";
/**
* Test the normal workflow of starting and stopping the profiler through the
* Perf component.
*/
addPerfTest(async () => {
const Perf = require("devtools/client/performance-new/components/Perf");
const React = require("devtools/client/shared/vendor/react");
const ReactDOM = require("devtools/client/shared/vendor/react-dom");
const perfFront = new MockPerfFront();
const container = document.querySelector("#container");
// Inject a function which will allow us to receive the profile.
let profile;
function receiveProfile(profileIn) {
profile = profileIn;
}
const element = React.createElement(Perf, { perfFront, receiveProfile });
const perfComponent = ReactDOM.render(element, container);
is(perfComponent.state.recordingState, "not-yet-known",
"The component at first is in an unknown state.");
await perfFront.flushAsyncQueue();
is(perfComponent.state.recordingState, "available-to-record",
"After talking to the actor, we're ready to record.");
const button = container.querySelector("button");
ok(button, "Selected the button to click.");
button.click();
is(perfComponent.state.recordingState, "request-to-start-recording",
"Sent in a request to start recording.");
await perfFront.flushAsyncQueue();
is(perfComponent.state.recordingState, "recording",
"The actor has started its recording");
button.click();
is(perfComponent.state.recordingState,
"request-to-get-profile-and-stop-profiler",
"We have requested to stop the profiler.");
await perfFront.flushAsyncQueue();
is(perfComponent.state.recordingState, "available-to-record",
"The profiler is available to record again.");
await perfFront.flushAsyncQueue();
is(typeof profile, "object", "Got a profile");
});
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -0,0 +1,59 @@
<!DOCTYPE HTML>
<html>
<!-- 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/. -->
<head>
<meta charset="utf-8">
<title>Perf component test</title>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
</head>
<body>
<div id="container"></div>
<pre id="test">
<script src="head.js" type="application/javascript"></script>
<script type="application/javascript">
"use strict";
/**
* Test the perf component when the profiler is already started.
*/
addPerfTest(async () => {
const Perf = require("devtools/client/performance-new/components/Perf");
const React = require("devtools/client/shared/vendor/react");
const ReactDOM = require("devtools/client/shared/vendor/react-dom");
const perfFront = new MockPerfFront();
const container = document.querySelector("#container");
ok(true, "Start the profiler before initiliazing the component, to simulate" +
"the profiler being controlled by another tool.");
perfFront.startProfiler();
await perfFront.flushAsyncQueue();
const receiveProfile = () => {};
const element = React.createElement(Perf, { perfFront, receiveProfile });
const perfComponent = ReactDOM.render(element, container);
is(perfComponent.state.recordingState, "not-yet-known",
"The component at first is in an unknown state.");
await perfFront.flushAsyncQueue();
is(perfComponent.state.recordingState, "other-is-recording",
"The profiler is not available to record.");
const button = container.querySelector("button");
ok(button, "Selected a button on the component");
button.click();
is(perfComponent.state.recordingState, "request-to-stop-profiler",
"We can request to stop the profiler.");
await perfFront.flushAsyncQueue();
is(perfComponent.state.recordingState, "available-to-record",
"The profiler is now available to record.");
});
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -0,0 +1,61 @@
<!DOCTYPE HTML>
<html>
<!-- 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/. -->
<head>
<meta charset="utf-8">
<title>Perf component test</title>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
</head>
<body>
<div id="container"></div>
<pre id="test">
<script src="head.js" type="application/javascript"></script>
<script type="application/javascript">
"use strict";
/**
* Test the perf component for when the profiler is already started.
*/
addPerfTest(async () => {
const Perf = require("devtools/client/performance-new/components/Perf");
const React = require("devtools/client/shared/vendor/react");
const ReactDOM = require("devtools/client/shared/vendor/react-dom");
const perfFront = new MockPerfFront();
const container = document.querySelector("#container");
const receiveProfile = () => {};
const element = React.createElement(Perf, { perfFront, receiveProfile });
const perfComponent = ReactDOM.render(element, container);
is(perfComponent.state.recordingState, "not-yet-known",
"The component at first is in an unknown state.");
await perfFront.flushAsyncQueue();
is(perfComponent.state.recordingState, "available-to-record",
"After talking to the actor, we're ready to record.");
document.querySelector("button").click();
is(perfComponent.state.recordingState, "request-to-start-recording",
"Sent in a request to start recording.");
await perfFront.flushAsyncQueue();
is(perfComponent.state.recordingState, "recording",
"The actor has started its recording");
ok(true, "Simulate a third party stopping the profiler.");
perfFront.stopProfilerAndDiscardProfile();
await perfFront.flushAsyncQueue();
ok(perfComponent.state.recordingUnexpectedlyStopped,
"The profiler unexpectedly stopped.");
is(perfComponent.state.recordingState, "available-to-record",
"However, the profiler is available to record again.");
});
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -0,0 +1,64 @@
<!DOCTYPE HTML>
<html>
<!-- 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/. -->
<head>
<meta charset="utf-8">
<title>Perf component test</title>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
</head>
<body>
<div id="container"></div>
<pre id="test">
<script src="head.js" type="application/javascript"></script>
<script type="application/javascript">
"use strict";
/**
* Test that the profiler gets disabled during private browsing.
*/
addPerfTest(async () => {
const Perf = require("devtools/client/performance-new/components/Perf");
const React = require("devtools/client/shared/vendor/react");
const ReactDOM = require("devtools/client/shared/vendor/react-dom");
const perfFront = new MockPerfFront();
const container = document.querySelector("#container");
perfFront.mockIsLocked = true;
const receiveProfile = () => {};
const element = React.createElement(Perf, { perfFront, receiveProfile });
const perfComponent = ReactDOM.render(element, container);
is(perfComponent.state.recordingState, "not-yet-known",
"The component at first is in an unknown state.");
await perfFront.flushAsyncQueue();
is(perfComponent.state.recordingState, "locked-by-private-browsing",
"After talking to the actor, it's locked for private browsing.");
perfFront.mockIsLocked = false;
perfFront.emit("profile-unlocked-from-private-browsing");
await perfFront.flushAsyncQueue();
is(perfComponent.state.recordingState, "available-to-record",
"After the profiler is unlocked, it's available to record.");
document.querySelector("button").click();
await perfFront.flushAsyncQueue();
is(perfComponent.state.recordingState, "recording",
"The actor has started its recording");
perfFront.mockIsLocked = true;
perfFront.emit("profile-locked-by-private-browsing");
await perfFront.flushAsyncQueue();
is(perfComponent.state.recordingState, "locked-by-private-browsing",
"The recording stops when going into private browsing mode.");
});
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -305,6 +305,9 @@ pref("devtools.webconsole.new-frontend-enabled", true);
// Enable the webconsole sidebar toggle
pref("devtools.webconsole.sidebarToggle", false);
// Disable the new performance recording panel by default
pref("devtools.performance.new-panel-enabled", false);
// Enable client-side mapping service for source maps
pref("devtools.source-map.client-service.enabled", true);

Просмотреть файл

@ -0,0 +1,23 @@
/* 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/. */
.perf {
width: 100%;
height: 100%;
position: absolute;
display: flex;
flex-direction: column;
align-items: center;
}
.devtools-button.perf-button {
padding: 5px;
margin: auto;
font-size: 120%;
}
.perf-additional-message {
margin: 10px;
margin-top: 65px;
}

Просмотреть файл

@ -42,6 +42,7 @@ DevToolsModules(
'memory.js',
'monitor.js',
'object.js',
'perf.js',
'performance-recording.js',
'performance.js',
'preference.js',

Просмотреть файл

@ -0,0 +1,153 @@
/* 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";
const protocol = require("devtools/shared/protocol");
const { ActorClassWithSpec, Actor } = protocol;
const { perfSpec } = require("devtools/shared/specs/perf");
const { Cc, Ci } = require("chrome");
const Services = require("Services");
loader.lazyGetter(this, "geckoProfiler", () => {
return Cc["@mozilla.org/tools/profiler;1"].getService(Ci.nsIProfiler);
});
loader.lazyImporter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
// Some platforms are built without the Gecko Profiler.
const IS_SUPPORTED_PLATFORM = "nsIProfiler" in Ci;
/**
* The PerfActor wraps the Gecko Profiler interface
*/
exports.PerfActor = ActorClassWithSpec(perfSpec, {
initialize(conn) {
Actor.prototype.initialize.call(this, conn);
// Only setup the observers on a supported platform.
if (IS_SUPPORTED_PLATFORM) {
this._observer = {
observe: this._observe.bind(this)
};
Services.obs.addObserver(this._observer, "profiler-started");
Services.obs.addObserver(this._observer, "profiler-stopped");
Services.obs.addObserver(this._observer, "chrome-document-global-created");
Services.obs.addObserver(this._observer, "last-pb-context-exited");
}
},
destroy() {
if (!IS_SUPPORTED_PLATFORM) {
return;
}
Services.obs.removeObserver(this._observer, "profiler-started");
Services.obs.removeObserver(this._observer, "profiler-stopped");
Services.obs.removeObserver(this._observer, "chrome-document-global-created");
Services.obs.removeObserver(this._observer, "last-pb-context-exited");
Actor.prototype.destroy.call(this);
},
startProfiler() {
if (!IS_SUPPORTED_PLATFORM) {
return false;
}
// For a quick implementation, decide on some default values. These may need
// to be tweaked or made configurable as needed.
const settings = {
entries: 1000000,
interval: 1,
features: ["js", "stackwalk", "threads", "leaf"],
threads: ["GeckoMain", "Compositor"]
};
try {
// This can throw an error if the profiler is in the wrong state.
geckoProfiler.StartProfiler(
settings.entries,
settings.interval,
settings.features,
settings.features.length,
settings.threads,
settings.threads.length
);
} catch (e) {
// In case any errors get triggered, bailout with a false.
return false;
}
return true;
},
stopProfilerAndDiscardProfile() {
if (!IS_SUPPORTED_PLATFORM) {
return;
}
geckoProfiler.StopProfiler();
},
async getProfileAndStopProfiler() {
if (!IS_SUPPORTED_PLATFORM) {
return null;
}
let profile;
try {
// Attempt to pull out the data.
profile = await geckoProfiler.getProfileDataAsync();
// Stop and discard the buffers.
geckoProfiler.StopProfiler();
} catch (e) {
// If there was any kind of error, bailout with no profile.
return null;
}
// Gecko Profiler errors can return an empty object, return null for this case
// as well.
if (Object.keys(profile).length === 0) {
return null;
}
return profile;
},
isActive() {
if (!IS_SUPPORTED_PLATFORM) {
return false;
}
return geckoProfiler.IsActive();
},
isSupportedPlatform() {
return IS_SUPPORTED_PLATFORM;
},
isLockedForPrivateBrowsing() {
if (!IS_SUPPORTED_PLATFORM) {
return false;
}
return !geckoProfiler.CanProfile();
},
/**
* Watch for events that happen within the browser. These can affect the current
* availability and state of the Gecko Profiler.
*/
_observe(subject, topic, _data) {
switch (topic) {
case "chrome-document-global-created":
if (PrivateBrowsingUtils.isWindowPrivate(subject)) {
this.emit("profile-locked-by-private-browsing");
}
break;
case "last-pb-context-exited":
this.emit("profile-unlocked-from-private-browsing");
break;
case "profiler-started":
case "profiler-stopped":
this.emit(topic);
break;
}
}
});

Просмотреть файл

@ -443,6 +443,15 @@ var DebuggerServer = {
constructor: "HeapSnapshotFileActor",
type: { global: true }
});
// Always register this as a global module, even while there is a pref turning
// on and off the other performance actor. This actor shouldn't conflict with
// the other one. These are also lazily loaded so there shouldn't be a performance
// impact.
this.registerModule("devtools/server/actors/perf", {
prefix: "perf",
constructor: "PerfActor",
type: { global: true }
});
},
/**
@ -534,7 +543,8 @@ var DebuggerServer = {
constructor: "TimelineActor",
type: { tab: true }
});
if ("nsIProfiler" in Ci) {
if ("nsIProfiler" in Ci &&
!Services.prefs.getBoolPref("devtools.performance.new-panel-enabled", false)) {
this.registerModule("devtools/server/actors/performance", {
prefix: "performance",
constructor: "PerformanceActor",

Просмотреть файл

@ -72,6 +72,9 @@ skip-if = e10s # Bug 1183605 - devtools/server/tests/browser/ tests are still di
[browser_markers-timestamp.js]
[browser_navigateEvents.js]
skip-if = e10s # Bug 1183605 - devtools/server/tests/browser/ tests are still disabled in E10S
[browser_perf-01.js]
[browser_perf-02.js]
[browser_perf-03.js]
[browser_perf-allocation-data.js]
skip-if = e10s # Bug 1183605 - devtools/server/tests/browser/ tests are still disabled in E10S
[browser_perf-profiler-01.js]

Просмотреть файл

@ -0,0 +1,47 @@
/* 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";
/**
* Run through a series of basic recording actions for the perf actor.
*/
add_task(async function () {
const {front, client} = await initPerfFront();
// Assert the initial state.
is(await front.isSupportedPlatform(), true,
"This test only runs on supported platforms.");
is(await front.isLockedForPrivateBrowsing(), false,
"The browser is not in private browsing mode.");
is(await front.isActive(), false,
"The profiler is not active yet.");
// Start the profiler.
const profilerStarted = once(front, "profiler-started");
await front.startProfiler();
await profilerStarted;
is(await front.isActive(), true, "The profiler was started.");
// Stop the profiler and assert the results.
const profilerStopped1 = once(front, "profiler-stopped");
const profile = await front.getProfileAndStopProfiler();
await profilerStopped1;
is(await front.isActive(), false, "The profiler was stopped.");
ok("threads" in profile, "The actor was used to record a profile.");
// Restart the profiler.
await front.startProfiler();
is(await front.isActive(), true, "The profiler was re-started.");
// Stop and discard.
const profilerStopped2 = once(front, "profiler-stopped");
await front.stopProfilerAndDiscardProfile();
await profilerStopped2;
is(await front.isActive(), false,
"The profiler was stopped and the profile discarded.");
// Clean up.
await front.destroy();
await client.close();
});

Просмотреть файл

@ -0,0 +1,30 @@
/* 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";
/**
* Test what happens when other tools control the profiler.
*/
add_task(async function () {
const {front, client} = await initPerfFront();
// Simulate other tools by getting an independent handle on the Gecko Profiler.
const geckoProfiler = Cc["@mozilla.org/tools/profiler;1"].getService(Ci.nsIProfiler);
is(await front.isActive(), false, "The profiler hasn't been started yet.");
// Start the profiler.
await front.startProfiler();
is(await front.isActive(), true, "The profiler was started.");
// Stop the profiler manually through the Gecko Profiler interface.
const profilerStopped = once(front, "profiler-stopped");
geckoProfiler.StopProfiler();
await profilerStopped;
is(await front.isActive(), false, "The profiler was stopped by another tool.");
// Clean up.
await front.destroy();
await client.close();
});

Просмотреть файл

@ -0,0 +1,33 @@
/* 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";
/**
* Test that the profiler emits events when private browsing windows are opened
* and closed.
*/
add_task(async function () {
const {front, client} = await initPerfFront();
is(await front.isLockedForPrivateBrowsing(), false,
"The profiler is not locked for private browsing.");
// Open up a new private browser window, and assert the correct events are fired.
const profilerLocked = once(front, "profile-locked-by-private-browsing");
const privateWindow = await BrowserTestUtils.openNewBrowserWindow({private: true});
await profilerLocked;
is(await front.isLockedForPrivateBrowsing(), true,
"The profiler is now locked because of private browsing.");
// Close the private browser window, and assert the correct events are fired.
const profilerUnlocked = once(front, "profile-unlocked-from-private-browsing");
await BrowserTestUtils.closeWindow(privateWindow);
await profilerUnlocked;
is(await front.isLockedForPrivateBrowsing(), false,
"The profiler is available again after closing the private browsing window.");
// Clean up.
await front.destroy();
await client.close();
});

Просмотреть файл

@ -108,6 +108,41 @@ function initDebuggerServer() {
DebuggerServer.registerAllActors();
}
async function initPerfFront() {
const {PerfFront} = require("devtools/shared/fronts/perf");
initDebuggerServer();
let client = new DebuggerClient(DebuggerServer.connectPipe());
await waitUntilClientConnected(client);
const rootForm = await getRootForm(client);
const front = PerfFront(client, rootForm);
return {front, client};
}
/**
* Gets the RootActor form from a DebuggerClient.
* @param {DebuggerClient} client
* @return {RootActor} Resolves when connected.
*/
function getRootForm(client) {
return new Promise(resolve => {
client.listTabs(rootForm => {
resolve(rootForm);
});
});
}
/**
* Wait until a DebuggerClient is connected.
* @param {DebuggerClient} client
* @return {Promise} Resolves when connected.
*/
function waitUntilClientConnected(client) {
return new Promise(resolve => {
client.addOneTimeListener("connected", resolve);
});
}
/**
* Connect a debugger client.
* @param {DebuggerClient}

Просмотреть файл

@ -23,6 +23,7 @@ DevToolsModules(
'layout.js',
'memory.js',
'node.js',
'perf.js',
'performance-recording.js',
'performance.js',
'preference.js',

Просмотреть файл

@ -0,0 +1,15 @@
/* 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";
const { FrontClassWithSpec, Front } = require("devtools/shared/protocol");
const { perfSpec } = require("devtools/shared/specs/perf");
exports.PerfFront = FrontClassWithSpec(perfSpec, {
initialize: function (client, form) {
Front.prototype.initialize.call(this, client, form);
this.actorID = form.perfActor;
this.manage(this);
}
});

Просмотреть файл

@ -132,6 +132,11 @@ const Types = exports.__TypesForTests = [
spec: "devtools/shared/specs/node",
front: "devtools/shared/fronts/node",
},
{
types: ["perf"],
spec: "devtools/shared/specs/perf",
front: "devtools/shared/fronts/perf",
},
{
types: ["performance"],
spec: "devtools/shared/specs/performance",

Просмотреть файл

@ -28,6 +28,7 @@ DevToolsModules(
'layout.js',
'memory.js',
'node.js',
'perf.js',
'performance-recording.js',
'performance.js',
'preference.js',

Просмотреть файл

@ -0,0 +1,66 @@
/* 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";
const { RetVal, generateActorSpec } = require("devtools/shared/protocol");
const perfDescription = {
typeName: "perf",
events: {
"profiler-started": {
type: "profiler-started"
},
"profiler-stopped": {
type: "profiler-stopped"
},
"profile-locked-by-private-browsing": {
type: "profile-locked-by-private-browsing"
},
"profile-unlocked-from-private-browsing": {
type: "profile-unlocked-from-private-browsing"
}
},
methods: {
startProfiler: {
request: {},
response: { value: RetVal("boolean") }
},
/**
* Returns null when unable to return the profile.
*/
getProfileAndStopProfiler: {
request: {},
response: RetVal("nullable:json")
},
stopProfilerAndDiscardProfile: {
request: {},
response: {}
},
isActive: {
request: {},
response: { value: RetVal("boolean") }
},
isSupportedPlatform: {
request: {},
response: { value: RetVal("boolean") }
},
isLockedForPrivateBrowsing: {
request: {},
response: { value: RetVal("boolean") }
}
}
};
exports.perfDescription = perfDescription;
const perfSpec = generateActorSpec(perfDescription);
exports.perfSpec = perfSpec;