chrome-devtools-frontend 1.0.1626840 → 1.0.1629211

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 (197) hide show
  1. package/front_end/core/common/VersionController.ts +33 -1
  2. package/front_end/core/host/UserMetrics.ts +0 -3
  3. package/front_end/core/root/ExperimentNames.ts +0 -4
  4. package/front_end/core/sdk/CSSContainerQuery.ts +3 -1
  5. package/front_end/core/sdk/CSSModel.ts +23 -0
  6. package/front_end/core/sdk/DOMModel.ts +0 -5
  7. package/front_end/entrypoints/formatter_worker/AcornTokenizer.ts +2 -1
  8. package/front_end/entrypoints/formatter_worker/CSSFormatter.ts +2 -4
  9. package/front_end/entrypoints/formatter_worker/ESTreeWalker.ts +3 -2
  10. package/front_end/entrypoints/formatter_worker/JavaScriptFormatter.ts +13 -0
  11. package/front_end/entrypoints/formatter_worker/Substitute.ts +1 -1
  12. package/front_end/entrypoints/greendev_floaty/FloatyEntrypoint.ts +0 -2
  13. package/front_end/entrypoints/greendev_floaty/greendev_floaty.ts +0 -2
  14. package/front_end/entrypoints/main/MainImpl.ts +0 -10
  15. package/front_end/generated/InspectorBackendCommands.ts +3 -3
  16. package/front_end/generated/protocol.ts +6 -0
  17. package/front_end/models/emulation/DeviceModeModel.ts +4 -0
  18. package/front_end/models/issues_manager/GenericIssue.ts +18 -1
  19. package/front_end/models/issues_manager/descriptions/genericBackUINavigationWouldSkipAd.md +4 -0
  20. package/front_end/models/javascript_metadata/NativeFunctions.js +4 -0
  21. package/front_end/panels/common/common.ts +1 -1
  22. package/front_end/panels/common/freDialog.css +1 -0
  23. package/front_end/panels/console/ConsoleView.ts +2 -2
  24. package/front_end/panels/elements/StylePropertiesSection.ts +6 -2
  25. package/front_end/panels/elements/StylesSidebarPane.ts +9 -8
  26. package/front_end/panels/elements/elements-meta.ts +0 -25
  27. package/front_end/panels/elements/elements.ts +0 -3
  28. package/front_end/panels/emulation/DeviceModeToolbar.ts +346 -265
  29. package/front_end/panels/emulation/DeviceModeView.ts +2 -2
  30. package/front_end/panels/profiler/HeapSnapshotGridNodes.ts +9 -4
  31. package/front_end/panels/profiler/ProfilesPanel.ts +7 -1
  32. package/front_end/panels/sources/AiCodeCompletionPlugin.ts +7 -6
  33. package/front_end/panels/sources/WatchExpressionsSidebarPane.ts +29 -18
  34. package/front_end/panels/sources/sources-meta.ts +7 -0
  35. package/front_end/panels/timeline/TimelineController.ts +1 -2
  36. package/front_end/panels/timeline/TimelinePanel.ts +1 -1
  37. package/front_end/panels/timeline/TimelineUIUtils.ts +2 -10
  38. package/front_end/panels/timeline/timeline-meta.ts +26 -0
  39. package/front_end/third_party/acorn/README.chromium +1 -1
  40. package/front_end/third_party/acorn/acorn.ts +1 -1
  41. package/front_end/third_party/acorn/package/CHANGELOG.md +128 -0
  42. package/front_end/third_party/acorn/package/README.md +42 -19
  43. package/front_end/third_party/acorn/package/dist/acorn.d.mts +883 -26
  44. package/front_end/third_party/acorn/package/dist/acorn.d.ts +859 -268
  45. package/front_end/third_party/acorn/package/dist/acorn.js +420 -115
  46. package/front_end/third_party/acorn/package/dist/acorn.mjs +420 -115
  47. package/front_end/third_party/acorn/package/dist/bin.js +1 -1
  48. package/front_end/third_party/acorn/package/package.json +3 -3
  49. package/front_end/third_party/chromium/README.chromium +1 -1
  50. package/front_end/third_party/puppeteer/README.chromium +2 -2
  51. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Browser.d.ts.map +1 -1
  52. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Browser.js +7 -7
  53. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Browser.js.map +1 -1
  54. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/BrowserContext.d.ts.map +1 -1
  55. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/BrowserContext.js +4 -3
  56. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/BrowserContext.js.map +1 -1
  57. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Frame.d.ts +15 -1
  58. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Frame.d.ts.map +1 -1
  59. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Frame.js.map +1 -1
  60. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/JSHandle.js +1 -1
  61. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/JSHandle.js.map +1 -1
  62. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Page.d.ts +2 -2
  63. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Page.d.ts.map +1 -1
  64. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Page.js +4 -3
  65. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Page.js.map +1 -1
  66. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Frame.d.ts +2 -2
  67. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Frame.d.ts.map +1 -1
  68. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Frame.js.map +1 -1
  69. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/ExecutionContext.d.ts.map +1 -1
  70. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/ExecutionContext.js +1 -0
  71. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/ExecutionContext.js.map +1 -1
  72. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Frame.d.ts +2 -5
  73. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Frame.d.ts.map +1 -1
  74. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Frame.js +4 -0
  75. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Frame.js.map +1 -1
  76. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/FrameManager.d.ts.map +1 -1
  77. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/FrameManager.js +3 -1
  78. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/FrameManager.js.map +1 -1
  79. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/IsolatedWorld.js +1 -1
  80. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/IsolatedWorld.js.map +1 -1
  81. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.d.ts +1 -0
  82. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.d.ts.map +1 -1
  83. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.js +3 -0
  84. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.js.map +1 -1
  85. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/TargetManager.d.ts +4 -0
  86. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/TargetManager.d.ts.map +1 -1
  87. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/TargetManager.js +2 -2
  88. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/TargetManager.js.map +1 -1
  89. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/common/BrowserConnector.d.ts +5 -0
  90. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/common/BrowserConnector.d.ts.map +1 -1
  91. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/common/BrowserConnector.js +9 -1
  92. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/common/BrowserConnector.js.map +1 -1
  93. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/common/EventEmitter.d.ts +5 -1
  94. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/common/EventEmitter.d.ts.map +1 -1
  95. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/common/EventEmitter.js +7 -0
  96. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/common/EventEmitter.js.map +1 -1
  97. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/injected/injected.d.ts +1 -1
  98. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/BrowserLauncher.d.ts.map +1 -1
  99. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/BrowserLauncher.js +6 -3
  100. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/BrowserLauncher.js.map +1 -1
  101. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/ScreenRecorder.d.ts.map +1 -1
  102. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/ScreenRecorder.js +1 -0
  103. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/ScreenRecorder.js.map +1 -1
  104. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.d.ts +1 -1
  105. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js +1 -1
  106. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Mutex.d.ts +2 -2
  107. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.d.ts +1 -1
  108. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.js +1 -1
  109. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.d.ts +23 -2
  110. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +171 -149
  111. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Browser.d.ts.map +1 -1
  112. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Browser.js +7 -7
  113. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Browser.js.map +1 -1
  114. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/BrowserContext.d.ts.map +1 -1
  115. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/BrowserContext.js +4 -3
  116. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/BrowserContext.js.map +1 -1
  117. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Frame.d.ts +15 -1
  118. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Frame.d.ts.map +1 -1
  119. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Frame.js.map +1 -1
  120. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/JSHandle.js +1 -1
  121. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/JSHandle.js.map +1 -1
  122. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Page.d.ts +2 -2
  123. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Page.d.ts.map +1 -1
  124. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Page.js +4 -3
  125. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Page.js.map +1 -1
  126. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Frame.d.ts +2 -2
  127. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Frame.d.ts.map +1 -1
  128. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Frame.js.map +1 -1
  129. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/ExecutionContext.d.ts.map +1 -1
  130. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/ExecutionContext.js +1 -0
  131. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/ExecutionContext.js.map +1 -1
  132. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Frame.d.ts +2 -5
  133. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Frame.d.ts.map +1 -1
  134. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Frame.js +4 -0
  135. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Frame.js.map +1 -1
  136. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/FrameManager.d.ts.map +1 -1
  137. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/FrameManager.js +3 -1
  138. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/FrameManager.js.map +1 -1
  139. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/IsolatedWorld.js +1 -1
  140. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/IsolatedWorld.js.map +1 -1
  141. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.d.ts +1 -0
  142. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.d.ts.map +1 -1
  143. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.js +3 -0
  144. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.js.map +1 -1
  145. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/TargetManager.d.ts +4 -0
  146. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/TargetManager.d.ts.map +1 -1
  147. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/TargetManager.js +2 -2
  148. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/TargetManager.js.map +1 -1
  149. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/common/BrowserConnector.d.ts +5 -0
  150. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/common/BrowserConnector.d.ts.map +1 -1
  151. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/common/BrowserConnector.js +8 -1
  152. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/common/BrowserConnector.js.map +1 -1
  153. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/common/EventEmitter.d.ts +5 -1
  154. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/common/EventEmitter.d.ts.map +1 -1
  155. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/common/EventEmitter.js +8 -1
  156. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/common/EventEmitter.js.map +1 -1
  157. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/BrowserLauncher.d.ts.map +1 -1
  158. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/BrowserLauncher.js +6 -3
  159. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/BrowserLauncher.js.map +1 -1
  160. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/ScreenRecorder.d.ts.map +1 -1
  161. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/ScreenRecorder.js +1 -0
  162. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/ScreenRecorder.js.map +1 -1
  163. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.d.ts +1 -1
  164. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js +1 -1
  165. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.d.ts +1 -1
  166. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.js +1 -1
  167. package/front_end/third_party/puppeteer/package/lib/types.d.ts +23 -2
  168. package/front_end/third_party/puppeteer/package/package.json +2 -2
  169. package/front_end/third_party/puppeteer/package/src/api/Browser.ts +6 -7
  170. package/front_end/third_party/puppeteer/package/src/api/BrowserContext.ts +4 -3
  171. package/front_end/third_party/puppeteer/package/src/api/Frame.ts +21 -1
  172. package/front_end/third_party/puppeteer/package/src/api/JSHandle.ts +1 -1
  173. package/front_end/third_party/puppeteer/package/src/api/Page.ts +9 -4
  174. package/front_end/third_party/puppeteer/package/src/bidi/Frame.ts +2 -1
  175. package/front_end/third_party/puppeteer/package/src/cdp/ExecutionContext.ts +1 -0
  176. package/front_end/third_party/puppeteer/package/src/cdp/Frame.ts +9 -5
  177. package/front_end/third_party/puppeteer/package/src/cdp/FrameManager.ts +3 -1
  178. package/front_end/third_party/puppeteer/package/src/cdp/IsolatedWorld.ts +1 -1
  179. package/front_end/third_party/puppeteer/package/src/cdp/Page.ts +4 -0
  180. package/front_end/third_party/puppeteer/package/src/cdp/TargetManager.ts +2 -2
  181. package/front_end/third_party/puppeteer/package/src/common/BrowserConnector.ts +19 -3
  182. package/front_end/third_party/puppeteer/package/src/common/EventEmitter.ts +10 -1
  183. package/front_end/third_party/puppeteer/package/src/node/BrowserLauncher.ts +7 -3
  184. package/front_end/third_party/puppeteer/package/src/node/ScreenRecorder.ts +1 -0
  185. package/front_end/third_party/puppeteer/package/src/revisions.ts +1 -1
  186. package/front_end/third_party/puppeteer/package/src/util/version.ts +1 -1
  187. package/front_end/ui/legacy/TabbedPane.ts +142 -16
  188. package/front_end/ui/legacy/TextPrompt.docs.ts +51 -0
  189. package/front_end/ui/legacy/TextPrompt.ts +55 -6
  190. package/front_end/ui/legacy/Toolbar.ts +2 -1
  191. package/front_end/ui/legacy/View.ts +2 -1
  192. package/front_end/ui/legacy/ViewManager.ts +11 -5
  193. package/front_end/ui/legacy/textPrompt.css +4 -0
  194. package/mcp/mcp.ts +1 -0
  195. package/package.json +1 -1
  196. package/front_end/panels/elements/NodeStackTraceWidget.ts +0 -76
  197. package/front_end/panels/elements/nodeStackTraceWidget.css +0 -11
