chrome-devtools-frontend 1.0.1645245 → 1.0.1646286

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 (42) hide show
  1. package/.agents/skills/devtools-source-maps/SKILL.md +124 -0
  2. package/docs/README.md +1 -0
  3. package/docs/using_source_maps.md +159 -0
  4. package/front_end/core/host/AidaClientTypes.ts +2 -0
  5. package/front_end/core/host/UserMetrics.ts +2 -1
  6. package/front_end/core/root/Runtime.ts +10 -0
  7. package/front_end/core/sdk/DebuggerModel.ts +7 -9
  8. package/front_end/core/sdk/NetworkRequest.ts +0 -24
  9. package/front_end/generated/InspectorBackendCommands.ts +2 -2
  10. package/front_end/generated/SupportedCSSProperties.js +75 -0
  11. package/front_end/generated/protocol.ts +0 -5
  12. package/front_end/models/ai_assistance/AiAgent2.ts +29 -2
  13. package/front_end/models/ai_assistance/AiConversation.ts +4 -2
  14. package/front_end/models/ai_assistance/AiOrigins.ts +63 -2
  15. package/front_end/models/ai_assistance/README.md +15 -4
  16. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +2 -2
  17. package/front_end/models/ai_assistance/agents/FileAgent.ts +9 -42
  18. package/front_end/models/ai_assistance/agents/NetworkAgent.snapshot.txt +2 -2
  19. package/front_end/models/ai_assistance/agents/NetworkAgent.ts +9 -133
  20. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +10 -2
  21. package/front_end/models/ai_assistance/agents/README.md +7 -0
  22. package/front_end/models/ai_assistance/ai_assistance.ts +4 -0
  23. package/front_end/models/ai_assistance/contexts/FileContext.ts +45 -0
  24. package/front_end/models/ai_assistance/contexts/RequestContext.snapshot.txt +48 -0
  25. package/front_end/models/ai_assistance/contexts/RequestContext.ts +116 -0
  26. package/front_end/models/ai_assistance/data_formatters/NetworkRequestFormatter.ts +2 -1
  27. package/front_end/models/ai_assistance/tools/README.md +1 -1
  28. package/front_end/models/trace/handlers/NetworkRequestsHandler.ts +15 -11
  29. package/front_end/models/web_mcp/WebMCPModel.ts +8 -48
  30. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +13 -13
  31. package/front_end/panels/ai_assistance/components/ChatInput.ts +4 -4
  32. package/front_end/panels/application/ApplicationPanelSidebar.ts +25 -0
  33. package/front_end/panels/application/WebMCPView.ts +40 -70
  34. package/front_end/panels/application/components/AdsView.ts +31 -28
  35. package/front_end/panels/application/components/adsView.css +6 -0
  36. package/front_end/panels/common/ExtensionServer.ts +5 -0
  37. package/front_end/panels/profiler/IsolateSelector.ts +4 -2
  38. package/front_end/panels/profiler/ProfileLauncherView.ts +194 -126
  39. package/front_end/panels/profiler/ProfilesPanel.ts +1 -3
  40. package/front_end/third_party/chromium/README.chromium +1 -1
  41. package/front_end/ui/visual_logging/KnownContextValues.ts +1 -0
  42. package/package.json +1 -1
@@ -2,21 +2,14 @@
2
2
  // Use of this source code is governed by a BSD-style license that can be
3
3
  // found in the LICENSE file.
4
4
 
5
- import * as Common from '../../../core/common/common.js';
6
5
  import * as Host from '../../../core/host/host.js';
7
- import * as i18n from '../../../core/i18n/i18n.js';
8
- import type * as Platform from '../../../core/platform/platform.js';
9
6
  import * as Root from '../../../core/root/root.js';
10
7
  import type * as SDK from '../../../core/sdk/sdk.js';
