chrome-devtools-frontend 1.0.1657855 → 1.0.1661063

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 (370) hide show
  1. package/.agents/skills/evaluate-ai-css-completion/SKILL.md +133 -0
  2. package/.agents/skills/evaluate-ai-css-completion/scripts/evaluate.js +76 -0
  3. package/.agents/skills/migrate-chromium-test/SKILL.md +15 -70
  4. package/.agents/skills/ui-eng-vision-orchestrator/SKILL.md +37 -18
  5. package/.agents/skills/version-control/SKILL.md +8 -1
  6. package/AUTHORS +1 -0
  7. package/SECURITY.md +3 -0
  8. package/docs/get_the_code.md +6 -0
  9. package/eslint.config.mjs +7 -0
  10. package/extension-api/ExtensionAPI.d.ts +88 -0
  11. package/front_end/Tests.js +6 -6
  12. package/front_end/core/common/Gzip.ts +12 -5
  13. package/front_end/core/host/AidaClient.ts +35 -9
  14. package/front_end/core/host/GdpClient.ts +10 -7
  15. package/front_end/core/host/InspectorFrontendHost.ts +1 -0
  16. package/front_end/core/host/InspectorFrontendHostStub.ts +2 -0
  17. package/front_end/core/platform/StringUtilities.ts +55 -4
  18. package/front_end/core/sdk/AccessibilityModel.snapshot.txt +17 -0
  19. package/front_end/core/sdk/AccessibilityModel.ts +132 -2
  20. package/front_end/core/sdk/CPUThrottlingManager.ts +10 -5
  21. package/front_end/core/sdk/CompilerSourceMappingContentProvider.ts +2 -5
  22. package/front_end/core/sdk/ConsoleModel.ts +5 -5
  23. package/front_end/core/sdk/DOMDebuggerModel.ts +7 -1
  24. package/front_end/core/sdk/DOMModel.ts +33 -18
  25. package/front_end/core/sdk/DebuggerModel.ts +4 -4
  26. package/front_end/core/sdk/EventBreakpointsModel.ts +2 -0
  27. package/front_end/core/sdk/FrameManager.ts +1 -0
  28. package/front_end/core/sdk/IsolateManager.ts +1 -0
  29. package/front_end/core/sdk/NetworkManager.ts +59 -11
  30. package/front_end/core/sdk/NetworkRequest.ts +38 -1
  31. package/front_end/core/sdk/OverlayModel.ts +45 -6
  32. package/front_end/core/sdk/PageResourceLoader.ts +2 -0
  33. package/front_end/core/sdk/ResourceTreeModel.ts +6 -7
  34. package/front_end/core/sdk/ScreenCaptureModel.ts +2 -2
  35. package/front_end/core/sdk/ServerTiming.ts +17 -13
  36. package/front_end/core/sdk/ServiceWorkerCacheModel.ts +4 -2
  37. package/front_end/core/sdk/ServiceWorkerManager.ts +19 -5
  38. package/front_end/core/sdk/SourceMap.ts +5 -5
  39. package/front_end/core/sdk/SourceMapManager.ts +3 -2
  40. package/front_end/core/sdk/TargetManager.ts +14 -1
  41. package/front_end/core/sdk/TraceObject.ts +4 -2
  42. package/front_end/core/sdk/sdk-meta.ts +170 -0
  43. package/front_end/design_system_tokens.css +0 -5
  44. package/front_end/entrypoints/formatter_worker/FormattedContentBuilder.ts +5 -2
  45. package/front_end/entrypoints/formatter_worker/HTMLFormatter.ts +26 -4
  46. package/front_end/entrypoints/formatter_worker/JavaScriptFormatter.ts +8 -3
  47. package/front_end/entrypoints/inspector_main/InspectorMain.ts +4 -4
  48. package/front_end/entrypoints/inspector_main/OutermostTargetSelector.ts +2 -2
  49. package/front_end/entrypoints/inspector_main/RenderingOptions.ts +36 -37
  50. package/front_end/entrypoints/inspector_main/inspector_main-meta.ts +15 -33
  51. package/front_end/entrypoints/js_app/js_app.ts +6 -5
  52. package/front_end/entrypoints/main/GlobalAiButton.ts +4 -4
  53. package/front_end/entrypoints/main/MainImpl.ts +20 -22
  54. package/front_end/entrypoints/main/main-meta.ts +42 -42
  55. package/front_end/entrypoints/node_app/node_app.ts +3 -2
  56. package/front_end/entrypoints/worker_app/WorkerMain.ts +1 -1
  57. package/front_end/foundation/Universe.ts +44 -1
  58. package/front_end/generated/InspectorBackendCommands.ts +2 -2
  59. package/front_end/generated/SupportedCSSProperties.js +19 -12
  60. package/front_end/generated/protocol.ts +3 -1
  61. package/front_end/models/ai_assistance/AiConversation.ts +19 -17
  62. package/front_end/models/ai_assistance/AiHistoryStorage.ts +37 -14
  63. package/front_end/models/ai_assistance/AiUtils.ts +38 -42
  64. package/front_end/models/ai_assistance/agents/AiAgent.ts +5 -1
  65. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +3 -2
  66. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +2 -1
  67. package/front_end/models/ai_assistance/agents/StorageAgent.ts +7 -5
  68. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +270 -286
  69. package/front_end/models/ai_assistance/data_formatters/UnitFormatters.ts +20 -0
  70. package/front_end/models/ai_assistance/performance/AICallTree.ts +3 -1
  71. package/front_end/models/ai_assistance/tools/ListNetworkRequests.ts +3 -2
  72. package/front_end/models/autofill_manager/AutofillManager.ts +3 -1
  73. package/front_end/models/bindings/CompilerScriptMapping.ts +7 -7
  74. package/front_end/models/bindings/ContentProviderBasedProject.ts +4 -4
  75. package/front_end/models/bindings/DebuggerLanguagePlugins.ts +5 -5
  76. package/front_end/models/bindings/DefaultScriptMapping.ts +3 -3
  77. package/front_end/models/bindings/NetworkProject.ts +5 -10
  78. package/front_end/models/bindings/PresentationConsoleMessageHelper.ts +2 -1
  79. package/front_end/models/bindings/ResourceMapping.ts +3 -4
  80. package/front_end/models/bindings/ResourceScriptMapping.ts +2 -3
  81. package/front_end/models/bindings/ResourceUtils.ts +1 -1
  82. package/front_end/models/bindings/SASSSourceMapping.ts +11 -9
  83. package/front_end/models/bindings/StylesSourceMapping.ts +3 -3
  84. package/front_end/models/bindings/TempFile.ts +8 -3
  85. package/front_end/models/emulation/EmulatedDevices.ts +389 -8
  86. package/front_end/models/extensions/ExtensionAPI.ts +116 -52
  87. package/front_end/models/extensions/LanguageExtensionEndpoint.ts +13 -12
  88. package/front_end/models/formatter/ScriptFormatter.ts +8 -11
  89. package/front_end/models/issues_manager/BounceTrackingIssue.ts +1 -1
  90. package/front_end/models/issues_manager/ClientHintIssue.ts +1 -1
  91. package/front_end/models/issues_manager/ConnectionAllowlistIssue.ts +1 -1
  92. package/front_end/models/issues_manager/ContentSecurityPolicyIssue.ts +7 -7
  93. package/front_end/models/issues_manager/CookieDeprecationMetadataIssue.ts +2 -2
  94. package/front_end/models/issues_manager/CookieIssue.ts +27 -16
  95. package/front_end/models/issues_manager/CorsIssue.ts +3 -3
  96. package/front_end/models/issues_manager/CrossOriginEmbedderPolicyIssue.ts +2 -2
  97. package/front_end/models/issues_manager/DeprecationIssue.ts +3 -3
  98. package/front_end/models/issues_manager/EmailVerificationRequestIssue.ts +1 -1
  99. package/front_end/models/issues_manager/FederatedAuthRequestIssue.ts +1 -1
  100. package/front_end/models/issues_manager/FederatedAuthUserInfoRequestIssue.ts +1 -1
  101. package/front_end/models/issues_manager/GenericIssue.ts +7 -7
  102. package/front_end/models/issues_manager/HeavyAdIssue.ts +3 -3
  103. package/front_end/models/issues_manager/Issue.ts +11 -12
  104. package/front_end/models/issues_manager/IssueAggregator.ts +0 -9
  105. package/front_end/models/issues_manager/IssueResolver.ts +2 -2
  106. package/front_end/models/issues_manager/IssuesManager.ts +136 -137
  107. package/front_end/models/issues_manager/MixedContentIssue.ts +1 -1
  108. package/front_end/models/issues_manager/PartitioningBlobURLIssue.ts +3 -3
  109. package/front_end/models/issues_manager/QuirksModeIssue.ts +1 -1
  110. package/front_end/models/issues_manager/SRIMessageSignatureIssue.ts +2 -2
  111. package/front_end/models/issues_manager/SelectivePermissionsInterventionIssue.ts +2 -2
  112. package/front_end/models/issues_manager/SharedArrayBufferIssue.ts +2 -2
  113. package/front_end/models/issues_manager/SharedDictionaryIssue.ts +1 -1
  114. package/front_end/models/issues_manager/UnencodedDigestIssue.ts +3 -3
  115. package/front_end/models/issues_manager/issues_manager.ts +0 -2
  116. package/front_end/models/javascript_metadata/NativeFunctions.js +5 -1
  117. package/front_end/models/persistence/AutomaticFileSystemWorkspaceBinding.ts +5 -0
  118. package/front_end/models/source_map_scopes/FunctionCodeResolver.ts +13 -10
  119. package/front_end/models/stack_trace/StackTraceModel.ts +3 -3
  120. package/front_end/models/text_utils/ContentData.ts +27 -0
  121. package/front_end/models/trace/helpers/SamplesIntegrator.ts +21 -5
  122. package/front_end/models/trace_source_maps_resolver/SourceMapsResolver.ts +29 -14
  123. package/front_end/models/web_mcp/WebMCPModel.ts +24 -6
  124. package/front_end/models/workspace/IgnoreListManager.ts +6 -6
  125. package/front_end/models/workspace/UISourceCode.ts +3 -3
  126. package/front_end/models/workspace/WorkspaceImpl.ts +10 -1
  127. package/front_end/models/workspace_diff/WorkspaceDiff.ts +10 -4
  128. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +25 -22
  129. package/front_end/panels/ai_assistance/ExportConversation.ts +3 -4
  130. package/front_end/panels/ai_assistance/ai_assistance-meta.ts +3 -3
  131. package/front_end/panels/ai_assistance/components/ChatInput.ts +2 -2
  132. package/front_end/panels/ai_assistance/components/ChatMessage.ts +9 -1
  133. package/front_end/panels/ai_assistance/components/DisabledWidget.ts +2 -2
  134. package/front_end/panels/animation/AnimationTimeline.ts +17 -19
  135. package/front_end/panels/animation/AnimationUI.ts +5 -5
  136. package/front_end/panels/animation/animation-meta.ts +2 -2
  137. package/front_end/panels/application/ApplicationPanelSidebar.ts +4 -3
  138. package/front_end/panels/application/DeviceBoundSessionsView.ts +2 -6
  139. package/front_end/panels/application/FrameDetailsView.ts +1 -1
  140. package/front_end/panels/application/IndexedDBViews.ts +220 -266
  141. package/front_end/panels/application/OpenedWindowDetailsView.ts +1 -1
  142. package/front_end/panels/application/ResourcesPanel.ts +9 -2
  143. package/front_end/panels/application/ServiceWorkersView.ts +252 -211
  144. package/front_end/panels/application/StorageView.ts +97 -33
  145. package/front_end/panels/application/WebMCPView.ts +53 -44
  146. package/front_end/panels/application/components/AdsView.ts +201 -30
  147. package/front_end/panels/application/components/adsView.css +25 -0
  148. package/front_end/panels/application/indexedDBViews.css +38 -9
  149. package/front_end/panels/application/preloading/components/RuleSetDetailsView.ts +2 -2
  150. package/front_end/panels/application/preloading/helper/PreloadingForward.ts +2 -1
  151. package/front_end/panels/application/storageView.css +26 -9
  152. package/front_end/panels/browser_debugger/XHRBreakpointsSidebarPane.ts +5 -1
  153. package/front_end/panels/browser_debugger/browser_debugger-meta.ts +8 -6
  154. package/front_end/panels/common/DOMLinkifier.ts +1 -1
  155. package/front_end/panels/common/ExtensionServer.ts +119 -14
  156. package/front_end/panels/console/ConsoleContextSelector.ts +1 -1
  157. package/front_end/panels/console/ConsolePinPane.ts +2 -2
  158. package/front_end/panels/console/ConsolePrompt.ts +2 -2
  159. package/front_end/panels/console/ConsoleView.ts +3 -2
  160. package/front_end/panels/console/PromptBuilder.ts +2 -1
  161. package/front_end/panels/console/SymbolizedErrorWidget.ts +2 -1
  162. package/front_end/panels/console_counters/WarningErrorCounter.ts +2 -2
  163. package/front_end/panels/coverage/CoverageListView.ts +28 -28
  164. package/front_end/panels/coverage/CoverageModel.ts +2 -1
  165. package/front_end/panels/coverage/CoverageView.ts +22 -22
  166. package/front_end/panels/coverage/coverage-meta.ts +5 -5
  167. package/front_end/panels/css_overview/CSSOverviewCompletedView.ts +36 -36
  168. package/front_end/panels/css_overview/CSSOverviewProcessingView.ts +1 -1
  169. package/front_end/panels/css_overview/CSSOverviewSidebarPanel.ts +2 -2
  170. package/front_end/panels/css_overview/CSSOverviewStartView.ts +6 -6
  171. package/front_end/panels/css_overview/CSSOverviewUnusedDeclarations.ts +6 -6
  172. package/front_end/panels/css_overview/css_overview-meta.ts +2 -2
  173. package/front_end/panels/developer_resources/DeveloperResourcesListView.ts +1 -1
  174. package/front_end/panels/elements/AccessibilityTreeUtils.ts +8 -102
  175. package/front_end/panels/elements/AccessibilityTreeView.ts +163 -70
  176. package/front_end/panels/elements/ElementsPanel.ts +17 -11
  177. package/front_end/panels/elements/ElementsTreeElement.ts +4 -1
  178. package/front_end/panels/elements/ElementsTreeOutline.ts +4 -4
  179. package/front_end/panels/elements/LayoutPane.ts +1 -1
  180. package/front_end/panels/elements/MetricsSidebarPane.ts +100 -34
  181. package/front_end/panels/elements/StylePropertiesSection.ts +2 -2
  182. package/front_end/panels/elements/StylePropertyTreeElement.ts +8 -2
  183. package/front_end/panels/elements/StylesSidebarPane.ts +1 -1
  184. package/front_end/panels/elements/components/AccessibilityTreeNode.ts +7 -18
  185. package/front_end/panels/elements/components/ElementsBreadcrumbs.ts +2 -2
  186. package/front_end/panels/elements/components/QueryContainer.ts +1 -1
  187. package/front_end/panels/emulation/DeviceModeView.ts +52 -70
  188. package/front_end/panels/issues/AffectedDirectivesView.ts +1 -1
  189. package/front_end/panels/issues/AffectedResourcesView.ts +2 -1
  190. package/front_end/panels/issues/IssueView.ts +0 -2
  191. package/front_end/panels/issues/IssuesPane.ts +1 -8
  192. package/front_end/panels/js_timeline/js_timeline-meta.ts +3 -2
  193. package/front_end/panels/layer_viewer/LayerViewHost.ts +1 -1
  194. package/front_end/panels/linear_memory_inspector/LinearMemoryInspectorController.ts +3 -3
  195. package/front_end/panels/linear_memory_inspector/LinearMemoryInspectorPane.ts +5 -5
  196. package/front_end/panels/linear_memory_inspector/components/LinearMemoryHighlightChipList.ts +4 -4
  197. package/front_end/panels/linear_memory_inspector/components/LinearMemoryInspector.ts +2 -2
  198. package/front_end/panels/linear_memory_inspector/components/LinearMemoryNavigator.ts +6 -6
  199. package/front_end/panels/linear_memory_inspector/components/LinearMemoryValueInterpreter.ts +3 -3
  200. package/front_end/panels/linear_memory_inspector/components/ValueInterpreterDisplay.ts +8 -9
  201. package/front_end/panels/linear_memory_inspector/components/ValueInterpreterDisplayUtils.ts +2 -2
  202. package/front_end/panels/linear_memory_inspector/components/ValueInterpreterSettings.ts +1 -1
  203. package/front_end/panels/linear_memory_inspector/linear_memory_inspector-meta.ts +2 -2
  204. package/front_end/panels/network/NetworkDataGridNode.ts +124 -36
  205. package/front_end/panels/network/NetworkLogView.ts +9 -1
  206. package/front_end/panels/network/NetworkLogViewColumns.ts +18 -0
  207. package/front_end/panels/network/NetworkPanel.ts +2 -1
  208. package/front_end/panels/network/RequestPayloadView.ts +68 -20
  209. package/front_end/panels/network/RequestPreviewView.ts +7 -1
  210. package/front_end/panels/network/RequestTimingView.ts +95 -127
  211. package/front_end/panels/network/components/ResponseHeaderSection.ts +1 -1
  212. package/front_end/panels/network/forward/UIFilter.ts +1 -0
  213. package/front_end/panels/network/requestPayloadView.css +10 -0
  214. package/front_end/panels/performance_monitor/PerformanceMonitor.ts +12 -12
  215. package/front_end/panels/performance_monitor/performance_monitor-meta.ts +7 -7
  216. package/front_end/panels/profiler/HeapDetachedElementsView.ts +2 -2
  217. package/front_end/panels/profiler/HeapProfileView.ts +563 -51
  218. package/front_end/panels/profiler/HeapSnapshotView.ts +14 -9
  219. package/front_end/panels/profiler/ProfilesPanel.ts +13 -7
  220. package/front_end/panels/profiler/WritableProfileHeader.ts +152 -0
  221. package/front_end/panels/profiler/profiler.ts +2 -2
  222. package/front_end/panels/recorder/{components/ControlButton.ts → ControlButton.ts} +9 -10
  223. package/front_end/panels/recorder/{components/CreateRecordingView.ts → CreateRecordingView.ts} +59 -58
  224. package/front_end/panels/recorder/ExtensionView.ts +123 -0
  225. package/front_end/panels/recorder/RecorderController.ts +102 -92
  226. package/front_end/panels/recorder/{components/RecordingListView.ts → RecordingListView.ts} +22 -23
  227. package/front_end/panels/recorder/{components/RecordingView.ts → RecordingView.ts} +99 -96
  228. package/front_end/panels/recorder/{components/ReplaySection.ts → ReplaySection.ts} +20 -20
  229. package/front_end/panels/recorder/{components/SelectorPicker.ts → SelectorPicker.ts} +13 -13
  230. package/front_end/panels/recorder/{components/StepEditor.ts → StepEditor.ts} +367 -336
  231. package/front_end/panels/recorder/{components/StepView.ts → StepView.ts} +48 -49
  232. package/front_end/panels/recorder/{components/TimelineSection.ts → TimelineSection.ts} +2 -2
  233. package/front_end/panels/recorder/models/RecorderSettings.ts +2 -2
  234. package/front_end/panels/recorder/recorder-meta.ts +7 -7
  235. package/front_end/panels/recorder/recorder.ts +23 -1
  236. package/front_end/panels/recorder/{components/recordingView.css → recordingView.css} +1 -1
  237. package/front_end/panels/recorder/util/util.ts +113 -0
  238. package/front_end/panels/screencast/ScreencastApp.ts +1 -1
  239. package/front_end/panels/screencast/ScreencastView.ts +12 -12
  240. package/front_end/panels/search/SearchResultsPane.ts +4 -4
  241. package/front_end/panels/search/SearchView.ts +20 -20
  242. package/front_end/panels/security/SecurityModel.ts +7 -7
  243. package/front_end/panels/security/SecurityPanel.ts +99 -107
  244. package/front_end/panels/security/SecurityPanelSidebar.ts +7 -7
  245. package/front_end/panels/security/security-meta.ts +2 -2
  246. package/front_end/panels/sensors/LocationsSettingsTab.ts +19 -19
  247. package/front_end/panels/sensors/SensorsView.ts +27 -27
  248. package/front_end/panels/sensors/sensors-meta.ts +13 -167
  249. package/front_end/panels/settings/AISettingsTab.ts +98 -56
  250. package/front_end/panels/settings/EditFileSystemView.ts +2 -2
  251. package/front_end/panels/settings/FrameworkIgnoreListSettingsTab.ts +20 -20
  252. package/front_end/panels/settings/KeybindsSettingsTab.ts +20 -21
  253. package/front_end/panels/settings/SettingsScreen.ts +11 -11
  254. package/front_end/panels/settings/WorkspaceSettingsTab.ts +5 -5
  255. package/front_end/panels/settings/components/SyncSection.ts +12 -12
  256. package/front_end/panels/settings/emulation/DevicesSettingsTab.ts +13 -13
  257. package/front_end/panels/settings/emulation/components/UserAgentClientHintsForm.ts +31 -31
  258. package/front_end/panels/settings/emulation/emulation-meta.ts +1 -1
  259. package/front_end/panels/settings/settings-meta.ts +14 -14
  260. package/front_end/panels/sources/InplaceFormatterEditorAction.ts +2 -2
  261. package/front_end/panels/sources/NavigatorView.ts +7 -6
  262. package/front_end/panels/sources/ScopeChainSidebarPane.ts +83 -37
  263. package/front_end/panels/sources/SourcesNavigator.ts +22 -21
  264. package/front_end/panels/sources/SourcesView.ts +10 -1
  265. package/front_end/panels/sources/TabbedEditorContainer.ts +1 -1
  266. package/front_end/panels/sources/scopeChainSidebarPane.css +12 -4
  267. package/front_end/panels/sources/sources-meta.ts +4 -4
  268. package/front_end/panels/timeline/CompatibilityTracksAppender.ts +5 -2
  269. package/front_end/panels/timeline/IsolateSelector.ts +10 -8
  270. package/front_end/panels/timeline/TimelineFlameChartDataProvider.ts +2 -1
  271. package/front_end/panels/timeline/TimelineFlameChartNetworkDataProvider.ts +2 -1
  272. package/front_end/panels/timeline/TimelineFlameChartView.ts +1 -1
  273. package/front_end/panels/timeline/TimelinePanel.ts +20 -8
  274. package/front_end/panels/timeline/TimelineUIUtils.ts +149 -131
  275. package/front_end/panels/timeline/components/NetworkRequestDetails.ts +4 -2
  276. package/front_end/panels/timeline/components/NetworkRequestTooltip.ts +4 -2
  277. package/front_end/panels/timeline/components/insights/ImageRef.ts +2 -1
  278. package/front_end/panels/timeline/timeline-meta.ts +3 -2
  279. package/front_end/panels/utils/utils.ts +7 -3
  280. package/front_end/panels/web_audio/WebAudioView.ts +17 -18
  281. package/front_end/panels/web_audio/web_audio-meta.ts +3 -3
  282. package/front_end/panels/whats_new/ReleaseNoteView.ts +1 -1
  283. package/front_end/panels/whats_new/whats_new-meta.ts +11 -11
  284. package/front_end/third_party/chromium/README.chromium +1 -1
  285. package/front_end/third_party/lit/lib/async-directive.d.ts +2 -1
  286. package/front_end/third_party/lit/lib/async-directive.js +1 -1
  287. package/front_end/third_party/lit/lib/async-directive.js.map +1 -1
  288. package/front_end/third_party/lit/lib/decorators.d.ts +2 -1
  289. package/front_end/third_party/lit/lib/decorators.js.map +1 -1
  290. package/front_end/third_party/lit/lib/directive.d.ts +2 -1
  291. package/front_end/third_party/lit/lib/directive.js.map +1 -1
  292. package/front_end/third_party/lit/lib/directives.d.ts +2 -1
  293. package/front_end/third_party/lit/lib/directives.js +8 -8
  294. package/front_end/third_party/lit/lib/directives.js.map +1 -1
  295. package/front_end/third_party/lit/lib/lit.d.ts +2 -1
  296. package/front_end/third_party/lit/lib/lit.js +1 -1
  297. package/front_end/third_party/lit/lib/lit.js.map +1 -1
  298. package/front_end/third_party/lit/lib/static-html.d.ts +2 -1
  299. package/front_end/third_party/lit/lib/static-html.js +1 -1
  300. package/front_end/third_party/lit/lib/static-html.js.map +1 -1
  301. package/front_end/third_party/lit/package.json +2 -2
  302. package/front_end/third_party/lit/rebuild.sh +1 -1
  303. package/front_end/third_party/lit/src/async-directive.ts +5 -0
  304. package/front_end/ui/components/dialogs/Dialog.ts +1 -1
  305. package/front_end/ui/components/dialogs/ShortcutDialog.ts +1 -1
  306. package/front_end/ui/components/diff_view/DiffView.ts +2 -2
  307. package/front_end/ui/components/issue_counter/IssueCounter.ts +3 -3
  308. package/front_end/ui/components/issue_counter/IssueLinkIcon.ts +5 -5
  309. package/front_end/ui/components/lists/List.ts +2 -2
  310. package/front_end/ui/components/panel_feedback/PreviewToggle.ts +1 -1
  311. package/front_end/ui/components/request_link_icon/RequestLinkIcon.ts +5 -5
  312. package/front_end/ui/components/settings/SettingCheckbox.ts +1 -1
  313. package/front_end/ui/components/snackbars/Snackbar.ts +1 -1
  314. package/front_end/ui/components/survey_link/SurveyLink.ts +3 -3
  315. package/front_end/ui/components/text_editor/config.ts +2 -2
  316. package/front_end/ui/components/tree_outline/TreeOutline.ts +34 -1
  317. package/front_end/ui/legacy/ReportView.ts +5 -0
  318. package/front_end/ui/legacy/Treeoutline.ts +6 -0
  319. package/front_end/ui/legacy/View.ts +1 -1
  320. package/front_end/ui/legacy/Widget.ts +14 -15
  321. package/front_end/ui/legacy/components/data_grid/DataGrid.ts +2 -0
  322. package/front_end/ui/legacy/components/data_grid/DataGridElement.ts +8 -3
  323. package/front_end/ui/legacy/components/data_grid/data_grid.ts +1 -0
  324. package/front_end/ui/legacy/components/object_ui/ObjectPopoverHelper.ts +1 -1
  325. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +106 -38
  326. package/front_end/ui/legacy/components/source_frame/ResourceSourceFrame.ts +7 -1
  327. package/front_end/ui/legacy/components/source_frame/SourceFrame.ts +2 -1
  328. package/front_end/ui/legacy/components/utils/ImagePreview.ts +4 -2
  329. package/front_end/ui/legacy/components/utils/JSPresentationUtils.ts +10 -1
  330. package/front_end/ui/legacy/inspectorCommon.css +5 -0
  331. package/front_end/ui/visual_logging/KnownContextValues.ts +3 -0
  332. package/mcp/mcp.ts +2 -0
  333. package/package.json +4 -4
  334. package/front_end/models/issues_manager/AttributionReportingIssue.ts +0 -247
  335. package/front_end/models/issues_manager/descriptions/arInsecureContext.md +0 -7
  336. package/front_end/models/issues_manager/descriptions/arInvalidInfoHeader.md +0 -5
  337. package/front_end/models/issues_manager/descriptions/arInvalidRegisterOsSourceHeader.md +0 -5
  338. package/front_end/models/issues_manager/descriptions/arInvalidRegisterOsTriggerHeader.md +0 -5
  339. package/front_end/models/issues_manager/descriptions/arInvalidRegisterSourceHeader.md +0 -5
  340. package/front_end/models/issues_manager/descriptions/arInvalidRegisterTriggerHeader.md +0 -5
  341. package/front_end/models/issues_manager/descriptions/arNavigationRegistrationUniqueScopeAlreadySet.md +0 -5
  342. package/front_end/models/issues_manager/descriptions/arNavigationRegistrationWithoutTransientUserActivation.md +0 -6
  343. package/front_end/models/issues_manager/descriptions/arNoRegisterOsSourceHeader.md +0 -5
  344. package/front_end/models/issues_manager/descriptions/arNoRegisterOsTriggerHeader.md +0 -5
  345. package/front_end/models/issues_manager/descriptions/arNoRegisterSourceHeader.md +0 -5
  346. package/front_end/models/issues_manager/descriptions/arNoRegisterTriggerHeader.md +0 -5
  347. package/front_end/models/issues_manager/descriptions/arNoWebOrOsSupport.md +0 -4
  348. package/front_end/models/issues_manager/descriptions/arOsSourceIgnored.md +0 -18
  349. package/front_end/models/issues_manager/descriptions/arOsTriggerIgnored.md +0 -19
  350. package/front_end/models/issues_manager/descriptions/arPermissionPolicyDisabled.md +0 -8
  351. package/front_end/models/issues_manager/descriptions/arSourceAndTriggerHeaders.md +0 -9
  352. package/front_end/models/issues_manager/descriptions/arSourceIgnored.md +0 -13
  353. package/front_end/models/issues_manager/descriptions/arTriggerIgnored.md +0 -12
  354. package/front_end/models/issues_manager/descriptions/arUntrustworthyReportingOrigin.md +0 -10
  355. package/front_end/models/issues_manager/descriptions/arWebAndOsHeaders.md +0 -11
  356. package/front_end/panels/issues/AttributionReportingIssueDetailsView.ts +0 -182
  357. package/front_end/panels/profiler/ProfileView.ts +0 -611
  358. package/front_end/panels/recorder/components/ExtensionView.ts +0 -132
  359. package/front_end/panels/recorder/components/components.ts +0 -25
  360. package/front_end/panels/recorder/components/util.ts +0 -116
  361. /package/front_end/panels/recorder/{components/controlButton.css → controlButton.css} +0 -0
  362. /package/front_end/panels/recorder/{components/createRecordingView.css → createRecordingView.css} +0 -0
  363. /package/front_end/panels/recorder/{components/extensionView.css → extensionView.css} +0 -0
  364. /package/front_end/panels/recorder/{components/recordingListView.css → recordingListView.css} +0 -0
  365. /package/front_end/panels/recorder/{components/replaySection.css → replaySection.css} +0 -0
  366. /package/front_end/panels/recorder/{components/selectorPicker.css → selectorPicker.css} +0 -0
  367. /package/front_end/panels/recorder/{components/startView.css → startView.css} +0 -0
  368. /package/front_end/panels/recorder/{components/stepEditor.css → stepEditor.css} +0 -0
  369. /package/front_end/panels/recorder/{components/stepView.css → stepView.css} +0 -0
  370. /package/front_end/panels/recorder/{components/timelineSection.css → timelineSection.css} +0 -0
