mirror of
https://github.com/gorhill/uBlock.git
synced 2026-03-11 09:04:36 +00:00
[mv3] Improve generic cosmetic filtering
Specifically, properly exclude generic cosmetic filters according to specific cosmetic exceptions. Related issue: https://github.com/uBlockOrigin/uBOL-home/issues/181
This commit is contained in:
parent
22fdf8fc1d
commit
a009623d97
12 changed files with 413 additions and 414 deletions
|
|
@ -179,6 +179,7 @@ function registerGeneric(context, genericDetails) {
|
|||
|
||||
if ( js.length === 0 ) { return; }
|
||||
|
||||
js.unshift('/js/scripting/isolated-api.js');
|
||||
js.push('/js/scripting/css-generic.js');
|
||||
|
||||
const { none, basic, optimal, complete } = filteringModeDetails;
|
||||
|
|
@ -252,6 +253,7 @@ function registerProcedural(context) {
|
|||
];
|
||||
if ( matches.length === 0 ) { return; }
|
||||
|
||||
js.unshift('/js/scripting/isolated-api.js');
|
||||
js.push('/js/scripting/css-procedural.js');
|
||||
|
||||
const excludeMatches = [];
|
||||
|
|
@ -311,6 +313,7 @@ function registerDeclarative(context) {
|
|||
];
|
||||
if ( matches.length === 0 ) { return; }
|
||||
|
||||
js.unshift('/js/scripting/isolated-api.js');
|
||||
js.push('/js/scripting/css-declarative.js');
|
||||
|
||||
const excludeMatches = [];
|
||||
|
|
@ -370,6 +373,7 @@ function registerSpecific(context) {
|
|||
];
|
||||
if ( matches.length === 0 ) { return; }
|
||||
|
||||
js.unshift('/js/scripting/isolated-api.js');
|
||||
js.push('/js/scripting/css-specific.js');
|
||||
|
||||
const excludeMatches = [];
|
||||
|
|
|
|||
|
|
@ -27,73 +27,41 @@
|
|||
|
||||
const declarativeImports = self.declarativeImports || [];
|
||||
self.declarativeImports = undefined;
|
||||
delete self.declarativeImports;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const hnParts = [];
|
||||
try { hnParts.push(...document.location.hostname.split('.')); }
|
||||
catch { }
|
||||
const hnpartslen = hnParts.length;
|
||||
if ( hnpartslen === 0 ) { return; }
|
||||
|
||||
const selectors = [];
|
||||
const exceptions = [];
|
||||
|
||||
for ( const { argsList, exceptionsMap, hostnamesMap, entitiesMap } of declarativeImports ) {
|
||||
const todoIndices = new Set();
|
||||
const tonotdoIndices = [];
|
||||
// Exceptions
|
||||
if ( exceptionsMap.size !== 0 ) {
|
||||
for ( let i = 0; i < hnpartslen; i++ ) {
|
||||
const hn = hnParts.slice(i).join('.');
|
||||
const excepted = exceptionsMap.get(hn);
|
||||
if ( excepted ) { tonotdoIndices.push(...excepted); }
|
||||
}
|
||||
exceptionsMap.clear();
|
||||
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; }
|
||||
}
|
||||
// Hostname-based
|
||||
if ( hostnamesMap.size !== 0 ) {
|
||||
const collectArgIndices = hn => {
|
||||
let argsIndices = hostnamesMap.get(hn);
|
||||
if ( argsIndices === undefined ) { return; }
|
||||
if ( typeof argsIndices === 'number' ) { argsIndices = [ argsIndices ]; }
|
||||
for ( const argsIndex of argsIndices ) {
|
||||
if ( tonotdoIndices.includes(argsIndex) ) { continue; }
|
||||
todoIndices.add(argsIndex);
|
||||
}
|
||||
};
|
||||
for ( let i = 0; i < hnpartslen; i++ ) {
|
||||
const hn = hnParts.slice(i).join('.');
|
||||
collectArgIndices(hn);
|
||||
}
|
||||
collectArgIndices('*');
|
||||
hostnamesMap.clear();
|
||||
};
|
||||
|
||||
const lookupAll = hostname => {
|
||||
for ( const details of declarativeImports ) {
|
||||
lookupHostname(hostname, details, selectors);
|
||||
lookupHostname(`~${hostname}`, details, exceptions);
|
||||
}
|
||||
// Entity-based
|
||||
if ( entitiesMap.size !== 0 ) {
|
||||
const n = hnpartslen - 1;
|
||||
for ( let i = 0; i < n; i++ ) {
|
||||
for ( let j = n; j > i; j-- ) {
|
||||
const en = hnParts.slice(i,j).join('.');
|
||||
let argsIndices = entitiesMap.get(en);
|
||||
if ( argsIndices === undefined ) { continue; }
|
||||
if ( typeof argsIndices === 'number' ) { argsIndices = [ argsIndices ]; }
|
||||
for ( const argsIndex of argsIndices ) {
|
||||
if ( tonotdoIndices.includes(argsIndex) ) { continue; }
|
||||
todoIndices.add(argsIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
entitiesMap.clear();
|
||||
}
|
||||
for ( const i of todoIndices ) {
|
||||
selectors.push(...argsList[i].map(json => JSON.parse(json)));
|
||||
}
|
||||
argsList.length = 0;
|
||||
}
|
||||
};
|
||||
|
||||
self.isolatedAPI.forEachHostname(lookupAll, {
|
||||
hasEntities: declarativeImports.some(a => a.hasEntities)
|
||||
});
|
||||
|
||||
declarativeImports.length = 0;
|
||||
|
||||
if ( selectors.length === 0 ) { return; }
|
||||
const exceptedSelectors = exceptions.length !== 0
|
||||
? selectors.filter(a => exceptions.includes(a) === false)
|
||||
: selectors;
|
||||
if ( exceptedSelectors.length === 0 ) { return; }
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
|
@ -126,8 +94,9 @@ const cssRuleFromProcedural = details => {
|
|||
};
|
||||
|
||||
const sheetText = [];
|
||||
for ( const selector of selectors ) {
|
||||
const ruleText = cssRuleFromProcedural(selector);
|
||||
for ( const selector of exceptedSelectors ) {
|
||||
const details = JSON.parse(selector);
|
||||
const ruleText = cssRuleFromProcedural(details);
|
||||
if ( ruleText === undefined ) { continue; }
|
||||
sheetText.push(ruleText);
|
||||
}
|
||||
|
|
@ -138,7 +107,7 @@ if ( sheetText.length === 0 ) { return; }
|
|||
chrome.runtime.sendMessage({ what: 'insertCSS', css }).catch(( ) => {
|
||||
count -= 1;
|
||||
if ( count === 0 ) { return; }
|
||||
uBOL_injectCSS(css, count - 1);
|
||||
uBOL_injectCSS(css, count);
|
||||
});
|
||||
})(sheetText.join('\n'));
|
||||
|
||||
|
|
|
|||
|
|
@ -26,10 +26,12 @@
|
|||
(function uBOL_cssGeneric() {
|
||||
|
||||
const genericSelectorMap = self.genericSelectorMap || new Map();
|
||||
delete self.genericSelectorMap;
|
||||
|
||||
self.genericSelectorMap = undefined;
|
||||
if ( genericSelectorMap.size === 0 ) { return; }
|
||||
|
||||
const genericExceptionMap = self.genericExceptionMap || new Map();
|
||||
self.genericExceptionMap = undefined;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const maxSurveyTimeSlice = 4;
|
||||
|
|
@ -76,6 +78,7 @@ const uBOL_idFromNode = (node, out) => {
|
|||
const selectorList = genericSelectorMap.get(hash);
|
||||
if ( selectorList === undefined ) { return; }
|
||||
genericSelectorMap.delete(hash);
|
||||
if ( isExcepted(hash) ) { return; }
|
||||
out.push(selectorList);
|
||||
};
|
||||
|
||||
|
|
@ -96,10 +99,20 @@ const uBOL_classesFromNode = (node, out) => {
|
|||
const selectorList = genericSelectorMap.get(hash);
|
||||
if ( selectorList === undefined ) { continue; }
|
||||
genericSelectorMap.delete(hash);
|
||||
if ( isExcepted(hash) ) { continue; }
|
||||
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 pendingNodes = {
|
||||
|
|
@ -191,12 +204,26 @@ const uBOL_injectCSS = (css, count = 10) => {
|
|||
chrome.runtime.sendMessage({ what: 'insertCSS', css }).catch(( ) => {
|
||||
count -= 1;
|
||||
if ( count === 0 ) { return; }
|
||||
uBOL_injectCSS(css, count - 1);
|
||||
uBOL_injectCSS(css, count);
|
||||
});
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const stopAll = reason => {
|
||||
if ( domChangeTimer !== undefined ) {
|
||||
self.clearTimeout(domChangeTimer);
|
||||
domChangeTimer = undefined;
|
||||
}
|
||||
domMutationObserver.disconnect();
|
||||
domMutationObserver.takeRecords();
|
||||
domMutationObserver = undefined;
|
||||
genericSelectorMap.clear();
|
||||
console.info(`uBOL: Generic cosmetic filtering stopped because ${reason}`);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
pendingNodes.add(document);
|
||||
uBOL_processNodes();
|
||||
|
||||
|
|
@ -219,20 +246,6 @@ needDomChangeObserver();
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
const stopAll = reason => {
|
||||
if ( domChangeTimer !== undefined ) {
|
||||
self.clearTimeout(domChangeTimer);
|
||||
domChangeTimer = undefined;
|
||||
}
|
||||
domMutationObserver.disconnect();
|
||||
domMutationObserver.takeRecords();
|
||||
domMutationObserver = undefined;
|
||||
genericSelectorMap.clear();
|
||||
console.info(`uBOL: Generic cosmetic filtering stopped because ${reason}`);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
})();
|
||||
|
||||
/******************************************************************************/
|
||||
|
|
|
|||
|
|
@ -27,73 +27,41 @@
|
|||
|
||||
const proceduralImports = self.proceduralImports || [];
|
||||
self.proceduralImports = undefined;
|
||||
delete self.proceduralImports;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const hnParts = [];
|
||||
try { hnParts.push(...document.location.hostname.split('.')); }
|
||||
catch { }
|
||||
const hnpartslen = hnParts.length;
|
||||
if ( hnpartslen === 0 ) { return; }
|
||||
|
||||
const selectors = [];
|
||||
const exceptions = [];
|
||||
|
||||
for ( const { argsList, exceptionsMap, hostnamesMap, entitiesMap } of proceduralImports ) {
|
||||
const todoIndices = new Set();
|
||||
const tonotdoIndices = [];
|
||||
// Exceptions
|
||||
if ( exceptionsMap.size !== 0 ) {
|
||||
for ( let i = 0; i < hnpartslen; i++ ) {
|
||||
const hn = hnParts.slice(i).join('.');
|
||||
const excepted = exceptionsMap.get(hn);
|
||||
if ( excepted ) { tonotdoIndices.push(...excepted); }
|
||||
}
|
||||
exceptionsMap.clear();
|
||||
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]);
|
||||
if ( done ) { break; }
|
||||
}
|
||||
// Hostname-based
|
||||
if ( hostnamesMap.size !== 0 ) {
|
||||
const collectArgIndices = hn => {
|
||||
let argsIndices = hostnamesMap.get(hn);
|
||||
if ( argsIndices === undefined ) { return; }
|
||||
if ( typeof argsIndices === 'number' ) { argsIndices = [ argsIndices ]; }
|
||||
for ( const argsIndex of argsIndices ) {
|
||||
if ( tonotdoIndices.includes(argsIndex) ) { continue; }
|
||||
todoIndices.add(argsIndex);
|
||||
}
|
||||
};
|
||||
for ( let i = 0; i < hnpartslen; i++ ) {
|
||||
const hn = hnParts.slice(i).join('.');
|
||||
collectArgIndices(hn);
|
||||
}
|
||||
collectArgIndices('*');
|
||||
hostnamesMap.clear();
|
||||
};
|
||||
|
||||
const lookupAll = hostname => {
|
||||
for ( const details of proceduralImports ) {
|
||||
lookupHostname(hostname, details, selectors);
|
||||
lookupHostname(`~${hostname}`, details, exceptions);
|
||||
}
|
||||
// Entity-based
|
||||
if ( entitiesMap.size !== 0 ) {
|
||||
const n = hnpartslen - 1;
|
||||
for ( let i = 0; i < n; i++ ) {
|
||||
for ( let j = n; j > i; j-- ) {
|
||||
const en = hnParts.slice(i,j).join('.');
|
||||
let argsIndices = entitiesMap.get(en);
|
||||
if ( argsIndices === undefined ) { continue; }
|
||||
if ( typeof argsIndices === 'number' ) { argsIndices = [ argsIndices ]; }
|
||||
for ( const argsIndex of argsIndices ) {
|
||||
if ( tonotdoIndices.includes(argsIndex) ) { continue; }
|
||||
todoIndices.add(argsIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
entitiesMap.clear();
|
||||
}
|
||||
for ( const i of todoIndices ) {
|
||||
selectors.push(...argsList[i].map(json => JSON.parse(json)));
|
||||
}
|
||||
argsList.length = 0;
|
||||
}
|
||||
};
|
||||
|
||||
self.isolatedAPI.forEachHostname(lookupAll, {
|
||||
hasEntities: proceduralImports.some(a => a.hasEntities)
|
||||
});
|
||||
|
||||
proceduralImports.length = 0;
|
||||
|
||||
if ( selectors.length === 0 ) { return; }
|
||||
const exceptedSelectors = exceptions.length !== 0
|
||||
? selectors.filter(a => exceptions.includes(a) === false)
|
||||
: selectors;
|
||||
if ( exceptedSelectors.length === 0 ) { return; }
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
|
@ -101,7 +69,7 @@ const uBOL_injectCSS = (css, count = 10) => {
|
|||
chrome.runtime.sendMessage({ what: 'insertCSS', css }).catch(( ) => {
|
||||
count -= 1;
|
||||
if ( count === 0 ) { return; }
|
||||
uBOL_injectCSS(css, count - 1);
|
||||
uBOL_injectCSS(css, count);
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -646,7 +614,7 @@ class ProceduralFilterer {
|
|||
this.uBOL_commitNow();
|
||||
}
|
||||
|
||||
addSelectors() {
|
||||
addSelectors(selectors) {
|
||||
for ( const selector of selectors ) {
|
||||
const pselector = new PSelectorRoot(selector);
|
||||
this.primeProceduralSelector(pselector);
|
||||
|
|
@ -787,7 +755,9 @@ class ProceduralFilterer {
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
const proceduralFilterer = new ProceduralFilterer(selectors);
|
||||
const proceduralFilterer = new ProceduralFilterer(
|
||||
exceptedSelectors.map(a => JSON.parse(a))
|
||||
);
|
||||
|
||||
const observer = new MutationObserver(mutations => {
|
||||
let domChanged = false;
|
||||
|
|
|
|||
|
|
@ -27,73 +27,41 @@
|
|||
|
||||
const specificImports = self.specificImports || [];
|
||||
self.specificImports = undefined;
|
||||
delete self.specificImports;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const hnParts = [];
|
||||
try { hnParts.push(...document.location.hostname.split('.')); }
|
||||
catch { }
|
||||
const hnpartslen = hnParts.length;
|
||||
if ( hnpartslen === 0 ) { return; }
|
||||
|
||||
const selectors = [];
|
||||
const exceptions = [];
|
||||
|
||||
for ( const { argsList, exceptionsMap, hostnamesMap, entitiesMap } of specificImports ) {
|
||||
const todoIndices = new Set();
|
||||
const tonotdoIndices = [];
|
||||
// Exceptions
|
||||
if ( exceptionsMap.size !== 0 ) {
|
||||
for ( let i = 0; i < hnpartslen; i++ ) {
|
||||
const hn = hnParts.slice(i).join('.');
|
||||
const excepted = exceptionsMap.get(hn);
|
||||
if ( excepted ) { tonotdoIndices.push(...excepted); }
|
||||
}
|
||||
exceptionsMap.clear();
|
||||
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; }
|
||||
}
|
||||
// Hostname-based
|
||||
if ( hostnamesMap.size !== 0 ) {
|
||||
const collectArgIndices = hn => {
|
||||
let argsIndices = hostnamesMap.get(hn);
|
||||
if ( argsIndices === undefined ) { return; }
|
||||
if ( typeof argsIndices === 'number' ) { argsIndices = [ argsIndices ]; }
|
||||
for ( const argsIndex of argsIndices ) {
|
||||
if ( tonotdoIndices.includes(argsIndex) ) { continue; }
|
||||
todoIndices.add(argsIndex);
|
||||
}
|
||||
};
|
||||
for ( let i = 0; i < hnpartslen; i++ ) {
|
||||
const hn = hnParts.slice(i).join('.');
|
||||
collectArgIndices(hn);
|
||||
}
|
||||
collectArgIndices('*');
|
||||
hostnamesMap.clear();
|
||||
};
|
||||
|
||||
const lookupAll = hostname => {
|
||||
for ( const details of specificImports ) {
|
||||
lookupHostname(hostname, details, selectors);
|
||||
lookupHostname(`~${hostname}`, details, exceptions);
|
||||
}
|
||||
// Entity-based
|
||||
if ( entitiesMap.size !== 0 ) {
|
||||
const n = hnpartslen - 1;
|
||||
for ( let i = 0; i < n; i++ ) {
|
||||
for ( let j = n; j > i; j-- ) {
|
||||
const en = hnParts.slice(i,j).join('.');
|
||||
let argsIndices = entitiesMap.get(en);
|
||||
if ( argsIndices === undefined ) { continue; }
|
||||
if ( typeof argsIndices === 'number' ) { argsIndices = [ argsIndices ]; }
|
||||
for ( const argsIndex of argsIndices ) {
|
||||
if ( tonotdoIndices.includes(argsIndex) ) { continue; }
|
||||
todoIndices.add(argsIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
entitiesMap.clear();
|
||||
}
|
||||
for ( const i of todoIndices ) {
|
||||
selectors.push(argsList[i]);
|
||||
}
|
||||
argsList.length = 0;
|
||||
}
|
||||
};
|
||||
|
||||
self.isolatedAPI.forEachHostname(lookupAll, {
|
||||
hasEntities: specificImports.some(a => a.hasEntities)
|
||||
});
|
||||
|
||||
specificImports.length = 0;
|
||||
|
||||
if ( selectors.length === 0 ) { return; }
|
||||
const exceptedSelectors = exceptions.length !== 0
|
||||
? selectors.filter(a => exceptions.includes(a) === false)
|
||||
: selectors;
|
||||
if ( exceptedSelectors.length === 0 ) { return; }
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
|
@ -101,9 +69,9 @@ if ( selectors.length === 0 ) { return; }
|
|||
chrome.runtime.sendMessage({ what: 'insertCSS', css }).catch(( ) => {
|
||||
count -= 1;
|
||||
if ( count === 0 ) { return; }
|
||||
uBOL_injectCSS(css, count - 1);
|
||||
uBOL_injectCSS(css, count);
|
||||
});
|
||||
})(`${selectors.join(',')}{display:none!important;}`);
|
||||
})(`${exceptedSelectors.join(',')}{display:none!important;}`);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
|
|
|||
80
platform/mv3/extension/js/scripting/isolated-api.js
Normal file
80
platform/mv3/extension/js/scripting/isolated-api.js
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
/*******************************************************************************
|
||||
|
||||
uBlock Origin Lite - a comprehensive, MV3-compliant content blocker
|
||||
Copyright (C) 2022-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/
|
||||
|
||||
*/
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
(api => {
|
||||
if ( typeof api === 'object' ) { return; }
|
||||
|
||||
const isolatedAPI = self.isolatedAPI = {};
|
||||
|
||||
const hostnameStack = (( ) => {
|
||||
const docloc = document.location;
|
||||
const origins = [ docloc.origin ];
|
||||
if ( docloc.ancestorOrigins ) {
|
||||
origins.push(...docloc.ancestorOrigins);
|
||||
}
|
||||
return origins.map((origin, i) => {
|
||||
const beg = origin.lastIndexOf('://');
|
||||
if ( beg === -1 ) { return; }
|
||||
const hn1 = origin.slice(beg+3)
|
||||
const end = hn1.indexOf(':');
|
||||
const hn2 = end === -1 ? hn1 : hn1.slice(0, end);
|
||||
return { hnparts: hn2.split('.'), i };
|
||||
}).filter(a => a !== undefined);
|
||||
})();
|
||||
|
||||
const forEachHostname = (entry, callback, details) => {
|
||||
const hnparts = entry.hnparts;
|
||||
const hnpartslen = hnparts.length;
|
||||
if ( hnpartslen === 0 ) { return; }
|
||||
for ( let i = 0; i < hnpartslen; i++ ) {
|
||||
const r = callback(`${hnparts.slice(i).join('.')}`, details);
|
||||
if ( r !== undefined ) { return r; }
|
||||
}
|
||||
if ( details.hasEntities !== true ) { return; }
|
||||
const n = hnpartslen - 1;
|
||||
for ( let i = 0; i < n; i++ ) {
|
||||
for ( let j = n; j > i; j-- ) {
|
||||
const r = callback(`${hnparts.slice(i,j).join('.')}.*`, details);
|
||||
if ( r !== undefined ) { return r; }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
isolatedAPI.forEachHostname = (callback, details) => {
|
||||
if ( hostnameStack.length === 0 ) { return; }
|
||||
return forEachHostname(hostnameStack[0], callback, details);
|
||||
};
|
||||
|
||||
isolatedAPI.forEachHostnameAncestors = (callback, details) => {
|
||||
for ( const entry of hostnameStack ) {
|
||||
if ( entry.i === 0 ) { continue; }
|
||||
const r = forEachHostname(entry, callback, details);
|
||||
if ( r !== undefined ) { return r; }
|
||||
}
|
||||
};
|
||||
})(self.isolatedAPI);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
void 0;
|
||||
|
|
@ -675,36 +675,69 @@ function loadAllSourceScriptlets() {
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
async function processGenericCosmeticFilters(assetDetails, bucketsMap, exceptionSet) {
|
||||
if ( bucketsMap === undefined ) { return 0; }
|
||||
if ( exceptionSet ) {
|
||||
for ( const [ hash, selectors ] of bucketsMap ) {
|
||||
let i = selectors.length;
|
||||
while ( i-- ) {
|
||||
const selector = selectors[i];
|
||||
if ( exceptionSet.has(selector) === false ) { continue; }
|
||||
selectors.splice(i, 1);
|
||||
//log(`\tRemoving excepted generic filter ##${selector}`);
|
||||
}
|
||||
if ( selectors.length === 0 ) {
|
||||
bucketsMap.delete(hash);
|
||||
// http://www.cse.yorku.ca/~oz/hash.html#djb2
|
||||
// Must mirror content script surveyor's version
|
||||
|
||||
async function processGenericCosmeticFilters(
|
||||
assetDetails,
|
||||
selectorList,
|
||||
exceptionList,
|
||||
declarativeMap
|
||||
) {
|
||||
const exceptionSet = new Set(
|
||||
exceptionList &&
|
||||
exceptionList.filter(a => a.key !== undefined).map(a => a.selector)
|
||||
);
|
||||
|
||||
const genericSelectorMap = new Map();
|
||||
if ( selectorList ) {
|
||||
for ( const { key, selector } of selectorList ) {
|
||||
if ( key === undefined ) { continue; }
|
||||
if ( exceptionSet.has(selector) ) { continue; }
|
||||
const type = key.charCodeAt(0);
|
||||
const hash = hashFromStr(type, key.slice(1));
|
||||
const selectors = genericSelectorMap.get(hash);
|
||||
if ( selectors === undefined ) {
|
||||
genericSelectorMap.set(hash, selector)
|
||||
} else {
|
||||
genericSelectorMap.set(hash, `${selectors},${selector}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( bucketsMap.size === 0 ) { return 0; }
|
||||
const bucketsList = Array.from(bucketsMap);
|
||||
const count = bucketsList.reduce((a, v) => a += v[1].length, 0);
|
||||
if ( count === 0 ) { return 0; }
|
||||
const selectorLists = bucketsList.map(v => [ v[0], v[1].join(',') ]);
|
||||
const originalScriptletMap = await loadAllSourceScriptlets();
|
||||
|
||||
// Specific exceptions
|
||||
const genericExceptionMap = new Map();
|
||||
if ( declarativeMap ) {
|
||||
for ( const details of declarativeMap.values() ) {
|
||||
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(' ')} `
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ( genericSelectorMap.size === 0 ) {
|
||||
if ( genericExceptionMap.size === 0 ) { return 0; }
|
||||
}
|
||||
|
||||
const originalScriptletMap = await loadAllSourceScriptlets();
|
||||
let patchedScriptlet = originalScriptletMap.get('css-generic').replace(
|
||||
'$rulesetId$',
|
||||
assetDetails.id
|
||||
);
|
||||
patchedScriptlet = safeReplace(patchedScriptlet,
|
||||
/\bself\.\$genericSelectorMap\$/,
|
||||
`${JSON.stringify(selectorLists, scriptletJsonReplacer)}`
|
||||
`${JSON.stringify(genericSelectorMap, scriptletJsonReplacer)}`
|
||||
);
|
||||
patchedScriptlet = safeReplace(patchedScriptlet,
|
||||
/\bself\.\$genericExceptionMap\$/,
|
||||
`${JSON.stringify(genericExceptionMap, scriptletJsonReplacer)}`
|
||||
);
|
||||
|
||||
writeFile(
|
||||
|
|
@ -712,24 +745,49 @@ async function processGenericCosmeticFilters(assetDetails, bucketsMap, exception
|
|||
patchedScriptlet
|
||||
);
|
||||
|
||||
log(`CSS-generic: ${count} plain CSS selectors`);
|
||||
log(`CSS-generic: ${genericSelectorMap.size} plain CSS selectors`);
|
||||
log(`CSS-generic: ${genericExceptionMap.size} specific CSS exceptions`);
|
||||
|
||||
return count;
|
||||
return genericSelectorMap.size + genericExceptionMap.size;
|
||||
}
|
||||
|
||||
const hashFromStr = (type, s) => {
|
||||
const len = s.length;
|
||||
const step = len + 7 >>> 3;
|
||||
let hash = (type << 5) + type ^ len;
|
||||
for ( let i = 0; i < len; i += step ) {
|
||||
hash = (hash << 5) + hash ^ s.charCodeAt(i);
|
||||
}
|
||||
return hash & 0xFFFFFF;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
async function processGenericHighCosmeticFilters(assetDetails, selectorSet, exceptionSet) {
|
||||
if ( selectorSet === undefined ) { return 0; }
|
||||
if ( exceptionSet ) {
|
||||
for ( const selector of selectorSet ) {
|
||||
if ( exceptionSet.has(selector) === false ) { continue; }
|
||||
selectorSet.delete(selector);
|
||||
//log(`\tRemoving excepted generic filter ##${selector}`);
|
||||
async function processGenericHighCosmeticFilters(
|
||||
assetDetails,
|
||||
genericSelectorList,
|
||||
genericExceptionList
|
||||
) {
|
||||
if ( genericSelectorList === undefined ) { return 0; }
|
||||
const genericSelectorSet = new Set(
|
||||
genericSelectorList
|
||||
.filter(a => a.key === undefined)
|
||||
.map(a => a.selector)
|
||||
);
|
||||
if ( genericExceptionList ) {
|
||||
const genericExceptionSet = new Set(
|
||||
genericExceptionList
|
||||
.filter(a => a.key === undefined)
|
||||
.map(a => a.selector)
|
||||
);
|
||||
for ( const selector of genericExceptionSet ) {
|
||||
if ( genericSelectorSet.has(selector) === false ) { continue; }
|
||||
genericSelectorSet.delete(selector);
|
||||
log(`\tRemoving excepted highly generic filter ##${selector}`);
|
||||
}
|
||||
}
|
||||
if ( selectorSet.size === 0 ) { return 0; }
|
||||
const selectorLists = Array.from(selectorSet).sort().join(',\n');
|
||||
if ( genericSelectorSet.size === 0 ) { return 0; }
|
||||
const selectorLists = Array.from(genericSelectorSet).sort().join(',\n');
|
||||
const originalScriptletMap = await loadAllSourceScriptlets();
|
||||
|
||||
let patchedScriptlet = originalScriptletMap.get('css-generichigh').replace(
|
||||
|
|
@ -746,9 +804,9 @@ async function processGenericHighCosmeticFilters(assetDetails, selectorSet, exce
|
|||
patchedScriptlet
|
||||
);
|
||||
|
||||
log(`CSS-generic-high: ${selectorSet.size} plain CSS selectors`);
|
||||
log(`CSS-generic-high: ${genericSelectorSet.size} plain CSS selectors`);
|
||||
|
||||
return selectorSet.size;
|
||||
return genericSelectorSet.size;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
|
@ -846,22 +904,33 @@ const scriptletJsonReplacer = (k, v) => {
|
|||
/******************************************************************************/
|
||||
|
||||
function argsMap2List(argsMap, hostnamesMap) {
|
||||
const argsList = [];
|
||||
const argsList = [ '' ];
|
||||
const indexMap = new Map();
|
||||
for ( const [ id, details ] of argsMap ) {
|
||||
indexMap.set(id, argsList.length);
|
||||
argsList.push(details);
|
||||
}
|
||||
const argsSeqs = [ 0 ];
|
||||
const argsSeqsIndices = new Map();
|
||||
for ( const [ hn, ids ] of hostnamesMap ) {
|
||||
const seqKey = JSON.stringify(ids);
|
||||
if ( argsSeqsIndices.has(seqKey) ) {
|
||||
hostnamesMap.set(hn, argsSeqsIndices.get(seqKey));
|
||||
continue;
|
||||
}
|
||||
const seqIndex = argsSeqs.length;
|
||||
argsSeqsIndices.set(seqKey, seqIndex);
|
||||
hostnamesMap.set(hn, seqIndex);
|
||||
if ( typeof ids === 'number' ) {
|
||||
hostnamesMap.set(hn, indexMap.get(ids));
|
||||
argsSeqs.push(indexMap.get(ids));
|
||||
continue;
|
||||
}
|
||||
for ( let i = 0; i < ids.length; i++ ) {
|
||||
ids[i] = indexMap.get(ids[i]);
|
||||
argsSeqs.push(-indexMap.get(ids[i]));
|
||||
}
|
||||
argsSeqs[argsSeqs.length-1] = -argsSeqs[argsSeqs.length-1];
|
||||
}
|
||||
return argsList;
|
||||
return { argsList, argsSeqs };
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
|
@ -885,38 +954,21 @@ async function processCosmeticFilters(assetDetails, mapin) {
|
|||
|
||||
const argsMap = domainBasedEntries.map(entry => [
|
||||
entry[0],
|
||||
{
|
||||
a: entry[1].a ? entry[1].a.join(',\n') : undefined,
|
||||
n: entry[1].n
|
||||
}
|
||||
entry[1].a ? entry[1].a.join('\n') : undefined,
|
||||
]);
|
||||
const hostnamesMap = new Map();
|
||||
let hasEntities = false;
|
||||
for ( const [ id, details ] of domainBasedEntries ) {
|
||||
if ( details.y === undefined ) { continue; }
|
||||
scriptletHostnameToIdMap(details.y, id, hostnamesMap);
|
||||
}
|
||||
const argsList = argsMap2List(argsMap, hostnamesMap);
|
||||
const entitiesMap = new Map();
|
||||
for ( const [ hn, details ] of hostnamesMap ) {
|
||||
if ( hn.endsWith('.*') === false ) { continue; }
|
||||
hostnamesMap.delete(hn);
|
||||
entitiesMap.set(hn.slice(0, -2), details);
|
||||
}
|
||||
|
||||
// Extract exceptions from argsList, simplify argsList entries
|
||||
const exceptionsMap = new Map();
|
||||
for ( let i = 0; i < argsList.length; i++ ) {
|
||||
const details = argsList[i];
|
||||
if ( details.n ) {
|
||||
for ( const hn of details.n ) {
|
||||
if ( exceptionsMap.has(hn) === false ) {
|
||||
exceptionsMap.set(hn, []);
|
||||
}
|
||||
exceptionsMap.get(hn).push(i);
|
||||
}
|
||||
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('.*'));
|
||||
}
|
||||
argsList[i] = details.a;
|
||||
}
|
||||
const { argsList, argsSeqs } = argsMap2List(argsMap, hostnamesMap);
|
||||
|
||||
const originalScriptletMap = await loadAllSourceScriptlets();
|
||||
let patchedScriptlet = originalScriptletMap.get('css-specific').replace(
|
||||
|
|
@ -927,17 +979,17 @@ async function processCosmeticFilters(assetDetails, mapin) {
|
|||
/\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,
|
||||
/\bself\.\$entitiesMap\$/,
|
||||
`${JSON.stringify(entitiesMap, scriptletJsonReplacer)}`
|
||||
);
|
||||
patchedScriptlet = safeReplace(patchedScriptlet,
|
||||
/\bself\.\$exceptionsMap\$/,
|
||||
`${JSON.stringify(exceptionsMap, scriptletJsonReplacer)}`
|
||||
'self.$hasEntities$',
|
||||
JSON.stringify(hasEntities)
|
||||
);
|
||||
writeFile(`${scriptletDir}/specific/${assetDetails.id}.js`, patchedScriptlet);
|
||||
generatedFiles.push(`${assetDetails.id}`);
|
||||
|
|
@ -945,10 +997,9 @@ async function processCosmeticFilters(assetDetails, mapin) {
|
|||
if ( generatedFiles.length !== 0 ) {
|
||||
log(`CSS-specific: ${mapin.size} distinct filters`);
|
||||
log(`\tCombined into ${hostnamesMap.size} distinct hostnames`);
|
||||
log(`\tCombined into ${entitiesMap.size} distinct entities`);
|
||||
}
|
||||
|
||||
return hostnamesMap.size + entitiesMap.size;
|
||||
return hostnamesMap.size;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
|
@ -973,38 +1024,21 @@ async function processDeclarativeCosmeticFilters(assetDetails, mapin) {
|
|||
|
||||
const argsMap = contentArray.map(entry => [
|
||||
entry[0],
|
||||
{
|
||||
a: entry[1].a,
|
||||
n: entry[1].n,
|
||||
}
|
||||
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 === undefined ) { continue; }
|
||||
scriptletHostnameToIdMap(details.y, id, hostnamesMap);
|
||||
}
|
||||
const argsList = argsMap2List(argsMap, hostnamesMap);
|
||||
const entitiesMap = new Map();
|
||||
for ( const [ hn, details ] of hostnamesMap ) {
|
||||
if ( hn.endsWith('.*') === false ) { continue; }
|
||||
hostnamesMap.delete(hn);
|
||||
entitiesMap.set(hn.slice(0, -2), details);
|
||||
}
|
||||
|
||||
// Extract exceptions from argsList, simplify argsList entries
|
||||
const exceptionsMap = new Map();
|
||||
for ( let i = 0; i < argsList.length; i++ ) {
|
||||
const details = argsList[i];
|
||||
if ( details.n ) {
|
||||
for ( const hn of details.n ) {
|
||||
if ( exceptionsMap.has(hn) === false ) {
|
||||
exceptionsMap.set(hn, []);
|
||||
}
|
||||
exceptionsMap.get(hn).push(i);
|
||||
}
|
||||
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('.*'));
|
||||
}
|
||||
argsList[i] = details.a;
|
||||
}
|
||||
const { argsList, argsSeqs } = argsMap2List(argsMap, hostnamesMap);
|
||||
|
||||
const originalScriptletMap = await loadAllSourceScriptlets();
|
||||
let patchedScriptlet = originalScriptletMap.get('css-declarative').replace(
|
||||
|
|
@ -1015,27 +1049,26 @@ async function processDeclarativeCosmeticFilters(assetDetails, mapin) {
|
|||
/\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,
|
||||
/\bself\.\$entitiesMap\$/,
|
||||
`${JSON.stringify(entitiesMap, scriptletJsonReplacer)}`
|
||||
);
|
||||
patchedScriptlet = safeReplace(patchedScriptlet,
|
||||
/\bself\.\$exceptionsMap\$/,
|
||||
`${JSON.stringify(exceptionsMap, scriptletJsonReplacer)}`
|
||||
'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`);
|
||||
log(`\tCombined into ${entitiesMap.size} distinct entities`);
|
||||
}
|
||||
|
||||
return hostnamesMap.size + entitiesMap.size;
|
||||
return hostnamesMap.size;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
|
@ -1059,39 +1092,21 @@ async function processProceduralCosmeticFilters(assetDetails, mapin) {
|
|||
|
||||
const argsMap = contentArray.map(entry => [
|
||||
entry[0],
|
||||
{
|
||||
a: entry[1].a,
|
||||
n: entry[1].n,
|
||||
}
|
||||
entry[1].a,
|
||||
]);
|
||||
const hostnamesMap = new Map();
|
||||
let hasEntities = false;
|
||||
for ( const [ id, details ] of contentArray ) {
|
||||
if ( details.y === undefined ) { continue; }
|
||||
scriptletHostnameToIdMap(details.y, id, hostnamesMap);
|
||||
}
|
||||
const argsList = argsMap2List(argsMap, hostnamesMap);
|
||||
const entitiesMap = new Map();
|
||||
for ( const [ hn, details ] of hostnamesMap ) {
|
||||
if ( hn.endsWith('.*') === false ) { continue; }
|
||||
hostnamesMap.delete(hn);
|
||||
entitiesMap.set(hn.slice(0, -2), details);
|
||||
}
|
||||
|
||||
// Extract exceptions from argsList, simplify argsList entries
|
||||
const exceptionsMap = new Map();
|
||||
for ( let i = 0; i < argsList.length; i++ ) {
|
||||
const details = argsList[i];
|
||||
if ( details.n ) {
|
||||
for ( const hn of details.n ) {
|
||||
if ( exceptionsMap.has(hn) === false ) {
|
||||
exceptionsMap.set(hn, []);
|
||||
}
|
||||
exceptionsMap.get(hn).push(i);
|
||||
}
|
||||
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('.*'));
|
||||
}
|
||||
argsList[i] = details.a;
|
||||
}
|
||||
|
||||
const { argsList, argsSeqs } = argsMap2List(argsMap, hostnamesMap);
|
||||
const originalScriptletMap = await loadAllSourceScriptlets();
|
||||
let patchedScriptlet = originalScriptletMap.get('css-procedural').replace(
|
||||
'$rulesetId$',
|
||||
|
|
@ -1101,27 +1116,26 @@ async function processProceduralCosmeticFilters(assetDetails, mapin) {
|
|||
/\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,
|
||||
/\bself\.\$entitiesMap\$/,
|
||||
`${JSON.stringify(entitiesMap, scriptletJsonReplacer)}`
|
||||
);
|
||||
patchedScriptlet = safeReplace(patchedScriptlet,
|
||||
/\bself\.\$exceptionsMap\$/,
|
||||
`${JSON.stringify(exceptionsMap, scriptletJsonReplacer)}`
|
||||
'self.$hasEntities$',
|
||||
JSON.stringify(hasEntities)
|
||||
);
|
||||
writeFile(`${scriptletDir}/procedural/${assetDetails.id}.js`, patchedScriptlet);
|
||||
|
||||
if ( contentArray.length !== 0 ) {
|
||||
log(`Procedural-related distinct filters: ${procedurals.size} distinct combined selectors`);
|
||||
log(`\tCombined into ${hostnamesMap.size} distinct hostnames`);
|
||||
log(`\tCombined into ${entitiesMap.size} distinct entities`);
|
||||
}
|
||||
|
||||
return hostnamesMap.size + entitiesMap.size;
|
||||
return hostnamesMap.size;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
|
@ -1233,13 +1247,14 @@ async function rulesetFromURLs(assetDetails) {
|
|||
|
||||
const genericCosmeticStats = await processGenericCosmeticFilters(
|
||||
assetDetails,
|
||||
results.genericCosmetic,
|
||||
results.genericCosmeticExceptions
|
||||
results.genericCosmeticFilters,
|
||||
results.genericCosmeticExceptions,
|
||||
declarativeCosmetic
|
||||
);
|
||||
const genericHighCosmeticStats = await processGenericHighCosmeticFilters(
|
||||
assetDetails,
|
||||
results.genericHighCosmetic,
|
||||
results.genericCosmeticExceptions
|
||||
results.genericCosmeticFilters,
|
||||
results.genericCosmeticExceptions,
|
||||
);
|
||||
const specificCosmeticStats = await processCosmeticFilters(
|
||||
assetDetails,
|
||||
|
|
|
|||
|
|
@ -28,15 +28,12 @@
|
|||
/******************************************************************************/
|
||||
|
||||
const argsList = self.$argsList$;
|
||||
|
||||
const argsSeqs = self.$argsSeqs$;
|
||||
const hostnamesMap = new Map(self.$hostnamesMap$);
|
||||
|
||||
const entitiesMap = new Map(self.$entitiesMap$);
|
||||
|
||||
const exceptionsMap = new Map(self.$exceptionsMap$);
|
||||
const hasEntities = self.$hasEntities$;
|
||||
|
||||
self.declarativeImports = self.declarativeImports || [];
|
||||
self.declarativeImports.push({ argsList, hostnamesMap, entitiesMap, exceptionsMap });
|
||||
self.declarativeImports.push({ argsList, argsSeqs, hostnamesMap, hasEntities });
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
|
|
|||
|
|
@ -27,26 +27,39 @@
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
const toImport = self.$genericSelectorMap$;
|
||||
const selectorsToImport = self.$genericSelectorMap$;
|
||||
const exceptionsToImport = self.$genericExceptionMap$;
|
||||
|
||||
const genericSelectorMap = self.genericSelectorMap || new Map();
|
||||
if ( selectorsToImport ) {
|
||||
const map = self.genericSelectorMap =
|
||||
self.genericSelectorMap || new Map();
|
||||
|
||||
if ( genericSelectorMap.size === 0 ) {
|
||||
self.genericSelectorMap = new Map(toImport);
|
||||
return;
|
||||
if ( map.size !== 0 ) {
|
||||
for ( const entry of selectorsToImport ) {
|
||||
const before = map.get(entry[0]);
|
||||
map.set(entry[0],
|
||||
before === undefined ? entry[1] : `${before},${entry[1]}`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
self.genericSelectorMap = new Map(selectorsToImport);
|
||||
}
|
||||
selectorsToImport.length = 0;
|
||||
}
|
||||
|
||||
for ( const toImportEntry of toImport ) {
|
||||
const existing = genericSelectorMap.get(toImportEntry[0]);
|
||||
genericSelectorMap.set(
|
||||
toImportEntry[0],
|
||||
existing === undefined
|
||||
? toImportEntry[1]
|
||||
: `${existing},${toImportEntry[1]}`
|
||||
);
|
||||
}
|
||||
if ( exceptionsToImport ) {
|
||||
const map = self.genericExceptionMap =
|
||||
self.genericExceptionMap || new Map();
|
||||
|
||||
self.genericSelectorMap = genericSelectorMap;
|
||||
if ( map.size !== 0 ) {
|
||||
for ( const entry of exceptionsToImport ) {
|
||||
map.set(entry[0], `${map.get(entry[0]) || ''}${entry[1]}`);
|
||||
}
|
||||
} else {
|
||||
self.genericExceptionMap = new Map(exceptionsToImport);
|
||||
}
|
||||
exceptionsToImport.length = 0;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
|
|
|||
|
|
@ -28,15 +28,12 @@
|
|||
/******************************************************************************/
|
||||
|
||||
const argsList = self.$argsList$;
|
||||
|
||||
const argsSeqs = self.$argsSeqs$;
|
||||
const hostnamesMap = new Map(self.$hostnamesMap$);
|
||||
|
||||
const entitiesMap = new Map(self.$entitiesMap$);
|
||||
|
||||
const exceptionsMap = new Map(self.$exceptionsMap$);
|
||||
const hasEntities = self.$hasEntities$;
|
||||
|
||||
self.proceduralImports = self.proceduralImports || [];
|
||||
self.proceduralImports.push({ argsList, hostnamesMap, entitiesMap, exceptionsMap });
|
||||
self.proceduralImports.push({ argsList, argsSeqs, hostnamesMap, hasEntities });
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
|
|
|||
|
|
@ -28,15 +28,12 @@
|
|||
/******************************************************************************/
|
||||
|
||||
const argsList = self.$argsList$;
|
||||
|
||||
const argsSeqs = self.$argsSeqs$;
|
||||
const hostnamesMap = new Map(self.$hostnamesMap$);
|
||||
|
||||
const entitiesMap = new Map(self.$entitiesMap$);
|
||||
|
||||
const exceptionsMap = new Map(self.$exceptionsMap$);
|
||||
const hasEntities = self.$hasEntities$;
|
||||
|
||||
self.specificImports = self.specificImports || [];
|
||||
self.specificImports.push({ argsList, hostnamesMap, entitiesMap, exceptionsMap });
|
||||
self.specificImports.push({ argsList, argsSeqs, hostnamesMap, hasEntities });
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
|
|
|||
|
|
@ -31,19 +31,6 @@ import staticNetFilteringEngine from './static-net-filtering.js';
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
// http://www.cse.yorku.ca/~oz/hash.html#djb2
|
||||
// Must mirror content script surveyor's version
|
||||
|
||||
const hashFromStr = (type, s) => {
|
||||
const len = s.length;
|
||||
const step = len + 7 >>> 3;
|
||||
let hash = (type << 5) + type ^ len;
|
||||
for ( let i = 0; i < len; i += step ) {
|
||||
hash = (hash << 5) + hash ^ s.charCodeAt(i);
|
||||
}
|
||||
return hash & 0xFFFFFF;
|
||||
};
|
||||
|
||||
const isRegex = hn => hn.startsWith('/') && hn.endsWith('/');
|
||||
|
||||
/******************************************************************************/
|
||||
|
|
@ -93,32 +80,19 @@ const keyFromSelector = selector => {
|
|||
function addGenericCosmeticFilter(context, selector, isException) {
|
||||
if ( selector === undefined ) { return; }
|
||||
if ( selector.length <= 1 ) { return; }
|
||||
if ( isException ) {
|
||||
if ( context.genericCosmeticExceptions === undefined ) {
|
||||
context.genericCosmeticExceptions = new Set();
|
||||
}
|
||||
context.genericCosmeticExceptions.add(selector);
|
||||
return;
|
||||
}
|
||||
if ( selector.charCodeAt(0) === 0x7B /* '{' */ ) { return; }
|
||||
const key = keyFromSelector(selector);
|
||||
if ( key === undefined ) {
|
||||
if ( context.genericHighCosmeticFilters === undefined ) {
|
||||
context.genericHighCosmeticFilters = new Set();
|
||||
if ( isException ) {
|
||||
if ( context.genericCosmeticExceptions === undefined ) {
|
||||
context.genericCosmeticExceptions = [];
|
||||
}
|
||||
context.genericHighCosmeticFilters.add(selector);
|
||||
context.genericCosmeticExceptions.push({ key, selector });
|
||||
return;
|
||||
}
|
||||
const type = key.charCodeAt(0);
|
||||
const hash = hashFromStr(type, key.slice(1));
|
||||
if ( context.genericCosmeticFilters === undefined ) {
|
||||
context.genericCosmeticFilters = new Map();
|
||||
context.genericCosmeticFilters = [];
|
||||
}
|
||||
let bucket = context.genericCosmeticFilters.get(hash);
|
||||
if ( bucket === undefined ) {
|
||||
context.genericCosmeticFilters.set(hash, bucket = []);
|
||||
}
|
||||
bucket.push(selector);
|
||||
context.genericCosmeticFilters.push({ key, selector });
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
|
@ -255,7 +229,10 @@ function addExtendedToDNR(context, parser) {
|
|||
if ( details === undefined ) {
|
||||
context.specificCosmeticFilters.set(compiled, details = {});
|
||||
}
|
||||
if ( exception ) {
|
||||
if ( compiled.startsWith('{') === false ) {
|
||||
details.key = keyFromSelector(compiled);
|
||||
}
|
||||
if ( exception || not ) {
|
||||
if ( details.excludeMatches === undefined ) {
|
||||
details.excludeMatches = [];
|
||||
}
|
||||
|
|
@ -497,8 +474,7 @@ async function dnrRulesetFromRawLists(lists, options = {}) {
|
|||
await Promise.all(toLoad);
|
||||
const result = {
|
||||
network: staticNetFilteringEngine.dnrFromCompiled('end', context),
|
||||
genericCosmetic: context.genericCosmeticFilters,
|
||||
genericHighCosmetic: context.genericHighCosmeticFilters,
|
||||
genericCosmeticFilters: context.genericCosmeticFilters,
|
||||
genericCosmeticExceptions: context.genericCosmeticExceptions,
|
||||
specificCosmetic: context.specificCosmeticFilters,
|
||||
scriptlet: context.scriptletFilters,
|
||||
|
|
|
|||
Loading…
Reference in a new issue