chrome-devtools-frontend 1.0.1683520 → 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 (661) 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/AUTHORS +1 -0
  6. package/config/typescript/tsconfig.base.json +1 -0
  7. package/config/typescript/tsconfig.eslint.json +9 -0
  8. package/docs/contributing/issues.md +1 -1
  9. package/docs/styleguide/ux/writing.md +2 -2
  10. package/eslint.config.mjs +18 -10
  11. package/front_end/core/common/Base64.ts +1 -1
  12. package/front_end/core/common/Color.ts +28 -6
  13. package/front_end/core/common/ColorConverter.ts +12 -0
  14. package/front_end/core/common/Console.ts +2 -1
  15. package/front_end/core/common/Object.ts +2 -2
  16. package/front_end/core/common/ResourceType.ts +12 -13
  17. package/front_end/core/common/Revealer.ts +1 -1
  18. package/front_end/core/common/Settings.ts +2 -2
  19. package/front_end/core/common/TextDictionary.ts +2 -2
  20. package/front_end/core/common/Throttler.ts +1 -1
  21. package/front_end/core/host/InspectorFrontendHostAPI.ts +2 -2
  22. package/front_end/core/host/UserMetrics.ts +51 -37
  23. package/front_end/core/host/host.ts +1 -1
  24. package/front_end/core/i18n/i18nImpl.ts +1 -1
  25. package/front_end/core/platform/HostRuntime.ts +3 -3
  26. package/front_end/core/platform/KeyboardUtilities.ts +1 -1
  27. package/front_end/core/protocol_client/InspectorBackend.ts +7 -6
  28. package/front_end/core/root/Runtime.ts +4 -2
  29. package/front_end/core/sdk/AnimationModel.ts +1 -1
  30. package/front_end/core/sdk/CPUThrottlingManager.ts +198 -0
  31. package/front_end/core/sdk/CSSMetadata.ts +11 -6
  32. package/front_end/core/sdk/CSSPropertyParser.ts +5 -3
  33. package/front_end/core/sdk/CSSPropertyParserMatchers.ts +90 -59
  34. package/front_end/core/sdk/ConsoleModel.ts +1 -1
  35. package/front_end/core/sdk/DOMModel.ts +3 -3
  36. package/front_end/core/sdk/DebuggerModel.ts +1 -1
  37. package/front_end/core/sdk/EmulationModel.ts +5 -0
  38. package/front_end/core/sdk/LayerTreeBase.ts +1 -1
  39. package/front_end/core/sdk/NetworkManager.ts +1 -1
  40. package/front_end/core/sdk/README.md +40 -0
  41. package/front_end/core/sdk/RehydratingConnection.ts +1 -1
  42. package/front_end/core/sdk/RemoteObject.ts +1 -1
  43. package/front_end/core/sdk/ResourceTreeModel.ts +2 -2
  44. package/front_end/core/sdk/SDKModel.ts +1 -1
  45. package/front_end/core/sdk/SDKSettings.ts +6 -0
  46. package/front_end/core/sdk/Script.ts +2 -1
  47. package/front_end/core/sdk/SecurityOrigin.ts +48 -0
  48. package/front_end/core/sdk/ServiceWorkerCacheModel.ts +0 -7
  49. package/front_end/core/sdk/ServiceWorkerManager.ts +75 -17
  50. package/front_end/core/sdk/StorageBucketsModel.ts +0 -6
  51. package/front_end/core/sdk/sdk.ts +2 -0
  52. package/front_end/core/text_utils/ContentData.ts +1 -1
  53. package/front_end/entrypoints/formatter_worker/FormattedContentBuilder.ts +3 -1
  54. package/front_end/entrypoints/formatter_worker/FormatterWorker.ts +1 -1
  55. package/front_end/entrypoints/formatter_worker/ScopeParser.ts +2 -2
  56. package/front_end/entrypoints/formatter_worker/Substitute.ts +1 -1
  57. package/front_end/entrypoints/formatter_worker/formatter_worker-entrypoint.ts +1 -1
  58. package/front_end/entrypoints/inspector_main/InspectorMain.ts +1 -1
  59. package/front_end/entrypoints/inspector_main/OutermostTargetSelector.ts +1 -1
  60. package/front_end/entrypoints/inspector_main/nodeIcon.css +1 -1
  61. package/front_end/entrypoints/inspector_main/outermostTargetSelector.css +3 -3
  62. package/front_end/entrypoints/inspector_main/renderingOptions.css +3 -3
  63. package/front_end/entrypoints/js_app/js_app.ts +25 -9
  64. package/front_end/entrypoints/main/MainImpl.ts +2 -1
  65. package/front_end/entrypoints/main/main-meta.ts +5 -31
  66. package/front_end/entrypoints/node_app/app/nodeConnectionsPanel.css +5 -5
  67. package/front_end/entrypoints/node_app/node_app.ts +1 -1
  68. package/front_end/foundation/Universe.ts +7 -0
  69. package/front_end/generated/ARIAProperties.ts +39 -1
  70. package/front_end/generated/InspectorBackendCommands.ts +13 -22
  71. package/front_end/generated/SupportedCSSProperties.ts +60 -119
  72. package/front_end/generated/protocol-mapping.d.ts +18 -59
  73. package/front_end/generated/protocol-proxy-api.d.ts +14 -47
  74. package/front_end/generated/protocol.ts +104 -331
  75. package/front_end/models/ai_assistance/AiAgent2.ts +25 -11
  76. package/front_end/models/ai_assistance/AiConversation.ts +23 -14
  77. package/front_end/models/ai_assistance/AiHistoryStorage.ts +2 -2
  78. package/front_end/models/ai_assistance/AiUtils.ts +16 -0
  79. package/front_end/models/ai_assistance/EvaluateAction.ts +1 -1
  80. package/front_end/models/ai_assistance/README.md +1 -1
  81. package/front_end/models/ai_assistance/agents/AccessibilityAgent.ts +2 -3
  82. package/front_end/models/ai_assistance/agents/AiAgent.ts +40 -25
  83. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +4 -3
  84. package/front_end/models/ai_assistance/agents/FileAgent.ts +2 -2
  85. package/front_end/models/ai_assistance/agents/NetworkAgent.ts +2 -2
  86. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +1 -1
  87. package/front_end/models/ai_assistance/agents/StorageAgent.ts +13 -11
  88. package/front_end/models/ai_assistance/agents/StylingAgent.snapshot.txt +2 -2
  89. package/front_end/models/ai_assistance/agents/StylingAgent.ts +9 -7
  90. package/front_end/models/ai_assistance/ai_assistance.ts +12 -0
  91. package/front_end/models/ai_assistance/contexts/AccessibilityContext.ts +16 -1
  92. package/front_end/models/ai_assistance/contexts/FileContext.ts +6 -0
  93. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +150 -325
  94. package/front_end/models/ai_assistance/injected.ts +5 -3
  95. package/front_end/models/ai_assistance/performance/AIContext.ts +1 -1
  96. package/front_end/models/ai_assistance/skills/README.md +1 -1
  97. package/front_end/models/ai_assistance/skills/accessibility.md +19 -10
  98. package/front_end/models/ai_assistance/skills/network.md +13 -7
  99. package/front_end/models/ai_assistance/skills/performance.md +22 -16
  100. package/front_end/models/ai_assistance/skills/sources.md +13 -7
  101. package/front_end/models/ai_assistance/skills/storage.md +19 -18
  102. package/front_end/models/ai_assistance/skills/styling.md +30 -37
  103. package/front_end/models/ai_assistance/tools/CookieUtils.ts +120 -0
  104. package/front_end/models/ai_assistance/tools/DOMStorageUtils.ts +53 -4
  105. package/front_end/models/ai_assistance/tools/ExecuteJavaScript.ts +8 -32
  106. package/front_end/models/ai_assistance/tools/GetCookieValues.ts +146 -0
  107. package/front_end/models/ai_assistance/tools/GetDetailedCallTree.ts +11 -9
  108. package/front_end/models/ai_assistance/tools/GetElementAccessibilityDetails.ts +17 -5
  109. package/front_end/models/ai_assistance/tools/GetFunctionCode.ts +12 -11
  110. package/front_end/models/ai_assistance/tools/GetInsightDetails.ts +11 -11
  111. package/front_end/models/ai_assistance/tools/GetLighthouseAudits.ts +15 -8
  112. package/front_end/models/ai_assistance/tools/GetNetworkRequestDetails.ts +3 -3
  113. package/front_end/models/ai_assistance/tools/GetResourceContent.ts +12 -12
  114. package/front_end/models/ai_assistance/tools/GetSourceContent.ts +3 -2
  115. package/front_end/models/ai_assistance/tools/GetStorageBreakdown.ts +132 -0
  116. package/front_end/models/ai_assistance/tools/GetStorageValues.ts +18 -5
  117. package/front_end/models/ai_assistance/tools/GetStyles.ts +3 -3
  118. package/front_end/models/ai_assistance/tools/GetTraceEventByKey.ts +9 -8
  119. package/front_end/models/ai_assistance/tools/GetTraceMainThreadSummary.ts +13 -14
  120. package/front_end/models/ai_assistance/tools/GetTraceNetworkSummary.ts +12 -10
  121. package/front_end/models/ai_assistance/tools/ListCookies.ts +93 -0
  122. package/front_end/models/ai_assistance/tools/ListNetworkRequests.ts +15 -6
  123. package/front_end/models/ai_assistance/tools/ListPageOrigins.ts +3 -3
  124. package/front_end/models/ai_assistance/tools/ListSources.ts +5 -3
  125. package/front_end/models/ai_assistance/tools/ListStorageKeys.ts +17 -5
  126. package/front_end/models/ai_assistance/tools/RecordPerformanceTrace.ts +3 -2
  127. package/front_end/models/ai_assistance/tools/ResolveDevtoolsNodePath.ts +4 -3
  128. package/front_end/models/ai_assistance/tools/RunLighthouse.ts +14 -17
  129. package/front_end/models/ai_assistance/tools/SelectTraceEventByKey.ts +9 -8
  130. package/front_end/models/ai_assistance/tools/Tool.ts +36 -13
  131. package/front_end/models/ai_assistance/tools/ToolRegistry.ts +41 -8
  132. package/front_end/models/badges/AiExplorerBadge.ts +3 -3
  133. package/front_end/models/badges/CodeWhispererBadge.ts +2 -2
  134. package/front_end/models/badges/DOMDetectiveBadge.ts +3 -3
  135. package/front_end/models/badges/SpeedsterBadge.ts +3 -3
  136. package/front_end/models/badges/StarterBadge.ts +4 -4
  137. package/front_end/models/bindings/CompilerScriptMapping.ts +11 -2
  138. package/front_end/models/bindings/ContentProviderBasedProject.ts +3 -2
  139. package/front_end/models/breakpoints/BreakpointManager.ts +1 -1
  140. package/front_end/models/emulation/EmulatedDevices.ts +2 -2
  141. package/front_end/models/formatter/FormatterWorkerPool.ts +2 -2
  142. package/front_end/models/formatter/ScriptFormatter.ts +1 -2
  143. package/front_end/models/geometry/GeometryImpl.ts +2 -2
  144. package/front_end/models/heap_snapshot/HeapSnapshotModel.ts +1 -1
  145. package/front_end/models/heap_snapshot/HeapSnapshotProxy.ts +3 -2
  146. package/front_end/models/issues_manager/ContentSecurityPolicyIssue.ts +5 -5
  147. package/front_end/models/issues_manager/DOMIssuesManager.ts +144 -0
  148. package/front_end/models/issues_manager/ElementAccessibilityIssue.ts +13 -1
  149. package/front_end/models/issues_manager/GenericIssue.ts +32 -20
  150. package/front_end/models/issues_manager/IssuesManager.ts +0 -1
  151. package/front_end/models/issues_manager/StylesheetLoadingIssue.ts +1 -1
  152. package/front_end/models/issues_manager/issues_manager.ts +2 -0
  153. package/front_end/models/javascript_metadata/{NativeFunctions.js → NativeFunctions.ts} +29 -9
  154. package/front_end/models/live-metrics/LiveMetrics.ts +9 -3
  155. package/front_end/models/live-metrics/web-vitals-injected/spec/spec.ts +58 -6
  156. package/front_end/models/live-metrics/web-vitals-injected/web-vitals-injected.ts +2 -62
  157. package/front_end/models/network_time_calculator/RequestTimeRanges.ts +2 -2
  158. package/front_end/models/persistence/AutomaticFileSystemWorkspaceBinding.ts +5 -0
  159. package/front_end/models/persistence/IsolatedFileSystem.ts +1 -1
  160. package/front_end/models/persistence/NetworkPersistenceManager.ts +35 -6
  161. package/front_end/models/stack_trace/StackTraceImpl.ts +2 -2
  162. package/front_end/models/trace/LanternComputationData.ts +2 -3
  163. package/front_end/models/trace/extras/StackTraceForEvent.ts +3 -2
  164. package/front_end/models/trace/extras/ThirdParties.ts +9 -9
  165. package/front_end/models/trace/handlers/AsyncJSCallsHandler.ts +7 -4
  166. package/front_end/models/trace/handlers/FramesHandler.ts +3 -3
  167. package/front_end/models/trace/handlers/LayoutShiftsHandler.ts +2 -2
  168. package/front_end/models/trace/handlers/SamplesHandler.ts +4 -2
  169. package/front_end/models/trace/handlers/UserInteractionsHandler.ts +1 -1
  170. package/front_end/models/trace/handlers/WarningsHandler.ts +2 -2
  171. package/front_end/models/trace/helpers/Network.ts +3 -3
  172. package/front_end/models/trace/helpers/Trace.ts +1 -1
  173. package/front_end/models/trace/insights/CLSCulprits.ts +2 -1
  174. package/front_end/models/trace/insights/Cache.ts +3 -1
  175. package/front_end/models/trace/insights/CharacterSet.ts +3 -1
  176. package/front_end/models/trace/insights/DOMSize.ts +3 -1
  177. package/front_end/models/trace/insights/DocumentLatency.ts +3 -1
  178. package/front_end/models/trace/insights/DuplicatedJavaScript.ts +3 -1
  179. package/front_end/models/trace/insights/FontDisplay.ts +2 -1
  180. package/front_end/models/trace/insights/ForcedReflow.ts +2 -1
  181. package/front_end/models/trace/insights/INPBreakdown.ts +3 -1
  182. package/front_end/models/trace/insights/ImageDelivery.ts +3 -1
  183. package/front_end/models/trace/insights/LCPBreakdown.ts +3 -1
  184. package/front_end/models/trace/insights/LCPDiscovery.ts +3 -1
  185. package/front_end/models/trace/insights/LegacyJavaScript.ts +3 -1
  186. package/front_end/models/trace/insights/ModernHTTP.ts +2 -1
  187. package/front_end/models/trace/insights/NetworkDependencyTree.ts +2 -1
  188. package/front_end/models/trace/insights/RenderBlocking.ts +5 -4
  189. package/front_end/models/trace/insights/SlowCSSSelector.ts +3 -1
  190. package/front_end/models/trace/insights/ThirdParties.ts +5 -3
  191. package/front_end/models/trace/insights/Viewport.ts +2 -1
  192. package/front_end/models/trace/lantern/simulation/Constants.ts +34 -2
  193. package/front_end/models/trace/lantern/simulation/DNSCache.ts +1 -1
  194. package/front_end/models/trace/types/TraceEvents.ts +8 -3
  195. package/front_end/models/trace_source_maps_resolver/SourceMapsResolver.ts +2 -1
  196. package/front_end/models/workspace/WorkspaceImpl.ts +18 -2
  197. package/front_end/panels/accessibility/AccessibilityAnnouncementRecordingView.ts +686 -2
  198. package/front_end/panels/accessibility/accessibilityNode.css +4 -4
  199. package/front_end/panels/accessibility/accessibilityProperties.css +1 -1
  200. package/front_end/panels/accessibility/accessibilitySidebarView.css +2 -2
  201. package/front_end/panels/accessibility/axBreadcrumbs.css +13 -13
  202. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +14 -14
  203. package/front_end/panels/ai_assistance/ExternalHandler.ts +89 -0
  204. package/front_end/panels/ai_assistance/aiAssistancePanel.css +1 -1
  205. package/front_end/panels/ai_assistance/ai_assistance.ts +1 -0
  206. package/front_end/panels/ai_assistance/components/ChatInput.ts +3 -2
  207. package/front_end/panels/ai_assistance/components/ChatView.ts +1 -1
  208. package/front_end/panels/ai_assistance/components/DisabledWidget.ts +1 -1
  209. package/front_end/panels/ai_assistance/components/ExploreWidget.ts +1 -1
  210. package/front_end/panels/ai_assistance/components/chatInput.css +12 -12
  211. package/front_end/panels/ai_assistance/components/chatMessage.css +16 -16
  212. package/front_end/panels/ai_assistance/components/chatView.css +2 -2
  213. package/front_end/panels/ai_assistance/components/exploreWidget.css +1 -1
  214. package/front_end/panels/ai_assistance/components/exportForAgentsDialog.css +4 -0
  215. package/front_end/panels/ai_assistance/components/walkthroughView.css +6 -6
  216. package/front_end/panels/animation/animationTimeline.css +62 -62
  217. package/front_end/panels/application/ApplicationPanelSidebar.ts +6 -1
  218. package/front_end/panels/application/BackgroundServiceModel.ts +4 -3
  219. package/front_end/panels/application/BackgroundServiceView.ts +290 -252
  220. package/front_end/panels/application/IndexedDBModel.ts +0 -7
  221. package/front_end/panels/application/WebMCPView.ts +15 -7
  222. package/front_end/panels/application/appManifestView.css +5 -5
  223. package/front_end/panels/application/backgroundServiceView.css +18 -2
  224. package/front_end/panels/application/components/AdsView.ts +100 -4
  225. package/front_end/panels/application/components/adsView.css +8 -5
  226. package/front_end/panels/application/components/backForwardCacheView.css +5 -5
  227. package/front_end/panels/application/components/bounceTrackingMitigationsView.css +1 -1
  228. package/front_end/panels/application/components/endpointsGrid.css +1 -1
  229. package/front_end/panels/application/components/permissionsPolicySection.css +1 -1
  230. package/front_end/panels/application/components/protocolHandlersView.css +6 -6
  231. package/front_end/panels/application/components/reportsGrid.css +1 -1
  232. package/front_end/panels/application/components/serviceWorkerRouterView.css +4 -4
  233. package/front_end/panels/application/components/trustTokensView.css +7 -7
  234. package/front_end/panels/application/cookieItemsView.css +3 -3
  235. package/front_end/panels/application/deviceBoundSessionsView.css +3 -3
  236. package/front_end/panels/application/frameDetailsReportView.css +11 -11
  237. package/front_end/panels/application/indexedDBViews.css +7 -7
  238. package/front_end/panels/application/originTrialTokenRows.css +2 -2
  239. package/front_end/panels/application/originTrialTreeView.css +3 -3
  240. package/front_end/panels/application/preloading/components/RuleSetDetailsView.css +2 -2
  241. package/front_end/panels/application/preloading/components/preloadingDetailsReportView.css +1 -1
  242. package/front_end/panels/application/preloading/components/preloadingDisabledInfobar.css +2 -2
  243. package/front_end/panels/application/preloading/components/preloadingGrid.css +2 -2
  244. package/front_end/panels/application/preloading/components/usedPreloadingView.css +7 -7
  245. package/front_end/panels/application/preloading/preloadingView.css +1 -1
  246. package/front_end/panels/application/preloading/preloadingViewDropDown.css +3 -3
  247. package/front_end/panels/application/serviceWorkerCacheViews.css +5 -5
  248. package/front_end/panels/application/serviceWorkerUpdateCycleView.css +12 -12
  249. package/front_end/panels/application/serviceWorkersView.css +15 -16
  250. package/front_end/panels/application/storageView.css +5 -5
  251. package/front_end/panels/application/webMCPView.css +9 -9
  252. package/front_end/panels/autofill/autofillView.css +4 -4
  253. package/front_end/panels/browser_debugger/categorizedBreakpointsSidebarPane.css +4 -4
  254. package/front_end/panels/browser_debugger/domBreakpointsSidebarPane.css +5 -3
  255. package/front_end/panels/browser_debugger/xhrBreakpointsSidebarPane.css +4 -4
  256. package/front_end/panels/changes/changesSidebar.css +1 -1
  257. package/front_end/panels/changes/changesView.css +1 -1
  258. package/front_end/panels/changes/combinedDiffView.css +1 -1
  259. package/front_end/panels/common/CPUThrottlingOption.ts +5 -10
  260. package/front_end/panels/common/badgeNotification.css +2 -2
  261. package/front_end/panels/common/domLinkifier.css +1 -1
  262. package/front_end/panels/common/gdpSignUpDialog.css +3 -3
  263. package/front_end/panels/common/geminiRebrandPromoDialog.css +1 -1
  264. package/front_end/panels/console/ConsolePrompt.ts +6 -3
  265. package/front_end/panels/console/ConsoleView.ts +23 -19
  266. package/front_end/panels/console/ConsoleViewMessage.ts +7 -2
  267. package/front_end/panels/console/console-meta.ts +9 -43
  268. package/front_end/panels/console/consoleContextSelector.css +2 -2
  269. package/front_end/panels/console/consolePinPane.css +9 -9
  270. package/front_end/panels/console/consolePrompt.css +5 -5
  271. package/front_end/panels/console/consoleView.css +39 -39
  272. package/front_end/panels/coverage/coverageListView.css +2 -2
  273. package/front_end/panels/css_overview/cssOverviewCompletedView.css +34 -34
  274. package/front_end/panels/css_overview/cssOverviewProcessingView.css +5 -5
  275. package/front_end/panels/css_overview/cssOverviewSidebarPanel.css +1 -1
  276. package/front_end/panels/css_overview/cssOverviewStartView.css +17 -17
  277. package/front_end/panels/elements/AdoptedStyleSheetTreeElement.ts +209 -62
  278. package/front_end/panels/elements/ColorSwatchPopoverIcon.ts +4 -4
  279. package/front_end/panels/elements/DOMTreeContextMenu.ts +59 -54
  280. package/front_end/panels/elements/ElementIssueUtils.ts +10 -2
  281. package/front_end/panels/elements/ElementsPanel.ts +5 -2
  282. package/front_end/panels/elements/ElementsTreeElement.ts +212 -255
  283. package/front_end/panels/elements/ElementsTreeOutline.ts +1804 -495
  284. package/front_end/panels/elements/ShortcutTreeElement.ts +6 -3
  285. package/front_end/panels/elements/StandaloneStylesContainer.ts +5 -11
  286. package/front_end/panels/elements/StylePropertiesSection.ts +65 -8
  287. package/front_end/panels/elements/StylePropertyTreeElement.ts +8 -0
  288. package/front_end/panels/elements/StylesContainer.ts +1 -0
  289. package/front_end/panels/elements/StylesSidebarPane.ts +317 -159
  290. package/front_end/panels/elements/classesPaneWidget.css +6 -6
  291. package/front_end/panels/elements/components/stylePropertyEditor.css +7 -7
  292. package/front_end/panels/elements/computedStyleWidget.css +1 -1
  293. package/front_end/panels/elements/elementStatePaneWidget.css +8 -8
  294. package/front_end/panels/elements/elements-meta.ts +14 -0
  295. package/front_end/panels/elements/elementsPanel.css +4 -4
  296. package/front_end/panels/elements/elementsTreeOutline.css +222 -38
  297. package/front_end/panels/elements/layersWidget.css +4 -4
  298. package/front_end/panels/elements/layoutPane.css +18 -18
  299. package/front_end/panels/elements/metricsSidebarPane.css +11 -11
  300. package/front_end/panels/elements/platformFontsWidget.css +5 -5
  301. package/front_end/panels/elements/propertiesWidget.css +2 -2
  302. package/front_end/panels/elements/stylePropertiesTreeOutline.css +20 -20
  303. package/front_end/panels/elements/stylesSidebarPane.css +37 -20
  304. package/front_end/panels/emulation/deviceModeView.css +49 -49
  305. package/front_end/panels/emulation/mediaQueryInspector.css +18 -18
  306. package/front_end/panels/event_listeners/eventListenersView.css +1 -1
  307. package/front_end/panels/explain/components/consoleInsight.css +29 -29
  308. package/front_end/panels/issues/components/elementsPanelLink.css +2 -2
  309. package/front_end/panels/issues/components/hideIssuesMenu.css +1 -1
  310. package/front_end/panels/issues/issuesPane.css +2 -2
  311. package/front_end/panels/issues/issuesTree.css +50 -50
  312. package/front_end/panels/layer_viewer/Layers3DView.ts +9 -10
  313. package/front_end/panels/layer_viewer/layerDetailsView.css +1 -1
  314. package/front_end/panels/layer_viewer/layerTreeOutline.css +2 -2
  315. package/front_end/panels/layer_viewer/layers3DView.css +1 -1
  316. package/front_end/panels/layer_viewer/paintProfiler.css +1 -1
  317. package/front_end/panels/lighthouse/LighthouseReportSelector.ts +2 -2
  318. package/front_end/panels/media/playerListView.css +9 -9
  319. package/front_end/panels/media/playerMessagesView.css +8 -8
  320. package/front_end/panels/media/playerPropertiesView.css +6 -6
  321. package/front_end/panels/mobile_throttling/NetworkPanelIndicator.ts +1 -1
  322. package/front_end/panels/mobile_throttling/throttlingSettingsTab.css +9 -9
  323. package/front_end/panels/network/NetworkDataGridNode.ts +1 -0
  324. package/front_end/panels/network/NetworkPanel.ts +9 -4
  325. package/front_end/panels/network/RequestConditionsDrawer.ts +12 -12
  326. package/front_end/panels/network/RequestPayloadView.ts +78 -26
  327. package/front_end/panels/network/binaryResourceView.css +1 -1
  328. package/front_end/panels/network/components/EditableSpan.css +3 -3
  329. package/front_end/panels/network/components/HeaderSectionRow.css +14 -14
  330. package/front_end/panels/network/components/RequestHeaderSection.css +7 -7
  331. package/front_end/panels/network/components/RequestHeadersView.css +18 -18
  332. package/front_end/panels/network/components/RequestTrustTokensView.css +1 -1
  333. package/front_end/panels/network/network-meta.ts +9 -9
  334. package/front_end/panels/network/networkConfigView.css +11 -11
  335. package/front_end/panels/network/networkLogView.css +35 -35
  336. package/front_end/panels/network/networkManageCustomHeadersView.css +1 -1
  337. package/front_end/panels/network/networkPanel.css +7 -7
  338. package/front_end/panels/network/networkTimingTable.css +13 -13
  339. package/front_end/panels/network/requestConditionsDrawer.css +4 -4
  340. package/front_end/panels/network/requestCookiesView.css +5 -5
  341. package/front_end/panels/network/requestDeviceBoundSessionsView.css +1 -1
  342. package/front_end/panels/network/requestHTMLView.css +1 -1
  343. package/front_end/panels/network/requestHeadersTree.css +19 -19
  344. package/front_end/panels/network/requestInitiatorView.css +1 -1
  345. package/front_end/panels/network/requestInitiatorViewTree.css +3 -3
  346. package/front_end/panels/network/requestPayloadTree.css +5 -5
  347. package/front_end/panels/network/requestPayloadView.css +1 -1
  348. package/front_end/panels/network/resourceChunkView.css +3 -3
  349. package/front_end/panels/network/signedExchangeInfoTree.css +6 -6
  350. package/front_end/panels/profiler/heapProfiler.css +15 -15
  351. package/front_end/panels/profiler/profileLauncherView.css +10 -10
  352. package/front_end/panels/recorder/RecordingView.ts +1 -1
  353. package/front_end/panels/recorder/StepEditor.ts +86 -7
  354. package/front_end/panels/recorder/TimelineSection.ts +2 -1
  355. package/front_end/panels/recorder/createRecordingView.css +17 -17
  356. package/front_end/panels/recorder/extensionView.css +5 -5
  357. package/front_end/panels/recorder/injected/RecordingClient.ts +1 -1
  358. package/front_end/panels/recorder/models/SchemaUtils.ts +2 -2
  359. package/front_end/panels/recorder/models/ScreenshotStorage.ts +1 -1
  360. package/front_end/panels/recorder/recorderPanel.css +7 -7
  361. package/front_end/panels/recorder/recordingListView.css +12 -12
  362. package/front_end/panels/recorder/recordingView.css +47 -47
  363. package/front_end/panels/recorder/stepEditor.css +2 -2
  364. package/front_end/panels/recorder/stepView.css +20 -20
  365. package/front_end/panels/recorder/timelineSection.css +9 -9
  366. package/front_end/panels/screencast/screencastView.css +12 -12
  367. package/front_end/panels/search/searchResultsPane.css +10 -10
  368. package/front_end/panels/search/searchView.css +9 -9
  369. package/front_end/panels/security/SecurityPanel.ts +84 -54
  370. package/front_end/panels/security/SecurityPanelSidebar.ts +0 -32
  371. package/front_end/panels/security/mainView.css +26 -26
  372. package/front_end/panels/security/originView.css +15 -15
  373. package/front_end/panels/sensors/locationsSettingsTab.css +6 -6
  374. package/front_end/panels/sensors/sensors.css +28 -28
  375. package/front_end/panels/settings/KeybindsSettingsTab.ts +5 -2
  376. package/front_end/panels/settings/components/syncSection.css +5 -5
  377. package/front_end/panels/settings/emulation/components/userAgentClientHintsForm.css +13 -13
  378. package/front_end/panels/settings/emulation/devicesSettingsTab.css +16 -16
  379. package/front_end/panels/settings/frameworkIgnoreListSettingsTab.css +2 -2
  380. package/front_end/panels/settings/keybindsSettingsTab.css +2 -2
  381. package/front_end/panels/settings/settingsScreen.css +5 -5
  382. package/front_end/panels/sources/UISourceCodeFrame.ts +1 -1
  383. package/front_end/panels/sources/breakpointEditDialog.css +9 -9
  384. package/front_end/panels/sources/breakpointsView.css +25 -25
  385. package/front_end/panels/sources/callStackSidebarPane.css +10 -10
  386. package/front_end/panels/sources/components/HeadersView.css +6 -6
  387. package/front_end/panels/sources/debuggerPausedMessage.css +4 -4
  388. package/front_end/panels/sources/dialog.css +2 -2
  389. package/front_end/panels/sources/navigatorTree.css +2 -2
  390. package/front_end/panels/sources/navigatorView.css +2 -2
  391. package/front_end/panels/sources/scopeChainSidebarPane.css +1 -1
  392. package/front_end/panels/sources/sourcesNavigator.css +1 -1
  393. package/front_end/panels/sources/sourcesPanel.css +2 -2
  394. package/front_end/panels/sources/sourcesView.css +1 -1
  395. package/front_end/panels/sources/threadsSidebarPane.css +5 -5
  396. package/front_end/panels/sources/watchExpressionsSidebarPane.css +8 -8
  397. package/front_end/panels/timeline/TimelineDetailsView.ts +9 -21
  398. package/front_end/panels/timeline/TimelineFlameChartView.ts +3 -5
  399. package/front_end/panels/timeline/TimelineMiniMap.ts +3 -3
  400. package/front_end/panels/timeline/components/BreadcrumbsUI.ts +2 -6
  401. package/front_end/panels/timeline/components/CWVMetrics.ts +9 -10
  402. package/front_end/panels/timeline/components/DetailsView.ts +12 -16
  403. package/front_end/panels/timeline/components/ExportTraceOptions.ts +13 -13
  404. package/front_end/panels/timeline/components/FieldSettingsDialog.ts +19 -19
  405. package/front_end/panels/timeline/components/IgnoreListSetting.ts +7 -9
  406. package/front_end/panels/timeline/components/InteractionBreakdown.ts +3 -3
  407. package/front_end/panels/timeline/components/LayoutShiftDetails.ts +13 -13
  408. package/front_end/panels/timeline/components/LiveMetricsView.ts +65 -65
  409. package/front_end/panels/timeline/components/MetricCard.ts +25 -25
  410. package/front_end/panels/timeline/components/NetworkRequestDetails.ts +22 -22
  411. package/front_end/panels/timeline/components/NetworkRequestTooltip.ts +9 -9
  412. package/front_end/panels/timeline/components/OriginMap.ts +7 -7
  413. package/front_end/panels/timeline/components/RelatedInsightChips.ts +2 -2
  414. package/front_end/panels/timeline/components/SidebarAnnotationsTab.ts +19 -20
  415. package/front_end/panels/timeline/components/SidebarSingleInsightSet.ts +1 -1
  416. package/front_end/panels/timeline/components/TimelineSummary.ts +2 -2
  417. package/front_end/panels/timeline/components/Utils.ts +2 -6
  418. package/front_end/panels/timeline/components/breadcrumbsUI.css +3 -3
  419. package/front_end/panels/timeline/components/exportTraceOptions.css +1 -1
  420. package/front_end/panels/timeline/components/fieldSettingsDialog.css +13 -13
  421. package/front_end/panels/timeline/components/ignoreListSetting.css +3 -3
  422. package/front_end/panels/timeline/components/insights/BaseInsightComponent.ts +6 -9
  423. package/front_end/panels/timeline/components/insights/Checklist.ts +2 -2
  424. package/front_end/panels/timeline/components/insights/Table.ts +1 -1
  425. package/front_end/panels/timeline/components/liveMetricsView.css +39 -39
  426. package/front_end/panels/timeline/components/metricCard.css +20 -20
  427. package/front_end/panels/timeline/components/networkRequestDetails.css +9 -9
  428. package/front_end/panels/timeline/components/networkRequestTooltip.css +14 -14
  429. package/front_end/panels/timeline/components/networkTrackWidget.css +2 -2
  430. package/front_end/panels/timeline/components/originMap.css +4 -4
  431. package/front_end/panels/timeline/components/relatedInsightChips.css +1 -1
  432. package/front_end/panels/timeline/components/sidebarAnnotationsTab.css +4 -4
  433. package/front_end/panels/timeline/components/sidebarInsightsTab.css +5 -5
  434. package/front_end/panels/timeline/components/sidebarSingleInsightSet.css +1 -1
  435. package/front_end/panels/timeline/components/timelineSummary.css +4 -4
  436. package/front_end/panels/timeline/overlays/OverlaysImpl.ts +4 -4
  437. package/front_end/panels/timeline/overlays/components/EntriesLinkOverlay.ts +1 -1
  438. package/front_end/panels/timeline/overlays/components/EntryLabelOverlay.ts +6 -6
  439. package/front_end/panels/timeline/overlays/components/TimeRangeOverlay.ts +1 -1
  440. package/front_end/panels/timeline/thirdPartyTreeView.css +5 -5
  441. package/front_end/panels/timeline/timeline-meta.ts +0 -6
  442. package/front_end/panels/timeline/timelineDetailsView.css +11 -11
  443. package/front_end/panels/timeline/timelinePanel.css +20 -20
  444. package/front_end/panels/timeline/timelineTreeView.css +8 -8
  445. package/front_end/panels/webauthn/webauthnPane.css +6 -6
  446. package/front_end/panels/whats_new/ReleaseNoteText.ts +2 -2
  447. package/front_end/third_party/chromium/README.chromium +1 -1
  448. package/front_end/third_party/i18n/i18n-impl.ts +1 -1
  449. package/front_end/third_party/puppeteer/README.chromium +2 -2
  450. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.d.ts +15 -1
  451. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +42 -8
  452. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Frame.d.ts +4 -0
  453. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Frame.d.ts.map +1 -1
  454. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Frame.js +15 -2
  455. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Frame.js.map +1 -1
  456. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/WebMCP.d.ts +14 -1
  457. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/WebMCP.d.ts.map +1 -1
  458. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/WebMCP.js +23 -1
  459. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/WebMCP.js.map +1 -1
  460. package/front_end/third_party/puppeteer/package/lib/puppeteer/generated/injected.d.ts +1 -1
  461. package/front_end/third_party/puppeteer/package/lib/puppeteer/generated/injected.d.ts.map +1 -1
  462. package/front_end/third_party/puppeteer/package/lib/puppeteer/generated/injected.js +1 -1
  463. package/front_end/third_party/puppeteer/package/lib/puppeteer/generated/injected.js.map +1 -1
  464. package/front_end/third_party/puppeteer/package/lib/puppeteer/injected/Poller.d.ts.map +1 -1
  465. package/front_end/third_party/puppeteer/package/lib/puppeteer/injected/Poller.js +77 -6
  466. package/front_end/third_party/puppeteer/package/lib/puppeteer/injected/Poller.js.map +1 -1
  467. package/front_end/third_party/puppeteer/package/lib/puppeteer/revisions.d.ts +3 -3
  468. package/front_end/third_party/puppeteer/package/lib/puppeteer/revisions.js +3 -3
  469. package/front_end/third_party/puppeteer/package/lib/puppeteer/revisions.js.map +1 -1
  470. package/front_end/third_party/puppeteer/package/lib/puppeteer/util/version.d.ts +1 -1
  471. package/front_end/third_party/puppeteer/package/lib/puppeteer/util/version.js +1 -1
  472. package/front_end/third_party/puppeteer/package/lib/types.d.ts +15 -1
  473. package/front_end/third_party/puppeteer/package/package.json +2 -2
  474. package/front_end/third_party/puppeteer/package/src/cdp/Frame.ts +16 -2
  475. package/front_end/third_party/puppeteer/package/src/cdp/WebMCP.ts +36 -1
  476. package/front_end/third_party/puppeteer/package/src/generated/injected.ts +1 -1
  477. package/front_end/third_party/puppeteer/package/src/injected/Poller.ts +86 -6
  478. package/front_end/third_party/puppeteer/package/src/revisions.ts +3 -3
  479. package/front_end/third_party/puppeteer/package/src/util/version.ts +1 -1
  480. package/front_end/third_party/web-vitals/README.chromium +2 -2
  481. package/front_end/third_party/web-vitals/package/README.md +22 -7
  482. package/front_end/third_party/web-vitals/package/dist/modules/attribution/onINP.js +6 -2
  483. package/front_end/third_party/web-vitals/package/dist/modules/attribution/onLCP.d.ts +2 -2
  484. package/front_end/third_party/web-vitals/package/dist/modules/attribution/onLCP.js +30 -3
  485. package/front_end/third_party/web-vitals/package/dist/modules/lib/InteractionManager.d.ts +14 -0
  486. package/front_end/third_party/web-vitals/package/dist/modules/lib/InteractionManager.js +19 -13
  487. package/front_end/third_party/web-vitals/package/dist/modules/lib/bindReporter.d.ts +1 -1
  488. package/front_end/third_party/web-vitals/package/dist/modules/lib/doubleRAF.js +1 -1
  489. package/front_end/third_party/web-vitals/package/dist/modules/lib/initMetric.d.ts +3 -3
  490. package/front_end/third_party/web-vitals/package/dist/modules/lib/softNavs.js +3 -1
  491. package/front_end/third_party/web-vitals/package/dist/modules/types/lcp.d.ts +15 -1
  492. package/front_end/third_party/web-vitals/package/package.json +1 -2
  493. package/front_end/third_party/web-vitals/package/src/attribution/onINP.ts +9 -2
  494. package/front_end/third_party/web-vitals/package/src/attribution/onLCP.ts +36 -5
  495. package/front_end/third_party/web-vitals/package/src/lib/InteractionManager.ts +21 -15
  496. package/front_end/third_party/web-vitals/package/src/lib/doubleRAF.ts +1 -1
  497. package/front_end/third_party/web-vitals/package/src/lib/softNavs.ts +4 -1
  498. package/front_end/third_party/web-vitals/package/src/types/lcp.ts +16 -1
  499. package/front_end/third_party/web-vitals/rebuild.sh +3 -3
  500. package/front_end/ui/comments/CommentAnchorResolver.ts +109 -4
  501. package/front_end/ui/comments/CommentOverlayManager.ts +16 -32
  502. package/front_end/ui/comments/commentsOverlay.css +6 -5
  503. package/front_end/ui/components/adorners/Adorner.ts +1 -1
  504. package/front_end/ui/components/adorners/adorner.css +6 -6
  505. package/front_end/ui/components/buttons/FloatingButton.ts +1 -1
  506. package/front_end/ui/components/buttons/button.css +9 -9
  507. package/front_end/ui/components/buttons/floatingButton.css +2 -2
  508. package/front_end/ui/components/buttons/textButton.css +12 -12
  509. package/front_end/ui/components/dialogs/Dialog.ts +1 -1
  510. package/front_end/ui/components/dialogs/dialog.css +1 -1
  511. package/front_end/ui/components/diff_view/diffView.css +3 -3
  512. package/front_end/ui/components/expandable_list/expandableList.css +9 -9
  513. package/front_end/ui/components/highlighting/HighlightElement.ts +1 -1
  514. package/front_end/ui/components/highlighting/HighlightManager.ts +4 -0
  515. package/front_end/ui/components/icon_button/iconButton.css +2 -2
  516. package/front_end/ui/components/input/checkbox.css +8 -8
  517. package/front_end/ui/components/input/textInput.css +3 -3
  518. package/front_end/ui/components/issue_counter/issueLinkIcon.css +2 -2
  519. package/front_end/ui/components/linkifier/linkifierImpl.css +1 -1
  520. package/front_end/ui/components/markdown_view/MarkdownImagesMap.ts +1 -1
  521. package/front_end/ui/components/markdown_view/MarkdownLinksMap.ts +1 -1
  522. package/front_end/ui/components/markdown_view/codeBlock.css +2 -2
  523. package/front_end/ui/components/markdown_view/markdownView.css +7 -7
  524. package/front_end/ui/components/menus/menuGroup.css +2 -2
  525. package/front_end/ui/components/menus/menuItem.css +2 -2
  526. package/front_end/ui/components/menus/selectMenu.css +2 -2
  527. package/front_end/ui/components/menus/selectMenuButton.css +4 -4
  528. package/front_end/ui/components/panel_feedback/panelFeedback.css +15 -15
  529. package/front_end/ui/components/panel_feedback/previewToggle.css +2 -2
  530. package/front_end/ui/components/panel_introduction_steps/panelIntroductionSteps.css +7 -7
  531. package/front_end/ui/components/report_view/report.css +3 -3
  532. package/front_end/ui/components/report_view/reportSectionDivider.css +1 -1
  533. package/front_end/ui/components/report_view/reportValue.css +1 -1
  534. package/front_end/ui/components/request_link_icon/requestLinkIcon.css +2 -2
  535. package/front_end/ui/components/settings/settingCheckbox.css +6 -6
  536. package/front_end/ui/components/text_editor/TextEditor.ts +3 -1
  537. package/front_end/ui/components/text_editor/config.ts +16 -4
  538. package/front_end/ui/kit/icons/icon.css +4 -4
  539. package/front_end/ui/kit/link/link.css +1 -1
  540. package/front_end/ui/legacy/ActionRegistration.ts +23 -23
  541. package/front_end/ui/legacy/Dialog.ts +1 -1
  542. package/front_end/ui/legacy/DockController.ts +7 -4
  543. package/front_end/ui/legacy/EmptyWidget.ts +13 -34
  544. package/front_end/ui/legacy/FilterBar.ts +4 -4
  545. package/front_end/ui/legacy/Infobar.ts +2 -2
  546. package/front_end/ui/legacy/InspectorDrawerView.ts +6 -6
  547. package/front_end/ui/legacy/InspectorView.ts +22 -27
  548. package/front_end/ui/legacy/KeyboardShortcut.ts +4 -7
  549. package/front_end/ui/legacy/ListWidget.ts +7 -7
  550. package/front_end/ui/legacy/PlusButton.ts +1 -3
  551. package/front_end/ui/legacy/RemoteDebuggingTerminatedScreen.ts +6 -6
  552. package/front_end/ui/legacy/SearchableView.ts +18 -17
  553. package/front_end/ui/legacy/ShortcutRegistry.ts +6 -3
  554. package/front_end/ui/legacy/SoftContextMenu.ts +8 -8
  555. package/front_end/ui/legacy/SoftDropDown.ts +1 -1
  556. package/front_end/ui/legacy/SuggestBox.ts +2 -2
  557. package/front_end/ui/legacy/TabbedPane.ts +10 -10
  558. package/front_end/ui/legacy/TargetCrashedScreen.ts +2 -2
  559. package/front_end/ui/legacy/Toolbar.ts +5 -5
  560. package/front_end/ui/legacy/Treeoutline.ts +1 -1
  561. package/front_end/ui/legacy/UIUtils.ts +90 -26
  562. package/front_end/ui/legacy/ViewManager.ts +1 -1
  563. package/front_end/ui/legacy/ViewRegistration.ts +7 -7
  564. package/front_end/ui/legacy/Widget.ts +5 -5
  565. package/front_end/ui/legacy/checkboxTextLabel.css +3 -3
  566. package/front_end/ui/legacy/components/color_picker/ColorFormatSpec.ts +16 -0
  567. package/front_end/ui/legacy/components/color_picker/FormatPickerContextMenu.ts +1 -0
  568. package/front_end/ui/legacy/components/color_picker/spectrum.css +90 -90
  569. package/front_end/ui/legacy/components/cookie_table/CookiesTable.ts +23 -24
  570. package/front_end/ui/legacy/components/cookie_table/cookiesTable.css +1 -1
  571. package/front_end/ui/legacy/components/data_grid/DataGrid.ts +26 -28
  572. package/front_end/ui/legacy/components/data_grid/ShowMoreDataGridNode.ts +3 -7
  573. package/front_end/ui/legacy/components/inline_editor/bezierEditor.css +13 -13
  574. package/front_end/ui/legacy/components/inline_editor/colorSwatch.css +7 -7
  575. package/front_end/ui/legacy/components/inline_editor/cssAngleEditor.css +5 -5
  576. package/front_end/ui/legacy/components/inline_editor/cssShadowEditor.css +18 -18
  577. package/front_end/ui/legacy/components/object_ui/CustomPreviewComponent.ts +104 -86
  578. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +8 -6
  579. package/front_end/ui/legacy/components/object_ui/objectPopover.css +4 -4
  580. package/front_end/ui/legacy/components/object_ui/objectPropertiesSection.css +6 -6
  581. package/front_end/ui/legacy/components/perf_ui/BrickBreaker.ts +2 -2
  582. package/front_end/ui/legacy/components/perf_ui/ChartViewport.ts +28 -11
  583. package/front_end/ui/legacy/components/perf_ui/FilmStripView.ts +6 -6
  584. package/front_end/ui/legacy/components/perf_ui/FlameChart.ts +14 -15
  585. package/front_end/ui/legacy/components/perf_ui/GCActionDelegate.ts +1 -1
  586. package/front_end/ui/legacy/components/perf_ui/NetworkPriorities.ts +5 -5
  587. package/front_end/ui/legacy/components/perf_ui/OverviewGrid.ts +5 -5
  588. package/front_end/ui/legacy/components/perf_ui/PieChart.ts +1 -1
  589. package/front_end/ui/legacy/components/perf_ui/TimelineOverviewPane.ts +0 -4
  590. package/front_end/ui/legacy/components/perf_ui/chartViewport.css +4 -4
  591. package/front_end/ui/legacy/components/perf_ui/filmStripView.css +6 -6
  592. package/front_end/ui/legacy/components/perf_ui/flameChart.css +7 -7
  593. package/front_end/ui/legacy/components/perf_ui/overviewGrid.css +11 -11
  594. package/front_end/ui/legacy/components/perf_ui/perf_ui-meta.ts +4 -6
  595. package/front_end/ui/legacy/components/perf_ui/pieChart.css +6 -6
  596. package/front_end/ui/legacy/components/perf_ui/timelineGrid.css +5 -5
  597. package/front_end/ui/legacy/components/quick_open/filteredListWidget.css +3 -3
  598. package/front_end/ui/legacy/components/source_frame/FontView.ts +2 -2
  599. package/front_end/ui/legacy/components/source_frame/ImageView.ts +13 -13
  600. package/front_end/ui/legacy/components/source_frame/JSONView.ts +1 -1
  601. package/front_end/ui/legacy/components/source_frame/PreviewFactory.ts +2 -2
  602. package/front_end/ui/legacy/components/source_frame/ResourceSourceFrame.ts +2 -2
  603. package/front_end/ui/legacy/components/source_frame/SourceFrame.ts +15 -18
  604. package/front_end/ui/legacy/components/source_frame/XMLView.ts +1 -1
  605. package/front_end/ui/legacy/components/source_frame/imageView.css +1 -1
  606. package/front_end/ui/legacy/components/source_frame/jsonView.css +1 -1
  607. package/front_end/ui/legacy/components/source_frame/resourceSourceFrame.css +1 -1
  608. package/front_end/ui/legacy/components/source_frame/source_frame-meta.ts +10 -10
  609. package/front_end/ui/legacy/components/source_frame/xmlTree.css +3 -3
  610. package/front_end/ui/legacy/components/utils/imagePreview.css +1 -1
  611. package/front_end/ui/legacy/components/utils/jsUtils.css +10 -10
  612. package/front_end/ui/legacy/dropTarget.css +2 -2
  613. package/front_end/ui/legacy/emptyWidget.css +6 -0
  614. package/front_end/ui/legacy/filter.css +19 -19
  615. package/front_end/ui/legacy/infobar.css +2 -2
  616. package/front_end/ui/legacy/inspectorCommon.css +67 -67
  617. package/front_end/ui/legacy/inspectorDrawerTabbedPane.css +1 -1
  618. package/front_end/ui/legacy/listWidget.css +5 -5
  619. package/front_end/ui/legacy/progressIndicator.css +4 -4
  620. package/front_end/ui/legacy/reportView.css +7 -7
  621. package/front_end/ui/legacy/searchableView.css +5 -5
  622. package/front_end/ui/legacy/smallBubble.css +3 -3
  623. package/front_end/ui/legacy/softContextMenu.css +4 -4
  624. package/front_end/ui/legacy/softDropDownButton.css +1 -1
  625. package/front_end/ui/legacy/splitWidget.css +9 -31
  626. package/front_end/ui/legacy/suggestBox.css +4 -4
  627. package/front_end/ui/legacy/tabbedPane.css +17 -17
  628. package/front_end/ui/legacy/textPrompt.css +2 -2
  629. package/front_end/ui/legacy/toolbar.css +4 -4
  630. package/front_end/ui/legacy/treeoutline.css +14 -14
  631. package/front_end/ui/legacy/viewContainers.css +6 -6
  632. package/front_end/ui/settings/ConsoleSettings.ts +66 -0
  633. package/front_end/ui/settings/MainSettings.ts +47 -0
  634. package/front_end/ui/settings/settings.ts +2 -0
  635. package/front_end/ui/visual_logging/KnownContextValues.ts +12 -0
  636. package/inspector_overlay/common.ts +12 -12
  637. package/inspector_overlay/css_grid_label_helpers.ts +16 -17
  638. package/inspector_overlay/drag_resize_handler.ts +2 -2
  639. package/inspector_overlay/highlight_common.ts +11 -13
  640. package/inspector_overlay/highlight_container_query.ts +2 -2
  641. package/inspector_overlay/highlight_flex_common.ts +6 -7
  642. package/inspector_overlay/highlight_grid_common.ts +3 -3
  643. package/inspector_overlay/highlight_isolated_element.ts +12 -3
  644. package/inspector_overlay/highlight_scroll_snap.ts +2 -2
  645. package/inspector_overlay/loadCSS.rollup.js +0 -1
  646. package/inspector_overlay/testing/InspectorOverlayHelpers.ts +13 -13
  647. package/inspector_overlay/tool_green_dev_anchors.ts +3 -3
  648. package/inspector_overlay/tool_highlight.ts +9 -9
  649. package/inspector_overlay/tool_paused.ts +4 -4
  650. package/inspector_overlay/tool_persistent.ts +11 -11
  651. package/inspector_overlay/tool_screenshot.ts +7 -7
  652. package/inspector_overlay/tool_source_order.ts +4 -4
  653. package/inspector_overlay/tool_viewport_size.ts +3 -3
  654. package/inspector_overlay/tool_window_controls.ts +4 -4
  655. package/mcp/mcp.ts +1 -0
  656. package/package.json +2 -2
  657. package/front_end/generated/protocol-tsconfig.json +0 -10
  658. package/front_end/panels/js_timeline/DIR_METADATA +0 -3
  659. package/front_end/panels/js_timeline/js_timeline-meta.ts +0 -193
  660. package/front_end/panels/js_timeline/js_timeline.ts +0 -1
  661. /package/front_end/entrypoints/{formatter_worker/FormatterActions.ts → formatter_actions/formatter_actions.ts} +0 -0
