chrome-devtools-frontend 1.0.1669021 → 1.0.1672557

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 (302) hide show
  1. package/.agents/skills/fixing-skipped-tests/SKILL.md +6 -6
  2. package/.agents/skills/gerrit-cli/SKILL.md +4 -10
  3. package/.agents/skills/gerrit-cli/references/commands.md +45 -2
  4. package/.agents/skills/gerrit-cli/references/workflows.md +7 -0
  5. package/.agents/skills/gerrit-cli/scripts/import_upstream.py +34 -152
  6. package/config/typescript/tsconfig.eslint.json +1 -1
  7. package/docs/contributing/settings-experiments-features.md +2 -8
  8. package/docs/ecosystem/automatic_workspace_folders.md +0 -19
  9. package/eslint.config.mjs +5 -5
  10. package/front_end/core/common/Lazy.ts +3 -3
  11. package/front_end/core/common/SettingRegistration.ts +0 -7
  12. package/front_end/core/common/Settings.ts +0 -38
  13. package/front_end/core/host/UserMetrics.ts +187 -953
  14. package/front_end/core/host/UserMetricsEnums.ts +949 -0
  15. package/front_end/core/root/Runtime.ts +21 -138
  16. package/front_end/core/sdk/CSSMetadata.ts +70 -65
  17. package/front_end/core/sdk/CategorizedBreakpoint.ts +0 -1
  18. package/front_end/core/sdk/ChildTargetManager.ts +0 -2
  19. package/front_end/core/sdk/EmulationModel.ts +0 -2
  20. package/front_end/core/sdk/EventBreakpointsModel.ts +0 -4
  21. package/front_end/core/sdk/NetworkManager.ts +0 -3
  22. package/front_end/core/sdk/PageResourceLoader.ts +9 -9
  23. package/front_end/core/sdk/RehydratingConnection.ts +52 -4
  24. package/front_end/core/sdk/Target.ts +0 -4
  25. package/front_end/devtools_compatibility.js +886 -0
  26. package/front_end/entrypoints/inspector_main/RenderingOptions.ts +7 -6
  27. package/front_end/entrypoints/main/ExecutionContextSelector.ts +0 -1
  28. package/front_end/entrypoints/main/MainImpl.ts +18 -22
  29. package/front_end/foundation/Universe.ts +7 -2
  30. package/front_end/generated/InspectorBackendCommands.ts +3 -10
  31. package/front_end/generated/{SupportedCSSProperties.js → SupportedCSSProperties.ts} +16 -0
  32. package/front_end/generated/protocol-mapping.d.ts +0 -42
  33. package/front_end/generated/protocol-proxy-api.d.ts +0 -38
  34. package/front_end/generated/protocol.ts +9 -169
  35. package/front_end/models/ai_assistance/AiAgent2.ts +11 -3
  36. package/front_end/models/ai_assistance/AiConversation.ts +1 -0
  37. package/front_end/models/ai_assistance/AiHistoryStorage.ts +8 -5
  38. package/front_end/models/ai_assistance/AiSetting.ts +108 -0
  39. package/front_end/models/ai_assistance/AiUtils.ts +47 -0
  40. package/front_end/models/ai_assistance/ChangeManager.ts +1 -0
  41. package/front_end/models/ai_assistance/ExtensionScope.ts +3 -12
  42. package/front_end/models/ai_assistance/agents/AiAgent.ts +11 -18
  43. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +3 -0
  44. package/front_end/models/ai_assistance/agents/ExecuteJavascript.ts +42 -17
  45. package/front_end/models/ai_assistance/agents/PerformanceAgent.snapshot.txt +1 -1
  46. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +3 -2
  47. package/front_end/models/ai_assistance/agents/StorageAgent.ts +253 -155
  48. package/front_end/models/ai_assistance/agents/StylingAgent.snapshot.txt +2 -2
  49. package/front_end/models/ai_assistance/ai_assistance.ts +4 -0
  50. package/front_end/models/ai_assistance/contexts/PerformanceTraceContext.ts +12 -6
  51. package/front_end/models/ai_assistance/data_formatters/FileFormatter.ts +3 -1
  52. package/front_end/models/ai_assistance/data_formatters/NetworkRequestFormatter.ts +1 -0
  53. package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.ts +4 -3
  54. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +1 -1
  55. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.ts +1 -0
  56. package/front_end/models/ai_assistance/injected.ts +1 -0
  57. package/front_end/models/ai_assistance/performance/AICallTree.ts +1 -0
  58. package/front_end/models/ai_assistance/skills/Skill.ts +1 -1
  59. package/front_end/models/ai_assistance/skills/SkillRegistry.ts +2 -0
  60. package/front_end/models/ai_assistance/skills/performance.md +10 -0
  61. package/front_end/models/ai_assistance/tools/ExecuteJavaScript.ts +52 -5
  62. package/front_end/models/ai_assistance/tools/GetElementAccessibilityDetails.ts +4 -4
  63. package/front_end/models/ai_assistance/tools/GetLighthouseAudits.ts +4 -4
  64. package/front_end/models/ai_assistance/tools/GetNetworkRequestDetails.ts +5 -4
  65. package/front_end/models/ai_assistance/tools/GetStyles.ts +5 -4
  66. package/front_end/models/ai_assistance/tools/ListNetworkRequests.ts +5 -4
  67. package/front_end/models/ai_assistance/tools/README.md +1 -1
  68. package/front_end/models/ai_assistance/tools/RecordPerformanceTrace.ts +63 -0
  69. package/front_end/models/ai_assistance/tools/ResolveDevtoolsNodePath.ts +5 -5
  70. package/front_end/models/ai_assistance/tools/Tool.ts +118 -24
  71. package/front_end/models/ai_assistance/tools/ToolRegistry.ts +2 -0
  72. package/front_end/models/badges/UserBadges.ts +2 -0
  73. package/front_end/models/bindings/NetworkProject.ts +5 -0
  74. package/front_end/models/bindings/ResourceScriptMapping.ts +2 -0
  75. package/front_end/models/bindings/ResourceUtils.ts +3 -0
  76. package/front_end/models/crux-manager/CrUXManager.ts +1 -0
  77. package/front_end/models/emulation/DeviceModeModel.ts +140 -7
  78. package/front_end/models/emulation/EmulatedDevices.ts +246 -793
  79. package/front_end/models/issues_manager/IssuesManager.ts +10 -2
  80. package/front_end/models/live-metrics/LiveMetrics.ts +41 -9
  81. package/front_end/models/logs/LogManager.ts +8 -5
  82. package/front_end/models/logs/NetworkLog.ts +1 -0
  83. package/front_end/models/persistence/IsolatedFileSystemManager.ts +21 -3
  84. package/front_end/models/persistence/NetworkPersistenceManager.ts +6 -0
  85. package/front_end/models/project_settings/ProjectSettingsModel.ts +8 -12
  86. package/front_end/models/trace/Processor.ts +42 -10
  87. package/front_end/models/trace/handlers/LayoutShiftsHandler.ts +12 -19
  88. package/front_end/models/trace/handlers/MetaHandler.ts +38 -5
  89. package/front_end/models/trace/insights/CLSCulprits.ts +1 -2
  90. package/front_end/models/trace/insights/CharacterSet.ts +1 -1
  91. package/front_end/models/trace/insights/Common.ts +21 -2
  92. package/front_end/models/trace/insights/DocumentLatency.ts +1 -1
  93. package/front_end/models/trace/insights/LCPBreakdown.ts +7 -6
  94. package/front_end/models/trace/insights/LCPDiscovery.ts +1 -1
  95. package/front_end/models/trace/insights/ModernHTTP.ts +7 -3
  96. package/front_end/models/trace/insights/NetworkDependencyTree.ts +1 -1
  97. package/front_end/models/trace/insights/RenderBlocking.ts +1 -1
  98. package/front_end/models/trace/insights/ThirdParties.ts +5 -2
  99. package/front_end/models/trace/insights/types.ts +11 -6
  100. package/front_end/models/trace/types/Configuration.ts +5 -0
  101. package/front_end/models/trace_source_maps_resolver/SourceMapsResolver.ts +3 -1
  102. package/front_end/models/workspace/IgnoreListManager.ts +2 -0
  103. package/front_end/models/workspace/UISourceCode.ts +2 -0
  104. package/front_end/models/workspace_diff/WorkspaceDiff.ts +4 -0
  105. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +22 -22
  106. package/front_end/panels/ai_assistance/ai_assistance-meta.ts +0 -7
  107. package/front_end/panels/ai_assistance/components/ChatInput.ts +4 -0
  108. package/front_end/panels/ai_assistance/components/ChatMessage.ts +79 -34
  109. package/front_end/panels/ai_assistance/components/WalkthroughView.ts +1 -2
  110. package/front_end/panels/ai_assistance/components/walkthroughView.css +1 -1
  111. package/front_end/panels/application/ApplicationPanelSidebar.ts +9 -106
  112. package/front_end/panels/application/ApplicationPanelTreeElement.ts +1 -1
  113. package/front_end/panels/application/application.ts +0 -10
  114. package/front_end/panels/application/components/components.ts +0 -4
  115. package/front_end/panels/browser_debugger/CategorizedBreakpointsSidebarPane.ts +0 -5
  116. package/front_end/panels/browser_debugger/EventListenerBreakpointsSidebarPane.ts +6 -12
  117. package/front_end/panels/browser_debugger/browser_debugger-meta.ts +5 -3
  118. package/front_end/panels/console/ConsoleFormat.ts +2 -62
  119. package/front_end/panels/console/ConsoleViewMessage.ts +14 -7
  120. package/front_end/panels/elements/ElementsTreeElement.ts +1 -2
  121. package/front_end/panels/emulation/AdvancedApp.ts +1 -2
  122. package/front_end/panels/emulation/DeviceModeToolbar.ts +83 -69
  123. package/front_end/panels/emulation/DeviceModeView.ts +4 -134
  124. package/front_end/panels/emulation/DeviceModeWrapper.ts +52 -69
  125. package/front_end/panels/emulation/MediaQueryInspector.ts +2 -2
  126. package/front_end/panels/emulation/emulation-meta.ts +12 -12
  127. package/front_end/panels/issues/IssuesPane.ts +3 -1
  128. package/front_end/panels/lighthouse/LighthouseController.ts +6 -0
  129. package/front_end/panels/linear_memory_inspector/components/LinearMemoryInspector.ts +17 -21
  130. package/front_end/panels/linear_memory_inspector/components/LinearMemoryNavigator.ts +206 -126
  131. package/front_end/panels/linear_memory_inspector/components/linearMemoryInspector.css +1 -1
  132. package/front_end/panels/mobile_throttling/CalibrationController.ts +1 -1
  133. package/front_end/panels/mobile_throttling/MobileThrottlingSelector.ts +3 -3
  134. package/front_end/panels/mobile_throttling/NetworkPanelIndicator.ts +6 -6
  135. package/front_end/panels/mobile_throttling/NetworkThrottlingSelector.ts +14 -12
  136. package/front_end/panels/mobile_throttling/ThrottlingManager.ts +13 -13
  137. package/front_end/panels/mobile_throttling/ThrottlingPresets.ts +10 -10
  138. package/front_end/panels/mobile_throttling/ThrottlingSettingsTab.ts +28 -28
  139. package/front_end/panels/mobile_throttling/mobile_throttling-meta.ts +6 -6
  140. package/front_end/panels/network/RequestHeadersView.ts +1 -1
  141. package/front_end/panels/network/ResourceChunkView.ts +243 -144
  142. package/front_end/panels/network/ResourceDirectSocketChunkView.ts +52 -29
  143. package/front_end/panels/network/ResourceWebSocketFrameView.ts +21 -35
  144. package/front_end/panels/network/resourceChunkView.css +41 -36
  145. package/front_end/panels/profiler/HeapDetachedElementsDataGrid.ts +21 -12
  146. package/front_end/panels/profiler/profiler.ts +4 -0
  147. package/front_end/panels/recorder/RecorderPanel.ts +6 -14
  148. package/front_end/panels/sensors/LocationsSettingsTab.ts +7 -7
  149. package/front_end/panels/settings/AISettingsTab.ts +75 -31
  150. package/front_end/panels/settings/SettingsScreen.ts +8 -13
  151. package/front_end/panels/settings/components/SyncSection.ts +3 -4
  152. package/front_end/panels/settings/emulation/DevicesSettingsTab.ts +650 -18
  153. package/front_end/panels/settings/emulation/devicesSettingsTab.css +18 -0
  154. package/front_end/panels/settings/settings-meta.ts +0 -1
  155. package/front_end/panels/sources/CategorizedBreakpointL10n.ts +0 -2
  156. package/front_end/panels/sources/DebuggerPausedMessage.ts +11 -5
  157. package/front_end/panels/sources/DebuggerPlugin.ts +1 -1
  158. package/front_end/panels/sources/EditingLocationHistoryManager.ts +12 -9
  159. package/front_end/panels/sources/SourcesPanel.ts +19 -17
  160. package/front_end/panels/sources/SourcesView.ts +191 -96
  161. package/front_end/panels/sources/TabbedEditorContainer.ts +3 -4
  162. package/front_end/panels/sources/WatchExpressionsSidebarPane.ts +2 -5
  163. package/front_end/panels/timeline/TimelinePanel.ts +2 -0
  164. package/front_end/panels/timeline/TimelineUIUtils.ts +15 -0
  165. package/front_end/panels/timeline/components/CWVMetrics.ts +16 -3
  166. package/front_end/panels/timeline/components/cwvMetrics.css +4 -0
  167. package/front_end/panels/timeline/components/liveMetricsView.css +0 -17
  168. package/front_end/panels/timeline/components/metricValueStyles.css +17 -0
  169. package/front_end/panels/timeline/timeline-meta.ts +3 -5
  170. package/front_end/third_party/chromium/README.chromium +1 -1
  171. package/front_end/third_party/codemirror.next/chunk/angular.js +1 -1
  172. package/front_end/third_party/codemirror.next/chunk/codemirror.js +1 -1
  173. package/front_end/third_party/codemirror.next/chunk/codemirror.js.map +1 -1
  174. package/front_end/third_party/codemirror.next/chunk/cpp.js +1 -1
  175. package/front_end/third_party/codemirror.next/chunk/cpp.js.map +1 -1
  176. package/front_end/third_party/codemirror.next/chunk/java.js +1 -1
  177. package/front_end/third_party/codemirror.next/chunk/java.js.map +1 -1
  178. package/front_end/third_party/codemirror.next/chunk/legacy.js +1 -1
  179. package/front_end/third_party/codemirror.next/chunk/less.js +1 -1
  180. package/front_end/third_party/codemirror.next/chunk/less.js.map +1 -1
  181. package/front_end/third_party/codemirror.next/chunk/markdown.js +1 -1
  182. package/front_end/third_party/codemirror.next/chunk/markdown.js.map +1 -1
  183. package/front_end/third_party/codemirror.next/chunk/php.js +1 -1
  184. package/front_end/third_party/codemirror.next/chunk/php.js.map +1 -1
  185. package/front_end/third_party/codemirror.next/chunk/python.js +1 -1
  186. package/front_end/third_party/codemirror.next/chunk/python.js.map +1 -1
  187. package/front_end/third_party/codemirror.next/chunk/sass.js +1 -1
  188. package/front_end/third_party/codemirror.next/chunk/sass.js.map +1 -1
  189. package/front_end/third_party/codemirror.next/chunk/svelte.js +1 -1
  190. package/front_end/third_party/codemirror.next/chunk/svelte.js.map +1 -1
  191. package/front_end/third_party/codemirror.next/chunk/vue.js +1 -1
  192. package/front_end/third_party/codemirror.next/chunk/wast.js +1 -1
  193. package/front_end/third_party/codemirror.next/chunk/wast.js.map +1 -1
  194. package/front_end/third_party/codemirror.next/chunk/xml.js +1 -1
  195. package/front_end/third_party/codemirror.next/chunk/xml.js.map +1 -1
  196. package/front_end/third_party/codemirror.next/codemirror.next.d.ts +1332 -193
  197. package/front_end/third_party/codemirror.next/codemirror.next.js +1 -1
  198. package/front_end/third_party/codemirror.next/rebuild.sh +1 -1
  199. package/front_end/third_party/puppeteer/README.chromium +2 -2
  200. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.d.ts +64 -15
  201. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +65 -16
  202. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Page.d.ts +50 -7
  203. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Page.d.ts.map +1 -1
  204. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Page.js.map +1 -1
  205. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/BidiOverCdp.d.ts +2 -1
  206. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/BidiOverCdp.d.ts.map +1 -1
  207. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/BidiOverCdp.js +5 -5
  208. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/BidiOverCdp.js.map +1 -1
  209. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/Connection.d.ts +2 -1
  210. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/Connection.d.ts.map +1 -1
  211. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/Connection.js +8 -6
  212. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/Connection.js.map +1 -1
  213. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/Dialog.d.ts.map +1 -1
  214. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/Dialog.js +3 -0
  215. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/Dialog.js.map +1 -1
  216. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Browser.d.ts.map +1 -1
  217. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Browser.js +16 -1
  218. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Browser.js.map +1 -1
  219. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Connection.d.ts +5 -1
  220. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Connection.d.ts.map +1 -1
  221. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Connection.js +11 -6
  222. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Connection.js.map +1 -1
  223. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Dialog.d.ts.map +1 -1
  224. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Dialog.js +5 -0
  225. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Dialog.js.map +1 -1
  226. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/NetworkManager.d.ts.map +1 -1
  227. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/NetworkManager.js +10 -0
  228. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/NetworkManager.js.map +1 -1
  229. package/front_end/third_party/puppeteer/package/lib/puppeteer/common/BrowserConnector.js +4 -3
  230. package/front_end/third_party/puppeteer/package/lib/puppeteer/common/BrowserConnector.js.map +1 -1
  231. package/front_end/third_party/puppeteer/package/lib/puppeteer/common/Debug.d.ts +27 -2
  232. package/front_end/third_party/puppeteer/package/lib/puppeteer/common/Debug.d.ts.map +1 -1
  233. package/front_end/third_party/puppeteer/package/lib/puppeteer/common/Debug.js +12 -1
  234. package/front_end/third_party/puppeteer/package/lib/puppeteer/common/Debug.js.map +1 -1
  235. package/front_end/third_party/puppeteer/package/lib/puppeteer/common/util.d.ts +2 -2
  236. package/front_end/third_party/puppeteer/package/lib/puppeteer/common/util.d.ts.map +1 -1
  237. package/front_end/third_party/puppeteer/package/lib/puppeteer/common/util.js +2 -2
  238. package/front_end/third_party/puppeteer/package/lib/puppeteer/common/util.js.map +1 -1
  239. package/front_end/third_party/puppeteer/package/lib/puppeteer/node/ChromeLauncher.d.ts.map +1 -1
  240. package/front_end/third_party/puppeteer/package/lib/puppeteer/node/ChromeLauncher.js +2 -0
  241. package/front_end/third_party/puppeteer/package/lib/puppeteer/node/ChromeLauncher.js.map +1 -1
  242. package/front_end/third_party/puppeteer/package/lib/puppeteer/node/ScreenRecorder.d.ts +2 -1
  243. package/front_end/third_party/puppeteer/package/lib/puppeteer/node/ScreenRecorder.d.ts.map +1 -1
  244. package/front_end/third_party/puppeteer/package/lib/puppeteer/node/ScreenRecorder.js +5 -4
  245. package/front_end/third_party/puppeteer/package/lib/puppeteer/node/ScreenRecorder.js.map +1 -1
  246. package/front_end/third_party/puppeteer/package/lib/puppeteer/revisions.d.ts +3 -3
  247. package/front_end/third_party/puppeteer/package/lib/puppeteer/revisions.js +3 -3
  248. package/front_end/third_party/puppeteer/package/lib/puppeteer/util/version.d.ts +1 -1
  249. package/front_end/third_party/puppeteer/package/lib/puppeteer/util/version.js +1 -1
  250. package/front_end/third_party/puppeteer/package/lib/types.d.ts +64 -15
  251. package/front_end/third_party/puppeteer/package/package.json +2 -2
  252. package/front_end/third_party/puppeteer/package/src/api/Page.ts +50 -7
  253. package/front_end/third_party/puppeteer/package/src/bidi/BidiOverCdp.ts +9 -7
  254. package/front_end/third_party/puppeteer/package/src/bidi/Connection.ts +9 -6
  255. package/front_end/third_party/puppeteer/package/src/bidi/Dialog.ts +4 -0
  256. package/front_end/third_party/puppeteer/package/src/cdp/Browser.ts +24 -1
  257. package/front_end/third_party/puppeteer/package/src/cdp/Connection.ts +11 -6
  258. package/front_end/third_party/puppeteer/package/src/cdp/Dialog.ts +7 -0
  259. package/front_end/third_party/puppeteer/package/src/cdp/NetworkManager.ts +11 -0
  260. package/front_end/third_party/puppeteer/package/src/common/BrowserConnector.ts +7 -3
  261. package/front_end/third_party/puppeteer/package/src/common/Debug.ts +30 -5
  262. package/front_end/third_party/puppeteer/package/src/common/util.ts +2 -2
  263. package/front_end/third_party/puppeteer/package/src/node/ChromeLauncher.ts +2 -0
  264. package/front_end/third_party/puppeteer/package/src/node/ScreenRecorder.ts +6 -4
  265. package/front_end/third_party/puppeteer/package/src/revisions.ts +3 -3
  266. package/front_end/third_party/puppeteer/package/src/util/version.ts +1 -1
  267. package/front_end/tsconfig.compatibility.json +2 -1
  268. package/front_end/tsconfig.json +1 -1
  269. package/front_end/ui/components/icon_button/IconButton.ts +1 -1
  270. package/front_end/ui/components/issue_counter/IssueCounter.ts +1 -1
  271. package/front_end/ui/components/settings/SettingCheckbox.ts +4 -7
  272. package/front_end/ui/components/settings/settings.ts +1 -2
  273. package/front_end/ui/legacy/Treeoutline.ts +52 -30
  274. package/front_end/ui/legacy/components/object_ui/CSSStyleSanitizer.ts +74 -0
  275. package/front_end/ui/legacy/components/object_ui/CustomPreviewComponent.ts +6 -1
  276. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +47 -54
  277. package/front_end/ui/legacy/components/object_ui/object_ui.ts +2 -0
  278. package/front_end/ui/legacy/components/quick_open/CommandMenu.ts +5 -27
  279. package/front_end/ui/legacy/components/settings_ui/SettingsUI.ts +10 -12
  280. package/front_end/ui/settings/SettingUIRegistration.ts +0 -8
  281. package/front_end/ui/visual_logging/KnownContextValues.ts +37 -0
  282. package/inspector_overlay/tsconfig.json +8 -2
  283. package/package.json +3 -2
  284. package/.agents/skills/gerrit-cli/scripts/gerrit_client_wrapper.py +0 -47
  285. package/front_end/core/sdk/CSSModel.test.api.ts +0 -102
  286. package/front_end/core/sdk/DOMModel.test.api.ts +0 -104
  287. package/front_end/foundation/Universe.test.api.ts +0 -73
  288. package/front_end/panels/application/SharedStorageEventsView.ts +0 -128
  289. package/front_end/panels/application/SharedStorageItemsView.ts +0 -179
  290. package/front_end/panels/application/SharedStorageListTreeElement.ts +0 -65
  291. package/front_end/panels/application/SharedStorageModel.ts +0 -249
  292. package/front_end/panels/application/SharedStorageTreeElement.ts +0 -40
  293. package/front_end/panels/application/components/SharedStorageAccessGrid.ts +0 -191
  294. package/front_end/panels/application/components/SharedStorageMetadataView.ts +0 -133
  295. package/front_end/panels/application/components/sharedStorageAccessGrid.css +0 -29
  296. package/front_end/panels/application/components/sharedStorageMetadataView.css +0 -29
  297. package/front_end/panels/application/sharedStorageEventsView.css +0 -9
  298. package/front_end/panels/network/OriginalResourceChunkView.ts +0 -340
  299. package/front_end/panels/network/originalResourceChunkView.css +0 -49
  300. package/front_end/ui/components/settings/SettingDeprecationWarning.ts +0 -47
  301. package/front_end/ui/components/settings/settingDeprecationWarning.css +0 -14
  302. /package/front_end/generated/{ARIAProperties.js → ARIAProperties.ts} +0 -0
