chrome-devtools-frontend 1.0.1556696 → 1.0.1559913

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 (201) hide show
  1. package/front_end/Images/src/container.svg +4 -0
  2. package/front_end/core/common/Gzip.ts +15 -0
  3. package/front_end/core/common/Object.ts +5 -1
  4. package/front_end/core/host/ResourceLoader.ts +1 -1
  5. package/front_end/core/host/UserMetrics.ts +3 -1
  6. package/front_end/core/sdk/CSSMetadata.ts +6 -6
  7. package/front_end/core/sdk/CSSModel.ts +2 -2
  8. package/front_end/core/sdk/DOMModel.ts +14 -3
  9. package/front_end/core/sdk/NetworkManager.ts +0 -7
  10. package/front_end/core/sdk/SourceMap.ts +16 -2
  11. package/front_end/core/sdk/SourceMapManager.ts +1 -1
  12. package/front_end/core/sdk/SourceMapScopesInfo.ts +11 -4
  13. package/front_end/entrypoints/formatter_worker/FormatterActions.ts +1 -0
  14. package/front_end/entrypoints/formatter_worker/ScopeParser.ts +51 -8
  15. package/front_end/entrypoints/main/GlobalAiButton.ts +5 -1
  16. package/front_end/generated/Deprecation.ts +0 -7
  17. package/front_end/generated/InspectorBackendCommands.ts +5 -4
  18. package/front_end/generated/SupportedCSSProperties.js +64 -32
  19. package/front_end/generated/protocol-mapping.d.ts +9 -0
  20. package/front_end/generated/protocol-proxy-api.d.ts +7 -0
  21. package/front_end/generated/protocol.ts +23 -1
  22. package/front_end/models/ai_assistance/agents/StylingAgent.ts +1 -1
  23. package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.ts +11 -7
  24. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +3 -3
  25. package/front_end/models/bindings/CompilerScriptMapping.ts +7 -6
  26. package/front_end/models/bindings/DebuggerWorkspaceBinding.ts +4 -4
  27. package/front_end/models/javascript_metadata/NativeFunctions.js +2 -2
  28. package/front_end/models/stack_trace/StackTraceImpl.ts +5 -3
  29. package/front_end/models/stack_trace/StackTraceModel.ts +53 -40
  30. package/front_end/models/trace/EventsSerializer.ts +8 -2
  31. package/front_end/models/trace/LanternComputationData.ts +4 -3
  32. package/front_end/models/trace/Processor.ts +6 -5
  33. package/front_end/models/trace/Styles.ts +10 -1
  34. package/front_end/models/trace/handlers/LargestImagePaintHandler.ts +2 -2
  35. package/front_end/models/trace/handlers/LayoutShiftsHandler.ts +2 -2
  36. package/front_end/models/trace/handlers/MetaHandler.ts +14 -0
  37. package/front_end/models/trace/handlers/PageLoadMetricsHandler.ts +54 -34
  38. package/front_end/models/trace/helpers/Timing.ts +8 -1
  39. package/front_end/models/trace/insights/Common.ts +1 -1
  40. package/front_end/models/trace/insights/LCPBreakdown.ts +4 -4
  41. package/front_end/models/trace/insights/LCPDiscovery.ts +3 -3
  42. package/front_end/models/trace/insights/RenderBlocking.ts +1 -1
  43. package/front_end/models/trace/insights/types.ts +1 -1
  44. package/front_end/models/trace/types/TraceEvents.ts +62 -10
  45. package/front_end/panels/application/AppManifestView.ts +134 -223
  46. package/front_end/panels/application/CookieItemsView.ts +1 -0
  47. package/front_end/panels/application/SharedStorageTreeElement.ts +3 -0
  48. package/front_end/panels/application/appManifestView.css +1 -1
  49. package/front_end/panels/common/AiCodeGenerationTeaser.ts +48 -12
  50. package/front_end/panels/common/aiCodeGenerationTeaser.css +14 -0
  51. package/front_end/panels/common/common.ts +1 -1
  52. package/front_end/panels/console/ConsoleViewMessage.ts +4 -3
  53. package/front_end/panels/console/consoleView.css +1 -1
  54. package/front_end/panels/elements/CSSRuleValidator.ts +38 -0
  55. package/front_end/panels/elements/ElementsTreeElement.ts +108 -58
  56. package/front_end/panels/elements/ElementsTreeOutline.ts +0 -17
  57. package/front_end/panels/elements/ElementsTreeOutlineRenderer.ts +7 -1
  58. package/front_end/panels/elements/StylesSidebarPane.ts +15 -4
  59. package/front_end/panels/elements/components/AdornerManager.ts +8 -0
  60. package/front_end/panels/emulation/DeviceModeToolbar.ts +3 -1
  61. package/front_end/panels/issues/AffectedResourcesView.ts +0 -1
  62. package/front_end/panels/lighthouse/LighthousePanel.ts +10 -0
  63. package/front_end/panels/lighthouse/lighthousePanel.css +46 -3
  64. package/front_end/panels/network/NetworkLogViewColumns.ts +9 -9
  65. package/front_end/panels/network/RequestCookiesView.ts +125 -141
  66. package/front_end/panels/network/components/RequestHeadersView.ts +2 -2
  67. package/front_end/panels/network/requestCookiesView.css +22 -20
  68. package/front_end/panels/recorder/components/RecordingView.ts +3 -3
  69. package/front_end/panels/recorder/components/StepView.ts +2 -1
  70. package/front_end/panels/settings/keybindsSettingsTab.css +4 -0
  71. package/front_end/panels/sources/CallStackSidebarPane.ts +7 -3
  72. package/front_end/panels/sources/DebuggerPausedMessage.ts +125 -90
  73. package/front_end/panels/sources/SourcesPanel.ts +10 -7
  74. package/front_end/panels/sources/debuggerPausedMessage.css +8 -0
  75. package/front_end/panels/timeline/StatusDialog.ts +4 -3
  76. package/front_end/panels/timeline/TimelineFlameChartNetworkDataProvider.ts +3 -16
  77. package/front_end/panels/timeline/TimelineFlameChartView.ts +64 -21
  78. package/front_end/panels/timeline/TimelinePanel.ts +71 -24
  79. package/front_end/panels/timeline/TimelineUIUtils.ts +28 -2
  80. package/front_end/panels/timeline/TimingsTrackAppender.ts +3 -1
  81. package/front_end/panels/timeline/components/SidebarSingleInsightSet.ts +1 -1
  82. package/front_end/panels/timeline/components/insights/RenderBlocking.ts +6 -4
  83. package/front_end/panels/timeline/components/sidebarInsightsTab.css +2 -0
  84. package/front_end/panels/timeline/overlays/OverlaysImpl.ts +4 -0
  85. package/front_end/panels/timeline/timelinePanel.css +8 -1
  86. package/front_end/panels/timeline/utils/EntryNodes.ts +2 -1
  87. package/front_end/third_party/acorn/estree-legacy.d.ts +2 -0
  88. package/front_end/third_party/chromium/README.chromium +1 -1
  89. package/front_end/third_party/puppeteer/README.chromium +2 -2
  90. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Browser.d.ts +12 -0
  91. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Browser.d.ts.map +1 -1
  92. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Browser.js.map +1 -1
  93. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Page.d.ts +14 -2
  94. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Page.d.ts.map +1 -1
  95. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Page.js.map +1 -1
  96. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Browser.d.ts +3 -1
  97. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Browser.d.ts.map +1 -1
  98. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Browser.js +6 -0
  99. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Browser.js.map +1 -1
  100. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/HTTPRequest.d.ts +0 -1
  101. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/HTTPRequest.d.ts.map +1 -1
  102. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/HTTPRequest.js +0 -20
  103. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/HTTPRequest.js.map +1 -1
  104. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Page.d.ts +3 -1
  105. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Page.d.ts.map +1 -1
  106. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Page.js +10 -14
  107. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Page.js.map +1 -1
  108. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/BrowsingContext.d.ts +1 -0
  109. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/BrowsingContext.d.ts.map +1 -1
  110. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/BrowsingContext.js +14 -0
  111. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/BrowsingContext.js.map +1 -1
  112. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.d.ts +3 -1
  113. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.d.ts.map +1 -1
  114. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.js +12 -0
  115. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.js.map +1 -1
  116. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/EmulationManager.d.ts +1 -0
  117. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/EmulationManager.d.ts.map +1 -1
  118. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/EmulationManager.js +22 -0
  119. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/EmulationManager.js.map +1 -1
  120. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.d.ts +3 -1
  121. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.d.ts.map +1 -1
  122. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.js +9 -2
  123. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.js.map +1 -1
  124. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/injected/injected.d.ts +1 -1
  125. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.d.ts +3 -3
  126. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js +3 -3
  127. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js.map +1 -1
  128. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Mutex.d.ts +2 -2
  129. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.d.ts +1 -1
  130. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.js +1 -1
  131. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.d.ts +26 -0
  132. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +72 -15
  133. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Browser.d.ts +12 -0
  134. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Browser.d.ts.map +1 -1
  135. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Browser.js.map +1 -1
  136. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Page.d.ts +14 -2
  137. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Page.d.ts.map +1 -1
  138. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Page.js.map +1 -1
  139. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Browser.d.ts +3 -1
  140. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Browser.d.ts.map +1 -1
  141. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Browser.js +6 -0
  142. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Browser.js.map +1 -1
  143. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/HTTPRequest.d.ts +0 -1
  144. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/HTTPRequest.d.ts.map +1 -1
  145. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/HTTPRequest.js +0 -20
  146. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/HTTPRequest.js.map +1 -1
  147. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Page.d.ts +3 -1
  148. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Page.d.ts.map +1 -1
  149. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Page.js +11 -15
  150. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Page.js.map +1 -1
  151. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/core/BrowsingContext.d.ts +1 -0
  152. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/core/BrowsingContext.d.ts.map +1 -1
  153. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/core/BrowsingContext.js +14 -0
  154. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/core/BrowsingContext.js.map +1 -1
  155. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.d.ts +3 -1
  156. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.d.ts.map +1 -1
  157. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.js +12 -0
  158. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.js.map +1 -1
  159. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/EmulationManager.d.ts +1 -0
  160. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/EmulationManager.d.ts.map +1 -1
  161. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/EmulationManager.js +22 -0
  162. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/EmulationManager.js.map +1 -1
  163. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.d.ts +3 -1
  164. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.d.ts.map +1 -1
  165. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.js +9 -2
  166. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.js.map +1 -1
  167. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.d.ts +3 -3
  168. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js +3 -3
  169. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js.map +1 -1
  170. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.d.ts +1 -1
  171. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.js +1 -1
  172. package/front_end/third_party/puppeteer/package/lib/types.d.ts +26 -0
  173. package/front_end/third_party/puppeteer/package/package.json +1 -1
  174. package/front_end/third_party/puppeteer/package/src/api/Browser.ts +18 -0
  175. package/front_end/third_party/puppeteer/package/src/api/Page.ts +16 -2
  176. package/front_end/third_party/puppeteer/package/src/bidi/Browser.ts +13 -0
  177. package/front_end/third_party/puppeteer/package/src/bidi/HTTPRequest.ts +0 -33
  178. package/front_end/third_party/puppeteer/package/src/bidi/Page.ts +14 -28
  179. package/front_end/third_party/puppeteer/package/src/bidi/core/BrowsingContext.ts +19 -0
  180. package/front_end/third_party/puppeteer/package/src/cdp/Browser.ts +19 -0
  181. package/front_end/third_party/puppeteer/package/src/cdp/EmulationManager.ts +30 -0
  182. package/front_end/third_party/puppeteer/package/src/cdp/Page.ts +15 -6
  183. package/front_end/third_party/puppeteer/package/src/revisions.ts +3 -3
  184. package/front_end/third_party/puppeteer/package/src/util/version.ts +1 -1
  185. package/front_end/ui/components/icon_button/iconButton.css +3 -1
  186. package/front_end/ui/components/report_view/ReportView.ts +11 -2
  187. package/front_end/ui/components/report_view/report.css +16 -0
  188. package/front_end/ui/components/text_editor/AiCodeGenerationProvider.ts +202 -32
  189. package/front_end/ui/components/text_editor/config.ts +6 -6
  190. package/front_end/ui/legacy/ContextMenu.ts +11 -2
  191. package/front_end/ui/legacy/SearchableView.ts +11 -5
  192. package/front_end/ui/legacy/SplitWidget.ts +1 -1
  193. package/front_end/ui/legacy/TextPrompt.ts +1 -1
  194. package/front_end/ui/legacy/Toolbar.ts +4 -0
  195. package/front_end/ui/legacy/UIUtils.ts +0 -2
  196. package/front_end/ui/legacy/components/cookie_table/CookiesTable.ts +18 -3
  197. package/front_end/ui/legacy/components/data_grid/DataGrid.ts +3 -3
  198. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +6 -0
  199. package/front_end/ui/legacy/components/perf_ui/FlameChart.ts +43 -9
  200. package/front_end/ui/visual_logging/KnownContextValues.ts +13 -0
  201. package/package.json +1 -1
