mirror of
https://github.com/gorhill/uBlock.git
synced 2024-11-10 01:02:08 +01:00
[mv3] Add support for highly generic cosmetic filters
Related issue: - https://github.com/uBlockOrigin/uBOL-issues/issues/54
This commit is contained in:
parent
1809a9b32c
commit
872eafa378
3 changed files with 136 additions and 11 deletions
|
@ -79,7 +79,12 @@ const arrayEq = (a = [], b = [], sort = true) => {
|
|||
|
||||
const normalizeRegisteredContentScripts = registered => {
|
||||
for ( const entry of registered ) {
|
||||
const { js } = entry;
|
||||
const { css = [], js = [] } = entry;
|
||||
for ( let i = 0; i < css.length; i++ ) {
|
||||
const path = css[i];
|
||||
if ( path.startsWith('/') ) { continue; }
|
||||
css[i] = `/${path}`;
|
||||
}
|
||||
for ( let i = 0; i < js.length; i++ ) {
|
||||
const path = js[i];
|
||||
if ( path.startsWith('/') ) { continue; }
|
||||
|
@ -91,6 +96,78 @@ const normalizeRegisteredContentScripts = registered => {
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
function registerHighGeneric(context, genericDetails) {
|
||||
const { before, filteringModeDetails, rulesetsDetails } = context;
|
||||
|
||||
const excludeHostnames = [];
|
||||
const css = [];
|
||||
for ( const details of rulesetsDetails ) {
|
||||
const hostnames = genericDetails.get(details.id);
|
||||
if ( hostnames !== undefined ) {
|
||||
excludeHostnames.push(...hostnames);
|
||||
}
|
||||
const count = details.css?.generichigh || 0;
|
||||
if ( count === 0 ) { continue; }
|
||||
css.push(`/rulesets/scripting/generichigh/${details.id}.css`);
|
||||
}
|
||||
|
||||
if ( css.length === 0 ) { return; }
|
||||
|
||||
const { none, basic, optimal, complete } = filteringModeDetails;
|
||||
const matches = [];
|
||||
const excludeMatches = [];
|
||||
if ( complete.has('all-urls') ) {
|
||||
excludeMatches.push(...ut.matchesFromHostnames(none));
|
||||
excludeMatches.push(...ut.matchesFromHostnames(basic));
|
||||
excludeMatches.push(...ut.matchesFromHostnames(optimal));
|
||||
excludeMatches.push(...ut.matchesFromHostnames(excludeHostnames));
|
||||
matches.push('<all_urls>');
|
||||
} else {
|
||||
matches.push(
|
||||
...ut.matchesFromHostnames(
|
||||
ut.subtractHostnameIters(
|
||||
Array.from(complete),
|
||||
excludeHostnames
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ( matches.length === 0 ) { return; }
|
||||
|
||||
const registered = before.get('css-generichigh');
|
||||
before.delete('css-generichigh'); // Important!
|
||||
|
||||
// https://github.com/w3c/webextensions/issues/414#issuecomment-1623992885
|
||||
// Once supported, add:
|
||||
// cssOrigin: 'USER',
|
||||
const directive = {
|
||||
id: 'css-generichigh',
|
||||
css,
|
||||
matches,
|
||||
excludeMatches,
|
||||
runAt: 'document_end',
|
||||
};
|
||||
|
||||
// register
|
||||
if ( registered === undefined ) {
|
||||
context.toAdd.push(directive);
|
||||
return;
|
||||
}
|
||||
|
||||
// update
|
||||
if (
|
||||
arrayEq(registered.css, css, false) === false ||
|
||||
arrayEq(registered.matches, matches) === false ||
|
||||
arrayEq(registered.excludeMatches, excludeMatches) === false
|
||||
) {
|
||||
context.toRemove.push('css-generichigh');
|
||||
context.toAdd.push(directive);
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
function registerGeneric(context, genericDetails) {
|
||||
const { before, filteringModeDetails, rulesetsDetails } = context;
|
||||
|
||||
|
@ -459,6 +536,7 @@ async function registerInjectables(origins) {
|
|||
registerScriptlet(context, scriptletDetails);
|
||||
registerSpecific(context);
|
||||
registerGeneric(context, genericDetails);
|
||||
registerHighGeneric(context, genericDetails);
|
||||
|
||||
toRemove.push(...Array.from(before.keys()));
|
||||
|
||||
|
|
|
@ -382,7 +382,8 @@ function loadAllSourceScriptlets() {
|
|||
const originalScriptletMap = new Map();
|
||||
for ( const details of results ) {
|
||||
originalScriptletMap.set(
|
||||
details.file.replace('.template.js', ''),
|
||||
details.file.replace('.template.js', '')
|
||||
.replace('.template.css', ''),
|
||||
details.text
|
||||
);
|
||||
}
|
||||
|
@ -395,7 +396,7 @@ function loadAllSourceScriptlets() {
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
async function processGenericCosmeticFilters(assetDetails, bucketsMap, exclusions) {
|
||||
async function processGenericCosmeticFilters(assetDetails, bucketsMap) {
|
||||
if ( bucketsMap === undefined ) { return 0; }
|
||||
if ( bucketsMap.size === 0 ) { return 0; }
|
||||
const bucketsList = Array.from(bucketsMap);
|
||||
|
@ -419,8 +420,6 @@ async function processGenericCosmeticFilters(assetDetails, bucketsMap, exclusion
|
|||
patchedScriptlet
|
||||
);
|
||||
|
||||
genericDetails.set(assetDetails.id, exclusions.sort());
|
||||
|
||||
log(`CSS-generic: ${count} plain CSS selectors`);
|
||||
|
||||
return count;
|
||||
|
@ -428,6 +427,33 @@ async function processGenericCosmeticFilters(assetDetails, bucketsMap, exclusion
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
async function processGenericHighCosmeticFilters(assetDetails, selectorSet) {
|
||||
if ( selectorSet === undefined ) { return 0; }
|
||||
if ( selectorSet.size === 0 ) { return 0; }
|
||||
const selectorLists = Array.from(selectorSet).sort().join(',\n');
|
||||
const originalScriptletMap = await loadAllSourceScriptlets();
|
||||
|
||||
let patchedScriptlet = originalScriptletMap.get('css-generichigh').replace(
|
||||
'$rulesetId$',
|
||||
assetDetails.id
|
||||
);
|
||||
patchedScriptlet = safeReplace(patchedScriptlet,
|
||||
/\$selectorList\$/,
|
||||
selectorLists
|
||||
);
|
||||
|
||||
writeFile(
|
||||
`${scriptletDir}/generichigh/${assetDetails.id}.css`,
|
||||
patchedScriptlet
|
||||
);
|
||||
|
||||
log(`CSS-generic-high: ${selectorSet.size} plain CSS selectors`);
|
||||
|
||||
return selectorSet.size;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// This merges selectors which are used by the same hostnames
|
||||
|
||||
function groupSelectorsByHostnames(mapin) {
|
||||
|
@ -887,10 +913,23 @@ async function rulesetFromURLs(assetDetails) {
|
|||
log(rejectedCosmetic.map(line => `\t${line}`).join('\n'), true);
|
||||
}
|
||||
|
||||
if (
|
||||
Array.isArray(results.network.generichideExclusions) &&
|
||||
results.network.generichideExclusions.length !== 0
|
||||
) {
|
||||
genericDetails.set(
|
||||
assetDetails.id,
|
||||
results.network.generichideExclusions.filter(hn => hn.endsWith('.*') === false).sort()
|
||||
);
|
||||
}
|
||||
|
||||
const genericCosmeticStats = await processGenericCosmeticFilters(
|
||||
assetDetails,
|
||||
results.genericCosmetic,
|
||||
results.network.generichideExclusions.filter(hn => hn.endsWith('.*') === false)
|
||||
results.genericCosmetic
|
||||
);
|
||||
const genericHighCosmeticStats = await processGenericHighCosmeticFilters(
|
||||
assetDetails,
|
||||
results.genericHighCosmetic
|
||||
);
|
||||
const specificCosmeticStats = await processCosmeticFilters(
|
||||
assetDetails,
|
||||
|
@ -933,6 +972,7 @@ async function rulesetFromURLs(assetDetails) {
|
|||
},
|
||||
css: {
|
||||
generic: genericCosmeticStats,
|
||||
generichigh: genericHighCosmeticStats,
|
||||
specific: specificCosmeticStats,
|
||||
declarative: declarativeStats,
|
||||
procedural: proceduralStats,
|
||||
|
|
|
@ -147,17 +147,23 @@ function addExtendedToDNR(context, parser) {
|
|||
|
||||
// Generic cosmetic filtering
|
||||
if ( parser.hasOptions() === false ) {
|
||||
if ( context.genericCosmeticFilters === undefined ) {
|
||||
context.genericCosmeticFilters = new Map();
|
||||
}
|
||||
const { compiled } = parser.result;
|
||||
if ( compiled === undefined ) { return; }
|
||||
if ( compiled.length <= 1 ) { return; }
|
||||
if ( compiled.charCodeAt(0) === 0x7B /* '{' */ ) { return; }
|
||||
const key = keyFromSelector(compiled);
|
||||
if ( key === undefined ) { return; }
|
||||
if ( key === undefined ) {
|
||||
if ( context.genericHighCosmeticFilters === undefined ) {
|
||||
context.genericHighCosmeticFilters = new Set();
|
||||
}
|
||||
context.genericHighCosmeticFilters.add(compiled);
|
||||
return;
|
||||
}
|
||||
const type = key.charCodeAt(0);
|
||||
const hash = hashFromStr(type, key.slice(1));
|
||||
if ( context.genericCosmeticFilters === undefined ) {
|
||||
context.genericCosmeticFilters = new Map();
|
||||
}
|
||||
let bucket = context.genericCosmeticFilters.get(hash);
|
||||
if ( bucket === undefined ) {
|
||||
context.genericCosmeticFilters.set(hash, bucket = []);
|
||||
|
@ -291,6 +297,7 @@ async function dnrRulesetFromRawLists(lists, options = {}) {
|
|||
return {
|
||||
network: staticNetFilteringEngine.dnrFromCompiled('end', context),
|
||||
genericCosmetic: context.genericCosmeticFilters,
|
||||
genericHighCosmetic: context.genericHighCosmeticFilters,
|
||||
specificCosmetic: context.specificCosmeticFilters,
|
||||
scriptlet: context.scriptletFilters,
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue