Ensure compiled sections are ordered in ascending id

Related issue:
- https://www.reddit.com/r/uBlockOrigin/comments/oq6kt5/ubo_loads_generic_filter_instead_of_specific/h6a4nca/
This commit is contained in:
Raymond Hill 2021-07-24 07:44:26 -04:00
parent 2035475371
commit c25938f5bc
No known key found for this signature in database
GPG key ID: 25E1490B761470C2
2 changed files with 87 additions and 107 deletions

View file

@ -32,6 +32,9 @@ const cosmeticSurveyingMissCountMax =
parseInt(vAPI.localStorage.getItem('cosmeticSurveyingMissCountMax'), 10) || parseInt(vAPI.localStorage.getItem('cosmeticSurveyingMissCountMax'), 10) ||
15; 15;
const COMPILED_SPECIFIC_SECTION = 0;
const COMPILED_GENERIC_SECTION = 1;
/******************************************************************************/ /******************************************************************************/
/******************************************************************************/ /******************************************************************************/
@ -340,8 +343,6 @@ FilterContainer.prototype.keyFromSelector = function(selector) {
/******************************************************************************/ /******************************************************************************/
FilterContainer.prototype.compile = function(parser, writer) { FilterContainer.prototype.compile = function(parser, writer) {
writer.select(µb.compiledCosmeticSection);
if ( parser.hasOptions() === false ) { if ( parser.hasOptions() === false ) {
this.compileGenericSelector(parser, writer); this.compileGenericSelector(parser, writer);
return true; return true;
@ -368,6 +369,7 @@ FilterContainer.prototype.compile = function(parser, writer) {
/******************************************************************************/ /******************************************************************************/
FilterContainer.prototype.compileGenericSelector = function(parser, writer) { FilterContainer.prototype.compileGenericSelector = function(parser, writer) {
writer.select(µb.compiledCosmeticSection + COMPILED_GENERIC_SECTION);
if ( parser.isException() ) { if ( parser.isException() ) {
this.compileGenericUnhideSelector(parser, writer); this.compileGenericUnhideSelector(parser, writer);
} else { } else {
@ -509,6 +511,7 @@ FilterContainer.prototype.compileSpecificSelector = function(
not, not,
writer writer
) { ) {
writer.select(µb.compiledCosmeticSection + COMPILED_SPECIFIC_SECTION);
const { raw, compiled, exception } = parser.result; const { raw, compiled, exception } = parser.result;
if ( compiled === undefined ) { if ( compiled === undefined ) {
const who = writer.properties.get('assetKey') || '?'; const who = writer.properties.get('assetKey') || '?';
@ -551,18 +554,13 @@ FilterContainer.prototype.compileTemporary = function(parser) {
FilterContainer.prototype.fromCompiledContent = function(reader, options) { FilterContainer.prototype.fromCompiledContent = function(reader, options) {
if ( options.skipCosmetic ) { if ( options.skipCosmetic ) {
this.skipCompiledContent(reader); this.skipCompiledContent(reader, COMPILED_SPECIFIC_SECTION);
return; this.skipCompiledContent(reader, COMPILED_GENERIC_SECTION);
}
if ( options.skipGenericCosmetic ) {
this.skipGenericCompiledContent(reader);
return; return;
} }
reader.select(µb.compiledCosmeticSection); // Specific cosmetic filter section
reader.select(µb.compiledCosmeticSection + COMPILED_SPECIFIC_SECTION);
let db, bucket;
while ( reader.next() ) { while ( reader.next() ) {
this.acceptedCount += 1; this.acceptedCount += 1;
const fingerprint = reader.fingerprint(); const fingerprint = reader.fingerprint();
@ -571,56 +569,8 @@ FilterContainer.prototype.fromCompiledContent = function(reader, options) {
continue; continue;
} }
this.duplicateBuster.add(fingerprint); this.duplicateBuster.add(fingerprint);
const args = reader.args(); const args = reader.args();
switch ( args[0] ) { switch ( args[0] ) {
// low generic, simple
case 0: // #AdBanner
case 2: // .largeAd
db = args[0] === 0 ? this.lowlyGeneric.id : this.lowlyGeneric.cl;
bucket = db.complex.get(args[1]);
if ( bucket === undefined ) {
db.simple.add(args[1]);
} else if ( Array.isArray(bucket) ) {
bucket.push(db.prefix + args[1]);
} else {
db.complex.set(args[1], [ bucket, db.prefix + args[1] ]);
}
break;
// low generic, complex
case 1: // #tads + div + .c
case 3: // .Mpopup + #Mad > #MadZone
db = args[0] === 1 ? this.lowlyGeneric.id : this.lowlyGeneric.cl;
bucket = db.complex.get(args[1]);
if ( bucket === undefined ) {
if ( db.simple.has(args[1]) ) {
db.complex.set(args[1], [ db.prefix + args[1], args[2] ]);
} else {
db.complex.set(args[1], args[2]);
db.simple.add(args[1]);
}
} else if ( Array.isArray(bucket) ) {
bucket.push(args[2]);
} else {
db.complex.set(args[1], [ bucket, args[2] ]);
}
break;
// High-high generic hide/simple selectors
// div[id^="allo"]
case 4:
this.highlyGeneric.simple.dict.add(args[1]);
break;
// High-high generic hide/complex selectors
// div[id^="allo"] > span
case 5:
this.highlyGeneric.complex.dict.add(args[1]);
break;
// hash, example.com, .promoted-tweet // hash, example.com, .promoted-tweet
// hash, example.*, .promoted-tweet // hash, example.*, .promoted-tweet
// //
@ -639,19 +589,19 @@ FilterContainer.prototype.fromCompiledContent = function(reader, options) {
} }
this.specificFilters.store(args[1], args[2] & 0b011, args[3]); this.specificFilters.store(args[1], args[2] & 0b011, args[3]);
break; break;
default: default:
this.discardedCount += 1; this.discardedCount += 1;
break; break;
} }
} }
};
/******************************************************************************/ if ( options.skipGenericCosmetic ) {
this.skipCompiledContent(reader, COMPILED_GENERIC_SECTION);
FilterContainer.prototype.skipGenericCompiledContent = function(reader) { return;
reader.select(µb.compiledCosmeticSection); }
// Generic cosmetic filter section
reader.select(µb.compiledCosmeticSection + COMPILED_GENERIC_SECTION);
while ( reader.next() ) { while ( reader.next() ) {
this.acceptedCount += 1; this.acceptedCount += 1;
const fingerprint = reader.fingerprint(); const fingerprint = reader.fingerprint();
@ -659,28 +609,52 @@ FilterContainer.prototype.skipGenericCompiledContent = function(reader) {
this.discardedCount += 1; this.discardedCount += 1;
continue; continue;
} }
const args = reader.args();
switch ( args[0] ) {
// https://github.com/uBlockOrigin/uBlock-issues/issues/803
// Handle specific filters meant to apply everywhere, i.e. selectors
// not to be injected conditionally through the DOM surveyor.
// hash, *, .promoted-tweet
case 8:
this.duplicateBuster.add(fingerprint); this.duplicateBuster.add(fingerprint);
if ( args[2] === 0b100 ) { const args = reader.args();
if ( this.reSimpleHighGeneric.test(args[3]) ) switch ( args[0] ) {
this.highlyGeneric.simple.dict.add(args[3]); // low generic, simple
else { case 0: // #AdBanner
this.highlyGeneric.complex.dict.add(args[3]); case 2: { // .largeAd
const db = args[0] === 0 ? this.lowlyGeneric.id : this.lowlyGeneric.cl;
const bucket = db.complex.get(args[1]);
if ( bucket === undefined ) {
db.simple.add(args[1]);
} else if ( Array.isArray(bucket) ) {
bucket.push(db.prefix + args[1]);
} else {
db.complex.set(args[1], [ bucket, db.prefix + args[1] ]);
} }
break; break;
} }
this.specificFilters.store(args[1], args[2] & 0b011, args[3]); // low generic, complex
case 1: // #tads + div + .c
case 3: { // .Mpopup + #Mad > #MadZone
const db = args[0] === 1 ? this.lowlyGeneric.id : this.lowlyGeneric.cl;
const bucket = db.complex.get(args[1]);
if ( bucket === undefined ) {
if ( db.simple.has(args[1]) ) {
db.complex.set(args[1], [ db.prefix + args[1], args[2] ]);
} else {
db.complex.set(args[1], args[2]);
db.simple.add(args[1]);
}
} else if ( Array.isArray(bucket) ) {
bucket.push(args[2]);
} else {
db.complex.set(args[1], [ bucket, args[2] ]);
}
break;
}
// High-high generic hide/simple selectors
// div[id^="allo"]
case 4:
this.highlyGeneric.simple.dict.add(args[1]);
break;
// High-high generic hide/complex selectors
// div[id^="allo"] > span
case 5:
this.highlyGeneric.complex.dict.add(args[1]);
break; break;
default: default:
this.discardedCount += 1; this.discardedCount += 1;
break; break;
@ -690,9 +664,8 @@ FilterContainer.prototype.skipGenericCompiledContent = function(reader) {
/******************************************************************************/ /******************************************************************************/
FilterContainer.prototype.skipCompiledContent = function(reader) { FilterContainer.prototype.skipCompiledContent = function(reader, sectionId) {
reader.select(µb.compiledCosmeticSection); reader.select(µb.compiledCosmeticSection + sectionId);
while ( reader.next() ) { while ( reader.next() ) {
this.acceptedCount += 1; this.acceptedCount += 1;
this.discardedCount += 1; this.discardedCount += 1;

View file

@ -123,6 +123,11 @@
/******************************************************************************/ /******************************************************************************/
// https://www.reddit.com/r/uBlockOrigin/comments/oq6kt5/ubo_loads_generic_filter_instead_of_specific/
// Ensure blocks of content are sorted in ascending id order, such that the
// specific cosmetic filters will be found (and thus reported) before the
// generic ones.
µBlock.CompiledLineIO = { µBlock.CompiledLineIO = {
serialize: JSON.stringify, serialize: JSON.stringify,
unserialize: JSON.parse, unserialize: JSON.parse,
@ -156,8 +161,10 @@
return this; return this;
} }
toString() { toString() {
let result = []; const result = [];
for ( let [ id, lines ] of this.blocks ) { const sortedBlocks =
Array.from(this.blocks).sort((a, b) => a[0] - b[0]);
for ( const [ id, lines ] of sortedBlocks ) {
if ( lines.length === 0 ) { continue; } if ( lines.length === 0 ) { continue; }
result.push( result.push(
this.io.blockStartPrefix + id, this.io.blockStartPrefix + id,