chrome-devtools-frontend 1.0.1700561 → 1.0.1701636

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 (53) hide show
  1. package/front_end/core/root/Runtime.ts +5 -0
  2. package/front_end/core/sdk/CSSPropertyParserMatchers.ts +16 -11
  3. package/front_end/core/sdk/NetworkManager.ts +7 -0
  4. package/front_end/core/sdk/SecurityOrigin.ts +1 -0
  5. package/front_end/core/sdk/SourceMap.ts +12 -1
  6. package/front_end/core/sdk/SourceMapCache.ts +12 -0
  7. package/front_end/core/sdk/SourceMapManager.ts +17 -8
  8. package/front_end/core/sdk/SourceMapScopeChainEntry.ts +2 -19
  9. package/front_end/core/sdk/SourceMapScopesInfo.ts +21 -0
  10. package/front_end/entrypoints/formatter_worker/ScopeParser.ts +7 -3
  11. package/front_end/entrypoints/formatter_worker/Substitute.ts +90 -26
  12. package/front_end/entrypoints/formatter_worker/formatter_worker-entrypoint.ts +1 -1
  13. package/front_end/entrypoints/main/ExecutionContextSelector.ts +17 -4
  14. package/front_end/generated/InspectorBackendCommands.ts +1 -3
  15. package/front_end/generated/protocol-mapping.d.ts +0 -8
  16. package/front_end/generated/protocol-proxy-api.d.ts +0 -6
  17. package/front_end/generated/protocol.ts +4 -22
  18. package/front_end/models/ai_assistance/debug.ts +10 -2
  19. package/front_end/models/comment_manager/CD4ABridge.ts +2 -2
  20. package/front_end/models/comment_manager/CommentManager.ts +3 -0
  21. package/front_end/models/formatter/FormatterWorkerPool.ts +2 -2
  22. package/front_end/models/javascript_metadata/NativeFunctions.ts +7 -2
  23. package/front_end/models/source_map_scopes/NamesResolver.ts +42 -39
  24. package/front_end/panels/accessibility/AccessibilityAnnouncementRecordingView.ts +70 -1
  25. package/front_end/panels/ai_assistance/components/ChatMessage.ts +1 -1
  26. package/front_end/panels/common/CommentsOverlayWidget.ts +29 -2
  27. package/front_end/panels/common/ExtensionServer.ts +18 -8
  28. package/front_end/panels/console/ConsolePrompt.ts +1 -1
  29. package/front_end/panels/console/ConsoleViewMessage.ts +1 -1
  30. package/front_end/panels/elements/ClassesPaneWidget.ts +2 -1
  31. package/front_end/panels/elements/DOMTreeContextMenu.ts +1 -1
  32. package/front_end/panels/elements/{ElementsTreeOutline.ts → DOMTreeWidget.ts} +34 -24
  33. package/front_end/panels/elements/ElementsPanel.ts +1 -1
  34. package/front_end/panels/elements/ElementsTreeElement.ts +1 -1
  35. package/front_end/panels/elements/ShortcutTreeElement.ts +1 -1
  36. package/front_end/panels/elements/StylePropertyTreeElement.ts +1 -1
  37. package/front_end/panels/elements/StylesSidebarPane.ts +4 -6
  38. package/front_end/panels/elements/TopLayerContainer.ts +1 -1
  39. package/front_end/panels/elements/elements.ts +4 -3
  40. package/front_end/panels/profiler/HeapDetachedElementsDataGrid.ts +1 -1
  41. package/front_end/panels/profiler/HeapProfileView.ts +8 -6
  42. package/front_end/panels/profiler/HeapSnapshotDataGrids.ts +8 -1
  43. package/front_end/panels/profiler/HeapSnapshotView.ts +6 -4
  44. package/front_end/panels/security/SecurityPanel.ts +87 -99
  45. package/front_end/panels/sensors/SensorsView.ts +87 -0
  46. package/front_end/panels/sensors/sensors-meta.ts +69 -1
  47. package/front_end/panels/settings/keybindsSettingsTab.css +1 -1
  48. package/front_end/third_party/chromium/README.chromium +1 -1
  49. package/front_end/ui/legacy/Treeoutline.ts +2 -1
  50. package/front_end/ui/legacy/components/data_grid/DataGridElement.ts +1 -1
  51. package/front_end/ui/legacy/treeoutline.css +2 -2
  52. package/front_end/ui/visual_logging/KnownContextValues.ts +7 -0
  53. package/package.json +1 -1
