mirror of
https://github.com/gorhill/uBlock.git
synced 2026-03-11 09:04:36 +00:00
draft
This commit is contained in:
parent
66a1c5347b
commit
d4743cf570
10 changed files with 163 additions and 92 deletions
|
|
@ -19,6 +19,8 @@
|
|||
Home: https://github.com/gorhill/uBlock
|
||||
*/
|
||||
|
||||
import * as scrmgr from './scripting-manager.js';
|
||||
|
||||
import {
|
||||
MODE_BASIC,
|
||||
MODE_OPTIMAL,
|
||||
|
|
@ -100,14 +102,13 @@ import {
|
|||
} from './debug.js';
|
||||
|
||||
import { dnr } from './ext-compat.js';
|
||||
import { registerInjectables } from './scripting-manager.js';
|
||||
import { toggleToolbarIcon } from './action.js';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const UBOL_ORIGIN = runtime.getURL('').replace(/\/$/, '').toLowerCase();
|
||||
|
||||
const canShowBlockedCount = typeof dnr.setExtensionActionOptions === 'function';
|
||||
const { registerInjectables } = scrmgr;
|
||||
|
||||
let pendingPermissionRequest;
|
||||
|
||||
|
|
@ -710,6 +711,8 @@ async function start() {
|
|||
|
||||
if ( process.wakeupRun === false ) {
|
||||
await startSession();
|
||||
} else {
|
||||
scrmgr.onWakeupRun();
|
||||
}
|
||||
|
||||
toggleDeveloperMode(rulesetConfig.developerMode);
|
||||
|
|
|
|||
|
|
@ -100,6 +100,16 @@ export async function sessionRemove(key) {
|
|||
return browser.storage.session.remove(key);
|
||||
}
|
||||
|
||||
export async function sessionKeys() {
|
||||
if ( notAnObject(browser?.storage?.session) ) { return; }
|
||||
if ( browser.storage.session.getKeys ) {
|
||||
return browser.storage.session.getKeys();
|
||||
}
|
||||
const bin = await browser.storage.session.get(null);
|
||||
if ( notAnObject(bin) ) { return; }
|
||||
return Object.keys(bin);
|
||||
}
|
||||
|
||||
export async function sessionAccessLevel(level) {
|
||||
try {
|
||||
browser.storage.session.setAccessLevel(level);
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import * as ut from './utils.js';
|
|||
import {
|
||||
browser,
|
||||
localKeys, localRemove, localWrite,
|
||||
sessionRemove,
|
||||
sessionKeys, sessionRead, sessionRemove, sessionWrite,
|
||||
} from './ext.js';
|
||||
import { ubolErr, ubolLog } from './debug.js';
|
||||
|
||||
|
|
@ -94,6 +94,15 @@ const normalizeRegisteredContentScripts = registered => {
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
async function resetCSSCache() {
|
||||
const keys = await sessionKeys();
|
||||
return Promise.all(
|
||||
keys.filter(a => a.startsWith('cache.css.')).map(a => sessionRemove(a))
|
||||
);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
function registerHighGeneric(context, genericDetails) {
|
||||
const { before, filteringModeDetails, rulesetsDetails } = context;
|
||||
|
||||
|
|
@ -297,7 +306,6 @@ async function registerProcedural(context) {
|
|||
const keys = await localKeys();
|
||||
for ( const key of keys ) {
|
||||
if ( key.startsWith('css.procedural.') === false ) { continue; }
|
||||
sessionRemove(key);
|
||||
localRemove(key);
|
||||
}
|
||||
}
|
||||
|
|
@ -322,7 +330,7 @@ async function registerProcedural(context) {
|
|||
for ( const id of rulesetIds ) {
|
||||
promises.push(
|
||||
fetchJSON(`/rulesets/scripting/procedural/${id}`).then(data => {
|
||||
return localWrite(`css.procedural.json.${id}`, data);
|
||||
return localWrite(`css.procedural.${id}`, data);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
|
@ -386,7 +394,6 @@ async function registerSpecific(context) {
|
|||
const keys = await localKeys();
|
||||
for ( const key of keys ) {
|
||||
if ( key.startsWith('css.specific.') === false ) { continue; }
|
||||
sessionRemove(key);
|
||||
localRemove(key);
|
||||
}
|
||||
}
|
||||
|
|
@ -411,7 +418,7 @@ async function registerSpecific(context) {
|
|||
for ( const id of rulesetIds ) {
|
||||
promises.push(
|
||||
fetchJSON(`/rulesets/scripting/specific/${id}`).then(data => {
|
||||
return localWrite(`css.specific.json.${id}`, data);
|
||||
return localWrite(`css.specific.${id}`, data);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
|
@ -547,7 +554,7 @@ function registerScriptlet(context, scriptletDetails) {
|
|||
// Issue: Safari appears to completely ignore excludeMatches
|
||||
// https://github.com/radiolondra/ExcludeMatches-Test
|
||||
|
||||
async function registerInjectables() {
|
||||
export async function registerInjectables() {
|
||||
if ( browser.scripting === undefined ) { return false; }
|
||||
|
||||
if ( registerInjectables.barrier ) { return true; }
|
||||
|
|
@ -612,6 +619,8 @@ async function registerInjectables() {
|
|||
}
|
||||
}
|
||||
|
||||
await resetCSSCache();
|
||||
|
||||
registerInjectables.barrier = false;
|
||||
|
||||
return true;
|
||||
|
|
@ -619,6 +628,25 @@ async function registerInjectables() {
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
export {
|
||||
registerInjectables
|
||||
};
|
||||
export async function onWakeupRun() {
|
||||
const cleanupTime = await sessionRead('scripting.manager.cleanup.time') || 0;
|
||||
const now = Date.now();
|
||||
const since = now - cleanupTime;
|
||||
if ( since < (15 * 60 * 1000) ) { return; } // 15 minutes
|
||||
const MAX_CACHE_ENTRY_LOW = 256;
|
||||
const MAX_CACHE_ENTRY_HIGH = MAX_CACHE_ENTRY_LOW +
|
||||
Math.min(Math.round(MAX_CACHE_ENTRY_LOW + MAX_CACHE_ENTRY_LOW / 8), 1);
|
||||
const keys = await sessionKeys() || [];
|
||||
const cacheKeys = keys.filter(a => a.startsWith('cache.css.'));
|
||||
if ( cacheKeys.length < MAX_CACHE_ENTRY_HIGH ) { return; }
|
||||
const entries = await Promise.all(cacheKeys.map(async a => {
|
||||
const entry = await sessionRead(a) || {};
|
||||
entry.key = a;
|
||||
return entry;
|
||||
}));
|
||||
entries.sort((a, b) => b.t - a.t);
|
||||
entries.slice(MAX_CACHE_ENTRY_LOW).map(a => sessionRemove(a.key));
|
||||
sessionWrite('scripting.manager.cleanup.time', now)
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
|
|
|||
|
|
@ -30,18 +30,8 @@ self.proceduralImports = undefined;
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
const isolatedAPI = self.isolatedAPI;
|
||||
|
||||
const selectors = [];
|
||||
|
||||
const cachedCSS = await isolatedAPI.sessionGet('css.procedural.cache') || {};
|
||||
if ( cachedCSS[isolatedAPI.docHostname] ) {
|
||||
selectors.push(...cachedCSS[isolatedAPI.docHostname]);
|
||||
} else {
|
||||
selectors.push(...await isolatedAPI.getSelectors('procedural', proceduralImports));
|
||||
cachedCSS[isolatedAPI.docHostname] = selectors;
|
||||
isolatedAPI.sessionSet('css.procedural.cache', cachedCSS);
|
||||
}
|
||||
const selectors = await self.cosmeticAPI.getSelectors('procedural', proceduralImports);
|
||||
self.cosmeticAPI.release();
|
||||
if ( selectors.length === 0 ) { return; }
|
||||
|
||||
proceduralImports.length = 0;
|
||||
|
|
|
|||
|
|
@ -30,24 +30,10 @@ self.specificImports = undefined;
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
const isolatedAPI = self.isolatedAPI;
|
||||
|
||||
const selectors = [];
|
||||
|
||||
self.cssAPI.update('*{visibility:hidden!important;}');
|
||||
|
||||
const cachedCSS = await isolatedAPI.sessionGet('css.specific.cache') || {};
|
||||
if ( cachedCSS[isolatedAPI.docHostname] ) {
|
||||
selectors.push(...cachedCSS[isolatedAPI.docHostname]);
|
||||
} else {
|
||||
selectors.push(...await isolatedAPI.getSelectors('specific', specificImports));
|
||||
cachedCSS[isolatedAPI.docHostname] = selectors;
|
||||
isolatedAPI.sessionSet('css.specific.cache', cachedCSS);
|
||||
}
|
||||
const insert = selectors.length !== 0
|
||||
? `${Array.from(selectors).join(',\n')}{display:none!important;}`
|
||||
: undefined;
|
||||
self.cssAPI.update(insert, '*{visibility:hidden!important;}');
|
||||
const selectors = await self.cosmeticAPI.getSelectors('specific', specificImports);
|
||||
self.cosmeticAPI.release();
|
||||
if ( selectors.length === 0 ) { return; }
|
||||
self.cssAPI.update(`${selectors.join(',\n')}{display:none!important;}`);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@
|
|||
|
||||
const hostnameStack = (( ) => {
|
||||
const docloc = document.location;
|
||||
isolatedAPI.docHostname = docloc.hostname;
|
||||
const origins = [ docloc.origin ];
|
||||
if ( docloc.ancestorOrigins ) {
|
||||
origins.push(...docloc.ancestorOrigins);
|
||||
|
|
@ -75,7 +74,38 @@
|
|||
}
|
||||
};
|
||||
|
||||
isolatedAPI.binarySearch = (sorted, target) => {
|
||||
})(self.isolatedAPI);
|
||||
|
||||
|
||||
(api => {
|
||||
if ( typeof api === 'object' ) { return; }
|
||||
|
||||
const cosmeticAPI = self.cosmeticAPI = {};
|
||||
|
||||
const sessionRead = async function(key) {
|
||||
try {
|
||||
const bin = await chrome.storage.session.get(key);
|
||||
return bin?.[key] ?? undefined;
|
||||
} catch {
|
||||
}
|
||||
};
|
||||
|
||||
const sessionWrite = function(key, data) {
|
||||
try {
|
||||
chrome.storage.session.set({ [key]: data });
|
||||
} catch {
|
||||
}
|
||||
};
|
||||
|
||||
const localRead = async function(key) {
|
||||
try {
|
||||
const bin = await chrome.storage.local.get(key);
|
||||
return bin?.[key] ?? undefined;
|
||||
} catch {
|
||||
}
|
||||
};
|
||||
|
||||
const binarySearch = (sorted, target) => {
|
||||
let l = 0, i = 0, d = 0;
|
||||
let r = sorted.length;
|
||||
let candidate;
|
||||
|
|
@ -96,57 +126,79 @@
|
|||
return -1;
|
||||
};
|
||||
|
||||
isolatedAPI.sessionGet = async function(key) {
|
||||
try {
|
||||
const bin = await chrome.storage.session.get(key);
|
||||
return bin?.[key] ?? undefined;
|
||||
} catch {
|
||||
const lookupHostname = (hostname, data) => {
|
||||
const listref = binarySearch(data.hostnames, hostname);
|
||||
if ( listref === -1 ) { return; }
|
||||
const ilist = data.selectorListRefs[listref];
|
||||
const list = JSON.parse(`[${data.selectorLists[ilist]}]`);
|
||||
const { result } = data;
|
||||
for ( const iselector of list ) {
|
||||
if ( iselector >= 0 ) {
|
||||
result.selectors.add(data.selectors[iselector]);
|
||||
} else {
|
||||
result.exceptions.add(data.selectors[~iselector]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
isolatedAPI.sessionSet = function(key, data) {
|
||||
try {
|
||||
chrome.storage.session.set({ [key]: data });
|
||||
} catch {
|
||||
}
|
||||
const selectorsFromRuleset = async (realm, rulesetId, result) => {
|
||||
const data = await localRead(`css.${realm}.${rulesetId}`);
|
||||
if ( typeof data !== 'object' || data === null ) { return; }
|
||||
data.result = result;
|
||||
self.isolatedAPI.forEachHostname(lookupHostname, data);
|
||||
};
|
||||
|
||||
isolatedAPI.localGet = async function(key) {
|
||||
try {
|
||||
const bin = await chrome.storage.local.get(key);
|
||||
return bin?.[key] ?? undefined;
|
||||
} catch {
|
||||
}
|
||||
};
|
||||
|
||||
isolatedAPI.getSelectors = async function(realm, details) {
|
||||
const fillCache = async function(realm, rulesetIds) {
|
||||
const selectors = new Set();
|
||||
const exceptions = new Set();
|
||||
const lookupHostname = (hostname, data) => {
|
||||
const listref = isolatedAPI.binarySearch(data.hostnames, hostname);
|
||||
if ( listref === -1 ) { return; }
|
||||
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 selectorsFromRuleset = async rulesetId => {
|
||||
const data = await isolatedAPI.localGet(`css.${realm}.json.${rulesetId}`);
|
||||
isolatedAPI.forEachHostname(lookupHostname, data);
|
||||
};
|
||||
await Promise.all(details.map(a => selectorsFromRuleset(a.rulesetId)));
|
||||
const result = { selectors, exceptions };
|
||||
await Promise.all(rulesetIds.map(a => selectorsFromRuleset(realm, a, result)));
|
||||
for ( const selector of exceptions ) {
|
||||
selectors.delete(selector);
|
||||
}
|
||||
return Array.from(selectors);
|
||||
cacheEntry[cacheSlots[realm]] = Array.from(selectors).map(a =>
|
||||
a.startsWith('{') ? JSON.parse(a) : a
|
||||
);
|
||||
};
|
||||
|
||||
})(self.isolatedAPI);
|
||||
const readCache = async ( ) => {
|
||||
cacheEntry = await sessionRead(cacheKey) || {};
|
||||
};
|
||||
|
||||
const cacheSlots = { 'specific': 's', 'procedural': 'p' };
|
||||
const cacheKey = `cache.css.${document.location.hostname || ''}`;
|
||||
let clientCount = 0;
|
||||
let cacheEntry;
|
||||
|
||||
cosmeticAPI.getSelectors = async function(realm, rulesetIds) {
|
||||
clientCount += 1;
|
||||
const slot = cacheSlots[realm];
|
||||
if ( cacheEntry === undefined ) {
|
||||
cacheEntry = readCache();
|
||||
}
|
||||
if ( cacheEntry instanceof Promise ) {
|
||||
await cacheEntry;
|
||||
}
|
||||
if ( cacheEntry[slot] === undefined ) {
|
||||
cacheEntry[slot] = fillCache(realm, rulesetIds);
|
||||
}
|
||||
if ( cacheEntry[slot] instanceof Promise ) {
|
||||
await cacheEntry[slot];
|
||||
}
|
||||
return cacheEntry[slot];
|
||||
};
|
||||
|
||||
cosmeticAPI.release = function() {
|
||||
clientCount -= 1;
|
||||
if ( clientCount !== 0 ) { return; }
|
||||
self.cosmeticAPI = undefined;
|
||||
const now = Math.round(Date.now() / 15000);
|
||||
const since = now - (cacheEntry.t || 0);
|
||||
if ( since <= 1 ) { return; }
|
||||
cacheEntry.t = now;
|
||||
sessionWrite(cacheKey, cacheEntry);
|
||||
};
|
||||
})(self.cosmeticAPI);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
|
|
|||
|
|
@ -25,20 +25,22 @@
|
|||
const inserted = new Set();
|
||||
|
||||
self.cssAPI = {
|
||||
insert(css) {
|
||||
update(insert, remove) {
|
||||
chrome.runtime.sendMessage({
|
||||
what: 'insertCSS',
|
||||
css,
|
||||
what: 'updateCSS',
|
||||
insert,
|
||||
remove,
|
||||
}).catch(( ) => {
|
||||
});
|
||||
inserted.add(css);
|
||||
inserted.add(insert);
|
||||
inserted.delete(remove);
|
||||
},
|
||||
};
|
||||
|
||||
self.addEventListener('pageshow', ( ) => {
|
||||
chrome.runtime.sendMessage({
|
||||
what: 'insertCSS',
|
||||
css: Array.from(inserted).join('\n'),
|
||||
what: 'updateCSS',
|
||||
insert: Array.from(inserted).join('\n'),
|
||||
}).catch(( ) => {
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -43,8 +43,8 @@ if ( details?.plainSelectors?.length ) {
|
|||
const selectors = details.plainSelectors;
|
||||
self.addEventListener('pageshow', ( ) => {
|
||||
chrome.runtime.sendMessage({
|
||||
what: 'insertCSS',
|
||||
css: `${selectors.join(',\n')}{display:none!important;}`,
|
||||
what: 'updateCSS',
|
||||
insert: `${selectors.join(',\n')}{display:none!important;}`,
|
||||
}).catch(( ) => {
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ if ( Boolean(chrome?.storage?.local) === false ) { return; }
|
|||
const rulesetId = self.$rulesetId$;
|
||||
|
||||
self.proceduralImports = self.proceduralImports || [];
|
||||
self.proceduralImports.push({ rulesetId });
|
||||
self.proceduralImports.push(rulesetId);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ if ( Boolean(chrome?.storage?.local) === false ) { return; }
|
|||
const rulesetId = self.$rulesetId$;
|
||||
|
||||
self.specificImports = self.specificImports || [];
|
||||
self.specificImports.push({ rulesetId });
|
||||
self.specificImports.push(rulesetId);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue