chrome-devtools-frontend 1.0.1625079 → 1.0.1625854

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 (171) hide show
  1. package/front_end/core/common/VersionController.ts +9 -7
  2. package/front_end/core/common/common.ts +0 -4
  3. package/front_end/core/host/Platform.ts +0 -9
  4. package/front_end/core/host/UserMetrics.ts +0 -55
  5. package/front_end/core/root/ExperimentNames.ts +0 -1
  6. package/front_end/core/sdk/CPUThrottlingManager.ts +1 -1
  7. package/front_end/core/sdk/CSSMetadata.ts +0 -8
  8. package/front_end/core/sdk/CSSProperty.ts +1 -5
  9. package/front_end/core/sdk/CSSPropertyParserMatchers.ts +0 -30
  10. package/front_end/{panels/application → core/sdk}/DOMStorageModel.ts +12 -10
  11. package/front_end/core/sdk/sdk.ts +2 -0
  12. package/front_end/entrypoints/main/MainImpl.ts +12 -8
  13. package/front_end/entrypoints/main/SimpleApp.ts +3 -4
  14. package/front_end/entrypoints/main/main-meta.ts +1 -1
  15. package/front_end/generated/InspectorBackendCommands.ts +3 -3
  16. package/front_end/generated/SupportedCSSProperties.js +21 -21
  17. package/front_end/generated/protocol.ts +4 -1
  18. package/front_end/models/ai_code_completion/AiCodeCompletion.ts +10 -2
  19. package/front_end/models/ai_code_generation/AiCodeGeneration.ts +5 -1
  20. package/front_end/models/issues_manager/CookieIssue.ts +0 -4
  21. package/front_end/models/issues_manager/RelatedIssue.ts +0 -14
  22. package/front_end/models/workspace/IgnoreListManager.ts +1 -1
  23. package/front_end/models/workspace/UISourceCode.ts +10 -5
  24. package/front_end/panels/ai_assistance/components/ChatMessage.ts +26 -9
  25. package/front_end/panels/application/ApplicationPanelSidebar.ts +32 -42
  26. package/front_end/panels/application/BackForwardCacheTreeElement.ts +2 -2
  27. package/front_end/panels/application/BounceTrackingMitigationsTreeElement.ts +2 -2
  28. package/front_end/panels/application/DOMStorageItemsView.ts +18 -13
  29. package/front_end/panels/application/InterestGroupTreeElement.ts +2 -2
  30. package/front_end/panels/application/ReportingApiTreeElement.ts +3 -3
  31. package/front_end/panels/application/ResourcesPanel.ts +1 -2
  32. package/front_end/panels/application/ServiceWorkerCacheTreeElement.ts +1 -2
  33. package/front_end/panels/application/StorageView.ts +1 -2
  34. package/front_end/panels/application/TrustTokensTreeElement.ts +2 -2
  35. package/front_end/panels/application/WebMCPTreeElement.ts +2 -2
  36. package/front_end/panels/application/application.ts +0 -2
  37. package/front_end/panels/console/ConsolePrompt.ts +1 -1
  38. package/front_end/panels/console/ConsoleView.ts +3 -4
  39. package/front_end/panels/elements/ColorSwatchPopoverIcon.ts +0 -152
  40. package/front_end/panels/elements/ElementsTreeElement.ts +4 -9
  41. package/front_end/panels/elements/StylePropertiesSection.ts +0 -61
  42. package/front_end/panels/elements/StylePropertyTreeElement.ts +1 -21
  43. package/front_end/panels/elements/StylesAiCodeCompletionProvider.ts +6 -4
  44. package/front_end/panels/elements/StylesSidebarPane.ts +2 -3
  45. package/front_end/panels/elements/stylesSidebarPane.css +0 -8
  46. package/front_end/panels/emulation/AdvancedApp.ts +3 -3
  47. package/front_end/panels/emulation/DeviceModeToolbar.ts +203 -377
  48. package/front_end/panels/emulation/emulation-meta.ts +1 -1
  49. package/front_end/panels/network/NetworkPanel.ts +1 -1
  50. package/front_end/panels/profiler/HeapProfilerPanel.ts +1 -2
  51. package/front_end/panels/screencast/ScreencastApp.ts +3 -3
  52. package/front_end/panels/screencast/screencast-meta.ts +1 -2
  53. package/front_end/panels/settings/SettingsScreen.ts +2 -2
  54. package/front_end/panels/sources/AiCodeCompletionPlugin.ts +2 -4
  55. package/front_end/panels/sources/SourcesNavigator.ts +1 -1
  56. package/front_end/panels/sources/UISourceCodeFrame.ts +1 -3
  57. package/front_end/panels/timeline/TimelinePanel.ts +2 -2
  58. package/front_end/third_party/chromium/README.chromium +1 -1
  59. package/front_end/third_party/puppeteer/README.chromium +2 -2
  60. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Page.d.ts +2 -2
  61. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/locators/locators.d.ts +3 -2
  62. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/locators/locators.d.ts.map +1 -1
  63. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/locators/locators.js +53 -23
  64. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/locators/locators.js.map +1 -1
  65. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.d.ts +3 -2
  66. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.d.ts.map +1 -1
  67. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.js +18 -8
  68. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.js.map +1 -1
  69. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/BrowserConnector.d.ts.map +1 -1
  70. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/BrowserConnector.js +2 -2
  71. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/BrowserConnector.js.map +1 -1
  72. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.d.ts.map +1 -1
  73. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.js +4 -0
  74. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.js.map +1 -1
  75. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/TargetManager.d.ts +1 -1
  76. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/TargetManager.d.ts.map +1 -1
  77. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/TargetManager.js +54 -19
  78. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/TargetManager.js.map +1 -1
  79. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/WebMCP.d.ts +5 -0
  80. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/WebMCP.d.ts.map +1 -1
  81. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/WebMCP.js +2 -9
  82. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/WebMCP.js.map +1 -1
  83. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/common/BrowserConnector.d.ts.map +1 -1
  84. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/common/BrowserConnector.js +3 -0
  85. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/common/BrowserConnector.js.map +1 -1
  86. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/common/ConnectOptions.d.ts +37 -1
  87. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/common/ConnectOptions.d.ts.map +1 -1
  88. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/injected/injected.d.ts +1 -1
  89. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/BrowserLauncher.d.ts.map +1 -1
  90. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/BrowserLauncher.js +5 -2
  91. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/BrowserLauncher.js.map +1 -1
  92. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/ChromeLauncher.d.ts.map +1 -1
  93. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/ChromeLauncher.js +1 -0
  94. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/ChromeLauncher.js.map +1 -1
  95. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.d.ts +3 -3
  96. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js +3 -3
  97. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Mutex.d.ts +2 -2
  98. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.d.ts +1 -1
  99. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.js +1 -1
  100. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.d.ts +47 -65
  101. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +143 -55
  102. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Page.d.ts +2 -2
  103. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/locators/locators.d.ts +3 -2
  104. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/locators/locators.d.ts.map +1 -1
  105. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/locators/locators.js +53 -23
  106. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/locators/locators.js.map +1 -1
  107. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.d.ts +3 -2
  108. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.d.ts.map +1 -1
  109. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.js +18 -8
  110. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.js.map +1 -1
  111. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/BrowserConnector.d.ts.map +1 -1
  112. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/BrowserConnector.js +2 -2
  113. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/BrowserConnector.js.map +1 -1
  114. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.d.ts.map +1 -1
  115. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.js +4 -0
  116. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.js.map +1 -1
  117. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/TargetManager.d.ts +1 -1
  118. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/TargetManager.d.ts.map +1 -1
  119. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/TargetManager.js +54 -19
  120. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/TargetManager.js.map +1 -1
  121. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/WebMCP.d.ts +5 -0
  122. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/WebMCP.d.ts.map +1 -1
  123. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/WebMCP.js +2 -9
  124. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/WebMCP.js.map +1 -1
  125. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/common/BrowserConnector.d.ts.map +1 -1
  126. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/common/BrowserConnector.js +3 -0
  127. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/common/BrowserConnector.js.map +1 -1
  128. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/common/ConnectOptions.d.ts +37 -1
  129. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/common/ConnectOptions.d.ts.map +1 -1
  130. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/BrowserLauncher.d.ts.map +1 -1
  131. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/BrowserLauncher.js +5 -2
  132. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/BrowserLauncher.js.map +1 -1
  133. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/ChromeLauncher.d.ts.map +1 -1
  134. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/ChromeLauncher.js +1 -0
  135. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/ChromeLauncher.js.map +1 -1
  136. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.d.ts +3 -3
  137. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js +3 -3
  138. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.d.ts +1 -1
  139. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.js +1 -1
  140. package/front_end/third_party/puppeteer/package/lib/types.d.ts +47 -65
  141. package/front_end/third_party/puppeteer/package/package.json +6 -6
  142. package/front_end/third_party/puppeteer/package/src/api/Page.ts +2 -2
  143. package/front_end/third_party/puppeteer/package/src/api/locators/locators.ts +69 -27
  144. package/front_end/third_party/puppeteer/package/src/cdp/Browser.ts +27 -8
  145. package/front_end/third_party/puppeteer/package/src/cdp/BrowserConnector.ts +4 -2
  146. package/front_end/third_party/puppeteer/package/src/cdp/Page.ts +6 -0
  147. package/front_end/third_party/puppeteer/package/src/cdp/TargetManager.ts +60 -18
  148. package/front_end/third_party/puppeteer/package/src/cdp/WebMCP.ts +7 -9
  149. package/front_end/third_party/puppeteer/package/src/common/BrowserConnector.ts +3 -0
  150. package/front_end/third_party/puppeteer/package/src/common/ConnectOptions.ts +37 -1
  151. package/front_end/third_party/puppeteer/package/src/node/BrowserLauncher.ts +7 -2
  152. package/front_end/third_party/puppeteer/package/src/node/ChromeLauncher.ts +1 -0
  153. package/front_end/third_party/puppeteer/package/src/revisions.ts +3 -3
  154. package/front_end/third_party/puppeteer/package/src/util/version.ts +1 -1
  155. package/front_end/ui/components/text_editor/AiCodeCompletionProvider.ts +19 -14
  156. package/front_end/ui/components/text_editor/AiCodeGenerationProvider.ts +8 -7
  157. package/front_end/{core/common → ui/legacy}/AppProvider.ts +1 -1
  158. package/front_end/ui/legacy/InspectorView.ts +4 -3
  159. package/front_end/ui/legacy/TabbedPane.ts +1 -1
  160. package/front_end/ui/legacy/UIUserMetrics.ts +70 -0
  161. package/front_end/ui/legacy/components/cookie_table/CookiesTable.ts +1 -8
  162. package/front_end/ui/legacy/components/inline_editor/inline_editor.ts +0 -6
  163. package/front_end/ui/legacy/legacy.ts +6 -0
  164. package/front_end/ui/visual_logging/KnownContextValues.ts +1 -0
  165. package/package.json +1 -1
  166. package/front_end/Images/src/custom-typography.svg +0 -3
  167. package/front_end/ui/legacy/components/inline_editor/FontEditor.ts +0 -798
  168. package/front_end/ui/legacy/components/inline_editor/FontEditorUnitConverter.ts +0 -250
  169. package/front_end/ui/legacy/components/inline_editor/FontEditorUtils.ts +0 -204
  170. package/front_end/ui/legacy/components/inline_editor/fontEditor.css +0 -185
  171. /package/front_end/{core/common → ui/legacy}/App.ts +0 -0
