@paulirish/trace_engine 0.0.23 → 0.0.24

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 (51) hide show
  1. package/generated/protocol.d.ts +48 -2
  2. package/models/trace/EntriesFilter.d.ts +14 -13
  3. package/models/trace/EntriesFilter.js +40 -38
  4. package/models/trace/EntriesFilter.js.map +1 -1
  5. package/models/trace/ModelImpl.d.ts +6 -4
  6. package/models/trace/ModelImpl.js +13 -4
  7. package/models/trace/ModelImpl.js.map +1 -1
  8. package/models/trace/Processor.js +6 -1
  9. package/models/trace/Processor.js.map +1 -1
  10. package/models/trace/extras/FetchNodes.js +1 -1
  11. package/models/trace/extras/FetchNodes.js.map +1 -1
  12. package/models/trace/extras/URLForEntry.d.ts +4 -0
  13. package/models/trace/extras/URLForEntry.js +35 -0
  14. package/models/trace/extras/URLForEntry.js.map +1 -0
  15. package/models/trace/extras/extras-tsconfig.json +1 -0
  16. package/models/trace/extras/extras.js.map +1 -1
  17. package/models/trace/handlers/AuctionWorkletsHandler.js +4 -2
  18. package/models/trace/handlers/AuctionWorkletsHandler.js.map +1 -1
  19. package/models/trace/handlers/InitiatorsHandler.js +1 -12
  20. package/models/trace/handlers/InitiatorsHandler.js.map +1 -1
  21. package/models/trace/handlers/LayoutShiftsHandler.d.ts +1 -0
  22. package/models/trace/handlers/LayoutShiftsHandler.js +10 -2
  23. package/models/trace/handlers/LayoutShiftsHandler.js.map +1 -1
  24. package/models/trace/handlers/NetworkRequestsHandler.js +4 -3
  25. package/models/trace/handlers/NetworkRequestsHandler.js.map +1 -1
  26. package/models/trace/handlers/RendererHandler.d.ts +1 -0
  27. package/models/trace/handlers/RendererHandler.js +2 -0
  28. package/models/trace/handlers/RendererHandler.js.map +1 -1
  29. package/models/trace/handlers/ScreenshotsHandler.js +3 -2
  30. package/models/trace/handlers/ScreenshotsHandler.js.map +1 -1
  31. package/models/trace/handlers/UserInteractionsHandler.js +3 -2
  32. package/models/trace/handlers/UserInteractionsHandler.js.map +1 -1
  33. package/models/trace/helpers/SyntheticEvents.d.ts +30 -0
  34. package/models/trace/helpers/SyntheticEvents.js +87 -0
  35. package/models/trace/helpers/SyntheticEvents.js.map +1 -0
  36. package/models/trace/helpers/Trace.js +4 -2
  37. package/models/trace/helpers/Trace.js.map +1 -1
  38. package/models/trace/helpers/helpers-tsconfig.json +1 -0
  39. package/models/trace/helpers/helpers.d.ts +1 -0
  40. package/models/trace/helpers/helpers.js +1 -0
  41. package/models/trace/helpers/helpers.js.map +1 -1
  42. package/models/trace/insights/CumulativeLayoutShift.d.ts +7 -2
  43. package/models/trace/insights/CumulativeLayoutShift.js +130 -2
  44. package/models/trace/insights/CumulativeLayoutShift.js.map +1 -1
  45. package/models/trace/types/File.d.ts +11 -6
  46. package/models/trace/types/File.js +0 -3
  47. package/models/trace/types/File.js.map +1 -1
  48. package/models/trace/types/TraceEvents.d.ts +85 -12
  49. package/models/trace/types/TraceEvents.js +27 -3
  50. package/models/trace/types/TraceEvents.js.map +1 -1
  51. package/package.json +1 -1
@@ -1,9 +1,11 @@
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 Helpers from '../helpers/helpers.js';
6
+ import * as Types from '../types/types.js';
5
7
  export function deps() {
6
- return ['Meta', 'Animations'];
8
+ return ['Meta', 'Animations', 'LayoutShifts', 'NetworkRequests'];
7
9
  }
8
10
  /**
9
11
  * Each failure reason is represented by a bit flag. The bit shift operator '<<' is used to define
@@ -37,6 +39,13 @@ const ACTIONABLE_FAILURE_REASONS = [
37
39
  failure: "UNSUPPORTED_TIMING_PARAMS" /* AnimationFailureReasons.UNSUPPORTED_TIMING_PARAMS */,
38
40
  },
39
41
  ];
42
+ // 500ms window.
43
+ // Use this window to consider events and requests that may have caused a layout shift.
44
+ const INVALIDATION_WINDOW = Helpers.Timing.secondsToMicroseconds(Types.Timing.Seconds(0.5));
45
+ function isInInvalidationWindow(event, targetEvent) {
46
+ const eventEnd = event.dur ? event.ts + event.dur : event.ts;
47
+ return eventEnd < targetEvent.ts && eventEnd >= targetEvent.ts - INVALIDATION_WINDOW;
48
+ }
40
49
  /**
41
50
  * Returns a list of NoncompositedAnimationFailures.
42
51
  */
@@ -68,14 +77,133 @@ function getNonCompositedAnimations(animations) {
68
77
  }
69
78
  return failures;
70
79
  }
