@paulirish/trace_engine 0.0.50 → 0.0.51

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 (225) hide show
  1. package/.tmp/tsbuildinfo/tsconfig.tsbuildinfo +1 -1
  2. package/core/platform/Constructor.d.ts +2 -2
  3. package/core/platform/Constructor.js.map +1 -1
  4. package/core/platform/StringUtilities.d.ts +2 -5
  5. package/core/platform/StringUtilities.js +0 -3
  6. package/core/platform/StringUtilities.js.map +1 -1
  7. package/core/platform/TypescriptUtilities.d.ts +1 -1
  8. package/core/platform/TypescriptUtilities.js +1 -1
  9. package/core/platform/TypescriptUtilities.js.map +1 -1
  10. package/core/platform/devtools_entrypoint-bundle-typescript-tsconfig.json +2 -2
  11. package/core/platform/platform-tsconfig.json +2 -3
  12. package/core/platform/platform.d.ts +1 -2
  13. package/core/platform/platform.js +1 -2
  14. package/core/platform/platform.js.map +1 -1
  15. package/generated/protocol.d.ts +156 -31
  16. package/locales/af.json +63 -3
  17. package/locales/am.json +63 -3
  18. package/locales/ar.json +63 -3
  19. package/locales/as.json +63 -3
  20. package/locales/az.json +63 -3
  21. package/locales/be.json +64 -4
  22. package/locales/bg.json +62 -2
  23. package/locales/bn.json +63 -3
  24. package/locales/bs.json +63 -3
  25. package/locales/ca.json +62 -2
  26. package/locales/cs.json +62 -2
  27. package/locales/cy.json +63 -3
  28. package/locales/da.json +63 -3
  29. package/locales/de.json +63 -3
  30. package/locales/el.json +62 -2
  31. package/locales/en-GB.json +63 -3
  32. package/locales/en-US.json +55 -22
  33. package/locales/en-XL.json +55 -22
  34. package/locales/es-419.json +63 -3
  35. package/locales/es.json +64 -4
  36. package/locales/et.json +64 -4
  37. package/locales/eu.json +79 -19
  38. package/locales/fa.json +62 -2
  39. package/locales/fi.json +63 -3
  40. package/locales/fil.json +63 -3
  41. package/locales/fr-CA.json +63 -3
  42. package/locales/fr.json +63 -3
  43. package/locales/gl.json +62 -2
  44. package/locales/gu.json +63 -3
  45. package/locales/he.json +63 -3
  46. package/locales/hi.json +62 -2
  47. package/locales/hr.json +63 -3
  48. package/locales/hu.json +62 -2
  49. package/locales/hy.json +63 -3
  50. package/locales/id.json +62 -2
  51. package/locales/is.json +64 -4
  52. package/locales/it.json +64 -4
  53. package/locales/ja.json +63 -3
  54. package/locales/ka.json +63 -3
  55. package/locales/kk.json +62 -2
  56. package/locales/km.json +62 -2
  57. package/locales/kn.json +63 -3
  58. package/locales/ko.json +63 -3
  59. package/locales/ky.json +63 -3
  60. package/locales/lo.json +63 -3
  61. package/locales/lt.json +62 -2
  62. package/locales/lv.json +62 -2
  63. package/locales/mk.json +62 -2
  64. package/locales/ml.json +63 -3
  65. package/locales/mn.json +63 -3
  66. package/locales/mr.json +64 -4
  67. package/locales/ms.json +63 -3
  68. package/locales/my.json +63 -3
  69. package/locales/ne.json +64 -4
  70. package/locales/nl.json +63 -3
  71. package/locales/no.json +62 -2
  72. package/locales/or.json +63 -3
  73. package/locales/pa.json +62 -2
  74. package/locales/pl.json +62 -2
  75. package/locales/pt-PT.json +62 -2
  76. package/locales/pt.json +63 -3
  77. package/locales/ro.json +63 -3
  78. package/locales/ru.json +63 -3
  79. package/locales/si.json +63 -3
  80. package/locales/sk.json +63 -3
  81. package/locales/sl.json +62 -2
  82. package/locales/sq.json +63 -3
  83. package/locales/sr-Latn.json +62 -2
  84. package/locales/sr.json +62 -2
  85. package/locales/sv.json +62 -2
  86. package/locales/sw.json +63 -3
  87. package/locales/ta.json +66 -6
  88. package/locales/te.json +63 -3
  89. package/locales/th.json +63 -3
  90. package/locales/tr.json +63 -3
  91. package/locales/uk.json +63 -3
  92. package/locales/ur.json +64 -4
  93. package/locales/uz.json +62 -2
  94. package/locales/vi.json +62 -2
  95. package/locales/zh-HK.json +63 -3
  96. package/locales/zh-TW.json +63 -3
  97. package/locales/zh.json +62 -2
  98. package/locales/zu.json +63 -3
  99. package/models/cpu_profile/CPUProfileDataModel.d.ts +8 -0
  100. package/models/cpu_profile/CPUProfileDataModel.js.map +1 -1
  101. package/models/cpu_profile/cpu_profile-tsconfig.json +2 -2
  102. package/models/cpu_profile/devtools_entrypoint-bundle-typescript-tsconfig.json +2 -2
  103. package/models/trace/Processor.d.ts +1 -1
  104. package/models/trace/Processor.js +94 -74
  105. package/models/trace/Processor.js.map +1 -1
  106. package/models/trace/TracingManager.js.map +1 -1
  107. package/models/trace/bundle-tsconfig.json +1 -1
  108. package/models/trace/devtools_entrypoint-bundle-typescript-tsconfig.json +20 -2
  109. package/models/trace/extras/ScriptDuplication.d.ts +11 -15
  110. package/models/trace/extras/ScriptDuplication.js +59 -77
  111. package/models/trace/extras/ScriptDuplication.js.map +1 -1
  112. package/models/trace/extras/ThirdParties.d.ts +6 -13
  113. package/models/trace/extras/ThirdParties.js +69 -133
  114. package/models/trace/extras/ThirdParties.js.map +1 -1
  115. package/models/trace/extras/TraceTree.d.ts +15 -3
  116. package/models/trace/extras/TraceTree.js +36 -8
  117. package/models/trace/extras/TraceTree.js.map +1 -1
  118. package/models/trace/extras/devtools_entrypoint-bundle-typescript-tsconfig.json +2 -2
  119. package/models/trace/extras/extras-tsconfig.json +2 -2
  120. package/models/trace/extras/extras.d.ts +880 -787
  121. package/models/trace/extras/extras.js +880 -787
  122. package/models/trace/handlers/AsyncJSCallsHandler.js +36 -4
  123. package/models/trace/handlers/AsyncJSCallsHandler.js.map +1 -1
  124. package/models/trace/handlers/InvalidationsHandler.js +1 -0
  125. package/models/trace/handlers/InvalidationsHandler.js.map +1 -1
  126. package/models/trace/handlers/ModelHandlers.d.ts +0 -1
  127. package/models/trace/handlers/ModelHandlers.js +0 -1
  128. package/models/trace/handlers/ModelHandlers.js.map +1 -1
  129. package/models/trace/handlers/NetworkRequestsHandler.js +1 -1
  130. package/models/trace/handlers/NetworkRequestsHandler.js.map +1 -1
  131. package/models/trace/handlers/RendererHandler.d.ts +1 -1
  132. package/models/trace/handlers/RendererHandler.js +2 -2
  133. package/models/trace/handlers/RendererHandler.js.map +1 -1
  134. package/models/trace/handlers/ScreenshotsHandler.js +3 -1
  135. package/models/trace/handlers/ScreenshotsHandler.js.map +1 -1
  136. package/models/trace/handlers/ScriptsHandler.d.ts +12 -0
  137. package/models/trace/handlers/ScriptsHandler.js +93 -1
  138. package/models/trace/handlers/ScriptsHandler.js.map +1 -1
  139. package/models/trace/handlers/devtools_entrypoint-bundle-typescript-tsconfig.json +2 -2
  140. package/models/trace/handlers/handlers-tsconfig.json +2 -3
  141. package/models/trace/helpers/Network.d.ts +5 -0
  142. package/models/trace/helpers/Network.js +17 -0
  143. package/models/trace/helpers/Network.js.map +1 -1
  144. package/models/trace/helpers/SamplesIntegrator.js +19 -23
  145. package/models/trace/helpers/SamplesIntegrator.js.map +1 -1
  146. package/models/trace/helpers/SyntheticEvents.d.ts +0 -1
  147. package/models/trace/helpers/SyntheticEvents.js +0 -4
  148. package/models/trace/helpers/SyntheticEvents.js.map +1 -1
  149. package/models/trace/helpers/Trace.d.ts +2 -1
  150. package/models/trace/helpers/Trace.js +101 -1
  151. package/models/trace/helpers/Trace.js.map +1 -1
  152. package/models/trace/helpers/TreeHelpers.d.ts +2 -5
  153. package/models/trace/helpers/TreeHelpers.js +0 -4
  154. package/models/trace/helpers/TreeHelpers.js.map +1 -1
  155. package/models/trace/helpers/devtools_entrypoint-bundle-typescript-tsconfig.json +2 -2
  156. package/models/trace/helpers/helpers-tsconfig.json +2 -2
  157. package/models/trace/insights/{UseCache.d.ts → Cache.d.ts} +2 -2
  158. package/models/trace/insights/{UseCache.js → Cache.js} +11 -5
  159. package/models/trace/insights/Cache.js.map +1 -0
  160. package/models/trace/insights/Common.d.ts +21 -2
  161. package/models/trace/insights/Common.js +86 -2
  162. package/models/trace/insights/Common.js.map +1 -1
  163. package/models/trace/insights/DocumentLatency.js +2 -8
  164. package/models/trace/insights/DocumentLatency.js.map +1 -1
  165. package/models/trace/insights/DuplicatedJavaScript.d.ts +7 -4
  166. package/models/trace/insights/DuplicatedJavaScript.js +25 -5
  167. package/models/trace/insights/DuplicatedJavaScript.js.map +1 -1
  168. package/models/trace/insights/ForcedReflow.d.ts +5 -1
  169. package/models/trace/insights/ForcedReflow.js +6 -2
  170. package/models/trace/insights/ForcedReflow.js.map +1 -1
  171. package/models/trace/insights/InteractionToNextPaint.d.ts +1 -0
  172. package/models/trace/insights/InteractionToNextPaint.js +3 -0
  173. package/models/trace/insights/InteractionToNextPaint.js.map +1 -1
  174. package/models/trace/insights/LegacyJavaScript.d.ts +32 -0
  175. package/models/trace/insights/LegacyJavaScript.js +77 -0
  176. package/models/trace/insights/LegacyJavaScript.js.map +1 -0
  177. package/models/trace/insights/Models.d.ts +3 -1
  178. package/models/trace/insights/Models.js +3 -1
  179. package/models/trace/insights/Models.js.map +1 -1
  180. package/models/trace/insights/ModernHTTP.d.ts +51 -0
  181. package/models/trace/insights/ModernHTTP.js +187 -0
  182. package/models/trace/insights/ModernHTTP.js.map +1 -0
  183. package/models/trace/insights/NetworkDependencyTree.js +1 -1
  184. package/models/trace/insights/NetworkDependencyTree.js.map +1 -1
  185. package/models/trace/insights/ThirdParties.d.ts +1 -5
  186. package/models/trace/insights/ThirdParties.js +8 -21
  187. package/models/trace/insights/ThirdParties.js.map +1 -1
  188. package/models/trace/insights/devtools_entrypoint-bundle-typescript-tsconfig.json +2 -2
  189. package/models/trace/insights/insights-tsconfig.json +8 -3
  190. package/models/trace/insights/types.d.ts +1 -0
  191. package/models/trace/insights/types.js.map +1 -1
  192. package/models/trace/lantern/core/core-tsconfig.json +2 -2
  193. package/models/trace/lantern/core/devtools_entrypoint-bundle-typescript-tsconfig.json +2 -2
  194. package/models/trace/lantern/devtools_entrypoint-bundle-typescript-tsconfig.json +2 -2
  195. package/models/trace/lantern/graph/devtools_entrypoint-bundle-typescript-tsconfig.json +2 -2
  196. package/models/trace/lantern/graph/graph-tsconfig.json +2 -2
  197. package/models/trace/lantern/lantern-tsconfig.json +2 -2
  198. package/models/trace/lantern/metrics/Metric.d.ts +4 -4
  199. package/models/trace/lantern/metrics/Metric.js +4 -6
  200. package/models/trace/lantern/metrics/Metric.js.map +1 -1
  201. package/models/trace/lantern/metrics/devtools_entrypoint-bundle-typescript-tsconfig.json +2 -2
  202. package/models/trace/lantern/metrics/metrics-tsconfig.json +2 -2
  203. package/models/trace/lantern/simulation/devtools_entrypoint-bundle-typescript-tsconfig.json +2 -2
  204. package/models/trace/lantern/simulation/simulation-tsconfig.json +2 -2
  205. package/models/trace/lantern/types/devtools_entrypoint-bundle-typescript-tsconfig.json +2 -2
  206. package/models/trace/lantern/types/types-tsconfig.json +2 -2
  207. package/models/trace/trace-tsconfig.json +2 -2
  208. package/models/trace/types/Configuration.d.ts +2 -0
  209. package/models/trace/types/Configuration.js.map +1 -1
  210. package/models/trace/types/TraceEvents.d.ts +28 -50
  211. package/models/trace/types/TraceEvents.js +8 -23
  212. package/models/trace/types/TraceEvents.js.map +1 -1
  213. package/models/trace/types/devtools_entrypoint-bundle-typescript-tsconfig.json +2 -2
  214. package/models/trace/types/types-tsconfig.json +2 -2
  215. package/package.json +3 -2
  216. package/test/test-trace-engine.mjs +19 -17
  217. package/third_party/legacy-javascript/legacy-javascript.d.ts +1 -0
  218. package/third_party/legacy-javascript/legacy-javascript.js +1 -0
  219. package/core/platform/ServerTiming.d.ts +0 -31
  220. package/core/platform/ServerTiming.js +0 -212
  221. package/core/platform/ServerTiming.js.map +0 -1
  222. package/models/trace/handlers/ServerTimingsHandler.d.ts +0 -9
  223. package/models/trace/handlers/ServerTimingsHandler.js +0 -106
  224. package/models/trace/handlers/ServerTimingsHandler.js.map +0 -1
  225. package/models/trace/insights/UseCache.js.map +0 -1
