chrome-devtools-frontend 1.0.1700561 → 1.0.1702515

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 (119) hide show
  1. package/eslint.config.mjs +3 -1
  2. package/front_end/core/host/UserMetricsEnums.ts +2 -1
  3. package/front_end/core/root/ExperimentNames.ts +1 -0
  4. package/front_end/core/root/Runtime.ts +5 -0
  5. package/front_end/core/sdk/CSSPropertyParserMatchers.ts +10 -10
  6. package/front_end/core/sdk/NetworkManager.ts +7 -0
  7. package/front_end/core/sdk/SecurityOrigin.ts +1 -0
  8. package/front_end/core/sdk/SourceMap.ts +12 -1
  9. package/front_end/core/sdk/SourceMapCache.ts +12 -0
  10. package/front_end/core/sdk/SourceMapManager.ts +17 -8
  11. package/front_end/core/sdk/SourceMapScopeChainEntry.ts +2 -19
  12. package/front_end/core/sdk/SourceMapScopesInfo.ts +21 -0
  13. package/front_end/devtools_compatibility.js +2 -1
  14. package/front_end/entrypoints/formatter_worker/ScopeParser.ts +7 -3
  15. package/front_end/entrypoints/formatter_worker/Substitute.ts +90 -26
  16. package/front_end/entrypoints/formatter_worker/formatter_worker-entrypoint.ts +1 -1
  17. package/front_end/entrypoints/main/ExecutionContextSelector.ts +17 -4
  18. package/front_end/entrypoints/main/MainImpl.ts +8 -0
  19. package/front_end/generated/InspectorBackendCommands.ts +4 -6
  20. package/front_end/generated/protocol-mapping.d.ts +0 -8
  21. package/front_end/generated/protocol-proxy-api.d.ts +0 -6
  22. package/front_end/generated/protocol.ts +4 -25
  23. package/front_end/models/ai_assistance/AiAgent2.ts +13 -1
  24. package/front_end/models/ai_assistance/AiConversation.ts +1 -1
  25. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +13 -19
  26. package/front_end/models/ai_assistance/debug.ts +10 -2
  27. package/front_end/models/change_tracker/ChangeTracker.ts +5 -64
  28. package/front_end/models/comment_manager/CD4ABridge.ts +2 -2
  29. package/front_end/models/comment_manager/CommentManager.ts +6 -5
  30. package/front_end/models/comment_manager/CommentThread.ts +30 -13
  31. package/front_end/models/formatter/FormatterWorkerPool.ts +2 -2
  32. package/front_end/models/issues_manager/CookieDeprecationMetadataIssue.ts +6 -7
  33. package/front_end/models/issues_manager/DeprecationIssue.ts +1 -1
  34. package/front_end/models/issues_manager/MarkdownIssueDescription.ts +14 -5
  35. package/front_end/models/issues_manager/SRIMessageSignatureIssue.ts +2 -4
  36. package/front_end/models/issues_manager/descriptions/cookieWarnMetadataGrantRead.md +0 -1
  37. package/front_end/models/issues_manager/descriptions/cookieWarnMetadataGrantReadOptOut.md +5 -0
  38. package/front_end/models/issues_manager/descriptions/cookieWarnMetadataGrantSet.md +0 -1
  39. package/front_end/models/issues_manager/descriptions/cookieWarnMetadataGrantSetOptOut.md +5 -0
  40. package/front_end/models/issues_manager/descriptions/deprecation.md +1 -1
  41. package/front_end/models/issues_manager/descriptions/sriValidationFailedIntegrityMismatch.md +2 -0
  42. package/front_end/models/javascript_metadata/NativeFunctions.ts +7 -2
  43. package/front_end/models/persistence/NetworkPersistenceManager.ts +14 -2
  44. package/front_end/models/source_map_scopes/NamesResolver.ts +42 -39
  45. package/front_end/panels/accessibility/AccessibilityAnnouncementRecordingView.ts +70 -1
  46. package/front_end/panels/ai_assistance/components/ChatMessage.ts +1 -1
  47. package/front_end/panels/common/CommentThreadWidget.ts +29 -10
  48. package/front_end/panels/common/CommentsOverlayWidget.ts +77 -14
  49. package/front_end/panels/common/ExtensionServer.ts +18 -8
  50. package/front_end/panels/common/commentThreadWidget.css +11 -0
  51. package/front_end/panels/console/ConsolePrompt.ts +1 -1
  52. package/front_end/panels/console/ConsoleViewMessage.ts +1 -1
  53. package/front_end/panels/elements/ClassesPaneWidget.ts +2 -1
  54. package/front_end/panels/elements/DOMTreeContextMenu.ts +1 -1
  55. package/front_end/panels/elements/{ElementsTreeOutline.ts → DOMTreeWidget.ts} +34 -24
  56. package/front_end/panels/elements/ElementsPanel.ts +1 -1
  57. package/front_end/panels/elements/ElementsTreeElement.ts +1 -1
  58. package/front_end/panels/elements/ShortcutTreeElement.ts +1 -1
  59. package/front_end/panels/elements/StylePropertiesSection.ts +10 -0
  60. package/front_end/panels/elements/StylesSidebarPane.ts +105 -55
  61. package/front_end/panels/elements/TopLayerContainer.ts +1 -1
  62. package/front_end/panels/elements/components/CSSQuery.ts +1 -1
  63. package/front_end/panels/elements/components/cssQuery.css +4 -0
  64. package/front_end/panels/elements/elements.ts +3 -3
  65. package/front_end/panels/elements/stylesSidebarPane.css +6 -0
  66. package/front_end/panels/profiler/HeapDetachedElementsDataGrid.ts +1 -1
  67. package/front_end/panels/profiler/HeapProfileView.ts +8 -6
  68. package/front_end/panels/profiler/HeapSnapshotDataGrids.ts +8 -1
  69. package/front_end/panels/profiler/HeapSnapshotView.ts +6 -4
  70. package/front_end/panels/security/SecurityPanel.ts +87 -99
  71. package/front_end/panels/sensors/SensorsView.ts +87 -0
  72. package/front_end/panels/sensors/sensors-meta.ts +69 -1
  73. package/front_end/panels/settings/keybindsSettingsTab.css +1 -1
  74. package/front_end/panels/sources/ResourceOriginPlugin.ts +8 -3
  75. package/front_end/panels/sources/ScopeChainSidebarPane.ts +3 -4
  76. package/front_end/panels/sources/TabbedEditorContainer.ts +339 -126
  77. package/front_end/panels/sources/UISourceCodeFrame.ts +9 -0
  78. package/front_end/third_party/chromium/README.chromium +1 -1
  79. package/front_end/third_party/chromium/client-variations/ClientVariations.js +25 -96
  80. package/front_end/third_party/codemirror.next/chunk/angular.js +1 -1
  81. package/front_end/third_party/codemirror.next/chunk/codemirror.js +1 -1
  82. package/front_end/third_party/codemirror.next/chunk/codemirror.js.map +1 -1
  83. package/front_end/third_party/codemirror.next/chunk/cpp.js +1 -1
  84. package/front_end/third_party/codemirror.next/chunk/cpp.js.map +1 -1
  85. package/front_end/third_party/codemirror.next/chunk/java.js +1 -1
  86. package/front_end/third_party/codemirror.next/chunk/java.js.map +1 -1
  87. package/front_end/third_party/codemirror.next/chunk/legacy.js +1 -1
  88. package/front_end/third_party/codemirror.next/chunk/less.js +1 -1
  89. package/front_end/third_party/codemirror.next/chunk/less.js.map +1 -1
  90. package/front_end/third_party/codemirror.next/chunk/markdown.js +1 -1
  91. package/front_end/third_party/codemirror.next/chunk/markdown.js.map +1 -1
  92. package/front_end/third_party/codemirror.next/chunk/php.js +1 -1
  93. package/front_end/third_party/codemirror.next/chunk/php.js.map +1 -1
  94. package/front_end/third_party/codemirror.next/chunk/python.js +1 -1
  95. package/front_end/third_party/codemirror.next/chunk/python.js.map +1 -1
  96. package/front_end/third_party/codemirror.next/chunk/sass.js +1 -1
  97. package/front_end/third_party/codemirror.next/chunk/sass.js.map +1 -1
  98. package/front_end/third_party/codemirror.next/chunk/svelte.js +1 -1
  99. package/front_end/third_party/codemirror.next/chunk/svelte.js.map +1 -1
  100. package/front_end/third_party/codemirror.next/chunk/vue.js +1 -1
  101. package/front_end/third_party/codemirror.next/chunk/wast.js +1 -1
  102. package/front_end/third_party/codemirror.next/chunk/wast.js.map +1 -1
  103. package/front_end/third_party/codemirror.next/chunk/xml.js +1 -1
  104. package/front_end/third_party/codemirror.next/chunk/xml.js.map +1 -1
  105. package/front_end/third_party/codemirror.next/codemirror.next.js +1 -1
  106. package/front_end/third_party/codemirror.next/package.json +4 -1
  107. package/front_end/third_party/codemirror.next/rebuild.sh +1 -1
  108. package/front_end/ui/comments/CommentOverlayManager.ts +11 -10
  109. package/front_end/ui/components/markdown_view/MarkdownPlaceholderLitRenderer.ts +117 -0
  110. package/front_end/ui/components/markdown_view/markdown_view.ts +2 -0
  111. package/front_end/ui/legacy/Treeoutline.ts +30 -20
  112. package/front_end/ui/legacy/UIUtils.ts +1 -1
  113. package/front_end/ui/legacy/components/data_grid/DataGridElement.ts +1 -1
  114. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +228 -343
  115. package/front_end/ui/legacy/components/object_ui/objectPropertiesSection.css +2 -27
  116. package/front_end/ui/legacy/treeoutline.css +2 -2
  117. package/front_end/ui/visual_logging/KnownContextValues.ts +7 -0
  118. package/mcp/mcp.ts +1 -0
  119. package/package.json +1 -1
