chrome-devtools-frontend 1.0.1721762 → 1.0.1726686

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 (273) hide show
  1. package/.agents/skills/review-ui-widgets/SKILL.md +7 -7
  2. package/.agents/skills/ui-eng-vision-local-lit-renderer/SKILL.md +12 -0
  3. package/.agents/skills/ui-eng-vision-widget-promoter/SKILL.md +3 -3
  4. package/.agents/skills/ui-widgets/SKILL.md +2 -1
  5. package/docs/cookbook/dependencies.md +57 -11
  6. package/docs/get_the_code.md +15 -0
  7. package/front_end/core/i18n/collect-ui-strings.js +58 -12
  8. package/front_end/core/i18n/i18nImpl.ts +1 -1
  9. package/front_end/core/root/Runtime.ts +1 -1
  10. package/front_end/core/sdk/ResourceTreeModel.ts +16 -0
  11. package/front_end/core/sdk/WebAuthnModel.ts +17 -10
  12. package/front_end/entrypoints/js_app/js_app.ts +2 -5
  13. package/front_end/entrypoints/node_app/node_app.ts +2 -3
  14. package/front_end/generated/InspectorBackendCommands.ts +3 -3
  15. package/front_end/generated/protocol.ts +17 -0
  16. package/front_end/models/ai_assistance/skills/lighthouse.md +2 -1
  17. package/front_end/models/ai_assistance/skills/network.md +1 -0
  18. package/front_end/models/ai_assistance/tools/ExecuteJavaScript.ts +26 -2
  19. package/front_end/models/ai_assistance/tools/GetLighthouseAudits.ts +1 -1
  20. package/front_end/models/ai_assistance/tools/ListNetworkRequests.ts +2 -7
  21. package/front_end/models/ai_assistance/tools/RunLighthouse.ts +2 -2
  22. package/front_end/models/comment_manager/CD4ABridge.ts +4 -0
  23. package/front_end/models/comment_manager/CommentManager.ts +0 -6
  24. package/front_end/models/comment_manager/CommentThread.ts +39 -19
  25. package/front_end/models/elements/StyleChanges.ts +66 -0
  26. package/front_end/models/trace/EventsSerializer.ts +2 -7
  27. package/front_end/models/trace/ModelImpl.ts +0 -2
  28. package/front_end/models/trace/handlers/FlowsHandler.ts +37 -29
  29. package/front_end/models/trace/handlers/NetworkRequestsHandler.ts +13 -6
  30. package/front_end/models/trace/helpers/SyntheticEvents.ts +10 -37
  31. package/front_end/models/trace/insights/RenderBlocking.ts +3 -1
  32. package/front_end/models/trace/lantern/simulation/Simulator.ts +4 -0
  33. package/front_end/models/trace/lantern/types/Lantern.ts +1 -0
  34. package/front_end/panels/accessibility/AccessibilityAnnouncementRecordingView.ts +92 -0
  35. package/front_end/panels/accessibility/SourceOrderView.ts +1 -1
  36. package/front_end/panels/accessibility/accessibilityAnnouncementRecordingListView.css +3 -1
  37. package/front_end/panels/accessibility/accessibilityAnnouncementRecordingView.css +12 -1
  38. package/front_end/panels/application/ApplicationPanelSidebar.ts +90 -0
  39. package/front_end/panels/application/StorageView.ts +348 -254
  40. package/front_end/panels/application/SubAppsView.ts +159 -0
  41. package/front_end/panels/application/application.ts +2 -0
  42. package/front_end/panels/application/preloading/PreloadingView.ts +360 -336
  43. package/front_end/panels/application/preloading/components/PreloadingDisabledInfobar.ts +3 -3
  44. package/front_end/panels/application/preloading/components/PreloadingGrid.ts +6 -3
  45. package/front_end/panels/application/preloading/components/RuleSetGrid.ts +17 -17
  46. package/front_end/panels/application/preloading/components/UsedPreloadingView.ts +2 -2
  47. package/front_end/panels/application/preloading/preloadingView.css +12 -4
  48. package/front_end/panels/application/preloading/preloadingViewDropDown.css +1 -1
  49. package/front_end/panels/application/storageView.css +16 -21
  50. package/front_end/panels/application/subAppsView.css +21 -0
  51. package/front_end/panels/browser_debugger/CategorizedBreakpointsSidebarPane.ts +2 -2
  52. package/front_end/panels/browser_debugger/browser_debugger-meta.ts +6 -8
  53. package/front_end/panels/comments/CommentsPane.ts +26 -9
  54. package/front_end/panels/comments/commentsPane.css +25 -6
  55. package/front_end/panels/common/CommentThreadWidget.ts +2 -2
  56. package/front_end/panels/common/CommentsOverlayWidget.ts +94 -35
  57. package/front_end/panels/common/commentThreadWidget.css +17 -0
  58. package/front_end/panels/common/commentsOverlay.css +5 -10
  59. package/front_end/panels/common/common-meta.ts +11 -1
  60. package/front_end/panels/elements/ClassesPaneWidget.ts +133 -89
  61. package/front_end/panels/elements/ColorSwatchPopoverIcon.ts +7 -33
  62. package/front_end/panels/elements/DOMTreeElementWidget.ts +3 -1
  63. package/front_end/panels/elements/DOMTreeWidget.ts +19 -3
  64. package/front_end/panels/elements/ElementStatePaneWidget.ts +11 -1
  65. package/front_end/panels/elements/PropertyRenderer.ts +3 -2
  66. package/front_end/panels/elements/StyleEditorWidget.ts +87 -39
  67. package/front_end/panels/elements/StylePropertiesSection.ts +32 -5
  68. package/front_end/panels/elements/StylePropertyTreeElement.ts +227 -162
  69. package/front_end/panels/elements/StylesSidebarPane.ts +1 -0
  70. package/front_end/panels/event_listeners/EventListenersView.ts +11 -8
  71. package/front_end/panels/layer_viewer/Layers3DView.ts +21 -37
  72. package/front_end/panels/linear_memory_inspector/components/ValueInterpreterSettings.ts +1 -1
  73. package/front_end/panels/network/NetworkConfigView.ts +12 -10
  74. package/front_end/panels/network/NetworkDataGridNode.ts +3 -1
  75. package/front_end/panels/network/RequestConditionsDrawer.ts +1 -1
  76. package/front_end/panels/network/RequestPayloadView.ts +12 -5
  77. package/front_end/panels/network/networkConfigView.css +8 -8
  78. package/front_end/panels/profiler/HeapProfileView.ts +28 -6
  79. package/front_end/panels/profiler/HeapSnapshotDataGrids.ts +7 -3
  80. package/front_end/panels/protocol_monitor/JSONEditor.ts +0 -1
  81. package/front_end/panels/recorder/RecorderPanel.ts +2 -2
  82. package/front_end/panels/screencast/ScreencastView.ts +3 -6
  83. package/front_end/panels/security/SecurityPanelSidebar.ts +0 -31
  84. package/front_end/panels/settings/SettingsScreen.ts +1 -1
  85. package/front_end/panels/settings/emulation/DevicesSettingsTab.ts +9 -16
  86. package/front_end/panels/settings/emulation/components/UserAgentClientHintsForm.ts +358 -345
  87. package/front_end/panels/settings/emulation/components/userAgentClientHintsForm.css +136 -121
  88. package/front_end/panels/sources/NavigatorView.ts +105 -45
  89. package/front_end/panels/sources/ScopeChainSidebarPane.ts +8 -5
  90. package/front_end/panels/sources/SourcesNavigator.ts +421 -213
  91. package/front_end/panels/sources/SourcesPanel.ts +9 -5
  92. package/front_end/panels/sources/TabbedEditorContainer.ts +41 -18
  93. package/front_end/panels/sources/UISourceCodeFrame.ts +1 -1
  94. package/front_end/panels/sources/WatchExpressionsSidebarPane.ts +10 -5
  95. package/front_end/panels/sources/sources-meta.ts +4 -15
  96. package/front_end/panels/sources/sourcesNavigator.css +5 -0
  97. package/front_end/panels/timeline/ModificationsManager.ts +0 -2
  98. package/front_end/panels/timeline/README.md +2 -0
  99. package/front_end/panels/timeline/TimelineCommentContext.ts +171 -0
  100. package/front_end/panels/timeline/TimelineCommentController.ts +168 -30
  101. package/front_end/panels/timeline/TimelinePanel.ts +4 -0
  102. package/front_end/panels/timeline/components/IgnoreListSetting.ts +1 -1
  103. package/front_end/panels/timeline/timeline.ts +2 -0
  104. package/front_end/panels/timeline/utils/Treemap.ts +8 -0
  105. package/front_end/panels/webauthn/WebauthnPane.ts +15 -13
  106. package/front_end/third_party/chromium/README.chromium +1 -1
  107. package/front_end/third_party/puppeteer/README.chromium +2 -2
  108. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.d.ts +113 -3
  109. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +21311 -20981
  110. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Browser.d.ts +36 -0
  111. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Browser.d.ts.map +1 -1
  112. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Browser.js +22 -0
  113. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Browser.js.map +1 -1
  114. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/BrowserContext.d.ts.map +1 -1
  115. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/BrowserContext.js +20 -18
  116. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/BrowserContext.js.map +1 -1
  117. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/HTTPRequest.d.ts +20 -1
  118. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/HTTPRequest.d.ts.map +1 -1
  119. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/HTTPRequest.js +2 -2
  120. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/HTTPRequest.js.map +1 -1
  121. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Page.d.ts +40 -1
  122. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Page.d.ts.map +1 -1
  123. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Page.js +3 -1
  124. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Page.js.map +1 -1
  125. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/locators/locators.d.ts +2 -0
  126. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/locators/locators.d.ts.map +1 -1
  127. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/locators/locators.js +41 -5
  128. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/locators/locators.js.map +1 -1
  129. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/HTTPRequest.d.ts +2 -1
  130. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/HTTPRequest.d.ts.map +1 -1
  131. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/HTTPRequest.js +23 -2
  132. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/HTTPRequest.js.map +1 -1
  133. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/Input.js +1 -1
  134. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/Page.d.ts +3 -2
  135. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/Page.d.ts.map +1 -1
  136. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/Page.js +13 -1
  137. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/Page.js.map +1 -1
  138. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/core/Request.d.ts +1 -0
  139. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/core/Request.d.ts.map +1 -1
  140. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/core/Request.js +18 -11
  141. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/core/Request.js.map +1 -1
  142. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Accessibility.d.ts +2 -1
  143. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Accessibility.d.ts.map +1 -1
  144. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Accessibility.js +8 -1
  145. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Accessibility.js.map +1 -1
  146. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Browser.d.ts +2 -1
  147. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Browser.d.ts.map +1 -1
  148. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Browser.js +24 -12
  149. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Browser.js.map +1 -1
  150. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/EmulationManager.d.ts +1 -1
  151. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/EmulationManager.d.ts.map +1 -1
  152. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/EmulationManager.js +13 -7
  153. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/EmulationManager.js.map +1 -1
  154. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Frame.d.ts.map +1 -1
  155. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Frame.js +5 -1
  156. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Frame.js.map +1 -1
  157. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/HTTPRequest.d.ts +22 -1
  158. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/HTTPRequest.d.ts.map +1 -1
  159. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/HTTPRequest.js +75 -9
  160. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/HTTPRequest.js.map +1 -1
  161. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/NetworkEventManager.d.ts +18 -4
  162. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/NetworkEventManager.d.ts.map +1 -1
  163. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/NetworkEventManager.js +8 -4
  164. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/NetworkEventManager.js.map +1 -1
  165. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/NetworkManager.d.ts.map +1 -1
  166. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/NetworkManager.js +65 -26
  167. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/NetworkManager.js.map +1 -1
  168. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Page.d.ts +3 -2
  169. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Page.d.ts.map +1 -1
  170. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Page.js +19 -0
  171. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Page.js.map +1 -1
  172. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/ScreenRecording.d.ts.map +1 -1
  173. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/ScreenRecording.js +4 -3
  174. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/ScreenRecording.js.map +1 -1
  175. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Target.d.ts +1 -0
  176. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Target.d.ts.map +1 -1
  177. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Target.js +4 -0
  178. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Target.js.map +1 -1
  179. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/TargetManager.d.ts +2 -0
  180. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/TargetManager.d.ts.map +1 -1
  181. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/TargetManager.js +95 -8
  182. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/TargetManager.js.map +1 -1
  183. package/front_end/third_party/puppeteer/package/lib/puppeteer/common/BrowserConnector.d.ts +0 -8
  184. package/front_end/third_party/puppeteer/package/lib/puppeteer/common/BrowserConnector.d.ts.map +1 -1
  185. package/front_end/third_party/puppeteer/package/lib/puppeteer/common/BrowserConnector.js +8 -24
  186. package/front_end/third_party/puppeteer/package/lib/puppeteer/common/BrowserConnector.js.map +1 -1
  187. package/front_end/third_party/puppeteer/package/lib/puppeteer/common/BrowserWebSocketTransport.d.ts +1 -0
  188. package/front_end/third_party/puppeteer/package/lib/puppeteer/common/BrowserWebSocketTransport.d.ts.map +1 -1
  189. package/front_end/third_party/puppeteer/package/lib/puppeteer/common/BrowserWebSocketTransport.js +5 -4
  190. package/front_end/third_party/puppeteer/package/lib/puppeteer/common/BrowserWebSocketTransport.js.map +1 -1
  191. package/front_end/third_party/puppeteer/package/lib/puppeteer/common/ConnectOptions.d.ts +9 -0
  192. package/front_end/third_party/puppeteer/package/lib/puppeteer/common/ConnectOptions.d.ts.map +1 -1
  193. package/front_end/third_party/puppeteer/package/lib/puppeteer/common/WaitTask.d.ts.map +1 -1
  194. package/front_end/third_party/puppeteer/package/lib/puppeteer/common/WaitTask.js +10 -0
  195. package/front_end/third_party/puppeteer/package/lib/puppeteer/common/WaitTask.js.map +1 -1
  196. package/front_end/third_party/puppeteer/package/lib/puppeteer/common/util.d.ts +10 -0
  197. package/front_end/third_party/puppeteer/package/lib/puppeteer/common/util.d.ts.map +1 -1
  198. package/front_end/third_party/puppeteer/package/lib/puppeteer/common/util.js +30 -5
  199. package/front_end/third_party/puppeteer/package/lib/puppeteer/common/util.js.map +1 -1
  200. package/front_end/third_party/puppeteer/package/lib/puppeteer/generated/injected.d.ts +1 -1
  201. package/front_end/third_party/puppeteer/package/lib/puppeteer/generated/injected.d.ts.map +1 -1
  202. package/front_end/third_party/puppeteer/package/lib/puppeteer/generated/injected.js +1 -1
  203. package/front_end/third_party/puppeteer/package/lib/puppeteer/generated/injected.js.map +1 -1
  204. package/front_end/third_party/puppeteer/package/lib/puppeteer/injected/Poller.d.ts +2 -0
  205. package/front_end/third_party/puppeteer/package/lib/puppeteer/injected/Poller.d.ts.map +1 -1
  206. package/front_end/third_party/puppeteer/package/lib/puppeteer/injected/Poller.js +33 -3
  207. package/front_end/third_party/puppeteer/package/lib/puppeteer/injected/Poller.js.map +1 -1
  208. package/front_end/third_party/puppeteer/package/lib/puppeteer/node/BrowserLauncher.d.ts.map +1 -1
  209. package/front_end/third_party/puppeteer/package/lib/puppeteer/node/BrowserLauncher.js +1 -2
  210. package/front_end/third_party/puppeteer/package/lib/puppeteer/node/BrowserLauncher.js.map +1 -1
  211. package/front_end/third_party/puppeteer/package/lib/puppeteer/node/NodeWebSocketTransport.d.ts +4 -0
  212. package/front_end/third_party/puppeteer/package/lib/puppeteer/node/NodeWebSocketTransport.d.ts.map +1 -1
  213. package/front_end/third_party/puppeteer/package/lib/puppeteer/node/NodeWebSocketTransport.js +1 -0
  214. package/front_end/third_party/puppeteer/package/lib/puppeteer/node/NodeWebSocketTransport.js.map +1 -1
  215. package/front_end/third_party/puppeteer/package/lib/puppeteer/revisions.d.ts +3 -3
  216. package/front_end/third_party/puppeteer/package/lib/puppeteer/revisions.js +3 -3
  217. package/front_end/third_party/puppeteer/package/lib/puppeteer/revisions.js.map +1 -1
  218. package/front_end/third_party/puppeteer/package/lib/puppeteer/util/encoding.d.ts +2 -2
  219. package/front_end/third_party/puppeteer/package/lib/puppeteer/util/encoding.d.ts.map +1 -1
  220. package/front_end/third_party/puppeteer/package/lib/puppeteer/util/version.d.ts +1 -1
  221. package/front_end/third_party/puppeteer/package/lib/puppeteer/util/version.js +1 -1
  222. package/front_end/third_party/puppeteer/package/lib/types.d.ts +113 -3
  223. package/front_end/third_party/puppeteer/package/package.json +4 -4
  224. package/front_end/third_party/puppeteer/package/src/api/Browser.ts +42 -0
  225. package/front_end/third_party/puppeteer/package/src/api/BrowserContext.ts +25 -22
  226. package/front_end/third_party/puppeteer/package/src/api/HTTPRequest.ts +22 -2
  227. package/front_end/third_party/puppeteer/package/src/api/Page.ts +45 -2
  228. package/front_end/third_party/puppeteer/package/src/api/locators/locators.ts +79 -32
  229. package/front_end/third_party/puppeteer/package/src/bidi/HTTPRequest.ts +32 -12
  230. package/front_end/third_party/puppeteer/package/src/bidi/Input.ts +1 -1
  231. package/front_end/third_party/puppeteer/package/src/bidi/Page.ts +18 -2
  232. package/front_end/third_party/puppeteer/package/src/bidi/core/Request.ts +21 -13
  233. package/front_end/third_party/puppeteer/package/src/cdp/Accessibility.ts +12 -2
  234. package/front_end/third_party/puppeteer/package/src/cdp/Browser.ts +37 -16
  235. package/front_end/third_party/puppeteer/package/src/cdp/EmulationManager.ts +14 -11
  236. package/front_end/third_party/puppeteer/package/src/cdp/Frame.ts +7 -1
  237. package/front_end/third_party/puppeteer/package/src/cdp/HTTPRequest.ts +101 -14
  238. package/front_end/third_party/puppeteer/package/src/cdp/NetworkEventManager.ts +26 -6
  239. package/front_end/third_party/puppeteer/package/src/cdp/NetworkManager.ts +111 -29
  240. package/front_end/third_party/puppeteer/package/src/cdp/Page.ts +37 -1
  241. package/front_end/third_party/puppeteer/package/src/cdp/ScreenRecording.ts +5 -4
  242. package/front_end/third_party/puppeteer/package/src/cdp/Target.ts +5 -0
  243. package/front_end/third_party/puppeteer/package/src/cdp/TargetManager.ts +122 -7
  244. package/front_end/third_party/puppeteer/package/src/common/BrowserConnector.ts +13 -33
  245. package/front_end/third_party/puppeteer/package/src/common/BrowserWebSocketTransport.ts +10 -5
  246. package/front_end/third_party/puppeteer/package/src/common/ConnectOptions.ts +10 -0
  247. package/front_end/third_party/puppeteer/package/src/common/WaitTask.ts +12 -0
  248. package/front_end/third_party/puppeteer/package/src/common/util.ts +39 -5
  249. package/front_end/third_party/puppeteer/package/src/generated/injected.ts +1 -1
  250. package/front_end/third_party/puppeteer/package/src/injected/Poller.ts +35 -3
  251. package/front_end/third_party/puppeteer/package/src/node/BrowserLauncher.ts +4 -2
  252. package/front_end/third_party/puppeteer/package/src/node/NodeWebSocketTransport.ts +5 -0
  253. package/front_end/third_party/puppeteer/package/src/revisions.ts +3 -3
  254. package/front_end/third_party/puppeteer/package/src/util/encoding.ts +2 -2
  255. package/front_end/third_party/puppeteer/package/src/util/version.ts +1 -1
  256. package/front_end/ui/comments/CommentAnchorResolver.ts +3 -0
  257. package/front_end/ui/comments/CommentOverlayManager.ts +2 -4
  258. package/front_end/ui/components/lists/List.ts +14 -1
  259. package/front_end/ui/components/panel_feedback/PreviewToggle.ts +1 -1
  260. package/front_end/ui/components/text_editor/AiCodeCompletionProvider.ts +23 -14
  261. package/front_end/ui/components/text_editor/AiCodeGenerationProvider.ts +3 -0
  262. package/front_end/ui/helpers/OpenInNewTab.ts +8 -0
  263. package/front_end/ui/legacy/LinkContextMenuProvider.ts +1 -1
  264. package/front_end/ui/legacy/ListWidget.ts +14 -18
  265. package/front_end/ui/legacy/TabbedPane.ts +46 -3
  266. package/front_end/ui/legacy/Toolbar.ts +0 -1
  267. package/front_end/ui/legacy/Treeoutline.ts +10 -3
  268. package/front_end/ui/legacy/UIUtils.ts +5 -2
  269. package/front_end/ui/legacy/components/inline_editor/Swatches.ts +19 -6
  270. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +20 -11
  271. package/front_end/ui/legacy/components/source_frame/XMLView.ts +27 -7
  272. package/front_end/ui/visual_logging/KnownContextValues.ts +5 -0
  273. package/package.json +1 -1
@@ -1,24 +1,23 @@
1
1
  // Copyright 2021 The Chromium Authors
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
- /* eslint-disable @devtools/no-lit-render-outside-of-view*/
5
4
 
6
5
  import '../../../../ui/kit/kit.js';
7
- import '../../../../ui/legacy/legacy.js';
8
6
 
7
+ import * as Common from '../../../../core/common/common.js';
9
8
  import * as i18n from '../../../../core/i18n/i18n.js';
10
9
  import * as Platform from '../../../../core/platform/platform.js';
11
10
  import type * as Protocol from '../../../../generated/protocol.js';
12
11
  import * as Buttons from '../../../../ui/components/buttons/buttons.js';
13
12
  import * as Input from '../../../../ui/components/input/input.js';
14
- import type * as UI from '../../../../ui/legacy/legacy.js';
13
+ import * as UI from '../../../../ui/legacy/legacy.js';
15
14
  import * as Lit from '../../../../ui/lit/lit.js';
16
15
  import * as VisualLogging from '../../../../ui/visual_logging/visual_logging.js';
17
16
  import * as EmulationUtils from '../utils/utils.js';
18
17
 
19
18
  import userAgentClientHintsFormStyles from './userAgentClientHintsForm.css.js';
20
19
 
21
- const {html} = Lit;
20
+ const {html, Directives: {ref}} = Lit;
22
21
 
23
22
  const UIStrings = {
24
23
  /**
@@ -194,20 +193,20 @@ const UIStrings = {
194
193
  const str_ = i18n.i18n.registerUIStrings('panels/settings/emulation/components/UserAgentClientHintsForm.ts', UIStrings);
195
194
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
196
195
 
197
- export class ClientHintsChangeEvent extends Event {
198
- static readonly eventName = 'clienthintschange';
199
- constructor() {
200
- super(ClientHintsChangeEvent.eventName);
201
- }
196
+ export const enum Events {
197
+ CLIENT_HINTS_CHANGE = 'clienthintschange',
198
+ CLIENT_HINTS_SUBMIT = 'clienthintssubmit',
202
199
  }
203
200
 
204
- export class ClientHintsSubmitEvent extends Event {
205
- static readonly eventName = 'clienthintssubmit';
206
- detail: {value: Protocol.Emulation.UserAgentMetadata};
201
+ export interface EventTypes {
202
+ [Events.CLIENT_HINTS_CHANGE]: Protocol.Emulation.UserAgentMetadata;
203
+ [Events.CLIENT_HINTS_SUBMIT]: Protocol.Emulation.UserAgentMetadata;
204
+ }
207
205
 
208
- constructor(value: Protocol.Emulation.UserAgentMetadata) {
209
- super(ClientHintsSubmitEvent.eventName);
210
- this.detail = {value};
206
+ declare global {
207
+ interface HTMLElementEventMap {
208
+ [Events.CLIENT_HINTS_CHANGE]: CustomEvent<Protocol.Emulation.UserAgentMetadata>;
209
+ [Events.CLIENT_HINTS_SUBMIT]: CustomEvent<Protocol.Emulation.UserAgentMetadata>;
211
210
  }
212
211
  }
213
212
 
@@ -249,12 +248,72 @@ export const ALL_PROTOCOL_FORM_FACTORS: string[] = [
249
248
  UIStrings.formFactorWatch,
250
249
  ] as const;
251
250
 
251
+ type BrandList = 'brands'|'fullVersionList';
252
+ type BrandInputType = 'brandName'|'brandVersion';
253
+ type TextField = 'fullVersion'|'platform'|'platformVersion'|'architecture'|'model';
254
+
255
+ export interface BrandRow {
256
+ brand: string;
257
+ version: string;
258
+ }
259
+
260
+ const BRAND_INPUT_ID_PREFIXES: Record<BrandList, string> = {
261
+ brands: 'ua',
262
+ fullVersionList: 'fvl',
263
+ };
264
+
265
+ const ADD_BRAND_BUTTON_IDS: Record<BrandList, string> = {
266
+ brands: 'add-brand-button',
267
+ fullVersionList: 'add-fvl-brand-button',
268
+ };
269
+
270
+ export interface ViewInput {
271
+ brands: BrandRow[];
272
+ fullVersionList: BrandRow[];
273
+ fullVersion: string;
274
+ formFactors: string[];
275
+ platform: string;
276
+ platformVersion: string;
277
+ architecture: string;
278
+ model: string;
279
+ mobile: boolean;
280
+ isFormOpened: boolean;
281
+ isFormDisabled: boolean;
282
+ showMobileCheckbox: boolean;
283
+ showSubmitButton: boolean;
284
+ useragentModifiedAriaMessage: string;
285
+ onTreeToggle: (key?: string) => void;
286
+ onBrandAdd: (list: BrandList) => void;
287
+ onBrandDelete: (list: BrandList, index: number) => void;
288
+ onBrandInputChange: (list: BrandList, index: number, brandInputType: BrandInputType, value: string) => void;
289
+ onTextFieldChange: (field: TextField, value: string) => void;
290
+ onMobileChange: (mobile: boolean) => void;
291
+ onFormFactorChange: (formFactor: string, isChecked: boolean) => void;
292
+ onSubmit: () => void;
293
+ }
294
+
295
+ export interface ViewOutput {
296
+ focusBrandInput?: (list: BrandList, index: number) => void;
297
+ focusAddBrandButton?: (list: BrandList) => void;
298
+ }
299
+
300
+ type View = typeof DEFAULT_VIEW;
301
+
302
+ const UserAgentClientHintsFormBase: Common.ObjectWrapper.EventMixin<EventTypes, typeof UI.Widget.Widget> =
303
+ Common.ObjectWrapper.eventMixin(UI.Widget.Widget);
304
+
252
305
  /**
253
306
  * Component for user agent client hints form, it is used in device settings panel
254
307
  * and network conditions panel. It is customizable through showMobileCheckbox and showSubmitButton.
308
+ *
309
+ * The form dispatches {@link Events.CLIENT_HINTS_CHANGE} when the user changes
310
+ * a value, and {@link Events.CLIENT_HINTS_SUBMIT} when the user submits the
311
+ * form. The widget element also dispatches each event as a DOM `CustomEvent`
312
+ * with the metadata in `detail`.
255
313
  */
256
- export class UserAgentClientHintsForm extends HTMLElement {
257
- readonly #shadow = this.attachShadow({mode: 'open'});
314
+ export class UserAgentClientHintsForm extends UserAgentClientHintsFormBase {
315
+ readonly #view: View;
316
+ readonly #viewOutput: ViewOutput = {};
258
317
 
259
318
  #isFormOpened = false;
260
319
  #isFormDisabled = false;
@@ -263,6 +322,11 @@ export class UserAgentClientHintsForm extends HTMLElement {
263
322
  #showSubmitButton = false;
264
323
  #useragentModifiedAriaMessage = '';
265
324
 
325
+ constructor(element?: HTMLElement, view: View = DEFAULT_VIEW) {
326
+ super(element);
327
+ this.#view = view;
328
+ }
329
+
266
330
  set value(data: UserAgentClientHintsFormData) {
267
331
  const {metaData = DEFAULT_METADATA, showMobileCheckbox = false, showSubmitButton = false} = data;
268
332
  this.#metaData = {
@@ -271,7 +335,7 @@ export class UserAgentClientHintsForm extends HTMLElement {
271
335
  };
272
336
  this.#showMobileCheckbox = showMobileCheckbox;
273
337
  this.#showSubmitButton = showSubmitButton;
274
- this.#render();
338
+ this.requestUpdate();
275
339
  }
276
340
 
277
341
  get value(): UserAgentClientHintsFormData {
@@ -281,23 +345,26 @@ export class UserAgentClientHintsForm extends HTMLElement {
281
345
  }
282
346
 
283
347
  set disabled(disableForm: boolean) {
348
+ if (this.#isFormDisabled === disableForm) {
349
+ return;
350
+ }
284
351
  this.#isFormDisabled = disableForm;
285
- this.#isFormOpened = false;
286
- this.#render();
352
+ if (disableForm) {
353
+ this.#isFormOpened = false;
354
+ }
355
+ this.requestUpdate();
287
356
  }
288
357
 
289
358
  get disabled(): boolean {
290
359
  return this.#isFormDisabled;
291
360
  }
292
361
 
293
- #handleTreeExpand = (event: KeyboardEvent): void => {
294
- if (event.code === 'Space' || event.code === 'Enter' || event.code === 'ArrowLeft' || event.code === 'ArrowRight') {
295
- event.consume(true);
296
- this.#handleTreeClick(event.code);
297
- }
298
- };
362
+ override wasShown(): void {
363
+ super.wasShown();
364
+ this.requestUpdate();
365
+ }
299
366
 
300
- #handleTreeClick = (key: string): void => {
367
+ #handleTreeClick = (key?: string): void => {
301
368
  if (this.#isFormDisabled) {
302
369
  return;
303
370
  }
@@ -305,11 +372,11 @@ export class UserAgentClientHintsForm extends HTMLElement {
305
372
  return;
306
373
  }
307
374
  this.#isFormOpened = !this.#isFormOpened;
308
- this.#render();
375
+ this.requestUpdate();
309
376
  };
310
377
 
311
- #handleUseragentInputChange = (value: string, index: number, brandInputType: 'brandName'|'brandVersion'): void => {
312
- const updatedUseragent = this.#metaData.brands?.map((browserBrand, brandIndex) => {
378
+ #handleBrandInputChange = (list: BrandList, index: number, brandInputType: BrandInputType, value: string): void => {
379
+ const updatedBrands = this.#metaData[list]?.map((browserBrand, brandIndex) => {
313
380
  if (brandIndex === index) {
314
381
  const {brand, version} = browserBrand;
315
382
  if (brandInputType === 'brandName') {
@@ -327,138 +394,51 @@ export class UserAgentClientHintsForm extends HTMLElement {
327
394
  });
328
395
  this.#metaData = {
329
396
  ...this.#metaData,
330
- brands: updatedUseragent,
331
- };
332
- this.dispatchEvent(new ClientHintsChangeEvent());
333
- this.#render();
334
- };
335
-
336
- #handleFullVersionListInputChange =
337
- (value: string, index: number, brandInputType: 'brandName'|'brandVersion'): void => {
338
- const fullVersionList = this.#metaData.fullVersionList?.map((browserBrand, brandIndex) => {
339
- if (brandIndex === index) {
340
- const {brand, version} = browserBrand;
341
- if (brandInputType === 'brandName') {
342
- return {
343
- brand: value,
344
- version,
345
- };
346
- }
347
- return {
348
- brand,
349
- version: value,
350
- };
351
- }
352
- return browserBrand;
353
- });
354
- this.#metaData = {
355
- ...this.#metaData,
356
- fullVersionList,
357
- };
358
- this.dispatchEvent(new ClientHintsChangeEvent());
359
- this.#render();
360
- };
361
-
362
- #handleUseragentDelete = (index: number): void => {
363
- const {brands = []} = this.#metaData;
364
- brands.splice(index, 1);
365
- this.#metaData = {
366
- ...this.#metaData,
367
- brands,
397
+ [list]: updatedBrands,
368
398
  };
369
- this.dispatchEvent(new ClientHintsChangeEvent());
370
- this.#useragentModifiedAriaMessage = i18nString(UIStrings.deletedBrand);
371
- this.#render();
372
-
373
- // after deleting a brand row, focus on next Brand input if available,
374
- // otherwise focus on the "Add Brand" button
375
- let nextFocusElement = this.shadowRoot?.getElementById(`ua-brand-${index + 1}-input`);
376
- if (!nextFocusElement) {
377
- nextFocusElement = this.shadowRoot?.getElementById('add-brand-button');
378
- }
379
- (nextFocusElement as HTMLElement)?.focus();
399
+ this.dispatchEventToListeners(Events.CLIENT_HINTS_CHANGE, this.#metaData);
400
+ this.requestUpdate();
380
401
  };
381
402
 
382
- #handleFullVersionListDelete = (index: number): void => {
383
- const {fullVersionList = []} = this.#metaData;
384
- fullVersionList.splice(index, 1);
403
+ #handleBrandDelete = async(list: BrandList, index: number): Promise<void> => {
404
+ const {[list]: brands = []} = this.#metaData;
405
+ const updatedBrands = brands.filter((_, brandIndex) => brandIndex !== index);
385
406
  this.#metaData = {
386
407
  ...this.#metaData,
