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.
- package/eslint.config.mjs +3 -1
- package/front_end/core/host/UserMetricsEnums.ts +2 -1
- package/front_end/core/root/ExperimentNames.ts +1 -0
- package/front_end/core/root/Runtime.ts +5 -0
- package/front_end/core/sdk/CSSPropertyParserMatchers.ts +10 -10
- package/front_end/core/sdk/NetworkManager.ts +7 -0
- package/front_end/core/sdk/SecurityOrigin.ts +1 -0
- package/front_end/core/sdk/SourceMap.ts +12 -1
- package/front_end/core/sdk/SourceMapCache.ts +12 -0
- package/front_end/core/sdk/SourceMapManager.ts +17 -8
- package/front_end/core/sdk/SourceMapScopeChainEntry.ts +2 -19
- package/front_end/core/sdk/SourceMapScopesInfo.ts +21 -0
- package/front_end/devtools_compatibility.js +2 -1
- package/front_end/entrypoints/formatter_worker/ScopeParser.ts +7 -3
- package/front_end/entrypoints/formatter_worker/Substitute.ts +90 -26
- package/front_end/entrypoints/formatter_worker/formatter_worker-entrypoint.ts +1 -1
- package/front_end/entrypoints/main/ExecutionContextSelector.ts +17 -4
- package/front_end/entrypoints/main/MainImpl.ts +8 -0
- package/front_end/generated/InspectorBackendCommands.ts +4 -6
- package/front_end/generated/protocol-mapping.d.ts +0 -8
- package/front_end/generated/protocol-proxy-api.d.ts +0 -6
- package/front_end/generated/protocol.ts +4 -25
- package/front_end/models/ai_assistance/AiAgent2.ts +13 -1
- package/front_end/models/ai_assistance/AiConversation.ts +1 -1
- package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +13 -19
- package/front_end/models/ai_assistance/debug.ts +10 -2
- package/front_end/models/change_tracker/ChangeTracker.ts +5 -64
- package/front_end/models/comment_manager/CD4ABridge.ts +2 -2
- package/front_end/models/comment_manager/CommentManager.ts +6 -5
- package/front_end/models/comment_manager/CommentThread.ts +30 -13
- package/front_end/models/formatter/FormatterWorkerPool.ts +2 -2
- package/front_end/models/issues_manager/CookieDeprecationMetadataIssue.ts +6 -7
- package/front_end/models/issues_manager/DeprecationIssue.ts +1 -1
- package/front_end/models/issues_manager/MarkdownIssueDescription.ts +14 -5
- package/front_end/models/issues_manager/SRIMessageSignatureIssue.ts +2 -4
- package/front_end/models/issues_manager/descriptions/cookieWarnMetadataGrantRead.md +0 -1
- package/front_end/models/issues_manager/descriptions/cookieWarnMetadataGrantReadOptOut.md +5 -0
- package/front_end/models/issues_manager/descriptions/cookieWarnMetadataGrantSet.md +0 -1
- package/front_end/models/issues_manager/descriptions/cookieWarnMetadataGrantSetOptOut.md +5 -0
- package/front_end/models/issues_manager/descriptions/deprecation.md +1 -1
- package/front_end/models/issues_manager/descriptions/sriValidationFailedIntegrityMismatch.md +2 -0
- package/front_end/models/javascript_metadata/NativeFunctions.ts +7 -2
- package/front_end/models/persistence/NetworkPersistenceManager.ts +14 -2
- package/front_end/models/source_map_scopes/NamesResolver.ts +42 -39
- package/front_end/panels/accessibility/AccessibilityAnnouncementRecordingView.ts +70 -1
- package/front_end/panels/ai_assistance/components/ChatMessage.ts +1 -1
- package/front_end/panels/common/CommentThreadWidget.ts +29 -10
- package/front_end/panels/common/CommentsOverlayWidget.ts +77 -14
- package/front_end/panels/common/ExtensionServer.ts +18 -8
- package/front_end/panels/common/commentThreadWidget.css +11 -0
- package/front_end/panels/console/ConsolePrompt.ts +1 -1
- package/front_end/panels/console/ConsoleViewMessage.ts +1 -1
- package/front_end/panels/elements/ClassesPaneWidget.ts +2 -1
- package/front_end/panels/elements/DOMTreeContextMenu.ts +1 -1
- package/front_end/panels/elements/{ElementsTreeOutline.ts → DOMTreeWidget.ts} +34 -24
- package/front_end/panels/elements/ElementsPanel.ts +1 -1
- package/front_end/panels/elements/ElementsTreeElement.ts +1 -1
- package/front_end/panels/elements/ShortcutTreeElement.ts +1 -1
- package/front_end/panels/elements/StylePropertiesSection.ts +10 -0
- package/front_end/panels/elements/StylesSidebarPane.ts +105 -55
- package/front_end/panels/elements/TopLayerContainer.ts +1 -1
- package/front_end/panels/elements/components/CSSQuery.ts +1 -1
- package/front_end/panels/elements/components/cssQuery.css +4 -0
- package/front_end/panels/elements/elements.ts +3 -3
- package/front_end/panels/elements/stylesSidebarPane.css +6 -0
- package/front_end/panels/profiler/HeapDetachedElementsDataGrid.ts +1 -1
- package/front_end/panels/profiler/HeapProfileView.ts +8 -6
- package/front_end/panels/profiler/HeapSnapshotDataGrids.ts +8 -1
- package/front_end/panels/profiler/HeapSnapshotView.ts +6 -4
- package/front_end/panels/security/SecurityPanel.ts +87 -99
- package/front_end/panels/sensors/SensorsView.ts +87 -0
- package/front_end/panels/sensors/sensors-meta.ts +69 -1
- package/front_end/panels/settings/keybindsSettingsTab.css +1 -1
- package/front_end/panels/sources/ResourceOriginPlugin.ts +8 -3
- package/front_end/panels/sources/ScopeChainSidebarPane.ts +3 -4
- package/front_end/panels/sources/TabbedEditorContainer.ts +339 -126
- package/front_end/panels/sources/UISourceCodeFrame.ts +9 -0
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/third_party/chromium/client-variations/ClientVariations.js +25 -96
- package/front_end/third_party/codemirror.next/chunk/angular.js +1 -1
- package/front_end/third_party/codemirror.next/chunk/codemirror.js +1 -1
- package/front_end/third_party/codemirror.next/chunk/codemirror.js.map +1 -1
- package/front_end/third_party/codemirror.next/chunk/cpp.js +1 -1
- package/front_end/third_party/codemirror.next/chunk/cpp.js.map +1 -1
- package/front_end/third_party/codemirror.next/chunk/java.js +1 -1
- package/front_end/third_party/codemirror.next/chunk/java.js.map +1 -1
- package/front_end/third_party/codemirror.next/chunk/legacy.js +1 -1
- package/front_end/third_party/codemirror.next/chunk/less.js +1 -1
- package/front_end/third_party/codemirror.next/chunk/less.js.map +1 -1
- package/front_end/third_party/codemirror.next/chunk/markdown.js +1 -1
- package/front_end/third_party/codemirror.next/chunk/markdown.js.map +1 -1
- package/front_end/third_party/codemirror.next/chunk/php.js +1 -1
- package/front_end/third_party/codemirror.next/chunk/php.js.map +1 -1
- package/front_end/third_party/codemirror.next/chunk/python.js +1 -1
- package/front_end/third_party/codemirror.next/chunk/python.js.map +1 -1
- package/front_end/third_party/codemirror.next/chunk/sass.js +1 -1
- package/front_end/third_party/codemirror.next/chunk/sass.js.map +1 -1
- package/front_end/third_party/codemirror.next/chunk/svelte.js +1 -1
- package/front_end/third_party/codemirror.next/chunk/svelte.js.map +1 -1
- package/front_end/third_party/codemirror.next/chunk/vue.js +1 -1
- package/front_end/third_party/codemirror.next/chunk/wast.js +1 -1
- package/front_end/third_party/codemirror.next/chunk/wast.js.map +1 -1
- package/front_end/third_party/codemirror.next/chunk/xml.js +1 -1
- package/front_end/third_party/codemirror.next/chunk/xml.js.map +1 -1
- package/front_end/third_party/codemirror.next/codemirror.next.js +1 -1
- package/front_end/third_party/codemirror.next/package.json +4 -1
- package/front_end/third_party/codemirror.next/rebuild.sh +1 -1
- package/front_end/ui/comments/CommentOverlayManager.ts +11 -10
- package/front_end/ui/components/markdown_view/MarkdownPlaceholderLitRenderer.ts +117 -0
- package/front_end/ui/components/markdown_view/markdown_view.ts +2 -0
- package/front_end/ui/legacy/Treeoutline.ts +30 -20
- package/front_end/ui/legacy/UIUtils.ts +1 -1
- package/front_end/ui/legacy/components/data_grid/DataGridElement.ts +1 -1
- package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +228 -343
- package/front_end/ui/legacy/components/object_ui/objectPropertiesSection.css +2 -27
- package/front_end/ui/legacy/treeoutline.css +2 -2
- package/front_end/ui/visual_logging/KnownContextValues.ts +7 -0
- package/mcp/mcp.ts +1 -0
- 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/
|
|
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 =
|
|
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
|
|
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 || (
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
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});
|
|
@@ -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
|
-
|
|
223
|
-
|
|
224
|
-
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
16
|
-
|
|
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,
|
|
12
|
-
const replacements = computeSubstitution(expression,
|
|
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,
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
|
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:
|
|
134
|
+
to: parsed.replacement,
|
|
76
135
|
offset: use.offset,
|
|
77
136
|
isShorthandAssignmentProperty: use.isShorthandAssignmentProperty,
|
|
78
137
|
});
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
|
-
|
|
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)
|
|
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);
|