@@ -1,8 +1,6 @@
1
1
  // Copyright 2016 The Chromium Authors
2
2
  // Use of this source code is governed by a BSD-style license that can be
3
3
  // found in the LICENSE file.
4
- /* eslint-disable @devtools/no-imperative-dom-api */
5
- /* eslint-disable @devtools/no-lit-render-outside-of-view */
6
4
 
7
5
  import '../../ui/legacy/legacy.js';
8
6
 
@@ -13,10 +11,58 @@ import * as Platform from '../../core/platform/platform.js';
13
11
  import * as EmulationModel from '../../models/emulation/emulation.js';
14
12
  import * as Buttons from '../../ui/components/buttons/buttons.js';
15
13
  import * as UI from '../../ui/legacy/legacy.js';
16
- import {Directives, html, i18nTemplate, type LitTemplate, render} from '../../ui/lit/lit.js';
14
+ import {Directive, Directives, html, i18nTemplate, type LitTemplate, noChange, render} from '../../ui/lit/lit.js';
17
15
  import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
18
16
  import * as MobileThrottling from '../mobile_throttling/mobile_throttling.js';
19
17
 
18
+ class AutoWidthSelectDirective extends Directive.Directive {
19
+ static itemWidthCache = new Map<string, string>();
20
+
21
+ constructor(partInfo: Directive.PartInfo) {
22
+ super(partInfo);
23
+ if (partInfo.type !== Directive.PartType.ELEMENT) {
24
+ throw new Error('AutoWidthSelectDirective must be used as an element directive');
25
+ }
26
+ }
27
+
28
+ override update(part: Directive.ElementPart, [text]: [string]): unknown {
29
+ /* eslint-disable @devtools/no-imperative-dom-api */
30
+ const select = part.element as HTMLSelectElement;
31
+
32
+ let widthPx = AutoWidthSelectDirective.itemWidthCache.get(text);
33
+ if (!widthPx) {
34
+ const measuringElement = document.createElement('select');
35
+ measuringElement.className = select.className;
36
+ measuringElement.style.width = 'fit-content';
37
+ measuringElement.style.position = 'absolute';
38
+ measuringElement.style.visibility = 'hidden';
39
+ measuringElement.style.pointerEvents = 'none';
40
+ measuringElement.appendChild(document.createElement('option'));
41
+ measuringElement.options[0].textContent = text;
42
+
43
+ // Append to the select's parent so it inherits the exact same Shadow DOM styles
44
+ select.parentElement?.appendChild(measuringElement);
45
+ const width = measuringElement.offsetWidth;
46
+ measuringElement.remove();
47
+
48
+ widthPx = width ? `${width}px` : '';
49
+ if (width > 0) {
50
+ AutoWidthSelectDirective.itemWidthCache.set(text, widthPx);
51
+ }
52
+ }
53
+
54
+ select.style.width = widthPx;
55
+ /* eslint-enable @devtools/no-imperative-dom-api */
56
+ return this.render(text);
57
+ }
58
+
59
+ render(_text: string): unknown {
60
+ return noChange;
61
+ }
62
+ }
63
+
64
+ const autoWidthSelect = Directive.directive(AutoWidthSelectDirective);
65
+
20
66
  const UIStrings = {
21
67
  /**
22
68
  * @description Title of the device dimensions selection item in the Device Mode Toolbar.
@@ -188,11 +234,227 @@ const UIStrings = {
188
234
  } as const;
189
235
  const str_ = i18n.i18n.registerUIStrings('panels/emulation/DeviceModeToolbar.ts', UIStrings);
190
236
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
191
- const {ifDefined, styleMap} = Directives;
237
+ const {ifDefined, live} = Directives;
192
238
  const {widget} = UI.Widget;
193
239
  const {bindToSetting} = UI.UIUtils;
194
240
 
195
- export class DeviceModeToolbar {
241
+ export interface DeviceModeOption {
242
+ device: EmulationModel.EmulatedDevices.EmulatedDevice;
243
+ title: string;
244
+ selected: boolean;
245
+ jslogContext: string;
246
+ }
247
+
248
+ export interface ViewInput {
249
+ isResponsive: boolean;
250
+ isFullHeight: boolean;
251
+ widthValue: string;
252
+ heightValue: string;
253
+ heightPlaceholder: string;
254
+ modeButtonTitle: string;
255
+ modeButtonDisabled: boolean;
256
+ showSpanButton: boolean;
257
+ showPostureItem: boolean;
258
+ deviceModeOptions: {
259
+ responsive: {title: string, selected: boolean, jslogContext: string},
260
+ standard: DeviceModeOption[],
261
+ custom: DeviceModeOption[],
262
+ edit: {title: string, jslogContext: string},
263
+ };
264
+ scaleOptions: Array<{title: string, value: number, selected: boolean, jslogContext: string}>;
265
+ dprOptions: Array<{title: string, value: number, selected: boolean, jslogContext: string}>;
266
+ uaOptions: Array<{title: string, value: EmulationModel.DeviceModeModel.UA, selected: boolean, jslogContext: string}>;
267
+ postureOptions: Array<{title: string, value: string, selected: boolean}>;
268
+ selectedDeviceOption: {title: string, selected: boolean, jslogContext: string}|undefined;
269
+ deviceText: string;
270
+ scaleText: string;
271
+ dprText: string;
272
+ uaText: string;
273
+ postureText: string;
274
+ onDeviceChange: (event: Event) => void;
275
+ onWidthChange: (event: Event) => void;
276
+ onHeightChange: (event: Event) => void;
277
+ onScaleChange: (event: Event) => void;
278
+ onDeviceScaleChange: (event: Event) => void;
279
+ onUAChange: (event: Event) => void;
280
+ onPostureChange: (event: Event) => void;
281
+ onModeMenuClick: (event: Event) => void;
282
+ onSpanClick: () => void;
283
+ onMoreOptionsClick: (event: Event) => void;
284
+ autoAdjustScaleSetting: Common.Settings.Setting<boolean>;
285
+ showDeviceScaleFactorSetting: Common.Settings.Setting<boolean>;
286
+ showUserAgentTypeSetting: Common.Settings.Setting<boolean>;
287
+ }
288
+
289
+ export type View = (input: ViewInput, output: object, target: HTMLElement|DocumentFragment) => void;
290
+
291
+ function createSizeInput(
292
+ title: string, jslogContext: string, value: string|undefined, disabled: boolean|undefined,
293
+ placeholder: string|undefined, onChange: (event: Event) => void): LitTemplate {
294
+ return html`
295
+ <input type="number"
296
+ max=${EmulationModel.DeviceModeModel.MaxDeviceSize}
297
+ min=${EmulationModel.DeviceModeModel.MinDeviceSize}
298
+ title=${title}
299
+ class="device-mode-size-input"
300
+ .disabled=${disabled ?? false}
301
+ jslog=${VisualLogging.textField().track({change: true}).context(jslogContext)}
302
+ .value=${value ?? ''}
303
+ placeholder=${ifDefined(placeholder)}
304
+ @change=${onChange}
305
+ @keydown=${(event: Event): void => {
306
+ const input = event.target as HTMLInputElement;
307
+ let modifiedValue = UI.UIUtils.modifiedFloatNumber(Number(input.value), event);
308
+ if (modifiedValue === null) {
309
+ return;
310
+ }
311
+ modifiedValue = Math.min(modifiedValue, EmulationModel.DeviceModeModel.MaxDeviceSize);
312
+ modifiedValue = Math.max(modifiedValue, EmulationModel.DeviceModeModel.MinDeviceSize);
313
+
314
+ event.preventDefault();
315
+ input.value = String(modifiedValue);
316
+ input.dispatchEvent(new Event('change'));
317
+ }}>`;
318
+ }
319
+
320
+ export const DEFAULT_VIEW: View = (input, _output, target) => {
321
+ // clang-format off
322
+ render(html`
323
+ <devtools-toolbar class="main-toolbar">
324
+ <div class="device-mode-empty-toolbar-element"></div>
325
+ ${i18nTemplate(str_, UIStrings.dimensions, {PH1: html`
326
+ <select class="dark-text toolbar-has-dropdown-shrinkable"
327
+ ${autoWidthSelect(input.deviceText)}
328
+ title=${i18nString(UIStrings.deviceType)}
329
+ aria-label=${i18nString(UIStrings.deviceType)}
330
+ @change=${input.onDeviceChange}
331
+ .value=${live(input.selectedDeviceOption === input.deviceModeOptions.responsive ? 'Responsive' : (input.selectedDeviceOption?.title || 'Responsive'))}
332
+ jslog=${VisualLogging.dropDown().track({change: true}).context('device')}>
333
+ <option value="Responsive" ?selected=${input.deviceModeOptions.responsive.selected} jslog=${VisualLogging.item(input.deviceModeOptions.responsive.jslogContext).track({click: true})}>
334
+ ${input.deviceModeOptions.responsive.title}
335
+ </option>
336
+ ${input.deviceModeOptions.standard.length > 0 ? html`
337
+ <optgroup label="Standard">
338
+ ${input.deviceModeOptions.standard.map(o => html`<option value=${o.title} ?selected=${o.selected} jslog=${VisualLogging.item(o.jslogContext).track({click: true})}>${o.title}</option>`)}
339
+ </optgroup>
340
+ ` : ''}
341
+ ${input.deviceModeOptions.custom.length > 0 ? html`
342
+ <optgroup label="Custom">
343
+ ${input.deviceModeOptions.custom.map(o => html`<option value=${o.title} ?selected=${o.selected} jslog=${VisualLogging.item(o.jslogContext).track({click: true})}>${o.title}</option>`)}
344
+ </optgroup>
345
+ ` : ''}
346
+ <option value="Edit" jslog=${VisualLogging.item(input.deviceModeOptions.edit.jslogContext).track({click: true})}>
347
+ ${input.deviceModeOptions.edit.title}
348
+ </option>
349
+ </select>`})}
350
+
351
+ ${createSizeInput(i18nString(UIStrings.width), 'width', input.widthValue, !input.isResponsive, '', input.onWidthChange)}
352
+
353
+ <div class="device-mode-x">×</div>
354
+ ${createSizeInput(i18nString(UIStrings.heightLeaveEmptyForFull), 'height', input.heightValue, !input.isResponsive, input.heightPlaceholder, input.onHeightChange)}
355
+
356
+ <div class="device-mode-empty-toolbar-element"></div>
357
+ <select class="dark-text toolbar-has-dropdown-shrinkable"
358
+ ${autoWidthSelect(input.scaleText)}
359
+ title=${i18nString(UIStrings.zoom)}
360
+ aria-label=${i18nString(UIStrings.zoom)}
361
+ @change=${input.onScaleChange}
362
+ .value=${String(input.scaleOptions.find(o => o.selected)?.value || '')}
363
+ jslog=${VisualLogging.dropDown().track({change: true}).context('scale')}>
364
+ ${input.scaleOptions.map(o => html`<option value=${o.value} ?selected=${o.selected} jslog=${VisualLogging.item(o.jslogContext).track({click: true})}>${o.title}</option>`)}
365
+ </select>
366
+
367
+ <devtools-button .data=${{variant: Buttons.Button.Variant.TOOLBAR, iconName: 'center-focus-weak',
368
+ toggledIconName: 'center-focus-weak', toggleType: Buttons.Button.ToggleType.PRIMARY} as Buttons.Button.ButtonData}
369
+ class="toolbar-button" title=${i18nString(UIStrings.autoadjustZoom)}
370
+ ${bindToSetting(input.autoAdjustScaleSetting)}>
371
+ </devtools-button>
372
+
373
+ <div class="device-mode-empty-toolbar-element"></div>
374
+
375
+ ${input.showDeviceScaleFactorSetting.get() ? html`
376
+ <div class="toolbar-text">
377
+ ${i18nTemplate(str_, UIStrings.dpr, {
378
+ PH1: html`
379
+ <select class="dark-text toolbar-has-dropdown-shrinkable"
380
+ ${autoWidthSelect(input.dprText)}
381
+ title=${i18nString(UIStrings.devicePixelRatio)}
382
+ aria-label=${i18nString(UIStrings.devicePixelRatio)}
383
+ @change=${input.onDeviceScaleChange}
384
+ .value=${String(input.dprOptions.find(o => o.selected)?.value || '')}
385
+ jslog=${VisualLogging.dropDown().track({change: true}).context('device-pixel-ratio')}
386
+ ?disabled=${!input.isResponsive}>
387
+ ${input.dprOptions.map(o => html`<option value=${o.value} ?selected=${o.selected} jslog=${VisualLogging.item(o.jslogContext).track({click: true})}>${o.title}</option>`)}
388
+ </select>`
389
+ })}
390
+ </div>` : ''}
391
+
392
+ <div class="device-mode-empty-toolbar-element"></div>
393
+ ${input.showUserAgentTypeSetting.get() ? html`
394
+ <select class="dark-text toolbar-has-dropdown-shrinkable"
395
+ ${autoWidthSelect(input.uaText)}
396
+ title=${i18nString(UIStrings.deviceType)}
397
+ aria-label=${i18nString(UIStrings.deviceType)}
398
+ @change=${input.onUAChange}
399
+ .value=${input.uaOptions.find(o => o.selected)?.value || ''}
400
+ jslog=${VisualLogging.dropDown().track({change: true}).context('device-type')}
401
+ ?disabled=${!input.isResponsive}>
402
+ ${input.uaOptions.map(o => html`<option value=${o.value} ?selected=${o.selected} jslog=${VisualLogging.item(o.jslogContext).track({click: true})}>${o.title}</option>`)}
403
+ </select>` : ''}
404
+ <select class="dark-text" ${widget(MobileThrottling.NetworkThrottlingSelector.NetworkThrottlingSelect, {
405
+ title: i18nString(UIStrings.throttling),
406
+ bindToGlobalConditions: true,
407
+ })}></select>
408
+ <select class="dark-text toolbar-has-dropdown-shrinkable" ${widget(
409
+ MobileThrottling.ThrottlingManager.SaveDataOverrideSelect)}></select>
410
+
411
+ <div class="device-mode-empty-toolbar-element"></div>
412
+ <devtools-button class="toolbar-button"
413
+ .data=${{variant: Buttons.Button.Variant.TOOLBAR, iconName: 'screen-rotation'} as Buttons.Button.ButtonData}
414
+ jslog=${VisualLogging.action('screen-rotation').track({click: true})}
415
+ @click=${input.onModeMenuClick}
416
+ .title=${input.modeButtonTitle}
417
+ .disabled=${input.modeButtonDisabled}>
418
+ </devtools-button>
419
+
420
+ <!-- Show dual screen toolbar -->
421
+ ${input.showSpanButton ? html`
422
+ <devtools-button class="toolbar-button"
423
+ .data=${{variant: Buttons.Button.Variant.TOOLBAR, iconName: 'device-fold'} as Buttons.Button.ButtonData}
424
+ jslog=${VisualLogging.action('device-fold').track({click: true})}
425
+ .title=${i18nString(UIStrings.toggleDualscreenMode)}
426
+ @click=${input.onSpanClick}>
427
+ </devtools-button>` : ''}
428
+
429
+ <!-- Show posture toolbar menu for foldable devices. -->
430
+ <div class="device-mode-empty-toolbar-element"></div>
431
+ ${input.showPostureItem ? html`
432
+ <select class="dark-text toolbar-has-dropdown-shrinkable"
433
+ ${autoWidthSelect(input.postureText)}
434
+ title=${i18nString(UIStrings.devicePosture)}
435
+ aria-label=${i18nString(UIStrings.devicePosture)}
436
+ @change=${input.onPostureChange}
437
+ .value=${input.postureOptions.find(o => o.selected)?.value || ''}
438
+ jslog=${VisualLogging.dropDown().track({change: true}).context('device-posture')}>
439
+ ${input.postureOptions.map(o => html`<option value=${o.value} ?selected=${o.selected} jslog=${VisualLogging.item(o.value.toLowerCase()).track({click: true})}>${o.title}</option>`)}
440
+ </select>` : ''}
441
+ </devtools-toolbar>
442
+ <devtools-toolbar class="device-mode-toolbar-options" wrappable>
443
+ <div class="device-mode-empty-toolbar-element"></div>
444
+ <devtools-button
445
+ .data=${{variant: Buttons.Button.Variant.TOOLBAR, iconName: 'dots-vertical', title: i18nString(UIStrings.moreOptions)} as Buttons.Button.ButtonData}
446
+ @click=${input.onMoreOptionsClick}
447
+ jslog=${VisualLogging.dropDown('more-options').track({click: true})}
448
+ ></devtools-button></devtools-toolbar>
449
+ `, target, {container: {
450
+ classes: ['device-mode-toolbar'],
451
+ attributes: {jslog: `${VisualLogging.toolbar('device-mode').track({resize: true})}`,
452
+ }
453
+ }});
454
+ // clang-format on
455
+ };
456
+
457
+ export class DeviceModeToolbar extends UI.Widget.Widget {
196
458
  private model: EmulationModel.DeviceModeModel.DeviceModeModel;
197
459
  private readonly showMediaInspectorSetting: Common.Settings.Setting<boolean>;
198
460
  private readonly showRulersSetting: Common.Settings.Setting<boolean>;
@@ -201,18 +463,16 @@ export class DeviceModeToolbar {
201
463
  private readonly showUserAgentTypeSetting: Common.Settings.Setting<boolean>;
202
464
  private autoAdjustScaleSetting: Common.Settings.Setting<boolean>;
203
465
  private readonly lastMode: Map<EmulationModel.EmulatedDevices.EmulatedDevice, EmulationModel.EmulatedDevices.Mode>;
204
- readonly #element: HTMLDivElement;
205
466
  private readonly emulatedDevicesList: EmulationModel.EmulatedDevices.EmulatedDevicesList;
206
467
  private readonly persistenceSetting: Common.Settings.Setting<{device: string, orientation: string, mode: string}>;
207
- private mainToolbar: UI.Toolbar.Toolbar;
208
- private optionsToolbar: UI.Toolbar.Toolbar;
209
- readonly #itemWidthCache = new Map<string, string>();
210
- #measuringElement: HTMLSelectElement|null = null;
468
+ private readonly view: View;
211
469
 
212
470
  constructor(
213
471
  model: EmulationModel.DeviceModeModel.DeviceModeModel,
214
- showMediaInspectorSetting: Common.Settings.Setting<boolean>,
215
- showRulersSetting: Common.Settings.Setting<boolean>) {
472
+ showMediaInspectorSetting: Common.Settings.Setting<boolean>, showRulersSetting: Common.Settings.Setting<boolean>,
473
+ view: View = DEFAULT_VIEW) {
474
+ super();
475
+ this.view = view;
216
476
  this.model = model;
217
477
  this.showMediaInspectorSetting = showMediaInspectorSetting;
218
478
  this.showRulersSetting = showRulersSetting;
@@ -220,24 +480,17 @@ export class DeviceModeToolbar {
220
480
  this.deviceOutlineSetting = this.model.deviceOutlineSetting();
221
481
  this.showDeviceScaleFactorSetting =
222
482
  Common.Settings.Settings.instance().createSetting('emulation.show-device-scale-factor', false);
223
- this.showDeviceScaleFactorSetting.addChangeListener(this.update, this);
483
+ this.showDeviceScaleFactorSetting.addChangeListener(this.requestUpdate, this);
224
484
 
225
485
  this.showUserAgentTypeSetting =
226
486
  Common.Settings.Settings.instance().createSetting('emulation.show-user-agent-type', false);
227
- this.showUserAgentTypeSetting.addChangeListener(this.update, this);
487
+ this.showUserAgentTypeSetting.addChangeListener(this.requestUpdate, this);
228
488
 
229
489
  this.autoAdjustScaleSetting =
230
490
  Common.Settings.Settings.instance().createSetting('emulation.auto-adjust-scale', true);
231
491
 
232
492
  this.lastMode = new Map();
233
493
 
234
- this.#element = document.createElement('div');
235
- this.#element.classList.add('device-mode-toolbar');
236
- this.#element.setAttribute('jslog', `${VisualLogging.toolbar('device-mode').track({resize: true})}`);
237
-
238
- this.mainToolbar = this.createMainToolbar();
239
- this.optionsToolbar = this.createOptionsToolbar();
240
-
241
494
  this.emulatedDevicesList = EmulationModel.EmulatedDevices.EmulatedDevicesList.instance();
242
495
  this.emulatedDevicesList.addEventListener(
243
496
  EmulationModel.EmulatedDevices.Events.CUSTOM_DEVICES_UPDATED, this.deviceListChanged, this);
@@ -247,56 +500,16 @@ export class DeviceModeToolbar {
247
500
  this.persistenceSetting = Common.Settings.Settings.instance().createSetting(
248
501
  'emulation.device-mode-value', {device: '', orientation: '', mode: ''});
249
502
 
250
- this.model.toolbarControlsEnabledSetting().addChangeListener(this.update, this);
251
- this.model.scaleSetting().addChangeListener(this.update, this);
252
- this.model.uaSetting().addChangeListener(this.update, this);
253
- this.model.deviceScaleFactorSetting().addChangeListener(this.update, this);
254
- this.model.addEventListener(EmulationModel.DeviceModeModel.Events.UPDATED, this.update, this);
503
+ this.model.toolbarControlsEnabledSetting().addChangeListener(this.requestUpdate, this);
504
+ this.model.scaleSetting().addChangeListener(this.requestUpdate, this);
505
+ this.model.uaSetting().addChangeListener(this.requestUpdate, this);
506
+ this.model.deviceScaleFactorSetting().addChangeListener(this.requestUpdate, this);
507
+ this.model.addEventListener(EmulationModel.DeviceModeModel.Events.UPDATED, this.requestUpdate, this);
255
508
 
256
- this.update();
509
+ this.performUpdate();
257
510
  }
258
511
 
259
- private createEmptyToolbarElement(): HTMLDivElement {
260
- const element = document.createElement('div');
261
- element.classList.add('device-mode-empty-toolbar-element');
262
- return element;
263
- }
264
-
265
- private createSizeInput(
266
- title: string, jslogContext: string, value: string|undefined, disabled: boolean|undefined,
267
- placeholder: string|undefined, onChange: (event: Event) => void): LitTemplate {
268
- return html`
269
- <input type="number"
270
- max=${EmulationModel.DeviceModeModel.MaxDeviceSize}
271
- min=${EmulationModel.DeviceModeModel.MinDeviceSize}
272
- title=${title}
273
- class="device-mode-size-input"
274
- .disabled=${disabled ?? false}
275
- jslog=${VisualLogging.textField().track({change: true}).context(jslogContext)}
276
- .value=${value ?? ''}
277
- placeholder=${ifDefined(placeholder)}
278
- @change=${onChange}
279
- @keydown=${(event: Event): void => {
280
- const input = event.target as HTMLInputElement;
281
- let modifiedValue = UI.UIUtils.modifiedFloatNumber(Number(input.value), event);
282
- if (modifiedValue === null) {
283
- return;
284
- }
285
- modifiedValue = Math.min(modifiedValue, EmulationModel.DeviceModeModel.MaxDeviceSize);
286
- modifiedValue = Math.max(modifiedValue, EmulationModel.DeviceModeModel.MinDeviceSize);
287
-
288
- event.preventDefault();
289
- input.value = String(modifiedValue);
290
- input.dispatchEvent(new Event('change'));
291
- }}>`;
292
- }
293
-
294
- private createMainToolbar(): UI.Toolbar.Toolbar {
295
- const mainToolbar = this.#element.createChild('devtools-toolbar', 'main-toolbar');
296
- return mainToolbar as UI.Toolbar.Toolbar;
297
- }
298
-
299
- private renderMainToolbar(): void {
512
+ override performUpdate(): void {
300
513
  const isResponsive = this.model.type() === EmulationModel.DeviceModeModel.Type.Responsive;
301
514
  const isFullHeight = isResponsive && this.model.isFullHeight();
302
515
  const size = this.model.appliedDeviceSize();
@@ -305,6 +518,24 @@ export class DeviceModeToolbar {
305
518
  const heightPlaceholder = String(size.height);
306
519
 
307
520
  const device = this.model.device();
521
+
522
+ if (this.model.type() === EmulationModel.DeviceModeModel.Type.Device && device) {
523
+ this.lastMode.set(device, (this.model.mode() as EmulationModel.EmulatedDevices.Mode));
524
+ }
525
+
526
+ const value = this.persistenceSetting.get();
527
+ const currentMode = this.model.mode();
528
+ if (device) {
529
+ value.device = device.title;
530
+ value.orientation = currentMode ? currentMode.orientation : '';
531
+ value.mode = currentMode ? currentMode.title : '';
532
+ } else {
533
+ value.device = '';
534
+ value.orientation = '';
535
+ value.mode = '';
536
+ }
537
+ this.persistenceSetting.set(value);
538
+
308
539
  let modeButtonTitle = i18nString(UIStrings.rotate);
309
540
  let modeButtonDisabled = false;
310
541
  if (this.model.isScreenOrientationLocked()) {
@@ -350,149 +581,64 @@ export class DeviceModeToolbar {
350
581
  const uaText = uaOptions.find(o => o.selected)?.title || '';
351
582
  const postureText = postureOptions.find(o => o.selected)?.title || '';
352
583
 
353
- // clang-format off
354
- render(html`
355
- <div class="device-mode-empty-toolbar-element"></div>
356
- ${i18nTemplate(str_, UIStrings.dimensions, {PH1: html`
357
- <select class="dark-text toolbar-has-dropdown-shrinkable"
358
- style=${styleMap({width: this.calculateItemWidth(deviceText)})}
359
- title=${i18nString(UIStrings.deviceType)}
360
- aria-label=${i18nString(UIStrings.deviceType)}
361
- @change=${this.onDeviceChange.bind(this)}
362
- .value=${selectedDeviceOption === deviceModeOptions.responsive ? 'Responsive' : (selectedDeviceOption?.title || 'Responsive')}
363
- jslog=${VisualLogging.dropDown().track({change: true}).context('device')}>
364
- <option value="Responsive" ?selected=${deviceModeOptions.responsive.selected} jslog=${VisualLogging.item(deviceModeOptions.responsive.jslogContext).track({click: true})}>
365
- ${deviceModeOptions.responsive.title}
366
- </option>
367
- ${deviceModeOptions.standard.length > 0 ? html`
368
- <optgroup label="Standard">
369
- ${deviceModeOptions.standard.map(o => html`<option value=${o.title} ?selected=${o.selected} jslog=${VisualLogging.item(o.jslogContext).track({click: true})}>${o.title}</option>`)}
370
- </optgroup>
371
- ` : ''}
372
- ${deviceModeOptions.custom.length > 0 ? html`
373
- <optgroup label="Custom">
374
- ${deviceModeOptions.custom.map(o => html`<option value=${o.title} ?selected=${o.selected} jslog=${VisualLogging.item(o.jslogContext).track({click: true})}>${o.title}</option>`)}
375
- </optgroup>
376
- ` : ''}
377
- <option value="Edit" jslog=${VisualLogging.item(deviceModeOptions.edit.jslogContext).track({click: true})}>
378
- ${deviceModeOptions.edit.title}
379
- </option>
380
- </select>`})}
381
-
382
- ${this.createSizeInput(i18nString(UIStrings.width), 'width', widthValue, !isResponsive, '', (event: Event) => {
584
+ const enabled = this.model.toolbarControlsEnabledSetting().get();
585
+ this.contentElement.classList.toggle('disabled', !enabled);
586
+
587
+ const input: ViewInput = {
588
+ isResponsive,
589
+ isFullHeight,
590
+ widthValue,
591
+ heightValue,
592
+ heightPlaceholder,
593
+ modeButtonTitle,
594
+ modeButtonDisabled,
595
+ showSpanButton,
596
+ showPostureItem,
597
+ deviceModeOptions,
598
+ scaleOptions,
599
+ dprOptions,
600
+ uaOptions,
601
+ postureOptions,
602
+ selectedDeviceOption,
603
+ deviceText,
604
+ scaleText,
605
+ dprText,
606
+ uaText,
607
+ postureText,
608
+ onDeviceChange: this.onDeviceChange.bind(this),
609
+ onWidthChange: (event: Event) => {
383
610
  const width = Number((event.target as HTMLInputElement).value);
384
611
  if (this.autoAdjustScaleSetting.get()) {
385
612
  this.model.setWidthAndScaleToFit(width);
386
613
  } else {
387
614
  this.model.setWidth(width);
388
615
  }
389
- })}
390
-
391
- <div class="device-mode-x">×</div>
392
- ${this.createSizeInput(i18nString(UIStrings.heightLeaveEmptyForFull), 'height', heightValue, !isResponsive, heightPlaceholder, (event: Event) => {
616
+ },
617
+ onHeightChange: (event: Event) => {
393
618
  const height = Number((event.target as HTMLInputElement).value);
394
619
  if (this.autoAdjustScaleSetting.get()) {
395
620
  this.model.setHeightAndScaleToFit(height);
396
621
  } else {
397
622
  this.model.setHeight(height);
398
623
  }
399
- })}
400
-
401
- <div class="device-mode-empty-toolbar-element"></div>
402
- <select class="dark-text toolbar-has-dropdown-shrinkable"
403
- style=${styleMap({width: this.calculateItemWidth(scaleText)})}
404
- title=${i18nString(UIStrings.zoom)}
405
- aria-label=${i18nString(UIStrings.zoom)}
406
- @change=${this.onScaleChange.bind(this)}
407
- .value=${String(scaleOptions.find(o => o.selected)?.value || '')}
408
- jslog=${VisualLogging.dropDown().track({change: true}).context('scale')}>
409
- ${scaleOptions.map(o => html`<option value=${o.value} ?selected=${o.selected} jslog=${VisualLogging.item(o.jslogContext).track({click: true})}>${o.title}</option>`)}
410
- </select>
411
-
412
- <devtools-button .data=${{variant: Buttons.Button.Variant.TOOLBAR, iconName: 'center-focus-weak',
413
- toggledIconName: 'center-focus-weak', toggleType: Buttons.Button.ToggleType.PRIMARY} as Buttons.Button.ButtonData}
414
- class="toolbar-button" title=${i18nString(UIStrings.autoadjustZoom)}
415
- ${bindToSetting(this.autoAdjustScaleSetting)}>
416
- </devtools-button>
417
-
418
- <div class="device-mode-empty-toolbar-element"></div>
419
-
420
- ${this.showDeviceScaleFactorSetting.get() ? html`
421
- ${i18nTemplate(str_, UIStrings.dpr, {
422
- PH1: html`
423
- <select class="dark-text toolbar-has-dropdown-shrinkable"
424
- style=${styleMap({width: this.calculateItemWidth(dprText)})}
425
- title=${i18nString(UIStrings.devicePixelRatio)}
426
- aria-label=${i18nString(UIStrings.devicePixelRatio)}
427
- @change=${this.onDeviceScaleChange.bind(this)}
428
- .value=${String(dprOptions.find(o => o.selected)?.value || '')}
429
- jslog=${VisualLogging.dropDown().track({change: true}).context('device-pixel-ratio')}
430
- ?disabled=${!isResponsive}>
431
- ${dprOptions.map(o => html`<option value=${o.value} ?selected=${o.selected} jslog=${VisualLogging.item(o.jslogContext).track({click: true})}>${o.title}</option>`)}
432
- </select>`
433
- })}` : ''}
434
-
435
- <div class="device-mode-empty-toolbar-element"></div>
436
- ${this.showUserAgentTypeSetting.get() ? html`
437
- <select class="dark-text toolbar-has-dropdown-shrinkable"
438
- style=${styleMap({width: this.calculateItemWidth(uaText)})}
439
- title=${i18nString(UIStrings.deviceType)}
440
- aria-label=${i18nString(UIStrings.deviceType)}
441
- @change=${this.onUAChange.bind(this)}
442
- .value=${uaOptions.find(o => o.selected)?.value || ''}
443
- jslog=${VisualLogging.dropDown().track({change: true}).context('device-type')}
444
- ?disabled=${!isResponsive}>
445
- ${uaOptions.map(o => html`<option value=${o.value} ?selected=${o.selected} jslog=${VisualLogging.item(o.jslogContext).track({click: true})}>${o.title}</option>`)}
446
- </select>` : ''}
447
- <select class="dark-text" ${widget(MobileThrottling.NetworkThrottlingSelector.NetworkThrottlingSelect, {
448
- title: i18nString(UIStrings.throttling),
449
- bindToGlobalConditions: true,
450
- })}></select>
451
- <select class="dark-text toolbar-has-dropdown-shrinkable" ${widget(
452
- MobileThrottling.ThrottlingManager.SaveDataOverrideSelect)}></select>
453
-
454
- <div class="device-mode-empty-toolbar-element"></div>
455
- <devtools-button class="toolbar-button"
456
- .data=${{variant: Buttons.Button.Variant.TOOLBAR, iconName: 'screen-rotation',
457
- disabled: modeButtonDisabled} as Buttons.Button.ButtonData}
458
- jslog=${VisualLogging.action('screen-rotation').track({click: true})}
459
- @click=${this.modeMenuClicked.bind(this)}
460
- .title=${modeButtonTitle}>
461
- </devtools-button>
462
-
463
- <!-- Show dual screen toolbar -->
464
- ${showSpanButton ? html`
465
- <devtools-button class="toolbar-button"
466
- .data=${{variant: Buttons.Button.Variant.TOOLBAR, iconName: 'device-fold'} as Buttons.Button.ButtonData}
467
- jslog=${VisualLogging.action('device-fold').track({click: true})}
468
- .title=${i18nString(UIStrings.toggleDualscreenMode)}
469
- @click=${this.spanClicked.bind(this)}>
470
- </devtools-button>` : ''}
471
-
472
- <!-- Show posture toolbar menu for foldable devices. -->
473
- <div class="device-mode-empty-toolbar-element"></div>
474
- ${showPostureItem ? html`
475
- <select class="dark-text toolbar-has-dropdown-shrinkable"
476
- style=${styleMap({width: this.calculateItemWidth(postureText)})}
477
- title=${i18nString(UIStrings.devicePosture)}
478
- aria-label=${i18nString(UIStrings.devicePosture)}
479
- @change=${this.onPostureChange.bind(this)}
480
- .value=${postureOptions.find(o => o.selected)?.value || ''}
481
- jslog=${VisualLogging.dropDown().track({change: true}).context('device-posture')}>
482
- ${postureOptions.map(o => html`<option value=${o.value} ?selected=${o.selected} jslog=${VisualLogging.item(o.value.toLowerCase()).track({click: true})}>${o.title}</option>`)}
483
- </select>` : ''}`, this.mainToolbar, {host: this});
484
- // clang-format on
485
- }
624
+ },
625
+ onScaleChange: this.onScaleChange.bind(this),
626
+ onDeviceScaleChange: this.onDeviceScaleChange.bind(this),
627
+ onUAChange: this.onUAChange.bind(this),
628
+ onPostureChange: this.onPostureChange.bind(this),
629
+ onModeMenuClick: this.modeMenuClicked.bind(this),
630
+ onSpanClick: this.spanClicked.bind(this),
631
+ onMoreOptionsClick: (event: Event) => {
632
+ const contextMenu = new UI.ContextMenu.ContextMenu(event);
633
+ this.appendOptionsMenuItems(contextMenu);
634
+ void contextMenu.show();
635
+ },
636
+ autoAdjustScaleSetting: this.autoAdjustScaleSetting,
637
+ showDeviceScaleFactorSetting: this.showDeviceScaleFactorSetting,
638
+ showUserAgentTypeSetting: this.showUserAgentTypeSetting,
639
+ };
486
640
 
487
- private createOptionsToolbar(): UI.Toolbar.Toolbar {
488
- const optionsToolbar = this.#element.createChild('devtools-toolbar', 'device-mode-toolbar-options');
489
- optionsToolbar.wrappable = true;
490
- optionsToolbar.appendToolbarItem(new UI.Toolbar.ToolbarItem(this.createEmptyToolbarElement()));
491
- const moreOptionsButton = new UI.Toolbar.ToolbarMenuButton(
492
- this.appendOptionsMenuItems.bind(this), true, undefined, 'more-options', 'dots-vertical');
493
- moreOptionsButton.setTitle(i18nString(UIStrings.moreOptions));
494
- optionsToolbar.appendToolbarItem(moreOptionsButton);
495
- return optionsToolbar;
641
+ this.view(input, {}, this.contentElement);
496
642
  }
497
643
 
498
644
  private getDevicePostureOptions(): Array<{title: string, value: string, selected: boolean}> {
@@ -737,7 +883,7 @@ export class DeviceModeToolbar {
737
883
  const value = (event.target as HTMLSelectElement).value;
738
884
  if (value === 'Edit') {
739
885
  this.emulatedDevicesList.revealCustomSetting();
740
- this.renderMainToolbar();
886
+ this.requestUpdate();
741
887
  } else if (value === 'Responsive') {
742
888
  this.switchToResponsive();
743
889
  } else {
@@ -751,7 +897,7 @@ export class DeviceModeToolbar {
751
897
  }
752
898
 
753
899
  private deviceListChanged(): void {
754
- this.renderMainToolbar();
900
+ this.requestUpdate();
755
901
  const device = this.model.device();
756
902
  if (!device) {
757
903
  return;
@@ -875,39 +1021,6 @@ export class DeviceModeToolbar {
875
1021
  return `${(this.model.scale() * 100).toFixed(0)}`;
876
1022
  }
877
1023
 
878
- element(): Element {
879
- return this.#element;
880
- }
881
-
882
- update(): void {
883
- const enabled = this.model.toolbarControlsEnabledSetting().get();
884
- this.mainToolbar.setEnabled(enabled);
885
- this.optionsToolbar.setEnabled(enabled);
886
-
887
- const device = this.model.device();
888
-
889
- if (this.model.type() === EmulationModel.DeviceModeModel.Type.Device) {
890
- this.lastMode.set(
891
- (this.model.device() as EmulationModel.EmulatedDevices.EmulatedDevice),
892
- (this.model.mode() as EmulationModel.EmulatedDevices.Mode));
893
- }
894
-
895
- const value = this.persistenceSetting.get();
896
- const currentMode = this.model.mode();
897
- if (device) {
898
- value.device = device.title;
899
- value.orientation = currentMode ? currentMode.orientation : '';
900
- value.mode = currentMode ? currentMode.title : '';
901
- } else {
902
- value.device = '';
903
- value.orientation = '';
904
- value.mode = '';
905
- }
906
- this.persistenceSetting.set(value);
907
-
908
- this.renderMainToolbar();
909
- }
910
-
911
1024
  restore(): void {
912
1025
  for (const device of this.allDevices()) {
913
1026
  if (device.title === this.persistenceSetting.get().device) {
@@ -924,36 +1037,4 @@ export class DeviceModeToolbar {
924
1037
 
925
1038
  this.model.emulate(EmulationModel.DeviceModeModel.Type.Responsive, null, null);
926
1039
  }
927
-
928
- private calculateItemWidth(text: string): string {
929
- if (!text) {
930
- return '';
931
- }
932
- if (this.#itemWidthCache.has(text)) {
933
- return this.#itemWidthCache.get(text) as string;
934
- }
935
-
936
- if (!this.#measuringElement) {
937
- this.#measuringElement = document.createElement('select');
938
- this.#measuringElement.className = 'dark-text toolbar-has-dropdown-shrinkable';
939
- this.#measuringElement.style.width = 'fit-content';
940
- this.#measuringElement.style.position = 'absolute';
941
- this.#measuringElement.style.visibility = 'hidden';
942
- this.#measuringElement.style.pointerEvents = 'none';
943
- const dummyOption = document.createElement('option');
944
- this.#measuringElement.appendChild(dummyOption);
945
- this.#element.appendChild(this.#measuringElement);
946
- }
947
-
948
- const dummyOption = this.#measuringElement.options[0];
949
- dummyOption.textContent = text;
950
-
951
- const width = this.#measuringElement.offsetWidth;
952
-
953
- const widthPx = width ? `${width}px` : '';
954
- if (width > 0) {
955
- this.#itemWidthCache.set(text, widthPx);
956
- }
957
- return widthPx;
958
- }
959
1040
  }