diff --git a/platform/mv3/extension/js/background.js b/platform/mv3/extension/js/background.js index d2ab2c38f..272ba4956 100644 --- a/platform/mv3/extension/js/background.js +++ b/platform/mv3/extension/js/background.js @@ -53,6 +53,10 @@ import { syncWithBrowserPermissions, } from './mode-manager.js'; +import { + ubolLog, +} from './utils.js'; + /******************************************************************************/ const rulesetConfig = { @@ -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; } @@ -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(); @@ -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 diff --git a/platform/mv3/extension/js/ruleset-manager.js b/platform/mv3/extension/js/ruleset-manager.js index e1d9cdca4..0888eee2a 100644 --- a/platform/mv3/extension/js/ruleset-manager.js +++ b/platform/mv3/extension/js/ruleset-manager.js @@ -27,6 +27,7 @@ import { browser, dnr, i18n } from './ext.js'; import { fetchJSON } from './fetch.js'; +import { ubolLog } from './utils.js'; /******************************************************************************/ @@ -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; @@ -142,7 +143,7 @@ async function updateRegexRules() { } } if ( rejectedRegexRules.length !== 0 ) { - console.info( + ubolLog( 'Rejected regexes:', rejectedRegexRules.map(rule => rule.condition.regexFilter) ); @@ -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 }); @@ -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 }); @@ -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 }); @@ -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 }); @@ -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 }); diff --git a/platform/mv3/extension/js/scripting-manager.js b/platform/mv3/extension/js/scripting-manager.js index f70758531..f60bb077c 100644 --- a/platform/mv3/extension/js/scripting-manager.js +++ b/platform/mv3/extension/js/scripting-manager.js @@ -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); }) diff --git a/platform/mv3/extension/js/scripting/css-declarative.js b/platform/mv3/extension/js/scripting/css-declarative.js index 71eca9b97..097d6fc7f 100644 --- a/platform/mv3/extension/js/scripting/css-declarative.js +++ b/platform/mv3/extension/js/scripting/css-declarative.js @@ -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')); /******************************************************************************/ diff --git a/platform/mv3/extension/js/scripting/css-generic.js b/platform/mv3/extension/js/scripting/css-generic.js index ad0169128..9da44f42b 100644 --- a/platform/mv3/extension/js/scripting/css-generic.js +++ b/platform/mv3/extension/js/scripting/css-generic.js @@ -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; }; /******************************************************************************/ @@ -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; }); }; @@ -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); + }); }; /******************************************************************************/ diff --git a/platform/mv3/extension/js/scripting/css-procedural.js b/platform/mv3/extension/js/scripting/css-procedural.js index b52cc209b..388f1c822 100644 --- a/platform/mv3/extension/js/scripting/css-procedural.js +++ b/platform/mv3/extension/js/scripting/css-procedural.js @@ -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 = { @@ -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; } diff --git a/platform/mv3/extension/js/scripting/css-specific.js b/platform/mv3/extension/js/scripting/css-specific.js index 0997385fb..40ad6dbb5 100644 --- a/platform/mv3/extension/js/scripting/css-specific.js +++ b/platform/mv3/extension/js/scripting/css-specific.js @@ -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;}`); /******************************************************************************/ diff --git a/platform/mv3/extension/js/utils.js b/platform/mv3/extension/js/utils.js index bf3fdcf87..b02341f84 100644 --- a/platform/mv3/extension/js/utils.js +++ b/platform/mv3/extension/js/utils.js @@ -25,6 +25,10 @@ /******************************************************************************/ +import { browser } from './ext.js'; + +/******************************************************************************/ + function parsedURLromOrigin(origin) { try { return new URL(origin); @@ -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, @@ -128,4 +140,5 @@ export { subtractHostnameIters, matchesFromHostnames, hostnamesFromMatches, + ubolLog, }; diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index 83c0fe294..3b4c4fad9 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -358,7 +358,6 @@ async function processNetworkFilters(assetDetails, network) { // Load all available scriptlets into a key-val map, where the key is the // scriptlet token, and val is the whole content of the file. -const scriptletDealiasingMap = new Map(); let scriptletsMapPromise; function loadAllSourceScriptlets() { @@ -367,28 +366,20 @@ function loadAllSourceScriptlets() { } scriptletsMapPromise = fs.readdir('./scriptlets').then(files => { - const reScriptletNameOrAlias = /^\/\/\/\s+(?:name|alias)\s+(\S+)/gm; + const readTemplateFile = file => + fs.readFile(`./scriptlets/${file}`, { encoding: 'utf8' }) + .then(text => ({ file, text })); const readPromises = []; for ( const file of files ) { - readPromises.push( - fs.readFile(`./scriptlets/${file}`, { encoding: 'utf8' }) - ); + readPromises.push(readTemplateFile(file)); } return Promise.all(readPromises).then(results => { const originalScriptletMap = new Map(); - for ( const text of results ) { - const aliasSet = new Set(); - for (;;) { - const match = reScriptletNameOrAlias.exec(text); - if ( match === null ) { break; } - aliasSet.add(match[1]); - } - if ( aliasSet.size === 0 ) { continue; } - const aliases = Array.from(aliasSet); - originalScriptletMap.set(aliases[0], text); - for ( let i = 0; i < aliases.length; i++ ) { - scriptletDealiasingMap.set(aliases[i], aliases[0]); - } + for ( const details of results ) { + originalScriptletMap.set( + details.file.replace('.template.js', ''), + details.text + ); } return originalScriptletMap; }); @@ -490,7 +481,7 @@ function groupHostnamesBySelectors(arrayin) { const out = Array.from(contentMap).map(a => [ a[0], { a: a[1].a, - y: a[1].y ? Array.from(a[1].y).sort(hnSort) : undefined, + y: a[1].y ? Array.from(a[1].y).sort(hnSort) : '*', n: a[1].n ? Array.from(a[1].n) : undefined, } ]).sort((a, b) => { @@ -564,7 +555,6 @@ async function processCosmeticFilters(assetDetails, mapin) { // The cosmetic filters will be injected programmatically as content // script and the decisions to activate the cosmetic filters will be // done at injection time according to the document's hostname. - const originalScriptletMap = await loadAllSourceScriptlets(); const generatedFiles = []; const argsMap = domainBasedEntries.map(entry => [ @@ -602,6 +592,7 @@ async function processCosmeticFilters(assetDetails, mapin) { argsList[i] = details.a; } + const originalScriptletMap = await loadAllSourceScriptlets(); const patchedScriptlet = originalScriptletMap.get('css-specific') .replace( '$rulesetId$', diff --git a/platform/mv3/make-scriptlets.js b/platform/mv3/make-scriptlets.js index 3955e324b..53a4435c6 100644 --- a/platform/mv3/make-scriptlets.js +++ b/platform/mv3/make-scriptlets.js @@ -164,7 +164,7 @@ export function compile(details, isTrusted) { export async function commit(rulesetId, path, writeFn) { const scriptletTemplate = await fs.readFile( - './scriptlet.template.js', + './scriptlets/scriptlet.template.js', { encoding: 'utf8' } ); const patchHnMap = hnmap => { diff --git a/platform/mv3/scriptlets/css-declarative.js b/platform/mv3/scriptlets/css-declarative.template.js similarity index 94% rename from platform/mv3/scriptlets/css-declarative.js rename to platform/mv3/scriptlets/css-declarative.template.js index 38eaad495..13415326a 100644 --- a/platform/mv3/scriptlets/css-declarative.js +++ b/platform/mv3/scriptlets/css-declarative.template.js @@ -27,10 +27,6 @@ /******************************************************************************/ -/// name css-declarative - -/******************************************************************************/ - // Important! // Isolate from global scope (function uBOL_cssDeclarativeImport() { diff --git a/platform/mv3/scriptlets/css-generic.js b/platform/mv3/scriptlets/css-generic.template.js similarity index 94% rename from platform/mv3/scriptlets/css-generic.js rename to platform/mv3/scriptlets/css-generic.template.js index 02010c5d6..4e9d93094 100644 --- a/platform/mv3/scriptlets/css-generic.js +++ b/platform/mv3/scriptlets/css-generic.template.js @@ -25,10 +25,6 @@ /******************************************************************************/ -/// name css-generic - -/******************************************************************************/ - // Important! // Isolate from global scope (function uBOL_cssGenericImport() { diff --git a/platform/mv3/scriptlets/css-procedural.js b/platform/mv3/scriptlets/css-procedural.template.js similarity index 94% rename from platform/mv3/scriptlets/css-procedural.js rename to platform/mv3/scriptlets/css-procedural.template.js index 81df35c2c..191d363b3 100644 --- a/platform/mv3/scriptlets/css-procedural.js +++ b/platform/mv3/scriptlets/css-procedural.template.js @@ -27,10 +27,6 @@ /******************************************************************************/ -/// name css-procedural - -/******************************************************************************/ - // Important! // Isolate from global scope (function uBOL_cssProceduralImport() { diff --git a/platform/mv3/scriptlets/css-specific.entity.js b/platform/mv3/scriptlets/css-specific.entity.js deleted file mode 100644 index e6f494f66..000000000 --- a/platform/mv3/scriptlets/css-specific.entity.js +++ /dev/null @@ -1,51 +0,0 @@ -/******************************************************************************* - - uBlock Origin - a browser extension to block requests. - Copyright (C) 2019-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'; - -/******************************************************************************/ - -/// name css-specific.entity - -/******************************************************************************/ - -// Important! -// Isolate from global scope -(function uBOL_cssSpecificEntityImport() { - -/******************************************************************************/ - -// $rulesetId$ - -const argsList = self.$argsList$; - -const entitiesMap = new Map(self.$entitiesMap$); - -self.specificEntityImports = self.specificEntityImports || []; -self.specificEntityImports.push({ argsList, entitiesMap }); - -/******************************************************************************/ - -})(); - -/******************************************************************************/ diff --git a/platform/mv3/scriptlets/css-specific.js b/platform/mv3/scriptlets/css-specific.template.js similarity index 94% rename from platform/mv3/scriptlets/css-specific.js rename to platform/mv3/scriptlets/css-specific.template.js index ffc871b77..81c318d06 100644 --- a/platform/mv3/scriptlets/css-specific.js +++ b/platform/mv3/scriptlets/css-specific.template.js @@ -27,10 +27,6 @@ /******************************************************************************/ -/// name css-specific - -/******************************************************************************/ - // Important! // Isolate from global scope (function uBOL_cssSpecificImports() { diff --git a/platform/mv3/scriptlet.template.js b/platform/mv3/scriptlets/scriptlet.template.js similarity index 100% rename from platform/mv3/scriptlet.template.js rename to platform/mv3/scriptlets/scriptlet.template.js