Bug 1597385 - Create a stripped down version of the profiler front-end; r=julienw

Differential Revision: https://phabricator.services.mozilla.com/D65146

--HG--
rename : devtools/client/performance-new/components/DevToolsAndPopup.js => devtools/client/performance-new/components/DevToolsPanel.js
extra : moz-landing-system : lando
This commit is contained in:
Greg Tatum 2020-03-09 14:52:57 +00:00
Родитель 6325813760
Коммит c3340d41b3
9 изменённых файлов: 364 добавлений и 222 удалений

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

@ -64,20 +64,10 @@ class Description extends PureComponent {
{ className: "perf-description" },
p(
null,
"This new recording panel is a bit different from the existing " +
"performance panel. It records the entire browser, and then opens up " +
"and shares the profile with ",
"Recordings launch ",
this.renderLink("https://profiler.firefox.com", "profiler.firefox.com"),
", a Mozilla performance analysis tool."
),
p(
null,
"This is still a prototype. Join along or file bugs at: ",
this.renderLink(
"https://github.com/firefox-devtools/profiler",
"github.com/firefox-devtools/profiler"
),
"."
" in a new tab. All data is stored locally, but you can choose to upload it",
" for sharing."
)
);
}

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

@ -12,7 +12,6 @@
* @typedef {Object} StateProps
* @property {boolean?} isSupportedPlatform
* @property {PageContext} pageContext
* @property {string | null} promptEnvRestart
*/
/**
@ -31,22 +30,19 @@ const {
const { connect } = require("devtools/client/shared/vendor/react-redux");
const {
div,
button,
hr,
} = require("devtools/client/shared/vendor/react-dom-factories");
const RecordingButton = createFactory(
require("devtools/client/performance-new/components/RecordingButton.js")
);
const Settings = createFactory(
require("devtools/client/performance-new/components/Settings.js")
require("devtools/client/performance-new/components/RecordingButton")
);
const Description = createFactory(
require("devtools/client/performance-new/components/Description.js")
require("devtools/client/performance-new/components/Description")
);
const DevToolsPresetSelection = createFactory(
require("devtools/client/performance-new/components/DevToolsPresetSelection")
);
const selectors = require("devtools/client/performance-new/store/selectors");
const {
restartBrowserWithEnvironmentVariable,
} = require("devtools/client/performance-new/browser");
/**
* This is the top level component for the DevTools panel and the profiler popup, but
@ -55,25 +51,9 @@ const {
*
* @extends {React.PureComponent<Props>}
*/
class DevToolsAndPopup extends PureComponent {
/** @param {Props} props */
constructor(props) {
super(props);
this.handleRestart = this.handleRestart.bind(this);
}
handleRestart() {
const { promptEnvRestart } = this.props;
if (!promptEnvRestart) {
throw new Error(
"handleRestart() should only be called when promptEnvRestart exists."
);
}
restartBrowserWithEnvironmentVariable(promptEnvRestart, "1");
}
class DevToolsPanel extends PureComponent {
render() {
const { isSupportedPlatform, pageContext, promptEnvRestart } = this.props;
const { isSupportedPlatform, pageContext } = this.props;
if (isSupportedPlatform === null) {
// We don't know yet if this is a supported platform, wait for a response.
@ -82,30 +62,10 @@ class DevToolsAndPopup extends PureComponent {
return div(
{ className: `perf perf-${pageContext}` },
promptEnvRestart
? div(
{ className: "perf-env-restart" },
div(
{
className:
"perf-photon-message-bar perf-photon-message-bar-warning perf-env-restart-fixed",
},
div({ className: "perf-photon-message-bar-warning-icon" }),
"The browser must be restarted to enable this feature.",
button(
{
className: "perf-photon-button perf-photon-button-micro",
type: "button",
onClick: this.handleRestart,
},
"Restart"
)
)
)
: null,
RecordingButton(),
Settings(),
pageContext === "devtools" ? Description() : null
Description(),
hr({ className: "perf-presets-hr" }),
DevToolsPresetSelection()
);
}
}
@ -118,8 +78,7 @@ function mapStateToProps(state) {
return {
isSupportedPlatform: selectors.getIsSupportedPlatform(state),
pageContext: selectors.getPageContext(state),
promptEnvRestart: selectors.getPromptEnvRestart(state),
};
}
module.exports = connect(mapStateToProps)(DevToolsAndPopup);
module.exports = connect(mapStateToProps)(DevToolsPanel);

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

@ -0,0 +1,186 @@
/* 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/. */
// @ts-check
/**
* @template P
* @typedef {import("react-redux").ResolveThunks<P>} ResolveThunks<P>
*/
/**
* @typedef {Object} StateProps
* @property {string} presetName
* @property {number} interval
* @property {string[]} threads
* @property {string[]} features
* @property {import("../@types/perf").Presets} presets
*/
/**
* @typedef {Object} ThunkDispatchProps
* @property {typeof actions.changePreset} changePreset
*/
/**
* @typedef {ResolveThunks<ThunkDispatchProps>} DispatchProps
* @typedef {StateProps & DispatchProps} Props
* @typedef {import("../@types/perf").State} StoreState
* @typedef {import("../@types/perf").FeatureDescription} FeatureDescription
*/
"use strict";
const { PureComponent } = require("devtools/client/shared/vendor/react");
const {
div,
select,
option,
button,
ul,
li,
span,
} = require("devtools/client/shared/vendor/react-dom-factories");
const { connect } = require("devtools/client/shared/vendor/react-redux");
const actions = require("devtools/client/performance-new/store/actions");
const selectors = require("devtools/client/performance-new/store/selectors");
const {
featureDescriptions,
} = require("devtools/client/performance-new/utils");
/**
* This component displays the preset selection for the DevTools panel. It should be
* basically the same implementation as the popup, but done in React. The popup
* is written using vanilla JS and browser chrome elements in order to be more
* performant.
*
* @extends {React.PureComponent<Props>}
*/
class DevToolsPresetSelection extends PureComponent {
/** @param {Props} props */
constructor(props) {
super(props);
this.onPresetChange = this.onPresetChange.bind(this);
this.handleLinkClick = this.handleLinkClick.bind(this);
/**
* Create an object map to easily look up feature description.
* @type {{[key: string]: FeatureDescription}}
*/
this.featureDescriptionMap = {};
for (const feature of featureDescriptions) {
this.featureDescriptionMap[feature.value] = feature;
}
}
/**
* Handle the select change.
* @param {React.ChangeEvent<HTMLSelectElement>} event
*/
onPresetChange(event) {
const { presets } = this.props;
this.props.changePreset(presets, event.target.value);
}
handleLinkClick() {
const { openTrustedLink } = require("devtools/client/shared/link");
openTrustedLink("about:profiling", {});
}
render() {
const { presetName, presets } = this.props;
let presetDescription;
const currentPreset = presets[presetName];
if (currentPreset) {
presetDescription = currentPreset.description;
} else {
const { interval, threads, features } = this.props;
presetDescription = div(
null,
ul(
{ className: "perf-presets-custom" },
li(
null,
span({ className: "perf-presets-custom-bold" }, "Interval: "),
`${interval} ms`
),
li(
null,
span({ className: "perf-presets-custom-bold" }, "Threads: "),
threads.join(", ")
),
features.map(feature => {
const description = this.featureDescriptionMap[feature];
if (!description) {
throw new Error(
"Could not find the feature description for " + feature
);
}
return li(
{ key: feature },
description ? description.name : feature
);
})
),
button(
{ className: "perf-external-link", onClick: this.handleLinkClick },
"Edit Settings…"
)
);
}
return div(
{ className: "perf-presets" },
div({ className: "perf-presets-settings" }, "Settings"),
div(
{ className: "perf-presets-details" },
div(
{ className: "perf-presets-details-row" },
select(
{
className: "perf-presets-select",
onChange: this.onPresetChange,
value: presetName,
},
Object.entries(presets).map(([name, preset]) =>
option({ key: name, value: name }, preset.label)
),
option({ value: "custom" }, "Custom")
)
// The overhead component will go here.
),
div(
{ className: "perf-presets-details-row perf-presets-description" },
presetDescription
)
)
);
}
}
/**
* @param {StoreState} state
* @returns {StateProps}
*/
function mapStateToProps(state) {
return {
presetName: selectors.getPresetName(state),
presets: selectors.getPresets(state),
interval: selectors.getInterval(state),
threads: selectors.getThreads(state),
features: selectors.getFeatures(state),
};
}
/**
* @type {ThunkDispatchProps}
*/
const mapDispatchToProps = {
changePreset: actions.changePreset,
};
module.exports = connect(
mapStateToProps,
mapDispatchToProps
)(DevToolsPresetSelection);

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

@ -60,68 +60,6 @@ class RecordingButton extends PureComponent {
this.props.getProfileAndStopProfiler(window);
}
/** @param {{
* disabled?: boolean,
* label?: React.ReactNode,
* onClick?: any,
* additionalMessage?: React.ReactNode,
* isPrimary?: boolean,
* pageContext?: PageContext,
* additionalButton?: {
* label: string,
* onClick: any,
* },
* }} buttonSettings
*/
renderButton(buttonSettings) {
const {
disabled,
label,
onClick,
additionalMessage,
isPrimary,
// pageContext,
additionalButton,
} = buttonSettings;
const nbsp = "\u00A0";
const showAdditionalMessage = false;
// TODO - Bug 1615431 - This feature needs to be migrated to about:profiling.
// const showAdditionalMessage = pageContext === "aboutprofiling" && additionalMessage;
const buttonClass = isPrimary ? "primary" : "default";
return div(
{ className: "perf-button-container" },
showAdditionalMessage
? div(
{ className: "perf-additional-message" },
additionalMessage || nbsp
)
: null,
div(
null,
button(
{
className: `perf-photon-button perf-photon-button-${buttonClass} perf-button`,
disabled,
onClick,
},
label
),
additionalButton
? button(
{
className: `perf-photon-button perf-photon-button-default perf-button`,
onClick: additionalButton.onClick,
disabled,
},
additionalButton.label
)
: null
)
);
}
render() {
const {
startRecording,
@ -132,8 +70,9 @@ class RecordingButton extends PureComponent {
} = this.props;
if (!isSupportedPlatform) {
return this.renderButton({
label: "Start recording",
return renderButton({
label: startRecordingLabel(),
isPrimary: true,
disabled: true,
additionalMessage:
"Your platform is not supported. The Gecko Profiler only " +
@ -147,37 +86,40 @@ class RecordingButton extends PureComponent {
return null;
case "available-to-record":
return this.renderButton({
return renderButton({
onClick: startRecording,
label: span(
null,
img({
className: "perf-button-image",
src: "chrome://devtools/skin/images/tool-profiler.svg",
}),
"Start recording"
),
isPrimary: true,
label: startRecordingLabel(),
additionalMessage: recordingUnexpectedlyStopped
? div(null, "The recording was stopped by another tool.")
: null,
});
case "request-to-stop-profiler":
return this.renderButton({
return renderButton({
label: "Stopping recording",
disabled: true,
});
case "request-to-get-profile-and-stop-profiler":
return this.renderButton({
return renderButton({
label: "Capturing profile",
disabled: true,
});
case "request-to-start-recording":
case "recording":
return this.renderButton({
label: "Capture recording",
return renderButton({
label: span(
null,
"Capture recording",
img({
className: "perf-button-image",
alt: "",
/* This icon is actually the "open in new page" icon. */
src: "chrome://devtools/skin/images/dock-undock.svg",
})
),
isPrimary: true,
onClick: this._getProfileAndStopProfiler,
disabled: recordingState === "request-to-start-recording",
@ -188,15 +130,9 @@ class RecordingButton extends PureComponent {
});
case "locked-by-private-browsing":
return this.renderButton({
label: span(
null,
img({
className: "perf-button-image",
src: "chrome://devtools/skin/images/tool-profiler.svg",
}),
"Start recording"
),
return renderButton({
label: startRecordingLabel(),
isPrimary: true,
disabled: true,
additionalMessage: `The profiler is disabled when Private Browsing is enabled.
Close all Private Windows to re-enable the profiler`,
@ -208,6 +144,75 @@ class RecordingButton extends PureComponent {
}
}
/**
* @param {{
* disabled?: boolean,
* label?: React.ReactNode,
* onClick?: any,
* additionalMessage?: React.ReactNode,
* isPrimary?: boolean,
* pageContext?: PageContext,
* additionalButton?: {
* label: string,
* onClick: any,
* },
* }} buttonSettings
*/
function renderButton(buttonSettings) {
const {
disabled,
label,
onClick,
additionalMessage,
isPrimary,
// pageContext,
additionalButton,
} = buttonSettings;
const buttonClass = isPrimary ? "primary" : "default";
return div(
{ className: "perf-button-container" },
div(
null,
button(
{
className: `perf-photon-button perf-photon-button-${buttonClass} perf-button`,
disabled,
onClick,
},
label
),
additionalButton
? button(
{
className: `perf-photon-button perf-photon-button-default perf-button`,
onClick: additionalButton.onClick,
disabled,
},
additionalButton.label
)
: null
),
additionalMessage
? div({ className: "perf-additional-message" }, additionalMessage)
: null
);
}
function startRecordingLabel() {
return span(
null,
"Start recording",
img({
className: "perf-button-image",
alt: "",
/* This icon is actually the "open in new page" icon. */
src: "chrome://devtools/skin/images/dock-undock.svg",
})
);
}
/**
* @param {StoreState} state
* @returns {StateProps}

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

@ -6,7 +6,8 @@
DevToolsModules(
'AboutProfiling.js',
'Description.js',
'DevToolsAndPopup.js',
'DevToolsPanel.js',
'DevToolsPresetSelection.js',
'DirectoryPicker.js',
'Presets.js',
'ProfilerEventHandling.js',

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

@ -35,8 +35,8 @@
const ReactDOM = require("devtools/client/shared/vendor/react-dom");
const React = require("devtools/client/shared/vendor/react");
const DevToolsAndPopup = React.createFactory(
require("devtools/client/performance-new/components/DevToolsAndPopup")
const DevToolsPanel = React.createFactory(
require("devtools/client/performance-new/components/DevToolsPanel")
);
const ProfilerEventHandling = React.createFactory(
require("devtools/client/performance-new/components/ProfilerEventHandling")
@ -93,9 +93,6 @@ async function gInit(perfFront, preferenceFront) {
Promise.resolve(perfFront.getSupportedFeatures()).catch(() => null),
]);
// This panel doesn't support presets yet, make sure it's always set to custom.
recordingPreferences.presetName = "custom";
// Do some initialization, especially with privileged things that are part of the
// the browser.
store.dispatch(
@ -139,7 +136,7 @@ async function gInit(perfFront, preferenceFront) {
React.Fragment,
null,
ProfilerEventHandling(),
DevToolsAndPopup()
DevToolsPanel()
)
),
document.querySelector("#root")

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

@ -187,7 +187,7 @@ function createPerfComponent() {
const ReactDOM = require("devtools/client/shared/vendor/react-dom");
const ReactRedux = require("devtools/client/shared/vendor/react-redux");
const DevToolsAndPopup = React.createFactory(
require("devtools/client/performance-new/components/DevToolsAndPopup")
require("devtools/client/performance-new/components/DevToolsPanel")
);
const ProfilerEventHandling = React.createFactory(
require("devtools/client/performance-new/components/ProfilerEventHandling")

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

@ -30,10 +30,11 @@
}
.perf-button-image {
vertical-align: text-top;
padding-inline-end: 4px;
vertical-align: middle;
padding-inline-start: 8px;
width: 13px;
-moz-context-properties: fill;
fill: var(--theme-icon-color);
fill: #fff;
}
.perf-button-container {
@ -47,7 +48,9 @@
}
.perf-additional-message {
margin: 10px;
font-size: 13px;
font-weight: bold;
font-style: italic;
}
.perf > * {
@ -55,6 +58,7 @@
}
.perf-description {
font-size: 13px;
line-height: 1.4;
}
@ -67,6 +71,7 @@
text-decoration: underline;
white-space: nowrap;
cursor: pointer;
font-size: inherit;
}
/* Settings */
@ -328,7 +333,6 @@
/* See https://design.firefox.com/photon/components/buttons.html for the spec */
.perf-photon-button {
--blue-50-a30: rgba(10, 132, 255, 0.3);
padding: 0 8px;
border: none;
margin: 0;
@ -375,14 +379,15 @@
}
.perf-photon-button[disabled] {
opacity: 0.4;
opacity: 0.6;
}
.perf-photon-button.perf-button {
margin: 10px;
}
button.perf-photon-button:focus {
button.perf-photon-button:focus,
.perf-presets-select:focus {
box-shadow: 0 0 0 1px var(--blue-50) inset, 0 0 0 1px var(--blue-50),
0 0 0 4px var(--blue-50-a30);
outline: 0;
@ -397,72 +402,70 @@ a.perf-photon-button:focus {
height: 24px;
}
/* Photon message bar - https://design.firefox.com/photon/components/message-bars.html */
.perf-presets-hr {
width: 100%;
border: 1px solid var(--grey-30);
border-left: 0;
border-right: 0;
border-bottom: 0;
}
.perf-photon-message-bar {
.perf-presets {
display: flex;
width: 100%;
min-height: 32px;
box-sizing: border-box;
align-items: center;
padding: 0 4px;
border-radius: 4px;
margin-block: 12px;
}
.perf-presets-settings {
margin-inline-end: 17px;
margin-block: 3px;
font-size: 13px;
font-weight: bold;
}
.perf-presets-description {
margin-block: 13px;
}
.perf-presets-select {
/* Layout */
position: relative;
min-width: 186px;
padding-block: 3px;
padding-inline: 5px;
border: 1px solid transparent;
/* Presentational: */
-moz-appearance: none;
background-color: var(--grey-20);
background-image: url('chrome://global/skin/icons/arrow-dropdown-12.svg');
background-position: right 4px center;
background-repeat: no-repeat;
border-radius: 2px;
border: 1px solid transparent;
color: inherit !important;
font-size: 12px;
font-weight: 400;
line-height: 1.4;
text-decoration: none;
}
.perf-photon-message-bar-warning {
/* This should include an info icon, but it's left out since it's probably not worth
* adding and maintaining an extra icon here. */
padding: 4px 8px;
background: var(--yellow-50);
color: var(--yellow-90);
fill: var(--yellow-90);
.perf-presets-select:hover {
border: 1px solid var(--grey-30);
}
.perf-photon-message-bar-warning-icon {
background: url("chrome://global/skin/icons/warning.svg");
-moz-context-properties: fill;
width: 16px;
height: 16px;
margin: 4px;
.perf-presets-select:focus {
box-shadow:
0 0 0 2px var(--blue-50),
0 0 0 6px var(--blue-50-a30);
outline: 0;
}
.perf-photon-message-bar .perf-photon-button {
margin-inline-start: 8px;
.perf-presets-custom {
padding-inline: 10px;
margin-block: 13px;
line-height: 1.3;
}
.perf-photon-message-bar-warning .perf-photon-button {
background-color: var(--yellow-60);
}
.perf-photon-message-bar-warning .perf-photon-button:hover {
background-color: var(--yellow-70);
}
.perf-photon-message-bar-warning .perf-photon-button:hover:active {
background-color: var(--yellow-80);
}
/* Restart prompt */
.perf-env-restart {
/* Create an empty space at the top of the page. */
width: 100%;
height: 38px;
}
.perf-env-restart-fixed {
/* This style is for the floating bar */
position: fixed;
top: 0;
left: 0;
right: 0;
/* Create a new stacking context, so that the fixed positioning will be over the
* rest of the components */
z-index: 1;
border-radius: 0;
.perf-presets-custom-bold {
font-weight: bold;
}

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

@ -52,6 +52,7 @@
--blue-30: #75baff;
--blue-40: #45a1ff;
--blue-50: #0a84ff;
--blue-50-a30: rgba(10, 132, 255, 0.3);
--blue-55: #0074e8;
--blue-60: #0060df;
--blue-70: #003eaa;