chrome-devtools-frontend
Version:
Chrome DevTools UI
247 lines (215 loc) • 10 kB
text/typescript
// Copyright (c) 2020 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 type * as Platform from '../../core/platform/platform.js';
import * as UI from '../../ui/legacy/legacy.js';
import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
import * as LinearMemoryInspectorComponents from './components/components.js';
import {type LazyUint8Array, LinearMemoryInspectorController} from './LinearMemoryInspectorController.js';
const UIStrings = {
/**
*@description Label in the Linear Memory inspector tool that serves as a placeholder if no inspections are open (i.e. nothing to see here).
* Inspection hereby refers to viewing, navigating and understanding the memory through this tool.
*/
noOpenInspections: 'No open inspections',
/**
*@description Label in the Linear Memory inspector tool that serves as a placeholder if no inspections are open (i.e. nothing to see here).
* Inspection hereby refers to viewing, navigating and understanding the memory through this tool.
*/
memoryInspectorExplanation: 'On this page you can inspect binary data.',
/**
*@description Label in the Linear Memory inspector tool for a link.
*/
learnMore: 'Learn more',
} as const;
const str_ = i18n.i18n.registerUIStrings('panels/linear_memory_inspector/LinearMemoryInspectorPane.ts', UIStrings);
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
let inspectorInstance: LinearMemoryInspectorPane;
const MEMORY_INSPECTOR_EXPLANATION_URL =
'https://developer.chrome.com/docs/devtools/memory-inspector' as Platform.DevToolsPath.UrlString;
export class LinearMemoryInspectorPane extends Common.ObjectWrapper.eventMixin<EventTypes, typeof UI.Widget.VBox>(
UI.Widget.VBox) {
readonly #tabbedPane: UI.TabbedPane.TabbedPane;
constructor() {
super(false);
this.element.setAttribute('jslog', `${VisualLogging.panel('linear-memory-inspector').track({resize: true})}`);
this.#tabbedPane = new UI.TabbedPane.TabbedPane();
this.#tabbedPane.setPlaceholderElement(this.createPlaceholder());
this.#tabbedPane.setCloseableTabs(true);
this.#tabbedPane.setAllowTabReorder(true, true);
this.#tabbedPane.addEventListener(UI.TabbedPane.Events.TabClosed, this.#tabClosed, this);
this.#tabbedPane.show(this.contentElement);
this.#tabbedPane.headerElement().setAttribute(
'jslog', `${VisualLogging.toolbar().track({keydown: 'ArrowUp|ArrowLeft|ArrowDown|ArrowRight|Enter|Space'})}`);
}
createPlaceholder(): HTMLElement {
const placeholder = document.createElement('div');
placeholder.classList.add('empty-state');
placeholder.createChild('span', 'empty-state-header').textContent = i18nString(UIStrings.noOpenInspections);
const description = placeholder.createChild('div', 'empty-state-description');
description.createChild('span').textContent = i18nString(UIStrings.memoryInspectorExplanation);
const link = UI.XLink.XLink.create(
MEMORY_INSPECTOR_EXPLANATION_URL, i18nString(UIStrings.learnMore), undefined, undefined, 'learn-more');
description.appendChild(link);
return placeholder;
}
static instance(): LinearMemoryInspectorPane {
if (!inspectorInstance) {
inspectorInstance = new LinearMemoryInspectorPane();
}
return inspectorInstance;
}
#tabView(tabId: string): LinearMemoryInspectorView {
const view = this.#tabbedPane.tabView(tabId);
if (view === null) {
throw new Error(`No linear memory inspector view for the given tab id: ${tabId}`);
}
return view as LinearMemoryInspectorView;
}
create(tabId: string, title: string, arrayWrapper: LazyUint8Array, address?: number): void {
const inspectorView = new LinearMemoryInspectorView(arrayWrapper, address, tabId);
this.#tabbedPane.appendTab(tabId, title, inspectorView, undefined, false, true);
this.#tabbedPane.selectTab(tabId);
}
close(tabId: string): void {
this.#tabbedPane.closeTab(tabId, false);
}
reveal(tabId: string, address?: number): void {
const view = this.#tabView(tabId);
if (address !== undefined) {
view.updateAddress(address);
}
this.refreshView(tabId);
this.#tabbedPane.selectTab(tabId);
}
refreshView(tabId: string): void {
const view = this.#tabView(tabId);
view.refreshData();
}
#tabClosed(event: Common.EventTarget.EventTargetEvent<UI.TabbedPane.EventData>): void {
const {tabId} = event.data;
this.dispatchEventToListeners(Events.VIEW_CLOSED, tabId);
}
}
export const enum Events {
VIEW_CLOSED = 'ViewClosed',
}
export interface EventTypes {
[Events.VIEW_CLOSED]: string;
}
export class LinearMemoryInspectorView extends UI.Widget.VBox {
#memoryWrapper: LazyUint8Array;
#address: number;
#tabId: string;
#inspector: LinearMemoryInspectorComponents.LinearMemoryInspector.LinearMemoryInspector;
firstTimeOpen: boolean;
readonly #hideValueInspector: boolean;
constructor(
memoryWrapper: LazyUint8Array, address: number|undefined = 0, tabId: string, hideValueInspector?: boolean) {
super(false);
if (address < 0 || address >= memoryWrapper.length()) {
throw new Error('Requested address is out of bounds.');
}
this.#memoryWrapper = memoryWrapper;
this.#address = address;
this.#tabId = tabId;
this.#hideValueInspector = Boolean(hideValueInspector);
this.#inspector = new LinearMemoryInspectorComponents.LinearMemoryInspector.LinearMemoryInspector();
this.#inspector.addEventListener(
LinearMemoryInspectorComponents.LinearMemoryInspector.MemoryRequestEvent.eventName,
(event: LinearMemoryInspectorComponents.LinearMemoryInspector.MemoryRequestEvent) => {
this.#memoryRequested(event);
});
this.#inspector.addEventListener(
LinearMemoryInspectorComponents.LinearMemoryInspector.AddressChangedEvent.eventName,
(event: LinearMemoryInspectorComponents.LinearMemoryInspector.AddressChangedEvent) => {
this.updateAddress(event.data);
});
this.#inspector.addEventListener(
LinearMemoryInspectorComponents.LinearMemoryInspector.SettingsChangedEvent.eventName,
(event: LinearMemoryInspectorComponents.LinearMemoryInspector.SettingsChangedEvent) => {
// Stop event from bubbling up, since no element further up needs the event.
event.stopPropagation();
this.saveSettings(event.data);
});
this.#inspector.addEventListener(
LinearMemoryInspectorComponents.LinearMemoryHighlightChipList.DeleteMemoryHighlightEvent.eventName,
(event: LinearMemoryInspectorComponents.LinearMemoryHighlightChipList.DeleteMemoryHighlightEvent) => {
LinearMemoryInspectorController.instance().removeHighlight(this.#tabId, event.data);
this.refreshData();
});
this.contentElement.appendChild(this.#inspector);
this.firstTimeOpen = true;
}
override wasShown(): void {
this.refreshData();
}
saveSettings(settings: LinearMemoryInspectorComponents.LinearMemoryInspector.Settings): void {
LinearMemoryInspectorController.instance().saveSettings(settings);
}
updateAddress(address: number): void {
if (address < 0 || address >= this.#memoryWrapper.length()) {
throw new Error('Requested address is out of bounds.');
}
this.#address = address;
}
refreshData(): void {
void LinearMemoryInspectorController.getMemoryForAddress(this.#memoryWrapper, this.#address).then(({
memory,
offset,
}) => {
let valueTypes;
let valueTypeModes;
let endianness;
if (this.firstTimeOpen) {
const settings = LinearMemoryInspectorController.instance().loadSettings();
valueTypes = settings.valueTypes;
valueTypeModes = settings.modes;
endianness = settings.endianness;
this.firstTimeOpen = false;
}
this.#inspector.data = {
memory,
address: this.#address,
memoryOffset: offset,
outerMemoryLength: this.#memoryWrapper.length(),
valueTypes,
valueTypeModes,
endianness,
highlightInfo: this.#getHighlightInfo(),
hideValueInspector: this.#hideValueInspector,
};
});
}
#memoryRequested(event: LinearMemoryInspectorComponents.LinearMemoryInspector.MemoryRequestEvent): void {
const {start, end, address} = event.data;
if (address < start || address >= end) {
throw new Error('Requested address is out of bounds.');
}
void LinearMemoryInspectorController.getMemoryRange(this.#memoryWrapper, start, end).then(memory => {
this.#inspector.data = {
memory,
address,
memoryOffset: start,
outerMemoryLength: this.#memoryWrapper.length(),
highlightInfo: this.#getHighlightInfo(),
hideValueInspector: this.#hideValueInspector,
};
});
}
#getHighlightInfo(): LinearMemoryInspectorComponents.LinearMemoryViewerUtils.HighlightInfo|undefined {
const highlightInfo = LinearMemoryInspectorController.instance().getHighlightInfo(this.#tabId);
if (highlightInfo !== undefined) {
if (highlightInfo.startAddress < 0 || highlightInfo.startAddress >= this.#memoryWrapper.length()) {
throw new Error('HighlightInfo start address is out of bounds.');
}
if (highlightInfo.size <= 0) {
throw new Error('Highlight size must be a positive number.');
}
}
return highlightInfo;
}
}