Raymond Hill 2018-12-05 19:18:20 -05:00
parent 0d6a18207a
commit e7d4aff2a2
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
4 changed files with 110 additions and 36 deletions

View File

@ -40,19 +40,28 @@
µBlock.cacheStorage = (function() { µBlock.cacheStorage = (function() {
// Firefox-specific: we use indexedDB because chrome.storage.local() has const STORAGE_NAME = 'uBlock0CacheStorage';
// poor performance in Firefox. See:
// https://bugzilla.mozilla.org/show_bug.cgi?id=1371255 // https://bugzilla.mozilla.org/show_bug.cgi?id=1371255
if ( vAPI.webextFlavor.soup.has('firefox') === false ) { // Firefox-specific: we use indexedDB because chrome.storage.local() has
// poor performance in Firefox.
// https://github.com/uBlockOrigin/uBlock-issues/issues/328
// Use IndexedDB for Chromium as well, to take advantage of LZ4
// compression.
if (
vAPI.webextFlavor.soup.has('firefox') === false &&
vAPI.webextFlavor.soup.has('chromium') === false
) {
// In case IndexedDB was used as cache storage, remove it.
indexedDB.deleteDatabase(STORAGE_NAME);
return vAPI.cacheStorage; return vAPI.cacheStorage;
} }
const STORAGE_NAME = 'uBlock0CacheStorage';
let db; let db;
let pendingInitialization; let pendingInitialization;
let dbByteLength; let dbByteLength;
let get = function get(input, callback) { const get = function get(input, callback) {
if ( typeof callback !== 'function' ) { return; } if ( typeof callback !== 'function' ) { return; }
if ( input === null ) { if ( input === null ) {
return getAllFromDb(callback); return getAllFromDb(callback);
@ -69,23 +78,23 @@
return getFromDb(toRead, output, callback); return getFromDb(toRead, output, callback);
}; };
let set = function set(input, callback) { const set = function set(input, callback) {
putToDb(input, callback); putToDb(input, callback);
}; };
let remove = function remove(key, callback) { const remove = function remove(key, callback) {
deleteFromDb(key, callback); deleteFromDb(key, callback);
}; };
let clear = function clear(callback) { const clear = function clear(callback) {
clearDb(callback); clearDb(callback);
}; };
let getBytesInUse = function getBytesInUse(keys, callback) { const getBytesInUse = function getBytesInUse(keys, callback) {
getDbSize(callback); getDbSize(callback);
}; };
let api = { const api = {
get, get,
set, set,
remove, remove,
@ -94,7 +103,7 @@
error: undefined error: undefined
}; };
let genericErrorHandler = function(ev) { const genericErrorHandler = function(ev) {
let error = ev.target && ev.target.error; let error = ev.target && ev.target.error;
if ( error && error.name === 'QuotaExceededError' ) { if ( error && error.name === 'QuotaExceededError' ) {
api.error = error.name; api.error = error.name;
@ -102,10 +111,10 @@
console.error('[%s]', STORAGE_NAME, error && error.name); console.error('[%s]', STORAGE_NAME, error && error.name);
}; };
function noopfn() { const noopfn = function () {
} };
let getDb = function getDb() { const getDb = function getDb() {
if ( db instanceof IDBDatabase ) { if ( db instanceof IDBDatabase ) {
return Promise.resolve(db); return Promise.resolve(db);
} }
@ -165,7 +174,7 @@
return pendingInitialization; return pendingInitialization;
}; };
let getFromDb = function(keys, keyvalStore, callback) { const getFromDb = function(keys, keyvalStore, callback) {
if ( typeof callback !== 'function' ) { return; } if ( typeof callback !== 'function' ) { return; }
if ( keys.length === 0 ) { return callback(keyvalStore); } if ( keys.length === 0 ) { return callback(keyvalStore); }
let promises = []; let promises = [];
@ -202,7 +211,7 @@
}); });
}; };
let visitAllFromDb = function(visitFn) { const visitAllFromDb = function(visitFn) {
getDb().then(( ) => { getDb().then(( ) => {
if ( !db ) { return visitFn(); } if ( !db ) { return visitFn(); }
let transaction = db.transaction(STORAGE_NAME); let transaction = db.transaction(STORAGE_NAME);
@ -221,7 +230,7 @@
}); });
}; };
let getAllFromDb = function(callback) { const getAllFromDb = function(callback) {
if ( typeof callback !== 'function' ) { return; } if ( typeof callback !== 'function' ) { return; }
let promises = []; let promises = [];
let keyvalStore = {}; let keyvalStore = {};
@ -245,7 +254,7 @@
}); });
}; };
let getDbSize = function(callback) { const getDbSize = function(callback) {
if ( typeof callback !== 'function' ) { return; } if ( typeof callback !== 'function' ) { return; }
if ( typeof dbByteLength === 'number' ) { if ( typeof dbByteLength === 'number' ) {
return Promise.resolve().then(( ) => { return Promise.resolve().then(( ) => {
@ -280,7 +289,7 @@
// https://developer.mozilla.org/en-US/docs/Web/API/IDBDatabase/transaction // https://developer.mozilla.org/en-US/docs/Web/API/IDBDatabase/transaction
// https://developer.mozilla.org/en-US/docs/Web/API/IDBObjectStore/put // https://developer.mozilla.org/en-US/docs/Web/API/IDBObjectStore/put
let putToDb = function(keyvalStore, callback) { const putToDb = function(keyvalStore, callback) {
if ( typeof callback !== 'function' ) { if ( typeof callback !== 'function' ) {
callback = noopfn; callback = noopfn;
} }
@ -326,7 +335,7 @@
}); });
}; };
let deleteFromDb = function(input, callback) { const deleteFromDb = function(input, callback) {
if ( typeof callback !== 'function' ) { if ( typeof callback !== 'function' ) {
callback = noopfn; callback = noopfn;
} }
@ -356,7 +365,7 @@
}); });
}; };
let clearDb = function(callback) { const clearDb = function(callback) {
if ( typeof callback !== 'function' ) { if ( typeof callback !== 'function' ) {
callback = noopfn; callback = noopfn;
} }
@ -383,6 +392,57 @@
// prime the db so that it's ready asap for next access. // prime the db so that it's ready asap for next access.
getDb(noopfn); getDb(noopfn);
// https://github.com/uBlockOrigin/uBlock-issues/issues/328
// Detect whether browser.storage.local was used as cache storage,
// and if so, move cache-related entries to the new storage.
{
const srcStorage = vAPI.cacheStorage;
const desStorage = api;
srcStorage.get(
[ 'assetCacheRegistry', 'assetSourceRegistry' ],
bin => {
if (
bin instanceof Object === false ||
bin.assetSourceRegistry instanceof Object === false
) {
return;
}
desStorage.set(bin);
const toRemove = [
'assetCacheRegistry',
'assetSourceRegistry',
'resourcesSelfie',
'selfie'
];
let toMigrate = 0;
const setEntry = function(assetKey, bin) {
if (
bin instanceof Object &&
bin[assetKey] !== undefined
) {
desStorage.set(bin);
}
toMigrate -= 1;
if ( toMigrate === 0 ) {
srcStorage.remove(toRemove);
}
};
for ( const key in bin.assetCacheRegistry ) {
if ( bin.assetCacheRegistry.hasOwnProperty(key) === false ) {
continue;
}
const assetKey = 'cache/' + key;
srcStorage.get(assetKey, setEntry.bind(null, assetKey));
toMigrate += 1;
toRemove.push(assetKey);
}
if ( toMigrate === 0 ) {
srcStorage.remove(toRemove);
}
}
);
}
return api; return api;
}()); }());

