@paulirish/trace_engine 0.0.48 → 0.0.49
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/.tmp/tsbuildinfo/tsconfig.tsbuildinfo +1 -1
- package/core/platform/TypedArrayUtilities.d.ts +2 -1
- package/core/platform/TypedArrayUtilities.js +9 -4
- package/core/platform/TypedArrayUtilities.js.map +1 -1
- package/generated/protocol.d.ts +19 -2
- package/locales/af.json +229 -16
- package/locales/am.json +231 -18
- package/locales/ar.json +229 -16
- package/locales/as.json +231 -18
- package/locales/az.json +231 -18
- package/locales/be.json +228 -15
- package/locales/bg.json +229 -16
- package/locales/bn.json +229 -16
- package/locales/bs.json +229 -16
- package/locales/ca.json +229 -16
- package/locales/cs.json +228 -15
- package/locales/cy.json +231 -18
- package/locales/da.json +230 -17
- package/locales/de.json +228 -15
- package/locales/el.json +229 -16
- package/locales/en-GB.json +229 -16
- package/locales/en-US.json +30 -3
- package/locales/en-XL.json +30 -3
- package/locales/es-419.json +229 -16
- package/locales/es.json +228 -15
- package/locales/et.json +230 -17
- package/locales/eu.json +231 -18
- package/locales/fa.json +230 -17
- package/locales/fi.json +229 -16
- package/locales/fil.json +231 -18
- package/locales/fr-CA.json +230 -17
- package/locales/fr.json +230 -17
- package/locales/gl.json +228 -15
- package/locales/gu.json +230 -17
- package/locales/he.json +229 -16
- package/locales/hi.json +230 -17
- package/locales/hr.json +229 -16
- package/locales/hu.json +229 -16
- package/locales/hy.json +228 -15
- package/locales/id.json +228 -15
- package/locales/is.json +229 -16
- package/locales/it.json +232 -19
- package/locales/ja.json +228 -15
- package/locales/ka.json +230 -17
- package/locales/kk.json +230 -17
- package/locales/km.json +231 -18
- package/locales/kn.json +228 -15
- package/locales/ko.json +229 -16
- package/locales/ky.json +230 -17
- package/locales/lo.json +229 -16
- package/locales/lt.json +229 -16
- package/locales/lv.json +230 -17
- package/locales/mk.json +229 -16
- package/locales/ml.json +230 -17
- package/locales/mn.json +229 -16
- package/locales/mr.json +230 -17
- package/locales/ms.json +229 -16
- package/locales/my.json +231 -18
- package/locales/ne.json +229 -16
- package/locales/nl.json +229 -16
- package/locales/no.json +229 -16
- package/locales/or.json +231 -18
- package/locales/pa.json +228 -15
- package/locales/pl.json +230 -17
- package/locales/pt-PT.json +229 -16
- package/locales/pt.json +229 -16
- package/locales/ro.json +228 -15
- package/locales/ru.json +230 -17
- package/locales/si.json +231 -18
- package/locales/sk.json +229 -16
- package/locales/sl.json +230 -17
- package/locales/sq.json +229 -16
- package/locales/sr-Latn.json +229 -16
- package/locales/sr.json +229 -16
- package/locales/sv.json +229 -16
- package/locales/sw.json +231 -18
- package/locales/ta.json +230 -17
- package/locales/te.json +231 -18
- package/locales/th.json +229 -16
- package/locales/tr.json +230 -17
- package/locales/uk.json +229 -16
- package/locales/ur.json +230 -17
- package/locales/uz.json +229 -16
- package/locales/vi.json +230 -17
- package/locales/zh-HK.json +229 -16
- package/locales/zh-TW.json +230 -17
- package/locales/zh.json +228 -15
- package/locales/zu.json +230 -17
- package/models/trace/Processor.js +5 -3
- package/models/trace/Processor.js.map +1 -1
- package/models/trace/extras/ScriptDuplication.d.ts +27 -8
- package/models/trace/extras/ScriptDuplication.js +27 -26
- package/models/trace/extras/ScriptDuplication.js.map +1 -1
- package/models/trace/extras/StackTraceForEvent.js +7 -0
- package/models/trace/extras/StackTraceForEvent.js.map +1 -1
- package/models/trace/handlers/ExtensionTraceDataHandler.js +14 -9
- package/models/trace/handlers/ExtensionTraceDataHandler.js.map +1 -1
- package/models/trace/handlers/MetaHandler.d.ts +13 -0
- package/models/trace/handlers/MetaHandler.js +27 -0
- package/models/trace/handlers/MetaHandler.js.map +1 -1
- package/models/trace/helpers/Network.d.ts +18 -0
- package/models/trace/helpers/Network.js +57 -0
- package/models/trace/helpers/Network.js.map +1 -1
- package/models/trace/helpers/SamplesIntegrator.js +1 -1
- package/models/trace/helpers/SamplesIntegrator.js.map +1 -1
- package/models/trace/helpers/Trace.js +4 -0
- package/models/trace/helpers/Trace.js.map +1 -1
- package/models/trace/insights/DocumentLatency.d.ts +2 -1
- package/models/trace/insights/DocumentLatency.js +3 -0
- package/models/trace/insights/DocumentLatency.js.map +1 -1
- package/models/trace/insights/{DuplicateJavaScript.d.ts → DuplicatedJavaScript.d.ts} +6 -1
- package/models/trace/insights/{DuplicateJavaScript.js → DuplicatedJavaScript.js} +15 -6
- package/models/trace/insights/DuplicatedJavaScript.js.map +1 -0
- package/models/trace/insights/FontDisplay.d.ts +1 -2
- package/models/trace/insights/FontDisplay.js.map +1 -1
- package/models/trace/insights/ForcedReflow.d.ts +23 -3
- package/models/trace/insights/ForcedReflow.js +63 -107
- package/models/trace/insights/ForcedReflow.js.map +1 -1
- package/models/trace/insights/Models.d.ts +2 -1
- package/models/trace/insights/Models.js +2 -1
- package/models/trace/insights/Models.js.map +1 -1
- package/models/trace/insights/NetworkDependencyTree.d.ts +2 -1
- package/models/trace/insights/NetworkDependencyTree.js +9 -4
- package/models/trace/insights/NetworkDependencyTree.js.map +1 -1
- package/models/trace/insights/Statistics.d.ts +4 -0
- package/models/trace/insights/Statistics.js +7 -0
- package/models/trace/insights/Statistics.js.map +1 -1
- package/models/trace/insights/UseCache.d.ts +69 -0
- package/models/trace/insights/UseCache.js +189 -0
- package/models/trace/insights/UseCache.js.map +1 -0
- package/models/trace/insights/insights-tsconfig.json +2 -1
- package/models/trace/insights/types.d.ts +1 -13
- package/models/trace/insights/types.js.map +1 -1
- package/models/trace/types/TraceEvents.d.ts +16 -4
- package/models/trace/types/TraceEvents.js +4 -1
- package/models/trace/types/TraceEvents.js.map +1 -1
- package/package.json +1 -1
- package/test/test-trace-engine.mjs +2 -1
- package/models/trace/insights/DuplicateJavaScript.js.map +0 -1
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
// Copyright 2025 The Chromium Authors. All rights reserved.
|
|
2
|
+
// Use of this source code is governed by a BSD-style license that can be
|
|
3
|
+
// found in the LICENSE file.
|
|
4
|
+
// import * as i18n from '../../../core/i18n/i18n.js';
|
|
5
|
+
import * as Helpers from '../helpers/helpers.js';
|
|
6
|
+
import { metricSavingsForWastedBytes } from './Common.js';
|
|
7
|
+
import { linearInterpolation } from './Statistics.js';
|
|
8
|
+
import { InsightCategory, } from './types.js';
|
|
9
|
+
export const UIStrings = {
|
|
10
|
+
/**
|
|
11
|
+
* @description Title of an insight that provides information and suggestions of resources that could improve their caching.
|
|
12
|
+
*/
|
|
13
|
+
title: 'Use efficient cache lifetimes',
|
|
14
|
+
/**
|
|
15
|
+
* @description Text to tell the user about how caching can help improve performance.
|
|
16
|
+
*/
|
|
17
|
+
description: 'A long cache lifetime can speed up repeat visits to your page. [Learn more](https://web.dev/uses-long-cache-ttl/).',
|
|
18
|
+
/**
|
|
19
|
+
* @description Column for a font loaded by the page to render text.
|
|
20
|
+
*/
|
|
21
|
+
requestColumn: 'Request',
|
|
22
|
+
/**
|
|
23
|
+
* @description Column for a resource cache's Time To Live.
|
|
24
|
+
*/
|
|
25
|
+
cacheTTL: 'Cache TTL',
|
|
26
|
+
/**
|
|
27
|
+
* @description Text describing that there were no requests found that need caching.
|
|
28
|
+
*/
|
|
29
|
+
noRequestsToCache: 'No requests with inefficient cache policies',
|
|
30
|
+
/**
|
|
31
|
+
* @description Table row value representing the remaining items not shown in the table due to size constraints. This row will always represent at least 2 items.
|
|
32
|
+
* @example {5} PH1
|
|
33
|
+
*/
|
|
34
|
+
others: '{PH1} others',
|
|
35
|
+
};
|
|
36
|
+
// const str_ = i18n.i18n.registerUIStrings('models/trace/insights/UseCache.ts', UIStrings);
|
|
37
|
+
export const i18nString = (i18nId, values) => ({i18nId, values}); // i18n.i18n.getLocalizedString.bind(undefined, str_);
|
|
38
|
+
// Threshold for cache hits.
|
|
39
|
+
const IGNORE_THRESHOLD_IN_PERCENT = 0.925;
|
|
40
|
+
function finalize(partialModel) {
|
|
41
|
+
return {
|
|
42
|
+
insightKey: 'UseCache',
|
|
43
|
+
strings: UIStrings,
|
|
44
|
+
title: i18nString(UIStrings.title),
|
|
45
|
+
description: i18nString(UIStrings.description),
|
|
46
|
+
category: InsightCategory.ALL,
|
|
47
|
+
state: partialModel.requests.length > 0 ? 'fail' : 'pass',
|
|
48
|
+
...partialModel,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Determines if a request is "cacheable".
|
|
53
|
+
* A request is "cacheable" if it is of the appropriate protocol and resource type
|
|
54
|
+
* (see Helpers.Network.NON_NETWORK_SCHEMES and Helpers.Network.STATIC_RESOURCE_TYPE)
|
|
55
|
+
* and has the appropriate statusCodes.
|
|
56
|
+
*/
|
|
57
|
+
export function isCacheable(request) {
|
|
58
|
+
// Caching doesn't make sense for requests not loaded over the network.
|
|
59
|
+
if (Helpers.Network.NON_NETWORK_SCHEMES.includes(request.args.data.protocol)) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
return Boolean(Helpers.Network.CACHEABLE_STATUS_CODES.has(request.args.data.statusCode) &&
|
|
63
|
+
Helpers.Network.STATIC_RESOURCE_TYPES.has(request.args.data.resourceType || "Other" /* Protocol.Network.ResourceType.Other */));
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Returns max-age if defined, otherwise expires header if defined, and null if not.
|
|
67
|
+
*/
|
|
68
|
+
export function computeCacheLifetimeInSeconds(headers, cacheControl) {
|
|
69
|
+
if (cacheControl?.['max-age'] !== undefined) {
|
|
70
|
+
return cacheControl['max-age'];
|
|
71
|
+
}
|
|
72
|
+
const expiresHeaders = headers.find(h => h.name === 'expires')?.value ?? null;
|
|
73
|
+
if (expiresHeaders) {
|
|
74
|
+
const expires = new Date(expiresHeaders).getTime();
|
|
75
|
+
// Treat expires values as having already expired.
|
|
76
|
+
if (!expires) {
|
|
77
|
+
return 0;
|
|
78
|
+
}
|
|
79
|
+
return Math.ceil((expires - Date.now()) / 1000);
|
|
80
|
+
}
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Computes the percent likelihood that a return visit will be within the cache lifetime, based on
|
|
85
|
+
* Chrome UMA stats see the note below.
|
|
86
|
+
* See https://github.com/GoogleChrome/lighthouse/blob/aba818f733552189de35121907cb5625a74af640/core/audits/byte-efficiency/uses-long-cache-ttl.js
|
|
87
|
+
* TODO: This Chrome UMA stat is outdated. Using this is fine for now, but update in follow-up.
|
|
88
|
+
*/
|
|
89
|
+
function getCacheHitProbability(maxAgeInSeconds) {
|
|
90
|
+
// This array contains the hand wavy distribution of the age of a resource in hours at the time of
|
|
91
|
+
// cache hit at 0th, 10th, 20th, 30th, etc percentiles. This is used to compute `wastedMs` since there
|
|
92
|
+
// are clearly diminishing returns to cache duration i.e. 6 months is not 2x better than 3 months.
|
|
93
|
+
// Based on UMA stats for HttpCache.StaleEntry.Validated.Age, see https://www.desmos.com/calculator/7v0qh1nzvh
|
|
94
|
+
// Example: a max-age of 12 hours already covers ~50% of cases, doubling to 24 hours covers ~10% more.
|
|
95
|
+
const RESOURCE_AGE_IN_HOURS_DECILES = [0, 0.2, 1, 3, 8, 12, 24, 48, 72, 168, 8760, Infinity];
|
|
96
|
+
const maxAgeInHours = maxAgeInSeconds / 3600;
|
|
97
|
+
const upperDecileIndex = RESOURCE_AGE_IN_HOURS_DECILES.findIndex(decile => decile >= maxAgeInHours);
|
|
98
|
+
// Clip the likelihood between 0 and 1
|
|
99
|
+
if (upperDecileIndex === RESOURCE_AGE_IN_HOURS_DECILES.length - 1) {
|
|
100
|
+
return 1;
|
|
101
|
+
}
|
|
102
|
+
if (upperDecileIndex === 0) {
|
|
103
|
+
return 0;
|
|
104
|
+
}
|
|
105
|
+
// Use the two closest decile points as control points
|
|
106
|
+
const upperDecileValue = RESOURCE_AGE_IN_HOURS_DECILES[upperDecileIndex];
|
|
107
|
+
const lowerDecileValue = RESOURCE_AGE_IN_HOURS_DECILES[upperDecileIndex - 1];
|
|
108
|
+
const upperDecile = upperDecileIndex / 10;
|
|
109
|
+
const lowerDecile = (upperDecileIndex - 1) / 10;
|
|
110
|
+
// Approximate the real likelihood with linear interpolation
|
|
111
|
+
return linearInterpolation(lowerDecileValue, lowerDecile, upperDecileValue, upperDecile, maxAgeInHours);
|
|
112
|
+
}
|
|
113
|
+
export function getCombinedHeaders(responseHeaders) {
|
|
114
|
+
const headers = new Map();
|
|
115
|
+
for (const header of responseHeaders) {
|
|
116
|
+
const name = header.name.toLowerCase();
|
|
117
|
+
if (headers.get(name)) {
|
|
118
|
+
headers.set(name, `${headers.get(name)}, ${header.value}`);
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
headers.set(name, header.value);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return headers;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Returns whether a request contains headers that disable caching.
|
|
128
|
+
* Disabled caching is checked on the 'cache-control' and 'pragma' headers.
|
|
129
|
+
*/
|
|
130
|
+
export function cachingDisabled(headers, parsedCacheControl) {
|
|
131
|
+
const cacheControl = headers?.get('cache-control') ?? null;
|
|
132
|
+
const pragma = headers?.get('pragma') ?? null;
|
|
133
|
+
// The HTTP/1.0 Pragma header can disable caching if cache-control is not set, see https://tools.ietf.org/html/rfc7234#section-5.4
|
|
134
|
+
if (!cacheControl && pragma?.includes('no-cache')) {
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
// If we have any of these, the user intentionally doesn't want to cache.
|
|
138
|
+
if (parsedCacheControl &&
|
|
139
|
+
(parsedCacheControl['must-revalidate'] || parsedCacheControl['no-cache'] || parsedCacheControl['no-store'] ||
|
|
140
|
+
parsedCacheControl['private'])) {
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
export function generateInsight(parsedTrace, context) {
|
|
146
|
+
const results = [];
|
|
147
|
+
const allRequests = parsedTrace.NetworkRequests.byTime;
|
|
148
|
+
let totalWastedBytes = 0;
|
|
149
|
+
const wastedBytesByRequestId = new Map();
|
|
150
|
+
for (const req of allRequests) {
|
|
151
|
+
if (!isCacheable(req)) {
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
const headers = getCombinedHeaders(req.args.data.responseHeaders);
|
|
155
|
+
const cacheControl = headers.get('cache-control') ?? null;
|
|
156
|
+
const parsedDirectives = Helpers.Network.parseCacheControl(cacheControl);
|
|
157
|
+
// Skip requests that are deliberately avoiding caching.
|
|
158
|
+
if (cachingDisabled(headers, parsedDirectives)) {
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
let ttl = computeCacheLifetimeInSeconds(req.args.data.responseHeaders, parsedDirectives);
|
|
162
|
+
// Ignore if a non-positive number.
|
|
163
|
+
if (ttl !== null && (!Number.isFinite(ttl) || ttl <= 0)) {
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
ttl = ttl || 0;
|
|
167
|
+
// If cache lifetime is high enough, let's skip.
|
|
168
|
+
const cacheHitProbability = getCacheHitProbability(ttl);
|
|
169
|
+
if (cacheHitProbability > IGNORE_THRESHOLD_IN_PERCENT) {
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
const transferSize = req.args.data.encodedDataLength || 0;
|
|
173
|
+
const wastedBytes = (1 - cacheHitProbability) * transferSize;
|
|
174
|
+
wastedBytesByRequestId.set(req.args.data.requestId, wastedBytes);
|
|
175
|
+
totalWastedBytes += wastedBytes;
|
|
176
|
+
results.push({ request: req, ttl, wastedBytes });
|
|
177
|
+
}
|
|
178
|
+
// Sort by transfer size.
|
|
179
|
+
results.sort((a, b) => {
|
|
180
|
+
return b.request.args.data.decodedBodyLength - a.request.args.data.decodedBodyLength || a.ttl - b.ttl;
|
|
181
|
+
});
|
|
182
|
+
return finalize({
|
|
183
|
+
relatedEvents: results.map(r => r.request),
|
|
184
|
+
requests: results,
|
|
185
|
+
metricSavings: metricSavingsForWastedBytes(wastedBytesByRequestId, context),
|
|
186
|
+
totalWastedBytes,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
//# sourceMappingURL=UseCache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UseCache.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/insights/UseCache.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2025 The Chromium Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\nimport * as i18n from '../../../core/i18n/i18n.js';\nimport * as Protocol from '../../../generated/protocol.js';\nimport type * as Handlers from '../handlers/handlers.js';\nimport * as Helpers from '../helpers/helpers.js';\nimport type * as Types from '../types/types.js';\n\nimport {metricSavingsForWastedBytes} from './Common.js';\nimport {linearInterpolation} from './Statistics.js';\nimport {\n InsightCategory,\n type InsightModel,\n type InsightSetContext,\n type PartialInsightModel,\n} from './types.js';\n\nexport const UIStrings = {\n /**\n * @description Title of an insight that provides information and suggestions of resources that could improve their caching.\n */\n title: 'Use efficient cache lifetimes',\n /**\n * @description Text to tell the user about how caching can help improve performance.\n */\n description:\n 'A long cache lifetime can speed up repeat visits to your page. [Learn more](https://web.dev/uses-long-cache-ttl/).',\n /**\n * @description Column for a font loaded by the page to render text.\n */\n requestColumn: 'Request',\n /**\n * @description Column for a resource cache's Time To Live.\n */\n cacheTTL: 'Cache TTL',\n /**\n * @description Text describing that there were no requests found that need caching.\n */\n noRequestsToCache: 'No requests with inefficient cache policies',\n /**\n * @description Table row value representing the remaining items not shown in the table due to size constraints. This row will always represent at least 2 items.\n * @example {5} PH1\n */\n others: '{PH1} others',\n} as const;\n\nconst str_ = i18n.i18n.registerUIStrings('models/trace/insights/UseCache.ts', UIStrings);\nexport const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);\n\nexport type UseCacheInsightModel = InsightModel<typeof UIStrings, {\n requests: Array<{\n request: Types.Events.SyntheticNetworkRequest,\n ttl: number,\n wastedBytes: number,\n }>,\n totalWastedBytes: number,\n}>;\n\n// Threshold for cache hits.\nconst IGNORE_THRESHOLD_IN_PERCENT = 0.925;\n\nfunction finalize(partialModel: PartialInsightModel<UseCacheInsightModel>): UseCacheInsightModel {\n return {\n insightKey: 'UseCache',\n strings: UIStrings,\n title: i18nString(UIStrings.title),\n description: i18nString(UIStrings.description),\n category: InsightCategory.ALL,\n state: partialModel.requests.length > 0 ? 'fail' : 'pass',\n ...partialModel,\n };\n}\n\n/**\n * Determines if a request is \"cacheable\".\n * A request is \"cacheable\" if it is of the appropriate protocol and resource type\n * (see Helpers.Network.NON_NETWORK_SCHEMES and Helpers.Network.STATIC_RESOURCE_TYPE)\n * and has the appropriate statusCodes.\n */\nexport function isCacheable(request: Types.Events.SyntheticNetworkRequest): boolean {\n // Caching doesn't make sense for requests not loaded over the network.\n if (Helpers.Network.NON_NETWORK_SCHEMES.includes(request.args.data.protocol)) {\n return false;\n }\n return Boolean(\n Helpers.Network.CACHEABLE_STATUS_CODES.has(request.args.data.statusCode) &&\n Helpers.Network.STATIC_RESOURCE_TYPES.has(request.args.data.resourceType || Protocol.Network.ResourceType.Other));\n}\n\n/**\n * Returns max-age if defined, otherwise expires header if defined, and null if not.\n */\nexport function computeCacheLifetimeInSeconds(\n headers: Array<{name: string, value: string}>, cacheControl: Helpers.Network.CacheControl|null): number|null {\n if (cacheControl?.['max-age'] !== undefined) {\n return cacheControl['max-age'];\n }\n\n const expiresHeaders = headers.find(h => h.name === 'expires')?.value ?? null;\n if (expiresHeaders) {\n const expires = new Date(expiresHeaders).getTime();\n // Treat expires values as having already expired.\n if (!expires) {\n return 0;\n }\n return Math.ceil((expires - Date.now()) / 1000);\n }\n return null;\n}\n\n/**\n * Computes the percent likelihood that a return visit will be within the cache lifetime, based on\n * Chrome UMA stats see the note below.\n * See https://github.com/GoogleChrome/lighthouse/blob/aba818f733552189de35121907cb5625a74af640/core/audits/byte-efficiency/uses-long-cache-ttl.js\n * TODO: This Chrome UMA stat is outdated. Using this is fine for now, but update in follow-up.\n */\nfunction getCacheHitProbability(maxAgeInSeconds: number): number {\n // This array contains the hand wavy distribution of the age of a resource in hours at the time of\n // cache hit at 0th, 10th, 20th, 30th, etc percentiles. This is used to compute `wastedMs` since there\n // are clearly diminishing returns to cache duration i.e. 6 months is not 2x better than 3 months.\n // Based on UMA stats for HttpCache.StaleEntry.Validated.Age, see https://www.desmos.com/calculator/7v0qh1nzvh\n // Example: a max-age of 12 hours already covers ~50% of cases, doubling to 24 hours covers ~10% more.\n const RESOURCE_AGE_IN_HOURS_DECILES = [0, 0.2, 1, 3, 8, 12, 24, 48, 72, 168, 8760, Infinity];\n\n const maxAgeInHours = maxAgeInSeconds / 3600;\n const upperDecileIndex = RESOURCE_AGE_IN_HOURS_DECILES.findIndex(decile => decile >= maxAgeInHours);\n\n // Clip the likelihood between 0 and 1\n if (upperDecileIndex === RESOURCE_AGE_IN_HOURS_DECILES.length - 1) {\n return 1;\n }\n if (upperDecileIndex === 0) {\n return 0;\n }\n\n // Use the two closest decile points as control points\n const upperDecileValue = RESOURCE_AGE_IN_HOURS_DECILES[upperDecileIndex];\n const lowerDecileValue = RESOURCE_AGE_IN_HOURS_DECILES[upperDecileIndex - 1];\n const upperDecile = upperDecileIndex / 10;\n const lowerDecile = (upperDecileIndex - 1) / 10;\n\n // Approximate the real likelihood with linear interpolation\n return linearInterpolation(lowerDecileValue, lowerDecile, upperDecileValue, upperDecile, maxAgeInHours);\n}\n\nexport function getCombinedHeaders(responseHeaders: Array<{name: string, value: string}>): Map<string, string> {\n const headers = new Map<string, string>();\n for (const header of responseHeaders) {\n const name = header.name.toLowerCase();\n if (headers.get(name)) {\n headers.set(name, `${headers.get(name)}, ${header.value}`);\n } else {\n headers.set(name, header.value);\n }\n }\n return headers;\n}\n\n/**\n * Returns whether a request contains headers that disable caching.\n * Disabled caching is checked on the 'cache-control' and 'pragma' headers.\n */\nexport function cachingDisabled(\n headers: Map<string, string>|null, parsedCacheControl: Helpers.Network.CacheControl|null): boolean {\n const cacheControl = headers?.get('cache-control') ?? null;\n const pragma = headers?.get('pragma') ?? null;\n\n // The HTTP/1.0 Pragma header can disable caching if cache-control is not set, see https://tools.ietf.org/html/rfc7234#section-5.4\n if (!cacheControl && pragma?.includes('no-cache')) {\n return true;\n }\n\n // If we have any of these, the user intentionally doesn't want to cache.\n if (parsedCacheControl &&\n (parsedCacheControl['must-revalidate'] || parsedCacheControl['no-cache'] || parsedCacheControl['no-store'] ||\n parsedCacheControl['private'])) {\n return true;\n }\n\n return false;\n}\n\nexport interface CacheableRequest {\n request: Types.Events.SyntheticNetworkRequest;\n ttl: number;\n wastedBytes: number;\n}\n\nexport function generateInsight(\n parsedTrace: Handlers.Types.ParsedTrace, context: InsightSetContext): UseCacheInsightModel {\n const results: CacheableRequest[] = [];\n const allRequests = parsedTrace.NetworkRequests.byTime;\n let totalWastedBytes = 0;\n const wastedBytesByRequestId = new Map<string, number>();\n for (const req of allRequests) {\n if (!isCacheable(req)) {\n continue;\n }\n\n const headers = getCombinedHeaders(req.args.data.responseHeaders);\n const cacheControl = headers.get('cache-control') ?? null;\n const parsedDirectives = Helpers.Network.parseCacheControl(cacheControl);\n\n // Skip requests that are deliberately avoiding caching.\n if (cachingDisabled(headers, parsedDirectives)) {\n continue;\n }\n\n let ttl = computeCacheLifetimeInSeconds(req.args.data.responseHeaders, parsedDirectives);\n // Ignore if a non-positive number.\n if (ttl !== null && (!Number.isFinite(ttl) || ttl <= 0)) {\n continue;\n }\n ttl = ttl || 0;\n\n // If cache lifetime is high enough, let's skip.\n const cacheHitProbability = getCacheHitProbability(ttl);\n if (cacheHitProbability > IGNORE_THRESHOLD_IN_PERCENT) {\n continue;\n }\n\n const transferSize = req.args.data.encodedDataLength || 0;\n const wastedBytes = (1 - cacheHitProbability) * transferSize;\n\n wastedBytesByRequestId.set(req.args.data.requestId, wastedBytes);\n totalWastedBytes += wastedBytes;\n\n results.push({request: req, ttl, wastedBytes});\n }\n\n // Sort by transfer size.\n results.sort((a, b) => {\n return b.request.args.data.decodedBodyLength - a.request.args.data.decodedBodyLength || a.ttl - b.ttl;\n });\n\n return finalize({\n relatedEvents: results.map(r => r.request),\n requests: results,\n metricSavings: metricSavingsForWastedBytes(wastedBytesByRequestId, context),\n totalWastedBytes,\n });\n}\n"]}
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"../../../../../../../front_end/models/trace/insights/Common.ts",
|
|
34
34
|
"../../../../../../../front_end/models/trace/insights/DOMSize.ts",
|
|
35
35
|
"../../../../../../../front_end/models/trace/insights/DocumentLatency.ts",
|
|
36
|
-
"../../../../../../../front_end/models/trace/insights/
|
|
36
|
+
"../../../../../../../front_end/models/trace/insights/DuplicatedJavaScript.ts",
|
|
37
37
|
"../../../../../../../front_end/models/trace/insights/FontDisplay.ts",
|
|
38
38
|
"../../../../../../../front_end/models/trace/insights/ForcedReflow.ts",
|
|
39
39
|
"../../../../../../../front_end/models/trace/insights/ImageDelivery.ts",
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
"../../../../../../../front_end/models/trace/insights/SlowCSSSelector.ts",
|
|
47
47
|
"../../../../../../../front_end/models/trace/insights/Statistics.ts",
|
|
48
48
|
"../../../../../../../front_end/models/trace/insights/ThirdParties.ts",
|
|
49
|
+
"../../../../../../../front_end/models/trace/insights/UseCache.ts",
|
|
49
50
|
"../../../../../../../front_end/models/trace/insights/Viewport.ts",
|
|
50
51
|
"../../../../../../../front_end/models/trace/insights/types.ts",
|
|
51
52
|
"../../../../../../../front_end/legacy/legacy-defs.d.ts",
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
// import type * as Common from '../../../core/common/common.js';
|
|
2
|
-
import type * as Protocol from '../../../generated/protocol.js';
|
|
3
2
|
import type * as Lantern from '../lantern/lantern.js';
|
|
4
3
|
import type * as Types from '../types/types.js';
|
|
5
4
|
import type * as Models from './Models.js';
|
|
@@ -24,17 +23,6 @@ export interface LanternContext {
|
|
|
24
23
|
simulator: Lantern.Simulation.Simulator<Types.Events.SyntheticNetworkRequest>;
|
|
25
24
|
metrics: Record<string, Lantern.Metrics.MetricResult>;
|
|
26
25
|
}
|
|
27
|
-
export interface ForcedReflowAggregatedData {
|
|
28
|
-
topLevelFunctionCall: Types.Events.CallFrame | Protocol.Runtime.CallFrame;
|
|
29
|
-
totalReflowTime: number;
|
|
30
|
-
bottomUpData: Set<string>;
|
|
31
|
-
topLevelFunctionCallEvents: Types.Events.Event[];
|
|
32
|
-
}
|
|
33
|
-
export interface BottomUpCallStack {
|
|
34
|
-
bottomUpData: Types.Events.CallFrame | Protocol.Runtime.CallFrame;
|
|
35
|
-
totalTime: number;
|
|
36
|
-
relatedEvents: Types.Events.Event[];
|
|
37
|
-
}
|
|
38
26
|
export type InsightModelsType = typeof Models;
|
|
39
27
|
export declare enum InsightWarning {
|
|
40
28
|
NO_FP = "NO_FP",
|
|
@@ -114,7 +102,7 @@ export declare const enum InsightKeys {
|
|
|
114
102
|
THIRD_PARTIES = "ThirdParties",
|
|
115
103
|
DOCUMENT_LATENCY = "DocumentLatency",
|
|
116
104
|
DOM_SIZE = "DOMSize",
|
|
117
|
-
DUPLICATE_JAVASCRIPT = "
|
|
105
|
+
DUPLICATE_JAVASCRIPT = "DuplicatedJavaScript",
|
|
118
106
|
FONT_DISPLAY = "FontDisplay",
|
|
119
107
|
FORCED_REFLOW = "ForcedReflow",
|
|
120
108
|
IMAGE_DELIVERY = "ImageDelivery",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/insights/types.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,yEAAyE;AACzE,6BAA6B;
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/insights/types.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,yEAAyE;AACzE,6BAA6B;AAmC7B,MAAM,CAAN,IAAY,cAMX;AAND,WAAY,cAAc;IACxB,iCAAe,CAAA;IACf,mCAAiB,CAAA;IACjB,uEAAuE;IACvE,6DAA2C,CAAA;IAC3C,yCAAuB,CAAA;AACzB,CAAC,EANW,cAAc,KAAd,cAAc,QAMzB;AAYD,MAAM,CAAN,IAAY,eAKX;AALD,WAAY,eAAe;IACzB,8BAAW,CAAA;IACX,8BAAW,CAAA;IACX,8BAAW,CAAA;IACX,8BAAW,CAAA;AACb,CAAC,EALW,eAAe,KAAf,eAAe,QAK1B","sourcesContent":["// Copyright 2024 The Chromium Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\nimport type * as Common from '../../../core/common/common.js';\nimport type * as Lantern from '../lantern/lantern.js';\nimport type * as Types from '../types/types.js';\n\nimport type * as Models from './Models.js';\n\n/**\n * Context for the portion of the trace an insight should look at.\n */\nexport type InsightSetContext = InsightSetContextWithoutNavigation|InsightSetContextWithNavigation;\n\nexport interface InsightSetContextWithoutNavigation {\n bounds: Types.Timing.TraceWindowMicro;\n frameId: string;\n navigation?: never;\n}\n\nexport interface InsightSetContextWithNavigation {\n bounds: Types.Timing.TraceWindowMicro;\n frameId: string;\n navigation: Types.Events.NavigationStart;\n navigationId: string;\n lantern?: LanternContext;\n}\n\nexport interface LanternContext {\n graph: Lantern.Graph.Node<Types.Events.SyntheticNetworkRequest>;\n simulator: Lantern.Simulation.Simulator<Types.Events.SyntheticNetworkRequest>;\n metrics: Record<string, Lantern.Metrics.MetricResult>;\n}\n\nexport type InsightModelsType = typeof Models;\n\nexport enum InsightWarning {\n NO_FP = 'NO_FP',\n NO_LCP = 'NO_LCP',\n // No network request could be identified as the primary HTML document.\n NO_DOCUMENT_REQUEST = 'NO_DOCUMENT_REQUEST',\n NO_LAYOUT = 'NO_LAYOUT',\n}\n\nexport interface MetricSavings {\n /* eslint-disable @typescript-eslint/naming-convention */\n FCP?: Types.Timing.Milli;\n LCP?: Types.Timing.Milli;\n TBT?: Types.Timing.Milli;\n CLS?: number;\n INP?: Types.Timing.Milli;\n /* eslint-enable @typescript-eslint/naming-convention */\n}\n\nexport enum InsightCategory {\n ALL = 'All',\n INP = 'INP',\n LCP = 'LCP',\n CLS = 'CLS',\n}\n\nexport type RelatedEventsMap = Map<Types.Events.Event, string[]>;\n\nexport type Checklist<Keys extends string> = Record<Keys, {label: Common.UIString.LocalizedString, value: boolean}>;\n\nexport type InsightModel<UIStrings extends Record<string, string> = Record<string, string>,\n ExtraDetail extends Record<string, unknown> = Record<string, unknown>> =\n ExtraDetail&{\n /** Used internally to identify the type of a model, not shown visibly to users **/\n insightKey: keyof InsightModelsType,\n /** Not used within DevTools - this is for external consumers (like Lighthouse). */\n strings: UIStrings,\n title: Common.UIString.LocalizedString,\n description: Common.UIString.LocalizedString,\n category: InsightCategory,\n state: 'pass' | 'fail' | 'informative',\n relatedEvents?: RelatedEventsMap | Types.Events.Event[],\n warnings?: InsightWarning[],\n metricSavings?: MetricSavings,\n frameId?: string,\n /**\n * If this insight is attached to a navigation, this stores its ID.\n */\n navigationId?: string,\n };\n\nexport type PartialInsightModel<T> =\n Omit<T, 'strings'|'title'|'description'|'category'|'state'|'insightKey'|'navigationId'|'frameId'>;\n\n/**\n * Contains insights for a specific navigation. If a trace began after a navigation already started,\n * this could instead represent the duration from the beginning of the trace up to the first recorded\n * navigation (or the end of the trace).\n */\nexport interface InsightSet {\n /** If for a navigation, this is the navigationId. Else it is Trace.Types.Events.NO_NAVIGATION. */\n id: Types.Events.NavigationId;\n /** The URL to show in the accordion list. */\n url: URL;\n frameId: string;\n bounds: Types.Timing.TraceWindowMicro;\n model: InsightModels;\n navigation?: Types.Events.NavigationStart;\n}\n\n/**\n * Contains insights for a specific insight set.\n */\nexport type InsightModels = {\n [I in keyof InsightModelsType]: ReturnType<InsightModelsType[I]['generateInsight']>;\n};\n\n/**\n * Contains insights for the entire trace. Insights are mostly grouped by `navigationId`, with one exception:\n *\n * If the analyzed trace started after the navigation, and has meaningful work with that span, there is no\n * navigation to map it to. In this case `Types.Events.NO_NAVIGATION` is used for the key.\n */\nexport type TraceInsightSets = Map<Types.Events.NavigationId, InsightSet>;\n\nexport const enum InsightKeys {\n LCP_PHASES = 'LCPPhases',\n INTERACTION_TO_NEXT_PAINT = 'InteractionToNextPaint',\n CLS_CULPRITS = 'CLSCulprits',\n THIRD_PARTIES = 'ThirdParties',\n DOCUMENT_LATENCY = 'DocumentLatency',\n DOM_SIZE = 'DOMSize',\n DUPLICATE_JAVASCRIPT = 'DuplicatedJavaScript',\n FONT_DISPLAY = 'FontDisplay',\n FORCED_REFLOW = 'ForcedReflow',\n IMAGE_DELIVERY = 'ImageDelivery',\n LCP_DISCOVERY = 'LCPDiscovery',\n NETWORK_DEPENDENCY_TREE = 'NetworkDependencyTree',\n RENDER_BLOCKING = 'RenderBlocking',\n SLOW_CSS_SELECTOR = 'SlowCSSSelector',\n VIEWPORT = 'Viewport',\n}\n"]}
|
|
@@ -1130,12 +1130,13 @@ export interface ConsoleTimeEnd extends PairableAsyncEnd {
|
|
|
1130
1130
|
}
|
|
1131
1131
|
export type ConsoleTime = ConsoleTimeBegin | ConsoleTimeEnd;
|
|
1132
1132
|
export interface ConsoleTimeStamp extends Event {
|
|
1133
|
-
cat: '
|
|
1133
|
+
cat: 'devtools.timeline';
|
|
1134
1134
|
name: Name.CONSOLE_TIME_STAMP;
|
|
1135
|
-
ph: Phase.
|
|
1135
|
+
ph: Phase.INSTANT;
|
|
1136
1136
|
args: Args & {
|
|
1137
1137
|
data?: ArgsData & {
|
|
1138
|
-
|
|
1138
|
+
message: string;
|
|
1139
|
+
name?: string | number;
|
|
1139
1140
|
start?: string | number;
|
|
1140
1141
|
end?: string | number;
|
|
1141
1142
|
track?: string | number;
|
|
@@ -1497,6 +1498,7 @@ export interface Layout extends Complete {
|
|
|
1497
1498
|
dirtyObjects: number;
|
|
1498
1499
|
partialLayout: boolean;
|
|
1499
1500
|
totalObjects: number;
|
|
1501
|
+
stackTrace?: CallFrame[];
|
|
1500
1502
|
};
|
|
1501
1503
|
endData?: {
|
|
1502
1504
|
layoutRoots: Array<{
|
|
@@ -1608,6 +1610,16 @@ export declare function isNetworkTrackEntry(event: Event): event is SyntheticWeb
|
|
|
1608
1610
|
export declare function isPrePaint(event: Event): event is PrePaint;
|
|
1609
1611
|
/** A VALID navigation start (as it has a populated documentLoaderURL) */
|
|
1610
1612
|
export declare function isNavigationStart(event: Event): event is NavigationStart;
|
|
1613
|
+
export interface DidCommitSameDocumentNavigation extends Complete {
|
|
1614
|
+
name: 'RenderFrameHostImpl::DidCommitSameDocumentNavigation';
|
|
1615
|
+
args: Args & {
|
|
1616
|
+
url: string;
|
|
1617
|
+
render_frame_host: {
|
|
1618
|
+
frame_type: string;
|
|
1619
|
+
};
|
|
1620
|
+
};
|
|
1621
|
+
}
|
|
1622
|
+
export declare function isDidCommitSameDocumentNavigation(event: Event): event is DidCommitSameDocumentNavigation;
|
|
1611
1623
|
export declare function isMainFrameViewport(event: Event): event is MainFrameViewport;
|
|
1612
1624
|
export declare function isSyntheticUserTiming(event: Event): event is SyntheticUserTimingPair;
|
|
1613
1625
|
export declare function isSyntheticConsoleTiming(event: Event): event is SyntheticConsoleTimingPair;
|
|
@@ -2123,7 +2135,7 @@ export declare const enum Name {
|
|
|
2123
2135
|
CONSOLE_TIME = "ConsoleTime",
|
|
2124
2136
|
USER_TIMING = "UserTiming",
|
|
2125
2137
|
INTERACTIVE_TIME = "InteractiveTime",
|
|
2126
|
-
CONSOLE_TIME_STAMP = "
|
|
2138
|
+
CONSOLE_TIME_STAMP = "TimeStamp",
|
|
2127
2139
|
BEGIN_FRAME = "BeginFrame",
|
|
2128
2140
|
NEEDS_BEGIN_FRAME_CHANGED = "NeedsBeginFrameChanged",
|
|
2129
2141
|
BEGIN_MAIN_THREAD_FRAME = "BeginMainThreadFrame",
|
|
@@ -374,6 +374,9 @@ export function isPrePaint(event) {
|
|
|
374
374
|
export function isNavigationStart(event) {
|
|
375
375
|
return event.name === 'navigationStart' && event.args?.data?.documentLoaderURL !== '';
|
|
376
376
|
}
|
|
377
|
+
export function isDidCommitSameDocumentNavigation(event) {
|
|
378
|
+
return event.name === 'RenderFrameHostImpl::DidCommitSameDocumentNavigation';
|
|
379
|
+
}
|
|
377
380
|
export function isMainFrameViewport(event) {
|
|
378
381
|
return event.name === 'PaintTimingVisualizer::Viewport';
|
|
379
382
|
}
|
|
@@ -422,7 +425,7 @@ export function isConsoleTime(event) {
|
|
|
422
425
|
return event.cat === 'blink.console' && isPhaseAsync(event.ph);
|
|
423
426
|
}
|
|
424
427
|
export function isConsoleTimeStamp(event) {
|
|
425
|
-
return event.ph === "
|
|
428
|
+
return event.ph === "I" /* Phase.INSTANT */ && event.name === "TimeStamp" /* Name.CONSOLE_TIME_STAMP */;
|
|
426
429
|
}
|
|
427
430
|
export function isUserTimingMeasure(event) {
|
|
428
431
|
return event.name === "UserTiming::Measure" /* Name.USER_TIMING_MEASURE */;
|