@paulirish/trace_engine 0.0.38 → 0.0.40

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 (276) hide show
  1. package/.tmp/tsbuildinfo/tsconfig.tsbuildinfo +1 -1
  2. package/core/platform/DevToolsPath.d.ts +30 -9
  3. package/core/platform/DevToolsPath.js +21 -0
  4. package/core/platform/DevToolsPath.js.map +1 -1
  5. package/core/platform/MapUtilities.js +1 -1
  6. package/core/platform/MapUtilities.js.map +1 -1
  7. package/core/platform/ServerTiming.d.ts +2 -2
  8. package/core/platform/ServerTiming.js.map +1 -1
  9. package/core/platform/StringUtilities.js +1 -1
  10. package/core/platform/StringUtilities.js.map +1 -1
  11. package/core/platform/devtools_entrypoint-bundle-typescript-tsconfig.json +2 -1
  12. package/core/platform/platform-tsconfig.json +2 -1
  13. package/generated/protocol.d.ts +203 -73
  14. package/locales/af.json +86 -0
  15. package/locales/am.json +86 -0
  16. package/locales/ar.json +86 -0
  17. package/locales/as.json +86 -0
  18. package/locales/az.json +86 -0
  19. package/locales/be.json +86 -0
  20. package/locales/bg.json +86 -0
  21. package/locales/bn.json +86 -0
  22. package/locales/bs.json +86 -0
  23. package/locales/ca.json +86 -0
  24. package/locales/cs.json +86 -0
  25. package/locales/cy.json +86 -0
  26. package/locales/da.json +86 -0
  27. package/locales/de.json +86 -0
  28. package/locales/el.json +86 -0
  29. package/locales/en-GB.json +86 -0
  30. package/locales/en-US.json +86 -0
  31. package/locales/en-XL.json +86 -0
  32. package/locales/es-419.json +86 -0
  33. package/locales/es.json +86 -0
  34. package/locales/et.json +86 -0
  35. package/locales/eu.json +86 -0
  36. package/locales/fa.json +86 -0
  37. package/locales/fi.json +86 -0
  38. package/locales/fil.json +86 -0
  39. package/locales/fr-CA.json +86 -0
  40. package/locales/fr.json +86 -0
  41. package/locales/gl.json +86 -0
  42. package/locales/gu.json +86 -0
  43. package/locales/he.json +86 -0
  44. package/locales/hi.json +86 -0
  45. package/locales/hr.json +86 -0
  46. package/locales/hu.json +86 -0
  47. package/locales/hy.json +86 -0
  48. package/locales/id.json +86 -0
  49. package/locales/is.json +86 -0
  50. package/locales/it.json +86 -0
  51. package/locales/ja.json +86 -0
  52. package/locales/ka.json +86 -0
  53. package/locales/kk.json +86 -0
  54. package/locales/km.json +86 -0
  55. package/locales/kn.json +86 -0
  56. package/locales/ko.json +86 -0
  57. package/locales/ky.json +86 -0
  58. package/locales/lo.json +86 -0
  59. package/locales/lt.json +86 -0
  60. package/locales/lv.json +86 -0
  61. package/locales/mk.json +86 -0
  62. package/locales/ml.json +86 -0
  63. package/locales/mn.json +86 -0
  64. package/locales/mr.json +86 -0
  65. package/locales/ms.json +86 -0
  66. package/locales/my.json +86 -0
  67. package/locales/ne.json +86 -0
  68. package/locales/nl.json +86 -0
  69. package/locales/no.json +86 -0
  70. package/locales/or.json +86 -0
  71. package/locales/pa.json +86 -0
  72. package/locales/pl.json +86 -0
  73. package/locales/pt-PT.json +86 -0
  74. package/locales/pt.json +86 -0
  75. package/locales/ro.json +86 -0
  76. package/locales/ru.json +86 -0
  77. package/locales/si.json +86 -0
  78. package/locales/sk.json +86 -0
  79. package/locales/sl.json +86 -0
  80. package/locales/sq.json +86 -0
  81. package/locales/sr-Latn.json +86 -0
  82. package/locales/sr.json +86 -0
  83. package/locales/sv.json +86 -0
  84. package/locales/sw.json +86 -0
  85. package/locales/ta.json +86 -0
  86. package/locales/te.json +86 -0
  87. package/locales/th.json +86 -0
  88. package/locales/tr.json +86 -0
  89. package/locales/uk.json +86 -0
  90. package/locales/ur.json +86 -0
  91. package/locales/uz.json +86 -0
  92. package/locales/vi.json +86 -0
  93. package/locales/zh-HK.json +86 -0
  94. package/locales/zh-TW.json +86 -0
  95. package/locales/zh.json +86 -0
  96. package/locales/zu.json +86 -0
  97. package/models/cpu_profile/ProfileTreeModel.js +0 -2
  98. package/models/cpu_profile/ProfileTreeModel.js.map +1 -1
  99. package/models/cpu_profile/cpu_profile-tsconfig.json +2 -1
  100. package/models/cpu_profile/devtools_entrypoint-bundle-typescript-tsconfig.json +2 -1
  101. package/models/trace/LanternComputationData.js +1 -1
  102. package/models/trace/LanternComputationData.js.map +1 -1
  103. package/models/trace/ModelImpl.d.ts +6 -6
  104. package/models/trace/ModelImpl.js +1 -0
  105. package/models/trace/ModelImpl.js.map +1 -1
  106. package/models/trace/Processor.d.ts +6 -0
  107. package/models/trace/Processor.js +90 -9
  108. package/models/trace/Processor.js.map +1 -1
  109. package/models/trace/devtools_entrypoint-bundle-typescript-tsconfig.json +2 -1
  110. package/models/trace/extras/StackTraceForEvent.d.ts +12 -0
  111. package/models/trace/extras/StackTraceForEvent.js +163 -0
  112. package/models/trace/extras/StackTraceForEvent.js.map +1 -0
  113. package/models/trace/extras/ThirdParties.d.ts +7 -4
  114. package/models/trace/extras/ThirdParties.js +56 -65
  115. package/models/trace/extras/ThirdParties.js.map +1 -1
  116. package/models/trace/extras/TraceTree.js +8 -8
  117. package/models/trace/extras/TraceTree.js.map +1 -1
  118. package/models/trace/extras/devtools_entrypoint-bundle-typescript-tsconfig.json +2 -1
  119. package/models/trace/extras/extras-tsconfig.json +3 -2
  120. package/models/trace/extras/extras.js.map +1 -1
  121. package/models/trace/handlers/AnimationFramesHandler.d.ts +11 -0
  122. package/models/trace/handlers/AnimationFramesHandler.js +102 -0
  123. package/models/trace/handlers/AnimationFramesHandler.js.map +1 -0
  124. package/models/trace/handlers/AsyncJSCallsHandler.d.ts +15 -0
  125. package/models/trace/handlers/AsyncJSCallsHandler.js +153 -0
  126. package/models/trace/handlers/AsyncJSCallsHandler.js.map +1 -0
  127. package/models/trace/handlers/DOMStatsHandler.d.ts +8 -0
  128. package/models/trace/handlers/DOMStatsHandler.js +22 -0
  129. package/models/trace/handlers/DOMStatsHandler.js.map +1 -0
  130. package/models/trace/handlers/ExtensionTraceDataHandler.d.ts +80 -2
  131. package/models/trace/handlers/ExtensionTraceDataHandler.js +163 -13
  132. package/models/trace/handlers/ExtensionTraceDataHandler.js.map +1 -1
  133. package/models/trace/handlers/FlowsHandler.js +47 -55
  134. package/models/trace/handlers/FlowsHandler.js.map +1 -1
  135. package/models/trace/handlers/InitiatorsHandler.d.ts +11 -0
  136. package/models/trace/handlers/InitiatorsHandler.js +33 -25
  137. package/models/trace/handlers/InitiatorsHandler.js.map +1 -1
  138. package/models/trace/handlers/LargestImagePaintHandler.d.ts +0 -2
  139. package/models/trace/handlers/LargestImagePaintHandler.js +27 -24
  140. package/models/trace/handlers/LargestImagePaintHandler.js.map +1 -1
  141. package/models/trace/handlers/LayoutShiftsHandler.d.ts +2 -2
  142. package/models/trace/handlers/LayoutShiftsHandler.js +3 -2
  143. package/models/trace/handlers/LayoutShiftsHandler.js.map +1 -1
  144. package/models/trace/handlers/MetaHandler.d.ts +2 -2
  145. package/models/trace/handlers/MetaHandler.js +1 -1
  146. package/models/trace/handlers/MetaHandler.js.map +1 -1
  147. package/models/trace/handlers/ModelHandlers.d.ts +4 -1
  148. package/models/trace/handlers/ModelHandlers.js +4 -1
  149. package/models/trace/handlers/ModelHandlers.js.map +1 -1
  150. package/models/trace/handlers/NetworkRequestsHandler.d.ts +2 -0
  151. package/models/trace/handlers/NetworkRequestsHandler.js +21 -0
  152. package/models/trace/handlers/NetworkRequestsHandler.js.map +1 -1
  153. package/models/trace/handlers/PageLoadMetricsHandler.d.ts +2 -2
  154. package/models/trace/handlers/PageLoadMetricsHandler.js +9 -9
  155. package/models/trace/handlers/PageLoadMetricsHandler.js.map +1 -1
  156. package/models/trace/handlers/RendererHandler.d.ts +4 -0
  157. package/models/trace/handlers/RendererHandler.js +33 -2
  158. package/models/trace/handlers/RendererHandler.js.map +1 -1
  159. package/models/trace/handlers/SamplesHandler.d.ts +2 -2
  160. package/models/trace/handlers/SamplesHandler.js +3 -3
  161. package/models/trace/handlers/SamplesHandler.js.map +1 -1
  162. package/models/trace/handlers/ServerTimingsHandler.js +2 -2
  163. package/models/trace/handlers/ServerTimingsHandler.js.map +1 -1
  164. package/models/trace/handlers/Threads.d.ts +1 -0
  165. package/models/trace/handlers/Threads.js +8 -0
  166. package/models/trace/handlers/Threads.js.map +1 -1
  167. package/models/trace/handlers/UserInteractionsHandler.js +5 -6
  168. package/models/trace/handlers/UserInteractionsHandler.js.map +1 -1
  169. package/models/trace/handlers/UserTimingsHandler.d.ts +1 -1
  170. package/models/trace/handlers/UserTimingsHandler.js +1 -1
  171. package/models/trace/handlers/UserTimingsHandler.js.map +1 -1
  172. package/models/trace/handlers/WarningsHandler.js +2 -2
  173. package/models/trace/handlers/WarningsHandler.js.map +1 -1
  174. package/models/trace/handlers/devtools_entrypoint-bundle-typescript-tsconfig.json +2 -1
  175. package/models/trace/handlers/handlers-tsconfig.json +9 -1
  176. package/models/trace/handlers/handlers.d.ts +1 -0
  177. package/models/trace/handlers/handlers.js +1 -0
  178. package/models/trace/handlers/handlers.js.map +1 -1
  179. package/models/trace/handlers/helpers.d.ts +19 -0
  180. package/models/trace/handlers/helpers.js +123 -0
  181. package/models/trace/handlers/helpers.js.map +1 -0
  182. package/models/trace/helpers/SamplesIntegrator.js +2 -2
  183. package/models/trace/helpers/SamplesIntegrator.js.map +1 -1
  184. package/models/trace/helpers/SyntheticEvents.js +1 -1
  185. package/models/trace/helpers/SyntheticEvents.js.map +1 -1
  186. package/models/trace/helpers/Timing.d.ts +5 -6
  187. package/models/trace/helpers/Timing.js +22 -31
  188. package/models/trace/helpers/Timing.js.map +1 -1
  189. package/models/trace/helpers/Trace.d.ts +24 -10
  190. package/models/trace/helpers/Trace.js +53 -6
  191. package/models/trace/helpers/Trace.js.map +1 -1
  192. package/models/trace/helpers/TreeHelpers.d.ts +0 -31
  193. package/models/trace/helpers/TreeHelpers.js +1 -142
  194. package/models/trace/helpers/TreeHelpers.js.map +1 -1
  195. package/models/trace/helpers/devtools_entrypoint-bundle-typescript-tsconfig.json +2 -1
  196. package/models/trace/helpers/helpers-tsconfig.json +2 -1
  197. package/models/trace/insights/CLSCulprits.js +1 -2
  198. package/models/trace/insights/CLSCulprits.js.map +1 -1
  199. package/models/trace/insights/Common.d.ts +38 -1
  200. package/models/trace/insights/Common.js +123 -0
  201. package/models/trace/insights/Common.js.map +1 -1
  202. package/models/trace/insights/DOMSize.d.ts +9 -0
  203. package/models/trace/insights/DOMSize.js +102 -0
  204. package/models/trace/insights/DOMSize.js.map +1 -0
  205. package/models/trace/insights/DocumentLatency.js +1 -1
  206. package/models/trace/insights/DocumentLatency.js.map +1 -1
  207. package/models/trace/insights/FontDisplay.js +1 -1
  208. package/models/trace/insights/FontDisplay.js.map +1 -1
  209. package/models/trace/insights/ImageDelivery.d.ts +22 -4
  210. package/models/trace/insights/ImageDelivery.js +76 -6
  211. package/models/trace/insights/ImageDelivery.js.map +1 -1
  212. package/models/trace/insights/LCPDiscovery.js +2 -2
  213. package/models/trace/insights/LCPDiscovery.js.map +1 -1
  214. package/models/trace/insights/LCPPhases.js +7 -7
  215. package/models/trace/insights/LCPPhases.js.map +1 -1
  216. package/models/trace/insights/Models.d.ts +1 -0
  217. package/models/trace/insights/Models.js +1 -0
  218. package/models/trace/insights/Models.js.map +1 -1
  219. package/models/trace/insights/Statistics.d.ts +14 -0
  220. package/models/trace/insights/Statistics.js +86 -0
  221. package/models/trace/insights/Statistics.js.map +1 -0
  222. package/models/trace/insights/ThirdParties.d.ts +2 -2
  223. package/models/trace/insights/ThirdParties.js +5 -4
  224. package/models/trace/insights/ThirdParties.js.map +1 -1
  225. package/models/trace/insights/Viewport.js +2 -2
  226. package/models/trace/insights/Viewport.js.map +1 -1
  227. package/models/trace/insights/devtools_entrypoint-bundle-typescript-tsconfig.json +2 -1
  228. package/models/trace/insights/insights-tsconfig.json +4 -1
  229. package/models/trace/insights/insights.d.ts +1 -0
  230. package/models/trace/insights/insights.js +1 -0
  231. package/models/trace/insights/insights.js.map +1 -1
  232. package/models/trace/insights/types.d.ts +4 -3
  233. package/models/trace/insights/types.js.map +1 -1
  234. package/models/trace/lantern/core/NetworkAnalyzer.d.ts +2 -2
  235. package/models/trace/lantern/core/NetworkAnalyzer.js +2 -3
  236. package/models/trace/lantern/core/NetworkAnalyzer.js.map +1 -1
  237. package/models/trace/lantern/core/core-tsconfig.json +2 -1
  238. package/models/trace/lantern/core/devtools_entrypoint-bundle-typescript-tsconfig.json +2 -1
  239. package/models/trace/lantern/devtools_entrypoint-bundle-typescript-tsconfig.json +2 -1
  240. package/models/trace/lantern/graph/PageDependencyGraph.js +0 -1
  241. package/models/trace/lantern/graph/PageDependencyGraph.js.map +1 -1
  242. package/models/trace/lantern/graph/devtools_entrypoint-bundle-typescript-tsconfig.json +2 -1
  243. package/models/trace/lantern/graph/graph-tsconfig.json +2 -1
  244. package/models/trace/lantern/lantern-tsconfig.json +2 -1
  245. package/models/trace/lantern/metrics/devtools_entrypoint-bundle-typescript-tsconfig.json +2 -1
  246. package/models/trace/lantern/metrics/metrics-tsconfig.json +2 -1
  247. package/models/trace/lantern/simulation/DNSCache.d.ts +2 -2
  248. package/models/trace/lantern/simulation/DNSCache.js.map +1 -1
  249. package/models/trace/lantern/simulation/devtools_entrypoint-bundle-typescript-tsconfig.json +2 -1
  250. package/models/trace/lantern/simulation/simulation-tsconfig.json +2 -1
  251. package/models/trace/lantern/types/Lantern.d.ts +10 -10
  252. package/models/trace/lantern/types/Lantern.js.map +1 -1
  253. package/models/trace/lantern/types/devtools_entrypoint-bundle-typescript-tsconfig.json +2 -1
  254. package/models/trace/lantern/types/types-tsconfig.json +2 -1
  255. package/models/trace/root-causes/LayoutShift.d.ts +6 -6
  256. package/models/trace/root-causes/LayoutShift.js +1 -1
  257. package/models/trace/root-causes/LayoutShift.js.map +1 -1
  258. package/models/trace/root-causes/RootCauses.d.ts +2 -2
  259. package/models/trace/root-causes/RootCauses.js.map +1 -1
  260. package/models/trace/root-causes/devtools_entrypoint-bundle-typescript-tsconfig.json +2 -1
  261. package/models/trace/root-causes/root-causes-tsconfig.json +2 -1
  262. package/models/trace/trace-tsconfig.json +2 -1
  263. package/models/trace/types/Configuration.d.ts +2 -2
  264. package/models/trace/types/Configuration.js.map +1 -1
  265. package/models/trace/types/Extensions.d.ts +3 -3
  266. package/models/trace/types/Extensions.js.map +1 -1
  267. package/models/trace/types/File.d.ts +11 -11
  268. package/models/trace/types/File.js.map +1 -1
  269. package/models/trace/types/Timing.js.map +1 -1
  270. package/models/trace/types/TraceEvents.d.ts +107 -31
  271. package/models/trace/types/TraceEvents.js +34 -14
  272. package/models/trace/types/TraceEvents.js.map +1 -1
  273. package/models/trace/types/devtools_entrypoint-bundle-typescript-tsconfig.json +2 -1
  274. package/models/trace/types/types-tsconfig.json +2 -1
  275. package/package.json +1 -1
  276. package/test/test-trace-engine.mjs +8 -7