View File

@ -198,7 +198,11 @@ return {
data: decodeValue(result.key, result.data) || result.data data: decodeValue(result.key, result.data) || result.data
}; };
}); });
} },
relinquish: function() {
ttlDelay = 1;
ttlManage(0);
},
}; };
/******************************************************************************/ /******************************************************************************/

View File

@ -53,6 +53,13 @@ vAPI.app.onShutdown = function() {
// - Schedule next update operation. // - Schedule next update operation.
var onAllReady = function() { var onAllReady = function() {
// 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();
µb.webRequest.start(); µb.webRequest.start();
initializeTabs(); initializeTabs();

View File

@ -608,15 +608,15 @@
if ( this.loadingFilterLists ) { return; } if ( this.loadingFilterLists ) { return; }
this.loadingFilterLists = true; this.loadingFilterLists = true;
var µb = this, const µb = µBlock;
filterlistsCount = 0, const loadedListKeys = [];
loadedListKeys = []; let filterlistsCount = 0;
if ( typeof callback !== 'function' ) { if ( typeof callback !== 'function' ) {
callback = this.noopFunc; callback = this.noopFunc;
} }
var onDone = function() { const onDone = function() {
µb.staticNetFilteringEngine.freeze(); µb.staticNetFilteringEngine.freeze();
µb.staticExtFilteringEngine.freeze(); µb.staticExtFilteringEngine.freeze();
µb.redirectEngine.freeze(); µb.redirectEngine.freeze();
@ -632,17 +632,19 @@
callback(); callback();
µb.selfieManager.destroy(); µb.selfieManager.destroy();
µb.lz4Codec.relinquish();
µb.loadingFilterLists = false; µb.loadingFilterLists = false;
}; };
var applyCompiledFilters = function(assetKey, compiled) { const applyCompiledFilters = function(assetKey, compiled) {
var snfe = µb.staticNetFilteringEngine, const snfe = µb.staticNetFilteringEngine;
sxfe = µb.staticExtFilteringEngine, const sxfe = µb.staticExtFilteringEngine;
acceptedCount = snfe.acceptedCount + sxfe.acceptedCount, let acceptedCount = snfe.acceptedCount + sxfe.acceptedCount,
discardedCount = snfe.discardedCount + sxfe.discardedCount; discardedCount = snfe.discardedCount + sxfe.discardedCount;
µb.applyCompiledFilters(compiled, assetKey === µb.userFiltersPath); µb.applyCompiledFilters(compiled, assetKey === µb.userFiltersPath);
if ( µb.availableFilterLists.hasOwnProperty(assetKey) ) { if ( µb.availableFilterLists.hasOwnProperty(assetKey) ) {
var entry = µb.availableFilterLists[assetKey]; const entry = µb.availableFilterLists[assetKey];
entry.entryCount = snfe.acceptedCount + sxfe.acceptedCount - entry.entryCount = snfe.acceptedCount + sxfe.acceptedCount -
acceptedCount; acceptedCount;
entry.entryUsedCount = entry.entryCount - entry.entryUsedCount = entry.entryCount -
@ -651,7 +653,7 @@
loadedListKeys.push(assetKey); loadedListKeys.push(assetKey);
}; };
var onCompiledListLoaded = function(details) { const onCompiledListLoaded = function(details) {
applyCompiledFilters(details.assetKey, details.content); applyCompiledFilters(details.assetKey, details.content);
filterlistsCount -= 1; filterlistsCount -= 1;
if ( filterlistsCount === 0 ) { if ( filterlistsCount === 0 ) {
@ -659,7 +661,7 @@
} }
}; };
var onFilterListsReady = function(lists) { const onFilterListsReady = function(lists) {
µb.availableFilterLists = lists; µb.availableFilterLists = lists;
µb.redirectEngine.reset(); µb.redirectEngine.reset();
@ -672,8 +674,8 @@
// because it *may* happens that some load operations are synchronous: // 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, ot assets with no
// content. // content.
var toLoad = []; const toLoad = [];
for ( var assetKey in lists ) { for ( const assetKey in lists ) {
if ( lists.hasOwnProperty(assetKey) === false ) { continue; } if ( lists.hasOwnProperty(assetKey) === false ) { continue; }
if ( lists[assetKey].off ) { continue; } if ( lists[assetKey].off ) { continue; }
toLoad.push(assetKey); toLoad.push(assetKey);
@ -683,7 +685,7 @@
return onDone(); return onDone();
} }
var i = toLoad.length; let i = toLoad.length;
while ( i-- ) { while ( i-- ) {
µb.getCompiledFilterList(toLoad[i], onCompiledListLoaded); µb.getCompiledFilterList(toLoad[i], onCompiledListLoaded);
} }
@ -1021,6 +1023,7 @@
staticExtFilteringEngine: µb.staticExtFilteringEngine.toSelfie() staticExtFilteringEngine: µb.staticExtFilteringEngine.toSelfie()
}); });
µb.cacheStorage.set({ selfie: selfie }); µb.cacheStorage.set({ selfie: selfie });
µb.lz4Codec.relinquish();
}; };
let load = function(callback) { let load = function(callback) {