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. * limitations under the License.
*/ */
import {QueryHandler, type QuerySelectorAll} from './QueryHandler.js'; import {
QueryHandler,
type QuerySelectorAll,
type QuerySelector,
} from './QueryHandler.js';
/** /**
* @internal * @internal
@ -27,4 +31,15 @@ export class XPathQueryHandler extends QueryHandler {
) => { ) => {
return xpathQuerySelectorAll(element, selector); 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* ( export const xpathQuerySelectorAll = function* (
root: Node, root: Node,
selector: string selector: string,
maxResults = -1
): Iterable<Node> { ): Iterable<Node> {
const doc = root.ownerDocument || document; const doc = root.ownerDocument || document;
const iterator = doc.evaluate( const iterator = doc.evaluate(
@ -28,8 +29,21 @@ export const xpathQuerySelectorAll = function* (
null, null,
XPathResult.ORDERED_NODE_ITERATOR_TYPE XPathResult.ORDERED_NODE_ITERATOR_TYPE
); );
const items = [];
let item; 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())) { 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];
} }
}; };