chrome-devtools-frontend 1.0.1667564 → 1.0.1669021

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 (211) hide show
  1. package/.agents/skills/devtools-ci/SKILL.md +65 -0
  2. package/.agents/skills/devtools-setting-migration/SKILL.md +290 -0
  3. package/.agents/skills/gerrit-cli/SKILL.md +59 -0
  4. package/.agents/skills/gerrit-cli/references/commands.md +93 -0
  5. package/.agents/skills/gerrit-cli/references/workflows.md +56 -0
  6. package/.agents/skills/gerrit-cli/scripts/gerrit_client_wrapper.py +47 -0
  7. package/.agents/skills/gerrit-cli/scripts/import_upstream.py +230 -0
  8. package/.agents/skills/ui-eng-vision-widget-promoter/SKILL.md +11 -4
  9. package/.agents/skills/ui-widgets/SKILL.md +25 -1
  10. package/docs/contributing/infrastructure.md +7 -1
  11. package/docs/ui_engineering.md +44 -1
  12. package/front_end/core/common/Settings.ts +11 -4
  13. package/front_end/core/host/AidaClient.ts +66 -47
  14. package/front_end/core/host/GcaClient.ts +4 -6
  15. package/front_end/core/host/ResourceLoader.ts +4 -2
  16. package/front_end/core/root/Runtime.ts +5 -5
  17. package/front_end/core/sdk/CSSModel.test.api.ts +102 -0
  18. package/front_end/core/sdk/CSSModel.ts +3 -2
  19. package/front_end/core/sdk/ConsoleModel.ts +3 -2
  20. package/front_end/core/sdk/DebuggerModel.ts +16 -7
  21. package/front_end/core/sdk/SDKSettings.ts +38 -0
  22. package/front_end/core/sdk/sdk-meta.ts +0 -120
  23. package/front_end/core/sdk/sdk.ts +2 -0
  24. package/front_end/entrypoints/main/MainImpl.ts +10 -3
  25. package/front_end/foundation/Universe.ts +7 -0
  26. package/front_end/generated/InspectorBackendCommands.ts +2 -2
  27. package/front_end/generated/protocol.ts +11 -3
  28. package/front_end/models/ai_assistance/AiAgent2.ts +1 -1
  29. package/front_end/models/ai_assistance/AiUtils.ts +67 -4
  30. package/front_end/models/ai_assistance/BuiltInAi.ts +11 -6
  31. package/front_end/models/ai_assistance/ChangeManager.ts +17 -1
  32. package/front_end/models/ai_assistance/StorageItem.ts +26 -5
  33. package/front_end/models/ai_code_completion/AiCodeCompletion.ts +2 -19
  34. package/front_end/models/ai_code_generation/AiCodeGeneration.ts +2 -3
  35. package/front_end/models/javascript_metadata/NativeFunctions.js +22 -9
  36. package/front_end/models/source_map_scopes/NamesResolver.ts +23 -17
  37. package/front_end/models/trace/handlers/RendererHandler.ts +20 -4
  38. package/front_end/models/trace/types/File.ts +1 -0
  39. package/front_end/models/workspace/UISourceCode.ts +0 -10
  40. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +61 -182
  41. package/front_end/panels/ai_assistance/components/ChatInput.ts +25 -8
  42. package/front_end/panels/ai_assistance/components/ChatMessage.ts +13 -69
  43. package/front_end/panels/ai_assistance/components/ChatView.ts +14 -8
  44. package/front_end/panels/ai_assistance/components/chatMessage.css +2 -20
  45. package/front_end/panels/application/ApplicationPanelSidebar.ts +2 -2
  46. package/front_end/panels/application/ApplicationPanelTreeElement.ts +41 -3
  47. package/front_end/panels/application/ServiceWorkerUpdateCycleView.ts +131 -134
  48. package/front_end/panels/application/ServiceWorkersView.ts +8 -8
  49. package/front_end/panels/application/application.ts +2 -0
  50. package/front_end/panels/application/components/AdsView.ts +10 -6
  51. package/front_end/panels/application/preloading/PreloadingView.ts +53 -45
  52. package/front_end/panels/application/preloading/components/PreloadingDetailsReportView.ts +111 -119
  53. package/front_end/panels/application/preloading/components/preloadingDetailsReportView.css +22 -24
  54. package/front_end/panels/application/serviceWorkerUpdateCycleView.css +105 -102
  55. package/front_end/panels/common/AiCodeCompletionDisclaimer.ts +107 -96
  56. package/front_end/panels/common/AiCodeCompletionSummaryToolbar.ts +17 -19
  57. package/front_end/panels/common/AiCodeCompletionTeaser.ts +6 -6
  58. package/front_end/panels/common/AiCodeGenerationTeaser.ts +28 -26
  59. package/front_end/panels/common/common.ts +1 -1
  60. package/front_end/panels/console/ConsoleInsightTeaser.ts +6 -8
  61. package/front_end/panels/console/ConsoleView.ts +6 -4
  62. package/front_end/panels/console/ConsoleViewMessage.ts +1 -1
  63. package/front_end/panels/console/console-meta.ts +28 -0
  64. package/front_end/panels/elements/StylesAiCodeCompletionProvider.ts +1 -1
  65. package/front_end/panels/elements/StylesSidebarPane.ts +3 -2
  66. package/front_end/panels/emulation/DeviceModeToolbar.ts +2 -2
  67. package/front_end/panels/emulation/DeviceModeView.ts +63 -104
  68. package/front_end/panels/emulation/DeviceModeWrapper.ts +18 -4
  69. package/front_end/panels/explain/components/ConsoleInsight.ts +4 -6
  70. package/front_end/panels/explain/explain-meta.ts +7 -49
  71. package/front_end/panels/network/NetworkDataGridNode.ts +1 -1
  72. package/front_end/panels/network/NetworkLogView.ts +1 -0
  73. package/front_end/panels/network/OriginalResourceChunkView.ts +340 -0
  74. package/front_end/panels/network/ResourceChunkView.ts +72 -46
  75. package/front_end/panels/network/ResourceDirectSocketChunkView.ts +3 -2
  76. package/front_end/panels/network/ResourceWebSocketFrameView.ts +3 -3
  77. package/front_end/panels/network/originalResourceChunkView.css +49 -0
  78. package/front_end/panels/screencast/ScreencastView.ts +401 -260
  79. package/front_end/panels/screencast/screencastView.css +113 -100
  80. package/front_end/panels/sensors/LocationsSettingsTab.ts +329 -135
  81. package/front_end/panels/sensors/locationsSettingsTab.css +78 -4
  82. package/front_end/panels/settings/AISettingsTab.ts +17 -100
  83. package/front_end/panels/snippets/ScriptSnippetFileSystem.ts +2 -2
  84. package/front_end/panels/snippets/SnippetsQuickOpen.ts +4 -4
  85. package/front_end/panels/sources/AddSourceMapURLDialog.ts +3 -3
  86. package/front_end/panels/sources/AiCodeCompletionPlugin.ts +4 -3
  87. package/front_end/panels/sources/BreakpointEditDialog.ts +14 -14
  88. package/front_end/panels/sources/BreakpointsView.ts +14 -14
  89. package/front_end/panels/sources/CallStackSidebarPane.ts +6 -6
  90. package/front_end/panels/sources/CategorizedBreakpointL10n.ts +18 -18
  91. package/front_end/panels/sources/CoveragePlugin.ts +7 -7
  92. package/front_end/panels/sources/DebuggerPausedMessage.ts +23 -23
  93. package/front_end/panels/sources/DebuggerPlugin.ts +31 -31
  94. package/front_end/panels/sources/FilteredUISourceCodeListProvider.ts +2 -2
  95. package/front_end/panels/sources/GoToLineQuickOpen.ts +10 -10
  96. package/front_end/panels/sources/NavigatorView.ts +34 -27
  97. package/front_end/panels/sources/OutlineQuickOpen.ts +3 -3
  98. package/front_end/panels/sources/PersistenceActions.ts +9 -9
  99. package/front_end/panels/sources/ProfilePlugin.ts +1 -1
  100. package/front_end/panels/sources/ScopeChainSidebarPane.ts +5 -5
  101. package/front_end/panels/sources/SnippetsPlugin.ts +2 -2
  102. package/front_end/panels/sources/SourcesNavigator.ts +20 -18
  103. package/front_end/panels/sources/SourcesPanel.ts +26 -25
  104. package/front_end/panels/sources/SourcesView.ts +5 -6
  105. package/front_end/panels/sources/TabbedEditorContainer.ts +3 -3
  106. package/front_end/panels/sources/ThreadsSidebarPane.ts +1 -1
  107. package/front_end/panels/sources/UISourceCodeFrame.ts +0 -2
  108. package/front_end/panels/sources/WatchExpressionsSidebarPane.ts +7 -7
  109. package/front_end/panels/sources/components/HeadersView.ts +2 -2
  110. package/front_end/panels/sources/sources-meta.ts +164 -71
  111. package/front_end/panels/sources/sources.ts +0 -2
  112. package/front_end/panels/timeline/TimelineHistoryManager.ts +14 -3
  113. package/front_end/panels/timeline/TimelineLoader.ts +72 -42
  114. package/front_end/panels/timeline/TimelinePanel.ts +29 -4
  115. package/front_end/panels/timeline/timelineHistoryManager.css +4 -0
  116. package/front_end/third_party/chromium/README.chromium +1 -1
  117. package/front_end/third_party/puppeteer/README.chromium +2 -2
  118. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.d.ts +187 -1
  119. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +91 -38
  120. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Browser.d.ts +145 -2
  121. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Browser.d.ts.map +1 -1
  122. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Browser.js +0 -2
  123. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Browser.js.map +1 -1
  124. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/BrowserContext.d.ts +0 -2
  125. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/BrowserContext.d.ts.map +1 -1
  126. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/BrowserContext.js +0 -2
  127. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/BrowserContext.js.map +1 -1
  128. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Dialog.d.ts +3 -2
  129. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Dialog.d.ts.map +1 -1
  130. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Dialog.js +8 -2
  131. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Dialog.js.map +1 -1
  132. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/JSHandle.d.ts +0 -2
  133. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/JSHandle.d.ts.map +1 -1
  134. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/JSHandle.js +0 -2
  135. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/JSHandle.js.map +1 -1
  136. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Page.d.ts +1 -3
  137. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Page.d.ts.map +1 -1
  138. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Page.js +0 -2
  139. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Page.js.map +1 -1
  140. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Realm.d.ts +0 -1
  141. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Realm.d.ts.map +1 -1
  142. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Realm.js +0 -1
  143. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Realm.js.map +1 -1
  144. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/BidiOverCdp.d.ts.map +1 -1
  145. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/BidiOverCdp.js +2 -2
  146. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/BidiOverCdp.js.map +1 -1
  147. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/Browser.d.ts +5 -1
  148. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/Browser.d.ts.map +1 -1
  149. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/Browser.js +12 -0
  150. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/Browser.js.map +1 -1
  151. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/HTTPResponse.d.ts.map +1 -1
  152. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/HTTPResponse.js +4 -1
  153. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/HTTPResponse.js.map +1 -1
  154. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Browser.d.ts +5 -1
  155. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Browser.d.ts.map +1 -1
  156. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Browser.js +49 -0
  157. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Browser.js.map +1 -1
  158. package/front_end/third_party/puppeteer/package/lib/puppeteer/common/EventEmitter.d.ts +0 -6
  159. package/front_end/third_party/puppeteer/package/lib/puppeteer/common/EventEmitter.d.ts.map +1 -1
  160. package/front_end/third_party/puppeteer/package/lib/puppeteer/common/EventEmitter.js +0 -6
  161. package/front_end/third_party/puppeteer/package/lib/puppeteer/common/EventEmitter.js.map +1 -1
  162. package/front_end/third_party/puppeteer/package/lib/puppeteer/generated/injected.d.ts +1 -1
  163. package/front_end/third_party/puppeteer/package/lib/puppeteer/generated/injected.d.ts.map +1 -1
  164. package/front_end/third_party/puppeteer/package/lib/puppeteer/generated/injected.js +1 -1
  165. package/front_end/third_party/puppeteer/package/lib/puppeteer/generated/injected.js.map +1 -1
  166. package/front_end/third_party/puppeteer/package/lib/puppeteer/injected/util.d.ts.map +1 -1
  167. package/front_end/third_party/puppeteer/package/lib/puppeteer/injected/util.js +3 -0
  168. package/front_end/third_party/puppeteer/package/lib/puppeteer/injected/util.js.map +1 -1
  169. package/front_end/third_party/puppeteer/package/lib/puppeteer/node/ScreenRecorder.d.ts +0 -3
  170. package/front_end/third_party/puppeteer/package/lib/puppeteer/node/ScreenRecorder.d.ts.map +1 -1
  171. package/front_end/third_party/puppeteer/package/lib/puppeteer/node/ScreenRecorder.js +0 -3
  172. package/front_end/third_party/puppeteer/package/lib/puppeteer/node/ScreenRecorder.js.map +1 -1
  173. package/front_end/third_party/puppeteer/package/lib/puppeteer/revisions.d.ts +3 -3
  174. package/front_end/third_party/puppeteer/package/lib/puppeteer/revisions.js +3 -3
  175. package/front_end/third_party/puppeteer/package/lib/puppeteer/util/disposable.d.ts +2 -2
  176. package/front_end/third_party/puppeteer/package/lib/puppeteer/util/disposable.js +2 -2
  177. package/front_end/third_party/puppeteer/package/lib/puppeteer/util/version.d.ts +1 -1
  178. package/front_end/third_party/puppeteer/package/lib/puppeteer/util/version.js +1 -1
  179. package/front_end/third_party/puppeteer/package/lib/types.d.ts +187 -1
  180. package/front_end/third_party/puppeteer/package/package.json +5 -5
  181. package/front_end/third_party/puppeteer/package/src/api/Browser.ts +155 -2
  182. package/front_end/third_party/puppeteer/package/src/api/BrowserContext.ts +0 -2
  183. package/front_end/third_party/puppeteer/package/src/api/Dialog.ts +10 -2
  184. package/front_end/third_party/puppeteer/package/src/api/JSHandle.ts +0 -2
  185. package/front_end/third_party/puppeteer/package/src/api/Page.ts +1 -3
  186. package/front_end/third_party/puppeteer/package/src/api/Realm.ts +0 -1
  187. package/front_end/third_party/puppeteer/package/src/bidi/BidiOverCdp.ts +4 -2
  188. package/front_end/third_party/puppeteer/package/src/bidi/Browser.ts +17 -0
  189. package/front_end/third_party/puppeteer/package/src/bidi/HTTPResponse.ts +5 -4
  190. package/front_end/third_party/puppeteer/package/src/cdp/Browser.ts +66 -0
  191. package/front_end/third_party/puppeteer/package/src/common/EventEmitter.ts +0 -6
  192. package/front_end/third_party/puppeteer/package/src/generated/injected.ts +1 -1
  193. package/front_end/third_party/puppeteer/package/src/injected/util.ts +4 -1
  194. package/front_end/third_party/puppeteer/package/src/node/ScreenRecorder.ts +0 -3
  195. package/front_end/third_party/puppeteer/package/src/revisions.ts +3 -3
  196. package/front_end/third_party/puppeteer/package/src/util/disposable.ts +2 -2
  197. package/front_end/third_party/puppeteer/package/src/util/version.ts +1 -1
  198. package/front_end/ui/components/text_editor/AiCodeCompletionProvider.ts +9 -7
  199. package/front_end/ui/components/text_editor/AiCodeGenerationProvider.ts +7 -8
  200. package/front_end/ui/legacy/Dialog.ts +103 -4
  201. package/front_end/ui/legacy/InspectorView.ts +1 -5
  202. package/front_end/ui/legacy/PopoverHelper.ts +5 -5
  203. package/front_end/ui/legacy/TabbedPane.ts +7 -2
  204. package/front_end/ui/legacy/components/cookie_table/CookiesTable.ts +40 -24
  205. package/front_end/ui/legacy/components/data_grid/DataGrid.ts +1 -0
  206. package/front_end/ui/legacy/components/data_grid/DataGridElement.ts +8 -4
  207. package/front_end/ui/legacy/components/perf_ui/FlameChart.ts +14 -7
  208. package/front_end/ui/legacy/components/quick_open/CommandMenu.ts +11 -1
  209. package/front_end/ui/visual_logging/KnownContextValues.ts +3 -0
  210. package/package.json +1 -1
  211. package/front_end/panels/sources/AiWarningInfobarPlugin.ts +0 -78