@@ -2,17 +2,16 @@
2
2
  // Use of this source code is governed by a BSD-style license that can be
3
3
  // found in the LICENSE file.
4
4
 
5
- import * as i18n from '../../../core/i18n/i18n.js';
6
- import * as Platform from '../../../core/platform/platform.js';
7
- import type * as Puppeteer from '../../../third_party/puppeteer/puppeteer.js';
8
- import * as Buttons from '../../../ui/components/buttons/buttons.js';
9
- import * as SuggestionInput from '../../../ui/components/suggestion_input/suggestion_input.js';
10
- import * as UI from '../../../ui/legacy/legacy.js';
11
- import * as Lit from '../../../ui/lit/lit.js';
12
- import * as VisualLogging from '../../../ui/visual_logging/visual_logging.js';
13
- import * as Models from '../models/models.js';
14
- import * as Util from '../util/util.js';
5
+ import * as i18n from '../../core/i18n/i18n.js';
6
+ import * as Platform from '../../core/platform/platform.js';
7
+ import type * as Puppeteer from '../../third_party/puppeteer/puppeteer.js';
8
+ import * as Buttons from '../../ui/components/buttons/buttons.js';
9
+ import * as SuggestionInput from '../../ui/components/suggestion_input/suggestion_input.js';
10
+ import * as UI from '../../ui/legacy/legacy.js';
11
+ import * as Lit from '../../ui/lit/lit.js';
12
+ import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
15
13
 
