[mv3] Eliminate false positives in new generic cosmetic exception code

Related commit:
a009623d97
This commit is contained in:
Raymond Hill 2025-03-13 19:02:20 -04:00
parent 4032ba01bf
commit 68962453ff
No known key found for this signature in database
GPG key ID: 25E1490B761470C2
3 changed files with 84 additions and 38 deletions

View file

@ -29,6 +29,9 @@ const genericSelectorMap = self.genericSelectorMap || new Map();
self.genericSelectorMap = undefined;
if ( genericSelectorMap.size === 0 ) { return; }
const genericExceptionSieve = self.genericExceptionSieve || new Set();
self.genericExceptionSieve = undefined;
const genericExceptionMap = self.genericExceptionMap || new Map();
self.genericExceptionMap = undefined;
@ -78,8 +81,11 @@ const uBOL_idFromNode = (node, out) => {
const selectorList = genericSelectorMap.get(hash);
if ( selectorList === undefined ) { return; }
genericSelectorMap.delete(hash);
if ( isExcepted(hash) ) { return; }
out.push(selectorList);
if ( genericExceptionSieve.has(hash) ) {
applyExceptions(selectorList, out);
} else {
out.push(selectorList);
}
};
// https://github.com/uBlockOrigin/uBlock-issues/discussions/2076
@ -99,18 +105,26 @@ const uBOL_classesFromNode = (node, out) => {
const selectorList = genericSelectorMap.get(hash);
if ( selectorList === undefined ) { continue; }
genericSelectorMap.delete(hash);
if ( isExcepted(hash) ) { continue; }
out.push(selectorList);
if ( genericExceptionSieve.has(hash) ) {
applyExceptions(selectorList, out);
} else {
out.push(selectorList);
}
}
};
const isExcepted = hash => {
const hostnames = genericExceptionMap.get(hash);
if ( hostnames === undefined ) { return; }
const hasEntities = hostnames.includes('.*');
return self.isolatedAPI.forEachHostname((hostname) => {
if ( hostnames.includes(` ${hostname} `) ) { return true; }
}, { hasEntities });
const applyExceptions = (selectorList, out) => {
const selectors = new Set(selectorList.split(',\n'));
self.isolatedAPI.forEachHostname(hostname => {
const exceptions = genericExceptionMap.get(hostname);
if ( exceptions === undefined ) { return; }
for ( const exception of exceptions.split('\n') ) {
selectors.delete(exception);
}
if ( selectors.size === 0 ) { return true; }
}, { hasEntities: true });
if ( selectors.size === 0 ) { return; }
out.push(Array.from(selectors).join(',\n'));
}
/******************************************************************************/

View file

@ -700,25 +700,31 @@ async function processGenericCosmeticFilters(
if ( selectors === undefined ) {
genericSelectorMap.set(hash, selector)
} else {
genericSelectorMap.set(hash, `${selectors},${selector}`)
genericSelectorMap.set(hash, `${selectors},\n${selector}`)
}
}
}
// Specific exceptions
const genericExceptionSieve = new Set();
const genericExceptionMap = new Map();
if ( declarativeMap ) {
for ( const details of declarativeMap.values() ) {
for ( const [ exception, details ] of declarativeMap ) {
if ( details.rejected ) { continue; }
if ( details.key === undefined ) { continue; }
if ( details.matches !== undefined ) { continue; }
if ( details.excludeMatches === undefined ) { continue; }
const type = details.key.charCodeAt(0);
const hash = hashFromStr(type, details.key.slice(1));
const hostnames = genericExceptionMap.get(hash) ?? '';
genericExceptionMap.set(hash,
`${hostnames} ${details.excludeMatches.join(' ')} `
);
genericExceptionSieve.add(hash);
for ( const hn of details.excludeMatches ) {
const exceptions = genericExceptionMap.get(hn);
if ( exceptions === undefined ) {
genericExceptionMap.set(hn, exception);
} else {
genericExceptionMap.set(hn, `${exceptions}\n${exception}`);
}
}
}
}
@ -735,6 +741,10 @@ async function processGenericCosmeticFilters(
/\bself\.\$genericSelectorMap\$/,
`${JSON.stringify(genericSelectorMap, scriptletJsonReplacer)}`
);
patchedScriptlet = safeReplace(patchedScriptlet,
/\bself\.\$genericExceptionSieve\$/,
`${JSON.stringify(genericExceptionSieve, scriptletJsonReplacer)}`
);
patchedScriptlet = safeReplace(patchedScriptlet,
/\bself\.\$genericExceptionMap\$/,
`${JSON.stringify(genericExceptionMap, scriptletJsonReplacer)}`
@ -745,10 +755,10 @@ async function processGenericCosmeticFilters(
patchedScriptlet
);
log(`CSS-generic: ${genericExceptionSieve.size} specific CSS exceptions`);
log(`CSS-generic: ${genericSelectorMap.size} plain CSS selectors`);
log(`CSS-generic: ${genericExceptionMap.size} specific CSS exceptions`);
return genericSelectorMap.size + genericExceptionMap.size;
return genericSelectorMap.size + genericExceptionSieve.size;
}
const hashFromStr = (type, s) => {
@ -1491,6 +1501,9 @@ async function main() {
urls: [ 'https://ublockorigin.github.io/uBOL-home/tests/test-filters.txt' ],
homeURL: 'https://ublockorigin.github.io/uBOL-home/tests/test-filters.html',
filters: [
'###gcf #gcf1 .fail',
'###gcf #gcf2 .fail',
'ublockorigin.github.io,localhost#@##gcf #gcf2 .fail',
],
});

View file

@ -27,38 +27,57 @@
/******************************************************************************/
const selectorsToImport = self.$genericSelectorMap$;
const exceptionsToImport = self.$genericExceptionMap$;
const genericSelectorMap = self.$genericSelectorMap$;
const genericExceptionSieve = self.$genericExceptionSieve$;
const genericExceptionMap = self.$genericExceptionMap$;
if ( selectorsToImport ) {
if ( genericSelectorMap ) {
const map = self.genericSelectorMap =
self.genericSelectorMap || new Map();
if ( map.size !== 0 ) {
for ( const entry of selectorsToImport ) {
for ( const entry of genericSelectorMap ) {
const before = map.get(entry[0]);
map.set(entry[0],
before === undefined ? entry[1] : `${before},${entry[1]}`
);
if ( before === undefined ) {
map.set(entry[0], entry[1]);
} else {
map.set(entry[0], `${before},\n${entry[1]}`);
}
}
} else {
self.genericSelectorMap = new Map(selectorsToImport);
self.genericSelectorMap = new Map(genericSelectorMap);
}
selectorsToImport.length = 0;
genericSelectorMap.length = 0;
}
if ( exceptionsToImport ) {
const map = self.genericExceptionMap =
self.genericExceptionMap || new Map();
if ( map.size !== 0 ) {
for ( const entry of exceptionsToImport ) {
map.set(entry[0], `${map.get(entry[0]) || ''}${entry[1]}`);
if ( genericExceptionSieve ) {
const hashes = self.genericExceptionSieve =
self.genericExceptionSieve || new Set();
if ( hashes.size !== 0 ) {
for ( const hash of genericExceptionSieve ) {
hashes.add(hash);
}
} else {
self.genericExceptionMap = new Map(exceptionsToImport);
self.genericExceptionSieve = new Set(genericExceptionSieve);
}
exceptionsToImport.length = 0;
genericExceptionSieve.length = 0;
}
if ( genericExceptionMap ) {
const map = self.genericExceptionMap =
self.genericExceptionMap || new Map();
if ( map.size !== 0 ) {
for ( const entry of genericExceptionMap ) {
const before = map.get(entry[0]);
if ( before === undefined ) {
map.set(entry[0], entry[1]);
} else {
map.set(entry[0], `${before}\n${entry[1]}`);
}
}
} else {
self.genericExceptionMap = new Map(genericExceptionMap);
}
genericExceptionMap.length = 0;
}
/******************************************************************************/