chrome-devtools-frontend 1.0.1642845 → 1.0.1643099

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 (128) hide show
  1. package/SECURITY.md +1 -0
  2. package/front_end/core/host/UserMetrics.ts +2 -1
  3. package/front_end/core/protocol_client/InspectorBackend.ts +4 -0
  4. package/front_end/core/sdk/CSSMatchedStyles.ts +55 -26
  5. package/front_end/core/sdk/CSSRule.ts +1 -0
  6. package/front_end/core/sdk/DebuggerModel.ts +5 -0
  7. package/front_end/entrypoints/greendev_floaty/FloatyEntrypoint.ts +4 -3
  8. package/front_end/entrypoints/greendev_floaty/greendev_floaty.ts +4 -3
  9. package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshot.ts +4 -5
  10. package/front_end/generated/InspectorBackendCommands.ts +1 -1
  11. package/front_end/generated/protocol.ts +7 -0
  12. package/front_end/models/ai_assistance/AiAgent2.ts +100 -18
  13. package/front_end/models/ai_assistance/AiConversation.ts +18 -14
  14. package/front_end/models/ai_assistance/AiUtils.ts +71 -0
  15. package/front_end/models/ai_assistance/ChangeManager.ts +2 -5
  16. package/front_end/models/ai_assistance/{agents/ConversationSummaryAgent.ts → ConversationSummary.ts} +29 -66
  17. package/front_end/models/ai_assistance/ExtensionScope.ts +1 -4
  18. package/front_end/models/ai_assistance/{agents/PerformanceAnnotationsAgent.ts → PerformanceAnnotations.ts} +47 -89
  19. package/front_end/models/ai_assistance/README.md +8 -0
  20. package/front_end/models/ai_assistance/agents/AccessibilityAgent.ts +65 -40
  21. package/front_end/models/ai_assistance/agents/AiAgent.ts +37 -6
  22. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.snapshot.txt +11 -0
  23. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +55 -5
  24. package/front_end/models/ai_assistance/agents/ExecuteJavascript.ts +2 -0
  25. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +119 -78
  26. package/front_end/models/ai_assistance/agents/StorageAgent.ts +47 -38
  27. package/front_end/models/ai_assistance/agents/StylingAgent.snapshot.txt +0 -25
  28. package/front_end/models/ai_assistance/agents/StylingAgent.ts +46 -326
  29. package/front_end/models/ai_assistance/ai_assistance.ts +14 -4
  30. package/front_end/models/ai_assistance/contexts/DOMNodeContext.snapshot.txt +51 -0
  31. package/front_end/models/ai_assistance/contexts/DOMNodeContext.ts +200 -0
  32. package/front_end/models/ai_assistance/skills/styling.md +44 -2
  33. package/front_end/models/ai_assistance/tools/ExecuteJavaScript.ts +140 -0
  34. package/front_end/models/ai_assistance/tools/GetStyles.ts +141 -0
  35. package/front_end/models/ai_assistance/tools/Tool.ts +64 -0
  36. package/front_end/models/ai_assistance/tools/ToolRegistry.ts +36 -0
  37. package/front_end/models/heap_snapshot/HeapSnapshotProxy.ts +5 -7
  38. package/front_end/models/lighthouse/LighthouseReporterTypes.ts +5 -0
  39. package/front_end/models/live-metrics/LiveMetrics.ts +24 -13
  40. package/front_end/models/stack_trace/DetailedErrorStackParser.ts +2 -2
  41. package/front_end/models/stack_trace/StackTrace.ts +4 -1
  42. package/front_end/models/stack_trace/StackTraceImpl.ts +9 -2
  43. package/front_end/models/stack_trace/StackTraceModel.ts +17 -4
  44. package/front_end/models/stack_trace/Trie.ts +1 -1
  45. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +25 -22
  46. package/front_end/panels/ai_assistance/ai_assistance-meta.ts +16 -0
  47. package/front_end/panels/ai_assistance/components/ChatInput.ts +2 -2
  48. package/front_end/panels/ai_assistance/components/ChatMessage.ts +96 -4
  49. package/front_end/panels/ai_assistance/components/chatMessage.css +6 -0
  50. package/front_end/panels/application/DOMStorageItemsView.ts +4 -0
  51. package/front_end/panels/application/KeyValueStorageItemsView.ts +39 -7
  52. package/front_end/panels/application/components/AdsView.ts +219 -0
  53. package/front_end/panels/application/components/adsView.css +54 -0
  54. package/front_end/panels/application/components/components.ts +2 -0
  55. package/front_end/panels/common/ExtensionServer.ts +26 -15
  56. package/front_end/panels/console/SymbolizedErrorWidget.ts +73 -22
  57. package/front_end/panels/elements/StandaloneStylesContainer.ts +1 -1
  58. package/front_end/panels/elements/StylePropertiesSection.ts +8 -0
  59. package/front_end/panels/elements/StylePropertyHighlighter.ts +4 -2
  60. package/front_end/panels/elements/StylePropertyTreeElement.ts +6 -5
  61. package/front_end/panels/elements/StylesContainer.ts +1 -1
  62. package/front_end/panels/elements/StylesSidebarPane.ts +4 -4
  63. package/front_end/panels/layer_viewer/PaintProfilerView.ts +106 -132
  64. package/front_end/panels/lighthouse/LighthousePanel.ts +4 -3
  65. package/front_end/panels/network/NetworkLogView.ts +8 -1
  66. package/front_end/panels/network/networkLogView.css +0 -15
  67. package/front_end/panels/timeline/overlays/components/EntryLabelOverlay.ts +5 -4
  68. package/front_end/third_party/chromium/README.chromium +1 -1
  69. package/front_end/third_party/lighthouse/README.chromium +2 -2
  70. package/front_end/third_party/lighthouse/lighthouse-dt-bundle.js +1607 -5733
  71. package/front_end/third_party/lighthouse/locales/ar-XB.json +290 -65
  72. package/front_end/third_party/lighthouse/locales/ar.json +290 -65
  73. package/front_end/third_party/lighthouse/locales/bg.json +290 -65
  74. package/front_end/third_party/lighthouse/locales/ca.json +295 -70
  75. package/front_end/third_party/lighthouse/locales/cs.json +290 -65
  76. package/front_end/third_party/lighthouse/locales/da.json +294 -69
  77. package/front_end/third_party/lighthouse/locales/de.json +295 -70
  78. package/front_end/third_party/lighthouse/locales/el.json +290 -65
  79. package/front_end/third_party/lighthouse/locales/en-GB.json +290 -65
  80. package/front_end/third_party/lighthouse/locales/en-US.json +79 -67
  81. package/front_end/third_party/lighthouse/locales/en-XA.json +253 -64
  82. package/front_end/third_party/lighthouse/locales/en-XL.json +79 -67
  83. package/front_end/third_party/lighthouse/locales/es-419.json +290 -65
  84. package/front_end/third_party/lighthouse/locales/es.json +298 -73
  85. package/front_end/third_party/lighthouse/locales/fi.json +290 -65
  86. package/front_end/third_party/lighthouse/locales/fil.json +290 -65
  87. package/front_end/third_party/lighthouse/locales/fr.json +294 -69
  88. package/front_end/third_party/lighthouse/locales/he.json +293 -68
  89. package/front_end/third_party/lighthouse/locales/hi.json +291 -66
  90. package/front_end/third_party/lighthouse/locales/hr.json +290 -65
  91. package/front_end/third_party/lighthouse/locales/hu.json +290 -65
  92. package/front_end/third_party/lighthouse/locales/id.json +290 -65
  93. package/front_end/third_party/lighthouse/locales/it.json +294 -69
  94. package/front_end/third_party/lighthouse/locales/ja.json +290 -65
  95. package/front_end/third_party/lighthouse/locales/ko.json +290 -65
  96. package/front_end/third_party/lighthouse/locales/lt.json +290 -65
  97. package/front_end/third_party/lighthouse/locales/lv.json +290 -65
  98. package/front_end/third_party/lighthouse/locales/nl.json +290 -65
  99. package/front_end/third_party/lighthouse/locales/no.json +290 -65
  100. package/front_end/third_party/lighthouse/locales/pl.json +290 -65
  101. package/front_end/third_party/lighthouse/locales/pt-PT.json +291 -66
  102. package/front_end/third_party/lighthouse/locales/pt.json +290 -65
  103. package/front_end/third_party/lighthouse/locales/ro.json +290 -65
  104. package/front_end/third_party/lighthouse/locales/ru.json +301 -76
  105. package/front_end/third_party/lighthouse/locales/sk.json +291 -66
  106. package/front_end/third_party/lighthouse/locales/sl.json +290 -65
  107. package/front_end/third_party/lighthouse/locales/sr-Latn.json +290 -65
  108. package/front_end/third_party/lighthouse/locales/sr.json +290 -65
  109. package/front_end/third_party/lighthouse/locales/sv.json +297 -72
  110. package/front_end/third_party/lighthouse/locales/ta.json +291 -66
  111. package/front_end/third_party/lighthouse/locales/te.json +293 -68
  112. package/front_end/third_party/lighthouse/locales/th.json +291 -66
  113. package/front_end/third_party/lighthouse/locales/tr.json +290 -65
  114. package/front_end/third_party/lighthouse/locales/uk.json +290 -65
  115. package/front_end/third_party/lighthouse/locales/vi.json +291 -66
  116. package/front_end/third_party/lighthouse/locales/zh-HK.json +292 -67
  117. package/front_end/third_party/lighthouse/locales/zh-TW.json +291 -66
  118. package/front_end/third_party/lighthouse/locales/zh.json +291 -66
  119. package/front_end/third_party/lighthouse/report/bundle.d.ts +6 -6
  120. package/front_end/third_party/lighthouse/report/bundle.js +4 -7
  121. package/front_end/third_party/lighthouse/report-assets/report-generator.mjs +2 -2
  122. package/front_end/ui/legacy/Widget.ts +32 -8
  123. package/front_end/ui/legacy/components/cookie_table/CookiesTable.ts +36 -3
  124. package/front_end/ui/legacy/components/data_grid/dataGridAiButton.css +20 -0
  125. package/front_end/ui/legacy/components/utils/Linkifier.ts +19 -4
  126. package/front_end/ui/visual_logging/KnownContextValues.ts +3 -0
  127. package/mcp/mcp.ts +1 -0
  128. package/package.json +1 -1
