UNPKG

chrome-devtools-frontend

Version:
194 lines (163 loc) 7.24 kB
// Copyright 2024 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. /* eslint-disable rulesdir/no-imperative-dom-api */ import * as Common from '../../core/common/common.js'; import * as i18n from '../../core/i18n/i18n.js'; import * as SDK from '../../core/sdk/sdk.js'; import type * as Protocol from '../../generated/protocol.js'; import type * as DataGrid from '../../ui/legacy/components/data_grid/data_grid.js'; import * as UI from '../../ui/legacy/legacy.js'; import {HeapDetachedElementsDataGrid, HeapDetachedElementsDataGridNode} from './HeapDetachedElementsDataGrid.js'; import { type DataDisplayDelegate, ProfileEvents as ProfileTypeEvents, type ProfileHeader, ProfileType, } from './ProfileHeader.js'; import {WritableProfileHeader} from './ProfileView.js'; const UIStrings = { /** *@description Button text to obtain the detached elements retained by JS */ startDetachedElements: 'Obtain detached elements', /** *@description The title for the collection of profiles that are gathered from various snapshots of the heap, using a sampling (e.g. every 1/100) technique. */ detachedElementsTitle: 'Detached elements', /** *@description Description in Heap Profile View of a profiler tool */ detachedElementsDescription: 'Detached elements shows objects that are retained by a JS reference.', /** *@description Name of a profile *@example {2} PH1 */ detachedElementProfile: 'Detached elements {PH1}', } as const; const str_ = i18n.i18n.registerUIStrings('panels/profiler/HeapDetachedElementsView.ts', UIStrings); const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_); export class DetachedElementsProfileView extends UI.View.SimpleView implements DataDisplayDelegate { readonly selectedSizeText: UI.Toolbar.ToolbarText; dataGrid: DataGrid.DataGrid.DataGridImpl<unknown>; profile: DetachedElementsProfileHeader; readonly parentDataDisplayDelegate: DataDisplayDelegate; constructor(dataDisplayDelegate: DataDisplayDelegate, profile: DetachedElementsProfileHeader) { super(i18nString(UIStrings.detachedElementsTitle)); this.element.classList.add('detached-elements-view'); this.profile = profile; this.parentDataDisplayDelegate = dataDisplayDelegate; this.selectedSizeText = new UI.Toolbar.ToolbarText(); this.dataGrid = new HeapDetachedElementsDataGrid(); this.populateElementsGrid(profile.detachedElements); this.dataGrid.asWidget().show(this.element); } showProfile(profile: ProfileHeader|null): UI.Widget.Widget|null { return this.parentDataDisplayDelegate.showProfile(profile); } showObject(objectId: string, perspectiveName: string): void { this.parentDataDisplayDelegate.showObject(objectId, perspectiveName); } async linkifyObject(): Promise<Element|null> { return null; } populateElementsGrid(detachedElements: Protocol.DOM.DetachedElementInfo[]|null): void { if (!detachedElements) { return; } const heapProfilerModel = this.profile.heapProfilerModel(); const domModel = heapProfilerModel?.target().model(SDK.DOMModel.DOMModel); if (!domModel) { return; } for (const detachedElement of detachedElements) { this.dataGrid.rootNode().appendChild(new HeapDetachedElementsDataGridNode(detachedElement, domModel)); } } override async toolbarItems(): Promise<UI.Toolbar.ToolbarItem[]> { return [...await super.toolbarItems(), this.selectedSizeText]; } } export class DetachedElementsProfileType extends Common.ObjectWrapper.eventMixin<DetachedElementsProfileType.EventTypes, typeof ProfileType>(ProfileType) { constructor(typeId?: string, description?: string) { super( typeId || i18nString(UIStrings.detachedElementsTitle), description || i18nString(UIStrings.detachedElementsTitle)); } override profileBeingRecorded(): DetachedElementsProfileHeader|null { return super.profileBeingRecorded() as DetachedElementsProfileHeader | null; } override get buttonTooltip(): Common.UIString.LocalizedString { return i18nString(UIStrings.startDetachedElements); } override buttonClicked(): boolean { void this.getDetachedElements(); return false; } async getDetachedElements(): Promise<void> { if (this.profileBeingRecorded()) { return; } const heapProfilerModel = UI.Context.Context.instance().flavor(SDK.HeapProfilerModel.HeapProfilerModel); const target = heapProfilerModel?.target(); const domModel = target?.model(SDK.DOMModel.DOMModel); if (!heapProfilerModel || !target || !domModel) { return; } const animationModel = target?.model(SDK.AnimationModel.AnimationModel); if (animationModel) { // TODO(b/406904348): Remove this once we correctly release animations on the backend. await animationModel.releaseAllAnimations(); } const data = await domModel.getDetachedDOMNodes(); const profile: DetachedElementsProfileHeader = new DetachedElementsProfileHeader(heapProfilerModel, this, data); this.addProfile(profile); this.dispatchEventToListeners(ProfileTypeEvents.PROFILE_COMPLETE, profile); } override get treeItemTitle(): Common.UIString.LocalizedString { return i18nString(UIStrings.detachedElementsTitle); } override get description(): string { return i18nString(UIStrings.detachedElementsDescription); } override isInstantProfile(): boolean { return true; } // eslint-disable-next-line @typescript-eslint/naming-convention static readonly TypeId = 'DetachedElements'; } export namespace DetachedElementsProfileType { export const enum Events { RECORDING_STOPPED = 'RecordingStopped', STATS_UPDATE = 'StatsUpdate', DETACHED_ELEMENTS_OBTAINED = 'DetachedElementsObtained', } export interface EventTypes { [Events.RECORDING_STOPPED]: void; [Events.STATS_UPDATE]: Protocol.HeapProfiler.SamplingHeapProfile|null; [Events.DETACHED_ELEMENTS_OBTAINED]: Protocol.DOM.DetachedElementInfo[]|null; } } export class DetachedElementsProfileHeader extends WritableProfileHeader { readonly heapProfilerModelInternal: SDK.HeapProfilerModel.HeapProfilerModel|null; readonly detachedElements: Protocol.DOM.DetachedElementInfo[]|null; constructor( heapProfilerModel: SDK.HeapProfilerModel.HeapProfilerModel|null, type: DetachedElementsProfileType, detachedElements: Protocol.DOM.DetachedElementInfo[]|null, title?: string) { super( heapProfilerModel?.debuggerModel() ?? null, type, title || i18nString(UIStrings.detachedElementProfile, {PH1: type.nextProfileUid()})); this.detachedElements = detachedElements; this.heapProfilerModelInternal = heapProfilerModel; } override createView(dataDisplayDelegate: DataDisplayDelegate): DetachedElementsProfileView { return new DetachedElementsProfileView(dataDisplayDelegate, this); } heapProfilerModel(): SDK.HeapProfilerModel.HeapProfilerModel|null { return this.heapProfilerModelInternal; } override profileType(): DetachedElementsProfileType { return super.profileType() as DetachedElementsProfileType; } }