1
0
mirror of https://github.com/gorhill/uBlock.git synced 2024-09-04 01:59:38 +02:00

Fix parsing of cosmetic filters with empty-string attribute value

Related issue:
- https://github.com/uBlockOrigin/uBlock-issues/issues/2303
This commit is contained in:
Raymond Hill 2022-09-29 12:54:25 -04:00
parent 5e130d3391
commit 80e17af9fa
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2

View File

@ -1572,66 +1572,91 @@ Parser.prototype.SelectorCompiler = class {
// https://github.com/uBlockOrigin/uBlock-issues/issues/2300
// Unquoted atrtibute values are parsed as Identifier instead of String.
astSerializePart(part) {
const out = [];
const { data } = part;
switch ( data.type ) {
case 'AttributeSelector': {
const name = data.name.name;
if ( data.matcher === null ) {
out.push(`[${name}]`);
break;
}
let value = data.value.value;
if ( typeof value !== 'string' ) {
value = data.value.name;
}
out.push(`[${name}${data.matcher}"${value}"]`);
break;
}
case 'ClassSelector':
out.push(`.${data.name}`);
break;
case 'Combinator':
out.push(data.name === ' ' ? ' ' : ` ${data.name} `);
break;
case 'Identifier':
out.push(data.name);
break;
case 'IdSelector':
out.push(`#${data.name}`);
break;
case 'Nth': {
const a = parseInt(data.nth.a, 10) || null;
const b = parseInt(data.nth.b, 10) || null;
if ( a !== null ) {
out.push(`${a}n`);
if ( b === null ) { break; }
if ( b < 0 ) {
out.push(`${b}`);
} else {
out.push(`+${b}`);
}
} else if ( b !== null ) {
out.push(`${b}`);
}
break;
}
case 'PseudoClassSelector':
case 'PseudoElementSelector':
out.push(`:${data.name}`);
if ( Array.isArray(part.args) ) {
out.push(`(${this.astSerialize(part.args)})`);
}
break;
case 'Raw':
out.push(data.value);
break;
case 'TypeSelector':
out.push(data.name);
break;
default:
break;
}
return out.join('');
}
astSerialize(parts) {
const out = [];
for ( const part of parts ) {
const { data } = part;
switch ( data.type ) {
case 'AttributeSelector': {
const name = data.name.name;
if ( data.matcher === null ) {
out.push(`[${name}]`);
break;
}
const value = data.value.value || data.value.name;
out.push(`[${name}${data.matcher}"${value}"]`);
break;
}
case 'AttributeSelector':
case 'ClassSelector':
out.push(`.${data.name}`);
break;
case 'Combinator':
out.push(data.name === ' ' ? ' ' : ` ${data.name} `);
break;
case 'Identifier':
out.push(data.name);
break;
case 'IdSelector':
out.push(`#${data.name}`);
break;
case 'Nth': {
const a = parseInt(data.nth.a, 10) || null;
const b = parseInt(data.nth.b, 10) || null;
if ( a !== null ) {
out.push(`${a}n`);
if ( b === null ) { break; }
if ( b < 0 ) {
out.push(`${b}`);
} else {
out.push(`+${b}`);
}
} else if ( b !== null ) {
out.push(`${b}`);
}
break;
}
case 'ActionSelector':
case 'Nth':
case 'PseudoClassSelector':
case 'PseudoElementSelector':
out.push(`:${data.name}`);
if ( Array.isArray(part.args) ) {
out.push(`(${this.astSerialize(part.args)})`);
}
break;
case 'Raw':
out.push(data.value);
case 'TypeSelector':
out.push(this.astSerializePart(part));
break;
case 'Selector':
if ( out.length !== 0 ) { out.push(','); }
break;
case 'TypeSelector':
out.push(data.name);
break;
default:
break;
}
@ -1663,55 +1688,20 @@ Parser.prototype.SelectorCompiler = class {
out.action = [ data.name, args ];
break;
}
case 'AttributeSelector': {
const s = data.matcher
? `[${data.name.name}${data.matcher}"${data.value.value}"]`
: `[${data.name.name}]`;
prelude.push(s);
break;
}
case 'ClassSelector': {
const s = `.${data.name}`;
prelude.push(s);
break;
}
case 'Combinator': {
const s = data.name === ' ' ? ' ' : ` ${data.name} `;
prelude.push(s);
break;
}
case 'IdSelector': {
const s = `#${data.name}`;
prelude.push(s);
break;
}
case 'PseudoClassSelector': {
prelude.push(`:${data.name}`);
if ( Array.isArray(part.args) ) {
prelude.push(`(${this.astSerialize(part.args)})`);
}
break;
}
case 'PseudoElementSelector': {
prelude.push(`::${data.name}`);
if ( Array.isArray(part.args) ) {
prelude.push(`(${this.astSerialize(part.args)})`);
}
break;
}
case 'SelectorList':
case 'AttributeSelector':
case 'ClassSelector':
case 'Combinator':
case 'IdSelector':
case 'PseudoClassSelector':
case 'PseudoElementSelector':
case 'TypeSelector':
prelude.push(this.astSerializePart(part));
break;
case 'Selector':
if ( prelude.length !== 0 ) {
prelude.push(', ');
}
break;
case 'SelectorList':
break;
case 'TypeSelector': {
prelude.push(data.name);
break;
}
case 'ProceduralSelector':
if ( prelude.length !== 0 ) {
if ( tasks.length === 0 ) {