chrome-devtools-frontend 1.0.1643099 → 1.0.1645245

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 (99) hide show
  1. package/eslint.config.mjs +3 -1
  2. package/extension-api/ExtensionAPI.d.ts +83 -12
  3. package/front_end/core/host/UserMetrics.ts +3 -3
  4. package/front_end/core/root/ExperimentNames.ts +0 -1
  5. package/front_end/core/sdk/CSSPropertyParserMatchers.ts +2 -3
  6. package/front_end/core/sdk/ConsoleModel.ts +4 -0
  7. package/front_end/core/sdk/NetworkRequest.ts +12 -1
  8. package/front_end/core/sdk/SourceMap.ts +15 -18
  9. package/front_end/entrypoints/greendev_floaty/FloatyEntrypoint.ts +0 -2
  10. package/front_end/entrypoints/greendev_floaty/greendev_floaty.ts +0 -2
  11. package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshot.ts +37 -0
  12. package/front_end/entrypoints/main/MainImpl.ts +0 -6
  13. package/front_end/generated/SupportedCSSProperties.js +4 -2
  14. package/front_end/models/ai_assistance/AiAgent2.ts +23 -12
  15. package/front_end/models/ai_assistance/README.md +5 -4
  16. package/front_end/models/ai_assistance/agents/AccessibilityAgent.ts +22 -16
  17. package/front_end/models/ai_assistance/agents/AiAgent.ts +19 -6
  18. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +5 -5
  19. package/front_end/models/ai_assistance/agents/ExecuteJavascript.ts +1 -94
  20. package/front_end/models/ai_assistance/agents/NetworkAgent.ts +25 -0
  21. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +37 -0
  22. package/front_end/models/ai_assistance/agents/README.md +57 -0
  23. package/front_end/models/ai_assistance/agents/StorageAgent.ts +54 -1
  24. package/front_end/models/ai_assistance/agents/StylingAgent.snapshot.txt +1 -2
  25. package/front_end/models/ai_assistance/agents/StylingAgent.ts +31 -3
  26. package/front_end/models/ai_assistance/tools/ExecuteJavaScript.ts +12 -21
  27. package/front_end/models/ai_assistance/tools/GetStyles.ts +19 -12
  28. package/front_end/models/ai_assistance/tools/README.md +45 -0
  29. package/front_end/models/ai_assistance/tools/Tool.ts +78 -9
  30. package/front_end/models/ai_assistance/tools/ToolRegistry.ts +21 -5
  31. package/front_end/models/bindings/DebuggerWorkspaceBinding.ts +6 -9
  32. package/front_end/models/bindings/DefaultScriptMapping.ts +2 -1
  33. package/front_end/models/bindings/SymbolizedError.ts +45 -35
  34. package/front_end/models/extensions/ExtensionAPI.ts +138 -47
  35. package/front_end/models/har/Importer.ts +1 -0
  36. package/front_end/models/heap_snapshot/HeapSnapshotModel.ts +18 -2
  37. package/front_end/models/heap_snapshot/HeapSnapshotProxy.ts +4 -0
  38. package/front_end/models/source_map_scopes/FunctionCodeResolver.ts +12 -2
  39. package/front_end/models/stack_trace/DetailedErrorStackParser.ts +58 -52
  40. package/front_end/models/stack_trace/StackTrace.ts +7 -0
  41. package/front_end/models/stack_trace/StackTraceImpl.ts +13 -4
  42. package/front_end/models/stack_trace/StackTraceModel.ts +43 -9
  43. package/front_end/models/trace/Styles.ts +29 -7
  44. package/front_end/models/trace/handlers/PageLoadMetricsHandler.ts +33 -4
  45. package/front_end/models/trace/helpers/Timing.ts +10 -0
  46. package/front_end/models/trace/types/TraceEvents.ts +22 -2
  47. package/front_end/panels/accessibility/AccessibilitySidebarView.ts +2 -1
  48. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +9 -2
  49. package/front_end/panels/ai_assistance/ai_assistance-meta.ts +16 -0
  50. package/front_end/panels/application/ApplicationPanelSidebar.ts +83 -0
  51. package/front_end/panels/application/ApplicationPanelTreeElement.ts +39 -0
  52. package/front_end/panels/application/CookieItemsView.ts +2 -2
  53. package/front_end/panels/application/ServiceWorkersView.ts +2 -2
  54. package/front_end/panels/application/WebMCPView.ts +0 -1
  55. package/front_end/panels/application/components/BackForwardCacheView.ts +1 -2
  56. package/front_end/panels/application/resourcesSidebar.css +11 -0
  57. package/front_end/panels/console/ConsoleView.ts +6 -1
  58. package/front_end/panels/console/ConsoleViewMessage.ts +46 -213
  59. package/front_end/panels/console/SymbolizedErrorWidget.ts +14 -8
  60. package/front_end/panels/elements/AdoptedStyleSheetTreeElement.ts +0 -1
  61. package/front_end/panels/elements/ElementsTreeElement.ts +0 -2
  62. package/front_end/panels/elements/PropertyRenderer.ts +0 -1
  63. package/front_end/panels/elements/StylesSidebarPane.ts +9 -2
  64. package/front_end/panels/issues/AffectedResourcesView.ts +1 -1
  65. package/front_end/panels/issues/AffectedSourcesView.ts +1 -1
  66. package/front_end/panels/lighthouse/LighthouseReportRenderer.ts +0 -1
  67. package/front_end/panels/mobile_throttling/ThrottlingSettingsTab.ts +10 -0
  68. package/front_end/panels/network/NetworkDataGridNode.ts +1 -2
  69. package/front_end/panels/network/NetworkLogView.ts +34 -7
  70. package/front_end/panels/profiler/HeapProfileView.ts +0 -1
  71. package/front_end/panels/profiler/HeapSnapshotGridNodes.ts +0 -1
  72. package/front_end/panels/profiler/HeapSnapshotView.ts +1 -1
  73. package/front_end/panels/settings/components/SyncSection.ts +1 -1
  74. package/front_end/panels/settings/emulation/DevicesSettingsTab.ts +1 -0
  75. package/front_end/panels/sources/SourcesPanel.ts +2 -1
  76. package/front_end/panels/timeline/TimelineFlameChartView.ts +5 -4
  77. package/front_end/panels/timeline/TimelinePanel.ts +7 -0
  78. package/front_end/panels/timeline/TimelineUIUtils.ts +13 -14
  79. package/front_end/panels/timeline/TimingsTrackAppender.ts +7 -5
  80. package/front_end/panels/timeline/components/LayoutShiftDetails.ts +0 -1
  81. package/front_end/panels/timeline/components/NetworkRequestDetails.ts +0 -2
  82. package/front_end/panels/timeline/components/insights/ForcedReflow.ts +0 -1
  83. package/front_end/panels/timeline/components/insights/NodeLink.ts +0 -1
  84. package/front_end/panels/timeline/overlays/OverlaysImpl.ts +2 -0
  85. package/front_end/third_party/chromium/README.chromium +1 -1
  86. package/front_end/ui/helpers/OpenInNewTab.ts +3 -3
  87. package/front_end/ui/kit/link/Link.ts +16 -2
  88. package/front_end/ui/legacy/InspectorDrawerView.ts +14 -5
  89. package/front_end/ui/legacy/InspectorView.ts +4 -1
  90. package/front_end/ui/legacy/PlusButton.ts +6 -1
  91. package/front_end/ui/legacy/StackedPane.ts +229 -0
  92. package/front_end/ui/legacy/ViewManager.ts +59 -169
  93. package/front_end/ui/legacy/Widget.ts +19 -1
  94. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +95 -31
  95. package/front_end/ui/legacy/components/utils/JSPresentationUtils.ts +0 -1
  96. package/front_end/ui/legacy/components/utils/Linkifier.ts +2 -16
  97. package/front_end/ui/legacy/legacy.ts +3 -1
  98. package/front_end/ui/visual_logging/KnownContextValues.ts +5 -0
  99. package/package.json +1 -1