@@ -1,66 +1,11 @@
1
1
  // Copyright 2025 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 Handlers from '../handlers/handlers.js';
4
5
  // Ignore modules smaller than an absolute threshold.
5
6
  const ABSOLUTE_SIZE_THRESHOLD_BYTES = 1024 * 0.5;
6
7
  // Ignore modules smaller than a % size of largest copy of the module.
7
8
  const RELATIVE_SIZE_THRESHOLD = 0.1;
8
- /**
9
- * Using a script's contents and source map, attribute every generated byte to an authored source file.
10
- */
11
- export function computeGeneratedFileSizes(script) {
12
- if (!script.sourceMap) {
13
- throw new Error('expected source map');
14
- }
15
- const map = script.sourceMap;
16
- const content = script.content ?? '';
17
- const contentLength = content.length;
18
- const lines = content.split('\n');
19
- const files = {};
20
- const totalBytes = contentLength;
21
- let unmappedBytes = totalBytes;
22
- const lastGeneratedColumnMap = computeLastGeneratedColumnMap(script.sourceMap);
23
- for (const mapping of map.mappings()) {
24
- const source = mapping.sourceURL;
25
- const lineNum = mapping.lineNumber;
26
- const colNum = mapping.columnNumber;
27
- const lastColNum = lastGeneratedColumnMap.get(mapping);
28
- // Webpack sometimes emits null mappings.
29
- // https://github.com/mozilla/source-map/pull/303
30
- if (!source) {
31
- continue;
32
- }
33
- // Lines and columns are zero-based indices. Visually, lines are shown as a 1-based index.
34
- const line = lines[lineNum];
35
- if (line === null || line === undefined) {
36
- const errorMessage = `${map.url()} mapping for line out of bounds: ${lineNum + 1}`;
37
- return { errorMessage };
38
- }
39
- if (colNum > line.length) {
40
- const errorMessage = `${map.url()} mapping for column out of bounds: ${lineNum + 1}:${colNum}`;
41
- return { errorMessage };
42
- }
43
- let mappingLength = 0;
44
- if (lastColNum !== undefined) {
45
- if (lastColNum > line.length) {
46
- const errorMessage = `${map.url()} mapping for last column out of bounds: ${lineNum + 1}:${lastColNum}`;
47
- return { errorMessage };
48
- }
49
- mappingLength = lastColNum - colNum;
50
- }
51
- else {
52
- // Add +1 to account for the newline.
53
- mappingLength = line.length - colNum + 1;
54
- }
55
- files[source] = (files[source] || 0) + mappingLength;
56
- unmappedBytes -= mappingLength;
57
- }
58
- return {
59
- files,
60
- unmappedBytes,
61
- totalBytes,
62
- };
63
- }
64
9
  export function normalizeSource(source) {
65
10
  // Trim trailing question mark - b/c webpack.
66
11
  source = source.replace(/\?$/, '');
@@ -111,33 +56,66 @@ export function normalizeDuplication(duplication) {
111
56
  data.estimatedDuplicateBytes = data.duplicates.slice(1).reduce((acc, cur) => acc + cur.attributedSize, 0);
112
57
  }
113
58
  }
114
- function computeLastGeneratedColumnMap(map) {
115
- const result = new Map();
116
- const mappings = map.mappings();
117
- for (let i = 0; i < mappings.length - 1; i++) {
118
- const mapping = mappings[i];
119
- const nextMapping = mappings[i + 1];
120
- if (mapping.lineNumber === nextMapping.lineNumber) {
121
- result.set(mapping, nextMapping.columnNumber);
59
+ function indexOfOrLength(haystack, needle, startPosition = 0) {
60
+ const index = haystack.indexOf(needle, startPosition);
61
+ return index === -1 ? haystack.length : index;
62
+ }
63
+ export function getNodeModuleName(source) {
64
+ const sourceSplit = source.split('node_modules/');
65
+ source = sourceSplit[sourceSplit.length - 1];
66
+ const indexFirstSlash = indexOfOrLength(source, '/');
67
+ if (source[0] === '@') {
68
+ return source.slice(0, indexOfOrLength(source, '/', indexFirstSlash + 1));
69
+ }
70
+ return source.slice(0, indexFirstSlash);
71
+ }
72
+ function groupByNodeModules(duplication) {
73
+ const groupedDuplication = new Map();
74
+ for (const [source, data] of duplication) {
75
+ if (!source.includes('node_modules')) {
76
+ groupedDuplication.set(source, data);
77
+ continue;
78
+ }
79
+ const nodeModuleKey = 'node_modules/' + getNodeModuleName(source);
80
+ const aggregatedData = groupedDuplication.get(nodeModuleKey) ?? {
81
+ duplicates: [],
82
+ // This is calculated in normalizeDuplication.
83
+ estimatedDuplicateBytes: 0,
84
+ };
85
+ groupedDuplication.set(nodeModuleKey, aggregatedData);
86
+ for (const { script, attributedSize } of data.duplicates) {
87
+ let duplicate = aggregatedData.duplicates.find(d => d.script === script);
88
+ if (!duplicate) {
89
+ duplicate = { script, attributedSize: 0 };
90
+ aggregatedData.duplicates.push(duplicate);
91
+ }
92
+ duplicate.attributedSize += attributedSize;
122
93
  }
123
94
  }
124
- // Now, all but the last mapping on each line will have 'lastColumnNumber' set to a number.
125
- return result;
95
+ return groupedDuplication;
96
+ }
97
+ /**
98
+ * Sort by estimated savings.
99
+ */
100
+ function sorted(duplication) {
101
+ return new Map([...duplication].sort((a, b) => b[1].estimatedDuplicateBytes - a[1].estimatedDuplicateBytes));
126
102
  }
127
103
  /**
128
- * Returns a @see ScriptDuplication for the given collection of script contents + source maps.
104
+ * Returns 2 @see ScriptDuplication for the given collection of script contents + source maps:
105
+ *
106
+ * 1. `duplication` keys correspond to authored files
107
+ * 2. `duplication` keys correspond to authored files, except all files within the same
108
+ * node_module package are aggregated under the same entry.
129
109
  */
130
110
  export function computeScriptDuplication(scriptsData) {
131
- const sizesMap = new Map();
132
- for (const script of scriptsData.scripts) {
133
- if (script.content && script.sourceMap) {
134
- sizesMap.set(script, computeGeneratedFileSizes(script));
135
- }
136
- }
137
111
  const sourceDatasMap = new Map();
138
112
  // Determine size of each `sources` entry.
139
- for (const [script, sizes] of sizesMap) {
140
- if (!script.sourceMap) {
113
+ for (const script of scriptsData.scripts) {
114
+ if (!script.content || !script.sourceMap) {
115
+ continue;
116
+ }
117
+ const sizes = Handlers.ModelHandlers.Scripts.getScriptGeneratedSizes(script);
118
+ if (!sizes) {
141
119
  continue;
142
120
  }
143
121
  if ('errorMessage' in sizes) {
@@ -172,8 +150,12 @@ export function computeScriptDuplication(scriptsData) {
172
150
  });
173
151
  }
174
152
  }
153
+ const duplicationGroupedByNodeModules = groupByNodeModules(duplication);
175
154
  normalizeDuplication(duplication);
176
- // Sort by estimated savings.
177
- return new Map([...duplication].sort((a, b) => b[1].estimatedDuplicateBytes - a[1].estimatedDuplicateBytes));
155
+ normalizeDuplication(duplicationGroupedByNodeModules);
156
+ return {
157
+ duplication: sorted(duplication),
158
+ duplicationGroupedByNodeModules: sorted(duplicationGroupedByNodeModules),
159
+ };
178
160
  }
179
161
  //# sourceMappingURL=ScriptDuplication.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ScriptDuplication.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/extras/ScriptDuplication.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2025 The Chromium Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\nimport type * as SDK from '../../../core/sdk/sdk.js';\nimport type * as Handlers from '../handlers/handlers.js';\n\n// Ignore modules smaller than an absolute threshold.\nconst ABSOLUTE_SIZE_THRESHOLD_BYTES = 1024 * 0.5;\n// Ignore modules smaller than a % size of largest copy of the module.\nconst RELATIVE_SIZE_THRESHOLD = 0.1;\n\ntype GeneratedFileSizes = {\n errorMessage: string,\n}|{files: Record<string, number>, unmappedBytes: number, totalBytes: number};\n\n/**\n * Using a script's contents and source map, attribute every generated byte to an authored source file.\n */\nexport function computeGeneratedFileSizes(script: Handlers.ModelHandlers.Scripts.Script): GeneratedFileSizes {\n if (!script.sourceMap) {\n throw new Error('expected source map');\n }\n\n const map = script.sourceMap;\n const content = script.content ?? '';\n const contentLength = content.length;\n const lines = content.split('\\n');\n const files: Record<string, number> = {};\n const totalBytes = contentLength;\n let unmappedBytes = totalBytes;\n\n const lastGeneratedColumnMap = computeLastGeneratedColumnMap(script.sourceMap);\n\n for (const mapping of map.mappings()) {\n const source = mapping.sourceURL;\n const lineNum = mapping.lineNumber;\n const colNum = mapping.columnNumber;\n const lastColNum = lastGeneratedColumnMap.get(mapping);\n\n // Webpack sometimes emits null mappings.\n // https://github.com/mozilla/source-map/pull/303\n if (!source) {\n continue;\n }\n\n // Lines and columns are zero-based indices. Visually, lines are shown as a 1-based index.\n\n const line = lines[lineNum];\n if (line === null || line === undefined) {\n const errorMessage = `${map.url()} mapping for line out of bounds: ${lineNum + 1}`;\n return {errorMessage};\n }\n\n if (colNum > line.length) {\n const errorMessage = `${map.url()} mapping for column out of bounds: ${lineNum + 1}:${colNum}`;\n return {errorMessage};\n }\n\n let mappingLength = 0;\n if (lastColNum !== undefined) {\n if (lastColNum > line.length) {\n const errorMessage = `${map.url()} mapping for last column out of bounds: ${lineNum + 1}:${lastColNum}`;\n return {errorMessage};\n }\n mappingLength = lastColNum - colNum;\n } else {\n // Add +1 to account for the newline.\n mappingLength = line.length - colNum + 1;\n }\n files[source] = (files[source] || 0) + mappingLength;\n unmappedBytes -= mappingLength;\n }\n\n return {\n files,\n unmappedBytes,\n totalBytes,\n };\n}\n\ninterface SourceData {\n source: string;\n resourceSize: number;\n}\n\nexport function normalizeSource(source: string): string {\n // Trim trailing question mark - b/c webpack.\n source = source.replace(/\\?$/, '');\n\n // Normalize paths for dependencies by only keeping everything after the last `node_modules`.\n const lastNodeModulesIndex = source.lastIndexOf('node_modules');\n if (lastNodeModulesIndex !== -1) {\n source = source.substring(lastNodeModulesIndex);\n }\n\n return source;\n}\n\nfunction shouldIgnoreSource(source: string): boolean {\n // Ignore bundle overhead.\n if (source.includes('webpack/bootstrap')) {\n return true;\n }\n if (source.includes('(webpack)/buildin')) {\n return true;\n }\n\n // Ignore webpack module shims, i.e. aliases of the form `module.exports = window.jQuery`\n if (source.includes('external ')) {\n return true;\n }\n\n return false;\n}\n\n/**\n * The key is a source map `sources` entry (these are URLs/file paths), but normalized\n * via `normalizeSource`.\n *\n * The value is an object with an entry for every script that has a source map which\n * denotes that this source was used, along with the estimated resource size it takes\n * up in the script.\n */\nexport type ScriptDuplication = Map<string, {\n /**\n * This is the sum of all (but one) `attributedSize` in `scripts`.\n *\n * One copy of this module is treated as the canonical version - the rest will\n * have non-zero `wastedBytes`. The canonical copy is the first entry of\n * `scripts`.\n *\n * In the case of all copies being the same version, all sizes are\n * equal and the selection doesn't matter (ignoring compression ratios). When\n * the copies are different versions, it does matter. Ideally the newest\n * version would be the canonical copy, but version information is not present.\n * Instead, size is used as a heuristic for latest version. This makes the\n * value here conserative in its estimation.\n */\n estimatedDuplicateBytes: number,\n duplicates: Array<{\n script: Handlers.ModelHandlers.Scripts.Script,\n /** The number of bytes in the script bundle that map back to this module. */\n attributedSize: number,\n }>,\n}>;\n\n/**\n * Sorts each array within @see ScriptDuplication by attributedSize, drops information\n * on sources that are too small, and calculates esimatedDuplicateBytes.\n */\nexport function normalizeDuplication(duplication: ScriptDuplication): void {\n for (const [key, data] of duplication) {\n // Sort by resource size.\n data.duplicates.sort((a, b) => b.attributedSize - a.attributedSize);\n\n // Ignore modules smaller than a % size of largest.\n if (data.duplicates.length > 1) {\n const largestResourceSize = data.duplicates[0].attributedSize;\n data.duplicates = data.duplicates.filter(duplicate => {\n const percentSize = duplicate.attributedSize / largestResourceSize;\n return percentSize >= RELATIVE_SIZE_THRESHOLD;\n });\n }\n\n // Ignore modules smaller than an absolute threshold.\n data.duplicates = data.duplicates.filter(duplicate => duplicate.attributedSize >= ABSOLUTE_SIZE_THRESHOLD_BYTES);\n\n // Delete any that now don't have multiple entries.\n if (data.duplicates.length <= 1) {\n duplication.delete(key);\n continue;\n }\n\n data.estimatedDuplicateBytes = data.duplicates.slice(1).reduce((acc, cur) => acc + cur.attributedSize, 0);\n }\n}\n\nfunction computeLastGeneratedColumnMap(map: SDK.SourceMap.SourceMap): Map<SDK.SourceMap.SourceMapEntry, number> {\n const result = new Map<SDK.SourceMap.SourceMapEntry, number>();\n\n const mappings = map.mappings();\n for (let i = 0; i < mappings.length - 1; i++) {\n const mapping = mappings[i];\n const nextMapping = mappings[i + 1];\n if (mapping.lineNumber === nextMapping.lineNumber) {\n result.set(mapping, nextMapping.columnNumber);\n }\n }\n\n // Now, all but the last mapping on each line will have 'lastColumnNumber' set to a number.\n return result;\n}\n\n/**\n * Returns a @see ScriptDuplication for the given collection of script contents + source maps.\n */\nexport function computeScriptDuplication(scriptsData: Handlers.ModelHandlers.Scripts.ScriptsData): ScriptDuplication {\n const sizesMap = new Map<Handlers.ModelHandlers.Scripts.Script, GeneratedFileSizes>();\n for (const script of scriptsData.scripts) {\n if (script.content && script.sourceMap) {\n sizesMap.set(script, computeGeneratedFileSizes(script));\n }\n }\n\n const sourceDatasMap = new Map<Handlers.ModelHandlers.Scripts.Script, SourceData[]>();\n\n // Determine size of each `sources` entry.\n for (const [script, sizes] of sizesMap) {\n if (!script.sourceMap) {\n continue;\n }\n\n if ('errorMessage' in sizes) {\n console.error(sizes.errorMessage);\n continue;\n }\n\n const sourceDataArray: SourceData[] = [];\n sourceDatasMap.set(script, sourceDataArray);\n\n const sources = script.sourceMap.sourceURLs();\n for (let i = 0; i < sources.length; i++) {\n if (shouldIgnoreSource(sources[i])) {\n continue;\n }\n\n const sourceSize = sizes.files[sources[i]];\n sourceDataArray.push({\n source: normalizeSource(sources[i]),\n resourceSize: sourceSize,\n });\n }\n }\n\n const duplication: ScriptDuplication = new Map();\n for (const [script, sourceDataArray] of sourceDatasMap) {\n for (const sourceData of sourceDataArray) {\n let data = duplication.get(sourceData.source);\n if (!data) {\n data = {estimatedDuplicateBytes: 0, duplicates: []};\n duplication.set(sourceData.source, data);\n }\n data.duplicates.push({\n script,\n attributedSize: sourceData.resourceSize,\n });\n }\n }\n\n normalizeDuplication(duplication);\n\n // Sort by estimated savings.\n return new Map([...duplication].sort((a, b) => b[1].estimatedDuplicateBytes - a[1].estimatedDuplicateBytes));\n}\n"]}
1
+ {"version":3,"file":"ScriptDuplication.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/extras/ScriptDuplication.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2025 The Chromium Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\nimport * as Handlers from '../handlers/handlers.js';\n\n// Ignore modules smaller than an absolute threshold.\nconst ABSOLUTE_SIZE_THRESHOLD_BYTES = 1024 * 0.5;\n// Ignore modules smaller than a % size of largest copy of the module.\nconst RELATIVE_SIZE_THRESHOLD = 0.1;\n\ninterface SourceData {\n source: string;\n resourceSize: number;\n}\n\nexport function normalizeSource(source: string): string {\n // Trim trailing question mark - b/c webpack.\n source = source.replace(/\\?$/, '');\n\n // Normalize paths for dependencies by only keeping everything after the last `node_modules`.\n const lastNodeModulesIndex = source.lastIndexOf('node_modules');\n if (lastNodeModulesIndex !== -1) {\n source = source.substring(lastNodeModulesIndex);\n }\n\n return source;\n}\n\nfunction shouldIgnoreSource(source: string): boolean {\n // Ignore bundle overhead.\n if (source.includes('webpack/bootstrap')) {\n return true;\n }\n if (source.includes('(webpack)/buildin')) {\n return true;\n }\n\n // Ignore webpack module shims, i.e. aliases of the form `module.exports = window.jQuery`\n if (source.includes('external ')) {\n return true;\n }\n\n return false;\n}\n\n/**\n * The key is a source map `sources` entry (these are URLs/file paths), but normalized\n * via `normalizeSource`.\n *\n * The value is an object with an entry for every script that has a source map which\n * denotes that this source was used, along with the estimated resource size it takes\n * up in the script.\n */\nexport type ScriptDuplication = Map<string, {\n /**\n * This is the sum of all (but one) `attributedSize` in `scripts`.\n *\n * One copy of this module is treated as the canonical version - the rest will\n * have non-zero `wastedBytes`. The canonical copy is the first entry of\n * `scripts`.\n *\n * In the case of all copies being the same version, all sizes are\n * equal and the selection doesn't matter (ignoring compression ratios). When\n * the copies are different versions, it does matter. Ideally the newest\n * version would be the canonical copy, but version information is not present.\n * Instead, size is used as a heuristic for latest version. This makes the\n * value here conserative in its estimation.\n */\n estimatedDuplicateBytes: number,\n duplicates: Array<{\n script: Handlers.ModelHandlers.Scripts.Script,\n /** The number of bytes in the script bundle that map back to this module. */\n attributedSize: number,\n }>,\n}>;\n\n/**\n * Sorts each array within @see ScriptDuplication by attributedSize, drops information\n * on sources that are too small, and calculates esimatedDuplicateBytes.\n */\nexport function normalizeDuplication(duplication: ScriptDuplication): void {\n for (const [key, data] of duplication) {\n // Sort by resource size.\n data.duplicates.sort((a, b) => b.attributedSize - a.attributedSize);\n\n // Ignore modules smaller than a % size of largest.\n if (data.duplicates.length > 1) {\n const largestResourceSize = data.duplicates[0].attributedSize;\n data.duplicates = data.duplicates.filter(duplicate => {\n const percentSize = duplicate.attributedSize / largestResourceSize;\n return percentSize >= RELATIVE_SIZE_THRESHOLD;\n });\n }\n\n // Ignore modules smaller than an absolute threshold.\n data.duplicates = data.duplicates.filter(duplicate => duplicate.attributedSize >= ABSOLUTE_SIZE_THRESHOLD_BYTES);\n\n // Delete any that now don't have multiple entries.\n if (data.duplicates.length <= 1) {\n duplication.delete(key);\n continue;\n }\n\n data.estimatedDuplicateBytes = data.duplicates.slice(1).reduce((acc, cur) => acc + cur.attributedSize, 0);\n }\n}\n\nfunction indexOfOrLength(haystack: string, needle: string, startPosition = 0): number {\n const index = haystack.indexOf(needle, startPosition);\n return index === -1 ? haystack.length : index;\n}\n\nexport function getNodeModuleName(source: string): string {\n const sourceSplit = source.split('node_modules/');\n source = sourceSplit[sourceSplit.length - 1];\n\n const indexFirstSlash = indexOfOrLength(source, '/');\n if (source[0] === '@') {\n return source.slice(0, indexOfOrLength(source, '/', indexFirstSlash + 1));\n }\n\n return source.slice(0, indexFirstSlash);\n}\n\nfunction groupByNodeModules(duplication: ScriptDuplication): ScriptDuplication {\n const groupedDuplication: ScriptDuplication = new Map();\n for (const [source, data] of duplication) {\n if (!source.includes('node_modules')) {\n groupedDuplication.set(source, data);\n continue;\n }\n\n const nodeModuleKey = 'node_modules/' + getNodeModuleName(source);\n const aggregatedData = groupedDuplication.get(nodeModuleKey) ?? {\n duplicates: [],\n // This is calculated in normalizeDuplication.\n estimatedDuplicateBytes: 0,\n };\n groupedDuplication.set(nodeModuleKey, aggregatedData);\n\n for (const {script, attributedSize} of data.duplicates) {\n let duplicate = aggregatedData.duplicates.find(d => d.script === script);\n if (!duplicate) {\n duplicate = {script, attributedSize: 0};\n aggregatedData.duplicates.push(duplicate);\n }\n duplicate.attributedSize += attributedSize;\n }\n }\n\n return groupedDuplication;\n}\n\n/**\n * Sort by estimated savings.\n */\nfunction sorted(duplication: ScriptDuplication): ScriptDuplication {\n return new Map([...duplication].sort((a, b) => b[1].estimatedDuplicateBytes - a[1].estimatedDuplicateBytes));\n}\n\n/**\n * Returns 2 @see ScriptDuplication for the given collection of script contents + source maps:\n *\n * 1. `duplication` keys correspond to authored files\n * 2. `duplication` keys correspond to authored files, except all files within the same\n * node_module package are aggregated under the same entry.\n */\nexport function computeScriptDuplication(scriptsData: Handlers.ModelHandlers.Scripts.ScriptsData):\n {duplication: ScriptDuplication, duplicationGroupedByNodeModules: ScriptDuplication} {\n const sourceDatasMap = new Map<Handlers.ModelHandlers.Scripts.Script, SourceData[]>();\n\n // Determine size of each `sources` entry.\n for (const script of scriptsData.scripts) {\n if (!script.content || !script.sourceMap) {\n continue;\n }\n\n const sizes = Handlers.ModelHandlers.Scripts.getScriptGeneratedSizes(script);\n if (!sizes) {\n continue;\n }\n\n if ('errorMessage' in sizes) {\n console.error(sizes.errorMessage);\n continue;\n }\n\n const sourceDataArray: SourceData[] = [];\n sourceDatasMap.set(script, sourceDataArray);\n\n const sources = script.sourceMap.sourceURLs();\n for (let i = 0; i < sources.length; i++) {\n if (shouldIgnoreSource(sources[i])) {\n continue;\n }\n\n const sourceSize = sizes.files[sources[i]];\n sourceDataArray.push({\n source: normalizeSource(sources[i]),\n resourceSize: sourceSize,\n });\n }\n }\n\n const duplication: ScriptDuplication = new Map();\n for (const [script, sourceDataArray] of sourceDatasMap) {\n for (const sourceData of sourceDataArray) {\n let data = duplication.get(sourceData.source);\n if (!data) {\n data = {estimatedDuplicateBytes: 0, duplicates: []};\n duplication.set(sourceData.source, data);\n }\n data.duplicates.push({\n script,\n attributedSize: sourceData.resourceSize,\n });\n }\n }\n\n const duplicationGroupedByNodeModules = groupByNodeModules(duplication);\n\n normalizeDuplication(duplication);\n normalizeDuplication(duplicationGroupedByNodeModules);\n\n return {\n duplication: sorted(duplication),\n duplicationGroupedByNodeModules: sorted(duplicationGroupedByNodeModules),\n };\n}\n"]}
@@ -1,10 +1,12 @@
1
- import * as ThirdPartyWeb from '../../../third_party/third-party-web/third-party-web.js';
2
- import * as Handlers from '../handlers/handlers.js';
1
+ import type * as ThirdPartyWeb from '../../../third_party/third-party-web/third-party-web.js';
2
+ import type * as Handlers from '../handlers/handlers.js';
3
3
  import * as Types from '../types/types.js';
4
4
  export type Entity = typeof ThirdPartyWeb.ThirdPartyWeb.entities[number];
5
5
  export interface Summary {
6
6
  transferSize: number;
7
- mainThreadTime: Types.Timing.Micro;
7
+ mainThreadTime: Types.Timing.Milli;
8
+ relatedEvents?: Types.Events.Event[];
9
+ entity: Entity;
8
10
  }
9
11
  export interface ThirdPartySummary {
10
12
  byEntity: Map<Entity, Summary>;
@@ -16,13 +18,4 @@ export interface ThirdPartySummary {
16
18
  /**
17
19
  * @param networkRequests Won't be filtered by trace bounds, so callers should ensure it is filtered.
18
20
  */
19
- export declare function summarizeThirdParties(parsedTrace: Handlers.Types.ParsedTrace, traceBounds: Types.Timing.TraceWindowMicro, networkRequests: Types.Events.SyntheticNetworkRequest[]): ThirdPartySummary;
20
- /**
21
- * Note: unlike summarizeThirdParties, this does not calculate mainThreadTime. The reason is that it is not
22
- * needed for its one use case, and when dragging the trace bounds it takes a long time to calculate.
23
- * If it is ever needed, we need to make getSelfTimeByUrl (see deleted code/blame) much faster (cache + bucket?).
24
- */
25
- export declare function getSummariesAndEntitiesWithMapping(parsedTrace: Handlers.Types.ParsedTrace, traceBounds: Types.Timing.TraceWindowMicro, entityMapping: Handlers.Helpers.EntityMappings): {
26
- summaries: ThirdPartySummary;
27
- entityByEvent: Map<Types.Events.Event, Handlers.Helpers.Entity>;
28
- };
21
+ export declare function summarizeThirdParties(parsedTrace: Handlers.Types.ParsedTrace, traceBounds: Types.Timing.TraceWindowMicro): Summary[];
@@ -1,153 +1,89 @@
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 ThirdPartyWeb from '../../../third_party/third-party-web/third-party-web.js';
5
- import * as Handlers from '../handlers/handlers.js';
6
4
  import * as Helpers from '../helpers/helpers.js';
7
5
  import * as Types from '../types/types.js';
8
- function getOrMakeSummaryByEntity(thirdPartySummary, event, url) {
9
- const entity = ThirdPartyWeb.ThirdPartyWeb.getEntity(url) ??
10
- Handlers.Helpers.makeUpEntity(thirdPartySummary.madeUpEntityCache, url);
11
- if (!entity) {
12
- return null;
13
- }
14
- const urls = thirdPartySummary.urlsByEntity.get(entity) ?? new Set();
15
- urls.add(url);
16
- thirdPartySummary.urlsByEntity.set(entity, urls);
17
- const events = thirdPartySummary.eventsByEntity.get(entity) ?? [];
18
- events.push(event);
19
- thirdPartySummary.eventsByEntity.set(entity, events);
20
- let summary = thirdPartySummary.byEntity.get(entity);
21
- if (summary) {
22
- return summary;
23
- }
24
- summary = { transferSize: 0, mainThreadTime: Types.Timing.Micro(0) };
25
- thirdPartySummary.byEntity.set(entity, summary);
26
- return summary;
27
- }
28
- function getOrMakeSummaryByURL(thirdPartySummary, url) {
29
- let summary = thirdPartySummary.byUrl.get(url);
30
- if (summary) {
31
- return summary;
32
- }
33
- summary = { transferSize: 0, mainThreadTime: Types.Timing.Micro(0) };
34
- thirdPartySummary.byUrl.set(url, summary);
35
- return summary;
36
- }
37
- // TODO: Remove and use the the BottomUpRootNode defined in ThirdPartyTreeView instead.
38
- function collectMainThreadActivity(thirdPartySummary, parsedTrace, bounds) {
39
- for (const process of parsedTrace.Renderer.processes.values()) {
40
- if (!process.isOnMainFrame) {
41
- continue;
42
- }
43
- for (const thread of process.threads.values()) {
44
- if (thread.name === 'CrRendererMain') {
45
- if (!thread.tree) {
46
- break;
47
- }
48
- for (const event of thread.entries) {
49
- if (!Helpers.Timing.eventIsInBounds(event, bounds)) {
50
- continue;
51
- }
52
- const node = parsedTrace.Renderer.entryToNode.get(event);
53
- if (!node || !node.selfTime) {
54
- continue;
55
- }
56
- const url = Handlers.Helpers.getNonResolvedURL(event, parsedTrace);
57
- if (!url) {
58
- continue;
59
- }
60
- let summary = getOrMakeSummaryByEntity(thirdPartySummary, event, url);
61
- if (summary) {
62
- summary.mainThreadTime = (summary.mainThreadTime + node.selfTime);
63
- }
64
- summary = getOrMakeSummaryByURL(thirdPartySummary, url);
65
- if (summary) {
66
- summary.mainThreadTime = (summary.mainThreadTime + node.selfTime);
67
- }
68
- }
69
- }
70
- }
71
- }
72
- }
73
- function collectNetworkActivity(thirdPartySummary, requests) {
74
- for (const request of requests) {
75
- const url = request.args.data.url;
76
- let summary = getOrMakeSummaryByEntity(thirdPartySummary, request, url);
77
- if (summary) {
78
- summary.transferSize += request.args.data.encodedDataLength;
79
- }
80
- summary = getOrMakeSummaryByURL(thirdPartySummary, url);
81
- if (summary) {
82
- summary.transferSize += request.args.data.encodedDataLength;
83
- }
6
+ import * as TraceFilter from './TraceFilter.js';
7
+ import * as TraceTree from './TraceTree.js';
8
+ /**
9
+ *
10
+ * Returns Main frame main thread events.
11
+ * These events are inline with the ones used by selectedEvents() of TimelineTreeViews
12
+ */
13
+ function collectMainThreadActivity(parsedTrace) {
14
+ // TODO: Note b/402658800 could be an issue here.
15
+ const mainFrameMainThread = parsedTrace.Renderer.processes.values()
16
+ .find(p => {
17
+ const url = p.url ?? '';
18
+ // Frame url checked a la CompatibilityTracksAppenders's addThreadAppenders
19
+ return p.isOnMainFrame && !url.startsWith('about:') && !url.startsWith('chrome:');
20
+ })
21
+ ?.threads.values()
22
+ .find(t => t.name === 'CrRendererMain');
23
+ if (!mainFrameMainThread) {
24
+ return [];
84
25
  }
26
+ return mainFrameMainThread.entries;
85
27
  }
86
28
  /**
87
29
  * @param networkRequests Won't be filtered by trace bounds, so callers should ensure it is filtered.
88
30
  */
89
- export function summarizeThirdParties(parsedTrace, traceBounds, networkRequests) {
90
- const thirdPartySummary = {
91
- byEntity: new Map(),
92
- byUrl: new Map(),
93
- urlsByEntity: new Map(),
94
- eventsByEntity: new Map(),
95
- madeUpEntityCache: new Map(),
96
- };
97
- collectMainThreadActivity(thirdPartySummary, parsedTrace, traceBounds);
98
- collectNetworkActivity(thirdPartySummary, networkRequests);
99
- return thirdPartySummary;
31
+ export function summarizeThirdParties(parsedTrace, traceBounds) {
32
+ const mainThreadEvents = collectMainThreadActivity(parsedTrace).sort(Helpers.Trace.eventTimeComparator);
33
+ const node = getBottomUpTree(mainThreadEvents, parsedTrace, traceBounds);
34
+ const summaries = summarizeBottomUp(node, parsedTrace);
35
+ return summaries;
100
36
  }
101
- function getSummaryMapWithMapping(events, entityByEvent, eventsByEntity) {
102
- const byEvent = new Map();
103
- const byEntity = new Map();
104
- const defaultSummary = { transferSize: 0, mainThreadTime: Types.Timing.Micro(0) };
105
- for (const event of events) {
106
- const urlSummary = byEvent.get(event) || { ...defaultSummary };
107
- if (Types.Events.isSyntheticNetworkRequest(event)) {
108
- urlSummary.transferSize += event.args.data.encodedDataLength;
37
+ function summarizeBottomUp(thirdPartyBottomUp, parsedTrace) {
38
+ const summaryForEntity = new Map();
39
+ const summaries = [];
40
+ // Our top nodes are the 3P entities.
41
+ // Tree nodes are built lazily, .children() is essential, it triggers the
42
+ // construction of the root node's child nodes.
43
+ const topNodes = [...thirdPartyBottomUp.children().values()].flat();
44
+ for (const node of topNodes) {
45
+ if (node.id === '') {
46
+ continue;
109
47
  }
110
- byEvent.set(event, urlSummary);
111
- }
112
- // Map each request's stat to a particular entity.
113
- for (const [request, requestSummary] of byEvent.entries()) {
114
- const entity = entityByEvent.get(request);
48
+ const entity = parsedTrace.Renderer.entityMappings.entityByEvent.get(node.event);
115
49
  if (!entity) {
116
- byEvent.delete(request);
117
50
  continue;
118
51
  }
119
- const entitySummary = byEntity.get(entity) || { ...defaultSummary };
120
- entitySummary.transferSize += requestSummary.transferSize;
121
- byEntity.set(entity, entitySummary);
52
+ const summary = {
53
+ transferSize: node.transferSize,
54
+ mainThreadTime: Types.Timing.Milli(node.selfTime),
55
+ // Lets use the mapper events as our source of events, since we use the main thread to construct
56
+ // the bottom up tree. The mapper will give us all related events.
57
+ relatedEvents: parsedTrace.Renderer.entityMappings.eventsByEntity.get(entity) ?? [],
58
+ entity,
59
+ };
60
+ summaryForEntity.set(entity, summary);
61
+ summaries.push(summary);
122
62
  }
123
- return { byEntity, eventsByEntity, madeUpEntityCache: new Map(), byUrl: new Map(), urlsByEntity: new Map() };
63
+ return summaries;
124
64
  }
125
- // TODO(crbug.com/352244718): Remove or refactor to use summarizeThirdParties/collectMainThreadActivity/etc.
126
- /**
127
- * Note: unlike summarizeThirdParties, this does not calculate mainThreadTime. The reason is that it is not
128
- * needed for its one use case, and when dragging the trace bounds it takes a long time to calculate.
129
- * If it is ever needed, we need to make getSelfTimeByUrl (see deleted code/blame) much faster (cache + bucket?).
130
- */
131
- export function getSummariesAndEntitiesWithMapping(parsedTrace, traceBounds, entityMapping) {
132
- const entityByEvent = new Map(entityMapping.entityByEvent);
133
- const eventsByEntity = new Map(entityMapping.eventsByEntity);
134
- // Consider events only in bounds.
135
- const entityByEventArr = Array.from(entityByEvent.entries());
136
- const filteredEntries = entityByEventArr.filter(([event]) => {
137
- return Helpers.Timing.eventIsInBounds(event, traceBounds);
138
- });
139
- const entityByEventFiltered = new Map(filteredEntries);
140
- // Consider events only in bounds.
141
- const eventsByEntityArr = Array.from(eventsByEntity.entries());
142
- const filtered = eventsByEntityArr.filter(([, events]) => {
143
- events.map(event => {
144
- return Helpers.Timing.eventIsInBounds(event, traceBounds);
145
- });
146
- return events.length > 0;
65
+ function getBottomUpTree(mainThreadEvents, parsedTrace, tracebounds) {
66
+ const mappings = parsedTrace.Renderer.entityMappings;
67
+ const groupingFunction = (event) => {
68
+ const entity = mappings?.entityByEvent.get(event);
69
+ return entity?.name ?? '';
70
+ };
71
+ // Use the same filtering as front_end/panels/timeline/TimelineTreeView.ts.
72
+ const visibleEvents = Helpers.Trace.VISIBLE_TRACE_EVENT_TYPES.values().toArray();
73
+ const filter = new TraceFilter.VisibleEventsFilter(visibleEvents.concat(["SyntheticNetworkRequest" /* Types.Events.Name.SYNTHETIC_NETWORK_REQUEST */]));
74
+ // The bottom up root node handles all the "in Tracebounds" checks we need for the insight.
75
+ const startTime = Helpers.Timing.microToMilli(tracebounds.min);
76
+ const endTime = Helpers.Timing.microToMilli(tracebounds.max);
77
+ const node = new TraceTree.BottomUpRootNode(mainThreadEvents, {
78
+ textFilter: new TraceFilter.ExclusiveNameFilter([]),
79
+ filters: [filter],
80
+ startTime,
81
+ endTime,
82
+ eventGroupIdCallback: groupingFunction,
83
+ calculateTransferSize: true,
84
+ // Ensure we group by 3P alongside eventID for correct 3P grouping.
85
+ forceGroupIdCallback: true,
147
86
  });
148
- const eventsByEntityFiltered = new Map(filtered);
149
- const allEvents = Array.from(entityByEvent.keys());
150
- const summaries = getSummaryMapWithMapping(allEvents, entityByEventFiltered, eventsByEntityFiltered);
151
- return { summaries, entityByEvent: entityByEventFiltered };
87
+ return node;
152
88
  }
153
89
  //# sourceMappingURL=ThirdParties.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ThirdParties.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/extras/ThirdParties.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.\n\nimport * as ThirdPartyWeb from '../../../third_party/third-party-web/third-party-web.js';\nimport * as Handlers from '../handlers/handlers.js';\nimport * as Helpers from '../helpers/helpers.js';\nimport * as Types from '../types/types.js';\n\nexport type Entity = typeof ThirdPartyWeb.ThirdPartyWeb.entities[number];\n\nexport interface Summary {\n transferSize: number;\n mainThreadTime: Types.Timing.Micro;\n}\n\nexport interface ThirdPartySummary {\n byEntity: Map<Entity, Summary>;\n byUrl: Map<string, Summary>;\n urlsByEntity: Map<Entity, Set<string>>;\n eventsByEntity: Map<Entity, Types.Events.Event[]>;\n madeUpEntityCache: Map<string, Entity>;\n}\n\nfunction getOrMakeSummaryByEntity(\n thirdPartySummary: ThirdPartySummary, event: Types.Events.Event, url: string): Summary|null {\n const entity = ThirdPartyWeb.ThirdPartyWeb.getEntity(url) ??\n Handlers.Helpers.makeUpEntity(thirdPartySummary.madeUpEntityCache, url);\n if (!entity) {\n return null;\n }\n\n const urls = thirdPartySummary.urlsByEntity.get(entity) ?? new Set();\n urls.add(url);\n thirdPartySummary.urlsByEntity.set(entity, urls);\n\n const events = thirdPartySummary.eventsByEntity.get(entity) ?? [];\n events.push(event);\n thirdPartySummary.eventsByEntity.set(entity, events);\n\n let summary = thirdPartySummary.byEntity.get(entity);\n if (summary) {\n return summary;\n }\n\n summary = {transferSize: 0, mainThreadTime: Types.Timing.Micro(0)};\n thirdPartySummary.byEntity.set(entity, summary);\n return summary;\n}\n\nfunction getOrMakeSummaryByURL(thirdPartySummary: ThirdPartySummary, url: string): Summary|null {\n let summary = thirdPartySummary.byUrl.get(url);\n if (summary) {\n return summary;\n }\n\n summary = {transferSize: 0, mainThreadTime: Types.Timing.Micro(0)};\n thirdPartySummary.byUrl.set(url, summary);\n return summary;\n}\n\n// TODO: Remove and use the the BottomUpRootNode defined in ThirdPartyTreeView instead.\nfunction collectMainThreadActivity(\n thirdPartySummary: ThirdPartySummary, parsedTrace: Handlers.Types.ParsedTrace,\n bounds: Types.Timing.TraceWindowMicro): void {\n for (const process of parsedTrace.Renderer.processes.values()) {\n if (!process.isOnMainFrame) {\n continue;\n }\n\n for (const thread of process.threads.values()) {\n if (thread.name === 'CrRendererMain') {\n if (!thread.tree) {\n break;\n }\n\n for (const event of thread.entries) {\n if (!Helpers.Timing.eventIsInBounds(event, bounds)) {\n continue;\n }\n\n const node = parsedTrace.Renderer.entryToNode.get(event);\n if (!node || !node.selfTime) {\n continue;\n }\n\n const url = Handlers.Helpers.getNonResolvedURL(event, parsedTrace);\n if (!url) {\n continue;\n }\n\n let summary = getOrMakeSummaryByEntity(thirdPartySummary, event, url);\n if (summary) {\n summary.mainThreadTime = (summary.mainThreadTime + node.selfTime) as Types.Timing.Micro;\n }\n\n summary = getOrMakeSummaryByURL(thirdPartySummary, url);\n if (summary) {\n summary.mainThreadTime = (summary.mainThreadTime + node.selfTime) as Types.Timing.Micro;\n }\n }\n }\n }\n }\n}\n\nfunction collectNetworkActivity(\n thirdPartySummary: ThirdPartySummary, requests: Types.Events.SyntheticNetworkRequest[]): void {\n for (const request of requests) {\n const url = request.args.data.url;\n\n let summary = getOrMakeSummaryByEntity(thirdPartySummary, request, url);\n if (summary) {\n summary.transferSize += request.args.data.encodedDataLength;\n }\n\n summary = getOrMakeSummaryByURL(thirdPartySummary, url);\n if (summary) {\n summary.transferSize += request.args.data.encodedDataLength;\n }\n }\n}\n\n/**\n * @param networkRequests Won't be filtered by trace bounds, so callers should ensure it is filtered.\n */\nexport function summarizeThirdParties(\n parsedTrace: Handlers.Types.ParsedTrace, traceBounds: Types.Timing.TraceWindowMicro,\n networkRequests: Types.Events.SyntheticNetworkRequest[]): ThirdPartySummary {\n const thirdPartySummary: ThirdPartySummary = {\n byEntity: new Map(),\n byUrl: new Map(),\n urlsByEntity: new Map(),\n eventsByEntity: new Map(),\n madeUpEntityCache: new Map(),\n };\n\n collectMainThreadActivity(thirdPartySummary, parsedTrace, traceBounds);\n collectNetworkActivity(thirdPartySummary, networkRequests);\n\n return thirdPartySummary;\n}\n\nfunction getSummaryMapWithMapping(\n events: Types.Events.Event[], entityByEvent: Map<Types.Events.Event, Handlers.Helpers.Entity>,\n eventsByEntity: Map<Handlers.Helpers.Entity, Types.Events.Event[]>): ThirdPartySummary {\n const byEvent = new Map<Types.Events.Event, Summary>();\n const byEntity = new Map<Handlers.Helpers.Entity, Summary>();\n const defaultSummary: Summary = {transferSize: 0, mainThreadTime: Types.Timing.Micro(0)};\n\n for (const event of events) {\n const urlSummary = byEvent.get(event) || {...defaultSummary};\n if (Types.Events.isSyntheticNetworkRequest(event)) {\n urlSummary.transferSize += event.args.data.encodedDataLength;\n }\n byEvent.set(event, urlSummary);\n }\n\n // Map each request's stat to a particular entity.\n for (const [request, requestSummary] of byEvent.entries()) {\n const entity = entityByEvent.get(request);\n if (!entity) {\n byEvent.delete(request);\n continue;\n }\n\n const entitySummary = byEntity.get(entity) || {...defaultSummary};\n entitySummary.transferSize += requestSummary.transferSize;\n byEntity.set(entity, entitySummary);\n }\n\n return {byEntity, eventsByEntity, madeUpEntityCache: new Map(), byUrl: new Map(), urlsByEntity: new Map()};\n}\n\n// TODO(crbug.com/352244718): Remove or refactor to use summarizeThirdParties/collectMainThreadActivity/etc.\n/**\n * Note: unlike summarizeThirdParties, this does not calculate mainThreadTime. The reason is that it is not\n * needed for its one use case, and when dragging the trace bounds it takes a long time to calculate.\n * If it is ever needed, we need to make getSelfTimeByUrl (see deleted code/blame) much faster (cache + bucket?).\n */\nexport function getSummariesAndEntitiesWithMapping(\n parsedTrace: Handlers.Types.ParsedTrace, traceBounds: Types.Timing.TraceWindowMicro,\n entityMapping: Handlers.Helpers.EntityMappings): {\n summaries: ThirdPartySummary,\n entityByEvent: Map<Types.Events.Event, Handlers.Helpers.Entity>,\n} {\n const entityByEvent = new Map(entityMapping.entityByEvent);\n const eventsByEntity = new Map(entityMapping.eventsByEntity);\n\n // Consider events only in bounds.\n const entityByEventArr = Array.from(entityByEvent.entries());\n const filteredEntries = entityByEventArr.filter(([event]) => {\n return Helpers.Timing.eventIsInBounds(event, traceBounds);\n });\n const entityByEventFiltered = new Map(filteredEntries);\n\n // Consider events only in bounds.\n const eventsByEntityArr = Array.from(eventsByEntity.entries());\n const filtered = eventsByEntityArr.filter(([, events]) => {\n events.map(event => {\n return Helpers.Timing.eventIsInBounds(event, traceBounds);\n });\n return events.length > 0;\n });\n const eventsByEntityFiltered = new Map(filtered);\n\n const allEvents = Array.from(entityByEvent.keys());\n const summaries = getSummaryMapWithMapping(allEvents, entityByEventFiltered, eventsByEntityFiltered);\n\n return {summaries, entityByEvent: entityByEventFiltered};\n}\n"]}
1
+ {"version":3,"file":"ThirdParties.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/extras/ThirdParties.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.\n\nimport type * as ThirdPartyWeb from '../../../third_party/third-party-web/third-party-web.js';\nimport type * as Handlers from '../handlers/handlers.js';\nimport * as Helpers from '../helpers/helpers.js';\nimport * as Types from '../types/types.js';\n\nimport * as TraceFilter from './TraceFilter.js';\nimport * as TraceTree from './TraceTree.js';\n\nexport type Entity = typeof ThirdPartyWeb.ThirdPartyWeb.entities[number];\n\nexport interface Summary {\n transferSize: number;\n mainThreadTime: Types.Timing.Milli;\n relatedEvents?: Types.Events.Event[];\n entity: Entity;\n}\n\nexport interface ThirdPartySummary {\n byEntity: Map<Entity, Summary>;\n byUrl: Map<string, Summary>;\n urlsByEntity: Map<Entity, Set<string>>;\n eventsByEntity: Map<Entity, Types.Events.Event[]>;\n madeUpEntityCache: Map<string, Entity>;\n}\n\n/**\n *\n * Returns Main frame main thread events.\n * These events are inline with the ones used by selectedEvents() of TimelineTreeViews\n */\nfunction collectMainThreadActivity(parsedTrace: Handlers.Types.ParsedTrace): Types.Events.Event[] {\n // TODO: Note b/402658800 could be an issue here.\n const mainFrameMainThread = parsedTrace.Renderer.processes.values()\n .find(p => {\n const url = p.url ?? '';\n // Frame url checked a la CompatibilityTracksAppenders's addThreadAppenders\n return p.isOnMainFrame && !url.startsWith('about:') && !url.startsWith('chrome:');\n })\n ?.threads.values()\n .find(t => t.name === 'CrRendererMain');\n\n if (!mainFrameMainThread) {\n return [];\n }\n\n return mainFrameMainThread.entries;\n}\n\n/**\n * @param networkRequests Won't be filtered by trace bounds, so callers should ensure it is filtered.\n */\nexport function summarizeThirdParties(\n parsedTrace: Handlers.Types.ParsedTrace, traceBounds: Types.Timing.TraceWindowMicro): Summary[] {\n const mainThreadEvents = collectMainThreadActivity(parsedTrace).sort(Helpers.Trace.eventTimeComparator);\n const node = getBottomUpTree(mainThreadEvents, parsedTrace, traceBounds);\n const summaries = summarizeBottomUp(node, parsedTrace);\n\n return summaries;\n}\n\nfunction summarizeBottomUp(\n thirdPartyBottomUp: TraceTree.BottomUpRootNode, parsedTrace: Handlers.Types.ParsedTrace): Summary[] {\n const summaryForEntity: Map<Entity, Summary> = new Map<Entity, Summary>();\n const summaries: Summary[] = [];\n\n // Our top nodes are the 3P entities.\n // Tree nodes are built lazily, .children() is essential, it triggers the\n // construction of the root node's child nodes.\n const topNodes = [...thirdPartyBottomUp.children().values()].flat();\n for (const node of topNodes) {\n if (node.id === '') {\n continue;\n }\n const entity = parsedTrace.Renderer.entityMappings.entityByEvent.get(node.event);\n if (!entity) {\n continue;\n }\n\n const summary: Summary = {\n transferSize: node.transferSize,\n mainThreadTime: Types.Timing.Milli(node.selfTime),\n // Lets use the mapper events as our source of events, since we use the main thread to construct\n // the bottom up tree. The mapper will give us all related events.\n relatedEvents: parsedTrace.Renderer.entityMappings.eventsByEntity.get(entity) ?? [],\n entity,\n };\n summaryForEntity.set(entity, summary);\n summaries.push(summary);\n }\n return summaries;\n}\n\nfunction getBottomUpTree(\n mainThreadEvents: Types.Events.Event[], parsedTrace: Handlers.Types.ParsedTrace,\n tracebounds: Types.Timing.TraceWindowMicro): TraceTree.BottomUpRootNode {\n const mappings = parsedTrace.Renderer.entityMappings;\n\n const groupingFunction = (event: Types.Events.Event): string => {\n const entity = mappings?.entityByEvent.get(event);\n return entity?.name ?? '';\n };\n // Use the same filtering as front_end/panels/timeline/TimelineTreeView.ts.\n const visibleEvents = Helpers.Trace.VISIBLE_TRACE_EVENT_TYPES.values().toArray();\n const filter =\n new TraceFilter.VisibleEventsFilter(visibleEvents.concat([Types.Events.Name.SYNTHETIC_NETWORK_REQUEST]));\n\n // The bottom up root node handles all the \"in Tracebounds\" checks we need for the insight.\n const startTime = Helpers.Timing.microToMilli(tracebounds.min);\n const endTime = Helpers.Timing.microToMilli(tracebounds.max);\n const node = new TraceTree.BottomUpRootNode(mainThreadEvents, {\n textFilter: new TraceFilter.ExclusiveNameFilter([]),\n filters: [filter],\n startTime,\n endTime,\n eventGroupIdCallback: groupingFunction,\n calculateTransferSize: true,\n // Ensure we group by 3P alongside eventID for correct 3P grouping.\n forceGroupIdCallback: true,\n }) as TraceTree.BottomUpRootNode;\n return node;\n}\n"]}
@@ -67,21 +67,33 @@ export declare class BottomUpRootNode extends Node {
67
67
  readonly filter: (e: Types.Events.Event) => boolean;
68
68
  readonly startTime: Types.Timing.Milli;
69
69
  readonly endTime: Types.Timing.Milli;
70
- private eventGroupIdCallback;
71
70
  totalTime: number;
71
+ eventGroupIdCallback: ((arg0: Types.Events.Event) => string) | null | undefined;
72
72
  private calculateTransferSize?;
73
- constructor(events: Types.Events.Event[], { textFilter, filters, startTime, endTime, eventGroupIdCallback, calculateTransferSize, }: {
73
+ private forceGroupIdCallback?;
74
+ constructor(events: Types.Events.Event[], { textFilter, filters, startTime, endTime, eventGroupIdCallback, calculateTransferSize, forceGroupIdCallback, }: {
74
75
  textFilter: TraceFilter;
75
76
  filters: readonly TraceFilter[];
76
77
  startTime: Types.Timing.Milli;
77
78
  endTime: Types.Timing.Milli;
78
79
  eventGroupIdCallback?: ((arg0: Types.Events.Event) => string) | null;
79
80
  calculateTransferSize?: boolean;
81
+ /**
82
+ * This forces using `eventGroupIdCallback` in combination with generateEventID
83
+ * to generate the ID of the node.
84
+ *
85
+ * This is used in the ThirdPartyTreeView and BottomUpTreeView, where we want to group all events
86
+ * related to a specific 3P entity together, regardless of the specific event name/type.
87
+ * There are cases where events under the same event name belong to different entities. But, because
88
+ * they get grouped first by event name/type, it throws off the 3P groupBy - grouping events of different
89
+ * 3P entities together.
90
+ */
91
+ forceGroupIdCallback?: boolean;
80
92
  });
81
93
  hasChildren(): boolean;
82
94
  filterChildren(children: ChildrenCache): ChildrenCache;
83
95
  children(): ChildrenCache;
84
- private ungrouppedTopNodes;
96
+ private ungroupedTopNodes;
85
97
  private grouppedTopNodes;
86
98
  }
87
99
  export declare class GroupNode extends Node {