14
+ import * as Models from './models/models.js';
16
15
  import {RequestSelectorAttributeEvent, SelectorPicker} from './SelectorPicker.js';
17
16
  import stepEditorStyles from './stepEditor.css.js';
18
17
  import {
@@ -27,10 +26,10 @@ import {
27
26
  type Keys,
28
27
  type OptionalKeys,
29
28
  type RequiredKeys,
30
- } from './util.js';
29
+ SharedObject,
30
+ } from './util/util.js';
31
31
 
32
- const {html, Decorators, Directives, LitElement} = Lit;
33
- const {customElement, property, state} = Decorators;
32
+ const {html, render, Directives} = Lit;
34
33
  const {live} = Directives;
35
34
  const {widget} = UI.Widget;
36
35
 
@@ -226,7 +225,7 @@ const attributesByType = deepFreeze<{
226
225
 
227
226
  const UIStrings = {
228
227
  /**
229
- * @description The text that is disabled when the steps were not saved due to an error. The error message itself is always in English and not translated.
228
+ * @description The text that is displayed when the steps were not saved due to an error. The error message itself is always in English and not translated.
230
229
  * @example {Saving failed} error
231
230
  */
232
231
  notSaved: 'Not saved: {error}',
@@ -240,7 +239,7 @@ const UIStrings = {
240
239
  */
241
240
  deleteRow: 'Delete row',
242
241
  /**
243
- * @description The title of a button that adds a new input field for the entry of the frame index. Frame index is the number of the frame within the page's frame tree.
242
+ * @description The title of a button that adds a new input field for the entry of the frame index. Frame index is the number of the frame within the pages frame tree.
244
243
  */
245
244
  addFrameIndex: 'Add frame index within the frame tree',
246
245
  /**
@@ -264,19 +263,13 @@ const UIStrings = {
264
263
  */
265
264
  removeSelector: 'Remove a selector',
266
265
  /**
267
- * @description The error message display when a user enters a type in the input not associates with any existing types.
266
+ * @description The error message displayed when a user enters a type in the input that is not associated with any existing types.
268
267
  */
269
- unknownActionType: 'Unknown action type.',
268
+ unknownActionType: 'Enter a valid action type',
270
269
  } as const;
271
- const str_ = i18n.i18n.registerUIStrings('panels/recorder/components/StepEditor.ts', UIStrings);
270
+ const str_ = i18n.i18n.registerUIStrings('panels/recorder/StepEditor.ts', UIStrings);
272
271
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
273
272
 
274
- declare global {
275
- interface HTMLElementTagNameMap {
276
- 'devtools-recorder-step-editor': StepEditor;
277
- }
278
- }
279
-
280
273
  export class StepEditedEvent extends Event {
281
274
  static readonly eventName = 'stepedited';
282
275
  data: Models.Schema.Step;
@@ -335,9 +328,9 @@ interface Puppeteer {
335
328
  }
336
329
 
337
330
  export class EditorState {
338
- static #puppeteer: Util.SharedObject.SharedObject<Puppeteer> = new Util.SharedObject.SharedObject(
339
- () => Models.RecordingPlayer.RecordingPlayer.connectPuppeteer(),
340
- ({browser}) => Models.RecordingPlayer.RecordingPlayer.disconnectPuppeteer(browser));
331
+ static #puppeteer: SharedObject.SharedObject<Puppeteer> =
332
+ new SharedObject.SharedObject(() => Models.RecordingPlayer.RecordingPlayer.connectPuppeteer(),
333
+ ({browser}) => Models.RecordingPlayer.RecordingPlayer.disconnectPuppeteer(browser));
341
334
 
342
335
  static async default(type: Models.Schema.StepType): Promise<DeepImmutable<EditorState>> {
343
336
  const state = {type};
@@ -358,9 +351,9 @@ export class EditorState {
358
351
  static async defaultByAttribute<Attribute extends keyof typeof defaultValuesByAttribute>(
359
352
  state: DeepImmutable<EditorState>,
360
353
  attribute: Attribute): Promise<DeepImmutable<typeof defaultValuesByAttribute[Attribute]>>;
361
- static async defaultByAttribute(_state: DeepImmutable<EditorState>, attribute: keyof typeof defaultValuesByAttribute):
362
- Promise<unknown> {
363
- return await this.#puppeteer.run(puppeteer => {
354
+ static async defaultByAttribute(_state: DeepImmutable<EditorState>,
355
+ attribute: keyof typeof defaultValuesByAttribute): Promise<unknown> {
356
+ return await this.#puppeteer.run<unknown>(puppeteer => {
364
357
  switch (attribute) {
365
358
  case 'assertedEvents': {
366
359
  return immutableDeepAssign(defaultValuesByAttribute.assertedEvents, new ArrayAssignments({
@@ -373,14 +366,12 @@ export class EditorState {
373
366
  return puppeteer.page.url() || defaultValuesByAttribute.url;
374
367
  }
375
368
  case 'height': {
376
- return (
377
- puppeteer.page.evaluate(() => (visualViewport as VisualViewport).height) ||
378
- defaultValuesByAttribute.height);
369
+ return puppeteer.page.evaluate(() => (visualViewport as VisualViewport).height)
370
+ .then(h => h || defaultValuesByAttribute.height);
379
371
  }
380
372
  case 'width': {
381
- return (
382
- puppeteer.page.evaluate(() => (visualViewport as VisualViewport).width) ||
383
- defaultValuesByAttribute.width);
373
+ return puppeteer.page.evaluate(() => (visualViewport as VisualViewport).width)
374
+ .then(w => w || defaultValuesByAttribute.width);
384
375
  }
385
376
  default: {
386
377
  return defaultValuesByAttribute[attribute];
@@ -389,11 +380,10 @@ export class EditorState {
389
380
  });
390
381
  }
391
382
 
392
- static fromStep(step: DeepImmutable<Models.Schema.Step>): DeepImmutable<EditorState> {
383
+ static fromStep(step: Models.Schema.Step): DeepImmutable<EditorState> {
393
384
  const state = structuredClone(step) as EditorState;
394
385
  for (const key of ['parameters', 'properties'] as Array<'properties'>) {
395
386
  if (key in step && step[key] !== undefined) {
396
- // @ts-expect-error Potential infinite type instantiation.
397
387
  state[key] = JSON.stringify(step[key]);
398
388
  }
399
389
  }
@@ -458,212 +448,98 @@ export class EditorState {
458
448
  * @fires RequestSelectorAttributeEvent#requestselectorattribute
459
449
  * @fires StepEditedEvent#stepedited
460
450
  */
461
- @customElement('devtools-recorder-step-editor')
462
- export class StepEditor extends LitElement {
463
- @state() private declare state: DeepImmutable<EditorState>;
464
- @state() private declare error: string|undefined;
465
-
466
- @property({type: Boolean}) declare isTypeEditable: boolean;
467
- @property({type: Boolean}) declare disabled: boolean;
468
-
469
- #renderedAttributes = new Set<Attribute>();
470
-
471
- constructor() {
472
- super();
473
-
474
- this.state = {type: Models.Schema.StepType.WaitForElement};
451
+ export interface ViewInput {
452
+ state: DeepImmutable<EditorState>;
453
+ disabled: boolean;
454
+ error: string|undefined;
455
+ isTypeEditable: boolean;
456
+ handleInputBlur: <A extends Attribute>(opts: {
457
+ attribute: A,
458
+ from(value: DataType<A>): DeepImmutable<DeepPartial<Assignments<EditorState>>>|undefined,
459
+ }) => (event: Event) => void;
460
+ handleTypeInputBlur: (event: Event) => Promise<void>;
461
+ handleAddRowClickEvent: (event: MouseEvent) => Promise<void>;
462
+ handleDeleteRowClick: (attribute: Attribute) => (event: MouseEvent) => void;
463
+ handleSelectorPicked: (data: Models.Schema.StepWithSelectors&
464
+ Pick<Models.Schema.ClickAttributes, 'offsetX'|'offsetY'>) => void;
465
+ handleAttributeRequested: (send: (attribute?: string) => void) => void;
466
+ handleAddOrRemoveClick: (assignments: DeepImmutable<DeepPartial<Assignments<EditorState>>>,
467
+ query: string) => (event: Event) => void;
468
+ handleKeyDownEvent: (event: Event) => void;
469
+ }
475
470
 
476
- this.isTypeEditable = true;
477
- this.disabled = false;
471
+ function renderInlineButton(
472
+ input: ViewInput, opts: {class: string, title: string, iconName: string, onClick: (event: MouseEvent) => void}):
473
+ Lit.TemplateResult|undefined {
474
+ if (input.disabled) {
475
+ return;
478
476
  }
477
+ return html`
478
+ <devtools-button
479
+ title=${opts.title}
480
+ .accessibleLabel=${opts.title}
481
+ .size=${Buttons.Button.Size.SMALL}
482
+ .iconName=${opts.iconName}
483
+ .variant=${Buttons.Button.Variant.ICON}
484
+ jslog=${VisualLogging.action(opts.class).track({
485
+ click: true,
486
+ })}
487
+ class="inline-button ${opts.class}"
488
+ @click=${opts.onClick}
489
+ ></devtools-button>
490
+ `;
491
+ }
479
492
 
480
- protected override createRenderRoot(): HTMLElement|DocumentFragment {
481
- const root = super.createRenderRoot();
482
- root.addEventListener('keydown', this.#handleKeyDownEvent);
483
- return root;
493
+ function renderDeleteButton(input: ViewInput, attribute: Attribute): Lit.TemplateResult|undefined {
494
+ if (input.disabled) {
495
+ return;
484
496
  }
485
497
 
486
- set step(step: DeepImmutable<Models.Schema.Step>) {
487
- this.state = deepFreeze(EditorState.fromStep(step));
488
- this.error = undefined;
498
+ const attributes = attributesByType[input.state.type];
499
+ const optional = [...attributes.optional].includes(attribute as typeof attributes.optional[number]);
500
+ if (!optional || input.disabled) {
501
+ return;
489
502
  }
490
503
 
491
- #commit(updatedState: DeepImmutable<EditorState>): void {
492
- try {
493
- this.dispatchEvent(new StepEditedEvent(EditorState.toStep(updatedState)));
494
- // Note we don't need to update this variable since it will come from up
495
- // the tree, but processing up the tree is asynchronous implying we cannot
496
- // reliably know when the state will come back down. Since we need to
497
- // focus the DOM elements that may be created as a result of this new
498
- // state, we set it here for waiting on the updateComplete promise later.
499
- this.state = updatedState;
500
- } catch (error) {
501
- this.error = error.message;
502
- }
503
- }
504
-
505
- #handleSelectorPicked =
506
- (data: Models.Schema.StepWithSelectors&Pick<Models.Schema.ClickAttributes, 'offsetX'|'offsetY'>): void => {
507
- this.#commit(immutableDeepAssign(this.state, {
508
- target: data.target,
509
- frame: data.frame,
510
- selectors: data.selectors.map(selector => typeof selector === 'string' ? [selector] : selector),
511
- offsetX: data.offsetX,
512
- offsetY: data.offsetY,
513
- }));
514
- };
515
-
516
- #handleAttributeRequested = (send: (attribute?: string) => void): void => {
517
- this.dispatchEvent(new RequestSelectorAttributeEvent(send));
518
- };
519
-
520
- #handleAddOrRemoveClick = (assignments: DeepImmutable<DeepPartial<Assignments<EditorState>>>, query: string):
521
- ((event: Event) => void) => event => {
522
- event.preventDefault();
523
- event.stopPropagation();
524
-
525
- this.#commit(immutableDeepAssign(this.state, assignments));
526
-
527
- this.#ensureFocus(query);
528
- };
529
-
530
- #handleKeyDownEvent = (event: Event): void => {
531
- assert(event instanceof KeyboardEvent);
532
- if (event.target instanceof SuggestionInput.SuggestionInput.SuggestionInput && event.key === 'Enter') {
533
- event.preventDefault();
534
- event.stopPropagation();
535
- const elements = this.renderRoot.querySelectorAll('devtools-suggestion-input');
536
- const element = [...elements].findIndex(value => value === event.target);
537
- if (element >= 0 && element + 1 < elements.length) {
538
- elements[element + 1].focus();
539
- } else {
540
- event.target.blur();
541
- }
542
- }
543
- };
544
-
545
- #handleInputBlur = <A extends Attribute>(opts: {
546
- attribute: A,
547
- // If there are not assignments, then we should ignore the event.
548
- from(this: StepEditor, value: DataType<A>): DeepImmutable<DeepPartial<Assignments<EditorState>>>|undefined,
549
- }): ((event: Event) => void) => event => {
550
- assert(event.target instanceof SuggestionInput.SuggestionInput.SuggestionInput);
551
- if (event.target.disabled) {
552
- return;
553
- }
554
-
555
- const dataType = dataTypeByAttribute[opts.attribute];
556
- const value = typeConverters[dataType](event.target.value) as DataType<A>;
557
- const assignments = opts.from.bind(this)(value);
558
- if (!assignments) {
559
- return;
560
- }
561
- this.#commit(immutableDeepAssign(this.state, assignments));
562
- };
563
-
564
- #handleTypeInputBlur = async(event: Event): Promise<void> => {
565
- assert(event.target instanceof SuggestionInput.SuggestionInput.SuggestionInput);
566
- if (event.target.disabled) {
567
- return;
568
- }
569
-
570
- const value = event.target.value as Models.Schema.StepType;
571
- if (value === this.state.type) {
572
- return;
573
- }
574
- if (!Object.values(Models.Schema.StepType).includes(value)) {
575
- this.error = i18nString(UIStrings.unknownActionType);
576
- return;
577
- }
578
- this.#commit(await EditorState.default(value));
579
- };
580
-
581
- #handleAddRowClickEvent = async(event: MouseEvent): Promise<void> => {
582
- event.preventDefault();
583
- event.stopPropagation();
584
-
585
- const attribute = (event.target as HTMLElement).dataset.attribute as Attribute;
586
-
587
- this.#commit(immutableDeepAssign(this.state, {
588
- [attribute]: await EditorState.defaultByAttribute(this.state, attribute),
589
- }));
590
-
591
- this.#ensureFocus(`[data-attribute=${attribute}].attribute devtools-suggestion-input`);
592
- };
593
-
594
- #renderInlineButton(opts: {class: string, title: string, iconName: string, onClick: (event: MouseEvent) => void}):
595
- Lit.TemplateResult|undefined {
596
- if (this.disabled) {
597
- return;
598
- }
599
- return html`
600
- <devtools-button
601
- title=${opts.title}
602
- .accessibleLabel=${opts.title}
603
- .size=${Buttons.Button.Size.SMALL}
604
- .iconName=${opts.iconName}
605
- .variant=${Buttons.Button.Variant.ICON}
606
- jslog=${VisualLogging.action(opts.class).track({
607
- click: true,
608
- })}
609
- class="inline-button ${opts.class}"
610
- @click=${opts.onClick}
611
- ></devtools-button>
612
- `;
613
- }
614
-
615
- #renderDeleteButton(attribute: Attribute): Lit.TemplateResult|undefined {
616
- if (this.disabled) {
617
- return;
618
- }
619
-
620
- const attributes = attributesByType[this.state.type];
621
- const optional = [...attributes.optional].includes(attribute as typeof attributes.optional[number]);
622
- if (!optional || this.disabled) {
623
- return;
624
- }
504
+ // clang-format off
505
+ return html`<devtools-button
506
+ .size=${Buttons.Button.Size.SMALL}
507
+ .iconName=${'bin'}
508
+ .variant=${Buttons.Button.Variant.ICON}
509
+ .title=${i18nString(UIStrings.deleteRow)}
510
+ class="inline-button delete-row"
511
+ data-attribute=${attribute}
512
+ jslog=${VisualLogging.action('delete').track({click: true})}
513
+ @click=${input.handleDeleteRowClick(attribute)}
514
+ ></devtools-button>`;
515
+ // clang-format on
516
+ }
625
517
 
626
- // clang-format off
627
- return html`<devtools-button
628
- .size=${Buttons.Button.Size.SMALL}
629
- .iconName=${'bin'}
630
- .variant=${Buttons.Button.Variant.ICON}
631
- .title=${i18nString(UIStrings.deleteRow)}
632
- class="inline-button delete-row"
633
- data-attribute=${attribute}
634
- jslog=${VisualLogging.action('delete').track({click: true})}
635
- @click=${(event: MouseEvent) => {
636
- event.preventDefault();
637
- event.stopPropagation();
518
+ type View = (input: ViewInput, _output: undefined, target: HTMLElement) => void;
638
519
 
639
- this.#commit(
640
- immutableDeepAssign(this.state, { [attribute]: undefined }),
641
- );
642
- }}
643
- ></devtools-button>`;
644
- // clang-format on
645
- }
520
+ const DEFAULT_VIEW: View = (input, _output, target) => {
521
+ const renderedAttributes = new Set<Attribute>();
646
522
 
647
- #renderTypeRow(editable: boolean): Lit.TemplateResult {
648
- this.#renderedAttributes.add('type');
523
+ function renderTypeRow(editable: boolean): Lit.TemplateResult {
524
+ renderedAttributes.add('type');
649
525
  // clang-format off
650
526
  return html`<div class="row attribute" data-attribute="type" jslog=${VisualLogging.treeItem('type').track({resize: true})}>
651
527
  <div id="type">type<span class="separator">:</span></div>
652
528
  <devtools-suggestion-input
653
529
  aria-labelledby="type"
654
- .disabled=${!editable || this.disabled}
530
+ .disabled=${!editable || input.disabled}
655
531
  .options=${Object.values(Models.Schema.StepType)}
656
532
  .placeholder=${defaultValuesByAttribute.type}
657
- .value=${live(this.state.type)}
658
- @blur=${this.#handleTypeInputBlur}
533
+ .value=${live(input.state.type)}
534
+ @blur=${input.handleTypeInputBlur}
659
535
  ></devtools-suggestion-input>
660
536
  </div>`;
661
537
  // clang-format on
662
538
  }
663
539
 
664
- #renderRow(attribute: Attribute): Lit.TemplateResult|undefined {
665
- this.#renderedAttributes.add(attribute);
666
- const attributeValue = this.state[attribute]?.toString();
540
+ function renderRow(attribute: Attribute): Lit.TemplateResult|undefined {
541
+ renderedAttributes.add(attribute);
542
+ const attributeValue = input.state[attribute]?.toString();
667
543
  if (attributeValue === undefined) {
668
544
  return;
669
545
  }
@@ -671,7 +547,7 @@ export class StepEditor extends LitElement {
671
547
  return html`<div class="row attribute" data-attribute=${attribute} jslog=${VisualLogging.treeItem(Platform.StringUtilities.toKebabCase(attribute)).track({resize: true})}>
672
548
  <div id=${attribute}>${attribute}<span class="separator">:</span></div>
673
549
  <devtools-suggestion-input
674
- .disabled=${this.disabled}
550
+ .disabled=${input.disabled}
675
551
  aria-labelledby=${attribute}
676
552
  .placeholder=${defaultValuesByAttribute[attribute].toString()}
677
553
  .value=${live(attributeValue)}
@@ -685,24 +561,24 @@ export class StepEditor extends LitElement {
685
561
  return '';
686
562
  }
687
563
  })()}
688
- @blur=${this.#handleInputBlur({
564
+ @blur=${input.handleInputBlur({
689
565
  attribute,
690
566
  from(value) {
691
- if (this.state[attribute] === undefined) {
567
+ if (input.state[attribute] === undefined || input.state[attribute] === value) {
692
568
  return;
693
569
  }
694
570
  return {[attribute]: value};
695
571
  },
696
572
  })}
697
573
  ></devtools-suggestion-input>
698
- ${this.#renderDeleteButton(attribute)}
574
+ ${renderDeleteButton(input, attribute)}
699
575
  </div>`;
700
576
  // clang-format on
701
577
  }
702
578
 
703
- #renderFrameRow(): Lit.TemplateResult|undefined {
704
- this.#renderedAttributes.add('frame');
705
- if (this.state.frame === undefined) {
579
+ function renderFrameRow(): Lit.TemplateResult|undefined {
580
+ renderedAttributes.add('frame');
581
+ if (input.state.frame === undefined) {
706
582
  return;
707
583
  }
708
584
  // clang-format off
@@ -710,21 +586,21 @@ export class StepEditor extends LitElement {
710
586
  <div class="attribute" data-attribute="frame" jslog=${VisualLogging.treeItem('frame').track({resize: true})}>
711
587
  <div class="row">
712
588
  <div id="frame">frame<span class="separator">:</span></div>
713
- ${this.#renderDeleteButton('frame')}
589
+ ${renderDeleteButton(input, 'frame')}
714
590
  </div>
715
- ${this.state.frame.map((frame, index, frames) => {
591
+ ${input.state.frame.map((frame, index, frames) => {
716
592
  return html`
717
593
  <div class="padded row">
718
594
  <devtools-suggestion-input
719
595
  aria-labelledby="frame"
720
- .disabled=${this.disabled}
596
+ .disabled=${input.disabled}
721
597
  .placeholder=${defaultValuesByAttribute.frame[0].toString()}
722
598
  .value=${live(frame.toString())}
723
599
  data-path=${`frame.${index}`}
724
- @blur=${this.#handleInputBlur({
600
+ @blur=${input.handleInputBlur({
725
601
  attribute: 'frame',
726
602
  from(value) {
727
- if (this.state.frame?.[index] === undefined) {
603
+ if (input.state.frame?.[index] === undefined || input.state.frame[index] === value) {
728
604
  return;
729
605
  }
730
606
  return {
@@ -733,11 +609,11 @@ export class StepEditor extends LitElement {
733
609
  },
734
610
  })}
735
611
  ></devtools-suggestion-input>
736
- ${this.#renderInlineButton({
612
+ ${renderInlineButton(input, {
737
613
  class: 'add-frame',
738
614
  title: i18nString(UIStrings.addFrameIndex),
739
615
  iconName: 'plus',
740
- onClick: this.#handleAddOrRemoveClick(
616
+ onClick: input.handleAddOrRemoveClick(
741
617
  {
742
618
  frame: new ArrayAssignments({
743
619
  [index + 1]: new InsertAssignment(
@@ -748,11 +624,11 @@ export class StepEditor extends LitElement {
748
624
  `devtools-suggestion-input[data-path="frame.${index + 1}"]`,
749
625
  ),
750
626
  })}
751
- ${this.#renderInlineButton({
627
+ ${renderInlineButton(input, {
752
628
  class: 'remove-frame',
753
629
  title: i18nString(UIStrings.removeFrameIndex),
754
630
  iconName: 'minus',
755
- onClick: this.#handleAddOrRemoveClick(
631
+ onClick: input.handleAddOrRemoveClick(
756
632
  {
757
633
  frame: new ArrayAssignments({ [index]: undefined }),
758
634
  },
@@ -770,9 +646,9 @@ export class StepEditor extends LitElement {
770
646
  // clang-format on
771
647
  }
772
648
 
773
- #renderSelectorsRow(): Lit.TemplateResult|undefined {
774
- this.#renderedAttributes.add('selectors');
775
- if (this.state.selectors === undefined) {
649
+ function renderSelectorsRow(): Lit.TemplateResult|undefined {
650
+ renderedAttributes.add('selectors');
651
+ if (input.state.selectors === undefined) {
776
652
  return;
777
653
  }
778
654
  // clang-format off
@@ -780,20 +656,20 @@ export class StepEditor extends LitElement {
780
656
  <div class="row">
781
657
  <div>selectors<span class="separator">:</span></div>
782
658
  ${widget(SelectorPicker, {
783
- disabled: this.disabled,
784
- onSelectorPicked: this.#handleSelectorPicked,
785
- onAttributeRequested: this.#handleAttributeRequested,
659
+ disabled: input.disabled,
660
+ onSelectorPicked: input.handleSelectorPicked,
661
+ onAttributeRequested: input.handleAttributeRequested,
786
662
  })}
787
- ${this.#renderDeleteButton('selectors')}
663
+ ${renderDeleteButton(input, 'selectors')}
788
664
  </div>
789
- ${this.state.selectors.map((selector, index, selectors) => {
665
+ ${input.state.selectors.map((selector, index, selectors) => {
790
666
  return html`<div class="padded row" data-selector-path=${index}>
791
667
  <div id="selector-${index}">selector #${index + 1}<span class="separator">:</span></div>
792
- ${this.#renderInlineButton({
668
+ ${renderInlineButton(input, {
793
669
  class: 'add-selector',
794
670
  title: i18nString(UIStrings.addSelector),
795
671
  iconName: 'plus',
796
- onClick: this.#handleAddOrRemoveClick(
672
+ onClick: input.handleAddOrRemoveClick(
797
673
  {
798
674
  selectors: new ArrayAssignments({
799
675
  [index + 1]: new InsertAssignment(
@@ -804,11 +680,11 @@ export class StepEditor extends LitElement {
804
680
  `devtools-suggestion-input[data-path="selectors.${index + 1}.0"]`,
805
681
  ),
806
682
  })}
807
- ${this.#renderInlineButton({
683
+ ${renderInlineButton(input, {
808
684
  class: 'remove-selector',
809
685
  title: i18nString(UIStrings.removeSelector),
810
686
  iconName: 'minus',
811
- onClick: this.#handleAddOrRemoveClick(
687
+ onClick: input.handleAddOrRemoveClick(
812
688
  { selectors: new ArrayAssignments({ [index]: undefined }) },
813
689
  `devtools-suggestion-input[data-path="selectors.${Math.min(
814
690
  index,
@@ -824,15 +700,15 @@ export class StepEditor extends LitElement {
824
700
  >
825
701
  <devtools-suggestion-input
826
702
  aria-labelledby="selector-${index}"
827
- .disabled=${this.disabled}
703
+ .disabled=${input.disabled}
828
704
  .placeholder=${defaultValuesByAttribute.selectors[0][0]}
829
705
  .value=${live(part)}
830
706
  data-path=${`selectors.${index}.${partIndex}`}
831
- @blur=${this.#handleInputBlur({
707
+ @blur=${input.handleInputBlur({
832
708
  attribute: 'selectors',
833
709
  from(value) {
834
710
  if (
835
- this.state.selectors?.[index]?.[partIndex] === undefined
711
+ input.state.selectors?.[index]?.[partIndex] === undefined || input.state.selectors[index][partIndex] === value
836
712
  ) {
837
713
  return;
838
714
  }
@@ -846,11 +722,11 @@ export class StepEditor extends LitElement {
846
722
  },
847
723
  })}
848
724
  ></devtools-suggestion-input>
849
- ${this.#renderInlineButton({
725
+ ${renderInlineButton(input, {
850
726
  class: 'add-selector-part',
851
727
  title: i18nString(UIStrings.addSelectorPart),
852
728
  iconName: 'plus',
853
- onClick: this.#handleAddOrRemoveClick(
729
+ onClick: input.handleAddOrRemoveClick(
854
730
  {
855
731
  selectors: new ArrayAssignments({
856
732
  [index]: new ArrayAssignments({
@@ -865,11 +741,11 @@ export class StepEditor extends LitElement {
865
741
  }"]`,
866
742
  ),
867
743
  })}
868
- ${this.#renderInlineButton({
744
+ ${renderInlineButton(input, {
869
745
  class: 'remove-selector-part',
870
746
  title: i18nString(UIStrings.removeSelectorPart),
871
747
  iconName: 'minus',
872
- onClick: this.#handleAddOrRemoveClick(
748
+ onClick: input.handleAddOrRemoveClick(
873
749
  {
874
750
  selectors: new ArrayAssignments({
875
751
  [index]: new ArrayAssignments({
@@ -890,18 +766,18 @@ export class StepEditor extends LitElement {
890
766
  // clang-format on
891
767
  }
892
768
 
893
- #renderAssertedEvents(): Lit.TemplateResult|undefined {
894
- this.#renderedAttributes.add('assertedEvents');
895
- if (this.state.assertedEvents === undefined) {
769
+ function renderAssertedEvents(): Lit.TemplateResult|undefined {
770
+ renderedAttributes.add('assertedEvents');
771
+ if (input.state.assertedEvents === undefined) {
896
772
  return;
897
773
  }
898
774
  // clang-format off
899
775
  return html`<div class="attribute" data-attribute="assertedEvents" jslog=${VisualLogging.treeItem('asserted-events')}>
900
776
  <div class="row">
901
777
  <div>asserted events<span class="separator">:</span></div>
902
- ${this.#renderDeleteButton('assertedEvents')}
778
+ ${renderDeleteButton(input, 'assertedEvents')}
903
779
  </div>
904
- ${this.state.assertedEvents.map((event, index) => {
780
+ ${input.state.assertedEvents.map((event, index) => {
905
781
  return html` <div class="padded row" jslog=${VisualLogging.treeItem('event-type')}>
906
782
  <div id="event-type">type<span class="separator">:</span></div>
907
783
  <div aria-labelledby="event-type">${event.type}</div>
@@ -910,13 +786,13 @@ export class StepEditor extends LitElement {
910
786
  <div id="event-title">title<span class="separator">:</span></div>
911
787
  <devtools-suggestion-input
912
788
  aria-labelledby="event-title"
913
- .disabled=${this.disabled}
789
+ .disabled=${input.disabled}
914
790
  .placeholder=${defaultValuesByAttribute.assertedEvents[0].title}
915
791
  .value=${live(event.title ?? '')}
916
- @blur=${this.#handleInputBlur({
792
+ @blur=${input.handleInputBlur({
917
793
  attribute: 'assertedEvents',
918
794
  from(value) {
919
- if (this.state.assertedEvents?.[index]?.title === undefined) {
795
+ if (input.state.assertedEvents?.[index]?.title === undefined || input.state.assertedEvents[index].title === value) {
920
796
  return;
921
797
  }
922
798
  return {
@@ -932,13 +808,13 @@ export class StepEditor extends LitElement {
932
808
  <div>url<span class="separator">:</span></div>
933
809
  <devtools-suggestion-input
934
810
  aria-labelledby="event-url"
935
- .disabled=${this.disabled}
811
+ .disabled=${input.disabled}
936
812
  .placeholder=${defaultValuesByAttribute.assertedEvents[0].url}
937
813
  .value=${live(event.url ?? '')}
938
- @blur=${this.#handleInputBlur({
814
+ @blur=${input.handleInputBlur({
939
815
  attribute: 'url',
940
816
  from(value) {
941
- if (this.state.assertedEvents?.[index]?.url === undefined) {
817
+ if (input.state.assertedEvents?.[index]?.url === undefined || input.state.assertedEvents[index].url === value) {
942
818
  return;
943
819
  }
944
820
  return {
@@ -955,29 +831,29 @@ export class StepEditor extends LitElement {
955
831
  // clang-format on
956
832
  }
957
833
 
958
- #renderAttributesRow(): Lit.TemplateResult|undefined {
959
- this.#renderedAttributes.add('attributes');
960
- if (this.state.attributes === undefined) {
834
+ function renderAttributesRow(): Lit.TemplateResult|undefined {
835
+ renderedAttributes.add('attributes');
836
+ if (input.state.attributes === undefined) {
961
837
  return;
962
838
  }
963
839
  // clang-format off
964
840
  return html`<div class="attribute" data-attribute="attributes" jslog=${VisualLogging.treeItem('attributes')}>
965
841
  <div class="row">
966
842
  <div>attributes<span class="separator">:</span></div>
967
- ${this.#renderDeleteButton('attributes')}
843
+ ${renderDeleteButton(input, 'attributes')}
968
844
  </div>
969
- ${this.state.attributes.map(({ name, value }, index, attributes) => {
845
+ ${input.state.attributes.map(({ name, value }, index, attributes) => {
970
846
  return html`<div class="padded row" jslog=${VisualLogging.treeItem('attribute')}>
971
847
  <devtools-suggestion-input
972
- .disabled=${this.disabled}
848
+ .disabled=${input.disabled}
973
849
  .placeholder=${defaultValuesByAttribute.attributes[0].name}
974
850
  .value=${live(name)}
975
851
  data-path=${`attributes.${index}.name`}
976
852
  jslog=${VisualLogging.key().track({change: true})}
977
- @blur=${this.#handleInputBlur({
853
+ @blur=${input.handleInputBlur({
978
854
  attribute: 'attributes',
979
855
  from(name) {
980
- if (this.state.attributes?.[index]?.name === undefined) {
856
+ if (input.state.attributes?.[index]?.name === undefined || input.state.attributes[index].name === name) {
981
857
  return;
982
858
  }
983
859
  return {
@@ -988,14 +864,14 @@ export class StepEditor extends LitElement {
988
864
  ></devtools-suggestion-input>
989
865
  <span class="separator">:</span>
990
866
  <devtools-suggestion-input
991
- .disabled=${this.disabled}
867
+ .disabled=${input.disabled}
992
868
  .placeholder=${defaultValuesByAttribute.attributes[0].value}
993
869
  .value=${live(value)}
994
870
  data-path=${`attributes.${index}.value`}
995
- @blur=${this.#handleInputBlur({
871
+ @blur=${input.handleInputBlur({
996
872
  attribute: 'attributes',
997
873
  from(value) {
998
- if (this.state.attributes?.[index]?.value === undefined) {
874
+ if (input.state.attributes?.[index]?.value === undefined || input.state.attributes[index].value === value) {
999
875
  return;
1000
876
  }
1001
877
  return {
@@ -1004,11 +880,11 @@ export class StepEditor extends LitElement {
1004
880
  },
1005
881
  })}
1006
882
  ></devtools-suggestion-input>
1007
- ${this.#renderInlineButton({
883
+ ${renderInlineButton(input, {
1008
884
  class: 'add-attribute-assertion',
1009
885
  title: i18nString(UIStrings.addSelectorPart),
1010
886
  iconName: 'plus',
1011
- onClick: this.#handleAddOrRemoveClick(
887
+ onClick: input.handleAddOrRemoveClick(
1012
888
  {
1013
889
  attributes: new ArrayAssignments({
1014
890
  [index + 1]: new InsertAssignment(
@@ -1036,11 +912,11 @@ export class StepEditor extends LitElement {
1036
912
  }.name"]`,
1037
913
  ),
1038
914
  })}
1039
- ${this.#renderInlineButton({
915
+ ${renderInlineButton(input, {
1040
916
  class: 'remove-attribute-assertion',
1041
917
  title: i18nString(UIStrings.removeSelectorPart),
1042
918
  iconName: 'minus',
1043
- onClick: this.#handleAddOrRemoveClick(
919
+ onClick: input.handleAddOrRemoveClick(
1044
920
  { attributes: new ArrayAssignments({ [index]: undefined }) },
1045
921
  `devtools-suggestion-input[data-path="attributes.${Math.min(
1046
922
  index,
@@ -1054,16 +930,16 @@ export class StepEditor extends LitElement {
1054
930
  // clang-format on
1055
931
  }
1056
932
 
1057
- #renderAddRowButtons(): Array<Lit.TemplateResult|undefined> {
1058
- const attributes = attributesByType[this.state.type];
1059
- return [...attributes.optional].filter(attr => this.state[attr] === undefined).map(attr => {
933
+ function renderAddRowButtons(): Array<Lit.TemplateResult|undefined> {
934
+ const attributes = attributesByType[input.state.type];
935
+ return [...attributes.optional].filter(attr => input.state[attr] === undefined).map(attr => {
1060
936
  // clang-format off
1061
937
  return html`<devtools-button
1062
938
  .variant=${Buttons.Button.Variant.OUTLINED}
1063
939
  class="add-row"
1064
940
  data-attribute=${attr}
1065
941
  jslog=${VisualLogging.action(`add-${Platform.StringUtilities.toKebabCase(attr)}`)}
1066
- @click=${this.#handleAddRowClickEvent}
942
+ @click=${input.handleAddRowClickEvent}
1067
943
  >
1068
944
  ${i18nString(UIStrings.addAttribute, {
1069
945
  attributeName: attr,
@@ -1073,63 +949,218 @@ export class StepEditor extends LitElement {
1073
949
  });
1074
950
  }
1075
951
 
952
+ // clang-format off
953
+ const result = html`
954
+ <style>${stepEditorStyles}</style>
955
+ <div class="wrapper" jslog=${VisualLogging.tree('step-editor')} >
956
+ ${renderTypeRow(input.isTypeEditable)} ${renderRow('target')}
957
+ ${renderFrameRow()} ${renderSelectorsRow()}
958
+ ${renderRow('deviceType')} ${renderRow('button')}
959
+ ${renderRow('url')} ${renderRow('x')}
960
+ ${renderRow('y')} ${renderRow('offsetX')}
961
+ ${renderRow('offsetY')} ${renderRow('value')}
962
+ ${renderRow('key')} ${renderRow('operator')}
963
+ ${renderRow('count')} ${renderRow('expression')}
964
+ ${renderRow('duration')} ${renderAssertedEvents()}
965
+ ${renderRow('timeout')} ${renderRow('width')}
966
+ ${renderRow('height')} ${renderRow('deviceScaleFactor')}
967
+ ${renderRow('isMobile')} ${renderRow('hasTouch')}
968
+ ${renderRow('isLandscape')} ${renderRow('download')}
969
+ ${renderRow('upload')} ${renderRow('latency')}
970
+ ${renderRow('name')} ${renderRow('parameters')}
971
+ ${renderRow('visible')} ${renderRow('properties')}
972
+ ${renderAttributesRow()}
973
+ ${input.error
974
+ ? html`
975
+ <div class="error">
976
+ ${i18nString(UIStrings.notSaved, {
977
+ error: input.error,
978
+ })}
979
+ </div>
980
+ `
981
+ : undefined}
982
+ ${!input.disabled
983
+ ? html`<div
984
+ class="row-buttons wrapped gap row regular-font no-margin"
985
+ >
986
+ ${renderAddRowButtons()}
987
+ </div>`
988
+ : undefined}
989
+ </div>
990
+ `;
991
+
992
+ // clang-format on
993
+ for (const key of Object.keys(dataTypeByAttribute)) {
994
+ if (!renderedAttributes.has(key as Attribute)) {
995
+ throw new Error(`The editable attribute ${key} does not have UI`);
996
+ }
997
+ }
998
+
999
+ render(result, target, {container: {listeners: {keydown: input.handleKeyDownEvent}}});
1000
+ };
1001
+
1002
+ export class StepEditor extends UI.Widget.Widget {
1003
+ #state: DeepImmutable<EditorState>;
1004
+ #error: string|undefined;
1005
+ #isTypeEditable = true;
1006
+ #disabled = false;
1007
+ #view: View;
1008
+
1009
+ constructor(element?: HTMLElement, view = DEFAULT_VIEW) {
1010
+ super(element, {useShadowDom: true});
1011
+ this.#state = {type: Models.Schema.StepType.WaitForElement};
1012
+ this.#view = view;
1013
+ }
1014
+
1015
+ set isTypeEditable(value: boolean) {
1016
+ this.#isTypeEditable = value;
1017
+ this.requestUpdate();
1018
+ }
1019
+
1020
+ set disabled(value: boolean) {
1021
+ this.#disabled = value;
1022
+ this.requestUpdate();
1023
+ }
1024
+
1025
+ set step(step: Models.Schema.Step) {
1026
+ this.#state = deepFreeze(EditorState.fromStep(step));
1027
+ this.#error = undefined;
1028
+ this.requestUpdate();
1029
+ }
1030
+
1031
+ override performUpdate(): void {
1032
+ const input: ViewInput = {
1033
+ state: this.#state,
1034
+ disabled: this.#disabled,
1035
+ error: this.#error,
1036
+ isTypeEditable: this.#isTypeEditable,
1037
+ handleInputBlur: this.#handleInputBlur,
1038
+ handleTypeInputBlur: this.#handleTypeInputBlur,
1039
+ handleAddRowClickEvent: this.#handleAddRowClickEvent,
1040
+ handleDeleteRowClick: this.#handleDeleteRowClick,
1041
+ handleSelectorPicked: this.#handleSelectorPicked,
1042
+ handleAttributeRequested: this.#handleAttributeRequested,
1043
+ handleAddOrRemoveClick: this.#handleAddOrRemoveClick,
1044
+ handleKeyDownEvent: this.#handleKeyDownEvent,
1045
+ };
1046
+ this.#view(input, undefined, this.contentElement);
1047
+ }
1048
+
1049
+ #commit(updatedState: DeepImmutable<EditorState>): void {
1050
+ try {
1051
+ this.element.dispatchEvent(new StepEditedEvent(EditorState.toStep(updatedState)));
1052
+ // Note we don't need to update this variable since it will come from up
1053
+ // the tree, but processing up the tree is asynchronous implying we cannot
1054
+ // reliably know when the state will come back down. Since we need to
1055
+ // focus the DOM elements that may be created as a result of this new
1056
+ // state, we set it here for waiting on the updateComplete promise later.
1057
+ this.#state = updatedState;
1058
+ } catch (error) {
1059
+ this.#error = error.message;
1060
+ }
1061
+ this.requestUpdate();
1062
+ }
1063
+
1064
+ #handleSelectorPicked =
1065
+ (data: Models.Schema.StepWithSelectors&Pick<Models.Schema.ClickAttributes, 'offsetX'|'offsetY'>): void => {
1066
+ this.#commit(immutableDeepAssign(this.#state, {
1067
+ target: data.target,
1068
+ frame: data.frame,
1069
+ selectors: data.selectors.map(selector => typeof selector === 'string' ? [selector] : selector),
1070
+ offsetX: data.offsetX,
1071
+ offsetY: data.offsetY,
1072
+ }));
1073
+ };
1074
+
1075
+ #handleAttributeRequested = (send: (attribute?: string) => void): void => {
1076
+ this.element.dispatchEvent(new RequestSelectorAttributeEvent(send));
1077
+ };
1078
+
1079
+ #handleAddOrRemoveClick = (assignments: DeepImmutable<DeepPartial<Assignments<EditorState>>>,
1080
+ query: string): ((event: Event) => void) => event => {
1081
+ event.preventDefault();
1082
+ event.stopPropagation();
1083
+
1084
+ this.#commit(immutableDeepAssign(this.#state, assignments));
1085
+
1086
+ this.#ensureFocus(query);
1087
+ };
1088
+
1089
+ #handleDeleteRowClick = (attribute: Attribute) => (event: Event): void => {
1090
+ event.preventDefault();
1091
+ event.stopPropagation();
1092
+ this.#commit(immutableDeepAssign(this.#state, {[attribute]: undefined}));
1093
+ };
1094
+
1076
1095
  #ensureFocus = (query: string): void => {
1077
1096
  void this.updateComplete.then(() => {
1078
- const node = this.renderRoot.querySelector<HTMLElement>(query);
1097
+ const node = this.contentElement.querySelector<HTMLElement>(query);
1079
1098
  node?.focus();
1080
1099
  });
1081
1100
  };
1082
1101
 
1083
- protected override render(): Lit.TemplateResult {
1084
- this.#renderedAttributes = new Set();
1102
+ #handleKeyDownEvent = (event: Event): void => {
1103
+ assert(event instanceof KeyboardEvent);
1104
+ if (event.target instanceof SuggestionInput.SuggestionInput.SuggestionInput && event.key === 'Enter') {
1105
+ event.preventDefault();
1106
+ event.stopPropagation();
1107
+ const elements = this.contentElement.querySelectorAll('devtools-suggestion-input');
1108
+ const element = [...elements].findIndex(value => value === event.target);
1109
+ if (element >= 0 && element + 1 < elements.length) {
1110
+ elements[element + 1].focus();
1111
+ } else {
1112
+ event.target.blur();
1113
+ }
1114
+ }
1115
+ };
1085
1116
 
1086
- // clang-format off
1087
- const result = html`
1088
- <style>${stepEditorStyles}</style>
1089
- <div class="wrapper" jslog=${VisualLogging.tree('step-editor')} >
1090
- ${this.#renderTypeRow(this.isTypeEditable)} ${this.#renderRow('target')}
1091
- ${this.#renderFrameRow()} ${this.#renderSelectorsRow()}
1092
- ${this.#renderRow('deviceType')} ${this.#renderRow('button')}
1093
- ${this.#renderRow('url')} ${this.#renderRow('x')}
1094
- ${this.#renderRow('y')} ${this.#renderRow('offsetX')}
1095
- ${this.#renderRow('offsetY')} ${this.#renderRow('value')}
1096
- ${this.#renderRow('key')} ${this.#renderRow('operator')}
1097
- ${this.#renderRow('count')} ${this.#renderRow('expression')}
1098
- ${this.#renderRow('duration')} ${this.#renderAssertedEvents()}
1099
- ${this.#renderRow('timeout')} ${this.#renderRow('width')}
1100
- ${this.#renderRow('height')} ${this.#renderRow('deviceScaleFactor')}
1101
- ${this.#renderRow('isMobile')} ${this.#renderRow('hasTouch')}
1102
- ${this.#renderRow('isLandscape')} ${this.#renderRow('download')}
1103
- ${this.#renderRow('upload')} ${this.#renderRow('latency')}
1104
- ${this.#renderRow('name')} ${this.#renderRow('parameters')}
1105
- ${this.#renderRow('visible')} ${this.#renderRow('properties')}
1106
- ${this.#renderAttributesRow()}
1107
- ${this.error
1108
- ? html`
1109
- <div class="error">
1110
- ${i18nString(UIStrings.notSaved, {
1111
- error: this.error,
1112
- })}
1113
- </div>
1114
- `
1115
- : undefined}
1116
- ${!this.disabled
1117
- ? html`<div
1118
- class="row-buttons wrapped gap row regular-font no-margin"
1119
- >
1120
- ${this.#renderAddRowButtons()}
1121
- </div>`
1122
- : undefined}
1123
- </div>
1124
- `;
1117
+ #handleInputBlur = <A extends Attribute>(opts: {
1118
+ attribute: A,
1119
+ // If there are not assignments, then we should ignore the event.
1120
+ from(this: StepEditor, value: DataType<A>): DeepImmutable<DeepPartial<Assignments<EditorState>>>|undefined,
1121
+ }): ((event: Event) => void) => event => {
1122
+ assert(event.target instanceof SuggestionInput.SuggestionInput.SuggestionInput);
1123
+ if (event.target.disabled) {
1124
+ return;
1125
+ }
1125
1126
 
1126
- // clang-format on
1127
- for (const key of Object.keys(dataTypeByAttribute)) {
1128
- if (!this.#renderedAttributes.has(key as Attribute)) {
1129
- throw new Error(`The editable attribute ${key} does not have UI`);
1130
- }
1127
+ const dataType = dataTypeByAttribute[opts.attribute];
1128
+ const value = typeConverters[dataType](event.target.value) as DataType<A>;
1129
+ const assignments = opts.from.bind(this)(value);
1130
+ if (!assignments) {
1131
+ return;
1131
1132
  }
1133
+ this.#commit(immutableDeepAssign(this.#state, assignments));
1134
+ };
1132
1135
 
1133
- return result;
1134
- }
1136
+ #handleTypeInputBlur = async(event: Event): Promise<void> => {
1137
+ assert(event.target instanceof SuggestionInput.SuggestionInput.SuggestionInput);
1138
+ if (event.target.disabled) {
1139
+ return;
1140
+ }
1141
+
1142
+ const value = event.target.value as Models.Schema.StepType;
1143
+ if (value === this.#state.type) {
1144
+ return;
1145
+ }
1146
+ if (!Object.values(Models.Schema.StepType).includes(value)) {
1147
+ this.#error = i18nString(UIStrings.unknownActionType);
1148
+ this.requestUpdate();
1149
+ return;
1150
+ }
1151
+ this.#commit(await EditorState.default(value));
1152
+ };
1153
+
1154
+ #handleAddRowClickEvent = async(event: MouseEvent): Promise<void> => {
1155
+ event.preventDefault();
1156
+ event.stopPropagation();
1157
+
1158
+ const attribute = (event.target as HTMLElement).dataset.attribute as Attribute;
1159
+
1160
+ this.#commit(immutableDeepAssign(this.#state, {
1161
+ [attribute]: await EditorState.defaultByAttribute(this.#state, attribute),
1162
+ }));
1163
+
1164
+ this.#ensureFocus(`[data-attribute=${attribute}].attribute devtools-suggestion-input`);
1165
+ };
1135
1166
  }