diff options
Diffstat (limited to 'background/storage.mjs')
-rw-r--r-- | background/storage.mjs | 380 |
1 files changed, 0 insertions, 380 deletions
diff --git a/background/storage.mjs b/background/storage.mjs deleted file mode 100644 index 00b1ace..0000000 --- a/background/storage.mjs +++ /dev/null @@ -1,380 +0,0 @@ -/** -* Myext storage manager -* -* Copyright (C) 2021 Wojtek Kosior -* -* Dual-licensed under: -* - 0BSD license -* - GPLv3 or (at your option) any later version -*/ - -import {TYPE_PREFIX, TYPE_NAME, list_prefixes} from '/common/stored_types.mjs'; -import {make_lock, lock, unlock} from '/common/lock.mjs'; -import make_once from '/common/once.mjs'; -import browser from '/common/browser.mjs'; - -"use strict"; - -var exports = {}; - -/* We're yet to decide how to handle errors... */ - -/* Here are some basic wrappers for storage API functions */ - -async function get(key) -{ - try { - /* Fix for fact that Chrome does not use promises here */ - let promise = window.browser === undefined ? - new Promise((resolve, reject) => - chrome.storage.local.get(key, val => resolve(val))) : - browser.storage.local.get(key); - - return (await promise)[key]; - } catch (e) { - console.log(e); - } -} - -async function set(key, value) -{ - try { - return browser.storage.local.set({[key]: value}); - } catch (e) { - console.log(e); - } -} - -async function setn(keys_and_values) -{ - let obj = Object(); - while (keys_and_values.length > 1) { - let value = keys_and_values.pop(); - let key = keys_and_values.pop(); - obj[key] = value; - } - - try { - return browser.storage.local.set(obj); - } catch (e) { - console.log(e); - } -} - -async function set_var(name, value) -{ - return set(TYPE_PREFIX.VAR + name, value); -} - -async function get_var(name) -{ - return get(TYPE_PREFIX.VAR + name); -} - -/* A special case of a persisted variable is one that contains list of items. */ - -async function get_list_var(name) -{ - let list = await 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 get(prefix + item)); - - return {map, prefix, name, listeners : new Set(), lock : make_lock()}; -} - -var pages; -var bundles; -var scripts; - -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) - list_by_prefix[prefix].listeners.add(cb); -} - -exports.remove_change_listener = function (cb, prefixes=list_prefixes) -{ - if (typeof(prefixes) === "string") - prefixes = [prefixes]; - - for (let prefix of prefixes) - list_by_prefix[prefix].listeners.delete(cb); -} - -function broadcast_change(change, list) -{ - for (let listener_callback of list.listeners) - listener_callback(change); -} - -/* 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) -{ - let key = list.prefix + item; - let old_val = list.map.get(item); - if (old_val === undefined) { - let items = list_items(list); - items.push(item); - await setn([key, value, "_" + list.name, items]); - } else { - await set(key, value); - } - - list.map.set(item, value) - - let change = { - prefix : list.prefix, - item, - old_val, - new_val : value - }; - - broadcast_change(change, list); - - 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) -{ - let old_val = list.map.get(item); - if (old_val === undefined) - return; - - let key = list.prefix + item; - let items = list_items(list); - let index = items.indexOf(item); - items.splice(index, 1); - - await setn([key, undefined, "_" + list.name, items]); - - list.map.delete(item); - - let change = { - prefix : list.prefix, - item, - old_val, - new_val : undefined - }; - - broadcast_change(change, list); - - 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) -{ - let 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; - } - - let new_key = list.prefix + new_item; - let old_key = list.prefix + old_item; - let items = list_items(list); - let index = items.indexOf(old_item); - items[index] = new_item; - await setn([old_key, undefined, new_key, new_val, "_" + list.name, items]); - - list.map.delete(old_item); - - let change = { - prefix : list.prefix, - item : old_item, - old_val, - new_val : undefined - }; - - broadcast_change(change, list); - - list.map.set(new_item, new_val); - - change.item = new_item; - change.old_val = undefined; - change.new_val = new_val; - - broadcast_change(change, list); - - 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 bundles, item name is chosen by user, data is an array of 2-element - * arrays with type prefix and script/bundle names. - */ - -/* For pages data argument is same as for bundles. 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; - broadcast_change(change, list); - } - - list.map = new Map(); - } - - await browser.storage.local.clear(); - - for (let list of lists) - unlock(list.lock); -} - -export default make_once(init); |