[mv3] Add support for ancestor context syntax in scriptlets

Related commit:
a483f7955f
This commit is contained in:
Raymond Hill 2025-03-07 17:04:02 -05:00
parent 536f0fba25
commit d006fd06e7
No known key found for this signature in database
GPG key ID: 25E1490B761470C2
3 changed files with 81 additions and 110 deletions

View file

@ -1526,8 +1526,8 @@ async function main() {
await rulesetFromURLs({
id: 'est-0',
group: 'regions',
lang: 'et',
group: 'regions',
lang: 'et',
name: '🇪🇪ee: Eesti saitidele kohandatud filter',
enabled: false,
urls: [ 'https://ubol-et.adblock.ee/list.txt' ],

View file

@ -97,8 +97,9 @@ export function compile(assetDetails, details) {
world: resourceEntry.world,
args: new Map(),
hostnames: new Map(),
entities: new Map(),
exceptions: new Map(),
hasEntities: false,
hasAncestors: false,
matches: new Set(),
});
}
@ -109,23 +110,21 @@ export function compile(assetDetails, details) {
const iArgs = scriptletDetails.args.get(argsToken);
if ( details.matches ) {
for ( const hn of details.matches ) {
if ( hn.endsWith('.*') ) {
const isEntity = hn.endsWith('.*') || hn.endsWith('.*>>');
scriptletDetails.hasEntities ||= isEntity;
const isAncestor = hn.endsWith('>>')
scriptletDetails.hasAncestors ||= isAncestor;
if ( isEntity || isAncestor ) {
scriptletDetails.matches.clear();
scriptletDetails.matches.add('*');
const entity = hn.slice(0, -2);
if ( scriptletDetails.entities.has(entity) === false ) {
scriptletDetails.entities.set(entity, new Set());
}
scriptletDetails.entities.get(entity).add(iArgs);
} else {
if ( scriptletDetails.matches.has('*') === false ) {
scriptletDetails.matches.add(hn);
}
if ( scriptletDetails.hostnames.has(hn) === false ) {
scriptletDetails.hostnames.set(hn, new Set());
}
scriptletDetails.hostnames.get(hn).add(iArgs);
}
if ( scriptletDetails.matches.has('*') === false ) {
scriptletDetails.matches.add(hn);
}
if ( scriptletDetails.hostnames.has(hn) === false ) {
scriptletDetails.hostnames.set(hn, new Set());
}
scriptletDetails.hostnames.get(hn).add(iArgs);
}
} else {
scriptletDetails.matches.add('*');
@ -172,8 +171,12 @@ export async function commit(rulesetId, path, writeFn) {
JSON.stringify(patchHnMap(details.hostnames))
);
content = safeReplace(content,
'self.$entitiesMap$',
JSON.stringify(patchHnMap(details.entities))
'self.$hasEntities$',
JSON.stringify(details.hasEntities)
);
content = safeReplace(content,
'self.$hasAncestors$',
JSON.stringify(details.hasAncestors)
);
content = safeReplace(content,
'self.$exceptionsMap$',

View file

@ -20,30 +20,13 @@
*/
/* eslint-disable indent */
// ruleset: $rulesetId$
// Important!
// Isolate from global scope
// Start of local scope
(( ) => {
/******************************************************************************/
// Start of code to inject
const uBOL_$scriptletName$ = function() {
const scriptletGlobals = {}; // eslint-disable-line
const argsList = self.$argsList$;
const hostnamesMap = new Map(self.$hostnamesMap$);
const entitiesMap = new Map(self.$entitiesMap$);
const exceptionsMap = new Map(self.$exceptionsMap$);
(function uBOL_$scriptletName$() {
/******************************************************************************/
@ -51,98 +34,83 @@ function $scriptletName$(){}
/******************************************************************************/
const hnParts = [];
try {
let origin = document.location.origin;
if ( origin === 'null' ) {
const origins = document.location.ancestorOrigins || [];
for ( let i = 0; i < origins.length; i++ ) {
origin = origins[i];
if ( origin !== 'null' ) { break; }
}
}
const beg = origin.lastIndexOf('://');
if ( beg === -1 ) { return; }
let hn = origin.slice(beg+3)
const end = hn.indexOf(':');
if ( end !== -1 ) { hn = hn.slice(0, end); }
hnParts.push(...hn.split('.'));
} catch {
}
const hnpartslen = hnParts.length;
if ( hnpartslen === 0 ) { return; }
const scriptletGlobals = {}; // eslint-disable-line
const argsList = self.$argsList$;
const hostnamesMap = new Map(self.$hostnamesMap$);
const exceptionsMap = new Map(self.$exceptionsMap$);
const hasEntities = self.$hasEntities$;
const hasAncestors = self.$hasAncestors$;
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();
}
// Hostname-based
if ( hostnamesMap.size !== 0 ) {
const collectArgIndices = hn => {
let argsIndices = hostnamesMap.get(hn);
if ( argsIndices === undefined ) { return; }
if ( typeof argsIndices === 'number' ) { argsIndices = [ argsIndices ]; }
const collectArgIndices = (hn, map, out) => {
let argsIndices = map.get(hn);
if ( argsIndices === undefined ) { return; }
if ( typeof argsIndices !== 'number' ) {
for ( const argsIndex of argsIndices ) {
if ( tonotdoIndices.includes(argsIndex) ) { continue; }
todoIndices.add(argsIndex);
out.add(argsIndex);
}
};
for ( let i = 0; i < hnpartslen; i++ ) {
const hn = hnParts.slice(i).join('.');
collectArgIndices(hn);
} else {
out.add(argsIndices);
}
collectArgIndices('*');
hostnamesMap.clear();
}
};
// 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);
const indicesFromHostname = (hostname, suffix = '') => {
const hnParts = hostname.split('.');
const hnpartslen = hnParts.length;
if ( hnpartslen === 0 ) { return; }
for ( let i = 0; i < hnpartslen; i++ ) {
const hn = `${hnParts.slice(i).join('.')}${suffix}`;
collectArgIndices(hn, hostnamesMap, todoIndices);
collectArgIndices(hn, exceptionsMap, tonotdoIndices);
}
if ( hasEntities ) {
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('.')}.*${suffix}`;
collectArgIndices(en, hostnamesMap, todoIndices);
collectArgIndices(en, exceptionsMap, tonotdoIndices);
}
}
}
entitiesMap.clear();
};
const entries = (( ) => {
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 hn = origin.slice(beg+3)
const end = hn.indexOf(':');
return { hn: end === -1 ? hn : hn.slice(0, end), i };
}).filter(a => a !== undefined);
})();
if ( entries.length === 0 ) { return; }
const todoIndices = new Set();
const tonotdoIndices = new Set();
indicesFromHostname(entries[0].hn);
if ( hasAncestors ) {
for ( const entry of entries ) {
if ( entry.i === 0 ) { continue; }
indicesFromHostname(entry.hn, '>>');
}
}
// Apply scriplets
for ( const i of todoIndices ) {
if ( tonotdoIndices.has(i) ) { continue; }
try { $scriptletName$(...argsList[i]); }
catch { }
}
argsList.length = 0;
/******************************************************************************/
};
// End of code to inject
/******************************************************************************/
uBOL_$scriptletName$();
/******************************************************************************/
// End of local scope
})();
/******************************************************************************/
void 0;