chrome-devtools-frontend 1.0.1696961 → 1.0.1697998

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 (121) hide show
  1. package/.geminiignore +0 -1
  2. package/.stylelintignore +2 -0
  3. package/front_end/core/common/Color.ts +5 -2
  4. package/front_end/core/common/VersionController.ts +5 -1
  5. package/front_end/core/host/InspectorFrontendHostAPI.ts +16 -3
  6. package/front_end/core/host/InspectorFrontendHostStub.ts +3 -2
  7. package/front_end/core/host/ResourceLoader.ts +1 -2
  8. package/front_end/core/platform/api/HostRuntime.ts +1 -0
  9. package/front_end/core/platform/browser/HostRuntime.ts +4 -0
  10. package/front_end/core/platform/node/HostRuntime.ts +5 -0
  11. package/front_end/core/root/Runtime.ts +5 -0
  12. package/front_end/core/sdk/AnimationModel.ts +9 -1
  13. package/front_end/core/sdk/DOMModel.ts +13 -0
  14. package/front_end/core/sdk/SourceMapScopeChainEntry.ts +9 -6
  15. package/front_end/core/sdk/SourceMapScopesInfo.ts +3 -1
  16. package/front_end/foundation/Universe.ts +8 -0
  17. package/front_end/generated/InspectorBackendCommands.ts +4 -1
  18. package/front_end/generated/protocol-mapping.d.ts +19 -0
  19. package/front_end/generated/protocol-proxy-api.d.ts +15 -0
  20. package/front_end/generated/protocol.ts +43 -2
  21. package/front_end/models/ai_assistance/AiAgent2.ts +20 -18
  22. package/front_end/models/ai_assistance/AiConversation.ts +38 -12
  23. package/front_end/models/ai_assistance/agents/AccessibilityAgent.ts +9 -2
  24. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +26 -0
  25. package/front_end/models/ai_assistance/agents/StylingAgent.ts +13 -4
  26. package/front_end/models/ai_assistance/ai_assistance.ts +4 -4
  27. package/front_end/models/ai_assistance/skills/performance.md +4 -3
  28. package/front_end/models/ai_assistance/tools/CookieUtils.ts +10 -8
  29. package/front_end/models/ai_assistance/tools/ExecuteJavaScript.ts +1 -1
  30. package/front_end/models/ai_assistance/tools/GetElementAccessibilityDetails.ts +1 -3
  31. package/front_end/models/ai_assistance/tools/GetNetworkRequestDetails.ts +9 -8
  32. package/front_end/models/ai_assistance/tools/GetSourceContent.ts +5 -6
  33. package/front_end/models/ai_assistance/tools/GetStorageValues.ts +6 -25
  34. package/front_end/models/ai_assistance/tools/GetStyles.ts +1 -3
  35. package/front_end/models/ai_assistance/tools/{GetFunctionCode.ts → GetTraceFunctionCode.ts} +26 -13
  36. package/front_end/models/ai_assistance/tools/{GetResourceContent.ts → GetTraceResourceContent.ts} +31 -11
  37. package/front_end/models/ai_assistance/tools/ListNetworkRequests.ts +7 -6
  38. package/front_end/models/ai_assistance/tools/ListPageOrigins.ts +8 -5
  39. package/front_end/models/ai_assistance/tools/ListSources.ts +8 -10
  40. package/front_end/models/ai_assistance/tools/ListStorageKeys.ts +6 -25
  41. package/front_end/models/ai_assistance/tools/README.md +5 -3
  42. package/front_end/models/ai_assistance/tools/ResolveDevtoolsNodePath.ts +1 -2
  43. package/front_end/models/ai_assistance/tools/Tool.ts +54 -16
  44. package/front_end/models/ai_assistance/tools/ToolRegistry.ts +6 -6
  45. package/front_end/models/change_tracker/ChangeTracker.ts +89 -0
  46. package/front_end/models/change_tracker/change_tracker.ts +9 -0
  47. package/front_end/models/elements/DOMChanges.ts +238 -0
  48. package/front_end/models/elements/elements.ts +2 -0
  49. package/front_end/models/issues_manager/MarkdownIssueDescription.ts +2 -2
  50. package/front_end/models/live-metrics/LiveMetrics.ts +1 -2
  51. package/front_end/models/trace/ModelImpl.ts +0 -6
  52. package/front_end/models/trace/Processor.ts +4 -7
  53. package/front_end/models/trace/helpers/Timing.ts +2 -1
  54. package/front_end/models/trace/types/Overlays.ts +12 -1
  55. package/front_end/panels/accessibility/AccessibilityAnnouncementRecordingView.ts +67 -19
  56. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +3 -1
  57. package/front_end/panels/ai_assistance/components/MarkdownRendererWithCodeBlock.ts +25 -5
  58. package/front_end/panels/application/IndexedDBViews.ts +1 -1
  59. package/front_end/panels/browser_debugger/ObjectEventListenersSidebarPane.ts +2 -3
  60. package/front_end/panels/elements/DOMTreeContextMenu.ts +16 -23
  61. package/front_end/panels/elements/ElementsTreeElement.ts +384 -79
  62. package/front_end/panels/elements/ElementsTreeOutline.ts +541 -82
  63. package/front_end/panels/elements/EventListenersWidget.ts +0 -1
  64. package/front_end/panels/event_listeners/EventListenersView.ts +241 -297
  65. package/front_end/panels/linear_memory_inspector/LinearMemoryInspectorController.ts +1 -1
  66. package/front_end/panels/mobile_throttling/ThrottlingSettingsTab.ts +10 -2
  67. package/front_end/panels/network/NetworkConfigView.ts +141 -116
  68. package/front_end/panels/network/NetworkPanel.ts +140 -52
  69. package/front_end/panels/network/RequestHeadersView.ts +65 -32
  70. package/front_end/panels/network/components/RequestHeadersView.css +7 -1
  71. package/front_end/panels/network/forward/BackendLinking.ts +52 -0
  72. package/front_end/panels/network/forward/forward.ts +2 -0
  73. package/front_end/panels/settings/BackendLinkingSettingsTab.ts +308 -0
  74. package/front_end/panels/settings/backendLinkingSettingsTab.css +78 -0
  75. package/front_end/panels/settings/settings-meta.ts +25 -0
  76. package/front_end/panels/settings/settings.ts +2 -0
  77. package/front_end/panels/timeline/README.md +3 -2
  78. package/front_end/panels/timeline/TimelineFlameChartView.ts +23 -2
  79. package/front_end/panels/timeline/TimelineTreeView.ts +11 -1
  80. package/front_end/panels/timeline/overlays/OverlaysImpl.ts +64 -4
  81. package/front_end/panels/timeline/timelineFlameChartView.css +47 -0
  82. package/front_end/panels/whats_new/ReleaseNoteText.ts +8 -8
  83. package/front_end/panels/whats_new/resources/WNDT.md +6 -6
  84. package/front_end/third_party/chromium/README.chromium +1 -1
  85. package/front_end/third_party/puppeteer/README.chromium +2 -2
  86. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.d.ts +6 -0
  87. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +5 -8
  88. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Coverage.d.ts +6 -0
  89. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Coverage.d.ts.map +1 -1
  90. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Coverage.js.map +1 -1
  91. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/ScreenRecording.d.ts.map +1 -1
  92. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/ScreenRecording.js +1 -5
  93. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/ScreenRecording.js.map +1 -1
  94. package/front_end/third_party/puppeteer/package/lib/puppeteer/revisions.d.ts +3 -3
  95. package/front_end/third_party/puppeteer/package/lib/puppeteer/revisions.js +3 -3
  96. package/front_end/third_party/puppeteer/package/lib/puppeteer/revisions.js.map +1 -1
  97. package/front_end/third_party/puppeteer/package/lib/puppeteer/util/version.d.ts +1 -1
  98. package/front_end/third_party/puppeteer/package/lib/puppeteer/util/version.js +1 -1
  99. package/front_end/third_party/puppeteer/package/lib/types.d.ts +6 -0
  100. package/front_end/third_party/puppeteer/package/package.json +2 -2
  101. package/front_end/third_party/puppeteer/package/src/cdp/Coverage.ts +6 -0
  102. package/front_end/third_party/puppeteer/package/src/cdp/ScreenRecording.ts +3 -7
  103. package/front_end/third_party/puppeteer/package/src/revisions.ts +3 -3
  104. package/front_end/third_party/puppeteer/package/src/util/version.ts +1 -1
  105. package/front_end/third_party/web-vitals/README.chromium +2 -2
  106. package/front_end/third_party/web-vitals/package/dist/modules/attribution/onINP.js +8 -5
  107. package/front_end/third_party/web-vitals/package/package.json +1 -1
  108. package/front_end/third_party/web-vitals/package/src/attribution/onINP.ts +8 -4
  109. package/front_end/third_party/web-vitals/rebuild.sh +2 -2
  110. package/front_end/ui/comments/CommentOverlayManager.ts +169 -40
  111. package/front_end/ui/comments/CommentThreadWidget.ts +221 -0
  112. package/front_end/ui/comments/CommentsOverlayWidget.ts +124 -12
  113. package/front_end/ui/comments/commentThreadWidget.css +173 -0
  114. package/front_end/ui/comments/comments.ts +2 -0
  115. package/front_end/ui/comments/commentsOverlay.css +6 -0
  116. package/front_end/ui/legacy/ContextMenu.ts +19 -0
  117. package/front_end/ui/legacy/SoftContextMenu.ts +6 -0
  118. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +1 -1
  119. package/front_end/ui/legacy/components/perf_ui/FlameChart.ts +66 -22
  120. package/front_end/ui/visual_logging/KnownContextValues.ts +4 -0
  121. package/package.json +9 -8
