Skip to content

Commit

Permalink
[mv3] Add support for admin configurations
Browse files Browse the repository at this point in the history
See `managed_storage.json` for available settings. Currently
only `noFiltering` setting is availale.

`noFiltering` is an array of strings, each being a domain for
which no filtering should occur.

Related discussion:
- uBlockOrigin/uBOL-home#35
  • Loading branch information
gorhill committed Jun 13, 2023
1 parent 0541ddb commit d19e62a
Show file tree
Hide file tree
Showing 9 changed files with 349 additions and 362 deletions.
3 changes: 3 additions & 0 deletions platform/mv3/chromium/manifest.json
Expand Up @@ -38,6 +38,9 @@
"storage"
],
"short_name": "uBO Lite",
"storage": {
"managed_schema": "managed_storage.json"
},
"version": "1.0",
"web_accessible_resources": []
}
91 changes: 34 additions & 57 deletions platform/mv3/extension/js/background.js
Expand Up @@ -34,9 +34,7 @@ import {
} from './ext.js';

import {
CURRENT_CONFIG_BASE_RULE_ID,
getRulesetDetails,
getDynamicRules,
defaultRulesetsFromLanguage,
enableRulesets,
getEnabledRulesetsDetails,
Expand Down Expand Up @@ -70,6 +68,7 @@ const rulesetConfig = {
const UBOL_ORIGIN = runtime.getURL('').replace(/\/$/, '');

let firstRun = false;
let wakeupRun = false;

/******************************************************************************/

Expand All @@ -83,53 +82,23 @@ async function loadRulesetConfig() {
rulesetConfig.version = data.version;
rulesetConfig.enabledRulesets = data.enabledRulesets;
rulesetConfig.autoReload = data.autoReload;
return false;
wakeupRun = true;
return;
}
data = await localRead('rulesetConfig');
if ( data ) {
rulesetConfig.version = data.version;
rulesetConfig.enabledRulesets = data.enabledRulesets;
rulesetConfig.autoReload = data.autoReload;
return false;
}
data = await loadRulesetConfig.convertLegacyStorage();
if ( data ) {
rulesetConfig.version = data.version;
rulesetConfig.enabledRulesets = data.enabledRulesets;
rulesetConfig.autoReload = data.autoReload;
return false;
sessionWrite('rulesetConfig', rulesetConfig);
return;
}
rulesetConfig.enabledRulesets = await defaultRulesetsFromLanguage();
sessionWrite('rulesetConfig', rulesetConfig);
localWrite('rulesetConfig', rulesetConfig);
return true;
firstRun = true;
}

// TODO: To remove after next stable release is widespread (2023-06-04)
loadRulesetConfig.convertLegacyStorage = async function() {
const dynamicRuleMap = await getDynamicRules();
const configRule = dynamicRuleMap.get(CURRENT_CONFIG_BASE_RULE_ID);
if ( configRule === undefined ) { return; }
let rawConfig;
try {
rawConfig = JSON.parse(self.atob(configRule.condition.urlFilter));
} catch(ex) {
return;
}
if ( rawConfig === undefined ) { return; }
const config = {
version: rawConfig[0],
enabledRulesets: rawConfig[1],
autoReload: rawConfig[2],
};
localWrite('rulesetConfig', config);
sessionWrite('rulesetConfig', config);
dnr.updateDynamicRules({
removeRuleIds: [ CURRENT_CONFIG_BASE_RULE_ID ],
});
return config;
};

async function saveRulesetConfig() {
sessionWrite('rulesetConfig', rulesetConfig);
return localWrite('rulesetConfig', rulesetConfig);
Expand All @@ -153,12 +122,13 @@ function hasOmnipotence() {
async function onPermissionsRemoved() {
const beforeMode = await getDefaultFilteringMode();
const modified = await syncWithBrowserPermissions();
if ( modified === false ) { return; }
if ( modified === false ) { return false; }
const afterMode = await getDefaultFilteringMode();
if ( beforeMode > 1 && afterMode <= 1 ) {
updateDynamicRules();
}
registerInjectables();
return true;
}

/******************************************************************************/
Expand All @@ -180,6 +150,7 @@ function onMessage(request, sender, callback) {
}).catch(reason => {
console.log(reason);
});
callback();
return;
}

Expand Down Expand Up @@ -273,8 +244,7 @@ function onMessage(request, sender, callback) {
}

case 'setDefaultFilteringMode': {
getDefaultFilteringMode(
).then(beforeLevel =>
getDefaultFilteringMode().then(beforeLevel =>
setDefaultFilteringMode(request.level).then(afterLevel =>
({ beforeLevel, afterLevel })
)
Expand All @@ -298,37 +268,44 @@ function onMessage(request, sender, callback) {
/******************************************************************************/

async function start() {
firstRun = await loadRulesetConfig();
await enableRulesets(rulesetConfig.enabledRulesets);
await loadRulesetConfig();

if ( wakeupRun === false ) {
await enableRulesets(rulesetConfig.enabledRulesets);
}

// We need to update the regex rules only when ruleset version changes.
const currentVersion = getCurrentVersion();
if ( currentVersion !== rulesetConfig.version ) {
ubolLog(`Version change: ${rulesetConfig.version} => ${currentVersion}`);
updateDynamicRules().then(( ) => {
rulesetConfig.version = currentVersion;
saveRulesetConfig();
});
if ( wakeupRun === false ) {
const currentVersion = getCurrentVersion();
if ( currentVersion !== rulesetConfig.version ) {
ubolLog(`Version change: ${rulesetConfig.version} => ${currentVersion}`);
updateDynamicRules().then(( ) => {
rulesetConfig.version = currentVersion;
saveRulesetConfig();
});
}
}

// Permissions may have been removed while the extension was disabled
await onPermissionsRemoved();
const permissionsChanged = await onPermissionsRemoved();

// 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.
registerInjectables();
if ( wakeupRun === false || permissionsChanged ) {
registerInjectables();

const enabledRulesets = await dnr.getEnabledRulesets();
ubolLog(`Enabled rulesets: ${enabledRulesets}`);
const enabledRulesets = await dnr.getEnabledRulesets();
ubolLog(`Enabled rulesets: ${enabledRulesets}`);

dnr.getAvailableStaticRuleCount().then(count => {
ubolLog(`Available static rule count: ${count}`);
});
dnr.getAvailableStaticRuleCount().then(count => {
ubolLog(`Available static rule count: ${count}`);
});
}

// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/declarativeNetRequest
// Firefox API does not support `dnr.setExtensionActionOptions`
if ( dnr.setExtensionActionOptions ) {
if ( wakeupRun === false && dnr.setExtensionActionOptions ) {
dnr.setExtensionActionOptions({ displayActionCountAsBadgeText: true });
}

Expand Down
49 changes: 29 additions & 20 deletions platform/mv3/extension/js/ext.js
Expand Up @@ -25,22 +25,22 @@

/******************************************************************************/

const browser =
export const browser =
self.browser instanceof Object &&
self.browser instanceof Element === false
? self.browser
: self.chrome;

const dnr = browser.declarativeNetRequest;
const i18n = browser.i18n;
const runtime = browser.runtime;
export const dnr = browser.declarativeNetRequest;
export const i18n = browser.i18n;
export const runtime = browser.runtime;

/******************************************************************************/

// The extension's service worker can be evicted at any time, so when we
// send a message, we try a few more times when the message fails to be sent.

function sendMessage(msg) {
export function sendMessage(msg) {
return new Promise((resolve, reject) => {
let i = 5;
const send = ( ) => {
Expand All @@ -61,50 +61,59 @@ function sendMessage(msg) {

/******************************************************************************/

async function localRead(key) {
export async function localRead(key) {
if ( browser.storage instanceof Object === false ) { return; }
if ( browser.storage.local instanceof Object === false ) { return; }
try {
const bin = await browser.storage.local.get(key);
if ( bin instanceof Object === false ) { return; }
return bin[key];
return bin[key] ?? undefined;
} catch(ex) {
}
}

async function localWrite(key, value) {
export async function localWrite(key, value) {
if ( browser.storage instanceof Object === false ) { return; }
if ( browser.storage.local instanceof Object === false ) { return; }
return browser.storage.local.set({ [key]: value });
}

export async function localRemove(key) {
if ( browser.storage instanceof Object === false ) { return; }
if ( browser.storage.local instanceof Object === false ) { return; }
return browser.storage.local.remove(key);
}

/******************************************************************************/

async function sessionRead(key) {
export async function sessionRead(key) {
if ( browser.storage instanceof Object === false ) { return; }
if ( browser.storage.session instanceof Object === false ) { return; }
try {
const bin = await browser.storage.session.get(key);
if ( bin instanceof Object === false ) { return; }
return bin[key];
return bin[key] ?? undefined;
} catch(ex) {
}
}

async function sessionWrite(key, value) {
export async function sessionWrite(key, value) {
if ( browser.storage instanceof Object === false ) { return; }
if ( browser.storage.session instanceof Object === false ) { return; }
return browser.storage.session.set({ [key]: value });
}

/******************************************************************************/

export {
browser,
dnr,
i18n,
runtime,
sendMessage,
localRead, localWrite,
sessionRead, sessionWrite,
};
export async function adminRead(key) {
if ( browser.storage instanceof Object === false ) { return; }
if ( browser.storage.local instanceof Object === false ) { return; }
try {
const bin = await browser.storage.managed.get(key);
if ( bin instanceof Object === false ) { return; }
return bin[key] ?? undefined;
} catch(ex) {
}
}

/******************************************************************************/

0 comments on commit d19e62a

Please sign in to comment.