@@ -188,25 +188,10 @@ const UIStrings = {
188
188
  } as const;
189
189
  const str_ = i18n.i18n.registerUIStrings('panels/emulation/DeviceModeToolbar.ts', UIStrings);
190
190
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
191
- const {ref, classMap} = Directives;
191
+ const {ifDefined, styleMap} = Directives;
192
192
  const {widget} = UI.Widget;
193
193
  const {bindToSetting} = UI.UIUtils;
194
194
 
195
- /**
196
- * Even though the emulation panel uses all UI elements, the tooltips are not supported.
197
- * That's because the emulation elements are rendered around the page context, rather
198
- * than in the DevTools panel itself. Therefore, we need to fall back to using the
199
- * built-in tooltip by setting the title attribute on the button's element.
200
- */
201
- function setTitleForButton(button: Buttons.Button.Button|UI.Toolbar.ToolbarMenuButton, title: string): void {
202
- if (button instanceof UI.Toolbar.ToolbarMenuButton) {
203
- button.setTitle(title);
204
- button.element.title = title;
205
- } else {
206
- button.title = title;
207
- }
208
- }
209
-
210
195
  export class DeviceModeToolbar {
211
196
  private model: EmulationModel.DeviceModeModel.DeviceModeModel;
212
197
  private readonly showMediaInspectorSetting: Common.Settings.Setting<boolean>;
@@ -219,23 +204,10 @@ export class DeviceModeToolbar {
219
204
  readonly #element: HTMLDivElement;
220
205
  private readonly emulatedDevicesList: EmulationModel.EmulatedDevices.EmulatedDevicesList;
221
206
  private readonly persistenceSetting: Common.Settings.Setting<{device: string, orientation: string, mode: string}>;
222
- private spanButton!: Buttons.Button.Button;
223
- private postureItem!: HTMLSelectElement;
224
- private modeButton!: Buttons.Button.Button;
225
- private widthInput!: HTMLInputElement;
226
- private heightInput!: HTMLInputElement;
227
- private deviceScaleItem!: HTMLSelectElement;
228
- private deviceScaleItems: HTMLElement[] = [];
229
- private deviceSelectItem!: HTMLSelectElement;
230
- private scaleItem!: HTMLSelectElement;
231
- private uaItem!: HTMLSelectElement;
232
- private cachedDeviceScale!: number|null;
233
- private cachedUaType!: string|null;
234
- private xItem?: HTMLElement;
235
- private cachedModelType?: EmulationModel.DeviceModeModel.Type;
236
- private cachedScale?: number;
237
- private cachedModelDevice?: EmulationModel.EmulatedDevices.EmulatedDevice|null;
238
- private cachedModelMode?: EmulationModel.EmulatedDevices.Mode|null;
207
+ private mainToolbar: UI.Toolbar.Toolbar;
208
+ private optionsToolbar: UI.Toolbar.Toolbar;
209
+ readonly #itemWidthCache = new Map<string, string>();
210
+ #measuringElement: HTMLSelectElement|null = null;
239
211
 
240
212
  constructor(
241
213
  model: EmulationModel.DeviceModeModel.DeviceModeModel,
@@ -248,11 +220,11 @@ export class DeviceModeToolbar {
248
220
  this.deviceOutlineSetting = this.model.deviceOutlineSetting();
249
221
  this.showDeviceScaleFactorSetting =
250
222
  Common.Settings.Settings.instance().createSetting('emulation.show-device-scale-factor', false);
251
- this.showDeviceScaleFactorSetting.addChangeListener(this.updateDeviceScaleFactorVisibility, this);
223
+ this.showDeviceScaleFactorSetting.addChangeListener(this.update, this);
252
224
 
253
225
  this.showUserAgentTypeSetting =
254
226
  Common.Settings.Settings.instance().createSetting('emulation.show-user-agent-type', false);
255
- this.showUserAgentTypeSetting.addChangeListener(this.updateUserAgentTypeVisibility, this);
227
+ this.showUserAgentTypeSetting.addChangeListener(this.update, this);
256
228
 
257
229
  this.autoAdjustScaleSetting =
258
230
  Common.Settings.Settings.instance().createSetting('emulation.auto-adjust-scale', true);
@@ -263,8 +235,8 @@ export class DeviceModeToolbar {
263
235
  this.#element.classList.add('device-mode-toolbar');
264
236
  this.#element.setAttribute('jslog', `${VisualLogging.toolbar('device-mode').track({resize: true})}`);
265
237
 
266
- const mainToolbar = this.createMainToolbar();
267
- const optionsToolbar = this.createOptionsToolbar();
238
+ this.mainToolbar = this.createMainToolbar();
239
+ this.optionsToolbar = this.createOptionsToolbar();
268
240
 
269
241
  this.emulatedDevicesList = EmulationModel.EmulatedDevices.EmulatedDevicesList.instance();
270
242
  this.emulatedDevicesList.addEventListener(
@@ -275,28 +247,9 @@ export class DeviceModeToolbar {
275
247
  this.persistenceSetting = Common.Settings.Settings.instance().createSetting(
276
248
  'emulation.device-mode-value', {device: '', orientation: '', mode: ''});
277
249
 
278
- this.model.toolbarControlsEnabledSetting().addChangeListener(updateToolbarsEnabled);
279
- updateToolbarsEnabled();
280
-
281
- this.updateDeviceMenuItems();
282
- this.updateScaleMenuItems();
283
- this.updateDeviceScaleMenuItems();
284
- this.updateUserAgentMenuItems();
285
- this.updateDevicePostureItems();
286
-
287
- function updateToolbarsEnabled(): void {
288
- const enabled = model.toolbarControlsEnabledSetting().get();
289
- mainToolbar.setEnabled(enabled);
290
- optionsToolbar.setEnabled(enabled);
291
- }
292
- }
250
+ this.model.toolbarControlsEnabledSetting().addChangeListener(this.update, this);
293
251
 
294
- private appendOption(parentElement: HTMLElement, title: string, value: string, jslogContext: string): void {
295
- const option = document.createElement('option');
296
- option.text = title;
297
- option.value = value;
298
- option.setAttribute('jslog', `${VisualLogging.item(jslogContext).track({click: true})}`);
299
- parentElement.appendChild(option);
252
+ this.update();
300
253
  }
301
254
 
302
255
  private createEmptyToolbarElement(): HTMLDivElement {
@@ -307,7 +260,7 @@ export class DeviceModeToolbar {
307
260
 
308
261
  private createSizeInput(
309
262
  title: string, jslogContext: string, value: string|undefined, disabled: boolean|undefined,
310
- refCallback: (el: Element|undefined) => void, onChange: () => void): LitTemplate {
263
+ placeholder: string|undefined, onChange: (event: Event) => void): LitTemplate {
311
264
  return html`
312
265
  <input type="number"
313
266
  max=${EmulationModel.DeviceModeModel.MaxDeviceSize}
@@ -317,8 +270,8 @@ export class DeviceModeToolbar {
317
270
  .disabled=${disabled ?? false}
318
271
  jslog=${VisualLogging.textField().track({change: true}).context(jslogContext)}
319
272
  .value=${value ?? ''}
273
+ placeholder=${ifDefined(placeholder)}
320
274
  @change=${onChange}
321
- ${ref(refCallback)}
322
275
  @keydown=${(event: Event): void => {
323
276
  const input = event.target as HTMLInputElement;
324
277
  let modifiedValue = UI.UIUtils.modifiedFloatNumber(Number(input.value), event);
@@ -336,50 +289,120 @@ export class DeviceModeToolbar {
336
289
 
337
290
  private createMainToolbar(): UI.Toolbar.Toolbar {
338
291
  const mainToolbar = this.#element.createChild('devtools-toolbar', 'main-toolbar');
292
+ return mainToolbar as UI.Toolbar.Toolbar;
293
+ }
294
+
295
+ private renderMainToolbar(): void {
296
+ const isResponsive = this.model.type() === EmulationModel.DeviceModeModel.Type.Responsive;
297
+ const isFullHeight = isResponsive && this.model.isFullHeight();
298
+ const size = this.model.appliedDeviceSize();
299
+ const widthValue = String(size.width);
300
+ const heightValue = isFullHeight ? '' : String(size.height);
301
+ const heightPlaceholder = String(size.height);
302
+
303
+ const device = this.model.device();
304
+ let modeButtonTitle = i18nString(UIStrings.rotate);
305
+ let modeButtonDisabled = false;
306
+ if (this.model.isScreenOrientationLocked()) {
307
+ modeButtonDisabled = true;
308
+ modeButtonTitle = i18nString(UIStrings.screenOrientationLocked);
309
+ } else if (device) {
310
+ const modeCount = device.modes.length;
311
+ modeButtonDisabled = modeCount < 2;
312
+ modeButtonTitle = modeCount === 2 ? i18nString(UIStrings.rotate) : i18nString(UIStrings.screenOrientationOptions);
313
+ } else if (isResponsive) {
314
+ modeButtonDisabled = false;
315
+ modeButtonTitle = i18nString(UIStrings.rotate);
316
+ } else {
317
+ modeButtonDisabled = true;
318
+ }
319
+
320
+ const showSpanButton = Boolean(device?.isDualScreen);
321
+ const showPostureItem = Boolean(!device?.isDualScreen && device?.isFoldableScreen);
322
+
323
+ const deviceModeOptions = this.getDeviceModeOptions();
324
+ const scaleOptions = this.getScaleOptions();
325
+ const dprOptions = this.getDeviceScaleFactorOptions();
326
+ const uaOptions = this.getUserAgentOptions();
327
+ const postureOptions = this.getDevicePostureOptions();
328
+
329
+ const selectedDeviceOption = [
330
+ deviceModeOptions.responsive,
331
+ ...deviceModeOptions.standard,
332
+ ...deviceModeOptions.custom,
333
+ ].find(o => o.selected);
334
+ const deviceText = selectedDeviceOption ? selectedDeviceOption.title : deviceModeOptions.responsive.title;
335
+
336
+ let scaleText = scaleOptions.find(o => o.selected)?.title || '';
337
+ if (scaleText.includes(' (')) {
338
+ scaleText = scaleText.substring(0, scaleText.indexOf(' ('));
339
+ }
340
+
341
+ let dprText = dprOptions.find(o => o.selected)?.title || '';
342
+ if (dprText.includes(' (')) {
343
+ dprText = dprText.substring(0, dprText.indexOf(' ('));
344
+ }
345
+
346
+ const uaText = uaOptions.find(o => o.selected)?.title || '';
347
+ const postureText = postureOptions.find(o => o.selected)?.title || '';
348
+
339
349
  // clang-format off
340
350
  render(html`
341
351
  <div class="device-mode-empty-toolbar-element"></div>
342
352
  ${i18nTemplate(str_, UIStrings.dimensions, {PH1: html`
343
353
  <select class="dark-text toolbar-has-dropdown-shrinkable"
354
+ style=${styleMap({width: this.calculateItemWidth(deviceText)})}
344
355
  title=${i18nString(UIStrings.deviceType)}
345
356
  aria-label=${i18nString(UIStrings.deviceType)}
346
357
  @change=${this.onDeviceChange.bind(this)}
347
- jslog=${VisualLogging.dropDown().track({change: true}).context('device')}
348
- ${ref(el => { this.deviceSelectItem = el as HTMLSelectElement; })}>
358
+ .value=${selectedDeviceOption === deviceModeOptions.responsive ? 'Responsive' : (selectedDeviceOption?.title || 'Responsive')}
359
+ jslog=${VisualLogging.dropDown().track({change: true}).context('device')}>
360
+ <option value="Responsive" ?selected=${deviceModeOptions.responsive.selected} jslog=${VisualLogging.item(deviceModeOptions.responsive.jslogContext).track({click: true})}>
361
+ ${deviceModeOptions.responsive.title}
362
+ </option>
363
+ ${deviceModeOptions.standard.length > 0 ? html`
364
+ <optgroup label="Standard">
365
+ ${deviceModeOptions.standard.map(o => html`<option value=${o.title} ?selected=${o.selected} jslog=${VisualLogging.item(o.jslogContext).track({click: true})}>${o.title}</option>`)}
366
+ </optgroup>
367
+ ` : ''}
368
+ ${deviceModeOptions.custom.length > 0 ? html`
369
+ <optgroup label="Custom">
370
+ ${deviceModeOptions.custom.map(o => html`<option value=${o.title} ?selected=${o.selected} jslog=${VisualLogging.item(o.jslogContext).track({click: true})}>${o.title}</option>`)}
371
+ </optgroup>
372
+ ` : ''}
373
+ <option value="Edit" jslog=${VisualLogging.item(deviceModeOptions.edit.jslogContext).track({click: true})}>
374
+ ${deviceModeOptions.edit.title}
375
+ </option>
349
376
  </select>`})}
350
377
 
351
- ${this.createSizeInput(
352
- i18nString(UIStrings.width), 'width', this.widthInput?.value, this.widthInput?.disabled,
353
- el => { this.widthInput = el as HTMLInputElement; },
354
- () => {
355
- const width = Number(this.widthInput.value);
356
- if (this.autoAdjustScaleSetting.get()) {
357
- this.model.setWidthAndScaleToFit(width);
358
- } else {
359
- this.model.setWidth(width);
360
- }
361
- })}
362
-
363
- <div class="device-mode-x" ${ref(el => { this.xItem = el as HTMLElement; })}>×</div>
364
- ${this.createSizeInput(
365
- i18nString(UIStrings.heightLeaveEmptyForFull), 'height', this.heightInput?.value, this.heightInput?.disabled,
366
- el => { this.heightInput = el as HTMLInputElement; },
367
- () => {
368
- const height = Number(this.heightInput.value);
369
- if (this.autoAdjustScaleSetting.get()) {
370
- this.model.setHeightAndScaleToFit(height);
371
- } else {
372
- this.model.setHeight(height);
373
- }
374
- })}
378
+ ${this.createSizeInput(i18nString(UIStrings.width), 'width', widthValue, !isResponsive, '', (event: Event) => {
379
+ const width = Number((event.target as HTMLInputElement).value);
380
+ if (this.autoAdjustScaleSetting.get()) {
381
+ this.model.setWidthAndScaleToFit(width);
382
+ } else {
383
+ this.model.setWidth(width);
384
+ }
385
+ })}
386
+
387
+ <div class="device-mode-x">×</div>
388
+ ${this.createSizeInput(i18nString(UIStrings.heightLeaveEmptyForFull), 'height', heightValue, !isResponsive, heightPlaceholder, (event: Event) => {
389
+ const height = Number((event.target as HTMLInputElement).value);
390
+ if (this.autoAdjustScaleSetting.get()) {
391
+ this.model.setHeightAndScaleToFit(height);
392
+ } else {
393
+ this.model.setHeight(height);
394
+ }
395
+ })}
375
396
 
376
397
  <div class="device-mode-empty-toolbar-element"></div>
377
398
  <select class="dark-text toolbar-has-dropdown-shrinkable"
399
+ style=${styleMap({width: this.calculateItemWidth(scaleText)})}
378
400
  title=${i18nString(UIStrings.zoom)}
379
401
  aria-label=${i18nString(UIStrings.zoom)}
380
402
  @change=${this.onScaleChange.bind(this)}
381
- jslog=${VisualLogging.dropDown().track({change: true}).context('scale')}
382
- ${ref((el: Element|undefined) => { this.scaleItem = el as HTMLSelectElement; })}>
403
+ .value=${String(scaleOptions.find(o => o.selected)?.value || '')}
404
+ jslog=${VisualLogging.dropDown().track({change: true}).context('scale')}>
405
+ ${scaleOptions.map(o => html`<option value=${o.value} ?selected=${o.selected} jslog=${VisualLogging.item(o.jslogContext).track({click: true})}>${o.title}</option>`)}
383
406
  </select>
384
407
 
385
408
  <devtools-button .data=${{variant: Buttons.Button.Variant.TOOLBAR, iconName: 'center-focus-weak',
@@ -390,28 +413,33 @@ export class DeviceModeToolbar {
390
413
 
391
414
  <div class="device-mode-empty-toolbar-element"></div>
392
415
 
393
- <span>
416
+ ${this.showDeviceScaleFactorSetting.get() ? html`
394
417
  ${i18nTemplate(str_, UIStrings.dpr, {
395
418
  PH1: html`
396
419
  <select class="dark-text toolbar-has-dropdown-shrinkable"
420
+ style=${styleMap({width: this.calculateItemWidth(dprText)})}
397
421
  title=${i18nString(UIStrings.devicePixelRatio)}
398
422
  aria-label=${i18nString(UIStrings.devicePixelRatio)}
399
423
  @change=${this.onDeviceScaleChange.bind(this)}
424
+ .value=${String(dprOptions.find(o => o.selected)?.value || '')}
400
425
  jslog=${VisualLogging.dropDown().track({change: true}).context('device-pixel-ratio')}
401
- ${ref(el => { this.deviceScaleItem = el as HTMLSelectElement; })}>
426
+ ?disabled=${!isResponsive}>
427
+ ${dprOptions.map(o => html`<option value=${o.value} ?selected=${o.selected} jslog=${VisualLogging.item(o.jslogContext).track({click: true})}>${o.title}</option>`)}
402
428
  </select>`
403
- })}
404
- </span>
429
+ })}` : ''}
405
430
 
406
431
  <div class="device-mode-empty-toolbar-element"></div>
407
- <select class="dark-text toolbar-has-dropdown-shrinkable
408
- ${classMap({hidden: !this.showUserAgentTypeSetting.get()})}"
409
- title=${i18nString(UIStrings.deviceType)}
410
- aria-label=${i18nString(UIStrings.deviceType)}
411
- @change=${this.onUAChange.bind(this)}
412
- jslog=${VisualLogging.dropDown().track({change: true}).context('device-type')}
413
- ${ref(el => { this.uaItem = el as HTMLSelectElement; })}>
414
- </select>
432
+ ${this.showUserAgentTypeSetting.get() ? html`
433
+ <select class="dark-text toolbar-has-dropdown-shrinkable"
434
+ style=${styleMap({width: this.calculateItemWidth(uaText)})}
435
+ title=${i18nString(UIStrings.deviceType)}
436
+ aria-label=${i18nString(UIStrings.deviceType)}
437
+ @change=${this.onUAChange.bind(this)}
438
+ .value=${uaOptions.find(o => o.selected)?.value || ''}
439
+ jslog=${VisualLogging.dropDown().track({change: true}).context('device-type')}
440
+ ?disabled=${!isResponsive}>
441
+ ${uaOptions.map(o => html`<option value=${o.value} ?selected=${o.selected} jslog=${VisualLogging.item(o.jslogContext).track({click: true})}>${o.title}</option>`)}
442
+ </select>` : ''}
415
443
  <select class="dark-text" ${widget(MobileThrottling.NetworkThrottlingSelector.NetworkThrottlingSelect, {
416
444
  title: i18nString(UIStrings.throttling),
417
445
  bindToGlobalConditions: true,
@@ -421,45 +449,35 @@ export class DeviceModeToolbar {
421
449
 
422
450
  <div class="device-mode-empty-toolbar-element"></div>
423
451
  <devtools-button class="toolbar-button"
424
- .data=${{variant: Buttons.Button.Variant.ICON, iconName: 'screen-rotation'} as Buttons.Button.ButtonData}
452
+ .data=${{variant: Buttons.Button.Variant.TOOLBAR, iconName: 'screen-rotation'} as Buttons.Button.ButtonData}
425
453
  jslog=${VisualLogging.action('screen-rotation').track({click: true})}
426
454
  @click=${this.modeMenuClicked.bind(this)}
427
- ${ref(el => { this.modeButton = el as Buttons.Button.Button; })}>
455
+ .title=${modeButtonTitle}
456
+ .disabled=${modeButtonDisabled}>
428
457
  </devtools-button>
429
458
 
430
459
  <!-- Show dual screen toolbar -->
431
- <devtools-button class="toolbar-button"
432
- .data=${{variant: Buttons.Button.Variant.ICON, iconName: 'device-fold'} as Buttons.Button.ButtonData}
433
- jslog=${VisualLogging.action('device-fold').track({click: true})}
434
- @click=${this.spanClicked.bind(this)}
435
- ${ref(el => { this.spanButton = el as Buttons.Button.Button; })}>
436
- </devtools-button>
460
+ ${showSpanButton ? html`
461
+ <devtools-button class="toolbar-button"
462
+ .data=${{variant: Buttons.Button.Variant.TOOLBAR, iconName: 'device-fold'} as Buttons.Button.ButtonData}
463
+ jslog=${VisualLogging.action('device-fold').track({click: true})}
464
+ .title=${i18nString(UIStrings.toggleDualscreenMode)}
465
+ @click=${this.spanClicked.bind(this)}>
466
+ </devtools-button>` : ''}
437
467
 
438
468
  <!-- Show posture toolbar menu for foldable devices. -->
439
469
  <div class="device-mode-empty-toolbar-element"></div>
440
- <select class="dark-text toolbar-has-dropdown-shrinkable"
441
- title=${i18nString(UIStrings.devicePosture)}
442
- aria-label=${i18nString(UIStrings.devicePosture)}
443
- @change=${this.onPostureChange.bind(this)}
444
- jslog=${VisualLogging.dropDown().track({change: true}).context('device-posture')}
445
- ${ref(el => { this.postureItem = el as HTMLSelectElement; })}>
446
- </select>`,
447
- mainToolbar);
470
+ ${showPostureItem ? html`
471
+ <select class="dark-text toolbar-has-dropdown-shrinkable"
472
+ style=${styleMap({width: this.calculateItemWidth(postureText)})}
473
+ title=${i18nString(UIStrings.devicePosture)}
474
+ aria-label=${i18nString(UIStrings.devicePosture)}
475
+ @change=${this.onPostureChange.bind(this)}
476
+ .value=${postureOptions.find(o => o.selected)?.value || ''}
477
+ jslog=${VisualLogging.dropDown().track({change: true}).context('device-posture')}>
478
+ ${postureOptions.map(o => html`<option value=${o.value} ?selected=${o.selected} jslog=${VisualLogging.item(o.value.toLowerCase()).track({click: true})}>${o.title}</option>`)}
479
+ </select>` : ''}`, this.mainToolbar, {host: this});
448
480
  // clang-format on
449
- const deviceScaleSpan = this.deviceScaleItem.parentElement as HTMLElement;
450
- for (const node of Array.from(deviceScaleSpan.childNodes)) {
451
- if (node instanceof HTMLElement) {
452
- this.deviceScaleItems.push(node);
453
- } else if (node instanceof Text) {
454
- const span = document.createElement('span');
455
- span.classList.add('toolbar-text');
456
- span.appendChild(node);
457
- this.deviceScaleItems.push(span);
458
- }
459
- }
460
- deviceScaleSpan.replaceWith(...this.deviceScaleItems);
461
- this.updateDeviceScaleFactorVisibility();
462
- return mainToolbar;
463
481
  }
464
482
 
465
483
  private createOptionsToolbar(): UI.Toolbar.Toolbar {
@@ -478,20 +496,8 @@ export class DeviceModeToolbar {
478
496
  return ['Continuous', 'Folded'].map(title => ({title, value: title, selected: currentPosture === title}));
479
497
  }
480
498
 
481
- private updateDevicePostureItems(): void {
482
- this.postureItem.replaceChildren();
483
- for (const option of this.getDevicePostureOptions()) {
484
- this.appendOption(this.postureItem, option.title, option.value, option.title.toLowerCase());
485
- }
486
- const currentPosture = this.currentDevicePosture();
487
- if (this.postureItem.value !== currentPosture) {
488
- this.postureItem.value = currentPosture;
489
- }
490
- this.resizeItem(this.postureItem);
491
- }
492
-
493
- private onPostureChange(): void {
494
- const value = this.postureItem.value;
499
+ private onPostureChange(event: Event): void {
500
+ const value = (event.target as HTMLSelectElement).value;
495
501
  if (value !== this.currentDevicePosture()) {
496
502
  this.spanClicked();
497
503
  }
@@ -539,21 +545,8 @@ export class DeviceModeToolbar {
539
545
  });
540
546
  }
541
547
 
542
- private updateScaleMenuItems(): void {
543
- this.scaleItem.replaceChildren();
544
- const options = this.getScaleOptions();
545
- for (const option of options) {
546
- this.appendOption(this.scaleItem, option.title, String(option.value), option.jslogContext);
547
- }
548
- const selected = options.find(o => o.selected);
549
- if (selected) {
550
- this.scaleItem.value = String(selected.value);
551
- }
552
- this.resizeItem(this.scaleItem, true);
553
- }
554
-
555
- private onScaleChange(): void {
556
- const value = Number(this.scaleItem.value);
548
+ private onScaleChange(event: Event): void {
549
+ const value = Number((event.target as HTMLSelectElement).value);
557
550
  this.model.scaleSetting().set(value);
558
551
  }
559
552
 
@@ -587,21 +580,8 @@ export class DeviceModeToolbar {
587
580
  });
588
581
  }
589
582
 
590
- private updateDeviceScaleMenuItems(): void {
591
- this.deviceScaleItem.replaceChildren();
592
- const options = this.getDeviceScaleFactorOptions();
593
- for (const option of options) {
594
- this.appendOption(this.deviceScaleItem, option.title, String(option.value), option.jslogContext);
595
- }
596
- const selected = options.find(o => o.selected);
597
- if (selected) {
598
- this.deviceScaleItem.value = String(selected.value);
599
- }
600
- this.resizeItem(this.deviceScaleItem, true);
601
- }
602
-
603
- private onDeviceScaleChange(): void {
604
- const value = Number(this.deviceScaleItem.value);
583
+ private onDeviceScaleChange(event: Event): void {
584
+ const value = Number((event.target as HTMLSelectElement).value);
605
585
  this.model.deviceScaleFactorSetting().set(value);
606
586
  }
607
587
 
@@ -622,23 +602,9 @@ export class DeviceModeToolbar {
622
602
  }));
623
603
  }
624
604
 
625
- private updateUserAgentMenuItems(): void {
626
- this.uaItem.replaceChildren();
627
- const options = this.getUserAgentOptions();
628
- for (const option of options) {
629
- this.appendOption(this.uaItem, option.title, option.value, option.jslogContext);
630
- }
631
- const selected = options.find(o => o.selected);
632
- if (selected) {
633
- this.uaItem.value = selected.value;
634
- }
635
- this.resizeItem(this.uaItem);
636
- }
637
-
638
- private onUAChange(): void {
639
- const value = this.uaItem.value as EmulationModel.DeviceModeModel.UA;
605
+ private onUAChange(event: Event): void {
606
+ const value = (event.target as HTMLSelectElement).value as EmulationModel.DeviceModeModel.UA;
640
607
  this.model.uaSetting().set(value);
641
- this.resizeItem(this.uaItem);
642
608
  }
643
609
 
644
610
  private appendOptionsMenuItems(contextMenu: UI.ContextMenu.ContextMenu): void {
@@ -763,48 +729,11 @@ export class DeviceModeToolbar {
763
729
  };
764
730
  }
765
731
 
766
- private updateDeviceMenuItems(): void {
767
- this.deviceSelectItem.replaceChildren();
768
- const options = this.getDeviceModeOptions();
769
-
770
- this.appendOption(this.deviceSelectItem, options.responsive.title, 'Responsive', options.responsive.jslogContext);
771
-
772
- appendGroup.call(this, options.standard, 'Standard');
773
- appendGroup.call(this, options.custom, 'Custom');
774
-
775
- this.appendOption(this.deviceSelectItem, options.edit.title, 'Edit', options.edit.jslogContext);
776
-
777
- this.updateDeviceSelection();
778
-
779
- function appendGroup(
780
- this: DeviceModeToolbar, group: Array<{title: string, jslogContext: string}>, label: string): void {
781
- if (!group.length) {
782
- return;
783
- }
784
- const optGroup = document.createElement('optgroup');
785
- optGroup.label = label;
786
- for (const item of group) {
787
- this.appendOption(optGroup, item.title, item.title, item.jslogContext);
788
- }
789
- this.deviceSelectItem.appendChild(optGroup);
790
- }
791
- }
792
-
793
- private updateDeviceSelection(): void {
794
- const device = this.model.device();
795
- if (this.model.type() === EmulationModel.DeviceModeModel.Type.Responsive) {
796
- this.deviceSelectItem.value = 'Responsive';
797
- } else if (device) {
798
- this.deviceSelectItem.value = device.title;
799
- }
800
- this.resizeItem(this.deviceSelectItem);
801
- }
802
-
803
- private onDeviceChange(): void {
804
- const value = this.deviceSelectItem.value;
732
+ private onDeviceChange(event: Event): void {
733
+ const value = (event.target as HTMLSelectElement).value;
805
734
  if (value === 'Edit') {
806
735
  this.emulatedDevicesList.revealCustomSetting();
807
- this.updateDeviceSelection();
736
+ this.renderMainToolbar();
808
737
  } else if (value === 'Responsive') {
809
738
  this.switchToResponsive();
810
739
  } else {
@@ -818,7 +747,7 @@ export class DeviceModeToolbar {
818
747
  }
819
748
 
820
749
  private deviceListChanged(): void {
821
- this.updateDeviceMenuItems();
750
+ this.renderMainToolbar();
822
751
  const device = this.model.device();
823
752
  if (!device) {
824
753
  return;
@@ -836,26 +765,6 @@ export class DeviceModeToolbar {
836
765
  }
837
766
  }
838
767
 
839
- private updateDeviceScaleFactorVisibility(): void {
840
- if (this.deviceScaleItem) {
841
- const visible = this.showDeviceScaleFactorSetting.get();
842
- this.deviceScaleItem.classList.toggle('hidden', !visible);
843
- for (const item of this.deviceScaleItems) {
844
- item.classList.toggle('hidden', !visible);
845
- }
846
- }
847
- }
848
-
849
- private updateUserAgentTypeVisibility(): void {
850
- if (this.uaItem) {
851
- const visible = this.showUserAgentTypeSetting.get();
852
- this.uaItem.classList.toggle('hidden', !visible);
853
- if (visible) {
854
- this.resizeItem(this.uaItem);
855
- }
856
- }
857
- }
858
-
859
768
  private spanClicked(): void {
860
769
  const device = this.model.device();
861
770
 
@@ -916,13 +825,10 @@ export class DeviceModeToolbar {
916
825
  return;
917
826
  }
918
827
 
919
- if (!this.modeButton) {
920
- return;
921
- }
922
-
828
+ const modeButton = event.target as HTMLElement;
923
829
  const contextMenu = new UI.ContextMenu.ContextMenu(event, {
924
- x: this.modeButton.getBoundingClientRect().left,
925
- y: this.modeButton.getBoundingClientRect().top + this.modeButton.offsetHeight,
830
+ x: modeButton.getBoundingClientRect().left,
831
+ y: modeButton.getBoundingClientRect().top + modeButton.offsetHeight,
926
832
  });
927
833
  addOrientation(EmulationModel.EmulatedDevices.Vertical, i18nString(UIStrings.portrait));
928
834
  addOrientation(EmulationModel.EmulatedDevices.Horizontal, i18nString(UIStrings.landscape));
@@ -970,77 +876,11 @@ export class DeviceModeToolbar {
970
876
  }
971
877
 
972
878
  update(): void {
973
- if (this.model.type() !== this.cachedModelType) {
974
- this.cachedModelType = this.model.type();
975
- this.widthInput.disabled = this.model.type() !== EmulationModel.DeviceModeModel.Type.Responsive;
976
-
977
- this.heightInput.disabled = this.model.type() !== EmulationModel.DeviceModeModel.Type.Responsive;
978
- this.deviceScaleItem.disabled = this.model.type() !== EmulationModel.DeviceModeModel.Type.Responsive;
979
- this.uaItem.disabled = this.model.type() !== EmulationModel.DeviceModeModel.Type.Responsive;
980
-
981
- if (this.model.type() === EmulationModel.DeviceModeModel.Type.Responsive) {
982
- this.modeButton.disabled = false;
983
- setTitleForButton(this.modeButton, i18nString(UIStrings.rotate));
984
- } else {
985
- this.modeButton.disabled = true;
986
- }
987
- }
988
-
989
- const size = this.model.appliedDeviceSize();
990
- this.widthInput.value = String(size.width);
991
- this.heightInput.value =
992
- this.model.type() === EmulationModel.DeviceModeModel.Type.Responsive && this.model.isFullHeight() ?
993
- '' :
994
- String(size.height);
995
- this.heightInput.placeholder = String(size.height);
996
-
997
- if (this.model.scale() !== this.cachedScale) {
998
- this.updateScaleMenuItems();
999
- this.cachedScale = this.model.scale();
1000
- }
1001
-
1002
- const deviceScale = this.model.appliedDeviceScaleFactor();
1003
- if (deviceScale !== this.cachedDeviceScale) {
1004
- this.deviceScaleItem.value = String(deviceScale);
1005
- this.cachedDeviceScale = deviceScale;
1006
- this.resizeItem(this.deviceScaleItem, true);
1007
- }
1008
-
1009
- const uaType = this.model.appliedUserAgentType();
1010
- if (uaType !== this.cachedUaType) {
1011
- this.uaItem.value = uaType;
1012
- this.cachedUaType = uaType;
1013
- this.updateDeviceScaleMenuItems();
1014
- this.resizeItem(this.uaItem);
1015
- }
879
+ const enabled = this.model.toolbarControlsEnabledSetting().get();
880
+ this.mainToolbar.setEnabled(enabled);
881
+ this.optionsToolbar.setEnabled(enabled);
1016
882
 
1017
883
  const device = this.model.device();
1018
- this.updateDeviceSelection();
1019
-
1020
- if (this.model.device() !== this.cachedModelDevice) {
1021
- const device = this.model.device();
1022
- if (device) {
1023
- const modeCount = device ? device.modes.length : 0;
1024
- this.modeButton.disabled = modeCount < 2;
1025
- setTitleForButton(
1026
- this.modeButton,
1027
- modeCount === 2 ? i18nString(UIStrings.rotate) : i18nString(UIStrings.screenOrientationOptions));
1028
- }
1029
- this.cachedModelDevice = device;
1030
- }
1031
-
1032
- if (device?.isDualScreen) {
1033
- this.spanButton.classList.toggle('hidden', false);
1034
- this.postureItem.classList.toggle('hidden', true);
1035
- } else if (device?.isFoldableScreen) {
1036
- this.spanButton.classList.toggle('hidden', true);
1037
- this.postureItem.classList.toggle('hidden', false);
1038
- this.updateDevicePostureItems();
1039
- } else {
1040
- this.spanButton.classList.toggle('hidden', true);
1041
- this.postureItem.classList.toggle('hidden', true);
1042
- }
1043
- setTitleForButton(this.spanButton, i18nString(UIStrings.toggleDualscreenMode));
1044
884
 
1045
885
  if (this.model.type() === EmulationModel.DeviceModeModel.Type.Device) {
1046
886
  this.lastMode.set(
@@ -1048,39 +888,20 @@ export class DeviceModeToolbar {
1048
888
  (this.model.mode() as EmulationModel.EmulatedDevices.Mode));
1049
889
  }
1050
890
 
1051
- if (this.model.mode() !== this.cachedModelMode && this.model.type() !== EmulationModel.DeviceModeModel.Type.None) {
1052
- this.cachedModelMode = this.model.mode();
1053
- const value = this.persistenceSetting.get();
1054
- const device = this.model.device();
1055
- if (device) {
1056
- value.device = device.title;
1057
- const mode = this.model.mode();
1058
- value.orientation = mode ? mode.orientation : '';
1059
- value.mode = mode ? mode.title : '';
1060
- } else {
1061
- value.device = '';
1062
- value.orientation = '';
1063
- value.mode = '';
1064
- }
1065
- this.persistenceSetting.set(value);
891
+ const value = this.persistenceSetting.get();
892
+ const currentMode = this.model.mode();
893
+ if (device) {
894
+ value.device = device.title;
895
+ value.orientation = currentMode ? currentMode.orientation : '';
896
+ value.mode = currentMode ? currentMode.title : '';
897
+ } else {
898
+ value.device = '';
899
+ value.orientation = '';
900
+ value.mode = '';
1066
901
  }
902
+ this.persistenceSetting.set(value);
1067
903
 
1068
- // When screen orientation is locked by the page (via screen.orientation.lock()),
1069
- // disable the rotate button to prevent user-initiated rotation.
1070
- // When unlocked, restore the button to its normal state.
1071
- if (this.model.isScreenOrientationLocked()) {
1072
- this.modeButton.disabled = true;
1073
- setTitleForButton(this.modeButton, i18nString(UIStrings.screenOrientationLocked));
1074
- } else if (this.cachedModelDevice) {
1075
- const modeCount = this.cachedModelDevice.modes.length;
1076
- this.modeButton.disabled = modeCount < 2;
1077
- setTitleForButton(
1078
- this.modeButton,
1079
- modeCount === 2 ? i18nString(UIStrings.rotate) : i18nString(UIStrings.screenOrientationOptions));
1080
- } else if (this.model.type() === EmulationModel.DeviceModeModel.Type.Responsive) {
1081
- this.modeButton.disabled = false;
1082
- setTitleForButton(this.modeButton, i18nString(UIStrings.rotate));
1083
- }
904
+ this.renderMainToolbar();
1084
905
  }
1085
906
 
1086
907
  restore(): void {
@@ -1100,30 +921,35 @@ export class DeviceModeToolbar {
1100
921
  this.model.emulate(EmulationModel.DeviceModeModel.Type.Responsive, null, null);
1101
922
  }
1102
923
 
1103
- private resizeItem(item: HTMLSelectElement, stripParens = false): void {
1104
- const selectedOption = item.options[item.selectedIndex];
1105
- if (!selectedOption) {
1106
- return;
924
+ private calculateItemWidth(text: string): string {
925
+ if (!text) {
926
+ return '';
927
+ }
928
+ if (this.#itemWidthCache.has(text)) {
929
+ return this.#itemWidthCache.get(text) as string;
1107
930
  }
1108
931
 
1109
- const dummySelect = item.cloneNode(false) as HTMLSelectElement;
1110
- const dummyOption = selectedOption.cloneNode(true) as HTMLOptionElement;
1111
-
1112
- if (stripParens) {
1113
- const parensIndex = dummyOption.text.indexOf(' (');
1114
- if (parensIndex >= 0) {
1115
- dummyOption.text = dummyOption.text.substring(0, parensIndex);
1116
- }
932
+ if (!this.#measuringElement) {
933
+ this.#measuringElement = document.createElement('select');
934
+ this.#measuringElement.className = 'dark-text toolbar-has-dropdown-shrinkable';
935
+ this.#measuringElement.style.width = 'fit-content';
936
+ this.#measuringElement.style.position = 'absolute';
937
+ this.#measuringElement.style.visibility = 'hidden';
938
+ this.#measuringElement.style.pointerEvents = 'none';
939
+ const dummyOption = document.createElement('option');
940
+ this.#measuringElement.appendChild(dummyOption);
941
+ this.#element.appendChild(this.#measuringElement);
1117
942
  }
1118
943
 
1119
- dummySelect.appendChild(dummyOption);
1120
- dummySelect.style.width = 'fit-content';
1121
- dummySelect.style.position = 'absolute';
1122
- dummySelect.style.visibility = 'hidden';
1123
- dummySelect.style.pointerEvents = 'none';
944
+ const dummyOption = this.#measuringElement.options[0];
945
+ dummyOption.textContent = text;
946
+
947
+ const width = this.#measuringElement.offsetWidth;
1124
948
 
1125
- item.parentElement?.appendChild(dummySelect);
1126
- item.style.width = dummySelect.offsetWidth + 'px';
1127
- dummySelect.remove();
949
+ const widthPx = width ? `${width}px` : '';
950
+ if (width > 0) {
951
+ this.#itemWidthCache.set(text, widthPx);
952
+ }
953
+ return widthPx;
1128
954
  }
1129
955
  }