diff options
Diffstat (limited to 'background/page_actions_server.js')
-rw-r--r-- | background/page_actions_server.js | 149 |
1 files changed, 149 insertions, 0 deletions
diff --git a/background/page_actions_server.js b/background/page_actions_server.js new file mode 100644 index 0000000..fbb672e --- /dev/null +++ b/background/page_actions_server.js @@ -0,0 +1,149 @@ +/** +* Myext serving of page actions to content scripts +* +* Copyright (C) 2021 Wojtek Kosior +* +* Dual-licensed under: +* - 0BSD license +* - GPLv3 or (at your option) any later version +*/ + +"use strict"; + +(() => { + const get_storage = window.get_storage; + const TYPE_PREFIX = window.TYPE_PREFIX; + const CONNECTION_TYPE = window.CONNECTION_TYPE; + const browser = window.browser; + const listen_for_connection = window.listen_for_connection; + const url_item = window.url_item; + const sha256 = window.sha256; + + var storage; + var handler; + + function send_scripts(url, port) + { + let settings = storage.get(TYPE_PREFIX.PAGE, url_item(url)); + if (settings === undefined) + return; + + let components = settings.components; + let processed_bundles = new Set(); + + send_scripts_rec(components, port, processed_bundles); + } + + // TODO: parallelize script fetching + async function send_scripts_rec(components, port, processed_bundles) + { + for (let [prefix, name] of components) { + if (prefix === TYPE_PREFIX.BUNDLE) { + if (processed_bundles.has(name)) { + console.log(`preventing recursive inclusion of bundle ${name}`); + continue; + } + + var bundle = storage.get(TYPE_PREFIX.BUNDLE, name); + + if (bundle === undefined) { + console.log(`no bundle in storage for key ${name}`); + continue; + } + + processed_bundles.add(name); + await send_scripts_rec(bundle, port, processed_bundles); + processed_bundles.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); + } + } + + function ajax_callback() + { + if (this.readyState == 4) + this.resolve_callback(this); + } + + function initiate_ajax_request(resolve, method, url) + { + var xhttp = new XMLHttpRequest(); + xhttp.resolve_callback = resolve; + xhttp.onreadystatechange = ajax_callback; + xhttp.open(method, url, true); + xhttp.send(); + } + + function make_ajax_request(method, url) + { + return new Promise((resolve, reject) => + initiate_ajax_request(resolve, method, url)); + } + + 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]); + let url = message.url; + console.log({url}); + send_scripts(url, port); + } + + 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); + } + + window.start_page_actions_server = start; +})(); |