@@ -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,15 @@ export class BaseVariableMatch implements Match {
56
56
  }
57
57
  }
58
58
 
59
+ // TODO(b/533783988): Remove ValueName handling once https://code.haverbeke.berlin/lezer/css/issues/36 is fixed and rolled.
60
+ function isVariableNameNode(node: CodeMirror.SyntaxNode|null|undefined, ast: SyntaxTree): boolean {
61
+ if (node?.name !== 'VariableName' && node?.name !== 'ValueName') {
62
+ return false;
63
+ }
64
+ const text = ast.text(node);
65
+ return text.length > 2 && text.startsWith('--');
66
+ }
67
+
59
68
  const BaseVariableMatcherBase: MatcherClass<BaseVariableMatch> = matcherBase(BaseVariableMatch);
60
69
  // This matcher provides matching for var() functions and basic computedText support. Computed text is resolved by a
61
70
  // callback. This matcher is intended to be used directly only in environments where CSSMatchedStyles is not available.
@@ -83,15 +92,11 @@ export class BaseVariableMatcher extends BaseVariableMatcherBase {
83
92
  const nameNode = args[0][0];
84
93
  const fallback = args.length === 2 ? args[1] : undefined;
85
94
 
86
- if (nameNode?.name !== 'VariableName' && nameNode?.name !== 'ValueName') {
95
+ if (!isVariableNameNode(nameNode, matching.ast)) {
87
96
  return null;
88
97
  }
89
98
 
90
99
  const varName = matching.ast.text(nameNode);
91
- if (!varName.startsWith('--') || varName.length <= 2) {
92
- return null;
93
- }
94
-
95
100
  return new BaseVariableMatch(
96
101
  matching.ast.text(node), node, varName, fallback, matching, this.#computedTextCallback);
97
102
  }
@@ -903,7 +908,7 @@ export class LinkableNameMatcher extends LinkableNameMatcherBase {
903
908
  propertyName === LinkableNameProperties.POSITION_TRY_FALLBACKS;
904
909
  // We only mark top level nodes or nodes that are inside `var()` expressions as linkable names.
905
910
  if (!propertyName || (node.name !== 'ValueName' && node.name !== 'VariableName') ||
906
- !isAParentDeclarationOrVarCall || (node.name === 'ValueName' && shouldMatchOnlyVariableName)) {
911
+ !isAParentDeclarationOrVarCall || (shouldMatchOnlyVariableName && !isVariableNameNode(node, matching.ast))) {
907
912
  return null;
908
913
  }
909
914
 
@@ -1117,8 +1122,8 @@ export class CustomFunctionMatcher extends CustomFunctionMatcherBase {
1117
1122
  if (node.name !== 'CallExpression') {
1118
1123
  return null;
1119
1124
  }
1120
- const callee = matching.ast.text(node.getChild('VariableName'));
1121
- if (!callee?.startsWith('--')) {
1125
+ const callee = matching.ast.text(node.getChild('VariableName') ?? node.getChild('Callee'));
1126
+ if (!callee || callee.length <= 2 || !callee.startsWith('--')) {
1122
1127
  return null;
1123
1128
  }
1124
1129
  const args = ASTUtils.callArgs(node);
@@ -1295,7 +1300,7 @@ export class AnchorFunctionMatcher extends AnchorFunctionMatcherBase {
1295
1300
  }
1296
1301
 
1297
1302
  override matches(node: CodeMirror.SyntaxNode, matching: BottomUpTreeMatching): AnchorFunctionMatch|null {
1298
- if (node.name === 'VariableName') {
1303
+ if (isVariableNameNode(node, matching.ast)) {
1299
1304
  // Double-dashed anchor reference to be rendered with a link to its matching anchor.
1300
1305
  let parent = node.parent;
1301
1306
  if (parent?.name !== 'ArgList') {
@@ -1316,7 +1321,7 @@ export class AnchorFunctionMatcher extends AnchorFunctionMatcherBase {
1316
1321
  if (calleeText === 'anchor' && args.length <= 2) {
1317
1322
  return null;
1318
1323
  }
1319
- if (args.find(arg => arg.name === 'VariableName')) {
1324
+ if (args.find(arg => isVariableNameNode(arg, matching.ast))) {
1320
1325
  // We have an explicit anchor reference, no need to render swatch.
1321
1326
  return null;
1322
1327
  }
@@ -1339,7 +1344,7 @@ export class PositionAnchorMatcher extends PositionAnchorMatcherBase {
1339
1344
  }
1340
1345
 
1341
1346
  override matches(node: CodeMirror.SyntaxNode, matching: BottomUpTreeMatching): PositionAnchorMatch|null {
1342
- if (node.name !== 'VariableName') {
1347
+ if (!isVariableNameNode(node, matching.ast)) {
1343
1348
  return null;
1344
1349
  }
1345
1350
 
@@ -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)) {
@@ -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];
@@ -1368,13 +1368,11 @@ inspectorBackend.registerCommand("Storage.setPrivateVerificationTokensTracking",
1368
1368
  inspectorBackend.registerCommand("Storage.setStorageBucketTracking", [{"name": "storageKey", "type": "string", "optional": false, "description": "", "typeRef": null}, {"name": "enable", "type": "boolean", "optional": false, "description": "", "typeRef": null}], [], "Set tracking for a storage key's buckets.");
1369
1369
  inspectorBackend.registerCommand("Storage.deleteStorageBucket", [{"name": "bucket", "type": "object", "optional": false, "description": "", "typeRef": "Storage.StorageBucket"}], [], "Deletes the Storage Bucket with the given storage key and bucket name.");
1370
1370
  inspectorBackend.registerCommand("Storage.runBounceTrackingMitigations", [], ["deletedSites"], "Deletes state for sites identified as potential bounce trackers, immediately.");
1371
- inspectorBackend.registerCommand("Storage.getRelatedWebsiteSets", [], ["sets"], "Returns the effective Related Website Sets in use by this profile for the browser session. The effective Related Website Sets will not change during a browser session.");
1372
1371
  inspectorBackend.registerType("Storage.UsageForType", [{"name": "storageType", "type": "string", "optional": false, "description": "Name of storage type.", "typeRef": "Storage.StorageType"}, {"name": "usage", "type": "number", "optional": false, "description": "Storage usage (bytes).", "typeRef": null}]);
1373
1372
  inspectorBackend.registerType("Storage.TrustTokens", [{"name": "issuerOrigin", "type": "string", "optional": false, "description": "", "typeRef": null}, {"name": "count", "type": "number", "optional": false, "description": "", "typeRef": null}]);
1374
1373
  inspectorBackend.registerType("Storage.PrivateVerificationToken", [{"name": "id", "type": "string", "optional": false, "description": "Unique identifier of the token in the database.", "typeRef": null}, {"name": "issuerOrigin", "type": "string", "optional": false, "description": "Origin of the token issuer.", "typeRef": null}, {"name": "keyId", "type": "number", "optional": false, "description": "Public key ID used to issue the token.", "typeRef": null}, {"name": "expiration", "type": "number", "optional": false, "description": "Expiration timestamp in seconds since the epoch.", "typeRef": "Network.TimeSinceEpoch"}, {"name": "creationTime", "type": "number", "optional": false, "description": "Token creation timestamp in seconds since the epoch.", "typeRef": "Network.TimeSinceEpoch"}, {"name": "version", "type": "number", "optional": false, "description": "Token protocol version.", "typeRef": null}, {"name": "token", "type": "string", "optional": false, "description": "Base64-encoded serialized token.", "typeRef": null}]);
1375
1374
  inspectorBackend.registerType("Storage.StorageBucket", [{"name": "storageKey", "type": "string", "optional": false, "description": "", "typeRef": "Storage.SerializedStorageKey"}, {"name": "name", "type": "string", "optional": true, "description": "If not specified, it is the default bucket of the storageKey.", "typeRef": null}]);
1376
1375
  inspectorBackend.registerType("Storage.StorageBucketInfo", [{"name": "bucket", "type": "object", "optional": false, "description": "", "typeRef": "Storage.StorageBucket"}, {"name": "id", "type": "string", "optional": false, "description": "", "typeRef": null}, {"name": "expiration", "type": "number", "optional": false, "description": "", "typeRef": "Network.TimeSinceEpoch"}, {"name": "quota", "type": "number", "optional": false, "description": "Storage quota (bytes).", "typeRef": null}, {"name": "persistent", "type": "boolean", "optional": false, "description": "", "typeRef": null}, {"name": "durability", "type": "string", "optional": false, "description": "", "typeRef": "Storage.StorageBucketsDurability"}]);
1377
- inspectorBackend.registerType("Storage.RelatedWebsiteSet", [{"name": "primarySites", "type": "array", "optional": false, "description": "The primary site of this set, along with the ccTLDs if there is any.", "typeRef": "string"}, {"name": "associatedSites", "type": "array", "optional": false, "description": "The associated sites of this set, along with the ccTLDs if there is any.", "typeRef": "string"}, {"name": "serviceSites", "type": "array", "optional": false, "description": "The service sites of this set, along with the ccTLDs if there is any.", "typeRef": "string"}]);
1378
1376
 
1379
1377
  // SystemInfo.
1380
1378
  inspectorBackend.registerEnum("SystemInfo.SubsamplingFormat", {Yuv420: "yuv420", Yuv422: "yuv422", Yuv444: "yuv444"});
@@ -1507,7 +1505,7 @@ inspectorBackend.registerCommand("WebMCP.enable", [], [], "Enables the WebMCP do
1507
1505
  inspectorBackend.registerCommand("WebMCP.disable", [], [], "Disables the WebMCP domain.");
1508
1506
  inspectorBackend.registerCommand("WebMCP.invokeTool", [{"name": "frameId", "type": "string", "optional": false, "description": "Frame in which to invoke the tool.", "typeRef": "Page.FrameId"}, {"name": "toolName", "type": "string", "optional": false, "description": "Name of the tool to invoke.", "typeRef": null}, {"name": "input", "type": "object", "optional": false, "description": "Input parameters for the tool, matching the tool's inputSchema.", "typeRef": null}], ["invocationId"], "Invokes a registered tool.");
1509
1507
  inspectorBackend.registerCommand("WebMCP.cancelInvocation", [{"name": "invocationId", "type": "string", "optional": false, "description": "Invocation identifier to cancel.", "typeRef": null}], [], "Cancels a pending tool invocation.");
1510
- inspectorBackend.registerType("WebMCP.Annotation", [{"name": "readOnly", "type": "boolean", "optional": true, "description": "A hint indicating that the tool does not modify any state.", "typeRef": null}, {"name": "untrustedContent", "type": "boolean", "optional": true, "description": "A hint indicating that the tool output may contain untrusted content, ex: UGC, 3rd party data.", "typeRef": null}, {"name": "consequential", "type": "boolean", "optional": true, "description": "A hint indicating that executing the tool will result in consequential actions, ex: booking a flight, transferring money.", "typeRef": null}, {"name": "autosubmit", "type": "boolean", "optional": true, "description": "If the declarative tool was declared with the autosubmit attribute.", "typeRef": null}]);
1508
+ inspectorBackend.registerType("WebMCP.Annotation", [{"name": "readOnly", "type": "boolean", "optional": true, "description": "A hint indicating that the tool does not modify any state.", "typeRef": null}, {"name": "untrustedContent", "type": "boolean", "optional": true, "description": "A hint indicating that the tool output may contain untrusted content, ex: UGC, 3rd party data.", "typeRef": null}, {"name": "consequential", "type": "boolean", "optional": true, "description": "A hint indicating that executing the tool will result in consequential actions, ex: booking a flight, transferring money.", "typeRef": null}, {"name": "debugging", "type": "boolean", "optional": true, "description": "If true, indicates that the tool is intended for debugging and developer tooling rather than end-user interactions.", "typeRef": null}, {"name": "autosubmit", "type": "boolean", "optional": true, "description": "If the declarative tool was declared with the autosubmit attribute.", "typeRef": null}]);
1511
1509
  inspectorBackend.registerType("WebMCP.Tool", [{"name": "name", "type": "string", "optional": false, "description": "Tool name.", "typeRef": null}, {"name": "description", "type": "string", "optional": false, "description": "Tool description.", "typeRef": null}, {"name": "inputSchema", "type": "object", "optional": true, "description": "Schema for the tool's input parameters.", "typeRef": null}, {"name": "annotations", "type": "object", "optional": true, "description": "Optional annotations for the tool.", "typeRef": "WebMCP.Annotation"}, {"name": "frameId", "type": "string", "optional": false, "description": "Frame identifier associated with the tool registration.", "typeRef": "Page.FrameId"}, {"name": "backendNodeId", "type": "number", "optional": true, "description": "Optional node ID for declarative tools.", "typeRef": "DOM.BackendNodeId"}, {"name": "stackTrace", "type": "object", "optional": true, "description": "The stack trace at the time of the registration.", "typeRef": "Runtime.StackTrace"}]);
1512
1510
  inspectorBackend.registerType("WebMCP.RemovedTool", [{"name": "name", "type": "string", "optional": false, "description": "Tool name.", "typeRef": null}, {"name": "frameId", "type": "string", "optional": false, "description": "Frame identifier associated with the tool registration.", "typeRef": "Page.FrameId"}]);
1513
1511
 
@@ -4996,14 +4996,6 @@ export namespace ProtocolMapping {
4996
4996
  paramsType: [];
4997
4997
  returnType: Protocol.Storage.RunBounceTrackingMitigationsResponse;
4998
4998
  };
4999
- /**
5000
- * Returns the effective Related Website Sets in use by this profile for the browser
5001
- * session. The effective Related Website Sets will not change during a browser session.
5002
- */
5003
- 'Storage.getRelatedWebsiteSets': {
5004
- paramsType: [];
5005
- returnType: Protocol.Storage.GetRelatedWebsiteSetsResponse;
5006
- };
5007
4999
  /**
5008
5000
  * Returns information about the system.
5009
5001
  */
@@ -4361,12 +4361,6 @@ declare namespace ProtocolProxyApi {
4361
4361
  */
4362
4362
  invoke_runBounceTrackingMitigations(): Promise<Protocol.Storage.RunBounceTrackingMitigationsResponse>;
4363
4363
 
4364
- /**
4365
- * Returns the effective Related Website Sets in use by this profile for the browser
4366
- * session. The effective Related Website Sets will not change during a browser session.
4367
- */
4368
- invoke_getRelatedWebsiteSets(): Promise<Protocol.Storage.GetRelatedWebsiteSetsResponse>;
4369
-
4370
4364
  }
4371
4365
  export interface StorageDispatcher {
4372
4366
  /**