@paulirish/trace_engine 0.0.58 → 0.0.60

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 (450) hide show
  1. package/.tmp/tsbuildinfo/analyze-trace.d.mts +2 -3
  2. package/.tmp/tsbuildinfo/analyze-trace.d.mts.map +1 -1
  3. package/.tmp/tsbuildinfo/tsconfig.tsbuildinfo +1 -1
  4. package/LICENSE +1 -1
  5. package/README.md +28 -1
  6. package/analyze-trace.mjs +5 -3
  7. package/core/platform/ArrayUtilities.d.ts +1 -0
  8. package/core/platform/ArrayUtilities.js +2 -2
  9. package/core/platform/ArrayUtilities.js.map +1 -1
  10. package/core/platform/Brand.js +1 -1
  11. package/core/platform/Brand.js.map +1 -1
  12. package/core/platform/Constructor.js +1 -1
  13. package/core/platform/Constructor.js.map +1 -1
  14. package/core/platform/DOMUtilities.js +1 -1
  15. package/core/platform/DOMUtilities.js.map +1 -1
  16. package/core/platform/DateUtilities.js +1 -1
  17. package/core/platform/DateUtilities.js.map +1 -1
  18. package/core/platform/DevToolsPath.js +1 -1
  19. package/core/platform/DevToolsPath.js.map +1 -1
  20. package/core/platform/KeyboardUtilities.js +1 -1
  21. package/core/platform/KeyboardUtilities.js.map +1 -1
  22. package/core/platform/MapUtilities.js +1 -1
  23. package/core/platform/MapUtilities.js.map +1 -1
  24. package/core/platform/MimeType.js +1 -1
  25. package/core/platform/MimeType.js.map +1 -1
  26. package/core/platform/NumberUtilities.js +1 -1
  27. package/core/platform/NumberUtilities.js.map +1 -1
  28. package/core/platform/StringUtilities.d.ts +2 -1
  29. package/core/platform/StringUtilities.js +34 -32
  30. package/core/platform/StringUtilities.js.map +1 -1
  31. package/core/platform/Timing.js +1 -1
  32. package/core/platform/Timing.js.map +1 -1
  33. package/core/platform/TypedArrayUtilities.js +1 -1
  34. package/core/platform/TypedArrayUtilities.js.map +1 -1
  35. package/core/platform/TypescriptUtilities.js +1 -1
  36. package/core/platform/TypescriptUtilities.js.map +1 -1
  37. package/core/platform/UIString.js +1 -1
  38. package/core/platform/UIString.js.map +1 -1
  39. package/core/platform/UserVisibleError.js +1 -1
  40. package/core/platform/UserVisibleError.js.map +1 -1
  41. package/core/platform/devtools_entrypoint-bundle-typescript-tsconfig.json +6 -2
  42. package/core/platform/platform-tsconfig.json +6 -2
  43. package/core/platform/platform.js +3 -29
  44. package/core/platform/platform.js.map +1 -1
  45. package/generated/protocol.d.ts +224 -14
  46. package/generated/protocol.js +1 -1
  47. package/locales/af.json +0 -9
  48. package/locales/am.json +0 -9
  49. package/locales/ar.json +0 -9
  50. package/locales/as.json +0 -9
  51. package/locales/az.json +0 -9
  52. package/locales/be.json +0 -9
  53. package/locales/bg.json +0 -9
  54. package/locales/bn.json +1 -10
  55. package/locales/bs.json +0 -9
  56. package/locales/ca.json +0 -9
  57. package/locales/cs.json +0 -9
  58. package/locales/cy.json +0 -9
  59. package/locales/da.json +0 -9
  60. package/locales/de.json +0 -9
  61. package/locales/el.json +0 -9
  62. package/locales/en-GB.json +0 -9
  63. package/locales/en-US.json +8 -8
  64. package/locales/en-XL.json +8 -8
  65. package/locales/es-419.json +0 -9
  66. package/locales/es.json +0 -9
  67. package/locales/et.json +0 -9
  68. package/locales/eu.json +0 -9
  69. package/locales/fa.json +0 -9
  70. package/locales/fi.json +0 -9
  71. package/locales/fil.json +0 -9
  72. package/locales/fr-CA.json +0 -9
  73. package/locales/fr.json +0 -9
  74. package/locales/gl.json +0 -9
  75. package/locales/gu.json +0 -9
  76. package/locales/he.json +0 -9
  77. package/locales/hi.json +0 -9
  78. package/locales/hr.json +4 -13
  79. package/locales/hu.json +0 -9
  80. package/locales/hy.json +0 -9
  81. package/locales/id.json +0 -9
  82. package/locales/is.json +0 -9
  83. package/locales/it.json +0 -9
  84. package/locales/ja.json +0 -9
  85. package/locales/ka.json +0 -9
  86. package/locales/kk.json +0 -9
  87. package/locales/km.json +0 -9
  88. package/locales/kn.json +0 -9
  89. package/locales/ko.json +0 -9
  90. package/locales/ky.json +0 -9
  91. package/locales/lo.json +0 -9
  92. package/locales/lt.json +0 -9
  93. package/locales/lv.json +0 -9
  94. package/locales/mk.json +0 -9
  95. package/locales/ml.json +0 -9
  96. package/locales/mn.json +0 -9
  97. package/locales/mr.json +0 -9
  98. package/locales/ms.json +0 -9
  99. package/locales/my.json +1 -10
  100. package/locales/ne.json +22 -31
  101. package/locales/nl.json +0 -9
  102. package/locales/no.json +0 -9
  103. package/locales/or.json +0 -9
  104. package/locales/pa.json +0 -9
  105. package/locales/pl.json +0 -9
  106. package/locales/pt-PT.json +0 -9
  107. package/locales/pt.json +0 -9
  108. package/locales/ro.json +0 -9
  109. package/locales/ru.json +0 -9
  110. package/locales/si.json +1 -10
  111. package/locales/sk.json +0 -9
  112. package/locales/sl.json +0 -9
  113. package/locales/sq.json +0 -9
  114. package/locales/sr-Latn.json +0 -9
  115. package/locales/sr.json +0 -9
  116. package/locales/sv.json +0 -9
  117. package/locales/sw.json +0 -9
  118. package/locales/ta.json +0 -9
  119. package/locales/te.json +0 -9
  120. package/locales/th.json +0 -9
  121. package/locales/tr.json +0 -9
  122. package/locales/uk.json +0 -9
  123. package/locales/ur.json +0 -9
  124. package/locales/uz.json +0 -9
  125. package/locales/vi.json +0 -9
  126. package/locales/zh-HK.json +0 -9
  127. package/locales/zh-TW.json +0 -9
  128. package/locales/zh.json +0 -9
  129. package/locales/zu.json +0 -9
  130. package/models/cpu_profile/CPUProfileDataModel.d.ts +1 -0
  131. package/models/cpu_profile/CPUProfileDataModel.js +1 -1
  132. package/models/cpu_profile/CPUProfileDataModel.js.map +1 -1
  133. package/models/cpu_profile/ProfileTreeModel.d.ts +1 -1
  134. package/models/cpu_profile/ProfileTreeModel.js +1 -1
  135. package/models/cpu_profile/ProfileTreeModel.js.map +1 -1
  136. package/models/cpu_profile/cpu_profile-tsconfig.json +6 -2
  137. package/models/cpu_profile/cpu_profile.js +1 -1
  138. package/models/cpu_profile/cpu_profile.js.map +1 -1
  139. package/models/cpu_profile/devtools_entrypoint-bundle-typescript-tsconfig.json +6 -2
  140. package/models/trace/EntityMapper.d.ts +33 -0
  141. package/models/trace/EntityMapper.js +123 -0
  142. package/models/trace/EntityMapper.js.map +1 -0
  143. package/models/trace/EventsSerializer.d.ts +11 -0
  144. package/models/trace/EventsSerializer.js +82 -0
  145. package/models/trace/EventsSerializer.js.map +1 -0
  146. package/models/trace/LanternComputationData.d.ts +3 -3
  147. package/models/trace/LanternComputationData.js +12 -10
  148. package/models/trace/LanternComputationData.js.map +1 -1
  149. package/models/trace/ModelImpl.d.ts +7 -14
  150. package/models/trace/ModelImpl.js +25 -52
  151. package/models/trace/ModelImpl.js.map +1 -1
  152. package/models/trace/Name.d.ts +12 -0
  153. package/models/trace/Name.js +115 -0
  154. package/models/trace/Name.js.map +1 -0
  155. package/models/trace/Processor.d.ts +1 -1
  156. package/models/trace/Processor.js +42 -61
  157. package/models/trace/Processor.js.map +1 -1
  158. package/models/trace/Styles.d.ts +50 -0
  159. package/models/trace/Styles.js +816 -0
  160. package/models/trace/Styles.js.map +1 -0
  161. package/models/trace/devtools_entrypoint-bundle-typescript-tsconfig.json +6 -2
  162. package/models/trace/extras/FilmStrip.d.ts +1 -1
  163. package/models/trace/extras/FilmStrip.js +7 -7
  164. package/models/trace/extras/FilmStrip.js.map +1 -1
  165. package/models/trace/extras/MainThreadActivity.js +1 -1
  166. package/models/trace/extras/MainThreadActivity.js.map +1 -1
  167. package/models/trace/extras/ScriptDuplication.js +1 -1
  168. package/models/trace/extras/ScriptDuplication.js.map +1 -1
  169. package/models/trace/extras/StackTraceForEvent.d.ts +2 -2
  170. package/models/trace/extras/StackTraceForEvent.js +21 -21
  171. package/models/trace/extras/StackTraceForEvent.js.map +1 -1
  172. package/models/trace/extras/ThirdParties.d.ts +2 -2
  173. package/models/trace/extras/ThirdParties.js +17 -17
  174. package/models/trace/extras/ThirdParties.js.map +1 -1
  175. package/models/trace/extras/TraceFilter.d.ts +1 -1
  176. package/models/trace/extras/TraceFilter.js +1 -1
  177. package/models/trace/extras/TraceFilter.js.map +1 -1
  178. package/models/trace/extras/TraceTree.d.ts +1 -0
  179. package/models/trace/extras/TraceTree.js +2 -2
  180. package/models/trace/extras/TraceTree.js.map +1 -1
  181. package/models/trace/extras/devtools_entrypoint-bundle-typescript-tsconfig.json +6 -2
  182. package/models/trace/extras/extras-tsconfig.json +6 -2
  183. package/models/trace/extras/extras.js.map +1 -1
  184. package/models/trace/handlers/AnimationFramesHandler.js +11 -11
  185. package/models/trace/handlers/AnimationFramesHandler.js.map +1 -1
  186. package/models/trace/handlers/AnimationHandler.js +5 -5
  187. package/models/trace/handlers/AnimationHandler.js.map +1 -1
  188. package/models/trace/handlers/AsyncJSCallsHandler.d.ts +3 -3
  189. package/models/trace/handlers/AsyncJSCallsHandler.js +9 -9
  190. package/models/trace/handlers/AsyncJSCallsHandler.js.map +1 -1
  191. package/models/trace/handlers/AuctionWorkletsHandler.js +12 -12
  192. package/models/trace/handlers/AuctionWorkletsHandler.js.map +1 -1
  193. package/models/trace/handlers/DOMStatsHandler.js +3 -3
  194. package/models/trace/handlers/DOMStatsHandler.js.map +1 -1
  195. package/models/trace/handlers/ExtensionTraceDataHandler.d.ts +15 -2
  196. package/models/trace/handlers/ExtensionTraceDataHandler.js +53 -64
  197. package/models/trace/handlers/ExtensionTraceDataHandler.js.map +1 -1
  198. package/models/trace/handlers/FlowsHandler.js +11 -11
  199. package/models/trace/handlers/FlowsHandler.js.map +1 -1
  200. package/models/trace/handlers/FramesHandler.d.ts +7 -0
  201. package/models/trace/handlers/FramesHandler.js +12 -10
  202. package/models/trace/handlers/FramesHandler.js.map +1 -1
  203. package/models/trace/handlers/GPUHandler.js +3 -3
  204. package/models/trace/handlers/GPUHandler.js.map +1 -1
  205. package/models/trace/handlers/ImagePaintingHandler.js +13 -13
  206. package/models/trace/handlers/ImagePaintingHandler.js.map +1 -1
  207. package/models/trace/handlers/InitiatorsHandler.js +32 -41
  208. package/models/trace/handlers/InitiatorsHandler.js.map +1 -1
  209. package/models/trace/handlers/InvalidationsHandler.js +63 -44
  210. package/models/trace/handlers/InvalidationsHandler.js.map +1 -1
  211. package/models/trace/handlers/LargestImagePaintHandler.js +5 -4
  212. package/models/trace/handlers/LargestImagePaintHandler.js.map +1 -1
  213. package/models/trace/handlers/LargestTextPaintHandler.js +3 -3
  214. package/models/trace/handlers/LargestTextPaintHandler.js.map +1 -1
  215. package/models/trace/handlers/LayerTreeHandler.js +11 -11
  216. package/models/trace/handlers/LayerTreeHandler.js.map +1 -1
  217. package/models/trace/handlers/LayoutShiftsHandler.d.ts +17 -4
  218. package/models/trace/handlers/LayoutShiftsHandler.js +47 -40
  219. package/models/trace/handlers/LayoutShiftsHandler.js.map +1 -1
  220. package/models/trace/handlers/MemoryHandler.js +3 -3
  221. package/models/trace/handlers/MemoryHandler.js.map +1 -1
  222. package/models/trace/handlers/MetaHandler.d.ts +22 -1
  223. package/models/trace/handlers/MetaHandler.js +30 -29
  224. package/models/trace/handlers/MetaHandler.js.map +1 -1
  225. package/models/trace/handlers/ModelHandlers.js +1 -1
  226. package/models/trace/handlers/ModelHandlers.js.map +1 -1
  227. package/models/trace/handlers/NetworkRequestsHandler.d.ts +10 -0
  228. package/models/trace/handlers/NetworkRequestsHandler.js +44 -25
  229. package/models/trace/handlers/NetworkRequestsHandler.js.map +1 -1
  230. package/models/trace/handlers/PageFramesHandler.js +3 -3
  231. package/models/trace/handlers/PageFramesHandler.js.map +1 -1
  232. package/models/trace/handlers/PageLoadMetricsHandler.js +5 -5
  233. package/models/trace/handlers/PageLoadMetricsHandler.js.map +1 -1
  234. package/models/trace/handlers/RendererHandler.d.ts +1 -1
  235. package/models/trace/handlers/RendererHandler.js +22 -22
  236. package/models/trace/handlers/RendererHandler.js.map +1 -1
  237. package/models/trace/handlers/SamplesHandler.d.ts +2 -2
  238. package/models/trace/handlers/SamplesHandler.js +7 -9
  239. package/models/trace/handlers/SamplesHandler.js.map +1 -1
  240. package/models/trace/handlers/ScreenshotsHandler.js +9 -10
  241. package/models/trace/handlers/ScreenshotsHandler.js.map +1 -1
  242. package/models/trace/handlers/ScriptsHandler.js +9 -8
  243. package/models/trace/handlers/ScriptsHandler.js.map +1 -1
  244. package/models/trace/handlers/SelectorStatsHandler.d.ts +2 -2
  245. package/models/trace/handlers/SelectorStatsHandler.js +13 -13
  246. package/models/trace/handlers/SelectorStatsHandler.js.map +1 -1
  247. package/models/trace/handlers/Threads.d.ts +2 -2
  248. package/models/trace/handlers/Threads.js +9 -9
  249. package/models/trace/handlers/Threads.js.map +1 -1
  250. package/models/trace/handlers/UserInteractionsHandler.d.ts +10 -3
  251. package/models/trace/handlers/UserInteractionsHandler.js +104 -84
  252. package/models/trace/handlers/UserInteractionsHandler.js.map +1 -1
  253. package/models/trace/handlers/UserTimingsHandler.d.ts +21 -0
  254. package/models/trace/handlers/UserTimingsHandler.js +67 -39
  255. package/models/trace/handlers/UserTimingsHandler.js.map +1 -1
  256. package/models/trace/handlers/WarningsHandler.js +14 -14
  257. package/models/trace/handlers/WarningsHandler.js.map +1 -1
  258. package/models/trace/handlers/WorkersHandler.js +7 -7
  259. package/models/trace/handlers/WorkersHandler.js.map +1 -1
  260. package/models/trace/handlers/devtools_entrypoint-bundle-typescript-tsconfig.json +6 -2
  261. package/models/trace/handlers/handlers-tsconfig.json +6 -2
  262. package/models/trace/handlers/handlers.js +1 -1
  263. package/models/trace/handlers/handlers.js.map +1 -1
  264. package/models/trace/handlers/helpers.d.ts +3 -2
  265. package/models/trace/handlers/helpers.js +10 -10
  266. package/models/trace/handlers/helpers.js.map +1 -1
  267. package/models/trace/handlers/types.d.ts +25 -2
  268. package/models/trace/handlers/types.js.map +1 -1
  269. package/models/trace/helpers/Extensions.js +8 -8
  270. package/models/trace/helpers/Extensions.js.map +1 -1
  271. package/models/trace/helpers/Network.js.map +1 -1
  272. package/models/trace/helpers/SamplesIntegrator.js +11 -9
  273. package/models/trace/helpers/SamplesIntegrator.js.map +1 -1
  274. package/models/trace/helpers/SyntheticEvents.js +1 -1
  275. package/models/trace/helpers/SyntheticEvents.js.map +1 -1
  276. package/models/trace/helpers/Timing.d.ts +4 -0
  277. package/models/trace/helpers/Timing.js +6 -4
  278. package/models/trace/helpers/Timing.js.map +1 -1
  279. package/models/trace/helpers/Trace.d.ts +21 -23
  280. package/models/trace/helpers/Trace.js +188 -67
  281. package/models/trace/helpers/Trace.js.map +1 -1
  282. package/models/trace/helpers/TreeHelpers.js +1 -1
  283. package/models/trace/helpers/TreeHelpers.js.map +1 -1
  284. package/models/trace/helpers/devtools_entrypoint-bundle-typescript-tsconfig.json +6 -2
  285. package/models/trace/helpers/helpers-tsconfig.json +6 -2
  286. package/models/trace/helpers/helpers.js +1 -1
  287. package/models/trace/helpers/helpers.js.map +1 -1
  288. package/models/trace/insights/CLSCulprits.d.ts +2 -2
  289. package/models/trace/insights/CLSCulprits.js +14 -14
  290. package/models/trace/insights/CLSCulprits.js.map +1 -1
  291. package/models/trace/insights/Cache.d.ts +2 -1
  292. package/models/trace/insights/Cache.js +8 -5
  293. package/models/trace/insights/Cache.js.map +1 -1
  294. package/models/trace/insights/Common.d.ts +9 -1
  295. package/models/trace/insights/Common.js +33 -1
  296. package/models/trace/insights/Common.js.map +1 -1
  297. package/models/trace/insights/DOMSize.d.ts +3 -2
  298. package/models/trace/insights/DOMSize.js +10 -7
  299. package/models/trace/insights/DOMSize.js.map +1 -1
  300. package/models/trace/insights/DocumentLatency.d.ts +2 -2
  301. package/models/trace/insights/DocumentLatency.js +18 -17
  302. package/models/trace/insights/DocumentLatency.js.map +1 -1
  303. package/models/trace/insights/DuplicatedJavaScript.d.ts +2 -2
  304. package/models/trace/insights/DuplicatedJavaScript.js +5 -5
  305. package/models/trace/insights/DuplicatedJavaScript.js.map +1 -1
  306. package/models/trace/insights/FontDisplay.d.ts +2 -1
  307. package/models/trace/insights/FontDisplay.js +7 -4
  308. package/models/trace/insights/FontDisplay.js.map +1 -1
  309. package/models/trace/insights/ForcedReflow.d.ts +2 -1
  310. package/models/trace/insights/ForcedReflow.js +6 -3
  311. package/models/trace/insights/ForcedReflow.js.map +1 -1
  312. package/models/trace/insights/INPBreakdown.d.ts +3 -3
  313. package/models/trace/insights/INPBreakdown.js +16 -5
  314. package/models/trace/insights/INPBreakdown.js.map +1 -1
  315. package/models/trace/insights/ImageDelivery.d.ts +2 -2
  316. package/models/trace/insights/ImageDelivery.js +13 -13
  317. package/models/trace/insights/ImageDelivery.js.map +1 -1
  318. package/models/trace/insights/LCPBreakdown.d.ts +3 -2
  319. package/models/trace/insights/LCPBreakdown.js +21 -15
  320. package/models/trace/insights/LCPBreakdown.js.map +1 -1
  321. package/models/trace/insights/LCPDiscovery.d.ts +2 -2
  322. package/models/trace/insights/LCPDiscovery.js +9 -11
  323. package/models/trace/insights/LCPDiscovery.js.map +1 -1
  324. package/models/trace/insights/LegacyJavaScript.d.ts +1 -1
  325. package/models/trace/insights/LegacyJavaScript.js +5 -4
  326. package/models/trace/insights/LegacyJavaScript.js.map +1 -1
  327. package/models/trace/insights/Models.js +1 -1
  328. package/models/trace/insights/Models.js.map +1 -1
  329. package/models/trace/insights/ModernHTTP.d.ts +2 -2
  330. package/models/trace/insights/ModernHTTP.js +6 -6
  331. package/models/trace/insights/ModernHTTP.js.map +1 -1
  332. package/models/trace/insights/NetworkDependencyTree.d.ts +6 -3
  333. package/models/trace/insights/NetworkDependencyTree.js +19 -16
  334. package/models/trace/insights/NetworkDependencyTree.js.map +1 -1
  335. package/models/trace/insights/RenderBlocking.d.ts +2 -2
  336. package/models/trace/insights/RenderBlocking.js +11 -11
  337. package/models/trace/insights/RenderBlocking.js.map +1 -1
  338. package/models/trace/insights/SlowCSSSelector.d.ts +3 -2
  339. package/models/trace/insights/SlowCSSSelector.js +9 -6
  340. package/models/trace/insights/SlowCSSSelector.js.map +1 -1
  341. package/models/trace/insights/Statistics.js +1 -1
  342. package/models/trace/insights/Statistics.js.map +1 -1
  343. package/models/trace/insights/ThirdParties.d.ts +2 -1
  344. package/models/trace/insights/ThirdParties.js +8 -5
  345. package/models/trace/insights/ThirdParties.js.map +1 -1
  346. package/models/trace/insights/Viewport.d.ts +2 -1
  347. package/models/trace/insights/Viewport.js +8 -5
  348. package/models/trace/insights/Viewport.js.map +1 -1
  349. package/models/trace/insights/devtools_entrypoint-bundle-typescript-tsconfig.json +6 -2
  350. package/models/trace/insights/insights-tsconfig.json +6 -2
  351. package/models/trace/insights/insights.d.ts +2 -0
  352. package/models/trace/insights/insights.js +3 -1
  353. package/models/trace/insights/insights.js.map +1 -1
  354. package/models/trace/insights/types.d.ts +4 -1
  355. package/models/trace/insights/types.js +2 -1
  356. package/models/trace/insights/types.js.map +1 -1
  357. package/models/trace/lantern/core/LanternError.js +1 -1
  358. package/models/trace/lantern/core/LanternError.js.map +1 -1
  359. package/models/trace/lantern/core/NetworkAnalyzer.js +1 -1
  360. package/models/trace/lantern/core/NetworkAnalyzer.js.map +1 -1
  361. package/models/trace/lantern/core/core-tsconfig.json +6 -2
  362. package/models/trace/lantern/core/core.js +1 -1
  363. package/models/trace/lantern/core/core.js.map +1 -1
  364. package/models/trace/lantern/core/devtools_entrypoint-bundle-typescript-tsconfig.json +6 -2
  365. package/models/trace/lantern/devtools_entrypoint-bundle-typescript-tsconfig.json +6 -2
  366. package/models/trace/lantern/graph/BaseNode.js +1 -1
  367. package/models/trace/lantern/graph/BaseNode.js.map +1 -1
  368. package/models/trace/lantern/graph/CPUNode.js +1 -1
  369. package/models/trace/lantern/graph/CPUNode.js.map +1 -1
  370. package/models/trace/lantern/graph/NetworkNode.js +1 -1
  371. package/models/trace/lantern/graph/NetworkNode.js.map +1 -1
  372. package/models/trace/lantern/graph/PageDependencyGraph.js +1 -1
  373. package/models/trace/lantern/graph/PageDependencyGraph.js.map +1 -1
  374. package/models/trace/lantern/graph/devtools_entrypoint-bundle-typescript-tsconfig.json +6 -2
  375. package/models/trace/lantern/graph/graph-tsconfig.json +6 -2
  376. package/models/trace/lantern/graph/graph.js +1 -1
  377. package/models/trace/lantern/graph/graph.js.map +1 -1
  378. package/models/trace/lantern/lantern-tsconfig.json +6 -2
  379. package/models/trace/lantern/lantern.js +1 -1
  380. package/models/trace/lantern/lantern.js.map +1 -1
  381. package/models/trace/lantern/metrics/FirstContentfulPaint.js +1 -1
  382. package/models/trace/lantern/metrics/FirstContentfulPaint.js.map +1 -1
  383. package/models/trace/lantern/metrics/Interactive.js +1 -1
  384. package/models/trace/lantern/metrics/Interactive.js.map +1 -1
  385. package/models/trace/lantern/metrics/LargestContentfulPaint.js +1 -1
  386. package/models/trace/lantern/metrics/LargestContentfulPaint.js.map +1 -1
  387. package/models/trace/lantern/metrics/MaxPotentialFID.js +1 -1
  388. package/models/trace/lantern/metrics/MaxPotentialFID.js.map +1 -1
  389. package/models/trace/lantern/metrics/Metric.js +1 -1
  390. package/models/trace/lantern/metrics/Metric.js.map +1 -1
  391. package/models/trace/lantern/metrics/SpeedIndex.js +1 -1
  392. package/models/trace/lantern/metrics/SpeedIndex.js.map +1 -1
  393. package/models/trace/lantern/metrics/TBTUtils.js +1 -1
  394. package/models/trace/lantern/metrics/TBTUtils.js.map +1 -1
  395. package/models/trace/lantern/metrics/TotalBlockingTime.js +1 -1
  396. package/models/trace/lantern/metrics/TotalBlockingTime.js.map +1 -1
  397. package/models/trace/lantern/metrics/devtools_entrypoint-bundle-typescript-tsconfig.json +6 -2
  398. package/models/trace/lantern/metrics/metrics-tsconfig.json +6 -2
  399. package/models/trace/lantern/metrics/metrics.js +1 -1
  400. package/models/trace/lantern/metrics/metrics.js.map +1 -1
  401. package/models/trace/lantern/simulation/ConnectionPool.js +1 -1
  402. package/models/trace/lantern/simulation/ConnectionPool.js.map +1 -1
  403. package/models/trace/lantern/simulation/Constants.js +1 -1
  404. package/models/trace/lantern/simulation/Constants.js.map +1 -1
  405. package/models/trace/lantern/simulation/DNSCache.js +1 -1
  406. package/models/trace/lantern/simulation/DNSCache.js.map +1 -1
  407. package/models/trace/lantern/simulation/SimulationTimingMap.js +1 -1
  408. package/models/trace/lantern/simulation/SimulationTimingMap.js.map +1 -1
  409. package/models/trace/lantern/simulation/Simulator.js +1 -1
  410. package/models/trace/lantern/simulation/Simulator.js.map +1 -1
  411. package/models/trace/lantern/simulation/TCPConnection.js +1 -1
  412. package/models/trace/lantern/simulation/TCPConnection.js.map +1 -1
  413. package/models/trace/lantern/simulation/devtools_entrypoint-bundle-typescript-tsconfig.json +6 -2
  414. package/models/trace/lantern/simulation/simulation-tsconfig.json +6 -2
  415. package/models/trace/lantern/simulation/simulation.js +1 -1
  416. package/models/trace/lantern/simulation/simulation.js.map +1 -1
  417. package/models/trace/lantern/types/Lantern.js +1 -1
  418. package/models/trace/lantern/types/Lantern.js.map +1 -1
  419. package/models/trace/lantern/types/devtools_entrypoint-bundle-typescript-tsconfig.json +6 -2
  420. package/models/trace/lantern/types/types-tsconfig.json +6 -2
  421. package/models/trace/lantern/types/types.js +1 -1
  422. package/models/trace/lantern/types/types.js.map +1 -1
  423. package/models/trace/trace-tsconfig.json +10 -2
  424. package/models/trace/trace.d.ts +5 -1
  425. package/models/trace/trace.js +6 -2
  426. package/models/trace/trace.js.map +1 -1
  427. package/models/trace/types/Configuration.d.ts +11 -0
  428. package/models/trace/types/Configuration.js +1 -1
  429. package/models/trace/types/Configuration.js.map +1 -1
  430. package/models/trace/types/Extensions.d.ts +25 -13
  431. package/models/trace/types/Extensions.js +6 -3
  432. package/models/trace/types/Extensions.js.map +1 -1
  433. package/models/trace/types/File.d.ts +13 -2
  434. package/models/trace/types/File.js +1 -1
  435. package/models/trace/types/File.js.map +1 -1
  436. package/models/trace/types/Overlays.d.ts +5 -4
  437. package/models/trace/types/Overlays.js +1 -1
  438. package/models/trace/types/Overlays.js.map +1 -1
  439. package/models/trace/types/Timing.d.ts +1 -0
  440. package/models/trace/types/Timing.js +1 -1
  441. package/models/trace/types/Timing.js.map +1 -1
  442. package/models/trace/types/TraceEvents.d.ts +81 -61
  443. package/models/trace/types/TraceEvents.js +42 -29
  444. package/models/trace/types/TraceEvents.js.map +1 -1
  445. package/models/trace/types/devtools_entrypoint-bundle-typescript-tsconfig.json +6 -2
  446. package/models/trace/types/types-tsconfig.json +6 -2
  447. package/models/trace/types/types.js +1 -1
  448. package/models/trace/types/types.js.map +1 -1
  449. package/package.json +1 -1
  450. package/test/test-trace-engine.mjs +4 -4