80
+ /**
81
+ * Given an array of layout shift and PrePaint events, returns a mapping from
82
+ * PrePaint events to layout shifts dispatched within it.
83
+ */
84
+ function getShiftsByPrePaintEvents(layoutShifts, prePaintEvents) {
85
+ // Maps from PrePaint events to LayoutShifts that occured in each one.
86
+ const shiftsByPrePaint = new Map();
87
+ // Associate all shifts to their corresponding PrePaint.
88
+ for (const prePaintEvent of prePaintEvents) {
89
+ const firstShiftIndex = Platform.ArrayUtilities.nearestIndexFromBeginning(layoutShifts, shift => shift.ts >= prePaintEvent.ts);
90
+ if (firstShiftIndex === null) {
91
+ // No layout shifts registered after this PrePaint start. Continue.
92
+ continue;
93
+ }
94
+ for (let i = firstShiftIndex; i < layoutShifts.length; i++) {
95
+ const shift = layoutShifts[i];
96
+ if (shift.ts >= prePaintEvent.ts && shift.ts <= prePaintEvent.ts + prePaintEvent.dur) {
97
+ const shiftsInPrePaint = Platform.MapUtilities.getWithDefault(shiftsByPrePaint, prePaintEvent, () => []);
98
+ shiftsInPrePaint.push(shift);
99
+ }
100
+ if (shift.ts > prePaintEvent.ts + prePaintEvent.dur) {
101
+ // Reached all layoutShifts of this PrePaint. Break out to continue with the next prePaint event.
102
+ break;
103
+ }
104
+ }
105
+ }
106
+ return shiftsByPrePaint;
107
+ }
108
+ /**
109
+ * This gets the first prePaint event that follows the provided event and returns it.
110
+ */
111
+ function getNextPrePaintEvent(prePaintEvents, targetEvent) {
112
+ // Get the first PrePaint event that happened after the targetEvent.
113
+ const nextPrePaintIndex = Platform.ArrayUtilities.nearestIndexFromBeginning(prePaintEvents, prePaint => prePaint.ts > targetEvent.ts + (targetEvent.dur || 0));
114
+ // No PrePaint event registered after this event
115
+ if (nextPrePaintIndex === null) {
116
+ return undefined;
117
+ }
118
+ return prePaintEvents[nextPrePaintIndex];
119
+ }
120
+ /**
121
+ * An Iframe is considered a root cause if the iframe event occurs before a prePaint event
122
+ * and within this prePaint event a layout shift(s) occurs.
123
+ */
124
+ function getIframeRootCauses(iframeCreatedEvents, prePaintEvents, shiftsByPrePaint, rootCausesByShift) {
125
+ for (const iframeEvent of iframeCreatedEvents) {
126
+ const nextPrePaint = getNextPrePaintEvent(prePaintEvents, iframeEvent);
127
+ // If no following prePaint, this is not a root cause.
128
+ if (!nextPrePaint) {
129
+ continue;
130
+ }
131
+ const shifts = shiftsByPrePaint.get(nextPrePaint);
132
+ // if no layout shift(s), this is not a root cause.
133
+ if (!shifts) {
134
+ continue;
135
+ }
136
+ for (const shift of shifts) {
137
+ const rootCausesForShift = Platform.MapUtilities.getWithDefault(rootCausesByShift, shift, () => {
138
+ return {
139
+ iframes: [],
140
+ fontRequests: [],
141
+ };
142
+ });
143
+ rootCausesForShift.iframes.push(iframeEvent);
144
+ }
145
+ }
146
+ return rootCausesByShift;
147
+ }
148
+ /**
149
+ * A font request is considered a root cause if the request occurs before a prePaint event
150
+ * and within this prePaint event a layout shift(s) occurs. Additionally, this font request should
151
+ * happen within the INVALIDATION_WINDOW of the prePaint event.
152
+ */
153
+ function getFontRootCauses(networkRequests, prePaintEvents, shiftsByPrePaint, rootCausesByShift) {
154
+ const fontRequests = networkRequests.filter(req => req.args.data.resourceType === 'Font' && req.args.data.mimeType.startsWith('font'));
155
+ for (const req of fontRequests) {
156
+ const nextPrePaint = getNextPrePaintEvent(prePaintEvents, req);
157
+ if (!nextPrePaint) {
158
+ continue;
159
+ }
160
+ // If the req is outside the INVALIDATION_WINDOW, it could not be a root cause.
161
+ if (!isInInvalidationWindow(req, nextPrePaint)) {
162
+ continue;
163
+ }
164
+ // Get the shifts that belong to this prepaint
165
+ const shifts = shiftsByPrePaint.get(nextPrePaint);
166
+ // if no layout shift(s) in this prePaint, the request is not a root cause.
167
+ if (!shifts) {
168
+ continue;
169
+ }
170
+ // Include the root cause to the shifts in this prePaint.
171
+ for (const shift of shifts) {
172
+ const rootCausesForShift = Platform.MapUtilities.getWithDefault(rootCausesByShift, shift, () => {
173
+ return {
174
+ iframes: [],
175
+ fontRequests: [],
176
+ };
177
+ });
178
+ rootCausesForShift.fontRequests.push(req);
179
+ }
180
+ }
181
+ return rootCausesByShift;
182
+ }
71
183
  export function generateInsight(traceParsedData, context) {
72
- const compositeAnimationEvents = traceParsedData.Animations.animations.filter(event => {
184
+ const isWithinSameNavigation = ((event) => {
73
185
  const nav = Helpers.Trace.getNavigationForTraceEvent(event, context.frameId, traceParsedData.Meta.navigationsByFrameId);
74
186
  return nav?.args.data?.navigationId === context.navigationId;
75
187
  });
188
+ const compositeAnimationEvents = traceParsedData.Animations.animations.filter(isWithinSameNavigation);
76
189
  const animationFailures = getNonCompositedAnimations(compositeAnimationEvents);
190
+ const iframeEvents = traceParsedData.LayoutShifts.renderFrameImplCreateChildFrameEvents.filter(isWithinSameNavigation);
191
+ const networkRequests = traceParsedData.NetworkRequests.byTime.filter(isWithinSameNavigation);
192
+ const layoutShifts = traceParsedData.LayoutShifts.clusters.flatMap(cluster =>
193
+ // Use one of the events in the cluster to determine if within the same navigation.
194
+ isWithinSameNavigation(cluster.events[0]) ? cluster.events : []);
195
+ const prePaintEvents = traceParsedData.LayoutShifts.prePaintEvents.filter(isWithinSameNavigation);
196
+ // Get root causes.
197
+ const rootCausesByShift = new Map();
198
+ const shiftsByPrePaint = getShiftsByPrePaintEvents(layoutShifts, prePaintEvents);
199
+ for (const shift of layoutShifts) {
200
+ rootCausesByShift.set(shift, { iframes: [], fontRequests: [] });
201
+ }
202
+ getIframeRootCauses(iframeEvents, prePaintEvents, shiftsByPrePaint, rootCausesByShift);
203
+ getFontRootCauses(networkRequests, prePaintEvents, shiftsByPrePaint, rootCausesByShift);
77
204
  return {
78
205
  animationFailures,
206
+ shifts: rootCausesByShift,
79
207
  };
80
208
  }
81
209
  //# sourceMappingURL=CumulativeLayoutShift.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"CumulativeLayoutShift.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/insights/CumulativeLayoutShift.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,yEAAyE;AACzE,6BAA6B;AAE7B,OAAO,KAAK,OAAO,MAAM,uBAAuB,CAAC;AASjD,MAAM,UAAU,IAAI;IAClB,OAAO,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;AAChC,CAAC;AA2BD;;;;;GAKG;AACH,MAAM,0BAA0B,GAAG;IACjC;QACE,IAAI,EAAE,CAAC,IAAI,EAAE;QACb,OAAO,mFAAkD;KAC1D;IACD;QACE,IAAI,EAAE,CAAC,IAAI,EAAE;QACb,OAAO,2FAAsD;KAC9D;IACD;QACE,IAAI,EAAE,CAAC,IAAI,EAAE;QACb,OAAO,+EAAgD;KACxD;IACD;QACE,IAAI,EAAE,CAAC,IAAI,CAAC;QACZ,OAAO,uFAAoD;KAC5D;IACD;QACE,IAAI,EAAE,CAAC,IAAI,CAAC;QACZ,OAAO,iFAAiD;KACzD;IACD;QACE,IAAI,EAAE,CAAC,IAAI,CAAC;QACZ,OAAO,qFAAmD;KAC3D;CACF,CAAC;AAEF;;GAEG;AACH,SAAS,0BAA0B,CAAC,UAA+D;IAEjG,MAAM,QAAQ,GAAoC,EAAE,CAAC;IACrD,KAAK,MAAM,KAAK,IAAI,UAAU,EAAE,CAAC;QAC/B,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC;QAC9C,MAAM,aAAa,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,IAAI,EAAE,CAAC;QAC1D;;;WAGG;QACH,KAAK,MAAM,KAAK,IAAI,aAAa,EAAE,CAAC;YAClC,MAAM,WAAW,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC;YACpD,MAAM,qBAAqB,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,qBAAqB,CAAC;YACpE,IAAI,CAAC,WAAW,IAAI,CAAC,qBAAqB,EAAE,CAAC;gBAC3C,SAAS;YACX,CAAC;YACD,MAAM,cAAc,GAAG,0BAA0B,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE;gBACzG,OAAO,MAAM,CAAC,OAAO,CAAC;YACxB,CAAC,CAAC,CAAC;YACH,MAAM,OAAO,GAAkC;gBAC7C,IAAI,EAAE,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW;gBACtC,cAAc;gBACd,qBAAqB;aACtB,CAAC;YACF,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,eAA0C,EAAE,OAAiC;IAE3G,MAAM,wBAAwB,GAAG,eAAe,CAAC,UAAU,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;QACpF,MAAM,GAAG,GACL,OAAO,CAAC,KAAK,CAAC,0BAA0B,CAAC,KAAK,EAAE,OAAO,CAAC,OAAO,EAAE,eAAe,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;QAChH,OAAO,GAAG,EAAE,IAAI,CAAC,IAAI,EAAE,YAAY,KAAK,OAAO,CAAC,YAAY,CAAC;IAC/D,CAAC,CAAC,CAAC;IACH,MAAM,iBAAiB,GAAG,0BAA0B,CAAC,wBAAwB,CAAC,CAAC;IAC/E,OAAO;QACL,iBAAiB;KAClB,CAAC;AACJ,CAAC","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 Helpers from '../helpers/helpers.js';\nimport type * as Types from '../types/types.js';\n\nimport {\n type InsightResult,\n type NavigationInsightContext,\n type RequiredData,\n} from './types.js';\n\nexport function deps(): ['Meta', 'Animations'] {\n return ['Meta', 'Animations'];\n}\n\nexport const enum AnimationFailureReasons {\n UNSUPPORTED_CSS_PROPERTY = 'UNSUPPORTED_CSS_PROPERTY',\n TRANSFROM_BOX_SIZE_DEPENDENT = 'TRANSFROM_BOX_SIZE_DEPENDENT',\n FILTER_MAY_MOVE_PIXELS = 'FILTER_MAY_MOVE_PIXELS',\n NON_REPLACE_COMPOSITE_MODE = 'NON_REPLACE_COMPOSITE_MODE',\n INCOMPATIBLE_ANIMATIONS = 'INCOMPATIBLE_ANIMATIONS',\n UNSUPPORTED_TIMING_PARAMS = 'UNSUPPORTED_TIMING_PARAMS',\n}\n\nexport interface NoncompositedAnimationFailure {\n /**\n * Animation name.\n */\n name?: string;\n /**\n * Failure reason based on mask number defined in\n * https://source.chromium.org/search?q=f:compositor_animations.h%20%22enum%20FailureReason%22.\n */\n failureReasons: AnimationFailureReasons[];\n /**\n * Unsupported properties.\n */\n unsupportedProperties?: Types.TraceEvents.TraceEventAnimation['args']['data']['unsupportedProperties'];\n}\n\n/**\n * Each failure reason is represented by a bit flag. The bit shift operator '<<' is used to define\n * which bit corresponds to each failure reason.\n * https://source.chromium.org/search?q=f:compositor_animations.h%20%22enum%20FailureReason%22\n * @type {{flag: number, failure: AnimationFailureReasons}[]}\n */\nconst ACTIONABLE_FAILURE_REASONS = [\n {\n flag: 1 << 13,\n failure: AnimationFailureReasons.UNSUPPORTED_CSS_PROPERTY,\n },\n {\n flag: 1 << 11,\n failure: AnimationFailureReasons.TRANSFROM_BOX_SIZE_DEPENDENT,\n },\n {\n flag: 1 << 12,\n failure: AnimationFailureReasons.FILTER_MAY_MOVE_PIXELS,\n },\n {\n flag: 1 << 4,\n failure: AnimationFailureReasons.NON_REPLACE_COMPOSITE_MODE,\n },\n {\n flag: 1 << 6,\n failure: AnimationFailureReasons.INCOMPATIBLE_ANIMATIONS,\n },\n {\n flag: 1 << 3,\n failure: AnimationFailureReasons.UNSUPPORTED_TIMING_PARAMS,\n },\n];\n\n/**\n * Returns a list of NoncompositedAnimationFailures.\n */\nfunction getNonCompositedAnimations(animations: readonly Types.TraceEvents.SyntheticAnimationPair[]):\n NoncompositedAnimationFailure[] {\n const failures: NoncompositedAnimationFailure[] = [];\n for (const event of animations) {\n const beginEvent = event.args.data.beginEvent;\n const instantEvents = event.args.data.instantEvents || [];\n /**\n * Animation events containing composite information are ASYNC_NESTABLE_INSTANT ('n').\n * An animation may also contain multiple 'n' events, so we look through those with useful non-composited data.\n */\n for (const event of instantEvents) {\n const failureMask = event.args.data.compositeFailed;\n const unsupportedProperties = event.args.data.unsupportedProperties;\n if (!failureMask || !unsupportedProperties) {\n continue;\n }\n const failureReasons = ACTIONABLE_FAILURE_REASONS.filter(reason => failureMask & reason.flag).map(reason => {\n return reason.failure;\n });\n const failure: NoncompositedAnimationFailure = {\n name: beginEvent.args.data.displayName,\n failureReasons,\n unsupportedProperties,\n };\n failures.push(failure);\n }\n }\n return failures;\n}\n\nexport function generateInsight(traceParsedData: RequiredData<typeof deps>, context: NavigationInsightContext):\n InsightResult<{animationFailures?: readonly NoncompositedAnimationFailure[]}> {\n const compositeAnimationEvents = traceParsedData.Animations.animations.filter(event => {\n const nav =\n Helpers.Trace.getNavigationForTraceEvent(event, context.frameId, traceParsedData.Meta.navigationsByFrameId);\n return nav?.args.data?.navigationId === context.navigationId;\n });\n const animationFailures = getNonCompositedAnimations(compositeAnimationEvents);\n return {\n animationFailures,\n };\n}\n"]}
1
+ {"version":3,"file":"CumulativeLayoutShift.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/insights/CumulativeLayoutShift.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 Platform from '../../../core/platform/platform.js';\nimport * as Helpers from '../helpers/helpers.js';\nimport * as Types from '../types/types.js';\n\nimport {\n type InsightResult,\n type NavigationInsightContext,\n type RequiredData,\n} from './types.js';\n\nexport function deps(): ['Meta', 'Animations', 'LayoutShifts', 'NetworkRequests'] {\n return ['Meta', 'Animations', 'LayoutShifts', 'NetworkRequests'];\n}\n\nexport const enum AnimationFailureReasons {\n UNSUPPORTED_CSS_PROPERTY = 'UNSUPPORTED_CSS_PROPERTY',\n TRANSFROM_BOX_SIZE_DEPENDENT = 'TRANSFROM_BOX_SIZE_DEPENDENT',\n FILTER_MAY_MOVE_PIXELS = 'FILTER_MAY_MOVE_PIXELS',\n NON_REPLACE_COMPOSITE_MODE = 'NON_REPLACE_COMPOSITE_MODE',\n INCOMPATIBLE_ANIMATIONS = 'INCOMPATIBLE_ANIMATIONS',\n UNSUPPORTED_TIMING_PARAMS = 'UNSUPPORTED_TIMING_PARAMS',\n}\n\nexport interface NoncompositedAnimationFailure {\n /**\n * Animation name.\n */\n name?: string;\n /**\n * Failure reason based on mask number defined in\n * https://source.chromium.org/search?q=f:compositor_animations.h%20%22enum%20FailureReason%22.\n */\n failureReasons: AnimationFailureReasons[];\n /**\n * Unsupported properties.\n */\n unsupportedProperties?: Types.TraceEvents.TraceEventAnimation['args']['data']['unsupportedProperties'];\n}\n\n/**\n * Each failure reason is represented by a bit flag. The bit shift operator '<<' is used to define\n * which bit corresponds to each failure reason.\n * https://source.chromium.org/search?q=f:compositor_animations.h%20%22enum%20FailureReason%22\n * @type {{flag: number, failure: AnimationFailureReasons}[]}\n */\nconst ACTIONABLE_FAILURE_REASONS = [\n {\n flag: 1 << 13,\n failure: AnimationFailureReasons.UNSUPPORTED_CSS_PROPERTY,\n },\n {\n flag: 1 << 11,\n failure: AnimationFailureReasons.TRANSFROM_BOX_SIZE_DEPENDENT,\n },\n {\n flag: 1 << 12,\n failure: AnimationFailureReasons.FILTER_MAY_MOVE_PIXELS,\n },\n {\n flag: 1 << 4,\n failure: AnimationFailureReasons.NON_REPLACE_COMPOSITE_MODE,\n },\n {\n flag: 1 << 6,\n failure: AnimationFailureReasons.INCOMPATIBLE_ANIMATIONS,\n },\n {\n flag: 1 << 3,\n failure: AnimationFailureReasons.UNSUPPORTED_TIMING_PARAMS,\n },\n];\n\n// 500ms window.\n// Use this window to consider events and requests that may have caused a layout shift.\nconst INVALIDATION_WINDOW = Helpers.Timing.secondsToMicroseconds(Types.Timing.Seconds(0.5));\n\nexport interface LayoutShiftRootCausesData {\n iframes: Types.TraceEvents.TraceEventRenderFrameImplCreateChildFrame[];\n fontRequests: Types.TraceEvents.SyntheticNetworkRequest[];\n}\n\nfunction isInInvalidationWindow(\n event: Types.TraceEvents.TraceEventData, targetEvent: Types.TraceEvents.TraceEventData): boolean {\n const eventEnd = event.dur ? event.ts + event.dur : event.ts;\n return eventEnd < targetEvent.ts && eventEnd >= targetEvent.ts - INVALIDATION_WINDOW;\n}\n\n/**\n * Returns a list of NoncompositedAnimationFailures.\n */\nfunction getNonCompositedAnimations(animations: readonly Types.TraceEvents.SyntheticAnimationPair[]):\n NoncompositedAnimationFailure[] {\n const failures: NoncompositedAnimationFailure[] = [];\n for (const event of animations) {\n const beginEvent = event.args.data.beginEvent;\n const instantEvents = event.args.data.instantEvents || [];\n /**\n * Animation events containing composite information are ASYNC_NESTABLE_INSTANT ('n').\n * An animation may also contain multiple 'n' events, so we look through those with useful non-composited data.\n */\n for (const event of instantEvents) {\n const failureMask = event.args.data.compositeFailed;\n const unsupportedProperties = event.args.data.unsupportedProperties;\n if (!failureMask || !unsupportedProperties) {\n continue;\n }\n const failureReasons = ACTIONABLE_FAILURE_REASONS.filter(reason => failureMask & reason.flag).map(reason => {\n return reason.failure;\n });\n const failure: NoncompositedAnimationFailure = {\n name: beginEvent.args.data.displayName,\n failureReasons,\n unsupportedProperties,\n };\n failures.push(failure);\n }\n }\n return failures;\n}\n\n/**\n * Given an array of layout shift and PrePaint events, returns a mapping from\n * PrePaint events to layout shifts dispatched within it.\n */\nfunction getShiftsByPrePaintEvents(\n layoutShifts: Types.TraceEvents.TraceEventLayoutShift[],\n prePaintEvents: Types.TraceEvents.TraceEventPrePaint[],\n ): Map<Types.TraceEvents.TraceEventPrePaint, Types.TraceEvents.TraceEventLayoutShift[]> {\n // Maps from PrePaint events to LayoutShifts that occured in each one.\n const shiftsByPrePaint = new Map<Types.TraceEvents.TraceEventPrePaint, Types.TraceEvents.TraceEventLayoutShift[]>();\n\n // Associate all shifts to their corresponding PrePaint.\n for (const prePaintEvent of prePaintEvents) {\n const firstShiftIndex =\n Platform.ArrayUtilities.nearestIndexFromBeginning(layoutShifts, shift => shift.ts >= prePaintEvent.ts);\n if (firstShiftIndex === null) {\n // No layout shifts registered after this PrePaint start. Continue.\n continue;\n }\n for (let i = firstShiftIndex; i < layoutShifts.length; i++) {\n const shift = layoutShifts[i];\n if (shift.ts >= prePaintEvent.ts && shift.ts <= prePaintEvent.ts + prePaintEvent.dur) {\n const shiftsInPrePaint = Platform.MapUtilities.getWithDefault(shiftsByPrePaint, prePaintEvent, () => []);\n shiftsInPrePaint.push(shift);\n }\n if (shift.ts > prePaintEvent.ts + prePaintEvent.dur) {\n // Reached all layoutShifts of this PrePaint. Break out to continue with the next prePaint event.\n break;\n }\n }\n }\n return shiftsByPrePaint;\n}\n\n/**\n * This gets the first prePaint event that follows the provided event and returns it.\n */\nfunction getNextPrePaintEvent(\n prePaintEvents: Types.TraceEvents.TraceEventPrePaint[],\n targetEvent: Types.TraceEvents.TraceEventData): Types.TraceEvents.TraceEventPrePaint|undefined {\n // Get the first PrePaint event that happened after the targetEvent.\n const nextPrePaintIndex = Platform.ArrayUtilities.nearestIndexFromBeginning(\n prePaintEvents, prePaint => prePaint.ts > targetEvent.ts + (targetEvent.dur || 0));\n // No PrePaint event registered after this event\n if (nextPrePaintIndex === null) {\n return undefined;\n }\n\n return prePaintEvents[nextPrePaintIndex];\n}\n\n/**\n * An Iframe is considered a root cause if the iframe event occurs before a prePaint event\n * and within this prePaint event a layout shift(s) occurs.\n */\nfunction getIframeRootCauses(\n iframeCreatedEvents: readonly Types.TraceEvents.TraceEventRenderFrameImplCreateChildFrame[],\n prePaintEvents: Types.TraceEvents.TraceEventPrePaint[],\n shiftsByPrePaint: Map<Types.TraceEvents.TraceEventPrePaint, Types.TraceEvents.TraceEventLayoutShift[]>,\n rootCausesByShift: Map<Types.TraceEvents.TraceEventLayoutShift, LayoutShiftRootCausesData>):\n Map<Types.TraceEvents.TraceEventLayoutShift, LayoutShiftRootCausesData> {\n for (const iframeEvent of iframeCreatedEvents) {\n const nextPrePaint = getNextPrePaintEvent(prePaintEvents, iframeEvent);\n // If no following prePaint, this is not a root cause.\n if (!nextPrePaint) {\n continue;\n }\n const shifts = shiftsByPrePaint.get(nextPrePaint);\n // if no layout shift(s), this is not a root cause.\n if (!shifts) {\n continue;\n }\n for (const shift of shifts) {\n const rootCausesForShift = Platform.MapUtilities.getWithDefault(rootCausesByShift, shift, () => {\n return {\n iframes: [],\n fontRequests: [],\n };\n });\n rootCausesForShift.iframes.push(iframeEvent);\n }\n }\n return rootCausesByShift;\n}\n\n/**\n * A font request is considered a root cause if the request occurs before a prePaint event\n * and within this prePaint event a layout shift(s) occurs. Additionally, this font request should\n * happen within the INVALIDATION_WINDOW of the prePaint event.\n */\nfunction getFontRootCauses(\n networkRequests: Types.TraceEvents.SyntheticNetworkRequest[],\n prePaintEvents: Types.TraceEvents.TraceEventPrePaint[],\n shiftsByPrePaint: Map<Types.TraceEvents.TraceEventPrePaint, Types.TraceEvents.TraceEventLayoutShift[]>,\n rootCausesByShift: Map<Types.TraceEvents.TraceEventLayoutShift, LayoutShiftRootCausesData>):\n Map<Types.TraceEvents.TraceEventLayoutShift, LayoutShiftRootCausesData> {\n const fontRequests =\n networkRequests.filter(req => req.args.data.resourceType === 'Font' && req.args.data.mimeType.startsWith('font'));\n\n for (const req of fontRequests) {\n const nextPrePaint = getNextPrePaintEvent(prePaintEvents, req);\n if (!nextPrePaint) {\n continue;\n }\n\n // If the req is outside the INVALIDATION_WINDOW, it could not be a root cause.\n if (!isInInvalidationWindow(req, nextPrePaint)) {\n continue;\n }\n\n // Get the shifts that belong to this prepaint\n const shifts = shiftsByPrePaint.get(nextPrePaint);\n\n // if no layout shift(s) in this prePaint, the request is not a root cause.\n if (!shifts) {\n continue;\n }\n // Include the root cause to the shifts in this prePaint.\n for (const shift of shifts) {\n const rootCausesForShift = Platform.MapUtilities.getWithDefault(rootCausesByShift, shift, () => {\n return {\n iframes: [],\n fontRequests: [],\n };\n });\n rootCausesForShift.fontRequests.push(req);\n }\n }\n return rootCausesByShift;\n}\n\nexport function generateInsight(\n traceParsedData: RequiredData<typeof deps>, context: NavigationInsightContext): InsightResult<{\n animationFailures?: readonly NoncompositedAnimationFailure[],\n shifts?: Map<Types.TraceEvents.TraceEventLayoutShift, LayoutShiftRootCausesData>,\n}> {\n const isWithinSameNavigation = ((event: Types.TraceEvents.TraceEventData): boolean => {\n const nav =\n Helpers.Trace.getNavigationForTraceEvent(event, context.frameId, traceParsedData.Meta.navigationsByFrameId);\n return nav?.args.data?.navigationId === context.navigationId;\n });\n\n const compositeAnimationEvents = traceParsedData.Animations.animations.filter(isWithinSameNavigation);\n const animationFailures = getNonCompositedAnimations(compositeAnimationEvents);\n\n const iframeEvents =\n traceParsedData.LayoutShifts.renderFrameImplCreateChildFrameEvents.filter(isWithinSameNavigation);\n const networkRequests = traceParsedData.NetworkRequests.byTime.filter(isWithinSameNavigation);\n\n const layoutShifts = traceParsedData.LayoutShifts.clusters.flatMap(\n cluster =>\n // Use one of the events in the cluster to determine if within the same navigation.\n isWithinSameNavigation(cluster.events[0]) ? cluster.events : [],\n );\n const prePaintEvents = traceParsedData.LayoutShifts.prePaintEvents.filter(isWithinSameNavigation);\n\n // Get root causes.\n const rootCausesByShift = new Map<Types.TraceEvents.TraceEventLayoutShift, LayoutShiftRootCausesData>();\n const shiftsByPrePaint = getShiftsByPrePaintEvents(layoutShifts, prePaintEvents);\n\n for (const shift of layoutShifts) {\n rootCausesByShift.set(shift, {iframes: [], fontRequests: []});\n }\n\n getIframeRootCauses(iframeEvents, prePaintEvents, shiftsByPrePaint, rootCausesByShift);\n getFontRootCauses(networkRequests, prePaintEvents, shiftsByPrePaint, rootCausesByShift);\n\n return {\n animationFailures,\n shifts: rootCausesByShift,\n };\n}\n"]}
@@ -1,5 +1,6 @@
1
+ import type * as Protocol from '../../../generated/protocol.js';
1
2
  import { type TraceWindowMicroSeconds } from './Timing.js';
2
- import { type TraceEventData } from './TraceEvents.js';
3
+ import { type ProcessID, type SampleIndex, type ThreadID, type TraceEventData } from './TraceEvents.js';
3
4
  export type TraceFile = {
4
5
  traceEvents: readonly TraceEventData[];
5
6
  metadata: MetaData;
@@ -12,10 +13,14 @@ export declare const enum DataOrigin {
12
13
  CPUProfile = "CPUProfile",
13
14
  TraceEvents = "TraceEvents"
14
15
  }
15
- export interface Annotations {
16
- entriesFilterAnnotations: {
17
- hiddenEntriesIndexes: number[];
18
- modifiedEntriesIndexes: number[];
16
+ export type RawEventKey = ['r', number];
17
+ export type ProfileCallKey = ['p', ProcessID, ThreadID, SampleIndex, Protocol.integer];
18
+ export type SyntheticEventKey = ['s', number];
19
+ export type TraceEventSerializableKey = RawEventKey | ProfileCallKey | SyntheticEventKey;
20
+ export interface Modifications {
21
+ entriesModifications: {
22
+ hiddenEntries: string[];
23
+ expandableEntries: string[];
19
24
  };
20
25
  initialBreadcrumb: Breadcrumb;
21
26
  }
@@ -31,6 +36,6 @@ export interface MetaData {
31
36
  cpuThrottling?: number;
32
37
  hardwareConcurrency?: number;
33
38
  dataOrigin?: DataOrigin;
34
- annotations?: Annotations;
39
+ modifications?: Modifications;
35
40
  }
36
41
  export type Contents = TraceFile | TraceEventData[];
@@ -1,5 +1,2 @@
1
- // Copyright 2023 The Chromium Authors. All rights reserved.
2
- // Use of this source code is governed by a BSD-style license that can be
3
- // found in the LICENSE file.
4
1
  export {};
5
2
  //# sourceMappingURL=File.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"File.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/types/File.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,yEAAyE;AACzE,6BAA6B","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 {type TraceWindowMicroSeconds} from './Timing.js';\nimport {type TraceEventData} from './TraceEvents.js';\n\nexport type TraceFile = {\n traceEvents: readonly TraceEventData[],\n metadata: MetaData,\n};\n\nexport interface Breadcrumb {\n window: TraceWindowMicroSeconds;\n child: Breadcrumb|null;\n}\n\nexport const enum DataOrigin {\n CPUProfile = 'CPUProfile',\n TraceEvents = 'TraceEvents',\n}\nexport interface Annotations {\n entriesFilterAnnotations: {\n hiddenEntriesIndexes: number[],\n modifiedEntriesIndexes: number[],\n };\n initialBreadcrumb: Breadcrumb;\n}\n\n/**\n * Trace metadata that we persist to the file. This will allow us to\n * store specifics for the trace, e.g., which tracks should be visible\n * on load.\n */\nexport interface MetaData {\n source?: 'DevTools';\n startTime?: string;\n networkThrottling?: string;\n cpuThrottling?: number;\n hardwareConcurrency?: number;\n dataOrigin?: DataOrigin;\n annotations?: Annotations;\n}\n\nexport type Contents = TraceFile|TraceEventData[];\n"]}
1
+ {"version":3,"file":"File.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/types/File.ts"],"names":[],"mappings":"","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.\nimport type * as Protocol from '../../../generated/protocol.js';\n\nimport {type TraceWindowMicroSeconds} from './Timing.js';\nimport {type ProcessID, type SampleIndex, type ThreadID, type TraceEventData} from './TraceEvents.js';\n\nexport type TraceFile = {\n traceEvents: readonly TraceEventData[],\n metadata: MetaData,\n};\n\nexport interface Breadcrumb {\n window: TraceWindowMicroSeconds;\n child: Breadcrumb|null;\n}\n\nexport const enum DataOrigin {\n CPUProfile = 'CPUProfile',\n TraceEvents = 'TraceEvents',\n}\n\n// Serializable keys are created for trace events to be able to save\n// references to timeline events in a trace file. These keys enable\n// user modifications that can be saved. See go/cpq:event-data-json for\n// more details on the key format.\nexport type RawEventKey = ['r', number];\nexport type ProfileCallKey = ['p', ProcessID, ThreadID, SampleIndex, Protocol.integer];\nexport type SyntheticEventKey = ['s', number];\nexport type TraceEventSerializableKey = RawEventKey|ProfileCallKey|SyntheticEventKey;\n\nexport interface Modifications {\n entriesModifications: {\n // Entries hidden by the user\n hiddenEntries: string[],\n // Entries that parent a hiddenEntry\n expandableEntries: string[],\n };\n initialBreadcrumb: Breadcrumb;\n}\n\n/**\n * Trace metadata that we persist to the file. This will allow us to\n * store specifics for the trace, e.g., which tracks should be visible\n * on load.\n */\nexport interface MetaData {\n source?: 'DevTools';\n startTime?: string;\n networkThrottling?: string;\n cpuThrottling?: number;\n hardwareConcurrency?: number;\n dataOrigin?: DataOrigin;\n modifications?: Modifications;\n}\n\nexport type Contents = TraceFile|TraceEventData[];\n"]}
@@ -79,7 +79,7 @@ export interface TraceEventSample extends TraceEventData {
79
79
  * A fake trace event created to support CDP.Profiler.Profiles in the
80
80
  * trace engine.
81
81
  */
82
- export interface SyntheticCpuProfile extends TraceEventInstant, SyntheticEvent<Phase.INSTANT> {
82
+ export interface SyntheticCpuProfile extends TraceEventInstant, SyntheticBasedEvent<Phase.INSTANT> {
83
83
  name: 'CpuProfile';
84
84
  args: TraceEventArgs & {
85
85
  data: TraceEventArgsData & {
@@ -247,7 +247,7 @@ interface SyntheticArgsData {
247
247
  totalTime: MicroSeconds;
248
248
  waiting: MicroSeconds;
249
249
  }
250
- export interface SyntheticNetworkRequest extends TraceEventComplete, SyntheticEvent<Phase.COMPLETE> {
250
+ export interface SyntheticNetworkRequest extends TraceEventComplete, SyntheticBasedEvent<Phase.COMPLETE> {
251
251
  rawSourceEvent: TraceEventData;
252
252
  args: TraceEventArgs & {
253
253
  data: TraceEventArgsData & {
@@ -306,7 +306,7 @@ export declare const enum AuctionWorkletType {
306
306
  SELLER = "seller",
307
307
  UNKNOWN = "unknown"
308
308
  }
309
- export interface SyntheticAuctionWorkletEvent extends TraceEventInstant, SyntheticEvent<Phase.INSTANT> {
309
+ export interface SyntheticAuctionWorkletEvent extends TraceEventInstant, SyntheticBasedEvent<Phase.INSTANT> {
310
310
  rawSourceEvent: TraceEventData;
311
311
  name: 'SyntheticAuctionWorkletEvent';
312
312
  pid: ProcessID;
@@ -370,7 +370,7 @@ export interface TraceEventScreenshot extends TraceEventData {
370
370
  ph: Phase.OBJECT_SNAPSHOT;
371
371
  }
372
372
  export declare function isTraceEventScreenshot(event: TraceEventData): event is TraceEventScreenshot;
373
- export interface SyntheticScreenshot extends TraceEventData, SyntheticEvent {
373
+ export interface SyntheticScreenshot extends TraceEventData, SyntheticBasedEvent {
374
374
  rawSourceEvent: TraceEventScreenshot;
375
375
  /** This is the correct presentation timestamp. */
376
376
  ts: MicroSeconds;
@@ -628,7 +628,7 @@ export interface LayoutShiftParsedData {
628
628
  cumulativeWeightedScoreInWindow: number;
629
629
  sessionWindowData: LayoutShiftSessionWindowData;
630
630
  }
631
- export interface SyntheticLayoutShift extends TraceEventLayoutShift, SyntheticEvent<Phase.INSTANT> {
631
+ export interface SyntheticLayoutShift extends TraceEventLayoutShift, SyntheticBasedEvent<Phase.INSTANT> {
632
632
  name: 'LayoutShift';
633
633
  rawSourceEvent: TraceEventLayoutShift;
634
634
  args: TraceEventArgs & {
@@ -661,6 +661,7 @@ export interface TraceEventResourceSendRequest extends TraceEventInstant {
661
661
  requestMethod?: string;
662
662
  renderBlocking?: RenderBlocking;
663
663
  initiator?: Initiator;
664
+ isLinkPreload?: boolean;
664
665
  };
665
666
  };
666
667
  }
@@ -743,7 +744,6 @@ export interface TraceEventResourceReceiveResponse extends TraceEventInstant {
743
744
  responseTime: MilliSeconds;
744
745
  statusCode: number;
745
746
  timing: TraceEventResourceReceiveResponseTimingData;
746
- isLinkPreload?: boolean;
747
747
  connectionId: number;
748
748
  connectionReused: boolean;
749
749
  headers?: Array<{
@@ -852,6 +852,14 @@ export interface TraceEventScheduleStyleRecalculation extends TraceEventInstant
852
852
  };
853
853
  }
854
854
  export declare function isTraceEventScheduleStyleRecalculation(event: TraceEventData): event is TraceEventScheduleStyleRecalculation;
855
+ export interface TraceEventRenderFrameImplCreateChildFrame extends TraceEventData {
856
+ name: KnownEventName.RenderFrameImplCreateChildFrame;
857
+ args: TraceEventArgs & {
858
+ child_frame_token: string;
859
+ frame_token: string;
860
+ };
861
+ }
862
+ export declare function isTraceEventRenderFrameImplCreateChildFrame(event: TraceEventData): event is TraceEventRenderFrameImplCreateChildFrame;
855
863
  export interface TraceEventPrePaint extends TraceEventComplete {
856
864
  name: 'PrePaint';
857
865
  }
@@ -1004,12 +1012,15 @@ export interface TraceEventPipelineReporter extends TraceEventData {
1004
1012
  };
1005
1013
  }
1006
1014
  export declare function isTraceEventPipelineReporter(event: TraceEventData): event is TraceEventPipelineReporter;
1007
- interface SyntheticEvent<Ph extends Phase = Phase> extends TraceEventData {
1015
+ export interface SyntheticBasedEvent<Ph extends Phase = Phase> extends SyntheticEntry {
1008
1016
  ph: Ph;
1009
1017
  rawSourceEvent: TraceEventData;
1010
1018
  }
1011
- export declare function isSyntheticEvent(event: TraceEventData): event is SyntheticEvent;
1012
- export interface SyntheticEventPair<T extends TraceEventPairableAsync = TraceEventPairableAsync> extends SyntheticEvent {
1019
+ export type SyntheticEntry = TraceEventData & {
1020
+ _tag: 'SyntheticEntryTag';
1021
+ };
1022
+ export declare function isSyntheticBasedEvent(event: TraceEventData): event is SyntheticBasedEvent;
1023
+ export interface SyntheticEventPair<T extends TraceEventPairableAsync = TraceEventPairableAsync> extends SyntheticBasedEvent {
1013
1024
  rawSourceEvent: TraceEventData;
1014
1025
  name: T['name'];
1015
1026
  cat: T['cat'];
@@ -1066,6 +1077,7 @@ export interface SyntheticProfileCall extends SyntheticTraceEntry {
1066
1077
  nodeId: Protocol.integer;
1067
1078
  sampleIndex: number;
1068
1079
  profileId: ProfileID;
1080
+ selfTime: MicroSeconds;
1069
1081
  }
1070
1082
  /**
1071
1083
  * A JS Sample reflects a single sample from the V8 CPU Profile
@@ -1253,7 +1265,7 @@ export interface TraceEventLayout extends TraceEventComplete {
1253
1265
  partialLayout: boolean;
1254
1266
  totalObjects: number;
1255
1267
  };
1256
- endData: {
1268
+ endData?: {
1257
1269
  layoutRoots: Array<{
1258
1270
  depth: number;
1259
1271
  nodeId: Protocol.DOM.BackendNodeId;
@@ -1283,6 +1295,11 @@ declare class CallFrameIdTag {
1283
1295
  }
1284
1296
  export type CallFrameID = number & CallFrameIdTag;
1285
1297
  export declare function CallFrameID(value: number): CallFrameID;
1298
+ declare class SampleIndexTag {
1299
+ #private;
1300
+ }
1301
+ export type SampleIndex = number & SampleIndexTag;
1302
+ export declare function SampleIndex(value: number): SampleIndex;
1286
1303
  declare class ProcessIdTag {
1287
1304
  #private;
1288
1305
  }
@@ -1542,12 +1559,64 @@ export interface TraceEventWebSocketCreate extends TraceEventInstant {
1542
1559
  identifier: number;
1543
1560
  url: string;
1544
1561
  frame?: string;
1562
+ workerId?: string;
1545
1563
  websocketProtocol?: string;
1546
1564
  stackTrace?: TraceEventCallFrame;
1547
1565
  };
1548
1566
  };
1549
1567
  }
1550
1568
  export declare function isTraceEventWebSocketCreate(event: TraceEventData): event is TraceEventWebSocketCreate;
1569
+ export interface TraceEventWebSocketInfo extends TraceEventInstant {
1570
+ name: KnownEventName.WebSocketDestroy | KnownEventName.WebSocketReceiveHandshake | KnownEventName.WebSocketReceiveHandshakeResponse;
1571
+ args: TraceEventArgs & {
1572
+ data: TraceEventArgsData & {
1573
+ identifier: number;
1574
+ url: string;
1575
+ frame?: string;
1576
+ workerId?: string;
1577
+ };
1578
+ };
1579
+ }
1580
+ export interface TraceEventWebSocketTransfer extends TraceEventInstant {
1581
+ name: KnownEventName.WebSocketSend | KnownEventName.WebSocketReceive;
1582
+ args: TraceEventArgs & {
1583
+ data: TraceEventArgsData & {
1584
+ identifier: number;
1585
+ url: string;
1586
+ frame?: string;
1587
+ workerId?: string;
1588
+ dataLength: number;
1589
+ };
1590
+ };
1591
+ }
1592
+ export declare function isTraceEventWebSocketInfo(traceEventData: TraceEventData): traceEventData is TraceEventWebSocketInfo;
1593
+ export declare function isTraceEventWebSocketTransfer(traceEventData: TraceEventData): traceEventData is TraceEventWebSocketTransfer;
1594
+ export interface TraceEventWebSocketSend extends TraceEventInstant {
1595
+ name: KnownEventName.WebSocketSend;
1596
+ args: TraceEventArgs & {
1597
+ data: TraceEventArgsData & {
1598
+ identifier: number;
1599
+ url: string;
1600
+ frame?: string;
1601
+ workerId?: string;
1602
+ dataLength: number;
1603
+ };
1604
+ };
1605
+ }
1606
+ export declare function isTraceEventWebSocketSend(event: TraceEventData): event is TraceEventWebSocketSend;
1607
+ export interface TraceEventWebSocketReceive extends TraceEventInstant {
1608
+ name: KnownEventName.WebSocketReceive;
1609
+ args: TraceEventArgs & {
1610
+ data: TraceEventArgsData & {
1611
+ identifier: number;
1612
+ url: string;
1613
+ frame?: string;
1614
+ workerId?: string;
1615
+ dataLength: number;
1616
+ };
1617
+ };
1618
+ }
1619
+ export declare function isTraceEventWebSocketReceive(event: TraceEventData): event is TraceEventWebSocketReceive;
1551
1620
  export interface TraceEventWebSocketSendHandshakeRequest extends TraceEventInstant {
1552
1621
  name: KnownEventName.WebSocketSendHandshakeRequest;
1553
1622
  args: TraceEventArgs & {
@@ -1578,7 +1647,8 @@ export interface TraceEventWebSocketDestroy extends TraceEventInstant {
1578
1647
  };
1579
1648
  }
1580
1649
  export declare function isTraceEventWebSocketDestroy(event: TraceEventData): event is TraceEventWebSocketDestroy;
1581
- export declare function isWebSocketTraceEvent(event: TraceEventData): event is TraceEventWebSocketCreate | TraceEventWebSocketDestroy | TraceEventWebSocketReceiveHandshakeResponse | TraceEventWebSocketSendHandshakeRequest;
1650
+ export declare function isWebSocketTraceEvent(event: TraceEventData): event is TraceEventWebSocketCreate | TraceEventWebSocketInfo | TraceEventWebSocketTransfer;
1651
+ export type WebSocketEvent = TraceEventWebSocketCreate | TraceEventWebSocketInfo | TraceEventWebSocketTransfer;
1582
1652
  export interface TraceEventV8Compile extends TraceEventComplete {
1583
1653
  name: KnownEventName.Compile;
1584
1654
  args: TraceEventArgs & {
@@ -1662,6 +1732,8 @@ export declare const enum KnownEventName {
1662
1732
  WebSocketSendHandshake = "WebSocketSendHandshakeRequest",
1663
1733
  WebSocketReceiveHandshake = "WebSocketReceiveHandshakeResponse",
1664
1734
  WebSocketDestroy = "WebSocketDestroy",
1735
+ WebSocketSend = "WebSocketSend",
1736
+ WebSocketReceive = "WebSocketReceive",
1665
1737
  CryptoDoEncrypt = "DoEncrypt",
1666
1738
  CryptoDoEncryptReply = "DoEncryptReply",
1667
1739
  CryptoDoDecrypt = "DoDecrypt",
@@ -1771,7 +1843,8 @@ export declare const enum KnownEventName {
1771
1843
  InputLatencyMouseWheel = "InputLatency::MouseWheel",
1772
1844
  ImplSideFling = "InputHandlerProxy::HandleGestureFling::started",
1773
1845
  SchedulePostMessage = "SchedulePostMessage",
1774
- HandlePostMessage = "HandlePostMessage"
1846
+ HandlePostMessage = "HandlePostMessage",
1847
+ RenderFrameImplCreateChildFrame = "RenderFrameImpl::createChildFrame"
1775
1848
  }
1776
1849
  export declare const Categories: {
1777
1850
  readonly Console: "blink.console";
@@ -68,10 +68,13 @@ export function isTraceEventBeginCommitCompositorFrame(event) {
68
68
  export function isTraceEventScheduleStyleRecalculation(event) {
69
69
  return event.name === "ScheduleStyleRecalculation" /* KnownEventName.ScheduleStyleRecalculation */;
70
70
  }
71
+ export function isTraceEventRenderFrameImplCreateChildFrame(event) {
72
+ return event.name === "RenderFrameImpl::createChildFrame" /* KnownEventName.RenderFrameImplCreateChildFrame */;
73
+ }
71
74
  export function isTraceEventPipelineReporter(event) {
72
75
  return event.name === "PipelineReporter" /* KnownEventName.PipelineReporter */;
73
76
  }
74
- export function isSyntheticEvent(event) {
77
+ export function isSyntheticBasedEvent(event) {
75
78
  return 'rawSourceEvent' in event;
76
79
  }
77
80
  export function isSyntheticInteractionEvent(event) {
@@ -155,6 +158,13 @@ class CallFrameIdTag {
155
158
  export function CallFrameID(value) {
156
159
  return value;
157
160
  }
161
+ class SampleIndexTag {
162
+ #sampleIndexTag;
163
+ }
164
+ // eslint-disable-next-line @typescript-eslint/naming-convention
165
+ export function SampleIndex(value) {
166
+ return value;
167
+ }
158
168
  class ProcessIdTag {
159
169
  #processIdTag;
160
170
  }
@@ -408,6 +418,21 @@ export function isTraceEventRequestIdleCallback(event) {
408
418
  export function isTraceEventWebSocketCreate(event) {
409
419
  return event.name === "WebSocketCreate" /* KnownEventName.WebSocketCreate */;
410
420
  }
421
+ export function isTraceEventWebSocketInfo(traceEventData) {
422
+ return traceEventData.name === "WebSocketSendHandshakeRequest" /* KnownEventName.WebSocketSendHandshakeRequest */ ||
423
+ traceEventData.name === "WebSocketReceiveHandshakeResponse" /* KnownEventName.WebSocketReceiveHandshakeResponse */ ||
424
+ traceEventData.name === "WebSocketDestroy" /* KnownEventName.WebSocketDestroy */;
425
+ }
426
+ export function isTraceEventWebSocketTransfer(traceEventData) {
427
+ return traceEventData.name === "WebSocketSend" /* KnownEventName.WebSocketSend */ ||
428
+ traceEventData.name === "WebSocketReceive" /* KnownEventName.WebSocketReceive */;
429
+ }
430
+ export function isTraceEventWebSocketSend(event) {
431
+ return event.name === "WebSocketSend" /* KnownEventName.WebSocketSend */;
432
+ }
433
+ export function isTraceEventWebSocketReceive(event) {
434
+ return event.name === "WebSocketReceive" /* KnownEventName.WebSocketReceive */;
435
+ }
411
436
  export function isTraceEventWebSocketSendHandshakeRequest(event) {
412
437
  return event.name === "WebSocketSendHandshakeRequest" /* KnownEventName.WebSocketSendHandshakeRequest */;
413
438
  }
@@ -418,8 +443,7 @@ export function isTraceEventWebSocketDestroy(event) {
418
443
  return event.name === "WebSocketDestroy" /* KnownEventName.WebSocketDestroy */;
419
444
  }
420
445
  export function isWebSocketTraceEvent(event) {
421
- return isTraceEventWebSocketCreate(event) || isTraceEventWebSocketDestroy(event) ||
422
- isTraceEventWebSocketReceiveHandshakeResponse(event) || isTraceEventWebSocketSendHandshakeRequest(event);
446
+ return isTraceEventWebSocketCreate(event) || isTraceEventWebSocketInfo(event) || isTraceEventWebSocketTransfer(event);
423
447
  }
424
448
  export function isTraceEventV8Compile(event) {
425
449
  return event.name === "v8.compile" /* KnownEventName.Compile */;