blob: 3c2464b3b00d750e3c238609e7bc3747ad356990 [file] [log] [blame]
Blink Reformat4c46d092018-04-07 15:32:371// Copyright (c) 2015 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
Tim van der Lippe97611c92020-02-12 16:56:584
Tim van der Lippe76961572021-04-06 10:48:075import * as Common from '../../core/common/common.js';
Tim van der Lippebb352e62021-04-01 17:57:286import * as i18n from '../../core/i18n/i18n.js';
Tim van der Lippeaa1ed7a2021-03-31 14:38:277import * as Platform from '../../core/platform/platform.js';
Tim van der Lippee00b92f2021-03-31 16:52:178import * as SDK from '../../core/sdk/sdk.js';
Tim van der Lippeaa61faf2021-04-07 15:32:079import * as UI from '../../ui/legacy/legacy.js';
Danil Somsikov8a65be02023-10-17 08:36:5810import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
Tim van der Lippe97611c92020-02-12 16:56:5811
Kriti Sapra40ca8da2021-07-20 09:33:5912import classesPaneWidgetStyles from './classesPaneWidget.css.js';
Tim van der Lippeaabc8302019-12-10 15:34:4513import {ElementsPanel} from './ElementsPanel.js';
14
Simon Zündfbfd1072021-03-01 07:38:5315const UIStrings = {
Vidal Guillermo Diazleal Ortega0cfa0ed2021-02-17 20:35:4916 /**
Jack Franklinfd72c072022-12-21 11:45:0117 * @description Prompt text for a text field in the Classes Pane Widget of the Elements panel.
18 * Class refers to a CSS class.
19 */
Vidal Guillermo Diazleal Ortega0cfa0ed2021-02-17 20:35:4920 addNewClass: 'Add new class',
21 /**
Jack Franklinfd72c072022-12-21 11:45:0122 * @description Screen reader announcement string when adding a CSS class via the Classes Pane Widget.
23 * @example {vbox flex-auto} PH1
24 */
Peter Marshallcc995412021-02-19 08:07:4325 classesSAdded: 'Classes {PH1} added',
Vidal Guillermo Diazleal Ortega0cfa0ed2021-02-17 20:35:4926 /**
Jack Franklinfd72c072022-12-21 11:45:0127 * @description Screen reader announcement string when adding a class via the Classes Pane Widget.
28 * @example {title-container} PH1
29 */
Peter Marshallcc995412021-02-19 08:07:4330 classSAdded: 'Class {PH1} added',
Vidal Guillermo Diazleal Ortega0cfa0ed2021-02-17 20:35:4931 /**
Jack Franklinfd72c072022-12-21 11:45:0132 * @description Accessible title read by screen readers for the Classes Pane Widget of the Elements
33 * panel. Element is a HTML DOM Element and classes refers to CSS classes.
34 */
Vidal Guillermo Diazleal Ortega0cfa0ed2021-02-17 20:35:4935 elementClasses: 'Element Classes',
36};
Jan Scheffler29905852021-04-14 11:30:3137const str_ = i18n.i18n.registerUIStrings('panels/elements/ClassesPaneWidget.ts', UIStrings);
Vidal Guillermo Diazleal Ortega0cfa0ed2021-02-17 20:35:4938const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
Tim van der Lippe97611c92020-02-12 16:56:5839export class ClassesPaneWidget extends UI.Widget.Widget {
Jan Scheffler77646c72021-08-13 18:40:3840 private input: HTMLElement;
41 private readonly classesContainer: HTMLElement;
42 private readonly prompt: ClassNamePrompt;
43 private readonly mutatingNodes: Set<SDK.DOMModel.DOMNode>;
44 private readonly pendingNodeClasses: Map<SDK.DOMModel.DOMNode, string>;
45 private readonly updateNodeThrottler: Common.Throttler.Throttler;
46 private previousTarget: SDK.DOMModel.DOMNode|null;
Jan Scheffler29905852021-04-14 11:30:3147
Blink Reformat4c46d092018-04-07 15:32:3748 constructor() {
49 super(true);
Blink Reformat4c46d092018-04-07 15:32:3750 this.contentElement.className = 'styles-element-classes-pane';
Danil Somsikov0cb97462024-01-30 15:11:2951 this.contentElement.setAttribute('jslog', `${VisualLogging.pane('elements-classes')}`);
Blink Reformat4c46d092018-04-07 15:32:3752 const container = this.contentElement.createChild('div', 'title-container');
Jan Scheffler77646c72021-08-13 18:40:3853 this.input = container.createChild('div', 'new-class-input monospace');
54 this.setDefaultFocusedElement(this.input);
55 this.classesContainer = this.contentElement.createChild('div', 'source-code');
56 this.classesContainer.classList.add('styles-element-classes-container');
57 this.prompt = new ClassNamePrompt(this.nodeClasses.bind(this));
58 this.prompt.setAutocompletionTimeout(0);
59 this.prompt.renderAsBlock();
Blink Reformat4c46d092018-04-07 15:32:3760
Jan Scheffler77646c72021-08-13 18:40:3861 const proxyElement = (this.prompt.attach(this.input) as HTMLElement);
62 this.prompt.setPlaceholder(i18nString(UIStrings.addNewClass));
63 this.prompt.addEventListener(UI.TextPrompt.Events.TextChanged, this.onTextChanged, this);
64 proxyElement.addEventListener('keydown', this.onKeyDown.bind(this), false);
Blink Reformat4c46d092018-04-07 15:32:3765
Sigurd Schneiderb9f6c792021-05-31 10:57:2466 SDK.TargetManager.TargetManager.instance().addModelListener(
Danil Somsikov29edf372023-03-03 16:18:5967 SDK.DOMModel.DOMModel, SDK.DOMModel.Events.DOMMutated, this.onDOMMutated, this, {scoped: true});
Jan Scheffler77646c72021-08-13 18:40:3868 this.mutatingNodes = new Set();
69 this.pendingNodeClasses = new Map();
70 this.updateNodeThrottler = new Common.Throttler.Throttler(0);
71 this.previousTarget = null;
72 UI.Context.Context.instance().addFlavorChangeListener(SDK.DOMModel.DOMNode, this.onSelectedNodeChanged, this);
Blink Reformat4c46d092018-04-07 15:32:3773 }
74
Jan Scheffler77646c72021-08-13 18:40:3875 private splitTextIntoClasses(text: string): string[] {
Mathias Bynens3abc0952020-04-20 14:15:5276 return text.split(/[,\s]/).map(className => className.trim()).filter(className => className.length);
Blink Reformat4c46d092018-04-07 15:32:3777 }
78
Jan Scheffler77646c72021-08-13 18:40:3879 private onKeyDown(event: KeyboardEvent): void {
Jack Frankline24bc6f2022-10-07 10:36:1880 if (!(event.key === 'Enter') && !Platform.KeyboardUtilities.isEscKey(event)) {
Blink Reformat4c46d092018-04-07 15:32:3781 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:3482 }
Blink Reformat4c46d092018-04-07 15:32:3783
Tim van der Lippebcd6b5c2021-01-13 12:31:5184 if (event.key === 'Enter') {
Blink Reformat4c46d092018-04-07 15:32:3785 event.consume();
Jan Scheffler77646c72021-08-13 18:40:3886 if (this.prompt.acceptAutoComplete()) {
Blink Reformat4c46d092018-04-07 15:32:3787 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:3488 }
Blink Reformat4c46d092018-04-07 15:32:3789 }
90
Jan Scheffler29905852021-04-14 11:30:3191 const eventTarget = (event.target as HTMLElement);
92 let text: ''|string = (eventTarget.textContent as string);
Jack Frankline24bc6f2022-10-07 10:36:1893 if (Platform.KeyboardUtilities.isEscKey(event)) {
Simon Zündda7058f2020-02-28 13:57:2894 if (!Platform.StringUtilities.isWhitespace(text)) {
Blink Reformat4c46d092018-04-07 15:32:3795 event.consume(true);
Tim van der Lippe1d6e57a2019-09-30 11:55:3496 }
Blink Reformat4c46d092018-04-07 15:32:3797 text = '';
98 }
99
Jan Scheffler77646c72021-08-13 18:40:38100 this.prompt.clearAutocomplete();
Tim van der Lippe32f760f2020-10-01 10:52:15101 eventTarget.textContent = '';
Blink Reformat4c46d092018-04-07 15:32:37102
Tim van der Lipped1a00aa2020-08-19 16:03:56103 const node = UI.Context.Context.instance().flavor(SDK.DOMModel.DOMNode);
Tim van der Lippe1d6e57a2019-09-30 11:55:34104 if (!node) {
Blink Reformat4c46d092018-04-07 15:32:37105 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34106 }
Blink Reformat4c46d092018-04-07 15:32:37107
Jan Scheffler77646c72021-08-13 18:40:38108 const classNames = this.splitTextIntoClasses(text);
Michael Liaocafccfd2020-04-02 17:11:59109 if (!classNames.length) {
Jan Scheffler77646c72021-08-13 18:40:38110 this.installNodeClasses(node);
Michael Liaocafccfd2020-04-02 17:11:59111 return;
112 }
113
Tim van der Lippe1d6e57a2019-09-30 11:55:34114 for (const className of classNames) {
Jan Scheffler77646c72021-08-13 18:40:38115 this.toggleClass(node, className, true);
Tim van der Lippe1d6e57a2019-09-30 11:55:34116 }
Michael Liaocafccfd2020-04-02 17:11:59117
118 // annoucementString is used for screen reader to announce that the class(es) has been added successfully.
119 const joinClassString = classNames.join(' ');
Vidal Guillermo Diazleal Ortega0cfa0ed2021-02-17 20:35:49120 const announcementString = classNames.length > 1 ? i18nString(UIStrings.classesSAdded, {PH1: joinClassString}) :
121 i18nString(UIStrings.classSAdded, {PH1: joinClassString});
Michael Liao7322dee2021-04-07 18:33:30122 UI.ARIAUtils.alert(announcementString);
Michael Liaocafccfd2020-04-02 17:11:59123
Jan Scheffler77646c72021-08-13 18:40:38124 this.installNodeClasses(node);
125 this.update();
Blink Reformat4c46d092018-04-07 15:32:37126 }
127
Jan Scheffler77646c72021-08-13 18:40:38128 private onTextChanged(): void {
Tim van der Lipped1a00aa2020-08-19 16:03:56129 const node = UI.Context.Context.instance().flavor(SDK.DOMModel.DOMNode);
Tim van der Lippe1d6e57a2019-09-30 11:55:34130 if (!node) {
Blink Reformat4c46d092018-04-07 15:32:37131 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34132 }
Jan Scheffler77646c72021-08-13 18:40:38133 this.installNodeClasses(node);
Blink Reformat4c46d092018-04-07 15:32:37134 }
135
Jan Scheffler77646c72021-08-13 18:40:38136 private onDOMMutated(event: Common.EventTarget.EventTargetEvent<SDK.DOMModel.DOMNode>): void {
Simon Zünd37cf1f52021-07-30 09:53:35137 const node = event.data;
Jan Scheffler77646c72021-08-13 18:40:38138 if (this.mutatingNodes.has(node)) {
Blink Reformat4c46d092018-04-07 15:32:37139 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34140 }
Tim van der Lippe32f760f2020-10-01 10:52:15141 cachedClassesMap.delete(node);
Jan Scheffler77646c72021-08-13 18:40:38142 this.update();
Blink Reformat4c46d092018-04-07 15:32:37143 }
144
Simon Zündf0aa2b42021-09-20 09:18:02145 private onSelectedNodeChanged(event: Common.EventTarget.EventTargetEvent<SDK.DOMModel.DOMNode|null>): void {
Jan Scheffler77646c72021-08-13 18:40:38146 if (this.previousTarget && this.prompt.text()) {
147 this.input.textContent = '';
148 this.installNodeClasses(this.previousTarget);
Blink Reformat4c46d092018-04-07 15:32:37149 }
Simon Zündf0aa2b42021-09-20 09:18:02150 this.previousTarget = event.data;
Jan Scheffler77646c72021-08-13 18:40:38151 this.update();
Blink Reformat4c46d092018-04-07 15:32:37152 }
153
Randolf Jungffd14242023-04-19 00:32:25154 override wasShown(): void {
Kriti Saprab2b29f22021-06-29 12:59:56155 super.wasShown();
Jan Scheffler77646c72021-08-13 18:40:38156 this.update();
Kriti Sapra40ca8da2021-07-20 09:33:59157 this.registerCSSFiles([classesPaneWidgetStyles]);
Blink Reformat4c46d092018-04-07 15:32:37158 }
159
Jan Scheffler77646c72021-08-13 18:40:38160 private update(): void {
Tim van der Lippe1d6e57a2019-09-30 11:55:34161 if (!this.isShowing()) {
Blink Reformat4c46d092018-04-07 15:32:37162 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34163 }
Blink Reformat4c46d092018-04-07 15:32:37164
Tim van der Lipped1a00aa2020-08-19 16:03:56165 let node = UI.Context.Context.instance().flavor(SDK.DOMModel.DOMNode);
Tim van der Lippe1d6e57a2019-09-30 11:55:34166 if (node) {
Blink Reformat4c46d092018-04-07 15:32:37167 node = node.enclosingElementOrSelf();
Tim van der Lippe1d6e57a2019-09-30 11:55:34168 }
Blink Reformat4c46d092018-04-07 15:32:37169
Jan Scheffler77646c72021-08-13 18:40:38170 this.classesContainer.removeChildren();
Jan Schefflera222c632021-08-13 12:46:15171 // @ts-ignore this.input is a div, not an input element. So this line makes no sense at all
Jan Scheffler77646c72021-08-13 18:40:38172 this.input.disabled = !node;
Blink Reformat4c46d092018-04-07 15:32:37173
Tim van der Lippe1d6e57a2019-09-30 11:55:34174 if (!node) {
Blink Reformat4c46d092018-04-07 15:32:37175 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34176 }
Blink Reformat4c46d092018-04-07 15:32:37177
Jan Scheffler77646c72021-08-13 18:40:38178 const classes = this.nodeClasses(node);
Simon Zündf27be3d2020-02-11 14:46:27179 const keys = [...classes.keys()];
Tim van der Lippe32f760f2020-10-01 10:52:15180 keys.sort(Platform.StringUtilities.caseInsensetiveComparator);
181 for (const className of keys) {
Kateryna Prokopenkof8956062024-06-19 08:30:37182 const label =
183 UI.UIUtils.CheckboxLabel.create(className, classes.get(className), undefined, 'element-class', true);
Blink Reformat4c46d092018-04-07 15:32:37184 label.classList.add('monospace');
Jan Scheffler77646c72021-08-13 18:40:38185 label.checkboxElement.addEventListener('click', this.onClick.bind(this, className), false);
186 this.classesContainer.appendChild(label);
Blink Reformat4c46d092018-04-07 15:32:37187 }
188 }
189
Jan Scheffler77646c72021-08-13 18:40:38190 private onClick(className: string, event: Event): void {
Tim van der Lipped1a00aa2020-08-19 16:03:56191 const node = UI.Context.Context.instance().flavor(SDK.DOMModel.DOMNode);
Tim van der Lippe1d6e57a2019-09-30 11:55:34192 if (!node) {
Blink Reformat4c46d092018-04-07 15:32:37193 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34194 }
Jan Scheffler29905852021-04-14 11:30:31195 const enabled = (event.target as HTMLInputElement).checked;
Jan Scheffler77646c72021-08-13 18:40:38196 this.toggleClass(node, className, enabled);
197 this.installNodeClasses(node);
Blink Reformat4c46d092018-04-07 15:32:37198 }
199
Jan Scheffler77646c72021-08-13 18:40:38200 private nodeClasses(node: SDK.DOMModel.DOMNode): Map<string, boolean> {
Tim van der Lippe32f760f2020-10-01 10:52:15201 let result = cachedClassesMap.get(node);
Blink Reformat4c46d092018-04-07 15:32:37202 if (!result) {
203 const classAttribute = node.getAttribute('class') || '';
204 const classes = classAttribute.split(/\s/);
205 result = new Map();
206 for (let i = 0; i < classes.length; ++i) {
207 const className = classes[i].trim();
Tim van der Lippe1d6e57a2019-09-30 11:55:34208 if (!className.length) {
Blink Reformat4c46d092018-04-07 15:32:37209 continue;
Tim van der Lippe1d6e57a2019-09-30 11:55:34210 }
Blink Reformat4c46d092018-04-07 15:32:37211 result.set(className, true);
212 }
Tim van der Lippe32f760f2020-10-01 10:52:15213 cachedClassesMap.set(node, result);
Blink Reformat4c46d092018-04-07 15:32:37214 }
215 return result;
216 }
217
Jan Scheffler77646c72021-08-13 18:40:38218 private toggleClass(node: SDK.DOMModel.DOMNode, className: string, enabled: boolean): void {
219 const classes = this.nodeClasses(node);
Blink Reformat4c46d092018-04-07 15:32:37220 classes.set(className, enabled);
Kateryna Prokopenko17cba892024-07-26 13:33:56221 ButtonProvider.instance().item().setChecked([...classes.values()].includes(true));
Blink Reformat4c46d092018-04-07 15:32:37222 }
223
Jan Scheffler77646c72021-08-13 18:40:38224 private installNodeClasses(node: SDK.DOMModel.DOMNode): void {
225 const classes = this.nodeClasses(node);
Jan Scheffler29905852021-04-14 11:30:31226 const activeClasses = new Set<string>();
Blink Reformat4c46d092018-04-07 15:32:37227 for (const className of classes.keys()) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34228 if (classes.get(className)) {
Blink Reformat4c46d092018-04-07 15:32:37229 activeClasses.add(className);
Tim van der Lippe1d6e57a2019-09-30 11:55:34230 }
Blink Reformat4c46d092018-04-07 15:32:37231 }
232
Jan Scheffler77646c72021-08-13 18:40:38233 const additionalClasses = this.splitTextIntoClasses(this.prompt.textWithCurrentSuggestion());
Tim van der Lippe1d6e57a2019-09-30 11:55:34234 for (const className of additionalClasses) {
Blink Reformat4c46d092018-04-07 15:32:37235 activeClasses.add(className);
Tim van der Lippe1d6e57a2019-09-30 11:55:34236 }
Blink Reformat4c46d092018-04-07 15:32:37237
Simon Zünda0d40622020-02-12 13:16:42238 const newClasses = [...activeClasses.values()].sort();
Blink Reformat4c46d092018-04-07 15:32:37239
Jan Scheffler77646c72021-08-13 18:40:38240 this.pendingNodeClasses.set(node, newClasses.join(' '));
Tim van der Lippe2d9a95c2022-01-04 15:18:03241 void this.updateNodeThrottler.schedule(this.flushPendingClasses.bind(this));
Blink Reformat4c46d092018-04-07 15:32:37242 }
243
Jan Scheffler77646c72021-08-13 18:40:38244 private async flushPendingClasses(): Promise<void> {
Blink Reformat4c46d092018-04-07 15:32:37245 const promises = [];
Jan Scheffler77646c72021-08-13 18:40:38246 for (const node of this.pendingNodeClasses.keys()) {
247 this.mutatingNodes.add(node);
248 const promise = node.setAttributeValuePromise('class', (this.pendingNodeClasses.get(node) as string))
Blink Reformat4c46d092018-04-07 15:32:37249 .then(onClassValueUpdated.bind(this, node));
250 promises.push(promise);
251 }
Jan Scheffler77646c72021-08-13 18:40:38252 this.pendingNodeClasses.clear();
Tim van der Lippe32f760f2020-10-01 10:52:15253 await Promise.all(promises);
Blink Reformat4c46d092018-04-07 15:32:37254
Jan Scheffler29905852021-04-14 11:30:31255 function onClassValueUpdated(this: ClassesPaneWidget, node: SDK.DOMModel.DOMNode): void {
Jan Scheffler77646c72021-08-13 18:40:38256 this.mutatingNodes.delete(node);
Blink Reformat4c46d092018-04-07 15:32:37257 }
258 }
Tim van der Lippe13f71fb2019-11-29 11:17:39259}
Blink Reformat4c46d092018-04-07 15:32:37260
Jan Scheffler29905852021-04-14 11:30:31261const cachedClassesMap = new WeakMap<SDK.DOMModel.DOMNode, Map<string, boolean>>();
Blink Reformat4c46d092018-04-07 15:32:37262
Jan Scheffler29905852021-04-14 11:30:31263let buttonProviderInstance: ButtonProvider;
Andres Olivares43c2d9f2021-02-10 16:48:39264
Jan Scheffler29905852021-04-14 11:30:31265export class ButtonProvider implements UI.Toolbar.Provider {
Jan Scheffler77646c72021-08-13 18:40:38266 private readonly button: UI.Toolbar.ToolbarToggle;
267 private view: ClassesPaneWidget;
Jan Scheffler29905852021-04-14 11:30:31268 private constructor() {
Kateryna Prokopenko17cba892024-07-26 13:33:56269 this.button = new UI.Toolbar.ToolbarToggle(i18nString(UIStrings.elementClasses), 'class');
270 this.button.element.style.setProperty('--dot-toggle-top', '12px');
271 this.button.element.style.setProperty('--dot-toggle-left', '18px');
Danil Somsikov8a65be02023-10-17 08:36:58272 this.button.element.setAttribute(
Danil Somsikov0cb97462024-01-30 15:11:29273 'jslog', `${VisualLogging.toggleSubpane('elements-classes').track({click: true})}`);
Jan Scheffler77646c72021-08-13 18:40:38274 this.button.addEventListener(UI.Toolbar.ToolbarButton.Events.Click, this.clicked, this);
275 this.view = new ClassesPaneWidget();
Blink Reformat4c46d092018-04-07 15:32:37276 }
277
Jan Scheffler29905852021-04-14 11:30:31278 static instance(opts: {
279 forceNew: boolean|null,
280 } = {forceNew: null}): ButtonProvider {
Andres Olivares43c2d9f2021-02-10 16:48:39281 const {forceNew} = opts;
282 if (!buttonProviderInstance || forceNew) {
283 buttonProviderInstance = new ButtonProvider();
284 }
285
286 return buttonProviderInstance;
287 }
288
Jan Scheffler77646c72021-08-13 18:40:38289 private clicked(): void {
290 ElementsPanel.instance().showToolbarPane(!this.view.isShowing() ? this.view : null, this.button);
Blink Reformat4c46d092018-04-07 15:32:37291 }
292
Kateryna Prokopenko17cba892024-07-26 13:33:56293 item(): UI.Toolbar.ToolbarToggle {
Jan Scheffler77646c72021-08-13 18:40:38294 return this.button;
Blink Reformat4c46d092018-04-07 15:32:37295 }
Tim van der Lippe13f71fb2019-11-29 11:17:39296}
Blink Reformat4c46d092018-04-07 15:32:37297
Tim van der Lippe97611c92020-02-12 16:56:58298export class ClassNamePrompt extends UI.TextPrompt.TextPrompt {
Jan Scheffler77646c72021-08-13 18:40:38299 private readonly nodeClasses: (arg0: SDK.DOMModel.DOMNode) => Map<string, boolean>;
300 private selectedFrameId: string|null;
301 private classNamesPromise: Promise<string[]>|null;
Jan Scheffler29905852021-04-14 11:30:31302 constructor(nodeClasses: (arg0: SDK.DOMModel.DOMNode) => Map<string, boolean>) {
Blink Reformat4c46d092018-04-07 15:32:37303 super();
Jan Scheffler77646c72021-08-13 18:40:38304 this.nodeClasses = nodeClasses;
305 this.initialize(this.buildClassNameCompletions.bind(this), ' ');
Blink Reformat4c46d092018-04-07 15:32:37306 this.disableDefaultSuggestionForEmptyInput();
Jan Scheffler77646c72021-08-13 18:40:38307 this.selectedFrameId = '';
308 this.classNamesPromise = null;
Blink Reformat4c46d092018-04-07 15:32:37309 }
310
Jan Scheffler77646c72021-08-13 18:40:38311 private async getClassNames(selectedNode: SDK.DOMModel.DOMNode): Promise<string[]> {
Blink Reformat4c46d092018-04-07 15:32:37312 const promises = [];
Jan Scheffler29905852021-04-14 11:30:31313 const completions = new Set<string>();
Jan Scheffler77646c72021-08-13 18:40:38314 this.selectedFrameId = selectedNode.frameId();
Blink Reformat4c46d092018-04-07 15:32:37315
316 const cssModel = selectedNode.domModel().cssModel();
317 const allStyleSheets = cssModel.allStyleSheets();
318 for (const stylesheet of allStyleSheets) {
Jan Scheffler77646c72021-08-13 18:40:38319 if (stylesheet.frameId !== this.selectedFrameId) {
Blink Reformat4c46d092018-04-07 15:32:37320 continue;
Tim van der Lippe1d6e57a2019-09-30 11:55:34321 }
Johan Bay04d50fc2022-03-01 12:07:04322 const cssPromise = cssModel.getClassNames(stylesheet.id).then(classes => {
Mathias Bynens3abc0952020-04-20 14:15:52323 for (const className of classes) {
324 completions.add(className);
325 }
326 });
Blink Reformat4c46d092018-04-07 15:32:37327 promises.push(cssPromise);
328 }
329
Jan Scheffler29905852021-04-14 11:30:31330 const ownerDocumentId = ((selectedNode.ownerDocument as SDK.DOMModel.DOMDocument).id);
Tim van der Lippe32f760f2020-10-01 10:52:15331
332 const domPromise = selectedNode.domModel().classNamesPromise(ownerDocumentId).then(classes => {
Mathias Bynens3abc0952020-04-20 14:15:52333 for (const className of classes) {
334 completions.add(className);
335 }
336 });
Blink Reformat4c46d092018-04-07 15:32:37337 promises.push(domPromise);
Mathias Bynens3abc0952020-04-20 14:15:52338 await Promise.all(promises);
339 return [...completions];
Blink Reformat4c46d092018-04-07 15:32:37340 }
341
Jan Scheffler77646c72021-08-13 18:40:38342 private async buildClassNameCompletions(expression: string, prefix: string, force?: boolean):
Jan Scheffler29905852021-04-14 11:30:31343 Promise<UI.SuggestBox.Suggestions> {
Tim van der Lippe1d6e57a2019-09-30 11:55:34344 if (!prefix || force) {
Jan Scheffler77646c72021-08-13 18:40:38345 this.classNamesPromise = null;
Tim van der Lippe1d6e57a2019-09-30 11:55:34346 }
Blink Reformat4c46d092018-04-07 15:32:37347
Tim van der Lipped1a00aa2020-08-19 16:03:56348 const selectedNode = UI.Context.Context.instance().flavor(SDK.DOMModel.DOMNode);
Tim van der Lippe1d6e57a2019-09-30 11:55:34349 if (!selectedNode || (!prefix && !force && !expression.trim())) {
Mathias Bynens41ea2632020-12-24 05:52:49350 return [];
Tim van der Lippe1d6e57a2019-09-30 11:55:34351 }
Blink Reformat4c46d092018-04-07 15:32:37352
Jan Scheffler77646c72021-08-13 18:40:38353 if (!this.classNamesPromise || this.selectedFrameId !== selectedNode.frameId()) {
354 this.classNamesPromise = this.getClassNames(selectedNode);
Tim van der Lippe1d6e57a2019-09-30 11:55:34355 }
Blink Reformat4c46d092018-04-07 15:32:37356
Jan Scheffler77646c72021-08-13 18:40:38357 let completions: string[] = await this.classNamesPromise;
358 const classesMap = this.nodeClasses((selectedNode as SDK.DOMModel.DOMNode));
Mathias Bynens41ea2632020-12-24 05:52:49359 completions = completions.filter(value => !classesMap.get(value));
Blink Reformat4c46d092018-04-07 15:32:37360
Mathias Bynens41ea2632020-12-24 05:52:49361 if (prefix[0] === '.') {
362 completions = completions.map(value => '.' + value);
363 }
364 return completions.filter(value => value.startsWith(prefix)).sort().map(completion => {
365 return {
366 text: completion,
367 title: undefined,
368 subtitle: undefined,
Mathias Bynens41ea2632020-12-24 05:52:49369 priority: undefined,
370 isSecondary: undefined,
371 subtitleRenderer: undefined,
372 selectionRange: undefined,
373 hideGhostText: undefined,
374 iconElement: undefined,
375 };
Blink Reformat4c46d092018-04-07 15:32:37376 });
377 }
Tim van der Lippe13f71fb2019-11-29 11:17:39378}