@@ -0,0 +1,45 @@
1
+ # AI Assistant Tools Architecture
2
+
3
+ This directory defines the **Tools** used by `AiAgent2` and other AI agents to execute code, read page state, or apply mutations to the inspected page.
4
+
5
+ ## Core Concepts
6
+
7
+ The architecture relies on three primary concepts to guarantee 100% compile-time type safety for tools:
8
+ 1. **BaseTool**: An interface capturing non-generic tool metadata (e.g. `name`, `description`, `parameters`). This allows the agent and DevTools UI to generically store, register, and describe functions to the AIDA client without needing to know their specific types at runtime.
9
+ 2. **Capability Interfaces**: Narrow TypeScript interfaces wrapping individual DevTools dependencies (e.g., ChangeManager, executeJsCode). This decouples tools from monolithic agent environments, making them reusable and easier to unit test.
10
+ 3. **Tool**: A generic TypeScript interface (`Tool<Args, ReturnType, ContextType>`) binding the execution handler to specific capability requirements. This enforces compile-time safety, ensuring that a tool handler is only invoked by an agent that fulfills all the required capabilities.
11
+
12
+ ## Capability Interfaces
13
+
14
+ Instead of passing a monolithic "grab-bag" context object to all tool handlers, DevTools AI tools declare exactly the capabilities they require. Available capability interfaces defined in `Tool.ts` include:
15
+
16
+ - `BaseToolCapability`: Base interface providing access to the top-level conversation context step.
17
+ - `PageExecutionCapability`: For tools executing JavaScript code on the inspected page.
18
+ - `StyleMutationCapability`: For tools managing and applying style mutations via a `ChangeManager`.
19
+ - `TargetCapability`: For tools requiring access to the page's current SDK `Target`.
20
+ - `OriginLockCapability`: For tools enforcing cross-origin security via origin locks.
21
+
22
+ ### Unified Context
23
+
24
+ The Agent (e.g., `AiAgent2`) builds the complete dependency union (`AllToolsContext`) and fulfills all capabilities. When the handler is invoked, TypeScript validates that the provided context matches the intersection of capabilities requested by that tool.
25
+
26
+ ## Authoring a New Tool
27
+
28
+ To author a new tool:
29
+
30
+ 1. **Define the Args interface**: Extend `ToolArgs` to represent the JSON parameter schema expected by the LLM.
31
+ 2. **Declare Capability Dependencies**: Determine which capabilities your tool handler needs. Intersect them to define the tool's `ContextType`.
32
+ 3. **Implement the `Tool` interface**: Pass the args interface, return type, and intersected ContextType to the generic parameters of `Tool`.
33
+ 4. **Instantiate in `ToolRegistry.ts`**: Add the instantiated tool class to the static `TOOLS` registry.
34
+
35
+ See concrete, production examples of capability-based tools in this directory:
36
+ - [ExecuteJavaScript.ts](ExecuteJavaScript.ts): Demonstrates use of `PageExecutionCapability` and `StyleMutationCapability`.
37
+ - [GetStyles.ts](GetStyles.ts): Demonstrates use of `TargetCapability` and `OriginLockCapability`.
38
+
39
+ ## ToolRegistry Lookup
40
+
41
+ To retrieve a tool from the registry with 100% type safety, call `ToolRegistry.get()` with the literal `ToolName` key:
42
+
43
+ ```typescript
44
+ const getStylesTool = ToolRegistry.get(ToolName.GET_STYLES); // Returns GetStylesTool class type
45
+ ```
@@ -3,22 +3,82 @@
3
3
  // found in the LICENSE file.
