chrome-devtools-frontend 1.0.1526630 → 1.0.1529186

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 (339) hide show
  1. package/docs/ui_engineering.md +159 -0
  2. package/eslint.config.mjs +6 -1
  3. package/front_end/core/host/UserMetrics.ts +2 -1
  4. package/front_end/core/i18n/i18nImpl.ts +6 -1
  5. package/front_end/core/protocol_client/protocol_client.ts +1 -1
  6. package/front_end/core/root/Runtime.ts +38 -4
  7. package/front_end/core/sdk/CSSMatchedStyles.ts +50 -7
  8. package/front_end/core/sdk/CSSRule.ts +35 -6
  9. package/front_end/core/sdk/Connections.ts +2 -1
  10. package/front_end/core/sdk/DOMModel.ts +4 -0
  11. package/front_end/core/sdk/DebuggerModel.ts +5 -1
  12. package/front_end/core/sdk/NetworkManager.ts +267 -34
  13. package/front_end/core/sdk/PreloadingModel.ts +82 -17
  14. package/front_end/core/sdk/RehydratingConnection.snapshot.txt +1 -1
  15. package/front_end/core/sdk/RehydratingConnection.ts +29 -4
  16. package/front_end/core/sdk/ScopeTreeCache.ts +8 -3
  17. package/front_end/core/sdk/SourceMap.ts +41 -11
  18. package/front_end/core/sdk/SourceMapManager.ts +13 -2
  19. package/front_end/core/sdk/SourceMapScopesInfo.ts +49 -2
  20. package/front_end/core/sdk/TargetManager.ts +0 -22
  21. package/front_end/core/sdk/TraceObject.ts +8 -7
  22. package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshot.ts +81 -0
  23. package/front_end/entrypoints/inspector_main/InspectorMain.ts +3 -1
  24. package/front_end/entrypoints/main/GlobalAiButton.ts +1 -0
  25. package/front_end/entrypoints/main/MainImpl.ts +42 -28
  26. package/front_end/generated/InspectorBackendCommands.js +3 -2
  27. package/front_end/generated/SupportedCSSProperties.js +2 -0
  28. package/front_end/generated/protocol.ts +17 -3
  29. package/front_end/models/ai_assistance/BuiltInAi.ts +111 -0
  30. package/front_end/models/ai_assistance/ConversationHandler.ts +15 -14
  31. package/front_end/models/ai_assistance/ai_assistance.ts +53 -24
  32. package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.snapshot.txt +105 -0
  33. package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.ts +6 -1
  34. package/front_end/models/extensions/ExtensionView.ts +3 -0
  35. package/front_end/models/javascript_metadata/NativeFunctions.js +31 -27
  36. package/front_end/models/live-metrics/web-vitals-injected/web-vitals-injected.ts +31 -29
  37. package/front_end/models/persistence/NetworkPersistenceManager.ts +3 -5
  38. package/front_end/models/persistence/PersistenceImpl.ts +0 -5
  39. package/front_end/models/persistence/persistence-meta.ts +0 -31
  40. package/front_end/models/persistence/persistence.ts +0 -6
  41. package/front_end/models/source_map_scopes/NamesResolver.ts +5 -11
  42. package/front_end/models/stack_trace/Trie.ts +9 -0
  43. package/front_end/models/trace/lantern/types/Lantern.ts +1 -1
  44. package/front_end/panels/accessibility/AXBreadcrumbsPane.ts +1 -0
  45. package/front_end/panels/accessibility/AccessibilitySidebarView.ts +1 -0
  46. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +120 -113
  47. package/front_end/panels/ai_assistance/PatchWidget.ts +9 -8
  48. package/front_end/panels/ai_assistance/SelectWorkspaceDialog.ts +2 -0
  49. package/front_end/panels/ai_assistance/components/ChatView.ts +29 -29
  50. package/front_end/panels/ai_assistance/components/UserActionRow.ts +1 -0
  51. package/front_end/panels/animation/AnimationTimeline.ts +1 -0
  52. package/front_end/panels/application/CookieItemsView.ts +1 -0
  53. package/front_end/panels/application/KeyValueStorageItemsView.ts +1 -0
  54. package/front_end/panels/application/ServiceWorkerCacheViews.ts +2 -0
  55. package/front_end/panels/application/preloading/components/PreloadingDetailsReportView.ts +11 -5
  56. package/front_end/panels/application/preloading/components/PreloadingMismatchedHeadersGrid.ts +2 -2
  57. package/front_end/panels/application/preloading/components/PreloadingString.ts +7 -5
  58. package/front_end/panels/application/preloading/components/UsedPreloadingView.ts +22 -10
  59. package/front_end/panels/changes/CombinedDiffView.ts +1 -0
  60. package/front_end/{models/persistence → panels/common}/PersistenceUtils.ts +15 -17
  61. package/front_end/panels/common/common.ts +1 -0
  62. package/front_end/panels/console/ConsoleInsightTeaser.ts +369 -0
  63. package/front_end/panels/console/ConsolePanel.ts +2 -0
  64. package/front_end/panels/console/ConsolePrompt.ts +12 -2
  65. package/front_end/panels/console/ConsoleSidebar.ts +1 -1
  66. package/front_end/panels/console/ConsoleView.ts +12 -0
  67. package/front_end/panels/console/ConsoleViewMessage.ts +44 -0
  68. package/front_end/panels/{explain → console}/PromptBuilder.ts +12 -7
  69. package/front_end/panels/console/console-meta.ts +14 -0
  70. package/front_end/panels/console/console.ts +6 -0
  71. package/front_end/panels/console/consoleInsightTeaser.css +83 -0
  72. package/front_end/panels/coverage/CoverageListView.ts +29 -11
  73. package/front_end/panels/coverage/CoverageView.ts +292 -284
  74. package/front_end/panels/coverage/coverageView.css +17 -0
  75. package/front_end/panels/elements/ComputedStyleWidget.ts +1 -0
  76. package/front_end/panels/elements/LayoutPane.ts +1 -0
  77. package/front_end/panels/elements/NodeStackTraceWidget.ts +1 -0
  78. package/front_end/panels/elements/StylePropertyTreeElement.ts +5 -1
  79. package/front_end/panels/elements/stylePropertiesTreeOutline.css +17 -0
  80. package/front_end/panels/emulation/DeviceModeView.ts +2 -0
  81. package/front_end/panels/explain/ActionDelegate.ts +4 -2
  82. package/front_end/panels/explain/components/ConsoleInsight.ts +14 -12
  83. package/front_end/panels/explain/explain-meta.ts +7 -0
  84. package/front_end/panels/explain/explain.ts +0 -1
  85. package/front_end/panels/js_timeline/js_timeline-meta.ts +1 -1
  86. package/front_end/panels/layer_viewer/Layers3DView.ts +2 -0
  87. package/front_end/panels/lighthouse/LighthouseReportSelector.ts +1 -0
  88. package/front_end/panels/linear_memory_inspector/LinearMemoryInspectorPane.ts +1 -0
  89. package/front_end/panels/media/MainView.ts +1 -0
  90. package/front_end/panels/media/TickingFlameChart.ts +2 -0
  91. package/front_end/panels/network/BlockedURLsPane.ts +237 -108
  92. package/front_end/panels/network/EventSourceMessagesView.ts +1 -0
  93. package/front_end/panels/network/NetworkItemView.ts +1 -0
  94. package/front_end/panels/network/NetworkLogView.ts +9 -7
  95. package/front_end/panels/network/NetworkOverview.ts +1 -0
  96. package/front_end/panels/network/RequestCookiesView.ts +1 -0
  97. package/front_end/panels/network/RequestHTMLView.ts +1 -0
  98. package/front_end/panels/network/RequestInitiatorView.ts +1 -0
  99. package/front_end/panels/network/RequestPayloadView.ts +1 -0
  100. package/front_end/panels/network/RequestPreviewView.ts +1 -0
  101. package/front_end/panels/network/RequestResponseView.ts +1 -0
  102. package/front_end/panels/network/RequestTimingView.ts +2 -0
  103. package/front_end/panels/network/ResourceDirectSocketChunkView.ts +1 -0
  104. package/front_end/panels/network/ResourceWebSocketFrameView.ts +1 -0
  105. package/front_end/panels/network/components/RequestHeadersView.ts +2 -0
  106. package/front_end/panels/network/components/RequestTrustTokensView.ts +2 -0
  107. package/front_end/panels/performance_monitor/PerformanceMonitor.ts +2 -0
  108. package/front_end/panels/profiler/HeapSnapshotDataGrids.ts +2 -0
  109. package/front_end/panels/profiler/HeapSnapshotView.ts +7 -0
  110. package/front_end/panels/profiler/IsolateSelector.ts +1 -0
  111. package/front_end/panels/profiler/LiveHeapProfileView.ts +1 -0
  112. package/front_end/panels/profiler/ProfileView.ts +1 -0
  113. package/front_end/panels/protocol_monitor/ProtocolMonitor.ts +1 -0
  114. package/front_end/panels/recorder/RecorderPanel.ts +2 -0
  115. package/front_end/panels/screencast/ScreencastView.ts +1 -0
  116. package/front_end/panels/search/SearchView.ts +1 -0
  117. package/front_end/panels/settings/AISettingsTab.ts +3 -3
  118. package/front_end/{models/persistence → panels/settings}/EditFileSystemView.ts +3 -6
  119. package/front_end/panels/settings/WorkspaceSettingsTab.ts +4 -1
  120. package/front_end/panels/settings/emulation/components/UserAgentClientHintsForm.ts +2 -2
  121. package/front_end/panels/settings/settings.ts +2 -0
  122. package/front_end/panels/sources/AiCodeCompletionPlugin.ts +12 -0
  123. package/front_end/panels/sources/BreakpointsView.ts +1 -0
  124. package/front_end/panels/sources/DebuggerPlugin.ts +1 -0
  125. package/front_end/{models/persistence → panels/sources}/PersistenceActions.ts +8 -12
  126. package/front_end/panels/sources/TabbedEditorContainer.ts +2 -1
  127. package/front_end/panels/sources/UISourceCodeFrame.ts +17 -2
  128. package/front_end/panels/sources/sources-meta.ts +15 -0
  129. package/front_end/panels/sources/sources.ts +2 -0
  130. package/front_end/panels/timeline/README.md +2 -2
  131. package/front_end/panels/timeline/TimelineFlameChartDataProvider.ts +1 -1
  132. package/front_end/panels/timeline/TimelineFlameChartView.ts +4 -3
  133. package/front_end/panels/timeline/TimelineLayersView.ts +1 -0
  134. package/front_end/panels/timeline/TimelinePaintProfilerView.ts +114 -37
  135. package/front_end/panels/timeline/TimelinePanel.ts +43 -62
  136. package/front_end/panels/timeline/TimelineTreeView.ts +1 -0
  137. package/front_end/panels/timeline/components/LiveMetricsView.ts +4 -8
  138. package/front_end/panels/timeline/components/Sidebar.ts +2 -0
  139. package/front_end/panels/timeline/components/SidebarSingleInsightSet.ts +1 -1
  140. package/front_end/panels/timeline/components/insights/BaseInsightComponent.ts +7 -7
  141. package/front_end/panels/timeline/overlays/OverlaysImpl.ts +1 -1
  142. package/front_end/panels/timeline/overlays/components/EntryLabelOverlay.ts +4 -4
  143. package/front_end/panels/utils/utils.ts +2 -1
  144. package/front_end/panels/web_audio/WebAudioView.ts +1 -0
  145. package/front_end/third_party/chromium/README.chromium +1 -1
  146. package/front_end/third_party/diff/diff_match_patch.js +1 -1
  147. package/front_end/third_party/lighthouse/README.chromium +2 -2
  148. package/front_end/third_party/lighthouse/lighthouse-dt-bundle.js +1530 -2426
  149. package/front_end/third_party/lighthouse/locales/ar-XB.json +107 -455
  150. package/front_end/third_party/lighthouse/locales/ar.json +107 -455
  151. package/front_end/third_party/lighthouse/locales/bg.json +96 -444
  152. package/front_end/third_party/lighthouse/locales/ca.json +96 -444
  153. package/front_end/third_party/lighthouse/locales/cs.json +96 -444
  154. package/front_end/third_party/lighthouse/locales/da.json +96 -444
  155. package/front_end/third_party/lighthouse/locales/de.json +96 -444
  156. package/front_end/third_party/lighthouse/locales/el.json +96 -444
  157. package/front_end/third_party/lighthouse/locales/en-GB.json +96 -444
  158. package/front_end/third_party/lighthouse/locales/en-US.json +116 -467
  159. package/front_end/third_party/lighthouse/locales/en-XA.json +93 -441
  160. package/front_end/third_party/lighthouse/locales/en-XL.json +116 -467
  161. package/front_end/third_party/lighthouse/locales/es-419.json +96 -444
  162. package/front_end/third_party/lighthouse/locales/es.json +96 -444
  163. package/front_end/third_party/lighthouse/locales/fi.json +96 -444
  164. package/front_end/third_party/lighthouse/locales/fil.json +96 -444
  165. package/front_end/third_party/lighthouse/locales/fr.json +96 -444
  166. package/front_end/third_party/lighthouse/locales/he.json +118 -466
  167. package/front_end/third_party/lighthouse/locales/hi.json +96 -444
  168. package/front_end/third_party/lighthouse/locales/hr.json +100 -448
  169. package/front_end/third_party/lighthouse/locales/hu.json +96 -444
  170. package/front_end/third_party/lighthouse/locales/id.json +96 -444
  171. package/front_end/third_party/lighthouse/locales/it.json +96 -444
  172. package/front_end/third_party/lighthouse/locales/ja.json +96 -444
  173. package/front_end/third_party/lighthouse/locales/ko.json +97 -445
  174. package/front_end/third_party/lighthouse/locales/lt.json +96 -444
  175. package/front_end/third_party/lighthouse/locales/lv.json +97 -445
  176. package/front_end/third_party/lighthouse/locales/nl.json +96 -444
  177. package/front_end/third_party/lighthouse/locales/no.json +96 -444
  178. package/front_end/third_party/lighthouse/locales/pl.json +96 -444
  179. package/front_end/third_party/lighthouse/locales/pt-PT.json +96 -444
  180. package/front_end/third_party/lighthouse/locales/pt.json +97 -445
  181. package/front_end/third_party/lighthouse/locales/ro.json +97 -445
  182. package/front_end/third_party/lighthouse/locales/ru.json +96 -444
  183. package/front_end/third_party/lighthouse/locales/sk.json +96 -444
  184. package/front_end/third_party/lighthouse/locales/sl.json +96 -444
  185. package/front_end/third_party/lighthouse/locales/sr-Latn.json +96 -444
  186. package/front_end/third_party/lighthouse/locales/sr.json +96 -444
  187. package/front_end/third_party/lighthouse/locales/sv.json +96 -444
  188. package/front_end/third_party/lighthouse/locales/ta.json +96 -444
  189. package/front_end/third_party/lighthouse/locales/te.json +97 -445
  190. package/front_end/third_party/lighthouse/locales/th.json +96 -444
  191. package/front_end/third_party/lighthouse/locales/tr.json +96 -444
  192. package/front_end/third_party/lighthouse/locales/uk.json +96 -444
  193. package/front_end/third_party/lighthouse/locales/vi.json +96 -444
  194. package/front_end/third_party/lighthouse/locales/zh-HK.json +96 -444
  195. package/front_end/third_party/lighthouse/locales/zh-TW.json +97 -445
  196. package/front_end/third_party/lighthouse/locales/zh.json +96 -444
  197. package/front_end/third_party/lighthouse/report/bundle.d.ts +8 -14
  198. package/front_end/third_party/lighthouse/report/bundle.js +10 -49
  199. package/front_end/third_party/lighthouse/report-assets/report-generator.mjs +1 -1
  200. package/front_end/third_party/web-vitals/README.chromium +5 -8
  201. package/front_end/third_party/web-vitals/package/README.md +191 -152
  202. package/front_end/third_party/web-vitals/package/dist/modules/attribution/index.d.ts +0 -1
  203. package/front_end/third_party/web-vitals/package/dist/modules/attribution/index.js +0 -1
  204. package/front_end/third_party/web-vitals/package/dist/modules/attribution/onCLS.d.ts +2 -2
  205. package/front_end/third_party/web-vitals/package/dist/modules/attribution/onCLS.js +45 -26
  206. package/front_end/third_party/web-vitals/package/dist/modules/attribution/onFCP.d.ts +2 -2
  207. package/front_end/third_party/web-vitals/package/dist/modules/attribution/onFCP.js +3 -3
  208. package/front_end/third_party/web-vitals/package/dist/modules/attribution/onINP.d.ts +10 -10
  209. package/front_end/third_party/web-vitals/package/dist/modules/attribution/onINP.js +307 -206
  210. package/front_end/third_party/web-vitals/package/dist/modules/attribution/onLCP.d.ts +2 -2
  211. package/front_end/third_party/web-vitals/package/dist/modules/attribution/onLCP.js +69 -49
  212. package/front_end/third_party/web-vitals/package/dist/modules/attribution/onTTFB.d.ts +2 -2
  213. package/front_end/third_party/web-vitals/package/dist/modules/attribution/onTTFB.js +2 -2
  214. package/front_end/third_party/web-vitals/package/dist/modules/index.d.ts +0 -1
  215. package/front_end/third_party/web-vitals/package/dist/modules/index.js +0 -1
  216. package/front_end/third_party/web-vitals/package/dist/modules/lib/InteractionManager.d.ts +33 -0
  217. package/front_end/third_party/web-vitals/package/dist/modules/lib/InteractionManager.js +111 -0
  218. package/front_end/third_party/web-vitals/package/dist/modules/lib/LCPEntryManager.d.ts +4 -0
  219. package/front_end/third_party/web-vitals/package/dist/modules/{attribution/deprecated.js → lib/LCPEntryManager.js} +6 -7
  220. package/front_end/third_party/web-vitals/package/dist/modules/lib/LayoutShiftManager.d.ts +6 -0
  221. package/front_end/third_party/web-vitals/package/dist/modules/lib/LayoutShiftManager.js +44 -0
  222. package/front_end/third_party/web-vitals/package/dist/modules/lib/bindReporter.js +1 -1
  223. package/front_end/third_party/web-vitals/package/dist/modules/lib/generateUniqueID.js +1 -1
  224. package/front_end/third_party/web-vitals/package/dist/modules/lib/getActivationStart.js +1 -1
  225. package/front_end/third_party/web-vitals/package/dist/modules/lib/getNavigationEntry.js +5 -7
  226. package/front_end/third_party/web-vitals/package/dist/modules/lib/getSelector.d.ts +1 -1
  227. package/front_end/third_party/web-vitals/package/dist/modules/lib/getSelector.js +9 -12
  228. package/front_end/third_party/web-vitals/package/dist/modules/lib/getVisibilityWatcher.d.ts +1 -0
  229. package/front_end/third_party/web-vitals/package/dist/modules/lib/getVisibilityWatcher.js +52 -33
  230. package/front_end/third_party/web-vitals/package/dist/modules/lib/initMetric.d.ts +0 -2
  231. package/front_end/third_party/web-vitals/package/dist/modules/lib/initMetric.js +2 -2
  232. package/front_end/third_party/web-vitals/package/dist/modules/lib/initUnique.d.ts +6 -0
  233. package/front_end/third_party/web-vitals/package/dist/modules/{deprecated.js → lib/initUnique.js} +11 -4
  234. package/front_end/third_party/web-vitals/package/dist/modules/lib/observe.js +3 -6
  235. package/front_end/third_party/web-vitals/package/dist/modules/lib/polyfills/interactionCountPolyfill.js +6 -6
  236. package/front_end/third_party/web-vitals/package/dist/modules/lib/{whenIdle.d.ts → whenIdleOrHidden.d.ts} +1 -1
  237. package/front_end/third_party/web-vitals/package/dist/modules/lib/{whenIdle.js → whenIdleOrHidden.js} +10 -8
  238. package/front_end/third_party/web-vitals/package/dist/modules/onCLS.js +17 -35
  239. package/front_end/third_party/web-vitals/package/dist/modules/onFCP.js +3 -5
  240. package/front_end/third_party/web-vitals/package/dist/modules/onINP.d.ts +9 -7
  241. package/front_end/third_party/web-vitals/package/dist/modules/onINP.js +27 -19
  242. package/front_end/third_party/web-vitals/package/dist/modules/onLCP.js +33 -26
  243. package/front_end/third_party/web-vitals/package/dist/modules/onTTFB.js +2 -4
  244. package/front_end/third_party/web-vitals/package/dist/modules/types/base.d.ts +6 -5
  245. package/front_end/third_party/web-vitals/package/dist/modules/types/cls.d.ts +5 -3
  246. package/front_end/third_party/web-vitals/package/dist/modules/types/inp.d.ts +80 -33
  247. package/front_end/third_party/web-vitals/package/dist/modules/types/lcp.d.ts +6 -2
  248. package/front_end/third_party/web-vitals/package/dist/modules/types.d.ts +28 -4
  249. package/front_end/third_party/web-vitals/package/dist/modules/types.js +0 -1
  250. package/front_end/third_party/web-vitals/package/package.json +4 -10
  251. package/front_end/third_party/web-vitals/package/src/attribution/index.ts +0 -1
  252. package/front_end/third_party/web-vitals/package/src/attribution/onCLS.ts +58 -33
  253. package/front_end/third_party/web-vitals/package/src/attribution/onFCP.ts +4 -4
  254. package/front_end/third_party/web-vitals/package/src/attribution/onINP.ts +382 -258
  255. package/front_end/third_party/web-vitals/package/src/attribution/onLCP.ts +96 -69
  256. package/front_end/third_party/web-vitals/package/src/attribution/onTTFB.ts +3 -3
  257. package/front_end/third_party/web-vitals/package/src/index.ts +0 -1
  258. package/front_end/third_party/web-vitals/package/src/lib/InteractionManager.ts +146 -0
  259. package/front_end/third_party/web-vitals/package/src/{attribution/deprecated.ts → lib/LCPEntryManager.ts} +6 -9
  260. package/front_end/third_party/web-vitals/package/src/lib/LayoutShiftManager.ts +50 -0
  261. package/front_end/third_party/web-vitals/package/src/lib/bindReporter.ts +1 -1
  262. package/front_end/third_party/web-vitals/package/src/lib/generateUniqueID.ts +1 -1
  263. package/front_end/third_party/web-vitals/package/src/lib/getActivationStart.ts +1 -1
  264. package/front_end/third_party/web-vitals/package/src/lib/getNavigationEntry.ts +5 -8
  265. package/front_end/third_party/web-vitals/package/src/lib/getSelector.ts +12 -12
  266. package/front_end/third_party/web-vitals/package/src/lib/getVisibilityWatcher.ts +57 -35
  267. package/front_end/third_party/web-vitals/package/src/lib/initMetric.ts +2 -2
  268. package/front_end/third_party/web-vitals/package/src/{deprecated.ts → lib/initUnique.ts} +14 -8
  269. package/front_end/third_party/web-vitals/package/src/lib/observe.ts +3 -11
  270. package/front_end/third_party/web-vitals/package/src/lib/polyfills/interactionCountPolyfill.ts +12 -6
  271. package/front_end/third_party/web-vitals/package/src/lib/{whenIdle.ts → whenIdleOrHidden.ts} +10 -8
  272. package/front_end/third_party/web-vitals/package/src/onCLS.ts +17 -38
  273. package/front_end/third_party/web-vitals/package/src/onFCP.ts +3 -6
  274. package/front_end/third_party/web-vitals/package/src/onINP.ts +33 -28
  275. package/front_end/third_party/web-vitals/package/src/onLCP.ts +36 -29
  276. package/front_end/third_party/web-vitals/package/src/onTTFB.ts +2 -5
  277. package/front_end/third_party/web-vitals/package/src/types/base.ts +5 -5
  278. package/front_end/third_party/web-vitals/package/src/types/cls.ts +5 -3
  279. package/front_end/third_party/web-vitals/package/src/types/inp.ts +88 -33
  280. package/front_end/third_party/web-vitals/package/src/types/lcp.ts +6 -2
  281. package/front_end/third_party/web-vitals/package/src/types.ts +47 -4
  282. package/front_end/third_party/web-vitals/patches/0001-Add-onEachInteraction-to-onINP-options.patch +75 -0
  283. package/front_end/third_party/web-vitals/rebuild.sh +32 -18
  284. package/front_end/third_party/web-vitals/web-vitals-tsconfig.json +5 -10
  285. package/front_end/third_party/web-vitals/web-vitals.ts +0 -2
  286. package/front_end/ui/components/docs/console_insight/basic.ts +3 -2
  287. package/front_end/ui/components/legacy_wrapper/LegacyWrapper.ts +2 -0
  288. package/front_end/ui/components/text_editor/TextEditor.ts +0 -2
  289. package/front_end/ui/legacy/InspectorView.ts +2 -0
  290. package/front_end/ui/legacy/ListWidget.ts +2 -2
  291. package/front_end/ui/legacy/SplitWidget.ts +2 -0
  292. package/front_end/ui/legacy/TabbedPane.ts +1 -0
  293. package/front_end/ui/legacy/TargetCrashedScreen.ts +1 -0
  294. package/front_end/ui/legacy/UIUtils.ts +8 -19
  295. package/front_end/ui/legacy/ViewManager.ts +1 -0
  296. package/front_end/ui/legacy/components/color_picker/FormatPickerContextMenu.ts +7 -20
  297. package/front_end/ui/legacy/components/color_picker/Spectrum.ts +2 -0
  298. package/front_end/ui/legacy/components/cookie_table/CookiesTable.ts +1 -0
  299. package/front_end/ui/legacy/components/inline_editor/BezierEditor.ts +1 -0
  300. package/front_end/ui/legacy/components/perf_ui/ChartViewport.ts +1 -0
  301. package/front_end/ui/legacy/components/quick_open/FilteredListWidget.ts +1 -0
  302. package/front_end/ui/legacy/components/source_frame/FontView.ts +1 -0
  303. package/front_end/ui/legacy/components/source_frame/ImageView.ts +1 -0
  304. package/front_end/ui/legacy/components/source_frame/JSONView.ts +1 -0
  305. package/front_end/ui/legacy/components/source_frame/SourceFrame.ts +1 -0
  306. package/front_end/ui/legacy/components/source_frame/StreamingContentHexView.ts +2 -0
  307. package/front_end/ui/visual_logging/KnownContextValues.ts +25 -0
  308. package/mcp/README.md +7 -0
  309. package/mcp/mcp.ts +8 -0
  310. package/package.json +1 -1
  311. package/front_end/models/live-metrics/web-vitals-injected/OnEachInteraction.ts +0 -34
  312. package/front_end/third_party/web-vitals/package/attribution.d.ts +0 -16
  313. package/front_end/third_party/web-vitals/package/attribution.js +0 -18
  314. package/front_end/third_party/web-vitals/package/dist/modules/attribution/deprecated.d.ts +0 -7
  315. package/front_end/third_party/web-vitals/package/dist/modules/attribution/onFID.d.ts +0 -11
  316. package/front_end/third_party/web-vitals/package/dist/modules/attribution/onFID.js +0 -46
  317. package/front_end/third_party/web-vitals/package/dist/modules/deprecated.d.ts +0 -5
  318. package/front_end/third_party/web-vitals/package/dist/modules/lib/interactions.d.ts +0 -31
  319. package/front_end/third_party/web-vitals/package/dist/modules/lib/interactions.js +0 -107
  320. package/front_end/third_party/web-vitals/package/dist/modules/lib/onHidden.d.ts +0 -1
  321. package/front_end/third_party/web-vitals/package/dist/modules/lib/onHidden.js +0 -22
  322. package/front_end/third_party/web-vitals/package/dist/modules/lib/polyfills/firstInputPolyfill.d.ts +0 -7
  323. package/front_end/third_party/web-vitals/package/dist/modules/lib/polyfills/firstInputPolyfill.js +0 -147
  324. package/front_end/third_party/web-vitals/package/dist/modules/lib/polyfills/getFirstHiddenTimePolyfill.d.ts +0 -1
  325. package/front_end/third_party/web-vitals/package/dist/modules/lib/polyfills/getFirstHiddenTimePolyfill.js +0 -25
  326. package/front_end/third_party/web-vitals/package/dist/modules/onFID.d.ts +0 -13
  327. package/front_end/third_party/web-vitals/package/dist/modules/onFID.js +0 -70
  328. package/front_end/third_party/web-vitals/package/dist/modules/types/fid.d.ts +0 -46
  329. package/front_end/third_party/web-vitals/package/dist/modules/types/fid.js +0 -16
  330. package/front_end/third_party/web-vitals/package/src/attribution/onFID.ts +0 -62
  331. package/front_end/third_party/web-vitals/package/src/lib/interactions.ts +0 -139
  332. package/front_end/third_party/web-vitals/package/src/lib/onHidden.ts +0 -23
  333. package/front_end/third_party/web-vitals/package/src/lib/polyfills/firstInputPolyfill.ts +0 -174
  334. package/front_end/third_party/web-vitals/package/src/onFID.ts +0 -105
  335. package/front_end/third_party/web-vitals/package/src/types/fid.ts +0 -65
  336. package/front_end/ui/components/text_editor/textEditor.css +0 -18
  337. package/front_end/ui/legacy/inlineButton.css +0 -22
  338. /package/front_end/entrypoints/{rehydrated_devtools_app/rehydrated_devtools_app.ts → trace_app/trace_app.ts} +0 -0
  339. /package/front_end/{models/persistence → panels/settings}/editFileSystemView.css +0 -0
