mirror of
https://github.com/gorhill/uBlock.git
synced 2026-03-11 09:04:36 +00:00
experimental
This commit is contained in:
parent
87cb6eb602
commit
7414e67505
9 changed files with 209 additions and 75 deletions
|
|
@ -63,6 +63,7 @@ import {
|
|||
browser,
|
||||
localRead, localRemove, localWrite,
|
||||
runtime,
|
||||
sessionAccessLevel,
|
||||
webextFlavor,
|
||||
} from './ext.js';
|
||||
|
||||
|
|
@ -667,6 +668,8 @@ async function startSession() {
|
|||
// launch time whether content css/scripts are properly registered.
|
||||
registerInjectables();
|
||||
|
||||
sessionAccessLevel({ accessLevel: 'TRUSTED_AND_UNTRUSTED_CONTEXTS' });
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/declarativeNetRequest
|
||||
// Firefox API does not support `dnr.setExtensionActionOptions`
|
||||
if ( canShowBlockedCount ) {
|
||||
|
|
|
|||
|
|
@ -100,6 +100,14 @@ export async function sessionRemove(key) {
|
|||
return browser.storage.session.remove(key);
|
||||
}
|
||||
|
||||
export async function sessionAccessLevel(level) {
|
||||
try {
|
||||
browser.storage.session.setAccessLevel(level);
|
||||
} catch {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
export async function adminRead(key) {
|
||||
|
|
|
|||
|
|
@ -21,7 +21,11 @@
|
|||
|
||||
import * as ut from './utils.js';
|
||||
|
||||
import { browser, localRemove } from './ext.js';
|
||||
import {
|
||||
browser,
|
||||
localKeys, localRemove, localWrite,
|
||||
sessionRemove,
|
||||
} from './ext.js';
|
||||
import { ubolErr, ubolLog } from './debug.js';
|
||||
|
||||
import { fetchJSON } from './fetch.js';
|
||||
|
|
@ -286,16 +290,25 @@ function registerGeneric(context, genericDetails) {
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
function registerProcedural(context) {
|
||||
async function registerProcedural(context) {
|
||||
const { before, filteringModeDetails, rulesetsDetails } = context;
|
||||
|
||||
const js = [];
|
||||
{
|
||||
const keys = await localKeys();
|
||||
for ( const key of keys ) {
|
||||
if ( key.startsWith('css.procedural.data.') === false ) { continue; }
|
||||
sessionRemove(key);
|
||||
localRemove(key);
|
||||
}
|
||||
}
|
||||
|
||||
const rulesetIds = [];
|
||||
for ( const rulesetDetails of rulesetsDetails ) {
|
||||
const count = rulesetDetails.css?.procedural || 0;
|
||||
if ( count === 0 ) { continue; }
|
||||
js.push(`/rulesets/scripting/procedural/${rulesetDetails.id}.js`);
|
||||
rulesetIds.push(rulesetDetails.id);
|
||||
}
|
||||
if ( js.length === 0 ) { return; }
|
||||
if ( rulesetIds.length === 0 ) { return; }
|
||||
|
||||
const { none, basic, optimal, complete } = filteringModeDetails;
|
||||
const matches = [
|
||||
|
|
@ -304,8 +317,21 @@ function registerProcedural(context) {
|
|||
];
|
||||
if ( matches.length === 0 ) { return; }
|
||||
|
||||
{
|
||||
const promises = [];
|
||||
for ( const id of rulesetIds ) {
|
||||
promises.push(
|
||||
fetchJSON(`/rulesets/scripting/procedural/data/${id}`).then(data => {
|
||||
return localWrite(`css.procedural.data.${id}`, data);
|
||||
})
|
||||
);
|
||||
}
|
||||
await Promise.all(promises);
|
||||
}
|
||||
|
||||
normalizeMatches(matches);
|
||||
|
||||
const js = rulesetIds.map(id => `/rulesets/scripting/procedural/${id}.js`);
|
||||
js.unshift('/js/scripting/css-api.js', '/js/scripting/isolated-api.js');
|
||||
js.push('/js/scripting/css-procedural.js');
|
||||
|
||||
|
|
@ -353,16 +379,25 @@ function registerProcedural(context) {
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
function registerSpecific(context) {
|
||||
async function registerSpecific(context) {
|
||||
const { before, filteringModeDetails, rulesetsDetails } = context;
|
||||
|
||||
const js = [];
|
||||
{
|
||||
const keys = await localKeys();
|
||||
for ( const key of keys ) {
|
||||
if ( key.startsWith('css.specific.data.') === false ) { continue; }
|
||||
sessionRemove(key);
|
||||
localRemove(key);
|
||||
}
|
||||
}
|
||||
|
||||
const rulesetIds = [];
|
||||
for ( const rulesetDetails of rulesetsDetails ) {
|
||||
const count = rulesetDetails.css?.specific || 0;
|
||||
if ( count === 0 ) { continue; }
|
||||
js.push(`/rulesets/scripting/specific/${rulesetDetails.id}.js`);
|
||||
rulesetIds.push(rulesetDetails.id);
|
||||
}
|
||||
if ( js.length === 0 ) { return; }
|
||||
if ( rulesetIds.length === 0 ) { return; }
|
||||
|
||||
const { none, basic, optimal, complete } = filteringModeDetails;
|
||||
const matches = [
|
||||
|
|
@ -371,8 +406,21 @@ function registerSpecific(context) {
|
|||
];
|
||||
if ( matches.length === 0 ) { return; }
|
||||
|
||||
{
|
||||
const promises = [];
|
||||
for ( const id of rulesetIds ) {
|
||||
promises.push(
|
||||
fetchJSON(`/rulesets/scripting/specific/data/${id}`).then(data => {
|
||||
return localWrite(`css.specific.data.${id}`, data);
|
||||
})
|
||||
);
|
||||
}
|
||||
await Promise.all(promises);
|
||||
}
|
||||
|
||||
normalizeMatches(matches);
|
||||
|
||||
const js = rulesetIds.map(id => `/rulesets/scripting/specific/${id}.js`);
|
||||
js.unshift('/js/scripting/css-api.js', '/js/scripting/isolated-api.js');
|
||||
js.push('/js/scripting/css-specific.js');
|
||||
|
||||
|
|
@ -533,9 +581,9 @@ async function registerInjectables() {
|
|||
};
|
||||
|
||||
await Promise.all([
|
||||
registerProcedural(context),
|
||||
registerScriptlet(context, scriptletDetails),
|
||||
registerSpecific(context),
|
||||
registerScriptlet(context, scriptletDetails),
|
||||
registerProcedural(context),
|
||||
registerGeneric(context, genericDetails),
|
||||
registerHighGeneric(context, genericDetails),
|
||||
registerCustomFilters(context),
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
|
||||
// Important!
|
||||
// Isolate from global scope
|
||||
(function uBOL_cssProcedural() {
|
||||
(async function uBOL_cssProcedural() {
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
|
@ -31,25 +31,12 @@ self.proceduralImports = undefined;
|
|||
/******************************************************************************/
|
||||
|
||||
const isolatedAPI = self.isolatedAPI;
|
||||
const selectors = new Set();
|
||||
const exceptions = new Set();
|
||||
|
||||
const lookupHostname = (hostname, details) => {
|
||||
const listref = isolatedAPI.binarySearch(details.hostnames, hostname);
|
||||
if ( listref === -1 ) { return; }
|
||||
if ( Array.isArray(details.selectorLists) === false ) {
|
||||
details.selectorLists = details.selectorLists.split(';');
|
||||
details.selectorListRefs = JSON.parse(`[${details.selectorListRefs}]`);
|
||||
}
|
||||
const ilist = details.selectorListRefs[listref];
|
||||
const list = JSON.parse(`[${details.selectorLists[ilist]}]`);
|
||||
for ( const iselector of list ) {
|
||||
if ( iselector >= 0 ) {
|
||||
selectors.add(details.selectors[iselector]);
|
||||
} else {
|
||||
exceptions.add(details.selectors[~iselector]);
|
||||
}
|
||||
}
|
||||
details.listrefs ||= [];
|
||||
details.listrefs.push(listref);
|
||||
};
|
||||
|
||||
const lookupAll = hostname => {
|
||||
|
|
@ -62,6 +49,38 @@ isolatedAPI.forEachHostname(lookupAll, {
|
|||
hasEntities: proceduralImports.some(a => a.hasEntities)
|
||||
});
|
||||
|
||||
const toLookup = proceduralImports.filter(a => Array.isArray(a.listrefs));
|
||||
if ( toLookup.length === 0 ) { return; }
|
||||
|
||||
const selectors = new Set();
|
||||
const exceptions = new Set();
|
||||
|
||||
const lookupSelectors = async details => {
|
||||
const { rulesetId } = details;
|
||||
const key = `css.procedural.data.${rulesetId}`;
|
||||
const data = await isolatedAPI.storageGet(key);
|
||||
if ( Boolean(data) === false ) { return; }
|
||||
if ( data.signature !== details.signature ) { return; }
|
||||
for ( const listref of details.listrefs ) {
|
||||
const ilist = data.selectorListRefs[listref];
|
||||
const list = JSON.parse(`[${data.selectorLists[ilist]}]`);
|
||||
for ( const iselector of list ) {
|
||||
if ( iselector >= 0 ) {
|
||||
selectors.add(data.selectors[iselector]);
|
||||
} else {
|
||||
exceptions.add(data.selectors[~iselector]);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const promises = [];
|
||||
for ( const details of toLookup ) {
|
||||
promises.push(lookupSelectors(details));
|
||||
}
|
||||
|
||||
await Promise.all(promises);
|
||||
|
||||
proceduralImports.length = 0;
|
||||
|
||||
for ( const selector of exceptions ) {
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
|
||||
// Important!
|
||||
// Isolate from global scope
|
||||
(function uBOL_cssSpecific() {
|
||||
(async function uBOL_cssSpecific() {
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
|
@ -31,25 +31,12 @@ self.specificImports = undefined;
|
|||
/******************************************************************************/
|
||||
|
||||
const isolatedAPI = self.isolatedAPI;
|
||||
const selectors = new Set();
|
||||
const exceptions = new Set();
|
||||
|
||||
const lookupHostname = (hostname, details) => {
|
||||
const listref = isolatedAPI.binarySearch(details.hostnames, hostname);
|
||||
if ( listref === -1 ) { return; }
|
||||
if ( Array.isArray(details.selectorLists) === false ) {
|
||||
details.selectorLists = details.selectorLists.split(';');
|
||||
details.selectorListRefs = JSON.parse(`[${details.selectorListRefs}]`);
|
||||
}
|
||||
const ilist = details.selectorListRefs[listref];
|
||||
const list = JSON.parse(`[${details.selectorLists[ilist]}]`);
|
||||
for ( const iselector of list ) {
|
||||
if ( iselector >= 0 ) {
|
||||
selectors.add(details.selectors[iselector]);
|
||||
} else {
|
||||
exceptions.add(details.selectors[~iselector]);
|
||||
}
|
||||
}
|
||||
details.listrefs ||= [];
|
||||
details.listrefs.push(listref);
|
||||
};
|
||||
|
||||
const lookupAll = hostname => {
|
||||
|
|
@ -62,7 +49,37 @@ isolatedAPI.forEachHostname(lookupAll, {
|
|||
hasEntities: specificImports.some(a => a.hasEntities)
|
||||
});
|
||||
|
||||
specificImports.length = 0;
|
||||
const toLookup = specificImports.filter(a => Array.isArray(a.listrefs));
|
||||
if ( toLookup.length === 0 ) { return; }
|
||||
|
||||
const selectors = new Set();
|
||||
const exceptions = new Set();
|
||||
|
||||
const lookupSelectors = async details => {
|
||||
const { rulesetId } = details;
|
||||
const key = `css.specific.data.${rulesetId}`;
|
||||
const data = await isolatedAPI.storageGet(key);
|
||||
if ( Boolean(data) === false ) { return; }
|
||||
if ( data.signature !== details.signature ) { return; }
|
||||
for ( const listref of details.listrefs ) {
|
||||
const ilist = data.selectorListRefs[listref];
|
||||
const list = JSON.parse(`[${data.selectorLists[ilist]}]`);
|
||||
for ( const iselector of list ) {
|
||||
if ( iselector >= 0 ) {
|
||||
selectors.add(data.selectors[iselector]);
|
||||
} else {
|
||||
exceptions.add(data.selectors[~iselector]);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const promises = [];
|
||||
for ( const details of toLookup ) {
|
||||
promises.push(lookupSelectors(details));
|
||||
}
|
||||
|
||||
await Promise.all(promises);
|
||||
|
||||
for ( const selector of exceptions ) {
|
||||
selectors.delete(selector);
|
||||
|
|
|
|||
|
|
@ -95,6 +95,47 @@
|
|||
return -1;
|
||||
};
|
||||
|
||||
const sessionGet = async function(key) {
|
||||
let data;
|
||||
try {
|
||||
const bin = await chrome.storage.session.get(key);
|
||||
data = bin?.[key] ?? undefined;
|
||||
} catch (error) {
|
||||
console.trace(error);
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
const sessionSet = function(key, data) {
|
||||
try {
|
||||
chrome.storage.session.set({ [key]: data });
|
||||
} catch (error) {
|
||||
console.trace(error);
|
||||
}
|
||||
};
|
||||
|
||||
const localGet = async function(key) {
|
||||
let data;
|
||||
try {
|
||||
const bin = await chrome.storage.local.get(key);
|
||||
data = bin?.[key] ?? undefined;
|
||||
} catch (error) {
|
||||
console.trace(error);
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
isolatedAPI.storageGet = async function(key) {
|
||||
let data = await sessionGet(key);
|
||||
if ( data === undefined ) {
|
||||
data = await localGet(key);
|
||||
if ( data !== undefined ) {
|
||||
sessionSet(key, data);
|
||||
}
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
})(self.isolatedAPI);
|
||||
|
||||
/******************************************************************************/
|
||||
|
|
|
|||
|
|
@ -863,30 +863,31 @@ async function processCosmeticFilters(assetDetails, realm, mapin) {
|
|||
allHostnames.set(hn, allSelectorLists.get(list));
|
||||
}
|
||||
|
||||
// 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();
|
||||
let patchedScriptlet = originalScriptletMap.get(`css-${realm}`).replace(
|
||||
'$rulesetId$',
|
||||
assetDetails.id
|
||||
);
|
||||
patchedScriptlet = safeReplace(patchedScriptlet,
|
||||
/\bself\.\$selectors\$/,
|
||||
`/* ${allSelectors.size} */ ${JSON.stringify(Array.from(allSelectors.keys()))}`
|
||||
);
|
||||
patchedScriptlet = safeReplace(patchedScriptlet,
|
||||
/\bself\.\$selectorLists\$/,
|
||||
`/* ${allSelectorLists.size} */ ${JSON.stringify(Array.from(allSelectorLists.keys()).join(';'))}`
|
||||
);
|
||||
const sortedHostnames = Array.from(allHostnames.keys()).toSorted((a, b) => {
|
||||
const d = a.length - b.length;
|
||||
if ( d !== 0 ) { return d; }
|
||||
return a < b ? -1 : 1;
|
||||
});
|
||||
|
||||
const data = JSON.stringify({
|
||||
signature: secret,
|
||||
selectors: Array.from(allSelectors.keys()),
|
||||
selectorLists: Array.from(allSelectorLists.keys()),
|
||||
selectorListRefs: sortedHostnames.map(a => allHostnames.get(a)),
|
||||
});
|
||||
writeFile(`${scriptletDir}/${realm}/data/${assetDetails.id}.json`, data);
|
||||
|
||||
// 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();
|
||||
let patchedScriptlet = originalScriptletMap.get(`css-${realm}`).replace(
|
||||
'self.$rulesetId$',
|
||||
JSON.stringify(assetDetails.id)
|
||||
);
|
||||
patchedScriptlet = safeReplace(patchedScriptlet,
|
||||
/\bself\.\$selectorListRefs\$/,
|
||||
`/* ${sortedHostnames.length} */ "${JSON.stringify(sortedHostnames.map(a => allHostnames.get(a))).slice(1, -1)}"`
|
||||
'self.$signature$',
|
||||
JSON.stringify(secret)
|
||||
);
|
||||
patchedScriptlet = safeReplace(patchedScriptlet,
|
||||
/\bself\.\$hostnames\$/,
|
||||
|
|
|
|||
|
|
@ -19,23 +19,21 @@
|
|||
Home: https://github.com/gorhill/uBlock
|
||||
*/
|
||||
|
||||
// ruleset: $rulesetId$
|
||||
|
||||
// Important!
|
||||
// Isolate from global scope
|
||||
(function uBOL_cssProceduralImport() {
|
||||
|
||||
if ( Boolean(chrome?.storage?.local) === false ) { return; }
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
||||
const selectors = self.$selectors$;
|
||||
const selectorLists = self.$selectorLists$;
|
||||
const selectorListRefs = self.$selectorListRefs$;
|
||||
const rulesetId = self.$rulesetId$;
|
||||
const signature = self.$signature$;
|
||||
const hostnames = self.$hostnames$;
|
||||
const hasEntities = self.$hasEntities$;
|
||||
|
||||
self.proceduralImports = self.proceduralImports || [];
|
||||
self.proceduralImports.push({ selectors, selectorLists, selectorListRefs, hostnames, hasEntities });
|
||||
self.proceduralImports.push({ rulesetId, signature, hostnames, hasEntities });
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
|
|
|||
|
|
@ -19,22 +19,21 @@
|
|||
Home: https://github.com/gorhill/uBlock
|
||||
*/
|
||||
|
||||
// ruleset: $rulesetId$
|
||||
|
||||
// Important!
|
||||
// Isolate from global scope
|
||||
(function uBOL_cssSpecificImports() {
|
||||
|
||||
if ( Boolean(chrome?.storage?.local) === false ) { return; }
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const selectors = self.$selectors$;
|
||||
const selectorLists = self.$selectorLists$;
|
||||
const selectorListRefs = self.$selectorListRefs$;
|
||||
const rulesetId = self.$rulesetId$;
|
||||
const signature = self.$signature$;
|
||||
const hostnames = self.$hostnames$;
|
||||
const hasEntities = self.$hasEntities$;
|
||||
|
||||
self.specificImports = self.specificImports || [];
|
||||
self.specificImports.push({ selectors, selectorLists, selectorListRefs, hostnames, hasEntities });
|
||||
self.specificImports.push({ rulesetId, signature, hostnames, hasEntities });
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue