chrome-devtools-frontend 1.0.1684555 → 1.0.1689415

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 (602) hide show
  1. package/.agents/skills/gerrit-cli/SKILL.md +29 -8
  2. package/.agents/skills/gerrit-cli/references/commands.md +51 -18
  3. package/.agents/skills/gerrit-cli/references/workflows.md +6 -6
  4. package/.agents/skills/gerrit-cli/scripts/import_upstream.py +43 -70
  5. package/config/typescript/tsconfig.base.json +1 -0
  6. package/config/typescript/tsconfig.eslint.json +9 -0
  7. package/docs/contributing/issues.md +1 -1
  8. package/docs/styleguide/ux/writing.md +2 -2
  9. package/eslint.config.mjs +18 -10
  10. package/front_end/core/common/Base64.ts +1 -1
  11. package/front_end/core/common/Color.ts +28 -6
  12. package/front_end/core/common/ColorConverter.ts +12 -0
  13. package/front_end/core/common/Console.ts +2 -1
  14. package/front_end/core/common/Object.ts +2 -2
  15. package/front_end/core/common/ResourceType.ts +12 -13
  16. package/front_end/core/common/Revealer.ts +1 -1
  17. package/front_end/core/common/Settings.ts +2 -2
  18. package/front_end/core/common/TextDictionary.ts +2 -2
  19. package/front_end/core/common/Throttler.ts +1 -1
  20. package/front_end/core/host/InspectorFrontendHostAPI.ts +2 -2
  21. package/front_end/core/host/UserMetrics.ts +51 -37
  22. package/front_end/core/host/host.ts +1 -1
  23. package/front_end/core/i18n/i18nImpl.ts +1 -1
  24. package/front_end/core/platform/HostRuntime.ts +3 -3
  25. package/front_end/core/platform/KeyboardUtilities.ts +1 -1
  26. package/front_end/core/protocol_client/InspectorBackend.ts +7 -6
  27. package/front_end/core/root/Runtime.ts +4 -2
  28. package/front_end/core/sdk/AnimationModel.ts +1 -1
  29. package/front_end/core/sdk/CPUThrottlingManager.ts +198 -0
  30. package/front_end/core/sdk/CSSMetadata.ts +11 -6
  31. package/front_end/core/sdk/CSSPropertyParser.ts +5 -3
  32. package/front_end/core/sdk/CSSPropertyParserMatchers.ts +90 -59
  33. package/front_end/core/sdk/ConsoleModel.ts +1 -1
  34. package/front_end/core/sdk/DOMModel.ts +3 -3
  35. package/front_end/core/sdk/DebuggerModel.ts +1 -1
  36. package/front_end/core/sdk/EmulationModel.ts +5 -0
  37. package/front_end/core/sdk/LayerTreeBase.ts +1 -1
  38. package/front_end/core/sdk/NetworkManager.ts +1 -1
  39. package/front_end/core/sdk/README.md +40 -0
  40. package/front_end/core/sdk/RehydratingConnection.ts +1 -1
  41. package/front_end/core/sdk/RemoteObject.ts +1 -1
  42. package/front_end/core/sdk/ResourceTreeModel.ts +2 -2
  43. package/front_end/core/sdk/SDKModel.ts +1 -1
  44. package/front_end/core/sdk/SDKSettings.ts +6 -0
  45. package/front_end/core/sdk/Script.ts +2 -1
  46. package/front_end/core/sdk/SecurityOrigin.ts +48 -0
  47. package/front_end/core/sdk/ServiceWorkerManager.ts +75 -17
  48. package/front_end/core/sdk/sdk.ts +2 -0
  49. package/front_end/core/text_utils/ContentData.ts +1 -1
  50. package/front_end/entrypoints/formatter_worker/FormattedContentBuilder.ts +3 -1
  51. package/front_end/entrypoints/formatter_worker/FormatterWorker.ts +1 -1
  52. package/front_end/entrypoints/formatter_worker/ScopeParser.ts +2 -2
  53. package/front_end/entrypoints/formatter_worker/Substitute.ts +1 -1
  54. package/front_end/entrypoints/formatter_worker/formatter_worker-entrypoint.ts +1 -1
  55. package/front_end/entrypoints/inspector_main/InspectorMain.ts +1 -1
  56. package/front_end/entrypoints/inspector_main/OutermostTargetSelector.ts +1 -1
  57. package/front_end/entrypoints/inspector_main/nodeIcon.css +1 -1
  58. package/front_end/entrypoints/inspector_main/outermostTargetSelector.css +3 -3
  59. package/front_end/entrypoints/inspector_main/renderingOptions.css +3 -3
  60. package/front_end/entrypoints/js_app/js_app.ts +25 -9
  61. package/front_end/entrypoints/node_app/app/nodeConnectionsPanel.css +5 -5
  62. package/front_end/entrypoints/node_app/node_app.ts +1 -1
  63. package/front_end/generated/ARIAProperties.ts +39 -1
  64. package/front_end/generated/InspectorBackendCommands.ts +2 -1
  65. package/front_end/generated/SupportedCSSProperties.ts +60 -119
  66. package/front_end/generated/protocol-mapping.d.ts +9 -0
  67. package/front_end/generated/protocol-proxy-api.d.ts +7 -0
  68. package/front_end/generated/protocol.ts +12 -2
  69. package/front_end/models/ai_assistance/AiAgent2.ts +13 -7
  70. package/front_end/models/ai_assistance/AiConversation.ts +8 -17
  71. package/front_end/models/ai_assistance/AiHistoryStorage.ts +2 -2
  72. package/front_end/models/ai_assistance/AiUtils.ts +16 -0
  73. package/front_end/models/ai_assistance/EvaluateAction.ts +1 -1
  74. package/front_end/models/ai_assistance/README.md +1 -1
  75. package/front_end/models/ai_assistance/agents/AccessibilityAgent.ts +2 -3
  76. package/front_end/models/ai_assistance/agents/AiAgent.ts +29 -6
  77. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +4 -3
  78. package/front_end/models/ai_assistance/agents/FileAgent.ts +2 -2
  79. package/front_end/models/ai_assistance/agents/NetworkAgent.ts +2 -2
  80. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +1 -1
  81. package/front_end/models/ai_assistance/agents/StorageAgent.ts +7 -5
  82. package/front_end/models/ai_assistance/agents/StylingAgent.snapshot.txt +2 -2
  83. package/front_end/models/ai_assistance/agents/StylingAgent.ts +9 -7
  84. package/front_end/models/ai_assistance/ai_assistance.ts +8 -0
  85. package/front_end/models/ai_assistance/contexts/AccessibilityContext.ts +16 -1
  86. package/front_end/models/ai_assistance/contexts/FileContext.ts +6 -0
  87. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +150 -325
  88. package/front_end/models/ai_assistance/injected.ts +5 -3
  89. package/front_end/models/ai_assistance/performance/AIContext.ts +1 -1
  90. package/front_end/models/ai_assistance/skills/README.md +1 -1
  91. package/front_end/models/ai_assistance/skills/accessibility.md +19 -10
  92. package/front_end/models/ai_assistance/skills/network.md +13 -7
  93. package/front_end/models/ai_assistance/skills/performance.md +22 -16
  94. package/front_end/models/ai_assistance/skills/sources.md +13 -7
  95. package/front_end/models/ai_assistance/skills/storage.md +19 -18
  96. package/front_end/models/ai_assistance/skills/styling.md +30 -37
  97. package/front_end/models/ai_assistance/tools/CookieUtils.ts +120 -0
  98. package/front_end/models/ai_assistance/tools/DOMStorageUtils.ts +53 -4
  99. package/front_end/models/ai_assistance/tools/ExecuteJavaScript.ts +8 -32
  100. package/front_end/models/ai_assistance/tools/GetCookieValues.ts +146 -0
  101. package/front_end/models/ai_assistance/tools/GetDetailedCallTree.ts +11 -9
  102. package/front_end/models/ai_assistance/tools/GetElementAccessibilityDetails.ts +17 -5
  103. package/front_end/models/ai_assistance/tools/GetFunctionCode.ts +12 -11
  104. package/front_end/models/ai_assistance/tools/GetInsightDetails.ts +11 -11
  105. package/front_end/models/ai_assistance/tools/GetLighthouseAudits.ts +3 -2
  106. package/front_end/models/ai_assistance/tools/GetNetworkRequestDetails.ts +3 -3
  107. package/front_end/models/ai_assistance/tools/GetResourceContent.ts +12 -12
  108. package/front_end/models/ai_assistance/tools/GetSourceContent.ts +3 -2
  109. package/front_end/models/ai_assistance/tools/GetStorageBreakdown.ts +132 -0
  110. package/front_end/models/ai_assistance/tools/GetStorageValues.ts +17 -4
  111. package/front_end/models/ai_assistance/tools/GetStyles.ts +3 -3
  112. package/front_end/models/ai_assistance/tools/GetTraceEventByKey.ts +9 -8
  113. package/front_end/models/ai_assistance/tools/GetTraceMainThreadSummary.ts +13 -14
  114. package/front_end/models/ai_assistance/tools/GetTraceNetworkSummary.ts +12 -10
  115. package/front_end/models/ai_assistance/tools/ListCookies.ts +93 -0
  116. package/front_end/models/ai_assistance/tools/ListNetworkRequests.ts +15 -6
  117. package/front_end/models/ai_assistance/tools/ListPageOrigins.ts +3 -3
  118. package/front_end/models/ai_assistance/tools/ListSources.ts +5 -3
  119. package/front_end/models/ai_assistance/tools/ListStorageKeys.ts +16 -4
  120. package/front_end/models/ai_assistance/tools/RecordPerformanceTrace.ts +3 -2
  121. package/front_end/models/ai_assistance/tools/ResolveDevtoolsNodePath.ts +4 -3
  122. package/front_end/models/ai_assistance/tools/RunLighthouse.ts +9 -9
  123. package/front_end/models/ai_assistance/tools/SelectTraceEventByKey.ts +9 -8
  124. package/front_end/models/ai_assistance/tools/Tool.ts +21 -11
  125. package/front_end/models/ai_assistance/tools/ToolRegistry.ts +41 -8
  126. package/front_end/models/badges/AiExplorerBadge.ts +3 -3
  127. package/front_end/models/badges/CodeWhispererBadge.ts +2 -2
  128. package/front_end/models/badges/DOMDetectiveBadge.ts +3 -3
  129. package/front_end/models/badges/SpeedsterBadge.ts +3 -3
  130. package/front_end/models/badges/StarterBadge.ts +4 -4
  131. package/front_end/models/bindings/CompilerScriptMapping.ts +11 -2
  132. package/front_end/models/bindings/ContentProviderBasedProject.ts +3 -2
  133. package/front_end/models/breakpoints/BreakpointManager.ts +1 -1
  134. package/front_end/models/emulation/EmulatedDevices.ts +2 -2
  135. package/front_end/models/formatter/FormatterWorkerPool.ts +2 -2
  136. package/front_end/models/formatter/ScriptFormatter.ts +1 -2
  137. package/front_end/models/geometry/GeometryImpl.ts +2 -2
  138. package/front_end/models/heap_snapshot/HeapSnapshotModel.ts +1 -1
  139. package/front_end/models/heap_snapshot/HeapSnapshotProxy.ts +3 -2
  140. package/front_end/models/issues_manager/ContentSecurityPolicyIssue.ts +5 -5
  141. package/front_end/models/issues_manager/GenericIssue.ts +18 -18
  142. package/front_end/models/issues_manager/StylesheetLoadingIssue.ts +1 -1
  143. package/front_end/models/javascript_metadata/{NativeFunctions.js → NativeFunctions.ts} +25 -9
  144. package/front_end/models/live-metrics/LiveMetrics.ts +9 -3
  145. package/front_end/models/live-metrics/web-vitals-injected/spec/spec.ts +58 -6
  146. package/front_end/models/live-metrics/web-vitals-injected/web-vitals-injected.ts +2 -62
  147. package/front_end/models/network_time_calculator/RequestTimeRanges.ts +2 -2
  148. package/front_end/models/persistence/AutomaticFileSystemWorkspaceBinding.ts +5 -0
  149. package/front_end/models/persistence/IsolatedFileSystem.ts +1 -1
  150. package/front_end/models/persistence/NetworkPersistenceManager.ts +35 -6
  151. package/front_end/models/stack_trace/StackTraceImpl.ts +2 -2
  152. package/front_end/models/trace/LanternComputationData.ts +2 -3
  153. package/front_end/models/trace/extras/StackTraceForEvent.ts +3 -2
  154. package/front_end/models/trace/extras/ThirdParties.ts +9 -9
  155. package/front_end/models/trace/handlers/AsyncJSCallsHandler.ts +7 -4
  156. package/front_end/models/trace/handlers/FramesHandler.ts +3 -3
  157. package/front_end/models/trace/handlers/LayoutShiftsHandler.ts +2 -2
  158. package/front_end/models/trace/handlers/SamplesHandler.ts +4 -2
  159. package/front_end/models/trace/handlers/UserInteractionsHandler.ts +1 -1
  160. package/front_end/models/trace/handlers/WarningsHandler.ts +2 -2
  161. package/front_end/models/trace/helpers/Network.ts +3 -3
  162. package/front_end/models/trace/helpers/Trace.ts +1 -1
  163. package/front_end/models/trace/insights/CLSCulprits.ts +2 -1
  164. package/front_end/models/trace/insights/Cache.ts +3 -1
  165. package/front_end/models/trace/insights/CharacterSet.ts +3 -1
  166. package/front_end/models/trace/insights/DOMSize.ts +3 -1
  167. package/front_end/models/trace/insights/DocumentLatency.ts +3 -1
  168. package/front_end/models/trace/insights/DuplicatedJavaScript.ts +3 -1
  169. package/front_end/models/trace/insights/FontDisplay.ts +2 -1
  170. package/front_end/models/trace/insights/ForcedReflow.ts +2 -1
  171. package/front_end/models/trace/insights/INPBreakdown.ts +3 -1
  172. package/front_end/models/trace/insights/ImageDelivery.ts +3 -1
  173. package/front_end/models/trace/insights/LCPBreakdown.ts +3 -1
  174. package/front_end/models/trace/insights/LCPDiscovery.ts +3 -1
  175. package/front_end/models/trace/insights/LegacyJavaScript.ts +3 -1
  176. package/front_end/models/trace/insights/ModernHTTP.ts +2 -1
  177. package/front_end/models/trace/insights/NetworkDependencyTree.ts +2 -1
  178. package/front_end/models/trace/insights/RenderBlocking.ts +5 -4
  179. package/front_end/models/trace/insights/SlowCSSSelector.ts +3 -1
  180. package/front_end/models/trace/insights/ThirdParties.ts +5 -3
  181. package/front_end/models/trace/insights/Viewport.ts +2 -1
  182. package/front_end/models/trace/lantern/simulation/Constants.ts +34 -2
  183. package/front_end/models/trace/lantern/simulation/DNSCache.ts +1 -1
  184. package/front_end/models/trace/types/TraceEvents.ts +8 -3
  185. package/front_end/models/trace_source_maps_resolver/SourceMapsResolver.ts +2 -1
  186. package/front_end/models/workspace/WorkspaceImpl.ts +18 -2
  187. package/front_end/panels/accessibility/AccessibilityAnnouncementRecordingView.ts +686 -2
  188. package/front_end/panels/accessibility/accessibilityNode.css +4 -4
  189. package/front_end/panels/accessibility/accessibilityProperties.css +1 -1
  190. package/front_end/panels/accessibility/accessibilitySidebarView.css +2 -2
  191. package/front_end/panels/accessibility/axBreadcrumbs.css +13 -13
  192. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +14 -14
  193. package/front_end/panels/ai_assistance/ExternalHandler.ts +1 -1
  194. package/front_end/panels/ai_assistance/aiAssistancePanel.css +1 -1
  195. package/front_end/panels/ai_assistance/components/ChatInput.ts +3 -2
  196. package/front_end/panels/ai_assistance/components/ChatView.ts +1 -1
  197. package/front_end/panels/ai_assistance/components/DisabledWidget.ts +1 -1
  198. package/front_end/panels/ai_assistance/components/ExploreWidget.ts +1 -1
  199. package/front_end/panels/ai_assistance/components/chatInput.css +12 -12
  200. package/front_end/panels/ai_assistance/components/chatMessage.css +16 -16
  201. package/front_end/panels/ai_assistance/components/chatView.css +2 -2
  202. package/front_end/panels/ai_assistance/components/exploreWidget.css +1 -1
  203. package/front_end/panels/ai_assistance/components/exportForAgentsDialog.css +4 -0
  204. package/front_end/panels/ai_assistance/components/walkthroughView.css +6 -6
  205. package/front_end/panels/animation/animationTimeline.css +62 -62
  206. package/front_end/panels/application/ApplicationPanelSidebar.ts +6 -1
  207. package/front_end/panels/application/BackgroundServiceView.ts +290 -252
  208. package/front_end/panels/application/WebMCPView.ts +15 -7
  209. package/front_end/panels/application/appManifestView.css +5 -5
  210. package/front_end/panels/application/backgroundServiceView.css +18 -2
  211. package/front_end/panels/application/components/AdsView.ts +100 -4
  212. package/front_end/panels/application/components/adsView.css +8 -5
  213. package/front_end/panels/application/components/backForwardCacheView.css +5 -5
  214. package/front_end/panels/application/components/bounceTrackingMitigationsView.css +1 -1
  215. package/front_end/panels/application/components/endpointsGrid.css +1 -1
  216. package/front_end/panels/application/components/permissionsPolicySection.css +1 -1
  217. package/front_end/panels/application/components/protocolHandlersView.css +6 -6
  218. package/front_end/panels/application/components/reportsGrid.css +1 -1
  219. package/front_end/panels/application/components/serviceWorkerRouterView.css +4 -4
  220. package/front_end/panels/application/components/trustTokensView.css +7 -7
  221. package/front_end/panels/application/cookieItemsView.css +3 -3
  222. package/front_end/panels/application/deviceBoundSessionsView.css +3 -3
  223. package/front_end/panels/application/frameDetailsReportView.css +11 -11
  224. package/front_end/panels/application/indexedDBViews.css +7 -7
  225. package/front_end/panels/application/originTrialTokenRows.css +2 -2
  226. package/front_end/panels/application/originTrialTreeView.css +3 -3
  227. package/front_end/panels/application/preloading/components/RuleSetDetailsView.css +2 -2
  228. package/front_end/panels/application/preloading/components/preloadingDetailsReportView.css +1 -1
  229. package/front_end/panels/application/preloading/components/preloadingDisabledInfobar.css +2 -2
  230. package/front_end/panels/application/preloading/components/preloadingGrid.css +2 -2
  231. package/front_end/panels/application/preloading/components/usedPreloadingView.css +7 -7
  232. package/front_end/panels/application/preloading/preloadingView.css +1 -1
  233. package/front_end/panels/application/preloading/preloadingViewDropDown.css +3 -3
  234. package/front_end/panels/application/serviceWorkerCacheViews.css +5 -5
  235. package/front_end/panels/application/serviceWorkerUpdateCycleView.css +12 -12
  236. package/front_end/panels/application/serviceWorkersView.css +15 -16
  237. package/front_end/panels/application/storageView.css +5 -5
  238. package/front_end/panels/application/webMCPView.css +9 -9
  239. package/front_end/panels/autofill/autofillView.css +4 -4
  240. package/front_end/panels/browser_debugger/categorizedBreakpointsSidebarPane.css +4 -4
  241. package/front_end/panels/browser_debugger/domBreakpointsSidebarPane.css +5 -3
  242. package/front_end/panels/browser_debugger/xhrBreakpointsSidebarPane.css +4 -4
  243. package/front_end/panels/changes/changesSidebar.css +1 -1
  244. package/front_end/panels/changes/changesView.css +1 -1
  245. package/front_end/panels/changes/combinedDiffView.css +1 -1
  246. package/front_end/panels/common/CPUThrottlingOption.ts +5 -10
  247. package/front_end/panels/common/badgeNotification.css +2 -2
  248. package/front_end/panels/common/domLinkifier.css +1 -1
  249. package/front_end/panels/common/gdpSignUpDialog.css +3 -3
  250. package/front_end/panels/common/geminiRebrandPromoDialog.css +1 -1
  251. package/front_end/panels/console/ConsolePrompt.ts +6 -3
  252. package/front_end/panels/console/ConsoleView.ts +6 -3
  253. package/front_end/panels/console/ConsoleViewMessage.ts +3 -1
  254. package/front_end/panels/console/console-meta.ts +5 -24
  255. package/front_end/panels/console/consoleContextSelector.css +2 -2
  256. package/front_end/panels/console/consolePinPane.css +9 -9
  257. package/front_end/panels/console/consolePrompt.css +5 -5
  258. package/front_end/panels/console/consoleView.css +39 -39
  259. package/front_end/panels/coverage/coverageListView.css +2 -2
  260. package/front_end/panels/css_overview/cssOverviewCompletedView.css +34 -34
  261. package/front_end/panels/css_overview/cssOverviewProcessingView.css +5 -5
  262. package/front_end/panels/css_overview/cssOverviewSidebarPanel.css +1 -1
  263. package/front_end/panels/css_overview/cssOverviewStartView.css +17 -17
  264. package/front_end/panels/elements/AdoptedStyleSheetTreeElement.ts +209 -62
  265. package/front_end/panels/elements/ColorSwatchPopoverIcon.ts +4 -4
  266. package/front_end/panels/elements/DOMTreeContextMenu.ts +59 -54
  267. package/front_end/panels/elements/ElementsPanel.ts +1 -1
  268. package/front_end/panels/elements/ElementsTreeElement.ts +130 -94
  269. package/front_end/panels/elements/ElementsTreeOutline.ts +1751 -363
  270. package/front_end/panels/elements/ShortcutTreeElement.ts +6 -3
  271. package/front_end/panels/elements/StylePropertiesSection.ts +65 -8
  272. package/front_end/panels/elements/StylePropertyTreeElement.ts +6 -0
  273. package/front_end/panels/elements/StylesSidebarPane.ts +143 -8
  274. package/front_end/panels/elements/classesPaneWidget.css +6 -6
  275. package/front_end/panels/elements/components/stylePropertyEditor.css +7 -7
  276. package/front_end/panels/elements/computedStyleWidget.css +1 -1
  277. package/front_end/panels/elements/elementStatePaneWidget.css +8 -8
  278. package/front_end/panels/elements/elements-meta.ts +14 -0
  279. package/front_end/panels/elements/elementsPanel.css +4 -4
  280. package/front_end/panels/elements/elementsTreeOutline.css +222 -38
  281. package/front_end/panels/elements/layersWidget.css +4 -4
  282. package/front_end/panels/elements/layoutPane.css +18 -18
  283. package/front_end/panels/elements/metricsSidebarPane.css +11 -11
  284. package/front_end/panels/elements/platformFontsWidget.css +5 -5
  285. package/front_end/panels/elements/propertiesWidget.css +2 -2
  286. package/front_end/panels/elements/stylePropertiesTreeOutline.css +20 -20
  287. package/front_end/panels/elements/stylesSidebarPane.css +37 -20
  288. package/front_end/panels/emulation/deviceModeView.css +49 -49
  289. package/front_end/panels/emulation/mediaQueryInspector.css +18 -18
  290. package/front_end/panels/event_listeners/eventListenersView.css +1 -1
  291. package/front_end/panels/explain/components/consoleInsight.css +29 -29
  292. package/front_end/panels/issues/components/elementsPanelLink.css +2 -2
  293. package/front_end/panels/issues/components/hideIssuesMenu.css +1 -1
  294. package/front_end/panels/issues/issuesPane.css +2 -2
  295. package/front_end/panels/issues/issuesTree.css +50 -50
  296. package/front_end/panels/layer_viewer/Layers3DView.ts +9 -10
  297. package/front_end/panels/layer_viewer/layerDetailsView.css +1 -1
  298. package/front_end/panels/layer_viewer/layerTreeOutline.css +2 -2
  299. package/front_end/panels/layer_viewer/layers3DView.css +1 -1
  300. package/front_end/panels/layer_viewer/paintProfiler.css +1 -1
  301. package/front_end/panels/lighthouse/LighthouseReportSelector.ts +2 -2
  302. package/front_end/panels/media/playerListView.css +9 -9
  303. package/front_end/panels/media/playerMessagesView.css +8 -8
  304. package/front_end/panels/media/playerPropertiesView.css +6 -6
  305. package/front_end/panels/mobile_throttling/NetworkPanelIndicator.ts +1 -1
  306. package/front_end/panels/mobile_throttling/throttlingSettingsTab.css +9 -9
  307. package/front_end/panels/network/NetworkDataGridNode.ts +1 -0
  308. package/front_end/panels/network/NetworkPanel.ts +9 -4
  309. package/front_end/panels/network/RequestConditionsDrawer.ts +12 -12
  310. package/front_end/panels/network/RequestPayloadView.ts +78 -26
  311. package/front_end/panels/network/binaryResourceView.css +1 -1
  312. package/front_end/panels/network/components/EditableSpan.css +3 -3
  313. package/front_end/panels/network/components/HeaderSectionRow.css +14 -14
  314. package/front_end/panels/network/components/RequestHeaderSection.css +7 -7
  315. package/front_end/panels/network/components/RequestHeadersView.css +18 -18
  316. package/front_end/panels/network/components/RequestTrustTokensView.css +1 -1
  317. package/front_end/panels/network/network-meta.ts +9 -9
  318. package/front_end/panels/network/networkConfigView.css +11 -11
  319. package/front_end/panels/network/networkLogView.css +35 -35
  320. package/front_end/panels/network/networkManageCustomHeadersView.css +1 -1
  321. package/front_end/panels/network/networkPanel.css +7 -7
  322. package/front_end/panels/network/networkTimingTable.css +13 -13
  323. package/front_end/panels/network/requestConditionsDrawer.css +4 -4
  324. package/front_end/panels/network/requestCookiesView.css +5 -5
  325. package/front_end/panels/network/requestDeviceBoundSessionsView.css +1 -1
  326. package/front_end/panels/network/requestHTMLView.css +1 -1
  327. package/front_end/panels/network/requestHeadersTree.css +19 -19
  328. package/front_end/panels/network/requestInitiatorView.css +1 -1
  329. package/front_end/panels/network/requestInitiatorViewTree.css +3 -3
  330. package/front_end/panels/network/requestPayloadTree.css +5 -5
  331. package/front_end/panels/network/requestPayloadView.css +1 -1
  332. package/front_end/panels/network/resourceChunkView.css +3 -3
  333. package/front_end/panels/network/signedExchangeInfoTree.css +6 -6
  334. package/front_end/panels/profiler/heapProfiler.css +15 -15
  335. package/front_end/panels/profiler/profileLauncherView.css +10 -10
  336. package/front_end/panels/recorder/RecordingView.ts +1 -1
  337. package/front_end/panels/recorder/StepEditor.ts +86 -7
  338. package/front_end/panels/recorder/TimelineSection.ts +2 -1
  339. package/front_end/panels/recorder/createRecordingView.css +17 -17
  340. package/front_end/panels/recorder/extensionView.css +5 -5
  341. package/front_end/panels/recorder/injected/RecordingClient.ts +1 -1
  342. package/front_end/panels/recorder/models/SchemaUtils.ts +2 -2
  343. package/front_end/panels/recorder/models/ScreenshotStorage.ts +1 -1
  344. package/front_end/panels/recorder/recorderPanel.css +7 -7
  345. package/front_end/panels/recorder/recordingListView.css +12 -12
  346. package/front_end/panels/recorder/recordingView.css +47 -47
  347. package/front_end/panels/recorder/stepEditor.css +2 -2
  348. package/front_end/panels/recorder/stepView.css +20 -20
  349. package/front_end/panels/recorder/timelineSection.css +9 -9
  350. package/front_end/panels/screencast/screencastView.css +12 -12
  351. package/front_end/panels/search/searchResultsPane.css +10 -10
  352. package/front_end/panels/search/searchView.css +9 -9
  353. package/front_end/panels/security/SecurityPanel.ts +84 -54
  354. package/front_end/panels/security/mainView.css +26 -26
  355. package/front_end/panels/security/originView.css +15 -15
  356. package/front_end/panels/sensors/locationsSettingsTab.css +6 -6
  357. package/front_end/panels/sensors/sensors.css +28 -28
  358. package/front_end/panels/settings/components/syncSection.css +5 -5
  359. package/front_end/panels/settings/emulation/components/userAgentClientHintsForm.css +13 -13
  360. package/front_end/panels/settings/emulation/devicesSettingsTab.css +16 -16
  361. package/front_end/panels/settings/frameworkIgnoreListSettingsTab.css +2 -2
  362. package/front_end/panels/settings/keybindsSettingsTab.css +2 -2
  363. package/front_end/panels/settings/settingsScreen.css +5 -5
  364. package/front_end/panels/sources/UISourceCodeFrame.ts +1 -1
  365. package/front_end/panels/sources/breakpointEditDialog.css +9 -9
  366. package/front_end/panels/sources/breakpointsView.css +25 -25
  367. package/front_end/panels/sources/callStackSidebarPane.css +10 -10
  368. package/front_end/panels/sources/components/HeadersView.css +6 -6
  369. package/front_end/panels/sources/debuggerPausedMessage.css +4 -4
  370. package/front_end/panels/sources/dialog.css +2 -2
  371. package/front_end/panels/sources/navigatorTree.css +2 -2
  372. package/front_end/panels/sources/navigatorView.css +2 -2
  373. package/front_end/panels/sources/scopeChainSidebarPane.css +1 -1
  374. package/front_end/panels/sources/sourcesNavigator.css +1 -1
  375. package/front_end/panels/sources/sourcesPanel.css +2 -2
  376. package/front_end/panels/sources/sourcesView.css +1 -1
  377. package/front_end/panels/sources/threadsSidebarPane.css +5 -5
  378. package/front_end/panels/sources/watchExpressionsSidebarPane.css +8 -8
  379. package/front_end/panels/timeline/TimelineDetailsView.ts +9 -21
  380. package/front_end/panels/timeline/TimelineFlameChartView.ts +3 -5
  381. package/front_end/panels/timeline/TimelineMiniMap.ts +3 -3
  382. package/front_end/panels/timeline/components/LiveMetricsView.ts +1 -1
  383. package/front_end/panels/timeline/components/breadcrumbsUI.css +3 -3
  384. package/front_end/panels/timeline/components/exportTraceOptions.css +1 -1
  385. package/front_end/panels/timeline/components/fieldSettingsDialog.css +13 -13
  386. package/front_end/panels/timeline/components/ignoreListSetting.css +3 -3
  387. package/front_end/panels/timeline/components/liveMetricsView.css +39 -39
  388. package/front_end/panels/timeline/components/metricCard.css +20 -20
  389. package/front_end/panels/timeline/components/networkRequestDetails.css +9 -9
  390. package/front_end/panels/timeline/components/networkRequestTooltip.css +14 -14
  391. package/front_end/panels/timeline/components/networkTrackWidget.css +2 -2
  392. package/front_end/panels/timeline/components/originMap.css +4 -4
  393. package/front_end/panels/timeline/components/relatedInsightChips.css +1 -1
  394. package/front_end/panels/timeline/components/sidebarAnnotationsTab.css +4 -4
  395. package/front_end/panels/timeline/components/sidebarInsightsTab.css +5 -5
  396. package/front_end/panels/timeline/components/sidebarSingleInsightSet.css +1 -1
  397. package/front_end/panels/timeline/components/timelineSummary.css +4 -4
  398. package/front_end/panels/timeline/overlays/OverlaysImpl.ts +4 -4
  399. package/front_end/panels/timeline/overlays/components/EntriesLinkOverlay.ts +1 -1
  400. package/front_end/panels/timeline/overlays/components/EntryLabelOverlay.ts +6 -6
  401. package/front_end/panels/timeline/overlays/components/TimeRangeOverlay.ts +1 -1
  402. package/front_end/panels/timeline/thirdPartyTreeView.css +5 -5
  403. package/front_end/panels/timeline/timeline-meta.ts +0 -6
  404. package/front_end/panels/timeline/timelineDetailsView.css +11 -11
  405. package/front_end/panels/timeline/timelinePanel.css +20 -20
  406. package/front_end/panels/timeline/timelineTreeView.css +8 -8
  407. package/front_end/panels/webauthn/webauthnPane.css +6 -6
  408. package/front_end/panels/whats_new/ReleaseNoteText.ts +2 -2
  409. package/front_end/third_party/chromium/README.chromium +1 -1
  410. package/front_end/third_party/i18n/i18n-impl.ts +1 -1
  411. package/front_end/third_party/puppeteer/README.chromium +2 -2
  412. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.d.ts +15 -1
  413. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +42 -8
  414. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Frame.d.ts +4 -0
  415. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Frame.d.ts.map +1 -1
  416. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Frame.js +15 -2
  417. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Frame.js.map +1 -1
  418. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/WebMCP.d.ts +14 -1
  419. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/WebMCP.d.ts.map +1 -1
  420. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/WebMCP.js +23 -1
  421. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/WebMCP.js.map +1 -1
  422. package/front_end/third_party/puppeteer/package/lib/puppeteer/generated/injected.d.ts +1 -1
  423. package/front_end/third_party/puppeteer/package/lib/puppeteer/generated/injected.d.ts.map +1 -1
  424. package/front_end/third_party/puppeteer/package/lib/puppeteer/generated/injected.js +1 -1
  425. package/front_end/third_party/puppeteer/package/lib/puppeteer/generated/injected.js.map +1 -1
  426. package/front_end/third_party/puppeteer/package/lib/puppeteer/injected/Poller.d.ts.map +1 -1
  427. package/front_end/third_party/puppeteer/package/lib/puppeteer/injected/Poller.js +77 -6
  428. package/front_end/third_party/puppeteer/package/lib/puppeteer/injected/Poller.js.map +1 -1
  429. package/front_end/third_party/puppeteer/package/lib/puppeteer/revisions.d.ts +3 -3
  430. package/front_end/third_party/puppeteer/package/lib/puppeteer/revisions.js +3 -3
  431. package/front_end/third_party/puppeteer/package/lib/puppeteer/revisions.js.map +1 -1
  432. package/front_end/third_party/puppeteer/package/lib/puppeteer/util/version.d.ts +1 -1
  433. package/front_end/third_party/puppeteer/package/lib/puppeteer/util/version.js +1 -1
  434. package/front_end/third_party/puppeteer/package/lib/types.d.ts +15 -1
  435. package/front_end/third_party/puppeteer/package/package.json +2 -2
  436. package/front_end/third_party/puppeteer/package/src/cdp/Frame.ts +16 -2
  437. package/front_end/third_party/puppeteer/package/src/cdp/WebMCP.ts +36 -1
  438. package/front_end/third_party/puppeteer/package/src/generated/injected.ts +1 -1
  439. package/front_end/third_party/puppeteer/package/src/injected/Poller.ts +86 -6
  440. package/front_end/third_party/puppeteer/package/src/revisions.ts +3 -3
  441. package/front_end/third_party/puppeteer/package/src/util/version.ts +1 -1
  442. package/front_end/third_party/web-vitals/README.chromium +2 -2
  443. package/front_end/third_party/web-vitals/package/README.md +22 -7
  444. package/front_end/third_party/web-vitals/package/dist/modules/attribution/onINP.js +6 -2
  445. package/front_end/third_party/web-vitals/package/dist/modules/attribution/onLCP.d.ts +2 -2
  446. package/front_end/third_party/web-vitals/package/dist/modules/attribution/onLCP.js +30 -3
  447. package/front_end/third_party/web-vitals/package/dist/modules/lib/InteractionManager.d.ts +14 -0
  448. package/front_end/third_party/web-vitals/package/dist/modules/lib/InteractionManager.js +19 -13
  449. package/front_end/third_party/web-vitals/package/dist/modules/lib/bindReporter.d.ts +1 -1
  450. package/front_end/third_party/web-vitals/package/dist/modules/lib/doubleRAF.js +1 -1
  451. package/front_end/third_party/web-vitals/package/dist/modules/lib/initMetric.d.ts +3 -3
  452. package/front_end/third_party/web-vitals/package/dist/modules/lib/softNavs.js +3 -1
  453. package/front_end/third_party/web-vitals/package/dist/modules/types/lcp.d.ts +15 -1
  454. package/front_end/third_party/web-vitals/package/package.json +1 -2
  455. package/front_end/third_party/web-vitals/package/src/attribution/onINP.ts +9 -2
  456. package/front_end/third_party/web-vitals/package/src/attribution/onLCP.ts +36 -5
  457. package/front_end/third_party/web-vitals/package/src/lib/InteractionManager.ts +21 -15
  458. package/front_end/third_party/web-vitals/package/src/lib/doubleRAF.ts +1 -1
  459. package/front_end/third_party/web-vitals/package/src/lib/softNavs.ts +4 -1
  460. package/front_end/third_party/web-vitals/package/src/types/lcp.ts +16 -1
  461. package/front_end/third_party/web-vitals/rebuild.sh +3 -3
  462. package/front_end/ui/comments/CommentAnchorResolver.ts +15 -4
  463. package/front_end/ui/comments/commentsOverlay.css +6 -5
  464. package/front_end/ui/components/adorners/Adorner.ts +1 -1
  465. package/front_end/ui/components/adorners/adorner.css +6 -6
  466. package/front_end/ui/components/buttons/FloatingButton.ts +1 -1
  467. package/front_end/ui/components/buttons/button.css +9 -9
  468. package/front_end/ui/components/buttons/floatingButton.css +2 -2
  469. package/front_end/ui/components/buttons/textButton.css +12 -12
  470. package/front_end/ui/components/dialogs/Dialog.ts +1 -1
  471. package/front_end/ui/components/dialogs/dialog.css +1 -1
  472. package/front_end/ui/components/diff_view/diffView.css +3 -3
  473. package/front_end/ui/components/expandable_list/expandableList.css +9 -9
  474. package/front_end/ui/components/highlighting/HighlightElement.ts +1 -1
  475. package/front_end/ui/components/highlighting/HighlightManager.ts +4 -0
  476. package/front_end/ui/components/icon_button/iconButton.css +2 -2
  477. package/front_end/ui/components/input/checkbox.css +8 -8
  478. package/front_end/ui/components/input/textInput.css +3 -3
  479. package/front_end/ui/components/issue_counter/issueLinkIcon.css +2 -2
  480. package/front_end/ui/components/linkifier/linkifierImpl.css +1 -1
  481. package/front_end/ui/components/markdown_view/MarkdownImagesMap.ts +1 -1
  482. package/front_end/ui/components/markdown_view/MarkdownLinksMap.ts +1 -1
  483. package/front_end/ui/components/markdown_view/codeBlock.css +2 -2
  484. package/front_end/ui/components/markdown_view/markdownView.css +7 -7
  485. package/front_end/ui/components/menus/menuGroup.css +2 -2
  486. package/front_end/ui/components/menus/menuItem.css +2 -2
  487. package/front_end/ui/components/menus/selectMenu.css +2 -2
  488. package/front_end/ui/components/menus/selectMenuButton.css +4 -4
  489. package/front_end/ui/components/panel_feedback/panelFeedback.css +15 -15
  490. package/front_end/ui/components/panel_feedback/previewToggle.css +2 -2
  491. package/front_end/ui/components/panel_introduction_steps/panelIntroductionSteps.css +7 -7
  492. package/front_end/ui/components/report_view/report.css +3 -3
  493. package/front_end/ui/components/report_view/reportSectionDivider.css +1 -1
  494. package/front_end/ui/components/report_view/reportValue.css +1 -1
  495. package/front_end/ui/components/request_link_icon/requestLinkIcon.css +2 -2
  496. package/front_end/ui/components/settings/settingCheckbox.css +6 -6
  497. package/front_end/ui/components/text_editor/TextEditor.ts +3 -1
  498. package/front_end/ui/components/text_editor/config.ts +16 -4
  499. package/front_end/ui/kit/icons/icon.css +4 -4
  500. package/front_end/ui/kit/link/link.css +1 -1
  501. package/front_end/ui/legacy/ActionRegistration.ts +23 -23
  502. package/front_end/ui/legacy/Dialog.ts +1 -1
  503. package/front_end/ui/legacy/DockController.ts +3 -3
  504. package/front_end/ui/legacy/EmptyWidget.ts +13 -34
  505. package/front_end/ui/legacy/FilterBar.ts +4 -4
  506. package/front_end/ui/legacy/Infobar.ts +2 -2
  507. package/front_end/ui/legacy/InspectorDrawerView.ts +6 -6
  508. package/front_end/ui/legacy/InspectorView.ts +19 -26
  509. package/front_end/ui/legacy/ListWidget.ts +7 -7
  510. package/front_end/ui/legacy/PlusButton.ts +1 -3
  511. package/front_end/ui/legacy/RemoteDebuggingTerminatedScreen.ts +6 -6
  512. package/front_end/ui/legacy/SearchableView.ts +16 -16
  513. package/front_end/ui/legacy/SoftContextMenu.ts +8 -8
  514. package/front_end/ui/legacy/SoftDropDown.ts +1 -1
  515. package/front_end/ui/legacy/SuggestBox.ts +2 -2
  516. package/front_end/ui/legacy/TabbedPane.ts +10 -10
  517. package/front_end/ui/legacy/TargetCrashedScreen.ts +2 -2
  518. package/front_end/ui/legacy/Toolbar.ts +5 -5
  519. package/front_end/ui/legacy/Treeoutline.ts +1 -1
  520. package/front_end/ui/legacy/UIUtils.ts +80 -20
  521. package/front_end/ui/legacy/ViewManager.ts +1 -1
  522. package/front_end/ui/legacy/ViewRegistration.ts +7 -7
  523. package/front_end/ui/legacy/Widget.ts +5 -5
  524. package/front_end/ui/legacy/checkboxTextLabel.css +3 -3
  525. package/front_end/ui/legacy/components/color_picker/ColorFormatSpec.ts +16 -0
  526. package/front_end/ui/legacy/components/color_picker/FormatPickerContextMenu.ts +1 -0
  527. package/front_end/ui/legacy/components/color_picker/spectrum.css +90 -90
  528. package/front_end/ui/legacy/components/cookie_table/cookiesTable.css +1 -1
  529. package/front_end/ui/legacy/components/data_grid/DataGrid.ts +26 -28
  530. package/front_end/ui/legacy/components/data_grid/ShowMoreDataGridNode.ts +3 -7
  531. package/front_end/ui/legacy/components/inline_editor/bezierEditor.css +13 -13
  532. package/front_end/ui/legacy/components/inline_editor/colorSwatch.css +7 -7
  533. package/front_end/ui/legacy/components/inline_editor/cssAngleEditor.css +5 -5
  534. package/front_end/ui/legacy/components/inline_editor/cssShadowEditor.css +18 -18
  535. package/front_end/ui/legacy/components/object_ui/CustomPreviewComponent.ts +104 -86
  536. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +8 -6
  537. package/front_end/ui/legacy/components/object_ui/objectPopover.css +4 -4
  538. package/front_end/ui/legacy/components/object_ui/objectPropertiesSection.css +6 -6
  539. package/front_end/ui/legacy/components/perf_ui/ChartViewport.ts +28 -11
  540. package/front_end/ui/legacy/components/perf_ui/TimelineOverviewPane.ts +0 -4
  541. package/front_end/ui/legacy/components/perf_ui/chartViewport.css +4 -4
  542. package/front_end/ui/legacy/components/perf_ui/filmStripView.css +6 -6
  543. package/front_end/ui/legacy/components/perf_ui/flameChart.css +7 -7
  544. package/front_end/ui/legacy/components/perf_ui/overviewGrid.css +11 -11
  545. package/front_end/ui/legacy/components/perf_ui/pieChart.css +6 -6
  546. package/front_end/ui/legacy/components/perf_ui/timelineGrid.css +5 -5
  547. package/front_end/ui/legacy/components/quick_open/filteredListWidget.css +3 -3
  548. package/front_end/ui/legacy/components/source_frame/ResourceSourceFrame.ts +1 -1
  549. package/front_end/ui/legacy/components/source_frame/imageView.css +1 -1
  550. package/front_end/ui/legacy/components/source_frame/jsonView.css +1 -1
  551. package/front_end/ui/legacy/components/source_frame/resourceSourceFrame.css +1 -1
  552. package/front_end/ui/legacy/components/source_frame/xmlTree.css +3 -3
  553. package/front_end/ui/legacy/components/utils/imagePreview.css +1 -1
  554. package/front_end/ui/legacy/components/utils/jsUtils.css +10 -10
  555. package/front_end/ui/legacy/dropTarget.css +2 -2
  556. package/front_end/ui/legacy/emptyWidget.css +6 -0
  557. package/front_end/ui/legacy/filter.css +19 -19
  558. package/front_end/ui/legacy/infobar.css +2 -2
  559. package/front_end/ui/legacy/inspectorCommon.css +67 -67
  560. package/front_end/ui/legacy/inspectorDrawerTabbedPane.css +1 -1
  561. package/front_end/ui/legacy/listWidget.css +5 -5
  562. package/front_end/ui/legacy/progressIndicator.css +4 -4
  563. package/front_end/ui/legacy/reportView.css +7 -7
  564. package/front_end/ui/legacy/searchableView.css +5 -5
  565. package/front_end/ui/legacy/smallBubble.css +3 -3
  566. package/front_end/ui/legacy/softContextMenu.css +4 -4
  567. package/front_end/ui/legacy/softDropDownButton.css +1 -1
  568. package/front_end/ui/legacy/splitWidget.css +9 -31
  569. package/front_end/ui/legacy/suggestBox.css +4 -4
  570. package/front_end/ui/legacy/tabbedPane.css +17 -17
  571. package/front_end/ui/legacy/textPrompt.css +2 -2
  572. package/front_end/ui/legacy/toolbar.css +4 -4
  573. package/front_end/ui/legacy/treeoutline.css +14 -14
  574. package/front_end/ui/legacy/viewContainers.css +6 -6
  575. package/front_end/ui/settings/ConsoleSettings.ts +34 -0
  576. package/front_end/ui/visual_logging/KnownContextValues.ts +12 -0
  577. package/inspector_overlay/common.ts +12 -12
  578. package/inspector_overlay/css_grid_label_helpers.ts +16 -17
  579. package/inspector_overlay/drag_resize_handler.ts +2 -2
  580. package/inspector_overlay/highlight_common.ts +11 -13
  581. package/inspector_overlay/highlight_container_query.ts +2 -2
  582. package/inspector_overlay/highlight_flex_common.ts +6 -7
  583. package/inspector_overlay/highlight_grid_common.ts +3 -3
  584. package/inspector_overlay/highlight_isolated_element.ts +12 -3
  585. package/inspector_overlay/highlight_scroll_snap.ts +2 -2
  586. package/inspector_overlay/loadCSS.rollup.js +0 -1
  587. package/inspector_overlay/testing/InspectorOverlayHelpers.ts +13 -13
  588. package/inspector_overlay/tool_green_dev_anchors.ts +3 -3
  589. package/inspector_overlay/tool_highlight.ts +9 -9
  590. package/inspector_overlay/tool_paused.ts +4 -4
  591. package/inspector_overlay/tool_persistent.ts +11 -11
  592. package/inspector_overlay/tool_screenshot.ts +7 -7
  593. package/inspector_overlay/tool_source_order.ts +4 -4
  594. package/inspector_overlay/tool_viewport_size.ts +3 -3
  595. package/inspector_overlay/tool_window_controls.ts +4 -4
  596. package/mcp/mcp.ts +1 -0
  597. package/package.json +2 -2
  598. package/front_end/generated/protocol-tsconfig.json +0 -10
  599. package/front_end/panels/js_timeline/DIR_METADATA +0 -3
  600. package/front_end/panels/js_timeline/js_timeline-meta.ts +0 -193
  601. package/front_end/panels/js_timeline/js_timeline.ts +0 -1
  602. /package/front_end/entrypoints/{formatter_worker/FormatterActions.ts → formatter_actions/formatter_actions.ts} +0 -0
@@ -35,6 +35,7 @@
35
35
  */
36
36
 
37
37
  import * as Common from '../../core/common/common.js';
38
+ import * as Host from '../../core/host/host.js';
38
39
  import * as i18n from '../../core/i18n/i18n.js';
39
40
  import * as SDK from '../../core/sdk/sdk.js';
40
41
  import * as Badges from '../../models/badges/badges.js';
@@ -44,19 +45,35 @@ import * as Highlighting from '../../ui/components/highlighting/highlighting.js'
44
45
  import * as IssueCounter from '../../ui/components/issue_counter/issue_counter.js';
45
46
  import * as UIComponentUtils from '../../ui/legacy/components/utils/utils.js';
46
47
  import * as UI from '../../ui/legacy/legacy.js';
47
- import {html, nothing, render} from '../../ui/lit/lit.js';
48
+ import * as Lit from '../../ui/lit/lit.js';
48
49
  import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
49
50
 
50
- import {AdoptedStyleSheetSetTreeElement, AdoptedStyleSheetTreeElement} from './AdoptedStyleSheetTreeElement.js';
51
+ import {
52
+ AdoptedStyleSheetContentsWidget,
53
+ AdoptedStyleSheetSetTreeElement,
54
+ AdoptedStyleSheetTreeElement,
55
+ } from './AdoptedStyleSheetTreeElement.js';
56
+ import * as ElementsComponents from './components/components.js';
57
+ import {cssPath, jsPath, xPath} from './DOMPath.js';
51
58
  import {showContextMenu} from './DOMTreeContextMenu.js';
52
59
  import {getElementIssueDetails} from './ElementIssueUtils.js';
53
- import {ElementsTreeElement, InitialChildrenLimit, isOpeningTag} from './ElementsTreeElement.js';
60
+ import {
61
+ adornerRef,
62
+ ElementsTreeElement,
63
+ ElementsTreeWidget,
64
+ ForbiddenClosingTagElements,
65
+ handleAdornerKeydown,
66
+ InitialChildrenLimit,
67
+ type InitialEditState,
68
+ isOpeningTag,
69
+ } from './ElementsTreeElement.js';
54
70
  import elementsTreeOutlineStyles from './elementsTreeOutline.css.js';
55
71
  import {ImagePreviewPopover} from './ImagePreviewPopover.js';
56
- import type {MarkerDecoratorRegistration} from './MarkerDecorator.js';
57
72
  import {ShortcutTreeElement} from './ShortcutTreeElement.js';
58
73
  import {TopLayerContainer} from './TopLayerContainer.js';
59
74
 
75
+ const {html, nothing, render, Directives: {classMap}} = Lit;
76
+
60
77
  const UIStrings = {
61
78
  /**
62
79
  * @description ARIA accessible name in the DOM tree outline of the Elements panel.
@@ -76,6 +93,10 @@ const UIStrings = {
76
93
  * @description Text for popover that directs to the Issues panel.
77
94
  */
78
95
  viewIssue: 'View issue:',
96
+ /**
97
+ * @description Link text content in the DOM tree outline of the Elements panel.
98
+ */
99
+ reveal: 'reveal',
79
100
  } as const;
80
101
  const str_ = i18n.i18n.registerUIStrings('panels/elements/ElementsTreeOutline.ts', UIStrings);
81
102
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
@@ -83,10 +104,12 @@ const elementsTreeOutlineByDOMModel = new WeakMap<SDK.DOMModel.DOMModel, Element
83
104
 
84
105
  const populatedTreeElements = new WeakSet<ElementsTreeElement>();
85
106
 
86
- export type View = typeof DEFAULT_VIEW;
107
+ export type View = (input: ViewInput, output: ViewOutput, target: HTMLElement) => void;
87
108
  export {elementsTreeOutlineStyles};
88
109
 
