Project

General

Profile

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

haketilo / html / display-panel.js @ b7e2870f

1
/**
2
 * Myext display panel logic
3
 *
4
 * Copyright (C) 2021 Wojtek Kosior
5
 * Redistribution terms are gathered in the `copyright' file.
6
 */
7

    
8
/*
9
 * IMPORTS_START
10
 * IMPORT browser
11
 * IMPORT is_chrome
12
 * IMPORT is_mozilla
13
 * IMPORT CONNECTION_TYPE
14
 * IMPORT url_item
15
 * IMPORT is_privileged_url
16
 * IMPORT TYPE_PREFIX
17
 * IMPORT nice_name
18
 * IMPORT open_in_settings
19
 * IMPORT for_each_possible_pattern
20
 * IMPORTS_END
21
 */
22

    
23
function by_id(id)
24
{
25
    return document.getElementById(id);
26
}
27

    
28
const tab_query = {currentWindow: true, active: true};
29

    
30
async function get_current_tab()
31
{
32
    /* Fix for fact that Chrome does not use promises here */
33
    const promise = is_chrome ?
34
	  new Promise((resolve, reject) =>
35
		      browser.tabs.query(tab_query, tab => resolve(tab))) :
36
	  browser.tabs.query(tab_query);
37

    
38
    try {
39
	return (await promise)[0];
40
    } catch(e) {
41
	console.log(e);
42
    }
43
}
44

    
45
const page_url_heading = by_id("page_url_heading");
46
const show_privileged_notice_chbx = by_id("show_privileged_notice_chbx");
47
const show_page_state_chbx = by_id("show_page_state_chbx");
48

    
49
async function show_page_activity_info()
50
{
51
    const tab = await get_current_tab();
52

    
53
    if (tab === undefined) {
54
	page_url_heading.textContent = "unknown page";
55
	return;
56
    }
57

    
58
    const url = url_item(tab.url);
59
    page_url_heading.textContent = url;
60
    if (is_privileged_url(url)) {
61
	show_privileged_notice_chbx.checked = true;
62
	return;
63
    }
64

    
65
    populate_possible_patterns_list(url);
66
    show_page_state_chbx.checked = true;
67

    
68
    try_to_connect(tab.id);
69
}
70

    
71
function populate_possible_patterns_list(url)
72
{
73
    for_each_possible_pattern(url, add_pattern_to_list);
74

    
75
    const port = browser.runtime.connect({name: CONNECTION_TYPE.PAGE_INFO});
76
    port.onMessage.addListener(handle_page_info);
77
    port.postMessage(["subscribe", url]);
78
}
79

    
80
const possible_patterns_ul = by_id("possible_patterns");
81
const pattern_li_template = by_id("pattern_li_template");
82
pattern_li_template.removeAttribute("id");
83
const known_patterns = new Map();
84

    
85
function add_pattern_to_list(pattern)
86
{
87
    const li = pattern_li_template.cloneNode(true);
88
    li.id = `pattern_li_${known_patterns.size}`;
89
    known_patterns.set(pattern, li.id);
90

    
91
    const span = li.firstElementChild;
92
    span.textContent = pattern;
93

    
94
    const button = span.nextElementSibling;
95
    const settings_opener = () => open_in_settings(TYPE_PREFIX.PAGE, pattern);
96
    button.addEventListener("click", settings_opener);
97

    
98
    possible_patterns_ul.appendChild(li)
99

    
100
    return li.id;
101
}
102

    
103
function ensure_pattern_exists(pattern)
104
{
105
    let id = known_patterns.get(pattern);
106
    /*
107
     * As long as pattern computation works well, we should never get into this
108
     * conditional block. This is just a safety measure. To be removed as part
109
     * of a bigger rework when we start taking iframes into account.
110
     */
111
    if (id === undefined) {
112
	console.log(`unknown pattern: ${pattern}`);
113
	id = add_pattern_to_list(pattern);
114
    }
115

    
116
    return id;
117
}
118

    
119
function set_pattern_li_button_text(li_id, text)
120
{
121
    by_id(li_id).firstElementChild.nextElementSibling.textContent = text;
122
}
123

    
124
function handle_page_info(message)
125
{
126
    const [type, data] = message;
127

    
128
    if (type === "change") {
129
	const li_id = ensure_pattern_exists(data.item);
130
	if (data.old_val === undefined)
131
	    set_pattern_li_button_text(li_id, "Edit in settings");
132
	if (data.new_val === undefined)
133
	    set_pattern_li_button_text(li_id, "Add setting");
134
    }
135

    
136
    if (type === "new_url") {
137
	for (const li_id of known_patterns.values())
138
	    set_pattern_li_button_text(li_id, "Add setting");
139
	for (const [pattern, settings] of data) {
140
	    set_pattern_li_button_text(ensure_pattern_exists(pattern),
141
				       "Edit in settings")
142
	}
143
    }
144
}
145

    
146
const connected_chbx = by_id("connected_chbx");
147

    
148
function try_to_connect(tab_id)
149
{
150
    /* This won't connect to iframes. We'll add support for them later */
151
    const connect_info = {name: CONNECTION_TYPE.ACTIVITY_INFO, frameId: 0};
152
    const port = browser.tabs.connect(tab_id, connect_info);
153

    
154
    port.onDisconnect.addListener(port => handle_disconnect(tab_id));
155
    port.onMessage.addListener(handle_activity_report);
156

    
157
    if (is_mozilla)
158
	setTimeout(() => monitor_connecting(port, tab_id), 1000);
159
}
160

    
161
const loading_chbx = by_id("loading_chbx");
162

    
163
function handle_disconnect(tab_id)
164
{
165
    if (is_chrome && !browser.runtime.lastError)
166
	return;
167

    
168
    /* return if there was no connection initialization failure */
169
    if (connected_chbx.checked)
170
	return;
171

    
172
    loading_chbx.checked = !loading_chbx.checked;
173
    setTimeout(() => try_to_connect(tab_id), 1000);
174
}
175

    
176
function monitor_connecting(port, tab_id)
177
{
178
    if (connected_chbx.checked)
179
	return;
180

    
181
    port.disconnect();
182
    loading_chbx.checked = !loading_chbx.checked;
183
    try_to_connect(tab_id);
184
}
185

    
186
const pattern_span = by_id("pattern");
187
const view_pattern_but = by_id("view_pattern");
188
const blocked_span = by_id("blocked");
189
const payload_span = by_id("payload");
190
const view_payload_but = by_id("view_payload");
191
const container_for_injected = by_id("container_for_injected");
192

    
193
function handle_activity_report(message)
194
{
195
    connected_chbx.checked = true;
196

    
197
    const [type, data] = message;
198

    
199
    if (type === "settings") {
200
	let [pattern, settings] = data;
201

    
202
	settings = settings || {};
203
	blocked_span.textContent = settings.allow ? "no" : "yes";
204

    
205
	if (pattern) {
206
	    pattern_span.textContent = pattern;
207
	    const settings_opener =
208
		  () => open_in_settings(TYPE_PREFIX.PAGE, pattern);
209
	    view_pattern_but.classList.remove("hide");
210
	    view_pattern_but.addEventListener("click", settings_opener);
211
	} else {
212
	    pattern_span.textContent = "none";
213
	}
214

    
215
	const components = settings.components;
216
	if (components) {
217
	    payload_span.textContent = nice_name(...components);
218
	    const settings_opener = () => open_in_settings(...components);
219
	    view_payload_but.classList.remove("hide");
220
	    view_payload_but.addEventListener("click", settings_opener);
221
	} else {
222
	    payload_span.textContent = "none";
223
	}
224
    }
225
    if (type === "script") {
226
	const h4 = document.createElement("h4");
227
	const pre = document.createElement("pre");
228
	h4.textContent = "script";
229
	pre.textContent = data;
230

    
231
	container_for_injected.appendChild(h4);
232
	container_for_injected.appendChild(pre);
233
    }
234
}
235

    
236
by_id("settings_but")
237
    .addEventListener("click", (e) => browser.runtime.openOptionsPage());
238

    
239
show_page_activity_info();
(2-2/4)