chrome-devtools-frontend 1.0.1550444 → 1.0.1555174

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 (466) hide show
  1. package/docs/styleguide/ux/components.md +34 -8
  2. package/eslint.config.mjs +7 -0
  3. package/front_end/Images/generate-css-vars.js +8 -4
  4. package/front_end/Images/src/spark.svg +10 -0
  5. package/front_end/core/common/Settings.ts +20 -8
  6. package/front_end/core/host/UserMetrics.ts +3 -1
  7. package/front_end/core/i18n/collect-ui-strings.js +19 -10
  8. package/front_end/core/i18n/generate-locales-js.js +4 -4
  9. package/front_end/core/protocol_client/CDPConnection.ts +1 -0
  10. package/front_end/core/protocol_client/InspectorBackend.ts +5 -1
  11. package/front_end/core/root/Runtime.ts +0 -12
  12. package/front_end/core/sdk/DOMModel.ts +38 -3
  13. package/front_end/core/sdk/DebuggerModel.ts +9 -4
  14. package/front_end/core/sdk/IsolateManager.ts +7 -0
  15. package/front_end/core/sdk/PageResourceLoader.ts +3 -3
  16. package/front_end/core/sdk/RehydratingConnection.ts +5 -2
  17. package/front_end/core/sdk/ResourceTreeModel.ts +0 -1
  18. package/front_end/entrypoints/main/MainImpl.ts +28 -10
  19. package/front_end/generated/SupportedCSSProperties.js +32 -0
  20. package/front_end/models/ai_assistance/AiConversation.ts +94 -4
  21. package/front_end/models/ai_assistance/agents/AiAgent.ts +30 -15
  22. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +179 -41
  23. package/front_end/models/ai_assistance/agents/StylingAgent.snapshot.txt +5 -0
  24. package/front_end/models/ai_assistance/agents/StylingAgent.ts +62 -0
  25. package/front_end/models/ai_assistance/data_formatters/NetworkRequestFormatter.ts +2 -1
  26. package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.ts +1 -7
  27. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +124 -12
  28. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.ts +125 -30
  29. package/front_end/models/ai_assistance/performance/AICallTree.ts +42 -0
  30. package/front_end/models/ai_code_generation/AiCodeGeneration.ts +12 -0
  31. package/front_end/models/issues_manager/Issue.ts +1 -0
  32. package/front_end/models/issues_manager/IssueAggregator.ts +9 -0
  33. package/front_end/models/issues_manager/IssuesManager.ts +5 -0
  34. package/front_end/models/issues_manager/PermissionElementIssue.ts +262 -0
  35. package/front_end/models/issues_manager/descriptions/permissionElementActivationDisabled.md +7 -0
  36. package/front_end/models/issues_manager/descriptions/permissionElementActivationDisabledWithOccluder.md +9 -0
  37. package/front_end/models/issues_manager/descriptions/permissionElementActivationDisabledWithOccluderParent.md +9 -0
  38. package/front_end/models/issues_manager/descriptions/permissionElementCspFrameAncestorsMissing.md +5 -0
  39. package/front_end/models/issues_manager/descriptions/permissionElementFencedFrameDisallowed.md +5 -0
  40. package/front_end/models/issues_manager/descriptions/permissionElementFontSizeTooLarge.md +5 -0
  41. package/front_end/models/issues_manager/descriptions/permissionElementFontSizeTooSmall.md +5 -0
  42. package/front_end/models/issues_manager/descriptions/permissionElementGeolocationDeprecated.md +5 -0
  43. package/front_end/models/issues_manager/descriptions/permissionElementInsetBoxShadowUnsupported.md +5 -0
  44. package/front_end/models/issues_manager/descriptions/permissionElementInvalidDisplayStyle.md +5 -0
  45. package/front_end/models/issues_manager/descriptions/permissionElementInvalidSizeValue.md +5 -0
  46. package/front_end/models/issues_manager/descriptions/permissionElementInvalidType.md +5 -0
  47. package/front_end/models/issues_manager/descriptions/permissionElementInvalidTypeActivation.md +5 -0
  48. package/front_end/models/issues_manager/descriptions/permissionElementLowContrast.md +5 -0
  49. package/front_end/models/issues_manager/descriptions/permissionElementNonOpaqueColor.md +5 -0
  50. package/front_end/models/issues_manager/descriptions/permissionElementPaddingBottomUnsupported.md +6 -0
  51. package/front_end/models/issues_manager/descriptions/permissionElementPaddingRightUnsupported.md +6 -0
  52. package/front_end/models/issues_manager/descriptions/permissionElementPermissionsPolicyBlocked.md +5 -0
  53. package/front_end/models/issues_manager/descriptions/permissionElementRegistrationFailed.md +5 -0
  54. package/front_end/models/issues_manager/descriptions/permissionElementRequestInProgress.md +5 -0
  55. package/front_end/models/issues_manager/descriptions/permissionElementSecurityChecksFailed.md +5 -0
  56. package/front_end/models/issues_manager/descriptions/permissionElementTypeNotSupported.md +5 -0
  57. package/front_end/models/issues_manager/descriptions/permissionElementUntrustedEvent.md +7 -0
  58. package/front_end/models/issues_manager/issues_manager.ts +2 -0
  59. package/front_end/models/javascript_metadata/NativeFunctions.js +23 -23
  60. package/front_end/models/stack_trace/StackTraceModel.ts +37 -18
  61. package/front_end/models/trace/Processor.ts +14 -15
  62. package/front_end/models/trace/insights/Common.ts +2 -8
  63. package/front_end/models/trace/insights/types.ts +12 -2
  64. package/front_end/models/trace/types/TraceEvents.ts +4 -1
  65. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +28 -6
  66. package/front_end/panels/ai_assistance/ai_assistance-meta.ts +9 -23
  67. package/front_end/panels/ai_assistance/ai_assistance.ts +1 -0
  68. package/front_end/panels/ai_assistance/components/ChatView.ts +78 -2
  69. package/front_end/panels/ai_assistance/components/CollapsibleAssistanceContentWidget.ts +71 -0
  70. package/front_end/panels/ai_assistance/components/PerformanceAgentFlameChart.ts +126 -0
  71. package/front_end/panels/ai_assistance/components/PerformanceAgentMarkdownRenderer.ts +131 -2
  72. package/front_end/panels/ai_assistance/components/chatView.css +28 -0
  73. package/front_end/panels/ai_assistance/components/collapsibleAssistanceContentWidget.css +33 -0
  74. package/front_end/panels/application/AppManifestView.ts +1007 -521
  75. package/front_end/panels/application/ApplicationPanelSidebar.ts +1 -17
  76. package/front_end/panels/application/BounceTrackingMitigationsTreeElement.ts +2 -6
  77. package/front_end/panels/application/ExtensionStorageItemsView.ts +3 -5
  78. package/front_end/panels/application/KeyValueStorageItemsView.ts +3 -2
  79. package/front_end/panels/application/components/BackForwardCacheView.ts +2 -2
  80. package/front_end/panels/application/components/BounceTrackingMitigationsView.ts +133 -118
  81. package/front_end/panels/application/preloading/PreloadingView.ts +46 -45
  82. package/front_end/panels/application/preloading/components/MismatchedPreloadingGrid.ts +80 -75
  83. package/front_end/panels/application/preloading/components/PreloadingDisabledInfobar.ts +169 -133
  84. package/front_end/panels/application/preloading/components/RuleSetDetailsView.ts +74 -93
  85. package/front_end/panels/application/preloading/components/RuleSetGrid.ts +142 -117
  86. package/front_end/panels/application/preloading/components/UsedPreloadingView.ts +51 -11
  87. package/front_end/panels/application/preloading/components/components.ts +0 -2
  88. package/front_end/panels/common/Annotation.ts +184 -0
  89. package/front_end/panels/common/AnnotationManager.ts +148 -11
  90. package/front_end/panels/common/ExtensionView.ts +47 -0
  91. package/front_end/panels/common/PersistenceUtils.ts +22 -25
  92. package/front_end/panels/common/annotation.css +40 -0
  93. package/front_end/panels/common/common.ts +1 -0
  94. package/front_end/panels/console/ConsoleInsightTeaser.ts +187 -5
  95. package/front_end/panels/console/ConsolePinPane.ts +437 -217
  96. package/front_end/panels/console/ConsolePrompt.ts +32 -223
  97. package/front_end/panels/console/ConsoleView.ts +67 -66
  98. package/front_end/panels/console/ConsoleViewMessage.ts +8 -3
  99. package/front_end/panels/console/consoleInsightTeaser.css +23 -0
  100. package/front_end/panels/console/consoleView.css +1 -1
  101. package/front_end/panels/elements/AdoptedStyleSheetTreeElement.ts +89 -0
  102. package/front_end/panels/elements/ColorSwatchPopoverIcon.ts +2 -2
  103. package/front_end/panels/elements/ComputedStyleWidget.ts +16 -5
  104. package/front_end/panels/elements/ElementsPanel.ts +38 -12
  105. package/front_end/panels/elements/ElementsSidebarPane.ts +1 -3
  106. package/front_end/panels/elements/ElementsTreeElement.ts +313 -353
  107. package/front_end/panels/elements/ElementsTreeOutline.ts +39 -13
  108. package/front_end/panels/elements/StylePropertyTreeElement.ts +15 -14
  109. package/front_end/panels/elements/StylesSidebarPane.ts +1 -1
  110. package/front_end/panels/emulation/MediaQueryInspector.ts +171 -125
  111. package/front_end/panels/explain/components/ConsoleInsight.ts +175 -150
  112. package/front_end/panels/explain/components/consoleInsight.css +348 -347
  113. package/front_end/panels/issues/AffectedPermissionElementsView.ts +46 -0
  114. package/front_end/panels/issues/IssueView.ts +2 -0
  115. package/front_end/panels/issues/IssuesPane.ts +6 -0
  116. package/front_end/panels/layer_viewer/LayerDetailsView.ts +165 -149
  117. package/front_end/panels/layer_viewer/Layers3DView.ts +131 -78
  118. package/front_end/panels/lighthouse/LighthouseStatusView.ts +149 -100
  119. package/front_end/panels/linear_memory_inspector/components/LinearMemoryHighlightChipList.ts +90 -64
  120. package/front_end/panels/linear_memory_inspector/components/LinearMemoryInspector.ts +25 -34
  121. package/front_end/panels/linear_memory_inspector/components/LinearMemoryValueInterpreter.ts +60 -44
  122. package/front_end/panels/linear_memory_inspector/components/ValueInterpreterDisplay.ts +170 -151
  123. package/front_end/panels/linear_memory_inspector/components/ValueInterpreterSettings.ts +50 -51
  124. package/front_end/panels/linear_memory_inspector/components/valueInterpreterDisplay.css +0 -13
  125. package/front_end/panels/linear_memory_inspector/components/valueInterpreterSettings.css +20 -18
  126. package/front_end/panels/mobile_throttling/CalibrationController.ts +3 -1
  127. package/front_end/panels/network/NetworkDataGridNode.ts +14 -6
  128. package/front_end/panels/network/NetworkItemView.ts +42 -3
  129. package/front_end/panels/network/NetworkLogView.ts +10 -11
  130. package/front_end/panels/network/NetworkPanel.ts +63 -1
  131. package/front_end/panels/network/RequestInitiatorView.ts +146 -113
  132. package/front_end/panels/network/components/RequestHeaderSection.css +51 -50
  133. package/front_end/panels/network/components/RequestHeaderSection.ts +81 -71
  134. package/front_end/panels/network/components/RequestHeadersView.css +1 -1
  135. package/front_end/panels/network/components/RequestHeadersView.ts +26 -11
  136. package/front_end/panels/network/components/RequestTrustTokensView.css +24 -14
  137. package/front_end/panels/network/components/RequestTrustTokensView.ts +144 -140
  138. package/front_end/panels/profiler/IsolateSelector.ts +2 -1
  139. package/front_end/panels/protocol_monitor/ProtocolMonitor.ts +49 -33
  140. package/front_end/panels/recorder/RecorderController.ts +2 -3
  141. package/front_end/panels/recorder/components/ControlButton.ts +68 -34
  142. package/front_end/panels/recorder/components/CreateRecordingView.ts +9 -6
  143. package/front_end/panels/recorder/components/RecordingView.ts +81 -88
  144. package/front_end/panels/recorder/components/ReplaySection.ts +226 -145
  145. package/front_end/panels/recorder/{controllers → components}/SelectorPicker.ts +129 -52
  146. package/front_end/panels/recorder/components/StepEditor.ts +21 -67
  147. package/front_end/panels/recorder/components/StepView.ts +222 -180
  148. package/front_end/panels/recorder/components/TimelineSection.ts +69 -48
  149. package/front_end/panels/recorder/components/components.ts +2 -2
  150. package/front_end/panels/recorder/components/selectorPicker.css +14 -0
  151. package/front_end/panels/recorder/components/stepEditor.css +0 -5
  152. package/front_end/panels/recorder/components/stepView.css +196 -198
  153. package/front_end/panels/recorder/extensions/ExtensionManager.ts +4 -48
  154. package/front_end/panels/recorder/models/ScreenshotUtils.ts +17 -11
  155. package/front_end/panels/security/CookieControlsView.ts +30 -26
  156. package/front_end/panels/settings/components/SyncSection.ts +0 -1
  157. package/front_end/panels/sources/CSSPlugin.ts +3 -3
  158. package/front_end/panels/sources/CallStackSidebarPane.ts +60 -75
  159. package/front_end/panels/sources/SourcesPanel.ts +1 -11
  160. package/front_end/panels/sources/TabbedEditorContainer.ts +11 -8
  161. package/front_end/panels/sources/ThreadsSidebarPane.ts +96 -101
  162. package/front_end/panels/sources/threadsSidebarPane.css +6 -5
  163. package/front_end/panels/timeline/CompatibilityTracksAppender.ts +0 -1
  164. package/front_end/panels/timeline/InteractionsTrackAppender.ts +2 -3
  165. package/front_end/panels/timeline/TimelineFlameChartDataProvider.ts +23 -33
  166. package/front_end/panels/timeline/TimelineFlameChartNetworkDataProvider.ts +4 -3
  167. package/front_end/panels/timeline/TimelineFlameChartView.ts +12 -0
  168. package/front_end/panels/timeline/TimelinePanel.ts +3 -5
  169. package/front_end/panels/timeline/components/IgnoreListSetting.ts +164 -142
  170. package/front_end/panels/timeline/components/InteractionBreakdown.ts +48 -28
  171. package/front_end/panels/timeline/components/LayoutShiftDetails.ts +18 -23
  172. package/front_end/panels/timeline/components/NetworkRequestTooltip.ts +98 -73
  173. package/front_end/panels/timeline/components/Sidebar.ts +16 -7
  174. package/front_end/panels/timeline/components/SidebarInsightsTab.ts +174 -137
  175. package/front_end/panels/timeline/components/SidebarSingleInsightSet.ts +262 -291
  176. package/front_end/panels/timeline/components/Utils.ts +25 -0
  177. package/front_end/panels/timeline/components/insights/BaseInsightComponent.ts +210 -146
  178. package/front_end/panels/timeline/components/insights/CLSCulprits.ts +8 -20
  179. package/front_end/panels/timeline/components/insights/Cache.ts +0 -9
  180. package/front_end/panels/timeline/components/insights/Checklist.ts +53 -43
  181. package/front_end/panels/timeline/components/insights/DOMSize.ts +11 -33
  182. package/front_end/panels/timeline/components/insights/DocumentLatency.ts +6 -12
  183. package/front_end/panels/timeline/components/insights/DuplicatedJavaScript.ts +0 -9
  184. package/front_end/panels/timeline/components/insights/EventRef.ts +47 -109
  185. package/front_end/panels/timeline/components/insights/FontDisplay.ts +0 -9
  186. package/front_end/panels/timeline/components/insights/ForcedReflow.ts +0 -9
  187. package/front_end/panels/timeline/components/insights/INPBreakdown.ts +0 -9
  188. package/front_end/panels/timeline/components/insights/ImageDelivery.ts +1 -10
  189. package/front_end/panels/timeline/components/insights/ImageRef.ts +112 -0
  190. package/front_end/panels/timeline/components/insights/InsightRenderer.ts +91 -0
  191. package/front_end/panels/timeline/components/insights/LCPBreakdown.ts +0 -9
  192. package/front_end/panels/timeline/components/insights/LCPDiscovery.ts +11 -14
  193. package/front_end/panels/timeline/components/insights/LegacyJavaScript.ts +0 -9
  194. package/front_end/panels/timeline/components/insights/ModernHTTP.ts +0 -9
  195. package/front_end/panels/timeline/components/insights/NetworkDependencyTree.ts +6 -21
  196. package/front_end/panels/timeline/components/insights/NodeLink.ts +68 -43
  197. package/front_end/panels/timeline/components/insights/README.md +2 -3
  198. package/front_end/panels/timeline/components/insights/RenderBlocking.ts +0 -9
  199. package/front_end/panels/timeline/components/insights/SlowCSSSelector.ts +0 -9
  200. package/front_end/panels/timeline/components/insights/ThirdParties.ts +0 -9
  201. package/front_end/panels/timeline/components/insights/Viewport.ts +7 -19
  202. package/front_end/panels/timeline/components/insights/baseInsightComponent.css +5 -0
  203. package/front_end/panels/timeline/components/insights/insights.ts +2 -0
  204. package/front_end/panels/timeline/components/interactionBreakdown.css +15 -13
  205. package/front_end/panels/timeline/components/sidebarInsightsTab.css +50 -48
  206. package/front_end/panels/timeline/enable-easter-egg.js +7 -3
  207. package/front_end/third_party/chromium/README.chromium +1 -1
  208. package/front_end/third_party/puppeteer/README.chromium +2 -2
  209. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/BluetoothEmulation.d.ts +96 -0
  210. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/BluetoothEmulation.d.ts.map +1 -0
  211. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/BluetoothEmulation.js +8 -0
  212. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/BluetoothEmulation.js.map +1 -0
  213. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Browser.d.ts +87 -0
  214. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Browser.d.ts.map +1 -1
  215. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Browser.js.map +1 -1
  216. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/DeviceRequestPrompt.d.ts +2 -6
  217. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/DeviceRequestPrompt.d.ts.map +1 -1
  218. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/DeviceRequestPrompt.js +5 -24
  219. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/DeviceRequestPrompt.js.map +1 -1
  220. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Page.d.ts +5 -0
  221. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Page.d.ts.map +1 -1
  222. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Page.js.map +1 -1
  223. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/api.d.ts +1 -0
  224. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/api.d.ts.map +1 -1
  225. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/api.js.map +1 -1
  226. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/BluetoothEmulation.d.ts +18 -0
  227. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/BluetoothEmulation.d.ts.map +1 -0
  228. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/BluetoothEmulation.js +42 -0
  229. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/BluetoothEmulation.js.map +1 -0
  230. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Browser.d.ts +4 -1
  231. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Browser.d.ts.map +1 -1
  232. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Browser.js +10 -0
  233. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Browser.js.map +1 -1
  234. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/DeviceRequestPrompt.d.ts +27 -0
  235. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/DeviceRequestPrompt.d.ts.map +1 -0
  236. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/DeviceRequestPrompt.js +90 -0
  237. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/DeviceRequestPrompt.js.map +1 -0
  238. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Frame.d.ts +3 -1
  239. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Frame.d.ts.map +1 -1
  240. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Frame.js +3 -2
  241. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Frame.js.map +1 -1
  242. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Page.d.ts +4 -1
  243. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Page.d.ts.map +1 -1
  244. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Page.js +5 -2
  245. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Page.js.map +1 -1
  246. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/BrowsingContext.d.ts +4 -0
  247. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/BrowsingContext.d.ts.map +1 -1
  248. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/BrowsingContext.js +14 -2
  249. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/BrowsingContext.js.map +1 -1
  250. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/util.d.ts.map +1 -1
  251. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/util.js +7 -0
  252. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/util.js.map +1 -1
  253. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Accessibility.js +1 -1
  254. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Accessibility.js.map +1 -1
  255. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/BluetoothEmulation.d.ts +18 -0
  256. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/BluetoothEmulation.d.ts.map +1 -0
  257. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/BluetoothEmulation.js +30 -0
  258. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/BluetoothEmulation.js.map +1 -0
  259. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.d.ts +5 -2
  260. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.d.ts.map +1 -1
  261. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.js +22 -3
  262. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.js.map +1 -1
  263. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/CdpSession.d.ts +1 -1
  264. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/CdpSession.d.ts.map +1 -1
  265. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/DeviceRequestPrompt.d.ts +2 -3
  266. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/DeviceRequestPrompt.d.ts.map +1 -1
  267. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/DeviceRequestPrompt.js +4 -6
  268. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/DeviceRequestPrompt.js.map +1 -1
  269. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/FrameManager.d.ts +2 -2
  270. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/FrameManager.d.ts.map +1 -1
  271. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/FrameManager.js +1 -1
  272. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/FrameManager.js.map +1 -1
  273. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.d.ts +2 -0
  274. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.d.ts.map +1 -1
  275. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.js +8 -0
  276. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.js.map +1 -1
  277. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/WebWorker.d.ts.map +1 -1
  278. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/WebWorker.js +8 -3
  279. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/WebWorker.js.map +1 -1
  280. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/cdp.d.ts +1 -0
  281. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/cdp.d.ts.map +1 -1
  282. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/cdp.js +1 -0
  283. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/cdp.js.map +1 -1
  284. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/common/BrowserConnector.js +30 -0
  285. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/common/BrowserConnector.js.map +1 -1
  286. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/common/ConnectOptions.d.ts +15 -0
  287. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/common/ConnectOptions.d.ts.map +1 -1
  288. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/injected/injected.d.ts +1 -1
  289. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/ChromeLauncher.d.ts +1 -1
  290. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/ChromeLauncher.d.ts.map +1 -1
  291. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/ChromeLauncher.js +2 -13
  292. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/ChromeLauncher.js.map +1 -1
  293. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/LaunchOptions.d.ts +5 -3
  294. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/LaunchOptions.d.ts.map +1 -1
  295. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/LaunchOptions.js +17 -0
  296. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/LaunchOptions.js.map +1 -1
  297. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.d.ts +3 -3
  298. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js +3 -3
  299. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js.map +1 -1
  300. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Mutex.d.ts +2 -2
  301. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.d.ts +1 -1
  302. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.js +1 -1
  303. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.d.ts +221 -4
  304. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +182 -95
  305. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/BluetoothEmulation.d.ts +96 -0
  306. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/BluetoothEmulation.d.ts.map +1 -0
  307. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/BluetoothEmulation.js +7 -0
  308. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/BluetoothEmulation.js.map +1 -0
  309. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Browser.d.ts +87 -0
  310. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Browser.d.ts.map +1 -1
  311. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Browser.js.map +1 -1
  312. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/DeviceRequestPrompt.d.ts +2 -6
  313. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/DeviceRequestPrompt.d.ts.map +1 -1
  314. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/DeviceRequestPrompt.js +4 -22
  315. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/DeviceRequestPrompt.js.map +1 -1
  316. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Page.d.ts +5 -0
  317. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Page.d.ts.map +1 -1
  318. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Page.js.map +1 -1
  319. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/api.d.ts +1 -0
  320. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/api.d.ts.map +1 -1
  321. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/api.js.map +1 -1
  322. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/BluetoothEmulation.d.ts +18 -0
  323. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/BluetoothEmulation.d.ts.map +1 -0
  324. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/BluetoothEmulation.js +38 -0
  325. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/BluetoothEmulation.js.map +1 -0
  326. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Browser.d.ts +4 -1
  327. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Browser.d.ts.map +1 -1
  328. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Browser.js +10 -0
  329. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Browser.js.map +1 -1
  330. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/DeviceRequestPrompt.d.ts +27 -0
  331. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/DeviceRequestPrompt.d.ts.map +1 -0
  332. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/DeviceRequestPrompt.js +85 -0
  333. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/DeviceRequestPrompt.js.map +1 -0
  334. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Frame.d.ts +3 -1
  335. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Frame.d.ts.map +1 -1
  336. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Frame.js +3 -2
  337. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Frame.js.map +1 -1
  338. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Page.d.ts +4 -1
  339. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Page.d.ts.map +1 -1
  340. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Page.js +5 -2
  341. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Page.js.map +1 -1
  342. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/core/BrowsingContext.d.ts +4 -0
  343. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/core/BrowsingContext.d.ts.map +1 -1
  344. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/core/BrowsingContext.js +14 -2
  345. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/core/BrowsingContext.js.map +1 -1
  346. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/util.d.ts.map +1 -1
  347. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/util.js +7 -0
  348. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/util.js.map +1 -1
  349. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Accessibility.js +1 -1
  350. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Accessibility.js.map +1 -1
  351. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/BluetoothEmulation.d.ts +18 -0
  352. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/BluetoothEmulation.d.ts.map +1 -0
  353. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/BluetoothEmulation.js +26 -0
  354. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/BluetoothEmulation.js.map +1 -0
  355. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.d.ts +5 -2
  356. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.d.ts.map +1 -1
  357. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.js +22 -3
  358. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.js.map +1 -1
  359. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/CdpSession.d.ts +1 -1
  360. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/CdpSession.d.ts.map +1 -1
  361. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/DeviceRequestPrompt.d.ts +2 -3
  362. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/DeviceRequestPrompt.d.ts.map +1 -1
  363. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/DeviceRequestPrompt.js +2 -4
  364. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/DeviceRequestPrompt.js.map +1 -1
  365. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/FrameManager.d.ts +2 -2
  366. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/FrameManager.d.ts.map +1 -1
  367. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/FrameManager.js +2 -2
  368. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/FrameManager.js.map +1 -1
  369. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.d.ts +2 -0
  370. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.d.ts.map +1 -1
  371. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.js +8 -0
  372. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.js.map +1 -1
  373. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/WebWorker.d.ts.map +1 -1
  374. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/WebWorker.js +8 -3
  375. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/WebWorker.js.map +1 -1
  376. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/cdp.d.ts +1 -0
  377. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/cdp.d.ts.map +1 -1
  378. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/cdp.js +1 -0
  379. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/cdp.js.map +1 -1
  380. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/common/BrowserConnector.js +31 -1
  381. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/common/BrowserConnector.js.map +1 -1
  382. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/common/ConnectOptions.d.ts +15 -0
  383. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/common/ConnectOptions.d.ts.map +1 -1
  384. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/ChromeLauncher.d.ts +1 -1
  385. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/ChromeLauncher.d.ts.map +1 -1
  386. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/ChromeLauncher.js +2 -13
  387. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/ChromeLauncher.js.map +1 -1
  388. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/LaunchOptions.d.ts +5 -3
  389. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/LaunchOptions.d.ts.map +1 -1
  390. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/LaunchOptions.js +16 -1
  391. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/LaunchOptions.js.map +1 -1
  392. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.d.ts +3 -3
  393. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js +3 -3
  394. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js.map +1 -1
  395. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.d.ts +1 -1
  396. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.js +1 -1
  397. package/front_end/third_party/puppeteer/package/lib/types.d.ts +221 -4
  398. package/front_end/third_party/puppeteer/package/package.json +4 -4
  399. package/front_end/third_party/puppeteer/package/src/api/BluetoothEmulation.ts +103 -0
  400. package/front_end/third_party/puppeteer/package/src/api/Browser.ts +96 -1
  401. package/front_end/third_party/puppeteer/package/src/api/DeviceRequestPrompt.ts +2 -10
  402. package/front_end/third_party/puppeteer/package/src/api/Page.ts +6 -0
  403. package/front_end/third_party/puppeteer/package/src/api/api.ts +1 -0
  404. package/front_end/third_party/puppeteer/package/src/bidi/BluetoothEmulation.ts +52 -0
  405. package/front_end/third_party/puppeteer/package/src/bidi/Browser.ts +15 -0
  406. package/front_end/third_party/puppeteer/package/src/bidi/DeviceRequestPrompt.ts +138 -0
  407. package/front_end/third_party/puppeteer/package/src/bidi/Frame.ts +7 -3
  408. package/front_end/third_party/puppeteer/package/src/bidi/Page.ts +10 -2
  409. package/front_end/third_party/puppeteer/package/src/bidi/core/BrowsingContext.ts +30 -2
  410. package/front_end/third_party/puppeteer/package/src/bidi/util.ts +8 -0
  411. package/front_end/third_party/puppeteer/package/src/cdp/Accessibility.ts +1 -1
  412. package/front_end/third_party/puppeteer/package/src/cdp/BluetoothEmulation.ts +47 -0
  413. package/front_end/third_party/puppeteer/package/src/cdp/Browser.ts +33 -3
  414. package/front_end/third_party/puppeteer/package/src/cdp/CdpSession.ts +1 -1
  415. package/front_end/third_party/puppeteer/package/src/cdp/DeviceRequestPrompt.ts +3 -8
  416. package/front_end/third_party/puppeteer/package/src/cdp/Frame.ts +2 -2
  417. package/front_end/third_party/puppeteer/package/src/cdp/FrameManager.ts +9 -4
  418. package/front_end/third_party/puppeteer/package/src/cdp/Page.ts +13 -0
  419. package/front_end/third_party/puppeteer/package/src/cdp/WebWorker.ts +8 -3
  420. package/front_end/third_party/puppeteer/package/src/cdp/cdp.ts +1 -0
  421. package/front_end/third_party/puppeteer/package/src/common/BrowserConnector.ts +45 -1
  422. package/front_end/third_party/puppeteer/package/src/common/ConnectOptions.ts +20 -0
  423. package/front_end/third_party/puppeteer/package/src/node/ChromeLauncher.ts +5 -17
  424. package/front_end/third_party/puppeteer/package/src/node/LaunchOptions.ts +23 -7
  425. package/front_end/third_party/puppeteer/package/src/revisions.ts +3 -3
  426. package/front_end/third_party/puppeteer/package/src/util/version.ts +1 -1
  427. package/front_end/third_party/puppeteer/puppeteer-tsconfig.json +4 -0
  428. package/front_end/ui/components/annotations/AnnotationRepository.ts +153 -13
  429. package/front_end/ui/components/settings/SettingCheckbox.ts +4 -6
  430. package/front_end/ui/components/snackbars/Snackbars.docs.ts +0 -1
  431. package/front_end/ui/components/text_editor/AiCodeCompletionTeaserPlaceholder.ts +12 -3
  432. package/front_end/ui/components/text_editor/AiCodeGenerationProvider.ts +182 -0
  433. package/front_end/ui/components/text_editor/text_editor.ts +1 -0
  434. package/front_end/ui/helpers/OpenInNewTab.ts +5 -1
  435. package/front_end/ui/i18n/i18n.ts +9 -9
  436. package/front_end/ui/kit/icons/Icon.docs.ts +22 -65
  437. package/front_end/ui/kit/kit.ts +1 -0
  438. package/front_end/ui/kit/link/Link.docs.ts +15 -0
  439. package/front_end/ui/kit/link/Link.ts +151 -0
  440. package/front_end/ui/kit/link/link.css +27 -0
  441. package/front_end/ui/legacy/EmptyWidget.ts +6 -0
  442. package/front_end/ui/legacy/Floaty.ts +442 -0
  443. package/front_end/ui/legacy/InspectorView.ts +12 -0
  444. package/front_end/ui/legacy/ReportView.ts +1 -1
  445. package/front_end/ui/legacy/SelectMenu.docs.ts +0 -1
  446. package/front_end/ui/legacy/TabbedPane.ts +150 -65
  447. package/front_end/ui/legacy/Widget.ts +1 -3
  448. package/front_end/ui/legacy/components/cookie_table/CookiesTable.ts +1 -1
  449. package/front_end/ui/legacy/components/inline_editor/ColorSwatch.ts +36 -36
  450. package/front_end/ui/legacy/components/object_ui/JavaScriptREPL.ts +22 -37
  451. package/front_end/ui/legacy/components/object_ui/RemoteObjectPreviewFormatter.ts +31 -1
  452. package/front_end/ui/legacy/components/perf_ui/FlameChart.ts +2 -2
  453. package/front_end/ui/legacy/components/utils/Linkifier.ts +7 -11
  454. package/front_end/ui/legacy/floaty.css +77 -0
  455. package/front_end/ui/legacy/legacy.ts +2 -0
  456. package/front_end/ui/legacy/tabbedPane.css +4 -7
  457. package/front_end/ui/visual_logging/KnownContextValues.ts +7 -0
  458. package/inspector_overlay/loadCSS.rollup.js +5 -4
  459. package/package.json +2 -2
  460. package/front_end/panels/application/preloading/components/PreloadingMismatchedHeadersGrid.ts +0 -99
  461. package/front_end/panels/recorder/components/SelectButton.ts +0 -304
  462. package/front_end/panels/recorder/controllers/controllers.ts +0 -7
  463. package/front_end/ui/components/chrome_link/ChromeLink.ts +0 -76
  464. package/front_end/ui/components/chrome_link/chromeLink.css +0 -12
  465. package/front_end/ui/components/chrome_link/chrome_link.ts +0 -9
  466. /package/front_end/panels/recorder/components/{selectButton.css → replaySection.css} +0 -0
