chrome-devtools-frontend 1.0.1695017 → 1.0.1696961

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 (247) hide show
  1. package/eslint.config.mjs +4 -1
  2. package/front_end/core/common/Base64.ts +14 -4
  3. package/front_end/core/common/Gzip.ts +1 -1
  4. package/front_end/core/common/Object.ts +0 -1
  5. package/front_end/core/platform/Size.ts +43 -0
  6. package/front_end/core/platform/api/HostRuntime.ts +14 -0
  7. package/front_end/core/platform/browser/HostRuntime.ts +66 -0
  8. package/front_end/core/platform/node/HostRuntime.ts +12 -3
  9. package/front_end/core/platform/platform.ts +1 -0
  10. package/front_end/core/sdk/AccessibilityModel.ts +2 -2
  11. package/front_end/core/sdk/CSSMatchedStyles.ts +2 -0
  12. package/front_end/core/sdk/CSSPropertyParserMatchers.ts +29 -0
  13. package/front_end/core/sdk/CSSStyleSheetHeader.ts +2 -2
  14. package/front_end/core/sdk/ConsoleModel.ts +3 -3
  15. package/front_end/core/sdk/DOMModel.ts +10 -6
  16. package/front_end/core/sdk/DebuggerModel.ts +6 -0
  17. package/front_end/core/sdk/NetworkRequest.ts +19 -19
  18. package/front_end/core/sdk/RuntimeModel.ts +5 -0
  19. package/front_end/core/sdk/Script.ts +2 -2
  20. package/front_end/core/sdk/ServerTiming.ts +6 -6
  21. package/front_end/core/sdk/SourceMap.ts +112 -14
  22. package/front_end/core/sdk/SourceMapRangeMappings.ts +43 -0
  23. package/front_end/core/sdk/SourceMapScopeChainEntry.ts +15 -10
  24. package/front_end/core/sdk/SourceMapScopesInfo.ts +92 -10
  25. package/front_end/core/sdk/sdk.ts +2 -0
  26. package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshot.ts +1 -1
  27. package/front_end/entrypoints/inspector_main/RenderingOptions.ts +6 -6
  28. package/front_end/entrypoints/main/MainImpl.ts +1 -1
  29. package/front_end/generated/InspectorBackendCommands.ts +6 -3
  30. package/front_end/generated/protocol-mapping.d.ts +7 -0
  31. package/front_end/generated/protocol-proxy-api.d.ts +5 -0
  32. package/front_end/generated/protocol.ts +36 -3
  33. package/front_end/models/ai_assistance/AiAgent2.ts +27 -27
  34. package/front_end/models/ai_assistance/agents/AiAgent.ts +4 -0
  35. package/front_end/models/ai_assistance/agents/PerformanceAgent.snapshot.txt +2 -2
  36. package/front_end/models/ai_assistance/contexts/AccessibilityContext.ts +1 -0
  37. package/front_end/models/ai_assistance/contexts/DOMNodeContext.ts +5 -4
  38. package/front_end/models/ai_assistance/contexts/FileContext.ts +1 -0
  39. package/front_end/models/ai_assistance/contexts/PerformanceTraceContext.ts +1 -0
  40. package/front_end/models/ai_assistance/contexts/RequestContext.ts +1 -0
  41. package/front_end/models/ai_assistance/contexts/StorageContext.ts +1 -0
  42. package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.snapshot.txt +5 -5
  43. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +6 -6
  44. package/front_end/models/ai_assistance/tools/GetElementAccessibilityDetails.ts +5 -8
  45. package/front_end/models/ai_assistance/tools/GetNetworkRequestDetails.ts +4 -8
  46. package/front_end/models/ai_assistance/tools/GetSourceContent.ts +7 -5
  47. package/front_end/models/ai_assistance/tools/GetStyles.ts +10 -6
  48. package/front_end/models/ai_assistance/tools/ListSources.ts +26 -7
  49. package/front_end/models/ai_assistance/tools/ResolveDevtoolsNodePath.ts +5 -10
  50. package/front_end/models/ai_assistance/tools/Tool.ts +8 -1
  51. package/front_end/models/bindings/FileUtils.ts +18 -40
  52. package/front_end/models/bindings/TempFile.ts +5 -12
  53. package/front_end/models/comment_manager/CommentManager.ts +30 -6
  54. package/front_end/models/crux-manager/CrUXManager.ts +8 -5
  55. package/front_end/models/emulation/DeviceModeModel.ts +55 -105
  56. package/front_end/models/heap_snapshot/HeapSnapshotProxy.ts +1 -1
  57. package/front_end/models/issues_manager/CookieIssue.ts +1 -1
  58. package/front_end/models/issues_manager/DeprecationIssue.ts +2 -2
  59. package/front_end/models/javascript_metadata/NativeFunctions.ts +1 -5
  60. package/front_end/models/live-metrics/LiveMetrics.ts +1 -1
  61. package/front_end/models/persistence/IsolatedFileSystem.ts +16 -23
  62. package/front_end/models/trace/insights/DuplicatedJavaScript.ts +1 -1
  63. package/front_end/models/trace/insights/INPBreakdown.ts +1 -1
  64. package/front_end/models/trace/insights/ImageDelivery.ts +3 -3
  65. package/front_end/models/trace/insights/LCPDiscovery.ts +2 -2
  66. package/front_end/models/trace/insights/NetworkDependencyTree.ts +3 -3
  67. package/front_end/models/trace/insights/Viewport.ts +1 -1
  68. package/front_end/models/trace/types/Overlays.ts +2 -2
  69. package/front_end/panels/accessibility/AccessibilityNodeView.ts +2 -2
  70. package/front_end/panels/accessibility/AccessibilitySubPane.ts +0 -1
  71. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +74 -18
  72. package/front_end/panels/ai_assistance/components/AIv2MarkdownRenderer.ts +14 -3
  73. package/front_end/panels/ai_assistance/components/ChatInput.ts +1 -1
  74. package/front_end/panels/ai_assistance/components/ChatMessage.ts +1 -1
  75. package/front_end/panels/animation/AnimationUI.ts +1 -1
  76. package/front_end/panels/application/AppManifestView.ts +9 -11
  77. package/front_end/panels/application/ApplicationPanelSidebar.ts +9 -9
  78. package/front_end/panels/application/BackgroundServiceView.ts +1 -1
  79. package/front_end/panels/application/CookieItemsView.ts +1 -1
  80. package/front_end/panels/application/DOMStorageItemsView.ts +1 -1
  81. package/front_end/panels/application/DeviceBoundSessionsView.ts +2 -2
  82. package/front_end/panels/application/FrameDetailsView.ts +5 -5
  83. package/front_end/panels/application/IndexedDBViews.ts +2 -2
  84. package/front_end/panels/application/KeyValueStorageItemsView.ts +1 -2
  85. package/front_end/panels/application/ReportingApiView.ts +1 -1
  86. package/front_end/panels/application/ServiceWorkerCacheTreeElement.ts +1 -1
  87. package/front_end/panels/application/ServiceWorkersView.ts +1 -1
  88. package/front_end/panels/application/StorageBucketsTreeElement.ts +1 -1
  89. package/front_end/panels/application/WebMCPView.ts +1 -1
  90. package/front_end/panels/application/components/AdsView.ts +136 -7
  91. package/front_end/panels/application/components/BackForwardCacheStrings.ts +116 -117
  92. package/front_end/panels/application/components/BackForwardCacheView.ts +7 -7
  93. package/front_end/panels/application/components/BounceTrackingMitigationsView.ts +2 -2
  94. package/front_end/panels/application/components/PermissionsPolicySection.ts +2 -2
  95. package/front_end/panels/application/components/ProtocolHandlersView.ts +1 -1
  96. package/front_end/panels/application/components/ReportsGrid.ts +1 -1
  97. package/front_end/panels/application/components/StorageMetadataView.ts +1 -1
  98. package/front_end/panels/application/components/TrustTokensView.ts +3 -3
  99. package/front_end/panels/application/components/adScriptsTable.css +22 -0
  100. package/front_end/panels/application/preloading/PreloadingView.ts +20 -22
  101. package/front_end/panels/application/preloading/components/MismatchedPreloadingGrid.ts +9 -9
  102. package/front_end/panels/application/preloading/components/PreloadingDetailsReportView.ts +31 -33
  103. package/front_end/panels/application/preloading/components/PreloadingDisabledInfobar.ts +23 -23
  104. package/front_end/panels/application/preloading/components/PreloadingGrid.ts +4 -4
  105. package/front_end/panels/application/preloading/components/PreloadingString.ts +161 -161
  106. package/front_end/panels/application/preloading/components/RuleSetDetailsView.ts +2 -6
  107. package/front_end/panels/application/preloading/components/RuleSetGrid.ts +8 -8
  108. package/front_end/panels/application/preloading/components/UsedPreloadingView.ts +32 -32
  109. package/front_end/panels/application/resourcesPanel.css +17 -17
  110. package/front_end/panels/browser_debugger/DOMBreakpointsSidebarPane.ts +1 -1
  111. package/front_end/panels/changes/ChangesView.ts +1 -1
  112. package/front_end/panels/common/AiCodeCompletionSummaryToolbar.ts +1 -1
  113. package/front_end/panels/common/CPUThrottlingOption.ts +0 -1
  114. package/front_end/panels/common/GdpSignUpDialog.ts +3 -4
  115. package/front_end/panels/common/GeminiRebrandPromoDialog.ts +1 -1
  116. package/front_end/panels/console/ConsoleInsightTeaser.ts +1 -1
  117. package/front_end/panels/console/ConsolePinPane.ts +0 -1
  118. package/front_end/panels/console/ConsoleView.ts +2 -3
  119. package/front_end/panels/console/ConsoleViewMessage.ts +2 -3
  120. package/front_end/panels/coverage/CoverageListView.ts +6 -6
  121. package/front_end/panels/coverage/CoverageView.ts +7 -7
  122. package/front_end/panels/css_overview/CSSOverviewCompletedView.ts +1 -1
  123. package/front_end/panels/elements/CSSRuleValidator.ts +10 -10
  124. package/front_end/panels/elements/ElementsPanel.ts +3 -3
  125. package/front_end/panels/elements/ElementsTreeElement.ts +1 -1
  126. package/front_end/panels/elements/ElementsTreeOutline.ts +1 -1
  127. package/front_end/panels/elements/LayoutPane.ts +1 -1
  128. package/front_end/panels/elements/StylePropertyTreeElement.ts +85 -2
  129. package/front_end/panels/elements/StylesSidebarPane.ts +3 -3
  130. package/front_end/panels/elements/stylePropertiesTreeOutline.css +2 -1
  131. package/front_end/panels/emulation/DeviceModeView.ts +1 -1
  132. package/front_end/panels/event_listeners/EventListenersView.ts +57 -89
  133. package/front_end/panels/explain/components/ConsoleInsight.ts +4 -4
  134. package/front_end/panels/issues/AffectedPartitioningBlobURLView.ts +3 -3
  135. package/front_end/panels/issues/IssuesPane.ts +1 -1
  136. package/front_end/panels/layer_viewer/Layers3DView.ts +4 -4
  137. package/front_end/panels/layers/LayerTreeModel.ts +1 -1
  138. package/front_end/panels/lighthouse/LighthouseController.ts +4 -4
  139. package/front_end/panels/lighthouse/LighthouseStatusView.ts +10 -10
  140. package/front_end/panels/lighthouse/LighthouseTimespanView.ts +2 -2
  141. package/front_end/panels/linear_memory_inspector/LinearMemoryInspectorController.ts +1 -1
  142. package/front_end/panels/linear_memory_inspector/LinearMemoryInspectorPane.ts +1 -1
  143. package/front_end/panels/media/MainView.ts +2 -2
  144. package/front_end/panels/mobile_throttling/CalibrationController.ts +4 -1
  145. package/front_end/panels/mobile_throttling/NetworkPanelIndicator.ts +1 -1
  146. package/front_end/panels/mobile_throttling/ThrottlingManager.ts +10 -0
  147. package/front_end/panels/mobile_throttling/ThrottlingSettingsTab.ts +3 -3
  148. package/front_end/panels/network/NetworkConfigView.ts +26 -16
  149. package/front_end/panels/network/NetworkItemView.ts +1 -1
  150. package/front_end/panels/network/NetworkLogView.ts +4 -5
  151. package/front_end/panels/network/NetworkPanel.ts +1 -1
  152. package/front_end/panels/network/RequestConditionsDrawer.ts +3 -3
  153. package/front_end/panels/network/RequestCookiesView.ts +2 -2
  154. package/front_end/panels/network/RequestPayloadView.ts +0 -2
  155. package/front_end/panels/network/RequestTimingView.ts +1 -1
  156. package/front_end/panels/network/ResourceChunkView.ts +1 -1
  157. package/front_end/panels/network/components/HeaderSectionRow.ts +2 -2
  158. package/front_end/panels/network/components/RequestHeaderSection.ts +1 -1
  159. package/front_end/panels/network/components/RequestTrustTokensView.ts +7 -7
  160. package/front_end/panels/network/components/ResponseHeaderSection.ts +3 -4
  161. package/front_end/panels/network/network-meta.ts +1 -1
  162. package/front_end/panels/profiler/HeapDetachedElementsView.ts +1 -1
  163. package/front_end/panels/profiler/HeapSnapshotGridNodes.ts +10 -10
  164. package/front_end/panels/profiler/HeapSnapshotView.ts +1 -1
  165. package/front_end/panels/profiler/IsolateSelector.ts +4 -4
  166. package/front_end/panels/profiler/ModuleUIStrings.ts +1 -1
  167. package/front_end/panels/profiler/ProfilesPanel.ts +2 -3
  168. package/front_end/panels/recorder/RecorderPanel.ts +1 -1
  169. package/front_end/panels/search/SearchView.ts +6 -6
  170. package/front_end/panels/security/SecurityModel.ts +4 -4
  171. package/front_end/panels/security/SecurityPanel.ts +83 -54
  172. package/front_end/panels/security/SecurityPanelSidebar.ts +41 -5
  173. package/front_end/panels/security/sidebar.css +9 -10
  174. package/front_end/panels/sensors/SensorsView.ts +1 -1
  175. package/front_end/panels/settings/AISettingsTab.ts +9 -9
  176. package/front_end/panels/settings/FrameworkIgnoreListSettingsTab.ts +1 -1
  177. package/front_end/panels/settings/KeybindsSettingsTab.ts +2 -2
  178. package/front_end/panels/settings/SettingsScreen.ts +156 -109
  179. package/front_end/panels/settings/WorkspaceSettingsTab.ts +1 -1
  180. package/front_end/panels/settings/components/SyncSection.ts +3 -3
  181. package/front_end/panels/settings/emulation/DevicesSettingsTab.ts +13 -13
  182. package/front_end/panels/settings/emulation/components/UserAgentClientHintsForm.ts +2 -2
  183. package/front_end/panels/snippets/SnippetsQuickOpen.ts +1 -1
  184. package/front_end/panels/sources/CSSPlugin.ts +3 -3
  185. package/front_end/panels/sources/DebuggerPlugin.ts +4 -4
  186. package/front_end/panels/sources/NavigatorView.ts +3 -3
  187. package/front_end/panels/sources/OutlineQuickOpen.ts +1 -1
  188. package/front_end/panels/sources/PersistenceActions.ts +2 -2
  189. package/front_end/panels/sources/SourcesNavigator.ts +5 -5
  190. package/front_end/panels/sources/TabbedEditorContainer.ts +1 -1
  191. package/front_end/panels/sources/WatchExpressionsSidebarPane.ts +0 -1
  192. package/front_end/panels/sources/components/HeadersView.ts +1 -1
  193. package/front_end/panels/timeline/LayoutShiftsTrackAppender.ts +1 -1
  194. package/front_end/panels/timeline/TimelinePaintProfilerView.ts +1 -1
  195. package/front_end/panels/timeline/TimelineUIUtils.ts +0 -1
  196. package/front_end/panels/timeline/components/ExportTraceOptions.ts +2 -2
  197. package/front_end/panels/timeline/components/FieldSettingsDialog.ts +4 -4
  198. package/front_end/panels/timeline/components/IgnoreListSetting.ts +1 -1
  199. package/front_end/panels/timeline/components/LiveMetricsView.ts +7 -7
  200. package/front_end/panels/timeline/components/MetricCard.ts +3 -3
  201. package/front_end/panels/timeline/components/MetricCompareStrings.ts +12 -14
  202. package/front_end/panels/timeline/components/OriginMap.ts +3 -3
  203. package/front_end/panels/timeline/components/SidebarAnnotationsTab.ts +4 -4
  204. package/front_end/panels/timeline/overlays/OverlaysImpl.ts +6 -0
  205. package/front_end/panels/timeline/overlays/components/EntryLabelOverlay.ts +1 -1
  206. package/front_end/panels/timeline/overlays/components/TimespanBreakdownOverlay.ts +3 -1
  207. package/front_end/panels/timeline/timelineMiniMap.css +9 -9
  208. package/front_end/panels/timeline/timelineStatusDialog.css +9 -9
  209. package/front_end/panels/web_audio/WebAudioView.ts +1 -1
  210. package/front_end/panels/webauthn/WebauthnPane.ts +1 -1
  211. package/front_end/third_party/chromium/README.chromium +1 -1
  212. package/front_end/ui/comments/CommentAnchorResolver.ts +114 -3
  213. package/front_end/ui/comments/CommentOverlayManager.ts +95 -16
  214. package/front_end/ui/components/dialogs/TypeToAllowDialog.ts +1 -1
  215. package/front_end/ui/components/markdown_view/codeBlock.css +3 -8
  216. package/front_end/ui/components/markdown_view/markdownView.css +2 -7
  217. package/front_end/ui/components/panel_feedback/PanelFeedback.ts +2 -2
  218. package/front_end/ui/components/panel_feedback/PreviewToggle.ts +1 -1
  219. package/front_end/ui/components/request_link_icon/RequestLinkIcon.ts +1 -1
  220. package/front_end/ui/components/text_editor/AiCodeCompletionTeaser.ts +4 -4
  221. package/front_end/ui/components/text_editor/AiCodeGenerationTeaser.ts +3 -3
  222. package/front_end/{models → ui}/geometry/GeometryImpl.ts +4 -40
  223. package/front_end/ui/legacy/GlassPane.ts +1 -1
  224. package/front_end/ui/legacy/RemoteDebuggingTerminatedScreen.ts +2 -2
  225. package/front_end/ui/legacy/SoftDropDown.ts +1 -1
  226. package/front_end/ui/legacy/SplitWidget.ts +1 -1
  227. package/front_end/ui/legacy/SuggestBox.ts +1 -1
  228. package/front_end/ui/legacy/TabbedPane.ts +1 -1
  229. package/front_end/ui/legacy/TargetCrashedScreen.ts +2 -2
  230. package/front_end/ui/legacy/Treeoutline.ts +5 -1
  231. package/front_end/ui/legacy/UIUtils.ts +1 -1
  232. package/front_end/ui/legacy/Widget.ts +1 -1
  233. package/front_end/ui/legacy/components/data_grid/DataGrid.ts +1 -1
  234. package/front_end/ui/legacy/components/inline_editor/AnimationTimingModel.ts +1 -1
  235. package/front_end/ui/legacy/components/inline_editor/AnimationTimingUI.ts +1 -1
  236. package/front_end/ui/legacy/components/inline_editor/BezierUI.ts +1 -1
  237. package/front_end/ui/legacy/components/inline_editor/CSSAngleUtils.ts +1 -1
  238. package/front_end/ui/legacy/components/inline_editor/CSSShadowEditor.ts +1 -1
  239. package/front_end/ui/legacy/components/inline_editor/PositionAreaEditor.ts +204 -17
  240. package/front_end/ui/legacy/components/inline_editor/positionAreaEditor.css +6 -0
  241. package/front_end/ui/legacy/components/object_ui/ObjectPopoverHelper.ts +1 -1
  242. package/front_end/ui/legacy/components/perf_ui/FilmStripView.ts +1 -1
  243. package/front_end/ui/legacy/components/perf_ui/FlameChart.ts +7 -9
  244. package/front_end/ui/legacy/components/quick_open/FilteredListWidget.ts +1 -1
  245. package/front_end/ui/visual_logging/KnownContextValues.ts +12 -0
  246. package/package.json +7 -4
  247. /package/front_end/{models → ui}/geometry/geometry.ts +0 -0
