chrome-devtools-frontend 1.0.1621678 → 1.0.1624409

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 (47) hide show
  1. package/.agents/skills/foundation-test-migration/SKILL.md +171 -0
  2. package/.agents/skills/verification/SKILL.md +2 -11
  3. package/front_end/Images/src/expand.svg +1 -0
  4. package/front_end/core/common/Base64.ts +12 -2
  5. package/front_end/core/i18n/i18nImpl.ts +8 -4
  6. package/front_end/core/root/Runtime.ts +28 -9
  7. package/front_end/entrypoints/device_mode_emulation_frame/device_mode_emulation_frame.ts +1 -1
  8. package/front_end/entrypoints/greendev_floaty/FloatyEntrypoint.ts +72 -8
  9. package/front_end/entrypoints/greendev_floaty/floaty.html +1 -1
  10. package/front_end/entrypoints/shell/shell.ts +1 -1
  11. package/front_end/generated/Deprecation.ts +7 -0
  12. package/front_end/generated/InspectorBackendCommands.ts +1 -1
  13. package/front_end/generated/SupportedCSSProperties.js +6 -6
  14. package/front_end/generated/protocol.ts +1 -0
  15. package/front_end/models/ai_assistance/agents/GreenDevAgent.ts +373 -112
  16. package/front_end/models/ai_assistance/agents/NetworkAgent.snapshot.txt +57 -0
  17. package/front_end/models/ai_assistance/agents/NetworkAgent.ts +2 -1
  18. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +3 -9
  19. package/front_end/models/javascript_metadata/NativeFunctions.js +9 -4
  20. package/front_end/panels/ai_assistance/components/ChatMessage.ts +295 -45
  21. package/front_end/panels/console/ConsoleView.ts +86 -7
  22. package/front_end/panels/console/ConsoleViewMessage.ts +23 -1
  23. package/front_end/panels/console/SymbolizedErrorWidget.ts +69 -0
  24. package/front_end/panels/console/console.ts +3 -0
  25. package/front_end/panels/elements/StylePropertiesSection.ts +1 -2
  26. package/front_end/panels/elements/StylePropertyTreeElement.ts +1 -1
  27. package/front_end/panels/elements/StylesAiCodeCompletionProvider.ts +6 -2
  28. package/front_end/panels/elements/StylesSidebarPane.ts +17 -4
  29. package/front_end/panels/emulation/DeviceModeToolbar.ts +189 -132
  30. package/front_end/panels/emulation/deviceModeView.css +25 -0
  31. package/front_end/panels/greendev/GreenDevPanel.ts +30 -3
  32. package/front_end/panels/media/EventDisplayTable.ts +1 -1
  33. package/front_end/panels/mobile_throttling/NetworkThrottlingSelector.ts +48 -27
  34. package/front_end/panels/mobile_throttling/ThrottlingManager.ts +67 -38
  35. package/front_end/panels/network/NetworkConfigView.ts +1 -1
  36. package/front_end/panels/profiler/HeapSnapshotView.ts +1 -22
  37. package/front_end/panels/sources/WatchExpressionsSidebarPane.ts +12 -4
  38. package/front_end/panels/timeline/components/liveMetricsView.css +2 -2
  39. package/front_end/third_party/chromium/README.chromium +1 -1
  40. package/front_end/{core → ui}/dom_extension/DOMExtension.ts +1 -1
  41. package/front_end/ui/legacy/UIUtils.ts +26 -4
  42. package/front_end/ui/legacy/Widget.ts +1 -1
  43. package/front_end/ui/visual_logging/KnownContextValues.ts +19 -0
  44. package/package.json +1 -1
  45. package/front_end/panels/emulation/components/DeviceSizeInputElement.ts +0 -134
  46. package/front_end/panels/emulation/components/components.ts +0 -9
  47. /package/front_end/{core → ui}/dom_extension/dom_extension.ts +0 -0