@@ -1,6 +1,7 @@
1
1
  // Copyright 2024 The Chromium Authors. All rights reserved.
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 Types from '../types/types.js';
5
6
  // A flow is a logic connection between trace events. We display this
6
7
  // connection as arrows between trace events belonging to the same flow.
@@ -19,7 +20,17 @@ import * as Types from '../types/types.js';
19
20
  // in the flows where their corresponding phase events were placed (if
20
21
  // there are any corresponding flow phase events at all).
21
22
  const flowDataByGroupToken = new Map();
22
- const flowPhaseBindingTokenToFlowId = new Map();
23
+ // Given a trace event's flow binding tuple (timestamp, process id,
24
+ // thread id and category) we determine if there is any flow data bound
25
+ // to it by using this map's content. It's built when processing flow
26
+ // events in a trace.
27
+ // An alternative to having a map of four levels is having single map
28
+ // from a string token built from concatenating the binding data to the
29
+ // corresponding flow data. However, this token would be calculated for
30
+ // every event in a trace, resulting in a lot of memory overhead and
31
+ // major GC triggering. So we are trading off readability for
32
+ // performance.
33
+ const boundFlowData = new Map();
23
34
  const flowsById = new Map();
24
35
  const flowEvents = [];
25
36
  const nonFlowEvents = [];
@@ -30,7 +41,7 @@ export function reset() {
30
41
  flowEvents.length = 0;
31
42
  nonFlowEvents.length = 0;
32
43
  flowDataByGroupToken.clear();
33
- flowPhaseBindingTokenToFlowId.clear();
44
+ boundFlowData.clear();
34
45
  flowsById.clear();
35
46
  }
