Fallback procedural :has() operator when argument is not a valid plain CSS selector

Related issue:
- https://github.com/uBlockOrigin/uBlock-issues/issues/2228
This commit is contained in:
Raymond Hill 2022-08-23 16:36:22 -04:00
parent 95661c5f49
commit 8f39a457b8
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
1 changed files with 25 additions and 9 deletions

View File

@ -1384,6 +1384,7 @@ Parser.prototype.SelectorCompiler = class {
this.reDropScope = /^\s*:scope\s*(?=[+>~])/; this.reDropScope = /^\s*:scope\s*(?=[+>~])/;
this.reIsDanglingSelector = /[+>~\s]\s*$/; this.reIsDanglingSelector = /[+>~\s]\s*$/;
this.reIsCombinator = /^\s*[+>~]/; this.reIsCombinator = /^\s*[+>~]/;
this.reForgivingOps = /:has\(/;
this.regexToRawValue = new Map(); this.regexToRawValue = new Map();
// https://github.com/gorhill/uBlock/issues/2793 // https://github.com/gorhill/uBlock/issues/2793
this.normalizedOperators = new Map([ this.normalizedOperators = new Map([
@ -1416,7 +1417,15 @@ Parser.prototype.SelectorCompiler = class {
} }
// Can be used in a declarative CSS rule? // Can be used in a declarative CSS rule?
if ( asProcedural === false && this.sheetSelectable(raw) ) { // https://github.com/uBlockOrigin/uBlock-issues/issues/2228
// Some operators are forgiving, so we need to exclude them for now
// as potentially declarative selectors until we validate that their
// arguments are themselves valid plain CSS selector.
if (
asProcedural === false &&
this.reForgivingOps.test(raw) === false &&
this.sheetSelectable(raw)
) {
out.compiled = raw; out.compiled = raw;
return true; return true;
} }
@ -1792,20 +1801,27 @@ Parser.prototype.SelectorCompiler = class {
// Unbalanced parenthesis? An unbalanced parenthesis is fine // Unbalanced parenthesis? An unbalanced parenthesis is fine
// as long as the last character is a closing parenthesis. // as long as the last character is a closing parenthesis.
if ( pcnt !== 0 && c !== 0x29 ) { return; } if ( pcnt !== 0 && c !== 0x29 ) { return; }
// Extract and remember operator/argument details.
const opname = raw.slice(opNameBeg, opNameEnd);
const oparg = raw.slice(opNameEnd + 1, i - 1);
const operator = this.normalizedOperators.get(opname) || opname;
// https://github.com/uBlockOrigin/uBlock-issues/issues/341#issuecomment-447603588 // https://github.com/uBlockOrigin/uBlock-issues/issues/341#issuecomment-447603588
// Maybe that one operator is a valid CSS selector and if so, // Maybe that one operator is a valid CSS selector and if so,
// then consider it to be part of the prefix. // then consider it to be part of the prefix.
if ( this.querySelectable(raw.slice(opNameBeg, i)) ) { continue; } // https://github.com/uBlockOrigin/uBlock-issues/issues/2228
// Extract and remember operator details. // Maybe an operator is a valid CSS selector, but if it is
let operator = raw.slice(opNameBeg, opNameEnd); // "forgiving", we also need to validate that the argument itself
operator = this.normalizedOperators.get(operator) || operator; // is also a valid CSS selector.
if (
this.querySelectable(raw.slice(opNameBeg, i)) &&
this.querySelectable(oparg)
) {
continue;
}
// Action operator can only be used as trailing operator in the // Action operator can only be used as trailing operator in the
// root task list. // root task list.
// Per-operator arguments validation // Per-operator arguments validation
const args = this.compileArgument( const args = this.compileArgument(operator, oparg);
operator,
raw.slice(opNameEnd + 1, i - 1)
);
if ( args === undefined ) { return; } if ( args === undefined ) { return; }
if ( opPrefixBeg === 0 ) { if ( opPrefixBeg === 0 ) {
prefix = raw.slice(0, opNameBeg); prefix = raw.slice(0, opNameBeg);