chrome-devtools-frontend 1.0.1643099 → 1.0.1643855

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 (45) 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 +0 -1
  4. package/front_end/core/root/ExperimentNames.ts +0 -1
  5. package/front_end/core/sdk/ConsoleModel.ts +4 -0
  6. package/front_end/core/sdk/NetworkRequest.ts +12 -0
  7. package/front_end/core/sdk/SourceMap.ts +15 -18
  8. package/front_end/entrypoints/greendev_floaty/FloatyEntrypoint.ts +0 -2
  9. package/front_end/entrypoints/greendev_floaty/greendev_floaty.ts +0 -2
  10. package/front_end/entrypoints/main/MainImpl.ts +0 -6
  11. package/front_end/models/ai_assistance/AiAgent2.ts +1 -0
  12. package/front_end/models/ai_assistance/agents/AccessibilityAgent.ts +22 -16
  13. package/front_end/models/ai_assistance/agents/AiAgent.ts +19 -6
  14. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +5 -5
  15. package/front_end/models/ai_assistance/agents/ExecuteJavascript.ts +1 -94
  16. package/front_end/models/ai_assistance/agents/NetworkAgent.ts +25 -0
  17. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +22 -0
  18. package/front_end/models/ai_assistance/agents/StorageAgent.ts +54 -1
  19. package/front_end/models/ai_assistance/agents/StylingAgent.snapshot.txt +1 -2
  20. package/front_end/models/ai_assistance/agents/StylingAgent.ts +5 -0
  21. package/front_end/models/ai_assistance/tools/ExecuteJavaScript.ts +4 -10
  22. package/front_end/models/ai_assistance/tools/Tool.ts +6 -0
  23. package/front_end/models/bindings/DebuggerWorkspaceBinding.ts +6 -9
  24. package/front_end/models/bindings/DefaultScriptMapping.ts +2 -1
  25. package/front_end/models/bindings/SymbolizedError.ts +45 -35
  26. package/front_end/models/extensions/ExtensionAPI.ts +138 -47
  27. package/front_end/models/har/Importer.ts +1 -0
  28. package/front_end/models/source_map_scopes/FunctionCodeResolver.ts +12 -2
  29. package/front_end/models/stack_trace/DetailedErrorStackParser.ts +44 -51
  30. package/front_end/models/stack_trace/StackTrace.ts +7 -0
  31. package/front_end/models/stack_trace/StackTraceImpl.ts +13 -4
  32. package/front_end/models/stack_trace/StackTraceModel.ts +9 -8
  33. package/front_end/panels/accessibility/AccessibilitySidebarView.ts +2 -1
  34. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +2 -1
  35. package/front_end/panels/application/ApplicationPanelSidebar.ts +39 -0
  36. package/front_end/panels/application/ApplicationPanelTreeElement.ts +39 -0
  37. package/front_end/panels/application/CookieItemsView.ts +2 -2
  38. package/front_end/panels/application/resourcesSidebar.css +11 -0
  39. package/front_end/panels/console/SymbolizedErrorWidget.ts +10 -7
  40. package/front_end/panels/settings/emulation/DevicesSettingsTab.ts +1 -0
  41. package/front_end/panels/sources/SourcesPanel.ts +2 -1
  42. package/front_end/ui/legacy/StackedPane.ts +229 -0
  43. package/front_end/ui/legacy/ViewManager.ts +59 -169
  44. package/front_end/ui/legacy/legacy.ts +3 -1
  45. package/package.json +1 -1