@@ -28,8 +28,13 @@
28
28
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
29
  */
30
30
  /* eslint no-return-assign: "off" */
31
+ import '../../ui/components/buttons/buttons.js';
32
+
31
33
  import * as i18n from '../../core/i18n/i18n.js';
34
+ import * as AIAssistance from '../../models/ai_assistance/ai_assistance.js';
32
35
  import * as Geometry from '../../models/geometry/geometry.js';
36
+ // eslint-disable-next-line @devtools/es-modules-import
37
+ import dataGridAiButtonStyles from '../../ui/legacy/components/data_grid/dataGridAiButton.css.js';
33
38
  import * as UI from '../../ui/legacy/legacy.js';
34
39
  import {Directives as LitDirectives, html, nothing, render} from '../../ui/lit/lit.js';
35
40
  import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
@@ -37,11 +42,13 @@ import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
37
42
  import * as ApplicationComponents from './components/components.js';
38
43
  import {StorageItemsToolbar} from './StorageItemsToolbar.js';
39
44
 
45
+ const STORAGE_FLOATING_BUTTON_ACTION_ID = 'ai-assistance.storage-floating-button';
46
+
40
47
  const {ARIAUtils} = UI;
41
48
  const {EmptyWidget} = UI.EmptyWidget;
42
49
  const {VBox, widget} = UI.Widget;
