uBlock/platform/mv3/extension/js/background.js

258 lines
7.3 KiB
JavaScript

/*******************************************************************************
uBlock Origin - a browser extension to block requests.
Copyright (C) 2022-present Raymond Hill
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see {http://www.gnu.org/licenses/}.
Home: https://github.com/gorhill/uBlock
*/
/* jshint esversion:11 */
'use strict';
/******************************************************************************/
import {
browser,
dnr,
runtime,
} from './ext.js';
import {
CURRENT_CONFIG_BASE_RULE_ID,
getRulesetDetails,
getDynamicRules,
defaultRulesetsFromLanguage,
enableRulesets,
getEnabledRulesetsStats,
updateRegexRules,
} from './ruleset-manager.js';
import {
getInjectableCount,
registerInjectable,
} from './scripting-manager.js';
import {
matchesTrustedSiteDirective,
toggleTrustedSiteDirective,
} from './trusted-sites.js';
/******************************************************************************/
const rulesetConfig = {
version: '',
enabledRulesets: [],
};
/******************************************************************************/
function getCurrentVersion() {
return runtime.getManifest().version;
}
async function loadRulesetConfig() {
const dynamicRuleMap = await getDynamicRules();
const configRule = dynamicRuleMap.get(CURRENT_CONFIG_BASE_RULE_ID);
if ( configRule === undefined ) {
rulesetConfig.enabledRulesets = await defaultRulesetsFromLanguage();
return;
}
const match = /^\|\|example.invalid\/([^\/]+)\/(?:([^\/]+)\/)?/.exec(
configRule.condition.urlFilter
);
if ( match === null ) { return; }
rulesetConfig.version = match[1];
if ( match[2] ) {
rulesetConfig.enabledRulesets =
decodeURIComponent(match[2] || '').split(' ');
}
}
async function saveRulesetConfig() {
const dynamicRuleMap = await getDynamicRules();
let configRule = dynamicRuleMap.get(CURRENT_CONFIG_BASE_RULE_ID);
if ( configRule === undefined ) {
configRule = {
id: CURRENT_CONFIG_BASE_RULE_ID,
action: {
type: 'allow',
},
condition: {
urlFilter: '',
},
};
}
const version = rulesetConfig.version;
const enabledRulesets = encodeURIComponent(rulesetConfig.enabledRulesets.join(' '));
const urlFilter = `||example.invalid/${version}/${enabledRulesets}/`;
if ( urlFilter === configRule.condition.urlFilter ) { return; }
configRule.condition.urlFilter = urlFilter;
return dnr.updateDynamicRules({
addRules: [ configRule ],
removeRuleIds: [ CURRENT_CONFIG_BASE_RULE_ID ],
});
}
/******************************************************************************/
async function hasGreatPowers(origin) {
return browser.permissions.contains({
origins: [ `${origin}/*` ]
});
}
function grantGreatPowers(hostname) {
return browser.permissions.request({
origins: [
`*://${hostname}/*`,
]
});
}
function revokeGreatPowers(hostname) {
return browser.permissions.remove({
origins: [
`*://${hostname}/*`,
]
});
}
/******************************************************************************/
function onMessage(request, sender, callback) {
switch ( request.what ) {
case 'applyRulesets': {
enableRulesets(request.enabledRulesets).then(( ) => {
rulesetConfig.enabledRulesets = request.enabledRulesets;
return Promise.all([
saveRulesetConfig(),
registerInjectable(),
]);
}).then(( ) => {
callback();
});
return true;
}
case 'getRulesetData': {
Promise.all([
getRulesetDetails(),
dnr.getEnabledRulesets(),
]).then(results => {
const [ rulesetDetails, enabledRulesets ] = results;
callback({
enabledRulesets,
rulesetDetails: Array.from(rulesetDetails.values()),
});
});
return true;
}
case 'grantGreatPowers':
grantGreatPowers(request.hostname).then(granted => {
console.info(`Granted uBOL great powers on ${request.hostname}: ${granted}`);
callback(granted);
});
return true;
case 'popupPanelData': {
Promise.all([
matchesTrustedSiteDirective(request),
hasGreatPowers(request.origin),
getEnabledRulesetsStats(),
getInjectableCount(request.origin),
]).then(results => {
callback({
isTrusted: results[0],
hasGreatPowers: results[1],
rulesetDetails: results[2],
injectableCount: results[3],
});
});
return true;
}
case 'revokeGreatPowers':
revokeGreatPowers(request.hostname).then(removed => {
console.info(`Revoked great powers from uBOL on ${request.hostname}: ${removed}`);
callback(removed);
});
return true;
case 'toggleTrustedSiteDirective': {
toggleTrustedSiteDirective(request).then(response => {
registerInjectable().then(( ) => {
callback(response);
});
});
return true;
}
default:
break;
}
}
async function onPermissionsChanged() {
await registerInjectable();
}
/******************************************************************************/
async function start() {
await loadRulesetConfig();
await enableRulesets(rulesetConfig.enabledRulesets);
// We need to update the regex rules only when ruleset version changes.
const currentVersion = getCurrentVersion();
if ( currentVersion !== rulesetConfig.version ) {
console.log(`Version change: ${rulesetConfig.version} => ${currentVersion}`);
updateRegexRules().then(( ) => {
rulesetConfig.version = currentVersion;
saveRulesetConfig();
});
}
// 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.
registerInjectable();
const enabledRulesets = await dnr.getEnabledRulesets();
console.log(`Enabled rulesets: ${enabledRulesets}`);
dnr.getAvailableStaticRuleCount().then(count => {
console.log(`Available static rule count: ${count}`);
});
dnr.setExtensionActionOptions({ displayActionCountAsBadgeText: true });
}
(async ( ) => {
await start();
runtime.onMessage.addListener(onMessage);
browser.permissions.onAdded.addListener(onPermissionsChanged);
browser.permissions.onRemoved.addListener(onPermissionsChanged);
})();