/** * Myext display panel logic * * Copyright (C) 2021 Wojtek Kosior * Redistribution terms are gathered in the `copyright' file. */ /* * IMPORTS_START * IMPORT browser * IMPORT is_chrome * IMPORT is_mozilla * IMPORT CONNECTION_TYPE * IMPORT url_item * IMPORT is_privileged_url * IMPORT TYPE_PREFIX * IMPORT nice_name * IMPORT open_in_settings * IMPORT for_each_possible_pattern * IMPORTS_END */ function by_id(id) { return document.getElementById(id); } const tab_query = {currentWindow: true, active: true}; async function get_current_tab() { /* Fix for fact that Chrome does not use promises here */ const promise = is_chrome ? new Promise((resolve, reject) => browser.tabs.query(tab_query, tab => resolve(tab))) : browser.tabs.query(tab_query); try { return (await promise)[0]; } catch(e) { console.log(e); } } const page_url_heading = by_id("page_url_heading"); const show_privileged_notice_chbx = by_id("show_privileged_notice_chbx"); const show_page_state_chbx = by_id("show_page_state_chbx"); async function show_page_activity_info() { const tab = await get_current_tab(); if (tab === undefined) { page_url_heading.textContent = "unknown page"; return; } const url = url_item(tab.url); page_url_heading.textContent = url; if (is_privileged_url(url)) { show_privileged_notice_chbx.checked = true; return; } populate_possible_patterns_list(url); show_page_state_chbx.checked = true; try_to_connect(tab.id); } function populate_possible_patterns_list(url) { for_each_possible_pattern(url, add_pattern_to_list); const port = browser.runtime.connect({name: CONNECTION_TYPE.PAGE_INFO}); port.onMessage.addListener(handle_page_info); port.postMessage(["subscribe", url]); } const possible_patterns_ul = by_id("possible_patterns"); const pattern_li_template = by_id("pattern_li_template"); pattern_li_template.removeAttribute("id"); const known_patterns = new Map(); function add_pattern_to_list(pattern) { const li = pattern_li_template.cloneNode(true); li.id = `pattern_li_${known_patterns.size}`; known_patterns.set(pattern, li.id); const span = li.firstElementChild; span.textContent = pattern; const button = span.nextElementSibling; const settings_opener = () => open_in_settings(TYPE_PREFIX.PAGE, pattern); button.addEventListener("click", settings_opener); possible_patterns_ul.appendChild(li) return li.id; } function ensure_pattern_exists(pattern) { let id = known_patterns.get(pattern); /* * As long as pattern computation works well, we should never get into this * conditional block. This is just a safety measure. To be removed as part * of a bigger rework when we start taking iframes into account. */ if (id === undefined) { console.log(`unknown pattern: ${pattern}`); id = add_pattern_to_list(pattern); } return id; } function set_pattern_li_button_text(li_id, text) { by_id(li_id).firstElementChild.nextElementSibling.textContent = text; } function handle_page_info(message) { const [type, data] = message; if (type === "change") { const li_id = ensure_pattern_exists(data.item); if (data.old_val === undefined) set_pattern_li_button_text(li_id, "Edit in settings"); if (data.new_val === undefined) set_pattern_li_button_text(li_id, "Add setting"); } if (type === "new_url") { for (const li_id of known_patterns.values()) set_pattern_li_button_text(li_id, "Add setting"); for (const [pattern, settings] of data) { set_pattern_li_button_text(ensure_pattern_exists(pattern), "Edit in settings") } } } const connected_chbx = by_id("connected_chbx"); function try_to_connect(tab_id) { /* This won't connect to iframes. We'll add support for them later */ const connect_info = {name: CONNECTION_TYPE.ACTIVITY_INFO, frameId: 0}; const port = browser.tabs.connect(tab_id, connect_info); port.onDisconnect.addListener(port => handle_disconnect(tab_id)); port.onMessage.addListener(handle_activity_report); if (is_mozilla) setTimeout(() => monitor_connecting(port, tab_id), 1000); } const loading_chbx = by_id("loading_chbx"); function handle_disconnect(tab_id) { if (is_chrome && !browser.runtime.lastError) return; /* return if there was no connection initialization failure */ if (connected_chbx.checked) return; loading_chbx.checked = !loading_chbx.checked; setTimeout(() => try_to_connect(tab_id), 1000); } function monitor_connecting(port, tab_id) { if (connected_chbx.checked) return; port.disconnect(); loading_chbx.checked = !loading_chbx.checked; try_to_connect(tab_id); } const pattern_span = by_id("pattern"); const view_pattern_but = by_id("view_pattern"); const blocked_span = by_id("blocked"); const payload_span = by_id("payload"); const view_payload_but = by_id("view_payload"); const container_for_injected = by_id("container_for_injected"); function handle_activity_report(message) { connected_chbx.checked = true; const [type, data] = message; if (type === "settings") { let [pattern, settings] = data; settings = settings || {}; blocked_span.textContent = settings.allow ? "no" : "yes"; if (pattern) { pattern_span.textContent = pattern; const settings_opener = () => open_in_settings(TYPE_PREFIX.PAGE, pattern); view_pattern_but.classList.remove("hide"); view_pattern_but.addEventListener("click", settings_opener); } else { pattern_span.textContent = "none"; } const components = settings.components; if (components) { payload_span.textContent = nice_name(...components); const settings_opener = () => open_in_settings(...components); view_payload_but.classList.remove("hide"); view_payload_but.addEventListener("click", settings_opener); } else { payload_span.textContent = "none"; } } if (type === "script") { const h4 = document.createElement("h4"); const pre = document.createElement("pre"); h4.textContent = "script"; pre.textContent = data; container_for_injected.appendChild(h4); container_for_injected.appendChild(pre); } } by_id("settings_but") .addEventListener("click", (e) => browser.runtime.openOptionsPage()); show_page_activity_info();