chrome-devtools-frontend 1.0.1643099 → 1.0.1645245

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/eslint.config.mjs +3 -1
  2. package/extension-api/ExtensionAPI.d.ts +83 -12
  3. package/front_end/core/host/UserMetrics.ts +3 -3
  4. package/front_end/core/root/ExperimentNames.ts +0 -1
  5. package/front_end/core/sdk/CSSPropertyParserMatchers.ts +2 -3
  6. package/front_end/core/sdk/ConsoleModel.ts +4 -0
  7. package/front_end/core/sdk/NetworkRequest.ts +12 -1
  8. package/front_end/core/sdk/SourceMap.ts +15 -18
  9. package/front_end/entrypoints/greendev_floaty/FloatyEntrypoint.ts +0 -2
  10. package/front_end/entrypoints/greendev_floaty/greendev_floaty.ts +0 -2
  11. package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshot.ts +37 -0
  12. package/front_end/entrypoints/main/MainImpl.ts +0 -6
  13. package/front_end/generated/SupportedCSSProperties.js +4 -2
  14. package/front_end/models/ai_assistance/AiAgent2.ts +23 -12
  15. package/front_end/models/ai_assistance/README.md +5 -4
  16. package/front_end/models/ai_assistance/agents/AccessibilityAgent.ts +22 -16
  17. package/front_end/models/ai_assistance/agents/AiAgent.ts +19 -6
  18. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +5 -5
  19. package/front_end/models/ai_assistance/agents/ExecuteJavascript.ts +1 -94
  20. package/front_end/models/ai_assistance/agents/NetworkAgent.ts +25 -0
  21. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +37 -0
  22. package/front_end/models/ai_assistance/agents/README.md +57 -0
  23. package/front_end/models/ai_assistance/agents/StorageAgent.ts +54 -1
  24. package/front_end/models/ai_assistance/agents/StylingAgent.snapshot.txt +1 -2
  25. package/front_end/models/ai_assistance/agents/StylingAgent.ts +31 -3
  26. package/front_end/models/ai_assistance/tools/ExecuteJavaScript.ts +12 -21
  27. package/front_end/models/ai_assistance/tools/GetStyles.ts +19 -12
  28. package/front_end/models/ai_assistance/tools/README.md +45 -0
  29. package/front_end/models/ai_assistance/tools/Tool.ts +78 -9
  30. package/front_end/models/ai_assistance/tools/ToolRegistry.ts +21 -5
  31. package/front_end/models/bindings/DebuggerWorkspaceBinding.ts +6 -9
  32. package/front_end/models/bindings/DefaultScriptMapping.ts +2 -1
  33. package/front_end/models/bindings/SymbolizedError.ts +45 -35
  34. package/front_end/models/extensions/ExtensionAPI.ts +138 -47
  35. package/front_end/models/har/Importer.ts +1 -0
  36. package/front_end/models/heap_snapshot/HeapSnapshotModel.ts +18 -2
  37. package/front_end/models/heap_snapshot/HeapSnapshotProxy.ts +4 -0
  38. package/front_end/models/source_map_scopes/FunctionCodeResolver.ts +12 -2
  39. package/front_end/models/stack_trace/DetailedErrorStackParser.ts +58 -52
  40. package/front_end/models/stack_trace/StackTrace.ts +7 -0
  41. package/front_end/models/stack_trace/StackTraceImpl.ts +13 -4
  42. package/front_end/models/stack_trace/StackTraceModel.ts +43 -9
  43. package/front_end/models/trace/Styles.ts +29 -7
  44. package/front_end/models/trace/handlers/PageLoadMetricsHandler.ts +33 -4
  45. package/front_end/models/trace/helpers/Timing.ts +10 -0
  46. package/front_end/models/trace/types/TraceEvents.ts +22 -2
  47. package/front_end/panels/accessibility/AccessibilitySidebarView.ts +2 -1
  48. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +9 -2
  49. package/front_end/panels/ai_assistance/ai_assistance-meta.ts +16 -0
  50. package/front_end/panels/application/ApplicationPanelSidebar.ts +83 -0
  51. package/front_end/panels/application/ApplicationPanelTreeElement.ts +39 -0
  52. package/front_end/panels/application/CookieItemsView.ts +2 -2
  53. package/front_end/panels/application/ServiceWorkersView.ts +2 -2
  54. package/front_end/panels/application/WebMCPView.ts +0 -1
  55. package/front_end/panels/application/components/BackForwardCacheView.ts +1 -2
  56. package/front_end/panels/application/resourcesSidebar.css +11 -0
  57. package/front_end/panels/console/ConsoleView.ts +6 -1
  58. package/front_end/panels/console/ConsoleViewMessage.ts +46 -213
  59. package/front_end/panels/console/SymbolizedErrorWidget.ts +14 -8
  60. package/front_end/panels/elements/AdoptedStyleSheetTreeElement.ts +0 -1
  61. package/front_end/panels/elements/ElementsTreeElement.ts +0 -2
  62. package/front_end/panels/elements/PropertyRenderer.ts +0 -1
  63. package/front_end/panels/elements/StylesSidebarPane.ts +9 -2
  64. package/front_end/panels/issues/AffectedResourcesView.ts +1 -1
  65. package/front_end/panels/issues/AffectedSourcesView.ts +1 -1
  66. package/front_end/panels/lighthouse/LighthouseReportRenderer.ts +0 -1
  67. package/front_end/panels/mobile_throttling/ThrottlingSettingsTab.ts +10 -0
  68. package/front_end/panels/network/NetworkDataGridNode.ts +1 -2
  69. package/front_end/panels/network/NetworkLogView.ts +34 -7
  70. package/front_end/panels/profiler/HeapProfileView.ts +0 -1
  71. package/front_end/panels/profiler/HeapSnapshotGridNodes.ts +0 -1
  72. package/front_end/panels/profiler/HeapSnapshotView.ts +1 -1
  73. package/front_end/panels/settings/components/SyncSection.ts +1 -1
  74. package/front_end/panels/settings/emulation/DevicesSettingsTab.ts +1 -0
  75. package/front_end/panels/sources/SourcesPanel.ts +2 -1
  76. package/front_end/panels/timeline/TimelineFlameChartView.ts +5 -4
  77. package/front_end/panels/timeline/TimelinePanel.ts +7 -0
  78. package/front_end/panels/timeline/TimelineUIUtils.ts +13 -14
  79. package/front_end/panels/timeline/TimingsTrackAppender.ts +7 -5
  80. package/front_end/panels/timeline/components/LayoutShiftDetails.ts +0 -1
  81. package/front_end/panels/timeline/components/NetworkRequestDetails.ts +0 -2
  82. package/front_end/panels/timeline/components/insights/ForcedReflow.ts +0 -1
  83. package/front_end/panels/timeline/components/insights/NodeLink.ts +0 -1
  84. package/front_end/panels/timeline/overlays/OverlaysImpl.ts +2 -0
  85. package/front_end/third_party/chromium/README.chromium +1 -1
  86. package/front_end/ui/helpers/OpenInNewTab.ts +3 -3
  87. package/front_end/ui/kit/link/Link.ts +16 -2
  88. package/front_end/ui/legacy/InspectorDrawerView.ts +14 -5
  89. package/front_end/ui/legacy/InspectorView.ts +4 -1
  90. package/front_end/ui/legacy/PlusButton.ts +6 -1
  91. package/front_end/ui/legacy/StackedPane.ts +229 -0
  92. package/front_end/ui/legacy/ViewManager.ts +59 -169
  93. package/front_end/ui/legacy/Widget.ts +19 -1
  94. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +95 -31
  95. package/front_end/ui/legacy/components/utils/JSPresentationUtils.ts +0 -1
  96. package/front_end/ui/legacy/components/utils/Linkifier.ts +2 -16
  97. package/front_end/ui/legacy/legacy.ts +3 -1
  98. package/front_end/ui/visual_logging/KnownContextValues.ts +5 -0
  99. package/package.json +1 -1
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
 
