Skip to content

Commit

Permalink
Fix some edge cases
Browse files Browse the repository at this point in the history
  • Loading branch information
asamuzaK committed Sep 16, 2024
1 parent c8d64a1 commit 3905c9e
Show file tree
Hide file tree
Showing 6 changed files with 157 additions and 33 deletions.
35 changes: 25 additions & 10 deletions src/js/finder.js
Original file line number Diff line number Diff line change
Expand Up @@ -714,18 +714,17 @@ export class Finder {
const nodes = this._matchCombinator(twig, node, opt);
if (nodes.size) {
if (leaves.length) {
let bool;
let bool = false;
for (const nextNode of nodes) {
bool = this._matchHasPseudoFunc(leaves, nextNode, opt);
if (bool) {
break;
}
}
return !!bool;
return bool;
}
return true;
}
return false;
}
return false;
}
Expand All @@ -740,7 +739,8 @@ export class Finder {
*/
_matchLogicalPseudoFunc(astData, node, opt) {
const { astName, branches, twigBranches } = astData;
const { isShadowRoot } = opt;
const isShadowRoot = opt.isShadowRoot ||
(this.#shadow && node.nodeType === DOCUMENT_FRAGMENT_NODE);
if (astName === 'has') {
let bool;
for (const leaves of branches) {
Expand All @@ -754,19 +754,29 @@ export class Finder {
if (this.#verifyShadowHost) {
return node;
}
return null;
} else {
return node;
}
}
return null;
} else {
// check for invalid shadow root
if (isShadowRoot) {
let invalid;
for (const branch of branches) {
if (branch.length > 1) {
return null;
invalid = true;
break;
} else if (astName === 'not') {
const [{ type: childAstType }] = branch;
if (childAstType !== PS_CLASS_SELECTOR) {
invalid = true;
break;
}
}
}
if (invalid) {
return null;
}
}
opt.forgive = astName === 'is' || astName === 'where';
const l = twigBranches.length;
Expand Down Expand Up @@ -812,8 +822,8 @@ export class Finder {
} else if (bool) {
return node;
}
return null;
}
return null;
}

/**
Expand Down Expand Up @@ -845,6 +855,8 @@ export class Finder {
} else {
const { branches } = walkAST(ast);
if (astName === 'has') {
// check for nested :has()
let forgiven;
for (const child of astChildren) {
const item = findAST(child, leaf => {
if (KEY_LOGICAL.includes(leaf.name) &&
Expand All @@ -856,13 +868,17 @@ export class Finder {
if (item) {
const itemName = item.name;
if (itemName === 'is' || itemName === 'where') {
return matched;
forgiven = true;
break;
} else {
const css = generateCSS(ast);
throw new DOMException(`Invalid selector ${css}`, SYNTAX_ERR);
}
}
}
if (forgiven) {
return matched;
}
astData = {
astName,
branches
Expand Down Expand Up @@ -2695,7 +2711,6 @@ export class Finder {
if (matched) {
return node;
}
return null;
}
}
return null;
Expand Down
3 changes: 0 additions & 3 deletions src/js/matcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,6 @@ export const matchLanguagePseudoClass = (ast, node) => {
if (node.getAttribute('lang')) {
return true;
}
return false;
} else {
let parent = node.parentNode;
let res;
Expand Down Expand Up @@ -173,7 +172,6 @@ export const matchLanguagePseudoClass = (ast, node) => {
return !!res;
}
}
return false;
}
return false;
};
Expand Down Expand Up @@ -383,7 +381,6 @@ export const matchAttributeSelector = (ast, node) => {
}
}
}
return false;
}
return false;
};
Expand Down
9 changes: 4 additions & 5 deletions src/js/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
CLASS_SELECTOR, DUO, HEX, HYPHEN, ID_SELECTOR, KEY_LOGICAL, NTH,
PS_CLASS_SELECTOR, PS_ELEMENT_SELECTOR, SELECTOR, SYNTAX_ERR, TYPE_SELECTOR
} from './constant.js';
const REG_EMPTY_PSEUDO_FUNC = /(?<=:(?:dir|has|host(?:-context)?|is|lang|not|nth-(?:last-)?(?:child|of-type)|where)\()\s+\)/g;
const REG_EMPTY_PS_FUNC = /(?<=:(?:dir|has|host(?:-context)?|is|lang|not|nth-(?:last-)?(?:child|of-type)|where)\()\s+\)/g;
const REG_SHADOW_PS_ELEMENT = /^part|slotted$/;
const U_FFFD = '\uFFFD';

Expand Down Expand Up @@ -77,13 +77,13 @@ export const preprocess = (...args) => {
if (typeof selector === 'string') {
let index = 0;
while (index >= 0) {
// @see https://drafts.csswg.org/selectors/#id-selectors
index = selector.indexOf('#', index);
if (index < 0) {
break;
}
const preHash = selector.substring(0, index + 1);
let postHash = selector.substring(index + 1);
// @see https://drafts.csswg.org/selectors/#id-selectors
// @see https://drafts.csswg.org/css-syntax-3/#ident-token-diagram
if (/^\d$/.test(postHash.substring(0, 1))) {
throw new DOMException(`Invalid selector ${selector}`, SYNTAX_ERR);
Expand Down Expand Up @@ -155,9 +155,8 @@ export const parseSelector = selector => {
}
} else if (message === '")" is expected') {
// workaround for https://github.com/csstree/csstree/issues/283
if (REG_EMPTY_PSEUDO_FUNC.test(selector)) {
res =
parseSelector(`${selector.replaceAll(REG_EMPTY_PSEUDO_FUNC, ')')}`);
if (REG_EMPTY_PS_FUNC.test(selector)) {
res = parseSelector(`${selector.replaceAll(REG_EMPTY_PS_FUNC, ')')}`);
} else if (!selector.endsWith(')')) {
res = parseSelector(`${selector})`);
} else {
Expand Down
29 changes: 14 additions & 15 deletions src/js/utility.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ export const resolveContent = node => {

/**
* traverse node tree
* @private
* @param {object} node - node
* @param {object} walker - tree walker
* @returns {?object} - current node
Expand All @@ -103,12 +102,19 @@ export const traverseNode = (node, walker) => {
return refNode;
} else {
if (refNode !== walker.root) {
let bool;
while (refNode) {
if (refNode === walker.root || refNode === node) {
if (refNode === node) {
bool = true;
break;
} else if (refNode === walker.root) {
break;
}
refNode = walker.parentNode();
}
if (bool) {
return refNode;
}
}
if (node.nodeType === ELEMENT_NODE) {
let bool;
Expand All @@ -122,11 +128,9 @@ export const traverseNode = (node, walker) => {
if (bool) {
return refNode;
}
return null;
} else {
return refNode;
}
}
return null;
};

/**
Expand Down Expand Up @@ -290,14 +294,13 @@ export const getDirectionality = node => {
if (level % 2 === 1) {
return 'rtl';
}
return 'ltr';
} else if (parentNode) {
const { nodeType: parentNodeType } = parentNode;
if (parentNodeType === ELEMENT_NODE) {
return getDirectionality(parentNode);
}
return 'ltr';
}
} else if (localName === 'input' && node.type === 'tel') {
return 'ltr';
} else if (localName === 'bdi') {
const text = node.textContent.trim();
Expand All @@ -306,11 +309,7 @@ export const getDirectionality = node => {
if (level % 2 === 1) {
return 'rtl';
}
return 'ltr';
}
return 'ltr';
} else if (localName === 'input' && node.type === 'tel') {
return 'ltr';
} else if (parentNode) {
if (localName === 'slot') {
const text = getSlottedTextContent(node);
Expand All @@ -326,7 +325,6 @@ export const getDirectionality = node => {
if (parentNodeType === ELEMENT_NODE) {
return getDirectionality(parentNode);
}
return 'ltr';
}
return 'ltr';
};
Expand Down Expand Up @@ -464,12 +462,15 @@ export const isFocusableArea = node => {
case 'summary': {
if (parentNode.localName === 'details') {
let child = parentNode.firstElementChild;
let bool = false;
while (child) {
if (child.localName === 'summary') {
return node === child;
bool = child === node;
break;
}
child = child.nextElementSibling;
}
return bool;
}
return false;
}
Expand All @@ -479,7 +480,6 @@ export const isFocusableArea = node => {
!(node.disabled || node.hasAttribute('disabled'))) {
return true;
}
return false;
}
}
} else if (node instanceof window.SVGElement) {
Expand Down Expand Up @@ -512,7 +512,6 @@ export const isFocusableArea = node => {
(node.href || node.hasAttributeNS(null, 'href'))) {
return true;
}
return false;
}
return false;
};
Expand Down
94 changes: 94 additions & 0 deletions test/finder.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3411,6 +3411,100 @@ describe('Finder', () => {
}, node, {});
assert.deepEqual(res, node, 'result');
});

it('should not match', () => {
const branches = [
[
{
loc: null,
name: 'host',
type: PS_CLASS_SELECTOR
},
{
loc: null,
name: '>',
type: COMBINATOR
},
{
loc: null,
name: 'span',
type: TYPE_SELECTOR
}
]
];
const html = `
<template id="template">
<div>
<slot id="foo" name="bar">Foo</slot>
</div>
</template>
<my-element id="baz">
<span id="qux" slot="foo">Qux</span>
</my-element>
`;
const container = document.getElementById('div0');
container.innerHTML = html;
class MyElement extends window.HTMLElement {
constructor() {
super();
const shadowRoot = this.attachShadow({ mode: 'open' });
const template = document.getElementById('template');
shadowRoot.appendChild(template.content.cloneNode(true));
}
};
window.customElements.define('my-element', MyElement);
const host = document.getElementById('baz');
const node = host.shadowRoot;
const finder = new Finder(window);
finder.setup(':not(:host > span)', node);
const res = finder._matchLogicalPseudoFunc({
astName: 'not',
branches
}, node, {});
assert.isNull(res, 'result');
});

it('should not match', () => {
const branches = [
[
{
loc: null,
name: 'div',
type: TYPE_SELECTOR
}
]
];
const html = `
<template id="template">
<div>
<slot id="foo" name="bar">Foo</slot>
</div>
</template>
<my-element id="baz">
<span id="qux" slot="foo">Qux</span>
</my-element>
`;
const container = document.getElementById('div0');
container.innerHTML = html;
class MyElement extends window.HTMLElement {
constructor() {
super();
const shadowRoot = this.attachShadow({ mode: 'open' });
const template = document.getElementById('template');
shadowRoot.appendChild(template.content.cloneNode(true));
}
};
window.customElements.define('my-element', MyElement);
const host = document.getElementById('baz');
const node = host.shadowRoot;
const finder = new Finder(window);
finder.setup(':not(div)', node);
const res = finder._matchLogicalPseudoFunc({
astName: 'not',
branches
}, node, {});
assert.isNull(res, 'result');
});
});

describe('match pseudo class selector', () => {
Expand Down
Loading

0 comments on commit 3905c9e

Please sign in to comment.