@@ -35,29 +35,45 @@
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';
41
42
  import * as Elements from '../../models/elements/elements.js';
42
- import * as IssuesManager from '../../models/issues_manager/issues_manager.js';
43
43
  import * as CodeHighlighter from '../../ui/components/code_highlighter/code_highlighter.js';
44
44
  import * as Highlighting from '../../ui/components/highlighting/highlighting.js';
45
45
  import * as IssueCounter from '../../ui/components/issue_counter/issue_counter.js';
46
46
  import * as UIComponentUtils from '../../ui/legacy/components/utils/utils.js';
47
47
  import * as UI from '../../ui/legacy/legacy.js';
48
- import {html, nothing, render} from '../../ui/lit/lit.js';
48
+ import * as Lit from '../../ui/lit/lit.js';
49
49
  import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
50
50
 
51
- 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';
52
58
  import {showContextMenu} from './DOMTreeContextMenu.js';
53
59
  import {getElementIssueDetails} from './ElementIssueUtils.js';
54
- 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';
55
70
  import elementsTreeOutlineStyles from './elementsTreeOutline.css.js';
56
71
  import {ImagePreviewPopover} from './ImagePreviewPopover.js';
57
- import type {MarkerDecoratorRegistration} from './MarkerDecorator.js';
58
72
  import {ShortcutTreeElement} from './ShortcutTreeElement.js';
