aboutsummaryrefslogtreecommitdiff
path: root/common/storage_client.js
diff options
context:
space:
mode:
Diffstat (limited to 'common/storage_client.js')
-rw-r--r--common/storage_client.js189
1 files changed, 189 insertions, 0 deletions
diff --git a/common/storage_client.js b/common/storage_client.js
new file mode 100644
index 0000000..39ece44
--- /dev/null
+++ b/common/storage_client.js
@@ -0,0 +1,189 @@
+/**
+* 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";
+
+(() => {
+ const CONNECTION_TYPE = window.CONNECTION_TYPE;
+ const TYPE_PREFIX = window.TYPE_PREFIX;
+ const list_prefixes = window.list_prefixes;
+ const make_once = window.make_once;
+ const browser = window.browser;
+
+ 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]);
+ }
+
+ window.get_storage = make_once(init);
+})();