mirror of
https://github.com/gorhill/uBlock.git
synced 2026-03-11 09:04:36 +00:00
[mv3] Merge all :style() filters with procedural filters
This commit is contained in:
parent
8ae7c1501b
commit
fed7f4a0b8
6 changed files with 77 additions and 320 deletions
|
|
@ -354,70 +354,6 @@ function registerProcedural(context) {
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
function registerDeclarative(context) {
|
||||
const { before, filteringModeDetails, rulesetsDetails } = context;
|
||||
|
||||
const js = [];
|
||||
for ( const rulesetDetails of rulesetsDetails ) {
|
||||
const count = rulesetDetails.css?.declarative || 0;
|
||||
if ( count === 0 ) { continue; }
|
||||
js.push(`/rulesets/scripting/declarative/${rulesetDetails.id}.js`);
|
||||
}
|
||||
if ( js.length === 0 ) { return; }
|
||||
|
||||
const { none, basic, optimal, complete } = filteringModeDetails;
|
||||
const matches = [
|
||||
...ut.matchesFromHostnames(optimal),
|
||||
...ut.matchesFromHostnames(complete),
|
||||
];
|
||||
if ( matches.length === 0 ) { return; }
|
||||
|
||||
normalizeMatches(matches);
|
||||
|
||||
js.unshift('/js/scripting/isolated-api.js');
|
||||
js.push('/js/scripting/css-declarative.js');
|
||||
|
||||
const excludeMatches = [];
|
||||
if ( none.has('all-urls') === false ) {
|
||||
excludeMatches.push(...ut.matchesFromHostnames(none));
|
||||
}
|
||||
if ( basic.has('all-urls') === false ) {
|
||||
excludeMatches.push(...ut.matchesFromHostnames(basic));
|
||||
}
|
||||
|
||||
const registered = before.get('css-declarative');
|
||||
before.delete('css-declarative'); // Important!
|
||||
|
||||
const directive = {
|
||||
id: 'css-declarative',
|
||||
js,
|
||||
matches,
|
||||
allFrames: true,
|
||||
runAt: 'document_start',
|
||||
};
|
||||
if ( excludeMatches.length !== 0 ) {
|
||||
directive.excludeMatches = excludeMatches;
|
||||
}
|
||||
|
||||
// register
|
||||
if ( registered === undefined ) {
|
||||
context.toAdd.push(directive);
|
||||
return;
|
||||
}
|
||||
|
||||
// update
|
||||
if (
|
||||
ut.strArrayEq(registered.js, js, false) === false ||
|
||||
ut.strArrayEq(registered.matches, matches) === false ||
|
||||
ut.strArrayEq(registered.excludeMatches, excludeMatches) === false
|
||||
) {
|
||||
context.toRemove.push('css-declarative');
|
||||
context.toAdd.push(directive);
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
function registerSpecific(context) {
|
||||
const { before, filteringModeDetails, rulesetsDetails } = context;
|
||||
|
||||
|
|
@ -602,7 +538,6 @@ async function registerInjectables() {
|
|||
};
|
||||
|
||||
await Promise.all([
|
||||
registerDeclarative(context),
|
||||
registerProcedural(context),
|
||||
registerScriptlet(context, scriptletDetails),
|
||||
registerSpecific(context),
|
||||
|
|
|
|||
|
|
@ -1,116 +0,0 @@
|
|||
/*******************************************************************************
|
||||
|
||||
uBlock Origin Lite - a comprehensive, MV3-compliant content blocker
|
||||
Copyright (C) 2014-present Raymond Hill
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
|
||||
Home: https://github.com/gorhill/uBlock
|
||||
*/
|
||||
|
||||
// Important!
|
||||
// Isolate from global scope
|
||||
(function uBOL_cssDeclarative() {
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const declarativeImports = self.declarativeImports || [];
|
||||
self.declarativeImports = undefined;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const selectors = [];
|
||||
const exceptions = [];
|
||||
|
||||
const lookupHostname = (hostname, details, out) => {
|
||||
let seqi = details.hostnamesMap.get(hostname);
|
||||
if ( seqi === undefined ) { return; }
|
||||
const { argsList, argsSeqs } = details;
|
||||
for (;;) {
|
||||
const argi = argsSeqs[seqi++];
|
||||
const done = argi > 0;
|
||||
out.push(...argsList[done ? argi : -argi].split('\n'));
|
||||
if ( done ) { break; }
|
||||
}
|
||||
};
|
||||
|
||||
const lookupAll = hostname => {
|
||||
for ( const details of declarativeImports ) {
|
||||
lookupHostname(hostname, details, selectors);
|
||||
lookupHostname(`~${hostname}`, details, exceptions);
|
||||
}
|
||||
};
|
||||
|
||||
self.isolatedAPI.forEachHostname(lookupAll, {
|
||||
hasEntities: declarativeImports.some(a => a.hasEntities)
|
||||
});
|
||||
|
||||
declarativeImports.length = 0;
|
||||
|
||||
const exceptedSelectors = exceptions.length !== 0
|
||||
? selectors.filter(a => exceptions.includes(a) === false)
|
||||
: selectors;
|
||||
if ( exceptedSelectors.length === 0 ) { return; }
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const cssRuleFromProcedural = details => {
|
||||
const { tasks, action } = details;
|
||||
let mq, selector;
|
||||
if ( Array.isArray(tasks) ) {
|
||||
if ( tasks[0][0] !== 'matches-media' ) { return; }
|
||||
mq = tasks[0][1];
|
||||
if ( tasks.length > 2 ) { return; }
|
||||
if ( tasks.length === 2 ) {
|
||||
if ( tasks[1][0] !== 'spath' ) { return; }
|
||||
selector = tasks[1][1];
|
||||
}
|
||||
}
|
||||
let style;
|
||||
if ( Array.isArray(action) ) {
|
||||
if ( action[0] !== 'style' ) { return; }
|
||||
selector = selector || details.selector;
|
||||
style = action[1];
|
||||
}
|
||||
if ( mq === undefined && style === undefined && selector === undefined ) { return; }
|
||||
if ( mq === undefined ) {
|
||||
return `${selector}\n{${style}}`;
|
||||
}
|
||||
if ( style === undefined ) {
|
||||
return `@media ${mq} {\n${selector}\n{display:none!important;}\n}`;
|
||||
}
|
||||
return `@media ${mq} {\n${selector}\n{${style}}\n}`;
|
||||
};
|
||||
|
||||
const sheetText = [];
|
||||
for ( const selector of exceptedSelectors ) {
|
||||
const details = JSON.parse(selector);
|
||||
const ruleText = cssRuleFromProcedural(details);
|
||||
if ( ruleText === undefined ) { continue; }
|
||||
sheetText.push(ruleText);
|
||||
}
|
||||
|
||||
if ( sheetText.length === 0 ) { return; }
|
||||
|
||||
chrome.runtime.sendMessage({
|
||||
what: 'insertCSS',
|
||||
css: sheetText.join('\n'),
|
||||
}).catch(( ) => {
|
||||
});
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
})();
|
||||
|
||||
void 0;
|
||||
|
|
@ -577,8 +577,7 @@ class ProceduralFilterer {
|
|||
}
|
||||
|
||||
addSelectors(selectors) {
|
||||
for ( const json of selectors ) {
|
||||
const selector = JSON.parse(json);
|
||||
for ( const selector of selectors ) {
|
||||
const pselector = new PSelectorRoot(selector);
|
||||
this.primeProceduralSelector(pselector);
|
||||
this.selectors.push(pselector);
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
|
||||
// Important!
|
||||
// Isolate from global scope
|
||||
(async function uBOL_cssProcedural() {
|
||||
(function uBOL_cssProcedural() {
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
|
@ -40,7 +40,7 @@ const lookupHostname = (hostname, details, out) => {
|
|||
for (;;) {
|
||||
const argi = argsSeqs[seqi++];
|
||||
const done = argi > 0;
|
||||
out.push(...argsList[done ? argi : -argi]);
|
||||
out.push(...JSON.parse(argsList[done ? argi : -argi]));
|
||||
if ( done ) { break; }
|
||||
}
|
||||
};
|
||||
|
|
@ -48,38 +48,88 @@ const lookupHostname = (hostname, details, out) => {
|
|||
const lookupAll = hostname => {
|
||||
for ( const details of proceduralImports ) {
|
||||
lookupHostname(hostname, details, selectors);
|
||||
lookupHostname(`~${hostname}`, details, exceptions);
|
||||
const matches = [];
|
||||
lookupHostname(`~${hostname}`, details, matches);
|
||||
if ( matches.length === 0 ) { continue; }
|
||||
exceptions.push(...matches.map(a => JSON.stringify(a)));
|
||||
}
|
||||
};
|
||||
|
||||
self.isolatedAPI.forEachHostname(lookupAll, {
|
||||
hasEntities: proceduralImports.some(a => a.hasEntities)
|
||||
});
|
||||
|
||||
proceduralImports.length = 0;
|
||||
|
||||
const exceptedSelectors = exceptions.length !== 0
|
||||
? selectors.filter(a => exceptions.includes(a) === false)
|
||||
? selectors.filter(a => exceptions.includes(JSON.stringify(a)) === false)
|
||||
: selectors;
|
||||
if ( exceptedSelectors.length === 0 ) { return; }
|
||||
|
||||
if ( self.cssProceduralAPI === undefined ) {
|
||||
self.cssProceduralAPI = chrome.runtime.sendMessage({
|
||||
what: 'injectCSSProceduralAPI'
|
||||
}).catch(( ) => {
|
||||
});
|
||||
const declaratives = exceptedSelectors.filter(a => a.cssable);
|
||||
if ( declaratives.length !== 0 ) {
|
||||
const cssRuleFromProcedural = details => {
|
||||
const { tasks, action } = details;
|
||||
let mq, selector;
|
||||
if ( Array.isArray(tasks) ) {
|
||||
if ( tasks[0][0] !== 'matches-media' ) { return; }
|
||||
mq = tasks[0][1];
|
||||
if ( tasks.length > 2 ) { return; }
|
||||
if ( tasks.length === 2 ) {
|
||||
if ( tasks[1][0] !== 'spath' ) { return; }
|
||||
selector = tasks[1][1];
|
||||
}
|
||||
}
|
||||
let style;
|
||||
if ( Array.isArray(action) ) {
|
||||
if ( action[0] !== 'style' ) { return; }
|
||||
selector = selector || details.selector;
|
||||
style = action[1];
|
||||
}
|
||||
if ( mq === undefined && style === undefined && selector === undefined ) { return; }
|
||||
if ( mq === undefined ) {
|
||||
return `${selector}\n{${style}}`;
|
||||
}
|
||||
if ( style === undefined ) {
|
||||
return `@media ${mq} {\n${selector}\n{display:none!important;}\n}`;
|
||||
}
|
||||
return `@media ${mq} {\n${selector}\n{${style}}\n}`;
|
||||
};
|
||||
const sheetText = [];
|
||||
for ( const details of declaratives ) {
|
||||
const ruleText = cssRuleFromProcedural(details);
|
||||
if ( ruleText === undefined ) { continue; }
|
||||
sheetText.push(ruleText);
|
||||
}
|
||||
if ( sheetText.length !== 0 ) {
|
||||
chrome.runtime.sendMessage({
|
||||
what: 'insertCSS',
|
||||
css: sheetText.join('\n'),
|
||||
}).catch(( ) => {
|
||||
});
|
||||
}
|
||||
}
|
||||
if ( self.cssProceduralAPI instanceof Promise ) {
|
||||
await self.cssProceduralAPI;
|
||||
}
|
||||
if ( self.cssProceduralAPI instanceof Object === false ) { return; }
|
||||
|
||||
self.cssProceduralAPI.addSelectors(exceptedSelectors);
|
||||
const procedurals = exceptedSelectors.filter(a => a.cssable === undefined);
|
||||
if ( procedurals.length !== 0 ) {
|
||||
const addSelectors = selectors => {
|
||||
if ( self.cssProceduralAPI instanceof Object === false ) { return; }
|
||||
self.cssProceduralAPI.addSelectors(selectors);
|
||||
};
|
||||
if ( self.cssProceduralAPI === undefined ) {
|
||||
self.cssProceduralAPI = chrome.runtime.sendMessage({
|
||||
what: 'injectCSSProceduralAPI'
|
||||
}).catch(( ) => {
|
||||
});
|
||||
}
|
||||
if ( self.cssProceduralAPI instanceof Promise ) {
|
||||
self.cssProceduralAPI.then(( ) => { addSelectors(procedurals); });
|
||||
} else {
|
||||
addSelectors(procedurals);
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
})();
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
void 0;
|
||||
|
|
|
|||
|
|
@ -1059,84 +1059,12 @@ async function processCosmeticFilters(assetDetails, mapin) {
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
async function processDeclarativeCosmeticFilters(assetDetails, mapin) {
|
||||
if ( mapin === undefined ) { return 0; }
|
||||
if ( mapin.size === 0 ) { return 0; }
|
||||
|
||||
// Distinguish declarative-compiled-as-procedural from actual procedural.
|
||||
const declaratives = new Map();
|
||||
mapin.forEach((details, jsonSelector) => {
|
||||
const selector = JSON.parse(jsonSelector);
|
||||
if ( selector.cssable !== true ) { return; }
|
||||
selector.cssable = undefined;
|
||||
declaratives.set(JSON.stringify(selector), details);
|
||||
});
|
||||
if ( declaratives.size === 0 ) { return 0; }
|
||||
|
||||
const contentArray = groupHostnamesBySelectors(
|
||||
groupSelectorsByHostnames(declaratives)
|
||||
);
|
||||
|
||||
const argsMap = contentArray.map(entry => [
|
||||
entry[0],
|
||||
entry[1].a ? entry[1].a.join('\n') : undefined,
|
||||
]);
|
||||
const hostnamesMap = new Map();
|
||||
let hasEntities = false;
|
||||
for ( const [ id, details ] of contentArray ) {
|
||||
if ( details.y ) {
|
||||
scriptletHostnameToIdMap(details.y, id, hostnamesMap);
|
||||
hasEntities ||= details.y.some(a => a.endsWith('.*'));
|
||||
}
|
||||
if ( details.n ) {
|
||||
scriptletHostnameToIdMap(details.n.map(a => `~${a}`), id, hostnamesMap);
|
||||
hasEntities ||= details.n.some(a => a.endsWith('.*'));
|
||||
}
|
||||
}
|
||||
const { argsList, argsSeqs } = argsMap2List(argsMap, hostnamesMap);
|
||||
|
||||
const originalScriptletMap = await loadAllSourceScriptlets();
|
||||
let patchedScriptlet = originalScriptletMap.get('css-declarative').replace(
|
||||
'$rulesetId$',
|
||||
assetDetails.id
|
||||
);
|
||||
patchedScriptlet = safeReplace(patchedScriptlet,
|
||||
/\bself\.\$argsList\$/,
|
||||
`${JSON.stringify(argsList, scriptletJsonReplacer)}`
|
||||
);
|
||||
patchedScriptlet = safeReplace(patchedScriptlet,
|
||||
/\bself\.\$argsSeqs\$/,
|
||||
`${JSON.stringify(argsSeqs, scriptletJsonReplacer)}`
|
||||
);
|
||||
patchedScriptlet = safeReplace(patchedScriptlet,
|
||||
/\bself\.\$hostnamesMap\$/,
|
||||
`${JSON.stringify(hostnamesMap, scriptletJsonReplacer)}`
|
||||
);
|
||||
patchedScriptlet = safeReplace(patchedScriptlet,
|
||||
'self.$hasEntities$',
|
||||
JSON.stringify(hasEntities)
|
||||
);
|
||||
writeFile(`${scriptletDir}/declarative/${assetDetails.id}.js`, patchedScriptlet);
|
||||
|
||||
if ( contentArray.length !== 0 ) {
|
||||
log(`CSS-declarative: ${declaratives.size} distinct filters`);
|
||||
log(`\tCombined into ${hostnamesMap.size} distinct hostnames`);
|
||||
}
|
||||
|
||||
return hostnamesMap.size;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
async function processProceduralCosmeticFilters(assetDetails, mapin) {
|
||||
if ( mapin === undefined ) { return 0; }
|
||||
if ( mapin.size === 0 ) { return 0; }
|
||||
|
||||
// Distinguish declarative-compiled-as-procedural from actual procedural.
|
||||
const procedurals = new Map();
|
||||
mapin.forEach((details, jsonSelector) => {
|
||||
const selector = JSON.parse(jsonSelector);
|
||||
if ( selector.cssable ) { return; }
|
||||
procedurals.set(jsonSelector, details);
|
||||
});
|
||||
if ( procedurals.size === 0 ) { return 0; }
|
||||
|
|
@ -1162,6 +1090,14 @@ async function processProceduralCosmeticFilters(assetDetails, mapin) {
|
|||
}
|
||||
}
|
||||
const { argsList, argsSeqs } = argsMap2List(argsMap, hostnamesMap);
|
||||
const argsListAfter = [];
|
||||
for ( const a of argsList ) {
|
||||
const aAfter = [];
|
||||
for ( let b of a ) {
|
||||
aAfter.push(JSON.parse(b));
|
||||
}
|
||||
argsListAfter.push(JSON.stringify(aAfter));
|
||||
}
|
||||
const originalScriptletMap = await loadAllSourceScriptlets();
|
||||
let patchedScriptlet = originalScriptletMap.get('css-procedural').replace(
|
||||
'$rulesetId$',
|
||||
|
|
@ -1169,7 +1105,7 @@ async function processProceduralCosmeticFilters(assetDetails, mapin) {
|
|||
);
|
||||
patchedScriptlet = safeReplace(patchedScriptlet,
|
||||
/\bself\.\$argsList\$/,
|
||||
`${JSON.stringify(argsList, scriptletJsonReplacer)}`
|
||||
`${JSON.stringify(argsListAfter, scriptletJsonReplacer)}`
|
||||
);
|
||||
patchedScriptlet = safeReplace(patchedScriptlet,
|
||||
/\bself\.\$argsSeqs\$/,
|
||||
|
|
@ -1333,10 +1269,6 @@ async function rulesetFromURLs(assetDetails) {
|
|||
assetDetails,
|
||||
declarativeCosmetic
|
||||
);
|
||||
const declarativeStats = await processDeclarativeCosmeticFilters(
|
||||
assetDetails,
|
||||
proceduralCosmetic
|
||||
);
|
||||
const proceduralStats = await processProceduralCosmeticFilters(
|
||||
assetDetails,
|
||||
proceduralCosmetic
|
||||
|
|
@ -1376,7 +1308,6 @@ async function rulesetFromURLs(assetDetails) {
|
|||
generic: genericCosmeticStats,
|
||||
generichigh: genericHighCosmeticStats,
|
||||
specific: specificCosmeticStats,
|
||||
declarative: declarativeStats,
|
||||
procedural: proceduralStats,
|
||||
},
|
||||
scriptlets: scriptletStats,
|
||||
|
|
|
|||
|
|
@ -1,42 +0,0 @@
|
|||
/*******************************************************************************
|
||||
|
||||
uBlock Origin Lite - a comprehensive, MV3-compliant content blocker
|
||||
Copyright (C) 2014-present Raymond Hill
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
|
||||
Home: https://github.com/gorhill/uBlock
|
||||
*/
|
||||
|
||||
// ruleset: $rulesetId$
|
||||
|
||||
// Important!
|
||||
// Isolate from global scope
|
||||
(function uBOL_cssDeclarativeImport() {
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const argsList = self.$argsList$;
|
||||
const argsSeqs = self.$argsSeqs$;
|
||||
const hostnamesMap = new Map(self.$hostnamesMap$);
|
||||
const hasEntities = self.$hasEntities$;
|
||||
|
||||
self.declarativeImports = self.declarativeImports || [];
|
||||
self.declarativeImports.push({ argsList, argsSeqs, hostnamesMap, hasEntities });
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
})();
|
||||
|
||||
/******************************************************************************/
|
||||
Loading…
Reference in a new issue