UNPKG

chrome-devtools-frontend

Version:
252 lines (222 loc) • 9.31 kB
// 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-lit-render-outside-of-view */ import '../../../ui/components/icon_button/icon_button.js'; import * as i18n from '../../../core/i18n/i18n.js'; import * as UI from '../../../ui/legacy/legacy.js'; import * as Lit from '../../../ui/lit/lit.js'; import * as VisualLogging from '../../../ui/visual_logging/visual_logging.js'; import valueInterpreterDisplayStyles from './valueInterpreterDisplay.css.js'; import { Endianness, format, getDefaultValueTypeMapping, getPointerAddress, isNumber, isPointer, isValidMode, VALUE_TYPE_MODE_LIST, ValueType, ValueTypeMode, } from './ValueInterpreterDisplayUtils.js'; const UIStrings = { /** *@description Tooltip text that appears when hovering over an unsigned interpretation of the memory under the Value Interpreter */ unsignedValue: '`Unsigned` value', /** *@description Tooltip text that appears when hovering over the element to change value type modes of under the Value Interpreter. Value type modes * are different ways of viewing a certain value, e.g.: 10 (decimal) can be 0xa in hexadecimal mode, or 12 in octal mode. */ changeValueTypeMode: 'Change mode', /** *@description Tooltip text that appears when hovering over a signed interpretation of the memory under the Value Interpreter */ signedValue: '`Signed` value', /** *@description Tooltip text that appears when hovering over a 'jump-to-address' button that is next to a pointer (32-bit or 64-bit) under the Value Interpreter */ jumpToPointer: 'Jump to address', /** *@description Tooltip text that appears when hovering over a 'jump-to-address' button that is next to a pointer (32-bit or 64-bit) with an invalid address under the Value Interpreter. */ addressOutOfRange: 'Address out of memory range', } as const; const str_ = i18n.i18n.registerUIStrings('panels/linear_memory_inspector/components/ValueInterpreterDisplay.ts', UIStrings); const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_); const {render, html} = Lit; const SORTED_VALUE_TYPES = Array.from(getDefaultValueTypeMapping().keys()); export interface ValueDisplayData { buffer: ArrayBuffer; valueTypes: Set<ValueType>; endianness: Endianness; memoryLength: number; valueTypeModes?: Map<ValueType, ValueTypeMode>; } export class ValueTypeModeChangedEvent extends Event { static readonly eventName = 'valuetypemodechanged'; data: {type: ValueType, mode: ValueTypeMode}; constructor(type: ValueType, mode: ValueTypeMode) { super(ValueTypeModeChangedEvent.eventName, { composed: true, }); this.data = {type, mode}; } } export class JumpToPointerAddressEvent extends Event { static readonly eventName = 'jumptopointeraddress'; data: number; constructor(address: number) { super(JumpToPointerAddressEvent.eventName, { composed: true, }); this.data = address; } } export class ValueInterpreterDisplay extends HTMLElement { readonly #shadow = this.attachShadow({mode: 'open'}); #endianness = Endianness.LITTLE; #buffer = new ArrayBuffer(0); #valueTypes = new Set<ValueType>(); #valueTypeModeConfig: Map<ValueType, ValueTypeMode> = getDefaultValueTypeMapping(); #memoryLength = 0; set data(data: ValueDisplayData) { this.#buffer = data.buffer; this.#endianness = data.endianness; this.#valueTypes = data.valueTypes; this.#memoryLength = data.memoryLength; if (data.valueTypeModes) { data.valueTypeModes.forEach((mode, valueType) => { if (isValidMode(valueType, mode)) { this.#valueTypeModeConfig.set(valueType, mode); } }); } this.#render(); } #render(): void { // Disabled until https://crbug.com/1079231 is fixed. // clang-format off render(html` <style>${UI.inspectorCommonStyles}</style> <style>${valueInterpreterDisplayStyles}</style> <div class="value-types"> ${SORTED_VALUE_TYPES.map(type => this.#valueTypes.has(type) ? this.#showValue(type) : '')} </div> `, this.#shadow, {host: this}, ); // clang-format on } #showValue(type: ValueType): Lit.TemplateResult { if (isNumber(type)) { return this.#renderNumberValues(type); } if (isPointer(type)) { return this.#renderPointerValue(type); } throw new Error(`No known way to format ${type}`); } #renderPointerValue(type: ValueType): Lit.TemplateResult { const unsignedValue = this.#parse({type, signed: false}); const address = getPointerAddress(type, this.#buffer, this.#endianness); const jumpDisabled = Number.isNaN(address) || BigInt(address) >= BigInt(this.#memoryLength); const buttonTitle = jumpDisabled ? i18nString(UIStrings.addressOutOfRange) : i18nString(UIStrings.jumpToPointer); const iconColor = jumpDisabled ? 'var(--icon-default)' : 'var(--icon-link)'; // Disabled until https://crbug.com/1079231 is fixed. // clang-format off return html` <span class="value-type-cell-no-mode value-type-cell selectable-text">${i18n.i18n.lockedString(type)}</span> <div class="value-type-cell"> <div class="value-type-value-with-link" data-value="true"> <span class="selectable-text">${unsignedValue}</span> ${ html` <button class="jump-to-button" data-jump="true" title=${buttonTitle} ?disabled=${jumpDisabled} jslog=${VisualLogging.action('linear-memory-inspector.jump-to-address').track({click: true})} @click=${this.#onJumpToAddressClicked.bind(this, Number(address))}> <devtools-icon .data=${ {iconName: 'open-externally', color: iconColor, width: '16px'}}> </devtools-icon> </button>`} </div> </div> `; // clang-format on } #onJumpToAddressClicked(address: number): void { this.dispatchEvent(new JumpToPointerAddressEvent(address)); } #renderNumberValues(type: ValueType): Lit.TemplateResult { // Disabled until https://crbug.com/1079231 is fixed. // clang-format off return html` <span class="value-type-cell selectable-text">${i18n.i18n.lockedString(type)}</span> <div> <select title=${i18nString(UIStrings.changeValueTypeMode)} data-mode-settings="true" jslog=${VisualLogging.dropDown('linear-memory-inspector.value-type-mode').track({change: true})} @change=${this.#onValueTypeModeChange.bind(this, type)}> ${VALUE_TYPE_MODE_LIST.filter(x => isValidMode(type, x)).map(mode => { return html` <option value=${mode} .selected=${this.#valueTypeModeConfig.get(type) === mode} jslog=${VisualLogging.item(mode).track({click: true})}>${ i18n.i18n.lockedString(mode)} </option>`; })} </select> </div> ${this.#renderSignedAndUnsigned(type)} `; // clang-format on } #renderSignedAndUnsigned(type: ValueType): Lit.TemplateResult { const unsignedValue = this.#parse({type, signed: false}); const signedValue = this.#parse({type, signed: true}); const mode = this.#valueTypeModeConfig.get(type); const showSignedAndUnsigned = signedValue !== unsignedValue && mode !== ValueTypeMode.HEXADECIMAL && mode !== ValueTypeMode.OCTAL; const unsignedRendered = html`<span class="value-type-cell selectable-text" title=${ i18nString(UIStrings.unsignedValue)} data-value="true">${unsignedValue}</span>`; if (!showSignedAndUnsigned) { return unsignedRendered; } // Some values are too long to show in one line, we're putting them into the next line. const showInMultipleLines = type === ValueType.INT32 || type === ValueType.INT64; const signedRendered = html`<span class="selectable-text" data-value="true" title=${ i18nString(UIStrings.signedValue)}>${signedValue}</span>`; if (showInMultipleLines) { return html` <div class="value-type-cell"> ${unsignedRendered} ${signedRendered} </div> `; } return html` <div class="value-type-cell" style="flex-direction: row;"> ${unsignedRendered} <span class="signed-divider"></span> ${signedRendered} </div> `; } #onValueTypeModeChange(type: ValueType, event: Event): void { event.preventDefault(); const select = event.target as HTMLInputElement; const mode = select.value as ValueTypeMode; this.dispatchEvent(new ValueTypeModeChangedEvent(type, mode)); } #parse(data: {type: ValueType, signed?: boolean}): string { const mode = this.#valueTypeModeConfig.get(data.type); return format( {buffer: this.#buffer, type: data.type, endianness: this.#endianness, signed: data.signed || false, mode}); } } customElements.define('devtools-linear-memory-inspector-interpreter-display', ValueInterpreterDisplay); declare global { interface HTMLElementTagNameMap { 'devtools-linear-memory-inspector-interpreter-display': ValueInterpreterDisplay; } }