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.
- package/.geminiignore +0 -1
- package/.stylelintignore +2 -0
- package/front_end/core/common/Color.ts +5 -2
- package/front_end/core/common/VersionController.ts +5 -1
- package/front_end/core/host/InspectorFrontendHostAPI.ts +16 -3
- package/front_end/core/host/InspectorFrontendHostStub.ts +3 -2
- package/front_end/core/host/ResourceLoader.ts +1 -2
- package/front_end/core/platform/api/HostRuntime.ts +1 -0
- package/front_end/core/platform/browser/HostRuntime.ts +4 -0
- package/front_end/core/platform/node/HostRuntime.ts +5 -0
- package/front_end/core/root/Runtime.ts +5 -0
- package/front_end/core/sdk/AnimationModel.ts +9 -1
- package/front_end/core/sdk/DOMModel.ts +13 -0
- package/front_end/core/sdk/SourceMapScopeChainEntry.ts +9 -6
- package/front_end/core/sdk/SourceMapScopesInfo.ts +3 -1
- package/front_end/foundation/Universe.ts +8 -0
- package/front_end/generated/InspectorBackendCommands.ts +4 -1
- package/front_end/generated/protocol-mapping.d.ts +19 -0
- package/front_end/generated/protocol-proxy-api.d.ts +15 -0
- package/front_end/generated/protocol.ts +43 -2
- package/front_end/models/ai_assistance/AiAgent2.ts +20 -18
- package/front_end/models/ai_assistance/AiConversation.ts +38 -12
- package/front_end/models/ai_assistance/agents/AccessibilityAgent.ts +9 -2
- package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +26 -0
- package/front_end/models/ai_assistance/agents/StylingAgent.ts +13 -4
- package/front_end/models/ai_assistance/ai_assistance.ts +4 -4
- package/front_end/models/ai_assistance/skills/performance.md +4 -3
- package/front_end/models/ai_assistance/tools/CookieUtils.ts +10 -8
- package/front_end/models/ai_assistance/tools/ExecuteJavaScript.ts +1 -1
- package/front_end/models/ai_assistance/tools/GetElementAccessibilityDetails.ts +1 -3
- package/front_end/models/ai_assistance/tools/GetNetworkRequestDetails.ts +9 -8
- package/front_end/models/ai_assistance/tools/GetSourceContent.ts +5 -6
- package/front_end/models/ai_assistance/tools/GetStorageValues.ts +6 -25
- package/front_end/models/ai_assistance/tools/GetStyles.ts +1 -3
- package/front_end/models/ai_assistance/tools/{GetFunctionCode.ts → GetTraceFunctionCode.ts} +26 -13
- package/front_end/models/ai_assistance/tools/{GetResourceContent.ts → GetTraceResourceContent.ts} +31 -11
- package/front_end/models/ai_assistance/tools/ListNetworkRequests.ts +7 -6
- package/front_end/models/ai_assistance/tools/ListPageOrigins.ts +8 -5
- package/front_end/models/ai_assistance/tools/ListSources.ts +8 -10
- package/front_end/models/ai_assistance/tools/ListStorageKeys.ts +6 -25
- package/front_end/models/ai_assistance/tools/README.md +5 -3
- package/front_end/models/ai_assistance/tools/ResolveDevtoolsNodePath.ts +1 -2
- package/front_end/models/ai_assistance/tools/Tool.ts +54 -16
- package/front_end/models/ai_assistance/tools/ToolRegistry.ts +6 -6
- package/front_end/models/change_tracker/ChangeTracker.ts +89 -0
- package/front_end/models/change_tracker/change_tracker.ts +9 -0
- package/front_end/models/elements/DOMChanges.ts +238 -0
- package/front_end/models/elements/elements.ts +2 -0
- package/front_end/models/issues_manager/MarkdownIssueDescription.ts +2 -2
- package/front_end/models/live-metrics/LiveMetrics.ts +1 -2
- package/front_end/models/trace/ModelImpl.ts +0 -6
- package/front_end/models/trace/Processor.ts +4 -7
- package/front_end/models/trace/helpers/Timing.ts +2 -1
- package/front_end/models/trace/types/Overlays.ts +12 -1
- package/front_end/panels/accessibility/AccessibilityAnnouncementRecordingView.ts +67 -19
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +3 -1
- package/front_end/panels/ai_assistance/components/MarkdownRendererWithCodeBlock.ts +25 -5
- package/front_end/panels/application/IndexedDBViews.ts +1 -1
- package/front_end/panels/browser_debugger/ObjectEventListenersSidebarPane.ts +2 -3
- package/front_end/panels/elements/DOMTreeContextMenu.ts +16 -23
- package/front_end/panels/elements/ElementsTreeElement.ts +384 -79
- package/front_end/panels/elements/ElementsTreeOutline.ts +541 -82
- package/front_end/panels/elements/EventListenersWidget.ts +0 -1
- package/front_end/panels/event_listeners/EventListenersView.ts +241 -297
- package/front_end/panels/linear_memory_inspector/LinearMemoryInspectorController.ts +1 -1
- package/front_end/panels/mobile_throttling/ThrottlingSettingsTab.ts +10 -2
- package/front_end/panels/network/NetworkConfigView.ts +141 -116
- package/front_end/panels/network/NetworkPanel.ts +140 -52
- package/front_end/panels/network/RequestHeadersView.ts +65 -32
- package/front_end/panels/network/components/RequestHeadersView.css +7 -1
- package/front_end/panels/network/forward/BackendLinking.ts +52 -0
- package/front_end/panels/network/forward/forward.ts +2 -0
- package/front_end/panels/settings/BackendLinkingSettingsTab.ts +308 -0
- package/front_end/panels/settings/backendLinkingSettingsTab.css +78 -0
- package/front_end/panels/settings/settings-meta.ts +25 -0
- package/front_end/panels/settings/settings.ts +2 -0
- package/front_end/panels/timeline/README.md +3 -2
- package/front_end/panels/timeline/TimelineFlameChartView.ts +23 -2
- package/front_end/panels/timeline/TimelineTreeView.ts +11 -1
- package/front_end/panels/timeline/overlays/OverlaysImpl.ts +64 -4
- package/front_end/panels/timeline/timelineFlameChartView.css +47 -0
- package/front_end/panels/whats_new/ReleaseNoteText.ts +8 -8
- package/front_end/panels/whats_new/resources/WNDT.md +6 -6
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/third_party/puppeteer/README.chromium +2 -2
- package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.d.ts +6 -0
- package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +5 -8
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Coverage.d.ts +6 -0
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Coverage.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Coverage.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/ScreenRecording.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/ScreenRecording.js +1 -5
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/ScreenRecording.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/revisions.d.ts +3 -3
- package/front_end/third_party/puppeteer/package/lib/puppeteer/revisions.js +3 -3
- package/front_end/third_party/puppeteer/package/lib/puppeteer/revisions.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/util/version.d.ts +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/util/version.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/types.d.ts +6 -0
- package/front_end/third_party/puppeteer/package/package.json +2 -2
- package/front_end/third_party/puppeteer/package/src/cdp/Coverage.ts +6 -0
- package/front_end/third_party/puppeteer/package/src/cdp/ScreenRecording.ts +3 -7
- package/front_end/third_party/puppeteer/package/src/revisions.ts +3 -3
- package/front_end/third_party/puppeteer/package/src/util/version.ts +1 -1
- package/front_end/third_party/web-vitals/README.chromium +2 -2
- package/front_end/third_party/web-vitals/package/dist/modules/attribution/onINP.js +8 -5
- package/front_end/third_party/web-vitals/package/package.json +1 -1
- package/front_end/third_party/web-vitals/package/src/attribution/onINP.ts +8 -4
- package/front_end/third_party/web-vitals/rebuild.sh +2 -2
- package/front_end/ui/comments/CommentOverlayManager.ts +169 -40
- package/front_end/ui/comments/CommentThreadWidget.ts +221 -0
- package/front_end/ui/comments/CommentsOverlayWidget.ts +124 -12
- package/front_end/ui/comments/commentThreadWidget.css +173 -0
- package/front_end/ui/comments/comments.ts +2 -0
- package/front_end/ui/comments/commentsOverlay.css +6 -0
- package/front_end/ui/legacy/ContextMenu.ts +19 -0
- package/front_end/ui/legacy/SoftContextMenu.ts +6 -0
- package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +1 -1
- package/front_end/ui/legacy/components/perf_ui/FlameChart.ts +66 -22
- package/front_end/ui/visual_logging/KnownContextValues.ts +4 -0
- 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
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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(
|
|
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
|
|
61
|
-
|
|
62
|
-
|
|
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
|
|
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
|
|
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(
|
|
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
|
|
118
|
-
if (
|
|
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(
|
|
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 {
|
|
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
|
|
102
|
-
|
|
103
|
-
|
|
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
|
|
20
|
-
-
|
|
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()
|
|
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(
|
|
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 `
|
|
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
|
|
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
|
-
|
|
146
|
+
getOriginLock(): OriginLockState;
|
|
136
147
|
}
|
|
137
148
|
|
|
138
149
|
/**
|
|
139
150
|
* Checks whether a target origin matches the established conversation origin lock.
|
|
140
|
-
*
|
|
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
|
-
|
|
155
|
+
originLock: OriginLockState,
|
|
144
156
|
targetOrigin: SDK.SecurityOrigin.SecurityOrigin|null|undefined,
|
|
145
157
|
): boolean {
|
|
146
|
-
if (
|
|
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(
|
|
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
|
-
|
|
224
|
-
|
|
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.
|
|
55
|
-
[ToolName.
|
|
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.
|
|
90
|
-
[ToolName.
|
|
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
|
+
}
|