4
4
 
5
5
  import type * as Host from '../../../core/host/host.js';
6
+ import type * as SDK from '../../../core/sdk/sdk.js';
6
7
  import type {ConversationContext, FunctionCallHandlerResult, FunctionHandlerOptions} from '../agents/AiAgent.js';
7
8
  import type {executeJsCode} from '../agents/ExecuteJavascript.js';
8
9
  import type {ChangeManager} from '../ChangeManager.js';
9
10
 
10
11
  /**
11
- * Context provided to the tool's handler execution.
12
+ * Base capability for all tool contexts, providing access to the conversation context.
12
13
  */
13
- export interface ToolContext {
14
+ export interface BaseToolCapability {
15
+ /**
16
+ * The active context for the current conversation step, if any.
17
+ */
14
18
  conversationContext: ConversationContext<unknown>|null;
15
- changeManager?: ChangeManager;
16
- createExtensionScope?: (changes: ChangeManager) => {
17
- install(): Promise<void>, uninstall(): Promise<void>,
19
+ }
20
+
21
+ /**
22
+ * Capability for tools that need to execute JavaScript code on the inspected page.
23
+ */
24
+ export interface PageExecutionCapability {
25
+ /**
26
+ * Function to execute JavaScript code in the page context.
27
+ */
28
+ execJs: typeof executeJsCode;
29
+
30
+ /**
31
+ * Returns the DOM node that acts as the execution context (i.e. `$0` inside the execution context)
32
+ * for running JavaScript.
33
+ */
34
+ getExecutionContextNode(): SDK.DOMModel.DOMNode|null;
35
+ }
36
+
37
+ /**
38
+ * Capability for tools that need to manage and apply style mutations to the page.
39
+ */
40
+ export interface StyleMutationCapability {
41
+ /**
42
+ * The change manager for tracking and applying style changes.
43
+ */
44
+ changeManager: ChangeManager;
45
+
46
+ /**
47
+ * Creates an extension scope for applying changes, ensuring they can be uninstalled when done.
48
+ */
49
+ createExtensionScope(changes: ChangeManager): {
50
+ install(): Promise<void>,
51
+ uninstall(): Promise<void>,
18
52
  };
19
- execJs?: typeof executeJsCode;
20
53
  }
21
54
 
55
+ /**
56
+ * Capability for tools that need access to the current SDK Target of the inspected page.
57
+ */
58
+ export interface TargetCapability {
59
+ /**
60
+ * Returns the current SDK Target for the inspected page.
61
+ */
62
+ getTarget(): SDK.Target.Target|null;
63
+ }
64
+
65
+ /**
66
+ * Capability for tools that need to enforce origin locking for security.
67
+ */
68
+ export interface OriginLockCapability {
69
+ /**
70
+ * Returns the origin that the current conversation is locked to, if any.
71
+ */
72
+ getEstablishedOrigin(): string|undefined;
73
+ }
74
+
75
+ /**
76
+ * Unified context interface providing all capabilities available in the project.
77
+ * Used by the agent to pass a complete context to any tool type-safely.
78
+ */
79
+ export type AllToolsContext =
80
+ BaseToolCapability&PageExecutionCapability&StyleMutationCapability&TargetCapability&OriginLockCapability;
81
+
22
82
  /**
23
83
  * Base argument type for AI Tools.
24
84
  */
@@ -44,21 +104,30 @@ export interface BaseTool {
44
104
 
45
105
  /**
46
106
  * Main generic interface for defining a Tool.
47
- * Binds the parameter schema properties and the handler implementation to a strict `Args` contract.
107
+ * Binds the parameter schema properties and the handler implementation to a strict `Args` and `ContextType` contract.
48
108
  *
49
109
  * @template Args - The expected object type for tool arguments. Must be an object type.
50
110
  * @template ReturnType - The type of data returned by the handler function.
111
+ * @template ContextType - The interface defining the capabilities this tool requires. Defaults to `BaseToolCapability`.
51
112
  */
52
- export interface Tool<Args extends ToolArgs = ToolArgs, ReturnType = unknown, > extends BaseTool {
113
+ export interface Tool<Args extends ToolArgs = ToolArgs, ReturnType = unknown,
114
+ ContextType extends BaseToolCapability = BaseToolCapability> extends BaseTool {
53
115
  readonly parameters: Host.AidaClient.FunctionObjectParam<keyof Args>;
54
116
  readonly displayInfoFromArgs?: (
55
117
  args: Args,
56
118
  ) => {
57
119
  title?: string, thought?: string, action?: string, suggestions?: [string, ...string[]],
58
120
  };
121
+ /**
122
+ * The implementation function called when the AI invokes this tool.
123
+ *
124
+ * @param args The arguments provided by the AI model matching the tool's parameter schema.
125
+ * @param context The context object providing the capabilities requested by `ContextType`.
126
+ * @param options Additional runtime options for the handler execution.
127
+ */
59
128
  handler(
60
129
  args: Args,
61
- context: ToolContext,
130
+ context: ContextType,
62
131
  options?: FunctionHandlerOptions,
63
132
  ): Promise<FunctionCallHandlerResult<ReturnType>>;
64
133
  }
@@ -4,11 +4,15 @@
4
4
 
5
5
  import {ExecuteJavaScriptTool} from './ExecuteJavaScript.js';
6
6
  import {GetStylesTool} from './GetStyles.js';
7
- import {type Tool, ToolName} from './Tool.js';
7
+ import {type AllToolsContext, type Tool, type ToolArgs, ToolName} from './Tool.js';
8
8
 
9
9
  /**
10
10
  * Plain object registry containing concrete instantiated tools.
11
- * Keep this type concrete (no type-erasure) to preserve exact tool types.
11
+ *
12
+ * This object is deliberately declared as a plain object without an explicit type annotation
13
+ * (like `Record<ToolName, Tool>`) to preserve the exact concrete type of each registered tool.
14
+ * This is required to support compile-time type safety and inference in the overloaded
15
+ * `ToolRegistry.get()` method, which maps a literal `ToolName` key to its specific class type.
12
16
  */
13
17
  export const TOOLS = {
14
18
  [ToolName.EXECUTE_JAVASCRIPT]: new ExecuteJavaScriptTool(),
@@ -23,14 +27,26 @@ export class ToolRegistry {
23
27
  * Retrieves a tool by its literal name with 100% type safety.
24
28
  *
25
29
  * @template K - A key from the `TOOLS` registry.
30
+ * @param name The literal name of the tool to retrieve.
26
31
  * @returns The concrete class type of the requested tool.
27
32
  */
28
33
  static get<K extends keyof typeof TOOLS>(name: K): typeof TOOLS[K];
29
34
  /**
30
35
  * Fallback retrieval signature for general or runtime string lookups.
36
+ *
37
+ * @param name The string name of the tool to retrieve, used when the tool name is only known at runtime.
38
+ * @returns The generic Tool interface, or undefined if not found.
31
39
  */
32
- static get(name: string): Tool|undefined;
33
- static get(name: string): Tool|undefined {
34
- return Object.prototype.hasOwnProperty.call(TOOLS, name) ? TOOLS[name as keyof typeof TOOLS] as Tool : undefined;
40
+ static get(name: string): Tool<ToolArgs, unknown, AllToolsContext>|undefined;
41
+ static get(name: string): Tool<ToolArgs, unknown, AllToolsContext>|undefined {
42
+ // We use a double assertion (`as unknown as Tool<...>`) here. TypeScript's variance
43
+ // rules prevent direct casting from specific concrete tools (which have narrowed,
44
+ // capability-specific contexts) to the generic `Tool` signature that uses `AllToolsContext`.
45
+ // This cast is runtime-safe because any capability requested by a specific tool is
46
+ // guaranteed to be satisfied by `AllToolsContext`, and the handler will only access
47
+ // the capabilities it expects.
48
+ return Object.prototype.hasOwnProperty.call(TOOLS, name) ?
49
+ TOOLS[name as keyof typeof TOOLS] as unknown as Tool<ToolArgs, unknown, AllToolsContext>:
50
+ undefined;
35
51
  }
36
52
  }
@@ -24,7 +24,6 @@ import {
24
24
  isErrorLike,
25
25
  type SymbolizedError,
26
26
  SymbolizedErrorObject,
27
- SymbolizedSyntaxError,
28
27
  UnparsableError,
29
28
  } from './SymbolizedError.js';
30
29
 
@@ -238,14 +237,6 @@ export class DebuggerWorkspaceBinding implements SDK.TargetManager.SDKModelObser
238
237
  ]);
239
238
  fetchedExceptionDetails = details;
240
239
  causeRemoteObject = causeRemote;
241
-
242
- if (remoteObject.className === 'SyntaxError' && fetchedExceptionDetails) {
243
- const syntaxError = await SymbolizedSyntaxError.fromExceptionDetails(
244
- remoteObject.runtimeModel().target(), this, fetchedExceptionDetails);
245
- if (syntaxError) {
246
- return syntaxError;
247
- }
248
- }
249
240
  } else if (remoteObject.type === 'string') {
250
241
  errorStack = remoteObject.description || '';
251
242
  if (!isErrorLike(errorStack)) {
@@ -271,6 +262,12 @@ export class DebuggerWorkspaceBinding implements SDK.TargetManager.SDKModelObser
271
262
  }
272
263
 
273
264
  const message = StackTraceImpl.DetailedErrorStackParser.parseMessage(errorStack);
265
+
266
+ if (remoteObject.subtype === 'error' && remoteObject.className === 'SyntaxError' && fetchedExceptionDetails) {
267
+ return await SymbolizedErrorObject.createForSyntaxError(remoteObject.runtimeModel().target(), this, message,
268
+ fetchedExceptionDetails, stackTrace, cause);
269
+ }
270
+
274
271
  return new SymbolizedErrorObject(message, stackTrace, cause);
275
272
  }
276
273
 
@@ -107,7 +107,8 @@ export class DefaultScriptMapping implements DebuggerSourceMapping {
107
107
  }
108
108
  this.#uiSourceCodeToScript.set(uiSourceCode, script);
109
109
  this.#scriptToUISourceCode.set(script, uiSourceCode);
110
- this.#project.addUISourceCodeWithProvider(uiSourceCode, script, null, 'text/javascript');
110
+ const mimeType = script.isWasm() ? 'application/wasm' : 'text/javascript';
111
+ this.#project.addUISourceCodeWithProvider(uiSourceCode, script, null, mimeType);
111
112
  void this.#debuggerWorkspaceBinding.updateLocations(script);
112
113
  }
113
114
 
@@ -15,7 +15,7 @@ export function isErrorLike(stack: string): boolean {
15
15
  return /\n\s*at\s/.test(stack) || stack.startsWith('SyntaxError:');
16
16
  }
17
17
 
18
- export type SymbolizedError = SymbolizedErrorObject|SymbolizedSyntaxError|UnparsableError;
18
+ export type SymbolizedError = SymbolizedErrorObject|UnparsableError;
19
19
 
20
20
  export class UnparsableError extends Common.ObjectWrapper.ObjectWrapper<EventTypes> {
21
21
  readonly errorStack: string;
@@ -45,6 +45,7 @@ export class SymbolizedErrorObject extends Common.ObjectWrapper.ObjectWrapper<Ev
45
45
  readonly message: string;
46
46
  readonly stackTrace: StackTrace.StackTrace.ParsedErrorStackTrace;
47
47
  readonly cause: SymbolizedError|null;
48
+ #syntaxErrorLocation: Workspace.UISourceCode.UILocation|null = null;
48
49
 
49
50
  constructor(message: string, stackTrace: StackTrace.StackTrace.ParsedErrorStackTrace, cause: SymbolizedError|null) {
50
51
  super();
@@ -64,53 +65,62 @@ export class SymbolizedErrorObject extends Common.ObjectWrapper.ObjectWrapper<Ev
64
65
  }
65
66
  }
66
67
 
67
- #fireUpdated(): void {
68
- this.dispatchEventToListeners(Events.UPDATED);
69
- }
70
- }
71
-
72
- export class SymbolizedSyntaxError extends Common.ObjectWrapper.ObjectWrapper<EventTypes> {
73
- readonly message: string;
74
- #uiLocation: Workspace.UISourceCode.UILocation|null = null;
75
-
76
- constructor(message: string) {
77
- super();
78
- this.message = message;
68
+ get syntaxErrorLocation(): Workspace.UISourceCode.UILocation|null {
69
+ return this.#syntaxErrorLocation;
79
70
  }
80
71
 
81
- get uiLocation(): Workspace.UISourceCode.UILocation|null {
82
- return this.#uiLocation;
83
- }
84
-
85
- static async fromExceptionDetails(
86
- target: SDK.Target.Target, debuggerWorkspaceBinding: DebuggerWorkspaceBinding,
87
- exceptionDetails: Protocol.Runtime.ExceptionDetails): Promise<SymbolizedSyntaxError|null> {
72
+ /**
73
+ * Evaluates if we should populate the `syntaxErrorLocation` based on the provided exception details.
74
+ *
75
+ * There are three primary cases for SyntaxError:
76
+ * 1. Programmatic `SyntaxError`: Thrown via `throw new SyntaxError('...', {cause: ...})`. Has a full stack trace,
77
+ * and an optional cause. The exception details point to the `throw` statement, which is identical to the top frame.
78
+ * We do NOT want to populate `syntaxErrorLocation` here to avoid redundant location rendering in the UI.
79
+ * 2. Script parse failure: Failed to parse a script. Has no stack trace but possesses a compile-time location.
80
+ * We DO want to populate `syntaxErrorLocation` to highlight where the parse failed.
81
+ * 3. `eval` parse failure: Failed to parse an eval string. Has a stack trace pointing to the `eval` call site
82
+ * and a compile-time location of the parse failure within the string. The exception details location differs
83
+ * from the top frame. We DO want to populate `syntaxErrorLocation` here.
84
+ */
85
+ static async createForSyntaxError(target: SDK.Target.Target, debuggerWorkspaceBinding: DebuggerWorkspaceBinding,
86
+ message: string, exceptionDetails: Protocol.Runtime.ExceptionDetails,
87
+ stackTrace: StackTrace.StackTrace.ParsedErrorStackTrace,
88
+ cause: SymbolizedError|null): Promise<SymbolizedErrorObject> {
88
89
  const {exception, scriptId, lineNumber, columnNumber} = exceptionDetails;
89
90
  if (!exception || exception.subtype !== 'error' || exception.className !== 'SyntaxError') {
90
- throw new Error('SymbolizedSyntaxError.fromExceptionDetails expects a SyntaxError');
91
+ throw new Error('SymbolizedErrorObject.createForSyntaxError expects a SyntaxError');
91
92
  }
93
+
94
+ const symbolizedError = new SymbolizedErrorObject(message, stackTrace, cause);
95
+
92
96
  if (!scriptId) {
93
- return null;
97
+ return symbolizedError;
94
98
  }
95
99
 
96
- const debuggerModel = target.model(SDK.DebuggerModel.DebuggerModel);
97
- if (!debuggerModel) {
98
- return null;
100
+ const topFrame = exceptionDetails.stackTrace?.callFrames[0];
101
+ const isProgrammaticThrow = topFrame && topFrame.scriptId === scriptId && topFrame.lineNumber === lineNumber &&
102
+ topFrame.columnNumber === columnNumber;
103
+
104
+ if (!isProgrammaticThrow) {
105
+ const debuggerModel = target.model(SDK.DebuggerModel.DebuggerModel);
106
+ if (debuggerModel) {
107
+ const rawLocation = debuggerModel.createRawLocationByScriptId(scriptId, lineNumber, columnNumber);
108
+ // We don't implement dispose here. We won't create many of these so a couple
109
+ // LiveLocationPools and SymbolizedErrorObject instances leaking is fine.
110
+ await debuggerWorkspaceBinding.createLiveLocation(
111
+ rawLocation, symbolizedError.#updateSyntaxErrorLocation.bind(symbolizedError), new LiveLocationPool());
112
+ }
99
113
  }
100
114
 
101
- const rawLocation = debuggerModel.createRawLocationByScriptId(scriptId, lineNumber, columnNumber);
102
- const symbolizedSyntaxError = new SymbolizedSyntaxError(exception.description || '');
103
-
104
- // We don't implement dispose here. We won't create many of these so a couple
105
- // LiveLocationPools and SymbolizedSyntaxError instances leaking is fine.
106
- await debuggerWorkspaceBinding.createLiveLocation(
107
- rawLocation, symbolizedSyntaxError.#update.bind(symbolizedSyntaxError), new LiveLocationPool());
115
+ return symbolizedError;
116
+ }
108
117
 
109
- return symbolizedSyntaxError;
118
+ async #updateSyntaxErrorLocation(liveLocation: LiveLocation): Promise<void> {
119
+ this.#syntaxErrorLocation = await liveLocation.uiLocation();
120
+ this.dispatchEventToListeners(Events.UPDATED);
110
121
  }
111
122
 
112
- async #update(liveLocation: LiveLocation): Promise<void> {
113
- this.#uiLocation = await liveLocation.uiLocation();
123
+ #fireUpdated(): void {
114
124
  this.dispatchEventToListeners(Events.UPDATED);
115
125
  }
116
126
  }
@@ -1067,6 +1067,46 @@ self.injectedExtensionAPI = function(
1067
1067
  return typeof lastArgument === 'function' ? lastArgument as (...args: unknown[]) => unknown : undefined;
1068
1068
  }
1069
1069
 
1070
+ /**
1071
+ * Helper to support both callback and Promise-based APIs.
1072
+ *
1073
+ * @param args The arguments object of the calling function.
1074
+ * @returns An object containing either the `callback` function, or the
1075
+ * `promise` and its `resolve`/`reject` functions.
1076
+ */
1077
+ function callbackOrPromise<ResolveT, CallbackArgsT extends unknown[] = [ResolveT]>(args: IArguments): {
1078
+ callback?: (...args: CallbackArgsT) => void,
1079
+ promise?: Promise<ResolveT>,
1080
+ resolve?: (value: ResolveT) => void,
1081
+ reject?: (error: unknown) => void,
1082
+ } {
1083
+ const callback = extractCallbackArgument(args);
1084
+ if (callback) {
1085
+ return {callback: callback as (...args: CallbackArgsT) => void};
1086
+ }
1087
+ const {promise, resolve, reject} = Promise.withResolvers<ResolveT>();
1088
+ return {promise, resolve, reject};
1089
+ }
1090
+
1091
+ /**
1092
+ * Checks if the `response` from the ExtensionServer indicates an error. If an
1093
+ * error occurred and a `Promise` `reject` function is provided, this function
1094
+ * will reject the promise with a generic 'DevTools API encountered an error' Error.
1095
+ *
1096
+ * @param response The response object from the ExtensionServer.
1097
+ * @param reject The promise reject function, if applicable.
1098
+ * @returns `true` if an error occurred and the promise was rejected, `false`
1099
+ * otherwise.
1100
+ */
1101
+ function checkErrorAndReject(response: unknown, reject?: (error: Error) => void): boolean {
1102
+ const res = response as {isError?: boolean, description?: string, details?: unknown[]};
1103
+ if (res.isError && reject) {
1104
+ reject(new Error('DevTools API encountered an error'));
1105
+ return true;
1106
+ }
1107
+ return false;
1108
+ }
1109
+
1070
1110
  const LanguageServicesAPI = declareInterfaceClass(LanguageServicesAPIImpl);
1071
1111
  const RecorderServicesAPI = declareInterfaceClass(RecorderServicesAPIImpl);
1072
1112
  const Performance = declareInterfaceClass(PerformanceImpl);
@@ -1247,43 +1287,68 @@ self.injectedExtensionAPI = function(
1247
1287
  extensionServer.sendRequest({command: PrivateAPI.Commands.Reload, options});
1248
1288
  },
1249
1289
 
1250
- eval: function(
1251
- expression: string,
1252
- evaluateOptions: {scriptExecutionContext?: string, frameURL?: string, useContentScriptContext?: boolean}):
1253
- Object |
1254
- null {
1255
- const callback = extractCallbackArgument(arguments);
1290
+ eval: function<E = unknown>(this: PublicAPI.Chrome.DevTools.InspectedWindow, expression: string,
1291
+ optionsOrCallback?: unknown,
1292
+ _callback?: (result: unknown, exceptionInfo: object) => void): Promise<E>|
1293
+ void {
1294
+ const options = (typeof optionsOrCallback === 'object' && optionsOrCallback !== null) ?
1295
+ optionsOrCallback as PrivateAPI.EvaluateOptions :
1296
+ undefined;
1297
+
1298
+ const {callback: callbackArg, promise, resolve, reject} = callbackOrPromise<E, [unknown, object?]>(arguments);
1299
+
1256
1300
  function callbackWrapper(result: unknown): void {
1257
- const {isError, isException, value} = result as {
1258
- value: unknown,
1301
+ if (checkErrorAndReject(result, reject)) {
1302
+ return;
1303
+ }
1304
+
1305
+ const res = result as {
1306
+ value?: unknown,
1259
1307
  isError?: boolean,
1260
1308
  isException?: boolean,
1261
1309
  };
1262
- if (isError || isException) {
1263
- callback?.(undefined, result);
1310
+
1311
+ if (res.isException) {
1312
+ reject?.(res);
1313
+ } else {
1314
+ resolve?.(res.value as E);
1315
+ }
1316
+
1317
+ if (res.isError || res.isException) {
1318
+ callbackArg?.(undefined, res);
1264
1319
  } else {
1265
- callback?.(value);
1320
+ callbackArg?.(res.value);
1266
1321
  }
1267
1322
  }
1268
1323
  extensionServer.sendRequest(
1269
- {
1270
- command: PrivateAPI.Commands.EvaluateOnInspectedPage,
1271
- expression,
1272
- evaluateOptions: (typeof evaluateOptions === 'object' ? evaluateOptions : undefined),
1273
- },
1274
- callback && callbackWrapper);
1275
- return null;
1276
- },
1324
+ {command: PrivateAPI.Commands.EvaluateOnInspectedPage, expression, evaluateOptions: options},
1325
+ callbackWrapper);
1277
1326
 
1278
- getResources: function(callback?: (resources: PublicAPI.Chrome.DevTools.Resource[]) => unknown): void {
1279
- function wrapResource(resourceData: APIImpl.ResourceData): APIImpl.Resource {
1280
- return new (Constructor(Resource))(resourceData);
1281
- }
1282
- function callbackWrapper(resources: unknown): void {
1283
- callback?.((resources as APIImpl.ResourceData[]).map(wrapResource));
1284
- }
1285
- extensionServer.sendRequest({command: PrivateAPI.Commands.GetPageResources}, callback && callbackWrapper);
1286
- },
1327
+ return promise;
1328
+ } as PublicAPI.Chrome.DevTools.InspectedWindow['eval'],
1329
+
1330
+ getResources: function(this: PublicAPI.Chrome.DevTools.InspectedWindow,
1331
+ _callback?: (resources: PublicAPI.Chrome.DevTools.Resource[]) => void):
1332
+ Promise<PublicAPI.Chrome.DevTools.Resource[]>|
1333
+ void {
1334
+ const {callback: callbackArg, promise, resolve, reject} =
1335
+ callbackOrPromise<PublicAPI.Chrome.DevTools.Resource[]>(arguments);
1336
+
1337
+ function callbackWrapper(response: unknown): void {
1338
+ if (checkErrorAndReject(response, reject)) {
1339
+ return;
1340
+ }
1341
+
1342
+ const wrappedResources =
1343
+ ((response || []) as APIImpl.ResourceData[]).map(r => new (Constructor(Resource))(r));
1344
+ resolve?.(wrappedResources);
1345
+ callbackArg?.(wrappedResources);
1346
+ }
1347
+
1348
+ extensionServer.sendRequest({command: PrivateAPI.Commands.GetPageResources}, callbackWrapper);
1349
+
1350
+ return promise;
1351
+ } as PublicAPI.Chrome.DevTools.InspectedWindow['getResources'],
1287
1352
  };
1288
1353
 
1289
1354
  function ResourceImpl(this: APIImpl.Resource, resourceData: APIImpl.ResourceData): void {
@@ -1292,9 +1357,9 @@ self.injectedExtensionAPI = function(
1292
1357
  this._buildId = resourceData.buildId;
1293
1358
  }
1294
1359
 
1295
- (ResourceImpl.prototype as Pick<
1296
- APIImpl.Resource,
1297
- 'url'|'type'|'buildId'|'getContent'|'setContent'|'setFunctionRangesForScript'|'attachSourceMapURL'>) = {
1360
+ (ResourceImpl.prototype as
1361
+ Pick<APIImpl.Resource,
1362
+ 'url'|'type'|'buildId'|'getContent'|'setContent'|'setFunctionRangesForScript'|'attachSourceMapURL'>) = {
1298
1363
  get url(): string {
1299
1364
  return (this as APIImpl.Resource)._url;
1300
1365
  },
@@ -1307,25 +1372,51 @@ self.injectedExtensionAPI = function(
1307
1372
  return (this as APIImpl.Resource)._buildId;
1308
1373
  },
1309
1374
 
1310
- getContent: function(this: APIImpl.Resource, callback?: (content: string, encoding: string) => unknown): void {
1311
- function callbackWrapper(response: unknown): void {
1312
- const {content, encoding} = response as {content: string, encoding: string};
1313
- callback?.(content, encoding);
1314
- }
1375
+ getContent: function(this: APIImpl.Resource, _callback?: (content: string, encoding: string) => void): Promise<{
1376
+ content: string, encoding: string,
1377
+ }>|
1378
+ void {
1379
+ const {callback: callbackArg, promise, resolve, reject} =
1380
+ callbackOrPromise<{content: string, encoding: string}, [string, string]>(arguments);
1315
1381
 
1316
- extensionServer.sendRequest(
1317
- {command: PrivateAPI.Commands.GetResourceContent, url: this._url}, callback && callbackWrapper);
1318
- },
1382
+ function callbackWrapper(response: unknown): void {
1383
+ if (checkErrorAndReject(response, reject)) {
1384
+ return;
1385
+ }
1319
1386
 
1320
- setContent: function(
1321
- this: APIImpl.Resource, content: string, commit: boolean, callback: (error?: Object) => unknown): void {
1322
- extensionServer.sendRequest(
1323
- {command: PrivateAPI.Commands.SetResourceContent, url: this._url, content, commit},
1324
- callback as (response: unknown) => unknown);
1325
- },
1387
+ const {content, encoding} = response as {content: string, encoding: string};
1388
+ resolve?.({content, encoding});
1389
+ callbackArg?.(content, encoding);
1390
+ }
1391
+
1392
+ extensionServer.sendRequest({command: PrivateAPI.Commands.GetResourceContent, url: this._url},
1393
+ callbackWrapper);
1394
+
1395
+ return promise;
1396
+ } as PublicAPI.Chrome.DevTools.Resource['getContent'],
1397
+
1398
+ setContent: function(this: APIImpl.Resource, content: string, commit: boolean,
1399
+ _callback?: (status?: object) => void): Promise<void>|
1400
+ void {
1401
+ const {callback: callbackArg, promise, resolve, reject} = callbackOrPromise<void, [object]>(arguments);
1402
+
1403
+ function callbackWrapper(response: unknown): void {
1404
+ if (checkErrorAndReject(response, reject)) {
1405
+ return;
1406
+ }
1407
+
1408
+ resolve?.();
1409
+ callbackArg?.(response as object);
1410
+ }
1411
+
1412
+ extensionServer.sendRequest(
1413
+ {command: PrivateAPI.Commands.SetResourceContent, url: this._url, content, commit}, callbackWrapper);
1414
+
1415
+ return promise;
1416
+ } as PublicAPI.Chrome.DevTools.Resource['setContent'],
1326
1417
 
1327
- setFunctionRangesForScript: function(
1328
- this: APIImpl.Resource, ranges: PublicAPI.Chrome.DevTools.NamedFunctionRange[]): Promise<void> {
1418
+ setFunctionRangesForScript: function(this: APIImpl.Resource,
1419
+ ranges: PublicAPI.Chrome.DevTools.NamedFunctionRange[]): Promise<void> {
1329
1420
  return new Promise(
1330
1421
  (resolve, reject) => extensionServer.sendRequest(
1331
1422
  {
@@ -40,6 +40,7 @@ export class Importer {
40
40
  }
41
41
  const request = SDK.NetworkRequest.NetworkRequest.createWithoutBackendRequest(
42
42
  'har-' + requests.length, entry.request.url, documentURL, initiator);
43
+ request.setIsImportedHar(true);
43
44
  const page = pageref ? pages.get(pageref) : undefined;
44
45
  if (!pageLoad && pageref && page) {
45
46
  pageLoad = Importer.buildPageLoad(page, request);
@@ -136,10 +136,16 @@ export class Diff {
136
136
  removedCount = 0;
137
137
  addedSize = 0;
138
138
  removedSize = 0;
139
- deletedIndexes: number[] = [];
140
- addedIndexes: number[] = [];
141
139
  countDelta!: number;
142
140
  sizeDelta!: number;
141
+ // Data about added nodes
142
+ addedIndexes: number[] = [];
143
+ addedIds: number[] = [];
144
+ addedSelfSizes: number[] = [];
145
+ // Data about deleted nodes
146
+ deletedIndexes: number[] = [];
147
+ deletedIds: number[] = [];
148
+ deletedSelfSizes: number[] = [];
143
149
  constructor(name: string) {
144
150
  this.name = name;
145
151
  }
@@ -287,3 +293,13 @@ export interface RetainingPaths {
287
293
  siblings?: boolean,
288
294
  };
289
295
  }
296
+
297
+ export interface DominatorNode {
298
+ nodeId: number;
299
+ nodeIndex: number;
300
+ nodeName: string;
301
+ retainedSize: number;
302
+ selfSize: number;
303
+ }
304
+
305
+ export type DominatorChain = DominatorNode[];
@@ -370,6 +370,10 @@ export class HeapSnapshotProxy extends HeapSnapshotProxyObject {
370
370
  return this.callMethodPromise('getRetainingPaths', nodeIndex, maxDepth, maxNodes, maxSiblings);
371
371
  }
372
372
 
373
+ getDominatorsOf(nodeIndex: number): Promise<HeapSnapshotModel.DominatorChain> {
374
+ return this.callMethodPromise('getDominatorsOf', nodeIndex);
375
+ }
376
+
373
377
  unignoreNodeInRetainersView(nodeIndex: number): Promise<void> {
374
378
  return this.callMethodPromise('unignoreNodeInRetainersView', nodeIndex);
375
379
  }