summaryrefslogtreecommitdiffstats
path: root/devtools/client/performance-new/components/panel/RecordingButton.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/performance-new/components/panel/RecordingButton.js')
-rw-r--r--devtools/client/performance-new/components/panel/RecordingButton.js265
1 files changed, 265 insertions, 0 deletions
diff --git a/devtools/client/performance-new/components/panel/RecordingButton.js b/devtools/client/performance-new/components/panel/RecordingButton.js
new file mode 100644
index 0000000000..352468cbd1
--- /dev/null
+++ b/devtools/client/performance-new/components/panel/RecordingButton.js
@@ -0,0 +1,265 @@
+/* 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 {RecordingState} recordingState
+ * @property {boolean | null} isSupportedPlatform
+ * @property {boolean} recordingUnexpectedlyStopped
+ * @property {PageContext} pageContext
+ */
+
+/**
+ * @typedef {Object} OwnProps
+ * @property {import("../../@types/perf").OnProfileReceived} onProfileReceived
+ * @property {import("../../@types/perf").PerfFront} perfFront
+ */
+
+/**
+ * @typedef {Object} ThunkDispatchProps
+ * @property {typeof actions.startRecording} startRecording
+ * @property {typeof actions.getProfileAndStopProfiler} getProfileAndStopProfiler
+ * @property {typeof actions.stopProfilerAndDiscardProfile} stopProfilerAndDiscardProfile
+
+ */
+
+/**
+ * @typedef {ResolveThunks<ThunkDispatchProps>} DispatchProps
+ * @typedef {StateProps & DispatchProps & OwnProps} Props
+ * @typedef {import("../../@types/perf").RecordingState} RecordingState
+ * @typedef {import("../../@types/perf").State} StoreState
+ * @typedef {import("../../@types/perf").PageContext} PageContext
+ */
+
+"use strict";
+
+const {
+ PureComponent,
+} = require("resource://devtools/client/shared/vendor/react.js");
+const {
+ div,
+ button,
+ span,
+ img,
+} = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
+const {
+ connect,
+} = require("resource://devtools/client/shared/vendor/react-redux.js");
+const actions = require("resource://devtools/client/performance-new/store/actions.js");
+const selectors = require("resource://devtools/client/performance-new/store/selectors.js");
+const React = require("resource://devtools/client/shared/vendor/react.js");
+const Localized = React.createFactory(
+ require("resource://devtools/client/shared/vendor/fluent-react.js").Localized
+);
+
+/**
+ * This component is not responsible for the full life cycle of recording a profile. It
+ * is only responsible for the actual act of stopping and starting recordings. It
+ * also reacts to the changes of the recording state from external changes.
+ *
+ * @extends {React.PureComponent<Props>}
+ */
+class RecordingButton extends PureComponent {
+ _onStartButtonClick = () => {
+ const { startRecording, perfFront } = this.props;
+ startRecording(perfFront);
+ };
+
+ _onCaptureButtonClick = async () => {
+ const { getProfileAndStopProfiler, onProfileReceived, perfFront } =
+ this.props;
+ const profile = await getProfileAndStopProfiler(perfFront);
+ onProfileReceived(profile);
+ };
+
+ _onStopButtonClick = () => {
+ const { stopProfilerAndDiscardProfile, perfFront } = this.props;
+ stopProfilerAndDiscardProfile(perfFront);
+ };
+
+ render() {
+ const {
+ recordingState,
+ isSupportedPlatform,
+ recordingUnexpectedlyStopped,
+ } = this.props;
+
+ if (!isSupportedPlatform) {
+ return renderButton({
+ label: startRecordingLabel(),
+ isPrimary: true,
+ disabled: true,
+ additionalMessage:
+ // No need to localize as this string is not displayed to Tier-1 platforms.
+ "Your platform is not supported. The Gecko Profiler only " +
+ "supports Tier-1 platforms.",
+ });
+ }
+
+ switch (recordingState) {
+ case "not-yet-known":
+ return null;
+
+ case "available-to-record":
+ return renderButton({
+ onClick: this._onStartButtonClick,
+ isPrimary: true,
+ label: startRecordingLabel(),
+ additionalMessage: recordingUnexpectedlyStopped
+ ? Localized(
+ { id: "perftools-status-recording-stopped-by-another-tool" },
+ div(null, "The recording was stopped by another tool.")
+ )
+ : null,
+ });
+
+ case "request-to-stop-profiler":
+ return renderButton({
+ label: Localized(
+ { id: "perftools-request-to-stop-profiler" },
+ "Stopping recording"
+ ),
+ disabled: true,
+ });
+
+ case "request-to-get-profile-and-stop-profiler":
+ return renderButton({
+ label: Localized(
+ { id: "perftools-request-to-get-profile-and-stop-profiler" },
+ "Capturing profile"
+ ),
+ disabled: true,
+ });
+
+ case "request-to-start-recording":
+ case "recording":
+ return renderButton({
+ label: span(
+ null,
+ Localized(
+ { id: "perftools-button-capture-recording" },
+ "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._onCaptureButtonClick,
+ disabled: recordingState === "request-to-start-recording",
+ additionalButton: {
+ label: Localized(
+ { id: "perftools-button-cancel-recording" },
+ "Cancel recording"
+ ),
+ onClick: this._onStopButtonClick,
+ },
+ });
+
+ default:
+ throw new Error("Unhandled recording state");
+ }
+ }
+}
+
+/**
+ * @param {{
+ * disabled?: boolean,
+ * label?: React.ReactNode,
+ * onClick?: any,
+ * additionalMessage?: React.ReactNode,
+ * isPrimary?: boolean,
+ * pageContext?: PageContext,
+ * additionalButton?: {
+ * label: React.ReactNode,
+ * 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,
+ Localized({ id: "perftools-button-start-recording" }, "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}
+ */
+function mapStateToProps(state) {
+ return {
+ recordingState: selectors.getRecordingState(state),
+ isSupportedPlatform: selectors.getIsSupportedPlatform(state),
+ recordingUnexpectedlyStopped:
+ selectors.getRecordingUnexpectedlyStopped(state),
+ pageContext: selectors.getPageContext(state),
+ };
+}
+
+/** @type {ThunkDispatchProps} */
+const mapDispatchToProps = {
+ startRecording: actions.startRecording,
+ stopProfilerAndDiscardProfile: actions.stopProfilerAndDiscardProfile,
+ getProfileAndStopProfiler: actions.getProfileAndStopProfiler,
+};
+
+module.exports = connect(mapStateToProps, mapDispatchToProps)(RecordingButton);