aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--background.html2
-rw-r--r--js/vapi-background.js302
-rw-r--r--js/vapi-storage.js339
4 files changed, 341 insertions, 304 deletions
diff --git a/Makefile b/Makefile
index 2197f98..f9c7b77 100644
--- a/Makefile
+++ b/Makefile
@@ -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;
+})();