blob: 8588e18426297e2485949f39d43d454bf8ebd1dc [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';
Tim van der Lippe021c7572021-04-19 10:49:4339import * as SDK from '../../core/sdk/sdk.js';
Simon Zünd5e6e1f82023-11-23 10:55:3140import type * as Protocol from '../../generated/protocol.js';
Jaroslav Sevcik9b05a522022-11-30 15:43:2141import * as Formatter from '../../models/formatter/formatter.js';
42import * as SourceMapScopes from '../../models/source_map_scopes/source_map_scopes.js';
Benedikt Meurerd8a95652024-01-04 08:29:1743import * as Buttons from '../../ui/components/buttons/buttons.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
Benedikt Meurer98128882023-12-03 13:59:4993 UI.ActionRegistration.ActionDelegate, UI.Toolbar.ItemsProvider,
94 UI.ContextMenu.Provider<ObjectUI.ObjectPropertiesSection.ObjectPropertyTreeElement|UISourceCodeFrame> {
Jan Schefflera222c632021-08-13 12:46:1595 private watchExpressions: WatchExpression[];
96 private emptyElement!: HTMLElement;
97 private readonly watchExpressionsSetting: Common.Settings.Setting<string[]>;
98 private readonly addButton: UI.Toolbar.ToolbarButton;
99 private readonly refreshButton: UI.Toolbar.ToolbarButton;
100 private readonly treeOutline: ObjectUI.ObjectPropertiesSection.ObjectPropertiesSectionsTreeOutline;
101 private readonly expandController: ObjectUI.ObjectPropertiesSection.ObjectPropertiesSectionsTreeExpandController;
102 private readonly linkifier: Components.Linkifier.Linkifier;
Jan Scheffler35199b92021-03-17 09:51:15103 private constructor() {
Blink Reformat4c46d092018-04-07 15:32:37104 super(true);
Blink Reformat4c46d092018-04-07 15:32:37105
Simon Zünd0243c602020-03-10 06:51:42106 // TODO(szuend): Replace with a Set once the web test
Tim van der Lippe021c7572021-04-19 10:49:43107 // panels/sources/debugger-ui/watch-expressions-preserve-expansion.js is either converted
Jan Scheffler35199b92021-03-17 09:51:15108 // to an e2e test or no longer accesses this variable directly.
Jan Schefflera222c632021-08-13 12:46:15109 this.watchExpressions = [];
110 this.watchExpressionsSetting =
Danil Somsikov2bfb9062024-01-30 16:31:09111 Common.Settings.Settings.instance().createLocalSetting<string[]>('watch-expressions', []);
Blink Reformat4c46d092018-04-07 15:32:37112
Simon Zünd00093852023-11-28 08:29:18113 this.addButton = new UI.Toolbar.ToolbarButton(
114 i18nString(UIStrings.addWatchExpression), 'plus', undefined, 'add-watch-expression');
Jan Schefflera222c632021-08-13 12:46:15115 this.addButton.addEventListener(UI.Toolbar.ToolbarButton.Events.Click, _event => {
Tim van der Lippe2d9a95c2022-01-04 15:18:03116 void this.addButtonClicked();
Tim van der Lippe37a35ff2020-03-03 13:49:02117 });
Simon Zünd00093852023-11-28 08:29:18118 this.refreshButton = new UI.Toolbar.ToolbarButton(
119 i18nString(UIStrings.refreshWatchExpressions), 'refresh', undefined, 'refresh-watch-expressions');
Jan Schefflera222c632021-08-13 12:46:15120 this.refreshButton.addEventListener(UI.Toolbar.ToolbarButton.Events.Click, this.update, this);
Blink Reformat4c46d092018-04-07 15:32:37121
122 this.contentElement.classList.add('watch-expressions');
Danil Somsikov0cb97462024-01-30 15:11:29123 this.contentElement.setAttribute('jslog', `${VisualLogging.section('sources.watch')}`);
Jan Schefflera222c632021-08-13 12:46:15124 this.contentElement.addEventListener('contextmenu', this.contextMenu.bind(this), false);
125 this.treeOutline = new ObjectUI.ObjectPropertiesSection.ObjectPropertiesSectionsTreeOutline();
Benedikt Meurer699358f2023-12-15 12:50:01126 this.treeOutline.hideOverflow();
Kriti Sapraae8f17e2021-08-18 10:28:02127
Jan Schefflera222c632021-08-13 12:46:15128 this.treeOutline.setShowSelectionOnKeyboardFocus(/* show */ true);
129 this.expandController =
130 new ObjectUI.ObjectPropertiesSection.ObjectPropertiesSectionsTreeExpandController(this.treeOutline);
Blink Reformat4c46d092018-04-07 15:32:37131
Tim van der Lipped1a00aa2020-08-19 16:03:56132 UI.Context.Context.instance().addFlavorChangeListener(SDK.RuntimeModel.ExecutionContext, this.update, this);
133 UI.Context.Context.instance().addFlavorChangeListener(SDK.DebuggerModel.CallFrame, this.update, this);
Jan Schefflera222c632021-08-13 12:46:15134 this.linkifier = new Components.Linkifier.Linkifier();
Blink Reformat4c46d092018-04-07 15:32:37135 this.update();
136 }
137
Jan Scheffler35199b92021-03-17 09:51:15138 static instance(): WatchExpressionsSidebarPane {
Andres Olivaresfd431fb2020-12-10 15:30:04139 if (!watchExpressionsSidebarPaneInstance) {
140 watchExpressionsSidebarPaneInstance = new WatchExpressionsSidebarPane();
141 }
142 return watchExpressionsSidebarPaneInstance;
143 }
144
Jan Scheffler35199b92021-03-17 09:51:15145 toolbarItems(): UI.Toolbar.ToolbarItem[] {
Jan Schefflera222c632021-08-13 12:46:15146 return [this.addButton, this.refreshButton];
Blink Reformat4c46d092018-04-07 15:32:37147 }
148
Randolf Jungffd14242023-04-19 00:32:25149 override focus(): void {
Jack Lynch5db2a3e2019-11-05 19:12:31150 if (this.hasFocus()) {
151 return;
152 }
Jan Schefflera222c632021-08-13 12:46:15153 if (this.watchExpressions.length > 0) {
154 this.treeOutline.forceSelect();
Jack Lynch5db2a3e2019-11-05 19:12:31155 }
156 }
157
Jan Scheffler35199b92021-03-17 09:51:15158 hasExpressions(): boolean {
Jan Schefflera222c632021-08-13 12:46:15159 return Boolean(this.watchExpressionsSetting.get().length);
Blink Reformat4c46d092018-04-07 15:32:37160 }
161
Jan Schefflera222c632021-08-13 12:46:15162 private saveExpressions(): void {
Blink Reformat4c46d092018-04-07 15:32:37163 const toSave = [];
Jan Schefflera222c632021-08-13 12:46:15164 for (let i = 0; i < this.watchExpressions.length; i++) {
165 const expression = this.watchExpressions[i].expression();
Sigurd Schneider8641b422021-07-16 06:13:35166 if (expression) {
167 toSave.push(expression);
Tim van der Lippe1d6e57a2019-09-30 11:55:34168 }
Blink Reformat4c46d092018-04-07 15:32:37169 }
170
Jan Schefflera222c632021-08-13 12:46:15171 this.watchExpressionsSetting.set(toSave);
Blink Reformat4c46d092018-04-07 15:32:37172 }
173
Jan Schefflera222c632021-08-13 12:46:15174 private async addButtonClicked(): Promise<void> {
Paul Lewis75c7d0d2020-03-19 12:17:26175 await UI.ViewManager.ViewManager.instance().showView('sources.watch');
Jan Schefflera222c632021-08-13 12:46:15176 this.emptyElement.classList.add('hidden');
177 this.createWatchExpression(null).startEditing();
Blink Reformat4c46d092018-04-07 15:32:37178 }
179
Randolf Jungffd14242023-04-19 00:32:25180 override async doUpdate(): Promise<void> {
Jan Schefflera222c632021-08-13 12:46:15181 this.linkifier.reset();
Blink Reformat4c46d092018-04-07 15:32:37182 this.contentElement.removeChildren();
Jan Schefflera222c632021-08-13 12:46:15183 this.treeOutline.removeChildren();
184 this.watchExpressions = [];
185 this.emptyElement = (this.contentElement.createChild('div', 'gray-info-message') as HTMLElement);
186 this.emptyElement.textContent = i18nString(UIStrings.noWatchExpressions);
187 this.emptyElement.tabIndex = -1;
188 const watchExpressionStrings = this.watchExpressionsSetting.get();
Jack Lynch338317a2020-08-19 21:59:49189 if (watchExpressionStrings.length) {
Jan Schefflera222c632021-08-13 12:46:15190 this.emptyElement.classList.add('hidden');
Jack Lynch338317a2020-08-19 21:59:49191 }
Blink Reformat4c46d092018-04-07 15:32:37192 for (let i = 0; i < watchExpressionStrings.length; ++i) {
193 const expression = watchExpressionStrings[i];
Tim van der Lippe1d6e57a2019-09-30 11:55:34194 if (!expression) {
Blink Reformat4c46d092018-04-07 15:32:37195 continue;
Tim van der Lippe1d6e57a2019-09-30 11:55:34196 }
Blink Reformat4c46d092018-04-07 15:32:37197
Jan Schefflera222c632021-08-13 12:46:15198 this.createWatchExpression(expression);
Blink Reformat4c46d092018-04-07 15:32:37199 }
Blink Reformat4c46d092018-04-07 15:32:37200 }
201
Jan Schefflera222c632021-08-13 12:46:15202 private createWatchExpression(expression: string|null): WatchExpression {
203 this.contentElement.appendChild(this.treeOutline.element);
204 const watchExpression = new WatchExpression(expression, this.expandController, this.linkifier);
Natasha Lee030b8672024-06-25 21:35:56205 UI.ARIAUtils.setLabel(this.contentElement, i18nString(UIStrings.addWatchExpression));
Benedikt Meurere02230c2021-09-09 13:57:22206 watchExpression.addEventListener(Events.ExpressionUpdated, this.watchExpressionUpdated, this);
Jan Schefflera222c632021-08-13 12:46:15207 this.treeOutline.appendChild(watchExpression.treeElement());
208 this.watchExpressions.push(watchExpression);
Blink Reformat4c46d092018-04-07 15:32:37209 return watchExpression;
210 }
211
Benedikt Meurere02230c2021-09-09 13:57:22212 private watchExpressionUpdated({data: watchExpression}: Common.EventTarget.EventTargetEvent<WatchExpression>): void {
Blink Reformat4c46d092018-04-07 15:32:37213 if (!watchExpression.expression()) {
Jan Schefflera222c632021-08-13 12:46:15214 Platform.ArrayUtilities.removeElement(this.watchExpressions, watchExpression);
215 this.treeOutline.removeChild(watchExpression.treeElement());
216 this.emptyElement.classList.toggle('hidden', Boolean(this.watchExpressions.length));
217 if (this.watchExpressions.length === 0) {
218 this.treeOutline.element.remove();
Jack Lynch5db2a3e2019-11-05 19:12:31219 }
Blink Reformat4c46d092018-04-07 15:32:37220 }
221
Jan Schefflera222c632021-08-13 12:46:15222 this.saveExpressions();
Blink Reformat4c46d092018-04-07 15:32:37223 }
224
Jan Schefflera222c632021-08-13 12:46:15225 private contextMenu(event: MouseEvent): void {
Tim van der Lippefbbf9812020-02-13 14:43:46226 const contextMenu = new UI.ContextMenu.ContextMenu(event);
Jan Schefflera222c632021-08-13 12:46:15227 this.populateContextMenu(contextMenu, event);
Tim van der Lippe2d9a95c2022-01-04 15:18:03228 void contextMenu.show();
Blink Reformat4c46d092018-04-07 15:32:37229 }
230
Jan Schefflera222c632021-08-13 12:46:15231 private populateContextMenu(contextMenu: UI.ContextMenu.ContextMenu, event: MouseEvent): void {
Blink Reformat4c46d092018-04-07 15:32:37232 let isEditing = false;
Jan Schefflera222c632021-08-13 12:46:15233 for (const watchExpression of this.watchExpressions) {
Tim van der Lippe0ad77da2020-10-01 16:38:00234 isEditing = isEditing || watchExpression.isEditing();
Tim van der Lippe1d6e57a2019-09-30 11:55:34235 }
Blink Reformat4c46d092018-04-07 15:32:37236
Tim van der Lippe1d6e57a2019-09-30 11:55:34237 if (!isEditing) {
Simon Zünd00093852023-11-28 08:29:18238 contextMenu.debugSection().appendItem(
239 i18nString(UIStrings.addWatchExpression), this.addButtonClicked.bind(this),
240 {jslogContext: 'add-watch-expression'});
Blink Reformat4c46d092018-04-07 15:32:37241 }
242
Jan Schefflera222c632021-08-13 12:46:15243 if (this.watchExpressions.length > 1) {
Changhao Han8eb616e2021-08-11 05:45:27244 contextMenu.debugSection().appendItem(
Simon Zünd00093852023-11-28 08:29:18245 i18nString(UIStrings.deleteAllWatchExpressions), this.deleteAllButtonClicked.bind(this),
246 {jslogContext: 'delete-all-watch-expressions'});
Changhao Han8eb616e2021-08-11 05:45:27247 }
248
Jan Schefflera222c632021-08-13 12:46:15249 const treeElement = this.treeOutline.treeElementFromEvent(event);
Jack Lynch5db2a3e2019-11-05 19:12:31250 if (!treeElement) {
Blink Reformat4c46d092018-04-07 15:32:37251 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34252 }
Jack Lynch5db2a3e2019-11-05 19:12:31253 const currentWatchExpression =
Jan Schefflera222c632021-08-13 12:46:15254 this.watchExpressions.find(watchExpression => treeElement.hasAncestorOrSelf(watchExpression.treeElement()));
Tim van der Lippe0ad77da2020-10-01 16:38:00255 if (currentWatchExpression) {
Jan Schefflera222c632021-08-13 12:46:15256 currentWatchExpression.populateContextMenu(contextMenu, event);
Tim van der Lippe0ad77da2020-10-01 16:38:00257 }
Blink Reformat4c46d092018-04-07 15:32:37258 }
259
Jan Schefflera222c632021-08-13 12:46:15260 private deleteAllButtonClicked(): void {
261 this.watchExpressions = [];
262 this.saveExpressions();
Blink Reformat4c46d092018-04-07 15:32:37263 this.update();
264 }
265
Jan Schefflera222c632021-08-13 12:46:15266 private async focusAndAddExpressionToWatch(expression: string): Promise<void> {
Benedikt Meurer61f1eef2021-03-17 10:38:29267 await UI.ViewManager.ViewManager.instance().showView('sources.watch');
Jan Schefflera222c632021-08-13 12:46:15268 this.createWatchExpression(expression);
269 this.saveExpressions();
Jack Lynch338317a2020-08-19 21:59:49270 this.update();
PhistucK065e1362018-05-05 09:51:09271 }
272
Jan Scheffler35199b92021-03-17 09:51:15273 handleAction(_context: UI.Context.Context, _actionId: string): boolean {
Tim van der Lipped1a00aa2020-08-19 16:03:56274 const frame = UI.Context.Context.instance().flavor(UISourceCodeFrame);
Tim van der Lippe1d6e57a2019-09-30 11:55:34275 if (!frame) {
Blink Reformat4c46d092018-04-07 15:32:37276 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34277 }
Tim van der Lippec1ab1122021-11-12 16:10:31278 const {state} = frame.textEditor;
279 const text = state.sliceDoc(state.selection.main.from, state.selection.main.to);
Tim van der Lippe2d9a95c2022-01-04 15:18:03280 void this.focusAndAddExpressionToWatch(text);
Blink Reformat4c46d092018-04-07 15:32:37281 return true;
282 }
283
Benedikt Meurer98128882023-12-03 13:59:49284 appendApplicableItems(
285 _event: Event, contextMenu: UI.ContextMenu.ContextMenu,
286 target: ObjectUI.ObjectPropertiesSection.ObjectPropertyTreeElement|UISourceCodeFrame): void {
287 if (target instanceof ObjectUI.ObjectPropertiesSection.ObjectPropertyTreeElement) {
288 if (!target.property.synthetic) {
289 contextMenu.debugSection().appendItem(
Danil Somsikov1b6ec852024-02-23 08:53:25290 i18nString(UIStrings.addPropertyPathToWatch), () => this.focusAndAddExpressionToWatch(target.path()),
291 {jslogContext: 'add-property-path-to-watch'});
Benedikt Meurer98128882023-12-03 13:59:49292 }
293 return;
PhistucK065e1362018-05-05 09:51:09294 }
295
Benedikt Meurer98128882023-12-03 13:59:49296 if (target.textEditor.state.selection.main.empty) {
Blink Reformat4c46d092018-04-07 15:32:37297 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34298 }
Blink Reformat4c46d092018-04-07 15:32:37299
300 contextMenu.debugSection().appendAction('sources.add-to-watch');
301 }
Benedikt Meurer98128882023-12-03 13:59:49302
Randolf Jungffd14242023-04-19 00:32:25303 override wasShown(): void {
Kriti Sapraae8f17e2021-08-18 10:28:02304 super.wasShown();
305 this.treeOutline.registerCSSFiles([watchExpressionsSidebarPaneStyles]);
306 this.registerCSSFiles([watchExpressionsSidebarPaneStyles, objectValueStyles]);
307 }
Tim van der Lippe8987f8f2020-01-03 15:03:16308}
Blink Reformat4c46d092018-04-07 15:32:37309
Benedikt Meurere02230c2021-09-09 13:57:22310export class WatchExpression extends Common.ObjectWrapper.ObjectWrapper<EventTypes> {
Jan Schefflera222c632021-08-13 12:46:15311 private treeElementInternal!: UI.TreeOutline.TreeElement;
312 private nameElement!: Element;
313 private valueElement!: Element;
314 private expressionInternal: string|null;
315 private readonly expandController: ObjectUI.ObjectPropertiesSection.ObjectPropertiesSectionsTreeExpandController;
316 private element: HTMLDivElement;
317 private editing: boolean;
318 private linkifier: Components.Linkifier.Linkifier;
319 private textPrompt?: ObjectUI.ObjectPropertiesSection.ObjectPropertyPrompt;
320 private result?: SDK.RemoteObject.RemoteObject|null;
321 private preventClickTimeout?: number;
Jan Scheffler35199b92021-03-17 09:51:15322 constructor(
323 expression: string|null,
324 expandController: ObjectUI.ObjectPropertiesSection.ObjectPropertiesSectionsTreeExpandController,
325 linkifier: Components.Linkifier.Linkifier) {
Blink Reformat4c46d092018-04-07 15:32:37326 super();
Tim van der Lippe0ad77da2020-10-01 16:38:00327
Jan Schefflera222c632021-08-13 12:46:15328 this.expressionInternal = expression;
329 this.expandController = expandController;
330 this.element = document.createElement('div');
331 this.element.classList.add('watch-expression');
332 this.element.classList.add('monospace');
333 this.editing = false;
334 this.linkifier = linkifier;
Blink Reformat4c46d092018-04-07 15:32:37335
Jan Schefflera222c632021-08-13 12:46:15336 this.createWatchExpression();
Blink Reformat4c46d092018-04-07 15:32:37337 this.update();
338 }
339
Jan Scheffler35199b92021-03-17 09:51:15340 treeElement(): UI.TreeOutline.TreeElement {
Jan Schefflera222c632021-08-13 12:46:15341 return this.treeElementInternal;
Blink Reformat4c46d092018-04-07 15:32:37342 }
343
Jan Scheffler35199b92021-03-17 09:51:15344 expression(): string|null {
Jan Schefflera222c632021-08-13 12:46:15345 return this.expressionInternal;
Blink Reformat4c46d092018-04-07 15:32:37346 }
347
Jaroslav Sevcik9b05a522022-11-30 15:43:21348 async #evaluateExpression(executionContext: SDK.RuntimeModel.ExecutionContext, expression: string):
349 Promise<SDK.RuntimeModel.EvaluationResult> {
Simon Zünde77e31ee2024-04-29 10:04:04350 const callFrame = executionContext.debuggerModel.selectedCallFrame();
tomble6500872024-05-29 09:15:29351 if (callFrame && callFrame.script.isJavaScript()) {
Simon Zünde77e31ee2024-04-29 10:04:04352 const nameMap = await SourceMapScopes.NamesResolver.allVariablesInCallFrame(callFrame);
353 try {
354 expression =
355 await Formatter.FormatterWorkerPool.formatterWorkerPool().javaScriptSubstitute(expression, nameMap);
356 } catch {
Jaroslav Sevcik9b05a522022-11-30 15:43:21357 }
358 }
359
360 return executionContext.evaluate(
361 {
362 expression,
363 objectGroup: WatchExpression.watchObjectGroupId,
364 includeCommandLineAPI: false,
365 silent: true,
366 returnByValue: false,
367 generatePreview: false,
Jaroslav Sevcik9b05a522022-11-30 15:43:21368 },
369 /* userGesture */ false,
370 /* awaitPromise */ false);
371 }
372
Jan Scheffler35199b92021-03-17 09:51:15373 update(): void {
Tim van der Lipped1a00aa2020-08-19 16:03:56374 const currentExecutionContext = UI.Context.Context.instance().flavor(SDK.RuntimeModel.ExecutionContext);
Jan Schefflera222c632021-08-13 12:46:15375 if (currentExecutionContext && this.expressionInternal) {
Jaroslav Sevcik9b05a522022-11-30 15:43:21376 void this.#evaluateExpression(currentExecutionContext, this.expressionInternal).then(result => {
377 if ('object' in result) {
378 this.createWatchExpression(result.object, result.exceptionDetails);
379 } else {
380 this.createWatchExpression();
381 }
382 });
Sigurd Schneider53dc6482021-06-30 11:22:02383 } else {
Jan Schefflera222c632021-08-13 12:46:15384 this.createWatchExpression();
Blink Reformat4c46d092018-04-07 15:32:37385 }
386 }
387
Jan Scheffler35199b92021-03-17 09:51:15388 startEditing(): void {
Jan Schefflera222c632021-08-13 12:46:15389 this.editing = true;
390 this.treeElementInternal.setDisableSelectFocus(true);
391 this.element.removeChildren();
392 const newDiv = this.element.createChild('div');
393 newDiv.textContent = this.nameElement.textContent;
394 this.textPrompt = new ObjectUI.ObjectPropertiesSection.ObjectPropertyPrompt();
395 this.textPrompt.renderAsBlock();
396 const proxyElement = (this.textPrompt.attachAndStartEditing(newDiv, this.finishEditing.bind(this)) as HTMLElement);
397 this.treeElementInternal.listItemElement.classList.add('watch-expression-editing');
398 this.treeElementInternal.collapse();
Blink Reformat4c46d092018-04-07 15:32:37399 proxyElement.classList.add('watch-expression-text-prompt-proxy');
Jan Schefflera222c632021-08-13 12:46:15400 proxyElement.addEventListener('keydown', this.promptKeyDown.bind(this), false);
401 const selection = this.element.getComponentSelection();
Tim van der Lippe0ad77da2020-10-01 16:38:00402 if (selection) {
403 selection.selectAllChildren(newDiv);
404 }
Blink Reformat4c46d092018-04-07 15:32:37405 }
406
Jan Scheffler35199b92021-03-17 09:51:15407 isEditing(): boolean {
Jan Schefflera222c632021-08-13 12:46:15408 return Boolean(this.editing);
Blink Reformat4c46d092018-04-07 15:32:37409 }
410
Jan Schefflera222c632021-08-13 12:46:15411 private finishEditing(event: Event, canceled?: boolean): void {
Tim van der Lippe1d6e57a2019-09-30 11:55:34412 if (event) {
Blink Reformat4c46d092018-04-07 15:32:37413 event.consume(canceled);
Tim van der Lippe1d6e57a2019-09-30 11:55:34414 }
Blink Reformat4c46d092018-04-07 15:32:37415
Jan Schefflera222c632021-08-13 12:46:15416 this.editing = false;
417 this.treeElementInternal.setDisableSelectFocus(false);
418 this.treeElementInternal.listItemElement.classList.remove('watch-expression-editing');
419 if (this.textPrompt) {
420 this.textPrompt.detach();
421 const newExpression = canceled ? this.expressionInternal : this.textPrompt.text();
422 this.textPrompt = undefined;
423 this.element.removeChildren();
424 this.updateExpression(newExpression);
Tim van der Lippe0ad77da2020-10-01 16:38:00425 }
Blink Reformat4c46d092018-04-07 15:32:37426 }
427
Jan Schefflera222c632021-08-13 12:46:15428 private dblClickOnWatchExpression(event: Event): void {
Blink Reformat4c46d092018-04-07 15:32:37429 event.consume();
Tim van der Lippe1d6e57a2019-09-30 11:55:34430 if (!this.isEditing()) {
Blink Reformat4c46d092018-04-07 15:32:37431 this.startEditing();
Tim van der Lippe1d6e57a2019-09-30 11:55:34432 }
Blink Reformat4c46d092018-04-07 15:32:37433 }
434
Jan Schefflera222c632021-08-13 12:46:15435 private updateExpression(newExpression: string|null): void {
436 if (this.expressionInternal) {
437 this.expandController.stopWatchSectionsWithId(this.expressionInternal);
Tim van der Lippe1d6e57a2019-09-30 11:55:34438 }
Jan Schefflera222c632021-08-13 12:46:15439 this.expressionInternal = newExpression;
Blink Reformat4c46d092018-04-07 15:32:37440 this.update();
Benedikt Meurere02230c2021-09-09 13:57:22441 this.dispatchEventToListeners(Events.ExpressionUpdated, this);
Blink Reformat4c46d092018-04-07 15:32:37442 }
443
Jan Schefflera222c632021-08-13 12:46:15444 private deleteWatchExpression(event: Event): void {
Blink Reformat4c46d092018-04-07 15:32:37445 event.consume(true);
Jan Schefflera222c632021-08-13 12:46:15446 this.updateExpression(null);
Blink Reformat4c46d092018-04-07 15:32:37447 }
448
Jan Schefflera222c632021-08-13 12:46:15449 private createWatchExpression(
450 result?: SDK.RemoteObject.RemoteObject, exceptionDetails?: Protocol.Runtime.ExceptionDetails): void {
451 this.result = result || null;
Blink Reformat4c46d092018-04-07 15:32:37452
Jan Schefflera222c632021-08-13 12:46:15453 this.element.removeChildren();
454 const oldTreeElement = this.treeElementInternal;
455 this.createWatchExpressionTreeElement(result, exceptionDetails);
Jack Lynch5db2a3e2019-11-05 19:12:31456 if (oldTreeElement && oldTreeElement.parent) {
457 const root = oldTreeElement.parent;
458 const index = root.indexOfChild(oldTreeElement);
459 root.removeChild(oldTreeElement);
Jan Schefflera222c632021-08-13 12:46:15460 root.insertChild(this.treeElementInternal, index);
Jack Lynch5db2a3e2019-11-05 19:12:31461 }
Jan Schefflera222c632021-08-13 12:46:15462 this.treeElementInternal.select();
Jack Lynch5db2a3e2019-11-05 19:12:31463 }
464
Jan Schefflera222c632021-08-13 12:46:15465 private createWatchExpressionHeader(
Jan Scheffler35199b92021-03-17 09:51:15466 expressionValue?: SDK.RemoteObject.RemoteObject, exceptionDetails?: Protocol.Runtime.ExceptionDetails): Element {
Jan Schefflera222c632021-08-13 12:46:15467 const headerElement = this.element.createChild('div', 'watch-expression-header');
Benedikt Meurerd8a95652024-01-04 08:29:17468 const deleteButton = new Buttons.Button.Button();
Kateryna Prokopenko0895c172024-04-17 09:49:13469 deleteButton.variant = Buttons.Button.Variant.ICON;
Benedikt Meurerd8a95652024-01-04 08:29:17470 deleteButton.iconName = 'bin';
471 deleteButton.className = 'watch-expression-delete-button';
472 deleteButton.jslogContext = 'delete-watch-expression';
473 deleteButton.size = Buttons.Button.Size.SMALL;
Vidal Guillermo Diazleal Ortega83edb472021-02-16 18:39:32474 UI.Tooltip.Tooltip.install(deleteButton, i18nString(UIStrings.deleteWatchExpression));
Jan Schefflera222c632021-08-13 12:46:15475 deleteButton.addEventListener('click', this.deleteWatchExpression.bind(this), false);
Jayson Chen377e4752024-04-17 19:56:43476 deleteButton.addEventListener('keydown', event => {
477 if (event.key === 'Enter') {
478 this.deleteWatchExpression(event);
479 }
480 });
Blink Reformat4c46d092018-04-07 15:32:37481
Jack Lynch5db2a3e2019-11-05 19:12:31482 const titleElement = headerElement.createChild('div', 'watch-expression-title tree-element-title');
Jack Lynche1767de2020-04-22 02:43:55483 titleElement.appendChild(deleteButton);
Jan Schefflera222c632021-08-13 12:46:15484 this.nameElement =
485 ObjectUI.ObjectPropertiesSection.ObjectPropertiesSection.createNameElement(this.expressionInternal);
Benedikt Meurer17324302023-12-20 13:00:13486 UI.Tooltip.Tooltip.install(this.nameElement as HTMLElement, this.expressionInternal);
Tim van der Lipped7cfd142021-01-07 12:17:24487 if (Boolean(exceptionDetails) || !expressionValue) {
Jan Schefflera222c632021-08-13 12:46:15488 this.valueElement = document.createElement('span');
489 this.valueElement.classList.add('watch-expression-error');
490 this.valueElement.classList.add('value');
Blink Reformat4c46d092018-04-07 15:32:37491 titleElement.classList.add('dimmed');
Jan Schefflera222c632021-08-13 12:46:15492 this.valueElement.textContent = i18nString(UIStrings.notAvailable);
Philip Pfaffe7426b002020-11-30 15:59:22493 if (exceptionDetails !== undefined && exceptionDetails.exception !== undefined &&
494 exceptionDetails.exception.description !== undefined) {
Jan Schefflera222c632021-08-13 12:46:15495 UI.Tooltip.Tooltip.install(this.valueElement as HTMLElement, exceptionDetails.exception.description);
Philip Pfaffe7426b002020-11-30 15:59:22496 }
Blink Reformat4c46d092018-04-07 15:32:37497 } else {
Tim van der Lippefbbf9812020-02-13 14:43:46498 const propertyValue =
499 ObjectUI.ObjectPropertiesSection.ObjectPropertiesSection.createPropertyValueWithCustomSupport(
Jan Schefflera222c632021-08-13 12:46:15500 expressionValue, Boolean(exceptionDetails), false /* showPreview */, titleElement, this.linkifier);
501 this.valueElement = propertyValue.element;
Blink Reformat4c46d092018-04-07 15:32:37502 }
Tim van der Lippef49e2322020-05-01 15:03:09503 const separatorElement = document.createElement('span');
504 separatorElement.classList.add('watch-expressions-separator');
Blink Reformat4c46d092018-04-07 15:32:37505 separatorElement.textContent = ': ';
Jan Schefflera222c632021-08-13 12:46:15506 titleElement.append(this.nameElement, separatorElement, this.valueElement);
Blink Reformat4c46d092018-04-07 15:32:37507
Jack Lynch5db2a3e2019-11-05 19:12:31508 return headerElement;
509 }
Blink Reformat4c46d092018-04-07 15:32:37510
Jan Schefflera222c632021-08-13 12:46:15511 private createWatchExpressionTreeElement(
Jan Scheffler35199b92021-03-17 09:51:15512 expressionValue?: SDK.RemoteObject.RemoteObject, exceptionDetails?: Protocol.Runtime.ExceptionDetails): void {
Jan Schefflera222c632021-08-13 12:46:15513 const headerElement = this.createWatchExpressionHeader(expressionValue, exceptionDetails);
Jack Lynch5db2a3e2019-11-05 19:12:31514
515 if (!exceptionDetails && expressionValue && expressionValue.hasChildren && !expressionValue.customPreview()) {
516 headerElement.classList.add('watch-expression-object-header');
Jan Schefflera222c632021-08-13 12:46:15517 this.treeElementInternal = new ObjectUI.ObjectPropertiesSection.RootElement(expressionValue, this.linkifier);
518 this.expandController.watchSection(
519 (this.expressionInternal as string),
520 (this.treeElementInternal as ObjectUI.ObjectPropertiesSection.RootElement));
521 this.treeElementInternal.toggleOnClick = false;
522 this.treeElementInternal.listItemElement.addEventListener('click', this.onSectionClick.bind(this), false);
523 this.treeElementInternal.listItemElement.addEventListener('dblclick', this.dblClickOnWatchExpression.bind(this));
Jack Lynch5db2a3e2019-11-05 19:12:31524 } else {
Jan Schefflera222c632021-08-13 12:46:15525 headerElement.addEventListener('dblclick', this.dblClickOnWatchExpression.bind(this));
526 this.treeElementInternal = new UI.TreeOutline.TreeElement();
Jack Lynch5db2a3e2019-11-05 19:12:31527 }
Jan Schefflera222c632021-08-13 12:46:15528 this.treeElementInternal.title = this.element;
529 this.treeElementInternal.listItemElement.classList.add('watch-expression-tree-item');
530 this.treeElementInternal.listItemElement.addEventListener('keydown', event => {
Tim van der Lippebcd6b5c2021-01-13 12:31:51531 if (event.key === 'Enter' && !this.isEditing()) {
Jack Lynch5db2a3e2019-11-05 19:12:31532 this.startEditing();
533 event.consume(true);
Jaroslav Sevcik9b05a522022-11-30 15:43:21534 } else if (event.key === 'Delete' && !this.isEditing()) {
535 this.deleteWatchExpression(event);
Jack Lynch5db2a3e2019-11-05 19:12:31536 }
537 });
Blink Reformat4c46d092018-04-07 15:32:37538 }
539
Jan Schefflera222c632021-08-13 12:46:15540 private onSectionClick(event: Event): void {
Blink Reformat4c46d092018-04-07 15:32:37541 event.consume(true);
Jan Scheffler35199b92021-03-17 09:51:15542 const mouseEvent = (event as MouseEvent);
Tim van der Lippe0ad77da2020-10-01 16:38:00543 if (mouseEvent.detail === 1) {
Jan Schefflera222c632021-08-13 12:46:15544 this.preventClickTimeout = window.setTimeout(handleClick.bind(this), 333);
545 } else if (this.preventClickTimeout !== undefined) {
546 window.clearTimeout(this.preventClickTimeout);
547 this.preventClickTimeout = undefined;
Blink Reformat4c46d092018-04-07 15:32:37548 }
549
Jan Scheffler35199b92021-03-17 09:51:15550 function handleClick(this: WatchExpression): void {
Jan Schefflera222c632021-08-13 12:46:15551 if (!this.treeElementInternal) {
Blink Reformat4c46d092018-04-07 15:32:37552 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34553 }
Blink Reformat4c46d092018-04-07 15:32:37554
Jan Schefflera222c632021-08-13 12:46:15555 if (this.treeElementInternal.expanded) {
556 this.treeElementInternal.collapse();
557 } else if (!this.editing) {
558 this.treeElementInternal.expand();
Tim van der Lippe1d6e57a2019-09-30 11:55:34559 }
Blink Reformat4c46d092018-04-07 15:32:37560 }
561 }
562
Jan Schefflera222c632021-08-13 12:46:15563 private promptKeyDown(event: KeyboardEvent): void {
Jack Frankline24bc6f2022-10-07 10:36:18564 const isEscapeKey = Platform.KeyboardUtilities.isEscKey(event);
565 if (event.key === 'Enter' || isEscapeKey) {
566 this.finishEditing(event, isEscapeKey);
Tim van der Lippe1d6e57a2019-09-30 11:55:34567 }
Blink Reformat4c46d092018-04-07 15:32:37568 }
569
Jan Schefflera222c632021-08-13 12:46:15570 populateContextMenu(contextMenu: UI.ContextMenu.ContextMenu, event: Event): void {
Blink Reformat4c46d092018-04-07 15:32:37571 if (!this.isEditing()) {
572 contextMenu.editSection().appendItem(
Simon Zünd00093852023-11-28 08:29:18573 i18nString(UIStrings.deleteWatchExpression), this.updateExpression.bind(this, null),
574 {jslogContext: 'delete-watch-expression'});
Blink Reformat4c46d092018-04-07 15:32:37575 }
576
Jan Schefflera222c632021-08-13 12:46:15577 if (!this.isEditing() && this.result && (this.result.type === 'number' || this.result.type === 'string')) {
Tim van der Lippefbbf9812020-02-13 14:43:46578 contextMenu.clipboardSection().appendItem(
Simon Zünd00093852023-11-28 08:29:18579 i18nString(UIStrings.copyValue), this.copyValueButtonClicked.bind(this),
580 {jslogContext: 'copy-watch-expression-value'});
Tim van der Lippe1d6e57a2019-09-30 11:55:34581 }
Blink Reformat4c46d092018-04-07 15:32:37582
Sigurd Schneider090c3b62020-11-10 10:26:49583 const target = UI.UIUtils.deepElementFromEvent(event);
Jan Schefflera222c632021-08-13 12:46:15584 if (target && this.valueElement.isSelfOrAncestor(target) && this.result) {
585 contextMenu.appendApplicableItems(this.result);
Tim van der Lippe1d6e57a2019-09-30 11:55:34586 }
Blink Reformat4c46d092018-04-07 15:32:37587 }
588
Jan Schefflera222c632021-08-13 12:46:15589 private copyValueButtonClicked(): void {
590 Host.InspectorFrontendHost.InspectorFrontendHostInstance.copyText(this.valueElement.textContent);
Blink Reformat4c46d092018-04-07 15:32:37591 }
Jan Scheffler35199b92021-03-17 09:51:15592
Sigurd Schneider8641b422021-07-16 06:13:35593 private static readonly watchObjectGroupId = 'watch-group';
Tim van der Lippe8987f8f2020-01-03 15:03:16594}
Blink Reformat4c46d092018-04-07 15:32:37595
Benedikt Meurere02230c2021-09-09 13:57:22596const enum Events {
597 ExpressionUpdated = 'ExpressionUpdated',
Jan Scheffler35199b92021-03-17 09:51:15598}
Benedikt Meurere02230c2021-09-09 13:57:22599
600type EventTypes = {
601 [Events.ExpressionUpdated]: WatchExpression,
602};