chrome-devtools-frontend 1.0.1519267 → 1.0.1520139

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 (90) hide show
  1. package/config/owner/COMMON_OWNERS +1 -2
  2. package/config/typescript/tsconfig.eslint.json +12 -1
  3. package/docs/ui_engineering.md +1011 -0
  4. package/front_end/core/host/GdpClient.ts +12 -3
  5. package/front_end/core/sdk/NetworkManager.ts +1 -0
  6. package/front_end/core/sdk/NetworkRequest.ts +10 -0
  7. package/front_end/entrypoints/main/MainImpl.ts +6 -1
  8. package/front_end/entrypoints/main/main-meta.ts +3 -3
  9. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +60 -34
  10. package/front_end/models/ai_assistance/agents/PerformanceAnnotationsAgent.ts +4 -4
  11. package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.snapshot.txt +127 -29
  12. package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.ts +100 -55
  13. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +317 -640
  14. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.ts +23 -19
  15. package/front_end/models/ai_assistance/performance/AICallTree.snapshot.txt +75 -0
  16. package/front_end/models/ai_assistance/performance/AICallTree.ts +14 -6
  17. package/front_end/models/ai_assistance/performance/AIContext.ts +62 -7
  18. package/front_end/models/ai_code_completion/AiCodeCompletion.ts +5 -5
  19. package/front_end/models/badges/Badge.ts +6 -1
  20. package/front_end/models/badges/StarterBadge.ts +5 -0
  21. package/front_end/models/javascript_metadata/NativeFunctions.js +5 -1
  22. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +14 -7
  23. package/front_end/panels/ai_assistance/PatchWidget.ts +17 -55
  24. package/front_end/panels/ai_assistance/components/ChatView.ts +44 -68
  25. package/front_end/panels/ai_assistance/components/PerformanceAgentMarkdownRenderer.ts +47 -1
  26. package/front_end/panels/ai_assistance/components/chatView.css +12 -0
  27. package/front_end/panels/animation/AnimationTimeline.ts +1 -1
  28. package/front_end/panels/animation/animationTimeline.css +4 -0
  29. package/front_end/panels/application/preloading/components/PreloadingString.ts +2 -5
  30. package/front_end/panels/common/AiCodeCompletionTeaser.ts +5 -0
  31. package/front_end/panels/common/aiCodeCompletionTeaser.css +6 -1
  32. package/front_end/panels/console/ConsolePrompt.ts +6 -0
  33. package/front_end/panels/console/ConsoleView.ts +4 -2
  34. package/front_end/panels/coverage/CoverageListView.ts +133 -158
  35. package/front_end/panels/coverage/CoverageView.ts +39 -16
  36. package/front_end/panels/mobile_throttling/NetworkThrottlingSelector.ts +2 -0
  37. package/front_end/panels/network/NetworkDataGridNode.ts +22 -0
  38. package/front_end/panels/network/NetworkLogViewColumns.ts +9 -0
  39. package/front_end/panels/recorder/components/CreateRecordingView.ts +2 -0
  40. package/front_end/panels/search/SearchResultsPane.ts +48 -15
  41. package/front_end/panels/search/SearchView.ts +33 -30
  42. package/front_end/panels/search/searchView.css +0 -2
  43. package/front_end/panels/settings/components/SyncSection.ts +3 -3
  44. package/front_end/panels/sources/AiCodeCompletionPlugin.ts +1 -4
  45. package/front_end/panels/sources/DebuggerPlugin.ts +4 -0
  46. package/front_end/panels/timeline/ThirdPartyTreeView.ts +1 -1
  47. package/front_end/panels/timeline/TimelineFlameChartDataProvider.ts +0 -8
  48. package/front_end/panels/timeline/TimelineFlameChartView.ts +5 -5
  49. package/front_end/panels/timeline/TimelinePanel.ts +2 -0
  50. package/front_end/panels/timeline/TimelineTreeView.ts +1 -1
  51. package/front_end/third_party/chromium/README.chromium +1 -1
  52. package/front_end/third_party/puppeteer/README.chromium +2 -2
  53. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/Realm.d.ts +2 -2
  54. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/generated/version.d.ts +1 -1
  55. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/generated/version.js +1 -1
  56. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/injected/injected.d.ts +1 -1
  57. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/BrowserLauncher.js +1 -1
  58. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/BrowserLauncher.js.map +1 -1
  59. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/PipeTransport.d.ts.map +1 -1
  60. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/PipeTransport.js +15 -16
  61. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/PipeTransport.js.map +1 -1
  62. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.d.ts +2 -2
  63. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js +2 -2
  64. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Function.js +1 -1
  65. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Function.js.map +1 -1
  66. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Mutex.d.ts +2 -2
  67. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +4 -4
  68. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/version.d.ts +1 -1
  69. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/version.js +1 -1
  70. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/BrowserLauncher.js +1 -1
  71. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/BrowserLauncher.js.map +1 -1
  72. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/PipeTransport.d.ts.map +1 -1
  73. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/PipeTransport.js +15 -16
  74. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/PipeTransport.js.map +1 -1
  75. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.d.ts +2 -2
  76. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js +2 -2
  77. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/Function.js +1 -1
  78. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/Function.js.map +1 -1
  79. package/front_end/third_party/puppeteer/package/package.json +3 -2
  80. package/front_end/third_party/puppeteer/package/src/generated/version.ts +1 -1
  81. package/front_end/third_party/puppeteer/package/src/node/BrowserLauncher.ts +1 -1
  82. package/front_end/third_party/puppeteer/package/src/node/PipeTransport.ts +15 -17
  83. package/front_end/third_party/puppeteer/package/src/revisions.ts +2 -2
  84. package/front_end/third_party/puppeteer/package/src/util/Function.ts +1 -1
  85. package/front_end/tsconfig.json +12 -1
  86. package/front_end/ui/legacy/InspectorView.ts +86 -13
  87. package/front_end/ui/legacy/TabbedPane.ts +2 -1
  88. package/front_end/ui/visual_logging/KnownContextValues.ts +6 -0
  89. package/front_end/ui/visual_logging/LoggingEvents.ts +1 -1
  90. package/package.json +1 -1