@@ -173,6 +173,7 @@ export function isIssueCodeSupported(code: Protocol.Audits.InspectorIssueCode):
173
173
  */
174
174
  export function createIssuesFromProtocolIssue(
175
175
  issuesModel: SDK.IssuesModel.IssuesModel|null, inspectorIssue: Protocol.Audits.InspectorIssue,
176
+ // eslint-disable-next-line @devtools/no-instance-of-migrated-singletons
176
177
  frameManager: SDK.FrameManager.FrameManager = SDK.FrameManager.FrameManager.instance()): Issue[] {
177
178
  const handler = issueCodeHandlers.get(inspectorIssue.code);
178
179
  if (handler) {
@@ -203,6 +204,7 @@ export function defaultHideIssueByCodeSetting(): HideIssueMenuSetting {
203
204
  return setting;
204
205
  }
205
206
 
207
+ // eslint-disable-next-line @devtools/no-instance-of-migrated-singletons
206
208
  export function getHideIssueByCodeSetting(settings: Common.Settings.Settings = Common.Settings.Settings.instance()):
207
209
  Common.Settings.Setting<HideIssueMenuSetting> {
208
210
  return settings.createSetting('hide-issue-by-code-setting-experiment-2021', defaultHideIssueByCodeSetting());
@@ -235,13 +237,18 @@ export class IssuesManager extends Common.ObjectWrapper.ObjectWrapper<EventTypes
235
237
  constructor(
236
238
  private readonly showThirdPartyIssuesSetting?: Common.Settings.Setting<boolean>,
237
239
  private readonly hideIssueSetting?: Common.Settings.Setting<HideIssueMenuSetting>,
240
+ // eslint-disable-next-line @devtools/no-instance-of-migrated-singletons
238
241
  frameManager: SDK.FrameManager.FrameManager = SDK.FrameManager.FrameManager.instance(),
242
+ // eslint-disable-next-line @devtools/no-instance-of-migrated-singletons
239
243
  targetManager: SDK.TargetManager.TargetManager = SDK.TargetManager.TargetManager.instance(),
244
+ // eslint-disable-next-line @devtools/no-instance-of-migrated-singletons
240
245
  workspace: Workspace.Workspace.WorkspaceImpl = Workspace.Workspace.WorkspaceImpl.instance(),
241
246
  debuggerWorkspaceBinding: Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding =
242
- Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance(),
247
+ // eslint-disable-next-line @devtools/no-instance-of-migrated-singletons
248
+ Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance(),
243
249
  cssWorkspaceBinding: Bindings.CSSWorkspaceBinding.CSSWorkspaceBinding =
244
- Bindings.CSSWorkspaceBinding.CSSWorkspaceBinding.instance(),
250
+ // eslint-disable-next-line @devtools/no-instance-of-migrated-singletons
251
+ Bindings.CSSWorkspaceBinding.CSSWorkspaceBinding.instance(),
245
252
  ) {
246
253
  super();
247
254
  this.#frameManager = frameManager;
@@ -523,6 +530,7 @@ export interface EventTypes {
523
530
 
524
531
  // @ts-expect-error
525
532
  globalThis.addIssueForTest = (issue: Protocol.Audits.InspectorIssue) => {
533
+ // eslint-disable-next-line @devtools/no-instance-of-migrated-singletons
526
534
  const mainTarget = SDK.TargetManager.TargetManager.instance().primaryPageTarget();
527
535
  const issuesModel = mainTarget?.model(SDK.IssuesModel.IssuesModel);
528
536
  issuesModel?.issueAdded({issue});
@@ -14,6 +14,13 @@ import * as CrUXManager from '../crux-manager/crux-manager.js';
14
14
 
15
15
  import * as Spec from './web-vitals-injected/spec/spec.js';
16
16
 
17
+ export const timelineEnableSoftNavigationsSettingDescriptor: Common.Settings.SettingDescriptor<boolean> = {
18
+ name: 'timeline-enable-soft-navigations',
19
+ type: Common.Settings.SettingType.BOOLEAN,
20
+ storageType: Common.Settings.SettingStorageType.SYNCED,
21
+ defaultValue: true,
22
+ };
23
+
17
24
  const UIStrings = {
18
25
  /**
19
26
  * @description Warning text indicating that the Largest Contentful Paint (LCP) performance metric was affected by the user changing the simulated device.
@@ -61,18 +68,19 @@ export class LiveMetrics extends Common.ObjectWrapper.ObjectWrapper<EventTypes>
61
68
  #mutex = new Common.Mutex.Mutex();
62
69
  readonly #targetManager: SDK.TargetManager.TargetManager;
63
70
  readonly #deviceModeModel: EmulationModel.DeviceModeModel.DeviceModeModel|null;
71
+ readonly #settings: Common.Settings.Settings;
64
72
 
65
- constructor(targetManager: SDK.TargetManager.TargetManager,
73
+ constructor(targetManager: SDK.TargetManager.TargetManager, settings: Common.Settings.Settings,
66
74
  deviceModeModel: EmulationModel.DeviceModeModel.DeviceModeModel|null) {
67
75
  super();
68
76
  this.#targetManager = targetManager;
77
+ this.#settings = settings;
69
78
  this.#deviceModeModel = deviceModeModel;
70
79
  this.#targetManager.observeTargets(this, {scoped: true});
71
80
  this.#targetManager.addModelListener(SDK.ResourceTreeModel.ResourceTreeModel,
72
81
  SDK.ResourceTreeModel.Events.PrimaryPageChanged, this.#onPrimaryPageChanged,
73
82
  this);
74
- Common.Settings.Settings.instance()
75
- .moduleSetting('timeline-enable-soft-navigations')
83
+ this.#settings.resolve(timelineEnableSoftNavigationsSettingDescriptor)
76
84
  .addChangeListener(this.#onSettingChanged, this);
77
85
  }
78
86
 
@@ -104,7 +112,8 @@ export class LiveMetrics extends Common.ObjectWrapper.ObjectWrapper<EventTypes>
104
112
  if (!Root.DevToolsContext.globalInstance().has(LiveMetrics) || forceNew) {
105
113
  Root.DevToolsContext.globalInstance().set(
106
114
  LiveMetrics,
107
- new LiveMetrics(SDK.TargetManager.TargetManager.instance(),
115
+ // eslint-disable-next-line @devtools/no-instance-of-migrated-singletons
116
+ new LiveMetrics(SDK.TargetManager.TargetManager.instance(), Common.Settings.Settings.instance(),
108
117
  EmulationModel.DeviceModeModel.DeviceModeModel.tryInstance()));
109
118
  }
110
119
 
@@ -149,6 +158,19 @@ export class LiveMetrics extends Common.ObjectWrapper.ObjectWrapper<EventTypes>
149
158
  return false;
150
159
  }
151
160
 
161
+ // Validate duration at runtime to prevent code injection via interpolation.
162
+ const duration = Number(interaction.duration);
163
+ if (!Number.isFinite(duration)) {
164
+ return false;
165
+ }
166
+
167
+ // JSON.stringify escapes quotes. We concatenate safeInteractionType (which is
168
+ // wrapped in double quotes) instead of interpolating it directly inside the
169
+ // single-quoted template below. This avoids syntax errors if the value contains
170
+ // single quotes (e.g. "pointer'"), which would otherwise terminate the
171
+ // single-quoted template string.
172
+ const safeInteractionType = JSON.stringify(interaction.interactionType ?? 'unknown');
173
+
152
174
  const scriptsTable = [];
153
175
  for (const loaf of interaction.longAnimationFrameTimings) {
154
176
  for (const script of loaf.scripts) {
@@ -179,8 +201,8 @@ export class LiveMetrics extends Common.ObjectWrapper.ObjectWrapper<EventTypes>
179
201
  '';
180
202
  await this.#target.runtimeAgent().invoke_evaluate({
181
203
  expression: `
182
- console.group('[DevTools] Long animation frames for ${interaction.duration}ms ${
183
- interaction.interactionType} interaction');
204
+ console.group('[DevTools] Long animation frames for ${duration}ms ' + ${
205
+ safeInteractionType} + ' interaction');
184
206
  console.log('Scripts${scriptLimitText}:');
185
207
  console.table(${JSON.stringify(scriptsTable)});
186
208
  console.log('Intersecting long animation frame events${loafLimitText}:', ${
@@ -206,6 +228,12 @@ export class LiveMetrics extends Common.ObjectWrapper.ObjectWrapper<EventTypes>
206
228
  */
207
229
  async #resolveNodeRef(index: number, executionContextId: Protocol.Runtime.ExecutionContextId):
208
230
  Promise<SDK.DOMModel.DOMNode|null> {
231
+ // Validate index at runtime because the payload comes from an untrusted binding
232
+ // and could contain malicious strings if type casted.
233
+ if (!Number.isInteger(index)) {
234
+ return null;
235
+ }
236
+
209
237
  if (!this.#target) {
210
238
  return null;
211
239
  }
@@ -412,7 +440,9 @@ export class LiveMetrics extends Common.ObjectWrapper.ObjectWrapper<EventTypes>
412
440
  this.#clearMetrics();
413
441
  this.#navigationType = webVitalsEvent.navigationType;
414
442
  if (webVitalsEvent.url) {
443
+ // eslint-disable-next-line @devtools/no-instance-of-migrated-singletons
415
444
  CrUXManager.CrUXManager.instance().setMainDocumentURL(webVitalsEvent.url);
445
+ // eslint-disable-next-line @devtools/no-instance-of-migrated-singletons
416
446
  void CrUXManager.CrUXManager.instance().refresh();
417
447
  }
418
448
  break;
@@ -445,7 +475,10 @@ export class LiveMetrics extends Common.ObjectWrapper.ObjectWrapper<EventTypes>
445
475
 
446
476
  const resourceTreeModel = this.#target.model(SDK.ResourceTreeModel.ResourceTreeModel);
447
477
  const primaryFrameId = resourceTreeModel?.mainFrame?.id;
448
- return Boolean(primaryFrameId && executionContext.frameId === primaryFrameId);
478
+ // Ensure the context belongs to our isolated world and is not the default
479
+ // page context, to prevent the page from spoofing events.
480
+ return Boolean(primaryFrameId && executionContext.frameId === primaryFrameId &&
481
+ executionContext.name === LIVE_METRICS_WORLD_NAME && !executionContext.isDefault);
449
482
  }
450
483
 
451
484
  async #onBindingCalled(event: {data: Protocol.Runtime.BindingCalledEvent}): Promise<void> {
@@ -546,8 +579,7 @@ export class LiveMetrics extends Common.ObjectWrapper.ObjectWrapper<EventTypes>
546
579
  });
547
580
  }
548
581
 
549
- const softNavsSettingValue =
550
- Common.Settings.Settings.instance().moduleSetting('timeline-enable-soft-navigations').get();
582
+ const softNavsSettingValue = this.#settings.resolve(timelineEnableSoftNavigationsSettingDescriptor).get();
551
583
  const source = `window.devToolsReportSoftNavs = ${softNavsSettingValue};\n` + await InjectedScript.get();
552
584
 
553
585
  // Inject the script
@@ -27,11 +27,14 @@ export class LogManager implements SDK.TargetManager.SDKModelObserver<SDK.LogMod
27
27
 
28
28
  static instance({forceNew}: {forceNew: boolean} = {forceNew: false}): LogManager {
29
29
  if (!Root.DevToolsContext.globalInstance().has(LogManager) || forceNew) {
30
- Root.DevToolsContext.globalInstance().set(LogManager,
31
- new LogManager(
32
- SDK.TargetManager.TargetManager.instance(),
33
- NetworkLog.instance(),
34
- ));
30
+ Root.DevToolsContext.globalInstance().set(
31
+ LogManager,
32
+ new LogManager(
33
+ // eslint-disable-next-line @devtools/no-instance-of-migrated-singletons
34
+ SDK.TargetManager.TargetManager.instance(),
35
+ // eslint-disable-next-line @devtools/no-instance-of-migrated-singletons
36
+ NetworkLog.instance(),
37
+ ));
35
38
  }
36
39
 
37
40
  return Root.DevToolsContext.globalInstance().get(LogManager);
@@ -56,6 +56,7 @@ export class NetworkLog extends Common.ObjectWrapper.ObjectWrapper<EventTypes> i
56
56
  static instance(): NetworkLog {
57
57
  if (!Root.DevToolsContext.globalInstance().has(NetworkLog)) {
58
58
  Root.DevToolsContext.globalInstance().set(
59
+ // eslint-disable-next-line @devtools/no-instance-of-migrated-singletons
59
60
  NetworkLog, new NetworkLog(SDK.TargetManager.TargetManager.instance(), Common.Settings.Settings.instance()));
60
61
  }
61
62
 
@@ -47,9 +47,7 @@ export class IsolatedFileSystemManager extends Common.ObjectWrapper.ObjectWrappe
47
47
  Host.InspectorFrontendHost.InspectorFrontendHostInstance.events.addEventListener(
48
48
  Host.InspectorFrontendHostAPI.Events.FileSystemRemoved, this.onFileSystemRemoved, this);
49
49
  Host.InspectorFrontendHost.InspectorFrontendHostInstance.events.addEventListener(
50
- Host.InspectorFrontendHostAPI.Events.FileSystemAdded, event => {
51
- this.onFileSystemAdded(event);
52
- }, this);
50
+ Host.InspectorFrontendHostAPI.Events.FileSystemAdded, this.onFileSystemAdded, this);
53
51
  Host.InspectorFrontendHost.InspectorFrontendHostInstance.events.addEventListener(
54
52
  Host.InspectorFrontendHostAPI.Events.FileSystemFilesChangedAddedRemoved, this.onFileSystemFilesChanged, this);
55
53
  Host.InspectorFrontendHost.InspectorFrontendHostInstance.events.addEventListener(
@@ -101,13 +99,33 @@ export class IsolatedFileSystemManager extends Common.ObjectWrapper.ObjectWrappe
101
99
  this.fileSystemsLoadedPromise = this.requestFileSystems();
102
100
  }
103
101
 
102
+ // TODO(crbug.com/542394587): Should be `Symbol.dispsoe`
103
+ dispose(): void {
104
+ Host.InspectorFrontendHost.InspectorFrontendHostInstance.events.removeEventListener(
105
+ Host.InspectorFrontendHostAPI.Events.FileSystemRemoved, this.onFileSystemRemoved, this);
106
+ Host.InspectorFrontendHost.InspectorFrontendHostInstance.events.removeEventListener(
107
+ Host.InspectorFrontendHostAPI.Events.FileSystemAdded, this.onFileSystemAdded, this);
108
+ Host.InspectorFrontendHost.InspectorFrontendHostInstance.events.removeEventListener(
109
+ Host.InspectorFrontendHostAPI.Events.FileSystemFilesChangedAddedRemoved, this.onFileSystemFilesChanged, this);
110
+ Host.InspectorFrontendHost.InspectorFrontendHostInstance.events.removeEventListener(
111
+ Host.InspectorFrontendHostAPI.Events.IndexingTotalWorkCalculated, this.onIndexingTotalWorkCalculated, this);
112
+ Host.InspectorFrontendHost.InspectorFrontendHostInstance.events.removeEventListener(
113
+ Host.InspectorFrontendHostAPI.Events.IndexingWorked, this.onIndexingWorked, this);
114
+ Host.InspectorFrontendHost.InspectorFrontendHostInstance.events.removeEventListener(
115
+ Host.InspectorFrontendHostAPI.Events.IndexingDone, this.onIndexingDone, this);
116
+ Host.InspectorFrontendHost.InspectorFrontendHostInstance.events.removeEventListener(
117
+ Host.InspectorFrontendHostAPI.Events.SearchCompleted, this.onSearchCompleted, this);
118
+ }
119
+
104
120
  static instance(opts: {
105
121
  forceNew?: boolean|null,
106
122
  settings?: Common.Settings.Settings|null,
107
123
  console?: Common.Console.Console|null,
108
124
  } = {}): IsolatedFileSystemManager {
109
125
  const forceNew = opts.forceNew ?? null;
126
+ // eslint-disable-next-line @devtools/no-instance-of-migrated-singletons
110
127
  const settings = opts.settings ?? Common.Settings.Settings.instance();
128
+ // eslint-disable-next-line @devtools/no-instance-of-migrated-singletons
111
129
  const console = opts.console ?? Common.Console.Console.instance();
112
130
  if (!Root.DevToolsContext.globalInstance().has(IsolatedFileSystemManager) || forceNew) {
113
131
  const instance = new IsolatedFileSystemManager(settings, console);
@@ -103,11 +103,17 @@ export class NetworkPersistenceManager extends Common.ObjectWrapper.ObjectWrappe
103
103
  NetworkPersistenceManager,
104
104
  new NetworkPersistenceManager(
105
105
  workspace,
106
+ // eslint-disable-next-line @devtools/no-instance-of-migrated-singletons
106
107
  PersistenceImpl.instance(),
108
+ // eslint-disable-next-line @devtools/no-instance-of-migrated-singletons
107
109
  Breakpoints.BreakpointManager.BreakpointManager.instance(),
110
+ // eslint-disable-next-line @devtools/no-instance-of-migrated-singletons
108
111
  SDK.TargetManager.TargetManager.instance(),
112
+ // eslint-disable-next-line @devtools/no-instance-of-migrated-singletons
109
113
  Common.Settings.Settings.instance(),
114
+ // eslint-disable-next-line @devtools/no-instance-of-migrated-singletons
110
115
  IsolatedFileSystemManager.instance(),
116
+ // eslint-disable-next-line @devtools/no-instance-of-migrated-singletons
111
117
  SDK.NetworkManager.MultitargetNetworkManager.instance(),
112
118
  ),
113
119
  );
@@ -4,7 +4,6 @@
4
4
 
5
5
  import * as Common from '../../core/common/common.js';
6
6
  import * as Platform from '../../core/platform/platform.js';
7
- import type * as Root from '../../core/root/root.js';
8
7
  import * as SDK from '../../core/sdk/sdk.js';
9
8
 
10
9
  /** The security origin for all DevTools (front-end) resources. */
@@ -107,23 +106,20 @@ export class ProjectSettingsModel extends Common.ObjectWrapper.ObjectWrapper<Eve
107
106
  }
108
107
 
109
108
  constructor(
110
- hostConfig: Root.Runtime.HostConfig,
111
109
  pageResourceLoader: SDK.PageResourceLoader.PageResourceLoader,
112
110
  targetManager: SDK.TargetManager.TargetManager,
113
111
  ) {
114
112
  super();
115
113
  this.#pageResourceLoader = pageResourceLoader;
116
114
  this.#targetManager = targetManager;
117
- if (hostConfig.devToolsWellKnown?.enabled) {
118
- this.#targetManager.addEventListener(
119
- SDK.TargetManager.Events.INSPECTED_URL_CHANGED,
120
- this.#inspectedURLChanged,
121
- this,
122
- );
123
- const target = this.#targetManager.primaryPageTarget();
124
- if (target !== null) {
125
- this.#inspectedURLChanged({data: target});
126
- }
115
+ this.#targetManager.addEventListener(
116
+ SDK.TargetManager.Events.INSPECTED_URL_CHANGED,
117
+ this.#inspectedURLChanged,
118
+ this,
119
+ );
120
+ const target = this.#targetManager.primaryPageTarget();
121
+ if (target !== null) {
122
+ this.#inspectedURLChanged({data: target});
127
123
  }
128
124
  }
129
125
 
@@ -440,7 +440,12 @@ export class TraceProcessor extends EventTarget {
440
440
  let id, urlString, navigation;
441
441
  if (context.navigation) {
442
442
  id = `NAVIGATION_${this.#insights.size}`;
443
- urlString = data.Meta.finalDisplayUrlByNavigationId.get(context.navigationId) ?? data.Meta.mainFrameURL;
443
+ if (Types.Events.isSoftNavigationStart(context.navigation)) {
444
+ urlString = context.navigation.args.context.URL;
445
+ } else {
446
+ urlString = data.Meta.finalDisplayUrlByNavigationId.get(context.navigation.args.data?.navigationId ?? '') ??
447
+ data.Meta.mainFrameURL;
448
+ }
444
449
  navigation = context.navigation;
445
450
  } else {
446
451
  id = Types.Events.NO_NAVIGATION;
@@ -523,17 +528,28 @@ export class TraceProcessor extends EventTarget {
523
528
 
524
529
  // Filter main frame navigations to those that have the necessary data (frameId and navigationId).
525
530
  // TODO(cjamcl): Does this filtering makes the "use the next nav as the end time" logic potentially broken? Are navs without nav id or frame even real?
526
- const navigations = data.Meta.mainFrameNavigations.filter(
527
- navigation => navigation.args.frame && navigation.args.data?.navigationId);
531
+ const hardNavigations = data.Meta.mainFrameNavigations.filter(navigation => navigation.args.frame &&
532
+ navigation.args.data?.navigationId);
533
+
534
+ const softNavigations =
535
+ this.#modelConfiguration.enableSoftNavigation ? Array.from(data.Meta.softNavigationsById.values()) : [];
536
+ const navigations = [...hardNavigations, ...softNavigations].sort((a, b) => a.ts - b.ts);
528
537
 
529
538
  this.#computeInsightsForInitialTracePeriod(data, navigations, options);
530
539
 
531
540
  for (const [index, navigation] of navigations.entries()) {
532
541
  const min = navigation.ts;
533
- // Use trace end for the last navigation, otherwise use the start of the next navigation.
534
- const max = index + 1 < navigations.length ? navigations[index + 1].ts : data.Meta.traceBounds.max;
542
+ // Use trace end for the last navigation, otherwise use the start of the next navigation (minus 1 to avoid overlap).
543
+ let max = index + 1 < navigations.length ? navigations[index + 1].ts : data.Meta.traceBounds.max;
544
+ if (index + 1 < navigations.length) {
545
+ max = Types.Timing.Micro(max - 1);
546
+ }
535
547
  const bounds = Helpers.Timing.traceWindowFromMicroSeconds(min, max);
536
- this.#computeInsightsForNavigation(navigation, bounds, data, traceEvents, options);
548
+ if (Types.Events.isSoftNavigationStart(navigation)) {
549
+ this.#computeInsightsForSoftNavigation(navigation, bounds, data, options);
550
+ } else {
551
+ this.#computeInsightsForNavigation(navigation, bounds, data, traceEvents, options);
552
+ }
537
553
  }
538
554
  }
539
555
 
@@ -541,12 +557,13 @@ export class TraceProcessor extends EventTarget {
541
557
  * Computes insights for the period before the first navigation, or for the entire trace if no navigations exist.
542
558
  */
543
559
  #computeInsightsForInitialTracePeriod(
544
- data: Handlers.Types.HandlerData, navigations: readonly Types.Events.NavigationStart[],
560
+ data: Handlers.Types.HandlerData,
561
+ navigations: ReadonlyArray<Types.Events.NavigationStart|Types.Events.SoftNavigationStart>,
545
562
  options: Types.Configuration.ParseOptions): void {
546
563
  // Determine bounds: Use the period before the first navigation if navigations exist, otherwise use the entire trace bounds.
547
- const bounds = navigations.length > 0 ?
548
- Helpers.Timing.traceWindowFromMicroSeconds(data.Meta.traceBounds.min, navigations[0].ts) :
549
- data.Meta.traceBounds;
564
+ const bounds = navigations.length > 0 ? Helpers.Timing.traceWindowFromMicroSeconds(
565
+ data.Meta.traceBounds.min, Types.Timing.Micro(navigations[0].ts - 1)) :
566
+ data.Meta.traceBounds;
550
567
 
551
568
  const context: Insights.Types.InsightSetContext = {
552
569
  options,
@@ -609,6 +626,21 @@ export class TraceProcessor extends EventTarget {
609
626
  };
610
627
  this.#computeInsightSet(data, context);
611
628
  }
629
+
630
+ /**
631
+ * Computes insights for a specific soft navigation event.
632
+ */
633
+ #computeInsightsForSoftNavigation(navigation: Types.Events.SoftNavigationStart, bounds: Types.Timing.TraceWindowMicro,
634
+ data: Handlers.Types.HandlerData, options: Types.Configuration.ParseOptions): void {
635
+ const frameId = navigation.args.frame;
636
+ const context: Insights.Types.InsightSetContextWithSoftNavigation = {
637
+ options,
638
+ bounds,
639
+ frameId,
640
+ navigation,
641
+ };
642
+ this.#computeInsightSet(data, context);
643
+ }
612
644
  }
613
645
 
614
646
  /**
@@ -39,7 +39,6 @@ import type {HandlerName} from './types.js';
39
39
 
40
40
  interface LayoutShiftsData {
41
41
  clusters: readonly Types.Events.SyntheticLayoutShiftCluster[];
42
- clustersByNavigationId: Map<Types.Events.NavigationId, Types.Events.SyntheticLayoutShiftCluster[]>;
43
42
  sessionMaxScore: number;
44
43
  // The session window which contains the SessionMaxScore
45
44
  clsWindowID: number;
@@ -110,7 +109,6 @@ let sessionMaxScore = 0;
110
109
  let clsWindowID = -1;
111
110
 
112
111
  let clusters: Types.Events.SyntheticLayoutShiftCluster[] = [];
113
- let clustersByNavigationId = new Map<Types.Events.NavigationId, Types.Events.SyntheticLayoutShiftCluster[]>();
114
112
 
115
113
  /**
116
114
  * Represents a point in time in which a LS score change
@@ -141,7 +139,6 @@ export function reset(): void {
141
139
  sessionMaxScore = 0;
142
140
  scoreRecords = [];
143
141
  clsWindowID = -1;
144
- clustersByNavigationId = new Map();
145
142
  }
146
143
 
147
144
  export function handleEvent(event: Types.Events.Event): void {
@@ -292,8 +289,9 @@ export async function finalize(): Promise<void> {
292
289
  }
293
290
 
294
291
  async function buildLayoutShiftsClusters(): Promise<void> {
295
- const {navigationsByFrameId, mainFrameId, traceBounds} = metaHandlerData();
296
- const navigations = navigationsByFrameId.get(mainFrameId) || [];
292
+ const {navigationsByFrameId, mainFrameId, traceBounds, softNavigationsById} = metaHandlerData();
293
+ const navigations: Types.Events.Event[] =
294
+ [...(navigationsByFrameId.get(mainFrameId) || []), ...softNavigationsById.values()].sort((a, b) => a.ts - b.ts);
297
295
  if (layoutShiftEvents.length === 0) {
298
296
  return;
299
297
  }
@@ -348,13 +346,15 @@ async function buildLayoutShiftsClusters(): Promise<void> {
348
346
  // If this cluster happened after a navigation, set the navigationId to
349
347
  // the current navigation. This lets us easily group clusters by
350
348
  // navigation.
351
- const navigationId = currentShiftNavigation === null ?
352
- Types.Events.NO_NAVIGATION :
353
- navigations[currentShiftNavigation].args.data?.navigationId;
354
- // TODO: `navigationId` is `string | undefined`, but the undefined portion
355
- // comes from `data.navigationId`. I don't think that is possible for this
356
- // event type. Can we make this typing stronger? In the meantime, we allow
357
- // `navigationId` to include undefined values.
349
+ let navigationId: string|undefined = undefined;
350
+ if (currentShiftNavigation !== null) {
351
+ const nav = navigations[currentShiftNavigation];
352
+ if (Types.Events.isNavigationStart(nav)) {
353
+ navigationId = nav.args.data?.navigationId;
354
+ }
355
+ } else {
356
+ navigationId = Types.Events.NO_NAVIGATION;
357
+ }
358
358
 
359
359
  clusters.push(Helpers.SyntheticEvents.SyntheticEventsManager
360
360
  .registerSyntheticEvent<Types.Events.SyntheticLayoutShiftCluster>({
@@ -519,12 +519,6 @@ async function buildLayoutShiftsClusters(): Promise<void> {
519
519
  sessionMaxScore = weightedScore;
520
520
  }
521
521
 
522
- if (cluster.navigationId) {
523
- const clustersForId = Platform.MapUtilities.getWithDefault(clustersByNavigationId, cluster.navigationId, () => {
524
- return [];
525
- });
526
- clustersForId.push(cluster);
527
- }
528
522
  }
529
523
  }
530
524
 
@@ -543,7 +537,6 @@ export function data(): LayoutShiftsData {
543
537
  remoteFonts,
544
538
  scoreRecords,
545
539
  backendNodeIds,
546
- clustersByNavigationId,
547
540
  paintImageEvents,
548
541
  };
549
542
  }
@@ -62,6 +62,7 @@ let navigationsByNavigationId = new Map<string, Types.Events.NavigationStart>();
62
62
  let softNavigationsById = new Map<number, Types.Events.SoftNavigationStart>();
63
63
  let finalDisplayUrlByNavigationId = new Map<string, string>();
64
64
  let mainFrameNavigations: Types.Events.NavigationStart[] = [];
65
+ let sameDocumentNavigations: Types.Events.DidCommitSameDocumentNavigation[] = [];
65
66
 
66
67
  // Represents all the threads in the trace, organized by process. This is mostly for internal
67
68
  // bookkeeping so that during the finalize pass we can obtain the main and browser thread IDs.
@@ -97,6 +98,7 @@ export function reset(): void {
97
98
  finalDisplayUrlByNavigationId = new Map();
98
99
  processNames = new Map();
99
100
  mainFrameNavigations = [];
101
+ sameDocumentNavigations = [];
100
102
 
101
103
  browserProcessId = Types.Events.ProcessID(-1);
102
104
  browserThreadId = Types.Events.ThreadID(-1);
@@ -346,15 +348,12 @@ export function handleEvent(event: Types.Events.Event): void {
346
348
  return;
347
349
  }
348
350
 
349
- // Update `finalDisplayUrlByNavigationId` to reflect history API navigations.
351
+ // Track history API navigations to compute final display URLs in finalize.
350
352
  if (Types.Events.isDidCommitSameDocumentNavigation(event)) {
351
353
  if (event.args.render_frame_host.frame_type !== 'PRIMARY_MAIN_FRAME') {
352
354
  return;
353
355
  }
354
-
355
- const navigation = mainFrameNavigations.at(-1);
356
- const key = navigation?.args.data?.navigationId ?? '';
357
- finalDisplayUrlByNavigationId.set(key, event.args.url);
356
+ sameDocumentNavigations.push(event);
358
357
  return;
359
358
  }
360
359
  }
@@ -440,6 +439,40 @@ export async function finalize(options?: FinalizeOptions): Promise<void> {
440
439
  mainFrameURL = firstMainFrameNav.args.data.documentLoaderURL;
441
440
  }
442
441
  }
442
+
443
+ // Compute final display URLs for each hard navigation.
444
+ // A hard navigation's final URL is updated by history API navigations (e.g. pushState).
445
+ // However, if a soft navigation occurs, subsequent history API navigations belong to the
446
+ // soft navigation and should not update the hard navigation's URL.
447
+ const softNavs = Array.from(softNavigationsById.values()).sort((a, b) => a.ts - b.ts);
448
+
449
+ // Create an array of intervals for each hard navigation.
450
+ const hardNavs = [...mainFrameNavigations];
451
+
452
+ // Add a faux navigation to cover the time before the first hard navigation, or the whole trace if there are none.
453
+ const intervals = [
454
+ {minTs: 0, maxTs: hardNavs.length > 0 ? hardNavs[0].ts : Infinity, key: ''},
455
+ ...hardNavs.map((nav, i) => ({
456
+ minTs: nav.ts,
457
+ maxTs: i + 1 < hardNavs.length ? hardNavs[i + 1].ts : Infinity,
458
+ key: nav.args.data?.navigationId ?? '',
459
+ })),
460
+ ];
461
+
462
+ for (const {minTs, maxTs, key} of intervals) {
463
+ // Find the first soft navigation within this hard navigation's time window.
464
+ const firstSoftNav = softNavs.find(sn => sn.ts >= minTs && sn.ts < maxTs);
465
+ // If a soft navigation occurred, we only consider history events before it.
466
+ const effectiveMaxTs = firstSoftNav ? firstSoftNav.ts : maxTs;
467
+
468
+ // Find the last history API navigation in the effective window.
469
+ const historyEvents = sameDocumentNavigations.filter(hn => hn.ts >= minTs && hn.ts < effectiveMaxTs);
470
+
471
+ const lastHistoryEvent = historyEvents.at(-1);
472
+ if (lastHistoryEvent) {
473
+ finalDisplayUrlByNavigationId.set(key, lastHistoryEvent.args.url);
474
+ }
475
+ }
443
476
  }
444
477
 
445
478
  export interface MetaHandlerData {
@@ -599,8 +599,7 @@ export function generateInsight(data: Handlers.Types.HandlerData, context: Insig
599
599
  const domLoadingEvents = data.LayoutShifts.domLoadingEvents.filter(isWithinContext);
600
600
  const unsizedImageEvents = data.LayoutShifts.layoutImageUnsizedEvents.filter(isWithinContext);
601
601
 
602
- const clusterKey = context.navigation ? context.navigationId : Types.Events.NO_NAVIGATION;
603
- const clusters = data.LayoutShifts.clustersByNavigationId.get(clusterKey) ?? [];
602
+ const clusters = data.LayoutShifts.clusters.filter(isWithinContext);
604
603
  const clustersByScore = clusters.toSorted((a, b) => b.clusterCumulativeScore - a.clusterCumulativeScore);
605
604
  const worstCluster = clustersByScore.at(0);
606
605
  const layoutShifts = clusters.flatMap(cluster => cluster.events);
@@ -127,7 +127,7 @@ function metaCharsetLabel(disposition: Types.Events.MetaCharsetDisposition|undef
127
127
 
128
128
  export function generateInsight(
129
129
  data: Handlers.Types.HandlerData, context: InsightSetContext): CharacterSetInsightModel {
130
- if (!context.navigation) {
130
+ if (!context.navigation || !('navigationId' in context)) {
131
131
  return finalize({});
132
132
  }
133
133
 
@@ -160,7 +160,26 @@ export function getFieldMetricsForInsightSet(
160
160
  return null;
161
161
  }
162
162
 
163
- const pageResult = getPageResult(cruxFieldData, insightSet.url.href, insightSet.url.origin, scope);
163
+ let pageResult = getPageResult(cruxFieldData, insightSet.url.href, insightSet.url.origin, scope);
164
+
165
+ if (!pageResult && insightSet.navigation && Types.Events.isSoftNavigationStart(insightSet.navigation)) {
166
+ pageResult = cruxFieldData.find(result => {
167
+ const key = scope ? result[`${scope.pageScope}-${scope.deviceScope}`]?.record.key :
168
+ (result['url-ALL'] || result['origin-ALL'])?.record.key;
169
+ if (!key) {
170
+ return false;
171
+ }
172
+ if (key.url) {
173
+ try {
174
+ return new URL(key.url).origin === insightSet.url.origin;
175
+ } catch {
176
+ return false;
177
+ }
178
+ }
179
+ return key.origin === insightSet.url.origin;
180
+ });
181
+ }
182
+
164
183
  if (!pageResult) {
165
184
  return null;
166
185
  }
@@ -266,7 +285,7 @@ function estimateSavingsWithGraphs(
266
285
  */
267
286
  export function metricSavingsForWastedBytes(
268
287
  wastedBytesByRequestId: Map<string, number>, context: InsightSetContext): MetricSavings|undefined {
269
- if (!context.navigation || !context.lantern) {
288
+ if (!context.navigation || !('lantern' in context) || !context.lantern) {
270
289
  return;
271
290
  }
272
291
 
@@ -191,7 +191,7 @@ function finalize(partialModel: PartialInsightModel<DocumentLatencyInsightModel>
191
191
 
192
192
  export function generateInsight(
193
193
  data: Handlers.Types.HandlerData, context: InsightSetContext): DocumentLatencyInsightModel {
194
- if (!context.navigation) {
194
+ if (!context.navigation || !('navigationId' in context)) {
195
195
  return finalize({});
196
196
  }
197
197
 
@@ -110,10 +110,10 @@ function anyValuesNaN(...values: number[]): boolean {
110
110
  * them to be missing on newer traces, but old trace files may lack some of the
111
111
  * data we rely on, so we want to handle that case.
112
112
  */
113
- function determineSubparts(
114
- nav: Types.Events.NavigationStart, docRequest: Types.Events.SyntheticNetworkRequest,
115
- lcpEvent: Types.Events.AnyLargestContentfulPaintCandidate,
116
- lcpRequest: Types.Events.SyntheticNetworkRequest|undefined): LCPSubparts|null {
113
+ function determineSubparts(nav: Types.Events.NavigationStart|Types.Events.SoftNavigationStart,
114
+ docRequest: Types.Events.SyntheticNetworkRequest,
115
+ lcpEvent: Types.Events.AnyLargestContentfulPaintCandidate,
116
+ lcpRequest: Types.Events.SyntheticNetworkRequest|undefined): LCPSubparts|null {
117
117
  const firstDocByteTs = calculateDocFirstByteTs(docRequest);
118
118
  if (firstDocByteTs === null) {
119
119
  return null;
@@ -232,9 +232,10 @@ export function generateInsight(
232
232
  const lcpMs = Helpers.Timing.microToMilli(metricScore.timing);
233
233
  // This helps position things on the timeline's UI accurately for a trace.
234
234
  const lcpTs = metricScore.event?.ts ? Helpers.Timing.microToMilli(metricScore.event?.ts) : undefined;
235
- const lcpRequest = data.LargestImagePaint.lcpRequestByNavigationId.get(context.navigationId);
235
+ const lcpRequest =
236
+ 'navigationId' in context ? data.LargestImagePaint.lcpRequestByNavigationId.get(context.navigationId) : undefined;
236
237
 
237
- const docRequest = networkRequests.byId.get(context.navigationId);
238
+ const docRequest = 'navigationId' in context ? networkRequests.byId.get(context.navigationId) : undefined;
238
239
  if (!docRequest) {
239
240
  return finalize({lcpMs, lcpTs, lcpEvent, lcpRequest, warnings: [InsightWarning.NO_DOCUMENT_REQUEST]});
240
241
  }
@@ -101,7 +101,7 @@ function finalize(partialModel: PartialInsightModel<LCPDiscoveryInsightModel>):
101
101
 
102
102
  export function generateInsight(
103
103
  data: Handlers.Types.HandlerData, context: InsightSetContext): LCPDiscoveryInsightModel {
104
- if (!context.navigation) {
104
+ if (!context.navigation || !('navigationId' in context)) {
105
105
  return finalize({});
106
106
  }
107
107