@@ -4,11 +4,21 @@
4
4
 
5
5
  import * as Common from '../../core/common/common.js';
6
6
  import * as i18n from '../../core/i18n/i18n.js';
7
+ import * as SDK from '../../core/sdk/sdk.js';
7
8
  import * as UI from '../../ui/legacy/legacy.js';
9
+ import * as SettingsUI from '../../ui/settings/settings.js';
8
10
 
9
11
  import type * as Console from './console.js';
10
12
 
11
13
  const UIStrings = {
14
+ /**
15
+ * @description Title of a setting under the Console category that can be invoked through the Command Menu.
16
+ */
17
+ preserveLogUponNavigation: 'Keep log on navigation',
18
+ /**
19
+ * @description Title of a setting under the Console category that can be invoked through the Command Menu.
20
+ */
21
+ doNotPreserveLogUponNavigation: 'Don’t keep log on navigation',
12
22
  /**
13
23
  * @description Title of the Console tool.
14
24
  */
@@ -201,6 +211,9 @@ UI.ActionRegistration.registerActionExtension({
201
211
  UI.ActionRegistration.KeybindSet.DEVTOOLS_DEFAULT,
202
212
  UI.ActionRegistration.KeybindSet.VS_CODE,
203
213
  ],
214
+ // The Cmd+` combination is used in macOS to activate the next
215
+ // open window in the front app. Therefore it was not implemented
216
+ // below.
204
217
  },
205
218
  ],