@@ -43,10 +43,20 @@ export class PerformanceInsightFormatter extends PerformanceTraceFormatter {
43
43
  #insight: Trace.Insights.Types.InsightModel;
44
44
  #parsedTrace: Trace.TraceModel.ParsedTrace;
45
45
 
46
+ /**
47
+ * A utility method because we dependency inject this formatter into
48
+ * PerformanceTraceFormatter; this allows you to pass
49
+ * PerformanceInsightFormatter.create rather than an anonymous
50
+ * function that wraps the constructor.
51
+ */
52
+ static create(focus: AgentFocus, insight: Trace.Insights.Types.InsightModel): PerformanceInsightFormatter {
53
+ return new PerformanceInsightFormatter(focus, insight);
54
+ }
55
+
46
56
  constructor(focus: AgentFocus, insight: Trace.Insights.Types.InsightModel) {
47
- super(focus);
57
+ super(focus, null);
48
58
  this.#insight = insight;
49
- this.#parsedTrace = focus.data.parsedTrace;
59
+ this.#parsedTrace = focus.parsedTrace;
50
60
  }
51
61
 
52
62
  #formatMilli(x?: number): string {
@@ -63,6 +73,28 @@ export class PerformanceInsightFormatter extends PerformanceTraceFormatter {
63
73
  return this.#formatMilli(Trace.Helpers.Timing.microToMilli(x as Trace.Types.Timing.Micro));
64
74
  }
65
75
 
76
+ #formatRequestUrl(request: Trace.Types.Events.SyntheticNetworkRequest): string {
77
+ const eventKey = this.eventsSerializer.keyForEvent(request);
78
+ return `${request.args.data.url} (eventKey: ${eventKey})`;
79
+ }
80
+
81
+ #formatScriptUrl(script: Trace.Handlers.ModelHandlers.Scripts.Script): string {
82
+ if (script.request) {
83
+ return this.#formatRequestUrl(script.request);
84
+ }
85
+
86
+ return script.url ?? script.sourceUrl ?? script.scriptId;
87
+ }
88
+
89
+ #formatUrl(url: string): string {
90
+ const request = this.#parsedTrace.data.NetworkRequests.byTime.find(request => request.args.data.url === url);
91
+ if (request) {
92
+ return this.#formatRequestUrl(request);
93
+ }
94
+
95
+ return url;
96
+ }
97
+
66
98
  /**
67
99
  * Information about LCP which we pass to the LLM for all insights that relate to LCP.
68
100
  */