387
- fullVersionList,
408
+ [list]: updatedBrands,
388
409
  };
389
- this.dispatchEvent(new ClientHintsChangeEvent());
410
+ this.dispatchEventToListeners(Events.CLIENT_HINTS_CHANGE, this.#metaData);
390
411
  this.#useragentModifiedAriaMessage = i18nString(UIStrings.deletedBrand);
391
- this.#render();
412
+ this.requestUpdate();
413
+ await this.updateComplete;
392
414
 
393
415
  // after deleting a brand row, focus on next Brand input if available,
394
416
  // otherwise focus on the "Add Brand" button
395
- let nextFocusElement = this.shadowRoot?.getElementById(`fvl-brand-${index + 1}-input`);
396
- if (!nextFocusElement) {
397
- nextFocusElement = this.shadowRoot?.getElementById('add-fvl-brand-button');
398
- }
399
- (nextFocusElement as HTMLElement)?.focus();
400
- };
401
-
402
- #handleAddUseragentBrandClick = (): void => {
403
- const {brands} = this.#metaData;
404
- this.#metaData = {
405
- ...this.#metaData,
406
- brands: [
407
- ...(Array.isArray(brands) ? brands : []),
408
- {
409
- brand: '',
410
- version: '',
411
- },
412
- ],
413
- };
414
- this.dispatchEvent(new ClientHintsChangeEvent());
415
- this.#useragentModifiedAriaMessage = i18nString(UIStrings.addedBrand);
416
- this.#render();
417
- const brandInputElements = this.shadowRoot?.querySelectorAll('.ua-brand-name-input');
418
- if (brandInputElements) {
419
- const lastBrandInputElement = Array.from(brandInputElements).pop();
420
- if (lastBrandInputElement) {
421
- (lastBrandInputElement as HTMLInputElement).focus();
422
- }
423
- }
424
- };
425
-
426
- #handleAddUseragentBrandKeyPress = (event: KeyboardEvent): void => {
427
- if (event.code === 'Space' || event.code === 'Enter') {
428
- event.preventDefault();
429
- this.#handleAddUseragentBrandClick();
417
+ if (index < updatedBrands.length) {
418
+ this.#viewOutput.focusBrandInput?.(list, index);
419
+ } else {
420
+ this.#viewOutput.focusAddBrandButton?.(list);
430
421
  }
431
422
  };
432
423
 
433
- #handleAddFullVersionListBrandClick = (): void => {
434
- const {fullVersionList} = this.#metaData;
424
+ #handleBrandAdd = async(list: BrandList): Promise<void> => {
425
+ const {[list]: brands} = this.#metaData;
426
+ const updatedBrands = [
427
+ ...(Array.isArray(brands) ? brands : []),
428
+ {
429
+ brand: '',
430
+ version: '',
431
+ },
432
+ ];
435
433
  this.#metaData = {
436
434
  ...this.#metaData,
437
- fullVersionList: [
438
- ...(Array.isArray(fullVersionList) ? fullVersionList : []),
439
- {
440
- brand: '',
441
- version: '',
442
- },
443
- ],
435
+ [list]: updatedBrands,
444
436
  };
445
- this.dispatchEvent(new ClientHintsChangeEvent());
437
+ this.dispatchEventToListeners(Events.CLIENT_HINTS_CHANGE, this.#metaData);
446
438
  this.#useragentModifiedAriaMessage = i18nString(UIStrings.addedBrand);
447
- this.#render();
448
- const brandInputElements = this.shadowRoot?.querySelectorAll('.fvl-brand-name-input');
449
- if (brandInputElements) {
450
- const lastBrandInputElement = Array.from(brandInputElements).pop();
451
- if (lastBrandInputElement) {
452
- (lastBrandInputElement as HTMLInputElement).focus();
453
- }
454
- }
455
- };
456
-
457
- #handleAddFullVersionListBrandKeyPress = (event: KeyboardEvent): void => {
458
- if (event.code === 'Space' || event.code === 'Enter') {
459
- event.preventDefault();
460
- this.#handleAddFullVersionListBrandClick();
461
- }
439
+ this.requestUpdate();
440
+ await this.updateComplete;
441
+ this.#viewOutput.focusBrandInput?.(list, updatedBrands.length - 1);
462
442
  };
463
443
 
464
444
  #handleFormFactorCheckboxChange = (formFactorValue: string, isChecked: boolean): void => {
@@ -474,8 +454,8 @@ export class UserAgentClientHintsForm extends HTMLElement {
474
454
  ...this.#metaData,
475
455
  formFactors: currentFormFactors,
476
456
  };
477
- this.dispatchEvent(new ClientHintsChangeEvent());
478
- this.#render();
457
+ this.dispatchEventToListeners(Events.CLIENT_HINTS_CHANGE, this.#metaData);
458
+ this.requestUpdate();
479
459
  };
480
460
 
481
461
  #handleInputChange = (stateKey: keyof Protocol.Emulation.UserAgentMetadata, value: string|boolean): void => {
@@ -484,25 +464,97 @@ export class UserAgentClientHintsForm extends HTMLElement {
484
464
  ...this.#metaData,
485
465
  [stateKey]: value,
486
466
  };
487
- this.#render();
467
+ this.requestUpdate();
488
468
  }
489
- this.dispatchEvent(new ClientHintsChangeEvent());
469
+ this.dispatchEventToListeners(Events.CLIENT_HINTS_CHANGE, this.#metaData);
490
470
  };
491
471
 
