diff --git a/platform/mv3/extension/js/scripting-manager.js b/platform/mv3/extension/js/scripting-manager.js index 17bd5ac8d..c7c9773d6 100644 --- a/platform/mv3/extension/js/scripting-manager.js +++ b/platform/mv3/extension/js/scripting-manager.js @@ -354,70 +354,6 @@ function registerProcedural(context) { /******************************************************************************/ -function registerDeclarative(context) { - const { before, filteringModeDetails, rulesetsDetails } = context; - - const js = []; - for ( const rulesetDetails of rulesetsDetails ) { - const count = rulesetDetails.css?.declarative || 0; - if ( count === 0 ) { continue; } - js.push(`/rulesets/scripting/declarative/${rulesetDetails.id}.js`); - } - if ( js.length === 0 ) { return; } - - const { none, basic, optimal, complete } = filteringModeDetails; - const matches = [ - ...ut.matchesFromHostnames(optimal), - ...ut.matchesFromHostnames(complete), - ]; - if ( matches.length === 0 ) { return; } - - normalizeMatches(matches); - - js.unshift('/js/scripting/isolated-api.js'); - js.push('/js/scripting/css-declarative.js'); - - const excludeMatches = []; - if ( none.has('all-urls') === false ) { - excludeMatches.push(...ut.matchesFromHostnames(none)); - } - if ( basic.has('all-urls') === false ) { - excludeMatches.push(...ut.matchesFromHostnames(basic)); - } - - const registered = before.get('css-declarative'); - before.delete('css-declarative'); // Important! - - const directive = { - id: 'css-declarative', - js, - matches, - allFrames: true, - runAt: 'document_start', - }; - if ( excludeMatches.length !== 0 ) { - directive.excludeMatches = excludeMatches; - } - - // register - if ( registered === undefined ) { - context.toAdd.push(directive); - return; - } - - // update - if ( - ut.strArrayEq(registered.js, js, false) === false || - ut.strArrayEq(registered.matches, matches) === false || - ut.strArrayEq(registered.excludeMatches, excludeMatches) === false - ) { - context.toRemove.push('css-declarative'); - context.toAdd.push(directive); - } -} - -/******************************************************************************/ - function registerSpecific(context) { const { before, filteringModeDetails, rulesetsDetails } = context; @@ -602,7 +538,6 @@ async function registerInjectables() { }; await Promise.all([ - registerDeclarative(context), registerProcedural(context), registerScriptlet(context, scriptletDetails), registerSpecific(context), diff --git a/platform/mv3/extension/js/scripting/css-declarative.js b/platform/mv3/extension/js/scripting/css-declarative.js deleted file mode 100644 index a0364ce20..000000000 --- a/platform/mv3/extension/js/scripting/css-declarative.js +++ /dev/null @@ -1,116 +0,0 @@ -/******************************************************************************* - - uBlock Origin Lite - a comprehensive, MV3-compliant content blocker - Copyright (C) 2014-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 -*/ - -// Important! -// Isolate from global scope -(function uBOL_cssDeclarative() { - -/******************************************************************************/ - -const declarativeImports = self.declarativeImports || []; -self.declarativeImports = undefined; - -/******************************************************************************/ - -const selectors = []; -const exceptions = []; - -const lookupHostname = (hostname, details, out) => { - let seqi = details.hostnamesMap.get(hostname); - if ( seqi === undefined ) { return; } - const { argsList, argsSeqs } = details; - for (;;) { - const argi = argsSeqs[seqi++]; - const done = argi > 0; - out.push(...argsList[done ? argi : -argi].split('\n')); - if ( done ) { break; } - } -}; - -const lookupAll = hostname => { - for ( const details of declarativeImports ) { - lookupHostname(hostname, details, selectors); - lookupHostname(`~${hostname}`, details, exceptions); - } -}; - -self.isolatedAPI.forEachHostname(lookupAll, { - hasEntities: declarativeImports.some(a => a.hasEntities) -}); - -declarativeImports.length = 0; - -const exceptedSelectors = exceptions.length !== 0 - ? selectors.filter(a => exceptions.includes(a) === false) - : selectors; -if ( exceptedSelectors.length === 0 ) { return; } - -/******************************************************************************/ - -const cssRuleFromProcedural = details => { - const { tasks, action } = details; - let mq, selector; - if ( Array.isArray(tasks) ) { - if ( tasks[0][0] !== 'matches-media' ) { return; } - mq = tasks[0][1]; - if ( tasks.length > 2 ) { return; } - if ( tasks.length === 2 ) { - if ( tasks[1][0] !== 'spath' ) { return; } - selector = tasks[1][1]; - } - } - let style; - if ( Array.isArray(action) ) { - if ( action[0] !== 'style' ) { return; } - selector = selector || details.selector; - style = action[1]; - } - if ( mq === undefined && style === undefined && selector === undefined ) { return; } - if ( mq === undefined ) { - return `${selector}\n{${style}}`; - } - if ( style === undefined ) { - return `@media ${mq} {\n${selector}\n{display:none!important;}\n}`; - } - return `@media ${mq} {\n${selector}\n{${style}}\n}`; -}; - -const sheetText = []; -for ( const selector of exceptedSelectors ) { - const details = JSON.parse(selector); - const ruleText = cssRuleFromProcedural(details); - if ( ruleText === undefined ) { continue; } - sheetText.push(ruleText); -} - -if ( sheetText.length === 0 ) { return; } - -chrome.runtime.sendMessage({ - what: 'insertCSS', - css: sheetText.join('\n'), -}).catch(( ) => { -}); - -/******************************************************************************/ - -})(); - -void 0; diff --git a/platform/mv3/extension/js/scripting/css-procedural-api.js b/platform/mv3/extension/js/scripting/css-procedural-api.js index 1446cdf7c..5559b2b1a 100644 --- a/platform/mv3/extension/js/scripting/css-procedural-api.js +++ b/platform/mv3/extension/js/scripting/css-procedural-api.js @@ -577,8 +577,7 @@ class ProceduralFilterer { } addSelectors(selectors) { - for ( const json of selectors ) { - const selector = JSON.parse(json); + for ( const selector of selectors ) { const pselector = new PSelectorRoot(selector); this.primeProceduralSelector(pselector); this.selectors.push(pselector); diff --git a/platform/mv3/extension/js/scripting/css-procedural.js b/platform/mv3/extension/js/scripting/css-procedural.js index f3d006089..376d305e1 100644 --- a/platform/mv3/extension/js/scripting/css-procedural.js +++ b/platform/mv3/extension/js/scripting/css-procedural.js @@ -21,7 +21,7 @@ // Important! // Isolate from global scope -(async function uBOL_cssProcedural() { +(function uBOL_cssProcedural() { /******************************************************************************/ @@ -40,7 +40,7 @@ const lookupHostname = (hostname, details, out) => { for (;;) { const argi = argsSeqs[seqi++]; const done = argi > 0; - out.push(...argsList[done ? argi : -argi]); + out.push(...JSON.parse(argsList[done ? argi : -argi])); if ( done ) { break; } } }; @@ -48,38 +48,88 @@ const lookupHostname = (hostname, details, out) => { const lookupAll = hostname => { for ( const details of proceduralImports ) { lookupHostname(hostname, details, selectors); - lookupHostname(`~${hostname}`, details, exceptions); + const matches = []; + lookupHostname(`~${hostname}`, details, matches); + if ( matches.length === 0 ) { continue; } + exceptions.push(...matches.map(a => JSON.stringify(a))); } }; self.isolatedAPI.forEachHostname(lookupAll, { hasEntities: proceduralImports.some(a => a.hasEntities) }); - proceduralImports.length = 0; const exceptedSelectors = exceptions.length !== 0 - ? selectors.filter(a => exceptions.includes(a) === false) + ? selectors.filter(a => exceptions.includes(JSON.stringify(a)) === false) : selectors; if ( exceptedSelectors.length === 0 ) { return; } -if ( self.cssProceduralAPI === undefined ) { - self.cssProceduralAPI = chrome.runtime.sendMessage({ - what: 'injectCSSProceduralAPI' - }).catch(( ) => { - }); +const declaratives = exceptedSelectors.filter(a => a.cssable); +if ( declaratives.length !== 0 ) { + const cssRuleFromProcedural = details => { + const { tasks, action } = details; + let mq, selector; + if ( Array.isArray(tasks) ) { + if ( tasks[0][0] !== 'matches-media' ) { return; } + mq = tasks[0][1]; + if ( tasks.length > 2 ) { return; } + if ( tasks.length === 2 ) { + if ( tasks[1][0] !== 'spath' ) { return; } + selector = tasks[1][1]; + } + } + let style; + if ( Array.isArray(action) ) { + if ( action[0] !== 'style' ) { return; } + selector = selector || details.selector; + style = action[1]; + } + if ( mq === undefined && style === undefined && selector === undefined ) { return; } + if ( mq === undefined ) { + return `${selector}\n{${style}}`; + } + if ( style === undefined ) { + return `@media ${mq} {\n${selector}\n{display:none!important;}\n}`; + } + return `@media ${mq} {\n${selector}\n{${style}}\n}`; + }; + const sheetText = []; + for ( const details of declaratives ) { + const ruleText = cssRuleFromProcedural(details); + if ( ruleText === undefined ) { continue; } + sheetText.push(ruleText); + } + if ( sheetText.length !== 0 ) { + chrome.runtime.sendMessage({ + what: 'insertCSS', + css: sheetText.join('\n'), + }).catch(( ) => { + }); + } } -if ( self.cssProceduralAPI instanceof Promise ) { - await self.cssProceduralAPI; -} -if ( self.cssProceduralAPI instanceof Object === false ) { return; } -self.cssProceduralAPI.addSelectors(exceptedSelectors); +const procedurals = exceptedSelectors.filter(a => a.cssable === undefined); +if ( procedurals.length !== 0 ) { + const addSelectors = selectors => { + if ( self.cssProceduralAPI instanceof Object === false ) { return; } + self.cssProceduralAPI.addSelectors(selectors); + }; + if ( self.cssProceduralAPI === undefined ) { + self.cssProceduralAPI = chrome.runtime.sendMessage({ + what: 'injectCSSProceduralAPI' + }).catch(( ) => { + }); + } + if ( self.cssProceduralAPI instanceof Promise ) { + self.cssProceduralAPI.then(( ) => { addSelectors(procedurals); }); + } else { + addSelectors(procedurals); + } +} /******************************************************************************/ })(); -/******************************************************************************/ - void 0; diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index 2a1e3d94a..9742d977a 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -1059,84 +1059,12 @@ async function processCosmeticFilters(assetDetails, mapin) { /******************************************************************************/ -async function processDeclarativeCosmeticFilters(assetDetails, mapin) { - if ( mapin === undefined ) { return 0; } - if ( mapin.size === 0 ) { return 0; } - - // Distinguish declarative-compiled-as-procedural from actual procedural. - const declaratives = new Map(); - mapin.forEach((details, jsonSelector) => { - const selector = JSON.parse(jsonSelector); - if ( selector.cssable !== true ) { return; } - selector.cssable = undefined; - declaratives.set(JSON.stringify(selector), details); - }); - if ( declaratives.size === 0 ) { return 0; } - - const contentArray = groupHostnamesBySelectors( - groupSelectorsByHostnames(declaratives) - ); - - const argsMap = contentArray.map(entry => [ - entry[0], - entry[1].a ? entry[1].a.join('\n') : undefined, - ]); - const hostnamesMap = new Map(); - let hasEntities = false; - for ( const [ id, details ] of contentArray ) { - if ( details.y ) { - scriptletHostnameToIdMap(details.y, id, hostnamesMap); - hasEntities ||= details.y.some(a => a.endsWith('.*')); - } - if ( details.n ) { - scriptletHostnameToIdMap(details.n.map(a => `~${a}`), id, hostnamesMap); - hasEntities ||= details.n.some(a => a.endsWith('.*')); - } - } - const { argsList, argsSeqs } = argsMap2List(argsMap, hostnamesMap); - - const originalScriptletMap = await loadAllSourceScriptlets(); - let patchedScriptlet = originalScriptletMap.get('css-declarative').replace( - '$rulesetId$', - assetDetails.id - ); - patchedScriptlet = safeReplace(patchedScriptlet, - /\bself\.\$argsList\$/, - `${JSON.stringify(argsList, scriptletJsonReplacer)}` - ); - patchedScriptlet = safeReplace(patchedScriptlet, - /\bself\.\$argsSeqs\$/, - `${JSON.stringify(argsSeqs, scriptletJsonReplacer)}` - ); - patchedScriptlet = safeReplace(patchedScriptlet, - /\bself\.\$hostnamesMap\$/, - `${JSON.stringify(hostnamesMap, scriptletJsonReplacer)}` - ); - patchedScriptlet = safeReplace(patchedScriptlet, - 'self.$hasEntities$', - JSON.stringify(hasEntities) - ); - writeFile(`${scriptletDir}/declarative/${assetDetails.id}.js`, patchedScriptlet); - - if ( contentArray.length !== 0 ) { - log(`CSS-declarative: ${declaratives.size} distinct filters`); - log(`\tCombined into ${hostnamesMap.size} distinct hostnames`); - } - - return hostnamesMap.size; -} - -/******************************************************************************/ - async function processProceduralCosmeticFilters(assetDetails, mapin) { if ( mapin === undefined ) { return 0; } if ( mapin.size === 0 ) { return 0; } - // Distinguish declarative-compiled-as-procedural from actual procedural. const procedurals = new Map(); mapin.forEach((details, jsonSelector) => { - const selector = JSON.parse(jsonSelector); - if ( selector.cssable ) { return; } procedurals.set(jsonSelector, details); }); if ( procedurals.size === 0 ) { return 0; } @@ -1162,6 +1090,14 @@ async function processProceduralCosmeticFilters(assetDetails, mapin) { } } const { argsList, argsSeqs } = argsMap2List(argsMap, hostnamesMap); + const argsListAfter = []; + for ( const a of argsList ) { + const aAfter = []; + for ( let b of a ) { + aAfter.push(JSON.parse(b)); + } + argsListAfter.push(JSON.stringify(aAfter)); + } const originalScriptletMap = await loadAllSourceScriptlets(); let patchedScriptlet = originalScriptletMap.get('css-procedural').replace( '$rulesetId$', @@ -1169,7 +1105,7 @@ async function processProceduralCosmeticFilters(assetDetails, mapin) { ); patchedScriptlet = safeReplace(patchedScriptlet, /\bself\.\$argsList\$/, - `${JSON.stringify(argsList, scriptletJsonReplacer)}` + `${JSON.stringify(argsListAfter, scriptletJsonReplacer)}` ); patchedScriptlet = safeReplace(patchedScriptlet, /\bself\.\$argsSeqs\$/, @@ -1333,10 +1269,6 @@ async function rulesetFromURLs(assetDetails) { assetDetails, declarativeCosmetic ); - const declarativeStats = await processDeclarativeCosmeticFilters( - assetDetails, - proceduralCosmetic - ); const proceduralStats = await processProceduralCosmeticFilters( assetDetails, proceduralCosmetic @@ -1376,7 +1308,6 @@ async function rulesetFromURLs(assetDetails) { generic: genericCosmeticStats, generichigh: genericHighCosmeticStats, specific: specificCosmeticStats, - declarative: declarativeStats, procedural: proceduralStats, }, scriptlets: scriptletStats, diff --git a/platform/mv3/scriptlets/css-declarative.template.js b/platform/mv3/scriptlets/css-declarative.template.js deleted file mode 100644 index f7d467600..000000000 --- a/platform/mv3/scriptlets/css-declarative.template.js +++ /dev/null @@ -1,42 +0,0 @@ -/******************************************************************************* - - uBlock Origin Lite - a comprehensive, MV3-compliant content blocker - Copyright (C) 2014-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 -*/ - -// ruleset: $rulesetId$ - -// Important! -// Isolate from global scope -(function uBOL_cssDeclarativeImport() { - -/******************************************************************************/ - -const argsList = self.$argsList$; -const argsSeqs = self.$argsSeqs$; -const hostnamesMap = new Map(self.$hostnamesMap$); -const hasEntities = self.$hasEntities$; - -self.declarativeImports = self.declarativeImports || []; -self.declarativeImports.push({ argsList, argsSeqs, hostnamesMap, hasEntities }); - -/******************************************************************************/ - -})(); - -/******************************************************************************/