@@ -171,7 +171,7 @@ Note: if the user asks a specific question about the trace (such as "What is my
171
171
  - \`nav-to-lcp\` (navigation to LCP)
172
172
  - \`lcp-ttfb\` (LCP TTFB phase)
173
173
  - \`lcp-render-delay\` (LCP render delay phase)
174
- - Insight names: \`LCPBreakdown\`, \`CLSCulprits\`, \`RenderBlocking\`, \`NetworkDependencyTree\`, \`ImageDelivery\`, \`FontDisplay\`, \`ThirdParties\`, \`ForcedReflow\`, \`Cache\`, \`DOMSize\`
174
+ - Insight names: \`LCPBreakdown\`, \`INPBreakdown\`, \`CLSCulprits\`, \`ThirdParties\`, \`DocumentLatency\`, \`DOMSize\`, \`DuplicatedJavaScript\`, \`FontDisplay\`, \`ForcedReflow\`, \`ImageDelivery\`, \`LCPDiscovery\`, \`LegacyJavaScript\`, \`NetworkDependencyTree\`, \`RenderBlocking\`, \`SlowCSSSelector\`, \`Viewport\`, \`ModernHTTP\`, \`Cache\`, \`CharacterSet\`
175
175
  - Navigation IDs: \`NAVIGATION_0\`, \`NAVIGATION_1\`, etc.
176
176
  - Use \`getEventByKey\` to get data on a specific trace event. This is great for root-cause analysis or validating any assumptions.
177
177
  - Provide clear, actionable recommendations. Avoid technical jargon unless necessary, and explain any technical terms used.
@@ -222,12 +222,6 @@ enum ScorePriority {
222
222
  DEFAULT = 1,
223
223
  }
224
224
 
225
- // TODO(crbug.com/503296282): Remove this when we add support for all insights
226
- const SUPPORTED_INSIGHT_WIDGETS = new Set<Trace.Insights.Types.InsightKeys>([
227
- Trace.Insights.Types.InsightKeys.LCP_BREAKDOWN,
228
- Trace.Insights.Types.InsightKeys.RENDER_BLOCKING,
229
- ]);
230
-
231
225
  export class PerformanceTraceContext extends ConversationContext<AgentFocus> {
232
226
  static fromParsedTrace(parsedTrace: Trace.TraceModel.ParsedTrace): PerformanceTraceContext {
233
227
  return new PerformanceTraceContext(AgentFocus.fromParsedTrace(parsedTrace));
@@ -545,7 +539,7 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
545
539
  // Case 2: Insight -> PERF_INSIGHT widget
546
540
  if (focus.insight) {
547
541
  const insightKey = focus.insight.insightKey;
548
- if (Trace.Insights.Common.isInsightKey(insightKey) && SUPPORTED_INSIGHT_WIDGETS.has(insightKey)) {
542
+ if (Trace.Insights.Common.isInsightKey(insightKey)) {
549
543
  widgets.push({
550
544
  name: 'PERF_INSIGHT',
551
545
  data: {
@@ -1018,7 +1012,7 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
1018
1012
  }
1019
1013
 
1020
1014
  const insightKey = params.insightName;
1021
- if (Trace.Insights.Common.isInsightKey(insightKey) && SUPPORTED_INSIGHT_WIDGETS.has(insightKey)) {
1015
+ if (Trace.Insights.Common.isInsightKey(insightKey)) {
1022
1016
  widgets.push({
1023
1017
  name: 'PERF_INSIGHT',
1024
1018
  data: {
@@ -2324,6 +2324,11 @@ export const NativeFunctions = [
2324
2324
  signatures: [["type"]],
2325
2325
  receivers: ["HTMLScriptElement","ClipboardItem"]
2326
2326
  },
2327
+ {
2328
+ name: "supports",
2329
+ signatures: [["operation","algorithm","?length"],["operation","algorithm","additionalAlgorithm"]],
2330
+ receivers: ["SubtleCrypto"]
2331
+ },
2327
2332
  {
2328
2333
  name: "toggle",
2329
2334
  signatures: [["token","?force"]]
@@ -7435,10 +7440,6 @@ export const NativeFunctions = [
7435
7440
  name: "setReportEventDataForAutomaticBeacons",
7436
7441
  signatures: [["event"]]
7437
7442
  },
7438
- {
7439
- name: "notifyEvent",
7440
- signatures: [["triggering_event"]]
7441
- },
7442
7443
  {
7443
7444
  name: "FencedFrameConfig",
7444
7445
  signatures: [["url"]]
@@ -9391,6 +9392,10 @@ export const NativeFunctions = [
9391
9392
  name: "startDiagnosticLogging",
9392
9393
  signatures: [["?options"]]
9393
9394
  },
9395
+ {
9396
+ name: "finishDiagnosticLogging",
9397
+ signatures: [["?options"]]
9398
+ },
9394
9399
  {
9395
9400
  name: "CloseEvent",
9396
9401
  signatures: [["type","?eventInitDict"]]
@@ -20,6 +20,7 @@ import * as AiAssistanceModel from '../../../models/ai_assistance/ai_assistance.
20
20
  import * as ComputedStyle from '../../../models/computed_style/computed_style.js';
21
21
  import * as Trace from '../../../models/trace/trace.js';
22
22
  import * as PanelsCommon from '../../../panels/common/common.js';
23
+ import * as TraceBounds from '../../../services/trace_bounds/trace_bounds.js';
23
24
  import * as Marked from '../../../third_party/marked/marked.js';
24
25
  import * as Buttons from '../../../ui/components/buttons/buttons.js';
25
26
  import * as Input from '../../../ui/components/input/input.js';
@@ -31,6 +32,7 @@ import * as Lit from '../../../ui/lit/lit.js';
31
32
  import * as VisualLogging from '../../../ui/visual_logging/visual_logging.js';
32
33
  import * as Elements from '../../elements/elements.js';
33
34
  import * as TimelineComponents from '../../timeline/components/components.js';
35
+ import type {BaseInsightComponent} from '../../timeline/components/insights/BaseInsightComponent.js';
34
36
  import * as TimelineInsights from '../../timeline/components/insights/insights.js';
35
37
  import * as Timeline from '../../timeline/timeline.js';
36
38
  import * as TimelineUtils from '../../timeline/utils/utils.js';
@@ -44,7 +46,7 @@ const {html, Directives: {ref, ifDefined}} = Lit;
44
46
  const lockedString = i18n.i18n.lockedString;
45
47
  const {widget} = UI.Widget;
46
48
 
47
- const REPORT_URL = 'https://crbug.com/364805393' as Platform.DevToolsPath.UrlString;
49
+ const REPORT_URL = 'https://crbug.com/508304827' as Platform.DevToolsPath.UrlString;
48
50
  const SCROLL_ROUNDING_OFFSET = 1;
49
51
  const MAX_NUM_LINES_IN_CODEBLOCK = 11;
50
52
 
@@ -201,6 +203,14 @@ const UIStringsNotTranslate = {
201
203
  * @description Accessible label for the reveal button in the LCP breakdown widget.
202
204
  */
203
205
  revealLcpBreakdown: 'Reveal LCP breakdown',
206
+ /**
207
+ * @description Accessible label for the reveal button in the LCP discovery widget.
208
+ */
209
+ revealLcpDiscovery: 'Reveal LCP discovery',
210
+ /**
211
+ * @description Accessible label for the reveal button in the layout shift culprits widget.
212
+ */
213
+ revealClsCulprits: 'Reveal layout shift culprits',
204
214
  /**
205
215
  * @description Accessible label for the reveal button in the render-blocking requests widget.
206
216
  */
@@ -217,6 +227,14 @@ const UIStringsNotTranslate = {
217
227
  * @description Accessible label for the reveal button in the bottom up thread activity widget.
218
228
  */
219
229
  revealBottomUpTree: 'Reveal bottom-up thread activity',
230
+ /**
231
+ * @description Accessible label for the reveal button in the network dependency tree widget.
232
+ */
233
+ revealNetworkDependencyTree: 'Reveal network dependency tree',
234
+ /**
235
+ * @description Accessible label for the reveal button in the 3rd parties widget.
236
+ */
237
+ revealThirdParties: 'Reveal 3rd parties',
220
238
  /**
221
239
  * @description Title for the core web vitals widget.
222
240
  */
@@ -225,10 +243,26 @@ const UIStringsNotTranslate = {
225
243
  * @description Title for the LCP breakdown widget.
226
244
  */
227
245
  lcpBreakdown: 'LCP breakdown',
246
+ /**
247
+ * @description Title for the LCP discovery widget.
248
+ */
249
+ lcpDiscovery: 'LCP discovery',
250
+ /**
251
+ * @description Title for the layout shift culprits widget.
252
+ */
253
+ clsCulprits: 'Layout shift culprits',
228
254
  /**
229
255
  * @description Title for the render-blocking requests widget.
230
256
  */
231
257
  renderBlockingBreakdown: 'Render-blocking requests',
258
+ /**
259
+ * @description Title for the network dependency tree widget.
260
+ */
261
+ networkDependencyTree: 'Network dependency tree',
262
+ /**
263
+ * @description Title for the 3rd parties widget.
264
+ */
265
+ thirdParties: '3rd parties',
232
266
  /**
233
267
  * @description Title for the LCP element widget.
234
268
  */
@@ -245,6 +279,110 @@ const UIStringsNotTranslate = {
245
279
  * @description Title for the bottom up thread activity widget.
246
280
  */
247
281
  bottomUpTree: 'Bottom-up thread activity',
282
+ /**
283
+ * @description Accessible label for the reveal button in the forced reflow widget.
284
+ */
285
+ revealForcedReflow: 'Reveal forced reflow',
286
+ /**
287
+ * @description Title for the forced reflow widget.
288
+ */
289
+ forcedReflow: 'Forced reflow',
290
+ /**
291
+ * @description Accessible label for the reveal button in the cache widget.
292
+ */
293
+ revealCache: 'Reveal efficient cache lifetimes',
294
+ /**
295
+ * @description Title for the cache widget.
296
+ */
297
+ cache: 'Efficient cache lifetimes',
298
+ /**
299
+ * @description Accessible label for the reveal button in the INP breakdown widget.
300
+ */
301
+ revealInpBreakdown: 'Reveal INP breakdown',
302
+ /**
303
+ * @description Title for the INP breakdown widget.
304
+ */
305
+ inpBreakdown: 'INP breakdown',
306
+ /**
307
+ * @description Accessible label for the reveal button in the document latency widget.
308
+ */
309
+ revealDocumentLatency: 'Reveal document latency',
310
+ /**
311
+ * @description Title for the document latency widget.
312
+ */
313
+ documentLatency: 'Document latency',
314
+ /**
315
+ * @description Accessible label for the reveal button in the DOM size widget.
316
+ */
317
+ revealDomSize: 'Reveal DOM size',
318
+ /**
319
+ * @description Title for the DOM size widget.
320
+ */
321
+ domSize: 'DOM size',
322
+ /**
323
+ * @description Accessible label for the reveal button in the duplicated JavaScript widget.
324
+ */
325
+ revealDuplicateJavaScript: 'Reveal duplicated JavaScript',
326
+ /**
327
+ * @description Title for the duplicated JavaScript widget.
328
+ */
329
+ duplicateJavaScript: 'Duplicated JavaScript',
330
+ /**
331
+ * @description Accessible label for the reveal button in the image delivery widget.
332
+ */
333
+ revealImageDelivery: 'Reveal image delivery',
334
+ /**
335
+ * @description Title for the image delivery widget.
336
+ */
337
+ imageDelivery: 'Image delivery',
338
+ /**
339
+ * @description Accessible label for the reveal button in the font display widget.
340
+ */
341
+ revealFontDisplay: 'Reveal font display',
342
+ /**
343
+ * @description Title for the font display widget.
344
+ */
345
+ fontDisplay: 'Font display',
346
+ /**
347
+ * @description Accessible label for the reveal button in the slow CSS selectors widget.
348
+ */
349
+ revealSlowCssSelector: 'Reveal slow CSS selectors',
350
+ /**
351
+ * @description Title for the slow CSS selectors widget.
352
+ */
353
+ slowCssSelector: 'Slow CSS selectors',
354
+ /**
355
+ * @description Accessible label for the reveal button in the legacy JavaScript widget.
356
+ */
357
+ revealLegacyJavaScript: 'Reveal legacy JavaScript',
358
+ /**
359
+ * @description Title for the legacy JavaScript widget.
360
+ */
361
+ legacyJavaScript: 'Legacy JavaScript',
362
+ /**
363
+ * @description Accessible label for the reveal button in the viewport optimization widget.
364
+ */
365
+ revealViewport: 'Reveal viewport optimization',
366
+ /**
367
+ * @description Title for the viewport optimization widget.
368
+ */
369
+ viewport: 'Viewport optimization',
370
+ /**
371
+ * @description Accessible label for the reveal button in the modern HTTP usage widget.
372
+ */
373
+ revealModernHttp: 'Reveal modern HTTP usage',
374
+ /**
375
+ * @description Title for the modern HTTP usage widget.
376
+ */
377
+ modernHttp: 'Modern HTTP usage',
378
+ /**
379
+ * @description Accessible label for the reveal button in the character set declaration widget.
380
+ */
381
+ revealCharacterSet: 'Reveal character set declaration',
382
+ /**
383
+ * @description Title for the character set declaration widget.
384
+ */
385
+ characterSet: 'Character set declaration',
248
386
  } as const;
249
387
 
250
388
  export interface Step {
@@ -922,55 +1060,167 @@ async function makeStylePropertiesWidget(widgetData: StylePropertiesAiWidget): P
922
1060
  };
923
1061
  }
924
1062
 
1063
+ const INSIGHT_METADATA: Record<string, {
1064
+ component: new () => BaseInsightComponent<Trace.Insights.Types.InsightModel>,
1065
+ accessibleLabel: string,
1066
+ title: string,
1067
+ jslog: string,
1068
+ }> = {
1069
+ [Trace.Insights.Types.InsightKeys.LCP_BREAKDOWN]: {
1070
+ component: TimelineInsights.LCPBreakdown.LCPBreakdown,
1071
+ accessibleLabel: UIStringsNotTranslate.revealLcpBreakdown,
1072
+ title: UIStringsNotTranslate.lcpBreakdown,
1073
+ jslog: 'lcp-breakdown-widget',
1074
+ },
1075
+ [Trace.Insights.Types.InsightKeys.RENDER_BLOCKING]: {
1076
+ component: TimelineInsights.RenderBlocking.RenderBlocking,
1077
+ accessibleLabel: UIStringsNotTranslate.revealRenderBlockingBreakdown,
1078
+ title: UIStringsNotTranslate.renderBlockingBreakdown,
1079
+ jslog: 'render-blocking-widget',
1080
+ },
1081
+ [Trace.Insights.Types.InsightKeys.LCP_DISCOVERY]: {
1082
+ component: TimelineInsights.LCPDiscovery.LCPDiscovery,
1083
+ accessibleLabel: UIStringsNotTranslate.revealLcpDiscovery,
1084
+ title: UIStringsNotTranslate.lcpDiscovery,
1085
+ jslog: 'lcp-discovery-widget',
1086
+ },
1087
+ [Trace.Insights.Types.InsightKeys.CLS_CULPRITS]: {
1088
+ component: TimelineInsights.CLSCulprits.CLSCulprits,
1089
+ accessibleLabel: UIStringsNotTranslate.revealClsCulprits,
1090
+ title: UIStringsNotTranslate.clsCulprits,
1091
+ jslog: 'cls-culprits-widget',
1092
+ },
1093
+ [Trace.Insights.Types.InsightKeys.NETWORK_DEPENDENCY_TREE]: {
1094
+ component: TimelineInsights.NetworkDependencyTree.NetworkDependencyTree,
1095
+ accessibleLabel: UIStringsNotTranslate.revealNetworkDependencyTree,
1096
+ title: UIStringsNotTranslate.networkDependencyTree,
1097
+ jslog: 'network-dependency-tree-widget',
1098
+ },
1099
+ [Trace.Insights.Types.InsightKeys.THIRD_PARTIES]: {
1100
+ component: TimelineInsights.ThirdParties.ThirdParties,
1101
+ accessibleLabel: UIStringsNotTranslate.revealThirdParties,
1102
+ title: UIStringsNotTranslate.thirdParties,
1103
+ jslog: 'third-parties-widget',
1104
+ },
1105
+ [Trace.Insights.Types.InsightKeys.FORCED_REFLOW]: {
1106
+ component: TimelineInsights.ForcedReflow.ForcedReflow,
1107
+ accessibleLabel: UIStringsNotTranslate.revealForcedReflow,
1108
+ title: UIStringsNotTranslate.forcedReflow,
1109
+ jslog: 'forced-reflow-widget',
1110
+ },
1111
+ [Trace.Insights.Types.InsightKeys.CACHE]: {
1112
+ component: TimelineInsights.Cache.Cache,
1113
+ accessibleLabel: UIStringsNotTranslate.revealCache,
1114
+ title: UIStringsNotTranslate.cache,
1115
+ jslog: 'cache-widget',
1116
+ },
1117
+ [Trace.Insights.Types.InsightKeys.INP_BREAKDOWN]: {
1118
+ component: TimelineInsights.INPBreakdown.INPBreakdown,
1119
+ accessibleLabel: UIStringsNotTranslate.revealInpBreakdown,
1120
+ title: UIStringsNotTranslate.inpBreakdown,
1121
+ jslog: 'inp-breakdown-widget',
1122
+ },
1123
+ [Trace.Insights.Types.InsightKeys.DOCUMENT_LATENCY]: {
1124
+ component: TimelineInsights.DocumentLatency.DocumentLatency,
1125
+ accessibleLabel: UIStringsNotTranslate.revealDocumentLatency,
1126
+ title: UIStringsNotTranslate.documentLatency,
1127
+ jslog: 'document-latency-widget',
1128
+ },
1129
+ [Trace.Insights.Types.InsightKeys.DOM_SIZE]: {
1130
+ component: TimelineInsights.DOMSize.DOMSize,
1131
+ accessibleLabel: UIStringsNotTranslate.revealDomSize,
1132
+ title: UIStringsNotTranslate.domSize,
1133
+ jslog: 'dom-size-widget',
1134
+ },
1135
+ [Trace.Insights.Types.InsightKeys.DUPLICATE_JAVASCRIPT]: {
1136
+ component: TimelineInsights.DuplicatedJavaScript.DuplicatedJavaScript,
1137
+ accessibleLabel: UIStringsNotTranslate.revealDuplicateJavaScript,
1138
+ title: UIStringsNotTranslate.duplicateJavaScript,
1139
+ jslog: 'duplicate-javascript-widget',
1140
+ },
1141
+ [Trace.Insights.Types.InsightKeys.IMAGE_DELIVERY]: {
1142
+ component: TimelineInsights.ImageDelivery.ImageDelivery,
1143
+ accessibleLabel: UIStringsNotTranslate.revealImageDelivery,
1144
+ title: UIStringsNotTranslate.imageDelivery,
1145
+ jslog: 'image-delivery-widget',
1146
+ },
1147
+ [Trace.Insights.Types.InsightKeys.FONT_DISPLAY]: {
1148
+ component: TimelineInsights.FontDisplay.FontDisplay,
1149
+ accessibleLabel: UIStringsNotTranslate.revealFontDisplay,
1150
+ title: UIStringsNotTranslate.fontDisplay,
1151
+ jslog: 'font-display-widget',
1152
+ },
1153
+ [Trace.Insights.Types.InsightKeys.SLOW_CSS_SELECTOR]: {
1154
+ component: TimelineInsights.SlowCSSSelector.SlowCSSSelector,
1155
+ accessibleLabel: UIStringsNotTranslate.revealSlowCssSelector,
1156
+ title: UIStringsNotTranslate.slowCssSelector,
1157
+ jslog: 'slow-css-selector-widget',
1158
+ },
1159
+ [Trace.Insights.Types.InsightKeys.LEGACY_JAVASCRIPT]: {
1160
+ component: TimelineInsights.LegacyJavaScript.LegacyJavaScript,
1161
+ accessibleLabel: UIStringsNotTranslate.revealLegacyJavaScript,
1162
+ title: UIStringsNotTranslate.legacyJavaScript,
1163
+ jslog: 'legacy-javascript-widget',
1164
+ },
1165
+ [Trace.Insights.Types.InsightKeys.VIEWPORT]: {
1166
+ component: TimelineInsights.Viewport.Viewport,
1167
+ accessibleLabel: UIStringsNotTranslate.revealViewport,
1168
+ title: UIStringsNotTranslate.viewport,
1169
+ jslog: 'viewport-widget',
1170
+ },
1171
+ [Trace.Insights.Types.InsightKeys.MODERN_HTTP]: {
1172
+ component: TimelineInsights.ModernHTTP.ModernHTTP,
1173
+ accessibleLabel: UIStringsNotTranslate.revealModernHttp,
1174
+ title: UIStringsNotTranslate.modernHttp,
1175
+ jslog: 'modern-http-widget',
1176
+ },
1177
+ [Trace.Insights.Types.InsightKeys.CHARACTER_SET]: {
1178
+ component: TimelineInsights.CharacterSet.CharacterSet,
1179
+ accessibleLabel: UIStringsNotTranslate.revealCharacterSet,
1180
+ title: UIStringsNotTranslate.characterSet,
1181
+ jslog: 'character-set-widget',
1182
+ },
1183
+ };
1184
+
1185
+ function renderInsightWidget<T extends Trace.Insights.Types.InsightModel>(
1186
+ component: new () => BaseInsightComponent<T>, insight: T, jslog: string, accessibleLabel: string, title: string,
1187
+ bounds?: Trace.Types.Timing.TraceWindowMicro): WidgetMakerResponse {
1188
+ const renderedWidget = html`<devtools-widget
1189
+ class=${jslog}
1190
+ ${widget(component, {
1191
+ model: insight,
1192
+ minimal: true,
1193
+ bounds: bounds ?? null,
1194
+ })}></devtools-widget>`;
1195
+
1196
+ return {
1197
+ renderedWidget,
1198
+ revealable: new TimelineUtils.Helpers.RevealableInsight(insight),
1199
+ accessibleRevealLabel: lockedString(accessibleLabel),
1200
+ title: lockedString(title),
1201
+ jslogContext: jslog,
1202
+ };
1203
+ }
1204
+
925
1205
  async function makePerfInsightWidget(widgetData: PerfInsightAiWidget): Promise<WidgetMakerResponse|null> {
926
- switch (widgetData.data.insight) {
927
- case Trace.Insights.Types.InsightKeys.LCP_BREAKDOWN: {
928
- const insight = widgetData.data.insightData;
929
- if (!insight || !Trace.Insights.Models.LCPBreakdown.isLCPBreakdownInsight(insight)) {
930
- return null;
931
- }
932
- // clang-format off
933
- const renderedWidget = html`<devtools-widget
934
- class="lcp-breakdown-widget"
935
- ${widget(TimelineInsights.LCPBreakdown.LCPBreakdown, {
936
- model: insight,
937
- minimal: true,
938
- })}></devtools-widget>`;
939
- // clang-format on
1206
+ const insightKey = widgetData.data.insight;
1207
+ const insight = widgetData.data.insightData;
940
1208
 
941
- return {
942
- renderedWidget,
943
- revealable: new TimelineUtils.Helpers.RevealableInsight(insight),
944
- accessibleRevealLabel: lockedString(UIStringsNotTranslate.revealLcpBreakdown),
945
- title: lockedString(UIStringsNotTranslate.lcpBreakdown),
946
- jslogContext: 'lcp-breakdown',
947
- };
948
- }
949
- case Trace.Insights.Types.InsightKeys.RENDER_BLOCKING: {
950
- const insight = widgetData.data.insightData;
951
- if (!insight || !Trace.Insights.Models.RenderBlocking.isRenderBlockingInsight(insight)) {
952
- return null;
953
- }
954
- // clang-format off
955
- const renderedWidget = html`<devtools-widget
956
- class="render-blocking-widget"
957
- ${widget(TimelineInsights.RenderBlocking.RenderBlocking, {
958
- model: insight,
959
- minimal: true,
960
- })}></devtools-widget>`;
961
- // clang-format on
1209
+ const meta = INSIGHT_METADATA[insightKey];
1210
+ if (!meta) {
1211
+ return null;
1212
+ }
962
1213
 
963
- return {
964
- renderedWidget,
965
- revealable: new TimelineUtils.Helpers.RevealableInsight(insight),
966
- accessibleRevealLabel: lockedString(UIStringsNotTranslate.revealRenderBlockingBreakdown),
967
- title: lockedString(UIStringsNotTranslate.renderBlockingBreakdown),
968
- jslogContext: 'render-blocking-widget',
969
- };
970
- }
971
- default:
1214
+ let bounds;
1215
+ if (insightKey === Trace.Insights.Types.InsightKeys.CLS_CULPRITS) {
1216
+ const traceBounds = TraceBounds.TraceBounds.BoundsManager.instance().state()?.micro.entireTraceBounds;
1217
+ if (!traceBounds) {
972
1218
  return null;
1219
+ }
1220
+ bounds = traceBounds;
973
1221
  }
1222
+
1223
+ return renderInsightWidget(meta.component, insight, meta.jslog, meta.accessibleLabel, meta.title, bounds);
974
1224
  }
975
1225
 
976
1226
  async function makeBottomUpTimelineTreeWidget(widgetData: BottomUpTreeAiWidget): Promise<WidgetMakerResponse|null> {
@@ -265,6 +265,16 @@ const UIStrings = {
265
265
  * @example {5} PH1
266
266
  */
267
267
  filteredMessagesInConsole: '{PH1} messages in console',
268
+ /**
269
+ * @description Tooltip for the collapse all button in the Console panel toolbar.
270
+ * Clicking this button will collapse all groups and stack traces.
271
+ */
272
+ collapseAll: 'Collapse all',
273
+ /**
274
+ * @description Tooltip for the expand all button in the Console panel toolbar.
275
+ * Clicking this button will expand all groups and stack traces.
276
+ */
277
+ expandAll: 'Expand all',
268
278
 
269
279
  } as const;
270
280
  const str_ = i18n.i18n.registerUIStrings('panels/console/ConsoleView.ts', UIStrings);
@@ -337,6 +347,8 @@ export class ConsoleView extends UI.Widget.VBox implements
337
347
  private issueResolver = new IssuesManager.IssueResolver.IssueResolver();
338
348
  #isDetached = false;
339
349
  #onIssuesCountUpdateBound = this.#onIssuesCountUpdate.bind(this);
350
+ #collapseAllButton: UI.Toolbar.ToolbarButton;
351
+ #allCollapsed = false;
340
352
  aiCodeCompletionConfig?: TextEditor.AiCodeCompletionProvider.AiCodeCompletionConfig;
341
353
  private aiCodeCompletionSummaryToolbarContainer?: HTMLElement;
342
354
  private aiCodeCompletionSummaryToolbar?: AiCodeCompletionSummaryToolbar;
@@ -428,6 +440,10 @@ export class ConsoleView extends UI.Widget.VBox implements
428
440
  i18nString(UIStrings.showConsoleSidebar), i18nString(UIStrings.hideConsoleSidebar),
429
441
  i18nString(UIStrings.consoleSidebarShown), i18nString(UIStrings.consoleSidebarHidden), 'console-sidebar'));
430
442
  toolbar.appendToolbarItem(UI.Toolbar.Toolbar.createActionButton('console.clear'));
443
+ this.#collapseAllButton =
444
+ new UI.Toolbar.ToolbarButton(i18nString(UIStrings.collapseAll), 'compress', undefined, 'console.collapse-all');
445
+ this.#collapseAllButton.addEventListener(UI.Toolbar.ToolbarButton.Events.CLICK, this.#toggleCollapseAll, this);
446
+ toolbar.appendToolbarItem(this.#collapseAllButton);
431
447
  toolbar.appendSeparator();
432
448
  toolbar.appendToolbarItem(this.consoleContextSelector.toolbarItem());
433
449
  toolbar.appendSeparator();
@@ -684,6 +700,62 @@ export class ConsoleView extends UI.Widget.VBox implements
684
700
  this.prompt.clearAiCodeCompletionCache();
685
701
  }
686
702
 
703
+ collapseAll(): void {
704
+ for (const message of this.consoleMessages) {
705
+ if (message instanceof ConsoleGroupViewMessage) {
706
+ message.setCollapsedSilent(true);
707
+ }
708
+ message.setTraceExpanded(false);
709
+ }
710
+ this.updateMessageList();
711
+ }
712
+
713
+ expandAll(): void {
714
+ for (const message of this.consoleMessages) {
715
+ if (message instanceof ConsoleGroupViewMessage) {
716
+ message.setCollapsedSilent(false);
717
+ }
718
+ message.setTraceExpanded(true);
719
+ }
720
+ this.updateMessageList();
721
+ }
722
+
723
+ #toggleCollapseAll(): void {
724
+ if (this.#allCollapsed) {
725
+ this.expandAll();
726
+ } else {
727
+ this.collapseAll();
728
+ }
729
+ this.#allCollapsed = !this.#allCollapsed;
730
+ this.#updateCollapseAllButton();
731
+ }
732
+
733
+ #updateCollapseAllButton(): void {
734
+ let hasExpandedMessages = false;
735
+ let hasCollapsedMessages = false;
736
+ for (const message of this.visibleViewMessages) {
737
+ if (message instanceof ConsoleGroupViewMessage) {
738
+ hasExpandedMessages = hasExpandedMessages || !message.collapsed();
739
+ hasCollapsedMessages = hasCollapsedMessages || message.collapsed();
740
+ }
741
+ if (message.isExpandableTrace()) {
742
+ hasExpandedMessages = hasExpandedMessages || message.isTraceExpanded();
743
+ hasCollapsedMessages = hasCollapsedMessages || !message.isTraceExpanded();
744
+ }
745
+ if (hasExpandedMessages && hasCollapsedMessages) {
746
+ break;
747
+ }
748
+ }
749
+ this.#allCollapsed = !hasExpandedMessages && hasCollapsedMessages;
750
+ if (this.#allCollapsed) {
751
+ this.#collapseAllButton.setGlyph('expand');
752
+ this.#collapseAllButton.setTitle(i18nString(UIStrings.expandAll));
753
+ } else {
754
+ this.#collapseAllButton.setGlyph('compress');
755
+ this.#collapseAllButton.setTitle(i18nString(UIStrings.collapseAll));
756
+ }
757
+ }
758
+
687
759
  #onIssuesCountUpdate(): void {
688
760
  void this.issueToolbarThrottle.schedule(async () => this.updateIssuesToolbarItem());
689
761
  this.issuesCountUpdatedForTest();
@@ -1121,11 +1193,13 @@ export class ConsoleView extends UI.Widget.VBox implements
1121
1193
  }
1122
1194
  const parentGroup = currentGroup.consoleGroup();
1123
1195
  if (parentGroup) {
1196
+ // If the parent group has its messages hidden, don't show this group.
1197
+ if (parentGroup.messagesHidden()) {
1198
+ return;
1199
+ }
1124
1200
  showGroup(parentGroup, visibleViewMessages);
1125
1201
  }
1126
- if (!parentGroup?.messagesHidden()) {
1127
- visibleViewMessages.push(currentGroup);
1128
- }
1202
+ visibleViewMessages.push(currentGroup);
1129
1203
  }
1130
1204
  }
1131
1205
 
@@ -1369,18 +1443,23 @@ export class ConsoleView extends UI.Widget.VBox implements
1369
1443
  for (const consoleMessage of this.consoleMessages) {
1370
1444
  consoleMessage.setInSimilarGroup(false);
1371
1445
  if (consoleMessage.consoleMessage().isGroupable()) {
1372
- // Since grouping similar messages is disabled, we need clear the
1373
- // reference to the artificial console group start.
1374
- consoleMessage.clearConsoleGroup();
1446
+ // Since grouping similar messages is disabled, we need to clear the
1447
+ // reference to the artificial console group start, but preserve
1448
+ // references to real console groups (console.group()/console.groupCollapsed()).
1449
+ const group = consoleMessage.consoleGroup();
1450
+ if (group && !this.consoleGroupStarts.includes(group)) {
1451
+ consoleMessage.clearConsoleGroup();
1452
+ }
1375
1453
  }
1376
1454
  this.appendMessageToEnd(
1377
1455
  consoleMessage,
1378
- true /* crbug.com/1082963: prevent collaps`e of same messages when "Group similar" is false */);
1456
+ true /* crbug.com/1082963: prevent collapse of same messages when "Group similar" is false */);
1379
1457
  }
