1
0
mirror of https://github.com/gorhill/uBlock.git synced 2024-10-04 16:47:15 +02:00

Further modularize uBO's codebase

Related issue:
- https://github.com/uBlockOrigin/uBlock-issues/issues/1664

Modularization is a necessary step toward possibly publishing
a more complete nodejs package to allow using uBO's filtering
capabilities outside of the uBO extension.

Additionally, as per feedback, remove undue usage of console
output as per feedback:
- https://github.com/uBlockOrigin/uBlock-issues/issues/1664#issuecomment-888451032
This commit is contained in:
Raymond Hill 2021-07-28 19:48:38 -04:00
parent d7cd6d72f6
commit 62b6826dd5
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
34 changed files with 1989 additions and 1878 deletions

View File

@ -29,10 +29,10 @@ import './lib/punycode.js';
import './lib/publicsuffixlist/publicsuffixlist.js';
import globals from './js/globals.js';
import snfe from './js/static-net-filtering.js';
import { FilteringContext } from './js/filtering-context.js';
import { LineIterator } from './js/text-iterators.js';
import { StaticFilteringParser } from './js/static-filtering-parser.js';
import { staticNetFilteringEngine } from './js/static-net-filtering.js';
import {
CompiledListReader,
@ -41,33 +41,31 @@ import {
/******************************************************************************/
function compileList(rawText, writer) {
function compileList(rawText, writer, options = {}) {
const lineIter = new LineIterator(rawText);
const parser = new StaticFilteringParser(true);
const events = Array.isArray(options.events) ? options.events : undefined;
parser.setMaxTokenLength(staticNetFilteringEngine.MAX_TOKEN_LENGTH);
parser.setMaxTokenLength(snfe.MAX_TOKEN_LENGTH);
while ( lineIter.eot() === false ) {
let line = lineIter.next();
while ( line.endsWith(' \\') ) {
if ( lineIter.peek(4) !== ' ' ) { break; }
line = line.slice(0, -2).trim() + lineIter.next().trim();
}
parser.analyze(line);
if ( parser.shouldIgnore() ) { continue; }
if ( parser.category !== parser.CATStaticNetFilter ) { continue; }
if ( parser.patternHasUnicode() && parser.toASCII() === false ) {
continue;
}
if ( staticNetFilteringEngine.compile(parser, writer) ) { continue; }
if ( staticNetFilteringEngine.error !== undefined ) {
console.info(JSON.stringify({
realm: 'message',
if ( snfe.compile(parser, writer) ) { continue; }
if ( snfe.error !== undefined && events !== undefined ) {
options.events.push({
type: 'error',
text: staticNetFilteringEngine.error
}));
text: snfe.error
});
}
}
@ -79,13 +77,13 @@ function applyList(name, raw) {
writer.properties.set('name', name);
const compiled = compileList(raw, writer);
const reader = new CompiledListReader(compiled);
staticNetFilteringEngine.fromCompiled(reader);
snfe.fromCompiled(reader);
}
function enableWASM(path) {
return Promise.all([
globals.publicSuffixList.enableWASM(`${path}/lib/publicsuffixlist`),
staticNetFilteringEngine.enableWASM(`${path}/js`),
snfe.enableWASM(`${path}/js`),
]);
}
@ -94,35 +92,32 @@ function pslInit(raw) {
const require = createRequire(import.meta.url); // jshint ignore:line
raw = require('./data/effective_tld_names.json');
if ( typeof raw !== 'string' || raw.trim() === '' ) {
console.info('Unable to populate public suffix list');
console.error('Unable to populate public suffix list');
return;
}
}
globals.publicSuffixList.parse(raw, globals.punycode.toASCII);
console.info('Public suffix list populated');
}
function restart(lists) {
function restart(lists, options = {}) {
// Remove all filters
reset();
if ( Array.isArray(lists) && lists.length !== 0 ) {
// Populate filtering engine with filter lists
for ( const { name, raw } of lists ) {
applyList(name, raw);
applyList(name, raw, options);
}
// Commit changes
staticNetFilteringEngine.freeze();
staticNetFilteringEngine.optimize();
snfe.freeze();
snfe.optimize();
}
console.info('Static network filtering engine populated');
return staticNetFilteringEngine;
return snfe;
}
function reset() {
staticNetFilteringEngine.reset();
snfe.reset();
}
export {

View File

@ -51,7 +51,7 @@ function fetch(listName) {
}
*/
pslInit();
await pslInit();
const snfe = await Promise.all([
fetch('easylist'),

View File

@ -5,37 +5,17 @@
<title>uBlock Origin</title>
</head>
<body>
<script src="js/console.js"></script>
<script src="lib/lz4/lz4-block-codec-any.js"></script>
<script src="js/webext.js"></script>
<script src="js/vapi.js"></script>
<script src="js/vapi-common.js"></script>
<script src="js/vapi-background.js" type="module"></script>
<script src="js/vapi-background-ext.js" type="module"></script><!-- platform-specific to extend common code paths -->
<script src="js/background.js" type="module"></script>
<script src="js/traffic.js" type="module"></script>
<script src="js/utils.js" type="module"></script>
<script src="js/lz4.js" type="module"></script>
<script src="js/cachestorage.js" type="module"></script>
<script src="js/assets.js" type="module"></script>
<script src="js/redirect-engine.js" type="module"></script>
<script src="js/dynamic-net-filtering.js" type="module"></script>
<script src="js/url-net-filtering.js" type="module"></script>
<script src="js/static-ext-filtering.js" type="module"></script>
<script src="js/cosmetic-filtering.js" type="module"></script>
<script src="js/scriptlet-filtering.js" type="module"></script>
<script src="js/html-filtering.js" type="module"></script>
<script src="js/httpheader-filtering.js" type="module"></script>
<script src="js/hnswitches.js" type="module"></script>
<script src="js/ublock.js" type="module"></script>
<script src="js/storage.js" type="module"></script>
<script src="js/logger.js" type="module"></script>
<script src="js/pagestore.js" type="module"></script>
<script src="js/tab.js" type="module"></script>
<script src="js/messaging.js" type="module"></script>
<script src="js/text-encode.js" type="module"></script>
<script src="js/contextmenu.js" type="module"></script>
<script src="js/reverselookup.js" type="module"></script>
<script src="js/start.js" type="module"></script>
<script src="js/commands.js" type="module"></script>
</body>

View File

@ -23,7 +23,9 @@
/******************************************************************************/
import µBlock from './background.js';
import cacheStorage from './cachestorage.js';
import logger from './logger.js';
import µb from './background.js';
/******************************************************************************/
@ -31,7 +33,7 @@ const reIsExternalPath = /^(?:[a-z-]+):\/\//;
const reIsUserAsset = /^user-/;
const errorCantConnectTo = vAPI.i18n('errorCantConnectTo');
const api = {};
const assets = {};
// A hint for various pieces of code to take measures if possible to save
// bandwidth of remote servers.
@ -41,13 +43,13 @@ let remoteServerFriendly = false;
const observers = [];
api.addObserver = function(observer) {
assets.addObserver = function(observer) {
if ( observers.indexOf(observer) === -1 ) {
observers.push(observer);
}
};
api.removeObserver = function(observer) {
assets.removeObserver = function(observer) {
let pos;
while ( (pos = observers.indexOf(observer)) !== -1 ) {
observers.splice(pos, 1);
@ -65,11 +67,11 @@ const fireNotification = function(topic, details) {
/******************************************************************************/
api.fetch = function(url, options = {}) {
assets.fetch = function(url, options = {}) {
return new Promise((resolve, reject) => {
// Start of executor
const timeoutAfter = µBlock.hiddenSettings.assetFetchTimeout * 1000 || 30000;
const timeoutAfter = µb.hiddenSettings.assetFetchTimeout * 1000 || 30000;
const xhr = new XMLHttpRequest();
let contentLoaded = 0;
let timeoutTimer;
@ -86,7 +88,7 @@ api.fetch = function(url, options = {}) {
};
const fail = function(details, msg) {
µBlock.logger.writeOne({
logger.writeOne({
realm: 'message',
type: 'error',
text: msg,
@ -154,7 +156,7 @@ api.fetch = function(url, options = {}) {
/******************************************************************************/
api.fetchText = async function(url) {
assets.fetchText = async function(url) {
const isExternal = reIsExternalPath.test(url);
let actualUrl = isExternal ? url : vAPI.getURL(url);
@ -171,7 +173,7 @@ api.fetchText = async function(url) {
// servers.
if ( isExternal && remoteServerFriendly !== true ) {
const cacheBypassToken =
µBlock.hiddenSettings.updateAssetBypassBrowserCache
µb.hiddenSettings.updateAssetBypassBrowserCache
? Math.floor(Date.now() / 1000) % 86413
: Math.floor(Date.now() / 3600000) % 13;
const queryValue = `_=${cacheBypassToken}`;
@ -185,7 +187,7 @@ api.fetchText = async function(url) {
let details = { content: '' };
try {
details = await api.fetch(actualUrl);
details = await assets.fetch(actualUrl);
// Consider an empty result to be an error
if ( stringIsNotEmpty(details.content) === false ) {
@ -223,7 +225,7 @@ api.fetchText = async function(url) {
// https://github.com/gorhill/uBlock/issues/3331
// Support the seamless loading of sublists.
api.fetchFilterList = async function(mainlistURL) {
assets.fetchFilterList = async function(mainlistURL) {
const toParsedURL = url => {
try {
return new URL(url.trim());
@ -265,7 +267,7 @@ api.fetchFilterList = async function(mainlistURL) {
}
if ( result instanceof Object === false ) { continue; }
const content = result.content;
const slices = µBlock.preparseDirectives.split(content);
const slices = µb.preparseDirectives.split(content);
for ( let i = 0, n = slices.length - 1; i < n; i++ ) {
const slice = content.slice(slices[i+0], slices[i+1]);
if ( (i & 1) !== 0 ) {
@ -288,7 +290,7 @@ api.fetchFilterList = async function(mainlistURL) {
out.push(
slice.slice(lastIndex, match.index + match[0].length),
`! >>>>>>>> ${subURL}\n`,
api.fetchText(subURL),
assets.fetchText(subURL),
`! <<<<<<<< ${subURL}\n`
);
lastIndex = reInclude.lastIndex;
@ -346,7 +348,7 @@ let assetSourceRegistryPromise,
const getAssetSourceRegistry = function() {
if ( assetSourceRegistryPromise === undefined ) {
assetSourceRegistryPromise = µBlock.cacheStorage.get(
assetSourceRegistryPromise = cacheStorage.get(
'assetSourceRegistry'
).then(bin => {
if (
@ -356,12 +358,12 @@ const getAssetSourceRegistry = function() {
assetSourceRegistry = bin.assetSourceRegistry;
return assetSourceRegistry;
}
return api.fetchText(
µBlock.assetsBootstrapLocation || 'assets/assets.json'
return assets.fetchText(
µb.assetsBootstrapLocation || 'assets/assets.json'
).then(details => {
return details.content !== ''
? details
: api.fetchText('assets/assets.json');
: assets.fetchText('assets/assets.json');
}).then(details => {
updateAssetSourceRegistry(details.content, true);
return assetSourceRegistry;
@ -418,7 +420,7 @@ const saveAssetSourceRegistry = (( ) => {
let timer;
const save = function() {
timer = undefined;
µBlock.cacheStorage.set({ assetSourceRegistry });
cacheStorage.set({ assetSourceRegistry });
};
return function(lazily) {
if ( timer !== undefined ) {
@ -464,13 +466,13 @@ const updateAssetSourceRegistry = function(json, silent) {
saveAssetSourceRegistry();
};
api.registerAssetSource = async function(assetKey, details) {
assets.registerAssetSource = async function(assetKey, details) {
await getAssetSourceRegistry();
registerAssetSource(assetKey, details);
saveAssetSourceRegistry(true);
};
api.unregisterAssetSource = async function(assetKey) {
assets.unregisterAssetSource = async function(assetKey) {
await getAssetSourceRegistry();
unregisterAssetSource(assetKey);
saveAssetSourceRegistry(true);
@ -489,7 +491,7 @@ let assetCacheRegistry = {};
const getAssetCacheRegistry = function() {
if ( assetCacheRegistryPromise === undefined ) {
assetCacheRegistryPromise = µBlock.cacheStorage.get(
assetCacheRegistryPromise = cacheStorage.get(
'assetCacheRegistry'
).then(bin => {
if (
@ -523,7 +525,7 @@ const saveAssetCacheRegistry = (( ) => {
let timer;
const save = function() {
timer = undefined;
µBlock.cacheStorage.set({ assetCacheRegistry });
cacheStorage.set({ assetCacheRegistry });
};
return function(lazily) {
if ( timer !== undefined ) { clearTimeout(timer); }
@ -547,7 +549,7 @@ const assetCacheRead = async function(assetKey, updateReadTime = false) {
const [ , bin ] = await Promise.all([
getAssetCacheRegistry(),
µBlock.cacheStorage.get(internalKey),
cacheStorage.get(internalKey),
]);
if (
bin instanceof Object === false ||
@ -593,7 +595,7 @@ const assetCacheWrite = async function(assetKey, details) {
if ( typeof options.url === 'string' ) {
entry.remoteURL = options.url;
}
µBlock.cacheStorage.set({
cacheStorage.set({
assetCacheRegistry,
[`cache/${assetKey}`]: content
});
@ -623,8 +625,8 @@ const assetCacheRemove = async function(pattern) {
}
if ( removedContent.length !== 0 ) {
await Promise.all([
µBlock.cacheStorage.remove(removedContent),
µBlock.cacheStorage.set({ assetCacheRegistry }),
cacheStorage.remove(removedContent),
cacheStorage.set({ assetCacheRegistry }),
]);
}
for ( let i = 0; i < removedEntries.length; i++ ) {
@ -658,7 +660,7 @@ const assetCacheMarkAsDirty = async function(pattern, exclude) {
mustSave = true;
}
if ( mustSave ) {
µBlock.cacheStorage.set({ assetCacheRegistry });
cacheStorage.set({ assetCacheRegistry });
}
};
@ -708,8 +710,8 @@ const saveUserAsset = function(assetKey, content) {
/******************************************************************************/
api.get = async function(assetKey, options = {}) {
if ( assetKey === µBlock.userFiltersPath ) {
assets.get = async function(assetKey, options = {}) {
if ( assetKey === µb.userFiltersPath ) {
return readUserAsset(assetKey);
}
@ -770,8 +772,8 @@ api.get = async function(assetKey, options = {}) {
continue;
}
const details = assetDetails.content === 'filters'
? await api.fetchFilterList(contentURL)
: await api.fetchText(contentURL);
? await assets.fetchFilterList(contentURL)
: await assets.fetchText(contentURL);
if ( details.content === '' ) { continue; }
if ( reIsExternalPath.test(contentURL) && options.dontCache !== true ) {
assetCacheWrite(assetKey, {
@ -832,8 +834,8 @@ const getRemote = async function(assetKey) {
if ( reIsExternalPath.test(contentURL) === false ) { continue; }
const result = assetDetails.content === 'filters'
? await api.fetchFilterList(contentURL)
: await api.fetchText(contentURL);
? await assets.fetchFilterList(contentURL)
: await assets.fetchText(contentURL);
// Failure
if ( stringIsNotEmpty(result.content) === false ) {
@ -861,7 +863,7 @@ const getRemote = async function(assetKey) {
/******************************************************************************/
api.put = async function(assetKey, content) {
assets.put = async function(assetKey, content) {
return reIsUserAsset.test(assetKey)
? await saveUserAsset(assetKey, content)
: await assetCacheWrite(assetKey, content);
@ -869,7 +871,7 @@ api.put = async function(assetKey, content) {
/******************************************************************************/
api.metadata = async function() {
assets.metadata = async function() {
await Promise.all([
getAssetSourceRegistry(),
getAssetCacheRegistry(),
@ -888,7 +890,7 @@ api.metadata = async function() {
assetEntry.isDefault =
assetEntry.off === undefined ||
assetEntry.off === true &&
µBlock.listMatchesEnvironment(assetEntry);
µb.listMatchesEnvironment(assetEntry);
}
if ( cacheEntry ) {
assetEntry.cached = true;
@ -911,13 +913,13 @@ api.metadata = async function() {
/******************************************************************************/
api.purge = assetCacheMarkAsDirty;
assets.purge = assetCacheMarkAsDirty;
api.remove = function(pattern) {
assets.remove = function(pattern) {
return assetCacheRemove(pattern);
};
api.rmrf = function() {
assets.rmrf = function() {
return assetCacheRemove(/./);
};
@ -1014,7 +1016,7 @@ const updateDone = function() {
fireNotification('after-assets-updated', { assetKeys: assetKeys });
};
api.updateStart = function(details) {
assets.updateStart = function(details) {
const oldUpdateDelay = updaterAssetDelay;
const newUpdateDelay = typeof details.delay === 'number'
? details.delay
@ -1031,7 +1033,7 @@ api.updateStart = function(details) {
updateFirst();
};
api.updateStop = function() {
assets.updateStop = function() {
if ( updaterTimer ) {
clearTimeout(updaterTimer);
updaterTimer = undefined;
@ -1041,15 +1043,13 @@ api.updateStop = function() {
}
};
api.isUpdating = function() {
assets.isUpdating = function() {
return updaterStatus === 'updating' &&
updaterAssetDelay <= µBlock.hiddenSettings.manualUpdateAssetFetchPeriod;
updaterAssetDelay <= µb.hiddenSettings.manualUpdateAssetFetchPeriod;
};
/******************************************************************************/
// Export
µBlock.assets = api;
export default assets;
/******************************************************************************/

View File

@ -24,6 +24,8 @@
/******************************************************************************/
import globals from './globals.js';
import logger from './logger.js';
import { FilteringContext } from './filtering-context.js';
import {
domainFromHostname,
@ -31,11 +33,6 @@ import {
originFromURI,
} from './uri-utils.js';
import { FilteringContext } from './filtering-context.js';
import { CompiledListWriter } from './static-filtering-io.js';
import { StaticFilteringParser } from './static-filtering-parser.js';
import { staticNetFilteringEngine } from './static-net-filtering.js';
/******************************************************************************/
// Not all platforms may have properly declared vAPI.webextFlavor.
@ -333,7 +330,6 @@ const µBlock = { // jshint ignore:line
if ( this.tabDomain === undefined ) {
void this.getTabDomain();
}
const logger = µBlock.logger;
const filters = this.filter;
// Many filters may have been applied to the current context
if ( Array.isArray(filters) === false ) {
@ -347,9 +343,6 @@ const µBlock = { // jshint ignore:line
};
µBlock.filteringContext = new µBlock.FilteringContext();
µBlock.CompiledListWriter = CompiledListWriter;
µBlock.StaticFilteringParser = StaticFilteringParser;
µBlock.staticNetFilteringEngine = staticNetFilteringEngine;
globals.µBlock = µBlock;

View File

@ -25,7 +25,8 @@
/******************************************************************************/
import µBlock from './background.js';
import lz4Codec from './lz4.js';
import µb from './background.js';
/******************************************************************************/
@ -54,431 +55,434 @@ import µBlock from './background.js';
// https://github.com/uBlockOrigin/uBlock-issues/issues/409
// Allow forcing the use of webext storage on Firefox.
µBlock.cacheStorage = (function() {
const STORAGE_NAME = 'uBlock0CacheStorage';
const STORAGE_NAME = 'uBlock0CacheStorage';
// Default to webext storage.
const storageLocal = webext.storage.local;
// Default to webext storage.
const localStorage = webext.storage.local;
const api = {
name: 'browser.storage.local',
get: localStorage.get,
set: localStorage.set,
remove: localStorage.remove,
clear: localStorage.clear,
getBytesInUse: localStorage.getBytesInUse,
select: function(selectedBackend) {
let actualBackend = selectedBackend;
if ( actualBackend === undefined || actualBackend === 'unset' ) {
actualBackend = vAPI.webextFlavor.soup.has('firefox')
? 'indexedDB'
: 'browser.storage.local';
}
if ( actualBackend === 'indexedDB' ) {
return selectIDB().then(success => {
if ( success || selectedBackend === 'indexedDB' ) {
clearWebext();
return 'indexedDB';
}
clearIDB();
return 'browser.storage.local';
});
}
if ( actualBackend === 'browser.storage.local' ) {
const cacheStorage = {
name: 'browser.storage.local',
get: storageLocal.get.bind(storageLocal),
set: storageLocal.set.bind(storageLocal),
remove: storageLocal.remove.bind(storageLocal),
clear: storageLocal.clear.bind(storageLocal),
// Not all platforms support getBytesInUse
getBytesInUse: storageLocal.getBytesInUse
? storageLocal.getBytesInUse.bind(storageLocal)
: undefined,
select: function(selectedBackend) {
let actualBackend = selectedBackend;
if ( actualBackend === undefined || actualBackend === 'unset' ) {
actualBackend = vAPI.webextFlavor.soup.has('firefox')
? 'indexedDB'
: 'browser.storage.local';
}
if ( actualBackend === 'indexedDB' ) {
return selectIDB().then(success => {
if ( success || selectedBackend === 'indexedDB' ) {
clearWebext();
return 'indexedDB';
}
clearIDB();
}
return Promise.resolve('browser.storage.local');
},
error: undefined
return 'browser.storage.local';
});
}
if ( actualBackend === 'browser.storage.local' ) {
clearIDB();
}
return Promise.resolve('browser.storage.local');
},
error: undefined
};
// Reassign API entries to that of indexedDB-based ones
const selectIDB = async function() {
let db;
let dbPromise;
let dbTimer;
const noopfn = function () {
};
// Reassign API entries to that of indexedDB-based ones
const selectIDB = async function() {
let db;
let dbPromise;
let dbTimer;
const disconnect = function() {
if ( dbTimer !== undefined ) {
clearTimeout(dbTimer);
dbTimer = undefined;
}
if ( db instanceof IDBDatabase ) {
db.close();
db = undefined;
}
};
const noopfn = function () {
};
const disconnect = function() {
if ( dbTimer !== undefined ) {
clearTimeout(dbTimer);
const keepAlive = function() {
if ( dbTimer !== undefined ) {
clearTimeout(dbTimer);
}
dbTimer = vAPI.setTimeout(
( ) => {
dbTimer = undefined;
}
if ( db instanceof IDBDatabase ) {
db.close();
db = undefined;
}
};
disconnect();
},
Math.max(
µb.hiddenSettings.autoUpdateAssetFetchPeriod * 2 * 1000,
180000
)
);
};
const keepAlive = function() {
if ( dbTimer !== undefined ) {
clearTimeout(dbTimer);
}
dbTimer = vAPI.setTimeout(
( ) => {
dbTimer = undefined;
disconnect();
},
Math.max(
µBlock.hiddenSettings.autoUpdateAssetFetchPeriod * 2 * 1000,
180000
)
);
};
// https://github.com/gorhill/uBlock/issues/3156
// I have observed that no event was fired in Tor Browser 7.0.7 +
// medium security level after the request to open the database was
// created. When this occurs, I have also observed that the `error`
// property was already set, so this means uBO can detect here whether
// the database can be opened successfully. A try-catch block is
// necessary when reading the `error` property because we are not
// allowed to read this propery outside of event handlers in newer
// implementation of IDBRequest (my understanding).
// https://github.com/gorhill/uBlock/issues/3156
// I have observed that no event was fired in Tor Browser 7.0.7 +
// medium security level after the request to open the database was
// created. When this occurs, I have also observed that the `error`
// property was already set, so this means uBO can detect here whether
// the database can be opened successfully. A try-catch block is
// necessary when reading the `error` property because we are not
// allowed to read this propery outside of event handlers in newer
// implementation of IDBRequest (my understanding).
const getDb = function() {
keepAlive();
if ( db !== undefined ) {
return Promise.resolve(db);
}
if ( dbPromise !== undefined ) {
return dbPromise;
}
dbPromise = new Promise(resolve => {
let req;
try {
req = indexedDB.open(STORAGE_NAME, 1);
if ( req.error ) {
console.log(req.error);
req = undefined;
}
} catch(ex) {
}
if ( req === undefined ) {
db = null;
dbPromise = undefined;
return resolve(null);
}
req.onupgradeneeded = function(ev) {
if ( ev.oldVersion === 1 ) { return; }
try {
const db = ev.target.result;
db.createObjectStore(STORAGE_NAME, { keyPath: 'key' });
} catch(ex) {
req.onerror();
}
};
req.onsuccess = function(ev) {
if ( resolve === undefined ) { return; }
req = undefined;
db = ev.target.result;
dbPromise = undefined;
resolve(db);
resolve = undefined;
};
req.onerror = req.onblocked = function() {
if ( resolve === undefined ) { return; }
req = undefined;
console.log(this.error);
db = null;
dbPromise = undefined;
resolve(null);
resolve = undefined;
};
setTimeout(( ) => {
if ( resolve === undefined ) { return; }
db = null;
dbPromise = undefined;
resolve(null);
resolve = undefined;
}, 5000);
});
const getDb = function() {
keepAlive();
if ( db !== undefined ) {
return Promise.resolve(db);
}
if ( dbPromise !== undefined ) {
return dbPromise;
};
const fromBlob = function(data) {
if ( data instanceof Blob === false ) {
return Promise.resolve(data);
}
return new Promise(resolve => {
const blobReader = new FileReader();
blobReader.onloadend = ev => {
resolve(new Uint8Array(ev.target.result));
};
blobReader.readAsArrayBuffer(data);
});
};
const toBlob = function(data) {
const value = data instanceof Uint8Array
? new Blob([ data ])
: data;
return Promise.resolve(value);
};
const compress = function(store, key, data) {
return µBlock.lz4Codec.encode(data, toBlob).then(value => {
store.push({ key, value });
});
};
const decompress = function(store, key, data) {
return µBlock.lz4Codec.decode(data, fromBlob).then(data => {
store[key] = data;
});
};
const getFromDb = async function(keys, keyvalStore, callback) {
if ( typeof callback !== 'function' ) { return; }
if ( keys.length === 0 ) { return callback(keyvalStore); }
const promises = [];
const gotOne = function() {
if ( typeof this.result !== 'object' ) { return; }
const { key, value } = this.result;
keyvalStore[key] = value;
if ( value instanceof Blob === false ) { return; }
promises.push(decompress(keyvalStore, key, value));
};
}
dbPromise = new Promise(resolve => {
let req;
try {
const db = await getDb();
if ( !db ) { return callback(); }
const transaction = db.transaction(STORAGE_NAME, 'readonly');
transaction.oncomplete =
transaction.onerror =
transaction.onabort = ( ) => {
Promise.all(promises).then(( ) => {
callback(keyvalStore);
});
};
const table = transaction.objectStore(STORAGE_NAME);
for ( const key of keys ) {
const req = table.get(key);
req.onsuccess = gotOne;
req.onerror = noopfn;
req = indexedDB.open(STORAGE_NAME, 1);
if ( req.error ) {
console.log(req.error);
req = undefined;
}
} catch(ex) {
}
catch(reason) {
console.info(`cacheStorage.getFromDb() failed: ${reason}`);
callback();
if ( req === undefined ) {
db = null;
dbPromise = undefined;
return resolve(null);
}
};
req.onupgradeneeded = function(ev) {
if ( ev.oldVersion === 1 ) { return; }
try {
const db = ev.target.result;
db.createObjectStore(STORAGE_NAME, { keyPath: 'key' });
} catch(ex) {
req.onerror();
}
};
req.onsuccess = function(ev) {
if ( resolve === undefined ) { return; }
req = undefined;
db = ev.target.result;
dbPromise = undefined;
resolve(db);
resolve = undefined;
};
req.onerror = req.onblocked = function() {
if ( resolve === undefined ) { return; }
req = undefined;
console.log(this.error);
db = null;
dbPromise = undefined;
resolve(null);
resolve = undefined;
};
setTimeout(( ) => {
if ( resolve === undefined ) { return; }
db = null;
dbPromise = undefined;
resolve(null);
resolve = undefined;
}, 5000);
});
return dbPromise;
};
const visitAllFromDb = async function(visitFn) {
const fromBlob = function(data) {
if ( data instanceof Blob === false ) {
return Promise.resolve(data);
}
return new Promise(resolve => {
const blobReader = new FileReader();
blobReader.onloadend = ev => {
resolve(new Uint8Array(ev.target.result));
};
blobReader.readAsArrayBuffer(data);
});
};
const toBlob = function(data) {
const value = data instanceof Uint8Array
? new Blob([ data ])
: data;
return Promise.resolve(value);
};
const compress = function(store, key, data) {
return lz4Codec.encode(data, toBlob).then(value => {
store.push({ key, value });
});
};
const decompress = function(store, key, data) {
return lz4Codec.decode(data, fromBlob).then(data => {
store[key] = data;
});
};
const getFromDb = async function(keys, keyvalStore, callback) {
if ( typeof callback !== 'function' ) { return; }
if ( keys.length === 0 ) { return callback(keyvalStore); }
const promises = [];
const gotOne = function() {
if ( typeof this.result !== 'object' ) { return; }
const { key, value } = this.result;
keyvalStore[key] = value;
if ( value instanceof Blob === false ) { return; }
promises.push(decompress(keyvalStore, key, value));
};
try {
const db = await getDb();
if ( !db ) { return visitFn(); }
if ( !db ) { return callback(); }
const transaction = db.transaction(STORAGE_NAME, 'readonly');
transaction.oncomplete =
transaction.onerror =
transaction.onabort = ( ) => visitFn();
transaction.onabort = ( ) => {
Promise.all(promises).then(( ) => {
callback(keyvalStore);
});
};
const table = transaction.objectStore(STORAGE_NAME);
const req = table.openCursor();
req.onsuccess = function(ev) {
let cursor = ev.target && ev.target.result;
if ( !cursor ) { return; }
let entry = cursor.value;
visitFn(entry);
cursor.continue();
};
};
const getAllFromDb = function(callback) {
if ( typeof callback !== 'function' ) { return; }
const promises = [];
const keyvalStore = {};
visitAllFromDb(entry => {
if ( entry === undefined ) {
Promise.all(promises).then(( ) => {
callback(keyvalStore);
});
return;
}
const { key, value } = entry;
keyvalStore[key] = value;
if ( entry.value instanceof Blob === false ) { return; }
promises.push(decompress(keyvalStore, key, value));
}).catch(reason => {
console.info(`cacheStorage.getAllFromDb() failed: ${reason}`);
callback();
});
};
// https://github.com/uBlockOrigin/uBlock-issues/issues/141
// Mind that IDBDatabase.transaction() and IDBObjectStore.put()
// can throw:
// https://developer.mozilla.org/en-US/docs/Web/API/IDBDatabase/transaction
// https://developer.mozilla.org/en-US/docs/Web/API/IDBObjectStore/put
const putToDb = async function(keyvalStore, callback) {
if ( typeof callback !== 'function' ) {
callback = noopfn;
}
const keys = Object.keys(keyvalStore);
if ( keys.length === 0 ) { return callback(); }
const promises = [ getDb() ];
const entries = [];
const dontCompress =
µBlock.hiddenSettings.cacheStorageCompression !== true;
for ( const key of keys ) {
const value = keyvalStore[key];
const isString = typeof value === 'string';
if ( isString === false || dontCompress ) {
entries.push({ key, value });
continue;
}
promises.push(compress(entries, key, value));
}
const finish = ( ) => {
if ( callback === undefined ) { return; }
let cb = callback;
callback = undefined;
cb();
};
try {
const results = await Promise.all(promises);
const db = results[0];
if ( !db ) { return callback(); }
const transaction = db.transaction(
STORAGE_NAME,
'readwrite'
);
transaction.oncomplete =
transaction.onerror =
transaction.onabort = finish;
const table = transaction.objectStore(STORAGE_NAME);
for ( const entry of entries ) {
table.put(entry);
}
} catch (ex) {
finish();
}
};
const deleteFromDb = async function(input, callback) {
if ( typeof callback !== 'function' ) {
callback = noopfn;
}
const keys = Array.isArray(input) ? input.slice() : [ input ];
if ( keys.length === 0 ) { return callback(); }
const finish = ( ) => {
if ( callback === undefined ) { return; }
let cb = callback;
callback = undefined;
cb();
};
try {
const db = await getDb();
if ( !db ) { return callback(); }
const transaction = db.transaction(STORAGE_NAME, 'readwrite');
transaction.oncomplete =
transaction.onerror =
transaction.onabort = finish;
const table = transaction.objectStore(STORAGE_NAME);
for ( const key of keys ) {
table.delete(key);
}
} catch (ex) {
finish();
}
};
const clearDb = async function(callback) {
if ( typeof callback !== 'function' ) {
callback = noopfn;
}
try {
const db = await getDb();
if ( !db ) { return callback(); }
const transaction = db.transaction(STORAGE_NAME, 'readwrite');
transaction.oncomplete =
transaction.onerror =
transaction.onabort = ( ) => {
callback();
};
transaction.objectStore(STORAGE_NAME).clear();
}
catch(reason) {
console.info(`cacheStorage.clearDb() failed: ${reason}`);
callback();
}
};
await getDb();
if ( !db ) { return false; }
api.name = 'indexedDB';
api.get = function get(keys) {
return new Promise(resolve => {
if ( keys === null ) {
return getAllFromDb(bin => resolve(bin));
}
let toRead, output = {};
if ( typeof keys === 'string' ) {
toRead = [ keys ];
} else if ( Array.isArray(keys) ) {
toRead = keys;
} else /* if ( typeof keys === 'object' ) */ {
toRead = Object.keys(keys);
output = keys;
}
getFromDb(toRead, output, bin => resolve(bin));
});
};
api.set = function set(keys) {
return new Promise(resolve => {
putToDb(keys, details => resolve(details));
});
};
api.remove = function remove(keys) {
return new Promise(resolve => {
deleteFromDb(keys, ( ) => resolve());
});
};
api.clear = function clear() {
return new Promise(resolve => {
clearDb(( ) => resolve());
});
};
api.getBytesInUse = function getBytesInUse() {
return Promise.resolve(0);
};
return true;
};
// https://github.com/uBlockOrigin/uBlock-issues/issues/328
// Delete cache-related entries from webext storage.
const clearWebext = async function() {
const bin = await webext.storage.local.get('assetCacheRegistry');
if (
bin instanceof Object === false ||
bin.assetCacheRegistry instanceof Object === false
) {
return;
}
const toRemove = [
'assetCacheRegistry',
'assetSourceRegistry',
'resourcesSelfie',
'selfie'
];
for ( const key in bin.assetCacheRegistry ) {
if ( bin.assetCacheRegistry.hasOwnProperty(key) ) {
toRemove.push('cache/' + key);
const req = table.get(key);
req.onsuccess = gotOne;
req.onerror = noopfn;
}
}
webext.storage.local.remove(toRemove);
catch(reason) {
console.info(`cacheStorage.getFromDb() failed: ${reason}`);
callback();
}
};
const clearIDB = function() {
const visitAllFromDb = async function(visitFn) {
const db = await getDb();
if ( !db ) { return visitFn(); }
const transaction = db.transaction(STORAGE_NAME, 'readonly');
transaction.oncomplete =
transaction.onerror =
transaction.onabort = ( ) => visitFn();
const table = transaction.objectStore(STORAGE_NAME);
const req = table.openCursor();
req.onsuccess = function(ev) {
let cursor = ev.target && ev.target.result;
if ( !cursor ) { return; }
let entry = cursor.value;
visitFn(entry);
cursor.continue();
};
};
const getAllFromDb = function(callback) {
if ( typeof callback !== 'function' ) { return; }
const promises = [];
const keyvalStore = {};
visitAllFromDb(entry => {
if ( entry === undefined ) {
Promise.all(promises).then(( ) => {
callback(keyvalStore);
});
return;
}
const { key, value } = entry;
keyvalStore[key] = value;
if ( entry.value instanceof Blob === false ) { return; }
promises.push(decompress(keyvalStore, key, value));
}).catch(reason => {
console.info(`cacheStorage.getAllFromDb() failed: ${reason}`);
callback();
});
};
// https://github.com/uBlockOrigin/uBlock-issues/issues/141
// Mind that IDBDatabase.transaction() and IDBObjectStore.put()
// can throw:
// https://developer.mozilla.org/en-US/docs/Web/API/IDBDatabase/transaction
// https://developer.mozilla.org/en-US/docs/Web/API/IDBObjectStore/put
const putToDb = async function(keyvalStore, callback) {
if ( typeof callback !== 'function' ) {
callback = noopfn;
}
const keys = Object.keys(keyvalStore);
if ( keys.length === 0 ) { return callback(); }
const promises = [ getDb() ];
const entries = [];
const dontCompress =
µb.hiddenSettings.cacheStorageCompression !== true;
for ( const key of keys ) {
const value = keyvalStore[key];
const isString = typeof value === 'string';
if ( isString === false || dontCompress ) {
entries.push({ key, value });
continue;
}
promises.push(compress(entries, key, value));
}
const finish = ( ) => {
if ( callback === undefined ) { return; }
let cb = callback;
callback = undefined;
cb();
};
try {
indexedDB.deleteDatabase(STORAGE_NAME);
} catch(ex) {
const results = await Promise.all(promises);
const db = results[0];
if ( !db ) { return callback(); }
const transaction = db.transaction(
STORAGE_NAME,
'readwrite'
);
transaction.oncomplete =
transaction.onerror =
transaction.onabort = finish;
const table = transaction.objectStore(STORAGE_NAME);
for ( const entry of entries ) {
table.put(entry);
}
} catch (ex) {
finish();
}
};
return api;
}());
const deleteFromDb = async function(input, callback) {
if ( typeof callback !== 'function' ) {
callback = noopfn;
}
const keys = Array.isArray(input) ? input.slice() : [ input ];
if ( keys.length === 0 ) { return callback(); }
const finish = ( ) => {
if ( callback === undefined ) { return; }
let cb = callback;
callback = undefined;
cb();
};
try {
const db = await getDb();
if ( !db ) { return callback(); }
const transaction = db.transaction(STORAGE_NAME, 'readwrite');
transaction.oncomplete =
transaction.onerror =
transaction.onabort = finish;
const table = transaction.objectStore(STORAGE_NAME);
for ( const key of keys ) {
table.delete(key);
}
} catch (ex) {
finish();
}
};
const clearDb = async function(callback) {
if ( typeof callback !== 'function' ) {
callback = noopfn;
}
try {
const db = await getDb();
if ( !db ) { return callback(); }
const transaction = db.transaction(STORAGE_NAME, 'readwrite');
transaction.oncomplete =
transaction.onerror =
transaction.onabort = ( ) => {
callback();
};
transaction.objectStore(STORAGE_NAME).clear();
}
catch(reason) {
console.info(`cacheStorage.clearDb() failed: ${reason}`);
callback();
}
};
await getDb();
if ( !db ) { return false; }
cacheStorage.name = 'indexedDB';
cacheStorage.get = function get(keys) {
return new Promise(resolve => {
if ( keys === null ) {
return getAllFromDb(bin => resolve(bin));
}
let toRead, output = {};
if ( typeof keys === 'string' ) {
toRead = [ keys ];
} else if ( Array.isArray(keys) ) {
toRead = keys;
} else /* if ( typeof keys === 'object' ) */ {
toRead = Object.keys(keys);
output = keys;
}
getFromDb(toRead, output, bin => resolve(bin));
});
};
cacheStorage.set = function set(keys) {
return new Promise(resolve => {
putToDb(keys, details => resolve(details));
});
};
cacheStorage.remove = function remove(keys) {
return new Promise(resolve => {
deleteFromDb(keys, ( ) => resolve());
});
};
cacheStorage.clear = function clear() {
return new Promise(resolve => {
clearDb(( ) => resolve());
});
};
cacheStorage.getBytesInUse = function getBytesInUse() {
return Promise.resolve(0);
};
return true;
};
// https://github.com/uBlockOrigin/uBlock-issues/issues/328
// Delete cache-related entries from webext storage.
const clearWebext = async function() {
const bin = await webext.storage.local.get('assetCacheRegistry');
if (
bin instanceof Object === false ||
bin.assetCacheRegistry instanceof Object === false
) {
return;
}
const toRemove = [
'assetCacheRegistry',
'assetSourceRegistry',
'resourcesSelfie',
'selfie'
];
for ( const key in bin.assetCacheRegistry ) {
if ( bin.assetCacheRegistry.hasOwnProperty(key) ) {
toRemove.push('cache/' + key);
}
}
webext.storage.local.remove(toRemove);
};
const clearIDB = function() {
try {
indexedDB.deleteDatabase(STORAGE_NAME);
} catch(ex) {
}
};
/******************************************************************************/
export default cacheStorage;
/******************************************************************************/

View File

@ -24,24 +24,23 @@
/******************************************************************************/
import { hostnameFromURI } from './uri-utils.js';
import µBlock from './background.js';
import µb from './background.js';
/******************************************************************************/
µBlock.canUseShortcuts = vAPI.commands instanceof Object;
µb.canUseShortcuts = vAPI.commands instanceof Object;
// https://github.com/uBlockOrigin/uBlock-issues/issues/386
// Firefox 74 and above has complete shotcut assignment user interface.
µBlock.canUpdateShortcuts =
µBlock.canUseShortcuts &&
µb.canUpdateShortcuts =
µb.canUseShortcuts &&
vAPI.webextFlavor.soup.has('firefox') &&
typeof vAPI.commands.update === 'function';
if ( µBlock.canUpdateShortcuts ) {
if ( µb.canUpdateShortcuts ) {
self.addEventListener(
'webextFlavor',
( ) => {
const µb = µBlock;
µb.canUpdateShortcuts = vAPI.webextFlavor.major < 74;
if ( µb.canUpdateShortcuts === false ) { return; }
vAPI.storage.get('commandShortcuts').then(bin => {
@ -65,7 +64,7 @@ if ( µBlock.canUpdateShortcuts ) {
// *****************************************************************************
// start of local namespace
if ( µBlock.canUseShortcuts === false ) { return; }
if ( µb.canUseShortcuts === false ) { return; }
const relaxBlockingMode = (( ) => {
const reloadTimers = new Map();
@ -73,7 +72,6 @@ const relaxBlockingMode = (( ) => {
return function(tab) {
if ( tab instanceof Object === false || tab.id <= 0 ) { return; }
const µb = µBlock;
const normalURL = µb.normalizeTabURL(tab.id, tab.url);
if ( µb.getNetFilteringSwitch(normalURL) === false ) { return; }
@ -161,8 +159,6 @@ const relaxBlockingMode = (( ) => {
})();
vAPI.commands.onCommand.addListener(async command => {
const µb = µBlock;
switch ( command ) {
case 'launch-element-picker':
case 'launch-element-zapper': {

View File

@ -21,15 +21,39 @@
'use strict';
self.log = (function() {
const noopFunc = function() {};
const info = function(s) { console.log(`[uBO] ${s}`); };
return {
get verbosity( ) { return; },
set verbosity(level) {
this.info = console.info = level === 'info' ? info : noopFunc;
},
info: noopFunc,
print: info,
/******************************************************************************/
function ubologSet(state = false) {
if ( state ) {
if ( ubolog.process instanceof Function ) {
ubolog.process();
}
ubolog = ubologDo;
} else {
ubolog = ubologIgnore;
}
}
function ubologDo(...args) {
console.info('[uBO]', ...args);
}
function ubologIgnore() {
}
let ubolog = (( ) => {
const pending = [];
const store = function(...args) {
pending.push(args);
};
store.process = function() {
for ( const args of pending ) {
ubologDo(...args);
}
};
return store;
})();
/******************************************************************************/
export { ubolog, ubologSet };

View File

@ -23,11 +23,11 @@
/******************************************************************************/
import µBlock from './background.js';
import µb from './background.js';
/******************************************************************************/
µBlock.contextMenu = (( ) => {
const contextMenu = (( ) => {
/******************************************************************************/
@ -61,8 +61,8 @@ const onBlockElement = function(details, tab) {
}
}
µBlock.epickerArgs.mouse = true;
µBlock.elementPickerExec(tab.id, 0, `${tagName}\t${src}`);
µb.epickerArgs.mouse = true;
µb.elementPickerExec(tab.id, 0, `${tagName}\t${src}`);
};
/******************************************************************************/
@ -70,8 +70,8 @@ const onBlockElement = function(details, tab) {
const onBlockElementInFrame = function(details, tab) {
if ( tab === undefined ) { return; }
if ( /^https?:\/\//.test(details.frameUrl) === false ) { return; }
µBlock.epickerArgs.mouse = false;
µBlock.elementPickerExec(tab.id, details.frameId);
µb.epickerArgs.mouse = false;
µb.elementPickerExec(tab.id, details.frameId);
};
/******************************************************************************/
@ -87,7 +87,7 @@ const onSubscribeToList = function(details) {
const url = parsedURL.searchParams.get('location');
if ( url === null ) { return; }
const title = parsedURL.searchParams.get('title') || '?';
const hash = µBlock.selectedFilterLists.indexOf(parsedURL) !== -1
const hash = µb.selectedFilterLists.indexOf(parsedURL) !== -1
? '#subscribed'
: '';
vAPI.tabs.open({
@ -104,7 +104,7 @@ const onSubscribeToList = function(details) {
const onTemporarilyAllowLargeMediaElements = function(details, tab) {
if ( tab === undefined ) { return; }
let pageStore = µBlock.pageStoreFromTabId(tab.id);
const pageStore = µb.pageStoreFromTabId(tab.id);
if ( pageStore === null ) { return; }
pageStore.temporarilyAllowLargeMediaElements(true);
};
@ -166,8 +166,8 @@ let currentBits = 0;
const update = function(tabId = undefined) {
let newBits = 0;
if ( µBlock.userSettings.contextMenuEnabled && tabId !== undefined ) {
const pageStore = µBlock.pageStoreFromTabId(tabId);
if ( µb.userSettings.contextMenuEnabled && tabId !== undefined ) {
const pageStore = µb.pageStoreFromTabId(tabId);
if ( pageStore && pageStore.getNetFilteringSwitch() ) {
if ( pageStore.shouldApplySpecificCosmeticFilters(0) ) {
newBits |= 0b0001;
@ -206,7 +206,7 @@ const update = function(tabId = undefined) {
// looked up after closing a window.
vAPI.contextMenu.onMustUpdate = async function(tabId = undefined) {
if ( µBlock.userSettings.contextMenuEnabled === false ) {
if ( µb.userSettings.contextMenuEnabled === false ) {
return update();
}
if ( tabId !== undefined ) {
@ -222,3 +222,9 @@ return { update: vAPI.contextMenu.onMustUpdate };
/******************************************************************************/
})();
/******************************************************************************/
export default contextMenu;
/******************************************************************************/

View File

@ -23,17 +23,22 @@
/******************************************************************************/
import logger from './logger.js';
import µb from './background.js';
import {
StaticExtFilteringHostnameDB,
StaticExtFilteringSessionDB,
} from './static-ext-filtering-db.js';
import {
domainFromHostname,
entityFromDomain,
hostnameFromURI,
} from './uri-utils.js';
import µBlock from './background.js';
/******************************************************************************/
const µb = µBlock;
const cosmeticSurveyingMissCountMax =
parseInt(vAPI.localStorage.getItem('cosmeticSurveyingMissCountMax'), 10) ||
15;
@ -205,10 +210,10 @@ const FilterContainer = function() {
this.selectorCacheTimer = null;
// specific filters
this.specificFilters = new µb.staticExtFilteringEngine.HostnameBasedDB(2);
this.specificFilters = new StaticExtFilteringHostnameDB(2);
// temporary filters
this.sessionFilterDB = new µb.staticExtFilteringEngine.SessionDB();
this.sessionFilterDB = new StaticExtFilteringSessionDB();
// low generic cosmetic filters, organized by id/class then simple/complex.
this.lowlyGeneric = Object.create(null);
@ -390,7 +395,7 @@ FilterContainer.prototype.compileGenericHideSelector = function(
const { raw, compiled, pseudoclass } = parser.result;
if ( compiled === undefined ) {
const who = writer.properties.get('name') || '?';
µb.logger.writeOne({
logger.writeOne({
realm: 'message',
type: 'error',
text: `Invalid generic cosmetic filter in ${who}: ${raw}`
@ -437,7 +442,7 @@ FilterContainer.prototype.compileGenericHideSelector = function(
return this.compileSpecificSelector(parser, '', false, writer);
}
const who = writer.properties.get('name') || '?';
µb.logger.writeOne({
logger.writeOne({
realm: 'message',
type: 'error',
text: `Invalid generic cosmetic filter in ${who}: ##${raw}`
@ -493,7 +498,7 @@ FilterContainer.prototype.compileGenericUnhideSelector = function(
const { raw, compiled } = parser.result;
if ( compiled === undefined ) {
const who = writer.properties.get('name') || '?';
µb.logger.writeOne({
logger.writeOne({
realm: 'message',
type: 'error',
text: `Invalid cosmetic filter in ${who}: #@#${raw}`
@ -523,7 +528,7 @@ FilterContainer.prototype.compileSpecificSelector = function(
const { raw, compiled, exception } = parser.result;
if ( compiled === undefined ) {
const who = writer.properties.get('name') || '?';
µb.logger.writeOne({
logger.writeOne({
realm: 'message',
type: 'error',
text: `Invalid cosmetic filter in ${who}: ##${raw}`
@ -1169,8 +1174,8 @@ FilterContainer.prototype.benchmark = async function() {
/******************************************************************************/
// Export
const cosmeticFilteringEngine = new FilterContainer();
µBlock.cosmeticFilteringEngine = new FilterContainer();
export default cosmeticFilteringEngine;
/******************************************************************************/

View File

@ -28,9 +28,9 @@
import '../lib/punycode.js';
import globals from './globals.js';
import µb from './background.js';
import { domainFromHostname } from './uri-utils.js';
import { LineIterator } from './text-iterators.js';
import µBlock from './background.js';
/******************************************************************************/
@ -266,7 +266,7 @@ const Matrix = class {
evaluateCellZ(srcHostname, desHostname, type) {
µBlock.decomposeHostname(srcHostname, this.decomposedSource);
µb.decomposeHostname(srcHostname, this.decomposedSource);
this.type = type;
const bitOffset = typeBitOffsets[type];
for ( const shn of this.decomposedSource ) {
@ -296,7 +296,7 @@ const Matrix = class {
// Precedence: from most specific to least specific
// Specific-destination, any party, any type
µBlock.decomposeHostname(desHostname, this.decomposedDestination);
µb.decomposeHostname(desHostname, this.decomposedDestination);
for ( const dhn of this.decomposedDestination ) {
if ( dhn === '*' ) { break; }
this.y = dhn;
@ -509,13 +509,13 @@ const Matrix = class {
async benchmark() {
const requests = await µBlock.loadBenchmarkDataset();
const requests = await µb.loadBenchmarkDataset();
if ( Array.isArray(requests) === false || requests.length === 0 ) {
log.print('No requests found to benchmark');
console.info('No requests found to benchmark');
return;
}
log.print(`Benchmarking sessionFirewall.evaluateCellZY()...`);
const fctxt = µBlock.filteringContext.duplicate();
console.info(`Benchmarking sessionFirewall.evaluateCellZY()...`);
const fctxt = µb.filteringContext.duplicate();
const t0 = self.performance.now();
for ( const request of requests ) {
fctxt.setURL(request.url);
@ -529,8 +529,8 @@ const Matrix = class {
}
const t1 = self.performance.now();
const dur = t1 - t0;
log.print(`Evaluated ${requests.length} requests in ${dur.toFixed(0)} ms`);
log.print(`\tAverage: ${(dur / requests.length).toFixed(3)} ms per request`);
console.info(`Evaluated ${requests.length} requests in ${dur.toFixed(0)} ms`);
console.info(`\tAverage: ${(dur / requests.length).toFixed(3)} ms per request`);
}
};
@ -544,11 +544,9 @@ Matrix.prototype.magicId = 1;
/******************************************************************************/
// Export
const sessionFirewall = new Matrix();
const permanentFirewall = new Matrix();
µBlock.Firewall = Matrix;
µBlock.sessionFirewall = new µBlock.Firewall();
µBlock.permanentFirewall = new µBlock.Firewall();
export { permanentFirewall, sessionFirewall };
/******************************************************************************/

View File

@ -28,8 +28,8 @@
import '../lib/punycode.js';
import globals from './globals.js';
import µb from './background.js';
import { LineIterator } from './text-iterators.js';
import µBlock from './background.js';
/******************************************************************************/
@ -228,7 +228,7 @@ HnSwitches.prototype.evaluateZ = function(switchName, hostname) {
return false;
}
this.n = switchName;
µBlock.decomposeHostname(hostname, this.decomposedSource);
µb.decomposeHostname(hostname, this.decomposedSource);
for ( const shn of this.decomposedSource ) {
let bits = this.switches.get(shn);
if ( bits !== undefined ) {
@ -323,11 +323,9 @@ HnSwitches.prototype.removeFromRuleParts = function(parts) {
/******************************************************************************/
// Export
const sessionSwitches = new HnSwitches();
const permanentSwitches = new HnSwitches();
µBlock.HnSwitches = HnSwitches;
µBlock.sessionSwitches = new HnSwitches();
µBlock.permanentSwitches = new HnSwitches();
export { permanentSwitches, sessionSwitches };
/******************************************************************************/

View File

@ -796,7 +796,7 @@ const getWasmModule = (( ) => {
).then(
WebAssembly.compileStreaming
).catch(reason => {
log.info(reason);
console.info(reason);
});
return wasmModulePromise;

View File

@ -23,22 +23,28 @@
/******************************************************************************/
import µBlock from './background.js';
import logger from './logger.js';
import µb from './background.js';
import { sessionFirewall } from './dynamic-net-filtering.js';
import {
StaticExtFilteringHostnameDB,
StaticExtFilteringSessionDB,
} from './static-ext-filtering-db.js';
/******************************************************************************/
const µb = µBlock;
const pselectors = new Map();
const duplicates = new Set();
const filterDB = new µb.staticExtFilteringEngine.HostnameBasedDB(2);
const sessionFilterDB = new µb.staticExtFilteringEngine.SessionDB();
const filterDB = new StaticExtFilteringHostnameDB(2);
const sessionFilterDB = new StaticExtFilteringSessionDB();
let acceptedCount = 0;
let discardedCount = 0;
let docRegister;
const api = {
const htmlFilteringEngine = {
get acceptedCount() {
return acceptedCount;
},
@ -237,7 +243,7 @@ PSelector.prototype.operatorToTaskMap = new Map([
PSelector.prototype.invalid = false;
const logOne = function(details, exception, selector) {
µBlock.filteringContext
µb.filteringContext
.duplicate()
.fromTabId(details.tabId)
.setRealm('extended')
@ -263,7 +269,7 @@ const applyProceduralSelector = function(details, selector) {
node.remove();
modified = true;
}
if ( modified && µb.logger.enabled ) {
if ( modified && logger.enabled ) {
logOne(details, 0, pselector.raw);
}
return modified;
@ -276,13 +282,13 @@ const applyCSSSelector = function(details, selector) {
node.remove();
modified = true;
}
if ( modified && µb.logger.enabled ) {
if ( modified && logger.enabled ) {
logOne(details, 0, selector);
}
return modified;
};
api.reset = function() {
htmlFilteringEngine.reset = function() {
filterDB.clear();
pselectors.clear();
duplicates.clear();
@ -290,16 +296,16 @@ api.reset = function() {
discardedCount = 0;
};
api.freeze = function() {
htmlFilteringEngine.freeze = function() {
duplicates.clear();
filterDB.collectGarbage();
};
api.compile = function(parser, writer) {
htmlFilteringEngine.compile = function(parser, writer) {
const { raw, compiled, exception } = parser.result;
if ( compiled === undefined ) {
const who = writer.properties.get('name') || '?';
µb.logger.writeOne({
logger.writeOne({
realm: 'message',
type: 'error',
text: `Invalid HTML filter in ${who}: ##${raw}`
@ -325,14 +331,14 @@ api.compile = function(parser, writer) {
}
};
api.compileTemporary = function(parser) {
htmlFilteringEngine.compileTemporary = function(parser) {
return {
session: sessionFilterDB,
selector: parser.result.compiled,
};
};
api.fromCompiledContent = function(reader) {
htmlFilteringEngine.fromCompiledContent = function(reader) {
// Don't bother loading filters if stream filtering is not supported.
if ( µb.canFilterResponseData === false ) { return; }
@ -351,11 +357,11 @@ api.fromCompiledContent = function(reader) {
}
};
api.getSession = function() {
htmlFilteringEngine.getSession = function() {
return sessionFilterDB;
};
api.retrieve = function(details) {
htmlFilteringEngine.retrieve = function(details) {
const hostname = details.hostname;
const plains = new Set();
@ -384,7 +390,7 @@ api.retrieve = function(details) {
// Do not filter if the site is under an `allow` rule.
if (
µb.userSettings.advancedUserEnabled &&
µb.sessionFirewall.evaluateCellZY(hostname, hostname, '*') === 2
sessionFirewall.evaluateCellZY(hostname, hostname, '*') === 2
) {
return;
}
@ -413,7 +419,7 @@ api.retrieve = function(details) {
}
};
api.apply = function(doc, details) {
htmlFilteringEngine.apply = function(doc, details) {
docRegister = doc;
let modified = false;
for ( const selector of details.selectors.plains ) {
@ -430,19 +436,17 @@ api.apply = function(doc, details) {
return modified;
};
api.toSelfie = function() {
htmlFilteringEngine.toSelfie = function() {
return filterDB.toSelfie();
};
api.fromSelfie = function(selfie) {
htmlFilteringEngine.fromSelfie = function(selfie) {
filterDB.fromSelfie(selfie);
pselectors.clear();
};
/******************************************************************************/
// Export
µBlock.htmlFilteringEngine = api;
export default htmlFilteringEngine;
/******************************************************************************/

View File

@ -23,15 +23,21 @@
/******************************************************************************/
import logger from './logger.js';
import µb from './background.js';
import { entityFromDomain } from './uri-utils.js';
import µBlock from './background.js';
import { sessionFirewall } from './dynamic-net-filtering.js';
import {
StaticExtFilteringHostnameDB,
StaticExtFilteringSessionDB,
} from './static-ext-filtering-db.js';
/******************************************************************************/
const µb = µBlock;
const duplicates = new Set();
const filterDB = new µb.staticExtFilteringEngine.HostnameBasedDB(1);
const sessionFilterDB = new µb.staticExtFilteringEngine.SessionDB();
const filterDB = new StaticExtFilteringHostnameDB(1);
const sessionFilterDB = new StaticExtFilteringSessionDB();
const $headers = new Set();
const $exceptions = new Set();
@ -62,7 +68,7 @@ const logOne = function(isException, token, fctxt) {
.toLogger();
};
const api = {
const httpheaderFilteringEngine = {
get acceptedCount() {
return acceptedCount;
},
@ -71,19 +77,19 @@ const api = {
}
};
api.reset = function() {
httpheaderFilteringEngine.reset = function() {
filterDB.clear();
duplicates.clear();
acceptedCount = 0;
discardedCount = 0;
};
api.freeze = function() {
httpheaderFilteringEngine.freeze = function() {
duplicates.clear();
filterDB.collectGarbage();
};
api.compile = function(parser, writer) {
httpheaderFilteringEngine.compile = function(parser, writer) {
writer.select(µb.compiledHTTPHeaderSection);
const { compiled, exception } = parser.result;
@ -117,7 +123,7 @@ api.compile = function(parser, writer) {
}
};
api.compileTemporary = function(parser) {
httpheaderFilteringEngine.compileTemporary = function(parser) {
return {
session: sessionFilterDB,
selector: parser.result.compiled.slice(15, -1),
@ -129,7 +135,7 @@ api.compileTemporary = function(parser) {
// ^ ^
// 15 -1
api.fromCompiledContent = function(reader) {
httpheaderFilteringEngine.fromCompiledContent = function(reader) {
reader.select(µb.compiledHTTPHeaderSection);
while ( reader.next() ) {
@ -146,11 +152,11 @@ api.fromCompiledContent = function(reader) {
}
};
api.getSession = function() {
httpheaderFilteringEngine.getSession = function() {
return sessionFilterDB;
};
api.apply = function(fctxt, headers) {
httpheaderFilteringEngine.apply = function(fctxt, headers) {
if ( filterDB.size === 0 ) { return; }
const hostname = fctxt.getHostname();
@ -178,13 +184,12 @@ api.apply = function(fctxt, headers) {
// Do not filter response headers if the site is under an `allow` rule.
if (
µb.userSettings.advancedUserEnabled &&
µb.sessionFirewall.evaluateCellZY(hostname, hostname, '*') === 2
sessionFirewall.evaluateCellZY(hostname, hostname, '*') === 2
) {
return;
}
const hasGlobalException = $exceptions.has('');
const loggerEnabled = µb.logger.enabled;
let modified = false;
@ -194,13 +199,13 @@ api.apply = function(fctxt, headers) {
if ( i === -1 ) { break; }
const isExcepted = hasGlobalException || $exceptions.has(name);
if ( isExcepted ) {
if ( loggerEnabled ) {
if ( logger.enabled ) {
logOne(true, hasGlobalException ? '' : name, fctxt);
}
break;
}
headers.splice(i, 1);
if ( loggerEnabled ) {
if ( logger.enabled ) {
logOne(false, name, fctxt);
}
modified = true;
@ -210,18 +215,16 @@ api.apply = function(fctxt, headers) {
return modified;
};
api.toSelfie = function() {
httpheaderFilteringEngine.toSelfie = function() {
return filterDB.toSelfie();
};
api.fromSelfie = function(selfie) {
httpheaderFilteringEngine.fromSelfie = function(selfie) {
filterDB.fromSelfie(selfie);
};
/******************************************************************************/
// Export
µb.httpheaderFilteringEngine = api;
export default httpheaderFilteringEngine;
/******************************************************************************/

View File

@ -23,10 +23,6 @@
/******************************************************************************/
import µBlock from './background.js';
/******************************************************************************/
let buffer = null;
let lastReadTime = 0;
let writePtr = 0;
@ -40,10 +36,10 @@ const janitor = ( ) => {
buffer !== null &&
lastReadTime < (Date.now() - logBufferObsoleteAfter)
) {
api.enabled = false;
logger.enabled = false;
buffer = null;
writePtr = 0;
api.ownerId = undefined;
logger.ownerId = undefined;
vAPI.messaging.broadcast({ what: 'loggerDisabled' });
}
if ( buffer !== null ) {
@ -58,7 +54,7 @@ const boxEntry = function(details) {
return JSON.stringify(details);
};
const api = {
const logger = {
enabled: false,
ownerId: undefined,
writeOne: function(details) {
@ -87,8 +83,6 @@ const api = {
/******************************************************************************/
// Export
µBlock.logger = api;
export default logger;
/******************************************************************************/

View File

@ -25,7 +25,7 @@
/******************************************************************************/
import µBlock from './background.js';
import µb from './background.js';
/*******************************************************************************
@ -46,7 +46,7 @@ let ttlTimer;
let ttlDelay = 60000;
const init = function() {
ttlDelay = µBlock.hiddenSettings.autoUpdateAssetFetchPeriod * 1000 + 15000;
ttlDelay = µb.hiddenSettings.autoUpdateAssetFetchPeriod * 1000 + 15000;
if ( lz4CodecInstance === null ) {
return Promise.resolve(null);
}
@ -55,7 +55,7 @@ const init = function() {
}
if ( pendingInitialization === undefined ) {
let flavor;
if ( µBlock.hiddenSettings.disableWebAssembly === true ) {
if ( µb.hiddenSettings.disableWebAssembly === true ) {
flavor = 'js';
}
pendingInitialization = lz4BlockCodec.createInstance(flavor)
@ -155,7 +155,7 @@ const decodeValue = function(inputArray) {
return s;
};
µBlock.lz4Codec = {
const lz4Codec = {
// Arguments:
// dataIn: must be a string
// Returns:
@ -199,3 +199,7 @@ const decodeValue = function(inputArray) {
};
/******************************************************************************/
export default lz4Codec;
/******************************************************************************/

View File

@ -26,7 +26,30 @@
import '../lib/publicsuffixlist/publicsuffixlist.js';
import '../lib/punycode.js';
import cacheStorage from './cachestorage.js';
import cosmeticFilteringEngine from './cosmetic-filtering.js';
import globals from './globals.js';
import logger from './logger.js';
import lz4Codec from './lz4.js';
import io from './assets.js';
import scriptletFilteringEngine from './scriptlet-filtering.js';
import staticExtFilteringEngine from './static-ext-filtering.js';
import staticFilteringReverseLookup from './reverselookup.js';
import staticNetFilteringEngine from './static-net-filtering.js';
import µb from './background.js';
import { redirectEngine } from './redirect-engine.js';
import { StaticFilteringParser } from './static-filtering-parser.js';
import { webRequest } from './traffic.js';
import {
permanentFirewall,
sessionFirewall,
} from './dynamic-net-filtering.js';
import {
permanentSwitches,
sessionSwitches,
} from './hnswitches.js';
import {
domainFromHostname,
@ -36,8 +59,10 @@ import {
isNetworkURI,
} from './uri-utils.js';
import { StaticFilteringParser } from './static-filtering-parser.js';
import µBlock from './background.js';
import {
permanentURLFiltering,
sessionURLFiltering,
} from './url-net-filtering.js';
/******************************************************************************/
@ -56,8 +81,6 @@ import µBlock from './background.js';
{
// >>>>> start of local scope
const µb = µBlock;
const clickToLoad = function(request, sender) {
const { tabId, frameId } = sender;
if ( tabId === undefined || frameId === undefined ) { return false; }
@ -81,7 +104,7 @@ const onMessage = function(request, sender, callback) {
switch ( request.what ) {
case 'getAssetContent':
// https://github.com/chrisaljoudi/uBlock/issues/417
µb.assets.get(request.url, {
io.get(request.url, {
dontCache: true,
needSourceURL: true,
}).then(result => {
@ -90,7 +113,7 @@ const onMessage = function(request, sender, callback) {
return;
case 'listsFromNetFilter':
µb.staticFilteringReverseLookup.fromNetFilter(
staticFilteringReverseLookup.fromNetFilter(
request.rawFilter
).then(response => {
callback(response);
@ -98,7 +121,7 @@ const onMessage = function(request, sender, callback) {
return;
case 'listsFromCosmeticFilter':
µb.staticFilteringReverseLookup.fromCosmeticFilter(
staticFilteringReverseLookup.fromCosmeticFilter(
request
).then(response => {
callback(response);
@ -115,9 +138,9 @@ const onMessage = function(request, sender, callback) {
case 'sfneBenchmark':
µb.loadBenchmarkDataset().then(requests => {
µb.staticNetFilteringEngine.benchmark(
staticNetFilteringEngine.benchmark(
requests,
{ redirectEngine: µb.redirectEngine }
{ redirectEngine }
).then(result => {
callback(result);
});
@ -146,7 +169,7 @@ const onMessage = function(request, sender, callback) {
case 'forceUpdateAssets':
µb.scheduleAssetUpdater(0);
µb.assets.updateStart({
io.updateStart({
delay: µb.hiddenSettings.manualUpdateAssetFetchPeriod
});
break;
@ -241,8 +264,6 @@ vAPI.messaging.setup(onMessage);
{
// >>>>> start of local scope
const µb = µBlock;
const createCounts = ( ) => {
return {
blocked: { any: 0, frame: 0, script: 0 },
@ -291,7 +312,7 @@ const firewallRuleTypes = [
const getFirewallRules = function(src, out) {
const ruleset = out.firewallRules = {};
const df = µb.sessionFirewall;
const df = sessionFirewall;
for ( const type of firewallRuleTypes ) {
const r = df.lookupRuleData('*', '*', type);
@ -358,26 +379,26 @@ const popupDataFromTabId = function(tabId, tabTitle) {
r.contentLastModified = pageStore.contentLastModified;
getFirewallRules(rootHostname, r);
r.canElementPicker = isNetworkURI(r.rawURL);
r.noPopups = µb.sessionSwitches.evaluateZ(
r.noPopups = sessionSwitches.evaluateZ(
'no-popups',
rootHostname
);
r.popupBlockedCount = pageStore.popupBlockedCount;
r.noCosmeticFiltering = µb.sessionSwitches.evaluateZ(
r.noCosmeticFiltering = sessionSwitches.evaluateZ(
'no-cosmetic-filtering',
rootHostname
);
r.noLargeMedia = µb.sessionSwitches.evaluateZ(
r.noLargeMedia = sessionSwitches.evaluateZ(
'no-large-media',
rootHostname
);
r.largeMediaCount = pageStore.largeMediaCount;
r.noRemoteFonts = µb.sessionSwitches.evaluateZ(
r.noRemoteFonts = sessionSwitches.evaluateZ(
'no-remote-fonts',
rootHostname
);
r.remoteFontCount = pageStore.remoteFontCount;
r.noScripting = µb.sessionSwitches.evaluateZ(
r.noScripting = sessionSwitches.evaluateZ(
'no-scripting',
rootHostname
);
@ -386,14 +407,14 @@ const popupDataFromTabId = function(tabId, tabTitle) {
getFirewallRules(undefined, r);
}
r.matrixIsDirty = µb.sessionFirewall.hasSameRules(
µb.permanentFirewall,
r.matrixIsDirty = sessionFirewall.hasSameRules(
permanentFirewall,
rootHostname,
r.hostnameDict
) === false;
if ( r.matrixIsDirty === false ) {
r.matrixIsDirty = µb.sessionSwitches.hasSameRules(
µb.permanentSwitches,
r.matrixIsDirty = sessionSwitches.hasSameRules(
permanentSwitches,
rootHostname
) === false;
}
@ -470,17 +491,17 @@ const onMessage = function(request, sender, callback) {
break;
case 'revertFirewallRules':
µb.sessionFirewall.copyRules(
µb.permanentFirewall,
sessionFirewall.copyRules(
permanentFirewall,
request.srcHostname,
request.desHostnames
);
µb.sessionSwitches.copyRules(
µb.permanentSwitches,
sessionSwitches.copyRules(
permanentSwitches,
request.srcHostname
);
// https://github.com/gorhill/uBlock/issues/188
µb.cosmeticFilteringEngine.removeFromSelectorCache(
cosmeticFilteringEngine.removeFromSelectorCache(
request.srcHostname,
'net'
);
@ -490,8 +511,8 @@ const onMessage = function(request, sender, callback) {
case 'saveFirewallRules':
if (
µb.permanentFirewall.copyRules(
µb.sessionFirewall,
permanentFirewall.copyRules(
sessionFirewall,
request.srcHostname,
request.desHostnames
)
@ -499,8 +520,8 @@ const onMessage = function(request, sender, callback) {
µb.savePermanentFirewallRules();
}
if (
µb.permanentSwitches.copyRules(
µb.sessionSwitches,
permanentSwitches.copyRules(
sessionSwitches,
request.srcHostname
)
) {
@ -556,8 +577,6 @@ vAPI.messaging.listen({
{
// >>>>> start of local scope
const µb = µBlock;
const retrieveContentScriptParameters = async function(sender, request) {
if ( µb.readyToFilter !== true ) { return; }
const { tabId, frameId } = sender;
@ -575,7 +594,6 @@ const retrieveContentScriptParameters = async function(sender, request) {
request.url = pageStore.getEffectiveFrameURL(sender);
}
const loggerEnabled = µb.logger.enabled;
const noSpecificCosmeticFiltering =
pageStore.shouldApplySpecificCosmeticFilters(frameId) === false;
const noGenericCosmeticFiltering =
@ -594,13 +612,13 @@ const retrieveContentScriptParameters = async function(sender, request) {
request.entity = entityFromDomain(request.domain);
response.specificCosmeticFilters =
µb.cosmeticFilteringEngine.retrieveSpecificSelectors(request, response);
cosmeticFilteringEngine.retrieveSpecificSelectors(request, response);
// The procedural filterer's code is loaded only when needed and must be
// present before returning response to caller.
if (
Array.isArray(response.specificCosmeticFilters.proceduralFilters) || (
loggerEnabled &&
logger.enabled &&
response.specificCosmeticFilters.exceptedFilters.length !== 0
)
) {
@ -620,14 +638,14 @@ const retrieveContentScriptParameters = async function(sender, request) {
µb.canInjectScriptletsNow === false ||
isNetworkURI(sender.frameURL) === false
) {
response.scriptlets = µb.scriptletFilteringEngine.retrieve(request);
response.scriptlets = scriptletFilteringEngine.retrieve(request);
}
// https://github.com/NanoMeow/QuickReports/issues/6#issuecomment-414516623
// Inject as early as possible to make the cosmetic logger code less
// sensitive to the removal of DOM nodes which may match injected
// cosmetic filters.
if ( loggerEnabled ) {
if ( logger.enabled ) {
if (
noSpecificCosmeticFiltering === false ||
noGenericCosmeticFiltering === false
@ -666,7 +684,7 @@ const onMessage = function(request, sender, callback) {
switch ( request.what ) {
case 'cosmeticFiltersInjected':
µb.cosmeticFilteringEngine.addToSelectorCache(request);
cosmeticFilteringEngine.addToSelectorCache(request);
break;
case 'getCollapsibleBlockedRequests':
@ -674,7 +692,7 @@ const onMessage = function(request, sender, callback) {
id: request.id,
hash: request.hash,
netSelectorCacheCountMax:
µb.cosmeticFilteringEngine.netSelectorCacheCountMax,
cosmeticFilteringEngine.netSelectorCacheCountMax,
};
if (
µb.userSettings.collapseBlocked &&
@ -705,7 +723,7 @@ const onMessage = function(request, sender, callback) {
request.tabId = sender.tabId;
request.frameId = sender.frameId;
response = {
result: µb.cosmeticFilteringEngine.retrieveGenericSelectors(request),
result: cosmeticFilteringEngine.retrieveGenericSelectors(request),
};
break;
@ -735,8 +753,6 @@ vAPI.messaging.listen({
// >>>>> start of local scope
const onMessage = function(request, sender, callback) {
const µb = µBlock;
// Async
switch ( request.what ) {
// The procedural filterer must be present in case the user wants to
@ -801,7 +817,7 @@ const fromBase64 = function(encoded) {
}
let u8array;
try {
u8array = µBlock.denseBase64.decode(encoded);
u8array = µb.denseBase64.decode(encoded);
} catch(ex) {
}
return Promise.resolve(u8array !== undefined ? u8array : encoded);
@ -809,22 +825,22 @@ const fromBase64 = function(encoded) {
const toBase64 = function(data) {
const value = data instanceof Uint8Array
? µBlock.denseBase64.encode(data)
? µb.denseBase64.encode(data)
: data;
return Promise.resolve(value);
};
const compress = function(json) {
return µBlock.lz4Codec.encode(json, toBase64);
return lz4Codec.encode(json, toBase64);
};
const decompress = function(encoded) {
return µBlock.lz4Codec.decode(encoded, fromBase64);
return lz4Codec.decode(encoded, fromBase64);
};
const onMessage = function(request, sender, callback) {
// Cloud storage support is optional.
if ( µBlock.cloudStorageSupported !== true ) {
if ( µb.cloudStorageSupported !== true ) {
callback();
return;
}
@ -833,7 +849,7 @@ const onMessage = function(request, sender, callback) {
switch ( request.what ) {
case 'cloudGetOptions':
vAPI.cloud.getOptions(function(options) {
options.enabled = µBlock.userSettings.cloudStorageEnabled === true;
options.enabled = µb.userSettings.cloudStorageEnabled === true;
callback(options);
});
return;
@ -849,7 +865,7 @@ const onMessage = function(request, sender, callback) {
});
case 'cloudPush':
if ( µBlock.hiddenSettings.cloudStorageCompression ) {
if ( µb.hiddenSettings.cloudStorageCompression ) {
request.encode = compress;
}
return vAPI.cloud.push(request).then(result => {
@ -901,8 +917,6 @@ vAPI.messaging.listen({
{
// >>>>> start of local scope
const µb = µBlock;
// Settings
const getLocalData = async function() {
const data = Object.assign({}, µb.restoreBackupSettings);
@ -924,9 +938,9 @@ const backupUserData = async function() {
hiddenSettings:
µb.getModifiedSettings(µb.hiddenSettings, µb.hiddenSettingsDefault),
whitelist: µb.arrayFromWhitelist(µb.netWhitelist),
dynamicFilteringString: µb.permanentFirewall.toString(),
urlFilteringString: µb.permanentURLFiltering.toString(),
hostnameSwitchesString: µb.permanentSwitches.toString(),
dynamicFilteringString: permanentFirewall.toString(),
urlFilteringString: permanentURLFiltering.toString(),
hostnameSwitchesString: permanentSwitches.toString(),
userFilters: userFilters.content,
};
@ -962,17 +976,17 @@ const restoreUserData = async function(request) {
// https://github.com/chrisaljoudi/uBlock/issues/1102
// Ensure all currently cached assets are flushed from storage AND memory.
µb.assets.rmrf();
io.rmrf();
// If we are going to restore all, might as well wipe out clean local
// storages
await Promise.all([
µb.cacheStorage.clear(),
cacheStorage.clear(),
vAPI.storage.clear(),
]);
// Restore block stats
µBlock.saveLocalSettings();
µb.saveLocalSettings();
// Restore user data
vAPI.storage.set(userData.userSettings);
@ -980,7 +994,7 @@ const restoreUserData = async function(request) {
// Restore advanced settings.
let hiddenSettings = userData.hiddenSettings;
if ( hiddenSettings instanceof Object === false ) {
hiddenSettings = µBlock.hiddenSettingsFromString(
hiddenSettings = µb.hiddenSettingsFromString(
userData.hiddenSettingsString || ''
);
}
@ -1027,7 +1041,7 @@ const restoreUserData = async function(request) {
// quite attached to numbers
const resetUserData = async function() {
await Promise.all([
µb.cacheStorage.clear(),
cacheStorage.clear(),
vAPI.storage.clear(),
]);
@ -1056,17 +1070,17 @@ const getLists = async function(callback) {
autoUpdate: µb.userSettings.autoUpdate,
available: null,
cache: null,
cosmeticFilterCount: µb.cosmeticFilteringEngine.getFilterCount(),
cosmeticFilterCount: cosmeticFilteringEngine.getFilterCount(),
current: µb.availableFilterLists,
ignoreGenericCosmeticFilters: µb.userSettings.ignoreGenericCosmeticFilters,
isUpdating: µb.assets.isUpdating(),
netFilterCount: µb.staticNetFilteringEngine.getFilterCount(),
isUpdating: io.isUpdating(),
netFilterCount: staticNetFilteringEngine.getFilterCount(),
parseCosmeticFilters: µb.userSettings.parseAllABPHideFilters,
userFiltersPath: µb.userFiltersPath
};
const [ lists, metadata ] = await Promise.all([
µb.getAvailableLists(),
µb.assets.metadata(),
io.metadata(),
]);
r.available = lists;
prepListEntries(r.available);
@ -1099,14 +1113,14 @@ const getOriginHints = function() {
const getRules = function() {
return {
permanentRules:
µb.permanentFirewall.toArray().concat(
µb.permanentSwitches.toArray(),
µb.permanentURLFiltering.toArray()
permanentFirewall.toArray().concat(
permanentSwitches.toArray(),
permanentURLFiltering.toArray()
),
sessionRules:
µb.sessionFirewall.toArray().concat(
µb.sessionSwitches.toArray(),
µb.sessionURLFiltering.toArray()
sessionFirewall.toArray().concat(
sessionSwitches.toArray(),
sessionURLFiltering.toArray()
),
pslSelfie: globals.publicSuffixList.toSelfie(),
};
@ -1115,13 +1129,13 @@ const getRules = function() {
const modifyRuleset = function(details) {
let swRuleset, hnRuleset, urlRuleset;
if ( details.permanent ) {
swRuleset = µb.permanentSwitches;
hnRuleset = µb.permanentFirewall;
urlRuleset = µb.permanentURLFiltering;
swRuleset = permanentSwitches;
hnRuleset = permanentFirewall;
urlRuleset = permanentURLFiltering;
} else {
swRuleset = µb.sessionSwitches;
hnRuleset = µb.sessionFirewall;
urlRuleset = µb.sessionURLFiltering;
swRuleset = sessionSwitches;
hnRuleset = sessionFirewall;
urlRuleset = sessionURLFiltering;
}
let toRemove = new Set(details.toRemove.trim().split(/\s*[\n\r]+\s*/));
for ( let rule of toRemove ) {
@ -1240,7 +1254,7 @@ const onMessage = function(request, sender, callback) {
case 'getAutoCompleteDetails':
response = {};
if ( (request.hintUpdateToken || 0) === 0 ) {
response.redirectResources = µb.redirectEngine.getResourceDetails();
response.redirectResources = redirectEngine.getResourceDetails();
response.preparseDirectiveTokens = µb.preparseDirectives.getTokens();
response.preparseDirectiveHints = µb.preparseDirectives.getHints();
response.expertMode = µb.hiddenSettings.filterAuthorMode;
@ -1257,22 +1271,22 @@ const onMessage = function(request, sender, callback) {
case 'modifyRuleset':
// https://github.com/chrisaljoudi/uBlock/issues/772
µb.cosmeticFilteringEngine.removeFromSelectorCache('*');
cosmeticFilteringEngine.removeFromSelectorCache('*');
modifyRuleset(request);
response = getRules();
break;
case 'purgeAllCaches':
if ( request.hard ) {
µb.assets.remove(/./);
io.remove(/./);
} else {
µb.assets.purge(/./, 'public_suffix_list.dat');
io.purge(/./, 'public_suffix_list.dat');
}
break;
case 'purgeCache':
µb.assets.purge(request.assetKey);
µb.assets.remove('compiled/' + request.assetKey);
io.purge(request.assetKey);
io.remove('compiled/' + request.assetKey);
break;
case 'readHiddenSettings':
@ -1325,7 +1339,6 @@ vAPI.messaging.listen({
{
// >>>>> start of local scope
const µb = µBlock;
const extensionOriginURL = vAPI.getURL('');
const documentBlockedURL = vAPI.getURL('document-blocked.html');
@ -1333,7 +1346,7 @@ const getLoggerData = async function(details, activeTabId, callback) {
const response = {
activeTabId,
colorBlind: µb.userSettings.colorBlindFriendly,
entries: µb.logger.readAll(details.ownerId),
entries: logger.readAll(details.ownerId),
filterAuthorMode: µb.hiddenSettings.filterAuthorMode,
tabIdsToken: µb.pageStoresToken,
tooltips: µb.userSettings.tooltipsDisabled === false
@ -1386,8 +1399,8 @@ const getURLFilteringData = function(details) {
dirty: false,
colors: colors
};
const suf = µb.sessionURLFiltering;
const puf = µb.permanentURLFiltering;
const suf = sessionURLFiltering;
const puf = permanentURLFiltering;
const urls = details.urls;
const context = details.context;
const type = details.type;
@ -1416,7 +1429,7 @@ const compileTemporaryException = function(filter) {
const parser = new StaticFilteringParser();
parser.analyze(filter);
if ( parser.shouldDiscard() ) { return; }
return µb.staticExtFilteringEngine.compileTemporary(parser);
return staticExtFilteringEngine.compileTemporary(parser);
};
const toggleTemporaryException = function(details) {
@ -1443,8 +1456,8 @@ const onMessage = function(request, sender, callback) {
switch ( request.what ) {
case 'readAll':
if (
µb.logger.ownerId !== undefined &&
µb.logger.ownerId !== request.ownerId
logger.ownerId !== undefined &&
logger.ownerId !== request.ownerId
) {
return callback({ unavailable: true });
}
@ -1466,14 +1479,14 @@ const onMessage = function(request, sender, callback) {
break;
case 'releaseView':
if ( request.ownerId === µb.logger.ownerId ) {
µb.logger.ownerId = undefined;
if ( request.ownerId === logger.ownerId ) {
logger.ownerId = undefined;
}
break;
case 'saveURLFilteringRules':
response = µb.permanentURLFiltering.copyRules(
µb.sessionURLFiltering,
response = permanentURLFiltering.copyRules(
sessionURLFiltering,
request.context,
request.urls,
request.type
@ -1539,7 +1552,7 @@ const onMessage = function(request, sender, callback) {
break;
case 'temporarilyWhitelistDocument':
µBlock.webRequest.strictBlockBypass(request.hostname);
webRequest.strictBlockBypass(request.hostname);
break;
default:
@ -1568,10 +1581,8 @@ vAPI.messaging.listen({
{
// >>>>> start of local scope
const µb = µBlock;
const logCosmeticFilters = function(tabId, details) {
if ( µb.logger.enabled === false ) { return; }
if ( logger.enabled === false ) { return; }
const filter = { source: 'cosmetic', raw: '' };
const fctxt = µb.filteringContext.duplicate();
@ -1588,7 +1599,7 @@ const logCosmeticFilters = function(tabId, details) {
};
const logCSPViolations = function(pageStore, request) {
if ( µb.logger.enabled === false || pageStore === null ) {
if ( logger.enabled === false || pageStore === null ) {
return false;
}
if ( request.violations.length === 0 ) {
@ -1606,7 +1617,7 @@ const logCSPViolations = function(pageStore, request) {
cspData = new Map();
const staticDirectives =
µb.staticNetFilteringEngine.matchAndFetchModifiers(fctxt, 'csp');
staticNetFilteringEngine.matchAndFetchModifiers(fctxt, 'csp');
if ( staticDirectives !== undefined ) {
for ( const directive of staticDirectives ) {
if ( directive.result !== 1 ) { continue; }
@ -1691,7 +1702,7 @@ const onMessage = function(request, sender, callback) {
switch ( request.what ) {
case 'inlinescriptFound':
if ( µb.logger.enabled && pageStore !== null ) {
if ( logger.enabled && pageStore !== null ) {
const fctxt = µb.filteringContext.duplicate();
fctxt.fromTabId(tabId)
.setType('inline-script')

View File

@ -23,14 +23,21 @@
/******************************************************************************/
import contextMenu from './contextmenu.js';
import logger from './logger.js';
import staticNetFilteringEngine from './static-net-filtering.js';
import µb from './background.js';
import { redirectEngine } from './redirect-engine.js';
import { sessionFirewall } from './dynamic-net-filtering.js';
import { sessionSwitches } from './hnswitches.js';
import { sessionURLFiltering } from './url-net-filtering.js';
import {
domainFromHostname,
hostnameFromURI,
isNetworkURI,
} from './uri-utils.js';
import µBlock from './background.js';
/*******************************************************************************
A PageRequestStore object is used to store net requests in two ways:
@ -42,10 +49,6 @@ To create a log of net requests
/******************************************************************************/
const µb = µBlock;
/******************************************************************************/
const NetFilteringResultCache = class {
constructor() {
this.init();
@ -217,18 +220,18 @@ const FrameStore = class {
}
this._cosmeticFilteringBits = 0b11;
{
const result = µb.staticNetFilteringEngine.matchRequestReverse(
const result = staticNetFilteringEngine.matchRequestReverse(
'specifichide',
this.rawURL
);
if ( result !== 0 && µb.logger.enabled ) {
µBlock.filteringContext
if ( result !== 0 && logger.enabled ) {
µb.filteringContext
.duplicate()
.fromTabId(tabId)
.setURL(this.rawURL)
.setRealm('network')
.setType('specifichide')
.setFilter(µb.staticNetFilteringEngine.toLogData())
.setFilter(staticNetFilteringEngine.toLogData())
.toLogger();
}
if ( result === 2 ) {
@ -236,18 +239,18 @@ const FrameStore = class {
}
}
{
const result = µb.staticNetFilteringEngine.matchRequestReverse(
const result = staticNetFilteringEngine.matchRequestReverse(
'generichide',
this.rawURL
);
if ( result !== 0 && µb.logger.enabled ) {
µBlock.filteringContext
if ( result !== 0 && logger.enabled ) {
µb.filteringContext
.duplicate()
.fromTabId(tabId)
.setURL(this.rawURL)
.setRealm('network')
.setType('generichide')
.setFilter(µb.staticNetFilteringEngine.toLogData())
.setFilter(staticNetFilteringEngine.toLogData())
.toLogger();
}
if ( result === 2 ) {
@ -559,18 +562,18 @@ const PageStore = class {
if ( this._noCosmeticFiltering === undefined ) {
this._noCosmeticFiltering = this.getNetFilteringSwitch() === false;
if ( this._noCosmeticFiltering === false ) {
this._noCosmeticFiltering = µb.sessionSwitches.evaluateZ(
this._noCosmeticFiltering = sessionSwitches.evaluateZ(
'no-cosmetic-filtering',
this.tabHostname
) === true;
if ( this._noCosmeticFiltering && µb.logger.enabled ) {
if ( this._noCosmeticFiltering && logger.enabled ) {
µb.filteringContext
.duplicate()
.fromTabId(this.tabId)
.setURL(this.rawURL)
.setRealm('cosmetic')
.setType('dom')
.setFilter(µb.sessionSwitches.toLogData())
.setFilter(sessionSwitches.toLogData())
.toLogger();
}
}
@ -620,12 +623,12 @@ const PageStore = class {
allFrames: true,
runAt: 'document_idle',
});
µb.contextMenu.update(this.tabId);
contextMenu.update(this.tabId);
}
temporarilyAllowLargeMediaElements(state) {
this.largeMediaCount = 0;
µb.contextMenu.update(this.tabId);
contextMenu.update(this.tabId);
if ( state ) {
this.allowLargeMediaElementsUntil = 0;
this.allowLargeMediaElementsRegex = undefined;
@ -784,32 +787,32 @@ const PageStore = class {
}
const requestType = fctxt.type;
const loggerEnabled = µb.logger.enabled;
const loggerEnabled = logger.enabled;
// Dynamic URL filtering.
let result = µb.sessionURLFiltering.evaluateZ(
let result = sessionURLFiltering.evaluateZ(
fctxt.getTabHostname(),
fctxt.url,
requestType
);
if ( result !== 0 && loggerEnabled ) {
fctxt.filter = µb.sessionURLFiltering.toLogData();
fctxt.filter = sessionURLFiltering.toLogData();
}
// Dynamic hostname/type filtering.
if ( result === 0 && µb.userSettings.advancedUserEnabled ) {
result = µb.sessionFirewall.evaluateCellZY(
result = sessionFirewall.evaluateCellZY(
fctxt.getTabHostname(),
fctxt.getHostname(),
requestType
);
if ( result !== 0 && result !== 3 && loggerEnabled ) {
fctxt.filter = µb.sessionFirewall.toLogData();
fctxt.filter = sessionFirewall.toLogData();
}
}
// Static filtering has lowest precedence.
const snfe = µb.staticNetFilteringEngine;
const snfe = staticNetFilteringEngine;
if ( result === 0 || result === 3 ) {
result = snfe.matchRequest(fctxt);
if ( result !== 0 ) {
@ -873,19 +876,19 @@ const PageStore = class {
if ( this.getNetFilteringSwitch(fctxt) === false ) { return 0; }
let result = µb.staticNetFilteringEngine.matchHeaders(fctxt, headers);
let result = staticNetFilteringEngine.matchHeaders(fctxt, headers);
if ( result === 0 ) { return 0; }
const loggerEnabled = µb.logger.enabled;
const loggerEnabled = logger.enabled;
if ( loggerEnabled ) {
fctxt.filter = µb.staticNetFilteringEngine.toLogData();
fctxt.filter = staticNetFilteringEngine.toLogData();
}
// Dynamic filtering allow rules
// URL filtering
if (
result === 1 &&
µb.sessionURLFiltering.evaluateZ(
sessionURLFiltering.evaluateZ(
fctxt.getTabHostname(),
fctxt.url,
fctxt.type
@ -893,14 +896,14 @@ const PageStore = class {
) {
result = 2;
if ( loggerEnabled ) {
fctxt.filter = µb.sessionURLFiltering.toLogData();
fctxt.filter = sessionURLFiltering.toLogData();
}
}
// Hostname filtering
if (
result === 1 &&
µb.userSettings.advancedUserEnabled &&
µb.sessionFirewall.evaluateCellZY(
sessionFirewall.evaluateCellZY(
fctxt.getTabHostname(),
fctxt.getHostname(),
fctxt.type
@ -908,7 +911,7 @@ const PageStore = class {
) {
result = 2;
if ( loggerEnabled ) {
fctxt.filter = µb.sessionFirewall.toLogData();
fctxt.filter = sessionFirewall.toLogData();
}
}
@ -916,24 +919,24 @@ const PageStore = class {
}
redirectBlockedRequest(fctxt) {
const directives = µb.staticNetFilteringEngine.redirectRequest(
µb.redirectEngine,
const directives = staticNetFilteringEngine.redirectRequest(
redirectEngine,
fctxt
);
if ( directives === undefined ) { return; }
if ( µb.logger.enabled !== true ) { return; }
if ( logger.enabled !== true ) { return; }
fctxt.pushFilters(directives.map(a => a.logData()));
if ( fctxt.redirectURL === undefined ) { return; }
fctxt.pushFilter({
source: 'redirect',
raw: µb.redirectEngine.resourceNameRegister
raw: redirectEngine.resourceNameRegister
});
}
redirectNonBlockedRequest(fctxt) {
const directives = µb.staticNetFilteringEngine.filterQuery(fctxt);
const directives = staticNetFilteringEngine.filterQuery(fctxt);
if ( directives === undefined ) { return; }
if ( µb.logger.enabled !== true ) { return; }
if ( logger.enabled !== true ) { return; }
fctxt.pushFilters(directives.map(a => a.logData()));
if ( fctxt.redirectURL === undefined ) { return; }
fctxt.pushFilter({
@ -944,13 +947,13 @@ const PageStore = class {
filterCSPReport(fctxt) {
if (
µb.sessionSwitches.evaluateZ(
sessionSwitches.evaluateZ(
'no-csp-reports',
fctxt.getHostname()
)
) {
if ( µb.logger.enabled ) {
fctxt.filter = µb.sessionSwitches.toLogData();
if ( logger.enabled ) {
fctxt.filter = sessionSwitches.toLogData();
}
return 1;
}
@ -962,13 +965,13 @@ const PageStore = class {
this.remoteFontCount += 1;
}
if (
µb.sessionSwitches.evaluateZ(
sessionSwitches.evaluateZ(
'no-remote-fonts',
fctxt.getTabHostname()
) !== false
) {
if ( µb.logger.enabled ) {
fctxt.filter = µb.sessionSwitches.toLogData();
if ( logger.enabled ) {
fctxt.filter = sessionSwitches.toLogData();
}
return 1;
}
@ -982,15 +985,15 @@ const PageStore = class {
}
if (
netFiltering === false ||
µb.sessionSwitches.evaluateZ(
sessionSwitches.evaluateZ(
'no-scripting',
fctxt.getTabHostname()
) === false
) {
return 0;
}
if ( µb.logger.enabled ) {
fctxt.filter = µb.sessionSwitches.toLogData();
if ( logger.enabled ) {
fctxt.filter = sessionSwitches.toLogData();
}
return 1;
}
@ -1019,7 +1022,7 @@ const PageStore = class {
return 0;
}
if (
µb.sessionSwitches.evaluateZ(
sessionSwitches.evaluateZ(
'no-large-media',
fctxt.getTabHostname()
) !== true
@ -1039,8 +1042,8 @@ const PageStore = class {
}, 500);
}
if ( µb.logger.enabled ) {
fctxt.filter = µb.sessionSwitches.toLogData();
if ( logger.enabled ) {
fctxt.filter = sessionSwitches.toLogData();
}
return 1;
@ -1069,14 +1072,14 @@ const PageStore = class {
}
}
if ( exceptCname === undefined ) {
const result = µb.staticNetFilteringEngine.matchRequestReverse(
const result = staticNetFilteringEngine.matchRequestReverse(
'cname',
frameStore instanceof Object
? frameStore.rawURL
: fctxt.getDocOrigin()
);
exceptCname = result === 2
? µb.staticNetFilteringEngine.toLogData()
? staticNetFilteringEngine.toLogData()
: false;
if ( frameStore instanceof Object ) {
frameStore.exceptCname = exceptCname;
@ -1128,6 +1131,6 @@ PageStore.prototype.collapsibleResources = new Set([
PageStore.junkyard = [];
PageStore.junkyardMax = 10;
µb.PageStore = PageStore;
/******************************************************************************/
export { PageStore };

View File

@ -23,8 +23,9 @@
/******************************************************************************/
import io from './assets.js';
import µb from './background.js';
import { LineIterator } from './text-iterators.js';
import µBlock from './background.js';
/******************************************************************************/
@ -408,7 +409,7 @@ RedirectEngine.prototype.resourcesFromString = function(text) {
// No more data, add the resource.
const name = this.aliases.get(fields[0]) || fields[0];
const mime = fields[1];
const content = µBlock.orphanizeString(
const content = µb.orphanizeString(
fields.slice(2).join(encoded ? '' : '\n')
);
this.resources.set(
@ -436,14 +437,11 @@ const removeTopCommentBlock = function(text) {
/******************************************************************************/
RedirectEngine.prototype.loadBuiltinResources = function() {
// TODO: remove once usage of uBO 1.20.4 is widespread.
µBlock.assets.remove('ublock-resources');
this.resources = new Map();
this.aliases = new Map();
const fetches = [
µBlock.assets.fetchText(
io.fetchText(
'/assets/resources/scriptlets.js'
).then(result => {
const content = result.content;
@ -505,7 +503,7 @@ RedirectEngine.prototype.loadBuiltinResources = function() {
continue;
}
fetches.push(
µBlock.assets.fetch(
io.fetch(
`/web_accessible_resources/${name}?secret=${vAPI.warSecret()}`,
{ responseType: details.data }
).then(
@ -547,7 +545,7 @@ RedirectEngine.prototype.getResourceDetails = function() {
const resourcesSelfieVersion = 5;
RedirectEngine.prototype.selfieFromResources = function() {
µBlock.assets.put(
io.put(
'compiled/redirectEngine/resources',
JSON.stringify({
version: resourcesSelfieVersion,
@ -558,7 +556,7 @@ RedirectEngine.prototype.selfieFromResources = function() {
};
RedirectEngine.prototype.resourcesFromSelfie = async function() {
const result = await µBlock.assets.get('compiled/redirectEngine/resources');
const result = await io.get('compiled/redirectEngine/resources');
let selfie;
try {
selfie = JSON.parse(result.content);
@ -580,13 +578,13 @@ RedirectEngine.prototype.resourcesFromSelfie = async function() {
};
RedirectEngine.prototype.invalidateResourcesSelfie = function() {
µBlock.assets.remove('compiled/redirectEngine/resources');
io.remove('compiled/redirectEngine/resources');
};
/******************************************************************************/
// Export
const redirectEngine = new RedirectEngine();
µBlock.redirectEngine = new RedirectEngine();
export { redirectEngine };
/******************************************************************************/

View File

@ -0,0 +1,293 @@
/*******************************************************************************
uBlock Origin - a browser extension to block requests.
Copyright (C) 2015-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
*/
'use strict';
/******************************************************************************/
{
// >>>>> start of local scope
/******************************************************************************/
const reBlockStart = /^#block-start-(\d+)\n/gm;
let listEntries = Object.create(null);
const extractBlocks = function(content, begId, endId) {
reBlockStart.lastIndex = 0;
const out = [];
let match = reBlockStart.exec(content);
while ( match !== null ) {
const beg = match.index + match[0].length;
const blockId = parseInt(match[1], 10);
if ( blockId >= begId && blockId < endId ) {
const end = content.indexOf('#block-end-' + match[1], beg);
out.push(content.slice(beg, end));
reBlockStart.lastIndex = end;
}
match = reBlockStart.exec(content);
}
return out.join('\n');
};
// https://github.com/MajkiIT/polish-ads-filter/issues/14768#issuecomment-536006312
// Avoid reporting badfilter-ed filters.
const fromNetFilter = function(details) {
const lists = [];
const compiledFilter = details.compiledFilter;
for ( const assetKey in listEntries ) {
const entry = listEntries[assetKey];
if ( entry === undefined ) { continue; }
const content = extractBlocks(entry.content, 100, 101);
let pos = 0;
for (;;) {
pos = content.indexOf(compiledFilter, pos);
if ( pos === -1 ) { break; }
// We need an exact match.
// https://github.com/gorhill/uBlock/issues/1392
// https://github.com/gorhill/uBlock/issues/835
const notFound = pos !== 0 &&
content.charCodeAt(pos - 1) !== 0x0A;
pos += compiledFilter.length;
if (
notFound ||
pos !== content.length && content.charCodeAt(pos) !== 0x0A
) {
continue;
}
lists.push({
assetKey: assetKey,
title: entry.title,
supportURL: entry.supportURL
});
break;
}
}
const response = {};
response[details.rawFilter] = lists;
self.postMessage({ id: details.id, response });
};
// Looking up filter lists from a cosmetic filter is a bit more complicated
// than with network filters:
//
// The filter is its raw representation, not its compiled version. This is
// because the cosmetic filtering engine can't translate a live cosmetic
// filter into its compiled version. Reason is I do not want to burden
// cosmetic filtering with the resource overhead of being able to recompile
// live cosmetic filters. I want the cosmetic filtering code to be left
// completely unaffected by reverse lookup requirements.
//
// Mainly, given a CSS selector and a hostname as context, we will derive
// various versions of compiled filters and see if there are matches. This
// way the whole CPU cost is incurred by the reverse lookup code -- in a
// worker thread, and the cosmetic filtering engine incurs no cost at all.
//
// For this though, the reverse lookup code here needs some knowledge of
// the inners of the cosmetic filtering engine.
// FilterContainer.fromCompiledContent() is our reference code to create
// the various compiled versions.
const fromCosmeticFilter = function(details) {
const match = /^#@?#\^?/.exec(details.rawFilter);
const prefix = match[0];
const exception = prefix.charAt(1) === '@';
const selector = details.rawFilter.slice(prefix.length);
const isHtmlFilter = prefix.endsWith('^');
const hostname = details.hostname;
// The longer the needle, the lower the number of false positives.
// https://github.com/uBlockOrigin/uBlock-issues/issues/1139
// Mind that there is no guarantee a selector has `\w` characters.
const needle = selector.match(/\w+|\*/g).reduce(function(a, b) {
return a.length > b.length ? a : b;
});
const regexFromLabels = (prefix, hn, suffix) =>
new RegExp(
prefix +
hn.split('.').reduce((acc, item) => `(${acc}\\.)?${item}`) +
suffix
);
// https://github.com/uBlockOrigin/uBlock-issues/issues/803
// Support looking up selectors of the form `*##...`
const reHostname = regexFromLabels('^', hostname, '$');
let reEntity;
{
const domain = details.domain;
const pos = domain.indexOf('.');
if ( pos !== -1 ) {
reEntity = regexFromLabels(
'^(',
hostname.slice(0, pos + hostname.length - domain.length),
'\\.)?\\*$'
);
}
}
const hostnameMatches = hn => {
return hn === '' ||
reHostname.test(hn) ||
reEntity !== undefined && reEntity.test(hn);
};
const response = Object.create(null);
for ( const assetKey in listEntries ) {
const entry = listEntries[assetKey];
if ( entry === undefined ) { continue; }
let content = extractBlocks(entry.content, 200, 1000),
isProcedural,
found;
let pos = 0;
while ( (pos = content.indexOf(needle, pos)) !== -1 ) {
let beg = content.lastIndexOf('\n', pos);
if ( beg === -1 ) { beg = 0; }
let end = content.indexOf('\n', pos);
if ( end === -1 ) { end = content.length; }
pos = end;
const fargs = JSON.parse(content.slice(beg, end));
const filterType = fargs[0];
// https://github.com/gorhill/uBlock/issues/2763
if (
filterType >= 0 &&
filterType <= 5 &&
details.ignoreGeneric
) {
continue;
}
// Do not confuse cosmetic filters with HTML ones.
if ( (filterType === 64) !== isHtmlFilter ) { continue; }
switch ( filterType ) {
// Lowly generic cosmetic filters
case 0: // simple id-based
if ( exception ) { break; }
if ( fargs[1] !== selector.slice(1) ) { break; }
if ( selector.charAt(0) !== '#' ) { break; }
found = prefix + selector;
break;
case 2: // simple class-based
if ( exception ) { break; }
if ( fargs[1] !== selector.slice(1) ) { break; }
if ( selector.charAt(0) !== '.' ) { break; }
found = prefix + selector;
break;
case 1: // complex id-based
case 3: // complex class-based
if ( exception ) { break; }
if ( fargs[2] !== selector ) { break; }
found = prefix + selector;
break;
// Highly generic cosmetic filters
case 4: // simple highly generic
case 5: // complex highly generic
if ( exception ) { break; }
if ( fargs[1] !== selector ) { break; }
found = prefix + selector;
break;
// Specific cosmetic filtering
// Generic exception
case 8:
// HTML filtering
// Response header filtering
case 64:
if ( exception !== ((fargs[2] & 0b001) !== 0) ) { break; }
isProcedural = (fargs[2] & 0b010) !== 0;
if (
isProcedural === false && fargs[3] !== selector ||
isProcedural && JSON.parse(fargs[3]).raw !== selector
) {
break;
}
if ( hostnameMatches(fargs[1]) === false ) { break; }
// https://www.reddit.com/r/uBlockOrigin/comments/d6vxzj/
// Ignore match if specific cosmetic filters are disabled
if (
filterType === 8 &&
exception === false &&
details.ignoreSpecific
) {
break;
}
found = fargs[1] + prefix + selector;
break;
// Scriptlet injection
case 32:
if ( exception !== ((fargs[2] & 0b001) !== 0) ) { break; }
if ( fargs[3] !== selector ) { break; }
if ( hostnameMatches(fargs[1]) ) {
found = fargs[1] + prefix + selector;
}
break;
}
if ( found !== undefined ) {
if ( response[found] === undefined ) {
response[found] = [];
}
response[found].push({
assetKey: assetKey,
title: entry.title,
supportURL: entry.supportURL
});
break;
}
}
}
self.postMessage({ id: details.id, response });
};
self.onmessage = function(e) { // jshint ignore:line
const msg = e.data;
switch ( msg.what ) {
case 'resetLists':
listEntries = Object.create(null);
break;
case 'setList':
listEntries[msg.details.assetKey] = msg.details;
break;
case 'fromNetFilter':
fromNetFilter(msg);
break;
case 'fromCosmeticFilter':
fromCosmeticFilter(msg);
break;
}
};
/******************************************************************************/
// <<<<< end of local scope
}
/******************************************************************************/

View File

@ -23,462 +23,192 @@
/******************************************************************************/
(( ) => {
// >>>>> start of local scope
import staticNetFilteringEngine from './static-net-filtering.js';
import µb from './background.js';
import { CompiledListWriter } from './static-filtering-io.js';
import { StaticFilteringParser } from './static-filtering-parser.js';
import {
domainFromHostname,
hostnameFromURI,
} from './uri-utils.js';
/******************************************************************************/
// Worker context
const workerTTL = 5 * 60 * 1000;
const pendingResponses = new Map();
if (
self.WorkerGlobalScope instanceof Object &&
self instanceof self.WorkerGlobalScope
) {
const reBlockStart = /^#block-start-(\d+)\n/gm;
let listEntries = Object.create(null);
let worker = null;
let workerTTLTimer;
let needLists = true;
let messageId = 1;
const extractBlocks = function(content, begId, endId) {
reBlockStart.lastIndex = 0;
const out = [];
let match = reBlockStart.exec(content);
while ( match !== null ) {
const beg = match.index + match[0].length;
const blockId = parseInt(match[1], 10);
if ( blockId >= begId && blockId < endId ) {
const end = content.indexOf('#block-end-' + match[1], beg);
out.push(content.slice(beg, end));
reBlockStart.lastIndex = end;
}
match = reBlockStart.exec(content);
}
return out.join('\n');
};
const onWorkerMessage = function(e) {
const msg = e.data;
const resolver = pendingResponses.get(msg.id);
pendingResponses.delete(msg.id);
resolver(msg.response);
};
// https://github.com/MajkiIT/polish-ads-filter/issues/14768#issuecomment-536006312
// Avoid reporting badfilter-ed filters.
const stopWorker = function() {
if ( workerTTLTimer !== undefined ) {
clearTimeout(workerTTLTimer);
workerTTLTimer = undefined;
}
if ( worker === null ) { return; }
worker.terminate();
worker = null;
needLists = true;
for ( const resolver of pendingResponses.values() ) {
resolver();
}
pendingResponses.clear();
};
const fromNetFilter = function(details) {
const lists = [];
const compiledFilter = details.compiledFilter;
const initWorker = function() {
if ( worker === null ) {
worker = new Worker('js/reverselookup-worker.js');
worker.onmessage = onWorkerMessage;
}
for ( const assetKey in listEntries ) {
const entry = listEntries[assetKey];
if ( entry === undefined ) { continue; }
const content = extractBlocks(entry.content, 100, 101);
let pos = 0;
for (;;) {
pos = content.indexOf(compiledFilter, pos);
if ( pos === -1 ) { break; }
// We need an exact match.
// https://github.com/gorhill/uBlock/issues/1392
// https://github.com/gorhill/uBlock/issues/835
const notFound = pos !== 0 &&
content.charCodeAt(pos - 1) !== 0x0A;
pos += compiledFilter.length;
if (
notFound ||
pos !== content.length && content.charCodeAt(pos) !== 0x0A
) {
continue;
}
lists.push({
assetKey: assetKey,
title: entry.title,
supportURL: entry.supportURL
});
break;
}
}
// The worker will be shutdown after n minutes without being used.
if ( workerTTLTimer !== undefined ) {
clearTimeout(workerTTLTimer);
}
workerTTLTimer = vAPI.setTimeout(stopWorker, workerTTL);
const response = {};
response[details.rawFilter] = lists;
if ( needLists === false ) {
return Promise.resolve();
}
needLists = false;
self.postMessage({ id: details.id, response });
};
const entries = new Map();
// Looking up filter lists from a cosmetic filter is a bit more complicated
// than with network filters:
//
// The filter is its raw representation, not its compiled version. This is
// because the cosmetic filtering engine can't translate a live cosmetic
// filter into its compiled version. Reason is I do not want to burden
// cosmetic filtering with the resource overhead of being able to recompile
// live cosmetic filters. I want the cosmetic filtering code to be left
// completely unaffected by reverse lookup requirements.
//
// Mainly, given a CSS selector and a hostname as context, we will derive
// various versions of compiled filters and see if there are matches. This
// way the whole CPU cost is incurred by the reverse lookup code -- in a
// worker thread, and the cosmetic filtering engine incurs no cost at all.
//
// For this though, the reverse lookup code here needs some knowledge of
// the inners of the cosmetic filtering engine.
// FilterContainer.fromCompiledContent() is our reference code to create
// the various compiled versions.
const onListLoaded = function(details) {
const entry = entries.get(details.assetKey);
const fromCosmeticFilter = function(details) {
const match = /^#@?#\^?/.exec(details.rawFilter);
const prefix = match[0];
const exception = prefix.charAt(1) === '@';
const selector = details.rawFilter.slice(prefix.length);
const isHtmlFilter = prefix.endsWith('^');
const hostname = details.hostname;
// The longer the needle, the lower the number of false positives.
// https://github.com/uBlockOrigin/uBlock-issues/issues/1139
// Mind that there is no guarantee a selector has `\w` characters.
const needle = selector.match(/\w+|\*/g).reduce(function(a, b) {
return a.length > b.length ? a : b;
});
const regexFromLabels = (prefix, hn, suffix) =>
new RegExp(
prefix +
hn.split('.').reduce((acc, item) => `(${acc}\\.)?${item}`) +
suffix
);
// https://github.com/uBlockOrigin/uBlock-issues/issues/803
// Support looking up selectors of the form `*##...`
const reHostname = regexFromLabels('^', hostname, '$');
let reEntity;
{
const domain = details.domain;
const pos = domain.indexOf('.');
if ( pos !== -1 ) {
reEntity = regexFromLabels(
'^(',
hostname.slice(0, pos + hostname.length - domain.length),
'\\.)?\\*$'
);
}
}
const hostnameMatches = hn => {
return hn === '' ||
reHostname.test(hn) ||
reEntity !== undefined && reEntity.test(hn);
};
const response = Object.create(null);
for ( const assetKey in listEntries ) {
const entry = listEntries[assetKey];
if ( entry === undefined ) { continue; }
let content = extractBlocks(entry.content, 200, 1000),
isProcedural,
found;
let pos = 0;
while ( (pos = content.indexOf(needle, pos)) !== -1 ) {
let beg = content.lastIndexOf('\n', pos);
if ( beg === -1 ) { beg = 0; }
let end = content.indexOf('\n', pos);
if ( end === -1 ) { end = content.length; }
pos = end;
const fargs = JSON.parse(content.slice(beg, end));
const filterType = fargs[0];
// https://github.com/gorhill/uBlock/issues/2763
if (
filterType >= 0 &&
filterType <= 5 &&
details.ignoreGeneric
) {
continue;
}
// Do not confuse cosmetic filters with HTML ones.
if ( (filterType === 64) !== isHtmlFilter ) { continue; }
switch ( filterType ) {
// Lowly generic cosmetic filters
case 0: // simple id-based
if ( exception ) { break; }
if ( fargs[1] !== selector.slice(1) ) { break; }
if ( selector.charAt(0) !== '#' ) { break; }
found = prefix + selector;
break;
case 2: // simple class-based
if ( exception ) { break; }
if ( fargs[1] !== selector.slice(1) ) { break; }
if ( selector.charAt(0) !== '.' ) { break; }
found = prefix + selector;
break;
case 1: // complex id-based
case 3: // complex class-based
if ( exception ) { break; }
if ( fargs[2] !== selector ) { break; }
found = prefix + selector;
break;
// Highly generic cosmetic filters
case 4: // simple highly generic
case 5: // complex highly generic
if ( exception ) { break; }
if ( fargs[1] !== selector ) { break; }
found = prefix + selector;
break;
// Specific cosmetic filtering
// Generic exception
case 8:
// HTML filtering
// Response header filtering
case 64:
if ( exception !== ((fargs[2] & 0b001) !== 0) ) { break; }
isProcedural = (fargs[2] & 0b010) !== 0;
if (
isProcedural === false && fargs[3] !== selector ||
isProcedural && JSON.parse(fargs[3]).raw !== selector
) {
break;
}
if ( hostnameMatches(fargs[1]) === false ) { break; }
// https://www.reddit.com/r/uBlockOrigin/comments/d6vxzj/
// Ignore match if specific cosmetic filters are disabled
if (
filterType === 8 &&
exception === false &&
details.ignoreSpecific
) {
break;
}
found = fargs[1] + prefix + selector;
break;
// Scriptlet injection
case 32:
if ( exception !== ((fargs[2] & 0b001) !== 0) ) { break; }
if ( fargs[3] !== selector ) { break; }
if ( hostnameMatches(fargs[1]) ) {
found = fargs[1] + prefix + selector;
}
break;
}
if ( found !== undefined ) {
if ( response[found] === undefined ) {
response[found] = [];
}
response[found].push({
assetKey: assetKey,
title: entry.title,
supportURL: entry.supportURL
});
break;
}
}
}
self.postMessage({ id: details.id, response });
};
self.onmessage = function(e) { // jshint ignore:line
const msg = e.data;
switch ( msg.what ) {
case 'resetLists':
listEntries = Object.create(null);
break;
case 'setList':
listEntries[msg.details.assetKey] = msg.details;
break;
case 'fromNetFilter':
fromNetFilter(msg);
break;
case 'fromCosmeticFilter':
fromCosmeticFilter(msg);
break;
}
};
return;
}
/******************************************************************************/
// Main context
{
if ( typeof µBlock !== 'object' ) { return; }
const workerTTL = 5 * 60 * 1000;
const pendingResponses = new Map();
let worker = null;
let workerTTLTimer;
let needLists = true;
let messageId = 1;
const onWorkerMessage = function(e) {
const msg = e.data;
const resolver = pendingResponses.get(msg.id);
pendingResponses.delete(msg.id);
resolver(msg.response);
};
const stopWorker = function() {
if ( workerTTLTimer !== undefined ) {
clearTimeout(workerTTLTimer);
workerTTLTimer = undefined;
}
if ( worker === null ) { return; }
worker.terminate();
worker = null;
needLists = true;
for ( const resolver of pendingResponses.values() ) {
resolver();
}
pendingResponses.clear();
};
const initWorker = function() {
if ( worker === null ) {
worker = new Worker('js/reverselookup.js');
worker.onmessage = onWorkerMessage;
}
// The worker will be shutdown after n minutes without being used.
if ( workerTTLTimer !== undefined ) {
clearTimeout(workerTTLTimer);
}
workerTTLTimer = vAPI.setTimeout(stopWorker, workerTTL);
if ( needLists === false ) {
return Promise.resolve();
}
needLists = false;
const entries = new Map();
const onListLoaded = function(details) {
const entry = entries.get(details.assetKey);
// https://github.com/gorhill/uBlock/issues/536
// Use assetKey when there is no filter list title.
worker.postMessage({
what: 'setList',
details: {
assetKey: details.assetKey,
title: entry.title || details.assetKey,
supportURL: entry.supportURL,
content: details.content
}
});
};
const µb = µBlock;
for ( const listKey in µb.availableFilterLists ) {
if ( µb.availableFilterLists.hasOwnProperty(listKey) === false ) {
continue;
}
const entry = µb.availableFilterLists[listKey];
if ( entry.off === true ) { continue; }
entries.set(listKey, {
title: listKey !== µb.userFiltersPath ?
entry.title :
vAPI.i18n('1pPageName'),
supportURL: entry.supportURL || ''
});
}
if ( entries.size === 0 ) {
return Promise.resolve();
}
const promises = [];
for ( const listKey of entries.keys() ) {
promises.push(
µb.getCompiledFilterList(listKey).then(details => {
onListLoaded(details);
})
);
}
return Promise.all(promises);
};
const fromNetFilter = async function(rawFilter) {
if ( typeof rawFilter !== 'string' || rawFilter === '' ) { return; }
const µb = µBlock;
const writer = new µb.CompiledListWriter();
const parser = new µb.StaticFilteringParser();
parser.setMaxTokenLength(µb.staticNetFilteringEngine.MAX_TOKEN_LENGTH);
parser.analyze(rawFilter);
if ( µb.staticNetFilteringEngine.compile(parser, writer) === false ) {
return;
}
await initWorker();
const id = messageId++;
worker.postMessage({
what: 'fromNetFilter',
id: id,
compiledFilter: writer.last(),
rawFilter: rawFilter
});
return new Promise(resolve => {
pendingResponses.set(id, resolve);
});
};
const fromCosmeticFilter = async function(details) {
if (
typeof details.rawFilter !== 'string' ||
details.rawFilter === ''
) {
return;
}
await initWorker();
const id = messageId++;
const hostname = µBlock.hostnameFromURI(details.url);
// https://github.com/gorhill/uBlock/issues/536
// Use assetKey when there is no filter list title.
worker.postMessage({
what: 'fromCosmeticFilter',
id: id,
domain: µBlock.domainFromHostname(hostname),
hostname: hostname,
ignoreGeneric:
µBlock.staticNetFilteringEngine.matchRequestReverse(
'generichide',
details.url
) === 2,
ignoreSpecific:
µBlock.staticNetFilteringEngine.matchRequestReverse(
'specifichide',
details.url
) === 2,
rawFilter: details.rawFilter
});
return new Promise(resolve => {
pendingResponses.set(id, resolve);
what: 'setList',
details: {
assetKey: details.assetKey,
title: entry.title || details.assetKey,
supportURL: entry.supportURL,
content: details.content
}
});
};
// This tells the worker that filter lists may have changed.
for ( const listKey in µb.availableFilterLists ) {
if ( µb.availableFilterLists.hasOwnProperty(listKey) === false ) {
continue;
}
const entry = µb.availableFilterLists[listKey];
if ( entry.off === true ) { continue; }
entries.set(listKey, {
title: listKey !== µb.userFiltersPath ?
entry.title :
vAPI.i18n('1pPageName'),
supportURL: entry.supportURL || ''
});
}
if ( entries.size === 0 ) {
return Promise.resolve();
}
const resetLists = function() {
needLists = true;
if ( worker === null ) { return; }
worker.postMessage({ what: 'resetLists' });
};
const promises = [];
for ( const listKey of entries.keys() ) {
promises.push(
µb.getCompiledFilterList(listKey).then(details => {
onListLoaded(details);
})
);
}
return Promise.all(promises);
};
µBlock.staticFilteringReverseLookup = {
fromNetFilter,
fromCosmeticFilter,
resetLists,
shutdown: stopWorker
};
}
const fromNetFilter = async function(rawFilter) {
if ( typeof rawFilter !== 'string' || rawFilter === '' ) { return; }
const writer = new CompiledListWriter();
const parser = new StaticFilteringParser();
parser.setMaxTokenLength(staticNetFilteringEngine.MAX_TOKEN_LENGTH);
parser.analyze(rawFilter);
if ( staticNetFilteringEngine.compile(parser, writer) === false ) {
return;
}
await initWorker();
const id = messageId++;
worker.postMessage({
what: 'fromNetFilter',
id: id,
compiledFilter: writer.last(),
rawFilter: rawFilter
});
return new Promise(resolve => {
pendingResponses.set(id, resolve);
});
};
const fromCosmeticFilter = async function(details) {
if (
typeof details.rawFilter !== 'string' ||
details.rawFilter === ''
) {
return;
}
await initWorker();
const id = messageId++;
const hostname = hostnameFromURI(details.url);
worker.postMessage({
what: 'fromCosmeticFilter',
id: id,
domain: domainFromHostname(hostname),
hostname: hostname,
ignoreGeneric:
staticNetFilteringEngine.matchRequestReverse(
'generichide',
details.url
) === 2,
ignoreSpecific:
staticNetFilteringEngine.matchRequestReverse(
'specifichide',
details.url
) === 2,
rawFilter: details.rawFilter
});
return new Promise(resolve => {
pendingResponses.set(id, resolve);
});
};
// This tells the worker that filter lists may have changed.
const resetLists = function() {
needLists = true;
if ( worker === null ) { return; }
worker.postMessage({ what: 'resetLists' });
};
/******************************************************************************/
// <<<<< end of local scope
})();
const staticFilteringReverseLookup = {
fromNetFilter,
fromCosmeticFilter,
resetLists,
shutdown: stopWorker
};
export default staticFilteringReverseLookup;
/******************************************************************************/

View File

@ -23,28 +23,35 @@
/******************************************************************************/
import logger from './logger.js';
import µb from './background.js';
import { redirectEngine } from './redirect-engine.js';
import { sessionFirewall } from './dynamic-net-filtering.js';
import {
StaticExtFilteringHostnameDB,
StaticExtFilteringSessionDB,
} from './static-ext-filtering-db.js';
import {
domainFromHostname,
entityFromDomain,
hostnameFromURI,
} from './uri-utils.js';
import µBlock from './background.js';
/******************************************************************************/
const µb = µBlock;
const duplicates = new Set();
const scriptletCache = new µb.MRUCache(32);
const reEscapeScriptArg = /[\\'"]/g;
const scriptletDB = new µb.staticExtFilteringEngine.HostnameBasedDB(1);
const sessionScriptletDB = new µb.staticExtFilteringEngine.SessionDB();
const scriptletDB = new StaticExtFilteringHostnameDB(1);
const sessionScriptletDB = new StaticExtFilteringSessionDB();
let acceptedCount = 0;
let discardedCount = 0;
const api = {
const scriptletFilteringEngine = {
get acceptedCount() {
return acceptedCount;
},
@ -132,7 +139,7 @@ const normalizeRawFilter = function(rawFilter) {
if ( end === -1 ) { end = rawEnd; }
const token = rawToken.slice(0, end).trim();
const alias = token.endsWith('.js') ? token.slice(0, -3) : token;
let normalized = µb.redirectEngine.aliases.get(`${alias}.js`);
let normalized = redirectEngine.aliases.get(`${alias}.js`);
normalized = normalized === undefined
? alias
: normalized.slice(0, -3);
@ -212,7 +219,7 @@ const patchScriptlet = function(content, args) {
};
const logOne = function(isException, token, details) {
µBlock.filteringContext
µb.filteringContext
.duplicate()
.fromTabId(details.tabId)
.setRealm('extended')
@ -226,19 +233,19 @@ const logOne = function(isException, token, details) {
.toLogger();
};
api.reset = function() {
scriptletFilteringEngine.reset = function() {
scriptletDB.clear();
duplicates.clear();
acceptedCount = 0;
discardedCount = 0;
};
api.freeze = function() {
scriptletFilteringEngine.freeze = function() {
duplicates.clear();
scriptletDB.collectGarbage();
};
api.compile = function(parser, writer) {
scriptletFilteringEngine.compile = function(parser, writer) {
writer.select(µb.compiledScriptletSection);
// Only exception filters are allowed to be global.
@ -272,7 +279,7 @@ api.compile = function(parser, writer) {
}
};
api.compileTemporary = function(parser) {
scriptletFilteringEngine.compileTemporary = function(parser) {
return {
session: sessionScriptletDB,
selector: parser.result.compiled,
@ -284,7 +291,7 @@ api.compileTemporary = function(parser) {
// ^ ^
// 4 -1
api.fromCompiledContent = function(reader) {
scriptletFilteringEngine.fromCompiledContent = function(reader) {
reader.select(µb.compiledScriptletSection);
while ( reader.next() ) {
@ -301,7 +308,7 @@ api.fromCompiledContent = function(reader) {
}
};
api.getSession = function() {
scriptletFilteringEngine.getSession = function() {
return sessionScriptletDB;
};
@ -309,12 +316,9 @@ const $scriptlets = new Set();
const $exceptions = new Set();
const $scriptletToCodeMap = new Map();
api.retrieve = function(request) {
scriptletFilteringEngine.retrieve = function(request) {
if ( scriptletDB.size === 0 ) { return; }
const reng = µb.redirectEngine;
if ( !reng ) { return; }
const hostname = request.hostname;
$scriptlets.clear();
@ -334,16 +338,14 @@ api.retrieve = function(request) {
// Do not inject scriptlets if the site is under an `allow` rule.
if (
µb.userSettings.advancedUserEnabled &&
µb.sessionFirewall.evaluateCellZY(hostname, hostname, '*') === 2
sessionFirewall.evaluateCellZY(hostname, hostname, '*') === 2
) {
return;
}
const loggerEnabled = µb.logger.enabled;
// Wholly disable scriptlet injection?
if ( $exceptions.has('') ) {
if ( loggerEnabled ) {
if ( logger.enabled ) {
logOne(true, '', request);
}
return;
@ -351,7 +353,7 @@ api.retrieve = function(request) {
$scriptletToCodeMap.clear();
for ( const rawToken of $scriptlets ) {
lookupScriptlet(rawToken, reng, $scriptletToCodeMap);
lookupScriptlet(rawToken, redirectEngine, $scriptletToCodeMap);
}
if ( $scriptletToCodeMap.size === 0 ) { return; }
@ -362,7 +364,7 @@ api.retrieve = function(request) {
if ( isException === false ) {
out.push(code);
}
if ( loggerEnabled ) {
if ( logger.enabled ) {
logOne(isException, rawToken, request);
}
}
@ -390,11 +392,11 @@ api.retrieve = function(request) {
return out.join('\n');
};
api.hasScriptlet = function(hostname, exceptionBit, scriptlet) {
scriptletFilteringEngine.hasScriptlet = function(hostname, exceptionBit, scriptlet) {
return scriptletDB.hasStr(hostname, exceptionBit, scriptlet);
};
api.injectNow = function(details) {
scriptletFilteringEngine.injectNow = function(details) {
if ( typeof details.frameId !== 'number' ) { return; }
const request = {
tabId: details.tabId,
@ -406,7 +408,7 @@ api.injectNow = function(details) {
};
request.domain = domainFromHostname(request.hostname);
request.entity = entityFromDomain(request.domain);
const scriptlets = µb.scriptletFilteringEngine.retrieve(request);
const scriptlets = this.retrieve(request);
if ( scriptlets === undefined ) { return; }
let code = contentscriptCode.assemble(request.hostname, scriptlets);
if ( µb.hiddenSettings.debugScriptletInjector ) {
@ -420,21 +422,21 @@ api.injectNow = function(details) {
});
};
api.toSelfie = function() {
scriptletFilteringEngine.toSelfie = function() {
return scriptletDB.toSelfie();
};
api.fromSelfie = function(selfie) {
scriptletFilteringEngine.fromSelfie = function(selfie) {
scriptletDB.fromSelfie(selfie);
};
api.benchmark = async function() {
scriptletFilteringEngine.benchmark = async function() {
const requests = await µb.loadBenchmarkDataset();
if ( Array.isArray(requests) === false || requests.length === 0 ) {
log.print('No requests found to benchmark');
console.info('No requests found to benchmark');
return;
}
log.print('Benchmarking scriptletFilteringEngine.retrieve()...');
console.info('Benchmarking scriptletFilteringEngine.retrieve()...');
const details = {
domain: '',
entity: '',
@ -456,14 +458,12 @@ api.benchmark = async function() {
}
const t1 = self.performance.now();
const dur = t1 - t0;
log.print(`Evaluated ${count} requests in ${dur.toFixed(0)} ms`);
log.print(`\tAverage: ${(dur / count).toFixed(3)} ms per request`);
console.info(`Evaluated ${count} requests in ${dur.toFixed(0)} ms`);
console.info(`\tAverage: ${(dur / count).toFixed(3)} ms per request`);
};
/******************************************************************************/
// Export
µBlock.scriptletFilteringEngine = api;
export default scriptletFilteringEngine;
/******************************************************************************/

View File

@ -23,7 +23,32 @@
/******************************************************************************/
import µBlock from './background.js';
import cacheStorage from './cachestorage.js';
import contextMenu from './contextmenu.js';
import io from './assets.js';
import lz4Codec from './lz4.js';
import staticExtFilteringEngine from './static-ext-filtering.js';
import staticFilteringReverseLookup from './reverselookup.js';
import staticNetFilteringEngine from './static-net-filtering.js';
import µb from './background.js';
import { redirectEngine } from './redirect-engine.js';
import { ubolog } from './console.js';
import { webRequest } from './traffic.js';
import {
permanentFirewall,
sessionFirewall,
} from './dynamic-net-filtering.js';
import {
permanentSwitches,
sessionSwitches,
} from './hnswitches.js';
import {
permanentURLFiltering,
sessionURLFiltering,
} from './url-net-filtering.js';
/******************************************************************************/
@ -32,21 +57,19 @@ import µBlock from './background.js';
(async ( ) => {
// >>>>> start of private scope
const µb = µBlock;
/******************************************************************************/
vAPI.app.onShutdown = function() {
µb.staticFilteringReverseLookup.shutdown();
µb.assets.updateStop();
µb.staticNetFilteringEngine.reset();
µb.staticExtFilteringEngine.reset();
µb.sessionFirewall.reset();
µb.permanentFirewall.reset();
µb.sessionURLFiltering.reset();
µb.permanentURLFiltering.reset();
µb.sessionSwitches.reset();
µb.permanentSwitches.reset();
staticFilteringReverseLookup.shutdown();
io.updateStop();
staticNetFilteringEngine.reset();
staticExtFilteringEngine.reset();
sessionFirewall.reset();
permanentFirewall.reset();
sessionURLFiltering.reset();
permanentURLFiltering.reset();
sessionSwitches.reset();
permanentSwitches.reset();
};
/******************************************************************************/
@ -116,7 +139,7 @@ const onVersionReady = function(lastVersion) {
// Since built-in resources may have changed since last version, we
// force a reload of all resources.
µb.redirectEngine.invalidateResourcesSelfie();
redirectEngine.invalidateResourcesSelfie();
// https://github.com/LiCybora/NanoDefenderFirefox/issues/196
// Toggle on the blocking of CSP reports by default for Firefox.
@ -124,8 +147,8 @@ const onVersionReady = function(lastVersion) {
lastVersionInt <= 1031003011 &&
vAPI.webextFlavor.soup.has('firefox')
) {
µb.sessionSwitches.toggle('no-csp-reports', '*', 1);
µb.permanentSwitches.toggle('no-csp-reports', '*', 1);
sessionSwitches.toggle('no-csp-reports', '*', 1);
permanentSwitches.toggle('no-csp-reports', '*', 1);
µb.saveHostnameSwitches();
}
};
@ -225,7 +248,7 @@ const onCacheSettingsReady = async function(fetched) {
}
if ( µb.selfieIsInvalid ) {
µb.selfieManager.destroy();
µb.cacheStorage.set(µb.systemSettings);
cacheStorage.set(µb.systemSettings);
}
};
@ -243,12 +266,12 @@ const onFirstFetchReady = function(fetched, adminExtra) {
fromFetch(µb.localSettings, fetched);
fromFetch(µb.restoreBackupSettings, fetched);
µb.permanentFirewall.fromString(fetched.dynamicFilteringString);
µb.sessionFirewall.assign(µb.permanentFirewall);
µb.permanentURLFiltering.fromString(fetched.urlFilteringString);
µb.sessionURLFiltering.assign(µb.permanentURLFiltering);
µb.permanentSwitches.fromString(fetched.hostnameSwitchesString);
µb.sessionSwitches.assign(µb.permanentSwitches);
permanentFirewall.fromString(fetched.dynamicFilteringString);
sessionFirewall.assign(permanentFirewall);
permanentURLFiltering.fromString(fetched.urlFilteringString);
sessionURLFiltering.assign(permanentURLFiltering);
permanentSwitches.fromString(fetched.hostnameSwitchesString);
sessionSwitches.assign(permanentSwitches);
onNetWhitelistReady(fetched.netWhitelist, adminExtra);
onVersionReady(fetched.version);
@ -307,10 +330,10 @@ const createDefaultProps = function() {
try {
// https://github.com/gorhill/uBlock/issues/531
await µb.restoreAdminSettings();
log.info(`Admin settings ready ${Date.now()-vAPI.T0} ms after launch`);
ubolog(`Admin settings ready ${Date.now()-vAPI.T0} ms after launch`);
await µb.loadHiddenSettings();
log.info(`Hidden settings ready ${Date.now()-vAPI.T0} ms after launch`);
ubolog(`Hidden settings ready ${Date.now()-vAPI.T0} ms after launch`);
// Maybe override current network listener suspend state
if ( µb.hiddenSettings.suspendTabsUntilReady === 'no' ) {
@ -320,42 +343,42 @@ try {
}
if ( µb.hiddenSettings.disableWebAssembly !== true ) {
µb.staticNetFilteringEngine.enableWASM('/js').then(( ) => {
log.info(`WASM modules ready ${Date.now()-vAPI.T0} ms after launch`);
staticNetFilteringEngine.enableWASM('/js').then(( ) => {
ubolog(`WASM modules ready ${Date.now()-vAPI.T0} ms after launch`);
});
}
const cacheBackend = await µb.cacheStorage.select(
const cacheBackend = await cacheStorage.select(
µb.hiddenSettings.cacheStorageAPI
);
log.info(`Backend storage for cache will be ${cacheBackend}`);
ubolog(`Backend storage for cache will be ${cacheBackend}`);
const adminExtra = await vAPI.adminStorage.get('toAdd');
log.info(`Extra admin settings ready ${Date.now()-vAPI.T0} ms after launch`);
ubolog(`Extra admin settings ready ${Date.now()-vAPI.T0} ms after launch`);
// https://github.com/uBlockOrigin/uBlock-issues/issues/1365
// Wait for onCacheSettingsReady() to be fully ready.
const [ , , lastVersion ] = await Promise.all([
µb.loadSelectedFilterLists().then(( ) => {
log.info(`List selection ready ${Date.now()-vAPI.T0} ms after launch`);
ubolog(`List selection ready ${Date.now()-vAPI.T0} ms after launch`);
}),
µb.cacheStorage.get(
cacheStorage.get(
{ compiledMagic: 0, selfieMagic: 0 }
).then(fetched => {
log.info(`Cache magic numbers ready ${Date.now()-vAPI.T0} ms after launch`);
ubolog(`Cache magic numbers ready ${Date.now()-vAPI.T0} ms after launch`);
onCacheSettingsReady(fetched);
}),
vAPI.storage.get(createDefaultProps()).then(fetched => {
log.info(`First fetch ready ${Date.now()-vAPI.T0} ms after launch`);
ubolog(`First fetch ready ${Date.now()-vAPI.T0} ms after launch`);
onFirstFetchReady(fetched, adminExtra);
return fetched.version;
}),
µb.loadUserSettings().then(fetched => {
log.info(`User settings ready ${Date.now()-vAPI.T0} ms after launch`);
ubolog(`User settings ready ${Date.now()-vAPI.T0} ms after launch`);
onUserSettingsReady(fetched);
}),
µb.loadPublicSuffixList().then(( ) => {
log.info(`PSL ready ${Date.now()-vAPI.T0} ms after launch`);
ubolog(`PSL ready ${Date.now()-vAPI.T0} ms after launch`);
}),
]);
@ -369,7 +392,7 @@ try {
}
// Prime the filtering engines before first use.
µb.staticNetFilteringEngine.prime();
staticNetFilteringEngine.prime();
// https://github.com/uBlockOrigin/uBlock-issues/issues/817#issuecomment-565730122
// Still try to load filter lists regardless of whether a serious error
@ -378,7 +401,7 @@ let selfieIsValid = false;
try {
selfieIsValid = await µb.selfieManager.load();
if ( selfieIsValid === true ) {
log.info(`Selfie ready ${Date.now()-vAPI.T0} ms after launch`);
ubolog(`Selfie ready ${Date.now()-vAPI.T0} ms after launch`);
}
} catch (ex) {
console.trace(ex);
@ -386,7 +409,7 @@ try {
if ( selfieIsValid !== true ) {
try {
await µb.loadFilterLists();
log.info(`Filter lists ready ${Date.now()-vAPI.T0} ms after launch`);
ubolog(`Filter lists ready ${Date.now()-vAPI.T0} ms after launch`);
} catch (ex) {
console.trace(ex);
}
@ -399,21 +422,21 @@ if ( selfieIsValid !== true ) {
µb.readyToFilter = true;
// Start network observers.
µb.webRequest.start();
webRequest.start();
// Ensure that the resources allocated for decompression purpose (likely
// large buffers) are garbage-collectable immediately after launch.
// Otherwise I have observed that it may take quite a while before the
// garbage collection of these resources kicks in. Relinquishing as soon
// as possible ensure minimal memory usage baseline.
µb.lz4Codec.relinquish();
lz4Codec.relinquish();
// Initialize internal state with maybe already existing tabs.
initializeTabs();
// https://github.com/chrisaljoudi/uBlock/issues/184
// Check for updates not too far in the future.
µb.assets.addObserver(µb.assetObserver.bind(µb));
io.addObserver(µb.assetObserver.bind(µb));
µb.scheduleAssetUpdater(
µb.userSettings.autoUpdate
? µb.hiddenSettings.autoUpdateDelayAfterLaunch * 1000
@ -422,7 +445,7 @@ initializeTabs();
// Force an update of the context menu according to the currently
// active tab.
µb.contextMenu.update();
contextMenu.update();
// Maybe install non-default popup document, or automatically select
// default UI according to platform.
@ -455,7 +478,7 @@ browser.runtime.onUpdateAvailable.addListener(details => {
}
});
log.info(`All ready ${Date.now()-vAPI.T0} ms after launch`);
ubolog(`All ready ${Date.now()-vAPI.T0} ms after launch`);
// <<<<< end of private scope
})();

View File

@ -0,0 +1,224 @@
/*******************************************************************************
uBlock Origin - a browser extension to block requests.
Copyright (C) 2017-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
*/
'use strict';
/******************************************************************************/
const StaticExtFilteringHostnameDB = class {
constructor(nBits, selfie = undefined) {
this.nBits = nBits;
this.timer = undefined;
this.strToIdMap = new Map();
this.hostnameToSlotIdMap = new Map();
// Array of integer pairs
this.hostnameSlots = [];
// Array of strings (selectors and pseudo-selectors)
this.strSlots = [];
this.size = 0;
if ( selfie !== undefined ) {
this.fromSelfie(selfie);
}
}
store(hn, bits, s) {
this.size += 1;
let iStr = this.strToIdMap.get(s);
if ( iStr === undefined ) {
iStr = this.strSlots.length;
this.strSlots.push(s);
this.strToIdMap.set(s, iStr);
if ( this.timer === undefined ) {
this.collectGarbage(true);
}
}
const strId = iStr << this.nBits | bits;
let iHn = this.hostnameToSlotIdMap.get(hn);
if ( iHn === undefined ) {
this.hostnameToSlotIdMap.set(hn, this.hostnameSlots.length);
this.hostnameSlots.push(strId, 0);
return;
}
// Add as last item.
while ( this.hostnameSlots[iHn+1] !== 0 ) {
iHn = this.hostnameSlots[iHn+1];
}
this.hostnameSlots[iHn+1] = this.hostnameSlots.length;
this.hostnameSlots.push(strId, 0);
}
clear() {
this.hostnameToSlotIdMap.clear();
this.hostnameSlots.length = 0;
this.strSlots.length = 0;
this.strToIdMap.clear();
this.size = 0;
}
collectGarbage(later = false) {
if ( later === false ) {
if ( this.timer !== undefined ) {
self.cancelIdleCallback(this.timer);
this.timer = undefined;
}
this.strToIdMap.clear();
return;
}
if ( this.timer !== undefined ) { return; }
this.timer = self.requestIdleCallback(
( ) => {
this.timer = undefined;
this.strToIdMap.clear();
},
{ timeout: 5000 }
);
}
// modifiers = 1: return only specific items
// modifiers = 2: return only generic items
//
retrieve(hostname, out, modifiers = 0) {
if ( modifiers === 2 ) {
hostname = '';
}
const mask = out.length - 1; // out.length must be power of two
for (;;) {
let iHn = this.hostnameToSlotIdMap.get(hostname);
if ( iHn !== undefined ) {
do {
const strId = this.hostnameSlots[iHn+0];
out[strId & mask].add(
this.strSlots[strId >>> this.nBits]
);
iHn = this.hostnameSlots[iHn+1];
} while ( iHn !== 0 );
}
if ( hostname === '' ) { break; }
const pos = hostname.indexOf('.');
if ( pos === -1 ) {
if ( modifiers === 1 ) { break; }
hostname = '';
} else {
hostname = hostname.slice(pos + 1);
}
}
}
hasStr(hostname, exceptionBit, value) {
let found = false;
for (;;) {
let iHn = this.hostnameToSlotIdMap.get(hostname);
if ( iHn !== undefined ) {
do {
const strId = this.hostnameSlots[iHn+0];
const str = this.strSlots[strId >>> this.nBits];
if ( (strId & exceptionBit) !== 0 ) {
if ( str === value || str === '' ) { return false; }
}
if ( str === value ) { found = true; }
iHn = this.hostnameSlots[iHn+1];
} while ( iHn !== 0 );
}
if ( hostname === '' ) { break; }
const pos = hostname.indexOf('.');
if ( pos !== -1 ) {
hostname = hostname.slice(pos + 1);
} else if ( hostname !== '*' ) {
hostname = '*';
} else {
hostname = '';
}
}
return found;
}
toSelfie() {
return {
hostnameToSlotIdMap: Array.from(this.hostnameToSlotIdMap),
hostnameSlots: this.hostnameSlots,
strSlots: this.strSlots,
size: this.size
};
}
fromSelfie(selfie) {
if ( selfie === undefined ) { return; }
this.hostnameToSlotIdMap = new Map(selfie.hostnameToSlotIdMap);
this.hostnameSlots = selfie.hostnameSlots;
this.strSlots = selfie.strSlots;
this.size = selfie.size;
}
};
/******************************************************************************/
const StaticExtFilteringSessionDB = class {
constructor() {
this.db = new Map();
}
compile(s) {
return s;
}
add(bits, s) {
const bucket = this.db.get(bits);
if ( bucket === undefined ) {
this.db.set(bits, new Set([ s ]));
} else {
bucket.add(s);
}
}
remove(bits, s) {
const bucket = this.db.get(bits);
if ( bucket === undefined ) { return; }
bucket.delete(s);
if ( bucket.size !== 0 ) { return; }
this.db.delete(bits);
}
retrieve(out) {
const mask = out.length - 1;
for ( const [ bits, bucket ] of this.db ) {
const i = bits & mask;
if ( out[i] instanceof Object === false ) { continue; }
for ( const s of bucket ) {
out[i].add(s);
}
}
}
has(bits, s) {
const selectors = this.db.get(bits);
return selectors !== undefined && selectors.has(s);
}
clear() {
this.db.clear();
}
get isNotEmpty() {
return this.db.size !== 0;
}
};
/******************************************************************************/
export {
StaticExtFilteringHostnameDB,
StaticExtFilteringSessionDB,
};
/******************************************************************************/

View File

@ -23,7 +23,12 @@
/******************************************************************************/
import µBlock from './background.js';
import cosmeticFilteringEngine from './cosmetic-filtering.js';
import htmlFilteringEngine from './html-filtering.js';
import httpheaderFilteringEngine from './httpheader-filtering.js';
import io from './assets.js';
import logger from './logger.js';
import scriptletFilteringEngine from './scriptlet-filtering.js';
/*******************************************************************************
@ -52,244 +57,49 @@ import µBlock from './background.js';
**/
const µb = µBlock;
//--------------------------------------------------------------------------
// Public API
//--------------------------------------------------------------------------
const api = {
const staticExtFilteringEngine = {
get acceptedCount() {
return µb.cosmeticFilteringEngine.acceptedCount +
µb.scriptletFilteringEngine.acceptedCount +
µb.httpheaderFilteringEngine.acceptedCount +
µb.htmlFilteringEngine.acceptedCount;
return cosmeticFilteringEngine.acceptedCount +
scriptletFilteringEngine.acceptedCount +
httpheaderFilteringEngine.acceptedCount +
htmlFilteringEngine.acceptedCount;
},
get discardedCount() {
return µb.cosmeticFilteringEngine.discardedCount +
µb.scriptletFilteringEngine.discardedCount +
µb.httpheaderFilteringEngine.discardedCount +
µb.htmlFilteringEngine.discardedCount;
return cosmeticFilteringEngine.discardedCount +
scriptletFilteringEngine.discardedCount +
httpheaderFilteringEngine.discardedCount +
htmlFilteringEngine.discardedCount;
},
};
//--------------------------------------------------------------------------
// Public classes
//--------------------------------------------------------------------------
api.HostnameBasedDB = class {
constructor(nBits, selfie = undefined) {
this.nBits = nBits;
this.timer = undefined;
this.strToIdMap = new Map();
this.hostnameToSlotIdMap = new Map();
// Array of integer pairs
this.hostnameSlots = [];
// Array of strings (selectors and pseudo-selectors)
this.strSlots = [];
this.size = 0;
if ( selfie !== undefined ) {
this.fromSelfie(selfie);
}
}
store(hn, bits, s) {
this.size += 1;
let iStr = this.strToIdMap.get(s);
if ( iStr === undefined ) {
iStr = this.strSlots.length;
this.strSlots.push(s);
this.strToIdMap.set(s, iStr);
if ( this.timer === undefined ) {
this.collectGarbage(true);
}
}
const strId = iStr << this.nBits | bits;
let iHn = this.hostnameToSlotIdMap.get(hn);
if ( iHn === undefined ) {
this.hostnameToSlotIdMap.set(hn, this.hostnameSlots.length);
this.hostnameSlots.push(strId, 0);
return;
}
// Add as last item.
while ( this.hostnameSlots[iHn+1] !== 0 ) {
iHn = this.hostnameSlots[iHn+1];
}
this.hostnameSlots[iHn+1] = this.hostnameSlots.length;
this.hostnameSlots.push(strId, 0);
}
clear() {
this.hostnameToSlotIdMap.clear();
this.hostnameSlots.length = 0;
this.strSlots.length = 0;
this.strToIdMap.clear();
this.size = 0;
}
collectGarbage(later = false) {
if ( later === false ) {
if ( this.timer !== undefined ) {
self.cancelIdleCallback(this.timer);
this.timer = undefined;
}
this.strToIdMap.clear();
return;
}
if ( this.timer !== undefined ) { return; }
this.timer = self.requestIdleCallback(
( ) => {
this.timer = undefined;
this.strToIdMap.clear();
},
{ timeout: 5000 }
);
}
// modifiers = 1: return only specific items
// modifiers = 2: return only generic items
//
retrieve(hostname, out, modifiers = 0) {
if ( modifiers === 2 ) {
hostname = '';
}
const mask = out.length - 1; // out.length must be power of two
for (;;) {
let iHn = this.hostnameToSlotIdMap.get(hostname);
if ( iHn !== undefined ) {
do {
const strId = this.hostnameSlots[iHn+0];
out[strId & mask].add(
this.strSlots[strId >>> this.nBits]
);
iHn = this.hostnameSlots[iHn+1];
} while ( iHn !== 0 );
}
if ( hostname === '' ) { break; }
const pos = hostname.indexOf('.');
if ( pos === -1 ) {
if ( modifiers === 1 ) { break; }
hostname = '';
} else {
hostname = hostname.slice(pos + 1);
}
}
}
hasStr(hostname, exceptionBit, value) {
let found = false;
for (;;) {
let iHn = this.hostnameToSlotIdMap.get(hostname);
if ( iHn !== undefined ) {
do {
const strId = this.hostnameSlots[iHn+0];
const str = this.strSlots[strId >>> this.nBits];
if ( (strId & exceptionBit) !== 0 ) {
if ( str === value || str === '' ) { return false; }
}
if ( str === value ) { found = true; }
iHn = this.hostnameSlots[iHn+1];
} while ( iHn !== 0 );
}
if ( hostname === '' ) { break; }
const pos = hostname.indexOf('.');
if ( pos !== -1 ) {
hostname = hostname.slice(pos + 1);
} else if ( hostname !== '*' ) {
hostname = '*';
} else {
hostname = '';
}
}
return found;
}
toSelfie() {
return {
hostnameToSlotIdMap: Array.from(this.hostnameToSlotIdMap),
hostnameSlots: this.hostnameSlots,
strSlots: this.strSlots,
size: this.size
};
}
fromSelfie(selfie) {
if ( selfie === undefined ) { return; }
this.hostnameToSlotIdMap = new Map(selfie.hostnameToSlotIdMap);
this.hostnameSlots = selfie.hostnameSlots;
this.strSlots = selfie.strSlots;
this.size = selfie.size;
}
};
api.SessionDB = class {
constructor() {
this.db = new Map();
}
compile(s) {
return s;
}
add(bits, s) {
const bucket = this.db.get(bits);
if ( bucket === undefined ) {
this.db.set(bits, new Set([ s ]));
} else {
bucket.add(s);
}
}
remove(bits, s) {
const bucket = this.db.get(bits);
if ( bucket === undefined ) { return; }
bucket.delete(s);
if ( bucket.size !== 0 ) { return; }
this.db.delete(bits);
}
retrieve(out) {
const mask = out.length - 1;
for ( const [ bits, bucket ] of this.db ) {
const i = bits & mask;
if ( out[i] instanceof Object === false ) { continue; }
for ( const s of bucket ) {
out[i].add(s);
}
}
}
has(bits, s) {
const selectors = this.db.get(bits);
return selectors !== undefined && selectors.has(s);
}
clear() {
this.db.clear();
}
get isNotEmpty() {
return this.db.size !== 0;
}
};
//--------------------------------------------------------------------------
// Public methods
//--------------------------------------------------------------------------
api.reset = function() {
µb.cosmeticFilteringEngine.reset();
µb.scriptletFilteringEngine.reset();
µb.httpheaderFilteringEngine.reset();
µb.htmlFilteringEngine.reset();
staticExtFilteringEngine.reset = function() {
cosmeticFilteringEngine.reset();
scriptletFilteringEngine.reset();
httpheaderFilteringEngine.reset();
htmlFilteringEngine.reset();
};
api.freeze = function() {
µb.cosmeticFilteringEngine.freeze();
µb.scriptletFilteringEngine.freeze();
µb.httpheaderFilteringEngine.freeze();
µb.htmlFilteringEngine.freeze();
staticExtFilteringEngine.freeze = function() {
cosmeticFilteringEngine.freeze();
scriptletFilteringEngine.freeze();
httpheaderFilteringEngine.freeze();
htmlFilteringEngine.freeze();
};
api.compile = function(parser, writer) {
staticExtFilteringEngine.compile = function(parser, writer) {
if ( parser.category !== parser.CATStaticExtFilter ) { return false; }
if ( (parser.flavorBits & parser.BITFlavorUnsupported) !== 0 ) {
const who = writer.properties.get('name') || '?';
µb.logger.writeOne({
logger.writeOne({
realm: 'message',
type: 'error',
text: `Invalid extended filter in ${who}: ${parser.raw}`
@ -299,13 +109,13 @@ api.compile = function(parser, writer) {
// Scriptlet injection
if ( (parser.flavorBits & parser.BITFlavorExtScriptlet) !== 0 ) {
µb.scriptletFilteringEngine.compile(parser, writer);
scriptletFilteringEngine.compile(parser, writer);
return true;
}
// Response header filtering
if ( (parser.flavorBits & parser.BITFlavorExtResponseHeader) !== 0 ) {
µb.httpheaderFilteringEngine.compile(parser, writer);
httpheaderFilteringEngine.compile(parser, writer);
return true;
}
@ -313,67 +123,65 @@ api.compile = function(parser, writer) {
// TODO: evaluate converting Adguard's `$$` syntax into uBO's HTML
// filtering syntax.
if ( (parser.flavorBits & parser.BITFlavorExtHTML) !== 0 ) {
µb.htmlFilteringEngine.compile(parser, writer);
htmlFilteringEngine.compile(parser, writer);
return true;
}
// Cosmetic filtering
µb.cosmeticFilteringEngine.compile(parser, writer);
cosmeticFilteringEngine.compile(parser, writer);
return true;
};
api.compileTemporary = function(parser) {
staticExtFilteringEngine.compileTemporary = function(parser) {
if ( (parser.flavorBits & parser.BITFlavorExtScriptlet) !== 0 ) {
return µb.scriptletFilteringEngine.compileTemporary(parser);
return scriptletFilteringEngine.compileTemporary(parser);
}
if ( (parser.flavorBits & parser.BITFlavorExtResponseHeader) !== 0 ) {
return µb.httpheaderFilteringEngine.compileTemporary(parser);
return httpheaderFilteringEngine.compileTemporary(parser);
}
if ( (parser.flavorBits & parser.BITFlavorExtHTML) !== 0 ) {
return µb.htmlFilteringEngine.compileTemporary(parser);
return htmlFilteringEngine.compileTemporary(parser);
}
return µb.cosmeticFilteringEngine.compileTemporary(parser);
return cosmeticFilteringEngine.compileTemporary(parser);
};
api.fromCompiledContent = function(reader, options) {
µb.cosmeticFilteringEngine.fromCompiledContent(reader, options);
µb.scriptletFilteringEngine.fromCompiledContent(reader, options);
µb.httpheaderFilteringEngine.fromCompiledContent(reader, options);
µb.htmlFilteringEngine.fromCompiledContent(reader, options);
staticExtFilteringEngine.fromCompiledContent = function(reader, options) {
cosmeticFilteringEngine.fromCompiledContent(reader, options);
scriptletFilteringEngine.fromCompiledContent(reader, options);
httpheaderFilteringEngine.fromCompiledContent(reader, options);
htmlFilteringEngine.fromCompiledContent(reader, options);
};
api.toSelfie = function(path) {
return µBlock.assets.put(
staticExtFilteringEngine.toSelfie = function(path) {
return io.put(
`${path}/main`,
JSON.stringify({
cosmetic: µb.cosmeticFilteringEngine.toSelfie(),
scriptlets: µb.scriptletFilteringEngine.toSelfie(),
httpHeaders: µb.httpheaderFilteringEngine.toSelfie(),
html: µb.htmlFilteringEngine.toSelfie(),
cosmetic: cosmeticFilteringEngine.toSelfie(),
scriptlets: scriptletFilteringEngine.toSelfie(),
httpHeaders: httpheaderFilteringEngine.toSelfie(),
html: htmlFilteringEngine.toSelfie(),
})
);
};
api.fromSelfie = function(path) {
return µBlock.assets.get(`${path}/main`).then(details => {
staticExtFilteringEngine.fromSelfie = function(path) {
return io.get(`${path}/main`).then(details => {
let selfie;
try {
selfie = JSON.parse(details.content);
} catch (ex) {
}
if ( selfie instanceof Object === false ) { return false; }
µb.cosmeticFilteringEngine.fromSelfie(selfie.cosmetic);
µb.scriptletFilteringEngine.fromSelfie(selfie.scriptlets);
µb.httpheaderFilteringEngine.fromSelfie(selfie.httpHeaders);
µb.htmlFilteringEngine.fromSelfie(selfie.html);
cosmeticFilteringEngine.fromSelfie(selfie.cosmetic);
scriptletFilteringEngine.fromSelfie(selfie.scriptlets);
httpheaderFilteringEngine.fromSelfie(selfie.httpHeaders);
htmlFilteringEngine.fromSelfie(selfie.html);
return true;
});
};
/******************************************************************************/
// Export
µBlock.staticExtFilteringEngine = api;
export default staticExtFilteringEngine;
/******************************************************************************/

View File

@ -3396,8 +3396,6 @@ FilterContainer.prototype.freeze = function() {
const filterBucketId = FilterBucket.fid;
const unserialize = CompiledListReader.unserialize;
const t0 = Date.now();
for ( const line of this.goodFilters ) {
if ( this.badFilters.has(line) ) {
this.discardedCount += 1;
@ -3487,8 +3485,6 @@ FilterContainer.prototype.freeze = function() {
this.optimize();
}, { timeout: 5000 });
}
console.info(`staticNetFilteringEngine.freeze() took ${Date.now()-t0} ms`);
};
/******************************************************************************/
@ -3499,8 +3495,6 @@ FilterContainer.prototype.optimize = function() {
this.optimizeTimerId = undefined;
}
const t0 = Date.now();
for ( let bits = 0, n = this.categories.length; bits < n; bits++ ) {
const bucket = this.categories[bits];
if ( bucket === undefined ) { continue; }
@ -3523,8 +3517,6 @@ FilterContainer.prototype.optimize = function() {
for ( let i = filterUnitWritePtr, n = filterUnits.length; i < n; i++ ) {
filterUnits[i] = null;
}
console.info(`staticNetFilteringEngine.optimize() took ${Date.now()-t0} ms`);
};
/******************************************************************************/
@ -4477,9 +4469,7 @@ FilterContainer.prototype.benchmark = async function(requests, options = {}) {
return text;
}
const print = log.print;
print(`Benchmarking staticNetFilteringEngine.matchRequest()...`);
console.info(`Benchmarking staticNetFilteringEngine.matchRequest()...`);
const fctxt = new FilteringContext();
@ -4489,12 +4479,12 @@ FilterContainer.prototype.benchmark = async function(requests, options = {}) {
fctxt.setDocOriginFromURL(request.frameUrl);
fctxt.setType(request.cpt);
const r = this.matchRequest(fctxt);
print(`Result=${r}:`);
print(`\ttype=${fctxt.type}`);
print(`\turl=${fctxt.url}`);
print(`\tdocOrigin=${fctxt.getDocOrigin()}`);
console.info(`Result=${r}:`);
console.info(`\ttype=${fctxt.type}`);
console.info(`\turl=${fctxt.url}`);
console.info(`\tdocOrigin=${fctxt.getDocOrigin()}`);
if ( r !== 0 ) {
console.log(this.toLogData());
console.info(this.toLogData());
}
return;
}
@ -4524,11 +4514,11 @@ FilterContainer.prototype.benchmark = async function(requests, options = {}) {
matchCount += 1;
if ( recorded !== undefined ) { recorded.push(r); }
if ( expected !== undefined && r !== expected[i] ) {
print(`Mismatch with reference results at ${i}:`);
print(`\tExpected ${expected[i]}, got ${r}:`);
print(`\ttype=${fctxt.type}`);
print(`\turl=${fctxt.url}`);
print(`\tdocOrigin=${fctxt.getDocOrigin()}`);
console.info(`Mismatch with reference results at ${i}:`);
console.info(`\tExpected ${expected[i]}, got ${r}:`);
console.info(`\ttype=${fctxt.type}`);
console.info(`\turl=${fctxt.url}`);
console.info(`\tdocOrigin=${fctxt.getDocOrigin()}`);
}
if ( r !== 1 ) {
if ( this.hasQuery(fctxt) ) {
@ -4564,7 +4554,7 @@ FilterContainer.prototype.benchmark = async function(requests, options = {}) {
);
}
const s = output.join('\n');
print(s);
console.info(s);
return s;
};
@ -4576,9 +4566,9 @@ FilterContainer.prototype.test = async function(docURL, type, url) {
fctxt.setType(type);
fctxt.setURL(url);
const r = this.matchRequest(fctxt);
console.log(`${r}`);
console.info(`${r}`);
if ( r !== 0 ) {
console.log(this.toLogData());
console.info(this.toLogData());
}
};
@ -4643,7 +4633,7 @@ FilterContainer.prototype.bucketHistogram = function() {
results.sort((a, b) => {
return b.size - a.size;
});
console.log(results);
console.info(results);
};
/*******************************************************************************
@ -4733,7 +4723,7 @@ FilterContainer.prototype.filterClassHistogram = function() {
const results = Array.from(filterClassDetails.values()).sort((a, b) => {
return b.count - a.count;
});
console.log(results);
console.info(results);
};
/******************************************************************************/
@ -4772,14 +4762,12 @@ FilterContainer.prototype.tokenHistograms = async function(requests) {
hitTokenMap.delete(token);
}
const tophits = Array.from(hitTokenMap).sort(customSort).slice(0, 100);
console.log('Misses:', JSON.stringify(topmisses));
console.log('Hits:', JSON.stringify(tophits));
console.info('Misses:', JSON.stringify(topmisses));
console.info('Hits:', JSON.stringify(tophits));
};
/******************************************************************************/
const staticNetFilteringEngine = new FilterContainer();
/******************************************************************************/
export { staticNetFilteringEngine };
export default staticNetFilteringEngine;

View File

@ -26,12 +26,24 @@
import '../lib/publicsuffixlist/publicsuffixlist.js';
import '../lib/punycode.js';
import cosmeticFilteringEngine from './cosmetic-filtering.js';
import globals from './globals.js';
import io from './assets.js';
import logger from './logger.js';
import lz4Codec from './lz4.js';
import staticExtFilteringEngine from './static-ext-filtering.js';
import staticFilteringReverseLookup from './reverselookup.js';
import staticNetFilteringEngine from './static-net-filtering.js';
import µb from './background.js';
import { hostnameFromURI } from './uri-utils.js';
import { sparseBase64 } from './base64-custom.js';
import { LineIterator } from './text-iterators.js';
import { permanentFirewall } from './dynamic-net-filtering.js';
import { permanentSwitches } from './hnswitches.js';
import { permanentURLFiltering } from './url-net-filtering.js';
import { redirectEngine } from './redirect-engine.js';
import { sparseBase64 } from './base64-custom.js';
import { StaticFilteringParser } from './static-filtering-parser.js';
import µBlock from './background.js';
import { ubolog, ubologSet } from './console.js';
import {
CompiledListReader,
@ -40,7 +52,7 @@ import {
/******************************************************************************/
µBlock.getBytesInUse = async function() {
µb.getBytesInUse = async function() {
const promises = [];
let bytesInUse;
@ -71,17 +83,16 @@ import {
if ( results.length > 1 && results[1] instanceof Object ) {
processCount(results[1].usage);
}
µBlock.storageUsed = bytesInUse;
µb.storageUsed = bytesInUse;
return bytesInUse;
};
/******************************************************************************/
µBlock.saveLocalSettings = (( ) => {
µb.saveLocalSettings = (( ) => {
const saveAfter = 4 * 60 * 1000;
const onTimeout = ( ) => {
const µb = µBlock;
if ( µb.localSettingsLastModified > µb.localSettingsLastSaved ) {
µb.saveLocalSettings();
}
@ -98,7 +109,7 @@ import {
/******************************************************************************/
µBlock.loadUserSettings = async function() {
µb.loadUserSettings = async function() {
const usDefault = this.userSettingsDefault;
const results = await Promise.all([
@ -126,7 +137,7 @@ import {
return usUser;
};
µBlock.saveUserSettings = function() {
µb.saveUserSettings = function() {
const toSave = this.getModifiedSettings(
this.userSettings,
this.userSettingsDefault
@ -154,7 +165,7 @@ import {
// Admin hidden settings have precedence over user hidden settings.
µBlock.loadHiddenSettings = async function() {
µb.loadHiddenSettings = async function() {
const hsDefault = this.hiddenSettingsDefault;
const hsAdmin = this.hiddenSettingsAdmin;
const hsUser = this.hiddenSettings;
@ -186,7 +197,7 @@ import {
hsDefault[name] = hsAdmin[name] = hsUser[name] = value;
}
}
µBlock.noDashboard = disableDashboard === true;
µb.noDashboard = disableDashboard === true;
if ( Array.isArray(disabledPopupPanelParts) ) {
const partNameToBit = new Map([
[ 'globalStats', 0b00010 ],
@ -230,7 +241,7 @@ import {
// This way the new default values in the future will properly apply for
// those which were not modified by the user.
µBlock.saveHiddenSettings = function() {
µb.saveHiddenSettings = function() {
vAPI.storage.set({
hiddenSettings: this.getModifiedSettings(
this.hiddenSettings,
@ -240,8 +251,8 @@ import {
};
self.addEventListener('hiddenSettingsChanged', ( ) => {
const µbhs = µBlock.hiddenSettings;
self.log.verbosity = µbhs.consoleLogLevel;
const µbhs = µb.hiddenSettings;
ubologSet(µbhs.consoleLogLevel === 'info');
vAPI.net.setOptions({
cnameIgnoreList: µbhs.cnameIgnoreList,
cnameIgnore1stParty: µbhs.cnameIgnore1stParty,
@ -255,7 +266,7 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
/******************************************************************************/
µBlock.hiddenSettingsFromString = function(raw) {
µb.hiddenSettingsFromString = function(raw) {
const out = Object.assign({}, this.hiddenSettingsDefault);
const lineIter = new LineIterator(raw);
while ( lineIter.eot() === false ) {
@ -273,7 +284,7 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
return out;
};
µBlock.hiddenSettingValueFromString = function(name, value) {
µb.hiddenSettingValueFromString = function(name, value) {
if ( typeof name !== 'string' || typeof value !== 'string' ) { return; }
const hsDefault = this.hiddenSettingsDefault;
if ( hsDefault.hasOwnProperty(name) === false ) { return; }
@ -305,7 +316,7 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
return r;
};
µBlock.stringFromHiddenSettings = function() {
µb.stringFromHiddenSettings = function() {
const out = [];
for ( const key of Object.keys(this.hiddenSettings).sort() ) {
out.push(key + ' ' + this.hiddenSettings[key]);
@ -315,31 +326,31 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
/******************************************************************************/
µBlock.savePermanentFirewallRules = function() {
µb.savePermanentFirewallRules = function() {
vAPI.storage.set({
dynamicFilteringString: this.permanentFirewall.toString()
dynamicFilteringString: permanentFirewall.toString()
});
};
/******************************************************************************/
µBlock.savePermanentURLFilteringRules = function() {
µb.savePermanentURLFilteringRules = function() {
vAPI.storage.set({
urlFilteringString: this.permanentURLFiltering.toString()
urlFilteringString: permanentURLFiltering.toString()
});
};
/******************************************************************************/
µBlock.saveHostnameSwitches = function() {
µb.saveHostnameSwitches = function() {
vAPI.storage.set({
hostnameSwitchesString: this.permanentSwitches.toString()
hostnameSwitchesString: permanentSwitches.toString()
});
};
/******************************************************************************/
µBlock.saveWhitelist = function() {
µb.saveWhitelist = function() {
vAPI.storage.set({
netWhitelist: this.arrayFromWhitelist(this.netWhitelist)
});
@ -348,7 +359,7 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
/******************************************************************************/
µBlock.loadSelectedFilterLists = async function() {
µb.loadSelectedFilterLists = async function() {
const bin = await vAPI.storage.get('selectedFilterLists');
if ( bin instanceof Object && Array.isArray(bin.selectedFilterLists) ) {
this.selectedFilterLists = bin.selectedFilterLists;
@ -357,11 +368,11 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
// https://github.com/gorhill/uBlock/issues/747
// Select default filter lists if first-time launch.
const lists = await this.assets.metadata();
const lists = await io.metadata();
this.saveSelectedFilterLists(this.autoSelectRegionalFilterLists(lists));
};
µBlock.saveSelectedFilterLists = function(newKeys, append = false) {
µb.saveSelectedFilterLists = function(newKeys, append = false) {
const oldKeys = this.selectedFilterLists.slice();
if ( append ) {
newKeys = newKeys.concat(oldKeys);
@ -380,7 +391,7 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
/******************************************************************************/
µBlock.applyFilterListSelection = function(details) {
µb.applyFilterListSelection = function(details) {
let selectedListKeySet = new Set(this.selectedFilterLists);
let importedLists = this.userSettings.importedLists.slice();
@ -458,7 +469,7 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
/******************************************************************************/
µBlock.listKeysFromCustomFilterLists = function(raw) {
µb.listKeysFromCustomFilterLists = function(raw) {
const urls = typeof raw === 'string'
? raw.trim().split(/[\n\r]+/)
: raw;
@ -476,20 +487,20 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
/******************************************************************************/
µBlock.saveUserFilters = function(content) {
µb.saveUserFilters = function(content) {
// https://github.com/gorhill/uBlock/issues/1022
// Be sure to end with an empty line.
content = content.trim();
if ( content !== '' ) { content += '\n'; }
this.removeCompiledFilterList(this.userFiltersPath);
return this.assets.put(this.userFiltersPath, content);
return io.put(this.userFiltersPath, content);
};
µBlock.loadUserFilters = function() {
return this.assets.get(this.userFiltersPath);
µb.loadUserFilters = function() {
return io.get(this.userFiltersPath);
};
µBlock.appendUserFilters = async function(filters, options) {
µb.appendUserFilters = async function(filters, options) {
filters = filters.trim();
if ( filters.length === 0 ) { return; }
@ -542,8 +553,8 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
const compiledFilters = this.compileFilters(filters, {
assetKey: this.userFiltersPath
});
const snfe = this.staticNetFilteringEngine;
const cfe = this.cosmeticFilteringEngine;
const snfe = staticNetFilteringEngine;
const cfe = cosmeticFilteringEngine;
const acceptedCount = snfe.acceptedCount + cfe.acceptedCount;
const discardedCount = snfe.discardedCount + cfe.discardedCount;
this.applyCompiledFilters(compiledFilters, true);
@ -557,9 +568,9 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
entry.entryCount += deltaEntryCount;
entry.entryUsedCount += deltaEntryUsedCount;
vAPI.storage.set({ 'availableFilterLists': this.availableFilterLists });
this.staticNetFilteringEngine.freeze();
this.redirectEngine.freeze();
this.staticExtFilteringEngine.freeze();
staticNetFilteringEngine.freeze();
redirectEngine.freeze();
staticExtFilteringEngine.freeze();
this.selfieManager.destroy();
// https://www.reddit.com/r/uBlockOrigin/comments/cj7g7m/
@ -571,18 +582,18 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
vAPI.messaging.broadcast({ what: 'userFiltersUpdated' });
};
µBlock.createUserFilters = function(details) {
µb.createUserFilters = function(details) {
this.appendUserFilters(details.filters, details);
// https://github.com/gorhill/uBlock/issues/1786
if ( details.docURL === undefined ) { return; }
this.cosmeticFilteringEngine.removeFromSelectorCache(
cosmeticFilteringEngine.removeFromSelectorCache(
hostnameFromURI(details.docURL)
);
};
/******************************************************************************/
µBlock.autoSelectRegionalFilterLists = function(lists) {
µb.autoSelectRegionalFilterLists = function(lists) {
const selectedListKeys = [ this.userFiltersPath ];
for ( const key in lists ) {
if ( lists.hasOwnProperty(key) === false ) { continue; }
@ -601,7 +612,7 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
/******************************************************************************/
µBlock.getAvailableLists = async function() {
µb.getAvailableLists = async function() {
let oldAvailableLists = {},
newAvailableLists = {};
@ -625,7 +636,7 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
title: '',
};
newAvailableLists[listKey] = entry;
this.assets.registerAssetSource(listKey, entry);
io.registerAssetSource(listKey, entry);
}
// Convert a no longer existing stock list into an imported list.
@ -645,7 +656,7 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
title: oldEntry.title || ''
};
newAvailableLists[listURL] = newEntry;
this.assets.registerAssetSource(listURL, newEntry);
io.registerAssetSource(listURL, newEntry);
importedListKeys.push(listURL);
this.userSettings.importedLists.push(listURL.trim());
this.saveUserSettings();
@ -654,8 +665,8 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
const promises = [
vAPI.storage.get('availableFilterLists'),
this.assets.metadata(),
this.badLists.size === 0 ? this.assets.get('ublock-badlists') : false,
io.metadata(),
this.badLists.size === 0 ? io.get('ublock-badlists') : false,
];
// Load previously saved available lists -- these contains data
@ -731,7 +742,7 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
if ( newEntry.submitter !== 'user' ) { continue; }
if ( importedListKeys.indexOf(assetKey) !== -1 ) { continue; }
delete newAvailableLists[assetKey];
this.assets.unregisterAssetSource(assetKey);
io.unregisterAssetSource(assetKey);
this.removeFilterList(assetKey);
}
@ -740,17 +751,17 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
/******************************************************************************/
µBlock.loadFilterLists = (( ) => {
µb.loadFilterLists = (( ) => {
const loadedListKeys = [];
let loadingPromise;
let t0 = 0;
const onDone = function() {
log.info(`loadFilterLists() took ${Date.now()-t0} ms`);
ubolog(`loadFilterLists() took ${Date.now()-t0} ms`);
this.staticNetFilteringEngine.freeze();
this.staticExtFilteringEngine.freeze();
this.redirectEngine.freeze();
staticNetFilteringEngine.freeze();
staticExtFilteringEngine.freeze();
redirectEngine.freeze();
vAPI.net.unsuspend();
vAPI.storage.set({ 'availableFilterLists': this.availableFilterLists });
@ -763,15 +774,15 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
});
this.selfieManager.destroy();
this.lz4Codec.relinquish();
lz4Codec.relinquish();
this.compiledFormatChanged = false;
loadingPromise = undefined;
};
const applyCompiledFilters = function(assetKey, compiled) {
const snfe = this.staticNetFilteringEngine;
const sxfe = this.staticExtFilteringEngine;
const snfe = staticNetFilteringEngine;
const sxfe = staticExtFilteringEngine;
let acceptedCount = snfe.acceptedCount + sxfe.acceptedCount,
discardedCount = snfe.discardedCount + sxfe.discardedCount;
this.applyCompiledFilters(compiled, assetKey === this.userFiltersPath);
@ -789,15 +800,15 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
this.availableFilterLists = lists;
vAPI.net.suspend();
this.redirectEngine.reset();
this.staticExtFilteringEngine.reset();
this.staticNetFilteringEngine.reset();
redirectEngine.reset();
staticExtFilteringEngine.reset();
staticNetFilteringEngine.reset();
this.selfieManager.destroy();
this.staticFilteringReverseLookup.resetLists();
staticFilteringReverseLookup.resetLists();
// We need to build a complete list of assets to pull first: this is
// because it *may* happens that some load operations are synchronous:
// This happens for assets which do not exist, ot assets with no
// This happens for assets which do not exist, or assets with no
// content.
const toLoad = [];
for ( const assetKey in lists ) {
@ -837,7 +848,7 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
/******************************************************************************/
µBlock.getCompiledFilterList = async function(assetKey) {
µb.getCompiledFilterList = async function(assetKey) {
const compiledPath = 'compiled/' + assetKey;
// https://github.com/uBlockOrigin/uBlock-issues/issues/1365
@ -847,7 +858,7 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
this.compiledFormatChanged === false &&
this.badLists.has(assetKey) === false
) {
const compiledDetails = await this.assets.get(compiledPath);
const compiledDetails = await io.get(compiledPath);
if (
parseInt(compiledDetails.content, 10) ===
this.systemSettings.compiledMagic
@ -862,7 +873,7 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
return { assetKey, content: '' };
}
const rawDetails = await this.assets.get(assetKey, { silent: true });
const rawDetails = await io.get(assetKey, { silent: true });
// Compiling an empty string results in an empty string.
if ( rawDetails.content === '' ) {
rawDetails.assetKey = assetKey;
@ -878,7 +889,7 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
const compiledContent =
this.compileFilters(rawDetails.content, { assetKey });
this.assets.put(compiledPath, compiledContent);
io.put(compiledPath, compiledContent);
return { assetKey, content: compiledContent };
};
@ -892,7 +903,7 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
// the whole raw filter list to be held in memory just because we cut out
// the title as a substring.
µBlock.extractFilterListMetadata = function(assetKey, raw) {
µb.extractFilterListMetadata = function(assetKey, raw) {
const listEntry = this.availableFilterLists[assetKey];
if ( listEntry === undefined ) { return; }
// Metadata expected to be found at the top of content.
@ -904,13 +915,13 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
const title = matches && matches[1].trim() || '';
if ( title !== '' && title !== listEntry.title ) {
listEntry.title = this.orphanizeString(title);
this.assets.registerAssetSource(assetKey, { title });
io.registerAssetSource(assetKey, { title });
}
matches = head.match(/(?:^|\n)(?:!|# )[\t ]*Homepage[\t ]*:[\t ]*(https?:\/\/\S+)\s/i);
const supportURL = matches && matches[1] || '';
if ( supportURL !== '' && supportURL !== listEntry.supportURL ) {
listEntry.supportURL = this.orphanizeString(supportURL);
this.assets.registerAssetSource(assetKey, { supportURL });
io.registerAssetSource(assetKey, { supportURL });
}
}
// Extract update frequency information
@ -924,7 +935,7 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
updateAfter = Math.max(updateAfter, 1);
if ( updateAfter !== listEntry.updateAfter ) {
listEntry.updateAfter = updateAfter;
this.assets.registerAssetSource(assetKey, { updateAfter });
io.registerAssetSource(assetKey, { updateAfter });
}
}
}
@ -932,18 +943,18 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
/******************************************************************************/
µBlock.removeCompiledFilterList = function(assetKey) {
this.assets.remove('compiled/' + assetKey);
µb.removeCompiledFilterList = function(assetKey) {
io.remove('compiled/' + assetKey);
};
µBlock.removeFilterList = function(assetKey) {
µb.removeFilterList = function(assetKey) {
this.removeCompiledFilterList(assetKey);
this.assets.remove(assetKey);
io.remove(assetKey);
};
/******************************************************************************/
µBlock.compileFilters = function(rawText, details = {}) {
µb.compileFilters = function(rawText, details = {}) {
const writer = new CompiledListWriter();
// Populate the writer with information potentially useful to the
@ -957,8 +968,6 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
// Useful references:
// https://adblockplus.org/en/filter-cheatsheet
// https://adblockplus.org/en/filters
const staticNetFilteringEngine = this.staticNetFilteringEngine;
const staticExtFilteringEngine = this.staticExtFilteringEngine;
const lineIter = new LineIterator(this.preparseDirectives.prune(rawText));
const parser = new StaticFilteringParser({ expertMode });
@ -990,7 +999,7 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
}
if ( staticNetFilteringEngine.compile(parser, writer) ) { continue; }
if ( staticNetFilteringEngine.error !== undefined ) {
this.logger.writeOne({
logger.writeOne({
realm: 'message',
type: 'error',
text: staticNetFilteringEngine.error
@ -1013,11 +1022,11 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
// Added `firstparty` argument: to avoid discarding cosmetic filters when
// applying 1st-party filters.
µBlock.applyCompiledFilters = function(rawText, firstparty) {
µb.applyCompiledFilters = function(rawText, firstparty) {
if ( rawText === '' ) { return; }
const reader = new CompiledListReader(rawText);
this.staticNetFilteringEngine.fromCompiled(reader);
this.staticExtFilteringEngine.fromCompiledContent(reader, {
staticNetFilteringEngine.fromCompiled(reader);
staticExtFilteringEngine.fromCompiledContent(reader, {
skipGenericCosmetic: this.userSettings.ignoreGenericCosmeticFilters,
skipCosmetic: !firstparty && !this.userSettings.parseAllABPHideFilters
});
@ -1027,7 +1036,7 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
// https://github.com/AdguardTeam/AdguardBrowserExtension/issues/917
µBlock.preparseDirectives = {
µb.preparseDirectives = {
// This method returns an array of indices, corresponding to position in
// the content string which should alternatively be parsed and discarded.
split: function(content) {
@ -1140,19 +1149,19 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
/******************************************************************************/
µBlock.loadRedirectResources = async function() {
µb.loadRedirectResources = async function() {
try {
const success = await this.redirectEngine.resourcesFromSelfie();
const success = await redirectEngine.resourcesFromSelfie();
if ( success === true ) { return true; }
const fetchPromises = [
this.redirectEngine.loadBuiltinResources()
redirectEngine.loadBuiltinResources()
];
const userResourcesLocation = this.hiddenSettings.userResourcesLocation;
if ( userResourcesLocation !== 'unset' ) {
for ( const url of userResourcesLocation.split(/\s+/) ) {
fetchPromises.push(this.assets.fetchText(url));
fetchPromises.push(io.fetchText(url));
}
}
@ -1172,10 +1181,10 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
content += '\n\n' + result.content;
}
this.redirectEngine.resourcesFromString(content);
this.redirectEngine.selfieFromResources();
redirectEngine.resourcesFromString(content);
redirectEngine.selfieFromResources();
} catch(ex) {
log.info(ex);
ubolog(ex);
return false;
}
return true;
@ -1183,31 +1192,31 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
/******************************************************************************/
µBlock.loadPublicSuffixList = async function() {
µb.loadPublicSuffixList = async function() {
const psl = globals.publicSuffixList;
if ( this.hiddenSettings.disableWebAssembly !== true ) {
psl.enableWASM('/lib/publicsuffixlist');
}
try {
const result = await this.assets.get(`compiled/${this.pslAssetKey}`);
const result = await io.get(`compiled/${this.pslAssetKey}`);
if ( psl.fromSelfie(result.content, sparseBase64) ) {
return;
}
} catch (ex) {
log.info(ex);
ubolog(ex);
}
const result = await this.assets.get(this.pslAssetKey);
const result = await io.get(this.pslAssetKey);
if ( result.content !== '' ) {
this.compilePublicSuffixList(result.content);
}
};
µBlock.compilePublicSuffixList = function(content) {
µb.compilePublicSuffixList = function(content) {
const psl = globals.publicSuffixList;
psl.parse(content, globals.punycode.toASCII);
this.assets.put(`compiled/${this.pslAssetKey}`, psl.toSelfie(sparseBase64));
io.put(`compiled/${this.pslAssetKey}`, psl.toSelfie(sparseBase64));
};
/******************************************************************************/
@ -1216,8 +1225,7 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
// be generated if the user doesn't change his filter lists selection for
// some set time.
µBlock.selfieManager = (( ) => {
const µb = µBlock;
µb.selfieManager = (( ) => {
let createTimer;
let destroyTimer;
@ -1227,28 +1235,27 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
const create = async function() {
await Promise.all([
µb.assets.put(
io.put(
'selfie/main',
JSON.stringify({
magic: µb.systemSettings.selfieMagic,
availableFilterLists: µb.availableFilterLists,
})
),
µb.redirectEngine.toSelfie('selfie/redirectEngine'),
µb.staticExtFilteringEngine.toSelfie(
redirectEngine.toSelfie('selfie/redirectEngine'),
staticExtFilteringEngine.toSelfie(
'selfie/staticExtFilteringEngine'
),
µb.staticNetFilteringEngine.toSelfie(
µb.assets,
staticNetFilteringEngine.toSelfie(io,
'selfie/staticNetFilteringEngine'
),
]);
µb.lz4Codec.relinquish();
lz4Codec.relinquish();
µb.selfieIsInvalid = false;
};
const loadMain = async function() {
const details = await µb.assets.get('selfie/main');
const details = await io.get('selfie/main');
if (
details instanceof Object === false ||
typeof details.content !== 'string' ||
@ -1278,12 +1285,11 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
try {
const results = await Promise.all([
loadMain(),
µb.redirectEngine.fromSelfie('selfie/redirectEngine'),
µb.staticExtFilteringEngine.fromSelfie(
redirectEngine.fromSelfie('selfie/redirectEngine'),
staticExtFilteringEngine.fromSelfie(
'selfie/staticExtFilteringEngine'
),
µb.staticNetFilteringEngine.fromSelfie(
µb.assets,
staticNetFilteringEngine.fromSelfie(io,
'selfie/staticNetFilteringEngine'
),
]);
@ -1292,14 +1298,14 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
}
}
catch (reason) {
log.info(reason);
ubolog(reason);
}
destroy();
return false;
};
const destroy = function() {
µb.assets.remove(/^selfie\//);
io.remove(/^selfie\//);
µb.selfieIsInvalid = true;
createTimer = vAPI.setTimeout(( ) => {
createTimer = undefined;
@ -1335,7 +1341,7 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
// necessarily present, i.e. administrators may removed entries which
// values are left to the user's choice.
µBlock.restoreAdminSettings = async function() {
µb.restoreAdminSettings = async function() {
let toOverwrite = {};
let data;
try {
@ -1368,7 +1374,7 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
typeof data.assetsBootstrapLocation === 'string' &&
data.assetsBootstrapLocation !== ''
) {
µBlock.assetsBootstrapLocation = data.assetsBootstrapLocation;
µb.assetsBootstrapLocation = data.assetsBootstrapLocation;
}
if ( typeof data.userSettings === 'object' ) {
@ -1409,7 +1415,7 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
Array.isArray(toOverwrite.trustedSiteDirectives) &&
toOverwrite.trustedSiteDirectives.length !== 0
) {
µBlock.netWhitelistDefault = toOverwrite.trustedSiteDirectives.slice();
µb.netWhitelistDefault = toOverwrite.trustedSiteDirectives.slice();
bin.netWhitelist = toOverwrite.trustedSiteDirectives.slice();
binNotEmpty = true;
} else if ( Array.isArray(data.whitelist) ) {
@ -1457,7 +1463,7 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
// https://github.com/gorhill/uBlock/issues/3210
// Support ability to auto-enable a filter list based on user agent.
µBlock.listMatchesEnvironment = function(details) {
µb.listMatchesEnvironment = function(details) {
// Matches language?
if ( typeof details.lang === 'string' ) {
let re = this.listMatchesEnvironment.reLang;
@ -1480,7 +1486,7 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
/******************************************************************************/
µBlock.scheduleAssetUpdater = (( ) => {
µb.scheduleAssetUpdater = (( ) => {
let timer, next = 0;
return function(updateDelay) {
@ -1502,7 +1508,7 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
timer = vAPI.setTimeout(( ) => {
timer = undefined;
next = 0;
this.assets.updateStart({
io.updateStart({
delay: this.hiddenSettings.autoUpdateAssetFetchPeriod * 1000 ||
120000,
auto: true,
@ -1513,7 +1519,7 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
/******************************************************************************/
µBlock.assetObserver = function(topic, details) {
µb.assetObserver = function(topic, details) {
// Do not update filter list if not in use.
// Also, ignore really bad lists, i.e. those which should not even be
// fetched from a remote server.
@ -1544,7 +1550,7 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
details.content
);
if ( this.badLists.has(details.assetKey) === false ) {
this.assets.put(
io.put(
'compiled/' + details.assetKey,
this.compileFilters(details.content, {
assetKey: details.assetKey
@ -1592,7 +1598,7 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
this.hiddenSettings.userResourcesLocation !== 'unset' ||
vAPI.webextFlavor.soup.has('devbuild')
) {
this.redirectEngine.invalidateResourcesSelfie();
redirectEngine.invalidateResourcesSelfie();
}
this.loadFilterLists();
}

View File

@ -23,6 +23,16 @@
/******************************************************************************/
import contextMenu from './contextmenu.js';
import logger from './logger.js';
import scriptletFilteringEngine from './scriptlet-filtering.js';
import staticNetFilteringEngine from './static-net-filtering.js';
import µb from './background.js';
import { PageStore } from './pagestore.js';
import { sessionFirewall } from './dynamic-net-filtering.js';
import { sessionSwitches } from './hnswitches.js';
import { sessionURLFiltering } from './url-net-filtering.js';
import {
domainFromHostname,
hostnameFromURI,
@ -30,8 +40,6 @@ import {
originFromURI,
} from './uri-utils.js';
import µBlock from './background.js';
/******************************************************************************/
/******************************************************************************/
@ -44,7 +52,7 @@ import µBlock from './background.js';
// hostname. This way, for a specific scheme you can create scope with
// rules which will apply only to that scheme.
µBlock.normalizeTabURL = (( ) => {
µb.normalizeTabURL = (( ) => {
const tabURLNormalizer = new URL('about:blank');
return (tabId, tabURL) => {
@ -93,9 +101,7 @@ import µBlock from './background.js';
// c: close opener
// d: close target
µBlock.onPopupUpdated = (( ) => {
const µb = µBlock;
const onPopupUpdated = (( ) => {
// https://github.com/gorhill/uBlock/commit/1d448b85b2931412508aa01bf899e0b6f0033626#commitcomment-14944764
// See if two URLs are different, disregarding scheme -- because the
// scheme can be unilaterally changed by the browser.
@ -162,13 +168,13 @@ import µBlock from './background.js';
// popunders.
if (
popupType === 'popup' &&
µb.sessionSwitches.evaluateZ(
sessionSwitches.evaluateZ(
'no-popups',
fctxt.getTabHostname()
)
) {
fctxt.filter = {
raw: 'no-popups: ' + µb.sessionSwitches.z + ' true',
raw: 'no-popups: ' + sessionSwitches.z + ' true',
result: 1,
source: 'switch'
};
@ -178,16 +184,16 @@ import µBlock from './background.js';
// https://github.com/gorhill/uBlock/issues/581
// Take into account popup-specific rules in dynamic URL
// filtering, OR generic allow rules.
let result = µb.sessionURLFiltering.evaluateZ(
let result = sessionURLFiltering.evaluateZ(
fctxt.getTabHostname(),
targetURL,
popupType
);
if (
result === 1 && µb.sessionURLFiltering.type === popupType ||
result === 1 && sessionURLFiltering.type === popupType ||
result === 2
) {
fctxt.filter = µb.sessionURLFiltering.toLogData();
fctxt.filter = sessionURLFiltering.toLogData();
return result;
}
@ -195,21 +201,21 @@ import µBlock from './background.js';
// Take into account `allow` rules in dynamic filtering: `block`
// rules are ignored, as block rules are not meant to block
// specific types like `popup` (just like with static filters).
result = µb.sessionFirewall.evaluateCellZY(
result = sessionFirewall.evaluateCellZY(
fctxt.getTabHostname(),
fctxt.getHostname(),
popupType
);
if ( result === 2 ) {
fctxt.filter = µb.sessionFirewall.toLogData();
fctxt.filter = sessionFirewall.toLogData();
return 2;
}
}
fctxt.type = popupType;
const result = µb.staticNetFilteringEngine.matchRequest(fctxt, 0b0001);
const result = staticNetFilteringEngine.matchRequest(fctxt, 0b0001);
if ( result !== 0 ) {
fctxt.filter = µb.staticNetFilteringEngine.toLogData();
fctxt.filter = staticNetFilteringEngine.toLogData();
return result;
}
@ -225,11 +231,11 @@ import µBlock from './background.js';
if (
fctxt.filter === undefined ||
fctxt.filter !== 'static' ||
fctxt.filter.token === µb.staticNetFilteringEngine.noTokenHash
fctxt.filter.token === staticNetFilteringEngine.noTokenHash
) {
return 0;
}
if ( fctxt.filter.token === µb.staticNetFilteringEngine.dotTokenHash ) {
if ( fctxt.filter.token === staticNetFilteringEngine.dotTokenHash ) {
return result;
}
const re = new RegExp(fctxt.filter.regex, 'i');
@ -357,7 +363,7 @@ import µBlock from './background.js';
// Log only for when there was a hit against an actual filter (allow or block).
// https://github.com/gorhill/uBlock/issues/2776
if ( µb.logger.enabled ) {
if ( logger.enabled ) {
fctxt.setRealm('network').setType(popupType);
if ( popupType === 'popup' ) {
fctxt.setURL(targetURL)
@ -463,8 +469,7 @@ housekeep itself.
*/
µBlock.tabContextManager = (( ) => {
const µb = µBlock;
µb.tabContextManager = (( ) => {
const tabContexts = new Map();
// https://github.com/chrisaljoudi/uBlock/issues/1001
@ -524,7 +529,7 @@ housekeep itself.
if ( targetTabId === candidate.opener.tabId ) {
candidate.opener.popunder = true;
}
const result = await µb.onPopupUpdated(tabId, candidate.opener);
const result = onPopupUpdated(tabId, candidate.opener);
if ( result === true ) {
candidate.destroy();
} else {
@ -864,20 +869,20 @@ vAPI.Tabs = class extends vAPI.Tabs {
super.onActivated(details);
if ( vAPI.isBehindTheSceneTabId(details.tabId) ) { return; }
// https://github.com/uBlockOrigin/uBlock-issues/issues/680
µBlock.updateToolbarIcon(details.tabId);
µBlock.contextMenu.update(details.tabId);
µb.updateToolbarIcon(details.tabId);
contextMenu.update(details.tabId);
}
onClosed(tabId) {
super.onClosed(tabId);
if ( vAPI.isBehindTheSceneTabId(tabId) ) { return; }
µBlock.unbindTabFromPageStore(tabId);
µBlock.contextMenu.update();
µb.unbindTabFromPageStore(tabId);
contextMenu.update();
}
onCreated(details) {
super.onCreated(details);
µBlock.tabContextManager.onTabCreated(details);
µb.tabContextManager.onTabCreated(details);
}
// When the DOM content of root frame is loaded, this means the tab
@ -895,7 +900,6 @@ vAPI.Tabs = class extends vAPI.Tabs {
// is not available at this point.
onNavigation(details) {
super.onNavigation(details);
const µb = µBlock;
const { frameId, tabId, url } = details;
if ( frameId === 0 ) {
µb.tabContextManager.commit(tabId, url);
@ -912,7 +916,7 @@ vAPI.Tabs = class extends vAPI.Tabs {
isNetworkURI(url) &&
pageStore.getNetFilteringSwitch()
) {
µb.scriptletFilteringEngine.injectNow(details);
scriptletFilteringEngine.injectNow(details);
}
}
@ -924,8 +928,8 @@ vAPI.Tabs = class extends vAPI.Tabs {
super.onUpdated(tabId, changeInfo, tab);
if ( !tab.url || tab.url === '' ) { return; }
if ( !changeInfo.url ) { return; }
µBlock.tabContextManager.commit(tabId, changeInfo.url);
µBlock.bindTabToPageStore(tabId, 'tabUpdated', tab);
µb.tabContextManager.commit(tabId, changeInfo.url);
µb.bindTabToPageStore(tabId, 'tabUpdated', tab);
}
};
@ -936,7 +940,7 @@ vAPI.tabs = new vAPI.Tabs();
// Create an entry for the tab if it doesn't exist.
µBlock.bindTabToPageStore = function(tabId, context, details = undefined) {
µb.bindTabToPageStore = function(tabId, context, details = undefined) {
this.updateToolbarIcon(tabId, 0b111);
// Do not create a page store for URLs which are of no interests
@ -950,7 +954,7 @@ vAPI.tabs = new vAPI.Tabs();
// Tab is not bound
if ( pageStore === undefined ) {
pageStore = this.PageStore.factory(tabId, details);
pageStore = PageStore.factory(tabId, details);
this.pageStores.set(tabId, pageStore);
this.pageStoresToken = Date.now();
return pageStore;
@ -981,7 +985,7 @@ vAPI.tabs = new vAPI.Tabs();
/******************************************************************************/
µBlock.unbindTabFromPageStore = function(tabId) {
µb.unbindTabFromPageStore = function(tabId) {
const pageStore = this.pageStores.get(tabId);
if ( pageStore === undefined ) { return; }
pageStore.dispose();
@ -991,11 +995,11 @@ vAPI.tabs = new vAPI.Tabs();
/******************************************************************************/
µBlock.pageStoreFromTabId = function(tabId) {
µb.pageStoreFromTabId = function(tabId) {
return this.pageStores.get(tabId) || null;
};
µBlock.mustPageStoreFromTabId = function(tabId) {
µb.mustPageStoreFromTabId = function(tabId) {
return this.pageStores.get(tabId) || this.pageStores.get(vAPI.noTabId);
};
@ -1008,19 +1012,19 @@ vAPI.tabs = new vAPI.Tabs();
// the document context (if present) of the network request.
{
const NoPageStore = class extends µBlock.PageStore {
const NoPageStore = class extends PageStore {
getNetFilteringSwitch(fctxt) {
if ( fctxt ) {
const docOrigin = fctxt.getDocOrigin();
if ( docOrigin ) {
return µBlock.getNetFilteringSwitch(docOrigin);
return µb.getNetFilteringSwitch(docOrigin);
}
}
return super.getNetFilteringSwitch();
}
};
const pageStore = new NoPageStore(vAPI.noTabId);
µBlock.pageStores.set(pageStore.tabId, pageStore);
µb.pageStores.set(pageStore.tabId, pageStore);
pageStore.title = vAPI.i18n('logBehindTheScene');
}
@ -1028,8 +1032,7 @@ vAPI.tabs = new vAPI.Tabs();
// Update visual of extension icon.
µBlock.updateToolbarIcon = (( ) => {
const µb = µBlock;
µb.updateToolbarIcon = (( ) => {
const tabIdToDetails = new Map();
const computeBadgeColor = (bits) => {
@ -1118,11 +1121,11 @@ vAPI.tabs = new vAPI.Tabs();
const checkTab = async tabId => {
const tab = await vAPI.tabs.get(tabId);
if ( tab instanceof Object && tab.discarded !== true ) { return; }
µBlock.unbindTabFromPageStore(tabId);
µb.unbindTabFromPageStore(tabId);
};
const pageStoreJanitor = function() {
const tabIds = Array.from(µBlock.pageStores.keys()).sort();
const tabIds = Array.from(µb.pageStores.keys()).sort();
if ( pageStoreJanitorSampleAt >= tabIds.length ) {
pageStoreJanitorSampleAt = 0;
}

View File

@ -23,17 +23,17 @@
/******************************************************************************/
import µBlock from './background.js';
import µb from './background.js';
/******************************************************************************/
µBlock.textEncode = (function() {
const textEncode = (( ) => {
if ( µBlock.canFilterResponseData !== true ) { return; }
if ( µb.canFilterResponseData !== true ) { return; }
// charset aliases extracted from:
// https://github.com/inexorabletash/text-encoding/blob/b4e5bc26e26e51f56e3daa9f13138c79f49d3c34/lib/encoding.js#L342
var normalizedCharset = new Map([
const normalizedCharset = new Map([
[ 'utf8', 'utf-8' ],
[ 'unicode-1-1-utf-8', 'utf-8' ],
[ 'utf-8', 'utf-8' ],
@ -66,7 +66,7 @@ import µBlock from './background.js';
]);
// http://www.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1250.TXT
var cp1250_range0 = new Uint8Array([
const cp1250_range0 = new Uint8Array([
/* 0x0100 */ 0x00, 0x00, 0xC3, 0xE3, 0xA5, 0xB9, 0xC6, 0xE6,
/* 0x0108 */ 0x00, 0x00, 0x00, 0x00, 0xC8, 0xE8, 0xCF, 0xEF,
/* 0x0110 */ 0xD0, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@ -86,7 +86,7 @@ import µBlock from './background.js';
]);
// http://www.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1251.TXT
var cp1251_range0 = new Uint8Array([
const cp1251_range0 = new Uint8Array([
/* 0x0400 */ 0x00, 0xA8, 0x80, 0x81, 0xAA, 0xBD, 0xB2, 0xAF,
/* 0x0408 */ 0xA3, 0x8A, 0x8C, 0x8E, 0x8D, 0x00, 0xA1, 0x8F,
/* 0x0410 */ 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
@ -109,7 +109,7 @@ import µBlock from './background.js';
]);
// https://www.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1252.TXT
var cp1252_range0 = new Uint8Array([
const cp1252_range0 = new Uint8Array([
/* 0x0150 */ 0x00, 0x00, 0x8C, 0x9C, 0x00, 0x00, 0x00, 0x00,
/* 0x0158 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 0x0160 */ 0x8A, 0x9A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@ -118,7 +118,7 @@ import µBlock from './background.js';
/* 0x0178 */ 0x9F, 0x00, 0x00, 0x00, 0x00, 0x8E, 0x9E, 0x00
]);
var cp125x_range0 = new Uint8Array([
const cp125x_range0 = new Uint8Array([
/* 0x2010 */ 0x00, 0x00, 0x00, 0x96, 0x97, 0x00, 0x00, 0x00,
/* 0x2018 */ 0x91, 0x92, 0x82, 0x00, 0x93, 0x94, 0x84, 0x00,
/* 0x2020 */ 0x86, 0x87, 0x95, 0x00, 0x00, 0x00, 0x85, 0x00,
@ -127,9 +127,9 @@ import µBlock from './background.js';
/* 0x2038 */ 0x00, 0x8B, 0x9B, 0x00, 0x00, 0x00, 0x00, 0x00
]);
var encoders = {
const encoders = {
'windows-1250': function(buf) {
var i = 0, n = buf.byteLength, o = 0, c;
let i = 0, n = buf.byteLength, o = 0, c;
while ( i < n ) {
c = buf[i++];
if ( c < 0x80 ) {
@ -174,7 +174,7 @@ import µBlock from './background.js';
return buf.slice(0, o);
},
'windows-1251': function(buf) {
var i = 0, n = buf.byteLength, o = 0, c;
let i = 0, n = buf.byteLength, o = 0, c;
while ( i < n ) {
c = buf[i++];
if ( c < 0x80 ) {
@ -211,7 +211,7 @@ import µBlock from './background.js';
return buf.slice(0, o);
},
'windows-1252': function(buf) {
var i = 0, n = buf.byteLength, o = 0, c;
let i = 0, n = buf.byteLength, o = 0, c;
while ( i < n ) {
c = buf[i++];
if ( c < 0x80 ) {
@ -267,3 +267,9 @@ import µBlock from './background.js';
}
};
})();
/******************************************************************************/
export default textEncode;
/******************************************************************************/

View File

@ -23,13 +23,22 @@
/******************************************************************************/
import htmlFilteringEngine from './html-filtering.js';
import httpheaderFilteringEngine from './httpheader-filtering.js';
import logger from './logger.js';
import scriptletFilteringEngine from './scriptlet-filtering.js';
import staticNetFilteringEngine from './static-net-filtering.js';
import textEncode from './text-encode.js';
import µb from './background.js';
import { sessionFirewall } from './dynamic-net-filtering.js';
import { sessionSwitches } from './hnswitches.js';
import { sessionURLFiltering } from './url-net-filtering.js';
import {
entityFromDomain,
isNetworkURI,
} from './uri-utils.js';
import µBlock from './background.js';
/******************************************************************************/
// Platform-specific behavior.
@ -55,7 +64,7 @@ const supportsFloc = document.interestCohort instanceof Function;
// Intercept and filter web requests.
const onBeforeRequest = function(details) {
const fctxt = µBlock.filteringContext.fromWebrequestDetails(details);
const fctxt = µb.filteringContext.fromWebrequestDetails(details);
// Special handling for root document.
// https://github.com/chrisaljoudi/uBlock/issues/1001
@ -72,7 +81,6 @@ const onBeforeRequest = function(details) {
}
// Lookup the page store associated with this tab id.
const µb = µBlock;
let pageStore = µb.pageStoreFromTabId(tabId);
if ( pageStore === null ) {
const tabContext = µb.tabContextManager.mustLookup(tabId);
@ -87,7 +95,7 @@ const onBeforeRequest = function(details) {
pageStore.journalAddRequest(fctxt, result);
if ( µb.logger.enabled ) {
if ( logger.enabled ) {
fctxt.setRealm('network').toLogger();
}
@ -121,7 +129,6 @@ const onBeforeRequest = function(details) {
/******************************************************************************/
const onBeforeRootFrameRequest = function(fctxt) {
const µb = µBlock;
const requestURL = fctxt.url;
// Special handling for root document.
@ -129,7 +136,6 @@ const onBeforeRootFrameRequest = function(fctxt) {
// This must be executed regardless of whether the request is
// behind-the-scene
const requestHostname = fctxt.getHostname();
const loggerEnabled = µb.logger.enabled;
let result = 0;
let logData;
@ -137,7 +143,7 @@ const onBeforeRootFrameRequest = function(fctxt) {
const trusted = µb.getNetFilteringSwitch(requestURL) === false;
if ( trusted ) {
result = 2;
if ( loggerEnabled ) {
if ( logger.enabled ) {
logData = { engine: 'u', result: 2, raw: 'whitelisted' };
}
}
@ -145,14 +151,14 @@ const onBeforeRootFrameRequest = function(fctxt) {
// Permanently unrestricted?
if (
result === 0 &&
µb.sessionSwitches.evaluateZ('no-strict-blocking', requestHostname)
sessionSwitches.evaluateZ('no-strict-blocking', requestHostname)
) {
result = 2;
if ( loggerEnabled ) {
if ( logger.enabled ) {
logData = {
engine: 'u',
result: 2,
raw: `no-strict-blocking: ${µb.sessionSwitches.z} true`
raw: `no-strict-blocking: ${sessionSwitches.z} true`
};
}
}
@ -160,7 +166,7 @@ const onBeforeRootFrameRequest = function(fctxt) {
// Temporarily whitelisted?
if ( result === 0 && strictBlockBypasser.isBypassed(requestHostname) ) {
result = 2;
if ( loggerEnabled ) {
if ( logger.enabled ) {
logData = {
engine: 'u',
result: 2,
@ -171,7 +177,7 @@ const onBeforeRootFrameRequest = function(fctxt) {
// Static filtering
if ( result === 0 ) {
({ result, logData } = shouldStrictBlock(fctxt, loggerEnabled));
({ result, logData } = shouldStrictBlock(fctxt, logger.enabled));
}
const pageStore = µb.bindTabToPageStore(fctxt.tabId, 'beforeRequest');
@ -180,7 +186,7 @@ const onBeforeRootFrameRequest = function(fctxt) {
pageStore.journalAddRequest(fctxt, result);
}
if ( loggerEnabled ) {
if ( logger.enabled ) {
fctxt.setFilter(logData);
}
@ -190,12 +196,12 @@ const onBeforeRootFrameRequest = function(fctxt) {
result !== 1 &&
trusted === false &&
pageStore !== null &&
µb.staticNetFilteringEngine.hasQuery(fctxt)
staticNetFilteringEngine.hasQuery(fctxt)
) {
pageStore.redirectNonBlockedRequest(fctxt);
}
if ( loggerEnabled ) {
if ( logger.enabled ) {
fctxt.setRealm('network').toLogger();
}
@ -259,8 +265,7 @@ const onBeforeRootFrameRequest = function(fctxt) {
// --------+--------+--------+--------+--------+--------+
const shouldStrictBlock = function(fctxt, loggerEnabled) {
const µb = µBlock;
const snfe = µb.staticNetFilteringEngine;
const snfe = staticNetFilteringEngine;
// Explicit filtering: `document` option
const rs = snfe.matchRequest(fctxt, 0b0011);
@ -358,7 +363,6 @@ const validateStrictBlock = function(fctxt, logData) {
// Intercept and filter behind-the-scene requests.
const onBeforeBehindTheSceneRequest = function(fctxt) {
const µb = µBlock;
const pageStore = µb.pageStoreFromTabId(fctxt.tabId);
if ( pageStore === null ) { return; }
@ -403,7 +407,7 @@ const onBeforeBehindTheSceneRequest = function(fctxt) {
// https://github.com/uBlockOrigin/uBlock-issues/issues/1204
onBeforeBehindTheSceneRequest.journalAddRequest(fctxt, result);
if ( µb.logger.enabled ) {
if ( logger.enabled ) {
fctxt.setRealm('network').toLogger();
}
@ -440,7 +444,7 @@ const onBeforeBehindTheSceneRequest = function(fctxt) {
const gc = ( ) => {
gcTimer = undefined;
if ( pageStoresToken !== µBlock.pageStoresToken ) { return reset(); }
if ( pageStoresToken !== µb.pageStoresToken ) { return reset(); }
gcTimer = vAPI.setTimeout(gc, 30011);
};
@ -448,15 +452,15 @@ const onBeforeBehindTheSceneRequest = function(fctxt) {
const docHostname = fctxt.getDocHostname();
if (
docHostname !== hostname ||
pageStoresToken !== µBlock.pageStoresToken
pageStoresToken !== µb.pageStoresToken
) {
hostname = docHostname;
pageStores = new Set();
for ( const pageStore of µBlock.pageStores.values() ) {
for ( const pageStore of µb.pageStores.values() ) {
if ( pageStore.tabHostname !== docHostname ) { continue; }
pageStores.add(pageStore);
}
pageStoresToken = µBlock.pageStoresToken;
pageStoresToken = µb.pageStoresToken;
if ( gcTimer !== undefined ) {
clearTimeout(gcTimer);
}
@ -486,7 +490,6 @@ const onHeadersReceived = function(details) {
return;
}
const µb = µBlock;
const fctxt = µb.filteringContext.fromWebrequestDetails(details);
const isRootDoc = fctxt.itype === fctxt.MAIN_FRAME;
@ -511,7 +514,7 @@ const onHeadersReceived = function(details) {
if ( isRootDoc === false && µb.hiddenSettings.filterOnHeaders === true ) {
const result = pageStore.filterOnHeaders(fctxt, responseHeaders);
if ( result !== 0 ) {
if ( µb.logger.enabled ) {
if ( logger.enabled ) {
fctxt.setRealm('network').toLogger();
}
if ( result === 1 ) {
@ -542,7 +545,7 @@ const onHeadersReceived = function(details) {
µb.canFilterResponseData && filterDocument(fctxt, details) === true;
let modifiedHeaders = false;
if ( µb.httpheaderFilteringEngine.apply(fctxt, responseHeaders) === true ) {
if ( httpheaderFilteringEngine.apply(fctxt, responseHeaders) === true ) {
modifiedHeaders = true;
}
if ( injectCSP(fctxt, pageStore, responseHeaders) === true ) {
@ -622,7 +625,6 @@ const normalizeBehindTheSceneResponseHeaders = function(details) {
**/
const filterDocument = (( ) => {
const µb = µBlock;
const filterers = new Map();
let domParser, xmlSerializer,
utf8TextDecoder, textDecoder, textEncoder;
@ -749,7 +751,7 @@ const filterDocument = (( ) => {
filterer.mime
);
charsetFound = charsetFromDoc(doc);
charsetUsed = µb.textEncode.normalizeCharset(charsetFound);
charsetUsed = textEncode.normalizeCharset(charsetFound);
if ( charsetUsed === undefined ) {
return streamClose(filterer);
}
@ -764,7 +766,7 @@ const filterDocument = (( ) => {
// In case of no explicit charset found, try to find one again, but
// this time with the whole document parsed.
if ( charsetFound === undefined ) {
charsetFound = µb.textEncode.normalizeCharset(charsetFromDoc(doc));
charsetFound = textEncode.normalizeCharset(charsetFromDoc(doc));
if ( charsetFound !== charsetUsed ) {
if ( charsetFound === undefined ) {
return streamClose(filterer);
@ -779,7 +781,7 @@ const filterDocument = (( ) => {
let modified = false;
if ( filterer.selectors !== undefined ) {
if ( µb.htmlFilteringEngine.apply(doc, filterer) ) {
if ( htmlFilteringEngine.apply(doc, filterer) ) {
modified = true;
}
}
@ -799,7 +801,7 @@ const filterDocument = (( ) => {
doc.documentElement.outerHTML
);
if ( charsetUsed !== 'utf-8' ) {
encodedStream = µb.textEncode.encode(
encodedStream = textEncode.encode(
charsetUsed,
encodedStream
);
@ -837,7 +839,7 @@ const filterDocument = (( ) => {
charset: undefined
};
request.selectors = µb.htmlFilteringEngine.retrieve(request);
request.selectors = htmlFilteringEngine.retrieve(request);
if ( request.selectors === undefined ) { return; }
const headers = extras.responseHeaders;
@ -847,7 +849,7 @@ const filterDocument = (( ) => {
if ( request.mime === undefined ) { return; }
let charset = charsetFromContentType(contentType);
if ( charset !== undefined ) {
charset = µb.textEncode.normalizeCharset(charset);
charset = textEncode.normalizeCharset(charset);
if ( charset === undefined ) { return; }
request.charset = charset;
}
@ -869,8 +871,6 @@ const filterDocument = (( ) => {
/******************************************************************************/
const injectCSP = function(fctxt, pageStore, responseHeaders) {
const µb = µBlock;
const loggerEnabled = µb.logger.enabled;
const cspSubsets = [];
const requestType = fctxt.type;
@ -881,8 +881,8 @@ const injectCSP = function(fctxt, pageStore, responseHeaders) {
const builtinDirectives = [];
if ( pageStore.filterScripting(fctxt, true) === 1 ) {
builtinDirectives.push(µBlock.cspNoScripting);
if ( loggerEnabled ) {
builtinDirectives.push(µb.cspNoScripting);
if ( logger.enabled ) {
fctxt.setRealm('network').setType('scripting').toLogger();
}
}
@ -896,9 +896,9 @@ const injectCSP = function(fctxt, pageStore, responseHeaders) {
fctxt2.setDocOriginFromURL(fctxt.url);
const result = pageStore.filterRequest(fctxt2);
if ( result === 1 ) {
builtinDirectives.push(µBlock.cspNoInlineScript);
builtinDirectives.push(µb.cspNoInlineScript);
}
if ( result === 2 && loggerEnabled ) {
if ( result === 2 && logger.enabled ) {
fctxt2.setRealm('network').toLogger();
}
}
@ -907,8 +907,8 @@ const injectCSP = function(fctxt, pageStore, responseHeaders) {
// - Use a CSP to also forbid inline fonts if remote fonts are blocked.
fctxt.type = 'inline-font';
if ( pageStore.filterRequest(fctxt) === 1 ) {
builtinDirectives.push(µBlock.cspNoInlineFont);
if ( loggerEnabled ) {
builtinDirectives.push(µb.cspNoInlineFont);
if ( logger.enabled ) {
fctxt.setRealm('network').toLogger();
}
}
@ -923,7 +923,7 @@ const injectCSP = function(fctxt, pageStore, responseHeaders) {
fctxt.type = requestType;
const staticDirectives =
µb.staticNetFilteringEngine.matchAndFetchModifiers(fctxt, 'csp');
staticNetFilteringEngine.matchAndFetchModifiers(fctxt, 'csp');
if ( staticDirectives !== undefined ) {
for ( const directive of staticDirectives ) {
if ( directive.result !== 1 ) { continue; }
@ -934,16 +934,16 @@ const injectCSP = function(fctxt, pageStore, responseHeaders) {
// URL filtering `allow` rules override static filtering.
if (
cspSubsets.length !== 0 &&
µb.sessionURLFiltering.evaluateZ(
sessionURLFiltering.evaluateZ(
fctxt.getTabHostname(),
fctxt.url,
'csp'
) === 2
) {
if ( loggerEnabled ) {
if ( logger.enabled ) {
fctxt.setRealm('network')
.setType('csp')
.setFilter(µb.sessionURLFiltering.toLogData())
.setFilter(sessionURLFiltering.toLogData())
.toLogger();
}
return;
@ -953,16 +953,16 @@ const injectCSP = function(fctxt, pageStore, responseHeaders) {
if (
cspSubsets.length !== 0 &&
µb.userSettings.advancedUserEnabled &&
µb.sessionFirewall.evaluateCellZY(
sessionFirewall.evaluateCellZY(
fctxt.getTabHostname(),
fctxt.getTabHostname(),
'*'
) === 2
) {
if ( loggerEnabled ) {
if ( logger.enabled ) {
fctxt.setRealm('network')
.setType('csp')
.setFilter(µb.sessionFirewall.toLogData())
.setFilter(sessionFirewall.toLogData())
.toLogger();
}
return;
@ -972,7 +972,7 @@ const injectCSP = function(fctxt, pageStore, responseHeaders) {
// Static CSP policies will be applied.
if ( loggerEnabled && staticDirectives !== undefined ) {
if ( logger.enabled && staticDirectives !== undefined ) {
fctxt.setRealm('network')
.pushFilters(staticDirectives.map(a => a.logData()))
.toLogger();
@ -1006,7 +1006,7 @@ const injectCSP = function(fctxt, pageStore, responseHeaders) {
const foilFloc = function(fctxt, responseHeaders) {
const hn = fctxt.getHostname();
if ( µBlock.scriptletFilteringEngine.hasScriptlet(hn, 1, 'no-floc') === false ) {
if ( scriptletFilteringEngine.hasScriptlet(hn, 1, 'no-floc') === false ) {
return false;
}
responseHeaders.push({
@ -1029,7 +1029,7 @@ const foilLargeMediaElement = function(details, fctxt, pageStore) {
if ( details.fromCache === true ) { return; }
let size = 0;
if ( µBlock.userSettings.largeMediaSize !== 0 ) {
if ( µb.userSettings.largeMediaSize !== 0 ) {
const headers = details.responseHeaders;
const i = headerIndexFromName('content-length', headers);
if ( i === -1 ) { return; }
@ -1039,7 +1039,7 @@ const foilLargeMediaElement = function(details, fctxt, pageStore) {
const result = pageStore.filterLargeMediaElement(fctxt, size);
if ( result === 0 ) { return; }
if ( µBlock.logger.enabled ) {
if ( logger.enabled ) {
fctxt.setRealm('network').toLogger();
}
@ -1083,14 +1083,14 @@ const strictBlockBypasser = {
if ( typeof hostname !== 'string' || hostname === '' ) { return; }
this.hostnameToDeadlineMap.set(
hostname,
Date.now() + µBlock.hiddenSettings.strictBlockingBypassDuration * 1000
Date.now() + µb.hiddenSettings.strictBlockingBypassDuration * 1000
);
},
isBypassed: function(hostname) {
if ( this.hostnameToDeadlineMap.size === 0 ) { return false; }
let bypassDuration =
µBlock.hiddenSettings.strictBlockingBypassDuration * 1000;
µb.hiddenSettings.strictBlockingBypassDuration * 1000;
if ( this.cleanupTimer === undefined ) {
this.cleanupTimer = vAPI.setTimeout(
( ) => {
@ -1122,9 +1122,7 @@ const strictBlockBypasser = {
/******************************************************************************/
// Export
µBlock.webRequest = {
const webRequest = {
start: (( ) => {
vAPI.net = new vAPI.Net();
vAPI.net.suspend();
@ -1147,3 +1145,7 @@ const strictBlockBypasser = {
};
/******************************************************************************/
export { webRequest };
/******************************************************************************/

View File

@ -23,8 +23,26 @@
/******************************************************************************/
import contextMenu from './contextmenu.js';
import cosmeticFilteringEngine from './cosmetic-filtering.js';
import µb from './background.js';
import { hostnameFromURI } from './uri-utils.js';
import µBlock from './background.js';
import { redirectEngine } from './redirect-engine.js';
import {
permanentFirewall,
sessionFirewall,
} from './dynamic-net-filtering.js';
import {
permanentSwitches,
sessionSwitches,
} from './hnswitches.js';
import {
permanentURLFiltering,
sessionURLFiltering,
} from './url-net-filtering.js';
/******************************************************************************/
/******************************************************************************/
@ -90,7 +108,7 @@ const matchBucket = function(url, hostname, bucket, start) {
/******************************************************************************/
µBlock.getNetFilteringSwitch = function(url) {
µb.getNetFilteringSwitch = function(url) {
const hostname = hostnameFromURI(url);
let key = hostname;
for (;;) {
@ -109,7 +127,7 @@ const matchBucket = function(url, hostname, bucket, start) {
/******************************************************************************/
µBlock.toggleNetFilteringSwitch = function(url, scope, newState) {
µb.toggleNetFilteringSwitch = function(url, scope, newState) {
const currentState = this.getNetFilteringSwitch(url);
if ( newState === undefined ) {
newState = !currentState;
@ -179,7 +197,7 @@ const matchBucket = function(url, hostname, bucket, start) {
/******************************************************************************/
µBlock.arrayFromWhitelist = function(whitelist) {
µb.arrayFromWhitelist = function(whitelist) {
const out = new Set();
for ( const bucket of whitelist.values() ) {
for ( const directive of bucket ) {
@ -189,13 +207,13 @@ const matchBucket = function(url, hostname, bucket, start) {
return Array.from(out).sort((a, b) => a.localeCompare(b));
};
µBlock.stringFromWhitelist = function(whitelist) {
µb.stringFromWhitelist = function(whitelist) {
return this.arrayFromWhitelist(whitelist).join('\n');
};
/******************************************************************************/
µBlock.whitelistFromArray = function(lines) {
µb.whitelistFromArray = function(lines) {
const whitelist = new Map();
// Comment bucket must always be ready to be used.
@ -273,27 +291,27 @@ const matchBucket = function(url, hostname, bucket, start) {
return whitelist;
};
µBlock.whitelistFromString = function(s) {
µb.whitelistFromString = function(s) {
return this.whitelistFromArray(s.split('\n'));
};
// https://github.com/gorhill/uBlock/issues/3717
µBlock.reWhitelistBadHostname = /[^a-z0-9.\-_\[\]:]/;
µBlock.reWhitelistHostnameExtractor = /([a-z0-9.\-_\[\]]+)(?::[\d*]+)?\/(?:[^\x00-\x20\/]|$)[^\x00-\x20]*$/;
µb.reWhitelistBadHostname = /[^a-z0-9.\-_\[\]:]/;
µb.reWhitelistHostnameExtractor = /([a-z0-9.\-_\[\]]+)(?::[\d*]+)?\/(?:[^\x00-\x20\/]|$)[^\x00-\x20]*$/;
/******************************************************************************/
µBlock.changeUserSettings = function(name, value) {
µb.changeUserSettings = function(name, value) {
let us = this.userSettings;
// Return all settings if none specified.
if ( name === undefined ) {
us = JSON.parse(JSON.stringify(us));
us.noCosmeticFiltering = this.sessionSwitches.evaluate('no-cosmetic-filtering', '*') === 1;
us.noLargeMedia = this.sessionSwitches.evaluate('no-large-media', '*') === 1;
us.noRemoteFonts = this.sessionSwitches.evaluate('no-remote-fonts', '*') === 1;
us.noScripting = this.sessionSwitches.evaluate('no-scripting', '*') === 1;
us.noCSPReports = this.sessionSwitches.evaluate('no-csp-reports', '*') === 1;
us.noCosmeticFiltering = sessionSwitches.evaluate('no-cosmetic-filtering', '*') === 1;
us.noLargeMedia = sessionSwitches.evaluate('no-large-media', '*') === 1;
us.noRemoteFonts = sessionSwitches.evaluate('no-remote-fonts', '*') === 1;
us.noScripting = sessionSwitches.evaluate('no-scripting', '*') === 1;
us.noCSPReports = sessionSwitches.evaluate('no-csp-reports', '*') === 1;
return us;
}
@ -338,11 +356,11 @@ const matchBucket = function(url, hostname, bucket, start) {
break;
case 'collapseBlocked':
if ( value === false ) {
this.cosmeticFilteringEngine.removeFromSelectorCache('*', 'net');
cosmeticFilteringEngine.removeFromSelectorCache('*', 'net');
}
break;
case 'contextMenuEnabled':
this.contextMenu.update(null);
contextMenu.update(null);
break;
case 'hyperlinkAuditingDisabled':
if ( this.privacySettingsSupported ) {
@ -371,8 +389,8 @@ const matchBucket = function(url, hostname, bucket, start) {
}
if ( switchName === undefined ) { break; }
let switchState = value ? 1 : 0;
this.sessionSwitches.toggle(switchName, '*', switchState);
if ( this.permanentSwitches.toggle(switchName, '*', switchState) ) {
sessionSwitches.toggle(switchName, '*', switchState);
if ( permanentSwitches.toggle(switchName, '*', switchState) ) {
this.saveHostnameSwitches();
}
break;
@ -399,13 +417,13 @@ const matchBucket = function(url, hostname, bucket, start) {
// https://www.reddit.com/r/uBlockOrigin/comments/8524cf/my_custom_scriptlets_doesnt_work_what_am_i_doing/
µBlock.changeHiddenSettings = function(hs) {
µb.changeHiddenSettings = function(hs) {
const mustReloadResources =
hs.userResourcesLocation !== this.hiddenSettings.userResourcesLocation;
this.hiddenSettings = hs;
this.saveHiddenSettings();
if ( mustReloadResources ) {
this.redirectEngine.invalidateResourcesSelfie();
redirectEngine.invalidateResourcesSelfie();
this.loadRedirectResources();
}
this.fireDOMEvent('hiddenSettingsChanged');
@ -413,7 +431,7 @@ const matchBucket = function(url, hostname, bucket, start) {
/******************************************************************************/
µBlock.elementPickerExec = async function(
µb.elementPickerExec = async function(
tabId,
frameId,
targetElement,
@ -451,18 +469,18 @@ const matchBucket = function(url, hostname, bucket, start) {
// Always set own rules, trying to be fancy to avoid setting seemingly
// (but not really) redundant rules led to this issue.
µBlock.toggleFirewallRule = function(details) {
µb.toggleFirewallRule = function(details) {
let { srcHostname, desHostname, requestType, action } = details;
if ( action !== 0 ) {
this.sessionFirewall.setCell(
sessionFirewall.setCell(
srcHostname,
desHostname,
requestType,
action
);
} else {
this.sessionFirewall.unsetCell(
sessionFirewall.unsetCell(
srcHostname,
desHostname,
requestType
@ -472,14 +490,14 @@ const matchBucket = function(url, hostname, bucket, start) {
// https://github.com/chrisaljoudi/uBlock/issues/731#issuecomment-73937469
if ( details.persist ) {
if ( action !== 0 ) {
this.permanentFirewall.setCell(
permanentFirewall.setCell(
srcHostname,
desHostname,
requestType,
action
);
} else {
this.permanentFirewall.unsetCell(
permanentFirewall.unsetCell(
srcHostname,
desHostname,
requestType,
@ -506,7 +524,7 @@ const matchBucket = function(url, hostname, bucket, start) {
}
// https://github.com/chrisaljoudi/uBlock/issues/420
this.cosmeticFilteringEngine.removeFromSelectorCache(srcHostname, 'net');
cosmeticFilteringEngine.removeFromSelectorCache(srcHostname, 'net');
if ( details.tabId === undefined ) { return; }
@ -525,8 +543,8 @@ const matchBucket = function(url, hostname, bucket, start) {
/******************************************************************************/
µBlock.toggleURLFilteringRule = function(details) {
let changed = this.sessionURLFiltering.setRule(
µb.toggleURLFilteringRule = function(details) {
let changed = sessionURLFiltering.setRule(
details.context,
details.url,
details.type,
@ -534,11 +552,11 @@ const matchBucket = function(url, hostname, bucket, start) {
);
if ( changed === false ) { return; }
this.cosmeticFilteringEngine.removeFromSelectorCache(details.context, 'net');
cosmeticFilteringEngine.removeFromSelectorCache(details.context, 'net');
if ( details.persist !== true ) { return; }
changed = this.permanentURLFiltering.setRule(
changed = permanentURLFiltering.setRule(
details.context,
details.url,
details.type,
@ -552,8 +570,8 @@ const matchBucket = function(url, hostname, bucket, start) {
/******************************************************************************/
µBlock.toggleHostnameSwitch = function(details) {
let changed = this.sessionSwitches.toggleZ(
µb.toggleHostnameSwitch = function(details) {
let changed = sessionSwitches.toggleZ(
details.name,
details.hostname,
!!details.deep,
@ -582,7 +600,7 @@ const matchBucket = function(url, hostname, bucket, start) {
if ( details.persist !== true ) { return; }
changed = this.permanentSwitches.toggleZ(
changed = permanentSwitches.toggleZ(
details.name,
details.hostname,
!!details.deep,
@ -595,29 +613,28 @@ const matchBucket = function(url, hostname, bucket, start) {
/******************************************************************************/
µBlock.blockingModeFromHostname = function(hn) {
µb.blockingModeFromHostname = function(hn) {
let bits = 0;
if ( this.sessionSwitches.evaluateZ('no-scripting', hn) ) {
if ( sessionSwitches.evaluateZ('no-scripting', hn) ) {
bits |= 0b00000010;
}
if ( this.userSettings.advancedUserEnabled ) {
const fw = this.sessionFirewall;
if ( fw.evaluateCellZY(hn, '*', '3p') === 1 ) {
if ( sessionFirewall.evaluateCellZY(hn, '*', '3p') === 1 ) {
bits |= 0b00000100;
}
if ( fw.evaluateCellZY(hn, '*', '3p-script') === 1 ) {
if ( sessionFirewall.evaluateCellZY(hn, '*', '3p-script') === 1 ) {
bits |= 0b00001000;
}
if ( fw.evaluateCellZY(hn, '*', '3p-frame') === 1 ) {
if ( sessionFirewall.evaluateCellZY(hn, '*', '3p-frame') === 1 ) {
bits |= 0b00010000;
}
}
return bits;
};
µBlock.parseBlockingProfiles = (( ) => {
µb.parseBlockingProfiles = (( ) => {
const parse = function() {
const s = µBlock.hiddenSettings.blockingProfiles;
const s = µb.hiddenSettings.blockingProfiles;
const profiles = [];
s.split(/\s+/).forEach(s => {
let pos = s.indexOf('/');
@ -629,8 +646,8 @@ const matchBucket = function(url, hostname, bucket, start) {
const color = s.slice(pos + 1);
profiles.push({ bits, color: color !== '' ? color : '#666' });
});
µBlock.liveBlockingProfiles = profiles;
µBlock.blockingProfileColorCache.clear();
µb.liveBlockingProfiles = profiles;
µb.blockingProfileColorCache.clear();
};
parse();
@ -642,7 +659,7 @@ const matchBucket = function(url, hostname, bucket, start) {
/******************************************************************************/
µBlock.scriptlets = (function() {
µb.scriptlets = (function() {
const pendingEntries = new Map();
const Entry = class {

View File

@ -23,8 +23,8 @@
/******************************************************************************/
import µb from './background.js';
import { LineIterator } from './text-iterators.js';
import µBlock from './background.js';
/*******************************************************************************
@ -200,7 +200,7 @@ URLNetFiltering.prototype.evaluateZ = function(context, target, type) {
if ( this.rules.size === 0 ) {
return 0;
}
µBlock.decomposeHostname(context, this.decomposedSource);
µb.decomposeHostname(context, this.decomposedSource);
for ( let shn of this.decomposedSource ) {
this.context = shn;
let entries = this.rules.get(shn + ' ' + type);
@ -370,11 +370,9 @@ URLNetFiltering.prototype.removeFromRuleParts = function(parts) {
/******************************************************************************/
// Export
const sessionURLFiltering = new URLNetFiltering();
const permanentURLFiltering = new URLNetFiltering();
µBlock.URLNetFiltering = URLNetFiltering;
µBlock.sessionURLFiltering = new URLNetFiltering();
µBlock.permanentURLFiltering = new URLNetFiltering();
export { permanentURLFiltering, sessionURLFiltering };
/******************************************************************************/

View File

@ -23,12 +23,13 @@
/******************************************************************************/
import io from './assets.js';
import µb from './background.js';
import { LineIterator } from './text-iterators.js';
import µBlock from './background.js';
/******************************************************************************/
µBlock.formatCount = function(count) {
µb.formatCount = function(count) {
if ( typeof count !== 'number' ) {
return '';
}
@ -53,7 +54,7 @@ import µBlock from './background.js';
/******************************************************************************/
µBlock.dateNowToSensibleString = function() {
µb.dateNowToSensibleString = function() {
const now = new Date(Date.now() - (new Date()).getTimezoneOffset() * 60000);
return now.toISOString().replace(/\.\d+Z$/, '')
.replace(/:/g, '.')
@ -62,7 +63,7 @@ import µBlock from './background.js';
/******************************************************************************/
µBlock.openNewTab = function(details) {
µb.openNewTab = function(details) {
if ( details.url.startsWith('logger-ui.html') ) {
if ( details.shiftKey ) {
this.changeUserSettings(
@ -92,7 +93,7 @@ import µBlock from './background.js';
/******************************************************************************/
µBlock.MRUCache = class {
µb.MRUCache = class {
constructor(size) {
this.size = size;
this.array = [];
@ -136,13 +137,13 @@ import µBlock from './background.js';
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
µBlock.escapeRegex = function(s) {
µb.escapeRegex = function(s) {
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
};
/******************************************************************************/
µBlock.decomposeHostname = (( ) => {
µb.decomposeHostname = (( ) => {
// For performance purpose, as simple tests as possible
const reHostnameVeryCoarse = /[g-z_-]/;
const reIPv4VeryCoarse = /\.\d+$/;
@ -196,7 +197,7 @@ import µBlock from './background.js';
// TODO: evaluate using TextEncoder/TextDecoder
µBlock.orphanizeString = function(s) {
µb.orphanizeString = function(s) {
return JSON.parse(JSON.stringify(s));
};
@ -217,10 +218,6 @@ import µBlock from './background.js';
// From uBO's dev console, launch the benchmark:
// µBlock.staticNetFilteringEngine.benchmark();
//
// The advanced setting `consoleLogLevel` must be set to `info` to see the
// results in uBO's dev console, see:
// https://github.com/gorhill/uBlock/wiki/Advanced-settings#consoleloglevel
//
// The usual browser dev tools can be used to obtain useful profiling
// data, i.e. start the profiler, call the benchmark method from the
// console, then stop the profiler when it completes.
@ -232,7 +229,7 @@ import µBlock from './background.js';
// Rename ./tmp/requests.json.gz to something else if you no longer want
// ./assets/requests.json in the build.
µBlock.loadBenchmarkDataset = (( ) => {
µb.loadBenchmarkDataset = (( ) => {
let datasetPromise;
let ttlTimer;
@ -251,13 +248,13 @@ import µBlock from './background.js';
return datasetPromise;
}
const datasetURL = µBlock.hiddenSettings.benchmarkDatasetURL;
const datasetURL = µb.hiddenSettings.benchmarkDatasetURL;
if ( datasetURL === 'unset' ) {
console.info(`No benchmark dataset available.`);
return Promise.resolve();
}
console.info(`Loading benchmark dataset...`);
datasetPromise = µBlock.assets.fetchText(datasetURL).then(details => {
datasetPromise = io.fetchText(datasetURL).then(details => {
console.info(`Parsing benchmark dataset...`);
const requests = [];
const lineIter = new LineIterator(details.content);
@ -288,7 +285,7 @@ import µBlock from './background.js';
/******************************************************************************/
µBlock.fireDOMEvent = function(name) {
µb.fireDOMEvent = function(name) {
if (
window instanceof Object &&
window.dispatchEvent instanceof Function &&
@ -302,7 +299,7 @@ import µBlock from './background.js';
// TODO: properly compare arrays
µBlock.getModifiedSettings = function(edit, orig = {}) {
µb.getModifiedSettings = function(edit, orig = {}) {
const out = {};
for ( const prop in edit ) {
if ( orig.hasOwnProperty(prop) && edit[prop] !== orig[prop] ) {
@ -312,7 +309,7 @@ import µBlock from './background.js';
return out;
};
µBlock.settingValueFromString = function(orig, name, s) {
µb.settingValueFromString = function(orig, name, s) {
if ( typeof name !== 'string' || typeof s !== 'string' ) { return; }
if ( orig.hasOwnProperty(name) === false ) { return; }
let r;