chrome-devtools-mcp 0.9.0 → 0.10.0
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/README.md +14 -9
- package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Console.js +1 -8
- package/build/node_modules/chrome-devtools-frontend/front_end/core/common/ParsedURL.js +10 -20
- package/build/node_modules/chrome-devtools-frontend/front_end/core/common/SegmentedRange.js +1 -2
- package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Settings.js +3 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/core/common/StringOutputStream.js +1 -4
- package/build/node_modules/chrome-devtools-frontend/front_end/core/host/AidaClient.js +19 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/core/host/DispatchHttpRequestClient.js +54 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/core/host/GdpClient.js +6 -51
- package/build/node_modules/chrome-devtools-frontend/front_end/core/host/InspectorFrontendHost.js +2 -2
- package/build/node_modules/chrome-devtools-frontend/front_end/core/host/InspectorFrontendHostAPI.js +32 -29
- package/build/node_modules/chrome-devtools-frontend/front_end/core/host/UserMetrics.js +14 -6
- package/build/node_modules/chrome-devtools-frontend/front_end/core/host/host.js +2 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/core/protocol_client/CDPConnection.js +17 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/core/protocol_client/InspectorBackend.js +68 -188
- package/build/node_modules/chrome-devtools-frontend/front_end/core/protocol_client/protocol_client.js +2 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/AnimationModel.js +1 -2
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSMatchedStyles.js +3 -3
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSModel.js +1 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSProperty.js +3 -6
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSPropertyParserMatchers.js +14 -10
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSStyleDeclaration.js +4 -4
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/ChildTargetManager.js +5 -33
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/Connections.js +9 -46
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/DOMModel.js +1 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/DebuggerModel.js +1 -2
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/EnhancedTracesParser.js +17 -3
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/NetworkManager.js +59 -37
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/NetworkRequest.js +5 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/RehydratingConnection.js +102 -4
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/SourceMap.js +2 -3
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/sdk-meta.js +8 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/generated/InspectorBackendCommands.js +1 -39
- package/build/node_modules/chrome-devtools-frontend/front_end/generated/SupportedCSSProperties.js +58 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.js +46 -45
- package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/performance/AIContext.js +10 -25
- package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/CompilerScriptMapping.js +1 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/DebuggerWorkspaceBinding.js +1 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/models/cpu_profile/ProfileTreeModel.js +6 -7
- package/build/node_modules/chrome-devtools-frontend/front_end/models/stack_trace/StackTraceModel.js +1 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/NetworkRequestsHandler.js +12 -3
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/types/TraceEvents.js +3 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace_source_maps_resolver/SourceMapsResolver.js +1 -1
- package/build/node_modules/chrome-devtools-frontend/mcp/mcp.js +14 -0
- package/build/src/DevToolsConnectionAdapter.js +1 -0
- package/build/src/DevtoolsUtils.js +44 -0
- package/build/src/McpContext.js +117 -7
- package/build/src/McpResponse.js +32 -21
- package/build/src/PageCollector.js +21 -9
- package/build/src/browser.js +8 -8
- package/build/src/cli.js +8 -3
- package/build/src/formatters/networkFormatter.js +2 -2
- package/build/src/formatters/snapshotFormatter.js +18 -6
- package/build/src/main.js +7 -2
- package/build/src/third_party/THIRD_PARTY_NOTICES +72 -52
- package/build/src/third_party/index.js +12684 -6052
- package/build/src/tools/emulation.js +37 -44
- package/build/src/tools/input.js +36 -6
- package/build/src/tools/network.js +27 -5
- package/build/src/tools/pages.js +59 -33
- package/build/src/tools/performance.js +5 -2
- package/build/src/tools/screenshot.js +2 -1
- package/build/src/tools/snapshot.js +13 -4
- package/build/src/trace-processing/parse.js +6 -16
- package/build/src/utils/keyboard.js +291 -0
- package/package.json +7 -6
package/build/node_modules/chrome-devtools-frontend/front_end/generated/InspectorBackendCommands.js
CHANGED
|
@@ -2,39 +2,6 @@
|
|
|
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
|
// File is generated by scripts/build/code_generator_frontend.py
|
|
5
|
-
/**
|
|
6
|
-
* @typedef {{
|
|
7
|
-
* registerCommand: function(
|
|
8
|
-
* string&any,
|
|
9
|
-
* !Array.<!{
|
|
10
|
-
* name: string,
|
|
11
|
-
* type: string,
|
|
12
|
-
* optional: boolean,
|
|
13
|
-
* description: string,
|
|
14
|
-
* typeRef: string | null
|
|
15
|
-
* }>,
|
|
16
|
-
* !Array.<string>,
|
|
17
|
-
* string
|
|
18
|
-
* ): void,
|
|
19
|
-
* registerEnum: function(string&any, !Object<string, string>): void,
|
|
20
|
-
* registerEvent: function(string&any, !Array<string>): void,
|
|
21
|
-
* registerType: function(
|
|
22
|
-
* string&any,
|
|
23
|
-
* !Array.<!{
|
|
24
|
-
* name: string,
|
|
25
|
-
* type: string,
|
|
26
|
-
* optional: boolean,
|
|
27
|
-
* description: string,
|
|
28
|
-
* typeRef: string | null
|
|
29
|
-
* }>
|
|
30
|
-
* ): void,
|
|
31
|
-
* }}
|
|
32
|
-
*/
|
|
33
|
-
// @ts-expect-error typedef
|
|
34
|
-
export let InspectorBackendAPI;
|
|
35
|
-
/**
|
|
36
|
-
* @param {!InspectorBackendAPI} inspectorBackend
|
|
37
|
-
*/
|
|
38
5
|
export function registerCommands(inspectorBackend) {
|
|
39
6
|
// Accessibility.
|
|
40
7
|
inspectorBackend.registerEnum("Accessibility.AXValueType", { Boolean: "boolean", Tristate: "tristate", BooleanOrUndefined: "booleanOrUndefined", Idref: "idref", IdrefList: "idrefList", Integer: "integer", Node: "node", NodeList: "nodeList", Number: "number", String: "string", ComputedString: "computedString", Token: "token", TokenList: "tokenList", DomRelation: "domRelation", Role: "role", InternalRole: "internalRole", ValueUndefined: "valueUndefined" });
|
|
@@ -804,10 +771,6 @@ export function registerCommands(inspectorBackend) {
|
|
|
804
771
|
inspectorBackend.registerEnum("Network.TrustTokenOperationDoneEventStatus", { Ok: "Ok", InvalidArgument: "InvalidArgument", MissingIssuerKeys: "MissingIssuerKeys", FailedPrecondition: "FailedPrecondition", ResourceExhausted: "ResourceExhausted", AlreadyExists: "AlreadyExists", ResourceLimited: "ResourceLimited", Unauthorized: "Unauthorized", BadResponse: "BadResponse", InternalError: "InternalError", UnknownError: "UnknownError", FulfilledLocally: "FulfilledLocally", SiteIssuerLimit: "SiteIssuerLimit" });
|
|
805
772
|
inspectorBackend.registerEvent("Network.trustTokenOperationDone", ["status", "type", "requestId", "topLevelOrigin", "issuerOrigin", "issuedTokenCount"]);
|
|
806
773
|
inspectorBackend.registerEvent("Network.policyUpdated", []);
|
|
807
|
-
inspectorBackend.registerEvent("Network.subresourceWebBundleMetadataReceived", ["requestId", "urls"]);
|
|
808
|
-
inspectorBackend.registerEvent("Network.subresourceWebBundleMetadataError", ["requestId", "errorMessage"]);
|
|
809
|
-
inspectorBackend.registerEvent("Network.subresourceWebBundleInnerResponseParsed", ["innerRequestId", "innerRequestURL", "bundleRequestId"]);
|
|
810
|
-
inspectorBackend.registerEvent("Network.subresourceWebBundleInnerResponseError", ["innerRequestId", "innerRequestURL", "errorMessage", "bundleRequestId"]);
|
|
811
774
|
inspectorBackend.registerEvent("Network.reportingApiReportAdded", ["report"]);
|
|
812
775
|
inspectorBackend.registerEvent("Network.reportingApiReportUpdated", ["report"]);
|
|
813
776
|
inspectorBackend.registerEvent("Network.reportingApiEndpointsChangedForOrigin", ["origin", "endpoints"]);
|
|
@@ -1299,8 +1262,7 @@ export function registerCommands(inspectorBackend) {
|
|
|
1299
1262
|
inspectorBackend.registerType("SystemInfo.Size", [{ "name": "width", "type": "number", "optional": false, "description": "Width in pixels.", "typeRef": null }, { "name": "height", "type": "number", "optional": false, "description": "Height in pixels.", "typeRef": null }]);
|
|
1300
1263
|
inspectorBackend.registerType("SystemInfo.VideoDecodeAcceleratorCapability", [{ "name": "profile", "type": "string", "optional": false, "description": "Video codec profile that is supported, e.g. VP9 Profile 2.", "typeRef": null }, { "name": "maxResolution", "type": "object", "optional": false, "description": "Maximum video dimensions in pixels supported for this |profile|.", "typeRef": "SystemInfo.Size" }, { "name": "minResolution", "type": "object", "optional": false, "description": "Minimum video dimensions in pixels supported for this |profile|.", "typeRef": "SystemInfo.Size" }]);
|
|
1301
1264
|
inspectorBackend.registerType("SystemInfo.VideoEncodeAcceleratorCapability", [{ "name": "profile", "type": "string", "optional": false, "description": "Video codec profile that is supported, e.g H264 Main.", "typeRef": null }, { "name": "maxResolution", "type": "object", "optional": false, "description": "Maximum video dimensions in pixels supported for this |profile|.", "typeRef": "SystemInfo.Size" }, { "name": "maxFramerateNumerator", "type": "number", "optional": false, "description": "Maximum encoding framerate in frames per second supported for this |profile|, as fraction's numerator and denominator, e.g. 24/1 fps, 24000/1001 fps, etc.", "typeRef": null }, { "name": "maxFramerateDenominator", "type": "number", "optional": false, "description": "", "typeRef": null }]);
|
|
1302
|
-
inspectorBackend.registerType("SystemInfo.
|
|
1303
|
-
inspectorBackend.registerType("SystemInfo.GPUInfo", [{ "name": "devices", "type": "array", "optional": false, "description": "The graphics devices on the system. Element 0 is the primary GPU.", "typeRef": "SystemInfo.GPUDevice" }, { "name": "auxAttributes", "type": "object", "optional": true, "description": "An optional dictionary of additional GPU related attributes.", "typeRef": null }, { "name": "featureStatus", "type": "object", "optional": true, "description": "An optional dictionary of graphics features and their status.", "typeRef": null }, { "name": "driverBugWorkarounds", "type": "array", "optional": false, "description": "An optional array of GPU driver bug workarounds.", "typeRef": "string" }, { "name": "videoDecoding", "type": "array", "optional": false, "description": "Supported accelerated video decoding capabilities.", "typeRef": "SystemInfo.VideoDecodeAcceleratorCapability" }, { "name": "videoEncoding", "type": "array", "optional": false, "description": "Supported accelerated video encoding capabilities.", "typeRef": "SystemInfo.VideoEncodeAcceleratorCapability" }, { "name": "imageDecoding", "type": "array", "optional": false, "description": "Supported accelerated image decoding capabilities.", "typeRef": "SystemInfo.ImageDecodeAcceleratorCapability" }]);
|
|
1265
|
+
inspectorBackend.registerType("SystemInfo.GPUInfo", [{ "name": "devices", "type": "array", "optional": false, "description": "The graphics devices on the system. Element 0 is the primary GPU.", "typeRef": "SystemInfo.GPUDevice" }, { "name": "auxAttributes", "type": "object", "optional": true, "description": "An optional dictionary of additional GPU related attributes.", "typeRef": null }, { "name": "featureStatus", "type": "object", "optional": true, "description": "An optional dictionary of graphics features and their status.", "typeRef": null }, { "name": "driverBugWorkarounds", "type": "array", "optional": false, "description": "An optional array of GPU driver bug workarounds.", "typeRef": "string" }, { "name": "videoDecoding", "type": "array", "optional": false, "description": "Supported accelerated video decoding capabilities.", "typeRef": "SystemInfo.VideoDecodeAcceleratorCapability" }, { "name": "videoEncoding", "type": "array", "optional": false, "description": "Supported accelerated video encoding capabilities.", "typeRef": "SystemInfo.VideoEncodeAcceleratorCapability" }]);
|
|
1304
1266
|
inspectorBackend.registerType("SystemInfo.ProcessInfo", [{ "name": "type", "type": "string", "optional": false, "description": "Specifies process type.", "typeRef": null }, { "name": "id", "type": "number", "optional": false, "description": "Specifies process id.", "typeRef": null }, { "name": "cpuTime", "type": "number", "optional": false, "description": "Specifies cumulative CPU usage in seconds across all threads of the process since the process start.", "typeRef": null }]);
|
|
1305
1267
|
// Target.
|
|
1306
1268
|
inspectorBackend.registerEnum("Target.WindowState", { Normal: "normal", Minimized: "minimized", Maximized: "maximized", Fullscreen: "fullscreen" });
|
package/build/node_modules/chrome-devtools-frontend/front_end/generated/SupportedCSSProperties.js
CHANGED
|
@@ -420,6 +420,7 @@ export const generatedProperties = [
|
|
|
420
420
|
"column-rule-color",
|
|
421
421
|
"column-rule-outset",
|
|
422
422
|
"column-rule-style",
|
|
423
|
+
"column-rule-visibility-items",
|
|
423
424
|
"column-rule-width",
|
|
424
425
|
"column-span",
|
|
425
426
|
"column-width",
|
|
@@ -517,6 +518,7 @@ export const generatedProperties = [
|
|
|
517
518
|
"inset-block-start",
|
|
518
519
|
"inset-inline-end",
|
|
519
520
|
"inset-inline-start",
|
|
521
|
+
"interactivity",
|
|
520
522
|
"interest-delay-end",
|
|
521
523
|
"interest-delay-start",
|
|
522
524
|
"interpolate-size",
|
|
@@ -638,6 +640,7 @@ export const generatedProperties = [
|
|
|
638
640
|
"row-rule-color",
|
|
639
641
|
"row-rule-outset",
|
|
640
642
|
"row-rule-style",
|
|
643
|
+
"row-rule-visibility-items",
|
|
641
644
|
"row-rule-width",
|
|
642
645
|
"ruby-align",
|
|
643
646
|
"ruby-overhang",
|
|
@@ -719,6 +722,7 @@ export const generatedProperties = [
|
|
|
719
722
|
"text-emphasis-style",
|
|
720
723
|
"text-grow",
|
|
721
724
|
"text-indent",
|
|
725
|
+
"text-justify",
|
|
722
726
|
"text-orientation",
|
|
723
727
|
"text-overflow",
|
|
724
728
|
"text-rendering",
|
|
@@ -1735,6 +1739,16 @@ export const generatedProperties = [
|
|
|
1735
1739
|
],
|
|
1736
1740
|
"name": "column-rule-style"
|
|
1737
1741
|
},
|
|
1742
|
+
{
|
|
1743
|
+
"inherited": false,
|
|
1744
|
+
"keywords": [
|
|
1745
|
+
"all",
|
|
1746
|
+
"around",
|
|
1747
|
+
"between",
|
|
1748
|
+
"none"
|
|
1749
|
+
],
|
|
1750
|
+
"name": "column-rule-visibility-items"
|
|
1751
|
+
},
|
|
1738
1752
|
{
|
|
1739
1753
|
"keywords": [
|
|
1740
1754
|
"thin",
|
|
@@ -3713,6 +3727,16 @@ export const generatedProperties = [
|
|
|
3713
3727
|
],
|
|
3714
3728
|
"name": "row-rule-style"
|
|
3715
3729
|
},
|
|
3730
|
+
{
|
|
3731
|
+
"inherited": false,
|
|
3732
|
+
"keywords": [
|
|
3733
|
+
"all",
|
|
3734
|
+
"around",
|
|
3735
|
+
"between",
|
|
3736
|
+
"none"
|
|
3737
|
+
],
|
|
3738
|
+
"name": "row-rule-visibility-items"
|
|
3739
|
+
},
|
|
3716
3740
|
{
|
|
3717
3741
|
"keywords": [
|
|
3718
3742
|
"thin",
|
|
@@ -4320,6 +4344,16 @@ export const generatedProperties = [
|
|
|
4320
4344
|
"inherited": true,
|
|
4321
4345
|
"name": "text-indent"
|
|
4322
4346
|
},
|
|
4347
|
+
{
|
|
4348
|
+
"inherited": true,
|
|
4349
|
+
"keywords": [
|
|
4350
|
+
"auto",
|
|
4351
|
+
"none",
|
|
4352
|
+
"inter-word",
|
|
4353
|
+
"inter-character"
|
|
4354
|
+
],
|
|
4355
|
+
"name": "text-justify"
|
|
4356
|
+
},
|
|
4323
4357
|
{
|
|
4324
4358
|
"inherited": true,
|
|
4325
4359
|
"keywords": [
|
|
@@ -5363,6 +5397,14 @@ export const generatedPropertyValues = {
|
|
|
5363
5397
|
"double"
|
|
5364
5398
|
]
|
|
5365
5399
|
},
|
|
5400
|
+
"column-rule-visibility-items": {
|
|
5401
|
+
"values": [
|
|
5402
|
+
"all",
|
|
5403
|
+
"around",
|
|
5404
|
+
"between",
|
|
5405
|
+
"none"
|
|
5406
|
+
]
|
|
5407
|
+
},
|
|
5366
5408
|
"column-rule-width": {
|
|
5367
5409
|
"values": [
|
|
5368
5410
|
"thin",
|
|
@@ -6479,6 +6521,14 @@ export const generatedPropertyValues = {
|
|
|
6479
6521
|
"double"
|
|
6480
6522
|
]
|
|
6481
6523
|
},
|
|
6524
|
+
"row-rule-visibility-items": {
|
|
6525
|
+
"values": [
|
|
6526
|
+
"all",
|
|
6527
|
+
"around",
|
|
6528
|
+
"between",
|
|
6529
|
+
"none"
|
|
6530
|
+
]
|
|
6531
|
+
},
|
|
6482
6532
|
"row-rule-width": {
|
|
6483
6533
|
"values": [
|
|
6484
6534
|
"thin",
|
|
@@ -6772,6 +6822,14 @@ export const generatedPropertyValues = {
|
|
|
6772
6822
|
"currentcolor"
|
|
6773
6823
|
]
|
|
6774
6824
|
},
|
|
6825
|
+
"text-justify": {
|
|
6826
|
+
"values": [
|
|
6827
|
+
"auto",
|
|
6828
|
+
"none",
|
|
6829
|
+
"inter-word",
|
|
6830
|
+
"inter-character"
|
|
6831
|
+
]
|
|
6832
|
+
},
|
|
6775
6833
|
"text-orientation": {
|
|
6776
6834
|
"values": [
|
|
6777
6835
|
"sideways",
|
|
@@ -15,7 +15,7 @@ export class PerformanceTraceFormatter {
|
|
|
15
15
|
constructor(focus) {
|
|
16
16
|
this.#focus = focus;
|
|
17
17
|
this.#parsedTrace = focus.parsedTrace;
|
|
18
|
-
this.#insightSet = focus.
|
|
18
|
+
this.#insightSet = focus.primaryInsightSet;
|
|
19
19
|
this.#eventsSerializer = focus.eventsSerializer;
|
|
20
20
|
}
|
|
21
21
|
serializeEvent(event) {
|
|
@@ -91,58 +91,62 @@ export class PerformanceTraceFormatter {
|
|
|
91
91
|
}
|
|
92
92
|
formatTraceSummary() {
|
|
93
93
|
const parsedTrace = this.#parsedTrace;
|
|
94
|
-
const insightSet = this.#insightSet;
|
|
95
94
|
const traceMetadata = this.#parsedTrace.metadata;
|
|
96
95
|
const data = parsedTrace.data;
|
|
97
96
|
const parts = [];
|
|
98
|
-
const lcp = insightSet ? Trace.Insights.Common.getLCP(insightSet) : null;
|
|
99
|
-
const cls = insightSet ? Trace.Insights.Common.getCLS(insightSet) : null;
|
|
100
|
-
const inp = insightSet ? Trace.Insights.Common.getINP(insightSet) : null;
|
|
101
97
|
parts.push(`URL: ${data.Meta.mainFrameURL}`);
|
|
102
|
-
parts.push(`
|
|
98
|
+
parts.push(`Trace bounds: ${this.serializeBounds(data.Meta.traceBounds)}`);
|
|
103
99
|
parts.push('CPU throttling: ' + (traceMetadata.cpuThrottling ? `${traceMetadata.cpuThrottling}x` : 'none'));
|
|
104
100
|
parts.push(`Network throttling: ${traceMetadata.networkThrottling ?? 'none'}`);
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
if (subparts
|
|
122
|
-
|
|
101
|
+
parts.push('\n# Available insight sets\n');
|
|
102
|
+
parts.push('The following is a list of insight sets. An insight set covers a specific part of the trace, split by navigations. The insights within each insight set are specific to that part of the trace. Be sure to consider the insight set id and bounds when calling functions. If no specific insight set or navigation is mentioned, assume the user is referring to the first one.');
|
|
103
|
+
for (const insightSet of parsedTrace.insights?.values() ?? []) {
|
|
104
|
+
const lcp = insightSet ? Trace.Insights.Common.getLCP(insightSet) : null;
|
|
105
|
+
const cls = insightSet ? Trace.Insights.Common.getCLS(insightSet) : null;
|
|
106
|
+
const inp = insightSet ? Trace.Insights.Common.getINP(insightSet) : null;
|
|
107
|
+
parts.push(`\n## insight set id: ${insightSet.id}\n`);
|
|
108
|
+
parts.push(`URL: ${insightSet.url}`);
|
|
109
|
+
parts.push(`Bounds: ${this.serializeBounds(insightSet.bounds)}`);
|
|
110
|
+
if (lcp || cls || inp) {
|
|
111
|
+
parts.push('Metrics (lab / observed):');
|
|
112
|
+
if (lcp) {
|
|
113
|
+
const nodeId = insightSet?.model.LCPBreakdown.lcpEvent?.args.data?.nodeId;
|
|
114
|
+
const nodeIdText = nodeId !== undefined ? `, nodeId: ${nodeId}` : '';
|
|
115
|
+
parts.push(` - LCP: ${Math.round(lcp.value / 1000)} ms, event: ${this.serializeEvent(lcp.event)}${nodeIdText}`);
|
|
116
|
+
const subparts = insightSet?.model.LCPBreakdown.subparts;
|
|
117
|
+
if (subparts) {
|
|
118
|
+
const serializeSubpart = (subpart) => {
|
|
119
|
+
return `${micros(subpart.range)}, bounds: ${this.serializeBounds(subpart)}`;
|
|
120
|
+
};
|
|
121
|
+
parts.push(' - LCP breakdown:');
|
|
122
|
+
parts.push(` - TTFB: ${serializeSubpart(subparts.ttfb)}`);
|
|
123
|
+
if (subparts.loadDelay !== undefined) {
|
|
124
|
+
parts.push(` - Load delay: ${serializeSubpart(subparts.loadDelay)}`);
|
|
125
|
+
}
|
|
126
|
+
if (subparts.loadDuration !== undefined) {
|
|
127
|
+
parts.push(` - Load duration: ${serializeSubpart(subparts.loadDuration)}`);
|
|
128
|
+
}
|
|
129
|
+
parts.push(` - Render delay: ${serializeSubpart(subparts.renderDelay)}`);
|
|
123
130
|
}
|
|
124
|
-
|
|
131
|
+
}
|
|
132
|
+
if (inp) {
|
|
133
|
+
parts.push(` - INP: ${Math.round(inp.value / 1000)} ms, event: ${this.serializeEvent(inp.event)}`);
|
|
134
|
+
}
|
|
135
|
+
if (cls) {
|
|
136
|
+
const eventText = cls.worstClusterEvent ? `, event: ${this.serializeEvent(cls.worstClusterEvent)}` : '';
|
|
137
|
+
parts.push(` - CLS: ${cls.value.toFixed(2)}${eventText}`);
|
|
125
138
|
}
|
|
126
139
|
}
|
|
127
|
-
|
|
128
|
-
parts.push(
|
|
140
|
+
else {
|
|
141
|
+
parts.push('Metrics (lab / observed): n/a');
|
|
129
142
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
parts.push(
|
|
143
|
+
const cruxParts = insightSet && this.#getCruxTraceSummary(insightSet);
|
|
144
|
+
if (cruxParts?.length) {
|
|
145
|
+
parts.push(...cruxParts);
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
parts.push('Metrics (field / real users): n/a – no data for this page in CrUX');
|
|
133
149
|
}
|
|
134
|
-
}
|
|
135
|
-
else {
|
|
136
|
-
parts.push('Metrics (lab / observed): n/a');
|
|
137
|
-
}
|
|
138
|
-
const cruxParts = insightSet && this.#getCruxTraceSummary(insightSet);
|
|
139
|
-
if (cruxParts?.length) {
|
|
140
|
-
parts.push(...cruxParts);
|
|
141
|
-
}
|
|
142
|
-
else {
|
|
143
|
-
parts.push('Metrics (field / real users): n/a – no data for this page in CrUX');
|
|
144
|
-
}
|
|
145
|
-
if (insightSet) {
|
|
146
150
|
parts.push('Available insights:');
|
|
147
151
|
for (const [insightName, model] of Object.entries(insightSet.model)) {
|
|
148
152
|
if (model.state === 'pass') {
|
|
@@ -172,9 +176,6 @@ export class PerformanceTraceFormatter {
|
|
|
172
176
|
parts.push(` - ${insightPartsText}`);
|
|
173
177
|
}
|
|
174
178
|
}
|
|
175
|
-
else {
|
|
176
|
-
parts.push('Available insights: none');
|
|
177
|
-
}
|
|
178
179
|
return parts.join('\n');
|
|
179
180
|
}
|
|
180
181
|
formatCriticalRequests() {
|
|
@@ -9,11 +9,8 @@ import { AICallTree } from './AICallTree.js';
|
|
|
9
9
|
* 2. If there are more, prefer the first we find that has a navigation associated with it.
|
|
10
10
|
* 3. If none with a navigation are found, fallback to the first one.
|
|
11
11
|
* 4. Otherwise, return null.
|
|
12
|
-
*
|
|
13
|
-
* TODO(cjamcl): we should just give the agent the entire insight set, and give
|
|
14
|
-
* summary detail about all of them + the ability to query each.
|
|
15
12
|
*/
|
|
16
|
-
function
|
|
13
|
+
function getPrimaryInsightSet(insights) {
|
|
17
14
|
const insightSets = Array.from(insights.values());
|
|
18
15
|
if (insightSets.length === 0) {
|
|
19
16
|
return null;
|
|
@@ -28,10 +25,8 @@ export class AgentFocus {
|
|
|
28
25
|
if (!parsedTrace.insights) {
|
|
29
26
|
throw new Error('missing insights');
|
|
30
27
|
}
|
|
31
|
-
const insightSet = getFirstInsightSet(parsedTrace.insights);
|
|
32
28
|
return new AgentFocus({
|
|
33
29
|
parsedTrace,
|
|
34
|
-
insightSet,
|
|
35
30
|
event: null,
|
|
36
31
|
callTree: null,
|
|
37
32
|
insight: null,
|
|
@@ -41,10 +36,8 @@ export class AgentFocus {
|
|
|
41
36
|
if (!parsedTrace.insights) {
|
|
42
37
|
throw new Error('missing insights');
|
|
43
38
|
}
|
|
44
|
-
const insightSet = getFirstInsightSet(parsedTrace.insights);
|
|
45
39
|
return new AgentFocus({
|
|
46
40
|
parsedTrace,
|
|
47
|
-
insightSet,
|
|
48
41
|
event: null,
|
|
49
42
|
callTree: null,
|
|
50
43
|
insight,
|
|
@@ -54,35 +47,27 @@ export class AgentFocus {
|
|
|
54
47
|
if (!parsedTrace.insights) {
|
|
55
48
|
throw new Error('missing insights');
|
|
56
49
|
}
|
|
57
|
-
const insightSet = getFirstInsightSet(parsedTrace.insights);
|
|
58
50
|
const result = AgentFocus.#getCallTreeOrEvent(parsedTrace, event);
|
|
59
|
-
return new AgentFocus({ parsedTrace,
|
|
51
|
+
return new AgentFocus({ parsedTrace, event: result.event, callTree: result.callTree, insight: null });
|
|
60
52
|
}
|
|
61
53
|
static fromCallTree(callTree) {
|
|
62
|
-
|
|
63
|
-
// Select the insight set containing the call tree.
|
|
64
|
-
// If for some reason that fails, fallback to the first one.
|
|
65
|
-
let insightSet = null;
|
|
66
|
-
if (insights) {
|
|
67
|
-
const callTreeTimeRange = Trace.Helpers.Timing.traceWindowFromEvent(callTree.rootNode.event);
|
|
68
|
-
insightSet = insights.values().find(set => Trace.Helpers.Timing.boundsIncludeTimeRange({
|
|
69
|
-
timeRange: callTreeTimeRange,
|
|
70
|
-
bounds: set.bounds,
|
|
71
|
-
})) ??
|
|
72
|
-
getFirstInsightSet(insights);
|
|
73
|
-
}
|
|
74
|
-
return new AgentFocus({ parsedTrace: callTree.parsedTrace, insightSet, event: null, callTree, insight: null });
|
|
54
|
+
return new AgentFocus({ parsedTrace: callTree.parsedTrace, event: null, callTree, insight: null });
|
|
75
55
|
}
|
|
76
56
|
#data;
|
|
57
|
+
#primaryInsightSet;
|
|
77
58
|
eventsSerializer = new Trace.EventsSerializer.EventsSerializer();
|
|
78
59
|
constructor(data) {
|
|
60
|
+
if (!data.parsedTrace.insights) {
|
|
61
|
+
throw new Error('missing insights');
|
|
62
|
+
}
|
|
79
63
|
this.#data = data;
|
|
64
|
+
this.#primaryInsightSet = getPrimaryInsightSet(data.parsedTrace.insights);
|
|
80
65
|
}
|
|
81
66
|
get parsedTrace() {
|
|
82
67
|
return this.#data.parsedTrace;
|
|
83
68
|
}
|
|
84
|
-
get
|
|
85
|
-
return this.#
|
|
69
|
+
get primaryInsightSet() {
|
|
70
|
+
return this.#primaryInsightSet;
|
|
86
71
|
}
|
|
87
72
|
/** Note: at most one of event or callTree is non-null. */
|
|
88
73
|
get event() {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import * as Common from '../../core/common/common.js';
|
|
5
5
|
import * as Platform from '../../core/platform/platform.js';
|
|
6
6
|
import * as SDK from '../../core/sdk/sdk.js';
|
|
7
|
-
// eslint-disable-next-line
|
|
7
|
+
// eslint-disable-next-line @devtools/es-modules-import
|
|
8
8
|
import * as StackTraceImpl from '../stack_trace/stack_trace_impl.js';
|
|
9
9
|
import * as TextUtils from '../text_utils/text_utils.js';
|
|
10
10
|
import * as Workspace from '../workspace/workspace.js';
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import * as Common from '../../core/common/common.js';
|
|
5
5
|
import * as Platform from '../../core/platform/platform.js';
|
|
6
6
|
import * as SDK from '../../core/sdk/sdk.js';
|
|
7
|
-
// eslint-disable-next-line
|
|
7
|
+
// eslint-disable-next-line @devtools/es-modules-import
|
|
8
8
|
import * as StackTraceImpl from '../stack_trace/stack_trace_impl.js';
|
|
9
9
|
import * as Workspace from '../workspace/workspace.js';
|
|
10
10
|
import { CompilerScriptMapping } from './CompilerScriptMapping.js';
|
package/build/node_modules/chrome-devtools-frontend/front_end/models/cpu_profile/ProfileTreeModel.js
CHANGED
|
@@ -9,7 +9,7 @@ export class ProfileNode {
|
|
|
9
9
|
id;
|
|
10
10
|
parent;
|
|
11
11
|
children;
|
|
12
|
-
|
|
12
|
+
originalFunctionName = null;
|
|
13
13
|
depth;
|
|
14
14
|
deoptReason;
|
|
15
15
|
constructor(callFrame) {
|
|
@@ -18,7 +18,6 @@ export class ProfileNode {
|
|
|
18
18
|
this.self = 0;
|
|
19
19
|
this.total = 0;
|
|
20
20
|
this.id = 0;
|
|
21
|
-
this.functionName = callFrame.functionName;
|
|
22
21
|
this.parent = null;
|
|
23
22
|
this.children = [];
|
|
24
23
|
}
|
|
@@ -34,11 +33,11 @@ export class ProfileNode {
|
|
|
34
33
|
get columnNumber() {
|
|
35
34
|
return this.callFrame.columnNumber;
|
|
36
35
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
this.
|
|
36
|
+
get functionName() {
|
|
37
|
+
return this.originalFunctionName ?? this.callFrame.functionName;
|
|
38
|
+
}
|
|
39
|
+
setOriginalFunctionName(name) {
|
|
40
|
+
this.originalFunctionName = name;
|
|
42
41
|
}
|
|
43
42
|
}
|
|
44
43
|
export class ProfileTreeModel {
|
package/build/node_modules/chrome-devtools-frontend/front_end/models/stack_trace/StackTraceModel.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
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
|
import * as SDK from '../../core/sdk/sdk.js';
|
|
5
|
-
// eslint-disable-next-line
|
|
5
|
+
// eslint-disable-next-line @devtools/es-modules-import
|
|
6
6
|
import * as StackTrace from './stack_trace.js';
|
|
7
7
|
import { AsyncFragmentImpl, FragmentImpl, FrameImpl, StackTraceImpl } from './StackTraceImpl.js';
|
|
8
8
|
import { Trie } from './Trie.js';
|
|
@@ -98,6 +98,9 @@ export function handleEvent(event) {
|
|
|
98
98
|
storeTraceEventWithRequestId(event.args.data.requestId, 'resourceMarkAsCached', event);
|
|
99
99
|
return;
|
|
100
100
|
}
|
|
101
|
+
if (Types.Events.isPreloadRenderBlockingStatusChangeEvent(event)) {
|
|
102
|
+
storeTraceEventWithRequestId(event.args.data.requestId, 'preloadRenderBlockingStatusChange', [event]);
|
|
103
|
+
}
|
|
101
104
|
if (Types.Events.isWebSocketCreate(event) || Types.Events.isWebSocketInfo(event) ||
|
|
102
105
|
Types.Events.isWebSocketTransfer(event)) {
|
|
103
106
|
const identifier = event.args.data.identifier;
|
|
@@ -387,11 +390,18 @@ export async function finalize() {
|
|
|
387
390
|
Types.Timing.Micro((timing.connectEnd - timing.connectStart) * MILLISECONDS_TO_MICROSECONDS) :
|
|
388
391
|
Types.Timing.Micro(0);
|
|
389
392
|
// Finally get some of the general data from the trace events.
|
|
390
|
-
const { frame, url, renderBlocking } = finalSendRequest.args.data;
|
|
393
|
+
const { frame, url, renderBlocking: sendRequestIsRenderBlocking } = finalSendRequest.args.data;
|
|
391
394
|
const { encodedDataLength, decodedBodyLength } = request.resourceFinish ? request.resourceFinish.args.data : { encodedDataLength: 0, decodedBodyLength: 0 };
|
|
392
395
|
const parsedUrl = new URL(url);
|
|
393
396
|
const isHttps = parsedUrl.protocol === 'https:';
|
|
394
397
|
const requestingFrameUrl = Helpers.Trace.activeURLForFrameAtTime(frame, finalSendRequest.ts, rendererProcessesByFrame) || '';
|
|
398
|
+
// A resource that is preloaded (and not marked as render blocking) can
|
|
399
|
+
// become render blocked later via a PreloadRenderBlockingStatusChange. In
|
|
400
|
+
// this case, we take the render blocking value of the last
|
|
401
|
+
// PreloadRenderBlockingStatusChange for this request.
|
|
402
|
+
const preloadRenderBlockingStatusChange = request.preloadRenderBlockingStatusChange?.at(-1)?.args.data.renderBlocking;
|
|
403
|
+
// In the event the property isn't set, assume non-blocking.
|
|
404
|
+
const isRenderBlocking = preloadRenderBlockingStatusChange ?? sendRequestIsRenderBlocking ?? 'non_blocking';
|
|
395
405
|
// Construct a synthetic trace event for this network request.
|
|
396
406
|
const networkEvent = Helpers.SyntheticEvents.SyntheticEventsManager.registerSyntheticEvent({
|
|
397
407
|
rawSourceEvent: finalSendRequest,
|
|
@@ -432,8 +442,7 @@ export async function finalize() {
|
|
|
432
442
|
initialPriority,
|
|
433
443
|
protocol: request.receiveResponse?.args.data.protocol ?? 'unknown',
|
|
434
444
|
redirects,
|
|
435
|
-
|
|
436
|
-
renderBlocking: renderBlocking ?? 'non_blocking',
|
|
445
|
+
renderBlocking: isRenderBlocking,
|
|
437
446
|
requestId,
|
|
438
447
|
requestingFrameUrl,
|
|
439
448
|
requestMethod: finalSendRequest.args.data.requestMethod,
|
package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/types/TraceEvents.js
CHANGED
|
@@ -574,3 +574,6 @@ export function isRundownScriptSourceLarge(event) {
|
|
|
574
574
|
export function isAnyScriptSourceEvent(event) {
|
|
575
575
|
return event.cat === 'disabled-by-default-devtools.v8-source-rundown-sources';
|
|
576
576
|
}
|
|
577
|
+
export function isPreloadRenderBlockingStatusChangeEvent(event) {
|
|
578
|
+
return event.name === "PreloadRenderBlockingStatusChange" /* Name.PRELOAD_RENDER_BLOCKING_STATUS_CHANGE */;
|
|
579
|
+
}
|
|
@@ -142,7 +142,7 @@ export class SourceMapsResolver extends EventTarget {
|
|
|
142
142
|
for (const node of nodes) {
|
|
143
143
|
const resolvedFunctionName = await SourceMapScopes.NamesResolver.resolveProfileFrameFunctionName(node.callFrame, target);
|
|
144
144
|
updatedMappings ||= Boolean(resolvedFunctionName);
|
|
145
|
-
node.
|
|
145
|
+
node.setOriginalFunctionName(resolvedFunctionName);
|
|
146
146
|
const debuggerModel = target.model(SDK.DebuggerModel.DebuggerModel);
|
|
147
147
|
const script = debuggerModel?.scriptForId(node.scriptId) || null;
|
|
148
148
|
const location = debuggerModel &&
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Copyright 2025 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
|
+
* IMPORTANT! Make sure that any class that is exported here has related unit
|
|
6
|
+
* tests added to foundation_unittests. See front_end/core/i18n/BUILD.gn as an
|
|
7
|
+
* example.
|
|
8
|
+
*/
|
|
9
|
+
export * as I18n from '../front_end/core/i18n/i18n.js';
|
|
10
|
+
export { ConnectionTransport } from '../front_end/core/protocol_client/ConnectionTransport.js';
|
|
11
|
+
export { PerformanceInsightFormatter } from '../front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.js';
|
|
12
|
+
export { PerformanceTraceFormatter } from '../front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.js';
|
|
13
|
+
export { AgentFocus } from '../front_end/models/ai_assistance/performance/AIContext.js';
|
|
14
|
+
export * as TraceEngine from '../front_end/models/trace/trace.js';
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Copyright 2025 Google LLC
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
|
+
// eslint-disable-next-line no-restricted-imports
|
|
6
7
|
import { ConnectionTransport as DevToolsConnectionTransport } from '../node_modules/chrome-devtools-frontend/front_end/core/protocol_client/ConnectionTransport.js';
|
|
7
8
|
/**
|
|
8
9
|
* Allows a puppeteer {@link ConnectionTransport} to act like a DevTools {@link Connection}.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
export function extractUrlLikeFromDevToolsTitle(title) {
|
|
7
|
+
const match = title.match(new RegExp(`DevTools - (.*)`));
|
|
8
|
+
return match?.[1] ?? undefined;
|
|
9
|
+
}
|
|
10
|
+
export function urlsEqual(url1, url2) {
|
|
11
|
+
const normalizedUrl1 = normalizeUrl(url1);
|
|
12
|
+
const normalizedUrl2 = normalizeUrl(url2);
|
|
13
|
+
return normalizedUrl1 === normalizedUrl2;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* For the sake of the MCP server, when we determine if two URLs are equal we
|
|
17
|
+
* remove some parts:
|
|
18
|
+
*
|
|
19
|
+
* 1. We do not care about the protocol.
|
|
20
|
+
* 2. We do not care about trailing slashes.
|
|
21
|
+
* 3. We do not care about "www".
|
|
22
|
+
*
|
|
23
|
+
* For example, if the user types "record a trace on foo.com", we would want to
|
|
24
|
+
* match a tab in the connected Chrome instance that is showing "www.foo.com/"
|
|
25
|
+
*/
|
|
26
|
+
function normalizeUrl(url) {
|
|
27
|
+
let result = url.trim();
|
|
28
|
+
// Remove protocols
|
|
29
|
+
if (result.startsWith('https://')) {
|
|
30
|
+
result = result.slice(8);
|
|
31
|
+
}
|
|
32
|
+
else if (result.startsWith('http://')) {
|
|
33
|
+
result = result.slice(7);
|
|
34
|
+
}
|
|
35
|
+
// Remove 'www.'. This ensures that we find the right URL regardless of if the user adds `www` or not.
|
|
36
|
+
if (result.startsWith('www.')) {
|
|
37
|
+
result = result.slice(4);
|
|
38
|
+
}
|
|
39
|
+
// Remove trailing slash
|
|
40
|
+
if (result.endsWith('/')) {
|
|
41
|
+
result = result.slice(0, -1);
|
|
42
|
+
}
|
|
43
|
+
return result;
|
|
44
|
+
}
|