chrome-devtools-frontend 1.0.1669021 → 1.0.1672388

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 (208) hide show
  1. package/.agents/skills/fixing-skipped-tests/SKILL.md +6 -6
  2. package/.agents/skills/gerrit-cli/SKILL.md +4 -10
  3. package/.agents/skills/gerrit-cli/references/commands.md +45 -2
  4. package/.agents/skills/gerrit-cli/references/workflows.md +7 -0
  5. package/.agents/skills/gerrit-cli/scripts/import_upstream.py +34 -152
  6. package/config/typescript/tsconfig.eslint.json +1 -1
  7. package/docs/contributing/settings-experiments-features.md +2 -8
  8. package/docs/ecosystem/automatic_workspace_folders.md +0 -19
  9. package/eslint.config.mjs +2 -2
  10. package/front_end/core/common/Lazy.ts +3 -3
  11. package/front_end/core/common/SettingRegistration.ts +0 -7
  12. package/front_end/core/common/Settings.ts +0 -38
  13. package/front_end/core/host/UserMetrics.ts +187 -953
  14. package/front_end/core/host/UserMetricsEnums.d.ts +949 -0
  15. package/front_end/core/root/Runtime.ts +21 -133
  16. package/front_end/core/sdk/CSSMetadata.ts +70 -65
  17. package/front_end/core/sdk/CategorizedBreakpoint.ts +0 -1
  18. package/front_end/core/sdk/ChildTargetManager.ts +0 -2
  19. package/front_end/core/sdk/EmulationModel.ts +0 -2
  20. package/front_end/core/sdk/EventBreakpointsModel.ts +0 -4
  21. package/front_end/core/sdk/NetworkManager.ts +0 -3
  22. package/front_end/core/sdk/PageResourceLoader.ts +9 -9
  23. package/front_end/core/sdk/RehydratingConnection.ts +52 -4
  24. package/front_end/core/sdk/Target.ts +0 -4
  25. package/front_end/devtools_compatibility.js +886 -0
  26. package/front_end/entrypoints/inspector_main/RenderingOptions.ts +7 -6
  27. package/front_end/entrypoints/main/ExecutionContextSelector.ts +0 -1
  28. package/front_end/entrypoints/main/MainImpl.ts +18 -22
  29. package/front_end/foundation/Universe.ts +7 -2
  30. package/front_end/generated/InspectorBackendCommands.ts +3 -10
  31. package/front_end/generated/protocol-mapping.d.ts +0 -42
  32. package/front_end/generated/protocol-proxy-api.d.ts +0 -38
  33. package/front_end/generated/protocol.ts +9 -169
  34. package/front_end/models/ai_assistance/AiAgent2.ts +11 -3
  35. package/front_end/models/ai_assistance/AiConversation.ts +1 -0
  36. package/front_end/models/ai_assistance/AiHistoryStorage.ts +8 -5
  37. package/front_end/models/ai_assistance/AiSetting.ts +108 -0
  38. package/front_end/models/ai_assistance/AiUtils.ts +47 -0
  39. package/front_end/models/ai_assistance/ChangeManager.ts +1 -0
  40. package/front_end/models/ai_assistance/ExtensionScope.ts +3 -12
  41. package/front_end/models/ai_assistance/agents/AiAgent.ts +11 -18
  42. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +3 -0
  43. package/front_end/models/ai_assistance/agents/ExecuteJavascript.ts +42 -17
  44. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +3 -2
  45. package/front_end/models/ai_assistance/agents/StorageAgent.ts +253 -155
  46. package/front_end/models/ai_assistance/agents/StylingAgent.snapshot.txt +2 -2
  47. package/front_end/models/ai_assistance/ai_assistance.ts +4 -0
  48. package/front_end/models/ai_assistance/contexts/PerformanceTraceContext.ts +12 -6
  49. package/front_end/models/ai_assistance/data_formatters/FileFormatter.ts +3 -1
  50. package/front_end/models/ai_assistance/data_formatters/NetworkRequestFormatter.ts +1 -0
  51. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.ts +1 -0
  52. package/front_end/models/ai_assistance/injected.ts +1 -0
  53. package/front_end/models/ai_assistance/performance/AICallTree.ts +1 -0
  54. package/front_end/models/ai_assistance/skills/Skill.ts +1 -1
  55. package/front_end/models/ai_assistance/skills/SkillRegistry.ts +2 -0
  56. package/front_end/models/ai_assistance/skills/performance.md +10 -0
  57. package/front_end/models/ai_assistance/tools/ExecuteJavaScript.ts +52 -5
  58. package/front_end/models/ai_assistance/tools/GetElementAccessibilityDetails.ts +4 -4
  59. package/front_end/models/ai_assistance/tools/GetLighthouseAudits.ts +4 -4
  60. package/front_end/models/ai_assistance/tools/GetNetworkRequestDetails.ts +5 -4
  61. package/front_end/models/ai_assistance/tools/GetStyles.ts +5 -4
  62. package/front_end/models/ai_assistance/tools/ListNetworkRequests.ts +5 -4
  63. package/front_end/models/ai_assistance/tools/README.md +1 -1
  64. package/front_end/models/ai_assistance/tools/RecordPerformanceTrace.ts +63 -0
  65. package/front_end/models/ai_assistance/tools/ResolveDevtoolsNodePath.ts +5 -5
  66. package/front_end/models/ai_assistance/tools/Tool.ts +118 -24
  67. package/front_end/models/ai_assistance/tools/ToolRegistry.ts +2 -0
  68. package/front_end/models/badges/UserBadges.ts +2 -0
  69. package/front_end/models/bindings/NetworkProject.ts +5 -0
  70. package/front_end/models/bindings/ResourceScriptMapping.ts +2 -0
  71. package/front_end/models/bindings/ResourceUtils.ts +3 -0
  72. package/front_end/models/crux-manager/CrUXManager.ts +1 -0
  73. package/front_end/models/emulation/DeviceModeModel.ts +140 -7
  74. package/front_end/models/emulation/EmulatedDevices.ts +246 -793
  75. package/front_end/models/issues_manager/IssuesManager.ts +10 -2
  76. package/front_end/models/live-metrics/LiveMetrics.ts +41 -9
  77. package/front_end/models/logs/LogManager.ts +8 -5
  78. package/front_end/models/logs/NetworkLog.ts +1 -0
  79. package/front_end/models/persistence/IsolatedFileSystemManager.ts +21 -3
  80. package/front_end/models/persistence/NetworkPersistenceManager.ts +6 -0
  81. package/front_end/models/project_settings/ProjectSettingsModel.ts +8 -12
  82. package/front_end/models/trace_source_maps_resolver/SourceMapsResolver.ts +3 -1
  83. package/front_end/models/workspace/IgnoreListManager.ts +2 -0
  84. package/front_end/models/workspace/UISourceCode.ts +2 -0
  85. package/front_end/models/workspace_diff/WorkspaceDiff.ts +4 -0
  86. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +22 -22
  87. package/front_end/panels/ai_assistance/ai_assistance-meta.ts +0 -7
  88. package/front_end/panels/ai_assistance/components/ChatInput.ts +4 -0
  89. package/front_end/panels/ai_assistance/components/ChatMessage.ts +79 -34
  90. package/front_end/panels/ai_assistance/components/WalkthroughView.ts +1 -2
  91. package/front_end/panels/ai_assistance/components/walkthroughView.css +1 -1
  92. package/front_end/panels/application/ApplicationPanelSidebar.ts +9 -106
  93. package/front_end/panels/application/ApplicationPanelTreeElement.ts +1 -1
  94. package/front_end/panels/application/application.ts +0 -10
  95. package/front_end/panels/application/components/components.ts +0 -4
  96. package/front_end/panels/browser_debugger/CategorizedBreakpointsSidebarPane.ts +0 -5
  97. package/front_end/panels/browser_debugger/EventListenerBreakpointsSidebarPane.ts +6 -12
  98. package/front_end/panels/browser_debugger/browser_debugger-meta.ts +5 -3
  99. package/front_end/panels/console/ConsoleFormat.ts +2 -62
  100. package/front_end/panels/console/ConsoleViewMessage.ts +14 -7
  101. package/front_end/panels/emulation/AdvancedApp.ts +1 -2
  102. package/front_end/panels/emulation/DeviceModeToolbar.ts +83 -69
  103. package/front_end/panels/emulation/DeviceModeView.ts +4 -134
  104. package/front_end/panels/emulation/DeviceModeWrapper.ts +52 -69
  105. package/front_end/panels/emulation/MediaQueryInspector.ts +2 -2
  106. package/front_end/panels/emulation/emulation-meta.ts +12 -12
  107. package/front_end/panels/issues/IssuesPane.ts +3 -1
  108. package/front_end/panels/lighthouse/LighthouseController.ts +6 -0
  109. package/front_end/panels/linear_memory_inspector/components/LinearMemoryInspector.ts +17 -21
  110. package/front_end/panels/linear_memory_inspector/components/LinearMemoryNavigator.ts +206 -126
  111. package/front_end/panels/linear_memory_inspector/components/linearMemoryInspector.css +1 -1
  112. package/front_end/panels/mobile_throttling/CalibrationController.ts +1 -1
  113. package/front_end/panels/mobile_throttling/MobileThrottlingSelector.ts +3 -3
  114. package/front_end/panels/mobile_throttling/NetworkPanelIndicator.ts +6 -6
  115. package/front_end/panels/mobile_throttling/NetworkThrottlingSelector.ts +11 -11
  116. package/front_end/panels/mobile_throttling/ThrottlingManager.ts +13 -13
  117. package/front_end/panels/mobile_throttling/ThrottlingPresets.ts +10 -10
  118. package/front_end/panels/mobile_throttling/ThrottlingSettingsTab.ts +28 -28
  119. package/front_end/panels/mobile_throttling/mobile_throttling-meta.ts +6 -6
  120. package/front_end/panels/network/RequestHeadersView.ts +1 -1
  121. package/front_end/panels/network/ResourceChunkView.ts +243 -144
  122. package/front_end/panels/network/ResourceDirectSocketChunkView.ts +52 -29
  123. package/front_end/panels/network/ResourceWebSocketFrameView.ts +21 -35
  124. package/front_end/panels/network/resourceChunkView.css +41 -36
  125. package/front_end/panels/profiler/HeapDetachedElementsDataGrid.ts +21 -12
  126. package/front_end/panels/profiler/profiler.ts +4 -0
  127. package/front_end/panels/recorder/RecorderPanel.ts +6 -14
  128. package/front_end/panels/sensors/LocationsSettingsTab.ts +7 -7
  129. package/front_end/panels/settings/AISettingsTab.ts +75 -31
  130. package/front_end/panels/settings/SettingsScreen.ts +8 -13
  131. package/front_end/panels/settings/components/SyncSection.ts +3 -4
  132. package/front_end/panels/settings/emulation/DevicesSettingsTab.ts +200 -18
  133. package/front_end/panels/settings/emulation/devicesSettingsTab.css +18 -0
  134. package/front_end/panels/settings/settings-meta.ts +0 -1
  135. package/front_end/panels/sources/CategorizedBreakpointL10n.ts +0 -2
  136. package/front_end/panels/sources/DebuggerPausedMessage.ts +11 -5
  137. package/front_end/panels/sources/DebuggerPlugin.ts +1 -1
  138. package/front_end/panels/sources/EditingLocationHistoryManager.ts +12 -9
  139. package/front_end/panels/sources/SourcesPanel.ts +19 -17
  140. package/front_end/panels/sources/SourcesView.ts +191 -96
  141. package/front_end/panels/sources/TabbedEditorContainer.ts +3 -4
  142. package/front_end/panels/sources/WatchExpressionsSidebarPane.ts +2 -5
  143. package/front_end/panels/timeline/TimelineUIUtils.ts +15 -0
  144. package/front_end/panels/timeline/timeline-meta.ts +3 -5
  145. package/front_end/third_party/chromium/README.chromium +1 -1
  146. package/front_end/third_party/codemirror.next/chunk/angular.js +1 -1
  147. package/front_end/third_party/codemirror.next/chunk/codemirror.js +1 -1
  148. package/front_end/third_party/codemirror.next/chunk/codemirror.js.map +1 -1
  149. package/front_end/third_party/codemirror.next/chunk/cpp.js +1 -1
  150. package/front_end/third_party/codemirror.next/chunk/cpp.js.map +1 -1
  151. package/front_end/third_party/codemirror.next/chunk/java.js +1 -1
  152. package/front_end/third_party/codemirror.next/chunk/java.js.map +1 -1
  153. package/front_end/third_party/codemirror.next/chunk/legacy.js +1 -1
  154. package/front_end/third_party/codemirror.next/chunk/less.js +1 -1
  155. package/front_end/third_party/codemirror.next/chunk/less.js.map +1 -1
  156. package/front_end/third_party/codemirror.next/chunk/markdown.js +1 -1
  157. package/front_end/third_party/codemirror.next/chunk/markdown.js.map +1 -1
  158. package/front_end/third_party/codemirror.next/chunk/php.js +1 -1
  159. package/front_end/third_party/codemirror.next/chunk/php.js.map +1 -1
  160. package/front_end/third_party/codemirror.next/chunk/python.js +1 -1
  161. package/front_end/third_party/codemirror.next/chunk/python.js.map +1 -1
  162. package/front_end/third_party/codemirror.next/chunk/sass.js +1 -1
  163. package/front_end/third_party/codemirror.next/chunk/sass.js.map +1 -1
  164. package/front_end/third_party/codemirror.next/chunk/svelte.js +1 -1
  165. package/front_end/third_party/codemirror.next/chunk/svelte.js.map +1 -1
  166. package/front_end/third_party/codemirror.next/chunk/vue.js +1 -1
  167. package/front_end/third_party/codemirror.next/chunk/wast.js +1 -1
  168. package/front_end/third_party/codemirror.next/chunk/wast.js.map +1 -1
  169. package/front_end/third_party/codemirror.next/chunk/xml.js +1 -1
  170. package/front_end/third_party/codemirror.next/chunk/xml.js.map +1 -1
  171. package/front_end/third_party/codemirror.next/codemirror.next.d.ts +1332 -193
  172. package/front_end/third_party/codemirror.next/codemirror.next.js +1 -1
  173. package/front_end/third_party/codemirror.next/rebuild.sh +1 -1
  174. package/front_end/tsconfig.compatibility.json +2 -1
  175. package/front_end/tsconfig.json +1 -1
  176. package/front_end/ui/components/icon_button/IconButton.ts +1 -1
  177. package/front_end/ui/components/issue_counter/IssueCounter.ts +1 -1
  178. package/front_end/ui/components/settings/SettingCheckbox.ts +4 -7
  179. package/front_end/ui/components/settings/settings.ts +1 -2
  180. package/front_end/ui/legacy/Treeoutline.ts +52 -30
  181. package/front_end/ui/legacy/components/object_ui/CSSStyleSanitizer.ts +74 -0
  182. package/front_end/ui/legacy/components/object_ui/CustomPreviewComponent.ts +6 -1
  183. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +47 -54
  184. package/front_end/ui/legacy/components/object_ui/object_ui.ts +2 -0
  185. package/front_end/ui/legacy/components/quick_open/CommandMenu.ts +5 -27
  186. package/front_end/ui/legacy/components/settings_ui/SettingsUI.ts +10 -12
  187. package/front_end/ui/settings/SettingUIRegistration.ts +0 -8
  188. package/front_end/ui/visual_logging/KnownContextValues.ts +21 -0
  189. package/inspector_overlay/tsconfig.json +8 -2
  190. package/package.json +3 -2
  191. package/.agents/skills/gerrit-cli/scripts/gerrit_client_wrapper.py +0 -47
  192. package/front_end/core/sdk/CSSModel.test.api.ts +0 -102
  193. package/front_end/core/sdk/DOMModel.test.api.ts +0 -104
  194. package/front_end/foundation/Universe.test.api.ts +0 -73
  195. package/front_end/panels/application/SharedStorageEventsView.ts +0 -128
  196. package/front_end/panels/application/SharedStorageItemsView.ts +0 -179
  197. package/front_end/panels/application/SharedStorageListTreeElement.ts +0 -65
  198. package/front_end/panels/application/SharedStorageModel.ts +0 -249
  199. package/front_end/panels/application/SharedStorageTreeElement.ts +0 -40
  200. package/front_end/panels/application/components/SharedStorageAccessGrid.ts +0 -191
  201. package/front_end/panels/application/components/SharedStorageMetadataView.ts +0 -133
  202. package/front_end/panels/application/components/sharedStorageAccessGrid.css +0 -29
  203. package/front_end/panels/application/components/sharedStorageMetadataView.css +0 -29
  204. package/front_end/panels/application/sharedStorageEventsView.css +0 -9
  205. package/front_end/panels/network/OriginalResourceChunkView.ts +0 -340
  206. package/front_end/panels/network/originalResourceChunkView.css +0 -49
  207. package/front_end/ui/components/settings/SettingDeprecationWarning.ts +0 -47
  208. package/front_end/ui/components/settings/settingDeprecationWarning.css +0 -14