43
50
  const {Size} = Geometry;
44
- const {repeat} = LitDirectives;
51
+ const {repeat, ifDefined} = LitDirectives;
45
52
 
46
53
  type Widget = UI.Widget.Widget;
47
54
  type VBox = UI.Widget.VBox;
@@ -87,6 +94,9 @@ export interface ViewInput {
87
94
  onDeleteAll: () => void;
88
95
  jslog?: string;
89
96
  classes?: string[];
97
+ aiButtonTitle?: string;
98
+ showAiButton?: boolean;
99
+ onAiButtonClick?: (item: {key: string, value: string}, event: Event) => void;
90
100
  }
91
101
 
92
102
  interface ViewOutput {
@@ -150,6 +160,7 @@ export abstract class KeyValueStorageItemsView extends UI.Widget.VBox {
150
160
  @deselect=${() => input.onSelect(null)}
151
161
  >
152
162
  <table>
163
+ ${input.showAiButton ? html`<style>${dataGridAiButtonStyles}</style>`: nothing}
153
164
  <tr>
154
165
  <th id="key" sortable ?editable=${input.editable}>
155
166
  ${i18nString(UIStrings.key)}
@@ -165,7 +176,15 @@ export abstract class KeyValueStorageItemsView extends UI.Widget.VBox {
165
176
  input.onEdit(item.key, item.value, e.detail.columnId, e.detail.valueBeforeEditing, e.detail.newText)}
166
177
  @delete=${() => input.onDelete(item.key)}
167
178
  selected=${(input.selectedKey === item.key) || nothing}>
168
- <td>${item.key}</td>
179
+ <td>${input.showAiButton ? html`
180
+ <span class="ai-button-container">
181
+ <devtools-floating-button
182
+ icon-name=${AIAssistance.AiUtils.getIconName()}
183
+ title=${ifDefined(input.aiButtonTitle)}
184
+ @click=${(e: Event) => input.onAiButtonClick?.(item, e)}
185
+ ></devtools-floating-button>
186
+ </span>
187
+ ` : nothing}${item.key}</td>
169
188
  <td>${item.value.substr(0, MAX_VALUE_LENGTH)}</td>
170
189
  </tr>`)}
171
190
  <tr placeholder></tr>
@@ -217,15 +236,24 @@ export abstract class KeyValueStorageItemsView extends UI.Widget.VBox {
217
236
  preview: this.#preview,
218
237
  jslog: this.#jslog,
219
238
  classes: this.#classes,
239
+ showAiButton: this.isAiButtonEnabled(),
240
+ aiButtonTitle: this.isAiButtonEnabled() &&
241
+ UI.ActionRegistry.ActionRegistry.instance().hasAction(STORAGE_FLOATING_BUTTON_ACTION_ID) ?
242
+ UI.ActionRegistry.ActionRegistry.instance().getAction(STORAGE_FLOATING_BUTTON_ACTION_ID).title() :
243
+ undefined,
220
244
  onSelect: (item: {key: string, value: string}|null) => {
221
245
  this.#toolbar?.setCanDeleteSelected(Boolean(item));
222
- if (!item) {
223
- void this.#previewEntry(null);
224
- } else {
225
- void this.#previewEntry(item);
226
- }
246
+ void this.#previewEntry(item);
227
247
  this.selectedItemChanged(item);
228
248
  },
249
+ onAiButtonClick: (item: {key: string, value: string}, event: Event) => {
250
+ event.stopPropagation();
251
+ viewInput.onSelect(item);
252
+ const actionRegistry = UI.ActionRegistry.ActionRegistry.instance();
253
+ if (actionRegistry.hasAction(STORAGE_FLOATING_BUTTON_ACTION_ID)) {
254
+ void actionRegistry.getAction(STORAGE_FLOATING_BUTTON_ACTION_ID).execute();
255
+ }
256
+ },
229
257
 
230
258
  onSort: (ascending: boolean) => {
231
259
  this.#isSortOrderAscending = ascending;
@@ -252,6 +280,10 @@ export abstract class KeyValueStorageItemsView extends UI.Widget.VBox {
252
280
  this.#view(viewInput, viewOutput, this.contentElement);
253
281
  }
254
282
 
283
+ protected isAiButtonEnabled(): boolean {
284
+ return false;
285
+ }
286
+
255
287
  protected get toolbar(): StorageItemsToolbar|undefined {
256
288
  return this.#toolbar;
257
289
  }
@@ -0,0 +1,219 @@
1
+ // Copyright 2026 The Chromium Authors
2
+ // Use of this source code is governed by a BSD-style license that can be
3
+ // found in the LICENSE file.
4
+
5
+ import * as i18n from '../../../core/i18n/i18n.js';
6
+ import * as SDK from '../../../core/sdk/sdk.js';
7
+ import type * as Protocol from '../../../generated/protocol.js';
8
+ import * as UI from '../../../ui/legacy/legacy.js';
9
+ import * as Lit from '../../../ui/lit/lit.js';
10
+
11
+ import adsViewStyles from './adsView.css.js';
12
+
13
+ const {html} = Lit;
14
+
15
+ const UIStrings = {
16
+ /**
17
+ * @description Title for a metric showing the percentage of the viewport covered by ads.
18
+ */
19
+ viewportAdDensity: 'Viewport ad density',
20
+ /**
21
+ * @description Title for a metric showing the number of ads in the viewport.
22
+ */
23
+ viewportAdCount: 'Viewport ad count',
24
+ /**
25
+ * @description Title for a metric showing the total CPU usage by ads.
26
+ */
27
+ totalCpuUsage: 'Total CPU usage by ads',
28
+ /**
29
+ * @description Title for a metric showing the total network usage by ads.
30
+ */
31
+ totalNetworkUsage: 'Total network usage by ads',
32
+ /**
33
+ * @description Subtext showing the average value of a metric.
34
+ * @example {5.00%} PH1
35
+ */
36
+ average: '(Average: {PH1})',
37
+ } as const;
38
+
39
+ const str_ = i18n.i18n.registerUIStrings('panels/application/components/AdsView.ts', UIStrings);
40
+ const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
41
+
42
+ export interface ViewInput {
43
+ metrics: Protocol.Ads.AdMetrics;
44
+ }
45
+
46
+ export type View = (input: ViewInput, output: undefined, target: HTMLElement|DocumentFragment) => void;
47
+
48
+ const DEFAULT_VIEW: View = (input, output, target) => {
49
+ const metrics = input.metrics;
50
+
51
+ const formatValue = (val: number, isPercentage: boolean): string => {
52
+ if (isPercentage) {
53
+ return new Intl
54
+ .NumberFormat(i18n.DevToolsLocale.DevToolsLocale.instance().locale, {
55
+ style: 'percent',
56
+ maximumFractionDigits: 0,
57
+ })
58
+ .format(val / 100);
59
+ }
60
+ return new Intl.NumberFormat(i18n.DevToolsLocale.DevToolsLocale.instance().locale).format(val);
61
+ };
62
+
63
+ const formatAverage = (val: number, isPercentage: boolean): string => {
64
+ if (isPercentage) {
65
+ return new Intl
66
+ .NumberFormat(i18n.DevToolsLocale.DevToolsLocale.instance().locale, {
67
+ style: 'percent',
68
+ minimumFractionDigits: 2,
69
+ maximumFractionDigits: 2,
70
+ })
71
+ .format(val / 100);
72
+ }
73
+ return new Intl
74
+ .NumberFormat(i18n.DevToolsLocale.DevToolsLocale.instance().locale, {
75
+ minimumFractionDigits: 2,
76
+ maximumFractionDigits: 2,
77
+ })
78
+ .format(val);
79
+ };
80
+
81
+ const formatCpu = (val: number): string => {
82
+ return i18n.TimeUtilities.preciseMillisToString(val, 1);
83
+ };
84
+
85
+ const formatNetwork = (val: number): string => {
86
+ return i18n.ByteUtilities.bytesToString(val);
87
+ };
88
+
89
+ // clang-format off
90
+ Lit.render(html`
91
+ <style>${adsViewStyles}</style>
92
+ <dl class="metrics-container">
93
+ <div class="metric-box">
94
+ <dt class="metric-title">${i18nString(UIStrings.viewportAdDensity)}</dt>
95
+ <dd class="metric-value">
96
+ <span>${formatValue(metrics.viewportAdDensityByArea, true)}</span>
97
+ <span class="metric-average">${i18nString(UIStrings.average, {PH1: formatAverage(metrics.averageViewportAdDensityByArea, true)})}</span>
98
+ </dd>
99
+ </div>
100
+ <div class="metric-box">
101
+ <dt class="metric-title">${i18nString(UIStrings.viewportAdCount)}</dt>
102
+ <dd class="metric-value">
103
+ <span>${formatValue(metrics.viewportAdCount, false)}</span>
104
+ <span class="metric-average">${i18nString(UIStrings.average, {PH1: formatAverage(metrics.averageViewportAdCount, false)})}</span>
105
+ </dd>
106
+ </div>
107
+ <div class="metric-box">
108
+ <dt class="metric-title">${i18nString(UIStrings.totalCpuUsage)}</dt>
109
+ <dd class="metric-value">
110
+ <span>${formatCpu(metrics.totalAdCpuTime)}</span>
111
+ </dd>
112
+ </div>
113
+ <div class="metric-box">
114
+ <dt class="metric-title">${i18nString(UIStrings.totalNetworkUsage)}</dt>
115
+ <dd class="metric-value">
116
+ <span>${formatNetwork(metrics.totalAdNetworkBytes)}</span>
117
+ </dd>
118
+ </div>
119
+ </dl>
120
+ `, target);
121
+ // clang-format on
122
+ };
123
+
124
+ export class AdsView extends UI.Widget.Widget {
125
+ #currentMetrics: Protocol.Ads.AdMetrics;
126
+ #pollTimer?: number;
127
+ #isPolling = false;
128
+ #pollSessionId = 0;
129
+ #view: View;
130
+
131
+ constructor(view: View = DEFAULT_VIEW) {
132
+ super({useShadowDom: true});
133
+ this.#view = view;
134
+ this.#currentMetrics = {
135
+ viewportAdDensityByArea: 0,
136
+ averageViewportAdDensityByArea: 0,
137
+ viewportAdCount: 0,
138
+ averageViewportAdCount: 0,
139
+ totalAdCpuTime: 0,
140
+ totalAdNetworkBytes: 0,
141
+ };
142
+ this.requestUpdate();
143
+ }
144
+
145
+ override wasShown(): void {
146
+ super.wasShown();
147
+ this.#startPolling();
148
+ SDK.TargetManager.TargetManager.instance().addModelListener(SDK.ResourceTreeModel.ResourceTreeModel,
149
+ SDK.ResourceTreeModel.Events.PrimaryPageChanged,
150
+ this.#onPrimaryPageChanged, this);
151
+ }
152
+
153
+ override willHide(): void {
154
+ this.#stopPolling();
155
+ SDK.TargetManager.TargetManager.instance().removeModelListener(SDK.ResourceTreeModel.ResourceTreeModel,
156
+ SDK.ResourceTreeModel.Events.PrimaryPageChanged,
157
+ this.#onPrimaryPageChanged, this);
158
+ super.willHide();
159
+ }
160
+
161
+ #startPolling(): void {
162
+ if (this.#isPolling) {
163
+ return;
164
+ }
165
+ this.#isPolling = true;
166
+ this.#pollSessionId++;
167
+ void this.#pollMetrics(this.#pollSessionId);
168
+ }
169
+
170
+ #stopPolling(): void {
171
+ this.#isPolling = false;
172
+ if (this.#pollTimer !== undefined) {
173
+ window.clearTimeout(this.#pollTimer);
174
+ this.#pollTimer = undefined;
175
+ }
176
+ }
177
+
178
+ async #pollMetrics(sessionId: number): Promise<void> {
179
+ if (!this.#isPolling || this.#pollSessionId !== sessionId) {
180
+ return;
181
+ }
182
+ const target = SDK.TargetManager.TargetManager.instance().primaryPageTarget();
183
+ if (target) {
184
+ const adsAgent = target.adsAgent();
185
+ if (adsAgent) {
186
+ const response = await adsAgent.invoke_getAdMetrics();
187
+ if (!this.#isPolling || this.#pollSessionId !== sessionId) {
188
+ return;
189
+ }
190
+ if (!response.getError()) {
191
+ this.#currentMetrics = response.metrics;
192
+ this.requestUpdate();
193
+ }
194
+ }
195
+ }
196
+ if (this.#isPolling && this.#pollSessionId === sessionId) {
197
+ this.#pollTimer = window.setTimeout(() => this.#pollMetrics(sessionId), 500);
198
+ }
199
+ }
200
+
201
+ #onPrimaryPageChanged(): void {
202
+ this.#currentMetrics = {
203
+ viewportAdDensityByArea: 0,
204
+ averageViewportAdDensityByArea: 0,
205
+ viewportAdCount: 0,
206
+ averageViewportAdCount: 0,
207
+ totalAdCpuTime: 0,
208
+ totalAdNetworkBytes: 0,
209
+ };
210
+ this.requestUpdate();
211
+ }
212
+
213
+ override performUpdate(): void {
214
+ const viewInput: ViewInput = {
215
+ metrics: this.#currentMetrics,
216
+ };
217
+ this.#view(viewInput, undefined, this.contentElement);
218
+ }
219
+ }
@@ -0,0 +1,54 @@
1
+ /*
2
+ * Copyright 2026 The Chromium Authors
3
+ * Use of this source code is governed by a BSD-style license that can be
4
+ * found in the LICENSE file.
5
+ */
6
+
7
+ :host {
8
+ padding: 12px;
9
+ display: flex;
10
+ flex-direction: column;
11
+ overflow: auto;
12
+ }
13
+
14
+ .metrics-container {
15
+ flex: 0 0 auto;
16
+ margin: 0 0 24px;
17
+ border: 1px solid var(--sys-color-divider);
18
+ display: grid;
19
+ grid-template-columns: repeat(2, 1fr);
20
+ gap: 1px;
21
+ background-color: var(--sys-color-divider);
22
+ }
23
+
24
+ .metric-box {
25
+ background-color: var(--sys-color-surface);
26
+ padding: 12px;
27
+ display: flex;
28
+ flex-direction: column;
29
+ align-items: center;
30
+ justify-content: center;
31
+ }
32
+
33
+ .metric-title {
34
+ font-size: 12px;
35
+ color: var(--sys-color-on-surface-subtle);
36
+ margin: 0 0 4px;
37
+ }
38
+
39
+ .metric-value {
40
+ font-size: 18px;
41
+ font-weight: bold;
42
+ color: var(--sys-color-on-surface);
43
+ display: flex;
44
+ flex-direction: column;
45
+ align-items: center;
46
+ margin: 0;
47
+ gap: 2px;
48
+ }
49
+
50
+ .metric-average {
51
+ font-size: 12px;
52
+ font-weight: normal;
53
+ color: var(--sys-color-on-surface-subtle);
54
+ }
@@ -2,6 +2,7 @@
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
4
 
5
+ import * as AdsView from './AdsView.js';
5
6
  import * as BackForwardCacheView from './BackForwardCacheView.js';
6
7
  import * as BounceTrackingMitigationsView from './BounceTrackingMitigationsView.js';
7
8
  import * as CrashReportContextGrid from './CrashReportContextGrid.js';
@@ -17,6 +18,7 @@ import * as StorageMetadataView from './StorageMetadataView.js';
17
18
  import * as TrustTokensView from './TrustTokensView.js';
18
19
 
19
20
  export {
21
+ AdsView,
20
22
  BackForwardCacheView,
21
23
  BounceTrackingMitigationsView,
22
24
  CrashReportContextGrid,
@@ -27,7 +27,13 @@ import * as ThemeSupport from '../../ui/legacy/theme_support/theme_support.js';
27
27
  import {ExtensionButton, ExtensionPanel, ExtensionSidebarPane} from './ExtensionPanel.js';
28
28
 
29
29
  const extensionOrigins = new WeakMap<MessagePort, Platform.DevToolsPath.UrlString>();
30
- const kPermittedSchemes = ['http:', 'https:', 'file:', 'data:', 'chrome-extension:', 'about:'];
30
+ const kForbiddenSchemes = [
31
+ 'chrome:',
32
+ 'chrome-untrusted:',
33
+ 'chrome-error:',
34
+ 'chrome-search:',
35
+ 'devtools:',
36
+ ];
31
37
 
32
38
  declare global {
33
39
  interface Window {
@@ -78,7 +84,6 @@ export class HostsPolicy {
78
84
  }
79
85
 
80
86
  class RegisteredExtension {
81
- openResourceScheme: null|string = null;
82
87
  constructor(readonly name: string, readonly hostsPolicy: HostsPolicy, readonly allowFileAccess: boolean) {
83
88
  }
84
89
 
@@ -91,8 +96,11 @@ class RegisteredExtension {
91
96
  return false;
92
97
  }
93
98
 
94
- if (this.openResourceScheme && inspectedURL.startsWith(this.openResourceScheme)) {
95
- return true;
99
+ let parsedURL;
100
+ try {
101
+ parsedURL = new URL(inspectedURL);
102
+ } catch {
103
+ return false;
96
104
  }
97
105
 
98
106
  if (!ExtensionServer.canInspectURL(inspectedURL)) {
@@ -104,12 +112,6 @@ class RegisteredExtension {
104
112
  }
105
113
 
106
114
  if (!this.allowFileAccess) {
107
- let parsedURL;
108
- try {
109
- parsedURL = new URL(inspectedURL);
110
- } catch {
111
- return false;
112
- }
113
115
  return parsedURL.protocol !== 'file:';
114
116
  }
115
117
 
@@ -863,18 +865,27 @@ export class ExtensionServer extends Common.ObjectWrapper.ObjectWrapper<EventTyp
863
865
  if (!extension) {
864
866
  throw new Error('Received a message from an unregistered extension');
865
867
  }
866
- if (message.urlScheme) {
867
- extension.openResourceScheme = message.urlScheme;
868
+ let validScheme = message.urlScheme;
869
+ if (validScheme) {
870
+ try {
871
+ const urlToParse = validScheme.replace(/:?(\/\/)?$/, '') + '://test';
872
+ validScheme = new URL(urlToParse).protocol;
873
+ } catch {
874
+ return this.status.E_BADARG('urlScheme', 'Invalid scheme');
875
+ }
876
+ if (kForbiddenSchemes.includes(validScheme) || validScheme === 'file:') {
877
+ return this.status.E_BADARG('urlScheme', 'Scheme is forbidden');
878
+ }
868
879
  }
869
880
  const extensionOrigin = this.getExtensionOrigin(port);
870
881
  const {name} = extension;
871
882
  const registration = {
872
883
  title: name,
873
884
  origin: extensionOrigin,
874
- scheme: message.urlScheme,
885
+ scheme: validScheme,
875
886
  handler: this.handleOpenURL.bind(this, port),
876
887
  shouldHandleOpenResource: (url: Platform.DevToolsPath.UrlString, schemes: Set<string>) =>
877
- Components.Linkifier.Linkifier.shouldHandleOpenResource(extension.openResourceScheme, url, schemes),
888
+ Components.Linkifier.Linkifier.shouldHandleOpenResource(validScheme || null, url, schemes),
878
889
  };
879
890
  if (message.handlerPresent) {
880
891
  Components.Linkifier.Linkifier.registerLinkHandler(registration);
@@ -1645,7 +1656,7 @@ export class ExtensionServer extends Common.ObjectWrapper.ObjectWrapper<EventTyp
1645
1656
  return false;
1646
1657
  }
1647
1658
 
1648
- if (!kPermittedSchemes.includes(parsedURL.protocol)) {
1659
+ if (kForbiddenSchemes.includes(parsedURL.protocol)) {
1649
1660
  return false;
1650
1661
  }
1651
1662
 
@@ -2,6 +2,7 @@
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
4
 
5
+ import type * as Platform from '../../core/platform/platform.js';
5
6
  import * as Bindings from '../../models/bindings/bindings.js';
6
7
  import type * as StackTrace from '../../models/stack_trace/stack_trace.js';
7
8
  import type * as Workspace from '../../models/workspace/workspace.js';
@@ -9,6 +10,8 @@ import * as Components from '../../ui/legacy/components/utils/utils.js';
9
10
  import * as UI from '../../ui/legacy/legacy.js';
10
11
  import * as Lit from '../../ui/lit/lit.js';
11
12
 
13
+ import {ConsoleViewMessage} from './ConsoleViewMessage.js';
14
+
12
15
  const {html, render} = Lit;
13
16
 
14
17
  export interface ViewInput {
@@ -24,20 +27,64 @@ function renderHeader(content: Lit.LitTemplate|Node|UI.Widget.Widget, isCause: b
24
27
  return html`<span class="error-message-text">${content}</span>`;
25
28
  }
26
29
 
27
- function renderFramePrefix(
28
- frame: StackTrace.StackTrace.ParsedErrorStackFrame,
29
- _options: Components.Linkifier.LinkifyOptions): Lit.LitTemplate {
30
+ function formatName(frame: StackTrace.StackTrace.ParsedErrorStackFrame): string {
31
+ let name = frame.name || '';
32
+ const isInline = Boolean(frame.rawName) && frame.name !== frame.rawName;
33
+ const shouldAppendMethodAlias = !isInline && frame.methodName && name && name !== frame.methodName &&
34
+ !name.endsWith('.' + frame.methodName) && !name.endsWith(' ' + frame.methodName);
35
+ if (shouldAppendMethodAlias) {
36
+ name += ` [as ${frame.methodName}]`;
37
+ }
38
+ return name;
39
+ }
40
+
41
+ function renderLinkElement(frame: StackTrace.StackTrace.ParsedErrorStackFrame,
42
+ options: Components.Linkifier.LinkifyOptions): HTMLElement|Lit.LitTemplate {
43
+ if (frame.url || frame.uiSourceCode) {
44
+ const link = Components.Linkifier.Linkifier.linkifyStackTraceFrame(frame, options);
45
+ link.tabIndex = -1;
46
+ return link;
47
+ }
48
+ return html`<span>&lt;anonymous&gt;</span>`;
49
+ }
50
+
51
+ function renderEvalOrigin(frame: StackTrace.StackTrace.ParsedErrorStackFrame,
52
+ options: Components.Linkifier.LinkifyOptions): Lit.LitTemplate {
53
+ const name = formatName(frame);
54
+ const linkElement = renderLinkElement(frame, options);
55
+
56
+ const asyncPrefix = frame.isAsync ? 'async ' : '';
57
+ const constructorPrefix = frame.isConstructor ? 'new ' : '';
58
+
59
+ if (frame.isEval) {
60
+ const evalOrigin = frame.evalOrigin ? renderEvalOrigin(frame.evalOrigin, options) : '<anonymous>';
61
+ if (name) {
62
+ return html`${asyncPrefix}${constructorPrefix}eval at ${name} (${evalOrigin})`;
63
+ }
64
+ return html`${asyncPrefix}${constructorPrefix}eval at ${evalOrigin}`;
65
+ }
66
+ if (name) {
67
+ return html`${asyncPrefix}${constructorPrefix}eval at ${name} (${linkElement})`;
68
+ }
69
+ return html`${asyncPrefix}${constructorPrefix}eval at ${linkElement}`;
70
+ }
71
+
72
+ function renderFramePrefix(frame: StackTrace.StackTrace.ParsedErrorStackFrame,
73
+ options: Components.Linkifier.LinkifyOptions): Lit.LitTemplate {
30
74
  const asyncPrefix = frame.isAsync ? 'async ' : '';
31
75
  if (frame.promiseIndex !== undefined) {
32
76
  const name = frame.name || 'Promise.all';
33
77
  return html`${asyncPrefix}${name} (index ${frame.promiseIndex})`;
34
78
  }
35
79
  const constructorPrefix = frame.isConstructor ? 'new ' : '';
36
- let name = frame.name || '';
37
- const isInline = Boolean(frame.rawName) && frame.name !== frame.rawName;
38
- if (!isInline && frame.methodName && name && name !== frame.methodName && !name.endsWith('.' + frame.methodName) &&
39
- !name.endsWith(' ' + frame.methodName)) {
40
- name += ` [as ${frame.methodName}]`;
80
+ const name = formatName(frame);
81
+
82
+ if (frame.isEval) {
83
+ const evalOrigin = frame.evalOrigin ? renderEvalOrigin(frame.evalOrigin, options) : '<anonymous>';
84
+ if (name) {
85
+ return html`${asyncPrefix}${constructorPrefix}${name} (${evalOrigin}, `;
86
+ }
87
+ return html`${asyncPrefix}${constructorPrefix}${evalOrigin}, `;
41
88
  }
42
89
 
43
90
  if (name) {
@@ -58,10 +105,25 @@ function renderFrameSuffix(frame: StackTrace.StackTrace.ParsedErrorStackFrame):
58
105
 
59
106
  const DEFAULT_VIEW = (input: ViewInput, _output: object, target: HTMLElement): void => {
60
107
  const renderError = (error: Bindings.SymbolizedError.SymbolizedError, isCause: boolean): Lit.LitTemplate => {
61
- if (!(error instanceof Bindings.SymbolizedError.SymbolizedErrorObject)) {
108
+ if (error instanceof Bindings.SymbolizedError.SymbolizedSyntaxError) {
62
109
  console.error('SymbolizedErrorWidget received an unsupported error type:', error);
63
110
  return Lit.nothing;
64
111
  }
112
+ if (error instanceof Bindings.SymbolizedError.UnparsableError) {
113
+ const fragment = ConsoleViewMessage.linkifyWithCustomLinkifier(
114
+ error.errorStack,
115
+ (text: string, url: Platform.DevToolsPath.UrlString, lineNumber?: number, columnNumber?: number) => {
116
+ const options = {text, lineNumber, columnNumber, ignoreListManager: input.ignoreListManager};
117
+ const linkElement = Components.Linkifier.Linkifier.linkifyURL(url, options);
118
+ linkElement.tabIndex = -1;
119
+ return linkElement;
120
+ });
121
+ const header = renderHeader(fragment, isCause);
122
+ return html`
123
+ <span class=${isCause ? 'console-message-stack-trace-wrapper' : ''}>${header}</span>
124
+ `;
125
+ }
126
+
65
127
  const linkOptions: Components.Linkifier.LinkifyOptions = {
66
128
  showColumnNumber: true,
67
129
  inlineFrameIndex: 0,
@@ -75,19 +137,8 @@ const DEFAULT_VIEW = (input: ViewInput, _output: object, target: HTMLElement): v
75
137
  return html`
76
138
  <span class=${isCause ? 'console-message-stack-trace-wrapper' : ''}
77
139
  >${header}${syncFrames.length > 0 ? '\n' : ''}${syncFrames.map((frame: StackTrace.StackTrace.ParsedErrorStackFrame, i: number) => {
78
- let linkElement: HTMLElement|Lit.LitTemplate = Lit.nothing;
79
- let isBuiltin = false;
80
- if (frame.promiseIndex !== undefined) {
81
- // Promise.all doesn't have a linkable location.
82
- isBuiltin = true;
83
- } else if (frame.url || frame.uiSourceCode) {
84
- const link = Components.Linkifier.Linkifier.linkifyStackTraceFrame(frame, linkOptions);
85
- link.tabIndex = -1;
86
- linkElement = link;
87
- } else {
88
- linkElement = html`<span>&lt;anonymous&gt;</span>`;
89
- isBuiltin = true;
90
- }
140
+ const isBuiltin = frame.promiseIndex !== undefined || (!frame.url && !frame.uiSourceCode);
141
+ const linkElement = frame.promiseIndex !== undefined ? Lit.nothing : renderLinkElement(frame, linkOptions);
91
142
 
92
143
  const newline = i < error.stackTrace.syncFragment.frames.length - 1 ? '\n' : '';
93
144
  const frameClass = isBuiltin ? 'formatted-builtin-stack-frame' : 'formatted-stack-frame';
@@ -276,7 +276,7 @@ export class StandaloneStylesContainer extends Common.ObjectWrapper.eventMixin<E
276
276
  return null;
277
277
  }
278
278
 
279
- jumpToFunctionDefinition(_functionName: string): void {
279
+ jumpToFunctionDefinition(_functionName: string, _treeScopeDistance: number): void {
280
280
  }
281
281
 
282
282
  continueEditingElement(_sectionIndex: number, _propertyIndex: number): void {
@@ -379,6 +379,14 @@ export class StylePropertiesSection {
379
379
  return this.sectionIdx;
380
380
  }
381
381
 
382
+ treeScopeDistance(): number {
383
+ const treeScope = this.styleInternal.parentRule?.treeScope;
384
+ if (!treeScope) {
385
+ return -1;
386
+ }
387
+ return SDK.CSSMatchedStyles.distanceToTreeScope(this.matchedStyles.node(), treeScope);
388
+ }
389
+
382
390
  static createRuleOriginNode(
383
391
  matchedStyles: SDK.CSSMatchedStyles.CSSMatchedStyles, linkifier: Components.Linkifier.Linkifier,
384
392
  rule: SDK.CSSRule.CSSRule|null): LitTemplate {
@@ -51,9 +51,11 @@ export class StylePropertyHighlighter {
51
51
  PanelUtils.highlightElement(block.titleElement() as HTMLElement);
52
52
  }
53
53
 
54
- findAndHighlightSection(sectionName: string, blockName: string): void {
54
+ findAndHighlightSection(sectionName: string, blockName: string, treeScopeDistance = -1): void {
55
55
  const block = this.styleSidebarPane.getSectionBlockByName(blockName);
56
- const section = block?.sections.find(section => section.headerText() === sectionName);
56
+ const section = block?.sections.find(
57
+ section => section.headerText() === sectionName &&
58
+ (treeScopeDistance === -1 || section.treeScopeDistance() === treeScopeDistance));
57
59
  if (!section || !block) {
58
60
  return;
59
61
  }