aboutsummaryrefslogtreecommitdiffstats
path: root/js/vapi-storage.js
diff options
context:
space:
mode:
authorAlessio Vanni <vannilla@firemail.cc>2019-06-23 02:27:59 +0200
committerAlessio Vanni <vannilla@firemail.cc>2019-06-23 02:27:59 +0200
commite29205dc4d013704cd6b1ee2fd7b7dc0ef226c01 (patch)
tree5d0a2526f472f6f03fa5936bd85763c80b181036 /js/vapi-storage.js
parent3e1d8467b38a31b8f3a9ef562fe9a890a5838276 (diff)
downloadematrix-e29205dc4d013704cd6b1ee2fd7b7dc0ef226c01.tar.lz
ematrix-e29205dc4d013704cd6b1ee2fd7b7dc0ef226c01.tar.xz
ematrix-e29205dc4d013704cd6b1ee2fd7b7dc0ef226c01.zip
Move storage API
Also remove optional cachestorage script. It doesn't exists and the cachedstorage, whatever it is, is defined as an alias for the normal storage system.
Diffstat (limited to 'js/vapi-storage.js')
-rw-r--r--js/vapi-storage.js339
1 files changed, 339 insertions, 0 deletions
diff --git a/js/vapi-storage.js b/js/vapi-storage.js
new file mode 100644
index 0000000..768958c
--- /dev/null
+++ b/js/vapi-storage.js
@@ -0,0 +1,339 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2014-2019 The uMatrix/uBlock Origin authors
+ Copyright (C) 2019 Alessio Vanni
+
+ 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://gitlab.com/vannilla/ematrix
+ uMatrix Home: https://github.com/gorhill/uMatrix
+*/
+
+/* global self, Components */
+
+// For background page (tabs management)
+
+'use strict';
+
+/******************************************************************************/
+
+(function () {
+ const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
+ const {Services} = Cu.import('resource://gre/modules/Services.jsm', null);
+
+ let vAPI = self.vAPI; // Guaranteed to be initialized by vapi-background.js
+
+ // API matches that of chrome.storage.local:
+ // https://developer.chrome.com/extensions/storage
+ vAPI.storage = (function () {
+ let db = null;
+ let vacuumTimer = null;
+
+ let close = function () {
+ if (vacuumTimer !== null) {
+ clearTimeout(vacuumTimer);
+ vacuumTimer = null;
+ }
+
+ if (db === null) {
+ return;
+ }
+
+ db.asyncClose();
+ db = null;
+ };
+
+ let open = function () {
+ if (db !== null) {
+ return db;
+ }
+
+ // Create path
+ let path = Services.dirsvc.get('ProfD', Ci.nsIFile);
+ path.append('ematrix-data');
+ if (!path.exists()) {
+ path.create(Ci.nsIFile.DIRECTORY_TYPE, parseInt('0774', 8));
+ }
+ if (!path.isDirectory()) {
+ throw Error('Should be a directory...');
+ }
+
+ let path2 = Services.dirsvc.get('ProfD', Ci.nsIFile);
+ path2.append('extension-data');
+ path2.append(location.host + '.sqlite');
+ if (path2.exists()) {
+ path2.moveTo(path, location.host+'.sqlite');
+ }
+
+ path.append(location.host + '.sqlite');
+
+ // Open database
+ try {
+ db = Services.storage.openDatabase(path);
+ if (db.connectionReady === false) {
+ db.asyncClose();
+ db = null;
+ }
+ } catch (ex) {
+ // Ignore
+ }
+
+ if (db === null) {
+ return null;
+ }
+
+ // Database was opened, register cleanup task
+ vAPI.addCleanUpTask(close);
+
+ // Setup database
+ db.createAsyncStatement('CREATE TABLE IF NOT EXISTS '
+ +'"settings" ("name" '
+ +'TEXT PRIMARY KEY NOT NULL, '
+ +'"value" TEXT);')
+ .executeAsync();
+
+ if (vacuum !== null) {
+ vacuumTimer = vAPI.setTimeout(vacuum, 60000);
+ }
+
+ return db;
+ };
+
+ // Vacuum only once, and only while idle
+ let vacuum = function () {
+ vacuumTimer = null;
+ if (db === null) {
+ return;
+ }
+ let idleSvc =
+ Cc['@mozilla.org/widget/idleservice;1']
+ .getService(Ci.nsIIdleService);
+
+ if (idleSvc.idleTime < 60000) {
+ vacuumTimer = vAPI.setTimeout(vacuum, 60000);
+ return;
+ }
+
+ db.createAsyncStatement('VACUUM').executeAsync();
+ vacuum = null;
+ };
+
+ // Execute a query
+ let runStatement = function (stmt, callback) {
+ let result = {};
+
+ stmt.executeAsync({
+ handleResult: function (rows) {
+ if (!rows || typeof callback !== 'function') {
+ return;
+ }
+
+ let row;
+ while ((row = rows.getNextRow())) {
+ // we assume that there will be two columns, since we're
+ // using it only for preferences
+ // eMatrix: the above comment is obsolete
+ // (it's not used just for preferences
+ // anymore), but we still expect two columns.
+ let res = row.getResultByIndex(0);
+ result[res] = row.getResultByIndex(1);
+ }
+ },
+ handleCompletion: function (reason) {
+ if (typeof callback === 'function' && reason === 0) {
+ callback(result);
+ }
+ },
+ handleError: function (error) {
+ console.error('SQLite error ', error.result, error.message);
+
+ // Caller expects an answer regardless of failure.
+ if (typeof callback === 'function' ) {
+ callback(null);
+ }
+ },
+ });
+ };
+
+ let bindNames = function (stmt, names) {
+ if (Array.isArray(names) === false || names.length === 0) {
+ return;
+ }
+
+ let params = stmt.newBindingParamsArray();
+
+ for (let i=names.length-1; i>=0; --i) {
+ let bp = params.newBindingParams();
+ bp.bindByName('name', names[i]);
+ params.addParams(bp);
+ }
+
+ stmt.bindParameters(params);
+ };
+
+ let clear = function (callback) {
+ if (open() === null) {
+ if (typeof callback === 'function') {
+ callback();
+ }
+ return;
+ }
+
+ runStatement(db.createAsyncStatement('DELETE FROM "settings";'),
+ callback);
+ };
+
+ let getBytesInUse = function (keys, callback) {
+ if (typeof callback !== 'function') {
+ return;
+ }
+
+ if (open() === null) {
+ callback(0);
+ return;
+ }
+
+ let stmt;
+ if (Array.isArray(keys)) {
+ stmt = db.createAsyncStatement('SELECT "size" AS "size", '
+ +'SUM(LENGTH("value")) '
+ +'FROM "settings" WHERE '
+ +'"name" = :name');
+ bindNames(keys);
+ } else {
+ stmt = db.createAsyncStatement('SELECT "size" AS "size", '
+ +'SUM(LENGTH("value")) '
+ +'FROM "settings"');
+ }
+
+ runStatement(stmt, function (result) {
+ callback(result.size);
+ });
+ };
+
+ let read = function (details, callback) {
+ if (typeof callback !== 'function') {
+ return;
+ }
+
+ let prepareResult = function (result) {
+ for (let key in result) {
+ if (result.hasOwnProperty(key) === false) {
+ continue;
+ }
+
+ result[key] = JSON.parse(result[key]);
+ }
+
+ if (typeof details === 'object' && details !== null) {
+ for (let key in details) {
+ if (result.hasOwnProperty(key) === false) {
+ result[key] = details[key];
+ }
+ }
+ }
+
+ callback(result);
+ };
+
+ if (open() === null) {
+ prepareResult({});
+ return;
+ }
+
+ let names = [];
+ if (details !== null) {
+ if (Array.isArray(details)) {
+ names = details;
+ } else if (typeof details === 'object') {
+ names = Object.keys(details);
+ } else {
+ names = [details.toString()];
+ }
+ }
+
+ let stmt;
+ if (names.length === 0) {
+ stmt = db.createAsyncStatement('SELECT * FROM "settings"');
+ } else {
+ stmt = db.createAsyncStatement('SELECT * FROM "settings" '
+ +'WHERE "name" = :name');
+ bindNames(stmt, names);
+ }
+
+ runStatement(stmt, prepareResult);
+ };
+
+ let remove = function (keys, callback) {
+ if (open() === null) {
+ if (typeof callback === 'function') {
+ callback();
+ }
+ return;
+ }
+
+ var stmt = db.createAsyncStatement('DELETE FROM "settings" '
+ +'WHERE "name" = :name');
+ bindNames(stmt, typeof keys === 'string' ? [keys] : keys);
+ runStatement(stmt, callback);
+ };
+
+ let write = function (details, callback) {
+ if (open() === null) {
+ if (typeof callback === 'function') {
+ callback();
+ }
+ return;
+ }
+
+ let stmt = db.createAsyncStatement('INSERT OR REPLACE INTO '
+ +'"settings" ("name", "value") '
+ +'VALUES(:name, :value)');
+ let params = stmt.newBindingParamsArray();
+
+ for (let key in details) {
+ if (details.hasOwnProperty(key) === false) {
+ continue;
+ }
+
+ let bp = params.newBindingParams();
+ bp.bindByName('name', key);
+ bp.bindByName('value', JSON.stringify(details[key]));
+ params.addParams(bp);
+ }
+
+ if (params.length === 0) {
+ return;
+ }
+
+ stmt.bindParameters(params);
+ runStatement(stmt, callback);
+ };
+
+ // Export API
+ var api = {
+ QUOTA_BYTES: 100 * 1024 * 1024,
+ clear: clear,
+ get: read,
+ getBytesInUse: getBytesInUse,
+ remove: remove,
+ set: write
+ };
+
+ return api;
+ })();
+
+ vAPI.cacheStorage = vAPI.storage;
+})();