@@ -2,9 +2,8 @@
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
4
 
5
- import * as Common from '../../core/common/common.js';
6
- import type * as Platform from '../../core/platform/platform.js';
7
5
  import type * as SDK from '../../core/sdk/sdk.js';
6
+ import * as ObjectUI from '../../ui/legacy/components/object_ui/object_ui.js';
8
7
 
9
8
  // VGA color palette
10
9
  const ANSI_COLORS = ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'gray'];
@@ -199,65 +198,6 @@ export const format = (fmt: string, args: SDK.RemoteObject.RemoteObject[]): {
199
198
  return {tokens, args: args.slice(argIndex)};
200
199
  };
201
200
 
202
- /**
203
- * This function converts a string into a partial regex string that
204
- * case-insensitively matches it in CSS, even if CSS escapes are used.
205
- *
206
- * @param cssString the target string.
207
- * @returns a partial regex matching the string in CSS.
208
- */
209
- const cssEscapeRegex = (cssString: string): string => {
210
- return [...cssString]
211
- .map(char => {
212
- const charCodes = new Set([char.toLowerCase(), char.toUpperCase()].map(c => c.charCodeAt(0).toString(16)));
213
- const charCodeRegex =
214
- [...charCodes].map(charCode => `\\\\0{0,${6 - charCode.length}}${charCode}[ \\n\\t]?`).join('|');
215
- return `\\\\?(?:${charCodeRegex}|${char})`;
216
- })
217
- .join('');
218
- };
219
-
220
201
  export const updateStyle = (currentStyle: Map<string, {value: string, priority: string}>, styleToAdd: string): void => {
221
- const ALLOWED_PROPERTY_PREFIXES = ['background', 'border', 'color', 'font', 'line', 'margin', 'padding', 'text'];
222
- // We only allow data URLs with the `url()` CSS function.
223
- // The capture group is not intended to grab the whole URL exactly, just enough so we can check the scheme.
224
- // The regex also covers CSS hex-escaped variations of `url()`.
225
- const URL_REGEX = new RegExp(`(?=${cssEscapeRegex('url')}\\(['"]?([^\\)]*))`, 'gi');
226
- // We greedily capture all `image-set()`s to make sure that all of
227
- // them properly use `url()`s to enforce the data URL check later.
228
- const IMAGESET_REGEX = new RegExp(`(?=(${cssEscapeRegex('image-set')}\\(.*))`, 'gi');
229
- const GOOD_IMAGESET_REGEX = /^image-set\((?:(?:(?:url|type)\("[^\\"]*"\)|[\d.]+(?:x|dpi|dpcm|dppx)),?\s*)+\)/i;
230
-
231
- currentStyle.clear();
232
- /* eslint-disable-next-line @devtools/no-imperative-dom-api --
233
- * We're not mutating the DOM here, but just need a temporary
234
- * `<span>` to parse `styleToAdd` into a `CSSStyleDeclaration`.
235
- **/
236
- const buffer = document.createElement('span');
237
- buffer.setAttribute('style', styleToAdd);
238
- for (const property of buffer.style) {
239
- if (!ALLOWED_PROPERTY_PREFIXES.some(
240
- prefix => property.startsWith(prefix) || property.startsWith(`-webkit-${prefix}`))) {
241
- continue;
242
- }
243
-
244
- const value = buffer.style.getPropertyValue(property);
245
- // We make sure every `image-set()` only uses `url()`s for its images.
246
- // If any of them seem malformed, we skip the whole property.
247
- const imageSets = [...value.matchAll(IMAGESET_REGEX)];
248
- if (imageSets.some(match => !GOOD_IMAGESET_REGEX.test(match[1]))) {
249
- continue;
250
- }
251
- // There could be multiple `url()` functions, so we check them all.
252
- // If any of them is not a `data` URL, we skip the whole property.
253
- const potentialUrls = [...value.matchAll(URL_REGEX)].map(match => match[1]);
254
- if (potentialUrls.some(
255
- potentialUrl => !Common.ParsedURL.schemeIs(potentialUrl as Platform.DevToolsPath.UrlString, 'data:'))) {
256
- continue;
257
- }
258
- currentStyle.set(property, {
259
- value,
260
- priority: buffer.style.getPropertyPriority(property),
261
- });
262
- }
202
+ ObjectUI.CSSStyleSanitizer.sanitizeStyle(currentStyle, styleToAdd);
263
203
  };
@@ -880,9 +880,13 @@ export class ConsoleViewMessage implements ConsoleViewportElement {
880
880
  if (includePreview && obj.preview) {
881
881
  renderPreview(true);
882
882
  } else if (obj.type === 'function') {
883
- const functionElement = titleElement.createChild('span');
884
- void ObjectUI.ObjectPropertiesSection.ObjectPropertiesSection.formatObjectAsFunction(obj, functionElement, false);
885
883
  titleElement.classList.add('object-value-function');
884
+ void ObjectUI.ObjectPropertiesSection.formatObjectAsFunction(obj, false).then(t => {
885
+ const fragment = document.createDocumentFragment();
886
+
887
+ render(t, fragment, {host: this});
888
+ titleElement.insertBefore(fragment, titleElement.firstChild);
889
+ });
886
890
  } else if (obj.subtype === 'trustedtype') {
887
891
  titleElement.appendChild(this.formatParameterAsTrustedType(obj));
888
892
  } else {
@@ -923,16 +927,19 @@ export class ConsoleViewMessage implements ConsoleViewportElement {
923
927
  return result;
924
928
 
925
929
  function formatTargetFunction(this: ConsoleViewMessage, targetFunction: SDK.RemoteObject.RemoteObject): void {
926
- const functionElement = document.createElement('span');
927
- const promise = ObjectUI.ObjectPropertiesSection.ObjectPropertiesSection.formatObjectAsFunction(
928
- targetFunction, functionElement, true, includePreview);
929
- result.appendChild(functionElement);
930
+ const promise = ObjectUI.ObjectPropertiesSection.formatObjectAsFunction(targetFunction, true, includePreview);
930
931
  if (targetFunction !== originalFunction) {
931
932
  const note = result.createChild('span', 'object-state-note info-note');
932
933
  UI.Tooltip.Tooltip.install(note, i18nString(UIStrings.functionWasResolvedFromBound));
933
934
  }
934
935
  result.addEventListener('contextmenu', this.contextMenuEventFired.bind(this, originalFunction), false);
935
- void promise.then(() => this.formattedParameterAsFunctionForTest());
936
+ void promise.then(t => {
937
+ const fragment = document.createDocumentFragment();
938
+
939
+ render(t, fragment, {host: this});
940
+ result.insertBefore(fragment, result.firstChild);
941
+ this.formattedParameterAsFunctionForTest();
942
+ });
936
943
  }
937
944
  }
938
945
 
@@ -58,8 +58,7 @@ export class AdvancedApp implements UI.App.App {
58
58
 
59
59
  this.inspectedPagePlaceholder = InspectedPagePlaceholder.instance();
60
60
  this.inspectedPagePlaceholder.addEventListener(Events.UPDATE, this.onSetInspectedPageBounds.bind(this), this);
61
- this.deviceModeView =
62
- DeviceModeWrapper.instance({inspectedPagePlaceholder: this.inspectedPagePlaceholder, forceNew: false});
61
+ this.deviceModeView = new DeviceModeWrapper();
63
62
 
64
63
  UI.DockController.DockController.instance().addEventListener(
65
64
  UI.DockController.Events.BEFORE_DOCK_SIDE_CHANGED, this.onBeforeDockSideChange, this);
@@ -65,163 +65,160 @@ const autoWidthSelect = Directive.directive(AutoWidthSelectDirective);
65
65
 
66
66
  const UIStrings = {
67
67
  /**
68
- * @description Title of the device dimensions selection item in the Device Mode Toolbar.
69
- * webpage in pixels.
68
+ * @description Title of the device dimensions selection item in the device mode toolbar.
70
69
  * @example {Responsive} PH1
71
70
  */
72
71
  dimensions: 'Dimensions: {PH1}',
73
72
  /**
74
- * @description Title of the device pixel ratio selection item in the Device Mode Toolbar.
73
+ * @description Title of the device pixel ratio selection item in the device mode toolbar.
75
74
  * @example {2.0} PH1
76
75
  */
77
76
  dpr: 'DPR: {PH1}',
78
77
  /**
79
- * @description Title of the width input textbox in the Device Mode Toolbar, for the width of the
78
+ * @description Title of the width input textbox in the device mode toolbar, for the width of the
80
79
  * webpage in pixels.
81
80
  */
82
81
  width: 'Width',
83
82
  /**
84
- * @description Title of the height input textbox in the Device Mode Toolbar, for the height of the
83
+ * @description Title of the height input textbox in the device mode toolbar, for the height of the
85
84
  * webpage in pixels. 'leave empty for full' is an instruction to the user - the webpage will be
86
85
  * full-height if this textbox is left empty.
87
86
  */
88
87
  heightLeaveEmptyForFull: 'Height (leave empty for full)',
89
88
  /**
90
- * @description Tooltip text for a drop-down menu where the user can select the zoom percentage of
89
+ * @description Tooltip for a drop-down menu where the user can select the zoom percentage of
91
90
  * the webpage preview.
92
91
  */
93
92
  zoom: 'Zoom',
94
93
  /**
95
- * @description Tooltip tip for a drop-down menu where the user can select the device pixel ratio
96
- * (the ratio between the physical pixels on a screen and CSS logical pixels) of the webpage
94
+ * @description Tooltip for a drop-down menu where the user can select the device pixel ratio
95
+ * (the ratio between physical pixels on a screen and CSS logical pixels) of the webpage
97
96
  * preview.
98
97
  */
99
98
  devicePixelRatio: 'Device pixel ratio',
100
99
  /**
101
- * @description Tooltip tip for a drop-down menu where the user can select the device type e.g.
102
- * Mobile, Desktop.
100
+ * @description Tooltip for a drop-down menu where the user can select the device type (e.g.
101
+ * Mobile or Desktop).
103
102
  */
104
103
  deviceType: 'Device type',
105
104
  /**
106
- * @description Tooltip text for a 'three dots' style menu button which shows an expanded set of options.
105
+ * @description Tooltip text for a three dots menu button which shows an expanded set of options.
107
106
  */
108
107
  moreOptions: 'More options',
109
108
  /**
110
109
  * @description A menu item in the drop-down box that allows the user to select the zoom level.
111
- * Labels the option to automatically fit the preview to the available window space. In the Device Mode Toolbar.
110
+ * Labels the option to automatically fit the preview to the available window space in the device mode toolbar.
112
111
  */
113
112
  fitToWindow: 'Fit to window',
114
113
  /**
115
114
  * @description A menu item in the drop-down box that allows the user to select the device pixel
116
115
  * ratio. Labels the default value which varies between device types, represented by the
117
- * placeholder, which is a number. In the Device Mode Toolbar.
116
+ * placeholder, which is a number, in the device mode toolbar.
118
117
  * @example {4.3} PH1
119
118
  */
120
119
  defaultF: '{PH1} (default)',
121
120
  /**
122
- * @description Command to hide the frame (like a picture frame) around the mobile device screen.
121
+ * @description Command to hide the frame around the mobile device screen.
123
122
  */
124
123
  hideDeviceFrame: 'Hide device frame',
125
124
  /**
126
- * @description Command to show the frame (like a picture frame) around the mobile device screen.
125
+ * @description Command to show the frame around the mobile device screen.
127
126
  */
128
127
  showDeviceFrame: 'Show device frame',
129
128
  /**
130
- * @description Command to hide a display in the Device Mode Toolbar that shows the different media
129
+ * @description Command to hide a display in the device mode toolbar that shows the different media
131
130
  * queries for the device, above the device screen.
132
- * https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries
131
+ * https://web.dev/learn/design/media-queries
133
132
  */
134
133
  hideMediaQueries: 'Hide media queries',
135
134
  /**
136
- * @description Command to show a display in the Device Mode Toolbar that shows the different media
135
+ * @description Command to show a display in the device mode toolbar that shows the different media
137
136
  * queries for the device, above the device screen.
138
- * https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries
137
+ * https://web.dev/learn/design/media-queries
139
138
  */
140
139
  showMediaQueries: 'Show media queries',
141
140
  /**
142
- * @description Command in the Device Mode Toolbar to hide a virtual ruler (for measuring),
141
+ * @description Command in the device mode toolbar to hide a virtual ruler,
143
142
  * displayed above and next to the device screen.
144
143
  */
145
144
  hideRulers: 'Hide rulers',
146
145
  /**
147
- * @description Command in the Device Mode Toolbar to show a virtual ruler (for measuring),
146
+ * @description Command in the device mode toolbar to show a virtual ruler,
148
147
  * displayed above and next to the device screen.
149
148
  */
150
149
  showRulers: 'Show rulers',
151
150
  /**
152
- * @description Command in the Device Mode Toolbar to remove the drop-down menu from the toolbar
151
+ * @description Command in the device mode toolbar to remove the drop-down menu from the toolbar
153
152
  * that lets the user override the device pixel ratio of the emulated device.
154
153
  */
155
154
  removeDevicePixelRatio: 'Remove device pixel ratio',
156
155
  /**
157
- * @description Command in the Device Mode Toolbar to add the drop-down menu to the toolbar
156
+ * @description Command in the device mode toolbar to add the drop-down menu to the toolbar
158
157
  * that lets the user override the device pixel ratio of the emulated device.
159
158
  */
160
159
  addDevicePixelRatio: 'Add device pixel ratio',
161
160
  /**
162
- * @description Command in the Device Mode Toolbar to add the drop-down menu to the toolbar
161
+ * @description Command in the device mode toolbar to remove the drop-down menu from the toolbar
163
162
  * that lets the user set the device type (e.g. Desktop or Mobile).
164
163
  */
165
164
  removeDeviceType: 'Remove device type',
166
165
  /**
167
- * @description Command in the Device Mode Toolbar to add the drop-down menu to the toolbar
168
- * that lets the user add the device type (e.g. Desktop or Mobile).
166
+ * @description Command in the device mode toolbar to add the drop-down menu to the toolbar
167
+ * that lets the user set the device type (e.g. Desktop or Mobile).
169
168
  */
170
169
  addDeviceType: 'Add device type',
171
170
  /**
172
- * @description A context menu item in the Device Mode Toolbar that resets all settings back to
171
+ * @description A context menu item in the device mode toolbar that resets all settings back to
173
172
  * their default values.
174
173
  */
175
174
  resetToDefaults: 'Reset to defaults',
176
175
  /**
177
- * @description A menu command in the Device Mode Toolbar that closes DevTools.
176
+ * @description A menu command in the device mode toolbar that closes DevTools.
178
177
  */
179
178
  closeDevtools: 'Close DevTools',
180
179
  /**
181
- * @description Title of the device selected in the Device Mode Toolbar. The 'response' device is
182
- * not a specific phone/tablet model but a virtual device that can change its height and width
183
- * dynamically by clicking and dragging the sides. 'Response' refers to response design:
184
- * https://developer.mozilla.org/en-US/docs/Learn/CSS/CSS_layout/Responsive_Design
180
+ * @description Title of the device selected in the device mode toolbar. The responsive device is
181
+ * not a specific phone or tablet model but a virtual device that can change its height and width
182
+ * dynamically by clicking and dragging the sides. Responsive refers to responsive design:
183
+ * https://web.dev/learn/design
185
184
  */
186
185
  responsive: 'Responsive',
187
186
  /**
188
- * @description A context menu item in the Device Mode Toolbar that takes the user to a new screen
189
- * where they can add/edit/remove custom devices.
187
+ * @description A context menu item in the device mode toolbar that takes the user to a screen
188
+ * where they can add, edit, or remove custom devices.
190
189
  */
191
190
  edit: 'Edit…',
192
191
  /**
193
- * @description Text describing the current orientation of the phone/device (vs. landscape).
192
+ * @description Text describing the portrait orientation of the emulated device.
194
193
  */
195
194
  portrait: 'Portrait',
196
195
  /**
197
- * @description Text describing the current orientation of the phone/device (vs. portrait).
196
+ * @description Text describing the landscape orientation of the emulated device.
198
197
  */
199
198
  landscape: 'Landscape',
200
199
  /**
201
- * @description Title of button in the Device Mode Toolbar which rotates the device 90 degrees.
200
+ * @description Title of a button in the device mode toolbar which rotates the device 90 degrees.
202
201
  */
203
202
  rotate: 'Rotate',
204
203
  /**
205
- * @description Tooltip of the rotate/screen orientation button.
204
+ * @description Tooltip of the rotate screen orientation button.
206
205
  */
207
206
  screenOrientationOptions: 'Screen orientation options',
208
207
  /**
209
- * @description Tooltip shown on the rotate button when screen orientation is locked by the page
210
- * via screen.orientation.lock().
208
+ * @description Tooltip shown on the rotate button when screen orientation is locked by the page.
211
209
  */
212
210
  screenOrientationLocked: 'Screen orientation is locked by the page',
213
211
  /**
214
- * @description Tooltip for a button which turns on/off dual-screen mode, which emulates devices
215
- * like tablets which have two screens.
212
+ * @description Tooltip for a button which toggles dual-screen mode to emulate devices with two screens.
216
213
  */
217
214
  toggleDualscreenMode: 'Toggle dual-screen mode',
218
215
  /**
219
- * @description Tooltip tip for a drop-down menu where the user can select the device
220
- * posture e.g. Continuous, Folded.
216
+ * @description Tooltip for a drop-down menu where the user can select the device
217
+ * posture (e.g. Continuous or Folded).
221
218
  */
222
219
  devicePosture: 'Device posture',
223
220
  /**
224
- * @description Title of the network throttling selection in the Device Mode Toolbar.
221
+ * @description Title of the network throttling selection in the device mode toolbar.
225
222
  */
226
223
  throttling: 'Throttling',
227
224
  } as const;
@@ -237,6 +234,11 @@ export interface DeviceModeOption {
237
234
  jslogContext: string;
238
235
  }
239
236
 
237
+ export interface DeviceGroup {
238
+ category: EmulationModel.EmulatedDevices.Category;
239
+ options: DeviceModeOption[];
240
+ }
241
+
240
242
  export interface ViewInput {
241
243
  isResponsive: boolean;
242
244
  isFullHeight: boolean;
@@ -249,7 +251,7 @@ export interface ViewInput {
249
251
  showPostureItem: boolean;
250
252
  deviceModeOptions: {
251
253
  responsive: {title: string, selected: boolean, jslogContext: string},
252
- standard: DeviceModeOption[],
254
+ standard: DeviceGroup[],
253
255
  custom: DeviceModeOption[],
254
256
  edit: {title: string, jslogContext: string},
255
257
  };
@@ -325,11 +327,11 @@ export const DEFAULT_VIEW: View = (input, _output, target) => {
325
327
  <option value="Responsive" ?selected=${input.deviceModeOptions.responsive.selected} jslog=${VisualLogging.item(input.deviceModeOptions.responsive.jslogContext).track({click: true})}>
326
328
  ${input.deviceModeOptions.responsive.title}
327
329
  </option>
328
- ${input.deviceModeOptions.standard.length > 0 ? html`
329
- <optgroup label="Standard">
330
- ${input.deviceModeOptions.standard.map(o => html`<option value=${o.title} ?selected=${o.selected} jslog=${VisualLogging.item(o.jslogContext).track({click: true})}>${o.title}</option>`)}
330
+ ${input.deviceModeOptions.standard.map(group => html`
331
+ <optgroup label=${EmulationModel.EmulatedDevices.getCategoryTitle(group.category)}>
332
+ ${group.options.map(o => html`<option value=${o.title} ?selected=${o.selected} jslog=${VisualLogging.item(o.jslogContext).track({click: true})}>${o.title}</option>`)}
331
333
  </optgroup>
332
- ` : ''}
334
+ `)}
333
335
  ${input.deviceModeOptions.custom.length > 0 ? html`
334
336
  <optgroup label="Custom">
335
337
  ${input.deviceModeOptions.custom.map(o => html`<option value=${o.title} ?selected=${o.selected} jslog=${VisualLogging.item(o.jslogContext).track({click: true})}>${o.title}</option>`)}
@@ -571,9 +573,10 @@ export class DeviceModeToolbar extends UI.Widget.Widget {
571
573
  const uaOptions = this.getUserAgentOptions();
572
574
  const postureOptions = this.getDevicePostureOptions();
573
575
 
576
+ const standardOptions = deviceModeOptions.standard.flatMap(g => g.options);
574
577
  const selectedDeviceOption = [
575
578
  deviceModeOptions.responsive,
576
- ...deviceModeOptions.standard,
579
+ ...standardOptions,
577
580
  ...deviceModeOptions.custom,
578
581
  ].find(o => o.selected);
579
582
  const deviceText = selectedDeviceOption ? selectedDeviceOption.title : deviceModeOptions.responsive.title;
@@ -880,18 +883,8 @@ export class DeviceModeToolbar extends UI.Widget.Widget {
880
883
 
881
884
  private getDeviceModeOptions(): {
882
885
  responsive: {title: string, selected: boolean, jslogContext: string},
883
- standard: Array<{
884
- device: EmulationModel.EmulatedDevices.EmulatedDevice,
885
- title: string,
886
- selected: boolean,
887
- jslogContext: string,
888
- }>,
889
- custom: Array<{
890
- device: EmulationModel.EmulatedDevices.EmulatedDevice,
891
- title: string,
892
- selected: boolean,
893
- jslogContext: string,
894
- }>,
886
+ standard: DeviceGroup[],
887
+ custom: DeviceModeOption[],
895
888
  edit: {title: string, jslogContext: string},
896
889
  } {
897
890
  if (!this.model) {
@@ -902,18 +895,39 @@ export class DeviceModeToolbar extends UI.Widget.Widget {
902
895
  edit: {title: i18nString(UIStrings.edit), jslogContext: 'edit'},
903
896
  };
904
897
  }
898
+ const currentDevice = this.model.device();
899
+ const optionsByCategory = new Map<EmulationModel.EmulatedDevices.Category, DeviceModeOption[]>();
900
+
901
+ for (const device of this.standardDevices()) {
902
+ const cat = EmulationModel.EmulatedDevices.deviceCategory(device);
903
+ let list = optionsByCategory.get(cat);
904
+ if (!list) {
905
+ list = [];
906
+ optionsByCategory.set(cat, list);
907
+ }
908
+ list.push({
909
+ device,
910
+ title: device.title,
911
+ selected: currentDevice === device,
912
+ jslogContext: Platform.StringUtilities.toKebabCase(device.title),
913
+ });
914
+ }
915
+
916
+ const groupedStandard: DeviceGroup[] = [];
917
+ for (const category of EmulationModel.EmulatedDevices.CATEGORY_ORDER) {
918
+ const options = optionsByCategory.get(category);
919
+ if (options && options.length > 0) {
920
+ groupedStandard.push({category, options});
921
+ }
922
+ }
923
+
905
924
  return {
906
925
  responsive: {
907
926
  title: i18nString(UIStrings.responsive),
908
927
  selected: this.model.type() === EmulationModel.DeviceModeModel.Type.Responsive,
909
928
  jslogContext: 'responsive',
910
929
  },
911
- standard: this.standardDevices().map(device => ({
912
- device,
913
- title: device.title,
914
- selected: this.model?.device() === device,
915
- jslogContext: Platform.StringUtilities.toKebabCase(device.title),
916
- })),
930
+ standard: groupedStandard,
917
931
  custom: this.customDevices().map(device => ({
918
932
  device,
919
933
  title: device.title,
@@ -5,12 +5,8 @@
5
5
  import * as Common from '../../core/common/common.js';
6
6
  import * as Host from '../../core/host/host.js';
7
7
  import * as i18n from '../../core/i18n/i18n.js';
8
- import * as Platform from '../../core/platform/platform.js';
9
- import * as TextUtils from '../../core/text_utils/text_utils.js';
10
- import type * as Protocol from '../../generated/protocol.js';
11
8
  import * as EmulationModel from '../../models/emulation/emulation.js';
12
9
  import * as Geometry from '../../models/geometry/geometry.js';
13
- import * as Workspace from '../../models/workspace/workspace.js';
14
10
  import * as UI from '../../ui/legacy/legacy.js';
15
11
  import {Directives, html, nothing, render} from '../../ui/lit/lit.js';
16
12
  import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
@@ -24,7 +20,7 @@ const {widget} = UI.Widget;
24
20
 
25
21
  const UIStrings = {
26
22
  /**
27
- * @description Bottom resizer element title in Device Mode View of the Device Toolbar
23
+ * @description Title of the bottom resizer element in the device mode view of the device mode toolbar.
28
24
  */
29
25
  doubleclickForFullHeight: 'Double-click for full height',
30
26
  /**
@@ -290,8 +286,8 @@ export class DeviceModeView extends UI.Widget.VBox {
290
286
  #lastScreenImageSrc?: string;
291
287
  #view: DeviceModeViewView;
292
288
 
293
- constructor(view: DeviceModeViewView = DEFAULT_DEVICE_MODE_VIEW) {
294
- super({useShadowDom: true});
289
+ constructor(element?: HTMLElement, view: DeviceModeViewView = DEFAULT_DEVICE_MODE_VIEW) {
290
+ super(element, {useShadowDom: true});
295
291
  this.#view = view;
296
292
 
297
293
  this.setMinimumSize(150, 150);
@@ -497,17 +493,6 @@ export class DeviceModeView extends UI.Widget.VBox {
497
493
  }
498
494
  }
499
495
 
500
- setNonEmulatedAvailableSize(element: Element): void {
501
- if (this.model.type() !== EmulationModel.DeviceModeModel.Type.None) {
502
- return;
503
- }
504
- const zoomFactor = UI.ZoomManager.ZoomManager.instance().zoomFactor();
505
- const rect = element.getBoundingClientRect();
506
- const availableSize =
507
- new Geometry.Size(Math.max(rect.width * zoomFactor, 1), Math.max(rect.height * zoomFactor, 1));
508
- this.model.setAvailableSize(availableSize, availableSize);
509
- }
510
-
511
496
  private contentAreaResized(): void {
512
497
  const contentArea = this.contentElement.querySelector<HTMLElement>('.device-mode-content-area');
513
498
  if (!contentArea) {
@@ -544,122 +529,7 @@ export class DeviceModeView extends UI.Widget.VBox {
544
529
  override willHide(): void {
545
530
  super.willHide();
546
531
  this.model.emulate(EmulationModel.DeviceModeModel.Type.None, null, null);
547
- }
548
-
549
- async captureScreenshot(): Promise<void> {
550
- const screenshot = await this.model.captureScreenshot(false);
551
- if (screenshot === null) {
552
- return;
553
- }
554
-
555
- const pageImage = new Image();
556
- pageImage.src = 'data:image/png;base64,' + screenshot;
557
- pageImage.onload = async () => {
558
- const scale = pageImage.naturalWidth / this.model.screenRect().width;
559
- const outlineRectFromModel = this.model.outlineRect();
560
- if (!outlineRectFromModel) {
561
- throw new Error('Unable to take screenshot: no outlineRect available.');
562
- }
563
- const outlineRect = outlineRectFromModel.scale(scale);
564
- const screenRect = this.model.screenRect().scale(scale);
565
- const visiblePageRect = this.model.visiblePageRect().scale(scale);
566
- const contentLeft = screenRect.left + visiblePageRect.left - outlineRect.left;
567
- const contentTop = screenRect.top + visiblePageRect.top - outlineRect.top;
568
-
569
- const canvas = new OffscreenCanvas(Math.floor(outlineRect.width),
570
- // Cap the height to not hit the GPU limit.
571
- // https://crbug.com/1260828
572
- Math.min((1 << 14), Math.floor(outlineRect.height)));
573
- const ctx = canvas.getContext('2d', {willReadFrequently: true});
574
- if (!ctx) {
575
- throw new Error('Could not get 2d context from canvas.');
576
- }
577
- ctx.imageSmoothingEnabled = false;
578
-
579
- if (this.model.outlineImage()) {
580
- await this.paintImage(ctx, this.model.outlineImage(), outlineRect.relativeTo(outlineRect));
581
- }
582
- if (this.model.screenImage()) {
583
- await this.paintImage(ctx, this.model.screenImage(), screenRect.relativeTo(outlineRect));
584
- }
585
- ctx.drawImage(pageImage, Math.floor(contentLeft), Math.floor(contentTop));
586
- void this.saveScreenshot(canvas);
587
- };
588
- }
589
-
590
- async captureFullSizeScreenshot(): Promise<void> {
591
- const screenshot = await this.model.captureScreenshot(true);
592
- if (screenshot === null) {
593
- return;
594
- }
595
- return this.saveScreenshotBase64(screenshot);
596
- }
597
-
598
- async captureAreaScreenshot(clip?: Protocol.Page.Viewport): Promise<void> {
599
- const screenshot = await this.model.captureScreenshot(false, clip);
600
- if (screenshot === null) {
601
- return;
602
- }
603
- return this.saveScreenshotBase64(screenshot);
604
- }
605
-
606
- private saveScreenshotBase64(screenshot: string): void {
607
- const pageImage = new Image();
608
- pageImage.src = 'data:image/png;base64,' + screenshot;
609
- pageImage.onload = () => {
610
- const canvas = new OffscreenCanvas(pageImage.naturalWidth,
611
- // Cap the height to not hit the GPU limit.
612
- // https://crbug.com/1260828
613
- Math.min((1 << 14), Math.floor(pageImage.naturalHeight)));
614
- const ctx = canvas.getContext('2d', {willReadFrequently: true});
615
- if (!ctx) {
616
- throw new Error('Could not get 2d context for base64 screenshot.');
617
- }
618
- ctx.imageSmoothingEnabled = false;
619
- ctx.drawImage(pageImage, 0, 0);
620
- void this.saveScreenshot(canvas);
621
- };
622
- }
623
-
624
- private paintImage(ctx: OffscreenCanvasRenderingContext2D|CanvasRenderingContext2D, src: string,
625
- rect: EmulationModel.DeviceModeModel.Rect): Promise<void> {
626
- return new Promise(resolve => {
627
- const image = new Image();
628
- image.crossOrigin = 'Anonymous';
629
- image.srcset = src;
630
- image.onerror = () => resolve();
631
- image.onload = () => {
632
- ctx.drawImage(image, rect.left, rect.top, rect.width, rect.height);
633
- resolve();
634
- };
635
- });
636
- }
637
-
638
- private async saveScreenshot(canvas: OffscreenCanvas): Promise<void> {
639
- const url = this.model.inspectedURL();
640
- let fileName = '';
641
- if (url) {
642
- const withoutFragment = Platform.StringUtilities.removeURLFragment(url);
643
- fileName = Platform.StringUtilities.trimURL(withoutFragment);
644
- }
645
-
646
- const device = this.model.device();
647
- if (device && this.model.type() === EmulationModel.DeviceModeModel.Type.Device) {
648
- fileName += `(${device.title})`;
649
- }
650
- fileName += '.png';
651
- const blob = await canvas.convertToBlob({type: 'image/png'});
652
- const dataUrl = await new Promise<string>((resolve, reject) => {
653
- const reader = new FileReader();
654
- reader.onloadend = () => resolve(reader.result as string);
655
- reader.onerror = reject;
656
- reader.readAsDataURL(blob);
657
- });
658
- const base64 = dataUrl.slice(dataUrl.indexOf(',') + 1);
659
- const contentData = new TextUtils.ContentData.ContentData(base64, /* isBase64=*/ true, 'image/png');
660
- await Workspace.FileManager.FileManager.instance().save(fileName as Platform.DevToolsPath.RawPathString,
661
- contentData, /* forceSaveAs=*/ true);
662
- Workspace.FileManager.FileManager.instance().close(fileName as Platform.DevToolsPath.RawPathString);
532
+ this.model.exitHingeMode();
663
533
  }
664
534
  }
665
535