@@ -1,26 +1,40 @@
1
1
  // Copyright 2016 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-imperative-dom-api */
4
+ /* eslint-disable @devtools/no-imperative-dom-api, @devtools/no-lit-render-outside-of-view */
5
+
6
+ import '../../ui/kit/kit.js';
7
+ import '../../ui/legacy/components/inline_editor/inline_editor.js';
5
8
 
6
9
  import * as Common from '../../core/common/common.js';
7
10
  import * as Host from '../../core/host/host.js';
8
11
  import * as i18n from '../../core/i18n/i18n.js';
9
- import type * as Platform from '../../core/platform/platform.js';
12
+ import * as Platform from '../../core/platform/platform.js';
10
13
  import * as SDK from '../../core/sdk/sdk.js';
11
14
  import type * as Protocol from '../../generated/protocol.js';
12
15
  import * as Buttons from '../../ui/components/buttons/buttons.js';
13
- import * as uiI18n from '../../ui/i18n/i18n.js';
14
- import {createIcon} from '../../ui/kit/kit.js';
15
- import * as InlineEditor from '../../ui/legacy/components/inline_editor/inline_editor.js';
16
16
  import * as Components from '../../ui/legacy/components/utils/utils.js';
17
17
  import * as UI from '../../ui/legacy/legacy.js';
