chrome-devtools-mcp 0.5.1 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/README.md +60 -5
  2. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Color.js +13 -9
  3. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/ColorConverter.js +9 -7
  4. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Gzip.js +1 -1
  5. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/MapWithDefault.js +5 -3
  6. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/ResourceType.js +0 -11
  7. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/ReturnToPanel.js +6 -4
  8. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/AidaClient.js +1 -1
  9. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/GdpClient.js +116 -59
  10. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/InspectorFrontendHost.js +3 -0
  11. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/Platform.js +5 -3
  12. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/UserMetrics.js +6 -4
  13. package/build/node_modules/chrome-devtools-frontend/front_end/core/platform/ArrayUtilities.js +1 -1
  14. package/build/node_modules/chrome-devtools-frontend/front_end/core/platform/StringUtilities.js +33 -31
  15. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSMetadata.js +4 -2
  16. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSModel.js +6 -0
  17. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSPropertyParser.js +11 -9
  18. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSPropertyParserMatchers.js +19 -13
  19. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSRule.js +4 -0
  20. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSStartingStyle.js +21 -0
  21. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/ChildTargetManager.js +30 -0
  22. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/DOMModel.js +20 -2
  23. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/EventBreakpointsModel.js +4 -2
  24. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/HttpReasonPhraseStrings.js +4 -2
  25. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/NetworkManager.js +9 -41
  26. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/NetworkRequest.js +0 -14
  27. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/PageResourceLoader.js +1 -1
  28. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/PreloadingModel.js +7 -5
  29. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/RehydratingConnection.js +6 -2
  30. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/RemoteObject.js +1 -1
  31. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/ResourceTreeModel.js +1 -0
  32. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/ScreenCaptureModel.js +20 -18
  33. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/Target.js +7 -1
  34. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/TraceObject.js +2 -2
  35. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/sdk.js +2 -1
  36. package/build/node_modules/chrome-devtools-frontend/front_end/generated/Deprecation.js +4 -4
  37. package/build/node_modules/chrome-devtools-frontend/front_end/generated/InspectorBackendCommands.js +5 -4
  38. package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/NetworkRequestFormatter.js +30 -3
  39. package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/performance/AIContext.js +18 -4
  40. package/build/node_modules/chrome-devtools-frontend/front_end/models/crux-manager/CrUXManager.js +1 -1
  41. package/build/node_modules/chrome-devtools-frontend/front_end/models/network_time_calculator/RequestTimeRanges.js +6 -4
  42. package/build/node_modules/chrome-devtools-frontend/front_end/models/source_map_scopes/NamesResolver.js +7 -5
  43. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/LanternComputationData.js +1 -0
  44. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/extras/TraceTree.js +1 -1
  45. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/FramesHandler.js +7 -5
  46. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/LayoutShiftsHandler.js +8 -4
  47. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/NetworkRequestsHandler.js +17 -0
  48. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/ScriptsHandler.js +2 -1
  49. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/helpers.js +1 -1
  50. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/helpers/Timing.js +4 -2
  51. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/helpers/Trace.js +8 -4
  52. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/DocumentLatency.js +10 -10
  53. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/INPBreakdown.js +12 -1
  54. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/LCPBreakdown.js +11 -1
  55. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/LegacyJavaScript.js +2 -1
  56. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/NetworkDependencyTree.js +2 -2
  57. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/types/TraceEvents.js +5 -3
  58. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace_source_maps_resolver/SourceMapsResolver.js +1 -1
  59. package/build/src/McpContext.js +28 -4
  60. package/build/src/McpResponse.js +25 -16
  61. package/build/src/Mutex.js +3 -6
  62. package/build/src/PageCollector.js +17 -6
  63. package/build/src/browser.js +21 -12
  64. package/build/src/cli.js +38 -1
  65. package/build/src/formatters/consoleFormatter.js +1 -1
  66. package/build/src/logger.js +2 -2
  67. package/build/src/main.js +18 -10
  68. package/build/src/tools/ToolDefinition.js +11 -0
  69. package/build/src/tools/input.js +1 -1
  70. package/build/src/tools/network.js +0 -1
  71. package/build/src/tools/pages.js +15 -5
  72. package/build/src/tools/performance.js +3 -3
  73. package/build/src/tools/screenshot.js +11 -3
  74. package/build/src/tools/snapshot.js +11 -5
  75. package/build/src/trace-processing/parse.js +1 -1
  76. package/package.json +5 -5
@@ -3,24 +3,26 @@
3
3
  // found in the LICENSE file.
4
4
  import { OverlayModel } from './OverlayModel.js';
5
5
  import { SDKModel } from './SDKModel.js';
6
- // Manages concurrent screencast requests by queuing and prioritizing.
7
- //
8
- // When startScreencast is invoked:
9
- // - If a screencast is currently active, the existing screencast's parameters and callbacks are
10
- // saved in the #screencastOperations array.
11
- // - The active screencast is then stopped.
12
- // - A new screencast is initiated using the parameters and callbacks from the current startScreencast call.
13
- //
14
- // When stopScreencast is invoked:
15
- // - The currently active screencast is stopped.
16
- // - The #screencastOperations is checked for interrupted screencast operations.
17
- // - If any operations are found, the latest one is started
18
- // using its saved parameters and callbacks.
19
- //
20
- // This ensures that:
21
- // - Only one screencast is active at a time.
22
- // - Interrupted screencasts are resumed after the current screencast is stopped.
23
- // This ensures animation previews, which use screencasting, don't disrupt ongoing remote debugging sessions. Without this mechanism, stopping a preview screencast would terminate the debugging screencast, freezing the ScreencastView.
6
+ /**
7
+ * Manages concurrent screencast requests by queuing and prioritizing.
8
+ *
9
+ * When startScreencast is invoked:
10
+ * - If a screencast is currently active, the existing screencast's parameters and callbacks are
11
+ * saved in the #screencastOperations array.
12
+ * - The active screencast is then stopped.
13
+ * - A new screencast is initiated using the parameters and callbacks from the current startScreencast call.
14
+ *
15
+ * When stopScreencast is invoked:
16
+ * - The currently active screencast is stopped.
17
+ * - The #screencastOperations is checked for interrupted screencast operations.
18
+ * - If any operations are found, the latest one is started
19
+ * using its saved parameters and callbacks.
20
+ *
21
+ * This ensures that:
22
+ * - Only one screencast is active at a time.
23
+ * - Interrupted screencasts are resumed after the current screencast is stopped.
24
+ * This ensures animation previews, which use screencasting, don't disrupt ongoing remote debugging sessions. Without this mechanism, stopping a preview screencast would terminate the debugging screencast, freezing the ScreencastView.
25
+ **/
24
26
  export class ScreenCaptureModel extends SDKModel {
25
27
  #agent;
26
28
  #nextScreencastOperationId = 1;
@@ -57,12 +57,15 @@ export class Target extends ProtocolClient.InspectorBackend.TargetBase {
57
57
  this.#capabilitiesMask = 4 /* Capability.JS */ | 8 /* Capability.LOG */ | 16 /* Capability.NETWORK */ | 32 /* Capability.TARGET */ |
58
58
  2048 /* Capability.INSPECTOR */ | 131072 /* Capability.IO */ | 524288 /* Capability.EVENT_BREAKPOINTS */;
59
59
  if (parentTarget?.type() !== Type.FRAME) {
60
- this.#capabilitiesMask |= 1 /* Capability.BROWSER */;
60
+ this.#capabilitiesMask |= 1 /* Capability.BROWSER */ | 8192 /* Capability.STORAGE */;
61
61
  }
62
62
  break;
63
63
  case Type.SHARED_WORKER:
64
64
  this.#capabilitiesMask = 4 /* Capability.JS */ | 8 /* Capability.LOG */ | 16 /* Capability.NETWORK */ | 32 /* Capability.TARGET */ |
65
65
  131072 /* Capability.IO */ | 262144 /* Capability.MEDIA */ | 2048 /* Capability.INSPECTOR */ | 524288 /* Capability.EVENT_BREAKPOINTS */;
66
+ if (parentTarget?.type() !== Type.FRAME) {
67
+ this.#capabilitiesMask |= 8192 /* Capability.STORAGE */;
68
+ }
66
69
  break;
67
70
  case Type.SHARED_STORAGE_WORKLET:
68
71
  this.#capabilitiesMask = 4 /* Capability.JS */ | 8 /* Capability.LOG */ | 2048 /* Capability.INSPECTOR */ | 524288 /* Capability.EVENT_BREAKPOINTS */;
@@ -70,6 +73,9 @@ export class Target extends ProtocolClient.InspectorBackend.TargetBase {
70
73
  case Type.Worker:
71
74
  this.#capabilitiesMask = 4 /* Capability.JS */ | 8 /* Capability.LOG */ | 16 /* Capability.NETWORK */ | 32 /* Capability.TARGET */ |
72
75
  131072 /* Capability.IO */ | 262144 /* Capability.MEDIA */ | 256 /* Capability.EMULATION */ | 524288 /* Capability.EVENT_BREAKPOINTS */;
76
+ if (parentTarget?.type() !== Type.FRAME) {
77
+ this.#capabilitiesMask |= 8192 /* Capability.STORAGE */;
78
+ }
73
79
  break;
74
80
  case Type.WORKLET:
75
81
  this.#capabilitiesMask = 4 /* Capability.JS */ | 8 /* Capability.LOG */ | 524288 /* Capability.EVENT_BREAKPOINTS */ | 16 /* Capability.NETWORK */;
@@ -3,7 +3,7 @@
3
3
  // found in the LICENSE file.
4
4
  import * as Common from '../../core/common/common.js';
5
5
  import { ResourceTreeModel } from './ResourceTreeModel.js';
6
- // A thin wrapper class, mostly to enable instanceof-based revealing of traces to open in Timeline.
6
+ /** A thin wrapper class, mostly to enable instanceof-based revealing of traces to open in Timeline. **/
7
7
  export class TraceObject {
8
8
  traceEvents;
9
9
  metadata;
@@ -15,7 +15,7 @@ export class TraceObject {
15
15
  this.metadata = metadata;
16
16
  }
17
17
  }
18
- // Another thin wrapper class to enable revealing individual trace events (aka entries) in Timeline panel.
18
+ /** Another thin wrapper class to enable revealing individual trace events (aka entries) in Timeline panel. **/
19
19
  export class RevealableEvent {
20
20
  event;
21
21
  // Only Trace.Types.Events.Event are passed in, but we can't depend on that type from SDK
@@ -33,6 +33,7 @@ import * as CSSPropertyParserMatchers from './CSSPropertyParserMatchers.js';
33
33
  import * as CSSQuery from './CSSQuery.js';
34
34
  import * as CSSRule from './CSSRule.js';
35
35
  import * as CSSScope from './CSSScope.js';
36
+ import * as CSSStartingStyle from './CSSStartingStyle.js';
36
37
  import * as CSSStyleDeclaration from './CSSStyleDeclaration.js';
37
38
  import * as CSSStyleSheetHeader from './CSSStyleSheetHeader.js';
38
39
  import * as CSSSupports from './CSSSupports.js';
@@ -85,5 +86,5 @@ import * as Target from './Target.js';
85
86
  import * as TargetManager from './TargetManager.js';
86
87
  import * as TraceObject from './TraceObject.js';
87
88
  import * as WebAuthnModel from './WebAuthnModel.js';
88
- export { AccessibilityModel, AnimationModel, AutofillModel, CategorizedBreakpoint, ChildTargetManager, CompilerSourceMappingContentProvider, Connections, ConsoleModel, Cookie, CookieModel, CookieParser, CPUProfilerModel, CPUThrottlingManager, CSSContainerQuery, CSSFontFace, CSSLayer, CSSMatchedStyles, CSSMedia, CSSMetadata, CSSModel, CSSProperty, CSSPropertyParser, CSSPropertyParserMatchers, CSSQuery, CSSRule, CSSScope, CSSStyleDeclaration, CSSStyleSheetHeader, CSSSupports, DebuggerModel, DOMDebuggerModel, DOMModel, EmulationModel, EnhancedTracesParser, EventBreakpointsModel, FrameAssociated, FrameManager, HeapProfilerModel, IOModel, IsolateManager, IssuesModel, LayerTreeBase, LogModel, NetworkManager, NetworkRequest, OverlayColorGenerator, OverlayModel, OverlayPersistentHighlighter, PageLoad, PageResourceLoader, PaintProfiler, PerformanceMetricsModel, PreloadingModel, RehydratingConnection, // TODO(crbug.com/444191656): Exported for tests.
89
+ export { AccessibilityModel, AnimationModel, AutofillModel, CategorizedBreakpoint, ChildTargetManager, CompilerSourceMappingContentProvider, Connections, ConsoleModel, Cookie, CookieModel, CookieParser, CPUProfilerModel, CPUThrottlingManager, CSSContainerQuery, CSSFontFace, CSSLayer, CSSMatchedStyles, CSSMedia, CSSMetadata, CSSModel, CSSProperty, CSSPropertyParser, CSSPropertyParserMatchers, CSSQuery, CSSRule, CSSScope, CSSStartingStyle, CSSStyleDeclaration, CSSStyleSheetHeader, CSSSupports, DebuggerModel, DOMDebuggerModel, DOMModel, EmulationModel, EnhancedTracesParser, EventBreakpointsModel, FrameAssociated, FrameManager, HeapProfilerModel, IOModel, IsolateManager, IssuesModel, LayerTreeBase, LogModel, NetworkManager, NetworkRequest, OverlayColorGenerator, OverlayModel, OverlayPersistentHighlighter, PageLoad, PageResourceLoader, PaintProfiler, PerformanceMetricsModel, PreloadingModel, RehydratingConnection, // TODO(crbug.com/444191656): Exported for tests.
89
90
  RemoteObject, Resource, ResourceTreeModel, RuntimeModel, ScreenCaptureModel, Script, SDKModel, SecurityOriginManager, ServerSentEventProtocol, ServerTiming, ServiceWorkerCacheModel, ServiceWorkerManager, SourceMap, SourceMapCache, SourceMapFunctionRanges, SourceMapManager, SourceMapScopeChainEntry, SourceMapScopesInfo, StorageBucketsModel, StorageKeyManager, Target, TargetManager, TraceObject, WebAuthnModel, };
@@ -55,15 +55,15 @@ export const UIStrings = {
55
55
  /**
56
56
  * @description Warning displayed to developers when the Geolocation API is used from an insecure origin (one that isn't localhost or doesn't use HTTPS) to notify them that this use is no longer supported.
57
57
  */
58
- GeolocationInsecureOrigin: "`getCurrentPosition()` and `watchPosition()` no longer work on insecure origins. To use this feature, you should consider switching your application to a secure origin, such as HTTPS. See https://goo.gle/chrome-insecure-origins for more details.",
58
+ GeolocationInsecureOrigin: "`getCurrentPosition()` and `watchPosition()` no longer work on insecure origins. To use this feature, you should consider switching your application to a secure origin, such as HTTPS. See https://www.chromium.org/Home/chromium-security/deprecating-powerful-features-on-insecure-origins/ for more details.",
59
59
  /**
60
60
  * @description Warning displayed to developers when the Geolocation API is used from an insecure origin (one that isn't localhost or doesn't use HTTPS) to notify them that this use is deprecated.
61
61
  */
62
- GeolocationInsecureOriginDeprecatedNotRemoved: "`getCurrentPosition()` and `watchPosition()` are deprecated on insecure origins. To use this feature, you should consider switching your application to a secure origin, such as HTTPS. See https://goo.gle/chrome-insecure-origins for more details.",
62
+ GeolocationInsecureOriginDeprecatedNotRemoved: "`getCurrentPosition()` and `watchPosition()` are deprecated on insecure origins. To use this feature, you should consider switching your application to a secure origin, such as HTTPS. See https://www.chromium.org/Home/chromium-security/deprecating-powerful-features-on-insecure-origins/ for more details.",
63
63
  /**
64
64
  * @description This warning occurs when the `getUserMedia()` API is invoked on an insecure (e.g., HTTP) site. This is only permitted on secure sites (e.g., HTTPS).
65
65
  */
66
- GetUserMediaInsecureOrigin: "`getUserMedia()` no longer works on insecure origins. To use this feature, you should consider switching your application to a secure origin, such as HTTPS. See https://goo.gle/chrome-insecure-origins for more details.",
66
+ GetUserMediaInsecureOrigin: "`getUserMedia()` no longer works on insecure origins. To use this feature, you should consider switching your application to a secure origin, such as HTTPS. See https://www.chromium.org/Home/chromium-security/deprecating-powerful-features-on-insecure-origins/ for more details.",
67
67
  /**
68
68
  * @description A deprecation warning shown to developers in the DevTools Issues tab when code tries to use the deprecated hostCandidate field, guiding developers to use the equivalent information in the .address and .port fields instead.
69
69
  */
@@ -103,7 +103,7 @@ export const UIStrings = {
103
103
  /**
104
104
  * @description Warning displayed to developers when the Notification API is used from an insecure origin (one that isn't localhost or doesn't use HTTPS) to notify them that this use is no longer supported.
105
105
  */
106
- NotificationInsecureOrigin: "The Notification API may no longer be used from insecure origins. You should consider switching your application to a secure origin, such as HTTPS. See https://goo.gle/chrome-insecure-origins for more details.",
106
+ NotificationInsecureOrigin: "The Notification API may no longer be used from insecure origins. You should consider switching your application to a secure origin, such as HTTPS. See https://www.chromium.org/Home/chromium-security/deprecating-powerful-features-on-insecure-origins/ for more details.",
107
107
  /**
108
108
  * @description Warning displayed to developers when permission to use notifications has been requested by a cross-origin iframe, to notify them that this use is no longer supported.
109
109
  */
@@ -205,7 +205,7 @@ export function registerCommands(inspectorBackend) {
205
205
  inspectorBackend.registerEnum("Browser.DownloadProgressEventState", { InProgress: "inProgress", Completed: "completed", Canceled: "canceled" });
206
206
  inspectorBackend.registerEvent("Browser.downloadProgress", ["guid", "totalBytes", "receivedBytes", "state", "filePath"]);
207
207
  inspectorBackend.registerCommand("Browser.setPermission", [{ "name": "permission", "type": "object", "optional": false, "description": "Descriptor of permission to override.", "typeRef": "Browser.PermissionDescriptor" }, { "name": "setting", "type": "string", "optional": false, "description": "Setting of the permission.", "typeRef": "Browser.PermissionSetting" }, { "name": "origin", "type": "string", "optional": true, "description": "Requesting origin the permission applies to, all origins if not specified.", "typeRef": null }, { "name": "embeddingOrigin", "type": "string", "optional": true, "description": "Embedding origin the permission applies to. It is ignored unless the requesting origin is present and valid. If the requesting origin is provided but the embedding origin isn't, the requesting origin is used as the embedding origin.", "typeRef": null }, { "name": "browserContextId", "type": "string", "optional": true, "description": "Context to override. When omitted, default browser context is used.", "typeRef": "Browser.BrowserContextID" }], [], "Set permission settings for given requesting and embedding origins.");
208
- inspectorBackend.registerCommand("Browser.grantPermissions", [{ "name": "permissions", "type": "array", "optional": false, "description": "", "typeRef": "Browser.PermissionType" }, { "name": "origin", "type": "string", "optional": true, "description": "Origin the permission applies to, all origins if not specified.", "typeRef": null }, { "name": "browserContextId", "type": "string", "optional": true, "description": "BrowserContext to override permissions. When omitted, default browser context is used.", "typeRef": "Browser.BrowserContextID" }], [], "Grant specific permissions to the given origin and reject all others.");
208
+ inspectorBackend.registerCommand("Browser.grantPermissions", [{ "name": "permissions", "type": "array", "optional": false, "description": "", "typeRef": "Browser.PermissionType" }, { "name": "origin", "type": "string", "optional": true, "description": "Origin the permission applies to, all origins if not specified.", "typeRef": null }, { "name": "browserContextId", "type": "string", "optional": true, "description": "BrowserContext to override permissions. When omitted, default browser context is used.", "typeRef": "Browser.BrowserContextID" }], [], "Grant specific permissions to the given origin and reject all others. Deprecated. Use setPermission instead.");
209
209
  inspectorBackend.registerCommand("Browser.resetPermissions", [{ "name": "browserContextId", "type": "string", "optional": true, "description": "BrowserContext to reset permissions. When omitted, default browser context is used.", "typeRef": "Browser.BrowserContextID" }], [], "Reset all permission management for all origins.");
210
210
  inspectorBackend.registerEnum("Browser.SetDownloadBehaviorRequestBehavior", { Deny: "deny", Allow: "allow", AllowAndName: "allowAndName", Default: "default" });
211
211
  inspectorBackend.registerCommand("Browser.setDownloadBehavior", [{ "name": "behavior", "type": "string", "optional": false, "description": "Whether to allow all or deny all download requests, or use default Chrome behavior if available (otherwise deny). |allowAndName| allows download and names files according to their download guids.", "typeRef": "Browser.SetDownloadBehaviorRequestBehavior" }, { "name": "browserContextId", "type": "string", "optional": true, "description": "BrowserContext to set download behavior. When omitted, default browser context is used.", "typeRef": "Browser.BrowserContextID" }, { "name": "downloadPath", "type": "string", "optional": true, "description": "The default path to save downloaded files to. This is required if behavior is set to 'allow' or 'allowAndName'.", "typeRef": null }, { "name": "eventsEnabled", "type": "boolean", "optional": true, "description": "Whether to emit download events (defaults to false).", "typeRef": null }], [], "Set the behavior when downloading a file.");
@@ -286,7 +286,7 @@ export function registerCommands(inspectorBackend) {
286
286
  inspectorBackend.registerType("CSS.Specificity", [{ "name": "a", "type": "number", "optional": false, "description": "The a component, which represents the number of ID selectors.", "typeRef": null }, { "name": "b", "type": "number", "optional": false, "description": "The b component, which represents the number of class selectors, attributes selectors, and pseudo-classes.", "typeRef": null }, { "name": "c", "type": "number", "optional": false, "description": "The c component, which represents the number of type selectors and pseudo-elements.", "typeRef": null }]);
287
287
  inspectorBackend.registerType("CSS.SelectorList", [{ "name": "selectors", "type": "array", "optional": false, "description": "Selectors in the list.", "typeRef": "CSS.Value" }, { "name": "text", "type": "string", "optional": false, "description": "Rule selector text.", "typeRef": null }]);
288
288
  inspectorBackend.registerType("CSS.CSSStyleSheetHeader", [{ "name": "styleSheetId", "type": "string", "optional": false, "description": "The stylesheet identifier.", "typeRef": "CSS.StyleSheetId" }, { "name": "frameId", "type": "string", "optional": false, "description": "Owner frame identifier.", "typeRef": "Page.FrameId" }, { "name": "sourceURL", "type": "string", "optional": false, "description": "Stylesheet resource URL. Empty if this is a constructed stylesheet created using new CSSStyleSheet() (but non-empty if this is a constructed stylesheet imported as a CSS module script).", "typeRef": null }, { "name": "sourceMapURL", "type": "string", "optional": true, "description": "URL of source map associated with the stylesheet (if any).", "typeRef": null }, { "name": "origin", "type": "string", "optional": false, "description": "Stylesheet origin.", "typeRef": "CSS.StyleSheetOrigin" }, { "name": "title", "type": "string", "optional": false, "description": "Stylesheet title.", "typeRef": null }, { "name": "ownerNode", "type": "number", "optional": true, "description": "The backend id for the owner node of the stylesheet.", "typeRef": "DOM.BackendNodeId" }, { "name": "disabled", "type": "boolean", "optional": false, "description": "Denotes whether the stylesheet is disabled.", "typeRef": null }, { "name": "hasSourceURL", "type": "boolean", "optional": true, "description": "Whether the sourceURL field value comes from the sourceURL comment.", "typeRef": null }, { "name": "isInline", "type": "boolean", "optional": false, "description": "Whether this stylesheet is created for STYLE tag by parser. This flag is not set for document.written STYLE tags.", "typeRef": null }, { "name": "isMutable", "type": "boolean", "optional": false, "description": "Whether this stylesheet is mutable. Inline stylesheets become mutable after they have been modified via CSSOM API. `<link>` element's stylesheets become mutable only if DevTools modifies them. Constructed stylesheets (new CSSStyleSheet()) are mutable immediately after creation.", "typeRef": null }, { "name": "isConstructed", "type": "boolean", "optional": false, "description": "True if this stylesheet is created through new CSSStyleSheet() or imported as a CSS module script.", "typeRef": null }, { "name": "startLine", "type": "number", "optional": false, "description": "Line offset of the stylesheet within the resource (zero based).", "typeRef": null }, { "name": "startColumn", "type": "number", "optional": false, "description": "Column offset of the stylesheet within the resource (zero based).", "typeRef": null }, { "name": "length", "type": "number", "optional": false, "description": "Size of the content (in characters).", "typeRef": null }, { "name": "endLine", "type": "number", "optional": false, "description": "Line offset of the end of the stylesheet within the resource (zero based).", "typeRef": null }, { "name": "endColumn", "type": "number", "optional": false, "description": "Column offset of the end of the stylesheet within the resource (zero based).", "typeRef": null }, { "name": "loadingFailed", "type": "boolean", "optional": true, "description": "If the style sheet was loaded from a network resource, this indicates when the resource failed to load", "typeRef": null }]);
289
- inspectorBackend.registerType("CSS.CSSRule", [{ "name": "styleSheetId", "type": "string", "optional": true, "description": "The css style sheet identifier (absent for user agent stylesheet and user-specified stylesheet rules) this rule came from.", "typeRef": "CSS.StyleSheetId" }, { "name": "selectorList", "type": "object", "optional": false, "description": "Rule selector data.", "typeRef": "CSS.SelectorList" }, { "name": "nestingSelectors", "type": "array", "optional": true, "description": "Array of selectors from ancestor style rules, sorted by distance from the current rule.", "typeRef": "string" }, { "name": "origin", "type": "string", "optional": false, "description": "Parent stylesheet's origin.", "typeRef": "CSS.StyleSheetOrigin" }, { "name": "style", "type": "object", "optional": false, "description": "Associated style declaration.", "typeRef": "CSS.CSSStyle" }, { "name": "media", "type": "array", "optional": true, "description": "Media list array (for rules involving media queries). The array enumerates media queries starting with the innermost one, going outwards.", "typeRef": "CSS.CSSMedia" }, { "name": "containerQueries", "type": "array", "optional": true, "description": "Container query list array (for rules involving container queries). The array enumerates container queries starting with the innermost one, going outwards.", "typeRef": "CSS.CSSContainerQuery" }, { "name": "supports", "type": "array", "optional": true, "description": "@supports CSS at-rule array. The array enumerates @supports at-rules starting with the innermost one, going outwards.", "typeRef": "CSS.CSSSupports" }, { "name": "layers", "type": "array", "optional": true, "description": "Cascade layer array. Contains the layer hierarchy that this rule belongs to starting with the innermost layer and going outwards.", "typeRef": "CSS.CSSLayer" }, { "name": "scopes", "type": "array", "optional": true, "description": "@scope CSS at-rule array. The array enumerates @scope at-rules starting with the innermost one, going outwards.", "typeRef": "CSS.CSSScope" }, { "name": "ruleTypes", "type": "array", "optional": true, "description": "The array keeps the types of ancestor CSSRules from the innermost going outwards.", "typeRef": "CSS.CSSRuleType" }, { "name": "startingStyles", "type": "array", "optional": true, "description": "@starting-style CSS at-rule array. The array enumerates @starting-style at-rules starting with the innermost one, going outwards.", "typeRef": "CSS.CSSStartingStyle" }]);
289
+ inspectorBackend.registerType("CSS.CSSRule", [{ "name": "styleSheetId", "type": "string", "optional": true, "description": "The css style sheet identifier (absent for user agent stylesheet and user-specified stylesheet rules) this rule came from.", "typeRef": "CSS.StyleSheetId" }, { "name": "selectorList", "type": "object", "optional": false, "description": "Rule selector data.", "typeRef": "CSS.SelectorList" }, { "name": "nestingSelectors", "type": "array", "optional": true, "description": "Array of selectors from ancestor style rules, sorted by distance from the current rule.", "typeRef": "string" }, { "name": "origin", "type": "string", "optional": false, "description": "Parent stylesheet's origin.", "typeRef": "CSS.StyleSheetOrigin" }, { "name": "style", "type": "object", "optional": false, "description": "Associated style declaration.", "typeRef": "CSS.CSSStyle" }, { "name": "originTreeScopeNodeId", "type": "number", "optional": true, "description": "The BackendNodeId of the DOM node that constitutes the origin tree scope of this rule.", "typeRef": "DOM.BackendNodeId" }, { "name": "media", "type": "array", "optional": true, "description": "Media list array (for rules involving media queries). The array enumerates media queries starting with the innermost one, going outwards.", "typeRef": "CSS.CSSMedia" }, { "name": "containerQueries", "type": "array", "optional": true, "description": "Container query list array (for rules involving container queries). The array enumerates container queries starting with the innermost one, going outwards.", "typeRef": "CSS.CSSContainerQuery" }, { "name": "supports", "type": "array", "optional": true, "description": "@supports CSS at-rule array. The array enumerates @supports at-rules starting with the innermost one, going outwards.", "typeRef": "CSS.CSSSupports" }, { "name": "layers", "type": "array", "optional": true, "description": "Cascade layer array. Contains the layer hierarchy that this rule belongs to starting with the innermost layer and going outwards.", "typeRef": "CSS.CSSLayer" }, { "name": "scopes", "type": "array", "optional": true, "description": "@scope CSS at-rule array. The array enumerates @scope at-rules starting with the innermost one, going outwards.", "typeRef": "CSS.CSSScope" }, { "name": "ruleTypes", "type": "array", "optional": true, "description": "The array keeps the types of ancestor CSSRules from the innermost going outwards.", "typeRef": "CSS.CSSRuleType" }, { "name": "startingStyles", "type": "array", "optional": true, "description": "@starting-style CSS at-rule array. The array enumerates @starting-style at-rules starting with the innermost one, going outwards.", "typeRef": "CSS.CSSStartingStyle" }]);
290
290
  inspectorBackend.registerType("CSS.RuleUsage", [{ "name": "styleSheetId", "type": "string", "optional": false, "description": "The css style sheet identifier (absent for user agent stylesheet and user-specified stylesheet rules) this rule came from.", "typeRef": "CSS.StyleSheetId" }, { "name": "startOffset", "type": "number", "optional": false, "description": "Offset of the start of the rule (including selector) from the beginning of the stylesheet.", "typeRef": null }, { "name": "endOffset", "type": "number", "optional": false, "description": "Offset of the end of the rule body from the beginning of the stylesheet.", "typeRef": null }, { "name": "used", "type": "boolean", "optional": false, "description": "Indicates whether the rule was actually used by some element in the page.", "typeRef": null }]);
291
291
  inspectorBackend.registerType("CSS.SourceRange", [{ "name": "startLine", "type": "number", "optional": false, "description": "Start line of range.", "typeRef": null }, { "name": "startColumn", "type": "number", "optional": false, "description": "Start column of range (inclusive).", "typeRef": null }, { "name": "endLine", "type": "number", "optional": false, "description": "End line of range", "typeRef": null }, { "name": "endColumn", "type": "number", "optional": false, "description": "End column of range (exclusive).", "typeRef": null }]);
292
292
  inspectorBackend.registerType("CSS.ShorthandEntry", [{ "name": "name", "type": "string", "optional": false, "description": "Shorthand name.", "typeRef": null }, { "name": "value", "type": "string", "optional": false, "description": "Shorthand value.", "typeRef": null }, { "name": "important", "type": "boolean", "optional": true, "description": "Whether the property has \\\"!important\\\" annotation (implies `false` if absent).", "typeRef": null }]);
@@ -835,7 +835,7 @@ export function registerCommands(inspectorBackend) {
835
835
  inspectorBackend.registerCommand("Network.takeResponseBodyForInterceptionAsStream", [{ "name": "interceptionId", "type": "string", "optional": false, "description": "", "typeRef": "Network.InterceptionId" }], ["stream"], "Returns a handle to the stream representing the response body. Note that after this command, the intercepted request can't be continued as is -- you either need to cancel it or to provide the response body. The stream only supports sequential read, IO.read will fail if the position is specified.");
836
836
  inspectorBackend.registerCommand("Network.replayXHR", [{ "name": "requestId", "type": "string", "optional": false, "description": "Identifier of XHR to replay.", "typeRef": "Network.RequestId" }], [], "This method sends a new XMLHttpRequest which is identical to the original one. The following parameters should be identical: method, url, async, request body, extra headers, withCredentials attribute, user, password.");
837
837
  inspectorBackend.registerCommand("Network.searchInResponseBody", [{ "name": "requestId", "type": "string", "optional": false, "description": "Identifier of the network response to search.", "typeRef": "Network.RequestId" }, { "name": "query", "type": "string", "optional": false, "description": "String to search for.", "typeRef": null }, { "name": "caseSensitive", "type": "boolean", "optional": true, "description": "If true, search is case sensitive.", "typeRef": null }, { "name": "isRegex", "type": "boolean", "optional": true, "description": "If true, treats string parameter as regex.", "typeRef": null }], ["result"], "Searches for given string in response content.");
838
- inspectorBackend.registerCommand("Network.setBlockedURLs", [{ "name": "urls", "type": "array", "optional": false, "description": "URL patterns to block. Wildcards ('*') are allowed.", "typeRef": "string" }], [], "Blocks URLs from loading.");
838
+ inspectorBackend.registerCommand("Network.setBlockedURLs", [{ "name": "urlPatterns", "type": "array", "optional": true, "description": "URL patterns to block. Patterns use the URLPattern constructor string syntax (https://urlpattern.spec.whatwg.org/). Example: `*://*:*/*.css`.", "typeRef": "string" }, { "name": "urls", "type": "array", "optional": true, "description": "URL patterns to block. Wildcards ('*') are allowed.", "typeRef": "string" }], [], "Blocks URLs from loading.");
839
839
  inspectorBackend.registerCommand("Network.setBypassServiceWorker", [{ "name": "bypass", "type": "boolean", "optional": false, "description": "Bypass service worker and load from network.", "typeRef": null }], [], "Toggles ignoring of service worker for each request.");
840
840
  inspectorBackend.registerCommand("Network.setCacheDisabled", [{ "name": "cacheDisabled", "type": "boolean", "optional": false, "description": "Cache disabled state.", "typeRef": null }], [], "Toggles ignoring cache for each request. If `true`, cache will not be used.");
841
841
  inspectorBackend.registerCommand("Network.setCookie", [{ "name": "name", "type": "string", "optional": false, "description": "Cookie name.", "typeRef": null }, { "name": "value", "type": "string", "optional": false, "description": "Cookie value.", "typeRef": null }, { "name": "url", "type": "string", "optional": true, "description": "The request-URI to associate with the setting of the cookie. This value can affect the default domain, path, source port, and source scheme values of the created cookie.", "typeRef": null }, { "name": "domain", "type": "string", "optional": true, "description": "Cookie domain.", "typeRef": null }, { "name": "path", "type": "string", "optional": true, "description": "Cookie path.", "typeRef": null }, { "name": "secure", "type": "boolean", "optional": true, "description": "True if cookie is secure.", "typeRef": null }, { "name": "httpOnly", "type": "boolean", "optional": true, "description": "True if cookie is http-only.", "typeRef": null }, { "name": "sameSite", "type": "string", "optional": true, "description": "Cookie SameSite type.", "typeRef": "Network.CookieSameSite" }, { "name": "expires", "type": "number", "optional": true, "description": "Cookie expiration date, session cookie if not set", "typeRef": "Network.TimeSinceEpoch" }, { "name": "priority", "type": "string", "optional": true, "description": "Cookie Priority type.", "typeRef": "Network.CookiePriority" }, { "name": "sameParty", "type": "boolean", "optional": true, "description": "True if cookie is SameParty.", "typeRef": null }, { "name": "sourceScheme", "type": "string", "optional": true, "description": "Cookie source scheme type.", "typeRef": "Network.CookieSourceScheme" }, { "name": "sourcePort", "type": "number", "optional": true, "description": "Cookie source port. Valid values are {-1, [1, 65535]}, -1 indicates an unspecified port. An unspecified port value allows protocol clients to emulate legacy cookie scope for the port. This is a temporary ability and it will be removed in the future.", "typeRef": null }, { "name": "partitionKey", "type": "object", "optional": true, "description": "Cookie partition key. If not set, the cookie will be set as not partitioned.", "typeRef": "Network.CookiePartitionKey" }], ["success"], "Sets a cookie with the given cookie data; may overwrite equivalent cookies if they exist.");
@@ -1221,7 +1221,8 @@ export function registerCommands(inspectorBackend) {
1221
1221
  inspectorBackend.registerEvent("Storage.attributionReportingTriggerRegistered", ["registration", "eventLevel", "aggregatable"]);
1222
1222
  inspectorBackend.registerEvent("Storage.attributionReportingReportSent", ["url", "body", "result", "netError", "netErrorName", "httpStatusCode"]);
1223
1223
  inspectorBackend.registerEvent("Storage.attributionReportingVerboseDebugReportSent", ["url", "body", "netError", "netErrorName", "httpStatusCode"]);
1224
- inspectorBackend.registerCommand("Storage.getStorageKeyForFrame", [{ "name": "frameId", "type": "string", "optional": false, "description": "", "typeRef": "Page.FrameId" }], ["storageKey"], "Returns a storage key given a frame id.");
1224
+ inspectorBackend.registerCommand("Storage.getStorageKeyForFrame", [{ "name": "frameId", "type": "string", "optional": false, "description": "", "typeRef": "Page.FrameId" }], ["storageKey"], "Returns a storage key given a frame id. Deprecated. Please use Storage.getStorageKey instead.");
1225
+ inspectorBackend.registerCommand("Storage.getStorageKey", [{ "name": "frameId", "type": "string", "optional": true, "description": "", "typeRef": "Page.FrameId" }], ["storageKey"], "Returns storage key for the given frame. If no frame ID is provided, the storage key of the target executing this command is returned.");
1225
1226
  inspectorBackend.registerCommand("Storage.clearDataForOrigin", [{ "name": "origin", "type": "string", "optional": false, "description": "Security origin.", "typeRef": null }, { "name": "storageTypes", "type": "string", "optional": false, "description": "Comma separated list of StorageType to clear.", "typeRef": null }], [], "Clears storage for origin.");
1226
1227
  inspectorBackend.registerCommand("Storage.clearDataForStorageKey", [{ "name": "storageKey", "type": "string", "optional": false, "description": "Storage key.", "typeRef": null }, { "name": "storageTypes", "type": "string", "optional": false, "description": "Comma separated list of StorageType to clear.", "typeRef": null }], [], "Clears storage for storage key.");
1227
1228
  inspectorBackend.registerCommand("Storage.getCookies", [{ "name": "browserContextId", "type": "string", "optional": true, "description": "Browser context to use when called on the browser endpoint.", "typeRef": "Browser.BrowserContextID" }], ["cookies"], "Returns all browser cookies.");
@@ -4,8 +4,10 @@
4
4
  var _a;
5
5
  import * as Logs from '../../logs/logs.js';
6
6
  import * as NetworkTimeCalculator from '../../network_time_calculator/network_time_calculator.js';
7
+ import * as TextUtils from '../../text_utils/text_utils.js';
7
8
  import { seconds } from './UnitFormatters.js';
8
9
  const MAX_HEADERS_SIZE = 1000;
10
+ const MAX_BODY_SIZE = 10000;
9
11
  /**
10
12
  * Sanitizes the set of headers, removing values that are not on the allow-list and replacing them with '<redacted>'.
11
13
  */
@@ -19,6 +21,7 @@ function sanitizeHeaders(headers) {
19
21
  }
20
22
  export class NetworkRequestFormatter {
21
23
  #calculator;
24
+ #request;
22
25
  static allowHeader(headerName) {
23
26
  return allowedHeaders.has(headerName.toLowerCase().trim());
24
27
  }
@@ -28,6 +31,23 @@ export class NetworkRequestFormatter {
28
31
  return prefix + header.name + ': ' + header.value + '\n';
29
32
  }), MAX_HEADERS_SIZE);
30
33
  }
34
+ static async formatBody(title, request, maxBodySize) {
35
+ const data = await request.requestContentData();
36
+ if (TextUtils.ContentData.ContentData.isError(data)) {
37
+ return '';
38
+ }
39
+ if (data.isEmpty) {
40
+ return `${title}\n<empty response>`;
41
+ }
42
+ if (data.isTextContent) {
43
+ const dataAsText = data.text;
44
+ if (dataAsText.length > maxBodySize) {
45
+ return `${title}\n${dataAsText.substring(0, maxBodySize) + '... <truncated>'}`;
46
+ }
47
+ return `${title}\n${dataAsText}`;
48
+ }
49
+ return `${title}\n<binary data>`;
50
+ }
31
51
  static formatInitiatorUrl(initiatorUrl, allowedOrigin) {
32
52
  const initiatorOrigin = new URL(initiatorUrl).origin;
33
53
  if (initiatorOrigin === allowedOrigin) {
@@ -35,7 +55,6 @@ export class NetworkRequestFormatter {
35
55
  }
36
56
  return '<redacted cross-origin initiator URL>';
37
57
  }
38
- #request;
39
58
  constructor(request, calculator) {
40
59
  this.#request = request;
41
60
  this.#calculator = calculator;
@@ -46,16 +65,24 @@ export class NetworkRequestFormatter {
46
65
  formatResponseHeaders() {
47
66
  return _a.formatHeaders('Response headers:', this.#request.responseHeaders);
48
67
  }
68
+ async formatResponseBody() {
69
+ return await _a.formatBody('Response body:', this.#request, MAX_BODY_SIZE);
70
+ }
49
71
  /**
50
72
  * Note: nothing here should include information from origins other than
51
73
  * the request's origin.
52
74
  */
53
- formatNetworkRequest() {
75
+ async formatNetworkRequest() {
76
+ let responseBody = await this.formatResponseBody();
77
+ if (responseBody) {
78
+ // if we have a response then we add 2 new line to follow same structure of the context
79
+ responseBody = `\n\n${responseBody}`;
80
+ }
54
81
  return `Request: ${this.#request.url()}
55
82
 
56
83
  ${this.formatRequestHeaders()}
57
84
 
58
- ${this.formatResponseHeaders()}
85
+ ${this.formatResponseHeaders()}${responseBody}
59
86
 
60
87
  Response status: ${this.#request.statusCode} ${this.#request.statusText}
61
88
 
@@ -3,11 +3,25 @@
3
3
  // found in the LICENSE file.
4
4
  import * as Trace from '../../../models/trace/trace.js';
5
5
  import { AICallTree } from './AICallTree.js';
6
+ /**
7
+ * Gets the first, most relevant InsightSet to use, following the logic of:
8
+ * 1. If there is only one InsightSet, use that.
9
+ * 2. If there are more, prefer the first we find that has a navigation associated with it.
10
+ * 3. If none with a navigation are found, fallback to the first one.
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
+ */
6
16
  function getFirstInsightSet(insights) {
7
- // Currently only support a single insight set. Pick the first one with a navigation.
8
- // TODO(cjamcl): we should just give the agent the entire insight set, and give
9
- // summary detail about all of them + the ability to query each.
10
- return [...insights.values()].filter(insightSet => insightSet.navigation).at(0) ?? null;
17
+ const insightSets = Array.from(insights.values());
18
+ if (insightSets.length === 0) {
19
+ return null;
20
+ }
21
+ if (insightSets.length === 1) {
22
+ return insightSets[0];
23
+ }
24
+ return insightSets.filter(set => set.navigation).at(0) ?? insightSets.at(0) ?? null;
11
25
  }
12
26
  export class AgentFocus {
13
27
  static fromParsedTrace(parsedTrace) {
@@ -19,7 +19,7 @@ const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
19
19
  const CRUX_API_KEY = 'AIzaSyCCSOx25vrb5z0tbedCB3_JRzzbVW6Uwgw';
20
20
  const DEFAULT_ENDPOINT = `https://chromeuxreport.googleapis.com/v1/records:queryRecord?key=${CRUX_API_KEY}`;
21
21
  let cruxManagerInstance;
22
- // TODO: Potentially support `TABLET`. Tablet field data will always be `null` until then.
22
+ /** TODO: Potentially support `TABLET`. Tablet field data will always be `null` until then. **/
23
23
  export const DEVICE_SCOPE_LIST = ['ALL', 'DESKTOP', 'PHONE'];
24
24
  const pageScopeList = ['origin', 'url'];
25
25
  const metrics = [
@@ -36,10 +36,12 @@ export function calculateRequestTimeRanges(request, navigationStart) {
36
36
  addRange(name, startTime + (start / 1000), startTime + (end / 1000));
37
37
  }
38
38
  }
39
- // In some situations, argument `start` may come before `startTime` (`timing.requestStart`). This is especially true
40
- // in cases such as SW static routing API where fields like `workerRouterEvaluationStart` or `workerCacheLookupStart`
41
- // is set before setting `timing.requestStart`. If the `start` and `end` is known to be a valid value (i.e. not default
42
- // invalid value -1 or undefined), we allow adding the range.
39
+ /**
40
+ * In some situations, argument `start` may come before `startTime` (`timing.requestStart`). This is especially true
41
+ * in cases such as SW static routing API where fields like `workerRouterEvaluationStart` or `workerCacheLookupStart`
42
+ * is set before setting `timing.requestStart`. If the `start` and `end` is known to be a valid value (i.e. not default
43
+ * invalid value -1 or undefined), we allow adding the range.
44
+ **/
43
45
  function addMaybeNegativeOffsetRange(name, start, end) {
44
46
  addRange(name, startTime + (start / 1000), startTime + (end / 1000));
45
47
  }
@@ -182,7 +182,7 @@ const resolveScope = async (script, scopeChain) => {
182
182
  return;
183
183
  }
184
184
  }
185
- // If there is no entry with the name field, try to infer the name from the source positions.
185
+ /** If there is no entry with the name field, try to infer the name from the source positions. **/
186
186
  async function resolvePosition() {
187
187
  if (!sourceMap) {
188
188
  return;
@@ -556,10 +556,12 @@ export class RemoteObject extends SDK.RemoteObject.RemoteObject {
556
556
  return this.object.isNode();
557
557
  }
558
558
  }
559
- // Resolve the frame's function name using the name associated with the opening
560
- // paren that starts the scope. If there is no name associated with the scope
561
- // start or if the function scope does not start with a left paren (e.g., arrow
562
- // function with one parameter), the resolution returns null.
559
+ /**
560
+ * Resolve the frame's function name using the name associated with the opening
561
+ * paren that starts the scope. If there is no name associated with the scope
562
+ * start or if the function scope does not start with a left paren (e.g., arrow
563
+ * function with one parameter), the resolution returns null.
564
+ **/
563
565
  async function getFunctionNameFromScopeStart(script, lineNumber, columnNumber) {
564
566
  // To reduce the overhead of resolving function names,
565
567
  // we check for source maps first and immediately leave
@@ -167,6 +167,7 @@ function createLanternRequest(parsedTrace, workerThreads, request) {
167
167
  priority: request.args.data.priority,
168
168
  frameId: request.args.data.frame,
169
169
  fromWorker,
170
+ serverResponseTime: request.args.data.lrServerResponseTime ?? undefined,
170
171
  // Set later.
171
172
  redirects: undefined,
172
173
  redirectSource: undefined,
@@ -573,7 +573,7 @@ export function eventStackFrame(event) {
573
573
  }
574
574
  return { ...topFrame, scriptId: String(topFrame.scriptId) };
575
575
  }
576
- // TODO(paulirish): rename to generateNodeId
576
+ /** TODO(paulirish): rename to generateNodeId **/
577
577
  export function generateEventID(event) {
578
578
  if (Types.Events.isProfileCall(event)) {
579
579
  const name = SamplesIntegrator.isNativeRuntimeFrame(event.callFrame) ?
@@ -410,7 +410,7 @@ export class PendingFrame {
410
410
  this.triggerTime = triggerTime;
411
411
  }
412
412
  }
413
- // The parameters of an impl-side BeginFrame.
413
+ /** The parameters of an impl-side BeginFrame. **/
414
414
  class BeginFrameInfo {
415
415
  seqId;
416
416
  startTime;
@@ -423,10 +423,12 @@ class BeginFrameInfo {
423
423
  this.isPartial = isPartial;
424
424
  }
425
425
  }
426
- // A queue of BeginFrames pending visualization.
427
- // BeginFrames are added into this queue as they occur; later when their
428
- // corresponding DrawFrames occur (or lack thereof), the BeginFrames are removed
429
- // from the queue and their timestamps are used for visualization.
426
+ /**
427
+ * A queue of BeginFrames pending visualization.
428
+ * BeginFrames are added into this queue as they occur; later when their
429
+ * corresponding DrawFrames occur (or lack thereof), the BeginFrames are removed
430
+ * from the queue and their timestamps are used for visualization.
431
+ **/
430
432
  export class TimelineFrameBeginFrameQueue {
431
433
  queueFrames = [];
432
434
  // Maps frameSeqId to BeginFrameInfo.
@@ -6,11 +6,15 @@ import * as Helpers from '../helpers/helpers.js';
6
6
  import * as Types from '../types/types.js';
7
7
  import { data as metaHandlerData } from './MetaHandler.js';
8
8
  import { data as screenshotsHandlerData } from './ScreenshotsHandler.js';
9
- // This represents the maximum #time we will allow a cluster to go before we
10
- // reset it.
9
+ /**
10
+ * This represents the maximum #time we will allow a cluster to go before we
11
+ * reset it.
12
+ **/
11
13
  export const MAX_CLUSTER_DURATION = Helpers.Timing.milliToMicro(Types.Timing.Milli(5000));
12
- // This represents the maximum #time we will allow between layout shift events
13
- // before considering it to be the start of a new cluster.
14
+ /**
15
+ * This represents the maximum #time we will allow between layout shift events
16
+ * before considering it to be the start of a new cluster.
17
+ **/
14
18
  export const MAX_SHIFT_TIME_DELTA = Helpers.Timing.milliToMicro(Types.Timing.Milli(1000));
15
19
  // Layout shifts are reported globally to the developer, irrespective of which
16
20
  // frame they originated in. However, each process does have its own individual
@@ -221,6 +221,7 @@ export async function finalize() {
221
221
  *
222
222
  * See `_updateTimingsForLightrider` in Lighthouse for more detail.
223
223
  */
224
+ let lrServerResponseTime;
224
225
  if (isLightrider && request.receiveResponse?.args.data.headers) {
225
226
  timing = {
226
227
  requestTime: Helpers.Timing.microToSeconds(request.sendRequests.at(0)?.ts ?? 0),
@@ -254,6 +255,13 @@ export async function finalize() {
254
255
  timing.connectEnd = TCPMs;
255
256
  timing.sslEnd = TCPMs;
256
257
  }
258
+ // Lightrider does not have any equivalent for `sendEnd` timing values. The
259
+ // closest we can get to the server response time is from a header that
260
+ // Lightrider sets.
261
+ const ResponseMsHeader = request.receiveResponse.args.data.headers.find(h => h.name === 'X-ResponseMs');
262
+ if (ResponseMsHeader) {
263
+ lrServerResponseTime = Math.max(0, parseInt(ResponseMsHeader.value, 10));
264
+ }
257
265
  }
258
266
  // TODO: consider allowing chrome / about.
259
267
  const allowedProtocols = [
@@ -346,6 +354,13 @@ export async function finalize() {
346
354
  const waiting = timing ?
347
355
  Types.Timing.Micro((timing.receiveHeadersEnd - timing.sendEnd) * MILLISECONDS_TO_MICROSECONDS) :
348
356
  Types.Timing.Micro(0);
357
+ // Server Response Time
358
+ // =======================
359
+ // Time from when the send finished going to when the first byte of headers were received.
360
+ const serverResponseTime = timing ?
361
+ Types.Timing.Micro(((timing.receiveHeadersStart ?? timing.receiveHeadersEnd) - timing.sendEnd) *
362
+ MILLISECONDS_TO_MICROSECONDS) :
363
+ Types.Timing.Micro(0);
349
364
  // Download
350
365
  // =======================
351
366
  // Time from receipt of headers to the finish time.
@@ -404,6 +419,7 @@ export async function finalize() {
404
419
  stalled,
405
420
  totalTime,
406
421
  waiting,
422
+ serverResponseTime,
407
423
  },
408
424
  // All fields below are from TraceEventsForNetworkRequest.
409
425
  decodedBodyLength,
@@ -428,6 +444,7 @@ export async function finalize() {
428
444
  initiator: finalSendRequest.args.data.initiator,
429
445
  stackTrace: finalSendRequest.args.data.stackTrace,
430
446
  timing,
447
+ lrServerResponseTime,
431
448
  url,
432
449
  failed: request.resourceFinish?.args.data.didFail ?? false,
433
450
  finished: Boolean(request.resourceFinish),
@@ -17,7 +17,7 @@ export function handleEvent(event) {
17
17
  const getOrMakeScript = (isolate, scriptIdAsNumber) => {
18
18
  const scriptId = String(scriptIdAsNumber);
19
19
  const key = `${isolate}.${scriptId}`;
20
- return Platform.MapUtilities.getWithDefault(scriptById, key, () => ({ isolate, scriptId, frame: '', ts: 0 }));
20
+ return Platform.MapUtilities.getWithDefault(scriptById, key, () => ({ isolate, scriptId, frame: '', ts: event.ts }));
21
21
  };
22
22
  if (Types.Events.isRundownScriptCompiled(event) && event.args.data) {
23
23
  const { isolate, scriptId, frame } = event.args.data;
@@ -30,6 +30,7 @@ export function handleEvent(event) {
30
30
  const { isolate, scriptId, url, sourceUrl, sourceMapUrl, sourceMapUrlElided } = event.args.data;
31
31
  const script = getOrMakeScript(isolate, scriptId);
32
32
  script.url = url;
33
+ script.ts = event.ts;
33
34
  if (sourceUrl) {
34
35
  script.sourceUrl = sourceUrl;
35
36
  }
@@ -142,7 +142,7 @@ export function addEventToEntityMapping(event, entityMappings) {
142
142
  }
143
143
  entityMappings.entityByEvent.set(event, entity);
144
144
  }
145
- // A slight upgrade of addEventToEntityMapping to handle the sub-events of a network request.
145
+ /** A slight upgrade of addEventToEntityMapping to handle the sub-events of a network request. **/
146
146
  export function addNetworkRequestToEntityMapping(networkRequest, entityMappings, requestTraceEvents) {
147
147
  const entity = getEntityForEvent(networkRequest, entityMappings);
148
148
  if (!entity) {
@@ -25,8 +25,10 @@ export function timeStampForEventAdjustedByClosestNavigation(event, traceBounds,
25
25
  }
26
26
  return Types.Timing.Micro(eventTimeStamp);
27
27
  }
28
- // Expands the trace window by a provided percentage or, if it the expanded window is smaller than 1 millisecond, expands it to 1 millisecond.
29
- // If the expanded window is outside of the max trace window, cut the overflowing bound to the max trace window bound.
28
+ /**
29
+ * Expands the trace window by a provided percentage or, if it the expanded window is smaller than 1 millisecond, expands it to 1 millisecond.
30
+ * If the expanded window is outside of the max trace window, cut the overflowing bound to the max trace window bound.
31
+ **/
30
32
  export function expandWindowByPercentOrToOneMillisecond(annotationWindow, maxTraceWindow, percentage) {
31
33
  // Expand min and max of the window by half of the provided percentage. That way, in total, the window will be expanded by the provided percentage.
32
34
  let newMin = annotationWindow.min - annotationWindow.range * (percentage / 100) / 2;
@@ -70,8 +70,10 @@ export function extractOriginFromTrace(firstNavigationURL) {
70
70
  }
71
71
  return null;
72
72
  }
73
- // Each thread contains events. Events indicate the thread and process IDs, which are
74
- // used to store the event in the correct process thread entry below.
73
+ /**
74
+ * Each thread contains events. Events indicate the thread and process IDs, which are
75
+ * used to store the event in the correct process thread entry below.
76
+ **/
75
77
  export function addEventToProcessThread(event, eventsInProcessThread) {
76
78
  const { tid, pid } = event;
77
79
  let eventsInThread = eventsInProcessThread.get(pid);
@@ -704,8 +706,10 @@ export function extractSampleTraceId(event) {
704
706
  }
705
707
  return event.args?.sampleTraceId ?? event.args?.data?.sampleTraceId ?? null;
706
708
  }
707
- // This exactly matches Trace.Styles.visibleTypes. See the runtime verification in maybeInitStylesMap.
708
- // TODO(crbug.com/410884528)
709
+ /**
710
+ * This exactly matches Trace.Styles.visibleTypes. See the runtime verification in maybeInitStylesMap.
711
+ * TODO(crbug.com/410884528)
712
+ **/
709
713
  export const VISIBLE_TRACE_EVENT_TYPES = new Set([
710
714
  "AbortPostTaskCallback" /* Types.Events.Name.ABORT_POST_TASK_CALLBACK */,
711
715
  "Animation" /* Types.Events.Name.ANIMATION */,