chrome-devtools-frontend 1.0.1698733 → 1.0.1700561

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 (164) hide show
  1. package/.agents/skills/devtools-verification/SKILL.md +19 -0
  2. package/.agents/skills/{repro-flaky-tests → fix-tests}/SKILL.md +2 -2
  3. package/front_end/core/sdk/CSSPropertyParserMatchers.ts +2 -2
  4. package/front_end/core/sdk/DOMModel.ts +3 -5
  5. package/front_end/core/sdk/NetworkManager.ts +2 -2
  6. package/front_end/core/sdk/OverlayModel.ts +11 -12
  7. package/front_end/core/sdk/ServiceWorkerCacheModel.ts +3 -0
  8. package/front_end/core/sdk/SourceMapScopeChainEntry.ts +84 -32
  9. package/front_end/core/sdk/StorageBucketsModel.ts +2 -0
  10. package/front_end/entrypoints/main/GlobalAiButton.ts +2 -6
  11. package/front_end/entrypoints/main/MainImpl.ts +8 -1
  12. package/front_end/generated/InspectorBackendCommands.ts +12 -2
  13. package/front_end/generated/SupportedCSSProperties.ts +14 -0
  14. package/front_end/generated/protocol-mapping.d.ts +48 -1
  15. package/front_end/generated/protocol-proxy-api.d.ts +37 -1
  16. package/front_end/generated/protocol.ts +131 -0
  17. package/front_end/models/bindings/NetworkProject.ts +30 -0
  18. package/front_end/models/bindings/ResourceScriptMapping.ts +62 -0
  19. package/front_end/models/change_tracker/ChangeTracker.ts +1 -1
  20. package/front_end/models/comment_manager/CD4ABridge.ts +2 -2
  21. package/front_end/models/comment_manager/CommentManager.ts +18 -0
  22. package/front_end/models/emulation/DeviceModeModel.ts +1 -1
  23. package/front_end/models/extensions/ExtensionAPI.ts +4 -2
  24. package/front_end/models/issues_manager/CookieIssue.ts +0 -15
  25. package/front_end/models/persistence/NetworkPersistenceManager.ts +29 -4
  26. package/front_end/models/source_map_scopes/NamesResolver.ts +34 -4
  27. package/front_end/models/source_map_scopes/ScopeChainModel.ts +31 -7
  28. package/front_end/panels/accessibility/AccessibilityAnnouncementRecordingView.ts +281 -3
  29. package/front_end/panels/accessibility/accessibilityAnnouncementRecordingView.css +85 -0
  30. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +1 -2
  31. package/front_end/panels/ai_assistance/ai_assistance-meta.ts +5 -8
  32. package/front_end/panels/animation/AnimationTimeline.ts +1 -1
  33. package/front_end/panels/application/AppManifestView.ts +128 -134
  34. package/front_end/panels/application/CrashReportContextView.ts +6 -6
  35. package/front_end/panels/application/DeviceBoundSessionsTreeElement.ts +17 -17
  36. package/front_end/panels/application/DeviceBoundSessionsView.ts +136 -137
  37. package/front_end/panels/application/IndexedDBModel.ts +3 -0
  38. package/front_end/panels/application/IndexedDBViews.ts +1 -1
  39. package/front_end/panels/application/KeyValueStorageItemsView.ts +1 -0
  40. package/front_end/panels/application/OriginTrialTreeView.ts +18 -23
  41. package/front_end/panels/application/ReportingApiTreeElement.ts +3 -3
  42. package/front_end/panels/application/ReportingApiView.ts +6 -14
  43. package/front_end/panels/application/application-meta.ts +7 -7
  44. package/front_end/panels/application/components/AdsView.ts +63 -1
  45. package/front_end/panels/application/components/adsView.css +11 -1
  46. package/front_end/panels/common/CommentThreadWidget.ts +20 -22
  47. package/front_end/panels/common/CommentsOverlayWidget.ts +115 -9
  48. package/front_end/panels/common/CommentsStatusBarPill.ts +13 -1
  49. package/front_end/panels/common/ExtensionServer.ts +43 -34
  50. package/front_end/panels/common/GeminiRebrandPromoDialog.ts +1 -1
  51. package/front_end/panels/common/commentThreadWidget.css +6 -0
  52. package/front_end/panels/common/common-meta.ts +4 -1
  53. package/front_end/panels/elements/CSSRuleValidator.ts +1 -1
  54. package/front_end/panels/elements/ElementsPanel.ts +4 -4
  55. package/front_end/panels/elements/ElementsTreeElement.ts +9 -2
  56. package/front_end/panels/elements/ElementsTreeOutline.ts +25 -33
  57. package/front_end/panels/elements/elementsTreeOutline.css +8 -4
  58. package/front_end/panels/event_listeners/EventListenersView.ts +9 -4
  59. package/front_end/panels/issues/IssuesPane.ts +34 -22
  60. package/front_end/panels/lighthouse/LighthouseController.ts +1 -1
  61. package/front_end/panels/mobile_throttling/NetworkPanelIndicator.ts +1 -1
  62. package/front_end/panels/mobile_throttling/ThrottlingManager.ts +1 -1
  63. package/front_end/panels/mobile_throttling/ThrottlingSettingsTab.ts +1 -1
  64. package/front_end/panels/network/RequestPayloadView.ts +17 -15
  65. package/front_end/panels/recorder/RecordingView.ts +32 -53
  66. package/front_end/panels/recorder/StepView.ts +19 -4
  67. package/front_end/panels/recorder/TimelineSection.ts +1 -1
  68. package/front_end/panels/recorder/controlButton.css +6 -0
  69. package/front_end/panels/recorder/recordingView.css +11 -2
  70. package/front_end/panels/settings/BackendLinkingSettingsTab.ts +2 -1
  71. package/front_end/panels/settings/EditFileSystemView.ts +1 -1
  72. package/front_end/panels/sources/CallStackSidebarPane.ts +1 -1
  73. package/front_end/panels/sources/DebuggerPlugin.ts +137 -56
  74. package/front_end/panels/sources/ScopeChainSidebarPane.ts +17 -15
  75. package/front_end/panels/timeline/components/DetailsView.ts +3 -3
  76. package/front_end/panels/timeline/components/IgnoreListSetting.ts +0 -2
  77. package/front_end/panels/timeline/components/LiveMetricsView.ts +59 -64
  78. package/front_end/panels/timeline/components/OriginMap.ts +1 -1
  79. package/front_end/panels/timeline/components/insights/BaseInsightComponent.ts +2 -3
  80. package/front_end/panels/timeline/components/liveMetricsView.css +1 -0
  81. package/front_end/third_party/chromium/README.chromium +1 -1
  82. package/front_end/third_party/lighthouse/README.chromium +2 -2
  83. package/front_end/third_party/lighthouse/lighthouse-dt-bundle.js +1580 -1524
  84. package/front_end/third_party/lighthouse/locales/ar-XB.json +111 -72
  85. package/front_end/third_party/lighthouse/locales/ar.json +113 -74
  86. package/front_end/third_party/lighthouse/locales/bg.json +112 -73
  87. package/front_end/third_party/lighthouse/locales/ca.json +112 -73
  88. package/front_end/third_party/lighthouse/locales/cs.json +112 -73
  89. package/front_end/third_party/lighthouse/locales/da.json +112 -73
  90. package/front_end/third_party/lighthouse/locales/de.json +112 -73
  91. package/front_end/third_party/lighthouse/locales/el.json +112 -73
  92. package/front_end/third_party/lighthouse/locales/en-GB.json +111 -72
  93. package/front_end/third_party/lighthouse/locales/en-US.json +102 -66
  94. package/front_end/third_party/lighthouse/locales/en-XA.json +111 -72
  95. package/front_end/third_party/lighthouse/locales/en-XL.json +102 -66
  96. package/front_end/third_party/lighthouse/locales/es-419.json +112 -73
  97. package/front_end/third_party/lighthouse/locales/es.json +112 -73
  98. package/front_end/third_party/lighthouse/locales/fi.json +112 -73
  99. package/front_end/third_party/lighthouse/locales/fil.json +112 -73
  100. package/front_end/third_party/lighthouse/locales/fr.json +112 -73
  101. package/front_end/third_party/lighthouse/locales/he.json +112 -73
  102. package/front_end/third_party/lighthouse/locales/hi.json +112 -73
  103. package/front_end/third_party/lighthouse/locales/hr.json +112 -73
  104. package/front_end/third_party/lighthouse/locales/hu.json +112 -73
  105. package/front_end/third_party/lighthouse/locales/id.json +112 -73
  106. package/front_end/third_party/lighthouse/locales/it.json +113 -74
  107. package/front_end/third_party/lighthouse/locales/ja.json +112 -73
  108. package/front_end/third_party/lighthouse/locales/ko.json +112 -73
  109. package/front_end/third_party/lighthouse/locales/lt.json +112 -73
  110. package/front_end/third_party/lighthouse/locales/lv.json +112 -73
  111. package/front_end/third_party/lighthouse/locales/nl.json +112 -73
  112. package/front_end/third_party/lighthouse/locales/no.json +112 -73
  113. package/front_end/third_party/lighthouse/locales/pl.json +112 -73
  114. package/front_end/third_party/lighthouse/locales/pt-PT.json +112 -73
  115. package/front_end/third_party/lighthouse/locales/pt.json +112 -73
  116. package/front_end/third_party/lighthouse/locales/ro.json +112 -73
  117. package/front_end/third_party/lighthouse/locales/ru.json +112 -73
  118. package/front_end/third_party/lighthouse/locales/sk.json +112 -73
  119. package/front_end/third_party/lighthouse/locales/sl.json +112 -73
  120. package/front_end/third_party/lighthouse/locales/sr-Latn.json +112 -73
  121. package/front_end/third_party/lighthouse/locales/sr.json +112 -73
  122. package/front_end/third_party/lighthouse/locales/sv.json +124 -85
  123. package/front_end/third_party/lighthouse/locales/ta.json +112 -73
  124. package/front_end/third_party/lighthouse/locales/te.json +112 -73
  125. package/front_end/third_party/lighthouse/locales/th.json +112 -73
  126. package/front_end/third_party/lighthouse/locales/tr.json +113 -74
  127. package/front_end/third_party/lighthouse/locales/uk.json +112 -73
  128. package/front_end/third_party/lighthouse/locales/vi.json +112 -73
  129. package/front_end/third_party/lighthouse/locales/zh-HK.json +112 -73
  130. package/front_end/third_party/lighthouse/locales/zh-TW.json +113 -74
  131. package/front_end/third_party/lighthouse/locales/zh.json +113 -74
  132. package/front_end/third_party/lighthouse/report/bundle.d.ts +4 -4
  133. package/front_end/third_party/lighthouse/report/bundle.js +10 -3
  134. package/front_end/third_party/lighthouse/report-assets/report-generator.mjs +2 -2
  135. package/front_end/third_party/lit/lit.ts +3 -0
  136. package/front_end/third_party/lit/package.json +3 -0
  137. package/front_end/third_party/lit/rebuild.sh +3 -2
  138. package/front_end/ui/comments/CommentAnchorResolver.ts +8 -2
  139. package/front_end/ui/comments/CommentOverlayManager.ts +2 -1
  140. package/front_end/ui/components/buttons/Button.ts +19 -1
  141. package/front_end/ui/legacy/ContextMenu.ts +16 -1
  142. package/front_end/ui/legacy/InspectorView.ts +4 -3
  143. package/front_end/ui/legacy/PlusButton.ts +5 -6
  144. package/front_end/ui/legacy/TabbedPane.ts +121 -80
  145. package/front_end/ui/legacy/Treeoutline.ts +37 -6
  146. package/front_end/ui/legacy/UIUtils.ts +7 -0
  147. package/front_end/ui/legacy/Widget.ts +33 -0
  148. package/front_end/ui/legacy/components/cookie_table/CookiesTable.ts +3 -0
  149. package/front_end/ui/legacy/components/data_grid/DataGridElement.ts +14 -4
  150. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +157 -171
  151. package/front_end/ui/lit/lit.ts +1 -1
  152. package/front_end/ui/visual_logging/KnownContextValues.ts +7 -0
  153. package/inspector_overlay/highlight_anchor.ts +197 -0
  154. package/inspector_overlay/tool_highlight.ts +8 -0
  155. package/package.json +1 -1
  156. package/.agents/skills/devtools-ci/SKILL.md +0 -65
  157. package/.agents/skills/evaluate-ai-css-completion/SKILL.md +0 -143
  158. package/.agents/skills/evaluate-ai-css-completion/scripts/evaluate.js +0 -104
  159. package/.agents/skills/fixing-skipped-tests/SKILL.md +0 -32
  160. package/.agents/skills/gerrit-cli/SKILL.md +0 -74
  161. package/.agents/skills/gerrit-cli/references/commands.md +0 -169
  162. package/.agents/skills/gerrit-cli/references/workflows.md +0 -63
  163. package/.agents/skills/gerrit-cli/scripts/import_upstream.py +0 -85
  164. package/front_end/models/issues_manager/descriptions/cookieExcludeBlockedWithinRelatedWebsiteSet.md +0 -4