18
+ import {html, i18nTemplate, type LitTemplate, nothing, render} from '../../ui/lit/lit.js';
18
19
  import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
19
20
 
20
21
  import appManifestViewStyles from './appManifestView.css.js';
21
22
  import * as ApplicationComponents from './components/components.js';
22
23
 
23
24
  const UIStrings = {
25
+ /**
26
+ * @description Text in App Manifest View of the Application panel
27
+ */
28
+ noManifestDetected: 'No manifest detected',
29
+ /**
30
+ * @description Description text on manifests in App Manifest View of the Application panel which describes the app manifest view tab
31
+ */
32
+ manifestDescription:
33
+ 'A manifest defines how your app appears on phone’s home screens and what the app looks like on launch.',
34
+ /**
35
+ * @description Text in App Manifest View of the Application panel
36
+ */
37
+ appManifest: 'Manifest',
24
38
  /**
25
39
  * @description Text in App Manifest View of the Application panel
26
40
  */
@@ -454,6 +468,655 @@ interface Screenshot {
454
468
  platform?: string;
455
469
  }
456
470
 
471
+ /* eslint-disable @typescript-eslint/naming-convention */
472
+ interface Manifest {
473
+ background_color?: string;
474
+ description?: string;
475
+ display?: string;
476
+ display_override?: string[];
477
+ icons?: Array<{
478
+ src: string,
479
+ sizes?: string,
480
+ type?: string,
481
+ purpose?: string,
482
+ }>;
483
+ id?: string;
484
+ name?: string;
485
+ note_taking?: {
486
+ new_note_url?: string,
487
+ };
488
+ orientation?: string;
489
+ protocol_handlers?: Protocol.Page.ProtocolHandler[];
490
+ screenshots?: Screenshot[];
491
+ short_name?: string;
492
+ shortcuts?: Array<{
493
+ name: string,
494
+ url: string,
495
+ description?: string,
496
+ short_name?: string,
497
+ icons?: Array<{
498
+ src: string,
499
+ sizes?: string,
500
+ type?: string,
501
+ purpose?: string,
502
+ }>,
503
+ }>;
504
+ start_url?: string;
505
+ theme_color?: string;
506
+ }
507
+ /* eslint-enable @typescript-eslint/naming-convention */
508
+
509
+ interface ReportSectionItem {
510
+ content: LitTemplate|LitTemplate[]|string|HTMLElement;
511
+ title?: string;
512
+ label?: string;
513
+ flexed?: boolean;
514
+ }
515
+
516
+ interface IdentitySectionData {
517
+ name: string;
518
+ shortName: string;
519
+ description: string;
520
+ appId: string|null;
521
+ recommendedId: string|null;
522
+ hasId: boolean;
523
+ warnings: Platform.UIString.LocalizedString[];
524
+ }
525
+
526
+ interface PresentationSectionData {
527
+ startUrl: string;
528
+ completeStartUrl: Platform.DevToolsPath.UrlString|null;
529
+ themeColor: Common.Color.Color|null;
530
+ backgroundColor: Common.Color.Color|null;
531
+ orientation: string;
532
+ display: string;
533
+ newNoteUrl?: string;
534
+ hasNewNoteUrl: boolean;
535
+ completeNewNoteUrl: Platform.DevToolsPath.UrlString|null;
536
+ }
537
+
538
+ interface ProtocolHandlersSectionData {
539
+ protocolHandlers: Protocol.Page.ProtocolHandler[];
540
+ manifestLink: Platform.DevToolsPath.UrlString;
541
+ }
542
+
543
+ interface IconsSectionData {
544
+ icons: Map<string, ProcessedImageResource[]>;
545
+ imageResourceErrors: Platform.UIString.LocalizedString[];
546
+ }
547
+
548
+ interface ProcessedShortcut {
549
+ name: string;
550
+ shortName?: string;
551
+ description?: string;
552
+ url: string;
553
+ shortcutUrl: Platform.DevToolsPath.UrlString;
554
+ icons: Map<string, ProcessedImageResource[]>;
555
+ }
556
+
557
+ interface ShortcutsSectionData {
558
+ shortcuts: ProcessedShortcut[];
559
+ warnings: Platform.UIString.LocalizedString[];
560
+ imageResourceErrors: Platform.UIString.LocalizedString[];
561
+ }
562
+
563
+ interface ProcessedScreenshot {
564
+ screenshot: Screenshot;
565
+ processedImage: ProcessedImageResource;
566
+ }
567
+
568
+ interface ScreenshotsSectionData {
569
+ screenshots: ProcessedScreenshot[];
570
+ warnings: Platform.UIString.LocalizedString[];
571
+ imageResourceErrors: Platform.UIString.LocalizedString[];
572
+ }
573
+
574
+ interface WindowControlsSectionData {
575
+ hasWco: boolean;
576
+ themeColor: string;
577
+ wcoStyleSheetText: boolean;
578
+ url: Platform.DevToolsPath.UrlString;
579
+ }
580
+
581
+ type ProcessedImageResource = {
582
+ imageResourceErrors: Platform.UIString.LocalizedString[],
583
+ imageUrl?: string,
584
+ squareSizedIconAvailable?: boolean,
585
+ }|{
586
+ imageResourceErrors: Platform.UIString.LocalizedString[],
587
+ imageUrl: string,
588
+ squareSizedIconAvailable: boolean,
589
+ title: string,
590
+ naturalWidth: number,
591
+ naturalHeight: number,
592
+ imageSrc: string,
593
+ };
594
+
595
+ function renderErrors(
596
+ errorsSection: UI.ReportView.Section, warnings?: Platform.UIString.LocalizedString[],
597
+ manifestErrors?: Protocol.Page.AppManifestError[], imageErrors?: Platform.UIString.LocalizedString[]): void {
598
+ errorsSection.clearContent();
599
+ errorsSection.element.classList.toggle(
600
+ 'hidden', !manifestErrors?.length && !warnings?.length && !imageErrors?.length);
601
+
602
+ for (const error of manifestErrors ?? []) {
603
+ const icon = UI.UIUtils.createIconLabel({
604
+ title: error.message,
605
+ iconName: error.critical ? 'cross-circle-filled' : 'warning-filled',
606
+ color: error.critical ? 'var(--icon-error)' : 'var(--icon-warning)',
607
+ });
608
+ errorsSection.appendRow().appendChild(icon);
609
+ }
610
+
611
+ for (const warning of warnings ?? []) {
612
+ const msgElement = document.createTextNode(warning);
613
+ errorsSection.appendRow().appendChild(msgElement);
614
+ }
615
+ for (const error of imageErrors ?? []) {
616
+ const msgElement = document.createTextNode(error);
617
+ errorsSection.appendRow().appendChild(msgElement);
618
+ }
619
+ }
620
+
621
+ function renderIdentity(identitySection: UI.ReportView.Section, identityData: IdentitySectionData): void {
622
+ const {name, shortName, description, appId, recommendedId, hasId} = identityData;
623
+ const fields: ReportSectionItem[] = [];
624
+ fields.push({title: i18nString(UIStrings.name), content: name});
625
+ fields.push({title: i18nString(UIStrings.shortName), content: shortName});
626
+ fields.push({title: i18nString(UIStrings.description), content: description});
627
+
628
+ if (appId && recommendedId) {
629
+ const onCopy = (): void => {
630
+ UI.ARIAUtils.LiveAnnouncer.alert(i18nString(UIStrings.copiedToClipboard, {PH1: recommendedId}));
631
+ Host.InspectorFrontendHost.InspectorFrontendHostInstance.copyText(recommendedId);
632
+ };
633
+ // clang-format off
634
+ fields.push({title: i18nString(UIStrings.computedAppId), label: 'App Id', content: html`
635
+ ${appId}
636
+ <devtools-icon class="inline-icon" name="help" title=${i18nString(UIStrings.appIdExplainer)}
637
+ jslog=${VisualLogging.action('help').track({hover: true})}>
638
+ </devtools-icon>
639
+ <devtools-link href="https://developer.chrome.com/blog/pwa-manifest-id/"
640
+ .jslogContext=${'learn-more'}>
641
+ ${i18nString(UIStrings.learnMore)}
642
+ </devtools-link>
643
+ ${!hasId ? html`
644
+ <div class="multiline-value">
645
+ ${i18nTemplate(str_, UIStrings.appIdNote, {
646
+ PH1: html`<code>${recommendedId}</code>`,
647
+ PH2: html`<devtools-button class="inline-button" @click=${onCopy}
648
+ .iconName=${'copy'}
649
+ .variant=${Buttons.Button.Variant.ICON}
650
+ .size=${Buttons.Button.Size.SMALL}
651
+ .jslogContext=${'manifest.copy-id'}
652
+ .title=${i18nString(UIStrings.copyToClipboard)}>
653
+ </devtools-button>`,
654
+ })}
655
+ </div>` : nothing}`});
656
+ // clang-format on
657
+ } else {
658
+ identitySection.removeField(i18nString(UIStrings.computedAppId));
659
+ }
660
+ setSectionContents(fields, identitySection);
661
+ }
662
+
663
+ function renderPresentation(
664
+ presentationSection: UI.ReportView.Section, presentationData: PresentationSectionData): void {
665
+ const {
666
+ startUrl,
667
+ completeStartUrl,
668
+ themeColor,
669
+ backgroundColor,
670
+ orientation,
671
+ display,
672
+ newNoteUrl,
673
+ hasNewNoteUrl,
674
+ completeNewNoteUrl,
675
+ } = presentationData;
676
+ const fields: ReportSectionItem[] = [
677
+ {
678
+ title: i18nString(UIStrings.startUrl),
679
+ label: i18nString(UIStrings.startUrl),
680
+ content: completeStartUrl ? Components.Linkifier.Linkifier.linkifyURL(
681
+ completeStartUrl, ({text: startUrl, tabStop: true, jslogContext: 'start-url'})) :
682
+ nothing,
683
+ },
684
+ {
685
+ title: i18nString(UIStrings.themeColor),
686
+ content: themeColor ? html`<devtools-color-swatch .color=${themeColor}></devtools-color-swatch>` : nothing,
687
+ },
688
+ {
689
+ title: i18nString(UIStrings.backgroundColor),
690
+ content: backgroundColor ? html`<devtools-color-swatch .color=${backgroundColor}></devtools-color-swatch>` :
691
+ nothing,
692
+ },
693
+ {title: i18nString(UIStrings.orientation), content: orientation},
694
+ {title: i18nString(UIStrings.display), content: display},
695
+ ];
696
+ if (completeNewNoteUrl) {
697
+ fields.push({
698
+ title: i18nString(UIStrings.newNoteUrl),
699
+ content: hasNewNoteUrl ?
700
+ Components.Linkifier.Linkifier.linkifyURL(completeNewNoteUrl, ({text: newNoteUrl, tabStop: true})) :
701
+ nothing,
702
+ });
703
+ }
704
+ setSectionContents(fields, presentationSection);
705
+ }
706
+
707
+ function renderProtocolHandlers(
708
+ protocolHandlersView: ApplicationComponents.ProtocolHandlersView.ProtocolHandlersView,
709
+ data: ProtocolHandlersSectionData): void {
710
+ protocolHandlersView.protocolHandlers = data.protocolHandlers;
711
+ protocolHandlersView.manifestLink = data.manifestLink;
712
+ }
713
+
714
+ function renderImage(imageSrc: string, imageUrl: string, naturalWidth: number): LitTemplate {
715
+ // clang-format off
716
+ return html`
717
+ <div class="image-wrapper">
718
+ <img src=${imageSrc} alt=${i18nString(UIStrings.imageFromS, {PH1: imageUrl})}
719
+ width=${naturalWidth}>
720
+ </div>`;
721
+ // clang-format on
722
+ }
723
+
724
+ function renderIcons(iconsSection: UI.ReportView.Section, data: IconsSectionData): void {
725
+ iconsSection.clearContent();
726
+
727
+ const contents: ReportSectionItem[] = [
728
+ // clang-format off
729
+ {
730
+ content: html`<devtools-checkbox class="mask-checkbox"
731
+ jslog=${VisualLogging.toggle('show-minimal-safe-area-for-maskable-icons')
732
+ .track({change: true})}
733
+ @click=${(event: Event) => {
734
+ iconsSection.setIconMasked((event.target as HTMLInputElement).checked);
735
+ }}>
736
+ ${i18nString(UIStrings.showOnlyTheMinimumSafeAreaFor)}
737
+ </devtools-checkbox>`},
738
+ // clang-format on
739
+ {
740
+ content: i18nTemplate(str_, UIStrings.needHelpReadOurS, {
741
+ PH1: html`
742
+ <devtools-link href="https://web.dev/maskable-icon/" .jslogContext=${'learn-more'}>
743
+ ${i18nString(UIStrings.documentationOnMaskableIcons)}
744
+ </devtools-link>`,
745
+ }),
746
+ },
747
+ ];
748
+ for (const [title, images] of data.icons) {
749
+ const content = images.filter(icon => 'imageSrc' in icon)
750
+ .map(icon => renderImage(icon.imageSrc, icon.imageUrl, icon.naturalWidth));
751
+ contents.push({title, content, flexed: true});
752
+ }
753
+ setSectionContents(contents, iconsSection);
754
+ }
755
+
756
+ function renderShortcuts(
757
+ reportView: UI.ReportView.ReportView, shortcutSections: UI.ReportView.Section[], data: ShortcutsSectionData): void {
758
+ for (const shortcutsSection of shortcutSections) {
759
+ shortcutsSection.detach(/** overrideHideOnDetach= */ true);
760
+ }
761
+ shortcutSections.length = 0;
762
+
763
+ let shortcutIndex = 1;
764
+ for (const shortcut of data.shortcuts) {
765
+ const shortcutSection = reportView.appendSection(i18nString(UIStrings.shortcutS, {PH1: shortcutIndex}));
766
+ shortcutSection.element.setAttribute('jslog', `${VisualLogging.section('shortcuts')}`);
767
+ shortcutSections.push(shortcutSection);
768
+
769
+ const fields: ReportSectionItem[] = [
770
+ {title: i18nString(UIStrings.name), flexed: true, content: shortcut.name},
771
+ ];
772
+ if (shortcut.shortName) {
773
+ fields.push({title: i18nString(UIStrings.shortName), flexed: true, content: shortcut.shortName});
774
+ }
775
+ if (shortcut.description) {
776
+ fields.push({title: i18nString(UIStrings.description), flexed: true, content: shortcut.description});
777
+ }
778
+ fields.push({
779
+ title: i18nString(UIStrings.url),
780
+ flexed: true,
781
+ content: Components.Linkifier.Linkifier.linkifyURL(
782
+ shortcut.shortcutUrl, ({text: shortcut.url, tabStop: true, jslogContext: 'shortcut'})),
783
+ });
784
+
785
+ for (const [title, images] of shortcut.icons) {
786
+ const content = images.filter(icon => 'imageSrc' in icon)
787
+ .map(icon => renderImage(icon.imageSrc, icon.imageUrl, icon.naturalWidth));
788
+ fields.push({title, content, flexed: true});
789
+ }
790
+ setSectionContents(fields, shortcutSection);
791
+ shortcutIndex++;
792
+ }
793
+ }
794
+
795
+ function renderScreenshots(
796
+ reportView: UI.ReportView.ReportView, screenshotsSections: UI.ReportView.Section[],
797
+ data: ScreenshotsSectionData): void {
798
+ for (const screenshotSection of screenshotsSections) {
799
+ screenshotSection.detach(/** overrideHideOnDetach= */ true);
800
+ }
801
+ screenshotsSections.length = 0;
802
+
803
+ let screenshotIndex = 1;
804
+ for (const processedScreenshot of data.screenshots) {
805
+ const {screenshot, processedImage} = processedScreenshot;
806
+ const screenshotSection = reportView.appendSection(i18nString(UIStrings.screenshotS, {PH1: screenshotIndex}));
807
+ screenshotsSections.push(screenshotSection);
808
+ const fields: ReportSectionItem[] = [];
809
+
810
+ if (screenshot.form_factor) {
811
+ fields.push({title: i18nString(UIStrings.formFactor), flexed: true, content: screenshot.form_factor});
812
+ }
813
+ if (screenshot.label) {
814
+ fields.push({title: i18nString(UIStrings.label), flexed: true, content: screenshot.label});
815
+ }
816
+ if (screenshot.platform) {
817
+ fields.push({title: i18nString(UIStrings.platform), flexed: true, content: screenshot.platform});
818
+ }
819
+
820
+ if ('imageSrc' in processedImage) {
821
+ const content = renderImage(processedImage.imageSrc, processedImage.imageUrl, processedImage.naturalWidth);
822
+ fields.push({title: processedImage.title, content, flexed: true});
823
+ }
824
+ setSectionContents(fields, screenshotSection);
825
+ screenshotIndex++;
826
+ }
827
+ }
828
+
829
+ function renderInstallability(
830
+ installabilitySection: UI.ReportView.Section, installabilityErrors: Protocol.Page.InstallabilityError[]): void {
831
+ installabilitySection.clearContent();
832
+ installabilitySection.element.classList.toggle('hidden', !installabilityErrors.length);
833
+ const errorMessages = getInstallabilityErrorMessages(installabilityErrors);
834
+ setSectionContents(errorMessages.map(content => ({content})), installabilitySection);
835
+ }
836
+
837
+ function renderWindowControlsSection(
838
+ windowControlsSection: UI.ReportView.Section, data: WindowControlsSectionData, selectedPlatform?: string,
839
+ onSelectOs?: (selectedOS: SDK.OverlayModel.EmulatedOSType) => Promise<void>,
840
+ onToggleWcoToolbar?: (enabled: boolean) => Promise<void>): void {
841
+ const {hasWco, url} = data;
842
+ const contents: ReportSectionItem[] = [];
843
+ if (hasWco) {
844
+ // clang-format off
845
+ contents.push({content: html`
846
+ <devtools-icon class="inline-icon" name="check-circle"></devtools-icon>
847
+ ${i18nTemplate(str_, UIStrings.wcoFound, {
848
+ PH1: html`<code class="wco">window-controls-overlay</code>`,
849
+ PH2: html`<code>
850
+ <devtools-link href="https://developer.mozilla.org/en-US/docs/Web/Manifest/display_override"
851
+ .jslogContext=${'display-override'}>
852
+ display-override
853
+ </devtools-link>
854
+ </code>`,
855
+ PH3: html`${Components.Linkifier.Linkifier.linkifyURL(url)}`,
856
+ })}`});
857
+ // clang-format on
858
+ if (selectedPlatform && onSelectOs && onToggleWcoToolbar) {
859
+ const controls = renderWindowControls(selectedPlatform, onSelectOs, onToggleWcoToolbar);
860
+ contents.push(controls);
861
+ }
862
+ } else {
863
+ // clang-format off
864
+ contents.push({content: html`
865
+ <devtools-icon class="inline-icon" name="info"></devtools-icon>
866
+ ${i18nTemplate(str_, UIStrings.wcoNotFound, {
867
+ PH1: html`<code>
868
+ <devtools-link href="https://developer.mozilla.org/en-US/docs/Web/Manifest/display_override"
869
+ .jslogContext=${'display-override'}>
870
+ display-override
871
+ </devtools-link>
872
+ </code>`})}`});
873
+ // clang-format on
874
+ }
875
+ // clang-format off
876
+ contents.push({content: i18nTemplate(str_, UIStrings.wcoNeedHelpReadMore, { PH1: html`<devtools-link
877
+ href="https://learn.microsoft.com/en-us/microsoft-edge/progressive-web-apps-chromium/how-to/window-controls-overlay"
878
+ .jslogContext=${'customize-pwa-tittle-bar'}>
879
+ ${i18nString(UIStrings.customizePwaTitleBar)}
880
+ </devtools-link>`})});
881
+ // clang-format on
882
+ windowControlsSection.clearContent();
883
+ setSectionContents(contents, windowControlsSection);
884
+ }
885
+
886
+ function getInstallabilityErrorMessages(installabilityErrors: Protocol.Page.InstallabilityError[]): string[] {
887
+ const errorMessages = [];
888
+ for (const installabilityError of installabilityErrors) {
889
+ let errorMessage;
890
+ switch (installabilityError.errorId) {
891
+ case 'not-in-main-frame':
892
+ errorMessage = i18nString(UIStrings.pageIsNotLoadedInTheMainFrame);
893
+ break;
894
+ case 'not-from-secure-origin':
895
+ errorMessage = i18nString(UIStrings.pageIsNotServedFromASecureOrigin);
896
+ break;
897
+ case 'no-manifest':
898
+ errorMessage = i18nString(UIStrings.pageHasNoManifestLinkUrl);
899
+ break;
900
+ case 'manifest-empty':
901
+ errorMessage = i18nString(UIStrings.manifestCouldNotBeFetchedIsEmpty);
902
+ break;
903
+ case 'start-url-not-valid':
904
+ errorMessage = i18nString(UIStrings.manifestStartUrlIsNotValid);
905
+ break;
906
+ case 'manifest-missing-name-or-short-name':
907
+ errorMessage = i18nString(UIStrings.manifestDoesNotContainANameOr);
908
+ break;
909
+ case 'manifest-display-not-supported':
910
+ errorMessage = i18nString(UIStrings.manifestDisplayPropertyMustBeOne);
911
+ break;
912
+ case 'manifest-missing-suitable-icon':
913
+ if (installabilityError.errorArguments.length !== 1 ||
914
+ installabilityError.errorArguments[0].name !== 'minimum-icon-size-in-pixels') {
915
+ console.error('Installability error does not have the correct errorArguments');
916
+ break;
917
+ }
918
+ errorMessage =
919
+ i18nString(UIStrings.manifestDoesNotContainASuitable, {PH1: installabilityError.errorArguments[0].value});
920
+ break;
921
+ case 'no-acceptable-icon':
922
+ if (installabilityError.errorArguments.length !== 1 ||
923
+ installabilityError.errorArguments[0].name !== 'minimum-icon-size-in-pixels') {
924
+ console.error('Installability error does not have the correct errorArguments');
925
+ break;
926
+ }
927
+ errorMessage =
928
+ i18nString(UIStrings.noSuppliedIconIsAtLeastSpxSquare, {PH1: installabilityError.errorArguments[0].value});
929
+ break;
930
+ case 'cannot-download-icon':
931
+ errorMessage = i18nString(UIStrings.couldNotDownloadARequiredIcon);
932
+ break;
933
+ case 'no-icon-available':
934
+ errorMessage = i18nString(UIStrings.downloadedIconWasEmptyOr);
935
+ break;
936
+ case 'platform-not-supported-on-android':
937
+ errorMessage = i18nString(UIStrings.theSpecifiedApplicationPlatform);
938
+ break;
939
+ case 'no-id-specified':
940
+ errorMessage = i18nString(UIStrings.noPlayStoreIdProvided);
941
+ break;
942
+ case 'ids-do-not-match':
943
+ errorMessage = i18nString(UIStrings.thePlayStoreAppUrlAndPlayStoreId);
944
+ break;
945
+ case 'already-installed':
946
+ errorMessage = i18nString(UIStrings.theAppIsAlreadyInstalled);
947
+ break;
948
+ case 'url-not-supported-for-webapk':
949
+ errorMessage = i18nString(UIStrings.aUrlInTheManifestContainsA);
950
+ break;
951
+ case 'in-incognito':
952
+ errorMessage = i18nString(UIStrings.pageIsLoadedInAnIncognitoWindow);
953
+ break;
954
+ case 'not-offline-capable':
955
+ errorMessage = i18nString(UIStrings.pageDoesNotWorkOffline);
956
+ break;
957
+ case 'no-url-for-service-worker':
958
+ errorMessage = i18nString(UIStrings.couldNotCheckServiceWorker);
959
+ break;
960
+ case 'prefer-related-applications':
961
+ errorMessage = i18nString(UIStrings.manifestSpecifies);
962
+ break;
963
+ case 'prefer-related-applications-only-beta-stable':
964
+ errorMessage = i18nString(UIStrings.preferrelatedapplicationsIsOnly);
965
+ break;
966
+ case 'manifest-display-override-not-supported':
967
+ errorMessage = i18nString(UIStrings.manifestContainsDisplayoverride);
968
+ break;
969
+ case 'warn-not-offline-capable':
970
+ errorMessage = i18nString(
971
+ UIStrings.pageDoesNotWorkOfflineThePage,
972
+ {PH1: 'https://developer.chrome.com/blog/improved-pwa-offline-detection/'});
973
+ break;
974
+ default:
975
+ console.error(`Installability error id '${installabilityError.errorId}' is not recognized`);
976
+ break;
977
+ }
978
+ if (errorMessage) {
979
+ errorMessages.push(errorMessage);
980
+ }
981
+ }
982
+ return errorMessages;
983
+ }
984
+
985
+ function renderWindowControls(
986
+ selectedPlatform: string, onSelectOs: (selectedOS: SDK.OverlayModel.EmulatedOSType) => Promise<void>,
987
+ onToggleWcoToolbar: (enabled: boolean) => Promise<void>): ReportSectionItem {
988
+ // clang-format off
989
+ return {content: html`
990
+ <devtools-checkbox @click=${(event: Event) => onToggleWcoToolbar((event.target as HTMLInputElement).checked)}
991
+ title=${i18nString(UIStrings.selectWindowControlsOverlayEmulationOs)}>
992
+ ${i18nString(UIStrings.selectWindowControlsOverlayEmulationOs)}
993
+ </devtools-checkbox>
994
+ <select value=${selectedPlatform}
995
+ @change=${(event: Event): void => {
996
+ const target = event.target as HTMLSelectElement;
997
+ const selectedOS = target.options[target.selectedIndex].value;
998
+ void onSelectOs(selectedOS as SDK.OverlayModel.EmulatedOSType);
999
+ }}
1000
+ .selectedIndex=${0}>
1001
+ <option value=${SDK.OverlayModel.EmulatedOSType.WINDOWS}
1002
+ jslog=${VisualLogging.item('windows').track({click: true})}>
1003
+ Windows
1004
+ </option>
1005
+ <option value=${SDK.OverlayModel.EmulatedOSType.MAC}
1006
+ jslog=${VisualLogging.item('macos').track({click: true })}>
1007
+ macOS
1008
+ </option>
1009
+ <option value=${SDK.OverlayModel.EmulatedOSType.LINUX}
1010
+ jslog=${VisualLogging.item('linux').track({click: true})}>
1011
+ Linux
1012
+ </option>
1013
+ </select>`};
1014
+ // clang-format on
1015
+ }
1016
+
1017
+ function setSectionContents(items: ReportSectionItem[], section: UI.ReportView.Section): void {
1018
+ for (const item of items) {
1019
+ if (!item.title) {
1020
+ render(item.content, section.appendRow());
1021
+ continue;
1022
+ }
1023
+ const element = item.flexed ? section.appendFlexedField(item.title) : section.appendField(item.title);
1024
+ if (item.label) {
1025
+ UI.ARIAUtils.setLabel(element, item.label);
1026
+ }
1027
+ render(item.content, element);
1028
+ }
1029
+ }
1030
+
1031
+ interface ViewInput {
1032
+ emptyView: UI.EmptyWidget.EmptyWidget;
1033
+ reportView: UI.ReportView.ReportView;
1034
+ errorsSection?: UI.ReportView.Section;
1035
+ installabilitySection?: UI.ReportView.Section;
1036
+ identitySection?: UI.ReportView.Section;
1037
+ presentationSection?: UI.ReportView.Section;
1038
+ protocolHandlersView?: ApplicationComponents.ProtocolHandlersView.ProtocolHandlersView;
1039
+ iconsSection?: UI.ReportView.Section;
1040
+ windowControlsSection?: UI.ReportView.Section;
1041
+ shortcutSections?: UI.ReportView.Section[];
1042
+ screenshotsSections?: UI.ReportView.Section[];
1043
+ parsedManifest?: Manifest;
1044
+ url?: Platform.DevToolsPath.UrlString;
1045
+ identityData?: IdentitySectionData;
1046
+ presentationData?: PresentationSectionData;
1047
+ protocolHandlersData?: ProtocolHandlersSectionData;
1048
+ iconsData?: IconsSectionData;
1049
+ shortcutsData?: ShortcutsSectionData;
1050
+ screenshotsData?: ScreenshotsSectionData;
1051
+ installabilityErrors?: Protocol.Page.InstallabilityError[];
1052
+ warnings?: Platform.UIString.LocalizedString[];
1053
+ errors?: Protocol.Page.AppManifestError[];
1054
+ imageErrors?: Platform.UIString.LocalizedString[];
1055
+ windowControlsData?: WindowControlsSectionData;
1056
+ selectedPlatform?: string;
1057
+ onSelectOs?: (selectedOS: SDK.OverlayModel.EmulatedOSType) => Promise<void>;
1058
+ onToggleWcoToolbar?: (enabled: boolean) => Promise<void>;
1059
+ }
1060
+
1061
+ type View = (input: ViewInput, output: undefined, target: HTMLElement) => void;
1062
+
1063
+ export const DEFAULT_VIEW: View = (input, _output, _target) => {
1064
+ const {
1065
+ reportView,
1066
+ errorsSection,
1067
+ installabilitySection,
1068
+ identitySection,
1069
+ presentationSection,
1070
+ protocolHandlersView,
1071
+ iconsSection,
1072
+ windowControlsSection,
1073
+ shortcutSections,
1074
+ screenshotsSections,
1075
+ identityData,
1076
+ presentationData,
1077
+ protocolHandlersData,
1078
+ iconsData,
1079
+ shortcutsData,
1080
+ screenshotsData,
1081
+ installabilityErrors,
1082
+ warnings,
1083
+ errors,
1084
+ imageErrors,
1085
+ windowControlsData,
1086
+ selectedPlatform,
1087
+ onSelectOs,
1088
+ onToggleWcoToolbar,
1089
+ } = input;
1090
+ if (identitySection && identityData) {
1091
+ renderIdentity(identitySection, identityData);
1092
+ }
1093
+ if (presentationSection && presentationData) {
1094
+ renderPresentation(presentationSection, presentationData);
1095
+ }
1096
+ if (protocolHandlersView && protocolHandlersData) {
1097
+ renderProtocolHandlers(protocolHandlersView, protocolHandlersData);
1098
+ }
1099
+ if (iconsSection && iconsData) {
1100
+ renderIcons(iconsSection, iconsData);
1101
+ }
1102
+ if (shortcutSections && shortcutsData) {
1103
+ renderShortcuts(reportView, shortcutSections, shortcutsData);
1104
+ }
1105
+ if (screenshotsSections && screenshotsData) {
1106
+ renderScreenshots(reportView, screenshotsSections, screenshotsData);
1107
+ }
1108
+ if (installabilitySection && installabilityErrors) {
1109
+ renderInstallability(installabilitySection, installabilityErrors);
1110
+ }
1111
+ if (windowControlsSection && windowControlsData) {
1112
+ renderWindowControlsSection(
1113
+ windowControlsSection, windowControlsData, selectedPlatform, onSelectOs, onToggleWcoToolbar);
1114
+ }
1115
+ if (errorsSection) {
1116
+ renderErrors(errorsSection, warnings, errors, imageErrors);
1117
+ }
1118
+ };
1119
+
457
1120
  export class AppManifestView extends Common.ObjectWrapper.eventMixin<EventTypes, typeof UI.Widget.VBox>(UI.Widget.VBox)
458
1121
  implements SDK.TargetManager.Observer {
459
1122
  private readonly emptyView: UI.EmptyWidget.EmptyWidget;
@@ -467,41 +1130,38 @@ export class AppManifestView extends Common.ObjectWrapper.eventMixin<EventTypes,
467
1130
  private readonly protocolHandlersSection: UI.ReportView.Section;
468
1131
  private readonly shortcutSections: UI.ReportView.Section[];
469
1132
  private readonly screenshotsSections: UI.ReportView.Section[];
470
- private nameField: HTMLElement;
471
- private shortNameField: HTMLElement;
472
- private descriptionField: Element;
473
- private readonly startURLField: HTMLElement;
474
- private readonly themeColorSwatch: InlineEditor.ColorSwatch.ColorSwatch;
475
- private readonly backgroundColorSwatch: InlineEditor.ColorSwatch.ColorSwatch;
476
- private orientationField: HTMLElement;
477
- private displayField: HTMLElement;
478
- private readonly newNoteUrlField: HTMLElement;
479
- private readonly throttler: Common.Throttler.Throttler;
480
1133
  private registeredListeners: Common.EventTarget.EventDescriptor[];
481
1134
  private target?: SDK.Target.Target;
482
1135
  private resourceTreeModel?: SDK.ResourceTreeModel.ResourceTreeModel|null;
483
1136
  private serviceWorkerManager?: SDK.ServiceWorkerManager.ServiceWorkerManager|null;
484
1137
  private overlayModel?: SDK.OverlayModel.OverlayModel|null;
485
1138
  private protocolHandlersView: ApplicationComponents.ProtocolHandlersView.ProtocolHandlersView;
486
-
487
- constructor(
488
- emptyView: UI.EmptyWidget.EmptyWidget, reportView: UI.ReportView.ReportView,
489
- throttler: Common.Throttler.Throttler) {
1139
+ private manifestUrl: Platform.DevToolsPath.UrlString;
1140
+ private manifestData: string|null;
1141
+ private manifestErrors: Protocol.Page.AppManifestError[];
1142
+ private installabilityErrors: Protocol.Page.InstallabilityError[];
1143
+ private appIdResponse: Protocol.Page.GetAppIdResponse|null;
1144
+ private wcoToolbarEnabled = false;
1145
+ private readonly view: View;
1146
+
1147
+ constructor(view: View = DEFAULT_VIEW) {
490
1148
  super({
491
1149
  jslog: `${VisualLogging.pane('manifest')}`,
492
1150
  useShadowDom: true,
493
1151
  });
1152
+ this.view = view;
494
1153
  this.registerRequiredCSS(appManifestViewStyles);
495
1154
 
496
1155
  this.contentElement.classList.add('manifest-container');
497
1156
 
498
- this.emptyView = emptyView;
1157
+ this.emptyView = new UI.EmptyWidget.EmptyWidget(
1158
+ i18nString(UIStrings.noManifestDetected), i18nString(UIStrings.manifestDescription));
499
1159
  this.emptyView.link = 'https://web.dev/add-manifest/' as Platform.DevToolsPath.UrlString;
500
1160
 
501
1161
  this.emptyView.show(this.contentElement);
502
1162
  this.emptyView.hideWidget();
503
1163
 
504
- this.reportView = reportView;
1164
+ this.reportView = new UI.ReportView.ReportView(i18nString(UIStrings.appManifest));
505
1165
  this.reportView.registerRequiredCSS(appManifestViewStyles);
506
1166
  this.reportView.element.classList.add('manifest-view-header');
507
1167
  this.reportView.show(this.contentElement);
@@ -524,29 +1184,14 @@ export class AppManifestView extends Common.ObjectWrapper.eventMixin<EventTypes,
524
1184
  this.shortcutSections = [];
525
1185
  this.screenshotsSections = [];
526
1186
 
527
- this.nameField = this.identitySection.appendField(i18nString(UIStrings.name));
528
- this.shortNameField = this.identitySection.appendField(i18nString(UIStrings.shortName));
529
- this.descriptionField = this.identitySection.appendFlexedField(i18nString(UIStrings.description));
530
-
531
- this.startURLField = this.presentationSection.appendField(i18nString(UIStrings.startUrl));
532
- UI.ARIAUtils.setLabel(this.startURLField, i18nString(UIStrings.startUrl));
533
-
534
- const themeColorField = this.presentationSection.appendField(i18nString(UIStrings.themeColor));
535
- this.themeColorSwatch = new InlineEditor.ColorSwatch.ColorSwatch();
536
- themeColorField.appendChild(this.themeColorSwatch);
537
-
538
- const backgroundColorField = this.presentationSection.appendField(i18nString(UIStrings.backgroundColor));
539
- this.backgroundColorSwatch = new InlineEditor.ColorSwatch.ColorSwatch();
540
- backgroundColorField.appendChild(this.backgroundColorSwatch);
541
-
542
- this.orientationField = this.presentationSection.appendField(i18nString(UIStrings.orientation));
543
- this.displayField = this.presentationSection.appendField(i18nString(UIStrings.display));
544
-
545
- this.newNoteUrlField = this.presentationSection.appendField(i18nString(UIStrings.newNoteUrl));
546
-
547
- this.throttler = throttler;
548
1187
  SDK.TargetManager.TargetManager.instance().observeTargets(this);
549
1188
  this.registeredListeners = [];
1189
+
1190
+ this.manifestUrl = Platform.DevToolsPath.EmptyUrlString;
1191
+ this.manifestData = null;
1192
+ this.manifestErrors = [];
1193
+ this.installabilityErrors = [];
1194
+ this.appIdResponse = null;
550
1195
  }
551
1196
 
552
1197
  getStaticSections(): UI.ReportView.Section[] {
@@ -614,20 +1259,32 @@ export class AppManifestView extends Common.ObjectWrapper.eventMixin<EventTypes,
614
1259
  this.resourceTreeModel.getAppId(),
615
1260
  ]);
616
1261
 
617
- void this.throttler.schedule(
618
- () => this.renderManifest(url, data, errors, installabilityErrors, appId),
619
- immediately ? Common.Throttler.Scheduling.AS_SOON_AS_POSSIBLE : Common.Throttler.Scheduling.DEFAULT);
1262
+ this.manifestUrl = url;
1263
+ this.manifestData = data;
1264
+ this.manifestErrors = errors;
1265
+ this.installabilityErrors = installabilityErrors;
1266
+ this.appIdResponse = appId;
1267
+
1268
+ if (immediately) {
1269
+ await this.performUpdate();
1270
+ } else {
1271
+ await this.requestUpdate();
1272
+ }
620
1273
  }
621
1274
 
622
- private async renderManifest(
623
- url: Platform.DevToolsPath.UrlString, data: string|null, errors: Protocol.Page.AppManifestError[],
624
- installabilityErrors: Protocol.Page.InstallabilityError[],
625
- appIdResponse: Protocol.Page.GetAppIdResponse): Promise<void> {
1275
+ override async performUpdate(): Promise<void> {
1276
+ const url = this.manifestUrl;
1277
+ let data = this.manifestData;
1278
+ const errors = this.manifestErrors;
1279
+ const installabilityErrors = this.installabilityErrors;
1280
+ const appIdResponse = this.appIdResponse;
1281
+
626
1282
  const appId = appIdResponse?.appId || null;
627
1283
  const recommendedId = appIdResponse?.recommendedId || null;
628
1284
  if ((!data || data === '{}') && !errors.length) {
629
1285
  this.emptyView.showWidget();
630
1286
  this.reportView.hideWidget();
1287
+ this.view({emptyView: this.emptyView, reportView: this.reportView}, undefined, this.contentElement);
631
1288
  this.dispatchEventToListeners(Events.MANIFEST_DETECTED, false);
632
1289
  return;
633
1290
  }
@@ -635,21 +1292,13 @@ export class AppManifestView extends Common.ObjectWrapper.eventMixin<EventTypes,
635
1292
  this.reportView.showWidget();
636
1293
  this.dispatchEventToListeners(Events.MANIFEST_DETECTED, true);
637
1294
 
638
- const link = Components.Linkifier.Linkifier.linkifyURL(url);
639
- link.tabIndex = 0;
1295
+ const link = Components.Linkifier.Linkifier.linkifyURL(url, {tabStop: true});
640
1296
  this.reportView.setURL(link);
641
- this.errorsSection.clearContent();
642
- this.errorsSection.element.classList.toggle('hidden', !errors.length);
643
- for (const error of errors) {
644
- const icon = UI.UIUtils.createIconLabel({
645
- title: error.message,
646
- iconName: error.critical ? 'cross-circle-filled' : 'warning-filled',
647
- color: error.critical ? 'var(--icon-error)' : 'var(--icon-warning)',
648
- });
649
- this.errorsSection.appendRow().appendChild(icon);
650
- }
651
1297
 
652
1298
  if (!data) {
1299
+ this.view(
1300
+ {emptyView: this.emptyView, reportView: this.reportView, errorsSection: this.errorsSection, errors},
1301
+ undefined, this.contentElement);
653
1302
  return;
654
1303
  }
655
1304
 
@@ -658,414 +1307,80 @@ export class AppManifestView extends Common.ObjectWrapper.eventMixin<EventTypes,
658
1307
  } // Trim the BOM as per https://tools.ietf.org/html/rfc7159#section-8.1.
659
1308
 
660
1309
  const parsedManifest = JSON.parse(data);
661
- this.nameField.textContent = stringProperty('name');
662
- this.shortNameField.textContent = stringProperty('short_name');
663
-
664
- const warnings = [];
665
-
666
- const description = stringProperty('description');
667
- this.descriptionField.textContent = description;
668
- // See https://crbug.com/1354304 for details.
669
- if (description.length > 300) {
670
- warnings.push(i18nString(UIStrings.descriptionMayBeTruncated));
671
- }
672
-
673
- const startURL = stringProperty('start_url');
674
- if (appId && recommendedId) {
675
- const appIdField = this.identitySection.appendField(i18nString(UIStrings.computedAppId));
676
- UI.ARIAUtils.setLabel(appIdField, 'App Id');
677
- appIdField.textContent = appId;
678
-
679
- const helpIcon = createIcon('help', 'inline-icon');
680
- helpIcon.title = i18nString(UIStrings.appIdExplainer);
681
- helpIcon.setAttribute('jslog', `${VisualLogging.action('help').track({hover: true})}`);
682
- appIdField.appendChild(helpIcon);
683
-
684
- const learnMoreLink = UI.XLink.XLink.create(
685
- 'https://developer.chrome.com/blog/pwa-manifest-id/', i18nString(UIStrings.learnMore), undefined, undefined,
686
- 'learn-more');
687
- appIdField.appendChild(learnMoreLink);
688
-
689
- if (!stringProperty('id')) {
690
- const suggestedIdNote = appIdField.createChild('div', 'multiline-value');
691
- const suggestedIdSpan = document.createElement('code');
692
- suggestedIdSpan.textContent = recommendedId;
693
-
694
- const copyButton = new Buttons.Button.Button();
695
- copyButton.data = {
696
- variant: Buttons.Button.Variant.ICON,
697
- iconName: 'copy',
698
- size: Buttons.Button.Size.SMALL,
699
- jslogContext: 'manifest.copy-id',
700
- title: i18nString(UIStrings.copyToClipboard),
701
- };
702
- copyButton.className = 'inline-button';
703
- copyButton.addEventListener('click', () => {
704
- UI.ARIAUtils.LiveAnnouncer.alert(i18nString(UIStrings.copiedToClipboard, {PH1: recommendedId}));
705
- Host.InspectorFrontendHost.InspectorFrontendHostInstance.copyText(recommendedId);
706
- });
707
- suggestedIdNote.appendChild(
708
- uiI18n.getFormatLocalizedString(str_, UIStrings.appIdNote, {PH1: suggestedIdSpan, PH2: copyButton}));
709
- }
710
- } else {
711
- this.identitySection.removeField(i18nString(UIStrings.computedAppId));
712
- }
713
-
714
- this.startURLField.removeChildren();
715
- if (startURL) {
716
- const completeURL = Common.ParsedURL.ParsedURL.completeURL(url, startURL);
717
- if (completeURL) {
718
- const link = Components.Linkifier.Linkifier.linkifyURL(
719
- completeURL, ({text: startURL} as Components.Linkifier.LinkifyURLOptions));
720
- link.tabIndex = 0;
721
- link.setAttribute('jslog', `${VisualLogging.link('start-url').track({click: true})}`);
722
- this.startURLField.appendChild(link);
723
- }
724
- }
725
-
726
- this.themeColorSwatch.classList.toggle('hidden', !stringProperty('theme_color'));
727
- const themeColor = Common.Color.parse(stringProperty('theme_color') || 'white') || Common.Color.parse('white');
728
- if (themeColor) {
729
- this.themeColorSwatch.renderColor(themeColor);
730
- }
731
- this.backgroundColorSwatch.classList.toggle('hidden', !stringProperty('background_color'));
732
- const backgroundColor =
733
- Common.Color.parse(stringProperty('background_color') || 'white') || Common.Color.parse('white');
734
- if (backgroundColor) {
735
- this.backgroundColorSwatch.renderColor(backgroundColor);
736
- }
737
-
738
- this.orientationField.textContent = stringProperty('orientation');
739
- const displayType = stringProperty('display');
740
- this.displayField.textContent = displayType;
741
-
742
- const noteTaking = parsedManifest['note_taking'] || {};
743
- const newNoteUrl = noteTaking['new_note_url'];
744
- const hasNewNoteUrl = typeof newNoteUrl === 'string';
745
- this.newNoteUrlField.parentElement?.classList.toggle('hidden', !hasNewNoteUrl);
746
- this.newNoteUrlField.removeChildren();
747
- if (hasNewNoteUrl) {
748
- const completeURL = (Common.ParsedURL.ParsedURL.completeURL(url, newNoteUrl) as Platform.DevToolsPath.UrlString);
749
- const link = Components.Linkifier.Linkifier.linkifyURL(
750
- completeURL, ({text: newNoteUrl} as Components.Linkifier.LinkifyURLOptions));
751
- link.tabIndex = 0;
752
- this.newNoteUrlField.appendChild(link);
753
- }
754
-
755
- const protocolHandlers = parsedManifest['protocol_handlers'] || [];
756
- this.protocolHandlersView.protocolHandlers = protocolHandlers;
757
- this.protocolHandlersView.manifestLink = url;
758
-
759
- const icons = parsedManifest['icons'] || [];
760
- this.iconsSection.clearContent();
761
-
762
- const shortcuts = parsedManifest['shortcuts'] || [];
763
- for (const shortcutsSection of this.shortcutSections) {
764
- shortcutsSection.detach(/** overrideHideOnDetach= */ true);
765
- }
766
-
767
- const screenshots: Screenshot[] = parsedManifest['screenshots'] || [];
768
- for (const screenshotSection of this.screenshotsSections) {
769
- screenshotSection.detach(/** overrideHideOnDetach= */ true);
770
- }
771
-
772
- const imageErrors: Platform.UIString.LocalizedString[] = [];
773
-
774
- const setIconMaskedCheckbox = UI.UIUtils.CheckboxLabel.create(i18nString(UIStrings.showOnlyTheMinimumSafeAreaFor));
775
- setIconMaskedCheckbox.classList.add('mask-checkbox');
776
- setIconMaskedCheckbox.setAttribute(
777
- 'jslog', `${VisualLogging.toggle('show-minimal-safe-area-for-maskable-icons').track({change: true})}`);
778
- setIconMaskedCheckbox.addEventListener('click', () => {
779
- this.iconsSection.setIconMasked(setIconMaskedCheckbox.checked);
780
- });
781
- this.iconsSection.appendRow().appendChild(setIconMaskedCheckbox);
782
- const documentationLink = UI.XLink.XLink.create(
783
- 'https://web.dev/maskable-icon/', i18nString(UIStrings.documentationOnMaskableIcons), undefined, undefined,
784
- 'learn-more');
785
- this.iconsSection.appendRow().appendChild(
786
- uiI18n.getFormatLocalizedString(str_, UIStrings.needHelpReadOurS, {PH1: documentationLink}));
787
-
788
- let squareSizedIconAvailable = false;
789
- for (const icon of icons) {
790
- const result = await this.appendImageResourceToSection(url, icon, this.iconsSection, /** isScreenshot= */ false);
791
- imageErrors.push(...result.imageResourceErrors);
792
- squareSizedIconAvailable = result.squareSizedIconAvailable || squareSizedIconAvailable;
793
- }
794
- if (!squareSizedIconAvailable) {
795
- imageErrors.push(i18nString(UIStrings.sSShouldHaveSquareIcon));
796
- }
797
-
798
- if (shortcuts.length > 4) {
799
- warnings.push(i18nString(UIStrings.shortcutsMayBeNotAvailable));
800
- }
801
-
802
- let shortcutIndex = 1;
803
- for (const shortcut of shortcuts) {
804
- const shortcutSection = this.reportView.appendSection(i18nString(UIStrings.shortcutS, {PH1: shortcutIndex}));
805
- shortcutSection.element.setAttribute('jslog', `${VisualLogging.section('shortcuts')}`);
806
- this.shortcutSections.push(shortcutSection);
807
-
808
- shortcutSection.appendFlexedField(i18nString(UIStrings.name), shortcut.name);
809
- if (shortcut.short_name) {
810
- shortcutSection.appendFlexedField(i18nString(UIStrings.shortName), shortcut.short_name);
811
- }
812
- if (shortcut.description) {
813
- shortcutSection.appendFlexedField(i18nString(UIStrings.description), shortcut.description);
814
- }
815
- const urlField = shortcutSection.appendFlexedField(i18nString(UIStrings.url));
816
- const shortcutUrl = Common.ParsedURL.ParsedURL.completeURL(url, shortcut.url) as Platform.DevToolsPath.UrlString;
817
- const link = Components.Linkifier.Linkifier.linkifyURL(
818
- shortcutUrl, ({text: shortcut.url} as Components.Linkifier.LinkifyURLOptions));
819
- link.setAttribute('jslog', `${VisualLogging.link('shortcut').track({click: true})}`);
820
- link.tabIndex = 0;
821
- urlField.appendChild(link);
822
-
823
- const shortcutIcons = shortcut.icons || [];
824
- let hasShortcutIconLargeEnough = false;
825
- for (const shortcutIcon of shortcutIcons) {
826
- const {imageResourceErrors: shortcutIconErrors} =
827
- await this.appendImageResourceToSection(url, shortcutIcon, shortcutSection, /** isScreenshot= */ false);
828
- imageErrors.push(...shortcutIconErrors);
829
- if (!hasShortcutIconLargeEnough && shortcutIcon.sizes) {
830
- const shortcutIconSize = shortcutIcon.sizes.match(/^(\d+)x(\d+)$/);
831
- if (shortcutIconSize && shortcutIconSize[1] >= 96 && shortcutIconSize[2] >= 96) {
832
- hasShortcutIconLargeEnough = true;
833
- }
834
- }
835
- }
836
- if (!hasShortcutIconLargeEnough) {
837
- imageErrors.push(i18nString(UIStrings.shortcutSShouldIncludeAXPixel, {PH1: shortcutIndex}));
838
- }
839
- shortcutIndex++;
840
- }
841
1310
 
842
- let screenshotIndex = 1;
843
- const formFactorScreenshotDimensions = new Map<string, {width: number, height: number}>();
844
- let haveScreenshotsDifferentAspectRatio = false;
845
- for (const screenshot of screenshots) {
846
- const screenshotSection =
847
- this.reportView.appendSection(i18nString(UIStrings.screenshotS, {PH1: screenshotIndex}));
848
- this.screenshotsSections.push(screenshotSection);
849
-
850
- if (screenshot.form_factor) {
851
- screenshotSection.appendFlexedField(i18nString(UIStrings.formFactor), screenshot.form_factor);
852
- }
853
- if (screenshot.label) {
854
- screenshotSection.appendFlexedField(i18nString(UIStrings.label), screenshot.label);
855
- }
856
- if (screenshot.platform) {
857
- screenshotSection.appendFlexedField(i18nString(UIStrings.platform), screenshot.platform);
858
- }
859
-
860
- const {imageResourceErrors: screenshotErrors, naturalWidth: width, naturalHeight: height} =
861
- await this.appendImageResourceToSection(url, screenshot, screenshotSection, /** isScreenshot= */ true);
862
- imageErrors.push(...screenshotErrors);
863
-
864
- if (screenshot.form_factor && width && height) {
865
- formFactorScreenshotDimensions.has(screenshot.form_factor) ||
866
- formFactorScreenshotDimensions.set(screenshot.form_factor, {width, height});
867
- const formFactorFirstScreenshotDimensions = formFactorScreenshotDimensions.get(screenshot.form_factor);
868
- if (formFactorFirstScreenshotDimensions) {
869
- haveScreenshotsDifferentAspectRatio = haveScreenshotsDifferentAspectRatio ||
870
- (width * formFactorFirstScreenshotDimensions.height !==
871
- height * formFactorFirstScreenshotDimensions.width);
872
- }
873
- }
874
-
875
- screenshotIndex++;
876
- }
877
-
878
- if (haveScreenshotsDifferentAspectRatio) {
879
- warnings.push(i18nString(UIStrings.screenshotsMustHaveSameAspectRatio));
880
- }
881
-
882
- const screenshotsForDesktop = screenshots.filter(screenshot => screenshot.form_factor === 'wide');
883
- const screenshotsForMobile = screenshots.filter(screenshot => screenshot.form_factor !== 'wide');
1311
+ const identityData = this.processIdentity(parsedManifest, appId, recommendedId);
884
1312
 
885
- if (screenshotsForDesktop.length < 1) {
886
- warnings.push(i18nString(UIStrings.noScreenshotsForRicherPWAInstallOnDesktop));
887
- }
888
- if (screenshotsForMobile.length < 1) {
889
- warnings.push(i18nString(UIStrings.noScreenshotsForRicherPWAInstallOnMobile));
890
- }
891
- if (screenshotsForDesktop.length > 8) {
892
- warnings.push(i18nString(UIStrings.tooManyScreenshotsForDesktop));
893
- }
894
- if (screenshotsForMobile.length > 5) {
895
- warnings.push(i18nString(UIStrings.tooManyScreenshotsForMobile));
896
- }
897
-
898
- this.installabilitySection.clearContent();
899
- this.installabilitySection.element.classList.toggle('hidden', !installabilityErrors.length);
900
- const errorMessages = this.getInstallabilityErrorMessages(installabilityErrors);
901
- for (const error of errorMessages) {
902
- const msgElement = document.createTextNode(error);
903
- this.installabilitySection.appendRow().appendChild(msgElement);
904
- }
905
-
906
- this.errorsSection.element.classList.toggle('hidden', !errors.length && !imageErrors.length && !warnings.length);
907
- for (const warning of warnings) {
908
- const msgElement = document.createTextNode(warning);
909
- this.errorsSection.appendRow().appendChild(msgElement);
910
- }
911
- for (const error of imageErrors) {
912
- const msgElement = document.createTextNode(error);
913
- this.errorsSection.appendRow().appendChild(msgElement);
914
- }
915
-
916
- function stringProperty(name: string): string {
917
- const value = parsedManifest[name];
918
- if (typeof value !== 'string') {
919
- return '';
920
- }
921
- return value;
922
- }
923
-
924
- this.windowControlsSection.clearContent();
925
- const displayOverride = parsedManifest['display_override'] || [];
926
- const hasWco = displayOverride.includes('window-controls-overlay');
1313
+ const presentationData = this.processPresentation(parsedManifest, url);
1314
+ const protocolHandlersData = this.processProtocolHandlers(parsedManifest, url);
927
1315
 
928
- const displayOverrideLink = UI.XLink.XLink.create(
929
- 'https://developer.mozilla.org/en-US/docs/Web/Manifest/display_override', 'display-override', undefined,
930
- undefined, 'display-override');
931
- const displayOverrideText = document.createElement('code');
932
- displayOverrideText.appendChild(displayOverrideLink);
1316
+ const iconsData = await this.processIcons(parsedManifest, url);
1317
+ const shortcutsData = await this.processShortcuts(parsedManifest, url);
933
1318
 
934
- const wcoStatusMessage = this.windowControlsSection.appendRow();
1319
+ const screenshotsData = await this.processScreenshots(parsedManifest, url);
935
1320
 
936
- if (hasWco) {
937
- const checkmarkIcon = createIcon('check-circle', 'inline-icon');
938
- wcoStatusMessage.appendChild(checkmarkIcon);
939
-
940
- const wco = document.createElement('code');
941
- wco.classList.add('wco');
942
- wco.textContent = 'window-controls-overlay';
943
- wcoStatusMessage.appendChild(
944
- uiI18n.getFormatLocalizedString(str_, UIStrings.wcoFound, {PH1: wco, PH2: displayOverrideText, PH3: link}));
945
-
946
- if (this.overlayModel) {
947
- await this.appendWindowControlsToSection(this.overlayModel, url, stringProperty('theme_color'));
948
- }
949
- } else {
950
- const infoIcon = createIcon('info', 'inline-icon');
951
-
952
- wcoStatusMessage.appendChild(infoIcon);
953
-
954
- wcoStatusMessage.appendChild(
955
- uiI18n.getFormatLocalizedString(str_, UIStrings.wcoNotFound, {PH1: displayOverrideText}));
956
- }
1321
+ const warnings = [
1322
+ ...identityData.warnings,
1323
+ ...shortcutsData.warnings,
1324
+ ...screenshotsData.warnings,
1325
+ ];
957
1326
 
958
- const wcoDocumentationLink = UI.XLink.XLink.create(
959
- 'https://learn.microsoft.com/en-us/microsoft-edge/progressive-web-apps-chromium/how-to/window-controls-overlay',
960
- i18nString(UIStrings.customizePwaTitleBar), undefined, undefined, 'customize-pwa-tittle-bar');
961
- this.windowControlsSection.appendRow().appendChild(
962
- uiI18n.getFormatLocalizedString(str_, UIStrings.wcoNeedHelpReadMore, {PH1: wcoDocumentationLink}));
1327
+ const imageErrors = [
1328
+ ...iconsData.imageResourceErrors,
1329
+ ...shortcutsData.imageResourceErrors,
1330
+ ...screenshotsData.imageResourceErrors,
1331
+ ];
963
1332
 
964
- this.dispatchEventToListeners(Events.MANIFEST_RENDERED);
1333
+ const windowControlsData = await this.processWindowControls(parsedManifest, url);
1334
+ const selectedPlatform = this.overlayModel?.getWindowControlsConfig().selectedPlatform;
1335
+ const onSelectOs = this.overlayModel ?
1336
+ (selectedOS: SDK.OverlayModel.EmulatedOSType) => this.onSelectOs(selectedOS, windowControlsData.themeColor) :
1337
+ undefined;
1338
+ const onToggleWcoToolbar = this.overlayModel ? (enabled: boolean) => this.onToggleWcoToolbar(enabled) : undefined;
1339
+ this.view(
1340
+ {
1341
+ emptyView: this.emptyView,
1342
+ reportView: this.reportView,
1343
+ errorsSection: this.errorsSection,
1344
+ installabilitySection: this.installabilitySection,
1345
+ identitySection: this.identitySection,
1346
+ presentationSection: this.presentationSection,
1347
+ protocolHandlersView: this.protocolHandlersView,
1348
+ iconsSection: this.iconsSection,
1349
+ windowControlsSection: this.windowControlsSection,
1350
+ shortcutSections: this.shortcutSections,
1351
+ screenshotsSections: this.screenshotsSections,
1352
+ parsedManifest,
1353
+ url,
1354
+ identityData,
1355
+ presentationData,
1356
+ protocolHandlersData,
1357
+ iconsData,
1358
+ shortcutsData,
1359
+ screenshotsData,
1360
+ installabilityErrors,
1361
+ warnings,
1362
+ errors,
1363
+ imageErrors,
1364
+ windowControlsData,
1365
+ selectedPlatform,
1366
+ onSelectOs,
1367
+ onToggleWcoToolbar,
1368
+ },
1369
+ undefined, this.contentElement);
965
1370
  }
966
1371
 
967
- getInstallabilityErrorMessages(installabilityErrors: Protocol.Page.InstallabilityError[]): string[] {
968
- const errorMessages = [];
969
- for (const installabilityError of installabilityErrors) {
970
- let errorMessage;
971
- switch (installabilityError.errorId) {
972
- case 'not-in-main-frame':
973
- errorMessage = i18nString(UIStrings.pageIsNotLoadedInTheMainFrame);
974
- break;
975
- case 'not-from-secure-origin':
976
- errorMessage = i18nString(UIStrings.pageIsNotServedFromASecureOrigin);
977
- break;
978
- case 'no-manifest':
979
- errorMessage = i18nString(UIStrings.pageHasNoManifestLinkUrl);
980
- break;
981
- case 'manifest-empty':
982
- errorMessage = i18nString(UIStrings.manifestCouldNotBeFetchedIsEmpty);
983
- break;
984
- case 'start-url-not-valid':
985
- errorMessage = i18nString(UIStrings.manifestStartUrlIsNotValid);
986
- break;
987
- case 'manifest-missing-name-or-short-name':
988
- errorMessage = i18nString(UIStrings.manifestDoesNotContainANameOr);
989
- break;
990
- case 'manifest-display-not-supported':
991
- errorMessage = i18nString(UIStrings.manifestDisplayPropertyMustBeOne);
992
- break;
993
- case 'manifest-missing-suitable-icon':
994
- if (installabilityError.errorArguments.length !== 1 ||
995
- installabilityError.errorArguments[0].name !== 'minimum-icon-size-in-pixels') {
996
- console.error('Installability error does not have the correct errorArguments');
997
- break;
998
- }
999
- errorMessage =
1000
- i18nString(UIStrings.manifestDoesNotContainASuitable, {PH1: installabilityError.errorArguments[0].value});
1001
- break;
1002
- case 'no-acceptable-icon':
1003
- if (installabilityError.errorArguments.length !== 1 ||
1004
- installabilityError.errorArguments[0].name !== 'minimum-icon-size-in-pixels') {
1005
- console.error('Installability error does not have the correct errorArguments');
1006
- break;
1007
- }
1008
- errorMessage = i18nString(
1009
- UIStrings.noSuppliedIconIsAtLeastSpxSquare, {PH1: installabilityError.errorArguments[0].value});
1010
- break;
1011
- case 'cannot-download-icon':
1012
- errorMessage = i18nString(UIStrings.couldNotDownloadARequiredIcon);
1013
- break;
1014
- case 'no-icon-available':
1015
- errorMessage = i18nString(UIStrings.downloadedIconWasEmptyOr);
1016
- break;
1017
- case 'platform-not-supported-on-android':
1018
- errorMessage = i18nString(UIStrings.theSpecifiedApplicationPlatform);
1019
- break;
1020
- case 'no-id-specified':
1021
- errorMessage = i18nString(UIStrings.noPlayStoreIdProvided);
1022
- break;
1023
- case 'ids-do-not-match':
1024
- errorMessage = i18nString(UIStrings.thePlayStoreAppUrlAndPlayStoreId);
1025
- break;
1026
- case 'already-installed':
1027
- errorMessage = i18nString(UIStrings.theAppIsAlreadyInstalled);
1028
- break;
1029
- case 'url-not-supported-for-webapk':
1030
- errorMessage = i18nString(UIStrings.aUrlInTheManifestContainsA);
1031
- break;
1032
- case 'in-incognito':
1033
- errorMessage = i18nString(UIStrings.pageIsLoadedInAnIncognitoWindow);
1034
- break;
1035
- case 'not-offline-capable':
1036
- errorMessage = i18nString(UIStrings.pageDoesNotWorkOffline);
1037
- break;
1038
- case 'no-url-for-service-worker':
1039
- errorMessage = i18nString(UIStrings.couldNotCheckServiceWorker);
1040
- break;
1041
- case 'prefer-related-applications':
1042
- errorMessage = i18nString(UIStrings.manifestSpecifies);
1043
- break;
1044
- case 'prefer-related-applications-only-beta-stable':
1045
- errorMessage = i18nString(UIStrings.preferrelatedapplicationsIsOnly);
1046
- break;
1047
- case 'manifest-display-override-not-supported':
1048
- errorMessage = i18nString(UIStrings.manifestContainsDisplayoverride);
1049
- break;
1050
- case 'warn-not-offline-capable':
1051
- errorMessage = i18nString(
1052
- UIStrings.pageDoesNotWorkOfflineThePage,
1053
- {PH1: 'https://developer.chrome.com/blog/improved-pwa-offline-detection/'});
1054
- break;
1055
- default:
1056
- console.error(`Installability error id '${installabilityError.errorId}' is not recognized`);
1057
- break;
1058
- }
1059
- if (errorMessage) {
1060
- errorMessages.push(errorMessage);
1061
- }
1372
+ private stringProperty(parsedManifest: Manifest, name: keyof Manifest): string {
1373
+ const value = parsedManifest[name];
1374
+ if (typeof value !== 'string') {
1375
+ return '';
1062
1376
  }
1063
- return errorMessages;
1377
+ return value;
1064
1378
  }
1065
1379
 
1066
1380
  private async loadImage(url: Platform.DevToolsPath.UrlString): Promise<{
1067
- image: HTMLImageElement,
1068
- wrapper: Element,
1381
+ naturalWidth: number,
1382
+ naturalHeight: number,
1383
+ src: string,
1069
1384
  }|null> {
1070
1385
  const frameId = this.resourceTreeModel?.mainFrame?.id;
1071
1386
  if (!this.target) {
@@ -1081,8 +1396,6 @@ export class AppManifestView extends Common.ObjectWrapper.eventMixin<EventTypes,
1081
1396
  initiatorUrl: this.target.inspectedURL(),
1082
1397
  },
1083
1398
  /* isBinary=*/ true);
1084
- const wrapper = document.createElement('div');
1085
- wrapper.classList.add('image-wrapper');
1086
1399
  const image = document.createElement('img');
1087
1400
  const result = new Promise((resolve, reject) => {
1088
1401
  image.onload = resolve;
@@ -1092,11 +1405,9 @@ export class AppManifestView extends Common.ObjectWrapper.eventMixin<EventTypes,
1092
1405
  // does not work, we can parse mimeType out of the response headers
1093
1406
  // using front_end/core/platform/MimeType.ts.
1094
1407
  image.src = 'data:application/octet-stream;base64,' + await Common.Base64.encode(content);
1095
- image.alt = i18nString(UIStrings.imageFromS, {PH1: url});
1096
- wrapper.appendChild(image);
1097
1408
  try {
1098
1409
  await result;
1099
- return {wrapper, image};
1410
+ return {naturalWidth: image.naturalWidth, naturalHeight: image.naturalHeight, src: image.src};
1100
1411
  } catch {
1101
1412
  }
1102
1413
  return null;
@@ -1128,17 +1439,17 @@ export class AppManifestView extends Common.ObjectWrapper.eventMixin<EventTypes,
1128
1439
  }
1129
1440
 
1130
1441
  checkSizeProblem(
1131
- size: ParsedSize, image: HTMLImageElement, resourceName: Platform.UIString.LocalizedString,
1442
+ size: ParsedSize, naturalWidth: number, naturalHeight: number, resourceName: Platform.UIString.LocalizedString,
1132
1443
  imageUrl: string): {hasSquareSize: boolean, error?: Platform.UIString.LocalizedString} {
1133
1444
  if ('any' in size) {
1134
- return {hasSquareSize: image.naturalWidth === image.naturalHeight};
1445
+ return {hasSquareSize: naturalWidth === naturalHeight};
1135
1446
  }
1136
1447
  const hasSquareSize = size.width === size.height;
1137
- if (image.naturalWidth !== size.width && image.naturalHeight !== size.height) {
1448
+ if (naturalWidth !== size.width && naturalHeight !== size.height) {
1138
1449
  return {
1139
1450
  error: i18nString(UIStrings.actualSizeSspxOfSSDoesNotMatch, {
1140
- PH1: image.naturalWidth,
1141
- PH2: image.naturalHeight,
1451
+ PH1: naturalWidth,
1452
+ PH2: naturalHeight,
1142
1453
  PH3: resourceName,
1143
1454
  PH4: imageUrl,
1144
1455
  PH5: size.width,
@@ -1147,35 +1458,29 @@ export class AppManifestView extends Common.ObjectWrapper.eventMixin<EventTypes,
1147
1458
  hasSquareSize,
1148
1459
  };
1149
1460
  }
1150
- if (image.naturalWidth !== size.width) {
1461
+ if (naturalWidth !== size.width) {
1151
1462
  return {
1152
1463
  error: i18nString(
1153
1464
  UIStrings.actualWidthSpxOfSSDoesNotMatch,
1154
- {PH1: image.naturalWidth, PH2: resourceName, PH3: imageUrl, PH4: size.width}),
1465
+ {PH1: naturalWidth, PH2: resourceName, PH3: imageUrl, PH4: size.width}),
1155
1466
  hasSquareSize,
1156
1467
  };
1157
1468
  }
1158
- if (image.naturalHeight !== size.height) {
1469
+ if (naturalHeight !== size.height) {
1159
1470
  return {
1160
1471
  error: i18nString(
1161
1472
  UIStrings.actualHeightSpxOfSSDoesNotMatch,
1162
- {PH1: image.naturalHeight, PH2: resourceName, PH3: imageUrl, PH4: size.height}),
1473
+ {PH1: naturalHeight, PH2: resourceName, PH3: imageUrl, PH4: size.height}),
1163
1474
  hasSquareSize,
1164
1475
  };
1165
1476
  }
1166
1477
  return {hasSquareSize};
1167
1478
  }
1168
1479
 
1169
- private async appendImageResourceToSection(
1170
- // TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration)
1171
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1172
- baseUrl: Platform.DevToolsPath.UrlString, imageResource: any, section: UI.ReportView.Section,
1173
- isScreenshot: boolean): Promise<{
1174
- imageResourceErrors: Platform.UIString.LocalizedString[],
1175
- squareSizedIconAvailable?: boolean,
1176
- naturalWidth?: number,
1177
- naturalHeight?: number,
1178
- }> {
1480
+ private async processImageResource(
1481
+ baseUrl: Platform.DevToolsPath.UrlString,
1482
+ imageResource: any, // eslint-disable-line @typescript-eslint/no-explicit-any
1483
+ isScreenshot: boolean): Promise<ProcessedImageResource> {
1179
1484
  const imageResourceErrors: Platform.UIString.LocalizedString[] = [];
1180
1485
  const resourceName = isScreenshot ? i18nString(UIStrings.screenshot) : i18nString(UIStrings.icon);
1181
1486
  if (!imageResource.src) {
@@ -1186,18 +1491,16 @@ export class AppManifestView extends Common.ObjectWrapper.eventMixin<EventTypes,
1186
1491
  if (!imageUrl) {
1187
1492
  imageResourceErrors.push(
1188
1493
  i18nString(UIStrings.sUrlSFailedToParse, {PH1: resourceName, PH2: imageResource['src']}));
1189
- return {imageResourceErrors};
1494
+ return {imageResourceErrors, imageUrl: imageResource['src']};
1190
1495
  }
1191
1496
  const result = await this.loadImage(imageUrl);
1192
1497
  if (!result) {
1193
1498
  imageResourceErrors.push(i18nString(UIStrings.sSFailedToLoad, {PH1: resourceName, PH2: imageUrl}));
1194
- return {imageResourceErrors};
1499
+ return {imageResourceErrors, imageUrl};
1195
1500
  }
1196
- const {wrapper, image} = result;
1197
- const {naturalWidth, naturalHeight} = image;
1501
+ const {src, naturalWidth, naturalHeight} = result;
1198
1502
  const sizes = this.parseSizes(imageResource['sizes'], resourceName, imageUrl, imageResourceErrors);
1199
1503
  const title = sizes.map(x => x.formatted).join(' ') + '\n' + (imageResource['type'] || '');
1200
- const field = section.appendFlexedField(title);
1201
1504
  let squareSizedIconAvailable = false;
1202
1505
  if (!imageResource.sizes) {
1203
1506
  imageResourceErrors.push(i18nString(UIStrings.sSDoesNotSpecifyItsSizeInThe, {PH1: resourceName, PH2: imageUrl}));
@@ -1206,13 +1509,13 @@ export class AppManifestView extends Common.ObjectWrapper.eventMixin<EventTypes,
1206
1509
  imageResourceErrors.push(i18nString(UIStrings.screenshotPixelSize, {url: imageUrl}));
1207
1510
  }
1208
1511
  for (const size of sizes) {
1209
- const {error, hasSquareSize} = this.checkSizeProblem(size, image, resourceName, imageUrl);
1512
+ const {error, hasSquareSize} = this.checkSizeProblem(size, naturalWidth, naturalHeight, resourceName, imageUrl);
1210
1513
  squareSizedIconAvailable = squareSizedIconAvailable || hasSquareSize;
1211
1514
  if (error) {
1212
1515
  imageResourceErrors.push(error);
1213
1516
  } else if (isScreenshot) {
1214
- const width = 'any' in size ? image.naturalWidth : size.width;
1215
- const height = 'any' in size ? image.naturalHeight : size.height;
1517
+ const width = 'any' in size ? naturalWidth : size.width;
1518
+ const height = 'any' in size ? naturalHeight : size.height;
1216
1519
  if (width < 320 || height < 320) {
1217
1520
  imageResourceErrors.push(
1218
1521
  i18nString(UIStrings.sSSizeShouldBeAtLeast320, {PH1: resourceName, PH2: imageUrl}));
@@ -1229,66 +1532,249 @@ export class AppManifestView extends Common.ObjectWrapper.eventMixin<EventTypes,
1229
1532
  }
1230
1533
  }
1231
1534
  }
1232
- image.width = image.naturalWidth;
1233
1535
 
1234
1536
  const purpose = typeof imageResource['purpose'] === 'string' ? imageResource['purpose'].toLowerCase() : '';
1235
1537
  if (purpose.includes('any') && purpose.includes('maskable')) {
1236
1538
  imageResourceErrors.push(i18nString(UIStrings.avoidPurposeAnyAndMaskable));
1237
1539
  }
1238
1540
 
1239
- field.appendChild(wrapper);
1240
- return {imageResourceErrors, squareSizedIconAvailable, naturalWidth, naturalHeight};
1541
+ return {
1542
+ imageResourceErrors,
1543
+ squareSizedIconAvailable,
1544
+ naturalWidth,
1545
+ naturalHeight,
1546
+ title,
1547
+ imageSrc: src,
1548
+ imageUrl,
1549
+ };
1550
+ }
1551
+
1552
+ private async onToggleWcoToolbar(enabled: boolean): Promise<void> {
1553
+ this.wcoToolbarEnabled = enabled;
1554
+ if (this.overlayModel) {
1555
+ await this.overlayModel.toggleWindowControlsToolbar(this.wcoToolbarEnabled);
1556
+ }
1241
1557
  }
1242
1558
 
1243
- private async appendWindowControlsToSection(
1244
- overlayModel: SDK.OverlayModel.OverlayModel, url: Platform.DevToolsPath.UrlString,
1245
- themeColor: string): Promise<void> {
1246
- const wcoStyleSheetText = await overlayModel.hasStyleSheetText(url);
1559
+ private async onSelectOs(selectedOS: SDK.OverlayModel.EmulatedOSType, themeColor: string): Promise<void> {
1560
+ if (this.overlayModel) {
1561
+ this.overlayModel.setWindowControlsPlatform(selectedOS);
1562
+ this.overlayModel.setWindowControlsThemeColor(themeColor);
1563
+ await this.overlayModel.toggleWindowControlsToolbar(this.wcoToolbarEnabled);
1564
+ }
1565
+ }
1247
1566
 
1248
- if (!wcoStyleSheetText) {
1249
- return;
1567
+ private processIdentity(parsedManifest: Manifest, appId: string|null, recommendedId: string|null):
1568
+ IdentitySectionData {
1569
+ const description = this.stringProperty(parsedManifest, 'description');
1570
+ const warnings: Platform.UIString.LocalizedString[] = [];
1571
+ // See https://crbug.com/1354304 for details.
1572
+ if (description.length > 300) {
1573
+ warnings.push(i18nString(UIStrings.descriptionMayBeTruncated));
1250
1574
  }
1575
+ return {
1576
+ name: this.stringProperty(parsedManifest, 'name'),
1577
+ shortName: this.stringProperty(parsedManifest, 'short_name'),
1578
+ description: this.stringProperty(parsedManifest, 'description'),
1579
+ appId,
1580
+ recommendedId,
1581
+ hasId: Boolean(this.stringProperty(parsedManifest, 'id')),
1582
+ warnings,
1583
+ };
1584
+ }
1251
1585
 
1252
- await overlayModel.toggleWindowControlsToolbar(false);
1586
+ private async processIcons(parsedManifest: Manifest, url: Platform.DevToolsPath.UrlString):
1587
+ Promise<IconsSectionData> {
1588
+ const icons = parsedManifest['icons'] || [];
1589
+ const imageErrors: Platform.UIString.LocalizedString[] = [];
1590
+ const processedIcons: ProcessedImageResource[] = [];
1591
+ let squareSizedIconAvailable = false;
1253
1592
 
1254
- const wcoOsCheckbox =
1255
- UI.UIUtils.CheckboxLabel.create(i18nString(UIStrings.selectWindowControlsOverlayEmulationOs), false);
1593
+ for (const icon of icons) {
1594
+ const result = await this.processImageResource(url, icon, /** isScreenshot= */ false);
1595
+ processedIcons.push(result);
1596
+ imageErrors.push(...result.imageResourceErrors);
1256
1597
 
1257
- wcoOsCheckbox.addEventListener('click', async () => {
1258
- await this.overlayModel?.toggleWindowControlsToolbar(wcoOsCheckbox.checked);
1259
- });
1598
+ if (result.squareSizedIconAvailable) {
1599
+ squareSizedIconAvailable = true;
1600
+ }
1601
+ }
1260
1602
 
1261
- const osSelectElement = wcoOsCheckbox.createChild('select');
1262
- osSelectElement.appendChild(UI.UIUtils.createOption('Windows', SDK.OverlayModel.EmulatedOSType.WINDOWS, 'windows'));
1263
- osSelectElement.appendChild(UI.UIUtils.createOption('macOS', SDK.OverlayModel.EmulatedOSType.MAC, 'macos'));
1264
- osSelectElement.appendChild(UI.UIUtils.createOption('Linux', SDK.OverlayModel.EmulatedOSType.LINUX, 'linux'));
1265
- osSelectElement.selectedIndex = 0;
1603
+ const processedIconsByTitle = Map.groupBy(
1604
+ processedIcons.filter((icon): icon is ProcessedImageResource&{title: string} => 'title' in icon),
1605
+ img => img.title,
1606
+ );
1266
1607
 
1267
- if (this.overlayModel) {
1268
- osSelectElement.value = this.overlayModel?.getWindowControlsConfig().selectedPlatform;
1608
+ if (!squareSizedIconAvailable) {
1609
+ imageErrors.push(i18nString(UIStrings.sSShouldHaveSquareIcon));
1610
+ }
1611
+ return {icons: processedIconsByTitle, imageResourceErrors: imageErrors};
1612
+ }
1613
+
1614
+ private async processShortcuts(parsedManifest: Manifest, url: Platform.DevToolsPath.UrlString):
1615
+ Promise<ShortcutsSectionData> {
1616
+ const shortcuts = parsedManifest['shortcuts'] || [];
1617
+ const processedShortcuts: ProcessedShortcut[] = [];
1618
+
1619
+ const warnings: Platform.UIString.LocalizedString[] = [];
1620
+ const imageErrors: Platform.UIString.LocalizedString[] = [];
1621
+
1622
+ if (shortcuts.length > 4) {
1623
+ warnings.push(i18nString(UIStrings.shortcutsMayBeNotAvailable));
1269
1624
  }
1270
1625
 
1271
- osSelectElement.addEventListener('change', async () => {
1272
- const selectedOS =
1273
- osSelectElement.options[osSelectElement.selectedIndex].value as SDK.OverlayModel.EmulatedOSType;
1274
- if (this.overlayModel) {
1275
- this.overlayModel.setWindowControlsPlatform(selectedOS);
1276
- await this.overlayModel.toggleWindowControlsToolbar(wcoOsCheckbox.checked);
1626
+ let shortcutIndex = 1;
1627
+ for (const shortcut of shortcuts) {
1628
+ const shortcutUrl = Common.ParsedURL.ParsedURL.completeURL(url, shortcut.url) as Platform.DevToolsPath.UrlString;
1629
+ const shortcutIcons = shortcut.icons || [];
1630
+ const processedIcons: ProcessedImageResource[] = [];
1631
+ let hasShortcutIconLargeEnough = false;
1632
+
1633
+ for (const shortcutIcon of shortcutIcons) {
1634
+ const result = await this.processImageResource(url, shortcutIcon, /** isScreenshot= */ false);
1635
+ processedIcons.push(result);
1636
+ imageErrors.push(...result.imageResourceErrors);
1637
+
1638
+ if (!hasShortcutIconLargeEnough && shortcutIcon.sizes) {
1639
+ const shortcutIconSize = shortcutIcon.sizes.match(/^(\d+)x(\d+)$/);
1640
+ if (shortcutIconSize && Number(shortcutIconSize[1]) >= 96 && Number(shortcutIconSize[2]) >= 96) {
1641
+ hasShortcutIconLargeEnough = true;
1642
+ }
1643
+ }
1277
1644
  }
1278
- });
1279
1645
 
1280
- this.windowControlsSection.appendRow().appendChild(wcoOsCheckbox);
1646
+ const iconsByTitle = Map.groupBy(
1647
+ processedIcons.filter(icon => 'title' in icon),
1648
+ img => img.title,
1649
+ );
1650
+
1651
+ processedShortcuts.push({
1652
+ name: shortcut.name,
1653
+ shortName: shortcut.short_name,
1654
+ description: shortcut.description,
1655
+ url: shortcut.url,
1656
+ shortcutUrl,
1657
+ icons: iconsByTitle,
1658
+ });
1659
+
1660
+ if (!hasShortcutIconLargeEnough) {
1661
+ imageErrors.push(i18nString(UIStrings.shortcutSShouldIncludeAXPixel, {PH1: shortcutIndex}));
1662
+ }
1663
+ shortcutIndex++;
1664
+ }
1665
+ return {shortcuts: processedShortcuts, warnings, imageResourceErrors: imageErrors};
1666
+ }
1667
+
1668
+ private async processScreenshots(parsedManifest: Manifest, url: Platform.DevToolsPath.UrlString):
1669
+ Promise<ScreenshotsSectionData> {
1670
+ const screenshots: Screenshot[] = parsedManifest['screenshots'] || [];
1671
+ const processedScreenshots: ProcessedScreenshot[] = [];
1672
+
1673
+ const warnings: Platform.UIString.LocalizedString[] = [];
1674
+ const imageErrors: Platform.UIString.LocalizedString[] = [];
1675
+
1676
+ let haveScreenshotsDifferentAspectRatio = false;
1677
+ const formFactorScreenshotDimensions = new Map<string, {width: number, height: number}>();
1678
+ for (const screenshot of screenshots) {
1679
+ const result = await this.processImageResource(url, screenshot, /** isScreenshot= */ true);
1680
+ processedScreenshots.push({screenshot, processedImage: result});
1681
+ imageErrors.push(...result.imageResourceErrors);
1682
+
1683
+ if (screenshot.form_factor && 'naturalWidth' in result) {
1684
+ const width = result.naturalWidth;
1685
+ const height = result.naturalHeight;
1686
+ formFactorScreenshotDimensions.has(screenshot.form_factor) ||
1687
+ formFactorScreenshotDimensions.set(screenshot.form_factor, {width, height});
1688
+ const formFactorFirstScreenshotDimensions = formFactorScreenshotDimensions.get(screenshot.form_factor);
1689
+ if (formFactorFirstScreenshotDimensions) {
1690
+ haveScreenshotsDifferentAspectRatio = haveScreenshotsDifferentAspectRatio ||
1691
+ (width * formFactorFirstScreenshotDimensions.height !==
1692
+ height * formFactorFirstScreenshotDimensions.width);
1693
+ }
1694
+ }
1695
+ }
1696
+
1697
+ if (haveScreenshotsDifferentAspectRatio) {
1698
+ warnings.push(i18nString(UIStrings.screenshotsMustHaveSameAspectRatio));
1699
+ }
1700
+
1701
+ const screenshotsForDesktop = screenshots.filter(screenshot => screenshot.form_factor === 'wide');
1702
+ const screenshotsForMobile = screenshots.filter(screenshot => screenshot.form_factor !== 'wide');
1703
+
1704
+ if (screenshotsForDesktop.length < 1) {
1705
+ warnings.push(i18nString(UIStrings.noScreenshotsForRicherPWAInstallOnDesktop));
1706
+ }
1707
+ if (screenshotsForMobile.length < 1) {
1708
+ warnings.push(i18nString(UIStrings.noScreenshotsForRicherPWAInstallOnMobile));
1709
+ }
1710
+ if (screenshotsForDesktop.length > 8) {
1711
+ warnings.push(i18nString(UIStrings.tooManyScreenshotsForDesktop));
1712
+ }
1713
+ if (screenshotsForMobile.length > 5) {
1714
+ warnings.push(i18nString(UIStrings.tooManyScreenshotsForMobile));
1715
+ }
1716
+
1717
+ return {screenshots: processedScreenshots, warnings, imageResourceErrors: imageErrors};
1718
+ }
1719
+
1720
+ private async processWindowControls(parsedManifest: Manifest, url: Platform.DevToolsPath.UrlString):
1721
+ Promise<WindowControlsSectionData> {
1722
+ const displayOverride = parsedManifest['display_override'] || [];
1723
+ const hasWco = displayOverride.includes('window-controls-overlay');
1724
+ const themeColor = this.stringProperty(parsedManifest, 'theme_color');
1725
+ let wcoStyleSheetText = false;
1726
+ if (this.overlayModel) {
1727
+ wcoStyleSheetText = await this.overlayModel.hasStyleSheetText(url);
1728
+ }
1729
+ return {
1730
+ hasWco,
1731
+ themeColor,
1732
+ wcoStyleSheetText,
1733
+ url,
1734
+ };
1735
+ }
1736
+
1737
+ private processPresentation(parsedManifest: Manifest, url: Platform.DevToolsPath.UrlString): PresentationSectionData {
1738
+ const startURL = this.stringProperty(parsedManifest, 'start_url');
1739
+ const completeURL = startURL ? Common.ParsedURL.ParsedURL.completeURL(url, startURL) : null;
1740
+ const themeColorString = this.stringProperty(parsedManifest, 'theme_color');
1741
+ const themeColor = themeColorString ? Common.Color.parse(themeColorString) ?? Common.Color.parse('white') : null;
1742
+ const backgroundColorString = this.stringProperty(parsedManifest, 'background_color');
1743
+ const backgroundColor =
1744
+ backgroundColorString ? Common.Color.parse(backgroundColorString) ?? Common.Color.parse('white') : null;
1745
+ const noteTaking = parsedManifest['note_taking'] || {};
1746
+ const newNoteUrl = noteTaking['new_note_url'];
1747
+ const hasNewNoteUrl = typeof newNoteUrl === 'string';
1748
+ const completeNewNoteUrl = hasNewNoteUrl ?
1749
+ (Common.ParsedURL.ParsedURL.completeURL(url, newNoteUrl) as Platform.DevToolsPath.UrlString) :
1750
+ null;
1751
+
1752
+ return {
1753
+ startUrl: startURL,
1754
+ completeStartUrl: completeURL,
1755
+ themeColor,
1756
+ backgroundColor,
1757
+ orientation: this.stringProperty(parsedManifest, 'orientation'),
1758
+ display: this.stringProperty(parsedManifest, 'display'),
1759
+ newNoteUrl,
1760
+ hasNewNoteUrl,
1761
+ completeNewNoteUrl,
1762
+ };
1763
+ }
1281
1764
 
1282
- overlayModel.setWindowControlsThemeColor(themeColor);
1765
+ private processProtocolHandlers(parsedManifest: Manifest, url: Platform.DevToolsPath.UrlString):
1766
+ ProtocolHandlersSectionData {
1767
+ return {
1768
+ protocolHandlers: parsedManifest['protocol_handlers'] || [],
1769
+ manifestLink: url,
1770
+ };
1283
1771
  }
1284
1772
  }
1285
1773
 
1286
1774
  export const enum Events {
1287
1775
  MANIFEST_DETECTED = 'ManifestDetected',
1288
- MANIFEST_RENDERED = 'ManifestRendered',
1289
1776
  }
1290
1777
 
1291
1778
  export interface EventTypes {
1292
1779
  [Events.MANIFEST_DETECTED]: boolean;
1293
- [Events.MANIFEST_RENDERED]: void;
1294
1780
  }