206
219
  });
@@ -446,6 +459,21 @@ Common.Settings.registerSettingExtension({
446
459
  defaultValue: true,
447
460
  });
448
461
 
462
+ SettingsUI.SettingUIRegistration.register(SDK.SDKSettings.preserveConsoleLogSettingDescriptor, {
463
+ category: Common.Settings.SettingCategory.CONSOLE,
464
+ title: i18nLazyString(UIStrings.preserveLogUponNavigation),
465
+ options: [
466
+ {
467
+ value: true,
468
+ title: i18nLazyString(UIStrings.preserveLogUponNavigation),
469
+ },
470
+ {
471
+ value: false,
472
+ title: i18nLazyString(UIStrings.doNotPreserveLogUponNavigation),
473
+ },
474
+ ],
475
+ });
476
+
449
477
  Common.Revealer.registerRevealer({
450
478
  contextTypes() {
451
479
  return [
@@ -76,7 +76,7 @@ export class StylesAiCodeCompletionProvider {
76
76
  aidaClient: this.#aidaClient,
77
77
  serverSideLoggingEnabled: !Root.Runtime.hostConfig.aidaAvailability?.disallowLogging,
78
78
  },
79
- this.#aiCodeCompletionConfig.panel,
79
+
80
80
  undefined, stopSequences);
81
81
  this.#aiCodeCompletionConfig.onFeatureEnabled();
82
82
  }
@@ -300,7 +300,8 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
300
300
  onSuggestionAccepted: this.#onAiCodeCompletionSuggestionAccepted.bind(this),
301
301
  onRequestTriggered: this.#onAiCodeCompletionRequestTriggered.bind(this),
302
302
  onResponseReceived: this.#onAiCodeCompletionResponseReceived.bind(this),
303
- panel: AiCodeCompletion.AiCodeCompletion.ContextFlavor.STYLES,
303
+ disclaimerTooltipId: DISCLAIMER_TOOLTIP_ID,
304
+ disclaimerTextVariant: 'styles',
304
305
  };
305
306
  this.aiCodeCompletionProvider =
306
307
  StylesAiCodeCompletionProvider.StylesAiCodeCompletionProvider.createInstance(this.aiCodeCompletionConfig);
@@ -1618,7 +1619,7 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
1618
1619
  citationsTooltipId: CITATIONS_TOOLTIP_ID,
1619
1620
  disclaimerTooltipId: DISCLAIMER_TOOLTIP_ID,
1620
1621
  spinnerTooltipId: SPINNER_TOOLTIP_ID,
1621
- panel: AiCodeCompletion.AiCodeCompletion.ContextFlavor.STYLES,
1622
+ disclaimerTextVariant: 'styles',
1622
1623
  });
