[mv3] Extend scriplet filtering support to all scriptlets

Source code of scriplets is now fetched directly from uBO
project, so there is no longer the need to keep duplicate
versions of scriplet code.

All scriplet filters are now supported.
This commit is contained in:
Raymond Hill 2023-06-02 17:04:15 -04:00
parent cb9a91d43c
commit b163097399
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
24 changed files with 363 additions and 3325 deletions

View File

@ -432,76 +432,6 @@ function registerScriptlet(context, scriptletDetails) {
/******************************************************************************/
function registerScriptletEntity(context) {
// https://bugzilla.mozilla.org/show_bug.cgi?id=1736575
// `MAIN` world not yet supported in Firefox
if ( isGecko ) { return; }
const { before, filteringModeDetails, rulesetsDetails } = context;
const js = [];
for ( const details of rulesetsDetails ) {
const { scriptlets } = details;
if ( scriptlets instanceof Object === false ) { continue; }
if ( Array.isArray(scriptlets.entityBasedTokens) === false ) { continue; }
if ( scriptlets.entityBasedTokens.length === 0 ) { continue; }
for ( const token of scriptlets.entityBasedTokens ) {
js.push(`/rulesets/scripting/scriptlet-entity/${details.id}.${token}.js`);
}
}
if ( js.length === 0 ) { return; }
const matches = [];
const excludeMatches = [];
if ( filteringModeDetails.extendedGeneric.has('all-urls') ) {
excludeMatches.push(...ut.matchesFromHostnames(filteringModeDetails.none));
excludeMatches.push(...ut.matchesFromHostnames(filteringModeDetails.network));
excludeMatches.push(...ut.matchesFromHostnames(filteringModeDetails.extendedSpecific));
matches.push('<all_urls>');
} else {
matches.push(
...ut.matchesFromHostnames(filteringModeDetails.extendedGeneric)
);
}
if ( matches.length === 0 ) { return; }
const registered = before.get('scriptlet.entity');
before.delete('scriptlet.entity'); // Important!
// register
if ( registered === undefined ) {
context.toAdd.push({
id: 'scriptlet.entity',
js,
allFrames: true,
matches,
excludeMatches,
runAt: 'document_start',
world: 'MAIN',
});
return;
}
// update
const directive = { id: 'scriptlet.entity' };
if ( arrayEq(registered.js, js, false) === false ) {
directive.js = js;
}
if ( arrayEq(registered.matches, matches) === false ) {
directive.matches = matches;
}
if ( arrayEq(registered.excludeMatches, excludeMatches) === false ) {
directive.excludeMatches = excludeMatches;
}
if ( directive.js || directive.matches || directive.excludeMatches ) {
context.toUpdate.push(directive);
}
}
/******************************************************************************/
function registerSpecific(context, specificDetails) {
const { filteringModeDetails } = context;
@ -749,7 +679,6 @@ async function registerInjectables(origins) {
registerDeclarative(context, declarativeDetails);
registerProcedural(context, proceduralDetails);
registerScriptlet(context, scriptletDetails);
registerScriptletEntity(context);
registerSpecific(context, specificDetails);
registerSpecificEntity(context);
registerGeneric(context, genericDetails);

View File

@ -32,6 +32,7 @@ import redirectResourcesMap from './js/redirect-resources.js';
import { dnrRulesetFromRawLists } from './js/static-dnr-filtering.js';
import { fnameFromFileId } from './js/utils.js';
import * as sfp from './js/static-filtering-parser.js';
import * as makeScriptlet from './make-scriptlets.js';
/******************************************************************************/
@ -872,261 +873,21 @@ async function processProceduralCosmeticFilters(assetDetails, mapin) {
async function processScriptletFilters(assetDetails, mapin) {
if ( mapin === undefined ) { return; }
const { domainBased, entityBased } = splitDomainAndEntity(mapin);
makeScriptlet.init();
// Load all available scriptlets into a key-val map, where the key is the
// scriptlet token, and val is the whole content of the file.
const originalScriptletMap = await loadAllSourceScriptlets();
let domainBasedTokens;
if ( domainBased.size !== 0 ) {
domainBasedTokens = await processDomainScriptletFilters(assetDetails, domainBased, originalScriptletMap);
for ( const details of mapin.values() ) {
makeScriptlet.compile(details, assetDetails.isTrusted);
}
let entityBasedTokens;
if ( entityBased.size !== 0 ) {
entityBasedTokens = await processEntityScriptletFilters(assetDetails, entityBased, originalScriptletMap);
}
return { domainBasedTokens, entityBasedTokens };
}
/******************************************************************************/
const parseScriptletArguments = raw => {
const out = [];
let s = raw;
let len = s.length;
let beg = 0, pos = 0;
let i = 1;
while ( beg < len ) {
pos = s.indexOf(',', pos);
// Escaped comma? If so, skip.
if ( pos > 0 && s.charCodeAt(pos - 1) === 0x5C /* '\\' */ ) {
s = s.slice(0, pos - 1) + s.slice(pos);
len -= 1;
continue;
}
if ( pos === -1 ) { pos = len; }
out.push(s.slice(beg, pos).trim());
beg = pos = pos + 1;
i++;
}
return out;
};
const parseScriptletFilter = (raw, scriptletMap, tokenSuffix = '') => {
const filter = raw.slice(4, -1);
const end = filter.length;
let pos = filter.indexOf(',');
if ( pos === -1 ) { pos = end; }
const parts = filter.trim().split(',').map(s => s.trim());
const token = scriptletDealiasingMap.get(parts[0]) || '';
if ( token === '' ) { return; }
if ( scriptletMap.has(`${token}${tokenSuffix}`) === false ) { return; }
return {
token,
args: parseScriptletArguments(parts.slice(1).join(',').trim()),
};
};
/******************************************************************************/
async function processDomainScriptletFilters(assetDetails, domainBased, originalScriptletMap) {
// For each instance of distinct scriptlet, we will collect distinct
// instances of arguments, and for each distinct set of argument, we
// will collect the set of hostnames for which the scriptlet/args is meant
// to execute. This will allow us a single content script file and the
// scriptlets execution will depend on hostname testing against the
// URL of the document at scriptlet execution time. In the end, we
// should have no more generated content script per subscription than the
// number of distinct source scriptlets.
const scriptletDetails = new Map();
const rejectedFilters = [];
for ( const [ rawFilter, entry ] of domainBased ) {
if ( entry.rejected ) {
rejectedFilters.push(rawFilter);
continue;
}
const normalized = parseScriptletFilter(rawFilter, originalScriptletMap);
if ( normalized === undefined ) {
log(`Discarded unsupported scriptlet filter: ${rawFilter}`, true);
continue;
}
let argsDetails = scriptletDetails.get(normalized.token);
if ( argsDetails === undefined ) {
argsDetails = new Map();
scriptletDetails.set(normalized.token, argsDetails);
}
const argsHash = JSON.stringify(normalized.args);
let hostnamesDetails = argsDetails.get(argsHash);
if ( hostnamesDetails === undefined ) {
hostnamesDetails = {
a: normalized.args,
y: new Set(),
n: new Set(),
};
argsDetails.set(argsHash, hostnamesDetails);
}
if ( entry.matches ) {
for ( const hn of entry.matches ) {
hostnamesDetails.y.add(hn);
}
}
if ( entry.excludeMatches ) {
for ( const hn of entry.excludeMatches ) {
hostnamesDetails.n.add(hn);
}
}
}
log(`Rejected scriptlet filters: ${rejectedFilters.length}`);
log(rejectedFilters.map(line => `\t${line}`).join('\n'), true);
const generatedFiles = [];
const tokens = [];
for ( const [ token, argsDetails ] of scriptletDetails ) {
const argsMap = Array.from(argsDetails).map(entry => [
uidint32(entry[0]),
{ a: entry[1].a, n: entry[1].n }
]);
const hostnamesMap = new Map();
for ( const [ argsHash, details ] of argsDetails ) {
scriptletHostnameToIdMap(details.y, uidint32(argsHash), hostnamesMap);
}
const argsList = argsMap2List(argsMap, hostnamesMap);
const patchedScriptlet = originalScriptletMap.get(token)
.replace(
'$rulesetId$',
assetDetails.id
).replace(
/\bself\.\$argsList\$/m,
`${JSON.stringify(argsList, scriptletJsonReplacer)}`
).replace(
/\bself\.\$hostnamesMap\$/m,
`${JSON.stringify(hostnamesMap, scriptletJsonReplacer)}`
const stats = await makeScriptlet.commit(
assetDetails.id,
`${scriptletDir}/scriptlet`,
writeFile
);
const fname = `${assetDetails.id}.${token}.js`;
const fpath = `${scriptletDir}/scriptlet/${fname}`;
writeFile(fpath, patchedScriptlet);
generatedFiles.push(fname);
tokens.push(token);
const hostnameMatches = new Set(hostnamesMap.keys());
if ( hostnameMatches.has('*') ) {
hostnameMatches.clear();
hostnameMatches.add('*');
if ( stats.length !== 0 ) {
scriptletStats.set(assetDetails.id, stats);
}
let rulesetScriptlets = scriptletStats.get(assetDetails.id);
if ( rulesetScriptlets === undefined ) {
scriptletStats.set(assetDetails.id, rulesetScriptlets = []);
}
rulesetScriptlets.push([ token, Array.from(hostnameMatches).sort() ]);
}
if ( generatedFiles.length !== 0 ) {
const scriptletFilterCount = Array.from(scriptletDetails.values())
.reduce((a, b) => a + b.size, 0);
log(`Scriptlet-related distinct filters: ${scriptletFilterCount}`);
log(`Scriptlet-related injectable files: ${generatedFiles.length}`);
log(`\t${generatedFiles.join(', ')}`);
}
return tokens;
}
/******************************************************************************/
async function processEntityScriptletFilters(assetDetails, entityBased, originalScriptletMap) {
// For each instance of distinct scriptlet, we will collect distinct
// instances of arguments, and for each distinct set of argument, we
// will collect the set of hostnames for which the scriptlet/args is meant
// to execute. This will allow us a single content script file and the
// scriptlets execution will depend on hostname testing against the
// URL of the document at scriptlet execution time. In the end, we
// should have no more generated content script per subscription than the
// number of distinct source scriptlets.
const scriptletMap = new Map();
const rejectedFilters = [];
for ( const [ rawFilter, entry ] of entityBased ) {
if ( entry.rejected ) {
rejectedFilters.push(rawFilter);
continue;
}
const normalized = parseScriptletFilter(rawFilter, originalScriptletMap, '.entity');
if ( normalized === undefined ) {
log(`Discarded unsupported scriptlet filter: ${rawFilter}`, true);
continue;
}
let argsDetails = scriptletMap.get(normalized.token);
if ( argsDetails === undefined ) {
argsDetails = new Map();
scriptletMap.set(normalized.token, argsDetails);
}
const argsHash = JSON.stringify(normalized.args);
let scriptletDetails = argsDetails.get(argsHash);
if ( scriptletDetails === undefined ) {
scriptletDetails = {
a: normalized.args,
y: new Set(),
n: new Set(),
};
argsDetails.set(argsHash, scriptletDetails);
}
if ( entry.matches ) {
for ( const entity of entry.matches ) {
scriptletDetails.y.add(entity);
}
}
if ( entry.excludeMatches ) {
for ( const hn of entry.excludeMatches ) {
scriptletDetails.n.add(hn);
}
}
}
log(`Rejected scriptlet filters: ${rejectedFilters.length}`);
log(rejectedFilters.map(line => `\t${line}`).join('\n'), true);
const generatedFiles = [];
const tokens = [];
for ( const [ token, argsDetails ] of scriptletMap ) {
const argsMap = Array.from(argsDetails).map(entry => [
uidint32(entry[0]),
{ a: entry[1].a, n: entry[1].n }
]);
const entitiesMap = new Map();
for ( const [ argsHash, details ] of argsDetails ) {
scriptletHostnameToIdMap(details.y, uidint32(argsHash), entitiesMap);
}
const argsList = argsMap2List(argsMap, entitiesMap);
const patchedScriptlet = originalScriptletMap.get(`${token}.entity`)
.replace(
'$rulesetId$',
assetDetails.id
).replace(
/\bself\.\$argsList\$/m,
`${JSON.stringify(argsList, scriptletJsonReplacer)}`
).replace(
/\bself\.\$entitiesMap\$/m,
`${JSON.stringify(entitiesMap, scriptletJsonReplacer)}`
);
const fname = `${assetDetails.id}.${token}.js`;
const fpath = `${scriptletDir}/scriptlet-entity/${fname}`;
writeFile(fpath, patchedScriptlet);
generatedFiles.push(fname);
tokens.push(token);
}
if ( generatedFiles.length !== 0 ) {
log(`Scriptlet-related entity-based injectable files: ${generatedFiles.length}`);
log(`\t${generatedFiles.join(', ')}`);
}
return tokens;
makeScriptlet.reset();
return { domainBasedTokens: stats.length };
}
/******************************************************************************/
@ -1306,6 +1067,7 @@ async function main() {
enabled: true,
urls: contentURLs,
homeURL: 'https://github.com/uBlockOrigin/uAssets',
isTrusted: true,
});
// Regional rulesets

View File

@ -0,0 +1,208 @@
/*******************************************************************************
uBlock Origin - a browser extension to block requests.
Copyright (C) 2017-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
*/
'use strict';
/******************************************************************************/
import fs from 'fs/promises';
import { builtinScriptlets } from './scriptlets.js';
/******************************************************************************/
const resourceDetails = new Map();
const resourceAliases = new Map();
const scriptletFiles = new Map();
/******************************************************************************/
function createScriptletCoreCode(scriptletToken) {
const details = resourceDetails.get(scriptletToken);
const components = new Map([ [ scriptletToken, details.code ] ]);
const dependencies = details.dependencies && details.dependencies.slice() || [];
while ( dependencies.length !== 0 ) {
const token = dependencies.shift();
if ( components.has(token) ) { continue; }
const details = resourceDetails.get(token);
if ( details === undefined ) { continue; }
components.set(token, details.code);
if ( Array.isArray(details.dependencies) === false ) { continue; }
dependencies.push(...details.dependencies);
}
return Array.from(components.values()).join('\n\n');
}
/******************************************************************************/
function safeReplace(text, pattern, replacement, count = 1) {
const rePattern = typeof pattern === 'string'
? new RegExp(pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))
: pattern;
let out = text;
for (;;) {
const match = rePattern.exec(out);
if ( match === null ) { break; }
out = out.slice(0, match.index) +
replacement +
out.slice(match.index + match[0].length);
count -= 1;
if ( count === 0 ) { break; }
}
return out;
}
/******************************************************************************/
export function init() {
for ( const scriptlet of builtinScriptlets ) {
const { name, aliases, fn } = scriptlet;
const entry = {
name: fn.name,
code: fn.toString(),
dependencies: scriptlet.dependencies,
requiresTrust: scriptlet.requiresTrust === true,
};
resourceDetails.set(name, entry);
if ( Array.isArray(aliases) === false ) { continue; }
for ( const alias of aliases ) {
resourceAliases.set(alias, name);
}
}
}
/******************************************************************************/
export function reset() {
scriptletFiles.clear();
}
/******************************************************************************/
export function compile(details, isTrusted) {
if ( details.args[0].endsWith('.js') === false ) {
details.args[0] += '.js';
}
if ( resourceAliases.has(details.args[0]) ) {
details.args[0] = resourceAliases.get(details.args[0]);
}
const scriptletToken = details.args[0];
const resourceEntry = resourceDetails.get(scriptletToken);
if ( resourceEntry === undefined ) { return; }
if ( resourceEntry.requiresTrust && isTrusted !== true ) {
console.log(`Rejecting ${scriptletToken}: source is not trusted`);
return;
}
if ( scriptletFiles.has(scriptletToken) === false ) {
scriptletFiles.set(scriptletToken, {
name: resourceEntry.name,
code: createScriptletCoreCode(scriptletToken),
args: new Map(),
hostnames: new Map(),
entities: new Map(),
exceptions: new Map(),
matches: new Set(),
});
}
const scriptletDetails = scriptletFiles.get(scriptletToken);
const argsToken = JSON.stringify(details.args.slice(1));
if ( scriptletDetails.args.has(argsToken) === false ) {
scriptletDetails.args.set(argsToken, scriptletDetails.args.size);
}
const iArgs = scriptletDetails.args.get(argsToken);
if ( details.matches ) {
for ( const hn of details.matches ) {
if ( hn.endsWith('.*') ) {
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);
}
}
} else {
scriptletDetails.matches.add('*');
}
if ( details.excludeMatches ) {
for ( const hn of details.excludeMatches ) {
if ( scriptletDetails.exceptions.has(hn) === false ) {
scriptletDetails.exceptions.set(hn, []);
}
scriptletDetails.exceptions.get(hn).push(iArgs);
}
}
}
/******************************************************************************/
export async function commit(rulesetId, path, writeFn) {
const scriptletTemplate = await fs.readFile(
'./scriptlet.template.js',
{ encoding: 'utf8' }
);
const patchHnMap = hnmap => {
const out = Array.from(hnmap);
out.forEach(a => {
const values = Array.from(a[1]);
a[1] = values.length === 1 ? values[0] : values;
});
return out;
};
const scriptletStats = [];
for ( const [ name, details ] of scriptletFiles ) {
let content = safeReplace(scriptletTemplate,
'function $scriptletName$(){}',
details.code
);
content = safeReplace(content, /\$rulesetId\$/, rulesetId, 0);
content = safeReplace(content, /\$scriptletName\$/, details.name, 0);
content = safeReplace(content,
'self.$argsList$',
JSON.stringify(Array.from(details.args.keys()))
);
content = safeReplace(content,
'self.$hostnamesMap$',
JSON.stringify(patchHnMap(details.hostnames))
);
content = safeReplace(content,
'self.$entitiesMap$',
JSON.stringify(patchHnMap(details.entities))
);
content = safeReplace(content,
'self.$exceptionsMap$',
JSON.stringify(Array.from(details.exceptions))
);
writeFn(`${path}/${rulesetId}.${name}`, content);
scriptletStats.push([ name.slice(0, -3), Array.from(details.matches).sort() ]);
}
return scriptletStats;
}
/******************************************************************************/

View File

@ -0,0 +1,123 @@
/*******************************************************************************
uBlock Origin - a browser extension to block requests.
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
*/
/* jshint esversion:11 */
'use strict';
// ruleset: $rulesetId$
/******************************************************************************/
// Important!
// Isolate from global scope
(function uBOL_$scriptletName$() {
/******************************************************************************/
const scriptletGlobals = new Map(); // jshint ignore: line
const argsList = self.$argsList$;
const hostnamesMap = new Map(self.$hostnamesMap$);
const entitiesMap = new Map(self.$entitiesMap$);
const exceptionsMap = new Map(self.$exceptionsMap$);
/******************************************************************************/
function $scriptletName$(){}
/******************************************************************************/
const hnParts = [];
try { hnParts.push(...document.location.hostname.split('.')); }
catch(ex) { }
const hnpartslen = hnParts.length;
if ( hnpartslen === 0 ) { return; }
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 ]; }
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();
}
// 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(argsIndex) ) { continue; }
todoIndices.add(argsIndex);
}
}
}
entitiesMap.clear();
}
// Apply scriplets
for ( const i of todoIndices ) {
try { $scriptletName$(...JSON.parse(argsList[i])); }
catch(ex) {}
}
argsList.length = 0;
/******************************************************************************/
})();
/******************************************************************************/
void 0;

