chrome-devtools-frontend 1.0.1652307 → 1.0.1656291

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 (267) hide show
  1. package/.agents/skills/fixing-skipped-tests/SKILL.md +32 -0
  2. package/.agents/skills/migrate-chromium-test/SKILL.md +144 -0
  3. package/.agents/skills/ui-eng-vision-local-lit-renderer/SKILL.md +116 -0
  4. package/.agents/skills/ui-eng-vision-logic-consolidator/SKILL.md +82 -0
  5. package/.agents/skills/ui-eng-vision-orchestrator/SKILL.md +123 -0
  6. package/.agents/skills/ui-eng-vision-test-scaffolder/SKILL.md +125 -0
  7. package/.agents/skills/ui-eng-vision-widget-promoter/SKILL.md +75 -0
  8. package/AUTHORS +1 -0
  9. package/config/owner/COMMON_OWNERS +0 -4
  10. package/docs/get_the_code.md +1 -2
  11. package/docs/playbook.md +1 -1
  12. package/docs/ui_engineering.md +3 -1
  13. package/eslint.config.mjs +0 -5
  14. package/front_end/Tests.js +1 -0
  15. package/front_end/core/common/Settings.ts +47 -30
  16. package/front_end/core/sdk/CPUThrottlingManager.ts +23 -167
  17. package/front_end/core/sdk/CSSMetadata.ts +23 -0
  18. package/front_end/core/sdk/CompilerSourceMappingContentProvider.ts +8 -4
  19. package/front_end/core/sdk/ConsoleModel.ts +13 -11
  20. package/front_end/core/sdk/DOMDebuggerModel.ts +11 -5
  21. package/front_end/core/sdk/DOMModel.ts +4 -0
  22. package/front_end/core/sdk/EmulationModel.ts +4 -0
  23. package/front_end/core/sdk/EventBreakpointsModel.ts +9 -5
  24. package/front_end/core/sdk/IsolateManager.ts +6 -6
  25. package/front_end/core/sdk/ResourceTreeModel.ts +8 -5
  26. package/front_end/core/sdk/TargetManager.ts +17 -0
  27. package/front_end/entrypoints/devtools_app/devtools_app.ts +0 -1
  28. package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshot.ts +459 -74
  29. package/front_end/entrypoints/main/MainImpl.ts +12 -42
  30. package/front_end/foundation/Universe.ts +116 -3
  31. package/front_end/generated/InspectorBackendCommands.ts +11 -6
  32. package/front_end/generated/SupportedCSSProperties.js +124 -34
  33. package/front_end/generated/protocol-mapping.d.ts +8 -0
  34. package/front_end/generated/protocol-proxy-api.d.ts +15 -0
  35. package/front_end/generated/protocol.ts +94 -1
  36. package/front_end/models/ai_assistance/AiConversation.ts +2 -7
  37. package/front_end/models/ai_assistance/README.md +4 -3
  38. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +1 -1
  39. package/front_end/models/ai_assistance/agents/NetworkAgent.snapshot.txt +1 -1
  40. package/front_end/models/ai_assistance/agents/PerformanceAgent.snapshot.txt +2 -2
  41. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +13 -320
  42. package/front_end/models/ai_assistance/agents/StylingAgent.snapshot.txt +0 -24
  43. package/front_end/models/ai_assistance/agents/StylingAgent.ts +2 -358
  44. package/front_end/models/ai_assistance/ai_assistance.ts +6 -8
  45. package/front_end/models/ai_assistance/contexts/PerformanceTraceContext.ts +295 -0
  46. package/front_end/models/ai_assistance/contexts/RequestContext.snapshot.txt +0 -1
  47. package/front_end/models/ai_assistance/data_formatters/NetworkRequestFormatter.ts +0 -2
  48. package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.snapshot.txt +10 -10
  49. package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.ts +12 -11
  50. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +6 -6
  51. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.ts +1 -14
  52. package/front_end/models/ai_assistance/skills/accessibility.md +4 -2
  53. package/front_end/models/ai_assistance/tools/GetElementAccessibilityDetails.ts +142 -0
  54. package/front_end/models/ai_assistance/tools/GetStyles.ts +1 -1
  55. package/front_end/models/ai_assistance/tools/README.md +21 -1
  56. package/front_end/models/ai_assistance/tools/{ResolveLighthousePath.ts → ResolveDevtoolsNodePath.ts} +18 -17
  57. package/front_end/models/ai_assistance/tools/Tool.ts +6 -1
  58. package/front_end/models/ai_assistance/tools/ToolRegistry.ts +4 -2
  59. package/front_end/models/autofill_manager/AutofillManager.ts +6 -15
  60. package/front_end/models/bindings/NetworkProject.ts +4 -8
  61. package/front_end/models/breakpoints/BreakpointManager.ts +9 -9
  62. package/front_end/models/emulation/DeviceModeModel.ts +24 -0
  63. package/front_end/models/emulation/EmulatedDevices.ts +108 -0
  64. package/front_end/models/heap_snapshot/HeapSnapshotModel.ts +36 -0
  65. package/front_end/models/heap_snapshot/HeapSnapshotProxy.ts +8 -0
  66. package/front_end/models/issues_manager/IssuesManager.ts +10 -6
  67. package/front_end/models/javascript_metadata/JavaScriptMetadata.ts +4 -5
  68. package/front_end/models/javascript_metadata/NativeFunctions.js +1 -1
  69. package/front_end/models/live-metrics/LiveMetrics.ts +6 -6
  70. package/front_end/models/live-metrics/web-vitals-injected/spec/spec.ts +5 -5
  71. package/front_end/models/live-metrics/web-vitals-injected/web-vitals-injected.ts +3 -3
  72. package/front_end/models/logs/LogManager.ts +25 -10
  73. package/front_end/models/logs/NetworkLog.ts +24 -19
  74. package/front_end/models/persistence/AutomaticFileSystemManager.ts +14 -13
  75. package/front_end/models/persistence/AutomaticFileSystemWorkspaceBinding.ts +26 -14
  76. package/front_end/models/persistence/IsolatedFileSystem.ts +9 -11
  77. package/front_end/models/persistence/IsolatedFileSystemManager.ts +22 -11
  78. package/front_end/models/persistence/NetworkPersistenceManager.ts +73 -44
  79. package/front_end/models/persistence/PersistenceImpl.ts +4 -5
  80. package/front_end/models/project_settings/ProjectSettingsModel.ts +12 -11
  81. package/front_end/models/trace/EntityMapper.ts +1 -1
  82. package/front_end/models/trace/handlers/ScreenshotsHandler.ts +14 -59
  83. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +12 -38
  84. package/front_end/panels/ai_assistance/ExportConversation.ts +4 -1
  85. package/front_end/panels/ai_assistance/components/ChatInput.ts +2 -2
  86. package/front_end/panels/application/DeviceBoundSessionsView.ts +15 -3
  87. package/front_end/panels/application/FrameDetailsView.ts +2 -1
  88. package/front_end/panels/application/IndexedDBViews.ts +38 -36
  89. package/front_end/panels/application/ServiceWorkersView.ts +23 -16
  90. package/front_end/panels/application/components/AdsView.ts +4 -0
  91. package/front_end/panels/autofill/AutofillView.ts +1 -1
  92. package/front_end/panels/autofill/autofill-meta.ts +3 -2
  93. package/front_end/panels/common/CPUThrottlingOption.ts +142 -0
  94. package/front_end/panels/common/ThrottlingUtils.ts +9 -4
  95. package/front_end/panels/common/common.ts +1 -1
  96. package/front_end/panels/console/ConsoleViewMessage.ts +45 -0
  97. package/front_end/panels/elements/AccessibilityTreeUtils.ts +26 -15
  98. package/front_end/panels/elements/AccessibilityTreeView.ts +12 -9
  99. package/front_end/panels/elements/ElementsPanel.ts +0 -74
  100. package/front_end/panels/elements/ElementsTreeOutline.ts +5 -4
  101. package/front_end/panels/emulation/DeviceModeToolbar.ts +46 -45
  102. package/front_end/panels/layer_viewer/LayerTreeOutline.ts +118 -68
  103. package/front_end/panels/layer_viewer/Layers3DView.ts +1 -1
  104. package/front_end/panels/layer_viewer/layerTreeOutline.css +20 -13
  105. package/front_end/panels/layers/LayersPanel.ts +1 -1
  106. package/front_end/panels/mobile_throttling/CalibrationController.ts +6 -5
  107. package/front_end/panels/mobile_throttling/MobileThrottlingSelector.ts +1 -1
  108. package/front_end/panels/mobile_throttling/ThrottlingManager.ts +40 -13
  109. package/front_end/panels/mobile_throttling/ThrottlingPresets.ts +13 -17
  110. package/front_end/panels/mobile_throttling/ThrottlingSettingsTab.ts +79 -30
  111. package/front_end/panels/mobile_throttling/throttlingSettingsTab.css +5 -0
  112. package/front_end/panels/network/LinkPreloadGenerator.ts +101 -0
  113. package/front_end/panels/network/NetworkLogView.ts +28 -9
  114. package/front_end/panels/network/NetworkPanel.ts +1 -63
  115. package/front_end/panels/network/RequestInitiatorView.ts +29 -4
  116. package/front_end/panels/network/components/HeaderSectionRow.css +9 -7
  117. package/front_end/panels/network/components/HeaderSectionRow.ts +20 -1
  118. package/front_end/panels/network/components/RequestHeaderSection.css +5 -3
  119. package/front_end/panels/network/components/RequestHeaderSection.ts +1 -2
  120. package/front_end/panels/network/components/RequestHeadersView.css +1 -1
  121. package/front_end/panels/network/network.ts +2 -0
  122. package/front_end/panels/profiler/HeapSnapshotView.ts +121 -33
  123. package/front_end/panels/profiler/ModuleUIStrings.ts +4 -0
  124. package/front_end/panels/security/SecurityPanel.ts +36 -23
  125. package/front_end/panels/settings/SettingsScreen.ts +18 -103
  126. package/front_end/panels/settings/settings-meta.ts +0 -24
  127. package/front_end/panels/timeline/TimelineController.ts +3 -2
  128. package/front_end/panels/timeline/TimelineHistoryManager.ts +38 -37
  129. package/front_end/panels/timeline/TimelinePanel.ts +52 -11
  130. package/front_end/panels/timeline/components/CPUThrottlingSelector.ts +20 -18
  131. package/front_end/panels/timeline/components/LiveMetricsView.ts +38 -38
  132. package/front_end/panels/timeline/components/MetricCard.ts +18 -18
  133. package/front_end/panels/timeline/components/liveMetricsView.css +6 -6
  134. package/front_end/panels/timeline/components/metricCard.css +4 -4
  135. package/front_end/panels/timeline/timelineFlamechartPopover.css +2 -0
  136. package/front_end/panels/utils/utils.ts +25 -24
  137. package/front_end/panels/whats_new/ReleaseNoteText.ts +3 -3
  138. package/front_end/panels/whats_new/resources/WNDT.md +1 -1
  139. package/front_end/third_party/chromium/README.chromium +1 -1
  140. package/front_end/third_party/puppeteer/README.chromium +2 -2
  141. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.d.ts +34 -55
  142. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +96 -83
  143. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Browser.d.ts +10 -1
  144. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Browser.d.ts.map +1 -1
  145. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Browser.js.map +1 -1
  146. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/BrowserContext.d.ts.map +1 -1
  147. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/BrowserContext.js +1 -1
  148. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/BrowserContext.js.map +1 -1
  149. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/CDPSession.d.ts.map +1 -1
  150. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/CDPSession.js.map +1 -1
  151. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/HTTPResponse.d.ts +3 -2
  152. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/HTTPResponse.d.ts.map +1 -1
  153. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/HTTPResponse.js.map +1 -1
  154. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Realm.d.ts.map +1 -1
  155. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Realm.js.map +1 -1
  156. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Target.d.ts +1 -1
  157. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Target.js +1 -1
  158. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/locators/locators.d.ts.map +1 -1
  159. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/locators/locators.js.map +1 -1
  160. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/HTTPResponse.d.ts.map +1 -1
  161. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/HTTPResponse.js +2 -1
  162. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/HTTPResponse.js.map +1 -1
  163. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/core/Realm.d.ts.map +1 -1
  164. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/core/Realm.js.map +1 -1
  165. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Browser.d.ts +2 -2
  166. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Browser.d.ts.map +1 -1
  167. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Browser.js +5 -2
  168. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Browser.js.map +1 -1
  169. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Frame.js +1 -1
  170. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Frame.js.map +1 -1
  171. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/FrameManager.d.ts.map +1 -1
  172. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/FrameManager.js +23 -13
  173. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/FrameManager.js.map +1 -1
  174. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/HTTPResponse.d.ts.map +1 -1
  175. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/HTTPResponse.js +4 -1
  176. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/HTTPResponse.js.map +1 -1
  177. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Input.js +1 -1
  178. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/LifecycleWatcher.d.ts.map +1 -1
  179. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/LifecycleWatcher.js.map +1 -1
  180. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Target.d.ts.map +1 -1
  181. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Target.js.map +1 -1
  182. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/WebMCP.d.ts +3 -41
  183. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/WebMCP.d.ts.map +1 -1
  184. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/WebMCP.js.map +1 -1
  185. package/front_end/third_party/puppeteer/package/lib/puppeteer/common/ConnectOptions.d.ts.map +1 -1
  186. package/front_end/third_party/puppeteer/package/lib/puppeteer/node/BrowserLauncher.d.ts.map +1 -1
  187. package/front_end/third_party/puppeteer/package/lib/puppeteer/node/BrowserLauncher.js +4 -2
  188. package/front_end/third_party/puppeteer/package/lib/puppeteer/node/BrowserLauncher.js.map +1 -1
  189. package/front_end/third_party/puppeteer/package/lib/puppeteer/node/LaunchOptions.d.ts +5 -0
  190. package/front_end/third_party/puppeteer/package/lib/puppeteer/node/LaunchOptions.d.ts.map +1 -1
  191. package/front_end/third_party/puppeteer/package/lib/puppeteer/revisions.d.ts +1 -1
  192. package/front_end/third_party/puppeteer/package/lib/puppeteer/revisions.js +1 -1
  193. package/front_end/third_party/puppeteer/package/lib/puppeteer/util/httpUtils.d.ts +1 -1
  194. package/front_end/third_party/puppeteer/package/lib/puppeteer/util/httpUtils.d.ts.map +1 -1
  195. package/front_end/third_party/puppeteer/package/lib/puppeteer/util/httpUtils.js +5 -5
  196. package/front_end/third_party/puppeteer/package/lib/puppeteer/util/httpUtils.js.map +1 -1
  197. package/front_end/third_party/puppeteer/package/lib/puppeteer/util/version.d.ts +1 -1
  198. package/front_end/third_party/puppeteer/package/lib/puppeteer/util/version.js +1 -1
  199. package/front_end/third_party/puppeteer/package/lib/types.d.ts +34 -55
  200. package/front_end/third_party/puppeteer/package/package.json +4 -4
  201. package/front_end/third_party/puppeteer/package/src/api/Browser.ts +13 -1
  202. package/front_end/third_party/puppeteer/package/src/api/BrowserContext.ts +2 -3
  203. package/front_end/third_party/puppeteer/package/src/api/CDPSession.ts +3 -1
  204. package/front_end/third_party/puppeteer/package/src/api/HTTPResponse.ts +3 -2
  205. package/front_end/third_party/puppeteer/package/src/api/Realm.ts +1 -2
  206. package/front_end/third_party/puppeteer/package/src/api/Target.ts +1 -1
  207. package/front_end/third_party/puppeteer/package/src/api/locators/locators.ts +1 -2
  208. package/front_end/third_party/puppeteer/package/src/bidi/HTTPResponse.ts +3 -1
  209. package/front_end/third_party/puppeteer/package/src/bidi/core/Realm.ts +1 -3
  210. package/front_end/third_party/puppeteer/package/src/cdp/Browser.ts +13 -3
  211. package/front_end/third_party/puppeteer/package/src/cdp/Frame.ts +1 -1
  212. package/front_end/third_party/puppeteer/package/src/cdp/FrameManager.ts +24 -13
  213. package/front_end/third_party/puppeteer/package/src/cdp/HTTPResponse.ts +4 -1
  214. package/front_end/third_party/puppeteer/package/src/cdp/Input.ts +1 -1
  215. package/front_end/third_party/puppeteer/package/src/cdp/LifecycleWatcher.ts +1 -4
  216. package/front_end/third_party/puppeteer/package/src/cdp/Target.ts +2 -4
  217. package/front_end/third_party/puppeteer/package/src/cdp/WebMCP.ts +7 -76
  218. package/front_end/third_party/puppeteer/package/src/common/ConnectOptions.ts +1 -4
  219. package/front_end/third_party/puppeteer/package/src/node/BrowserLauncher.ts +4 -1
  220. package/front_end/third_party/puppeteer/package/src/node/LaunchOptions.ts +5 -0
  221. package/front_end/third_party/puppeteer/package/src/revisions.ts +1 -1
  222. package/front_end/third_party/puppeteer/package/src/util/httpUtils.ts +5 -5
  223. package/front_end/third_party/puppeteer/package/src/util/version.ts +1 -1
  224. package/front_end/third_party/puppeteer/third_party/parsel/README.chromium +1 -1
  225. package/front_end/ui/components/dialogs/Dialog.ts +3 -0
  226. package/front_end/ui/components/markdown_view/MarkdownLinksMap.ts +0 -7
  227. package/front_end/ui/helpers/OpenInNewTab.ts +15 -7
  228. package/front_end/ui/legacy/InspectorDrawerView.ts +7 -5
  229. package/front_end/ui/legacy/ListWidget.ts +12 -0
  230. package/front_end/ui/legacy/SplitWidget.ts +12 -11
  231. package/front_end/ui/legacy/TabbedPane.ts +1 -108
  232. package/front_end/ui/legacy/components/data_grid/DataGrid.ts +9 -0
  233. package/front_end/ui/legacy/components/data_grid/DataGridElement.ts +192 -139
  234. package/front_end/ui/legacy/components/data_grid/DataGridExporter.ts +89 -0
  235. package/front_end/ui/legacy/components/data_grid/dataGrid.css +10 -0
  236. package/front_end/ui/legacy/components/data_grid/data_grid.ts +3 -1
  237. package/front_end/ui/legacy/components/object_ui/CustomPreviewComponent.ts +8 -1
  238. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +110 -17
  239. package/front_end/ui/legacy/components/perf_ui/FlameChart.ts +89 -40
  240. package/front_end/ui/legacy/treeoutline.css +1 -1
  241. package/front_end/ui/visual_logging/KnownContextValues.ts +3 -0
  242. package/package.json +4 -3
  243. package/front_end/Images/navigationControls.png +0 -0
  244. package/front_end/Images/navigationControls_2x.png +0 -0
  245. package/front_end/Images/popoverArrows.png +0 -0
  246. package/front_end/entrypoints/greendev_floaty/FloatyEntrypoint.ts +0 -746
  247. package/front_end/entrypoints/greendev_floaty/floaty.css +0 -301
  248. package/front_end/entrypoints/greendev_floaty/floaty.html +0 -38
  249. package/front_end/entrypoints/greendev_floaty/greendev_floaty.ts +0 -415
  250. package/front_end/models/ai_assistance/agents/GreenDevAgent.ts +0 -834
  251. package/front_end/models/ai_assistance/agents/GreenDevAgentAntigravityCliSocketClient.ts +0 -53
  252. package/front_end/models/ai_assistance/agents/GreenDevAgentGeminiCliSocketClient.ts +0 -117
  253. package/front_end/models/annotations/AnnotationRepository.ts +0 -238
  254. package/front_end/models/annotations/AnnotationType.ts +0 -10
  255. package/front_end/models/annotations/README.md +0 -7
  256. package/front_end/models/annotations/annotations.ts +0 -6
  257. package/front_end/models/greendev/Prototypes.ts +0 -68
  258. package/front_end/models/greendev/README.md +0 -5
  259. package/front_end/models/greendev/greendev.ts +0 -5
  260. package/front_end/panels/common/Annotation.ts +0 -184
  261. package/front_end/panels/common/AnnotationManager.ts +0 -208
  262. package/front_end/panels/common/annotation.css +0 -40
  263. package/front_end/panels/greendev/GreenDevPanel.css +0 -282
  264. package/front_end/panels/greendev/GreenDevPanel.ts +0 -364
  265. package/front_end/panels/greendev/GreenDevShared.ts +0 -13
  266. package/front_end/panels/greendev/greendev-meta.ts +0 -52
  267. package/front_end/panels/greendev/greendev.ts +0 -9