1380
1458
  }
1381
1459
  this.updateFilterStatus();
1382
1460
  this.#searchableView.updateSearchMatchesCount(this.regexMatchRanges.length);
1383
1461
  this.highlightMatch(this.currentMatchRangeIndex, false); // Re-highlight current match without scrolling.
1462
+ this.#updateCollapseAllButton();
1384
1463
  this.viewport.invalidate();
1385
1464
  this.messagesCountElement.setAttribute(
1386
1465
  'aria-label', i18nString(UIStrings.filteredMessagesInConsole, {PH1: this.visibleViewMessages.length}));
@@ -299,6 +299,7 @@ export class ConsoleViewMessage implements ConsoleViewportElement {
299
299
  protected messageIcon: Icon|null;
300
300
  private traceExpanded: boolean;
301
301
  private expandTrace: ((arg0: boolean) => void)|null;
302
+ private hasStackTrace: boolean;
302
303
  protected anchorElement: HTMLElement|null;
303
304
  protected contentElementInternal: HTMLElement|null;
304
305
  private nestingLevelMarkers: HTMLElement[]|null;
@@ -340,6 +341,7 @@ export class ConsoleViewMessage implements ConsoleViewportElement {
340
341
  this.messageIcon = null;
341
342
  this.traceExpanded = false;
342
343
  this.expandTrace = null;
344
+ this.hasStackTrace = false;
343
345
  this.anchorElement = null;
344
346
  this.contentElementInternal = null;
345
347
  this.nestingLevelMarkers = null;
@@ -653,6 +655,8 @@ export class ConsoleViewMessage implements ConsoleViewportElement {
653
655
  this.expandTrace(true);
654
656
  }
655
657
 
658
+ this.hasStackTrace = true;
659
+
656
660
  // @ts-expect-error
657
661
  toggleElement._expandStackTraceForTest = this.expandTrace.bind(this, true);
658
662
  return toggleElement;
@@ -1200,6 +1204,20 @@ export class ConsoleViewMessage implements ConsoleViewportElement {
1200
1204
  return this.consoleGroupInternal;
1201
1205
  }
1202
1206
 
1207
+ isTraceExpanded(): boolean {
1208
+ return this.traceExpanded;
1209
+ }
1210
+
1211
+ isExpandableTrace(): boolean {
1212
+ return this.hasStackTrace;
1213
+ }
1214
+
1215
+ setTraceExpanded(expanded: boolean): void {
1216
+ if (this.expandTrace && this.traceExpanded !== expanded) {
1217
+ this.expandTrace(expanded);
1218
+ }
1219
+ }
1220
+
1203
1221
  setInSimilarGroup(inSimilarGroup: boolean, isLast?: boolean): void {
1204
1222
  this.inSimilarGroup = inSimilarGroup;
1205
1223
  this.lastInSimilarGroup = inSimilarGroup && Boolean(isLast);
@@ -2161,11 +2179,15 @@ export class ConsoleGroupViewMessage extends ConsoleViewMessage {
2161
2179
  }
2162
2180
 
2163
2181
  setCollapsed(collapsed: boolean): void {
2182
+ this.setCollapsedSilent(collapsed);
2183
+ this.onToggle.call(null);
2184
+ }
2185
+
2186
+ setCollapsedSilent(collapsed: boolean): void {
2164
2187
  this.collapsedInternal = collapsed;
2165
2188
  if (this.expandGroupIcon) {
2166
2189
  this.expandGroupIcon.name = this.collapsedInternal ? 'triangle-right' : 'triangle-down';
2167
2190
  }
2168
- this.onToggle.call(null);
2169
2191
  }
2170
2192
 
2171
2193
  collapsed(): boolean {