@@ -2,6 +2,7 @@
2
2
  // Use of this source code is governed by a BSD-style license that can be
3
3
  // found in the LICENSE file.
4
4
 
5
+ import * as Common from '../../core/common/common.js';
5
6
  import * as SDK from '../../core/sdk/sdk.js';
6
7
  import type * as Protocol from '../../generated/protocol.js';
7
8
 
@@ -32,6 +33,7 @@ export type TranslateRawFrames = (frames: readonly RawFrame[], target: SDK.Targe
32
33
  */
33
34
  export class StackTraceModel extends SDK.SDKModel.SDKModel<unknown> {
34
35
  readonly #trie = new Trie();
36
+ readonly #mutex = new Common.Mutex.Mutex();
35
37
 
36
38
  /** @returns the {@link StackTraceModel} for the target, or the model for the primaryPageTarget when passing null/undefined */
37
39
  static #modelForTarget(target: SDK.Target.Target|null|undefined): StackTraceModel {
@@ -45,7 +47,7 @@ export class StackTraceModel extends SDK.SDKModel.SDKModel<unknown> {
45
47
  async createFromProtocolRuntime(stackTrace: Protocol.Runtime.StackTrace, rawFramesToUIFrames: TranslateRawFrames):
46
48
  Promise<StackTrace.StackTrace.StackTrace> {
47
49
  const [syncFragment, asyncFragments] = await Promise.all([
48
- this.#createSyncFragment(stackTrace, rawFramesToUIFrames),
50
+ this.#createFragment(stackTrace.callFrames, rawFramesToUIFrames),
49
51
  this.#createAsyncFragments(stackTrace, rawFramesToUIFrames),
50
52
  ]);
51
53
 
@@ -65,53 +67,51 @@ export class StackTraceModel extends SDK.SDKModel.SDKModel<unknown> {
65
67
 
66
68
  /** Trigger re-translation of all fragments with the provide script in their call stack */
67
69
  async scriptInfoChanged(script: SDK.Script.Script, translateRawFrames: TranslateRawFrames): Promise<void> {
68
- const translatePromises: Array<Promise<unknown>> = [];
69
- let stackTracesToUpdate = new Set<AnyStackTraceImpl>();
70
-
71
- for (const fragment of this.#affectedFragments(script)) {
72
- // We trigger re-translation only for fragments of leaf-nodes. Any fragment along the ancestor-chain
73
- // is re-translated as a side-effect.
74
- // We just need to remember the stack traces of the skipped over fragments, so we can send the
75
- // UPDATED event also to them.
76
- if (fragment.node.children.length === 0) {
77
- translatePromises.push(this.#translateFragment(fragment, translateRawFrames));
70
+ const release = await this.#mutex.acquire();
71
+ try {
72
+ const translatePromises: Array<Promise<unknown>> = [];
73
+ let stackTracesToUpdate = new Set<AnyStackTraceImpl>();
74
+
75
+ for (const fragment of this.#affectedFragments(script)) {
76
+ // We trigger re-translation only for fragments of leaf-nodes. Any fragment along the ancestor-chain
77
+ // is re-translated as a side-effect.
78
+ // We just need to remember the stack traces of the skipped over fragments, so we can send the
79
+ // UPDATED event also to them.
80
+ if (fragment.node.children.length === 0) {
81
+ translatePromises.push(this.#translateFragment(fragment, translateRawFrames));
82
+ }
83
+ stackTracesToUpdate = stackTracesToUpdate.union(fragment.stackTraces);
78
84
  }
79
- stackTracesToUpdate = stackTracesToUpdate.union(fragment.stackTraces);
80
- }
81
85
 
82
- await Promise.all(translatePromises);
86
+ await Promise.all(translatePromises);
83
87
 
84
- for (const stackTrace of stackTracesToUpdate) {
85
- stackTrace.dispatchEventToListeners(StackTrace.StackTrace.Events.UPDATED);
88
+ for (const stackTrace of stackTracesToUpdate) {
89
+ stackTrace.dispatchEventToListeners(StackTrace.StackTrace.Events.UPDATED);
90
+ }
91
+ } finally {
92
+ release();
86
93
  }
87
94
  }
88
95
 
89
- async #createSyncFragment(stackTrace: Protocol.Runtime.StackTrace, rawFramesToUIFrames: TranslateRawFrames):
90
- Promise<FragmentImpl> {
91
- const fragment = this.#createFragment(stackTrace.callFrames);
92
- await this.#translateFragment(fragment, rawFramesToUIFrames);
93
- return fragment;
94
- }
95
-
96
96
  async #createDebuggableFragment(
97
97
  pausedDetails: SDK.DebuggerModel.DebuggerPausedDetails,
98
98
  rawFramesToUIFrames: TranslateRawFrames): Promise<DebuggableFragmentImpl> {
99
- const fragment = this.#createFragment(pausedDetails.callFrames.map(frame => ({
100
- scriptId: frame.script.scriptId,
101
- url: frame.script.sourceURL,
102
- functionName: frame.functionName,
103
- lineNumber: frame.location().lineNumber,
104
- columnNumber: frame.location().columnNumber,
105
- })));
106
- await this.#translateFragment(fragment, rawFramesToUIFrames);
99
+ const fragment = await this.#createFragment(
100
+ pausedDetails.callFrames.map(frame => ({
101
+ scriptId: frame.script.scriptId,
102
+ url: frame.script.sourceURL,
103
+ functionName: frame.functionName,
104
+ lineNumber: frame.location().lineNumber,
105
+ columnNumber: frame.location().columnNumber,
106
+ })),
107
+ rawFramesToUIFrames);
107
108
  return new DebuggableFragmentImpl(fragment, pausedDetails.callFrames);
108
109
  }
109
110
 
110
111
  async #createAsyncFragments(
111
112
  stackTraceOrPausedEvent: Protocol.Runtime.StackTrace|SDK.DebuggerModel.DebuggerPausedDetails,
112
113
  rawFramesToUIFrames: TranslateRawFrames): Promise<AsyncFragmentImpl[]> {
113
- const asyncFragments: AsyncFragmentImpl[] = [];
114
- const translatePromises: Array<Promise<unknown>> = [];
114
+ const asyncFragments: Array<Promise<AsyncFragmentImpl>> = [];
115
115
 
116
116
  const debuggerModel = this.target().model(SDK.DebuggerModel.DebuggerModel);
117
117
  if (debuggerModel) {
@@ -122,18 +122,31 @@ export class StackTraceModel extends SDK.SDKModel.SDKModel<unknown> {
122
122
  continue;
123
123
  }
124
124
  const model = StackTraceModel.#modelForTarget(target);
125
- const fragment = model.#createFragment(asyncStackTrace.callFrames);
126
- translatePromises.push(model.#translateFragment(fragment, rawFramesToUIFrames));
127
- asyncFragments.push(new AsyncFragmentImpl(asyncStackTrace.description ?? '', fragment));
125
+ const asyncFragmentPromise =
126
+ model.#createFragment(asyncStackTrace.callFrames, rawFramesToUIFrames)
127
+ .then(fragment => new AsyncFragmentImpl(asyncStackTrace.description ?? '', fragment));
128
+ asyncFragments.push(asyncFragmentPromise);
128
129
  }
129
130
  }
130
131
 
131
- await Promise.all(translatePromises);
132
- return asyncFragments;
132
+ return await Promise.all(asyncFragments);
133
133
  }
134
134
 
135
- #createFragment(frames: RawFrame[]): FragmentImpl {
136
- return FragmentImpl.getOrCreate(this.#trie.insert(frames));
135
+ async #createFragment(frames: RawFrame[], rawFramesToUIFrames: TranslateRawFrames): Promise<FragmentImpl> {
136
+ const release = await this.#mutex.acquire();
137
+ try {
138
+ const node = this.#trie.insert(frames);
139
+ const requiresTranslation = !Boolean(node.fragment);
140
+ const fragment = FragmentImpl.getOrCreate(node);
141
+
142
+ if (requiresTranslation) {
143
+ await this.#translateFragment(fragment, rawFramesToUIFrames);
144
+ }
145
+
146
+ return fragment;
147
+ } finally {
148
+ release();
149
+ }
137
150
  }
138
151
 
139
152
  async #translateFragment(fragment: FragmentImpl, rawFramesToUIFrames: TranslateRawFrames): Promise<void> {
@@ -20,9 +20,15 @@ export class EventsSerializer {
20
20
  }
21
21
 
22
22
  const rawEvents = Helpers.SyntheticEvents.SyntheticEventsManager.getActiveManager().getRawTraceEvents();
23
+ const isSynthetic = Types.Events.isSyntheticBased(event);
24
+ const index = rawEvents.indexOf(isSynthetic ? event.rawSourceEvent : event);
25
+ if (index === -1) {
26
+ throw new Error(`Unknown trace event: ${event.name}`);
27
+ }
28
+
23
29
  const key: Types.File.SyntheticEventKey|Types.File.RawEventKey = Types.Events.isSyntheticBased(event) ?
24
- `${Types.File.EventKeyType.SYNTHETIC_EVENT}-${rawEvents.indexOf(event.rawSourceEvent)}` :
25
- `${Types.File.EventKeyType.RAW_EVENT}-${rawEvents.indexOf(event)}`;
30
+ `${Types.File.EventKeyType.SYNTHETIC_EVENT}-${index}` :
31
+ `${Types.File.EventKeyType.RAW_EVENT}-${index}`;
26
32
  if (key.length < 3) {
27
33
  return null;
28
34
  }
@@ -10,14 +10,15 @@ import type * as Types from './types/types.js';
10
10
 
11
11
  type NetworkRequest = Lantern.Types.NetworkRequest<Types.Events.SyntheticNetworkRequest>;
12
12
 
13
- function createProcessedNavigation(data: Handlers.Types.HandlerData, frameId: string, navigationId: string):
14
- Lantern.Types.Simulation.ProcessedNavigation {
13
+ function createProcessedNavigation(
14
+ data: Handlers.Types.HandlerData, frameId: string,
15
+ navigation: Types.Events.NavigationStart): Lantern.Types.Simulation.ProcessedNavigation {
15
16
  const scoresByNav = data.PageLoadMetrics.metricScoresByFrameId.get(frameId);
16
17
  if (!scoresByNav) {
17
18
  throw new Lantern.Core.LanternError('missing metric scores for frame');
18
19
  }
19
20
 
20
- const scores = scoresByNav.get(navigationId);
21
+ const scores = scoresByNav.get(navigation);
21
22
  if (!scores) {
22
23
  throw new Lantern.Core.LanternError('missing metric scores for specified navigation');
23
24
  }
@@ -268,7 +268,8 @@ export class TraceProcessor extends EventTarget {
268
268
 
269
269
  #createLanternContext(
270
270
  data: Handlers.Types.HandlerData, traceEvents: readonly Types.Events.Event[], frameId: string,
271
- navigationId: string, options: Types.Configuration.ParseOptions): Insights.Types.LanternContext|undefined {
271
+ navigation: Types.Events.NavigationStart,
272
+ options: Types.Configuration.ParseOptions): Insights.Types.LanternContext|undefined {
272
273
  // Check for required handlers.
273
274
  if (!data.NetworkRequests || !data.Workers || !data.PageLoadMetrics) {
274
275
  return;
@@ -278,7 +279,7 @@ export class TraceProcessor extends EventTarget {
278
279
  }
279
280
 
280
281
  const navStarts = data.Meta.navigationsByFrameId.get(frameId);
281
- const navStartIndex = navStarts?.findIndex(n => n.args.data?.navigationId === navigationId);
282
+ const navStartIndex = navStarts?.findIndex(n => n === navigation);
282
283
  if (!navStarts || navStartIndex === undefined || navStartIndex === -1) {
283
284
  throw new Lantern.Core.LanternError('Could not find navigation start');
284
285
  }
@@ -295,7 +296,7 @@ export class TraceProcessor extends EventTarget {
295
296
 
296
297
  const requests = LanternComputationData.createNetworkRequests(trace, data, startTime, endTime);
297
298
  const graph = LanternComputationData.createGraph(requests, trace, data);
298
- const processedNavigation = LanternComputationData.createProcessedNavigation(data, frameId, navigationId);
299
+ const processedNavigation = LanternComputationData.createProcessedNavigation(data, frameId, navigation);
299
300
 
300
301
  const networkAnalysis = Lantern.Core.NetworkAnalyzer.analyze(requests);
301
302
  if (!networkAnalysis) {
@@ -455,7 +456,7 @@ export class TraceProcessor extends EventTarget {
455
456
  model.frameId = context.frameId;
456
457
  const navId = context.navigation?.args.data?.navigationId;
457
458
  if (navId) {
458
- model.navigationId = navId;
459
+ model.navigation = context.navigation;
459
460
  }
460
461
  model.createOverlays = () => {
461
462
  // @ts-expect-error: model is a union of all possible insight model types.
@@ -571,7 +572,7 @@ export class TraceProcessor extends EventTarget {
571
572
  let lantern: Insights.Types.LanternContext|undefined;
572
573
  try {
573
574
  options.logger?.start('insights:createLanternContext');
574
- lantern = this.#createLanternContext(data, traceEvents, frameId, navigationId, options);
575
+ lantern = this.#createLanternContext(data, traceEvents, frameId, navigation, options);
575
576
  } catch (e) {
576
577
  // Handle Lantern errors gracefully
577
578
  // Don't allow an error in constructing the Lantern graphs to break the rest of the trace processor.
@@ -879,6 +879,11 @@ export function maybeInitSylesMap(): EventStylesMap {
879
879
  defaultCategoryStyles.rendering,
880
880
  true,
881
881
  ),
882
+ [Types.Events.Name.MARK_LCP_CANDIDATE_FOR_SOFT_NAVIGATION]: new TimelineRecordStyle(
883
+ i18nString(UIStrings.largestContentfulPaint),
884
+ defaultCategoryStyles.rendering,
885
+ true,
886
+ ),
882
887
 
883
888
  [Types.Events.Name.TIME_STAMP]:
884
889
  new TimelineRecordStyle(i18nString(UIStrings.timestamp), defaultCategoryStyles.scripting),
@@ -1118,7 +1123,7 @@ export function markerDetailsForEvent(event: Types.Events.Event): {
1118
1123
  color = 'var(--sys-color-green-bright)';
1119
1124
  title = Handlers.ModelHandlers.PageLoadMetrics.MetricName.FCP;
1120
1125
  }
1121
- if (Types.Events.isLargestContentfulPaintCandidate(event)) {
1126
+ if (Types.Events.isAnyLargestContentfulPaintCandidate(event)) {
1122
1127
  color = 'var(--sys-color-green)';
1123
1128
  title = Handlers.ModelHandlers.PageLoadMetrics.MetricName.LCP;
1124
1129
  }
@@ -1126,6 +1131,10 @@ export function markerDetailsForEvent(event: Types.Events.Event): {
1126
1131
  color = 'var(--color-text-primary)';
1127
1132
  title = Handlers.ModelHandlers.PageLoadMetrics.MetricName.NAV;
1128
1133
  }
1134
+ if (Types.Events.isSoftNavigationStart(event)) {
1135
+ color = 'var(--sys-color-blue)';
1136
+ title = Handlers.ModelHandlers.PageLoadMetrics.MetricName.SOFT_NAV;
1137
+ }
1129
1138
  if (Types.Events.isMarkDOMContent(event)) {
1130
1139
  color = 'var(--color-text-disabled)';
1131
1140
  title = Handlers.ModelHandlers.PageLoadMetrics.MetricName.DCL;
@@ -55,9 +55,9 @@ export async function finalize(): Promise<void> {
55
55
  const metricScoresByFrameId = pageLoadMetricsData().metricScoresByFrameId;
56
56
 
57
57
  for (const [navigationId, navigation] of navigationsByNavigationId) {
58
- const lcpMetric = metricScoresByFrameId.get(navigation.args.frame)?.get(navigationId)?.get(MetricName.LCP);
58
+ const lcpMetric = metricScoresByFrameId.get(navigation.args.frame)?.get(navigation)?.get(MetricName.LCP);
59
59
  const lcpEvent = lcpMetric?.event;
60
- if (!lcpEvent || !Types.Events.isLargestContentfulPaintCandidate(lcpEvent)) {
60
+ if (!lcpEvent || !Types.Events.isAnyLargestContentfulPaintCandidate(lcpEvent)) {
61
61
  continue;
62
62
  }
63
63
 
@@ -443,7 +443,7 @@ async function buildLayoutShiftsClusters(): Promise<void> {
443
443
  }
444
444
 
445
445
  let largestScore = 0;
446
- let worstShiftEvent: Types.Events.Event|null = null;
446
+ let worstShiftEvent: Types.Events.SyntheticLayoutShift|null = null;
447
447
 
448
448
  for (const shift of cluster.events) {
449
449
  weightedScore += shift.args.data ? shift.args.data.weighted_score_delta : 0;
@@ -504,7 +504,7 @@ async function buildLayoutShiftsClusters(): Promise<void> {
504
504
  // Update the cluster's worst layout shift.
505
505
  if (worstShiftEvent) {
506
506
  cluster.worstShiftEvent = worstShiftEvent;
507
- cluster.rawSourceEvent = worstShiftEvent;
507
+ cluster.rawSourceEvent = worstShiftEvent.rawSourceEvent;
508
508
  }
509
509
 
510
510
  // layout shifts are already sorted by time ascending.
@@ -59,6 +59,7 @@ let traceBounds: Types.Timing.TraceWindowMicro = makeNewTraceBounds();
59
59
  */
60
60
  let navigationsByFrameId = new Map<string, Types.Events.NavigationStart[]>();
61
61
  let navigationsByNavigationId = new Map<string, Types.Events.NavigationStart>();
62
+ let softNavigationsById = new Map<number, Types.Events.SoftNavigationStart>();
62
63
  let finalDisplayUrlByNavigationId = new Map<string, string>();
63
64
  let mainFrameNavigations: Types.Events.NavigationStart[] = [];
64
65
 
@@ -92,6 +93,7 @@ const CHROME_WEB_TRACE_EVENTS = new Set([
92
93
  export function reset(): void {
93
94
  navigationsByFrameId = new Map();
94
95
  navigationsByNavigationId = new Map();
96
+ softNavigationsById = new Map();
95
97
  finalDisplayUrlByNavigationId = new Map();
96
98
  processNames = new Map();
97
99
  mainFrameNavigations = [];
@@ -324,6 +326,10 @@ export function handleEvent(event: Types.Events.Event): void {
324
326
  return;
325
327
  }
326
328
 
329
+ if (Types.Events.isSoftNavigationStart(event)) {
330
+ softNavigationsById.set(event.args.context.performanceTimelineNavigationId, event);
331
+ }
332
+
327
333
  // Update `finalDisplayUrlByNavigationId` to reflect the latest redirect for each navigation.
328
334
  if (Types.Events.isResourceSendRequest(event)) {
329
335
  if (event.args.data.resourceType !== 'Document') {
@@ -445,7 +451,14 @@ export interface MetaHandlerData {
445
451
  browserThreadId: Types.Events.ThreadID;
446
452
  gpuProcessId: Types.Events.ProcessID;
447
453
  navigationsByFrameId: Map<string, Types.Events.NavigationStart[]>;
454
+ /**
455
+ * This does not include soft navigations.
456
+ *
457
+ * TODO(crbug.com/414468047): include soft navs here, so that
458
+ * PageLoadMetricsHandler and insights can use this map for all navigation types.
459
+ */
448
460
  navigationsByNavigationId: Map<string, Types.Events.NavigationStart>;
461
+ softNavigationsById: Map<number, Types.Events.SoftNavigationStart>;
449
462
  /**
450
463
  * The user-visible URL displayed to users in the address bar.
451
464
  * This captures:
@@ -514,6 +527,7 @@ export function data(): MetaHandlerData {
514
527
  mainFrameURL,
515
528
  navigationsByFrameId,
516
529
  navigationsByNavigationId,
530
+ softNavigationsById,
517
531
  finalDisplayUrlByNavigationId,
518
532
  threadsInProcess,
519
533
  rendererProcessesByFrame: rendererProcessesByFrameId,
@@ -5,7 +5,9 @@
5
5
  /**
6
6
  * This handler stores page load metrics, including web vitals,
7
7
  * and exports them in the shape of a map with the following shape:
8
- * Map(FrameId -> Map(navigationID -> metrics) )
8
+ * Map(FrameId -> Map(navigation -> metrics) )
9
+ *
10
+ * Includes soft navigations.
9
11
  *
10
12
  * It also exports all markers in a trace in an array.
11
13
  *
@@ -22,14 +24,15 @@ import type {HandlerName} from './types.js';
22
24
 
23
25
  // Small helpers to make the below type easier to read.
24
26
  type FrameId = string;
25
- type NavigationId = string;
27
+ type AnyNavigationStart = Types.Events.NavigationStart|Types.Events.SoftNavigationStart;
28
+
26
29
  /**
27
30
  * This represents the metric scores for all navigations, for all frames in a trace.
28
31
  * Given a frame id, the map points to another map from navigation id to metric scores.
29
32
  * The metric scores include the event related to the metric as well as the data regarding
30
33
  * the score itself.
31
34
  */
32
- let metricScoresByFrameId = new Map<FrameId, Map<NavigationId, Map<MetricName, MetricScore>>>();
35
+ let metricScoresByFrameId = new Map<FrameId, Map<AnyNavigationStart, Map<MetricName, MetricScore>>>();
33
36
 
34
37
  /**
35
38
  * Page load events with no associated duration that happened in the
@@ -54,7 +57,7 @@ let pageLoadEventsArray: Types.Events.PageLoadEvent[] = [];
54
57
  // trace, we store that and delete the prior event. When we've parsed the
55
58
  // entire trace this set will contain all the LCP events that were used - e.g.
56
59
  // the candidates that were the actual LCP events.
57
- let selectedLCPCandidateEvents = new Set<Types.Events.LargestContentfulPaintCandidate>();
60
+ let selectedLCPCandidateEvents = new Set<Types.Events.AnyLargestContentfulPaintCandidate>();
58
61
 
59
62
  export function handleEvent(event: Types.Events.Event): void {
60
63
  if (!Types.Events.eventIsPageLoadEvent(event)) {
@@ -64,11 +67,7 @@ export function handleEvent(event: Types.Events.Event): void {
64
67
  }
65
68
 
66
69
  function storePageLoadMetricAgainstNavigationId(
67
- navigation: Types.Events.NavigationStart, event: Types.Events.PageLoadEvent): void {
68
- const navigationId = navigation.args.data?.navigationId;
69
- if (!navigationId) {
70
- throw new Error('Navigation event unexpectedly had no navigation ID.');
71
- }
70
+ navigation: AnyNavigationStart, event: Types.Events.PageLoadEvent): void {
72
71
  const frameId = getFrameIdForPageLoadEvent(event);
73
72
  const {rendererProcessesByFrame} = metaHandlerData();
74
73
 
@@ -95,7 +94,7 @@ function storePageLoadMetricAgainstNavigationId(
95
94
  const fcpTime = Types.Timing.Micro(event.ts - navigation.ts);
96
95
  const classification = scoreClassificationForFirstContentfulPaint(fcpTime);
97
96
  const metricScore = {event, metricName: MetricName.FCP, classification, navigation, timing: fcpTime};
98
- storeMetricScore(frameId, navigationId, metricScore);
97
+ storeMetricScore(frameId, navigation, metricScore);
99
98
  return;
100
99
  }
101
100
 
@@ -103,7 +102,7 @@ function storePageLoadMetricAgainstNavigationId(
103
102
  const paintTime = Types.Timing.Micro(event.ts - navigation.ts);
104
103
  const classification = ScoreClassification.UNCLASSIFIED;
105
104
  const metricScore = {event, metricName: MetricName.FP, classification, navigation, timing: paintTime};
106
- storeMetricScore(frameId, navigationId, metricScore);
105
+ storeMetricScore(frameId, navigation, metricScore);
107
106
  return;
108
107
  }
109
108
 
@@ -116,7 +115,7 @@ function storePageLoadMetricAgainstNavigationId(
116
115
  navigation,
117
116
  timing: dclTime,
118
117
  };
119
- storeMetricScore(frameId, navigationId, metricScore);
118
+ storeMetricScore(frameId, navigation, metricScore);
120
119
  return;
121
120
  }
122
121
 
@@ -129,7 +128,7 @@ function storePageLoadMetricAgainstNavigationId(
129
128
  navigation,
130
129
  timing: ttiValue,
131
130
  };
132
- storeMetricScore(frameId, navigationId, tti);
131
+ storeMetricScore(frameId, navigation, tti);
133
132
 
134
133
  const tbtValue = Helpers.Timing.milliToMicro(Types.Timing.Milli(event.args.args.total_blocking_time_ms));
135
134
  const tbt = {
@@ -139,7 +138,7 @@ function storePageLoadMetricAgainstNavigationId(
139
138
  navigation,
140
139
  timing: tbtValue,
141
140
  };
142
- storeMetricScore(frameId, navigationId, tbt);
141
+ storeMetricScore(frameId, navigation, tbt);
143
142
  return;
144
143
  }
145
144
 
@@ -152,11 +151,11 @@ function storePageLoadMetricAgainstNavigationId(
152
151
  navigation,
153
152
  timing: loadTime,
154
153
  };
155
- storeMetricScore(frameId, navigationId, metricScore);
154
+ storeMetricScore(frameId, navigation, metricScore);
156
155
  return;
157
156
  }
158
157
 
159
- if (Types.Events.isLargestContentfulPaintCandidate(event)) {
158
+ if (Types.Events.isAnyLargestContentfulPaintCandidate(event)) {
160
159
  const candidateIndex = event.args.data?.candidateIndex;
161
160
  if (!candidateIndex) {
162
161
  throw new Error('Largest Contentful Paint unexpectedly had no candidateIndex.');
@@ -170,16 +169,16 @@ function storePageLoadMetricAgainstNavigationId(
170
169
  timing: lcpTime,
171
170
  };
172
171
  const metricsByNavigation = Platform.MapUtilities.getWithDefault(metricScoresByFrameId, frameId, () => new Map());
173
- const metrics = Platform.MapUtilities.getWithDefault(metricsByNavigation, navigationId, () => new Map());
172
+ const metrics = Platform.MapUtilities.getWithDefault(metricsByNavigation, navigation, () => new Map());
174
173
  const lastLCPCandidate = metrics.get(MetricName.LCP);
175
174
  if (lastLCPCandidate === undefined) {
176
175
  selectedLCPCandidateEvents.add(lcp.event);
177
- storeMetricScore(frameId, navigationId, lcp);
176
+ storeMetricScore(frameId, navigation, lcp);
178
177
  return;
179
178
  }
180
179
  const lastLCPCandidateEvent = lastLCPCandidate.event;
181
180
 
182
- if (!Types.Events.isLargestContentfulPaintCandidate(lastLCPCandidateEvent)) {
181
+ if (!Types.Events.isAnyLargestContentfulPaintCandidate(lastLCPCandidateEvent)) {
183
182
  return;
184
183
  }
185
184
  const lastCandidateIndex = lastLCPCandidateEvent.args.data?.candidateIndex;
@@ -192,19 +191,22 @@ function storePageLoadMetricAgainstNavigationId(
192
191
  if (lastCandidateIndex < candidateIndex) {
193
192
  selectedLCPCandidateEvents.delete(lastLCPCandidateEvent);
194
193
  selectedLCPCandidateEvents.add(lcp.event);
195
- storeMetricScore(frameId, navigationId, lcp);
194
+ storeMetricScore(frameId, navigation, lcp);
196
195
  }
197
196
  return;
198
197
  }
199
198
  if (Types.Events.isLayoutShift(event)) {
200
199
  return;
201
200
  }
201
+ if (Types.Events.isSoftNavigationStart(event)) {
202
+ return;
203
+ }
202
204
  return Platform.assertNever(event, `Unexpected event type: ${event}`);
203
205
  }
204
206
 
205
- function storeMetricScore(frameId: string, navigationId: string, metricScore: MetricScore): void {
207
+ function storeMetricScore(frameId: string, navigation: AnyNavigationStart, metricScore: MetricScore): void {
206
208
  const metricsByNavigation = Platform.MapUtilities.getWithDefault(metricScoresByFrameId, frameId, () => new Map());
207
- const metrics = Platform.MapUtilities.getWithDefault(metricsByNavigation, navigationId, () => new Map());
209
+ const metrics = Platform.MapUtilities.getWithDefault(metricsByNavigation, navigation, () => new Map());
208
210
  // If an entry with that metric name is present, delete it so that the new entry that
209
211
  // will replace it is added at the end of the map. This way we guarantee the map entries
210
212
  // are ordered in ASC manner by timestamp.
@@ -214,8 +216,9 @@ function storeMetricScore(frameId: string, navigationId: string, metricScore: Me
214
216
 
215
217
  export function getFrameIdForPageLoadEvent(event: Types.Events.PageLoadEvent): string {
216
218
  if (Types.Events.isFirstContentfulPaint(event) || Types.Events.isInteractiveTime(event) ||
217
- Types.Events.isLargestContentfulPaintCandidate(event) || Types.Events.isNavigationStart(event) ||
218
- Types.Events.isLayoutShift(event) || Types.Events.isFirstPaint(event)) {
219
+ Types.Events.isAnyLargestContentfulPaintCandidate(event) || Types.Events.isNavigationStart(event) ||
220
+ Types.Events.isSoftNavigationStart(event) || Types.Events.isLayoutShift(event) ||
221
+ Types.Events.isFirstPaint(event)) {
219
222
  return event.args.frame;
220
223
  }
221
224
  if (Types.Events.isMarkDOMContent(event) || Types.Events.isMarkLoad(event)) {
@@ -228,16 +231,23 @@ export function getFrameIdForPageLoadEvent(event: Types.Events.PageLoadEvent): s
228
231
  Platform.assertNever(event, `Unexpected event type: ${event}`);
229
232
  }
230
233
 
231
- function getNavigationForPageLoadEvent(event: Types.Events.PageLoadEvent): Types.Events.NavigationStart|null {
232
- if (Types.Events.isFirstContentfulPaint(event) || Types.Events.isLargestContentfulPaintCandidate(event) ||
234
+ function getNavigationForPageLoadEvent(event: Types.Events.PageLoadEvent): AnyNavigationStart|null {
235
+ if (Types.Events.isFirstContentfulPaint(event) || Types.Events.isAnyLargestContentfulPaintCandidate(event) ||
233
236
  Types.Events.isFirstPaint(event)) {
234
- const navigationId = event.args.data?.navigationId;
235
- if (!navigationId) {
236
- throw new Error('Trace event unexpectedly had no navigation ID.');
237
+ const {navigationsByNavigationId, softNavigationsById} = metaHandlerData();
238
+ let navigation;
239
+ if (event.name === Types.Events.Name.MARK_LCP_CANDIDATE_FOR_SOFT_NAVIGATION &&
240
+ event.args.data?.performanceTimelineNavigationId) {
241
+ navigation = softNavigationsById.get(event.args.data.performanceTimelineNavigationId);
237
242
  }
238
- const {navigationsByNavigationId} = metaHandlerData();
239
- const navigation = navigationsByNavigationId.get(navigationId);
243
+ if (!navigation) {
244
+ const navigationId = event.args.data?.navigationId;
245
+ if (!navigationId) {
246
+ throw new Error('Trace event unexpectedly had no navigation ID.');
247
+ }
240
248
 
249
+ navigation = navigationsByNavigationId.get(navigationId);
250
+ }
241
251
  if (!navigation) {
242
252
  // This event's navigation has been filtered out by the meta handler as a noise event.
243
253
  return null;
@@ -245,6 +255,11 @@ function getNavigationForPageLoadEvent(event: Types.Events.PageLoadEvent): Types
245
255
  return navigation;
246
256
  }
247
257
 
258
+ if (Types.Events.isSoftNavigationStart(event)) {
259
+ const {softNavigationsById} = metaHandlerData();
260
+ return softNavigationsById.get(event.args.context.performanceTimelineNavigationId) ?? null;
261
+ }
262
+
248
263
  if (Types.Events.isMarkDOMContent(event) || Types.Events.isInteractiveTime(event) ||
249
264
  Types.Events.isLayoutShift(event) || Types.Events.isMarkLoad(event)) {
250
265
  const frameId = getFrameIdForPageLoadEvent(event);
@@ -378,7 +393,8 @@ export async function finalize(): Promise<void> {
378
393
  const allFinalLCPEvents = gatherFinalLCPEvents();
379
394
  const mainFrame = metaHandlerData().mainFrameId;
380
395
  // Filter out LCP candidates to use only definitive LCP values
381
- const allEventsButLCP = pageLoadEventsArray.filter(event => !Types.Events.isLargestContentfulPaintCandidate(event));
396
+ const allEventsButLCP =
397
+ pageLoadEventsArray.filter(event => !Types.Events.isAnyLargestContentfulPaintCandidate(event));
382
398
  const markerEvents = [...allFinalLCPEvents, ...allEventsButLCP].filter(Types.Events.isMarkerEvent);
383
399
  // Filter by main frame and sort.
384
400
  allMarkerEvents =
@@ -391,8 +407,10 @@ export interface PageLoadMetricsData {
391
407
  * Given a frame id, the map points to another map from navigation id to metric scores.
392
408
  * The metric scores include the event related to the metric as well as the data regarding
393
409
  * the score itself.
410
+ *
411
+ * Includes soft navigations.
394
412
  */
395
- metricScoresByFrameId: Map<string, Map<string, Map<MetricName, MetricScore>>>;
413
+ metricScoresByFrameId: Map<string, Map<AnyNavigationStart, Map<MetricName, MetricScore>>>;
396
414
  /**
397
415
  * Page load events with no associated duration that happened in the
398
416
  * main frame.
@@ -437,6 +455,8 @@ export const enum MetricName {
437
455
  CLS = 'CLS',
438
456
  // Navigation
439
457
  NAV = 'Nav',
458
+ // Soft Navigation (just "Nav" b/c space is limited in flame chart)
459
+ SOFT_NAV = 'Nav',
440
460
  // Note: INP is handled in UserInteractionsHandler
441
461
  }
442
462
 
@@ -445,7 +465,7 @@ export interface MetricScore {
445
465
  classification: ScoreClassification;
446
466
  event?: Types.Events.PageLoadEvent;
447
467
  // The last navigation that occurred before this metric score.
448
- navigation?: Types.Events.NavigationStart;
468
+ navigation?: AnyNavigationStart;
449
469
  estimated?: boolean;
450
470
  timing: Types.Timing.Micro;
451
471
  }
@@ -22,10 +22,17 @@ export function timeStampForEventAdjustedByClosestNavigation(
22
22
  event: Types.Events.Event,
23
23
  traceBounds: Types.Timing.TraceWindowMicro,
24
24
  navigationsByNavigationId: Map<string, Types.Events.NavigationStart>,
25
+ softNavigationsById: Map<number, Types.Events.SoftNavigationStart>,
25
26
  navigationsByFrameId: Map<string, Types.Events.NavigationStart[]>,
26
27
  ): Types.Timing.Micro {
27
28
  let eventTimeStamp = event.ts - traceBounds.min;
28
- if (event.args?.data?.navigationId) {
29
+ if (event.name === Types.Events.Name.MARK_LCP_CANDIDATE_FOR_SOFT_NAVIGATION &&
30
+ event.args?.data?.performanceTimelineNavigationId) {
31
+ const navigationForEvent = softNavigationsById.get(event.args.data.performanceTimelineNavigationId);
32
+ if (navigationForEvent) {
33
+ eventTimeStamp = event.ts - navigationForEvent.ts;
34
+ }
35
+ } else if (event.args?.data?.navigationId) {
29
36
  const navigationForEvent = navigationsByNavigationId.get(event.args.data.navigationId);
30
37
  if (navigationForEvent) {
31
38
  eventTimeStamp = event.ts - navigationForEvent.ts;
@@ -27,7 +27,7 @@ export function getInsight<InsightName extends keyof InsightModels>(
27
27
  }
28
28
 
29
29
  export function getLCP(insightSet: InsightSet):
30
- {value: Types.Timing.Micro, event: Types.Events.LargestContentfulPaintCandidate}|null {
30
+ {value: Types.Timing.Micro, event: Types.Events.AnyLargestContentfulPaintCandidate}|null {
31
31
  const insight = getInsight(InsightKeys.LCP_BREAKDOWN, insightSet);
32
32
  if (!insight || !insight.lcpMs || !insight.lcpEvent) {
33
33
  return null;
@@ -95,7 +95,7 @@ export function isLCPBreakdownInsight(model: InsightModel): model is LCPBreakdow
95
95
  export type LCPBreakdownInsightModel = InsightModel<typeof UIStrings, {
96
96
  lcpMs?: Types.Timing.Milli,
97
97
  lcpTs?: Types.Timing.Milli,
98
- lcpEvent?: Types.Events.LargestContentfulPaintCandidate,
98
+ lcpEvent?: Types.Events.AnyLargestContentfulPaintCandidate,
99
99
  /** The network request for the LCP image, if there was one. */
100
100
  lcpRequest?: Types.Events.SyntheticNetworkRequest,
101
101
  subparts?: LCPSubparts,
@@ -112,7 +112,7 @@ function anyValuesNaN(...values: number[]): boolean {
112
112
  */
113
113
  function determineSubparts(
114
114
  nav: Types.Events.NavigationStart, docRequest: Types.Events.SyntheticNetworkRequest,
115
- lcpEvent: Types.Events.LargestContentfulPaintCandidate,
115
+ lcpEvent: Types.Events.AnyLargestContentfulPaintCandidate,
116
116
  lcpRequest: Types.Events.SyntheticNetworkRequest|undefined): LCPSubparts|null {
117
117
  const firstDocByteTs = calculateDocFirstByteTs(docRequest);
118
118
  if (firstDocByteTs === null) {
@@ -218,13 +218,13 @@ export function generateInsight(
218
218
  throw new Error('no frame metrics');
219
219
  }
220
220
 
221
- const navMetrics = frameMetrics.get(context.navigationId);
221
+ const navMetrics = frameMetrics.get(context.navigation);
222
222
  if (!navMetrics) {
223
223
  throw new Error('no navigation metrics');
224
224
  }
225
225
  const metricScore = navMetrics.get(Handlers.ModelHandlers.PageLoadMetrics.MetricName.LCP);
226
226
  const lcpEvent = metricScore?.event;
227
- if (!lcpEvent || !Types.Events.isLargestContentfulPaintCandidate(lcpEvent)) {
227
+ if (!lcpEvent || !Types.Events.isAnyLargestContentfulPaintCandidate(lcpEvent)) {
228
228
  return finalize({warnings: [InsightWarning.NO_LCP]});
229
229
  }
230
230