Browse Source
use indexedDB to cache assets: https://bugzilla.mozilla.org/show_bug.cgi?id=1371255#c11
pull/2/head
use indexedDB to cache assets: https://bugzilla.mozilla.org/show_bug.cgi?id=1371255#c11
pull/2/head
gorhill
7 years ago
4 changed files with 296 additions and 24 deletions
-
269platform/webext/vapi-cachestorage.js
-
1src/background.html
-
29tools/make-webext-hybrid.sh
-
21tools/make-webext.sh
@ -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; |
|||
}); |
|||
} |
|||
}()); |
|||
|
|||
/******************************************************************************/ |
Write
Preview
Loading…
Cancel
Save
Reference in new issue