View File

@ -1,181 +0,0 @@
/*******************************************************************************
uBlock Origin - a browser extension to block requests.
Copyright (C) 2019-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
The scriptlets below are meant to be injected only into a
web page context.
*/
/* jshint esversion:11 */
'use strict';
/******************************************************************************/
/// name abort-current-script.entity
/// alias acs.entity
/// alias abort-current-inline-script.entity
/// alias acis.entity
/******************************************************************************/
// Important!
// Isolate from global scope
(function uBOL_abortCurrentScriptEntity() {
/******************************************************************************/
// $rulesetId$
const argsList = self.$argsList$;
const entitiesMap = new Map(self.$entitiesMap$);
/******************************************************************************/
// Issues to mind before changing anything:
// https://github.com/uBlockOrigin/uBlock-issues/issues/2154
const scriptlet = (
target = '',
needle = '',
context = ''
) => {
if ( target === '' ) { return; }
const reRegexEscape = /[.*+?^${}()|[\]\\]/g;
const reNeedle = (( ) => {
if ( needle === '' ) { return /^/; }
if ( /^\/.+\/$/.test(needle) ) {
return new RegExp(needle.slice(1,-1));
}
return new RegExp(needle.replace(reRegexEscape, '\\$&'));
})();
const reContext = (( ) => {
if ( context === '' ) { return; }
if ( /^\/.+\/$/.test(context) ) {
return new RegExp(context.slice(1,-1));
}
return new RegExp(context.replace(reRegexEscape, '\\$&'));
})();
const chain = target.split('.');
let owner = window;
let prop;
for (;;) {
prop = chain.shift();
if ( chain.length === 0 ) { break; }
owner = owner[prop];
if ( owner instanceof Object === false ) { return; }
}
let value;
let desc = Object.getOwnPropertyDescriptor(owner, prop);
if (
desc instanceof Object === false ||
desc.get instanceof Function === false
) {
value = owner[prop];
desc = undefined;
}
const magic = String.fromCharCode(Date.now() % 26 + 97) +
Math.floor(Math.random() * 982451653 + 982451653).toString(36);
const scriptTexts = new WeakMap();
const getScriptText = elem => {
let text = elem.textContent;
if ( text.trim() !== '' ) { return text; }
if ( scriptTexts.has(elem) ) { return scriptTexts.get(elem); }
const [ , mime, content ] =
/^data:([^,]*),(.+)$/.exec(elem.src.trim()) ||
[ '', '', '' ];
try {
switch ( true ) {
case mime.endsWith(';base64'):
text = self.atob(content);
break;
default:
text = self.decodeURIComponent(content);
break;
}
} catch(ex) {
}
scriptTexts.set(elem, text);
return text;
};
const validate = ( ) => {
const e = document.currentScript;
if ( e instanceof HTMLScriptElement === false ) { return; }
if ( reContext !== undefined && reContext.test(e.src) === false ) {
return;
}
if ( reNeedle.test(getScriptText(e)) === false ) { return; }
throw new ReferenceError(magic);
};
Object.defineProperty(owner, prop, {
get: function() {
validate();
return desc instanceof Object
? desc.get.call(owner)
: value;
},
set: function(a) {
validate();
if ( desc instanceof Object ) {
desc.set.call(owner, a);
} else {
value = a;
}
}
});
const oe = window.onerror;
window.onerror = function(msg) {
if ( typeof msg === 'string' && msg.includes(magic) ) {
return true;
}
if ( oe instanceof Function ) {
return oe.apply(this, arguments);
}
}.bind();
};
/******************************************************************************/
const hnparts = [];
try { hnparts.push(...document.location.hostname.split('.')); } catch(ex) { }
const hnpartslen = hnparts.length - 1;
for ( let i = 0; i < hnpartslen; i++ ) {
for ( let j = hnpartslen; j > i; j-- ) {
const hn = hnparts.slice(i).join('.');
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 ) {
const details = argsList[argsIndex];
if ( details.n && details.n.includes(hn) ) { continue; }
try { scriptlet(...details.a); } catch(ex) {}
}
}
}
argsList.length = 0;
entitiesMap.clear();
/******************************************************************************/
})();
/******************************************************************************/

