From 6247f163d3ca89d5570450ac7ac8fd18f73bb74b Mon Sep 17 00:00:00 2001 From: Wojtek Kosior Date: Thu, 2 Sep 2021 18:35:49 +0200 Subject: enable toggling of global script blocking policy\n\nThis commit also introduces `light_storage' module which is later going to replace the storage code we use right now.\nAlso included is a hack to properly display scrollbars under Mozilla (needs testing on newer Mozilla browsers). --- common/observable.js | 28 +++++------ common/storage_light.js | 129 ++++++++++++++++++++++++++++++++++++++++++++++++ common/storage_raw.js | 11 +++-- 3 files changed, 148 insertions(+), 20 deletions(-) create mode 100644 common/storage_light.js (limited to 'common') diff --git a/common/observable.js b/common/observable.js index 1fb0b0a..02f1c1b 100644 --- a/common/observable.js +++ b/common/observable.js @@ -6,28 +6,22 @@ * Redistribution terms are gathered in the `copyright' file. */ -function make() -{ - return new Set(); -} +const make = (value=undefined) => ({value, listeners: new Set()}); +const subscribe = (observable, cb) => observable.listeners.add(cb); +const unsubscribe = (observable, cb) => observable.listeners.delete(cb); -function subscribe(observable, cb) -{ - observable.add(cb); -} - -function unsubscribe(observable, cb) -{ - observable.delete(cb); -} +const silent_set = (observable, value) => observable.value = value; +const broadcast = (observable, ...values) => + observable.listeners.forEach(cb => cb(...values)); -function broadcast(observable, event) +function set(observable, value) { - for (const callback of observable) - callback(event); + const old_value = observable.value; + silent_set(observable, value); + broadcast(observable, value, old_value); } -const observables = {make, subscribe, unsubscribe, broadcast}; +const observables = {make, subscribe, unsubscribe, broadcast, silent_set, set}; /* * EXPORTS_START diff --git a/common/storage_light.js b/common/storage_light.js new file mode 100644 index 0000000..067bf0c --- /dev/null +++ b/common/storage_light.js @@ -0,0 +1,129 @@ +/** + * part of Hachette + * Storage manager, lighter than the previous one. + * + * Copyright (C) 2021 Wojtek Kosior + * Redistribution terms are gathered in the `copyright' file. + */ + +/* + * IMPORTS_START + * IMPORT TYPE_PREFIX + * IMPORT raw_storage + * IMPORT is_mozilla + * IMPORT observables + */ + +const reg_spec = new Set(["\\", "[", "]", "(", ")", "{", "}", ".", "*", "+"]); +const escape_reg_special = c => reg_spec.has(c) ? "\\" + c : c; + +function make_regex(name) +{ + return new RegExp(`^${name.split("").map(escape_reg_special).join("")}\$`); +} + +const listeners_by_callback = new Map(); + +function listen(callback, prefix, name) +{ + let by_prefix = listeners_by_callback.get(callback); + if (!by_prefix) { + by_prefix = new Map(); + listeners_by_callback.set(callback, by_prefix); + } + + let by_name = by_prefix.get(prefix); + if (!by_name) { + by_name = new Map(); + by_prefix.set(prefix, by_name); + } + + let name_reg = by_name.get(name); + if (!name_reg) { + name_reg = name.test ? name : make_regex(name); + by_name.set(name, name_reg); + } +} + +function no_listen(callback, prefix, name) +{ + const by_prefix = listeners_by_callback.get(callback); + if (!by_prefix) + return; + + const by_name = by_prefix.get(prefix); + if (!by_name) + return; + + const name_reg = by_name.get(name); + if (!name_reg) + return; + + by_name.delete(name); + + if (by_name.size === 0) + by_prefix.delete(prefix); + + if (by_prefix.size === 0) + listeners_by_callback.delete(callback); +} + +function storage_change_callback(changes, area) +{ + if (is_mozilla && area !== "local") + {console.log("area", area);return;} + + for (const item of Object.keys(changes)) { + for (const [callback, by_prefix] of listeners_by_callback.entries()) { + const by_name = by_prefix.get(item[0]); + if (!by_name) + continue; + + for (const reg of by_name.values()) { + if (!reg.test(item.substring(1))) + continue; + + try { + callback(item, changes[item]); + } catch(e) { + console.error(e); + } + } + } + } +} + +raw_storage.listen(storage_change_callback); + + +const created_observables = new Map(); + +async function observe(prefix, name) +{ + const observable = observables.make(); + const callback = (it, ch) => observables.set(observable, ch.newValue); + listen(callback, prefix, name); + created_observables.set(observable, [callback, prefix, name]); + observables.silent_set(observable, await raw_storage.get(prefix + name)); + + return observable; +} + +const observe_var = name => observe(TYPE_PREFIX.VAR, name); + +function no_observe(observable) +{ + no_listen(...created_observables.get(observable) || []); + created_observables.delete(observable); +} + +const light_storage = {}; +Object.assign(light_storage, raw_storage); +Object.assign(light_storage, + {listen, no_listen, observe, observe_var, no_observe}); + +/* + * EXPORTS_START + * EXPORT light_storage + * EXPORTS_END + */ diff --git a/common/storage_raw.js b/common/storage_raw.js index 9ce3980..4c02ee4 100644 --- a/common/storage_raw.js +++ b/common/storage_raw.js @@ -26,8 +26,9 @@ async function get(key) async function set(key_or_object, value) { - return browser.storage.local.set(typeof key_or_object === "object" ? - key_or_object : {[key]: value}); + const arg = typeof key_or_object === "object" ? + key_or_object : {[key_or_object]: value}; + return browser.storage.local.set(arg); } async function set_var(name, value) @@ -40,7 +41,11 @@ async function get_var(name) return get(TYPE_PREFIX.VAR + name); } -const raw_storage = {get, set, get_var, set_var}; +const on_changed = browser.storage.onChanged || browser.storage.local.onChanged; +const listen = cb => on_changed.addListener(cb); +const no_listen = cb => on_changed.removeListener(cb); + +const raw_storage = {get, set, get_var, set_var, listen, no_listen}; /* * EXPORTS_START -- cgit v1.2.3