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
|
# https://stackoverflow.com/a/6273809
|
||||||
run_options := $(filter-out $@,$(MAKECMDGOALS))
|
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
|
compare maxcost medcost mincost modifiers record wasm
|
||||||
|
|
||||||
sources := $(wildcard assets/* assets/*/* dist/version src/* src/*/* src/*/*/* src/*/*/*/*)
|
sources := $(wildcard assets/* assets/*/* dist/version src/* src/*/* src/*/*/* src/*/*/*/*)
|
||||||
|
@ -55,12 +56,16 @@ dig: dist/build/uBlock0.dig
|
||||||
dig-snfe: dig
|
dig-snfe: dig
|
||||||
cd dist/build/uBlock0.dig && npm run snfe $(run_options)
|
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
|
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
|
tools/make-mv3.sh firefox
|
||||||
|
|
||||||
|
mv3-firefox: dist/build/uBOLite.firefox
|
||||||
|
|
||||||
mv3-quick: tools/make-mv3.sh $(sources) $(platform)
|
mv3-quick: tools/make-mv3.sh $(sources) $(platform)
|
||||||
tools/make-mv3.sh quick
|
tools/make-mv3.sh quick
|
||||||
|
|
||||||
|
|
|
@ -114,11 +114,19 @@ h3[data-i18n="filteringMode0Name"]::first-letter {
|
||||||
.groupEntry:not([data-groupkey="user"]) .listEntry:not(.isDefault).unused {
|
.groupEntry:not([data-groupkey="user"]) .listEntry:not(.isDefault).unused {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.listEntry.fromAdmin:has(input[disabled]:not(:checked)) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
.listEntry > * {
|
.listEntry > * {
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
unicode-bidi: embed;
|
unicode-bidi: embed;
|
||||||
}
|
}
|
||||||
|
.listEntry .checkbox:has(input[disabled]),
|
||||||
|
.listEntry .checkbox:has(input[disabled]) ~ span {
|
||||||
|
filter: var(--checkbox-disabled-filter);
|
||||||
|
}
|
||||||
.listEntry .listname {
|
.listEntry .listname {
|
||||||
white-space: nowrap;
|
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
|
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 {
|
import {
|
||||||
MODE_BASIC,
|
MODE_BASIC,
|
||||||
MODE_OPTIMAL,
|
MODE_OPTIMAL,
|
||||||
|
@ -49,75 +31,51 @@ import {
|
||||||
syncWithBrowserPermissions,
|
syncWithBrowserPermissions,
|
||||||
} from './mode-manager.js';
|
} 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 {
|
import {
|
||||||
getMatchedRules,
|
getMatchedRules,
|
||||||
isSideloaded,
|
isSideloaded,
|
||||||
ubolLog,
|
ubolLog,
|
||||||
} from './debug.js';
|
} from './debug.js';
|
||||||
|
|
||||||
|
import {
|
||||||
|
loadRulesetConfig,
|
||||||
|
process,
|
||||||
|
rulesetConfig,
|
||||||
|
saveRulesetConfig,
|
||||||
|
} from './config.js';
|
||||||
|
|
||||||
import { broadcastMessage } from './utils.js';
|
import { broadcastMessage } from './utils.js';
|
||||||
|
import { getAdminRulesets } from './admin.js';
|
||||||
import { registerInjectables } from './scripting-manager.js';
|
import { registerInjectables } from './scripting-manager.js';
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
const rulesetConfig = {
|
|
||||||
version: '',
|
|
||||||
enabledRulesets: [ 'default' ],
|
|
||||||
autoReload: true,
|
|
||||||
showBlockedCount: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
const UBOL_ORIGIN = runtime.getURL('').replace(/\/$/, '');
|
const UBOL_ORIGIN = runtime.getURL('').replace(/\/$/, '');
|
||||||
|
|
||||||
const canShowBlockedCount = typeof dnr.setExtensionActionOptions === 'function';
|
const canShowBlockedCount = typeof dnr.setExtensionActionOptions === 'function';
|
||||||
|
|
||||||
let firstRun = false;
|
|
||||||
let wakeupRun = false;
|
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
function getCurrentVersion() {
|
function getCurrentVersion() {
|
||||||
return runtime.getManifest().version;
|
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) {
|
async function hasGreatPowers(origin) {
|
||||||
|
@ -216,7 +174,9 @@ function onMessage(request, sender, callback) {
|
||||||
}).then(( ) => {
|
}).then(( ) => {
|
||||||
registerInjectables();
|
registerInjectables();
|
||||||
callback();
|
callback();
|
||||||
broadcastMessage({ enabledRulesets: rulesetConfig.enabledRulesets });
|
return dnr.getEnabledRulesets();
|
||||||
|
}).then(enabledRulesets => {
|
||||||
|
broadcastMessage({ enabledRulesets });
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -227,25 +187,28 @@ function onMessage(request, sender, callback) {
|
||||||
getTrustedSites(),
|
getTrustedSites(),
|
||||||
getRulesetDetails(),
|
getRulesetDetails(),
|
||||||
dnr.getEnabledRulesets(),
|
dnr.getEnabledRulesets(),
|
||||||
|
getAdminRulesets(),
|
||||||
]).then(results => {
|
]).then(results => {
|
||||||
const [
|
const [
|
||||||
defaultFilteringMode,
|
defaultFilteringMode,
|
||||||
trustedSites,
|
trustedSites,
|
||||||
rulesetDetails,
|
rulesetDetails,
|
||||||
enabledRulesets,
|
enabledRulesets,
|
||||||
|
adminRulesets,
|
||||||
] = results;
|
] = results;
|
||||||
callback({
|
callback({
|
||||||
defaultFilteringMode,
|
defaultFilteringMode,
|
||||||
trustedSites: Array.from(trustedSites),
|
trustedSites: Array.from(trustedSites),
|
||||||
enabledRulesets,
|
enabledRulesets,
|
||||||
|
adminRulesets,
|
||||||
maxNumberOfEnabledRulesets: dnr.MAX_NUMBER_OF_ENABLED_STATIC_RULESETS,
|
maxNumberOfEnabledRulesets: dnr.MAX_NUMBER_OF_ENABLED_STATIC_RULESETS,
|
||||||
rulesetDetails: Array.from(rulesetDetails.values()),
|
rulesetDetails: Array.from(rulesetDetails.values()),
|
||||||
autoReload: rulesetConfig.autoReload,
|
autoReload: rulesetConfig.autoReload,
|
||||||
showBlockedCount: rulesetConfig.showBlockedCount,
|
showBlockedCount: rulesetConfig.showBlockedCount,
|
||||||
canShowBlockedCount,
|
canShowBlockedCount,
|
||||||
firstRun,
|
firstRun: process.firstRun,
|
||||||
});
|
});
|
||||||
firstRun = false;
|
process.firstRun = false;
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -367,6 +330,8 @@ function onMessage(request, sender, callback) {
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
@ -374,12 +339,12 @@ function onMessage(request, sender, callback) {
|
||||||
async function start() {
|
async function start() {
|
||||||
await loadRulesetConfig();
|
await loadRulesetConfig();
|
||||||
|
|
||||||
if ( wakeupRun === false ) {
|
if ( process.wakeupRun === false ) {
|
||||||
await enableRulesets(rulesetConfig.enabledRulesets);
|
await enableRulesets(rulesetConfig.enabledRulesets);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need to update the regex rules only when ruleset version changes.
|
// We need to update the regex rules only when ruleset version changes.
|
||||||
if ( wakeupRun === false ) {
|
if ( process.wakeupRun === false ) {
|
||||||
const currentVersion = getCurrentVersion();
|
const currentVersion = getCurrentVersion();
|
||||||
if ( currentVersion !== rulesetConfig.version ) {
|
if ( currentVersion !== rulesetConfig.version ) {
|
||||||
ubolLog(`Version change: ${rulesetConfig.version} => ${currentVersion}`);
|
ubolLog(`Version change: ${rulesetConfig.version} => ${currentVersion}`);
|
||||||
|
@ -396,7 +361,7 @@ async function start() {
|
||||||
// Unsure whether the browser remembers correctly registered css/scripts
|
// Unsure whether the browser remembers correctly registered css/scripts
|
||||||
// after we quit the browser. For now uBOL will check unconditionally at
|
// after we quit the browser. For now uBOL will check unconditionally at
|
||||||
// launch time whether content css/scripts are properly registered.
|
// launch time whether content css/scripts are properly registered.
|
||||||
if ( wakeupRun === false || permissionsChanged ) {
|
if ( process.wakeupRun === false || permissionsChanged ) {
|
||||||
registerInjectables();
|
registerInjectables();
|
||||||
|
|
||||||
const enabledRulesets = await dnr.getEnabledRulesets();
|
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
|
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/declarativeNetRequest
|
||||||
// Firefox API does not support `dnr.setExtensionActionOptions`
|
// Firefox API does not support `dnr.setExtensionActionOptions`
|
||||||
if ( wakeupRun === false && canShowBlockedCount ) {
|
if ( process.wakeupRun === false && canShowBlockedCount ) {
|
||||||
dnr.setExtensionActionOptions({
|
dnr.setExtensionActionOptions({
|
||||||
displayActionCountAsBadgeText: rulesetConfig.showBlockedCount,
|
displayActionCountAsBadgeText: rulesetConfig.showBlockedCount,
|
||||||
});
|
});
|
||||||
|
@ -421,7 +386,7 @@ async function start() {
|
||||||
( ) => { onPermissionsRemoved(); }
|
( ) => { onPermissionsRemoved(); }
|
||||||
);
|
);
|
||||||
|
|
||||||
if ( firstRun ) {
|
if ( process.firstRun ) {
|
||||||
const enableOptimal = await hasOmnipotence();
|
const enableOptimal = await hasOmnipotence();
|
||||||
if ( enableOptimal ) {
|
if ( enableOptimal ) {
|
||||||
const afterLevel = await setDefaultFilteringMode(MODE_OPTIMAL);
|
const afterLevel = await setDefaultFilteringMode(MODE_OPTIMAL);
|
||||||
|
@ -434,7 +399,7 @@ async function start() {
|
||||||
if ( disableFirstRunPage !== true ) {
|
if ( disableFirstRunPage !== true ) {
|
||||||
runtime.openOptionsPage();
|
runtime.openOptionsPage();
|
||||||
} else {
|
} 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,
|
getDynamicRules,
|
||||||
} from './ruleset-manager.js';
|
} from './ruleset-manager.js';
|
||||||
|
|
||||||
import {
|
|
||||||
adminRead,
|
|
||||||
browser,
|
|
||||||
dnr,
|
|
||||||
localRead, localRemove, localWrite,
|
|
||||||
sessionRead, sessionWrite,
|
|
||||||
} from './ext.js';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
broadcastMessage,
|
broadcastMessage,
|
||||||
hostnamesFromMatches,
|
hostnamesFromMatches,
|
||||||
|
@ -39,6 +31,15 @@ import {
|
||||||
toBroaderHostname,
|
toBroaderHostname,
|
||||||
} from './utils.js';
|
} from './utils.js';
|
||||||
|
|
||||||
|
import {
|
||||||
|
browser,
|
||||||
|
dnr,
|
||||||
|
localRead, localWrite,
|
||||||
|
sessionRead, sessionWrite,
|
||||||
|
} from './ext.js';
|
||||||
|
|
||||||
|
import { adminReadEx } from './admin.js';
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
// 0: no filtering
|
// 0: no filtering
|
||||||
|
@ -224,38 +225,40 @@ function applyFilteringMode(filteringModes, hostname, afterLevel) {
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
async function readFilteringModeDetails() {
|
export async function readFilteringModeDetails(bypassCache = false) {
|
||||||
if ( readFilteringModeDetails.cache ) {
|
if ( bypassCache === false ) {
|
||||||
return readFilteringModeDetails.cache;
|
if ( readFilteringModeDetails.cache ) {
|
||||||
}
|
return readFilteringModeDetails.cache;
|
||||||
const sessionModes = await sessionRead('filteringModeDetails');
|
}
|
||||||
if ( sessionModes instanceof Object ) {
|
const sessionModes = await sessionRead('filteringModeDetails');
|
||||||
readFilteringModeDetails.cache = unserializeModeDetails(sessionModes);
|
if ( sessionModes instanceof Object ) {
|
||||||
return readFilteringModeDetails.cache;
|
readFilteringModeDetails.cache = unserializeModeDetails(sessionModes);
|
||||||
|
return readFilteringModeDetails.cache;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let [ userModes, adminNoFiltering ] = await Promise.all([
|
let [ userModes, adminNoFiltering ] = await Promise.all([
|
||||||
localRead('filteringModeDetails'),
|
localRead('filteringModeDetails'),
|
||||||
localRead('adminNoFiltering'),
|
adminReadEx('noFiltering'),
|
||||||
]);
|
]);
|
||||||
if ( userModes === undefined ) {
|
if ( userModes === undefined ) {
|
||||||
userModes = { basic: [ 'all-urls' ] };
|
userModes = { basic: [ 'all-urls' ] };
|
||||||
}
|
}
|
||||||
userModes = unserializeModeDetails(userModes);
|
userModes = unserializeModeDetails(userModes);
|
||||||
if ( Array.isArray(adminNoFiltering) ) {
|
if ( Array.isArray(adminNoFiltering) ) {
|
||||||
|
if ( adminNoFiltering.includes('-*') ) {
|
||||||
|
userModes.none.clear();
|
||||||
|
}
|
||||||
for ( const hn of adminNoFiltering ) {
|
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);
|
filteringModesToDNR(userModes);
|
||||||
sessionWrite('filteringModeDetails', serializeModeDetails(userModes));
|
sessionWrite('filteringModeDetails', serializeModeDetails(userModes));
|
||||||
readFilteringModeDetails.cache = userModes;
|
readFilteringModeDetails.cache = userModes;
|
||||||
adminRead('noFiltering').then(results => {
|
|
||||||
if ( results ) {
|
|
||||||
localWrite('adminNoFiltering', results);
|
|
||||||
} else {
|
|
||||||
localRemove('adminNoFiltering');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return userModes;
|
return userModes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,8 +19,14 @@
|
||||||
Home: https://github.com/gorhill/uBlock
|
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 { fetchJSON } from './fetch.js';
|
||||||
|
import { getAdminRulesets } from './admin.js';
|
||||||
import { ubolLog } from './debug.js';
|
import { ubolLog } from './debug.js';
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
@ -460,7 +466,22 @@ async function defaultRulesetsFromLanguage() {
|
||||||
|
|
||||||
async function enableRulesets(ids) {
|
async function enableRulesets(ids) {
|
||||||
const afterIds = new Set(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 enableRulesetSet = new Set();
|
||||||
const disableRulesetSet = new Set();
|
const disableRulesetSet = new Set();
|
||||||
for ( const id of afterIds ) {
|
for ( const id of afterIds ) {
|
||||||
|
@ -472,13 +493,8 @@ async function enableRulesets(ids) {
|
||||||
disableRulesetSet.add(id);
|
disableRulesetSet.add(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( enableRulesetSet.size === 0 && disableRulesetSet.size === 0 ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Be sure the rulesets to enable/disable do exist in the current version,
|
// Be sure the rulesets to enable/disable do exist in the current version,
|
||||||
// otherwise the API throws.
|
// otherwise the API throws.
|
||||||
const rulesetDetails = await getRulesetDetails();
|
|
||||||
for ( const id of enableRulesetSet ) {
|
for ( const id of enableRulesetSet ) {
|
||||||
if ( rulesetDetails.has(id) ) { continue; }
|
if ( rulesetDetails.has(id) ) { continue; }
|
||||||
enableRulesetSet.delete(id);
|
enableRulesetSet.delete(id);
|
||||||
|
@ -487,6 +503,11 @@ async function enableRulesets(ids) {
|
||||||
if ( rulesetDetails.has(id) ) { continue; }
|
if ( rulesetDetails.has(id) ) { continue; }
|
||||||
disableRulesetSet.delete(id);
|
disableRulesetSet.delete(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( enableRulesetSet.size === 0 && disableRulesetSet.size === 0 ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const enableRulesetIds = Array.from(enableRulesetSet);
|
const enableRulesetIds = Array.from(enableRulesetSet);
|
||||||
const disableRulesetIds = Array.from(disableRulesetSet);
|
const disableRulesetIds = Array.from(disableRulesetSet);
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,18 @@ function hashFromIterable(iter) {
|
||||||
return Array.from(iter).sort().join('\n');
|
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) {
|
function rulesetStats(rulesetId) {
|
||||||
|
@ -94,10 +106,13 @@ function renderFilterLists() {
|
||||||
li.title = listStatsTemplate
|
li.title = listStatsTemplate
|
||||||
.replace('{{ruleCount}}', renderNumber(stats.ruleCount))
|
.replace('{{ruleCount}}', renderNumber(stats.ruleCount))
|
||||||
.replace('{{filterCount}}', renderNumber(stats.filterCount));
|
.replace('{{filterCount}}', renderNumber(stats.filterCount));
|
||||||
|
const fromAdmin = isAdminRuleset(ruleset.id);
|
||||||
|
dom.cl.toggle(li, 'fromAdmin', fromAdmin);
|
||||||
|
const disabled = stats.ruleCount === 0 || fromAdmin;
|
||||||
dom.attr(
|
dom.attr(
|
||||||
qs$(li, '.input.checkbox'),
|
qs$(li, '.input.checkbox input'),
|
||||||
'disabled',
|
'disabled',
|
||||||
stats.ruleCount === 0 ? '' : null
|
disabled ? '' : null
|
||||||
);
|
);
|
||||||
dom.cl.remove(li, 'discard');
|
dom.cl.remove(li, 'discard');
|
||||||
return li;
|
return li;
|
||||||
|
@ -358,7 +373,9 @@ async function applyEnabledRulesets() {
|
||||||
const checked = qs$(liEntry, 'input[type="checkbox"]:checked') !== null;
|
const checked = qs$(liEntry, 'input[type="checkbox"]:checked') !== null;
|
||||||
dom.cl.toggle(liEntry, 'checked', checked);
|
dom.cl.toggle(liEntry, 'checked', checked);
|
||||||
if ( checked === false ) { continue; }
|
if ( checked === false ) { continue; }
|
||||||
enabledRulesets.push(liEntry.dataset.listkey);
|
const { listkey } = liEntry.dataset;
|
||||||
|
if ( isAdminRuleset(listkey) ) { continue; }
|
||||||
|
enabledRulesets.push(listkey);
|
||||||
}
|
}
|
||||||
|
|
||||||
await sendMessage({
|
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;
|
const message = ev.data;
|
||||||
if ( message instanceof Object === false ) { return; }
|
if ( message instanceof Object === false ) { return; }
|
||||||
const local = cachedRulesetData;
|
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 ( message.enabledRulesets !== undefined ) {
|
||||||
if ( hashFromIterable(message.enabledRulesets) !== hashFromIterable(local.enabledRulesets) ) {
|
if ( hashFromIterable(message.enabledRulesets) !== hashFromIterable(local.enabledRulesets) ) {
|
||||||
local.enabledRulesets = message.enabledRulesets;
|
local.enabledRulesets = message.enabledRulesets;
|
||||||
|
@ -503,6 +530,7 @@ sendMessage({
|
||||||
renderWidgets();
|
renderWidgets();
|
||||||
} catch(ex) {
|
} catch(ex) {
|
||||||
}
|
}
|
||||||
|
listen();
|
||||||
}).catch(reason => {
|
}).catch(reason => {
|
||||||
console.trace(reason);
|
console.trace(reason);
|
||||||
});
|
});
|
||||||
|
|
|
@ -115,7 +115,7 @@ const hostnamesFromMatches = origins => {
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
export const broadcastMessage = message => {
|
const broadcastMessage = message => {
|
||||||
const bc = new self.BroadcastChannel('uBOL');
|
const bc = new self.BroadcastChannel('uBOL');
|
||||||
bc.postMessage(message);
|
bc.postMessage(message);
|
||||||
};
|
};
|
||||||
|
@ -123,6 +123,7 @@ export const broadcastMessage = message => {
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
broadcastMessage,
|
||||||
parsedURLromOrigin,
|
parsedURLromOrigin,
|
||||||
toBroaderHostname,
|
toBroaderHostname,
|
||||||
isDescendantHostname,
|
isDescendantHostname,
|
||||||
|
|
|
@ -10,6 +10,12 @@
|
||||||
"disableFirstRunPage": {
|
"disableFirstRunPage": {
|
||||||
"title": "Disable first run page",
|
"title": "Disable first run page",
|
||||||
"type": "boolean"
|
"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