Skip to content

Commit 6f7cc16

Browse files
hanselfmu-chromiumDevtools-frontend LUCI CQ
authored andcommitted
Small cleanup for ElementsTreeElement
This is one of a few cleanup efforts to pave the way for better integrating squiggly line into ElementsTreeElement, including extracting functions that do not rely on the instance, and making functions more modular and testable. Bug: None Change-Id: I11ea69a5c408197028de7cb90f35ebceeed3d990 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/6276551 Reviewed-by: Eric Leese <[email protected]> Auto-Submit: Changhao Han <[email protected]> Commit-Queue: Changhao Han <[email protected]>
1 parent 9748d8e commit 6f7cc16

File tree

4 files changed

+76
-34
lines changed

4 files changed

+76
-34
lines changed

front_end/panels/elements/BUILD.gn

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ ts_library("unittests") {
163163
"DOMLinkifier.test.ts",
164164
"ElementStatePaneWidget.test.ts",
165165
"ElementsPanel.test.ts",
166+
"ElementsTreeElement.test.ts",
166167
"ElementsTreeElementHighlighter.test.ts",
167168
"ElementsTreeOutline.test.ts",
168169
"InspectElementModeController.test.ts",
@@ -180,6 +181,7 @@ ts_library("unittests") {
180181

181182
deps = [
182183
":bundle",
184+
"../../models/text_utils:bundle",
183185
"../../testing",
184186
"./components:unittests",
185187
]
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright 2025 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.
4+
5+
import * as TextUtils from '../../models/text_utils/text_utils.js';
6+
7+
import * as Elements from './elements.js';
8+
9+
describe('ElementsTreeElement', () => {
10+
describe('convertUnicodeCharsToHTMLEntities', () => {
11+
it('converts unicode characters to HTML entities', () => {
12+
const input = '\u2002\u2002This string has spaces\xA0\xA0and other\u202Aunicode characters\u200A.';
13+
const expected = {
14+
text: '&ensp;&ensp;This string has spaces&nbsp;&nbsp;and other&#x202A;unicode characters&hairsp;.',
15+
entityRanges: [
16+
new TextUtils.TextRange.SourceRange(0, 6),
17+
new TextUtils.TextRange.SourceRange(6, 6),
18+
new TextUtils.TextRange.SourceRange(34, 6),
19+
new TextUtils.TextRange.SourceRange(40, 6),
20+
new TextUtils.TextRange.SourceRange(55, 8),
21+
new TextUtils.TextRange.SourceRange(81, 8),
22+
],
23+
};
24+
25+
const result = Elements.ElementsTreeElement.convertUnicodeCharsToHTMLEntities(input);
26+
assert.strictEqual(result.text, expected.text);
27+
assert.deepEqual(result.entityRanges, expected.entityRanges);
28+
});
29+
30+
it('returns the original string if no unicode characters are present', () => {
31+
const input = 'ThisStringHasNoWhitespace';
32+
const expected = {
33+
text: 'ThisStringHasNoWhitespace',
34+
entityRanges: [],
35+
};
36+
37+
const result = Elements.ElementsTreeElement.convertUnicodeCharsToHTMLEntities(input);
38+
assert.strictEqual(result.text, expected.text);
39+
assert.deepEqual(result.entityRanges, expected.entityRanges);
40+
});
41+
});
42+
});

front_end/panels/elements/ElementsTreeElement.ts

Lines changed: 30 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -283,12 +283,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
283283
this.gutterContainer = this.contentElement.createChild('div', 'gutter-container');
284284
this.gutterContainer.addEventListener('click', this.showContextMenu.bind(this));
285285
const gutterMenuIcon = new IconButton.Icon.Icon();
286-
gutterMenuIcon.data = {
287-
color: 'var(--icon-default)',
288-
iconName: 'dots-horizontal',
289-
height: '16px',
290-
width: '16px',
291-
};
286+
gutterMenuIcon.name = 'dots-horizontal';
292287
this.gutterContainer.append(gutterMenuIcon);
293288
this.decorationsElement = this.gutterContainer.createChild('div', 'hidden');
294289

@@ -1138,7 +1133,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
11381133
return;
11391134
}
11401135

1141-
const initialValue = this.convertWhitespaceToEntities(maybeInitialValue).text;
1136+
const initialValue = convertUnicodeCharsToHTMLEntities(maybeInitialValue).text;
11421137
this.htmlEditElement = document.createElement('div');
11431138
this.htmlEditElement.className = 'source-code elements-tree-editor';
11441139

@@ -1613,7 +1608,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
16131608
let additionalHighlightOffset = 0;
16141609

16151610
function setValueWithEntities(this: ElementsTreeElement, element: Element, value: string): void {
1616-
const result = this.convertWhitespaceToEntities(value);
1611+
const result = convertUnicodeCharsToHTMLEntities(value);
16171612
highlightCount = result.entityRanges.length;
16181613
value = result.text.replace(closingPunctuationRegex, (match, replaceOffset) => {
16191614
while (highlightIndex < highlightCount && result.entityRanges[highlightIndex].offset < replaceOffset) {
@@ -1827,30 +1822,6 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
18271822
}
18281823
}
18291824

1830-
private convertWhitespaceToEntities(text: string): {
1831-
text: string,
1832-
entityRanges: TextUtils.TextRange.SourceRange[],
1833-
} {
1834-
let result = '';
1835-
let lastIndexAfterEntity = 0;
1836-
const entityRanges = [];
1837-
const charToEntity = MappedCharToEntity;
1838-
for (let i = 0, size = text.length; i < size; ++i) {
1839-
const char = text.charAt(i);
1840-
if (charToEntity.has(char)) {
1841-
result += text.substring(lastIndexAfterEntity, i);
1842-
const entityValue = '&' + charToEntity.get(char) + ';';
1843-
entityRanges.push(new TextUtils.TextRange.SourceRange(result.length, entityValue.length));
1844-
result += entityValue;
1845-
lastIndexAfterEntity = i + 1;
1846-
}
1847-
}
1848-
if (result) {
1849-
result += text.substring(lastIndexAfterEntity);
1850-
}
1851-
return {text: result || text, entityRanges};
1852-
}
1853-
18541825
private nodeTitleInfo(updateRecord: UpdateRecord|null): DocumentFragment {
18551826
const node = this.nodeInternal;
18561827
const titleDOM = document.createDocumentFragment();
@@ -1912,7 +1883,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
19121883
if (!firstChild) {
19131884
throw new Error('ElementsTreeElement._nodeTitleInfo expects node.firstChild to be defined.');
19141885
}
1915-
const result = this.convertWhitespaceToEntities(firstChild.nodeValue());
1886+
const result = convertUnicodeCharsToHTMLEntities(firstChild.nodeValue());
19161887
textNodeElement.textContent = Platform.StringUtilities.collapseWhitespace(result.text);
19171888
UI.UIUtils.highlightRangesWithStyleClass(textNodeElement, result.entityRanges, 'webkit-html-entity-value');
19181889
UI.UIUtils.createTextChild(titleDOM, '\u200B');
@@ -1952,7 +1923,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
19521923
const textNodeElement = titleDOM.createChild('span', 'webkit-html-text-node');
19531924
textNodeElement.setAttribute(
19541925
'jslog', `${VisualLogging.value('text-node').track({change: true, dblclick: true})}`);
1955-
const result = this.convertWhitespaceToEntities(node.nodeValue());
1926+
const result = convertUnicodeCharsToHTMLEntities(node.nodeValue());
19561927
textNodeElement.textContent = Platform.StringUtilities.collapseWhitespace(result.text);
19571928
UI.UIUtils.highlightRangesWithStyleClass(textNodeElement, result.entityRanges, 'webkit-html-entity-value');
19581929
UI.UIUtils.createTextChild(titleDOM, '"');
@@ -2555,6 +2526,31 @@ export function adornerComparator(adornerA: Adorners.Adorner.Adorner, adornerB:
25552526
}
25562527
return compareCategories;
25572528
}
2529+
2530+
export function convertUnicodeCharsToHTMLEntities(text: string): {
2531+
text: string,
2532+
entityRanges: TextUtils.TextRange.SourceRange[],
2533+
} {
2534+
let result = '';
2535+
let lastIndexAfterEntity = 0;
2536+
const entityRanges = [];
2537+
const charToEntity = MappedCharToEntity;
2538+
for (let i = 0, size = text.length; i < size; ++i) {
2539+
const char = text.charAt(i);
2540+
if (charToEntity.has(char)) {
2541+
result += text.substring(lastIndexAfterEntity, i);
2542+
const entityValue = '&' + charToEntity.get(char) + ';';
2543+
entityRanges.push(new TextUtils.TextRange.SourceRange(result.length, entityValue.length));
2544+
result += entityValue;
2545+
lastIndexAfterEntity = i + 1;
2546+
}
2547+
}
2548+
if (result) {
2549+
result += text.substring(lastIndexAfterEntity);
2550+
}
2551+
return {text: result || text, entityRanges};
2552+
}
2553+
25582554
export interface EditorHandles {
25592555
commit: () => void;
25602556
cancel: () => void;

front_end/panels/elements/elementsTreeOutline.css

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,8 @@ select {
312312
visibility: hidden;
313313
position: relative;
314314
left: -1px;
315+
width: 16px;
316+
height: 16px;
315317
}
316318

317319
.elements-disclosure li.selected .gutter-container:not(.has-decorations) > devtools-icon {

0 commit comments

Comments
 (0)