diff options
Diffstat (limited to 'devtools/client/performance-new/@types')
-rw-r--r-- | devtools/client/performance-new/@types/README.md | 5 | ||||
-rw-r--r-- | devtools/client/performance-new/@types/gecko.d.ts | 458 | ||||
-rw-r--r-- | devtools/client/performance-new/@types/perf.d.ts | 688 |
3 files changed, 1151 insertions, 0 deletions
diff --git a/devtools/client/performance-new/@types/README.md b/devtools/client/performance-new/@types/README.md new file mode 100644 index 0000000000..204e88a419 --- /dev/null +++ b/devtools/client/performance-new/@types/README.md @@ -0,0 +1,5 @@ +# TypeScript @types + +This folder contains the type files that can be imported into various files. These types are collected here, and are likely cumbersome to type in JSDoc, or are separate enough from the code that it's easier to type them in separate files (such as the Redux store types). + +In addition, there is an ambient type file, `gecko.d.ts` file that contains the ambient types that are unique to Gecko. diff --git a/devtools/client/performance-new/@types/gecko.d.ts b/devtools/client/performance-new/@types/gecko.d.ts new file mode 100644 index 0000000000..e03844cb34 --- /dev/null +++ b/devtools/client/performance-new/@types/gecko.d.ts @@ -0,0 +1,458 @@ +/* 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-TODO - Needs typing. + * + * This file contains type stubs for loading things from Gecko. All of these + * types should be used in the correct places eventually. + */ + +/** + * Namespace anything that has its types mocked out here. These definitions are + * only "good enough" to get the type checking to pass in this directory. + * Eventually some more structured solution should be found. This namespace is + * global and makes sure that all the definitions inside do not clash with + * naming. + */ +declare namespace MockedExports { + /** + * This interface teaches ChromeUtils.import how to find modules. + */ + interface KnownModules { + Services: typeof import("Services"); + "resource://gre/modules/AppConstants.sys.mjs": typeof import("resource://gre/modules/AppConstants.sys.mjs"); + "resource:///modules/CustomizableUI.sys.mjs": typeof import("resource:///modules/CustomizableUI.sys.mjs"); + "resource:///modules/CustomizableWidgets.sys.mjs": typeof import("resource:///modules/CustomizableWidgets.sys.mjs"); + "resource://devtools/shared/loader/Loader.sys.mjs": typeof import("resource://devtools/shared/loader/Loader.sys.mjs"); + "resource://devtools/client/performance-new/shared/background.sys.mjs": typeof import("resource://devtools/client/performance-new/shared/background.sys.mjs"); + "resource://devtools/client/performance-new/shared/symbolication.sys.mjs": typeof import("resource://devtools/client/performance-new/shared/symbolication.sys.mjs"); + "resource://devtools/shared/loader/browser-loader.js": any; + "resource://devtools/client/performance-new/popup/menu-button.sys.mjs": typeof import("resource://devtools/client/performance-new/popup/menu-button.sys.mjs"); + "resource://devtools/client/performance-new/shared/typescript-lazy-load.sys.mjs": typeof import("resource://devtools/client/performance-new/shared/typescript-lazy-load.sys.mjs"); + "resource://devtools/client/performance-new/popup/logic.sys.mjs": typeof import("resource://devtools/client/performance-new/popup/logic.sys.mjs"); + "resource:///modules/PanelMultiView.sys.mjs": typeof import("resource:///modules/PanelMultiView.sys.mjs"); + } + + interface ChromeUtils { + /** + * This function reads the KnownModules and resolves which import to use. + * If you are getting the TS2345 error: + * + * Argument of type '"resource:///.../file.jsm"' is not assignable to parameter + * of type + * + * Then add the file path to the KnownModules above. + */ + import: <S extends keyof KnownModules>(module: S) => KnownModules[S]; + importESModule: <S extends keyof KnownModules>( + module: S + ) => KnownModules[S]; + defineModuleGetter: (target: any, variable: string, path: string) => void; + defineESModuleGetters: (target: any, mappings: any) => void; + } + + interface MessageManager { + loadFrameScript(url: string, flag: boolean): void; + sendAsyncMessage: (event: string, data: any) => void; + addMessageListener: (event: string, listener: (event: any) => void) => void; + } + + interface Browser { + addWebTab: (url: string, options: any) => BrowserTab; + contentPrincipal: any; + selectedTab: BrowserTab; + selectedBrowser?: ChromeBrowser; + messageManager: MessageManager; + ownerDocument?: ChromeDocument; + } + + interface BrowserTab { + linkedBrowser: Browser; + } + + interface ChromeWindow { + gBrowser: Browser; + focus(): void; + openWebLinkIn( + url: string, + where: "current" | "tab" | "window", + options: Partial<{ + // Not all possible options are present, please add more if/when needed. + userContextId: number; + forceNonPrivate: boolean; + relatedToCurrent: boolean; + resolveOnContentBrowserCreated: ( + contentBrowser: ChromeBrowser + ) => unknown; + }> + ): void; + } + + interface ChromeBrowser { + browsingContext?: BrowsingContext; + } + + interface BrowsingContext { + /** + * A unique identifier for the browser element that is hosting this + * BrowsingContext tree. Every BrowsingContext in the element's tree will + * return the same ID in all processes and it will remain stable regardless of + * process changes. When a browser element's frameloader is switched to + * another browser element this ID will remain the same but hosted under the + * under the new browser element. + * We are using this identifier for getting the active tab ID and passing to + * the profiler back-end. See `getActiveBrowserID` for the usage. + */ + browserId: number; + } + + type GetPref<T> = (prefName: string, defaultValue?: T) => T; + type SetPref<T> = (prefName: string, value?: T) => T; + type nsIPrefBranch = { + clearUserPref: (prefName: string) => void; + getStringPref: GetPref<string>; + setStringPref: SetPref<string>; + getCharPref: GetPref<string>; + setCharPref: SetPref<string>; + getIntPref: GetPref<number>; + setIntPref: SetPref<number>; + getBoolPref: GetPref<boolean>; + setBoolPref: SetPref<boolean>; + addObserver: ( + aDomain: string, + aObserver: PrefObserver, + aHoldWeak?: boolean + ) => void; + removeObserver: (aDomain: string, aObserver: PrefObserver) => void; + }; + + type PrefObserverFunction = ( + aSubject: nsIPrefBranch, + aTopic: "nsPref:changed", + aData: string + ) => unknown; + type PrefObserver = PrefObserverFunction | { observe: PrefObserverFunction }; + + interface nsIURI {} + + interface SharedLibrary { + start: number; + end: number; + offset: number; + name: string; + path: string; + debugName: string; + debugPath: string; + breakpadId: string; + arch: string; + } + + interface ProfileGenerationAdditionalInformation { + sharedLibraries: SharedLibrary[]; + } + + interface ProfileAndAdditionalInformation { + profile: ArrayBuffer; + additionalInformation?: ProfileGenerationAdditionalInformation; + } + + type Services = { + env: { + set: (name: string, value: string) => void; + get: (name: string) => string; + exists: (name: string) => boolean; + }; + prefs: nsIPrefBranch; + profiler: { + StartProfiler: ( + entryCount: number, + interval: number, + features: string[], + filters?: string[], + activeTabId?: number, + duration?: number + ) => void; + StopProfiler: () => void; + IsPaused: () => boolean; + Pause: () => void; + Resume: () => void; + IsSamplingPaused: () => boolean; + PauseSampling: () => void; + ResumeSampling: () => void; + GetFeatures: () => string[]; + getProfileDataAsync: (sinceTime?: number) => Promise<object>; + getProfileDataAsArrayBuffer: (sinceTime?: number) => Promise<ArrayBuffer>; + getProfileDataAsGzippedArrayBuffer: ( + sinceTime?: number + ) => Promise<ProfileAndAdditionalInformation>; + IsActive: () => boolean; + sharedLibraries: SharedLibrary[]; + }; + platform: string; + obs: { + addObserver: (observer: object, type: string) => void; + removeObserver: (observer: object, type: string) => void; + }; + wm: { + getMostRecentWindow: (name: string) => ChromeWindow; + getMostRecentNonPBWindow: (name: string) => ChromeWindow; + }; + focus: { + activeWindow: ChromeWindow; + }; + io: { + newURI(url: string): nsIURI; + }; + scriptSecurityManager: any; + startup: { + quit: (optionsBitmask: number) => void; + eForceQuit: number; + eRestart: number; + }; + }; + + const EventEmitter: { + decorate: (target: object) => void; + }; + + const AppConstantsSYSMJS: { + AppConstants: { + platform: string; + }; + }; + + interface BrowsingContextStub {} + interface PrincipalStub {} + + interface WebChannelTarget { + browsingContext: BrowsingContextStub; + browser: Browser; + eventTarget: null; + principal: PrincipalStub; + } + + // TS-TODO + const CustomizableUISYSMJS: any; + const CustomizableWidgetsSYSMJS: any; + const PanelMultiViewSYSMJS: any; + + const LoaderESM: { + require: (path: string) => any; + }; + + const Services: Services; + + // This class is needed by the Cc importing mechanism. e.g. + // Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker); + class nsIFilePicker {} + + interface FilePicker { + init: (window: Window, title: string, mode: number) => void; + open: (callback: (rv: number) => unknown) => void; + // The following are enum values. + modeGetFolder: number; + returnOK: number; + file: { + path: string; + }; + } + + interface Cc { + "@mozilla.org/filepicker;1": { + createInstance(instance: nsIFilePicker): FilePicker; + }; + } + + interface Ci { + nsIFilePicker: nsIFilePicker; + } + + interface Cu { + /** + * This function reads the KnownModules and resolves which import to use. + * If you are getting the TS2345 error: + * + * Argument of type '"resource:///.../file.jsm"' is not assignable to parameter + * of type + * + * Then add the file path to the KnownModules above. + */ + import: <S extends keyof KnownModules>(module: S) => KnownModules[S]; + exportFunction: (fn: Function, scope: object, options?: object) => void; + cloneInto: (value: any, scope: object, options?: object) => void; + isInAutomation: boolean; + } + + interface FluentLocalization { + /** + * This function sets the attributes data-l10n-id and possibly data-l10n-args + * on the element. + */ + setAttributes( + target: Element, + id?: string, + args?: Record<string, string> + ): void; + } +} + +interface PathUtilsInterface { + split: (path: string) => string[]; + isAbsolute: (path: string) => boolean; +} + +declare module "resource://devtools/client/shared/vendor/react.js" { + import * as React from "react"; + export = React; +} + +declare module "resource://devtools/client/shared/vendor/react-dom-factories.js" { + import * as ReactDomFactories from "react-dom-factories"; + export = ReactDomFactories; +} + +declare module "resource://devtools/client/shared/vendor/redux.js" { + import * as Redux from "redux"; + export = Redux; +} + +declare module "resource://devtools/client/shared/vendor/react-redux.js" { + import * as ReactRedux from "react-redux"; + export = ReactRedux; +} + +declare module "resource://devtools/shared/event-emitter2.js" { + export = MockedExports.EventEmitter; +} + +declare module "Services" { + export = MockedExports.Services; +} + +declare module "ChromeUtils" { + export = ChromeUtils; +} + +declare module "resource://gre/modules/AppConstants.sys.mjs" { + export = MockedExports.AppConstantsSYSMJS; +} + +declare module "resource://devtools/client/performance-new/shared/background.sys.mjs" { + import * as Background from "devtools/client/performance-new/shared/background.sys.mjs"; + export = Background; +} + +declare module "resource://devtools/client/performance-new/shared/symbolication.sys.mjs" { + import * as PerfSymbolication from "devtools/client/performance-new/shared/symbolication.sys.mjs"; + export = PerfSymbolication; +} + +declare module "resource:///modules/CustomizableUI.sys.mjs" { + export = MockedExports.CustomizableUISYSMJS; +} + +declare module "resource:///modules/CustomizableWidgets.sys.mjs" { + export = MockedExports.CustomizableWidgetsSYSMJS; +} + +declare module "resource:///modules/PanelMultiView.sys.mjs" { + export = MockedExports.PanelMultiViewSYSMJS; +} + +declare module "resource://devtools/shared/loader/Loader.sys.mjs" { + export = MockedExports.LoaderESM; +} + +declare var ChromeUtils: MockedExports.ChromeUtils; + +declare var PathUtils: PathUtilsInterface; + +// These global objects can be used directly in JSM files only. +declare var Cu: MockedExports.Cu; +declare var Cc: MockedExports.Cc; +declare var Ci: MockedExports.Ci; +declare var Services: MockedExports.Services; + +/** + * This is a variant on the normal Document, as it contains chrome-specific properties. + */ +declare interface ChromeDocument extends Document { + /** + * Create a XUL element of a specific type. Right now this function + * only refines iframes, but more tags could be added. + */ + createXULElement: ((type: "iframe") => XULIframeElement) & + ((type: string) => XULElement); + + /** + * This is a fluent instance connected to this document. + */ + l10n: MockedExports.FluentLocalization; +} + +/** + * This is a variant on the HTMLElement, as it contains chrome-specific properties. + */ +declare interface ChromeHTMLElement extends HTMLElement { + ownerDocument: ChromeDocument; +} + +declare interface XULElement extends HTMLElement { + ownerDocument: ChromeDocument; +} + +declare interface XULIframeElement extends XULElement { + contentWindow: ChromeWindow; + src: string; +} + +declare interface ChromeWindow extends Window { + openWebLinkIn: ( + url: string, + where: "current" | "tab" | "tabshifted" | "window" | "save", + // TS-TODO + params?: unknown + ) => void; + openTrustedLinkIn: ( + url: string, + where: "current" | "tab" | "tabshifted" | "window" | "save", + // TS-TODO + params?: unknown + ) => void; +} + +declare class ChromeWorker extends Worker {} + +declare interface MenuListElement extends XULElement { + value: string; + disabled: boolean; +} + +declare interface XULCommandEvent extends Event { + target: XULElement; +} + +declare interface XULElementWithCommandHandler { + addEventListener: ( + type: "command", + handler: (event: XULCommandEvent) => void, + isCapture?: boolean + ) => void; + removeEventListener: ( + type: "command", + handler: (event: XULCommandEvent) => void, + isCapture?: boolean + ) => void; +} + +declare type nsIPrefBranch = MockedExports.nsIPrefBranch; + +// chrome context-only DOM isInstance method +// XXX: This hackishly extends Function because there is no way to extend DOM constructors. +// Callers should manually narrow the type when needed. +// See also https://github.com/microsoft/TypeScript-DOM-lib-generator/issues/222 +interface Function { + isInstance(obj: any): boolean; +} diff --git a/devtools/client/performance-new/@types/perf.d.ts b/devtools/client/performance-new/@types/perf.d.ts new file mode 100644 index 0000000000..2c7ec7f0b4 --- /dev/null +++ b/devtools/client/performance-new/@types/perf.d.ts @@ -0,0 +1,688 @@ +/* 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/. */ + +/** + * This file contains the shared types for the performance-new client. + */ + +import { + Reducer as ReduxReducer, + Store as ReduxStore, +} from "resource://devtools/client/shared/vendor/redux.js"; + +export interface PanelWindow { + gToolbox?: any; + gStore?: Store; + gInit( + perfFront: PerfFront, + traits: RootTraits, + pageContext: PageContext, + openAboutProfiling: () => void + ): Promise<void>; + gDestroy(): void; + gIsPanelDestroyed?: boolean; +} + +/** + * TS-TODO - Stub. + */ +export interface Target { + // TODO + client: any; +} + +/** + * TS-TODO - Stub. + */ +export interface Toolbox { + target: Target; +} + +/** + * TS-TODO - Stub. + */ +export interface Commands { + client: any; + targetCommand: { + targetFront: { + getTrait: ( + traitName: string + ) => unknown; + }; + }; +} + +/** + * TS-TODO - Stub. + */ +export interface PerfFront { + startProfiler: (options: RecordingSettings) => Promise<boolean>; + getProfileAndStopProfiler: () => Promise<any>; + stopProfilerAndDiscardProfile: () => Promise<void>; + getSymbolTable: ( + path: string, + breakpadId: string + ) => Promise<[number[], number[], number[]]>; + isActive: () => Promise<boolean>; + isSupportedPlatform: () => Promise<boolean>; + on: (type: string, listener: () => void) => void; + off: (type: string, listener: () => void) => void; + destroy: () => void; + getSupportedFeatures: () => Promise<string[]>; +} + +/** + * TS-TODO - Stub + */ +export interface PreferenceFront { + clearUserPref: (prefName: string) => Promise<void>; + getStringPref: (prefName: string) => Promise<string>; + setStringPref: (prefName: string, value: string) => Promise<void>; + getCharPref: (prefName: string) => Promise<string>; + setCharPref: (prefName: string, value: string) => Promise<void>; + getIntPref: (prefName: string) => Promise<number>; + setIntPref: (prefName: string, value: number) => Promise<void>; +} + +export interface RootTraits { + // There are no traits used by the performance front end at the moment. +} + +export type RecordingState = + // The initial state before we've queried the PerfActor + | "not-yet-known" + // The profiler is available, we haven't started recording yet. + | "available-to-record" + // An async request has been sent to start the profiler. + | "request-to-start-recording" + // An async request has been sent to get the profile and stop the profiler. + | "request-to-get-profile-and-stop-profiler" + // An async request has been sent to stop the profiler. + | "request-to-stop-profiler" + // The profiler notified us that our request to start it actually started + // it, or it was already started. + | "recording"; + +// We are currently migrating to a new UX workflow with about:profiling. +// This type provides an easy way to change the implementation based +// on context. +export type PageContext = + | "devtools" + | "devtools-remote" + | "aboutlogging" + | "aboutprofiling" + | "aboutprofiling-remote"; + +export type PrefPostfix = "" | ".remote"; + +export interface State { + recordingState: RecordingState; + recordingUnexpectedlyStopped: boolean; + isSupportedPlatform: boolean | null; + recordingSettings: RecordingSettings; + initializedValues: InitializedValues | null; + promptEnvRestart: null | string; +} + +export type Selector<T> = (state: State) => T; + +export type ThunkDispatch = <Returns>(action: ThunkAction<Returns>) => Returns; +export type PlainDispatch = (action: Action) => Action; +export type GetState = () => State; +export type SymbolTableAsTuple = [Uint32Array, Uint32Array, Uint8Array]; + +/** + * The `dispatch` function can accept either a plain action or a thunk action. + * This is similar to a type `(action: Action | ThunkAction) => any` except this + * allows to type the return value as well. + */ +export type Dispatch = PlainDispatch & ThunkDispatch; + +export type ThunkAction<Returns> = ({ + dispatch, + getState, +}: { + dispatch: Dispatch; + getState: GetState; +}) => Returns; + +export interface Library { + start: number; + end: number; + offset: number; + name: string; + path: string; + debugName: string; + debugPath: string; + breakpadId: string; + arch: string; +} + +/** + * Only provide types for the GeckoProfile as much as we need it. There is no + * reason to maintain a full type definition here. + */ +export interface MinimallyTypedGeckoProfile { + libs: Library[]; + processes: MinimallyTypedGeckoProfile[]; +} + +export type GetSymbolTableCallback = ( + debugName: string, + breakpadId: string +) => Promise<SymbolTableAsTuple>; + +export interface SymbolicationService { + getSymbolTable: GetSymbolTableCallback; + querySymbolicationApi: (path: string, requestJson: string) => Promise<string>; +} + +export type ReceiveProfile = ( + geckoProfile: MinimallyTypedGeckoProfile, + profilerViewMode: ProfilerViewMode | undefined, + getSymbolTableCallback: GetSymbolTableCallback +) => void; + +/** + * This is the type signature for a function to restart the browser with a given + * environment variable. Currently only implemented for the popup. + */ +export type RestartBrowserWithEnvironmentVariable = ( + envName: string, + value: string +) => void; + +/** + * This is the type signature for the event listener that's called once the + * profile has been obtained. + */ +export type OnProfileReceived = (profile: MinimallyTypedGeckoProfile) => void; + +/** + * This is the type signature for a function to query the browser for the + * ID of the active tab. + */ +export type GetActiveBrowserID = () => number; + +/** + * This interface is injected into profiler.firefox.com + */ +interface GeckoProfilerFrameScriptInterface { + getProfile: () => Promise<MinimallyTypedGeckoProfile>; + getSymbolTable: GetSymbolTableCallback; +} + +export interface RecordingSettings { + presetName: string; + entries: number; + interval: number; // in milliseconds + features: string[]; + threads: string[]; + objdirs: string[]; + // The duration is currently not wired up to the UI yet. See Bug 1587165. + duration?: number; +} + +/** + * A Redux Reducer that knows about the performance-new client's Actions. + */ +export type Reducer<S> = (state: S | undefined, action: Action) => S; + +export interface InitializedValues { + // The current list of presets, loaded in from a JSM. + presets: Presets; + // Determine the current page context. + pageContext: PageContext; + // The list of profiler features that the current target supports. + supportedFeatures: string[]; + // Allow about:profiling to switch back to the remote devtools panel. + openRemoteDevTools?: () => void; +} + +/** + * Export a store that is opinionated about our State definition, and the union + * of all Actions, as well as specific Dispatch behavior. + */ +export type Store = ReduxStore<State, Action>; + +export type Action = + | { + type: "REPORT_PROFILER_READY"; + isActive: boolean; + } + | { + type: "REPORT_PROFILER_STARTED"; + } + | { + type: "REPORT_PROFILER_STOPPED"; + } + | { + type: "REQUESTING_TO_START_RECORDING"; + } + | { + type: "REQUESTING_TO_STOP_RECORDING"; + } + | { + type: "REQUESTING_PROFILE"; + } + | { + type: "OBTAINED_PROFILE"; + } + | { + type: "CHANGE_INTERVAL"; + interval: number; + } + | { + type: "CHANGE_ENTRIES"; + entries: number; + } + | { + type: "CHANGE_FEATURES"; + features: string[]; + promptEnvRestart: string | null; + } + | { + type: "CHANGE_THREADS"; + threads: string[]; + } + | { + type: "CHANGE_OBJDIRS"; + objdirs: string[]; + } + | { + type: "INITIALIZE_STORE"; + isSupportedPlatform: boolean; + presets: Presets; + pageContext: PageContext; + openRemoteDevTools?: () => void; + supportedFeatures: string[]; + } + | { + type: "CHANGE_PRESET"; + presetName: string; + preset: PresetDefinition | undefined; + } + | { + type: "UPDATE_SETTINGS_FROM_PREFERENCES"; + recordingSettingsFromPreferences: RecordingSettings; + }; + +export interface InitializeStoreValues { + isSupportedPlatform: boolean; + presets: Presets; + pageContext: PageContext; + supportedFeatures: string[]; + openRemoteDevTools?: () => void; +} + +export type PopupBackgroundFeatures = { [feature: string]: boolean }; + +// TS-TODO - Stub +export interface ContentFrameMessageManager { + addMessageListener: (event: string, listener: (event: any) => void) => void; + addEventListener: (event: string, listener: (event: any) => void) => void; + sendAsyncMessage: (name: string, data: any) => void; +} + +/** + * This interface serves as documentation for all of the prefs used by the + * performance-new client. Each preference access string access can be coerced to + * one of the properties of this interface. + */ +export interface PerformancePref { + /** + * The recording preferences by default are controlled by different presets. + * This pref stores that preset. + */ + Preset: "devtools.performance.recording.preset"; + /** + * Stores the total number of entries to be used in the profile buffer. + */ + Entries: "devtools.performance.recording.entries"; + /** + * The recording interval, stored in microseconds. Note that the StartProfiler + * interface uses milliseconds, but this lets us store higher precision numbers + * inside of an integer preference store. + */ + Interval: "devtools.performance.recording.interval"; + /** + * The features enabled for the profiler, stored as a comma-separated list. + */ + Features: "devtools.performance.recording.features"; + /** + * The threads to profile, stored as a comma-separated list. + */ + Threads: "devtools.performance.recording.threads"; + /** + * The location of the objdirs to use, stored as a comma-separated list. + */ + ObjDirs: "devtools.performance.recording.objdirs"; + /** + * The duration of the profiling window to use in seconds. Setting this to 0 + * will cause no profile window to be used, and the values will naturally roll + * off from the profiling buffer. + * + * This is currently not hooked up to any UI. See Bug 1587165. + */ + Duration: "devtools.performance.recording.duration"; + /** + * Normally this defaults to https://profiler.firefox.com, but this can be overridden + * to point the profiler to a different URL, such as http://localhost:4242/ for + * local development workflows. + */ + UIBaseUrl: "devtools.performance.recording.ui-base-url"; + /** + * This pref allows tests to override the /from-browser in order to more easily + * test the profile injection mechanism. + */ + UIBaseUrlPathPref: "devtools.performance.recording.ui-base-url-path"; + /** + * This controls whether we enable the active tab view when capturing in web + * developer preset. + * We're not enabling the active-tab view in all environments until we + * iron out all its issues. + */ + UIEnableActiveTabView: "devtools.performance.recording.active-tab-view.enabled"; + /** + * The profiler popup has some introductory text explaining what it is the first + * time that you open it. After that, it is not displayed by default. + */ + PopupIntroDisplayed: "devtools.performance.popup.intro-displayed"; + /** + * This preference is used outside of the performance-new type system + * (in DevToolsStartup). It toggles the availability of the profiler menu + * button in the customization palette. + */ + PopupFeatureFlag: "devtools.performance.popup.feature-flag"; +} + +/* The next 2 types bring some duplication from gecko.d.ts, but this is simpler + * this way. */ + +/** + * This is a function called by a preference observer. + */ +export type PrefObserverFunction = ( + aSubject: nsIPrefBranch, + aTopic: "nsPref:changed", + aData: string +) => unknown; + +/** + * This is the type of an observer we can pass to Service.prefs.addObserver and + * Service.prefs.removeObserver. + */ +export type PrefObserver = + | PrefObserverFunction + | { observe: PrefObserverFunction }; + +/** + * Scale a number value. + */ +export type NumberScaler = (value: number) => number; + +/** + * A collection of functions to scale numbers. + */ +export interface ScaleFunctions { + fromFractionToValue: NumberScaler; + fromValueToFraction: NumberScaler; + fromFractionToSingleDigitValue: NumberScaler; + steps: number; +} + +/** + * View mode for the Firefox Profiler front-end timeline. + * `undefined` is defaulted to full automatically. + */ +export type ProfilerViewMode = "full" | "active-tab" | "origins"; + +export interface PresetDefinition { + entries: number; + interval: number; + features: string[]; + threads: string[]; + duration: number; + profilerViewMode?: ProfilerViewMode; + l10nIds: { + popup: { + label: string; + description: string; + }; + devtools: { + label: string; + description: string; + }; + }; +} + +export interface Presets { + [presetName: string]: PresetDefinition; +} + +// Should be kept in sync with the types in https://github.com/firefox-devtools/profiler/blob/main/src/app-logic/web-channel.js . +// Compatibility is handled as follows: +// - The front-end needs to worry about compatibility and handle older browser versions. +// - The browser can require the latest front-end version and does not need to keep any legacy functionality for older front-end versions. + +type MessageFromFrontend = { + requestId: number; +} & RequestFromFrontend; + +export type RequestFromFrontend = + | StatusQueryRequest + | EnableMenuButtonRequest + | GetProfileRequest + | GetExternalPowerTracksRequest + | GetSymbolTableRequest + | QuerySymbolicationApiRequest; + +type StatusQueryRequest = { type: "STATUS_QUERY" }; +type EnableMenuButtonRequest = { type: "ENABLE_MENU_BUTTON" }; +type GetProfileRequest = { type: "GET_PROFILE" }; +type GetExternalPowerTracksRequest = { + type: "GET_EXTERNAL_POWER_TRACKS", + startTime: number, + endTime: number, +}; +type GetSymbolTableRequest = { + type: "GET_SYMBOL_TABLE"; + debugName: string; + breakpadId: string; +}; +type QuerySymbolicationApiRequest = { + type: "QUERY_SYMBOLICATION_API"; + path: string; + requestJson: string; +}; + +export type MessageToFrontend<R> = + | OutOfBandErrorMessageToFrontend + | ErrorResponseMessageToFrontend + | SuccessResponseMessageToFrontend<R>; + +type OutOfBandErrorMessageToFrontend = { + errno: number; + error: string; +}; + +type ErrorResponseMessageToFrontend = { + type: "ERROR_RESPONSE"; + requestId: number; + error: string; +}; + +type SuccessResponseMessageToFrontend<R> = { + type: "SUCCESS_RESPONSE"; + requestId: number; + response: R; +}; + +export type ResponseToFrontend = + | StatusQueryResponse + | EnableMenuButtonResponse + | GetProfileResponse + | GetExternalPowerTracksResponse + | GetSymbolTableResponse + | QuerySymbolicationApiResponse; + +type StatusQueryResponse = { + menuButtonIsEnabled: boolean; + // The version indicates which message types are supported by the browser. + // No version: + // Shipped in Firefox 76. + // Supports the following message types: + // - STATUS_QUERY + // - ENABLE_MENU_BUTTON + // Version 1: + // Shipped in Firefox 93. + // Adds support for the following message types: + // - GET_PROFILE + // - GET_SYMBOL_TABLE + // - QUERY_SYMBOLICATION_API + // Version 2: + // Shipped in Firefox 121. + // Adds support for the following message types: + // - GET_EXTERNAL_POWER_TRACKS + version: number; +}; +type EnableMenuButtonResponse = void; +type GetProfileResponse = ArrayBuffer | MinimallyTypedGeckoProfile; +type GetExternalPowerTracksResponse = Array<object>; +type GetSymbolTableResponse = SymbolTableAsTuple; +type QuerySymbolicationApiResponse = string; + +/** + * This represents an event channel that can talk to a content page on the web. + * This interface is a manually typed version of toolkit/modules/WebChannel.sys.mjs + * and is opinionated about the types of messages we can send with it. + * + * The definition is here rather than gecko.d.ts because it was simpler than getting + * generics working with the ChromeUtils.import machinery. + */ +export class ProfilerWebChannel { + constructor(id: string, url: MockedExports.nsIURI); + send: ( + message: MessageToFrontend<ResponseToFrontend>, + target: MockedExports.WebChannelTarget + ) => void; + listen: ( + handler: ( + idle: string, + message: MessageFromFrontend, + target: MockedExports.WebChannelTarget + ) => void + ) => void; +} + +/** + * The per-tab information that is stored when a new profile is captured + * and a profiler tab is opened, to serve the correct profile to the tab + * that sends the WebChannel message. + */ +export type ProfilerBrowserInfo = { + profileCaptureResult: ProfileCaptureResult; + symbolicationService: SymbolicationService; +}; + +export type ProfileCaptureResult = + | { + type: "SUCCESS"; + profile: MinimallyTypedGeckoProfile | ArrayBuffer; + } + | { + type: "ERROR"; + error: Error; + }; + +/** + * Describes all of the profiling features that can be turned on and + * off in about:profiling. + */ +export interface FeatureDescription { + // The name of the feature as shown in the UI. + name: string; + // The key value of the feature, this will be stored in prefs, and used in the + // nsiProfiler interface. + value: string; + // The full description of the preset, this will need to be localized. + title: string; + // This will give the user a hint that it's recommended on. + recommended?: boolean; + // This will give the user a hint that it's an experimental feature. + experimental?: boolean; + // This will give a reason if the feature is disabled. + disabledReason?: string; +} + +// The key has the shape `${debugName}:${breakpadId}`. +export type LibInfoMapKey = string; + +// This is a subset of the full Library struct. +export type LibInfoMapValue = { + name: string; + path: string; + debugName: string; + debugPath: string; + breakpadId: string; + arch: string; +}; + +export type SymbolicationWorkerInitialMessage = { + request: SymbolicationWorkerRequest; + // A map that allows looking up library info based on debugName + breakpadId. + // This is rather redundant at the moment, but it will make more sense once + // we can request symbols for multiple different libraries with one worker + // message. + libInfoMap: Map<LibInfoMapKey, LibInfoMapValue>; + // An array of objdir paths on the host machine that should be searched for + // relevant build artifacts. + objdirs: string[]; + // The profiler-get-symbols wasm module. + module: WebAssembly.Module; +}; + +export type SymbolicationWorkerRequest = + | { + type: "GET_SYMBOL_TABLE"; + // The debugName of the binary whose symbols should be obtained. + debugName: string; + // The breakpadId for the binary whose symbols should be obtained. + breakpadId: string; + } + | { + type: "QUERY_SYMBOLICATION_API"; + // The API entry path, such as "/symbolicate/v5". + path: string; + // The payload JSON, as a string. + requestJson: string; + }; + +export type SymbolicationWorkerError = { + name: string; + message: string; + fileName?: string; + lineNumber?: number; +}; + +export type SymbolicationWorkerReplyData<R> = + | { + result: R; + } + | { + error: SymbolicationWorkerError; + }; + +// This type is used in the symbolication worker for the return type of the +// FileAndPathHelper's readFile method. +// FIXME: Or rather, this type *would* be used if the worker code was checked +// by TypeScript. +export interface FileHandle { + // Return the length of the file in bytes. + getLength: () => number; + // Synchronously read the bytes at offset `offset` into the array `dest`. + readBytesInto: (dest: Uint8Array, offset: number) => void; + // Called when the file is no longer needed, to allow closing the file. + drop: () => void; +} |