@@ -518,11 +518,11 @@ export class HeapSnapshotNode implements HeapSnapshotItem {
518
518
  }
519
519
  }
520
520
 
521
- detachedness(): DOMLinkState {
521
+ detachedness(): HeapSnapshotModel.HeapSnapshotModel.DOMLinkState {
522
522
  return this.#detachednessAndClassIndex() & BITMASK_FOR_DOM_LINK_STATE;
523
523
  }
524
524
 
525
- setDetachedness(detachedness: DOMLinkState): void {
525
+ setDetachedness(detachedness: HeapSnapshotModel.HeapSnapshotModel.DOMLinkState): void {
526
526
  let value = this.#detachednessAndClassIndex();
527
527
  value &= ~BITMASK_FOR_DOM_LINK_STATE; // Clear the old bits.
528
528
  value |= detachedness; // Set the new bits.
@@ -567,6 +567,31 @@ export class HeapSnapshotNode implements HeapSnapshotItem {
567
567
  return undefined;
568
568
  }
569
569
 
570
+ nodeValueAsInt(): number|undefined {
571
+ if (this.rawType() !== this.snapshot.nodeNumberType) {
572
+ return undefined;
573
+ }
574
+ if (this.rawName() !== 'int') {
575
+ return undefined;
576
+ }
577
+ const valNode = this.findInternalEdgeTarget('value');
578
+ if (!valNode) {
579
+ return undefined;
580
+ }
581
+ const value = parseInt(valNode.rawName(), 10);
582
+ return isNaN(value) ? undefined : value;
583
+ }
584
+
585
+ nodeStringLength(): number|undefined {
586
+ const lengthNode = this.findInternalEdgeTarget('length');
587
+ return lengthNode ? lengthNode.nodeValueAsInt() : undefined;
588
+ }
589
+
590
+ nodeStringHash(): number|undefined {
591
+ const hashNode = this.findInternalEdgeTarget('hash');
592
+ return hashNode ? hashNode.nodeValueAsInt() : undefined;
593
+ }
594
+
570
595
  nodeIsTruncatedString(): boolean {
571
596
  const truncNode = this.findInternalEdgeTarget('truncated');
572
597
  if (!truncNode) {
@@ -879,14 +904,7 @@ export class SecondaryInitManager {
879
904
  }
880
905
  }
881
906
 
882
- /**
883
- * DOM node link state.
884
- */
885
- const enum DOMLinkState {
886
- UNKNOWN = 0,
887
- ATTACHED = 1,
888
- DETACHED = 2,
889
- }
907
+ // Bitmask for accessing DOMLinkState in the detachedness field.
890
908
  const BITMASK_FOR_DOM_LINK_STATE = 3;
891
909
 
892
910
  // The class index is stored in the upper 30 bits of the detachedness field.
@@ -911,6 +929,13 @@ const MIN_OBJECT_COUNT_PER_INTERFACE = 2;
911
929
  // long tail of unpopular interfaces that don't help analysis.
912
930
  const MIN_OBJECT_PROPORTION_PER_INTERFACE = 1000;
913
931
 
932
+ // Values in the nodeNativeContextAttribution array:
933
+ // >= 0: The ordinal of the specific native context that owns the object.
934
+ // -1 (NO_NATIVE_CONTEXT): The object is not reachable from any native context.
935
+ // -2 (SHARED_NATIVE_CONTEXT): The object is reachable from multiple native contexts.
936
+ const NO_NATIVE_CONTEXT = -1;
937
+ const SHARED_NATIVE_CONTEXT = -2;
938
+
914
939
  export abstract class HeapSnapshot {
915
940
  nodes: Platform.TypedArrayUtilities.BigUint32Array;
916
941
  containmentEdges: Platform.TypedArrayUtilities.BigUint32Array;
@@ -986,6 +1011,9 @@ export abstract class HeapSnapshot {
986
1011
  #nodeDistancesForRetainersView: Int32Array|undefined;
987
1012
  #edgeNamesThatAreNotWeakMaps: Platform.TypedArrayUtilities.BitVector;
988
1013
  detachednessAndClassIndexArray?: Uint32Array;
1014
+ nodeNativeContextAttribution!: Int32Array;
1015
+ #nativeContextSizes!: HeapSnapshotModel.HeapSnapshotModel.NativeContextSizes;
1016
+ #nativeContextOrdinals!: number[];
989
1017
  #interfaceNames = new Map<string, number>();
990
1018
  #interfaceDefinitions?: InterfaceDefinition[];
991
1019
 
@@ -1083,8 +1111,11 @@ export abstract class HeapSnapshot {
1083
1111
  this.buildSamples();
1084
1112
  this.#progress.updateStatus('Building locations…');
1085
1113
  this.buildLocationMap();
1114
+ this.#progress.updateStatus('Calculating native context attribution…');
1115
+ this.calculateNativeContextAttribution();
1086
1116
  this.#progress.updateStatus('Calculating retained sizes…');
1087
1117
  await this.installResultsFromSecondThread(resultsFromSecondWorker);
1118
+ this.calculateNativeContextSizes();
1088
1119
  this.#progress.updateStatus('Calculating statistics…');
1089
1120
  this.calculateStatistics();
1090
1121
 
@@ -1127,6 +1158,27 @@ export abstract class HeapSnapshot {
1127
1158
  return undefined;
1128
1159
  }
1129
1160
 
1161
+ getObjectInfo(nodeIndex: number): HeapSnapshotModel.HeapSnapshotModel.ObjectInfo {
1162
+ const nodesLength = this.nodes.length;
1163
+ const nodeFieldCount = this.nodeFieldCount;
1164
+ if (!Number.isInteger(nodeIndex) || nodeIndex < 0 || nodeIndex >= nodesLength || nodeIndex % nodeFieldCount !== 0) {
1165
+ throw new Error('Invalid nodeIndex ' + nodeIndex);
1166
+ }
1167
+ const node = this.createNode(nodeIndex);
1168
+ return {
1169
+ id: node.id(),
1170
+ name: node.name(),
1171
+ type: node.type(),
1172
+ nodeIndex,
1173
+ detachedness: node.detachedness(),
1174
+ selfSize: node.selfSize(),
1175
+ retainedSize: node.retainedSize(),
1176
+ distance: node.distance(),
1177
+ edgeCount: node.edgesCount(),
1178
+ retainerCount: node.retainersCount(),
1179
+ };
1180
+ }
1181
+
1130
1182
  private startInitStep1InSecondThread(secondWorker: PlatformApi.HostRuntime.WorkerMessagePort):
1131
1183
  Promise<ResultsFromSecondWorker> {
1132
1184
  const resultsFromSecondWorker = new Promise<ResultsFromSecondWorker>((resolve, reject) => {
@@ -1366,7 +1418,9 @@ export abstract class HeapSnapshot {
1366
1418
 
1367
1419
  getDuplicateStrings(): HeapSnapshotModel.HeapSnapshotModel.DuplicateStringGroup[] {
1368
1420
  const filter = this.createNamedFilter('duplicatedStrings');
1369
- const groupsMap = new Map<string, HeapSnapshotModel.HeapSnapshotModel.DuplicateStringGroup>();
1421
+ const untruncatedGroups = new Map<string, HeapSnapshotModel.HeapSnapshotModel.DuplicateStringGroup>();
1422
+
1423
+ const truncatedGroups = new Map<string, HeapSnapshotModel.HeapSnapshotModel.DuplicateStringGroup[]>();
1370
1424
  const node = this.createNode(0);
1371
1425
 
1372
1426
  for (let i = 0; i < this.nodeCount; ++i) {
@@ -1375,39 +1429,69 @@ export abstract class HeapSnapshot {
1375
1429
  const name = node.name();
1376
1430
  const truncated = node.nodeIsTruncatedString();
1377
1431
 
1378
- // Note that there is exactly one group for each duplicated string value. So
1379
- // truncated strings might end up in the same group as non-truncated strings if the
1380
- // prefix matches the non-truncated string. This should be unlikely though and we
1381
- // don't handle this here to avoid that additional complexity.
1382
- let group = groupsMap.get(name);
1383
- if (!group) {
1384
- group = {
1385
- value: name,
1386
- count: 0,
1387
- totalSelfSize: 0,
1388
- totalRetainedSize: 0,
1389
- nodes: [],
1390
- truncated,
1391
- };
1392
- groupsMap.set(name, group);
1393
- } else if (truncated) {
1394
- // Make sure the truncated flag is set in case the group was initially created by a
1395
- // non-truncated string.
1396
- group.truncated = true;
1432
+ if (truncated) {
1433
+ const length = node.nodeStringLength();
1434
+ const hash = node.nodeStringHash();
1435
+ let groups = truncatedGroups.get(name);
1436
+ if (!groups) {
1437
+ groups = [];
1438
+ truncatedGroups.set(name, groups);
1439
+ }
1440
+ let group = groups.find(g => g.length === length && g.hash === hash);
1441
+ if (!group) {
1442
+ group = {
1443
+ value: name,
1444
+ count: 0,
1445
+ totalSelfSize: 0,
1446
+ totalRetainedSize: 0,
1447
+ nodes: [],
1448
+ truncated: true,
1449
+ length,
1450
+ hash,
1451
+ };
1452
+ groups.push(group);
1453
+ }
1454
+ group.count++;
1455
+ group.totalSelfSize += node.selfSize();
1456
+ group.totalRetainedSize += node.retainedSize();
1457
+ group.nodes.push({
1458
+ id: node.id(),
1459
+ selfSize: node.selfSize(),
1460
+ retainedSize: node.retainedSize(),
1461
+ distance: node.distance(),
1462
+ });
1463
+ } else {
1464
+ let group = untruncatedGroups.get(name);
1465
+ if (!group) {
1466
+ group = {
1467
+ value: name,
1468
+ count: 0,
1469
+ totalSelfSize: 0,
1470
+ totalRetainedSize: 0,
1471
+ nodes: [],
1472
+ truncated: false,
1473
+ };
1474
+ untruncatedGroups.set(name, group);
1475
+ }
1476
+ group.count++;
1477
+ group.totalSelfSize += node.selfSize();
1478
+ group.totalRetainedSize += node.retainedSize();
1479
+ group.nodes.push({
1480
+ id: node.id(),
1481
+ selfSize: node.selfSize(),
1482
+ retainedSize: node.retainedSize(),
1483
+ distance: node.distance(),
1484
+ });
1397
1485
  }
1398
- group.count++;
1399
- group.totalSelfSize += node.selfSize();
1400
- group.totalRetainedSize += node.retainedSize();
1401
- group.nodes.push({
1402
- id: node.id(),
1403
- selfSize: node.selfSize(),
1404
- retainedSize: node.retainedSize(),
1405
- distance: node.distance(),
1406
- });
1407
1486
  }
1408
1487
  }
1409
1488
 
1410
- return Array.from(groupsMap.values()).sort((a, b) => b.totalRetainedSize - a.totalRetainedSize);
1489
+ const allGroups: HeapSnapshotModel.HeapSnapshotModel.DuplicateStringGroup[] = [
1490
+ ...untruncatedGroups.values(),
1491
+ ...Array.from(truncatedGroups.values()).flat(),
1492
+ ];
1493
+
1494
+ return allGroups.sort((a, b) => b.totalRetainedSize - a.totalRetainedSize);
1411
1495
  }
1412
1496
 
1413
1497
  private createNodeIdFilter(minNodeId: number, maxNodeId: number): (arg0: HeapSnapshotNode) => boolean {
@@ -1485,7 +1569,7 @@ export abstract class HeapSnapshot {
1485
1569
  case 'objectsRetainedByDetachedDomNodes':
1486
1570
  // Traverse the graph, avoiding detached nodes.
1487
1571
  traverse((_node: HeapSnapshotNode, edge: HeapSnapshotEdge) => {
1488
- return edge.node().detachedness() !== DOMLinkState.DETACHED;
1572
+ return edge.node().detachedness() !== HeapSnapshotModel.HeapSnapshotModel.DOMLinkState.DETACHED;
1489
1573
  });
1490
1574
  markUnreachableNodes();
1491
1575
  return (node: HeapSnapshotNode) => !getBit(node);
@@ -1497,33 +1581,8 @@ export abstract class HeapSnapshot {
1497
1581
  });
1498
1582
  markUnreachableNodes();
1499
1583
  return (node: HeapSnapshotNode) => !getBit(node);
1500
- case 'duplicatedStrings': {
1501
- const stringToNodeIndexMap = new Map<string, number>();
1502
- const node = this.createNode(0);
1503
- for (let i = 0; i < this.nodeCount; ++i) {
1504
- node.nodeIndex = i * this.nodeFieldCount;
1505
- const rawType = node.rawType();
1506
- if (rawType === this.nodeStringType || rawType === this.nodeConsStringType) {
1507
- // Check whether the cons string is already "flattened", meaning
1508
- // that one of its two parts is the empty string. If so, we should
1509
- // skip it. We don't help anyone by reporting a flattened cons
1510
- // string as a duplicate with its own content, since V8 controls
1511
- // that behavior internally.
1512
- if (node.isFlatConsString()) {
1513
- continue;
1514
- }
1515
- const name = node.name();
1516
- const alreadyVisitedNodeIndex = stringToNodeIndexMap.get(name);
1517
- if (alreadyVisitedNodeIndex === undefined) {
1518
- stringToNodeIndexMap.set(name, node.nodeIndex);
1519
- } else {
1520
- bitmap.setBit(alreadyVisitedNodeIndex / this.nodeFieldCount);
1521
- bitmap.setBit(node.nodeIndex / this.nodeFieldCount);
1522
- }
1523
- }
1524
- }
1525
- return getBit;
1526
- }
1584
+ case 'duplicatedStrings':
1585
+ return this.createDuplicatedStringsFilter(bitmap);
1527
1586
  case 'objectsRetainedByEventHandlers': {
1528
1587
  // This filter is based on the assumption that event handler functions are contained
1529
1588
  // (directly or indirectly) by V8EventListener nodes. In particular, the callback_object_
@@ -1589,10 +1648,87 @@ export abstract class HeapSnapshot {
1589
1648
 
1590
1649
  return (node: HeapSnapshotNode) => !getBit(node);
1591
1650
  }
1651
+ case 'sharedNativeContext':
1652
+ return (node: HeapSnapshotNode) => {
1653
+ const ordinal = node.nodeIndex / this.nodeFieldCount;
1654
+ return this.nodeNativeContextAttribution[ordinal] === SHARED_NATIVE_CONTEXT;
1655
+ };
1656
+ case 'noNativeContext':
1657
+ return (node: HeapSnapshotNode) => {
1658
+ const ordinal = node.nodeIndex / this.nodeFieldCount;
1659
+ return this.nodeNativeContextAttribution[ordinal] === NO_NATIVE_CONTEXT;
1660
+ };
1661
+ default:
1662
+ if (filterName.startsWith('nativeContext_')) {
1663
+ const targetNodeIndex = Number(filterName.substring('nativeContext_'.length));
1664
+ const targetOrdinal = targetNodeIndex / this.nodeFieldCount;
1665
+ return (node: HeapSnapshotNode) => {
1666
+ const ordinal = node.nodeIndex / this.nodeFieldCount;
1667
+ return this.nodeNativeContextAttribution[ordinal] === targetOrdinal;
1668
+ };
1669
+ }
1592
1670
  }
1593
1671
  throw new Error('Invalid filter name');
1594
1672
  }
1595
1673
 
1674
+ private createDuplicatedStringsFilter(bitmap: Platform.TypedArrayUtilities.BitVector):
1675
+ (node: HeapSnapshotNode) => boolean {
1676
+ const untruncatedStringToNodeIndexMap = new Map<string, number>();
1677
+ const truncatedStringToNodeIndexesMap =
1678
+ new Map<string, Array<{nodeIndex: number, length?: number, hash?: number}>>();
1679
+ const node = this.createNode(0);
1680
+ for (let i = 0; i < this.nodeCount; ++i) {
1681
+ node.nodeIndex = i * this.nodeFieldCount;
1682
+ const rawType = node.rawType();
1683
+ if (rawType !== this.nodeStringType && rawType !== this.nodeConsStringType) {
1684
+ continue;
1685
+ }
1686
+ // Check whether the cons string is already "flattened", meaning
1687
+ // that one of its two parts is the empty string. If so, we should
1688
+ // skip it. We don't help anyone by reporting a flattened cons
1689
+ // string as a duplicate with its own content, since V8 controls
1690
+ // that behavior internally.
1691
+ if (node.isFlatConsString()) {
1692
+ continue;
1693
+ }
1694
+ // Skip string node used e.g. for encoding int values in the heap
1695
+ // snapshot. Real JS strings will have self size greater than 0.
1696
+ if (node.selfSize() === 0) {
1697
+ continue;
1698
+ }
1699
+ const name = node.name();
1700
+ const truncated = node.nodeIsTruncatedString();
1701
+ if (truncated) {
1702
+ const length = node.nodeStringLength();
1703
+ const hash = node.nodeStringHash();
1704
+ let entries = truncatedStringToNodeIndexesMap.get(name);
1705
+ if (!entries) {
1706
+ entries = [];
1707
+ truncatedStringToNodeIndexesMap.set(name, entries);
1708
+ }
1709
+ const match = entries.find(e => e.length === length && e.hash === hash);
1710
+ if (match) {
1711
+ bitmap.setBit(match.nodeIndex / this.nodeFieldCount);
1712
+ bitmap.setBit(node.nodeIndex / this.nodeFieldCount);
1713
+ } else {
1714
+ entries.push({nodeIndex: node.nodeIndex, length, hash});
1715
+ }
1716
+ } else {
1717
+ const alreadyVisitedNodeIndex = untruncatedStringToNodeIndexMap.get(name);
1718
+ if (alreadyVisitedNodeIndex === undefined) {
1719
+ untruncatedStringToNodeIndexMap.set(name, node.nodeIndex);
1720
+ } else {
1721
+ bitmap.setBit(alreadyVisitedNodeIndex / this.nodeFieldCount);
1722
+ bitmap.setBit(node.nodeIndex / this.nodeFieldCount);
1723
+ }
1724
+ }
1725
+ }
1726
+ return (node: HeapSnapshotNode): boolean => {
1727
+ const ordinal = node.nodeIndex / this.nodeFieldCount;
1728
+ return bitmap.getBit(ordinal);
1729
+ };
1730
+ }
1731
+
1596
1732
  getAggregatesByClassKey(sortedIndexes: boolean, key?: string, filter?: ((arg0: HeapSnapshotNode) => boolean)):
1597
1733
  Record<string, HeapSnapshotModel.HeapSnapshotModel.AggregatedInfo> {
1598
1734
  let aggregates: Record<string, HeapSnapshotModel.HeapSnapshotModel.AggregatedInfo>;
@@ -2338,6 +2474,255 @@ export abstract class HeapSnapshot {
2338
2474
  }
2339
2475
  }
2340
2476
 
2477
+ private calculateNativeContextAttribution(): void {
2478
+ // Map from node ordinal to its attributed native context.
2479
+ // Value is either a native context ordinal (>= 0), NO_NATIVE_CONTEXT, or SHARED_NATIVE_CONTEXT.
2480
+ const attribution = new Int32Array(this.nodeCount).fill(NO_NATIVE_CONTEXT);
2481
+
2482
+ // First, try to infer a fixed native context for each object directly (e.g., via its map or direct links).
2483
+ // These direct attributions are considered "fixed" and will not be overwritten by the subsequent propagation phase.
2484
+ const isFixed = Platform.TypedArrayUtilities.createBitVector(this.nodeCount);
2485
+ const edgeTargets = this.buildInitEdgeTargets();
2486
+ this.#nativeContextOrdinals = [];
2487
+ for (let ordinal = 0; ordinal < this.nodeCount; ordinal++) {
2488
+ if (this.isNativeContext(ordinal)) {
2489
+ this.#nativeContextOrdinals.push(ordinal);
2490
+ attribution[ordinal] = ordinal;
2491
+ isFixed.setBit(ordinal);
2492
+ } else {
2493
+ const owner = this.inferFixedNativeContextForOrdinal(ordinal, edgeTargets);
2494
+ if (owner >= 0) {
2495
+ attribution[ordinal] = owner;
2496
+ isFixed.setBit(ordinal);
2497
+ }
2498
+ }
2499
+ }
2500
+
2501
+ // Propagate the fixed native context attributions to the rest of the nodes based on reachability.
2502
+ this.propagateNativeContextAttribution(attribution, isFixed);
2503
+ this.nodeNativeContextAttribution = attribution;
2504
+ }
2505
+
2506
+ private calculateNativeContextSizes(): void {
2507
+ const nodeFieldCount = this.nodeFieldCount;
2508
+ const node = this.createNode(0);
2509
+ const nativeContexts: HeapSnapshotModel.HeapSnapshotModel.NativeContextSize[] = [];
2510
+ const ordinalToInfo = new Map<number, HeapSnapshotModel.HeapSnapshotModel.NativeContextSize>();
2511
+ for (const ordinal of this.#nativeContextOrdinals) {
2512
+ node.nodeIndex = ordinal * nodeFieldCount;
2513
+ const info = {
2514
+ nodeId: node.id(),
2515
+ nodeIndex: node.nodeIndex,
2516
+ nodeName: node.name(),
2517
+ attributedSize: 0,
2518
+ retainedSize: node.retainedSize(),
2519
+ selfSize: node.selfSize(),
2520
+ };
2521
+ nativeContexts.push(info);
2522
+ ordinalToInfo.set(ordinal, info);
2523
+ }
2524
+
2525
+ let sharedSize = 0;
2526
+ let noAttributionSize = 0;
2527
+ const selfSizeOffset = this.nodeSelfSizeOffset;
2528
+ const nodes = this.nodes;
2529
+
2530
+ for (let i = 0; i < this.nodeCount; ++i) {
2531
+ const ownerOrdinal = this.nodeNativeContextAttribution[i];
2532
+ const selfSize = nodes.getValue(i * nodeFieldCount + selfSizeOffset);
2533
+ if (ownerOrdinal === SHARED_NATIVE_CONTEXT) {
2534
+ sharedSize += selfSize;
2535
+ } else if (ownerOrdinal === NO_NATIVE_CONTEXT) {
2536
+ noAttributionSize += selfSize;
2537
+ } else {
2538
+ console.assert(ownerOrdinal >= 0, 'ownerOrdinal should be >= 0');
2539
+ const info = ordinalToInfo.get(ownerOrdinal);
2540
+ console.assert(info !== undefined, 'info should exist');
2541
+ if (info) {
2542
+ info.attributedSize += selfSize;
2543
+ }
2544
+ }
2545
+ }
2546
+
2547
+ this.#nativeContextSizes = {
2548
+ nativeContexts,
2549
+ sharedSize,
2550
+ noAttributionSize,
2551
+ };
2552
+ }
2553
+
2554
+ // Precomputes and maps specific outgoing edge targets for every node in the heap.
2555
+ // For each node ordinal, it stores the target ordinal of its:
2556
+ // - 'native_context' edge (in the returned 'nativeContext' array)
2557
+ // - 'map' edge (in the returned 'map' array)
2558
+ // This allows fast O(1) lookups of these key edges during attribution.
2559
+ private buildInitEdgeTargets(): {
2560
+ nativeContext: Int32Array,
2561
+ map: Int32Array,
2562
+ } {
2563
+ const {
2564
+ nodeCount,
2565
+ nodeFieldCount,
2566
+ containmentEdges,
2567
+ edgeFieldsCount,
2568
+ edgeTypeOffset,
2569
+ edgeNameOffset,
2570
+ edgeToNodeOffset,
2571
+ edgeInternalType,
2572
+ firstEdgeIndexes,
2573
+ strings,
2574
+ } = this;
2575
+
2576
+ const nativeContext = new Int32Array(nodeCount).fill(-1);
2577
+ const map = new Int32Array(nodeCount).fill(-1);
2578
+
2579
+ const nativeContextIdx = strings.indexOf('native_context');
2580
+ const mapIdx = strings.indexOf('map');
2581
+
2582
+ for (let ordinal = 0; ordinal < nodeCount; ordinal++) {
2583
+ const first = firstEdgeIndexes[ordinal];
2584
+ const last = firstEdgeIndexes[ordinal + 1];
2585
+ for (let edgeIndex = first; edgeIndex < last; edgeIndex += edgeFieldsCount) {
2586
+ const edgeType = containmentEdges.getValue(edgeIndex + edgeTypeOffset);
2587
+ if (edgeType !== edgeInternalType) {
2588
+ continue;
2589
+ }
2590
+
2591
+ const nameIdx = containmentEdges.getValue(edgeIndex + edgeNameOffset);
2592
+ const childNodeIndex = containmentEdges.getValue(edgeIndex + edgeToNodeOffset);
2593
+ const childOrdinal = childNodeIndex / nodeFieldCount;
2594
+
2595
+ if (nameIdx === nativeContextIdx && nativeContext[ordinal] === -1 && this.isNativeContext(childOrdinal)) {
2596
+ nativeContext[ordinal] = childOrdinal;
2597
+ } else if (nameIdx === mapIdx && map[ordinal] === -1) {
2598
+ map[ordinal] = childOrdinal;
2599
+ }
2600
+ }
2601
+ }
2602
+
2603
+ return {nativeContext, map};
2604
+ }
2605
+
2606
+ // Infers the native context for a node by looking at its Map.
2607
+ // In V8, objects point to their Map, and Maps point to their Meta-Map (the Map of the Map).
2608
+ // To save space, individual Maps do not have a direct link to the NativeContext.
2609
+ // Instead, the Meta-Map (which is unique per NativeContext) has a 'native_context' edge.
2610
+ // Thus, we can find the NativeContext of an object by traversing:
2611
+ // Object -> Map -> Meta-Map -> NativeContext.
2612
+ private inferFixedNativeContextForOrdinal(ordinal: number, edgeTargets: {
2613
+ nativeContext: Int32Array,
2614
+ map: Int32Array,
2615
+ }): number {
2616
+ const mapOrdinal = edgeTargets.map[ordinal];
2617
+ if (mapOrdinal >= 0) {
2618
+ const metaMapOrdinal = edgeTargets.map[mapOrdinal];
2619
+ if (metaMapOrdinal >= 0) {
2620
+ const mapNativeContextOrdinal = edgeTargets.nativeContext[metaMapOrdinal];
2621
+ if (mapNativeContextOrdinal >= 0) {
2622
+ return mapNativeContextOrdinal;
2623
+ }
2624
+ }
2625
+ }
2626
+
2627
+ return NO_NATIVE_CONTEXT;
2628
+ }
2629
+
2630
+ private mergeNativeContextOwner(current: number, incoming: number): number {
2631
+ console.assert(incoming !== NO_NATIVE_CONTEXT, 'Incoming owner should not be NO_NATIVE_CONTEXT');
2632
+ if (current === SHARED_NATIVE_CONTEXT || incoming === SHARED_NATIVE_CONTEXT) {
2633
+ return SHARED_NATIVE_CONTEXT;
2634
+ }
2635
+ if (current === NO_NATIVE_CONTEXT) {
2636
+ return incoming;
2637
+ }
2638
+ if (current === incoming) {
2639
+ return current;
2640
+ }
2641
+ return SHARED_NATIVE_CONTEXT;
2642
+ }
2643
+
2644
+ private propagateNativeContextAttribution(attribution: Int32Array,
2645
+ isFixed: Platform.TypedArrayUtilities.BitVector): void {
2646
+ const {
2647
+ nodeCount,
2648
+ containmentEdges,
2649
+ edgeFieldsCount,
2650
+ edgeTypeOffset,
2651
+ edgeToNodeOffset,
2652
+ edgeShortcutType,
2653
+ edgeWeakType,
2654
+ nodeFieldCount,
2655
+ firstEdgeIndexes,
2656
+ } = this;
2657
+
2658
+ // Initialize the queue with all nodes that have a fixed (directly inferred) native context.
2659
+ // Propagation will start from these "anchors".
2660
+ const queue: number[] = [];
2661
+ for (let ordinal = 0; ordinal < nodeCount; ordinal++) {
2662
+ if (isFixed.getBit(ordinal)) {
2663
+ queue.push(ordinal);
2664
+ }
2665
+ }
2666
+
2667
+ let queueIndex = 0;
2668
+ while (queueIndex < queue.length) {
2669
+ const ordinal = queue[queueIndex];
2670
+ queueIndex++;
2671
+
2672
+ const current = attribution[ordinal];
2673
+ console.assert(current !== NO_NATIVE_CONTEXT, 'Queue should not contain unattributed nodes');
2674
+
2675
+ const first = firstEdgeIndexes[ordinal];
2676
+ const last = firstEdgeIndexes[ordinal + 1];
2677
+ for (let edgeIndex = first; edgeIndex < last; edgeIndex += edgeFieldsCount) {
2678
+ const edgeType = containmentEdges.getValue(edgeIndex + edgeTypeOffset);
2679
+ if (edgeType === edgeShortcutType || edgeType === edgeWeakType) {
2680
+ continue;
2681
+ }
2682
+ const childNodeIndex = containmentEdges.getValue(edgeIndex + edgeToNodeOffset);
2683
+ const childOrdinal = childNodeIndex / nodeFieldCount;
2684
+
2685
+ // Skip if it is a self-loop, or if the child node has a "fixed" attribution.
2686
+ // Fixed attributions are directly inferred and cannot be overwritten by propagation.
2687
+ if (childOrdinal === ordinal || isFixed.getBit(childOrdinal)) {
2688
+ continue;
2689
+ }
2690
+
2691
+ // Merge the parent's native context owner into the child's owner.
2692
+ // Nodes can be visited multiple times: first, a node might be attributed to a specific
2693
+ // native context. If it is later reached by a different native context, the merge will
2694
+ // transition its owner to SHARED_NATIVE_CONTEXT.
2695
+ // If the owner changes (e.g., transitioning to SHARED), we queue the child again
2696
+ // to propagate the updated owner to its retainees.
2697
+ const merged = this.mergeNativeContextOwner(attribution[childOrdinal], current);
2698
+ if (merged !== attribution[childOrdinal]) {
2699
+ attribution[childOrdinal] = merged;
2700
+ queue.push(childOrdinal);
2701
+ }
2702
+ }
2703
+ }
2704
+ }
2705
+
2706
+ getNativeContextSizes(): HeapSnapshotModel.HeapSnapshotModel.NativeContextSizes {
2707
+ return this.#nativeContextSizes;
2708
+ }
2709
+
2710
+ nodeNativeContext(nodeIndex: number): number {
2711
+ const ordinal = nodeIndex / this.nodeFieldCount;
2712
+ const nativeContextOrdinal = this.nodeNativeContextAttribution[ordinal];
2713
+ if (nativeContextOrdinal < 0) {
2714
+ return nativeContextOrdinal;
2715
+ }
2716
+ return nativeContextOrdinal * this.nodeFieldCount;
2717
+ }
2718
+
2719
+ private isNativeContext(nodeOrdinal: number): boolean {
2720
+ const nameIdx = this.nodes.getValue(nodeOrdinal * this.nodeFieldCount + this.nodeNameOffset);
2721
+ const name = this.strings[nameIdx];
2722
+ return name === 'system / NativeContext' || name.startsWith('system / NativeContext / ') ||
2723
+ name === 'Detached system / NativeContext' || name.startsWith('Detached system / NativeContext / ');
2724
+ }
2725
+
2341
2726
  interfaceDefinitions(): string {
2342
2727
  return JSON.stringify(this.#interfaceDefinitions ?? []);
2343
2728
  }
@@ -2665,9 +3050,9 @@ export abstract class HeapSnapshot {
2665
3050
  node.nodeIndex = nodeIndex;
2666
3051
  node.setDetachedness(newState);
2667
3052
 
2668
- if (newState === DOMLinkState.ATTACHED) {
3053
+ if (newState === HeapSnapshotModel.HeapSnapshotModel.DOMLinkState.ATTACHED) {
2669
3054
  attached.push(nodeOrdinal);
2670
- } else if (newState === DOMLinkState.DETACHED) {
3055
+ } else if (newState === HeapSnapshotModel.HeapSnapshotModel.DOMLinkState.DETACHED) {
2671
3056
  // Detached state: Rewire node name.
2672
3057
  addDetachedPrefixToNodeName(snapshot, nodeIndex);
2673
3058
  detached.push(nodeOrdinal);
@@ -2691,7 +3076,7 @@ export abstract class HeapSnapshot {
2691
3076
  node.nodeIndex = nodeOrdinal * this.nodeFieldCount;
2692
3077
  const state = node.detachedness();
2693
3078
  // Bail out for objects that have no known state. For all other objects set that state.
2694
- if (state === DOMLinkState.UNKNOWN) {
3079
+ if (state === HeapSnapshotModel.HeapSnapshotModel.DOMLinkState.UNKNOWN) {
2695
3080
  continue;
2696
3081
  }
2697
3082
  processNode(this, nodeOrdinal, state);
@@ -2699,7 +3084,7 @@ export abstract class HeapSnapshot {
2699
3084
  // 2. If the parent is attached, then the child is also attached.
2700
3085
  while (attached.length !== 0) {
2701
3086
  const nodeOrdinal = (attached.pop() as number);
2702
- propagateState(this, nodeOrdinal, DOMLinkState.ATTACHED);
3087
+ propagateState(this, nodeOrdinal, HeapSnapshotModel.HeapSnapshotModel.DOMLinkState.ATTACHED);
2703
3088
  }
2704
3089
  // 3. If the parent is not attached, then the child inherits the parent's state.
2705
3090
  while (detached.length !== 0) {
@@ -2707,10 +3092,10 @@ export abstract class HeapSnapshot {
2707
3092
  node.nodeIndex = nodeOrdinal * this.nodeFieldCount;
2708
3093
  const nodeState = node.detachedness();
2709
3094
  // Ignore if the node has been found through propagating forward attached state.
2710
- if (nodeState === DOMLinkState.ATTACHED) {
3095
+ if (nodeState === HeapSnapshotModel.HeapSnapshotModel.DOMLinkState.ATTACHED) {
2711
3096
  continue;
2712
3097
  }
2713
- propagateState(this, nodeOrdinal, DOMLinkState.DETACHED);
3098
+ propagateState(this, nodeOrdinal, HeapSnapshotModel.HeapSnapshotModel.DOMLinkState.DETACHED);
2714
3099
  }
2715
3100
 
2716
3101
  console.timeEnd('propagateDOMState');