diff options
Diffstat (limited to 'common/storage_client.mjs')
-rw-r--r-- | common/storage_client.mjs | 186 |
1 files changed, 186 insertions, 0 deletions
diff --git a/common/storage_client.mjs b/common/storage_client.mjs new file mode 100644 index 0000000..8260ad7 --- /dev/null +++ b/common/storage_client.mjs @@ -0,0 +1,186 @@ +/** +* Myext storage through connection (client side) +* +* Copyright (C) 2021 Wojtek Kosior +* +* Dual-licensed under: +* - 0BSD license +* - GPLv3 or (at your option) any later version +*/ + +"use strict"; + +import CONNECTION_TYPE from './connection_types.mjs'; +import {TYPE_PREFIX, list_prefixes} from '/common/stored_types.mjs'; +import make_once from './once.mjs'; +import browser from '/common/browser.mjs'; + +var call_id = 0; +var port; +var calls_waiting = new Map(); + +function set_call_callback(resolve, reject, func, args) +{ + port.postMessage([call_id, func, args]); + calls_waiting.set(call_id++, [resolve, reject]); +} + +async function remote_call(func, args) +{ + return new Promise((resolve, reject) => + set_call_callback(resolve, reject, func, args)); +} + +function handle_message(message) +{ + let callbacks = calls_waiting.get(message.call_id); + if (callbacks === undefined) { + handle_change(message); + return; + } + + let [resolve, reject] = callbacks; + calls_waiting.delete(message.call_id); + if (message.error !== undefined) + setTimeout(reject, 0, message.error); + else + setTimeout(resolve, 0, message.result); +} + +function list(name, prefix) +{ + return {prefix, name, listeners : new Set()}; +} + +var scripts = list("scripts", TYPE_PREFIX.SCRIPT); +var bundles = list("bundles", TYPE_PREFIX.BUNDLE); +var pages = list("pages", TYPE_PREFIX.PAGE); + +const list_by_prefix = { + [TYPE_PREFIX.SCRIPT] : scripts, + [TYPE_PREFIX.BUNDLE] : bundles, + [TYPE_PREFIX.PAGE] : pages +}; + +var resolve_init; + +function handle_first_message(message) +{ + for (let prefix of Object.keys(message)) + list_by_prefix[prefix].map = new Map(message[prefix]); + + port.onMessage.removeListener(handle_first_message); + port.onMessage.addListener(handle_message); + + resolve_init(); +} + +function handle_change(change) +{ + let list = list_by_prefix[change.prefix]; + + if (change.new_val === undefined) + list.map.delete(change.item); + else + list.map.set(change.item, change.new_val); + + for (let listener_callback of list.listeners) + listener_callback(change); +} + +var exports = {}; + +function start_connection(resolve) +{ + resolve_init = resolve; + port = browser.runtime.connect({name : CONNECTION_TYPE.REMOTE_STORAGE}); + port.onMessage.addListener(handle_first_message); +} + +async function init() { + await new Promise((resolve, reject) => start_connection(resolve)); + return exports; +} + +for (let call_name of ["set", "remove", "replace", "clear"]) + exports [call_name] = (...args) => remote_call(call_name, args); + +// TODO: Much of the code below is copy-pasted from /background/storage.mjs. +// This should later be refactored into a separate module +// to avoid duplication. + +/* + * 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); +} + +/* 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); +} + +exports.get = function (prefix, item) +{ + return list_by_prefix[prefix].map.get(item); +} + +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]); +} + +export default make_once(init); |