chrome-devtools-frontend 1.0.1646286 → 1.0.1649421
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SECURITY.md +1 -1
- package/extension-api/ExtensionAPI.d.ts +26 -0
- package/front_end/core/common/Debouncer.ts +9 -1
- package/front_end/core/sdk/CSSMatchedStyles.ts +3 -1
- package/front_end/core/sdk/CSSMetadata.ts +1 -0
- package/front_end/core/sdk/CSSPropertyParserMatchers.ts +65 -0
- package/front_end/generated/InspectorBackendCommands.ts +2 -1
- package/front_end/generated/SupportedCSSProperties.js +739 -21
- package/front_end/generated/protocol.ts +26 -0
- package/front_end/models/ai_assistance/AiAgent2.ts +14 -9
- package/front_end/models/ai_assistance/agents/AiAgent.ts +10 -3
- package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +7 -0
- package/front_end/models/ai_assistance/agents/StorageAgent.ts +45 -0
- package/front_end/models/ai_assistance/ai_assistance.ts +4 -0
- package/front_end/models/ai_assistance/skills/Skill.ts +1 -1
- package/front_end/models/ai_assistance/skills/SkillRegistry.ts +2 -0
- package/front_end/models/ai_assistance/skills/network.md +16 -0
- package/front_end/models/ai_assistance/skills/styling.md +1 -1
- package/front_end/models/ai_assistance/tools/GetNetworkRequestDetails.ts +118 -0
- package/front_end/models/ai_assistance/tools/ListNetworkRequests.ts +124 -0
- package/front_end/models/ai_assistance/tools/Tool.ts +2 -0
- package/front_end/models/ai_assistance/tools/ToolRegistry.ts +4 -0
- package/front_end/models/emulation/EmulatedDevices.ts +430 -12
- package/front_end/models/extensions/ExtensionAPI.ts +47 -21
- package/front_end/models/javascript_metadata/NativeFunctions.js +5 -1
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +40 -1
- package/front_end/panels/ai_assistance/ai_assistance.ts +1 -0
- package/front_end/panels/ai_assistance/components/AIv2MarkdownRenderer.ts +228 -0
- package/front_end/panels/ai_assistance/components/ChatMessage.ts +39 -2
- package/front_end/panels/application/ApplicationPanelSidebar.ts +4 -0
- package/front_end/panels/application/CookieItemsView.ts +55 -6
- package/front_end/panels/application/DOMStorageItemsView.ts +43 -8
- package/front_end/panels/application/KeyValueStorageItemsView.ts +17 -9
- package/front_end/panels/application/WebMCPTreeElement.ts +8 -0
- package/front_end/panels/elements/StylePropertyTreeElement.ts +73 -16
- package/front_end/panels/elements/StylesAiCodeCompletionProvider.ts +9 -1
- package/front_end/panels/elements/StylesSidebarPane.ts +22 -8
- package/front_end/panels/layer_viewer/PaintProfilerView.ts +309 -182
- package/front_end/panels/layer_viewer/paintProfiler.css +27 -23
- package/front_end/panels/layers/LayerPaintProfilerView.ts +86 -29
- package/front_end/panels/sources/WatchExpressionsSidebarPane.ts +210 -356
- package/front_end/panels/sources/watchExpressionsSidebarPane.css +7 -0
- package/front_end/panels/timeline/TimelinePaintProfilerView.ts +5 -5
- package/front_end/panels/timeline/components/NetworkTrackWidget.ts +122 -0
- package/front_end/panels/timeline/components/components.ts +2 -0
- package/front_end/panels/timeline/components/networkTrackWidget.css +31 -0
- package/front_end/panels/timeline/timeline.ts +2 -0
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/ui/components/buttons/FloatingButton.ts +17 -2
- package/front_end/ui/components/buttons/floatingButton.css +2 -2
- package/front_end/ui/components/text_editor/AiCodeCompletionProvider.ts +1 -1
- package/front_end/ui/legacy/TextPrompt.ts +24 -18
- package/front_end/ui/legacy/Treeoutline.ts +31 -4
- package/front_end/ui/legacy/components/cookie_table/CookiesTable.ts +50 -24
- package/front_end/ui/legacy/components/utils/Linkifier.ts +10 -0
- package/front_end/ui/visual_logging/KnownContextValues.ts +13 -0
- package/package.json +1 -1
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// Copyright 2021 The Chromium Authors
|
|
2
2
|
// Use of this source code is governed by a BSD-style license that can be
|
|
3
3
|
// found in the LICENSE file.
|
|
4
|
-
/* eslint-disable @devtools/no-imperative-dom-api */
|
|
5
4
|
|
|
6
5
|
/*
|
|
7
6
|
* Copyright (C) IBM Corp. 2009 All rights reserved.
|
|
@@ -43,13 +42,12 @@ import * as Formatter from '../../models/formatter/formatter.js';
|
|
|
43
42
|
import * as SourceMapScopes from '../../models/source_map_scopes/source_map_scopes.js';
|
|
44
43
|
import * as StackTrace from '../../models/stack_trace/stack_trace.js';
|
|
45
44
|
import * as Buttons from '../../ui/components/buttons/buttons.js';
|
|
46
|
-
import * as TextEditor from '../../ui/components/text_editor/text_editor.js';
|
|
47
45
|
import * as ObjectUI from '../../ui/legacy/components/object_ui/object_ui.js';
|
|
48
46
|
// eslint-disable-next-line @devtools/es-modules-import
|
|
49
47
|
import objectValueStyles from '../../ui/legacy/components/object_ui/objectValue.css.js';
|
|
50
48
|
import * as Components from '../../ui/legacy/components/utils/utils.js';
|
|
51
49
|
import * as UI from '../../ui/legacy/legacy.js';
|
|
52
|
-
import {Directives, html, render, type TemplateResult} from '../../ui/lit/lit.js';
|
|
50
|
+
import {Directives, html, nothing, render, type TemplateResult} from '../../ui/lit/lit.js';
|
|
53
51
|
import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
|
|
54
52
|
|
|
55
53
|
import {UISourceCodeFrame} from './UISourceCodeFrame.js';
|
|
@@ -88,26 +86,27 @@ const UIStrings = {
|
|
|
88
86
|
* @description A context menu item in the Watch Expressions Sidebar Pane of the Sources panel and Network pane request.
|
|
89
87
|
*/
|
|
90
88
|
copyValue: 'Copy value',
|
|
91
|
-
/**
|
|
92
|
-
* @description announcement for when watch expression is deleted
|
|
93
|
-
*/
|
|
94
|
-
watchExpressionDeleted: 'Watch expression deleted',
|
|
95
89
|
} as const;
|
|
96
90
|
const str_ = i18n.i18n.registerUIStrings('panels/sources/WatchExpressionsSidebarPane.ts', UIStrings);
|
|
97
91
|
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
|
98
92
|
let watchExpressionsSidebarPaneInstance: WatchExpressionsSidebarPane;
|
|
99
|
-
const {
|
|
93
|
+
const {classMap, ifDefined} = Directives;
|
|
100
94
|
interface ViewInput {
|
|
95
|
+
linkifier: Components.Linkifier.Linkifier;
|
|
96
|
+
onFinishEditing(watchExpression: WatchExpression, detail: string|null): void;
|
|
97
|
+
onStartEditing(watchExpression: WatchExpression): void;
|
|
98
|
+
onDelete(watchExpression: WatchExpression): void;
|
|
101
99
|
onCopyWatchExpression(watchExpression: WatchExpression): void;
|
|
102
100
|
onDeleteAll(): unknown;
|
|
103
101
|
onAddExpression(): unknown;
|
|
102
|
+
onExpand(e: WatchExpression, expanded: boolean): unknown;
|
|
104
103
|
watchExpressions: WatchExpression[];
|
|
105
104
|
}
|
|
106
105
|
type View = (input: ViewInput, output: object, target: HTMLElement) => void;
|
|
107
106
|
export const DEFAULT_VIEW: View = (input, output, target) => {
|
|
108
107
|
const onContextMenu = (watchExpression: WatchExpression|undefined, event: Event): void => {
|
|
109
108
|
const contextMenu = new UI.ContextMenu.ContextMenu(event);
|
|
110
|
-
const isEditing = input.watchExpressions.some(e => e.
|
|
109
|
+
const isEditing = input.watchExpressions.some(e => e.editing);
|
|
111
110
|
|
|
112
111
|
if (!isEditing) {
|
|
113
112
|
contextMenu.debugSection().appendItem(
|
|
@@ -121,14 +120,14 @@ export const DEFAULT_VIEW: View = (input, output, target) => {
|
|
|
121
120
|
}
|
|
122
121
|
|
|
123
122
|
if (watchExpression) {
|
|
124
|
-
if (!watchExpression.
|
|
125
|
-
contextMenu.editSection().appendItem(
|
|
126
|
-
|
|
127
|
-
|
|
123
|
+
if (!watchExpression.editing) {
|
|
124
|
+
contextMenu.editSection().appendItem(i18nString(UIStrings.deleteWatchExpression),
|
|
125
|
+
() => input.onDelete(watchExpression),
|
|
126
|
+
{jslogContext: 'delete-watch-expression'});
|
|
128
127
|
}
|
|
129
128
|
|
|
130
|
-
if (!watchExpression.
|
|
131
|
-
(watchExpression.result.type === 'number' || watchExpression.result.type === 'string')) {
|
|
129
|
+
if (!watchExpression.editing && watchExpression.result &&
|
|
130
|
+
(watchExpression.result.object.type === 'number' || watchExpression.result.object.type === 'string')) {
|
|
132
131
|
contextMenu.clipboardSection().appendItem(
|
|
133
132
|
i18nString(UIStrings.copyValue), () => input.onCopyWatchExpression(watchExpression),
|
|
134
133
|
{jslogContext: 'copy-watch-expression-value'});
|
|
@@ -140,10 +139,78 @@ export const DEFAULT_VIEW: View = (input, output, target) => {
|
|
|
140
139
|
event.consume();
|
|
141
140
|
};
|
|
142
141
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
142
|
+
const onExpressionKeydown = (expression: WatchExpression, event: KeyboardEvent): void => {
|
|
143
|
+
if (event.key === 'Enter' && !expression.editing) {
|
|
144
|
+
event.consume(true);
|
|
145
|
+
input.onStartEditing(expression);
|
|
146
|
+
} else if (event.key === 'Delete' && !expression.editing) {
|
|
147
|
+
event.consume(true);
|
|
148
|
+
input.onDelete(expression);
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const renderNameElement = (e: WatchExpression): Element => {
|
|
153
|
+
const nameElement = ObjectUI.ObjectPropertiesSection.ObjectPropertiesSection.createNameElement(e.expression);
|
|
154
|
+
UI.Tooltip.Tooltip.install(nameElement as HTMLElement, e.expression);
|
|
155
|
+
return nameElement;
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const renderTreeElement = (e: WatchExpression): TemplateResult =>
|
|
159
|
+
// clang-format off
|
|
160
|
+
html`<li
|
|
161
|
+
class=${classMap({'watch-expression-tree-item': true, 'watch-expression-editing': e.editing })}
|
|
162
|
+
@keydown=${onExpressionKeydown.bind(undefined, e)}
|
|
163
|
+
@expand=${(event: UI.TreeOutline.TreeViewElement.ExpandEvent) => input.onExpand(e, event.detail.expanded)}
|
|
164
|
+
role=treeitem>
|
|
165
|
+
<devtools-prompt
|
|
166
|
+
value=${e.expression ?? ''}
|
|
167
|
+
@commit=${(event: UI.TextPrompt.TextPromptElement.CommitEvent) => input.onFinishEditing(e, event.detail)}
|
|
168
|
+
@cancel=${() => input.onFinishEditing(e, null)}
|
|
169
|
+
?editing=${e.editing}
|
|
170
|
+
@mousedown=${(event: Event) => e.editing && event.stopPropagation()}
|
|
171
|
+
@click=${(event: Event) => e.editing && event.stopPropagation()}
|
|
172
|
+
@dblclick=${(event: Event) => e.editing && event.stopPropagation()}
|
|
173
|
+
class=${classMap({monospace: true, 'watch-expression': true,
|
|
174
|
+
'watch-expression-text-prompt-proxy': e.editing})}>
|
|
175
|
+
<div class=${classMap({
|
|
176
|
+
'watch-expression-header': true,
|
|
177
|
+
'watch-expression-object-header': !e.exceptionDetails && e.result !== undefined &&
|
|
178
|
+
e.result.hasChildren && !e.result.object.customPreview(),
|
|
179
|
+
})}
|
|
180
|
+
@contextmenu=${onContextMenu.bind(undefined, e)}
|
|
181
|
+
@dblclick=${() => input.onStartEditing(e)}>
|
|
182
|
+
<div class=${classMap({'watch-expression-title': true,
|
|
183
|
+
'tree-element-title': true,
|
|
184
|
+
dimmed: Boolean(e.exceptionDetails) && !e.result})}>
|
|
185
|
+
<devtools-button
|
|
186
|
+
.data=${{
|
|
187
|
+
variant: Buttons.Button.Variant.ICON,
|
|
188
|
+
iconName: 'bin',
|
|
189
|
+
size: Buttons.Button.Size.SMALL,
|
|
190
|
+
jslogContext: 'delete-watch-expression',
|
|
191
|
+
} as Buttons.Button.ButtonData}
|
|
192
|
+
class=watch-expression-delete-button
|
|
193
|
+
title=${i18nString(UIStrings.deleteWatchExpression)}
|
|
194
|
+
@click=${() => input.onDelete(e)}></devtools-button>
|
|
195
|
+
${renderNameElement(e)}<span class=watch-expressions-separator>: </span>${e.exceptionDetails || !e.result
|
|
196
|
+
? html`<span
|
|
197
|
+
class="watch-expression-error value"
|
|
198
|
+
title=${ifDefined(e.exceptionDetails?.exception?.description)}
|
|
199
|
+
>${i18nString(UIStrings.notAvailable)}</span>`
|
|
200
|
+
: ObjectUI.ObjectPropertiesSection.ObjectPropertiesSection.createPropertyValueWithCustomSupport(
|
|
201
|
+
e.result.object, Boolean(e.exceptionDetails), false /* showPreview */, input.linkifier)}
|
|
202
|
+
</div>
|
|
203
|
+
</div>
|
|
204
|
+
</devtools-prompt>
|
|
205
|
+
${e.editing || !e.result || e.exceptionDetails ||
|
|
206
|
+
!e.result.hasChildren || e.result.object.customPreview() ? nothing : html`
|
|
207
|
+
<ul role=group>
|
|
208
|
+
${ObjectUI.ObjectPropertiesSection.ObjectPropertyTreeElement.createPropertyNodes(
|
|
209
|
+
e.result.children ?? {}, true /* skipProto */, true /* skipGettersAndSetters */).map(
|
|
210
|
+
node => html`<devtools-tree-wrapper .treeElement=${node}></devtools-tree-wrapper>`)}
|
|
211
|
+
</ul>`}
|
|
212
|
+
</li>`;
|
|
213
|
+
// clang-format on
|
|
147
214
|
|
|
148
215
|
render(
|
|
149
216
|
// clang-format off
|
|
@@ -152,13 +219,12 @@ export const DEFAULT_VIEW: View = (input, output, target) => {
|
|
|
152
219
|
? html`<div class=gray-info-message tabindex=-1 >
|
|
153
220
|
${i18nString(UIStrings.noWatchExpressions)}
|
|
154
221
|
</div>`
|
|
155
|
-
: html`<devtools-tree hide-overflow show-selection-on-keyboard-focus .template=${html`
|
|
222
|
+
: html`<devtools-tree autofocus hide-overflow show-selection-on-keyboard-focus toggle-on-click .template=${html`
|
|
156
223
|
<ul role=tree class="source-code object-properties-section">
|
|
157
224
|
<style>${ObjectUI.ObjectPropertiesSection.objectValueStyles}</style>
|
|
158
225
|
<style>${ObjectUI.ObjectPropertiesSection.objectPropertiesSectionStyles}</style>
|
|
159
226
|
<style>${watchExpressionsSidebarPaneStyles}</style>
|
|
160
|
-
${
|
|
161
|
-
e => html`<devtools-tree-wrapper .treeElement=${e.treeElement()}></devtools-tree-wrapper>`)}
|
|
227
|
+
${input.watchExpressions.map(renderTreeElement)}
|
|
162
228
|
</ul>`}>
|
|
163
229
|
</devtools-tree>`
|
|
164
230
|
}`,
|
|
@@ -181,7 +247,7 @@ export class WatchExpressionsSidebarPane extends UI.Widget.VBox implements UI.Ac
|
|
|
181
247
|
#watchExpressionsSetting: Common.Settings.Setting<string[]>;
|
|
182
248
|
private readonly linkifier: Components.Linkifier.Linkifier;
|
|
183
249
|
#view: View;
|
|
184
|
-
#
|
|
250
|
+
#expansionTrackers = new Map<string, ObjectUI.ObjectPropertiesSection.ObjectTreeExpansionTracker>();
|
|
185
251
|
constructor() {
|
|
186
252
|
super({useShadowDom: true});
|
|
187
253
|
this.registerRequiredCSS(watchExpressionsSidebarPaneStyles, objectValueStyles);
|
|
@@ -196,7 +262,7 @@ export class WatchExpressionsSidebarPane extends UI.Widget.VBox implements UI.Ac
|
|
|
196
262
|
StackTrace.StackTrace.DebuggableFrameFlavor, this.#refreshExpressions, this);
|
|
197
263
|
this.linkifier = new Components.Linkifier.Linkifier();
|
|
198
264
|
this.#view = DEFAULT_VIEW;
|
|
199
|
-
this.#refreshExpressions();
|
|
265
|
+
void this.#refreshExpressions();
|
|
200
266
|
}
|
|
201
267
|
|
|
202
268
|
static instance(): WatchExpressionsSidebarPane {
|
|
@@ -236,7 +302,7 @@ export class WatchExpressionsSidebarPane extends UI.Widget.VBox implements UI.Ac
|
|
|
236
302
|
private saveExpressions(): void {
|
|
237
303
|
const toSave = [];
|
|
238
304
|
for (let i = 0; i < this.#watchExpressions.length; i++) {
|
|
239
|
-
const expression = this.#watchExpressions[i].expression
|
|
305
|
+
const expression = this.#watchExpressions[i].expression;
|
|
240
306
|
if (expression) {
|
|
241
307
|
toSave.push(expression);
|
|
242
308
|
}
|
|
@@ -248,104 +314,117 @@ export class WatchExpressionsSidebarPane extends UI.Widget.VBox implements UI.Ac
|
|
|
248
314
|
private async addButtonClicked(event?: Event): Promise<void> {
|
|
249
315
|
event?.consume(true);
|
|
250
316
|
await UI.ViewManager.ViewManager.instance().showView('sources.watch');
|
|
251
|
-
const watchExpression =
|
|
317
|
+
const watchExpression = new WatchExpression();
|
|
318
|
+
this.#watchExpressions.push(watchExpression);
|
|
319
|
+
watchExpression.editing = true;
|
|
252
320
|
this.requestUpdate();
|
|
253
|
-
// This synchronization is only needed because startEditing is imperative and
|
|
254
|
-
// requires the tree element to be in the DOM. This can be removed once the
|
|
255
|
-
// prompt is migrated to Lit.
|
|
256
|
-
await this.updateComplete;
|
|
257
|
-
watchExpression.startEditing();
|
|
258
321
|
}
|
|
259
322
|
|
|
260
323
|
private refreshButtonClicked(event: Event): void {
|
|
261
324
|
event.consume(true);
|
|
262
|
-
this.#refreshExpressions();
|
|
325
|
+
void this.#refreshExpressions();
|
|
263
326
|
}
|
|
264
|
-
|
|
265
|
-
#refreshExpressions(): void {
|
|
327
|
+
async #refreshExpressions(): Promise<void> {
|
|
266
328
|
this.linkifier.reset();
|
|
267
|
-
for (const expression of this.#watchExpressions) {
|
|
268
|
-
expression.removeEventListener(Events.EXPRESSION_UPDATED, this.watchExpressionUpdated, this);
|
|
269
|
-
}
|
|
270
329
|
this.#watchExpressions = [];
|
|
271
330
|
const watchExpressionStrings = this.#watchExpressionsSetting.get();
|
|
272
331
|
|
|
273
|
-
const
|
|
274
|
-
this.#
|
|
332
|
+
const oldExpansionTrackers = this.#expansionTrackers;
|
|
333
|
+
this.#expansionTrackers = new Map();
|
|
275
334
|
|
|
335
|
+
const promises = [];
|
|
276
336
|
for (let i = 0; i < watchExpressionStrings.length; ++i) {
|
|
277
337
|
const expression = watchExpressionStrings[i];
|
|
278
338
|
if (!expression) {
|
|
279
339
|
continue;
|
|
280
340
|
}
|
|
281
341
|
|
|
282
|
-
const
|
|
283
|
-
if (
|
|
284
|
-
this.#
|
|
342
|
+
const tracker = oldExpansionTrackers.get(expression);
|
|
343
|
+
if (tracker) {
|
|
344
|
+
this.#expansionTrackers.set(expression, tracker);
|
|
285
345
|
}
|
|
286
346
|
|
|
287
|
-
|
|
347
|
+
const watchExpression = new WatchExpression();
|
|
348
|
+
this.#watchExpressions.push(watchExpression);
|
|
349
|
+
promises.push(watchExpression.setExpression(expression, this.#getExpansionTracker(expression)));
|
|
288
350
|
}
|
|
351
|
+
await Promise.all(promises);
|
|
289
352
|
this.requestUpdate();
|
|
290
353
|
}
|
|
291
354
|
|
|
292
355
|
override async performUpdate(): Promise<void> {
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
356
|
+
this.#view({
|
|
357
|
+
watchExpressions: this.watchExpressions,
|
|
358
|
+
linkifier: this.linkifier,
|
|
359
|
+
onDeleteAll: this.#onDeleteAll.bind(this),
|
|
360
|
+
onAddExpression: this.addButtonClicked.bind(this),
|
|
361
|
+
onCopyWatchExpression: watchExpression => {
|
|
362
|
+
if (watchExpression.result?.object.description) {
|
|
363
|
+
Host.InspectorFrontendHost.InspectorFrontendHostInstance.copyText(watchExpression.result.object.description);
|
|
364
|
+
}
|
|
365
|
+
},
|
|
366
|
+
onFinishEditing: async (watchExpression: WatchExpression, expression: string|null) => {
|
|
367
|
+
if (!watchExpression.editing) {
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
watchExpression.editing = false;
|
|
371
|
+
this.requestUpdate();
|
|
372
|
+
if (expression === '' || (expression === null && !watchExpression.expression)) {
|
|
373
|
+
Platform.ArrayUtilities.removeElement(this.#watchExpressions, watchExpression);
|
|
374
|
+
} else if (expression) {
|
|
375
|
+
if (watchExpression.expression && watchExpression.expression !== expression) {
|
|
376
|
+
this.#getExpansionTracker(watchExpression.expression).clear();
|
|
377
|
+
this.#getExpansionTracker(expression).clear();
|
|
303
378
|
}
|
|
304
|
-
|
|
305
|
-
|
|
379
|
+
await watchExpression.setExpression(expression, this.#getExpansionTracker(expression));
|
|
380
|
+
}
|
|
381
|
+
this.saveExpressions();
|
|
382
|
+
this.requestUpdate();
|
|
383
|
+
},
|
|
384
|
+
onStartEditing: (watchExpression: WatchExpression): void => {
|
|
385
|
+
watchExpression.editing = true;
|
|
386
|
+
this.requestUpdate();
|
|
387
|
+
},
|
|
388
|
+
onDelete: (watchExpression: WatchExpression): void => {
|
|
389
|
+
Platform.ArrayUtilities.removeElement(this.#watchExpressions, watchExpression);
|
|
390
|
+
this.saveExpressions();
|
|
391
|
+
this.requestUpdate();
|
|
392
|
+
},
|
|
393
|
+
onExpand: async(e: WatchExpression, expanded: boolean): Promise<void> => {
|
|
394
|
+
if (expanded) {
|
|
395
|
+
await e.result?.populateChildrenIfNeeded();
|
|
396
|
+
this.requestUpdate();
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
},
|
|
400
|
+
{}, this.contentElement);
|
|
306
401
|
}
|
|
307
402
|
|
|
308
|
-
#
|
|
403
|
+
#getExpansionTracker(expression: string): ObjectUI.ObjectPropertiesSection.ObjectTreeExpansionTracker {
|
|
309
404
|
if (expression === null) {
|
|
310
405
|
return new ObjectUI.ObjectPropertiesSection.ObjectTreeExpansionTracker();
|
|
311
406
|
}
|
|
312
|
-
let
|
|
313
|
-
if (!
|
|
314
|
-
|
|
315
|
-
this.#
|
|
407
|
+
let expansionTracker = this.#expansionTrackers.get(expression);
|
|
408
|
+
if (!expansionTracker) {
|
|
409
|
+
expansionTracker = new ObjectUI.ObjectPropertiesSection.ObjectTreeExpansionTracker();
|
|
410
|
+
this.#expansionTrackers.set(expression, expansionTracker);
|
|
316
411
|
}
|
|
317
|
-
return
|
|
412
|
+
return expansionTracker;
|
|
318
413
|
}
|
|
319
414
|
|
|
320
|
-
|
|
321
|
-
const expandController = this.#getExpandController(expression);
|
|
322
|
-
const watchExpression = new WatchExpression(expression, expandController, this.linkifier);
|
|
323
|
-
watchExpression.addEventListener(Events.EXPRESSION_UPDATED, this.watchExpressionUpdated, this);
|
|
324
|
-
this.#watchExpressions.push(watchExpression);
|
|
325
|
-
return watchExpression;
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
private watchExpressionUpdated({data: watchExpression}: Common.EventTarget.EventTargetEvent<WatchExpression>): void {
|
|
329
|
-
const expression = watchExpression.expression();
|
|
330
|
-
if (!expression) {
|
|
331
|
-
Platform.ArrayUtilities.removeElement(this.#watchExpressions, watchExpression);
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
this.saveExpressions();
|
|
335
|
-
this.requestUpdate();
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
private deleteAllButtonClicked(): void {
|
|
415
|
+
#onDeleteAll(): void {
|
|
339
416
|
this.#watchExpressions = [];
|
|
340
417
|
this.saveExpressions();
|
|
341
|
-
this.#refreshExpressions();
|
|
418
|
+
void this.#refreshExpressions();
|
|
342
419
|
}
|
|
343
420
|
|
|
344
|
-
|
|
421
|
+
async #focusAndAddExpressionToWatch(expression: string): Promise<void> {
|
|
345
422
|
await UI.ViewManager.ViewManager.instance().showView('sources.watch');
|
|
346
|
-
|
|
423
|
+
const watchExpression = new WatchExpression();
|
|
424
|
+
await watchExpression.setExpression(expression, this.#getExpansionTracker(expression));
|
|
425
|
+
this.watchExpressions.push(watchExpression);
|
|
347
426
|
this.saveExpressions();
|
|
348
|
-
this.#refreshExpressions();
|
|
427
|
+
await this.#refreshExpressions();
|
|
349
428
|
}
|
|
350
429
|
|
|
351
430
|
handleAction(_context: UI.Context.Context, _actionId: string): boolean {
|
|
@@ -355,7 +434,7 @@ export class WatchExpressionsSidebarPane extends UI.Widget.VBox implements UI.Ac
|
|
|
355
434
|
}
|
|
356
435
|
const {state} = frame.textEditor;
|
|
357
436
|
const text = state.sliceDoc(state.selection.main.from, state.selection.main.to);
|
|
358
|
-
void this
|
|
437
|
+
void this.#focusAndAddExpressionToWatch(text);
|
|
359
438
|
return true;
|
|
360
439
|
}
|
|
361
440
|
|
|
@@ -364,9 +443,9 @@ export class WatchExpressionsSidebarPane extends UI.Widget.VBox implements UI.Ac
|
|
|
364
443
|
target: ObjectUI.ObjectPropertiesSection.ObjectPropertyTreeElement|UISourceCodeFrame): void {
|
|
365
444
|
if (target instanceof ObjectUI.ObjectPropertiesSection.ObjectPropertyTreeElement) {
|
|
366
445
|
if (!target.property.property.synthetic) {
|
|
367
|
-
contextMenu.debugSection().appendItem(
|
|
368
|
-
|
|
369
|
-
|
|
446
|
+
contextMenu.debugSection().appendItem(i18nString(UIStrings.addPropertyPathToWatch),
|
|
447
|
+
() => this.#focusAndAddExpressionToWatch(target.path()),
|
|
448
|
+
{jslogContext: 'add-property-path-to-watch'});
|
|
370
449
|
}
|
|
371
450
|
return;
|
|
372
451
|
}
|
|
@@ -379,60 +458,36 @@ export class WatchExpressionsSidebarPane extends UI.Widget.VBox implements UI.Ac
|
|
|
379
458
|
}
|
|
380
459
|
}
|
|
381
460
|
|
|
382
|
-
class
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
}
|
|
461
|
+
export class WatchExpression {
|
|
462
|
+
editing = false;
|
|
463
|
+
#exceptionDetails: Protocol.Runtime.ExceptionDetails|undefined = undefined;
|
|
464
|
+
#result: ObjectUI.ObjectPropertiesSection.ObjectTree|undefined = undefined;
|
|
465
|
+
#expression: string|null = null;
|
|
388
466
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
private nameElement!: Element;
|
|
392
|
-
private valueElement!: Element;
|
|
393
|
-
#expression: string|null;
|
|
394
|
-
readonly #expandController: ObjectUI.ObjectPropertiesSection.ObjectTreeExpansionTracker;
|
|
395
|
-
private element: HTMLDivElement;
|
|
396
|
-
private editing: boolean;
|
|
397
|
-
private linkifier: Components.Linkifier.Linkifier;
|
|
398
|
-
private textPrompt?: ObjectPropertyPrompt;
|
|
399
|
-
#result?: SDK.RemoteObject.RemoteObject|null;
|
|
400
|
-
private preventClickTimeout?: number;
|
|
401
|
-
#updateComplete: Promise<void> = Promise.resolve();
|
|
402
|
-
constructor(
|
|
403
|
-
expression: string|null, expandController: ObjectUI.ObjectPropertiesSection.ObjectTreeExpansionTracker,
|
|
404
|
-
linkifier: Components.Linkifier.Linkifier) {
|
|
405
|
-
super();
|
|
406
|
-
|
|
407
|
-
this.#expression = expression;
|
|
408
|
-
this.#expandController = expandController;
|
|
409
|
-
this.element = document.createElement('div');
|
|
410
|
-
this.element.classList.add('watch-expression');
|
|
411
|
-
this.element.classList.add('monospace');
|
|
412
|
-
this.editing = false;
|
|
413
|
-
this.linkifier = linkifier;
|
|
414
|
-
|
|
415
|
-
void this.createWatchExpression();
|
|
416
|
-
this.update();
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
get result(): SDK.RemoteObject.RemoteObject|null {
|
|
420
|
-
return this.#result ?? null;
|
|
421
|
-
}
|
|
422
|
-
get updateComplete(): Promise<void> {
|
|
423
|
-
return this.#updateComplete;
|
|
467
|
+
get exceptionDetails(): Protocol.Runtime.ExceptionDetails|undefined {
|
|
468
|
+
return this.#exceptionDetails;
|
|
424
469
|
}
|
|
425
470
|
|
|
426
|
-
|
|
427
|
-
return this.#
|
|
471
|
+
get result(): ObjectUI.ObjectPropertiesSection.ObjectTree|undefined {
|
|
472
|
+
return this.#result;
|
|
428
473
|
}
|
|
429
474
|
|
|
430
|
-
expression(): string|null {
|
|
475
|
+
get expression(): string|null {
|
|
431
476
|
return this.#expression;
|
|
432
477
|
}
|
|
433
478
|
|
|
434
|
-
async
|
|
435
|
-
|
|
479
|
+
async setExpression(
|
|
480
|
+
expression: string,
|
|
481
|
+
expandController: ObjectUI.ObjectPropertiesSection.ObjectTreeExpansionTracker,
|
|
482
|
+
): Promise<void> {
|
|
483
|
+
this.#exceptionDetails = this.#result = undefined;
|
|
484
|
+
this.#expression = expression;
|
|
485
|
+
|
|
486
|
+
const executionContext = UI.Context.Context.instance().flavor(SDK.RuntimeModel.ExecutionContext);
|
|
487
|
+
if (!expression || !executionContext) {
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
|
|
436
491
|
const callFrame = executionContext.debuggerModel.selectedCallFrame();
|
|
437
492
|
if (callFrame?.script.isJavaScript()) {
|
|
438
493
|
const nameMap = await SourceMapScopes.NamesResolver.allVariablesInCallFrame(callFrame);
|
|
@@ -443,231 +498,30 @@ export class WatchExpression extends Common.ObjectWrapper.ObjectWrapper<EventTyp
|
|
|
443
498
|
}
|
|
444
499
|
}
|
|
445
500
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
} else {
|
|
467
|
-
await this.createWatchExpression();
|
|
468
|
-
}
|
|
469
|
-
})
|
|
470
|
-
.catch(() => {});
|
|
471
|
-
} else {
|
|
472
|
-
this.#updateComplete = this.createWatchExpression();
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
startEditing(): void {
|
|
477
|
-
this.editing = true;
|
|
478
|
-
this.#treeElement.setDisableSelectFocus(true);
|
|
479
|
-
this.element.removeChildren();
|
|
480
|
-
const newDiv = this.element.createChild('div');
|
|
481
|
-
newDiv.textContent = this.nameElement.textContent;
|
|
482
|
-
this.textPrompt = new ObjectPropertyPrompt();
|
|
483
|
-
this.textPrompt.renderAsBlock();
|
|
484
|
-
const proxyElement = (this.textPrompt.attachAndStartEditing(newDiv, this.finishEditing.bind(this)) as HTMLElement);
|
|
485
|
-
this.#treeElement.listItemElement.classList.add('watch-expression-editing');
|
|
486
|
-
this.#treeElement.collapse();
|
|
487
|
-
proxyElement.classList.add('watch-expression-text-prompt-proxy');
|
|
488
|
-
proxyElement.addEventListener('keydown', this.promptKeyDown.bind(this), false);
|
|
489
|
-
const selection = this.element.getComponentSelection();
|
|
490
|
-
if (selection) {
|
|
491
|
-
selection.selectAllChildren(newDiv);
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
isEditing(): boolean {
|
|
496
|
-
return Boolean(this.editing);
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
private finishEditing(event: Event, canceled?: boolean): void {
|
|
500
|
-
if (event) {
|
|
501
|
-
event.consume(canceled);
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
this.editing = false;
|
|
505
|
-
this.#treeElement.setDisableSelectFocus(false);
|
|
506
|
-
this.#treeElement.listItemElement.classList.remove('watch-expression-editing');
|
|
507
|
-
if (this.textPrompt) {
|
|
508
|
-
const text = this.textPrompt.text();
|
|
509
|
-
this.textPrompt.detach();
|
|
510
|
-
const newExpression = canceled ? this.#expression : text;
|
|
511
|
-
this.textPrompt = undefined;
|
|
512
|
-
this.element.removeChildren();
|
|
513
|
-
this.updateExpression(newExpression);
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
private dblClickOnWatchExpression(event: Event): void {
|
|
518
|
-
event.consume();
|
|
519
|
-
if (!this.isEditing()) {
|
|
520
|
-
this.startEditing();
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
updateExpression(newExpression: string|null): void {
|
|
525
|
-
if (this.#expression !== newExpression && newExpression) {
|
|
526
|
-
this.#expandController.clear();
|
|
527
|
-
}
|
|
528
|
-
this.#expression = newExpression;
|
|
529
|
-
this.update();
|
|
530
|
-
this.dispatchEventToListeners(Events.EXPRESSION_UPDATED, this);
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
private deleteWatchExpression(event: Event): void {
|
|
534
|
-
event.consume(true);
|
|
535
|
-
UI.ARIAUtils.LiveAnnouncer.alert(i18nString(UIStrings.watchExpressionDeleted));
|
|
536
|
-
this.updateExpression(null);
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
async createWatchExpression(
|
|
540
|
-
result?: SDK.RemoteObject.RemoteObject, exceptionDetails?: Protocol.Runtime.ExceptionDetails): Promise<void> {
|
|
541
|
-
this.#result = result || null;
|
|
542
|
-
|
|
543
|
-
this.element.removeChildren();
|
|
544
|
-
const oldTreeElement = this.#treeElement;
|
|
545
|
-
await this.#createWatchExpressionTreeElement(result, exceptionDetails);
|
|
546
|
-
if (oldTreeElement?.parent) {
|
|
547
|
-
const root = oldTreeElement.parent;
|
|
548
|
-
const index = root.indexOfChild(oldTreeElement);
|
|
549
|
-
root.removeChild(oldTreeElement);
|
|
550
|
-
root.insertChild(this.#treeElement, index);
|
|
551
|
-
}
|
|
552
|
-
this.#treeElement.select();
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
private createWatchExpressionHeader(
|
|
556
|
-
expressionValue?: SDK.RemoteObject.RemoteObject, exceptionDetails?: Protocol.Runtime.ExceptionDetails): Element {
|
|
557
|
-
const headerElement = this.element.createChild('div', 'watch-expression-header');
|
|
558
|
-
const deleteButton = new Buttons.Button.Button();
|
|
559
|
-
deleteButton.data = {
|
|
560
|
-
variant: Buttons.Button.Variant.ICON,
|
|
561
|
-
iconName: 'bin',
|
|
562
|
-
size: Buttons.Button.Size.SMALL,
|
|
563
|
-
jslogContext: 'delete-watch-expression',
|
|
564
|
-
};
|
|
565
|
-
deleteButton.className = 'watch-expression-delete-button';
|
|
566
|
-
UI.Tooltip.Tooltip.install(deleteButton, i18nString(UIStrings.deleteWatchExpression));
|
|
567
|
-
deleteButton.addEventListener('click', this.deleteWatchExpression.bind(this), false);
|
|
568
|
-
deleteButton.addEventListener('keydown', event => {
|
|
569
|
-
if (event.key === 'Enter') {
|
|
570
|
-
this.deleteWatchExpression(event);
|
|
571
|
-
}
|
|
572
|
-
});
|
|
573
|
-
|
|
574
|
-
const titleElement = headerElement.createChild('div', 'watch-expression-title tree-element-title');
|
|
575
|
-
titleElement.appendChild(deleteButton);
|
|
576
|
-
this.nameElement = ObjectUI.ObjectPropertiesSection.ObjectPropertiesSection.createNameElement(this.#expression);
|
|
577
|
-
UI.Tooltip.Tooltip.install(this.nameElement as HTMLElement, this.#expression);
|
|
578
|
-
if (Boolean(exceptionDetails) || !expressionValue) {
|
|
579
|
-
this.valueElement = document.createElement('span');
|
|
580
|
-
this.valueElement.classList.add('watch-expression-error');
|
|
581
|
-
this.valueElement.classList.add('value');
|
|
582
|
-
titleElement.classList.add('dimmed');
|
|
583
|
-
this.valueElement.textContent = i18nString(UIStrings.notAvailable);
|
|
584
|
-
if (exceptionDetails?.exception?.description !== undefined) {
|
|
585
|
-
UI.Tooltip.Tooltip.install(this.valueElement as HTMLElement, exceptionDetails.exception.description);
|
|
586
|
-
}
|
|
587
|
-
} else {
|
|
588
|
-
const propertyValue =
|
|
589
|
-
ObjectUI.ObjectPropertiesSection.ObjectPropertiesSection.createPropertyValueWithCustomSupport(
|
|
590
|
-
expressionValue, Boolean(exceptionDetails), false /* showPreview */, this.linkifier);
|
|
591
|
-
this.valueElement = propertyValue;
|
|
592
|
-
}
|
|
593
|
-
const separatorElement = document.createElement('span');
|
|
594
|
-
separatorElement.classList.add('watch-expressions-separator');
|
|
595
|
-
separatorElement.textContent = ': ';
|
|
596
|
-
titleElement.append(this.nameElement, separatorElement, this.valueElement);
|
|
597
|
-
|
|
598
|
-
return headerElement;
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
async #createWatchExpressionTreeElement(
|
|
602
|
-
expressionValue?: SDK.RemoteObject.RemoteObject,
|
|
603
|
-
exceptionDetails?: Protocol.Runtime.ExceptionDetails): Promise<void> {
|
|
604
|
-
const headerElement = this.createWatchExpressionHeader(expressionValue, exceptionDetails);
|
|
605
|
-
|
|
606
|
-
if (!exceptionDetails && expressionValue && expressionValue.hasChildren && !expressionValue.customPreview()) {
|
|
607
|
-
headerElement.classList.add('watch-expression-object-header');
|
|
608
|
-
const objectTree = new ObjectUI.ObjectPropertiesSection.ObjectTree(expressionValue, {
|
|
609
|
-
readOnly: true,
|
|
610
|
-
propertiesMode: ObjectUI.ObjectPropertiesSection.ObjectPropertiesMode.OWN_AND_INTERNAL_AND_INHERITED,
|
|
611
|
-
expansionTracker: this.#expandController,
|
|
612
|
-
});
|
|
613
|
-
await this.#expandController.apply(objectTree);
|
|
614
|
-
this.#treeElement = new ObjectUI.ObjectPropertiesSection.RootElement(objectTree, this.linkifier);
|
|
615
|
-
this.#treeElement.toggleOnClick = false;
|
|
616
|
-
this.#treeElement.listItemElement.addEventListener('click', this.onSectionClick.bind(this), false);
|
|
617
|
-
this.#treeElement.listItemElement.addEventListener('dblclick', this.dblClickOnWatchExpression.bind(this));
|
|
618
|
-
} else {
|
|
619
|
-
headerElement.addEventListener('dblclick', this.dblClickOnWatchExpression.bind(this));
|
|
620
|
-
this.#treeElement = new UI.TreeOutline.TreeElement();
|
|
621
|
-
}
|
|
622
|
-
this.#treeElement.title = this.element;
|
|
623
|
-
this.#treeElement.listItemElement.classList.add('watch-expression-tree-item');
|
|
624
|
-
this.#treeElement.listItemElement.addEventListener('keydown', event => {
|
|
625
|
-
if (event.key === 'Enter' && !this.isEditing()) {
|
|
626
|
-
this.startEditing();
|
|
627
|
-
event.consume(true);
|
|
628
|
-
} else if (event.key === 'Delete' && !this.isEditing()) {
|
|
629
|
-
this.deleteWatchExpression(event);
|
|
630
|
-
}
|
|
631
|
-
});
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
private onSectionClick(event: MouseEvent): void {
|
|
635
|
-
event.consume(true);
|
|
636
|
-
const mouseEvent = event;
|
|
637
|
-
if (mouseEvent.detail === 1) {
|
|
638
|
-
this.preventClickTimeout = window.setTimeout(handleClick.bind(this), 333);
|
|
639
|
-
} else if (this.preventClickTimeout !== undefined) {
|
|
640
|
-
window.clearTimeout(this.preventClickTimeout);
|
|
641
|
-
this.preventClickTimeout = undefined;
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
function handleClick(this: WatchExpression): void {
|
|
645
|
-
if (!this.#treeElement) {
|
|
646
|
-
return;
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
if (this.#treeElement.expanded) {
|
|
650
|
-
this.#treeElement.collapse();
|
|
651
|
-
} else if (!this.editing) {
|
|
652
|
-
this.#treeElement.expand();
|
|
501
|
+
try {
|
|
502
|
+
const result = await executionContext.evaluate({
|
|
503
|
+
expression,
|
|
504
|
+
objectGroup: WatchExpression.watchObjectGroupId,
|
|
505
|
+
includeCommandLineAPI: false,
|
|
506
|
+
silent: true,
|
|
507
|
+
returnByValue: false,
|
|
508
|
+
generatePreview: false,
|
|
509
|
+
},
|
|
510
|
+
/* userGesture */ false,
|
|
511
|
+
/* awaitPromise */ false);
|
|
512
|
+
if ('object' in result) {
|
|
513
|
+
const objectTree = new ObjectUI.ObjectPropertiesSection.ObjectTree(result.object, {
|
|
514
|
+
readOnly: true,
|
|
515
|
+
propertiesMode: ObjectUI.ObjectPropertiesSection.ObjectPropertiesMode.OWN_AND_INTERNAL_AND_INHERITED,
|
|
516
|
+
expansionTracker: expandController,
|
|
517
|
+
});
|
|
518
|
+
await expandController.apply(objectTree);
|
|
519
|
+
this.#result = objectTree;
|
|
520
|
+
this.#exceptionDetails = result.exceptionDetails;
|
|
653
521
|
}
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
private promptKeyDown(event: KeyboardEvent): void {
|
|
658
|
-
const isEscapeKey = Platform.KeyboardUtilities.isEscKey(event);
|
|
659
|
-
if (event.key === 'Enter' || isEscapeKey) {
|
|
660
|
-
this.finishEditing(event, isEscapeKey);
|
|
522
|
+
} catch {
|
|
661
523
|
}
|
|
662
524
|
}
|
|
663
525
|
|
|
664
526
|
private static readonly watchObjectGroupId = 'watch-group';
|
|
665
527
|
}
|
|
666
|
-
|
|
667
|
-
const enum Events {
|
|
668
|
-
EXPRESSION_UPDATED = 'ExpressionUpdated',
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
interface EventTypes {
|
|
672
|
-
[Events.EXPRESSION_UPDATED]: WatchExpression;
|
|
673
|
-
}
|