aboutsummaryrefslogtreecommitdiff
path: root/background/storage.js
diff options
context:
space:
mode:
Diffstat (limited to 'background/storage.js')
-rw-r--r--background/storage.js359
1 files changed, 0 insertions, 359 deletions
diff --git a/background/storage.js b/background/storage.js
deleted file mode 100644
index fbd4a7e..0000000
--- a/background/storage.js
+++ /dev/null
@@ -1,359 +0,0 @@
-/**
- * This file is part of Haketilo.
- *
- * Function: Storage manager.
- *
- * Copyright (C) 2021 Wojtek Kosior
- *
- * 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.
- *
- * As additional permission under GNU GPL version 3 section 7, you
- * may distribute forms of that code without the copy of the GNU
- * GPL normally required by section 4, provided you include this
- * license notice and, in case of non-source distribution, a URL
- * through which recipients can access the Corresponding Source.
- * If you modify file(s) with this exception, you may extend this
- * exception to your version of the file(s), but you are not
- * obligated to do so. If you do not wish to do so, delete this
- * exception statement from your version.
- *
- * As a special exception to the GPL, any HTML file which merely
- * makes function calls to this code, and for that purpose
- * includes it by reference shall be deemed a separate work for
- * copyright law purposes. If you modify this code, you may extend
- * this exception to your version of the code, but you are not
- * obligated to do so. If you do not wish to do so, delete this
- * exception statement from your version.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- * I, Wojtek Kosior, thereby promise not to sue for violation of this file's
- * license. Although I request that you do not make use of this code in a
- * proprietary program, I am not going to enforce this in court.
- */
-
-#IMPORT common/storage_raw.js AS raw_storage
-#IMPORT common/observables.js
-
-#FROM common/stored_types.js IMPORT list_prefixes, TYPE_NAME
-#FROM common/lock.js IMPORT lock, unlock, make_lock
-#FROM common/once.js IMPORT make_once
-#FROM common/browser.js IMPORT browser
-
-var exports = {};
-
-/* A special case of persisted variable is one that contains list of items. */
-
-async function get_list_var(name)
-{
- let list = await raw_storage.get_var(name);
-
- return list === undefined ? [] : list;
-}
-
-/* We maintain in-memory copies of some stored lists. */
-
-async function list(prefix)
-{
- let name = TYPE_NAME[prefix] + "s"; /* Make plural. */
- let map = new Map();
-
- for (let item of await get_list_var(name))
- map.set(item, await raw_storage.get(prefix + item));
-
- return {map, prefix, name, observable: observables.make(),
- lock: make_lock()};
-}
-
-var list_by_prefix = {};
-
-async function init()
-{
- for (let prefix of list_prefixes)
- list_by_prefix[prefix] = await list(prefix);
-
- return exports;
-}
-
-/*
- * Facilitate listening to changes
- */
-
-exports.add_change_listener = function (cb, prefixes=list_prefixes)
-{
- if (typeof(prefixes) === "string")
- prefixes = [prefixes];
-
- for (let prefix of prefixes)
- observables.subscribe(list_by_prefix[prefix].observable, cb);
-}
-
-exports.remove_change_listener = function (cb, prefixes=list_prefixes)
-{
- if (typeof(prefixes) === "string")
- prefixes = [prefixes];
-
- for (let prefix of prefixes)
- observables.unsubscribe(list_by_prefix[prefix].observable, cb);
-}
-
-/* Prepare some hepler functions to get elements of a list */
-
-function list_items_it(list, with_values=false)
-{
- return with_values ? list.map.entries() : list.map.keys();
-}
-
-function list_entries_it(list)
-{
- return list_items_it(list, true);
-}
-
-function list_items(list, with_values=false)
-{
- let array = [];
-
- for (let item of list_items_it(list, with_values))
- array.push(item);
-
- return array;
-}
-
-function list_entries(list)
-{
- return list_items(list, true);
-}
-
-/*
- * Below we make additional effort to update map of given kind of items
- * every time an item is added/removed to keep everything coherent.
- */
-async function set_item(item, value, list)
-{
- await lock(list.lock);
- let result = await _set_item(...arguments);
- unlock(list.lock)
- return result;
-}
-async function _set_item(item, value, list)
-{
- const key = list.prefix + item;
- const old_val = list.map.get(item);
- const set_obj = {[key]: value};
- if (old_val === undefined) {
- const items = list_items(list);
- items.push(item);
- set_obj["_" + list.name] = items;
- }
-
- await raw_storage.set(set_obj);
- list.map.set(item, value);
-
- const change = {
- prefix : list.prefix,
- item,
- old_val,
- new_val : value
- };
-
- observables.broadcast(list.observable, change);
-
- return old_val;
-}
-
-// TODO: The actual idea to set value to undefined is good - this way we can
-// also set a new list of items in the same API call. But such key
-// is still stored in the storage. We need to somehow remove it later.
-// For that, we're going to have to store 1 more list of each kind.
-async function remove_item(item, list)
-{
- await lock(list.lock);
- let result = await _remove_item(...arguments);
- unlock(list.lock)
- return result;
-}
-async function _remove_item(item, list)
-{
- const old_val = list.map.get(item);
- if (old_val === undefined)
- return;
-
- const items = list_items(list);
- const index = items.indexOf(item);
- items.splice(index, 1);
-
- await raw_storage.set({
- [list.prefix + item]: undefined,
- ["_" + list.name]: items
- });
- list.map.delete(item);
-
- const change = {
- prefix : list.prefix,
- item,
- old_val,
- new_val : undefined
- };
-
- observables.broadcast(list.observable, change);
-
- return old_val;
-}
-
-// TODO: same as above applies here
-async function replace_item(old_item, new_item, list, new_val=undefined)
-{
- await lock(list.lock);
- let result = await _replace_item(...arguments);
- unlock(list.lock)
- return result;
-}
-async function _replace_item(old_item, new_item, list, new_val=undefined)
-{
- const old_val = list.map.get(old_item);
- if (new_val === undefined) {
- if (old_val === undefined)
- return;
- new_val = old_val;
- } else if (new_val === old_val && new_item === old_item) {
- return old_val;
- }
-
- if (old_item === new_item || old_val === undefined) {
- await _set_item(new_item, new_val, list);
- return old_val;
- }
-
- const items = list_items(list);
- const index = items.indexOf(old_item);
- items[index] = new_item;
-
- await raw_storage.set({
- [list.prefix + old_item]: undefined,
- [list.prefix + new_item]: new_val,
- ["_" + list.name]: items
- });
- list.map.delete(old_item);
-
- const change = {
- prefix : list.prefix,
- item : old_item,
- old_val,
- new_val : undefined
- };
-
- observables.broadcast(list.observable, change);
-
- list.map.set(new_item, new_val);
-
- change.item = new_item;
- change.old_val = undefined;
- change.new_val = new_val;
-
- observables.broadcast(list.observable, change);
-
- return old_val;
-}
-
-/*
- * For scripts, item name is chosen by user, data should be
- * an object containing:
- * - script's url and hash or
- * - script's text or
- * - all three
- */
-
-/*
- * For bags, item name is chosen by user, data is an array of 2-element
- * arrays with type prefix and script/bag names.
- */
-
-/*
- * For pages data argument is an object with properties `allow'
- * and `components'. Item name is url.
- */
-
-exports.set = async function (prefix, item, data)
-{
- return set_item(item, data, list_by_prefix[prefix]);
-}
-
-exports.get = function (prefix, item)
-{
- return list_by_prefix[prefix].map.get(item);
-}
-
-exports.remove = async function (prefix, item)
-{
- return remove_item(item, list_by_prefix[prefix]);
-}
-
-exports.replace = async function (prefix, old_item, new_item,
- new_data=undefined)
-{
- return replace_item(old_item, new_item, list_by_prefix[prefix],
- new_data);
-}
-
-exports.get_all_names = function (prefix)
-{
- return list_items(list_by_prefix[prefix]);
-}
-
-exports.get_all_names_it = function (prefix)
-{
- return list_items_it(list_by_prefix[prefix]);
-}
-
-exports.get_all = function (prefix)
-{
- return list_entries(list_by_prefix[prefix]);
-}
-
-exports.get_all_it = function (prefix)
-{
- return list_entries_it(list_by_prefix[prefix]);
-}
-
-/* Finally, a quick way to wipe all the data. */
-// TODO: maybe delete items in such order that none of them ever references
-// an already-deleted one?
-exports.clear = async function ()
-{
- let lists = list_prefixes.map((p) => list_by_prefix[p]);
-
- for (let list of lists)
- await lock(list.lock);
-
- for (let list of lists) {
-
- let change = {
- prefix : list.prefix,
- new_val : undefined
- };
-
- for (let [item, val] of list_entries_it(list)) {
- change.item = item;
- change.old_val = val;
- observables.broadcast(list.observable, change);
- }
-
- list.map = new Map();
- }
-
- await browser.storage.local.clear();
-
- for (let list of lists)
- unlock(list.lock);
-}
-
-#EXPORT make_once(init) AS get_storage