chrome-devtools-frontend 1.0.1512147 → 1.0.1512349
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/front_end/Images/src/ai-explorer-badge.svg +114 -0
- package/front_end/Images/src/code-whisperer-badge.svg +166 -0
- package/front_end/Images/src/devtools-user-badge.svg +129 -0
- package/front_end/Images/src/dom-detective-badge.svg +136 -0
- package/front_end/Images/src/speedster-badge.svg +166 -0
- package/front_end/core/host/GdpClient.ts +38 -2
- package/front_end/core/i18n/NumberFormatter.ts +7 -0
- package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +3 -19
- package/front_end/models/ai_assistance/ai_assistance.ts +1 -1
- package/front_end/models/ai_assistance/data_formatters/NetworkRequestFormatter.ts +7 -6
- package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.snapshot.txt +119 -119
- package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.ts +43 -52
- package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +100 -100
- package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.ts +11 -17
- package/front_end/models/ai_assistance/data_formatters/UnitFormatters.ts +151 -0
- package/front_end/models/badges/Badge.ts +7 -4
- package/front_end/models/badges/DOMDetectiveBadge.ts +20 -0
- package/front_end/models/badges/SpeedsterBadge.ts +4 -1
- package/front_end/models/badges/StarterBadge.ts +5 -1
- package/front_end/models/badges/UserBadges.ts +33 -7
- package/front_end/models/trace/ModelImpl.ts +0 -13
- package/front_end/panels/common/BadgeNotification.ts +119 -9
- package/front_end/panels/common/badgeNotification.css +4 -0
- package/front_end/panels/elements/ElementsTreeElement.ts +12 -0
- package/front_end/panels/elements/ElementsTreeOutline.ts +3 -0
- package/front_end/panels/elements/StylePropertiesSection.ts +3 -0
- package/front_end/panels/elements/StylePropertyTreeElement.ts +5 -0
- package/front_end/panels/settings/SettingsScreen.ts +3 -9
- package/front_end/panels/settings/components/SyncSection.ts +6 -2
- package/front_end/panels/timeline/TimelinePanel.ts +21 -9
- package/front_end/panels/timeline/TimelineUIUtils.ts +4 -3
- package/front_end/ui/legacy/filter.css +1 -1
- package/front_end/ui/legacy/inspectorCommon.css +1 -1
- package/front_end/ui/legacy/softDropDownButton.css +1 -1
- package/package.json +1 -1
- package/front_end/models/ai_assistance/data_formatters/Types.ts +0 -9
@@ -9,7 +9,7 @@ import type {ConversationSuggestion} from '../agents/AiAgent.js';
|
|
9
9
|
import {
|
10
10
|
NetworkRequestFormatter,
|
11
11
|
} from './NetworkRequestFormatter.js';
|
12
|
-
import
|
12
|
+
import {bytes, micros, millis} from './UnitFormatters.js';
|
13
13
|
|
14
14
|
/**
|
15
15
|
* For a given frame ID and navigation ID, returns the LCP Event and the LCP Request, if the resource was an image.
|
@@ -43,21 +43,17 @@ function getLCPData(parsedTrace: Trace.Handlers.Types.ParsedTrace, frameId: stri
|
|
43
43
|
export class PerformanceInsightFormatter {
|
44
44
|
#insight: Trace.Insights.Types.InsightModel;
|
45
45
|
#parsedTrace: Trace.Handlers.Types.ParsedTrace;
|
46
|
-
#unitFormatters: UnitFormatters;
|
47
46
|
|
48
|
-
constructor(
|
49
|
-
formatters: UnitFormatters, parsedTrace: Trace.Handlers.Types.ParsedTrace,
|
50
|
-
insight: Trace.Insights.Types.InsightModel) {
|
47
|
+
constructor(parsedTrace: Trace.Handlers.Types.ParsedTrace, insight: Trace.Insights.Types.InsightModel) {
|
51
48
|
this.#insight = insight;
|
52
49
|
this.#parsedTrace = parsedTrace;
|
53
|
-
this.#unitFormatters = formatters;
|
54
50
|
}
|
55
51
|
|
56
52
|
#formatMilli(x?: number): string {
|
57
53
|
if (x === undefined) {
|
58
54
|
return '';
|
59
55
|
}
|
60
|
-
return
|
56
|
+
return millis(x);
|
61
57
|
}
|
62
58
|
|
63
59
|
#formatMicro(x?: number): string {
|
@@ -94,8 +90,7 @@ export class PerformanceInsightFormatter {
|
|
94
90
|
if (lcpRequest) {
|
95
91
|
parts.push(`${theLcpElement} is an image fetched from \`${lcpRequest.args.data.url}\`.`);
|
96
92
|
const request = TraceEventFormatter.networkRequests(
|
97
|
-
|
98
|
-
{verbose: true, customTitle: 'LCP resource network request'});
|
93
|
+
[lcpRequest], this.#parsedTrace, {verbose: true, customTitle: 'LCP resource network request'});
|
99
94
|
parts.push(request);
|
100
95
|
} else {
|
101
96
|
parts.push(`${theLcpElement} is text and was not fetched from the network.`);
|
@@ -417,7 +412,7 @@ export class PerformanceInsightFormatter {
|
|
417
412
|
output += `The following list contains the largest transfer sizes by a 3rd party script:\n\n`;
|
418
413
|
for (const entry of thirdPartyTransferSizeEntries) {
|
419
414
|
if (entry.transferSize > 0) {
|
420
|
-
output += `- ${entry.entity.name}: ${
|
415
|
+
output += `- ${entry.entity.name}: ${bytes(entry.transferSize)}\n`;
|
421
416
|
}
|
422
417
|
}
|
423
418
|
output += '\n';
|
@@ -475,18 +470,18 @@ ${this.#links()}`;
|
|
475
470
|
.map(optimization => {
|
476
471
|
const message =
|
477
472
|
Trace.Insights.Models.ImageDelivery.getOptimizationMessage(optimization);
|
478
|
-
const byteSavings =
|
473
|
+
const byteSavings = bytes(optimization.byteSavings);
|
479
474
|
return `${message} (Est ${byteSavings})`;
|
480
475
|
})
|
481
476
|
.join('\n');
|
482
477
|
|
483
478
|
return `### ${image.request.args.data.url}
|
484
|
-
- Potential savings: ${
|
479
|
+
- Potential savings: ${bytes(image.byteSavings)}
|
485
480
|
- Optimizations:\n${optimizations}`;
|
486
481
|
})
|
487
482
|
.join('\n\n');
|
488
483
|
|
489
|
-
return `Total potential savings: ${
|
484
|
+
return `Total potential savings: ${bytes(this.#insight.wastedBytes)}
|
490
485
|
|
491
486
|
The following images could be optimized:\n\n${imageDetails}`;
|
492
487
|
}
|
@@ -546,8 +541,8 @@ ${checklistBulletPoints.map(point => `- ${point.name}: ${point.passed ? 'PASSED'
|
|
546
541
|
}
|
547
542
|
|
548
543
|
if (Trace.Insights.Models.RenderBlocking.isRenderBlocking(this.#insight)) {
|
549
|
-
const requestSummary =
|
550
|
-
this.#
|
544
|
+
const requestSummary =
|
545
|
+
TraceEventFormatter.networkRequests(this.#insight.renderBlockingRequests, this.#parsedTrace);
|
551
546
|
|
552
547
|
if (requestSummary.length === 0) {
|
553
548
|
return 'There are no network requests that are render blocking.';
|
@@ -582,7 +577,7 @@ ${requestSummary}`;
|
|
582
577
|
|
583
578
|
return `${this.#lcpMetricSharedContext()}
|
584
579
|
|
585
|
-
${TraceEventFormatter.networkRequests(
|
580
|
+
${TraceEventFormatter.networkRequests([documentRequest], this.#parsedTrace, {
|
586
581
|
verbose: true,
|
587
582
|
customTitle: 'Document network request'
|
588
583
|
})}
|
@@ -622,8 +617,7 @@ ${checklistBulletPoints.map(point => `- ${point.name}: ${point.passed ? 'PASSED'
|
|
622
617
|
} as const;
|
623
618
|
|
624
619
|
const shiftsFormatted = worstCluster.events.map((layoutShift, index) => {
|
625
|
-
return TraceEventFormatter.layoutShift(
|
626
|
-
this.#unitFormatters, layoutShift, index, this.#parsedTrace, shifts.get(layoutShift));
|
620
|
+
return TraceEventFormatter.layoutShift(layoutShift, index, this.#parsedTrace, shifts.get(layoutShift));
|
627
621
|
});
|
628
622
|
|
629
623
|
return `The worst layout shift cluster was the cluster that started at ${
|
@@ -637,9 +631,8 @@ ${shiftsFormatted.join('\n')}`;
|
|
637
631
|
|
638
632
|
if (Trace.Insights.Models.ModernHTTP.isModernHTTP(this.#insight)) {
|
639
633
|
const requestSummary = (this.#insight.http1Requests.length === 1) ?
|
640
|
-
TraceEventFormatter.networkRequests(
|
641
|
-
|
642
|
-
TraceEventFormatter.networkRequests(this.#unitFormatters, this.#insight.http1Requests, this.#parsedTrace);
|
634
|
+
TraceEventFormatter.networkRequests(this.#insight.http1Requests, this.#parsedTrace, {verbose: true}) :
|
635
|
+
TraceEventFormatter.networkRequests(this.#insight.http1Requests, this.#parsedTrace);
|
643
636
|
|
644
637
|
if (requestSummary.length === 0) {
|
645
638
|
return 'There are no requests that were served over a legacy HTTP protocol.';
|
@@ -861,8 +854,7 @@ export interface NetworkRequestFormatOptions {
|
|
861
854
|
|
862
855
|
export class TraceEventFormatter {
|
863
856
|
static layoutShift(
|
864
|
-
|
865
|
-
parsedTrace: Trace.Handlers.Types.ParsedTrace,
|
857
|
+
shift: Trace.Types.Events.SyntheticLayoutShift, index: number, parsedTrace: Trace.Handlers.Types.ParsedTrace,
|
866
858
|
rootCauses?: Trace.Insights.Models.CLSCulprits.LayoutShiftRootCausesData): string {
|
867
859
|
const baseTime = parsedTrace.Meta.traceBounds.min;
|
868
860
|
|
@@ -896,15 +888,15 @@ export class TraceEventFormatter {
|
|
896
888
|
|
897
889
|
const startTime = Trace.Helpers.Timing.microToMilli(Trace.Types.Timing.Micro(shift.ts - baseTime));
|
898
890
|
return `### Layout shift ${index + 1}:
|
899
|
-
- Start time: ${
|
891
|
+
- Start time: ${millis(startTime)}
|
900
892
|
- Score: ${shift.args.data?.weighted_score_delta.toFixed(4)}
|
901
893
|
${rootCauseText}`;
|
902
894
|
}
|
903
895
|
|
904
896
|
// Stringify network requests for the LLM model.
|
905
897
|
static networkRequests(
|
906
|
-
|
907
|
-
|
898
|
+
requests: readonly Trace.Types.Events.SyntheticNetworkRequest[], parsedTrace: Trace.Handlers.Types.ParsedTrace,
|
899
|
+
options?: NetworkRequestFormatOptions): string {
|
908
900
|
if (requests.length === 0) {
|
909
901
|
return '';
|
910
902
|
}
|
@@ -921,12 +913,11 @@ ${rootCauseText}`;
|
|
921
913
|
// For a single request, use `formatRequestVerbosely`, which formats with all fields specified and does not require a
|
922
914
|
// format description.
|
923
915
|
if (verbose) {
|
924
|
-
return requests
|
925
|
-
.map(request => this.#networkRequestVerbosely(formatters, request, parsedTrace, options?.customTitle))
|
916
|
+
return requests.map(request => this.#networkRequestVerbosely(request, parsedTrace, options?.customTitle))
|
926
917
|
.join('\n');
|
927
918
|
}
|
928
919
|
|
929
|
-
return this.#networkRequestsArrayCompressed(
|
920
|
+
return this.#networkRequestsArrayCompressed(requests, parsedTrace);
|
930
921
|
}
|
931
922
|
|
932
923
|
/**
|
@@ -938,8 +929,8 @@ ${rootCauseText}`;
|
|
938
929
|
* talk to jacktfranklin@.
|
939
930
|
*/
|
940
931
|
static #networkRequestVerbosely(
|
941
|
-
|
942
|
-
|
932
|
+
request: Trace.Types.Events.SyntheticNetworkRequest, parsedTrace: Trace.Handlers.Types.ParsedTrace,
|
933
|
+
customTitle?: string): string {
|
943
934
|
const {
|
944
935
|
url,
|
945
936
|
statusCode,
|
@@ -991,8 +982,8 @@ ${rootCauseText}`;
|
|
991
982
|
const redirects = request.args.data.redirects.map((redirect, index) => {
|
992
983
|
const startTime = redirect.ts - baseTime;
|
993
984
|
return `#### Redirect ${index + 1}: ${redirect.url}
|
994
|
-
- Start time: ${
|
995
|
-
- Duration: ${
|
985
|
+
- Start time: ${micros(startTime)}
|
986
|
+
- Duration: ${micros(redirect.dur)}`;
|
996
987
|
});
|
997
988
|
|
998
989
|
const initiators = this.#getInitiatorChain(parsedTrace, request);
|
@@ -1000,14 +991,14 @@ ${rootCauseText}`;
|
|
1000
991
|
|
1001
992
|
return `${titlePrefix}: ${url}
|
1002
993
|
Timings:
|
1003
|
-
- Queued at: ${
|
1004
|
-
- Request sent at: ${
|
1005
|
-
- Download complete at: ${
|
1006
|
-
- Main thread processing completed at: ${
|
994
|
+
- Queued at: ${micros(startTimesForLifecycle.queuedAt)}
|
995
|
+
- Request sent at: ${micros(startTimesForLifecycle.requestSentAt)}
|
996
|
+
- Download complete at: ${micros(startTimesForLifecycle.downloadCompletedAt)}
|
997
|
+
- Main thread processing completed at: ${micros(startTimesForLifecycle.processingCompletedAt)}
|
1007
998
|
Durations:
|
1008
|
-
- Download time: ${
|
1009
|
-
- Main thread processing time: ${
|
1010
|
-
- Total duration: ${
|
999
|
+
- Download time: ${micros(downloadTime)}
|
1000
|
+
- Main thread processing time: ${micros(mainThreadProcessingDuration)}
|
1001
|
+
- Total duration: ${micros(request.dur)}${initiator ? `\nInitiator: ${initiator.args.data.url}` : ''}
|
1011
1002
|
Redirects:${redirects.length ? '\n' + redirects.join('\n') : ' no redirects'}
|
1012
1003
|
Status code: ${statusCode}
|
1013
1004
|
MIME Type: ${mimeType}
|
@@ -1037,7 +1028,7 @@ ${NetworkRequestFormatter.formatHeaders('Response headers', responseHeaders ?? [
|
|
1037
1028
|
// For a single request, use `formatRequestVerbosely`, which formats with all fields specified and does not require a
|
1038
1029
|
// format description.
|
1039
1030
|
static #networkRequestsArrayCompressed(
|
1040
|
-
|
1031
|
+
requests: readonly Trace.Types.Events.SyntheticNetworkRequest[],
|
1041
1032
|
parsedTrace: Trace.Handlers.Types.ParsedTrace): string {
|
1042
1033
|
const networkDataString = `
|
1043
1034
|
Network requests data:
|
@@ -1048,7 +1039,7 @@ Network requests data:
|
|
1048
1039
|
requests
|
1049
1040
|
.map(request => {
|
1050
1041
|
const urlIndex = TraceEventFormatter.#getOrAssignUrlIndex(urlIdToIndex, request.args.data.url);
|
1051
|
-
return this.#networkRequestCompressedFormat(
|
1042
|
+
return this.#networkRequestCompressedFormat(urlIndex, request, parsedTrace, urlIdToIndex);
|
1052
1043
|
})
|
1053
1044
|
.join('\n');
|
1054
1045
|
|
@@ -1104,7 +1095,7 @@ The order of headers corresponds to an internal fixed list. If a header is not p
|
|
1104
1095
|
* See `networkDataFormatDescription` above for specifics.
|
1105
1096
|
*/
|
1106
1097
|
static #networkRequestCompressedFormat(
|
1107
|
-
|
1098
|
+
urlIndex: number, request: Trace.Types.Events.SyntheticNetworkRequest,
|
1108
1099
|
parsedTrace: Trace.Handlers.Types.ParsedTrace, urlIdToIndex: Map<string, number>): string {
|
1109
1100
|
const {
|
1110
1101
|
statusCode,
|
@@ -1123,13 +1114,13 @@ The order of headers corresponds to an internal fixed list. If a header is not p
|
|
1123
1114
|
parsedTrace.Meta.navigationsByFrameId,
|
1124
1115
|
);
|
1125
1116
|
const baseTime = navigationForEvent?.ts ?? parsedTrace.Meta.traceBounds.min;
|
1126
|
-
const queuedTime =
|
1127
|
-
const requestSentTime =
|
1128
|
-
const downloadCompleteTime =
|
1129
|
-
const processingCompleteTime =
|
1130
|
-
const totalDuration =
|
1131
|
-
const downloadDuration =
|
1132
|
-
const mainThreadProcessingDuration =
|
1117
|
+
const queuedTime = micros(request.ts - baseTime);
|
1118
|
+
const requestSentTime = micros(syntheticData.sendStartTime - baseTime);
|
1119
|
+
const downloadCompleteTime = micros(syntheticData.finishTime - baseTime);
|
1120
|
+
const processingCompleteTime = micros(request.ts + request.dur - baseTime);
|
1121
|
+
const totalDuration = micros(request.dur);
|
1122
|
+
const downloadDuration = micros(syntheticData.finishTime - syntheticData.downloadStart);
|
1123
|
+
const mainThreadProcessingDuration = micros(request.ts + request.dur - syntheticData.finishTime);
|
1133
1124
|
const renderBlocking = Trace.Helpers.Network.isSyntheticNetworkRequestEventRenderBlocking(request) ? 't' : 'f';
|
1134
1125
|
const finalPriority = priority;
|
1135
1126
|
const headerValues = responseHeaders
|
@@ -1142,8 +1133,8 @@ The order of headers corresponds to an internal fixed list. If a header is not p
|
|
1142
1133
|
const redirects = request.args.data.redirects
|
1143
1134
|
.map(redirect => {
|
1144
1135
|
const urlIndex = TraceEventFormatter.#getOrAssignUrlIndex(urlIdToIndex, redirect.url);
|
1145
|
-
const redirectStartTime =
|
1146
|
-
const redirectDuration =
|
1136
|
+
const redirectStartTime = micros(redirect.ts - baseTime);
|
1137
|
+
const redirectDuration = micros(redirect.dur);
|
1147
1138
|
return `[${urlIndex}|${redirectStartTime}|${redirectDuration}]`;
|
1148
1139
|
})
|
1149
1140
|
.join(',');
|