@@ -4,13 +4,15 @@
4
4
 
5
5
  import type * as Common from '../../core/common/common.js';
6
6
  import * as Root from '../../core/root/root.js';
7
+ import * as SDK from '../../core/sdk/sdk.js';
8
+ import type * as Protocol from '../../generated/protocol.js';
7
9
  import * as CommentManager from '../../models/comment_manager/comment_manager.js';
8
10
  import * as Comments from '../../ui/comments/comments.js';
9
11
  import * as UI from '../../ui/legacy/legacy.js';
10
12
  import * as Lit from '../../ui/lit/lit.js';
11
13
 
12
14
  import commentsOverlayStyles from './commentsOverlay.css.js';
13
- import {CommentThreadWidget} from './CommentThreadWidget.js';
15
+ import {CommentThreadWidget, type Title} from './CommentThreadWidget.js';
14
16
 
15
17
  const {
16
18
  html,
@@ -27,6 +29,7 @@ export interface ViewInput {
27
29
  onPinClick: (threadId: string) => void;
28
30
  activeThread: CommentManager.CommentManager.CommentThread|null;
29
31
  activePin: Comments.CommentOverlayManager.PinPositionData|null;
32
+ title: Title;
30
33
  onAddComment: (text: string) => void;
31
34
  }
32
35
 
@@ -96,6 +99,7 @@ const DEFAULT_VIEW: View = (input: ViewInput, _output: undefined, target: HTMLEl
96
99
  )}px`,
97
100
  })}>
98
101
  ${UI.Widget.widget(CommentThreadWidget, {
102
+ title: input.title,
99
103
  comments: [...item.thread.comments],
100
104
  onAddComment: input.onAddComment,
101
105
  })}
@@ -115,6 +119,8 @@ export class CommentsOverlayWidget extends UI.Widget.Widget {
115
119
  readonly #commentManager: CommentManager.CommentManager.CommentManager;
116
120
  #commentOverlayManager: Comments.CommentOverlayManager.CommentOverlayManager;
117
121
  #activeThreadId: string|null = null;
122
+ #cachedTitle: Title = {text: ''};
123
+ #cachedTitleAnchor: CommentManager.CommentManager.CommentAnchorSignature|null = null;
118
124
 
119
125
  constructor(
120
126
  element: HTMLElement|undefined,
@@ -157,6 +163,11 @@ export class CommentsOverlayWidget extends UI.Widget.Widget {
157
163
  this.#onCommentModeChanged,
158
164
  this,
159
165
  );
166
+ this.#commentManager.addEventListener(
167
+ CommentManager.CommentManager.Events.AGENT_ATTACHED_CHANGED,
168
+ this.#onAgentAttachedChanged,
169
+ this,
170
+ );
160
171
 
161
172
  this.requestUpdate();
162
173
  }
@@ -184,10 +195,25 @@ export class CommentsOverlayWidget extends UI.Widget.Widget {
184
195
  this.#onCommentModeChanged,
185
196
  this,
186
197
  );
198
+ this.#commentManager.removeEventListener(
199
+ CommentManager.CommentManager.Events.AGENT_ATTACHED_CHANGED,
200
+ this.#onAgentAttachedChanged,
201
+ this,
202
+ );
187
203
 
188
204
  super.willHide();
189
205
  }
190
206
 
207
+ #onAgentAttachedChanged(
208
+ event: Common.EventTarget.EventTargetEvent<
209
+ CommentManager.CommentManager.EventTypes[CommentManager.CommentManager.Events.AGENT_ATTACHED_CHANGED]>,
210
+ ): void {
211
+ if (!event.data) {
212
+ this.#activeThreadId = null;
213
+ }
214
+ this.requestUpdate();
215
+ }
216
+
191
217
  #onCommentModeChanged(
192
218
  event: Common.EventTarget.EventTargetEvent<
193
219
  CommentManager.CommentManager.EventTypes[CommentManager.CommentManager.Events.COMMENT_MODE_CHANGED]>,
@@ -213,6 +239,43 @@ export class CommentsOverlayWidget extends UI.Widget.Widget {
213
239
  this.requestUpdate();
214
240
  }
215
241
 
242
+ async #getOrComputeTitle(anchor: CommentManager.CommentManager.CommentAnchorSignature|null): Promise<Title> {
243
+ if (anchor === this.#cachedTitleAnchor) {
244
+ return this.#cachedTitle;
245
+ }
246
+ const title = anchor ? await this.#computeTitle(anchor) : {text: ''};
247
+ this.#cachedTitleAnchor = anchor;
248
+ this.#cachedTitle = title;
249
+ return title;
250
+ }
251
+
252
+ async #computeTitle(anchor: CommentManager.CommentManager.CommentAnchorSignature): Promise<Title> {
253
+ if (anchor.node) {
254
+ const target = SDK.TargetManager.TargetManager.instance().targetById(anchor.node.targetId);
255
+ if (target) {
256
+ const deferredNode = new SDK.DOMModel.DeferredDOMNode(
257
+ target,
258
+ anchor.node.backendNodeId as Protocol.DOM.BackendNodeId,
259
+ );
260
+ const node = await deferredNode.resolvePromise();
261
+ if (node) {
262
+ return {node};
263
+ }
264
+ }
265
+ return {text: ''};
266
+ }
267
+
268
+ if (anchor.networkRequestId) {
269
+ const target = SDK.TargetManager.TargetManager.instance().primaryPageTarget();
270
+ const request = target?.model(SDK.NetworkManager.NetworkManager)?.requestForId(anchor.networkRequestId);
271
+ if (request) {
272
+ return {text: request.name()};
273
+ }
274
+ }
275
+
276
+ return {text: anchor.textSignature || ''};
277
+ }
278
+
216
279
  #handlePinClick = (threadId: string): void => {
217
280
  const thread = this.#commentManager.getCommentThread(threadId);
218
281
  if (this.#activeThreadId === threadId) {
@@ -227,16 +290,34 @@ export class CommentsOverlayWidget extends UI.Widget.Widget {
227
290
  this.requestUpdate();
228
291
  };
229
292
 
230
- override performUpdate(): void {
293
+ override async performUpdate(signal?: AbortSignal): Promise<void> {
294
+ if (!this.#commentManager.isAgentAttached()) {
295
+ this.#view(
296
+ {
297
+ pins: [],
298
+ highlights: [],
299
+ hoverHighlight: null,
300
+ commentMode: false,
301
+ onPinClick: this.#handlePinClick,
302
+ activeThread: null,
303
+ activePin: null,
304
+ title: {text: ''},
305
+ onAddComment: () => {},
306
+ },
307
+ undefined,
308
+ this.contentElement,
309
+ );
310
+ return;
311
+ }
312
+
313
+ const activeThread =
314
+ this.#activeThreadId ? this.#commentManager.getCommentThread(this.#activeThreadId) ?? null : null;
315
+ const title = await this.#getOrComputeTitle(activeThread?.anchor ?? null);
316
+ signal?.throwIfAborted();
317
+
231
318
  const pins = this.#commentOverlayManager.getPinPositions();
232
319
  const highlights = this.#commentOverlayManager.getHighlightRects();
233
-
234
- let activePin: Comments.CommentOverlayManager.PinPositionData|null = null;
235
- let activeThread: CommentManager.CommentManager.CommentThread|null = null;
236
- if (this.#activeThreadId) {
237
- activePin = pins.find(p => p.id === this.#activeThreadId) ?? null;
238
- activeThread = this.#commentManager.getCommentThread(this.#activeThreadId) ?? null;
239
- }
320
+ const activePin = this.#activeThreadId ? pins.find(p => p.id === this.#activeThreadId) ?? null : null;
240
321
 
241
322
  const viewInput: ViewInput = {
242
323
  pins,
@@ -246,6 +327,7 @@ export class CommentsOverlayWidget extends UI.Widget.Widget {
246
327
  onPinClick: this.#handlePinClick,
247
328
  activeThread,
248
329
  activePin,
330
+ title,
249
331
  onAddComment: (text: string) => {
250
332
  activeThread?.save(text);
251
333
  },
@@ -291,3 +373,27 @@ export class ActionDelegate implements UI.ActionRegistration.ActionDelegate {
291
373
  }
292
374
  }
293
375
  }
376
+
377
+ export class ButtonProvider implements UI.Toolbar.Provider {
378
+ readonly #button: UI.Toolbar.ToolbarButton;
379
+ readonly #commentManager: CommentManager.CommentManager.CommentManager;
380
+
381
+ constructor(commentManager?: CommentManager.CommentManager.CommentManager) {
382
+ this.#commentManager = commentManager ??
383
+ Root.DevToolsContext.globalInstance().get(
384
+ CommentManager.CommentManager.CommentManager,
385
+ );
386
+ this.#button = UI.Toolbar.Toolbar.createActionButton('comments.toggle-comment-mode');
387
+ this.#button.setVisible(this.#commentManager.isAgentAttached());
388
+ this.#commentManager.addEventListener(
389
+ CommentManager.CommentManager.Events.AGENT_ATTACHED_CHANGED,
390
+ event => {
391
+ this.#button.setVisible(event.data);
392
+ },
393
+ );
394
+ }
395
+
396
+ item(): UI.Toolbar.ToolbarItem|null {
397
+ return this.#button;
398
+ }
399
+ }
@@ -72,6 +72,11 @@ export class CommentsStatusBarPill extends UI.Widget.Widget {
72
72
  this.#onThreadsChanged,
73
73
  this,
74
74
  );
75
+ this.#commentManager.addEventListener(
76
+ CommentManager.CommentManager.Events.AGENT_ATTACHED_CHANGED,
77
+ this.#onThreadsChanged,
78
+ this,
79
+ );
75
80
  this.requestUpdate();
76
81
  }
77
82
 
@@ -81,12 +86,19 @@ export class CommentsStatusBarPill extends UI.Widget.Widget {
81
86
  this.#onThreadsChanged,
82
87
  this,
83
88
  );
89
+ this.#commentManager.removeEventListener(
90
+ CommentManager.CommentManager.Events.AGENT_ATTACHED_CHANGED,
91
+ this.#onThreadsChanged,
92
+ this,
93
+ );
84
94
  super.willHide();
85
95
  }
86
96
 
87
97
  override performUpdate(): void {
88
98
  const viewInput: ViewInput = {
89
- threads: this.#commentManager.getCommentThreads().filter(thread => thread.status !== 'DRAFT'),
99
+ threads: this.#commentManager.isAgentAttached() ?
100
+ this.#commentManager.getCommentThreads().filter(thread => thread.status !== 'DRAFT') :
101
+ [],
90
102
  onPillClick: this.#handlePillClick,
91
103
  };
92
104
  this.#view(viewInput, undefined, this.contentElement);
@@ -584,7 +584,8 @@ export class ExtensionServer extends Common.ObjectWrapper.ObjectWrapper<EventTyp
584
584
  return this.subscribers.has(type);
585
585
  }
586
586
 
587
- private postNotification(type: string, args: unknown[], filter?: (extension: RegisteredExtension) => boolean): void {
587
+ private postNotification(type: string, args: unknown[], filter?: (extension: RegisteredExtension) => boolean,
588
+ argsForExtension?: (extension: RegisteredExtension) => unknown[]): void {
588
589
  if (!this.extensionsEnabled) {
589
590
  return;
590
591
  }
@@ -592,18 +593,22 @@ export class ExtensionServer extends Common.ObjectWrapper.ObjectWrapper<EventTyp
592
593
  if (!subscribers) {
593
594
  return;
594
595
  }
595
- const message = {command: 'notify-' + type, arguments: args};
596
596
  for (const subscriber of subscribers) {
597
597
  if (!this.extensionEnabled(subscriber)) {
598
598
  continue;
599
599
  }
600
- if (filter) {
600
+ let extension: RegisteredExtension|undefined;
601
+ if (filter || argsForExtension) {
601
602
  const origin = extensionOrigins.get(subscriber);
602
- const extension = origin && this.registeredExtensions.get(origin);
603
- if (!extension || !filter(extension)) {
603
+ extension = origin && this.registeredExtensions.get(origin);
604
+ if (!extension || (filter && !filter(extension))) {
604
605
  continue;
605
606
  }
606
607
  }
608
+ const message = {
609
+ command: 'notify-' + type,
610
+ arguments: extension && argsForExtension ? argsForExtension(extension) : args,
611
+ };
607
612
  subscriber.postMessage(message);
608
613
  }
609
614
  }
@@ -1104,10 +1109,9 @@ export class ExtensionServer extends Common.ObjectWrapper.ObjectWrapper<EventTyp
1104
1109
  return this.evaluate(expression, true, true, evaluateOptions, this.getExtensionOrigin(port), callback.bind(this));
1105
1110
  }
1106
1111
 
1107
- private harEntryReferencesBlockedURL(entry: HAR.Log.EntryDTO, extension: RegisteredExtension): boolean {
1112
+ private sanitizeHarEntry(entry: HAR.Log.EntryDTO, extension: RegisteredExtension): HAR.Log.EntryDTO {
1108
1113
  const baseURL = entry.request.url;
1109
1114
 
1110
- // Helper to cleanly resolve and check permission
1111
1115
  const isBlocked = (url: string|undefined): boolean => {
1112
1116
  if (!url) {
1113
1117
  return false;
@@ -1116,44 +1120,49 @@ export class ExtensionServer extends Common.ObjectWrapper.ObjectWrapper<EventTyp
1116
1120
  const absoluteURL = new URL(url, baseURL).toString() as Platform.DevToolsPath.UrlString;
1117
1121
  return !extension.isAllowedOnTarget(absoluteURL);
1118
1122
  } catch {
1119
- return true; // Fallback safely: treat unparsable URLs as blocked
1123
+ return true;
1120
1124
  }
1121
1125
  };
1122
1126
 
1123
- if (isBlocked(entry.response.redirectURL)) {
1124
- return true;
1125
- }
1127
+ let initiator = entry._initiator;
1126
1128
  if (entry._initiator) {
1127
1129
  if (isBlocked(entry._initiator.url)) {
1128
- return true;
1129
- }
1130
- let stack = entry._initiator.stack;
1131
- while (stack) {
1132
- if (stack.callFrames.some(f => isBlocked(f.url))) {
1133
- return true;
1130
+ initiator = null;
1131
+ } else {
1132
+ let stack = entry._initiator.stack;
1133
+ while (stack) {
1134
+ if (stack.callFrames.some(f => isBlocked(f.url))) {
1135
+ initiator = null;
1136
+ break;
1137
+ }
1138
+ stack = stack.parent;
1134
1139
  }
1135
- stack = stack.parent;
1136
1140
  }
1137
1141
  }
1138
- for (const header of entry.response.headers) {
1142
+
1143
+ const headerReferencesBlockedURL = (header: {name: string, value: string}): boolean => {
1139
1144
  const name = header.name.toLowerCase();
1140
1145
  if (name === 'location' || name === 'content-location') {
1141
- if (isBlocked(header.value)) {
1142
- return true;
1143
- }
1144
- } else if (name === 'refresh') {
1146
+ return isBlocked(header.value);
1147
+ }
1148
+ if (name === 'refresh') {
1145
1149
  const match = header.value.match(/;\s*url\s*=\s*(.+)$/i);
1146
- if (isBlocked(match?.[1]?.trim())) {
1147
- return true;
1148
- }
1149
- } else if (name === 'link') {
1150
+ return isBlocked(match?.[1]?.trim());
1151
+ }
1152
+ if (name === 'link') {
1150
1153
  const urls = [...header.value.matchAll(/<([^>]+)>/g)].map(m => m[1]);
1151
- if (urls.some(url => isBlocked(url))) {
1152
- return true;
1153
- }
1154
+ return urls.some(url => isBlocked(url));
1154
1155
  }
1156
+ return false;
1157
+ };
1158
+
1159
+ const headers = entry.response.headers.filter(header => !headerReferencesBlockedURL(header));
1160
+ const redirectURL = isBlocked(entry.response.redirectURL) ? '' : entry.response.redirectURL;
1161
+ if (initiator === entry._initiator && headers.length === entry.response.headers.length &&
1162
+ redirectURL === entry.response.redirectURL) {
1163
+ return entry;
1155
1164
  }
1156
- return false;
1165
+ return {...entry, _initiator: initiator, response: {...entry.response, headers, redirectURL}};
1157
1166
  }
1158
1167
 
1159
1168
  private async onGetHAR(message: Extensions.ExtensionAPI.PrivateAPI.ExtensionServerRequestMessage, port: MessagePort):
@@ -1173,7 +1182,7 @@ export class ExtensionServer extends Common.ObjectWrapper.ObjectWrapper<EventTyp
1173
1182
  // @ts-expect-error
1174
1183
  harLog.entries[i]._requestId = this.requestId(requests[i]);
1175
1184
  }
1176
- harLog.entries = harLog.entries.filter(entry => !this.harEntryReferencesBlockedURL(entry, extension));
1185
+ harLog.entries = harLog.entries.map(entry => this.sanitizeHarEntry(entry, extension));
1177
1186
  return harLog;
1178
1187
  }
1179
1188
 
@@ -1513,8 +1522,8 @@ export class ExtensionServer extends Common.ObjectWrapper.ObjectWrapper<EventTyp
1513
1522
  this.postNotification(
1514
1523
  Extensions.ExtensionAPI.PrivateAPI.Events.NetworkRequestFinished, [this.requestId(request), entry],
1515
1524
  extension => extension.isAllowedOnTarget(entry.request.url as Platform.DevToolsPath.UrlString) &&
1516
- (!targetUrl || extension.isAllowedOnTarget(targetUrl)) &&
1517
- !this.harEntryReferencesBlockedURL(entry, extension));
1525
+ (!targetUrl || extension.isAllowedOnTarget(targetUrl)),
1526
+ extension => [this.requestId(request), this.sanitizeHarEntry(entry, extension)]);
1518
1527
  }
1519
1528
 
1520
1529
  private notifyElementsSelectionChanged(): void {
@@ -26,7 +26,7 @@ const UIStrings = {
26
26
  /**
27
27
  * @description ARIA label for the dialog.
28
28
  */
29
- dialogAriaLabel: 'Gemini 3 Flash in DevTools',
29
+ dialogAriaLabel: '`Gemini 3 Flash` in `DevTools`',
30
30
  /**
31
31
  * @description Button text for dismissing the dialog.
32
32
  */
@@ -27,6 +27,7 @@
27
27
  width: 100%;
28
28
  justify-content: space-between;
29
29
  align-items: center;
30
+ gap: var(--sys-size-4);
30
31
  }
31
32
 
32
33
  .sent-status {
@@ -35,6 +36,7 @@
35
36
  gap: var(--sys-size-2);
36
37
  font-size: var(--sys-typescale-body5-size);
37
38
  color: var(--sys-color-on-surface-subtle);
39
+ flex-shrink: 0;
38
40
  }
39
41
 
40
42
  .check-icon {
@@ -165,7 +167,11 @@
165
167
  cursor: pointer;
166
168
  }
167
169
 
170
+ .selected-item devtools-widget,
168
171
  .selected-item-text {
172
+ display: block;
173
+ flex: 0 1 auto;
174
+ min-width: 0;
169
175
  overflow: hidden;
170
176
  white-space: nowrap;
171
177
  text-overflow: ellipsis;
@@ -65,7 +65,10 @@ UI.ActionRegistration.registerActionExtension({
65
65
  });
66
66
 
67
67
  UI.Toolbar.registerToolbarItem({
68
- actionId: 'comments.toggle-comment-mode',
68
+ async loadItem() {
69
+ const Common = await loadCommonModule();
70
+ return new Common.CommentsOverlayWidget.ButtonProvider();
71
+ },
69
72
  location: UI.Toolbar.ToolbarItemLocation.MAIN_TOOLBAR_LEFT,
70
73
  order: 1,
71
74
  condition: isCommentsEnabled,
@@ -72,7 +72,7 @@ const UIStrings = {
72
72
  * @example {Arial} PH5
73
73
  */
74
74
  fontVariationSettingsWarning:
75
- 'Value for setting "{PH1}" {PH2} is outside the supported range [{PH3}, {PH4}] for font-family "{PH5}"',
75
+ 'Value for setting "{PH1}" {PH2} is outside the supported range [{PH3}, {PH4}] for font-family "{PH5}".',
76
76
  /**
77
77
  * @description The message shown in the Styles tab when the user hovers over a property declaration that has no effect on flex or grid child items.
78
78
  * @example {flex} CONTAINER_DISPLAY_NAME
@@ -868,7 +868,8 @@ export class ElementsPanel extends UI.Panel.Panel implements UI.SearchableView.S
868
868
 
869
869
  // If any of these properties are undefined or reset to an invalid value,
870
870
  // this means the search/highlight request is outdated.
871
- const highlightRequestValid = this.searchConfig && this.searchResults && (this.currentSearchResultIndex !== -1);
871
+ const highlightRequestValid =
872
+ this.searchConfig && this.searchResults && (this.currentSearchResultIndex === index);
872
873
  if (highlightRequestValid) {
873
874
  this.highlightCurrentSearchResult();
874
875
  }
@@ -877,12 +878,11 @@ export class ElementsPanel extends UI.Panel.Panel implements UI.SearchableView.S
877
878
  }
878
879
 
879
880
  void searchResult.node.scrollIntoView();
880
- if (searchResult.node) {
881
- this.#domTreeWidget.highlightMatch(searchResult.node, this.searchConfig?.query);
882
- }
881
+ this.#domTreeWidget.highlightMatch(searchResult.node, this.searchConfig?.query);
883
882
  }
884
883
 
885
884
  private hideSearchHighlights(): void {
885
+ SDK.OverlayModel.OverlayModel.hideDOMNodeHighlight(this.#targetManager);
886
886
  if (!this.searchResults?.length || this.currentSearchResultIndex === -1) {
887
887
  return;
888
888
  }
@@ -1821,11 +1821,16 @@ export class ElementsTreeWidget extends UI.Widget.Widget {
1821
1821
  }
1822
1822
  }
1823
1823
 
1824
- hideSearchHighlights(): void {
1824
+ #clearHighlights(): void {
1825
1825
  Highlighting.HighlightManager.HighlightManager.instance().removeHighlights(this.#highlights);
1826
1826
  this.#highlights = [];
1827
1827
  }
1828
1828
 
1829
+ hideSearchHighlights(): void {
1830
+ this.#searchQuery = null;
1831
+ this.#clearHighlights();
1832
+ }
1833
+
1829
1834
  setInClipboard(inClipboard: boolean): void {
1830
1835
  if (this.inClipboard === inClipboard) {
1831
1836
  return;
@@ -2949,7 +2954,7 @@ export class ElementsTreeWidget extends UI.Widget.Widget {
2949
2954
  }
2950
2955
 
2951
2956
  #highlightSearchResults(): void {
2952
- this.hideSearchHighlights();
2957
+ this.#clearHighlights();
2953
2958
 
2954
2959
  if (!this.searchQuery) {
2955
2960
  return;
@@ -3173,6 +3178,8 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
3173
3178
  drag: true,
3174
3179
  click: true,
3175
3180
  })}`);
