aboutsummaryrefslogtreecommitdiff
path: root/background/page_actions_server.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'background/page_actions_server.mjs')
-rw-r--r--background/page_actions_server.mjs145
1 files changed, 145 insertions, 0 deletions
diff --git a/background/page_actions_server.mjs b/background/page_actions_server.mjs
new file mode 100644
index 0000000..5fb4924
--- /dev/null
+++ b/background/page_actions_server.mjs
@@ -0,0 +1,145 @@
+/**
+* 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
+*/
+
+import get_storage from './storage.mjs';
+import {TYPE_PREFIX} from '/common/stored_types.mjs';
+import CONNECTION_TYPE from '/common/connection_types.mjs';
+import browser from '/common/browser.mjs';
+import listen_for_connection from './message_server.mjs';
+import url_item from './url_item.mjs';
+import sha256 from './sha256.mjs';
+
+"use strict";
+
+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]);
+}
+
+export default async function start()
+{
+ storage = await get_storage();
+
+ listen_for_connection(CONNECTION_TYPE.PAGE_ACTIONS, new_connection);
+}