492
- #handleSubmit = (event: Event): void => {
493
- event.preventDefault();
472
+ #handleSubmit = (): void => {
494
473
  if (this.#showSubmitButton) {
495
- this.dispatchEvent(new ClientHintsSubmitEvent(this.#metaData));
496
- this.#render();
474
+ this.dispatchEventToListeners(Events.CLIENT_HINTS_SUBMIT, this.#metaData);
475
+ this.requestUpdate();
476
+ }
477
+ };
478
+
479
+ validate = (): UI.ListWidget.ValidatorResult => {
480
+ for (const [metaDataKey, metaDataValue] of Object.entries(this.#metaData)) {
481
+ if (metaDataKey === 'brands' || metaDataKey === 'fullVersionList') {
482
+ // for structured fields, check each individual brand/version
483
+ const brandList = metaDataValue as Protocol.Emulation.UserAgentBrandVersion[] | undefined;
484
+ const isBrandValid = (brandList ?? []).every(({brand, version}) => {
485
+ const brandNameResult = EmulationUtils.UserAgentMetadata.validateAsStructuredHeadersString(
486
+ brand, i18nString(UIStrings.notRepresentable));
487
+ const brandVersionResult = EmulationUtils.UserAgentMetadata.validateAsStructuredHeadersString(
488
+ version, i18nString(UIStrings.notRepresentable));
489
+ return brandNameResult.valid && brandVersionResult.valid;
490
+ });
491
+ if (!isBrandValid) {
492
+ return {valid: false, errorMessage: i18nString(UIStrings.notRepresentable)};
493
+ }
494
+ } else if (metaDataKey === 'formFactors') {
495
+ const formFactors = metaDataValue as string[] | undefined;
496
+ if (formFactors) {
497
+ for (const ff of formFactors) {
498
+ if (!ALL_PROTOCOL_FORM_FACTORS.includes(ff)) {
499
+ return {
500
+ valid: false,
501
+ errorMessage: i18nString(UIStrings.notRepresentable) + ` (Invalid form factor: ${ff})`,
502
+ };
503
+ }
504
+ const ffError = EmulationUtils.UserAgentMetadata.validateAsStructuredHeadersString(
505
+ ff, i18nString(UIStrings.notRepresentable));
506
+ if (!ffError.valid) {
507
+ return ffError;
508
+ }
509
+ }
510
+ }
511
+ } else {
512
+ // otherwise, validate the value as a string
513
+ const metaDataError = EmulationUtils.UserAgentMetadata.validateAsStructuredHeadersString(
514
+ metaDataValue, i18nString(UIStrings.notRepresentable));
515
+ if (!metaDataError.valid) {
516
+ return metaDataError;
517
+ }
518
+ }
497
519
  }
520
+ return {valid: true};
498
521
  };
499
522
 
500
- #renderInputWithLabel(
501
- label: string, placeholder: string, value: string,
502
- stateKey: keyof Protocol.Emulation.UserAgentMetadata): Lit.TemplateResult {
523
+ override performUpdate(): void {
524
+ const input: ViewInput = {
525
+ brands: this.#metaData.brands ?? [{brand: '', version: ''}],
526
+ fullVersionList: this.#metaData.fullVersionList ?? [{brand: '', version: ''}],
527
+ fullVersion: this.#metaData.fullVersion || '',
528
+ formFactors: this.#metaData.formFactors ?? [],
529
+ platform: this.#metaData.platform,
530
+ platformVersion: this.#metaData.platformVersion,
531
+ architecture: this.#metaData.architecture,
532
+ model: this.#metaData.model,
533
+ mobile: this.#metaData.mobile,
534
+ isFormOpened: this.#isFormOpened,
535
+ isFormDisabled: this.#isFormDisabled,
536
+ showMobileCheckbox: this.#showMobileCheckbox,
537
+ showSubmitButton: this.#showSubmitButton,
538
+ useragentModifiedAriaMessage: this.#useragentModifiedAriaMessage,
539
+ onTreeToggle: this.#handleTreeClick,
540
+ onBrandAdd: list => void this.#handleBrandAdd(list),
541
+ onBrandDelete: (list, index) => void this.#handleBrandDelete(list, index),
542
+ onBrandInputChange: this.#handleBrandInputChange,
543
+ onTextFieldChange: this.#handleInputChange,
544
+ onMobileChange: mobile => this.#handleInputChange('mobile', mobile),
545
+ onFormFactorChange: this.#handleFormFactorCheckboxChange,
546
+ onSubmit: this.#handleSubmit,
547
+ };
548
+ this.#view(input, this.#viewOutput, this.contentElement);
549
+ }
550
+ }
551
+
552
+ export const DEFAULT_VIEW = (input: ViewInput, output: ViewOutput, target: HTMLElement): void => {
553
+ const renderInputWithLabel = (label: string, placeholder: string, value: string,
554
+ stateKey: TextField): Lit.TemplateResult => {
503
555
  const handleInputChange = (event: KeyboardEvent): void => {
504
556
  const value = (event.target as HTMLInputElement).value;
505
- this.#handleInputChange(stateKey, value);
557
+ input.onTextFieldChange(stateKey, value);
506
558
  };
507
559
  return html`
508
560
  <label class="full-row label input-field-label-container">
@@ -518,17 +570,16 @@ export class UserAgentClientHintsForm extends HTMLElement {
518
570
  />
519
571
  </label>
520
572
  `;
521
- }
573
+ };
522
574
 
523
- #renderPlatformSection(): Lit.TemplateResult {
524
- const {platform, platformVersion} = this.#metaData;
575
+ const renderPlatformSection = (): Lit.TemplateResult => {
525
576
  const handlePlatformNameChange = (event: KeyboardEvent): void => {
526
577
  const value = (event.target as HTMLInputElement).value;
527
- this.#handleInputChange('platform', value);
578
+ input.onTextFieldChange('platform', value);
528
579
  };
529
580
  const handlePlatformVersionChange = (event: KeyboardEvent): void => {
530
581
  const value = (event.target as HTMLInputElement).value;
531
- this.#handleInputChange('platformVersion', value);
582
+ input.onTextFieldChange('platformVersion', value);
532
583
  };
533
584
  return html`
534
585
  <span class="full-row label">${i18nString(UIStrings.platformLabel)}</span>
@@ -537,7 +588,7 @@ export class UserAgentClientHintsForm extends HTMLElement {
537
588
  class="input-field half-row"
538
589
  type="text"
539
590
  @input=${handlePlatformNameChange}
540
- .value=${platform}
591
+ .value=${input.platform}
541
592
  placeholder=${i18nString(UIStrings.platformPlaceholder)}
542
593
  aria-label=${i18nString(UIStrings.platformLabel)}
543
594
  jslog=${VisualLogging.textField('platform').track({
@@ -548,7 +599,7 @@ export class UserAgentClientHintsForm extends HTMLElement {
548
599
  class="input-field half-row"
549
600
  type="text"
550
601
  @input=${handlePlatformVersionChange}
551
- .value=${platformVersion}
602
+ .value=${input.platformVersion}
552
603
  placeholder=${i18nString(UIStrings.platformVersion)}
553
604
  aria-label=${i18nString(UIStrings.platformVersion)}
554
605
  jslog=${VisualLogging.textField('platform-version').track({
@@ -557,21 +608,20 @@ export class UserAgentClientHintsForm extends HTMLElement {
557
608
  />
558
609
  </div>
559
610
  `;
560
- }
611
+ };
561
612
 
562
- #renderDeviceModelSection(): Lit.TemplateResult {
563
- const {model, mobile} = this.#metaData;
613
+ const renderDeviceModelSection = (): Lit.TemplateResult => {
564
614
  const handleDeviceModelChange = (event: KeyboardEvent): void => {
565
615
  const value = (event.target as HTMLInputElement).value;
566
- this.#handleInputChange('model', value);
616
+ input.onTextFieldChange('model', value);
567
617
  };
568
618
  const handleMobileChange = (event: KeyboardEvent): void => {
569
619
  const value = (event.target as HTMLInputElement).checked;
570
- this.#handleInputChange('mobile', value);
620
+ input.onMobileChange(value);
571
621
  };
572
- const mobileCheckboxInput = this.#showMobileCheckbox ? html`
622
+ const mobileCheckboxInput = input.showMobileCheckbox ? html`
573
623
  <label class="mobile-checkbox-container">
574
- <input type="checkbox" @input=${handleMobileChange} .checked=${mobile}
624
+ <input type="checkbox" @input=${handleMobileChange} .checked=${input.mobile}
575
625
  jslog=${VisualLogging.toggle('mobile').track({
576
626
  click: true,
577
627
  })}
@@ -584,10 +634,10 @@ export class UserAgentClientHintsForm extends HTMLElement {
584
634
  <span class="full-row label">${i18nString(UIStrings.deviceModel)}</span>
585
635
  <div class="full-row brand-row" aria-label=${i18nString(UIStrings.deviceProperties)} role="group">
586
636
  <input
587
- class="input-field ${this.#showMobileCheckbox ? 'device-model-input' : 'full-row'}"
637
+ class="input-field ${input.showMobileCheckbox ? 'device-model-input' : 'full-row'}"
588
638
  type="text"
589
639
  @input=${handleDeviceModelChange}
590
- .value=${model}
640
+ .value=${input.model}
591
641
  placeholder=${i18nString(UIStrings.deviceModel)}
592
642
  jslog=${VisualLogging.textField('model').track({
593
643
  change: true,
@@ -596,22 +646,13 @@ export class UserAgentClientHintsForm extends HTMLElement {
596
646
  ${mobileCheckboxInput}
597
647
  </div>
598
648
  `;
599
- }
649
+ };
600
650
 
