chrome-devtools-frontend 1.0.1697595 → 1.0.1698733
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/.agents/skills/version-control/SKILL.md +21 -9
- package/.stylelintignore +2 -0
- package/front_end/core/common/VersionController.ts +5 -1
- package/front_end/core/platform/HostRuntime.ts +12 -0
- package/front_end/core/platform/api/HostRuntime.ts +13 -0
- package/front_end/core/platform/browser/HostRuntime.ts +34 -0
- package/front_end/core/platform/node/HostRuntime.ts +40 -0
- package/front_end/core/sdk/AnimationModel.ts +21 -99
- package/front_end/core/sdk/CSSModel.ts +4 -2
- package/front_end/core/sdk/CSSPropertyParser.ts +4 -4
- package/front_end/core/sdk/CSSPropertyParserMatchers.ts +4 -44
- package/front_end/core/sdk/Connections.ts +6 -1
- package/front_end/core/sdk/ConsoleModel.ts +1 -12
- package/front_end/core/sdk/DOMModel.ts +3 -51
- package/front_end/core/sdk/DebuggerModel.ts +9 -8
- package/front_end/core/sdk/NetworkRequest.ts +1 -1
- package/front_end/core/sdk/PageFunctions.ts +187 -0
- package/front_end/core/sdk/Resource.ts +1 -1
- package/front_end/core/sdk/RuntimeModel.ts +1 -20
- package/front_end/core/sdk/Script.ts +21 -0
- package/front_end/core/sdk/SourceMap.ts +15 -2
- package/front_end/core/sdk/SourceMapCache.ts +13 -10
- package/front_end/core/sdk/SourceMapManager.ts +11 -9
- package/front_end/core/sdk/SourceMapScopeChainEntry.ts +59 -17
- package/front_end/core/sdk/SourceMapScopesInfo.ts +8 -2
- package/front_end/entrypoints/heap_snapshot_worker/ContextAnalyzer.ts +635 -0
- package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshot.ts +26 -12
- package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshotLoader.ts +16 -2
- package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshotWorkerDispatcher.ts +4 -4
- package/front_end/entrypoints/heap_snapshot_worker/heap_snapshot_worker.ts +2 -0
- package/front_end/entrypoints/shell/shell.ts +1 -1
- package/front_end/generated/InspectorBackendCommands.ts +1 -1
- package/front_end/generated/protocol.ts +8 -2
- package/front_end/models/ai_assistance/DOMHelpers.ts +95 -0
- package/front_end/models/ai_assistance/EvaluateAction.ts +5 -42
- package/front_end/models/ai_assistance/ExtensionScope.ts +2 -31
- package/front_end/models/ai_assistance/agents/AiAgent.ts +2 -1
- package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +27 -2
- package/front_end/models/ai_assistance/ai_assistance.ts +6 -4
- package/front_end/models/ai_assistance/contexts/DOMNodeContext.ts +10 -7
- package/front_end/models/ai_assistance/contexts/FileContext.ts +2 -10
- package/front_end/models/ai_assistance/skills/performance.md +4 -3
- package/front_end/models/ai_assistance/tools/GetSourceContent.ts +3 -2
- 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/ListSources.ts +21 -14
- package/front_end/models/ai_assistance/tools/Tool.ts +2 -2
- package/front_end/models/ai_assistance/tools/ToolRegistry.ts +6 -6
- package/front_end/models/bindings/CompilerScriptMapping.ts +3 -10
- package/front_end/models/bindings/DebuggerLanguagePlugins.ts +48 -17
- package/front_end/models/bindings/ResourceScriptMapping.ts +2 -2
- package/front_end/models/bindings/StylesSourceMapping.ts +1 -1
- package/front_end/models/comment_manager/CD4ABridge.ts +55 -12
- package/front_end/models/comment_manager/CommentManager.ts +46 -91
- package/front_end/models/comment_manager/CommentThread.ts +145 -0
- package/front_end/models/comment_manager/comment_manager.ts +2 -0
- package/front_end/models/elements/DOMChanges.ts +238 -0
- package/front_end/models/elements/elements.ts +2 -0
- package/front_end/models/emulation/DeviceModeModel.ts +53 -0
- package/front_end/models/extensions/ExtensionAPI.ts +8 -6
- package/front_end/models/extensions/ExtensionEndpoint.ts +11 -8
- package/front_end/models/extensions/HostUrlPattern.ts +4 -3
- package/front_end/models/extensions/LanguageExtensionEndpoint.ts +3 -2
- package/front_end/models/extensions/RecorderExtensionEndpoint.ts +2 -1
- package/front_end/models/formatter/FormatterWorkerPool.ts +4 -6
- package/front_end/models/heap_snapshot/HeapSnapshotModel.ts +55 -0
- package/front_end/models/heap_snapshot/HeapSnapshotProxy.ts +18 -14
- package/front_end/models/issues_manager/IssuesManager.ts +5 -0
- package/front_end/models/issues_manager/WebInstallIssue.ts +63 -0
- package/front_end/models/issues_manager/descriptions/webInstallManifestMissingId.md +3 -0
- package/front_end/models/issues_manager/descriptions/webInstallManifestMissingNameOrShortName.md +3 -0
- package/front_end/models/issues_manager/descriptions/webInstallManifestParsingOrNetworkError.md +3 -0
- package/front_end/models/issues_manager/descriptions/webInstallNoManifest.md +3 -0
- package/front_end/models/issues_manager/descriptions/webInstallStartUrlInvalid.md +3 -0
- package/front_end/models/issues_manager/issues_manager.ts +2 -0
- package/front_end/models/workspace/UISourceCode.ts +20 -0
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +7 -7
- package/front_end/panels/ai_assistance/components/AIv2MarkdownRenderer.ts +4 -10
- package/front_end/panels/ai_assistance/components/MarkdownRendererWithCodeBlock.ts +25 -5
- package/front_end/panels/browser_debugger/ObjectEventListenersSidebarPane.ts +2 -3
- package/front_end/panels/common/CommentThreadWidget.ts +221 -0
- package/front_end/{ui/comments → panels/common}/CommentsOverlayWidget.ts +117 -28
- package/front_end/{ui/comments → panels/common}/CommentsStatusBarPill.ts +5 -5
- package/front_end/panels/common/ExtensionServer.ts +2 -1
- package/front_end/panels/common/commentThreadWidget.css +173 -0
- package/front_end/{ui/comments → panels/common}/commentsOverlay.css +8 -1
- package/front_end/{ui/comments/comments-meta.ts → panels/common/common-meta.ts} +11 -11
- package/front_end/panels/common/common.ts +3 -0
- package/front_end/panels/elements/AdoptedStyleSheetTreeElement.ts +3 -3
- package/front_end/panels/elements/ElementsPanel.ts +5 -1
- package/front_end/panels/elements/ElementsTreeElement.ts +102 -21
- package/front_end/panels/elements/ElementsTreeOutline.ts +326 -91
- package/front_end/panels/elements/EventListenersWidget.ts +0 -1
- package/front_end/panels/elements/PropertiesWidget.ts +11 -10
- package/front_end/panels/elements/PropertyRenderer.ts +29 -25
- package/front_end/panels/elements/StylePropertyTreeElement.ts +6 -6
- package/front_end/panels/elements/elementsTreeOutline.css +7 -1
- package/front_end/panels/emulation/DeviceModeToolbar.ts +2 -3
- package/front_end/panels/event_listeners/EventListenersView.ts +235 -302
- package/front_end/panels/linear_memory_inspector/LinearMemoryInspectorController.ts +6 -7
- package/front_end/panels/linear_memory_inspector/components/linearMemoryInspector.css +5 -1
- package/front_end/panels/linear_memory_inspector/linear_memory_inspector-meta.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 +7 -4
- package/front_end/panels/network/RequestHeadersView.ts +1 -1
- package/front_end/panels/network/forward/BackendLinking.ts +5 -2
- package/front_end/panels/recorder/stepView.css +1 -0
- package/front_end/panels/security/SecurityPanel.ts +5 -5
- package/front_end/panels/security/SecurityPanelSidebar.ts +2 -11
- package/front_end/panels/settings/BackendLinkingSettingsTab.ts +7 -2
- package/front_end/panels/sources/DebuggerPlugin.ts +1 -1
- package/front_end/panels/sources/WatchExpressionsSidebarPane.ts +11 -16
- package/front_end/panels/sources/sources-meta.ts +1 -1
- package/front_end/panels/timeline/TimelineTreeView.ts +11 -1
- package/front_end/panels/timeline/overlays/OverlaysImpl.ts +0 -3
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/ui/comments/CommentOverlayManager.ts +89 -42
- package/front_end/ui/comments/comments.ts +0 -4
- package/front_end/ui/legacy/Treeoutline.ts +4 -0
- package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +15 -8
- package/front_end/ui/legacy/components/perf_ui/FlameChart.ts +4 -0
- package/front_end/ui/visual_logging/KnownContextValues.ts +1 -0
- package/package.json +5 -5
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026 The Chromium Authors
|
|
3
|
+
* Use of this source code is governed by a BSD-style license that can be
|
|
4
|
+
* found in the LICENSE file.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
@scope to (devtools-widget > *) {
|
|
8
|
+
.comment-thread-widget {
|
|
9
|
+
display: flex;
|
|
10
|
+
width: var(--sys-size-30);
|
|
11
|
+
padding: var(--sys-size-8);
|
|
12
|
+
flex-direction: column;
|
|
13
|
+
align-items: flex-start;
|
|
14
|
+
gap: var(--sys-size-4);
|
|
15
|
+
border-radius: var(--sys-shape-corner-medium);
|
|
16
|
+
background: var(--sys-color-base);
|
|
17
|
+
box-shadow: var(--sys-elevation-level2);
|
|
18
|
+
cursor: default;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.comment-thread-widget.submitted {
|
|
22
|
+
background: var(--sys-color-surface2);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.header {
|
|
26
|
+
display: flex;
|
|
27
|
+
width: 100%;
|
|
28
|
+
justify-content: space-between;
|
|
29
|
+
align-items: center;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.sent-status {
|
|
33
|
+
display: flex;
|
|
34
|
+
align-items: center;
|
|
35
|
+
gap: var(--sys-size-2);
|
|
36
|
+
font-size: var(--sys-typescale-body5-size);
|
|
37
|
+
color: var(--sys-color-on-surface-subtle);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.check-icon {
|
|
41
|
+
width: var(--sys-size-7);
|
|
42
|
+
height: var(--sys-size-7);
|
|
43
|
+
color: var(--sys-color-on-surface-subtle);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.selected-item {
|
|
47
|
+
display: flex;
|
|
48
|
+
height: calc(var(--sys-size-8) + var(--sys-size-2));
|
|
49
|
+
padding: var(--sys-size-2) var(--sys-size-4) var(--sys-size-2) var(--sys-size-3);
|
|
50
|
+
align-items: center;
|
|
51
|
+
border-radius: var(--sys-shape-corner-extra-small);
|
|
52
|
+
border: var(--sys-size-1) solid var(--sys-color-divider);
|
|
53
|
+
background: var(--sys-color-base);
|
|
54
|
+
font-family: var(--source-code-font-family);
|
|
55
|
+
font-size: var(--monospace-font-size);
|
|
56
|
+
color: var(--sys-color-token-tag);
|
|
57
|
+
overflow: hidden;
|
|
58
|
+
max-width: 100%;
|
|
59
|
+
min-width: 0;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.comment-text {
|
|
63
|
+
width: 100%;
|
|
64
|
+
font-family: var(--default-font-family);
|
|
65
|
+
font-size: var(--sys-typescale-body4-size);
|
|
66
|
+
color: var(--sys-color-on-surface-subtle);
|
|
67
|
+
line-height: var(--sys-size-9);
|
|
68
|
+
overflow-wrap: break-word;
|
|
69
|
+
white-space: pre-wrap;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.agent-response {
|
|
73
|
+
display: flex;
|
|
74
|
+
gap: var(--sys-size-2);
|
|
75
|
+
margin-top: var(--sys-size-4);
|
|
76
|
+
width: 100%;
|
|
77
|
+
align-items: flex-start;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.elbow {
|
|
81
|
+
width: var(--sys-size-6);
|
|
82
|
+
height: var(--sys-size-6);
|
|
83
|
+
margin-left: var(--sys-size-3);
|
|
84
|
+
margin-right: var(--sys-size-3);
|
|
85
|
+
border-left: var(--sys-size-1) solid var(--sys-color-neutral-outline);
|
|
86
|
+
border-bottom: var(--sys-size-1) solid var(--sys-color-neutral-outline);
|
|
87
|
+
border-bottom-left-radius: var(--sys-size-6);
|
|
88
|
+
flex-shrink: 0;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.response-content {
|
|
92
|
+
display: flex;
|
|
93
|
+
flex-direction: column;
|
|
94
|
+
flex: 1;
|
|
95
|
+
min-width: 0;
|
|
96
|
+
|
|
97
|
+
--code-block-background-color: var(--sys-color-base);
|
|
98
|
+
--code-background-color: var(--sys-color-base);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.response-header {
|
|
102
|
+
font-weight: bold;
|
|
103
|
+
margin-bottom: var(--sys-size-2);
|
|
104
|
+
font-family: var(--default-font-family);
|
|
105
|
+
font-size: var(--sys-typescale-body5-size);
|
|
106
|
+
line-height: var(--sys-size-8);
|
|
107
|
+
color: var(--sys-color-on-surface-subtle);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.response-content devtools-markdown-view {
|
|
111
|
+
font-family: var(--default-font-family);
|
|
112
|
+
font-size: var(--sys-typescale-body4-size);
|
|
113
|
+
color: var(--sys-color-on-surface-subtle);
|
|
114
|
+
line-height: var(--sys-size-9);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.input {
|
|
118
|
+
height: auto;
|
|
119
|
+
min-height: var(--sys-size-14);
|
|
120
|
+
max-height: var(--sys-size-27);
|
|
121
|
+
field-sizing: content;
|
|
122
|
+
resize: none;
|
|
123
|
+
scrollbar-width: none;
|
|
124
|
+
padding: var(--sys-size-5) var(--sys-size-8);
|
|
125
|
+
border-radius: var(--sys-shape-corner-small);
|
|
126
|
+
border: var(--sys-size-1) solid var(--sys-color-outline);
|
|
127
|
+
background: var(--sys-color-base);
|
|
128
|
+
font-family: var(--default-font-family);
|
|
129
|
+
font-size: var(--sys-typescale-body5-size);
|
|
130
|
+
line-height: var(--sys-size-9);
|
|
131
|
+
letter-spacing: 0;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.input::placeholder {
|
|
135
|
+
color: var(--sys-color-on-surface-subtle);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.footer {
|
|
139
|
+
display: flex;
|
|
140
|
+
width: 100%;
|
|
141
|
+
justify-content: flex-end;
|
|
142
|
+
align-items: center;
|
|
143
|
+
gap: var(--sys-size-5);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.info-icon {
|
|
147
|
+
color: var(--sys-color-on-surface-subtle);
|
|
148
|
+
cursor: pointer;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.info-tooltip-container {
|
|
152
|
+
max-width: var(--sys-size-28);
|
|
153
|
+
padding: var(--sys-size-4) var(--sys-size-5);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.tooltip-link {
|
|
157
|
+
display: block;
|
|
158
|
+
margin-top: var(--sys-size-4);
|
|
159
|
+
color: var(--sys-color-primary);
|
|
160
|
+
padding-left: 0;
|
|
161
|
+
background: none;
|
|
162
|
+
border: none;
|
|
163
|
+
font: inherit;
|
|
164
|
+
text-decoration: underline;
|
|
165
|
+
cursor: pointer;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.selected-item-text {
|
|
169
|
+
overflow: hidden;
|
|
170
|
+
white-space: nowrap;
|
|
171
|
+
text-overflow: ellipsis;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
position: fixed;
|
|
11
11
|
inset: 0;
|
|
12
12
|
pointer-events: none;
|
|
13
|
-
/* Needs to be above regular panel widgets (e.g. flame chart at z-index 2000)
|
|
13
|
+
/* Needs to be above regular panel widgets (e.g. flame chart at z-index 2000)
|
|
14
|
+
but below floating glass panes (which start at z-index 3000). */
|
|
14
15
|
z-index: 2500;
|
|
15
16
|
overflow: hidden;
|
|
16
17
|
width: 100vw;
|
|
@@ -66,4 +67,10 @@
|
|
|
66
67
|
background-color: color-mix(in srgb, var(--sys-color-primary), transparent 85%);
|
|
67
68
|
box-sizing: border-box;
|
|
68
69
|
}
|
|
70
|
+
|
|
71
|
+
.comment-popup-widget {
|
|
72
|
+
position: absolute;
|
|
73
|
+
z-index: 2501;
|
|
74
|
+
pointer-events: auto;
|
|
75
|
+
}
|
|
69
76
|
}
|
|
@@ -6,7 +6,7 @@ import * as i18n from '../../core/i18n/i18n.js';
|
|
|
6
6
|
import type * as Root from '../../core/root/root.js';
|
|
7
7
|
import * as UI from '../../ui/legacy/legacy.js';
|
|
8
8
|
|
|
9
|
-
import type * as
|
|
9
|
+
import type * as Common from './common.js';
|
|
10
10
|
|
|
11
11
|
const UIStrings = {
|
|
12
12
|
/**
|
|
@@ -22,16 +22,16 @@ const UIStrings = {
|
|
|
22
22
|
*/
|
|
23
23
|
comments: 'Comments',
|
|
24
24
|
} as const;
|
|
25
|
-
const str_ = i18n.i18n.registerUIStrings('
|
|
25
|
+
const str_ = i18n.i18n.registerUIStrings('panels/common/common-meta.ts', UIStrings);
|
|
26
26
|
const i18nLazyString = i18n.i18n.getLazilyComputedLocalizedString.bind(undefined, str_);
|
|
27
27
|
|
|
28
|
-
let
|
|
28
|
+
let loadedCommonModule: (typeof Common|undefined);
|
|
29
29
|
|
|
30
|
-
async function
|
|
31
|
-
if (!
|
|
32
|
-
|
|
30
|
+
async function loadCommonModule(): Promise<typeof Common> {
|
|
31
|
+
if (!loadedCommonModule) {
|
|
32
|
+
loadedCommonModule = await import('./common.js');
|
|
33
33
|
}
|
|
34
|
-
return
|
|
34
|
+
return loadedCommonModule;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
function isCommentsEnabled(config?: Root.Runtime.HostConfig): boolean {
|
|
@@ -46,8 +46,8 @@ UI.ViewManager.registerViewExtension({
|
|
|
46
46
|
commandPrompt: i18nLazyString(UIStrings.showComments),
|
|
47
47
|
title: i18nLazyString(UIStrings.comments),
|
|
48
48
|
async loadView(universe) {
|
|
49
|
-
const
|
|
50
|
-
return new
|
|
49
|
+
const Common = await loadCommonModule();
|
|
50
|
+
return new Common.CommentsStatusBarPill.CommentsStatusBarPill(undefined, [universe.commentManager]);
|
|
51
51
|
},
|
|
52
52
|
});
|
|
53
53
|
|
|
@@ -59,8 +59,8 @@ UI.ActionRegistration.registerActionExtension({
|
|
|
59
59
|
toggleable: true,
|
|
60
60
|
condition: isCommentsEnabled,
|
|
61
61
|
async loadActionDelegate() {
|
|
62
|
-
const
|
|
63
|
-
return new
|
|
62
|
+
const Common = await loadCommonModule();
|
|
63
|
+
return new Common.CommentsOverlayWidget.ActionDelegate();
|
|
64
64
|
},
|
|
65
65
|
});
|
|
66
66
|
|
|
@@ -13,3 +13,6 @@ export * as ExtensionView from './ExtensionView.js';
|
|
|
13
13
|
export * as PersistenceUtils from './PersistenceUtils.js';
|
|
14
14
|
export * as DOMLinkifier from './DOMLinkifier.js';
|
|
15
15
|
export * as ExtensionIframe from './ExtensionView.js';
|
|
16
|
+
export * as CommentThreadWidget from './CommentThreadWidget.js';
|
|
17
|
+
export * as CommentsOverlayWidget from './CommentsOverlayWidget.js';
|
|
18
|
+
export * as CommentsStatusBarPill from './CommentsStatusBarPill.js';
|
|
@@ -20,7 +20,7 @@ const {Directives: {ref}, html, render} = Lit;
|
|
|
20
20
|
|
|
21
21
|
export class AdoptedStyleSheetSetTreeElement extends UI.TreeOutline.TreeElement {
|
|
22
22
|
constructor(readonly adoptedStyleSheets: SDK.DOMModel.AdoptedStyleSheet[]) {
|
|
23
|
-
super('');
|
|
23
|
+
super('', undefined, 'adopted-style-sheets');
|
|
24
24
|
const documentElement = this.listItemElement.createChild('span');
|
|
25
25
|
UI.UIUtils.createTextChild(documentElement, '#adopted-style-sheets');
|
|
26
26
|
for (const adoptedStyleSheet of adoptedStyleSheets) {
|
|
@@ -33,7 +33,7 @@ export class AdoptedStyleSheetTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
33
33
|
private eventListener: Common.EventTarget.EventDescriptor|null = null;
|
|
34
34
|
|
|
35
35
|
constructor(readonly adoptedStyleSheet: SDK.DOMModel.AdoptedStyleSheet) {
|
|
36
|
-
super('');
|
|
36
|
+
super('', undefined, 'adopted-style-sheet');
|
|
37
37
|
const header = adoptedStyleSheet.cssModel.styleSheetHeaderForId(adoptedStyleSheet.id);
|
|
38
38
|
if (header) {
|
|
39
39
|
AdoptedStyleSheetTreeElement.createContents(header, this);
|
|
@@ -79,7 +79,7 @@ export class AdoptedStyleSheetContentsTreeElement extends UI.TreeOutline.TreeEle
|
|
|
79
79
|
private readonly widgetWrapper: HTMLElement;
|
|
80
80
|
|
|
81
81
|
constructor(styleSheetHeader: SDK.CSSStyleSheetHeader.CSSStyleSheetHeader) {
|
|
82
|
-
super('');
|
|
82
|
+
super('', undefined, 'adopted-style-sheet-contents');
|
|
83
83
|
this.widgetWrapper = document.createElement('div');
|
|
84
84
|
this.widgetWrapper.style.display = 'contents';
|
|
85
85
|
this.title = this.widgetWrapper;
|
|
@@ -228,6 +228,10 @@ export class ElementsPanel extends UI.Panel.Panel implements UI.SearchableView.S
|
|
|
228
228
|
return this.#domTreeWidget.getTreeOutlineForTesting();
|
|
229
229
|
}
|
|
230
230
|
|
|
231
|
+
getDOMTreeWidgetForTesting(): DOMTreeWidget {
|
|
232
|
+
return this.#domTreeWidget;
|
|
233
|
+
}
|
|
234
|
+
|
|
231
235
|
constructor(
|
|
232
236
|
targetManager?: SDK.TargetManager.TargetManager,
|
|
233
237
|
settings?: Common.Settings.Settings,
|
|
@@ -1300,7 +1304,7 @@ export class ElementsPanel extends UI.Panel.Panel implements UI.SearchableView.S
|
|
|
1300
1304
|
}
|
|
1301
1305
|
|
|
1302
1306
|
toggleHideElement(node: SDK.DOMModel.DOMNode): void {
|
|
1303
|
-
this.#domTreeWidget.toggleHideElement(node);
|
|
1307
|
+
void this.#domTreeWidget.toggleHideElement(node);
|
|
1304
1308
|
}
|
|
1305
1309
|
|
|
1306
1310
|
toggleEditAsHTML(node: SDK.DOMModel.DOMNode): void {
|
|
@@ -48,7 +48,8 @@ import * as Protocol from '../../generated/protocol.js';
|
|
|
48
48
|
import * as AIAssistance from '../../models/ai_assistance/ai_assistance.js';
|
|
49
49
|
import * as Badges from '../../models/badges/badges.js';
|
|
50
50
|
import * as Bindings from '../../models/bindings/bindings.js';
|
|
51
|
-
import
|
|
51
|
+
import * as ChangeTracker from '../../models/change_tracker/change_tracker.js';
|
|
52
|
+
import * as Elements from '../../models/elements/elements.js';
|
|
52
53
|
import * as IssuesManager from '../../models/issues_manager/issues_manager.js';
|
|
53
54
|
import * as Workspace from '../../models/workspace/workspace.js';
|
|
54
55
|
import * as CodeMirror from '../../third_party/codemirror.next/codemirror.next.js';
|
|
@@ -65,6 +66,7 @@ import * as PanelsCommon from '../common/common.js';
|
|
|
65
66
|
import * as Media from '../media/media.js';
|
|
66
67
|
|
|
67
68
|
import * as ElementsComponents from './components/components.js';
|
|
69
|
+
import {cssPath} from './DOMPath.js';
|
|
68
70
|
import {getElementIssueDetails} from './ElementIssueUtils.js';
|
|
69
71
|
import {ElementsPanel} from './ElementsPanel.js';
|
|
70
72
|
import * as ElementStatePaneWidget from './ElementStatePaneWidget.js';
|
|
@@ -72,6 +74,33 @@ import {type ElementsTreeOutline, MappedCharToEntity} from './ElementsTreeOutlin
|
|
|
72
74
|
import {ImagePreviewPopover} from './ImagePreviewPopover.js';
|
|
73
75
|
import {getRegisteredDecorators, type MarkerDecorator, type MarkerDecoratorRegistration} from './MarkerDecorator.js';
|
|
74
76
|
|
|
77
|
+
/**
|
|
78
|
+
* Returns the CSS selector of `node` that is used as the text signature of the anchor a change is
|
|
79
|
+
* recorded on, or `undefined` if `tracker` is absent or not recording changes.
|
|
80
|
+
*
|
|
81
|
+
* Resolving the selector walks the ancestor chain and scans sibling lists, so it is skipped
|
|
82
|
+
* entirely while change tracking is disabled.
|
|
83
|
+
*/
|
|
84
|
+
export function buildChangeSelector(tracker: ChangeTracker.ChangeTracker.ChangeTracker|undefined,
|
|
85
|
+
node: SDK.DOMModel.DOMNode): string|undefined {
|
|
86
|
+
if (!tracker?.isTracking) {
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
89
|
+
if (node.nodeType() === Node.ELEMENT_NODE) {
|
|
90
|
+
const selector = cssPath(node, true);
|
|
91
|
+
if (selector) {
|
|
92
|
+
return selector;
|
|
93
|
+
}
|
|
94
|
+
} else if (node.parentNode && node.parentNode.nodeType() === Node.ELEMENT_NODE) {
|
|
95
|
+
const selector = cssPath(node.parentNode, true);
|
|
96
|
+
if (selector) {
|
|
97
|
+
return selector;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
|
|
75
104
|
const {html, nothing, render, Directives: {classMap, ref, repeat, until}} = Lit;
|
|
76
105
|
const {animateOn} = UI.UIUtils;
|
|
77
106
|
|
|
@@ -325,6 +354,7 @@ export interface ViewInput {
|
|
|
325
354
|
descendantDecorations: Decoration[];
|
|
326
355
|
decorationsTooltip: string;
|
|
327
356
|
indent: number;
|
|
357
|
+
renderSelection?: boolean;
|
|
328
358
|
|
|
329
359
|
editorState: CodeMirror.EditorState|null;
|
|
330
360
|
editorWidth: number|null;
|
|
@@ -770,14 +800,6 @@ function renderTag(node: SDK.DOMModel.DOMNode, tagName: string, isClosingTag: bo
|
|
|
770
800
|
hasUpdates = hasUpdates || (!expanded && updateRecord.hasChangedChildren());
|
|
771
801
|
}
|
|
772
802
|
|
|
773
|
-
// We are taking full text content of the tag, including attributes and children, to set the aria label.
|
|
774
|
-
// FIXME: we should compute the aria label ourselves if it is event needed.
|
|
775
|
-
const setAriaLabel = ref(el => {
|
|
776
|
-
if (el?.textContent) {
|
|
777
|
-
UI.ARIAUtils.setLabel(el, el.textContent);
|
|
778
|
-
}
|
|
779
|
-
});
|
|
780
|
-
|
|
781
803
|
const tagNameClass = isClosingTag ? 'webkit-html-close-tag-name' : 'webkit-html-tag-name';
|
|
782
804
|
const hasTagIssues = !isClosingTag && Boolean(issues?.some(issue => {
|
|
783
805
|
const details = getElementIssueDetails(issue);
|
|
@@ -790,8 +812,13 @@ function renderTag(node: SDK.DOMModel.DOMNode, tagName: string, isClosingTag: bo
|
|
|
790
812
|
const tagString = (isClosingTag ? '/' : '') + tagName;
|
|
791
813
|
const jslog = !isClosingTag ? VisualLogging.value('tag-name').track({change: true, dblclick: true}) : '';
|
|
792
814
|
|
|
815
|
+
const ariaLabel = isClosingTag ?
|
|
816
|
+
`</${tagName}>` :
|
|
817
|
+
`<${tagName}${
|
|
818
|
+
attributes.map(attr => (attr.value ? ` ${attr.name}="${attr.value}"` : ` ${attr.name}`)).join('')}>`;
|
|
819
|
+
|
|
793
820
|
return html`<span
|
|
794
|
-
class=${classMap(tagClasses)}
|
|
821
|
+
class=${classMap(tagClasses)} aria-label=${ariaLabel}
|
|
795
822
|
><<span class=${classMap(tagNameClasses)} jslog=${jslog || nothing} ${
|
|
796
823
|
animateOn(hasUpdates, DOM_UPDATE_ANIMATION_CLASS_NAME)}>${tagString}</span>${
|
|
797
824
|
attributes.map(attr => html` ${renderAttribute(attr, updateRecord, false, node, issues)}`)}></span>\u200B`;
|
|
@@ -880,7 +907,7 @@ export const DEFAULT_VIEW = (input: ViewInput, output: ViewOutput, target: HTMLE
|
|
|
880
907
|
input.onExpand,
|
|
881
908
|
input.issues,
|
|
882
909
|
)}</span>` : nothing}
|
|
883
|
-
|
|
910
|
+
${input.renderSelection !== false ? html`<div class="selection fill ${input.editorState ? 'hidden' : ''}" style=${`margin-left: ${-input.indent}px`}></div>` : nothing}
|
|
884
911
|
<div class=${classMap(gutterContainerClasses)}
|
|
885
912
|
style="left: ${-input.indent}px"
|
|
886
913
|
@click=${input.onGutterClick}>
|
|
@@ -1102,7 +1129,10 @@ export const DEFAULT_VIEW = (input: ViewInput, output: ViewOutput, target: HTMLE
|
|
|
1102
1129
|
if (event.key === 'Escape') {
|
|
1103
1130
|
event.consume(true);
|
|
1104
1131
|
}
|
|
1105
|
-
}}
|
|
1132
|
+
}}
|
|
1133
|
+
@mousedown=${(event: Event) => event.stopPropagation()}
|
|
1134
|
+
@click=${(event: Event) => event.stopPropagation()}
|
|
1135
|
+
class="source-code elements-tree-editor" style="width: ${input.editorWidth ?? 0}px;">
|
|
1106
1136
|
<devtools-text-editor .state=${input.editorState} ${ref(el => {
|
|
1107
1137
|
output.editorRef = el as TextEditor.TextEditor.TextEditor;
|
|
1108
1138
|
})}></devtools-text-editor>
|
|
@@ -1124,9 +1154,14 @@ export interface InitialEditState {
|
|
|
1124
1154
|
}
|
|
1125
1155
|
|
|
1126
1156
|
export class ElementsTreeWidget extends UI.Widget.Widget {
|
|
1127
|
-
static override readonly INJECT: readonly[typeof IssuesManager.DOMIssuesManager.DOMIssuesManager
|
|
1128
|
-
|
|
1157
|
+
static override readonly INJECT: readonly[typeof IssuesManager.DOMIssuesManager.DOMIssuesManager,
|
|
1158
|
+
typeof ChangeTracker.ChangeTracker.ChangeTracker] =
|
|
1159
|
+
[
|
|
1160
|
+
IssuesManager.DOMIssuesManager.DOMIssuesManager,
|
|
1161
|
+
ChangeTracker.ChangeTracker.ChangeTracker,
|
|
1162
|
+
] as const;
|
|
1129
1163
|
#domIssuesManager?: IssuesManager.DOMIssuesManager.DOMIssuesManager;
|
|
1164
|
+
#changeTracker?: ChangeTracker.ChangeTracker.ChangeTracker;
|
|
1130
1165
|
|
|
1131
1166
|
#node!: SDK.DOMModel.DOMNode;
|
|
1132
1167
|
#eventsBound = false;
|
|
@@ -1137,6 +1172,7 @@ export class ElementsTreeWidget extends UI.Widget.Widget {
|
|
|
1137
1172
|
isXMLMimeType = false;
|
|
1138
1173
|
disableEdits = false;
|
|
1139
1174
|
showAIButton = false;
|
|
1175
|
+
renderSelection = true;
|
|
1140
1176
|
isDOMNodeSelected = false;
|
|
1141
1177
|
initialEdit?: InitialEditState|null;
|
|
1142
1178
|
onInitialEditCompleted?: () => void;
|
|
@@ -1370,13 +1406,25 @@ export class ElementsTreeWidget extends UI.Widget.Widget {
|
|
|
1370
1406
|
return this.#domIssuesManager?.issuesForNode(this.node) ?? [];
|
|
1371
1407
|
}
|
|
1372
1408
|
|
|
1409
|
+
get changeTracker(): ChangeTracker.ChangeTracker.ChangeTracker|undefined {
|
|
1410
|
+
// The widget is currently created for non-widget ElementsTreeElement so
|
|
1411
|
+
// the changeTracker can be empty, relying on the manual resolution.
|
|
1412
|
+
// Note that this only works once the widget is attached to the DOM.
|
|
1413
|
+
this.#changeTracker ??=
|
|
1414
|
+
UI.Widget.lookupUniverseForElement(this.contentElement)?.get(ChangeTracker.ChangeTracker.ChangeTracker);
|
|
1415
|
+
return this.#changeTracker;
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1373
1418
|
constructor(
|
|
1374
1419
|
element?: HTMLElement,
|
|
1375
|
-
[domIssuesManager]:
|
|
1420
|
+
[domIssuesManager, changeTracker]:
|
|
1421
|
+
UI.Widget.WidgetDependencies<typeof ElementsTreeWidget>|[IssuesManager.DOMIssuesManager.DOMIssuesManager?,
|
|
1422
|
+
ChangeTracker.ChangeTracker.ChangeTracker?] = [],
|
|
1376
1423
|
view: View = DEFAULT_VIEW,
|
|
1377
1424
|
) {
|
|
1378
1425
|
super(element);
|
|
1379
1426
|
this.#domIssuesManager = domIssuesManager;
|
|
1427
|
+
this.#changeTracker = changeTracker;
|
|
1380
1428
|
this.#view = view;
|
|
1381
1429
|
|
|
1382
1430
|
this.#expandedChildrenLimit = InitialChildrenLimit;
|
|
@@ -1461,6 +1509,7 @@ export class ElementsTreeWidget extends UI.Widget.Widget {
|
|
|
1461
1509
|
}
|
|
1462
1510
|
|
|
1463
1511
|
#clearDOMNextUpdate = false;
|
|
1512
|
+
|
|
1464
1513
|
#isBound = false;
|
|
1465
1514
|
|
|
1466
1515
|
override wasShown(): void {
|
|
@@ -1537,6 +1586,7 @@ export class ElementsTreeWidget extends UI.Widget.Widget {
|
|
|
1537
1586
|
descendantDecorations: this.#expanded ? [] : this.#descendantDecorations,
|
|
1538
1587
|
decorationsTooltip: this.#decorationsTooltip,
|
|
1539
1588
|
indent: this.#getLeftIndent(),
|
|
1589
|
+
renderSelection: this.renderSelection,
|
|
1540
1590
|
showScrollSnapAdorner: Boolean(this.#layout?.hasScroll) && !isClosingTag,
|
|
1541
1591
|
scrollSnapAdornerActive: this.#scrollSnapAdornerActive,
|
|
1542
1592
|
showSlotAdorner: Boolean(this.node.assignedSlot) && !isClosingTag,
|
|
@@ -2448,7 +2498,8 @@ export class ElementsTreeWidget extends UI.Widget.Widget {
|
|
|
2448
2498
|
}
|
|
2449
2499
|
// The relatedTarget is null when no element gains focus, e.g. switching windows.
|
|
2450
2500
|
const relatedTarget = (event.relatedTarget as Node | null);
|
|
2451
|
-
if (relatedTarget && !relatedTarget.isSelfOrDescendant(this.#editorRef)
|
|
2501
|
+
if (relatedTarget && !relatedTarget.isSelfOrDescendant(this.#editorRef) &&
|
|
2502
|
+
!relatedTarget.isSelfOrDescendant(this.contentElement)) {
|
|
2452
2503
|
this.editing?.commit();
|
|
2453
2504
|
}
|
|
2454
2505
|
},
|
|
@@ -2595,10 +2646,16 @@ export class ElementsTreeWidget extends UI.Widget.Widget {
|
|
|
2595
2646
|
}
|
|
2596
2647
|
|
|
2597
2648
|
if (attributeName !== null && (attributeName.trim() || newText.trim()) && oldText !== newText) {
|
|
2649
|
+
const edit = {attributeName, oldText, newText};
|
|
2598
2650
|
this.node.setAttribute(attributeName, newText, (error: string|null) => {
|
|
2651
|
+
if (!error) {
|
|
2652
|
+
const changeTracker = this.changeTracker;
|
|
2653
|
+
Elements.DOMChanges.trackAttributeEdit(changeTracker, this.node,
|
|
2654
|
+
buildChangeSelector(changeTracker, this.node), edit);
|
|
2655
|
+
Badges.UserBadges.instance().recordAction(Badges.BadgeAction.DOM_ELEMENT_OR_ATTRIBUTE_EDITED);
|
|
2656
|
+
}
|
|
2599
2657
|
moveToNextAttributeIfNeeded.call(this, error);
|
|
2600
2658
|
});
|
|
2601
|
-
Badges.UserBadges.instance().recordAction(Badges.BadgeAction.DOM_ELEMENT_OR_ATTRIBUTE_EDITED);
|
|
2602
2659
|
return;
|
|
2603
2660
|
}
|
|
2604
2661
|
|
|
@@ -2660,6 +2717,9 @@ export class ElementsTreeWidget extends UI.Widget.Widget {
|
|
|
2660
2717
|
}
|
|
2661
2718
|
|
|
2662
2719
|
Badges.UserBadges.instance().recordAction(Badges.BadgeAction.DOM_ELEMENT_OR_ATTRIBUTE_EDITED);
|
|
2720
|
+
const changeTracker = this.changeTracker;
|
|
2721
|
+
Elements.DOMChanges.trackTagNameEdit(changeTracker, newNode, buildChangeSelector(changeTracker, newNode),
|
|
2722
|
+
oldText ?? tagName ?? '', newText);
|
|
2663
2723
|
if (this.selectNodeAfterEdit) {
|
|
2664
2724
|
this.selectNodeAfterEdit(wasExpanded, error, newNode, moveDirection);
|
|
2665
2725
|
}
|
|
@@ -2668,8 +2728,14 @@ export class ElementsTreeWidget extends UI.Widget.Widget {
|
|
|
2668
2728
|
|
|
2669
2729
|
private textNodeEditingCommitted(textNode: SDK.DOMModel.DOMNode, _element: Element, newText: string): void {
|
|
2670
2730
|
this.editing = null;
|
|
2731
|
+
const oldValue = textNode.nodeValue() ?? '';
|
|
2671
2732
|
|
|
2672
|
-
function callback(this: ElementsTreeWidget): void {
|
|
2733
|
+
function callback(this: ElementsTreeWidget, error?: string|null): void {
|
|
2734
|
+
if (!error && oldValue !== newText) {
|
|
2735
|
+
const changeTracker = this.changeTracker;
|
|
2736
|
+
Elements.DOMChanges.trackTextNodeEdit(changeTracker, textNode, buildChangeSelector(changeTracker, textNode),
|
|
2737
|
+
oldValue, newText);
|
|
2738
|
+
}
|
|
2673
2739
|
this.#clearDOMNextUpdate = true;
|
|
2674
2740
|
this.updateTitle();
|
|
2675
2741
|
}
|
|
@@ -2821,7 +2887,7 @@ export class ElementsTreeWidget extends UI.Widget.Widget {
|
|
|
2821
2887
|
async remove(): Promise<void> {
|
|
2822
2888
|
if (this.isToggledToHidden?.(this.node)) {
|
|
2823
2889
|
// Unhide the node before removing. This avoids inconsistent state if the node is restored via undo.
|
|
2824
|
-
await this.toggleHideElement
|
|
2890
|
+
await this.node.toggleHideElement();
|
|
2825
2891
|
}
|
|
2826
2892
|
if (this.node.pseudoType()) {
|
|
2827
2893
|
return;
|
|
@@ -2830,7 +2896,14 @@ export class ElementsTreeWidget extends UI.Widget.Widget {
|
|
|
2830
2896
|
if (!this.node.parentNode || this.node.parentNode.nodeType() === Node.DOCUMENT_NODE) {
|
|
2831
2897
|
return;
|
|
2832
2898
|
}
|
|
2833
|
-
|
|
2899
|
+
// The selector has to be resolved before the node is detached from the tree.
|
|
2900
|
+
const changeTracker = this.changeTracker;
|
|
2901
|
+
const selector = buildChangeSelector(changeTracker, this.node);
|
|
2902
|
+
await this.node.removeNode((err: string|null) => {
|
|
2903
|
+
if (!err) {
|
|
2904
|
+
Elements.DOMChanges.trackNodeRemoval(changeTracker, this.node, selector);
|
|
2905
|
+
}
|
|
2906
|
+
});
|
|
2834
2907
|
}
|
|
2835
2908
|
|
|
2836
2909
|
toggleEditAsHTML(callback?: ((arg0: boolean) => void), startEditing?: boolean): void {
|
|
@@ -2851,7 +2924,15 @@ export class ElementsTreeWidget extends UI.Widget.Widget {
|
|
|
2851
2924
|
|
|
2852
2925
|
const commitChange = (initialValue: string, value: string): void => {
|
|
2853
2926
|
if (initialValue !== value) {
|
|
2854
|
-
node.
|
|
2927
|
+
// The selector has to be resolved before the node is detached from the tree.
|
|
2928
|
+
const changeTracker = this.changeTracker;
|
|
2929
|
+
const selector = buildChangeSelector(changeTracker, node);
|
|
2930
|
+
node.setOuterHTML(value, (error: string|null) => {
|
|
2931
|
+
if (!error) {
|
|
2932
|
+
Elements.DOMChanges.trackHTMLEdit(changeTracker, node, selector, initialValue, value);
|
|
2933
|
+
}
|
|
2934
|
+
selectNode(error);
|
|
2935
|
+
});
|
|
2855
2936
|
}
|
|
2856
2937
|
};
|
|
2857
2938
|
|