View File

@ -1,184 +0,0 @@
/*******************************************************************************
uBlock Origin - a browser extension to block requests.
Copyright (C) 2019-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
The scriptlets below are meant to be injected only into a
web page context.
*/
/* jshint esversion:11 */
'use strict';
/******************************************************************************/
/// name abort-current-script
/// alias acs
/// alias abort-current-inline-script
/// alias acis
/******************************************************************************/
// Important!
// Isolate from global scope
(function uBOL_abortCurrentScript() {
/******************************************************************************/
// $rulesetId$
const argsList = self.$argsList$;
const hostnamesMap = new Map(self.$hostnamesMap$);
/******************************************************************************/
// Issues to mind before changing anything:
// https://github.com/uBlockOrigin/uBlock-issues/issues/2154
const scriptlet = (
target = '',
needle = '',
context = ''
) => {
if ( target === '' ) { return; }
const reRegexEscape = /[.*+?^${}()|[\]\\]/g;
const reNeedle = (( ) => {
if ( needle === '' ) { return /^/; }
if ( /^\/.+\/$/.test(needle) ) {
return new RegExp(needle.slice(1,-1));
}
return new RegExp(needle.replace(reRegexEscape, '\\$&'));
})();
const reContext = (( ) => {
if ( context === '' ) { return; }
if ( /^\/.+\/$/.test(context) ) {
return new RegExp(context.slice(1,-1));
}
return new RegExp(context.replace(reRegexEscape, '\\$&'));
})();
const chain = target.split('.');
let owner = window;
let prop;
for (;;) {
prop = chain.shift();
if ( chain.length === 0 ) { break; }
owner = owner[prop];
if ( owner instanceof Object === false ) { return; }
}
let value;
let desc = Object.getOwnPropertyDescriptor(owner, prop);
if (
desc instanceof Object === false ||
desc.get instanceof Function === false
) {
value = owner[prop];
desc = undefined;
}
const magic = String.fromCharCode(Date.now() % 26 + 97) +
Math.floor(Math.random() * 982451653 + 982451653).toString(36);
const scriptTexts = new WeakMap();
const getScriptText = elem => {
let text = elem.textContent;
if ( text.trim() !== '' ) { return text; }
if ( scriptTexts.has(elem) ) { return scriptTexts.get(elem); }
const [ , mime, content ] =
/^data:([^,]*),(.+)$/.exec(elem.src.trim()) ||
[ '', '', '' ];
try {
switch ( true ) {
case mime.endsWith(';base64'):
text = self.atob(content);
break;
default:
text = self.decodeURIComponent(content);
break;
}
} catch(ex) {
}
scriptTexts.set(elem, text);
return text;
};
const validate = ( ) => {
const e = document.currentScript;
if ( e instanceof HTMLScriptElement === false ) { return; }
if ( reContext !== undefined && reContext.test(e.src) === false ) {
return;
}
if ( reNeedle.test(getScriptText(e)) === false ) { return; }
throw new ReferenceError(magic);
};
Object.defineProperty(owner, prop, {
get: function() {
validate();
return desc instanceof Object
? desc.get.call(owner)
: value;
},
set: function(a) {
validate();
if ( desc instanceof Object ) {
desc.set.call(owner, a);
} else {
value = a;
}
}
});
const oe = window.onerror;
window.onerror = function(msg) {
if ( typeof msg === 'string' && msg.includes(magic) ) {
return true;
}
if ( oe instanceof Function ) {
return oe.apply(this, arguments);
}
}.bind();
};
/******************************************************************************/
let hn;
try { hn = document.location.hostname; } catch(ex) { }
while ( hn ) {
if ( hostnamesMap.has(hn) ) {
let argsIndices = hostnamesMap.get(hn);
if ( typeof argsIndices === 'number' ) { argsIndices = [ argsIndices ]; }
for ( const argsIndex of argsIndices ) {
const details = argsList[argsIndex];
if ( details.n && details.n.includes(hn) ) { continue; }
try { scriptlet(...details.a); } catch(ex) {}
}
}
if ( hn === '*' ) { break; }
const pos = hn.indexOf('.');
if ( pos !== -1 ) {
hn = hn.slice(pos + 1);
} else {
hn = '*';
}
}
argsList.length = 0;
hostnamesMap.clear();
/******************************************************************************/
})();
/******************************************************************************/

View File

@ -1,139 +0,0 @@
/*******************************************************************************
uBlock Origin - a browser extension to block requests.
Copyright (C) 2019-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
The scriptlets below are meant to be injected only into a
web page context.
*/
/* jshint esversion:11 */
'use strict';
/******************************************************************************/
/// name abort-on-property-read.entity
/// alias aopr.entity
/******************************************************************************/
// Important!
// Isolate from global scope
(function uBOL_abortOnPropertyReadEntity() {
/******************************************************************************/
// $rulesetId$
const argsList = self.$argsList$;
const entitiesMap = new Map(self.$entitiesMap$);
/******************************************************************************/
const ObjGetOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
const ObjDefineProperty = Object.defineProperty;
const magic =
String.fromCharCode(Date.now() % 26 + 97) +
Math.floor(Math.random() * 982451653 + 982451653).toString(36);
const abort = function() {
throw new ReferenceError(magic);
};
const makeProxy = function(owner, chain) {
const pos = chain.indexOf('.');
if ( pos === -1 ) {
const desc = ObjGetOwnPropertyDescriptor(owner, chain);
if ( !desc || desc.get !== abort ) {
ObjDefineProperty(owner, chain, {
get: abort,
set: function(){}
});
}
return;
}
const prop = chain.slice(0, pos);
let v = owner[prop];
chain = chain.slice(pos + 1);
if ( v ) {
makeProxy(v, chain);
return;
}
const desc = ObjGetOwnPropertyDescriptor(owner, prop);
if ( desc && desc.set !== undefined ) { return; }
ObjDefineProperty(owner, prop, {
get: function() { return v; },
set: function(a) {
v = a;
if ( a instanceof Object ) {
makeProxy(a, chain);
}
}
});
};
const scriptlet = (
chain = ''
) => {
const owner = window;
makeProxy(owner, chain);
const oe = window.onerror;
window.onerror = function(msg, src, line, col, error) {
if ( typeof msg === 'string' && msg.includes(magic) ) {
return true;
}
if ( oe instanceof Function ) {
return oe(msg, src, line, col, error);
}
}.bind();
};
/******************************************************************************/
const hnparts = [];
try { hnparts.push(...document.location.hostname.split('.')); } catch(ex) { }
const hnpartslen = hnparts.length - 1;
for ( let i = 0; i < hnpartslen; i++ ) {
for ( let j = hnpartslen; j > i; j-- ) {
const hn = hnparts.slice(i).join('.');
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 ) {
const details = argsList[argsIndex];
if ( details.n && details.n.includes(hn) ) { continue; }
try { scriptlet(...details.a); } catch(ex) {}
}
}
}
argsList.length = 0;
entitiesMap.clear();
/******************************************************************************/
})();
/******************************************************************************/

View File

@ -1,143 +0,0 @@
/*******************************************************************************
uBlock Origin - a browser extension to block requests.
Copyright (C) 2019-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
The scriptlets below are meant to be injected only into a
web page context.
*/
/* jshint esversion:11 */
'use strict';
/******************************************************************************/
/// name abort-on-property-read
/// alias aopr
/******************************************************************************/
// Important!
// Isolate from global scope
(function uBOL_abortOnPropertyRead() {
/******************************************************************************/
// $rulesetId$
const argsList = self.$argsList$;
const hostnamesMap = new Map(self.$hostnamesMap$);
/******************************************************************************/
const ObjGetOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
const ObjDefineProperty = Object.defineProperty;
const magic =
String.fromCharCode(Date.now() % 26 + 97) +
Math.floor(Math.random() * 982451653 + 982451653).toString(36);
const abort = function() {
throw new ReferenceError(magic);
};
const makeProxy = function(owner, chain) {
const pos = chain.indexOf('.');
if ( pos === -1 ) {
const desc = ObjGetOwnPropertyDescriptor(owner, chain);
if ( !desc || desc.get !== abort ) {
ObjDefineProperty(owner, chain, {
get: abort,
set: function(){}
});
}
return;
}
const prop = chain.slice(0, pos);
let v = owner[prop];
chain = chain.slice(pos + 1);
if ( v ) {
makeProxy(v, chain);
return;
}
const desc = ObjGetOwnPropertyDescriptor(owner, prop);
if ( desc && desc.set !== undefined ) { return; }
ObjDefineProperty(owner, prop, {
get: function() { return v; },
set: function(a) {
v = a;
if ( a instanceof Object ) {
makeProxy(a, chain);
}
}
});
};
const scriptlet = (
chain = ''
) => {
const owner = window;
makeProxy(owner, chain);
const oe = window.onerror;
window.onerror = function(msg, src, line, col, error) {
if ( typeof msg === 'string' && msg.includes(magic) ) {
return true;
}
if ( oe instanceof Function ) {
return oe(msg, src, line, col, error);
}
}.bind();
};
/******************************************************************************/
let hn;
try { hn = document.location.hostname; } catch(ex) { }
while ( hn ) {
if ( hostnamesMap.has(hn) ) {
let argsIndices = hostnamesMap.get(hn);
if ( typeof argsIndices === 'number' ) { argsIndices = [ argsIndices ]; }
for ( const argsIndex of argsIndices ) {
const details = argsList[argsIndex];
if ( details.n && details.n.includes(hn) ) { continue; }
try { scriptlet(...details.a); } catch(ex) {}
}
}
if ( hn === '*' ) { break; }
const pos = hn.indexOf('.');
if ( pos !== -1 ) {
hn = hn.slice(pos + 1);
} else {
hn = '*';
}
}
argsList.length = 0;
hostnamesMap.clear();
/******************************************************************************/
})();
/******************************************************************************/

View File

@ -1,113 +0,0 @@
/*******************************************************************************
uBlock Origin - a browser extension to block requests.
Copyright (C) 2019-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
The scriptlets below are meant to be injected only into a
web page context.
*/
/* jshint esversion:11 */
'use strict';
/******************************************************************************/
/// name abort-on-property-write.entity
/// alias aopw.entity
/******************************************************************************/
// Important!
// Isolate from global scope
(function uBOL_abortOnPropertyWriteEntity() {
/******************************************************************************/
// $rulesetId$
const argsList = self.$argsList$;
const entitiesMap = new Map(self.$entitiesMap$);
/******************************************************************************/
const magic =
String.fromCharCode(Date.now() % 26 + 97) +
Math.floor(Math.random() * 982451653 + 982451653).toString(36);
const abort = function() {
throw new ReferenceError(magic);
};
const scriptlet = (
prop = ''
) => {
let owner = window;
for (;;) {
const pos = prop.indexOf('.');
if ( pos === -1 ) { break; }
owner = owner[prop.slice(0, pos)];
if ( owner instanceof Object === false ) { return; }
prop = prop.slice(pos + 1);
}
delete owner[prop];
Object.defineProperty(owner, prop, {
set: function() {
abort();
}
});
const oe = window.onerror;
window.onerror = function(msg, src, line, col, error) {
if ( typeof msg === 'string' && msg.includes(magic) ) {
return true;
}
if ( oe instanceof Function ) {
return oe(msg, src, line, col, error);
}
}.bind();
};
/******************************************************************************/
const hnparts = [];
try { hnparts.push(...document.location.hostname.split('.')); } catch(ex) { }
const hnpartslen = hnparts.length - 1;
for ( let i = 0; i < hnpartslen; i++ ) {
for ( let j = hnpartslen; j > i; j-- ) {
const hn = hnparts.slice(i).join('.');
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 ) {
const details = argsList[argsIndex];
if ( details.n && details.n.includes(hn) ) { continue; }
try { scriptlet(...details.a); } catch(ex) {}
}
}
}
argsList.length = 0;
entitiesMap.clear();
/******************************************************************************/
})();
/******************************************************************************/

View File

@ -1,117 +0,0 @@
/*******************************************************************************
uBlock Origin - a browser extension to block requests.
Copyright (C) 2019-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
The scriptlets below are meant to be injected only into a
web page context.
*/
/* jshint esversion:11 */
'use strict';
/******************************************************************************/
/// name abort-on-property-write
/// alias aopw
/******************************************************************************/
// Important!
// Isolate from global scope
(function uBOL_abortOnPropertyWrite() {
/******************************************************************************/
// $rulesetId$
const argsList = self.$argsList$;
const hostnamesMap = new Map(self.$hostnamesMap$);
/******************************************************************************/
const magic =
String.fromCharCode(Date.now() % 26 + 97) +
Math.floor(Math.random() * 982451653 + 982451653).toString(36);
const abort = function() {
throw new ReferenceError(magic);
};
const scriptlet = (
prop = ''
) => {
let owner = window;
for (;;) {
const pos = prop.indexOf('.');
if ( pos === -1 ) { break; }
owner = owner[prop.slice(0, pos)];
if ( owner instanceof Object === false ) { return; }
prop = prop.slice(pos + 1);
}
delete owner[prop];
Object.defineProperty(owner, prop, {
set: function() {
abort();
}
});
const oe = window.onerror;
window.onerror = function(msg, src, line, col, error) {
if ( typeof msg === 'string' && msg.includes(magic) ) {
return true;
}
if ( oe instanceof Function ) {
return oe(msg, src, line, col, error);
}
}.bind();
};
/******************************************************************************/
let hn;
try { hn = document.location.hostname; } catch(ex) { }
while ( hn ) {
if ( hostnamesMap.has(hn) ) {
let argsIndices = hostnamesMap.get(hn);
if ( typeof argsIndices === 'number' ) { argsIndices = [ argsIndices ]; }
for ( const argsIndex of argsIndices ) {
const details = argsList[argsIndex];
if ( details.n && details.n.includes(hn) ) { continue; }
try { scriptlet(...details.a); } catch(ex) {}
}
}
if ( hn === '*' ) { break; }
const pos = hn.indexOf('.');
if ( pos !== -1 ) {
hn = hn.slice(pos + 1);
} else {
hn = '*';
}
}
argsList.length = 0;
hostnamesMap.clear();
/******************************************************************************/
})();
/******************************************************************************/

View File

@ -1,160 +0,0 @@
/*******************************************************************************
uBlock Origin - a browser extension to block requests.
Copyright (C) 2019-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
The scriptlets below are meant to be injected only into a
web page context.
*/
/* jshint esversion:11 */
'use strict';
/******************************************************************************/
/// name json-prune
/******************************************************************************/
// Important!
// Isolate from global scope
(function uBOL_jsonPrune() {
/******************************************************************************/
// $rulesetId$
const argsList = self.$argsList$;
const hostnamesMap = new Map(self.$hostnamesMap$);
/******************************************************************************/
// https://github.com/uBlockOrigin/uBlock-issues/issues/1545
// - Add support for "remove everything if needle matches" case
const scriptlet = (
rawPrunePaths = '',
rawNeedlePaths = ''
) => {
const prunePaths = rawPrunePaths !== ''
? rawPrunePaths.split(/ +/)
: [];
let needlePaths;
if ( prunePaths.length === 0 ) { return; }
needlePaths = prunePaths.length !== 0 && rawNeedlePaths !== ''
? rawNeedlePaths.split(/ +/)
: [];
const findOwner = function(root, path, prune = false) {
let owner = root;
let chain = path;
for (;;) {
if ( typeof owner !== 'object' || owner === null ) {
return false;
}
const pos = chain.indexOf('.');
if ( pos === -1 ) {
if ( prune === false ) {
return owner.hasOwnProperty(chain);
}
if ( chain === '*' ) {
for ( const key in owner ) {
if ( owner.hasOwnProperty(key) === false ) { continue; }
delete owner[key];
}
} else if ( owner.hasOwnProperty(chain) ) {
delete owner[chain];
}
return true;
}
const prop = chain.slice(0, pos);
if (
prop === '[]' && Array.isArray(owner) ||
prop === '*' && owner instanceof Object
) {
const next = chain.slice(pos + 1);
let found = false;
for ( const key of Object.keys(owner) ) {
found = findOwner(owner[key], next, prune) || found;
}
return found;
}
if ( owner.hasOwnProperty(prop) === false ) { return false; }
owner = owner[prop];
chain = chain.slice(pos + 1);
}
};
const mustProcess = function(root) {
for ( const needlePath of needlePaths ) {
if ( findOwner(root, needlePath) === false ) {
return false;
}
}
return true;
};
const pruner = function(o) {
if ( mustProcess(o) === false ) { return o; }
for ( const path of prunePaths ) {
findOwner(o, path, true);
}
return o;
};
JSON.parse = new Proxy(JSON.parse, {
apply: function() {
return pruner(Reflect.apply(...arguments));
},
});
Response.prototype.json = new Proxy(Response.prototype.json, {
apply: function() {
return Reflect.apply(...arguments).then(o => pruner(o));
},
});
};
/******************************************************************************/
let hn;
try { hn = document.location.hostname; } catch(ex) { }
while ( hn ) {
if ( hostnamesMap.has(hn) ) {
let argsIndices = hostnamesMap.get(hn);
if ( typeof argsIndices === 'number' ) { argsIndices = [ argsIndices ]; }
for ( const argsIndex of argsIndices ) {
const details = argsList[argsIndex];
if ( details.n && details.n.includes(hn) ) { continue; }
try { scriptlet(...details.a); } catch(ex) {}
}
}
if ( hn === '*' ) { break; }
const pos = hn.indexOf('.');
if ( pos !== -1 ) {
hn = hn.slice(pos + 1);
} else {
hn = '*';
}
}
argsList.length = 0;
hostnamesMap.clear();
/******************************************************************************/
})();
/******************************************************************************/

View File

@ -1,175 +0,0 @@
/*******************************************************************************
uBlock Origin - a browser extension to block requests.
Copyright (C) 2019-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
The scriptlets below are meant to be injected only into a
web page context.
*/
/* jshint esversion:11 */
'use strict';
/******************************************************************************/
/// name no-addeventlistener-if.entity
/// alias noaelif.entity
/// alias aeld.entity
/******************************************************************************/
// Important!
// Isolate from global scope
(function uBOL_noAddEventListenerIfEntity() {
/******************************************************************************/
// $rulesetId$
const argsList = self.$argsList$;
const entitiesMap = new Map(self.$entitiesMap$);
/******************************************************************************/
const regexpFromArg = arg => {
if ( arg === '' ) { return /^/; }
if ( /^\/.+\/$/.test(arg) ) { return new RegExp(arg.slice(1,-1)); }
return new RegExp(arg.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
};
/******************************************************************************/
// Dependencies
const scriptletGlobals = new Map();
function safeSelf() {
if ( scriptletGlobals.has('safeSelf') ) {
return scriptletGlobals.get('safeSelf');
}
const safe = {
'RegExp': self.RegExp,
'RegExp_test': self.RegExp.prototype.test,
'RegExp_exec': self.RegExp.prototype.exec,
'addEventListener': self.EventTarget.prototype.addEventListener,
'removeEventListener': self.EventTarget.prototype.removeEventListener,
};
scriptletGlobals.set('safeSelf', safe);
return safe;
}
function runAt(fn, when) {
const intFromReadyState = state => {
const targets = {
'loading': 1,
'interactive': 2, 'end': 2, '2': 2,
'complete': 3, 'idle': 3, '3': 3,
};
const tokens = Array.isArray(state) ? state : [ state ];
for ( const token of tokens ) {
const prop = `${token}`;
if ( targets.hasOwnProperty(prop) === false ) { continue; }
return targets[prop];
}
return 0;
};
const runAt = intFromReadyState(when);
if ( intFromReadyState(document.readyState) >= runAt ) {
fn(); return;
}
const onStateChange = ( ) => {
if ( intFromReadyState(document.readyState) < runAt ) { return; }
fn();
safe.removeEventListener.apply(document, args);
};
const safe = safeSelf();
const args = [ 'readystatechange', onStateChange, { capture: true } ];
safe.addEventListener.apply(document, args);
}
/******************************************************************************/
const scriptlet = (
arg1 = '',
arg2 = ''
) => {
const details = typeof arg1 !== 'object'
? { type: arg1, pattern: arg2 }
: arg1;
const { type = '', pattern = '' } = details;
if ( typeof type !== 'string' ) { return; }
if ( typeof pattern !== 'string' ) { return; }
const reType = regexpFromArg(type);
const rePattern = regexpFromArg(pattern);
const trapEddEventListeners = ( ) => {
const eventListenerHandler = {
apply: function(target, thisArg, args) {
let type, handler;
try {
type = String(args[0]);
handler = String(args[1]);
} catch(ex) {
}
if (
reType.test(type) === false ||
rePattern.test(handler) === false
) {
return target.apply(thisArg, args);
}
}
};
self.EventTarget.prototype.addEventListener = new Proxy(
self.EventTarget.prototype.addEventListener,
eventListenerHandler
);
};
runAt(( ) => {
trapEddEventListeners();
}, details.runAt);
};
/******************************************************************************/
const hnparts = [];
try { hnparts.push(...document.location.hostname.split('.')); } catch(ex) { }
const hnpartslen = hnparts.length - 1;
for ( let i = 0; i < hnpartslen; i++ ) {
for ( let j = hnpartslen; j > i; j-- ) {
const hn = hnparts.slice(i).join('.');
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 ) {
const details = argsList[argsIndex];
if ( details.n && details.n.includes(hn) ) { continue; }
try { scriptlet(...details.a); } catch(ex) {}
}
}
}
argsList.length = 0;
entitiesMap.clear();
/******************************************************************************/
})();
/******************************************************************************/

