chrome-devtools-frontend
Version:
Chrome DevTools UI
477 lines (387 loc) • 23.9 kB
text/typescript
// Copyright 2011 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import * as Common from '../common/common.js';
import {InspectorFrontendHostInstance} from './InspectorFrontendHost.js';
import {EnumeratedHistogram} from './InspectorFrontendHostAPI.js';
import * as Enums from './UserMetricsEnums.js';
export class UserMetrics {
sourcesPanelFileDebugged(mediaType?: string): void {
const code = (mediaType && MediaTypes[mediaType as keyof typeof MediaTypes]) || MediaTypes.Unknown;
InspectorFrontendHostInstance.recordEnumeratedHistogram(EnumeratedHistogram.SourcesPanelFileDebugged, code,
MediaTypes.MAX_VALUE);
}
sourcesPanelFileOpened(mediaType?: string): void {
const code = (mediaType && MediaTypes[mediaType as keyof typeof MediaTypes]) || MediaTypes.Unknown;
InspectorFrontendHostInstance.recordEnumeratedHistogram(EnumeratedHistogram.SourcesPanelFileOpened, code,
MediaTypes.MAX_VALUE);
}
networkPanelResponsePreviewOpened(mediaType: string): void {
const code = (mediaType && MediaTypes[mediaType as keyof typeof MediaTypes]) || MediaTypes.Unknown;
InspectorFrontendHostInstance.recordEnumeratedHistogram(EnumeratedHistogram.NetworkPanelResponsePreviewOpened, code,
MediaTypes.MAX_VALUE);
}
actionTaken(action: Action): void {
InspectorFrontendHostInstance.recordEnumeratedHistogram(EnumeratedHistogram.ActionTaken, action, Action.MAX_VALUE);
}
resendRequest(resourceType: ResendRequestType): void {
InspectorFrontendHostInstance.recordEnumeratedHistogram(EnumeratedHistogram.ResendRequest, resourceType,
ResendRequestType.MAX_VALUE);
}
keybindSetSettingChanged(keybindSet: string): void {
const value = KeybindSetSettings[keybindSet as keyof typeof KeybindSetSettings] || 0;
InspectorFrontendHostInstance.recordEnumeratedHistogram(EnumeratedHistogram.KeybindSetSettingChanged, value,
KeybindSetSettings.MAX_VALUE);
}
keyboardShortcutFired(actionId: string): void {
const action =
KeyboardShortcutAction[actionId as keyof typeof KeyboardShortcutAction] || KeyboardShortcutAction.OtherShortcut;
InspectorFrontendHostInstance.recordEnumeratedHistogram(EnumeratedHistogram.KeyboardShortcutFired, action,
KeyboardShortcutAction.MAX_VALUE);
}
issuesPanelOpenedFrom(issueOpener: IssueOpener): void {
InspectorFrontendHostInstance.recordEnumeratedHistogram(EnumeratedHistogram.IssuesPanelOpenedFrom, issueOpener,
IssueOpener.MAX_VALUE);
}
issuesPanelIssueExpanded(issueExpandedCategory?: string): void {
if (issueExpandedCategory === undefined) {
return;
}
const issueExpanded = IssueExpanded[issueExpandedCategory as keyof typeof IssueExpanded];
if (issueExpanded === undefined) {
return;
}
InspectorFrontendHostInstance.recordEnumeratedHistogram(EnumeratedHistogram.IssuesPanelIssueExpanded, issueExpanded,
IssueExpanded.MAX_VALUE);
}
issuesPanelResourceOpened(issueCategory: string, type: string): void {
const key = issueCategory + type;
const value = IssueResourceOpened[key as keyof typeof IssueResourceOpened];
if (value === undefined) {
return;
}
InspectorFrontendHostInstance.recordEnumeratedHistogram(EnumeratedHistogram.IssuesPanelResourceOpened, value,
IssueResourceOpened.MAX_VALUE);
}
issueCreated(code: string): void {
const issueCreated = IssueCreated[code as keyof typeof IssueCreated];
if (issueCreated === undefined) {
return;
}
InspectorFrontendHostInstance.recordEnumeratedHistogram(EnumeratedHistogram.IssueCreated, issueCreated,
IssueCreated.MAX_VALUE);
}
experimentEnabledAtLaunch(experimentId: string): void {
const experiment = DevtoolsExperiments[experimentId as keyof typeof DevtoolsExperiments];
if (experiment === undefined) {
return;
}
InspectorFrontendHostInstance.recordEnumeratedHistogram(EnumeratedHistogram.ExperimentEnabledAtLaunch, experiment,
DevtoolsExperiments.MAX_VALUE);
}
navigationSettingAtFirstTimelineLoad(state: TimelineNavigationSetting): void {
InspectorFrontendHostInstance.recordEnumeratedHistogram(EnumeratedHistogram.TimelineNavigationSettingState, state,
TimelineNavigationSetting.MAX_VALUE);
}
experimentDisabledAtLaunch(experimentId: string): void {
const experiment = DevtoolsExperiments[experimentId as keyof typeof DevtoolsExperiments];
if (experiment === undefined) {
return;
}
InspectorFrontendHostInstance.recordEnumeratedHistogram(EnumeratedHistogram.ExperimentDisabledAtLaunch, experiment,
DevtoolsExperiments.MAX_VALUE);
}
experimentChanged(experimentId: string, isEnabled: boolean): void {
const experiment = DevtoolsExperiments[experimentId as keyof typeof DevtoolsExperiments];
if (experiment === undefined) {
return;
}
const actionName = isEnabled ? EnumeratedHistogram.ExperimentEnabled : EnumeratedHistogram.ExperimentDisabled;
InspectorFrontendHostInstance.recordEnumeratedHistogram(actionName, experiment, DevtoolsExperiments.MAX_VALUE);
}
developerResourceLoaded(developerResourceLoaded: DeveloperResourceLoaded): void {
if (developerResourceLoaded >= DeveloperResourceLoaded.MAX_VALUE) {
return;
}
InspectorFrontendHostInstance.recordEnumeratedHistogram(EnumeratedHistogram.DeveloperResourceLoaded,
developerResourceLoaded, DeveloperResourceLoaded.MAX_VALUE);
}
developerResourceScheme(developerResourceScheme: DeveloperResourceScheme): void {
if (developerResourceScheme >= DeveloperResourceScheme.MAX_VALUE) {
return;
}
InspectorFrontendHostInstance.recordEnumeratedHistogram(EnumeratedHistogram.DeveloperResourceScheme,
developerResourceScheme, DeveloperResourceScheme.MAX_VALUE);
}
language(language: Intl.UnicodeBCP47LocaleIdentifier): void {
const languageCode = Language[language as keyof typeof Language];
if (languageCode === undefined) {
return;
}
InspectorFrontendHostInstance.recordEnumeratedHistogram(EnumeratedHistogram.Language, languageCode,
Language.MAX_VALUE);
}
syncSetting(devtoolsSyncSettingEnabled: boolean): void {
InspectorFrontendHostInstance.getSyncInformation(syncInfo => {
let settingValue = SyncSetting.CHROME_SYNC_DISABLED;
if (syncInfo.isSyncActive && !syncInfo.arePreferencesSynced) {
settingValue = SyncSetting.CHROME_SYNC_SETTINGS_DISABLED;
} else if (syncInfo.isSyncActive && syncInfo.arePreferencesSynced) {
settingValue = devtoolsSyncSettingEnabled ? SyncSetting.DEVTOOLS_SYNC_SETTING_ENABLED :
SyncSetting.DEVTOOLS_SYNC_SETTING_DISABLED;
}
InspectorFrontendHostInstance.recordEnumeratedHistogram(EnumeratedHistogram.SyncSetting, settingValue,
SyncSetting.MAX_VALUE);
});
}
recordingToggled(value: RecordingToggled): void {
InspectorFrontendHostInstance.recordEnumeratedHistogram(EnumeratedHistogram.RecordingToggled, value,
RecordingToggled.MAX_VALUE);
}
recordingReplayFinished(value: RecordingReplayFinished): void {
InspectorFrontendHostInstance.recordEnumeratedHistogram(EnumeratedHistogram.RecordingReplayFinished, value,
RecordingReplayFinished.MAX_VALUE);
}
recordingReplayStarted(value: RecordingReplayStarted): void {
InspectorFrontendHostInstance.recordEnumeratedHistogram(EnumeratedHistogram.RecordingReplayStarted, value,
RecordingReplayStarted.MAX_VALUE);
}
lighthouseModeRun(type: LighthouseModeRun): void {
InspectorFrontendHostInstance.recordEnumeratedHistogram(EnumeratedHistogram.LighthouseModeRun, type,
LighthouseModeRun.MAX_VALUE);
}
lighthouseCategoryUsed(type: LighthouseCategoryUsed): void {
InspectorFrontendHostInstance.recordEnumeratedHistogram(EnumeratedHistogram.LighthouseCategoryUsed, type,
LighthouseCategoryUsed.MAX_VALUE);
}
swatchActivated(swatch: SwatchType): void {
InspectorFrontendHostInstance.recordEnumeratedHistogram(EnumeratedHistogram.SwatchActivated, swatch,
SwatchType.MAX_VALUE);
}
workspacesPopulated(wallClockTimeInMilliseconds: number): void {
InspectorFrontendHostInstance.recordPerformanceHistogram('DevTools.Workspaces.PopulateWallClocktime',
wallClockTimeInMilliseconds);
}
visualLoggingProcessingDone(timeInMilliseconds: number): void {
InspectorFrontendHostInstance.recordPerformanceHistogram('DevTools.VisualLogging.ProcessingTime',
timeInMilliseconds);
}
freestylerQueryLength(numberOfCharacters: number): void {
InspectorFrontendHostInstance.recordCountHistogram('DevTools.Freestyler.QueryLength', numberOfCharacters, 0,
100_000, 100);
}
freestylerEvalResponseSize(bytes: number): void {
InspectorFrontendHostInstance.recordCountHistogram('DevTools.Freestyler.EvalResponseSize', bytes, 0, 100_000, 100);
}
builtInAiAvailability(availability: BuiltInAiAvailability): void {
InspectorFrontendHostInstance.recordEnumeratedHistogram(EnumeratedHistogram.BuiltInAiAvailability, availability,
BuiltInAiAvailability.MAX_VALUE);
}
consoleInsightTeaserGenerated(timeInMilliseconds: number): void {
InspectorFrontendHostInstance.recordPerformanceHistogram('DevTools.Insights.TeaserGenerationTime',
timeInMilliseconds);
}
consoleInsightTeaserGeneratedMedium(timeInMilliseconds: number): void {
InspectorFrontendHostInstance.recordPerformanceHistogramMedium('DevTools.Insights.TeaserGenerationTimeMedium',
timeInMilliseconds);
}
consoleInsightTeaserFirstChunkGenerated(timeInMilliseconds: number): void {
InspectorFrontendHostInstance.recordPerformanceHistogram('DevTools.Insights.TeaserFirstChunkGenerationTime',
timeInMilliseconds);
}
consoleInsightTeaserFirstChunkGeneratedMedium(timeInMilliseconds: number): void {
InspectorFrontendHostInstance.recordPerformanceHistogramMedium(
'DevTools.Insights.TeaserFirstChunkGenerationTimeMedium', timeInMilliseconds);
}
consoleInsightTeaserChunkToEndMedium(timeInMilliseconds: number): void {
InspectorFrontendHostInstance.recordPerformanceHistogramMedium('DevTools.Insights.TeaserChunkToEndMedium',
timeInMilliseconds);
}
consoleInsightTeaserAbortedAfterFirstCharacter(timeInMilliseconds: number): void {
InspectorFrontendHostInstance.recordPerformanceHistogram('DevTools.Insights.TeaserAfterFirstCharacterAbortionTime',
timeInMilliseconds);
}
consoleInsightTeaserAbortedBeforeFirstCharacter(timeInMilliseconds: number): void {
InspectorFrontendHostInstance.recordPerformanceHistogram('DevTools.Insights.TeaserBeforeFirstCharacterAbortionTime',
timeInMilliseconds);
}
consoleInsightLongTeaserGenerated(timeInMilliseconds: number): void {
InspectorFrontendHostInstance.recordPerformanceHistogram('DevTools.Insights.LongTeaserGenerationTime',
timeInMilliseconds);
}
consoleInsightShortTeaserGenerated(timeInMilliseconds: number): void {
InspectorFrontendHostInstance.recordPerformanceHistogram('DevTools.Insights.ShortTeaserGenerationTime',
timeInMilliseconds);
}
}
/**
* Creates a proxy that delays the resolution of UMA enum values until runtime.
* We do this to decouple the UMA metrics from the TypeScript compilation.
* This ensures that when DevTools is used with remote debugging or as a
* custom frontend, the enum boundary values exactly match what the host
* Chrome binary expects (which is provided via devtools_compatibility.js).
*/
function createDynamicEnumProxy<T extends object>(enumName: string, fallbackEnum: T): T {
return new Proxy(fallbackEnum, {
get(_target, prop) {
if (typeof prop === 'symbol') {
return Reflect.get(fallbackEnum, prop);
}
const metrics =
// eslint-disable-next-line @typescript-eslint/naming-convention
(globalThis as {DevToolsMetrics?: Record<string, Record<string, number|string>>}).DevToolsMetrics;
const enumObj = metrics && metrics[enumName];
if (enumObj && prop in enumObj) {
return enumObj[prop as keyof typeof enumObj];
}
// Support reverse lookups for numeric values.
if (typeof prop === 'string' && /^\d+$/.test(prop)) {
const value = Number(prop);
for (const [key, val] of Object.entries(enumObj || {})) {
if (val === value) {
return key as keyof T;
}
}
}
// Fallback to the compile-time TypeScript enum if DevToolsMetrics is missing
return Reflect.get(fallbackEnum, prop);
},
has(_target, prop) {
const metrics =
// eslint-disable-next-line @typescript-eslint/naming-convention
(globalThis as {DevToolsMetrics?: Record<string, Record<string, number|string>>}).DevToolsMetrics;
const enumObj = metrics && metrics[enumName];
if (enumObj && prop in enumObj) {
return true;
}
if (typeof prop === 'string' && /^\d+$/.test(prop)) {
return Object.values(enumObj || {}).includes(Number(prop));
}
return Reflect.has(fallbackEnum, prop);
},
ownKeys(_target) {
const metrics =
// eslint-disable-next-line @typescript-eslint/naming-convention
(globalThis as {DevToolsMetrics?: Record<string, Record<string, number|string>>}).DevToolsMetrics;
const enumObj = metrics && metrics[enumName];
return enumObj ? Reflect.ownKeys(enumObj) : Reflect.ownKeys(fallbackEnum);
},
getOwnPropertyDescriptor(_target, prop) {
const metrics =
// eslint-disable-next-line @typescript-eslint/naming-convention
(globalThis as {DevToolsMetrics?: Record<string, Record<string, number|string>>}).DevToolsMetrics;
const enumObj = metrics && metrics[enumName];
if (!enumObj) {
return Reflect.getOwnPropertyDescriptor(fallbackEnum, prop);
}
return Reflect.getOwnPropertyDescriptor(enumObj, prop);
},
}) as T;
}
export type Action = Enums.Action;
export const Action = createDynamicEnumProxy<typeof Enums.Action>('Action', Enums.Action);
export type PanelCodes = Enums.PanelCodes;
export const PanelCodes = createDynamicEnumProxy<typeof Enums.PanelCodes>('PanelCodes', Enums.PanelCodes);
export type MediaTypes = Enums.MediaTypes;
export const MediaTypes = createDynamicEnumProxy<typeof Enums.MediaTypes>('MediaTypes', Enums.MediaTypes);
export type KeybindSetSettings = Enums.KeybindSetSettings;
export const KeybindSetSettings =
createDynamicEnumProxy<typeof Enums.KeybindSetSettings>('KeybindSetSettings', Enums.KeybindSetSettings);
export type KeyboardShortcutAction = Enums.KeyboardShortcutAction;
export const KeyboardShortcutAction =
createDynamicEnumProxy<typeof Enums.KeyboardShortcutAction>('KeyboardShortcutAction', Enums.KeyboardShortcutAction);
export type IssueOpener = Enums.IssueOpener;
export const IssueOpener = createDynamicEnumProxy<typeof Enums.IssueOpener>('IssueOpener', Enums.IssueOpener);
export type DevtoolsExperiments = Enums.DevtoolsExperiments;
export const DevtoolsExperiments =
createDynamicEnumProxy<typeof Enums.DevtoolsExperiments>('DevtoolsExperiments', Enums.DevtoolsExperiments);
export type IssueExpanded = Enums.IssueExpanded;
export const IssueExpanded = createDynamicEnumProxy<typeof Enums.IssueExpanded>('IssueExpanded', Enums.IssueExpanded);
export type IssueResourceOpened = Enums.IssueResourceOpened;
export const IssueResourceOpened =
createDynamicEnumProxy<typeof Enums.IssueResourceOpened>('IssueResourceOpened', Enums.IssueResourceOpened);
export type IssueCreated = Enums.IssueCreated;
export const IssueCreated = createDynamicEnumProxy<typeof Enums.IssueCreated>('IssueCreated', Enums.IssueCreated);
export type DeveloperResourceLoaded = Enums.DeveloperResourceLoaded;
export const DeveloperResourceLoaded = createDynamicEnumProxy<typeof Enums.DeveloperResourceLoaded>(
'DeveloperResourceLoaded', Enums.DeveloperResourceLoaded);
export type DeveloperResourceScheme = Enums.DeveloperResourceScheme;
export const DeveloperResourceScheme = createDynamicEnumProxy<typeof Enums.DeveloperResourceScheme>(
'DeveloperResourceScheme', Enums.DeveloperResourceScheme);
export type Language = Enums.Language;
export const Language = createDynamicEnumProxy<typeof Enums.Language>('Language', Enums.Language);
export type SyncSetting = Enums.SyncSetting;
export const SyncSetting = createDynamicEnumProxy<typeof Enums.SyncSetting>('SyncSetting', Enums.SyncSetting);
export type RecordingToggled = Enums.RecordingToggled;
export const RecordingToggled =
createDynamicEnumProxy<typeof Enums.RecordingToggled>('RecordingToggled', Enums.RecordingToggled);
export type RecordingAssertion = Enums.RecordingAssertion;
export const RecordingAssertion =
createDynamicEnumProxy<typeof Enums.RecordingAssertion>('RecordingAssertion', Enums.RecordingAssertion);
export type RecordingReplayFinished = Enums.RecordingReplayFinished;
export const RecordingReplayFinished = createDynamicEnumProxy<typeof Enums.RecordingReplayFinished>(
'RecordingReplayFinished', Enums.RecordingReplayFinished);
export type RecordingReplaySpeed = Enums.RecordingReplaySpeed;
export const RecordingReplaySpeed =
createDynamicEnumProxy<typeof Enums.RecordingReplaySpeed>('RecordingReplaySpeed', Enums.RecordingReplaySpeed);
export type RecordingReplayStarted = Enums.RecordingReplayStarted;
export const RecordingReplayStarted =
createDynamicEnumProxy<typeof Enums.RecordingReplayStarted>('RecordingReplayStarted', Enums.RecordingReplayStarted);
export type RecordingEdited = Enums.RecordingEdited;
export const RecordingEdited =
createDynamicEnumProxy<typeof Enums.RecordingEdited>('RecordingEdited', Enums.RecordingEdited);
export type RecordingExported = Enums.RecordingExported;
export const RecordingExported =
createDynamicEnumProxy<typeof Enums.RecordingExported>('RecordingExported', Enums.RecordingExported);
export type RecordingCodeToggled = Enums.RecordingCodeToggled;
export const RecordingCodeToggled =
createDynamicEnumProxy<typeof Enums.RecordingCodeToggled>('RecordingCodeToggled', Enums.RecordingCodeToggled);
export type RecordingCopiedToClipboard = Enums.RecordingCopiedToClipboard;
export const RecordingCopiedToClipboard = createDynamicEnumProxy<typeof Enums.RecordingCopiedToClipboard>(
'RecordingCopiedToClipboard', Enums.RecordingCopiedToClipboard);
export type ManifestSectionCodes = Enums.ManifestSectionCodes;
export const ManifestSectionCodes =
createDynamicEnumProxy<typeof Enums.ManifestSectionCodes>('ManifestSectionCodes', Enums.ManifestSectionCodes);
export type LighthouseModeRun = Enums.LighthouseModeRun;
export const LighthouseModeRun =
createDynamicEnumProxy<typeof Enums.LighthouseModeRun>('LighthouseModeRun', Enums.LighthouseModeRun);
export type LighthouseCategoryUsed = Enums.LighthouseCategoryUsed;
export const LighthouseCategoryUsed =
createDynamicEnumProxy<typeof Enums.LighthouseCategoryUsed>('LighthouseCategoryUsed', Enums.LighthouseCategoryUsed);
export type SwatchType = Enums.SwatchType;
export const SwatchType = createDynamicEnumProxy<typeof Enums.SwatchType>('SwatchType', Enums.SwatchType);
export type BadgeType = Enums.BadgeType;
export const BadgeType = createDynamicEnumProxy<typeof Enums.BadgeType>('BadgeType', Enums.BadgeType);
export type AnimationsPlaybackRate = Enums.AnimationsPlaybackRate;
export const AnimationsPlaybackRate =
createDynamicEnumProxy<typeof Enums.AnimationsPlaybackRate>('AnimationsPlaybackRate', Enums.AnimationsPlaybackRate);
export type TimelineNavigationSetting = Enums.TimelineNavigationSetting;
export const TimelineNavigationSetting = createDynamicEnumProxy<typeof Enums.TimelineNavigationSetting>(
'TimelineNavigationSetting', Enums.TimelineNavigationSetting);
export type BuiltInAiAvailability = Enums.BuiltInAiAvailability;
export const BuiltInAiAvailability =
createDynamicEnumProxy<typeof Enums.BuiltInAiAvailability>('BuiltInAiAvailability', Enums.BuiltInAiAvailability);
export type ResendRequestType = Enums.ResendRequestType;
export const ResendRequestType =
createDynamicEnumProxy<typeof Enums.ResendRequestType>('ResendRequestType', Enums.ResendRequestType);
const resendRequestTypeMap = new Map<Common.ResourceType.ResourceType, keyof typeof Enums.ResendRequestType>([
[Common.ResourceType.resourceTypes.XHR, 'XHR'],
[Common.ResourceType.resourceTypes.Fetch, 'FETCH'],
[Common.ResourceType.resourceTypes.Script, 'SCRIPT'],
[Common.ResourceType.resourceTypes.Stylesheet, 'STYLESHEET'],
[Common.ResourceType.resourceTypes.Image, 'IMAGE'],
[Common.ResourceType.resourceTypes.Media, 'MEDIA'],
[Common.ResourceType.resourceTypes.Font, 'FONT'],
[Common.ResourceType.resourceTypes.Wasm, 'WASM'],
[Common.ResourceType.resourceTypes.Manifest, 'MANIFEST'],
[Common.ResourceType.resourceTypes.TextTrack, 'TEXT_TRACK'],
[Common.ResourceType.resourceTypes.SourceMapScript, 'SOURCE_MAP_SCRIPT'],
[Common.ResourceType.resourceTypes.SourceMapStyleSheet, 'SOURCE_MAP_STYLE_SHEET'],
[Common.ResourceType.resourceTypes.Document, 'DOCUMENT'],
[Common.ResourceType.resourceTypes.Prefetch, 'PREFETCH'],
[Common.ResourceType.resourceTypes.Ping, 'PING'],
]);
export function resendRequestType(resourceType: Common.ResourceType.ResourceType): ResendRequestType {
const key = resendRequestTypeMap.get(resourceType);
return (key ? ResendRequestType[key] : undefined) ?? ResendRequestType.OTHER;
}