blob: da74a2d3590a2eaad3e8349ac662919f3eaeb799 [file] [log] [blame]
Jan Scheffler35199b92021-03-17 09:51:151// Copyright 2021 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Blink Reformat4c46d092018-04-07 15:32:375/*
6 * Copyright (C) IBM Corp. 2009 All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
10 * met:
11 *
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * * Redistributions in binary form must reproduce the above
15 * copyright notice, this list of conditions and the following disclaimer
16 * in the documentation and/or other materials provided with the
17 * distribution.
18 * * Neither the name of IBM Corp. nor the names of its
19 * contributors may be used to endorse or promote products derived from
20 * this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 */
Tim van der Lippe8987f8f2020-01-03 15:03:1634
Tim van der Lippe021c7572021-04-19 10:49:4335import * as Common from '../../core/common/common.js';
36import * as Host from '../../core/host/host.js';
37import * as i18n from '../../core/i18n/i18n.js';
38import * as Platform from '../../core/platform/platform.js';
Jaroslav Sevcik9b05a522022-11-30 15:43:2139import * as Root from '../../core/root/root.js';
Tim van der Lippe021c7572021-04-19 10:49:4340import * as SDK from '../../core/sdk/sdk.js';
Simon Zünd5e6e1f82023-11-23 10:55:3141import type * as Protocol from '../../generated/protocol.js';
Jaroslav Sevcik9b05a522022-11-30 15:43:2142import * as Formatter from '../../models/formatter/formatter.js';
43import * as SourceMapScopes from '../../models/source_map_scopes/source_map_scopes.js';
Tim van der Lippe021c7572021-04-19 10:49:4344import * as ObjectUI from '../../ui/legacy/components/object_ui/object_ui.js';
Kriti Sapraae8f17e2021-08-18 10:28:0245// eslint-disable-next-line rulesdir/es_modules_import
46import objectValueStyles from '../../ui/legacy/components/object_ui/objectValue.css.js';
Tim van der Lippe339ad262021-04-21 12:23:3647import * as Components from '../../ui/legacy/components/utils/utils.js';
Tim van der Lippe021c7572021-04-19 10:49:4348import * as UI from '../../ui/legacy/legacy.js';
Simon Zünd5e6e1f82023-11-23 10:55:3149import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
Tim van der Lippefbbf9812020-02-13 14:43:4650
Paul Lewis39944952020-01-22 15:45:1851import {UISourceCodeFrame} from './UISourceCodeFrame.js';
Simon Zünd5e6e1f82023-11-23 10:55:3152import watchExpressionsSidebarPaneStyles from './watchExpressionsSidebarPane.css.js';
Paul Lewis39944952020-01-22 15:45:1853
Simon Zünd697fb0b2021-03-01 10:12:4254const UIStrings = {
Vidal Guillermo Diazleal Ortega83edb472021-02-16 18:39:3255 /**
Jack Franklinfd72c072022-12-21 11:45:0156 *@description A context menu item in the Watch Expressions Sidebar Pane of the Sources panel
57 */
Vidal Guillermo Diazleal Ortega83edb472021-02-16 18:39:3258 addWatchExpression: 'Add watch expression',
59 /**
Jack Franklinfd72c072022-12-21 11:45:0160 *@description Tooltip/screen reader label of a button in the Sources panel that refreshes all watch expressions.
61 */
Vidal Guillermo Diazleal Ortega83edb472021-02-16 18:39:3262 refreshWatchExpressions: 'Refresh watch expressions',
63 /**
Jack Franklinfd72c072022-12-21 11:45:0164 *@description Empty element text content in Watch Expressions Sidebar Pane of the Sources panel
65 */
Vidal Guillermo Diazleal Ortega83edb472021-02-16 18:39:3266 noWatchExpressions: 'No watch expressions',
67 /**
Jack Franklinfd72c072022-12-21 11:45:0168 *@description A context menu item in the Watch Expressions Sidebar Pane of the Sources panel
69 */
Vidal Guillermo Diazleal Ortega83edb472021-02-16 18:39:3270 deleteAllWatchExpressions: 'Delete all watch expressions',
71 /**
Jack Franklinfd72c072022-12-21 11:45:0172 *@description A context menu item in the Watch Expressions Sidebar Pane of the Sources panel
73 */
Vidal Guillermo Diazleal Ortega83edb472021-02-16 18:39:3274 addPropertyPathToWatch: 'Add property path to watch',
75 /**
Jack Franklinfd72c072022-12-21 11:45:0176 *@description A context menu item in the Watch Expressions Sidebar Pane of the Sources panel
77 */
Vidal Guillermo Diazleal Ortega83edb472021-02-16 18:39:3278 deleteWatchExpression: 'Delete watch expression',
79 /**
Jack Franklinfd72c072022-12-21 11:45:0180 *@description Value element text content in Watch Expressions Sidebar Pane of the Sources panel
81 */
Vidal Guillermo Diazleal Ortega83edb472021-02-16 18:39:3282 notAvailable: '<not available>',
83 /**
Jack Franklinfd72c072022-12-21 11:45:0184 *@description A context menu item in the Watch Expressions Sidebar Pane of the Sources panel and Network pane request.
85 */
Vidal Guillermo Diazleal Ortega83edb472021-02-16 18:39:3286 copyValue: 'Copy value',
87};
Tim van der Lippe021c7572021-04-19 10:49:4388const str_ = i18n.i18n.registerUIStrings('panels/sources/WatchExpressionsSidebarPane.ts', UIStrings);
Vidal Guillermo Diazleal Ortega83edb472021-02-16 18:39:3289const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
Jan Scheffler35199b92021-03-17 09:51:1590let watchExpressionsSidebarPaneInstance: WatchExpressionsSidebarPane;
Andres Olivaresfd431fb2020-12-10 15:30:0491
Jan Scheffler35199b92021-03-17 09:51:1592export class WatchExpressionsSidebarPane extends UI.ThrottledWidget.ThrottledWidget implements
93 UI.ActionRegistration.ActionDelegate, UI.Toolbar.ItemsProvider, UI.ContextMenu.Provider {
Jan Schefflera222c632021-08-13 12:46:1594 private watchExpressions: WatchExpression[];
95 private emptyElement!: HTMLElement;
96 private readonly watchExpressionsSetting: Common.Settings.Setting<string[]>;
97 private readonly addButton: UI.Toolbar.ToolbarButton;
98 private readonly refreshButton: UI.Toolbar.ToolbarButton;
99 private readonly treeOutline: ObjectUI.ObjectPropertiesSection.ObjectPropertiesSectionsTreeOutline;
100 private readonly expandController: ObjectUI.ObjectPropertiesSection.ObjectPropertiesSectionsTreeExpandController;
101 private readonly linkifier: Components.Linkifier.Linkifier;
Jan Scheffler35199b92021-03-17 09:51:15102 private constructor() {
Blink Reformat4c46d092018-04-07 15:32:37103 super(true);
Blink Reformat4c46d092018-04-07 15:32:37104
Simon Zünd0243c602020-03-10 06:51:42105 // TODO(szuend): Replace with a Set once the web test
Tim van der Lippe021c7572021-04-19 10:49:43106 // panels/sources/debugger-ui/watch-expressions-preserve-expansion.js is either converted
Jan Scheffler35199b92021-03-17 09:51:15107 // to an e2e test or no longer accesses this variable directly.
Jan Schefflera222c632021-08-13 12:46:15108 this.watchExpressions = [];
109 this.watchExpressionsSetting =
Sigurd Schneider8641b422021-07-16 06:13:35110 Common.Settings.Settings.instance().createLocalSetting<string[]>('watchExpressions', []);
Blink Reformat4c46d092018-04-07 15:32:37111
Benedikt Meurer7d5cc372023-04-12 12:20:41112 this.addButton = new UI.Toolbar.ToolbarButton(i18nString(UIStrings.addWatchExpression), 'plus');
Jan Schefflera222c632021-08-13 12:46:15113 this.addButton.addEventListener(UI.Toolbar.ToolbarButton.Events.Click, _event => {
Tim van der Lippe2d9a95c2022-01-04 15:18:03114 void this.addButtonClicked();
Tim van der Lippe37a35ff2020-03-03 13:49:02115 });
Benedikt Meurer32476232023-04-11 13:19:58116 this.refreshButton = new UI.Toolbar.ToolbarButton(i18nString(UIStrings.refreshWatchExpressions), 'refresh');
Jan Schefflera222c632021-08-13 12:46:15117 this.refreshButton.addEventListener(UI.Toolbar.ToolbarButton.Events.Click, this.update, this);
Blink Reformat4c46d092018-04-07 15:32:37118
119 this.contentElement.classList.add('watch-expressions');
Simon Zünd5e6e1f82023-11-23 10:55:31120 this.contentElement.setAttribute('jslog', `${VisualLogging.pane().context('debugger-watch')}`);
Jan Schefflera222c632021-08-13 12:46:15121 this.contentElement.addEventListener('contextmenu', this.contextMenu.bind(this), false);
122 this.treeOutline = new ObjectUI.ObjectPropertiesSection.ObjectPropertiesSectionsTreeOutline();
Kriti Sapraae8f17e2021-08-18 10:28:02123
Jan Schefflera222c632021-08-13 12:46:15124 this.treeOutline.setShowSelectionOnKeyboardFocus(/* show */ true);
125 this.expandController =
126 new ObjectUI.ObjectPropertiesSection.ObjectPropertiesSectionsTreeExpandController(this.treeOutline);
Blink Reformat4c46d092018-04-07 15:32:37127
Tim van der Lipped1a00aa2020-08-19 16:03:56128 UI.Context.Context.instance().addFlavorChangeListener(SDK.RuntimeModel.ExecutionContext, this.update, this);
129 UI.Context.Context.instance().addFlavorChangeListener(SDK.DebuggerModel.CallFrame, this.update, this);
Jan Schefflera222c632021-08-13 12:46:15130 this.linkifier = new Components.Linkifier.Linkifier();
Blink Reformat4c46d092018-04-07 15:32:37131 this.update();
132 }
133
Jan Scheffler35199b92021-03-17 09:51:15134 static instance(): WatchExpressionsSidebarPane {
Andres Olivaresfd431fb2020-12-10 15:30:04135 if (!watchExpressionsSidebarPaneInstance) {
136 watchExpressionsSidebarPaneInstance = new WatchExpressionsSidebarPane();
137 }
138 return watchExpressionsSidebarPaneInstance;
139 }
140
Jan Scheffler35199b92021-03-17 09:51:15141 toolbarItems(): UI.Toolbar.ToolbarItem[] {
Jan Schefflera222c632021-08-13 12:46:15142 return [this.addButton, this.refreshButton];
Blink Reformat4c46d092018-04-07 15:32:37143 }
144
Randolf Jungffd14242023-04-19 00:32:25145 override focus(): void {
Jack Lynch5db2a3e2019-11-05 19:12:31146 if (this.hasFocus()) {
147 return;
148 }
Jan Schefflera222c632021-08-13 12:46:15149 if (this.watchExpressions.length > 0) {
150 this.treeOutline.forceSelect();
Jack Lynch5db2a3e2019-11-05 19:12:31151 }
152 }
153
Jan Scheffler35199b92021-03-17 09:51:15154 hasExpressions(): boolean {
Jan Schefflera222c632021-08-13 12:46:15155 return Boolean(this.watchExpressionsSetting.get().length);
Blink Reformat4c46d092018-04-07 15:32:37156 }
157
Jan Schefflera222c632021-08-13 12:46:15158 private saveExpressions(): void {
Blink Reformat4c46d092018-04-07 15:32:37159 const toSave = [];
Jan Schefflera222c632021-08-13 12:46:15160 for (let i = 0; i < this.watchExpressions.length; i++) {
161 const expression = this.watchExpressions[i].expression();
Sigurd Schneider8641b422021-07-16 06:13:35162 if (expression) {
163 toSave.push(expression);
Tim van der Lippe1d6e57a2019-09-30 11:55:34164 }
Blink Reformat4c46d092018-04-07 15:32:37165 }
166
Jan Schefflera222c632021-08-13 12:46:15167 this.watchExpressionsSetting.set(toSave);
Blink Reformat4c46d092018-04-07 15:32:37168 }
169
Jan Schefflera222c632021-08-13 12:46:15170 private async addButtonClicked(): Promise<void> {
Paul Lewis75c7d0d2020-03-19 12:17:26171 await UI.ViewManager.ViewManager.instance().showView('sources.watch');
Jan Schefflera222c632021-08-13 12:46:15172 this.emptyElement.classList.add('hidden');
173 this.createWatchExpression(null).startEditing();
Blink Reformat4c46d092018-04-07 15:32:37174 }
175
Randolf Jungffd14242023-04-19 00:32:25176 override async doUpdate(): Promise<void> {
Jan Schefflera222c632021-08-13 12:46:15177 this.linkifier.reset();
Blink Reformat4c46d092018-04-07 15:32:37178 this.contentElement.removeChildren();
Jan Schefflera222c632021-08-13 12:46:15179 this.treeOutline.removeChildren();
180 this.watchExpressions = [];
181 this.emptyElement = (this.contentElement.createChild('div', 'gray-info-message') as HTMLElement);
182 this.emptyElement.textContent = i18nString(UIStrings.noWatchExpressions);
183 this.emptyElement.tabIndex = -1;
184 const watchExpressionStrings = this.watchExpressionsSetting.get();
Jack Lynch338317a2020-08-19 21:59:49185 if (watchExpressionStrings.length) {
Jan Schefflera222c632021-08-13 12:46:15186 this.emptyElement.classList.add('hidden');
Jack Lynch338317a2020-08-19 21:59:49187 }
Blink Reformat4c46d092018-04-07 15:32:37188 for (let i = 0; i < watchExpressionStrings.length; ++i) {
189 const expression = watchExpressionStrings[i];
Tim van der Lippe1d6e57a2019-09-30 11:55:34190 if (!expression) {
Blink Reformat4c46d092018-04-07 15:32:37191 continue;
Tim van der Lippe1d6e57a2019-09-30 11:55:34192 }
Blink Reformat4c46d092018-04-07 15:32:37193
Jan Schefflera222c632021-08-13 12:46:15194 this.createWatchExpression(expression);
Blink Reformat4c46d092018-04-07 15:32:37195 }
Blink Reformat4c46d092018-04-07 15:32:37196 }
197
Jan Schefflera222c632021-08-13 12:46:15198 private createWatchExpression(expression: string|null): WatchExpression {
199 this.contentElement.appendChild(this.treeOutline.element);
200 const watchExpression = new WatchExpression(expression, this.expandController, this.linkifier);
Benedikt Meurere02230c2021-09-09 13:57:22201 watchExpression.addEventListener(Events.ExpressionUpdated, this.watchExpressionUpdated, this);
Jan Schefflera222c632021-08-13 12:46:15202 this.treeOutline.appendChild(watchExpression.treeElement());
203 this.watchExpressions.push(watchExpression);
Blink Reformat4c46d092018-04-07 15:32:37204 return watchExpression;
205 }
206
Benedikt Meurere02230c2021-09-09 13:57:22207 private watchExpressionUpdated({data: watchExpression}: Common.EventTarget.EventTargetEvent<WatchExpression>): void {
Blink Reformat4c46d092018-04-07 15:32:37208 if (!watchExpression.expression()) {
Jan Schefflera222c632021-08-13 12:46:15209 Platform.ArrayUtilities.removeElement(this.watchExpressions, watchExpression);
210 this.treeOutline.removeChild(watchExpression.treeElement());
211 this.emptyElement.classList.toggle('hidden', Boolean(this.watchExpressions.length));
212 if (this.watchExpressions.length === 0) {
213 this.treeOutline.element.remove();
Jack Lynch5db2a3e2019-11-05 19:12:31214 }
Blink Reformat4c46d092018-04-07 15:32:37215 }
216
Jan Schefflera222c632021-08-13 12:46:15217 this.saveExpressions();
Blink Reformat4c46d092018-04-07 15:32:37218 }
219
Jan Schefflera222c632021-08-13 12:46:15220 private contextMenu(event: MouseEvent): void {
Tim van der Lippefbbf9812020-02-13 14:43:46221 const contextMenu = new UI.ContextMenu.ContextMenu(event);
Jan Schefflera222c632021-08-13 12:46:15222 this.populateContextMenu(contextMenu, event);
Tim van der Lippe2d9a95c2022-01-04 15:18:03223 void contextMenu.show();
Blink Reformat4c46d092018-04-07 15:32:37224 }
225
Jan Schefflera222c632021-08-13 12:46:15226 private populateContextMenu(contextMenu: UI.ContextMenu.ContextMenu, event: MouseEvent): void {
Blink Reformat4c46d092018-04-07 15:32:37227 let isEditing = false;
Jan Schefflera222c632021-08-13 12:46:15228 for (const watchExpression of this.watchExpressions) {
Tim van der Lippe0ad77da2020-10-01 16:38:00229 isEditing = isEditing || watchExpression.isEditing();
Tim van der Lippe1d6e57a2019-09-30 11:55:34230 }
Blink Reformat4c46d092018-04-07 15:32:37231
Tim van der Lippe1d6e57a2019-09-30 11:55:34232 if (!isEditing) {
Jan Schefflera222c632021-08-13 12:46:15233 contextMenu.debugSection().appendItem(i18nString(UIStrings.addWatchExpression), this.addButtonClicked.bind(this));
Blink Reformat4c46d092018-04-07 15:32:37234 }
235
Jan Schefflera222c632021-08-13 12:46:15236 if (this.watchExpressions.length > 1) {
Changhao Han8eb616e2021-08-11 05:45:27237 contextMenu.debugSection().appendItem(
Jan Schefflera222c632021-08-13 12:46:15238 i18nString(UIStrings.deleteAllWatchExpressions), this.deleteAllButtonClicked.bind(this));
Changhao Han8eb616e2021-08-11 05:45:27239 }
240
Jan Schefflera222c632021-08-13 12:46:15241 const treeElement = this.treeOutline.treeElementFromEvent(event);
Jack Lynch5db2a3e2019-11-05 19:12:31242 if (!treeElement) {
Blink Reformat4c46d092018-04-07 15:32:37243 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34244 }
Jack Lynch5db2a3e2019-11-05 19:12:31245 const currentWatchExpression =
Jan Schefflera222c632021-08-13 12:46:15246 this.watchExpressions.find(watchExpression => treeElement.hasAncestorOrSelf(watchExpression.treeElement()));
Tim van der Lippe0ad77da2020-10-01 16:38:00247 if (currentWatchExpression) {
Jan Schefflera222c632021-08-13 12:46:15248 currentWatchExpression.populateContextMenu(contextMenu, event);
Tim van der Lippe0ad77da2020-10-01 16:38:00249 }
Blink Reformat4c46d092018-04-07 15:32:37250 }
251
Jan Schefflera222c632021-08-13 12:46:15252 private deleteAllButtonClicked(): void {
253 this.watchExpressions = [];
254 this.saveExpressions();
Blink Reformat4c46d092018-04-07 15:32:37255 this.update();
256 }
257
Jan Schefflera222c632021-08-13 12:46:15258 private async focusAndAddExpressionToWatch(expression: string): Promise<void> {
Benedikt Meurer61f1eef2021-03-17 10:38:29259 await UI.ViewManager.ViewManager.instance().showView('sources.watch');
Jan Schefflera222c632021-08-13 12:46:15260 this.createWatchExpression(expression);
261 this.saveExpressions();
Jack Lynch338317a2020-08-19 21:59:49262 this.update();
PhistucK065e1362018-05-05 09:51:09263 }
264
Jan Scheffler35199b92021-03-17 09:51:15265 handleAction(_context: UI.Context.Context, _actionId: string): boolean {
Tim van der Lipped1a00aa2020-08-19 16:03:56266 const frame = UI.Context.Context.instance().flavor(UISourceCodeFrame);
Tim van der Lippe1d6e57a2019-09-30 11:55:34267 if (!frame) {
Blink Reformat4c46d092018-04-07 15:32:37268 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34269 }
Tim van der Lippec1ab1122021-11-12 16:10:31270 const {state} = frame.textEditor;
271 const text = state.sliceDoc(state.selection.main.from, state.selection.main.to);
Tim van der Lippe2d9a95c2022-01-04 15:18:03272 void this.focusAndAddExpressionToWatch(text);
Blink Reformat4c46d092018-04-07 15:32:37273 return true;
274 }
275
Jan Scheffler35199b92021-03-17 09:51:15276 appendApplicableItems(event: Event, contextMenu: UI.ContextMenu.ContextMenu, target: Object): void {
Tim van der Lippefbbf9812020-02-13 14:43:46277 if (target instanceof ObjectUI.ObjectPropertiesSection.ObjectPropertyTreeElement && !target.property.synthetic) {
PhistucK065e1362018-05-05 09:51:09278 contextMenu.debugSection().appendItem(
Jan Schefflera222c632021-08-13 12:46:15279 i18nString(UIStrings.addPropertyPathToWatch), () => this.focusAndAddExpressionToWatch(target.path()));
PhistucK065e1362018-05-05 09:51:09280 }
281
Tim van der Lipped1a00aa2020-08-19 16:03:56282 const frame = UI.Context.Context.instance().flavor(UISourceCodeFrame);
Tim van der Lippec1ab1122021-11-12 16:10:31283 if (!frame || frame.textEditor.state.selection.main.empty) {
Blink Reformat4c46d092018-04-07 15:32:37284 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34285 }
Blink Reformat4c46d092018-04-07 15:32:37286
287 contextMenu.debugSection().appendAction('sources.add-to-watch');
288 }
Randolf Jungffd14242023-04-19 00:32:25289 override wasShown(): void {
Kriti Sapraae8f17e2021-08-18 10:28:02290 super.wasShown();
291 this.treeOutline.registerCSSFiles([watchExpressionsSidebarPaneStyles]);
292 this.registerCSSFiles([watchExpressionsSidebarPaneStyles, objectValueStyles]);
293 }
Tim van der Lippe8987f8f2020-01-03 15:03:16294}
Blink Reformat4c46d092018-04-07 15:32:37295
Benedikt Meurere02230c2021-09-09 13:57:22296export class WatchExpression extends Common.ObjectWrapper.ObjectWrapper<EventTypes> {
Jan Schefflera222c632021-08-13 12:46:15297 private treeElementInternal!: UI.TreeOutline.TreeElement;
298 private nameElement!: Element;
299 private valueElement!: Element;
300 private expressionInternal: string|null;
301 private readonly expandController: ObjectUI.ObjectPropertiesSection.ObjectPropertiesSectionsTreeExpandController;
302 private element: HTMLDivElement;
303 private editing: boolean;
304 private linkifier: Components.Linkifier.Linkifier;
305 private textPrompt?: ObjectUI.ObjectPropertiesSection.ObjectPropertyPrompt;
306 private result?: SDK.RemoteObject.RemoteObject|null;
307 private preventClickTimeout?: number;
Kateryna Prokopenko52a9b592021-10-20 15:06:01308 private resizeObserver?: ResizeObserver;
Jan Scheffler35199b92021-03-17 09:51:15309 constructor(
310 expression: string|null,
311 expandController: ObjectUI.ObjectPropertiesSection.ObjectPropertiesSectionsTreeExpandController,
312 linkifier: Components.Linkifier.Linkifier) {
Blink Reformat4c46d092018-04-07 15:32:37313 super();
Tim van der Lippe0ad77da2020-10-01 16:38:00314
Jan Schefflera222c632021-08-13 12:46:15315 this.expressionInternal = expression;
316 this.expandController = expandController;
317 this.element = document.createElement('div');
318 this.element.classList.add('watch-expression');
319 this.element.classList.add('monospace');
320 this.editing = false;
321 this.linkifier = linkifier;
Blink Reformat4c46d092018-04-07 15:32:37322
Jan Schefflera222c632021-08-13 12:46:15323 this.createWatchExpression();
Blink Reformat4c46d092018-04-07 15:32:37324 this.update();
325 }
326
Jan Scheffler35199b92021-03-17 09:51:15327 treeElement(): UI.TreeOutline.TreeElement {
Jan Schefflera222c632021-08-13 12:46:15328 return this.treeElementInternal;
Blink Reformat4c46d092018-04-07 15:32:37329 }
330
Jan Scheffler35199b92021-03-17 09:51:15331 expression(): string|null {
Jan Schefflera222c632021-08-13 12:46:15332 return this.expressionInternal;
Blink Reformat4c46d092018-04-07 15:32:37333 }
334
Jaroslav Sevcik9b05a522022-11-30 15:43:21335 async #evaluateExpression(executionContext: SDK.RuntimeModel.ExecutionContext, expression: string):
336 Promise<SDK.RuntimeModel.EvaluationResult> {
337 if (Root.Runtime.experiments.isEnabled('evaluateExpressionsWithSourceMaps')) {
338 const callFrame = executionContext.debuggerModel.selectedCallFrame();
339 if (callFrame) {
340 const nameMap = await SourceMapScopes.NamesResolver.allVariablesInCallFrame(callFrame);
341 try {
342 expression =
343 await Formatter.FormatterWorkerPool.formatterWorkerPool().javaScriptSubstitute(expression, nameMap);
344 } catch {
345 }
346 }
347 }
348
349 return executionContext.evaluate(
350 {
351 expression,
352 objectGroup: WatchExpression.watchObjectGroupId,
353 includeCommandLineAPI: false,
354 silent: true,
355 returnByValue: false,
356 generatePreview: false,
Jaroslav Sevcik9b05a522022-11-30 15:43:21357 },
358 /* userGesture */ false,
359 /* awaitPromise */ false);
360 }
361
Jan Scheffler35199b92021-03-17 09:51:15362 update(): void {
Tim van der Lipped1a00aa2020-08-19 16:03:56363 const currentExecutionContext = UI.Context.Context.instance().flavor(SDK.RuntimeModel.ExecutionContext);
Jan Schefflera222c632021-08-13 12:46:15364 if (currentExecutionContext && this.expressionInternal) {
Jaroslav Sevcik9b05a522022-11-30 15:43:21365 void this.#evaluateExpression(currentExecutionContext, this.expressionInternal).then(result => {
366 if ('object' in result) {
367 this.createWatchExpression(result.object, result.exceptionDetails);
368 } else {
369 this.createWatchExpression();
370 }
371 });
Sigurd Schneider53dc6482021-06-30 11:22:02372 } else {
Jan Schefflera222c632021-08-13 12:46:15373 this.createWatchExpression();
Blink Reformat4c46d092018-04-07 15:32:37374 }
375 }
376
Jan Scheffler35199b92021-03-17 09:51:15377 startEditing(): void {
Jan Schefflera222c632021-08-13 12:46:15378 this.editing = true;
379 this.treeElementInternal.setDisableSelectFocus(true);
380 this.element.removeChildren();
381 const newDiv = this.element.createChild('div');
382 newDiv.textContent = this.nameElement.textContent;
383 this.textPrompt = new ObjectUI.ObjectPropertiesSection.ObjectPropertyPrompt();
384 this.textPrompt.renderAsBlock();
385 const proxyElement = (this.textPrompt.attachAndStartEditing(newDiv, this.finishEditing.bind(this)) as HTMLElement);
386 this.treeElementInternal.listItemElement.classList.add('watch-expression-editing');
387 this.treeElementInternal.collapse();
Blink Reformat4c46d092018-04-07 15:32:37388 proxyElement.classList.add('watch-expression-text-prompt-proxy');
Jan Schefflera222c632021-08-13 12:46:15389 proxyElement.addEventListener('keydown', this.promptKeyDown.bind(this), false);
390 const selection = this.element.getComponentSelection();
Tim van der Lippe0ad77da2020-10-01 16:38:00391 if (selection) {
392 selection.selectAllChildren(newDiv);
393 }
Blink Reformat4c46d092018-04-07 15:32:37394 }
395
Jan Scheffler35199b92021-03-17 09:51:15396 isEditing(): boolean {
Jan Schefflera222c632021-08-13 12:46:15397 return Boolean(this.editing);
Blink Reformat4c46d092018-04-07 15:32:37398 }
399
Jan Schefflera222c632021-08-13 12:46:15400 private finishEditing(event: Event, canceled?: boolean): void {
Tim van der Lippe1d6e57a2019-09-30 11:55:34401 if (event) {
Blink Reformat4c46d092018-04-07 15:32:37402 event.consume(canceled);
Tim van der Lippe1d6e57a2019-09-30 11:55:34403 }
Blink Reformat4c46d092018-04-07 15:32:37404
Jan Schefflera222c632021-08-13 12:46:15405 this.editing = false;
406 this.treeElementInternal.setDisableSelectFocus(false);
407 this.treeElementInternal.listItemElement.classList.remove('watch-expression-editing');
408 if (this.textPrompt) {
409 this.textPrompt.detach();
410 const newExpression = canceled ? this.expressionInternal : this.textPrompt.text();
411 this.textPrompt = undefined;
412 this.element.removeChildren();
413 this.updateExpression(newExpression);
Tim van der Lippe0ad77da2020-10-01 16:38:00414 }
Blink Reformat4c46d092018-04-07 15:32:37415 }
416
Jan Schefflera222c632021-08-13 12:46:15417 private dblClickOnWatchExpression(event: Event): void {
Blink Reformat4c46d092018-04-07 15:32:37418 event.consume();
Tim van der Lippe1d6e57a2019-09-30 11:55:34419 if (!this.isEditing()) {
Blink Reformat4c46d092018-04-07 15:32:37420 this.startEditing();
Tim van der Lippe1d6e57a2019-09-30 11:55:34421 }
Blink Reformat4c46d092018-04-07 15:32:37422 }
423
Jan Schefflera222c632021-08-13 12:46:15424 private updateExpression(newExpression: string|null): void {
425 if (this.expressionInternal) {
426 this.expandController.stopWatchSectionsWithId(this.expressionInternal);
Tim van der Lippe1d6e57a2019-09-30 11:55:34427 }
Kateryna Prokopenko52a9b592021-10-20 15:06:01428 this.resizeObserver?.disconnect();
Jan Schefflera222c632021-08-13 12:46:15429 this.expressionInternal = newExpression;
Blink Reformat4c46d092018-04-07 15:32:37430 this.update();
Benedikt Meurere02230c2021-09-09 13:57:22431 this.dispatchEventToListeners(Events.ExpressionUpdated, this);
Blink Reformat4c46d092018-04-07 15:32:37432 }
433
Jan Schefflera222c632021-08-13 12:46:15434 private deleteWatchExpression(event: Event): void {
Blink Reformat4c46d092018-04-07 15:32:37435 event.consume(true);
Jan Schefflera222c632021-08-13 12:46:15436 this.updateExpression(null);
Blink Reformat4c46d092018-04-07 15:32:37437 }
438
Jan Schefflera222c632021-08-13 12:46:15439 private createWatchExpression(
440 result?: SDK.RemoteObject.RemoteObject, exceptionDetails?: Protocol.Runtime.ExceptionDetails): void {
441 this.result = result || null;
Blink Reformat4c46d092018-04-07 15:32:37442
Jan Schefflera222c632021-08-13 12:46:15443 this.element.removeChildren();
444 const oldTreeElement = this.treeElementInternal;
445 this.createWatchExpressionTreeElement(result, exceptionDetails);
Jack Lynch5db2a3e2019-11-05 19:12:31446 if (oldTreeElement && oldTreeElement.parent) {
447 const root = oldTreeElement.parent;
448 const index = root.indexOfChild(oldTreeElement);
449 root.removeChild(oldTreeElement);
Jan Schefflera222c632021-08-13 12:46:15450 root.insertChild(this.treeElementInternal, index);
Jack Lynch5db2a3e2019-11-05 19:12:31451 }
Jan Schefflera222c632021-08-13 12:46:15452 this.treeElementInternal.select();
Jack Lynch5db2a3e2019-11-05 19:12:31453 }
454
Jan Schefflera222c632021-08-13 12:46:15455 private createWatchExpressionHeader(
Jan Scheffler35199b92021-03-17 09:51:15456 expressionValue?: SDK.RemoteObject.RemoteObject, exceptionDetails?: Protocol.Runtime.ExceptionDetails): Element {
Jan Schefflera222c632021-08-13 12:46:15457 const headerElement = this.element.createChild('div', 'watch-expression-header');
Benedikt Meurer6337cf12023-04-17 11:10:20458 const deleteButton = UI.Icon.Icon.create('cross', 'watch-expression-delete-button');
Kateryna Prokopenko52a9b592021-10-20 15:06:01459 this.resizeObserver = new ResizeObserver(entries => {
460 entries.forEach(entry => {
461 // 55 serves as a width threshold here (in px)
462 if (entry.contentRect.width < 55) {
463 deleteButton.classList.remove('right-aligned');
464 deleteButton.classList.add('left-aligned');
465 } else {
466 deleteButton.classList.remove('left-aligned');
467 deleteButton.classList.add('right-aligned');
468 }
469 });
470 });
471 this.resizeObserver.observe(headerElement);
Vidal Guillermo Diazleal Ortega83edb472021-02-16 18:39:32472 UI.Tooltip.Tooltip.install(deleteButton, i18nString(UIStrings.deleteWatchExpression));
Jan Schefflera222c632021-08-13 12:46:15473 deleteButton.addEventListener('click', this.deleteWatchExpression.bind(this), false);
Blink Reformat4c46d092018-04-07 15:32:37474
Jack Lynch5db2a3e2019-11-05 19:12:31475 const titleElement = headerElement.createChild('div', 'watch-expression-title tree-element-title');
Jack Lynche1767de2020-04-22 02:43:55476 titleElement.appendChild(deleteButton);
Jan Schefflera222c632021-08-13 12:46:15477 this.nameElement =
478 ObjectUI.ObjectPropertiesSection.ObjectPropertiesSection.createNameElement(this.expressionInternal);
Tim van der Lipped7cfd142021-01-07 12:17:24479 if (Boolean(exceptionDetails) || !expressionValue) {
Jan Schefflera222c632021-08-13 12:46:15480 this.valueElement = document.createElement('span');
481 this.valueElement.classList.add('watch-expression-error');
482 this.valueElement.classList.add('value');
Blink Reformat4c46d092018-04-07 15:32:37483 titleElement.classList.add('dimmed');
Jan Schefflera222c632021-08-13 12:46:15484 this.valueElement.textContent = i18nString(UIStrings.notAvailable);
Philip Pfaffe7426b002020-11-30 15:59:22485 if (exceptionDetails !== undefined && exceptionDetails.exception !== undefined &&
486 exceptionDetails.exception.description !== undefined) {
Jan Schefflera222c632021-08-13 12:46:15487 UI.Tooltip.Tooltip.install(this.valueElement as HTMLElement, exceptionDetails.exception.description);
Philip Pfaffe7426b002020-11-30 15:59:22488 }
Blink Reformat4c46d092018-04-07 15:32:37489 } else {
Tim van der Lippefbbf9812020-02-13 14:43:46490 const propertyValue =
491 ObjectUI.ObjectPropertiesSection.ObjectPropertiesSection.createPropertyValueWithCustomSupport(
Jan Schefflera222c632021-08-13 12:46:15492 expressionValue, Boolean(exceptionDetails), false /* showPreview */, titleElement, this.linkifier);
493 this.valueElement = propertyValue.element;
Blink Reformat4c46d092018-04-07 15:32:37494 }
Tim van der Lippef49e2322020-05-01 15:03:09495 const separatorElement = document.createElement('span');
496 separatorElement.classList.add('watch-expressions-separator');
Blink Reformat4c46d092018-04-07 15:32:37497 separatorElement.textContent = ': ';
Jan Schefflera222c632021-08-13 12:46:15498 titleElement.append(this.nameElement, separatorElement, this.valueElement);
Blink Reformat4c46d092018-04-07 15:32:37499
Jack Lynch5db2a3e2019-11-05 19:12:31500 return headerElement;
501 }
Blink Reformat4c46d092018-04-07 15:32:37502
Jan Schefflera222c632021-08-13 12:46:15503 private createWatchExpressionTreeElement(
Jan Scheffler35199b92021-03-17 09:51:15504 expressionValue?: SDK.RemoteObject.RemoteObject, exceptionDetails?: Protocol.Runtime.ExceptionDetails): void {
Jan Schefflera222c632021-08-13 12:46:15505 const headerElement = this.createWatchExpressionHeader(expressionValue, exceptionDetails);
Jack Lynch5db2a3e2019-11-05 19:12:31506
507 if (!exceptionDetails && expressionValue && expressionValue.hasChildren && !expressionValue.customPreview()) {
508 headerElement.classList.add('watch-expression-object-header');
Jan Schefflera222c632021-08-13 12:46:15509 this.treeElementInternal = new ObjectUI.ObjectPropertiesSection.RootElement(expressionValue, this.linkifier);
510 this.expandController.watchSection(
511 (this.expressionInternal as string),
512 (this.treeElementInternal as ObjectUI.ObjectPropertiesSection.RootElement));
513 this.treeElementInternal.toggleOnClick = false;
514 this.treeElementInternal.listItemElement.addEventListener('click', this.onSectionClick.bind(this), false);
515 this.treeElementInternal.listItemElement.addEventListener('dblclick', this.dblClickOnWatchExpression.bind(this));
Jack Lynch5db2a3e2019-11-05 19:12:31516 } else {
Jan Schefflera222c632021-08-13 12:46:15517 headerElement.addEventListener('dblclick', this.dblClickOnWatchExpression.bind(this));
518 this.treeElementInternal = new UI.TreeOutline.TreeElement();
Jack Lynch5db2a3e2019-11-05 19:12:31519 }
Jan Schefflera222c632021-08-13 12:46:15520 this.treeElementInternal.title = this.element;
521 this.treeElementInternal.listItemElement.classList.add('watch-expression-tree-item');
522 this.treeElementInternal.listItemElement.addEventListener('keydown', event => {
Tim van der Lippebcd6b5c2021-01-13 12:31:51523 if (event.key === 'Enter' && !this.isEditing()) {
Jack Lynch5db2a3e2019-11-05 19:12:31524 this.startEditing();
525 event.consume(true);
Jaroslav Sevcik9b05a522022-11-30 15:43:21526 } else if (event.key === 'Delete' && !this.isEditing()) {
527 this.deleteWatchExpression(event);
Jack Lynch5db2a3e2019-11-05 19:12:31528 }
529 });
Blink Reformat4c46d092018-04-07 15:32:37530 }
531
Jan Schefflera222c632021-08-13 12:46:15532 private onSectionClick(event: Event): void {
Blink Reformat4c46d092018-04-07 15:32:37533 event.consume(true);
Jan Scheffler35199b92021-03-17 09:51:15534 const mouseEvent = (event as MouseEvent);
Tim van der Lippe0ad77da2020-10-01 16:38:00535 if (mouseEvent.detail === 1) {
Jan Schefflera222c632021-08-13 12:46:15536 this.preventClickTimeout = window.setTimeout(handleClick.bind(this), 333);
537 } else if (this.preventClickTimeout !== undefined) {
538 window.clearTimeout(this.preventClickTimeout);
539 this.preventClickTimeout = undefined;
Blink Reformat4c46d092018-04-07 15:32:37540 }
541
Jan Scheffler35199b92021-03-17 09:51:15542 function handleClick(this: WatchExpression): void {
Jan Schefflera222c632021-08-13 12:46:15543 if (!this.treeElementInternal) {
Blink Reformat4c46d092018-04-07 15:32:37544 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34545 }
Blink Reformat4c46d092018-04-07 15:32:37546
Jan Schefflera222c632021-08-13 12:46:15547 if (this.treeElementInternal.expanded) {
548 this.treeElementInternal.collapse();
549 } else if (!this.editing) {
550 this.treeElementInternal.expand();
Tim van der Lippe1d6e57a2019-09-30 11:55:34551 }
Blink Reformat4c46d092018-04-07 15:32:37552 }
553 }
554
Jan Schefflera222c632021-08-13 12:46:15555 private promptKeyDown(event: KeyboardEvent): void {
Jack Frankline24bc6f2022-10-07 10:36:18556 const isEscapeKey = Platform.KeyboardUtilities.isEscKey(event);
557 if (event.key === 'Enter' || isEscapeKey) {
558 this.finishEditing(event, isEscapeKey);
Tim van der Lippe1d6e57a2019-09-30 11:55:34559 }
Blink Reformat4c46d092018-04-07 15:32:37560 }
561
Jan Schefflera222c632021-08-13 12:46:15562 populateContextMenu(contextMenu: UI.ContextMenu.ContextMenu, event: Event): void {
Blink Reformat4c46d092018-04-07 15:32:37563 if (!this.isEditing()) {
564 contextMenu.editSection().appendItem(
Jan Schefflera222c632021-08-13 12:46:15565 i18nString(UIStrings.deleteWatchExpression), this.updateExpression.bind(this, null));
Blink Reformat4c46d092018-04-07 15:32:37566 }
567
Jan Schefflera222c632021-08-13 12:46:15568 if (!this.isEditing() && this.result && (this.result.type === 'number' || this.result.type === 'string')) {
Tim van der Lippefbbf9812020-02-13 14:43:46569 contextMenu.clipboardSection().appendItem(
Jan Schefflera222c632021-08-13 12:46:15570 i18nString(UIStrings.copyValue), this.copyValueButtonClicked.bind(this));
Tim van der Lippe1d6e57a2019-09-30 11:55:34571 }
Blink Reformat4c46d092018-04-07 15:32:37572
Sigurd Schneider090c3b62020-11-10 10:26:49573 const target = UI.UIUtils.deepElementFromEvent(event);
Jan Schefflera222c632021-08-13 12:46:15574 if (target && this.valueElement.isSelfOrAncestor(target) && this.result) {
575 contextMenu.appendApplicableItems(this.result);
Tim van der Lippe1d6e57a2019-09-30 11:55:34576 }
Blink Reformat4c46d092018-04-07 15:32:37577 }
578
Jan Schefflera222c632021-08-13 12:46:15579 private copyValueButtonClicked(): void {
580 Host.InspectorFrontendHost.InspectorFrontendHostInstance.copyText(this.valueElement.textContent);
Blink Reformat4c46d092018-04-07 15:32:37581 }
Jan Scheffler35199b92021-03-17 09:51:15582
Sigurd Schneider8641b422021-07-16 06:13:35583 private static readonly watchObjectGroupId = 'watch-group';
Tim van der Lippe8987f8f2020-01-03 15:03:16584}
Blink Reformat4c46d092018-04-07 15:32:37585
Benedikt Meurere02230c2021-09-09 13:57:22586const enum Events {
587 ExpressionUpdated = 'ExpressionUpdated',
Jan Scheffler35199b92021-03-17 09:51:15588}
Benedikt Meurere02230c2021-09-09 13:57:22589
590type EventTypes = {
591 [Events.ExpressionUpdated]: WatchExpression,
592};