36
47
  export function handleEvent(event) {
@@ -41,22 +52,20 @@ export function handleEvent(event) {
41
52
  nonFlowEvents.push(event);
42
53
  }
43
54
  function processNonFlowEvent(event) {
44
- const flowPhaseBindingToken = flowPhaseBindingTokenForEvent(event);
45
- const flowIds = flowPhaseBindingTokenToFlowId.get(flowPhaseBindingToken);
46
- if (!flowIds) {
55
+ const flowDataForEvent = boundFlowData.get(event.ts)?.get(event.pid)?.get(event.tid)?.get(event.cat);
56
+ if (!flowDataForEvent) {
47
57
  return;
48
58
  }
49
- for (const flowId of flowIds) {
50
- const flow = flowsById.get(flowId);
51
- if (!flow) {
52
- continue;
53
- }
54
- const timeIndex = flow.times.indexOf(event.ts);
55
- if (timeIndex < 0) {
56
- continue;
57
- }
58
- flow.events[timeIndex] = event;
59
+ const { flows, bindingParsed } = flowDataForEvent;
60
+ if (bindingParsed) {
61
+ // We only consider the first event for a given flow binding tuple.
62
+ return;
63
+ }
64
+ for (const flowId of flows) {
65
+ const flow = Platform.MapUtilities.getWithDefault(flowsById, flowId, () => new Map());
66
+ flow.set(event.ts, event);
59
67
  }
68
+ flowDataForEvent.bindingParsed = true;
60
69
  }
61
70
  /**
62
71
  * Creates unique flows by tracking flow phase events. A new created
@@ -68,32 +77,29 @@ function processFlowEvent(flowPhaseEvent) {
68
77
  const flowGroup = flowGroupTokenForFlowPhaseEvent(flowPhaseEvent);
69
78
  switch (flowPhaseEvent.ph) {
70
79
  case ("s" /* Types.Events.Phase.FLOW_START */): {
71
- const flowMetadata = { flowId: flowPhaseEvent.id, times: [flowPhaseEvent.ts] };
72
- flowDataByGroupToken.set(flowGroup, flowMetadata);
73
- addFlowIdToFlowPhaseBinding(flowPhaseBindingTokenForEvent(flowPhaseEvent), flowMetadata.flowId);
80
+ const flowMetadata = { flowId: flowPhaseEvent.id, times: new Map([[flowPhaseEvent.ts, undefined]]) };
81
+ flowDataByGroupToken.set(flowGroup, flowPhaseEvent.id);
82
+ addFlowIdToEventBinding(flowPhaseEvent, flowMetadata.flowId);
74
83
  return;
75
84
  }
76
85
  case ("t" /* Types.Events.Phase.FLOW_STEP */): {
77
- const flow = flowDataByGroupToken.get(flowGroup);
78
- if (!flow || flow.times.length < 0) {
86
+ const flowId = flowDataByGroupToken.get(flowGroup);
87
+ if (flowId === undefined) {
79
88
  // Found non-start flow event with no corresponding start flow,
80
- // start event. Quietly ignore problematic event.
89
+ // start event. Quietly ignore the problematic event.
81
90
  return;
82
91
  }
83
- addFlowIdToFlowPhaseBinding(flowPhaseBindingTokenForEvent(flowPhaseEvent), flow.flowId);
84
- flow.times.push(flowPhaseEvent.ts);
92
+ addFlowIdToEventBinding(flowPhaseEvent, flowId);
85
93
  return;
86
94
  }
87
95
  case ("f" /* Types.Events.Phase.FLOW_END */): {
88
- const flow = flowDataByGroupToken.get(flowGroup);
89
- if (!flow || flow.times.length < 0) {
96
+ const flowId = flowDataByGroupToken.get(flowGroup);
97
+ if (flowId === undefined) {
90
98
  // Found non-start flow event with no corresponding start flow,
91
- // start event. Quietly ignore problematic event.
99
+ // start event. Quietly ignore the problematic event.
92
100
  return;
93
101
  }
94
- addFlowIdToFlowPhaseBinding(flowPhaseBindingTokenForEvent(flowPhaseEvent), flow.flowId);
95
- flow.times.push(flowPhaseEvent.ts);
96
- flowsById.set(flow.flowId, { times: flow.times, events: [] });
102
+ addFlowIdToEventBinding(flowPhaseEvent, flowId);
97
103
  // We don't need this data anymore as the flow has been finished,
98
104
  // so we can drop it.
99
105
  flowDataByGroupToken.delete(flowGroup);
@@ -102,16 +108,15 @@ function processFlowEvent(flowPhaseEvent) {
102
108
  }
103
109
  /**
104
110
  * A single trace event can belong to multiple flows. This method
105
- * tracks which flows (flowId) an event belongs to (given
106
- * its flow phase binding token).
111
+ * tracks which flows (flowId) an event belongs to given its flow
112
+ * binding tuple (made of its ts, pid, tid and cat).
107
113
  */
108
- function addFlowIdToFlowPhaseBinding(flowPhaseBinding, flowId) {
109
- let flowIds = flowPhaseBindingTokenToFlowId.get(flowPhaseBinding);
110
- if (!flowIds) {
111
- flowIds = new Set();
112
- }
113
- flowIds.add(flowId);
114
- flowPhaseBindingTokenToFlowId.set(flowPhaseBinding, flowIds);
114
+ function addFlowIdToEventBinding(event, flowId) {
115
+ const flowsByPid = Platform.MapUtilities.getWithDefault(boundFlowData, event.ts, () => new Map());
116
+ const flowsByTid = Platform.MapUtilities.getWithDefault(flowsByPid, event.pid, () => new Map());
117
+ const flowsByCat = Platform.MapUtilities.getWithDefault(flowsByTid, event.tid, () => new Map());
118
+ const flowData = Platform.MapUtilities.getWithDefault(flowsByCat, event.cat, () => ({ flows: new Set(), bindingParsed: false }));
119
+ flowData.flows.add(flowId);
115
120
  }
116
121
  /**
117
122
  * Returns a token to group flow phase events (start, step and end)
@@ -127,27 +132,14 @@ function addFlowIdToFlowPhaseBinding(flowPhaseBinding, flowId) {
127
132
  function flowGroupTokenForFlowPhaseEvent(event) {
128
133
  return `${event.cat}${ID_COMPONENT_SEPARATOR}${event.name}${ID_COMPONENT_SEPARATOR}${event.id}`;
129
134
  }
130
- /**
131
- * A flow phase binding is a token that allows us to associate a flow
132
- * phase event to its corresponding event. This association indicates
133
- * what role a trace event plays in a flow.
134
- * We can assign a trace event with a flow phase when its category,
135
- * thread id, process id and timestamp matches those of a flow phase
136
- * event.
137
- */
138
- function flowPhaseBindingTokenForEvent(event) {
139
- // This function is called many times (one per event) and creating a
140
- // string every time can trigger GC. If this becomes a problem, a
141
- // possible optimization is to use a multi-key map with the
142
- // binding token components, a trade off between memory performance
143
- // and readability.
144
- return `${event.cat}${ID_COMPONENT_SEPARATOR}${event.tid}${ID_COMPONENT_SEPARATOR}${event.pid}${ID_COMPONENT_SEPARATOR}${event.ts}`;
145
- }
146
135
  export async function finalize() {
147
136
  // Order is important: flow events need to be handled first.
148
137
  flowEvents.forEach(processFlowEvent);
149
138
  nonFlowEvents.forEach(processNonFlowEvent);
150
- flows = [...flowsById.values()].map(f => f.events).filter(flow => flow.length > 1);
139
+ flows = [...flowsById.values()]
140
+ .map(flowMapping => [...flowMapping.values()])
141
+ .map(flow => flow.filter(event => event !== undefined))
142
+ .filter(flow => flow.length > 1);
151
143
  }
152
144
  export function data() {
153
145
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"FlowsHandler.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/handlers/FlowsHandler.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 The Chromium Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\nimport * as Types from '../types/types.js';\n\n// A flow is a logic connection between trace events. We display this\n// connection as arrows between trace events belonging to the same flow.\n\n// In the trace event format, flows are represented with pairing \"flow\n// phase\" events. Each flow phase event corresponds to one trace event\n// and indicates the role a trace event plays in a flow (start, step or\n// end). For each flow, one `start` and one `end` phase events are\n// included, while the amount of `step` phase events can be >= 0.\n\n// A flow phase event is assigned to a trace event when their cat, tid,\n// pid and ts are equal (see @flowPhaseBindingTokenForEvent ).\n\n// It's possible for a single event to belong to multiple flows. In that\n// case, it will have multiple corresponding flow phase events (one\n// per flow).\n\n// To parse flows, we first handle flow phase events, by creating unique\n// flows with the timestamps of each phase. Then, we place trace events\n// in the flows where their corresponding phase events were placed (if\n// there are any corresponding flow phase events at all).\nconst flowDataByGroupToken = new Map<string, {flowId: number, times: Types.Timing.MicroSeconds[]}>();\nconst flowPhaseBindingTokenToFlowId = new Map<string, Set<number>>();\nconst flowsById = new Map<number, {times: Types.Timing.MicroSeconds[], events: Types.Events.Event[]}>();\nconst flowEvents: Types.Events.FlowEvent[] = [];\nconst nonFlowEvents: Types.Events.Event[] = [];\nlet flows: Types.Events.Event[][] = [];\nconst ID_COMPONENT_SEPARATOR = '-$-';\nexport function reset(): void {\n flows = [];\n flowEvents.length = 0;\n nonFlowEvents.length = 0;\n flowDataByGroupToken.clear();\n flowPhaseBindingTokenToFlowId.clear();\n flowsById.clear();\n}\n\nexport function handleEvent(event: Types.Events.Event): void {\n if (Types.Events.isFlowPhaseEvent(event)) {\n flowEvents.push(event);\n return;\n }\n nonFlowEvents.push(event);\n}\n\nfunction processNonFlowEvent(event: Types.Events.Event): void {\n const flowPhaseBindingToken = flowPhaseBindingTokenForEvent(event);\n const flowIds = flowPhaseBindingTokenToFlowId.get(flowPhaseBindingToken);\n if (!flowIds) {\n return;\n }\n for (const flowId of flowIds) {\n const flow = flowsById.get(flowId);\n if (!flow) {\n continue;\n }\n const timeIndex = flow.times.indexOf(event.ts);\n if (timeIndex < 0) {\n continue;\n }\n flow.events[timeIndex] = event;\n }\n}\n\n/**\n * Creates unique flows by tracking flow phase events. A new created\n * flow whenever a flow start phase event is detected.\n * Subsequent flow phase events with the same group token are added to\n * this flow until a flow end phase is detected.\n */\nfunction processFlowEvent(flowPhaseEvent: Types.Events.FlowEvent): void {\n const flowGroup = flowGroupTokenForFlowPhaseEvent(flowPhaseEvent);\n switch (flowPhaseEvent.ph) {\n case (Types.Events.Phase.FLOW_START): {\n const flowMetadata = {flowId: flowPhaseEvent.id, times: [flowPhaseEvent.ts]};\n flowDataByGroupToken.set(flowGroup, flowMetadata);\n addFlowIdToFlowPhaseBinding(flowPhaseBindingTokenForEvent(flowPhaseEvent), flowMetadata.flowId);\n return;\n }\n case (Types.Events.Phase.FLOW_STEP): {\n const flow = flowDataByGroupToken.get(flowGroup);\n if (!flow || flow.times.length < 0) {\n // Found non-start flow event with no corresponding start flow,\n // start event. Quietly ignore problematic event.\n return;\n }\n addFlowIdToFlowPhaseBinding(flowPhaseBindingTokenForEvent(flowPhaseEvent), flow.flowId);\n flow.times.push(flowPhaseEvent.ts);\n return;\n }\n case (Types.Events.Phase.FLOW_END): {\n const flow = flowDataByGroupToken.get(flowGroup);\n if (!flow || flow.times.length < 0) {\n // Found non-start flow event with no corresponding start flow,\n // start event. Quietly ignore problematic event.\n return;\n }\n addFlowIdToFlowPhaseBinding(flowPhaseBindingTokenForEvent(flowPhaseEvent), flow.flowId);\n flow.times.push(flowPhaseEvent.ts);\n flowsById.set(flow.flowId, {times: flow.times, events: []});\n // We don't need this data anymore as the flow has been finished,\n // so we can drop it.\n flowDataByGroupToken.delete(flowGroup);\n }\n }\n}\n\n/**\n * A single trace event can belong to multiple flows. This method\n * tracks which flows (flowId) an event belongs to (given\n * its flow phase binding token).\n */\nfunction addFlowIdToFlowPhaseBinding(flowPhaseBinding: string, flowId: number): void {\n let flowIds = flowPhaseBindingTokenToFlowId.get(flowPhaseBinding);\n if (!flowIds) {\n flowIds = new Set();\n }\n flowIds.add(flowId);\n flowPhaseBindingTokenToFlowId.set(flowPhaseBinding, flowIds);\n}\n\n/**\n * Returns a token to group flow phase events (start, step and end)\n * belonging to the same flow. Flow phase events belonging to the same\n * flow share category, thread id, process id and name.\n *\n * Note that other phase events of other flows can share these\n * attributes too. For this reason, we group flow phase events in\n * cycles. A cycle starts on a flow start phase event and finishes on a\n * flow end phase event. For this reason, flow phase events need to be\n * handled in timestamp order.\n */\nfunction flowGroupTokenForFlowPhaseEvent(event: Types.Events.FlowEvent): string {\n return `${event.cat}${ID_COMPONENT_SEPARATOR}${event.name}${ID_COMPONENT_SEPARATOR}${event.id}`;\n}\n\n/**\n * A flow phase binding is a token that allows us to associate a flow\n * phase event to its corresponding event. This association indicates\n * what role a trace event plays in a flow.\n * We can assign a trace event with a flow phase when its category,\n * thread id, process id and timestamp matches those of a flow phase\n * event.\n */\nfunction flowPhaseBindingTokenForEvent(event: Types.Events.Event): string {\n // This function is called many times (one per event) and creating a\n // string every time can trigger GC. If this becomes a problem, a\n // possible optimization is to use a multi-key map with the\n // binding token components, a trade off between memory performance\n // and readability.\n return `${event.cat}${ID_COMPONENT_SEPARATOR}${event.tid}${ID_COMPONENT_SEPARATOR}${event.pid}${\n ID_COMPONENT_SEPARATOR}${event.ts}`;\n}\n\nexport async function finalize(): Promise<void> {\n // Order is important: flow events need to be handled first.\n flowEvents.forEach(processFlowEvent);\n nonFlowEvents.forEach(processNonFlowEvent);\n flows = [...flowsById.values()].map(f => f.events).filter(flow => flow.length > 1);\n}\n\nexport function data(): {flows: Types.Events.Event[][]} {\n return {\n flows,\n };\n}\n"]}
1
+ {"version":3,"file":"FlowsHandler.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/handlers/FlowsHandler.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 The Chromium Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\nimport * as Platform from '../../../core/platform/platform.js';\nimport * as Types from '../types/types.js';\n\n// A flow is a logic connection between trace events. We display this\n// connection as arrows between trace events belonging to the same flow.\n\n// In the trace event format, flows are represented with pairing \"flow\n// phase\" events. Each flow phase event corresponds to one trace event\n// and indicates the role a trace event plays in a flow (start, step or\n// end). For each flow, one `start` and one `end` phase events are\n// included, while the amount of `step` phase events can be >= 0.\n\n// A flow phase event is assigned to a trace event when their cat, tid,\n// pid and ts are equal (see @flowPhaseBindingTokenForEvent ).\n\n// It's possible for a single event to belong to multiple flows. In that\n// case, it will have multiple corresponding flow phase events (one\n// per flow).\n\n// To parse flows, we first handle flow phase events, by creating unique\n// flows with the timestamps of each phase. Then, we place trace events\n// in the flows where their corresponding phase events were placed (if\n// there are any corresponding flow phase events at all).\nconst flowDataByGroupToken = new Map<string, number>();\n\ninterface EventFlowData {\n flows: Set<number>;\n bindingParsed: boolean;\n}\ntype FlowBindingTuple =\n Map<Types.Timing.MicroSeconds, Map<Types.Events.ProcessID, Map<Types.Events.ThreadID, Map<string, EventFlowData>>>>;\n\n// Given a trace event's flow binding tuple (timestamp, process id,\n// thread id and category) we determine if there is any flow data bound\n// to it by using this map's content. It's built when processing flow\n// events in a trace.\n// An alternative to having a map of four levels is having single map\n// from a string token built from concatenating the binding data to the\n// corresponding flow data. However, this token would be calculated for\n// every event in a trace, resulting in a lot of memory overhead and\n// major GC triggering. So we are trading off readability for\n// performance.\nconst boundFlowData: FlowBindingTuple = new Map();\n\nconst flowsById = new Map<number, Map<Types.Timing.MicroSeconds, Types.Events.Event>>();\nconst flowEvents: Types.Events.FlowEvent[] = [];\nconst nonFlowEvents: Types.Events.Event[] = [];\nlet flows: Types.Events.Event[][] = [];\nconst ID_COMPONENT_SEPARATOR = '-$-';\nexport function reset(): void {\n flows = [];\n flowEvents.length = 0;\n nonFlowEvents.length = 0;\n flowDataByGroupToken.clear();\n boundFlowData.clear();\n flowsById.clear();\n}\n\nexport function handleEvent(event: Types.Events.Event): void {\n if (Types.Events.isFlowPhaseEvent(event)) {\n flowEvents.push(event);\n return;\n }\n nonFlowEvents.push(event);\n}\n\nfunction processNonFlowEvent(event: Types.Events.Event): void {\n const flowDataForEvent = boundFlowData.get(event.ts)?.get(event.pid)?.get(event.tid)?.get(event.cat);\n if (!flowDataForEvent) {\n return;\n }\n const {flows, bindingParsed} = flowDataForEvent;\n if (bindingParsed) {\n // We only consider the first event for a given flow binding tuple.\n return;\n }\n for (const flowId of flows) {\n const flow = Platform.MapUtilities.getWithDefault(\n flowsById, flowId, () => new Map<Types.Timing.MicroSeconds, Types.Events.Event>());\n flow.set(event.ts, event);\n }\n flowDataForEvent.bindingParsed = true;\n}\n\n/**\n * Creates unique flows by tracking flow phase events. A new created\n * flow whenever a flow start phase event is detected.\n * Subsequent flow phase events with the same group token are added to\n * this flow until a flow end phase is detected.\n */\nfunction processFlowEvent(flowPhaseEvent: Types.Events.FlowEvent): void {\n const flowGroup = flowGroupTokenForFlowPhaseEvent(flowPhaseEvent);\n switch (flowPhaseEvent.ph) {\n case (Types.Events.Phase.FLOW_START): {\n const flowMetadata = {flowId: flowPhaseEvent.id, times: new Map([[flowPhaseEvent.ts, undefined]])};\n flowDataByGroupToken.set(flowGroup, flowPhaseEvent.id);\n addFlowIdToEventBinding(flowPhaseEvent, flowMetadata.flowId);\n return;\n }\n case (Types.Events.Phase.FLOW_STEP): {\n const flowId = flowDataByGroupToken.get(flowGroup);\n if (flowId === undefined) {\n // Found non-start flow event with no corresponding start flow,\n // start event. Quietly ignore the problematic event.\n return;\n }\n addFlowIdToEventBinding(flowPhaseEvent, flowId);\n return;\n }\n case (Types.Events.Phase.FLOW_END): {\n const flowId = flowDataByGroupToken.get(flowGroup);\n if (flowId === undefined) {\n // Found non-start flow event with no corresponding start flow,\n // start event. Quietly ignore the problematic event.\n return;\n }\n addFlowIdToEventBinding(flowPhaseEvent, flowId);\n // We don't need this data anymore as the flow has been finished,\n // so we can drop it.\n flowDataByGroupToken.delete(flowGroup);\n }\n }\n}\n\ntype MapValueType<T extends Map<unknown, unknown>> = NonNullable<ReturnType<T['get']>>;\n/**\n * A single trace event can belong to multiple flows. This method\n * tracks which flows (flowId) an event belongs to given its flow\n * binding tuple (made of its ts, pid, tid and cat).\n */\nfunction addFlowIdToEventBinding(event: Types.Events.Event, flowId: number): void {\n const flowsByPid =\n Platform.MapUtilities.getWithDefault<Types.Timing.MicroSeconds, MapValueType<typeof boundFlowData>>(\n boundFlowData, event.ts, () => new Map());\n const flowsByTid = Platform.MapUtilities.getWithDefault<Types.Events.ProcessID, MapValueType<typeof flowsByPid>>(\n flowsByPid, event.pid, () => new Map());\n const flowsByCat = Platform.MapUtilities.getWithDefault<Types.Events.ThreadID, MapValueType<typeof flowsByTid>>(\n flowsByTid, event.tid, () => new Map());\n const flowData =\n Platform.MapUtilities.getWithDefault(flowsByCat, event.cat, () => ({flows: new Set(), bindingParsed: false}));\n flowData.flows.add(flowId);\n}\n\n/**\n * Returns a token to group flow phase events (start, step and end)\n * belonging to the same flow. Flow phase events belonging to the same\n * flow share category, thread id, process id and name.\n *\n * Note that other phase events of other flows can share these\n * attributes too. For this reason, we group flow phase events in\n * cycles. A cycle starts on a flow start phase event and finishes on a\n * flow end phase event. For this reason, flow phase events need to be\n * handled in timestamp order.\n */\nfunction flowGroupTokenForFlowPhaseEvent(event: Types.Events.FlowEvent): string {\n return `${event.cat}${ID_COMPONENT_SEPARATOR}${event.name}${ID_COMPONENT_SEPARATOR}${event.id}`;\n}\n\nexport async function finalize(): Promise<void> {\n // Order is important: flow events need to be handled first.\n flowEvents.forEach(processFlowEvent);\n nonFlowEvents.forEach(processNonFlowEvent);\n flows = [...flowsById.values()]\n .map(flowMapping => [...flowMapping.values()])\n .map(flow => flow.filter(event => event !== undefined))\n .filter(flow => flow.length > 1);\n}\n\nexport function data(): {flows: Types.Events.Event[][]} {\n return {\n flows,\n };\n}\n"]}
@@ -1,5 +1,15 @@
1
1
  import * as Types from '../types/types.js';
2
2
  export declare function reset(): void;
3
+ /**
4
+ * IMPORTANT: Before adding support for new initiator relationships in
5
+ * trace events consider using Perfetto's flow API on the events in
6
+ * question, so that they get automatically computed.
7
+ * @see {@link flowsHandlerData}
8
+ *
9
+ * The events manually computed here were added before we had support
10
+ * for flow events. As such they should be migrated to use the flow
11
+ * API so that no manual parsing is needed.
12
+ */
3
13
  export declare function handleEvent(event: Types.Events.Event): void;
4
14
  export declare function finalize(): Promise<void>;
5
15
  export interface InitiatorsData {
@@ -7,3 +17,4 @@ export interface InitiatorsData {
7
17
  initiatorToEvents: Map<Types.Events.Event, Types.Events.Event[]>;
8
18
  }
9
19
  export declare function data(): InitiatorsData;
20
+ export declare function deps(): ['Flows', 'AsyncJSCalls'];
@@ -3,6 +3,8 @@
3
3
  // found in the LICENSE file.
4
4
  import * as Helpers from '../helpers/helpers.js';
5
5
  import * as Types from '../types/types.js';
6
+ import { data as AsyncJSCallsHandlerData } from './AsyncJSCallsHandler.js';
7
+ import { data as flowsHandlerData } from './FlowsHandler.js';
6
8
  const lastScheduleStyleRecalcByFrame = new Map();
7
9
  // This tracks the last event that is considered to have invalidated the layout
8
10
  // for a given frame.
@@ -15,15 +17,16 @@ const lastInvalidationEventForFrame = new Map();
15
17
  // These are the same - just UpdateLayoutTree is what the event from Chromium
16
18
  // is called.
17
19
  const lastUpdateLayoutTreeByFrame = new Map();
18
- // This tracks postmessage dispatch and handler events for creating initiator association
19
- const postMessageHandlerEvents = [];
20
- const schedulePostMessageEventByTraceId = new Map();
21
20
  // These two maps store the same data but in different directions.
22
21
  // For a given event, tell me what its initiator was. An event can only have one initiator.
23
22
  const eventToInitiatorMap = new Map();
24
23
  // For a given event, tell me what events it initiated. An event can initiate
25
24
  // multiple events, hence why the value for this map is an array.
26
25
  const initiatorToEventsMap = new Map();
26
+ // Note: we are keeping the parsing of the following async JS schedulers
27
+ // for backwards compatibility only. They are targeted to be removed
28
+ // completely by M134. See more details at crbug.com/383974422
29
+ // TODO(andoli): remove manual parsing of async JS schedulers.
27
30
  const requestAnimationFrameEventsById = new Map();
28
31
  const timerInstallEventsById = new Map();
29
32
  const requestIdleCallbackEventsById = new Map();
@@ -40,8 +43,6 @@ export function reset() {
40
43
  requestIdleCallbackEventsById.clear();
41
44
  webSocketCreateEventsById.clear();
42
45
  schedulePostTaskCallbackEventsById.clear();
43
- schedulePostMessageEventByTraceId.clear();
44
- postMessageHandlerEvents.length = 0;
45
46
  }
46
47
  function storeInitiator(data) {
47
48
  eventToInitiatorMap.set(data.event, data.initiator);
@@ -49,6 +50,16 @@ function storeInitiator(data) {
49
50
  eventsForInitiator.push(data.event);
50
51
  initiatorToEventsMap.set(data.initiator, eventsForInitiator);
51
52
  }
53
+ /**
54
+ * IMPORTANT: Before adding support for new initiator relationships in
55
+ * trace events consider using Perfetto's flow API on the events in
56
+ * question, so that they get automatically computed.
57
+ * @see {@link flowsHandlerData}
58
+ *
59
+ * The events manually computed here were added before we had support
60
+ * for flow events. As such they should be migrated to use the flow
61
+ * API so that no manual parsing is needed.
62
+ */
52
63
  export function handleEvent(event) {
53
64
  if (Types.Events.isScheduleStyleRecalculation(event)) {
54
65
  lastScheduleStyleRecalcByFrame.set(event.args.data.frame, event);
@@ -164,33 +175,27 @@ export function handleEvent(event) {
164
175
  storeInitiator({ event, initiator: matchingSchedule });
165
176
  }
166
177
  }
167
- // Store schedulePostMessage Events by their traceIds.
168
- // so they can be reconciled later with matching handlePostMessage events with same traceIds.
169
- else if (Types.Events.isHandlePostMessage(event)) {
170
- postMessageHandlerEvents.push(event);
171
- }
172
- else if (Types.Events.isSchedulePostMessage(event)) {
173
- const traceId = event.args.data?.traceId;
174
- if (traceId) {
175
- schedulePostMessageEventByTraceId.set(traceId, event);
178
+ }
179
+ function createRelationshipsFromFlows() {
180
+ const flows = flowsHandlerData().flows;
181
+ for (let i = 0; i < flows.length; i++) {
182
+ const flow = flows[i];
183
+ for (let j = 0; j < flow.length - 1; j++) {
184
+ storeInitiator({ event: flow[j + 1], initiator: flow[j] });
176
185
  }
177
186
  }
178
187
  }
179
- function finalizeInitiatorRelationship() {
180
- for (const handlerEvent of postMessageHandlerEvents) {
181
- const traceId = handlerEvent.args.data?.traceId;
182
- const matchingSchedulePostMesssageEvent = schedulePostMessageEventByTraceId.get(traceId);
183
- if (matchingSchedulePostMesssageEvent) {
184
- // Set schedulePostMesssage events as initiators for handler events.
185
- storeInitiator({ event: handlerEvent, initiator: matchingSchedulePostMesssageEvent });
188
+ function createRelationshipsFromAsyncJSCalls() {
189
+ const asyncCallEntries = AsyncJSCallsHandlerData().schedulerToRunEntryPoints.entries();
190
+ for (const [asyncCaller, asyncCallees] of asyncCallEntries) {
191
+ for (const asyncCallee of asyncCallees) {
192
+ storeInitiator({ event: asyncCallee, initiator: asyncCaller });
186
193
  }
187
194
  }
188
195
  }
189
196
  export async function finalize() {
190
- // During event processing, we may encounter initiators before the handler events themselves
191
- // (e.g dispatch events on worker and handler events on the main thread)
192
- // we don't want to miss out on events whose initiators haven't been processed yet
193
- finalizeInitiatorRelationship();
197
+ createRelationshipsFromFlows();
198
+ createRelationshipsFromAsyncJSCalls();
194
199
  }
195
200
  export function data() {
196
201
  return {
@@ -198,4 +203,7 @@ export function data() {
198
203
  initiatorToEvents: initiatorToEventsMap,
199
204
  };
200
205
  }
206
+ export function deps() {
207
+ return ['Flows', 'AsyncJSCalls'];
208
+ }
201
209
  //# sourceMappingURL=InitiatorsHandler.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"InitiatorsHandler.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/handlers/InitiatorsHandler.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2023 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\nconst lastScheduleStyleRecalcByFrame = new Map<string, Types.Events.ScheduleStyleRecalculation>();\n\n// This tracks the last event that is considered to have invalidated the layout\n// for a given frame.\n// Note that although there is an InvalidateLayout event, there are also other\n// events (ScheduleStyleRecalculation) that could be the reason a layout was\n// invalidated.\nconst lastInvalidationEventForFrame = new Map<string, Types.Events.Event>();\n\n// Important: although the event is called UpdateLayoutTree, in the UI we\n// present these to the user as \"Recalculate Style\". So don't get confused!\n// These are the same - just UpdateLayoutTree is what the event from Chromium\n// is called.\nconst lastUpdateLayoutTreeByFrame = new Map<string, Types.Events.UpdateLayoutTree>();\n\n// This tracks postmessage dispatch and handler events for creating initiator association\nconst postMessageHandlerEvents: Types.Events.HandlePostMessage[] = [];\nconst schedulePostMessageEventByTraceId: Map<string, Types.Events.SchedulePostMessage> = new Map();\n\n// These two maps store the same data but in different directions.\n// For a given event, tell me what its initiator was. An event can only have one initiator.\nconst eventToInitiatorMap = new Map<Types.Events.Event, Types.Events.Event>();\n// For a given event, tell me what events it initiated. An event can initiate\n// multiple events, hence why the value for this map is an array.\nconst initiatorToEventsMap = new Map<Types.Events.Event, Types.Events.Event[]>();\n\nconst requestAnimationFrameEventsById: Map<number, Types.Events.RequestAnimationFrame> = new Map();\nconst timerInstallEventsById: Map<number, Types.Events.TimerInstall> = new Map();\nconst requestIdleCallbackEventsById: Map<number, Types.Events.RequestIdleCallback> = new Map();\nconst webSocketCreateEventsById: Map<number, Types.Events.WebSocketCreate> = new Map();\nconst schedulePostTaskCallbackEventsById: Map<number, Types.Events.SchedulePostTaskCallback> = new Map();\n\nexport function reset(): void {\n lastScheduleStyleRecalcByFrame.clear();\n lastInvalidationEventForFrame.clear();\n lastUpdateLayoutTreeByFrame.clear();\n timerInstallEventsById.clear();\n eventToInitiatorMap.clear();\n initiatorToEventsMap.clear();\n requestAnimationFrameEventsById.clear();\n requestIdleCallbackEventsById.clear();\n webSocketCreateEventsById.clear();\n schedulePostTaskCallbackEventsById.clear();\n schedulePostMessageEventByTraceId.clear();\n postMessageHandlerEvents.length = 0;\n}\n\nfunction storeInitiator(data: {initiator: Types.Events.Event, event: Types.Events.Event}): void {\n eventToInitiatorMap.set(data.event, data.initiator);\n const eventsForInitiator = initiatorToEventsMap.get(data.initiator) || [];\n eventsForInitiator.push(data.event);\n initiatorToEventsMap.set(data.initiator, eventsForInitiator);\n}\n\nexport function handleEvent(event: Types.Events.Event): void {\n if (Types.Events.isScheduleStyleRecalculation(event)) {\n lastScheduleStyleRecalcByFrame.set(event.args.data.frame, event);\n } else if (Types.Events.isUpdateLayoutTree(event)) {\n // IMPORTANT: although the trace event is called UpdateLayoutTree, this\n // represents a Styles Recalculation. This event in the timeline is shown to\n // the user as \"Recalculate Styles.\"\n if (event.args.beginData) {\n // Store the last UpdateLayout event: we use this when we see an\n // InvalidateLayout and try to figure out its initiator.\n lastUpdateLayoutTreeByFrame.set(event.args.beginData.frame, event);\n\n // If this frame has seen a ScheduleStyleRecalc event, then that event is\n // considered to be the initiator of this StylesRecalc.\n const scheduledStyleForFrame = lastScheduleStyleRecalcByFrame.get(event.args.beginData.frame);\n if (scheduledStyleForFrame) {\n storeInitiator({\n event,\n initiator: scheduledStyleForFrame,\n });\n }\n }\n } else if (Types.Events.isInvalidateLayout(event)) {\n // By default, the InvalidateLayout event is what triggered the layout invalidation for this frame.\n let invalidationInitiator: Types.Events.Event = event;\n\n // However, if we have not had any prior invalidations for this frame, we\n // want to consider StyleRecalculation events as they might be the actual\n // cause of this layout invalidation.\n if (!lastInvalidationEventForFrame.has(event.args.data.frame)) {\n // 1. If we have not had an invalidation event for this frame\n // 2. AND we have had an UpdateLayoutTree for this frame\n // 3. AND the UpdateLayoutTree event ended AFTER the InvalidateLayout startTime\n // 4. AND we have an initiator for the UpdateLayoutTree event\n // 5. Then we set the last invalidation event for this frame to be the UpdateLayoutTree's initiator.\n const lastUpdateLayoutTreeForFrame = lastUpdateLayoutTreeByFrame.get(event.args.data.frame);\n if (lastUpdateLayoutTreeForFrame) {\n const {endTime} = Helpers.Timing.eventTimingsMicroSeconds(lastUpdateLayoutTreeForFrame);\n const initiatorOfUpdateLayout = eventToInitiatorMap.get(lastUpdateLayoutTreeForFrame);\n\n if (initiatorOfUpdateLayout && endTime && endTime > event.ts) {\n invalidationInitiator = initiatorOfUpdateLayout;\n }\n }\n }\n lastInvalidationEventForFrame.set(event.args.data.frame, invalidationInitiator);\n } else if (Types.Events.isLayout(event)) {\n // The initiator of a Layout event is the last Invalidation event.\n const lastInvalidation = lastInvalidationEventForFrame.get(event.args.beginData.frame);\n if (lastInvalidation) {\n storeInitiator({\n event,\n initiator: lastInvalidation,\n });\n }\n // Now clear the last invalidation for the frame: the last invalidation has been linked to a Layout event, so it cannot be the initiator for any future layouts.\n lastInvalidationEventForFrame.delete(event.args.beginData.frame);\n } else if (Types.Events.isRequestAnimationFrame(event)) {\n requestAnimationFrameEventsById.set(event.args.data.id, event);\n } else if (Types.Events.isFireAnimationFrame(event)) {\n // If we get a fire event, that means we should have had the\n // RequestAnimationFrame event by now. If so, we can set that as the\n // initiator for the fire event.\n const matchingRequestEvent = requestAnimationFrameEventsById.get(event.args.data.id);\n if (matchingRequestEvent) {\n storeInitiator({\n event,\n initiator: matchingRequestEvent,\n });\n }\n } else if (Types.Events.isTimerInstall(event)) {\n timerInstallEventsById.set(event.args.data.timerId, event);\n } else if (Types.Events.isTimerFire(event)) {\n const matchingInstall = timerInstallEventsById.get(event.args.data.timerId);\n if (matchingInstall) {\n storeInitiator({event, initiator: matchingInstall});\n }\n } else if (Types.Events.isRequestIdleCallback(event)) {\n requestIdleCallbackEventsById.set(event.args.data.id, event);\n } else if (Types.Events.isFireIdleCallback(event)) {\n const matchingRequestEvent = requestIdleCallbackEventsById.get(event.args.data.id);\n if (matchingRequestEvent) {\n storeInitiator({\n event,\n initiator: matchingRequestEvent,\n });\n }\n } else if (Types.Events.isWebSocketCreate(event)) {\n webSocketCreateEventsById.set(event.args.data.identifier, event);\n } else if (Types.Events.isWebSocketInfo(event) || Types.Events.isWebSocketTransfer(event)) {\n const matchingCreateEvent = webSocketCreateEventsById.get(event.args.data.identifier);\n if (matchingCreateEvent) {\n storeInitiator({\n event,\n initiator: matchingCreateEvent,\n });\n }\n } else if (Types.Events.isSchedulePostTaskCallback(event)) {\n schedulePostTaskCallbackEventsById.set(event.args.data.taskId, event);\n } else if (Types.Events.isRunPostTaskCallback(event) || Types.Events.isAbortPostTaskCallback(event)) {\n const matchingSchedule = schedulePostTaskCallbackEventsById.get(event.args.data.taskId);\n if (matchingSchedule) {\n storeInitiator({event, initiator: matchingSchedule});\n }\n }\n // Store schedulePostMessage Events by their traceIds.\n // so they can be reconciled later with matching handlePostMessage events with same traceIds.\n else if (Types.Events.isHandlePostMessage(event)) {\n postMessageHandlerEvents.push(event);\n } else if (Types.Events.isSchedulePostMessage(event)) {\n const traceId = event.args.data?.traceId;\n if (traceId) {\n schedulePostMessageEventByTraceId.set(traceId, event);\n }\n }\n}\n\nfunction finalizeInitiatorRelationship(): void {\n for (const handlerEvent of postMessageHandlerEvents) {\n const traceId = handlerEvent.args.data?.traceId;\n const matchingSchedulePostMesssageEvent = schedulePostMessageEventByTraceId.get(traceId);\n if (matchingSchedulePostMesssageEvent) {\n // Set schedulePostMesssage events as initiators for handler events.\n storeInitiator({event: handlerEvent, initiator: matchingSchedulePostMesssageEvent});\n }\n }\n}\n\nexport async function finalize(): Promise<void> {\n // During event processing, we may encounter initiators before the handler events themselves\n // (e.g dispatch events on worker and handler events on the main thread)\n // we don't want to miss out on events whose initiators haven't been processed yet\n finalizeInitiatorRelationship();\n}\n\nexport interface InitiatorsData {\n eventToInitiator: Map<Types.Events.Event, Types.Events.Event>;\n initiatorToEvents: Map<Types.Events.Event, Types.Events.Event[]>;\n}\n\nexport function data(): InitiatorsData {\n return {\n eventToInitiator: eventToInitiatorMap,\n initiatorToEvents: initiatorToEventsMap,\n };\n}\n"]}
1
+ {"version":3,"file":"InitiatorsHandler.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/handlers/InitiatorsHandler.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2023 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 AsyncJSCallsHandlerData} from './AsyncJSCallsHandler.js';\nimport {data as flowsHandlerData} from './FlowsHandler.js';\n\nconst lastScheduleStyleRecalcByFrame = new Map<string, Types.Events.ScheduleStyleRecalculation>();\n\n// This tracks the last event that is considered to have invalidated the layout\n// for a given frame.\n// Note that although there is an InvalidateLayout event, there are also other\n// events (ScheduleStyleRecalculation) that could be the reason a layout was\n// invalidated.\nconst lastInvalidationEventForFrame = new Map<string, Types.Events.Event>();\n\n// Important: although the event is called UpdateLayoutTree, in the UI we\n// present these to the user as \"Recalculate Style\". So don't get confused!\n// These are the same - just UpdateLayoutTree is what the event from Chromium\n// is called.\nconst lastUpdateLayoutTreeByFrame = new Map<string, Types.Events.UpdateLayoutTree>();\n\n// These two maps store the same data but in different directions.\n// For a given event, tell me what its initiator was. An event can only have one initiator.\nconst eventToInitiatorMap = new Map<Types.Events.Event, Types.Events.Event>();\n// For a given event, tell me what events it initiated. An event can initiate\n// multiple events, hence why the value for this map is an array.\nconst initiatorToEventsMap = new Map<Types.Events.Event, Types.Events.Event[]>();\n\n// Note: we are keeping the parsing of the following async JS schedulers\n// for backwards compatibility only. They are targeted to be removed\n// completely by M134. See more details at crbug.com/383974422\n// TODO(andoli): remove manual parsing of async JS schedulers.\nconst requestAnimationFrameEventsById: Map<number, Types.Events.RequestAnimationFrame> = new Map();\nconst timerInstallEventsById: Map<number, Types.Events.TimerInstall> = new Map();\nconst requestIdleCallbackEventsById: Map<number, Types.Events.RequestIdleCallback> = new Map();\n\nconst webSocketCreateEventsById: Map<number, Types.Events.WebSocketCreate> = new Map();\nconst schedulePostTaskCallbackEventsById: Map<number, Types.Events.SchedulePostTaskCallback> = new Map();\n\nexport function reset(): void {\n lastScheduleStyleRecalcByFrame.clear();\n lastInvalidationEventForFrame.clear();\n lastUpdateLayoutTreeByFrame.clear();\n timerInstallEventsById.clear();\n eventToInitiatorMap.clear();\n initiatorToEventsMap.clear();\n requestAnimationFrameEventsById.clear();\n requestIdleCallbackEventsById.clear();\n webSocketCreateEventsById.clear();\n schedulePostTaskCallbackEventsById.clear();\n}\n\nfunction storeInitiator(data: {initiator: Types.Events.Event, event: Types.Events.Event}): void {\n eventToInitiatorMap.set(data.event, data.initiator);\n const eventsForInitiator = initiatorToEventsMap.get(data.initiator) || [];\n eventsForInitiator.push(data.event);\n initiatorToEventsMap.set(data.initiator, eventsForInitiator);\n}\n\n/**\n * IMPORTANT: Before adding support for new initiator relationships in\n * trace events consider using Perfetto's flow API on the events in\n * question, so that they get automatically computed.\n * @see {@link flowsHandlerData}\n *\n * The events manually computed here were added before we had support\n * for flow events. As such they should be migrated to use the flow\n * API so that no manual parsing is needed.\n */\nexport function handleEvent(event: Types.Events.Event): void {\n if (Types.Events.isScheduleStyleRecalculation(event)) {\n lastScheduleStyleRecalcByFrame.set(event.args.data.frame, event);\n } else if (Types.Events.isUpdateLayoutTree(event)) {\n // IMPORTANT: although the trace event is called UpdateLayoutTree, this\n // represents a Styles Recalculation. This event in the timeline is shown to\n // the user as \"Recalculate Styles.\"\n if (event.args.beginData) {\n // Store the last UpdateLayout event: we use this when we see an\n // InvalidateLayout and try to figure out its initiator.\n lastUpdateLayoutTreeByFrame.set(event.args.beginData.frame, event);\n\n // If this frame has seen a ScheduleStyleRecalc event, then that event is\n // considered to be the initiator of this StylesRecalc.\n const scheduledStyleForFrame = lastScheduleStyleRecalcByFrame.get(event.args.beginData.frame);\n if (scheduledStyleForFrame) {\n storeInitiator({\n event,\n initiator: scheduledStyleForFrame,\n });\n }\n }\n } else if (Types.Events.isInvalidateLayout(event)) {\n // By default, the InvalidateLayout event is what triggered the layout invalidation for this frame.\n let invalidationInitiator: Types.Events.Event = event;\n\n // However, if we have not had any prior invalidations for this frame, we\n // want to consider StyleRecalculation events as they might be the actual\n // cause of this layout invalidation.\n if (!lastInvalidationEventForFrame.has(event.args.data.frame)) {\n // 1. If we have not had an invalidation event for this frame\n // 2. AND we have had an UpdateLayoutTree for this frame\n // 3. AND the UpdateLayoutTree event ended AFTER the InvalidateLayout startTime\n // 4. AND we have an initiator for the UpdateLayoutTree event\n // 5. Then we set the last invalidation event for this frame to be the UpdateLayoutTree's initiator.\n const lastUpdateLayoutTreeForFrame = lastUpdateLayoutTreeByFrame.get(event.args.data.frame);\n if (lastUpdateLayoutTreeForFrame) {\n const {endTime} = Helpers.Timing.eventTimingsMicroSeconds(lastUpdateLayoutTreeForFrame);\n const initiatorOfUpdateLayout = eventToInitiatorMap.get(lastUpdateLayoutTreeForFrame);\n\n if (initiatorOfUpdateLayout && endTime && endTime > event.ts) {\n invalidationInitiator = initiatorOfUpdateLayout;\n }\n }\n }\n lastInvalidationEventForFrame.set(event.args.data.frame, invalidationInitiator);\n } else if (Types.Events.isLayout(event)) {\n // The initiator of a Layout event is the last Invalidation event.\n const lastInvalidation = lastInvalidationEventForFrame.get(event.args.beginData.frame);\n if (lastInvalidation) {\n storeInitiator({\n event,\n initiator: lastInvalidation,\n });\n }\n // Now clear the last invalidation for the frame: the last invalidation has been linked to a Layout event, so it cannot be the initiator for any future layouts.\n lastInvalidationEventForFrame.delete(event.args.beginData.frame);\n } else if (Types.Events.isRequestAnimationFrame(event)) {\n requestAnimationFrameEventsById.set(event.args.data.id, event);\n } else if (Types.Events.isFireAnimationFrame(event)) {\n // If we get a fire event, that means we should have had the\n // RequestAnimationFrame event by now. If so, we can set that as the\n // initiator for the fire event.\n const matchingRequestEvent = requestAnimationFrameEventsById.get(event.args.data.id);\n if (matchingRequestEvent) {\n storeInitiator({\n event,\n initiator: matchingRequestEvent,\n });\n }\n } else if (Types.Events.isTimerInstall(event)) {\n timerInstallEventsById.set(event.args.data.timerId, event);\n } else if (Types.Events.isTimerFire(event)) {\n const matchingInstall = timerInstallEventsById.get(event.args.data.timerId);\n if (matchingInstall) {\n storeInitiator({event, initiator: matchingInstall});\n }\n } else if (Types.Events.isRequestIdleCallback(event)) {\n requestIdleCallbackEventsById.set(event.args.data.id, event);\n } else if (Types.Events.isFireIdleCallback(event)) {\n const matchingRequestEvent = requestIdleCallbackEventsById.get(event.args.data.id);\n if (matchingRequestEvent) {\n storeInitiator({\n event,\n initiator: matchingRequestEvent,\n });\n }\n } else if (Types.Events.isWebSocketCreate(event)) {\n webSocketCreateEventsById.set(event.args.data.identifier, event);\n } else if (Types.Events.isWebSocketInfo(event) || Types.Events.isWebSocketTransfer(event)) {\n const matchingCreateEvent = webSocketCreateEventsById.get(event.args.data.identifier);\n if (matchingCreateEvent) {\n storeInitiator({\n event,\n initiator: matchingCreateEvent,\n });\n }\n } else if (Types.Events.isSchedulePostTaskCallback(event)) {\n schedulePostTaskCallbackEventsById.set(event.args.data.taskId, event);\n } else if (Types.Events.isRunPostTaskCallback(event) || Types.Events.isAbortPostTaskCallback(event)) {\n const matchingSchedule = schedulePostTaskCallbackEventsById.get(event.args.data.taskId);\n if (matchingSchedule) {\n storeInitiator({event, initiator: matchingSchedule});\n }\n }\n}\n\nfunction createRelationshipsFromFlows(): void {\n const flows = flowsHandlerData().flows;\n for (let i = 0; i < flows.length; i++) {\n const flow = flows[i];\n for (let j = 0; j < flow.length - 1; j++) {\n storeInitiator({event: flow[j + 1], initiator: flow[j]});\n }\n }\n}\n\nfunction createRelationshipsFromAsyncJSCalls(): void {\n const asyncCallEntries = AsyncJSCallsHandlerData().schedulerToRunEntryPoints.entries();\n for (const [asyncCaller, asyncCallees] of asyncCallEntries) {\n for (const asyncCallee of asyncCallees) {\n storeInitiator({event: asyncCallee, initiator: asyncCaller});\n }\n }\n}\n\nexport async function finalize(): Promise<void> {\n createRelationshipsFromFlows();\n createRelationshipsFromAsyncJSCalls();\n}\n\nexport interface InitiatorsData {\n eventToInitiator: Map<Types.Events.Event, Types.Events.Event>;\n initiatorToEvents: Map<Types.Events.Event, Types.Events.Event[]>;\n}\n\nexport function data(): InitiatorsData {\n return {\n eventToInitiator: eventToInitiatorMap,\n initiatorToEvents: initiatorToEventsMap,\n };\n}\n\nexport function deps(): ['Flows', 'AsyncJSCalls'] {\n return ['Flows', 'AsyncJSCalls'];\n}\n"]}
@@ -1,11 +1,9 @@
1
- import type * as Protocol from '../../../generated/protocol.js';
2
1
  import * as Types from '../types/types.js';
3
2
  import type { HandlerName } from './types.js';
4
3
  export declare function reset(): void;
5
4
  export declare function handleEvent(event: Types.Events.Event): void;
6
5
  export declare function finalize(): Promise<void>;
7
6
  export interface LargestImagePaintData {
8
- imageByDOMNodeId: Map<Protocol.DOM.BackendNodeId, Types.Events.LargestImagePaintCandidate>;
9
7
  lcpRequestByNavigation: Map<Types.Events.NavigationStart | null, Types.Events.SyntheticNetworkRequest>;
10
8
  }
11
9
  export declare function data(): LargestImagePaintData;
@@ -1,9 +1,11 @@
1
1
  // Copyright 2022 The Chromium Authors. All rights reserved.
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 Types from '../types/types.js';
5
6
  import { data as metaData } from './MetaHandler.js';
6
7
  import { data as networkRequestsData } from './NetworkRequestsHandler.js';
8
+ import { data as pageLoadMetricsData } from './PageLoadMetricsHandler.js';
7
9
  /**
8
10
  * If the LCP resource was an image, and that image was fetched over the
9
11
  * network, we want to be able to find the network request in order to construct
@@ -16,45 +18,46 @@ import { data as networkRequestsData } from './NetworkRequestsHandler.js';
16
18
  * `LargestContentfulPaint::Candidate` will have. So, when we find an image
17
19
  * paint candidate, we can store it, keying it on the node ID.
18
20
  * Then, when it comes to finding the network request for an LCP image, we can
19
- *
20
21
  * use the nodeId from the LCP candidate to find the image candidate. That image
21
22
  * candidate also contains a `imageUrl` property, which will have the full URL
22
23
  * to the image.
24
+ *
25
+ * `BackendNodeId`s are only unique within a given renderer process, so this is
26
+ * also keyed on `ProcessId`.
23
27
  **/
24
- const imageByDOMNodeId = new Map();
28
+ const imagePaintsByNodeIdAndProcess = new Map();
25
29
  const lcpRequestByNavigation = new Map();
26
- const lcpPaintEventByNavigation = new Map();
27
- let currentNavigation;
28
30
  export function reset() {
29
- imageByDOMNodeId.clear();
30
- lcpRequestByNavigation.clear();
31
- lcpPaintEventByNavigation.clear();
32
- currentNavigation = null;
31
+ imagePaintsByNodeIdAndProcess.clear();
33
32
  }
34
33
  export function handleEvent(event) {
35
- if (Types.Events.isNavigationStart(event)) {
36
- currentNavigation = event;
37
- return;
38
- }
39
- if (!Types.Events.isLargestImagePaintCandidate(event)) {
40
- return;
41
- }
42
- if (!event.args.data) {
34
+ if (!Types.Events.isLargestImagePaintCandidate(event) || !event.args.data) {
43
35
  return;
44
36
  }
45
- imageByDOMNodeId.set(event.args.data.DOMNodeId, event);
46
- lcpPaintEventByNavigation.set(currentNavigation, event);
37
+ const imagePaintsByNodeId = Platform.MapUtilities.getWithDefault(imagePaintsByNodeIdAndProcess, event.pid, () => new Map());
38
+ imagePaintsByNodeId.set(event.args.data.DOMNodeId, event);
47
39
  }
48
40
  export async function finalize() {
49
41
  const requests = networkRequestsData().byTime;
50
- const traceBounds = metaData().traceBounds;
51
- for (const [navigation, event] of lcpPaintEventByNavigation) {
52
- const lcpUrl = event.args.data?.imageUrl;
42
+ const { traceBounds, navigationsByNavigationId } = metaData();
43
+ const metricScoresByFrameId = pageLoadMetricsData().metricScoresByFrameId;
44
+ for (const [navigationId, navigation] of navigationsByNavigationId) {
45
+ const lcpMetric = metricScoresByFrameId.get(navigation.args.frame)?.get(navigationId)?.get("LCP" /* MetricName.LCP */);
46
+ const lcpEvent = lcpMetric?.event;
47
+ if (!lcpEvent || !Types.Events.isLargestContentfulPaintCandidate(lcpEvent)) {
48
+ continue;
49
+ }
50
+ const nodeId = lcpEvent.args.data?.nodeId;
51
+ if (!nodeId) {
52
+ continue;
53
+ }
54
+ const lcpImagePaintEvent = imagePaintsByNodeIdAndProcess.get(lcpEvent.pid)?.get(nodeId);
55
+ const lcpUrl = lcpImagePaintEvent?.args.data?.imageUrl;
53
56
  if (!lcpUrl) {
54
57
  continue;
55
58
  }
56
59
  const startTime = navigation?.ts ?? traceBounds.min;
57
- const endTime = event.ts;
60
+ const endTime = lcpImagePaintEvent.ts;
58
61
  let lcpRequest;
59
62
  for (const request of requests) {
60
63
  if (request.ts < startTime) {
@@ -74,9 +77,9 @@ export async function finalize() {
74
77
  }
75
78
  }
76
79
  export function data() {
77
- return { imageByDOMNodeId, lcpRequestByNavigation };
80
+ return { lcpRequestByNavigation };
78
81
  }
79
82
  export function deps() {
80
- return ['Meta', 'NetworkRequests'];
83
+ return ['Meta', 'NetworkRequests', 'PageLoadMetrics'];
81
84
  }
82
85
  //# sourceMappingURL=LargestImagePaintHandler.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"LargestImagePaintHandler.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/handlers/LargestImagePaintHandler.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 type * as Protocol from '../../../generated/protocol.js';\nimport * as Types from '../types/types.js';\n\nimport {data as metaData} from './MetaHandler.js';\nimport {data as networkRequestsData} from './NetworkRequestsHandler.js';\nimport type {HandlerName} from './types.js';\n\n/**\n * If the LCP resource was an image, and that image was fetched over the\n * network, we want to be able to find the network request in order to construct\n * the critical path for an LCP image.\n * Within the trace file there are `LargestImagePaint::Candidate` events.\n * Within their data object, they contain a `DOMNodeId` property, which maps to\n * the DOM Node ID for that image.\n *\n * This id maps exactly to the `data.nodeId` property that a\n * `LargestContentfulPaint::Candidate` will have. So, when we find an image\n * paint candidate, we can store it, keying it on the node ID.\n * Then, when it comes to finding the network request for an LCP image, we can\n *\n * use the nodeId from the LCP candidate to find the image candidate. That image\n * candidate also contains a `imageUrl` property, which will have the full URL\n * to the image.\n **/\nconst imageByDOMNodeId = new Map<Protocol.DOM.BackendNodeId, Types.Events.LargestImagePaintCandidate>();\nconst lcpRequestByNavigation = new Map<Types.Events.NavigationStart|null, Types.Events.SyntheticNetworkRequest>();\nconst lcpPaintEventByNavigation = new Map<Types.Events.NavigationStart|null, Types.Events.LargestImagePaintCandidate>();\nlet currentNavigation: Types.Events.NavigationStart|null;\n\nexport function reset(): void {\n imageByDOMNodeId.clear();\n lcpRequestByNavigation.clear();\n lcpPaintEventByNavigation.clear();\n currentNavigation = null;\n}\n\nexport function handleEvent(event: Types.Events.Event): void {\n if (Types.Events.isNavigationStart(event)) {\n currentNavigation = event;\n return;\n }\n\n if (!Types.Events.isLargestImagePaintCandidate(event)) {\n return;\n }\n\n if (!event.args.data) {\n return;\n }\n\n imageByDOMNodeId.set(event.args.data.DOMNodeId, event);\n lcpPaintEventByNavigation.set(currentNavigation, event);\n}\n\nexport async function finalize(): Promise<void> {\n const requests = networkRequestsData().byTime;\n const traceBounds = metaData().traceBounds;\n\n for (const [navigation, event] of lcpPaintEventByNavigation) {\n const lcpUrl = event.args.data?.imageUrl;\n if (!lcpUrl) {\n continue;\n }\n\n const startTime = navigation?.ts ?? traceBounds.min;\n const endTime = event.ts;\n\n let lcpRequest;\n for (const request of requests) {\n if (request.ts < startTime) {\n continue;\n }\n if (request.ts >= endTime) {\n break;\n }\n\n if (request.args.data.url === lcpUrl || request.args.data.redirects.some(r => r.url === lcpUrl)) {\n lcpRequest = request;\n break;\n }\n }\n\n if (lcpRequest) {\n lcpRequestByNavigation.set(navigation, lcpRequest);\n }\n }\n}\n\nexport interface LargestImagePaintData {\n imageByDOMNodeId: Map<Protocol.DOM.BackendNodeId, Types.Events.LargestImagePaintCandidate>;\n lcpRequestByNavigation: Map<Types.Events.NavigationStart|null, Types.Events.SyntheticNetworkRequest>;\n}\n\nexport function data(): LargestImagePaintData {\n return {imageByDOMNodeId, lcpRequestByNavigation};\n}\n\nexport function deps(): HandlerName[] {\n return ['Meta', 'NetworkRequests'];\n}\n"]}
1
+ {"version":3,"file":"LargestImagePaintHandler.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/handlers/LargestImagePaintHandler.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 Platform from '../../../core/platform/platform.js';\nimport type * as Protocol from '../../../generated/protocol.js';\nimport * as Types from '../types/types.js';\n\nimport {data as metaData} from './MetaHandler.js';\nimport {data as networkRequestsData} from './NetworkRequestsHandler.js';\nimport {data as pageLoadMetricsData, MetricName} from './PageLoadMetricsHandler.js';\nimport type {HandlerName} from './types.js';\n\n/**\n * If the LCP resource was an image, and that image was fetched over the\n * network, we want to be able to find the network request in order to construct\n * the critical path for an LCP image.\n * Within the trace file there are `LargestImagePaint::Candidate` events.\n * Within their data object, they contain a `DOMNodeId` property, which maps to\n * the DOM Node ID for that image.\n *\n * This id maps exactly to the `data.nodeId` property that a\n * `LargestContentfulPaint::Candidate` will have. So, when we find an image\n * paint candidate, we can store it, keying it on the node ID.\n * Then, when it comes to finding the network request for an LCP image, we can\n * use the nodeId from the LCP candidate to find the image candidate. That image\n * candidate also contains a `imageUrl` property, which will have the full URL\n * to the image.\n *\n * `BackendNodeId`s are only unique within a given renderer process, so this is\n * also keyed on `ProcessId`.\n **/\nconst imagePaintsByNodeIdAndProcess =\n new Map<Types.Events.ProcessID, Map<Protocol.DOM.BackendNodeId, Types.Events.LargestImagePaintCandidate>>();\nconst lcpRequestByNavigation = new Map<Types.Events.NavigationStart|null, Types.Events.SyntheticNetworkRequest>();\n\nexport function reset(): void {\n imagePaintsByNodeIdAndProcess.clear();\n}\n\nexport function handleEvent(event: Types.Events.Event): void {\n if (!Types.Events.isLargestImagePaintCandidate(event) || !event.args.data) {\n return;\n }\n\n const imagePaintsByNodeId =\n Platform.MapUtilities.getWithDefault(imagePaintsByNodeIdAndProcess, event.pid, () => new Map());\n imagePaintsByNodeId.set(event.args.data.DOMNodeId, event);\n}\n\nexport async function finalize(): Promise<void> {\n const requests = networkRequestsData().byTime;\n const {traceBounds, navigationsByNavigationId} = metaData();\n const metricScoresByFrameId = pageLoadMetricsData().metricScoresByFrameId;\n\n for (const [navigationId, navigation] of navigationsByNavigationId) {\n const lcpMetric = metricScoresByFrameId.get(navigation.args.frame)?.get(navigationId)?.get(MetricName.LCP);\n const lcpEvent = lcpMetric?.event;\n if (!lcpEvent || !Types.Events.isLargestContentfulPaintCandidate(lcpEvent)) {\n continue;\n }\n\n const nodeId = lcpEvent.args.data?.nodeId;\n if (!nodeId) {\n continue;\n }\n\n const lcpImagePaintEvent = imagePaintsByNodeIdAndProcess.get(lcpEvent.pid)?.get(nodeId);\n const lcpUrl = lcpImagePaintEvent?.args.data?.imageUrl;\n if (!lcpUrl) {\n continue;\n }\n\n const startTime = navigation?.ts ?? traceBounds.min;\n const endTime = lcpImagePaintEvent.ts;\n\n let lcpRequest;\n for (const request of requests) {\n if (request.ts < startTime) {\n continue;\n }\n if (request.ts >= endTime) {\n break;\n }\n\n if (request.args.data.url === lcpUrl || request.args.data.redirects.some(r => r.url === lcpUrl)) {\n lcpRequest = request;\n break;\n }\n }\n\n if (lcpRequest) {\n lcpRequestByNavigation.set(navigation, lcpRequest);\n }\n }\n}\n\nexport interface LargestImagePaintData {\n lcpRequestByNavigation: Map<Types.Events.NavigationStart|null, Types.Events.SyntheticNetworkRequest>;\n}\n\nexport function data(): LargestImagePaintData {\n return {lcpRequestByNavigation};\n}\n\nexport function deps(): HandlerName[] {\n return ['Meta', 'NetworkRequests', 'PageLoadMetrics'];\n}\n"]}
@@ -21,10 +21,10 @@ interface LayoutShifts {
21
21
  }
22
22
  export declare const MAX_CLUSTER_DURATION: Types.Timing.MicroSeconds;
23
23
  export declare const MAX_SHIFT_TIME_DELTA: Types.Timing.MicroSeconds;
24
- type ScoreRecord = {
24
+ interface ScoreRecord {
25
25
  ts: number;
26
26
  score: number;
27
- };
27
+ }
28
28
  export declare function reset(): void;
29
29
  export declare function handleEvent(event: Types.Events.Event): void;
30
30
  export declare function finalize(): Promise<void>;
@@ -8,10 +8,10 @@ import { data as metaHandlerData } from './MetaHandler.js';
8
8
  import { data as screenshotsHandlerData } from './ScreenshotsHandler.js';
9
9
  // This represents the maximum #time we will allow a cluster to go before we
10
10
  // reset it.
11
- export const MAX_CLUSTER_DURATION = Helpers.Timing.millisecondsToMicroseconds(Types.Timing.MilliSeconds(5000));
11
+ export const MAX_CLUSTER_DURATION = Helpers.Timing.milliToMicro(Types.Timing.MilliSeconds(5000));
12
12
  // This represents the maximum #time we will allow between layout shift events
13
13
  // before considering it to be the start of a new cluster.
14
- export const MAX_SHIFT_TIME_DELTA = Helpers.Timing.millisecondsToMicroseconds(Types.Timing.MilliSeconds(1000));
14
+ export const MAX_SHIFT_TIME_DELTA = Helpers.Timing.milliToMicro(Types.Timing.MilliSeconds(1000));
15
15
  // Layout shifts are reported globally to the developer, irrespective of which
16
16
  // frame they originated in. However, each process does have its own individual
17
17
  // CLS score, so we need to segment by process. This means Layout Shifts from
@@ -265,6 +265,7 @@ async function buildLayoutShiftsClusters() {
265
265
  const shift = Helpers.SyntheticEvents.SyntheticEventsManager.registerSyntheticEvent({
266
266
  rawSourceEvent: event,
267
267
  ...event,
268
+ name: "SyntheticLayoutShift" /* Types.Events.Name.SYNTHETIC_LAYOUT_SHIFT */,
268
269
  args: {
269
270
  frame: event.args.frame,
270
271
  data: {