use indexedDB to cache assets: https://bugzilla.mozilla.org/show_bug.cgi?id=1371255#c11
parent
5ea59ab7c4
commit
406f6473b6
@ -0,0 +1,269 @@
|
||||
/*******************************************************************************
|
||||
|
||||
uMatrix - a browser extension to block requests.
|
||||
Copyright (C) 2016-2017 The uBlock Origin authors
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
|
||||
Home: https://github.com/gorhill/uBlock
|
||||
*/
|
||||
|
||||
/* global indexedDB, IDBDatabase */
|
||||
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// The code below has been originally manually imported from:
|
||||
// Commit: https://github.com/nikrolls/uBlock-Edge/commit/d1538ea9bea89d507219d3219592382eee306134
|
||||
// Commit date: 29 October 2016
|
||||
// Commit author: https://github.com/nikrolls
|
||||
// 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.
|
||||
|
||||
// Imported from uBlock Origin project.
|
||||
|
||||
vAPI.cacheStorage = (function() {
|
||||
const STORAGE_NAME = 'uMatrixCacheStorage';
|
||||
var db;
|
||||
var pending = [];
|
||||
|
||||
// prime the db so that it's ready asap for next access.
|
||||
getDb(noopfn);
|
||||
|
||||
return { get, set, remove, clear, getBytesInUse };
|
||||
|
||||
function get(input, callback) {
|
||||
if ( typeof callback !== 'function' ) { return; }
|
||||
if ( input === null ) {
|
||||
return getAllFromDb(callback);
|
||||
}
|
||||
var toRead, output = {};
|
||||
if ( typeof input === 'string' ) {
|
||||
toRead = [ input ];
|
||||
} else if ( Array.isArray(input) ) {
|
||||
toRead = input;
|
||||
} else /* if ( typeof input === 'object' ) */ {
|
||||
toRead = Object.keys(input);
|
||||
output = input;
|
||||
}
|
||||
return getFromDb(toRead, output, callback);
|
||||
}
|
||||
|
||||
function set(input, callback) {
|
||||
putToDb(input, callback);
|
||||
}
|
||||
|
||||
function remove(key, callback) {
|
||||
deleteFromDb(key, callback);
|
||||
}
|
||||
|
||||
function clear(callback) {
|
||||
clearDb(callback);
|
||||
}
|
||||
|
||||
function getBytesInUse(keys, callback) {
|
||||
// TODO: implement this
|
||||
callback(0);
|
||||
}
|
||||
|
||||
function genericErrorHandler(error) {
|
||||
console.error('[uMatrix cacheStorage]', error);
|
||||
}
|
||||
|
||||
function noopfn() {
|
||||
}
|
||||
|
||||
function processPendings() {
|
||||
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 ) {
|
||||
return callback(db);
|
||||
}
|
||||
pending.push(callback);
|
||||
if ( pending.length !== 1 ) { return; }
|
||||
// This will fail in private browsing mode.
|
||||
var req = indexedDB.open(STORAGE_NAME, 1);
|
||||
req.onupgradeneeded = function(ev) {
|
||||
db = ev.target.result;
|
||||
db.onerror = genericErrorHandler;
|
||||
var table = db.createObjectStore(STORAGE_NAME, { keyPath: 'key' });
|
||||
table.createIndex('value', 'value', { unique: false });
|
||||
};
|
||||
req.onsuccess = function(ev) {
|
||||
db = ev.target.result;
|
||||
db.onerror = genericErrorHandler;
|
||||
processPendings();
|
||||
};
|
||||
req.onerror = function() {
|
||||
console.log(this.error);
|
||||
processPendings();
|
||||
pending = undefined;
|
||||
};
|
||||
}
|
||||
|
||||
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;
|
||||
});
|
||||
}
|
||||
}());
|
||||
|
||||
/******************************************************************************/
|
Loading…
Reference in New Issue