@@ -1,35 +1,30 @@
1
- // Copyright 2022 The Chromium Authors. All rights reserved.
1
+ // Copyright 2022 The Chromium Authors
2
2
  // Use of this source code is governed by a BSD-style license that can be
3
3
  // found in the LICENSE file.
4
+ import * as Platform from '../../../core/platform/platform.js';
4
5
  import * as Helpers from '../helpers/helpers.js';
5
6
  import * as Types from '../types/types.js';
6
7
  import { data as metaHandlerData } from './MetaHandler.js';
7
8
  import { ScoreClassification } from './PageLoadMetricsHandler.js';
8
- // This handler serves two purposes. It generates a list of events that are
9
- // used to show user clicks in the timeline. It is also used to gather
10
- // EventTimings into Interactions, which we use to show interactions and
11
- // highlight long interactions to the user, along with INP.
12
- // We don't need to know which process / thread these events occurred in,
13
- // because they are effectively global, so we just track all that we find.
14
- const allEvents = [];
15
- const beginCommitCompositorFrameEvents = [];
16
- const parseMetaViewportEvents = [];
9
+ // This handler gathers EventTimings into Interactions, which we use to show
10
+ // interactions and highlight long interactions to the user, along with INP.
11
+ let beginCommitCompositorFrameEvents = [];
12
+ let parseMetaViewportEvents = [];
17
13
  export const LONG_INTERACTION_THRESHOLD = Helpers.Timing.milliToMicro(Types.Timing.Milli(200));