View File

@ -1,180 +0,0 @@
/*******************************************************************************
uBlock Origin - a browser extension to block requests.
Copyright (C) 2019-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
The scriptlets below are meant to be injected only into a
web page context.
*/
/* jshint esversion:11 */
'use strict';
/******************************************************************************/
/// name no-addeventlistener-if
/// alias noaelif
/// alias addEventListener-defuser
/// alias aeld
/******************************************************************************/
// Important!
// Isolate from global scope
(function uBOL_noAddEventListenerIf() {
/******************************************************************************/
// $rulesetId$
const argsList = self.$argsList$;
const hostnamesMap = new Map(self.$hostnamesMap$);
/******************************************************************************/
const regexpFromArg = arg => {
if ( arg === '' ) { return /^/; }
if ( /^\/.+\/$/.test(arg) ) { return new RegExp(arg.slice(1,-1)); }
return new RegExp(arg.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
};
/******************************************************************************/
// Dependencies
const scriptletGlobals = new Map();
function safeSelf() {
if ( scriptletGlobals.has('safeSelf') ) {
return scriptletGlobals.get('safeSelf');
}
const safe = {
'RegExp': self.RegExp,
'RegExp_test': self.RegExp.prototype.test,
'RegExp_exec': self.RegExp.prototype.exec,
'addEventListener': self.EventTarget.prototype.addEventListener,
'removeEventListener': self.EventTarget.prototype.removeEventListener,
};
scriptletGlobals.set('safeSelf', safe);
return safe;
}
function runAt(fn, when) {
const intFromReadyState = state => {
const targets = {
'loading': 1,
'interactive': 2, 'end': 2, '2': 2,
'complete': 3, 'idle': 3, '3': 3,
};
const tokens = Array.isArray(state) ? state : [ state ];
for ( const token of tokens ) {
const prop = `${token}`;
if ( targets.hasOwnProperty(prop) === false ) { continue; }
return targets[prop];
}
return 0;
};
const runAt = intFromReadyState(when);
if ( intFromReadyState(document.readyState) >= runAt ) {
fn(); return;
}
const onStateChange = ( ) => {
if ( intFromReadyState(document.readyState) < runAt ) { return; }
fn();
safe.removeEventListener.apply(document, args);
};
const safe = safeSelf();
const args = [ 'readystatechange', onStateChange, { capture: true } ];
safe.addEventListener.apply(document, args);
}
/******************************************************************************/
const scriptlet = (
arg1 = '',
arg2 = ''
) => {
const details = typeof arg1 !== 'object'
? { type: arg1, pattern: arg2 }
: arg1;
const { type = '', pattern = '' } = details;
if ( typeof type !== 'string' ) { return; }
if ( typeof pattern !== 'string' ) { return; }
const reType = regexpFromArg(type);
const rePattern = regexpFromArg(pattern);
const trapEddEventListeners = ( ) => {
const eventListenerHandler = {
apply: function(target, thisArg, args) {
let type, handler;
try {
type = String(args[0]);
handler = String(args[1]);
} catch(ex) {
}
if (
reType.test(type) === false ||
rePattern.test(handler) === false
) {
return target.apply(thisArg, args);
}
}
};
self.EventTarget.prototype.addEventListener = new Proxy(
self.EventTarget.prototype.addEventListener,
eventListenerHandler
);
};
runAt(( ) => {
trapEddEventListeners();
}, details.runAt);
};
/******************************************************************************/
let hn;
try { hn = document.location.hostname; } catch(ex) { }
while ( hn ) {
if ( hostnamesMap.has(hn) ) {
let argsIndices = hostnamesMap.get(hn);
if ( typeof argsIndices === 'number' ) { argsIndices = [ argsIndices ]; }
for ( const argsIndex of argsIndices ) {
const details = argsList[argsIndex];
if ( details.n && details.n.includes(hn) ) { continue; }
try { scriptlet(...details.a); } catch(ex) {}
}
}
if ( hn === '*' ) { break; }
const pos = hn.indexOf('.');
if ( pos !== -1 ) {
hn = hn.slice(pos + 1);
} else {
hn = '*';
}
}
argsList.length = 0;
hostnamesMap.clear();
/******************************************************************************/
})();
/******************************************************************************/

View File

@ -1,143 +0,0 @@
/*******************************************************************************
uBlock Origin - a browser extension to block requests.
Copyright (C) 2019-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
The scriptlets below are meant to be injected only into a
web page context.
*/
/* jshint esversion:11 */
'use strict';
/******************************************************************************/
/// name no-fetch-if
/******************************************************************************/
// Important!
// Isolate from global scope
(function uBOL_noFetchIf() {
/******************************************************************************/
// $rulesetId$
const argsList = self.$argsList$;
const hostnamesMap = new Map(self.$hostnamesMap$);
/******************************************************************************/
const scriptlet = (
conditions = ''
) => {
const needles = [];
for ( const condition of conditions.split(/\s+/) ) {
if ( condition === '' ) { continue; }
const pos = condition.indexOf(':');
let key, value;
if ( pos !== -1 ) {
key = condition.slice(0, pos);
value = condition.slice(pos + 1);
} else {
key = 'url';
value = condition;
}
if ( value === '' ) {
value = '^';
} else if ( value.startsWith('/') && value.endsWith('/') ) {
value = value.slice(1, -1);
} else {
value = value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
needles.push({ key, re: new RegExp(value) });
}
self.fetch = new Proxy(self.fetch, {
apply: function(target, thisArg, args) {
let proceed = true;
try {
let details;
if ( args[0] instanceof self.Request ) {
details = args[0];
} else {
details = Object.assign({ url: args[0] }, args[1]);
}
const props = new Map();
for ( const prop in details ) {
let v = details[prop];
if ( typeof v !== 'string' ) {
try { v = JSON.stringify(v); }
catch(ex) { }
}
if ( typeof v !== 'string' ) { continue; }
props.set(prop, v);
}
proceed = needles.length === 0;
for ( const { key, re } of needles ) {
if (
props.has(key) === false ||
re.test(props.get(key)) === false
) {
proceed = true;
break;
}
}
} catch(ex) {
}
return proceed
? Reflect.apply(target, thisArg, args)
: Promise.resolve(new Response());
}
});
};
/******************************************************************************/
let hn;
try { hn = document.location.hostname; } catch(ex) { }
while ( hn ) {
if ( hostnamesMap.has(hn) ) {
let argsIndices = hostnamesMap.get(hn);
if ( typeof argsIndices === 'number' ) { argsIndices = [ argsIndices ]; }
for ( const argsIndex of argsIndices ) {
const details = argsList[argsIndex];
if ( details.n && details.n.includes(hn) ) { continue; }
try { scriptlet(...details.a); } catch(ex) {}
}
}
if ( hn === '*' ) { break; }
const pos = hn.indexOf('.');
if ( pos !== -1 ) {
hn = hn.slice(pos + 1);
} else {
hn = '*';
}
}
argsList.length = 0;
hostnamesMap.clear();
/******************************************************************************/
})();
/******************************************************************************/

View File

@ -1,127 +0,0 @@
/*******************************************************************************
uBlock Origin - a browser extension to block requests.
Copyright (C) 2019-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
The scriptlets below are meant to be injected only into a
web page context.
*/
/* jshint esversion:11 */
'use strict';
/******************************************************************************/
/// name no-setinterval-if
/// alias no-setInterval-if
/// alias nosiif
/******************************************************************************/
// Important!
// Isolate from global scope
(function uBOL_noSetIntervalIf() {
/******************************************************************************/
// $rulesetId$
const argsList = self.$argsList$;
const hostnamesMap = new Map(self.$hostnamesMap$);
/******************************************************************************/
const scriptlet = (
needle = '',
delay = ''
) => {
const needleNot = needle.charAt(0) === '!';
if ( needleNot ) { needle = needle.slice(1); }
if ( delay === '' ) { delay = undefined; }
let delayNot = false;
if ( delay !== undefined ) {
delayNot = delay.charAt(0) === '!';
if ( delayNot ) { delay = delay.slice(1); }
delay = parseInt(delay, 10);
}
if ( needle.startsWith('/') && needle.endsWith('/') ) {
needle = needle.slice(1,-1);
} else if ( needle !== '' ) {
needle = needle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
const reNeedle = new RegExp(needle);
const regexpTest = RegExp.prototype.test;
self.setInterval = new Proxy(self.setInterval, {
apply: function(target, thisArg, args) {
const a = String(args[0]);
const b = args[1];
let defuse;
if ( needle !== '' ) {
defuse = regexpTest.call(reNeedle, a) !== needleNot;
}
if ( defuse !== false && delay !== undefined ) {
defuse = (b === delay || isNaN(b) && isNaN(delay) ) !== delayNot;
}
if ( defuse ) {
args[0] = function(){};
}
return target.apply(thisArg, args);
},
get(target, prop, receiver) {
if ( prop === 'toString' ) {
return target.toString.bind(target);
}
return Reflect.get(target, prop, receiver);
},
});
};
/******************************************************************************/
let hn;
try { hn = document.location.hostname; } catch(ex) { }
while ( hn ) {
if ( hostnamesMap.has(hn) ) {
let argsIndices = hostnamesMap.get(hn);
if ( typeof argsIndices === 'number' ) { argsIndices = [ argsIndices ]; }
for ( const argsIndex of argsIndices ) {
const details = argsList[argsIndex];
if ( details.n && details.n.includes(hn) ) { continue; }
try { scriptlet(...details.a); } catch(ex) {}
}
}
if ( hn === '*' ) { break; }
const pos = hn.indexOf('.');
if ( pos !== -1 ) {
hn = hn.slice(pos + 1);
} else {
hn = '*';
}
}
argsList.length = 0;
hostnamesMap.clear();
/******************************************************************************/
})();
/******************************************************************************/

View File

@ -1,123 +0,0 @@
/*******************************************************************************
uBlock Origin - a browser extension to block requests.
Copyright (C) 2019-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
The scriptlets below are meant to be injected only into a
web page context.
*/
/* jshint esversion:11 */
'use strict';
/******************************************************************************/
/// name no-settimeout-if.entity
/// alias no-setTimeout-if.entity
/// alias nostif.entity
/******************************************************************************/
// Important!
// Isolate from global scope
(function uBOL_noSetTimeoutIfEntity() {
/******************************************************************************/
// $rulesetId$
const argsList = self.$argsList$;
const entitiesMap = new Map(self.$entitiesMap$);
/******************************************************************************/
const scriptlet = (
needle = '',
delay = ''
) => {
const needleNot = needle.charAt(0) === '!';
if ( needleNot ) { needle = needle.slice(1); }
if ( delay === '' ) { delay = undefined; }
let delayNot = false;
if ( delay !== undefined ) {
delayNot = delay.charAt(0) === '!';
if ( delayNot ) { delay = delay.slice(1); }
delay = parseInt(delay, 10);
}
if ( needle.startsWith('/') && needle.endsWith('/') ) {
needle = needle.slice(1,-1);
} else if ( needle !== '' ) {
needle = needle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
const reNeedle = new RegExp(needle);
const regexpTest = RegExp.prototype.test;
self.setTimeout = new Proxy(self.setTimeout, {
apply: function(target, thisArg, args) {
const a = String(args[0]);
const b = args[1];
let defuse;
if ( needle !== '' ) {
defuse = regexpTest.call(reNeedle, a) !== needleNot;
}
if ( defuse !== false && delay !== undefined ) {
defuse = (b === delay || isNaN(b) && isNaN(delay) ) !== delayNot;
}
if ( defuse ) {
args[0] = function(){};
}
return target.apply(thisArg, args);
},
get(target, prop, receiver) {
if ( prop === 'toString' ) {
return target.toString.bind(target);
}
return Reflect.get(target, prop, receiver);
},
});
};
/******************************************************************************/
const hnparts = [];
try { hnparts.push(...document.location.hostname.split('.')); } catch(ex) { }
const hnpartslen = hnparts.length - 1;
for ( let i = 0; i < hnpartslen; i++ ) {
for ( let j = hnpartslen; j > i; j-- ) {
const hn = hnparts.slice(i).join('.');
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 ) {
const details = argsList[argsIndex];
if ( details.n && details.n.includes(hn) ) { continue; }
try { scriptlet(...details.a); } catch(ex) {}
}
}
}
argsList.length = 0;
entitiesMap.clear();
/******************************************************************************/
})();
/******************************************************************************/

View File

@ -1,127 +0,0 @@
/*******************************************************************************
uBlock Origin - a browser extension to block requests.
Copyright (C) 2019-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
The scriptlets below are meant to be injected only into a
web page context.
*/
/* jshint esversion:11 */
'use strict';
/******************************************************************************/
/// name no-settimeout-if
/// alias no-setTimeout-if
/// alias nostif
/******************************************************************************/
// Important!
// Isolate from global scope
(function uBOL_noSetTimeoutIf() {
/******************************************************************************/
// $rulesetId$
const argsList = self.$argsList$;
const hostnamesMap = new Map(self.$hostnamesMap$);
/******************************************************************************/
const scriptlet = (
needle = '',
delay = ''
) => {
const needleNot = needle.charAt(0) === '!';
if ( needleNot ) { needle = needle.slice(1); }
if ( delay === '' ) { delay = undefined; }
let delayNot = false;
if ( delay !== undefined ) {
delayNot = delay.charAt(0) === '!';
if ( delayNot ) { delay = delay.slice(1); }
delay = parseInt(delay, 10);
}
if ( needle.startsWith('/') && needle.endsWith('/') ) {
needle = needle.slice(1,-1);
} else if ( needle !== '' ) {
needle = needle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
const reNeedle = new RegExp(needle);
const regexpTest = RegExp.prototype.test;
self.setTimeout = new Proxy(self.setTimeout, {
apply: function(target, thisArg, args) {
const a = String(args[0]);
const b = args[1];
let defuse;
if ( needle !== '' ) {
defuse = regexpTest.call(reNeedle, a) !== needleNot;
}
if ( defuse !== false && delay !== undefined ) {
defuse = (b === delay || isNaN(b) && isNaN(delay) ) !== delayNot;
}
if ( defuse ) {
args[0] = function(){};
}
return target.apply(thisArg, args);
},
get(target, prop, receiver) {
if ( prop === 'toString' ) {
return target.toString.bind(target);
}
return Reflect.get(target, prop, receiver);
},
});
};
/******************************************************************************/
let hn;
try { hn = document.location.hostname; } catch(ex) { }
while ( hn ) {
if ( hostnamesMap.has(hn) ) {
let argsIndices = hostnamesMap.get(hn);
if ( typeof argsIndices === 'number' ) { argsIndices = [ argsIndices ]; }
for ( const argsIndex of argsIndices ) {
const details = argsList[argsIndex];
if ( details.n && details.n.includes(hn) ) { continue; }
try { scriptlet(...details.a); } catch(ex) {}
}
}
if ( hn === '*' ) { break; }
const pos = hn.indexOf('.');
if ( pos !== -1 ) {
hn = hn.slice(pos + 1);
} else {
hn = '*';
}
}
argsList.length = 0;
hostnamesMap.clear();
/******************************************************************************/
})();
/******************************************************************************/

View File

@ -1,154 +0,0 @@
/*******************************************************************************
uBlock Origin - a browser extension to block requests.
Copyright (C) 2019-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
The scriptlets below are meant to be injected only into a
web page context.
*/
/* jshint esversion:11 */
'use strict';
/******************************************************************************/
/// name no-windowopen-if.entity
/// alias no-windowOpen-if.entity
/// alias nowoif.entity
/******************************************************************************/
// Important!
// Isolate from global scope
(function uBOL_noWindowOpenIfEntity() {
/******************************************************************************/
// $rulesetId$
const argsList = self.$argsList$;
const entitiesMap = new Map(self.$entitiesMap$);
/******************************************************************************/
const scriptlet = (
needle = '',
delay = '',
options = ''
) => {
const newSyntax = /^[01]?$/.test(needle) === false;
let pattern = '';
let targetResult = true;
let autoRemoveAfter = -1;
if ( newSyntax ) {
pattern = needle;
if ( pattern.startsWith('!') ) {
targetResult = false;
pattern = pattern.slice(1);
}
autoRemoveAfter = parseInt(delay);
if ( isNaN(autoRemoveAfter) ) {
autoRemoveAfter = -1;
}
} else {
pattern = delay;
if ( needle === '0' ) {
targetResult = false;
}
}
if ( pattern === '' ) {
pattern = '.?';
} else if ( /^\/.+\/$/.test(pattern) ) {
pattern = pattern.slice(1,-1);
} else {
pattern = pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
const rePattern = new RegExp(pattern);
const createDecoy = function(tag, urlProp, url) {
const decoy = document.createElement(tag);
decoy[urlProp] = url;
decoy.style.setProperty('height','1px', 'important');
decoy.style.setProperty('position','fixed', 'important');
decoy.style.setProperty('top','-1px', 'important');
decoy.style.setProperty('width','1px', 'important');
document.body.appendChild(decoy);
setTimeout(( ) => decoy.remove(), autoRemoveAfter * 1000);
return decoy;
};
window.open = new Proxy(window.open, {
apply: function(target, thisArg, args) {
const url = args[0];
if ( rePattern.test(url) !== targetResult ) {
return target.apply(thisArg, args);
}
if ( autoRemoveAfter < 0 ) { return null; }
const decoy = /\bobj\b/.test(options)
? createDecoy('object', 'data', url)
: createDecoy('iframe', 'src', url);
let popup = decoy.contentWindow;
if ( typeof popup === 'object' && popup !== null ) {
Object.defineProperty(popup, 'closed', { value: false });
} else {
const noopFunc = (function(){}).bind(self);
popup = new Proxy(self, {
get: function(target, prop) {
if ( prop === 'closed' ) { return false; }
const r = Reflect.get(...arguments);
if ( typeof r === 'function' ) { return noopFunc; }
return target[prop];
},
set: function() {
return Reflect.set(...arguments);
},
});
}
return popup;
}
});
};
/******************************************************************************/
const hnparts = [];
try { hnparts.push(...document.location.hostname.split('.')); } catch(ex) { }
const hnpartslen = hnparts.length - 1;
for ( let i = 0; i < hnpartslen; i++ ) {
for ( let j = hnpartslen; j > i; j-- ) {
const hn = hnparts.slice(i).join('.');
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 ) {
const details = argsList[argsIndex];
if ( details.n && details.n.includes(hn) ) { continue; }
try { scriptlet(...details.a); } catch(ex) {}
}
}
}
argsList.length = 0;
entitiesMap.clear();
/******************************************************************************/
})();
/******************************************************************************/

View File

@ -1,159 +0,0 @@
/*******************************************************************************
uBlock Origin - a browser extension to block requests.
Copyright (C) 2019-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
The scriptlets below are meant to be injected only into a
web page context.
*/
/* jshint esversion:11 */
'use strict';
/******************************************************************************/
/// name no-windowopen-if
/// alias no-windowOpen-if
/// alias nowoif
/// alias window.open-defuser
/******************************************************************************/
// Important!
// Isolate from global scope
(function uBOL_noWindowOpenIf() {
/******************************************************************************/
// $rulesetId$
const argsList = self.$argsList$;
const hostnamesMap = new Map(self.$hostnamesMap$);
/******************************************************************************/
const scriptlet = (
needle = '',
delay = '',
options = ''
) => {
const newSyntax = /^[01]?$/.test(needle) === false;
let pattern = '';
let targetResult = true;
let autoRemoveAfter = -1;
if ( newSyntax ) {
pattern = needle;
if ( pattern.startsWith('!') ) {
targetResult = false;
pattern = pattern.slice(1);
}
autoRemoveAfter = parseInt(delay);
if ( isNaN(autoRemoveAfter) ) {
autoRemoveAfter = -1;
}
} else {
pattern = delay;
if ( needle === '0' ) {
targetResult = false;
}
}
if ( pattern === '' ) {
pattern = '.?';
} else if ( /^\/.+\/$/.test(pattern) ) {
pattern = pattern.slice(1,-1);
} else {
pattern = pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
const rePattern = new RegExp(pattern);
const createDecoy = function(tag, urlProp, url) {
const decoy = document.createElement(tag);
decoy[urlProp] = url;
decoy.style.setProperty('height','1px', 'important');
decoy.style.setProperty('position','fixed', 'important');
decoy.style.setProperty('top','-1px', 'important');
decoy.style.setProperty('width','1px', 'important');
document.body.appendChild(decoy);
setTimeout(( ) => decoy.remove(), autoRemoveAfter * 1000);
return decoy;
};
window.open = new Proxy(window.open, {
apply: function(target, thisArg, args) {
const url = args[0];
if ( rePattern.test(url) !== targetResult ) {
return target.apply(thisArg, args);
}
if ( autoRemoveAfter < 0 ) { return null; }
const decoy = /\bobj\b/.test(options)
? createDecoy('object', 'data', url)
: createDecoy('iframe', 'src', url);
let popup = decoy.contentWindow;
if ( typeof popup === 'object' && popup !== null ) {
Object.defineProperty(popup, 'closed', { value: false });
} else {
const noopFunc = (function(){}).bind(self);
popup = new Proxy(self, {
get: function(target, prop) {
if ( prop === 'closed' ) { return false; }
const r = Reflect.get(...arguments);
if ( typeof r === 'function' ) { return noopFunc; }
return target[prop];
},
set: function() {
return Reflect.set(...arguments);
},
});
}
return popup;
}
});
};
/******************************************************************************/
let hn;
try { hn = document.location.hostname; } catch(ex) { }
while ( hn ) {
if ( hostnamesMap.has(hn) ) {
let argsIndices = hostnamesMap.get(hn);
if ( typeof argsIndices === 'number' ) { argsIndices = [ argsIndices ]; }
for ( const argsIndex of argsIndices ) {
const details = argsList[argsIndex];
if ( details.n && details.n.includes(hn) ) { continue; }
try { scriptlet(...details.a); } catch(ex) {}
}
}
if ( hn === '*' ) { break; }
const pos = hn.indexOf('.');
if ( pos !== -1 ) {
hn = hn.slice(pos + 1);
} else {
hn = '*';
}
}
argsList.length = 0;
hostnamesMap.clear();
/******************************************************************************/
})();
/******************************************************************************/

View File

@ -1,145 +0,0 @@
/*******************************************************************************
uBlock Origin - a browser extension to block requests.
Copyright (C) 2019-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
The scriptlets below are meant to be injected only into a
web page context.
*/
/* jshint esversion:11 */
'use strict';
/******************************************************************************/
/// name no-xhr-if
/******************************************************************************/
// Important!
// Isolate from global scope
(function uBOL_noXhrIf() {
/******************************************************************************/
// $rulesetId$
const argsList = self.$argsList$;
const hostnamesMap = new Map(self.$hostnamesMap$);
/******************************************************************************/
const scriptlet = (
conditions = ''
) => {
const xhrInstances = new WeakMap();
const needles = [];
for ( const condition of conditions.split(/\s+/) ) {
if ( condition === '' ) { continue; }
const pos = condition.indexOf(':');
let key, value;
if ( pos !== -1 ) {
key = condition.slice(0, pos);
value = condition.slice(pos + 1);
} else {
key = 'url';
value = condition;
}
if ( value === '' ) {
value = '^';
} else if ( value.startsWith('/') && value.endsWith('/') ) {
value = value.slice(1, -1);
} else {
value = value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
needles.push({ key, re: new RegExp(value) });
}
self.XMLHttpRequest = class extends self.XMLHttpRequest {
open(...args) {
const argNames = [ 'method', 'url' ];
const haystack = new Map();
for ( let i = 0; i < args.length && i < argNames.length; i++ ) {
haystack.set(argNames[i], args[i]);
}
if ( haystack.size !== 0 ) {
let matches = true;
for ( const { key, re } of needles ) {
matches = re.test(haystack.get(key) || '');
if ( matches === false ) { break; }
}
if ( matches ) {
xhrInstances.set(this, haystack);
}
}
return super.open(...args);
}
send(...args) {
const haystack = xhrInstances.get(this);
if ( haystack === undefined ) {
return super.send(...args);
}
Object.defineProperties(this, {
readyState: { value: 4, writable: false },
response: { value: '', writable: false },
responseText: { value: '', writable: false },
responseURL: { value: haystack.get('url'), writable: false },
responseXML: { value: '', writable: false },
status: { value: 200, writable: false },
statusText: { value: 'OK', writable: false },
});
this.dispatchEvent(new Event('readystatechange'));
this.dispatchEvent(new Event('load'));
this.dispatchEvent(new Event('loadend'));
}
};
};
/******************************************************************************/
let hn;
try { hn = document.location.hostname; } catch(ex) { }
while ( hn ) {
if ( hostnamesMap.has(hn) ) {
let argsIndices = hostnamesMap.get(hn);
if ( typeof argsIndices === 'number' ) { argsIndices = [ argsIndices ]; }
for ( const argsIndex of argsIndices ) {
const details = argsList[argsIndex];
if ( details.n && details.n.includes(hn) ) { continue; }
try { scriptlet(...details.a); } catch(ex) {}
}
}
if ( hn === '*' ) { break; }
const pos = hn.indexOf('.');
if ( pos !== -1 ) {
hn = hn.slice(pos + 1);
} else {
hn = '*';
}
}
argsList.length = 0;
hostnamesMap.clear();
/******************************************************************************/
})();
/******************************************************************************/

View File

@ -1,313 +0,0 @@
/*******************************************************************************
uBlock Origin - a browser extension to block requests.
Copyright (C) 2019-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
The scriptlets below are meant to be injected only into a
web page context.
*/
/* jshint esversion:11 */
'use strict';
/******************************************************************************/
/// name set-constant.entity
/// alias set.entity
/******************************************************************************/
// Important!
// Isolate from global scope
(function uBOL_setConstantEntity() {
/******************************************************************************/
// $rulesetId$
const argsList = self.$argsList$;
const entitiesMap = new Map(self.$entitiesMap$);
/******************************************************************************/
// Dependencies
const scriptletGlobals = new Map();
function safeSelf() {
if ( scriptletGlobals.has('safeSelf') ) {
return scriptletGlobals.get('safeSelf');
}
const safe = {
'RegExp': self.RegExp,
'RegExp_test': self.RegExp.prototype.test,
'RegExp_exec': self.RegExp.prototype.exec,
'addEventListener': self.EventTarget.prototype.addEventListener,
'removeEventListener': self.EventTarget.prototype.removeEventListener,
};
scriptletGlobals.set('safeSelf', safe);
return safe;
}
function runAt(fn, when) {
const intFromReadyState = state => {
const targets = {
'loading': 1,
'interactive': 2, 'end': 2, '2': 2,
'complete': 3, 'idle': 3, '3': 3,
};
const tokens = Array.isArray(state) ? state : [ state ];
for ( const token of tokens ) {
const prop = `${token}`;
if ( targets.hasOwnProperty(prop) === false ) { continue; }
return targets[prop];
}
return 0;
};
const runAt = intFromReadyState(when);
if ( intFromReadyState(document.readyState) >= runAt ) {
fn(); return;
}
const onStateChange = ( ) => {
if ( intFromReadyState(document.readyState) < runAt ) { return; }
fn();
safe.removeEventListener.apply(document, args);
};
const safe = safeSelf();
const args = [ 'readystatechange', onStateChange, { capture: true } ];
safe.addEventListener.apply(document, args);
}
/******************************************************************************/
const scriptlet = (
arg1 = '',
arg2 = '',
arg3 = ''
) => {
const details = typeof arg1 !== 'object'
? { prop: arg1, value: arg2 }
: arg1;
if ( arg3 !== '' ) {
if ( /^\d$/.test(arg3) ) {
details.options = [ arg3 ];
} else {
details.options = Array.from(arguments).slice(2);
}
}
const { prop: chain = '', value: cValue = '' } = details;
if ( typeof chain !== 'string' ) { return; }
if ( chain === '' ) { return; }
const options = details.options || [];
function setConstant(chain, cValue) {
const trappedProp = (( ) => {
const pos = chain.lastIndexOf('.');
if ( pos === -1 ) { return chain; }
return chain.slice(pos+1);
})();
if ( trappedProp === '' ) { return; }
const thisScript = document.currentScript;
const objectDefineProperty = Object.defineProperty.bind(Object);
const cloakFunc = fn => {
objectDefineProperty(fn, 'name', { value: trappedProp });
const proxy = new Proxy(fn, {
defineProperty(target, prop) {
if ( prop !== 'toString' ) {
return Reflect.deleteProperty(...arguments);
}
return true;
},
deleteProperty(target, prop) {
if ( prop !== 'toString' ) {
return Reflect.deleteProperty(...arguments);
}
return true;
},
get(target, prop) {
if ( prop === 'toString' ) {
return function() {
return `function ${trappedProp}() { [native code] }`;
}.bind(null);
}
return Reflect.get(...arguments);
},
});
return proxy;
};
if ( cValue === 'undefined' ) {
cValue = undefined;
} else if ( cValue === 'false' ) {
cValue = false;
} else if ( cValue === 'true' ) {
cValue = true;
} else if ( cValue === 'null' ) {
cValue = null;
} else if ( cValue === "''" ) {
cValue = '';
} else if ( cValue === '[]' ) {
cValue = [];
} else if ( cValue === '{}' ) {
cValue = {};
} else if ( cValue === 'noopFunc' ) {
cValue = cloakFunc(function(){});
} else if ( cValue === 'trueFunc' ) {
cValue = cloakFunc(function(){ return true; });
} else if ( cValue === 'falseFunc' ) {
cValue = cloakFunc(function(){ return false; });
} else if ( /^-?\d+$/.test(cValue) ) {
cValue = parseInt(cValue);
if ( isNaN(cValue) ) { return; }
if ( Math.abs(cValue) > 0x7FFF ) { return; }
} else {
return;
}
if ( options.includes('asFunction') ) {
cValue = ( ) => cValue;
} else if ( options.includes('asCallback') ) {
cValue = ( ) => (( ) => cValue);
} else if ( options.includes('asResolved') ) {
cValue = Promise.resolve(cValue);
} else if ( options.includes('asRejected') ) {
cValue = Promise.reject(cValue);
}
let aborted = false;
const mustAbort = function(v) {
if ( aborted ) { return true; }
aborted =
(v !== undefined && v !== null) &&
(cValue !== undefined && cValue !== null) &&
(typeof v !== typeof cValue);
return aborted;
};
// https://github.com/uBlockOrigin/uBlock-issues/issues/156
// Support multiple trappers for the same property.
const trapProp = function(owner, prop, configurable, handler) {
if ( handler.init(configurable ? owner[prop] : cValue) === false ) { return; }
const odesc = Object.getOwnPropertyDescriptor(owner, prop);
let prevGetter, prevSetter;
if ( odesc instanceof Object ) {
owner[prop] = cValue;
if ( odesc.get instanceof Function ) {
prevGetter = odesc.get;
}
if ( odesc.set instanceof Function ) {
prevSetter = odesc.set;
}
}
try {
objectDefineProperty(owner, prop, {
configurable,
get() {
if ( prevGetter !== undefined ) {
prevGetter();
}
return handler.getter(); // cValue
},
set(a) {
if ( prevSetter !== undefined ) {
prevSetter(a);
}
handler.setter(a);
}
});
} catch(ex) {
}
};
const trapChain = function(owner, chain) {
const pos = chain.indexOf('.');
if ( pos === -1 ) {
trapProp(owner, chain, false, {
v: undefined,
init: function(v) {
if ( mustAbort(v) ) { return false; }
this.v = v;
return true;
},
getter: function() {
return document.currentScript === thisScript
? this.v
: cValue;
},
setter: function(a) {
if ( mustAbort(a) === false ) { return; }
cValue = a;
}
});
return;
}
const prop = chain.slice(0, pos);
const v = owner[prop];
chain = chain.slice(pos + 1);
if ( v instanceof Object || typeof v === 'object' && v !== null ) {
trapChain(v, chain);
return;
}
trapProp(owner, prop, true, {
v: undefined,
init: function(v) {
this.v = v;
return true;
},
getter: function() {
return this.v;
},
setter: function(a) {
this.v = a;
if ( a instanceof Object ) {
trapChain(a, chain);
}
}
});
};
trapChain(window, chain);
}
runAt(( ) => {
setConstant(chain, cValue);
}, options);
};
/******************************************************************************/
const hnparts = [];
try { hnparts.push(...document.location.hostname.split('.')); } catch(ex) { }
const hnpartslen = hnparts.length - 1;
for ( let i = 0; i < hnpartslen; i++ ) {
for ( let j = hnpartslen; j > i; j-- ) {
const hn = hnparts.slice(i).join('.');
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 ) {
const details = argsList[argsIndex];
if ( details.n && details.n.includes(hn) ) { continue; }
try { scriptlet(...details.a); } catch(ex) {}
}
}
}
argsList.length = 0;
entitiesMap.clear();
/******************************************************************************/
})();
/******************************************************************************/

View File

@ -1,316 +0,0 @@
/*******************************************************************************
uBlock Origin - a browser extension to block requests.
Copyright (C) 2019-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
The scriptlets below are meant to be injected only into a
web page context.
*/
/* jshint esversion:11 */
'use strict';
/******************************************************************************/
/// name set-constant
/// alias set
/******************************************************************************/
// Important!
// Isolate from global scope
(function uBOL_setConstant() {
/******************************************************************************/
// $rulesetId$
const argsList = self.$argsList$;
const hostnamesMap = new Map(self.$hostnamesMap$);
/******************************************************************************/
// Dependencies
const scriptletGlobals = new Map();
function safeSelf() {
if ( scriptletGlobals.has('safeSelf') ) {
return scriptletGlobals.get('safeSelf');
}
const safe = {
'RegExp': self.RegExp,
'RegExp_test': self.RegExp.prototype.test,
'RegExp_exec': self.RegExp.prototype.exec,
'addEventListener': self.EventTarget.prototype.addEventListener,
'removeEventListener': self.EventTarget.prototype.removeEventListener,
};
scriptletGlobals.set('safeSelf', safe);
return safe;
}
function runAt(fn, when) {
const intFromReadyState = state => {
const targets = {
'loading': 1,
'interactive': 2, 'end': 2, '2': 2,
'complete': 3, 'idle': 3, '3': 3,
};
const tokens = Array.isArray(state) ? state : [ state ];
for ( const token of tokens ) {
const prop = `${token}`;
if ( targets.hasOwnProperty(prop) === false ) { continue; }
return targets[prop];
}
return 0;
};
const runAt = intFromReadyState(when);
if ( intFromReadyState(document.readyState) >= runAt ) {
fn(); return;
}
const onStateChange = ( ) => {
if ( intFromReadyState(document.readyState) < runAt ) { return; }
fn();
safe.removeEventListener.apply(document, args);
};
const safe = safeSelf();
const args = [ 'readystatechange', onStateChange, { capture: true } ];
safe.addEventListener.apply(document, args);
}
/******************************************************************************/
const scriptlet = (
arg1 = '',
arg2 = '',
arg3 = ''
) => {
const details = typeof arg1 !== 'object'
? { prop: arg1, value: arg2 }
: arg1;
if ( arg3 !== '' ) {
if ( /^\d$/.test(arg3) ) {
details.options = [ arg3 ];
} else {
details.options = Array.from(arguments).slice(2);
}
}
const { prop: chain = '', value: cValue = '' } = details;
if ( typeof chain !== 'string' ) { return; }
if ( chain === '' ) { return; }
const options = details.options || [];
function setConstant(chain, cValue) {
const trappedProp = (( ) => {
const pos = chain.lastIndexOf('.');
if ( pos === -1 ) { return chain; }
return chain.slice(pos+1);
})();
if ( trappedProp === '' ) { return; }
const thisScript = document.currentScript;
const objectDefineProperty = Object.defineProperty.bind(Object);
const cloakFunc = fn => {
objectDefineProperty(fn, 'name', { value: trappedProp });
const proxy = new Proxy(fn, {
defineProperty(target, prop) {
if ( prop !== 'toString' ) {
return Reflect.deleteProperty(...arguments);
}
return true;
},
deleteProperty(target, prop) {
if ( prop !== 'toString' ) {
return Reflect.deleteProperty(...arguments);
}
return true;
},
get(target, prop) {
if ( prop === 'toString' ) {
return function() {
return `function ${trappedProp}() { [native code] }`;
}.bind(null);
}
return Reflect.get(...arguments);
},
});
return proxy;
};
if ( cValue === 'undefined' ) {
cValue = undefined;
} else if ( cValue === 'false' ) {
cValue = false;
} else if ( cValue === 'true' ) {
cValue = true;
} else if ( cValue === 'null' ) {
cValue = null;
} else if ( cValue === "''" ) {
cValue = '';
} else if ( cValue === '[]' ) {
cValue = [];
} else if ( cValue === '{}' ) {
cValue = {};
} else if ( cValue === 'noopFunc' ) {
cValue = cloakFunc(function(){});
} else if ( cValue === 'trueFunc' ) {
cValue = cloakFunc(function(){ return true; });
} else if ( cValue === 'falseFunc' ) {
cValue = cloakFunc(function(){ return false; });
} else if ( /^-?\d+$/.test(cValue) ) {
cValue = parseInt(cValue);
if ( isNaN(cValue) ) { return; }
if ( Math.abs(cValue) > 0x7FFF ) { return; }
} else {
return;
}
if ( options.includes('asFunction') ) {
cValue = ( ) => cValue;
} else if ( options.includes('asCallback') ) {
cValue = ( ) => (( ) => cValue);
} else if ( options.includes('asResolved') ) {
cValue = Promise.resolve(cValue);
} else if ( options.includes('asRejected') ) {
cValue = Promise.reject(cValue);
}
let aborted = false;
const mustAbort = function(v) {
if ( aborted ) { return true; }
aborted =
(v !== undefined && v !== null) &&
(cValue !== undefined && cValue !== null) &&
(typeof v !== typeof cValue);
return aborted;
};
// https://github.com/uBlockOrigin/uBlock-issues/issues/156
// Support multiple trappers for the same property.
const trapProp = function(owner, prop, configurable, handler) {
if ( handler.init(configurable ? owner[prop] : cValue) === false ) { return; }
const odesc = Object.getOwnPropertyDescriptor(owner, prop);
let prevGetter, prevSetter;
if ( odesc instanceof Object ) {
owner[prop] = cValue;
if ( odesc.get instanceof Function ) {
prevGetter = odesc.get;
}
if ( odesc.set instanceof Function ) {
prevSetter = odesc.set;
}
}
try {
objectDefineProperty(owner, prop, {
configurable,
get() {
if ( prevGetter !== undefined ) {
prevGetter();
}
return handler.getter(); // cValue
},
set(a) {
if ( prevSetter !== undefined ) {
prevSetter(a);
}
handler.setter(a);
}
});
} catch(ex) {
}
};
const trapChain = function(owner, chain) {
const pos = chain.indexOf('.');
if ( pos === -1 ) {
trapProp(owner, chain, false, {
v: undefined,
init: function(v) {
if ( mustAbort(v) ) { return false; }
this.v = v;
return true;
},
getter: function() {
return document.currentScript === thisScript
? this.v
: cValue;
},
setter: function(a) {
if ( mustAbort(a) === false ) { return; }
cValue = a;
}
});
return;
}
const prop = chain.slice(0, pos);
const v = owner[prop];
chain = chain.slice(pos + 1);
if ( v instanceof Object || typeof v === 'object' && v !== null ) {
trapChain(v, chain);
return;
}
trapProp(owner, prop, true, {
v: undefined,
init: function(v) {
this.v = v;
return true;
},
getter: function() {
return this.v;
},
setter: function(a) {
this.v = a;
if ( a instanceof Object ) {
trapChain(a, chain);
}
}
});
};
trapChain(window, chain);
}
runAt(( ) => {
setConstant(chain, cValue);
}, options);
};
/******************************************************************************/
let hn;
try { hn = document.location.hostname; } catch(ex) { }
while ( hn ) {
if ( hostnamesMap.has(hn) ) {
let argsIndices = hostnamesMap.get(hn);
if ( typeof argsIndices === 'number' ) { argsIndices = [ argsIndices ]; }
for ( const argsIndex of argsIndices ) {
const details = argsList[argsIndex];
if ( details.n && details.n.includes(hn) ) { continue; }
try { scriptlet(...details.a); } catch(ex) {}
}
}
if ( hn === '*' ) { break; }
const pos = hn.indexOf('.');
if ( pos !== -1 ) {
hn = hn.slice(pos + 1);
} else {
hn = '*';
}
}
argsList.length = 0;
hostnamesMap.clear();
/******************************************************************************/
})();
/******************************************************************************/

View File

@ -96,13 +96,27 @@ function addExtendedToDNR(context, parser) {
context.scriptletFilters = new Map();
}
const exception = parser.isException();
const raw = parser.getTypeString(sfp.NODE_TYPE_EXT_PATTERN_RAW);
const root = parser.getBranchFromType(sfp.NODE_TYPE_EXT_PATTERN_SCRIPTLET);
const walker = parser.getWalker(root);
const args = [];
for ( let node = walker.next(); node !== 0; node = walker.next() ) {
switch ( parser.getNodeType(node) ) {
case sfp.NODE_TYPE_EXT_PATTERN_SCRIPTLET_TOKEN:
case sfp.NODE_TYPE_EXT_PATTERN_SCRIPTLET_ARG:
args.push(parser.getNodeString(node));
break;
default:
break;
}
}
walker.dispose();
const argsToken = JSON.stringify(args);
for ( const { hn, not, bad } of parser.getExtFilterDomainIterator() ) {
if ( bad ) { continue; }
if ( exception ) { continue; }
let details = context.scriptletFilters.get(raw);
let details = context.scriptletFilters.get(argsToken);
if ( details === undefined ) {
context.scriptletFilters.set(raw, details = {});
context.scriptletFilters.set(argsToken, details = { args });
}
if ( not ) {
if ( details.excludeMatches === undefined ) {

View File

@ -82,6 +82,7 @@ if [ "$QUICK" != "yes" ]; then
cp platform/mv3/*.js $TMPDIR/
cp platform/mv3/extension/js/utils.js $TMPDIR/js/
cp assets/assets.json $TMPDIR/
cp assets/resources/scriptlets.js $TMPDIR/
cp -R platform/mv3/scriptlets $TMPDIR/
mkdir -p $TMPDIR/web_accessible_resources
cp src/web_accessible_resources/* $TMPDIR/web_accessible_resources/