Skip to content

Commit

Permalink
Ensure compiled sections are ordered in ascending id
Browse files Browse the repository at this point in the history
  • Loading branch information
gorhill committed Jul 24, 2021
1 parent 2035475 commit c25938f
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 90 deletions.
149 changes: 61 additions & 88 deletions src/js/cosmetic-filtering.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ const cosmeticSurveyingMissCountMax =
parseInt(vAPI.localStorage.getItem('cosmeticSurveyingMissCountMax'), 10) ||
15;

const COMPILED_SPECIFIC_SECTION = 0;
const COMPILED_GENERIC_SECTION = 1;

/******************************************************************************/
/******************************************************************************/

Expand Down Expand Up @@ -340,16 +343,14 @@ FilterContainer.prototype.keyFromSelector = function(selector) {
/******************************************************************************/

FilterContainer.prototype.compile = function(parser, writer) {
writer.select(µb.compiledCosmeticSection);

if ( parser.hasOptions() === false ) {
this.compileGenericSelector(parser, writer);
return true;
}

// https://github.com/chrisaljoudi/uBlock/issues/151
// Negated hostname means the filter applies to all non-negated hostnames
// of same filter OR globally if there is no non-negated hostnames.
// Negated hostname means the filter applies to all non-negated hostnames
// of same filter OR globally if there is no non-negated hostnames.
let applyGlobally = true;
for ( const { hn, not, bad } of parser.extOptions() ) {
if ( bad ) { continue; }
Expand All @@ -368,6 +369,7 @@ FilterContainer.prototype.compile = function(parser, writer) {
/******************************************************************************/

FilterContainer.prototype.compileGenericSelector = function(parser, writer) {
writer.select(µb.compiledCosmeticSection + COMPILED_GENERIC_SECTION);
if ( parser.isException() ) {
this.compileGenericUnhideSelector(parser, writer);
} else {
Expand Down Expand Up @@ -509,6 +511,7 @@ FilterContainer.prototype.compileSpecificSelector = function(
not,
writer
) {
writer.select(µb.compiledCosmeticSection + COMPILED_SPECIFIC_SECTION);
const { raw, compiled, exception } = parser.result;
if ( compiled === undefined ) {
const who = writer.properties.get('assetKey') || '?';
Expand Down Expand Up @@ -551,18 +554,13 @@ FilterContainer.prototype.compileTemporary = function(parser) {

FilterContainer.prototype.fromCompiledContent = function(reader, options) {
if ( options.skipCosmetic ) {
this.skipCompiledContent(reader);
this.skipCompiledContent(reader, COMPILED_SPECIFIC_SECTION);
this.skipCompiledContent(reader, COMPILED_GENERIC_SECTION);
return;
}
if ( options.skipGenericCosmetic ) {
this.skipGenericCompiledContent(reader);
return;
}

reader.select(µb.compiledCosmeticSection);

let db, bucket;

// Specific cosmetic filter section
reader.select(µb.compiledCosmeticSection + COMPILED_SPECIFIC_SECTION);
while ( reader.next() ) {
this.acceptedCount += 1;
const fingerprint = reader.fingerprint();
Expand All @@ -571,56 +569,8 @@ FilterContainer.prototype.fromCompiledContent = function(reader, options) {
continue;
}
this.duplicateBuster.add(fingerprint);

const args = reader.args();

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.*, .promoted-tweet
//
Expand All @@ -639,60 +589,83 @@ FilterContainer.prototype.fromCompiledContent = function(reader, options) {
}
this.specificFilters.store(args[1], args[2] & 0b011, args[3]);
break;

default:
this.discardedCount += 1;
break;
}
}
};

/******************************************************************************/

FilterContainer.prototype.skipGenericCompiledContent = function(reader) {
reader.select(µb.compiledCosmeticSection);
if ( options.skipGenericCosmetic ) {
this.skipCompiledContent(reader, COMPILED_GENERIC_SECTION);
return;
}

// Generic cosmetic filter section
reader.select(µb.compiledCosmeticSection + COMPILED_GENERIC_SECTION);
while ( reader.next() ) {
this.acceptedCount += 1;
const fingerprint = reader.fingerprint();
if ( this.duplicateBuster.has(fingerprint) ) {
this.discardedCount += 1;
continue;
}

this.duplicateBuster.add(fingerprint);
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);
if ( args[2] === 0b100 ) {
if ( this.reSimpleHighGeneric.test(args[3]) )
this.highlyGeneric.simple.dict.add(args[3]);
else {
this.highlyGeneric.complex.dict.add(args[3]);
// low generic, simple
case 0: // #AdBanner
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;
}
// 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]);
}
break;
} else if ( Array.isArray(bucket) ) {
bucket.push(args[2]);
} else {
db.complex.set(args[1], [ bucket, args[2] ]);
}
this.specificFilters.store(args[1], args[2] & 0b011, args[3]);
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;
default:
this.discardedCount += 1;
break;
}
}
}
};

/******************************************************************************/

FilterContainer.prototype.skipCompiledContent = function(reader) {
reader.select(µb.compiledCosmeticSection);

FilterContainer.prototype.skipCompiledContent = function(reader, sectionId) {
reader.select(µb.compiledCosmeticSection + sectionId);
while ( reader.next() ) {
this.acceptedCount += 1;
this.discardedCount += 1;
Expand Down
11 changes: 9 additions & 2 deletions src/js/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
serialize: JSON.stringify,
unserialize: JSON.parse,
Expand Down Expand Up @@ -156,8 +161,10 @@
return this;
}
toString() {
let result = [];
for ( let [ id, lines ] of this.blocks ) {
const result = [];
const sortedBlocks =
Array.from(this.blocks).sort((a, b) => a[0] - b[0]);
for ( const [ id, lines ] of sortedBlocks ) {
if ( lines.length === 0 ) { continue; }
result.push(
this.io.blockStartPrefix + id,
Expand Down

0 comments on commit c25938f

Please sign in to comment.