@paulirish/trace_engine 0.0.60 → 0.0.62

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 (302) hide show
  1. package/.tmp/tsbuildinfo/analyze-trace.d.mts.map +1 -1
  2. package/.tmp/tsbuildinfo/tsconfig.tsbuildinfo +1 -1
  3. package/analyze-inspector-issues.mjs +1 -1
  4. package/analyze-trace.mjs +1 -2
  5. package/core/platform/ArrayUtilities.d.ts +2 -0
  6. package/core/platform/ArrayUtilities.js +11 -1
  7. package/core/platform/ArrayUtilities.js.map +1 -1
  8. package/core/platform/HostRuntime.d.ts +4 -0
  9. package/core/platform/HostRuntime.js +25 -0
  10. package/core/platform/HostRuntime.js.map +1 -0
  11. package/core/platform/KeyboardUtilities.d.ts +6 -2
  12. package/core/platform/KeyboardUtilities.js.map +1 -1
  13. package/core/platform/StringUtilities.d.ts +2 -0
  14. package/core/platform/StringUtilities.js +64 -13
  15. package/core/platform/StringUtilities.js.map +1 -1
  16. package/core/platform/api/HostRuntime.d.ts +36 -0
  17. package/core/platform/api/HostRuntime.js +5 -0
  18. package/core/platform/api/HostRuntime.js.map +1 -0
  19. package/core/platform/api/api-tsconfig.json +43 -0
  20. package/core/platform/api/api.d.ts +2 -0
  21. package/core/platform/api/api.js +6 -0
  22. package/core/platform/api/api.js.map +1 -0
  23. package/core/platform/api/api_node_typecheck-tsconfig.json +48 -0
  24. package/core/platform/api/bundle-tsconfig.json +1 -0
  25. package/core/platform/api/devtools_entrypoint-bundle-typescript-tsconfig.json +48 -0
  26. package/core/platform/browser/HostRuntime.d.ts +2 -0
  27. package/core/platform/browser/HostRuntime.js +64 -0
  28. package/core/platform/browser/HostRuntime.js.map +1 -0
  29. package/core/platform/browser/browser-tsconfig.json +48 -0
  30. package/core/platform/browser/browser.d.ts +2 -0
  31. package/core/platform/browser/browser.js +6 -0
  32. package/core/platform/browser/browser.js.map +1 -0
  33. package/core/platform/browser/bundle-tsconfig.json +1 -0
  34. package/core/platform/browser/devtools_entrypoint-bundle-typescript-tsconfig.json +48 -0
  35. package/core/platform/devtools_entrypoint-bundle-typescript-tsconfig.json +4 -3
  36. package/core/platform/node/HostRuntime.d.ts +2 -0
  37. package/core/platform/node/HostRuntime.js +73 -0
  38. package/core/platform/node/HostRuntime.js.map +1 -0
  39. package/core/platform/node/bundle-tsconfig.json +1 -0
  40. package/core/platform/node/devtools_entrypoint-bundle-typescript-tsconfig.json +48 -0
  41. package/core/platform/node/node-tsconfig.json +52 -0
  42. package/core/platform/node/node.d.ts +2 -0
  43. package/core/platform/node/node.js +6 -0
  44. package/core/platform/node/node.js.map +1 -0
  45. package/core/platform/platform-tsconfig.json +17 -5
  46. package/core/platform/platform.d.ts +2 -2
  47. package/core/platform/platform.js +2 -2
  48. package/core/platform/platform.js.map +1 -1
  49. package/core/platform/platform_node_typecheck-tsconfig.json +74 -0
  50. package/generated/protocol.d.ts +1517 -592
  51. package/locales/af.json +83 -38
  52. package/locales/am.json +83 -38
  53. package/locales/ar.json +86 -41
  54. package/locales/as.json +83 -38
  55. package/locales/az.json +83 -38
  56. package/locales/be.json +81 -36
  57. package/locales/bg.json +81 -36
  58. package/locales/bn.json +83 -38
  59. package/locales/bs.json +82 -37
  60. package/locales/ca.json +81 -36
  61. package/locales/cs.json +81 -36
  62. package/locales/cy.json +83 -38
  63. package/locales/da.json +83 -38
  64. package/locales/de.json +81 -36
  65. package/locales/el.json +82 -37
  66. package/locales/en-GB.json +83 -38
  67. package/locales/en-US.json +104 -35
  68. package/locales/en-XL.json +104 -35
  69. package/locales/es-419.json +81 -36
  70. package/locales/es.json +82 -37
  71. package/locales/et.json +83 -38
  72. package/locales/eu.json +81 -36
  73. package/locales/fa.json +84 -39
  74. package/locales/fi.json +81 -36
  75. package/locales/fil.json +83 -38
  76. package/locales/fr-CA.json +82 -37
  77. package/locales/fr.json +168 -123
  78. package/locales/gl.json +82 -37
  79. package/locales/gu.json +82 -37
  80. package/locales/he.json +96 -51
  81. package/locales/hi.json +82 -37
  82. package/locales/hr.json +81 -36
  83. package/locales/hu.json +82 -37
  84. package/locales/hy.json +82 -37
  85. package/locales/id.json +83 -38
  86. package/locales/is.json +82 -37
  87. package/locales/it.json +81 -36
  88. package/locales/ja.json +81 -36
  89. package/locales/ka.json +83 -38
  90. package/locales/kk.json +81 -36
  91. package/locales/km.json +83 -38
  92. package/locales/kn.json +82 -37
  93. package/locales/ko.json +82 -37
  94. package/locales/ky.json +81 -36
  95. package/locales/lo.json +82 -37
  96. package/locales/lt.json +81 -36
  97. package/locales/lv.json +81 -36
  98. package/locales/mk.json +84 -39
  99. package/locales/ml.json +82 -37
  100. package/locales/mn.json +82 -37
  101. package/locales/mr.json +83 -38
  102. package/locales/ms.json +81 -36
  103. package/locales/my.json +82 -37
  104. package/locales/ne.json +82 -37
  105. package/locales/nl.json +82 -37
  106. package/locales/no.json +81 -36
  107. package/locales/or.json +81 -36
  108. package/locales/pa.json +81 -36
  109. package/locales/pl.json +81 -36
  110. package/locales/pt-PT.json +81 -36
  111. package/locales/pt.json +83 -38
  112. package/locales/ro.json +82 -37
  113. package/locales/ru.json +81 -36
  114. package/locales/si.json +81 -36
  115. package/locales/sk.json +81 -36
  116. package/locales/sl.json +81 -36
  117. package/locales/sq.json +82 -37
  118. package/locales/sr-Latn.json +82 -37
  119. package/locales/sr.json +82 -37
  120. package/locales/sv.json +84 -39
  121. package/locales/sw.json +83 -38
  122. package/locales/ta.json +82 -37
  123. package/locales/te.json +82 -37
  124. package/locales/th.json +82 -37
  125. package/locales/tr.json +81 -36
  126. package/locales/uk.json +81 -36
  127. package/locales/ur.json +82 -37
  128. package/locales/uz.json +82 -37
  129. package/locales/vi.json +83 -38
  130. package/locales/zh-HK.json +81 -36
  131. package/locales/zh-TW.json +84 -39
  132. package/locales/zh.json +84 -39
  133. package/locales/zu.json +82 -37
  134. package/models/cpu_profile/CPUProfileDataModel.d.ts +9 -0
  135. package/models/cpu_profile/CPUProfileDataModel.js +9 -7
  136. package/models/cpu_profile/CPUProfileDataModel.js.map +1 -1
  137. package/models/cpu_profile/ProfileTreeModel.d.ts +3 -2
  138. package/models/cpu_profile/ProfileTreeModel.js +6 -7
  139. package/models/cpu_profile/ProfileTreeModel.js.map +1 -1
  140. package/models/cpu_profile/cpu_profile-tsconfig.json +4 -3
  141. package/models/cpu_profile/devtools_entrypoint-bundle-typescript-tsconfig.json +4 -3
  142. package/models/trace/EntityMapper.d.ts +1 -0
  143. package/models/trace/EntityMapper.js +10 -0
  144. package/models/trace/EntityMapper.js.map +1 -1
  145. package/models/trace/EventsSerializer.js +10 -2
  146. package/models/trace/EventsSerializer.js.map +1 -1
  147. package/models/trace/LanternComputationData.d.ts +1 -1
  148. package/models/trace/LanternComputationData.js +3 -8
  149. package/models/trace/LanternComputationData.js.map +1 -1
  150. package/models/trace/ModelImpl.d.ts +1 -0
  151. package/models/trace/ModelImpl.js +9 -6
  152. package/models/trace/ModelImpl.js.map +1 -1
  153. package/models/trace/Processor.js +23 -23
  154. package/models/trace/Processor.js.map +1 -1
  155. package/models/trace/Styles.js +13 -5
  156. package/models/trace/Styles.js.map +1 -1
  157. package/models/trace/devtools_entrypoint-bundle-typescript-tsconfig.json +4 -3
  158. package/models/trace/extras/Initiators.d.ts +12 -0
  159. package/models/trace/extras/Initiators.js +47 -0
  160. package/models/trace/extras/Initiators.js.map +1 -0
  161. package/models/trace/extras/TraceTree.js +13 -5
  162. package/models/trace/extras/TraceTree.js.map +1 -1
  163. package/models/trace/extras/devtools_entrypoint-bundle-typescript-tsconfig.json +4 -3
  164. package/models/trace/extras/extras-tsconfig.json +11 -3
  165. package/models/trace/extras/extras.d.ts +0 -3979
  166. package/models/trace/extras/extras.js +0 -3979
  167. package/models/trace/extras/extras.js.map +1 -1
  168. package/models/trace/handlers/LargestImagePaintHandler.js +2 -2
  169. package/models/trace/handlers/LargestImagePaintHandler.js.map +1 -1
  170. package/models/trace/handlers/LayoutShiftsHandler.js +1 -1
  171. package/models/trace/handlers/LayoutShiftsHandler.js.map +1 -1
  172. package/models/trace/handlers/MetaHandler.d.ts +12 -1
  173. package/models/trace/handlers/MetaHandler.js +10 -1
  174. package/models/trace/handlers/MetaHandler.js.map +1 -1
  175. package/models/trace/handlers/NetworkRequestsHandler.d.ts +8 -1
  176. package/models/trace/handlers/NetworkRequestsHandler.js +22 -4
  177. package/models/trace/handlers/NetworkRequestsHandler.js.map +1 -1
  178. package/models/trace/handlers/PageLoadMetricsHandler.d.ts +13 -3
  179. package/models/trace/handlers/PageLoadMetricsHandler.js +71 -27
  180. package/models/trace/handlers/PageLoadMetricsHandler.js.map +1 -1
  181. package/models/trace/handlers/SamplesHandler.js +59 -6
  182. package/models/trace/handlers/SamplesHandler.js.map +1 -1
  183. package/models/trace/handlers/ScriptsHandler.js +24 -0
  184. package/models/trace/handlers/ScriptsHandler.js.map +1 -1
  185. package/models/trace/handlers/UserTimingsHandler.d.ts +1 -1
  186. package/models/trace/handlers/UserTimingsHandler.js +1 -1
  187. package/models/trace/handlers/UserTimingsHandler.js.map +1 -1
  188. package/models/trace/handlers/devtools_entrypoint-bundle-typescript-tsconfig.json +4 -3
  189. package/models/trace/handlers/handlers-tsconfig.json +13 -3
  190. package/models/trace/helpers/Network.js +1 -1
  191. package/models/trace/helpers/Network.js.map +1 -1
  192. package/models/trace/helpers/SamplesIntegrator.js +1 -8
  193. package/models/trace/helpers/SamplesIntegrator.js.map +1 -1
  194. package/models/trace/helpers/Timing.d.ts +1 -1
  195. package/models/trace/helpers/Timing.js +9 -2
  196. package/models/trace/helpers/Timing.js.map +1 -1
  197. package/models/trace/helpers/Trace.d.ts +1 -0
  198. package/models/trace/helpers/Trace.js +12 -5
  199. package/models/trace/helpers/Trace.js.map +1 -1
  200. package/models/trace/helpers/devtools_entrypoint-bundle-typescript-tsconfig.json +4 -3
  201. package/models/trace/helpers/helpers-tsconfig.json +7 -3
  202. package/models/trace/insights/CLSCulprits.d.ts +1 -1
  203. package/models/trace/insights/CLSCulprits.js +2 -1
  204. package/models/trace/insights/CLSCulprits.js.map +1 -1
  205. package/models/trace/insights/Cache.d.ts +1 -1
  206. package/models/trace/insights/Cache.js +2 -1
  207. package/models/trace/insights/Cache.js.map +1 -1
  208. package/models/trace/insights/CharacterSet.d.ts +49 -0
  209. package/models/trace/insights/CharacterSet.js +132 -0
  210. package/models/trace/insights/CharacterSet.js.map +1 -0
  211. package/models/trace/insights/Common.d.ts +2 -2
  212. package/models/trace/insights/Common.js +1 -6
  213. package/models/trace/insights/Common.js.map +1 -1
  214. package/models/trace/insights/DOMSize.d.ts +1 -1
  215. package/models/trace/insights/DOMSize.js +2 -1
  216. package/models/trace/insights/DOMSize.js.map +1 -1
  217. package/models/trace/insights/DocumentLatency.d.ts +1 -1
  218. package/models/trace/insights/DocumentLatency.js +2 -1
  219. package/models/trace/insights/DocumentLatency.js.map +1 -1
  220. package/models/trace/insights/DuplicatedJavaScript.d.ts +1 -1
  221. package/models/trace/insights/DuplicatedJavaScript.js +2 -1
  222. package/models/trace/insights/DuplicatedJavaScript.js.map +1 -1
  223. package/models/trace/insights/FontDisplay.d.ts +1 -1
  224. package/models/trace/insights/FontDisplay.js +2 -1
  225. package/models/trace/insights/FontDisplay.js.map +1 -1
  226. package/models/trace/insights/ForcedReflow.d.ts +2 -2
  227. package/models/trace/insights/ForcedReflow.js +3 -2
  228. package/models/trace/insights/ForcedReflow.js.map +1 -1
  229. package/models/trace/insights/INPBreakdown.d.ts +1 -1
  230. package/models/trace/insights/INPBreakdown.js +2 -1
  231. package/models/trace/insights/INPBreakdown.js.map +1 -1
  232. package/models/trace/insights/ImageDelivery.d.ts +1 -1
  233. package/models/trace/insights/ImageDelivery.js +2 -1
  234. package/models/trace/insights/ImageDelivery.js.map +1 -1
  235. package/models/trace/insights/LCPBreakdown.d.ts +2 -2
  236. package/models/trace/insights/LCPBreakdown.js +4 -3
  237. package/models/trace/insights/LCPBreakdown.js.map +1 -1
  238. package/models/trace/insights/LCPDiscovery.d.ts +8 -4
  239. package/models/trace/insights/LCPDiscovery.js +18 -11
  240. package/models/trace/insights/LCPDiscovery.js.map +1 -1
  241. package/models/trace/insights/LegacyJavaScript.d.ts +1 -1
  242. package/models/trace/insights/LegacyJavaScript.js +2 -1
  243. package/models/trace/insights/LegacyJavaScript.js.map +1 -1
  244. package/models/trace/insights/Models.d.ts +1 -0
  245. package/models/trace/insights/Models.js +1 -0
  246. package/models/trace/insights/Models.js.map +1 -1
  247. package/models/trace/insights/ModernHTTP.d.ts +1 -1
  248. package/models/trace/insights/ModernHTTP.js +2 -1
  249. package/models/trace/insights/ModernHTTP.js.map +1 -1
  250. package/models/trace/insights/NetworkDependencyTree.d.ts +1 -1
  251. package/models/trace/insights/NetworkDependencyTree.js +6 -4
  252. package/models/trace/insights/NetworkDependencyTree.js.map +1 -1
  253. package/models/trace/insights/RenderBlocking.d.ts +2 -2
  254. package/models/trace/insights/RenderBlocking.js +8 -7
  255. package/models/trace/insights/RenderBlocking.js.map +1 -1
  256. package/models/trace/insights/SlowCSSSelector.d.ts +1 -1
  257. package/models/trace/insights/SlowCSSSelector.js +2 -1
  258. package/models/trace/insights/SlowCSSSelector.js.map +1 -1
  259. package/models/trace/insights/ThirdParties.js +2 -1
  260. package/models/trace/insights/ThirdParties.js.map +1 -1
  261. package/models/trace/insights/Viewport.d.ts +1 -1
  262. package/models/trace/insights/Viewport.js +2 -1
  263. package/models/trace/insights/Viewport.js.map +1 -1
  264. package/models/trace/insights/devtools_entrypoint-bundle-typescript-tsconfig.json +4 -3
  265. package/models/trace/insights/insights-tsconfig.json +26 -3
  266. package/models/trace/insights/types.d.ts +17 -6
  267. package/models/trace/insights/types.js +1 -0
  268. package/models/trace/insights/types.js.map +1 -1
  269. package/models/trace/lantern/core/core-tsconfig.json +4 -3
  270. package/models/trace/lantern/core/devtools_entrypoint-bundle-typescript-tsconfig.json +4 -3
  271. package/models/trace/lantern/devtools_entrypoint-bundle-typescript-tsconfig.json +4 -3
  272. package/models/trace/lantern/graph/devtools_entrypoint-bundle-typescript-tsconfig.json +4 -3
  273. package/models/trace/lantern/graph/graph-tsconfig.json +4 -3
  274. package/models/trace/lantern/lantern-tsconfig.json +4 -3
  275. package/models/trace/lantern/metrics/FirstContentfulPaint.js +6 -6
  276. package/models/trace/lantern/metrics/FirstContentfulPaint.js.map +1 -1
  277. package/models/trace/lantern/metrics/devtools_entrypoint-bundle-typescript-tsconfig.json +4 -3
  278. package/models/trace/lantern/metrics/metrics-tsconfig.json +4 -3
  279. package/models/trace/lantern/simulation/devtools_entrypoint-bundle-typescript-tsconfig.json +4 -3
  280. package/models/trace/lantern/simulation/simulation-tsconfig.json +4 -3
  281. package/models/trace/lantern/types/Lantern.d.ts +7 -7
  282. package/models/trace/lantern/types/Lantern.js.map +1 -1
  283. package/models/trace/lantern/types/devtools_entrypoint-bundle-typescript-tsconfig.json +4 -3
  284. package/models/trace/lantern/types/types-tsconfig.json +4 -3
  285. package/models/trace/trace-tsconfig.json +7 -3
  286. package/models/trace/types/Configuration.d.ts +1 -4
  287. package/models/trace/types/Configuration.js +0 -1
  288. package/models/trace/types/Configuration.js.map +1 -1
  289. package/models/trace/types/File.d.ts +8 -0
  290. package/models/trace/types/File.js.map +1 -1
  291. package/models/trace/types/TraceEvents.d.ts +97 -15
  292. package/models/trace/types/TraceEvents.js +44 -11
  293. package/models/trace/types/TraceEvents.js.map +1 -1
  294. package/models/trace/types/devtools_entrypoint-bundle-typescript-tsconfig.json +4 -3
  295. package/models/trace/types/types-tsconfig.json +10 -3
  296. package/package.json +1 -1
  297. package/test/test-trace-engine.mjs +8 -9
  298. package/.tmp/tsbuildinfo/models/trace/extras/polyfills.d.ts +0 -4
  299. package/.tmp/tsbuildinfo/models/trace/extras/polyfills.d.ts.map +0 -1
  300. package/core/platform/DOMUtilities.d.ts +0 -16
  301. package/core/platform/DOMUtilities.js +0 -123
  302. package/core/platform/DOMUtilities.js.map +0 -1