package/eslint.config.mjs CHANGED
@@ -59,6 +59,7 @@ export default defineConfig([
59
59
  'scripts/migration/**/*.js',
60
60
  'scripts/protocol_typescript/*.js',
61
61
  'scripts/deps/tests/fixtures',
62
+ 'scripts/gn_deps_verifier/tests/fixtures',
62
63
  'test/**/fixtures/',
63
64
  'test/e2e/**/*.js',
64
65
  'test/shared/**/*.js',
@@ -740,7 +741,8 @@ export default defineConfig([
740
741
  'front_end/**/*.docs.ts',
741
742
  'test/**/*.ts',
742
743
  '**/testing/*.ts',
743
- 'scripts/eslint_rules/test/**/*',
744
+ 'scripts/eslint_rules/tests/**/*',
745
+ 'scripts/gn_deps_verifier/tests/**/*',
744
746
  'extensions/cxx_debugging/e2e/**/*.ts',
745
747
  'extensions/cxx_debugging/tests/**/*.ts',
746
748
  ],
@@ -498,10 +498,11 @@ export enum DevtoolsExperiments {
498
498
  'durable-messages' = 110,
499
499
  'jpeg-xl' = 111,
500
500
  'plus-button' = 112,
501
+ 'source-map-scopes-in-sources-panel' = 113,
501
502
  /* eslint-enable @typescript-eslint/naming-convention */
502
503
 
503
504
  // Increment this when new experiments are added.
504
- MAX_VALUE = 113,
505
+ MAX_VALUE = 114,
505
506
  }
506
507
 
507
508
  /** Update DevToolsIssuesPanelIssueExpanded from tools/metrics/histograms/enums.xml if new enum is added. **/
@@ -9,6 +9,7 @@ export enum ExperimentName {
9
9
  DURABLE_MESSAGES = 'durable-messages',
10
10
  JPEG_XL = 'jpeg-xl',
11
11
  PLUS_BUTTON = 'plus-button',
12
+ SOURCE_MAP_SCOPES_IN_SOURCES_PANEL = 'source-map-scopes-in-sources-panel',
12
13
  // Adding or removing an entry from this enum?
13
14
  // You will need to update:
14
15
  // 1. DevToolsExperiments enum in host/UserMetrics.ts
@@ -549,6 +549,10 @@ interface DevToolsNetworkBackendLinking {
549
549
  enabled: boolean;
550
550
  }
551
551
 
552
+ interface DevToolsAiNaturalLanguageInterface {
553
+ enabled: boolean;
554
+ }
555
+
552
556
  /**
553
557
  * The host configuration that we expect from the DevTools back-end.
554
558
  *
@@ -579,6 +583,7 @@ export type HostConfig = Platform.TypeScriptUtilities.RecursivePartial<{
579
583
  devToolsAiCodeGeneration: HostConfigAiCodeGeneration,
580
584
  devToolsAiCodeCompletionStyles: HostConfigAiCodeCompletionStyles,
581
585
  devToolsVeLogging: HostConfigVeLogging,
586
+ devToolsAiNaturalLanguageInterface: DevToolsAiNaturalLanguageInterface,
582
587
  /**
583
588
  * OffTheRecord here indicates that the user's profile is either incognito,
584
589
  * or guest mode, rather than a "normal" profile.
@@ -56,6 +56,10 @@ export class BaseVariableMatch implements Match {
56
56
  }
57
57
  }
58
58
 
59
+ function isVariableNameNode(node: CodeMirror.SyntaxNode|null|undefined, ast: SyntaxTree): boolean {
60
+ return node?.name === 'VariableName' && ast.text(node).length > 2;
61
+ }
62
+
59
63
  const BaseVariableMatcherBase: MatcherClass<BaseVariableMatch> = matcherBase(BaseVariableMatch);
60
64
  // This matcher provides matching for var() functions and basic computedText support. Computed text is resolved by a
61
65
  // callback. This matcher is intended to be used directly only in environments where CSSMatchedStyles is not available.
@@ -83,15 +87,11 @@ export class BaseVariableMatcher extends BaseVariableMatcherBase {
83
87
  const nameNode = args[0][0];
84
88
  const fallback = args.length === 2 ? args[1] : undefined;
85
89
 
86
- if (nameNode?.name !== 'VariableName' && nameNode?.name !== 'ValueName') {
90
+ if (!isVariableNameNode(nameNode, matching.ast)) {
87
91
  return null;
88
92
  }
89
93
 
90
94
  const varName = matching.ast.text(nameNode);
91
- if (!varName.startsWith('--') || varName.length <= 2) {
92
- return null;
93
- }
94
-
95
95
  return new BaseVariableMatch(
96
96
  matching.ast.text(node), node, varName, fallback, matching, this.#computedTextCallback);
97
97
  }
@@ -903,7 +903,7 @@ export class LinkableNameMatcher extends LinkableNameMatcherBase {
903
903
  propertyName === LinkableNameProperties.POSITION_TRY_FALLBACKS;
904
904
  // We only mark top level nodes or nodes that are inside `var()` expressions as linkable names.
905
905
  if (!propertyName || (node.name !== 'ValueName' && node.name !== 'VariableName') ||
906
- !isAParentDeclarationOrVarCall || (node.name === 'ValueName' && shouldMatchOnlyVariableName)) {
906
+ !isAParentDeclarationOrVarCall || (shouldMatchOnlyVariableName && !isVariableNameNode(node, matching.ast))) {
907
907
  return null;
908
908
  }
909
909
 
@@ -1118,7 +1118,7 @@ export class CustomFunctionMatcher extends CustomFunctionMatcherBase {
1118
1118
  return null;
1119
1119
  }
1120
1120
  const callee = matching.ast.text(node.getChild('VariableName'));
1121
- if (!callee?.startsWith('--')) {
1121
+ if (!callee || callee.length <= 2 || !callee.startsWith('--')) {
1122
1122
  return null;
1123
1123
  }
1124
1124
  const args = ASTUtils.callArgs(node);
@@ -1295,7 +1295,7 @@ export class AnchorFunctionMatcher extends AnchorFunctionMatcherBase {
1295
1295
  }
1296
1296
 
1297
1297
  override matches(node: CodeMirror.SyntaxNode, matching: BottomUpTreeMatching): AnchorFunctionMatch|null {
1298
- if (node.name === 'VariableName') {
1298
+ if (isVariableNameNode(node, matching.ast)) {
1299
1299
  // Double-dashed anchor reference to be rendered with a link to its matching anchor.
1300
1300
  let parent = node.parent;
1301
1301
  if (parent?.name !== 'ArgList') {
@@ -1316,7 +1316,7 @@ export class AnchorFunctionMatcher extends AnchorFunctionMatcherBase {
1316
1316
  if (calleeText === 'anchor' && args.length <= 2) {
1317
1317
  return null;
1318
1318
  }
1319
- if (args.find(arg => arg.name === 'VariableName')) {
1319
+ if (args.find(arg => isVariableNameNode(arg, matching.ast))) {
1320
1320
  // We have an explicit anchor reference, no need to render swatch.
1321
1321
  return null;
1322
1322
  }
@@ -1339,7 +1339,7 @@ export class PositionAnchorMatcher extends PositionAnchorMatcherBase {
1339
1339
  }
1340
1340
 
1341
1341
  override matches(node: CodeMirror.SyntaxNode, matching: BottomUpTreeMatching): PositionAnchorMatch|null {
1342
- if (node.name !== 'VariableName') {
1342
+ if (!isVariableNameNode(node, matching.ast)) {
1343
1343
  return null;
1344
1344
  }
1345
1345
 
@@ -2629,6 +2629,13 @@ export class InterceptedRequest {
2629
2629
  this.networkRequest.setCookieHeaders =
2630
2630
  InterceptedRequest.mergeSetCookieHeaders(originalSetCookieHeaders, setCookieHeadersFromOverrides);
2631
2631
  this.networkRequest.hasOverriddenContent = isBodyOverridden;
2632
+ if (isBodyOverridden) {
2633
+ this.networkRequest.setContentDataProvider(async () => {
2634
+ const {mimeType, charset} = this.getMimeTypeAndCharset();
2635
+ return new TextUtils.ContentData.ContentData(body, /* isBase64= */ true,
2636
+ mimeType ?? 'application/octet-stream', charset ?? undefined);
2637
+ });
2638
+ }
2632
2639
  }