@@ -14,6 +14,7 @@ import {
14
14
  type DataTool,
15
15
  isOriginAllowedByLock,
16
16
  type OriginLockCapability,
17
+ resolveOriginFromLock,
17
18
  ToolName,
18
19
  } from './Tool.js';
19
20
 
@@ -76,12 +77,12 @@ export class ListNetworkRequestsTool implements
76
77
  const requests: NetworkRequestSummary[] = [];
77
78
  // A conversation is locked to an origin once the first query is made.
78
79
  // We only allow inspecting requests matching the conversation's established origin.
79
- const establishedOrigin = context.getEstablishedOrigin();
80
- if (!establishedOrigin || establishedOrigin.isOpaque()) {
81
- return {
82
- error: 'Opaque origin not allowed',
83
- };
80
+ const originLock = context.getOriginLock();
81
+ const originResult = resolveOriginFromLock(originLock);
82
+ if ('error' in originResult) {
83
+ return originResult;
84
84
  }
85
+ const establishedOrigin = originResult.origin;
85
86
 
86
87
  // eslint-disable-next-line @devtools/no-instance-of-migrated-singletons
87
88
  const networkLog = this.#networkLog ?? Logs.NetworkLog.NetworkLog.instance();
@@ -89,7 +90,7 @@ export class ListNetworkRequestsTool implements
89
90
  const requestsToShow: SDK.NetworkRequest.NetworkRequest[] = [];
90
91
  for (const request of networkLog.requests()) {
91
92
  // If the request's initiator origin does not match the locked origin, skip it.
92
- if (!isOriginAllowedByLock(establishedOrigin, request.initiatorSecurityOrigin())) {
93
+ if (!isOriginAllowedByLock(originLock, request.initiatorSecurityOrigin())) {
93
94
  hasCrossOriginRequest = true;
94
95
  continue;
95
96
  }
@@ -10,7 +10,9 @@ import {
10
10
  type BaseToolCapability,
11
11
  type DataHandlerResult,
12
12
  type DataTool,
13
+ isOriginAllowedByLock,
13
14
  type OriginLockCapability,
15
+ resolveOriginFromLock,
14
16
  ToolName,
15
17
  } from './Tool.js';
16
18
 
@@ -57,14 +59,15 @@ export class ListPageOriginsTool implements
57
59
  const targetManager = SDK.TargetManager.TargetManager.instance();
58
60
  const primaryPageTarget = targetManager.primaryPageTarget();
59
61
 
60
- const establishedOrigin = context.getEstablishedOrigin();
61
- if (!establishedOrigin || establishedOrigin.isOpaque()) {
62
- return {error: 'No origin available or not allowed.'};
62
+ const originLock = context.getOriginLock();
63
+ const originResult = resolveOriginFromLock(originLock);
64
+ if ('error' in originResult) {
65
+ return originResult;
63
66
  }
64
67
 
65
68
  const pageOrigin =
66
69
  primaryPageTarget ? SDK.SecurityOrigin.SecurityOrigin.create(primaryPageTarget.inspectedURL()) : null;
67
- if (!pageOrigin || !pageOrigin.isSameOriginWith(establishedOrigin)) {
70
+ if (!pageOrigin || !isOriginAllowedByLock(originLock, pageOrigin)) {
68
71
  return {error: 'No origin available or not allowed.'};
69
72
  }
70
73
 
@@ -77,7 +80,7 @@ export class ListPageOriginsTool implements
77
80
  // Filter out frames that are not same-origin to the page's allowed origin.
78
81
  // Under site isolation, frames can be hosted on different targets/processes,
79
82
  // so we check the security origin of the frame directly instead of the target.
80
- if (!frameOrigin.isSameOriginWith(establishedOrigin)) {
83
+ if (!isOriginAllowedByLock(originLock, frameOrigin)) {
81
84
  continue;
82
85
  }
83
86
  if (!origins.some(existing => existing.isSameOriginWith(frameOrigin))) {
@@ -14,6 +14,8 @@ import {
14
14
  type DataTool,
15
15
  isOriginAllowedByLock,
16
16
  type OriginLockCapability,
17
+ type OriginLockState,
18
+ resolveOriginFromLock,
17
19
  ToolName,
18
20
  } from './Tool.js';
19
21
 
@@ -75,8 +77,9 @@ export class ListSourcesTool implements
75
77
  }
76
78
  }
77
79
 
80
+ const originLock: OriginLockState = {status: 'ESTABLISHED_ORIGIN', origin: establishedOrigin};
78
81
  return [...uiSourceCodes.values()].filter(
79
- file => isOriginAllowedByLock(establishedOrigin, FileContext.originForUISourceCode(file)));
82
+ file => isOriginAllowedByLock(originLock, FileContext.originForUISourceCode(file)));
80
83
  }
81
84
 
82
85
  static getSourceById(
@@ -85,9 +88,6 @@ export class ListSourcesTool implements
85
88
  // eslint-disable-next-line @devtools/no-instance-of-migrated-singletons
86
89
  workspace: Workspace.Workspace.WorkspaceImpl = Workspace.Workspace.WorkspaceImpl.instance(),
87
90
  ): Workspace.UISourceCode.UISourceCode|undefined {
88
- if (establishedOrigin.isOpaque()) {
89
- return undefined;
90
- }
91
91
  return ListSourcesTool.getUISourceCodes(establishedOrigin, workspace)
92
92
  .find(file => ListSourcesTool.uiSourceCodeId.get(file) === id);
93
93
  }
@@ -114,14 +114,12 @@ export class ListSourcesTool implements
114
114
  _params: Record<string, never>,
115
115
  context: BaseToolCapability&OriginLockCapability,
116
116
  ): Promise<DataHandlerResult<{files: SourceSummary[]}>> {
117
- const establishedOrigin = context.getEstablishedOrigin();
118
- if (!establishedOrigin || establishedOrigin.isOpaque()) {
119
- return {
120
- error: 'Opaque origin not allowed',
121
- };
117
+ const originResult = resolveOriginFromLock(context.getOriginLock());
118
+ if ('error' in originResult) {
119
+ return originResult;
122
120
  }
123
121
 
124
- const files = ListSourcesTool.getUISourceCodes(establishedOrigin);
122
+ const files = ListSourcesTool.getUISourceCodes(originResult.origin);
125
123
 
126
124
  return {
127
125
  result: {
@@ -7,7 +7,8 @@ import * as i18n from '../../../core/i18n/i18n.js';
7
7
  import * as Platform from '../../../core/platform/platform.js';
8
8
  import * as SDK from '../../../core/sdk/sdk.js';
9
9
 
10
- import {MAX_TARGET_ORIGINS, resolveDOMStorages} from './DOMStorageUtils.js';
10
+ import {resolveAllowedTargetOrigins} from './CookieUtils.js';
11
+ import {resolveDOMStorages} from './DOMStorageUtils.js';
11
12
  import {
12
13
  type BaseToolCapability,
13
14
  type DataHandlerResult,
@@ -98,31 +99,11 @@ export class ListStorageKeysTool implements DataTool<ListStorageKeysArgs, ListSt
98
99
 
99
100
  // eslint-disable-next-line @devtools/no-instance-of-migrated-singletons
100
101
  const targetManager = SDK.TargetManager.TargetManager.instance();
101
- const primaryPageTarget = targetManager.primaryPageTarget();
102
-
103
- const establishedOrigin = context.getEstablishedOrigin();
104
- if (!establishedOrigin || establishedOrigin.isOpaque()) {
105
- return {error: 'No origin available or not allowed.'};
106
- }
107
-
108
- if (!primaryPageTarget) {
109
- return {error: 'No origin available or not allowed.'};
110
- }
111
-
112
- const pageOrigin = primaryPageTarget.inspectedSecurityOrigin();
113
- if (!pageOrigin.isSameOriginWith(establishedOrigin)) {
114
- return {error: 'No origin available or not allowed.'};
115
- }
116
-
117
- const candidateOrigins: SDK.SecurityOrigin.SecurityOrigin[] = (args.origins && args.origins.length > 0) ?
118
- args.origins.map(origin => SDK.SecurityOrigin.SecurityOrigin.create(origin)) :
119
- [establishedOrigin];
120
- const validOrigins =
121
- candidateOrigins.filter(origin => origin.isSameOriginWith(establishedOrigin)).map(origin => origin.siteId());
122
- const targetOrigins = Array.from(new Set(validOrigins)).slice(0, MAX_TARGET_ORIGINS);
123
- if (targetOrigins.length === 0) {
124
- return {error: 'No valid origins found.'};
102
+ const targetOriginsResult = resolveAllowedTargetOrigins(args.origins, context, targetManager);
103
+ if ('error' in targetOriginsResult) {
104
+ return {error: targetOriginsResult.error};
125
105
  }
106
+ const {targetOrigins, primaryPageTarget} = targetOriginsResult;
126
107
 
127
108
  const storageKey = (targetOrigins.length === 1 && args.storageKey) ? args.storageKey : undefined;
128
109
  const storageKeysByOrigin: ListStorageKeysResult['storageKeysByOrigin'] = {};
@@ -16,10 +16,12 @@ Instead of passing a monolithic "grab-bag" context object to all tool handlers,
16
16
  - `BaseToolCapability`: Base interface providing access to the top-level conversation context step.
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
- - `OriginLockCapability`: Enforces origin boundaries for tools that access inspected page state or resources. Tools verify targets against the established origin before reading data to prevent unauthorized cross-origin access.
20
- - For DOM nodes and source files: wrap the target in a context helper (such as `DOMNodeContext` or `FileContext`) and call `isOriginAllowed(establishedOrigin)`.
19
+ - `OriginLockCapability`: Enforces origin boundaries for tools that access inspected page state or resources via `getOriginLock(): OriginLockState` (`ESTABLISHED_ORIGIN`, `BLOCKED_BY_NAVIGATION`, or `UNINITIALIZED`).
20
+ - Tools check for `BLOCKED_BY_NAVIGATION` (cross-origin navigation during the run) and `UNINITIALIZED` (no lock established yet) states and return appropriate errors.
21
+ - For DOM nodes, source files, and execution contexts: validate target origins using `isOriginAllowedByLock(originLock, targetOrigin)`.
21
22
  - For cookies and DOM storage: validate target origins using `resolveAllowedTargetOrigins()`.
22
- - For network requests: compare `request.initiatorSecurityOrigin()` to the established origin.
23
+ - For network requests: compare request initiators using `isOriginAllowedByLock(originLock, request.initiatorSecurityOrigin())`.
24
+ - Transition from legacy V1: Tools no longer inspect context wrappers (such as `DOMNodeContext.isOriginAllowed()`). Tools validate `SecurityOrigin` instances directly against `OriginLockState`. Legacy V1 agents provide an adapter callback in their tool invocation contexts to satisfy `OriginLockCapability`.
23
25
 
24
26
  ### Unified Context
25
27
 
@@ -108,10 +108,9 @@ export class ResolveDevtoolsNodePathTool implements DataTool<ResolveDevtoolsNode
108
108
  return {error: 'Error: Could not retrieve resolved node.'};
109
109
  }
110
110
 
111
- const establishedOrigin = context.getEstablishedOrigin();
112
111
  // Security check: Ensure the resolved node belongs to the same origin
113
112
  // that this AI assistance session is locked to, preventing cross-origin access.
114
- if (!isOriginAllowedByLock(establishedOrigin, node.securityOrigin())) {
113
+ if (!isOriginAllowedByLock(context.getOriginLock(), node.securityOrigin())) {
115
114
  return {error: 'Error: Node does not belong to the current origin.'};
116
115
  }
117
116
 
@@ -113,43 +113,81 @@ export interface TargetCapability {
113
113
  * so tools can resolve DOM nodes and frame hierarchies across frames.
114
114
  *
115
115
  * Tools that consume this target must independently validate the security origin of
116
- * any resolved entities (e.g. via `node.securityOrigin()`) against `getEstablishedOrigin()`.
116
+ * any resolved entities (e.g. via `node.securityOrigin()`) against `getOriginLock()`.
117
117
  */
118
118
  getTarget(): SDK.Target.Target|null;
119
119
  }
120
120
 
121
+ /**
122
+ * Origin-locking state for AI Assistance:
123
+ * - `ESTABLISHED_ORIGIN`: The conversation is bound to a specific origin. Operations are
124
+ * limited to this origin.
125
+ * - `BLOCKED_BY_NAVIGATION`: An unapproved cross-origin navigation occurred during the
126
+ * active run. Origin-restricted operations are blocked and must return an error.
127
+ * - `UNINITIALIZED`: No origin lock has been established yet. Operations requiring a locked
128
+ * origin must return an error.
129
+ */
130
+ export type OriginLockState = {
131
+ status: 'ESTABLISHED_ORIGIN',
132
+ origin: SDK.SecurityOrigin.SecurityOrigin,
133
+ }|{
134
+ status: 'BLOCKED_BY_NAVIGATION',
135
+ }|{
136
+ status: 'UNINITIALIZED',
137
+ };
138
+
121
139
  /**
122
140
  * Capability for tools that enforce conversation origin boundaries.
123
141
  */
124
142
  export interface OriginLockCapability {
125
143
  /**
126
- * Returns the security origin locked for the current conversation.
127
- *
128
- * TODO: When V1 agents (StylingAgent, AccessibilityAgent) are removed,
129
- * simplify getEstablishedOrigin() to return SDK.SecurityOrigin.SecurityOrigin
130
- * non-optionally.
131
- *
132
- * @returns The established {@link SDK.SecurityOrigin.SecurityOrigin}, or `undefined`
133
- * if the conversation is not yet locked to an origin (e.g. before the first query).
144
+ * Returns the current origin-locking state for the active conversation.
134
145
  */
135
- getEstablishedOrigin(): SDK.SecurityOrigin.SecurityOrigin|undefined;
146
+ getOriginLock(): OriginLockState;
136
147
  }
137
148
 
138
149
  /**
139
150
  * Checks whether a target origin matches the established conversation origin lock.
140
- * Fails closed (returns false) if established origin is missing/opaque or target is cross-origin.
151
+ * Returns `false` if the lock is not established, either origin is opaque, or the
152
+ * target origin does not match the established origin.
141
153
  */
142
154
  export function isOriginAllowedByLock(
143
- establishedOrigin: SDK.SecurityOrigin.SecurityOrigin|undefined,
155
+ originLock: OriginLockState,
144
156
  targetOrigin: SDK.SecurityOrigin.SecurityOrigin|null|undefined,
145
157
  ): boolean {
146
- if (!establishedOrigin || establishedOrigin.isOpaque()) {
158
+ if (originLock.status !== 'ESTABLISHED_ORIGIN') {
159
+ return false;
160
+ }
161
+ if (originLock.origin.isOpaque()) {
147
162
  return false;
148
163
  }
149
164
  if (!targetOrigin || targetOrigin.isOpaque()) {
150
165
  return false;
151
166
  }
152
- return targetOrigin.isSameOriginWith(establishedOrigin);
167
+ return targetOrigin.isSameOriginWith(originLock.origin);
168
+ }
169
+
170
+ /**
171
+ * Resolves the conversation's established origin from the origin lock state.
172
+ * Returns an error object if origin access is blocked by navigation, the lock
173
+ * is uninitialized, or the established origin is opaque.
174
+ */
175
+ export function resolveOriginFromLock(
176
+ originLock: OriginLockState,
177
+ ): {origin: SDK.SecurityOrigin.SecurityOrigin}|{
178
+ error: string,
179
+ }
180
+ {
181
+ if (originLock.status === 'BLOCKED_BY_NAVIGATION') {
182
+ return {error: 'Cross-origin access blocked due to navigation.'};
183
+ }
184
+ if (originLock.status === 'UNINITIALIZED') {
185
+ return {error: 'No origin established for this conversation.'};
186
+ }
187
+ if (originLock.origin.isOpaque()) {
188
+ return {error: 'No origin available or not allowed.'};
189
+ }
190
+ return {origin: originLock.origin};
153
191
  }
154
192
 
155
193
  /**
@@ -220,8 +258,8 @@ export const enum ToolName {
220
258
  GET_TRACE_NETWORK_SUMMARY = 'getTraceNetworkSummary',
221
259
  RUN_LIGHTHOUSE = 'runLighthouse',
222
260
  GET_DETAILED_CALL_TREE = 'getDetailedCallTree',
223
- GET_FUNCTION_CODE = 'getFunctionCode',
224
- GET_RESOURCE_CONTENT = 'getResourceContent',
261
+ GET_TRACE_FUNCTION_CODE = 'getTraceFunctionCode',
262
+ GET_TRACE_RESOURCE_CONTENT = 'getTraceResourceContent',
225
263
  GET_INSIGHT_DETAILS = 'getInsightDetails',
226
264
  GET_STORAGE_BREAKDOWN = 'getStorageBreakdown',
227
265
  }
@@ -6,18 +6,18 @@ import {ExecuteJavaScriptTool} from './ExecuteJavaScript.js';
6
6
  import {GetCookieValuesTool} from './GetCookieValues.js';
7
7
  import {GetDetailedCallTreeTool} from './GetDetailedCallTree.js';
8
8
  import {GetElementAccessibilityDetailsTool} from './GetElementAccessibilityDetails.js';
9
- import {GetFunctionCodeTool} from './GetFunctionCode.js';
10
9
  import {GetInsightDetailsTool} from './GetInsightDetails.js';
11
10
  import {GetLighthouseAuditsTool} from './GetLighthouseAudits.js';
12
11
  import {GetNetworkRequestDetailsTool} from './GetNetworkRequestDetails.js';
13
- import {GetResourceContentTool} from './GetResourceContent.js';
14
12
  import {GetSourceContentTool} from './GetSourceContent.js';
15
13
  import {GetStorageBreakdownTool} from './GetStorageBreakdown.js';
16
14
  import {GetStorageValuesTool} from './GetStorageValues.js';
17
15
  import {GetStylesTool} from './GetStyles.js';
18
16
  import {GetTraceEventByKeyTool} from './GetTraceEventByKey.js';
17
+ import {GetTraceFunctionCodeTool} from './GetTraceFunctionCode.js';
19
18
  import {GetTraceMainThreadSummaryTool} from './GetTraceMainThreadSummary.js';
20
19
  import {GetTraceNetworkSummaryTool} from './GetTraceNetworkSummary.js';
20
+ import {GetTraceResourceContentTool} from './GetTraceResourceContent.js';
21
21
  import {ListCookiesTool} from './ListCookies.js';
22
22
  import {ListNetworkRequestsTool} from './ListNetworkRequests.js';
23
23
  import {ListPageOriginsTool} from './ListPageOrigins.js';
@@ -51,8 +51,8 @@ export interface BaseToolsRegistryMap {
51
51
  [ToolName.GET_TRACE_NETWORK_SUMMARY]: GetTraceNetworkSummaryTool;
52
52
  [ToolName.RUN_LIGHTHOUSE]: RunLighthouseTool;
53
53
  [ToolName.GET_DETAILED_CALL_TREE]: GetDetailedCallTreeTool;
54
- [ToolName.GET_FUNCTION_CODE]: GetFunctionCodeTool;
55
- [ToolName.GET_RESOURCE_CONTENT]: GetResourceContentTool;
54
+ [ToolName.GET_TRACE_FUNCTION_CODE]: GetTraceFunctionCodeTool;
55
+ [ToolName.GET_TRACE_RESOURCE_CONTENT]: GetTraceResourceContentTool;
56
56
  [ToolName.GET_INSIGHT_DETAILS]: GetInsightDetailsTool;
57
57
  [ToolName.GET_STORAGE_BREAKDOWN]: GetStorageBreakdownTool;
58
58
  }
@@ -86,8 +86,8 @@ export const TOOLS: BaseToolsRegistryMap = {
86
86
  [ToolName.GET_TRACE_NETWORK_SUMMARY]: new GetTraceNetworkSummaryTool(),
87
87
  [ToolName.RUN_LIGHTHOUSE]: new RunLighthouseTool(),
88
88
  [ToolName.GET_DETAILED_CALL_TREE]: new GetDetailedCallTreeTool(),
89
- [ToolName.GET_FUNCTION_CODE]: new GetFunctionCodeTool(),
90
- [ToolName.GET_RESOURCE_CONTENT]: new GetResourceContentTool(),
89
+ [ToolName.GET_TRACE_FUNCTION_CODE]: new GetTraceFunctionCodeTool(),
90
+ [ToolName.GET_TRACE_RESOURCE_CONTENT]: new GetTraceResourceContentTool(),
91
91
  [ToolName.GET_INSIGHT_DETAILS]: new GetInsightDetailsTool(),
92
92
  [ToolName.GET_STORAGE_BREAKDOWN]: new GetStorageBreakdownTool(),
93
93
  };
@@ -0,0 +1,89 @@
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 Root from '../../core/root/root.js';
6
+ import type * as CommentManager from '../comment_manager/comment_manager.js';
7
+
8
+ export type ChangeRecord = CommentManager.CommentManager.ChangeRecord;
9
+
10
+ export const MAX_RECORDS = 1000;
11
+
12
+ export class ChangeTracker {
13
+ readonly #records = new Map<string, ChangeRecord>();
14
+ readonly #commentManager: CommentManager.CommentManager.CommentManager;
15
+ readonly #maxRecords: number;
16
+ #lastRecord?: ChangeRecord;
17
+
18
+ constructor(commentManager: CommentManager.CommentManager.CommentManager, maxRecords: number = MAX_RECORDS) {
19
+ this.#commentManager = commentManager;
20
+ this.#maxRecords = Math.max(1, maxRecords);
21
+ }
22
+
23
+ get maxRecords(): number {
24
+ return this.#maxRecords;
25
+ }
26
+
27
+ get isTracking(): boolean {
28
+ return Boolean(Root.Runtime.hostConfig.devToolsComments?.enabled);
29
+ }
30
+
31
+ trackChange(
32
+ description: string,
33
+ anchor: CommentManager.CommentManager.CommentAnchorSignature,
34
+ ): ChangeRecord|null {
35
+ if (!this.isTracking) {
36
+ return null;
37
+ }
38
+ const record: ChangeRecord = {
39
+ id: crypto.randomUUID(),
40
+ description,
41
+ timestamp: Date.now(),
42
+ };
43
+ while (this.#records.size >= this.#maxRecords) {
44
+ this.#evictOldestRecord();
45
+ }
46
+
47
+ const thread = this.#commentManager.createCommentThread(anchor, undefined, undefined, [record]);
48
+ this.#records.set(thread.id, record);
49
+ this.#lastRecord = record;
50
+ return record;
51
+ }
52
+
53
+ #evictOldestRecord(): void {
54
+ // Delete the oldest record for which the comment thread has no user comments.
55
+ // If all comment threads have user comments, delete the oldest record.
56
+ for (const threadId of this.#records.keys()) {
57
+ const thread = this.#commentManager.getCommentThread(threadId);
58
+ if (!thread || thread.comments.length === 0) {
59
+ this.#commentManager.removeCommentThread(threadId);
60
+ this.#records.delete(threadId);
61
+ return;
62
+ }
63
+ }
64
+
65
+ const oldestThreadId = this.#records.keys().next().value;
66
+ if (oldestThreadId) {
67
+ this.#records.delete(oldestThreadId);
68
+ }
69
+ }
70
+
71
+ getChanges(): ChangeRecord[] {
72
+ return Array.from(this.#records.values());
73
+ }
74
+
75
+ getLastChange(): ChangeRecord|undefined {
76
+ return this.#lastRecord;
77
+ }
78
+
79
+ clear(): void {
80
+ for (const threadId of this.#records.keys()) {
81
+ const thread = this.#commentManager.getCommentThread(threadId);
82
+ if (!thread || thread.comments.length === 0) {
83
+ this.#commentManager.removeCommentThread(threadId);
84
+ }
85
+ }
86
+ this.#records.clear();
87
+ this.#lastRecord = undefined;
88
+ }
89
+ }
@@ -0,0 +1,9 @@
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 ChangeTracker from './ChangeTracker.js';
6
+
7
+ export {
8
+ ChangeTracker,
9
+ };