@@ -14,7 +14,7 @@ export const UIStrings = {
14
14
  /**
15
15
  * @description Text to tell the user about how caching can help improve performance.
16
16
  */
17
- description: 'A long cache lifetime can speed up repeat visits to your page. [Learn more](https://web.dev/uses-long-cache-ttl/).',
17
+ description: 'A long cache lifetime can speed up repeat visits to your page. [Learn more about caching](https://developer.chrome.com/docs/performance/insights/cache).',
18
18
  /**
19
19
  * @description Column for a font loaded by the page to render text.
20
20
  */
@@ -43,6 +43,7 @@ function finalize(partialModel) {
43
43
  strings: UIStrings,
44
44
  title: i18nString(UIStrings.title),
45
45
  description: i18nString(UIStrings.description),
46
+ docs: 'https://developer.chrome.com/docs/performance/insights/cache',
46
47
  category: InsightCategory.ALL,
47
48
  state: partialModel.requests.length > 0 ? 'fail' : 'pass',
48
49
  ...partialModel,
@@ -1 +1 @@
1
- {"version":3,"file":"Cache.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/insights/Cache.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2025 The Chromium Authors\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 InsightKeys,\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/Cache.ts', UIStrings);\nexport const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);\n\nexport type CacheInsightModel = InsightModel<typeof UIStrings, {\n requests: Array<{\n request: Types.Events.SyntheticNetworkRequest,\n ttl: number,\n wastedBytes: number,\n }>,\n}>;\n\n// Threshold for cache hits.\nconst IGNORE_THRESHOLD_IN_PERCENT = 0.925;\n\nfunction finalize(partialModel: PartialInsightModel<CacheInsightModel>): CacheInsightModel {\n return {\n insightKey: InsightKeys.CACHE,\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 * historical Chrome UMA stats (see RESOURCE_AGE_IN_HOURS_DECILES comment).\n *\n * This function returns values on this curve: https://www.desmos.com/calculator/eaqiszhugy (but using seconds, rather than hours)\n * See http://github.com/GoogleChrome/lighthouse/pull/3531 for history.\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/jjwc5mzuwd\n // This UMA data is from 2017 but the metric isn't tracked any longer in 2025.\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 isCacheInsight(model: InsightModel): model is CacheInsightModel {\n return model.insightKey === InsightKeys.CACHE;\n}\n\nexport function generateInsight(data: Handlers.Types.HandlerData, context: InsightSetContext): CacheInsightModel {\n const isWithinContext = (event: Types.Events.Event): boolean => Helpers.Timing.eventIsInBounds(event, context.bounds);\n const contextRequests = data.NetworkRequests.byTime.filter(isWithinContext);\n\n const results: CacheableRequest[] = [];\n let totalWastedBytes = 0;\n const wastedBytesByRequestId = new Map<string, number>();\n for (const req of contextRequests) {\n if (!req.args.data.responseHeaders || !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 // Ignore >= 30d.\n const ttlDays = ttl / 86400;\n if (ttlDays >= 30) {\n continue;\n }\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 wastedBytes: totalWastedBytes,\n });\n}\n\nexport function createOverlayForRequest(request: Types.Events.SyntheticNetworkRequest): Types.Overlays.EntryOutline {\n return {\n type: 'ENTRY_OUTLINE',\n entry: request,\n outlineReason: 'ERROR',\n };\n}\n\nexport function createOverlays(model: CacheInsightModel): Types.Overlays.Overlay[] {\n return model.requests.map(req => createOverlayForRequest(req.request));\n}\n"]}
1
+ {"version":3,"file":"Cache.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/insights/Cache.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2025 The Chromium Authors\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 InsightKeys,\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 about caching](https://developer.chrome.com/docs/performance/insights/cache).',\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/Cache.ts', UIStrings);\nexport const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);\n\nexport type CacheInsightModel = InsightModel<typeof UIStrings, {\n requests: Array<{\n request: Types.Events.SyntheticNetworkRequest,\n ttl: number,\n wastedBytes: number,\n }>,\n}>;\n\n// Threshold for cache hits.\nconst IGNORE_THRESHOLD_IN_PERCENT = 0.925;\n\nfunction finalize(partialModel: PartialInsightModel<CacheInsightModel>): CacheInsightModel {\n return {\n insightKey: InsightKeys.CACHE,\n strings: UIStrings,\n title: i18nString(UIStrings.title),\n description: i18nString(UIStrings.description),\n docs: 'https://developer.chrome.com/docs/performance/insights/cache',\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 * historical Chrome UMA stats (see RESOURCE_AGE_IN_HOURS_DECILES comment).\n *\n * This function returns values on this curve: https://www.desmos.com/calculator/eaqiszhugy (but using seconds, rather than hours)\n * See http://github.com/GoogleChrome/lighthouse/pull/3531 for history.\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/jjwc5mzuwd\n // This UMA data is from 2017 but the metric isn't tracked any longer in 2025.\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 isCacheInsight(model: InsightModel): model is CacheInsightModel {\n return model.insightKey === InsightKeys.CACHE;\n}\n\nexport function generateInsight(data: Handlers.Types.HandlerData, context: InsightSetContext): CacheInsightModel {\n const isWithinContext = (event: Types.Events.Event): boolean => Helpers.Timing.eventIsInBounds(event, context.bounds);\n const contextRequests = data.NetworkRequests.byTime.filter(isWithinContext);\n\n const results: CacheableRequest[] = [];\n let totalWastedBytes = 0;\n const wastedBytesByRequestId = new Map<string, number>();\n for (const req of contextRequests) {\n if (!req.args.data.responseHeaders || !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 // Ignore >= 30d.\n const ttlDays = ttl / 86400;\n if (ttlDays >= 30) {\n continue;\n }\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 wastedBytes: totalWastedBytes,\n });\n}\n\nexport function createOverlayForRequest(request: Types.Events.SyntheticNetworkRequest): Types.Overlays.EntryOutline {\n return {\n type: 'ENTRY_OUTLINE',\n entry: request,\n outlineReason: 'ERROR',\n };\n}\n\nexport function createOverlays(model: CacheInsightModel): Types.Overlays.Overlay[] {\n return model.requests.map(req => createOverlayForRequest(req.request));\n}\n"]}
@@ -0,0 +1,49 @@
1
+ import type * as Handlers from '../handlers/handlers.js';
2
+ import type * as Types from '../types/types.js';
3
+ import { type Checklist, type InsightModel, type InsightSetContext } from './types.js';
4
+ export declare const UIStrings: {
5
+ /**
6
+ * @description Title of an insight that checks whether the page declares a character encoding early enough.
7
+ */
8
+ readonly title: "Declare a character encoding";
9
+ /**
10
+ * @description Description of an insight that checks whether the page has a proper character encoding declaration via HTTP header or early meta tag.
11
+ */
12
+ readonly description: "A character encoding declaration is required. It can be done with a meta charset tag in the first 1024 bytes of the HTML or in the Content-Type HTTP response header. [Learn more about declaring the character encoding](https://developer.chrome.com/docs/insights/charset/).";
13
+ /**
14
+ * @description Text to tell the user that the charset is declared in the Content-Type HTTP response header.
15
+ */
16
+ readonly passingHttpHeader: "Declares charset in HTTP header";
17
+ /**
18
+ * @description Text to tell the user that the charset is NOT declared in the Content-Type HTTP response header.
19
+ */
20
+ readonly failedHttpHeader: "Does not declare charset in HTTP header";
21
+ /**
22
+ * @description Text to tell the user that a meta charset tag was found in the first 1024 bytes of the HTML.
23
+ */
24
+ readonly passingMetaCharsetEarly: "Declares charset using a meta tag in the first 1024 bytes";
25
+ /**
26
+ * @description Text to tell the user that a meta charset tag was found, but too late in the HTML.
27
+ */
28
+ readonly failedMetaCharsetLate: "Declares charset using a meta tag after the first 1024 bytes";
29
+ /**
30
+ * @description Text to tell the user that no meta charset tag was found in the HTML.
31
+ */
32
+ readonly failedMetaCharsetMissing: "Does not declare charset using a meta tag";
33
+ /**
34
+ * @description Text to tell the user that trace data did not include the Blink signal for meta charset.
35
+ */
36
+ readonly failedMetaCharsetUnknown: "Could not determine meta charset declaration from trace";
37
+ };
38
+ export declare const i18nString: (id: string, values?: Record<string, string> | undefined) => Record<string, string>;
39
+ export type CharacterSetInsightModel = InsightModel<typeof UIStrings, {
40
+ data?: {
41
+ hasHttpCharset: boolean;
42
+ checklist: Checklist<'httpCharset' | 'metaCharset'>;
43
+ metaCharsetDisposition?: Types.Events.MetaCharsetDisposition;
44
+ documentRequest?: Types.Events.SyntheticNetworkRequest;
45
+ };
46
+ }>;
47
+ export declare function isCharacterSetInsight(model: InsightModel): model is CharacterSetInsightModel;
48
+ export declare function generateInsight(data: Handlers.Types.HandlerData, context: InsightSetContext): CharacterSetInsightModel;
49
+ export declare function createOverlays(model: CharacterSetInsightModel): Types.Overlays.Overlay[];
@@ -0,0 +1,132 @@
1
+ // Copyright 2026 The Chromium Authors
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 { InsightCategory, InsightKeys, InsightWarning, } from './types.js';
6
+ export const UIStrings = {
7
+ /**
8
+ * @description Title of an insight that checks whether the page declares a character encoding early enough.
9
+ */
10
+ title: 'Declare a character encoding',
11
+ /**
12
+ * @description Description of an insight that checks whether the page has a proper character encoding declaration via HTTP header or early meta tag.
13
+ */
14
+ description: 'A character encoding declaration is required. It can be done with a meta charset tag in the first 1024 bytes of the HTML or in the Content-Type HTTP response header. [Learn more about declaring the character encoding](https://developer.chrome.com/docs/insights/charset/).',
15
+ /**
16
+ * @description Text to tell the user that the charset is declared in the Content-Type HTTP response header.
17
+ */
18
+ passingHttpHeader: 'Declares charset in HTTP header',
19
+ /**
20
+ * @description Text to tell the user that the charset is NOT declared in the Content-Type HTTP response header.
21
+ */
22
+ failedHttpHeader: 'Does not declare charset in HTTP header',
23
+ /**
24
+ * @description Text to tell the user that a meta charset tag was found in the first 1024 bytes of the HTML.
25
+ */
26
+ passingMetaCharsetEarly: 'Declares charset using a meta tag in the first 1024 bytes',
27
+ /**
28
+ * @description Text to tell the user that a meta charset tag was found, but too late in the HTML.
29
+ */
30
+ failedMetaCharsetLate: 'Declares charset using a meta tag after the first 1024 bytes',
31
+ /**
32
+ * @description Text to tell the user that no meta charset tag was found in the HTML.
33
+ */
34
+ failedMetaCharsetMissing: 'Does not declare charset using a meta tag',
35
+ /**
36
+ * @description Text to tell the user that trace data did not include the Blink signal for meta charset.
37
+ */
38
+ failedMetaCharsetUnknown: 'Could not determine meta charset declaration from trace',
39
+ };
40
+ // const str_ = i18n.i18n.registerUIStrings('models/trace/insights/CharacterSet.ts', UIStrings);
41
+ export const i18nString = (i18nId, values) => ({i18nId, values}); // i18n.i18n.getLocalizedString.bind(undefined, str_);
42
+ const CHARSET_HTTP_REGEX = /charset\s*=\s*[a-zA-Z0-9\-_:.()]{2,}/i;
43
+ export function isCharacterSetInsight(model) {
44
+ return model.insightKey === InsightKeys.CHARACTER_SET;
45
+ }
46
+ function finalize(partialModel) {
47
+ let hasFailure = false;
48
+ if (partialModel.data) {
49
+ hasFailure = !partialModel.data.checklist.httpCharset.value && !partialModel.data.checklist.metaCharset.value;
50
+ }
51
+ return {
52
+ insightKey: InsightKeys.CHARACTER_SET,
53
+ strings: UIStrings,
54
+ title: i18nString(UIStrings.title),
55
+ description: i18nString(UIStrings.description),
56
+ docs: 'https://developer.chrome.com/docs/insights/charset/',
57
+ category: InsightCategory.ALL,
58
+ state: hasFailure ? 'fail' : 'pass',
59
+ ...partialModel,
60
+ };
61
+ }
62
+ function hasCharsetInContentType(request) {
63
+ if (!request.args.data.responseHeaders) {
64
+ return false;
65
+ }
66
+ for (const header of request.args.data.responseHeaders) {
67
+ if (header.name.toLowerCase() === 'content-type') {
68
+ return CHARSET_HTTP_REGEX.test(header.value);
69
+ }
70
+ }
71
+ return false;
72
+ }
73
+ function findMetaCharsetDisposition(data, context) {
74
+ if (!context.navigation) {
75
+ return undefined;
76
+ }
77
+ return data.PageLoadMetrics.metaCharsetCheckEventsByNavigation.get(context.navigation)
78
+ ?.at(-1)
79
+ ?.args.data?.disposition;
80
+ }
81
+ function metaCharsetLabel(disposition) {
82
+ switch (disposition) {
83
+ case 'found-in-first-1024-bytes':
84
+ return i18nString(UIStrings.passingMetaCharsetEarly);
85
+ case 'found-after-first-1024-bytes':
86
+ return i18nString(UIStrings.failedMetaCharsetLate);
87
+ case 'not-found':
88
+ return i18nString(UIStrings.failedMetaCharsetMissing);
89
+ default:
90
+ return i18nString(UIStrings.failedMetaCharsetUnknown);
91
+ }
92
+ }
93
+ export function generateInsight(data, context) {
94
+ if (!context.navigation) {
95
+ return finalize({});
96
+ }
97
+ const documentRequest = data.NetworkRequests.byId.get(context.navigationId);
98
+ if (!documentRequest) {
99
+ return finalize({ warnings: [InsightWarning.NO_DOCUMENT_REQUEST] });
100
+ }
101
+ const hasHttpCharset = hasCharsetInContentType(documentRequest);
102
+ const metaCharsetDisposition = findMetaCharsetDisposition(data, context);
103
+ const hasMetaCharsetInFirst1024Bytes = metaCharsetDisposition === 'found-in-first-1024-bytes';
104
+ return finalize({
105
+ relatedEvents: [documentRequest],
106
+ data: {
107
+ hasHttpCharset,
108
+ metaCharsetDisposition,
109
+ documentRequest,
110
+ checklist: {
111
+ httpCharset: {
112
+ label: hasHttpCharset ? i18nString(UIStrings.passingHttpHeader) : i18nString(UIStrings.failedHttpHeader),
113
+ value: hasHttpCharset,
114
+ },
115
+ metaCharset: {
116
+ label: metaCharsetLabel(metaCharsetDisposition),
117
+ value: hasMetaCharsetInFirst1024Bytes,
118
+ },
119
+ },
120
+ },
121
+ });
122
+ }
123
+ export function createOverlays(model) {
124
+ if (!model.data?.documentRequest) {
125
+ return [];
126
+ }
127
+ return [{
128
+ type: 'ENTRY_SELECTED',
129
+ entry: model.data.documentRequest,
130
+ }];
131
+ }
132
+ //# sourceMappingURL=CharacterSet.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CharacterSet.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/insights/CharacterSet.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2026 The Chromium Authors\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 type * as Handlers from '../handlers/handlers.js';\nimport type * as Types from '../types/types.js';\n\nimport {\n type Checklist,\n InsightCategory,\n InsightKeys,\n type InsightModel,\n type InsightSetContext,\n InsightWarning,\n type PartialInsightModel,\n} from './types.js';\n\nexport const UIStrings = {\n /**\n * @description Title of an insight that checks whether the page declares a character encoding early enough.\n */\n title: 'Declare a character encoding',\n /**\n * @description Description of an insight that checks whether the page has a proper character encoding declaration via HTTP header or early meta tag.\n */\n description:\n 'A character encoding declaration is required. It can be done with a meta charset tag in the first 1024 bytes of the HTML or in the Content-Type HTTP response header. [Learn more about declaring the character encoding](https://developer.chrome.com/docs/insights/charset/).',\n /**\n * @description Text to tell the user that the charset is declared in the Content-Type HTTP response header.\n */\n passingHttpHeader: 'Declares charset in HTTP header',\n /**\n * @description Text to tell the user that the charset is NOT declared in the Content-Type HTTP response header.\n */\n failedHttpHeader: 'Does not declare charset in HTTP header',\n /**\n * @description Text to tell the user that a meta charset tag was found in the first 1024 bytes of the HTML.\n */\n passingMetaCharsetEarly: 'Declares charset using a meta tag in the first 1024 bytes',\n /**\n * @description Text to tell the user that a meta charset tag was found, but too late in the HTML.\n */\n failedMetaCharsetLate: 'Declares charset using a meta tag after the first 1024 bytes',\n /**\n * @description Text to tell the user that no meta charset tag was found in the HTML.\n */\n failedMetaCharsetMissing: 'Does not declare charset using a meta tag',\n /**\n * @description Text to tell the user that trace data did not include the Blink signal for meta charset.\n */\n failedMetaCharsetUnknown: 'Could not determine meta charset declaration from trace',\n} as const;\n\nconst str_ = i18n.i18n.registerUIStrings('models/trace/insights/CharacterSet.ts', UIStrings);\nexport const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);\n\nconst CHARSET_HTTP_REGEX = /charset\\s*=\\s*[a-zA-Z0-9\\-_:.()]{2,}/i;\n\nexport type CharacterSetInsightModel = InsightModel<typeof UIStrings, {\n data?: {\n hasHttpCharset: boolean,\n checklist: Checklist<'httpCharset'|'metaCharset'>,\n metaCharsetDisposition?: Types.Events.MetaCharsetDisposition,\n documentRequest?: Types.Events.SyntheticNetworkRequest,\n },\n}>;\n\nexport function isCharacterSetInsight(model: InsightModel): model is CharacterSetInsightModel {\n return model.insightKey === InsightKeys.CHARACTER_SET;\n}\n\nfunction finalize(partialModel: PartialInsightModel<CharacterSetInsightModel>): CharacterSetInsightModel {\n let hasFailure = false;\n if (partialModel.data) {\n hasFailure = !partialModel.data.checklist.httpCharset.value && !partialModel.data.checklist.metaCharset.value;\n }\n\n return {\n insightKey: InsightKeys.CHARACTER_SET,\n strings: UIStrings,\n title: i18nString(UIStrings.title),\n description: i18nString(UIStrings.description),\n docs: 'https://developer.chrome.com/docs/insights/charset/',\n category: InsightCategory.ALL,\n state: hasFailure ? 'fail' : 'pass',\n ...partialModel,\n };\n}\n\nfunction hasCharsetInContentType(request: Types.Events.SyntheticNetworkRequest): boolean {\n if (!request.args.data.responseHeaders) {\n return false;\n }\n for (const header of request.args.data.responseHeaders) {\n if (header.name.toLowerCase() === 'content-type') {\n return CHARSET_HTTP_REGEX.test(header.value);\n }\n }\n return false;\n}\n\nfunction findMetaCharsetDisposition(\n data: Handlers.Types.HandlerData,\n context: InsightSetContext,\n ): Types.Events.MetaCharsetDisposition|undefined {\n if (!context.navigation) {\n return undefined;\n }\n return data.PageLoadMetrics.metaCharsetCheckEventsByNavigation.get(context.navigation)\n ?.at(-1)\n ?.args.data?.disposition;\n}\n\nfunction metaCharsetLabel(disposition: Types.Events.MetaCharsetDisposition|undefined): ReturnType<typeof i18nString> {\n switch (disposition) {\n case 'found-in-first-1024-bytes':\n return i18nString(UIStrings.passingMetaCharsetEarly);\n case 'found-after-first-1024-bytes':\n return i18nString(UIStrings.failedMetaCharsetLate);\n case 'not-found':\n return i18nString(UIStrings.failedMetaCharsetMissing);\n default:\n return i18nString(UIStrings.failedMetaCharsetUnknown);\n }\n}\n\nexport function generateInsight(\n data: Handlers.Types.HandlerData, context: InsightSetContext): CharacterSetInsightModel {\n if (!context.navigation) {\n return finalize({});\n }\n\n const documentRequest = data.NetworkRequests.byId.get(context.navigationId);\n if (!documentRequest) {\n return finalize({warnings: [InsightWarning.NO_DOCUMENT_REQUEST]});\n }\n\n const hasHttpCharset = hasCharsetInContentType(documentRequest);\n const metaCharsetDisposition = findMetaCharsetDisposition(data, context);\n const hasMetaCharsetInFirst1024Bytes = metaCharsetDisposition === 'found-in-first-1024-bytes';\n\n return finalize({\n relatedEvents: [documentRequest],\n data: {\n hasHttpCharset,\n metaCharsetDisposition,\n documentRequest,\n checklist: {\n httpCharset: {\n label: hasHttpCharset ? i18nString(UIStrings.passingHttpHeader) : i18nString(UIStrings.failedHttpHeader),\n value: hasHttpCharset,\n },\n metaCharset: {\n label: metaCharsetLabel(metaCharsetDisposition),\n value: hasMetaCharsetInFirst1024Bytes,\n },\n },\n },\n });\n}\n\nexport function createOverlays(model: CharacterSetInsightModel): Types.Overlays.Overlay[] {\n if (!model.data?.documentRequest) {\n return [];\n }\n\n return [{\n type: 'ENTRY_SELECTED',\n entry: model.data.documentRequest,\n }];\n}\n"]}
@@ -3,10 +3,10 @@ import * as Protocol from '../../../generated/protocol.js';
3
3
  import type * as Handlers from '../handlers/handlers.js';