3181
+ this.listItemElement.setAttribute('data-backend-node-id', String(node.backendNodeId()));
3182
+ this.listItemElement.setAttribute('data-target-id', node.domModel().target().id());
3176
3183
 
3177
3184
  this.widgetWrapper = document.createElement('div');
3178
3185
  this.widgetWrapper.style.display = 'contents';
@@ -919,6 +919,7 @@ export const DECLARATIVE_VIEW: View = (input: ViewInput, _output: ViewOutput, ta
919
919
  // clang-format off
920
920
  return html`
921
921
  <li role="treeitem"
922
+ selectable=${input.selectEnabled ? 'true' : 'false'}
922
923
  ?selected=${isSelected && !input.selectedClosingTag}
923
924
  class=${classes}
924
925
  style=${styleMap({'--indent': `${computeLeftIndent(depth, isExpandable)}px`})}
@@ -933,8 +934,7 @@ export const DECLARATIVE_VIEW: View = (input: ViewInput, _output: ViewOutput, ta
933
934
  @dragleave=${on(onDragLeave)}
934
935
  @drop=${on(onDrop)}
935
936
  @dragend=${on(onDragEnd)}
936
- jslog=${treeItemJslog(undefined, true)}>
937
- ${UI.Widget.widget(ElementsTreeWidget, {
937
+ jslog=${treeItemJslog(undefined, true)}>${UI.Widget.widget(ElementsTreeWidget, {
938
938
  node,
939
939
  isClosingTag: false,
940
940
  renderSelection: false,
@@ -976,32 +976,28 @@ export const DECLARATIVE_VIEW: View = (input: ViewInput, _output: ViewOutput, ta
976
976
  }
977
977
  },
978
978
  updateRecord: input.updateRecordForNode?.(node) ?? null,
979
- })}
980
- ${hasChildren ? html`
981
- <ul role="group">
982
- ${UI.TreeOutline.ifExpanded(html`
979
+ })}${hasChildren ? html`<ul role="group">
980
+ ${isExpanded && !isEditingAsHTML ? html`
983
981
  ${node.adoptedStyleSheetsForNode.length > 0 ? renderAdoptedStyleSheets(node, depth + 1) : nothing}
984
982
  ${repeat(children, child => child.id, child => renderNode(child, depth + 1))}
985
983
  ${remainingChildrenCount > 0 ? html`
986
984
  <li role="treeitem"
985
+ selectable="false"
987
986
  class="elements-tree-expand-all"
988
987
  style=${styleMap({'--indent': `${computeLeftIndent(depth + 1, false)}px`})}
989
- jslog=${treeItemJslog('show-all-nodes')}>
990
- <devtools-button
988
+ jslog=${treeItemJslog('show-all-nodes')}><devtools-button
991
989
  .variant=${Buttons.Button.Variant.OUTLINED}
992
990
  title=${i18nString(UIStrings.showAllNodesDMore, {PH1: remainingChildrenCount})}
993
991
  @click=${on((event: Event) => {
994
992
  event.stopPropagation();
995
993
  input.onExpandAllChildren?.(node);
996
- })}>
997
- ${i18nString(UIStrings.showAllNodesDMore, {PH1: remainingChildrenCount})}
998
- </devtools-button>
999
- </li>
994
+ })}>${i18nString(UIStrings.showAllNodesDMore, {PH1: remainingChildrenCount})}</devtools-button></li>
1000
995
  ` : nothing}
1001
996
  ${node.isInsertionPoint() ? node.distributedNodes().map(distributedNode => renderShortcut(distributedNode, depth + 1)) : nothing}
1002
997
  ${node instanceof SDK.DOMModel.DOMDocument ? renderTopLayerContainer(node, depth + 1) : nothing}
1003
998
  ${needsClosingTag ? html`
1004
999
  <li role="treeitem"
1000
+ selectable=${input.selectEnabled ? 'true' : 'false'}
1005
1001
  ?selected=${isSelected && Boolean(input.selectedClosingTag)}
1006
1002
  class=${classMap({
1007
1003
  selected: isSelected && Boolean(input.selectedClosingTag),
@@ -1018,8 +1014,7 @@ export const DECLARATIVE_VIEW: View = (input: ViewInput, _output: ViewOutput, ta
1018
1014
  @dragover=${on(onClosingTagDragOver)}
1019
1015
  @dragleave=${on(onDragLeave)}
1020
1016
  @drop=${on(onClosingTagDrop)}
1021
- @dragend=${on(onDragEnd)}>
1022
- ${UI.Widget.widget(ElementsTreeWidget, {
1017
+ @dragend=${on(onDragEnd)}>${UI.Widget.widget(ElementsTreeWidget, {
1023
1018
  node,
1024
1019
  isClosingTag: true,
1025
1020
  renderSelection: false,
@@ -1037,13 +1032,10 @@ export const DECLARATIVE_VIEW: View = (input: ViewInput, _output: ViewOutput, ta
1037
1032
  }
1038
1033
  },
1039
1034
  updateRecord: input.updateRecordForNode?.(node) ?? null,
1040
- })}
1041
- </li>
1035
+ })}</li>
1042
1036
  ` : nothing}
1043
- `)}
1044
- </ul>
1045
- ` : nothing}
1046
- </li>
1037
+ ` : nothing}
1038
+ </ul>` : nothing}</li>
1047
1039
  `;
1048
1040
  // clang-format on
1049
1041
  };