@@ -81,14 +113,15 @@ export class PerformanceInsightFormatter extends PerformanceTraceFormatter {
81
113
  }
82
114
 
83
115
  const {metricScore, lcpRequest, lcpEvent} = data;
84
- const theLcpElement =
85
- lcpEvent.args.data?.nodeName ? `The LCP element (${lcpEvent.args.data.nodeName})` : 'The LCP element';
116
+ const theLcpElement = lcpEvent.args.data?.nodeName ?
117
+ `The LCP element (${lcpEvent.args.data.nodeName}, nodeId: ${lcpEvent.args.data.nodeId})` :
118
+ 'The LCP element';
86
119
  const parts: string[] = [
87
120
  `The Largest Contentful Paint (LCP) time for this navigation was ${this.#formatMicro(metricScore.timing)}.`,
88
121
  ];
89
122
 
90
123
  if (lcpRequest) {
91
- parts.push(`${theLcpElement} is an image fetched from \`${lcpRequest.args.data.url}\`.`);
124
+ parts.push(`${theLcpElement} is an image fetched from ${this.#formatRequestUrl(lcpRequest)}.`);
92
125
  const request =
93
126
  this.formatNetworkRequests([lcpRequest], {verbose: true, customTitle: 'LCP resource network request'});
94
127
  parts.push(request);
@@ -203,7 +236,7 @@ export class PerformanceInsightFormatter extends PerformanceTraceFormatter {
203
236
  let output = 'The following resources were associated with ineffficient cache policies:\n';
204
237
 
205
238
  for (const entry of insight.requests) {
206
- output += `\n- ${entry.request.args.data.url}`;
239
+ output += `\n- ${this.#formatRequestUrl(entry.request)}`;
207
240
  output += `\n - Cache Time to Live (TTL): ${entry.ttl} seconds`;
208
241
  output += `\n - Wasted bytes: ${bytes(entry.wastedBytes)}`;
209
242
  }
@@ -212,6 +245,57 @@ export class PerformanceInsightFormatter extends PerformanceTraceFormatter {
212
245
  return output;
213
246
  }
214
247
 
248
+ #formatLayoutShift(
249
+ shift: Trace.Types.Events.SyntheticLayoutShift, index: number,
250
+ rootCauses?: Trace.Insights.Models.CLSCulprits.LayoutShiftRootCausesData): string {
251
+ const baseTime = this.#parsedTrace.data.Meta.traceBounds.min;
252
+
253
+ const potentialRootCauses: string[] = [];
254
+ if (rootCauses) {
255
+ rootCauses.iframes.forEach(
256
+ iframe => potentialRootCauses.push(
257
+ `- An iframe (id: ${iframe.frame}, url: ${iframe.url ?? 'unknown'} was injected into the page)`));
258
+ rootCauses.webFonts.forEach(req => {
259
+ potentialRootCauses.push(`- A font that was loaded over the network: ${this.#formatRequestUrl(req)}.`);
260
+ });
261
+ rootCauses.nonCompositedAnimations.forEach(nonCompositedFailure => {
262
+ potentialRootCauses.push('- A non-composited animation:');
263
+
264
+ const animationInfoOutput = [];
265
+ potentialRootCauses.push(`- non-composited animation: \`${nonCompositedFailure.name || '(unnamed)'}\``);
266
+ if (nonCompositedFailure.name) {
267
+ animationInfoOutput.push(`Animation name: ${nonCompositedFailure.name}`);
268
+ }
269
+ if (nonCompositedFailure.unsupportedProperties) {
270
+ animationInfoOutput.push('Unsupported CSS properties:');
271
+ animationInfoOutput.push('- ' + nonCompositedFailure.unsupportedProperties.join(', '));
272
+ }
273
+ animationInfoOutput.push('Failure reasons:');
274
+ animationInfoOutput.push(' - ' + nonCompositedFailure.failureReasons.join(', '));
275
+
276
+ // Extra padding to the detail to not mess up the indentation.
277
+ potentialRootCauses.push(animationInfoOutput.map(l => ' '.repeat(4) + l).join('\n'));
278
+ });
279
+
280
+ rootCauses.unsizedImages.forEach(img => {
281
+ // TODO(b/413284569): if we store a nice human readable name for this
282
+ // image in the trace metadata, we can do something much nicer here.
283
+ const url = img.paintImageEvent.args.data.url;
284
+ const nodeName = img.paintImageEvent.args.data.nodeName;
285
+ const extraText = url ? `url: ${this.#formatUrl(url)}` : `id: ${img.backendNodeId}`;
286
+ potentialRootCauses.push(`- An unsized image (${nodeName}) (${extraText}).`);
287
+ });
288
+ }
289
+ const rootCauseText = potentialRootCauses.length ? `- Potential root causes:\n ${potentialRootCauses.join('\n')}` :
290
+ '- No potential root causes identified';
291
+
292
+ const startTime = Trace.Helpers.Timing.microToMilli(Trace.Types.Timing.Micro(shift.ts - baseTime));
293
+ return `### Layout shift ${index + 1}:
294
+ - Start time: ${millis(startTime)}
295
+ - Score: ${shift.args.data?.weighted_score_delta.toFixed(4)}
296
+ ${rootCauseText}`;
297
+ }
298
+
215
299
  /**
216
300
  * Create an AI prompt string out of the CLS Culprits Insight model to use with Ask AI.
217
301
  * @param insight The CLS Culprits Model to query.
@@ -220,7 +304,7 @@ export class PerformanceInsightFormatter extends PerformanceTraceFormatter {
220
304
  formatClsCulpritsInsight(insight: Trace.Insights.Models.CLSCulprits.CLSCulpritsInsightModel): string {
221
305
  const {worstCluster, shifts} = insight;
222
306
  if (!worstCluster) {
223
- return '';
307
+ return 'No layout shifts were found.';
224
308
  }
225
309
 
226
310
  const baseTime = this.#parsedTrace.data.Meta.traceBounds.min;
@@ -231,7 +315,7 @@ export class PerformanceInsightFormatter extends PerformanceTraceFormatter {
231
315
  } as const;
232
316
 
233
317
  const shiftsFormatted = worstCluster.events.map((layoutShift, index) => {
234
- return TraceEventFormatter.layoutShift(layoutShift, index, this.#parsedTrace, shifts.get(layoutShift));
318
+ return this.#formatLayoutShift(layoutShift, index, shifts.get(layoutShift));
235
319
  });
236
320
 
237
321
  return `The worst layout shift cluster was the cluster that started at ${
@@ -384,8 +468,9 @@ Duplication grouped by Node modules: ${filesFormatted}`;
384
468
  const url = new Common.ParsedURL.ParsedURL(font.request.args.data.url);
385
469
  fontName = url.isValid ? url.lastPathComponent : '(not available)';
386
470
  }
387
- output += `\n - Font name: ${fontName}, URL: ${font.request.args.data.url}, Property 'font-display' set to: '${
388
- font.display}', Wasted time: ${this.#formatMilli(font.wastedTime)}.`;
471
+ output += `\n - Font name: ${fontName}, URL: ${
472
+ this.#formatRequestUrl(font.request)}, Property 'font-display' set to: '${font.display}', Wasted time: ${
473
+ this.#formatMilli(font.wastedTime)}.`;
389
474
  }
390
475
 
391
476
  output += '\n\n' + Trace.Insights.Models.FontDisplay.UIStrings.description;
@@ -469,7 +554,7 @@ Duplication grouped by Node modules: ${filesFormatted}`;
469
554
  })
470
555
  .join('\n');
471
556
 
472
- return `### ${image.request.args.data.url}
557
+ return `### ${this.#formatRequestUrl(image.request)}
473
558
  - Potential savings: ${bytes(image.byteSavings)}
474
559
  - Optimizations:\n${optimizations}`;
475
560
  })
@@ -580,7 +665,9 @@ ${checklistBulletPoints.map(point => `- ${point.name}: ${point.passed ? 'PASSED'
580
665
 
581
666
  const filesFormatted =
582
667
  Array.from(legacyJavaScriptResults)
583
- .map(([script, result]) => `\n- Script: ${script.url} - Wasted bytes: ${result.estimatedByteSavings} bytes
668
+ .map(
669
+ ([script, result]) =>
670
+ `\n- Script: ${this.#formatScriptUrl(script)} - Wasted bytes: ${result.estimatedByteSavings} bytes
584
671
  Matches:
585
672
  ${result.matches.map(match => `Line: ${match.line}, Column: ${match.column}, Name: ${match.name}`).join('\n')}`)
586
673
  .join('\n');
@@ -631,7 +718,7 @@ ${requestSummary}`;
631
718
  function formatNode(
632
719
  this: PerformanceInsightFormatter, node: Trace.Insights.Models.NetworkDependencyTree.CriticalRequestNode,
633
720
  indent: string): string {
634
- const url = node.request.args.data.url;
721
+ const url = this.#formatRequestUrl(node.request);
635
722
  const time = this.#formatMicro(node.timeFromInitialRequest);
636
723
  const isLongest = node.isLongest ? ' (longest chain)' : '';
637
724
  let nodeString = `${indent}- ${url} (${time})${isLongest}\n`;
@@ -1065,45 +1152,3 @@ Polyfills and transforms enable older browsers to use new JavaScript features. H
1065
1152
  }
1066
1153
  }
1067
1154
  }
1068
-
1069
- export class TraceEventFormatter {
1070
- static layoutShift(
1071
- shift: Trace.Types.Events.SyntheticLayoutShift, index: number, parsedTrace: Trace.TraceModel.ParsedTrace,
1072
- rootCauses?: Trace.Insights.Models.CLSCulprits.LayoutShiftRootCausesData): string {
1073
- const baseTime = parsedTrace.data.Meta.traceBounds.min;
1074
-
1075
- const potentialRootCauses: string[] = [];
1076
- if (rootCauses) {
1077
- rootCauses.iframes.forEach(
1078
- iframe => potentialRootCauses.push(
1079
- `An iframe (id: ${iframe.frame}, url: ${iframe.url ?? 'unknown'} was injected into the page)`));
1080
- rootCauses.webFonts.forEach(req => {
1081
- potentialRootCauses.push(`A font that was loaded over the network (${req.args.data.url}).`);
1082
- });
1083
- // TODO(b/413285103): use the nice strings for non-composited animations.
1084
- // The code for this lives in TimelineUIUtils but that cannot be used
1085
- // within models. We should move it and then expose the animations info
1086
- // more nicely.
1087
- rootCauses.nonCompositedAnimations.forEach(_ => {
1088
- potentialRootCauses.push('A non composited animation.');
1089
- });
1090
- rootCauses.unsizedImages.forEach(img => {
1091
- // TODO(b/413284569): if we store a nice human readable name for this
1092
- // image in the trace metadata, we can do something much nicer here.
1093
- const url = img.paintImageEvent.args.data.url;
1094
- const nodeName = img.paintImageEvent.args.data.nodeName;
1095
- const extraText = url ? `url: ${url}` : `id: ${img.backendNodeId}`;
1096
- potentialRootCauses.push(`An unsized image (${nodeName}) (${extraText}).`);
1097
- });
1098
- }
1099
- const rootCauseText = potentialRootCauses.length ?
1100
- `- Potential root causes:\n - ${potentialRootCauses.join('\n - ')}` :
1101
- '- No potential root causes identified';
1102
-
1103
- const startTime = Trace.Helpers.Timing.microToMilli(Trace.Types.Timing.Micro(shift.ts - baseTime));
1104
- return `### Layout shift ${index + 1}:
1105
- - Start time: ${millis(startTime)}
1106
- - Score: ${shift.args.data?.weighted_score_delta.toFixed(4)}
1107
- ${rootCauseText}`;
1108
- }
1109
- }