18
14
  const INP_GOOD_TIMING = LONG_INTERACTION_THRESHOLD;
19
15
  const INP_MEDIUM_TIMING = Helpers.Timing.milliToMicro(Types.Timing.Milli(500));
20
16
  let longestInteractionEvent = null;
21
- const interactionEvents = [];
22
- const interactionEventsWithNoNesting = [];
23
- const eventTimingEndEventsById = new Map();
24
- const eventTimingStartEventsForInteractions = [];
17
+ let interactionEvents = [];
18
+ let interactionEventsWithNoNesting = [];
19
+ let eventTimingStartEventsForInteractions = [];
20
+ let eventTimingEndEventsForInteractions = [];
25
21
  export function reset() {
26
- allEvents.length = 0;
27
- beginCommitCompositorFrameEvents.length = 0;
28
- parseMetaViewportEvents.length = 0;
29
- interactionEvents.length = 0;
30
- eventTimingStartEventsForInteractions.length = 0;
31
- eventTimingEndEventsById.clear();
32
- interactionEventsWithNoNesting.length = 0;
22
+ beginCommitCompositorFrameEvents = [];
23
+ parseMetaViewportEvents = [];
24
+ interactionEvents = [];
25
+ eventTimingStartEventsForInteractions = [];
26
+ eventTimingEndEventsForInteractions = [];
27
+ interactionEventsWithNoNesting = [];
33
28
  longestInteractionEvent = null;
34
29
  }