59
73
  import {TopLayerContainer} from './TopLayerContainer.js';
60
74
 
75
+ const {html, nothing, render, Directives: {classMap}} = Lit;
76
+
61
77
  const UIStrings = {
62
78
  /**
63
79
  * @description ARIA accessible name in the DOM tree outline of the Elements panel.
@@ -77,6 +93,10 @@ const UIStrings = {
77
93
  * @description Text for popover that directs to the Issues panel.
78
94
  */
79
95
  viewIssue: 'View issue:',
96
+ /**
97
+ * @description Link text content in the DOM tree outline of the Elements panel.
98
+ */
99
+ reveal: 'reveal',
80
100
  } as const;
81
101
  const str_ = i18n.i18n.registerUIStrings('panels/elements/ElementsTreeOutline.ts', UIStrings);
82
102
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
@@ -84,10 +104,12 @@ const elementsTreeOutlineByDOMModel = new WeakMap<SDK.DOMModel.DOMModel, Element
84
104
 
85
105
  const populatedTreeElements = new WeakSet<ElementsTreeElement>();
86
106
 
87
- export type View = typeof DEFAULT_VIEW;
107
+ export type View = (input: ViewInput, output: ViewOutput, target: HTMLElement) => void;
88
108
  export {elementsTreeOutlineStyles};
