diff options
author | Wojtek Kosior <wk@koszkonutek-tmp.pl.eu.org> | 2021-05-10 18:07:05 +0200 |
---|---|---|
committer | Wojtek Kosior <wk@koszkonutek-tmp.pl.eu.org> | 2021-05-10 18:18:52 +0200 |
commit | 01937dc9d5215ef96ce756e3ccda51bf29032f58 (patch) | |
tree | 609ec5bb48c692796520f7982c06b30633038588 /background/script_injector.mjs | |
download | browser-extension-01937dc9d5215ef96ce756e3ccda51bf29032f58.tar.gz browser-extension-01937dc9d5215ef96ce756e3ccda51bf29032f58.zip |
initial commit
Diffstat (limited to 'background/script_injector.mjs')
-rw-r--r-- | background/script_injector.mjs | 122 |
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; |