mirror of https://github.com/gorhill/uBlock.git
[mv3] Add support to add/remove rulesets through policies
Related discussion: https://github.com/uBlockOrigin/uBOL-home/discussions/35#discussioncomment-11157444 New policy setting: `rulesets` Type: array Type of array items: string Each item in the list is a list id (as seen in `rulesets/ruleset-details.json`), prefixed with either `+` to enable the ruleset, or `-` to disable the ruleset. Users will not be able to enable or disable rulesets present in the `rulesets` policy. Disabled rulesets will not appear in the dashboard. Use `-*` to remove all non-default rulesets, except for those added using `+[ruleset_id]`. Additionally, some work has been done to properly handle policy changes in a non-blocking and deferred manner, as I observed that it often takes long for calls to `storage.manage.get` to resolve. This potentailly takes care of the following issue: https://github.com/uBlockOrigin/uBOL-home/issues/174
This commit is contained in:
parent
74921a0f27
commit
15dae359f7
11
Makefile
11
Makefile
|
@ -1,7 +1,8 @@
|
|||
# https://stackoverflow.com/a/6273809
|
||||
run_options := $(filter-out $@,$(MAKECMDGOALS))
|
||||
|
||||
.PHONY: all clean cleanassets test lint chromium opera firefox npm dig mv3 mv3-quick \
|
||||
.PHONY: all clean cleanassets test lint chromium opera firefox npm dig \
|
||||
mv3 mv3-quick mv3-chromium mv3-firefox \
|
||||
compare maxcost medcost mincost modifiers record wasm
|
||||
|
||||
sources := $(wildcard assets/* assets/*/* dist/version src/* src/*/* src/*/*/* src/*/*/*/*)
|
||||
|
@ -55,12 +56,16 @@ dig: dist/build/uBlock0.dig
|
|||
dig-snfe: dig
|
||||
cd dist/build/uBlock0.dig && npm run snfe $(run_options)
|
||||
|
||||
mv3-chromium: tools/make-mv3.sh $(sources) $(platform)
|
||||
dist/build/uBOLite.chromium: tools/make-mv3.sh $(sources) $(platform)
|
||||
tools/make-mv3.sh chromium
|
||||
|
||||
mv3-firefox: tools/make-mv3.sh $(sources) $(platform)
|
||||
mv3-chromium: dist/build/uBOLite.chromium
|
||||
|
||||
dist/build/uBOLite.firefox: tools/make-mv3.sh $(sources) $(platform)
|
||||
tools/make-mv3.sh firefox
|
||||
|
||||
mv3-firefox: dist/build/uBOLite.firefox
|
||||
|
||||
mv3-quick: tools/make-mv3.sh $(sources) $(platform)
|
||||
tools/make-mv3.sh quick
|
||||
|
||||
|
|
|
@ -114,11 +114,19 @@ h3[data-i18n="filteringMode0Name"]::first-letter {
|
|||
.groupEntry:not([data-groupkey="user"]) .listEntry:not(.isDefault).unused {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.listEntry.fromAdmin:has(input[disabled]:not(:checked)) {
|
||||
display: none;
|
||||
}
|
||||
.listEntry > * {
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
unicode-bidi: embed;
|
||||
}
|
||||
.listEntry .checkbox:has(input[disabled]),
|
||||
.listEntry .checkbox:has(input[disabled]) ~ span {
|
||||
filter: var(--checkbox-disabled-filter);
|
||||
}
|
||||
.listEntry .listname {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
/*******************************************************************************
|
||||
|
||||
uBlock Origin Lite - a comprehensive, MV3-compliant content blocker
|
||||
Copyright (C) 2022-present Raymond Hill
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
|
||||
Home: https://github.com/gorhill/uBlock
|
||||
*/
|
||||
|
||||
import {
|
||||
adminRead,
|
||||
localRead, localWrite,
|
||||
sessionRead, sessionWrite,
|
||||
} from './ext.js';
|
||||
|
||||
import {
|
||||
enableRulesets,
|
||||
getRulesetDetails,
|
||||
} from './ruleset-manager.js';
|
||||
|
||||
import {
|
||||
getTrustedSites,
|
||||
readFilteringModeDetails,
|
||||
} from './mode-manager.js';
|
||||
|
||||
import { broadcastMessage } from './utils.js';
|
||||
import { dnr } from './ext.js';
|
||||
import { registerInjectables } from './scripting-manager.js';
|
||||
import { rulesetConfig } from './config.js';
|
||||
import { ubolLog } from './debug.js';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const adminSettings = {
|
||||
keys: new Set(),
|
||||
timer: undefined,
|
||||
change(key) {
|
||||
this.keys.add(key);
|
||||
if ( this.timer !== undefined ) { return; }
|
||||
this.timer = self.setTimeout(( ) => {
|
||||
this.timer = undefined;
|
||||
this.process();
|
||||
}, 127);
|
||||
},
|
||||
async process() {
|
||||
if ( this.keys.has('rulesets') ) {
|
||||
ubolLog('admin setting "rulesets" changed');
|
||||
await enableRulesets(rulesetConfig.enabledRulesets);
|
||||
await registerInjectables();
|
||||
const results = await Promise.all([
|
||||
getAdminRulesets(),
|
||||
dnr.getEnabledRulesets(),
|
||||
]);
|
||||
const [ adminRulesets, enabledRulesets ] = results;
|
||||
broadcastMessage({ adminRulesets, enabledRulesets });
|
||||
}
|
||||
if ( this.keys.has('noFiltering') ) {
|
||||
ubolLog('admin setting "noFiltering" changed');
|
||||
await readFilteringModeDetails(true);
|
||||
const trustedSites = await getTrustedSites();
|
||||
broadcastMessage({ trustedSites: Array.from(trustedSites) });
|
||||
}
|
||||
this.keys.clear();
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
export async function getAdminRulesets() {
|
||||
const adminList = await adminReadEx('rulesets');
|
||||
const adminRulesets = new Set(Array.isArray(adminList) && adminList || []);
|
||||
if ( adminRulesets.has('-*') ) {
|
||||
adminRulesets.delete('-*');
|
||||
const rulesetDetails = await getRulesetDetails();
|
||||
for ( const ruleset of rulesetDetails.values() ) {
|
||||
if ( ruleset.enabled ) { continue; }
|
||||
if ( adminRulesets.has(`+${ruleset.id}`) ) { continue; }
|
||||
adminRulesets.add(`-${ruleset.id}`);
|
||||
}
|
||||
}
|
||||
return Array.from(adminRulesets);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
export async function adminReadEx(key) {
|
||||
let cacheValue;
|
||||
const session = await sessionRead(`admin_${key}`);
|
||||
if ( session ) {
|
||||
cacheValue = session.data;
|
||||
} else {
|
||||
const local = await localRead(`admin_${key}`);
|
||||
if ( local ) {
|
||||
cacheValue = local.data;
|
||||
}
|
||||
}
|
||||
adminRead(key).then(async value => {
|
||||
const adminKey = `admin_${key}`;
|
||||
await Promise.all([
|
||||
sessionWrite(adminKey, { data: value }),
|
||||
localWrite(adminKey, { data: value }),
|
||||
]);
|
||||
if ( JSON.stringify(value) === JSON.stringify(cacheValue) ) { return; }
|
||||
adminSettings.change(key);
|
||||
});
|
||||
return cacheValue;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
|
@ -19,24 +19,6 @@
|
|||
Home: https://github.com/gorhill/uBlock
|
||||
*/
|
||||
|
||||
import {
|
||||
adminRead,
|
||||
browser,
|
||||
dnr,
|
||||
localRead, localWrite,
|
||||
runtime,
|
||||
sessionRead, sessionWrite,
|
||||
windows,
|
||||
} from './ext.js';
|
||||
|
||||
import {
|
||||
defaultRulesetsFromLanguage,
|
||||
enableRulesets,
|
||||
getEnabledRulesetsDetails,
|
||||
getRulesetDetails,
|
||||
updateDynamicRules,
|
||||
} from './ruleset-manager.js';
|
||||
|
||||
import {
|
||||
MODE_BASIC,
|
||||
MODE_OPTIMAL,
|
||||
|
@ -49,75 +31,51 @@ import {
|
|||
syncWithBrowserPermissions,
|
||||
} from './mode-manager.js';
|
||||
|
||||
import {
|
||||
adminRead,
|
||||
browser,
|
||||
dnr,
|
||||
localRead, localWrite,
|
||||
runtime,
|
||||
windows,
|
||||
} from './ext.js';
|
||||
|
||||
import {
|
||||
enableRulesets,
|
||||
getEnabledRulesetsDetails,
|
||||
getRulesetDetails,
|
||||
updateDynamicRules,
|
||||
} from './ruleset-manager.js';
|
||||
|
||||
import {
|
||||
getMatchedRules,
|
||||
isSideloaded,
|
||||
ubolLog,
|
||||
} from './debug.js';
|
||||
|
||||
import {
|
||||
loadRulesetConfig,
|
||||
process,
|
||||
rulesetConfig,
|
||||
saveRulesetConfig,
|
||||
} from './config.js';
|
||||
|
||||
import { broadcastMessage } from './utils.js';
|
||||
import { getAdminRulesets } from './admin.js';
|
||||
import { registerInjectables } from './scripting-manager.js';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const rulesetConfig = {
|
||||
version: '',
|
||||
enabledRulesets: [ 'default' ],
|
||||
autoReload: true,
|
||||
showBlockedCount: true,
|
||||
};
|
||||
|
||||
const UBOL_ORIGIN = runtime.getURL('').replace(/\/$/, '');
|
||||
|
||||
const canShowBlockedCount = typeof dnr.setExtensionActionOptions === 'function';
|
||||
|
||||
let firstRun = false;
|
||||
let wakeupRun = false;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
function getCurrentVersion() {
|
||||
return runtime.getManifest().version;
|
||||
}
|
||||
|
||||
async function loadRulesetConfig() {
|
||||
let data = await sessionRead('rulesetConfig');
|
||||
if ( data ) {
|
||||
rulesetConfig.version = data.version;
|
||||
rulesetConfig.enabledRulesets = data.enabledRulesets;
|
||||
rulesetConfig.autoReload = typeof data.autoReload === 'boolean'
|
||||
? data.autoReload
|
||||
: true;
|
||||
rulesetConfig.showBlockedCount = typeof data.showBlockedCount === 'boolean'
|
||||
? data.showBlockedCount
|
||||
: true;
|
||||
wakeupRun = true;
|
||||
return;
|
||||
}
|
||||
data = await localRead('rulesetConfig');
|
||||
if ( data ) {
|
||||
rulesetConfig.version = data.version;
|
||||
rulesetConfig.enabledRulesets = data.enabledRulesets;
|
||||
rulesetConfig.autoReload = typeof data.autoReload === 'boolean'
|
||||
? data.autoReload
|
||||
: true;
|
||||
rulesetConfig.showBlockedCount = typeof data.showBlockedCount === 'boolean'
|
||||
? data.showBlockedCount
|
||||
: true;
|
||||
sessionWrite('rulesetConfig', rulesetConfig);
|
||||
return;
|
||||
}
|
||||
rulesetConfig.enabledRulesets = await defaultRulesetsFromLanguage();
|
||||
sessionWrite('rulesetConfig', rulesetConfig);
|
||||
localWrite('rulesetConfig', rulesetConfig);
|
||||
firstRun = true;
|
||||
}
|
||||
|
||||
async function saveRulesetConfig() {
|
||||
sessionWrite('rulesetConfig', rulesetConfig);
|
||||
return localWrite('rulesetConfig', rulesetConfig);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
async function hasGreatPowers(origin) {
|
||||
|
@ -216,7 +174,9 @@ function onMessage(request, sender, callback) {
|
|||
}).then(( ) => {
|
||||
registerInjectables();
|
||||
callback();
|
||||
broadcastMessage({ enabledRulesets: rulesetConfig.enabledRulesets });
|
||||
return dnr.getEnabledRulesets();
|
||||
}).then(enabledRulesets => {
|
||||
broadcastMessage({ enabledRulesets });
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
@ -227,25 +187,28 @@ function onMessage(request, sender, callback) {
|
|||
getTrustedSites(),
|
||||
getRulesetDetails(),
|
||||
dnr.getEnabledRulesets(),
|
||||
getAdminRulesets(),
|
||||
]).then(results => {
|
||||
const [
|
||||
defaultFilteringMode,
|
||||
trustedSites,
|
||||
rulesetDetails,
|
||||
enabledRulesets,
|
||||
adminRulesets,
|
||||
] = results;
|
||||
callback({
|
||||
defaultFilteringMode,
|
||||
trustedSites: Array.from(trustedSites),
|
||||
enabledRulesets,
|
||||
adminRulesets,
|
||||
maxNumberOfEnabledRulesets: dnr.MAX_NUMBER_OF_ENABLED_STATIC_RULESETS,
|
||||
rulesetDetails: Array.from(rulesetDetails.values()),
|
||||
autoReload: rulesetConfig.autoReload,
|
||||
showBlockedCount: rulesetConfig.showBlockedCount,
|
||||
canShowBlockedCount,
|
||||
firstRun,
|
||||
firstRun: process.firstRun,
|
||||
});
|
||||
firstRun = false;
|
||||
process.firstRun = false;
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
@ -367,6 +330,8 @@ function onMessage(request, sender, callback) {
|
|||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
@ -374,12 +339,12 @@ function onMessage(request, sender, callback) {
|
|||
async function start() {
|
||||
await loadRulesetConfig();
|
||||
|
||||
if ( wakeupRun === false ) {
|
||||
if ( process.wakeupRun === false ) {
|
||||
await enableRulesets(rulesetConfig.enabledRulesets);
|
||||
}
|
||||
|
||||
// We need to update the regex rules only when ruleset version changes.
|
||||
if ( wakeupRun === false ) {
|
||||
if ( process.wakeupRun === false ) {
|
||||
const currentVersion = getCurrentVersion();
|
||||
if ( currentVersion !== rulesetConfig.version ) {
|
||||
ubolLog(`Version change: ${rulesetConfig.version} => ${currentVersion}`);
|
||||
|
@ -396,7 +361,7 @@ async function start() {
|
|||
// Unsure whether the browser remembers correctly registered css/scripts
|
||||
// after we quit the browser. For now uBOL will check unconditionally at
|
||||
// launch time whether content css/scripts are properly registered.
|
||||
if ( wakeupRun === false || permissionsChanged ) {
|
||||
if ( process.wakeupRun === false || permissionsChanged ) {
|
||||
registerInjectables();
|
||||
|
||||
const enabledRulesets = await dnr.getEnabledRulesets();
|
||||
|
@ -409,7 +374,7 @@ async function start() {
|
|||
|
||||
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/declarativeNetRequest
|
||||
// Firefox API does not support `dnr.setExtensionActionOptions`
|
||||
if ( wakeupRun === false && canShowBlockedCount ) {
|
||||
if ( process.wakeupRun === false && canShowBlockedCount ) {
|
||||
dnr.setExtensionActionOptions({
|
||||
displayActionCountAsBadgeText: rulesetConfig.showBlockedCount,
|
||||
});
|
||||
|
@ -421,7 +386,7 @@ async function start() {
|
|||
( ) => { onPermissionsRemoved(); }
|
||||
);
|
||||
|
||||
if ( firstRun ) {
|
||||
if ( process.firstRun ) {
|
||||
const enableOptimal = await hasOmnipotence();
|
||||
if ( enableOptimal ) {
|
||||
const afterLevel = await setDefaultFilteringMode(MODE_OPTIMAL);
|
||||
|
@ -434,7 +399,7 @@ async function start() {
|
|||
if ( disableFirstRunPage !== true ) {
|
||||
runtime.openOptionsPage();
|
||||
} else {
|
||||
firstRun = false;
|
||||
process.firstRun = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
/*******************************************************************************
|
||||
|
||||
uBlock Origin Lite - a comprehensive, MV3-compliant content blocker
|
||||
Copyright (C) 2022-present Raymond Hill
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
|
||||
Home: https://github.com/gorhill/uBlock
|
||||
*/
|
||||
|
||||
import {
|
||||
localRead, localWrite,
|
||||
sessionRead, sessionWrite,
|
||||
} from './ext.js';
|
||||
|
||||
import { defaultRulesetsFromLanguage } from './ruleset-manager.js';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
export const rulesetConfig = {
|
||||
version: '',
|
||||
enabledRulesets: [ 'default' ],
|
||||
autoReload: true,
|
||||
showBlockedCount: true,
|
||||
};
|
||||
|
||||
export const process = {
|
||||
firstRun: false,
|
||||
wakeupRun: false,
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
export async function loadRulesetConfig() {
|
||||
const sessionData = await sessionRead('rulesetConfig');
|
||||
if ( sessionData ) {
|
||||
rulesetConfig.version = sessionData.version;
|
||||
rulesetConfig.enabledRulesets = sessionData.enabledRulesets;
|
||||
rulesetConfig.autoReload = typeof sessionData.autoReload === 'boolean'
|
||||
? sessionData.autoReload
|
||||
: true;
|
||||
rulesetConfig.showBlockedCount = typeof sessionData.showBlockedCount === 'boolean'
|
||||
? sessionData.showBlockedCount
|
||||
: true;
|
||||
process.wakeupRun = true;
|
||||
return;
|
||||
}
|
||||
const localData = await localRead('rulesetConfig');
|
||||
if ( localData ) {
|
||||
rulesetConfig.version = localData.version;
|
||||
rulesetConfig.enabledRulesets = localData.enabledRulesets;
|
||||
rulesetConfig.autoReload = typeof localData.autoReload === 'boolean'
|
||||
? localData.autoReload
|
||||
: true;
|
||||
rulesetConfig.showBlockedCount = typeof localData.showBlockedCount === 'boolean'
|
||||
? localData.showBlockedCount
|
||||
: true;
|
||||
sessionWrite('rulesetConfig', rulesetConfig);
|
||||
return;
|
||||
}
|
||||
rulesetConfig.enabledRulesets = await defaultRulesetsFromLanguage();
|
||||
sessionWrite('rulesetConfig', rulesetConfig);
|
||||
localWrite('rulesetConfig', rulesetConfig);
|
||||
process.firstRun = true;
|
||||
}
|
||||
|
||||
export async function saveRulesetConfig() {
|
||||
sessionWrite('rulesetConfig', rulesetConfig);
|
||||
return localWrite('rulesetConfig', rulesetConfig);
|
||||
}
|
|
@ -24,14 +24,6 @@ import {
|
|||
getDynamicRules,
|
||||
} from './ruleset-manager.js';
|
||||
|
||||
import {
|
||||
adminRead,
|
||||
browser,
|
||||
dnr,
|
||||
localRead, localRemove, localWrite,
|
||||
sessionRead, sessionWrite,
|
||||
} from './ext.js';
|
||||
|
||||
import {
|
||||
broadcastMessage,
|
||||
hostnamesFromMatches,
|
||||
|
@ -39,6 +31,15 @@ import {
|
|||
toBroaderHostname,
|
||||
} from './utils.js';
|
||||
|
||||
import {
|
||||
browser,
|
||||
dnr,
|
||||
localRead, localWrite,
|
||||
sessionRead, sessionWrite,
|
||||
} from './ext.js';
|
||||
|
||||
import { adminReadEx } from './admin.js';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// 0: no filtering
|
||||
|
@ -224,38 +225,40 @@ function applyFilteringMode(filteringModes, hostname, afterLevel) {
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
async function readFilteringModeDetails() {
|
||||
if ( readFilteringModeDetails.cache ) {
|
||||
return readFilteringModeDetails.cache;
|
||||
}
|
||||
const sessionModes = await sessionRead('filteringModeDetails');
|
||||
if ( sessionModes instanceof Object ) {
|
||||
readFilteringModeDetails.cache = unserializeModeDetails(sessionModes);
|
||||
return readFilteringModeDetails.cache;
|
||||
export async function readFilteringModeDetails(bypassCache = false) {
|
||||
if ( bypassCache === false ) {
|
||||
if ( readFilteringModeDetails.cache ) {
|
||||
return readFilteringModeDetails.cache;
|
||||
}
|
||||
const sessionModes = await sessionRead('filteringModeDetails');
|
||||
if ( sessionModes instanceof Object ) {
|
||||
readFilteringModeDetails.cache = unserializeModeDetails(sessionModes);
|
||||
return readFilteringModeDetails.cache;
|
||||
}
|
||||
}
|
||||
let [ userModes, adminNoFiltering ] = await Promise.all([
|
||||
localRead('filteringModeDetails'),
|
||||
localRead('adminNoFiltering'),
|
||||
adminReadEx('noFiltering'),
|
||||
]);
|
||||
if ( userModes === undefined ) {
|
||||
userModes = { basic: [ 'all-urls' ] };
|
||||
}
|
||||
userModes = unserializeModeDetails(userModes);
|
||||
if ( Array.isArray(adminNoFiltering) ) {
|
||||
if ( adminNoFiltering.includes('-*') ) {
|
||||
userModes.none.clear();
|
||||
}
|
||||
for ( const hn of adminNoFiltering ) {
|
||||
applyFilteringMode(userModes, hn, 0);
|
||||
if ( hn.charAt(0) === '-' ) {
|
||||
userModes.none.delete(hn.slice(1));
|
||||
} else {
|
||||
applyFilteringMode(userModes, hn, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
filteringModesToDNR(userModes);
|
||||
sessionWrite('filteringModeDetails', serializeModeDetails(userModes));
|
||||
readFilteringModeDetails.cache = userModes;
|
||||
adminRead('noFiltering').then(results => {
|
||||
if ( results ) {
|
||||
localWrite('adminNoFiltering', results);
|
||||
} else {
|
||||
localRemove('adminNoFiltering');
|
||||
}
|
||||
});
|
||||
return userModes;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,8 +19,14 @@
|
|||
Home: https://github.com/gorhill/uBlock
|
||||
*/
|
||||
|
||||
import { browser, dnr, i18n } from './ext.js';
|
||||
import {
|
||||
browser,
|
||||
dnr,
|
||||
i18n,
|
||||
} from './ext.js';
|
||||
|
||||
import { fetchJSON } from './fetch.js';
|
||||
import { getAdminRulesets } from './admin.js';
|
||||
import { ubolLog } from './debug.js';
|
||||
|
||||
/******************************************************************************/
|
||||
|
@ -460,7 +466,22 @@ async function defaultRulesetsFromLanguage() {
|
|||
|
||||
async function enableRulesets(ids) {
|
||||
const afterIds = new Set(ids);
|
||||
const beforeIds = new Set(await dnr.getEnabledRulesets());
|
||||
const [ beforeIds, adminIds, rulesetDetails ] = await Promise.all([
|
||||
dnr.getEnabledRulesets().then(ids => new Set(ids)),
|
||||
getAdminRulesets(),
|
||||
getRulesetDetails(),
|
||||
]);
|
||||
|
||||
for ( const token of adminIds ) {
|
||||
const c0 = token.charAt(0);
|
||||
const id = token.slice(1);
|
||||
if ( c0 === '+' ) {
|
||||
afterIds.add(id);
|
||||
} else if ( c0 === '-' ) {
|
||||
afterIds.delete(id);
|
||||
}
|
||||
}
|
||||
|
||||
const enableRulesetSet = new Set();
|
||||
const disableRulesetSet = new Set();
|
||||
for ( const id of afterIds ) {
|
||||
|
@ -472,13 +493,8 @@ async function enableRulesets(ids) {
|
|||
disableRulesetSet.add(id);
|
||||
}
|
||||
|
||||
if ( enableRulesetSet.size === 0 && disableRulesetSet.size === 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Be sure the rulesets to enable/disable do exist in the current version,
|
||||
// otherwise the API throws.
|
||||
const rulesetDetails = await getRulesetDetails();
|
||||
for ( const id of enableRulesetSet ) {
|
||||
if ( rulesetDetails.has(id) ) { continue; }
|
||||
enableRulesetSet.delete(id);
|
||||
|
@ -487,6 +503,11 @@ async function enableRulesets(ids) {
|
|||
if ( rulesetDetails.has(id) ) { continue; }
|
||||
disableRulesetSet.delete(id);
|
||||
}
|
||||
|
||||
if ( enableRulesetSet.size === 0 && disableRulesetSet.size === 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const enableRulesetIds = Array.from(enableRulesetSet);
|
||||
const disableRulesetIds = Array.from(disableRulesetSet);
|
||||
|
||||
|
@ -497,7 +518,7 @@ async function enableRulesets(ids) {
|
|||
ubolLog(`Disable ruleset: ${disableRulesetIds}`);
|
||||
}
|
||||
await dnr.updateEnabledRulesets({ enableRulesetIds, disableRulesetIds });
|
||||
|
||||
|
||||
return updateDynamicRules();
|
||||
}
|
||||
|
||||
|
|
|
@ -40,6 +40,18 @@ function hashFromIterable(iter) {
|
|||
return Array.from(iter).sort().join('\n');
|
||||
}
|
||||
|
||||
function isAdminRuleset(listkey) {
|
||||
const { adminRulesets = [] } = cachedRulesetData;
|
||||
for ( const id of adminRulesets ) {
|
||||
const pos = id.indexOf(listkey);
|
||||
if ( pos === 0 ) { return true; }
|
||||
if ( pos !== 1 ) { continue; }
|
||||
const c = id.charAt(0);
|
||||
if ( c === '+' || c === '-' ) { return true; }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
function rulesetStats(rulesetId) {
|
||||
|
@ -94,10 +106,13 @@ function renderFilterLists() {
|
|||
li.title = listStatsTemplate
|
||||
.replace('{{ruleCount}}', renderNumber(stats.ruleCount))
|
||||
.replace('{{filterCount}}', renderNumber(stats.filterCount));
|
||||
const fromAdmin = isAdminRuleset(ruleset.id);
|
||||
dom.cl.toggle(li, 'fromAdmin', fromAdmin);
|
||||
const disabled = stats.ruleCount === 0 || fromAdmin;
|
||||
dom.attr(
|
||||
qs$(li, '.input.checkbox'),
|
||||
qs$(li, '.input.checkbox input'),
|
||||
'disabled',
|
||||
stats.ruleCount === 0 ? '' : null
|
||||
disabled ? '' : null
|
||||
);
|
||||
dom.cl.remove(li, 'discard');
|
||||
return li;
|
||||
|
@ -358,7 +373,9 @@ async function applyEnabledRulesets() {
|
|||
const checked = qs$(liEntry, 'input[type="checkbox"]:checked') !== null;
|
||||
dom.cl.toggle(liEntry, 'checked', checked);
|
||||
if ( checked === false ) { continue; }
|
||||
enabledRulesets.push(liEntry.dataset.listkey);
|
||||
const { listkey } = liEntry.dataset;
|
||||
if ( isAdminRuleset(listkey) ) { continue; }
|
||||
enabledRulesets.push(listkey);
|
||||
}
|
||||
|
||||
await sendMessage({
|
||||
|
@ -433,9 +450,12 @@ localRead('hideUnusedFilterLists').then(value => {
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
const bc = new self.BroadcastChannel('uBOL');
|
||||
function listen() {
|
||||
const bc = new self.BroadcastChannel('uBOL');
|
||||
bc.onmessage = listen.onmessage;
|
||||
}
|
||||
|
||||
bc.onmessage = ev => {
|
||||
listen.onmessage = ev => {
|
||||
const message = ev.data;
|
||||
if ( message instanceof Object === false ) { return; }
|
||||
const local = cachedRulesetData;
|
||||
|
@ -477,6 +497,13 @@ bc.onmessage = ev => {
|
|||
}
|
||||
}
|
||||
|
||||
if ( message.adminRulesets !== undefined ) {
|
||||
if ( hashFromIterable(message.adminRulesets) !== hashFromIterable(local.adminRulesets) ) {
|
||||
local.adminRulesets = message.adminRulesets;
|
||||
render = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( message.enabledRulesets !== undefined ) {
|
||||
if ( hashFromIterable(message.enabledRulesets) !== hashFromIterable(local.enabledRulesets) ) {
|
||||
local.enabledRulesets = message.enabledRulesets;
|
||||
|
@ -503,6 +530,7 @@ sendMessage({
|
|||
renderWidgets();
|
||||
} catch(ex) {
|
||||
}
|
||||
listen();
|
||||
}).catch(reason => {
|
||||
console.trace(reason);
|
||||
});
|
||||
|
|
|
@ -115,7 +115,7 @@ const hostnamesFromMatches = origins => {
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
export const broadcastMessage = message => {
|
||||
const broadcastMessage = message => {
|
||||
const bc = new self.BroadcastChannel('uBOL');
|
||||
bc.postMessage(message);
|
||||
};
|
||||
|
@ -123,6 +123,7 @@ export const broadcastMessage = message => {
|
|||
/******************************************************************************/
|
||||
|
||||
export {
|
||||
broadcastMessage,
|
||||
parsedURLromOrigin,
|
||||
toBroaderHostname,
|
||||
isDescendantHostname,
|
||||
|
|
|
@ -10,6 +10,12 @@
|
|||
"disableFirstRunPage": {
|
||||
"title": "Disable first run page",
|
||||
"type": "boolean"
|
||||
},
|
||||
"rulesets": {
|
||||
"title": "Rulesets to add/remove",
|
||||
"description": "Prefix a ruleset id with '+' to add, or '-' to remove. Use '-*' to disable all non-default lists.",
|
||||
"type": "array",
|
||||
"items": { "type": "string" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue