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
@@ -0,0 +1,142 @@
1
+ // Copyright 2026 The Chromium Authors
2
+ // Use of this source code is governed by a BSD-style license that can be
3
+ // found in the LICENSE file.
4
+
5
+ import * as Host from '../../../core/host/host.js';
6
+ import * as i18n from '../../../core/i18n/i18n.js';
7
+ import * as SDK from '../../../core/sdk/sdk.js';
8
+ import type * as Protocol from '../../../generated/protocol.js';
9
+ import type {FunctionCallHandlerResult} from '../agents/AiAgent.js';
10
+ import {DOMNodeContext} from '../contexts/DOMNodeContext.js';
11
+
12
+ import {
13
+ type BaseToolCapability,
14
+ type OriginLockCapability,
15
+ type TargetCapability,
16
+ type Tool,
17
+ type ToolArgs,
18
+ ToolName,
19
+ } from './Tool.js';
20
+
21
+ /**
22
+ * Arguments for getting accessibility details of an element.
23
+ */
24
+ export interface GetElementAccessibilityDetailsArgs extends ToolArgs {
25
+ /**
26
+ * The backend node ID of the element to inspect.
27
+ */
28
+ element: number;
29
+ explanation: string;
30
+ }
31
+
32
+ /**
33
+ * A tool that retrieves fine-grained accessibility properties (role, name, ARIA properties, focus state)
34
+ * for a resolved element backend node ID. It also returns a DOM snapshot of the element's subtree.
35
+ */
36
+ export class GetElementAccessibilityDetailsTool implements
37
+ Tool<GetElementAccessibilityDetailsArgs, string, BaseToolCapability&TargetCapability&OriginLockCapability> {
38
+ readonly name = ToolName.GET_ELEMENT_ACCESSIBILITY_DETAILS;
39
+ readonly description =
40
+ 'Get detailed accessibility information for an element on the inspected page by its backend node ID.';
41
+
42
+ readonly parameters: Host.AidaClient.FunctionObjectParam<keyof GetElementAccessibilityDetailsArgs> = {
43
+ type: Host.AidaClient.ParametersTypes.OBJECT,
44
+ description: 'Arguments for getting element accessibility details.',
45
+ nullable: false,
46
+ properties: {
47
+ explanation: {
48
+ type: Host.AidaClient.ParametersTypes.STRING,
49
+ description: 'Reason for requesting accessibility details.',
50
+ nullable: false,
51
+ },
52
+ element: {
53
+ type: Host.AidaClient.ParametersTypes.INTEGER,
54
+ description: 'The backend node ID of the element.',
55
+ nullable: false,
56
+ },
57
+ },
58
+ required: ['explanation', 'element'],
59
+ };
60
+
61
+ displayInfoFromArgs(params: GetElementAccessibilityDetailsArgs): {
62
+ title: string,
63
+ thought: string,
64
+ action: string,
65
+ } {
66
+ return {
67
+ title: 'Reading accessibility details',
68
+ thought: params.explanation,
69
+ action: `getElementAccessibilityDetails(${params.element})`,
70
+ };
71
+ }
72
+
73
+ /**
74
+ * Handles the request to retrieve accessibility details.
75
+ *
76
+ * Resolves the element backend node ID, validates its origin against the locked origin,
77
+ * requests the AX subtree via AccessibilityModel, and maps the relevant attributes.
78
+ */
79
+ async handler(
80
+ params: GetElementAccessibilityDetailsArgs,
81
+ context: BaseToolCapability&TargetCapability&OriginLockCapability,
82
+ ): Promise<FunctionCallHandlerResult<string>> {
83
+ const establishedOrigin = context.getEstablishedOrigin();
84
+ if (!establishedOrigin) {
85
+ return {error: 'Error: Origin lock is not established.'};
86
+ }
87
+
88
+ const target = context.getTarget();
89
+ if (!target) {
90
+ return {error: 'Error: Inspected target not found.'};
91
+ }
92
+
93
+ // Resolve the backend node ID to an SDK DOMNode. We use DeferredDOMNode
94
+ // because the backend node ID is the stable identifier received from external
95
+ // sources (like Lighthouse audits), and we need to fetch the local node object to query it.
96
+ const deferredNode = new SDK.DOMModel.DeferredDOMNode(target, params.element as Protocol.DOM.BackendNodeId);
97
+ const resolved = await deferredNode.resolvePromise();
98
+ if (!resolved) {
99
+ return {error: 'Error: Could not resolve element by ID.'};
100
+ }
101
+
102
+ const nodeContext = new DOMNodeContext(resolved);
103
+ // Security check: Ensure the element matches the active conversation's origin lock.
104
+ if (!nodeContext.isOriginAllowed(establishedOrigin)) {
105
+ return {error: 'Error: Node does not belong to the locked origin.'};
106
+ }
107
+
108
+ const axModel = target.model(SDK.AccessibilityModel.AccessibilityModel);
109
+ if (!axModel) {
110
+ return {error: 'Error: Accessibility model not found.'};
111
+ }
112
+
113
+ // Accessibility data is not loaded by default for all nodes to optimize performance.
114
+ // We must explicitly request and load the accessibility subtree for the resolved node before querying.
115
+ await axModel.requestAndLoadSubTreeToNode(resolved);
116
+ const axNode = axModel.axNodeForDOMNode(resolved);
117
+ if (!axNode) {
118
+ return {error: 'Error: AX node details not found.'};
119
+ }
120
+
121
+ const properties = {
122
+ role: axNode.role()?.value,
123
+ name: axNode.name()?.value,
124
+ properties: axNode.properties()?.map(p => ({name: p.name, value: p.value?.value})) ?? [],
125
+ };
126
+
127
+ // Take a snapshot of the resolved node's DOM structure. This is required
128
+ // by the DOM_TREE UI widget to render the element's local tree in the AI response panel.
129
+ const snapshot = await resolved.takeSnapshot();
130
+ return {
131
+ result: JSON.stringify(properties, null, 2),
132
+ widgets: [{
133
+ name: 'DOM_TREE',
134
+ data: {
135
+ root: snapshot,
136
+ title: i18n.i18n.lockedString('Element details'),
137
+ accessibleRevealLabel: i18n.i18n.lockedString('Reveal element'),
138
+ },
139
+ }],
140
+ };
141
+ }
142
+ }
@@ -105,7 +105,7 @@ export class GetStylesTool implements
105
105
  return {error: 'Error: Could not find the element with uid=' + uid};
106
106
  }
107
107
  const newContext = new DOMNodeContext(resolved);
108
- if (establishedOrigin !== newContext.getOrigin()) {
108
+ if (!newContext.isOriginAllowed(establishedOrigin)) {
109
109
  return {error: 'Error: Node does not belong to the current origin.'};
110
110
  }
111
111
  const styles = await resolved.domModel().cssModel().getComputedStyle(resolved.id);
@@ -17,12 +17,30 @@ Instead of passing a monolithic "grab-bag" context object to all tool handlers,
17
17
  - `PageExecutionCapability`: For tools executing JavaScript code on the inspected page.
18
18
  - `StyleMutationCapability`: For tools managing and applying style mutations via a `ChangeManager`.
19
19
  - `TargetCapability`: For tools requiring access to the page's current SDK `Target`.
20
- - `OriginLockCapability`: For tools enforcing cross-origin security via origin locks. Tools that fetch resources or state from the inspected page (such as styling or source code) must use this capability to verify that the target resource matches the conversation's established origin. This prevents the LLM from executing tools against out-of-origin elements or documents.
20
+ - `OriginLockCapability`: For tools enforcing cross-origin security via origin locks. Tools that fetch resources or state from the inspected page (such as styling or source code) must use this capability to verify that the target resource matches the conversation's established origin. This prevents the LLM from executing tools against out-of-origin elements or documents. Verification should be done by wrapping resolved resources in helper context classes (e.g., `DOMNodeContext`) and calling `isOriginAllowed(establishedOrigin)`.
21
21
 
22
22
  ### Unified Context
23
23
 
24
24
  The Agent (e.g., `AiAgent2`) builds the complete dependency union (`AllToolsCapabilities`) and fulfills all capabilities. When the handler is invoked, TypeScript validates that the provided context matches the intersection of capabilities requested by that tool.
25
25
 
26
+ ## Returning UI Widgets
27
+
28
+ Tools can return rich UI components (widgets) to be rendered directly in the AI Assistance panel alongside text results. To do this, include the widget payload in the `widgets` field of the returned `FunctionCallHandlerResult`:
29
+
30
+ ```typescript
31
+ return {
32
+ result: JSON.stringify(properties, null, 2),
33
+ widgets: [{
34
+ name: 'DOM_TREE',
35
+ data: {
36
+ root: snapshot,
37
+ title: i18n.i18n.lockedString('Element details'),
38
+ accessibleRevealLabel: i18n.i18n.lockedString('Reveal element'),
39
+ },
40
+ }],
41
+ };
42
+ ```
43
+
26
44
  ## Authoring a New Tool
27
45
 
28
46
  To author a new tool:
@@ -35,6 +53,8 @@ To author a new tool:
35
53
  See concrete, production examples of capability-based tools in this directory:
36
54
  - [ExecuteJavaScript.ts](ExecuteJavaScript.ts): Demonstrates use of `PageExecutionCapability` and `StyleMutationCapability`.
37
55
  - [GetStyles.ts](GetStyles.ts): Demonstrates use of `TargetCapability` and `OriginLockCapability`.
56
+ - [GetElementAccessibilityDetails.ts](GetElementAccessibilityDetails.ts): Demonstrates returning UI widgets (`DOM_TREE`) and origin checks with context wrappers.
57
+ - [ResolveDevtoolsNodePath.ts](ResolveDevtoolsNodePath.ts): Demonstrates path resolution input validation and origin lock checks.
38
58
 
39
59
  ## ToolRegistry Lookup
40
60
 
@@ -17,11 +17,11 @@ import {
17
17
  } from './Tool.js';
18
18
 
19
19
  /**
20
- * Arguments for resolving a Lighthouse path to a backend node ID.
20
+ * Arguments for resolving a DevTools node path to a backend node ID.
21
21
  */
22
- export interface ResolveLighthousePathArgs extends ToolArgs {
22
+ export interface ResolveDevtoolsNodePathArgs extends ToolArgs {
23
23
  /**
24
- * A Lighthouse-style element path.
24
+ * A DevTools node path.
25
25
  * This is typically a comma-separated list of child indices and tag names
26
26
  * representing the path from the root to the target element (e.g., "1,HTML,1,BODY").
27
27
  */
@@ -30,19 +30,20 @@ export interface ResolveLighthousePathArgs extends ToolArgs {
30
30
  }
31
31
 
32
32
  /**
33
- * A tool that resolves a Lighthouse-style element path to a backend node ID.
33
+ * A tool that resolves a DevTools node path to a backend node ID.
34
34
  *
35
35
  * This is used by the AI assistant to identify specific DOM nodes referred to in
36
- * Lighthouse reports. It ensures the resolved node belongs to the locked origin.
36
+ * Lighthouse reports or other sources using node paths. It ensures the resolved node
37
+ * belongs to the locked origin.
37
38
  */
38
- export class ResolveLighthousePathTool implements
39
- Tool<ResolveLighthousePathArgs, {backendNodeId: number}, BaseToolCapability&TargetCapability&OriginLockCapability> {
40
- readonly name = ToolName.RESOLVE_LIGHTHOUSE_PATH;
41
- readonly description = 'Resolves a Lighthouse path to a backend node ID.';
39
+ export class ResolveDevtoolsNodePathTool implements Tool<ResolveDevtoolsNodePathArgs, {backendNodeId: number},
40
+ BaseToolCapability&TargetCapability&OriginLockCapability> {
41
+ readonly name = ToolName.RESOLVE_DEVTOOLS_NODE_PATH;
42
+ readonly description = 'Resolves a DevTools node path to a backend node ID.';
42
43
 
43
- readonly parameters: Host.AidaClient.FunctionObjectParam<keyof ResolveLighthousePathArgs> = {
44
+ readonly parameters: Host.AidaClient.FunctionObjectParam<keyof ResolveDevtoolsNodePathArgs> = {
44
45
  type: Host.AidaClient.ParametersTypes.OBJECT,
45
- description: 'Arguments for resolving a Lighthouse path to a backend node ID.',
46
+ description: 'Arguments for resolving a DevTools node path to a backend node ID.',
46
47
  nullable: false,
47
48
  properties: {
48
49
  explanation: {
@@ -52,14 +53,14 @@ export class ResolveLighthousePathTool implements
52
53
  },
53
54
  path: {
54
55
  type: Host.AidaClient.ParametersTypes.STRING,
55
- description: 'Lighthouse path string.',
56
+ description: 'DevTools node path string.',
56
57
  nullable: false,
57
58
  },
58
59
  },
59
60
  required: ['explanation', 'path'],
60
61
  };
61
62
 
62
- displayInfoFromArgs(params: ResolveLighthousePathArgs): {
63
+ displayInfoFromArgs(params: ResolveDevtoolsNodePathArgs): {
63
64
  title: string,
64
65
  thought: string,
65
66
  action: string,
@@ -67,7 +68,7 @@ export class ResolveLighthousePathTool implements
67
68
  return {
68
69
  title: 'Resolving element path',
69
70
  thought: params.explanation,
70
- action: `resolveLighthousePath('${params.path}')`,
71
+ action: `resolveDevtoolsNodePath('${params.path}')`,
71
72
  };
72
73
  }
73
74
 
@@ -79,7 +80,7 @@ export class ResolveLighthousePathTool implements
79
80
  * access to nodes from other origins.
80
81
  */
81
82
  async handler(
82
- params: ResolveLighthousePathArgs,
83
+ params: ResolveDevtoolsNodePathArgs,
83
84
  context: BaseToolCapability&TargetCapability&OriginLockCapability,
84
85
  ): Promise<FunctionCallHandlerResult<{backendNodeId: number}>> {
85
86
  const establishedOrigin = context.getEstablishedOrigin();
@@ -95,11 +96,11 @@ export class ResolveLighthousePathTool implements
95
96
 
96
97
  let nodeId;
97
98
  try {
98
- // Resolves the Lighthouse path (a representation of the path to a node)
99
+ // Resolves the DevTools node path (a representation of the path to a node)
99
100
  // and ensures the node is loaded into the frontend DOM model, returning its ID.
100
101
  nodeId = await domModel.pushNodeByPathToFrontend(params.path);
101
102
  } catch {
102
- return {error: 'Error: Could not find node by path.'};
103
+ // pushNodeByPathToFrontend can fail or return undefined
103
104
  }
104
105
  if (!nodeId) {
105
106
  return {error: 'Error: Could not find node by path.'};
@@ -98,7 +98,8 @@ export const enum ToolName {
98
98
  LIST_NETWORK_REQUESTS = 'listNetworkRequests',
99
99
  GET_NETWORK_REQUEST_DETAILS = 'getNetworkRequestDetails',
100
100
  GET_LIGHTHOUSE_AUDITS = 'getLighthouseAudits',
101
- RESOLVE_LIGHTHOUSE_PATH = 'resolveLighthousePath',
101
+ RESOLVE_DEVTOOLS_NODE_PATH = 'resolveDevtoolsNodePath',
102
+ GET_ELEMENT_ACCESSIBILITY_DETAILS = 'getElementAccessibilityDetails',
102
103
  }
103
104
 
104
105
  /**
@@ -125,6 +126,10 @@ export interface BaseTool {
125
126
  export interface Tool<Args extends ToolArgs = ToolArgs, ReturnType = unknown,
126
127
  ContextType extends BaseToolCapability = BaseToolCapability> extends BaseTool {
127
128
  readonly parameters: Host.AidaClient.FunctionObjectParam<keyof Args>;
129
+ /**
130
+ * Converts the tool arguments into user-friendly display information.
131
+ * This is used by the UI to show what the agent is doing (e.g., in the history/steps log).
132
+ */
128
133
  readonly displayInfoFromArgs?: (
129
134
  args: Args,
130
135
  ) => {
@@ -3,11 +3,12 @@
3
3
  // found in the LICENSE file.
4
4
 
5
5
  import {ExecuteJavaScriptTool} from './ExecuteJavaScript.js';
6
+ import {GetElementAccessibilityDetailsTool} from './GetElementAccessibilityDetails.js';
6
7
  import {GetLighthouseAuditsTool} from './GetLighthouseAudits.js';
7
8
  import {GetNetworkRequestDetailsTool} from './GetNetworkRequestDetails.js';
8
9
  import {GetStylesTool} from './GetStyles.js';
9
10
  import {ListNetworkRequestsTool} from './ListNetworkRequests.js';
10
- import {ResolveLighthousePathTool} from './ResolveLighthousePath.js';
11
+ import {ResolveDevtoolsNodePathTool} from './ResolveDevtoolsNodePath.js';
11
12
  import {type AllToolsCapabilities, type Tool, type ToolArgs, ToolName} from './Tool.js';
12
13
 
13
14
  /**
@@ -24,7 +25,8 @@ export const TOOLS = {
24
25
  [ToolName.LIST_NETWORK_REQUESTS]: new ListNetworkRequestsTool(),
25
26
  [ToolName.GET_NETWORK_REQUEST_DETAILS]: new GetNetworkRequestDetailsTool(),
26
27
  [ToolName.GET_LIGHTHOUSE_AUDITS]: new GetLighthouseAuditsTool(),
27
- [ToolName.RESOLVE_LIGHTHOUSE_PATH]: new ResolveLighthousePathTool(),
28
+ [ToolName.RESOLVE_DEVTOOLS_NODE_PATH]: new ResolveDevtoolsNodePathTool(),
29
+ [ToolName.GET_ELEMENT_ACCESSIBILITY_DETAILS]: new GetElementAccessibilityDetailsTool(),
28
30
  };
29
31
 
30
32
  /**
@@ -7,27 +7,18 @@ import * as Platform from '../../core/platform/platform.js';
7
7
  import * as SDK from '../../core/sdk/sdk.js';
8
8
  import type * as Protocol from '../../generated/protocol.js';
9
9
 
10
- let autofillManagerInstance: AutofillManager;
11
-
12
10
  export class AutofillManager extends Common.ObjectWrapper.ObjectWrapper<EventTypes> {
13
11
  #address = '';
14
12
  #filledFields: Protocol.Autofill.FilledField[] = [];
15
13
  #matches: Match[] = [];
16
14
  #autofillModel: SDK.AutofillModel.AutofillModel|null = null;
15
+ #frameManager: SDK.FrameManager.FrameManager;
17
16
 
18
- private constructor() {
17
+ constructor(targetManager: SDK.TargetManager.TargetManager, frameManager: SDK.FrameManager.FrameManager) {
19
18
  super();
20
- SDK.TargetManager.TargetManager.instance().addModelListener(
21
- SDK.AutofillModel.AutofillModel, SDK.AutofillModel.Events.ADDRESS_FORM_FILLED, this.#addressFormFilled, this,
22
- {scoped: true});
23
- }
24
-
25
- static instance(opts: {forceNew: boolean|null} = {forceNew: null}): AutofillManager {
26
- const {forceNew} = opts;
27
- if (!autofillManagerInstance || forceNew) {
28
- autofillManagerInstance = new AutofillManager();
29
- }
30
- return autofillManagerInstance;
19
+ this.#frameManager = frameManager;
20
+ targetManager.addModelListener(SDK.AutofillModel.AutofillModel, SDK.AutofillModel.Events.ADDRESS_FORM_FILLED,
21
+ this.#addressFormFilled, this, {scoped: true});
31
22
  }
32
23
 
33
24
  async #addressFormFilled({data}: Common.EventTarget.EventTargetEvent<
@@ -56,7 +47,7 @@ export class AutofillManager extends Common.ObjectWrapper.ObjectWrapper<EventTyp
56
47
 
57
48
  highlightFilledField(filledField: Protocol.Autofill.FilledField): void {
58
49
  const backendNodeId = filledField.fieldId;
59
- const target = SDK.FrameManager.FrameManager.instance().getFrame(filledField.frameId)?.resourceTreeModel().target();
50
+ const target = this.#frameManager.getFrame(filledField.frameId)?.resourceTreeModel().target();
60
51
  if (target) {
61
52
  const deferredNode = new SDK.DOMModel.DeferredDOMNode(target, backendNodeId);
62
53
  const domModel = target.model(SDK.DOMModel.DOMModel);
@@ -3,6 +3,7 @@
3
3
  // found in the LICENSE file.
4
4
 
5
5
  import * as Common from '../../core/common/common.js';
6
+ import * as Root from '../../core/root/root.js';
6
7
  import * as SDK from '../../core/sdk/sdk.js';
7
8
  import type * as Protocol from '../../generated/protocol.js';
8
9
  import type * as Workspace from '../workspace/workspace.js';
@@ -13,21 +14,16 @@ const uiSourceCodeToAttributionMap = new WeakMap<Workspace.UISourceCode.UISource
13
14
  }>>();
14
15
  const projectToTargetMap = new WeakMap<Workspace.Workspace.Project, SDK.Target.Target>();
15
16
 
16
- let networkProjectManagerInstance: NetworkProjectManager;
17
-
18
17
  export class NetworkProjectManager extends Common.ObjectWrapper.ObjectWrapper<EventTypes> {
19
- private constructor() {
20
- super();
21
- }
22
18
 
23
19
  static instance({forceNew}: {
24
20
  forceNew: boolean,
25
21
  } = {forceNew: false}): NetworkProjectManager {
26
- if (!networkProjectManagerInstance || forceNew) {
27
- networkProjectManagerInstance = new NetworkProjectManager();
22
+ if (!Root.DevToolsContext.globalInstance().has(NetworkProjectManager) || forceNew) {
23
+ Root.DevToolsContext.globalInstance().set(NetworkProjectManager, new NetworkProjectManager());
28
24
  }
29
25
 
30
- return networkProjectManagerInstance;
26
+ return Root.DevToolsContext.globalInstance().get(NetworkProjectManager);
31
27
  }
32
28
  }
33
29
 
@@ -14,7 +14,6 @@ import * as SourceMapScopes from '../source_map_scopes/source_map_scopes.js';
14
14
  import type * as TextUtils from '../text_utils/text_utils.js';
15
15
  import * as Workspace from '../workspace/workspace.js';
16
16
 
17
- let breakpointManagerInstance: BreakpointManager;
18
17
  const INITIAL_RESTORE_BREAKPOINT_COUNT = 100;
19
18
 
20
19
  export class BreakpointManager extends Common.ObjectWrapper.ObjectWrapper<EventTypes> implements
@@ -35,10 +34,9 @@ export class BreakpointManager extends Common.ObjectWrapper.ObjectWrapper<EventT
35
34
  readonly #breakpointByStorageId = new Map<string, Breakpoint>();
36
35
  #updateBindingsCallbacks: Array<(uiSourceCode: Workspace.UISourceCode.UISourceCode) => Promise<void>> = [];
37
36
 
38
- private constructor(
39
- targetManager: SDK.TargetManager.TargetManager, workspace: Workspace.Workspace.WorkspaceImpl,
40
- debuggerWorkspaceBinding: Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding,
41
- settings: Common.Settings.Settings, restoreInitialBreakpointCount?: number) {
37
+ constructor(targetManager: SDK.TargetManager.TargetManager, workspace: Workspace.Workspace.WorkspaceImpl,
38
+ debuggerWorkspaceBinding: Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding,
39
+ settings: Common.Settings.Settings, restoreInitialBreakpointCount?: number) {
42
40
  super();
43
41
  this.#workspace = workspace;
44
42
  this.targetManager = targetManager;
@@ -85,18 +83,20 @@ export class BreakpointManager extends Common.ObjectWrapper.ObjectWrapper<EventT
85
83
  }): BreakpointManager {
86
84
  const {forceNew, targetManager, workspace, debuggerWorkspaceBinding, settings, restoreInitialBreakpointCount} =
87
85
  opts;
88
- if (!breakpointManagerInstance || forceNew) {
86
+ if (!Root.DevToolsContext.globalInstance().has(BreakpointManager) || forceNew) {
89
87
  if (!targetManager || !workspace || !debuggerWorkspaceBinding || !settings) {
90
88
  throw new Error(
91
89
  `Unable to create settings: targetManager, workspace, debuggerWorkspaceBinding, and settings must be provided: ${
92
90
  new Error().stack}`);
93
91
  }
94
92
 
95
- breakpointManagerInstance = new BreakpointManager(
96
- targetManager, workspace, debuggerWorkspaceBinding, settings, restoreInitialBreakpointCount);
93
+ Root.DevToolsContext.globalInstance().set(
94
+ BreakpointManager,
95
+ new BreakpointManager(targetManager, workspace, debuggerWorkspaceBinding, settings,
96
+ restoreInitialBreakpointCount));
97
97
  }
98
98
 
99
- return breakpointManagerInstance;
99
+ return Root.DevToolsContext.globalInstance().get(BreakpointManager);
100
100
  }
101
101
 
102
102
  modelAdded(debuggerModel: SDK.DebuggerModel.DebuggerModel): void {
@@ -200,6 +200,10 @@ export class DeviceModeModel extends Common.ObjectWrapper.ObjectWrapper<EventTyp
200
200
  }
201
201
  }
202
202
 
203
+ static removeInstance(): void {
204
+ deviceModeModelInstance = null;
205
+ }
206
+
203
207
  dispose(): void {
204
208
  SDK.TargetManager.TargetManager.instance().unobserveModels(SDK.EmulationModel.EmulationModel, this);
205
209
  }
@@ -573,6 +577,25 @@ export class DeviceModeModel extends Common.ObjectWrapper.ObjectWrapper<EventTyp
573
577
  return this.#mode.insets;
574
578
  }
575
579
 
580
+ private currentSafeAreaInsets(): Insets|null {
581
+ if (this.#type !== Type.Device || !this.#mode) {
582
+ return null;
583
+ }
584
+ return this.#mode.safeAreaInsets ?? null;
585
+ }
586
+
587
+ private applySafeAreaInsets(insets: Insets|null): void {
588
+ if (!this.#emulationModel) {
589
+ return;
590
+ }
591
+ if (insets) {
592
+ void this.#emulationModel.setSafeAreaInsets(
593
+ {top: insets.top, left: insets.left, bottom: insets.bottom, right: insets.right});
594
+ } else {
595
+ void this.#emulationModel.setSafeAreaInsets({});
596
+ }
597
+ }
598
+
576
599
  private getScreenOrientationType(): Protocol.Emulation.ScreenOrientationType {
577
600
  if (!this.#mode) {
578
601
  throw new Error('Mode required to get orientation type.');
@@ -649,6 +672,7 @@ export class DeviceModeModel extends Common.ObjectWrapper.ObjectWrapper<EventTyp
649
672
  if (overlayModel) {
650
673
  overlayModel.setShowViewportSizeOnResize(this.#type === Type.None);
651
674
  }
675
+ this.applySafeAreaInsets(this.currentSafeAreaInsets());
652
676
  this.dispatchEventToListeners(Events.UPDATED);
653
677
  }
654
678
 
@@ -103,6 +103,15 @@ export class EmulatedDevice {
103
103
  parseIntValue(json, 'bottom'));
104
104
  }
105
105
 
106
+ function parseCutoutShape(json: any): CutoutShape {
107
+ const shape = parseValue(json, 'shape', 'string');
108
+ if (shape !== CutoutShape.PILL && shape !== CutoutShape.NOTCH && shape !== CutoutShape.CIRCLE &&
109
+ shape !== CutoutShape.RECTANGLE) {
110
+ throw new Error('Emulated device mode has unsupported cutout shape: ' + shape);
111
+ }
112
+ return shape;
113
+ }
114
+
106
115
  function parseRGBA(json: any): SDK.OverlayModel.HighlightColor {
107
116
  const result = {} as SDK.OverlayModel.HighlightColor;
108
117
  result.r = parseIntValue(json, 'r');
@@ -257,6 +266,40 @@ export class EmulatedDevice {
257
266
  throw new Error('Emulated device mode \'' + mode.title + '\'has wrong mode insets');
258
267
  }
259
268
  mode.image = (parseValue(modes[i], 'image', 'string', null) as string);
269
+ const safeAreaInsets = parseValue(modes[i], 'safe-area-insets', 'object', null);
270
+ if (safeAreaInsets) {
271
+ mode.safeAreaInsets = parseInsets(safeAreaInsets);
272
+ }
273
+ const cutout = parseValue(modes[i], 'cutout', 'object', null);
274
+ if (cutout) {
275
+ const shape = parseCutoutShape(cutout);
276
+ const baseCutout = {
277
+ x: parseIntValue(cutout, 'x'),
278
+ y: parseIntValue(cutout, 'y'),
279
+ width: parseIntValue(cutout, 'width'),
280
+ height: parseIntValue(cutout, 'height'),
281
+ };
282
+ if (shape === CutoutShape.PILL) {
283
+ mode.cutout = {shape, ...baseCutout, borderRadius: parseIntValue(cutout, 'border-radius')};
284
+ } else if (shape === CutoutShape.NOTCH) {
285
+ mode.cutout = {
286
+ shape,
287
+ ...baseCutout,
288
+ upperRadius: parseIntValue(cutout, 'upper-radius'),
289
+ lowerRadius: parseIntValue(cutout, 'lower-radius'),
290
+ };
291
+ } else if (shape === CutoutShape.CIRCLE) {
292
+ mode.cutout = {
293
+ shape,
294
+ ...baseCutout,
295
+ cx: parseIntValue(cutout, 'cx'),
296
+ cy: parseIntValue(cutout, 'cy'),
297
+ radius: parseIntValue(cutout, 'radius'),
298
+ };
299
+ } else {
300
+ mode.cutout = {shape, ...baseCutout};
301
+ }
302
+ }
260
303
  result.modes.push(mode);
261
304
  }
262
305
  result.#showByDefault = (parseValue(json, 'show-by-default', 'boolean', undefined) as boolean);
@@ -359,6 +402,35 @@ export class EmulatedDevice {
359
402
  },
360
403
  image: this.modes[i].image || undefined,
361
404
  };
405
+ const safeAreaInsets = this.modes[i].safeAreaInsets;
406
+ if (safeAreaInsets) {
407
+ mode['safe-area-insets'] = {
408
+ left: safeAreaInsets.left,
409
+ top: safeAreaInsets.top,
410
+ right: safeAreaInsets.right,
411
+ bottom: safeAreaInsets.bottom,
412
+ };
413
+ }
414
+ const cutout = this.modes[i].cutout;
415
+ if (cutout) {
416
+ mode.cutout = {
417
+ shape: cutout.shape,
418
+ x: cutout.x,
419
+ y: cutout.y,
420
+ width: cutout.width,
421
+ height: cutout.height,
422
+ };
423
+ if (cutout.shape === CutoutShape.PILL) {
424
+ mode.cutout['border-radius'] = cutout.borderRadius;
425
+ } else if (cutout.shape === CutoutShape.NOTCH) {
426
+ mode.cutout['upper-radius'] = cutout.upperRadius;
427
+ mode.cutout['lower-radius'] = cutout.lowerRadius;
428
+ } else if (cutout.shape === CutoutShape.CIRCLE) {
429
+ mode.cutout.cx = cutout.cx;
430
+ mode.cutout.cy = cutout.cy;
431
+ mode.cutout.radius = cutout.radius;
432
+ }
433
+ }
362
434
  json['modes'].push(mode);
363
435
  }
364
436
 
@@ -637,7 +709,24 @@ export interface Mode {
637
709
  orientation: string;
638
710
  insets: Insets;
639
711
  image: string|null;
712
+ safeAreaInsets?: Insets;
713
+ cutout?: Cutout;
640
714
  }
715
+ export const enum CutoutShape {
716
+ PILL = 'pill',
717
+ NOTCH = 'notch',
718
+ CIRCLE = 'circle',
719
+ RECTANGLE = 'rectangle',
720
+ }
721
+ export interface BaseCutout {
722
+ x: number;
723
+ y: number;
724
+ width: number;
725
+ height: number;
726
+ }
727
+ export type Cutout = BaseCutout&({shape: CutoutShape.RECTANGLE}|{shape: CutoutShape.PILL, borderRadius: number}|
728
+ {shape: CutoutShape.NOTCH, upperRadius: number, lowerRadius: number}|
729
+ {shape: CutoutShape.CIRCLE, cx: number, cy: number, radius: number});
641
730
  export interface Orientation {
642
731
  width: number;
643
732
  height: number;
@@ -655,6 +744,25 @@ export interface JSONMode {
655
744
  top: number,
656
745
  bottom: number,
657
746
  };
747
+ 'safe-area-insets'?: {
748
+ left: number,
749
+ right: number,
750
+ top: number,
751
+ bottom: number,
752
+ };
753
+ cutout?: {
754
+ shape: string,
755
+ x: number,
756
+ y: number,
757
+ width: number,
758
+ height: number,
759
+ 'border-radius'?: number,
760
+ 'upper-radius'?: number,
761
+ 'lower-radius'?: number,
762
+ cx?: number,
763
+ cy?: number,
764
+ radius?: number,
765
+ };
658
766
  }
659
767
 
660
768
  // These props should quoted for the script to work properly