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
|
@@ -0,0 +1,238 @@
|
|
|
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 Platform from '../../core/platform/platform.js';
|
|
6
|
+
import type * as SDK from '../../core/sdk/sdk.js';
|
|
7
|
+
import type * as ChangeTracker from '../change_tracker/change_tracker.js';
|
|
8
|
+
import type * as CommentManager from '../comment_manager/comment_manager.js';
|
|
9
|
+
|
|
10
|
+
type Tracker = ChangeTracker.ChangeTracker.ChangeTracker|undefined;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Maximum number of characters of page controlled data (tag names, attribute names and values,
|
|
14
|
+
* text node values and HTML) that a change description embeds. The inspected page fully controls
|
|
15
|
+
* these strings and `Changed HTML from ... to ...` in particular can otherwise hold on to the
|
|
16
|
+
* entire subtree of an element.
|
|
17
|
+
*/
|
|
18
|
+
export const MAX_VALUE_LENGTH = 100;
|
|
19
|
+
|
|
20
|
+
/** Shortens page controlled data so that both its start and its end stay recognizable. */
|
|
21
|
+
function trimValue(value: string): string {
|
|
22
|
+
return Platform.StringUtilities.trimMiddle(value, MAX_VALUE_LENGTH);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function buildAnchor(node: SDK.DOMModel.DOMNode,
|
|
26
|
+
selector = 'element'): CommentManager.CommentManager.CommentAnchorSignature {
|
|
27
|
+
return {
|
|
28
|
+
vePath: 'Panel: elements > Tree: elements > TreeItem',
|
|
29
|
+
textSignature: selector,
|
|
30
|
+
node: {
|
|
31
|
+
backendNodeId: node.backendNodeId(),
|
|
32
|
+
targetId: node.domModel().target().id(),
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Parses a raw attribute string (e.g. 'class="btn"', 'data-id=123', or 'disabled')
|
|
39
|
+
* into its attribute name and unquoted value for single-attribute inline edits.
|
|
40
|
+
*/
|
|
41
|
+
function parseAttributeText(text: string): {name: string, value: string} {
|
|
42
|
+
const trimmed = text.trim();
|
|
43
|
+
const eqIdx = trimmed.indexOf('=');
|
|
44
|
+
if (eqIdx === -1) {
|
|
45
|
+
return {name: trimmed, value: ''};
|
|
46
|
+
}
|
|
47
|
+
const name = trimmed.slice(0, eqIdx).trim();
|
|
48
|
+
let value = trimmed.slice(eqIdx + 1).trim();
|
|
49
|
+
if (value.length >= 2 &&
|
|
50
|
+
((value.startsWith('"') && value.endsWith('"')) || (value.startsWith('\'') && value.endsWith('\'')))) {
|
|
51
|
+
value = value.slice(1, -1);
|
|
52
|
+
}
|
|
53
|
+
return {name, value};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function describeAddAttribute(attributeName: string, value?: string): string {
|
|
57
|
+
if (value !== undefined && value !== '') {
|
|
58
|
+
return `Added attribute ${trimValue(attributeName)}="${trimValue(value)}"`;
|
|
59
|
+
}
|
|
60
|
+
return `Added attribute "${trimValue(attributeName)}"`;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function describeRemoveAttribute(attributeName: string): string {
|
|
64
|
+
return `Removed attribute "${trimValue(attributeName)}"`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Describes a modification of an existing attribute, which covers renaming the attribute, changing
|
|
69
|
+
* its value, or both at once. `oldValue` is `undefined` if the previous value is unknown.
|
|
70
|
+
*/
|
|
71
|
+
function describeAttributeChange(
|
|
72
|
+
oldAttributeName: string,
|
|
73
|
+
oldValue: string|undefined,
|
|
74
|
+
newAttributeName: string,
|
|
75
|
+
newValue: string,
|
|
76
|
+
): string {
|
|
77
|
+
if (oldAttributeName !== newAttributeName) {
|
|
78
|
+
if (oldValue !== undefined && oldValue !== newValue) {
|
|
79
|
+
return `Renamed attribute "${trimValue(oldAttributeName)}"="${trimValue(oldValue)}" to "${
|
|
80
|
+
trimValue(newAttributeName)}"="${trimValue(newValue)}"`;
|
|
81
|
+
}
|
|
82
|
+
return `Renamed attribute "${trimValue(oldAttributeName)}" to "${trimValue(newAttributeName)}"`;
|
|
83
|
+
}
|
|
84
|
+
if (oldValue !== undefined) {
|
|
85
|
+
return `Changed attribute "${trimValue(newAttributeName)}" from "${trimValue(oldValue)}" to "${
|
|
86
|
+
trimValue(newValue)}"`;
|
|
87
|
+
}
|
|
88
|
+
return `Changed attribute "${trimValue(newAttributeName)}" to "${trimValue(newValue)}"`;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface AttributeEdit {
|
|
92
|
+
/** Name of the edited attribute, or an empty string when a new attribute is added. */
|
|
93
|
+
attributeName: string;
|
|
94
|
+
/** Raw attribute text before the edit, if there was any. */
|
|
95
|
+
oldText: string|null;
|
|
96
|
+
/** Raw attribute text the user committed. Empty when the attribute is removed. */
|
|
97
|
+
newText: string;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function describeAttributeEdit({attributeName, oldText, newText}: AttributeEdit): string {
|
|
101
|
+
const parsedNew = parseAttributeText(newText);
|
|
102
|
+
const parsedOld = oldText !== null ? parseAttributeText(oldText) : undefined;
|
|
103
|
+
|
|
104
|
+
if (!newText.trim()) {
|
|
105
|
+
return describeRemoveAttribute(attributeName.trim());
|
|
106
|
+
}
|
|
107
|
+
if (!attributeName.trim()) {
|
|
108
|
+
return describeAddAttribute(parsedNew.name, parsedNew.value);
|
|
109
|
+
}
|
|
110
|
+
return describeAttributeChange(
|
|
111
|
+
attributeName.trim(),
|
|
112
|
+
parsedOld?.value,
|
|
113
|
+
parsedNew.name || attributeName.trim(),
|
|
114
|
+
parsedNew.value,
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* These strings are for the consumption by AI agents, they don't need to be translated.
|
|
120
|
+
*/
|
|
121
|
+
function describeTagNameEdit(oldTagName: string, newTagName: string): string {
|
|
122
|
+
return `Renamed tag from <${trimValue(oldTagName)}> to <${trimValue(newTagName)}>`;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function describeTextNodeEdit(oldText: string, newText: string): string {
|
|
126
|
+
return `Changed text from "${trimValue(oldText)}" to "${trimValue(newText)}"`;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function describeNodeRemoval(tagName: string): string {
|
|
130
|
+
return `Removed node <${trimValue(tagName)}>`;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function describeHTMLEdit(oldValue?: string, newValue?: string): string {
|
|
134
|
+
if (oldValue !== undefined && newValue !== undefined) {
|
|
135
|
+
return `Changed HTML from "${trimValue(oldValue)}" to "${trimValue(newValue)}"`;
|
|
136
|
+
}
|
|
137
|
+
if (newValue !== undefined) {
|
|
138
|
+
return `Changed HTML to "${trimValue(newValue)}"`;
|
|
139
|
+
}
|
|
140
|
+
return 'Edited HTML';
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function describeVisibilityToggle(tagName: string, hidden: boolean): string {
|
|
144
|
+
return hidden ? `Hid element <${trimValue(tagName)}>` : `Unhid element <${trimValue(tagName)}>`;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function describeNodeDuplication(tagName: string): string {
|
|
148
|
+
return `Duplicated node <${trimValue(tagName)}>`;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function describeNodeMove(tagName: string, directionUp: boolean): string {
|
|
152
|
+
return `Moved node <${trimValue(tagName)}> ${directionUp ? 'up' : 'down'}`;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function describeNodeDrop(tagName: string): string {
|
|
156
|
+
return `Moved node <${trimValue(tagName)}> via drag and drop`;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function describeNodePaste(tagName: string, isCut: boolean): string {
|
|
160
|
+
return isCut ? `Pasted (moved) node <${trimValue(tagName)}>` : `Pasted node <${trimValue(tagName)}>`;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function trackAttributeEdit(tracker: Tracker, node: SDK.DOMModel.DOMNode, selector: string|undefined,
|
|
164
|
+
edit: AttributeEdit): void {
|
|
165
|
+
if (!tracker?.isTracking) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
tracker.trackChange(describeAttributeEdit(edit), buildAnchor(node, selector));
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export function trackTagNameEdit(tracker: Tracker, node: SDK.DOMModel.DOMNode, selector: string|undefined,
|
|
172
|
+
oldTagName: string, newTagName: string): void {
|
|
173
|
+
if (!tracker?.isTracking) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
tracker.trackChange(describeTagNameEdit(oldTagName, newTagName), buildAnchor(node, selector));
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function trackTextNodeEdit(tracker: Tracker, node: SDK.DOMModel.DOMNode, selector: string|undefined,
|
|
180
|
+
oldText: string, newText: string): void {
|
|
181
|
+
if (!tracker?.isTracking) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
tracker.trackChange(describeTextNodeEdit(oldText, newText), buildAnchor(node, selector));
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export function trackNodeRemoval(tracker: Tracker, node: SDK.DOMModel.DOMNode, selector: string|undefined): void {
|
|
188
|
+
if (!tracker?.isTracking) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
tracker.trackChange(describeNodeRemoval(node.nodeName().toLowerCase()), buildAnchor(node, selector));
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export function trackHTMLEdit(tracker: Tracker, node: SDK.DOMModel.DOMNode, selector: string|undefined,
|
|
195
|
+
oldValue: string, newValue: string): void {
|
|
196
|
+
if (!tracker?.isTracking) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
tracker.trackChange(describeHTMLEdit(oldValue, newValue), buildAnchor(node, selector));
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export function trackVisibilityToggle(tracker: Tracker, node: SDK.DOMModel.DOMNode, selector: string|undefined,
|
|
203
|
+
hidden: boolean): void {
|
|
204
|
+
if (!tracker?.isTracking) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
tracker.trackChange(describeVisibilityToggle(node.nodeName().toLowerCase(), hidden), buildAnchor(node, selector));
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export function trackNodeDuplication(tracker: Tracker, node: SDK.DOMModel.DOMNode, selector: string|undefined): void {
|
|
211
|
+
if (!tracker?.isTracking) {
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
tracker.trackChange(describeNodeDuplication(node.nodeName().toLowerCase()), buildAnchor(node, selector));
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export function trackNodeMove(tracker: Tracker, node: SDK.DOMModel.DOMNode, selector: string|undefined,
|
|
218
|
+
directionUp: boolean): void {
|
|
219
|
+
if (!tracker?.isTracking) {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
tracker.trackChange(describeNodeMove(node.nodeName().toLowerCase(), directionUp), buildAnchor(node, selector));
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export function trackNodeDrop(tracker: Tracker, node: SDK.DOMModel.DOMNode, selector: string|undefined): void {
|
|
226
|
+
if (!tracker?.isTracking) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
tracker.trackChange(describeNodeDrop(node.nodeName().toLowerCase()), buildAnchor(node, selector));
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export function trackNodePaste(tracker: Tracker, node: SDK.DOMModel.DOMNode, selector: string|undefined,
|
|
233
|
+
isCut: boolean): void {
|
|
234
|
+
if (!tracker?.isTracking) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
tracker.trackChange(describeNodePaste(node.nodeName().toLowerCase(), isCut), buildAnchor(node, selector));
|
|
238
|
+
}
|
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
// Use of this source code is governed by a BSD-style license that can be
|
|
3
3
|
// found in the LICENSE file.
|
|
4
4
|
|
|
5
|
+
import * as DOMChanges from './DOMChanges.js';
|
|
5
6
|
import * as ElementUpdateRecord from './ElementUpdateRecord.js';
|
|
6
7
|
|
|
7
8
|
export {
|
|
9
|
+
DOMChanges,
|
|
8
10
|
ElementUpdateRecord,
|
|
9
11
|
};
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// Use of this source code is governed by a BSD-style license that can be
|
|
3
3
|
// found in the LICENSE file.
|
|
4
4
|
|
|
5
|
+
import * as Platform from '../../core/platform/platform.js';
|
|
5
6
|
import * as Marked from '../../third_party/marked/marked.js';
|
|
6
7
|
|
|
7
8
|
/**
|
|
@@ -57,8 +58,7 @@ export interface IssueDescription {
|
|
|
57
58
|
|
|
58
59
|
export async function getFileContent(url: URL): Promise<string> {
|
|
59
60
|
try {
|
|
60
|
-
|
|
61
|
-
return await response.text();
|
|
61
|
+
return await Platform.HostRuntime.HOST_RUNTIME.loadTextFile(url);
|
|
62
62
|
} catch {
|
|
63
63
|
throw new Error(
|
|
64
64
|
`Markdown file ${url.toString()} not found. Make sure it is correctly listed in the relevant BUILD.gn files.`);
|
|
@@ -42,8 +42,7 @@ class InjectedScript {
|
|
|
42
42
|
static async get(): Promise<string> {
|
|
43
43
|
if (!this.#injectedScript) {
|
|
44
44
|
const url = new URL('./web-vitals-injected/web-vitals-injected.generated.js', import.meta.url);
|
|
45
|
-
|
|
46
|
-
this.#injectedScript = await result.text();
|
|
45
|
+
this.#injectedScript = await Platform.HostRuntime.HOST_RUNTIME.loadTextFile(url);
|
|
47
46
|
}
|
|
48
47
|
return this.#injectedScript;
|
|
49
48
|
}
|
|
@@ -232,12 +232,6 @@ export class ModelUpdateEvent extends Event {
|
|
|
232
232
|
}
|
|
233
233
|
}
|
|
234
234
|
|
|
235
|
-
declare global {
|
|
236
|
-
interface HTMLElementEventMap {
|
|
237
|
-
[ModelUpdateEvent.eventName]: ModelUpdateEvent;
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
235
|
export function isModelUpdateDataComplete(eventData: ModelUpdateEventData): eventData is ModelUpdateEventComplete {
|
|
242
236
|
return eventData.type === ModelUpdateType.COMPLETE;
|
|
243
237
|
}
|
|
@@ -19,7 +19,10 @@ const enum Status {
|
|
|
19
19
|
|
|
20
20
|
export class TraceParseProgressEvent extends Event {
|
|
21
21
|
static readonly eventName = 'traceparseprogress';
|
|
22
|
-
constructor(
|
|
22
|
+
constructor(
|
|
23
|
+
public data: Model.TraceParseEventProgressData,
|
|
24
|
+
init: ConstructorParameters<typeof Event>[1] = {bubbles: true},
|
|
25
|
+
) {
|
|
23
26
|
super(TraceParseProgressEvent.eventName, init);
|
|
24
27
|
}
|
|
25
28
|
}
|
|
@@ -44,12 +47,6 @@ function calculateProgress(value: number, phase: ProgressPhase): number {
|
|
|
44
47
|
return value * phase;
|
|
45
48
|
}
|
|
46
49
|
|
|
47
|
-
declare global {
|
|
48
|
-
interface HTMLElementEventMap {
|
|
49
|
-
[TraceParseProgressEvent.eventName]: TraceParseProgressEvent;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
50
|
export class TraceProcessor extends EventTarget {
|
|
54
51
|
// We force the Meta handler to be enabled, so the TraceHandlers type here is
|
|
55
52
|
// the model handlers the user passes in and the Meta handler.
|
|
@@ -155,7 +155,8 @@ export function traceWindowFromOverlay(overlay: Types.Overlays.Overlay): Types.T
|
|
|
155
155
|
switch (overlay.type) {
|
|
156
156
|
case 'ENTRY_LABEL':
|
|
157
157
|
case 'ENTRY_OUTLINE':
|
|
158
|
-
case 'ENTRY_SELECTED':
|
|
158
|
+
case 'ENTRY_SELECTED':
|
|
159
|
+
case 'COMMENT_PIN': {
|
|
159
160
|
return traceWindowFromEvent(overlay.entry);
|
|
160
161
|
}
|
|
161
162
|
|
|
@@ -131,8 +131,19 @@ export interface BottomInfoBar {
|
|
|
131
131
|
};
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
+
/**
|
|
135
|
+
* Represents an active comment pin anchored to a flame chart entry.
|
|
136
|
+
* Links to a CommentThread by ID in CommentManager, rendering a pin icon and
|
|
137
|
+
* outline highlight over the trace event.
|
|
138
|
+
*/
|
|
139
|
+
export interface CommentPin {
|
|
140
|
+
type: 'COMMENT_PIN';
|
|
141
|
+
entry: OverlayEntry;
|
|
142
|
+
commentThreadId: string;
|
|
143
|
+
}
|
|
144
|
+
|
|
134
145
|
/**
|
|
135
146
|
* All supported overlay types.
|
|
136
147
|
*/
|
|
137
148
|
export type Overlay = EntrySelected|EntryOutline|TimeRangeLabel|EntryLabel|EntriesLink|TimespanBreakdown|
|
|
138
|
-
TimestampMarker|CandyStripedTimeRange|TimingsMarker|BottomInfoBar;
|
|
149
|
+
TimestampMarker|CandyStripedTimeRange|TimingsMarker|BottomInfoBar|CommentPin;
|
|
@@ -205,7 +205,28 @@ export function injectedScript(ariaLiveApi: string, jsTriggeredApi: string): voi
|
|
|
205
205
|
let lastRecordedPoliteness: string|null = null;
|
|
206
206
|
let lastRecordedTime = 0;
|
|
207
207
|
|
|
208
|
-
|
|
208
|
+
const pendingLiveNodes = new Set<Element>();
|
|
209
|
+
let scheduledFlushId: number|null = null;
|
|
210
|
+
|
|
211
|
+
function scheduleFlush(): void {
|
|
212
|
+
if (scheduledFlushId !== null) {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
scheduledFlushId = window.requestAnimationFrame(() => {
|
|
216
|
+
scheduledFlushId = null;
|
|
217
|
+
flushPendingNodes();
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function flushPendingNodes(): void {
|
|
222
|
+
const nodes = Array.from(pendingLiveNodes);
|
|
223
|
+
pendingLiveNodes.clear();
|
|
224
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
225
|
+
processLiveNode(nodes[i]);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Queues an active live region node for deferred visibility checking and emission.
|
|
209
230
|
function recordLiveNode(node: Node|null|undefined): void {
|
|
210
231
|
if (!node || node.nodeType !== Node.ELEMENT_NODE) {
|
|
211
232
|
return;
|
|
@@ -218,6 +239,22 @@ export function injectedScript(ariaLiveApi: string, jsTriggeredApi: string): voi
|
|
|
218
239
|
if (element.getAttribute('aria-hidden') === 'true') {
|
|
219
240
|
return;
|
|
220
241
|
}
|
|
242
|
+
pendingLiveNodes.add(element);
|
|
243
|
+
scheduleFlush();
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Emits an announcement payload for an active live region node via the CDP binding after layout.
|
|
247
|
+
function processLiveNode(element: Element): void {
|
|
248
|
+
if ('isConnected' in element && !element.isConnected) {
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
const politeness = derivePoliteness(element);
|
|
252
|
+
if (politeness === 'off') {
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
if (element.getAttribute('aria-hidden') === 'true') {
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
221
258
|
if (!checkVisibility(element)) {
|
|
222
259
|
return;
|
|
223
260
|
}
|
|
@@ -293,18 +330,14 @@ export function injectedScript(ariaLiveApi: string, jsTriggeredApi: string): voi
|
|
|
293
330
|
for (const node of mutation.addedNodes) {
|
|
294
331
|
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
295
332
|
const el = node as Element;
|
|
296
|
-
if (el.shadowRoot) {
|
|
297
|
-
observeSubtree(el.shadowRoot);
|
|
298
|
-
scanAndObserveShadowRoots(el.shadowRoot);
|
|
299
|
-
}
|
|
300
333
|
scanAndObserveShadowRoots(el);
|
|
301
334
|
|
|
302
|
-
if (el.matches
|
|
335
|
+
if (el.matches(selector)) {
|
|
303
336
|
recordLiveNode(el);
|
|
304
337
|
}
|
|
305
|
-
const children = el.querySelectorAll
|
|
306
|
-
for (
|
|
307
|
-
recordLiveNode(
|
|
338
|
+
const children = el.querySelectorAll(selector);
|
|
339
|
+
for (let i = 0; i < children.length; i++) {
|
|
340
|
+
recordLiveNode(children[i]);
|
|
308
341
|
}
|
|
309
342
|
}
|
|
310
343
|
}
|
|
@@ -348,20 +381,29 @@ export function injectedScript(ariaLiveApi: string, jsTriggeredApi: string): voi
|
|
|
348
381
|
}
|
|
349
382
|
}
|
|
350
383
|
|
|
351
|
-
function scanAndObserveShadowRoots(
|
|
352
|
-
if (!
|
|
384
|
+
function scanAndObserveShadowRoots(root: Element|ShadowRoot|null|undefined): void {
|
|
385
|
+
if (!root) {
|
|
353
386
|
return;
|
|
354
387
|
}
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
388
|
+
const queue: Array<Element|ShadowRoot> = [root];
|
|
389
|
+
while (queue.length > 0) {
|
|
390
|
+
const current = queue.pop();
|
|
391
|
+
if (!current) {
|
|
392
|
+
continue;
|
|
393
|
+
}
|
|
394
|
+
const el = current as Element;
|
|
395
|
+
if (el.shadowRoot && !observedRoots.has(el.shadowRoot)) {
|
|
358
396
|
observeSubtree(el.shadowRoot);
|
|
359
|
-
|
|
397
|
+
queue.push(el.shadowRoot);
|
|
398
|
+
}
|
|
399
|
+
const descendants = current.querySelectorAll('*');
|
|
400
|
+
for (let i = 0; i < descendants.length; i++) {
|
|
401
|
+
const descendant = descendants[i];
|
|
402
|
+
if (descendant.shadowRoot && !observedRoots.has(descendant.shadowRoot)) {
|
|
403
|
+
observeSubtree(descendant.shadowRoot);
|
|
404
|
+
queue.push(descendant.shadowRoot);
|
|
405
|
+
}
|
|
360
406
|
}
|
|
361
|
-
}
|
|
362
|
-
const children = (node as Element).children || [];
|
|
363
|
-
for (let i = 0; i < children.length; i++) {
|
|
364
|
-
scanAndObserveShadowRoots(children[i]);
|
|
365
407
|
}
|
|
366
408
|
}
|
|
367
409
|
|
|
@@ -380,6 +422,7 @@ export function injectedScript(ariaLiveApi: string, jsTriggeredApi: string): voi
|
|
|
380
422
|
const shadow = origAttachShadow.apply(this, [init]);
|
|
381
423
|
if (init && init.mode === 'open') {
|
|
382
424
|
observeSubtree(shadow);
|
|
425
|
+
scanAndObserveShadowRoots(shadow);
|
|
383
426
|
}
|
|
384
427
|
return shadow;
|
|
385
428
|
};
|
|
@@ -392,6 +435,11 @@ export function injectedScript(ariaLiveApi: string, jsTriggeredApi: string): voi
|
|
|
392
435
|
// Registers cleanup function invoked during recording teardown.
|
|
393
436
|
window.__announcementsRecorderBinding_cleanup = function(): void {
|
|
394
437
|
observer.disconnect();
|
|
438
|
+
if (scheduledFlushId !== null) {
|
|
439
|
+
window.cancelAnimationFrame(scheduledFlushId);
|
|
440
|
+
scheduledFlushId = null;
|
|
441
|
+
}
|
|
442
|
+
pendingLiveNodes.clear();
|
|
395
443
|
if (originalElementAriaNotify) {
|
|
396
444
|
try {
|
|
397
445
|
(Element.prototype as unknown as Record<string, unknown>)['ariaNotify'] = originalElementAriaNotify;
|
|
@@ -337,7 +337,9 @@ function getMarkdownRenderer(conversation?: AiAssistanceModel.AiConversation.AiC
|
|
|
337
337
|
return new AccessibilityAgentMarkdownRenderer(mainDocumentURL);
|
|
338
338
|
}
|
|
339
339
|
|
|
340
|
-
return new MarkdownRendererWithCodeBlock(
|
|
340
|
+
return new MarkdownRendererWithCodeBlock({
|
|
341
|
+
getEstablishedOrigin: () => conversation?.origin,
|
|
342
|
+
});
|
|
341
343
|
}
|
|
342
344
|
|
|
343
345
|
interface ToolbarViewInput {
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import * as Common from '../../../core/common/common.js';
|
|
6
6
|
import * as Platform from '../../../core/platform/platform.js';
|
|
7
|
+
import type * as SDK from '../../../core/sdk/sdk.js';
|
|
7
8
|
import * as AiAssistanceModel from '../../../models/ai_assistance/ai_assistance.js';
|
|
8
9
|
import * as Logs from '../../../models/logs/logs.js';
|
|
9
10
|
import type * as Marked from '../../../third_party/marked/marked.js';
|
|
@@ -12,7 +13,18 @@ import * as Lit from '../../../ui/lit/lit.js';
|
|
|
12
13
|
|
|
13
14
|
const {html} = Lit;
|
|
14
15
|
|
|
16
|
+
export interface MarkdownRendererWithCodeBlockOptions {
|
|
17
|
+
/**
|
|
18
|
+
* Retrieves the established origin locked for the active conversation.
|
|
19
|
+
* Required to authorize #file-<id> links and prevent cross-origin file leakage.
|
|
20
|
+
* When omitted or unauthorized, file links safely render as plain inert text.
|
|
21
|
+
*/
|
|
22
|
+
getEstablishedOrigin?: () => SDK.SecurityOrigin.SecurityOrigin | undefined;
|
|
23
|
+
}
|
|
24
|
+
|
|
15
25
|
/**
|
|
26
|
+
* Markdown renderer for AI assistance conversations.
|
|
27
|
+
*
|
|
16
28
|
* The model returns multiline code blocks in an erroneous way with the language being in new line.
|
|
17
29
|
* This renderer takes that into account and correctly updates the parsed multiline token with the language
|
|
18
30
|
* correctly identified and stripped from the content.
|
|
@@ -23,8 +35,13 @@ const {html} = Lit;
|
|
|
23
35
|
* color: red;
|
|
24
36
|
* }
|
|
25
37
|
* ```
|
|
26
|
-
|
|
38
|
+
*
|
|
39
|
+
* Also handles linkifying DevTools internal resource links (#req- and origin-locked #file-).
|
|
40
|
+
*/
|
|
27
41
|
export class MarkdownRendererWithCodeBlock extends MarkdownView.MarkdownView.MarkdownInsightRenderer {
|
|
42
|
+
constructor(private readonly options: MarkdownRendererWithCodeBlockOptions = {}) {
|
|
43
|
+
super();
|
|
44
|
+
}
|
|
28
45
|
#revealableLink(revealable: unknown, label: string): Lit.LitTemplate {
|
|
29
46
|
return html`<devtools-link @click=${(e: Event) => {
|
|
30
47
|
e.preventDefault();
|
|
@@ -48,13 +65,16 @@ export class MarkdownRendererWithCodeBlock extends MarkdownView.MarkdownView.Mar
|
|
|
48
65
|
return html`${fallbackText}`;
|
|
49
66
|
}
|
|
50
67
|
if (href.startsWith('#file-')) {
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
68
|
+
const fileId = Number(href.substring(6));
|
|
69
|
+
const file = AiAssistanceModel.ContextSelectionAgent.ContextSelectionAgent.getSourceById(
|
|
70
|
+
fileId,
|
|
71
|
+
this.options.getEstablishedOrigin?.(),
|
|
72
|
+
);
|
|
54
73
|
|
|
55
74
|
if (file) {
|
|
56
75
|
return this.#revealableLink(file, file.name());
|
|
57
76
|
}
|
|
77
|
+
// Fall back to plain text to prevent rendering unauthorized or unverified links.
|
|
58
78
|
return html`${fallbackText}`;
|
|
59
79
|
}
|
|
60
80
|
return null;
|
|
@@ -78,7 +98,7 @@ export class MarkdownRendererWithCodeBlock extends MarkdownView.MarkdownView.Mar
|
|
|
78
98
|
|
|
79
99
|
if (token.type === 'codespan') {
|
|
80
100
|
// LLM likes outputting the link inside a codespan block.
|
|
81
|
-
// Remove the codespan and render the link directly
|
|
101
|
+
// Remove the codespan and render the link directly.
|
|
82
102
|
const matches = token.text.match(/^\[(.*)\]\((.+)\)$/);
|
|
83
103
|
if (matches?.[2]) {
|
|
84
104
|
const link = this.#renderLink(
|
|
@@ -298,7 +298,7 @@ const renderDataGrid = (input: IndexedDBDataViewInput): LitTemplate => {
|
|
|
298
298
|
${input.isIndex ? html`<th id="primary-key" weight="2">${renderKeyColumnHeader(i18nString(UIStrings.primaryKey), input.objectStore.keyPath)}</th>` : nothing}
|
|
299
299
|
<th id="value" weight="8">${i18nString(UIStrings.valueString)}</th>
|
|
300
300
|
</tr>
|
|
301
|
-
${repeat(input.entries,
|
|
301
|
+
${repeat(input.entries, entry => entry, (entry, index) => {
|
|
302
302
|
return html`
|
|
303
303
|
<tr ?selected=${index + input.skipCount === input.selectedRowNumber}
|
|
304
304
|
class="data-grid-data-row"
|
|
@@ -20,8 +20,6 @@ export class ObjectEventListenersSidebarPane extends UI.Widget.VBox implements U
|
|
|
20
20
|
this.contentElement.setAttribute('jslog', `${VisualLogging.section('sources.global-listeners')}`);
|
|
21
21
|
|
|
22
22
|
this.eventListenersView = new EventListeners.EventListenersView.EventListenersView();
|
|
23
|
-
this.eventListenersView.changeCallback = this.requestUpdate.bind(this);
|
|
24
|
-
this.eventListenersView.enableDefaultTreeFocus = true;
|
|
25
23
|
this.eventListenersView.show(this.element);
|
|
26
24
|
this.setDefaultFocusedChild(this.eventListenersView);
|
|
27
25
|
this.requestUpdate();
|
|
@@ -57,7 +55,8 @@ export class ObjectEventListenersSidebarPane extends UI.Widget.VBox implements U
|
|
|
57
55
|
windowObjects.push(result.object);
|
|
58
56
|
}
|
|
59
57
|
}
|
|
60
|
-
|
|
58
|
+
this.eventListenersView.objects = windowObjects;
|
|
59
|
+
await this.eventListenersView.updateComplete;
|
|
61
60
|
}
|
|
62
61
|
|
|
63
62
|
override wasShown(): void {
|