@@ -16,19 +16,17 @@
16
16
 
17
17
  import {getLoadState} from '../lib/getLoadState.js';
18
18
  import {getSelector} from '../lib/getSelector.js';
19
- import {
20
- longestInteractionList,
21
- entryPreProcessingCallbacks,
22
- longestInteractionMap,
23
- } from '../lib/interactions.js';
19
+ import {initUnique} from '../lib/initUnique.js';
20
+ import {InteractionManager, Interaction} from '../lib/InteractionManager.js';
24
21
  import {observe} from '../lib/observe.js';
25
- import {whenIdle} from '../lib/whenIdle.js';
22
+ import {whenIdleOrHidden} from '../lib/whenIdleOrHidden.js';
26
23
  import {onINP as unattributedOnINP} from '../onINP.js';
27
24
  import {
28
25
  INPAttribution,
26
+ INPAttributionReportOpts,
29
27
  INPMetric,
30
28
  INPMetricWithAttribution,
31
- ReportOpts,
29
+ INPLongestScriptSummary,
32
30
  } from '../types.js';
33
31
 
34
32
  interface pendingEntriesGroup {
@@ -48,289 +46,415 @@ interface pendingEntriesGroup {
48
46
  // keeping the most recent 50 should be more than sufficient.
49
47
  const MAX_PREVIOUS_FRAMES = 50;
50
48
 
51
- // A PerformanceObserver, observing new `long-animation-frame` entries.
52
- // If this variable is defined it means the browser supports LoAF.
53
- let loafObserver: PerformanceObserver | undefined;
49
+ /**
50
+ * Calculates the [INP](https://web.dev/articles/inp) value for the current
51
+ * page and calls the `callback` function once the value is ready, along with
52
+ * the `event` performance entries reported for that interaction. The reported
53
+ * value is a `DOMHighResTimeStamp`.
54
+ *
55
+ * A custom `durationThreshold` configuration option can optionally be passed
56
+ * to control what `event-timing` entries are considered for INP reporting. The
57
+ * default threshold is `40`, which means INP scores of less than 40 will not
58
+ * be reported. To avoid reporting no interactions in these cases, the library
59
+ * will fall back to the input delay of the first interaction. Note that this
60
+ * will not affect your 75th percentile INP value unless that value is also
61
+ * less than 40 (well below the recommended
62
+ * [good](https://web.dev/articles/inp#what_is_a_good_inp_score) threshold).
63
+ *
64
+ * If the `reportAllChanges` configuration option is set to `true`, the
65
+ * `callback` function will be called as soon as the value is initially
66
+ * determined as well as any time the value changes throughout the page
67
+ * lifespan.
68
+ *
69
+ * _**Important:** INP should be continually monitored for changes throughout
70
+ * the entire lifespan of a page—including if the user returns to the page after
71
+ * it has been hidden/backgrounded. However, since browsers often [will not fire
72
+ * additional callbacks once the user has backgrounded a
73
+ * page](https://developer.chrome.com/blog/page-lifecycle-api/#advice-hidden),
74
+ * `callback` is always called when the page's visibility state changes to
75
+ * hidden. As a result, the `callback` function might be called multiple times
76
+ * during the same page load._
77
+ */
78
+ export const onINP = (
79
+ onReport: (metric: INPMetricWithAttribution) => void,
80
+ opts: INPAttributionReportOpts = {},
81
+ ) => {
82
+ // Clone the opts object to ensure it's unique, so we can initialize a
83
+ // single instance of the `InteractionManager` class that's shared only with
84
+ // this function invocation and the `unattributedOnINP()` invocation below
85
+ // (which is passed the same `opts` object).
86
+ opts = Object.assign({}, opts);
87
+
88
+ const interactionManager = initUnique(opts, InteractionManager);
89
+
90
+ // A list of LoAF entries that have been dispatched and could potentially
91
+ // intersect with the INP candidate interaction. Note that periodically this
92
+ // list is cleaned up and entries that are known to not match INP are removed.
93
+ let pendingLoAFs: PerformanceLongAnimationFrameTiming[] = [];
94
+
95
+ // An array of groups of all the event timing entries that occurred within a
96
+ // particular frame. Note that periodically this array is cleaned up and entries
97
+ // that are known to not match INP are removed.
98
+ let pendingEntriesGroups: pendingEntriesGroup[] = [];
99
+
100
+ // The `processingEnd` time of most recently-processed event, chronologically.
101
+ let latestProcessingEnd: number = 0;
102
+
103
+ // A WeakMap to look up the event-timing-entries group of a given entry.
104
+ // Note that this only maps from "important" entries: either the first input or
105
+ // those with an `interactionId`.
106
+ const entryToEntriesGroupMap: WeakMap<
107
+ PerformanceEventTiming,
108
+ pendingEntriesGroup
109
+ > = new WeakMap();
110
+
111
+ // A mapping of interactionIds to the target Node.
112
+ const interactionTargetMap: WeakMap<Interaction, string> = new WeakMap();
113
+
114
+ // A boolean flag indicating whether or not a cleanup task has been queued.
115
+ let cleanupPending = false;
116
+
117
+ /**
118
+ * Adds new LoAF entries to the `pendingLoAFs` list.
119
+ */
120
+ const handleLoAFEntries = (
121
+ entries: PerformanceLongAnimationFrameTiming[],
122
+ ) => {
123
+ pendingLoAFs = pendingLoAFs.concat(entries);
124
+ queueCleanup();
125
+ };
126
+
127
+ const saveInteractionTarget = (interaction: Interaction) => {
128
+ if (!interactionTargetMap.get(interaction)) {
129
+ const node = interaction.entries[0].target;
130
+ if (node) {
131
+ const customTarget = opts.generateTarget?.(node) ?? getSelector(node);
132
+ interactionTargetMap.set(interaction, customTarget);
133
+ }
134
+ }
135
+ };
136
+
137
+ /**
138
+ * Groups entries that were presented within the same animation frame by
139
+ * a common `renderTime`. This function works by referencing
140
+ * `pendingEntriesGroups` and using an existing render time if one is found
141
+ * (otherwise creating a new one). This function also adds all interaction
142
+ * entries to an `entryToRenderTimeMap` WeakMap so that the "grouped" entries
143
+ * can be looked up later.
144
+ */
145
+ const groupEntriesByRenderTime = (entry: PerformanceEventTiming) => {
146
+ const renderTime = entry.startTime + entry.duration;
147
+ let group;
148
+
149
+ latestProcessingEnd = Math.max(latestProcessingEnd, entry.processingEnd);
150
+
151
+ // Iterate over all previous render times in reverse order to find a match.
152
+ // Go in reverse since the most likely match will be at the end.
153
+ for (let i = pendingEntriesGroups.length - 1; i >= 0; i--) {
154
+ const potentialGroup = pendingEntriesGroups[i];
155
+
156
+ // If a group's render time is within 8ms of the entry's render time,
157
+ // assume they were part of the same frame and add it to the group.
158
+ if (Math.abs(renderTime - potentialGroup.renderTime) <= 8) {
159
+ group = potentialGroup;
160
+ group.startTime = Math.min(entry.startTime, group.startTime);
161
+ group.processingStart = Math.min(
162
+ entry.processingStart,
163
+ group.processingStart,
164
+ );
165
+ group.processingEnd = Math.max(
166
+ entry.processingEnd,
167
+ group.processingEnd,
168
+ );
169
+ group.entries.push(entry);
170
+
171
+ break;
172
+ }
173
+ }
54
174
 
55
- // A list of LoAF entries that have been dispatched and could potentially
56
- // intersect with the INP candidate interaction. Note that periodically this
57
- // list is cleaned up and entries that are known to not match INP are removed.
58
- let pendingLoAFs: PerformanceLongAnimationFrameTiming[] = [];
175
+ // If there was no matching group, assume this is a new frame.
176
+ if (!group) {
177
+ group = {
178
+ startTime: entry.startTime,
179
+ processingStart: entry.processingStart,
180
+ processingEnd: entry.processingEnd,
181
+ renderTime,
182
+ entries: [entry],
183
+ };
184
+
185
+ pendingEntriesGroups.push(group);
186
+ }
59
187
 
60
- // An array of groups of all the event timing entries that occurred within a
61
- // particular frame. Note that periodically this array is cleaned up and entries
62
- // that are known to not match INP are removed.
63
- let pendingEntriesGroups: pendingEntriesGroup[] = [];
188
+ // Store the grouped render time for this entry for reference later.
189
+ if (entry.interactionId || entry.entryType === 'first-input') {
190
+ entryToEntriesGroupMap.set(entry, group);
191
+ }
64
192
 
65
- // The `processingEnd` time of most recently-processed event, chronologically.
66
- let latestProcessingEnd: number = 0;
193
+ queueCleanup();
194
+ };
67
195
 
68
- // A WeakMap to look up the event-timing-entries group of a given entry.
69
- // Note that this only maps from "important" entries: either the first input or
70
- // those with an `interactionId`.
71
- const entryToEntriesGroupMap: WeakMap<
72
- PerformanceEventTiming,
73
- pendingEntriesGroup
74
- > = new WeakMap();
196
+ const queueCleanup = () => {
197
+ // Queue cleanup of entries that are not part of any INP candidates.
198
+ if (!cleanupPending) {
199
+ whenIdleOrHidden(cleanupEntries);
200
+ cleanupPending = true;
201
+ }
202
+ };
75
203
 
76
- // A mapping of interactionIds to the target Node.
77
- export const interactionTargetMap: Map<number, Node> = new Map();
204
+ const cleanupEntries = () => {
205
+ // Keep all render times that are part of a pending INP candidate or
206
+ // that occurred within the 50 most recently-dispatched groups of events.
207
+ const longestInteractionGroups =
208
+ interactionManager._longestInteractionList.map((i) => {
209
+ return entryToEntriesGroupMap.get(i.entries[0]);
210
+ });
211
+ const minIndex = pendingEntriesGroups.length - MAX_PREVIOUS_FRAMES;
212
+ pendingEntriesGroups = pendingEntriesGroups.filter((group, index) => {
213
+ if (index >= minIndex) return true;
214
+ return longestInteractionGroups.includes(group);
215
+ });
78
216
 
79
- // A reference to the idle task used to clean up entries from the above
80
- // variables. If the value is -1 it means no task is queue, and if it's
81
- // greater than -1 the value corresponds to the idle callback handle.
82
- let idleHandle: number = -1;
217
+ // Keep all pending LoAF entries that either:
218
+ // 1) intersect with entries in the newly cleaned up `pendingEntriesGroups`
219
+ // 2) occur after the most recently-processed event entry (for up to MAX_PREVIOUS_FRAMES)
220
+ const loafsToKeep: Set<PerformanceLongAnimationFrameTiming> = new Set();
221
+ for (const group of pendingEntriesGroups) {
222
+ const loafs = getIntersectingLoAFs(group.startTime, group.processingEnd);
223
+ for (const loaf of loafs) {
224
+ loafsToKeep.add(loaf);
225
+ }
226
+ }
227
+ const prevFrameIndexCutoff = pendingLoAFs.length - 1 - MAX_PREVIOUS_FRAMES;
228
+ // Filter `pendingLoAFs` to preserve LoAF order.
229
+ pendingLoAFs = pendingLoAFs.filter((loaf, index) => {
230
+ if (
231
+ loaf.startTime > latestProcessingEnd &&
232
+ index > prevFrameIndexCutoff
233
+ ) {
234
+ return true;
235
+ }
83
236
 
84
- /**
85
- * Adds new LoAF entries to the `pendingLoAFs` list.
86
- */
87
- const handleLoAFEntries = (entries: PerformanceLongAnimationFrameTiming[]) => {
88
- pendingLoAFs = pendingLoAFs.concat(entries);
89
- queueCleanup();
90
- };
237
+ return loafsToKeep.has(loaf);
238
+ });
91
239
 
92
- // Get a reference to the interaction target element in case it's removed
93
- // from the DOM later.
94
- const saveInteractionTarget = (entry: PerformanceEventTiming) => {
95
- if (
96
- entry.interactionId &&
97
- entry.target &&
98
- !interactionTargetMap.has(entry.interactionId)
240
+ cleanupPending = false;
241
+ };
242
+
243
+ async function handleOnEachInteractionCallback(
244
+ entry: PerformanceEventTiming,
99
245
  ) {
100
- interactionTargetMap.set(entry.interactionId, entry.target);
101
- }
102
- };
246
+ if (!opts.onEachInteraction) {
247
+ return;
248
+ }
103
249
 
104
- /**
105
- * Groups entries that were presented within the same animation frame by
106
- * a common `renderTime`. This function works by referencing
107
- * `pendingEntriesGroups` and using an existing render time if one is found
108
- * (otherwise creating a new one). This function also adds all interaction
109
- * entries to an `entryToRenderTimeMap` WeakMap so that the "grouped" entries
110
- * can be looked up later.
111
- */
112
- const groupEntriesByRenderTime = (entry: PerformanceEventTiming) => {
113
- const renderTime = entry.startTime + entry.duration;
114
- let group;
115
-
116
- latestProcessingEnd = Math.max(latestProcessingEnd, entry.processingEnd);
117
-
118
- // Iterate over all previous render times in reverse order to find a match.
119
- // Go in reverse since the most likely match will be at the end.
120
- for (let i = pendingEntriesGroups.length - 1; i >= 0; i--) {
121
- const potentialGroup = pendingEntriesGroups[i];
122
-
123
- // If a group's render time is within 8ms of the entry's render time,
124
- // assume they were part of the same frame and add it to the group.
125
- if (Math.abs(renderTime - potentialGroup.renderTime) <= 8) {
126
- group = potentialGroup;
127
- group.startTime = Math.min(entry.startTime, group.startTime);
128
- group.processingStart = Math.min(
129
- entry.processingStart,
130
- group.processingStart,
131
- );
132
- group.processingEnd = Math.max(entry.processingEnd, group.processingEnd);
133
- group.entries.push(entry);
134
-
135
- break;
250
+ // Wait a microtask so this "pre" processing callback actually
251
+ // becomes a "post" processing callback.
252
+ void (await Promise.resolve());
253
+ if (!entry.interactionId) {
254
+ return;
136
255
  }
137
- }
138
256
 
139
- // If there was no matching group, assume this is a new frame.
140
- if (!group) {
141
- group = {
142
- startTime: entry.startTime,
143
- processingStart: entry.processingStart,
144
- processingEnd: entry.processingEnd,
145
- renderTime,
257
+ const interaction = attributeINP({
146
258
  entries: [entry],
147
- };
148
-
149
- pendingEntriesGroups.push(group);
259
+ // The only value we really need for `attributeINP` is `entries`
260
+ // Everything else is included to fill out the type.
261
+ name: 'INP',
262
+ rating: 'good',
263
+ value: entry.duration,
264
+ delta: entry.duration,
265
+ navigationType: 'navigate',
266
+ id: 'N/A',
267
+ });
268
+ opts.onEachInteraction(interaction);
150
269
  }
151
270
 
152
- // Store the grouped render time for this entry for reference later.
153
- if (entry.interactionId || entry.entryType === 'first-input') {
154
- entryToEntriesGroupMap.set(entry, group);
155
- }
271
+ interactionManager._onBeforeProcessingEntry = (
272
+ entry: PerformanceEventTiming,
273
+ ) => {
274
+ void handleOnEachInteractionCallback(entry);
275
+ groupEntriesByRenderTime(entry);
276
+ };
277
+ interactionManager._onAfterProcessingINPCandidate = saveInteractionTarget;
156
278
 
157
- queueCleanup();
158
- };
279
+ const getIntersectingLoAFs = (
280
+ start: DOMHighResTimeStamp,
281
+ end: DOMHighResTimeStamp,
282
+ ) => {
283
+ const intersectingLoAFs: PerformanceLongAnimationFrameTiming[] = [];
159
284
 
160
- const queueCleanup = () => {
161
- // Queue cleanup of entries that are not part of any INP candidates.
162
- if (idleHandle < 0) {
163
- idleHandle = whenIdle(cleanupEntries);
164
- }
165
- };
285
+ for (const loaf of pendingLoAFs) {
286
+ // If the LoAF ends before the given start time, ignore it.
287
+ if (loaf.startTime + loaf.duration < start) continue;
288
+
289
+ // If the LoAF starts after the given end time, ignore it and all
290
+ // subsequent pending LoAFs (because they're in time order).
291
+ if (loaf.startTime > end) break;
166
292
 
167
- const cleanupEntries = () => {
168
- // Delete any stored interaction target elements if they're not part of one
169
- // of the 10 longest interactions.
170
- if (interactionTargetMap.size > 10) {
171
- interactionTargetMap.forEach((_, key) => {
172
- if (!longestInteractionMap.has(key)) {
173
- interactionTargetMap.delete(key);
293
+ // Still here? If so this LoAF intersects with the interaction.
294
+ intersectingLoAFs.push(loaf);
295
+ }
296
+ return intersectingLoAFs;
297
+ };
298
+
299
+ const attributeLoAFDetails = (attribution: INPAttribution) => {
300
+ // If there is no LoAF data then nothing further to attribute
301
+ if (!attribution.longAnimationFrameEntries?.length) {
302
+ return;
303
+ }
304
+
305
+ const interactionTime = attribution.interactionTime;
306
+ const inputDelay = attribution.inputDelay;
307
+ const processingDuration = attribution.processingDuration;
308
+
309
+ // Stats across all LoAF entries and scripts.
310
+ let totalScriptDuration = 0;
311
+ let totalStyleAndLayoutDuration = 0;
312
+ let totalPaintDuration = 0;
313
+ let longestScriptDuration = 0;
314
+ let longestScriptEntry: PerformanceScriptTiming | undefined;
315
+ let longestScriptSubpart: INPLongestScriptSummary['subpart'] | undefined;
316
+
317
+ for (const loafEntry of attribution.longAnimationFrameEntries) {
318
+ totalStyleAndLayoutDuration =
319
+ totalStyleAndLayoutDuration +
320
+ loafEntry.startTime +
321
+ loafEntry.duration -
322
+ loafEntry.styleAndLayoutStart;
323
+
324
+ for (const script of loafEntry.scripts) {
325
+ const scriptEndTime = script.startTime + script.duration;
326
+ if (scriptEndTime < interactionTime) {
327
+ continue;
328
+ }
329
+ const intersectingScriptDuration =
330
+ scriptEndTime - Math.max(interactionTime, script.startTime);
331
+ // Since forcedStyleAndLayoutDuration doesn't provide timestamps, we
332
+ // apportion the total based on the intersectingScriptDuration. Not
333
+ // correct depending on when it occurred, but the best we can do.
334
+ const intersectingForceStyleAndLayoutDuration = script.duration
335
+ ? (intersectingScriptDuration / script.duration) *
336
+ script.forcedStyleAndLayoutDuration
337
+ : 0;
338
+ // For scripts we exclude forcedStyleAndLayout (same as DevTools does
339
+ // in its summary totals) and instead include that in
340
+ // totalStyleAndLayoutDuration
341
+ totalScriptDuration +=
342
+ intersectingScriptDuration - intersectingForceStyleAndLayoutDuration;
343
+ totalStyleAndLayoutDuration += intersectingForceStyleAndLayoutDuration;
344
+
345
+ if (intersectingScriptDuration > longestScriptDuration) {
346
+ // Set the subpart this occurred in.
347
+ longestScriptSubpart =
348
+ script.startTime < interactionTime + inputDelay
349
+ ? 'input-delay'
350
+ : script.startTime >=
351
+ interactionTime + inputDelay + processingDuration
352
+ ? 'presentation-delay'
353
+ : 'processing-duration';
354
+
355
+ longestScriptEntry = script;
356
+ longestScriptDuration = intersectingScriptDuration;
357
+ }
174
358
  }
175
- });
176
- }
359
+ }
177
360
 
178
- // Keep all render times that are part of a pending INP candidate or
179
- // that occurred within the 50 most recently-dispatched groups of events.
180
- const longestInteractionGroups = longestInteractionList.map((i) => {
181
- return entryToEntriesGroupMap.get(i.entries[0]);
182
- });
183
- const minIndex = pendingEntriesGroups.length - MAX_PREVIOUS_FRAMES;
184
- pendingEntriesGroups = pendingEntriesGroups.filter((group, index) => {
185
- if (index >= minIndex) return true;
186
- return longestInteractionGroups.includes(group);
187
- });
188
-
189
- // Keep all pending LoAF entries that either:
190
- // 1) intersect with entries in the newly cleaned up `pendingEntriesGroups`
191
- // 2) occur after the most recently-processed event entry (for up to MAX_PREVIOUS_FRAMES)
192
- const loafsToKeep: Set<PerformanceLongAnimationFrameTiming> = new Set();
193
- for (let i = 0; i < pendingEntriesGroups.length; i++) {
194
- const group = pendingEntriesGroups[i];
195
- getIntersectingLoAFs(group.startTime, group.processingEnd).forEach(
196
- (loaf) => {
197
- loafsToKeep.add(loaf);
198
- },
199
- );
200
- }
201
- const prevFrameIndexCutoff = pendingLoAFs.length - 1 - MAX_PREVIOUS_FRAMES;
202
- // Filter `pendingLoAFs` to preserve LoAF order.
203
- pendingLoAFs = pendingLoAFs.filter((loaf, index) => {
204
- if (loaf.startTime > latestProcessingEnd && index > prevFrameIndexCutoff) {
205
- return true;
361
+ // Calculate the totalPaintDuration from the last LoAF after
362
+ // presentationDelay starts (where available)
363
+ const lastLoAF = attribution.longAnimationFrameEntries.at(-1);
364
+ const lastLoAFEndTime = lastLoAF
365
+ ? lastLoAF.startTime + lastLoAF.duration
366
+ : 0;
367
+ if (lastLoAFEndTime >= interactionTime + inputDelay + processingDuration) {
368
+ totalPaintDuration = attribution.nextPaintTime - lastLoAFEndTime;
206
369
  }
207
370
 
208
- return loafsToKeep.has(loaf);
209
- });
371
+ if (longestScriptEntry && longestScriptSubpart) {
372
+ attribution.longestScript = {
373
+ entry: longestScriptEntry,
374
+ subpart: longestScriptSubpart,
375
+ intersectingDuration: longestScriptDuration,
376
+ };
377
+ }
378
+ attribution.totalScriptDuration = totalScriptDuration;
379
+ attribution.totalStyleAndLayoutDuration = totalStyleAndLayoutDuration;
380
+ attribution.totalPaintDuration = totalPaintDuration;
381
+ attribution.totalUnattributedDuration =
382
+ attribution.nextPaintTime -
383
+ interactionTime -
384
+ totalScriptDuration -
385
+ totalStyleAndLayoutDuration -
386
+ totalPaintDuration;
387
+ };
210
388
 
211
- // Reset the idle callback handle so it can be queued again.
212
- idleHandle = -1;
213
- };
389
+ const attributeINP = (metric: INPMetric): INPMetricWithAttribution => {
390
+ const firstEntry = metric.entries[0];
391
+ const group = entryToEntriesGroupMap.get(firstEntry)!;
214
392
 
215
- entryPreProcessingCallbacks.push(
216
- saveInteractionTarget,
217
- groupEntriesByRenderTime,
218
- );
393
+ const processingStart = firstEntry.processingStart;
219
394
 
220
- const getIntersectingLoAFs = (
221
- start: DOMHighResTimeStamp,
222
- end: DOMHighResTimeStamp,
223
- ) => {
224
- const intersectingLoAFs = [];
395
+ // Due to the fact that durations can be rounded down to the nearest 8ms,
396
+ // we have to clamp `nextPaintTime` so it doesn't appear to occur before
397
+ // processing starts. Note: we can't use `processingEnd` since processing
398
+ // can extend beyond the event duration in some cases (see next comment).
399
+ const nextPaintTime = Math.max(
400
+ firstEntry.startTime + firstEntry.duration,
401
+ processingStart,
402
+ );
225
403
 
226
- for (let i = 0, loaf; (loaf = pendingLoAFs[i]); i++) {
227
- // If the LoAF ends before the given start time, ignore it.
228
- if (loaf.startTime + loaf.duration < start) continue;
404
+ // For the purposes of attribution, clamp `processingEnd` to `nextPaintTime`,
405
+ // so processing is never reported as taking longer than INP (which can
406
+ // happen via the web APIs in the case of sync modals, e.g. `alert()`).
407
+ // See: https://github.com/GoogleChrome/web-vitals/issues/492
408
+ const processingEnd = Math.min(group.processingEnd, nextPaintTime);
229
409
 
230
- // If the LoAF starts after the given end time, ignore it and all
231
- // subsequent pending LoAFs (because they're in time order).
232
- if (loaf.startTime > end) break;
410
+ // Sort the entries in processing time order.
411
+ const processedEventEntries = group.entries.sort((a, b) => {
412
+ return a.processingStart - b.processingStart;
413
+ });
233
414
 
234
- // Still here? If so this LoAF intersects with the interaction.
235
- intersectingLoAFs.push(loaf);
236
- }
237
- return intersectingLoAFs;
238
- };
415
+ const longAnimationFrameEntries: PerformanceLongAnimationFrameTiming[] =
416
+ getIntersectingLoAFs(firstEntry.startTime, processingEnd);
417
+
418
+ const interaction = interactionManager._longestInteractionMap.get(
419
+ firstEntry.interactionId,
420
+ );
421
+
422
+ const attribution: INPAttribution = {
423
+ // TS flags the next line because `interactionTargetMap.get()` might
424
+ // return `undefined`, but we ignore this assuming the user knows what
425
+ // they are doing.
426
+ interactionTarget: interactionTargetMap.get(interaction!)!,
427
+ interactionType: firstEntry.name.startsWith('key')
428
+ ? 'keyboard'
429
+ : 'pointer',
430
+ interactionTime: firstEntry.startTime,
431
+ nextPaintTime: nextPaintTime,
432
+ processedEventEntries: processedEventEntries,
433
+ longAnimationFrameEntries: longAnimationFrameEntries,
434
+ inputDelay: processingStart - firstEntry.startTime,
435
+ processingDuration: processingEnd - processingStart,
436
+ presentationDelay: nextPaintTime - processingEnd,
437
+ loadState: getLoadState(firstEntry.startTime),
438
+ longestScript: undefined,
439
+ totalScriptDuration: undefined,
440
+ totalStyleAndLayoutDuration: undefined,
441
+ totalPaintDuration: undefined,
442
+ totalUnattributedDuration: undefined,
443
+ };
239
444
 
240
- export const attributeINP = (metric: INPMetric): INPMetricWithAttribution => {
241
- const firstEntry = metric.entries[0];
242
- const group = entryToEntriesGroupMap.get(firstEntry)!;
243
-
244
- const processingStart = firstEntry.processingStart;
245
- const processingEnd = group.processingEnd;
246
-
247
- // Sort the entries in processing time order.
248
- const processedEventEntries = group.entries.sort((a, b) => {
249
- return a.processingStart - b.processingStart;
250
- });
251
-
252
- const longAnimationFrameEntries: PerformanceLongAnimationFrameTiming[] =
253
- getIntersectingLoAFs(firstEntry.startTime, processingEnd);
254
-
255
- // The first interaction entry may not have a target defined, so use the
256
- // first one found in the entry list.
257
- // TODO: when the following bug is fixed just use `firstInteractionEntry`.
258
- // https://bugs.chromium.org/p/chromium/issues/detail?id=1367329
259
- // As a fallback, also check the interactionTargetMap (to account for
260
- // cases where the element is removed from the DOM before reporting happens).
261
- const firstEntryWithTarget = metric.entries.find((entry) => entry.target);
262
- const interactionTargetElement =
263
- (firstEntryWithTarget && firstEntryWithTarget.target) ||
264
- interactionTargetMap.get(firstEntry.interactionId);
265
-
266
- // Since entry durations are rounded to the nearest 8ms, we need to clamp
267
- // the `nextPaintTime` value to be higher than the `processingEnd` or
268
- // end time of any LoAF entry.
269
- const nextPaintTimeCandidates = [
270
- firstEntry.startTime + firstEntry.duration,
271
- processingEnd,
272
- ].concat(
273
- longAnimationFrameEntries.map((loaf) => loaf.startTime + loaf.duration),
274
- );
275
-
276
- const nextPaintTime = Math.max.apply(Math, nextPaintTimeCandidates);
277
-
278
- const attribution: INPAttribution = {
279
- interactionTarget: getSelector(interactionTargetElement),
280
- interactionTargetElement: interactionTargetElement,
281
- interactionType: firstEntry.name.startsWith('key') ? 'keyboard' : 'pointer',
282
- interactionTime: firstEntry.startTime,
283
- nextPaintTime: nextPaintTime,
284
- processedEventEntries: processedEventEntries,
285
- longAnimationFrameEntries: longAnimationFrameEntries,
286
- inputDelay: processingStart - firstEntry.startTime,
287
- processingDuration: processingEnd - processingStart,
288
- presentationDelay: Math.max(nextPaintTime - processingEnd, 0),
289
- loadState: getLoadState(firstEntry.startTime),
445
+ attributeLoAFDetails(attribution);
446
+
447
+ // Use `Object.assign()` to ensure the original metric object is returned.
448
+ const metricWithAttribution: INPMetricWithAttribution = Object.assign(
449
+ metric,
450
+ {attribution},
451
+ );
452
+ return metricWithAttribution;
290
453
  };
291
454
 
292
- // Use Object.assign to set property to keep tsc happy.
293
- const metricWithAttribution: INPMetricWithAttribution = Object.assign(
294
- metric,
295
- {attribution},
296
- );
297
- return metricWithAttribution;
298
- };
455
+ // Start observing LoAF entries for attribution.
456
+ observe('long-animation-frame', handleLoAFEntries);
299
457
 
300
- /**
301
- * Calculates the [INP](https://web.dev/articles/inp) value for the current
302
- * page and calls the `callback` function once the value is ready, along with
303
- * the `event` performance entries reported for that interaction. The reported
304
- * value is a `DOMHighResTimeStamp`.
305
- *
306
- * A custom `durationThreshold` configuration option can optionally be passed to
307
- * control what `event-timing` entries are considered for INP reporting. The
308
- * default threshold is `40`, which means INP scores of less than 40 are
309
- * reported as 0. Note that this will not affect your 75th percentile INP value
310
- * unless that value is also less than 40 (well below the recommended
311
- * [good](https://web.dev/articles/inp#what_is_a_good_inp_score) threshold).
312
- *
313
- * If the `reportAllChanges` configuration option is set to `true`, the
314
- * `callback` function will be called as soon as the value is initially
315
- * determined as well as any time the value changes throughout the page
316
- * lifespan.
317
- *
318
- * _**Important:** INP should be continually monitored for changes throughout
319
- * the entire lifespan of a page—including if the user returns to the page after
320
- * it's been hidden/backgrounded. However, since browsers often [will not fire
321
- * additional callbacks once the user has backgrounded a
322
- * page](https://developer.chrome.com/blog/page-lifecycle-api/#advice-hidden),
323
- * `callback` is always called when the page's visibility state changes to
324
- * hidden. As a result, the `callback` function might be called multiple times
325
- * during the same page load._
326
- */
327
- export const onINP = (
328
- onReport: (metric: INPMetricWithAttribution) => void,
329
- opts?: ReportOpts,
330
- ) => {
331
- if (!loafObserver) {
332
- loafObserver = observe('long-animation-frame', handleLoAFEntries);
333
- }
334
458
  unattributedOnINP((metric: INPMetric) => {
335
459
  const metricWithAttribution = attributeINP(metric);
336
460
  onReport(metricWithAttribution);