Properly serialize CSS combinators according to position in selector

Related issue:
https://github.com/uBlockOrigin/uBlock-issues/issues/2778

Regression from:
bb41d9594f

The regression occurred because the modified code made the assumption
that a leading combinator would never be preceded by whitespace, while
the parser didn't prevent this.

The parser has been fixed to ensure there is never a leading
whitespace in a selector.
This commit is contained in:
Raymond Hill 2023-08-15 10:07:42 -04:00
parent 79cf5f574c
commit fbc7a0e0ae
No known key found for this signature in database
GPG key ID: 25E1490B761470C2

View file

@ -3364,7 +3364,7 @@ class ExtSelectorCompiler {
out.push(`.${data.name}`);
break;
case 'Combinator':
out.push(data.name === ' ' ? ' ' : ` ${data.name} `);
out.push(data.name);
break;
case 'Identifier':
if ( this.reInvalidIdentifier.test(data.name) ) { return; }
@ -3436,6 +3436,28 @@ class ExtSelectorCompiler {
return out.join('');
}
astAppendPart(part, out) {
const { data } = part;
switch ( data.type ) {
case 'Combinator': {
const s = this.astSerializePart(part);
if ( s === undefined ) { return false; }
if ( out.length === 0 ) {
if ( s !== ' ' ) {
out.push(s, ' ');
}
} else {
out.push(' ');
if ( s !== ' ' ) {
out.push(s, ' ');
}
}
break;
}
}
return true;
}
astSerialize(parts, plainCSS = true) {
const out = [];
for ( const part of parts ) {
@ -3443,7 +3465,6 @@ class ExtSelectorCompiler {
switch ( data.type ) {
case 'AttributeSelector':
case 'ClassSelector':
case 'Combinator':
case 'Identifier':
case 'IdSelector':
case 'Nth':
@ -3455,6 +3476,9 @@ class ExtSelectorCompiler {
out.push(s);
break;
}
case 'Combinator':
if ( this.astAppendPart(part, out) === false ) { return; }
break;
case 'Raw':
if ( plainCSS ) { return; }
out.push(this.astSerializePart(part));
@ -3499,7 +3523,6 @@ class ExtSelectorCompiler {
}
case 'AttributeSelector':
case 'ClassSelector':
case 'Combinator':
case 'IdSelector':
case 'PseudoClassSelector':
case 'PseudoElementSelector':
@ -3509,6 +3532,10 @@ class ExtSelectorCompiler {
prelude.push(component);
break;
}
case 'Combinator': {
if ( this.astAppendPart(part, prelude) === false ) { return; }
break;
}
case 'ProceduralSelector': {
if ( prelude.length !== 0 ) {
let spath = prelude.join('');