Skip to content

Commit 030745d

Browse files
[js] Reducing GC pressure in the TypeScript find-elements atom (#17580)
1 parent 4749fd6 commit 030745d

2 files changed

Lines changed: 49 additions & 18 deletions

File tree

javascript/atoms/test/find_elements_typescript_test.html

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,28 @@
134134
assert.equal(elements[0].getAttribute('id'), 'input1');
135135
});
136136

137+
QUnit.test('finds elements by name with double-quote in value', function(assert) {
138+
var el = document.createElement('input');
139+
el.setAttribute('name', 'my"input');
140+
el.setAttribute('id', 'quoted-name-input');
141+
fixture.appendChild(el);
142+
var elements = findElements({'name': 'my"input'}, fixture);
143+
assert.equal(elements.length, 1);
144+
assert.equal(elements[0].getAttribute('id'), 'quoted-name-input');
145+
fixture.removeChild(el);
146+
});
147+
148+
QUnit.test('finds elements by name with backslash in value', function(assert) {
149+
var el = document.createElement('input');
150+
el.setAttribute('name', 'my\\input');
151+
el.setAttribute('id', 'backslash-name-input');
152+
fixture.appendChild(el);
153+
var elements = findElements({'name': 'my\\input'}, fixture);
154+
assert.equal(elements.length, 1);
155+
assert.equal(elements[0].getAttribute('id'), 'backslash-name-input');
156+
fixture.removeChild(el);
157+
});
158+
137159
QUnit.test('finds elements by xpath', function(assert) {
138160
var elements = findElements({'xpath': './/p'}, fixture);
139161
assert.equal(elements.length, 3);
@@ -145,6 +167,11 @@
145167
assert.equal(elements[0].getAttribute('id'), 'para2');
146168
});
147169

170+
QUnit.test('finds elements by xpath using axis notation', function(assert) {
171+
var elements = findElements({'xpath': 'descendant::p'}, fixture);
172+
assert.equal(elements.length, 3);
173+
});
174+
148175
QUnit.test('throws for unsupported strategy', function(assert) {
149176
assert.throws(function() {
150177
findElements({'unsupported': 'value'}, fixture);

javascript/atoms/typescript/find-elements.ts

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@
8585
}
8686

8787
function nameMany(target: string, root: Root): Element[] {
88-
return Array.from(root.querySelectorAll('*')).filter(el => el.getAttribute('name') === target)
88+
return Array.from(root.querySelectorAll('[name="' + target.replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"]'))
8989
}
9090

9191
function tagNameMany(target: string, root: Root): Element[] {
@@ -108,26 +108,30 @@
108108
return []
109109
}
110110
try {
111-
const reversedNs: Record<string, string> = {}
112-
const allNodes = doc.getElementsByTagName('*')
113-
for (let i = 0; i < allNodes.length; i++) {
114-
const n = allNodes[i]
115-
const ns = n.namespaceURI
116-
if (ns && !reversedNs[ns]) {
117-
let prefix = n.lookupPrefix(ns)
118-
if (!prefix) {
119-
const m = ns.match('.*/(\\w+)/?$')
120-
prefix = m ? m[1] : 'xhtml'
111+
// Namespace prefixes require a colon in the XPath expression. Skip the
112+
// expensive full-DOM scan when the expression contains no colon at all.
113+
let resolver: ((prefix: string | null) => string | null) | null = null
114+
if (target.indexOf(':') !== -1) {
115+
const reversedNs: Record<string, string> = {}
116+
const allNodes = doc.getElementsByTagName('*')
117+
for (let i = 0; i < allNodes.length; i++) {
118+
const n = allNodes[i]
119+
const ns = n.namespaceURI
120+
if (ns && !reversedNs[ns]) {
121+
let prefix = n.lookupPrefix(ns)
122+
if (!prefix) {
123+
const m = ns.match('.*/(\\w+)/?$')
124+
prefix = m ? m[1] : 'xhtml'
125+
}
126+
reversedNs[ns] = prefix!
121127
}
122-
reversedNs[ns] = prefix!
123128
}
129+
const namespaces: Record<string, string> = {}
130+
for (const key in reversedNs) {
131+
namespaces[reversedNs[key]] = key
132+
}
133+
resolver = (prefix: string | null): string | null => namespaces[prefix || ''] || null
124134
}
125-
const namespaces: Record<string, string> = {}
126-
for (const key in reversedNs) {
127-
namespaces[reversedNs[key]] = key
128-
}
129-
let resolver: XPathNSResolver | ((prefix: string | null) => string | null) =
130-
(prefix: string | null): string | null => namespaces[prefix || ''] || null
131135

132136
let result: XPathResult | null = null
133137
try {

0 commit comments

Comments
 (0)