diff options
Diffstat (limited to 'devtools/client/performance-new/panel')
-rw-r--r-- | devtools/client/performance-new/panel/README.md | 6 | ||||
-rw-r--r-- | devtools/client/performance-new/panel/index.xhtml | 29 | ||||
-rw-r--r-- | devtools/client/performance-new/panel/initializer.js | 189 | ||||
-rw-r--r-- | devtools/client/performance-new/panel/moz.build | 12 | ||||
-rw-r--r-- | devtools/client/performance-new/panel/panel.js | 107 |
5 files changed, 343 insertions, 0 deletions
diff --git a/devtools/client/performance-new/panel/README.md b/devtools/client/performance-new/panel/README.md new file mode 100644 index 0000000000..d2054521d0 --- /dev/null +++ b/devtools/client/performance-new/panel/README.md @@ -0,0 +1,6 @@ +This directory contains the files specific to the devtools panel. This panel is +used in the devtools toolbox attached to a page, but also when profiling +performance from about:profiling. + +The devtools panel also uses the redux store in `store/` as well as react +components in `components/`. diff --git a/devtools/client/performance-new/panel/index.xhtml b/devtools/client/performance-new/panel/index.xhtml new file mode 100644 index 0000000000..161eb850ed --- /dev/null +++ b/devtools/client/performance-new/panel/index.xhtml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> + +<!-- 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 src="resource://devtools/client/performance-new/panel/initializer.js"></script> + <script + src="chrome://devtools/content/shared/theme-switching.js" + defer="true" + ></script> + </body> +</html> diff --git a/devtools/client/performance-new/panel/initializer.js b/devtools/client/performance-new/panel/initializer.js new file mode 100644 index 0000000000..2264e4764f --- /dev/null +++ b/devtools/client/performance-new/panel/initializer.js @@ -0,0 +1,189 @@ +/* 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 +/* exported gInit, gDestroy, loader */ + +/** + * @typedef {import("../@types/perf").PerfFront} PerfFront + * @typedef {import("../@types/perf").PreferenceFront} PreferenceFront + * @typedef {import("../@types/perf").RecordingSettings} RecordingSettings + * @typedef {import("../@types/perf").PageContext} PageContext + * @typedef {import("../@types/perf").PanelWindow} PanelWindow + * @typedef {import("../@types/perf").Store} Store + * @typedef {import("../@types/perf").MinimallyTypedGeckoProfile} MinimallyTypedGeckoProfile + * @typedef {import("../@types/perf").ProfileCaptureResult} ProfileCaptureResult + * @typedef {import("../@types/perf").ProfilerViewMode} ProfilerViewMode + * @typedef {import("../@types/perf").RootTraits} RootTraits + */ +"use strict"; + +{ + // Create the browser loader, but take care not to conflict with + // TypeScript. See devtools/client/performance-new/typescript.md and + // the section on "Do not overload require" for more information. + + const { BrowserLoader } = ChromeUtils.import( + "resource://devtools/shared/loader/browser-loader.js" + ); + const browserLoader = BrowserLoader({ + baseURI: "resource://devtools/client/performance-new/", + window, + }); + + /** + * @type {any} - Coerce the current scope into an `any`, and assign the + * loaders to the scope. They can then be used freely below. + */ + const scope = this; + scope.require = browserLoader.require; + scope.loader = browserLoader.loader; +} + +const ReactDOM = require("resource://devtools/client/shared/vendor/react-dom.js"); +const React = require("resource://devtools/client/shared/vendor/react.js"); +const FluentReact = require("resource://devtools/client/shared/vendor/fluent-react.js"); +const { + FluentL10n, +} = require("resource://devtools/client/shared/fluent-l10n/fluent-l10n.js"); +const Provider = React.createFactory( + require("resource://devtools/client/shared/vendor/react-redux.js").Provider +); +const LocalizationProvider = React.createFactory( + FluentReact.LocalizationProvider +); +const DevToolsPanel = React.createFactory( + require("resource://devtools/client/performance-new/components/panel/DevToolsPanel.js") +); +const ProfilerEventHandling = React.createFactory( + require("resource://devtools/client/performance-new/components/panel/ProfilerEventHandling.js") +); +const ProfilerPreferenceObserver = React.createFactory( + require("resource://devtools/client/performance-new/components/shared/ProfilerPreferenceObserver.js") +); +const createStore = require("resource://devtools/client/shared/redux/create-store.js"); +const selectors = require("resource://devtools/client/performance-new/store/selectors.js"); +const reducers = require("resource://devtools/client/performance-new/store/reducers.js"); +const actions = require("resource://devtools/client/performance-new/store/actions.js"); +const { + openProfilerTab, + sharedLibrariesFromProfile, +} = require("resource://devtools/client/performance-new/shared/browser.js"); +const { createLocalSymbolicationService } = ChromeUtils.importESModule( + "resource://devtools/client/performance-new/shared/symbolication.sys.mjs" +); +const { + presets, + getProfilerViewModeForCurrentPreset, + registerProfileCaptureForBrowser, +} = ChromeUtils.importESModule( + "resource://devtools/client/performance-new/shared/background.sys.mjs" +); + +/** + * This file initializes the DevTools Panel UI. It is in charge of initializing + * the DevTools specific environment, and then passing those requirements into + * the UI. + */ + +/** + * Initialize the panel by creating a redux store, and render the root component. + * + * @param {PerfFront} perfFront - The Perf actor's front. Used to start and stop recordings. + * @param {RootTraits} traits - The traits coming from the root actor. This + * makes it possible to change some code path + * depending on the server version. + * @param {PageContext} pageContext - The context that the UI is being loaded in under. + * @param {(() => void)} openAboutProfiling - Optional call to open about:profiling + */ +async function gInit(perfFront, traits, pageContext, openAboutProfiling) { + const store = createStore(reducers); + const isSupportedPlatform = await perfFront.isSupportedPlatform(); + const supportedFeatures = await perfFront.getSupportedFeatures(); + + { + // Expose the store as a global, for testing. + const anyWindow = /** @type {any} */ (window); + const panelWindow = /** @type {PanelWindow} */ (anyWindow); + // The store variable is a `ReduxStore`, not our `Store` type, as defined + // in perf.d.ts. Coerce it into the `Store` type. + const anyStore = /** @type {any} */ (store); + panelWindow.gStore = anyStore; + } + + const l10n = new FluentL10n(); + await l10n.init([ + "devtools/client/perftools.ftl", + // For -brand-shorter-name used in some profiler preset descriptions. + "branding/brand.ftl", + // Needed for the onboarding UI + "devtools/client/toolbox-options.ftl", + "toolkit/branding/brandings.ftl", + ]); + + // Do some initialization, especially with privileged things that are part of the + // the browser. + store.dispatch( + actions.initializeStore({ + isSupportedPlatform, + presets, + supportedFeatures, + pageContext, + }) + ); + + /** + * @param {MinimallyTypedGeckoProfile} profile + */ + const onProfileReceived = async profile => { + const objdirs = selectors.getObjdirs(store.getState()); + const profilerViewMode = getProfilerViewModeForCurrentPreset(pageContext); + const sharedLibraries = sharedLibrariesFromProfile(profile); + const symbolicationService = createLocalSymbolicationService( + sharedLibraries, + objdirs, + perfFront + ); + const browser = await openProfilerTab(profilerViewMode); + + /** + * @type {ProfileCaptureResult} + */ + const profileCaptureResult = { type: "SUCCESS", profile }; + + registerProfileCaptureForBrowser( + browser, + profileCaptureResult, + symbolicationService + ); + }; + + const onEditSettingsLinkClicked = openAboutProfiling; + + ReactDOM.render( + Provider( + { store }, + LocalizationProvider( + { bundles: l10n.getBundles() }, + React.createElement( + React.Fragment, + null, + ProfilerEventHandling({ perfFront, traits }), + ProfilerPreferenceObserver(), + DevToolsPanel({ + perfFront, + onProfileReceived, + onEditSettingsLinkClicked, + }) + ) + ) + ), + document.querySelector("#root") + ); + + window.addEventListener("unload", () => gDestroy(), { once: true }); +} + +function gDestroy() { + ReactDOM.unmountComponentAtNode(document.querySelector("#root")); +} diff --git a/devtools/client/performance-new/panel/moz.build b/devtools/client/performance-new/panel/moz.build new file mode 100644 index 0000000000..298ed5a866 --- /dev/null +++ b/devtools/client/performance-new/panel/moz.build @@ -0,0 +1,12 @@ +# 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( + "initializer.js", + "panel.js", +) + +with Files("**"): + BUG_COMPONENT = ("DevTools", "Performance Tools (Profiler/Timeline)") diff --git a/devtools/client/performance-new/panel/panel.js b/devtools/client/performance-new/panel/panel.js new file mode 100644 index 0000000000..d099f3c296 --- /dev/null +++ b/devtools/client/performance-new/panel/panel.js @@ -0,0 +1,107 @@ +/* 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 +"use strict"; + +/** + * This file contains the PerformancePanel, which uses a common API for DevTools to + * start and load everything. This will call `gInit` from the initializer.js file, + * which does the important initialization for the panel. This code is more concerned + * with wiring this panel into the rest of DevTools and fetching the Actor's fronts. + */ + +/** + * @typedef {import("../@types/perf").PanelWindow} PanelWindow + * @typedef {import("../@types/perf").Toolbox} Toolbox + * @typedef {import("../@types/perf").Target} Target + * @typedef {import("../@types/perf").Commands} Commands + */ + +class PerformancePanel { + /** + * @param {PanelWindow} iframeWindow + * @param {Toolbox} toolbox + * @param {Commands} commands + */ + constructor(iframeWindow, toolbox, commands) { + this.panelWin = iframeWindow; + this.toolbox = toolbox; + this.commands = commands; + + const EventEmitter = require("resource://devtools/shared/event-emitter.js"); + EventEmitter.decorate(this); + } + + /** + * This is implemented (and overwritten) by the EventEmitter. Is there a way + * to use mixins with JSDoc? + * + * @param {string} eventName + */ + emit(eventName) {} + + /** + * Open is effectively an asynchronous constructor. + * @return {Promise<PerformancePanel>} Resolves when the Perf tool completes + * opening. + */ + open() { + if (!this._opening) { + this._opening = this._doOpen(); + } + return this._opening; + } + + /** + * This function is the actual implementation of the open() method. + * @returns Promise<PerformancePanel> + */ + async _doOpen() { + this.panelWin.gToolbox = this.toolbox; + this.panelWin.gIsPanelDestroyed = false; + + const perfFront = await this.commands.client.mainRoot.getFront("perf"); + + // Note: we are not using traits in the panel at the moment but we keep the + // wiring in case we need it later on. + const traits = {}; + + await this.panelWin.gInit( + perfFront, + traits, + "devtools", + this._openAboutProfiling + ); + return this; + } + + _openAboutProfiling() { + const { + openTrustedLink, + } = require("resource://devtools/client/shared/link.js"); + openTrustedLink("about:profiling", {}); + } + + // DevToolPanel API: + + /** + * @returns {Target} target + */ + get target() { + return this.toolbox.target; + } + + destroy() { + // Make sure this panel is not already destroyed. + if (this._destroyed) { + return; + } + this.panelWin.gDestroy(); + this.emit("destroyed"); + this._destroyed = true; + this.panelWin.gIsPanelDestroyed = true; + } +} + +exports.PerformancePanel = PerformancePanel; |