2633
2640
 
2634
2641
  void this.#fetchAgent.invoke_fulfillRequest({requestId: this.requestId, responseCode, body, responseHeaders});
@@ -28,6 +28,7 @@ const OPAQUE_PREFIXES = [
28
28
  'about:',
29
29
  'blob:about',
30
30
  'blob:data',
31
+ 'blob:file',
31
32
  'blob:null',
32
33
  ] as const;
33
34
 
@@ -7,7 +7,7 @@ import * as Common from '../common/common.js';
7
7
  import * as Platform from '../platform/platform.js';
8
8
  import * as TextUtils from '../text_utils/text_utils.js';
9
9
 
10
- import type {CallFrame, ScopeChainEntry} from './DebuggerModel.js';
10
+ import type {CallFrame, Location, 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';
@@ -843,6 +843,17 @@ export class SourceMap {
843
843
  return this.#scopesInfo.resolveMappedScopeChain(frame);
844
844
  }
845
845
 
846
+ resolveMappedVariablesAtPosition(location: Location,
847
+ ignoreInnerBlockScopes = false): Array<Map<string, string|null>>|null {
848
+ this.#ensureSourceMapProcessed();
849
+ if (this.#provenance === SourceMapProvenance.USER || !this.#scopesInfo?.hasVariablesAndBindings()) {
850
+ return null;
851
+ }
852
+
853
+ return this.#scopesInfo.resolveMappedVariablesAtPosition(location.lineNumber, location.columnNumber,
854
+ ignoreInnerBlockScopes);
855
+ }
856
+
846
857
  findOriginalFunctionName(position: ScopesCodec.Position): string|null {
847
858
  this.#ensureSourceMapProcessed();
848
859
  return this.#scopesInfo?.findOriginalFunctionName(position) ?? null;
@@ -28,11 +28,17 @@ export class SourceMapCache {
28
28
  }
29
29
 
30
30
  async set(debugId: DebugId, securityOrigin: Platform.DevToolsPath.UrlString, sourceMap: SourceMapV3): Promise<void> {
31
+ if (!securityOrigin || securityOrigin === 'file://') {
32
+ return;
33
+ }
31
34
  const cache = await this.#cache();
32
35
  await cache?.put(SourceMapCache.#urlForDebugId(debugId, securityOrigin), new Response(JSON.stringify(sourceMap)));
33
36
  }
34
37
 
35
38
  async get(debugId: DebugId, securityOrigin: Platform.DevToolsPath.UrlString): Promise<SourceMapV3|null> {
39
+ if (!securityOrigin || securityOrigin === 'file://') {
40
+ return null;
41
+ }
36
42
  const cache = await this.#cache();
37
43
  const response = await cache?.match(SourceMapCache.#urlForDebugId(debugId, securityOrigin));
38
44
  return (await response?.json() as SourceMapV3 | undefined) ?? null;
@@ -65,10 +71,16 @@ const IN_MEMORY_INSTANCE = new (class implements Pick<SourceMapCache, 'get'|'set
65
71
  readonly #cache = new Map<string, SourceMapV3>();
66
72
 
67
73
  async set(debugId: DebugId, securityOrigin: Platform.DevToolsPath.UrlString, sourceMap: SourceMapV3): Promise<void> {
74
+ if (!securityOrigin || securityOrigin === 'file://') {
75
+ return;
76
+ }
68
77
  this.#cache.set(`${debugId}|${securityOrigin}`, sourceMap);
69
78
  }
70
79
 
71
80
  async get(debugId: DebugId, securityOrigin: Platform.DevToolsPath.UrlString): Promise<SourceMapV3|null> {
81
+ if (!securityOrigin || securityOrigin === 'file://') {
82
+ return null;
83
+ }
72
84
  return this.#cache.get(`${debugId}|${securityOrigin}`) ?? null;
73
85
  }
74
86
 
@@ -7,6 +7,7 @@ import * as Platform from '../platform/platform.js';
7
7
 
8
8
  import type {FrameAssociated} from './FrameAssociated.js';
9
9
  import {PageResourceLoader, type PageResourceLoadInitiator, type ResourceLoader} from './PageResourceLoader.js';
10
+ import {SecurityOrigin} from './SecurityOrigin.js';
10
11
  import {type DebugId, parseSourceMap, SourceMap, type SourceMapProvenance, type SourceMapV3} from './SourceMap.js';
11
12
  import {SourceMapCache} from './SourceMapCache.js';
12
13
  import {type Target, Type} from './Target.js';
@@ -215,14 +216,24 @@ export class SourceMapManager<T extends FrameAssociated> extends Common.ObjectWr
215
216
  }
216
217
  }
217
218
 
219
+ function getCacheOrigin(initiator: PageResourceLoadInitiator): Platform.DevToolsPath.UrlString|null {
220
+ if (!initiator.initiatorUrl) {
221
+ return null;
222
+ }
223
+ const securityOrigin = SecurityOrigin.create(initiator.initiatorUrl);
224
+ if (securityOrigin.isOpaque() || (securityOrigin.isFile() && securityOrigin.siteId() === 'file:///')) {
225
+ return null;
226
+ }
227
+ return securityOrigin.siteId() as Platform.DevToolsPath.UrlString;
228
+ }
229
+
218
230
  async function loadSourceMap(resourceLoader: ResourceLoader, sourceMapCache: SourceMapCache,
219
231
  url: Platform.DevToolsPath.UrlString, debugId: DebugId|null,
220
232
  initiator: PageResourceLoadInitiator): Promise<SourceMapV3> {
221
233
  try {
222
- if (debugId) {
223
- const securityOrigin = initiator.initiatorUrl ? Common.ParsedURL.ParsedURL.extractOrigin(initiator.initiatorUrl) :
224
- Platform.DevToolsPath.EmptyUrlString;
225
- const cachedSourceMap = await sourceMapCache.get(debugId, securityOrigin);
234
+ const cacheOrigin = debugId ? getCacheOrigin(initiator) : null;
235
+ if (debugId && cacheOrigin) {
236
+ const cachedSourceMap = await sourceMapCache.get(debugId, cacheOrigin);
226
237
  if (cachedSourceMap) {
227
238
  return cachedSourceMap;
228
239
  }
@@ -230,11 +241,9 @@ async function loadSourceMap(resourceLoader: ResourceLoader, sourceMapCache: Sou
230
241
 
231
242
  const {content} = await resourceLoader.loadResource(url, initiator);
232
243
  const sourceMap = parseSourceMap(content);
233
- if (debugId && 'debugId' in sourceMap && sourceMap.debugId === debugId) {
244
+ if (debugId && cacheOrigin && 'debugId' in sourceMap && sourceMap.debugId === debugId) {
234
245
  // In case something goes wrong with updating the cache, we still want to use the source map.
235
- const securityOrigin = initiator.initiatorUrl ? Common.ParsedURL.ParsedURL.extractOrigin(initiator.initiatorUrl) :
236
- Platform.DevToolsPath.EmptyUrlString;
237
- await sourceMapCache.set(sourceMap.debugId as DebugId, securityOrigin, sourceMap).catch();
246
+ await sourceMapCache.set(sourceMap.debugId as DebugId, cacheOrigin, sourceMap).catch();
238
247
  }
239
248
  return sourceMap;
240
249
  } catch (cause) {
@@ -8,7 +8,7 @@ import * as i18n from '../i18n/i18n.js';
8
8
 
9
9
  import type {CallFrame, LocationRange, ScopeChainEntry} from './DebuggerModel.js';
10
10
  import {type GetPropertiesResult, type RemoteObject, RemoteObjectImpl, RemoteObjectProperty} from './RemoteObject.js';
11
- import {contains} from './SourceMapScopesInfo.js';
11
+ import {findExpression} from './SourceMapScopesInfo.js';
12
12
 
13
13
  const UIStrings = {
14
14
  /**
@@ -263,25 +263,8 @@ class SourceMapScopeRemoteObject extends RemoteObjectImpl {
263
263
 
264
264
  /** @returns null if the variable is unavailable at the current paused location */
265
265
  #findExpression(index: number): string|null {
266
- if (!this.#range) {
267
- return null;
268
- }
269
-
270
- const expressionOrSubRanges = this.#range.values[index];
271
- if (typeof expressionOrSubRanges === 'string') {
272
- return expressionOrSubRanges;
273
- }
274
- if (!expressionOrSubRanges) {
275
- return null;
276
- }
277
-
278
266
  const pausedPosition = this.#callFrame.location();
279
- for (const range of expressionOrSubRanges) {
280
- if (contains({start: range.from, end: range.to}, pausedPosition.lineNumber, pausedPosition.columnNumber)) {
281
- return range.value ?? null;
282
- }
283
- }
284
- return null;
267
+ return findExpression(this.#range, index, pausedPosition?.lineNumber, pausedPosition?.columnNumber);
285
268
  }
286
269
 
287
270
  static #unavailableProperty(name: string): RemoteObjectProperty {
@@ -406,6 +406,20 @@ export class SourceMapScopesInfo {
406
406
  return rangeChain;
407
407
  }
408
408
 
409
+ resolveMappedVariablesAtPosition(line: number, column: number,
410
+ ignoreInnerBlockScopes = false): Array<Map<string, string|null>>|null {
411
+ const rangeChain = this.#findGeneratedRangeChain(line, column);
412
+ const startScope = rangeChain.at(-1)?.originalScope;
413
+ const innerMostScope =
414
+ (startScope && ignoreInnerBlockScopes && this.#findFunctionScopeInOriginalScopeChain(startScope)) || startScope;
415
+ const result: Array<Map<string, string|null>> = [];
416
+ for (let scope = innerMostScope; scope; scope = scope.parent) {
417
+ const range = rangeChain.findLast(r => r.originalScope === scope);
418
+ result.push(new Map(scope.variables.map((v, i) => [v, findExpression(range, i, line, column)])));
419
+ }
420
+ return innerMostScope ? result : null;
421
+ }
422
+
409
423
  /**
410
424
  * Returns the authored function name of the function containing the provided generated position.
411
425
  */
@@ -570,6 +584,13 @@ export interface TranslatedFrame {
570
584
  url?: Platform.DevToolsPath.UrlString;
571
585
  }
572
586
 
587
+ export function findExpression(range: ScopesCodec.GeneratedRange|undefined, index: number, line = 0,
588
+ column = 0): string|null {
589
+ const val = range?.values[index];
590
+ return (typeof val === 'string' ? val : val?.find(r => contains({start: r.from, end: r.to}, line, column))?.value) ??
591
+ null;
592
+ }
593
+
573
594
  export function contains(
574
595
  range: Pick<ScopesCodec.GeneratedRange, 'start'|'end'>, line: number, column: number): boolean {
575
596
  if (range.start.line > line || (range.start.line === line && range.start.column > column)) {
@@ -876,7 +876,8 @@
876
876
  'durable-messages': 110,
877
877
  'jpeg-xl': 111,
878
878
  'plus-button': 112,
879
- MAX_VALUE: 113,
879
+ 'source-map-scopes-in-sources-panel': 113,
880
+ MAX_VALUE: 114,
880
881
  },
881
882
  IssueExpanded: {
882
883
  CrossOriginEmbedderPolicy: 0,
@@ -11,9 +11,13 @@ export function parseScopes(expression: string, sourceType: 'module'|'script' =
11
11
  // Parse the expression and find variables and scopes.
12
12
  let root: Acorn.ESTree.Node|null = null;
13
13
  try {
14
- root = Acorn.parse(
15
- expression, {ecmaVersion: ECMA_VERSION, allowAwaitOutsideFunction: true, ranges: false, sourceType}) as
16
- Acorn.ESTree.Node;
14
+ root = Acorn.parse(expression, {
15
+ ecmaVersion: ECMA_VERSION,
16
+ allowAwaitOutsideFunction: true,
17
+ checkPrivateFields: false,
18
+ ranges: false,
19
+ sourceType,
20
+ }) as Acorn.ESTree.Node;
17
21
  } catch {
18
22
  return null;
19
23
  }
@@ -6,10 +6,10 @@ import * as Acorn from '../../third_party/acorn/acorn.js';
6
6
  import {DefinitionKind} from '../formatter_actions/formatter_actions.js';
7
7
 
8
8
  import {ECMA_VERSION} from './AcornTokenizer.js';
9
- import {ScopeVariableAnalysis} from './ScopeParser.js';
9
+ import {ScopeVariableAnalysis, type VariableUses} from './ScopeParser.js';
10
10
 
11
- export function substituteExpression(expression: string, nameMap: Map<string, string|null>): string {
12
- const replacements = computeSubstitution(expression, nameMap);
11
+ export function substituteExpression(expression: string, nameMaps: Array<Map<string, string|null>>): string {
12
+ const replacements = computeSubstitution(expression, nameMaps);
13
13
  return applySubstitution(expression, replacements);
14
14
  }
15
15
 
@@ -20,13 +20,44 @@ interface Replacement {
20
20
  isShorthandAssignmentProperty: boolean;
21
21
  }
22
22
 
23
+ function parseBindingExpression(expression: string): {
24
+ replacement: string,
25
+ freeVariables: string[],
26
+ allNames: Set<string>,
27
+ } {
28
+ const options = {
29
+ ecmaVersion: ECMA_VERSION,
30
+ allowAwaitOutsideFunction: true,
31
+ allowImportExportEverywhere: true,
32
+ checkPrivateFields: false,
33
+ ranges: false,
34
+ } as const;
35
+ const root = Acorn.parse(`(${expression})`, options);
36
+ const exprAt = Acorn.Parser.parseExpressionAt(expression, 0, options);
37
+ if (root.body.length !== 1 || root.body[0].type !== 'ExpressionStatement') {
38
+ throw new SyntaxError(`Invalid binding expression '${expression}'`);
39
+ }
40
+ const expr = root.body[0].expression;
41
+ if (exprAt.start !== expr.start - 1 || exprAt.end !== expr.end - 1) {
42
+ throw new SyntaxError(`Invalid binding expression '${expression}'`);
43
+ }
44
+ const analysis = new ScopeVariableAnalysis(root as Acorn.ESTree.Node, `(${expression})`);
45
+ analysis.run();
46
+ const needsParens = expr.type !== 'Identifier' && expr.type !== 'MemberExpression' && expr.type !== 'ThisExpression';
47
+ return {
48
+ replacement: needsParens ? `(${expression})` : expression,
49
+ freeVariables: [...analysis.getFreeVariables().keys()],
50
+ allNames: analysis.getAllNames(),
51
+ };
52
+ }
53
+
23
54
  /**
24
55
  * Given an |expression| and a mapping from names to new names, the |computeSubstitution|
25
56
  * function returns a list of replacements sorted by the offset. The function throws if
26
57
  * it cannot parse the expression or the substitution is impossible to perform (for example
27
58
  * if the substitution target is 'this' within a function, it would become bound there).
28
59
  **/
29
- function computeSubstitution(expression: string, nameMap: Map<string, string|null>): Replacement[] {
60
+ function computeSubstitution(expression: string, nameMaps: Array<Map<string, string|null>>): Replacement[] {
30
61
  // Parse the expression and find variables and scopes.
31
62
  const root = Acorn.parse(expression, {
32
63
  ecmaVersion: ECMA_VERSION,
@@ -42,10 +73,33 @@ function computeSubstitution(expression: string, nameMap: Map<string, string|nul
42
73
 
43
74
  // Prepare the machinery for generating fresh names (to avoid variable captures).
44
75
  const allNames = scopeVariables.getAllNames();
45
- for (const rename of nameMap.values()) {
46
- if (rename) {
47
- allNames.add(rename);
76
+ const nameMap = new Map<string, string|null>();
77
+ const parsedBindings = new Map<string, ReturnType<typeof parseBindingExpression>>();
78
+ const shadowedNames = new Set<string>();
79
+ for (const scopeMap of nameMaps) {
80
+ const scopeNames = new Set<string>();
81
+ for (const [name, rename] of scopeMap.entries()) {
82
+ let parsed: ReturnType<typeof parseBindingExpression>|undefined;
83
+ if (rename !== null) {
84
+ try {
85
+ parsed = parseBindingExpression(rename);
86
+ parsed.allNames.forEach(id => allNames.add(id));
87
+ parsed.freeVariables.forEach(id => scopeNames.add(id));
88
+ } catch (error) {
89
+ if (!nameMap.has(name) && freeVariables.has(name)) {
90
+ throw error;
91
+ }
92
+ }
93
+ }
94
+ if (!nameMap.has(name)) {
95
+ const isShadowed = parsed?.freeVariables.some(id => shadowedNames.has(id));
96
+ nameMap.set(name, isShadowed ? null : rename);
97
+ if (parsed && !isShadowed) {
98
+ parsedBindings.set(name, parsed);
99
+ }
100
+ }
48
101
  }
102
+ scopeNames.forEach(id => shadowedNames.add(id));
49
103
  }
50
104
  function getNewName(base: string): string {
51
105
  let i = 1;
@@ -58,6 +112,7 @@ function computeSubstitution(expression: string, nameMap: Map<string, string|nul
58
112
  }
59
113
 
60
114
  // Perform the substitutions.
115
+ const capturedBinders = new Map<VariableUses, string>();
61
116
  for (const [name, rename] of nameMap.entries()) {
62
117
  const defUse = freeVariables.get(name);
63
118
  if (!defUse) {
@@ -68,34 +123,43 @@ function computeSubstitution(expression: string, nameMap: Map<string, string|nul
68
123
  throw new Error(`Cannot substitute '${name}' as the underlying variable '${rename}' is unavailable`);
69
124
  }
70
125
 
71
- const binders = [];
126
+ const parsed = parsedBindings.get(name);
127
+ if (!parsed) {
128
+ continue;
129
+ }
130
+ const freeIds = [...parsed.freeVariables];
72
131
  for (const use of defUse) {
73
132
  result.push({
74
133
  from: name,
75
- to: rename,
134
+ to: parsed.replacement,
76
135
  offset: use.offset,
77
136
  isShorthandAssignmentProperty: use.isShorthandAssignmentProperty,
78
137
  });
79
- binders.push(...use.scope.findBinders(rename));
80
- }
81
- // If there is a capturing binder, rename the bound variable.
82
- for (const binder of binders) {
83
- if (binder.definitionKind === DefinitionKind.FIXED) {
84
- // If the identifier is bound to a fixed name, such as 'this',
85
- // then refuse to do the substitution.
86
- throw new Error(`Cannot avoid capture of '${rename}'`);
87
- }
88
- const newName = getNewName(rename);
89
- for (const use of binder.uses) {
90
- result.push({
91
- from: rename,
92
- to: newName,
93
- offset: use.offset,
94
- isShorthandAssignmentProperty: use.isShorthandAssignmentProperty,
95
- });
138
+ for (const freeId of freeIds) {
139
+ for (const binder of use.scope.findBinders(freeId)) {
140
+ capturedBinders.set(binder, freeId);
141
+ }
96
142
  }
97
143
  }
98
144
  }
145
+
146
+ // If there is a capturing binder, rename the bound variable.
147
+ for (const [binder, freeId] of capturedBinders.entries()) {
148
+ if (binder.definitionKind === DefinitionKind.FIXED) {
149
+ // If the identifier is bound to a fixed name, such as 'this',
150
+ // then refuse to do the substitution.
151
+ throw new Error(`Cannot avoid capture of '${freeId}'`);
152
+ }
153
+ const newName = getNewName(freeId);
154
+ for (const use of binder.uses) {
155
+ result.push({
156
+ from: freeId,
157
+ to: newName,
158
+ offset: use.offset,
159
+ isShorthandAssignmentProperty: use.isShorthandAssignmentProperty,
160
+ });
161
+ }
162
+ }
99
163
  result.sort((l, r) => l.offset - r.offset);
100
164
  return result;
101
165
  }
@@ -13,7 +13,7 @@ Platform.HostRuntime.HOST_RUNTIME.workerScope.onmessage = function(event): void
13
13
  indentString: string,
14
14
  content: string,
15
15
  mimeType: string,
16
- mapping: Map<string, string|null>,
16
+ mapping: Array<Map<string, string|null>>,
17
17
  sourceType: 'module'|'script',
18
18
  } = event.data.params;
19
19
  if (!method) {
@@ -37,7 +37,7 @@ export class ExecutionContextSelector implements SDK.TargetManager.SDKModelObser
37
37
 
38
38
  function deferred(this: ExecutionContextSelector): void {
39
39
  // We always want the second context for the service worker targets.
40
- if (!this.#context.flavor(SDK.Target.Target)) {
40
+ if (!this.#context.flavor(SDK.Target.Target) && !runtimeModel.target().targetInfo()?.subtype) {
41
41
  this.#context.setFlavor(SDK.Target.Target, runtimeModel.target());
42
42
  }
43
43
  }
@@ -51,7 +51,8 @@ export class ExecutionContextSelector implements SDK.TargetManager.SDKModelObser
51
51
 
52
52
  const models = this.#targetManager.models(SDK.RuntimeModel.RuntimeModel);
53
53
  if (this.#context.flavor(SDK.Target.Target) === runtimeModel.target() && models.length) {
54
- this.#context.setFlavor(SDK.Target.Target, models[0].target());
54
+ const nextModel = models.find(m => !m.target().targetInfo()?.subtype);
55
+ this.#context.setFlavor(SDK.Target.Target, nextModel ? nextModel.target() : null);
55
56
  }
56
57
  }
57
58
 
@@ -100,7 +101,8 @@ export class ExecutionContextSelector implements SDK.TargetManager.SDKModelObser
100
101
  }
101
102
 
102
103
  #shouldSwitchToContext(executionContext: SDK.RuntimeModel.ExecutionContext): boolean {
103
- if (executionContext.target().targetInfo()?.subtype) {
104
+ if (executionContext.target().targetInfo()?.subtype &&
105
+ this.#context.flavor(SDK.Target.Target) !== executionContext.target()) {
104
106
  return false;
105
107
  }
106
108
  if (this.#lastSelectedContextId && this.#lastSelectedContextId === this.#contextPersistentId(executionContext)) {
@@ -164,7 +166,10 @@ export class ExecutionContextSelector implements SDK.TargetManager.SDKModelObser
164
166
  }
165
167
 
166
168
  #switchContextIfNecessary(executionContext: SDK.RuntimeModel.ExecutionContext): boolean {
167
- if (!this.#context.flavor(SDK.RuntimeModel.ExecutionContext) || this.#shouldSwitchToContext(executionContext)) {
169
+ if ((!this.#context.flavor(SDK.RuntimeModel.ExecutionContext) &&
170
+ (!executionContext.target().targetInfo()?.subtype ||
171
+ this.#context.flavor(SDK.Target.Target) === executionContext.target())) ||
172
+ this.#shouldSwitchToContext(executionContext)) {
168
173
  this.#ignoreContextChanged = true;
169
174
  this.#context.setFlavor(SDK.RuntimeModel.ExecutionContext, executionContext);
170
175
  this.#ignoreContextChanged = false;
@@ -177,6 +182,10 @@ export class ExecutionContextSelector implements SDK.TargetManager.SDKModelObser
177
182
  const runtimeModels = this.#targetManager.models(SDK.RuntimeModel.RuntimeModel);
178
183
  let newContext: SDK.RuntimeModel.ExecutionContext|null = null;
179
184
  for (let i = 0; i < runtimeModels.length && !newContext; ++i) {
185
+ if (runtimeModels[i].target().targetInfo()?.subtype &&
186
+ this.#context.flavor(SDK.Target.Target) !== runtimeModels[i].target()) {
187
+ continue;
188
+ }
180
189
  const executionContexts = runtimeModels[i].executionContexts();
181
190
  for (const executionContext of executionContexts) {
182
191
  if (this.#isDefaultContext(executionContext)) {
@@ -187,6 +196,10 @@ export class ExecutionContextSelector implements SDK.TargetManager.SDKModelObser
187
196
  }
188
197
  if (!newContext) {
189
198
  for (let i = 0; i < runtimeModels.length && !newContext; ++i) {
199
+ if (runtimeModels[i].target().targetInfo()?.subtype &&
200
+ this.#context.flavor(SDK.Target.Target) !== runtimeModels[i].target()) {
201
+ continue;
202
+ }
190
203
  const executionContexts = runtimeModels[i].executionContexts();
191
204
  if (executionContexts.length) {
192
205
  newContext = executionContexts[0];
@@ -415,6 +415,14 @@ export class MainImpl {
415
415
  requiresChromeRestart: false,
416
416
  });
417
417
 
418
+ Root.Runtime.experiments.register({
419
+ name: Root.ExperimentNames.ExperimentName.SOURCE_MAP_SCOPES_IN_SOURCES_PANEL,
420
+ title: 'Source map scopes in the Sources panel',
421
+ aboutFlag: 'devtools-source-map-scopes-in-sources-panel',
422
+ isEnabled: Root.Runtime.hostConfig.devToolsSourceMapScopesInSourcesPanel?.enabled ?? false,
423
+ requiresChromeRestart: false,
424
+ });
425
+
418
426
  for (const experiment of Root.Runtime.experiments.allConfigurableExperiments()) {
419
427
  if (experiment.isEnabled()) {
420
428
  Host.userMetrics.experimentEnabledAtLaunch(experiment.name);