diff options
Diffstat (limited to 'background')
-rw-r--r-- | background/indexeddb_files_server.js | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/background/indexeddb_files_server.js b/background/indexeddb_files_server.js new file mode 100644 index 0000000..cf78ca6 --- /dev/null +++ b/background/indexeddb_files_server.js @@ -0,0 +1,152 @@ +/** + * This file is part of Haketilo. + * + * Function: Allow content scripts to query IndexedDB through messages to + * background script. + * + * Copyright (C) 2022 Wojtek Kosior <koszko@koszko.org> + * + * 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/indexeddb.js AS haketilodb + +#FROM common/browser.js IMPORT browser + +async function get_resource_files(getting, id) { + if (getting.defs_by_res_id.has(id)) + return; + + getting.defs_by_res_id.set(id, null); + + const definition = await haketilodb.idb_get(getting.tx, "resource", id); + if (!definition) + throw {haketilo_error_type: "missing", id}; + + getting.defs_by_res_id.set(id, definition); + + const file_proms = (definition.scripts || []) + .map(s => haketilodb.idb_get(getting.tx, "files", s.hash_key)); + + const deps_proms = (definition.dependencies || []) + .map(dep_id => get_resource_files(getting, dep_id)); + + const files = (await Promise.all(file_proms)).map(f => f.contents); + getting.files_by_res_id.set(id, files); + + await Promise.all(deps_proms); +} + +function get_files_list(defs_by_res_id, files_by_res_id, root_id) { + const processed = new Set(), to_process = [["start", root_id]], + trace = new Set(), files = []; + + while (to_process.length > 0) { + const [what, id] = to_process.pop(); + if (what === "end") { + trace.delete(id); + files.push(...files_by_res_id.get(id)); + continue; + } + + if (trace.has(id)) + throw {haketilo_error_type: "circular", id}; + + if (processed.has(id)) + continue; + + trace.add(id); + to_process.push(["end", id]); + processed.add(id); + + const ds = (defs_by_res_id.get(id).dependencies || []).reverse(); + ds.forEach(dep_id => to_process.push(["start", dep_id])); + } + + return files; +} + +async function send_resource_files(root_resource_id, send_cb) { + const db = await haketilodb.get(); + const getting = { + defs_by_res_id: new Map(), + files_by_res_id: new Map(), + tx: db.transaction(["files", "resource"]) + }; + + let prom_cbs, prom = new Promise((...cbs) => prom_cbs = cbs); + + getting.tx.onerror = e => prom_cbs[1]({haketilo_error_type: "db", e}); + + get_resource_files(getting, root_resource_id, new Set()).then(...prom_cbs); + + try { + await prom; + const files = get_files_list( + getting.defs_by_res_id, + getting.files_by_res_id, + root_resource_id + ); + var to_send = {files}; + } catch(e) { + if (typeof e === "object" && "haketilo_error_type" in e) { + if (e.haketilo_error_type === "db") { + console.error(e.e); + delete e.e; + } + var to_send = {error: e}; + } else { + console.error(e); + var to_send = {error: {haketilo_error_type: "other"}}; + } + } + + send_cb(to_send); +} + +function on_indexeddb_files_request([type, resource_id], sender, respond_cb) { + if (type !== "indexeddb_files") + return; + + send_resource_files(resource_id, respond_cb); + + return true; +} + +function start() { + browser.runtime.onMessage.addListener(on_indexeddb_files_request); +} +#EXPORT start |