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
1 changed files with 30 additions and 3 deletions

View File

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