@@ -496,8 +496,9 @@ export enum Action {
496
496
  AiCodeGenerationRequestTriggeredFromSources = 205,
497
497
  AiCodeCompletionFreCompletedFromConsole = 206,
498
498
  AiCodeCompletionFreCompletedFromSources = 207,
499
- AiAssistanceOpenedFromStoragePanelFloatingButton = 208,
500
- MAX_VALUE = 209,
499
+ AiAssistanceOpenedFromApplicationPanelFloatingButton = 208,
500
+ AiAssistanceOpenedFromApplicationPanel = 209,
501
+ MAX_VALUE = 210,
501
502
  /* eslint-enable @typescript-eslint/naming-convention */
502
503
  }
503
504
 
@@ -767,7 +768,6 @@ export enum DevtoolsExperiments {
767
768
  /* eslint-disable @typescript-eslint/naming-convention */
768
769
  'protocol-monitor' = 13,
769
770
  'instrumentation-breakpoints' = 61,
770
- 'use-source-map-scopes' = 76,
771
771
  'durable-messages' = 110,
772
772
  'jpeg-xl' = 111,
773
773
  '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',
@@ -561,9 +561,8 @@ export class ColorMatcher extends matcherBase(ColorMatch) {
561
561
  const colorText = args.length >= 2 ? matching.getComputedTextRange(args[0], args[args.length - 1]) : '';
562
562
  // colorText holds the fully substituted parenthesized expression, so colorFunc + colorText is the color
563
563
  // function call.
564
- const isRelativeColorSyntax = Boolean(
565
- colorText.match(/^[^)]*\(\W*from\W+/) && !matching.hasUnresolvedSubstitutions(node) &&
566
- CSS.supports('color', colorFunc + colorText));
564
+ const isRelativeColorSyntax =
565
+ Boolean(colorText.match(/^[^)]*\(\W*from\W+/) && !matching.hasUnresolvedSubstitutions(node));
567
566
  if (!isRelativeColorSyntax) {
568
567
  return new ColorMatch(text, node);
569
568
  }
@@ -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
  }
@@ -1908,7 +1920,6 @@ export interface EventTypes {
1908
1920
  [Events.RESPONSE_HEADERS_CHANGED]: void;
1909
1921
  [Events.WEBSOCKET_FRAME_ADDED]: WebSocketFrame;
1910
1922
  [Events.DIRECTSOCKET_CHUNK_ADDED]: DirectSocketChunk;
1911
- [Events.DIRECTSOCKET_CHUNK_ADDED]: DirectSocketChunk;
1912
1923
  [Events.EVENT_SOURCE_MESSAGE_ADDED]: EventSourceMessage;
1913
1924
  [Events.TRUST_TOKEN_RESULT_ADDED]: void;
1914
1925
  }
@@ -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 = {
@@ -2740,6 +2740,8 @@ export abstract class HeapSnapshot {
2740
2740
  const nodeAId = baseIds[i];
2741
2741
  if (nodeAId < nodeB.id()) {
2742
2742
  diff.deletedIndexes.push(baseIndexes[i]);
2743
+ diff.deletedIds.push(nodeAId);
2744
+ diff.deletedSelfSizes.push(baseSelfSizes[i]);
2743
2745
  diff.removedCount++;
2744
2746
  diff.removedSize += baseSelfSizes[i];
2745
2747
  ++i;
@@ -2747,6 +2749,8 @@ export abstract class HeapSnapshot {
2747
2749
  nodeAId >
2748
2750
  nodeB.id()) { // Native nodes(e.g. dom groups) may have ids less than max JS object id in the base snapshot
2749
2751
  diff.addedIndexes.push(indexes[j]);
2752
+ diff.addedIds.push(nodeB.id());
2753
+ diff.addedSelfSizes.push(nodeB.selfSize());
2750
2754
  diff.addedCount++;
2751
2755
  diff.addedSize += nodeB.selfSize();
2752
2756
  nodeB.nodeIndex = indexes[++j];
@@ -2757,12 +2761,16 @@ export abstract class HeapSnapshot {
2757
2761
  }
2758
2762
  while (i < l) {
2759
2763
  diff.deletedIndexes.push(baseIndexes[i]);
2764
+ diff.deletedIds.push(baseIds[i]);
2765
+ diff.deletedSelfSizes.push(baseSelfSizes[i]);
2760
2766
  diff.removedCount++;
2761
2767
  diff.removedSize += baseSelfSizes[i];
2762
2768
  ++i;
2763
2769
  }
2764
2770
  while (j < m) {
2765
2771
  diff.addedIndexes.push(indexes[j]);
2772
+ diff.addedIds.push(nodeB.id());
2773
+ diff.addedSelfSizes.push(nodeB.selfSize());
2766
2774
  diff.addedCount++;
2767
2775
  diff.addedSize += nodeB.selfSize();
2768
2776
  nodeB.nodeIndex = indexes[++j];
@@ -2975,6 +2983,35 @@ export abstract class HeapSnapshot {
2975
2983
  return {paths, limitsReached};
2976
2984
  }
2977
2985
 
2986
+ getDominatorsOf(nodeIndex: number): HeapSnapshotModel.HeapSnapshotModel.DominatorChain {
2987
+ const chain: HeapSnapshotModel.HeapSnapshotModel.DominatorNode[] = [];
2988
+ let currentIndex = nodeIndex;
2989
+ const rootIndex = this.rootNodeIndex;
2990
+
2991
+ while (currentIndex !== undefined) {
2992
+ const node = this.createNode(currentIndex);
2993
+ chain.push({
2994
+ nodeId: node.id(),
2995
+ nodeIndex: currentIndex,
2996
+ nodeName: node.name(),
2997
+ retainedSize: node.retainedSize(),
2998
+ selfSize: node.selfSize(),
2999
+ });
3000
+
3001
+ if (currentIndex === rootIndex) {
3002
+ break;
3003
+ }
3004
+
3005
+ const nextIndex = node.dominatorIndex();
3006
+ if (nextIndex === currentIndex) {
3007
+ break;
3008
+ }
3009
+ currentIndex = nextIndex;
3010
+ }
3011
+
3012
+ return chain;
3013
+ }
3014
+
2978
3015
  createAddedNodesProvider(baseSnapshotId: number, classKey: string): HeapSnapshotNodesProvider {
2979
3016
  const snapshotDiff = this.#snapshotDiffs[baseSnapshotId];
2980
3017
  const diffForClass = snapshotDiff[classKey];
@@ -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(';'));
@@ -3860,7 +3860,8 @@ export const generatedProperties = [
3860
3860
  {
3861
3861
  "keywords": [
3862
3862
  "auto",
3863
- "none"
3863
+ "none",
3864
+ "normal"
3864
3865
  ],
3865
3866
  "name": "position-anchor"
3866
3867
  },
@@ -6987,7 +6988,8 @@ export const generatedPropertyValues = {
6987
6988
  "position-anchor": {
6988
6989
  "values": [
6989
6990
  "auto",
6990
- "none"
6991
+ "none",
6992
+ "normal"
6991
6993
  ]
6992
6994
  },
6993
6995
  "position-area": {
@@ -3,9 +3,11 @@
3
3
  // found in the LICENSE file.
4
4
 
5
5
  import * as Host from '../../core/host/host.js';
6
+ import * as SDK from '../../core/sdk/sdk.js';
6
7
 
7
8
  import {
8
9
  AiAgent,
10
+ type AllowedOriginResult,
9
11
  type ContextResponse,
10
12
  type ConversationContext,
11
13
  type MultimodalInputType,
@@ -19,7 +21,7 @@ import {debugLog} from './debug.js';
19
21
  import {ExtensionScope} from './ExtensionScope.js';
20
22
  import type {Skill, SkillName} from './skills/Skill.js';
21
23
  import {SKILLS} from './skills/SkillRegistry.js';
22
- import type {Tool} from './tools/Tool.js';
24
+ import type {AllToolsContext, Tool, ToolArgs} from './tools/Tool.js';
23
25
  import {ToolRegistry} from './tools/ToolRegistry.js';
24
26
 
25
27
  const SKILL_DISPLAY_NAMES: Record<SkillName, string> = {
@@ -35,6 +37,7 @@ export class AiAgent2 extends AiAgent<unknown> {
35
37
  #skillsInjected = false;
36
38
  #changes = new ChangeManager();
37
39
  #execJs: typeof executeJsCode;
40
+ readonly #allowedOrigin?: () => AllowedOriginResult;
38
41
 
39
42
  get options(): RequestOptions {
40
43
  return {};
@@ -46,6 +49,7 @@ export class AiAgent2 extends AiAgent<unknown> {
46
49
  constructor(opts: ExecuteJsAgentOptions) {
47
50
  super(opts);
48
51
  this.#execJs = opts.execJs ?? executeJsCode;
52
+ this.#allowedOrigin = opts.allowedOrigin;
49
53
  this.#declaredTools.add('learnSkills');
50
54
  const skillsList = Object.keys(SKILLS).join(', ');
51
55
  this.declareFunction<{skills: SkillName[]}>('learnSkills', {
@@ -169,7 +173,7 @@ User query: ${enhancedQuery}`;
169
173
  * Declares a tool to be available to the agent model, verifying first that
170
174
  * it hasn't already been declared to prevent duplicate declaration errors.
171
175
  */
172
- #declareTool(tool: Tool): void {
176
+ #declareTool(tool: Tool<ToolArgs, unknown, AllToolsContext>): void {
173
177
  if (this.#declaredTools.has(tool.name)) {
174
178
  debugLog(`AiAgent2: Tool ${tool.name} is already declared`);
175
179
  return;
@@ -179,19 +183,26 @@ User query: ${enhancedQuery}`;
179
183
  description: tool.description,
180
184
  parameters: tool.parameters,
181
185
  displayInfoFromArgs: tool.displayInfoFromArgs,
182
- handler: (args, options) => tool.handler(
183
- args,
184
- {
185
- conversationContext: this.context ?? null,
186
- changeManager: this.#changes,
187
- createExtensionScope: this.#createExtensionScope.bind(this),
188
- execJs: this.#execJs,
189
- },
190
- options,
191
- ),
186
+ handler: (args, options) => {
187
+ const context: AllToolsContext = {
188
+ conversationContext: this.context ?? null,
189
+ changeManager: this.#changes,
190
+ createExtensionScope: this.#createExtensionScope.bind(this),
191
+ execJs: this.#execJs,
192
+ getExecutionContextNode: () => this.context instanceof DOMNodeContext ? this.context.getItem() : null,
193
+ getTarget: () => SDK.TargetManager.TargetManager.instance().primaryPageTarget(),
194
+ getEstablishedOrigin: () => this.#getConversationOrigin(),
195
+ };
196
+ return tool.handler(args, context, options);
197
+ },
192
198
  });
193
199
  }
194
200
 
201
+ #getConversationOrigin(): string|undefined {
202
+ const allowed = this.#allowedOrigin?.();
203
+ return allowed && 'origin' in allowed ? allowed.origin : undefined;
204
+ }
205
+
195
206
  get activeSkills(): Set<SkillName> {
196
207
  return this.#activeSkills;
197
208
  }
@@ -24,7 +24,7 @@ To deal with the work to take an object that is the AI agent's context and turn
24
24
 
25
25
  ## Future Architecture (V2) - WIP
26
26
 
27
- We are currently working on migrating the DevTools AI Assistance from a siloed multi-agent architecture to a unified, skill-based single-agent architecture (`AIAgent2`).
27
+ We are migrating the DevTools AI Assistance from a siloed multi-agent architecture to a unified, skill-based single-agent architecture (`AIAgent2`).
28
28
 
29
29
  In this new architecture:
30
30
  - A single agent (`AIAgent2`) handles multiple domains.
@@ -36,15 +36,16 @@ This work is currently in progress and behind a feature flag.
36
36
  ### Skills Build System
37
37
 
38
38
  To support dynamic loading of skills, we generate JavaScript files from Markdown files containing skill definitions.
39
- We use a nested `BUILD.gn` file in the `skills/` subdirectory specifically for this purpose. This ensures that GN's `target_gen_dir` points to `gen/front_end/models/ai_assistance/skills/`, placing the generated `.skill.js` files in the same relative structure as their source `.md` files. This allows TypeScript files in the `skills/` directory (like `map.ts`) to import the generated files using relative paths (e.g., `./styling.skill.js`) seamlessly.
39
+ We use a nested `BUILD.gn` file in the `skills/` subdirectory specifically for this purpose. This ensures that GN's `target_gen_dir` points to `gen/front_end/models/ai_assistance/skills/`, placing the generated `.skill.js` files in the same relative structure as their source `.md` files. This allows TypeScript files in the `skills/` directory (like `map.ts`) to import the generated files using relative paths (e.g., `./styling.skill.js`) seamlessly. See the [Skills README](skills/README.md) for full details.
40
40
 
41
41
  ### Tools and ToolRegistry
42
42
 
43
43
  To support skills requiring execution of code or fetching page state (like computed styles), the architecture defines **Tools** in the `tools/` directory.
44
44
 
45
45
  - **BaseTool**: A non-generic base interface capturing tool metadata (`name`, `description`, `parameters`). This acts as the type-erased representation for generic registry storage and fallback string lookups.
46
- - **Tool**: A generic interface extending `BaseTool` that binds argument types (`Args`) to the schema `parameters` and the `handler()` method. This ensures compile-time safety and alignment inside each tool implementation.
47
- - **ToolRegistry**: A static registry (`ToolRegistry`) storing instantiated tools. It uses TypeScript function overloading and generic lookups (`static get<K extends keyof typeof TOOLS>(name: K): typeof TOOLS[K]`) to return the precise class type of each tool, preventing type-erasure and escape-hatches (such as `any` or `as unknown` type assertions) at integration points like `AiAgent.ts`.
46
+ - **Tool**: A generic interface parameterized by `<Args, ReturnType, ContextType>` that binds parameter argument types and handler execution to strict contracts. `ContextType` defaults to `BaseToolCapability`, ensuring that each tool explicitly requests only the dependencies it requires.
47
+ - **Capability Contexts**: Instead of passing a monolithic grab-bag context to all tools, dependencies are broken into narrow capability interfaces (e.g. `PageExecutionCapability`, `StyleMutationCapability`, `TargetCapability`, `OriginLockCapability`). Tools declare their required dependencies by intersecting these interfaces on their generic `ContextType` definition. The caller/Agent fulfills the complete capability context (`AllToolsContext`), guaranteeing 100% compile-time type safety for dependencies without runtime checks.
48
+ - **ToolRegistry**: A static registry (`ToolRegistry`) storing instantiated tools. It uses TypeScript function overloading and generic lookups (`static get<K extends keyof typeof TOOLS>(name: K): typeof TOOLS[K]`) to return the precise class type of each tool, preventing type-erasure and escape-hatches (such as `any` or `as unknown` type assertions) at integration points like `AiAgent2.ts`. See the [Tools README](tools/README.md) for authoring instructions.
48
49
 
49
50
  ## Performance specific documentation
50
51
 
@@ -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