Project

General

Profile

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

haketilo / background / page_actions_server.js @ dcfc78b0

1
/**
2
 * Myext serving of page actions to content scripts
3
 *
4
 * Copyright (C) 2021 Wojtek Kosior
5
 * Redistribution terms are gathered in the `copyright' file.
6
 */
7

    
8
/*
9
 * IMPORTS_START
10
 * IMPORT get_storage
11
 * IMPORT TYPE_PREFIX
12
 * IMPORT CONNECTION_TYPE
13
 * IMPORT browser
14
 * IMPORT retrieve_nonce
15
 * IMPORT listen_for_connection
16
 * IMPORT sha256
17
 * IMPORT get_query_best
18
 * IMPORTS_END
19
 */
20

    
21
var storage;
22
var query_best;
23
var handler;
24

    
25
function send_actions(url, port)
26
{
27
    let [pattern, settings] = query_best(url);
28

    
29
    port.postMessage(["settings", [pattern, settings]]);
30

    
31
    if (settings === undefined)
32
	return;
33

    
34
    let components = settings.components;
35
    let processed_bags = new Set();
36

    
37
    if (components !== undefined)
38
	send_scripts([components], port, processed_bags);
39
}
40

    
41
// TODO: parallelize script fetching
42
async function send_scripts(components, port, processed_bags)
43
{
44
    for (let [prefix, name] of components) {
45
	if (prefix === TYPE_PREFIX.BAG) {
46
	    if (processed_bags.has(name)) {
47
		console.log(`preventing recursive inclusion of bag ${name}`);
48
		continue;
49
	    }
50

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

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

    
58
	    processed_bags.add(name);
59
	    await send_scripts(bag, port, processed_bags);
60

    
61
	    processed_bags.delete(name);
62
	} else {
63
	    let script_text = await get_script_text(name);
64
	    if (script_text === undefined)
65
		continue;
66

    
67
	    port.postMessage(["inject", [script_text]]);
68
	}
69
    }
70
}
71

    
72
async function get_script_text(script_name)
73
{
74
    try {
75
	let script_data = storage.get(TYPE_PREFIX.SCRIPT, script_name);
76
	if (script_data === undefined) {
77
	    console.log(`missing data for ${script_name}`);
78
	    return;
79
	}
80
	let script_text = script_data.text;
81
	if (!script_text)
82
	    script_text = await fetch_remote_script(script_data);
83
	return script_text;
84
    } catch (e) {
85
	console.log(e);
86
    }
87
}
88

    
89
function ajax_callback()
90
{
91
    if (this.readyState == 4)
92
	this.resolve_callback(this);
93
}
94

    
95
function initiate_ajax_request(resolve, method, url)
96
{
97
    var xhttp = new XMLHttpRequest();
98
    xhttp.resolve_callback = resolve;
99
    xhttp.onreadystatechange = ajax_callback;
100
    xhttp.open(method, url, true);
101
    xhttp.send();
102
}
103

    
104
function make_ajax_request(method, url)
105
{
106
    return new Promise((resolve, reject) =>
107
		       initiate_ajax_request(resolve, method, url));
108
}
109

    
110
async function fetch_remote_script(script_data)
111
{
112
    try {
113
	let xhttp = await make_ajax_request("GET", script_data.url);
114
	if (xhttp.status === 200) {
115
	    let computed_hash = sha256(xhttp.responseText);
116
	    if (computed_hash !== script_data.hash) {
117
		console.log(`Bad hash for ${script_data.url}\n    got ${computed_hash} instead of ${script_data.hash}`);
118
		return;
119
	    }
120
	    return xhttp.responseText;
121
	} else {
122
	    console.log("script not fetched: " + script_data.url);
123
	    return;
124
	}
125
    } catch (e) {
126
	console.log(e);
127
    }
128
}
129

    
130
function handle_message(port, message, handler)
131
{
132
    port.onMessage.removeListener(handler[0]);
133
    let url = message.url;
134
    console.log({url});
135
    send_actions(url, port);
136
}
137

    
138
function new_connection(port)
139
{
140
    console.log("new page actions connection!");
141
    port.postMessage(['nonce', retrieve_nonce((port.sender.tab || '').id, port.sender.frameId)]);
142
    let handler = [];
143
    handler.push(m => handle_message(port, m, handler));
144
    port.onMessage.addListener(handler[0]);
145
}
146

    
147
async function start_page_actions_server()
148
{
149
    storage = await get_storage();
150
    query_best = await get_query_best();
151

    
152
    listen_for_connection(CONNECTION_TYPE.PAGE_ACTIONS, new_connection);
153
}
154

    
155
/*
156
 * EXPORTS_START
157
 * EXPORT start_page_actions_server
158
 * EXPORTS_END
159
 */
(4-4/9)