summaryrefslogtreecommitdiff
path: root/background
diff options
context:
space:
mode:
Diffstat (limited to 'background')
-rw-r--r--background/background.js (renamed from background/policy_injector.js)58
-rw-r--r--background/broadcast_broker.js7
-rw-r--r--background/main.js238
-rw-r--r--background/page_actions_server.js149
-rw-r--r--background/patterns_query_manager.js28
-rw-r--r--background/storage.js359
-rw-r--r--background/storage_server.js95
-rw-r--r--background/webrequest.js2
8 files changed, 39 insertions, 897 deletions
diff --git a/background/policy_injector.js b/background/background.js
index 36c950e..90826ef 100644
--- a/background/policy_injector.js
+++ b/background/background.js
@@ -1,10 +1,9 @@
/**
* This file is part of Haketilo.
*
- * Function: Injecting policy to page by modifying HTTP headers.
+ * Function: Background scripts - main script.
*
- * Copyright (C) 2021, Wojtek Kosior
- * Copyright (C) 2021, jahoti
+ * Copyright (C) 2022 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
@@ -37,51 +36,32 @@
* 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.
*/
-#FROM common/misc.js IMPORT csp_header_regex
+#IMPORT background/patterns_query_manager.js
+#IMPORT background/webrequest.js
+#IMPORT background/CORS_bypass_server.js
+#IMPORT background/broadcast_broker.js
+#IMPORT background/indexeddb_files_server.js
-/* Re-enable the import below once nonce stuff here is ready */
-#IF NEVER
#FROM common/misc.js IMPORT gen_nonce
-#ENDIF
-
-/* CSP rule that blocks scripts according to policy's needs. */
-function make_csp_rule(policy)
-{
- let rule = "prefetch-src 'none'; script-src-attr 'none';";
- const script_src = policy.nonce !== undefined ?
- `'nonce-${policy.nonce}'` : "'none'";
- rule += ` script-src ${script_src}; script-src-elem ${script_src};`;
- return rule;
-}
-
-function inject_csp_headers(headers, policy)
-{
- let csp_headers;
-
- if (policy.payload) {
- headers = headers.filter(h => !csp_header_regex.test(h.name));
-
- // TODO: make CSP rules with nonces and facilitate passing them to
- // content scripts via dynamic content script registration or
- // synchronous XHRs
- // policy.nonce = gen_nonce();
- }
+function main() {
+ const secret = gen_nonce();
- if (!policy.allow && (policy.nonce || !policy.payload)) {
- headers.push({
- name: "content-security-policy",
- value: make_csp_rule(policy)
- });
- }
+ /*
+ * Some other services depend on IndexedDB which depende on broadcast
+ * broker, hence we start it first.
+ */
+ broadcast_broker.start();
+ CORS_bypass_server.start();
+ indexeddb_files_server.start();
- return headers;
+ patterns_query_manager.start(secret);
+ webrequest.start(secret);
}
-#EXPORT inject_csp_headers
+main();
diff --git a/background/broadcast_broker.js b/background/broadcast_broker.js
index 79a1bb7..d8a0e28 100644
--- a/background/broadcast_broker.js
+++ b/background/broadcast_broker.js
@@ -42,8 +42,6 @@
* proprietary program, I am not going to enforce this in court.
*/
-#IMPORT common/connection_types.js AS CONNECTION_TYPE
-
#FROM common/message_server.js IMPORT listen_for_connection
let next_id = 1;
@@ -169,8 +167,7 @@ function remove_broadcast_sender(sender_ctx)
function start()
{
- listen_for_connection(CONNECTION_TYPE.BROADCAST_SEND, new_broadcast_sender);
- listen_for_connection(CONNECTION_TYPE.BROADCAST_LISTEN,
- new_broadcast_listener);
+ listen_for_connection("broadcast_send", new_broadcast_sender);
+ listen_for_connection("broadcast_listen", new_broadcast_listener);
}
#EXPORT start
diff --git a/background/main.js b/background/main.js
deleted file mode 100644
index 61c96ac..0000000
--- a/background/main.js
+++ /dev/null
@@ -1,238 +0,0 @@
-/**
- * This file is part of Haketilo.
- *
- * Function: Main background script.
- *
- * Copyright (C) 2021 Wojtek Kosior <koszko@koszko.org>
- * Copyright (C) 2021 Jahoti <jahoti@envs.net>
- *
- * 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_light.js AS light_storage
-
-#IMPORT background/storage_server.js
-#IMPORT background/page_actions_server.js
-#IMPORT background/stream_filter.js
-
-#FROM common/browser.js IMPORT browser
-#FROM common/stored_types.js IMPORT TYPE_PREFIX
-#FROM background/storage.js IMPORT get_storage
-#FROM common/misc.js IMPORT is_privileged_url
-#FROM common/settings_query.js IMPORT query_best
-#FROM background/policy_injector.js IMPORT inject_csp_headers
-
-const initial_data = (
-#INCLUDE_VERBATIM default_settings.json
-);
-
-storage_server.start();
-page_actions_server.start();
-
-async function init_ext(install_details)
-{
- if (install_details.reason != "install")
- return;
-
- let storage = await get_storage();
-
- await storage.clear();
-
- /* Below we add sample settings to the extension. */
- for (let setting of initial_data) {
- let [key, value] = Object.entries(setting)[0];
- storage.set(key[0], key.substring(1), value);
- }
-}
-
-browser.runtime.onInstalled.addListener(init_ext);
-
-/*
- * The function below implements a more practical interface for what it does by
- * wrapping the old query_best() function.
- */
-function decide_policy_for_url(storage, policy_observable, url)
-{
- if (storage === undefined)
- return {allow: false};
-
- const settings =
- {allow: policy_observable !== undefined && policy_observable.value};
-
- const [pattern, queried_settings] = query_best(storage, url);
-
- if (queried_settings) {
- settings.payload = queried_settings.components;
- settings.allow = !!queried_settings.allow && !settings.payload;
- settings.pattern = pattern;
- }
-
- return settings;
-}
-
-let storage;
-let policy_observable = {};
-
-function sanitize_web_page(details)
-{
- const url = details.url;
- if (is_privileged_url(details.url))
- return;
-
- const policy =
- decide_policy_for_url(storage, policy_observable, details.url);
-
- let headers = details.responseHeaders;
-
- headers = inject_csp_headers(headers, policy);
-
- let skip = false;
- for (const header of headers) {
- if ((header.name.toLowerCase().trim() === "content-disposition" &&
- /^\s*attachment\s*(;.*)$/i.test(header.value)))
- skip = true;
- }
- skip = skip || (details.statusCode >= 300 && details.statusCode < 400);
-
- if (!skip) {
- /* Check for API availability. */
- if (browser.webRequest.filterResponseData)
- headers = stream_filter.apply(details, headers, policy);
- }
-
- return {responseHeaders: headers};
-}
-
-const request_url_regex = /^[^?]*\?url=(.*)$/;
-const redirect_url_template = browser.runtime.getURL("dummy") + "?settings=";
-
-function synchronously_smuggle_policy(details)
-{
- /*
- * Content script will make a synchronous XmlHttpRequest to extension's
- * `dummy` file to query settings for given URL. We smuggle that
- * information in query parameter of the URL we redirect to.
- * A risk of fingerprinting arises if a page with script execution allowed
- * guesses the dummy file URL and makes an AJAX call to it. It is currently
- * a problem in ManifestV2 Chromium-family port of Haketilo because Chromium
- * uses predictable URLs for web-accessible resources. We plan to fix it in
- * the future ManifestV3 port.
- */
- if (details.type !== "xmlhttprequest")
- return {cancel: true};
-
- console.debug(`Settings queried using XHR for '${details.url}'.`);
-
- let policy = {allow: false};
-
- try {
- /*
- * request_url should be of the following format:
- * <url_for_extension's_dummy_file>?url=<valid_urlencoded_url>
- */
- const match = request_url_regex.exec(details.url);
- const queried_url = decodeURIComponent(match[1]);
-
- if (details.initiator && !queried_url.startsWith(details.initiator)) {
- console.warn(`Blocked suspicious query of '${url}' by '${details.initiator}'. This might be the result of page fingerprinting the browser.`);
- return {cancel: true};
- }
-
- policy = decide_policy_for_url(storage, policy_observable, queried_url);
- } catch (e) {
- console.warn(`Bad request! Expected ${browser.runtime.getURL("dummy")}?url=<valid_urlencoded_url>. Got ${request_url}. This might be the result of page fingerprinting the browser.`);
- }
-
- const encoded_policy = encodeURIComponent(JSON.stringify(policy));
-
- return {redirectUrl: redirect_url_template + encoded_policy};
-}
-
-const all_types = [
- "main_frame", "sub_frame", "stylesheet", "script", "image", "font",
- "object", "xmlhttprequest", "ping", "csp_report", "media", "websocket",
- "other", "main_frame", "sub_frame"
-];
-
-async function start_webRequest_operations()
-{
- storage = await get_storage();
-
-#IF CHROMIUM
- const extra_opts = ["blocking", "extraHeaders"];
-#ELSE
- const extra_opts = ["blocking"];
-#ENDIF
-
- browser.webRequest.onHeadersReceived.addListener(
- sanitize_web_page,
- {urls: ["<all_urls>"], types: ["main_frame", "sub_frame"]},
- extra_opts.concat("responseHeaders")
- );
-
- const dummy_url_pattern = browser.runtime.getURL("dummy") + "?url=*";
- browser.webRequest.onBeforeRequest.addListener(
- synchronously_smuggle_policy,
- {urls: [dummy_url_pattern], types: ["xmlhttprequest"]},
- extra_opts
- );
-
- policy_observable = await light_storage.observe_var("default_allow");
-}
-
-start_webRequest_operations();
-
-#IF MOZILLA
-const code = `\
-console.warn("Hi, I'm Mr Dynamic!");
-
-console.debug("let's see how window.haketilo_exports looks like now");
-
-console.log("haketilo_exports", window.haketilo_exports);
-`
-
-async function test_dynamic_content_scripts()
-{
- browser.contentScripts.register({
- "js": [{code}],
- "matches": ["<all_urls>"],
- "allFrames": true,
- "runAt": "document_start"
-});
-}
-
-test_dynamic_content_scripts();
-#ENDIF
diff --git a/background/page_actions_server.js b/background/page_actions_server.js
deleted file mode 100644
index 67c9b9e..0000000
--- a/background/page_actions_server.js
+++ /dev/null
@@ -1,149 +0,0 @@
-/**
- * This file is part of Haketilo.
- *
- * Function: Serving page actions to content scripts.
- *
- * 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_light.js AS light_storage
-#IMPORT common/connection_types.js AS CONNECTION_TYPE
-
-#FROM common/browser.js IMPORT browser
-#FROM common/message_server.js IMPORT listen_for_connection
-#FROM background/storage.js IMPORT get_storage
-#FROM common/stored_types.js IMPORT TYPE_PREFIX
-#FROM common/sha256.js IMPORT sha256
-#FROM common/ajax.js IMPORT make_ajax_request
-
-var storage;
-var handler;
-
-// TODO: parallelize script fetching
-async function send_scripts(components, port, processed_bags)
-{
- for (let [prefix, name] of components) {
- if (prefix === TYPE_PREFIX.BAG) {
- if (processed_bags.has(name)) {
- console.log(`preventing recursive inclusion of bag ${name}`);
- continue;
- }
-
- var bag = storage.get(TYPE_PREFIX.BAG, name);
-
- if (bag === undefined) {
- console.log(`no bag in storage for key ${name}`);
- continue;
- }
-
- processed_bags.add(name);
- await send_scripts(bag, port, processed_bags);
-
- processed_bags.delete(name);
- } else {
- let script_text = await get_script_text(name);
- if (script_text === undefined)
- continue;
-
- port.postMessage(["inject", [script_text]]);
- }
- }
-}
-
-async function get_script_text(script_name)
-{
- try {
- let script_data = storage.get(TYPE_PREFIX.SCRIPT, script_name);
- if (script_data === undefined) {
- console.log(`missing data for ${script_name}`);
- return;
- }
- let script_text = script_data.text;
- if (!script_text)
- script_text = await fetch_remote_script(script_data);
- return script_text;
- } catch (e) {
- console.log(e);
- }
-}
-
-async function fetch_remote_script(script_data)
-{
- try {
- let xhttp = await make_ajax_request("GET", script_data.url);
- if (xhttp.status === 200) {
- let computed_hash = sha256(xhttp.responseText);
- if (computed_hash !== script_data.hash) {
- console.log(`Bad hash for ${script_data.url}\n got ${computed_hash} instead of ${script_data.hash}`);
- return;
- }
- return xhttp.responseText;
- } else {
- console.log("script not fetched: " + script_data.url);
- return;
- }
- } catch (e) {
- console.log(e);
- }
-}
-
-function handle_message(port, message, handler)
-{
- port.onMessage.removeListener(handler[0]);
- console.debug(`Loading payload '${message.payload}'.`);
-
- const processed_bags = new Set();
-
- send_scripts([message.payload], port, processed_bags);
-}
-
-function new_connection(port)
-{
- console.log("new page actions connection!");
- let handler = [];
- handler.push(m => handle_message(port, m, handler));
- port.onMessage.addListener(handler[0]);
-}
-
-async function start()
-{
- storage = await get_storage();
-
- listen_for_connection(CONNECTION_TYPE.PAGE_ACTIONS, new_connection);
-}
-#EXPORT start
diff --git a/background/patterns_query_manager.js b/background/patterns_query_manager.js
index 3b74ee9..9de9d35 100644
--- a/background/patterns_query_manager.js
+++ b/background/patterns_query_manager.js
@@ -64,8 +64,7 @@ let registered_script = null;
let script_update_occuring = false;
let script_update_needed;
-async function update_content_script()
-{
+async function update_content_script() {
if (script_update_occuring)
return;
@@ -98,8 +97,7 @@ if (this.haketilo_content_script_main)
}
#ENDIF
-function register(kind, object)
-{
+function register(kind, object) {
if (kind === "mappings") {
for (const [pattern, resource] of Object.entries(object.payloads))
pqt.register(tree, pattern, object.identifier, resource);
@@ -118,8 +116,7 @@ function register(kind, object)
#ENDIF
}
-function changed(kind, change)
-{
+function changed(kind, change) {
const old_version = currently_registered.get(change.key);
if (old_version !== undefined) {
if (kind === "mappings") {
@@ -143,8 +140,19 @@ function changed(kind, change)
#ENDIF
}
-async function start(secret_)
-{
+function setting_changed(change) {
+ if (change.key !== "default_allow")
+ return;
+
+ default_allow.value = (change.new_val || {}).value;
+
+#IF MOZILLA || MV3
+ script_update_needed = true;
+ setTimeout(update_content_script, 0);
+#ENDIF
+}
+
+async function start(secret_) {
secret = secret_;
const [mapping_tracking, initial_mappings] =
@@ -155,9 +163,9 @@ async function start(secret_)
initial_mappings.forEach(m => register("mappings", m));
initial_blocking.forEach(b => register("blocking", b));
- const set_allow_val = ch => default_allow.value = (ch.new_val || {}).value;
const [setting_tracking, initial_settings] =
- await haketilodb.track.settings(set_allow_val);
+ await haketilodb.track.settings(setting_changed);
+
for (const setting of initial_settings) {
if (setting.name === "default_allow")
Object.assign(default_allow, setting);
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
diff --git a/background/storage_server.js b/background/storage_server.js
deleted file mode 100644
index 5aa07bf..0000000
--- a/background/storage_server.js
+++ /dev/null
@@ -1,95 +0,0 @@
-/**
- * This file is part of Haketilo.
- *
- * Function: Storage through messages (server side).
- *
- * 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/connection_types.js AS CONNECTION_TYPE
-
-#FROM common/message_server.js IMPORT listen_for_connection
-#FROM background/storage.js IMPORT get_storage
-#FROM common/stored_types.js IMPORT list_prefixes
-
-var storage;
-
-async function handle_remote_call(port, message)
-{
- let [call_id, func, args] = message;
-
- try {
- let result = await Promise.resolve(storage[func](...args));
- port.postMessage({call_id, result});
- } catch (error) {
- error = error + '';
- port.postMessage({call_id, error});
- }
-}
-
-function remove_storage_listener(cb)
-{
- storage.remove_change_listener(cb);
-}
-
-function new_connection(port)
-{
- console.log("new remote storage connection!");
-
- const message = {};
- for (const prefix of list_prefixes)
- message[prefix] = storage.get_all(prefix);
-
- port.postMessage(message);
-
- let handle_change = change => port.postMessage(change);
-
- storage.add_change_listener(handle_change);
-
- port.onMessage.addListener(m => handle_remote_call(port, m));
- port.onDisconnect.addListener(() =>
- remove_storage_listener(handle_change));
-}
-
-async function start()
-{
- storage = await get_storage();
-
- listen_for_connection(CONNECTION_TYPE.REMOTE_STORAGE, new_connection);
-}
-#EXPORT start
diff --git a/background/webrequest.js b/background/webrequest.js
index bd091dc..cb89a3d 100644
--- a/background/webrequest.js
+++ b/background/webrequest.js
@@ -172,7 +172,5 @@ async function start(secret_)
extra_opts
);
#ENDIF
-
- await track_default_allow();
}
#EXPORT start