@@ -11,6 +11,7 @@ import type {CallFrame, ScopeChainEntry} from './DebuggerModel.js';
11
11
  import {scopeTreeForScript} from './ScopeTreeCache.js';
12
12
  import type {Script} from './Script.js';
13
13
  import {buildOriginalScopes, decodePastaRanges, type NamedFunctionRange} from './SourceMapFunctionRanges.js';
14
+ import {decodeRangeMappings} from './SourceMapRangeMappings.js';
14
15
  import {SourceMapScopesInfo, type TranslatedFrame} from './SourceMapScopesInfo.js';
15
16
 
16
17
  /**
@@ -33,6 +34,7 @@ export interface SourceMapV3Object {
33
34
  names?: string[];
34
35
  ignoreList?: number[];
35
36
  scopes?: string;
37
+ rangeMappings?: string;
36
38
  debugId?: string;
37
39
  x_google_linecount?: number;
38
40
  x_google_ignoreList?: number[];
@@ -92,10 +94,17 @@ export class SourceMapEntry {
92
94
  readonly sourceLineNumber: number;
93
95
  readonly sourceColumnNumber: number;
94
96
  readonly name?: string;
97
+ /**
98
+ * Whether this entry covers everything up to the following entry, mapping the generated
99
+ * code character by character (including newlines) onto the original code.
100
+ *
101
+ * @see https://github.com/tc39/source-map/blob/main/proposals/range-mappings.md
102
+ */
103
+ readonly isRangeMapping: boolean;
95
104
 