11
- import type * as NetworkTimeCalculator from '../../network_time_calculator/network_time_calculator.js';
12
- import {extractContextOrigin} from '../AiOrigins.js';
13
- import {NetworkRequestFormatter} from '../data_formatters/NetworkRequestFormatter.js';
8
+ import type {RequestContext} from '../contexts/RequestContext.js';
14
9
 
15
10
  import {
16
11
  AiAgent,
17
- type ContextDetail,
18
12
  type ContextResponse,
19
- ConversationContext,
20
13
  type RequestOptions,
21
14
  ResponseType,
22
15
  } from './AiAgent.js';
@@ -73,90 +66,6 @@ This request aims to retrieve a list of products matching the search query "lapt
73
66
  `;
74
67
  /* clang-format on */
75
68
 
76
- /*
77
- * Strings that don't need to be translated at this time.
78
- */
79
- const UIStringsNotTranslate = {
80
- /**
81
- * @description Heading text for the block that shows the network request details.
82
- */
83
- request: 'Request',
84
- /**
85
- * @description Heading text for the block that shows the network response details.
86
- */
87
- response: 'Response',
88
- /**
89
- * @description Prefix text for request URL.
90
- */
91
- requestUrl: 'Request URL',
92
- /**
93
- * @description Title text for request timing details.
94
- */
95
- timing: 'Timing',
96
- /**
97
- * @description Title text for request initiator chain.
98
- */
99
- requestInitiatorChain: 'Request initiator chain',
100
- } as const;
101
-
102
- const lockedString = i18n.i18n.lockedString;
103
-
104
- /**
105
- * Returns the origin for a network request in the AI context.
106
- *
107
- * To prevent cross-origin prompt injection attacks, HAR-imported requests
108
- * are isolated from live pages. We assign them a virtual origin
109
- * (`imported-har://${domain}`) so they do not share the origin of live pages
110
- * (e.g., `https://${domain}`). This forces a conversation reset when transitioning
111
- * between imported HAR data and live pages.
112
- */
113
- export function getRequestContextOrigin(request: SDK.NetworkRequest.NetworkRequest): string {
114
- const origin = extractContextOrigin(request.documentURL);
115
- if (request.isImportedHar()) {
116
- const parsed = Common.ParsedURL.ParsedURL.fromString(origin as Platform.DevToolsPath.UrlString);
117
- return `imported-har://${parsed ? parsed.domain() : origin}`;
118
- }
119
- return origin;
120
- }
121
-
122
- export class RequestContext extends ConversationContext<SDK.NetworkRequest.NetworkRequest> {
123
- #request: SDK.NetworkRequest.NetworkRequest;
124
- #calculator: NetworkTimeCalculator.NetworkTransferTimeCalculator;
125
-
126
- constructor(
127
- request: SDK.NetworkRequest.NetworkRequest, calculator: NetworkTimeCalculator.NetworkTransferTimeCalculator) {
128
- super();
129
- this.#request = request;
130
- this.#calculator = calculator;
131
- }
132
-
133
- /**
134
- * Note: this is not the literal origin of the network request. This URL
135
- * is used to determine when we should force the user to start a new AI
136
- * conversation when the context changes. We allow a single AI conversation to
137
- * inspect all network requests that were made for that given target URL.
138
- */
139
- override getURL(): string {
140
- return this.#request.documentURL;
141
- }
142
-
143
- override getOrigin(): string {
144
- return getRequestContextOrigin(this.#request);
145
- }
146
-
147
- override getItem(): SDK.NetworkRequest.NetworkRequest {
148
- return this.#request;
149
- }
150
-
151
- get calculator(): NetworkTimeCalculator.NetworkTimeCalculator {
152
- return this.#calculator;
153
- }
154
-
155
- override getTitle(): string {
156
- return this.#request.name();
157
- }
158
- }
159
-
160
69
  /**
161
70
  * One agent instance handles one conversation. Create a new agent
162
71
  * instance for a new conversation.
@@ -183,9 +92,14 @@ export class NetworkAgent extends AiAgent<SDK.NetworkRequest.NetworkRequest> {
183
92
  return;
184
93
  }
185
94
 
95
+ const details = await selectedNetworkRequest.getUserFacingDetails();
96
+ if (!details) {
97
+ return;
98
+ }
99
+
186
100
  yield {
187
101
  type: ResponseType.CONTEXT,
188
- details: await createContextDetailsForNetworkAgent(selectedNetworkRequest),
102
+ details,
189
103
  widgets: [{
190
104
  name: 'NETWORK_REQUEST_GENERAL_HEADERS',
191
105
  data: {
@@ -196,46 +110,8 @@ export class NetworkAgent extends AiAgent<SDK.NetworkRequest.NetworkRequest> {
196
110
  }
197
111
 
198
112
  override async enhanceQuery(query: string, selectedNetworkRequest: RequestContext|null): Promise<string> {
199
- const networkEnchantmentQuery = selectedNetworkRequest ?
200
- `# Selected network request \n${
201
- await (new NetworkRequestFormatter(selectedNetworkRequest.getItem(), selectedNetworkRequest.calculator)
202
- .formatNetworkRequest())}\n\n# User request\n\n` :
203
- '';
113
+ const promptDetails = selectedNetworkRequest ? await selectedNetworkRequest.getPromptDetails() : null;
114
+ const networkEnchantmentQuery = promptDetails ? `${promptDetails}\n\n# User request\n\n` : '';
204
115
  return `${networkEnchantmentQuery}${query}`;
205
116
  }
206
117
  }
207
-
208
- async function createContextDetailsForNetworkAgent(
209
- selectedNetworkRequest: RequestContext,
210
- ): Promise<[ContextDetail, ...ContextDetail[]]> {
211
- const request = selectedNetworkRequest.getItem();
212
- const formatter = new NetworkRequestFormatter(request, selectedNetworkRequest.calculator);
213
- const requestContextDetail: ContextDetail = {
214
- title: lockedString(UIStringsNotTranslate.request),
215
- text: lockedString(UIStringsNotTranslate.requestUrl) + ': ' + request.url() + '\n\n' +
216
- formatter.formatRequestHeaders(),
217
- };
218
- const responseBody = await formatter.formatResponseBody();
219
- const responseBodyString = responseBody ? `\n\n${responseBody}` : '';
220
-
221
- const responseContextDetail: ContextDetail = {
222
- title: lockedString(UIStringsNotTranslate.response),
223
- text: formatter.formatResponseHeaders() + responseBodyString +
224
- `\n\n${formatter.formatStatus()}${formatter.formatFailureReasons()}`,
225
- };
226
- const timingContextDetail: ContextDetail = {
227
- title: lockedString(UIStringsNotTranslate.timing),
228
- text: formatter.formatNetworkRequestTiming(),
229
- };
230
- const initiatorChainContextDetail: ContextDetail = {
231
- title: lockedString(UIStringsNotTranslate.requestInitiatorChain),
232
- text: formatter.formatRequestInitiatorChain(),
233
- };
234
-
235
- return [
236
- requestContextDetail,
237
- responseContextDetail,
238
- timingContextDetail,
239
- initiatorChainContextDetail,
240
- ];
241
- }
@@ -15,7 +15,7 @@ import * as Logs from '../../logs/logs.js';
15
15
  import * as SourceMapScopes from '../../source_map_scopes/source_map_scopes.js';
16
16
  import * as TextUtils from '../../text_utils/text_utils.js';
17
17
  import * as Trace from '../../trace/trace.js';
18
- import {extractContextOrigin} from '../AiOrigins.js';
18
+ import {canResourceContentsBeReadForTrace, extractContextOrigin} from '../AiOrigins.js';
19
19
  import {sanitizeHeaders} from '../data_formatters/NetworkRequestFormatter.js';
20
20
  import {
21
21
  PerformanceInsightFormatter,
@@ -1472,8 +1472,16 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
1472
1472
  },
1473
1473
  handler: async args => {
1474
1474
  debugLog('Function call: getResourceContent');
1475
+ if (!isFresh) {
1476
+ return {error: 'Cannot use this tool on an imported file.'};
1477
+ }
1478
+
1479
+ const url = args.url;
1480
+ const allowedOrigin = context.getOrigin();
1481
+ if (!canResourceContentsBeReadForTrace(url, allowedOrigin)) {
1482
+ return {error: 'Resource not found'};
1483
+ }
1475
1484
 
1476
- const url = args.url as Platform.DevToolsPath.UrlString;
1477
1485
  let content: string|undefined;
1478
1486
 
1479
1487
  // First check parsedTrace.data.Scripts.
@@ -31,6 +31,13 @@ Example from `PerformanceAgent`:
31
31
  }
32
32
  ```
33
33
 
34
+ ### Origin Validation (`isOriginAllowed`)
35
+
36
+ Before running an execution loop, the system validates the context's origin:
37
+ * **Opaque Origin Rejection**: Opaque origins are rejected immediately, blocking conversations on untrusted/opaque contexts (like `about:blank`, `data:` URLs, or opaque blob URLs).
38
+ * **Origin Lock Integrity**: If the conversation is already locked to an origin, the new context's origin must be equivalent (as defined by `areOriginsEquivalent()`) to the locked origin.
39
+ * **Safety Termination**: If validation fails, a cross-origin error is yielded and execution stops.
40
+
34
41
  ## Performance Agent
35
42
 
36
43
  The `PerformanceAgent` analyzes performance traces. This documentation details the specific data provided to the agent and the data it can retrieve via functions.
@@ -23,6 +23,8 @@ import * as AiUtils from './AiUtils.js';
23
23
  import * as BuiltInAi from './BuiltInAi.js';
24
24
  import * as ChangeManager from './ChangeManager.js';
25
25
  import * as DOMNodeContext from './contexts/DOMNodeContext.js';
26
+ import * as FileContext from './contexts/FileContext.js';
27
+ import * as RequestContext from './contexts/RequestContext.js';
26
28
  import * as ConversationSummary from './ConversationSummary.js';
27
29
  import * as FileFormatter from './data_formatters/FileFormatter.js';
28
30
  import * as LighthouseFormatter from './data_formatters/LighthouseFormatter.js';
@@ -66,6 +68,7 @@ export {
66
68
  ExecuteJavaScript,
67
69
  ExtensionScope,
68
70
  FileAgent,
71
+ FileContext,
69
72
  FileFormatter,
70
73
  GetStyles,
71
74
  GreenDevAgent,
@@ -80,6 +83,7 @@ export {
80
83
  PerformanceAnnotations,
81
84
  PerformanceInsightFormatter,
82
85
  PerformanceTraceFormatter,
86
+ RequestContext,
83
87
  StorageAgent,
84
88
  StorageItem,
85
89
  StylingAgent,
@@ -0,0 +1,45 @@
1
+ // Copyright 2026 The Chromium Authors
2
+ // Use of this source code is governed by a BSD-style license that can be
3
+ // found in the LICENSE file.
4
+
5
+ import type * as Workspace from '../../workspace/workspace.js';
6
+ import {type ContextDetail, ConversationContext} from '../agents/AiAgent.js';
7
+ import {FileFormatter} from '../data_formatters/FileFormatter.js';
8
+
9
+ export class FileContext extends ConversationContext<Workspace.UISourceCode.UISourceCode> {
10
+ #file: Workspace.UISourceCode.UISourceCode;
11
+
12
+ constructor(file: Workspace.UISourceCode.UISourceCode) {
13
+ super();
14
+ this.#file = file;
15
+ }
16
+
17
+ override getURL(): string {
18
+ return this.#file.url();
19
+ }
20
+
21
+ override getItem(): Workspace.UISourceCode.UISourceCode {
22
+ return this.#file;
23
+ }
24
+
25
+ override getTitle(): string {
26
+ return this.#file.displayName();
27
+ }
28
+
29
+ override async getPromptDetails(): Promise<string|null> {
30
+ return `# Selected file\n${new FileFormatter(this.#file).formatFile()}`;
31
+ }
32
+
33
+ override async getUserFacingDetails(): Promise<[ContextDetail, ...ContextDetail[]]|null> {
34
+ return [
35
+ {
36
+ title: 'Selected file',
37
+ text: new FileFormatter(this.#file).formatFile(),
38
+ },
39
+ ];
40
+ }
41
+
42
+ override async refresh(): Promise<void> {
43
+ await this.#file.requestContentData();
44
+ }
45
+ }
@@ -0,0 +1,48 @@
1
+ Title: RequestContext getPromptDetails describes the network request correctly
2
+ Content:
3
+ # Selected network request
4
+ Request: https://www.example.com/api/users
5
+
6
+ Request headers:
7
+ Accept: application/json
8
+
9
+ Response headers:
10
+ Content-Type: application/json
11
+
12
+ Response body:
13
+ {}
14
+
15
+ Response status: 200
16
+ Network request status: pending
17
+
18
+ Request timing:
19
+ Queued at (timestamp): 0 s
20
+ Started at (timestamp): 0 s
21
+ Connection start (stalled) (duration): -
22
+ Duration (duration): -
23
+
24
+ Request initiator chain:
25
+ - URL: https://www.example.com/api/users
26
+ === end content
27
+
28
+ Title: RequestContext getUserFacingDetails returns details correctly
29
+ Content:
30
+ [
31
+ {
32
+ "title": "Request",
33
+ "text": "Request URL: https://www.example.com/api/users\n\nRequest headers:\nAccept: application/json"
34
+ },
35
+ {
36
+ "title": "Response",
37
+ "text": "Response headers:\nContent-Type: application/json\n\nResponse body:\n{}\n\nResponse status: 200\nNetwork request status: pending\n"
38
+ },
39
+ {
40
+ "title": "Timing",
41
+ "text": "Queued at (timestamp): 0 s\nStarted at (timestamp): 0 s\nConnection start (stalled) (duration): -\nDuration (duration): -"
42
+ },
43
+ {
44
+ "title": "Request initiator chain",
45
+ "text": "- URL: https://www.example.com/api/users"
46
+ }
47
+ ]
48
+ === end content
@@ -0,0 +1,116 @@
1
+ // Copyright 2026 The Chromium Authors
2
+ // Use of this source code is governed by a BSD-style license that can be
3
+ // found in the LICENSE file.
4
+
5
+ import * as Common from '../../../core/common/common.js';
6
+ import * as i18n from '../../../core/i18n/i18n.js';
7
+ import type * as Platform from '../../../core/platform/platform.js';
8
+ import type * as SDK from '../../../core/sdk/sdk.js';
9
+ import type * as NetworkTimeCalculator from '../../network_time_calculator/network_time_calculator.js';
10
+ import {
11
+ type ContextDetail,
12
+ ConversationContext,
13
+ } from '../agents/AiAgent.js';
14
+ import {extractContextOrigin} from '../AiOrigins.js';
15
+ import {NetworkRequestFormatter} from '../data_formatters/NetworkRequestFormatter.js';
16
+
17
+ const UIStringsNotTranslate = {
18
+ request: 'Request',
19
+ response: 'Response',
20
+ requestUrl: 'Request URL',
21
+ timing: 'Timing',
22
+ requestInitiatorChain: 'Request initiator chain',
23
+ } as const;
24
+
25
+ const lockedString = i18n.i18n.lockedString;
26
+
27
+ /**
28
+ * Returns the origin for a network request in the AI context.
29
+ *
30
+ * To prevent cross-origin prompt injection attacks, HAR-imported requests
31
+ * are isolated from live pages. We assign them a virtual origin
32
+ * (`imported-har://${domain}`) so they do not share the origin of live pages
33
+ * (e.g., `https://${domain}`). This forces a conversation reset when transitioning
34
+ * between imported HAR data and live pages.
35
+ */
36
+ export function getRequestContextOrigin(request: SDK.NetworkRequest.NetworkRequest): string {
37
+ const origin = extractContextOrigin(request.documentURL);
38
+ if (request.isImportedHar()) {
39
+ const parsed = Common.ParsedURL.ParsedURL.fromString(origin as Platform.DevToolsPath.UrlString);
40
+ return `imported-har://${parsed ? parsed.domain() : origin}`;
41
+ }
42
+ return origin;
43
+ }
44
+
45
+ export class RequestContext extends ConversationContext<SDK.NetworkRequest.NetworkRequest> {
46
+ #request: SDK.NetworkRequest.NetworkRequest;
47
+ #calculator: NetworkTimeCalculator.NetworkTransferTimeCalculator;
48
+
49
+ constructor(
50
+ request: SDK.NetworkRequest.NetworkRequest,
51
+ calculator: NetworkTimeCalculator.NetworkTransferTimeCalculator,
52
+ ) {
53
+ super();
54
+ this.#request = request;
55
+ this.#calculator = calculator;
56
+ }
57
+
58
+ /**
59
+ * Note: this is not the literal origin of the network request. This URL
60
+ * is used to determine when we should force the user to start a new AI
61
+ * conversation when the context changes. We allow a single AI conversation to
62
+ * inspect all network requests that were made for that given target URL.
63
+ */
64
+ override getURL(): string {
65
+ return this.#request.documentURL;
66
+ }
67
+
68
+ override getOrigin(): string {
69
+ return getRequestContextOrigin(this.#request);
70
+ }
71
+
72
+ override getItem(): SDK.NetworkRequest.NetworkRequest {
73
+ return this.#request;
74
+ }
75
+
76
+ override getTitle(): string {
77
+ return this.#request.name();
78
+ }
79
+
80
+ override async getPromptDetails(): Promise<string|null> {
81
+ const formatter = new NetworkRequestFormatter(this.#request, this.#calculator);
82
+ return `# Selected network request\n${await formatter.formatNetworkRequest()}`;
83
+ }
84
+
85
+ override async getUserFacingDetails(): Promise<[ContextDetail, ...ContextDetail[]]|null> {
86
+ const formatter = new NetworkRequestFormatter(this.#request, this.#calculator);
87
+ const requestContextDetail: ContextDetail = {
88
+ title: lockedString(UIStringsNotTranslate.request),
89
+ text: lockedString(UIStringsNotTranslate.requestUrl) + ': ' + this.#request.url() + '\n\n' +
90
+ formatter.formatRequestHeaders(),
91
+ };
92
+ const responseBody = await formatter.formatResponseBody();
93
+ const responseBodyString = responseBody ? `\n\n${responseBody}` : '';
94
+
95
+ const responseContextDetail: ContextDetail = {
96
+ title: lockedString(UIStringsNotTranslate.response),
97
+ text: formatter.formatResponseHeaders() + responseBodyString +
98
+ `\n\n${formatter.formatStatus()}${formatter.formatFailureReasons()}`,
99
+ };
100
+ const timingContextDetail: ContextDetail = {
101
+ title: lockedString(UIStringsNotTranslate.timing),
102
+ text: formatter.formatNetworkRequestTiming(),
103
+ };
104
+ const initiatorChainContextDetail: ContextDetail = {
105
+ title: lockedString(UIStringsNotTranslate.requestInitiatorChain),
106
+ text: formatter.formatRequestInitiatorChain(),
107
+ };
108
+
109
+ return [
110
+ requestContextDetail,
111
+ responseContextDetail,
112
+ timingContextDetail,
113
+ initiatorChainContextDetail,
114
+ ];
115
+ }
116
+ }
@@ -92,7 +92,8 @@ export class NetworkRequestFormatter {
92
92
  }): string {
93
93
  let responseStatus = '';
94
94
  if (status.statusCode) {
95
- responseStatus = `Response status: ${status.statusCode} ${status.statusText}\n`;
95
+ const statusText = status.statusText ? ` ${status.statusText}` : '';
96
+ responseStatus = `Response status: ${status.statusCode}${statusText}\n`;
96
97
  }
97
98
  const flags = [];
98
99
  flags.push(status.finished ? 'finished' : 'pending');
@@ -17,7 +17,7 @@ Instead of passing a monolithic "grab-bag" context object to all tool handlers,
17
17
  - `PageExecutionCapability`: For tools executing JavaScript code on the inspected page.
18
18
  - `StyleMutationCapability`: For tools managing and applying style mutations via a `ChangeManager`.
19
19
  - `TargetCapability`: For tools requiring access to the page's current SDK `Target`.
20
- - `OriginLockCapability`: For tools enforcing cross-origin security via origin locks.
20
+ - `OriginLockCapability`: For tools enforcing cross-origin security via origin locks. Tools that fetch resources or state from the inspected page (such as styling or source code) must use this capability to verify that the target resource matches the conversation's established origin. This prevents the LLM from executing tools against out-of-origin elements or documents.
21
21
 
22
22
  ### Unified Context
23
23
 
@@ -213,6 +213,14 @@ export function handleEvent(event: Types.Events.Event): void {
213
213
 
214
214
  export async function finalize(): Promise<void> {
215
215
  const {rendererProcessesByFrame} = metaHandlerData();
216
+
217
+ const allowedProtocols = [
218
+ 'blob:',
219
+ 'file:',
220
+ 'filesystem:',
221
+ 'http:',
222
+ 'https:',
223
+ ];
216
224
  for (const [requestId, request] of requestMap.entries()) {
217
225
  // If we have an incomplete set of events here, we choose to drop the network
218
226
  // request rather than attempt to synthesize the missing data.
@@ -248,7 +256,7 @@ export async function finalize(): Promise<void> {
248
256
  }
249
257
 
250
258
  redirects.push({
251
- url: sendRequest.args.data.url,
259
+ url: allowedProtocols.some(p => sendRequest.args.data.url.startsWith(p)) ? sendRequest.args.data.url : '',
252
260
  priority: sendRequest.args.data.priority,
253
261
  requestMethod: sendRequest.args.data.requestMethod,
254
262
  ts,
@@ -360,15 +368,7 @@ export async function finalize(): Promise<void> {
360
368
  }
361
369
  }
362
370
 
363
- // TODO: consider allowing chrome / about.
364
- const allowedProtocols = [
365
- 'blob:',
366
- 'file:',
367
- 'filesystem:',
368
- 'http:',
369
- 'https:',
370
- ];
371
- if (!allowedProtocols.some(p => firstSendRequest.args.data.url.startsWith(p))) {
371
+ if (!allowedProtocols.some(p => finalSendRequest.args.data.url.startsWith(p))) {
372
372
  continue;
373
373
  }
374
374
 
@@ -572,7 +572,11 @@ export async function finalize(): Promise<void> {
572
572
  responseHeaders: request.receiveResponse?.args.data.headers ?? null,
573
573
  fetchPriorityHint: finalSendRequest.args.data.fetchPriorityHint ?? 'auto',
574
574
  initiator: finalSendRequest.args.data.initiator,
575
- stackTrace: finalSendRequest.args.data.stackTrace,
575
+ stackTrace: finalSendRequest.args.data.stackTrace?.map(
576
+ frame => ({
577
+ ...frame,
578
+ url: allowedProtocols.some(p => frame.url.startsWith(p)) ? frame.url : '',
579
+ })),
576
580
  timing,
577
581
  lrServerResponseTime,
578
582
  url,
@@ -9,7 +9,7 @@ import * as SDK from '../../core/sdk/sdk.js';
9
9
  import type * as ProtocolProxyApi from '../../generated/protocol-proxy-api.js';
10
10
  import type * as Protocol from '../../generated/protocol.js';
11
11
  import * as Bindings from '../bindings/bindings.js';
12
- import * as StackTrace from '../stack_trace/stack_trace.js';
12
+ import type * as StackTrace from '../stack_trace/stack_trace.js';
13
13
 
14
14
  export const enum Events {
15
15
  TOOLS_ADDED = 'ToolsAdded',
@@ -18,20 +18,13 @@ export const enum Events {
18
18
  TOOL_RESPONDED = 'ToolResponded',
19
19
  }
20
20
 
21
- export interface ExceptionDetails {
22
- readonly error: SDK.RemoteObject.RemoteObject;
23
- readonly description: string;
24
- readonly frames: StackTrace.ErrorStackParser.ParsedErrorFrame[];
25
- readonly cause?: ExceptionDetails;
26
- }
27
-
28
21
  export class Result {
29
22
  readonly status: Protocol.WebMCP.InvocationStatus;
30
23
  readonly output?: unknown;
31
24
  readonly errorText?: string;
32
25
  // TODO(crbug.com/494516094) Clean this up if the target disappears?
33
26
  readonly #exception?: SDK.RemoteObject.RemoteObject;
34
- #exceptionDetails?: Promise<ExceptionDetails|undefined>;
27
+ #symbolizedError?: Promise<Bindings.SymbolizedError.SymbolizedError|null>;
35
28
 
36
29
  constructor(
37
30
  status: Protocol.WebMCP.InvocationStatus, output: unknown|undefined, errorText: string|undefined,
@@ -42,46 +35,13 @@ export class Result {
42
35
  this.output = output;
43
36
  }
44
37
 
45
- get exceptionDetails(): Promise<ExceptionDetails|undefined>|undefined {
46
- if (!this.#exceptionDetails) {
47
- this.#exceptionDetails = this.#resolveExceptionDetails(this.#exception);
48
- }
49
- return this.#exceptionDetails;
50
- }
51
-
52
- async #resolveExceptionDetails(errorObj: SDK.RemoteObject.RemoteObject|undefined):
53
- Promise<ExceptionDetails|undefined> {
54
- if (!errorObj) {
55
- return undefined;
56
- }
57
- const error = SDK.RemoteObject.RemoteError.objectAsError(errorObj);
58
- const [details, cause] = await Promise.all([error.exceptionDetails(), error.cause()]);
59
- const description = error.errorStack;
60
-
61
- const frames =
62
- StackTrace.ErrorStackParser.parseSourcePositionsFromErrorStack(errorObj.runtimeModel(), error.errorStack) || [];
63
- if (details?.stackTrace) {
64
- StackTrace.ErrorStackParser.augmentErrorStackWithScriptIds(frames, details.stackTrace);
38
+ get symbolizedError(): Promise<Bindings.SymbolizedError.SymbolizedError|null>|undefined {
39
+ if (!this.#symbolizedError) {
40
+ this.#symbolizedError = this.#exception ?
41
+ Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance().createSymbolizedError(this.#exception) :
42
+ Promise.resolve(null);
65
43
  }
66
-
67
- if (cause?.subtype === 'error') {
68
- return {error: errorObj, description, frames, cause: await this.#resolveExceptionDetails(cause)};
69
- }
70
-
71
- if (cause?.type === 'string') {
72
- return {
73
- error: errorObj,
74
- description,
75
- frames,
76
- cause: {
77
- error: cause,
78
- description: cause.value as string,
79
- frames: [],
80
- }
81
- };
82
- }
83
-
84
- return {error: errorObj, description, frames};
44
+ return this.#symbolizedError;
85
45
  }
86
46
  }
87
47