4
4
  import * as Types from '../types/types.js';
5
5
  import { type InsightModel, type InsightModels, type InsightSet, type InsightSetContext, type MetricSavings } from './types.js';
6
- export declare function getInsight<InsightName extends keyof InsightModels>(insightName: InsightName, insightSet: InsightSet): InsightModels[InsightName] | null;
6
+ export declare function getInsight<InsightName extends keyof InsightModels>(insightName: InsightName, insightSet: InsightSet): InsightModels[InsightName];
7
7
  export declare function getLCP(insightSet: InsightSet): {
8
8
  value: Types.Timing.Micro;
9
- event: Types.Events.LargestContentfulPaintCandidate;
9
+ event: Types.Events.AnyLargestContentfulPaintCandidate;
10
10
  } | null;
11
11
  export declare function getINP(insightSet: InsightSet): {
12
12
  value: Types.Timing.Micro;
@@ -7,12 +7,7 @@ import { getLogNormalScore } from './Statistics.js';
7
7
  import { InsightKeys, } from './types.js';
8
8
  const GRAPH_SAVINGS_PRECISION = 50;
9
9
  export function getInsight(insightName, insightSet) {
10
- const insight = insightSet.model[insightName];
11
- if (insight instanceof Error) {
12
- return null;
13
- }
14
- // For some reason typescript won't narrow the type by removing Error, so do it manually.
15
- return insight;
10
+ return insightSet.model[insightName];
16
11
  }
17
12
  export function getLCP(insightSet) {
18
13
  const insight = getInsight(InsightKeys.LCP_BREAKDOWN, insightSet);
@@ -1 +1 @@
1
- {"version":3,"file":"Common.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/insights/Common.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 The Chromium Authors\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 Protocol from '../../../generated/protocol.js';\nimport type * as CrUXManager from '../../crux-manager/crux-manager.js';\nimport type * as Handlers from '../handlers/handlers.js';\nimport * as Helpers from '../helpers/helpers.js';\nimport type * as Lantern from '../lantern/lantern.js';\nimport * as Types from '../types/types.js';\n\nimport {getLogNormalScore} from './Statistics.js';\nimport {\n InsightKeys,\n type InsightModel,\n type InsightModels,\n type InsightSet,\n type InsightSetContext,\n type MetricSavings,\n} from './types.js';\n\nconst GRAPH_SAVINGS_PRECISION = 50;\n\nexport function getInsight<InsightName extends keyof InsightModels>(\n insightName: InsightName, insightSet: InsightSet): InsightModels[InsightName]|null {\n const insight = insightSet.model[insightName];\n if (insight instanceof Error) {\n return null;\n }\n\n // For some reason typescript won't narrow the type by removing Error, so do it manually.\n return insight;\n}\n\nexport function getLCP(insightSet: InsightSet):\n {value: Types.Timing.Micro, event: Types.Events.LargestContentfulPaintCandidate}|null {\n const insight = getInsight(InsightKeys.LCP_BREAKDOWN, insightSet);\n if (!insight || !insight.lcpMs || !insight.lcpEvent) {\n return null;\n }\n\n const value = Helpers.Timing.milliToMicro(insight.lcpMs);\n return {value, event: insight.lcpEvent};\n}\n\nexport function getINP(insightSet: InsightSet):\n {value: Types.Timing.Micro, event: Types.Events.SyntheticInteractionPair}|null {\n const insight = getInsight(InsightKeys.INP_BREAKDOWN, insightSet);\n if (!insight?.longestInteractionEvent?.dur) {\n return null;\n }\n\n const value = insight.longestInteractionEvent.dur;\n return {value, event: insight.longestInteractionEvent};\n}\n\nexport function getCLS(insightSet: InsightSet): {value: number, worstClusterEvent: Types.Events.Event|null} {\n const insight = getInsight(InsightKeys.CLS_CULPRITS, insightSet);\n if (!insight) {\n // Unlike the other metrics, there is always a value for CLS even with no data.\n return {value: 0, worstClusterEvent: null};\n }\n\n // TODO(cjamcl): the CLS insight should be doing this for us.\n let maxScore = 0;\n let worstCluster;\n for (const cluster of insight.clusters) {\n if (cluster.clusterCumulativeScore > maxScore) {\n maxScore = cluster.clusterCumulativeScore;\n worstCluster = cluster;\n }\n }\n\n return {value: maxScore, worstClusterEvent: worstCluster ?? null};\n}\n\nexport function evaluateLCPMetricScore(value: Types.Timing.Milli): number {\n return getLogNormalScore({p10: 2500, median: 4000}, value);\n}\n\nexport function evaluateINPMetricScore(value: Types.Timing.Milli): number {\n return getLogNormalScore({p10: 200, median: 500}, value);\n}\n\nexport function evaluateCLSMetricScore(value: number): number {\n return getLogNormalScore({p10: 0.1, median: 0.25}, value);\n}\n\nexport interface CrUXFieldMetricTimingResult {\n value: Types.Timing.Micro;\n pageScope: CrUXManager.PageScope;\n}\nexport interface CrUXFieldMetricNumberResult {\n value: number;\n pageScope: CrUXManager.PageScope;\n}\nexport interface CrUXFieldMetricResults {\n fcp: CrUXFieldMetricTimingResult|null;\n lcp: CrUXFieldMetricTimingResult|null;\n inp: CrUXFieldMetricTimingResult|null;\n cls: CrUXFieldMetricNumberResult|null;\n lcpBreakdown: {\n ttfb: CrUXFieldMetricTimingResult|null,\n loadDelay: CrUXFieldMetricTimingResult|null,\n loadDuration: CrUXFieldMetricTimingResult|null,\n renderDelay: CrUXFieldMetricTimingResult|null,\n };\n}\n\nfunction getPageResult(\n cruxFieldData: CrUXManager.PageResult[], url: string, origin: string,\n scope: CrUXManager.Scope|null = null): CrUXManager.PageResult|undefined {\n return cruxFieldData.find(result => {\n const key = scope ? result[`${scope.pageScope}-${scope.deviceScope}`]?.record.key :\n (result['url-ALL'] || result['origin-ALL'])?.record.key;\n return (key?.url && key.url === url) || (key?.origin && key.origin === origin);\n });\n}\n\nfunction getMetricResult(\n pageResult: CrUXManager.PageResult, name: CrUXManager.StandardMetricNames,\n scope: CrUXManager.Scope|null = null): CrUXFieldMetricNumberResult|null {\n const scopes: Array<{pageScope: CrUXManager.PageScope, deviceScope: CrUXManager.DeviceScope}> = [];\n if (scope) {\n scopes.push(scope);\n } else {\n scopes.push({pageScope: 'url', deviceScope: 'ALL'});\n scopes.push({pageScope: 'origin', deviceScope: 'ALL'});\n }\n\n for (const scope of scopes) {\n const key = `${scope.pageScope}-${scope.deviceScope}` as const;\n let value = pageResult[key]?.record.metrics[name]?.percentiles?.p75;\n if (typeof value === 'string') {\n value = Number(value);\n }\n if (typeof value === 'number' && Number.isFinite(value)) {\n return {value, pageScope: scope.pageScope};\n }\n }\n\n return null;\n}\n\nfunction getMetricTimingResult(\n pageResult: CrUXManager.PageResult, name: CrUXManager.StandardMetricNames,\n scope: CrUXManager.Scope|null = null): CrUXFieldMetricTimingResult|null {\n const result = getMetricResult(pageResult, name, scope);\n if (result) {\n const valueMs = result.value as Types.Timing.Milli;\n return {value: Helpers.Timing.milliToMicro(valueMs), pageScope: result.pageScope};\n }\n\n return null;\n}\n\nexport function getFieldMetricsForInsightSet(\n insightSet: InsightSet, metadata: Types.File.MetaData|null,\n scope: CrUXManager.Scope|null = null): CrUXFieldMetricResults|null {\n const cruxFieldData = metadata?.cruxFieldData;\n if (!cruxFieldData) {\n return null;\n }\n\n const pageResult = getPageResult(cruxFieldData, insightSet.url.href, insightSet.url.origin, scope);\n if (!pageResult) {\n return null;\n }\n\n return {\n fcp: getMetricTimingResult(pageResult, 'first_contentful_paint', scope),\n lcp: getMetricTimingResult(pageResult, 'largest_contentful_paint', scope),\n inp: getMetricTimingResult(pageResult, 'interaction_to_next_paint', scope),\n cls: getMetricResult(pageResult, 'cumulative_layout_shift', scope),\n lcpBreakdown: {\n ttfb: getMetricTimingResult(pageResult, 'largest_contentful_paint_image_time_to_first_byte', scope),\n loadDelay: getMetricTimingResult(pageResult, 'largest_contentful_paint_image_resource_load_delay', scope),\n loadDuration: getMetricTimingResult(pageResult, 'largest_contentful_paint_image_resource_load_duration', scope),\n renderDelay: getMetricTimingResult(pageResult, 'largest_contentful_paint_image_element_render_delay', scope),\n }\n };\n}\n\nexport function calculateMetricWeightsForSorting(\n insightSet: InsightSet, metadata: Types.File.MetaData|null): {lcp: number, inp: number, cls: number} {\n const weights = {\n lcp: 1 / 3,\n inp: 1 / 3,\n cls: 1 / 3,\n };\n\n const cruxFieldData = metadata?.cruxFieldData;\n if (!cruxFieldData) {\n return weights;\n }\n\n const fieldMetrics = getFieldMetricsForInsightSet(insightSet, metadata);\n if (!fieldMetrics) {\n return weights;\n }\n\n const fieldLcp = fieldMetrics.lcp?.value ?? null;\n const fieldInp = fieldMetrics.inp?.value ?? null;\n const fieldCls = fieldMetrics.cls?.value ?? null;\n const fieldLcpScore = fieldLcp !== null ? evaluateLCPMetricScore(Helpers.Timing.microToMilli(fieldLcp)) : 0;\n const fieldInpScore = fieldInp !== null ? evaluateINPMetricScore(Helpers.Timing.microToMilli(fieldInp)) : 0;\n const fieldClsScore = fieldCls !== null ? evaluateCLSMetricScore(fieldCls) : 0;\n const fieldLcpScoreInverted = 1 - fieldLcpScore;\n const fieldInpScoreInverted = 1 - fieldInpScore;\n const fieldClsScoreInverted = 1 - fieldClsScore;\n const invertedSum = fieldLcpScoreInverted + fieldInpScoreInverted + fieldClsScoreInverted;\n if (!invertedSum) {\n return weights;\n }\n\n weights.lcp = fieldLcpScoreInverted / invertedSum;\n weights.inp = fieldInpScoreInverted / invertedSum;\n weights.cls = fieldClsScoreInverted / invertedSum;\n\n return weights;\n}\n\n/**\n * Simulates the provided graph before and after the byte savings from `wastedBytesByRequestId` are applied.\n */\nfunction estimateSavingsWithGraphs(\n wastedBytesByRequestId: Map<string, number>, simulator: Lantern.Simulation.Simulator,\n graph: Lantern.Graph.Node): Types.Timing.Milli {\n const simulationBeforeChanges = simulator.simulate(graph);\n\n const originalTransferSizes = new Map<string, number>();\n graph.traverse(node => {\n if (node.type !== 'network') {\n return;\n }\n const wastedBytes = wastedBytesByRequestId.get(node.request.requestId);\n if (!wastedBytes) {\n return;\n }\n\n const original = node.request.transferSize;\n originalTransferSizes.set(node.request.requestId, original);\n\n node.request.transferSize = Math.max(original - wastedBytes, 0);\n });\n\n const simulationAfterChanges = simulator.simulate(graph);\n\n // Restore the original transfer size after we've done our simulation\n graph.traverse(node => {\n if (node.type !== 'network') {\n return;\n }\n const originalTransferSize = originalTransferSizes.get(node.request.requestId);\n if (originalTransferSize === undefined) {\n return;\n }\n node.request.transferSize = originalTransferSize;\n });\n\n let savings = simulationBeforeChanges.timeInMs - simulationAfterChanges.timeInMs;\n savings = Math.round(savings / GRAPH_SAVINGS_PRECISION) * GRAPH_SAVINGS_PRECISION;\n return Types.Timing.Milli(savings);\n}\n\n/**\n * Estimates the FCP & LCP savings for wasted bytes in `wastedBytesByRequestId`.\n */\nexport function metricSavingsForWastedBytes(\n wastedBytesByRequestId: Map<string, number>, context: InsightSetContext): MetricSavings|undefined {\n if (!context.navigation || !context.lantern) {\n return;\n }\n\n if (!wastedBytesByRequestId.size) {\n return {FCP: Types.Timing.Milli(0), LCP: Types.Timing.Milli(0)};\n }\n\n const simulator = context.lantern.simulator;\n const fcpGraph = context.lantern.metrics.firstContentfulPaint.optimisticGraph;\n const lcpGraph = context.lantern.metrics.largestContentfulPaint.optimisticGraph;\n\n return {\n FCP: estimateSavingsWithGraphs(wastedBytesByRequestId, simulator, fcpGraph),\n LCP: estimateSavingsWithGraphs(wastedBytesByRequestId, simulator, lcpGraph),\n };\n}\n\n/**\n * Returns whether the network request was sent encoded.\n */\nexport function isRequestCompressed(request: Types.Events.SyntheticNetworkRequest): boolean {\n if (!request.args.data.responseHeaders) {\n return false;\n }\n\n // FYI: In Lighthouse, older devtools logs (like our test fixtures) seems to be\n // lower case, while modern logs are Cased-Like-This.\n const patterns = [\n /^content-encoding$/i, /^x-content-encoding-over-network$/i,\n /^x-original-content-encoding$/i, // Lightrider.\n ];\n const compressionTypes = ['gzip', 'br', 'deflate', 'zstd'];\n return request.args.data.responseHeaders.some(\n header => patterns.some(p => header.name.match(p)) && compressionTypes.includes(header.value));\n}\n\nexport function isRequestServedFromBrowserCache(request: Types.Events.SyntheticNetworkRequest): boolean {\n if (!request.args.data.responseHeaders || request.args.data.failed) {\n return false;\n }\n\n // Not Modified?\n if (request.args.data.statusCode === 304) {\n return true;\n }\n\n // TODO: for some reason ResourceReceiveResponse events never show a 304 status\n // code, so the above is never gonna work. For now, fall back to a dirty check of\n // looking at the ratio of transfer size and resource size. If it's really small,\n // we certainly did not use the network to fetch it.\n\n const {transferSize, resourceSize} = getRequestSizes(request);\n const ratio = resourceSize ? transferSize / resourceSize : 0;\n if (ratio < 0.01) {\n return true;\n }\n\n return false;\n}\n\nfunction getRequestSizes(request: Types.Events.SyntheticNetworkRequest): {resourceSize: number, transferSize: number} {\n const resourceSize = request.args.data.decodedBodyLength;\n const transferSize = request.args.data.encodedDataLength;\n return {resourceSize, transferSize};\n}\n\n/**\n * Estimates the number of bytes the content of this network record would have consumed on the network based on the\n * uncompressed size (totalBytes). Uses the actual transfer size from the network record if applicable,\n * minus the size of the response headers.\n *\n * @param totalBytes Uncompressed size of the resource\n */\nexport function estimateCompressedContentSize(\n request: Types.Events.SyntheticNetworkRequest|undefined, totalBytes: number,\n resourceType: Protocol.Network.ResourceType): number {\n if (!request || isRequestServedFromBrowserCache(request)) {\n // We don't know how many bytes this asset used on the network, but we can guess it was\n // roughly the size of the content gzipped.\n // See https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/optimize-encoding-and-transfer for specific CSS/Script examples\n // See https://discuss.httparchive.org/t/file-size-and-compression-savings/145 for fallback multipliers\n switch (resourceType) {\n case 'Stylesheet':\n // Stylesheets tend to compress extremely well.\n return Math.round(totalBytes * 0.2);\n case 'Script':\n case 'Document':\n // Scripts and HTML compress fairly well too.\n return Math.round(totalBytes * 0.33);\n default:\n // Otherwise we'll just fallback to the average savings in HTTPArchive\n return Math.round(totalBytes * 0.5);\n }\n }\n\n // Get the size of the response body on the network.\n const {transferSize, resourceSize} = getRequestSizes(request);\n let contentTransferSize = transferSize;\n if (!isRequestCompressed(request)) {\n // This is not compressed, so we can use resourceSize directly.\n // This would be equivalent to transfer size minus headers transfer size, but transfer size\n // may also include bytes for SSL connection etc.\n contentTransferSize = resourceSize;\n }\n // TODO(cjamcl): Get \"responseHeadersTransferSize\" in Network handler.\n // else if (request.responseHeadersTransferSize) {\n // // Subtract the size of the encoded headers.\n // contentTransferSize =\n // Math.max(0, contentTransferSize - request.responseHeadersTransferSize);\n // }\n\n if (request.args.data.resourceType === resourceType) {\n // This was a regular standalone asset, just use the transfer size.\n return contentTransferSize;\n }\n\n // This was an asset that was inlined in a different resource type (e.g. HTML document).\n // Use the compression ratio of the resource to estimate the total transferred bytes.\n // Get the compression ratio, if it's an invalid number, assume no compression.\n const compressionRatio = Number.isFinite(resourceSize) && resourceSize > 0 ? (contentTransferSize / resourceSize) : 1;\n return Math.round(totalBytes * compressionRatio);\n}\n\n/**\n * Utility function to estimate the ratio of the compression of a script.\n * This excludes the size of the response headers.\n */\nexport function estimateCompressionRatioForScript(script: Handlers.ModelHandlers.Scripts.Script): number {\n if (!script.request) {\n // Can't find request, so just use 1.\n return 1;\n }\n\n const request = script.request;\n const contentLength = request.args.data.decodedBodyLength ?? script.content?.length ?? 0;\n const compressedSize = estimateCompressedContentSize(request, contentLength, Protocol.Network.ResourceType.Script);\n if (contentLength === 0 || compressedSize === 0) {\n return 1;\n }\n\n const compressionRatio = compressedSize / contentLength;\n return compressionRatio;\n}\n\nexport function calculateDocFirstByteTs(docRequest: Types.Events.SyntheticNetworkRequest): Types.Timing.Micro|null {\n if (docRequest.args.data.protocol === 'file') {\n // file: requests do not have timings\n return docRequest.ts;\n }\n\n const timing = docRequest.args.data.timing;\n if (!timing) {\n // Older traces do not have timings.\n return null;\n }\n\n // Time that first byte (headers) are received.\n // For older traces, receiveHeadersStart can be missing (ex: web.dev.json.gz).\n // In that case use the headers end timing, which should be pretty close to when\n // the headers start.\n return Types.Timing.Micro(\n Helpers.Timing.secondsToMicro(timing.requestTime) +\n Helpers.Timing.milliToMicro(timing.receiveHeadersStart ?? timing.receiveHeadersEnd));\n}\n\n/**\n * Calculates the trace bounds for the given insight that are relevant.\n *\n * Uses the insight's overlays to determine the relevant trace bounds. If there are\n * no overlays, falls back to the insight set's navigation bounds.\n */\nexport function insightBounds(\n insight: InsightModel, insightSetBounds: Types.Timing.TraceWindowMicro): Types.Timing.TraceWindowMicro {\n const overlays = insight.createOverlays?.() ?? [];\n const windows = overlays.map(Helpers.Timing.traceWindowFromOverlay).filter(bounds => !!bounds);\n const overlaysBounds = Helpers.Timing.combineTraceWindowsMicro(windows);\n if (overlaysBounds) {\n return overlaysBounds;\n }\n\n return insightSetBounds;\n}\n"]}
1
+ {"version":3,"file":"Common.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/insights/Common.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 The Chromium Authors\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 Protocol from '../../../generated/protocol.js';\nimport type * as CrUXManager from '../../crux-manager/crux-manager.js';\nimport type * as Handlers from '../handlers/handlers.js';\nimport * as Helpers from '../helpers/helpers.js';\nimport type * as Lantern from '../lantern/lantern.js';\nimport * as Types from '../types/types.js';\n\nimport {getLogNormalScore} from './Statistics.js';\nimport {\n InsightKeys,\n type InsightModel,\n type InsightModels,\n type InsightSet,\n type InsightSetContext,\n type MetricSavings,\n} from './types.js';\n\nconst GRAPH_SAVINGS_PRECISION = 50;\n\nexport function getInsight<InsightName extends keyof InsightModels>(\n insightName: InsightName, insightSet: InsightSet): InsightModels[InsightName] {\n return insightSet.model[insightName];\n}\n\nexport function getLCP(insightSet: InsightSet):\n {value: Types.Timing.Micro, event: Types.Events.AnyLargestContentfulPaintCandidate}|null {\n const insight = getInsight(InsightKeys.LCP_BREAKDOWN, insightSet);\n if (!insight || !insight.lcpMs || !insight.lcpEvent) {\n return null;\n }\n\n const value = Helpers.Timing.milliToMicro(insight.lcpMs);\n return {value, event: insight.lcpEvent};\n}\n\nexport function getINP(insightSet: InsightSet):\n {value: Types.Timing.Micro, event: Types.Events.SyntheticInteractionPair}|null {\n const insight = getInsight(InsightKeys.INP_BREAKDOWN, insightSet);\n if (!insight?.longestInteractionEvent?.dur) {\n return null;\n }\n\n const value = insight.longestInteractionEvent.dur;\n return {value, event: insight.longestInteractionEvent};\n}\n\nexport function getCLS(insightSet: InsightSet): {value: number, worstClusterEvent: Types.Events.Event|null} {\n const insight = getInsight(InsightKeys.CLS_CULPRITS, insightSet);\n if (!insight) {\n // Unlike the other metrics, there is always a value for CLS even with no data.\n return {value: 0, worstClusterEvent: null};\n }\n\n // TODO(cjamcl): the CLS insight should be doing this for us.\n let maxScore = 0;\n let worstCluster;\n for (const cluster of insight.clusters) {\n if (cluster.clusterCumulativeScore > maxScore) {\n maxScore = cluster.clusterCumulativeScore;\n worstCluster = cluster;\n }\n }\n\n return {value: maxScore, worstClusterEvent: worstCluster ?? null};\n}\n\nexport function evaluateLCPMetricScore(value: Types.Timing.Milli): number {\n return getLogNormalScore({p10: 2500, median: 4000}, value);\n}\n\nexport function evaluateINPMetricScore(value: Types.Timing.Milli): number {\n return getLogNormalScore({p10: 200, median: 500}, value);\n}\n\nexport function evaluateCLSMetricScore(value: number): number {\n return getLogNormalScore({p10: 0.1, median: 0.25}, value);\n}\n\nexport interface CrUXFieldMetricTimingResult {\n value: Types.Timing.Micro;\n pageScope: CrUXManager.PageScope;\n}\nexport interface CrUXFieldMetricNumberResult {\n value: number;\n pageScope: CrUXManager.PageScope;\n}\nexport interface CrUXFieldMetricResults {\n fcp: CrUXFieldMetricTimingResult|null;\n lcp: CrUXFieldMetricTimingResult|null;\n inp: CrUXFieldMetricTimingResult|null;\n cls: CrUXFieldMetricNumberResult|null;\n lcpBreakdown: {\n ttfb: CrUXFieldMetricTimingResult|null,\n loadDelay: CrUXFieldMetricTimingResult|null,\n loadDuration: CrUXFieldMetricTimingResult|null,\n renderDelay: CrUXFieldMetricTimingResult|null,\n };\n}\n\nfunction getPageResult(\n cruxFieldData: CrUXManager.PageResult[], url: string, origin: string,\n scope: CrUXManager.Scope|null = null): CrUXManager.PageResult|undefined {\n return cruxFieldData.find(result => {\n const key = scope ? result[`${scope.pageScope}-${scope.deviceScope}`]?.record.key :\n (result['url-ALL'] || result['origin-ALL'])?.record.key;\n return (key?.url && key.url === url) || (key?.origin && key.origin === origin);\n });\n}\n\nfunction getMetricResult(\n pageResult: CrUXManager.PageResult, name: CrUXManager.StandardMetricNames,\n scope: CrUXManager.Scope|null = null): CrUXFieldMetricNumberResult|null {\n const scopes: Array<{pageScope: CrUXManager.PageScope, deviceScope: CrUXManager.DeviceScope}> = [];\n if (scope) {\n scopes.push(scope);\n } else {\n scopes.push({pageScope: 'url', deviceScope: 'ALL'});\n scopes.push({pageScope: 'origin', deviceScope: 'ALL'});\n }\n\n for (const scope of scopes) {\n const key = `${scope.pageScope}-${scope.deviceScope}` as const;\n let value = pageResult[key]?.record.metrics[name]?.percentiles?.p75;\n if (typeof value === 'string') {\n value = Number(value);\n }\n if (typeof value === 'number' && Number.isFinite(value)) {\n return {value, pageScope: scope.pageScope};\n }\n }\n\n return null;\n}\n\nfunction getMetricTimingResult(\n pageResult: CrUXManager.PageResult, name: CrUXManager.StandardMetricNames,\n scope: CrUXManager.Scope|null = null): CrUXFieldMetricTimingResult|null {\n const result = getMetricResult(pageResult, name, scope);\n if (result) {\n const valueMs = result.value as Types.Timing.Milli;\n return {value: Helpers.Timing.milliToMicro(valueMs), pageScope: result.pageScope};\n }\n\n return null;\n}\n\nexport function getFieldMetricsForInsightSet(\n insightSet: InsightSet, metadata: Types.File.MetaData|null,\n scope: CrUXManager.Scope|null = null): CrUXFieldMetricResults|null {\n const cruxFieldData = metadata?.cruxFieldData;\n if (!cruxFieldData) {\n return null;\n }\n\n const pageResult = getPageResult(cruxFieldData, insightSet.url.href, insightSet.url.origin, scope);\n if (!pageResult) {\n return null;\n }\n\n return {\n fcp: getMetricTimingResult(pageResult, 'first_contentful_paint', scope),\n lcp: getMetricTimingResult(pageResult, 'largest_contentful_paint', scope),\n inp: getMetricTimingResult(pageResult, 'interaction_to_next_paint', scope),\n cls: getMetricResult(pageResult, 'cumulative_layout_shift', scope),\n lcpBreakdown: {\n ttfb: getMetricTimingResult(pageResult, 'largest_contentful_paint_image_time_to_first_byte', scope),\n loadDelay: getMetricTimingResult(pageResult, 'largest_contentful_paint_image_resource_load_delay', scope),\n loadDuration: getMetricTimingResult(pageResult, 'largest_contentful_paint_image_resource_load_duration', scope),\n renderDelay: getMetricTimingResult(pageResult, 'largest_contentful_paint_image_element_render_delay', scope),\n }\n };\n}\n\nexport function calculateMetricWeightsForSorting(\n insightSet: InsightSet, metadata: Types.File.MetaData|null): {lcp: number, inp: number, cls: number} {\n const weights = {\n lcp: 1 / 3,\n inp: 1 / 3,\n cls: 1 / 3,\n };\n\n const cruxFieldData = metadata?.cruxFieldData;\n if (!cruxFieldData) {\n return weights;\n }\n\n const fieldMetrics = getFieldMetricsForInsightSet(insightSet, metadata);\n if (!fieldMetrics) {\n return weights;\n }\n\n const fieldLcp = fieldMetrics.lcp?.value ?? null;\n const fieldInp = fieldMetrics.inp?.value ?? null;\n const fieldCls = fieldMetrics.cls?.value ?? null;\n const fieldLcpScore = fieldLcp !== null ? evaluateLCPMetricScore(Helpers.Timing.microToMilli(fieldLcp)) : 0;\n const fieldInpScore = fieldInp !== null ? evaluateINPMetricScore(Helpers.Timing.microToMilli(fieldInp)) : 0;\n const fieldClsScore = fieldCls !== null ? evaluateCLSMetricScore(fieldCls) : 0;\n const fieldLcpScoreInverted = 1 - fieldLcpScore;\n const fieldInpScoreInverted = 1 - fieldInpScore;\n const fieldClsScoreInverted = 1 - fieldClsScore;\n const invertedSum = fieldLcpScoreInverted + fieldInpScoreInverted + fieldClsScoreInverted;\n if (!invertedSum) {\n return weights;\n }\n\n weights.lcp = fieldLcpScoreInverted / invertedSum;\n weights.inp = fieldInpScoreInverted / invertedSum;\n weights.cls = fieldClsScoreInverted / invertedSum;\n\n return weights;\n}\n\n/**\n * Simulates the provided graph before and after the byte savings from `wastedBytesByRequestId` are applied.\n */\nfunction estimateSavingsWithGraphs(\n wastedBytesByRequestId: Map<string, number>, simulator: Lantern.Simulation.Simulator,\n graph: Lantern.Graph.Node): Types.Timing.Milli {\n const simulationBeforeChanges = simulator.simulate(graph);\n\n const originalTransferSizes = new Map<string, number>();\n graph.traverse(node => {\n if (node.type !== 'network') {\n return;\n }\n const wastedBytes = wastedBytesByRequestId.get(node.request.requestId);\n if (!wastedBytes) {\n return;\n }\n\n const original = node.request.transferSize;\n originalTransferSizes.set(node.request.requestId, original);\n\n node.request.transferSize = Math.max(original - wastedBytes, 0);\n });\n\n const simulationAfterChanges = simulator.simulate(graph);\n\n // Restore the original transfer size after we've done our simulation\n graph.traverse(node => {\n if (node.type !== 'network') {\n return;\n }\n const originalTransferSize = originalTransferSizes.get(node.request.requestId);\n if (originalTransferSize === undefined) {\n return;\n }\n node.request.transferSize = originalTransferSize;\n });\n\n let savings = simulationBeforeChanges.timeInMs - simulationAfterChanges.timeInMs;\n savings = Math.round(savings / GRAPH_SAVINGS_PRECISION) * GRAPH_SAVINGS_PRECISION;\n return Types.Timing.Milli(savings);\n}\n\n/**\n * Estimates the FCP & LCP savings for wasted bytes in `wastedBytesByRequestId`.\n */\nexport function metricSavingsForWastedBytes(\n wastedBytesByRequestId: Map<string, number>, context: InsightSetContext): MetricSavings|undefined {\n if (!context.navigation || !context.lantern) {\n return;\n }\n\n if (!wastedBytesByRequestId.size) {\n return {FCP: Types.Timing.Milli(0), LCP: Types.Timing.Milli(0)};\n }\n\n const simulator = context.lantern.simulator;\n const fcpGraph = context.lantern.metrics.firstContentfulPaint.optimisticGraph;\n const lcpGraph = context.lantern.metrics.largestContentfulPaint.optimisticGraph;\n\n return {\n FCP: estimateSavingsWithGraphs(wastedBytesByRequestId, simulator, fcpGraph),\n LCP: estimateSavingsWithGraphs(wastedBytesByRequestId, simulator, lcpGraph),\n };\n}\n\n/**\n * Returns whether the network request was sent encoded.\n */\nexport function isRequestCompressed(request: Types.Events.SyntheticNetworkRequest): boolean {\n if (!request.args.data.responseHeaders) {\n return false;\n }\n\n // FYI: In Lighthouse, older devtools logs (like our test fixtures) seems to be\n // lower case, while modern logs are Cased-Like-This.\n const patterns = [\n /^content-encoding$/i, /^x-content-encoding-over-network$/i,\n /^x-original-content-encoding$/i, // Lightrider.\n ];\n const compressionTypes = ['gzip', 'br', 'deflate', 'zstd'];\n return request.args.data.responseHeaders.some(\n header => patterns.some(p => header.name.match(p)) && compressionTypes.includes(header.value));\n}\n\nexport function isRequestServedFromBrowserCache(request: Types.Events.SyntheticNetworkRequest): boolean {\n if (!request.args.data.responseHeaders || request.args.data.failed) {\n return false;\n }\n\n // Not Modified?\n if (request.args.data.statusCode === 304) {\n return true;\n }\n\n // TODO: for some reason ResourceReceiveResponse events never show a 304 status\n // code, so the above is never gonna work. For now, fall back to a dirty check of\n // looking at the ratio of transfer size and resource size. If it's really small,\n // we certainly did not use the network to fetch it.\n\n const {transferSize, resourceSize} = getRequestSizes(request);\n const ratio = resourceSize ? transferSize / resourceSize : 0;\n if (ratio < 0.01) {\n return true;\n }\n\n return false;\n}\n\nfunction getRequestSizes(request: Types.Events.SyntheticNetworkRequest): {resourceSize: number, transferSize: number} {\n const resourceSize = request.args.data.decodedBodyLength;\n const transferSize = request.args.data.encodedDataLength;\n return {resourceSize, transferSize};\n}\n\n/**\n * Estimates the number of bytes the content of this network record would have consumed on the network based on the\n * uncompressed size (totalBytes). Uses the actual transfer size from the network record if applicable,\n * minus the size of the response headers.\n *\n * @param totalBytes Uncompressed size of the resource\n */\nexport function estimateCompressedContentSize(\n request: Types.Events.SyntheticNetworkRequest|undefined, totalBytes: number,\n resourceType: Protocol.Network.ResourceType): number {\n if (!request || isRequestServedFromBrowserCache(request)) {\n // We don't know how many bytes this asset used on the network, but we can guess it was\n // roughly the size of the content gzipped.\n // See https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/optimize-encoding-and-transfer for specific CSS/Script examples\n // See https://discuss.httparchive.org/t/file-size-and-compression-savings/145 for fallback multipliers\n switch (resourceType) {\n case 'Stylesheet':\n // Stylesheets tend to compress extremely well.\n return Math.round(totalBytes * 0.2);\n case 'Script':\n case 'Document':\n // Scripts and HTML compress fairly well too.\n return Math.round(totalBytes * 0.33);\n default:\n // Otherwise we'll just fallback to the average savings in HTTPArchive\n return Math.round(totalBytes * 0.5);\n }\n }\n\n // Get the size of the response body on the network.\n const {transferSize, resourceSize} = getRequestSizes(request);\n let contentTransferSize = transferSize;\n if (!isRequestCompressed(request)) {\n // This is not compressed, so we can use resourceSize directly.\n // This would be equivalent to transfer size minus headers transfer size, but transfer size\n // may also include bytes for SSL connection etc.\n contentTransferSize = resourceSize;\n }\n // TODO(cjamcl): Get \"responseHeadersTransferSize\" in Network handler.\n // else if (request.responseHeadersTransferSize) {\n // // Subtract the size of the encoded headers.\n // contentTransferSize =\n // Math.max(0, contentTransferSize - request.responseHeadersTransferSize);\n // }\n\n if (request.args.data.resourceType === resourceType) {\n // This was a regular standalone asset, just use the transfer size.\n return contentTransferSize;\n }\n\n // This was an asset that was inlined in a different resource type (e.g. HTML document).\n // Use the compression ratio of the resource to estimate the total transferred bytes.\n // Get the compression ratio, if it's an invalid number, assume no compression.\n const compressionRatio = Number.isFinite(resourceSize) && resourceSize > 0 ? (contentTransferSize / resourceSize) : 1;\n return Math.round(totalBytes * compressionRatio);\n}\n\n/**\n * Utility function to estimate the ratio of the compression of a script.\n * This excludes the size of the response headers.\n */\nexport function estimateCompressionRatioForScript(script: Handlers.ModelHandlers.Scripts.Script): number {\n if (!script.request) {\n // Can't find request, so just use 1.\n return 1;\n }\n\n const request = script.request;\n const contentLength = request.args.data.decodedBodyLength ?? script.content?.length ?? 0;\n const compressedSize = estimateCompressedContentSize(request, contentLength, Protocol.Network.ResourceType.Script);\n if (contentLength === 0 || compressedSize === 0) {\n return 1;\n }\n\n const compressionRatio = compressedSize / contentLength;\n return compressionRatio;\n}\n\nexport function calculateDocFirstByteTs(docRequest: Types.Events.SyntheticNetworkRequest): Types.Timing.Micro|null {\n if (docRequest.args.data.protocol === 'file') {\n // file: requests do not have timings\n return docRequest.ts;\n }\n\n const timing = docRequest.args.data.timing;\n if (!timing) {\n // Older traces do not have timings.\n return null;\n }\n\n // Time that first byte (headers) are received.\n // For older traces, receiveHeadersStart can be missing (ex: web.dev.json.gz).\n // In that case use the headers end timing, which should be pretty close to when\n // the headers start.\n return Types.Timing.Micro(\n Helpers.Timing.secondsToMicro(timing.requestTime) +\n Helpers.Timing.milliToMicro(timing.receiveHeadersStart ?? timing.receiveHeadersEnd));\n}\n\n/**\n * Calculates the trace bounds for the given insight that are relevant.\n *\n * Uses the insight's overlays to determine the relevant trace bounds. If there are\n * no overlays, falls back to the insight set's navigation bounds.\n */\nexport function insightBounds(\n insight: InsightModel, insightSetBounds: Types.Timing.TraceWindowMicro): Types.Timing.TraceWindowMicro {\n const overlays = insight.createOverlays?.() ?? [];\n const windows = overlays.map(Helpers.Timing.traceWindowFromOverlay).filter(bounds => !!bounds);\n const overlaysBounds = Helpers.Timing.combineTraceWindowsMicro(windows);\n if (overlaysBounds) {\n return overlaysBounds;\n }\n\n return insightSetBounds;\n}\n"]}
@@ -10,7 +10,7 @@ export declare const UIStrings: {
10
10
  /**
11
11
  * @description Description of an insight that recommends reducing the size of the DOM tree as a means to improve page responsiveness. "DOM" is an acronym and should not be translated. "layout reflows" are when the browser will recompute the layout of content on the page.
12
12
  */
13
- readonly description: "A large DOM can increase the duration of style calculations and layout reflows, impacting page responsiveness. A large DOM will also increase memory usage. [Learn how to avoid an excessive DOM size](https://developer.chrome.com/docs/lighthouse/performance/dom-size/).";
13
+ readonly description: "A large DOM can increase the duration of style calculations and layout reflows, impacting page responsiveness. A large DOM will also increase memory usage. [Learn how to avoid an excessive DOM size](https://developer.chrome.com/docs/performance/insights/dom-size).";
14
14
  /**
15
15
  * @description Header for a column containing the names of statistics as opposed to the actual statistic values.
16
16
  */
@@ -14,7 +14,7 @@ export const UIStrings = {
14
14
  /**
15
15
  * @description Description of an insight that recommends reducing the size of the DOM tree as a means to improve page responsiveness. "DOM" is an acronym and should not be translated. "layout reflows" are when the browser will recompute the layout of content on the page.
16
16
  */
17
- description: 'A large DOM can increase the duration of style calculations and layout reflows, impacting page responsiveness. A large DOM will also increase memory usage. [Learn how to avoid an excessive DOM size](https://developer.chrome.com/docs/lighthouse/performance/dom-size/).',
17
+ description: 'A large DOM can increase the duration of style calculations and layout reflows, impacting page responsiveness. A large DOM will also increase memory usage. [Learn how to avoid an excessive DOM size](https://developer.chrome.com/docs/performance/insights/dom-size).',
18
18
  /**
19
19
  * @description Header for a column containing the names of statistics as opposed to the actual statistic values.
20
20
  */
@@ -73,6 +73,7 @@ function finalize(partialModel) {
73
73
  strings: UIStrings,
74
74
  title: i18nString(UIStrings.title),
75
75
  description: i18nString(UIStrings.description),
76
+ docs: 'https://developer.chrome.com/docs/performance/insights/dom-size',
76
77
  category: InsightCategory.INP,
77
78
  state: relatedEvents.length > 0 ? 'informative' : 'pass',
78
79
  ...partialModel,
@@ -1 +1 @@
1
- {"version":3,"file":"DOMSize.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/insights/DOMSize.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 The Chromium Authors\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 * as i18n from '../../../core/i18n/i18n.js';\nimport * as Handlers from '../handlers/handlers.js';\nimport * as Helpers from '../helpers/helpers.js';\nimport * as Types from '../types/types.js';\n\nimport {\n InsightCategory,\n InsightKeys,\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 recommends reducing the size of the DOM tree as a means to improve page responsiveness. \"DOM\" is an acronym and should not be translated.\n */\n title: 'Optimize DOM size',\n /**\n * @description Description of an insight that recommends reducing the size of the DOM tree as a means to improve page responsiveness. \"DOM\" is an acronym and should not be translated. \"layout reflows\" are when the browser will recompute the layout of content on the page.\n */\n description:\n 'A large DOM can increase the duration of style calculations and layout reflows, impacting page responsiveness. A large DOM will also increase memory usage. [Learn how to avoid an excessive DOM size](https://developer.chrome.com/docs/lighthouse/performance/dom-size/).',\n /**\n * @description Header for a column containing the names of statistics as opposed to the actual statistic values.\n */\n statistic: 'Statistic',\n /**\n * @description Header for a column containing the value of a statistic.\n */\n value: 'Value',\n /**\n * @description Header for a column containing the page element related to a statistic.\n */\n element: 'Element',\n /**\n * @description Label for a value representing the total number of elements on the page.\n */\n totalElements: 'Total elements',\n /**\n * @description Label for a value representing the maximum depth of the Document Object Model (DOM). \"DOM\" is a acronym and should not be translated.\n */\n maxDOMDepth: 'DOM depth',\n /**\n * @description Label for a value representing the maximum number of child elements of any parent element on the page.\n */\n maxChildren: 'Most children',\n /**\n * @description Text for a section.\n */\n topUpdatesDescription:\n 'These are the largest layout and style recalculation events. Their performance impact may be reduced by making the DOM simpler.',\n /**\n * @description Label used for a time duration.\n */\n duration: 'Duration',\n /**\n * @description Message displayed in a table detailing how big a layout (rendering) is.\n * @example {134} PH1\n */\n largeLayout: 'Layout ({PH1} objects)',\n /**\n * @description Message displayed in a table detailing how big a style recalculation (rendering) is.\n * @example {134} PH1\n */\n largeStyleRecalc: 'Style recalculation ({PH1} elements)',\n} as const;\n\nconst str_ = i18n.i18n.registerUIStrings('models/trace/insights/DOMSize.ts', UIStrings);\nexport const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);\n\nconst DOM_SIZE_DURATION_THRESHOLD = Helpers.Timing.milliToMicro(Types.Timing.Milli(40));\n\n// These thresholds were selected to maximize the number of long (>40ms) events above\n// the threshold while maximizing the number of short (<40ms) events below the threshold.\n// See go/rpp-dom-size-thresholds for the analysis that produced these thresholds.\nconst LAYOUT_OBJECTS_THRESHOLD = 100;\nconst STYLE_RECALC_ELEMENTS_THRESHOLD = 300;\n\nexport type DOMSizeInsightModel = InsightModel<typeof UIStrings, {\n largeLayoutUpdates: Types.Events.Layout[],\n largeStyleRecalcs: Types.Events.RecalcStyle[],\n largeUpdates: Array<\n {label: Common.UIString.LocalizedString, duration: Types.Timing.Milli, size: number, event: Types.Events.Event}>,\n maxDOMStats?: Types.Events.DOMStats,\n}>;\n\nfunction finalize(partialModel: PartialInsightModel<DOMSizeInsightModel>): DOMSizeInsightModel {\n const relatedEvents = [...partialModel.largeLayoutUpdates, ...partialModel.largeStyleRecalcs];\n return {\n insightKey: InsightKeys.DOM_SIZE,\n strings: UIStrings,\n title: i18nString(UIStrings.title),\n description: i18nString(UIStrings.description),\n category: InsightCategory.INP,\n state: relatedEvents.length > 0 ? 'informative' : 'pass',\n ...partialModel,\n relatedEvents,\n };\n}\n\nexport function isDomSizeInsight(model: InsightModel): model is DOMSizeInsightModel {\n return model.insightKey === InsightKeys.DOM_SIZE;\n}\n\nexport function generateInsight(data: Handlers.Types.HandlerData, context: InsightSetContext): DOMSizeInsightModel {\n const isWithinContext = (event: Types.Events.Event): boolean => Helpers.Timing.eventIsInBounds(event, context.bounds);\n\n const mainTid = context.navigation?.tid;\n\n const largeLayoutUpdates: Types.Events.Layout[] = [];\n const largeStyleRecalcs: Types.Events.RecalcStyle[] = [];\n\n const threads = Handlers.Threads.threadsInRenderer(data.Renderer, data.AuctionWorklets);\n for (const thread of threads) {\n if (thread.type !== Handlers.Threads.ThreadType.MAIN_THREAD) {\n continue;\n }\n\n if (mainTid === undefined) {\n // We won't have a specific thread ID to reference if the context does not have a navigation.\n // In this case, we'll just filter out any OOPIFs threads.\n if (!thread.processIsOnMainFrame) {\n continue;\n }\n } else if (thread.tid !== mainTid) {\n continue;\n }\n\n const rendererThread = data.Renderer.processes.get(thread.pid)?.threads.get(thread.tid);\n if (!rendererThread) {\n continue;\n }\n\n const {entries, layoutEvents, recalcStyleEvents} = rendererThread;\n if (!entries.length) {\n continue;\n }\n\n const first = entries[0];\n const last = entries[entries.length - 1];\n const timeRange =\n Helpers.Timing.traceWindowFromMicroSeconds(first.ts, Types.Timing.Micro(last.ts + (last.dur ?? 0)));\n if (!Helpers.Timing.boundsIncludeTimeRange({timeRange, bounds: context.bounds})) {\n continue;\n }\n\n for (const event of layoutEvents) {\n if (event.dur < DOM_SIZE_DURATION_THRESHOLD || !isWithinContext(event)) {\n continue;\n }\n\n const {dirtyObjects} = event.args.beginData;\n if (dirtyObjects > LAYOUT_OBJECTS_THRESHOLD) {\n largeLayoutUpdates.push(event);\n }\n }\n\n for (const event of recalcStyleEvents) {\n if (event.dur < DOM_SIZE_DURATION_THRESHOLD || !isWithinContext(event)) {\n continue;\n }\n\n const {elementCount} = event.args;\n if (elementCount > STYLE_RECALC_ELEMENTS_THRESHOLD) {\n largeStyleRecalcs.push(event);\n }\n }\n }\n\n const largeUpdates: DOMSizeInsightModel['largeUpdates'] = [\n ...largeLayoutUpdates.map(event => {\n const duration = (event.dur / 1000) as Types.Timing.Milli;\n const size = event.args.beginData.dirtyObjects;\n const label = i18nString(UIStrings.largeLayout, {PH1: size});\n return {label, duration, size, event};\n }),\n ...largeStyleRecalcs.map(event => {\n const duration = (event.dur / 1000) as Types.Timing.Milli;\n const size = event.args.elementCount;\n const label = i18nString(UIStrings.largeStyleRecalc, {PH1: size});\n return {label, duration, size, event};\n }),\n ].sort((a, b) => b.duration - a.duration).slice(0, 5);\n\n const domStatsEvents = data.DOMStats.domStatsByFrameId.get(context.frameId)?.filter(isWithinContext) ?? [];\n let maxDOMStats: Types.Events.DOMStats|undefined;\n for (const domStats of domStatsEvents) {\n // While recording a cross-origin navigation, there can be overlapping dom stats from before & after\n // the navigation which share a frameId. In this case we should also ensure the pid matches up with\n // the navigation we care about (i.e. from after the navigation event).\n const navigationPid = context.navigation?.pid;\n if (navigationPid && domStats.pid !== navigationPid) {\n continue;\n }\n\n if (!maxDOMStats || domStats.args.data.totalElements > maxDOMStats.args.data.totalElements) {\n maxDOMStats = domStats;\n }\n }\n\n return finalize({\n largeLayoutUpdates,\n largeStyleRecalcs,\n largeUpdates,\n maxDOMStats,\n });\n}\n\nexport function createOverlays(model: DOMSizeInsightModel): Types.Overlays.Overlay[] {\n const entries = [...model.largeStyleRecalcs, ...model.largeLayoutUpdates];\n return entries.map(entry => ({\n type: 'ENTRY_OUTLINE',\n entry,\n outlineReason: 'ERROR',\n }));\n}\n"]}
1
+ {"version":3,"file":"DOMSize.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/insights/DOMSize.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 The Chromium Authors\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 * as i18n from '../../../core/i18n/i18n.js';\nimport * as Handlers from '../handlers/handlers.js';\nimport * as Helpers from '../helpers/helpers.js';\nimport * as Types from '../types/types.js';\n\nimport {\n InsightCategory,\n InsightKeys,\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 recommends reducing the size of the DOM tree as a means to improve page responsiveness. \"DOM\" is an acronym and should not be translated.\n */\n title: 'Optimize DOM size',\n /**\n * @description Description of an insight that recommends reducing the size of the DOM tree as a means to improve page responsiveness. \"DOM\" is an acronym and should not be translated. \"layout reflows\" are when the browser will recompute the layout of content on the page.\n */\n description:\n 'A large DOM can increase the duration of style calculations and layout reflows, impacting page responsiveness. A large DOM will also increase memory usage. [Learn how to avoid an excessive DOM size](https://developer.chrome.com/docs/performance/insights/dom-size).',\n /**\n * @description Header for a column containing the names of statistics as opposed to the actual statistic values.\n */\n statistic: 'Statistic',\n /**\n * @description Header for a column containing the value of a statistic.\n */\n value: 'Value',\n /**\n * @description Header for a column containing the page element related to a statistic.\n */\n element: 'Element',\n /**\n * @description Label for a value representing the total number of elements on the page.\n */\n totalElements: 'Total elements',\n /**\n * @description Label for a value representing the maximum depth of the Document Object Model (DOM). \"DOM\" is a acronym and should not be translated.\n */\n maxDOMDepth: 'DOM depth',\n /**\n * @description Label for a value representing the maximum number of child elements of any parent element on the page.\n */\n maxChildren: 'Most children',\n /**\n * @description Text for a section.\n */\n topUpdatesDescription:\n 'These are the largest layout and style recalculation events. Their performance impact may be reduced by making the DOM simpler.',\n /**\n * @description Label used for a time duration.\n */\n duration: 'Duration',\n /**\n * @description Message displayed in a table detailing how big a layout (rendering) is.\n * @example {134} PH1\n */\n largeLayout: 'Layout ({PH1} objects)',\n /**\n * @description Message displayed in a table detailing how big a style recalculation (rendering) is.\n * @example {134} PH1\n */\n largeStyleRecalc: 'Style recalculation ({PH1} elements)',\n} as const;\n\nconst str_ = i18n.i18n.registerUIStrings('models/trace/insights/DOMSize.ts', UIStrings);\nexport const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);\n\nconst DOM_SIZE_DURATION_THRESHOLD = Helpers.Timing.milliToMicro(Types.Timing.Milli(40));\n\n// These thresholds were selected to maximize the number of long (>40ms) events above\n// the threshold while maximizing the number of short (<40ms) events below the threshold.\n// See go/rpp-dom-size-thresholds for the analysis that produced these thresholds.\nconst LAYOUT_OBJECTS_THRESHOLD = 100;\nconst STYLE_RECALC_ELEMENTS_THRESHOLD = 300;\n\nexport type DOMSizeInsightModel = InsightModel<typeof UIStrings, {\n largeLayoutUpdates: Types.Events.Layout[],\n largeStyleRecalcs: Types.Events.RecalcStyle[],\n largeUpdates: Array<\n {label: Common.UIString.LocalizedString, duration: Types.Timing.Milli, size: number, event: Types.Events.Event}>,\n maxDOMStats?: Types.Events.DOMStats,\n}>;\n\nfunction finalize(partialModel: PartialInsightModel<DOMSizeInsightModel>): DOMSizeInsightModel {\n const relatedEvents = [...partialModel.largeLayoutUpdates, ...partialModel.largeStyleRecalcs];\n return {\n insightKey: InsightKeys.DOM_SIZE,\n strings: UIStrings,\n title: i18nString(UIStrings.title),\n description: i18nString(UIStrings.description),\n docs: 'https://developer.chrome.com/docs/performance/insights/dom-size',\n category: InsightCategory.INP,\n state: relatedEvents.length > 0 ? 'informative' : 'pass',\n ...partialModel,\n relatedEvents,\n };\n}\n\nexport function isDomSizeInsight(model: InsightModel): model is DOMSizeInsightModel {\n return model.insightKey === InsightKeys.DOM_SIZE;\n}\n\nexport function generateInsight(data: Handlers.Types.HandlerData, context: InsightSetContext): DOMSizeInsightModel {\n const isWithinContext = (event: Types.Events.Event): boolean => Helpers.Timing.eventIsInBounds(event, context.bounds);\n\n const mainTid = context.navigation?.tid;\n\n const largeLayoutUpdates: Types.Events.Layout[] = [];\n const largeStyleRecalcs: Types.Events.RecalcStyle[] = [];\n\n const threads = Handlers.Threads.threadsInRenderer(data.Renderer, data.AuctionWorklets);\n for (const thread of threads) {\n if (thread.type !== Handlers.Threads.ThreadType.MAIN_THREAD) {\n continue;\n }\n\n if (mainTid === undefined) {\n // We won't have a specific thread ID to reference if the context does not have a navigation.\n // In this case, we'll just filter out any OOPIFs threads.\n if (!thread.processIsOnMainFrame) {\n continue;\n }\n } else if (thread.tid !== mainTid) {\n continue;\n }\n\n const rendererThread = data.Renderer.processes.get(thread.pid)?.threads.get(thread.tid);\n if (!rendererThread) {\n continue;\n }\n\n const {entries, layoutEvents, recalcStyleEvents} = rendererThread;\n if (!entries.length) {\n continue;\n }\n\n const first = entries[0];\n const last = entries[entries.length - 1];\n const timeRange =\n Helpers.Timing.traceWindowFromMicroSeconds(first.ts, Types.Timing.Micro(last.ts + (last.dur ?? 0)));\n if (!Helpers.Timing.boundsIncludeTimeRange({timeRange, bounds: context.bounds})) {\n continue;\n }\n\n for (const event of layoutEvents) {\n if (event.dur < DOM_SIZE_DURATION_THRESHOLD || !isWithinContext(event)) {\n continue;\n }\n\n const {dirtyObjects} = event.args.beginData;\n if (dirtyObjects > LAYOUT_OBJECTS_THRESHOLD) {\n largeLayoutUpdates.push(event);\n }\n }\n\n for (const event of recalcStyleEvents) {\n if (event.dur < DOM_SIZE_DURATION_THRESHOLD || !isWithinContext(event)) {\n continue;\n }\n\n const {elementCount} = event.args;\n if (elementCount > STYLE_RECALC_ELEMENTS_THRESHOLD) {\n largeStyleRecalcs.push(event);\n }\n }\n }\n\n const largeUpdates: DOMSizeInsightModel['largeUpdates'] = [\n ...largeLayoutUpdates.map(event => {\n const duration = (event.dur / 1000) as Types.Timing.Milli;\n const size = event.args.beginData.dirtyObjects;\n const label = i18nString(UIStrings.largeLayout, {PH1: size});\n return {label, duration, size, event};\n }),\n ...largeStyleRecalcs.map(event => {\n const duration = (event.dur / 1000) as Types.Timing.Milli;\n const size = event.args.elementCount;\n const label = i18nString(UIStrings.largeStyleRecalc, {PH1: size});\n return {label, duration, size, event};\n }),\n ].sort((a, b) => b.duration - a.duration).slice(0, 5);\n\n const domStatsEvents = data.DOMStats.domStatsByFrameId.get(context.frameId)?.filter(isWithinContext) ?? [];\n let maxDOMStats: Types.Events.DOMStats|undefined;\n for (const domStats of domStatsEvents) {\n // While recording a cross-origin navigation, there can be overlapping dom stats from before & after\n // the navigation which share a frameId. In this case we should also ensure the pid matches up with\n // the navigation we care about (i.e. from after the navigation event).\n const navigationPid = context.navigation?.pid;\n if (navigationPid && domStats.pid !== navigationPid) {\n continue;\n }\n\n if (!maxDOMStats || domStats.args.data.totalElements > maxDOMStats.args.data.totalElements) {\n maxDOMStats = domStats;\n }\n }\n\n return finalize({\n largeLayoutUpdates,\n largeStyleRecalcs,\n largeUpdates,\n maxDOMStats,\n });\n}\n\nexport function createOverlays(model: DOMSizeInsightModel): Types.Overlays.Overlay[] {\n const entries = [...model.largeStyleRecalcs, ...model.largeLayoutUpdates];\n return entries.map(entry => ({\n type: 'ENTRY_OUTLINE',\n entry,\n outlineReason: 'ERROR',\n }));\n}\n"]}
@@ -9,7 +9,7 @@ export declare const UIStrings: {
9
9
  /**
10
10
  * @description Description of an insight that provides a breakdown for how long it took to download the main document.
11
11
  */
12
- readonly description: "Your first network request is the most important. Reduce its latency by avoiding redirects, ensuring a fast server response, and enabling text compression.";
12
+ readonly description: "Your first network request is the most important. [Reduce its latency](https://developer.chrome.com/docs/performance/insights/document-latency) by avoiding redirects, ensuring a fast server response, and enabling text compression.";
13
13
  /**
14
14
  * @description Text to tell the user that the document request does not have redirects.
15
15
  */
@@ -14,7 +14,7 @@ export const UIStrings = {
14
14
  /**
15
15
  * @description Description of an insight that provides a breakdown for how long it took to download the main document.
16
16
  */
17
- description: 'Your first network request is the most important. Reduce its latency by avoiding redirects, ensuring a fast server response, and enabling text compression.',
17
+ description: 'Your first network request is the most important. [Reduce its latency](https://developer.chrome.com/docs/performance/insights/document-latency) by avoiding redirects, ensuring a fast server response, and enabling text compression.',
18
18
  /**
19
19
  * @description Text to tell the user that the document request does not have redirects.
20
20
  */
@@ -148,6 +148,7 @@ function finalize(partialModel) {
148
148
  strings: UIStrings,
149
149
  title: i18nString(UIStrings.title),
150
150
  description: i18nString(UIStrings.description),
151
+ docs: 'https://developer.chrome.com/docs/performance/insights/document-latency',
151
152
  category: InsightCategory.ALL,
152
153
  state: hasFailure ? 'fail' : 'pass',
153
154
  ...partialModel,