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.
- package/config/owner/COMMON_OWNERS +1 -2
- package/config/typescript/tsconfig.eslint.json +12 -1
- package/docs/ui_engineering.md +1011 -0
- package/front_end/core/host/GdpClient.ts +12 -3
- package/front_end/core/sdk/NetworkManager.ts +1 -0
- package/front_end/core/sdk/NetworkRequest.ts +10 -0
- package/front_end/entrypoints/main/MainImpl.ts +6 -1
- package/front_end/entrypoints/main/main-meta.ts +3 -3
- package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +60 -34
- package/front_end/models/ai_assistance/agents/PerformanceAnnotationsAgent.ts +4 -4
- package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.snapshot.txt +127 -29
- package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.ts +100 -55
- package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +317 -640
- package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.ts +23 -19
- package/front_end/models/ai_assistance/performance/AICallTree.snapshot.txt +75 -0
- package/front_end/models/ai_assistance/performance/AICallTree.ts +14 -6
- package/front_end/models/ai_assistance/performance/AIContext.ts +62 -7
- package/front_end/models/ai_code_completion/AiCodeCompletion.ts +5 -5
- package/front_end/models/badges/Badge.ts +6 -1
- package/front_end/models/badges/StarterBadge.ts +5 -0
- package/front_end/models/javascript_metadata/NativeFunctions.js +5 -1
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +14 -7
- package/front_end/panels/ai_assistance/PatchWidget.ts +17 -55
- package/front_end/panels/ai_assistance/components/ChatView.ts +44 -68
- package/front_end/panels/ai_assistance/components/PerformanceAgentMarkdownRenderer.ts +47 -1
- package/front_end/panels/ai_assistance/components/chatView.css +12 -0
- package/front_end/panels/animation/AnimationTimeline.ts +1 -1
- package/front_end/panels/animation/animationTimeline.css +4 -0
- package/front_end/panels/application/preloading/components/PreloadingString.ts +2 -5
- package/front_end/panels/common/AiCodeCompletionTeaser.ts +5 -0
- package/front_end/panels/common/aiCodeCompletionTeaser.css +6 -1
- package/front_end/panels/console/ConsolePrompt.ts +6 -0
- package/front_end/panels/console/ConsoleView.ts +4 -2
- package/front_end/panels/coverage/CoverageListView.ts +133 -158
- package/front_end/panels/coverage/CoverageView.ts +39 -16
- package/front_end/panels/mobile_throttling/NetworkThrottlingSelector.ts +2 -0
- package/front_end/panels/network/NetworkDataGridNode.ts +22 -0
- package/front_end/panels/network/NetworkLogViewColumns.ts +9 -0
- package/front_end/panels/recorder/components/CreateRecordingView.ts +2 -0
- package/front_end/panels/search/SearchResultsPane.ts +48 -15
- package/front_end/panels/search/SearchView.ts +33 -30
- package/front_end/panels/search/searchView.css +0 -2
- package/front_end/panels/settings/components/SyncSection.ts +3 -3
- package/front_end/panels/sources/AiCodeCompletionPlugin.ts +1 -4
- package/front_end/panels/sources/DebuggerPlugin.ts +4 -0
- package/front_end/panels/timeline/ThirdPartyTreeView.ts +1 -1
- package/front_end/panels/timeline/TimelineFlameChartDataProvider.ts +0 -8
- package/front_end/panels/timeline/TimelineFlameChartView.ts +5 -5
- package/front_end/panels/timeline/TimelinePanel.ts +2 -0
- package/front_end/panels/timeline/TimelineTreeView.ts +1 -1
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/third_party/puppeteer/README.chromium +2 -2
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/Realm.d.ts +2 -2
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/generated/version.d.ts +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/generated/version.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/injected/injected.d.ts +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/BrowserLauncher.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/BrowserLauncher.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/PipeTransport.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/PipeTransport.js +15 -16
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/PipeTransport.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.d.ts +2 -2
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js +2 -2
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Function.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Function.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Mutex.d.ts +2 -2
- package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +4 -4
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/version.d.ts +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/version.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/BrowserLauncher.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/BrowserLauncher.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/PipeTransport.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/PipeTransport.js +15 -16
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/PipeTransport.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.d.ts +2 -2
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js +2 -2
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/Function.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/Function.js.map +1 -1
- package/front_end/third_party/puppeteer/package/package.json +3 -2
- package/front_end/third_party/puppeteer/package/src/generated/version.ts +1 -1
- package/front_end/third_party/puppeteer/package/src/node/BrowserLauncher.ts +1 -1
- package/front_end/third_party/puppeteer/package/src/node/PipeTransport.ts +15 -17
- package/front_end/third_party/puppeteer/package/src/revisions.ts +2 -2
- package/front_end/third_party/puppeteer/package/src/util/Function.ts +1 -1
- package/front_end/tsconfig.json +12 -1
- package/front_end/ui/legacy/InspectorView.ts +86 -13
- package/front_end/ui/legacy/TabbedPane.ts +2 -1
- package/front_end/ui/visual_logging/KnownContextValues.ts +6 -0
- package/front_end/ui/visual_logging/LoggingEvents.ts +1 -1
- 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.
|
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
|
-
|
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
|
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
|
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
|
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: ${
|
388
|
-
font.display}', Wasted time: ${
|
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
|
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(
|
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
|
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
|
-
}
|