aboutsummaryrefslogtreecommitdiff
path: root/background/script_injector.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'background/script_injector.mjs')
-rw-r--r--background/script_injector.mjs122
1 files changed, 122 insertions, 0 deletions
diff --git a/background/script_injector.mjs b/background/script_injector.mjs
new file mode 100644
index 0000000..3298a43
--- /dev/null
+++ b/background/script_injector.mjs
@@ -0,0 +1,122 @@
+/**
+* Myext script injector
+*
+* Copyright (C) 2021 Wojtek Kosior
+*
+* Dual-licensed under:
+* - 0BSD license
+* - GPLv3 or (at your option) any later version
+*/
+
+import {TYPE_PREFIX} from '/common/stored_types.mjs';
+import sha256 from './sha256.mjs';
+import {url_item} from './ResponseProcessor.mjs';
+import get_storage from './storage.mjs';
+
+"use strict";
+
+var storage;
+
+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);
+ }
+}
+
+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);
+ }
+}
+
+// TODO: parallelize script fetching
+// TODO: guard against infinite recursion
+
+async function inject_scripts_rec(components, doc)
+{
+ for (let [prefix, name] of components) {
+ if (prefix === TYPE_PREFIX.BUNDLE) {
+ var bundle = storage.get(TYPE_PREFIX.BUNDLE, name);
+
+ if (bundle === undefined) {
+ console.log(`no bundle in storage for key ${elem_key}`);
+ continue;
+ }
+ await inject_scripts_rec(bundle, doc);
+ } else {
+ let script_text = await get_script_text(name,);
+ if (script_text === undefined)
+ continue;
+
+ let script = doc.createElement("script");
+ script.textContent = script_text;
+ doc.body.appendChild(script);
+ }
+ }
+}
+
+async function inject_scripts(url, doc)
+{
+ storage = await get_storage();
+
+ url = url_item(url);
+
+ let components = storage.get(TYPE_PREFIX.PAGE, url);
+
+ if (components === undefined) {
+ console.log(`got nothing for ${url}`);
+ return
+ } else {
+ console.log(`got ${components.length} component(s) for ${url}`);
+ }
+
+ await inject_scripts_rec(components, doc);
+}
+
+export default inject_scripts;