chrome-devtools-frontend 1.0.1697595 → 1.0.1697998
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/.stylelintignore +2 -0
- package/front_end/core/common/VersionController.ts +5 -1
- package/front_end/core/sdk/AnimationModel.ts +9 -1
- package/front_end/generated/InspectorBackendCommands.ts +1 -1
- package/front_end/generated/protocol.ts +8 -2
- package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +26 -0
- package/front_end/models/ai_assistance/ai_assistance.ts +4 -4
- package/front_end/models/ai_assistance/skills/performance.md +4 -3
- package/front_end/models/ai_assistance/tools/{GetFunctionCode.ts → GetTraceFunctionCode.ts} +26 -13
- package/front_end/models/ai_assistance/tools/{GetResourceContent.ts → GetTraceResourceContent.ts} +31 -11
- package/front_end/models/ai_assistance/tools/Tool.ts +2 -2
- package/front_end/models/ai_assistance/tools/ToolRegistry.ts +6 -6
- package/front_end/models/elements/DOMChanges.ts +238 -0
- package/front_end/models/elements/elements.ts +2 -0
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +3 -1
- package/front_end/panels/ai_assistance/components/MarkdownRendererWithCodeBlock.ts +25 -5
- package/front_end/panels/browser_debugger/ObjectEventListenersSidebarPane.ts +2 -3
- package/front_end/panels/elements/ElementsTreeElement.ts +98 -20
- package/front_end/panels/elements/ElementsTreeOutline.ts +271 -43
- package/front_end/panels/elements/EventListenersWidget.ts +0 -1
- package/front_end/panels/event_listeners/EventListenersView.ts +241 -297
- package/front_end/panels/linear_memory_inspector/LinearMemoryInspectorController.ts +1 -1
- package/front_end/panels/mobile_throttling/ThrottlingSettingsTab.ts +10 -2
- package/front_end/panels/network/NetworkConfigView.ts +141 -116
- package/front_end/panels/network/NetworkPanel.ts +7 -4
- package/front_end/panels/network/RequestHeadersView.ts +1 -1
- package/front_end/panels/network/forward/BackendLinking.ts +5 -2
- package/front_end/panels/settings/BackendLinkingSettingsTab.ts +7 -2
- package/front_end/panels/timeline/TimelineTreeView.ts +11 -1
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/ui/comments/CommentOverlayManager.ts +169 -40
- package/front_end/ui/comments/CommentThreadWidget.ts +221 -0
- package/front_end/ui/comments/CommentsOverlayWidget.ts +124 -12
- package/front_end/ui/comments/commentThreadWidget.css +173 -0
- package/front_end/ui/comments/comments.ts +2 -0
- package/front_end/ui/comments/commentsOverlay.css +6 -0
- package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +1 -1
- package/front_end/ui/legacy/components/perf_ui/FlameChart.ts +4 -0
- package/package.json +5 -5
package/.stylelintignore
CHANGED
|
@@ -17,7 +17,7 @@ export class VersionController {
|
|
|
17
17
|
static readonly SYNCED_VERSION_SETTING_NAME = 'syncedInspectorVersion';
|
|
18
18
|
static readonly LOCAL_VERSION_SETTING_NAME = 'localInspectorVersion';
|
|
19
19
|
|
|
20
|
-
static readonly CURRENT_VERSION =
|
|
20
|
+
static readonly CURRENT_VERSION = 47;
|
|
21
21
|
|
|
22
22
|
readonly #settings: Settings;
|
|
23
23
|
readonly #globalVersionSetting: Setting<number>;
|
|
@@ -897,6 +897,10 @@ export class VersionController {
|
|
|
897
897
|
}
|
|
898
898
|
}
|
|
899
899
|
|
|
900
|
+
updateVersionFrom46To47(): void {
|
|
901
|
+
this.#settings.syncedStorage.remove('network.backend-linking-rules');
|
|
902
|
+
}
|
|
903
|
+
|
|
900
904
|
/*
|
|
901
905
|
* Any new migration should be added before this comment.
|
|
902
906
|
*
|
|
@@ -920,7 +920,15 @@ export class AnimationGroup {
|
|
|
920
920
|
}
|
|
921
921
|
|
|
922
922
|
seekTo(currentTime: number): void {
|
|
923
|
-
|
|
923
|
+
const animations = [];
|
|
924
|
+
const currentTimes = [];
|
|
925
|
+
for (const animation of this.#animations) {
|
|
926
|
+
// Seek from the end of the animation if it is reversed.
|
|
927
|
+
animations.push(animation.id());
|
|
928
|
+
currentTimes.push(animation.playbackRate() >= 0 ? currentTime :
|
|
929
|
+
animation.endTime() - animation.startTime() - currentTime);
|
|
930
|
+
}
|
|
931
|
+
void this.#animationModel.agent.invoke_seekAnimations({animations, currentTimes});
|
|
924
932
|
}
|
|
925
933
|
|
|
926
934
|
paused(): boolean {
|
|
@@ -62,7 +62,7 @@ inspectorBackend.registerCommand("Animation.getCurrentTime", [{"name": "id", "ty
|
|
|
62
62
|
inspectorBackend.registerCommand("Animation.getPlaybackRate", [], ["playbackRate"], "Gets the playback rate of the document timeline.");
|
|
63
63
|
inspectorBackend.registerCommand("Animation.releaseAnimations", [{"name": "animations", "type": "array", "optional": false, "description": "List of animation ids to seek.", "typeRef": "string"}], [], "Releases a set of animations to no longer be manipulated.");
|
|
64
64
|
inspectorBackend.registerCommand("Animation.resolveAnimation", [{"name": "animationId", "type": "string", "optional": false, "description": "Animation id.", "typeRef": null}], ["remoteObject"], "Gets the remote object of the Animation.");
|
|
65
|
-
inspectorBackend.registerCommand("Animation.seekAnimations", [{"name": "animations", "type": "array", "optional": false, "description": "List of animation ids to seek.", "typeRef": "string"}, {"name": "currentTime", "type": "number", "optional":
|
|
65
|
+
inspectorBackend.registerCommand("Animation.seekAnimations", [{"name": "animations", "type": "array", "optional": false, "description": "List of animation ids to seek.", "typeRef": "string"}, {"name": "currentTime", "type": "number", "optional": true, "description": "Set each animation to the same time.", "typeRef": null}, {"name": "currentTimes", "type": "array", "optional": true, "description": "Set each animation to a different time. If set, should have the same length as animations. Exactly one of currentTime or currentTimes should be set.", "typeRef": "number"}], [], "Seek a set of animations to a particular time within each animation.");
|
|
66
66
|
inspectorBackend.registerCommand("Animation.setPaused", [{"name": "animations", "type": "array", "optional": false, "description": "Animations to set the pause state of.", "typeRef": "string"}, {"name": "paused", "type": "boolean", "optional": false, "description": "Paused state to set to.", "typeRef": null}], [], "Sets the paused state of a set of animations.");
|
|
67
67
|
inspectorBackend.registerCommand("Animation.setPlaybackRate", [{"name": "playbackRate", "type": "number", "optional": false, "description": "Playback rate for animations on page", "typeRef": null}], [], "Sets the playback rate of the document timeline.");
|
|
68
68
|
inspectorBackend.registerCommand("Animation.setTiming", [{"name": "animationId", "type": "string", "optional": false, "description": "Animation id.", "typeRef": null}, {"name": "duration", "type": "number", "optional": false, "description": "Duration of the animation.", "typeRef": null}, {"name": "delay", "type": "number", "optional": false, "description": "Delay of the animation.", "typeRef": null}], [], "Sets the timing of an animation node.");
|
|
@@ -749,9 +749,15 @@ export namespace Animation {
|
|
|
749
749
|
*/
|
|
750
750
|
animations: string[];
|
|
751
751
|
/**
|
|
752
|
-
* Set
|
|
752
|
+
* Set each animation to the same time.
|
|
753
753
|
*/
|
|
754
|
-
currentTime
|
|
754
|
+
currentTime?: number;
|
|
755
|
+
/**
|
|
756
|
+
* Set each animation to a different time. If set, should have the same
|
|
757
|
+
* length as animations. Exactly one of currentTime or currentTimes should
|
|
758
|
+
* be set.
|
|
759
|
+
*/
|
|
760
|
+
currentTimes?: number[];
|
|
755
761
|
}
|
|
756
762
|
|
|
757
763
|
export interface SetPausedRequest {
|
|
@@ -20,6 +20,7 @@ import {StorageContext} from '../contexts/StorageContext.js';
|
|
|
20
20
|
import {formatBytesToKb, seconds} from '../data_formatters/UnitFormatters.js';
|
|
21
21
|
import {debugLog} from '../debug.js';
|
|
22
22
|
import {StorageItem} from '../StorageItem.js';
|
|
23
|
+
import {isOriginAllowedByLock} from '../tools/Tool.js';
|
|
23
24
|
|
|
24
25
|
import {
|
|
25
26
|
type AgentOptions,
|
|
@@ -587,4 +588,29 @@ export class ContextSelectionAgent extends AiAgent<never> {
|
|
|
587
588
|
|
|
588
589
|
return [...uiSourceCodes.values()];
|
|
589
590
|
}
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* Resolves a workspace source file by its ID, ensuring that the file's security
|
|
594
|
+
* origin is authorized by the conversation's established origin lock.
|
|
595
|
+
*
|
|
596
|
+
* Fails closed by returning `undefined` if the established origin is missing or opaque,
|
|
597
|
+
* if the file ID is invalid, or if the file origin does not match the lock.
|
|
598
|
+
*/
|
|
599
|
+
static getSourceById(
|
|
600
|
+
id: number,
|
|
601
|
+
establishedOrigin?: SDK.SecurityOrigin.SecurityOrigin,
|
|
602
|
+
// eslint-disable-next-line @devtools/no-instance-of-migrated-singletons
|
|
603
|
+
workspace: Workspace.Workspace.WorkspaceImpl = Workspace.Workspace.WorkspaceImpl.instance(),
|
|
604
|
+
): Workspace.UISourceCode.UISourceCode|undefined {
|
|
605
|
+
if (!establishedOrigin || !Number.isInteger(id) || id <= 0) {
|
|
606
|
+
return undefined;
|
|
607
|
+
}
|
|
608
|
+
return ContextSelectionAgent.getUISourceCodes(workspace).find(file => {
|
|
609
|
+
if (ContextSelectionAgent.uiSourceCodeId.get(file) !== id) {
|
|
610
|
+
return false;
|
|
611
|
+
}
|
|
612
|
+
return isOriginAllowedByLock({status: 'ESTABLISHED_ORIGIN', origin: establishedOrigin},
|
|
613
|
+
FileContext.originForUISourceCode(file));
|
|
614
|
+
});
|
|
615
|
+
}
|
|
590
616
|
}
|
|
@@ -46,18 +46,18 @@ import * as ExecuteJavaScript from './tools/ExecuteJavaScript.js';
|
|
|
46
46
|
import * as GetCookieValues from './tools/GetCookieValues.js';
|
|
47
47
|
import * as GetDetailedCallTree from './tools/GetDetailedCallTree.js';
|
|
48
48
|
import * as GetElementAccessibilityDetails from './tools/GetElementAccessibilityDetails.js';
|
|
49
|
-
import * as GetFunctionCode from './tools/GetFunctionCode.js';
|
|
50
49
|
import * as GetInsightDetails from './tools/GetInsightDetails.js';
|
|
51
50
|
import * as GetLighthouseAudits from './tools/GetLighthouseAudits.js';
|
|
52
51
|
import * as GetNetworkRequestDetails from './tools/GetNetworkRequestDetails.js';
|
|
53
|
-
import * as GetResourceContent from './tools/GetResourceContent.js';
|
|
54
52
|
import * as GetSourceContent from './tools/GetSourceContent.js';
|
|
55
53
|
import * as GetStorageBreakdown from './tools/GetStorageBreakdown.js';
|
|
56
54
|
import * as GetStorageValues from './tools/GetStorageValues.js';
|
|
57
55
|
import * as GetStyles from './tools/GetStyles.js';
|
|
58
56
|
import * as GetTraceEventByKey from './tools/GetTraceEventByKey.js';
|
|
57
|
+
import * as GetTraceFunctionCode from './tools/GetTraceFunctionCode.js';
|
|
59
58
|
import * as GetTraceMainThreadSummary from './tools/GetTraceMainThreadSummary.js';
|
|
60
59
|
import * as GetTraceNetworkSummary from './tools/GetTraceNetworkSummary.js';
|
|
60
|
+
import * as GetTraceResourceContent from './tools/GetTraceResourceContent.js';
|
|
61
61
|
import * as ListCookies from './tools/ListCookies.js';
|
|
62
62
|
import * as ListNetworkRequests from './tools/ListNetworkRequests.js';
|
|
63
63
|
import * as ListPageOrigins from './tools/ListPageOrigins.js';
|
|
@@ -99,18 +99,18 @@ export {
|
|
|
99
99
|
GetCookieValues,
|
|
100
100
|
GetDetailedCallTree,
|
|
101
101
|
GetElementAccessibilityDetails,
|
|
102
|
-
GetFunctionCode,
|
|
103
102
|
GetInsightDetails,
|
|
104
103
|
GetLighthouseAudits,
|
|
105
104
|
GetNetworkRequestDetails,
|
|
106
|
-
GetResourceContent,
|
|
107
105
|
GetSourceContent,
|
|
108
106
|
GetStorageBreakdown,
|
|
109
107
|
GetStorageValues,
|
|
110
108
|
GetStyles,
|
|
111
109
|
GetTraceEventByKey,
|
|
110
|
+
GetTraceFunctionCode,
|
|
112
111
|
GetTraceMainThreadSummary,
|
|
113
112
|
GetTraceNetworkSummary,
|
|
113
|
+
GetTraceResourceContent,
|
|
114
114
|
Injected,
|
|
115
115
|
LighthouseFormatter,
|
|
116
116
|
ListCookies,
|
|
@@ -8,8 +8,8 @@ allowed-tools:
|
|
|
8
8
|
- getTraceMainThreadSummary
|
|
9
9
|
- getTraceNetworkSummary
|
|
10
10
|
- getDetailedCallTree
|
|
11
|
-
-
|
|
12
|
-
-
|
|
11
|
+
- getTraceFunctionCode
|
|
12
|
+
- getTraceResourceContent
|
|
13
13
|
- getInsightDetails
|
|
14
14
|
---
|
|
15
15
|
You are an expert web performance assistant integrated into Chrome DevTools.
|
|
@@ -29,7 +29,8 @@ Your goal is to provide actionable advice about web page performance by analyzin
|
|
|
29
29
|
3. **Call Tree, Event & Source Inspection**:
|
|
30
30
|
- Use `getDetailedCallTree` to retrieve bottom-up execution trees for expensive main thread tasks.
|
|
31
31
|
- Use `getTraceEventByKey` to inspect timing and payload data for individual events.
|
|
32
|
-
- Use `
|
|
32
|
+
- Use `getTraceFunctionCode` to inspect a specific function identified in the call tree, including line-by-line CPU profile execution costs.
|
|
33
|
+
- Use `getTraceResourceContent` to inspect the full text of a script or resource captured within the trace by URL.
|
|
33
34
|
|
|
34
35
|
4. **UI Selection & Trace Recording**:
|
|
35
36
|
- Use `selectTraceEventByKey` to reveal and select an event in the Performance Flamechart if requested.
|
|
@@ -21,54 +21,67 @@ const UIStringsNotTranslate = {
|
|
|
21
21
|
|
|
22
22
|
const lockedString = i18n.i18n.lockedString;
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Arguments for {@link GetTraceFunctionCodeTool}.
|
|
26
|
+
*/
|
|
27
|
+
export interface GetTraceFunctionCodeArgs extends ToolArgs {
|
|
28
|
+
/** The URL of the script containing the function recorded in the performance trace. */
|
|
25
29
|
scriptUrl: string;
|
|
30
|
+
/** The line number where the function is defined (0-based, as reported in the call tree). */
|
|
26
31
|
line: number;
|
|
32
|
+
/** The column number where the function is defined (0-based, as reported in the call tree). */
|
|
27
33
|
column: number;
|
|
28
34
|
}
|
|
29
35
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
36
|
+
/**
|
|
37
|
+
* Retrieves function code and line-by-line CPU profile execution costs from the performance trace.
|
|
38
|
+
*
|
|
39
|
+
* Preconditions:
|
|
40
|
+
* - Requires an active, freshly recorded trace session (fails on imported traces).
|
|
41
|
+
* - Requires resource access via the performance trace context.
|
|
42
|
+
*/
|
|
43
|
+
export class GetTraceFunctionCodeTool implements
|
|
44
|
+
DataTool<GetTraceFunctionCodeArgs, string, BaseToolCapability&PerformanceTraceCapability> {
|
|
45
|
+
readonly name: ToolName = ToolName.GET_TRACE_FUNCTION_CODE;
|
|
33
46
|
readonly description: string =
|
|
34
|
-
'Retrieves the code for a function
|
|
47
|
+
'Retrieves the code for a function recorded in the performance trace at the specified location, annotated with line-by-line CPU runtime profiling execution costs. Do not call this tool unless a performance trace recording is actively loaded.';
|
|
35
48
|
|
|
36
|
-
readonly parameters: Host.AidaClient.FunctionObjectParam<keyof
|
|
49
|
+
readonly parameters: Host.AidaClient.FunctionObjectParam<keyof GetTraceFunctionCodeArgs> = {
|
|
37
50
|
type: Host.AidaClient.ParametersTypes.OBJECT,
|
|
38
|
-
description: 'Arguments for looking up function code.',
|
|
51
|
+
description: 'Arguments for looking up function code from the performance trace profile.',
|
|
39
52
|
nullable: false,
|
|
40
53
|
properties: {
|
|
41
54
|
scriptUrl: {
|
|
42
55
|
type: Host.AidaClient.ParametersTypes.STRING,
|
|
43
|
-
description: 'The
|
|
56
|
+
description: 'The URL of the script containing the function recorded in the performance trace.',
|
|
44
57
|
nullable: false,
|
|
45
58
|
},
|
|
46
59
|
line: {
|
|
47
60
|
type: Host.AidaClient.ParametersTypes.INTEGER,
|
|
48
|
-
description: 'The line number where the function is defined.',
|
|
61
|
+
description: 'The line number where the function is defined (0-based, as reported in the call tree).',
|
|
49
62
|
nullable: false,
|
|
50
63
|
},
|
|
51
64
|
column: {
|
|
52
65
|
type: Host.AidaClient.ParametersTypes.INTEGER,
|
|
53
|
-
description: 'The column number where the function is defined.',
|
|
66
|
+
description: 'The column number where the function is defined (0-based, as reported in the call tree).',
|
|
54
67
|
nullable: false,
|
|
55
68
|
},
|
|
56
69
|
},
|
|
57
70
|
required: ['scriptUrl', 'line', 'column'],
|
|
58
71
|
};
|
|
59
72
|
|
|
60
|
-
displayInfoFromArgs(params:
|
|
73
|
+
displayInfoFromArgs(params: GetTraceFunctionCodeArgs): {
|
|
61
74
|
title: string,
|
|
62
75
|
action: string,
|
|
63
76
|
} {
|
|
64
77
|
return {
|
|
65
78
|
title: lockedString(UIStringsNotTranslate.lookingUpFunctionCode),
|
|
66
|
-
action: `
|
|
79
|
+
action: `getTraceFunctionCode('${params.scriptUrl}', ${params.line}, ${params.column})`,
|
|
67
80
|
};
|
|
68
81
|
}
|
|
69
82
|
|
|
70
83
|
async handler(
|
|
71
|
-
params:
|
|
84
|
+
params: GetTraceFunctionCodeArgs,
|
|
72
85
|
capabilities: BaseToolCapability&PerformanceTraceCapability,
|
|
73
86
|
): Promise<DataHandlerResult<string>> {
|
|
74
87
|
const performanceTraceContext = capabilities.getPerformanceTraceContext();
|
package/front_end/models/ai_assistance/tools/{GetResourceContent.ts → GetTraceResourceContent.ts}
RENAMED
|
@@ -25,42 +25,57 @@ const UIStringsNotTranslate = {
|
|
|
25
25
|
|
|
26
26
|
const lockedString = i18n.i18n.lockedString;
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
/**
|
|
29
|
+
* Arguments for {@link GetTraceResourceContentTool}.
|
|
30
|
+
*/
|
|
31
|
+
export interface GetTraceResourceContentArgs extends ToolArgs {
|
|
32
|
+
/** The URL of the resource captured in the performance trace to retrieve. */
|
|
29
33
|
url: string;
|
|
30
34
|
}
|
|
31
35
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
36
|
+
/**
|
|
37
|
+
* Retrieves text content for a resource or script captured in the performance trace.
|
|
38
|
+
*
|
|
39
|
+
* Precedence:
|
|
40
|
+
* 1. Checks trace metadata (`parsedTrace.data.Scripts`) for scripts captured during recording.
|
|
41
|
+
* 2. Falls back to querying the live page target's `ResourceTreeModel`.
|
|
42
|
+
*
|
|
43
|
+
* Preconditions:
|
|
44
|
+
* - Requires an active, freshly recorded trace session (fails on imported traces).
|
|
45
|
+
* - Fails if resource is binary/non-text, cross-origin, or a `file://` URL.
|
|
46
|
+
*/
|
|
47
|
+
export class GetTraceResourceContentTool implements DataTool<
|
|
48
|
+
GetTraceResourceContentArgs, {content: string}, BaseToolCapability&TargetCapability&PerformanceTraceCapability> {
|
|
49
|
+
readonly name: ToolName = ToolName.GET_TRACE_RESOURCE_CONTENT;
|
|
35
50
|
readonly description: string =
|
|
36
|
-
'Retrieves the content of
|
|
51
|
+
'Retrieves the text content of a script or resource captured within the recorded performance trace by URL. Only use this for text resource types. Do not call this tool on imported traces or for general workspace files (use listSources and getSourceContent instead).';
|
|
37
52
|
|
|
38
|
-
readonly parameters: Host.AidaClient.FunctionObjectParam<keyof
|
|
53
|
+
readonly parameters: Host.AidaClient.FunctionObjectParam<keyof GetTraceResourceContentArgs> = {
|
|
39
54
|
type: Host.AidaClient.ParametersTypes.OBJECT,
|
|
40
|
-
description: 'Arguments for looking up resource content.',
|
|
55
|
+
description: 'Arguments for looking up resource content from the performance trace.',
|
|
41
56
|
nullable: false,
|
|
42
57
|
properties: {
|
|
43
58
|
url: {
|
|
44
59
|
type: Host.AidaClient.ParametersTypes.STRING,
|
|
45
|
-
description: 'The
|
|
60
|
+
description: 'The URL of the resource captured in the performance trace to retrieve.',
|
|
46
61
|
nullable: false,
|
|
47
62
|
},
|
|
48
63
|
},
|
|
49
64
|
required: ['url'],
|
|
50
65
|
};
|
|
51
66
|
|
|
52
|
-
displayInfoFromArgs(params:
|
|
67
|
+
displayInfoFromArgs(params: GetTraceResourceContentArgs): {
|
|
53
68
|
title: string,
|
|
54
69
|
action: string,
|
|
55
70
|
} {
|
|
56
71
|
return {
|
|
57
72
|
title: lockedString(UIStringsNotTranslate.lookingAtResourceContent),
|
|
58
|
-
action: `
|
|
73
|
+
action: `getTraceResourceContent('${params.url}')`,
|
|
59
74
|
};
|
|
60
75
|
}
|
|
61
76
|
|
|
62
77
|
async handler(
|
|
63
|
-
params:
|
|
78
|
+
params: GetTraceResourceContentArgs,
|
|
64
79
|
capabilities: BaseToolCapability&TargetCapability&PerformanceTraceCapability,
|
|
65
80
|
): Promise<DataHandlerResult<{content: string}>> {
|
|
66
81
|
const performanceTraceContext = capabilities.getPerformanceTraceContext();
|
|
@@ -72,6 +87,10 @@ export class GetResourceContentTool implements DataTool<
|
|
|
72
87
|
return {error: 'Cannot use this tool on an imported file.'};
|
|
73
88
|
}
|
|
74
89
|
|
|
90
|
+
if (!params.url) {
|
|
91
|
+
return {error: 'Missing arg: url'};
|
|
92
|
+
}
|
|
93
|
+
|
|
75
94
|
if (!performanceTraceContext.canAccessResource(params.url)) {
|
|
76
95
|
return {error: 'Resource not found'};
|
|
77
96
|
}
|
|
@@ -82,6 +101,7 @@ export class GetResourceContentTool implements DataTool<
|
|
|
82
101
|
let content: string;
|
|
83
102
|
|
|
84
103
|
const url = params.url as Platform.DevToolsPath.UrlString;
|
|
104
|
+
// Check trace metadata for scripts captured during recording before falling back to live page resources.
|
|
85
105
|
const script = parsedTrace.data.Scripts?.scripts.find(script => script.url === params.url);
|
|
86
106
|
if (script?.content !== undefined) {
|
|
87
107
|
content = script.content;
|
|
@@ -258,8 +258,8 @@ export const enum ToolName {
|
|
|
258
258
|
GET_TRACE_NETWORK_SUMMARY = 'getTraceNetworkSummary',
|
|
259
259
|
RUN_LIGHTHOUSE = 'runLighthouse',
|
|
260
260
|
GET_DETAILED_CALL_TREE = 'getDetailedCallTree',
|
|
261
|
-
|
|
262
|
-
|
|
261
|
+
GET_TRACE_FUNCTION_CODE = 'getTraceFunctionCode',
|
|
262
|
+
GET_TRACE_RESOURCE_CONTENT = 'getTraceResourceContent',
|
|
263
263
|
GET_INSIGHT_DETAILS = 'getInsightDetails',
|
|
264
264
|
GET_STORAGE_BREAKDOWN = 'getStorageBreakdown',
|
|
265
265
|
}
|
|
@@ -6,18 +6,18 @@ import {ExecuteJavaScriptTool} from './ExecuteJavaScript.js';
|
|
|
6
6
|
import {GetCookieValuesTool} from './GetCookieValues.js';
|
|
7
7
|
import {GetDetailedCallTreeTool} from './GetDetailedCallTree.js';
|
|
8
8
|
import {GetElementAccessibilityDetailsTool} from './GetElementAccessibilityDetails.js';
|
|
9
|
-
import {GetFunctionCodeTool} from './GetFunctionCode.js';
|
|
10
9
|
import {GetInsightDetailsTool} from './GetInsightDetails.js';
|
|
11
10
|
import {GetLighthouseAuditsTool} from './GetLighthouseAudits.js';
|
|
12
11
|
import {GetNetworkRequestDetailsTool} from './GetNetworkRequestDetails.js';
|
|
13
|
-
import {GetResourceContentTool} from './GetResourceContent.js';
|
|
14
12
|
import {GetSourceContentTool} from './GetSourceContent.js';
|
|
15
13
|
import {GetStorageBreakdownTool} from './GetStorageBreakdown.js';
|
|
16
14
|
import {GetStorageValuesTool} from './GetStorageValues.js';
|
|
17
15
|
import {GetStylesTool} from './GetStyles.js';
|
|
18
16
|
import {GetTraceEventByKeyTool} from './GetTraceEventByKey.js';
|
|
17
|
+
import {GetTraceFunctionCodeTool} from './GetTraceFunctionCode.js';
|
|
19
18
|
import {GetTraceMainThreadSummaryTool} from './GetTraceMainThreadSummary.js';
|
|
20
19
|
import {GetTraceNetworkSummaryTool} from './GetTraceNetworkSummary.js';
|
|
20
|
+
import {GetTraceResourceContentTool} from './GetTraceResourceContent.js';
|
|
21
21
|
import {ListCookiesTool} from './ListCookies.js';
|
|
22
22
|
import {ListNetworkRequestsTool} from './ListNetworkRequests.js';
|
|
23
23
|
import {ListPageOriginsTool} from './ListPageOrigins.js';
|
|
@@ -51,8 +51,8 @@ export interface BaseToolsRegistryMap {
|
|
|
51
51
|
[ToolName.GET_TRACE_NETWORK_SUMMARY]: GetTraceNetworkSummaryTool;
|
|
52
52
|
[ToolName.RUN_LIGHTHOUSE]: RunLighthouseTool;
|
|
53
53
|
[ToolName.GET_DETAILED_CALL_TREE]: GetDetailedCallTreeTool;
|
|
54
|
-
[ToolName.
|
|
55
|
-
[ToolName.
|
|
54
|
+
[ToolName.GET_TRACE_FUNCTION_CODE]: GetTraceFunctionCodeTool;
|
|
55
|
+
[ToolName.GET_TRACE_RESOURCE_CONTENT]: GetTraceResourceContentTool;
|
|
56
56
|
[ToolName.GET_INSIGHT_DETAILS]: GetInsightDetailsTool;
|
|
57
57
|
[ToolName.GET_STORAGE_BREAKDOWN]: GetStorageBreakdownTool;
|
|
58
58
|
}
|
|
@@ -86,8 +86,8 @@ export const TOOLS: BaseToolsRegistryMap = {
|
|
|
86
86
|
[ToolName.GET_TRACE_NETWORK_SUMMARY]: new GetTraceNetworkSummaryTool(),
|
|
87
87
|
[ToolName.RUN_LIGHTHOUSE]: new RunLighthouseTool(),
|
|
88
88
|
[ToolName.GET_DETAILED_CALL_TREE]: new GetDetailedCallTreeTool(),
|
|
89
|
-
[ToolName.
|
|
90
|
-
[ToolName.
|
|
89
|
+
[ToolName.GET_TRACE_FUNCTION_CODE]: new GetTraceFunctionCodeTool(),
|
|
90
|
+
[ToolName.GET_TRACE_RESOURCE_CONTENT]: new GetTraceResourceContentTool(),
|
|
91
91
|
[ToolName.GET_INSIGHT_DETAILS]: new GetInsightDetailsTool(),
|
|
92
92
|
[ToolName.GET_STORAGE_BREAKDOWN]: new GetStorageBreakdownTool(),
|
|
93
93
|
};
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
// Copyright 2026 The Chromium Authors
|
|
2
|
+
// Use of this source code is governed by a BSD-style license that can be
|
|
3
|
+
// found in the LICENSE file.
|
|
4
|
+
|
|
5
|
+
import * as Platform from '../../core/platform/platform.js';
|
|
6
|
+
import type * as SDK from '../../core/sdk/sdk.js';
|
|
7
|
+
import type * as ChangeTracker from '../change_tracker/change_tracker.js';
|
|
8
|
+
import type * as CommentManager from '../comment_manager/comment_manager.js';
|
|
9
|
+
|
|
10
|
+
type Tracker = ChangeTracker.ChangeTracker.ChangeTracker|undefined;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Maximum number of characters of page controlled data (tag names, attribute names and values,
|
|
14
|
+
* text node values and HTML) that a change description embeds. The inspected page fully controls
|
|
15
|
+
* these strings and `Changed HTML from ... to ...` in particular can otherwise hold on to the
|
|
16
|
+
* entire subtree of an element.
|
|
17
|
+
*/
|
|
18
|
+
export const MAX_VALUE_LENGTH = 100;
|
|
19
|
+
|
|
20
|
+
/** Shortens page controlled data so that both its start and its end stay recognizable. */
|
|
21
|
+
function trimValue(value: string): string {
|
|
22
|
+
return Platform.StringUtilities.trimMiddle(value, MAX_VALUE_LENGTH);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function buildAnchor(node: SDK.DOMModel.DOMNode,
|
|
26
|
+
selector = 'element'): CommentManager.CommentManager.CommentAnchorSignature {
|
|
27
|
+
return {
|
|
28
|
+
vePath: 'Panel: elements > Tree: elements > TreeItem',
|
|
29
|
+
textSignature: selector,
|
|
30
|
+
node: {
|
|
31
|
+
backendNodeId: node.backendNodeId(),
|
|
32
|
+
targetId: node.domModel().target().id(),
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Parses a raw attribute string (e.g. 'class="btn"', 'data-id=123', or 'disabled')
|
|
39
|
+
* into its attribute name and unquoted value for single-attribute inline edits.
|
|
40
|
+
*/
|
|
41
|
+
function parseAttributeText(text: string): {name: string, value: string} {
|
|
42
|
+
const trimmed = text.trim();
|
|
43
|
+
const eqIdx = trimmed.indexOf('=');
|
|
44
|
+
if (eqIdx === -1) {
|
|
45
|
+
return {name: trimmed, value: ''};
|
|
46
|
+
}
|
|
47
|
+
const name = trimmed.slice(0, eqIdx).trim();
|
|
48
|
+
let value = trimmed.slice(eqIdx + 1).trim();
|
|
49
|
+
if (value.length >= 2 &&
|
|
50
|
+
((value.startsWith('"') && value.endsWith('"')) || (value.startsWith('\'') && value.endsWith('\'')))) {
|
|
51
|
+
value = value.slice(1, -1);
|
|
52
|
+
}
|
|
53
|
+
return {name, value};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function describeAddAttribute(attributeName: string, value?: string): string {
|
|
57
|
+
if (value !== undefined && value !== '') {
|
|
58
|
+
return `Added attribute ${trimValue(attributeName)}="${trimValue(value)}"`;
|
|
59
|
+
}
|
|
60
|
+
return `Added attribute "${trimValue(attributeName)}"`;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function describeRemoveAttribute(attributeName: string): string {
|
|
64
|
+
return `Removed attribute "${trimValue(attributeName)}"`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Describes a modification of an existing attribute, which covers renaming the attribute, changing
|
|
69
|
+
* its value, or both at once. `oldValue` is `undefined` if the previous value is unknown.
|
|
70
|
+
*/
|
|
71
|
+
function describeAttributeChange(
|
|
72
|
+
oldAttributeName: string,
|
|
73
|
+
oldValue: string|undefined,
|
|
74
|
+
newAttributeName: string,
|
|
75
|
+
newValue: string,
|
|
76
|
+
): string {
|
|
77
|
+
if (oldAttributeName !== newAttributeName) {
|
|
78
|
+
if (oldValue !== undefined && oldValue !== newValue) {
|
|
79
|
+
return `Renamed attribute "${trimValue(oldAttributeName)}"="${trimValue(oldValue)}" to "${
|
|
80
|
+
trimValue(newAttributeName)}"="${trimValue(newValue)}"`;
|
|
81
|
+
}
|
|
82
|
+
return `Renamed attribute "${trimValue(oldAttributeName)}" to "${trimValue(newAttributeName)}"`;
|
|
83
|
+
}
|
|
84
|
+
if (oldValue !== undefined) {
|
|
85
|
+
return `Changed attribute "${trimValue(newAttributeName)}" from "${trimValue(oldValue)}" to "${
|
|
86
|
+
trimValue(newValue)}"`;
|
|
87
|
+
}
|
|
88
|
+
return `Changed attribute "${trimValue(newAttributeName)}" to "${trimValue(newValue)}"`;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface AttributeEdit {
|
|
92
|
+
/** Name of the edited attribute, or an empty string when a new attribute is added. */
|
|
93
|
+
attributeName: string;
|
|
94
|
+
/** Raw attribute text before the edit, if there was any. */
|
|
95
|
+
oldText: string|null;
|
|
96
|
+
/** Raw attribute text the user committed. Empty when the attribute is removed. */
|
|
97
|
+
newText: string;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function describeAttributeEdit({attributeName, oldText, newText}: AttributeEdit): string {
|
|
101
|
+
const parsedNew = parseAttributeText(newText);
|
|
102
|
+
const parsedOld = oldText !== null ? parseAttributeText(oldText) : undefined;
|
|
103
|
+
|
|
104
|
+
if (!newText.trim()) {
|
|
105
|
+
return describeRemoveAttribute(attributeName.trim());
|
|
106
|
+
}
|
|
107
|
+
if (!attributeName.trim()) {
|
|
108
|
+
return describeAddAttribute(parsedNew.name, parsedNew.value);
|
|
109
|
+
}
|
|
110
|
+
return describeAttributeChange(
|
|
111
|
+
attributeName.trim(),
|
|
112
|
+
parsedOld?.value,
|
|
113
|
+
parsedNew.name || attributeName.trim(),
|
|
114
|
+
parsedNew.value,
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* These strings are for the consumption by AI agents, they don't need to be translated.
|
|
120
|
+
*/
|
|
121
|
+
function describeTagNameEdit(oldTagName: string, newTagName: string): string {
|
|
122
|
+
return `Renamed tag from <${trimValue(oldTagName)}> to <${trimValue(newTagName)}>`;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function describeTextNodeEdit(oldText: string, newText: string): string {
|
|
126
|
+
return `Changed text from "${trimValue(oldText)}" to "${trimValue(newText)}"`;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function describeNodeRemoval(tagName: string): string {
|
|
130
|
+
return `Removed node <${trimValue(tagName)}>`;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function describeHTMLEdit(oldValue?: string, newValue?: string): string {
|
|
134
|
+
if (oldValue !== undefined && newValue !== undefined) {
|
|
135
|
+
return `Changed HTML from "${trimValue(oldValue)}" to "${trimValue(newValue)}"`;
|
|
136
|
+
}
|
|
137
|
+
if (newValue !== undefined) {
|
|
138
|
+
return `Changed HTML to "${trimValue(newValue)}"`;
|
|
139
|
+
}
|
|
140
|
+
return 'Edited HTML';
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function describeVisibilityToggle(tagName: string, hidden: boolean): string {
|
|
144
|
+
return hidden ? `Hid element <${trimValue(tagName)}>` : `Unhid element <${trimValue(tagName)}>`;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function describeNodeDuplication(tagName: string): string {
|
|
148
|
+
return `Duplicated node <${trimValue(tagName)}>`;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function describeNodeMove(tagName: string, directionUp: boolean): string {
|
|
152
|
+
return `Moved node <${trimValue(tagName)}> ${directionUp ? 'up' : 'down'}`;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function describeNodeDrop(tagName: string): string {
|
|
156
|
+
return `Moved node <${trimValue(tagName)}> via drag and drop`;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function describeNodePaste(tagName: string, isCut: boolean): string {
|
|
160
|
+
return isCut ? `Pasted (moved) node <${trimValue(tagName)}>` : `Pasted node <${trimValue(tagName)}>`;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function trackAttributeEdit(tracker: Tracker, node: SDK.DOMModel.DOMNode, selector: string|undefined,
|
|
164
|
+
edit: AttributeEdit): void {
|
|
165
|
+
if (!tracker?.isTracking) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
tracker.trackChange(describeAttributeEdit(edit), buildAnchor(node, selector));
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export function trackTagNameEdit(tracker: Tracker, node: SDK.DOMModel.DOMNode, selector: string|undefined,
|
|
172
|
+
oldTagName: string, newTagName: string): void {
|
|
173
|
+
if (!tracker?.isTracking) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
tracker.trackChange(describeTagNameEdit(oldTagName, newTagName), buildAnchor(node, selector));
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function trackTextNodeEdit(tracker: Tracker, node: SDK.DOMModel.DOMNode, selector: string|undefined,
|
|
180
|
+
oldText: string, newText: string): void {
|
|
181
|
+
if (!tracker?.isTracking) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
tracker.trackChange(describeTextNodeEdit(oldText, newText), buildAnchor(node, selector));
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export function trackNodeRemoval(tracker: Tracker, node: SDK.DOMModel.DOMNode, selector: string|undefined): void {
|
|
188
|
+
if (!tracker?.isTracking) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
tracker.trackChange(describeNodeRemoval(node.nodeName().toLowerCase()), buildAnchor(node, selector));
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export function trackHTMLEdit(tracker: Tracker, node: SDK.DOMModel.DOMNode, selector: string|undefined,
|
|
195
|
+
oldValue: string, newValue: string): void {
|
|
196
|
+
if (!tracker?.isTracking) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
tracker.trackChange(describeHTMLEdit(oldValue, newValue), buildAnchor(node, selector));
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export function trackVisibilityToggle(tracker: Tracker, node: SDK.DOMModel.DOMNode, selector: string|undefined,
|
|
203
|
+
hidden: boolean): void {
|
|
204
|
+
if (!tracker?.isTracking) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
tracker.trackChange(describeVisibilityToggle(node.nodeName().toLowerCase(), hidden), buildAnchor(node, selector));
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export function trackNodeDuplication(tracker: Tracker, node: SDK.DOMModel.DOMNode, selector: string|undefined): void {
|
|
211
|
+
if (!tracker?.isTracking) {
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
tracker.trackChange(describeNodeDuplication(node.nodeName().toLowerCase()), buildAnchor(node, selector));
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export function trackNodeMove(tracker: Tracker, node: SDK.DOMModel.DOMNode, selector: string|undefined,
|
|
218
|
+
directionUp: boolean): void {
|
|
219
|
+
if (!tracker?.isTracking) {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
tracker.trackChange(describeNodeMove(node.nodeName().toLowerCase(), directionUp), buildAnchor(node, selector));
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export function trackNodeDrop(tracker: Tracker, node: SDK.DOMModel.DOMNode, selector: string|undefined): void {
|
|
226
|
+
if (!tracker?.isTracking) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
tracker.trackChange(describeNodeDrop(node.nodeName().toLowerCase()), buildAnchor(node, selector));
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export function trackNodePaste(tracker: Tracker, node: SDK.DOMModel.DOMNode, selector: string|undefined,
|
|
233
|
+
isCut: boolean): void {
|
|
234
|
+
if (!tracker?.isTracking) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
tracker.trackChange(describeNodePaste(node.nodeName().toLowerCase(), isCut), buildAnchor(node, selector));
|
|
238
|
+
}
|
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
// Use of this source code is governed by a BSD-style license that can be
|
|
3
3
|
// found in the LICENSE file.
|
|
4
4
|
|
|
5
|
+
import * as DOMChanges from './DOMChanges.js';
|
|
5
6
|
import * as ElementUpdateRecord from './ElementUpdateRecord.js';
|
|
6
7
|
|
|
7
8
|
export {
|
|
9
|
+
DOMChanges,
|
|
8
10
|
ElementUpdateRecord,
|
|
9
11
|
};
|