96
- constructor(
97
- lineNumber: number, columnNumber: number, sourceIndex?: number, sourceURL?: Platform.DevToolsPath.UrlString,
98
- sourceLineNumber?: number, sourceColumnNumber?: number, name?: string) {
105
+ constructor(lineNumber: number, columnNumber: number, sourceIndex?: number,
106
+ sourceURL?: Platform.DevToolsPath.UrlString, sourceLineNumber?: number, sourceColumnNumber?: number,
107
+ name?: string, isRangeMapping = false) {
99
108
  this.lineNumber = lineNumber;
100
109
  this.columnNumber = columnNumber;
101
110
  this.sourceIndex = sourceIndex;
@@ -103,6 +112,7 @@ export class SourceMapEntry {
103
112
  this.sourceLineNumber = (sourceLineNumber as number);
104
113
  this.sourceColumnNumber = (sourceColumnNumber as number);
105
114
  this.name = name;
115
+ this.isRangeMapping = isRangeMapping;
106
116
  }
107
117
 
108
118
  static compare(entry1: SourceMapEntry, entry2: SourceMapEntry): number {
@@ -526,6 +536,22 @@ export class SourceMap {
526
536
  const tokenIter = new TokenIterator(map.mappings);
527
537
  let sourceURL: Platform.DevToolsPath.UrlString|undefined = this.#sourceInfos[sourceIndex]?.sourceURL;
528
538
 
539
+ // For every line of this section, the index of its first entry in `mappings` and the
540
+ // number of entries on that line. The `rangeMappings` field addresses entries by their
541
+ // index within a line, so this is what resolves those indices below.
542
+ const lineStarts: number[] = [];
543
+ const lineCounts: number[] = [];
544
+ const mappings = this.mappings();
545
+ const pushEntry = (entry: SourceMapEntry): void => {
546
+ const line = entry.lineNumber - baseLineNumber;
547
+ if (lineCounts[line] === undefined) {
548
+ lineStarts[line] = mappings.length;
549
+ lineCounts[line] = 0;
550
+ }
551
+ lineCounts[line]++;
552
+ mappings.push(entry);
553
+ };
554
+
529
555
  while (true) {
530
556
  if (tokenIter.peek() === ',') {
531
557
  tokenIter.next();
@@ -542,7 +568,7 @@ export class SourceMap {
542
568
 
543
569
  columnNumber += tokenIter.nextVLQ();
544
570
  if (!tokenIter.hasNext() || this.isSeparator(tokenIter.peek())) {
545
- this.mappings().push(new SourceMapEntry(lineNumber, columnNumber));
571
+ pushEntry(new SourceMapEntry(lineNumber, columnNumber));
546
572
  continue;
547
573
  }
548
574
 
@@ -555,16 +581,18 @@ export class SourceMap {
555
581
  sourceColumnNumber += tokenIter.nextVLQ();
556
582
 
557
583
  if (!tokenIter.hasNext() || this.isSeparator(tokenIter.peek())) {
558
- this.mappings().push(
584
+ pushEntry(
559
585
  new SourceMapEntry(lineNumber, columnNumber, sourceIndex, sourceURL, sourceLineNumber, sourceColumnNumber));
560
586
  continue;
561
587
  }
562
588
 
563
589
  nameIndex += tokenIter.nextVLQ();
564
- this.mappings().push(new SourceMapEntry(
565
- lineNumber, columnNumber, sourceIndex, sourceURL, sourceLineNumber, sourceColumnNumber, names[nameIndex]));
590
+ pushEntry(new SourceMapEntry(lineNumber, columnNumber, sourceIndex, sourceURL, sourceLineNumber,
591
+ sourceColumnNumber, names[nameIndex]));
566
592
  }
567
593
 
594
+ this.#markRangeMappings(map, lineStarts, lineCounts);
595
+
568
596
  if (!this.#scopesInfo) {
569
597
  this.#scopesInfo = new SourceMapScopesInfo(this, {scopes: [], ranges: []});
570
598
  }
@@ -583,6 +611,44 @@ export class SourceMap {
583
611
  }
584
612
  }
585
613
 
614
+ /**
615
+ * Marks the entries of the section that was just parsed which the `rangeMappings` field of
616
+ * that section points at.
617
+ *
618
+ * A malformed field never invalidates the SourceMap: a field that can't be decoded is
619
+ * ignored altogether, and indices that don't point at a mapping with an original position
620
+ * are skipped.
621
+ *
622
+ * @param lineStarts index in `mappings` of the first entry of each line of the section.
623
+ * @param lineCounts number of entries on each line of the section.
624
+ */
625
+ #markRangeMappings(map: SourceMapV3Object, lineStarts: number[], lineCounts: number[]): void {
626
+ if (typeof map.rangeMappings !== 'string') {
627
+ return;
628
+ }
629
+ let rangeMappings: number[][];
630
+ try {
631
+ rangeMappings = decodeRangeMappings(map.rangeMappings);
632
+ } catch {
633
+ return;
634
+ }
635
+
636
+ const mappings = this.mappings();
637
+ for (let line = 0; line < rangeMappings.length; ++line) {
638
+ for (const index of rangeMappings[line]) {
639
+ if (index >= (lineCounts[line] ?? 0)) {
640
+ // The indices are sorted, so the rest of the line is out of bounds as well.
641
+ break;
642
+ }
643
+ const mappingIndex = lineStarts[line] + index;
644
+ if (mappings[mappingIndex].sourceURL === undefined) {
645
+ continue;
646
+ }
647
+ mappings[mappingIndex] = asRangeMapping(mappings[mappingIndex]);
648
+ }
649
+ }
650
+ }
651
+
586
652
  private parseBloombergScopes(map: SourceMapV3Object): Array<ScopesCodec.OriginalScope|null> {
587
653
  const scopeList = map.x_com_bloomberg_sourcesFunctionMappings;
588
654
  if (!scopeList) {
@@ -791,9 +857,17 @@ export class SourceMap {
791
857
  }
792
858
  }
793
859
 
860
+ /** @returns a copy of the {@link entry} that is marked as a range mapping. */
861
+ function asRangeMapping(entry: SourceMapEntry): SourceMapEntry {
862
+ return new SourceMapEntry(entry.lineNumber, entry.columnNumber, entry.sourceIndex, entry.sourceURL,
863
+ entry.sourceLineNumber, entry.sourceColumnNumber, entry.name, true);
864
+ }
865
+
794
866
  const VLQ_BASE_SHIFT = 5;
795
867
  const VLQ_BASE_MASK = (1 << 5) - 1;
796
868
  const VLQ_CONTINUATION_MASK = 1 << 5;
869
+ /** The largest shift an unsigned VLQ digit may contribute at while still fitting into 32 bits. */
870
+ const VLQ_UNSIGNED_MAX_SHIFT = 30;
797
871
 
798
872
  export class TokenIterator {
799
873
  readonly #string: string;
@@ -823,26 +897,50 @@ export class TokenIterator {
823
897
 
824
898
  nextVLQ(): number {
825
899
  // Read unsigned value.
900
+ let result = this.#decodeVLQ(false);
901
+
902
+ // Fix the sign.
903
+ const negative = result & 1;
904
+ result >>= 1;
905
+ return negative ? -result : result;
906
+ }
907
+
908
+ /**
909
+ * Decodes an unsigned Base64 VLQ number, as used by the `rangeMappings` field of the
910
+ * "range mappings" proposal. In contrast to {@link nextVLQ} the least significant bit
911
+ * carries a value rather than a sign, so the full 32 bit range is available. Numbers
912
+ * that don't fit into 32 bits are rejected.
913
+ *
914
+ * @see https://github.com/tc39/source-map/blob/main/proposals/range-mappings.md
915
+ */
916
+ nextUnsignedVLQ(): number {
917
+ return this.#decodeVLQ(true);
918
+ }
919
+
920
+ #decodeVLQ(unsigned: boolean): number {
826
921
  let result = 0;
827
922
  let shift = 0;
828
923
  let digit: number = VLQ_CONTINUATION_MASK;
829
924
  while (digit & VLQ_CONTINUATION_MASK) {
830
925
  if (!this.hasNext()) {
831
- throw new Error('Unexpected end of input while decodling VLQ number!');
926
+ throw new Error('Unexpected end of input while decoding VLQ number!');
927
+ }
928
+ if (unsigned && shift > VLQ_UNSIGNED_MAX_SHIFT) {
929
+ throw new Error('Unsigned VLQ number does not fit into 32 bits!');
832
930
  }
833
931
  const charCode = this.nextCharCode();
834
932
  digit = Common.Base64.BASE64_CODES[charCode];
835
933
  if (charCode !== 65 /* 'A' */ && digit === 0) {
836
934
  throw new Error(`Unexpected char '${String.fromCharCode(charCode)}' encountered while decoding`);
837
935
  }
838
- result += (digit & VLQ_BASE_MASK) << shift;
936
+ // Unsigned numbers may use the full 32 bits, where `<<` would sign extend.
937
+ result += unsigned ? (digit & VLQ_BASE_MASK) * 2 ** shift : (digit & VLQ_BASE_MASK) << shift;
839
938
  shift += VLQ_BASE_SHIFT;
840
939
  }
841
-
842
- // Fix the sign.
843
- const negative = result & 1;
844
- result >>= 1;
845
- return negative ? -result : result;
940
+ if (unsigned && result > 0xFFFFFFFF) {
941
+ throw new Error('Unsigned VLQ number does not fit into 32 bits!');
942
+ }
943
+ return result;
846
944
  }
847
945
 
848
946
  /**
@@ -0,0 +1,43 @@
1
+ // Copyright 2026 The Chromium Authors
2
+ // Use of this source code is governed by a BSD-style license that can be
3
+ // found in the LICENSE file.
4
+
5
+ import {TokenIterator} from './SourceMap.js';
6
+
7
+ /**
8
+ * Implements decoding of the `rangeMappings` field of the "range mappings" proposal.
9
+ *
10
+ * The field holds one entry per line of the generated code, separated by `;`. Each line is
11
+ * a bare sequence of unsigned Base64 VLQs (no separators in between) denoting which of the
12
+ * mappings on that line are range mappings: the first VLQ is an absolute index into the
13
+ * line's mappings, every subsequent one an offset from the previous index. An offset of zero
14
+ * repeats the previous index, which is tolerated rather than treated as an error.
15
+ *
16
+ * @returns for every line of the generated code, the sorted indices of the mappings on
17
+ * that line which are range mappings.
18
+ * @throws if the field is not a well-formed sequence of unsigned VLQs and `;` separators.
19
+ * @see https://github.com/tc39/source-map/blob/main/proposals/range-mappings.md
20
+ */
21
+ export function decodeRangeMappings(encodedRangeMappings: string): number[][] {
22
+ const rangeMappings: number[][] = [];
23
+ const tokenIter = new TokenIterator(encodedRangeMappings);
24
+
25
+ let indices: number[] = [];
26
+ let currentIndex = 0;
27
+ while (tokenIter.hasNext()) {
28
+ if (tokenIter.peek() === ';') {
29
+ tokenIter.next();
30
+ rangeMappings.push(indices);
31
+ indices = [];
32
+ currentIndex = 0;
33
+ continue;
34
+ }
35
+
36
+ // The first index of a line is absolute, i.e. relative to 0.
37
+ currentIndex += tokenIter.nextUnsignedVLQ();
38
+ indices.push(currentIndex);
39
+ }
40
+ rangeMappings.push(indices);
41
+
42
+ return rangeMappings;
43
+ }
@@ -41,19 +41,22 @@ export class SourceMapScopeChainEntry implements ScopeChainEntry {
41
41
  readonly #range?: ScopesCodec.GeneratedRange;
42
42
  readonly #isInnerMostFunction: boolean;
43
43
  readonly #returnValue?: RemoteObject;
44
+ readonly #scopeNumber?: number;
44
45
 
45
46
  /**
46
47
  * @param isInnerMostFunction If `scope` is the innermost 'function' scope. Only used for labeling as we name the
47
48
  * scope of the paused function 'Local', while other outer 'function' scopes are named 'Closure'.
49
+ * @param scopeNumber The V8 scope in which `scope`s binding expressions must be evaluated. Defaults to the
50
+ * inner-most scope.
48
51
  */
49
- constructor(
50
- callFrame: CallFrame, scope: ScopesCodec.OriginalScope, range: ScopesCodec.GeneratedRange|undefined,
51
- isInnerMostFunction: boolean, returnValue: RemoteObject|undefined) {
52
+ constructor(callFrame: CallFrame, scope: ScopesCodec.OriginalScope, range: ScopesCodec.GeneratedRange|undefined,
53
+ isInnerMostFunction: boolean, returnValue: RemoteObject|undefined, scopeNumber?: number) {
52
54
  this.#callFrame = callFrame;
53
55
  this.#scope = scope;
54
56
  this.#range = range;
55
57
  this.#isInnerMostFunction = isInnerMostFunction;
56
58
  this.#returnValue = returnValue;
59
+ this.#scopeNumber = scopeNumber;
57
60
  }
58
61
 
59
62
  extraProperties(): RemoteObjectProperty[] {
@@ -102,7 +105,7 @@ export class SourceMapScopeChainEntry implements ScopeChainEntry {
102
105
  }
103
106
 
104
107
  object(): RemoteObject {
105
- return new SourceMapScopeRemoteObject(this.#callFrame, this.#scope, this.#range);
108
+ return new SourceMapScopeRemoteObject(this.#callFrame, this.#scope, this.#range, this.#scopeNumber);
106
109
  }
107
110
 
108
111
  description(): string {
@@ -118,18 +121,22 @@ class SourceMapScopeRemoteObject extends RemoteObjectImpl {
118
121
  readonly #callFrame: CallFrame;
119
122
  readonly #scope: ScopesCodec.OriginalScope;
120
123
  readonly #range?: ScopesCodec.GeneratedRange;
124
+ readonly #scopeNumber?: number;
121
125
 
122
- constructor(callFrame: CallFrame, scope: ScopesCodec.OriginalScope, range: ScopesCodec.GeneratedRange|undefined) {
126
+ constructor(callFrame: CallFrame, scope: ScopesCodec.OriginalScope, range: ScopesCodec.GeneratedRange|undefined,
127
+ scopeNumber: number|undefined) {
123
128
  super(
124
129
  callFrame.debuggerModel.runtimeModel(), /* objectId */ undefined, 'object', /* sub type */ undefined,
125
130
  /* value */ null);
126
131
  this.#callFrame = callFrame;
127
132
  this.#scope = scope;
128
133
  this.#range = range;
134
+ this.#scopeNumber = scopeNumber;
129
135
  }
130
136
 
131
- override async doGetProperties(_ownProperties: boolean, accessorPropertiesOnly: boolean, generatePreview: boolean):
132
- Promise<GetPropertiesResult> {
137
+ override async doGetProperties(_ownProperties: boolean, accessorPropertiesOnly: boolean,
138
+ _nonIndexedPropertiesOnly: boolean,
139
+ generatePreview: boolean): Promise<GetPropertiesResult> {
133
140
  if (accessorPropertiesOnly) {
134
141
  return {properties: [], internalProperties: []};
135
142
  }
@@ -142,9 +149,7 @@ class SourceMapScopeRemoteObject extends RemoteObjectImpl {
142
149
  continue;
143
150
  }
144
151
 
145
- // TODO(crbug.com/40277685): Once we can evaluate expressions in scopes other than the innermost one,
146
- // we need to find the find the CDP scope that matches `this.#range` and evaluate in that.
147
- const result = await this.#callFrame.evaluate({expression, generatePreview});
152
+ const result = await this.#callFrame.evaluate({expression, generatePreview, scopeNumber: this.#scopeNumber});
148
153
  if ('error' in result || result.exceptionDetails) {
149
154
  // TODO(crbug.com/40277685): Make these errors user-visible to aid tooling developers.
150
155
  // E.g. show the error on hover or expose it in the developer resources panel.
@@ -8,7 +8,7 @@ import type * as ScopesCodec from '../../third_party/source-map-scopes-codec/sou
8
8
  import type * as Platform from '../platform/platform.js';
9
9
  import type * as TextUtils from '../text_utils/text_utils.js';
10
10
 
11
- import type {CallFrame, ScopeChainEntry} from './DebuggerModel.js';
11
+ import type {CallFrame, Scope, ScopeChainEntry} from './DebuggerModel.js';
12
12
  import type {SourceMap} from './SourceMap.js';
13
13
  import {SourceMapScopeChainEntry} from './SourceMapScopeChainEntry.js';
14
14
 
@@ -190,13 +190,6 @@ export class SourceMapScopesInfo {
190
190
  return comparePositions(a.start, b.start);
191
191
  }
192
192
 
193
- function comparePositions(a: ScopesCodec.Position, b: ScopesCodec.Position): number {
194
- if (a.line !== b.line) {
195
- return a.line - b.line;
196
- }
197
- return a.column - b.column;
198
- }
199
-
200
193
  function positionFromOffset(offset: number): ScopesCodec.Position {
201
194
  const location = text.positionFromOffset(offset);
202
195
  return {line: location.lineNumber, column: location.columnNumber};
@@ -369,8 +362,9 @@ export class SourceMapScopesInfo {
369
362
  const isFunctionScope = originalScope.kind === 'function';
370
363
  const isInnerMostFunction = isFunctionScope && !seenFunctionScope;
371
364
  const returnValue = isInnerMostFunction ? callFrame.returnValue() : null;
372
- result.push(
373
- new SourceMapScopeChainEntry(callFrame, originalScope, range, isInnerMostFunction, returnValue ?? undefined));
365
+ const scopeNumber = range ? findMatchingScopeNumber(callFrame, range) : undefined;
366
+ result.push(new SourceMapScopeChainEntry(callFrame, originalScope, range, isInnerMostFunction,
367
+ returnValue ?? undefined, scopeNumber));
374
368
  seenFunctionScope ||= isFunctionScope;
375
369
  }
376
370
 
@@ -580,3 +574,91 @@ export function contains(
580
574
 
581
575
  return true;
582
576
  }
577
+
578
+ export function comparePositions(a: ScopesCodec.Position, b: ScopesCodec.Position): number {
579
+ if (a.line !== b.line) {
580
+ return a.line - b.line;
581
+ }
582
+ return a.column - b.column;
583
+ }
584
+
585
+ function positionRange(callFrame: CallFrame, scope: Scope): {start: ScopesCodec.Position, end: ScopesCodec.Position}|
586
+ null {
587
+ const range = scope.range();
588
+ if (range === null || range.start.scriptId !== callFrame.location().scriptId ||
589
+ range.end.scriptId !== callFrame.location().scriptId) {
590
+ return null;
591
+ }
592
+ return {
593
+ start: {line: range.start.lineNumber, column: range.start.columnNumber},
594
+ end: {line: range.end.lineNumber, column: range.end.columnNumber},
595
+ };
596
+ }
597
+
598
+ /**
599
+ * Finds the V8 scope that corresponds to the source map's generated `range`.
600
+ *
601
+ * We need this to evaluate a scope's binding expressions in the right V8 scope. `evaluateOnCallFrame`
602
+ * defaults to the inner-most scope, where declarations shadow the outer variables we actually want to read.
603
+ *
604
+ * V8's scope ranges and the source map's generated ranges don't have to agree (in particular with
605
+ * inlining), so besides an exact match we accept the outer-most V8 scope contained in `range` (a generated
606
+ * range for a function spans the whole function text, while V8's scope only covers params + body), or
607
+ * failing that the inner-most V8 scope containing `range`.
608
+ *
609
+ * @returns The scope number, or `undefined` if nothing matched. Callers should then omit `scopeNumber`
610
+ * and let CDP default to the inner-most scope.
611
+ */
612
+ export function findMatchingScopeNumber(callFrame: CallFrame, range: ScopesCodec.GeneratedRange): number|undefined {
613
+ const scopeChain = callFrame.scopeChain();
614
+
615
+ const exactMatch = scopeChain.find(scope => {
616
+ const scopeRange = positionRange(callFrame, scope);
617
+ return scopeRange !== null && comparePositions(scopeRange.start, range.start) === 0 &&
618
+ comparePositions(scopeRange.end, range.end) === 0;
619
+ });
620
+ if (exactMatch !== undefined) {
621
+ return exactMatch.ordinal();
622
+ }
623
+
624
+ // A stack frame must correspond to a V8 function scope. Inlined ranges make it easy to accidentally
625
+ // land on an unrelated block/catch/with scope, so search the function scopes first.
626
+ if (range.isStackFrame) {
627
+ const functionScopes = scopeChain.filter(scope => scope.type() === Protocol.Debugger.ScopeType.Local ||
628
+ scope.type() === Protocol.Debugger.ScopeType.Closure);
629
+ const functionScope = findBestScope(callFrame, functionScopes, range);
630
+ if (functionScope !== undefined) {
631
+ return functionScope.ordinal();
632
+ }
633
+ }
634
+
635
+ return findBestScope(callFrame, scopeChain, range)?.ordinal();
636
+ }
637
+
638
+ /**
639
+ * @param scopes Ordered inner-most to outer-most.
640
+ * @returns The outer-most scope contained in `range`, or if there is none, the inner-most scope
641
+ * containing `range`.
642
+ */
643
+ function findBestScope(callFrame: CallFrame, scopes: Scope[], range: ScopesCodec.GeneratedRange): Scope|undefined {
644
+ let outerMostContainedScope: Scope|undefined;
645
+ let innerMostContainingScope: Scope|undefined;
646
+
647
+ for (const scope of scopes) {
648
+ const scopeRange = positionRange(callFrame, scope);
649
+ if (scopeRange === null) {
650
+ continue;
651
+ }
652
+ const rangeContainsScope =
653
+ comparePositions(range.start, scopeRange.start) <= 0 && comparePositions(scopeRange.end, range.end) <= 0;
654
+ const scopeContainsRange =
655
+ comparePositions(scopeRange.start, range.start) <= 0 && comparePositions(range.end, scopeRange.end) <= 0;
656
+ if (rangeContainsScope) {
657
+ outerMostContainedScope = scope;
658
+ } else if (scopeContainsRange) {
659
+ innerMostContainingScope ??= scope;
660
+ }
661
+ }
662
+
663
+ return outerMostContainedScope ?? innerMostContainingScope;
664
+ }
@@ -89,6 +89,7 @@ import * as SourceMap from './SourceMap.js';
89
89
  import * as SourceMapCache from './SourceMapCache.js';
90
90
  import * as SourceMapFunctionRanges from './SourceMapFunctionRanges.js';
91
91
  import * as SourceMapManager from './SourceMapManager.js';
92
+ import * as SourceMapRangeMappings from './SourceMapRangeMappings.js';
92
93
  import * as SourceMapScopeChainEntry from './SourceMapScopeChainEntry.js';
93
94
  import * as SourceMapScopesInfo from './SourceMapScopesInfo.js';
94
95
  import * as StorageBucketsModel from './StorageBucketsModel.js';
@@ -179,6 +180,7 @@ export {
179
180
  SourceMapCache,
180
181
  SourceMapFunctionRanges,
181
182
  SourceMapManager,
183
+ SourceMapRangeMappings,
182
184
  SourceMapScopeChainEntry,
183
185
  SourceMapScopesInfo,
184
186
  StorageBucketsModel,
@@ -1135,7 +1135,7 @@ export abstract class HeapSnapshot {
1135
1135
  this.#allocationProfile = new AllocationProfile(this.profile, liveObjects);
1136
1136
  }
1137
1137
 
1138
- this.#progress.updateStatus('Finished processing.');
1138
+ this.#progress.updateStatus('Finished processing');
1139
1139
  }
1140
1140
 
1141
1141
  nodeIndexForId(nodeId: number): number|undefined {
@@ -45,7 +45,7 @@ const UIStrings = {
45
45
  /**
46
46
  * @description Explanation text for the 'Layer borders' setting in the Rendering panel.
47
47
  */
48
- showsLayerBordersOrangeoliveAnd: 'Shows layer borders (orange/olive) and tiles (cyan).',
48
+ showsLayerBordersOrangeoliveAnd: 'Shows layer borders (orange/olive) and tiles (cyan)',
49
49
  /**
50
50
  * @description The name of a checkbox setting in the Rendering panel. This setting shows the
51
51
  * rendering statistics for frames e.g. frames per second. Frame is a noun.
@@ -55,7 +55,7 @@ const UIStrings = {
55
55
  * @description Explanation text for the 'Frame rendering stats' setting in the Rendering panel.
56
56
  * Plots is a verb. GPU = Graphics Processing Unit.
57
57
  */
58
- plotsFrameThroughputDropped: 'Plots frame throughput, dropped frames distribution, and GPU memory.',
58
+ plotsFrameThroughputDropped: 'Plots frame throughput, dropped frames distribution, and GPU memory',
59
59
  /**
60
60
  * @description The name of a checkbox setting in the Rendering panel. This setting highlights
61
61
  * elements that can slow down scrolling on the page.
@@ -65,7 +65,7 @@ const UIStrings = {
65
65
  * @description Explanation text for the 'Scrolling performance issues' setting in the Rendering panel.
66
66
  */
67
67
  highlightsElementsTealThatCan:
68
- 'Highlights elements (teal) that can slow down scrolling, including touch & wheel event handlers and other main-thread scrolling situations.',
68
+ 'Highlights elements (teal) that can slow down scrolling, including touch & wheel event handlers and other main-thread scrolling situations',
69
69
  /**
70
70
  * @description The name of a checkbox setting in the Rendering panel. This setting highlights the
71
71
  * rendering elements for ads that are found on the page.
@@ -74,7 +74,7 @@ const UIStrings = {
74
74
  /**
75
75
  * @description Explanation text for the 'Highlight ads' setting in the Rendering panel.
76
76
  */
77
- highlightsElementsRedDetectedToBe: 'Highlights elements (red) detected to be ads.',
77
+ highlightsElementsRedDetectedToBe: 'Highlights elements (red) detected to be ads',
78
78
  /**
79
79
  * @description The name of a checkbox setting in the Rendering panel. This setting prevents the
80
80
  * webpage from loading 'local' fonts. Local fonts are fonts that are installed on the user's
@@ -102,7 +102,7 @@ const UIStrings = {
102
102
  /**
103
103
  * @description Explanation text for the 'Emulate automatic dark mode' setting in the Rendering panel.
104
104
  */
105
- emulatesAutoDarkMode: 'Enables automatic dark mode and sets `prefers-color-scheme` to `dark`.',
105
+ emulatesAutoDarkMode: 'Enables automatic dark mode and sets `prefers-color-scheme` to `dark`',
106
106
  /**
107
107
  * @description Explanation text for the 'Emulate CSS media type' setting in the Rendering panel.
108
108
  * This setting overrides the CSS media type on the page (https://developer.mozilla.org/en-US/docs/Web/CSS/@media#media_types).
@@ -148,7 +148,7 @@ const UIStrings = {
148
148
  /**
149
149
  * @description Explanation text for the image format disabling settings in the Rendering panel.
150
150
  */
151
- requiresAPageReloadToApplyAnd: 'Requires a page reload to apply and disables caching for image requests.',
151
+ requiresAPageReloadToApplyAnd: 'Requires a page reload to apply and disables caching for image requests',
152
152
  /**
153
153
  * @description The name of a checkbox setting in the Rendering panel. This setting disables the
154
154
  * page from loading images with the JPEG XL format.
@@ -74,7 +74,7 @@ const UIStrings = {
74
74
  * @description Tooltip for the dock side menu options explaining how to restore the last dock position.
75
75
  * @example {Ctrl+Shift+D} PH1
76
76
  */
77
- placementOfDevtoolsRelativeToThe: 'Placement of DevTools relative to the page. ({PH1} to restore last position)',
77
+ placementOfDevtoolsRelativeToThe: 'Placement of DevTools relative to the page ({PH1} to restore last position)',
78
78
  /**
79
79
  * @description Tooltip and label for the button to undock DevTools into a separate window.
80
80
  */