Skip to content

Commit

Permalink
[mv3] Mitigation: Inject CSS user styles to enforce cosmetic filtering
Browse files Browse the repository at this point in the history
Related issues:
- uBlockOrigin/uBOL-home#5 (comment)
- w3c/webextensions#403

Currently, there is no other way to inject CSS user styles than to
wake up the service worker, so that it can inject the CSS styles
itself using the `scripting.insertCSS()` method.

If ever the MV3 API supports injecting CSS user styles directly
from a content script, uBOL will be back to be fully declarative.

At this point the service worker is very lightweight since the
filtering is completely  declarative, so this is not too much of
an issue performance-wise except for the fact that waking up the
service worker for the sole purpose of injecting CSS user styles
and nothing else introduces a pointless overhead.

Hopefully the MV3 API will mature to address such inefficiency.
  • Loading branch information
gorhill committed Jun 4, 2023
1 parent e16bd1b commit 6dbbb95
Show file tree
Hide file tree
Showing 16 changed files with 101 additions and 151 deletions.
32 changes: 29 additions & 3 deletions platform/mv3/extension/js/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ import {
syncWithBrowserPermissions,
} from './mode-manager.js';

import {
ubolLog,
} from './utils.js';

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

const rulesetConfig = {
Expand Down Expand Up @@ -169,6 +173,28 @@ async function onPermissionsRemoved() {

function onMessage(request, sender, callback) {

// Does not require trusted origin.

switch ( request.what ) {

case 'insertCSS': {
const tabId = sender?.tab?.id ?? false;
const frameId = sender?.frameId ?? false;
if ( tabId === false || frameId === false ) { return; }
browser.scripting.insertCSS({
css: request.css,
origin: 'USER',
target: { tabId, frameIds: [ frameId ] },
});
return;
}

default:
break;
}

// Does requires trusted origin.

// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/MessageSender
// Firefox API does not set `sender.origin`
if ( sender.origin !== undefined && sender.origin !== UBOL_ORIGIN ) { return; }
Expand Down Expand Up @@ -284,7 +310,7 @@ async function start() {
// 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}`);
ubolLog(`Version change: ${rulesetConfig.version} => ${currentVersion}`);
updateDynamicRules().then(( ) => {
rulesetConfig.version = currentVersion;
saveRulesetConfig();
Expand All @@ -300,10 +326,10 @@ async function start() {
registerInjectables();

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

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

// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/declarativeNetRequest
Expand Down
27 changes: 14 additions & 13 deletions platform/mv3/extension/js/ruleset-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

import { browser, dnr, i18n } from './ext.js';
import { fetchJSON } from './fetch.js';
import { ubolLog } from './utils.js';

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

Expand Down Expand Up @@ -72,8 +73,8 @@ function getDynamicRules() {
const map = new Map(
rules.map(rule => [ rule.id, rule ])
);
console.info(`Dynamic rule count: ${map.size}`);
console.info(`Available dynamic rule count: ${dnr.MAX_NUMBER_OF_DYNAMIC_AND_SESSION_RULES - map.size}`);
ubolLog(`Dynamic rule count: ${map.size}`);
ubolLog(`Available dynamic rule count: ${dnr.MAX_NUMBER_OF_DYNAMIC_AND_SESSION_RULES - map.size}`);
return map;
});
return dynamicRuleMapPromise;
Expand Down Expand Up @@ -142,7 +143,7 @@ async function updateRegexRules() {
}
}
if ( rejectedRegexRules.length !== 0 ) {
console.info(
ubolLog(
'Rejected regexes:',
rejectedRegexRules.map(rule => rule.condition.regexFilter)
);
Expand Down Expand Up @@ -178,10 +179,10 @@ async function updateRegexRules() {
if ( addRules.length === 0 && removeRuleIds.length === 0 ) { return; }

if ( removeRuleIds.length !== 0 ) {
console.info(`Remove ${removeRuleIds.length} DNR regex rules`);
ubolLog(`Remove ${removeRuleIds.length} DNR regex rules`);
}
if ( addRules.length !== 0 ) {
console.info(`Add ${addRules.length} DNR regex rules`);
ubolLog(`Add ${addRules.length} DNR regex rules`);
}

return dnr.updateDynamicRules({ addRules, removeRuleIds });
Expand Down Expand Up @@ -250,10 +251,10 @@ async function updateRemoveparamRules() {
if ( addRules.length === 0 && removeRuleIds.length === 0 ) { return; }

if ( removeRuleIds.length !== 0 ) {
console.info(`Remove ${removeRuleIds.length} DNR removeparam rules`);
ubolLog(`Remove ${removeRuleIds.length} DNR removeparam rules`);
}
if ( addRules.length !== 0 ) {
console.info(`Add ${addRules.length} DNR removeparam rules`);
ubolLog(`Add ${addRules.length} DNR removeparam rules`);
}

return dnr.updateDynamicRules({ addRules, removeRuleIds });
Expand Down Expand Up @@ -322,10 +323,10 @@ async function updateRedirectRules() {
if ( addRules.length === 0 && removeRuleIds.length === 0 ) { return; }

if ( removeRuleIds.length !== 0 ) {
console.info(`Remove ${removeRuleIds.length} DNR redirect rules`);
ubolLog(`Remove ${removeRuleIds.length} DNR redirect rules`);
}
if ( addRules.length !== 0 ) {
console.info(`Add ${addRules.length} DNR redirect rules`);
ubolLog(`Add ${addRules.length} DNR redirect rules`);
}

return dnr.updateDynamicRules({ addRules, removeRuleIds });
Expand Down Expand Up @@ -394,10 +395,10 @@ async function updateCspRules() {
if ( addRules.length === 0 && removeRuleIds.length === 0 ) { return; }

if ( removeRuleIds.length !== 0 ) {
console.info(`Remove ${removeRuleIds.length} DNR csp rules`);
ubolLog(`Remove ${removeRuleIds.length} DNR csp rules`);
}
if ( addRules.length !== 0 ) {
console.info(`Add ${addRules.length} DNR csp rules`);
ubolLog(`Add ${addRules.length} DNR csp rules`);
}

return dnr.updateDynamicRules({ addRules, removeRuleIds });
Expand Down Expand Up @@ -482,10 +483,10 @@ async function enableRulesets(ids) {
const disableRulesetIds = Array.from(disableRulesetSet);

if ( enableRulesetIds.length !== 0 ) {
console.info(`Enable rulesets: ${enableRulesetIds}`);
ubolLog(`Enable rulesets: ${enableRulesetIds}`);
}
if ( disableRulesetIds.length !== 0 ) {
console.info(`Disable ruleset: ${disableRulesetIds}`);
ubolLog(`Disable ruleset: ${disableRulesetIds}`);
}
await dnr.updateEnabledRulesets({ enableRulesetIds, disableRulesetIds });

Expand Down
6 changes: 3 additions & 3 deletions platform/mv3/extension/js/scripting-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -472,21 +472,21 @@ async function registerInjectables(origins) {
toRemove.push(...Array.from(before.keys()));

if ( toRemove.length !== 0 ) {
console.info(`Unregistered ${toRemove} content (css/js)`);
ut.ubolLog(`Unregistered ${toRemove} content (css/js)`);
promises.push(
browser.scripting.unregisterContentScripts({ ids: toRemove })
.catch(reason => { console.info(reason); })
);
}
if ( toAdd.length !== 0 ) {
console.info(`Registered ${toAdd.map(v => v.id)} content (css/js)`);
ut.ubolLog(`Registered ${toAdd.map(v => v.id)} content (css/js)`);
promises.push(
browser.scripting.registerContentScripts(toAdd)
.catch(reason => { console.info(reason); })
);
}
if ( toUpdate.length !== 0 ) {
console.info(`Updated ${toUpdate.map(v => v.id)} content (css/js)`);
ut.ubolLog(`Updated ${toUpdate.map(v => v.id)} content (css/js)`);
promises.push(
browser.scripting.updateContentScripts(toUpdate)
.catch(reason => { console.info(reason); })
Expand Down
16 changes: 7 additions & 9 deletions platform/mv3/extension/js/scripting/css-declarative.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,15 +135,13 @@ for ( const selector of selectors ) {

if ( sheetText.length === 0 ) { return; }

try {
const sheet = new CSSStyleSheet();
sheet.replace(`@layer{${sheetText.join('\n')}}`);
document.adoptedStyleSheets = [
...document.adoptedStyleSheets,
sheet
];
} catch(ex) {
}
(function uBOL_injectCSS(css, count = 10) {
chrome.runtime.sendMessage({ what: 'insertCSS', css }).catch(( ) => {
count -= 1;
if ( count === 0 ) { return; }
uBOL_injectCSS(css, count - 1);
});
})(sheetText.join('\n'));

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

Expand Down
22 changes: 9 additions & 13 deletions platform/mv3/extension/js/scripting/css-generic.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ const hashFromStr = (type, s) => {
for ( let i = 0; i < len; i += step ) {
hash = (hash << 5) + hash ^ s.charCodeAt(i);
}
return hash & 0xFF_FFFF;
return hash & 0xFFFFFF;
};

/******************************************************************************/
Expand Down Expand Up @@ -162,7 +162,8 @@ const uBOL_processNodes = ( ) => {
if ( styleSheetTimer !== undefined ) { return; }
styleSheetTimer = self.requestAnimationFrame(( ) => {
styleSheetTimer = undefined;
uBOL_injectStyleSheet();
uBOL_injectCSS(`${styleSheetSelectors.join(',')}{display:none!important;}`);
styleSheetSelectors.length = 0;
});
};

Expand All @@ -187,17 +188,12 @@ const uBOL_processChanges = mutations => {

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

const uBOL_injectStyleSheet = ( ) => {
try {
const sheet = new CSSStyleSheet();
sheet.replace(`@layer{${styleSheetSelectors.join(',')}{display:none!important;}}`);
document.adoptedStyleSheets = [
...document.adoptedStyleSheets,
sheet
];
} catch(ex) {
}
styleSheetSelectors.length = 0;
const uBOL_injectCSS = (css, count = 10) => {
chrome.runtime.sendMessage({ what: 'insertCSS', css }).catch(( ) => {
count -= 1;
if ( count === 0 ) { return; }
uBOL_injectCSS(css, count - 1);
});
};

/******************************************************************************/
Expand Down
20 changes: 7 additions & 13 deletions platform/mv3/extension/js/scripting/css-procedural.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,16 +103,12 @@ if ( selectors.length === 0 ) { return; }

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

const addStylesheet = text => {
try {
const sheet = new CSSStyleSheet();
sheet.replace(`@layer{${text}}`);
document.adoptedStyleSheets = [
...document.adoptedStyleSheets,
sheet
];
} catch(ex) {
}
const uBOL_injectCSS = (css, count = 10) => {
chrome.runtime.sendMessage({ what: 'insertCSS', css }).catch(( ) => {
count -= 1;
if ( count === 0 ) { return; }
uBOL_injectCSS(css, count - 1);
});
};

const nonVisualElements = {
Expand Down Expand Up @@ -641,9 +637,7 @@ class ProceduralFilterer {
if ( styleToken !== undefined ) { return styleToken; }
styleToken = this.randomToken();
this.styleTokenMap.set(style, styleToken);
addStylesheet(
`[${this.masterToken}][${styleToken}]\n{${style}}\n`,
);
uBOL_injectCSS(`[${this.masterToken}][${styleToken}]\n{${style}}\n`);
return styleToken;
}

Expand Down
16 changes: 7 additions & 9 deletions platform/mv3/extension/js/scripting/css-specific.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,15 +103,13 @@ if ( selectors.length === 0 ) { return; }

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

try {
const sheet = new CSSStyleSheet();
sheet.replace(`@layer{${selectors.join(',')}{display:none!important;}}`);
document.adoptedStyleSheets = [
...document.adoptedStyleSheets,
sheet
];
} catch(ex) {
}
(function uBOL_injectCSS(css, count = 10) {
chrome.runtime.sendMessage({ what: 'insertCSS', css }).catch(( ) => {
count -= 1;
if ( count === 0 ) { return; }
uBOL_injectCSS(css, count - 1);
});
})(`${selectors.join(',')}{display:none!important;}`);

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

Expand Down
13 changes: 13 additions & 0 deletions platform/mv3/extension/js/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@

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

import { browser } from './ext.js';

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

function parsedURLromOrigin(origin) {
try {
return new URL(origin);
Expand Down Expand Up @@ -119,6 +123,14 @@ const hostnamesFromMatches = origins => {

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

const ubolLog = (...args) => {
// Do not pollute dev console in stable release.
if ( browser.runtime.id === 'ddkjiahejlhfcafbddmgiahcphecmpfh' ) { return; }
console.info(...args);
};

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

export {
parsedURLromOrigin,
toBroaderHostname,
Expand All @@ -128,4 +140,5 @@ export {
subtractHostnameIters,
matchesFromHostnames,
hostnamesFromMatches,
ubolLog,
};
Loading

0 comments on commit 6dbbb95

Please sign in to comment.