mirror of https://github.com/gorhill/uBlock.git
[mv3] Mind trusted-site directives when registering content scripts
This commit is contained in:
parent
f374c05753
commit
70a0de9d00
|
@ -25,19 +25,34 @@
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
import { browser, dnr, i18n, runtime } from './ext.js';
|
import {
|
||||||
import { fetchJSON } from './fetch.js';
|
browser,
|
||||||
import { getInjectableCount, registerInjectable } from './scripting-manager.js';
|
dnr,
|
||||||
import { parsedURLromOrigin } from './utils.js';
|
runtime,
|
||||||
|
} from './ext.js';
|
||||||
|
|
||||||
|
import {
|
||||||
|
CURRENT_CONFIG_BASE_RULE_ID,
|
||||||
|
getRulesetDetails,
|
||||||
|
getDynamicRules,
|
||||||
|
defaultRulesetsFromLanguage,
|
||||||
|
enableRulesets,
|
||||||
|
getEnabledRulesetsStats,
|
||||||
|
updateRegexRules,
|
||||||
|
} from './ruleset-manager.js';
|
||||||
|
|
||||||
|
import {
|
||||||
|
getInjectableCount,
|
||||||
|
registerInjectable,
|
||||||
|
} from './scripting-manager.js';
|
||||||
|
|
||||||
|
import {
|
||||||
|
matchesTrustedSiteDirective,
|
||||||
|
toggleTrustedSiteDirective,
|
||||||
|
} from './trusted-sites.js';
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
const RULE_REALM_SIZE = 1000000;
|
|
||||||
const REGEXES_REALM_START = 1000000;
|
|
||||||
const REGEXES_REALM_END = REGEXES_REALM_START + RULE_REALM_SIZE;
|
|
||||||
const TRUSTED_DIRECTIVE_BASE_RULE_ID = 8000000;
|
|
||||||
const CURRENT_CONFIG_BASE_RULE_ID = 9000000;
|
|
||||||
|
|
||||||
const rulesetConfig = {
|
const rulesetConfig = {
|
||||||
version: '',
|
version: '',
|
||||||
enabledRulesets: [],
|
enabledRulesets: [],
|
||||||
|
@ -45,42 +60,6 @@ const rulesetConfig = {
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
let rulesetDetailsPromise;
|
|
||||||
|
|
||||||
function getRulesetDetails() {
|
|
||||||
if ( rulesetDetailsPromise !== undefined ) {
|
|
||||||
return rulesetDetailsPromise;
|
|
||||||
}
|
|
||||||
rulesetDetailsPromise = fetchJSON('/rulesets/ruleset-details').then(entries => {
|
|
||||||
const map = new Map(
|
|
||||||
entries.map(entry => [ entry.id, entry ])
|
|
||||||
);
|
|
||||||
return map;
|
|
||||||
});
|
|
||||||
return rulesetDetailsPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
let dynamicRuleMapPromise;
|
|
||||||
|
|
||||||
function getDynamicRules() {
|
|
||||||
if ( dynamicRuleMapPromise !== undefined ) {
|
|
||||||
return dynamicRuleMapPromise;
|
|
||||||
}
|
|
||||||
dynamicRuleMapPromise = dnr.getDynamicRules().then(rules => {
|
|
||||||
const map = new Map(
|
|
||||||
rules.map(rule => [ rule.id, rule ])
|
|
||||||
);
|
|
||||||
console.log(`Dynamic rule count: ${map.size}`);
|
|
||||||
console.log(`Available dynamic rule count: ${dnr.MAX_NUMBER_OF_DYNAMIC_AND_SESSION_RULES - map.size}`);
|
|
||||||
return map;
|
|
||||||
});
|
|
||||||
return dynamicRuleMapPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
function getCurrentVersion() {
|
function getCurrentVersion() {
|
||||||
return runtime.getManifest().version;
|
return runtime.getManifest().version;
|
||||||
}
|
}
|
||||||
|
@ -134,276 +113,6 @@ async function saveRulesetConfig() {
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
async function updateRegexRules() {
|
|
||||||
const [
|
|
||||||
rulesetDetails,
|
|
||||||
dynamicRules
|
|
||||||
] = await Promise.all([
|
|
||||||
getRulesetDetails(),
|
|
||||||
dnr.getDynamicRules(),
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Avoid testing already tested regexes
|
|
||||||
const validRegexSet = new Set(
|
|
||||||
dynamicRules.filter(rule =>
|
|
||||||
rule.condition?.regexFilter && true || false
|
|
||||||
).map(rule =>
|
|
||||||
rule.condition.regexFilter
|
|
||||||
)
|
|
||||||
);
|
|
||||||
const allRules = [];
|
|
||||||
const toCheck = [];
|
|
||||||
|
|
||||||
// Fetch regexes for all enabled rulesets
|
|
||||||
const toFetch = [];
|
|
||||||
for ( const details of rulesetDetails.values() ) {
|
|
||||||
if ( details.enabled !== true ) { continue; }
|
|
||||||
if ( details.rules.regexes === 0 ) { continue; }
|
|
||||||
toFetch.push(fetchJSON(`/rulesets/${details.id}.regexes`));
|
|
||||||
}
|
|
||||||
const regexRulesets = await Promise.all(toFetch);
|
|
||||||
|
|
||||||
// Validate fetched regexes
|
|
||||||
let regexRuleId = REGEXES_REALM_START;
|
|
||||||
for ( const rules of regexRulesets ) {
|
|
||||||
if ( Array.isArray(rules) === false ) { continue; }
|
|
||||||
for ( const rule of rules ) {
|
|
||||||
rule.id = regexRuleId++;
|
|
||||||
const {
|
|
||||||
regexFilter: regex,
|
|
||||||
isUrlFilterCaseSensitive: isCaseSensitive
|
|
||||||
} = rule.condition;
|
|
||||||
allRules.push(rule);
|
|
||||||
toCheck.push(
|
|
||||||
validRegexSet.has(regex)
|
|
||||||
? { isSupported: true }
|
|
||||||
: dnr.isRegexSupported({ regex, isCaseSensitive })
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collate results
|
|
||||||
const results = await Promise.all(toCheck);
|
|
||||||
const newRules = [];
|
|
||||||
for ( let i = 0; i < allRules.length; i++ ) {
|
|
||||||
const rule = allRules[i];
|
|
||||||
const result = results[i];
|
|
||||||
if ( result instanceof Object && result.isSupported ) {
|
|
||||||
newRules.push(rule);
|
|
||||||
} else {
|
|
||||||
console.info(`${result.reason}: ${rule.condition.regexFilter}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
console.info(
|
|
||||||
`Rejected regex filters: ${allRules.length-newRules.length} out of ${allRules.length}`
|
|
||||||
);
|
|
||||||
|
|
||||||
// Add validated regex rules to dynamic ruleset without affecting rules
|
|
||||||
// outside regex rule realm.
|
|
||||||
const dynamicRuleMap = await getDynamicRules();
|
|
||||||
const newRuleMap = new Map(newRules.map(rule => [ rule.id, rule ]));
|
|
||||||
const addRules = [];
|
|
||||||
const removeRuleIds = [];
|
|
||||||
for ( const oldRule of dynamicRuleMap.values() ) {
|
|
||||||
if ( oldRule.id < REGEXES_REALM_START ) { continue; }
|
|
||||||
if ( oldRule.id >= REGEXES_REALM_END ) { continue; }
|
|
||||||
const newRule = newRuleMap.get(oldRule.id);
|
|
||||||
if ( newRule === undefined ) {
|
|
||||||
removeRuleIds.push(oldRule.id);
|
|
||||||
dynamicRuleMap.delete(oldRule.id);
|
|
||||||
} else if ( JSON.stringify(oldRule) !== JSON.stringify(newRule) ) {
|
|
||||||
removeRuleIds.push(oldRule.id);
|
|
||||||
addRules.push(newRule);
|
|
||||||
dynamicRuleMap.set(oldRule.id, newRule);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for ( const newRule of newRuleMap.values() ) {
|
|
||||||
if ( dynamicRuleMap.has(newRule.id) ) { continue; }
|
|
||||||
addRules.push(newRule);
|
|
||||||
dynamicRuleMap.set(newRule.id, newRule);
|
|
||||||
}
|
|
||||||
if ( addRules.length !== 0 || removeRuleIds.length !== 0 ) {
|
|
||||||
return dnr.updateDynamicRules({ addRules, removeRuleIds });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
async function matchesTrustedSiteDirective(details) {
|
|
||||||
const url = parsedURLromOrigin(details.origin);
|
|
||||||
if ( url === undefined ) { return false; }
|
|
||||||
|
|
||||||
const dynamicRuleMap = await getDynamicRules();
|
|
||||||
let rule = dynamicRuleMap.get(TRUSTED_DIRECTIVE_BASE_RULE_ID);
|
|
||||||
if ( rule === undefined ) { return false; }
|
|
||||||
const domainSet = new Set(rule.condition.requestDomains);
|
|
||||||
let hostname = url.hostname;
|
|
||||||
for (;;) {
|
|
||||||
if ( domainSet.has(hostname) ) { return true; }
|
|
||||||
const pos = hostname.indexOf('.');
|
|
||||||
if ( pos === -1 ) { break; }
|
|
||||||
hostname = hostname.slice(pos+1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function addTrustedSiteDirective(details) {
|
|
||||||
const url = parsedURLromOrigin(details.origin);
|
|
||||||
if ( url === undefined ) { return false; }
|
|
||||||
|
|
||||||
const dynamicRuleMap = await getDynamicRules();
|
|
||||||
let rule = dynamicRuleMap.get(TRUSTED_DIRECTIVE_BASE_RULE_ID);
|
|
||||||
if ( rule !== undefined ) {
|
|
||||||
rule.condition.initiatorDomains = undefined;
|
|
||||||
if ( Array.isArray(rule.condition.requestDomains) === false ) {
|
|
||||||
rule.condition.requestDomains = [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( rule === undefined ) {
|
|
||||||
rule = {
|
|
||||||
id: TRUSTED_DIRECTIVE_BASE_RULE_ID,
|
|
||||||
action: {
|
|
||||||
type: 'allowAllRequests',
|
|
||||||
},
|
|
||||||
condition: {
|
|
||||||
requestDomains: [ url.hostname ],
|
|
||||||
resourceTypes: [ 'main_frame' ],
|
|
||||||
},
|
|
||||||
priority: TRUSTED_DIRECTIVE_BASE_RULE_ID,
|
|
||||||
};
|
|
||||||
dynamicRuleMap.set(TRUSTED_DIRECTIVE_BASE_RULE_ID, rule);
|
|
||||||
} else if ( rule.condition.requestDomains.includes(url.hostname) === false ) {
|
|
||||||
rule.condition.requestDomains.push(url.hostname);
|
|
||||||
}
|
|
||||||
|
|
||||||
await dnr.updateDynamicRules({
|
|
||||||
addRules: [ rule ],
|
|
||||||
removeRuleIds: [ TRUSTED_DIRECTIVE_BASE_RULE_ID ],
|
|
||||||
});
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function removeTrustedSiteDirective(details) {
|
|
||||||
const url = parsedURLromOrigin(details.origin);
|
|
||||||
if ( url === undefined ) { return false; }
|
|
||||||
|
|
||||||
const dynamicRuleMap = await getDynamicRules();
|
|
||||||
let rule = dynamicRuleMap.get(TRUSTED_DIRECTIVE_BASE_RULE_ID);
|
|
||||||
if ( rule === undefined ) { return false; }
|
|
||||||
rule.condition.initiatorDomains = undefined;
|
|
||||||
if ( Array.isArray(rule.condition.requestDomains) === false ) {
|
|
||||||
rule.condition.requestDomains = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const domainSet = new Set(rule.condition.requestDomains);
|
|
||||||
const beforeCount = domainSet.size;
|
|
||||||
let hostname = url.hostname;
|
|
||||||
for (;;) {
|
|
||||||
domainSet.delete(hostname);
|
|
||||||
const pos = hostname.indexOf('.');
|
|
||||||
if ( pos === -1 ) { break; }
|
|
||||||
hostname = hostname.slice(pos+1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( domainSet.size === beforeCount ) { return false; }
|
|
||||||
|
|
||||||
if ( domainSet.size === 0 ) {
|
|
||||||
dynamicRuleMap.delete(TRUSTED_DIRECTIVE_BASE_RULE_ID);
|
|
||||||
await dnr.updateDynamicRules({
|
|
||||||
removeRuleIds: [ TRUSTED_DIRECTIVE_BASE_RULE_ID ]
|
|
||||||
});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
rule.condition.requestDomains = Array.from(domainSet);
|
|
||||||
|
|
||||||
await dnr.updateDynamicRules({
|
|
||||||
addRules: [ rule ],
|
|
||||||
removeRuleIds: [ TRUSTED_DIRECTIVE_BASE_RULE_ID ],
|
|
||||||
});
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function toggleTrustedSiteDirective(details) {
|
|
||||||
return details.state
|
|
||||||
? removeTrustedSiteDirective(details)
|
|
||||||
: addTrustedSiteDirective(details);
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
async function enableRulesets(ids) {
|
|
||||||
const afterIds = new Set(ids);
|
|
||||||
const beforeIds = new Set(await dnr.getEnabledRulesets());
|
|
||||||
const enableRulesetIds = [];
|
|
||||||
const disableRulesetIds = [];
|
|
||||||
for ( const id of afterIds ) {
|
|
||||||
if ( beforeIds.has(id) ) { continue; }
|
|
||||||
enableRulesetIds.push(id);
|
|
||||||
}
|
|
||||||
for ( const id of beforeIds ) {
|
|
||||||
if ( afterIds.has(id) ) { continue; }
|
|
||||||
disableRulesetIds.push(id);
|
|
||||||
}
|
|
||||||
if ( enableRulesetIds.length !== 0 || disableRulesetIds.length !== 0 ) {
|
|
||||||
return dnr.updateEnabledRulesets({ enableRulesetIds,disableRulesetIds });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getEnabledRulesetsStats() {
|
|
||||||
const [
|
|
||||||
rulesetDetails,
|
|
||||||
ids,
|
|
||||||
] = await Promise.all([
|
|
||||||
getRulesetDetails(),
|
|
||||||
dnr.getEnabledRulesets(),
|
|
||||||
]);
|
|
||||||
const out = [];
|
|
||||||
for ( const id of ids ) {
|
|
||||||
const ruleset = rulesetDetails.get(id);
|
|
||||||
if ( ruleset === undefined ) { continue; }
|
|
||||||
out.push(ruleset);
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function defaultRulesetsFromLanguage() {
|
|
||||||
const out = [ 'default' ];
|
|
||||||
|
|
||||||
const dropCountry = lang => {
|
|
||||||
const pos = lang.indexOf('-');
|
|
||||||
if ( pos === -1 ) { return lang; }
|
|
||||||
return lang.slice(0, pos);
|
|
||||||
};
|
|
||||||
|
|
||||||
const langSet = new Set();
|
|
||||||
|
|
||||||
await i18n.getAcceptLanguages().then(langs => {
|
|
||||||
for ( const lang of langs.map(dropCountry) ) {
|
|
||||||
langSet.add(lang);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
langSet.add(dropCountry(i18n.getUILanguage()));
|
|
||||||
|
|
||||||
const reTargetLang = new RegExp(
|
|
||||||
`\\b(${Array.from(langSet).join('|')})\\b`
|
|
||||||
);
|
|
||||||
|
|
||||||
const rulesetDetails = await getRulesetDetails();
|
|
||||||
for ( const [ id, details ] of rulesetDetails ) {
|
|
||||||
if ( typeof details.lang !== 'string' ) { continue; }
|
|
||||||
if ( reTargetLang.test(details.lang) === false ) { continue; }
|
|
||||||
out.push(id);
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
async function hasGreatPowers(origin) {
|
async function hasGreatPowers(origin) {
|
||||||
return browser.permissions.contains({
|
return browser.permissions.contains({
|
||||||
origins: [ `${origin}/*` ]
|
origins: [ `${origin}/*` ]
|
||||||
|
@ -491,7 +200,9 @@ function onMessage(request, sender, callback) {
|
||||||
|
|
||||||
case 'toggleTrustedSiteDirective': {
|
case 'toggleTrustedSiteDirective': {
|
||||||
toggleTrustedSiteDirective(request).then(response => {
|
toggleTrustedSiteDirective(request).then(response => {
|
||||||
callback(response);
|
registerInjectable().then(( ) => {
|
||||||
|
callback(response);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,262 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
|
||||||
|
uBlock Origin - a browser extension to block requests.
|
||||||
|
Copyright (C) 2022-present Raymond Hill
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
|
||||||
|
Home: https://github.com/gorhill/uBlock
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* jshint esversion:11 */
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
import { dnr, i18n } from './ext.js';
|
||||||
|
import { fetchJSON } from './fetch.js';
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
const RULE_REALM_SIZE = 1000000;
|
||||||
|
const REGEXES_REALM_START = 1000000;
|
||||||
|
const REGEXES_REALM_END = REGEXES_REALM_START + RULE_REALM_SIZE;
|
||||||
|
const TRUSTED_DIRECTIVE_BASE_RULE_ID = 8000000;
|
||||||
|
const CURRENT_CONFIG_BASE_RULE_ID = 9000000;
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
let rulesetDetailsPromise;
|
||||||
|
|
||||||
|
function getRulesetDetails() {
|
||||||
|
if ( rulesetDetailsPromise !== undefined ) {
|
||||||
|
return rulesetDetailsPromise;
|
||||||
|
}
|
||||||
|
rulesetDetailsPromise = fetchJSON('/rulesets/ruleset-details').then(entries => {
|
||||||
|
const map = new Map(
|
||||||
|
entries.map(entry => [ entry.id, entry ])
|
||||||
|
);
|
||||||
|
return map;
|
||||||
|
});
|
||||||
|
return rulesetDetailsPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
let dynamicRuleMapPromise;
|
||||||
|
|
||||||
|
function getDynamicRules() {
|
||||||
|
if ( dynamicRuleMapPromise !== undefined ) {
|
||||||
|
return dynamicRuleMapPromise;
|
||||||
|
}
|
||||||
|
dynamicRuleMapPromise = dnr.getDynamicRules().then(rules => {
|
||||||
|
const map = new Map(
|
||||||
|
rules.map(rule => [ rule.id, rule ])
|
||||||
|
);
|
||||||
|
console.log(`Dynamic rule count: ${map.size}`);
|
||||||
|
console.log(`Available dynamic rule count: ${dnr.MAX_NUMBER_OF_DYNAMIC_AND_SESSION_RULES - map.size}`);
|
||||||
|
return map;
|
||||||
|
});
|
||||||
|
return dynamicRuleMapPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
async function updateRegexRules() {
|
||||||
|
const [
|
||||||
|
rulesetDetails,
|
||||||
|
dynamicRules
|
||||||
|
] = await Promise.all([
|
||||||
|
getRulesetDetails(),
|
||||||
|
dnr.getDynamicRules(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Avoid testing already tested regexes
|
||||||
|
const validRegexSet = new Set(
|
||||||
|
dynamicRules.filter(rule =>
|
||||||
|
rule.condition?.regexFilter && true || false
|
||||||
|
).map(rule =>
|
||||||
|
rule.condition.regexFilter
|
||||||
|
)
|
||||||
|
);
|
||||||
|
const allRules = [];
|
||||||
|
const toCheck = [];
|
||||||
|
|
||||||
|
// Fetch regexes for all enabled rulesets
|
||||||
|
const toFetch = [];
|
||||||
|
for ( const details of rulesetDetails.values() ) {
|
||||||
|
if ( details.enabled !== true ) { continue; }
|
||||||
|
if ( details.rules.regexes === 0 ) { continue; }
|
||||||
|
toFetch.push(fetchJSON(`/rulesets/${details.id}.regexes`));
|
||||||
|
}
|
||||||
|
const regexRulesets = await Promise.all(toFetch);
|
||||||
|
|
||||||
|
// Validate fetched regexes
|
||||||
|
let regexRuleId = REGEXES_REALM_START;
|
||||||
|
for ( const rules of regexRulesets ) {
|
||||||
|
if ( Array.isArray(rules) === false ) { continue; }
|
||||||
|
for ( const rule of rules ) {
|
||||||
|
rule.id = regexRuleId++;
|
||||||
|
const {
|
||||||
|
regexFilter: regex,
|
||||||
|
isUrlFilterCaseSensitive: isCaseSensitive
|
||||||
|
} = rule.condition;
|
||||||
|
allRules.push(rule);
|
||||||
|
toCheck.push(
|
||||||
|
validRegexSet.has(regex)
|
||||||
|
? { isSupported: true }
|
||||||
|
: dnr.isRegexSupported({ regex, isCaseSensitive })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collate results
|
||||||
|
const results = await Promise.all(toCheck);
|
||||||
|
const newRules = [];
|
||||||
|
for ( let i = 0; i < allRules.length; i++ ) {
|
||||||
|
const rule = allRules[i];
|
||||||
|
const result = results[i];
|
||||||
|
if ( result instanceof Object && result.isSupported ) {
|
||||||
|
newRules.push(rule);
|
||||||
|
} else {
|
||||||
|
console.info(`${result.reason}: ${rule.condition.regexFilter}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.info(
|
||||||
|
`Rejected regex filters: ${allRules.length-newRules.length} out of ${allRules.length}`
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add validated regex rules to dynamic ruleset without affecting rules
|
||||||
|
// outside regex rule realm.
|
||||||
|
const dynamicRuleMap = await getDynamicRules();
|
||||||
|
const newRuleMap = new Map(newRules.map(rule => [ rule.id, rule ]));
|
||||||
|
const addRules = [];
|
||||||
|
const removeRuleIds = [];
|
||||||
|
for ( const oldRule of dynamicRuleMap.values() ) {
|
||||||
|
if ( oldRule.id < REGEXES_REALM_START ) { continue; }
|
||||||
|
if ( oldRule.id >= REGEXES_REALM_END ) { continue; }
|
||||||
|
const newRule = newRuleMap.get(oldRule.id);
|
||||||
|
if ( newRule === undefined ) {
|
||||||
|
removeRuleIds.push(oldRule.id);
|
||||||
|
dynamicRuleMap.delete(oldRule.id);
|
||||||
|
} else if ( JSON.stringify(oldRule) !== JSON.stringify(newRule) ) {
|
||||||
|
removeRuleIds.push(oldRule.id);
|
||||||
|
addRules.push(newRule);
|
||||||
|
dynamicRuleMap.set(oldRule.id, newRule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for ( const newRule of newRuleMap.values() ) {
|
||||||
|
if ( dynamicRuleMap.has(newRule.id) ) { continue; }
|
||||||
|
addRules.push(newRule);
|
||||||
|
dynamicRuleMap.set(newRule.id, newRule);
|
||||||
|
}
|
||||||
|
if ( addRules.length !== 0 || removeRuleIds.length !== 0 ) {
|
||||||
|
return dnr.updateDynamicRules({ addRules, removeRuleIds });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
async function defaultRulesetsFromLanguage() {
|
||||||
|
const out = [ 'default' ];
|
||||||
|
|
||||||
|
const dropCountry = lang => {
|
||||||
|
const pos = lang.indexOf('-');
|
||||||
|
if ( pos === -1 ) { return lang; }
|
||||||
|
return lang.slice(0, pos);
|
||||||
|
};
|
||||||
|
|
||||||
|
const langSet = new Set();
|
||||||
|
|
||||||
|
await i18n.getAcceptLanguages().then(langs => {
|
||||||
|
for ( const lang of langs.map(dropCountry) ) {
|
||||||
|
langSet.add(lang);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
langSet.add(dropCountry(i18n.getUILanguage()));
|
||||||
|
|
||||||
|
const reTargetLang = new RegExp(
|
||||||
|
`\\b(${Array.from(langSet).join('|')})\\b`
|
||||||
|
);
|
||||||
|
|
||||||
|
const rulesetDetails = await getRulesetDetails();
|
||||||
|
for ( const [ id, details ] of rulesetDetails ) {
|
||||||
|
if ( typeof details.lang !== 'string' ) { continue; }
|
||||||
|
if ( reTargetLang.test(details.lang) === false ) { continue; }
|
||||||
|
out.push(id);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
async function enableRulesets(ids) {
|
||||||
|
const afterIds = new Set(ids);
|
||||||
|
const beforeIds = new Set(await dnr.getEnabledRulesets());
|
||||||
|
const enableRulesetIds = [];
|
||||||
|
const disableRulesetIds = [];
|
||||||
|
for ( const id of afterIds ) {
|
||||||
|
if ( beforeIds.has(id) ) { continue; }
|
||||||
|
enableRulesetIds.push(id);
|
||||||
|
}
|
||||||
|
for ( const id of beforeIds ) {
|
||||||
|
if ( afterIds.has(id) ) { continue; }
|
||||||
|
disableRulesetIds.push(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( enableRulesetIds.length !== 0 ) {
|
||||||
|
console.info(`Enable rulesets: ${enableRulesetIds}`);
|
||||||
|
}
|
||||||
|
if ( disableRulesetIds.length !== 0 ) {
|
||||||
|
console.info(`Disable ruleset: ${disableRulesetIds}`);
|
||||||
|
}
|
||||||
|
if ( enableRulesetIds.length !== 0 || disableRulesetIds.length !== 0 ) {
|
||||||
|
return dnr.updateEnabledRulesets({ enableRulesetIds, disableRulesetIds });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
async function getEnabledRulesetsStats() {
|
||||||
|
const [
|
||||||
|
rulesetDetails,
|
||||||
|
ids,
|
||||||
|
] = await Promise.all([
|
||||||
|
getRulesetDetails(),
|
||||||
|
dnr.getEnabledRulesets(),
|
||||||
|
]);
|
||||||
|
const out = [];
|
||||||
|
for ( const id of ids ) {
|
||||||
|
const ruleset = rulesetDetails.get(id);
|
||||||
|
if ( ruleset === undefined ) { continue; }
|
||||||
|
out.push(ruleset);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
export {
|
||||||
|
REGEXES_REALM_START,
|
||||||
|
REGEXES_REALM_END,
|
||||||
|
TRUSTED_DIRECTIVE_BASE_RULE_ID,
|
||||||
|
CURRENT_CONFIG_BASE_RULE_ID,
|
||||||
|
getRulesetDetails,
|
||||||
|
getDynamicRules,
|
||||||
|
enableRulesets,
|
||||||
|
defaultRulesetsFromLanguage,
|
||||||
|
getEnabledRulesetsStats,
|
||||||
|
updateRegexRules,
|
||||||
|
};
|
|
@ -27,7 +27,14 @@
|
||||||
|
|
||||||
import { browser, dnr } from './ext.js';
|
import { browser, dnr } from './ext.js';
|
||||||
import { fetchJSON } from './fetch.js';
|
import { fetchJSON } from './fetch.js';
|
||||||
import { parsedURLromOrigin } from './utils.js';
|
import { matchesTrustedSiteDirective } from './trusted-sites.js';
|
||||||
|
|
||||||
|
import {
|
||||||
|
parsedURLromOrigin,
|
||||||
|
toBroaderHostname,
|
||||||
|
fidFromFileName,
|
||||||
|
fnameFromFileId,
|
||||||
|
} from './utils.js';
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
|
@ -72,12 +79,6 @@ const hostnamesFromMatches = origins => {
|
||||||
return out;
|
return out;
|
||||||
};
|
};
|
||||||
|
|
||||||
const toBroaderHostname = hn => {
|
|
||||||
if ( hn === '*' ) { return ''; }
|
|
||||||
const pos = hn.indexOf('.');
|
|
||||||
return pos !== -1 ? hn.slice(pos+1) : '*';
|
|
||||||
};
|
|
||||||
|
|
||||||
const arrayEq = (a, b) => {
|
const arrayEq = (a, b) => {
|
||||||
if ( a === undefined ) { return b === undefined; }
|
if ( a === undefined ) { return b === undefined; }
|
||||||
if ( b === undefined ) { return false; }
|
if ( b === undefined ) { return false; }
|
||||||
|
@ -103,9 +104,9 @@ const toRegisterable = (fname, entry) => {
|
||||||
if ( entry.excludeMatches ) {
|
if ( entry.excludeMatches ) {
|
||||||
directive.excludeMatches = matchesFromHostnames(entry.excludeMatches);
|
directive.excludeMatches = matchesFromHostnames(entry.excludeMatches);
|
||||||
}
|
}
|
||||||
directive.js = [ `/rulesets/js/${fname.slice(0,1)}/${fname.slice(1)}.js` ];
|
directive.js = [ `/rulesets/js/${fname.slice(0,2)}/${fname.slice(2)}.js` ];
|
||||||
directive.runAt = 'document_start';
|
directive.runAt = 'document_start';
|
||||||
if ( (parseInt(fname,16) & MAIN_WORLD_BIT) !== 0 ) {
|
if ( (fidFromFileName(fname) & MAIN_WORLD_BIT) !== 0 ) {
|
||||||
directive.world = 'MAIN';
|
directive.world = 'MAIN';
|
||||||
}
|
}
|
||||||
return directive;
|
return directive;
|
||||||
|
@ -150,8 +151,12 @@ async function getInjectableCount(origin) {
|
||||||
const details = scriptingDetails.get(rulesetId);
|
const details = scriptingDetails.get(rulesetId);
|
||||||
let hn = url.hostname;
|
let hn = url.hostname;
|
||||||
while ( hn !== '' ) {
|
while ( hn !== '' ) {
|
||||||
const fnames = details.matches.get(hn);
|
const fids = details.matches?.get(hn);
|
||||||
total += fnames && fnames.length || 0;
|
if ( typeof fids === 'number' ) {
|
||||||
|
total += 1;
|
||||||
|
} else if ( Array.isArray(fids) ) {
|
||||||
|
total += fids.length;
|
||||||
|
}
|
||||||
hn = toBroaderHostname(hn);
|
hn = toBroaderHostname(hn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -161,8 +166,6 @@ async function getInjectableCount(origin) {
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
// TODO: Mind trusted-site directives.
|
|
||||||
|
|
||||||
async function registerInjectable() {
|
async function registerInjectable() {
|
||||||
|
|
||||||
const [
|
const [
|
||||||
|
@ -176,27 +179,37 @@ async function registerInjectable() {
|
||||||
browser.scripting.getRegisteredContentScripts(),
|
browser.scripting.getRegisteredContentScripts(),
|
||||||
getScriptingDetails(),
|
getScriptingDetails(),
|
||||||
]).then(results => {
|
]).then(results => {
|
||||||
results[0] = new Set(hostnamesFromMatches(results[0].origins));
|
results[0] = new Map(
|
||||||
|
hostnamesFromMatches(results[0].origins).map(hn => [ hn, false ])
|
||||||
|
);
|
||||||
return results;
|
return results;
|
||||||
});
|
});
|
||||||
|
|
||||||
if ( hostnames.has('*') && hostnames.size > 1 ) {
|
if ( hostnames.has('*') && hostnames.size > 1 ) {
|
||||||
hostnames.clear();
|
hostnames.clear();
|
||||||
hostnames.add('*');
|
hostnames.set('*', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
Array.from(hostnames.keys()).map(
|
||||||
|
hn => matchesTrustedSiteDirective({ hostname: hn })
|
||||||
|
.then(trusted => hostnames.set(hn, trusted))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
const toRegister = new Map();
|
const toRegister = new Map();
|
||||||
|
|
||||||
const checkRealm = (details, prop, hn) => {
|
const checkMatches = (details, hn) => {
|
||||||
const fids = details[prop]?.get(hn);
|
let fids = details.matches?.get(hn);
|
||||||
if ( fids === undefined ) { return; }
|
if ( fids === undefined ) { return; }
|
||||||
|
if ( typeof fids === 'number' ) { fids = [ fids ]; }
|
||||||
for ( const fid of fids ) {
|
for ( const fid of fids ) {
|
||||||
const fname = fid.toString(16).padStart(8,'0');
|
const fname = fnameFromFileId(fid);
|
||||||
const existing = toRegister.get(fname);
|
const existing = toRegister.get(fname);
|
||||||
if ( existing ) {
|
if ( existing ) {
|
||||||
existing[prop].push(hn);
|
existing.matches.push(hn);
|
||||||
} else {
|
} else {
|
||||||
toRegister.set(fname, { [prop]: [ hn ] });
|
toRegister.set(fname, { matches: [ hn ] });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -204,10 +217,37 @@ async function registerInjectable() {
|
||||||
for ( const rulesetId of rulesetIds ) {
|
for ( const rulesetId of rulesetIds ) {
|
||||||
const details = scriptingDetails.get(rulesetId);
|
const details = scriptingDetails.get(rulesetId);
|
||||||
if ( details === undefined ) { continue; }
|
if ( details === undefined ) { continue; }
|
||||||
for ( let hn of hostnames ) {
|
for ( let [ hn, trusted ] of hostnames ) {
|
||||||
|
if ( trusted ) { continue; }
|
||||||
while ( hn !== '' ) {
|
while ( hn !== '' ) {
|
||||||
checkRealm(details, 'matches', hn);
|
checkMatches(details, hn);
|
||||||
checkRealm(details, 'excludeMatches', hn);
|
hn = toBroaderHostname(hn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkExcludeMatches = (details, hn) => {
|
||||||
|
let fids = details.excludeMatches?.get(hn);
|
||||||
|
if ( fids === undefined ) { return; }
|
||||||
|
if ( typeof fids === 'number' ) { fids = [ fids ]; }
|
||||||
|
for ( const fid of fids ) {
|
||||||
|
const fname = fnameFromFileId(fid);
|
||||||
|
const existing = toRegister.get(fname);
|
||||||
|
if ( existing === undefined ) { continue; }
|
||||||
|
if ( existing.excludeMatches ) {
|
||||||
|
existing.excludeMatches.push(hn);
|
||||||
|
} else {
|
||||||
|
toRegister.set(fname, { excludeMatches: [ hn ] });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for ( const rulesetId of rulesetIds ) {
|
||||||
|
const details = scriptingDetails.get(rulesetId);
|
||||||
|
if ( details === undefined ) { continue; }
|
||||||
|
for ( let hn of hostnames.keys() ) {
|
||||||
|
while ( hn !== '' ) {
|
||||||
|
checkExcludeMatches(details, hn);
|
||||||
hn = toBroaderHostname(hn);
|
hn = toBroaderHostname(hn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,160 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
|
||||||
|
uBlock Origin - a browser extension to block requests.
|
||||||
|
Copyright (C) 2022-present Raymond Hill
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
|
||||||
|
Home: https://github.com/gorhill/uBlock
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* jshint esversion:11 */
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
import { dnr } from './ext.js';
|
||||||
|
|
||||||
|
import {
|
||||||
|
parsedURLromOrigin,
|
||||||
|
toBroaderHostname,
|
||||||
|
} from './utils.js';
|
||||||
|
|
||||||
|
import {
|
||||||
|
TRUSTED_DIRECTIVE_BASE_RULE_ID,
|
||||||
|
getDynamicRules
|
||||||
|
} from './ruleset-manager.js';
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
async function matchesTrustedSiteDirective(details) {
|
||||||
|
const hostname =
|
||||||
|
details.hostname ||
|
||||||
|
parsedURLromOrigin(details.origin)?.hostname ||
|
||||||
|
undefined;
|
||||||
|
if ( hostname === undefined ) { return false; }
|
||||||
|
|
||||||
|
const dynamicRuleMap = await getDynamicRules();
|
||||||
|
let rule = dynamicRuleMap.get(TRUSTED_DIRECTIVE_BASE_RULE_ID);
|
||||||
|
if ( rule === undefined ) { return false; }
|
||||||
|
|
||||||
|
const domainSet = new Set(rule.condition.requestDomains);
|
||||||
|
let hn = hostname;
|
||||||
|
while ( hn ) {
|
||||||
|
if ( domainSet.has(hn) ) { return true; }
|
||||||
|
hn = toBroaderHostname(hn);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
async function addTrustedSiteDirective(details) {
|
||||||
|
const url = parsedURLromOrigin(details.origin);
|
||||||
|
if ( url === undefined ) { return false; }
|
||||||
|
|
||||||
|
const dynamicRuleMap = await getDynamicRules();
|
||||||
|
let rule = dynamicRuleMap.get(TRUSTED_DIRECTIVE_BASE_RULE_ID);
|
||||||
|
if ( rule !== undefined ) {
|
||||||
|
rule.condition.initiatorDomains = undefined;
|
||||||
|
if ( Array.isArray(rule.condition.requestDomains) === false ) {
|
||||||
|
rule.condition.requestDomains = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( rule === undefined ) {
|
||||||
|
rule = {
|
||||||
|
id: TRUSTED_DIRECTIVE_BASE_RULE_ID,
|
||||||
|
action: {
|
||||||
|
type: 'allowAllRequests',
|
||||||
|
},
|
||||||
|
condition: {
|
||||||
|
requestDomains: [ url.hostname ],
|
||||||
|
resourceTypes: [ 'main_frame' ],
|
||||||
|
},
|
||||||
|
priority: TRUSTED_DIRECTIVE_BASE_RULE_ID,
|
||||||
|
};
|
||||||
|
dynamicRuleMap.set(TRUSTED_DIRECTIVE_BASE_RULE_ID, rule);
|
||||||
|
} else if ( rule.condition.requestDomains.includes(url.hostname) === false ) {
|
||||||
|
rule.condition.requestDomains.push(url.hostname);
|
||||||
|
}
|
||||||
|
|
||||||
|
await dnr.updateDynamicRules({
|
||||||
|
addRules: [ rule ],
|
||||||
|
removeRuleIds: [ TRUSTED_DIRECTIVE_BASE_RULE_ID ],
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
async function removeTrustedSiteDirective(details) {
|
||||||
|
const url = parsedURLromOrigin(details.origin);
|
||||||
|
if ( url === undefined ) { return false; }
|
||||||
|
|
||||||
|
const dynamicRuleMap = await getDynamicRules();
|
||||||
|
let rule = dynamicRuleMap.get(TRUSTED_DIRECTIVE_BASE_RULE_ID);
|
||||||
|
if ( rule === undefined ) { return false; }
|
||||||
|
rule.condition.initiatorDomains = undefined;
|
||||||
|
if ( Array.isArray(rule.condition.requestDomains) === false ) {
|
||||||
|
rule.condition.requestDomains = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const domainSet = new Set(rule.condition.requestDomains);
|
||||||
|
const beforeCount = domainSet.size;
|
||||||
|
let hostname = url.hostname;
|
||||||
|
for (;;) {
|
||||||
|
domainSet.delete(hostname);
|
||||||
|
const pos = hostname.indexOf('.');
|
||||||
|
if ( pos === -1 ) { break; }
|
||||||
|
hostname = hostname.slice(pos+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( domainSet.size === beforeCount ) { return false; }
|
||||||
|
|
||||||
|
if ( domainSet.size === 0 ) {
|
||||||
|
dynamicRuleMap.delete(TRUSTED_DIRECTIVE_BASE_RULE_ID);
|
||||||
|
await dnr.updateDynamicRules({
|
||||||
|
removeRuleIds: [ TRUSTED_DIRECTIVE_BASE_RULE_ID ]
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
rule.condition.requestDomains = Array.from(domainSet);
|
||||||
|
|
||||||
|
await dnr.updateDynamicRules({
|
||||||
|
addRules: [ rule ],
|
||||||
|
removeRuleIds: [ TRUSTED_DIRECTIVE_BASE_RULE_ID ],
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
async function toggleTrustedSiteDirective(details) {
|
||||||
|
return details.state
|
||||||
|
? removeTrustedSiteDirective(details)
|
||||||
|
: addTrustedSiteDirective(details);
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
export {
|
||||||
|
matchesTrustedSiteDirective,
|
||||||
|
toggleTrustedSiteDirective,
|
||||||
|
};
|
|
@ -34,4 +34,25 @@ function parsedURLromOrigin(origin) {
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
export { parsedURLromOrigin };
|
const toBroaderHostname = hn => {
|
||||||
|
if ( hn === '*' ) { return ''; }
|
||||||
|
const pos = hn.indexOf('.');
|
||||||
|
return pos !== -1 ? hn.slice(pos+1) : '*';
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
const fnameFromFileId = fid =>
|
||||||
|
fid.toString(32).padStart(7, '0');
|
||||||
|
|
||||||
|
const fidFromFileName = fname =>
|
||||||
|
parseInt(fname, 32);
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
export {
|
||||||
|
parsedURLromOrigin,
|
||||||
|
toBroaderHostname,
|
||||||
|
fnameFromFileId,
|
||||||
|
fidFromFileName,
|
||||||
|
};
|
||||||
|
|
|
@ -28,9 +28,9 @@ import https from 'https';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import process from 'process';
|
import process from 'process';
|
||||||
import { createHash } from 'crypto';
|
import { createHash } from 'crypto';
|
||||||
|
|
||||||
import { dnrRulesetFromRawLists } from './js/static-dnr-filtering.js';
|
import { dnrRulesetFromRawLists } from './js/static-dnr-filtering.js';
|
||||||
import { StaticFilteringParser } from './js/static-filtering-parser.js';
|
import { StaticFilteringParser } from './js/static-filtering-parser.js';
|
||||||
|
import { fnameFromFileId } from './js/utils.js';
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
|
@ -349,25 +349,27 @@ function addScriptingAPIResources(id, entry, prop, fid) {
|
||||||
for ( const hn of entry[prop] ) {
|
for ( const hn of entry[prop] ) {
|
||||||
let details = scriptingDetails.get(id);
|
let details = scriptingDetails.get(id);
|
||||||
if ( details === undefined ) {
|
if ( details === undefined ) {
|
||||||
details = {
|
details = {};
|
||||||
matches: new Map(),
|
|
||||||
excludeMatches: new Map(),
|
|
||||||
};
|
|
||||||
scriptingDetails.set(id, details);
|
scriptingDetails.set(id, details);
|
||||||
}
|
}
|
||||||
|
if ( details[prop] === undefined ) {
|
||||||
|
details[prop] = new Map();
|
||||||
|
}
|
||||||
let fids = details[prop].get(hn);
|
let fids = details[prop].get(hn);
|
||||||
if ( fids === undefined ) {
|
if ( fids === undefined ) {
|
||||||
fids = new Set();
|
details[prop].set(hn, fid);
|
||||||
|
} else if ( fids instanceof Set ) {
|
||||||
|
fids.add(fid);
|
||||||
|
} else if ( fid !== fids ) {
|
||||||
|
fids = new Set([ fids, fid ]);
|
||||||
details[prop].set(hn, fids);
|
details[prop].set(hn, fids);
|
||||||
}
|
}
|
||||||
fids.add(fid);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const toCSSFileId = s => uidint32(s) & ~0b1;
|
const toCSSFileId = s => uidint32(s) & ~0b1;
|
||||||
const toJSFileId = s => uidint32(s) | 0b1;
|
const toJSFileId = s => uidint32(s) | 0b1;
|
||||||
const fileNameFromId = id => id.toString(16).padStart(8,'0');
|
const pathFromFileName = fname => `${scriptletDir}/${fname.slice(0,2)}/${fname.slice(2)}.js`;
|
||||||
|
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
|
@ -487,8 +489,8 @@ async function processCosmeticFilters(assetDetails, mapin) {
|
||||||
const fid = toCSSFileId(patchedScriptlet);
|
const fid = toCSSFileId(patchedScriptlet);
|
||||||
if ( globalPatchedScriptletsSet.has(fid) === false ) {
|
if ( globalPatchedScriptletsSet.has(fid) === false ) {
|
||||||
globalPatchedScriptletsSet.add(fid);
|
globalPatchedScriptletsSet.add(fid);
|
||||||
const fname = fileNameFromId(fid);
|
const fname = fnameFromFileId(fid);
|
||||||
writeFile(`${scriptletDir}/${fname.slice(0,1)}/${fname.slice(1)}.js`, patchedScriptlet, {});
|
writeFile(pathFromFileName(fname), patchedScriptlet, {});
|
||||||
generatedFiles.push(fname);
|
generatedFiles.push(fname);
|
||||||
}
|
}
|
||||||
for ( const entry of slice ) {
|
for ( const entry of slice ) {
|
||||||
|
@ -649,8 +651,8 @@ async function processScriptletFilters(assetDetails, mapin) {
|
||||||
const fid = toJSFileId(patchedScriptlet);
|
const fid = toJSFileId(patchedScriptlet);
|
||||||
if ( globalPatchedScriptletsSet.has(fid) === false ) {
|
if ( globalPatchedScriptletsSet.has(fid) === false ) {
|
||||||
globalPatchedScriptletsSet.add(fid);
|
globalPatchedScriptletsSet.add(fid);
|
||||||
const fname = fileNameFromId(fid);
|
const fname = fnameFromFileId(fid);
|
||||||
writeFile(`${scriptletDir}/${fname.slice(0,1)}/${fname.slice(1)}.js`, patchedScriptlet, {});
|
writeFile(pathFromFileName(fname), patchedScriptlet, {});
|
||||||
generatedFiles.push(fname);
|
generatedFiles.push(fname);
|
||||||
}
|
}
|
||||||
for ( const details of argsDetails.values() ) {
|
for ( const details of argsDetails.values() ) {
|
||||||
|
|
|
@ -49,6 +49,7 @@ if [ "$1" != "quick" ]; then
|
||||||
./tools/make-nodejs.sh $TMPDIR
|
./tools/make-nodejs.sh $TMPDIR
|
||||||
cp platform/mv3/package.json $TMPDIR/
|
cp platform/mv3/package.json $TMPDIR/
|
||||||
cp platform/mv3/*.js $TMPDIR/
|
cp platform/mv3/*.js $TMPDIR/
|
||||||
|
cp platform/mv3/extension/js/utils.js $TMPDIR/js/
|
||||||
cp assets/assets.json $TMPDIR/
|
cp assets/assets.json $TMPDIR/
|
||||||
cp -R platform/mv3/scriptlets $TMPDIR/
|
cp -R platform/mv3/scriptlets $TMPDIR/
|
||||||
cd $TMPDIR
|
cd $TMPDIR
|
||||||
|
|
Loading…
Reference in New Issue