Project

General

Profile

Download (4.46 KB) Statistics
| Branch: | Tag: | Revision:

haketilo / background / page_actions_server.js @ 6bae771d

1
/**
2
 * Myext serving of page actions to content scripts
3
 *
4
 * Copyright (C) 2021 Wojtek Kosior
5
 *
6
 * This code is dual-licensed under:
7
 * - Asshole license 1.0,
8
 * - GPLv3 or (at your option) any later version
9
 *
10
 * "dual-licensed" means you can choose the license you prefer.
11
 *
12
 * This code is released under a permissive license because I disapprove of
13
 * copyright and wouldn't be willing to sue a violator. Despite not putting
14
 * this code under copyleft (which is also kind of copyright), I do not want
15
 * it to be made proprietary. Hence, the permissive alternative to GPL is the
16
 * Asshole license 1.0 that allows me to call you an asshole if you use it.
17
 * This means you're legally ok regardless of how you utilize this code but if
18
 * you make it into something nonfree, you're an asshole.
19
 *
20
 * You should have received a copy of both GPLv3 and Asshole license 1.0
21
 * together with this code. If not, please see:
22
 * - https://www.gnu.org/licenses/gpl-3.0.en.html
23
 * - https://koszko.org/asshole-license.txt
24
 */
25

    
26
"use strict";
27

    
28
(() => {
29
    const get_storage = window.get_storage;
30
    const TYPE_PREFIX = window.TYPE_PREFIX;
31
    const CONNECTION_TYPE = window.CONNECTION_TYPE;
32
    const browser = window.browser;
33
    const listen_for_connection = window.listen_for_connection;
34
    const sha256 = window.sha256;
35
    const get_query_best = window.get_query_best;
36

    
37
    var storage;
38
    var query_best;
39
    var handler;
40

    
41
    function send_scripts(url, port)
42
    {
43
	let [pattern, settings] = query_best(url);
44
	if (settings === undefined)
45
	    return;
46

    
47
	let components = settings.components;
48
	let processed_bags = new Set();
49

    
50
	if (components !== undefined)
51
	    send_scripts_rec([components], port, processed_bags);
52
    }
53

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

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

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

    
71
		processed_bags.add(name);
72
		await send_scripts_rec(bag, port, processed_bags);
73
		processed_bags.delete(name);
74
	    } else {
75
		let script_text = await get_script_text(name);
76
		if (script_text === undefined)
77
		    continue;
78

    
79
		port.postMessage({inject : [script_text]});
80
	    }
81
	}
82
    }
83

    
84
    async function get_script_text(script_name)
85
    {
86
	try {
87
	    let script_data = storage.get(TYPE_PREFIX.SCRIPT, script_name);
88
	    if (script_data === undefined) {
89
		console.log(`missing data for ${script_name}`);
90
		return;
91
	    }
92
	    let script_text = script_data.text;
93
	    if (!script_text)
94
		script_text = await fetch_remote_script(script_data);
95
	    return script_text;
96
	} catch (e) {
97
	    console.log(e);
98
	}
99
    }
100

    
101
    function ajax_callback()
102
    {
103
	if (this.readyState == 4)
104
	    this.resolve_callback(this);
105
    }
106

    
107
    function initiate_ajax_request(resolve, method, url)
108
    {
109
	var xhttp = new XMLHttpRequest();
110
	xhttp.resolve_callback = resolve;
111
	xhttp.onreadystatechange = ajax_callback;
112
	xhttp.open(method, url, true);
113
	xhttp.send();
114
    }
115

    
116
    function make_ajax_request(method, url)
117
    {
118
	return new Promise((resolve, reject) =>
119
			   initiate_ajax_request(resolve, method, url));
120
    }
121

    
122
    async function fetch_remote_script(script_data)
123
    {
124
	try {
125
	    let xhttp = await make_ajax_request("GET", script_data.url);
126
	    if (xhttp.status === 200) {
127
		let computed_hash = sha256(xhttp.responseText);
128
		if (computed_hash !== script_data.hash) {
129
		    console.log(`Bad hash for ${script_data.url}\n    got ${computed_hash} instead of ${script_data.hash}`);
130
		    return;
131
		}
132
		return xhttp.responseText;
133
	    } else {
134
		console.log("script not fetched: " + script_data.url);
135
		return;
136
	    }
137
	} catch (e) {
138
	    console.log(e);
139
	}
140
    }
141

    
142
    function handle_message(port, message, handler)
143
    {
144
	port.onMessage.removeListener(handler[0]);
145
	let url = message.url;
146
	console.log({url});
147
	send_scripts(url, port);
148
    }
149

    
150
    function new_connection(port)
151
    {
152
	console.log("new page actions connection!");
153
	let handler = [];
154
	handler.push(m => handle_message(port, m, handler));
155
	port.onMessage.addListener(handler[0]);
156
    }
157

    
158
    async function start()
159
    {
160
	storage = await get_storage();
161
	query_best = await get_query_best();
162

    
163
	listen_for_connection(CONNECTION_TYPE.PAGE_ACTIONS, new_connection);
164
    }
165

    
166
    window.start_page_actions_server = start;
167
})();
(4-4/9)