package/eslint.config.mjs CHANGED
@@ -721,7 +721,8 @@ export default defineConfig([
721
721
  'test/**/*.ts',
722
722
  '**/testing/*.ts',
723
723
  'scripts/eslint_rules/test/**/*',
724
- 'extensions/cxx_debugging/e2e/**',
724
+ 'extensions/cxx_debugging/e2e/**/*.ts',
725
+ 'extensions/cxx_debugging/tests/**/*.ts',
725
726
  ],
726
727
 
727
728
  rules: {
@@ -750,6 +751,7 @@ export default defineConfig([
750
751
  ],
751
752
 
752
753
  '@devtools/check-test-definitions': 'error',
754
+ '@devtools/prefer-chai-assert': 'error',
753
755
  '@devtools/no-assert-strict-equal-for-arrays-and-objects': 'error',
754
756
  '@devtools/no-assert-deep-strict-equal': 'error',
755
757
  '@devtools/no-assert-equal': 'error',
@@ -19,8 +19,35 @@ export namespace Chrome {
19
19
  */
20
20
  readonly buildId?: string;
21
21
 
22
+ /**
23
+ * Returns the `content` and `encoding` of the resource. If a `callback`
24
+ * is provided, it is invoked with the `content` and `encoding` and the
25
+ * method returns `void`. If no `callback` is provided, the method returns
26
+ * a `Promise`.
27
+ *
28
+ * @param callback Optional callback to be invoked with the content and
29
+ * encoding.
30
+ * @returns A Promise that resolves to an object containing the content
31
+ * and encoding if no callback is provided, otherwise void. Rejects with
32
+ * an error object on failure.
33
+ */
34
+ getContent(): Promise<{content: string, encoding: string}>;
22
35
  getContent(callback: (content: string, encoding: string) => unknown): void;
23
- setContent(content: string, commit: boolean, callback?: (error?: Object) => unknown): void;
36
+
37
+ /**
38
+ * Sets the content of the resource. If a `callback` is provided, it is
39
+ * invoked when the content is set and the method returns `void`. If no
40
+ * `callback` is provided, the method returns a `Promise`.
41
+ *
42
+ * @param content The new content of the resource.
43
+ * @param commit Whether to commit the changes.
44
+ * @param callback Optional callback to be invoked when the content is
45
+ * set.
46
+ * @returns A Promise that resolves when the content is set if no callback
47
+ * is provided, otherwise void. Rejects with an error object on failure.
48
+ */
49
+ setContent(content: string, commit: boolean): Promise<void>;
50
+ setContent(content: string, commit: boolean, callback: (error?: object) => unknown): void;
24
51
  /**
25
52
  * Augments this resource's scopes information based on the list of {@link NamedFunctionRange}s
26
53
  * for improved debuggability and function naming.
@@ -40,18 +67,62 @@ export namespace Chrome {
40
67
  onResourceAdded: EventSink<(resource: Resource) => unknown>;
41
68
  onResourceContentCommitted: EventSink<(resource: Resource, content: string) => unknown>;
42
69
 
43
- eval(
44
- expression: string,
45
- options?: {scriptExecutionContext?: string, frameURL?: string, useContentScriptContext?: boolean},
46
- callback?: (result: unknown, exceptioninfo: {
47
- code: string,
48
- description: string,
49
- details: unknown[],
50
- isError: boolean,
51
- isException: boolean,
52
- value: string,
53
- }) => unknown): void;
70
+ /**
71
+ * Evaluates a JavaScript expression in the context of the inspected page.
72
+ *
73
+ * If a `callback` is provided, it is invoked with the result and
74
+ * exception information and the method returns `void`. If no `callback`
75
+ * is provided, the method returns a `Promise`.
76
+ *
77
+ * @template E The type of the value that the Promise resolves to.
78
+ * @param expression The JavaScript expression to evaluate.
79
+ * @param optionsOrCallback Optional options for evaluation, or a callback
80
+ * function.
81
+ * @param callback Optional callback to be invoked with the evaluation
82
+ * result and exception information.
83
+ * @returns A Promise that resolves to the result if no callback is
84
+ * provided, otherwise void. Rejects with an error object on failure.
85
+ */
86
+ eval<E = unknown>(expression: string, options?: {
87
+ scriptExecutionContext?: string,
88
+ frameURL?: string,
89
+ useContentScriptContext?: boolean,
90
+ }): Promise<E>;
91
+ eval(expression: string,
92
+ optionsOrCallback: {scriptExecutionContext?: string, frameURL?: string, useContentScriptContext?: boolean}|
93
+ undefined|((result: unknown, exceptioninfo: {
94
+ code: string,
95
+ description: string,
96
+ details: unknown[],
97
+ isError: boolean,
98
+ isException: boolean,
99
+ value: string,
100
+ }) => unknown),
101
+ callback?: (result: unknown, exceptioninfo: {
102
+ code: string,
103
+ description: string,
104
+ details: unknown[],
105
+ isError: boolean,
106
+ isException: boolean,
107
+ value: string,
108
+ }) => unknown): void;
109
+
110
+ /**
111
+ * Retrieves all resources within the inspected window.
112
+ *
113
+ * If a `callback` is provided, it is invoked with the array of resources
114
+ * and the method returns `void`. If no `callback` is provided, the method
115
+ * returns a `Promise`.
116
+ *
117
+ * @param callback Optional callback to be invoked with the array of
118
+ * resources.
119
+ * @returns A Promise that resolves to an array of resources if no
120
+ * callback is provided, otherwise void. Rejects with an error object on
121
+ * failure.
122
+ */
123
+ getResources(): Promise<Resource[]>;
54
124
  getResources(callback: (resources: Resource[]) => unknown): void;
125
+
55
126
  reload(reloadOptions?: {ignoreCache?: boolean, injectedScript?: string, userAgent?: string}): void;
56
127
  }
57
128
 
@@ -767,7 +767,6 @@ export enum DevtoolsExperiments {
767
767
  /* eslint-disable @typescript-eslint/naming-convention */
768
768
  'protocol-monitor' = 13,
769
769
  'instrumentation-breakpoints' = 61,
770
- 'use-source-map-scopes' = 76,
771
770
  'durable-messages' = 110,
772
771
  'jpeg-xl' = 111,
773
772
  'plus-button' = 112,
@@ -6,7 +6,6 @@ export enum ExperimentName {
6
6
  ALL = '*',
7
7
  PROTOCOL_MONITOR = 'protocol-monitor',
8
8
  INSTRUMENTATION_BREAKPOINTS = 'instrumentation-breakpoints',
9
- USE_SOURCE_MAP_SCOPES = 'use-source-map-scopes',
10
9
  DURABLE_MESSAGES = 'durable-messages',
11
10
  JPEG_XL = 'jpeg-xl',
12
11
  PLUS_BUTTON = 'plus-button',
@@ -547,6 +547,7 @@ export interface ConsoleMessageDetails {
547
547
  context?: string;
548
548
  affectedResources?: AffectedResources;
549
549
  category?: Protocol.Log.LogEntryCategory;
550
+ exceptionDetails?: Protocol.Runtime.ExceptionDetails;
550
551
  }
551
552
 
552
553
  export class ConsoleMessage {
@@ -570,6 +571,7 @@ export class ConsoleMessage {
570
571
  #exceptionId?: number = undefined;
571
572
  #affectedResources?: AffectedResources;
572
573
  category?: Protocol.Log.LogEntryCategory;
574
+ readonly exceptionDetails?: Protocol.Runtime.ExceptionDetails;
573
575
 
574
576
  /**
575
577
  * The parent frame of the `console.log` call of logpoints or conditional breakpoints
@@ -600,6 +602,7 @@ export class ConsoleMessage {
600
602
  this.workerId = details?.workerId;
601
603
  this.#affectedResources = details?.affectedResources;
602
604
  this.category = details?.category;
605
+ this.exceptionDetails = details?.exceptionDetails;
603
606
 
604
607
  if (!this.#executionContextId && this.#runtimeModel) {
605
608
  if (this.scriptId) {
@@ -646,6 +649,7 @@ export class ConsoleMessage {
646
649
  executionContextId: exceptionDetails.executionContextId,
647
650
  scriptId: exceptionDetails.scriptId,
648
651
  affectedResources,
652
+ exceptionDetails,
649
653
  };
650
654
  return new ConsoleMessage(
651
655
  runtimeModel, Protocol.Log.LogEntrySource.Javascript, Protocol.Log.LogEntryLevel.Error,
@@ -299,6 +299,10 @@ export class NetworkRequest extends Common.ObjectWrapper.ObjectWrapper<EventType
299
299
  #contentDataProvider?: () => Promise<TextUtils.ContentData.ContentDataOrError>;
300
300
  #isSameSite: boolean|null = null;
301
301
  #wasIntercepted = false;
302
+ /**
303
+ * Whether this request was imported from a HAR file.
304
+ */
305
+ #isImportedHar = false;
302
306
  #associatedData = new Map<string, object>();
303
307
  #hasOverriddenContent = false;
304
308
  #hasThirdPartyCookiePhaseoutIssue = false;
@@ -1148,6 +1152,14 @@ export class NetworkRequest extends Common.ObjectWrapper.ObjectWrapper<EventType
1148
1152
  this.#wasIntercepted = wasIntercepted;
1149
1153
  }
1150
1154
 
1155
+ isImportedHar(): boolean {
1156
+ return this.#isImportedHar;
1157
+ }
1158
+
1159
+ setIsImportedHar(isImportedHar: boolean): void {
1160
+ this.#isImportedHar = isImportedHar;
1161
+ }
1162
+
1151
1163
  setEarlyHintsHeaders(headers: NameValue[]): void {
1152
1164
  this.earlyHintsHeaders = headers;
1153
1165
  }
@@ -6,7 +6,6 @@ import * as TextUtils from '../../models/text_utils/text_utils.js';
6
6
  import * as ScopesCodec from '../../third_party/source-map-scopes-codec/source-map-scopes-codec.js';
7
7
  import * as Common from '../common/common.js';
8
8
  import * as Platform from '../platform/platform.js';
9
- import * as Root from '../root/root.js';
10
9
 
11
10
  import type {CallFrame, ScopeChainEntry} from './DebuggerModel.js';
12
11
  import {scopeTreeForScript} from './ScopeTreeCache.js';
@@ -584,23 +583,21 @@ export class SourceMap {
584
583
  lineNumber, columnNumber, sourceIndex, sourceURL, sourceLineNumber, sourceColumnNumber, names[nameIndex]));
585
584
  }
586
585
 
587
- if (Root.Runtime.experiments.isEnabled(Root.ExperimentNames.ExperimentName.USE_SOURCE_MAP_SCOPES)) {
588
- if (!this.#scopesInfo) {
589
- this.#scopesInfo = new SourceMapScopesInfo(this, {scopes: [], ranges: []});
590
- }
591
- if (map.scopes) {
592
- const {scopes, ranges} = ScopesCodec.decode(
593
- map as ScopesCodec.SourceMapJson,
594
- {mode: ScopesCodec.DecodeMode.LAX, generatedOffset: {line: baseLineNumber, column: baseColumnNumber}});
595
- this.#scopesInfo.addOriginalScopes(scopes);
596
- this.#scopesInfo.addGeneratedRanges(ranges);
597
- } else if (map.x_com_bloomberg_sourcesFunctionMappings) {
598
- const originalScopes = this.parseBloombergScopes(map);
599
- this.#scopesInfo.addOriginalScopes(originalScopes);
600
- } else {
601
- // Keep the OriginalScope[] tree array consistent with sources.
602
- this.#scopesInfo.addOriginalScopes(new Array(map.sources.length).fill(null));
603
- }
586
+ if (!this.#scopesInfo) {
587
+ this.#scopesInfo = new SourceMapScopesInfo(this, {scopes: [], ranges: []});
588
+ }
589
+ if (map.scopes) {
590
+ const {scopes, ranges} = ScopesCodec.decode(
591
+ map as ScopesCodec.SourceMapJson,
592
+ {mode: ScopesCodec.DecodeMode.LAX, generatedOffset: {line: baseLineNumber, column: baseColumnNumber}});
593
+ this.#scopesInfo.addOriginalScopes(scopes);
594
+ this.#scopesInfo.addGeneratedRanges(ranges);
595
+ } else if (map.x_com_bloomberg_sourcesFunctionMappings) {
596
+ const originalScopes = this.parseBloombergScopes(map);
597
+ this.#scopesInfo.addOriginalScopes(originalScopes);
598
+ } else {
599
+ // Keep the OriginalScope[] tree array consistent with sources.
600
+ this.#scopesInfo.addOriginalScopes(new Array(map.sources.length).fill(null));
604
601
  }
605
602
  }
606
603
 
@@ -593,8 +593,6 @@ async function init(): Promise<void> {
593
593
 
594
594
  safeRegisterExperiment(
595
595
  Root.ExperimentNames.ExperimentName.INSTRUMENTATION_BREAKPOINTS, 'Enable instrumentation breakpoints');
596
- safeRegisterExperiment(
597
- Root.ExperimentNames.ExperimentName.USE_SOURCE_MAP_SCOPES, 'Use scope information from source maps');
598
596
  safeRegisterExperiment(Root.ExperimentNames.ExperimentName.PROTOCOL_MONITOR, 'Protocol Monitor');
599
597
 
600
598
  const hostUnsyncedStorage: Common.Settings.SettingsBackingStore = {
@@ -247,8 +247,6 @@ async function init(): Promise<void> {
247
247
  // Register necessary experiments to avoid "Unknown experiment" errors.
248
248
  Root.Runtime.experiments.register(
249
249
  Root.ExperimentNames.ExperimentName.INSTRUMENTATION_BREAKPOINTS, 'Enable instrumentation breakpoints');
250
- Root.Runtime.experiments.register(
251
- Root.ExperimentNames.ExperimentName.USE_SOURCE_MAP_SCOPES, 'Use scope information from source maps');
252
250
  Root.Runtime.experiments.register(Root.ExperimentNames.ExperimentName.PROTOCOL_MONITOR, 'Protocol Monitor');
253
251
 
254
252
  const WINDOW_LOCAL_STORAGE: Common.Settings.SettingsBackingStore = {
@@ -365,8 +365,6 @@ export class MainImpl {
365
365
  // Debugging
366
366
  Root.Runtime.experiments.register(
367
367
  Root.ExperimentNames.ExperimentName.INSTRUMENTATION_BREAKPOINTS, 'Instrumentation breakpoints');
368
- Root.Runtime.experiments.register(
369
- Root.ExperimentNames.ExperimentName.USE_SOURCE_MAP_SCOPES, 'Use scope information from source maps');
370
368
 
371
369
  Root.Runtime.experiments.registerHostExperiment({
372
370
  name: Root.ExperimentNames.ExperimentName.DURABLE_MESSAGES,
@@ -392,10 +390,6 @@ export class MainImpl {
392
390
  requiresChromeRestart: false,
393
391
  });
394
392
 
395
- Root.Runtime.experiments.enableExperimentsByDefault([
396
- Root.ExperimentNames.ExperimentName.USE_SOURCE_MAP_SCOPES,
397
- ]);
398
-
399
393
  const enabledExperiments = Root.Runtime.Runtime.queryParam('enabledExperiments');
400
394
  if (enabledExperiments) {
401
395
  Root.Runtime.experiments.setServerEnabledExperiments(enabledExperiments.split(';'));
@@ -186,6 +186,7 @@ User query: ${enhancedQuery}`;
186
186
  changeManager: this.#changes,
187
187
  createExtensionScope: this.#createExtensionScope.bind(this),
188
188
  execJs: this.#execJs,
189
+ getExecutionContextNode: () => this.context instanceof DOMNodeContext ? this.context.getItem() : null,
189
190
  },
190
191
  options,
191
192
  ),
@@ -13,6 +13,8 @@ import {ChangeManager} from '../ChangeManager.js';
13
13
  import {LighthouseFormatter} from '../data_formatters/LighthouseFormatter.js';
14
14
  import {debugLog} from '../debug.js';
15
15
  import {ExtensionScope} from '../ExtensionScope.js';
16
+ import {ToolName} from '../tools/Tool.js';
17
+ import {ToolRegistry} from '../tools/ToolRegistry.js';
16
18
 
17
19
  import {
18
20
  AiAgent,
@@ -25,10 +27,8 @@ import {
25
27
  } from './AiAgent.js';
26
28
  import {
27
29
  type CreateExtensionScopeFunction,
28
- executeJavaScriptFunction,
29
30
  type ExecuteJsAgentOptions,
30
31
  executeJsCode,
31
- JavascriptExecutor
32
32
  } from './ExecuteJavascript.js';
33
33
 
34
34
  /**
@@ -117,7 +117,6 @@ export class AccessibilityAgent extends AiAgent<LHModel.ReporterTypes.ReportJSON
117
117
  (overrides?: LHModel.RunTypes.RunOverrides) => Promise<LHModel.ReporterTypes.ReportJSON|null>;
118
118
 
119
119
  #execJs: typeof executeJsCode;
120
- #javascriptExecutor: JavascriptExecutor;
121
120
  #changes: ChangeManager;
122
121
  #createExtensionScope: CreateExtensionScopeFunction;
123
122
 
@@ -129,14 +128,6 @@ export class AccessibilityAgent extends AiAgent<LHModel.ReporterTypes.ReportJSON
129
128
  this.#createExtensionScope = opts.createExtensionScope ?? ((changes: ChangeManager) => {
130
129
  return new ExtensionScope(changes, this.sessionId, this.#getDocumentBodyNode());
131
130
  });
132
- this.#javascriptExecutor = new JavascriptExecutor(
133
- {
134
- executionMode: this.executionMode,
135
- getContextNode: () => this.#getDocumentBodyNode(),
136
- createExtensionScope: this.#createExtensionScope.bind(this),
137
- changes: this.#changes,
138
- },
139
- this.#execJs);
140
131
  }
141
132
 
142
133
  get userTier(): string|undefined {
@@ -271,16 +262,31 @@ export class AccessibilityAgent extends AiAgent<LHModel.ReporterTypes.ReportJSON
271
262
  }
272
263
  });
273
264
 
274
- const executeJsDeclaration = executeJavaScriptFunction(this.#javascriptExecutor);
275
- this.declareFunction('executeJavaScript', {
276
- ...executeJsDeclaration,
277
- handler: async (params, options) => {
265
+ const executeJsTool = ToolRegistry.get(ToolName.EXECUTE_JAVASCRIPT);
266
+ if (!executeJsTool) {
267
+ throw new Error('Required tool "executeJavaScript" not found');
268
+ }
269
+ this.declareFunction(executeJsTool.name, {
270
+ description: executeJsTool.description,
271
+ parameters: executeJsTool.parameters,
272
+ displayInfoFromArgs: executeJsTool.displayInfoFromArgs,
273
+ handler: async (args, options) => {
278
274
  if (isImported) {
279
275
  return {
280
276
  error: 'Cannot use this tool on an imported file.',
281
277
  };
282
278
  }
283
- return await executeJsDeclaration.handler(params, options);
279
+ return await executeJsTool.handler(
280
+ args,
281
+ {
282
+ conversationContext: this.context ?? null,
283
+ changeManager: this.#changes,
284
+ createExtensionScope: this.#createExtensionScope.bind(this),
285
+ execJs: this.#execJs,
286
+ getExecutionContextNode: () => this.#getDocumentBodyNode(),
287
+ },
288
+ options,
289
+ );
284
290
  },
285
291
  });
286
292
 
@@ -509,8 +509,8 @@ export abstract class AiAgent<T> {
509
509
  this.#allowedOrigin = opts.allowedOrigin;
510
510
  }
511
511
 
512
- async enhanceQuery(query: string, selected: ConversationContext<T>|null, multimodalInputType?: MultimodalInputType):
513
- Promise<string>;
512
+ async enhanceQuery(query: string, selected: ConversationContext<T>|null,
513
+ multimodalInputType?: MultimodalInputType): Promise<string>;
514
514
  async enhanceQuery(query: string): Promise<string> {
515
515
  return query;
516
516
  }
@@ -557,9 +557,21 @@ export abstract class AiAgent<T> {
557
557
  return undefined;
558
558
  }
559
559
 
560
- buildRequest(
561
- part: Host.AidaClient.Part|Host.AidaClient.Part[],
562
- role: Host.AidaClient.Role.USER|Host.AidaClient.Role.ROLE_UNSPECIFIED): Host.AidaClient.DoConversationRequest {
560
+ /**
561
+ * Preamble features appended to the `client_version` in metadata.
562
+ * This is required ONLY for the Styling Agent for legacy reasons to serve
563
+ * different server-side preambles based on the Chrome version.
564
+ * Other agents should NOT set or override this.
565
+ * If you are curious about this, look for `do_conversation_handler.cc` in
566
+ * Google3 or chat to @jacktfranklin.
567
+ */
568
+ preambleFeatures(): string[] {
569
+ return [];
570
+ }
571
+
572
+ buildRequest(part: Host.AidaClient.Part|Host.AidaClient.Part[],
573
+ role: Host.AidaClient.Role.USER|
574
+ Host.AidaClient.Role.ROLE_UNSPECIFIED): Host.AidaClient.DoConversationRequest {
563
575
  const parts = Array.isArray(part) ? part : [part];
564
576
  const currentMessage: Host.AidaClient.Content = {
565
577
  parts,
@@ -600,7 +612,8 @@ export abstract class AiAgent<T> {
600
612
  disable_user_content_logging: !(this.#serverSideLoggingEnabled ?? false),
601
613
  string_session_id: this.#sessionId,
602
614
  user_tier: userTier,
603
- client_version: Root.Runtime.getChromeVersion(),
615
+ client_version:
616
+ Root.Runtime.getChromeVersion() + this.preambleFeatures().map(feature => `+${feature}`).join(''),
604
617
  },
605
618
 
606
619
  functionality_type: enableAidaFunctionCalling ? Host.AidaClient.FunctionalityType.AGENTIC_CHAT :
@@ -27,7 +27,7 @@ import {
27
27
  type RequestOptions,
28
28
  } from './AiAgent.js';
29
29
  import {FileContext} from './FileAgent.js';
30
- import {RequestContext} from './NetworkAgent.js';
30
+ import {getRequestContextOrigin, RequestContext} from './NetworkAgent.js';
31
31
  import {PerformanceTraceContext} from './PerformanceAgent.js';
32
32
  import {StorageContext} from './StorageAgent.js';
33
33
 
@@ -146,7 +146,7 @@ export class ContextSelectionAgent extends AiAgent<never> {
146
146
  let hasCrossOriginRequest = false;
147
147
  const requestsToShow: NetworkRequest[] = [];
148
148
  for (const request of Logs.NetworkLog.NetworkLog.instance().requests()) {
149
- const documentOrigin = Common.ParsedURL.ParsedURL.extractOrigin(request.documentURL);
149
+ const requestOrigin = getRequestContextOrigin(request);
150
150
  /**
151
151
  * NOTE: this origin check does not ensure that all the requests are
152
152
  * from the same origin as the target page. Instead, it ensures that
@@ -155,7 +155,7 @@ export class ContextSelectionAgent extends AiAgent<never> {
155
155
  * during the loading of the target page, and do not leak URLs from
156
156
  * other pages.
157
157
  */
158
- if (origin && documentOrigin !== origin) {
158
+ if (origin && requestOrigin !== origin) {
159
159
  hasCrossOriginRequest = true;
160
160
  continue;
161
161
  }
@@ -230,8 +230,8 @@ export class ContextSelectionAgent extends AiAgent<never> {
230
230
  return false;
231
231
  }
232
232
 
233
- const documentOrigin = Common.ParsedURL.ParsedURL.extractOrigin(req.documentURL);
234
- return !origin || documentOrigin === origin;
233
+ const requestOrigin = getRequestContextOrigin(req);
234
+ return !origin || requestOrigin === origin;
235
235
  });
236
236
 
237
237
  if (request) {
@@ -12,9 +12,7 @@ import {debugLog} from '../debug.js';
12
12
  import {EvaluateAction, formatError, SideEffectError} from '../EvaluateAction.js';
13
13
  import {FREESTYLER_WORLD_NAME} from '../injected.js';
14
14
 
15
- import type {
16
- AgentOptions as BaseAgentOptions, FunctionCallHandlerResult, FunctionDeclaration, FunctionHandlerOptions,} from
17
- './AiAgent.js';
15
+ import type {AgentOptions as BaseAgentOptions, FunctionCallHandlerResult, FunctionHandlerOptions,} from './AiAgent.js';
18
16
 
19
17
  const lockedString = i18n.i18n.lockedString;
20
18
 
@@ -28,97 +26,6 @@ export interface ExecuteJsAgentOptions extends BaseAgentOptions {
28
26
  execJs?: typeof executeJsCode;
29
27
  }
30
28
 
31
- // TODO(crbug.com/510206549): De-duplicate this function by migrating AccessibilityAgent to use
32
- // the registry-based ExecuteJavaScriptTool in tools/ExecuteJavaScript.ts.
33
- export function executeJavaScriptFunction(executor: JavascriptExecutor): FunctionDeclaration<
34
- {
35
- title: string,
36
- explanation: string,
37
- code: string,
38
- },
39
- unknown> {
40
- return {
41
- description:
42
- 'This function allows you to run JavaScript code on the inspected page to access the element styles and page content.\nCall this function to gather additional information or modify the page state. Call this function enough times to investigate the user request.',
43
- parameters: {
44
- type: Host.AidaClient.ParametersTypes.OBJECT,
45
- description: '',
46
- nullable: false,
47
- properties: {
48
- code: {
49
- type: Host.AidaClient.ParametersTypes.STRING,
50
- description:
51
- `JavaScript code snippet to run on the inspected page. Make sure the code is formatted for readability.
52
-
53
- # Instructions
54
-
55
- * To return data, define a top-level \`data\` variable and populate it with data you want to get. Only JSON-serializable objects can be assigned to \`data\`.
56
- * If you modify styles on an element, ALWAYS call the pre-defined global \`async setElementStyles(el: Element, styles: object)\` function. This function is an internal mechanism for you and should never be presented as a command/advice to the user.
57
- * **CRITICAL** Only get styles that might be relevant to the user request.
58
- * **CRITICAL** Never assume a selector for the elements unless you verified your knowledge.
59
- * **CRITICAL** Consider that \`data\` variable from the previous function calls are not available in a new function call.
60
-
61
- For example, the code to change element styles:
62
-
63
- \`\`\`
64
- await setElementStyles($0, {
65
- color: 'blue',
66
- });
67
- \`\`\`
68
-
69
- For example, the code to get overlapping elements:
70
-
71
- \`\`\`
72
- const data = {
73
- overlappingElements: Array.from(document.querySelectorAll('*'))
74
- .filter(el => {
75
- const rect = el.getBoundingClientRect();
76
- const popupRect = $0.getBoundingClientRect();
77
- return (
78
- el !== $0 &&
79
- rect.left < popupRect.right &&
80
- rect.right > popupRect.left &&
81
- rect.top < popupRect.bottom &&
82
- rect.bottom > popupRect.top
83
- );
84
- })
85
- .map(el => ({
86
- tagName: el.tagName,
87
- id: el.id,
88
- className: el.className,
89
- zIndex: window.getComputedStyle(el)['z-index']
90
- }))
91
- };
92
- \`\`\`
93
- `,
94
- },
95
- explanation: {
96
- type: Host.AidaClient.ParametersTypes.STRING,
97
- description: 'Explain why you want to run this code',
98
- },
99
- title: {
100
- type: Host.AidaClient.ParametersTypes.STRING,
101
- description: 'Provide a summary of what the code does. For example, "Checking related element styles".',
102
- },
103
- },
104
- required: ['code', 'explanation', 'title']
105
- },
106
- displayInfoFromArgs: params => {
107
- return {
108
- title: params.title,
109
- thought: params.explanation,
110
- action: params.code,
111
- };
112
- },
113
- handler: async (
114
- params,
115
- options,
116
- ) => {
117
- return await executor.executeAction(params.code, options);
118
- },
119
- };
120
- }
121
-
122
29
  export async function executeJsCode(
123
30
  functionDeclaration: string,
124
31
  {throwOnSideEffect, contextNode}: {throwOnSideEffect: boolean, contextNode: SDK.DOMModel.DOMNode|null}):
@@ -2,11 +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';
5
6
  import * as Host from '../../../core/host/host.js';
6
7
  import * as i18n from '../../../core/i18n/i18n.js';
8
+ import type * as Platform from '../../../core/platform/platform.js';
7
9
  import * as Root from '../../../core/root/root.js';
8
10
  import type * as SDK from '../../../core/sdk/sdk.js';
9
11
  import type * as NetworkTimeCalculator from '../../network_time_calculator/network_time_calculator.js';
12
+ import {extractContextOrigin} from '../AiOrigins.js';
10
13
  import {NetworkRequestFormatter} from '../data_formatters/NetworkRequestFormatter.js';
11
14
 
12
15
  import {
@@ -98,6 +101,24 @@ const UIStringsNotTranslate = {
98
101
 
99
102
  const lockedString = i18n.i18n.lockedString;
100
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
+
101
122
  export class RequestContext extends ConversationContext<SDK.NetworkRequest.NetworkRequest> {
102
123
  #request: SDK.NetworkRequest.NetworkRequest;
103
124
  #calculator: NetworkTimeCalculator.NetworkTransferTimeCalculator;
@@ -119,6 +140,10 @@ export class RequestContext extends ConversationContext<SDK.NetworkRequest.Netwo
119
140
  return this.#request.documentURL;
120
141
  }
121
142
 
143
+ override getOrigin(): string {
144
+ return getRequestContextOrigin(this.#request);
145
+ }
146
+
122
147
  override getItem(): SDK.NetworkRequest.NetworkRequest {
123
148
  return this.#request;
124
149
  }
@@ -15,6 +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
19
  import {sanitizeHeaders} from '../data_formatters/NetworkRequestFormatter.js';
19
20
  import {
20
21
  PerformanceInsightFormatter,
@@ -256,6 +257,27 @@ export class PerformanceTraceContext extends ConversationContext<AgentFocus> {
256
257
  }
257
258
  }
258
259
 
260
+ /**
261
+ * Returns the origin for a performance trace in the AI context.
262
+ *
263
+ * To prevent cross-origin prompt injection attacks, imported traces
264
+ * are isolated from live pages. We assign them a virtual origin
265
+ * (`imported-trace://${domain}`) so they do not share the origin of live pages
266
+ * (e.g., `https://${domain}`). This forces a conversation reset when transitioning
267
+ * between imported trace data and live pages.
268
+ */
269
+ override getOrigin(): string {
270
+ const parsedTrace = this.#focus.parsedTrace;
271
+ const url = this.getURL();
272
+ const origin = extractContextOrigin(url);
273
+ const isFresh = Tracing.FreshRecording.Tracker.instance().recordingIsFresh(parsedTrace);
274
+ if (!isFresh) {
275
+ const parsed = Common.ParsedURL.ParsedURL.fromString(origin as Platform.DevToolsPath.UrlString);
276
+ return `imported-trace://${parsed ? parsed.domain() : origin}`;
277
+ }
278
+ return origin;
279
+ }
280
+
259
281
  override getItem(): AgentFocus {
260
282
  return this.#focus;
261
283
  }