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.
- package/eslint.config.mjs +3 -1
- package/extension-api/ExtensionAPI.d.ts +83 -12
- package/front_end/core/host/UserMetrics.ts +3 -3
- package/front_end/core/root/ExperimentNames.ts +0 -1
- package/front_end/core/sdk/CSSPropertyParserMatchers.ts +2 -3
- package/front_end/core/sdk/ConsoleModel.ts +4 -0
- package/front_end/core/sdk/NetworkRequest.ts +12 -1
- package/front_end/core/sdk/SourceMap.ts +15 -18
- package/front_end/entrypoints/greendev_floaty/FloatyEntrypoint.ts +0 -2
- package/front_end/entrypoints/greendev_floaty/greendev_floaty.ts +0 -2
- package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshot.ts +37 -0
- package/front_end/entrypoints/main/MainImpl.ts +0 -6
- package/front_end/generated/SupportedCSSProperties.js +4 -2
- package/front_end/models/ai_assistance/AiAgent2.ts +23 -12
- package/front_end/models/ai_assistance/README.md +5 -4
- package/front_end/models/ai_assistance/agents/AccessibilityAgent.ts +22 -16
- package/front_end/models/ai_assistance/agents/AiAgent.ts +19 -6
- package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +5 -5
- package/front_end/models/ai_assistance/agents/ExecuteJavascript.ts +1 -94
- package/front_end/models/ai_assistance/agents/NetworkAgent.ts +25 -0
- package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +37 -0
- package/front_end/models/ai_assistance/agents/README.md +57 -0
- package/front_end/models/ai_assistance/agents/StorageAgent.ts +54 -1
- package/front_end/models/ai_assistance/agents/StylingAgent.snapshot.txt +1 -2
- package/front_end/models/ai_assistance/agents/StylingAgent.ts +31 -3
- package/front_end/models/ai_assistance/tools/ExecuteJavaScript.ts +12 -21
- package/front_end/models/ai_assistance/tools/GetStyles.ts +19 -12
- package/front_end/models/ai_assistance/tools/README.md +45 -0
- package/front_end/models/ai_assistance/tools/Tool.ts +78 -9
- package/front_end/models/ai_assistance/tools/ToolRegistry.ts +21 -5
- package/front_end/models/bindings/DebuggerWorkspaceBinding.ts +6 -9
- package/front_end/models/bindings/DefaultScriptMapping.ts +2 -1
- package/front_end/models/bindings/SymbolizedError.ts +45 -35
- package/front_end/models/extensions/ExtensionAPI.ts +138 -47
- package/front_end/models/har/Importer.ts +1 -0
- package/front_end/models/heap_snapshot/HeapSnapshotModel.ts +18 -2
- package/front_end/models/heap_snapshot/HeapSnapshotProxy.ts +4 -0
- package/front_end/models/source_map_scopes/FunctionCodeResolver.ts +12 -2
- package/front_end/models/stack_trace/DetailedErrorStackParser.ts +58 -52
- package/front_end/models/stack_trace/StackTrace.ts +7 -0
- package/front_end/models/stack_trace/StackTraceImpl.ts +13 -4
- package/front_end/models/stack_trace/StackTraceModel.ts +43 -9
- package/front_end/models/trace/Styles.ts +29 -7
- package/front_end/models/trace/handlers/PageLoadMetricsHandler.ts +33 -4
- package/front_end/models/trace/helpers/Timing.ts +10 -0
- package/front_end/models/trace/types/TraceEvents.ts +22 -2
- package/front_end/panels/accessibility/AccessibilitySidebarView.ts +2 -1
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +9 -2
- package/front_end/panels/ai_assistance/ai_assistance-meta.ts +16 -0
- package/front_end/panels/application/ApplicationPanelSidebar.ts +83 -0
- package/front_end/panels/application/ApplicationPanelTreeElement.ts +39 -0
- package/front_end/panels/application/CookieItemsView.ts +2 -2
- package/front_end/panels/application/ServiceWorkersView.ts +2 -2
- package/front_end/panels/application/WebMCPView.ts +0 -1
- package/front_end/panels/application/components/BackForwardCacheView.ts +1 -2
- package/front_end/panels/application/resourcesSidebar.css +11 -0
- package/front_end/panels/console/ConsoleView.ts +6 -1
- package/front_end/panels/console/ConsoleViewMessage.ts +46 -213
- package/front_end/panels/console/SymbolizedErrorWidget.ts +14 -8
- package/front_end/panels/elements/AdoptedStyleSheetTreeElement.ts +0 -1
- package/front_end/panels/elements/ElementsTreeElement.ts +0 -2
- package/front_end/panels/elements/PropertyRenderer.ts +0 -1
- package/front_end/panels/elements/StylesSidebarPane.ts +9 -2
- package/front_end/panels/issues/AffectedResourcesView.ts +1 -1
- package/front_end/panels/issues/AffectedSourcesView.ts +1 -1
- package/front_end/panels/lighthouse/LighthouseReportRenderer.ts +0 -1
- package/front_end/panels/mobile_throttling/ThrottlingSettingsTab.ts +10 -0
- package/front_end/panels/network/NetworkDataGridNode.ts +1 -2
- package/front_end/panels/network/NetworkLogView.ts +34 -7
- package/front_end/panels/profiler/HeapProfileView.ts +0 -1
- package/front_end/panels/profiler/HeapSnapshotGridNodes.ts +0 -1
- package/front_end/panels/profiler/HeapSnapshotView.ts +1 -1
- package/front_end/panels/settings/components/SyncSection.ts +1 -1
- package/front_end/panels/settings/emulation/DevicesSettingsTab.ts +1 -0
- package/front_end/panels/sources/SourcesPanel.ts +2 -1
- package/front_end/panels/timeline/TimelineFlameChartView.ts +5 -4
- package/front_end/panels/timeline/TimelinePanel.ts +7 -0
- package/front_end/panels/timeline/TimelineUIUtils.ts +13 -14
- package/front_end/panels/timeline/TimingsTrackAppender.ts +7 -5
- package/front_end/panels/timeline/components/LayoutShiftDetails.ts +0 -1
- package/front_end/panels/timeline/components/NetworkRequestDetails.ts +0 -2
- package/front_end/panels/timeline/components/insights/ForcedReflow.ts +0 -1
- package/front_end/panels/timeline/components/insights/NodeLink.ts +0 -1
- package/front_end/panels/timeline/overlays/OverlaysImpl.ts +2 -0
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/ui/helpers/OpenInNewTab.ts +3 -3
- package/front_end/ui/kit/link/Link.ts +16 -2
- package/front_end/ui/legacy/InspectorDrawerView.ts +14 -5
- package/front_end/ui/legacy/InspectorView.ts +4 -1
- package/front_end/ui/legacy/PlusButton.ts +6 -1
- package/front_end/ui/legacy/StackedPane.ts +229 -0
- package/front_end/ui/legacy/ViewManager.ts +59 -169
- package/front_end/ui/legacy/Widget.ts +19 -1
- package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +95 -31
- package/front_end/ui/legacy/components/utils/JSPresentationUtils.ts +0 -1
- package/front_end/ui/legacy/components/utils/Linkifier.ts +2 -16
- package/front_end/ui/legacy/legacy.ts +3 -1
- package/front_end/ui/visual_logging/KnownContextValues.ts +5 -0
- 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
|
-
*
|
|
12
|
+
* Base capability for all tool contexts, providing access to the conversation context.
|
|
12
13
|
*/
|
|
13
|
-
export interface
|
|
14
|
+
export interface BaseToolCapability {
|
|
15
|
+
/**
|
|
16
|
+
* The active context for the current conversation step, if any.
|
|
17
|
+
*/
|
|
14
18
|
conversationContext: ConversationContext<unknown>|null;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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,
|
|
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:
|
|
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
|
-
*
|
|
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
|
|
33
|
-
static get(name: string): Tool
|
|
34
|
-
|
|
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
|
-
|
|
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|
|
|
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
|
-
|
|
68
|
-
this
|
|
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
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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('
|
|
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
|
|
97
|
+
return symbolizedError;
|
|
94
98
|
}
|
|
95
99
|
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
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
|
-
|
|
102
|
-
|
|
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
|
-
|
|
118
|
+
async #updateSyntaxErrorLocation(liveLocation: LiveLocation): Promise<void> {
|
|
119
|
+
this.#syntaxErrorLocation = await liveLocation.uiLocation();
|
|
120
|
+
this.dispatchEventToListeners(Events.UPDATED);
|
|
110
121
|
}
|
|
111
122
|
|
|
112
|
-
|
|
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
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
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
|
-
|
|
1258
|
-
|
|
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
|
-
|
|
1263
|
-
|
|
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
|
-
|
|
1320
|
+
callbackArg?.(res.value);
|
|
1266
1321
|
}
|
|
1267
1322
|
}
|
|
1268
1323
|
extensionServer.sendRequest(
|
|
1269
|
-
{
|
|
1270
|
-
|
|
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
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
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
|
|
1296
|
-
|
|
1297
|
-
|
|
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,
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
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
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1382
|
+
function callbackWrapper(response: unknown): void {
|
|
1383
|
+
if (checkErrorAndReject(response, reject)) {
|
|
1384
|
+
return;
|
|
1385
|
+
}
|
|
1319
1386
|
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
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
|
-
|
|
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
|
}
|