diff options
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | background.html | 2 | ||||
-rw-r--r-- | js/vapi-background.js | 302 | ||||
-rw-r--r-- | js/vapi-storage.js | 339 |
4 files changed, 341 insertions, 304 deletions
@@ -121,7 +121,7 @@ JS2 := js/httpsb.js js/i18n.js js/liquid-dict.js js/logger.js \ JS3 := js/tab.js js/traffic.js js/udom.js js/uritools.js js/user-rules.js \ js/usersettings.js js/utils.js js/vapi-background.js js/vapi-client.js \ js/vapi-common.js js/vapi-popup.js js/vapi-tabs.js js/vapi-window.js \ - js/xal.js js/vapi-core.js js/vapi-browser.js + js/xal.js js/vapi-core.js js/vapi-browser.js js/vapi-storage.js JS := $(JS1) $(JS2) $(JS3) diff --git a/background.html b/background.html index 7d2a027..9aac3a5 100644 --- a/background.html +++ b/background.html @@ -37,7 +37,7 @@ <script src="js/vapi-background.js"></script> <script src="js/vapi-tabs.js"></script> <script src="js/vapi-browser.js"></script> -<script src="js/vapi-cachestorage.js"></script><!-- Optional --> +<script src="js/vapi-storage.js"></script> <script src="js/background.js"></script> <script src="js/xal.js"></script> <script src="js/usersettings.js"></script> diff --git a/js/vapi-background.js b/js/vapi-background.js index 7f5f223..14544cb 100644 --- a/js/vapi-background.js +++ b/js/vapi-background.js @@ -36,308 +36,6 @@ let vAPI = self.vAPI; // Guaranteed to be initialized by vapi-core.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; - // Icon-related stuff vAPI.setIcon = function (tabId, iconId, badge) { // If badge is undefined, then setIcon was called from the 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; +})(); |