chrome-devtools-frontend 1.0.1724140 → 1.0.1726686
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/review-ui-widgets/SKILL.md +7 -7
- package/.agents/skills/ui-eng-vision-local-lit-renderer/SKILL.md +12 -0
- package/.agents/skills/ui-eng-vision-widget-promoter/SKILL.md +3 -3
- package/.agents/skills/ui-widgets/SKILL.md +2 -1
- package/docs/cookbook/dependencies.md +6 -4
- package/front_end/core/i18n/i18nImpl.ts +1 -1
- package/front_end/core/root/Runtime.ts +1 -1
- package/front_end/core/sdk/ResourceTreeModel.ts +16 -0
- package/front_end/entrypoints/js_app/js_app.ts +2 -5
- package/front_end/entrypoints/node_app/node_app.ts +2 -3
- package/front_end/generated/InspectorBackendCommands.ts +1 -1
- package/front_end/generated/protocol.ts +1 -0
- package/front_end/models/ai_assistance/skills/lighthouse.md +2 -1
- package/front_end/models/ai_assistance/skills/network.md +1 -0
- package/front_end/models/ai_assistance/tools/ExecuteJavaScript.ts +26 -2
- package/front_end/models/ai_assistance/tools/GetLighthouseAudits.ts +1 -1
- package/front_end/models/ai_assistance/tools/ListNetworkRequests.ts +2 -7
- package/front_end/models/ai_assistance/tools/RunLighthouse.ts +2 -2
- package/front_end/models/comment_manager/CommentManager.ts +0 -6
- package/front_end/models/comment_manager/CommentThread.ts +23 -19
- package/front_end/models/elements/StyleChanges.ts +66 -0
- package/front_end/models/trace/EventsSerializer.ts +2 -7
- package/front_end/models/trace/ModelImpl.ts +0 -2
- package/front_end/models/trace/handlers/FlowsHandler.ts +37 -29
- package/front_end/models/trace/helpers/SyntheticEvents.ts +10 -37
- package/front_end/models/trace/insights/RenderBlocking.ts +3 -1
- package/front_end/models/trace/lantern/simulation/Simulator.ts +4 -0
- package/front_end/models/trace/lantern/types/Lantern.ts +1 -0
- package/front_end/panels/accessibility/SourceOrderView.ts +1 -1
- package/front_end/panels/application/ApplicationPanelSidebar.ts +90 -0
- package/front_end/panels/application/StorageView.ts +196 -174
- package/front_end/panels/application/SubAppsView.ts +2 -2
- package/front_end/panels/application/preloading/PreloadingView.ts +360 -336
- package/front_end/panels/application/preloading/components/PreloadingDisabledInfobar.ts +2 -2
- package/front_end/panels/application/preloading/components/PreloadingGrid.ts +6 -3
- package/front_end/panels/application/preloading/components/RuleSetGrid.ts +17 -17
- package/front_end/panels/application/preloading/components/UsedPreloadingView.ts +2 -2
- package/front_end/panels/application/preloading/preloadingView.css +12 -4
- package/front_end/panels/application/preloading/preloadingViewDropDown.css +1 -1
- package/front_end/panels/application/storageView.css +16 -21
- package/front_end/panels/browser_debugger/CategorizedBreakpointsSidebarPane.ts +2 -2
- package/front_end/panels/browser_debugger/browser_debugger-meta.ts +6 -8
- package/front_end/panels/comments/CommentsPane.ts +26 -9
- package/front_end/panels/comments/commentsPane.css +25 -6
- package/front_end/panels/common/CommentThreadWidget.ts +2 -2
- package/front_end/panels/common/CommentsOverlayWidget.ts +94 -35
- package/front_end/panels/common/commentThreadWidget.css +17 -0
- package/front_end/panels/common/commentsOverlay.css +5 -10
- package/front_end/panels/common/common-meta.ts +11 -1
- package/front_end/panels/elements/ClassesPaneWidget.ts +130 -88
- package/front_end/panels/elements/ColorSwatchPopoverIcon.ts +7 -33
- package/front_end/panels/elements/DOMTreeWidget.ts +7 -3
- package/front_end/panels/elements/ElementStatePaneWidget.ts +11 -1
- package/front_end/panels/elements/PropertyRenderer.ts +3 -2
- package/front_end/panels/elements/StyleEditorWidget.ts +60 -41
- package/front_end/panels/elements/StylePropertiesSection.ts +32 -5
- package/front_end/panels/elements/StylePropertyTreeElement.ts +227 -162
- package/front_end/panels/event_listeners/EventListenersView.ts +11 -8
- package/front_end/panels/linear_memory_inspector/components/ValueInterpreterSettings.ts +1 -1
- package/front_end/panels/network/NetworkDataGridNode.ts +3 -1
- package/front_end/panels/network/RequestConditionsDrawer.ts +1 -1
- package/front_end/panels/network/RequestPayloadView.ts +12 -5
- package/front_end/panels/profiler/HeapProfileView.ts +28 -6
- package/front_end/panels/profiler/HeapSnapshotDataGrids.ts +7 -3
- package/front_end/panels/protocol_monitor/JSONEditor.ts +0 -1
- package/front_end/panels/recorder/RecorderPanel.ts +2 -2
- package/front_end/panels/settings/SettingsScreen.ts +1 -1
- package/front_end/panels/sources/NavigatorView.ts +105 -45
- package/front_end/panels/sources/ScopeChainSidebarPane.ts +8 -5
- package/front_end/panels/sources/SourcesNavigator.ts +421 -213
- package/front_end/panels/sources/SourcesPanel.ts +6 -4
- package/front_end/panels/sources/WatchExpressionsSidebarPane.ts +10 -5
- package/front_end/panels/sources/sources-meta.ts +4 -15
- package/front_end/panels/sources/sourcesNavigator.css +5 -0
- package/front_end/panels/timeline/ModificationsManager.ts +0 -2
- package/front_end/panels/timeline/README.md +2 -0
- package/front_end/panels/timeline/TimelineCommentController.ts +132 -30
- package/front_end/panels/timeline/TimelinePanel.ts +4 -0
- package/front_end/panels/timeline/components/IgnoreListSetting.ts +1 -1
- package/front_end/panels/timeline/utils/Treemap.ts +8 -0
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/ui/comments/CommentAnchorResolver.ts +3 -0
- package/front_end/ui/comments/CommentOverlayManager.ts +2 -4
- package/front_end/ui/components/lists/List.ts +14 -1
- package/front_end/ui/components/panel_feedback/PreviewToggle.ts +1 -1
- package/front_end/ui/components/text_editor/AiCodeCompletionProvider.ts +23 -14
- package/front_end/ui/components/text_editor/AiCodeGenerationProvider.ts +3 -0
- package/front_end/ui/legacy/Toolbar.ts +0 -1
- package/front_end/ui/legacy/Treeoutline.ts +7 -2
- package/front_end/ui/legacy/UIUtils.ts +4 -1
- package/front_end/ui/legacy/components/inline_editor/Swatches.ts +19 -6
- package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +20 -11
- package/front_end/ui/legacy/components/source_frame/XMLView.ts +27 -7
- package/front_end/ui/visual_logging/KnownContextValues.ts +1 -0
- package/package.json +1 -1
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
// Copyright 2015 The Chromium Authors
|
|
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
|
-
/* eslint-disable @devtools/no-imperative-dom-api */
|
|
5
4
|
|
|
6
5
|
import * as Common from '../../core/common/common.js';
|
|
7
6
|
import * as i18n from '../../core/i18n/i18n.js';
|
|
8
7
|
import * as Platform from '../../core/platform/platform.js';
|
|
9
8
|
import * as SDK from '../../core/sdk/sdk.js';
|
|
10
9
|
import * as UI from '../../ui/legacy/legacy.js';
|
|
10
|
+
import {Directives, html, render} from '../../ui/lit/lit.js';
|
|
11
11
|
import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
|
|
12
12
|
|
|
13
13
|
import classesPaneWidgetStyles from './classesPaneWidget.css.js';
|
|
@@ -37,42 +37,89 @@ const UIStrings = {
|
|
|
37
37
|
} as const;
|
|
38
38
|
const str_ = i18n.i18n.registerUIStrings('panels/elements/ClassesPaneWidget.ts', UIStrings);
|
|
39
39
|
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
|
40
|
+
|
|
41
|
+
const {ref, repeat} = Directives;
|
|
42
|
+
|
|
43
|
+
export interface ClassItem {
|
|
44
|
+
className: string;
|
|
45
|
+
enabled: boolean;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface ViewInput {
|
|
49
|
+
classes: ClassItem[];
|
|
50
|
+
onCommit: () => void;
|
|
51
|
+
/** Returns whether the prompt had text to discard, in which case the key event is fully consumed. */
|
|
52
|
+
onCancel: () => boolean;
|
|
53
|
+
onClick: (className: string, enabled: boolean) => void;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface ViewOutput {
|
|
57
|
+
attachPrompt?: (prompt: UI.TextPrompt.TextPrompt) => void;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export type View = (input: ViewInput, output: ViewOutput, target: HTMLElement) => void;
|
|
61
|
+
|
|
62
|
+
export const DEFAULT_VIEW: View = (input, output, target) => {
|
|
63
|
+
const onKeyDown = (event: KeyboardEvent): void => {
|
|
64
|
+
if (event.key === Platform.KeyboardUtilities.ENTER_KEY && !event.isComposing) {
|
|
65
|
+
event.consume();
|
|
66
|
+
input.onCommit();
|
|
67
|
+
} else if (Platform.KeyboardUtilities.isEscKey(event) && input.onCancel()) {
|
|
68
|
+
// Escape discarded non-empty prompt text, so stop it here instead of letting
|
|
69
|
+
// it reach global DevTools shortcuts (e.g. closing the drawer).
|
|
70
|
+
event.consume(true);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
// clang-format off
|
|
74
|
+
render(html`
|
|
75
|
+
<style>${classesPaneWidgetStyles}</style>
|
|
76
|
+
<div class="styles-element-classes-pane">
|
|
77
|
+
<div class="title-container" @keydown=${onKeyDown}>
|
|
78
|
+
<div class="new-class-input monospace" autofocus ${ref(element => {
|
|
79
|
+
output.attachPrompt = element instanceof HTMLElement ? prompt => prompt.attach(element) : undefined;
|
|
80
|
+
})}></div>
|
|
81
|
+
</div>
|
|
82
|
+
<div class="source-code styles-element-classes-container">
|
|
83
|
+
${repeat(input.classes, item => item.className, item => html`
|
|
84
|
+
<devtools-checkbox
|
|
85
|
+
class="monospace"
|
|
86
|
+
small
|
|
87
|
+
.checked=${item.enabled}
|
|
88
|
+
title=${item.className}
|
|
89
|
+
jslog=${VisualLogging.toggle('element-class').track({change: true})}
|
|
90
|
+
@click=${(event: Event) => input.onClick(item.className, (event.target as UI.UIUtils.CheckboxLabel).checked)}>${item.className}</devtools-checkbox>`)}
|
|
91
|
+
</div>
|
|
92
|
+
</div>`, target);
|
|
93
|
+
// clang-format on
|
|
94
|
+
};
|
|
95
|
+
|
|
40
96
|
export class ClassesPaneWidget extends UI.Widget.Widget {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
constructor() {
|
|
50
|
-
super({
|
|
97
|
+
readonly #view: View;
|
|
98
|
+
readonly #prompt: ClassNamePrompt;
|
|
99
|
+
#promptAttached = false;
|
|
100
|
+
readonly #mutatingNodes: Set<SDK.DOMModel.DOMNode>;
|
|
101
|
+
readonly #pendingNodeClasses: Map<SDK.DOMModel.DOMNode, string>;
|
|
102
|
+
readonly #updateNodeThrottler: Common.Throttler.Throttler;
|
|
103
|
+
#previousTarget: SDK.DOMModel.DOMNode|null;
|
|
104
|
+
|
|
105
|
+
constructor(element?: HTMLElement, view: View = DEFAULT_VIEW) {
|
|
106
|
+
super(element, {
|
|
51
107
|
jslog: `${VisualLogging.pane('elements-classes')}`,
|
|
52
108
|
useShadowDom: true,
|
|
53
109
|
});
|
|
54
|
-
this
|
|
55
|
-
this
|
|
56
|
-
|
|
57
|
-
this.
|
|
58
|
-
|
|
59
|
-
this.
|
|
60
|
-
this.classesContainer.classList.add('styles-element-classes-container');
|
|
61
|
-
this.prompt = new ClassNamePrompt(this.nodeClasses.bind(this));
|
|
62
|
-
this.prompt.setAutocompletionTimeout(0);
|
|
63
|
-
this.prompt.renderAsBlock();
|
|
64
|
-
|
|
65
|
-
const proxyElement = (this.prompt.attach(this.input) as HTMLElement);
|
|
66
|
-
this.prompt.setPlaceholder(i18nString(UIStrings.addNewClass));
|
|
67
|
-
this.prompt.addEventListener(UI.TextPrompt.Events.TEXT_CHANGED, this.onTextChanged, this);
|
|
68
|
-
proxyElement.addEventListener('keydown', this.onKeyDown.bind(this), false);
|
|
110
|
+
this.#view = view;
|
|
111
|
+
this.#prompt = new ClassNamePrompt(this.nodeClasses.bind(this));
|
|
112
|
+
this.#prompt.setAutocompletionTimeout(0);
|
|
113
|
+
this.#prompt.renderAsBlock();
|
|
114
|
+
|
|
115
|
+
this.#prompt.addEventListener(UI.TextPrompt.Events.TEXT_CHANGED, this.onTextChanged, this);
|
|
69
116
|
|
|
70
117
|
SDK.TargetManager.TargetManager.instance().addModelListener(
|
|
71
118
|
SDK.DOMModel.DOMModel, SDK.DOMModel.Events.DOMMutated, this.onDOMMutated, this, {scoped: true});
|
|
72
|
-
this
|
|
73
|
-
this
|
|
74
|
-
this
|
|
75
|
-
this
|
|
119
|
+
this.#mutatingNodes = new Set();
|
|
120
|
+
this.#pendingNodeClasses = new Map();
|
|
121
|
+
this.#updateNodeThrottler = new Common.Throttler.Throttler(0);
|
|
122
|
+
this.#previousTarget = null;
|
|
76
123
|
UI.Context.Context.instance().addFlavorChangeListener(SDK.DOMModel.DOMNode, this.onSelectedNodeChanged, this);
|
|
77
124
|
}
|
|
78
125
|
|
|
@@ -82,29 +129,22 @@ export class ClassesPaneWidget extends UI.Widget.Widget {
|
|
|
82
129
|
.filter(className => className.length);
|
|
83
130
|
}
|
|
84
131
|
|
|
85
|
-
private
|
|
86
|
-
if (
|
|
132
|
+
private onCommit(): void {
|
|
133
|
+
if (this.#prompt.acceptAutoComplete()) {
|
|
87
134
|
return;
|
|
88
135
|
}
|
|
136
|
+
this.applyPromptText(this.#prompt.text());
|
|
137
|
+
}
|
|
89
138
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
const eventTarget = (event.target as HTMLElement);
|
|
98
|
-
let text = eventTarget.textContent;
|
|
99
|
-
if (Platform.KeyboardUtilities.isEscKey(event)) {
|
|
100
|
-
if (!Platform.StringUtilities.isWhitespace(text)) {
|
|
101
|
-
event.consume(true);
|
|
102
|
-
}
|
|
103
|
-
text = '';
|
|
104
|
-
}
|
|
139
|
+
/** Discards the prompt text and returns whether there was any non-whitespace text to discard. */
|
|
140
|
+
private onCancel(): boolean {
|
|
141
|
+
const hadText = !Platform.StringUtilities.isWhitespace(this.#prompt.text());
|
|
142
|
+
this.applyPromptText('');
|
|
143
|
+
return hadText;
|
|
144
|
+
}
|
|
105
145
|
|
|
106
|
-
|
|
107
|
-
|
|
146
|
+
private applyPromptText(text: string): void {
|
|
147
|
+
this.#prompt.setText('');
|
|
108
148
|
|
|
109
149
|
const node = UI.Context.Context.instance().flavor(SDK.DOMModel.DOMNode);
|
|
110
150
|
if (!node) {
|
|
@@ -128,7 +168,7 @@ export class ClassesPaneWidget extends UI.Widget.Widget {
|
|
|
128
168
|
UI.ARIAUtils.LiveAnnouncer.alert(announcementString);
|
|
129
169
|
|
|
130
170
|
this.installNodeClasses(node);
|
|
131
|
-
this.
|
|
171
|
+
this.requestUpdate();
|
|
132
172
|
}
|
|
133
173
|
|
|
134
174
|
private onTextChanged(): void {
|
|
@@ -141,65 +181,67 @@ export class ClassesPaneWidget extends UI.Widget.Widget {
|
|
|
141
181
|
|
|
142
182
|
private onDOMMutated(event: Common.EventTarget.EventTargetEvent<SDK.DOMModel.DOMNode>): void {
|
|
143
183
|
const node = event.data;
|
|
144
|
-
if (this
|
|
184
|
+
if (this.#mutatingNodes.has(node)) {
|
|
145
185
|
return;
|
|
146
186
|
}
|
|
147
187
|
cachedClassesMap.delete(node);
|
|
148
|
-
this.
|
|
188
|
+
this.requestUpdate();
|
|
149
189
|
}
|
|
150
190
|
|
|
151
191
|
private onSelectedNodeChanged(event: Common.EventTarget.EventTargetEvent<SDK.DOMModel.DOMNode|null>): void {
|
|
152
|
-
if (this
|
|
153
|
-
this.
|
|
154
|
-
this.installNodeClasses(this
|
|
192
|
+
if (this.#previousTarget && this.#promptAttached && this.#prompt.text()) {
|
|
193
|
+
this.#prompt.setText('');
|
|
194
|
+
this.installNodeClasses(this.#previousTarget);
|
|
155
195
|
}
|
|
156
|
-
this
|
|
157
|
-
this.
|
|
196
|
+
this.#previousTarget = event.data;
|
|
197
|
+
this.requestUpdate();
|
|
158
198
|
}
|
|
159
199
|
|
|
160
200
|
override wasShown(): void {
|
|
161
201
|
super.wasShown();
|
|
162
|
-
|
|
202
|
+
// Render synchronously rather than via requestUpdate(): callers such as
|
|
203
|
+
// StylesSidebarPane.showToolbarPane() focus the widget right after show(),
|
|
204
|
+
// so the autofocus prompt must already exist. This also re-reads the
|
|
205
|
+
// selected node, which may have changed while the pane was hidden.
|
|
206
|
+
this.performUpdate();
|
|
163
207
|
}
|
|
164
208
|
|
|
165
|
-
|
|
166
|
-
if (!this.isShowing()) {
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
|
|
209
|
+
override performUpdate(): void {
|
|
170
210
|
let node = UI.Context.Context.instance().flavor(SDK.DOMModel.DOMNode);
|
|
171
211
|
if (node) {
|
|
172
212
|
node = node.enclosingElementOrSelf();
|
|
173
213
|
}
|
|
174
214
|
|
|
175
|
-
this.
|
|
176
|
-
// @ts-expect-error this.input is a div, not an input element. So this line makes no sense at all
|
|
177
|
-
this.input.disabled = !node;
|
|
178
|
-
|
|
179
|
-
if (!node) {
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
const classes = this.nodeClasses(node);
|
|
215
|
+
const classes = node ? this.nodeClasses(node) : new Map<string, boolean>();
|
|
184
216
|
const keys = [...classes.keys()];
|
|
185
217
|
keys.sort(Platform.StringUtilities.caseInsensetiveComparator);
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
218
|
+
const input: ViewInput = {
|
|
219
|
+
classes: keys.map(className => ({className, enabled: Boolean(classes.get(className))})),
|
|
220
|
+
onCommit: this.onCommit.bind(this),
|
|
221
|
+
onCancel: this.onCancel.bind(this),
|
|
222
|
+
onClick: this.onClick.bind(this),
|
|
223
|
+
};
|
|
224
|
+
const output: ViewOutput = {};
|
|
225
|
+
this.#view(input, output, this.contentElement);
|
|
226
|
+
if (output.attachPrompt && !this.#promptAttached) {
|
|
227
|
+
output.attachPrompt(this.#prompt);
|
|
228
|
+
this.#prompt.setPlaceholder(i18nString(UIStrings.addNewClass));
|
|
229
|
+
this.#promptAttached = true;
|
|
230
|
+
}
|
|
231
|
+
if (this.#promptAttached) {
|
|
232
|
+
// Classes can only be added to a selected node.
|
|
233
|
+
this.#prompt.setEnabled(Boolean(node));
|
|
192
234
|
}
|
|
193
235
|
}
|
|
194
236
|
|
|
195
|
-
private onClick(className: string,
|
|
237
|
+
private onClick(className: string, enabled: boolean): void {
|
|
196
238
|
const node = UI.Context.Context.instance().flavor(SDK.DOMModel.DOMNode);
|
|
197
239
|
if (!node) {
|
|
198
240
|
return;
|
|
199
241
|
}
|
|
200
|
-
const enabled = (event.target as HTMLInputElement).checked;
|
|
201
242
|
this.toggleClass(node, className, enabled);
|
|
202
243
|
this.installNodeClasses(node);
|
|
244
|
+
this.requestUpdate();
|
|
203
245
|
}
|
|
204
246
|
|
|
205
247
|
private nodeClasses(node: SDK.DOMModel.DOMNode): Map<string, boolean> {
|
|
@@ -235,30 +277,30 @@ export class ClassesPaneWidget extends UI.Widget.Widget {
|
|
|
235
277
|
}
|
|
236
278
|
}
|
|
237
279
|
|
|
238
|
-
const additionalClasses = this.splitTextIntoClasses(this
|
|
280
|
+
const additionalClasses = this.splitTextIntoClasses(this.#prompt.textWithCurrentSuggestion());
|
|
239
281
|
for (const className of additionalClasses) {
|
|
240
282
|
activeClasses.add(className);
|
|
241
283
|
}
|
|
242
284
|
|
|
243
285
|
const newClasses = [...activeClasses.values()].sort();
|
|
244
286
|
|
|
245
|
-
this
|
|
246
|
-
void this
|
|
287
|
+
this.#pendingNodeClasses.set(node, newClasses.join(' '));
|
|
288
|
+
void this.#updateNodeThrottler.schedule(this.flushPendingClasses.bind(this));
|
|
247
289
|
}
|
|
248
290
|
|
|
249
291
|
private async flushPendingClasses(): Promise<void> {
|
|
250
292
|
const promises = [];
|
|
251
|
-
for (const node of this
|
|
252
|
-
this
|
|
253
|
-
const promise = node.setAttributeValuePromise('class', (this
|
|
293
|
+
for (const node of this.#pendingNodeClasses.keys()) {
|
|
294
|
+
this.#mutatingNodes.add(node);
|
|
295
|
+
const promise = node.setAttributeValuePromise('class', (this.#pendingNodeClasses.get(node) as string))
|
|
254
296
|
.then(onClassValueUpdated.bind(this, node));
|
|
255
297
|
promises.push(promise);
|
|
256
298
|
}
|
|
257
|
-
this
|
|
299
|
+
this.#pendingNodeClasses.clear();
|
|
258
300
|
await Promise.all(promises);
|
|
259
301
|
|
|
260
302
|
function onClassValueUpdated(this: ClassesPaneWidget, node: SDK.DOMModel.DOMNode): void {
|
|
261
|
-
this
|
|
303
|
+
this.#mutatingNodes.delete(node);
|
|
262
304
|
}
|
|
263
305
|
}
|
|
264
306
|
}
|
|
@@ -21,11 +21,6 @@ const UIStrings = {
|
|
|
21
21
|
* allows the user to edit cubic-bezier CSS properties directly.
|
|
22
22
|
*/
|
|
23
23
|
openCubicBezierEditor: 'Open cubic bezier editor',
|
|
24
|
-
/**
|
|
25
|
-
* @description Tooltip text for an icon that opens the shadow editor. The shadow editor is a tool
|
|
26
|
-
* which allows the user to edit CSS shadow properties.
|
|
27
|
-
*/
|
|
28
|
-
openShadowEditor: 'Open shadow editor',
|
|
29
24
|
} as const;
|
|
30
25
|
const str_ = i18n.i18n.registerUIStrings('panels/elements/ColorSwatchPopoverIcon.ts', UIStrings);
|
|
31
26
|
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
|
@@ -321,56 +316,35 @@ export interface ShadowEventTypes {
|
|
|
321
316
|
export class ShadowSwatchPopoverHelper extends Common.ObjectWrapper.ObjectWrapper<ShadowEventTypes> {
|
|
322
317
|
private treeElement: StylePropertyTreeElement;
|
|
323
318
|
private readonly swatchPopoverHelper: InlineEditor.SwatchPopoverHelper.SwatchPopoverHelper;
|
|
324
|
-
private readonly shadowSwatch: InlineEditor.Swatches.CSSShadowSwatch;
|
|
325
|
-
private iconElement: HTMLSpanElement;
|
|
326
319
|
private readonly boundShadowChanged:
|
|
327
320
|
(event: Common.EventTarget.EventTargetEvent<InlineEditor.CSSShadowEditor.CSSShadowModel>) => void;
|
|
328
321
|
private readonly boundOnScroll: (event: Event) => void;
|
|
329
322
|
private cssShadowEditor?: InlineEditor.CSSShadowEditor.CSSShadowEditor;
|
|
330
323
|
private scrollerElement?: Element;
|
|
331
324
|
private originalPropertyText?: string|null;
|
|
332
|
-
constructor(
|
|
333
|
-
|
|
334
|
-
shadowSwatch: InlineEditor.Swatches.CSSShadowSwatch) {
|
|
325
|
+
constructor(treeElement: StylePropertyTreeElement,
|
|
326
|
+
swatchPopoverHelper: InlineEditor.SwatchPopoverHelper.SwatchPopoverHelper) {
|
|
335
327
|
super();
|
|
336
328
|
this.treeElement = treeElement;
|
|
337
329
|
this.swatchPopoverHelper = swatchPopoverHelper;
|
|
338
|
-
this.shadowSwatch = shadowSwatch;
|
|
339
|
-
this.iconElement = shadowSwatch.iconElement();
|
|
340
|
-
|
|
341
|
-
UI.Tooltip.Tooltip.install(this.iconElement, i18nString(UIStrings.openShadowEditor));
|
|
342
|
-
this.iconElement.addEventListener('click', this.iconClick.bind(this), false);
|
|
343
|
-
this.iconElement.addEventListener('keydown', this.keyDown.bind(this), false);
|
|
344
|
-
this.iconElement.addEventListener('mousedown', event => event.consume(), false);
|
|
345
330
|
|
|
346
331
|
this.boundShadowChanged = this.shadowChanged.bind(this);
|
|
347
332
|
this.boundOnScroll = this.onScroll.bind(this);
|
|
348
333
|
}
|
|
349
334
|
|
|
350
|
-
|
|
351
|
-
if (Platform.KeyboardUtilities.isEnterOrSpaceKey(event)) {
|
|
352
|
-
event.consume(true);
|
|
353
|
-
this.showPopover();
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
private iconClick(event: Event): void {
|
|
358
|
-
event.consume(true);
|
|
359
|
-
this.showPopover();
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
showPopover(): void {
|
|
335
|
+
showPopover(shadowSwatch: InlineEditor.Swatches.CSSShadowSwatch): void {
|
|
363
336
|
if (this.swatchPopoverHelper.isShowing()) {
|
|
364
337
|
this.swatchPopoverHelper.hide(true);
|
|
365
338
|
return;
|
|
366
339
|
}
|
|
367
340
|
|
|
341
|
+
const iconElement = shadowSwatch.iconElement();
|
|
368
342
|
this.cssShadowEditor = new InlineEditor.CSSShadowEditor.CSSShadowEditor();
|
|
369
343
|
this.cssShadowEditor.element.classList.toggle('with-padding', true);
|
|
370
|
-
this.cssShadowEditor.setModel(
|
|
344
|
+
this.cssShadowEditor.setModel(shadowSwatch.model());
|
|
371
345
|
this.cssShadowEditor.addEventListener(InlineEditor.CSSShadowEditor.Events.SHADOW_CHANGED, this.boundShadowChanged);
|
|
372
|
-
this.swatchPopoverHelper.show(this.cssShadowEditor,
|
|
373
|
-
this.scrollerElement =
|
|
346
|
+
this.swatchPopoverHelper.show(this.cssShadowEditor, iconElement, this.onPopoverHidden.bind(this));
|
|
347
|
+
this.scrollerElement = iconElement.enclosingNodeOrSelfWithClass('style-panes-wrapper');
|
|
374
348
|
if (this.scrollerElement) {
|
|
375
349
|
this.scrollerElement.addEventListener('scroll', this.boundOnScroll, false);
|
|
376
350
|
}
|
|
@@ -120,7 +120,7 @@ interface ViewInput {
|
|
|
120
120
|
selectedClosingTag: boolean;
|
|
121
121
|
|
|
122
122
|
onSelect: (node: SDK.DOMModel.DOMNode, isClosingTag: boolean, selectedByUser: boolean) => void;
|
|
123
|
-
onExpand: (node: SDK.DOMModel.DOMNode, expanded: boolean) => void;
|
|
123
|
+
onExpand: (node: SDK.DOMModel.DOMNode, expanded: boolean, recursively?: boolean) => void;
|
|
124
124
|
onContextMenu: (node: SDK.DOMModel.DOMNode, event: MouseEvent) => void;
|
|
125
125
|
onClearMaxRows: () => void;
|
|
126
126
|
onHoverNode: (node: SDK.DOMModel.DOMNode|null, showInfo: boolean, isClosingTag: boolean) => void;
|
|
@@ -659,7 +659,7 @@ export const DEFAULT_VIEW: View = (input: ViewInput, _output: undefined, target:
|
|
|
659
659
|
if (!isCollapsible) {
|
|
660
660
|
return;
|
|
661
661
|
}
|
|
662
|
-
input.onExpand(node, event.detail.expanded);
|
|
662
|
+
input.onExpand(node, event.detail.expanded, event.detail.recursively);
|
|
663
663
|
};
|
|
664
664
|
|
|
665
665
|
const isDragOver = input.dragOverNode?.node === node && !input.dragOverNode.isClosingTag;
|
|
@@ -1789,7 +1789,11 @@ export class DOMTreeWidget extends UI.Widget.Widget {
|
|
|
1789
1789
|
node.highlight();
|
|
1790
1790
|
Host.userMetrics.actionTaken(Host.UserMetrics.Action.ChangeInspectedNodeInElementsPanel);
|
|
1791
1791
|
},
|
|
1792
|
-
onExpand: (node: SDK.DOMModel.DOMNode, expanded: boolean) => {
|
|
1792
|
+
onExpand: (node: SDK.DOMModel.DOMNode, expanded: boolean, recursively = false) => {
|
|
1793
|
+
if (expanded && recursively) {
|
|
1794
|
+
void this.expandRecursively(node);
|
|
1795
|
+
return;
|
|
1796
|
+
}
|
|
1793
1797
|
this.setNodeExpanded(node, expanded);
|
|
1794
1798
|
},
|
|
1795
1799
|
onContextMenu: (node: SDK.DOMModel.DOMNode, event: MouseEvent) => {
|
|
@@ -69,6 +69,8 @@ enum SpecificPseudoStates {
|
|
|
69
69
|
AUTOFILL = 'autofill',
|
|
70
70
|
OPEN = 'open',
|
|
71
71
|
TARGET_CURRENT = 'target-current',
|
|
72
|
+
BUFFERING = 'buffering',
|
|
73
|
+
STALLED = 'stalled',
|
|
72
74
|
}
|
|
73
75
|
|
|
74
76
|
interface ElementState {
|
|
@@ -92,7 +94,7 @@ export const DEFAULT_VIEW: View = (input, _output, target) => {
|
|
|
92
94
|
return html`
|
|
93
95
|
<div id=${state.state}>
|
|
94
96
|
<devtools-checkbox class="small" @click=${input.onStateCheckboxClicked}
|
|
95
|
-
jslog=${VisualLogging.toggle(state.state).track({change: true})}
|
|
97
|
+
jslog=${VisualLogging.toggle(state.state).track({change: true})} .checked=${Boolean(state.checked)} ?disabled=${state.disabled}
|
|
96
98
|
title=${':' + state.state}>
|
|
97
99
|
<span class="source-code">${':' + state.state}</span>
|
|
98
100
|
</devtools-checkbox>
|
|
@@ -184,6 +186,8 @@ export class ElementStatePaneWidget extends UI.Widget.Widget {
|
|
|
184
186
|
this.#states.set(SpecificPseudoStates.OPEN, {state: SpecificPseudoStates.OPEN, type: 'specific'});
|
|
185
187
|
this.#states.set(
|
|
186
188
|
SpecificPseudoStates.TARGET_CURRENT, {state: SpecificPseudoStates.TARGET_CURRENT, type: 'specific'});
|
|
189
|
+
this.#states.set(SpecificPseudoStates.BUFFERING, {state: SpecificPseudoStates.BUFFERING, type: 'specific'});
|
|
190
|
+
this.#states.set(SpecificPseudoStates.STALLED, {state: SpecificPseudoStates.STALLED, type: 'specific'});
|
|
187
191
|
|
|
188
192
|
setDualStateCheckboxes(SpecificPseudoStates.VALID, SpecificPseudoStates.INVALID);
|
|
189
193
|
setDualStateCheckboxes(SpecificPseudoStates.USER_VALID, SpecificPseudoStates.USER_INVALID);
|
|
@@ -399,6 +403,12 @@ export class ElementStatePaneWidget extends UI.Widget.Widget {
|
|
|
399
403
|
} else {
|
|
400
404
|
hideSpecificCheckbox(SpecificPseudoStates.TARGET_CURRENT, true);
|
|
401
405
|
}
|
|
406
|
+
|
|
407
|
+
// :buffering and :stalled are independent. Forcing :stalled does not force
|
|
408
|
+
// :buffering, even though a real stalled media element is also buffering.
|
|
409
|
+
const isMediaElement = isElementOfTypes(node, ['audio', 'video']);
|
|
410
|
+
hideSpecificCheckbox(SpecificPseudoStates.BUFFERING, !isMediaElement);
|
|
411
|
+
hideSpecificCheckbox(SpecificPseudoStates.STALLED, !isMediaElement);
|
|
402
412
|
}
|
|
403
413
|
}
|
|
404
414
|
|
|
@@ -324,8 +324,9 @@ export class TracingContext {
|
|
|
324
324
|
// evaluation callback is run. The callback should return synchronously an array of Nodes as placeholder to be
|
|
325
325
|
// rendered immediately and optionally a callback for asynchronous updates of the placeholder nodes. The callback
|
|
326
326
|
// returns a boolean indicating whether the update was successful or not.
|
|
327
|
-
applyEvaluation
|
|
328
|
-
|
|
327
|
+
applyEvaluation<T extends Node[]|LitTemplate = Node[]>(
|
|
328
|
+
children: TracingContext[], evaluation: () => ({placeholder: T, asyncEvalCallback?: () => Promise<boolean>})): T
|
|
329
|
+
|null {
|
|
329
330
|
if (this.#evaluationCount === 0 || children.some(child => child.#appliedEvaluations >= this.#evaluationCount)) {
|
|
330
331
|
this.#setHasMoreEvaluations(true);
|
|
331
332
|
children.forEach(child => this.#asyncEvalCallbacks.push(...child.#asyncEvalCallbacks));
|
|
@@ -3,15 +3,19 @@
|
|
|
3
3
|
// found in the LICENSE file.
|
|
4
4
|
/* eslint-disable @devtools/no-imperative-dom-api */
|
|
5
5
|
|
|
6
|
+
import '../../ui/kit/kit.js';
|
|
7
|
+
|
|
6
8
|
import * as Elements from '../../models/elements/elements.js';
|
|
7
|
-
import {createIcon} from '../../ui/kit/kit.js';
|
|
8
9
|
import * as UI from '../../ui/legacy/legacy.js';
|
|
10
|
+
import {Directives, html, type LitTemplate} from '../../ui/lit/lit.js';
|
|
11
|
+
import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
|
|
9
12
|
|
|
10
13
|
import type * as ElementsComponents from './components/components.js';
|
|
11
14
|
import type {StylePropertiesSection} from './StylePropertiesSection.js';
|
|
12
15
|
import {StylePropertyTreeElement} from './StylePropertyTreeElement.js';
|
|
13
16
|
import type {StylesContainer} from './StylesContainer.js';
|
|
14
17
|
|
|
18
|
+
const {ref} = Directives;
|
|
15
19
|
type PropertySelectedEvent = ElementsComponents.StylePropertyEditor.PropertySelectedEvent;
|
|
16
20
|
type PropertyDeselectedEvent = ElementsComponents.StylePropertyEditor.PropertyDeselectedEvent;
|
|
17
21
|
|
|
@@ -118,7 +122,6 @@ export class StyleEditorWidget extends UI.Widget.VBox {
|
|
|
118
122
|
});
|
|
119
123
|
}
|
|
120
124
|
}
|
|
121
|
-
|
|
122
125
|
async render(): Promise<void> {
|
|
123
126
|
if (!this.editor) {
|
|
124
127
|
return;
|
|
@@ -150,47 +153,63 @@ export class StyleEditorWidget extends UI.Widget.VBox {
|
|
|
150
153
|
}
|
|
151
154
|
}
|
|
152
155
|
|
|
153
|
-
static createTriggerButton(
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
const
|
|
157
|
-
triggerButton
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
156
|
+
static createTriggerButton(stylesContainer: StylesContainer, section: StylePropertiesSection,
|
|
157
|
+
editorClass: {new(): Editor}, buttonTitle: string, triggerKey: string,
|
|
158
|
+
jslogContext: string, onClick?: () => void): LitTemplate {
|
|
159
|
+
const popoverHelper = stylesContainer.swatchPopoverHelper();
|
|
160
|
+
let triggerButton: Element|undefined;
|
|
161
|
+
// clang-format off
|
|
162
|
+
return html`<devtools-icon
|
|
163
|
+
${ref(e => {
|
|
164
|
+
triggerButton = e;
|
|
165
|
+
if (e && popoverHelper.isShowing(StyleEditorWidget.instance()) &&
|
|
166
|
+
StyleEditorWidget.instance().getTriggerKey() === triggerKey) {
|
|
167
|
+
popoverHelper.setAnchorElement(e);
|
|
168
|
+
}
|
|
169
|
+
})}
|
|
170
|
+
name="flex-wrap"
|
|
171
|
+
class="styles-pane-button"
|
|
172
|
+
title=${buttonTitle}
|
|
173
|
+
role="button"
|
|
174
|
+
tabindex="-1"
|
|
175
|
+
jslog=${VisualLogging.showStyleEditor(jslogContext).track({click: true})}
|
|
176
|
+
@click=${async (event: Event) => {
|
|
177
|
+
event.stopPropagation();
|
|
178
|
+
onClick?.();
|
|
179
|
+
if (!triggerButton) {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
const widget = StyleEditorWidget.instance();
|
|
183
|
+
widget.element.classList.toggle('with-padding', true);
|
|
184
|
+
widget.setEditor(editorClass);
|
|
185
|
+
widget.bindContext(stylesContainer, section);
|
|
186
|
+
widget.setTriggerKey(triggerKey);
|
|
187
|
+
await widget.render();
|
|
188
|
+
widget.focus();
|
|
189
|
+
const scrollerElement = triggerButton.enclosingNodeOrSelfWithClass('style-panes-wrapper');
|
|
190
|
+
const onScroll = (): void => {
|
|
191
|
+
popoverHelper.hide(true);
|
|
192
|
+
};
|
|
193
|
+
const onStylesUpdateCompleted = widget.requestUpdate.bind(widget);
|
|
194
|
+
stylesContainer.addStyleUpdateListener(onStylesUpdateCompleted);
|
|
195
|
+
popoverHelper.show(widget, triggerButton, () => {
|
|
196
|
+
widget.#trackChanges();
|
|
197
|
+
widget.unbindContext();
|
|
198
|
+
if (scrollerElement) {
|
|
199
|
+
scrollerElement.removeEventListener('scroll', onScroll);
|
|
200
|
+
}
|
|
201
|
+
stylesContainer.removeStyleUpdateListener(onStylesUpdateCompleted);
|
|
202
|
+
});
|
|
179
203
|
if (scrollerElement) {
|
|
180
|
-
scrollerElement.
|
|
204
|
+
scrollerElement.addEventListener('scroll', onScroll);
|
|
181
205
|
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
// Stop propagation to prevent the property editor from being activated.
|
|
190
|
-
event.stopPropagation();
|
|
191
|
-
};
|
|
192
|
-
|
|
193
|
-
return triggerButton;
|
|
206
|
+
}}
|
|
207
|
+
@mouseup=${(event: Event) => {
|
|
208
|
+
// Stop propagation to prevent the property editor from being activated.
|
|
209
|
+
event.stopPropagation();
|
|
210
|
+
}}
|
|
211
|
+
></devtools-icon>`;
|
|
212
|
+
// clang-format on
|
|
194
213
|
}
|
|
195
214
|
}
|
|
196
215
|
|