89
109
 
90
110
  interface ViewInput {
111
+ domTreeWidget?: DOMTreeWidget;
112
+ rootDOMNode: SDK.DOMModel.DOMNode|null;
91
113
  omitRootDOMNode: boolean;
92
114
  selectEnabled: boolean;
93
115
  hideGutter: boolean;
@@ -105,6 +127,9 @@ interface ViewInput {
105
127
  preventTabOrder: boolean;
106
128
  deindentSingleNode: boolean;
107
129
  currentHighlightedNode: SDK.DOMModel.DOMNode|null;
130
+ hoveredNode?: SDK.DOMModel.DOMNode|null;
131
+ searchMatchNode?: SDK.DOMModel.DOMNode|null;
132
+ searchMatchQuery?: string|null;
108
133
 
109
134
  selectedNode: SDK.DOMModel.DOMNode|null;
110
135
 
@@ -113,12 +138,55 @@ interface ViewInput {
113
138
  onElementsTreeUpdated: (event: Common.EventTarget.EventTargetEvent<SDK.DOMModel.DOMNode[]>) => void;
114
139
  onElementCollapsed: () => void;
115
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;
116
182
  }
117
183
 
118
184
  interface ViewOutput {
119
185
  elementsTreeOutline?: ElementsTreeOutline;
120
186
  imagePreviewPopover?: ImagePreviewPopover;
121
187
  highlightedTreeElement: ElementsTreeElement|null;
188
+ searchMatchTreeElement?: ElementsTreeElement|null;
189
+ searchMatchQuery?: string;
122
190
  isUpdatingHighlights: boolean;
123
191
  alreadyExpandedParentTreeElement: ElementsTreeElement|null;
124
192
  }
@@ -128,9 +196,9 @@ export const DEFAULT_VIEW = (input: ViewInput, output: ViewOutput, target: HTMLE
128
196
  // FIXME: this is basically a ref to existing imperative
129
197
  // implementation. Once this is declarative the ref should not be
130
198
  // needed.
131
- const elementsTreeOutline = new ElementsTreeOutline(input.omitRootDOMNode, input.selectEnabled, input.hideGutter,
132
- input.maxTreeDepth, input.enableContextMenu, input.showComments,
133
- 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);
134
202
  output.elementsTreeOutline = elementsTreeOutline;
135
203
  elementsTreeOutline.addEventListener(ElementsTreeOutline.Events.SelectedNodeChanged, input.onSelectedNodeChanged,
136
204
  this);
@@ -147,9 +215,21 @@ export const DEFAULT_VIEW = (input: ViewInput, output: ViewOutput, target: HTMLE
147
215
  elementsTreeOutline.elementInternal.addEventListener('contextmenu', (event: MouseEvent) => {
148
216
  const treeElement = elementsTreeOutline.treeElementFromEventInternal(event);
149
217
  if (treeElement instanceof ElementsTreeElement) {
150
- output.elementsTreeOutline?.showContextMenu(treeElement, event);
218
+ input.onContextMenu?.(treeElement.node(), event, treeElement.widget);
151
219
  }
152
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);
153
233
  output.imagePreviewPopover = new ImagePreviewPopover(
154
234
  elementsTreeOutline.contentElement,
155
235
  event => {
@@ -176,8 +256,27 @@ export const DEFAULT_VIEW = (input: ViewInput, output: ViewOutput, target: HTMLE
176
256
  return null;
177
257
  }
178
258
 
179
- const issues = elementsTreeOutline.issuesByNodeElement(hoveredNode);
180
- if (!issues) {
259
+ const listItem = UI.UIUtils.enclosingNodeOrSelfWithNodeName(hoveredNode, 'li');
260
+ if (!listItem) {
261
+ return null;
262
+ }
263
+ const treeElement =
264
+ UI.TreeOutline.TreeElement.getTreeElementBylistItemNode(listItem) as ElementsTreeElement | undefined;
265
+ const node = treeElement?.node();
266
+ if (!node) {
267
+ return null;
268
+ }
269
+ let issues = treeElement?.widget?.issues ?? [];
270
+ if (hoveredNode.classList.contains('webkit-html-attribute-name')) {
271
+ const attrName = hoveredNode.textContent;
272
+ issues = issues.filter(issue => getElementIssueDetails(issue)?.attribute === attrName);
273
+ } else if (hoveredNode.classList.contains('webkit-html-tag-name')) {
274
+ issues = issues.filter(issue => {
275
+ const details = getElementIssueDetails(issue);
276
+ return Boolean(details && !details.attribute);
277
+ });
278
+ }
279
+ if (issues.length === 0) {
181
280
  return null;
182
281
  }
183
282
 
@@ -191,7 +290,6 @@ export const DEFAULT_VIEW = (input: ViewInput, output: ViewOutput, target: HTMLE
191
290
  ${issues.map(issue => {
192
291
  const elementIssueDetails = getElementIssueDetails(issue);
193
292
  if (!elementIssueDetails) {
194
- // This shouldn't happen, but add this if check to pass ts check.
195
293
  return nothing;
196
294
  }
197
295
  const issueKindIconName = IssueCounter.IssueCounter.getIssueKindIconName(issue.getKind());
@@ -216,7 +314,9 @@ export const DEFAULT_VIEW = (input: ViewInput, output: ViewOutput, target: HTMLE
216
314
  output.elementsTreeOutline.maxTreeDepth = input.maxTreeDepth;
217
315
  output.elementsTreeOutline.enableContextMenu = input.enableContextMenu ?? true;
218
316
  output.elementsTreeOutline.showContextMenu = (treeElement, event) => {
219
- void showContextMenu(treeElement, event);
317
+ if (event instanceof MouseEvent) {
318
+ input.onContextMenu?.(treeElement.node(), event, treeElement.widget);
319
+ }
220
320
  };
221
321
  let needsUpdate = false;
222
322
  const showComments = input.showComments ?? true;
@@ -292,6 +392,578 @@ export const DEFAULT_VIEW = (input: ViewInput, output: ViewOutput, target: HTMLE
292
392
  treeElement?.reveal(true);
293
393
  output.isUpdatingHighlights = false;
294
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 */
295
967
  };
296
968
 
297
969
  /**
@@ -312,6 +984,8 @@ export class DOMTreeWidget extends UI.Widget.Widget {
312
984
  (event:
313
985
  Common.EventTarget.EventTargetEvent<{node: SDK.DOMModel.DOMNode | null, focus: boolean}>) => void = () => {};
314
986
  onElementsTreeUpdated: (event: Common.EventTarget.EventTargetEvent<SDK.DOMModel.DOMNode[]>) => void = () => {};
987
+ onElementCollapsed: () => void = () => {};
988
+ onElementExpanded: () => void = () => {};
315
989
  onDocumentUpdated: (domModel: SDK.DOMModel.DOMModel) => void = () => {};
316
990
 
317
991
  #maxTreeDepth?: number;
@@ -336,14 +1010,37 @@ export class DOMTreeWidget extends UI.Widget.Widget {
336
1010
  return this.#maxRows;
337
1011
  }
338
1012
 
1013
+ get visibleWidth(): number {
1014
+ return this.#visibleWidth ?? this.contentElement.offsetWidth;
1015
+ }
1016
+
339
1017
  set visibleWidth(width: number) {
340
1018
  this.#visibleWidth = width;
341
1019
  this.performUpdate();
342
1020
  }
343
1021
 
1022
+ #rootDOMNode: SDK.DOMModel.DOMNode|null = null;
1023
+ #selectedDOMNode: SDK.DOMModel.DOMNode|null = null;
1024
+ #expandedNodes = new Set<SDK.DOMModel.DOMNode>();
1025
+
344
1026
  // FIXME: this is not declarative because ElementsTreeOutline can
345
1027
  // change root node internally.
346
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
+ }
347
1044
  this.performUpdate();
348
1045
  if (!this.#viewOutput.elementsTreeOutline) {
349
1046
  throw new Error('Unexpected: missing elementsTreeOutline');
@@ -352,6 +1049,9 @@ export class DOMTreeWidget extends UI.Widget.Widget {
352
1049
  this.performUpdate();
353
1050
  }
354
1051
  get rootDOMNode(): SDK.DOMModel.DOMNode|null {
1052
+ if (this.#view === DECLARATIVE_VIEW) {
1053
+ return this.#rootDOMNode;
1054
+ }
355
1055
  return this.#viewOutput.elementsTreeOutline?.rootDOMNode ?? null;
356
1056
  }
357
1057
 
@@ -410,6 +1110,14 @@ export class DOMTreeWidget extends UI.Widget.Widget {
410
1110
  }
411
1111
 
412
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;
413
1121
 
414
1122
  #view: View;
415
1123
  #viewOutput: ViewOutput = {
@@ -419,12 +1127,15 @@ export class DOMTreeWidget extends UI.Widget.Widget {
419
1127
  };
420
1128
  #highlightThrottler = new Common.Throttler.Throttler(100);
421
1129
 
422
- constructor(element?: HTMLElement, view?: View) {
1130
+ constructor(
1131
+ element?: HTMLElement,
1132
+ view: View = DEFAULT_VIEW,
1133
+ ) {
423
1134
  super(element, {
424
1135
  useShadowDom: false,
425
1136
  delegatesFocus: false,
426
1137
  });
427
- this.#view = view ?? DEFAULT_VIEW;
1138
+ this.#view = view;
428
1139
  this.#showHTMLCommentsSetting.addChangeListener(this.#onShowHTMLCommentsChange, this);
429
1140
  if (Common.Settings.Settings.instance().moduleSetting('highlight-node-on-hover-in-overlay').get()) {
430
1141
  SDK.TargetManager.TargetManager.instance().addModelListener(
@@ -436,6 +1147,23 @@ export class DOMTreeWidget extends UI.Widget.Widget {
436
1147
  }
437
1148
  }
438
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
+
439
1167
  #onShowHTMLCommentsChange(): void {
440
1168
  this.#showComments = this.#showHTMLCommentsSetting.get();
441
1169
  const selectedNode = this.selectedDOMNode();
@@ -465,10 +1193,33 @@ export class DOMTreeWidget extends UI.Widget.Widget {
465
1193
 
466
1194
  selectDOMNode(node: SDK.DOMModel.DOMNode|SDK.DOMModel.AdoptedStyleSheet|null, focus?: boolean): void {
467
1195
  if (node instanceof SDK.DOMModel.AdoptedStyleSheet) {
468
- this.#viewOutput?.elementsTreeOutline?.highlightAdoptedStyleSheet(node);
469
- } else {
470
- 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;
471
1221
  }
1222
+ this.#viewOutput?.elementsTreeOutline?.selectDOMNode(node, focus);
472
1223
  }
473
1224
 
474
1225
  highlightNodeAttribute(node: SDK.DOMModel.DOMNode, attribute: string): void {
@@ -489,9 +1240,103 @@ export class DOMTreeWidget extends UI.Widget.Widget {
489
1240
  }
490
1241
 
491
1242
  selectedDOMNode(): SDK.DOMModel.DOMNode|null {
1243
+ if (this.#view === DECLARATIVE_VIEW) {
1244
+ return this.#selectedDOMNode;
1245
+ }
492
1246
  return this.#viewOutput.elementsTreeOutline?.selectedDOMNode() ?? null;
493
1247
  }
494
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
+
495
1340
  /**
496
1341
  * FIXME: this is called to re-render everything from scratch, for
497
1342
  * example, if global settings changed. Instead, the setting values
@@ -512,9 +1357,58 @@ export class DOMTreeWidget extends UI.Widget.Widget {
512
1357
  return this.#viewOutput.elementsTreeOutline?.findTreeElement(node) || null;
513
1358
  }
514
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
+
515
1407
  override performUpdate(): void {
516
1408
  const firstRender = !this.#viewOutput.elementsTreeOutline;
517
1409
  this.#view({
1410
+ domTreeWidget: this,
1411
+ rootDOMNode: this.#rootDOMNode,
518
1412
  omitRootDOMNode: this.omitRootDOMNode,
519
1413
  selectEnabled: this.selectEnabled,
520
1414
  hideGutter: this.hideGutter,
@@ -533,6 +1427,9 @@ export class DOMTreeWidget extends UI.Widget.Widget {
533
1427
  deindentSingleNode: this.deindentSingleNode,
534
1428
 
535
1429
  currentHighlightedNode: this.#currentHighlightedNode,
1430
+ hoveredNode: this.#hoveredDOMNode,
1431
+ searchMatchNode: this.#searchMatchNode,
1432
+ searchMatchQuery: this.#searchMatchQuery,
536
1433
  selectedNode: this.selectedDOMNode(),
537
1434
  onElementsTreeUpdated: this.onElementsTreeUpdated.bind(this),
538
1435
  onSelectedNodeChanged: event => {
@@ -545,8 +1442,117 @@ export class DOMTreeWidget extends UI.Widget.Widget {
545
1442
  onElementExpanded: () => {
546
1443
  this.#clearHighlightedNode();
547
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
+ },
548
1551
  },
549
1552
  this.#viewOutput, this.contentElement);
1553
+ if (this.#viewOutput.elementsTreeOutline) {
1554
+ this.#viewOutput.elementsTreeOutline.domTreeWidget = this;
1555
+ }
550
1556
  if (firstRender && this.#viewOutput.elementsTreeOutline) {
551
1557
  this.#viewOutput.elementsTreeOutline.addEventListener(ElementsTreeOutline.Events.ShowAllRows, () => {
552
1558
  this.maxRows = undefined;
@@ -555,6 +1561,37 @@ export class DOMTreeWidget extends UI.Widget.Widget {
555
1561
  }
556
1562
 
557
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
+ }
558
1595
  this.performUpdate();
559
1596
  if (!this.#viewOutput.elementsTreeOutline) {
560
1597
  throw new Error('Unexpected: missing elementsTreeOutline');
@@ -564,6 +1601,24 @@ export class DOMTreeWidget extends UI.Widget.Widget {
564
1601
  }
565
1602
 
566
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
+ }
567
1622
  this.#viewOutput.elementsTreeOutline?.unwireFromDOMModel(domModel);
568
1623
  this.performUpdate();
569
1624
  }
@@ -593,71 +1648,674 @@ export class DOMTreeWidget extends UI.Widget.Widget {
593
1648
  }
594
1649
 
595
1650
  highlightMatch(node: SDK.DOMModel.DOMNode, query?: string): void {
596
- const treeElement = this.#viewOutput.elementsTreeOutline?.findTreeElement(node);
597
- if (!treeElement) {
598
- return;
599
- }
600
- if (query) {
601
- treeElement.highlightSearchResults(query);
602
- }
603
- treeElement.reveal();
604
- const matches = treeElement.listItemElement.getElementsByClassName(Highlighting.highlightedSearchResultClassName);
605
- if (matches.length) {
606
- matches[0].scrollIntoViewIfNeeded(false);
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();
607
1657
  }
608
- treeElement.select(/* omitFocus */ true);
609
1658
  }
610
1659
 
611
1660
  hideMatchHighlights(node: SDK.DOMModel.DOMNode): void {
612
- const treeElement = this.#viewOutput.elementsTreeOutline?.findTreeElement(node);
613
- if (!treeElement) {
614
- return;
1661
+ if (this.#searchMatchNode === node) {
1662
+ this.#searchMatchNode = null;
1663
+ this.#searchMatchQuery = null;
1664
+ this.performUpdate();
615
1665
  }
616
- treeElement.hideSearchHighlights();
617
1666
  }
618
1667
 
619
1668
  toggleHideElement(node: SDK.DOMModel.DOMNode): void {
620
1669
  void node.toggleHideElement();
621
1670
  }
622
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
+
623
1686
  isToggledToHidden(node: SDK.DOMModel.DOMNode): boolean {
624
1687
  return node.isToggledToHidden();
625
1688
  }
626
1689
 
627
- toggleEditAsHTML(node: SDK.DOMModel.DOMNode): void {
628
- this.#viewOutput.elementsTreeOutline?.toggleEditAsHTML(node);
629
- }
1690
+ #multilineEditing: MultilineEditorController|null = null;
630
1691
 
631
- duplicateNode(node: SDK.DOMModel.DOMNode): void {
632
- node.duplicate();
1692
+ setMultilineEditing(multilineEditing: MultilineEditorController|null): void {
1693
+ this.#multilineEditing = multilineEditing;
633
1694
  }
634
1695
 
635
- copyStyles(node: SDK.DOMModel.DOMNode): void {
636
- void this.#viewOutput.elementsTreeOutline?.findTreeElement(node)?.copyStyles();
1696
+ multilineEditing(): MultilineEditorController|null {
1697
+ return this.#multilineEditing;
637
1698
  }
638
1699
 
639
- /**
640
- * FIXME: used to determine focus state, probably we can have a better
641
- * way to do it.
642
- */
643
- empty(): boolean {
644
- return !this.#viewOutput.elementsTreeOutline;
1700
+ runPendingUpdates(): void {
1701
+ this.#viewOutput.elementsTreeOutline?.runPendingUpdates();
1702
+ this.performUpdate();
645
1703
  }
646
1704
 
647
- override focus(): void {
648
- super.focus();
649
- this.#viewOutput.elementsTreeOutline?.focus();
1705
+ override onResize(): void {
1706
+ super.onResize();
1707
+ if (this.#multilineEditing) {
1708
+ this.#multilineEditing.resize();
1709
+ }
650
1710
  }
651
1711
 
652
- override wasShown(): void {
653
- super.wasShown();
654
- this.#visible = true;
655
- this.performUpdate();
1712
+ override willHide(): void {
1713
+ super.willHide();
1714
+ if (this.#multilineEditing) {
1715
+ this.#multilineEditing.cancel();
1716
+ }
656
1717
  }
657
1718
 
658
- override wasHidden(): void {
659
- super.wasHidden();
660
- this.#visible = false;
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?.()) {
2258
+ return;
2259
+ }
2260
+
2261
+ // Do not interfere with text editing.
2262
+ if (UI.UIUtils.isEditing()) {
2263
+ return;
2264
+ }
2265
+
2266
+ const targetNode = this.selectedDOMNode();
2267
+ if (!targetNode) {
2268
+ return;
2269
+ }
2270
+
2271
+ if (originalEvent.clipboardData) {
2272
+ originalEvent.clipboardData.clearData();
2273
+ }
2274
+ (event as {handled?: boolean}).handled = true;
2275
+
2276
+ this.performCopyOrCut(isCut, targetNode);
2277
+ }
2278
+
2279
+ onPaste(event: Event): void {
2280
+ // Do not interfere with text editing.
2281
+ if (UI.UIUtils.isEditing()) {
2282
+ return;
2283
+ }
2284
+
2285
+ const targetNode = this.selectedDOMNode();
2286
+ if (!targetNode || !this.canPaste(targetNode)) {
2287
+ return;
2288
+ }
2289
+
2290
+ (event as {handled?: boolean}).handled = true;
2291
+ this.pasteNode(targetNode);
2292
+ }
2293
+
2294
+ /**
2295
+ * FIXME: used to determine focus state, probably we can have a better
2296
+ * way to do it.
2297
+ */
2298
+ empty(): boolean {
2299
+ if (this.#view === DECLARATIVE_VIEW) {
2300
+ return !this.#rootDOMNode;
2301
+ }
2302
+ return !this.#viewOutput.elementsTreeOutline;
2303
+ }
2304
+
2305
+ override focus(): void {
2306
+ super.focus();
2307
+ this.#viewOutput.elementsTreeOutline?.focus();
2308
+ }
2309
+
2310
+ override wasShown(): void {
2311
+ super.wasShown();
2312
+ this.#visible = true;
2313
+ this.performUpdate();
2314
+ }
2315
+
2316
+ override wasHidden(): void {
2317
+ super.wasHidden();
2318
+ this.#visible = false;
661
2319
  this.#viewOutput.imagePreviewPopover?.hide();
662
2320
  this.performUpdate();
663
2321
  }
@@ -681,6 +2339,13 @@ export class DOMTreeWidget extends UI.Widget.Widget {
681
2339
  if (domModel.existingDocument()) {
682
2340
  this.rootDOMNode = domModel.existingDocument();
683
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
+ });
684
2349
  } else {
685
2350
  void domModel.requestDocument();
686
2351
  }
@@ -703,19 +2368,13 @@ export class ElementsTreeOutline extends
703
2368
  private visible: boolean;
704
2369
  private updateRecords: Map<SDK.DOMModel.DOMNode, Elements.ElementUpdateRecord.ElementUpdateRecord>;
705
2370
  private treeElementsBeingUpdated: Set<ElementsTreeElement>;
706
- decoratorExtensions: MarkerDecoratorRegistration[]|null;
707
- private multilineEditing?: MultilineEditorController|null;
708
2371
  private visibleWidthInternal?: number;
709
- private clipboardNodeData?: ClipboardData;
710
2372
  private isXMLMimeTypeInternal?: boolean|null;
711
2373
  suppressRevealAndSelect = false;
712
2374
  private previousHoveredElement?: UI.TreeOutline.TreeElement;
713
- private treeElementBeingDragged?: ElementsTreeElement;
714
2375
  private dragOverTreeElement?: ElementsTreeElement;
715
2376
  private updateModifiedNodesTimeout?: number;
716
2377
  #topLayerContainerByDocument = new WeakMap<SDK.DOMModel.DOMDocument, TopLayerContainer>();
717
- #issuesManager?: IssuesManager.IssuesManager.IssuesManager;
718
- #nodeElementToIssues = new Map<Element, IssuesManager.Issue.Issue[]>();
719
2378
  maxTreeDepth?: number;
720
2379
  enableContextMenu: boolean;
721
2380
  showComments: boolean;
@@ -724,18 +2383,19 @@ export class ElementsTreeOutline extends
724
2383
  expandRoot: boolean;
725
2384
  #maxRowsShown?: number;
726
2385
  #showAllButton?: HTMLElement;
2386
+ domTreeWidget: DOMTreeWidget|null = null;
727
2387
 
728
- constructor(
729
- omitRootDOMNode?: boolean, selectEnabled?: boolean, hideGutter?: boolean, maxTreeDepth?: number,
730
- enableContextMenu?: boolean, showComments?: boolean, showAIButton?: boolean, disableEdits?: boolean,
731
- expandRoot?: boolean) {
732
- super();
2388
+ get hoveredTreeElement(): UI.TreeOutline.TreeElement|null {
2389
+ return this.previousHoveredElement ?? null;
2390
+ }
733
2391
 
734
- this.#issuesManager = IssuesManager.IssuesManager.IssuesManager.instance();
735
- this.#issuesManager.addEventListener(IssuesManager.IssuesManager.Events.ISSUE_ADDED, this.#onIssueAdded, this);
736
- this.#issuesManager.addEventListener(
737
- IssuesManager.IssuesManager.Events.ISSUE_HIDDEN_STATUS_UPDATED, this.#onIssueHiddenStatusUpdated, this);
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) {
2395
+ super();
738
2396
 
2397
+ this.domTreeWidget = domTreeWidget ?? null;
2398
+ this.renderSelection = true;
739
2399
  this.treeElementByNode = new WeakMap();
740
2400
  const shadowContainer = document.createElement('div');
741
2401
  this.shadowRoot = UI.UIUtils.createShadowRootWithCoreStyles(
@@ -756,120 +2416,34 @@ export class ElementsTreeOutline extends
756
2416
  this.elementInternal.addEventListener('mousedown', this.onmousedown.bind(this), false);
757
2417
  this.elementInternal.addEventListener('mousemove', this.onmousemove.bind(this), false);
758
2418
  this.elementInternal.addEventListener('mouseleave', this.onmouseleave.bind(this), false);
759
- this.elementInternal.addEventListener('keydown', this.onKeyDown.bind(this), false);
760
2419
 
761
2420
  if (!this.disableEdits) {
762
2421
  this.elementInternal.addEventListener('dragstart', this.ondragstart.bind(this), false);
763
2422
  this.elementInternal.addEventListener('dragover', this.ondragover.bind(this), false);
764
2423
  this.elementInternal.addEventListener('dragleave', this.ondragleave.bind(this), false);
765
2424
  this.elementInternal.addEventListener('drop', this.ondrop.bind(this), false);
766
- this.elementInternal.addEventListener('dragend', this.ondragend.bind(this), false);
767
- this.elementInternal.addEventListener('clipboard-beforecopy', this.onBeforeCopy.bind(this), false);
768
- this.elementInternal.addEventListener('clipboard-copy', this.onCopyOrCut.bind(this, false), false);
769
- this.elementInternal.addEventListener('clipboard-cut', this.onCopyOrCut.bind(this, true), false);
770
- this.elementInternal.addEventListener('clipboard-paste', this.onPaste.bind(this), false);
771
- }
772
-
773
- outlineDisclosureElement.appendChild(this.elementInternal);
774
- this.element = shadowContainer;
775
- this.contentElement.setAttribute('jslog', `${VisualLogging.tree('elements')}`);
776
-
777
- this.includeRootDOMNode = !omitRootDOMNode;
778
- this.selectEnabled = selectEnabled;
779
- this.rootDOMNodeInternal = null;
780
- this.selectedDOMNodeInternal = null;
781
-
782
- this.visible = false;
783
-
784
- this.updateRecords = new Map();
785
- this.treeElementsBeingUpdated = new Set();
786
-
787
- this.decoratorExtensions = null;
788
- this.setUseLightSelectionColor(true);
789
- }
790
-
791
- static forDOMModel(domModel: SDK.DOMModel.DOMModel): ElementsTreeOutline|null {
792
- return elementsTreeOutlineByDOMModel.get(domModel) || null;
793
- }
794
-
795
- #onIssueAdded(event: Common.EventTarget.EventTargetEvent<IssuesManager.IssuesManager.IssueAddedEvent>): void {
796
- void this.#addTreeElementIssue(event.data.issue);
797
- }
798
-
799
- #onIssueHiddenStatusUpdated(
800
- event: Common.EventTarget.EventTargetEvent<IssuesManager.IssuesManager.IssueHiddenStatusUpdatedEvent>): void {
801
- const issue = event.data.issue;
802
- if (!issue) {
803
- return;
804
- }
805
-
806
- if (issue.isHidden()) {
807
- void this.#removeTreeElementIssue(issue);
808
- return;
809
- }
810
- void this.#addTreeElementIssue(issue);
811
- }
812
-
813
- #addAllElementIssues(): void {
814
- if (!this.#issuesManager) {
815
- return;
816
- }
817
- for (const issue of this.#issuesManager.issues()) {
818
- void this.#addTreeElementIssue(issue);
819
- }
820
- }
821
-
822
- async #addTreeElementIssue(issue: IssuesManager.Issue.Issue): Promise<void> {
823
- if (issue.isHidden()) {
824
- return;
825
- }
826
-
827
- const elementIssueDetails = getElementIssueDetails(issue);
828
- if (!elementIssueDetails) {
829
- return;
830
- }
831
- const {nodeId} = elementIssueDetails;
832
- if (!this.rootDOMNode || !nodeId) {
833
- return;
834
- }
835
- const deferredDOMNode = new SDK.DOMModel.DeferredDOMNode(this.rootDOMNode.domModel().target(), nodeId);
836
- const node = await deferredDOMNode.resolvePromise();
837
-
838
- if (!node) {
839
- return;
2425
+ this.elementInternal.addEventListener('dragend', this.ondragend.bind(this), false);
840
2426
  }
841
2427
 
842
- const treeElement = this.findTreeElement(node);
843
- if (treeElement) {
844
- treeElement.addIssue(issue);
845
- const treeElementNodeElementsToIssues = treeElement.issuesByNodeElement;
846
- // This element could be the treeElement tags name or an attribute.
847
- for (const [element, issues] of treeElementNodeElementsToIssues) {
848
- this.#nodeElementToIssues.set(element, issues);
849
- }
850
- }
851
- }
2428
+ outlineDisclosureElement.appendChild(this.elementInternal);
2429
+ this.element = shadowContainer;
2430
+ this.contentElement.setAttribute('jslog', `${VisualLogging.tree('elements')}`);
852
2431
 
853
- async #removeTreeElementIssue(issue: IssuesManager.Issue.Issue): Promise<void> {
854
- const elementIssueDetails = getElementIssueDetails(issue);
855
- if (!elementIssueDetails) {
856
- return;
857
- }
858
- const {nodeId} = elementIssueDetails;
859
- if (!this.rootDOMNode || !nodeId) {
860
- return;
861
- }
862
- const deferredDOMNode = new SDK.DOMModel.DeferredDOMNode(this.rootDOMNode.domModel().target(), nodeId);
863
- const node = await deferredDOMNode.resolvePromise();
2432
+ this.includeRootDOMNode = !omitRootDOMNode;
2433
+ this.selectEnabled = selectEnabled;
2434
+ this.rootDOMNodeInternal = null;
2435
+ this.selectedDOMNodeInternal = null;
864
2436
 
865
- if (!node) {
866
- return;
867
- }
2437
+ this.visible = false;
868
2438
 
869
- const treeElement = this.findTreeElement(node);
870
- if (treeElement) {
871
- treeElement.removeIssue(issue);
872
- }
2439
+ this.updateRecords = new Map();
2440
+ this.treeElementsBeingUpdated = new Set();
2441
+
2442
+ this.setUseLightSelectionColor(true);
2443
+ }
2444
+
2445
+ static forDOMModel(domModel: SDK.DOMModel.DOMModel): ElementsTreeOutline|null {
2446
+ return elementsTreeOutlineByDOMModel.get(domModel) || null;
873
2447
  }
874
2448
 
875
2449
  deindentSingleNode(): void {
@@ -879,176 +2453,44 @@ export class ElementsTreeOutline extends
879
2453
  }
880
2454
  }
881
2455
 
882
- updateNodeElementToIssue(element: Element, issues: IssuesManager.Issue.Issue[]): void {
883
- if (!issues || issues.length === 0) {
884
- this.#nodeElementToIssues.delete(element);
885
- return;
886
- }
887
- this.#nodeElementToIssues.set(element, issues);
888
- }
889
-
890
- issuesByNodeElement(element: Element): IssuesManager.Issue.Issue[]|undefined {
891
- return this.#nodeElementToIssues.get(element);
892
- }
893
-
894
2456
  setWordWrap(wrap: boolean): void {
895
2457
  this.elementInternal.classList.toggle('elements-tree-nowrap', !wrap);
896
2458
  }
897
2459
 
898
2460
  setMultilineEditing(multilineEditing: MultilineEditorController|null): void {
899
- this.multilineEditing = multilineEditing;
2461
+ this.domTreeWidget?.setMultilineEditing(multilineEditing);
900
2462
  }
901
2463
 
902
2464
  visibleWidth(): number {
903
- return this.visibleWidthInternal || 0;
2465
+ return this.domTreeWidget?.visibleWidth ?? this.visibleWidthInternal ?? 0;
904
2466
  }
905
2467
 
906
2468
  setVisibleWidth(width: number): void {
907
2469
  this.visibleWidthInternal = width;
908
- if (this.multilineEditing) {
909
- this.multilineEditing.resize();
910
- }
911
2470
  }
912
2471
 
913
- private setClipboardData(data: ClipboardData|null): void {
914
- if (this.clipboardNodeData) {
915
- const treeElement = this.findTreeElement(this.clipboardNodeData.node);
916
- if (treeElement) {
917
- treeElement.setInClipboard(false);
918
- }
919
- delete this.clipboardNodeData;
920
- }
921
-
922
- if (data) {
923
- const treeElement = this.findTreeElement(data.node);
924
- if (treeElement) {
925
- treeElement.setInClipboard(true);
926
- }
927
- this.clipboardNodeData = data;
928
- }
2472
+ setClipboardData(data: ClipboardData|null): void {
2473
+ this.domTreeWidget?.setClipboardData(data);
929
2474
  }
930
2475
 
931
2476
  resetClipboardIfNeeded(removedNode: SDK.DOMModel.DOMNode): void {
932
- if (this.clipboardNodeData?.node === removedNode) {
933
- this.setClipboardData(null);
934
- }
935
- }
936
-
937
- private onBeforeCopy(event: Event): void {
938
- event.handled = true;
939
- }
940
-
941
- private onCopyOrCut(isCut: boolean, event: Event): void {
942
- this.setClipboardData(null);
943
- // @ts-expect-error this bound in the main entry point
944
- const originalEvent = event['original'];
945
-
946
- if (!originalEvent?.target) {
947
- return;
948
- }
949
-
950
- // Don't prevent the normal copy if the user has a selection.
951
- if (originalEvent.target instanceof Node && originalEvent.target.hasSelection()) {
952
- return;
953
- }
954
-
955
- // Do not interfere with text editing.
956
- if (UI.UIUtils.isEditing()) {
957
- return;
958
- }
959
-
960
- const targetNode = this.selectedDOMNode();
961
- if (!targetNode) {
962
- return;
963
- }
964
-
965
- if (!originalEvent.clipboardData) {
966
- return;
967
- }
968
- originalEvent.clipboardData.clearData();
969
- event.handled = true;
970
-
971
- this.performCopyOrCut(isCut, targetNode);
2477
+ this.domTreeWidget?.resetClipboardIfNeeded(removedNode);
972
2478
  }
973
2479
 
974
2480
  performCopyOrCut(isCut: boolean, node: SDK.DOMModel.DOMNode|null, includeShadowRoots = false): void {
975
- if (!node) {
976
- return;
977
- }
978
- if (isCut && (node.isShadowRoot() || node.ancestorUserAgentShadowRoot())) {
979
- return;
980
- }
981
- void node.getOuterHTML(includeShadowRoots).then(outerHTML => {
982
- if (outerHTML !== null) {
983
- UI.UIUtils.copyTextToClipboard(outerHTML);
984
- }
985
- });
986
- this.setClipboardData({node, isCut});
2481
+ this.domTreeWidget?.performCopyOrCut(isCut, node, includeShadowRoots);
987
2482
  }
988
2483
 
989
2484
  canPaste(targetNode: SDK.DOMModel.DOMNode): boolean {
990
- if (targetNode.isShadowRoot() || targetNode.ancestorUserAgentShadowRoot()) {
991
- return false;
992
- }
993
-
994
- if (!this.clipboardNodeData) {
995
- return false;
996
- }
997
-
998
- const node = this.clipboardNodeData.node;
999
- if (this.clipboardNodeData.isCut && (node === targetNode || node.isAncestor(targetNode))) {
1000
- return false;
1001
- }
1002
-
1003
- if (targetNode.domModel() !== node.domModel()) {
1004
- return false;
1005
- }
1006
- return true;
2485
+ return this.domTreeWidget?.canPaste(targetNode) ?? false;
1007
2486
  }
1008
2487
 
1009
2488
  pasteNode(targetNode: SDK.DOMModel.DOMNode): void {
1010
- if (this.canPaste(targetNode)) {
1011
- this.performPaste(targetNode);
1012
- }
2489
+ this.domTreeWidget?.pasteNode(targetNode);
1013
2490
  }
1014
2491
 
1015
2492
  duplicateNode(targetNode: SDK.DOMModel.DOMNode): void {
1016
- targetNode.duplicate();
1017
- }
1018
-
1019
- private onPaste(event: Event): void {
1020
- // Do not interfere with text editing.
1021
- if (UI.UIUtils.isEditing()) {
1022
- return;
1023
- }
1024
-
1025
- const targetNode = this.selectedDOMNode();
1026
- if (!targetNode || !this.canPaste(targetNode)) {
1027
- return;
1028
- }
1029
-
1030
- event.handled = true;
1031
- this.performPaste(targetNode);
1032
- }
1033
-
1034
- private performPaste(targetNode: SDK.DOMModel.DOMNode): void {
1035
- if (!this.clipboardNodeData) {
1036
- return;
1037
- }
1038
- if (this.clipboardNodeData.isCut) {
1039
- this.clipboardNodeData.node.moveTo(targetNode, null, expandCallback.bind(this));
1040
- this.setClipboardData(null);
1041
- } else {
1042
- this.clipboardNodeData.node.copyTo(targetNode, null, expandCallback.bind(this));
1043
- }
1044
-
1045
- function expandCallback(
1046
- this: ElementsTreeOutline, error: string|null, pastedNode: SDK.DOMModel.DOMNode|null): void {
1047
- if (error || !pastedNode) {
1048
- return;
1049
- }
1050
- this.selectDOMNode(pastedNode);
1051
- }
2493
+ this.domTreeWidget?.duplicateNode(targetNode);
1052
2494
  }
1053
2495
 
1054
2496
  setVisible(visible: boolean): void {
@@ -1057,9 +2499,7 @@ export class ElementsTreeOutline extends
1057
2499
  }
1058
2500
  this.visible = visible;
1059
2501
  if (!this.visible) {
1060
- if (this.multilineEditing) {
1061
- this.multilineEditing.cancel();
1062
- }
2502
+ this.domTreeWidget?.multilineEditing()?.cancel();
1063
2503
  return;
1064
2504
  }
1065
2505
 
@@ -1330,13 +2770,22 @@ export class ElementsTreeOutline extends
1330
2770
  }
1331
2771
 
1332
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
+
1333
2782
  const scrollContainer = this.element.parentElement;
1334
2783
  if (!scrollContainer) {
1335
2784
  return null;
1336
2785
  }
1337
2786
 
1338
- const x = event.pageX;
1339
- const y = event.pageY;
2787
+ const x = event.clientX;
2788
+ const y = event.clientY;
1340
2789
 
1341
2790
  // Our list items have 1-pixel cracks between them vertically. We avoid
1342
2791
  // the cracks by checking slightly above and slightly below the mouse
@@ -1386,9 +2835,15 @@ export class ElementsTreeOutline extends
1386
2835
  return;
1387
2836
  }
1388
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);
1389
2845
  this.setHoverEffect(element);
1390
- this.highlightTreeElement(
1391
- (element as UI.TreeOutline.TreeElement), !UI.KeyboardShortcut.KeyboardShortcut.eventHasEitherCtrlOrMeta(event));
2846
+ this.highlightTreeElement((element as UI.TreeOutline.TreeElement), showInfo);
1392
2847
  }
1393
2848
 
1394
2849
  private highlightTreeElement(element: UI.TreeOutline.TreeElement, showInfo: boolean): void {
@@ -1406,131 +2861,61 @@ export class ElementsTreeOutline extends
1406
2861
  }
1407
2862
 
1408
2863
  private onmouseleave(_event: MouseEvent): void {
2864
+ this.domTreeWidget?.setHoveredNode(null);
1409
2865
  this.setHoverEffect(null);
1410
2866
  SDK.OverlayModel.OverlayModel.hideDOMNodeHighlight(SDK.TargetManager.TargetManager.instance());
1411
2867
  }
1412
2868
 
1413
2869
  private ondragstart(event: DragEvent): boolean|undefined {
1414
- const node = (event.target as Node | null);
1415
- if (!node || node.hasSelection()) {
1416
- return false;
1417
- }
1418
- if (node.nodeName === 'A') {
1419
- return false;
1420
- }
1421
-
1422
- const treeElement = this.validDragSourceOrTarget(this.treeElementFromEventInternal(event));
1423
- if (!treeElement) {
1424
- return false;
1425
- }
1426
-
1427
- if (treeElement.node().nodeName() === 'BODY' || treeElement.node().nodeName() === 'HEAD') {
2870
+ const treeElement = this.treeElementFromEventInternal(event);
2871
+ if (!(treeElement instanceof ElementsTreeElement)) {
1428
2872
  return false;
1429
2873
  }
1430
-
1431
- if (!event.dataTransfer || !treeElement.listItemElement.textContent) {
1432
- return;
1433
- }
1434
- event.dataTransfer.setData('text/plain', treeElement.listItemElement.textContent.replace(/\u200b/g, ''));
1435
- event.dataTransfer.effectAllowed = 'copyMove';
1436
- this.treeElementBeingDragged = treeElement;
1437
-
1438
- SDK.OverlayModel.OverlayModel.hideDOMNodeHighlight(SDK.TargetManager.TargetManager.instance());
1439
-
1440
- 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;
1441
2878
  }
1442
2879
 
1443
2880
  private ondragover(event: DragEvent): boolean {
1444
- if (!this.treeElementBeingDragged) {
1445
- return false;
1446
- }
1447
-
1448
- const treeElement = this.validDragSourceOrTarget(this.treeElementFromEventInternal(event));
1449
- if (!treeElement) {
2881
+ const treeElement = this.treeElementFromEventInternal(event);
2882
+ if (!(treeElement instanceof ElementsTreeElement)) {
2883
+ this.clearDragOverTreeElementMarker();
1450
2884
  return false;
1451
2885
  }
1452
-
1453
- let node: (SDK.DOMModel.DOMNode|null) = (treeElement.node() as SDK.DOMModel.DOMNode | null);
1454
- while (node) {
1455
- if (node === this.treeElementBeingDragged.nodeInternal) {
1456
- 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;
1457
2892
  }
1458
- node = node.parentNode;
1459
- }
1460
-
1461
- treeElement.listItemElement.classList.add('elements-drag-over');
1462
- this.dragOverTreeElement = treeElement;
1463
- event.preventDefault();
1464
- if (event.dataTransfer) {
1465
- event.dataTransfer.dropEffect = 'move';
2893
+ } else {
2894
+ this.clearDragOverTreeElementMarker();
1466
2895
  }
1467
- return false;
2896
+ return !success;
1468
2897
  }
1469
2898
 
1470
2899
  private ondragleave(event: DragEvent): boolean {
1471
2900
  this.clearDragOverTreeElementMarker();
2901
+ this.domTreeWidget?.onDragLeave(event);
1472
2902
  event.preventDefault();
1473
2903
  return false;
1474
2904
  }
1475
2905
 
1476
- private validDragSourceOrTarget(treeElement: UI.TreeOutline.TreeElement|null): ElementsTreeElement|null {
1477
- if (!treeElement) {
1478
- return null;
1479
- }
1480
-
1481
- if (!(treeElement instanceof ElementsTreeElement)) {
1482
- return null;
1483
- }
1484
- const elementsTreeElement = (treeElement);
1485
-
1486
- const node = elementsTreeElement.node();
1487
- if (!node.parentNode || node.parentNode.nodeType() !== Node.ELEMENT_NODE) {
1488
- return null;
1489
- }
1490
-
1491
- return elementsTreeElement;
1492
- }
1493
-
1494
2906
  private ondrop(event: DragEvent): void {
1495
2907
  event.preventDefault();
1496
2908
  const treeElement = this.treeElementFromEventInternal(event);
1497
2909
  if (treeElement instanceof ElementsTreeElement) {
1498
- this.doMove(treeElement);
1499
- }
1500
- }
1501
-
1502
- private doMove(treeElement: ElementsTreeElement): void {
1503
- if (!this.treeElementBeingDragged) {
1504
- return;
1505
- }
1506
-
1507
- let parentNode;
1508
- let anchorNode;
1509
-
1510
- if (treeElement.isClosingTag()) {
1511
- // Drop onto closing tag -> insert as last child.
1512
- parentNode = treeElement.node();
1513
- anchorNode = null;
1514
- } else {
1515
- const dragTargetNode = treeElement.node();
1516
- parentNode = dragTargetNode.parentNode;
1517
- anchorNode = dragTargetNode;
2910
+ this.domTreeWidget?.onDrop(treeElement.node(), treeElement.isClosingTag(), event);
1518
2911
  }
1519
-
1520
- if (!parentNode) {
1521
- return;
1522
- }
1523
- const wasExpanded = this.treeElementBeingDragged.expanded;
1524
- this.treeElementBeingDragged.nodeInternal.moveTo(
1525
- parentNode, anchorNode, this.selectNodeAfterEdit.bind(this, wasExpanded));
1526
-
1527
- delete this.treeElementBeingDragged;
2912
+ this.clearDragOverTreeElementMarker();
1528
2913
  }
1529
2914
 
1530
2915
  private ondragend(event: DragEvent): void {
1531
2916
  event.preventDefault();
1532
2917
  this.clearDragOverTreeElementMarker();
1533
- delete this.treeElementBeingDragged;
2918
+ this.domTreeWidget?.onDragEnd(event);
1534
2919
  }
1535
2920
 
1536
2921
  private clearDragOverTreeElementMarker(): void {
@@ -1546,83 +2931,8 @@ export class ElementsTreeOutline extends
1546
2931
  this.updateModifiedNodes();
1547
2932
  }
1548
2933
 
1549
- private onKeyDown(event: Event): void {
1550
- const keyboardEvent = (event as KeyboardEvent);
1551
- if (UI.UIUtils.isEditing()) {
1552
- return;
1553
- }
1554
- const node = this.selectedDOMNode();
1555
- if (!node) {
1556
- return;
1557
- }
1558
- const treeElement = this.treeElementByNode.get(node);
1559
- if (!treeElement) {
1560
- return;
1561
- }
1562
-
1563
- if (UI.KeyboardShortcut.KeyboardShortcut.eventHasCtrlEquivalentKey(keyboardEvent) && node.parentNode) {
1564
- if (keyboardEvent.key === 'ArrowUp' && node.previousSibling) {
1565
- node.moveTo(node.parentNode, node.previousSibling, this.selectNodeAfterEdit.bind(this, treeElement.expanded));
1566
- keyboardEvent.consume(true);
1567
- return;
1568
- }
1569
- if (keyboardEvent.key === 'ArrowDown' && node.nextSibling) {
1570
- node.moveTo(
1571
- node.parentNode, node.nextSibling.nextSibling, this.selectNodeAfterEdit.bind(this, treeElement.expanded));
1572
- keyboardEvent.consume(true);
1573
- return;
1574
- }
1575
- }
1576
- }
1577
-
1578
2934
  toggleEditAsHTML(node: SDK.DOMModel.DOMNode, startEditing?: boolean, callback?: (() => void)): void {
1579
- const treeElement = this.treeElementByNode.get(node);
1580
- if (!treeElement?.hasEditableNode()) {
1581
- return;
1582
- }
1583
-
1584
- if (node.pseudoType()) {
1585
- return;
1586
- }
1587
-
1588
- const parentNode = node.parentNode;
1589
- const index = node.index;
1590
- const wasExpanded = treeElement.expanded;
1591
-
1592
- treeElement.toggleEditAsHTML(editingFinished.bind(this), startEditing);
1593
-
1594
- function editingFinished(this: ElementsTreeOutline, success: boolean): void {
1595
- if (callback) {
1596
- callback();
1597
- }
1598
- if (!success) {
1599
- return;
1600
- }
1601
-
1602
- Badges.UserBadges.instance().recordAction(Badges.BadgeAction.DOM_ELEMENT_OR_ATTRIBUTE_EDITED);
1603
-
1604
- // Select it and expand if necessary. We force tree update so that it processes dom events and is up to date.
1605
- this.runPendingUpdates();
1606
-
1607
- if (!index) {
1608
- return;
1609
- }
1610
-
1611
- const children = parentNode?.children();
1612
- const newNode = children ? children[index] || parentNode : parentNode;
1613
- if (!newNode) {
1614
- return;
1615
- }
1616
-
1617
- this.selectDOMNode(newNode, true);
1618
-
1619
- if (wasExpanded) {
1620
- const newTreeItem = this.findTreeElement(newNode);
1621
- if (newTreeItem) {
1622
- newTreeItem.expand();
1623
- }
1624
- }
1625
- }
2935
+ this.domTreeWidget?.toggleEditAsHTML(node, startEditing, callback);
1626
2936
  }
1627
2937
 
1628
2938
  selectNodeAfterEdit(wasExpanded: boolean, error: string|null, newNode: SDK.DOMModel.DOMNode|null): ElementsTreeElement
@@ -1660,7 +2970,7 @@ export class ElementsTreeOutline extends
1660
2970
  private reset(): void {
1661
2971
  this.rootDOMNode = null;
1662
2972
  this.selectDOMNode(null, false);
1663
- delete this.clipboardNodeData;
2973
+ this.setClipboardData(null);
1664
2974
  SDK.OverlayModel.OverlayModel.hideDOMNodeHighlight(SDK.TargetManager.TargetManager.instance());
1665
2975
  this.updateRecords.clear();
1666
2976
  }
@@ -1720,7 +3030,6 @@ export class ElementsTreeOutline extends
1720
3030
  this.reset();
1721
3031
  if (domModel.existingDocument()) {
1722
3032
  this.rootDOMNode = domModel.existingDocument();
1723
- this.#addAllElementIssues();
1724
3033
  }
1725
3034
  }
1726
3035