This commit is contained in:
Raymond Hill 2024-12-01 19:43:34 -05:00
parent 0eb7d70b7d
commit 61d6cd803c
No known key found for this signature in database
GPG key ID: 25E1490B761470C2
6 changed files with 315 additions and 421 deletions

View file

@ -37,6 +37,7 @@ import {
dnr,
localRead, localRemove, localWrite,
runtime,
sessionRemove,
windows,
} from './ext.js';
@ -47,6 +48,7 @@ import {
import {
enableRulesets,
excludeFromStrictBlock,
getEnabledRulesetsDetails,
getRulesetDetails,
updateDynamicRules,
@ -335,6 +337,13 @@ function onMessage(request, sender, callback) {
});
return true;
case 'excludeFromStrictBlock': {
excludeFromStrictBlock(request.hostname, request.permanent).then(( ) => {
callback();
});
return true;
}
case 'getMatchedRules':
getMatchedRules(request.tabId).then(entries => {
callback(entries);
@ -361,18 +370,22 @@ async function start() {
await loadRulesetConfig();
if ( process.wakeupRun === false ) {
await enableRulesets(rulesetConfig.enabledRulesets);
sessionRemove('excludedStrictBlockHostnames');
}
const rulesetsUpdated = process.wakeupRun === false &&
await enableRulesets(rulesetConfig.enabledRulesets);
// We need to update the regex rules only when ruleset version changes.
if ( process.wakeupRun === false ) {
const currentVersion = getCurrentVersion();
if ( currentVersion !== rulesetConfig.version ) {
ubolLog(`Version change: ${rulesetConfig.version} => ${currentVersion}`);
updateDynamicRules().then(( ) => {
rulesetConfig.version = currentVersion;
saveRulesetConfig();
});
rulesetConfig.version = currentVersion;
saveRulesetConfig();
if ( rulesetsUpdated === false ) {
updateDynamicRules();
}
}
}

View file

@ -98,6 +98,12 @@ export async function sessionWrite(key, value) {
return browser.storage.session.set({ [key]: value });
}
export async function sessionRemove(key) {
if ( browser.storage instanceof Object === false ) { return; }
if ( browser.storage.session instanceof Object === false ) { return; }
return browser.storage.session.remove(key);
}
/******************************************************************************/
export async function adminRead(key) {

View file

@ -19,11 +19,6 @@
Home: https://github.com/gorhill/uBlock
*/
import {
TRUSTED_DIRECTIVE_BASE_RULE_ID,
getDynamicRules,
} from './ruleset-manager.js';
import {
broadcastMessage,
hostnamesFromMatches,
@ -33,12 +28,12 @@ import {
import {
browser,
dnr,
localRead, localWrite,
sessionRead, sessionWrite,
} from './ext.js';
import { adminReadEx } from './admin.js';
import { filteringModesToDNR } from './ruleset-manager.js';
/******************************************************************************/
@ -74,19 +69,6 @@ const pruneHostnameFromSet = (hostname, hnSet) => {
/******************************************************************************/
const eqSets = (setBefore, setAfter) => {
if ( setBefore.size !== setAfter.size ) { return false; }
for ( const hn of setAfter ) {
if ( setBefore.has(hn) === false ) { return false; }
}
for ( const hn of setBefore ) {
if ( setAfter.has(hn) === false ) { return false; }
}
return true;
};
/******************************************************************************/
const serializeModeDetails = details => {
return {
none: Array.from(details.none),
@ -284,93 +266,6 @@ async function writeFilteringModeDetails(afterDetails) {
/******************************************************************************/
async function filteringModesToDNR(modes) {
const dynamicRuleMap = await getDynamicRules();
const trustedRule = dynamicRuleMap.get(TRUSTED_DIRECTIVE_BASE_RULE_ID+0);
const beforeRequestDomainSet = new Set(trustedRule?.condition.requestDomains);
const beforeExcludedRrequestDomainSet = new Set(trustedRule?.condition.excludedRequestDomains);
if ( trustedRule !== undefined && beforeRequestDomainSet.size === 0 ) {
beforeRequestDomainSet.add('all-urls');
} else {
beforeExcludedRrequestDomainSet.add('all-urls');
}
const noneHostnames = new Set([ ...modes.none ]);
const notNoneHostnames = new Set([ ...modes.basic, ...modes.optimal, ...modes.complete ]);
let afterRequestDomainSet = new Set();
let afterExcludedRequestDomainSet = new Set();
if ( noneHostnames.has('all-urls') ) {
afterRequestDomainSet = new Set([ 'all-urls' ]);
afterExcludedRequestDomainSet = notNoneHostnames;
} else {
afterRequestDomainSet = noneHostnames;
afterExcludedRequestDomainSet = new Set([ 'all-urls' ]);
}
if ( eqSets(beforeRequestDomainSet, afterRequestDomainSet) ) {
if ( eqSets(beforeExcludedRrequestDomainSet, afterExcludedRequestDomainSet) ) {
return;
}
}
const removeRuleIds = [
TRUSTED_DIRECTIVE_BASE_RULE_ID+0,
TRUSTED_DIRECTIVE_BASE_RULE_ID+1,
];
dynamicRuleMap.delete(TRUSTED_DIRECTIVE_BASE_RULE_ID+0);
dynamicRuleMap.delete(TRUSTED_DIRECTIVE_BASE_RULE_ID+1);
const allowEverywhere = afterRequestDomainSet.delete('all-urls');
afterExcludedRequestDomainSet.delete('all-urls');
const addRules = [];
if (
allowEverywhere ||
afterRequestDomainSet.size !== 0 ||
afterExcludedRequestDomainSet.size !== 0
) {
const rule0 = {
id: TRUSTED_DIRECTIVE_BASE_RULE_ID+0,
action: { type: 'allowAllRequests' },
condition: {
resourceTypes: [ 'main_frame' ],
},
priority: 100,
};
if ( afterRequestDomainSet.size !== 0 ) {
rule0.condition.requestDomains = Array.from(afterRequestDomainSet);
} else if ( afterExcludedRequestDomainSet.size !== 0 ) {
rule0.condition.excludedRequestDomains = Array.from(afterExcludedRequestDomainSet);
}
addRules.push(rule0);
dynamicRuleMap.set(TRUSTED_DIRECTIVE_BASE_RULE_ID+0, rule0);
// https://github.com/uBlockOrigin/uBOL-home/issues/114
const rule1 = {
id: TRUSTED_DIRECTIVE_BASE_RULE_ID+1,
action: { type: 'allow' },
condition: {
resourceTypes: [ 'script' ],
},
priority: 100,
};
if ( rule0.condition.requestDomains ) {
rule1.condition.initiatorDomains = rule0.condition.requestDomains.slice();
} else if ( rule0.condition.excludedRequestDomains ) {
rule1.condition.excludedInitiatorDomains = rule0.condition.excludedRequestDomains.slice();
}
addRules.push(rule1);
dynamicRuleMap.set(TRUSTED_DIRECTIVE_BASE_RULE_ID+1, rule1);
}
const updateOptions = { removeRuleIds };
if ( addRules.length ) {
updateOptions.addRules = addRules;
}
await dnr.updateDynamicRules(updateOptions);
}
/******************************************************************************/
export async function getFilteringModeDetails() {
const actualDetails = await readFilteringModeDetails();
return {
@ -401,7 +296,7 @@ export function getDefaultFilteringMode() {
return getFilteringMode('all-urls');
}
export function setDefaultFilteringMode(afterLevel) {
export async function setDefaultFilteringMode(afterLevel) {
return setFilteringMode('all-urls', afterLevel);
}

View file

@ -34,7 +34,7 @@ import punycode from './punycode.js';
const popupPanelData = {};
const currentTab = {};
let tabHostname = '';
const tabURL = new URL(runtime.getURL('/'));
/******************************************************************************/
@ -68,8 +68,8 @@ function setFilteringMode(level, commit = false) {
}
async function commitFilteringMode() {
if ( tabHostname === '' ) { return; }
const targetHostname = normalizedHostname(tabHostname);
if ( tabURL.hostname === '' ) { return; }
const targetHostname = normalizedHostname(tabURL.hostname);
const modeSlider = qs$('.filteringModeSlider');
const afterLevel = parseInt(modeSlider.dataset.level, 10);
const beforeLevel = parseInt(modeSlider.dataset.levelBefore, 10);
@ -100,7 +100,9 @@ async function commitFilteringMode() {
}
if ( actualLevel !== beforeLevel && popupPanelData.autoReload ) {
self.setTimeout(( ) => {
browser.tabs.reload(currentTab.id);
browser.tabs.update(currentTab.id, {
url: tabURL.href,
});
}, 437);
}
}
@ -317,8 +319,12 @@ async function init() {
let url;
try {
const strictBlockURL = runtime.getURL('/strict-block.html');
url = new URL(currentTab.url);
tabHostname = url.hostname || '';
if ( url.href.startsWith(strictBlockURL) ) {
url = new URL(url.hash.slice(1));
}
tabURL.href = url.href || '';
} catch(ex) {
}
@ -326,7 +332,7 @@ async function init() {
const response = await sendMessage({
what: 'popupPanelData',
origin: url.origin,
hostname: normalizedHostname(tabHostname),
hostname: normalizedHostname(tabURL.hostname),
});
if ( response instanceof Object ) {
Object.assign(popupPanelData, response);
@ -337,7 +343,7 @@ async function init() {
setFilteringMode(popupPanelData.level);
dom.text('#hostname', punycode.toUnicode(tabHostname));
dom.text('#hostname', punycode.toUnicode(tabURL.hostname));
dom.cl.toggle('#showMatchedRules', 'enabled',
popupPanelData.isSideloaded === true &&

View file

@ -26,24 +26,34 @@ import {
runtime,
} from './ext.js';
import {
localRead, localRemove, localWrite,
sessionRead, sessionRemove, sessionWrite,
} from './ext.js';
import { fetchJSON } from './fetch.js';
import { getAdminRulesets } from './admin.js';
import { ubolLog } from './debug.js';
/******************************************************************************/
const RULE_REALM_SIZE = 1000000;
const REGEXES_REALM_START = 1000000;
const REGEXES_REALM_END = REGEXES_REALM_START + RULE_REALM_SIZE;
const REMOVEPARAMS_REALM_START = REGEXES_REALM_END;
const REMOVEPARAMS_REALM_END = REMOVEPARAMS_REALM_START + RULE_REALM_SIZE;
const REDIRECT_REALM_START = REMOVEPARAMS_REALM_END;
const REDIRECT_REALM_END = REDIRECT_REALM_START + RULE_REALM_SIZE;
const MODIFYHEADERS_REALM_START = REDIRECT_REALM_END;
const MODIFYHEADERS_REALM_END = MODIFYHEADERS_REALM_START + RULE_REALM_SIZE;
const STRICTBLOCK_REALM_START = MODIFYHEADERS_REALM_END;
const TRUSTED_DIRECTIVE_BASE_RULE_ID = 8000000;
let dynamicRulesetId = 1;
/******************************************************************************/
const eqSets = (setBefore, setAfter) => {
if ( setBefore.size !== setAfter.size ) { return false; }
for ( const hn of setAfter ) {
if ( setBefore.has(hn) === false ) { return false; }
}
for ( const hn of setBefore ) {
if ( setAfter.has(hn) === false ) { return false; }
}
return true;
};
/******************************************************************************/
function getRulesetDetails() {
@ -61,29 +71,17 @@ function getRulesetDetails() {
/******************************************************************************/
function getDynamicRules() {
function getDynamicRules(...args) {
if ( getDynamicRules.promise !== undefined ) {
return getDynamicRules.promise;
}
getDynamicRules.promise = dnr.getDynamicRules().then(rules => {
const rulesMap = new Map(rules.map(rule => [ rule.id, rule ]));
ubolLog(`Dynamic rule count: ${rulesMap.size}`);
ubolLog(`Available dynamic rule count: ${dnr.MAX_NUMBER_OF_DYNAMIC_AND_SESSION_RULES - rulesMap.size}`);
return rulesMap;
getDynamicRules.promise = dnr.getDynamicRules(...args).then(rules => {
getDynamicRules.promise = undefined;
return rules;
});
return getDynamicRules.promise;
}
function getDynamicRuleIds() {
if ( getDynamicRuleIds.promise !== undefined ) {
return getDynamicRuleIds.promise;
}
getDynamicRuleIds.promise = dnr.getDynamicRules().then(rules => {
return rules.map(rule => rule.id);
});
return getDynamicRuleIds.promise;
}
/******************************************************************************/
async function pruneInvalidRegexRules(realm, rulesIn) {
@ -129,7 +127,7 @@ pruneInvalidRegexRules.validated = new Map();
/******************************************************************************/
async function updateRegexRules() {
async function updateRegexRules(toAdd) {
const rulesetDetails = await getEnabledRulesetsDetails();
// Fetch regexes for all enabled rulesets
@ -142,69 +140,31 @@ async function updateRegexRules() {
// Collate all regexes rules
const allRules = [];
let regexRuleId = REGEXES_REALM_START;
for ( const rules of regexRulesets ) {
if ( Array.isArray(rules) === false ) { continue; }
for ( const rule of rules ) {
rule.id = regexRuleId++;
rule.id = dynamicRulesetId++;
allRules.push(rule);
}
}
if ( allRules.length === 0 ) { return; }
const validatedRules = await pruneInvalidRegexRules('regexes', allRules);
const validRules = await pruneInvalidRegexRules('regexes', allRules);
if ( validRules.length === 0 ) { return; }
// Add validated regex rules to dynamic ruleset without affecting rules
// outside regex rules realm.
const dynamicRuleMap = await getDynamicRules();
const newRuleMap = new Map(validatedRules.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; }
if ( removeRuleIds.length !== 0 ) {
ubolLog(`Remove ${removeRuleIds.length} DNR regex rules`);
}
if ( addRules.length !== 0 ) {
ubolLog(`Add ${addRules.length} DNR regex rules`);
}
return dnr.updateDynamicRules({ addRules, removeRuleIds }).catch(reason => {
console.error(`updateRegexRules() / ${reason}`);
});
ubolLog(`Add ${validRules.length} DNR regex rules`);
toAdd.push(...validRules);
}
/******************************************************************************/
async function updateRemoveparamRules() {
async function updateRemoveparamRules(toAdd) {
const [
hasOmnipotence,
rulesetDetails,
dynamicRuleMap,
] = await Promise.all([
browser.permissions.contains({ origins: [ '<all_urls>' ] }),
getEnabledRulesetsDetails(),
getDynamicRules(),
]);
// Fetch removeparam rules for all enabled rulesets
@ -218,69 +178,32 @@ async function updateRemoveparamRules() {
// Removeparam rules can only be enforced with omnipotence
const allRules = [];
if ( hasOmnipotence ) {
let removeparamRuleId = REMOVEPARAMS_REALM_START;
for ( const rules of removeparamRulesets ) {
if ( Array.isArray(rules) === false ) { continue; }
for ( const rule of rules ) {
rule.id = removeparamRuleId++;
rule.id = dynamicRulesetId++;
allRules.push(rule);
}
}
}
if ( allRules.length === 0 ) { return; }
const validatedRules = await pruneInvalidRegexRules('removeparam', allRules);
const validRules = await pruneInvalidRegexRules('removeparam', allRules);
if ( validRules.length === 0 ) { return; }
// Add removeparam rules to dynamic ruleset without affecting rules
// outside removeparam rules realm.
const newRuleMap = new Map(validatedRules.map(rule => [ rule.id, rule ]));
const addRules = [];
const removeRuleIds = [];
for ( const oldRule of dynamicRuleMap.values() ) {
if ( oldRule.id < REMOVEPARAMS_REALM_START ) { continue; }
if ( oldRule.id >= REMOVEPARAMS_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; }
if ( removeRuleIds.length !== 0 ) {
ubolLog(`Remove ${removeRuleIds.length} DNR removeparam rules`);
}
if ( addRules.length !== 0 ) {
ubolLog(`Add ${addRules.length} DNR removeparam rules`);
}
return dnr.updateDynamicRules({ addRules, removeRuleIds }).catch(reason => {
console.error(`updateRemoveparamRules() / ${reason}`);
});
ubolLog(`Add ${validRules.length} DNR removeparam rules`);
toAdd.push(...validRules);
}
/******************************************************************************/
async function updateRedirectRules() {
async function updateRedirectRules(toAdd) {
const [
hasOmnipotence,
rulesetDetails,
dynamicRuleMap,
] = await Promise.all([
browser.permissions.contains({ origins: [ '<all_urls>' ] }),
getEnabledRulesetsDetails(),
getDynamicRules(),
]);
// Fetch redirect rules for all enabled rulesets
@ -294,69 +217,32 @@ async function updateRedirectRules() {
// Redirect rules can only be enforced with omnipotence
const allRules = [];
if ( hasOmnipotence ) {
let redirectRuleId = REDIRECT_REALM_START;
for ( const rules of redirectRulesets ) {
if ( Array.isArray(rules) === false ) { continue; }
for ( const rule of rules ) {
rule.id = redirectRuleId++;
rule.id = dynamicRulesetId++;
allRules.push(rule);
}
}
}
if ( allRules.length === 0 ) { return; }
const validatedRules = await pruneInvalidRegexRules('redirect', allRules);
const validRules = await pruneInvalidRegexRules('redirect', allRules);
if ( validRules.length === 0 ) { return; }
// Add redirect rules to dynamic ruleset without affecting rules
// outside redirect rules realm.
const newRuleMap = new Map(validatedRules.map(rule => [ rule.id, rule ]));
const addRules = [];
const removeRuleIds = [];
for ( const oldRule of dynamicRuleMap.values() ) {
if ( oldRule.id < REDIRECT_REALM_START ) { continue; }
if ( oldRule.id >= REDIRECT_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; }
if ( removeRuleIds.length !== 0 ) {
ubolLog(`Remove ${removeRuleIds.length} DNR redirect rules`);
}
if ( addRules.length !== 0 ) {
ubolLog(`Add ${addRules.length} DNR redirect rules`);
}
return dnr.updateDynamicRules({ addRules, removeRuleIds }).catch(reason => {
console.error(`updateRedirectRules() / ${reason}`);
});
ubolLog(`Add ${validRules.length} DNR redirect rules`);
toAdd.push(...validRules);
}
/******************************************************************************/
async function updateModifyHeadersRules() {
async function updateModifyHeadersRules(toAdd) {
const [
hasOmnipotence,
rulesetDetails,
dynamicRuleMap,
] = await Promise.all([
browser.permissions.contains({ origins: [ '<all_urls>' ] }),
getEnabledRulesetsDetails(),
getDynamicRules(),
]);
// Fetch modifyHeaders rules for all enabled rulesets
@ -370,69 +256,36 @@ async function updateModifyHeadersRules() {
// Redirect rules can only be enforced with omnipotence
const allRules = [];
if ( hasOmnipotence ) {
let ruleId = MODIFYHEADERS_REALM_START;
for ( const rules of rulesets ) {
if ( Array.isArray(rules) === false ) { continue; }
for ( const rule of rules ) {
rule.id = ruleId++;
rule.id = dynamicRulesetId++;
allRules.push(rule);
}
}
}
if ( allRules.length === 0 ) { return; }
const validatedRules = await pruneInvalidRegexRules('modify-headers', allRules);
const validRules = await pruneInvalidRegexRules('modify-headers', allRules);
if ( validRules.length === 0 ) { return; }
// Add modifyHeaders rules to dynamic ruleset without affecting rules
// outside modifyHeaders realm.
const newRuleMap = new Map(validatedRules.map(rule => [ rule.id, rule ]));
const addRules = [];
const removeRuleIds = [];
for ( const oldRule of dynamicRuleMap.values() ) {
if ( oldRule.id < MODIFYHEADERS_REALM_START ) { continue; }
if ( oldRule.id >= MODIFYHEADERS_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; }
if ( removeRuleIds.length !== 0 ) {
ubolLog(`Remove ${removeRuleIds.length} DNR modifyHeaders rules`);
}
if ( addRules.length !== 0 ) {
ubolLog(`Add ${addRules.length} DNR modifyHeaders rules`);
}
return dnr.updateDynamicRules({ addRules, removeRuleIds }).catch(reason => {
console.error(`updateModifyHeadersRules() / ${reason}`);
});
ubolLog(`Add ${validRules.length} DNR modify-headers rules`);
toAdd.push(...validRules);
}
/******************************************************************************/
async function updateStrictBlockRules() {
async function updateStrictBlockRules(toAdd) {
const [
hasOmnipotence,
rulesetDetails,
dynamicRuleMap,
permanentlyExcluded = [],
temporarilyExcluded = [],
] = await Promise.all([
browser.permissions.contains({ origins: [ '<all_urls>' ] }),
getEnabledRulesetsDetails(),
getDynamicRules(),
localRead('excludedStrictBlockHostnames'),
sessionRead('excludedStrictBlockHostnames'),
]);
// Fetch strick-block hostnames
@ -443,6 +296,8 @@ async function updateStrictBlockRules() {
}
const strictBlockRulesets = await Promise.all(toFetch);
const allExcluded = permanentlyExcluded.concat(temporarilyExcluded);
// Strict-block rules can only be enforced with omnipotence
let toStrictBlock = new Set();
if ( hasOmnipotence ) {
@ -450,63 +305,207 @@ async function updateStrictBlockRules() {
if ( Array.isArray(hostnames) === false ) { continue; }
toStrictBlock = toStrictBlock.union(new Set(hostnames));
}
} else if ( allExcluded.length !== 0 ) {
localRemove('excludedStrictBlockHostnames');
sessionRemove('excludedStrictBlockHostnames');
allExcluded.length = 0;
}
for ( const hn of allExcluded ) {
toStrictBlock.delete(hn);
}
if ( toStrictBlock.size === 0 ) { return; }
const ubolOrigin = runtime.getURL('').replace(/\/$/, '');
const rule = {
id: dynamicRulesetId++,
action: {
type: 'redirect',
redirect: {
regexSubstitution: `${ubolOrigin}/strict-block.html#\\0`,
},
},
condition: {
regexFilter: '^https:?//.+',
requestDomains: Array.from(toStrictBlock),
resourceTypes: [ 'main_frame' ],
},
priority: 29,
};
if ( allExcluded.length !== 0 ) {
rule.condition.excludedRequestDomains = allExcluded;
}
toAdd.push(rule);
ubolLog(`Add 1 DNR strict-block rules with ${toStrictBlock.size} domains`);
}
/******************************************************************************/
async function updateDynamicRules() {
dynamicRulesetId = 1;
const addRules = [];
const removeRuleIds = [];
if ( dynamicRuleMap.has(STRICTBLOCK_REALM_START) ) {
removeRuleIds.push(STRICTBLOCK_REALM_START);
dynamicRuleMap.delete(STRICTBLOCK_REALM_START);
}
if ( toStrictBlock.size !== 0 ) {
const ubolOrigin = runtime.getURL('').replace(/\/$/, '');
addRules.push({
id: STRICTBLOCK_REALM_START,
action: {
type: 'redirect',
redirect: {
regexSubstitution: `${ubolOrigin}/strict-block.html#\\0`,
},
},
condition: {
regexFilter: '^.+$',
requestDomains: Array.from(toStrictBlock),
resourceTypes: [ 'main_frame' ],
},
priority: 19,
});
dynamicRuleMap.set(STRICTBLOCK_REALM_START, addRules[0]);
}
const [ removeRuleIds ] = await Promise.all([
getDynamicRules().then(rules =>
rules.map(rule => rule.id)
.filter(id => id < TRUSTED_DIRECTIVE_BASE_RULE_ID)
),
updateRegexRules(addRules),
updateRemoveparamRules(addRules),
updateRedirectRules(addRules),
updateModifyHeadersRules(addRules),
updateStrictBlockRules(addRules),
]);
if ( addRules.length === 0 && removeRuleIds.length === 0 ) { return; }
if ( removeRuleIds.length !== 0 ) {
ubolLog(`Remove ${removeRuleIds.length} DNR strict-block rules`);
ubolLog(`Remove ${removeRuleIds.length} dynamic DNR rules`);
}
if ( addRules.length !== 0 ) {
ubolLog(`Add ${addRules.length} DNR strict-block rules`);
ubolLog(`Add ${addRules.length} dynamic DNR rules`);
}
return dnr.updateDynamicRules({ addRules, removeRuleIds }).catch(reason => {
console.error(`updateStrictBlockRules() / ${reason}`);
console.error(`updateDynamicRules() / ${reason}`);
});
}
/******************************************************************************/
// TODO: group all omnipotence-related rules into one realm.
async function filteringModesToDNR(modes) {
const trustedRules = await getDynamicRules({
ruleIds: [ TRUSTED_DIRECTIVE_BASE_RULE_ID+0 ],
});
const trustedRule = trustedRules.length !== 0 && trustedRules[0] || undefined;
const beforeRequestDomainSet = new Set(trustedRule?.condition.requestDomains);
const beforeExcludedRrequestDomainSet = new Set(trustedRule?.condition.excludedRequestDomains);
if ( trustedRule !== undefined && beforeRequestDomainSet.size === 0 ) {
beforeRequestDomainSet.add('all-urls');
} else {
beforeExcludedRrequestDomainSet.add('all-urls');
}
async function updateDynamicRules() {
ubolLog('Called updateDynamicRules()');
return Promise.all([
updateRegexRules(),
updateRemoveparamRules(),
updateRedirectRules(),
updateModifyHeadersRules(),
updateStrictBlockRules(),
const noneHostnames = new Set([ ...modes.none ]);
const notNoneHostnames = new Set([ ...modes.basic, ...modes.optimal, ...modes.complete ]);
let afterRequestDomainSet = new Set();
let afterExcludedRequestDomainSet = new Set();
if ( noneHostnames.has('all-urls') ) {
afterRequestDomainSet = new Set([ 'all-urls' ]);
afterExcludedRequestDomainSet = notNoneHostnames;
} else {
afterRequestDomainSet = noneHostnames;
afterExcludedRequestDomainSet = new Set([ 'all-urls' ]);
}
if ( eqSets(beforeRequestDomainSet, afterRequestDomainSet) ) {
if ( eqSets(beforeExcludedRrequestDomainSet, afterExcludedRequestDomainSet) ) {
return;
}
}
const removeRuleIds = [];
if ( trustedRule ) {
removeRuleIds.push(
TRUSTED_DIRECTIVE_BASE_RULE_ID+0,
TRUSTED_DIRECTIVE_BASE_RULE_ID+1
);
}
const allowEverywhere = afterRequestDomainSet.delete('all-urls');
afterExcludedRequestDomainSet.delete('all-urls');
const addRules = [];
if (
allowEverywhere ||
afterRequestDomainSet.size !== 0 ||
afterExcludedRequestDomainSet.size !== 0
) {
const rule0 = {
id: TRUSTED_DIRECTIVE_BASE_RULE_ID+0,
action: { type: 'allowAllRequests' },
condition: {
resourceTypes: [ 'main_frame' ],
},
priority: 100,
};
if ( afterRequestDomainSet.size !== 0 ) {
rule0.condition.requestDomains = Array.from(afterRequestDomainSet);
} else if ( afterExcludedRequestDomainSet.size !== 0 ) {
rule0.condition.excludedRequestDomains = Array.from(afterExcludedRequestDomainSet);
}
addRules.push(rule0);
unexcludeFromStrictBlock(afterRequestDomainSet);
// https://github.com/uBlockOrigin/uBOL-home/issues/114
const rule1 = {
id: TRUSTED_DIRECTIVE_BASE_RULE_ID+1,
action: { type: 'allow' },
condition: {
resourceTypes: [ 'script' ],
},
priority: 100,
};
if ( rule0.condition.requestDomains ) {
rule1.condition.initiatorDomains = rule0.condition.requestDomains.slice();
} else if ( rule0.condition.excludedRequestDomains ) {
rule1.condition.excludedInitiatorDomains = rule0.condition.excludedRequestDomains.slice();
}
addRules.push(rule1);
}
if ( addRules.length === 0 && removeRuleIds.length === 0 ) { return; }
return dnr.updateDynamicRules({ addRules, removeRuleIds });
}
/******************************************************************************/
async function excludeFromStrictBlock(hostname, permanent) {
if ( typeof hostname !== 'string' || hostname === '' ) { return; }
const readFn = permanent ? localRead : sessionRead;
const hostnames = new Set(await readFn('excludedStrictBlockHostnames'));
hostnames.add(hostname);
const writeFn = permanent ? localWrite : sessionWrite;
await writeFn('excludedStrictBlockHostnames', Array.from(hostnames));
return updateDynamicRules();
}
async function unexcludeFromStrictBlock(hostnames) {
const [
permanentlyExcluded,
temporarilyExcluded,
] = await Promise.all([
localRead('excludedStrictBlockHostnames').then(r => r = new Set(r)),
sessionRead('excludedStrictBlockHostnames').then(r => r = new Set(r)),
]);
const permanentCountBefore = permanentlyExcluded.size;
const temporaryCountBefore = temporarilyExcluded.size;
for ( const hn of hostnames ) {
permanentlyExcluded.delete(hn);
temporarilyExcluded.delete(hn);
}
const promises = [];
if ( permanentlyExcluded.size !== permanentCountBefore ) {
if ( permanentlyExcluded.size === 0 ) {
promises.push(
localRemove('excludedStrictBlockHostnames')
);
} else {
promises.push(
localWrite('excludedStrictBlockHostnames', Array.from(permanentlyExcluded))
);
}
}
if ( temporarilyExcluded.size !== temporaryCountBefore ) {
if ( temporarilyExcluded.size === 0 ) {
promises.push(
sessionRemove('excludedStrictBlockHostnames')
);
} else {
promises.push(
sessionWrite('excludedStrictBlockHostnames', Array.from(temporarilyExcluded))
);
}
}
if ( promises.length === 0 ) { return; }
await Promise.all(promises);
return updateDynamicRules();
}
/******************************************************************************/
@ -583,7 +582,7 @@ async function enableRulesets(ids) {
}
if ( enableRulesetSet.size === 0 && disableRulesetSet.size === 0 ) {
return;
return false;
}
const enableRulesetIds = Array.from(enableRulesetSet);
@ -597,7 +596,9 @@ async function enableRulesets(ids) {
}
await dnr.updateEnabledRulesets({ enableRulesetIds, disableRulesetIds });
return updateDynamicRules();
await updateDynamicRules();
return true;
}
/******************************************************************************/
@ -622,11 +623,11 @@ async function getEnabledRulesetsDetails() {
/******************************************************************************/
export {
TRUSTED_DIRECTIVE_BASE_RULE_ID,
getRulesetDetails,
getDynamicRules,
enableRulesets,
defaultRulesetsFromLanguage,
enableRulesets,
excludeFromStrictBlock,
filteringModesToDNR,
getRulesetDetails,
getEnabledRulesetsDetails,
updateDynamicRules,
};

View file

@ -38,7 +38,7 @@ setURL(self.location.hash.slice(1));
/******************************************************************************/
const urlToFragment = raw => {
function urlToFragment(raw) {
try {
const fragment = new DocumentFragment();
const url = new URL(raw);
@ -51,12 +51,23 @@ const urlToFragment = raw => {
} catch(_) {
}
return raw;
};
}
/******************************************************************************/
async function proceed() {
await sendMessage({
what: 'excludeFromStrictBlock',
hostname: toURL.hostname,
permanent: qs$('#disableWarning').checked,
});
window.location.replace(toURL.href);
}
/******************************************************************************/
dom.clear('#theURL > p > span:first-of-type');
qs$('#theURL > p > span:first-of-type').append(urlToFragment(toURL));
qs$('#theURL > p > span:first-of-type').append(urlToFragment(toURL.href));
/******************************************************************************/
@ -64,8 +75,6 @@ qs$('#theURL > p > span:first-of-type').append(urlToFragment(toURL));
// Parse URL to extract as much useful information as possible. This is
// useful to assist the user in deciding whether to navigate to the web page.
(( ) => {
if ( typeof URL !== 'function' ) { return; }
const reURL = /^https?:\/\//;
const liFromParam = function(name, value) {
@ -124,27 +133,26 @@ qs$('#theURL > p > span:first-of-type').append(urlToFragment(toURL));
return true;
};
if ( renderParams(qs$('#parsed'), toURL) === false ) { return; }
if ( renderParams(qs$('#parsed'), toURL.href) === false ) { return; }
dom.cl.remove('#toggleParse', 'hidden');
dom.on('#toggleParse', 'click', ( ) => {
dom.cl.toggle('#theURL', 'collapsed');
vAPI.localStorage.setItem(
'document-blocked-expand-url',
(dom.cl.has('#theURL', 'collapsed') === false).toString()
);
//vAPI.localStorage.setItem(
// 'document-blocked-expand-url',
// (dom.cl.has('#theURL', 'collapsed') === false).toString()
//);
});
vAPI.localStorage.getItemAsync('document-blocked-expand-url').then(value => {
dom.cl.toggle('#theURL', 'collapsed', value !== 'true' && value !== true);
});
//vAPI.localStorage.getItemAsync('document-blocked-expand-url').then(value => {
// dom.cl.toggle('#theURL', 'collapsed', value !== 'true' && value !== true);
//});
})();
/******************************************************************************/
// https://www.reddit.com/r/uBlockOrigin/comments/breeux/close_this_window_doesnt_work_on_firefox/
// https://www.reddit.com/r/uBlockOrigin/comments/breeux/
if ( window.history.length > 1 ) {
dom.on('#back', 'click', ( ) => {
window.history.back();
@ -159,37 +167,6 @@ if ( window.history.length > 1 ) {
qs$('#back').style.display = 'none';
}
/******************************************************************************/
const getTargetHostname = function() {
const url = new URL(toURL);
return url.hostname;
};
const proceedToURL = function() {
window.location.replace(toURL);
};
const proceedTemporary = async function() {
await sendMessage({
what: 'temporarilyWhitelistDocument',
hostname: getTargetHostname(),
});
proceedToURL();
};
const proceedPermanent = async function() {
await sendMessage({
what: 'toggleHostnameSwitch',
name: 'no-strict-blocking',
hostname: getTargetHostname(),
deep: true,
state: true,
persist: true,
});
proceedToURL();
};
dom.on('#disableWarning', 'change', ev => {
const checked = ev.target.checked;
dom.cl.toggle('[data-i18n="docblockedBack"]', 'disabled', checked);
@ -197,11 +174,7 @@ dom.on('#disableWarning', 'change', ev => {
});
dom.on('#proceed', 'click', ( ) => {
if ( qs$('#disableWarning').checked ) {
proceedPermanent();
} else {
proceedTemporary();
}
proceed();
});
/******************************************************************************/