mirror of
https://github.com/gorhill/uBlock.git
synced 2024-11-10 01:02:08 +01:00
Work toward modernizing code base: promisification
Swathes of code have been converted to use Promises/async/await. More left to do. In the process, a regression affecting the fix to <https://github.com/uBlockOrigin/uBlock-issues/issues/682> has been fixed.
This commit is contained in:
parent
6c7d3a40d6
commit
e27328f931
7 changed files with 1032 additions and 1207 deletions
|
@ -43,6 +43,15 @@ vAPI.lastError = function() {
|
|||
return chrome.runtime.lastError;
|
||||
};
|
||||
|
||||
vAPI.apiIsPromisified = (( ) => {
|
||||
try {
|
||||
return browser.storage.local.get('_') instanceof Promise;
|
||||
}
|
||||
catch(ex) {
|
||||
}
|
||||
return false;
|
||||
})();
|
||||
|
||||
// https://github.com/gorhill/uBlock/issues/875
|
||||
// https://code.google.com/p/chromium/issues/detail?id=410868#c8
|
||||
// Must not leave `lastError` unchecked.
|
||||
|
@ -107,9 +116,83 @@ vAPI.app = {
|
|||
/******************************************************************************/
|
||||
/******************************************************************************/
|
||||
|
||||
// chrome.storage.local.get(null, function(bin){ console.debug('%o', bin); });
|
||||
|
||||
vAPI.storage = browser.storage.local;
|
||||
vAPI.storage = (( ) => {
|
||||
if ( vAPI.apiIsPromisified ) {
|
||||
return browser.storage.local;
|
||||
}
|
||||
return {
|
||||
clear: function(callback) {
|
||||
if ( callback !== undefined ) {
|
||||
return browser.storage.local.clear(...arguments);
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
browser.storage.local.clear(( ) => {
|
||||
const lastError = browser.runtime.lastError;
|
||||
if ( lastError instanceof Object ) {
|
||||
return reject(lastError);
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
},
|
||||
get: function(keys, callback) {
|
||||
if ( callback !== undefined ) {
|
||||
return browser.storage.local.get(...arguments);
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
browser.storage.local.get(keys, result => {
|
||||
const lastError = browser.runtime.lastError;
|
||||
if ( lastError instanceof Object ) {
|
||||
return reject(lastError);
|
||||
}
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
},
|
||||
getBytesInUse: function(keys, callback) {
|
||||
if ( callback !== undefined ) {
|
||||
return browser.storage.local.getBytesInUse(...arguments);
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
browser.storage.local.getBytesInUse(keys, result => {
|
||||
const lastError = browser.runtime.lastError;
|
||||
if ( lastError instanceof Object ) {
|
||||
return reject(lastError);
|
||||
}
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
},
|
||||
remove: function(keys, callback) {
|
||||
if ( callback !== undefined ) {
|
||||
return browser.storage.local.remove(...arguments);
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
browser.storage.local.remove(keys, ( ) => {
|
||||
const lastError = browser.runtime.lastError;
|
||||
if ( lastError instanceof Object ) {
|
||||
return reject(lastError);
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
},
|
||||
set: function(items, callback) {
|
||||
if ( callback !== undefined ) {
|
||||
return browser.storage.local.set(...arguments);
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
browser.storage.local.set(items, ( ) => {
|
||||
const lastError = browser.runtime.lastError;
|
||||
if ( lastError instanceof Object ) {
|
||||
return reject(lastError);
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
})();
|
||||
|
||||
/******************************************************************************/
|
||||
/******************************************************************************/
|
||||
|
@ -1286,7 +1369,7 @@ vAPI.commands = chrome.commands;
|
|||
/******************************************************************************/
|
||||
|
||||
// https://github.com/gorhill/uBlock/issues/531
|
||||
// Storage area dedicated to admin settings. Read-only.
|
||||
// Storage area dedicated to admin settings. Read-only.
|
||||
|
||||
// https://github.com/gorhill/uBlock/commit/43a5ed735b95a575a9339b6e71a1fcb27a99663b#commitcomment-13965030
|
||||
// Not all Chromium-based browsers support managed storage. Merely testing or
|
||||
|
@ -1297,26 +1380,48 @@ vAPI.commands = chrome.commands;
|
|||
// https://github.com/gorhill/uBlock/issues/900
|
||||
// Also, UC Browser: http://www.upsieutoc.com/image/WXuH
|
||||
|
||||
vAPI.adminStorage = chrome.storage.managed && {
|
||||
getItem: function(key, callback) {
|
||||
const onRead = function(store) {
|
||||
vAPI.adminStorage = (( ) => {
|
||||
if ( browser.storage.managed instanceof Object === false ) {
|
||||
return {
|
||||
getItem: function() {
|
||||
return Promise.resolve();
|
||||
},
|
||||
};
|
||||
}
|
||||
const managedStorage = vAPI.apiIsPromisified
|
||||
? browser.storage.managed
|
||||
: {
|
||||
get: function(keys) {
|
||||
return new Promise((resolve, reject) => {
|
||||
browser.storage.managed.get(keys, result => {
|
||||
const lastError = browser.runtime.lastError;
|
||||
if ( lastError instanceof Object ) {
|
||||
return reject(lastError);
|
||||
}
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
return {
|
||||
getItem: async function(key) {
|
||||
let bin;
|
||||
try {
|
||||
bin = await managedStorage.get(key);
|
||||
} catch(ex) {
|
||||
}
|
||||
let data;
|
||||
if (
|
||||
!chrome.runtime.lastError &&
|
||||
typeof store === 'object' &&
|
||||
store !== null
|
||||
chrome.runtime.lastError instanceof Object === false &&
|
||||
bin instanceof Object
|
||||
) {
|
||||
data = store[key];
|
||||
data = bin[key];
|
||||
}
|
||||
callback(data);
|
||||
};
|
||||
try {
|
||||
chrome.storage.managed.get(key, onRead);
|
||||
} catch (ex) {
|
||||
callback();
|
||||
return data;
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
})();
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
/******************************************************************************/
|
||||
|
|
919
src/js/assets.js
919
src/js/assets.js
File diff suppressed because it is too large
Load diff
|
@ -58,9 +58,10 @@ const onMessage = function(request, sender, callback) {
|
|||
// https://github.com/chrisaljoudi/uBlock/issues/417
|
||||
µb.assets.get(
|
||||
request.url,
|
||||
{ dontCache: true, needSourceURL: true },
|
||||
callback
|
||||
);
|
||||
{ dontCache: true, needSourceURL: true }
|
||||
).then(result => {
|
||||
callback(result);
|
||||
});
|
||||
return;
|
||||
|
||||
case 'listsFromNetFilter':
|
||||
|
@ -791,24 +792,18 @@ vAPI.messaging.listen({
|
|||
const µb = µBlock;
|
||||
|
||||
// Settings
|
||||
const getLocalData = function(callback) {
|
||||
const onStorageInfoReady = function(bytesInUse) {
|
||||
const o = µb.restoreBackupSettings;
|
||||
callback({
|
||||
storageUsed: bytesInUse,
|
||||
lastRestoreFile: o.lastRestoreFile,
|
||||
lastRestoreTime: o.lastRestoreTime,
|
||||
lastBackupFile: o.lastBackupFile,
|
||||
lastBackupTime: o.lastBackupTime,
|
||||
cloudStorageSupported: µb.cloudStorageSupported,
|
||||
privacySettingsSupported: µb.privacySettingsSupported
|
||||
});
|
||||
};
|
||||
|
||||
µb.getBytesInUse(onStorageInfoReady);
|
||||
const getLocalData = async function() {
|
||||
const data = Object.assign({}, µb.restoreBackupSettings);
|
||||
data.storageUsed = await µb.getBytesInUse();
|
||||
return data;
|
||||
};
|
||||
|
||||
const backupUserData = function(callback) {
|
||||
const backupUserData = async function() {
|
||||
const [ userFilters, localData ] = await Promise.all([
|
||||
µb.loadUserFilters(),
|
||||
getLocalData(),
|
||||
]);
|
||||
|
||||
const userData = {
|
||||
timeStamp: Date.now(),
|
||||
version: vAPI.app.version,
|
||||
|
@ -821,23 +816,17 @@ const backupUserData = function(callback) {
|
|||
dynamicFilteringString: µb.permanentFirewall.toString(),
|
||||
urlFilteringString: µb.permanentURLFiltering.toString(),
|
||||
hostnameSwitchesString: µb.permanentSwitches.toString(),
|
||||
userFilters: ''
|
||||
userFilters: userFilters.content,
|
||||
};
|
||||
|
||||
const onUserFiltersReady = function(details) {
|
||||
userData.userFilters = details.content;
|
||||
const filename = vAPI.i18n('aboutBackupFilename')
|
||||
.replace('{{datetime}}', µb.dateNowToSensibleString())
|
||||
.replace(/ +/g, '_');
|
||||
µb.restoreBackupSettings.lastBackupFile = filename;
|
||||
µb.restoreBackupSettings.lastBackupTime = Date.now();
|
||||
vAPI.storage.set(µb.restoreBackupSettings);
|
||||
getLocalData(function(localData) {
|
||||
callback({ localData: localData, userData: userData });
|
||||
});
|
||||
};
|
||||
const filename = vAPI.i18n('aboutBackupFilename')
|
||||
.replace('{{datetime}}', µb.dateNowToSensibleString())
|
||||
.replace(/ +/g, '_');
|
||||
µb.restoreBackupSettings.lastBackupFile = filename;
|
||||
µb.restoreBackupSettings.lastBackupTime = Date.now();
|
||||
vAPI.storage.set(µb.restoreBackupSettings);
|
||||
|
||||
µb.assets.get(µb.userFiltersPath, onUserFiltersReady);
|
||||
return { localData, userData };
|
||||
};
|
||||
|
||||
const restoreUserData = function(request) {
|
||||
|
@ -881,7 +870,7 @@ const restoreUserData = function(request) {
|
|||
lastBackupFile: '',
|
||||
lastBackupTime: 0
|
||||
});
|
||||
µb.assets.put(µb.userFiltersPath, userData.userFilters);
|
||||
µb.saveUserFilters(userData.userFilters);
|
||||
if ( Array.isArray(userData.selectedFilterLists) ) {
|
||||
µb.saveSelectedFilterLists(userData.selectedFilterLists, restart);
|
||||
} else {
|
||||
|
@ -902,18 +891,17 @@ const restoreUserData = function(request) {
|
|||
|
||||
// Remove all stored data but keep global counts, people can become
|
||||
// quite attached to numbers
|
||||
const resetUserData = function() {
|
||||
let count = 3;
|
||||
const countdown = ( ) => {
|
||||
count -= 1;
|
||||
if ( count === 0 ) {
|
||||
vAPI.app.restart();
|
||||
}
|
||||
};
|
||||
µb.cacheStorage.clear().then(( ) => countdown()); // 1
|
||||
vAPI.storage.clear(countdown); // 2
|
||||
µb.saveLocalSettings(countdown); // 3
|
||||
const resetUserData = async function() {
|
||||
vAPI.localStorage.removeItem('immediateHiddenSettings');
|
||||
|
||||
await Promise.all([
|
||||
µb.cacheStorage.clear(),
|
||||
vAPI.storage.clear(),
|
||||
]);
|
||||
|
||||
await µb.saveLocalSettings();
|
||||
|
||||
vAPI.app.restart();
|
||||
};
|
||||
|
||||
// 3rd-party filters
|
||||
|
@ -932,7 +920,7 @@ const prepListEntries = function(entries) {
|
|||
}
|
||||
};
|
||||
|
||||
const getLists = function(callback) {
|
||||
const getLists = async function(callback) {
|
||||
const r = {
|
||||
autoUpdate: µb.userSettings.autoUpdate,
|
||||
available: null,
|
||||
|
@ -946,17 +934,15 @@ const getLists = function(callback) {
|
|||
parseCosmeticFilters: µb.userSettings.parseAllABPHideFilters,
|
||||
userFiltersPath: µb.userFiltersPath
|
||||
};
|
||||
const onMetadataReady = function(entries) {
|
||||
r.cache = entries;
|
||||
prepListEntries(r.cache);
|
||||
callback(r);
|
||||
};
|
||||
const onLists = function(lists) {
|
||||
r.available = lists;
|
||||
prepListEntries(r.available);
|
||||
µb.assets.metadata(onMetadataReady);
|
||||
};
|
||||
µb.getAvailableLists(onLists);
|
||||
const [ lists, metadata ] = await Promise.all([
|
||||
µb.getAvailableLists(),
|
||||
µb.assets.metadata(),
|
||||
]);
|
||||
r.available = lists;
|
||||
prepListEntries(r.available);
|
||||
r.cache = metadata;
|
||||
prepListEntries(r.cache);
|
||||
callback(r);
|
||||
};
|
||||
|
||||
// My rules
|
||||
|
@ -1060,29 +1046,37 @@ const onMessage = function(request, sender, callback) {
|
|||
// Async
|
||||
switch ( request.what ) {
|
||||
case 'backupUserData':
|
||||
return backupUserData(callback);
|
||||
return backupUserData().then(data => {
|
||||
callback(data);
|
||||
});
|
||||
|
||||
case 'getLists':
|
||||
return getLists(callback);
|
||||
|
||||
case 'getLocalData':
|
||||
return getLocalData(callback);
|
||||
return getLocalData().then(localData => {
|
||||
callback(localData);
|
||||
});
|
||||
|
||||
case 'getShortcuts':
|
||||
return getShortcuts(callback);
|
||||
|
||||
case 'readUserFilters':
|
||||
return µb.loadUserFilters(callback);
|
||||
return µb.loadUserFilters().then(result => {
|
||||
callback(result);
|
||||
});
|
||||
|
||||
case 'writeUserFilters':
|
||||
return µb.saveUserFilters(request.content, callback);
|
||||
return µb.saveUserFilters(request.content).then(result => {
|
||||
callback(result);
|
||||
});
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Sync
|
||||
var response;
|
||||
let response;
|
||||
|
||||
switch ( request.what ) {
|
||||
case 'canUpdateShortcuts':
|
||||
|
|
|
@ -573,21 +573,20 @@ RedirectEngine.prototype.toSelfie = function(path) {
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
RedirectEngine.prototype.fromSelfie = function(path) {
|
||||
return µBlock.assets.get(`${path}/main`).then(details => {
|
||||
let selfie;
|
||||
try {
|
||||
selfie = JSON.parse(details.content);
|
||||
} catch (ex) {
|
||||
}
|
||||
if ( selfie instanceof Object === false ) { return false; }
|
||||
this.rules = new Map(selfie.rules);
|
||||
this.ruleSources = new Set(selfie.ruleSources);
|
||||
this.ruleDestinations = new Set(selfie.ruleDestinations);
|
||||
this.resetCache();
|
||||
this.modifyTime = Date.now();
|
||||
return true;
|
||||
});
|
||||
RedirectEngine.prototype.fromSelfie = async function(path) {
|
||||
const result = await µBlock.assets.get(`${path}/main`);
|
||||
let selfie;
|
||||
try {
|
||||
selfie = JSON.parse(result.content);
|
||||
} catch (ex) {
|
||||
}
|
||||
if ( selfie instanceof Object === false ) { return false; }
|
||||
this.rules = new Map(selfie.rules);
|
||||
this.ruleSources = new Set(selfie.ruleSources);
|
||||
this.ruleDestinations = new Set(selfie.ruleDestinations);
|
||||
this.resetCache();
|
||||
this.modifyTime = Date.now();
|
||||
return true;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
@ -788,29 +787,26 @@ RedirectEngine.prototype.selfieFromResources = function() {
|
|||
);
|
||||
};
|
||||
|
||||
RedirectEngine.prototype.resourcesFromSelfie = function() {
|
||||
return µBlock.assets.get(
|
||||
'compiled/redirectEngine/resources'
|
||||
).then(details => {
|
||||
let selfie;
|
||||
try {
|
||||
selfie = JSON.parse(details.content);
|
||||
} catch(ex) {
|
||||
}
|
||||
if (
|
||||
selfie instanceof Object === false ||
|
||||
selfie.version !== resourcesSelfieVersion ||
|
||||
Array.isArray(selfie.resources) === false
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
this.aliases = new Map(selfie.aliases);
|
||||
this.resources = new Map();
|
||||
for ( const [ token, entry ] of selfie.resources ) {
|
||||
this.resources.set(token, RedirectEntry.fromSelfie(entry));
|
||||
}
|
||||
return true;
|
||||
});
|
||||
RedirectEngine.prototype.resourcesFromSelfie = async function() {
|
||||
const result = await µBlock.assets.get('compiled/redirectEngine/resources');
|
||||
let selfie;
|
||||
try {
|
||||
selfie = JSON.parse(result.content);
|
||||
} catch(ex) {
|
||||
}
|
||||
if (
|
||||
selfie instanceof Object === false ||
|
||||
selfie.version !== resourcesSelfieVersion ||
|
||||
Array.isArray(selfie.resources) === false
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
this.aliases = new Map(selfie.aliases);
|
||||
this.resources = new Map();
|
||||
for ( const [ token, entry ] of selfie.resources ) {
|
||||
this.resources.set(token, RedirectEntry.fromSelfie(entry));
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
RedirectEngine.prototype.invalidateResourcesSelfie = function() {
|
||||
|
|
|
@ -23,59 +23,64 @@
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
µBlock.staticFilteringReverseLookup = (function() {
|
||||
µBlock.staticFilteringReverseLookup = (( ) => {
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var worker = null;
|
||||
var workerTTL = 5 * 60 * 1000;
|
||||
var workerTTLTimer = null;
|
||||
var needLists = true;
|
||||
var messageId = 1;
|
||||
var pendingResponses = Object.create(null);
|
||||
const workerTTL = 5 * 60 * 1000;
|
||||
const pendingResponses = new Map();
|
||||
|
||||
let worker = null;
|
||||
let workerTTLTimer;
|
||||
let needLists = true;
|
||||
let messageId = 1;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var onWorkerMessage = function(e) {
|
||||
var msg = e.data;
|
||||
var callback = pendingResponses[msg.id];
|
||||
delete pendingResponses[msg.id];
|
||||
const onWorkerMessage = function(e) {
|
||||
const msg = e.data;
|
||||
const callback = pendingResponses.get(msg.id);
|
||||
pendingResponses.delete(msg.id);
|
||||
callback(msg.response);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var stopWorker = function() {
|
||||
workerTTLTimer = null;
|
||||
if ( worker === null ) {
|
||||
return;
|
||||
const stopWorker = function() {
|
||||
if ( workerTTLTimer !== undefined ) {
|
||||
clearTimeout(workerTTLTimer);
|
||||
workerTTLTimer = undefined;
|
||||
}
|
||||
if ( worker === null ) { return; }
|
||||
worker.terminate();
|
||||
worker = null;
|
||||
needLists = true;
|
||||
pendingResponses = Object.create(null);
|
||||
pendingResponses.clear();
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var initWorker = function(callback) {
|
||||
const initWorker = function() {
|
||||
if ( worker === null ) {
|
||||
worker = new Worker('js/reverselookup-worker.js');
|
||||
worker.onmessage = onWorkerMessage;
|
||||
}
|
||||
|
||||
if ( needLists === false ) {
|
||||
callback();
|
||||
return;
|
||||
// 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;
|
||||
|
||||
var entries = Object.create(null);
|
||||
var countdown = 0;
|
||||
const entries = new Map();
|
||||
|
||||
var onListLoaded = function(details) {
|
||||
var entry = entries[details.assetKey];
|
||||
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.
|
||||
|
@ -89,44 +94,40 @@ var initWorker = function(callback) {
|
|||
content: details.content
|
||||
}
|
||||
});
|
||||
|
||||
countdown -= 1;
|
||||
if ( countdown === 0 ) {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
var µb = µBlock;
|
||||
var listKey, entry;
|
||||
|
||||
for ( listKey in µb.availableFilterLists ) {
|
||||
const µb = µBlock;
|
||||
for ( const listKey in µb.availableFilterLists ) {
|
||||
if ( µb.availableFilterLists.hasOwnProperty(listKey) === false ) {
|
||||
continue;
|
||||
}
|
||||
entry = µb.availableFilterLists[listKey];
|
||||
const entry = µb.availableFilterLists[listKey];
|
||||
if ( entry.off === true ) { continue; }
|
||||
entries[listKey] = {
|
||||
entries.set(listKey, {
|
||||
title: listKey !== µb.userFiltersPath ?
|
||||
entry.title :
|
||||
vAPI.i18n('1pPageName'),
|
||||
supportURL: entry.supportURL || ''
|
||||
};
|
||||
countdown += 1;
|
||||
});
|
||||
}
|
||||
if ( entries.size === 0 ) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
if ( countdown === 0 ) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
for ( listKey in entries ) {
|
||||
µb.getCompiledFilterList(listKey, onListLoaded);
|
||||
const promises = [];
|
||||
for ( const listKey of entries.keys() ) {
|
||||
promises.push(
|
||||
µb.getCompiledFilterList(listKey).then(details => {
|
||||
onListLoaded(details);
|
||||
})
|
||||
);
|
||||
}
|
||||
return Promise.all(promises);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var fromNetFilter = function(compiledFilter, rawFilter, callback) {
|
||||
const fromNetFilter = async function(compiledFilter, rawFilter, callback) {
|
||||
if ( typeof callback !== 'function' ) {
|
||||
return;
|
||||
}
|
||||
|
@ -136,32 +137,22 @@ var fromNetFilter = function(compiledFilter, rawFilter, callback) {
|
|||
return;
|
||||
}
|
||||
|
||||
if ( workerTTLTimer !== null ) {
|
||||
clearTimeout(workerTTLTimer);
|
||||
workerTTLTimer = null;
|
||||
}
|
||||
await initWorker();
|
||||
|
||||
var onWorkerReady = function() {
|
||||
var id = messageId++;
|
||||
var message = {
|
||||
what: 'fromNetFilter',
|
||||
id: id,
|
||||
compiledFilter: compiledFilter,
|
||||
rawFilter: rawFilter
|
||||
};
|
||||
pendingResponses[id] = callback;
|
||||
worker.postMessage(message);
|
||||
|
||||
// The worker will be shutdown after n minutes without being used.
|
||||
workerTTLTimer = vAPI.setTimeout(stopWorker, workerTTL);
|
||||
const id = messageId++;
|
||||
const message = {
|
||||
what: 'fromNetFilter',
|
||||
id: id,
|
||||
compiledFilter: compiledFilter,
|
||||
rawFilter: rawFilter
|
||||
};
|
||||
|
||||
initWorker(onWorkerReady);
|
||||
pendingResponses.set(id, callback);
|
||||
worker.postMessage(message);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var fromCosmeticFilter = function(details, callback) {
|
||||
const fromCosmeticFilter = async function(details, callback) {
|
||||
if ( typeof callback !== 'function' ) { return; }
|
||||
|
||||
if ( details.rawFilter === '' ) {
|
||||
|
@ -169,50 +160,38 @@ var fromCosmeticFilter = function(details, callback) {
|
|||
return;
|
||||
}
|
||||
|
||||
if ( workerTTLTimer !== null ) {
|
||||
clearTimeout(workerTTLTimer);
|
||||
workerTTLTimer = null;
|
||||
}
|
||||
await initWorker();
|
||||
|
||||
let onWorkerReady = function() {
|
||||
let id = messageId++;
|
||||
let hostname = µBlock.URI.hostnameFromURI(details.url);
|
||||
pendingResponses[id] = callback;
|
||||
worker.postMessage({
|
||||
what: 'fromCosmeticFilter',
|
||||
id: id,
|
||||
domain: µBlock.URI.domainFromHostname(hostname),
|
||||
hostname: hostname,
|
||||
ignoreGeneric: µBlock.staticNetFilteringEngine
|
||||
.matchStringGenericHide(details.url) === 2,
|
||||
rawFilter: details.rawFilter
|
||||
});
|
||||
|
||||
// The worker will be shutdown after n minutes without being used.
|
||||
workerTTLTimer = vAPI.setTimeout(stopWorker, workerTTL);
|
||||
};
|
||||
|
||||
initWorker(onWorkerReady);
|
||||
const id = messageId++;
|
||||
const hostname = µBlock.URI.hostnameFromURI(details.url);
|
||||
pendingResponses.set(id, callback);
|
||||
worker.postMessage({
|
||||
what: 'fromCosmeticFilter',
|
||||
id: id,
|
||||
domain: µBlock.URI.domainFromHostname(hostname),
|
||||
hostname: hostname,
|
||||
ignoreGeneric: µBlock.staticNetFilteringEngine
|
||||
.matchStringGenericHide(details.url) === 2,
|
||||
rawFilter: details.rawFilter
|
||||
});
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// This tells the worker that filter lists may have changed.
|
||||
|
||||
var resetLists = function() {
|
||||
const resetLists = function() {
|
||||
needLists = true;
|
||||
if ( worker === null ) {
|
||||
return;
|
||||
}
|
||||
if ( worker === null ) { return; }
|
||||
worker.postMessage({ what: 'resetLists' });
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
return {
|
||||
fromNetFilter: fromNetFilter,
|
||||
fromCosmeticFilter: fromCosmeticFilter,
|
||||
resetLists: resetLists,
|
||||
fromNetFilter,
|
||||
fromCosmeticFilter,
|
||||
resetLists,
|
||||
shutdown: stopWorker
|
||||
};
|
||||
|
||||
|
|
104
src/js/start.js
104
src/js/start.js
|
@ -25,8 +25,8 @@
|
|||
|
||||
// Load all: executed once.
|
||||
|
||||
{
|
||||
// >>>>> start of local scope
|
||||
(async ( ) => {
|
||||
// >>>>> start of private scope
|
||||
|
||||
const µb = µBlock;
|
||||
|
||||
|
@ -97,8 +97,6 @@ const onAllReady = function() {
|
|||
vAPI.app.restart();
|
||||
}
|
||||
});
|
||||
|
||||
log.info(`All ready ${Date.now()-vAPI.T0} ms after launch`);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
@ -152,27 +150,6 @@ const initializeTabs = function() {
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
// Filtering engines dependencies:
|
||||
// - PSL
|
||||
|
||||
const onPSLReady = function() {
|
||||
log.info(`PSL ready ${Date.now()-vAPI.T0} ms after launch`);
|
||||
|
||||
µb.selfieManager.load().then(valid => {
|
||||
if ( valid === true ) {
|
||||
log.info(`Selfie ready ${Date.now()-vAPI.T0} ms after launch`);
|
||||
onAllReady();
|
||||
return;
|
||||
}
|
||||
µb.loadFilterLists(( ) => {
|
||||
log.info(`Filter lists ready ${Date.now()-vAPI.T0} ms after launch`);
|
||||
onAllReady();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const onCommandShortcutsReady = function(commandShortcuts) {
|
||||
if ( Array.isArray(commandShortcuts) === false ) { return; }
|
||||
µb.commandShortcuts = new Map(commandShortcuts);
|
||||
|
@ -226,8 +203,6 @@ const onNetWhitelistReady = function(netWhitelistRaw) {
|
|||
// User settings are in memory
|
||||
|
||||
const onUserSettingsReady = function(fetched) {
|
||||
log.info(`User settings ready ${Date.now()-vAPI.T0} ms after launch`);
|
||||
|
||||
const userSettings = µb.userSettings;
|
||||
|
||||
fromFetch(userSettings, fetched);
|
||||
|
@ -271,8 +246,6 @@ const onSystemSettingsReady = function(fetched) {
|
|||
/******************************************************************************/
|
||||
|
||||
const onFirstFetchReady = function(fetched) {
|
||||
log.info(`First fetch ready ${Date.now()-vAPI.T0} ms after launch`);
|
||||
|
||||
// https://github.com/uBlockOrigin/uBlock-issues/issues/507
|
||||
// Firefox-specific: somehow `fetched` is undefined under certain
|
||||
// circumstances even though we asked to load with default values.
|
||||
|
@ -291,10 +264,6 @@ const onFirstFetchReady = function(fetched) {
|
|||
onNetWhitelistReady(fetched.netWhitelist);
|
||||
onVersionReady(fetched.version);
|
||||
onCommandShortcutsReady(fetched.commandShortcuts);
|
||||
|
||||
µb.loadPublicSuffixList().then(( ) => {
|
||||
onPSLReady();
|
||||
});
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
@ -347,42 +316,45 @@ const createDefaultProps = function() {
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
const onHiddenSettingsReady = function() {
|
||||
return µb.cacheStorage.select(
|
||||
µb.hiddenSettings.cacheStorageAPI
|
||||
).then(backend => {
|
||||
log.info(`Backend storage for cache will be ${backend}`);
|
||||
});
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// TODO(seamless migration):
|
||||
// Eventually selected filter list keys will be loaded as a fetchable
|
||||
// property. Until then we need to handle backward and forward
|
||||
// compatibility, this means a special asynchronous call to load selected
|
||||
// filter lists.
|
||||
|
||||
const onAdminSettingsRestored = function() {
|
||||
try {
|
||||
// https://github.com/gorhill/uBlock/issues/531
|
||||
await µb.restoreAdminSettings();
|
||||
log.info(`Admin settings ready ${Date.now()-vAPI.T0} ms after launch`);
|
||||
|
||||
Promise.all([
|
||||
µb.loadHiddenSettings().then(( ) =>
|
||||
onHiddenSettingsReady()
|
||||
),
|
||||
µb.loadSelectedFilterLists(),
|
||||
]).then(( ) => {
|
||||
log.info(`List selection ready ${Date.now()-vAPI.T0} ms after launch`);
|
||||
vAPI.storage.get(createDefaultProps(), onFirstFetchReady);
|
||||
});
|
||||
};
|
||||
await µb.loadHiddenSettings();
|
||||
log.info(`Hidden settings ready ${Date.now()-vAPI.T0} ms after launch`);
|
||||
|
||||
/******************************************************************************/
|
||||
const cacheBackend = await µb.cacheStorage.select(
|
||||
µb.hiddenSettings.cacheStorageAPI
|
||||
);
|
||||
log.info(`Backend storage for cache will be ${cacheBackend}`);
|
||||
|
||||
// https://github.com/gorhill/uBlock/issues/531
|
||||
µb.restoreAdminSettings().then(( ) => {
|
||||
onAdminSettingsRestored();
|
||||
});
|
||||
await Promise.all([
|
||||
µb.loadSelectedFilterLists().then(( ) => {
|
||||
log.info(`List selection ready ${Date.now()-vAPI.T0} ms after launch`);
|
||||
}),
|
||||
vAPI.storage.get(createDefaultProps()).then(fetched => {
|
||||
log.info(`First fetch ready ${Date.now()-vAPI.T0} ms after launch`);
|
||||
onFirstFetchReady(fetched);
|
||||
}),
|
||||
µb.loadPublicSuffixList().then(( ) => {
|
||||
log.info(`PSL ready ${Date.now()-vAPI.T0} ms after launch`);
|
||||
}),
|
||||
]);
|
||||
|
||||
// <<<<< end of local scope
|
||||
const selfieIsValid = await µb.selfieManager.load();
|
||||
if ( selfieIsValid === true ) {
|
||||
log.info(`Selfie ready ${Date.now()-vAPI.T0} ms after launch`);
|
||||
} else {
|
||||
await µb.loadFilterLists();
|
||||
log.info(`Filter lists ready ${Date.now()-vAPI.T0} ms after launch`);
|
||||
}
|
||||
} catch (ex) {
|
||||
console.trace(ex);
|
||||
}
|
||||
|
||||
onAllReady();
|
||||
log.info(`All ready ${Date.now()-vAPI.T0} ms after launch`);
|
||||
|
||||
// <<<<< end of private scope
|
||||
})();
|
||||
|
|
|
@ -25,48 +25,44 @@
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
µBlock.getBytesInUse = function(callback) {
|
||||
if ( typeof callback !== 'function' ) {
|
||||
callback = this.noopFunc;
|
||||
}
|
||||
µBlock.getBytesInUse = async function() {
|
||||
const promises = [];
|
||||
let bytesInUse;
|
||||
let countdown = 0;
|
||||
|
||||
const process = count => {
|
||||
if ( typeof count === 'number' ) {
|
||||
if ( bytesInUse === undefined ) {
|
||||
bytesInUse = 0;
|
||||
}
|
||||
bytesInUse += count;
|
||||
}
|
||||
countdown -= 1;
|
||||
if ( countdown > 0 ) { return; }
|
||||
µBlock.storageUsed = bytesInUse;
|
||||
callback(bytesInUse);
|
||||
};
|
||||
|
||||
// Not all platforms implement this method.
|
||||
if ( vAPI.storage.getBytesInUse instanceof Function ) {
|
||||
countdown += 1;
|
||||
vAPI.storage.getBytesInUse(null, process);
|
||||
}
|
||||
promises.push(
|
||||
vAPI.storage.getBytesInUse instanceof Function
|
||||
? vAPI.storage.getBytesInUse(null)
|
||||
: undefined
|
||||
);
|
||||
|
||||
if (
|
||||
navigator.storage instanceof Object &&
|
||||
navigator.storage.estimate instanceof Function
|
||||
) {
|
||||
countdown += 1;
|
||||
navigator.storage.estimate().then(estimate => {
|
||||
process(estimate.usage);
|
||||
});
|
||||
promises.push(navigator.storage.estimate());
|
||||
}
|
||||
if ( countdown === 0 ) {
|
||||
callback();
|
||||
|
||||
const results = await Promise.all(promises);
|
||||
|
||||
const processCount = count => {
|
||||
if ( typeof count !== 'number' ) { return; }
|
||||
if ( bytesInUse === undefined ) { bytesInUse = 0; }
|
||||
bytesInUse += count;
|
||||
return bytesInUse;
|
||||
};
|
||||
|
||||
processCount(results[0]);
|
||||
if ( results.length > 1 && results[1] instanceof Object ) {
|
||||
processCount(results[1].usage);
|
||||
}
|
||||
µBlock.storageUsed = bytesInUse;
|
||||
return bytesInUse;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
µBlock.saveLocalSettings = (function() {
|
||||
µBlock.saveLocalSettings = (( ) => {
|
||||
const saveAfter = 4 * 60 * 1000;
|
||||
|
||||
const onTimeout = ( ) => {
|
||||
|
@ -79,9 +75,9 @@
|
|||
|
||||
vAPI.setTimeout(onTimeout, saveAfter);
|
||||
|
||||
return function(callback) {
|
||||
return function() {
|
||||
this.localSettingsLastSaved = Date.now();
|
||||
vAPI.storage.set(this.localSettings, callback);
|
||||
return vAPI.storage.set(this.localSettings);
|
||||
};
|
||||
})();
|
||||
|
||||
|
@ -93,40 +89,31 @@
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
µBlock.loadHiddenSettings = function() {
|
||||
return new Promise(resolve => {
|
||||
// >>>> start of executor
|
||||
µBlock.loadHiddenSettings = async function() {
|
||||
const bin = await vAPI.storage.get('hiddenSettings');
|
||||
if ( bin instanceof Object === false ) { return; }
|
||||
|
||||
vAPI.storage.get('hiddenSettings', bin => {
|
||||
if ( bin instanceof Object === false ) {
|
||||
return resolve();
|
||||
}
|
||||
const hs = bin.hiddenSettings;
|
||||
if ( hs instanceof Object ) {
|
||||
const hsDefault = this.hiddenSettingsDefault;
|
||||
for ( const key in hsDefault ) {
|
||||
if (
|
||||
hsDefault.hasOwnProperty(key) &&
|
||||
hs.hasOwnProperty(key) &&
|
||||
typeof hs[key] === typeof hsDefault[key]
|
||||
) {
|
||||
this.hiddenSettings[key] = hs[key];
|
||||
}
|
||||
}
|
||||
if ( typeof this.hiddenSettings.suspendTabsUntilReady === 'boolean' ) {
|
||||
this.hiddenSettings.suspendTabsUntilReady =
|
||||
this.hiddenSettings.suspendTabsUntilReady
|
||||
? 'yes'
|
||||
: 'unset';
|
||||
const hs = bin.hiddenSettings;
|
||||
if ( hs instanceof Object ) {
|
||||
const hsDefault = this.hiddenSettingsDefault;
|
||||
for ( const key in hsDefault ) {
|
||||
if (
|
||||
hsDefault.hasOwnProperty(key) &&
|
||||
hs.hasOwnProperty(key) &&
|
||||
typeof hs[key] === typeof hsDefault[key]
|
||||
) {
|
||||
this.hiddenSettings[key] = hs[key];
|
||||
}
|
||||
}
|
||||
self.log.verbosity = this.hiddenSettings.consoleLogLevel;
|
||||
resolve();
|
||||
this.fireDOMEvent('hiddenSettingsChanged');
|
||||
});
|
||||
|
||||
// <<<< end of executor
|
||||
});
|
||||
if ( typeof this.hiddenSettings.suspendTabsUntilReady === 'boolean' ) {
|
||||
this.hiddenSettings.suspendTabsUntilReady =
|
||||
this.hiddenSettings.suspendTabsUntilReady
|
||||
? 'yes'
|
||||
: 'unset';
|
||||
}
|
||||
}
|
||||
self.log.verbosity = this.hiddenSettings.consoleLogLevel;
|
||||
this.fireDOMEvent('hiddenSettingsChanged');
|
||||
};
|
||||
|
||||
// Note: Save only the settings which values differ from the default ones.
|
||||
|
@ -261,30 +248,16 @@
|
|||
|
||||
**/
|
||||
|
||||
µBlock.loadSelectedFilterLists = function() {
|
||||
return new Promise(resolve => {
|
||||
// >>>> start of executor
|
||||
|
||||
vAPI.storage.get('selectedFilterLists', bin => {
|
||||
// Select default filter lists if first-time launch.
|
||||
if (
|
||||
bin instanceof Object === false ||
|
||||
Array.isArray(bin.selectedFilterLists) === false
|
||||
) {
|
||||
this.assets.metadata(availableLists => {
|
||||
this.saveSelectedFilterLists(
|
||||
this.autoSelectRegionalFilterLists(availableLists)
|
||||
);
|
||||
resolve();
|
||||
});
|
||||
return;
|
||||
}
|
||||
µBlock.loadSelectedFilterLists = async function() {
|
||||
const bin = await vAPI.storage.get('selectedFilterLists');
|
||||
if ( bin instanceof Object && Array.isArray(bin.selectedFilterLists) ) {
|
||||
this.selectedFilterLists = bin.selectedFilterLists;
|
||||
resolve();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// <<<< end of executor
|
||||
});
|
||||
// Select default filter lists if first-time launch.
|
||||
const lists = await this.assets.metadata();
|
||||
this.saveSelectedFilterLists(this.autoSelectRegionalFilterLists(lists));
|
||||
};
|
||||
|
||||
µBlock.saveSelectedFilterLists = function(newKeys, append, callback) {
|
||||
|
@ -409,20 +382,20 @@
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
µBlock.saveUserFilters = function(content, callback) {
|
||||
µBlock.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.assets.put(this.userFiltersPath, content, callback);
|
||||
this.removeCompiledFilterList(this.userFiltersPath);
|
||||
return this.assets.put(this.userFiltersPath, content);
|
||||
};
|
||||
|
||||
µBlock.loadUserFilters = function(callback) {
|
||||
return this.assets.get(this.userFiltersPath, callback);
|
||||
µBlock.loadUserFilters = function() {
|
||||
return this.assets.get(this.userFiltersPath);
|
||||
};
|
||||
|
||||
µBlock.appendUserFilters = function(filters, options) {
|
||||
µBlock.appendUserFilters = async function(filters, options) {
|
||||
filters = filters.trim();
|
||||
if ( filters.length === 0 ) { return; }
|
||||
|
||||
|
@ -443,59 +416,56 @@
|
|||
.replace('{{origin}}', options.origin);
|
||||
}
|
||||
|
||||
const onSaved = ( ) => {
|
||||
const compiledFilters = this.compileFilters(
|
||||
filters,
|
||||
{ assetKey: this.userFiltersPath }
|
||||
);
|
||||
const snfe = this.staticNetFilteringEngine;
|
||||
const cfe = this.cosmeticFilteringEngine;
|
||||
const acceptedCount = snfe.acceptedCount + cfe.acceptedCount;
|
||||
const discardedCount = snfe.discardedCount + cfe.discardedCount;
|
||||
this.applyCompiledFilters(compiledFilters, true);
|
||||
const entry = this.availableFilterLists[this.userFiltersPath];
|
||||
const deltaEntryCount =
|
||||
snfe.acceptedCount +
|
||||
cfe.acceptedCount - acceptedCount;
|
||||
const deltaEntryUsedCount =
|
||||
deltaEntryCount -
|
||||
(snfe.discardedCount + cfe.discardedCount - discardedCount);
|
||||
entry.entryCount += deltaEntryCount;
|
||||
entry.entryUsedCount += deltaEntryUsedCount;
|
||||
vAPI.storage.set({ 'availableFilterLists': this.availableFilterLists });
|
||||
this.staticNetFilteringEngine.freeze();
|
||||
this.redirectEngine.freeze();
|
||||
this.staticExtFilteringEngine.freeze();
|
||||
this.selfieManager.destroy();
|
||||
const details = await this.loadUserFilters();
|
||||
if ( details.error ) { return; }
|
||||
|
||||
// https://www.reddit.com/r/uBlockOrigin/comments/cj7g7m/
|
||||
// https://www.reddit.com/r/uBlockOrigin/comments/cnq0bi/
|
||||
if ( options.killCache ) {
|
||||
browser.webRequest.handlerBehaviorChanged();
|
||||
// The comment, if any, will be applied if and only if it is different
|
||||
// from the last comment found in the user filter list.
|
||||
if ( comment !== '' ) {
|
||||
const pos = details.content.lastIndexOf(comment);
|
||||
if (
|
||||
pos === -1 ||
|
||||
details.content.indexOf('\n!', pos + 1) !== -1
|
||||
) {
|
||||
filters = '\n' + comment + '\n' + filters;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const onLoaded = details => {
|
||||
if ( details.error ) { return; }
|
||||
// The comment, if any, will be applied if and only if it is different
|
||||
// from the last comment found in the user filter list.
|
||||
if ( comment !== '' ) {
|
||||
const pos = details.content.lastIndexOf(comment);
|
||||
if (
|
||||
pos === -1 ||
|
||||
details.content.indexOf('\n!', pos + 1) !== -1
|
||||
) {
|
||||
filters = '\n' + comment + '\n' + filters;
|
||||
}
|
||||
}
|
||||
// https://github.com/chrisaljoudi/uBlock/issues/976
|
||||
// If we reached this point, the filter quite probably needs to be
|
||||
// added for sure: do not try to be too smart, trying to avoid
|
||||
// duplicates at this point may lead to more issues.
|
||||
this.saveUserFilters(details.content.trim() + '\n' + filters, onSaved);
|
||||
};
|
||||
// https://github.com/chrisaljoudi/uBlock/issues/976
|
||||
// If we reached this point, the filter quite probably needs to be
|
||||
// added for sure: do not try to be too smart, trying to avoid
|
||||
// duplicates at this point may lead to more issues.
|
||||
await this.saveUserFilters(details.content.trim() + '\n' + filters);
|
||||
|
||||
this.loadUserFilters(onLoaded);
|
||||
const compiledFilters = this.compileFilters(
|
||||
filters,
|
||||
{ assetKey: this.userFiltersPath }
|
||||
);
|
||||
const snfe = this.staticNetFilteringEngine;
|
||||
const cfe = this.cosmeticFilteringEngine;
|
||||
const acceptedCount = snfe.acceptedCount + cfe.acceptedCount;
|
||||
const discardedCount = snfe.discardedCount + cfe.discardedCount;
|
||||
this.applyCompiledFilters(compiledFilters, true);
|
||||
const entry = this.availableFilterLists[this.userFiltersPath];
|
||||
const deltaEntryCount =
|
||||
snfe.acceptedCount +
|
||||
cfe.acceptedCount - acceptedCount;
|
||||
const deltaEntryUsedCount =
|
||||
deltaEntryCount -
|
||||
(snfe.discardedCount + cfe.discardedCount - discardedCount);
|
||||
entry.entryCount += deltaEntryCount;
|
||||
entry.entryUsedCount += deltaEntryUsedCount;
|
||||
vAPI.storage.set({ 'availableFilterLists': this.availableFilterLists });
|
||||
this.staticNetFilteringEngine.freeze();
|
||||
this.redirectEngine.freeze();
|
||||
this.staticExtFilteringEngine.freeze();
|
||||
this.selfieManager.destroy();
|
||||
|
||||
// https://www.reddit.com/r/uBlockOrigin/comments/cj7g7m/
|
||||
// https://www.reddit.com/r/uBlockOrigin/comments/cnq0bi/
|
||||
if ( options.killCache ) {
|
||||
browser.webRequest.handlerBehaviorChanged();
|
||||
}
|
||||
};
|
||||
|
||||
µBlock.createUserFilters = function(details) {
|
||||
|
@ -525,7 +495,7 @@
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
µBlock.getAvailableLists = function(callback) {
|
||||
µBlock.getAvailableLists = async function() {
|
||||
let oldAvailableLists = {},
|
||||
newAvailableLists = {};
|
||||
|
||||
|
@ -577,105 +547,87 @@
|
|||
this.saveSelectedFilterLists([ listURL ], true);
|
||||
};
|
||||
|
||||
// Load previously saved available lists -- these contains data
|
||||
// computed at run-time, we will reuse this data if possible.
|
||||
const [ bin, entries ] = await Promise.all([
|
||||
vAPI.storage.get('availableFilterLists'),
|
||||
this.assets.metadata(),
|
||||
]);
|
||||
|
||||
oldAvailableLists = bin && bin.availableFilterLists || {};
|
||||
|
||||
for ( const assetKey in entries ) {
|
||||
if ( entries.hasOwnProperty(assetKey) === false ) { continue; }
|
||||
const entry = entries[assetKey];
|
||||
if ( entry.content !== 'filters' ) { continue; }
|
||||
newAvailableLists[assetKey] = Object.assign({}, entry);
|
||||
}
|
||||
|
||||
// Load set of currently selected filter lists.
|
||||
const listKeySet = new Set(this.selectedFilterLists);
|
||||
for ( const listKey in newAvailableLists ) {
|
||||
if ( newAvailableLists.hasOwnProperty(listKey) ) {
|
||||
newAvailableLists[listKey].off = !listKeySet.has(listKey);
|
||||
}
|
||||
}
|
||||
|
||||
//finalize();
|
||||
// Final steps:
|
||||
// - reuse existing list metadata if any;
|
||||
// - unregister unreferenced imported filter lists if any.
|
||||
const finalize = ( ) => {
|
||||
// Reuse existing metadata.
|
||||
for ( const assetKey in oldAvailableLists ) {
|
||||
const oldEntry = oldAvailableLists[assetKey];
|
||||
const newEntry = newAvailableLists[assetKey];
|
||||
// List no longer exists. If a stock list, try to convert to
|
||||
// imported list if it was selected.
|
||||
if ( newEntry === undefined ) {
|
||||
this.removeFilterList(assetKey);
|
||||
if ( assetKey.indexOf('://') === -1 ) {
|
||||
customListFromStockList(assetKey);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if ( oldEntry.entryCount !== undefined ) {
|
||||
newEntry.entryCount = oldEntry.entryCount;
|
||||
}
|
||||
if ( oldEntry.entryUsedCount !== undefined ) {
|
||||
newEntry.entryUsedCount = oldEntry.entryUsedCount;
|
||||
}
|
||||
// This may happen if the list name was pulled from the list
|
||||
// content.
|
||||
// https://github.com/chrisaljoudi/uBlock/issues/982
|
||||
// There is no guarantee the title was successfully extracted from
|
||||
// the list content.
|
||||
if (
|
||||
newEntry.title === '' &&
|
||||
typeof oldEntry.title === 'string' &&
|
||||
oldEntry.title !== ''
|
||||
) {
|
||||
newEntry.title = oldEntry.title;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove unreferenced imported filter lists.
|
||||
const dict = new Set(importedListKeys);
|
||||
for ( const assetKey in newAvailableLists ) {
|
||||
const newEntry = newAvailableLists[assetKey];
|
||||
if ( newEntry.submitter !== 'user' ) { continue; }
|
||||
if ( dict.has(assetKey) ) { continue; }
|
||||
delete newAvailableLists[assetKey];
|
||||
this.assets.unregisterAssetSource(assetKey);
|
||||
// Reuse existing metadata.
|
||||
for ( const assetKey in oldAvailableLists ) {
|
||||
const oldEntry = oldAvailableLists[assetKey];
|
||||
const newEntry = newAvailableLists[assetKey];
|
||||
// List no longer exists. If a stock list, try to convert to
|
||||
// imported list if it was selected.
|
||||
if ( newEntry === undefined ) {
|
||||
this.removeFilterList(assetKey);
|
||||
}
|
||||
};
|
||||
|
||||
// Built-in filter lists loaded.
|
||||
const onBuiltinListsLoaded = entries => {
|
||||
for ( const assetKey in entries ) {
|
||||
if ( entries.hasOwnProperty(assetKey) === false ) { continue; }
|
||||
const entry = entries[assetKey];
|
||||
if ( entry.content !== 'filters' ) { continue; }
|
||||
newAvailableLists[assetKey] = Object.assign({}, entry);
|
||||
}
|
||||
|
||||
// Load set of currently selected filter lists.
|
||||
const listKeySet = new Set(this.selectedFilterLists);
|
||||
for ( const listKey in newAvailableLists ) {
|
||||
if ( newAvailableLists.hasOwnProperty(listKey) ) {
|
||||
newAvailableLists[listKey].off = !listKeySet.has(listKey);
|
||||
if ( assetKey.indexOf('://') === -1 ) {
|
||||
customListFromStockList(assetKey);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if ( oldEntry.entryCount !== undefined ) {
|
||||
newEntry.entryCount = oldEntry.entryCount;
|
||||
}
|
||||
if ( oldEntry.entryUsedCount !== undefined ) {
|
||||
newEntry.entryUsedCount = oldEntry.entryUsedCount;
|
||||
}
|
||||
// This may happen if the list name was pulled from the list
|
||||
// content.
|
||||
// https://github.com/chrisaljoudi/uBlock/issues/982
|
||||
// There is no guarantee the title was successfully extracted from
|
||||
// the list content.
|
||||
if (
|
||||
newEntry.title === '' &&
|
||||
typeof oldEntry.title === 'string' &&
|
||||
oldEntry.title !== ''
|
||||
) {
|
||||
newEntry.title = oldEntry.title;
|
||||
}
|
||||
}
|
||||
|
||||
finalize();
|
||||
callback(newAvailableLists);
|
||||
};
|
||||
// Remove unreferenced imported filter lists.
|
||||
for ( const assetKey in newAvailableLists ) {
|
||||
const newEntry = newAvailableLists[assetKey];
|
||||
if ( newEntry.submitter !== 'user' ) { continue; }
|
||||
if ( importedListKeys.indexOf(assetKey) !== -1 ) { continue; }
|
||||
delete newAvailableLists[assetKey];
|
||||
this.assets.unregisterAssetSource(assetKey);
|
||||
this.removeFilterList(assetKey);
|
||||
}
|
||||
|
||||
// Available lists previously computed.
|
||||
const onOldAvailableListsLoaded = bin => {
|
||||
oldAvailableLists = bin && bin.availableFilterLists || {};
|
||||
this.assets.metadata(onBuiltinListsLoaded);
|
||||
};
|
||||
|
||||
// Load previously saved available lists -- these contains data
|
||||
// computed at run-time, we will reuse this data if possible.
|
||||
vAPI.storage.get('availableFilterLists', onOldAvailableListsLoaded);
|
||||
return newAvailableLists;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// This is used to be re-entrancy resistant.
|
||||
µBlock.loadingFilterLists = false;
|
||||
|
||||
µBlock.loadFilterLists = function(callback) {
|
||||
// Callers are expected to check this first.
|
||||
if ( this.loadingFilterLists ) { return; }
|
||||
this.loadingFilterLists = true;
|
||||
|
||||
µBlock.loadFilterLists = (( ) => {
|
||||
const loadedListKeys = [];
|
||||
let filterlistsCount = 0;
|
||||
let loadingPromise;
|
||||
|
||||
if ( typeof callback !== 'function' ) {
|
||||
callback = this.noopFunc;
|
||||
}
|
||||
|
||||
const onDone = ( ) => {
|
||||
const onDone = function() {
|
||||
this.staticNetFilteringEngine.freeze();
|
||||
this.staticExtFilteringEngine.freeze();
|
||||
this.redirectEngine.freeze();
|
||||
|
@ -690,15 +642,13 @@
|
|||
listKeys: loadedListKeys
|
||||
});
|
||||
|
||||
callback();
|
||||
|
||||
this.selfieManager.destroy();
|
||||
this.lz4Codec.relinquish();
|
||||
|
||||
this.loadingFilterLists = false;
|
||||
loadingPromise = undefined;
|
||||
};
|
||||
|
||||
const applyCompiledFilters = (assetKey, compiled) => {
|
||||
const applyCompiledFilters = function(assetKey, compiled) {
|
||||
const snfe = this.staticNetFilteringEngine;
|
||||
const sxfe = this.staticExtFilteringEngine;
|
||||
let acceptedCount = snfe.acceptedCount + sxfe.acceptedCount,
|
||||
|
@ -714,15 +664,7 @@
|
|||
loadedListKeys.push(assetKey);
|
||||
};
|
||||
|
||||
const onCompiledListLoaded = details => {
|
||||
applyCompiledFilters(details.assetKey, details.content);
|
||||
filterlistsCount -= 1;
|
||||
if ( filterlistsCount === 0 ) {
|
||||
onDone();
|
||||
}
|
||||
};
|
||||
|
||||
const onFilterListsReady = lists => {
|
||||
const onFilterListsReady = function(lists) {
|
||||
this.availableFilterLists = lists;
|
||||
|
||||
vAPI.net.suspend();
|
||||
|
@ -740,66 +682,71 @@
|
|||
for ( const assetKey in lists ) {
|
||||
if ( lists.hasOwnProperty(assetKey) === false ) { continue; }
|
||||
if ( lists[assetKey].off ) { continue; }
|
||||
toLoad.push(assetKey);
|
||||
}
|
||||
filterlistsCount = toLoad.length;
|
||||
if ( filterlistsCount === 0 ) {
|
||||
return onDone();
|
||||
|
||||
toLoad.push(
|
||||
this.getCompiledFilterList(assetKey).then(details => {
|
||||
applyCompiledFilters.call(
|
||||
this,
|
||||
details.assetKey,
|
||||
details.content
|
||||
);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
let i = toLoad.length;
|
||||
while ( i-- ) {
|
||||
this.getCompiledFilterList(toLoad[i], onCompiledListLoaded);
|
||||
}
|
||||
return Promise.all(toLoad);
|
||||
};
|
||||
|
||||
this.getAvailableLists(onFilterListsReady);
|
||||
this.loadRedirectResources();
|
||||
};
|
||||
return function() {
|
||||
if ( loadingPromise instanceof Promise === false ) {
|
||||
loadedListKeys.length = 0;
|
||||
loadingPromise = Promise.all([
|
||||
this.getAvailableLists().then(lists => {
|
||||
return onFilterListsReady.call(this, lists);
|
||||
}),
|
||||
this.loadRedirectResources(),
|
||||
]).then(( ) => {
|
||||
onDone.call(this);
|
||||
});
|
||||
}
|
||||
return loadingPromise;
|
||||
};
|
||||
})();
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
µBlock.getCompiledFilterList = function(assetKey, callback) {
|
||||
µBlock.getCompiledFilterList = async function(assetKey) {
|
||||
const compiledPath = 'compiled/' + assetKey;
|
||||
let rawContent;
|
||||
|
||||
const onCompiledListLoaded2 = details => {
|
||||
if ( details.content === '' ) {
|
||||
details.content = this.compileFilters(
|
||||
rawContent,
|
||||
{ assetKey: assetKey }
|
||||
);
|
||||
this.assets.put(compiledPath, details.content);
|
||||
}
|
||||
rawContent = undefined;
|
||||
details.assetKey = assetKey;
|
||||
callback(details);
|
||||
};
|
||||
let compiledDetails = await this.assets.get(compiledPath);
|
||||
if ( compiledDetails.content !== '' ) {
|
||||
compiledDetails.assetKey = assetKey;
|
||||
return compiledDetails;
|
||||
}
|
||||
|
||||
const onRawListLoaded = details => {
|
||||
if ( details.content === '' ) {
|
||||
details.assetKey = assetKey;
|
||||
callback(details);
|
||||
return;
|
||||
}
|
||||
this.extractFilterListMetadata(assetKey, details.content);
|
||||
// Fectching the raw content may cause the compiled content to be
|
||||
// generated somewhere else in uBO, hence we try one last time to
|
||||
// fetch the compiled content in case it has become available.
|
||||
rawContent = details.content;
|
||||
this.assets.get(compiledPath, onCompiledListLoaded2);
|
||||
};
|
||||
const rawDetails = await this.assets.get(assetKey);
|
||||
// Compiling an empty string results in an empty string.
|
||||
if ( rawDetails.content === '' ) {
|
||||
rawDetails.assetKey = assetKey;
|
||||
return rawDetails;
|
||||
}
|
||||
|
||||
const onCompiledListLoaded1 = details => {
|
||||
if ( details.content === '' ) {
|
||||
this.assets.get(assetKey, onRawListLoaded);
|
||||
return;
|
||||
}
|
||||
details.assetKey = assetKey;
|
||||
callback(details);
|
||||
};
|
||||
this.extractFilterListMetadata(assetKey, rawDetails.content);
|
||||
|
||||
this.assets.get(compiledPath, onCompiledListLoaded1);
|
||||
// Fetching the raw content may cause the compiled content to be
|
||||
// generated somewhere else in uBO, hence we try one last time to
|
||||
// fetch the compiled content in case it has become available.
|
||||
compiledDetails = await this.assets.get(compiledPath);
|
||||
if ( compiledDetails.content === '' ) {
|
||||
compiledDetails.content = this.compileFilters(
|
||||
rawDetails.content,
|
||||
{ assetKey: assetKey }
|
||||
);
|
||||
this.assets.put(compiledPath, compiledDetails.content);
|
||||
}
|
||||
|
||||
compiledDetails.assetKey = assetKey;
|
||||
return compiledDetails;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
@ -851,7 +798,7 @@
|
|||
/******************************************************************************/
|
||||
|
||||
µBlock.compileFilters = function(rawText, details) {
|
||||
let writer = new this.CompiledLineIO.Writer();
|
||||
const writer = new this.CompiledLineIO.Writer();
|
||||
|
||||
// Populate the writer with information potentially useful to the
|
||||
// client compilers.
|
||||
|
@ -1007,8 +954,9 @@
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
µBlock.loadRedirectResources = function() {
|
||||
return this.redirectEngine.resourcesFromSelfie().then(success => {
|
||||
µBlock.loadRedirectResources = async function() {
|
||||
try {
|
||||
const success = await this.redirectEngine.resourcesFromSelfie();
|
||||
if ( success === true ) { return true; }
|
||||
|
||||
const fetchPromises = [
|
||||
|
@ -1022,8 +970,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
return Promise.all(fetchPromises);
|
||||
}).then(results => {
|
||||
const results = await Promise.all(fetchPromises);
|
||||
if ( Array.isArray(results) === false ) { return results; }
|
||||
|
||||
let content = '';
|
||||
|
@ -1041,35 +988,34 @@
|
|||
|
||||
this.redirectEngine.resourcesFromString(content);
|
||||
this.redirectEngine.selfieFromResources();
|
||||
return true;
|
||||
}).catch(reason => {
|
||||
log.info(reason);
|
||||
} catch(ex) {
|
||||
log.info(ex);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
µBlock.loadPublicSuffixList = function() {
|
||||
µBlock.loadPublicSuffixList = async function() {
|
||||
if ( this.hiddenSettings.disableWebAssembly === false ) {
|
||||
publicSuffixList.enableWASM();
|
||||
}
|
||||
|
||||
return this.assets.get(
|
||||
'compiled/' + this.pslAssetKey
|
||||
).then(details =>
|
||||
publicSuffixList.fromSelfie(details.content, µBlock.base64)
|
||||
).catch(reason => {
|
||||
console.info(reason);
|
||||
return false;
|
||||
}).then(success => {
|
||||
if ( success ) { return; }
|
||||
return this.assets.get(this.pslAssetKey, details => {
|
||||
if ( details.content !== '' ) {
|
||||
this.compilePublicSuffixList(details.content);
|
||||
}
|
||||
});
|
||||
});
|
||||
try {
|
||||
const result = await this.assets.get(`compiled/${this.pslAssetKey}`);
|
||||
if ( publicSuffixList.fromSelfie(result.content, this.base64) ) {
|
||||
return;
|
||||
}
|
||||
} catch (ex) {
|
||||
console.error(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await this.assets.get(this.pslAssetKey);
|
||||
if ( result.content !== '' ) {
|
||||
this.compilePublicSuffixList(result.content);
|
||||
}
|
||||
};
|
||||
|
||||
µBlock.compilePublicSuffixList = function(content) {
|
||||
|
@ -1111,7 +1057,7 @@
|
|||
});
|
||||
};
|
||||
|
||||
const load = function() {
|
||||
const load = async function() {
|
||||
return Promise.all([
|
||||
µb.assets.get('selfie/main').then(details => {
|
||||
if (
|
||||
|
@ -1162,10 +1108,7 @@
|
|||
}, µb.hiddenSettings.selfieAfter * 60000);
|
||||
};
|
||||
|
||||
return {
|
||||
load: load,
|
||||
destroy: destroy
|
||||
};
|
||||
return { load, destroy };
|
||||
})();
|
||||
|
||||
/******************************************************************************/
|
||||
|
@ -1177,97 +1120,82 @@
|
|||
// necessarily present, i.e. administrators may removed entries which
|
||||
// values are left to the user's choice.
|
||||
|
||||
µBlock.restoreAdminSettings = function() {
|
||||
return new Promise(resolve => {
|
||||
// >>>> start of executor
|
||||
|
||||
if ( vAPI.adminStorage instanceof Object === false ) {
|
||||
return resolve();
|
||||
µBlock.restoreAdminSettings = async function() {
|
||||
let data;
|
||||
try {
|
||||
const json = await vAPI.adminStorage.getItem('adminSettings');
|
||||
if ( typeof json === 'string' && json !== '' ) {
|
||||
data = JSON.parse(json);
|
||||
}
|
||||
} catch (ex) {
|
||||
console.error(ex);
|
||||
}
|
||||
|
||||
vAPI.adminStorage.getItem('adminSettings', json => {
|
||||
let data;
|
||||
if ( typeof json === 'string' && json !== '' ) {
|
||||
try {
|
||||
data = JSON.parse(json);
|
||||
} catch (ex) {
|
||||
console.error(ex);
|
||||
if ( data instanceof Object === false ) { return; }
|
||||
|
||||
const bin = {};
|
||||
let binNotEmpty = false;
|
||||
|
||||
// https://github.com/uBlockOrigin/uBlock-issues/issues/666
|
||||
// Allows an admin to set their own 'assets.json' file, with their
|
||||
// own set of stock assets.
|
||||
if (
|
||||
typeof data.assetsBootstrapLocation === 'string' &&
|
||||
data.assetsBootstrapLocation !== ''
|
||||
) {
|
||||
µBlock.assetsBootstrapLocation = data.assetsBootstrapLocation;
|
||||
}
|
||||
|
||||
if ( typeof data.userSettings === 'object' ) {
|
||||
for ( const name in this.userSettings ) {
|
||||
if ( this.userSettings.hasOwnProperty(name) === false ) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ( data instanceof Object === false ) {
|
||||
return resolve();
|
||||
}
|
||||
|
||||
const bin = {};
|
||||
let binNotEmpty = false;
|
||||
|
||||
// https://github.com/uBlockOrigin/uBlock-issues/issues/666
|
||||
// Allows an admin to set their own 'assets.json' file, with their
|
||||
// own set of stock assets.
|
||||
if (
|
||||
typeof data.assetsBootstrapLocation === 'string' &&
|
||||
data.assetsBootstrapLocation !== ''
|
||||
) {
|
||||
µBlock.assetsBootstrapLocation = data.assetsBootstrapLocation;
|
||||
}
|
||||
|
||||
if ( typeof data.userSettings === 'object' ) {
|
||||
for ( const name in this.userSettings ) {
|
||||
if ( this.userSettings.hasOwnProperty(name) === false ) {
|
||||
continue;
|
||||
}
|
||||
if ( data.userSettings.hasOwnProperty(name) === false ) {
|
||||
continue;
|
||||
}
|
||||
bin[name] = data.userSettings[name];
|
||||
binNotEmpty = true;
|
||||
if ( data.userSettings.hasOwnProperty(name) === false ) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// 'selectedFilterLists' is an array of filter list tokens. Each token
|
||||
// is a reference to an asset in 'assets.json'.
|
||||
if ( Array.isArray(data.selectedFilterLists) ) {
|
||||
bin.selectedFilterLists = data.selectedFilterLists;
|
||||
bin[name] = data.userSettings[name];
|
||||
binNotEmpty = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( Array.isArray(data.whitelist) ) {
|
||||
bin.netWhitelist = data.whitelist;
|
||||
binNotEmpty = true;
|
||||
} else if ( typeof data.netWhitelist === 'string' ) {
|
||||
bin.netWhitelist = data.netWhitelist.split('\n');
|
||||
binNotEmpty = true;
|
||||
}
|
||||
// 'selectedFilterLists' is an array of filter list tokens. Each token
|
||||
// is a reference to an asset in 'assets.json'.
|
||||
if ( Array.isArray(data.selectedFilterLists) ) {
|
||||
bin.selectedFilterLists = data.selectedFilterLists;
|
||||
binNotEmpty = true;
|
||||
}
|
||||
|
||||
if ( typeof data.dynamicFilteringString === 'string' ) {
|
||||
bin.dynamicFilteringString = data.dynamicFilteringString;
|
||||
binNotEmpty = true;
|
||||
}
|
||||
if ( Array.isArray(data.whitelist) ) {
|
||||
bin.netWhitelist = data.whitelist;
|
||||
binNotEmpty = true;
|
||||
} else if ( typeof data.netWhitelist === 'string' ) {
|
||||
bin.netWhitelist = data.netWhitelist.split('\n');
|
||||
binNotEmpty = true;
|
||||
}
|
||||
|
||||
if ( typeof data.urlFilteringString === 'string' ) {
|
||||
bin.urlFilteringString = data.urlFilteringString;
|
||||
binNotEmpty = true;
|
||||
}
|
||||
if ( typeof data.dynamicFilteringString === 'string' ) {
|
||||
bin.dynamicFilteringString = data.dynamicFilteringString;
|
||||
binNotEmpty = true;
|
||||
}
|
||||
|
||||
if ( typeof data.hostnameSwitchesString === 'string' ) {
|
||||
bin.hostnameSwitchesString = data.hostnameSwitchesString;
|
||||
binNotEmpty = true;
|
||||
}
|
||||
if ( typeof data.urlFilteringString === 'string' ) {
|
||||
bin.urlFilteringString = data.urlFilteringString;
|
||||
binNotEmpty = true;
|
||||
}
|
||||
|
||||
if ( binNotEmpty ) {
|
||||
vAPI.storage.set(bin);
|
||||
}
|
||||
if ( typeof data.hostnameSwitchesString === 'string' ) {
|
||||
bin.hostnameSwitchesString = data.hostnameSwitchesString;
|
||||
binNotEmpty = true;
|
||||
}
|
||||
|
||||
if ( typeof data.userFilters === 'string' ) {
|
||||
this.assets.put(this.userFiltersPath, data.userFilters);
|
||||
}
|
||||
if ( binNotEmpty ) {
|
||||
vAPI.storage.set(bin);
|
||||
}
|
||||
|
||||
resolve();
|
||||
});
|
||||
|
||||
// <<<< end of executor
|
||||
});
|
||||
if ( typeof data.userFilters === 'string' ) {
|
||||
this.saveUserFilters(data.userFilters);
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
|
Loading…
Reference in a new issue