chrome-devtools-frontend 1.0.1724140 → 1.0.1726686

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.
Files changed (95) hide show
  1. package/.agents/skills/review-ui-widgets/SKILL.md +7 -7
  2. package/.agents/skills/ui-eng-vision-local-lit-renderer/SKILL.md +12 -0
  3. package/.agents/skills/ui-eng-vision-widget-promoter/SKILL.md +3 -3
  4. package/.agents/skills/ui-widgets/SKILL.md +2 -1
  5. package/docs/cookbook/dependencies.md +6 -4
  6. package/front_end/core/i18n/i18nImpl.ts +1 -1
  7. package/front_end/core/root/Runtime.ts +1 -1
  8. package/front_end/core/sdk/ResourceTreeModel.ts +16 -0
  9. package/front_end/entrypoints/js_app/js_app.ts +2 -5
  10. package/front_end/entrypoints/node_app/node_app.ts +2 -3
  11. package/front_end/generated/InspectorBackendCommands.ts +1 -1
  12. package/front_end/generated/protocol.ts +1 -0
  13. package/front_end/models/ai_assistance/skills/lighthouse.md +2 -1
  14. package/front_end/models/ai_assistance/skills/network.md +1 -0
  15. package/front_end/models/ai_assistance/tools/ExecuteJavaScript.ts +26 -2
  16. package/front_end/models/ai_assistance/tools/GetLighthouseAudits.ts +1 -1
  17. package/front_end/models/ai_assistance/tools/ListNetworkRequests.ts +2 -7
  18. package/front_end/models/ai_assistance/tools/RunLighthouse.ts +2 -2
  19. package/front_end/models/comment_manager/CommentManager.ts +0 -6
  20. package/front_end/models/comment_manager/CommentThread.ts +23 -19
  21. package/front_end/models/elements/StyleChanges.ts +66 -0
  22. package/front_end/models/trace/EventsSerializer.ts +2 -7
  23. package/front_end/models/trace/ModelImpl.ts +0 -2
  24. package/front_end/models/trace/handlers/FlowsHandler.ts +37 -29
  25. package/front_end/models/trace/helpers/SyntheticEvents.ts +10 -37
  26. package/front_end/models/trace/insights/RenderBlocking.ts +3 -1
  27. package/front_end/models/trace/lantern/simulation/Simulator.ts +4 -0
  28. package/front_end/models/trace/lantern/types/Lantern.ts +1 -0
  29. package/front_end/panels/accessibility/SourceOrderView.ts +1 -1
  30. package/front_end/panels/application/ApplicationPanelSidebar.ts +90 -0
  31. package/front_end/panels/application/StorageView.ts +196 -174
  32. package/front_end/panels/application/SubAppsView.ts +2 -2
  33. package/front_end/panels/application/preloading/PreloadingView.ts +360 -336
  34. package/front_end/panels/application/preloading/components/PreloadingDisabledInfobar.ts +2 -2
  35. package/front_end/panels/application/preloading/components/PreloadingGrid.ts +6 -3
  36. package/front_end/panels/application/preloading/components/RuleSetGrid.ts +17 -17
  37. package/front_end/panels/application/preloading/components/UsedPreloadingView.ts +2 -2
  38. package/front_end/panels/application/preloading/preloadingView.css +12 -4
  39. package/front_end/panels/application/preloading/preloadingViewDropDown.css +1 -1
  40. package/front_end/panels/application/storageView.css +16 -21
  41. package/front_end/panels/browser_debugger/CategorizedBreakpointsSidebarPane.ts +2 -2
  42. package/front_end/panels/browser_debugger/browser_debugger-meta.ts +6 -8
  43. package/front_end/panels/comments/CommentsPane.ts +26 -9
  44. package/front_end/panels/comments/commentsPane.css +25 -6
  45. package/front_end/panels/common/CommentThreadWidget.ts +2 -2
  46. package/front_end/panels/common/CommentsOverlayWidget.ts +94 -35
  47. package/front_end/panels/common/commentThreadWidget.css +17 -0
  48. package/front_end/panels/common/commentsOverlay.css +5 -10
  49. package/front_end/panels/common/common-meta.ts +11 -1
  50. package/front_end/panels/elements/ClassesPaneWidget.ts +130 -88
  51. package/front_end/panels/elements/ColorSwatchPopoverIcon.ts +7 -33
  52. package/front_end/panels/elements/DOMTreeWidget.ts +7 -3
  53. package/front_end/panels/elements/ElementStatePaneWidget.ts +11 -1
  54. package/front_end/panels/elements/PropertyRenderer.ts +3 -2
  55. package/front_end/panels/elements/StyleEditorWidget.ts +60 -41
  56. package/front_end/panels/elements/StylePropertiesSection.ts +32 -5
  57. package/front_end/panels/elements/StylePropertyTreeElement.ts +227 -162
  58. package/front_end/panels/event_listeners/EventListenersView.ts +11 -8
  59. package/front_end/panels/linear_memory_inspector/components/ValueInterpreterSettings.ts +1 -1
  60. package/front_end/panels/network/NetworkDataGridNode.ts +3 -1
  61. package/front_end/panels/network/RequestConditionsDrawer.ts +1 -1
  62. package/front_end/panels/network/RequestPayloadView.ts +12 -5
  63. package/front_end/panels/profiler/HeapProfileView.ts +28 -6
  64. package/front_end/panels/profiler/HeapSnapshotDataGrids.ts +7 -3
  65. package/front_end/panels/protocol_monitor/JSONEditor.ts +0 -1
  66. package/front_end/panels/recorder/RecorderPanel.ts +2 -2
  67. package/front_end/panels/settings/SettingsScreen.ts +1 -1
  68. package/front_end/panels/sources/NavigatorView.ts +105 -45
  69. package/front_end/panels/sources/ScopeChainSidebarPane.ts +8 -5
  70. package/front_end/panels/sources/SourcesNavigator.ts +421 -213
  71. package/front_end/panels/sources/SourcesPanel.ts +6 -4
  72. package/front_end/panels/sources/WatchExpressionsSidebarPane.ts +10 -5
  73. package/front_end/panels/sources/sources-meta.ts +4 -15
  74. package/front_end/panels/sources/sourcesNavigator.css +5 -0
  75. package/front_end/panels/timeline/ModificationsManager.ts +0 -2
  76. package/front_end/panels/timeline/README.md +2 -0
  77. package/front_end/panels/timeline/TimelineCommentController.ts +132 -30
  78. package/front_end/panels/timeline/TimelinePanel.ts +4 -0
  79. package/front_end/panels/timeline/components/IgnoreListSetting.ts +1 -1
  80. package/front_end/panels/timeline/utils/Treemap.ts +8 -0
  81. package/front_end/third_party/chromium/README.chromium +1 -1
  82. package/front_end/ui/comments/CommentAnchorResolver.ts +3 -0
  83. package/front_end/ui/comments/CommentOverlayManager.ts +2 -4
  84. package/front_end/ui/components/lists/List.ts +14 -1
  85. package/front_end/ui/components/panel_feedback/PreviewToggle.ts +1 -1
  86. package/front_end/ui/components/text_editor/AiCodeCompletionProvider.ts +23 -14
  87. package/front_end/ui/components/text_editor/AiCodeGenerationProvider.ts +3 -0
  88. package/front_end/ui/legacy/Toolbar.ts +0 -1
  89. package/front_end/ui/legacy/Treeoutline.ts +7 -2
  90. package/front_end/ui/legacy/UIUtils.ts +4 -1
  91. package/front_end/ui/legacy/components/inline_editor/Swatches.ts +19 -6
  92. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +20 -11
  93. package/front_end/ui/legacy/components/source_frame/XMLView.ts +27 -7
  94. package/front_end/ui/visual_logging/KnownContextValues.ts +1 -0
  95. package/package.json +1 -1
