aboutsummaryrefslogtreecommitdiff
path: root/background/page_actions_server.js
blob: 2a0b858988e9a1187b2f2f61d533086b87dce268 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
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_bags = new Set();

	send_scripts_rec(components, port, processed_bags);
    }

    // TODO: parallelize script fetching
    async function send_scripts_rec(components, port, processed_bags)
    {
	for (let [prefix, name] of components) {
	    if (prefix === TYPE_PREFIX.BAG) {
		if (processed_bags.has(name)) {
		    console.log(`preventing recursive inclusion of bag ${name}`);
		    continue;
		}

		var bag = storage.get(TYPE_PREFIX.BAG, name);

		if (bag === undefined) {
		    console.log(`no bag in storage for key ${name}`);
		    continue;
		}

		processed_bags.add(name);
		await send_scripts_rec(bag, port, processed_bags);
		processed_bags.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;
})();