UNPKG

@quick-game/cli

Version:

Command line interface for rapid qg development

250 lines 12.1 kB
// Copyright 2021 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. import * as Host from '../../core/host/host.js'; import * as i18n from '../../core/i18n/i18n.js'; import * as SDK from '../../core/sdk/sdk.js'; import * as SourceMapScopes from '../../models/source_map_scopes/source_map_scopes.js'; import * as LinearMemoryInspector from '../../ui/components/linear_memory_inspector/linear_memory_inspector.js'; import * as ObjectUI from '../../ui/legacy/components/object_ui/object_ui.js'; import * as Components from '../../ui/legacy/components/utils/utils.js'; import * as UI from '../../ui/legacy/legacy.js'; import scopeChainSidebarPaneStyles from './scopeChainSidebarPane.css.js'; const UIStrings = { /** *@description Loading indicator in Scope Sidebar Pane of the Sources panel */ loading: 'Loading...', /** *@description Not paused message element text content in Call Stack Sidebar Pane of the Sources panel */ notPaused: 'Not paused', /** *@description Empty placeholder in Scope Chain Sidebar Pane of the Sources panel */ noVariables: 'No variables', /** *@description Text in the Sources panel Scope pane describing a closure scope. *@example {func} PH1 */ closureS: 'Closure ({PH1})', /** *@description Text that refers to closure as a programming term */ closure: 'Closure', /** *@description Text in Scope Chain Sidebar Pane of the Sources panel */ exception: 'Exception', /** *@description Text in Scope Chain Sidebar Pane of the Sources panel */ returnValue: 'Return value', /** *@description A context menu item in the Scope View of the Sources Panel */ revealInMemoryInspectorPanel: 'Reveal in Memory Inspector panel', }; const str_ = i18n.i18n.registerUIStrings('panels/sources/ScopeChainSidebarPane.ts', UIStrings); const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_); let scopeChainSidebarPaneInstance; export class ScopeChainSidebarPane extends UI.Widget.VBox { treeOutline; expandController; linkifier; infoElement; #scopesScript = null; constructor() { super(true); this.treeOutline = new ObjectUI.ObjectPropertiesSection.ObjectPropertiesSectionsTreeOutline(); this.treeOutline.setShowSelectionOnKeyboardFocus(/* show */ true); this.expandController = new ObjectUI.ObjectPropertiesSection.ObjectPropertiesSectionsTreeExpandController(this.treeOutline); this.linkifier = new Components.Linkifier.Linkifier(); this.infoElement = document.createElement('div'); this.infoElement.className = 'gray-info-message'; this.infoElement.tabIndex = -1; SDK.TargetManager.TargetManager.instance().addModelListener(SDK.DebuggerModel.DebuggerModel, SDK.DebuggerModel.Events.DebugInfoAttached, this.debugInfoAttached, this); void this.update(); } static instance() { if (!scopeChainSidebarPaneInstance) { scopeChainSidebarPaneInstance = new ScopeChainSidebarPane(); } return scopeChainSidebarPaneInstance; } flavorChanged(_object) { void this.update(); } focus() { if (this.hasFocus()) { return; } if (UI.Context.Context.instance().flavor(SDK.DebuggerModel.DebuggerPausedDetails)) { this.treeOutline.forceSelect(); } } sourceMapAttached(event) { if (event.data.client === this.#scopesScript) { void this.update(); } } setScopeSourceMapSubscription(callFrame) { const oldScript = this.#scopesScript; this.#scopesScript = callFrame?.script ?? null; // Shortcut for the case when we are listening to the same model. if (oldScript?.debuggerModel === this.#scopesScript?.debuggerModel) { return; } if (oldScript) { oldScript.debuggerModel.sourceMapManager().removeEventListener(SDK.SourceMapManager.Events.SourceMapAttached, this.sourceMapAttached, this); } if (this.#scopesScript) { this.#scopesScript.debuggerModel.sourceMapManager().addEventListener(SDK.SourceMapManager.Events.SourceMapAttached, this.sourceMapAttached, this); } } debugInfoAttached(event) { if (event.data === this.#scopesScript) { void this.update(); } } async update() { // The `resolveThisObject(callFrame)` and `resolveScopeChain(callFrame)` calls // below may take a while to complete, so indicate to the user that something // is happening (see https://crbug.com/1162416). this.infoElement.textContent = i18nString(UIStrings.loading); this.contentElement.removeChildren(); this.contentElement.appendChild(this.infoElement); this.linkifier.reset(); const callFrame = UI.Context.Context.instance().flavor(SDK.DebuggerModel.CallFrame); this.setScopeSourceMapSubscription(callFrame); const [thisObject, scopeChain] = await Promise.all([ SourceMapScopes.NamesResolver.resolveThisObject(callFrame), SourceMapScopes.NamesResolver.resolveScopeChain(callFrame), ]); // By now the developer might have moved on, and we don't want to show stale // scope information, so check again that we're still on the same CallFrame. if (callFrame === UI.Context.Context.instance().flavor(SDK.DebuggerModel.CallFrame)) { const details = UI.Context.Context.instance().flavor(SDK.DebuggerModel.DebuggerPausedDetails); this.treeOutline.removeChildren(); if (!details || !callFrame || !scopeChain) { this.infoElement.textContent = i18nString(UIStrings.notPaused); return; } this.contentElement.removeChildren(); this.contentElement.appendChild(this.treeOutline.element); let foundLocalScope = false; for (let i = 0; i < scopeChain.length; ++i) { const scope = scopeChain[i]; const extraProperties = this.extraPropertiesForScope(scope, details, callFrame, thisObject, i === 0); if (scope.type() === "local" /* Protocol.Debugger.ScopeType.Local */) { foundLocalScope = true; } const section = this.createScopeSectionTreeElement(scope, extraProperties); if (scope.type() === "global" /* Protocol.Debugger.ScopeType.Global */) { section.collapse(); } else if (!foundLocalScope || scope.type() === "local" /* Protocol.Debugger.ScopeType.Local */) { section.expand(); } this.treeOutline.appendChild(section); if (i === 0) { section.select(/* omitFocus */ true); } } this.sidebarPaneUpdatedForTest(); } } createScopeSectionTreeElement(scope, extraProperties) { let emptyPlaceholder = null; if (scope.type() === "local" /* Protocol.Debugger.ScopeType.Local */ || "closure" /* Protocol.Debugger.ScopeType.Closure */) { emptyPlaceholder = i18nString(UIStrings.noVariables); } let title = scope.typeName(); if (scope.type() === "closure" /* Protocol.Debugger.ScopeType.Closure */) { const scopeName = scope.name(); if (scopeName) { title = i18nString(UIStrings.closureS, { PH1: UI.UIUtils.beautifyFunctionName(scopeName) }); } else { title = i18nString(UIStrings.closure); } } let subtitle = scope.description(); if (!title || title === subtitle) { subtitle = null; } const icon = scope.icon(); const titleElement = document.createElement('div'); titleElement.classList.add('scope-chain-sidebar-pane-section-header'); titleElement.classList.add('tree-element-title'); if (icon) { const iconElement = document.createElement('img'); iconElement.classList.add('scope-chain-sidebar-pane-section-icon'); iconElement.src = icon; titleElement.appendChild(iconElement); } titleElement.createChild('div', 'scope-chain-sidebar-pane-section-subtitle').textContent = subtitle; titleElement.createChild('div', 'scope-chain-sidebar-pane-section-title').textContent = title; const section = new ObjectUI.ObjectPropertiesSection.RootElement(SourceMapScopes.NamesResolver.resolveScopeInObject(scope), this.linkifier, emptyPlaceholder, 0 /* ObjectUI.ObjectPropertiesSection.ObjectPropertiesMode.All */, extraProperties); section.title = titleElement; section.listItemElement.classList.add('scope-chain-sidebar-pane-section'); section.listItemElement.setAttribute('aria-label', title); this.expandController.watchSection(title + (subtitle ? ':' + subtitle : ''), section); return section; } extraPropertiesForScope(scope, details, callFrame, thisObject, isFirstScope) { if (scope.type() !== "local" /* Protocol.Debugger.ScopeType.Local */ || callFrame.script.isWasm()) { return []; } const extraProperties = []; if (thisObject) { extraProperties.push(new SDK.RemoteObject.RemoteObjectProperty('this', thisObject, undefined, undefined, undefined, undefined, undefined, /* synthetic */ true)); } if (isFirstScope) { const exception = details.exception(); if (exception) { extraProperties.push(new SDK.RemoteObject.RemoteObjectProperty(i18nString(UIStrings.exception), exception, undefined, undefined, undefined, undefined, undefined, /* synthetic */ true)); } const returnValue = callFrame.returnValue(); if (returnValue) { extraProperties.push(new SDK.RemoteObject.RemoteObjectProperty(i18nString(UIStrings.returnValue), returnValue, undefined, undefined, undefined, undefined, undefined, /* synthetic */ true, callFrame.setReturnValue.bind(callFrame))); } } return extraProperties; } sidebarPaneUpdatedForTest() { } wasShown() { super.wasShown(); this.treeOutline.registerCSSFiles([scopeChainSidebarPaneStyles]); this.registerCSSFiles([scopeChainSidebarPaneStyles]); } } let openLinearMemoryInspectorInstance; export class OpenLinearMemoryInspector extends UI.Widget.VBox { static instance(opts = { forceNew: null }) { const { forceNew } = opts; if (!openLinearMemoryInspectorInstance || forceNew) { openLinearMemoryInspectorInstance = new OpenLinearMemoryInspector(); } return openLinearMemoryInspectorInstance; } appendApplicableItems(event, contextMenu, target) { if (target instanceof ObjectUI.ObjectPropertiesSection.ObjectPropertyTreeElement) { if (target.property && target.property.value && LinearMemoryInspector.LinearMemoryInspectorController.isMemoryObjectProperty(target.property.value)) { const expression = target.path(); contextMenu.debugSection().appendItem(i18nString(UIStrings.revealInMemoryInspectorPanel), this.openMemoryInspector.bind(this, expression, target.property.value)); } } } async openMemoryInspector(expression, obj) { const controller = LinearMemoryInspector.LinearMemoryInspectorController.LinearMemoryInspectorController.instance(); Host.userMetrics.linearMemoryInspectorRevealedFrom(Host.UserMetrics.LinearMemoryInspectorRevealedFrom.ContextMenu); void controller.openInspectorView(obj, /* address */ undefined, expression); } } //# sourceMappingURL=ScopeChainSidebarPane.js.map