@@ -1070,7 +1062,7 @@ export const DECLARATIVE_VIEW: View = (input: ViewInput, _output: ViewOutput, ta
1070
1062
  <style>${CodeHighlighter.codeHighlighterStyles}</style>
1071
1063
  <div class=${disclosureClasses} style=${disclosureStyles}>
1072
1064
  <devtools-tree
1073
- class="elements-tree-outline source-code ${input.wrap ? '' : 'elements-tree-nowrap'} ${input.hideGutter ? 'elements-hide-gutter' : ''} ${isSingleNode ? 'single-node' : ''}"
1065
+ class="elements-tree-outline ${input.wrap ? '' : 'elements-tree-nowrap'} ${input.hideGutter ? 'elements-hide-gutter' : ''} ${isSingleNode ? 'single-node' : ''}"
1074
1066
  disclosure-class="elements-disclosure ${isSingleNode ? 'single-node' : ''} ${input.maxRowsShown ? 'elements-tree-truncated' : ''}"
1075
1067
  jslog=${VisualLogging.tree('elements')}
1076
1068
  ?show-selection-on-keyboard-focus=${input.showSelectionOnKeyboardFocus}
@@ -1096,19 +1088,16 @@ export const DECLARATIVE_VIEW: View = (input: ViewInput, _output: ViewOutput, ta
1096
1088
  const root = input.rootDOMNode;
1097
1089
  return html`
1098
1090
  <li role="treeitem"
1091
+ selectable="false"
1099
1092
  class="elements-tree-expand-all"
1100
1093
  style=${styleMap({'--indent': `${computeLeftIndent(0, false)}px`})}
1101
- jslog=${treeItemJslog('show-all-nodes')}>
1102
- <devtools-button
1094
+ jslog=${treeItemJslog('show-all-nodes')}><devtools-button
1103
1095
  .variant=${Buttons.Button.Variant.OUTLINED}
1104
1096
  title=${i18nString(UIStrings.showAllNodesDMore, {PH1: remaining})}
1105
1097
  @click=${on((event: Event) => {
1106
1098
  event.stopPropagation();
1107
1099
  input.onExpandAllChildren?.(root);
1108
- })}>
1109
- ${i18nString(UIStrings.showAllNodesDMore, {PH1: remaining})}
1110
- </devtools-button>
1111
- </li>
1100
+ })}>${i18nString(UIStrings.showAllNodesDMore, {PH1: remaining})}</devtools-button></li>
1112
1101
  `;
1113
1102
  })() : nothing}
