1
0
mirror of https://github.com/gorhill/uBlock.git synced 2024-09-15 07:22:28 +02:00

added optional lz4 compression for cache storage (https://github.com/uBlockOrigin/uBlock-issues/issues/141)

Squashed commit of the following:

commit 6a8473822537636ac54d5dabdb14472114bb730b
Author: Raymond Hill <rhill@raymondhill.net>
Date:   Mon Aug 6 10:56:44 2018 -0400

    remove remnant of snappyjs and spurious instruction

commit 9a4b709bee97d3cc2235fab602359fa5953bdb46
Author: Raymond Hill <rhill@raymondhill.net>
Date:   Mon Aug 6 09:48:58 2018 -0400

    make cache storage compression optionally available on all platforms

    New advanced setting: `cacheStorageCompression`. Default is `false`.

commit 22ee6547f2f7c9c5aefe25dea1262a1b31612155
Author: Raymond Hill <rhill@raymondhill.net>
Date:   Sun Aug 5 19:16:26 2018 -0400

    remove Chromium from lz4 experiment

commit ee3e201c45afe983508f70713a2d43af74737d8d
Author: Raymond Hill <rhill@raymondhill.net>
Date:   Sun Aug 5 18:52:43 2018 -0400

    import lz4-block-codec.wasm library

commit 883a3118efcfd749c82356fde7134754d6ae371d
Author: Raymond Hill <rhill@raymondhill.net>
Date:   Sun Aug 5 18:50:46 2018 -0400

    implement storage compression through lz4-wasm [draft]

commit 48d1ccaba407de447c2cd6747dc3a90839c260a7
Merge: 8ae77e6 b34c897
Author: Raymond Hill <rhill@raymondhill.net>
Date:   Sat Aug 4 08:56:51 2018 -0400

    Merge branch 'master' of github.com:gorhill/uBlock into lz4

commit 8ae77e6aeeaa85af335e664c2560d2afd37288c6
Author: Raymond Hill <rhill@raymondhill.net>
Date:   Wed Jul 25 18:17:45 2018 -0400

    experiment with compression
This commit is contained in:
Raymond Hill 2018-08-06 12:34:41 -04:00
parent b34c897553
commit e163080518
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
5 changed files with 438 additions and 156 deletions

View File

@ -1,7 +1,7 @@
/******************************************************************************* /*******************************************************************************
uBlock Origin - a browser extension to block requests. uBlock Origin - a browser extension to block requests.
Copyright (C) 2016-2018 The uBlock Origin authors Copyright (C) 2016-present The uBlock Origin authors
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -19,7 +19,7 @@
Home: https://github.com/gorhill/uBlock Home: https://github.com/gorhill/uBlock
*/ */
/* global indexedDB, IDBDatabase */ /* global IDBDatabase, indexedDB */
'use strict'; 'use strict';
@ -48,15 +48,10 @@ vAPI.cacheStorage = (function() {
} }
const STORAGE_NAME = 'uBlock0CacheStorage'; const STORAGE_NAME = 'uBlock0CacheStorage';
var db; let db;
var pending = []; let pendingInitialization;
// prime the db so that it's ready asap for next access. let get = function get(input, callback) {
getDb(noopfn);
return { get, set, remove, clear, getBytesInUse };
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);
@ -71,51 +66,48 @@ vAPI.cacheStorage = (function() {
output = input; output = input;
} }
return getFromDb(toRead, output, callback); return getFromDb(toRead, output, callback);
} };
function set(input, callback) { let set = function set(input, callback) {
putToDb(input, callback); putToDb(input, callback);
} };
function remove(key, callback) { let remove = function remove(key, callback) {
deleteFromDb(key, callback); deleteFromDb(key, callback);
} };
function clear(callback) { let clear = function clear(callback) {
clearDb(callback); clearDb(callback);
} };
function getBytesInUse(keys, callback) { let getBytesInUse = function getBytesInUse(keys, callback) {
// TODO: implement this // TODO: implement this
callback(0); callback(0);
} };
function genericErrorHandler(error) { let api = { get, set, remove, clear, getBytesInUse, error: undefined };
console.error('[%s]', STORAGE_NAME, error);
} let genericErrorHandler = function(ev) {
let error = ev.target && ev.target.error;
if ( error && error.name === 'QuotaExceededError' ) {
api.error = error.name;
}
console.error('[%s]', STORAGE_NAME, error && error.name);
};
function noopfn() { function noopfn() {
} }
function processPendings() { let getDb = function getDb() {
var cb;
while ( (cb = pending.shift()) ) {
cb(db);
}
}
function getDb(callback) {
if ( pending === undefined ) {
return callback();
}
if ( pending.length !== 0 ) {
return pending.push(callback);
}
if ( db instanceof IDBDatabase ) { if ( db instanceof IDBDatabase ) {
return callback(db); return Promise.resolve(db);
}
if ( db === null ) {
return Promise.resolve(null);
}
if ( pendingInitialization !== undefined ) {
return pendingInitialization;
} }
pending.push(callback);
if ( pending.length !== 1 ) { return; }
// https://github.com/gorhill/uBlock/issues/3156 // https://github.com/gorhill/uBlock/issues/3156
// I have observed that no event was fired in Tor Browser 7.0.7 + // 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 // medium security level after the request to open the database was
@ -125,90 +117,92 @@ vAPI.cacheStorage = (function() {
// necessary when reading the `error` property because we are not // necessary when reading the `error` property because we are not
// allowed to read this propery outside of event handlers in newer // allowed to read this propery outside of event handlers in newer
// implementation of IDBRequest (my understanding). // implementation of IDBRequest (my understanding).
var req; pendingInitialization = new Promise(resolve => {
try { let req;
req = indexedDB.open(STORAGE_NAME, 1); try {
if ( req.error ) { req = indexedDB.open(STORAGE_NAME, 1);
console.log(req.error); if ( req.error ) {
console.log(req.error);
req = undefined;
}
} catch(ex) {
}
if ( req === undefined ) {
pendingInitialization = undefined;
db = null;
resolve(null);
return;
}
req.onupgradeneeded = function(ev) {
req = undefined; req = undefined;
} let db = ev.target.result;
} catch(ex) { db.onerror = db.onabort = genericErrorHandler;
} let table = db.createObjectStore(STORAGE_NAME, { keyPath: 'key' });
if ( req === undefined ) { table.createIndex('value', 'value', { unique: false });
processPendings(); };
pending = undefined; req.onsuccess = function(ev) {
return; pendingInitialization = undefined;
} req = undefined;
req.onupgradeneeded = function(ev) { db = ev.target.result;
req = undefined; db.onerror = db.onabort = genericErrorHandler;
db = ev.target.result; resolve(db);
db.onerror = db.onabort = genericErrorHandler; };
var table = db.createObjectStore(STORAGE_NAME, { keyPath: 'key' }); req.onerror = req.onblocked = function() {
table.createIndex('value', 'value', { unique: false }); pendingInitialization = undefined;
}; req = undefined;
req.onsuccess = function(ev) { db = null;
req = undefined; console.log(this.error);
db = ev.target.result; resolve(null);
db.onerror = db.onabort = genericErrorHandler; };
processPendings(); });
}; return pendingInitialization;
req.onerror = req.onblocked = function() { };
req = undefined;
console.log(this.error);
processPendings();
pending = undefined;
};
}
function getFromDb(keys, store, callback) { let getFromDb = function(keys, keystore, callback) {
if ( typeof callback !== 'function' ) { return; } if ( typeof callback !== 'function' ) { return; }
if ( keys.length === 0 ) { return callback(store); } if ( keys.length === 0 ) { return callback(keystore); }
var gotOne = function() { let gotOne = function() {
if ( typeof this.result === 'object' ) { if ( typeof this.result === 'object' ) {
store[this.result.key] = this.result.value; keystore[this.result.key] = this.result.value;
} }
}; };
getDb(function(db) { getDb().then(( ) => {
if ( !db ) { return callback(); } if ( !db ) { return callback(); }
var transaction = db.transaction(STORAGE_NAME); let transaction = db.transaction(STORAGE_NAME);
transaction.oncomplete = transaction.oncomplete =
transaction.onerror = transaction.onerror =
transaction.onabort = function() { transaction.onabort = ( ) => callback(keystore);
return callback(store); let table = transaction.objectStore(STORAGE_NAME);
}; for ( let key of keys ) {
var table = transaction.objectStore(STORAGE_NAME); let req = table.get(key);
for ( var key of keys ) {
var req = table.get(key);
req.onsuccess = gotOne; req.onsuccess = gotOne;
req.onerror = noopfn; req.onerror = noopfn;
req = undefined; req = undefined;
} }
}); });
} };
function getAllFromDb(callback) { let getAllFromDb = function(callback) {
if ( typeof callback !== 'function' ) { if ( typeof callback !== 'function' ) {
callback = noopfn; callback = noopfn;
} }
getDb(function(db) { getDb().then(( ) => {
if ( !db ) { return callback(); } if ( !db ) { return callback(); }
var output = {}; let keystore = {};
var transaction = db.transaction(STORAGE_NAME); let transaction = db.transaction(STORAGE_NAME);
transaction.oncomplete = transaction.oncomplete =
transaction.onerror = transaction.onerror =
transaction.onabort = function() { transaction.onabort = ( ) => callback(keystore);
callback(output); let table = transaction.objectStore(STORAGE_NAME),
};
var table = transaction.objectStore(STORAGE_NAME),
req = table.openCursor(); req = table.openCursor();
req.onsuccess = function(ev) { req.onsuccess = function(ev) {
var cursor = ev.target.result; let cursor = ev.target.result;
if ( !cursor ) { return; } if ( !cursor ) { return; }
output[cursor.key] = cursor.value; keystore[cursor.key] = cursor.value;
cursor.continue(); cursor.continue();
}; };
}); });
} };
// https://github.com/uBlockOrigin/uBlock-issues/issues/141 // https://github.com/uBlockOrigin/uBlock-issues/issues/141
// Mind that IDBDatabase.transaction() and IDBObjectStore.put() // Mind that IDBDatabase.transaction() and IDBObjectStore.put()
@ -216,20 +210,19 @@ vAPI.cacheStorage = (function() {
// 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
function putToDb(input, callback) { let putToDb = function(keystore, callback) {
if ( typeof callback !== 'function' ) { if ( typeof callback !== 'function' ) {
callback = noopfn; callback = noopfn;
} }
let keys = Object.keys(input); let keys = Object.keys(keystore);
if ( keys.length === 0 ) { return callback(); } if ( keys.length === 0 ) { return callback(); }
getDb(function(db) { getDb().then(( ) => {
if ( !db ) { return callback(); } if ( !db ) { return callback(); }
let finish = () => { let finish = ( ) => {
if ( callback !== undefined ) { if ( callback === undefined ) { return; }
let cb = callback; let cb = callback;
callback = undefined; callback = undefined;
cb(); cb();
}
}; };
try { try {
let transaction = db.transaction(STORAGE_NAME, 'readwrite'); let transaction = db.transaction(STORAGE_NAME, 'readwrite');
@ -240,7 +233,7 @@ vAPI.cacheStorage = (function() {
for ( let key of keys ) { for ( let key of keys ) {
let entry = {}; let entry = {};
entry.key = key; entry.key = key;
entry.value = input[key]; entry.value = keystore[key];
table.put(entry); table.put(entry);
entry = undefined; entry = undefined;
} }
@ -248,39 +241,64 @@ vAPI.cacheStorage = (function() {
finish(); finish();
} }
}); });
} };
function deleteFromDb(input, callback) { let deleteFromDb = function(input, callback) {
if ( typeof callback !== 'function' ) { if ( typeof callback !== 'function' ) {
callback = noopfn; callback = noopfn;
} }
var keys = Array.isArray(input) ? input.slice() : [ input ]; let keys = Array.isArray(input) ? input.slice() : [ input ];
if ( keys.length === 0 ) { return callback(); } if ( keys.length === 0 ) { return callback(); }
getDb(function(db) { getDb().then(db => {
if ( !db ) { return callback(); } if ( !db ) { return callback(); }
var transaction = db.transaction(STORAGE_NAME, 'readwrite'); let finish = ( ) => {
transaction.oncomplete = if ( callback === undefined ) { return; }
transaction.onerror = let cb = callback;
transaction.onabort = callback; callback = undefined;
var table = transaction.objectStore(STORAGE_NAME); cb();
for ( var key of keys ) { };
table.delete(key); try {
let transaction = db.transaction(STORAGE_NAME, 'readwrite');
transaction.oncomplete =
transaction.onerror =
transaction.onabort = finish;
let table = transaction.objectStore(STORAGE_NAME);
for ( let key of keys ) {
table.delete(key);
}
} catch (ex) {
finish();
} }
}); });
} };
function clearDb(callback) { let clearDb = function(callback) {
if ( typeof callback !== 'function' ) { if ( typeof callback !== 'function' ) {
callback = noopfn; callback = noopfn;
} }
getDb(function(db) { getDb().then(db => {
if ( !db ) { return callback(); } if ( !db ) { return callback(); }
var req = db.transaction(STORAGE_NAME, 'readwrite') let finish = ( ) => {
.objectStore(STORAGE_NAME) if ( callback === undefined ) { return; }
.clear(); let cb = callback;
req.onsuccess = req.onerror = callback; callback = undefined;
cb();
};
try {
let req = db.transaction(STORAGE_NAME, 'readwrite')
.objectStore(STORAGE_NAME)
.clear();
req.onsuccess = req.onerror = finish;
} catch (ex) {
finish();
}
}); });
} };
// prime the db so that it's ready asap for next access.
getDb(noopfn);
return api;
}()); }());
/******************************************************************************/ /******************************************************************************/

View File

@ -19,6 +19,8 @@
Home: https://github.com/gorhill/uBlock Home: https://github.com/gorhill/uBlock
*/ */
/* global WebAssembly */
'use strict'; 'use strict';
/******************************************************************************/ /******************************************************************************/
@ -409,6 +411,247 @@ api.unregisterAssetSource = function(assetKey) {
}); });
}; };
/*******************************************************************************
Experimental support for cache storage compression.
For background information on the topic, see:
https://github.com/uBlockOrigin/uBlock-issues/issues/141#issuecomment-407737186
**/
let lz4Codec = (function() {
let lz4wasmInstance;
let pendingInitialization;
let textEncoder, textDecoder;
let ttlCount = 0;
let ttlTimer;
const ttlDelay = 60 * 1000;
let init = function() {
if (
lz4wasmInstance === null ||
WebAssembly instanceof Object === false ||
typeof WebAssembly.instantiateStreaming !== 'function'
) {
lz4wasmInstance = null;
return Promise.resolve(null);
}
if ( lz4wasmInstance instanceof WebAssembly.Instance ) {
return Promise.resolve(lz4wasmInstance);
}
if ( pendingInitialization === undefined ) {
pendingInitialization = WebAssembly.instantiateStreaming(
fetch('lib/lz4-block-codec.wasm', { mode: 'same-origin' })
).then(result => {
pendingInitialization = undefined;
lz4wasmInstance = result && result.instance || null;
});
pendingInitialization.catch(( ) => {
lz4wasmInstance = null;
});
}
return pendingInitialization;
};
// We can't shrink memory usage of wasm instances, and in the current
// case memory usage can grow to a significant amount given that
// a single contiguous memory buffer is required to accommodate both
// input and output data. Thus a time-to-live implementation which
// will cause the wasm instance to be forgotten after enough time
// elapse without the instance being used.
let destroy = function() {
console.info(
'uBO: freeing lz4-block-codec.wasm instance (memory.buffer = %d kB)',
lz4wasmInstance.exports.memory.buffer.byteLength >>> 10
);
lz4wasmInstance = undefined;
textEncoder = textDecoder = undefined;
ttlCount = 0;
ttlTimer = undefined;
};
let ttlManage = function(count) {
if ( ttlTimer !== undefined ) {
clearTimeout(ttlTimer);
ttlTimer = undefined;
}
ttlCount += count;
if ( ttlCount > 0 ) { return; }
if ( lz4wasmInstance === null ) { return; }
ttlTimer = vAPI.setTimeout(destroy, ttlDelay);
};
let growMemoryTo = function(byteLength) {
let lz4api = lz4wasmInstance.exports;
let neededByteLength = lz4api.getLinearMemoryOffset() + byteLength;
let pageCountBefore = lz4api.memory.buffer.byteLength >>> 16;
let pageCountAfter = (neededByteLength + 65535) >>> 16;
if ( pageCountAfter > pageCountBefore ) {
lz4api.memory.grow(pageCountAfter - pageCountBefore);
}
return lz4api.memory;
};
let resolveEncodedValue = function(resolve, key, value) {
let t0 = window.performance.now();
let lz4api = lz4wasmInstance.exports;
let mem0 = lz4api.getLinearMemoryOffset();
let memory = growMemoryTo(mem0 + 65536 * 4);
let hashTable = new Int32Array(memory.buffer, mem0, 65536);
hashTable.fill(-65536, 0, 65536);
let hashTableSize = hashTable.byteLength;
if ( textEncoder === undefined ) {
textEncoder = new TextEncoder();
}
let inputArray = textEncoder.encode(value);
let inputSize = inputArray.byteLength;
let memSize =
hashTableSize +
inputSize +
8 + lz4api.lz4BlockEncodeBound(inputSize);
memory = growMemoryTo(memSize);
let inputMem = new Uint8Array(
memory.buffer,
mem0 + hashTableSize,
inputSize
);
inputMem.set(inputArray);
let outputSize = lz4api.lz4BlockEncode(
mem0 + hashTableSize,
inputSize,
mem0 + hashTableSize + inputSize + 8
);
if ( outputSize === 0 ) { resolve(value); }
let outputMem = new Uint8Array(
memory.buffer,
mem0 + hashTableSize + inputSize,
8 + outputSize
);
outputMem[0] = 0x18;
outputMem[1] = 0x4D;
outputMem[2] = 0x22;
outputMem[3] = 0x04;
outputMem[4] = (inputSize >>> 0) & 0xFF;
outputMem[5] = (inputSize >>> 8) & 0xFF;
outputMem[6] = (inputSize >>> 16) & 0xFF;
outputMem[7] = (inputSize >>> 24) & 0xFF;
console.info(
'uBO: [%s] compressed %d bytes into %d bytes in %s ms',
key,
inputSize,
outputSize,
(window.performance.now() - t0).toFixed(2)
);
resolve(new Blob([ outputMem ]));
};
let resolveDecodedValue = function(resolve, ev, key, value) {
let inputBuffer = ev.target.result;
if ( inputBuffer instanceof ArrayBuffer === false ) {
return resolve(value);
}
let t0 = window.performance.now();
let metadata = new Uint8Array(inputBuffer, 0, 8);
if (
metadata[0] !== 0x18 ||
metadata[1] !== 0x4D ||
metadata[2] !== 0x22 ||
metadata[3] !== 0x04
) {
return resolve(value);
}
let inputSize = inputBuffer.byteLength - 8;
let outputSize =
(metadata[4] << 0) |
(metadata[5] << 8) |
(metadata[6] << 16) |
(metadata[7] << 24);
let lz4api = lz4wasmInstance.exports;
let mem0 = lz4api.getLinearMemoryOffset();
let memSize = inputSize + outputSize;
let memory = growMemoryTo(memSize);
let inputArea = new Uint8Array(
memory.buffer,
mem0,
inputSize
);
inputArea.set(new Uint8Array(inputBuffer, 8, inputSize));
outputSize = lz4api.lz4BlockDecode(inputSize);
if ( outputSize === 0 ) {
return resolve(value);
}
let outputArea = new Uint8Array(
memory.buffer,
mem0 + inputSize,
outputSize
);
if ( textDecoder === undefined ) {
textDecoder = new TextDecoder();
}
value = textDecoder.decode(outputArea);
console.info(
'uBO: [%s] decompressed %d bytes into %d bytes in %s ms',
key,
inputSize,
outputSize,
(window.performance.now() - t0).toFixed(2)
);
resolve(value);
};
let encodeValue = function(key, value) {
if ( !lz4wasmInstance ) {
return Promise.resolve(value);
}
return new Promise(resolve => {
resolveEncodedValue(resolve, key, value);
});
};
let decodeValue = function(key, value) {
if ( !lz4wasmInstance ) {
return Promise.resolve(value);
}
return new Promise(resolve => {
let blobReader = new FileReader();
blobReader.onloadend = ev => {
resolveDecodedValue(resolve, ev, key, value);
};
blobReader.readAsArrayBuffer(value);
});
};
return {
encode: function(key, value) {
if ( typeof value !== 'string' || value.length < 4096 ) {
return Promise.resolve(value);
}
ttlManage(1);
return init().then(( ) => {
return encodeValue(key, value);
}).then(result => {
ttlManage(-1);
return result;
});
},
decode: function(key, value) {
if ( value instanceof Blob === false ) {
return Promise.resolve(value);
}
ttlManage(1);
return init().then(( ) => {
return decodeValue(key, value);
}).then(result => {
ttlManage(-1);
return result;
});
}
};
})();
/******************************************************************************* /*******************************************************************************
The purpose of the asset cache registry is to keep track of all assets The purpose of the asset cache registry is to keep track of all assets
@ -472,26 +715,32 @@ var saveAssetCacheRegistry = (function() {
var assetCacheRead = function(assetKey, callback) { var assetCacheRead = function(assetKey, callback) {
let internalKey = 'cache/' + assetKey; let internalKey = 'cache/' + assetKey;
let reportBack = function(content, err) { let reportBack = function(content) {
if ( content instanceof Blob ) { content = ''; }
let details = { assetKey: assetKey, content: content }; let details = { assetKey: assetKey, content: content };
if ( err ) { details.error = err; } if ( content === '' ) { details.error = 'E_NOTFOUND'; }
callback(details); callback(details);
}; };
let onAssetRead = function(bin) { let onAssetRead = function(bin) {
if ( if (
bin instanceof Object === false || bin instanceof Object === false ||
stringIsNotEmpty(bin[internalKey]) === false bin.hasOwnProperty(internalKey) === false
) { ) {
return reportBack('', 'E_NOTFOUND'); return reportBack('');
} }
let entry = assetCacheRegistry[assetKey]; let entry = assetCacheRegistry[assetKey];
if ( entry === undefined ) { if ( entry === undefined ) {
return reportBack('', 'E_NOTFOUND'); return reportBack('');
} }
entry.readTime = Date.now(); entry.readTime = Date.now();
saveAssetCacheRegistry(true); saveAssetCacheRegistry(true);
reportBack(bin[internalKey]); if ( µBlock.hiddenSettings.cacheStorageCompression !== true ) {
return reportBack(bin[internalKey]);
}
lz4Codec.decode(internalKey, bin[internalKey]).then(result => {
reportBack(result);
});
}; };
let onReady = function() { let onReady = function() {
@ -502,8 +751,8 @@ var assetCacheRead = function(assetKey, callback) {
}; };
var assetCacheWrite = function(assetKey, details, callback) { var assetCacheWrite = function(assetKey, details, callback) {
var internalKey = 'cache/' + assetKey; let internalKey = 'cache/' + assetKey;
var content = ''; let content = '';
if ( typeof details === 'string' ) { if ( typeof details === 'string' ) {
content = details; content = details;
} else if ( details instanceof Object ) { } else if ( details instanceof Object ) {
@ -514,16 +763,19 @@ var assetCacheWrite = function(assetKey, details, callback) {
return assetCacheRemove(assetKey, callback); return assetCacheRemove(assetKey, callback);
} }
var reportBack = function(content) { let reportBack = function(content) {
var details = { assetKey: assetKey, content: content }; let bin = { assetCacheRegistry: assetCacheRegistry };
bin[internalKey] = content;
vAPI.cacheStorage.set(bin);
let details = { assetKey: assetKey, content: content };
if ( typeof callback === 'function' ) { if ( typeof callback === 'function' ) {
callback(details); callback(details);
} }
fireNotification('after-asset-updated', details); fireNotification('after-asset-updated', details);
}; };
var onReady = function() { let onReady = function() {
var entry = assetCacheRegistry[assetKey]; let entry = assetCacheRegistry[assetKey];
if ( entry === undefined ) { if ( entry === undefined ) {
entry = assetCacheRegistry[assetKey] = {}; entry = assetCacheRegistry[assetKey] = {};
} }
@ -531,10 +783,12 @@ var assetCacheWrite = function(assetKey, details, callback) {
if ( details instanceof Object && typeof details.url === 'string' ) { if ( details instanceof Object && typeof details.url === 'string' ) {
entry.remoteURL = details.url; entry.remoteURL = details.url;
} }
var bin = { assetCacheRegistry: assetCacheRegistry }; if ( µBlock.hiddenSettings.cacheStorageCompression !== true ) {
bin[internalKey] = content; return reportBack(content);
vAPI.cacheStorage.set(bin); }
reportBack(content); lz4Codec.encode(internalKey, content).then(result => {
reportBack(result);
});
}; };
getAssetCacheRegistry(onReady); getAssetCacheRegistry(onReady);
}; };

View File

@ -1,7 +1,7 @@
/******************************************************************************* /*******************************************************************************
uBlock Origin - a browser extension to block requests. uBlock Origin - a browser extension to block requests.
Copyright (C) 2014-2018 Raymond Hill Copyright (C) 2014-present Raymond Hill
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -42,6 +42,7 @@ var µBlock = (function() { // jshint ignore:line
assetFetchTimeout: 30, assetFetchTimeout: 30,
autoUpdateAssetFetchPeriod: 120, autoUpdateAssetFetchPeriod: 120,
autoUpdatePeriod: 7, autoUpdatePeriod: 7,
cacheStorageCompression: false,
debugScriptlets: false, debugScriptlets: false,
ignoreRedirectFilters: false, ignoreRedirectFilters: false,
ignoreScriptInjectFilters: false, ignoreScriptInjectFilters: false,
@ -138,7 +139,7 @@ var µBlock = (function() { // jshint ignore:line
// Read-only // Read-only
systemSettings: { systemSettings: {
compiledMagic: 3, // Increase when compiled format changes compiledMagic: 3, // Increase when compiled format changes
selfieMagic: 3 // Increase when selfie format changes selfieMagic: 4 // Increase when selfie format changes
}, },
restoreBackupSettings: { restoreBackupSettings: {

View File

@ -1025,13 +1025,13 @@
let create = function() { let create = function() {
timer = null; timer = null;
let selfie = { let selfie = JSON.stringify({
magic: µb.systemSettings.selfieMagic, magic: µb.systemSettings.selfieMagic,
availableFilterLists: JSON.stringify(µb.availableFilterLists), availableFilterLists: µb.availableFilterLists,
staticNetFilteringEngine: JSON.stringify(µb.staticNetFilteringEngine.toSelfie()), staticNetFilteringEngine: µb.staticNetFilteringEngine.toSelfie(),
redirectEngine: JSON.stringify(µb.redirectEngine.toSelfie()), redirectEngine: µb.redirectEngine.toSelfie(),
staticExtFilteringEngine: JSON.stringify(µb.staticExtFilteringEngine.toSelfie()) staticExtFilteringEngine: µb.staticExtFilteringEngine.toSelfie()
}; });
vAPI.cacheStorage.set({ selfie: selfie }); vAPI.cacheStorage.set({ selfie: selfie });
}; };
@ -1039,16 +1039,25 @@
vAPI.cacheStorage.get('selfie', function(bin) { vAPI.cacheStorage.get('selfie', function(bin) {
if ( if (
bin instanceof Object === false || bin instanceof Object === false ||
bin.selfie instanceof Object === false || typeof bin.selfie !== 'string'
bin.selfie.magic !== µb.systemSettings.selfieMagic ||
bin.selfie.redirectEngine === undefined
) { ) {
return callback(false); return callback(false);
} }
µb.availableFilterLists = JSON.parse(bin.selfie.availableFilterLists); let selfie;
µb.staticNetFilteringEngine.fromSelfie(JSON.parse(bin.selfie.staticNetFilteringEngine)); try {
µb.redirectEngine.fromSelfie(JSON.parse(bin.selfie.redirectEngine)); selfie = JSON.parse(bin.selfie);
µb.staticExtFilteringEngine.fromSelfie(JSON.parse(bin.selfie.staticExtFilteringEngine)); } catch(ex) {
}
if (
selfie instanceof Object === false ||
selfie.magic !== µb.systemSettings.selfieMagic
) {
return callback(false);
}
µb.availableFilterLists = selfie.availableFilterLists;
µb.staticNetFilteringEngine.fromSelfie(selfie.staticNetFilteringEngine);
µb.redirectEngine.fromSelfie(selfie.redirectEngine);
µb.staticExtFilteringEngine.fromSelfie(selfie.staticExtFilteringEngine);
callback(true); callback(true);
}); });
}; };

Binary file not shown.