89
110
  interface ViewInput {
111
+ domTreeWidget?: DOMTreeWidget;
112
+ rootDOMNode: SDK.DOMModel.DOMNode|null;
90
113
  omitRootDOMNode: boolean;
91
114
  selectEnabled: boolean;
92
115
  hideGutter: boolean;
@@ -104,6 +127,9 @@ interface ViewInput {
104
127
  preventTabOrder: boolean;
105
128
  deindentSingleNode: boolean;
106
129
  currentHighlightedNode: SDK.DOMModel.DOMNode|null;
130
+ hoveredNode?: SDK.DOMModel.DOMNode|null;
131
+ searchMatchNode?: SDK.DOMModel.DOMNode|null;
132
+ searchMatchQuery?: string|null;
107
133
 
108
134
  selectedNode: SDK.DOMModel.DOMNode|null;
109
135
 
@@ -112,12 +138,55 @@ interface ViewInput {
112
138
  onElementsTreeUpdated: (event: Common.EventTarget.EventTargetEvent<SDK.DOMModel.DOMNode[]>) => void;
113
139
  onElementCollapsed: () => void;
114
140
  onElementExpanded: () => void;
141
+
142
+ onSelect?: (node: SDK.DOMModel.DOMNode, selectedByUser?: boolean) => void;
143
+ onExpand?: (node: SDK.DOMModel.DOMNode, expanded: boolean) => void;
144
+ onContextMenu?: (node: SDK.DOMModel.DOMNode, event: MouseEvent, widget?: ElementsTreeWidget) => void;
145
+ onHoverNode?: (node: SDK.DOMModel.DOMNode, showInfo?: boolean) => void;
146
+ onLeave?: () => void;
147
+ onToggleHideElement?: (node: SDK.DOMModel.DOMNode) => void;
148
+ onKeyDown?: (event: KeyboardEvent) => void;
149
+ isToggledToHidden?: (node: SDK.DOMModel.DOMNode) => boolean;
150
+ onDuplicateNode?: (node: SDK.DOMModel.DOMNode) => void;
151
+ isNodeExpanded?: (node: SDK.DOMModel.DOMNode) => boolean;
152
+ isNodeInClipboard?: (node: SDK.DOMModel.DOMNode) => boolean;
153
+ onCopyOrCut?: (isCut: boolean, event: Event) => void;
154
+ onPaste?: (event: Event) => void;
155
+ onSelectNodeAfterEdit?: (wasExpanded: boolean, error: string|null, newNode: SDK.DOMModel.DOMNode|null,
156
+ moveDirection?: string) => void;
157
+ nodeToEdit?: ({node: SDK.DOMModel.DOMNode}&InitialEditState)|null;
158
+ onInitialEditCompleted?: () => void;
159
+ dragOverNode?: {node: SDK.DOMModel.DOMNode, isClosingTag: boolean}|null;
160
+ isValidDragSource?: (node: SDK.DOMModel.DOMNode) => boolean;
161
+ onDragStart?: (node: SDK.DOMModel.DOMNode, event: DragEvent, textContent?: string) => boolean;
162
+ onDragOver?: (node: SDK.DOMModel.DOMNode, isClosingTag: boolean, event: DragEvent) => boolean;
163
+ onDragLeave?: (event: DragEvent) => void;
164
+ onDrop?: (node: SDK.DOMModel.DOMNode, isClosingTag: boolean, event: DragEvent) => void;
165
+ onDragEnd?: (event: DragEvent) => void;
166
+ getTopLayerShortcuts?: (doc: SDK.DOMModel.DOMDocument) => SDK.DOMModel.DOMNodeShortcut[];
167
+ isTopLayerExpanded?: (doc: SDK.DOMModel.DOMDocument) => boolean;
168
+ onToggleTopLayerExpanded?: (doc: SDK.DOMModel.DOMDocument, expanded: boolean) => void;
169
+ onSelectTopLayerContainer?: (doc: SDK.DOMModel.DOMDocument) => void;
170
+ isTopLayerShortcutExpanded?: (shortcut: SDK.DOMModel.DOMNodeShortcut) => boolean;
171
+ onToggleTopLayerShortcutExpanded?: (shortcut: SDK.DOMModel.DOMNodeShortcut, expanded: boolean) => void;
172
+ selectedTopLayerShortcut?: SDK.DOMModel.DOMNodeShortcut|null;
173
+ onSelectTopLayerShortcut?: (shortcut: SDK.DOMModel.DOMNodeShortcut) => void;
174
+ onRevealTopLayerShortcut?: (shortcut: SDK.DOMModel.DOMNodeShortcut) => void;
175
+ isAdoptedStyleSheetsExpanded?: (node: SDK.DOMModel.DOMNode) => boolean;
176
+ onToggleAdoptedStyleSheetsExpanded?: (node: SDK.DOMModel.DOMNode, expanded: boolean) => void;
177
+ onSelectAdoptedStyleSheets?: (node: SDK.DOMModel.DOMNode) => void;
178
+ isAdoptedStyleSheetExpanded?: (sheet: SDK.DOMModel.AdoptedStyleSheet) => boolean;
179
+ onToggleAdoptedStyleSheetExpanded?: (sheet: SDK.DOMModel.AdoptedStyleSheet, expanded: boolean) => void;
180
+ selectedAdoptedStyleSheet?: SDK.DOMModel.AdoptedStyleSheet|null;
181
+ onSelectAdoptedStyleSheet?: (sheet: SDK.DOMModel.AdoptedStyleSheet) => void;
115
182
  }
116
183
 
117
184
  interface ViewOutput {
118
185
  elementsTreeOutline?: ElementsTreeOutline;
119
186
  imagePreviewPopover?: ImagePreviewPopover;
120
187
  highlightedTreeElement: ElementsTreeElement|null;
188
+ searchMatchTreeElement?: ElementsTreeElement|null;
189
+ searchMatchQuery?: string;
121
190
  isUpdatingHighlights: boolean;
122
191
  alreadyExpandedParentTreeElement: ElementsTreeElement|null;
123
192
  }
@@ -127,9 +196,9 @@ export const DEFAULT_VIEW = (input: ViewInput, output: ViewOutput, target: HTMLE
127
196
  // FIXME: this is basically a ref to existing imperative
128
197
  // implementation. Once this is declarative the ref should not be
129
198
  // needed.
130
- const elementsTreeOutline = new ElementsTreeOutline(input.omitRootDOMNode, input.selectEnabled, input.hideGutter,
131
- input.maxTreeDepth, input.enableContextMenu, input.showComments,
132
- input.showAIButton, input.disableEdits, input.expandRoot);
199
+ const elementsTreeOutline = new ElementsTreeOutline(
200
+ input.omitRootDOMNode, input.selectEnabled, input.hideGutter, input.maxTreeDepth, input.enableContextMenu,
201
+ input.showComments, input.showAIButton, input.disableEdits, input.expandRoot, input.domTreeWidget);
133
202
  output.elementsTreeOutline = elementsTreeOutline;
134
203
  elementsTreeOutline.addEventListener(ElementsTreeOutline.Events.SelectedNodeChanged, input.onSelectedNodeChanged,
135
204
  this);
@@ -146,9 +215,21 @@ export const DEFAULT_VIEW = (input: ViewInput, output: ViewOutput, target: HTMLE
146
215
  elementsTreeOutline.elementInternal.addEventListener('contextmenu', (event: MouseEvent) => {
147
216
  const treeElement = elementsTreeOutline.treeElementFromEventInternal(event);
148
217
  if (treeElement instanceof ElementsTreeElement) {
149
- output.elementsTreeOutline?.showContextMenu(treeElement, event);
218
+ input.onContextMenu?.(treeElement.node(), event, treeElement.widget);
150
219
  }
151
220
  }, false);
221
+ elementsTreeOutline.elementInternal.addEventListener('keydown', (event: KeyboardEvent) => {
222
+ input.onKeyDown?.(event);
223
+ }, false);
224
+ elementsTreeOutline.elementInternal.addEventListener('clipboard-copy', (event: Event) => {
225
+ input.onCopyOrCut?.(false, event);
226
+ }, false);
227
+ elementsTreeOutline.elementInternal.addEventListener('clipboard-cut', (event: Event) => {
228
+ input.onCopyOrCut?.(true, event);
229
+ }, false);
230
+ elementsTreeOutline.elementInternal.addEventListener('clipboard-paste', (event: Event) => {
231
+ input.onPaste?.(event);
232
+ }, false);
152
233
  output.imagePreviewPopover = new ImagePreviewPopover(
153
234
  elementsTreeOutline.contentElement,
154
235
  event => {
@@ -209,7 +290,6 @@ export const DEFAULT_VIEW = (input: ViewInput, output: ViewOutput, target: HTMLE
209
290
  ${issues.map(issue => {
210
291
  const elementIssueDetails = getElementIssueDetails(issue);
211
292
  if (!elementIssueDetails) {
212
- // This shouldn't happen, but add this if check to pass ts check.
213
293
  return nothing;
214
294
  }
215
295
  const issueKindIconName = IssueCounter.IssueCounter.getIssueKindIconName(issue.getKind());
@@ -234,7 +314,9 @@ export const DEFAULT_VIEW = (input: ViewInput, output: ViewOutput, target: HTMLE
234
314
  output.elementsTreeOutline.maxTreeDepth = input.maxTreeDepth;
235
315
  output.elementsTreeOutline.enableContextMenu = input.enableContextMenu ?? true;
236
316
  output.elementsTreeOutline.showContextMenu = (treeElement, event) => {
237
- void showContextMenu(treeElement, event);
317
+ if (event instanceof MouseEvent) {
318
+ input.onContextMenu?.(treeElement.node(), event, treeElement.widget);
319
+ }
238
320
  };
239
321
  let needsUpdate = false;
240
322
  const showComments = input.showComments ?? true;
@@ -310,6 +392,578 @@ export const DEFAULT_VIEW = (input: ViewInput, output: ViewOutput, target: HTMLE
310
392
  treeElement?.reveal(true);
311
393
  output.isUpdatingHighlights = false;
312
394
  }
395
+
396
+ const previousSearchMatchNode = output.searchMatchTreeElement?.node() ?? null;
397
+ if (previousSearchMatchNode !== input.searchMatchNode || output.searchMatchQuery !== input.searchMatchQuery) {
398
+ if (output.searchMatchTreeElement) {
399
+ output.searchMatchTreeElement.hideSearchHighlights();
400
+ output.searchMatchTreeElement = null;
401
+ }
402
+ output.searchMatchQuery = input.searchMatchQuery ?? undefined;
403
+ if (input.searchMatchNode) {
404
+ const treeElement = output.elementsTreeOutline.findTreeElement(input.searchMatchNode);
405
+ if (treeElement) {
406
+ output.searchMatchTreeElement = treeElement;
407
+ if (input.searchMatchQuery) {
408
+ treeElement.highlightSearchResults(input.searchMatchQuery);
409
+ }
410
+ treeElement.reveal();
411
+ const matches =
412
+ treeElement.listItemElement.getElementsByClassName(Highlighting.highlightedSearchResultClassName);
413
+ if (matches.length) {
414
+ matches[0].scrollIntoViewIfNeeded(false);
415
+ }
416
+ }
417
+ }
418
+ }
419
+
420
+ if (input.nodeToEdit) {
421
+ const treeElement = output.elementsTreeOutline.findTreeElement(input.nodeToEdit.node);
422
+ if (treeElement) {
423
+ const edit = input.nodeToEdit;
424
+ input.onInitialEditCompleted?.();
425
+ if (edit.isEditAsHTML) {
426
+ treeElement.widget.toggleEditAsHTML(edit.editAsHTMLCallback);
427
+ } else if (edit.isProcessingInstruction) {
428
+ treeElement.widget.startEditingProcessingInstructionValue();
429
+ } else if (edit.isTextNode) {
430
+ const textNode = treeElement.widget.contentElement.querySelector('.webkit-html-text-node');
431
+ if (textNode) {
432
+ treeElement.widget.startEditingTextNode(textNode);
433
+ }
434
+ } else if (edit.isNewAttribute) {
435
+ treeElement.widget.addNewAttribute();
436
+ } else if (edit.attributeName) {
437
+ treeElement.widget.triggerEditAttribute(edit.attributeName);
438
+ }
439
+ }
440
+ }
441
+ };
442
+
443
+ function isMaxDepthReached(node: SDK.DOMModel.DOMNode, rootDOMNode: SDK.DOMModel.DOMNode|null, maxTreeDepth?: number,
444
+ omitRootDOMNode?: boolean): boolean {
445
+ if (maxTreeDepth === undefined || maxTreeDepth === Infinity) {
446
+ return false;
447
+ }
448
+ if (node.nodeType() === Node.DOCUMENT_NODE || node.isShadowRoot()) {
449
+ return false;
450
+ }
451
+ let depth = 0;
452
+ let current: SDK.DOMModel.DOMNode|null = node;
453
+ while (current && current !== rootDOMNode) {
454
+ depth++;
455
+ current = current.parentNode;
456
+ }
457
+ if (!omitRootDOMNode) {
458
+ depth++;
459
+ }
460
+ return depth >= maxTreeDepth;
461
+ }
462
+
463
+ function nodeHasVisibleChildren(node: SDK.DOMModel.DOMNode, rootDOMNode: SDK.DOMModel.DOMNode|null = null,
464
+ maxTreeDepth?: number, omitRootDOMNode?: boolean): boolean {
465
+ if (isMaxDepthReached(node, rootDOMNode, maxTreeDepth, omitRootDOMNode)) {
466
+ return false;
467
+ }
468
+ if (node.isIframe() || node.contentDocument() || node.templateContent() ||
469
+ ElementsTreeWidget.visibleShadowRoots(node).length || node.hasPseudoElements() || node.isInsertionPoint() ||
470
+ node.adoptedStyleSheetsForNode.length) {
471
+ return true;
472
+ }
473
+ return Boolean(node.childNodeCount()) && !ElementsTreeWidget.canShowInlineText(node);
474
+ }
475
+
476
+ function getVisibleChildren(node: SDK.DOMModel.DOMNode, showComments = true): SDK.DOMModel.DOMNode[] {
477
+ const children: SDK.DOMModel.DOMNode[] = [];
478
+ children.push(...ElementsTreeWidget.visibleShadowRoots(node));
479
+
480
+ const contentDocument = node.contentDocument();
481
+ if (contentDocument) {
482
+ children.push(contentDocument);
483
+ }
484
+
485
+ const templateContent = node.templateContent();
486
+ if (templateContent) {
487
+ children.push(templateContent);
488
+ }
489
+
490
+ children.push(...node.viewTransitionPseudoElements());
491
+
492
+ const markerPseudo = node.markerPseudoElement();
493
+ if (markerPseudo) {
494
+ children.push(markerPseudo);
495
+ }
496
+
497
+ const checkmarkPseudo = node.checkmarkPseudoElement();
498
+ if (checkmarkPseudo) {
499
+ children.push(checkmarkPseudo);
500
+ }
501
+
502
+ const beforePseudo = node.beforePseudoElement();
503
+ if (beforePseudo) {
504
+ children.push(beforePseudo);
505
+ }
506
+
507
+ children.push(...node.carouselPseudoElements());
508
+
509
+ if (node.childNodeCount()) {
510
+ const nodeChildren = node.children();
511
+ if (nodeChildren) {
512
+ let filteredChildren = nodeChildren;
513
+ if (!showComments) {
514
+ filteredChildren = filteredChildren.filter(n => n.nodeType() !== Node.COMMENT_NODE);
515
+ }
516
+ children.push(...filteredChildren);
517
+ }
518
+ }
519
+
520
+ const afterPseudo = node.afterPseudoElement();
521
+ if (afterPseudo) {
522
+ children.push(afterPseudo);
523
+ }
524
+
525
+ const pickerIconPseudo = node.pickerIconPseudoElement();
526
+ if (pickerIconPseudo) {
527
+ children.push(pickerIconPseudo);
528
+ }
529
+
530
+ const interestButtonPseudo = node.interestButtonPseudoElement();
531
+ if (interestButtonPseudo) {
532
+ children.push(interestButtonPseudo);
533
+ }
534
+
535
+ const backdropPseudo = node.backdropPseudoElement();
536
+ if (backdropPseudo) {
537
+ children.push(backdropPseudo);
538
+ }
539
+
540
+ return children;
541
+ }
542
+
543
+ function isAncestorOf(ancestor: SDK.DOMModel.DOMNode, descendant: SDK.DOMModel.DOMNode): boolean {
544
+ let current: SDK.DOMModel.DOMNode|null = descendant.parentNode;
545
+ while (current) {
546
+ if (current === ancestor) {
547
+ return true;
548
+ }
549
+ current = current.parentNode;
550
+ }
551
+ return false;
552
+ }
553
+
554
+ function computeLeftIndent(depth: number, isExpandable: boolean): number {
555
+ return 12 * (depth - 1) + (isExpandable ? 1 : 12);
556
+ }
557
+
558
+ export const DECLARATIVE_VIEW: View = (input: ViewInput, _output: ViewOutput, target: HTMLElement): void => {
559
+ let rootNodes: SDK.DOMModel.DOMNode[] = [];
560
+ const rootDOMNode = input.rootDOMNode;
561
+ if (rootDOMNode) {
562
+ if (input.omitRootDOMNode) {
563
+ rootNodes = getVisibleChildren(rootDOMNode, input.showComments ?? true);
564
+ } else {
565
+ rootNodes = [rootDOMNode];
566
+ }
567
+ }
568
+
569
+ const on = Lit.Directive.directive(Lit.CustomDirectives.InterceptBindingDirective);
570
+
571
+ const renderShortcut = (shortcut: SDK.DOMModel.DOMNodeShortcut, shortcutDepth: number): Lit.LitTemplate => {
572
+ const hasShortcutChildren = shortcut.childShortcuts.length > 0;
573
+ const isShortcutExpanded = Boolean(input.isTopLayerShortcutExpanded?.(shortcut));
574
+ const isShortcutSelected = input.selectedTopLayerShortcut === shortcut;
575
+ let title = shortcut.nodeName.toLowerCase();
576
+ if (shortcut.nodeType === Node.ELEMENT_NODE) {
577
+ title = '<' + title + '>';
578
+ }
579
+ const onShortcutExpand = (event: UI.TreeOutline.TreeViewElement.ExpandEvent): void => {
580
+ input.onToggleTopLayerShortcutExpanded?.(shortcut, event.detail.expanded);
581
+ };
582
+ const onShortcutSelect = (): void => {
583
+ input.onSelectTopLayerShortcut?.(shortcut);
584
+ };
585
+ const onReveal = (event: Event): void => {
586
+ event.stopPropagation();
587
+ input.onRevealTopLayerShortcut?.(shortcut);
588
+ };
589
+ const onShortcutMouseMove = (event: MouseEvent): void => {
590
+ event.stopPropagation();
591
+ shortcut.deferredNode.highlight();
592
+ };
593
+ const onShortcutMouseLeave = (event: MouseEvent): void => {
594
+ event.stopPropagation();
595
+ SDK.OverlayModel.OverlayModel.hideDOMNodeHighlight(SDK.TargetManager.TargetManager.instance());
596
+ };
597
+
598
+ return html`
599
+ <li role="treeitem"
600
+ ?selected=${isShortcutSelected}
601
+ ?open=${isShortcutExpanded}
602
+ class="elements-tree-shortcut"
603
+ style="--indent: ${computeLeftIndent(shortcutDepth, hasShortcutChildren)}px"
604
+ @select=${onShortcutSelect}
605
+ @expand=${onShortcutExpand}
606
+ @mousemove=${on(onShortcutMouseMove)}
607
+ @mouseleave=${on(onShortcutMouseLeave)}
608
+ jslog=${VisualLogging.treeItem().parent('elementsTreeOutline')}>
609
+ <div class="selection fill"></div>
610
+ <span class="elements-tree-shortcut-title">\u21AA ${title}</span>
611
+ <devtools-adorner
612
+ .name=${ElementsComponents.AdornerManager.RegisteredAdorners.REVEAL}
613
+ class="adorner-reveal clickable"
614
+ role=button
615
+ tabindex=0
616
+ jslog=${VisualLogging.adorner('reveal').track({
617
+ click: true,
618
+ })}
619
+ aria-label=${i18nString(UIStrings.reveal)}
620
+ @click=${on(onReveal)}
621
+ @keydown=${handleAdornerKeydown(onReveal)}
622
+ @mousedown=${(e: Event) => e.consume()}
623
+ ${adornerRef()}>
624
+ <span class="adorner-with-icon">
625
+ <devtools-icon name="select-element"></devtools-icon>
626
+ <span>${ElementsComponents.AdornerManager.RegisteredAdorners.REVEAL}</span>
627
+ </span>
628
+ </devtools-adorner>
629
+ ${
630
+ hasShortcutChildren ? html`
631
+ <ul role="group">
632
+ ${UI.TreeOutline.ifExpanded(html`
633
+ ${shortcut.childShortcuts.map(child => renderShortcut(child, shortcutDepth + 1))}
634
+ `)}
635
+ </ul>
636
+ ` :
637
+ nothing}
638
+ </li>
639
+ `;
640
+ };
641
+
642
+ const renderTopLayerContainer =
643
+ (doc: SDK.DOMModel.DOMDocument, containerDepth: number): Lit.LitTemplate|typeof nothing => {
644
+ const shortcuts = input.getTopLayerShortcuts?.(doc) ?? [];
645
+ if (shortcuts.length === 0) {
646
+ return nothing;
647
+ }
648
+ const isTopLayerExpanded = Boolean(input.isTopLayerExpanded?.(doc));
649
+ const onTopLayerExpand = (event: UI.TreeOutline.TreeViewElement.ExpandEvent): void => {
650
+ input.onToggleTopLayerExpanded?.(doc, event.detail.expanded);
651
+ };
652
+ const onTopLayerSelect = (): void => {
653
+ input.onSelectTopLayerContainer?.(doc);
654
+ };
655
+
656
+ return html`
657
+ <li role="treeitem"
658
+ ?open=${isTopLayerExpanded}
659
+ class="elements-tree-top-layer-container"
660
+ style="--indent: ${computeLeftIndent(containerDepth, true)}px"
661
+ @select=${onTopLayerSelect}
662
+ @expand=${onTopLayerExpand}
663
+ jslog=${VisualLogging.treeItem().parent('elementsTreeOutline')}>
664
+ <div class="selection fill"></div>
665
+ <span class="elements-tree-shortcut-title">#top-layer</span>
666
+ <ul role="group">
667
+ ${UI.TreeOutline.ifExpanded(html`
668
+ ${shortcuts.map(sc => renderShortcut(sc, containerDepth + 1))}
669
+ `)}
670
+ </ul>
671
+ </li>
672
+ `;
673
+ };
674
+
675
+ const renderAdoptedStyleSheet = (sheet: SDK.DOMModel.AdoptedStyleSheet, depth: number): Lit.LitTemplate => {
676
+ const header = sheet.cssModel.styleSheetHeaderForId(sheet.id);
677
+ const linkText = header?.sourceURL;
678
+ const isExpanded = Boolean(input.isAdoptedStyleSheetExpanded?.(sheet));
679
+ const isSelected = input.selectedAdoptedStyleSheet === sheet;
680
+ const onExpand = (event: UI.TreeOutline.TreeViewElement.ExpandEvent): void => {
681
+ input.onToggleAdoptedStyleSheetExpanded?.(sheet, event.detail.expanded);
682
+ };
683
+ const onSelect = (): void => {
684
+ input.onSelectAdoptedStyleSheet?.(sheet);
685
+ };
686
+
687
+ // clang-format off
688
+ return html`
689
+ <li role="treeitem"
690
+ ?selected=${isSelected}
691
+ ?open=${isExpanded}
692
+ class="elements-tree-adopted-style-sheet"
693
+ style="--indent: ${computeLeftIndent(depth, true)}px"
694
+ @select=${onSelect}
695
+ @expand=${onExpand}
696
+ jslog=${VisualLogging.treeItem('adopted-style-sheet').parent('elementsTreeOutline')}>
697
+ <div class="selection fill"></div>
698
+ <span class="elements-tree-shortcut-title">#adopted-style-sheet${linkText ? html` (${UIComponentUtils.Linkifier.Linkifier.linkifyURL(linkText, {
699
+ text: linkText,
700
+ preventClick: true,
701
+ showColumnNumber: false,
702
+ })})` : nothing}</span>
703
+ <ul role="group">
704
+ ${UI.TreeOutline.ifExpanded(html`
705
+ ${header ? html`
706
+ <li role="treeitem"
707
+ class="elements-tree-adopted-style-sheet-contents"
708
+ style="--indent: ${computeLeftIndent(depth + 1, false)}px"
709
+ jslog=${VisualLogging.treeItem('adopted-style-sheet-contents').parent('elementsTreeOutline')}>
710
+ <div class="selection fill"></div>
711
+ ${UI.Widget.widget(AdoptedStyleSheetContentsWidget, {styleSheetHeader: header})}
712
+ </li>
713
+ ` : nothing}
714
+ `)}
715
+ </ul>
716
+ </li>
717
+ `;
718
+ // clang-format on
719
+ };
720
+
721
+ const renderAdoptedStyleSheets = (node: SDK.DOMModel.DOMNode, depth: number): Lit.LitTemplate|typeof nothing => {
722
+ const sheets = node.adoptedStyleSheetsForNode;
723
+ if (!sheets || sheets.length === 0) {
724
+ return nothing;
725
+ }
726
+ const isExpanded = Boolean(input.isAdoptedStyleSheetsExpanded?.(node));
727
+ const onExpand = (event: UI.TreeOutline.TreeViewElement.ExpandEvent): void => {
728
+ input.onToggleAdoptedStyleSheetsExpanded?.(node, event.detail.expanded);
729
+ };
730
+ const onSelect = (): void => {
731
+ input.onSelectAdoptedStyleSheets?.(node);
732
+ };
733
+
734
+ // clang-format off
735
+ return html`
736
+ <li role="treeitem"
737
+ ?open=${isExpanded}
738
+ class="elements-tree-adopted-style-sheets"
739
+ style="--indent: ${computeLeftIndent(depth, true)}px"
740
+ @select=${onSelect}
741
+ @expand=${onExpand}
742
+ jslog=${VisualLogging.treeItem('adopted-style-sheets').parent('elementsTreeOutline')}>
743
+ <div class="selection fill"></div>
744
+ <span class="elements-tree-shortcut-title">#adopted-style-sheets</span>
745
+ <ul role="group">
746
+ ${UI.TreeOutline.ifExpanded(html`
747
+ ${sheets.map(sheet => renderAdoptedStyleSheet(sheet, depth + 1))}
748
+ `)}
749
+ </ul>
750
+ </li>
751
+ `;
752
+ // clang-format on
753
+ };
754
+
755
+ const renderNode = (node: SDK.DOMModel.DOMNode, depth = 0): Lit.LitTemplate => {
756
+ const isSelected = input.selectedNode === node;
757
+ const isHovered = (input.currentHighlightedNode === node) || (input.hoveredNode === node);
758
+ const isExpanded = Boolean(
759
+ (input.currentHighlightedNode && isAncestorOf(node, input.currentHighlightedNode)) ||
760
+ (input.isNodeExpanded ?
761
+ input.isNodeExpanded(node) :
762
+ (input.expandRoot &&
763
+ (node === input.rootDOMNode || (input.omitRootDOMNode && node.parentNode === input.rootDOMNode)))));
764
+ const hasChildren = nodeHasVisibleChildren(node, input.rootDOMNode, input.maxTreeDepth, input.omitRootDOMNode);
765
+ const children = hasChildren ? getVisibleChildren(node, input.showComments ?? true) : [];
766
+ const tagName = node.nodeName().toLowerCase();
767
+ const needsClosingTag = node.nodeType() === Node.ELEMENT_NODE && !ForbiddenClosingTagElements.has(tagName) &&
768
+ !node.pseudoType() && (hasChildren || !ElementsTreeWidget.canShowInlineText(node));
769
+
770
+ // TODO: Move tree node pagination ("Show all nodes" button and expandedChildrenLimit) to a declarative tree slice model.
771
+ // TODO: Move ImagePreviewPopover and issue tooltip helpers into declarative Lit directives or tooltip components.
772
+ // TODO: Move DOMModel event subscription and reactive state synchronization (updateRecords, DOM update animations) directly into DOMTreeWidget.
773
+
774
+ const on = Lit.Directive.directive(Lit.CustomDirectives.InterceptBindingDirective);
775
+
776
+ const onSelect = (): void => {
777
+ input.onSelect?.(node, /* selectedByUser= */ true);
778
+ };
779
+
780
+ const onExpand = (event: UI.TreeOutline.TreeViewElement.ExpandEvent): void => {
781
+ input.onExpand?.(node, event.detail.expanded);
782
+ };
783
+
784
+ const isDragOver = input.dragOverNode?.node === node && !input.dragOverNode.isClosingTag;
785
+ const isClosingTagDragOver = input.dragOverNode?.node === node && Boolean(input.dragOverNode.isClosingTag);
786
+ const isDraggable = !input.disableEdits && Boolean(input.isValidDragSource?.(node));
787
+
788
+ const classes = classMap({
789
+ hovered: isHovered,
790
+ 'in-clipboard': Boolean(input.isNodeInClipboard?.(node)),
791
+ 'elements-drag-over': isDragOver,
792
+ });
793
+
794
+ const onMouseMove = (event: MouseEvent): void => {
795
+ event.stopPropagation();
796
+ const showInfo = !UI.KeyboardShortcut.KeyboardShortcut.eventHasEitherCtrlOrMeta(event);
797
+ input.onHoverNode?.(node, showInfo);
798
+ };
799
+
800
+ const onContextMenu = (event: MouseEvent): void => {
801
+ event.stopPropagation();
802
+ input.onContextMenu?.(node, event);
803
+ };
804
+
805
+ const onDragStart = (event: DragEvent): void => {
806
+ event.stopPropagation();
807
+ const currentTarget = event.currentTarget as HTMLElement | null;
808
+ const textContent = currentTarget?.firstElementChild?.textContent ?? currentTarget?.textContent ?? undefined;
809
+ input.onDragStart?.(node, event, textContent);
810
+ };
811
+
812
+ const onDragOver = (event: DragEvent): void => {
813
+ event.stopPropagation();
814
+ input.onDragOver?.(node, /* isClosingTag= */ false, event);
815
+ };
816
+
817
+ const onClosingTagDragOver = (event: DragEvent): void => {
818
+ event.stopPropagation();
819
+ input.onDragOver?.(node, /* isClosingTag= */ true, event);
820
+ };
821
+
822
+ const onDragLeave = (event: DragEvent): void => {
823
+ event.stopPropagation();
824
+ input.onDragLeave?.(event);
825
+ };
826
+
827
+ const onDrop = (event: DragEvent): void => {
828
+ event.stopPropagation();
829
+ input.onDrop?.(node, /* isClosingTag= */ false, event);
830
+ };
831
+
832
+ const onClosingTagDrop = (event: DragEvent): void => {
833
+ event.stopPropagation();
834
+ input.onDrop?.(node, /* isClosingTag= */ true, event);
835
+ };
836
+
837
+ const onDragEnd = (event: DragEvent): void => {
838
+ event.stopPropagation();
839
+ input.onDragEnd?.(event);
840
+ };
841
+
842
+ /* clang-format off */
843
+ return html`
844
+ <li role="treeitem"
845
+ ?selected=${isSelected}
846
+ class=${classes}
847
+ ?open=${isExpanded}
848
+ draggable=${isDraggable ? 'true' : 'false'}
849
+ @select=${onSelect}
850
+ @expand=${onExpand}
851
+ @mousemove=${on(onMouseMove)}
852
+ @contextmenu=${on(onContextMenu)}
853
+ @dragstart=${on(onDragStart)}
854
+ @dragover=${on(onDragOver)}
855
+ @dragleave=${on(onDragLeave)}
856
+ @drop=${on(onDrop)}
857
+ @dragend=${on(onDragEnd)}
858
+ jslog=${VisualLogging.treeItem().parent('elementsTreeOutline').track({
859
+ keydown: 'ArrowUp|ArrowDown|ArrowLeft|ArrowRight|Backspace|Delete|Enter|Space|Home|End',
860
+ resize: true,
861
+ drag: true,
862
+ click: true,
863
+ })}>
864
+ ${UI.Widget.widget(ElementsTreeWidget, {
865
+ node,
866
+ isClosingTag: false,
867
+ expanded: isExpanded,
868
+ isExpandable: hasChildren,
869
+ selected: isSelected,
870
+ isDOMNodeSelected: isSelected,
871
+ hovered: isHovered,
872
+ searchQuery: input.searchMatchNode === node ? (input.searchMatchQuery ?? null) : null,
873
+ inClipboard: input.isNodeInClipboard?.(node) ?? false,
874
+ computeLeftIndent: computeLeftIndent(depth, hasChildren),
875
+ disableEdits: input.disableEdits ?? false,
876
+ showAIButton: input.showAIButton ?? true,
877
+ initialEdit: input.nodeToEdit?.node === node ? input.nodeToEdit : null,
878
+ onInitialEditCompleted: input.onInitialEditCompleted,
879
+ revealInTopLayer: (n: SDK.DOMModel.DOMNode) => input.domTreeWidget?.revealInTopLayer(n),
880
+ setMultilineEditing: multilineEditing => input.domTreeWidget?.setMultilineEditing(multilineEditing),
881
+ visibleWidth: () => input.domTreeWidget?.visibleWidth ?? 0,
882
+ selectDOMNode: (n: SDK.DOMModel.DOMNode, selectedByUser?: boolean) => input.onSelect?.(n, selectedByUser),
883
+ selectNodeAfterEdit: (wasExpanded: boolean, error: string|null, newNode: SDK.DOMModel.DOMNode|null, moveDirection?: string) => {
884
+ input.onSelectNodeAfterEdit?.(wasExpanded, error, newNode, moveDirection);
885
+ },
886
+ toggleHideElement: (n: SDK.DOMModel.DOMNode) => {
887
+ input.onToggleHideElement?.(n);
888
+ return Promise.resolve();
889
+ },
890
+ isToggledToHidden: (n: SDK.DOMModel.DOMNode) => input.isToggledToHidden?.(n) ?? false,
891
+ showContextMenu: (event: Event, widget?: ElementsTreeWidget) => {
892
+ if (event instanceof MouseEvent) {
893
+ input.onContextMenu?.(node, event, widget);
894
+ }
895
+ },
896
+ })}
897
+ ${hasChildren ? html`
898
+ <ul role="group">
899
+ ${UI.TreeOutline.ifExpanded(html`
900
+ ${node.adoptedStyleSheetsForNode.length > 0 ? renderAdoptedStyleSheets(node, depth + 1) : nothing}
901
+ ${children.map(child => renderNode(child, depth + 1))}
902
+ ${node instanceof SDK.DOMModel.DOMDocument ? renderTopLayerContainer(node, depth + 1) : nothing}
903
+ ${needsClosingTag ? html`
904
+ <li role="treeitem"
905
+ class=${classMap({hovered: isHovered, 'elements-drag-over': isClosingTagDragOver})}
906
+ jslog=${VisualLogging.treeItem().parent('elementsTreeOutline')}
907
+ @mousemove=${on(onMouseMove)}
908
+ @dragover=${on(onClosingTagDragOver)}
909
+ @dragleave=${on(onDragLeave)}
910
+ @drop=${on(onClosingTagDrop)}>
911
+ ${UI.Widget.widget(ElementsTreeWidget, {
912
+ node,
913
+ isClosingTag: true,
914
+ expanded: false,
915
+ isExpandable: false,
916
+ selected: false,
917
+ isDOMNodeSelected: false,
918
+ hovered: isHovered,
919
+ computeLeftIndent: computeLeftIndent(depth, false),
920
+ disableEdits: input.disableEdits ?? false,
921
+ showAIButton: false,
922
+ showContextMenu: (event: Event, widget?: ElementsTreeWidget) => {
923
+ if (event instanceof MouseEvent) {
924
+ input.onContextMenu?.(node, event, widget);
925
+ }
926
+ },
927
+ })}
928
+ </li>
929
+ ` : nothing}
930
+ `)}
931
+ </ul>
932
+ ` : nothing}
933
+ </li>
934
+ `;
935
+ /* clang-format on */
936
+ };
937
+
938
+ /* clang-format off */
939
+ render(html`
940
+ <style>${UI.inspectorCommonStyles}</style>
941
+ <style>${elementsTreeOutlineStyles}</style>
942
+ <style>${CodeHighlighter.codeHighlighterStyles}</style>
943
+ <div class="elements-disclosure ${input.deindentSingleNode && rootNodes.length === 1 && !nodeHasVisibleChildren(rootNodes[0], input.rootDOMNode, input.maxTreeDepth, input.omitRootDOMNode) ? 'single-node' : ''}">
944
+ <devtools-tree
945
+ class="elements-tree-outline source-code ${input.wrap ? '' : 'elements-tree-nowrap'} ${input.hideGutter ? 'elements-hide-gutter' : ''}"
946
+ aria-label=${i18nString(UIStrings.pageDom)}
947
+ jslog=${VisualLogging.tree('elements')}
948
+ ?show-selection-on-keyboard-focus=${input.showSelectionOnKeyboardFocus}
949
+ @keydown=${input.onKeyDown}
950
+ @clipboard-copy=${(event: Event) => input.onCopyOrCut?.(false, event)}
951
+ @clipboard-cut=${(event: Event) => input.onCopyOrCut?.(true, event)}
952
+ @clipboard-paste=${(event: Event) => input.onPaste?.(event)}
953
+ @mouseleave=${input.onLeave}
954
+ .template=${html`
955
+ <style>${UI.inspectorCommonStyles}</style>
956
+ <style>${elementsTreeOutlineStyles}</style>
957
+ <style>${CodeHighlighter.codeHighlighterStyles}</style>
958
+ <ul role="tree">
959
+ ${rootNodes.map(node => renderNode(node))}
960
+ ${input.omitRootDOMNode && input.rootDOMNode instanceof SDK.DOMModel.DOMDocument ? renderTopLayerContainer(input.rootDOMNode, 1) : nothing}
961
+ </ul>
962
+ `}>
963
+ </devtools-tree>
964
+ </div>
965
+ `, target);
966
+ /* clang-format on */
313
967
  };
314
968
 
315
969
  /**
@@ -330,6 +984,8 @@ export class DOMTreeWidget extends UI.Widget.Widget {
330
984
  (event:
331
985
  Common.EventTarget.EventTargetEvent<{node: SDK.DOMModel.DOMNode | null, focus: boolean}>) => void = () => {};
332
986
  onElementsTreeUpdated: (event: Common.EventTarget.EventTargetEvent<SDK.DOMModel.DOMNode[]>) => void = () => {};
987
+ onElementCollapsed: () => void = () => {};
988
+ onElementExpanded: () => void = () => {};
333
989
  onDocumentUpdated: (domModel: SDK.DOMModel.DOMModel) => void = () => {};
334
990
 
335
991
  #maxTreeDepth?: number;
@@ -354,14 +1010,37 @@ export class DOMTreeWidget extends UI.Widget.Widget {
354
1010
  return this.#maxRows;
355
1011
  }
356
1012
 
1013
+ get visibleWidth(): number {
1014
+ return this.#visibleWidth ?? this.contentElement.offsetWidth;
1015
+ }
1016
+
357
1017
  set visibleWidth(width: number) {
358
1018
  this.#visibleWidth = width;
359
1019
  this.performUpdate();
360
1020
  }
361
1021
 
1022
+ #rootDOMNode: SDK.DOMModel.DOMNode|null = null;
1023
+ #selectedDOMNode: SDK.DOMModel.DOMNode|null = null;
1024
+ #expandedNodes = new Set<SDK.DOMModel.DOMNode>();
1025
+
362
1026
  // FIXME: this is not declarative because ElementsTreeOutline can
363
1027
  // change root node internally.
364
1028
  set rootDOMNode(node: SDK.DOMModel.DOMNode|null) {
1029
+ if (this.#view === DECLARATIVE_VIEW) {
1030
+ this.#rootDOMNode = node;
1031
+ if (node) {
1032
+ if (this.expandRoot || this.omitRootDOMNode) {
1033
+ this.#expandedNodes.add(node);
1034
+ }
1035
+ if (!node.children() && node.childNodeCount()) {
1036
+ void node.getChildNodes(() => {
1037
+ this.performUpdate();
1038
+ });
1039
+ }
1040
+ }
1041
+ this.performUpdate();
1042
+ return;
1043
+ }
365
1044
  this.performUpdate();
366
1045
  if (!this.#viewOutput.elementsTreeOutline) {
367
1046
  throw new Error('Unexpected: missing elementsTreeOutline');
@@ -370,6 +1049,9 @@ export class DOMTreeWidget extends UI.Widget.Widget {
370
1049
  this.performUpdate();
371
1050
  }
372
1051
  get rootDOMNode(): SDK.DOMModel.DOMNode|null {
1052
+ if (this.#view === DECLARATIVE_VIEW) {
1053
+ return this.#rootDOMNode;
1054
+ }
373
1055
  return this.#viewOutput.elementsTreeOutline?.rootDOMNode ?? null;
374
1056
  }
375
1057
 
@@ -428,6 +1110,14 @@ export class DOMTreeWidget extends UI.Widget.Widget {
428
1110
  }
429
1111
 
430
1112
  #currentHighlightedNode: SDK.DOMModel.DOMNode|null = null;
1113
+ #wiredDOMModels = new Set<SDK.DOMModel.DOMModel>();
1114
+ #topLayerShortcutsByDocument = new WeakMap<SDK.DOMModel.DOMDocument, SDK.DOMModel.DOMNodeShortcut[]>();
1115
+ #topLayerContainerExpandedByDocument = new WeakMap<SDK.DOMModel.DOMDocument, boolean>();
1116
+ #topLayerShortcutExpanded = new WeakMap<SDK.DOMModel.DOMNodeShortcut, boolean>();
1117
+ #selectedTopLayerShortcut: SDK.DOMModel.DOMNodeShortcut|null = null;
1118
+ #adoptedStyleSheetsExpandedByNode = new WeakMap<SDK.DOMModel.DOMNode, boolean>();
1119
+ #adoptedStyleSheetExpanded = new WeakMap<SDK.DOMModel.AdoptedStyleSheet, boolean>();
1120
+ #selectedAdoptedStyleSheet: SDK.DOMModel.AdoptedStyleSheet|null = null;
431
1121
 
432
1122
  #view: View;
433
1123
  #viewOutput: ViewOutput = {
@@ -457,6 +1147,23 @@ export class DOMTreeWidget extends UI.Widget.Widget {
457
1147
  }
458
1148
  }
459
1149
 
1150
+ #onDocumentUpdated(event: Common.EventTarget.EventTargetEvent<SDK.DOMModel.DOMModel>): void {
1151
+ const domModel = event.data;
1152
+ if (this.#view === DECLARATIVE_VIEW) {
1153
+ this.#selectedDOMNode = null;
1154
+ this.#expandedNodes.clear();
1155
+ this.#currentHighlightedNode = null;
1156
+ }
1157
+ if (domModel.existingDocument()) {
1158
+ this.rootDOMNode = domModel.existingDocument();
1159
+ }
1160
+ this.onDocumentUpdated(domModel);
1161
+ }
1162
+
1163
+ #onDOMNodeChanged(): void {
1164
+ this.requestUpdate();
1165
+ }
1166
+
460
1167
  #onShowHTMLCommentsChange(): void {
461
1168
  this.#showComments = this.#showHTMLCommentsSetting.get();
462
1169
  const selectedNode = this.selectedDOMNode();
@@ -486,10 +1193,33 @@ export class DOMTreeWidget extends UI.Widget.Widget {
486
1193
 
487
1194
  selectDOMNode(node: SDK.DOMModel.DOMNode|SDK.DOMModel.AdoptedStyleSheet|null, focus?: boolean): void {
488
1195
  if (node instanceof SDK.DOMModel.AdoptedStyleSheet) {
489
- this.#viewOutput?.elementsTreeOutline?.highlightAdoptedStyleSheet(node);
490
- } else {
491
- this.#viewOutput?.elementsTreeOutline?.selectDOMNode(node, focus);
1196
+ this.highlightAdoptedStyleSheet(node);
1197
+ return;
1198
+ }
1199
+ this.#selectedAdoptedStyleSheet = null;
1200
+ if (this.#view === DECLARATIVE_VIEW) {
1201
+ if (this.#selectedDOMNode === node) {
1202
+ return;
1203
+ }
1204
+ this.#selectedDOMNode = node;
1205
+ if (node) {
1206
+ for (let current: (SDK.DOMModel.DOMNode|null) = node.parentNode; current; current = current.parentNode) {
1207
+ this.#expandedNodes.add(current);
1208
+ if (!current.children() && current.childNodeCount()) {
1209
+ void current.getChildNodes(() => {
1210
+ this.performUpdate();
1211
+ });
1212
+ }
1213
+ }
1214
+ }
1215
+ this.#clearHighlightedNode();
1216
+ this.onSelectedNodeChanged(
1217
+ {data: {node, focus: Boolean(focus)}} as
1218
+ Common.EventTarget.EventTargetEvent<{node: SDK.DOMModel.DOMNode | null, focus: boolean}>);
1219
+ this.performUpdate();
1220
+ return;
492
1221
  }
1222
+ this.#viewOutput?.elementsTreeOutline?.selectDOMNode(node, focus);
493
1223
  }
494
1224
 
495
1225
  highlightNodeAttribute(node: SDK.DOMModel.DOMNode, attribute: string): void {
@@ -510,9 +1240,103 @@ export class DOMTreeWidget extends UI.Widget.Widget {
510
1240
  }
511
1241
 
512
1242
  selectedDOMNode(): SDK.DOMModel.DOMNode|null {
1243
+ if (this.#view === DECLARATIVE_VIEW) {
1244
+ return this.#selectedDOMNode;
1245
+ }
513
1246
  return this.#viewOutput.elementsTreeOutline?.selectedDOMNode() ?? null;
514
1247
  }
515
1248
 
1249
+ setNodeExpanded(node: SDK.DOMModel.DOMNode, expanded: boolean): void {
1250
+ if (this.#view === DECLARATIVE_VIEW) {
1251
+ if (expanded) {
1252
+ this.#expandedNodes.add(node);
1253
+ if (!node.children() && node.childNodeCount()) {
1254
+ void node.getChildNodes(() => {
1255
+ this.performUpdate();
1256
+ });
1257
+ }
1258
+ this.onElementExpanded();
1259
+ } else {
1260
+ this.#expandedNodes.delete(node);
1261
+ this.onElementCollapsed();
1262
+ }
1263
+ if (!this.#currentHighlightedNode || !isAncestorOf(node, this.#currentHighlightedNode)) {
1264
+ this.#clearHighlightedNode();
1265
+ }
1266
+ this.performUpdate();
1267
+ return;
1268
+ }
1269
+ const treeElement = this.#viewOutput.elementsTreeOutline?.findTreeElement(node);
1270
+ if (expanded) {
1271
+ treeElement?.expand();
1272
+ } else {
1273
+ treeElement?.collapse();
1274
+ }
1275
+ }
1276
+
1277
+ isNodeExpanded(node: SDK.DOMModel.DOMNode): boolean {
1278
+ if (this.#view === DECLARATIVE_VIEW) {
1279
+ return this.#expandedNodes.has(node);
1280
+ }
1281
+ return Boolean(this.#viewOutput.elementsTreeOutline?.findTreeElement(node)?.expanded);
1282
+ }
1283
+
1284
+ async expandRecursively(node: SDK.DOMModel.DOMNode, maxDepth = Number.MAX_VALUE): Promise<void> {
1285
+ if (this.#view === DECLARATIVE_VIEW) {
1286
+ await node.getSubtree(100, true);
1287
+ const expand = async(n: SDK.DOMModel.DOMNode, depth: number): Promise<void> => {
1288
+ if (depth > maxDepth) {
1289
+ return;
1290
+ }
1291
+ this.#expandedNodes.add(n);
1292
+ let children = n.children();
1293
+ if (!children && n.childNodeCount()) {
1294
+ children = await new Promise<SDK.DOMModel.DOMNode[]>(resolve => {
1295
+ void n.getChildNodes(() => resolve(n.children() ?? []));
1296
+ });
1297
+ }
1298
+ const pseudoElements = Array.from(n.pseudoElements().values()).flat();
1299
+ const allChildren = [...(children ?? []), ...pseudoElements];
1300
+ if (allChildren.length) {
1301
+ await Promise.all(allChildren.map(child => expand(child, depth + 1)));
1302
+ }
1303
+ };
1304
+ await expand(node, 0);
1305
+ this.performUpdate();
1306
+ return;
1307
+ }
1308
+ const treeElement = this.#viewOutput.elementsTreeOutline?.findTreeElement(node);
1309
+ if (treeElement) {
1310
+ await treeElement.expandRecursively();
1311
+ }
1312
+ }
1313
+
1314
+ collapseChildren(node: SDK.DOMModel.DOMNode): void {
1315
+ if (this.#view === DECLARATIVE_VIEW) {
1316
+ const collapse = (n: SDK.DOMModel.DOMNode): void => {
1317
+ const pseudoElements = Array.from(n.pseudoElements().values()).flat();
1318
+ const allChildren = [...(n.children() ?? []), ...pseudoElements];
1319
+ for (const child of allChildren) {
1320
+ this.#expandedNodes.delete(child);
1321
+ collapse(child);
1322
+ }
1323
+ };
1324
+ collapse(node);
1325
+ this.performUpdate();
1326
+ return;
1327
+ }
1328
+ const treeElement = this.#viewOutput.elementsTreeOutline?.findTreeElement(node);
1329
+ treeElement?.collapseChildren();
1330
+ }
1331
+
1332
+ showContextMenu(node: SDK.DOMModel.DOMNode, event: MouseEvent,
1333
+ widget?: ElementsTreeWidget): Promise<UI.ContextMenu.ContextMenu|undefined> {
1334
+ if (!this.#enableContextMenu) {
1335
+ return Promise.resolve(undefined);
1336
+ }
1337
+ return showContextMenu(this, node, event, widget);
1338
+ }
1339
+
516
1340
  /**
517
1341
  * FIXME: this is called to re-render everything from scratch, for
518
1342
  * example, if global settings changed. Instead, the setting values
@@ -533,9 +1357,58 @@ export class DOMTreeWidget extends UI.Widget.Widget {
533
1357
  return this.#viewOutput.elementsTreeOutline?.findTreeElement(node) || null;
534
1358
  }
535
1359
 
1360
+ #hoveredDOMNode: SDK.DOMModel.DOMNode|null = null;
1361
+ #searchMatchNode: SDK.DOMModel.DOMNode|null = null;
1362
+ #searchMatchQuery: string|null = null;
1363
+ #nodeToEdit: ({node: SDK.DOMModel.DOMNode}&InitialEditState)|null = null;
1364
+ #draggedNode: SDK.DOMModel.DOMNode|null = null;
1365
+ #draggedNodeWasExpanded = false;
1366
+ #dragOverNode: {node: SDK.DOMModel.DOMNode, isClosingTag: boolean}|null = null;
1367
+
1368
+ hoveredDOMNode(): SDK.DOMModel.DOMNode|null {
1369
+ if (this.#view === DECLARATIVE_VIEW) {
1370
+ return this.#hoveredDOMNode;
1371
+ }
1372
+ const hoveredElement = this.#viewOutput.elementsTreeOutline?.hoveredTreeElement;
1373
+ if (hoveredElement instanceof ElementsTreeElement) {
1374
+ return hoveredElement.node();
1375
+ }
1376
+ return null;
1377
+ }
1378
+
1379
+ searchMatchNode(): SDK.DOMModel.DOMNode|null {
1380
+ return this.#searchMatchNode;
1381
+ }
1382
+
1383
+ searchMatchQuery(): string|null {
1384
+ return this.#searchMatchQuery;
1385
+ }
1386
+
1387
+ setHoveredNode(node: SDK.DOMModel.DOMNode|null, showInfo = true): void {
1388
+ if (this.#hoveredDOMNode === node) {
1389
+ return;
1390
+ }
1391
+ this.#hoveredDOMNode = node;
1392
+ if (node) {
1393
+ const treeElement = this.treeElementForNode(node);
1394
+ const selectorList = treeElement?.isDisplayContents() ? '*' : undefined;
1395
+ node.domModel().overlayModel().highlightInOverlay({node, selectorList}, 'all', showInfo);
1396
+ } else {
1397
+ SDK.OverlayModel.OverlayModel.hideDOMNodeHighlight(SDK.TargetManager.TargetManager.instance());
1398
+ }
1399
+ if (this.#view === DECLARATIVE_VIEW) {
1400
+ this.performUpdate();
1401
+ } else {
1402
+ const treeElement = node ? this.treeElementForNode(node) : null;
1403
+ this.#viewOutput.elementsTreeOutline?.setHoverEffect(treeElement);
1404
+ }
1405
+ }
1406
+
536
1407
  override performUpdate(): void {
537
1408
  const firstRender = !this.#viewOutput.elementsTreeOutline;
538
1409
  this.#view({
1410
+ domTreeWidget: this,
1411
+ rootDOMNode: this.#rootDOMNode,
539
1412
  omitRootDOMNode: this.omitRootDOMNode,
540
1413
  selectEnabled: this.selectEnabled,
541
1414
  hideGutter: this.hideGutter,
@@ -554,6 +1427,9 @@ export class DOMTreeWidget extends UI.Widget.Widget {
554
1427
  deindentSingleNode: this.deindentSingleNode,
555
1428
 
556
1429
  currentHighlightedNode: this.#currentHighlightedNode,
1430
+ hoveredNode: this.#hoveredDOMNode,
1431
+ searchMatchNode: this.#searchMatchNode,
1432
+ searchMatchQuery: this.#searchMatchQuery,
557
1433
  selectedNode: this.selectedDOMNode(),
558
1434
  onElementsTreeUpdated: this.onElementsTreeUpdated.bind(this),
559
1435
  onSelectedNodeChanged: event => {
@@ -566,8 +1442,117 @@ export class DOMTreeWidget extends UI.Widget.Widget {
566
1442
  onElementExpanded: () => {
567
1443
  this.#clearHighlightedNode();
568
1444
  },
1445
+ onHoverNode: (node: SDK.DOMModel.DOMNode, showInfo?: boolean) => {
1446
+ this.setHoveredNode(node, showInfo);
1447
+ },
1448
+ onLeave: () => {
1449
+ this.setHoveredNode(null);
1450
+ },
1451
+ onSelect: (node: SDK.DOMModel.DOMNode, selectedByUser?: boolean) => {
1452
+ this.selectDOMNode(node, selectedByUser);
1453
+ },
1454
+ onExpand: (node: SDK.DOMModel.DOMNode, expanded: boolean) => {
1455
+ this.setNodeExpanded(node, expanded);
1456
+ },
1457
+ onContextMenu: (node: SDK.DOMModel.DOMNode, event: MouseEvent, widget?: ElementsTreeWidget) => {
1458
+ void this.showContextMenu(node, event, widget);
1459
+ },
1460
+ onToggleHideElement: (node: SDK.DOMModel.DOMNode) => {
1461
+ this.toggleHideElement(node);
1462
+ },
1463
+ onKeyDown: (event: KeyboardEvent) => {
1464
+ this.onKeyDown(event);
1465
+ },
1466
+ isToggledToHidden: (node: SDK.DOMModel.DOMNode) => {
1467
+ return this.isToggledToHidden(node);
1468
+ },
1469
+ onDuplicateNode: (node: SDK.DOMModel.DOMNode) => {
1470
+ this.duplicateNode(node);
1471
+ },
1472
+ isNodeExpanded: (node: SDK.DOMModel.DOMNode) => {
1473
+ return this.isNodeExpanded(node);
1474
+ },
1475
+ isNodeInClipboard: (node: SDK.DOMModel.DOMNode) => {
1476
+ return this.isNodeInClipboard(node);
1477
+ },
1478
+ onCopyOrCut: (isCut: boolean, event: Event) => {
1479
+ this.onCopyOrCut(isCut, event);
1480
+ },
1481
+ onPaste: (event: Event) => {
1482
+ this.onPaste(event);
1483
+ },
1484
+ onSelectNodeAfterEdit:
1485
+ (wasExpanded: boolean, error: string|null, newNode: SDK.DOMModel.DOMNode|null, moveDirection?: string) => {
1486
+ this.selectNodeAfterEdit(wasExpanded, error, newNode, moveDirection);
1487
+ },
1488
+ nodeToEdit: this.#nodeToEdit,
1489
+ onInitialEditCompleted: () => {
1490
+ this.#nodeToEdit = null;
1491
+ },
1492
+ dragOverNode: this.#dragOverNode,
1493
+ isValidDragSource: (node: SDK.DOMModel.DOMNode) => this.isValidDragSource(node),
1494
+ onDragStart: (node: SDK.DOMModel.DOMNode, event: DragEvent, textContent?: string) =>
1495
+ this.onDragStart(node, event, textContent),
1496
+ onDragOver: (node: SDK.DOMModel.DOMNode, isClosingTag: boolean, event: DragEvent) =>
1497
+ this.onDragOver(node, isClosingTag, event),
1498
+ onDragLeave: (event: DragEvent) => this.onDragLeave(event),
1499
+ onDrop: (node: SDK.DOMModel.DOMNode, isClosingTag: boolean, event: DragEvent) =>
1500
+ this.onDrop(node, isClosingTag, event),
1501
+ onDragEnd: (event: DragEvent) => this.onDragEnd(event),
1502
+ getTopLayerShortcuts: (doc: SDK.DOMModel.DOMDocument) => this.topLayerShortcuts(doc),
1503
+ isTopLayerExpanded: (doc: SDK.DOMModel.DOMDocument) => this.isTopLayerExpanded(doc),
1504
+ onToggleTopLayerExpanded: (doc: SDK.DOMModel.DOMDocument, expanded: boolean) =>
1505
+ this.setTopLayerExpanded(doc, expanded),
1506
+ onSelectTopLayerContainer: (_doc: SDK.DOMModel.DOMDocument) => {
1507
+ this.#selectedTopLayerShortcut = null;
1508
+ this.selectDOMNode(null);
1509
+ this.performUpdate();
1510
+ },
1511
+ isTopLayerShortcutExpanded: (shortcut: SDK.DOMModel.DOMNodeShortcut) => this.isTopLayerShortcutExpanded(shortcut),
1512
+ onToggleTopLayerShortcutExpanded: (shortcut: SDK.DOMModel.DOMNodeShortcut, expanded: boolean) =>
1513
+ this.setTopLayerShortcutExpanded(shortcut, expanded),
1514
+ selectedTopLayerShortcut: this.#selectedTopLayerShortcut,
1515
+ onSelectTopLayerShortcut: (shortcut: SDK.DOMModel.DOMNodeShortcut) => {
1516
+ this.#selectedTopLayerShortcut = shortcut;
1517
+ this.selectDOMNode(null);
1518
+ shortcut.deferredNode.highlight();
1519
+ shortcut.deferredNode.resolve(node => {
1520
+ if (node) {
1521
+ this.selectDOMNode(node, true);
1522
+ }
1523
+ });
1524
+ this.performUpdate();
1525
+ },
1526
+ onRevealTopLayerShortcut: (shortcut: SDK.DOMModel.DOMNodeShortcut) => {
1527
+ shortcut.deferredNode.resolve(node => {
1528
+ if (node) {
1529
+ this.selectDOMNode(node, true);
1530
+ node.highlight();
1531
+ }
1532
+ });
1533
+ },
1534
+ isAdoptedStyleSheetsExpanded: (node: SDK.DOMModel.DOMNode) => this.isAdoptedStyleSheetsExpanded(node),
1535
+ onToggleAdoptedStyleSheetsExpanded: (node: SDK.DOMModel.DOMNode, expanded: boolean) =>
1536
+ this.setAdoptedStyleSheetsExpanded(node, expanded),
1537
+ onSelectAdoptedStyleSheets: (_node: SDK.DOMModel.DOMNode) => {
1538
+ this.#selectedAdoptedStyleSheet = null;
1539
+ this.selectDOMNode(null);
1540
+ this.performUpdate();
1541
+ },
1542
+ isAdoptedStyleSheetExpanded: (sheet: SDK.DOMModel.AdoptedStyleSheet) => this.isAdoptedStyleSheetExpanded(sheet),
1543
+ onToggleAdoptedStyleSheetExpanded: (sheet: SDK.DOMModel.AdoptedStyleSheet, expanded: boolean) =>
1544
+ this.setAdoptedStyleSheetExpanded(sheet, expanded),
1545
+ selectedAdoptedStyleSheet: this.#selectedAdoptedStyleSheet,
1546
+ onSelectAdoptedStyleSheet: (sheet: SDK.DOMModel.AdoptedStyleSheet) => {
1547
+ this.#selectedAdoptedStyleSheet = sheet;
1548
+ this.selectDOMNode(null);
1549
+ this.performUpdate();
1550
+ },
569
1551
  },
570
1552
  this.#viewOutput, this.contentElement);
1553
+ if (this.#viewOutput.elementsTreeOutline) {
1554
+ this.#viewOutput.elementsTreeOutline.domTreeWidget = this;
1555
+ }
571
1556
  if (firstRender && this.#viewOutput.elementsTreeOutline) {
572
1557
  this.#viewOutput.elementsTreeOutline.addEventListener(ElementsTreeOutline.Events.ShowAllRows, () => {
573
1558
  this.maxRows = undefined;
@@ -576,6 +1561,37 @@ export class DOMTreeWidget extends UI.Widget.Widget {
576
1561
  }
577
1562
 
578
1563
  modelAdded(domModel: SDK.DOMModel.DOMModel): void {
1564
+ if (this.#view === DECLARATIVE_VIEW) {
1565
+ if (!this.#wiredDOMModels.has(domModel)) {
1566
+ this.#wiredDOMModels.add(domModel);
1567
+ domModel.addEventListener(SDK.DOMModel.Events.DocumentUpdated, this.#onDocumentUpdated, this);
1568
+ domModel.addEventListener(SDK.DOMModel.Events.NodeInserted, this.#onDOMNodeChanged, this);
1569
+ domModel.addEventListener(SDK.DOMModel.Events.NodeRemoved, this.#onDOMNodeChanged, this);
1570
+ domModel.addEventListener(SDK.DOMModel.Events.AttrModified, this.#onDOMNodeChanged, this);
1571
+ domModel.addEventListener(SDK.DOMModel.Events.AttrRemoved, this.#onDOMNodeChanged, this);
1572
+ domModel.addEventListener(SDK.DOMModel.Events.CharacterDataModified, this.#onDOMNodeChanged, this);
1573
+ domModel.addEventListener(SDK.DOMModel.Events.ChildNodeCountUpdated, this.#onDOMNodeChanged, this);
1574
+ domModel.addEventListener(SDK.DOMModel.Events.MarkersChanged, this.#onDOMNodeChanged, this);
1575
+ domModel.addEventListener(SDK.DOMModel.Events.AdoptedStyleSheetsModified, this.#onDOMNodeChanged, this);
1576
+ domModel.addEventListener(SDK.DOMModel.Events.TopLayerElementsChanged, this.#onTopLayerElementsChanged, this);
1577
+ }
1578
+ if (this.isShowing() && !domModel.parentModel() &&
1579
+ (!this.rootDOMNode || this.rootDOMNode.domModel() !== domModel)) {
1580
+ if (domModel.existingDocument()) {
1581
+ this.rootDOMNode = domModel.existingDocument();
1582
+ this.onDocumentUpdated(domModel);
1583
+ } else {
1584
+ void domModel.requestDocument().then(document => {
1585
+ if (document && this.isShowing()) {
1586
+ this.rootDOMNode = document;
1587
+ this.onDocumentUpdated(domModel);
1588
+ }
1589
+ });
1590
+ }
1591
+ }
1592
+ this.performUpdate();
1593
+ return;
1594
+ }
579
1595
  this.performUpdate();
580
1596
  if (!this.#viewOutput.elementsTreeOutline) {
581
1597
  throw new Error('Unexpected: missing elementsTreeOutline');
@@ -585,6 +1601,24 @@ export class DOMTreeWidget extends UI.Widget.Widget {
585
1601
  }
586
1602
 
587
1603
  modelRemoved(domModel: SDK.DOMModel.DOMModel): void {
1604
+ if (this.#view === DECLARATIVE_VIEW) {
1605
+ if (this.#wiredDOMModels.has(domModel)) {
1606
+ this.#wiredDOMModels.delete(domModel);
1607
+ domModel.removeEventListener(SDK.DOMModel.Events.DocumentUpdated, this.#onDocumentUpdated, this);
1608
+ domModel.removeEventListener(SDK.DOMModel.Events.NodeInserted, this.#onDOMNodeChanged, this);
1609
+ domModel.removeEventListener(SDK.DOMModel.Events.NodeRemoved, this.#onDOMNodeChanged, this);
1610
+ domModel.removeEventListener(SDK.DOMModel.Events.AttrModified, this.#onDOMNodeChanged, this);
1611
+ domModel.removeEventListener(SDK.DOMModel.Events.AttrRemoved, this.#onDOMNodeChanged, this);
1612
+ domModel.removeEventListener(SDK.DOMModel.Events.CharacterDataModified, this.#onDOMNodeChanged, this);
1613
+ domModel.removeEventListener(SDK.DOMModel.Events.ChildNodeCountUpdated, this.#onDOMNodeChanged, this);
1614
+ domModel.removeEventListener(SDK.DOMModel.Events.MarkersChanged, this.#onDOMNodeChanged, this);
1615
+ domModel.removeEventListener(SDK.DOMModel.Events.AdoptedStyleSheetsModified, this.#onDOMNodeChanged, this);
1616
+ domModel.removeEventListener(SDK.DOMModel.Events.TopLayerElementsChanged, this.#onTopLayerElementsChanged,
1617
+ this);
1618
+ }
1619
+ this.performUpdate();
1620
+ return;
1621
+ }
588
1622
  this.#viewOutput.elementsTreeOutline?.unwireFromDOMModel(domModel);
589
1623
  this.performUpdate();
590
1624
  }
@@ -614,47 +1648,647 @@ export class DOMTreeWidget extends UI.Widget.Widget {
614
1648
  }
615
1649
 
616
1650
  highlightMatch(node: SDK.DOMModel.DOMNode, query?: string): void {
617
- const treeElement = this.#viewOutput.elementsTreeOutline?.findTreeElement(node);
618
- if (!treeElement) {
1651
+ this.#searchMatchNode = node;
1652
+ this.#searchMatchQuery = query ?? null;
1653
+ if (this.selectedDOMNode() !== node) {
1654
+ this.selectDOMNode(node, /* focus= */ false);
1655
+ } else {
1656
+ this.performUpdate();
1657
+ }
1658
+ }
1659
+
1660
+ hideMatchHighlights(node: SDK.DOMModel.DOMNode): void {
1661
+ if (this.#searchMatchNode === node) {
1662
+ this.#searchMatchNode = null;
1663
+ this.#searchMatchQuery = null;
1664
+ this.performUpdate();
1665
+ }
1666
+ }
1667
+
1668
+ toggleHideElement(node: SDK.DOMModel.DOMNode): void {
1669
+ void node.toggleHideElement();
1670
+ }
1671
+
1672
+ async removeNode(node: SDK.DOMModel.DOMNode): Promise<void> {
1673
+ if (this.isToggledToHidden(node)) {
1674
+ // Unhide the node before removing. This avoids inconsistent state if the node is restored via undo.
1675
+ this.toggleHideElement(node);
1676
+ }
1677
+ if (node.pseudoType()) {
1678
+ return;
1679
+ }
1680
+ if (!node.parentNode || node.parentNode.nodeType() === Node.DOCUMENT_NODE) {
1681
+ return;
1682
+ }
1683
+ void node.removeNode();
1684
+ }
1685
+
1686
+ isToggledToHidden(node: SDK.DOMModel.DOMNode): boolean {
1687
+ return node.isToggledToHidden();
1688
+ }
1689
+
1690
+ #multilineEditing: MultilineEditorController|null = null;
1691
+
1692
+ setMultilineEditing(multilineEditing: MultilineEditorController|null): void {
1693
+ this.#multilineEditing = multilineEditing;
1694
+ }
1695
+
1696
+ multilineEditing(): MultilineEditorController|null {
1697
+ return this.#multilineEditing;
1698
+ }
1699
+
1700
+ runPendingUpdates(): void {
1701
+ this.#viewOutput.elementsTreeOutline?.runPendingUpdates();
1702
+ this.performUpdate();
1703
+ }
1704
+
1705
+ override onResize(): void {
1706
+ super.onResize();
1707
+ if (this.#multilineEditing) {
1708
+ this.#multilineEditing.resize();
1709
+ }
1710
+ }
1711
+
1712
+ override willHide(): void {
1713
+ super.willHide();
1714
+ if (this.#multilineEditing) {
1715
+ this.#multilineEditing.cancel();
1716
+ }
1717
+ }
1718
+
1719
+ toggleEditAsHTML(node: SDK.DOMModel.DOMNode, startEditing?: boolean, callback?: (() => void)): void {
1720
+ if (node.pseudoType() || node.isShadowRoot() || node.nodeType() === Node.DOCUMENT_NODE) {
1721
+ return;
1722
+ }
1723
+ const parentNode = node.parentNode;
1724
+ const index = node.index;
1725
+ const wasExpanded = this.isNodeExpanded(node);
1726
+
1727
+ const editingFinished = (success: boolean): void => {
1728
+ if (callback) {
1729
+ callback();
1730
+ }
1731
+ if (!success) {
1732
+ return;
1733
+ }
1734
+
1735
+ Badges.UserBadges.instance().recordAction(Badges.BadgeAction.DOM_ELEMENT_OR_ATTRIBUTE_EDITED);
1736
+
1737
+ this.runPendingUpdates();
1738
+
1739
+ if (index === undefined) {
1740
+ return;
1741
+ }
1742
+
1743
+ const children = parentNode?.children();
1744
+ const newNode = children ? children[index] || parentNode : parentNode;
1745
+ if (!newNode) {
1746
+ return;
1747
+ }
1748
+
1749
+ this.selectDOMNode(newNode, true);
1750
+
1751
+ if (wasExpanded) {
1752
+ this.setNodeExpanded(newNode, true);
1753
+ }
1754
+ };
1755
+
1756
+ if (this.#multilineEditing) {
1757
+ this.#multilineEditing.commit();
1758
+ return;
1759
+ }
1760
+
1761
+ if (startEditing === false) {
1762
+ return;
1763
+ }
1764
+
1765
+ this.#nodeToEdit = {
1766
+ node,
1767
+ isEditAsHTML: true,
1768
+ editAsHTMLCallback: editingFinished,
1769
+ };
1770
+ this.performUpdate();
1771
+ }
1772
+
1773
+ duplicateNode(node: SDK.DOMModel.DOMNode): void {
1774
+ node.duplicate();
1775
+ }
1776
+
1777
+ nodeBeingDragged(): SDK.DOMModel.DOMNode|null {
1778
+ return this.#draggedNode;
1779
+ }
1780
+
1781
+ dragOverNode(): {node: SDK.DOMModel.DOMNode, isClosingTag: boolean}|null {
1782
+ return this.#dragOverNode;
1783
+ }
1784
+
1785
+ isValidDragSource(node: SDK.DOMModel.DOMNode): boolean {
1786
+ if (this.disableEdits) {
1787
+ return false;
1788
+ }
1789
+ if (!node.parentNode || node.parentNode.nodeType() !== Node.ELEMENT_NODE) {
1790
+ return false;
1791
+ }
1792
+ const nodeName = node.nodeName();
1793
+ if (nodeName === 'BODY' || nodeName === 'HEAD') {
1794
+ return false;
1795
+ }
1796
+ return true;
1797
+ }
1798
+
1799
+ isValidDragTarget(targetNode: SDK.DOMModel.DOMNode): boolean {
1800
+ if (!this.#draggedNode) {
1801
+ return false;
1802
+ }
1803
+ if (!targetNode.parentNode || targetNode.parentNode.nodeType() !== Node.ELEMENT_NODE) {
1804
+ return false;
1805
+ }
1806
+ let current: SDK.DOMModel.DOMNode|null = targetNode;
1807
+ while (current) {
1808
+ if (current === this.#draggedNode) {
1809
+ return false;
1810
+ }
1811
+ current = current.parentNode;
1812
+ }
1813
+ return true;
1814
+ }
1815
+
1816
+ onDragStart(node: SDK.DOMModel.DOMNode, event: DragEvent, textContent?: string): boolean {
1817
+ const target = (event.target as Node | null);
1818
+ if (!target) {
1819
+ return false;
1820
+ }
1821
+ const selection = target.getComponentSelection();
1822
+ if (selection && selection.type === 'Range' && !selection.isCollapsed && selection.toString().length > 0 &&
1823
+ target.hasSelection()) {
1824
+ return false;
1825
+ }
1826
+ if (target.nodeName === 'A') {
1827
+ return false;
1828
+ }
1829
+ if (!this.isValidDragSource(node)) {
1830
+ return false;
1831
+ }
1832
+ this.#draggedNode = node;
1833
+ this.#draggedNodeWasExpanded = this.isNodeExpanded(node);
1834
+
1835
+ if (event.dataTransfer) {
1836
+ const text = textContent ?? node.nodeName().toLowerCase();
1837
+ event.dataTransfer.setData('text/plain', text.replace(/\u200b/g, ''));
1838
+ event.dataTransfer.effectAllowed = 'copyMove';
1839
+ }
1840
+
1841
+ SDK.OverlayModel.OverlayModel.hideDOMNodeHighlight(SDK.TargetManager.TargetManager.instance());
1842
+ return true;
1843
+ }
1844
+
1845
+ onDragOver(node: SDK.DOMModel.DOMNode, isClosingTag: boolean, event: DragEvent): boolean {
1846
+ if (!this.#draggedNode) {
1847
+ return false;
1848
+ }
1849
+ if (!this.isValidDragTarget(node)) {
1850
+ return false;
1851
+ }
1852
+ event.preventDefault();
1853
+ if (event.dataTransfer) {
1854
+ event.dataTransfer.dropEffect = 'move';
1855
+ }
1856
+ if (this.#dragOverNode?.node !== node || this.#dragOverNode?.isClosingTag !== isClosingTag) {
1857
+ this.#dragOverNode = {node, isClosingTag};
1858
+ if (this.#view === DECLARATIVE_VIEW) {
1859
+ this.performUpdate();
1860
+ }
1861
+ }
1862
+ return true;
1863
+ }
1864
+
1865
+ onDragLeave(event: DragEvent): void {
1866
+ event.preventDefault();
1867
+ if (this.#dragOverNode) {
1868
+ this.#dragOverNode = null;
1869
+ if (this.#view === DECLARATIVE_VIEW) {
1870
+ this.performUpdate();
1871
+ }
1872
+ }
1873
+ }
1874
+
1875
+ onDrop(node: SDK.DOMModel.DOMNode, isClosingTag: boolean, event: DragEvent): void {
1876
+ event.preventDefault();
1877
+ if (!this.#draggedNode) {
1878
+ return;
1879
+ }
1880
+ this.moveNode(this.#draggedNode, node, isClosingTag);
1881
+ this.#draggedNode = null;
1882
+ this.#dragOverNode = null;
1883
+ if (this.#view === DECLARATIVE_VIEW) {
1884
+ this.performUpdate();
1885
+ }
1886
+ }
1887
+
1888
+ onDragEnd(event: DragEvent): void {
1889
+ event.preventDefault();
1890
+ this.#draggedNode = null;
1891
+ this.#dragOverNode = null;
1892
+ if (this.#view === DECLARATIVE_VIEW) {
1893
+ this.performUpdate();
1894
+ }
1895
+ }
1896
+
1897
+ moveNode(draggedNode: SDK.DOMModel.DOMNode, targetNode: SDK.DOMModel.DOMNode, isClosingTag: boolean): void {
1898
+ let parentNode: SDK.DOMModel.DOMNode|null;
1899
+ let anchorNode: SDK.DOMModel.DOMNode|null;
1900
+ if (isClosingTag) {
1901
+ // Drop onto closing tag -> insert as last child.
1902
+ parentNode = targetNode;
1903
+ anchorNode = null;
1904
+ } else {
1905
+ parentNode = targetNode.parentNode;
1906
+ anchorNode = targetNode;
1907
+ }
1908
+ if (!parentNode) {
1909
+ return;
1910
+ }
1911
+ const wasExpanded = this.#draggedNodeWasExpanded;
1912
+ draggedNode.moveTo(parentNode, anchorNode,
1913
+ (error, newNode) => this.selectNodeAfterEdit(wasExpanded, error, newNode));
1914
+ }
1915
+
1916
+ selectNodeAfterEdit(wasExpanded: boolean, error: string|null, newNode: SDK.DOMModel.DOMNode|null,
1917
+ moveDirection?: string): void {
1918
+ if (error || !newNode) {
1919
+ return;
1920
+ }
1921
+ this.selectDOMNode(newNode, /* selectedByUser= */ true);
1922
+ if (wasExpanded) {
1923
+ this.setNodeExpanded(newNode, true);
1924
+ }
1925
+ if (moveDirection) {
1926
+ if (newNode.nodeType() === Node.PROCESSING_INSTRUCTION_NODE) {
1927
+ this.#nodeToEdit = {node: newNode, isProcessingInstruction: true};
1928
+ } else if (moveDirection !== 'forward') {
1929
+ this.#nodeToEdit = {node: newNode, isNewAttribute: true};
1930
+ } else {
1931
+ const attributes = newNode.attributes();
1932
+ if (attributes.length > 0) {
1933
+ this.#nodeToEdit = {node: newNode, attributeName: attributes[0].name};
1934
+ } else {
1935
+ this.#nodeToEdit = {node: newNode, isNewAttribute: true};
1936
+ }
1937
+ }
1938
+ this.performUpdate();
1939
+ }
1940
+ }
1941
+
1942
+ topLayerShortcuts(document: SDK.DOMModel.DOMDocument): SDK.DOMModel.DOMNodeShortcut[] {
1943
+ return this.#topLayerShortcutsByDocument.get(document) ?? [];
1944
+ }
1945
+
1946
+ isTopLayerExpanded(document: SDK.DOMModel.DOMDocument): boolean {
1947
+ return this.#topLayerContainerExpandedByDocument.get(document) ?? false;
1948
+ }
1949
+
1950
+ setTopLayerExpanded(document: SDK.DOMModel.DOMDocument, expanded: boolean): void {
1951
+ this.#topLayerContainerExpandedByDocument.set(document, expanded);
1952
+ this.performUpdate();
1953
+ }
1954
+
1955
+ isTopLayerShortcutExpanded(shortcut: SDK.DOMModel.DOMNodeShortcut): boolean {
1956
+ return this.#topLayerShortcutExpanded.get(shortcut) ?? false;
1957
+ }
1958
+
1959
+ setTopLayerShortcutExpanded(shortcut: SDK.DOMModel.DOMNodeShortcut, expanded: boolean): void {
1960
+ this.#topLayerShortcutExpanded.set(shortcut, expanded);
1961
+ this.performUpdate();
1962
+ }
1963
+
1964
+ #onTopLayerElementsChanged(
1965
+ event: Common.EventTarget.EventTargetEvent<SDK.DOMModel.EventTypes['TopLayerElementsChanged']>): void {
1966
+ this.#topLayerShortcutsByDocument.set(event.data.document, event.data.documentShortcuts);
1967
+ this.performUpdate();
1968
+ }
1969
+
1970
+ revealInTopLayer(node: SDK.DOMModel.DOMNode): void {
1971
+ const document = node.ownerDocument;
1972
+ if (!document) {
1973
+ return;
1974
+ }
1975
+ if (this.#viewOutput.elementsTreeOutline) {
1976
+ this.#viewOutput.elementsTreeOutline.revealInTopLayer(node);
1977
+ return;
1978
+ }
1979
+ const shortcuts = this.topLayerShortcuts(document);
1980
+ const findShortcut = (list: SDK.DOMModel.DOMNodeShortcut[]): SDK.DOMModel.DOMNodeShortcut|null => {
1981
+ for (const sc of list) {
1982
+ if (sc.deferredNode.backendNodeId() === node.backendNodeId()) {
1983
+ return sc;
1984
+ }
1985
+ const found = findShortcut(sc.childShortcuts);
1986
+ if (found) {
1987
+ return found;
1988
+ }
1989
+ }
1990
+ return null;
1991
+ };
1992
+ const shortcut = findShortcut(shortcuts);
1993
+ if (shortcut) {
1994
+ this.#topLayerContainerExpandedByDocument.set(document, true);
1995
+ this.#selectedTopLayerShortcut = shortcut;
1996
+ this.performUpdate();
1997
+ }
1998
+ }
1999
+
2000
+ isAdoptedStyleSheetsExpanded(node: SDK.DOMModel.DOMNode): boolean {
2001
+ return this.#adoptedStyleSheetsExpandedByNode.get(node) ?? false;
2002
+ }
2003
+
2004
+ setAdoptedStyleSheetsExpanded(node: SDK.DOMModel.DOMNode, expanded: boolean): void {
2005
+ this.#adoptedStyleSheetsExpandedByNode.set(node, expanded);
2006
+ this.performUpdate();
2007
+ }
2008
+
2009
+ isAdoptedStyleSheetExpanded(sheet: SDK.DOMModel.AdoptedStyleSheet): boolean {
2010
+ return this.#adoptedStyleSheetExpanded.get(sheet) ?? false;
2011
+ }
2012
+
2013
+ setAdoptedStyleSheetExpanded(sheet: SDK.DOMModel.AdoptedStyleSheet, expanded: boolean): void {
2014
+ this.#adoptedStyleSheetExpanded.set(sheet, expanded);
2015
+ this.performUpdate();
2016
+ }
2017
+
2018
+ highlightAdoptedStyleSheet(adoptedStyleSheet: SDK.DOMModel.AdoptedStyleSheet): void {
2019
+ if (this.#viewOutput.elementsTreeOutline) {
2020
+ this.#viewOutput.elementsTreeOutline.highlightAdoptedStyleSheet(adoptedStyleSheet);
2021
+ return;
2022
+ }
2023
+ const parent = adoptedStyleSheet.parent;
2024
+ if (!parent) {
2025
+ return;
2026
+ }
2027
+ for (let current: (SDK.DOMModel.DOMNode|null) = parent; current; current = current.parentNode) {
2028
+ this.#expandedNodes.add(current);
2029
+ }
2030
+ this.setAdoptedStyleSheetsExpanded(parent, true);
2031
+ this.#selectedAdoptedStyleSheet = adoptedStyleSheet;
2032
+ this.selectDOMNode(null);
2033
+ this.performUpdate();
2034
+ }
2035
+
2036
+ startEditing(node: SDK.DOMModel.DOMNode): void {
2037
+ if (UI.UIUtils.isEditing()) {
2038
+ return;
2039
+ }
2040
+ const nodeType = node.nodeType();
2041
+ if (nodeType === Node.ELEMENT_NODE) {
2042
+ if (node.isShadowRoot() || node.ancestorUserAgentShadowRoot()) {
2043
+ return;
2044
+ }
2045
+ const attributes = node.attributes();
2046
+ if (attributes.length > 0) {
2047
+ this.#nodeToEdit = {node, attributeName: attributes[0].name};
2048
+ } else {
2049
+ this.#nodeToEdit = {node, isNewAttribute: true};
2050
+ }
2051
+ this.performUpdate();
2052
+ } else if (nodeType === Node.TEXT_NODE) {
2053
+ this.#nodeToEdit = {node, isTextNode: true};
2054
+ this.performUpdate();
2055
+ } else if (nodeType === Node.PROCESSING_INSTRUCTION_NODE) {
2056
+ this.#nodeToEdit = {node, isProcessingInstruction: true};
2057
+ this.performUpdate();
2058
+ }
2059
+ }
2060
+
2061
+ onKeyDown(event: KeyboardEvent): boolean {
2062
+ if (UI.UIUtils.isEditing()) {
2063
+ return false;
2064
+ }
2065
+ const node = this.selectedDOMNode();
2066
+ if (!node) {
2067
+ return false;
2068
+ }
2069
+
2070
+ if (UI.KeyboardShortcut.KeyboardShortcut.eventHasCtrlEquivalentKey(event) && node.parentNode) {
2071
+ const wasExpanded = this.isNodeExpanded(node);
2072
+ if (event.key === 'ArrowUp' && node.previousSibling) {
2073
+ node.moveTo(node.parentNode, node.previousSibling, (error, newNode) => {
2074
+ this.selectNodeAfterEdit(wasExpanded, error, newNode);
2075
+ });
2076
+ event.consume(true);
2077
+ return true;
2078
+ }
2079
+ if (event.key === 'ArrowDown' && node.nextSibling) {
2080
+ node.moveTo(node.parentNode, node.nextSibling.nextSibling, (error, newNode) => {
2081
+ this.selectNodeAfterEdit(wasExpanded, error, newNode);
2082
+ });
2083
+ event.consume(true);
2084
+ return true;
2085
+ }
2086
+ }
2087
+
2088
+ if (event.key === 'Enter') {
2089
+ this.startEditing(node);
2090
+ event.consume(true);
2091
+ return true;
2092
+ }
2093
+
2094
+ if (event.key === 'F2') {
2095
+ this.toggleEditAsHTML(node);
2096
+ event.consume(true);
2097
+ return true;
2098
+ }
2099
+
2100
+ if (event.key === 'Delete' || event.key === 'Backspace') {
2101
+ void this.removeNode(node);
2102
+ event.consume(true);
2103
+ return true;
2104
+ }
2105
+
2106
+ if (event.key === 'h' || event.key === 'H') {
2107
+ this.toggleHideElement(node);
2108
+ event.consume(true);
2109
+ return true;
2110
+ }
2111
+
2112
+ return false;
2113
+ }
2114
+
2115
+ #clipboardData: ClipboardData|null = null;
2116
+
2117
+ clipboardData(): ClipboardData|null {
2118
+ return this.#clipboardData;
2119
+ }
2120
+
2121
+ setClipboardData(data: ClipboardData|null): void {
2122
+ if (this.#clipboardData) {
2123
+ const prevTreeElement = this.#viewOutput.elementsTreeOutline?.findTreeElement(this.#clipboardData.node);
2124
+ if (prevTreeElement) {
2125
+ prevTreeElement.setInClipboard(false);
2126
+ }
2127
+ this.#clipboardData = null;
2128
+ }
2129
+
2130
+ if (data) {
2131
+ const treeElement = this.#viewOutput.elementsTreeOutline?.findTreeElement(data.node);
2132
+ if (treeElement) {
2133
+ treeElement.setInClipboard(true);
2134
+ }
2135
+ this.#clipboardData = data;
2136
+ }
2137
+ this.requestUpdate();
2138
+ }
2139
+
2140
+ resetClipboardIfNeeded(removedNode: SDK.DOMModel.DOMNode): void {
2141
+ if (this.#clipboardData?.node === removedNode) {
2142
+ this.setClipboardData(null);
2143
+ }
2144
+ }
2145
+
2146
+ isNodeInClipboard(node: SDK.DOMModel.DOMNode): boolean {
2147
+ return Boolean(this.#clipboardData?.isCut && this.#clipboardData?.node === node);
2148
+ }
2149
+
2150
+ async copyOuterHTML(node: SDK.DOMModel.DOMNode, includeShadowRoots = false): Promise<void> {
2151
+ const outerHTML = await node.getOuterHTML(includeShadowRoots);
2152
+ if (outerHTML !== null) {
2153
+ UI.UIUtils.copyTextToClipboard(outerHTML);
2154
+ }
2155
+ }
2156
+
2157
+ copyCSSPath(node: SDK.DOMModel.DOMNode): void {
2158
+ Host.InspectorFrontendHost.InspectorFrontendHostInstance.copyText(cssPath(node, true));
2159
+ }
2160
+
2161
+ copyJSPath(node: SDK.DOMModel.DOMNode): void {
2162
+ Host.InspectorFrontendHost.InspectorFrontendHostInstance.copyText(jsPath(node, true));
2163
+ }
2164
+
2165
+ copyXPath(node: SDK.DOMModel.DOMNode, optimized = true): void {
2166
+ Host.InspectorFrontendHost.InspectorFrontendHostInstance.copyText(xPath(node, optimized));
2167
+ }
2168
+
2169
+ copyFullXPath(node: SDK.DOMModel.DOMNode): void {
2170
+ Host.InspectorFrontendHost.InspectorFrontendHostInstance.copyText(xPath(node, false));
2171
+ }
2172
+
2173
+ async copyStyles(node: SDK.DOMModel.DOMNode): Promise<void> {
2174
+ const cssModel = node.domModel().cssModel();
2175
+ const cascade = await cssModel.cachedMatchedCascadeForNode(node);
2176
+ if (!cascade) {
2177
+ return;
2178
+ }
2179
+
2180
+ const indent = Common.Settings.Settings.instance().moduleSetting('text-editor-indent').get();
2181
+ const lines: string[] = [];
2182
+ for (const style of cascade.nodeStyles().reverse()) {
2183
+ for (const property of style.leadingProperties()) {
2184
+ if (!property.parsedOk || property.disabled || !property.activeInStyle() || property.implicit) {
2185
+ continue;
2186
+ }
2187
+ if (cascade.isInherited(style) && !SDK.CSSMetadata.cssMetadata().isPropertyInherited(property.name)) {
2188
+ continue;
2189
+ }
2190
+ if (style.parentRule?.isUserAgent()) {
2191
+ continue;
2192
+ }
2193
+ if (cascade.propertyState(property) !== SDK.CSSMatchedStyles.PropertyState.ACTIVE) {
2194
+ continue;
2195
+ }
2196
+ lines.push(`${indent}${property.name}: ${property.value};`);
2197
+ }
2198
+ }
2199
+
2200
+ Host.InspectorFrontendHost.InspectorFrontendHostInstance.copyText(lines.join('\n'));
2201
+ }
2202
+
2203
+ performCopyOrCut(isCut: boolean, node: SDK.DOMModel.DOMNode|null, includeShadowRoots = false): void {
2204
+ if (!node) {
2205
+ return;
2206
+ }
2207
+ if (isCut && (node.isShadowRoot() || node.ancestorUserAgentShadowRoot())) {
2208
+ return;
2209
+ }
2210
+ void this.copyOuterHTML(node, includeShadowRoots);
2211
+ this.setClipboardData({node, isCut});
2212
+ }
2213
+
2214
+ canPaste(targetNode: SDK.DOMModel.DOMNode): boolean {
2215
+ if (targetNode.isShadowRoot() || targetNode.ancestorUserAgentShadowRoot()) {
2216
+ return false;
2217
+ }
2218
+
2219
+ if (!this.#clipboardData) {
2220
+ return false;
2221
+ }
2222
+
2223
+ const node = this.#clipboardData.node;
2224
+ if (this.#clipboardData.isCut && (node === targetNode || node.isAncestor(targetNode))) {
2225
+ return false;
2226
+ }
2227
+
2228
+ if (targetNode.domModel() !== node.domModel()) {
2229
+ return false;
2230
+ }
2231
+ return true;
2232
+ }
2233
+
2234
+ pasteNode(targetNode: SDK.DOMModel.DOMNode): void {
2235
+ if (!this.canPaste(targetNode) || !this.#clipboardData) {
2236
+ return;
2237
+ }
2238
+ const wasExpanded = this.isNodeExpanded(this.#clipboardData.node);
2239
+ if (this.#clipboardData.isCut) {
2240
+ this.#clipboardData.node.moveTo(targetNode, null, this.selectNodeAfterEdit.bind(this, wasExpanded));
2241
+ this.setClipboardData(null);
2242
+ } else {
2243
+ this.#clipboardData.node.copyTo(targetNode, null, this.selectNodeAfterEdit.bind(this, wasExpanded));
2244
+ }
2245
+ }
2246
+
2247
+ onCopyOrCut(isCut: boolean, event: Event): void {
2248
+ this.setClipboardData(null);
2249
+ // @ts-expect-error this bound in the main entry point
2250
+ const originalEvent = (event as CustomEvent)['original'] || event;
2251
+
2252
+ if (!originalEvent?.target) {
2253
+ return;
2254
+ }
2255
+
2256
+ // Don't prevent the normal copy if the user has a selection.
2257
+ if (originalEvent.target instanceof Node && (originalEvent.target as HTMLElement).hasSelection?.()) {
619
2258
  return;
620
2259
  }
621
- if (query) {
622
- treeElement.highlightSearchResults(query);
623
- }
624
- treeElement.reveal();
625
- const matches = treeElement.listItemElement.getElementsByClassName(Highlighting.highlightedSearchResultClassName);
626
- if (matches.length) {
627
- matches[0].scrollIntoViewIfNeeded(false);
2260
+
2261
+ // Do not interfere with text editing.
2262
+ if (UI.UIUtils.isEditing()) {
2263
+ return;
628
2264
  }
629
- treeElement.select(/* omitFocus */ true);
630
- }
631
2265
 
632
- hideMatchHighlights(node: SDK.DOMModel.DOMNode): void {
633
- const treeElement = this.#viewOutput.elementsTreeOutline?.findTreeElement(node);
634
- if (!treeElement) {
2266
+ const targetNode = this.selectedDOMNode();
2267
+ if (!targetNode) {
635
2268
  return;
636
2269
  }
637
- treeElement.hideSearchHighlights();
638
- }
639
2270
 
640
- toggleHideElement(node: SDK.DOMModel.DOMNode): void {
641
- void node.toggleHideElement();
642
- }
2271
+ if (originalEvent.clipboardData) {
2272
+ originalEvent.clipboardData.clearData();
2273
+ }
2274
+ (event as {handled?: boolean}).handled = true;
643
2275
 
644
- isToggledToHidden(node: SDK.DOMModel.DOMNode): boolean {
645
- return node.isToggledToHidden();
2276
+ this.performCopyOrCut(isCut, targetNode);
646
2277
  }
647
2278
 
648
- toggleEditAsHTML(node: SDK.DOMModel.DOMNode): void {
649
- this.#viewOutput.elementsTreeOutline?.toggleEditAsHTML(node);
650
- }
2279
+ onPaste(event: Event): void {
2280
+ // Do not interfere with text editing.
2281
+ if (UI.UIUtils.isEditing()) {
2282
+ return;
2283
+ }
651
2284
 
652
- duplicateNode(node: SDK.DOMModel.DOMNode): void {
653
- node.duplicate();
654
- }
2285
+ const targetNode = this.selectedDOMNode();
2286
+ if (!targetNode || !this.canPaste(targetNode)) {
2287
+ return;
2288
+ }
655
2289
 
656
- copyStyles(node: SDK.DOMModel.DOMNode): void {
657
- void this.#viewOutput.elementsTreeOutline?.findTreeElement(node)?.copyStyles();
2290
+ (event as {handled?: boolean}).handled = true;
2291
+ this.pasteNode(targetNode);
658
2292
  }
659
2293
 
660
2294
  /**
@@ -662,6 +2296,9 @@ export class DOMTreeWidget extends UI.Widget.Widget {
662
2296
  * way to do it.
663
2297
  */
664
2298
  empty(): boolean {
2299
+ if (this.#view === DECLARATIVE_VIEW) {
2300
+ return !this.#rootDOMNode;
2301
+ }
665
2302
  return !this.#viewOutput.elementsTreeOutline;
666
2303
  }
667
2304
 
@@ -702,6 +2339,13 @@ export class DOMTreeWidget extends UI.Widget.Widget {
702
2339
  if (domModel.existingDocument()) {
703
2340
  this.rootDOMNode = domModel.existingDocument();
704
2341
  this.onDocumentUpdated(domModel);
2342
+ } else if (this.#view === DECLARATIVE_VIEW) {
2343
+ void domModel.requestDocument().then(document => {
2344
+ if (document && this.isShowing()) {
2345
+ this.rootDOMNode = document;
2346
+ this.onDocumentUpdated(domModel);
2347
+ }
2348
+ });
705
2349
  } else {
706
2350
  void domModel.requestDocument();
707
2351
  }
@@ -724,14 +2368,10 @@ export class ElementsTreeOutline extends
724
2368
  private visible: boolean;
725
2369
  private updateRecords: Map<SDK.DOMModel.DOMNode, Elements.ElementUpdateRecord.ElementUpdateRecord>;
726
2370
  private treeElementsBeingUpdated: Set<ElementsTreeElement>;
727
- decoratorExtensions: MarkerDecoratorRegistration[]|null;
728
- private multilineEditing?: MultilineEditorController|null;
729
2371
  private visibleWidthInternal?: number;
730
- private clipboardNodeData?: ClipboardData;
731
2372
  private isXMLMimeTypeInternal?: boolean|null;
732
2373
  suppressRevealAndSelect = false;
733
2374
  private previousHoveredElement?: UI.TreeOutline.TreeElement;
734
- private treeElementBeingDragged?: ElementsTreeElement;
735
2375
  private dragOverTreeElement?: ElementsTreeElement;
736
2376
  private updateModifiedNodesTimeout?: number;
737
2377
  #topLayerContainerByDocument = new WeakMap<SDK.DOMModel.DOMDocument, TopLayerContainer>();
@@ -743,13 +2383,19 @@ export class ElementsTreeOutline extends
743
2383
  expandRoot: boolean;
744
2384
  #maxRowsShown?: number;
745
2385
  #showAllButton?: HTMLElement;
2386
+ domTreeWidget: DOMTreeWidget|null = null;
746
2387
 
747
- constructor(
748
- omitRootDOMNode?: boolean, selectEnabled?: boolean, hideGutter?: boolean, maxTreeDepth?: number,
749
- enableContextMenu?: boolean, showComments?: boolean, showAIButton?: boolean, disableEdits?: boolean,
750
- expandRoot?: boolean) {
2388
+ get hoveredTreeElement(): UI.TreeOutline.TreeElement|null {
2389
+ return this.previousHoveredElement ?? null;
2390
+ }
2391
+
2392
+ constructor(omitRootDOMNode?: boolean, selectEnabled?: boolean, hideGutter?: boolean, maxTreeDepth?: number,
2393
+ enableContextMenu?: boolean, showComments?: boolean, showAIButton?: boolean, disableEdits?: boolean,
2394
+ expandRoot?: boolean, domTreeWidget?: DOMTreeWidget|null) {
751
2395
  super();
752
2396
 
2397
+ this.domTreeWidget = domTreeWidget ?? null;
2398
+ this.renderSelection = true;
753
2399
  this.treeElementByNode = new WeakMap();
754
2400
  const shadowContainer = document.createElement('div');
755
2401
  this.shadowRoot = UI.UIUtils.createShadowRootWithCoreStyles(
@@ -770,7 +2416,6 @@ export class ElementsTreeOutline extends
770
2416
  this.elementInternal.addEventListener('mousedown', this.onmousedown.bind(this), false);
771
2417
  this.elementInternal.addEventListener('mousemove', this.onmousemove.bind(this), false);
772
2418
  this.elementInternal.addEventListener('mouseleave', this.onmouseleave.bind(this), false);
773
- this.elementInternal.addEventListener('keydown', this.onKeyDown.bind(this), false);
774
2419
 
775
2420
  if (!this.disableEdits) {
776
2421
  this.elementInternal.addEventListener('dragstart', this.ondragstart.bind(this), false);
@@ -778,10 +2423,6 @@ export class ElementsTreeOutline extends
778
2423
  this.elementInternal.addEventListener('dragleave', this.ondragleave.bind(this), false);
779
2424
  this.elementInternal.addEventListener('drop', this.ondrop.bind(this), false);
780
2425
  this.elementInternal.addEventListener('dragend', this.ondragend.bind(this), false);
781
- this.elementInternal.addEventListener('clipboard-beforecopy', this.onBeforeCopy.bind(this), false);
782
- this.elementInternal.addEventListener('clipboard-copy', this.onCopyOrCut.bind(this, false), false);
783
- this.elementInternal.addEventListener('clipboard-cut', this.onCopyOrCut.bind(this, true), false);
784
- this.elementInternal.addEventListener('clipboard-paste', this.onPaste.bind(this), false);
785
2426
  }
786
2427
 
787
2428
  outlineDisclosureElement.appendChild(this.elementInternal);
@@ -798,7 +2439,6 @@ export class ElementsTreeOutline extends
798
2439
  this.updateRecords = new Map();
799
2440
  this.treeElementsBeingUpdated = new Set();
800
2441
 
801
- this.decoratorExtensions = null;
802
2442
  this.setUseLightSelectionColor(true);
803
2443
  }
804
2444
 
@@ -818,159 +2458,39 @@ export class ElementsTreeOutline extends
818
2458
  }
819
2459
 
820
2460
  setMultilineEditing(multilineEditing: MultilineEditorController|null): void {
821
- this.multilineEditing = multilineEditing;
2461
+ this.domTreeWidget?.setMultilineEditing(multilineEditing);
822
2462
  }
823
2463
 
824
2464
  visibleWidth(): number {
825
- return this.visibleWidthInternal || 0;
2465
+ return this.domTreeWidget?.visibleWidth ?? this.visibleWidthInternal ?? 0;
826
2466
  }
827
2467
 
828
2468
  setVisibleWidth(width: number): void {
829
2469
  this.visibleWidthInternal = width;
830
- if (this.multilineEditing) {
831
- this.multilineEditing.resize();
832
- }
833
2470
  }
834
2471
 
835
- private setClipboardData(data: ClipboardData|null): void {
836
- if (this.clipboardNodeData) {
837
- const treeElement = this.findTreeElement(this.clipboardNodeData.node);
838
- if (treeElement) {
839
- treeElement.setInClipboard(false);
840
- }
841
- delete this.clipboardNodeData;
842
- }
843
-
844
- if (data) {
845
- const treeElement = this.findTreeElement(data.node);
846
- if (treeElement) {
847
- treeElement.setInClipboard(true);
848
- }
849
- this.clipboardNodeData = data;
850
- }
2472
+ setClipboardData(data: ClipboardData|null): void {
2473
+ this.domTreeWidget?.setClipboardData(data);
851
2474
  }
852
2475
 
853
2476
  resetClipboardIfNeeded(removedNode: SDK.DOMModel.DOMNode): void {
854
- if (this.clipboardNodeData?.node === removedNode) {
855
- this.setClipboardData(null);
856
- }
857
- }
858
-
859
- private onBeforeCopy(event: Event): void {
860
- event.handled = true;
861
- }
862
-
863
- private onCopyOrCut(isCut: boolean, event: Event): void {
864
- this.setClipboardData(null);
865
- // @ts-expect-error this bound in the main entry point
866
- const originalEvent = event['original'];
867
-
868
- if (!originalEvent?.target) {
869
- return;
870
- }
871
-
872
- // Don't prevent the normal copy if the user has a selection.
873
- if (originalEvent.target instanceof Node && originalEvent.target.hasSelection()) {
874
- return;
875
- }
876
-
877
- // Do not interfere with text editing.
878
- if (UI.UIUtils.isEditing()) {
879
- return;
880
- }
881
-
882
- const targetNode = this.selectedDOMNode();
883
- if (!targetNode) {
884
- return;
885
- }
886
-
887
- if (!originalEvent.clipboardData) {
888
- return;
889
- }
890
- originalEvent.clipboardData.clearData();
891
- event.handled = true;
892
-
893
- this.performCopyOrCut(isCut, targetNode);
2477
+ this.domTreeWidget?.resetClipboardIfNeeded(removedNode);
894
2478
  }
895
2479
 
896
2480
  performCopyOrCut(isCut: boolean, node: SDK.DOMModel.DOMNode|null, includeShadowRoots = false): void {
897
- if (!node) {
898
- return;
899
- }
900
- if (isCut && (node.isShadowRoot() || node.ancestorUserAgentShadowRoot())) {
901
- return;
902
- }
903
- void node.getOuterHTML(includeShadowRoots).then(outerHTML => {
904
- if (outerHTML !== null) {
905
- UI.UIUtils.copyTextToClipboard(outerHTML);
906
- }
907
- });
908
- this.setClipboardData({node, isCut});
2481
+ this.domTreeWidget?.performCopyOrCut(isCut, node, includeShadowRoots);
909
2482
  }
910
2483
 
911
2484
  canPaste(targetNode: SDK.DOMModel.DOMNode): boolean {
912
- if (targetNode.isShadowRoot() || targetNode.ancestorUserAgentShadowRoot()) {
913
- return false;
914
- }
915
-
916
- if (!this.clipboardNodeData) {
917
- return false;
918
- }
919
-
920
- const node = this.clipboardNodeData.node;
921
- if (this.clipboardNodeData.isCut && (node === targetNode || node.isAncestor(targetNode))) {
922
- return false;
923
- }
924
-
925
- if (targetNode.domModel() !== node.domModel()) {
926
- return false;
927
- }
928
- return true;
2485
+ return this.domTreeWidget?.canPaste(targetNode) ?? false;
929
2486
  }
930
2487
 
931
2488
  pasteNode(targetNode: SDK.DOMModel.DOMNode): void {
932
- if (this.canPaste(targetNode)) {
933
- this.performPaste(targetNode);
934
- }
2489
+ this.domTreeWidget?.pasteNode(targetNode);
935
2490
  }
936
2491
 
937
2492
  duplicateNode(targetNode: SDK.DOMModel.DOMNode): void {
938
- targetNode.duplicate();
939
- }
940
-
941
- private onPaste(event: Event): void {
942
- // Do not interfere with text editing.
943
- if (UI.UIUtils.isEditing()) {
944
- return;
945
- }
946
-
947
- const targetNode = this.selectedDOMNode();
948
- if (!targetNode || !this.canPaste(targetNode)) {
949
- return;
950
- }
951
-
952
- event.handled = true;
953
- this.performPaste(targetNode);
954
- }
955
-
956
- private performPaste(targetNode: SDK.DOMModel.DOMNode): void {
957
- if (!this.clipboardNodeData) {
958
- return;
959
- }
960
- if (this.clipboardNodeData.isCut) {
961
- this.clipboardNodeData.node.moveTo(targetNode, null, expandCallback.bind(this));
962
- this.setClipboardData(null);
963
- } else {
964
- this.clipboardNodeData.node.copyTo(targetNode, null, expandCallback.bind(this));
965
- }
966
-
967
- function expandCallback(
968
- this: ElementsTreeOutline, error: string|null, pastedNode: SDK.DOMModel.DOMNode|null): void {
969
- if (error || !pastedNode) {
970
- return;
971
- }
972
- this.selectDOMNode(pastedNode);
973
- }
2493
+ this.domTreeWidget?.duplicateNode(targetNode);
974
2494
  }
975
2495
 
976
2496
  setVisible(visible: boolean): void {
@@ -979,9 +2499,7 @@ export class ElementsTreeOutline extends
979
2499
  }
980
2500
  this.visible = visible;
981
2501
  if (!this.visible) {
982
- if (this.multilineEditing) {
983
- this.multilineEditing.cancel();
984
- }
2502
+ this.domTreeWidget?.multilineEditing()?.cancel();
985
2503
  return;
986
2504
  }
987
2505
 
@@ -1252,13 +2770,22 @@ export class ElementsTreeOutline extends
1252
2770
  }
1253
2771
 
1254
2772
  treeElementFromEventInternal(event: MouseEvent): UI.TreeOutline.TreeElement|null {
2773
+ for (const target of event.composedPath()) {
2774
+ if (target instanceof HTMLLIElement) {
2775
+ const element = UI.TreeOutline.TreeElement.getTreeElementBylistItemNode(target);
2776
+ if (element) {
2777
+ return element;
2778
+ }
2779
+ }
2780
+ }
2781
+
1255
2782
  const scrollContainer = this.element.parentElement;
1256
2783
  if (!scrollContainer) {
1257
2784
  return null;
1258
2785
  }
1259
2786
 
1260
- const x = event.pageX;
1261
- const y = event.pageY;
2787
+ const x = event.clientX;
2788
+ const y = event.clientY;
1262
2789
 
1263
2790
  // Our list items have 1-pixel cracks between them vertically. We avoid
1264
2791
  // the cracks by checking slightly above and slightly below the mouse
@@ -1308,9 +2835,15 @@ export class ElementsTreeOutline extends
1308
2835
  return;
1309
2836
  }
1310
2837
 
2838
+ const showInfo = !UI.KeyboardShortcut.KeyboardShortcut.eventHasEitherCtrlOrMeta(event);
2839
+ if (this.domTreeWidget && element instanceof ElementsTreeElement) {
2840
+ this.domTreeWidget.setHoveredNode(element.node(), showInfo);
2841
+ return;
2842
+ }
2843
+
2844
+ this.domTreeWidget?.setHoveredNode(null);
1311
2845
  this.setHoverEffect(element);
1312
- this.highlightTreeElement(
1313
- (element as UI.TreeOutline.TreeElement), !UI.KeyboardShortcut.KeyboardShortcut.eventHasEitherCtrlOrMeta(event));
2846
+ this.highlightTreeElement((element as UI.TreeOutline.TreeElement), showInfo);
1314
2847
  }
1315
2848
 
1316
2849
  private highlightTreeElement(element: UI.TreeOutline.TreeElement, showInfo: boolean): void {
@@ -1328,131 +2861,61 @@ export class ElementsTreeOutline extends
1328
2861
  }
1329
2862
 
1330
2863
  private onmouseleave(_event: MouseEvent): void {
2864
+ this.domTreeWidget?.setHoveredNode(null);
1331
2865
  this.setHoverEffect(null);
1332
2866
  SDK.OverlayModel.OverlayModel.hideDOMNodeHighlight(SDK.TargetManager.TargetManager.instance());
1333
2867
  }
1334
2868
 
1335
2869
  private ondragstart(event: DragEvent): boolean|undefined {
1336
- const node = (event.target as Node | null);
1337
- if (!node || node.hasSelection()) {
1338
- return false;
1339
- }
1340
- if (node.nodeName === 'A') {
1341
- return false;
1342
- }
1343
-
1344
- const treeElement = this.validDragSourceOrTarget(this.treeElementFromEventInternal(event));
1345
- if (!treeElement) {
1346
- return false;
1347
- }
1348
-
1349
- if (treeElement.node().nodeName() === 'BODY' || treeElement.node().nodeName() === 'HEAD') {
2870
+ const treeElement = this.treeElementFromEventInternal(event);
2871
+ if (!(treeElement instanceof ElementsTreeElement)) {
1350
2872
  return false;
1351
2873
  }
1352
-
1353
- if (!event.dataTransfer || !treeElement.listItemElement.textContent) {
1354
- return;
1355
- }
1356
- event.dataTransfer.setData('text/plain', treeElement.listItemElement.textContent.replace(/\u200b/g, ''));
1357
- event.dataTransfer.effectAllowed = 'copyMove';
1358
- this.treeElementBeingDragged = treeElement;
1359
-
1360
- SDK.OverlayModel.OverlayModel.hideDOMNodeHighlight(SDK.TargetManager.TargetManager.instance());
1361
-
1362
- return true;
2874
+ const textContent = treeElement.widget.contentElement.textContent || treeElement.listItemElement.textContent ||
2875
+ treeElement.node().nodeName().toLowerCase();
2876
+ const success = this.domTreeWidget?.onDragStart(treeElement.node(), event, textContent);
2877
+ return success;
1363
2878
  }
1364
2879
 
1365
2880
  private ondragover(event: DragEvent): boolean {
1366
- if (!this.treeElementBeingDragged) {
1367
- return false;
1368
- }
1369
-
1370
- const treeElement = this.validDragSourceOrTarget(this.treeElementFromEventInternal(event));
1371
- if (!treeElement) {
2881
+ const treeElement = this.treeElementFromEventInternal(event);
2882
+ if (!(treeElement instanceof ElementsTreeElement)) {
2883
+ this.clearDragOverTreeElementMarker();
1372
2884
  return false;
1373
2885
  }
1374
-
1375
- let node: (SDK.DOMModel.DOMNode|null) = (treeElement.node() as SDK.DOMModel.DOMNode | null);
1376
- while (node) {
1377
- if (node === this.treeElementBeingDragged.nodeInternal) {
1378
- return false;
2886
+ const success = this.domTreeWidget?.onDragOver(treeElement.node(), treeElement.isClosingTag(), event);
2887
+ if (success) {
2888
+ if (this.dragOverTreeElement !== treeElement) {
2889
+ this.clearDragOverTreeElementMarker();
2890
+ treeElement.listItemElement.classList.add('elements-drag-over');
2891
+ this.dragOverTreeElement = treeElement;
1379
2892
  }
1380
- node = node.parentNode;
1381
- }
1382
-
1383
- treeElement.listItemElement.classList.add('elements-drag-over');
1384
- this.dragOverTreeElement = treeElement;
1385
- event.preventDefault();
1386
- if (event.dataTransfer) {
1387
- event.dataTransfer.dropEffect = 'move';
2893
+ } else {
2894
+ this.clearDragOverTreeElementMarker();
1388
2895
  }
1389
- return false;
2896
+ return !success;
1390
2897
  }
1391
2898
 
1392
2899
  private ondragleave(event: DragEvent): boolean {
1393
2900
  this.clearDragOverTreeElementMarker();
2901
+ this.domTreeWidget?.onDragLeave(event);
1394
2902
  event.preventDefault();
1395
2903
  return false;
1396
2904
  }
1397
2905
 
1398
- private validDragSourceOrTarget(treeElement: UI.TreeOutline.TreeElement|null): ElementsTreeElement|null {
1399
- if (!treeElement) {
1400
- return null;
1401
- }
1402
-
1403
- if (!(treeElement instanceof ElementsTreeElement)) {
1404
- return null;
1405
- }
1406
- const elementsTreeElement = (treeElement);
1407
-
1408
- const node = elementsTreeElement.node();
1409
- if (!node.parentNode || node.parentNode.nodeType() !== Node.ELEMENT_NODE) {
1410
- return null;
1411
- }
1412
-
1413
- return elementsTreeElement;
1414
- }
1415
-
1416
2906
  private ondrop(event: DragEvent): void {
1417
2907
  event.preventDefault();
1418
2908
  const treeElement = this.treeElementFromEventInternal(event);
1419
2909
  if (treeElement instanceof ElementsTreeElement) {
1420
- this.doMove(treeElement);
1421
- }
1422
- }
1423
-
1424
- private doMove(treeElement: ElementsTreeElement): void {
1425
- if (!this.treeElementBeingDragged) {
1426
- return;
1427
- }
1428
-
1429
- let parentNode;
1430
- let anchorNode;
1431
-
1432
- if (treeElement.isClosingTag()) {
1433
- // Drop onto closing tag -> insert as last child.
1434
- parentNode = treeElement.node();
1435
- anchorNode = null;
1436
- } else {
1437
- const dragTargetNode = treeElement.node();
1438
- parentNode = dragTargetNode.parentNode;
1439
- anchorNode = dragTargetNode;
2910
+ this.domTreeWidget?.onDrop(treeElement.node(), treeElement.isClosingTag(), event);
1440
2911
  }
1441
-
1442
- if (!parentNode) {
1443
- return;
1444
- }
1445
- const wasExpanded = this.treeElementBeingDragged.expanded;
1446
- this.treeElementBeingDragged.nodeInternal.moveTo(
1447
- parentNode, anchorNode, this.selectNodeAfterEdit.bind(this, wasExpanded));
1448
-
1449
- delete this.treeElementBeingDragged;
2912
+ this.clearDragOverTreeElementMarker();
1450
2913
  }
1451
2914
 
1452
2915
  private ondragend(event: DragEvent): void {
1453
2916
  event.preventDefault();
1454
2917
  this.clearDragOverTreeElementMarker();
1455
- delete this.treeElementBeingDragged;
2918
+ this.domTreeWidget?.onDragEnd(event);
1456
2919
  }
1457
2920
 
1458
2921
  private clearDragOverTreeElementMarker(): void {
@@ -1468,83 +2931,8 @@ export class ElementsTreeOutline extends
1468
2931
  this.updateModifiedNodes();
1469
2932
  }
1470
2933
 
1471
- private onKeyDown(event: Event): void {
1472
- const keyboardEvent = (event as KeyboardEvent);
1473
- if (UI.UIUtils.isEditing()) {
1474
- return;
1475
- }
1476
- const node = this.selectedDOMNode();
1477
- if (!node) {
1478
- return;
1479
- }
1480
- const treeElement = this.treeElementByNode.get(node);
1481
- if (!treeElement) {
1482
- return;
1483
- }
1484
-
1485
- if (UI.KeyboardShortcut.KeyboardShortcut.eventHasCtrlEquivalentKey(keyboardEvent) && node.parentNode) {
1486
- if (keyboardEvent.key === 'ArrowUp' && node.previousSibling) {
1487
- node.moveTo(node.parentNode, node.previousSibling, this.selectNodeAfterEdit.bind(this, treeElement.expanded));
1488
- keyboardEvent.consume(true);
1489
- return;
1490
- }
1491
- if (keyboardEvent.key === 'ArrowDown' && node.nextSibling) {
1492
- node.moveTo(
1493
- node.parentNode, node.nextSibling.nextSibling, this.selectNodeAfterEdit.bind(this, treeElement.expanded));
1494
- keyboardEvent.consume(true);
1495
- return;
1496
- }
1497
- }
1498
- }
1499
-
1500
2934
  toggleEditAsHTML(node: SDK.DOMModel.DOMNode, startEditing?: boolean, callback?: (() => void)): void {
1501
- const treeElement = this.treeElementByNode.get(node);
1502
- if (!treeElement?.hasEditableNode()) {
1503
- return;
1504
- }
1505
-
1506
- if (node.pseudoType()) {
1507
- return;
1508
- }
1509
-
1510
- const parentNode = node.parentNode;
1511
- const index = node.index;
1512
- const wasExpanded = treeElement.expanded;
1513
-
1514
- treeElement.toggleEditAsHTML(editingFinished.bind(this), startEditing);
1515
-
1516
- function editingFinished(this: ElementsTreeOutline, success: boolean): void {
1517
- if (callback) {
1518
- callback();
1519
- }
1520
- if (!success) {
1521
- return;
1522
- }
1523
-
1524
- Badges.UserBadges.instance().recordAction(Badges.BadgeAction.DOM_ELEMENT_OR_ATTRIBUTE_EDITED);
1525
-
1526
- // Select it and expand if necessary. We force tree update so that it processes dom events and is up to date.
1527
- this.runPendingUpdates();
1528
-
1529
- if (!index) {
1530
- return;
1531
- }
1532
-
1533
- const children = parentNode?.children();
1534
- const newNode = children ? children[index] || parentNode : parentNode;
1535
- if (!newNode) {
1536
- return;
1537
- }
1538
-
1539
- this.selectDOMNode(newNode, true);
1540
-
1541
- if (wasExpanded) {
1542
- const newTreeItem = this.findTreeElement(newNode);
1543
- if (newTreeItem) {
1544
- newTreeItem.expand();
1545
- }
1546
- }
1547
- }
2935
+ this.domTreeWidget?.toggleEditAsHTML(node, startEditing, callback);
1548
2936
  }
1549
2937
 
1550
2938
  selectNodeAfterEdit(wasExpanded: boolean, error: string|null, newNode: SDK.DOMModel.DOMNode|null): ElementsTreeElement
@@ -1582,7 +2970,7 @@ export class ElementsTreeOutline extends
1582
2970
  private reset(): void {
1583
2971
  this.rootDOMNode = null;
1584
2972
  this.selectDOMNode(null, false);
1585
- delete this.clipboardNodeData;
2973
+ this.setClipboardData(null);
1586
2974
  SDK.OverlayModel.OverlayModel.hideDOMNodeHighlight(SDK.TargetManager.TargetManager.instance());
1587
2975
  this.updateRecords.clear();
1588
2976
  }