1114
1103
  ${input.omitRootDOMNode && input.rootDOMNode instanceof SDK.DOMModel.DOMDocument ? renderTopLayerContainer(input.rootDOMNode, 0) : nothing}
@@ -1475,7 +1464,6 @@ export class DOMTreeWidget extends UI.Widget.Widget {
1475
1464
  if (domModel.existingDocument()) {
1476
1465
  this.rootDOMNode = domModel.existingDocument();
1477
1466
  }
1478
- this.onDocumentUpdated(domModel);
1479
1467
  }
1480
1468
 
1481
1469
  #updateModifiedNodesTimeout?: number;
@@ -1529,12 +1517,12 @@ export class DOMTreeWidget extends UI.Widget.Widget {
1529
1517
  event: Common.EventTarget.EventTargetEvent<{node: SDK.DOMModel.DOMNode, parent: SDK.DOMModel.DOMNode}>): void {
1530
1518
  const {node, parent} = event.data;
1531
1519
  this.resetClipboardIfNeeded(node);
1532
- if (this.#selectedDOMNode && (this.#selectedDOMNode === node || node.isAncestor(this.#selectedDOMNode))) {
1533
- this.selectDOMNode(this.#findNextNodeOnRemoval(node, parent), true);
1534
- }
1535
1520
  if (parent) {
1536
1521
  this.#addUpdateRecord(parent).nodeRemoved(node);
1537
1522
  }
1523
+ if (this.#selectedDOMNode && (this.#selectedDOMNode === node || node.isAncestor(this.#selectedDOMNode))) {
1524
+ this.selectDOMNode(this.#findNextNodeOnRemoval(node, parent), true);
1525
+ }
1538
1526
  this.#updateModifiedNodesSoon();
1539
1527
  }
1540
1528
 
@@ -1682,6 +1670,11 @@ export class DOMTreeWidget extends UI.Widget.Widget {
1682
1670
  }
1683
1671
  this.#selectedAdoptedStyleSheet = null;
1684
1672
  if (this.#view === DECLARATIVE_VIEW) {
1673
+ if (node?.nodeType() === Node.TEXT_NODE && node.parentNode &&
1674
+ (!nodeHasVisibleChildren(node.parentNode, this.rootDOMNode, this.maxTreeDepth, this.omitRootDOMNode) ||
1675
+ !getVisibleChildren(node.parentNode, this.#showComments).includes(node))) {
1676
+ node = node.parentNode;
1677
+ }
1685
1678
  const isSameNode = this.#selectedDOMNode === node && this.#selectedClosingTag === Boolean(isClosingTag);
1686
1679
  this.#selectedDOMNode = node;
1687
1680
  this.#selectedClosingTag = Boolean(isClosingTag);
@@ -2326,9 +2319,8 @@ export class DOMTreeWidget extends UI.Widget.Widget {
2326
2319
  this.#searchMatchQuery = query ?? null;
2327
2320
  if (this.selectedDOMNode() !== node) {
2328
2321
  this.selectDOMNode(node, /* focus= */ false);
2329
- } else {
2330
- this.performUpdate();
2331
2322
  }
2323
+ this.performUpdate();
2332
2324
  }
2333
2325
 
2334
2326
  hideMatchHighlights(node: SDK.DOMModel.DOMNode): void {