@@ -1,13 +1,13 @@
1
1
  // Copyright 2015 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
  import * as Common from '../../core/common/common.js';
7
6
  import * as i18n from '../../core/i18n/i18n.js';
8
7
  import * as Platform from '../../core/platform/platform.js';
9
8
  import * as SDK from '../../core/sdk/sdk.js';
10
9
  import * as UI from '../../ui/legacy/legacy.js';
10
+ import {Directives, html, render} from '../../ui/lit/lit.js';
11
11
  import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
12
12
 
13
13
  import classesPaneWidgetStyles from './classesPaneWidget.css.js';
@@ -37,42 +37,89 @@ const UIStrings = {
37
37
  } as const;
38
38
  const str_ = i18n.i18n.registerUIStrings('panels/elements/ClassesPaneWidget.ts', UIStrings);
39
39
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
40
+
41
+ const {ref, repeat} = Directives;
42
+
43
+ export interface ClassItem {
44
+ className: string;
45
+ enabled: boolean;
46
+ }
47
+
48
+ export interface ViewInput {
49
+ classes: ClassItem[];
50
+ onCommit: () => void;
51
+ /** Returns whether the prompt had text to discard, in which case the key event is fully consumed. */
52
+ onCancel: () => boolean;
53
+ onClick: (className: string, enabled: boolean) => void;
54
+ }
55
+
56
+ export interface ViewOutput {
57
+ attachPrompt?: (prompt: UI.TextPrompt.TextPrompt) => void;
58
+ }
59
+
60
+ export type View = (input: ViewInput, output: ViewOutput, target: HTMLElement) => void;
61
+
62
+ export const DEFAULT_VIEW: View = (input, output, target) => {
63
+ const onKeyDown = (event: KeyboardEvent): void => {
64
+ if (event.key === Platform.KeyboardUtilities.ENTER_KEY && !event.isComposing) {
65
+ event.consume();
66
+ input.onCommit();
67
+ } else if (Platform.KeyboardUtilities.isEscKey(event) && input.onCancel()) {
68
+ // Escape discarded non-empty prompt text, so stop it here instead of letting
69
+ // it reach global DevTools shortcuts (e.g. closing the drawer).
70
+ event.consume(true);
71
+ }
72
+ };
73
+ // clang-format off
74
+ render(html`
75
+ <style>${classesPaneWidgetStyles}</style>
76
+ <div class="styles-element-classes-pane">
77
+ <div class="title-container" @keydown=${onKeyDown}>
78
+ <div class="new-class-input monospace" autofocus ${ref(element => {
79
+ output.attachPrompt = element instanceof HTMLElement ? prompt => prompt.attach(element) : undefined;
80
+ })}></div>
81
+ </div>
82
+ <div class="source-code styles-element-classes-container">
83
+ ${repeat(input.classes, item => item.className, item => html`
84
+ <devtools-checkbox
85
+ class="monospace"
86
+ small
87
+ .checked=${item.enabled}
88
+ title=${item.className}
89
+ jslog=${VisualLogging.toggle('element-class').track({change: true})}
90
+ @click=${(event: Event) => input.onClick(item.className, (event.target as UI.UIUtils.CheckboxLabel).checked)}>${item.className}</devtools-checkbox>`)}
91
+ </div>
92
+ </div>`, target);
93
+ // clang-format on
94
+ };
95
+
40
96
  export class ClassesPaneWidget extends UI.Widget.Widget {
41
- private input: HTMLElement;
42
- private readonly classesContainer: HTMLElement;
43
- private readonly prompt: ClassNamePrompt;
44
- private readonly mutatingNodes: Set<SDK.DOMModel.DOMNode>;
45
- private readonly pendingNodeClasses: Map<SDK.DOMModel.DOMNode, string>;
46
- private readonly updateNodeThrottler: Common.Throttler.Throttler;
47
- private previousTarget: SDK.DOMModel.DOMNode|null;
48
-
49
- constructor() {
50
- super({
97
+ readonly #view: View;
98
+ readonly #prompt: ClassNamePrompt;
99
+ #promptAttached = false;
100
+ readonly #mutatingNodes: Set<SDK.DOMModel.DOMNode>;
101
+ readonly #pendingNodeClasses: Map<SDK.DOMModel.DOMNode, string>;
102
+ readonly #updateNodeThrottler: Common.Throttler.Throttler;
103
+ #previousTarget: SDK.DOMModel.DOMNode|null;
104
+
105
+ constructor(element?: HTMLElement, view: View = DEFAULT_VIEW) {
106
+ super(element, {
51
107
  jslog: `${VisualLogging.pane('elements-classes')}`,
52
108
  useShadowDom: true,
53
109
  });
54
- this.registerRequiredCSS(classesPaneWidgetStyles);
55
- this.contentElement.className = 'styles-element-classes-pane';
56
- const container = this.contentElement.createChild('div', 'title-container');
57
- this.input = container.createChild('div', 'new-class-input monospace');
58
- this.setDefaultFocusedElement(this.input);
59
- this.classesContainer = this.contentElement.createChild('div', 'source-code');
60
- this.classesContainer.classList.add('styles-element-classes-container');
61
- this.prompt = new ClassNamePrompt(this.nodeClasses.bind(this));
62
- this.prompt.setAutocompletionTimeout(0);
63
- this.prompt.renderAsBlock();
64
-
65
- const proxyElement = (this.prompt.attach(this.input) as HTMLElement);
66
- this.prompt.setPlaceholder(i18nString(UIStrings.addNewClass));
67
- this.prompt.addEventListener(UI.TextPrompt.Events.TEXT_CHANGED, this.onTextChanged, this);
68
- proxyElement.addEventListener('keydown', this.onKeyDown.bind(this), false);
110
+ this.#view = view;
111
+ this.#prompt = new ClassNamePrompt(this.nodeClasses.bind(this));
112
+ this.#prompt.setAutocompletionTimeout(0);
113
+ this.#prompt.renderAsBlock();
114
+
115
+ this.#prompt.addEventListener(UI.TextPrompt.Events.TEXT_CHANGED, this.onTextChanged, this);
69
116
 
70
117
  SDK.TargetManager.TargetManager.instance().addModelListener(
71
118
  SDK.DOMModel.DOMModel, SDK.DOMModel.Events.DOMMutated, this.onDOMMutated, this, {scoped: true});
72
- this.mutatingNodes = new Set();
73
- this.pendingNodeClasses = new Map();
74
- this.updateNodeThrottler = new Common.Throttler.Throttler(0);
75
- this.previousTarget = null;
119
+ this.#mutatingNodes = new Set();
120
+ this.#pendingNodeClasses = new Map();
121
+ this.#updateNodeThrottler = new Common.Throttler.Throttler(0);
122
+ this.#previousTarget = null;
76
123
  UI.Context.Context.instance().addFlavorChangeListener(SDK.DOMModel.DOMNode, this.onSelectedNodeChanged, this);
77
124
  }
78
125
 
@@ -82,29 +129,22 @@ export class ClassesPaneWidget extends UI.Widget.Widget {
82
129
  .filter(className => className.length);
83
130
  }
84
131
 
85
- private onKeyDown(event: KeyboardEvent): void {
86
- if (!(event.key === 'Enter') && !Platform.KeyboardUtilities.isEscKey(event)) {
132
+ private onCommit(): void {
133
+ if (this.#prompt.acceptAutoComplete()) {
87
134
  return;
88
135
  }
136
+ this.applyPromptText(this.#prompt.text());
137
+ }
89
138
 
90
- if (event.key === 'Enter') {
91
- event.consume();
92
- if (this.prompt.acceptAutoComplete()) {
93
- return;
94
- }
95
- }
96
-
97
- const eventTarget = (event.target as HTMLElement);
98
- let text = eventTarget.textContent;
99
- if (Platform.KeyboardUtilities.isEscKey(event)) {
100
- if (!Platform.StringUtilities.isWhitespace(text)) {
101
- event.consume(true);
102
- }
103
- text = '';
104
- }
139
+ /** Discards the prompt text and returns whether there was any non-whitespace text to discard. */
140
+ private onCancel(): boolean {
141
+ const hadText = !Platform.StringUtilities.isWhitespace(this.#prompt.text());
142
+ this.applyPromptText('');
143
+ return hadText;
144
+ }
105
145
 
106
- this.prompt.clearAutocomplete();
107
- eventTarget.textContent = '';
146
+ private applyPromptText(text: string): void {
147
+ this.#prompt.setText('');
108
148
 
109
149
  const node = UI.Context.Context.instance().flavor(SDK.DOMModel.DOMNode);
110
150
  if (!node) {
@@ -128,7 +168,7 @@ export class ClassesPaneWidget extends UI.Widget.Widget {
128
168
  UI.ARIAUtils.LiveAnnouncer.alert(announcementString);
129
169
 
130
170
  this.installNodeClasses(node);
131
- this.update();
171
+ this.requestUpdate();
132
172
  }
133
173
 
134
174
  private onTextChanged(): void {
@@ -141,65 +181,67 @@ export class ClassesPaneWidget extends UI.Widget.Widget {
141
181
 
142
182
  private onDOMMutated(event: Common.EventTarget.EventTargetEvent<SDK.DOMModel.DOMNode>): void {
143
183
  const node = event.data;
144
- if (this.mutatingNodes.has(node)) {
184
+ if (this.#mutatingNodes.has(node)) {
145
185
  return;
146
186
  }
147
187
  cachedClassesMap.delete(node);
148
- this.update();
188
+ this.requestUpdate();
149
189
  }
150
190
 
151
191
  private onSelectedNodeChanged(event: Common.EventTarget.EventTargetEvent<SDK.DOMModel.DOMNode|null>): void {
152
- if (this.previousTarget && this.prompt.text()) {
153
- this.input.textContent = '';
154
- this.installNodeClasses(this.previousTarget);
192
+ if (this.#previousTarget && this.#promptAttached && this.#prompt.text()) {
193
+ this.#prompt.setText('');
194
+ this.installNodeClasses(this.#previousTarget);
155
195
  }
156
- this.previousTarget = event.data;
157
- this.update();
196
+ this.#previousTarget = event.data;
197
+ this.requestUpdate();
158
198
  }
159
199
 
160
200
  override wasShown(): void {
161
201
  super.wasShown();
162
- this.update();
202
+ // Render synchronously rather than via requestUpdate(): callers such as
203
+ // StylesSidebarPane.showToolbarPane() focus the widget right after show(),
204
+ // so the autofocus prompt must already exist. This also re-reads the
205
+ // selected node, which may have changed while the pane was hidden.
206
+ this.performUpdate();
163
207
  }
164
208
 
165
- update(): void {
166
- if (!this.isShowing()) {
167
- return;
168
- }
169
-
209
+ override performUpdate(): void {
170
210
  let node = UI.Context.Context.instance().flavor(SDK.DOMModel.DOMNode);
171
211
  if (node) {
172
212
  node = node.enclosingElementOrSelf();
173
213
  }
174
214
 
175
- this.classesContainer.removeChildren();
176
- // @ts-expect-error this.input is a div, not an input element. So this line makes no sense at all
177
- this.input.disabled = !node;
178
-
179
- if (!node) {
180
- return;
181
- }
182
-
183
- const classes = this.nodeClasses(node);
215
+ const classes = node ? this.nodeClasses(node) : new Map<string, boolean>();
184
216
  const keys = [...classes.keys()];
185
217
  keys.sort(Platform.StringUtilities.caseInsensetiveComparator);
186
- for (const className of keys) {
187
- const checkbox =
188
- UI.UIUtils.CheckboxLabel.createWithStringLiteral(className, classes.get(className), 'element-class', true);
189
- checkbox.classList.add('monospace');
190
- checkbox.addEventListener('click', this.onClick.bind(this, className), false);
191
- this.classesContainer.appendChild(checkbox);
218
+ const input: ViewInput = {
219
+ classes: keys.map(className => ({className, enabled: Boolean(classes.get(className))})),
220
+ onCommit: this.onCommit.bind(this),
221
+ onCancel: this.onCancel.bind(this),
222
+ onClick: this.onClick.bind(this),
223
+ };
224
+ const output: ViewOutput = {};
225
+ this.#view(input, output, this.contentElement);
226
+ if (output.attachPrompt && !this.#promptAttached) {
227
+ output.attachPrompt(this.#prompt);
228
+ this.#prompt.setPlaceholder(i18nString(UIStrings.addNewClass));
229
+ this.#promptAttached = true;
230
+ }
231
+ if (this.#promptAttached) {
232
+ // Classes can only be added to a selected node.
233
+ this.#prompt.setEnabled(Boolean(node));
192
234
  }
193
235
  }
194
236
 
195
- private onClick(className: string, event: Event): void {
237
+ private onClick(className: string, enabled: boolean): void {
196
238
  const node = UI.Context.Context.instance().flavor(SDK.DOMModel.DOMNode);
197
239
  if (!node) {
198
240
  return;
199
241
  }
200
- const enabled = (event.target as HTMLInputElement).checked;
201
242
  this.toggleClass(node, className, enabled);
202
243
  this.installNodeClasses(node);
244
+ this.requestUpdate();
203
245
  }
204
246
 
205
247
  private nodeClasses(node: SDK.DOMModel.DOMNode): Map<string, boolean> {
@@ -235,30 +277,30 @@ export class ClassesPaneWidget extends UI.Widget.Widget {
235
277
  }
236
278
  }
237
279
 
238
- const additionalClasses = this.splitTextIntoClasses(this.prompt.textWithCurrentSuggestion());
280
+ const additionalClasses = this.splitTextIntoClasses(this.#prompt.textWithCurrentSuggestion());
239
281
  for (const className of additionalClasses) {
240
282
  activeClasses.add(className);
241
283
  }
242
284
 
243
285
  const newClasses = [...activeClasses.values()].sort();
244
286
 
245
- this.pendingNodeClasses.set(node, newClasses.join(' '));
246
- void this.updateNodeThrottler.schedule(this.flushPendingClasses.bind(this));
287
+ this.#pendingNodeClasses.set(node, newClasses.join(' '));
288
+ void this.#updateNodeThrottler.schedule(this.flushPendingClasses.bind(this));
247
289
  }
248
290
 
249
291
  private async flushPendingClasses(): Promise<void> {
250
292
  const promises = [];
251
- for (const node of this.pendingNodeClasses.keys()) {
252
- this.mutatingNodes.add(node);
253
- const promise = node.setAttributeValuePromise('class', (this.pendingNodeClasses.get(node) as string))
293
+ for (const node of this.#pendingNodeClasses.keys()) {
294
+ this.#mutatingNodes.add(node);
295
+ const promise = node.setAttributeValuePromise('class', (this.#pendingNodeClasses.get(node) as string))
254
296
  .then(onClassValueUpdated.bind(this, node));
255
297
  promises.push(promise);
256
298
  }
257
- this.pendingNodeClasses.clear();
299
+ this.#pendingNodeClasses.clear();
258
300
  await Promise.all(promises);
259
301
 
260
302
  function onClassValueUpdated(this: ClassesPaneWidget, node: SDK.DOMModel.DOMNode): void {
261
- this.mutatingNodes.delete(node);
303
+ this.#mutatingNodes.delete(node);
262
304
  }
263
305
  }
264
306
  }
@@ -21,11 +21,6 @@ const UIStrings = {
21
21
  * allows the user to edit cubic-bezier CSS properties directly.
22
22
  */
23
23
  openCubicBezierEditor: 'Open cubic bezier editor',
24
- /**
25
- * @description Tooltip text for an icon that opens the shadow editor. The shadow editor is a tool
26
- * which allows the user to edit CSS shadow properties.
27
- */
28
- openShadowEditor: 'Open shadow editor',
29
24
  } as const;
30
25
  const str_ = i18n.i18n.registerUIStrings('panels/elements/ColorSwatchPopoverIcon.ts', UIStrings);
31
26
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
@@ -321,56 +316,35 @@ export interface ShadowEventTypes {
321
316
  export class ShadowSwatchPopoverHelper extends Common.ObjectWrapper.ObjectWrapper<ShadowEventTypes> {
322
317
  private treeElement: StylePropertyTreeElement;
323
318
  private readonly swatchPopoverHelper: InlineEditor.SwatchPopoverHelper.SwatchPopoverHelper;
324
- private readonly shadowSwatch: InlineEditor.Swatches.CSSShadowSwatch;
325
- private iconElement: HTMLSpanElement;
326
319
  private readonly boundShadowChanged:
327
320
  (event: Common.EventTarget.EventTargetEvent<InlineEditor.CSSShadowEditor.CSSShadowModel>) => void;
328
321
  private readonly boundOnScroll: (event: Event) => void;
329
322
  private cssShadowEditor?: InlineEditor.CSSShadowEditor.CSSShadowEditor;
330
323
  private scrollerElement?: Element;
331
324
  private originalPropertyText?: string|null;
332
- constructor(
333
- treeElement: StylePropertyTreeElement, swatchPopoverHelper: InlineEditor.SwatchPopoverHelper.SwatchPopoverHelper,
334
- shadowSwatch: InlineEditor.Swatches.CSSShadowSwatch) {
325
+ constructor(treeElement: StylePropertyTreeElement,
326
+ swatchPopoverHelper: InlineEditor.SwatchPopoverHelper.SwatchPopoverHelper) {
335
327
  super();
336
328
  this.treeElement = treeElement;
337
329
  this.swatchPopoverHelper = swatchPopoverHelper;
338
- this.shadowSwatch = shadowSwatch;
339
- this.iconElement = shadowSwatch.iconElement();
340
-
341
- UI.Tooltip.Tooltip.install(this.iconElement, i18nString(UIStrings.openShadowEditor));
342
- this.iconElement.addEventListener('click', this.iconClick.bind(this), false);
343
- this.iconElement.addEventListener('keydown', this.keyDown.bind(this), false);
344
- this.iconElement.addEventListener('mousedown', event => event.consume(), false);
345
330
 
346
331
  this.boundShadowChanged = this.shadowChanged.bind(this);
347
332
  this.boundOnScroll = this.onScroll.bind(this);
348
333
  }
349
334
 
350
- private keyDown(event: KeyboardEvent): void {
351
- if (Platform.KeyboardUtilities.isEnterOrSpaceKey(event)) {
352
- event.consume(true);
353
- this.showPopover();
354
- }
355
- }
356
-
357
- private iconClick(event: Event): void {
358
- event.consume(true);
359
- this.showPopover();
360
- }
361
-
362
- showPopover(): void {
335
+ showPopover(shadowSwatch: InlineEditor.Swatches.CSSShadowSwatch): void {
363
336
  if (this.swatchPopoverHelper.isShowing()) {
364
337
  this.swatchPopoverHelper.hide(true);
365
338
  return;
366
339
  }
367
340
 
341
+ const iconElement = shadowSwatch.iconElement();
368
342
  this.cssShadowEditor = new InlineEditor.CSSShadowEditor.CSSShadowEditor();
369
343
  this.cssShadowEditor.element.classList.toggle('with-padding', true);
370
- this.cssShadowEditor.setModel(this.shadowSwatch.model());
344
+ this.cssShadowEditor.setModel(shadowSwatch.model());
371
345
  this.cssShadowEditor.addEventListener(InlineEditor.CSSShadowEditor.Events.SHADOW_CHANGED, this.boundShadowChanged);
372
- this.swatchPopoverHelper.show(this.cssShadowEditor, this.iconElement, this.onPopoverHidden.bind(this));
373
- this.scrollerElement = this.iconElement.enclosingNodeOrSelfWithClass('style-panes-wrapper');
346
+ this.swatchPopoverHelper.show(this.cssShadowEditor, iconElement, this.onPopoverHidden.bind(this));
347
+ this.scrollerElement = iconElement.enclosingNodeOrSelfWithClass('style-panes-wrapper');
374
348
  if (this.scrollerElement) {
375
349
  this.scrollerElement.addEventListener('scroll', this.boundOnScroll, false);
376
350
  }
@@ -120,7 +120,7 @@ interface ViewInput {
120
120
  selectedClosingTag: boolean;
121
121
 
122
122
  onSelect: (node: SDK.DOMModel.DOMNode, isClosingTag: boolean, selectedByUser: boolean) => void;
123
- onExpand: (node: SDK.DOMModel.DOMNode, expanded: boolean) => void;
123
+ onExpand: (node: SDK.DOMModel.DOMNode, expanded: boolean, recursively?: boolean) => void;
124
124
  onContextMenu: (node: SDK.DOMModel.DOMNode, event: MouseEvent) => void;
125
125
  onClearMaxRows: () => void;
126
126
  onHoverNode: (node: SDK.DOMModel.DOMNode|null, showInfo: boolean, isClosingTag: boolean) => void;
@@ -659,7 +659,7 @@ export const DEFAULT_VIEW: View = (input: ViewInput, _output: undefined, target:
659
659
  if (!isCollapsible) {
660
660
  return;
661
661
  }
662
- input.onExpand(node, event.detail.expanded);
662
+ input.onExpand(node, event.detail.expanded, event.detail.recursively);
663
663
  };
664
664
 
665
665
  const isDragOver = input.dragOverNode?.node === node && !input.dragOverNode.isClosingTag;
@@ -1789,7 +1789,11 @@ export class DOMTreeWidget extends UI.Widget.Widget {
1789
1789
  node.highlight();
1790
1790
  Host.userMetrics.actionTaken(Host.UserMetrics.Action.ChangeInspectedNodeInElementsPanel);
1791
1791
  },
1792
- onExpand: (node: SDK.DOMModel.DOMNode, expanded: boolean) => {
1792
+ onExpand: (node: SDK.DOMModel.DOMNode, expanded: boolean, recursively = false) => {
1793
+ if (expanded && recursively) {
1794
+ void this.expandRecursively(node);
1795
+ return;
1796
+ }
1793
1797
  this.setNodeExpanded(node, expanded);
1794
1798
  },
1795
1799
  onContextMenu: (node: SDK.DOMModel.DOMNode, event: MouseEvent) => {
@@ -69,6 +69,8 @@ enum SpecificPseudoStates {
69
69
  AUTOFILL = 'autofill',
70
70
  OPEN = 'open',
71
71
  TARGET_CURRENT = 'target-current',
72
+ BUFFERING = 'buffering',
73
+ STALLED = 'stalled',
72
74
  }
73
75
 
74
76
  interface ElementState {
@@ -92,7 +94,7 @@ export const DEFAULT_VIEW: View = (input, _output, target) => {
92
94
  return html`
93
95
  <div id=${state.state}>
94
96
  <devtools-checkbox class="small" @click=${input.onStateCheckboxClicked}
95
- jslog=${VisualLogging.toggle(state.state).track({change: true})} ?checked=${state.checked} ?disabled=${state.disabled}
97
+ jslog=${VisualLogging.toggle(state.state).track({change: true})} .checked=${Boolean(state.checked)} ?disabled=${state.disabled}
96
98
  title=${':' + state.state}>
97
99
  <span class="source-code">${':' + state.state}</span>
98
100
  </devtools-checkbox>
@@ -184,6 +186,8 @@ export class ElementStatePaneWidget extends UI.Widget.Widget {
184
186
  this.#states.set(SpecificPseudoStates.OPEN, {state: SpecificPseudoStates.OPEN, type: 'specific'});
185
187
  this.#states.set(
186
188
  SpecificPseudoStates.TARGET_CURRENT, {state: SpecificPseudoStates.TARGET_CURRENT, type: 'specific'});
189
+ this.#states.set(SpecificPseudoStates.BUFFERING, {state: SpecificPseudoStates.BUFFERING, type: 'specific'});
190
+ this.#states.set(SpecificPseudoStates.STALLED, {state: SpecificPseudoStates.STALLED, type: 'specific'});
187
191
 
188
192
  setDualStateCheckboxes(SpecificPseudoStates.VALID, SpecificPseudoStates.INVALID);
189
193
  setDualStateCheckboxes(SpecificPseudoStates.USER_VALID, SpecificPseudoStates.USER_INVALID);
@@ -399,6 +403,12 @@ export class ElementStatePaneWidget extends UI.Widget.Widget {
399
403
  } else {
400
404
  hideSpecificCheckbox(SpecificPseudoStates.TARGET_CURRENT, true);
401
405
  }
406
+
407
+ // :buffering and :stalled are independent. Forcing :stalled does not force
408
+ // :buffering, even though a real stalled media element is also buffering.
409
+ const isMediaElement = isElementOfTypes(node, ['audio', 'video']);
410
+ hideSpecificCheckbox(SpecificPseudoStates.BUFFERING, !isMediaElement);
411
+ hideSpecificCheckbox(SpecificPseudoStates.STALLED, !isMediaElement);
402
412
  }
403
413
  }
404
414
 
@@ -324,8 +324,9 @@ export class TracingContext {
324
324
  // evaluation callback is run. The callback should return synchronously an array of Nodes as placeholder to be
325
325
  // rendered immediately and optionally a callback for asynchronous updates of the placeholder nodes. The callback
326
326
  // returns a boolean indicating whether the update was successful or not.
327
- applyEvaluation(children: TracingContext[],
328
- evaluation: () => ({placeholder: Node[], asyncEvalCallback?: () => Promise<boolean>})): Node[]|null {
327
+ applyEvaluation<T extends Node[]|LitTemplate = Node[]>(
328
+ children: TracingContext[], evaluation: () => ({placeholder: T, asyncEvalCallback?: () => Promise<boolean>})): T
329
+ |null {
329
330
  if (this.#evaluationCount === 0 || children.some(child => child.#appliedEvaluations >= this.#evaluationCount)) {
330
331
  this.#setHasMoreEvaluations(true);
331
332
  children.forEach(child => this.#asyncEvalCallbacks.push(...child.#asyncEvalCallbacks));
@@ -3,15 +3,19 @@
3
3
  // found in the LICENSE file.
4
4
  /* eslint-disable @devtools/no-imperative-dom-api */
5
5
 
6
+ import '../../ui/kit/kit.js';
7
+
6
8
  import * as Elements from '../../models/elements/elements.js';
7
- import {createIcon} from '../../ui/kit/kit.js';
8
9
  import * as UI from '../../ui/legacy/legacy.js';
10
+ import {Directives, html, type LitTemplate} from '../../ui/lit/lit.js';
11
+ import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
9
12
 
10
13
  import type * as ElementsComponents from './components/components.js';
11
14
  import type {StylePropertiesSection} from './StylePropertiesSection.js';
12
15
  import {StylePropertyTreeElement} from './StylePropertyTreeElement.js';
13
16
  import type {StylesContainer} from './StylesContainer.js';
14
17
 
18
+ const {ref} = Directives;
15
19
  type PropertySelectedEvent = ElementsComponents.StylePropertyEditor.PropertySelectedEvent;
16
20
  type PropertyDeselectedEvent = ElementsComponents.StylePropertyEditor.PropertyDeselectedEvent;
17
21
 
@@ -118,7 +122,6 @@ export class StyleEditorWidget extends UI.Widget.VBox {
118
122
  });
119
123
  }
120
124
  }
121
-
122
125
  async render(): Promise<void> {
123
126
  if (!this.editor) {
124
127
  return;
@@ -150,47 +153,63 @@ export class StyleEditorWidget extends UI.Widget.VBox {
150
153
  }
151
154
  }
152
155
 
153
- static createTriggerButton(
154
- stylesContainer: StylesContainer, section: StylePropertiesSection, editorClass: {new(): Editor},
155
- buttonTitle: string, triggerKey: string): HTMLElement {
156
- const triggerButton = createIcon('flex-wrap', 'styles-pane-button');
157
- triggerButton.title = buttonTitle;
158
- triggerButton.role = 'button';
159
-
160
- triggerButton.onclick = async event => {
161
- event.stopPropagation();
162
- const popoverHelper = stylesContainer.swatchPopoverHelper();
163
- const widget = StyleEditorWidget.instance();
164
- widget.element.classList.toggle('with-padding', true);
165
- widget.setEditor(editorClass);
166
- widget.bindContext(stylesContainer, section);
167
- widget.setTriggerKey(triggerKey);
168
- await widget.render();
169
- widget.focus();
170
- const scrollerElement = triggerButton.enclosingNodeOrSelfWithClass('style-panes-wrapper');
171
- const onScroll = (): void => {
172
- popoverHelper.hide(true);
173
- };
174
- const onStylesUpdateCompleted = widget.requestUpdate.bind(widget);
175
- stylesContainer.addStyleUpdateListener(onStylesUpdateCompleted);
176
- popoverHelper.show(widget, triggerButton, () => {
177
- widget.#trackChanges();
178
- widget.unbindContext();
156
+ static createTriggerButton(stylesContainer: StylesContainer, section: StylePropertiesSection,
157
+ editorClass: {new(): Editor}, buttonTitle: string, triggerKey: string,
158
+ jslogContext: string, onClick?: () => void): LitTemplate {
159
+ const popoverHelper = stylesContainer.swatchPopoverHelper();
160
+ let triggerButton: Element|undefined;
161
+ // clang-format off
162
+ return html`<devtools-icon
163
+ ${ref(e => {
164
+ triggerButton = e;
165
+ if (e && popoverHelper.isShowing(StyleEditorWidget.instance()) &&
166
+ StyleEditorWidget.instance().getTriggerKey() === triggerKey) {
167
+ popoverHelper.setAnchorElement(e);
168
+ }
169
+ })}
170
+ name="flex-wrap"
171
+ class="styles-pane-button"
172
+ title=${buttonTitle}
173
+ role="button"
174
+ tabindex="-1"
175
+ jslog=${VisualLogging.showStyleEditor(jslogContext).track({click: true})}
176
+ @click=${async (event: Event) => {
177
+ event.stopPropagation();
178
+ onClick?.();
179
+ if (!triggerButton) {
180
+ return;
181
+ }
182
+ const widget = StyleEditorWidget.instance();
183
+ widget.element.classList.toggle('with-padding', true);
184
+ widget.setEditor(editorClass);
185
+ widget.bindContext(stylesContainer, section);
186
+ widget.setTriggerKey(triggerKey);
187
+ await widget.render();
188
+ widget.focus();
189
+ const scrollerElement = triggerButton.enclosingNodeOrSelfWithClass('style-panes-wrapper');
190
+ const onScroll = (): void => {
191
+ popoverHelper.hide(true);
192
+ };
193
+ const onStylesUpdateCompleted = widget.requestUpdate.bind(widget);
194
+ stylesContainer.addStyleUpdateListener(onStylesUpdateCompleted);
195
+ popoverHelper.show(widget, triggerButton, () => {
196
+ widget.#trackChanges();
197
+ widget.unbindContext();
198
+ if (scrollerElement) {
199
+ scrollerElement.removeEventListener('scroll', onScroll);
200
+ }
201
+ stylesContainer.removeStyleUpdateListener(onStylesUpdateCompleted);
202
+ });
179
203
  if (scrollerElement) {
180
- scrollerElement.removeEventListener('scroll', onScroll);
204
+ scrollerElement.addEventListener('scroll', onScroll);
181
205
  }
182
- stylesContainer.removeStyleUpdateListener(onStylesUpdateCompleted);
183
- });
184
- if (scrollerElement) {
185
- scrollerElement.addEventListener('scroll', onScroll);
186
- }
187
- };
188
- triggerButton.onmouseup = event => {
189
- // Stop propagation to prevent the property editor from being activated.
190
- event.stopPropagation();
191
- };
192
-
193
- return triggerButton;
206
+ }}
207
+ @mouseup=${(event: Event) => {
208
+ // Stop propagation to prevent the property editor from being activated.
209
+ event.stopPropagation();
210
+ }}
211
+ ></devtools-icon>`;
212
+ // clang-format on
194
213
  }
195
214
  }
196
215