1623
1624
  const containingPane = this.contentElement.enclosingNodeOrSelfWithClass('style-panes-wrapper') as HTMLElement;
1624
1625
  this.#aiCodeCompletionSummaryToolbarContainer =
@@ -512,8 +512,8 @@ export class DeviceModeToolbar extends UI.Widget.Widget {
512
512
  super.wasShown();
513
513
  // TODO(crbug.com/407750803): Revisit once DeviceModeView is migrated.
514
514
  this.performUpdate(); // Trigger a manual update eagerly, DeviceModeView needs to measure our height.
515
+ this.restore();
515
516
  }
516
-
517
517
  override performUpdate(): void {
518
518
  if (!this.model) {
519
519
  return;
@@ -1078,7 +1078,7 @@ export class DeviceModeToolbar extends UI.Widget.Widget {
1078
1078
  return !this.model ? '' : `${(this.model.scale() * 100).toFixed(0)}`;
1079
1079
  }
1080
1080
 
1081
- restore(): void {
1081
+ private restore(): void {
1082
1082
  if (!this.model) {
1083
1083
  return;
1084
1084
  }
@@ -1,15 +1,16 @@
1
1
  // Copyright 2015 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 */
5
4
 
6
5
  import * as Common from '../../core/common/common.js';
7
6
  import * as Host from '../../core/host/host.js';
8
7
  import * as i18n from '../../core/i18n/i18n.js';
9
8
  import * as Platform from '../../core/platform/platform.js';
9
+ import * as TextUtils from '../../core/text_utils/text_utils.js';
10
10
  import type * as Protocol from '../../generated/protocol.js';
11
11
  import * as EmulationModel from '../../models/emulation/emulation.js';
12
12
  import * as Geometry from '../../models/geometry/geometry.js';
13
+ import * as Workspace from '../../models/workspace/workspace.js';
13
14
  import * as UI from '../../ui/legacy/legacy.js';
14
15
  import {Directives, html, nothing, render} from '../../ui/lit/lit.js';
15
16
  import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
@@ -60,16 +61,6 @@ const UIStrings = {
60
61
  const str_ = i18n.i18n.registerUIStrings('panels/emulation/DeviceModeView.ts', UIStrings);
61
62
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
62
63
 
63
- export interface DeviceModeViewRefs {
64
- toolbar: DeviceModeToolbar;
65
- bottomRightResizerElement: HTMLElement;
66
- bottomLeftResizerElement: HTMLElement;
67
- rightResizerElement: HTMLElement;
68
- leftResizerElement: HTMLElement;
69
- bottomResizerElement: HTMLElement;
70
- pageArea: HTMLElement;
71
- }
72
-
73
64
  export interface DeviceModeViewInput {
74
65
  model: EmulationModel.DeviceModeModel.DeviceModeModel;
75
66
  showMediaInspectorSetting: Common.Settings.Setting<boolean>;
@@ -78,10 +69,12 @@ export interface DeviceModeViewInput {
78
69
  outlineImageLoaded: boolean;
79
70
  screenImage: string;
80
71
  screenImageLoaded: boolean;
72
+ resizable: boolean;
81
73
  showRulers: boolean;
82
74
  showMediaInspector: boolean;
83
75
  scale: number;
84
76
  cachedCssScreenRect?: EmulationModel.DeviceModeModel.Rect;
77
+ cachedCssVisiblePageRect?: EmulationModel.DeviceModeModel.Rect;
85
78
  cachedOutlineRect?: EmulationModel.DeviceModeModel.Rect;
86
79
  onApplyPresetSize: (size: number, e: Event) => void;
87
80
  bottomRightResizer: UI.ResizerWidget.ResizerWidget;
@@ -101,7 +94,7 @@ export interface DeviceModeViewInput {
101
94
 
102
95
  export type DeviceModeViewView = (
103
96
  input: DeviceModeViewInput,
104
- output: DeviceModeViewRefs,
97
+ output: undefined,
105
98
  target: HTMLElement,
106
99
  ) => void;
107
100
 
@@ -130,7 +123,7 @@ function resizerRef(
130
123
 
131
124
  export const DEFAULT_DEVICE_MODE_VIEW: DeviceModeViewView = (
132
125
  input: DeviceModeViewInput,
133
- output: DeviceModeViewRefs,
126
+ _output: undefined,
134
127
  target: HTMLElement,
135
128
  ): void => {
136
129
  const sizes = [320, 375, 425, 768, 1024, 1440, 2560];
@@ -145,8 +138,7 @@ export const DEFAULT_DEVICE_MODE_VIEW: DeviceModeViewView = (
145
138
  ];
146
139
  // clang-format off
147
140
  render(html`
148
- <devtools-widget ${UI.Widget.widget(DeviceModeToolbar, {model: input.model})}
149
- ${UI.Widget.widgetRef(DeviceModeToolbar, t => { output.toolbar = t; })}></devtools-widget>
141
+ ${UI.Widget.widget(DeviceModeToolbar, {model: input.model})}
150
142
  <div class=${classMap({
151
143
  'device-mode-content-clip': true,
152
144
  vbox: true,
@@ -199,33 +191,44 @@ export const DEFAULT_DEVICE_MODE_VIEW: DeviceModeViewView = (
199
191
  @load=${(): void => input.onScreenImageLoaded(true)}
200
192
  @error=${(): void => input.onScreenImageLoaded(false)}>
201
193
  <div class="device-mode-resizer device-mode-bottom-right-resizer"
194
+ ?hidden=${!input.resizable}
202
195
  jslog=${VisualLogging.slider('device-mode-resizer').track({drag: true})}
203
196
  ${ref(input.bottomRightResizerRef)}>
204
197
  <div></div>
205
198
  </div>
206
199
  <div class="device-mode-resizer device-mode-bottom-left-resizer"
200
+ ?hidden=${!input.resizable}
207
201
  jslog=${VisualLogging.slider('device-mode-resizer').track({drag: true})}
208
202
  ${ref(input.bottomLeftResizerRef)}>
209
203
  <div></div>
210
204
  </div>
211
205
  <div class="device-mode-resizer device-mode-right-resizer"
206
+ ?hidden=${!input.resizable}
212
207
  jslog=${VisualLogging.slider('device-mode-resizer').track({drag: true})}
213
208
  ${ref(input.rightResizerRef)}>
214
209
  <div></div>
215
210
  </div>
216
211
  <div class="device-mode-resizer device-mode-left-resizer"
212
+ ?hidden=${!input.resizable}
217
213
  jslog=${VisualLogging.slider('device-mode-resizer').track({drag: true})}
218
214
  ${ref(input.leftResizerRef)}>
219
215
  <div></div>
220
216
  </div>
221
217
  <div class="device-mode-resizer device-mode-bottom-resizer"
218
+ ?hidden=${!input.resizable}
222
219
  jslog=${VisualLogging.slider('device-mode-resizer').track({drag: true})}
223
220
  title=${i18nString(UIStrings.doubleclickForFullHeight)}
224
221
  ${ref(input.bottomResizerRef)}
225
222
  @dblclick=${input.onDoubleclickBottomResizer}>
226
223
  <div></div>
227
224
  </div>
228
- <div class="device-mode-page-area" ${ref((el?: Element) => { if (el instanceof HTMLElement) { output.pageArea = el; } })}><slot></slot></div>
225
+ <div class="device-mode-page-area"
226
+ style=${styleMap(input.cachedCssVisiblePageRect ? {
227
+ left: `${input.cachedCssVisiblePageRect.left}px`,
228
+ top: `${input.cachedCssVisiblePageRect.top}px`,
229
+ width: `${input.cachedCssVisiblePageRect.width}px`,
230
+ height: `${input.cachedCssVisiblePageRect.height}px`,
231
+ } : {})}><slot></slot></div>
229
232
  </div>
230
233
  ${input.showRulers ? html`
231
234
  <devtools-widget class="device-mode-ruler-top device-mode-ruler"
@@ -247,7 +250,11 @@ export const DEFAULT_DEVICE_MODE_VIEW: DeviceModeViewView = (
247
250
  ` : nothing}
248
251
  </div>
249
252
  </div>
250
- `, target);
253
+ `, target, {
254
+ container: {
255
+ classes: ['device-mode-view'],
256
+ },
257
+ });
251
258
  // clang-format on
252
259
  };
253
260
 
@@ -256,34 +263,16 @@ export class DeviceModeView extends UI.Widget.VBox {
256
263
  private model: EmulationModel.DeviceModeModel.DeviceModeModel;
257
264
  private showMediaInspectorSetting: Common.Settings.Setting<boolean>;
258
265
  private showRulersSetting: Common.Settings.Setting<boolean>;
259
- pageArea!: HTMLElement;
260
- rightResizerElement!: HTMLElement;
261
- leftResizerElement!: HTMLElement;
262
- bottomResizerElement!: HTMLElement;
263
- bottomRightResizerElement!: HTMLElement;
264
- bottomLeftResizerElement!: HTMLElement;
265
266
  private readonly bottomRightResizer = this.createResizer(2, 1);
266
267
  private readonly bottomLeftResizer = this.createResizer(-2, 1);
267
268
  private readonly rightResizer = this.createResizer(2, 0);
268
269
  private readonly leftResizer = this.createResizer(-2, 0);
269
270
  private readonly bottomResizer = this.createResizer(0, 1);
270
- private readonly bottomRightResizerRef = resizerRef(this.bottomRightResizer, el => {
271
- this.bottomRightResizerElement = el;
272
- });
273
- private readonly bottomLeftResizerRef = resizerRef(this.bottomLeftResizer, el => {
274
- this.bottomLeftResizerElement = el;
275
- });
276
- private readonly rightResizerRef = resizerRef(this.rightResizer, el => {
277
- this.rightResizerElement = el;
278
- });
279
- private readonly leftResizerRef = resizerRef(this.leftResizer, el => {
280
- this.leftResizerElement = el;
281
- });
282
- private readonly bottomResizerRef = resizerRef(this.bottomResizer, el => {
283
- this.bottomResizerElement = el;
284
- });
285
- private cachedResizable!: boolean|undefined;
286
- toolbar!: DeviceModeToolbar;
271
+ private readonly bottomRightResizerRef = resizerRef(this.bottomRightResizer);
272
+ private readonly bottomLeftResizerRef = resizerRef(this.bottomLeftResizer);
273
+ private readonly rightResizerRef = resizerRef(this.rightResizer);
274
+ private readonly leftResizerRef = resizerRef(this.leftResizer);
275
+ private readonly bottomResizerRef = resizerRef(this.bottomResizer);
287
276
  private slowPositionStart?: {
288
277
  x: number,
289
278
  y: number,
@@ -295,8 +284,6 @@ export class DeviceModeView extends UI.Widget.VBox {
295
284
  private cachedMediaInspectorVisible?: boolean;
296
285
  private cachedShowRulers?: boolean;
297
286
  private cachedScale?: number;
298
- private handleWidth?: number;
299
- private handleHeight?: number;
300
287
  #outlineImageLoaded = false;
301
288
  #lastOutlineImageSrc?: string;
302
289
  #screenImageLoaded = false;
@@ -308,7 +295,6 @@ export class DeviceModeView extends UI.Widget.VBox {
308
295
  this.#view = view;
309
296
 
310
297
  this.setMinimumSize(150, 150);
311
- this.element.classList.add('device-mode-view');
312
298
  this.registerRequiredCSS(deviceModeViewStyles);
313
299
 
314
300
  this.model = EmulationModel.DeviceModeModel.DeviceModeModel.instance();
@@ -339,11 +325,13 @@ export class DeviceModeView extends UI.Widget.VBox {
339
325
  outlineImageLoaded: this.#outlineImageLoaded,
340
326
  screenImage: this.model.screenImage(),
341
327
  screenImageLoaded: this.#screenImageLoaded,
328
+ resizable: this.model.type() === EmulationModel.DeviceModeModel.Type.Responsive,
342
329
  showRulers: this.showRulersSetting.get() && this.model.type() !== EmulationModel.DeviceModeModel.Type.None,
343
330
  showMediaInspector:
344
331
  this.showMediaInspectorSetting.get() && this.model.type() !== EmulationModel.DeviceModeModel.Type.None,
345
332
  scale: this.model.scale(),
346
333
  cachedCssScreenRect: this.cachedCssScreenRect,
334
+ cachedCssVisiblePageRect: this.cachedCssVisiblePageRect,
347
335
  cachedOutlineRect: this.cachedOutlineRect,
348
336
  onApplyPresetSize: (width: number, e: Event): void => {
349
337
  this.model.emulate(EmulationModel.DeviceModeModel.Type.Responsive, null, null);
@@ -364,7 +352,7 @@ export class DeviceModeView extends UI.Widget.VBox {
364
352
  onOutlineImageLoaded: (success: boolean): void => this.onOutlineImageLoaded(success),
365
353
  onScreenImageLoaded: (success: boolean): void => this.onScreenImageLoaded(success),
366
354
  };
367
- this.#view(input, this, this.contentElement);
355
+ this.#view(input, undefined, this.contentElement);
368
356
  }
369
357
 
370
358
  private onOutlineImageLoaded(success: boolean): void {
@@ -380,6 +368,7 @@ export class DeviceModeView extends UI.Widget.VBox {
380
368
  this.requestUpdate();
381
369
  }
382
370
  }
371
+
383
372
  private createResizer(widthFactor: number, heightFactor: number): UI.ResizerWidget.ResizerWidget {
384
373
  const resizer = new UI.ResizerWidget.ResizerWidget();
385
374
  let cursor: 'nwse-resize'|'nesw-resize'|('ew-resize' | 'ns-resize') = widthFactor ? 'ew-resize' : 'ns-resize';
@@ -450,13 +439,6 @@ export class DeviceModeView extends UI.Widget.VBox {
450
439
  }
451
440
 
452
441
  private updateUI(): void {
453
- function applyRect(element: HTMLElement, rect: EmulationModel.DeviceModeModel.Rect): void {
454
- element.style.left = rect.left + 'px';
455
- element.style.top = rect.top + 'px';
456
- element.style.width = rect.width + 'px';
457
- element.style.height = rect.height + 'px';
458
- }
459
-
460
442
  if (!this.isShowing()) {
461
443
  return;
462
444
  }
@@ -474,7 +456,6 @@ export class DeviceModeView extends UI.Widget.VBox {
474
456
 
475
457
  const cssVisiblePageRect = this.model.visiblePageRect().scale(1 / zoomFactor);
476
458
  if (!this.cachedCssVisiblePageRect || !cssVisiblePageRect.isEqual(this.cachedCssVisiblePageRect)) {
477
- applyRect(this.pageArea, cssVisiblePageRect);
478
459
  callDoResize = true;
479
460
  this.cachedCssVisiblePageRect = cssVisiblePageRect;
480
461
  }
@@ -488,16 +469,6 @@ export class DeviceModeView extends UI.Widget.VBox {
488
469
  }
489
470
  }
490
471
 
491
- const resizable = this.model.type() === EmulationModel.DeviceModeModel.Type.Responsive;
492
- if (resizable !== this.cachedResizable) {
493
- this.rightResizerElement.classList.toggle('hidden', !resizable);
494
- this.leftResizerElement.classList.toggle('hidden', !resizable);
495
- this.bottomResizerElement.classList.toggle('hidden', !resizable);
496
- this.bottomRightResizerElement.classList.toggle('hidden', !resizable);
497
- this.bottomLeftResizerElement.classList.toggle('hidden', !resizable);
498
- this.cachedResizable = resizable;
499
- }
500
-
501
472
  const mediaInspectorVisible =
502
473
  this.showMediaInspectorSetting.get() && this.model.type() !== EmulationModel.DeviceModeModel.Type.None;
503
474
  if (mediaInspectorVisible !== this.cachedMediaInspectorVisible) {
@@ -518,7 +489,6 @@ export class DeviceModeView extends UI.Widget.VBox {
518
489
  }
519
490
 
520
491
  this.requestUpdate();
521
- this.toolbar.requestUpdate();
522
492
  if (callDoResize) {
523
493
  this.doResize();
524
494
  }
@@ -545,29 +515,18 @@ export class DeviceModeView extends UI.Widget.VBox {
545
515
  }
546
516
  const zoomFactor = UI.ZoomManager.ZoomManager.instance().zoomFactor();
547
517
  const rect = contentArea.getBoundingClientRect();
518
+ const handleWidth = this.contentElement.querySelector<HTMLElement>('.device-mode-right-resizer')?.offsetWidth || 20;
519
+ const handleHeight =
520
+ this.contentElement.querySelector<HTMLElement>('.device-mode-bottom-resizer')?.offsetHeight || 20;
548
521
  const availableSize =
549
522
  new Geometry.Size(Math.max(rect.width * zoomFactor, 1), Math.max(rect.height * zoomFactor, 1));
550
- const preferredSize = new Geometry.Size(
551
- Math.max((rect.width - 2 * (this.handleWidth || 0)) * zoomFactor, 1),
552
- Math.max((rect.height - (this.handleHeight || 0)) * zoomFactor, 1));
523
+ const preferredSize = new Geometry.Size(Math.max((rect.width - 2 * handleWidth) * zoomFactor, 1),
524
+ Math.max((rect.height - handleHeight) * zoomFactor, 1));
553
525
  this.model.setAvailableSize(availableSize, preferredSize);
554
526
  }
555
527
 
556
- private measureHandles(): void {
557
- const hidden = this.rightResizerElement.classList.contains('hidden');
558
- this.rightResizerElement.classList.toggle('hidden', false);
559
- this.bottomResizerElement.classList.toggle('hidden', false);
560
- this.handleWidth = this.rightResizerElement.offsetWidth;
561
- this.handleHeight = this.bottomResizerElement.offsetHeight;
562
- this.rightResizerElement.classList.toggle('hidden', hidden);
563
- this.bottomResizerElement.classList.toggle('hidden', hidden);
564
- }
565
-
566
528
  private zoomChanged(): void {
567
- delete this.handleWidth;
568
- delete this.handleHeight;
569
529
  if (this.isShowing()) {
570
- this.measureHandles();
571
530
  this.contentAreaResized();
572
531
  }
573
532
  }
@@ -580,8 +539,6 @@ export class DeviceModeView extends UI.Widget.VBox {
580
539
 
581
540
  override wasShown(): void {
582
541
  super.wasShown();
583
- this.measureHandles();
584
- this.toolbar.restore();
585
542
  }
586
543
 
587
544
  override willHide(): void {
@@ -609,11 +566,10 @@ export class DeviceModeView extends UI.Widget.VBox {
609
566
  const contentLeft = screenRect.left + visiblePageRect.left - outlineRect.left;
610
567
  const contentTop = screenRect.top + visiblePageRect.top - outlineRect.top;
611
568
 
612
- const canvas = document.createElement('canvas');
613
- canvas.width = Math.floor(outlineRect.width);
614
- // Cap the height to not hit the GPU limit.
615
- // https://crbug.com/1260828
616
- canvas.height = Math.min((1 << 14), Math.floor(outlineRect.height));
569
+ const canvas = new OffscreenCanvas(Math.floor(outlineRect.width),
570
+ // Cap the height to not hit the GPU limit.
571
+ // https://crbug.com/1260828
572
+ Math.min((1 << 14), Math.floor(outlineRect.height)));
617
573
  const ctx = canvas.getContext('2d', {willReadFrequently: true});
618
574
  if (!ctx) {
619
575
  throw new Error('Could not get 2d context from canvas.');
@@ -627,7 +583,7 @@ export class DeviceModeView extends UI.Widget.VBox {
627
583
  await this.paintImage(ctx, this.model.screenImage(), screenRect.relativeTo(outlineRect));
628
584
  }
629
585
  ctx.drawImage(pageImage, Math.floor(contentLeft), Math.floor(contentTop));
630
- this.saveScreenshot((canvas));
586
+ void this.saveScreenshot(canvas);
631
587
  };
632
588
  }
633
589
 
@@ -651,23 +607,22 @@ export class DeviceModeView extends UI.Widget.VBox {
651
607
  const pageImage = new Image();
652
608
  pageImage.src = 'data:image/png;base64,' + screenshot;
653
609
  pageImage.onload = () => {
654
- const canvas = document.createElement('canvas');
655
- canvas.width = pageImage.naturalWidth;
656
- // Cap the height to not hit the GPU limit.
657
- // https://crbug.com/1260828
658
- canvas.height = Math.min((1 << 14), Math.floor(pageImage.naturalHeight));
610
+ const canvas = new OffscreenCanvas(pageImage.naturalWidth,
611
+ // Cap the height to not hit the GPU limit.
612
+ // https://crbug.com/1260828
613
+ Math.min((1 << 14), Math.floor(pageImage.naturalHeight)));
659
614
  const ctx = canvas.getContext('2d', {willReadFrequently: true});
660
615
  if (!ctx) {
661
616
  throw new Error('Could not get 2d context for base64 screenshot.');
662
617
  }
663
618
  ctx.imageSmoothingEnabled = false;
664
619
  ctx.drawImage(pageImage, 0, 0);
665
- this.saveScreenshot((canvas));
620
+ void this.saveScreenshot(canvas);
666
621
  };
667
622
  }
668
623
 
669
- private paintImage(ctx: CanvasRenderingContext2D, src: string, rect: EmulationModel.DeviceModeModel.Rect):
670
- Promise<void> {
624
+ private paintImage(ctx: OffscreenCanvasRenderingContext2D|CanvasRenderingContext2D, src: string,
625
+ rect: EmulationModel.DeviceModeModel.Rect): Promise<void> {
671
626
  return new Promise(resolve => {
672
627
  const image = new Image();
673
628
  image.crossOrigin = 'Anonymous';
@@ -680,7 +635,7 @@ export class DeviceModeView extends UI.Widget.VBox {
680
635
  });
681
636
  }
682
637
 
683
- private saveScreenshot(canvas: HTMLCanvasElement): void {
638
+ private async saveScreenshot(canvas: OffscreenCanvas): Promise<void> {
684
639
  const url = this.model.inspectedURL();
685
640
  let fileName = '';
686
641
  if (url) {
@@ -692,15 +647,19 @@ export class DeviceModeView extends UI.Widget.VBox {
692
647
  if (device && this.model.type() === EmulationModel.DeviceModeModel.Type.Device) {
693
648
  fileName += `(${device.title})`;
694
649
  }
695
- const link = document.createElement('a');
696
- link.download = fileName + '.png';
697
- canvas.toBlob(blob => {
698
- if (blob === null) {
699
- return;
700
- }
701
- link.href = URL.createObjectURL(blob);
702
- link.click();
650
+ fileName += '.png';
651
+ const blob = await canvas.convertToBlob({type: 'image/png'});
652
+ const dataUrl = await new Promise<string>((resolve, reject) => {
653
+ const reader = new FileReader();
654
+ reader.onloadend = () => resolve(reader.result as string);
655
+ reader.onerror = reject;
656
+ reader.readAsDataURL(blob);
703
657
  });
658
+ const base64 = dataUrl.slice(dataUrl.indexOf(',') + 1);
659
+ const contentData = new TextUtils.ContentData.ContentData(base64, /* isBase64=*/ true, 'image/png');
660
+ await Workspace.FileManager.FileManager.instance().save(fileName as Platform.DevToolsPath.RawPathString,
661
+ contentData, /* forceSaveAs=*/ true);
662
+ Workspace.FileManager.FileManager.instance().close(fileName as Platform.DevToolsPath.RawPathString);
704
663
  }
705
664
  }
706
665
 
@@ -138,6 +138,8 @@ export class ActionDelegate implements UI.ActionRegistration.ActionDelegate {
138
138
  throw new Error(`Unable to get box model of the node: ${new Error().stack}`);
139
139
  }
140
140
  const nodeBorderQuad = nodeBoxModel.border;
141
+ // Calculate the rendered bounding box from the border quad to account for CSS scaling and transforms (e.g. zoom or transform: scale).
142
+ const {minX, maxX, minY, maxY} = getQuadBoundingBox(nodeBorderQuad);
141
143
 
142
144
  // Get Layout Metrics to account for the Visual Viewport scroll and zoom.
143
145
  const metrics = await node.domModel().target().pageAgent().invoke_getLayoutMetrics();
@@ -155,10 +157,10 @@ export class ActionDelegate implements UI.ActionRegistration.ActionDelegate {
155
157
  // Assemble the final Clip.
156
158
  // The absolute coordinates are: Global (OOPiF) + Viewport Scroll + Local Node Position (Border Box).
157
159
  const clip = {
158
- x: oopifOffsetX + scrollX + nodeBorderQuad[0],
159
- y: oopifOffsetY + scrollY + nodeBorderQuad[1],
160
- width: nodeBoxModel.width,
161
- height: nodeBoxModel.height,
160
+ x: oopifOffsetX + scrollX + minX,
161
+ y: oopifOffsetY + scrollY + minY,
162
+ width: maxX - minX,
163
+ height: maxY - minY,
162
164
  scale: 1,
163
165
  };
164
166
 
@@ -249,3 +251,15 @@ async function getOopifOffset(target: SDK.Target.Target|null): Promise<{x: numbe
249
251
  y: iframeContentY + scrollY + parentOffset.y,
250
252
  };
251
253
  }
254
+
255
+ /**
256
+ * Calculate the axis-aligned bounding box for a Quad [x1, y1, x2, y2, x3, y3, x4, y4].
257
+ * This accounts for CSS scaling and transforms (e.g. zoom, transform: scale).
258
+ */
259
+ function getQuadBoundingBox(quad: Protocol.DOM.Quad): {minX: number, maxX: number, minY: number, maxY: number} {
260
+ const minX = Math.min(quad[0], quad[2], quad[4], quad[6]);
261
+ const maxX = Math.max(quad[0], quad[2], quad[4], quad[6]);
262
+ const minY = Math.min(quad[1], quad[3], quad[5], quad[7]);
263
+ const maxY = Math.max(quad[1], quad[3], quad[5], quad[7]);
264
+ return {minX, maxX, minY, maxY};
265
+ }
@@ -10,6 +10,7 @@ import * as Host from '../../../core/host/host.js';
10
10
  import * as i18n from '../../../core/i18n/i18n.js';
11
11
  import type * as Platform from '../../../core/platform/platform.js';
12
12
  import * as Root from '../../../core/root/root.js';
13
+ import * as AiAssistanceModel from '../../../models/ai_assistance/ai_assistance.js';
13
14
  import * as Marked from '../../../third_party/marked/marked.js';
14
15
  import * as Buttons from '../../../ui/components/buttons/buttons.js';
15
16
  import * as Input from '../../../ui/components/input/input.js';
@@ -861,12 +862,9 @@ export class ConsoleInsight extends UI.Widget.Widget {
861
862
  // off -> entrypoints are shown, and point to the AI setting panel where the setting can be turned on
862
863
  // on -> entrypoints are shown, and console insights can be generated
863
864
  #getConsoleInsightsEnabledSetting(): Common.Settings.Setting<boolean>|undefined {
864
- try {
865
- return Common.Settings.Settings.instance().moduleSetting('console-insights-enabled') as
866
- Common.Settings.Setting<boolean>;
867
- } catch {
868
- return;
869
- }
865
+ const result = Common.Settings.Settings.instance().maybeResolve(
866
+ AiAssistanceModel.AiUtils.consoleInsightsEnabledSettingDescriptor);
867
+ return 'setting' in result ? (result.setting as Common.Settings.Setting<boolean>) : undefined;
870
868
  }
871
869
 
872
870
  // off -> consent reminder is shown, unless the 'console-insights-enabled'-setting has been enabled in the current DevTools session
@@ -4,10 +4,11 @@
4
4
 
5
5
  import * as Common from '../../core/common/common.js';
6
6
  import * as i18n from '../../core/i18n/i18n.js';
7
- import type * as Platform from '../../core/platform/platform.js';
8
7
  import type * as Root from '../../core/root/root.js';
8
+ import * as AiAssistanceModel from '../../models/ai_assistance/ai_assistance.js';
9
9
  import * as Console from '../../panels/console/console.js';
10
10
  import * as UI from '../../ui/legacy/legacy.js';
11
+ import * as SettingUIRegistration from '../../ui/settings/settings.js';
11
12
 
12
13
  const UIStrings = {
13
14
  /**
@@ -27,27 +28,9 @@ const UIStrings = {
27
28
  * the settings tab.
28
29
  */
29
30
  enableConsoleInsights: 'Understand console messages with AI',
30
- /**
31
- * @description Message shown to the user if the DevTools locale is not
32
- * supported.
33
- */
34
- wrongLocale: 'To use this feature, set your language preference to English in DevTools settings.',
35
- /**
36
- * @description Message shown to the user if the user's region is not
37
- * supported.
38
- */
39
- geoRestricted: 'This feature is unavailable in your region.',
40
- /**
41
- * @description Message shown to the user if the enterprise policy does
42
- * not allow this feature.
43
- */
44
- policyRestricted: 'This setting is managed by your administrator.',
45
31
  } as const;
46
32
  const str_ = i18n.i18n.registerUIStrings('panels/explain/explain-meta.ts', UIStrings);
47
33
  const i18nLazyString = i18n.i18n.getLazilyComputedLocalizedString.bind(undefined, str_);
48
- const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
49
-
50
- const setting = 'console-insights-enabled';
51
34
 
52
35
  const actions = [
53
36
  {
@@ -92,11 +75,6 @@ const actions = [
92
75
  },
93
76
  ];
94
77
 
95
- function isLocaleRestricted(): boolean {
96
- const devtoolsLocale = i18n.DevToolsLocale.DevToolsLocale.instance();
97
- return !devtoolsLocale.locale.startsWith('en-');
98
- }
99
-
100
78
  function isGeoRestricted(config?: Root.Runtime.HostConfig): boolean {
101
79
  return config?.aidaAvailability?.blockedByGeo === true;
102
80
  }
@@ -109,31 +87,11 @@ function isFeatureEnabled(config?: Root.Runtime.HostConfig): boolean {
109
87
  return (config?.aidaAvailability?.enabled && config?.devToolsConsoleInsights?.enabled) === true;
110
88
  }
111
89
 
112
- Common.Settings.registerSettingExtension({
113
- category: Common.Settings.SettingCategory.AI,
114
- settingName: setting,
115
- settingType: Common.Settings.SettingType.BOOLEAN,
116
- title: i18nLazyString(UIStrings.enableConsoleInsights),
117
- defaultValue: false,
118
- reloadRequired: false,
119
- condition: config => isFeatureEnabled(config),
120
- disabledCondition: config => {
121
- const reasons: Platform.UIString.LocalizedString[] = [];
122
- if (isGeoRestricted(config)) {
123
- reasons.push(i18nString(UIStrings.geoRestricted));
124
- }
125
- if (isPolicyRestricted(config)) {
126
- reasons.push(i18nString(UIStrings.policyRestricted));
127
- }
128
- if (isLocaleRestricted()) {
129
- reasons.push(i18nString(UIStrings.wrongLocale));
130
- }
131
- if (reasons.length > 0) {
132
- return {disabled: true, reasons};
133
- }
134
- return {disabled: false};
135
- },
136
- });
90
+ SettingUIRegistration.SettingUIRegistration.register(AiAssistanceModel.AiUtils.consoleInsightsEnabledSettingDescriptor,
91
+ {
92
+ category: Common.Settings.SettingCategory.AI,
93
+ title: i18nLazyString(UIStrings.enableConsoleInsights),
94
+ });
137
95
 
138
96
  for (const action of actions) {
139
97
  UI.ActionRegistration.registerActionExtension({
@@ -378,7 +378,7 @@ export interface RequestActivatedEvent {
378
378
  }
379
379
 
380
380
  export interface EventTypes {
381
- [Events.RequestSelected]: SDK.NetworkRequest.NetworkRequest;
381
+ [Events.RequestSelected]: SDK.NetworkRequest.NetworkRequest|null;
382
382
  [Events.RequestActivated]: RequestActivatedEvent;
383
383
  }
384
384