35
30
  export function handleEvent(event) {
@@ -46,9 +41,8 @@ export function handleEvent(event) {
46
41
  }
47
42
  if (Types.Events.isEventTimingEnd(event)) {
48
43
  // Store the end event; for each start event that is an interaction, we need the matching end event to calculate the duration correctly.
49
- eventTimingEndEventsById.set(event.id, event);
44
+ eventTimingEndEventsForInteractions.push(event);
50
45
  }
51
- allEvents.push(event);
52
46
  // From this point on we want to find events that represent interactions.
53
47
  // These events are always start events - those are the ones that contain all
54
48
  // the metadata about the interaction.
@@ -119,8 +113,17 @@ export function categoryOfInteraction(interaction) {
119
113
  * =======B=[keyup]=====
120
114
  * ====C=[pointerdown]=
121
115
  * =D=[pointerup]=
116
+ *
117
+ * Additionally, this method will also maximise the processing duration of the
118
+ * events that we keep as non-nested. We want to make sure we give an accurate
119
+ * representation of main thread activity, so if we keep an event + hide its
120
+ * nested children, we set the top level event's processing start &
121
+ * processing end to be the earliest processing start & the latest processing
122
+ * end of its children. This ensures we report a more accurate main thread
123
+ * activity time which is important as we want developers to focus on fixing
124
+ * this.
122
125
  **/
123
- export function removeNestedInteractions(interactions) {
126
+ export function removeNestedInteractionsAndSetProcessingTime(interactions) {
124
127
  /**
125
128
  * Because we nest events only that are in the same category, we store the
126
129
  * longest event for a given end time by category.
@@ -197,68 +200,86 @@ function writeSyntheticTimespans(event) {
197
200
  }
198
201
  export async function finalize() {
199
202
  const { navigationsByFrameId } = metaHandlerData();
200
- // For each interaction start event, find the async end event by the ID, and then create the Synthetic Interaction event.
201
- for (const interactionStartEvent of eventTimingStartEventsForInteractions) {
202
- const endEvent = eventTimingEndEventsById.get(interactionStartEvent.id);
203
- if (!endEvent) {
204
- // If we cannot find an end event, bail and drop this event.
205
- continue;
206
- }
207
- const { type, interactionId, timeStamp, processingStart, processingEnd } = interactionStartEvent.args.data;
208
- if (!type || !interactionId || !timeStamp || !processingStart || !processingEnd) {
209
- // A valid interaction event that we care about has to have a type (e.g. pointerdown, keyup).
210
- // We also need to ensure it has an interactionId and various timings. There are edge cases where these aren't included in the trace event.
211
- continue;
203
+ const beginAndEndEvents = Platform.ArrayUtilities.mergeOrdered(eventTimingStartEventsForInteractions, eventTimingEndEventsForInteractions, Helpers.Trace.eventTimeComparator);
204
+ // Pair up the begin & end events and create synthetic user timing events.
205
+ const beginEventById = new Map();
206
+ for (const event of beginAndEndEvents) {
207
+ if (Types.Events.isEventTimingStart(event)) {
208
+ const forId = beginEventById.get(event.id) ?? [];
209
+ forId.push(event);
210
+ beginEventById.set(event.id, forId);
212
211
  }
213
- // In the future we will add microsecond timestamps to the trace events…
214
- // (See https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/timing/window_performance.cc;l=900-901;drc=b503c262e425eae59ced4a80d59d176ed07152c7 )
215
- // …but until then we can use the millisecond precision values that are in
216
- // the trace event. To adjust them to be relative to the event.ts and the
217
- // trace timestamps, for both processingStart and processingEnd we subtract
218
- // the event timestamp (NOT event.ts, but the timeStamp millisecond value
219
- // emitted in args.data), and then add that value to the event.ts. This
220
- // will give us a processingStart and processingEnd time in microseconds
221
- // that is relative to event.ts, and can be used when drawing boxes.
222
- // There is some inaccuracy here as we are converting milliseconds to microseconds, but it is good enough until the backend emits more accurate numbers.
223
- const processingStartRelativeToTraceTime = Types.Timing.Micro(Helpers.Timing.milliToMicro(processingStart) - Helpers.Timing.milliToMicro(timeStamp) +
224
- interactionStartEvent.ts);
225
- const processingEndRelativeToTraceTime = Types.Timing.Micro((Helpers.Timing.milliToMicro(processingEnd) - Helpers.Timing.milliToMicro(timeStamp)) +
226
- interactionStartEvent.ts);
227
- // Ultimate frameId fallback only needed for TSC, see comments in the type.
228
- const frameId = interactionStartEvent.args.frame ?? interactionStartEvent.args.data.frame ?? '';
229
- const navigation = Helpers.Trace.getNavigationForTraceEvent(interactionStartEvent, frameId, navigationsByFrameId);
230
- const navigationId = navigation?.args.data?.navigationId;
231
- const interactionEvent = Helpers.SyntheticEvents.SyntheticEventsManager.registerSyntheticEvent({
232
- // Use the start event to define the common fields.
233
- rawSourceEvent: interactionStartEvent,
234
- cat: interactionStartEvent.cat,
235
- name: interactionStartEvent.name,
236
- pid: interactionStartEvent.pid,
237
- tid: interactionStartEvent.tid,
238
- ph: interactionStartEvent.ph,
239
- processingStart: processingStartRelativeToTraceTime,
240
- processingEnd: processingEndRelativeToTraceTime,
241
- // These will be set in writeSyntheticTimespans()
242
- inputDelay: Types.Timing.Micro(-1),
243
- mainThreadHandling: Types.Timing.Micro(-1),
244
- presentationDelay: Types.Timing.Micro(-1),
245
- args: {
246
- data: {
247
- beginEvent: interactionStartEvent,
248
- endEvent,
249
- frame: frameId,
250
- navigationId,
212
+ else if (Types.Events.isEventTimingEnd(event)) {
213
+ const beginEvents = beginEventById.get(event.id) ?? [];
214
+ const beginEvent = beginEvents.pop();
215
+ if (!beginEvent) {
216
+ continue;
217
+ }
218
+ const { type, interactionId, timeStamp, processingStart, processingEnd } = beginEvent.args.data;
219
+ if (!type || !interactionId || !timeStamp || !processingStart || !processingEnd) {
220
+ // A valid interaction event that we care about has to have a type (e.g. pointerdown, keyup).
221
+ // We also need to ensure it has an interactionId and various timings. There are edge cases where these aren't included in the trace event.
222
+ continue;
223
+ }
224
+ // In the future we will add microsecond timestamps to the trace events…
225
+ // (See https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/timing/window_performance.cc;l=900-901;drc=b503c262e425eae59ced4a80d59d176ed07152c7 )
226
+ // …but until then we can use the millisecond precision values that are in
227
+ // the trace event. To adjust them to be relative to the event.ts and the
228
+ // trace timestamps, for both processingStart and processingEnd we subtract
229
+ // the event timestamp (NOT event.ts, but the timeStamp millisecond value
230
+ // emitted in args.data), and then add that value to the event.ts. This
231
+ // will give us a processingStart and processingEnd time in microseconds
232
+ // that is relative to event.ts, and can be used when drawing boxes.
233
+ // There is some inaccuracy here as we are converting milliseconds to
234
+ // microseconds, but it is good enough until the backend emits more
235
+ // accurate numbers.
236
+ const processingStartRelativeToTraceTime = Types.Timing.Micro(Helpers.Timing.milliToMicro(processingStart) - Helpers.Timing.milliToMicro(timeStamp) + beginEvent.ts);
237
+ const processingEndRelativeToTraceTime = Types.Timing.Micro((Helpers.Timing.milliToMicro(processingEnd) - Helpers.Timing.milliToMicro(timeStamp)) + beginEvent.ts);
238
+ // Ultimate frameId fallback only needed for TSC, see comments in the type.
239
+ const frameId = beginEvent.args.frame ?? beginEvent.args.data.frame ?? '';
240
+ const navigation = Helpers.Trace.getNavigationForTraceEvent(beginEvent, frameId, navigationsByFrameId);
241
+ const navigationId = navigation?.args.data?.navigationId;
242
+ const interactionEvent = Helpers.SyntheticEvents.SyntheticEventsManager.registerSyntheticEvent({
243
+ // Use the start event to define the common fields.
244
+ rawSourceEvent: beginEvent,
245
+ cat: beginEvent.cat,
246
+ name: beginEvent.name,
247
+ pid: beginEvent.pid,
248
+ tid: beginEvent.tid,
249
+ ph: beginEvent.ph,
250
+ processingStart: processingStartRelativeToTraceTime,
251
+ processingEnd: processingEndRelativeToTraceTime,
252
+ // These will be set in writeSyntheticTimespans()
253
+ inputDelay: Types.Timing.Micro(-1),
254
+ mainThreadHandling: Types.Timing.Micro(-1),
255
+ presentationDelay: Types.Timing.Micro(-1),
256
+ args: {
257
+ data: {
258
+ beginEvent,
259
+ endEvent: event,
260
+ frame: frameId,
261
+ navigationId,
262
+ },
251
263
  },
252
- },
253
- ts: interactionStartEvent.ts,
254
- dur: Types.Timing.Micro(endEvent.ts - interactionStartEvent.ts),
255
- type: interactionStartEvent.args.data.type,
256
- interactionId: interactionStartEvent.args.data.interactionId,
257
- });
258
- writeSyntheticTimespans(interactionEvent);
259
- interactionEvents.push(interactionEvent);
264
+ ts: beginEvent.ts,
265
+ dur: Types.Timing.Micro(event.ts - beginEvent.ts),
266
+ type: beginEvent.args.data.type,
267
+ interactionId: beginEvent.args.data.interactionId,
268
+ });
269
+ writeSyntheticTimespans(interactionEvent);
270
+ interactionEvents.push(interactionEvent);
271
+ }
260
272
  }
261
- interactionEventsWithNoNesting.push(...removeNestedInteractions(interactionEvents));
273
+ // Once we gather up all the interactions, we want to remove nested
274
+ // interactions. Interactions can be nested because one user action (e.g. a
275
+ // click) will cause a pointerdown, pointerup and click. But we don't want to
276
+ // fill the interactions track with lots of noise. To fix this, we go through
277
+ // all the events and remove any nested ones so on the timeline we focus the
278
+ // user on the most important events, which we define as the longest one. But
279
+ // this algorithm assumes the events are in ASC order, so we first sort the
280
+ // set of interactions.
281
+ Helpers.Trace.sortTraceEventsInPlace(interactionEvents);
282
+ interactionEventsWithNoNesting.push(...removeNestedInteractionsAndSetProcessingTime(interactionEvents));
262
283
  // Pick the longest interactions from the set that were not nested, as we
263
284
  // know those are the set of the largest interactions.
264
285
  for (const interactionEvent of interactionEventsWithNoNesting) {
@@ -269,7 +290,6 @@ export async function finalize() {
269
290
  }
270
291
  export function data() {
271
292
  return {
272
- allEvents,
273
293
  beginCommitCompositorFrameEvents,
274
294
  parseMetaViewportEvents,
275
295
  interactionEvents,
@@ -1 +1 @@
1
- {"version":3,"file":"UserInteractionsHandler.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/handlers/UserInteractionsHandler.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2022 The Chromium Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\nimport * as Helpers from '../helpers/helpers.js';\nimport * as Types from '../types/types.js';\n\nimport {data as metaHandlerData} from './MetaHandler.js';\nimport {ScoreClassification} from './PageLoadMetricsHandler.js';\nimport type {HandlerName} from './types.js';\n\n// This handler serves two purposes. It generates a list of events that are\n// used to show user clicks in the timeline. It is also used to gather\n// EventTimings into Interactions, which we use to show interactions and\n// highlight long interactions to the user, along with INP.\n\n// We don't need to know which process / thread these events occurred in,\n// because they are effectively global, so we just track all that we find.\nconst allEvents: Types.Events.EventTimingBeginOrEnd[] = [];\n\nconst beginCommitCompositorFrameEvents: Types.Events.BeginCommitCompositorFrame[] = [];\nconst parseMetaViewportEvents: Types.Events.ParseMetaViewport[] = [];\n\nexport const LONG_INTERACTION_THRESHOLD = Helpers.Timing.milliToMicro(Types.Timing.Milli(200));\n\nconst INP_GOOD_TIMING = LONG_INTERACTION_THRESHOLD;\nconst INP_MEDIUM_TIMING = Helpers.Timing.milliToMicro(Types.Timing.Milli(500));\n\nexport interface UserInteractionsData {\n /** All the user events we found in the trace */\n allEvents: readonly Types.Events.EventTimingBeginOrEnd[];\n /** All the BeginCommitCompositorFrame events we found in the trace */\n beginCommitCompositorFrameEvents: readonly Types.Events.BeginCommitCompositorFrame[];\n /** All the ParseMetaViewport events we found in the trace */\n parseMetaViewportEvents: readonly Types.Events.ParseMetaViewport[];\n /**\n * All the interaction events we found in the trace that had an\n * interactionId and a duration > 0\n **/\n interactionEvents: readonly Types.Events.SyntheticInteractionPair[];\n /**\n * If the user rapidly generates interaction events (think typing into a\n * text box), in the UI we only really want to show the user the longest\n * interaction in that set.\n * For example picture interactions like this:\n * ===[interaction A]==========\n * =[interaction B]======\n * =[interaction C]=\n *\n * These events all end at the same time, and so in this instance we only want\n * to show the first interaction A on the timeline, as that is the longest one\n * and the one the developer should be focusing on. So this array of events is\n * all the interaction events filtered down, removing any nested interactions\n * entirely.\n **/\n interactionEventsWithNoNesting: readonly Types.Events.SyntheticInteractionPair[];\n // The longest duration interaction event. Can be null if the trace has no interaction events.\n longestInteractionEvent: Readonly<Types.Events.SyntheticInteractionPair>|null;\n // All interactions that went over the interaction threshold (200ms, see https://web.dev/inp/)\n interactionsOverThreshold: Readonly<Set<Types.Events.SyntheticInteractionPair>>;\n}\n\nlet longestInteractionEvent: Types.Events.SyntheticInteractionPair|null = null;\n\nconst interactionEvents: Types.Events.SyntheticInteractionPair[] = [];\nconst interactionEventsWithNoNesting: Types.Events.SyntheticInteractionPair[] = [];\nconst eventTimingEndEventsById = new Map<string, Types.Events.EventTimingEnd>();\nconst eventTimingStartEventsForInteractions: Types.Events.EventTimingBegin[] = [];\n\nexport function reset(): void {\n allEvents.length = 0;\n beginCommitCompositorFrameEvents.length = 0;\n parseMetaViewportEvents.length = 0;\n interactionEvents.length = 0;\n eventTimingStartEventsForInteractions.length = 0;\n eventTimingEndEventsById.clear();\n interactionEventsWithNoNesting.length = 0;\n longestInteractionEvent = null;\n}\n\nexport function handleEvent(event: Types.Events.Event): void {\n if (Types.Events.isBeginCommitCompositorFrame(event)) {\n beginCommitCompositorFrameEvents.push(event);\n return;\n }\n\n if (Types.Events.isParseMetaViewport(event)) {\n parseMetaViewportEvents.push(event);\n return;\n }\n\n if (!Types.Events.isEventTiming(event)) {\n return;\n }\n\n if (Types.Events.isEventTimingEnd(event)) {\n // Store the end event; for each start event that is an interaction, we need the matching end event to calculate the duration correctly.\n eventTimingEndEventsById.set(event.id, event);\n }\n\n allEvents.push(event);\n\n // From this point on we want to find events that represent interactions.\n // These events are always start events - those are the ones that contain all\n // the metadata about the interaction.\n if (!event.args.data || !Types.Events.isEventTimingStart(event)) {\n return;\n }\n const {duration, interactionId} = event.args.data;\n // We exclude events for the sake of interactions if:\n // 1. They have no duration.\n // 2. They have no interactionId\n // 3. They have an interactionId of 0: this indicates that it's not an\n // interaction that we care about because it hasn't had its own interactionId\n // set (0 is the default on the backend).\n // See: https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/timing/responsiveness_metrics.cc;l=133;drc=40c209a9c365ebb9f16fb99dfe78c7fe768b9594\n\n if (duration < 1 || interactionId === undefined || interactionId === 0) {\n return;\n }\n\n // Store the start event. In the finalize() function we will pair this with\n // its end event and create the synthetic interaction event.\n eventTimingStartEventsForInteractions.push(event);\n}\n\n/**\n * See https://web.dev/better-responsiveness-metric/#interaction-types for the\n * table that defines these sets.\n **/\nconst pointerEventTypes = new Set([\n 'pointerdown',\n 'touchstart',\n 'pointerup',\n 'touchend',\n 'mousedown',\n 'mouseup',\n 'click',\n]);\n\nconst keyboardEventTypes = new Set([\n 'keydown',\n 'keypress',\n 'keyup',\n]);\n\nexport type InteractionCategory = 'KEYBOARD'|'POINTER'|'OTHER';\nexport function categoryOfInteraction(interaction: Types.Events.SyntheticInteractionPair): InteractionCategory {\n if (pointerEventTypes.has(interaction.type)) {\n return 'POINTER';\n }\n if (keyboardEventTypes.has(interaction.type)) {\n return 'KEYBOARD';\n }\n\n return 'OTHER';\n}\n\n/**\n * We define a set of interactions as nested where:\n * 1. Their end times align.\n * 2. The longest interaction's start time is earlier than all other\n * interactions with the same end time.\n * 3. The interactions are of the same category [each interaction is either\n * categorised as keyboard, or pointer.]\n *\n * =============A=[pointerup]=\n * ====B=[pointerdown]=\n * ===C=[pointerdown]==\n * ===D=[pointerup]===\n *\n * In this example, B, C and D are all nested and therefore should not be\n * returned from this function.\n *\n * However, in this example we would only consider B nested (under A) and D\n * nested (under C). A and C both stay because they are of different types.\n * ========A=[keydown]====\n * =======B=[keyup]=====\n * ====C=[pointerdown]=\n * =D=[pointerup]=\n **/\nexport function removeNestedInteractions(interactions: readonly Types.Events.SyntheticInteractionPair[]):\n readonly Types.Events.SyntheticInteractionPair[] {\n /**\n * Because we nest events only that are in the same category, we store the\n * longest event for a given end time by category.\n **/\n const earliestEventForEndTimePerCategory:\n Record<InteractionCategory, Map<Types.Timing.Micro, Types.Events.SyntheticInteractionPair>> = {\n POINTER: new Map(),\n KEYBOARD: new Map(),\n OTHER: new Map(),\n };\n\n function storeEventIfEarliestForCategoryAndEndTime(interaction: Types.Events.SyntheticInteractionPair): void {\n const category = categoryOfInteraction(interaction);\n const earliestEventForEndTime = earliestEventForEndTimePerCategory[category];\n const endTime = Types.Timing.Micro(interaction.ts + interaction.dur);\n\n const earliestCurrentEvent = earliestEventForEndTime.get(endTime);\n if (!earliestCurrentEvent) {\n earliestEventForEndTime.set(endTime, interaction);\n return;\n }\n if (interaction.ts < earliestCurrentEvent.ts) {\n earliestEventForEndTime.set(endTime, interaction);\n } else if (\n interaction.ts === earliestCurrentEvent.ts &&\n interaction.interactionId === earliestCurrentEvent.interactionId) {\n // We have seen in traces that the same interaction can have multiple\n // events (e.g. a 'click' and a 'pointerdown'). Often only one of these\n // events will have an event handler bound to it which caused delay on\n // the main thread, and the others will not. This leads to a situation\n // where if we pick one of the events that had no event handler, its\n // processing duration (processingEnd - processingStart) will be 0, but if we\n // had picked the event that had the slow event handler, we would show\n // correctly the main thread delay due to the event handler.\n // So, if we find events with the same interactionId and the same\n // begin/end times, we pick the one with the largest (processingEnd -\n // processingStart) time in order to make sure we find the event with the\n // worst main thread delay, as that is the one the user should care\n // about.\n const currentProcessingDuration = earliestCurrentEvent.processingEnd - earliestCurrentEvent.processingStart;\n const newProcessingDuration = interaction.processingEnd - interaction.processingStart;\n\n // Use the new interaction if it has a longer processing duration than the existing one.\n if (newProcessingDuration > currentProcessingDuration) {\n earliestEventForEndTime.set(endTime, interaction);\n }\n }\n\n // Maximize the processing duration based on the \"children\" interactions.\n // We pick the earliest start processing duration, and the latest end\n // processing duration to avoid under-reporting.\n if (interaction.processingStart < earliestCurrentEvent.processingStart) {\n earliestCurrentEvent.processingStart = interaction.processingStart;\n writeSyntheticTimespans(earliestCurrentEvent);\n }\n if (interaction.processingEnd > earliestCurrentEvent.processingEnd) {\n earliestCurrentEvent.processingEnd = interaction.processingEnd;\n writeSyntheticTimespans(earliestCurrentEvent);\n }\n }\n\n for (const interaction of interactions) {\n storeEventIfEarliestForCategoryAndEndTime(interaction);\n }\n\n // Combine all the events that we have kept from all the per-category event\n // maps back into an array and sort them by timestamp.\n const keptEvents = Object.values(earliestEventForEndTimePerCategory)\n .flatMap(eventsByEndTime => Array.from(eventsByEndTime.values()));\n keptEvents.sort((eventA, eventB) => {\n return eventA.ts - eventB.ts;\n });\n return keptEvents;\n}\n\nfunction writeSyntheticTimespans(event: Types.Events.SyntheticInteractionPair): void {\n const startEvent = event.args.data.beginEvent;\n const endEvent = event.args.data.endEvent;\n\n event.inputDelay = Types.Timing.Micro(event.processingStart - startEvent.ts);\n event.mainThreadHandling = Types.Timing.Micro(event.processingEnd - event.processingStart);\n event.presentationDelay = Types.Timing.Micro(endEvent.ts - event.processingEnd);\n}\n\nexport async function finalize(): Promise<void> {\n const {navigationsByFrameId} = metaHandlerData();\n\n // For each interaction start event, find the async end event by the ID, and then create the Synthetic Interaction event.\n for (const interactionStartEvent of eventTimingStartEventsForInteractions) {\n const endEvent = eventTimingEndEventsById.get(interactionStartEvent.id);\n if (!endEvent) {\n // If we cannot find an end event, bail and drop this event.\n continue;\n }\n const {type, interactionId, timeStamp, processingStart, processingEnd} = interactionStartEvent.args.data;\n if (!type || !interactionId || !timeStamp || !processingStart || !processingEnd) {\n // A valid interaction event that we care about has to have a type (e.g. pointerdown, keyup).\n // We also need to ensure it has an interactionId and various timings. There are edge cases where these aren't included in the trace event.\n continue;\n }\n\n // In the future we will add microsecond timestamps to the trace events…\n // (See https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/timing/window_performance.cc;l=900-901;drc=b503c262e425eae59ced4a80d59d176ed07152c7 )\n // …but until then we can use the millisecond precision values that are in\n // the trace event. To adjust them to be relative to the event.ts and the\n // trace timestamps, for both processingStart and processingEnd we subtract\n // the event timestamp (NOT event.ts, but the timeStamp millisecond value\n // emitted in args.data), and then add that value to the event.ts. This\n // will give us a processingStart and processingEnd time in microseconds\n // that is relative to event.ts, and can be used when drawing boxes.\n // There is some inaccuracy here as we are converting milliseconds to microseconds, but it is good enough until the backend emits more accurate numbers.\n const processingStartRelativeToTraceTime = Types.Timing.Micro(\n Helpers.Timing.milliToMicro(processingStart) - Helpers.Timing.milliToMicro(timeStamp) +\n interactionStartEvent.ts,\n );\n\n const processingEndRelativeToTraceTime = Types.Timing.Micro(\n (Helpers.Timing.milliToMicro(processingEnd) - Helpers.Timing.milliToMicro(timeStamp)) +\n interactionStartEvent.ts);\n\n // Ultimate frameId fallback only needed for TSC, see comments in the type.\n const frameId = interactionStartEvent.args.frame ?? interactionStartEvent.args.data.frame ?? '';\n const navigation = Helpers.Trace.getNavigationForTraceEvent(interactionStartEvent, frameId, navigationsByFrameId);\n const navigationId = navigation?.args.data?.navigationId;\n const interactionEvent =\n Helpers.SyntheticEvents.SyntheticEventsManager.registerSyntheticEvent<Types.Events.SyntheticInteractionPair>({\n // Use the start event to define the common fields.\n rawSourceEvent: interactionStartEvent,\n cat: interactionStartEvent.cat,\n name: interactionStartEvent.name,\n pid: interactionStartEvent.pid,\n tid: interactionStartEvent.tid,\n ph: interactionStartEvent.ph,\n processingStart: processingStartRelativeToTraceTime,\n processingEnd: processingEndRelativeToTraceTime,\n // These will be set in writeSyntheticTimespans()\n inputDelay: Types.Timing.Micro(-1),\n mainThreadHandling: Types.Timing.Micro(-1),\n presentationDelay: Types.Timing.Micro(-1),\n args: {\n data: {\n beginEvent: interactionStartEvent,\n endEvent,\n frame: frameId,\n navigationId,\n },\n },\n ts: interactionStartEvent.ts,\n dur: Types.Timing.Micro(endEvent.ts - interactionStartEvent.ts),\n type: interactionStartEvent.args.data.type,\n interactionId: interactionStartEvent.args.data.interactionId,\n });\n writeSyntheticTimespans(interactionEvent);\n\n interactionEvents.push(interactionEvent);\n }\n\n interactionEventsWithNoNesting.push(...removeNestedInteractions(interactionEvents));\n\n // Pick the longest interactions from the set that were not nested, as we\n // know those are the set of the largest interactions.\n for (const interactionEvent of interactionEventsWithNoNesting) {\n if (!longestInteractionEvent || longestInteractionEvent.dur < interactionEvent.dur) {\n longestInteractionEvent = interactionEvent;\n }\n }\n}\n\nexport function data(): UserInteractionsData {\n return {\n allEvents,\n beginCommitCompositorFrameEvents,\n parseMetaViewportEvents,\n interactionEvents,\n interactionEventsWithNoNesting,\n longestInteractionEvent,\n interactionsOverThreshold: new Set(interactionEvents.filter(event => {\n return event.dur > LONG_INTERACTION_THRESHOLD;\n })),\n };\n}\n\nexport function deps(): HandlerName[] {\n return ['Meta'];\n}\n\n/**\n * Classifications sourced from\n * https://web.dev/articles/inp#good-score\n */\nexport function scoreClassificationForInteractionToNextPaint(timing: Types.Timing.Micro): ScoreClassification {\n if (timing <= INP_GOOD_TIMING) {\n return ScoreClassification.GOOD;\n }\n\n if (timing <= INP_MEDIUM_TIMING) {\n return ScoreClassification.OK;\n }\n\n return ScoreClassification.BAD;\n}\n"]}
1
+ {"version":3,"file":"UserInteractionsHandler.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/handlers/UserInteractionsHandler.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2022 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 Platform from '../../../core/platform/platform.js';\nimport * as Helpers from '../helpers/helpers.js';\nimport * as Types from '../types/types.js';\n\nimport {data as metaHandlerData} from './MetaHandler.js';\nimport {ScoreClassification} from './PageLoadMetricsHandler.js';\nimport type {HandlerName} from './types.js';\n\n// This handler gathers EventTimings into Interactions, which we use to show\n// interactions and highlight long interactions to the user, along with INP.\n\nlet beginCommitCompositorFrameEvents: Types.Events.BeginCommitCompositorFrame[] = [];\nlet parseMetaViewportEvents: Types.Events.ParseMetaViewport[] = [];\n\nexport const LONG_INTERACTION_THRESHOLD = Helpers.Timing.milliToMicro(Types.Timing.Milli(200));\n\nconst INP_GOOD_TIMING = LONG_INTERACTION_THRESHOLD;\nconst INP_MEDIUM_TIMING = Helpers.Timing.milliToMicro(Types.Timing.Milli(500));\n\nexport interface UserInteractionsData {\n /** All the BeginCommitCompositorFrame events we found in the trace */\n beginCommitCompositorFrameEvents: readonly Types.Events.BeginCommitCompositorFrame[];\n /** All the ParseMetaViewport events we found in the trace */\n parseMetaViewportEvents: readonly Types.Events.ParseMetaViewport[];\n /**\n * All the interaction events we found in the trace that had an\n * interactionId and a duration > 0\n **/\n interactionEvents: readonly Types.Events.SyntheticInteractionPair[];\n /**\n * If the user rapidly generates interaction events (think typing into a\n * text box), in the UI we only really want to show the user the longest\n * interaction in that set.\n * For example picture interactions like this:\n * ===[interaction A]==========\n * =[interaction B]======\n * =[interaction C]=\n *\n * These events all end at the same time, and so in this instance we only want\n * to show the first interaction A on the timeline, as that is the longest one\n * and the one the developer should be focusing on. So this array of events is\n * all the interaction events filtered down, removing any nested interactions\n * entirely.\n **/\n interactionEventsWithNoNesting: readonly Types.Events.SyntheticInteractionPair[];\n // The longest duration interaction event. Can be null if the trace has no interaction events.\n longestInteractionEvent: Readonly<Types.Events.SyntheticInteractionPair>|null;\n // All interactions that went over the interaction threshold (200ms, see https://web.dev/inp/)\n interactionsOverThreshold: Readonly<Set<Types.Events.SyntheticInteractionPair>>;\n}\n\nlet longestInteractionEvent: Types.Events.SyntheticInteractionPair|null = null;\n\nlet interactionEvents: Types.Events.SyntheticInteractionPair[] = [];\nlet interactionEventsWithNoNesting: Types.Events.SyntheticInteractionPair[] = [];\nlet eventTimingStartEventsForInteractions: Types.Events.EventTimingBegin[] = [];\nlet eventTimingEndEventsForInteractions: Types.Events.EventTimingEnd[] = [];\n\nexport function reset(): void {\n beginCommitCompositorFrameEvents = [];\n parseMetaViewportEvents = [];\n interactionEvents = [];\n eventTimingStartEventsForInteractions = [];\n eventTimingEndEventsForInteractions = [];\n interactionEventsWithNoNesting = [];\n longestInteractionEvent = null;\n}\n\nexport function handleEvent(event: Types.Events.Event): void {\n if (Types.Events.isBeginCommitCompositorFrame(event)) {\n beginCommitCompositorFrameEvents.push(event);\n return;\n }\n\n if (Types.Events.isParseMetaViewport(event)) {\n parseMetaViewportEvents.push(event);\n return;\n }\n\n if (!Types.Events.isEventTiming(event)) {\n return;\n }\n\n if (Types.Events.isEventTimingEnd(event)) {\n // Store the end event; for each start event that is an interaction, we need the matching end event to calculate the duration correctly.\n eventTimingEndEventsForInteractions.push(event);\n }\n\n // From this point on we want to find events that represent interactions.\n // These events are always start events - those are the ones that contain all\n // the metadata about the interaction.\n if (!event.args.data || !Types.Events.isEventTimingStart(event)) {\n return;\n }\n const {duration, interactionId} = event.args.data;\n // We exclude events for the sake of interactions if:\n // 1. They have no duration.\n // 2. They have no interactionId\n // 3. They have an interactionId of 0: this indicates that it's not an\n // interaction that we care about because it hasn't had its own interactionId\n // set (0 is the default on the backend).\n // See: https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/timing/responsiveness_metrics.cc;l=133;drc=40c209a9c365ebb9f16fb99dfe78c7fe768b9594\n\n if (duration < 1 || interactionId === undefined || interactionId === 0) {\n return;\n }\n\n // Store the start event. In the finalize() function we will pair this with\n // its end event and create the synthetic interaction event.\n eventTimingStartEventsForInteractions.push(event);\n}\n\n/**\n * See https://web.dev/better-responsiveness-metric/#interaction-types for the\n * table that defines these sets.\n **/\nconst pointerEventTypes = new Set([\n 'pointerdown',\n 'touchstart',\n 'pointerup',\n 'touchend',\n 'mousedown',\n 'mouseup',\n 'click',\n]);\n\nconst keyboardEventTypes = new Set([\n 'keydown',\n 'keypress',\n 'keyup',\n]);\n\nexport type InteractionCategory = 'KEYBOARD'|'POINTER'|'OTHER';\nexport function categoryOfInteraction(interaction: Types.Events.SyntheticInteractionPair): InteractionCategory {\n if (pointerEventTypes.has(interaction.type)) {\n return 'POINTER';\n }\n if (keyboardEventTypes.has(interaction.type)) {\n return 'KEYBOARD';\n }\n\n return 'OTHER';\n}\n\n/**\n * We define a set of interactions as nested where:\n * 1. Their end times align.\n * 2. The longest interaction's start time is earlier than all other\n * interactions with the same end time.\n * 3. The interactions are of the same category [each interaction is either\n * categorised as keyboard, or pointer.]\n *\n * =============A=[pointerup]=\n * ====B=[pointerdown]=\n * ===C=[pointerdown]==\n * ===D=[pointerup]===\n *\n * In this example, B, C and D are all nested and therefore should not be\n * returned from this function.\n *\n * However, in this example we would only consider B nested (under A) and D\n * nested (under C). A and C both stay because they are of different types.\n * ========A=[keydown]====\n * =======B=[keyup]=====\n * ====C=[pointerdown]=\n * =D=[pointerup]=\n *\n * Additionally, this method will also maximise the processing duration of the\n * events that we keep as non-nested. We want to make sure we give an accurate\n * representation of main thread activity, so if we keep an event + hide its\n * nested children, we set the top level event's processing start &\n * processing end to be the earliest processing start & the latest processing\n * end of its children. This ensures we report a more accurate main thread\n * activity time which is important as we want developers to focus on fixing\n * this.\n **/\nexport function removeNestedInteractionsAndSetProcessingTime(\n interactions: readonly Types.Events.SyntheticInteractionPair[]): readonly Types.Events.SyntheticInteractionPair[] {\n /**\n * Because we nest events only that are in the same category, we store the\n * longest event for a given end time by category.\n **/\n const earliestEventForEndTimePerCategory:\n Record<InteractionCategory, Map<Types.Timing.Micro, Types.Events.SyntheticInteractionPair>> = {\n POINTER: new Map(),\n KEYBOARD: new Map(),\n OTHER: new Map(),\n };\n\n function storeEventIfEarliestForCategoryAndEndTime(interaction: Types.Events.SyntheticInteractionPair): void {\n const category = categoryOfInteraction(interaction);\n const earliestEventForEndTime = earliestEventForEndTimePerCategory[category];\n const endTime = Types.Timing.Micro(interaction.ts + interaction.dur);\n\n const earliestCurrentEvent = earliestEventForEndTime.get(endTime);\n if (!earliestCurrentEvent) {\n earliestEventForEndTime.set(endTime, interaction);\n return;\n }\n if (interaction.ts < earliestCurrentEvent.ts) {\n earliestEventForEndTime.set(endTime, interaction);\n } else if (\n interaction.ts === earliestCurrentEvent.ts &&\n interaction.interactionId === earliestCurrentEvent.interactionId) {\n // We have seen in traces that the same interaction can have multiple\n // events (e.g. a 'click' and a 'pointerdown'). Often only one of these\n // events will have an event handler bound to it which caused delay on\n // the main thread, and the others will not. This leads to a situation\n // where if we pick one of the events that had no event handler, its\n // processing duration (processingEnd - processingStart) will be 0, but if we\n // had picked the event that had the slow event handler, we would show\n // correctly the main thread delay due to the event handler.\n // So, if we find events with the same interactionId and the same\n // begin/end times, we pick the one with the largest (processingEnd -\n // processingStart) time in order to make sure we find the event with the\n // worst main thread delay, as that is the one the user should care\n // about.\n const currentProcessingDuration = earliestCurrentEvent.processingEnd - earliestCurrentEvent.processingStart;\n const newProcessingDuration = interaction.processingEnd - interaction.processingStart;\n\n // Use the new interaction if it has a longer processing duration than the existing one.\n if (newProcessingDuration > currentProcessingDuration) {\n earliestEventForEndTime.set(endTime, interaction);\n }\n }\n\n // Maximize the processing duration based on the \"children\" interactions.\n // We pick the earliest start processing duration, and the latest end\n // processing duration to avoid under-reporting.\n if (interaction.processingStart < earliestCurrentEvent.processingStart) {\n earliestCurrentEvent.processingStart = interaction.processingStart;\n writeSyntheticTimespans(earliestCurrentEvent);\n }\n if (interaction.processingEnd > earliestCurrentEvent.processingEnd) {\n earliestCurrentEvent.processingEnd = interaction.processingEnd;\n writeSyntheticTimespans(earliestCurrentEvent);\n }\n }\n\n for (const interaction of interactions) {\n storeEventIfEarliestForCategoryAndEndTime(interaction);\n }\n\n // Combine all the events that we have kept from all the per-category event\n // maps back into an array and sort them by timestamp.\n const keptEvents = Object.values(earliestEventForEndTimePerCategory)\n .flatMap(eventsByEndTime => Array.from(eventsByEndTime.values()));\n keptEvents.sort((eventA, eventB) => {\n return eventA.ts - eventB.ts;\n });\n return keptEvents;\n}\n\nfunction writeSyntheticTimespans(event: Types.Events.SyntheticInteractionPair): void {\n const startEvent = event.args.data.beginEvent;\n const endEvent = event.args.data.endEvent;\n\n event.inputDelay = Types.Timing.Micro(event.processingStart - startEvent.ts);\n event.mainThreadHandling = Types.Timing.Micro(event.processingEnd - event.processingStart);\n event.presentationDelay = Types.Timing.Micro(endEvent.ts - event.processingEnd);\n}\n\nexport async function finalize(): Promise<void> {\n const {navigationsByFrameId} = metaHandlerData();\n\n const beginAndEndEvents = Platform.ArrayUtilities.mergeOrdered(\n eventTimingStartEventsForInteractions, eventTimingEndEventsForInteractions,\n Helpers.Trace.eventTimeComparator) as Types.Events.EventTimingBeginOrEnd[];\n\n // Pair up the begin & end events and create synthetic user timing events.\n const beginEventById = new Map<string, Types.Events.EventTimingBegin[]>();\n for (const event of beginAndEndEvents) {\n if (Types.Events.isEventTimingStart(event)) {\n const forId = beginEventById.get(event.id) ?? [];\n forId.push(event);\n beginEventById.set(event.id, forId);\n } else if (Types.Events.isEventTimingEnd(event)) {\n const beginEvents = beginEventById.get(event.id) ?? [];\n const beginEvent = beginEvents.pop();\n if (!beginEvent) {\n continue;\n }\n const {type, interactionId, timeStamp, processingStart, processingEnd} = beginEvent.args.data;\n if (!type || !interactionId || !timeStamp || !processingStart || !processingEnd) {\n // A valid interaction event that we care about has to have a type (e.g. pointerdown, keyup).\n // We also need to ensure it has an interactionId and various timings. There are edge cases where these aren't included in the trace event.\n continue;\n }\n // In the future we will add microsecond timestamps to the trace events…\n // (See https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/timing/window_performance.cc;l=900-901;drc=b503c262e425eae59ced4a80d59d176ed07152c7 )\n // …but until then we can use the millisecond precision values that are in\n // the trace event. To adjust them to be relative to the event.ts and the\n // trace timestamps, for both processingStart and processingEnd we subtract\n // the event timestamp (NOT event.ts, but the timeStamp millisecond value\n // emitted in args.data), and then add that value to the event.ts. This\n // will give us a processingStart and processingEnd time in microseconds\n // that is relative to event.ts, and can be used when drawing boxes.\n // There is some inaccuracy here as we are converting milliseconds to\n // microseconds, but it is good enough until the backend emits more\n // accurate numbers.\n const processingStartRelativeToTraceTime = Types.Timing.Micro(\n Helpers.Timing.milliToMicro(processingStart) - Helpers.Timing.milliToMicro(timeStamp) + beginEvent.ts,\n );\n\n const processingEndRelativeToTraceTime = Types.Timing.Micro(\n (Helpers.Timing.milliToMicro(processingEnd) - Helpers.Timing.milliToMicro(timeStamp)) + beginEvent.ts);\n\n // Ultimate frameId fallback only needed for TSC, see comments in the type.\n const frameId = beginEvent.args.frame ?? beginEvent.args.data.frame ?? '';\n const navigation = Helpers.Trace.getNavigationForTraceEvent(beginEvent, frameId, navigationsByFrameId);\n const navigationId = navigation?.args.data?.navigationId;\n const interactionEvent =\n Helpers.SyntheticEvents.SyntheticEventsManager.registerSyntheticEvent<Types.Events.SyntheticInteractionPair>({\n // Use the start event to define the common fields.\n rawSourceEvent: beginEvent,\n cat: beginEvent.cat,\n name: beginEvent.name,\n pid: beginEvent.pid,\n tid: beginEvent.tid,\n ph: beginEvent.ph,\n processingStart: processingStartRelativeToTraceTime,\n processingEnd: processingEndRelativeToTraceTime,\n // These will be set in writeSyntheticTimespans()\n inputDelay: Types.Timing.Micro(-1),\n mainThreadHandling: Types.Timing.Micro(-1),\n presentationDelay: Types.Timing.Micro(-1),\n args: {\n data: {\n beginEvent,\n endEvent: event,\n frame: frameId,\n navigationId,\n },\n },\n ts: beginEvent.ts,\n dur: Types.Timing.Micro(event.ts - beginEvent.ts),\n type: beginEvent.args.data.type,\n interactionId: beginEvent.args.data.interactionId,\n });\n writeSyntheticTimespans(interactionEvent);\n interactionEvents.push(interactionEvent);\n }\n }\n\n // Once we gather up all the interactions, we want to remove nested\n // interactions. Interactions can be nested because one user action (e.g. a\n // click) will cause a pointerdown, pointerup and click. But we don't want to\n // fill the interactions track with lots of noise. To fix this, we go through\n // all the events and remove any nested ones so on the timeline we focus the\n // user on the most important events, which we define as the longest one. But\n // this algorithm assumes the events are in ASC order, so we first sort the\n // set of interactions.\n Helpers.Trace.sortTraceEventsInPlace(interactionEvents);\n interactionEventsWithNoNesting.push(...removeNestedInteractionsAndSetProcessingTime(interactionEvents));\n\n // Pick the longest interactions from the set that were not nested, as we\n // know those are the set of the largest interactions.\n for (const interactionEvent of interactionEventsWithNoNesting) {\n if (!longestInteractionEvent || longestInteractionEvent.dur < interactionEvent.dur) {\n longestInteractionEvent = interactionEvent;\n }\n }\n}\n\nexport function data(): UserInteractionsData {\n return {\n beginCommitCompositorFrameEvents,\n parseMetaViewportEvents,\n interactionEvents,\n interactionEventsWithNoNesting,\n longestInteractionEvent,\n interactionsOverThreshold: new Set(interactionEvents.filter(event => {\n return event.dur > LONG_INTERACTION_THRESHOLD;\n })),\n };\n}\n\nexport function deps(): HandlerName[] {\n return ['Meta'];\n}\n\n/**\n * Classifications sourced from\n * https://web.dev/articles/inp#good-score\n */\nexport function scoreClassificationForInteractionToNextPaint(timing: Types.Timing.Micro): ScoreClassification {\n if (timing <= INP_GOOD_TIMING) {\n return ScoreClassification.GOOD;\n }\n\n if (timing <= INP_MEDIUM_TIMING) {\n return ScoreClassification.OK;\n }\n\n return ScoreClassification.BAD;\n}\n"]}
@@ -28,6 +28,27 @@ export interface UserTimingsData {
28
28
  measureTraceByTraceId: Map<number, Types.Events.UserTimingMeasure>;
29
29
  }
30
30
  export declare function reset(): void;
31
+ /**
32
+ * Similar to the default {@see Helpers.Trace.eventTimeComparator}
33
+ * but with a twist:
34
+ * In case of equal start and end times, put the second event (within a
35
+ * track) first.
36
+ *
37
+ * Explanation:
38
+ * User timing entries come as trace events dispatched when
39
+ * performance.measure/mark is called. The trace events buffered in
40
+ * devtools frontend are sorted by the start time. If their start time
41
+ * is the same, then the event for the first call will appear first.
42
+ *
43
+ * When entries are meant to be stacked, the corresponding
44
+ * performance.measure calls usually are done in bottom-up direction:
45
+ * calls for children first and for parent later (because the call
46
+ * is usually done when the measured task is over). This means that
47
+ * when two user timing events have the same start and end time, usually
48
+ * the second event is the parent of the first. Hence the switch.
49
+ *
50
+ */
51
+ export declare function userTimingComparator<T extends Types.Events.SyntheticEventPair | Types.Events.ConsoleTimeStamp>(a: T, b: T, originalArray: readonly T[]): number;
31
52
  export declare function handleEvent(event: Types.Events.Event): void;
32
53
  export declare function finalize(): Promise<void>;
33
54
  export declare function data(): UserTimingsData;
@@ -1,4 +1,4 @@
1
- // Copyright 2022 The Chromium Authors. All rights reserved.
1
+ // Copyright 2022 The Chromium Authors
2
2
  // Use of this source code is governed by a BSD-style license that can be
3
3
  // found in the LICENSE file.
4
4
  import * as Helpers from '../helpers/helpers.js';
@@ -19,18 +19,18 @@ let syntheticEvents = [];
19
19
  // overridden timestamp and duration. To prevent breaking potential deps
20
20
  // created since then, a second event was added instead of changing the
21
21
  // params of the first.
22
- const measureTraceByTraceId = new Map();
23
- const performanceMeasureEvents = [];
24
- const performanceMarkEvents = [];
25
- const consoleTimings = [];
26
- const timestampEvents = [];
22
+ let measureTraceByTraceId = new Map();
23
+ let performanceMeasureEvents = [];
24
+ let performanceMarkEvents = [];
25
+ let consoleTimings = [];
26
+ let timestampEvents = [];
27
27
  export function reset() {
28
- syntheticEvents.length = 0;
29
- performanceMeasureEvents.length = 0;
30
- performanceMarkEvents.length = 0;
31
- consoleTimings.length = 0;
32
- timestampEvents.length = 0;
33
- measureTraceByTraceId.clear();
28
+ syntheticEvents = [];
29
+ performanceMeasureEvents = [];
30
+ performanceMarkEvents = [];
31
+ consoleTimings = [];
32
+ timestampEvents = [];
33
+ measureTraceByTraceId = new Map();
34
34
  }
35
35
  const resourceTimingNames = [
36
36
  'workerStart',
@@ -75,11 +75,43 @@ const navTimingNames = [
75
75
  // Appear in the timings track (they still appear in the main thread
76
76
  // flame chart).
77
77
  const ignoredNames = [...resourceTimingNames, ...navTimingNames];
78
+ function getEventTimings(event) {
79
+ if ('dur' in event) {
80
+ // It's a SyntheticEventPair.
81
+ return { start: event.ts, end: Types.Timing.Micro(event.ts + (event.dur ?? 0)) };
82
+ }
83
+ if (Types.Events.isConsoleTimeStamp(event)) {
84
+ const { start, end } = event.args.data || {};
85
+ if (typeof start === 'number' && typeof end === 'number') {
86
+ return { start: Types.Timing.Micro(start), end: Types.Timing.Micro(end) };
87
+ }
88
+ }
89
+ // A ConsoleTimeStamp without start/end is just a point in time, so dur is 0.
90
+ return { start: event.ts, end: event.ts };
91
+ }
92
+ function getEventTrack(event) {
93
+ if (event.cat === 'blink.user_timing') {
94
+ // This is a SyntheticUserTimingPair
95
+ const detailString = event.args.data.beginEvent.args?.detail;
96
+ if (detailString) {
97
+ const details = Helpers.Trace.parseDevtoolsDetails(detailString, 'devtools');
98
+ if (details && 'track' in details) {
99
+ return details.track;
100
+ }
101
+ }
102
+ }
103
+ else if (Types.Events.isConsoleTimeStamp(event)) {
104
+ const track = event.args.data?.track;
105
+ return typeof track === 'string' ? track : undefined;
106
+ }
107
+ // SyntheticConsoleTimingPair does not have track info.
108
+ return undefined;
109
+ }
78
110
  /**
79
111
  * Similar to the default {@see Helpers.Trace.eventTimeComparator}
80
112
  * but with a twist:
81
- * In case of equal start and end times, always put the second event
82
- * first.
113
+ * In case of equal start and end times, put the second event (within a
114
+ * track) first.
83
115
  *
84
116
  * Explanation:
85
117
  * User timing entries come as trace events dispatched when
@@ -91,31 +123,27 @@ const ignoredNames = [...resourceTimingNames, ...navTimingNames];
91
123
  * performance.measure calls usually are done in bottom-up direction:
92
124
  * calls for children first and for parent later (because the call
93
125
  * is usually done when the measured task is over). This means that
94
- * when two user timing events have the start and end time, usually the
95
- * second event is the parent of the first. Hence the switch.
126
+ * when two user timing events have the same start and end time, usually
127
+ * the second event is the parent of the first. Hence the switch.
96
128
  *
97
129
  */
98
- function userTimingComparator(a, b, originalArray) {
99
- const aBeginTime = a.ts;
100
- const bBeginTime = b.ts;
101
- if (aBeginTime < bBeginTime) {
102
- return -1;
103
- }
104
- if (aBeginTime > bBeginTime) {
105
- return 1;
106
- }
107
- const aDuration = a.dur ?? 0;
108
- const bDuration = b.dur ?? 0;
109
- const aEndTime = aBeginTime + aDuration;
110
- const bEndTime = bBeginTime + bDuration;
111
- if (aEndTime > bEndTime) {
112
- return -1;
130
+ export function userTimingComparator(a, b, originalArray) {
131
+ const { start: aStart, end: aEnd } = getEventTimings(a);
132
+ const { start: bStart, end: bEnd } = getEventTimings(b);
133
+ const timeDifference = Helpers.Trace.compareBeginAndEnd(aStart, bStart, aEnd, bEnd);
134
+ if (timeDifference) {
135
+ return timeDifference;
113
136
  }
114
- if (aEndTime < bEndTime) {
115
- return 1;
137
+ // Never re-order entries across different tracks.
138
+ const aTrack = getEventTrack(a);
139
+ const bTrack = getEventTrack(b);
140
+ if (aTrack !== bTrack) {
141
+ return 0; // Preserve current positions.
116
142
  }
117
143
  // Prefer the event located in a further position in the original array.
118
- return originalArray.indexOf(b) - originalArray.indexOf(a);
144
+ const aIndex = originalArray.indexOf(a);
145
+ const bIndex = originalArray.indexOf(b);
146
+ return bIndex - aIndex;
119
147
  }
120
148
  export function handleEvent(event) {
121
149
  if (ignoredNames.includes(event.name)) {
@@ -142,15 +170,15 @@ export async function finalize() {
142
170
  const asyncEvents = [...performanceMeasureEvents, ...consoleTimings];
143
171
  syntheticEvents = Helpers.Trace.createMatchedSortedSyntheticEvents(asyncEvents);
144
172
  syntheticEvents = syntheticEvents.sort((a, b) => userTimingComparator(a, b, [...syntheticEvents]));
173
+ timestampEvents = timestampEvents.sort((a, b) => userTimingComparator(a, b, [...timestampEvents]));
145
174
  }
146
175
  export function data() {
147
176
  return {
148
- performanceMeasures: syntheticEvents.filter(e => e.cat === 'blink.user_timing'),
149
177
  consoleTimings: syntheticEvents.filter(e => e.cat === 'blink.console'),
150
- // TODO(crbug/41484172): UserTimingsHandler.test.ts fails if this is not copied.
151
- performanceMarks: [...performanceMarkEvents],
152
- timestampEvents: [...timestampEvents],
153
- measureTraceByTraceId: new Map(measureTraceByTraceId),
178
+ performanceMeasures: syntheticEvents.filter(e => e.cat === 'blink.user_timing'),
179
+ performanceMarks: performanceMarkEvents,
180
+ timestampEvents,
181
+ measureTraceByTraceId,
154
182
  };
155
183
  }
156
184
  //# sourceMappingURL=UserTimingsHandler.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"UserTimingsHandler.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/handlers/UserTimingsHandler.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2022 The Chromium Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\nimport * as Helpers from '../helpers/helpers.js';\nimport * as Types from '../types/types.js';\n\n/**\n * IMPORTANT!\n * See UserTimings.md in this directory for some handy documentation on\n * UserTimings and the trace events we parse currently.\n **/\nlet syntheticEvents: Array<Types.Events.SyntheticEventPair<Types.Events.PairableAsync>> = [];\n\n// There are two events dispatched for performance.measure calls: one to\n// represent the measured timing in the tracing clock (which we type as\n// PerformanceMeasure) and another one for the call itself (which we\n// type as UserTimingMeasure). The two events corresponding to the same\n// call are linked together by a common trace_id. The reason two events\n// are dispatched is because the first was originally added with the\n// implementation of the performance.measure API and it uses an\n// overridden timestamp and duration. To prevent breaking potential deps\n// created since then, a second event was added instead of changing the\n// params of the first.\nconst measureTraceByTraceId = new Map<number, Types.Events.UserTimingMeasure>();\nconst performanceMeasureEvents: Types.Events.PerformanceMeasure[] = [];\nconst performanceMarkEvents: Types.Events.PerformanceMark[] = [];\n\nconst consoleTimings: Array<Types.Events.ConsoleTimeBegin|Types.Events.ConsoleTimeEnd> = [];\n\nconst timestampEvents: Types.Events.ConsoleTimeStamp[] = [];\n\nexport interface UserTimingsData {\n /**\n * Events triggered with the performance.measure() API.\n * https://developer.mozilla.org/en-US/docs/Web/API/Performance/measure\n */\n performanceMeasures: readonly Types.Events.SyntheticUserTimingPair[];\n /**\n * Events triggered with the performance.mark() API.\n * https://developer.mozilla.org/en-US/docs/Web/API/Performance/mark\n */\n performanceMarks: readonly Types.Events.PerformanceMark[];\n /**\n * Events triggered with the console.time(), console.timeEnd() and\n * console.timeLog() API.\n * https://developer.mozilla.org/en-US/docs/Web/API/console/time\n */\n consoleTimings: readonly Types.Events.SyntheticConsoleTimingPair[];\n /**\n * Events triggered with the console.timeStamp() API\n * https://developer.mozilla.org/en-US/docs/Web/API/console/timeStamp\n */\n timestampEvents: readonly Types.Events.ConsoleTimeStamp[];\n /**\n * Events triggered to trace the call to performance.measure itself,\n * cached by trace_id.\n */\n measureTraceByTraceId: Map<number, Types.Events.UserTimingMeasure>;\n}\n\nexport function reset(): void {\n syntheticEvents.length = 0;\n performanceMeasureEvents.length = 0;\n performanceMarkEvents.length = 0;\n consoleTimings.length = 0;\n timestampEvents.length = 0;\n measureTraceByTraceId.clear();\n}\n\nconst resourceTimingNames = [\n 'workerStart',\n 'redirectStart',\n 'redirectEnd',\n 'fetchStart',\n 'domainLookupStart',\n 'domainLookupEnd',\n 'connectStart',\n 'connectEnd',\n 'secureConnectionStart',\n 'requestStart',\n 'responseStart',\n 'responseEnd',\n];\nconst navTimingNames = [\n 'navigationStart',\n 'unloadEventStart',\n 'unloadEventEnd',\n 'redirectStart',\n 'redirectEnd',\n 'fetchStart',\n 'commitNavigationEnd',\n 'domainLookupStart',\n 'domainLookupEnd',\n 'connectStart',\n 'connectEnd',\n 'secureConnectionStart',\n 'requestStart',\n 'responseStart',\n 'responseEnd',\n 'domLoading',\n 'domInteractive',\n 'domContentLoadedEventStart',\n 'domContentLoadedEventEnd',\n 'domComplete',\n 'loadEventStart',\n 'loadEventEnd',\n];\n// These are events dispatched under the blink.user_timing category\n// but that the user didn't add. Filter them out so that they do not\n// Appear in the timings track (they still appear in the main thread\n// flame chart).\nconst ignoredNames = [...resourceTimingNames, ...navTimingNames];\n\n/**\n * Similar to the default {@see Helpers.Trace.eventTimeComparator}\n * but with a twist:\n * In case of equal start and end times, always put the second event\n * first.\n *\n * Explanation:\n * User timing entries come as trace events dispatched when\n * performance.measure/mark is called. The trace events buffered in\n * devtools frontend are sorted by the start time. If their start time\n * is the same, then the event for the first call will appear first.\n *\n * When entries are meant to be stacked, the corresponding\n * performance.measure calls usually are done in bottom-up direction:\n * calls for children first and for parent later (because the call\n * is usually done when the measured task is over). This means that\n * when two user timing events have the start and end time, usually the\n * second event is the parent of the first. Hence the switch.\n *\n */\nfunction userTimingComparator(\n a: Helpers.Trace.TimeSpan, b: Helpers.Trace.TimeSpan, originalArray: Helpers.Trace.TimeSpan[]): number {\n const aBeginTime = a.ts;\n const bBeginTime = b.ts;\n if (aBeginTime < bBeginTime) {\n return -1;\n }\n if (aBeginTime > bBeginTime) {\n return 1;\n }\n const aDuration = a.dur ?? 0;\n const bDuration = b.dur ?? 0;\n const aEndTime = aBeginTime + aDuration;\n const bEndTime = bBeginTime + bDuration;\n if (aEndTime > bEndTime) {\n return -1;\n }\n if (aEndTime < bEndTime) {\n return 1;\n }\n // Prefer the event located in a further position in the original array.\n return originalArray.indexOf(b) - originalArray.indexOf(a);\n}\n\nexport function handleEvent(event: Types.Events.Event): void {\n if (ignoredNames.includes(event.name)) {\n return;\n }\n if (Types.Events.isUserTimingMeasure(event)) {\n measureTraceByTraceId.set(event.args.traceId, event);\n }\n if (Types.Events.isPerformanceMeasure(event)) {\n performanceMeasureEvents.push(event);\n return;\n }\n if (Types.Events.isPerformanceMark(event)) {\n performanceMarkEvents.push(event);\n }\n if (Types.Events.isConsoleTime(event)) {\n consoleTimings.push(event);\n }\n if (Types.Events.isConsoleTimeStamp(event)) {\n timestampEvents.push(event);\n }\n}\n\nexport async function finalize(): Promise<void> {\n const asyncEvents = [...performanceMeasureEvents, ...consoleTimings];\n syntheticEvents = Helpers.Trace.createMatchedSortedSyntheticEvents(asyncEvents);\n syntheticEvents = syntheticEvents.sort((a, b) => userTimingComparator(a, b, [...syntheticEvents]));\n}\n\nexport function data(): UserTimingsData {\n return {\n performanceMeasures: syntheticEvents.filter(e => e.cat === 'blink.user_timing') as\n Types.Events.SyntheticUserTimingPair[],\n consoleTimings: syntheticEvents.filter(e => e.cat === 'blink.console') as Types.Events.SyntheticConsoleTimingPair[],\n // TODO(crbug/41484172): UserTimingsHandler.test.ts fails if this is not copied.\n performanceMarks: [...performanceMarkEvents],\n timestampEvents: [...timestampEvents],\n measureTraceByTraceId: new Map(measureTraceByTraceId),\n };\n}\n"]}
1
+ {"version":3,"file":"UserTimingsHandler.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/handlers/UserTimingsHandler.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2022 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 Helpers from '../helpers/helpers.js';\nimport * as Types from '../types/types.js';\n\n/**\n * IMPORTANT!\n * See UserTimings.md in this directory for some handy documentation on\n * UserTimings and the trace events we parse currently.\n **/\nlet syntheticEvents: Array<Types.Events.SyntheticEventPair<Types.Events.PairableAsync>> = [];\n\n// There are two events dispatched for performance.measure calls: one to\n// represent the measured timing in the tracing clock (which we type as\n// PerformanceMeasure) and another one for the call itself (which we\n// type as UserTimingMeasure). The two events corresponding to the same\n// call are linked together by a common trace_id. The reason two events\n// are dispatched is because the first was originally added with the\n// implementation of the performance.measure API and it uses an\n// overridden timestamp and duration. To prevent breaking potential deps\n// created since then, a second event was added instead of changing the\n// params of the first.\nlet measureTraceByTraceId = new Map<number, Types.Events.UserTimingMeasure>();\nlet performanceMeasureEvents: Types.Events.PerformanceMeasure[] = [];\nlet performanceMarkEvents: Types.Events.PerformanceMark[] = [];\n\nlet consoleTimings: Array<Types.Events.ConsoleTimeBegin|Types.Events.ConsoleTimeEnd> = [];\n\nlet timestampEvents: Types.Events.ConsoleTimeStamp[] = [];\n\nexport interface UserTimingsData {\n /**\n * Events triggered with the performance.measure() API.\n * https://developer.mozilla.org/en-US/docs/Web/API/Performance/measure\n */\n performanceMeasures: readonly Types.Events.SyntheticUserTimingPair[];\n /**\n * Events triggered with the performance.mark() API.\n * https://developer.mozilla.org/en-US/docs/Web/API/Performance/mark\n */\n performanceMarks: readonly Types.Events.PerformanceMark[];\n /**\n * Events triggered with the console.time(), console.timeEnd() and\n * console.timeLog() API.\n * https://developer.mozilla.org/en-US/docs/Web/API/console/time\n */\n consoleTimings: readonly Types.Events.SyntheticConsoleTimingPair[];\n /**\n * Events triggered with the console.timeStamp() API\n * https://developer.mozilla.org/en-US/docs/Web/API/console/timeStamp\n */\n timestampEvents: readonly Types.Events.ConsoleTimeStamp[];\n /**\n * Events triggered to trace the call to performance.measure itself,\n * cached by trace_id.\n */\n measureTraceByTraceId: Map<number, Types.Events.UserTimingMeasure>;\n}\n\nexport function reset(): void {\n syntheticEvents = [];\n performanceMeasureEvents = [];\n performanceMarkEvents = [];\n consoleTimings = [];\n timestampEvents = [];\n measureTraceByTraceId = new Map();\n}\n\nconst resourceTimingNames = [\n 'workerStart',\n 'redirectStart',\n 'redirectEnd',\n 'fetchStart',\n 'domainLookupStart',\n 'domainLookupEnd',\n 'connectStart',\n 'connectEnd',\n 'secureConnectionStart',\n 'requestStart',\n 'responseStart',\n 'responseEnd',\n];\nconst navTimingNames = [\n 'navigationStart',\n 'unloadEventStart',\n 'unloadEventEnd',\n 'redirectStart',\n 'redirectEnd',\n 'fetchStart',\n 'commitNavigationEnd',\n 'domainLookupStart',\n 'domainLookupEnd',\n 'connectStart',\n 'connectEnd',\n 'secureConnectionStart',\n 'requestStart',\n 'responseStart',\n 'responseEnd',\n 'domLoading',\n 'domInteractive',\n 'domContentLoadedEventStart',\n 'domContentLoadedEventEnd',\n 'domComplete',\n 'loadEventStart',\n 'loadEventEnd',\n];\n// These are events dispatched under the blink.user_timing category\n// but that the user didn't add. Filter them out so that they do not\n// Appear in the timings track (they still appear in the main thread\n// flame chart).\nconst ignoredNames = [...resourceTimingNames, ...navTimingNames];\n\nfunction getEventTimings(event: Types.Events.SyntheticEventPair|Types.Events.ConsoleTimeStamp):\n {start: Types.Timing.Micro, end: Types.Timing.Micro} {\n if ('dur' in event) {\n // It's a SyntheticEventPair.\n return {start: event.ts, end: Types.Timing.Micro(event.ts + (event.dur ?? 0))};\n }\n\n if (Types.Events.isConsoleTimeStamp(event)) {\n const {start, end} = event.args.data || {};\n if (typeof start === 'number' && typeof end === 'number') {\n return {start: Types.Timing.Micro(start), end: Types.Timing.Micro(end)};\n }\n }\n\n // A ConsoleTimeStamp without start/end is just a point in time, so dur is 0.\n return {start: event.ts, end: event.ts};\n}\n\nfunction getEventTrack(event: Types.Events.SyntheticEventPair|Types.Events.ConsoleTimeStamp): string|undefined {\n if (event.cat === 'blink.user_timing') {\n // This is a SyntheticUserTimingPair\n const detailString =\n ((event as Types.Events.SyntheticUserTimingPair).args.data.beginEvent.args as {detail?: string})?.detail;\n if (detailString) {\n const details = Helpers.Trace.parseDevtoolsDetails(detailString, 'devtools');\n if (details && 'track' in details) {\n return details.track;\n }\n }\n } else if (Types.Events.isConsoleTimeStamp(event)) {\n const track = event.args.data?.track;\n return typeof track === 'string' ? track : undefined;\n }\n\n // SyntheticConsoleTimingPair does not have track info.\n return undefined;\n}\n\n/**\n * Similar to the default {@see Helpers.Trace.eventTimeComparator}\n * but with a twist:\n * In case of equal start and end times, put the second event (within a\n * track) first.\n *\n * Explanation:\n * User timing entries come as trace events dispatched when\n * performance.measure/mark is called. The trace events buffered in\n * devtools frontend are sorted by the start time. If their start time\n * is the same, then the event for the first call will appear first.\n *\n * When entries are meant to be stacked, the corresponding\n * performance.measure calls usually are done in bottom-up direction:\n * calls for children first and for parent later (because the call\n * is usually done when the measured task is over). This means that\n * when two user timing events have the same start and end time, usually\n * the second event is the parent of the first. Hence the switch.\n *\n */\nexport function userTimingComparator<T extends Types.Events.SyntheticEventPair|Types.Events.ConsoleTimeStamp>(\n a: T, b: T, originalArray: readonly T[]): number {\n const {start: aStart, end: aEnd} = getEventTimings(a);\n const {start: bStart, end: bEnd} = getEventTimings(b);\n const timeDifference = Helpers.Trace.compareBeginAndEnd(aStart, bStart, aEnd, bEnd);\n if (timeDifference) {\n return timeDifference;\n }\n\n // Never re-order entries across different tracks.\n const aTrack = getEventTrack(a);\n const bTrack = getEventTrack(b);\n if (aTrack !== bTrack) {\n return 0; // Preserve current positions.\n }\n\n // Prefer the event located in a further position in the original array.\n const aIndex = originalArray.indexOf(a);\n const bIndex = originalArray.indexOf(b);\n return bIndex - aIndex;\n}\n\nexport function handleEvent(event: Types.Events.Event): void {\n if (ignoredNames.includes(event.name)) {\n return;\n }\n if (Types.Events.isUserTimingMeasure(event)) {\n measureTraceByTraceId.set(event.args.traceId, event);\n }\n if (Types.Events.isPerformanceMeasure(event)) {\n performanceMeasureEvents.push(event);\n return;\n }\n if (Types.Events.isPerformanceMark(event)) {\n performanceMarkEvents.push(event);\n }\n if (Types.Events.isConsoleTime(event)) {\n consoleTimings.push(event);\n }\n if (Types.Events.isConsoleTimeStamp(event)) {\n timestampEvents.push(event);\n }\n}\n\nexport async function finalize(): Promise<void> {\n const asyncEvents = [...performanceMeasureEvents, ...consoleTimings];\n syntheticEvents = Helpers.Trace.createMatchedSortedSyntheticEvents(asyncEvents);\n syntheticEvents = syntheticEvents.sort((a, b) => userTimingComparator(a, b, [...syntheticEvents]));\n timestampEvents = timestampEvents.sort((a, b) => userTimingComparator(a, b, [...timestampEvents]));\n}\n\nexport function data(): UserTimingsData {\n return {\n consoleTimings: syntheticEvents.filter(e => e.cat === 'blink.console') as Types.Events.SyntheticConsoleTimingPair[],\n performanceMeasures: syntheticEvents.filter(e => e.cat === 'blink.user_timing') as\n Types.Events.SyntheticUserTimingPair[],\n performanceMarks: performanceMarkEvents,\n timestampEvents,\n measureTraceByTraceId,\n };\n}\n"]}