fix: xpath queries should be atomic (#11101)

This commit is contained in:
Alex Rudenko 2023-10-09 14:21:22 +02:00 committed by GitHub
parent 8324c16348
commit 6098bab2ba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 32 additions and 3 deletions

View File

@ -14,7 +14,11 @@
* limitations under the License.
*/
import {QueryHandler, type QuerySelectorAll} from './QueryHandler.js';
import {
QueryHandler,
type QuerySelectorAll,
type QuerySelector,
} from './QueryHandler.js';
/**
* @internal
@ -27,4 +31,15 @@ export class XPathQueryHandler extends QueryHandler {
) => {
return xpathQuerySelectorAll(element, selector);
};
static override querySelector: QuerySelector = (
element: Node,
selector: string,
{xpathQuerySelectorAll}
) => {
for (const result of xpathQuerySelectorAll(element, selector, 1)) {
return result;
}
return null;
};
}

View File

@ -19,7 +19,8 @@
*/
export const xpathQuerySelectorAll = function* (
root: Node,
selector: string
selector: string,
maxResults = -1
): Iterable<Node> {
const doc = root.ownerDocument || document;
const iterator = doc.evaluate(
@ -28,8 +29,21 @@ export const xpathQuerySelectorAll = function* (
null,
XPathResult.ORDERED_NODE_ITERATOR_TYPE
);
const items = [];
let item;
// Read all results upfront to avoid
// https://stackoverflow.com/questions/48235278/xpath-error-the-document-has-mutated-since-the-result-was-returned.
while ((item = iterator.iterateNext())) {
yield item;
items.push(item);
if (maxResults && items.length === maxResults) {
break;
}
}
for (let i = 0; i < items.length; i++) {
item = items[i];
yield item as Node;
delete items[i];
}
};