601
- #renderUseragent(): Lit.TemplateResult {
602
- const {
603
- brands =
604
- [
605
- {
606
- brand: '',
607
- version: '',
608
- },
609
- ],
610
- } = this.#metaData;
611
- const brandElements = brands.map((brandRow, index) => {
651
+ const renderUseragent = (): Lit.TemplateResult => {
652
+ const brandElements = input.brands.map((brandRow, index) => {
612
653
  const {brand, version} = brandRow;
613
654
  const handleDeleteClick = (): void => {
614
- this.#handleUseragentDelete(index);
655
+ input.onBrandDelete('brands', index);
615
656
  };
616
657
  const handleKeyPress = (event: KeyboardEvent): void => {
617
658
  if (event.code === 'Space' || event.code === 'Enter') {
@@ -621,11 +662,11 @@ export class UserAgentClientHintsForm extends HTMLElement {
621
662
  };
622
663
  const handleBrandChange = (event: KeyboardEvent): void => {
623
664
  const value = (event.target as HTMLInputElement).value;
624
- this.#handleUseragentInputChange(value, index, 'brandName');
665
+ input.onBrandInputChange('brands', index, 'brandName', value);
625
666
  };
626
667
  const handleVersionChange = (event: KeyboardEvent): void => {
627
668
  const value = (event.target as HTMLInputElement).value;
628
- this.#handleUseragentInputChange(value, index, 'brandVersion');
669
+ input.onBrandInputChange('brands', index, 'brandVersion', value);
629
670
  };
630
671
  return html`
631
672
  <div class="full-row brand-row" aria-label=${i18nString(UIStrings.brandProperties)} role="group">
@@ -669,6 +710,12 @@ export class UserAgentClientHintsForm extends HTMLElement {
669
710
  </div>
670
711
  `;
671
712
  });
713
+ const handleAddBrandKeyPress = (event: KeyboardEvent): void => {
714
+ if (event.code === 'Space' || event.code === 'Enter') {
715
+ event.preventDefault();
716
+ input.onBrandAdd('brands');
717
+ }
718
+ };
672
719
  return html`
673
720
  <span class="full-row label">${i18nString(UIStrings.useragent)}</span>
674
721
  ${brandElements}
@@ -678,8 +725,8 @@ export class UserAgentClientHintsForm extends HTMLElement {
678
725
  tabindex="0"
679
726
  id="add-brand-button"
680
727
  aria-label=${i18nString(UIStrings.addBrand)}
681
- @click=${this.#handleAddUseragentBrandClick}
682
- @keypress=${this.#handleAddUseragentBrandKeyPress}
728
+ @click=${() => input.onBrandAdd('brands')}
729
+ @keypress=${handleAddBrandKeyPress}
683
730
  >
684
731
  <devtools-icon
685
732
  aria-hidden="true" name="plus" class="medium">
@@ -687,22 +734,13 @@ export class UserAgentClientHintsForm extends HTMLElement {
687
734
  ${i18nString(UIStrings.addBrand)}
688
735
  </div>
689
736
  `;
690
- }
737
+ };
691
738
 
692
- #renderFullVersionList(): Lit.TemplateResult {
693
- const {
694
- fullVersionList =
695
- [
696
- {
697
- brand: '',
698
- version: '',
699
- },
700
- ],
701
- } = this.#metaData;
702
- const elements = fullVersionList.map((brandRow, index) => {
739
+ const renderFullVersionList = (): Lit.TemplateResult => {
740
+ const elements = input.fullVersionList.map((brandRow, index) => {
703
741
  const {brand, version} = brandRow;
704
742
  const handleDeleteClick = (): void => {
705
- this.#handleFullVersionListDelete(index);
743
+ input.onBrandDelete('fullVersionList', index);
706
744
  };
707
745
  const handleKeyPress = (event: KeyboardEvent): void => {
708
746
  if (event.code === 'Space' || event.code === 'Enter') {
@@ -712,11 +750,11 @@ export class UserAgentClientHintsForm extends HTMLElement {
712
750
  };
713
751
  const handleBrandChange = (event: KeyboardEvent): void => {
714
752
  const value = (event.target as HTMLInputElement).value;
715
- this.#handleFullVersionListInputChange(value, index, 'brandName');
753
+ input.onBrandInputChange('fullVersionList', index, 'brandName', value);
716
754
  };
717
755
  const handleVersionChange = (event: KeyboardEvent): void => {
718
756
  const value = (event.target as HTMLInputElement).value;
719
- this.#handleFullVersionListInputChange(value, index, 'brandVersion');
757
+ input.onBrandInputChange('fullVersionList', index, 'brandVersion', value);
720
758
  };
721
759
  return html`
722
760
  <div
@@ -751,7 +789,7 @@ export class UserAgentClientHintsForm extends HTMLElement {
751
789
  change: true,
752
790
  })}
753
791
  />
754
- <devtools-icon name="bin"
792
+ <devtools-icon name="bin"
755
793
  title=${i18nString(UIStrings.brandFullVersionListDelete)}
756
794
  class="medium delete-icon"
757
795
  tabindex="0"
@@ -764,6 +802,12 @@ export class UserAgentClientHintsForm extends HTMLElement {
764
802
  </div>
765
803
  `;
766
804
  });
805
+ const handleAddBrandKeyPress = (event: KeyboardEvent): void => {
806
+ if (event.code === 'Space' || event.code === 'Enter') {
807
+ event.preventDefault();
808
+ input.onBrandAdd('fullVersionList');
809
+ }
810
+ };
767
811
  return html`
768
812
  <span class="full-row label">${i18nString(UIStrings.fullVersionList)}</span>
769
813
  ${elements}
@@ -773,8 +817,8 @@ export class UserAgentClientHintsForm extends HTMLElement {
773
817
  tabindex="0"
774
818
  id="add-fvl-brand-button"
775
819
  aria-label=${i18nString(UIStrings.addBrand)}
776
- @click=${this.#handleAddFullVersionListBrandClick}
777
- @keypress=${this.#handleAddFullVersionListBrandKeyPress}
820
+ @click=${() => input.onBrandAdd('fullVersionList')}
821
+ @keypress=${handleAddBrandKeyPress}
778
822
  >
779
823
  <devtools-icon name="plus" class="medium"
780
824
  aria-hidden="true">
@@ -782,11 +826,11 @@ export class UserAgentClientHintsForm extends HTMLElement {
782
826
  ${i18nString(UIStrings.addBrand)}
783
827
  </div>
784
828
  `;
785
- }
829
+ };
786
830
 
787
- #renderFormFactorsSection(): Lit.TemplateResult {
831
+ const renderFormFactorsSection = (): Lit.TemplateResult => {
788
832
  const checkboxElements = ALL_PROTOCOL_FORM_FACTORS.map(ffValue => {
789
- const isChecked = this.#metaData.formFactors?.includes(ffValue) || false;
833
+ const isChecked = input.formFactors.includes(ffValue);
790
834
  const labelStringId = `formFactor${ffValue}` as keyof typeof UIStrings;
791
835
  const label = i18nString(UIStrings[labelStringId]);
792
836
 
@@ -799,8 +843,7 @@ export class UserAgentClientHintsForm extends HTMLElement {
799
843
  jslog=${VisualLogging.toggle(Platform.StringUtilities.toKebabCase(ffValue)).track({
800
844
  click: true,
801
845
  })}
802
- @change=${
803
- (e: Event) => this.#handleFormFactorCheckboxChange(ffValue, (e.target as HTMLInputElement).checked)}
846
+ @change=${(e: Event) => input.onFormFactorChange(ffValue, (e.target as HTMLInputElement).checked)}
804
847
  />
805
848
  ${label}
806
849
  </label>
@@ -816,138 +859,108 @@ export class UserAgentClientHintsForm extends HTMLElement {
816
859
  ${checkboxElements}
817
860
  </div>
818
861
  `;
819
- }
820
-
821
- #render(): void {
822
- const {fullVersion, architecture} = this.#metaData;
823
- const useragentSection = this.#renderUseragent();
824
- const fullVersionListSection = this.#renderFullVersionList();
825
- const fullBrowserInput = this.#renderInputWithLabel(
826
- i18nString(UIStrings.fullBrowserVersion), i18nString(UIStrings.fullBrowserVersionPlaceholder),
827
- fullVersion || '', 'fullVersion');
828
- const formFactorsSection = this.#renderFormFactorsSection();
829
- const platformSection = this.#renderPlatformSection();
830
- const architectureInput = this.#renderInputWithLabel(
831
- i18nString(UIStrings.architecture), i18nString(UIStrings.architecturePlaceholder), architecture,
832
- 'architecture');
833
- const deviceModelSection = this.#renderDeviceModelSection();
834
- // clang-format off
835
- const submitButton = this.#showSubmitButton ? html`
836
- <devtools-button
837
- .variant=${Buttons.Button.Variant.OUTLINED}
838
- .type=${'submit'}
839
- >
840
- ${i18nString(UIStrings.update)}
841
- </devtools-button>
842
- ` : Lit.nothing;
843
- // clang-format on
844
-
845
- // clang-format off
846
- const output = html`
847
- <style>${Input.checkboxStyles}</style>
848
- <style>${userAgentClientHintsFormStyles}</style>
849
- <section class="root">
850
- <div class="tree-title">
851
- <div
852
- role=button
853
- @click=${this.#handleTreeClick}
854
- tabindex=${this.#isFormDisabled ? '-1' : '0'}
855
- @keydown=${this.#handleTreeExpand}
856
- aria-expanded=${this.#isFormOpened}
857
- aria-controls=form-container
858
- aria-disabled=${this.#isFormDisabled}
859
- aria-label=${i18nString(UIStrings.title)}
860
- jslog=${VisualLogging.toggleSubpane().track({click: true})}>
861
- <devtools-icon name=triangle-right></devtools-icon>
862
- <devtools-icon name=triangle-down></devtools-icon>
863
- ${i18nString(UIStrings.title)}
864
- </div>
865
- <devtools-icon tabindex=${this.#isFormDisabled ? '-1' : '0'} class=info-icon name=info aria-label=${i18nString(UIStrings.userAgentClientHintsInfo)} title=${i18nString(UIStrings.userAgentClientHintsInfo)}></devtools-icon>
866
- <devtools-link
867
- tabindex=${this.#isFormDisabled ? '-1' : '0'}
868
- href="https://web.dev/user-agent-client-hints/"
869
- class="link"
870
- aria-label=${i18nString(UIStrings.learnMore)}
871
- jslogcontext="learn-more"
872
- >
873
- ${i18nString(UIStrings.learnMore)}
874
- </devtools-link>
875
- </div>
876
- <form
877
- id="form-container"
878
- class="form-container ${this.#isFormOpened ? '' : 'hide-container'}"
879
- @submit=${this.#handleSubmit}
880
- >
881
- ${useragentSection}
882
- <hr class="section-separator">
883
- ${fullVersionListSection}
884
- <hr class="section-separator">
885
- ${fullBrowserInput}
886
- <hr class="section-separator">
887
- ${formFactorsSection}
888
- <hr class="section-separator">
889
- ${platformSection}
890
- <hr class="section-separator">
891
- ${architectureInput}
892
- <hr class="section-separator">
893
- ${deviceModelSection}
894
- ${submitButton}
895
- </form>
896
- <div aria-live="polite" aria-label=${this.#useragentModifiedAriaMessage}></div>
897
- </section>
898
- `;
899
- // clang-format on
900
- Lit.render(output, this.#shadow, {host: this});
901
- }
862
+ };
902
863
 
903
- validate = (): UI.ListWidget.ValidatorResult => {
904
- for (const [metaDataKey, metaDataValue] of Object.entries(this.#metaData)) {
905
- if (metaDataKey === 'brands' || metaDataKey === 'fullVersionList') {
906
- // for structured fields, check each individual brand/version
907
- const isBrandValid = this.#metaData.brands?.every(({brand, version}) => {
908
- const brandNameResult = EmulationUtils.UserAgentMetadata.validateAsStructuredHeadersString(
909
- brand, i18nString(UIStrings.notRepresentable));
910
- const brandVersionResult = EmulationUtils.UserAgentMetadata.validateAsStructuredHeadersString(
911
- version, i18nString(UIStrings.notRepresentable));
912
- return brandNameResult.valid && brandVersionResult.valid;
913
- });
914
- if (!isBrandValid) {
915
- return {valid: false, errorMessage: i18nString(UIStrings.notRepresentable)};
916
- }
917
- } else if (metaDataKey === 'formFactors') {
918
- const formFactors = metaDataValue as string[] | undefined;
919
- if (formFactors) {
920
- for (const ff of formFactors) {
921
- if (!ALL_PROTOCOL_FORM_FACTORS.includes(ff)) {
922
- return {
923
- valid: false,
924
- errorMessage: i18nString(UIStrings.notRepresentable) + ` (Invalid form factor: ${ff})`,
925
- };
926
- }
927
- const ffError = EmulationUtils.UserAgentMetadata.validateAsStructuredHeadersString(
928
- ff, i18nString(UIStrings.notRepresentable));
929
- if (!ffError.valid) {
930
- return ffError;
931
- }
932
- }
933
- }
934
- } else {
935
- // otherwise, validate the value as a string
936
- const metaDataError = EmulationUtils.UserAgentMetadata.validateAsStructuredHeadersString(
937
- metaDataValue, i18nString(UIStrings.notRepresentable));
938
- if (!metaDataError.valid) {
939
- return metaDataError;
940
- }
941
- }
864
+ const useragentSection = renderUseragent();
865
+ const fullVersionListSection = renderFullVersionList();
866
+ const fullBrowserInput =
867
+ renderInputWithLabel(i18nString(UIStrings.fullBrowserVersion),
868
+ i18nString(UIStrings.fullBrowserVersionPlaceholder), input.fullVersion, 'fullVersion');
869
+ const formFactorsSection = renderFormFactorsSection();
870
+ const platformSection = renderPlatformSection();
871
+ const architectureInput =
872
+ renderInputWithLabel(i18nString(UIStrings.architecture), i18nString(UIStrings.architecturePlaceholder),
873
+ input.architecture, 'architecture');
874
+ const deviceModelSection = renderDeviceModelSection();
875
+ const handleTreeKeyDown = (event: KeyboardEvent): void => {
876
+ if (event.code === 'Space' || event.code === 'Enter' || event.code === 'ArrowLeft' || event.code === 'ArrowRight') {
877
+ event.consume(true);
878
+ input.onTreeToggle(event.code);
942
879
  }
943
- return {valid: true};
944
880
  };
945
- }
946
-
947
- customElements.define('devtools-user-agent-client-hints-form', UserAgentClientHintsForm);
948
-
949
- declare global {
950
- interface HTMLElementTagNameMap {
951
- 'devtools-user-agent-client-hints-form': UserAgentClientHintsForm;
952
- }
953
- }
881
+ const handleSubmit = (event: SubmitEvent): void => {
882
+ event.preventDefault();
883
+ input.onSubmit();
884
+ };
885
+ const setFormElement = (element: Element|undefined): void => {
886
+ if (!(element instanceof HTMLFormElement)) {
887
+ output.focusBrandInput = undefined;
888
+ output.focusAddBrandButton = undefined;
889
+ return;
890
+ }
891
+ output.focusBrandInput = (list: BrandList, index: number): void => {
892
+ element.querySelector<HTMLElement>(`#${BRAND_INPUT_ID_PREFIXES[list]}-brand-${index + 1}-input`)?.focus();
893
+ };
894
+ output.focusAddBrandButton = (list: BrandList): void => {
895
+ element.querySelector<HTMLElement>(`#${ADD_BRAND_BUTTON_IDS[list]}`)?.focus();
896
+ };
897
+ };
898
+ // clang-format off
899
+ const submitButton = input.showSubmitButton ? html`
900
+ <devtools-button
901
+ .variant=${Buttons.Button.Variant.OUTLINED}
902
+ .type=${'submit'}
903
+ >
904
+ ${i18nString(UIStrings.update)}
905
+ </devtools-button>
906
+ ` : Lit.nothing;
907
+ // clang-format on
908
+
909
+ // clang-format off
910
+ const template = html`
911
+ <style>${Input.checkboxStyles}</style>
912
+ <style>${userAgentClientHintsFormStyles}</style>
913
+ <section class="root">
914
+ <div class="tree-title">
915
+ <div
916
+ role=button
917
+ @click=${() => input.onTreeToggle()}
918
+ tabindex=${input.isFormDisabled ? '-1' : '0'}
919
+ @keydown=${handleTreeKeyDown}
920
+ aria-expanded=${input.isFormOpened}
921
+ aria-controls=form-container
922
+ aria-disabled=${input.isFormDisabled}
923
+ aria-label=${i18nString(UIStrings.title)}
924
+ jslog=${VisualLogging.toggleSubpane().track({click: true})}>
925
+ <devtools-icon name=triangle-right></devtools-icon>
926
+ <devtools-icon name=triangle-down></devtools-icon>
927
+ ${i18nString(UIStrings.title)}
928
+ </div>
929
+ <devtools-icon tabindex=${input.isFormDisabled ? '-1' : '0'} class=info-icon name=info aria-label=${i18nString(UIStrings.userAgentClientHintsInfo)} title=${i18nString(UIStrings.userAgentClientHintsInfo)}></devtools-icon>
930
+ <devtools-link
931
+ tabindex=${input.isFormDisabled ? '-1' : '0'}
932
+ href="https://web.dev/user-agent-client-hints/"
933
+ class="link"
934
+ aria-label=${i18nString(UIStrings.learnMore)}
935
+ jslogcontext="learn-more"
936
+ >
937
+ ${i18nString(UIStrings.learnMore)}
938
+ </devtools-link>
939
+ </div>
940
+ <form
941
+ id="form-container"
942
+ class="form-container ${input.isFormOpened ? '' : 'hide-container'}"
943
+ @submit=${handleSubmit}
944
+ ${ref(setFormElement)}
945
+ >
946
+ ${useragentSection}
947
+ <hr class="section-separator">
948
+ ${fullVersionListSection}
949
+ <hr class="section-separator">
950
+ ${fullBrowserInput}
951
+ <hr class="section-separator">
952
+ ${formFactorsSection}
953
+ <hr class="section-separator">
954
+ ${platformSection}
955
+ <hr class="section-separator">
956
+ ${architectureInput}
957
+ <hr class="section-separator">
958
+ ${deviceModelSection}
959
+ ${submitButton}
960
+ </form>
961
+ <div aria-live="polite" aria-label=${input.useragentModifiedAriaMessage}></div>
962
+ </section>
963
+ `;
964
+ // clang-format on
965
+ Lit.render(template, target);
966
+ };