1
0
mirror of https://github.com/gorhill/uBlock.git synced 2024-10-06 01:27:12 +02:00
This commit is contained in:
gorhill 2017-08-29 18:32:00 -04:00
parent 572aecc517
commit beb7933016
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
8 changed files with 264 additions and 171 deletions

View File

@ -4,7 +4,8 @@
"eqeqeq": true, "eqeqeq": true,
"esnext": true, "esnext": true,
"globals": { "globals": {
"chrome": false, "browser": false, // global variable in Firefox, Edge
"chrome": false, // global variable in Chromium, Chrome, Opera
"Components": false, // global variable in Firefox "Components": false, // global variable in Firefox
"safari": false, "safari": false,
"self": false, "self": false,

View File

@ -10,6 +10,7 @@
<script src="lib/publicsuffixlist.js"></script> <script src="lib/publicsuffixlist.js"></script>
<script src="js/vapi-common.js"></script> <script src="js/vapi-common.js"></script>
<script src="js/vapi-background.js"></script> <script src="js/vapi-background.js"></script>
<script src="js/vapi-cachestorage.js"></script><!-- Optional -->
<script src="js/background.js"></script> <script src="js/background.js"></script>
<script src="js/utils.js"></script> <script src="js/utils.js"></script>
<script src="js/uritools.js"></script> <script src="js/uritools.js"></script>

View File

@ -28,6 +28,7 @@
(function() { (function() {
let µb = µBlock; let µb = µBlock;
let migratedKeys = new Set(); let migratedKeys = new Set();
let reCacheStorageKeys = /^(?:assetCacheRegistry|assetSourceRegistry|cache\/.+|selfie)$/;
let migrateAll = function(callback) { let migrateAll = function(callback) {
let migrateKeyValue = function(details, callback) { let migrateKeyValue = function(details, callback) {
@ -40,7 +41,11 @@
migratedKeys.add(details.key); migratedKeys.add(details.key);
let bin = {}; let bin = {};
bin[details.key] = JSON.parse(details.value); bin[details.key] = JSON.parse(details.value);
self.browser.storage.local.set(bin, callback); if ( reCacheStorageKeys.test(details.key) ) {
vAPI.cacheStorage.set(bin, callback);
} else {
vAPI.storage.set(bin, callback);
}
}; };
let migrateNext = function() { let migrateNext = function() {
@ -62,7 +67,7 @@
self.browser.runtime.sendMessage({ what: 'webext:storageMigrateDone' }); self.browser.runtime.sendMessage({ what: 'webext:storageMigrateDone' });
return callback(); return callback();
} }
self.browser.storage.local.set({ legacyStorageMigrated: true }); vAPI.storage.set({ legacyStorageMigrated: true });
migrateNext(); migrateNext();
}); });
}; };

View File

@ -19,6 +19,8 @@
Home: https://github.com/gorhill/uBlock Home: https://github.com/gorhill/uBlock
*/ */
/* global indexedDB, IDBDatabase */
'use strict'; 'use strict';
/******************************************************************************/ /******************************************************************************/
@ -29,64 +31,50 @@
// Commit author: https://github.com/nikrolls // Commit author: https://github.com/nikrolls
// Commit message: "Implement cacheStorage using IndexedDB" // Commit message: "Implement cacheStorage using IndexedDB"
// The original imported code has been subsequently modified as it was not
// compatible with Firefox.
// (a Promise thing, see https://github.com/dfahlander/Dexie.js/issues/317)
// Furthermore, code to migrate from browser.storage.local to vAPI.cacheStorage
// has been added, for seamless migration of cache-related entries into
// indexedDB.
vAPI.cacheStorage = (function() { vAPI.cacheStorage = (function() {
const STORAGE_NAME = 'uBlockStorage'; const STORAGE_NAME = 'uBlock0CacheStorage';
const db = getDb(); var db;
var pending = [];
return {get, set, remove, clear, getBytesInUse}; // prime the db so that it's ready asap for next access.
getDb(noopfn);
function get(key, callback) { return { get, set, remove, clear, getBytesInUse };
let promise;
if (key === null) { function get(input, callback) {
promise = getAllFromDb(); if ( typeof callback !== 'function' ) { return; }
} else if (typeof key === 'string') { if ( input === null ) {
promise = getFromDb(key).then(result => [result]); return getAllFromDb(callback);
} else if (typeof key === 'object') {
const keys = Array.isArray(key) ? [].concat(key) : Object.keys(key);
const requests = keys.map(key => getFromDb(key));
promise = Promise.all(requests);
} else {
promise = Promise.resolve([]);
} }
var toRead, output = {};
promise.then(results => convertResultsToHash(results)) if ( typeof input === 'string' ) {
.then((converted) => { toRead = [ input ];
if (typeof key === 'object' && !Array.isArray(key)) { } else if ( Array.isArray(input) ) {
callback(Object.assign({}, key, converted)); toRead = input;
} else { } else /* if ( typeof input === 'object' ) */ {
callback(converted); toRead = Object.keys(input);
} output = input;
}) }
.catch((e) => { return getFromDb(toRead, output, callback);
browser.runtime.lastError = e;
callback(null);
});
} }
function set(data, callback) { function set(input, callback) {
const requests = Object.keys(data).map( putToDb(input, callback);
key => putToDb(key, data[key])
);
Promise.all(requests)
.then(() => callback && callback())
.catch(e => (browser.runtime.lastError = e, callback && callback()));
} }
function remove(key, callback) { function remove(key, callback) {
const keys = [].concat(key); deleteFromDb(key, callback);
const requests = keys.map(key => deleteFromDb(key));
Promise.all(requests)
.then(() => callback && callback())
.catch(e => (browser.runtime.lastError = e, callback && callback()));
} }
function clear(callback) { function clear(callback) {
clearDb() clearDb(callback);
.then(() => callback && callback())
.catch(e => (browser.runtime.lastError = e, callback && callback()));
} }
function getBytesInUse(keys, callback) { function getBytesInUse(keys, callback) {
@ -94,87 +82,180 @@ vAPI.cacheStorage = (function() {
callback(0); callback(0);
} }
function getDb() { function genericErrorHandler(error) {
const openRequest = window.indexedDB.open(STORAGE_NAME, 1); console.error('[uBlock0 cacheStorage]', error);
openRequest.onupgradeneeded = upgradeSchema;
return convertToPromise(openRequest).then((db) => {
db.onerror = console.error;
return db;
});
} }
function upgradeSchema(event) { function noopfn() {
const db = event.target.result;
db.onerror = (error) => console.error('[storage] Error updating IndexedDB schema:', error);
const objectStore = db.createObjectStore(STORAGE_NAME, {keyPath: 'key'});
objectStore.createIndex('value', 'value', {unique: false});
} }
function getNewTransaction(mode = 'readonly') { function getDb(callback) {
return db.then(db => db.transaction(STORAGE_NAME, mode).objectStore(STORAGE_NAME)); if ( pending.length !== 0 ) {
} pending.push(callback);
return;
function getFromDb(key) { }
return getNewTransaction() if ( db instanceof IDBDatabase ) {
.then(store => store.get(key)) return callback(db);
.then(request => convertToPromise(request)); }
} pending.push(callback);
if ( pending.length !== 1 ) { return; }
function getAllFromDb() { var req = indexedDB.open(STORAGE_NAME, 1);
return getNewTransaction() req.onupgradeneeded = function(ev) {
.then((store) => { db = ev.target.result;
return new Promise((resolve, reject) => { db.onerror = genericErrorHandler;
const request = store.openCursor(); var table = db.createObjectStore(STORAGE_NAME, { keyPath: 'key' });
const output = []; table.createIndex('value', 'value', { unique: false });
};
request.onsuccess = (event) => { req.onsuccess = function(ev) {
const cursor = event.target.result; db = ev.target.result;
if (cursor) { db.onerror = genericErrorHandler;
output.push(cursor.value); var cb;
cursor.continue(); while ( (cb = pending.shift()) ) {
} else { cb(db);
resolve(output);
}
};
request.onerror = reject;
});
});
}
function putToDb(key, value) {
return getNewTransaction('readwrite')
.then(store => store.put({key, value}))
.then(request => convertToPromise(request));
}
function deleteFromDb(key) {
return getNewTransaction('readwrite')
.then(store => store.delete(key))
.then(request => convertToPromise(request));
}
function clearDb() {
return getNewTransaction('readwrite')
.then(store => store.clear())
.then(request => convertToPromise(request));
}
function convertToPromise(eventTarget) {
return new Promise((resolve, reject) => {
eventTarget.onsuccess = () => resolve(eventTarget.result);
eventTarget.onerror = reject;
});
}
function convertResultsToHash(results) {
return results.reduce((output, item) => {
if (item) {
output[item.key] = item.value;
} }
return output; };
}, {}); req.onerror = function(ev) {
console.log(ev);
var cb;
while ( (cb = pending.shift()) ) {
cb(db);
}
};
}
function getFromDb(keys, store, callback) {
if ( typeof callback !== 'function' ) { return; }
if ( keys.length === 0 ) { return callback(store); }
var notfoundKeys = new Set(keys);
var gotOne = function() {
if ( typeof this.result === 'object' ) {
store[this.result.key] = this.result.value;
notfoundKeys.delete(this.result.key);
}
};
getDb(function(db) {
if ( !db ) { return callback(); }
var transaction = db.transaction(STORAGE_NAME);
transaction.oncomplete = transaction.onerror = function() {
if ( notfoundKeys.size === 0 ) {
return callback(store);
}
maybeMigrate(Array.from(notfoundKeys), store, callback);
};
var table = transaction.objectStore(STORAGE_NAME);
for ( var key of keys ) {
var req = table.get(key);
req.onsuccess = gotOne;
req.onerror = noopfn;
}
});
}
// Migrate from storage API
// TODO: removes once all users are migrated to the new cacheStorage.
function maybeMigrate(keys, store, callback) {
var toMigrate = new Set(),
i = keys.length;
while ( i-- ) {
var key = keys[i];
toMigrate.add(key);
// If migrating a compiled list, also migrate the non-compiled
// counterpart.
if ( /^cache\/compiled\//.test(key) ) {
toMigrate.add(key.replace('/compiled', ''));
}
}
vAPI.storage.get(Array.from(toMigrate), function(bin) {
if ( bin instanceof Object === false ) {
return callback(store);
}
var migratedKeys = Object.keys(bin);
if ( migratedKeys.length === 0 ) {
return callback(store);
}
var i = migratedKeys.length;
while ( i-- ) {
var key = migratedKeys[i];
store[key] = bin[key];
}
vAPI.storage.remove(migratedKeys);
vAPI.cacheStorage.set(bin);
callback(store);
});
}
function getAllFromDb(callback) {
if ( typeof callback !== 'function' ) {
callback = noopfn;
}
getDb(function(db) {
if ( !db ) { return callback(); }
var output = {};
var transaction = db.transaction(STORAGE_NAME);
transaction.oncomplete = transaction.onerror = function() {
callback(output);
};
var table = transaction.objectStore(STORAGE_NAME),
req = table.openCursor();
req.onsuccess = function(ev) {
var cursor = ev.target.result;
if ( !cursor ) { return; }
output[cursor.key] = cursor.value;
cursor.continue();
};
});
}
function putToDb(input, callback) {
if ( typeof callback !== 'function' ) {
callback = noopfn;
}
var keys = Object.keys(input);
if ( keys.length === 0 ) { return callback(); }
getDb(function(db) {
if ( !db ) { return callback(); }
var transaction = db.transaction(STORAGE_NAME, 'readwrite');
transaction.oncomplete = transaction.onerror = callback;
var table = transaction.objectStore(STORAGE_NAME),
entry = {};
for ( var key of keys ) {
entry.key = key;
entry.value = input[key];
table.put(entry);
}
});
}
function deleteFromDb(input, callback) {
if ( typeof callback !== 'function' ) {
callback = noopfn;
}
var keys = Array.isArray(input) ? input.slice() : [ input ];
if ( keys.length === 0 ) { return callback(); }
getDb(function(db) {
if ( !db ) { return callback(); }
var transaction = db.transaction(STORAGE_NAME, 'readwrite');
transaction.oncomplete = transaction.onerror = callback;
var table = transaction.objectStore(STORAGE_NAME);
for ( var key of keys ) {
table.delete(key);
}
});
// TODO: removes once all users are migrated to the new cacheStorage.
vAPI.storage.remove(keys);
}
function clearDb(callback) {
if ( typeof callback !== 'function' ) {
callback = noopfn;
}
getDb(function(db) {
if ( !db ) { return callback(); }
var req = db.transaction(STORAGE_NAME, 'readwrite')
.objectStore(STORAGE_NAME)
.clear();
req.onsuccess = req.onerror = callback;
});
} }
}()); }());

View File

@ -10,6 +10,7 @@
<script src="lib/publicsuffixlist.js"></script> <script src="lib/publicsuffixlist.js"></script>
<script src="js/vapi-common.js"></script> <script src="js/vapi-common.js"></script>
<script src="js/vapi-background.js"></script> <script src="js/vapi-background.js"></script>
<script src="js/vapi-cachestorage.js"></script><!-- Optional -->
<script src="js/background.js"></script> <script src="js/background.js"></script>
<script src="js/utils.js"></script> <script src="js/utils.js"></script>
<script src="js/uritools.js"></script> <script src="js/uritools.js"></script>

View File

@ -127,7 +127,10 @@ var onVersionReady = function(lastVersion) {
/******************************************************************************/ /******************************************************************************/
var onSelfieReady = function(selfie) { var onSelfieReady = function(selfie) {
if ( selfie === null || selfie.magic !== µb.systemSettings.selfieMagic ) { if (
selfie instanceof Object === false ||
selfie.magic !== µb.systemSettings.selfieMagic
) {
return false; return false;
} }
if ( publicSuffixList.fromSelfie(selfie.publicSuffixList) !== true ) { if ( publicSuffixList.fromSelfie(selfie.publicSuffixList) !== true ) {
@ -221,13 +224,13 @@ var onFirstFetchReady = function(fetched) {
onNetWhitelistReady(fetched.netWhitelist); onNetWhitelistReady(fetched.netWhitelist);
onVersionReady(fetched.version); onVersionReady(fetched.version);
// If we have a selfie, skip loading PSL, filters // If we have a selfie, skip loading PSL, filter lists
if ( onSelfieReady(fetched.selfie) ) { vAPI.cacheStorage.get('selfie', function(bin) {
onAllReady(); if ( bin instanceof Object && onSelfieReady(bin.selfie) ) {
return; return onAllReady();
} }
µb.loadPublicSuffixList(onPSLReady);
µb.loadPublicSuffixList(onPSLReady); });
}; };
/******************************************************************************/ /******************************************************************************/
@ -266,7 +269,6 @@ var onSelectedFilterListsLoaded = function() {
'lastBackupFile': '', 'lastBackupFile': '',
'lastBackupTime': 0, 'lastBackupTime': 0,
'netWhitelist': µb.netWhitelistDefault, 'netWhitelist': µb.netWhitelistDefault,
'selfie': null,
'selfieMagic': '', 'selfieMagic': '',
'version': '0.0.0.0' 'version': '0.0.0.0'
}; };

View File

@ -11,31 +11,32 @@ mkdir -p $DES/webextension
bash ./tools/make-assets.sh $DES/webextension bash ./tools/make-assets.sh $DES/webextension
cp -R src/css $DES/webextension/ cp -R src/css $DES/webextension/
cp -R src/img $DES/webextension/ cp -R src/img $DES/webextension/
cp -R src/js $DES/webextension/ cp -R src/js $DES/webextension/
cp -R src/lib $DES/webextension/ cp -R src/lib $DES/webextension/
cp -R src/_locales $DES/webextension/ cp -R src/_locales $DES/webextension/
cp -R $DES/webextension/_locales/nb $DES/webextension/_locales/no cp -R $DES/webextension/_locales/nb $DES/webextension/_locales/no
cp src/*.html $DES/webextension/ cp src/*.html $DES/webextension/
cp platform/chromium/*.js $DES/webextension/js/ cp platform/chromium/*.js $DES/webextension/js/
cp -R platform/chromium/img $DES/webextension/ cp -R platform/chromium/img $DES/webextension/
cp platform/chromium/*.html $DES/webextension/ cp platform/chromium/*.html $DES/webextension/
cp platform/chromium/*.json $DES/webextension/ cp platform/chromium/*.json $DES/webextension/
cp LICENSE.txt $DES/webextension/ cp LICENSE.txt $DES/webextension/
cp platform/webext/manifest.json $DES/webextension/ cp platform/webext/manifest.json $DES/webextension/
cp platform/webext/background.html $DES/webextension/ cp platform/webext/background.html $DES/webextension/
cp platform/webext/options_ui.html $DES/webextension/ cp platform/webext/options_ui.html $DES/webextension/
cp platform/webext/polyfill.js $DES/webextension/js/ cp platform/webext/polyfill.js $DES/webextension/js/
cp platform/webext/vapi-usercss.js $DES/webextension/js/ cp platform/webext/vapi-usercss.js $DES/webextension/js/
cp platform/webext/from-legacy.js $DES/webextension/js/ cp platform/webext/vapi-cachestorage.js $DES/webextension/js/
cp platform/webext/from-legacy.js $DES/webextension/js/
rm $DES/webextension/js/options_ui.js rm $DES/webextension/js/options_ui.js
cp platform/webext/bootstrap.js $DES/ cp platform/webext/bootstrap.js $DES/
cp platform/webext/chrome.manifest $DES/ cp platform/webext/chrome.manifest $DES/
cp platform/webext/install.rdf $DES/ cp platform/webext/install.rdf $DES/
mv $DES/webextension/img/icon_128.png $DES/icon.png mv $DES/webextension/img/icon_128.png $DES/icon.png
echo "*** uBlock0.webext-hybrid: Generating meta..." echo "*** uBlock0.webext-hybrid: Generating meta..."
python tools/make-webext-hybrid-meta.py $DES/ python tools/make-webext-hybrid-meta.py $DES/

View File

@ -11,23 +11,24 @@ mkdir -p $DES
bash ./tools/make-assets.sh $DES bash ./tools/make-assets.sh $DES
cp -R src/css $DES/ cp -R src/css $DES/
cp -R src/img $DES/ cp -R src/img $DES/
cp -R src/js $DES/ cp -R src/js $DES/
cp -R src/lib $DES/ cp -R src/lib $DES/
cp -R src/_locales $DES/ cp -R src/_locales $DES/
cp -R $DES/_locales/nb $DES/_locales/no cp -R $DES/_locales/nb $DES/_locales/no
cp src/*.html $DES/ cp src/*.html $DES/
cp platform/chromium/*.js $DES/js/ cp platform/chromium/*.js $DES/js/
cp -R platform/chromium/img $DES/ cp -R platform/chromium/img $DES/
cp platform/chromium/*.html $DES/ cp platform/chromium/*.html $DES/
cp platform/chromium/*.json $DES/ cp platform/chromium/*.json $DES/
cp LICENSE.txt $DES/ cp LICENSE.txt $DES/
cp platform/webext/manifest.json $DES/ cp platform/webext/manifest.json $DES/
cp platform/webext/options_ui.html $DES/ cp platform/webext/options_ui.html $DES/
cp platform/webext/polyfill.js $DES/js/ cp platform/webext/polyfill.js $DES/js/
cp platform/webext/vapi-usercss.js $DES/js/ cp platform/webext/vapi-cachestorage.js $DES/js/
cp platform/webext/vapi-usercss.js $DES/js/
rm $DES/js/options_ui.js rm $DES/js/options_ui.js
echo "*** uBlock0.webext: Generating meta..." echo "*** uBlock0.webext: Generating meta..."