Skip to content

Commit

Permalink
Fix infinite loop
Browse files Browse the repository at this point in the history
Fix regression caused by #104
  • Loading branch information
asamuzaK committed Jul 24, 2024
1 parent b966410 commit a87255b
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 13 deletions.
2 changes: 2 additions & 0 deletions src/js/constant.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ export const REG_FORM_CTRL =
export const REG_FORM_VALID = /^(?:button|form|input|select|textarea)$/;
export const REG_HEX = /^([\da-f]{1,6}\s?)/i;
export const REG_INTERACT = /^(?:details|dialog)$/;
export const REG_INVALIDATE_PSEUDO =
/^(?:(?:any-)?link|defined|dir|has|host(?:-context)?|not)$/;
export const REG_INVALID_SELECTOR = /^$|^\s*>|,\s*$/;
export const REG_LANG = new RegExp(`^(?:\\*-)?${ALPHA_NUM}${LANG_PART}$`, 'i');
export const REG_LANG_QUOTED = /(:lang\(\s*("[A-Za-z\d\-*]*")\s*\))/;
Expand Down
26 changes: 13 additions & 13 deletions src/js/finder.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ import {
import {
BIT_01, COMBINATOR, DOCUMENT_FRAGMENT_NODE, DOCUMENT_NODE, ELEMENT_NODE,
EMPTY, NOT_SUPPORTED_ERR, REG_ANCHOR, REG_FORM, REG_FORM_CTRL,
REG_FORM_VALID, REG_INTERACT, REG_LOGICAL_PSEUDO, REG_SHADOW_HOST,
REG_TYPE_CHECK, REG_TYPE_DATE, REG_TYPE_RANGE, REG_TYPE_RESET,
REG_TYPE_SUBMIT, REG_TYPE_TEXT, SELECTOR_ATTR, SELECTOR_CLASS, SELECTOR_ID,
SELECTOR_PSEUDO_CLASS, SELECTOR_PSEUDO_ELEMENT, SELECTOR_TYPE, SHOW_ALL,
SYNTAX_ERR, TARGET_ALL, TARGET_FIRST, TARGET_LINEAL, TARGET_SELF, TEXT_NODE,
WALKER_FILTER
REG_FORM_VALID, REG_INTERACT, REG_INVALIDATE_PSEUDO, REG_LOGICAL_PSEUDO,
REG_SHADOW_HOST, REG_TYPE_CHECK, REG_TYPE_DATE, REG_TYPE_RANGE,
REG_TYPE_RESET, REG_TYPE_SUBMIT, REG_TYPE_TEXT, SELECTOR_ATTR,
SELECTOR_CLASS, SELECTOR_ID, SELECTOR_PSEUDO_CLASS, SELECTOR_PSEUDO_ELEMENT,
SELECTOR_TYPE, SHOW_ALL, SYNTAX_ERR, TARGET_ALL, TARGET_FIRST, TARGET_LINEAL,
TARGET_SELF, TEXT_NODE, WALKER_FILTER
} from './constant.js';
const DIR_NEXT = 'next';
const DIR_PREV = 'prev';
Expand Down Expand Up @@ -1799,16 +1799,20 @@ export class Finder {
bool = matched;
}
if (typeof bool !== 'boolean') {
let cacheable = true;
if (node.nodeType === ELEMENT_NODE && REG_FORM.test(node.localName)) {
cacheable = false;
}
for (const leaf of leaves) {
switch (leaf.type) {
case SELECTOR_ATTR:
case SELECTOR_ID: {
this.#invalidate = true;
cacheable = false;
break;
}
case SELECTOR_PSEUDO_CLASS: {
if (/^(?:(?:any-)?link|defined|dir|has)$/.test(leaf.name)) {
this.#invalidate = true;
if (REG_INVALIDATE_PSEUDO.test(leaf.name)) {
cacheable = false;
}
break;
}
Expand All @@ -1819,10 +1823,6 @@ export class Finder {
break;
}
}
let cacheable = true;
if (node.nodeType === ELEMENT_NODE && REG_FORM.test(node.localName)) {
cacheable = false;
}
if (cacheable && !this.#invalidate) {
if (!result) {
result = new WeakMap();
Expand Down
95 changes: 95 additions & 0 deletions test/wpt.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1682,6 +1682,101 @@ describe('local wpt test cases', () => {
});
});

describe('css/selectors/has-basic.html', () => {
it('should get matched node(s)', () => {
const html = `
<main id=main>
<div id=a class="ancestor">
<div id=b class="parent ancestor">
<div id=c class="sibling descendant">
<div id=d class="descendant"></div>
</div>
<div id=e class="target descendant"></div>
</div>
<div id=f class="parent ancestor">
<div id=g class="target descendant"></div>
</div>
<div id=h class="parent ancestor">
<div id=i class="target descendant"></div>
<div id=j class="sibling descendant">
<div id=k class="descendant"></div>
</div>
</div>
</div>
</main>
`;
document.body.innerHTML = html;
const main = document.getElementById('main');
assert.deepEqual(main.querySelectorAll(':has(#a)'), []);
assert.deepEqual(main.querySelectorAll(':has(.ancestor)'), [
document.getElementById('a')
]);
assert.deepEqual(main.querySelectorAll(':has(.target)'), [
document.getElementById('a'),
document.getElementById('b'),
document.getElementById('f'),
document.getElementById('h')
]);
assert.deepEqual(main.querySelectorAll(':has(.descendant)'), [
document.getElementById('a'),
document.getElementById('b'),
document.getElementById('c'),
document.getElementById('f'),
document.getElementById('h'),
document.getElementById('j')
]);
assert.deepEqual(main.querySelectorAll('.parent:has(.target)'), [
document.getElementById('b'),
document.getElementById('f'),
document.getElementById('h')
]);
assert.deepEqual(main.querySelectorAll(':has(.sibling ~ .target)'), [
document.getElementById('a'),
document.getElementById('b')
]);
assert.deepEqual(main.querySelectorAll('.parent:has(.sibling ~ .target)'), [
document.getElementById('b')
]);
assert.deepEqual(main.querySelectorAll(':has(:is(.target ~ .sibling .descendant))'), [
document.getElementById('a'),
document.getElementById('h'),
document.getElementById('j')
]);
assert.deepEqual(main.querySelectorAll('.parent:has(:is(.target ~ .sibling .descendant))'), [
document.getElementById('h')
]);
assert.deepEqual(main.querySelectorAll('.sibling:has(.descendant) ~ .target'), [
document.getElementById('e')
]);
assert.deepEqual(main.querySelectorAll(':has(> .parent)'), [
document.getElementById('a')
]);
assert.deepEqual(main.querySelectorAll(':has(> .target)'), [
document.getElementById('b'),
document.getElementById('f'),
document.getElementById('h')
]);
assert.deepEqual(main.querySelectorAll(':has(> .parent, > .target)'), [
document.getElementById('a'),
document.getElementById('b'),
document.getElementById('f'),
document.getElementById('h')
]);
assert.deepEqual(main.querySelectorAll(':has(+ #h)'), [
document.getElementById('f')
]);
assert.deepEqual(main.querySelectorAll('.parent:has(~ #h)'), [
document.getElementById('b'),
document.getElementById('f')
]);
assert.deepEqual(main.querySelector('.sibling:has(.descendant)'),
document.getElementById('c'));
assert.deepEqual(document.getElementById('k').closest('.ancestor:has(.descendant)'),
document.getElementById('h'));
assert.isTrue(document.getElementById('h').matches(':has(.target ~ .sibling .descendant)'));
});
});

describe('css/selectors/has-argument-with-explicit-scope.html', () => {
const html = `
<main>
Expand Down

0 comments on commit a87255b

Please sign in to comment.