/** * GNU LibreJS - A browser add-on to block nonfree nontrivial JavaScript. * * * Copyright (C) 2017, 2018 NateN1222 * Copyright (C) 2018 Ruben Rodriguez * Copyright (C) 2018 Giorgio Maone * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ var fromTab = window.location.hash.match(/^#fromTab=(\d+)/) && RegExp.$1; if (fromTab) { let browserStyle = document.createElement("link"); browserStyle.rel = "stylesheet"; browserStyle.href = "chrome://browser/content/extension.css"; document.head.appendChild(browserStyle); document.documentElement.classList.add("tab"); } var myPort = browser.runtime.connect({ name: "port-from-cs" }); var currentReport; // Sends a message that tells the background script the window is open myPort.postMessage({ "update": true, tabId: parseInt(currentReport && currentReport.tabId || fromTab) || "" }); // Display the actual extension version Number document.querySelector("#version").textContent = browser.runtime.getManifest().version; // Enable autotest button if this is a test-enabled build / session (async () => { if (await Test.getURL()) { let button = document.querySelector("#autotest"); button.style.display = "block"; button.onclick = async () => { await Test.getTab(true); close(); } } })(); var liTemplate = document.querySelector("#li-template"); liTemplate.remove(); document.querySelector("#info").addEventListener("click", e => { let button = e.target; if (button.tagName === "A") { setTimeout(close, 100); return; } if (button.tagName !== "BUTTON") button = button.closest("button"); if (button.matches(".toggle-source")) { let parent = button.parentNode; if (!parent.querySelector(".source").textContent) { parent.querySelector("a").click(); } else { parent.classList.toggle("visible"); } return; } if (!button.matches(".buttons > button")) return; let domain = button.querySelector(".domain"); let li = button.closest("li"); let entry = li && li._scriptEntry || [currentReport.url, "Page's site"]; let action = button.className; let site = domain ? domain.textContent : button.name === "*" ? currentReport.site : ""; if (site) { ([action] = action.split("-")); } myPort.postMessage({ [action]: entry, site, tabId: currentReport.tabId }); }); document.querySelector("#report-tab").onclick = e => { myPort.postMessage({ report_tab: currentReport }); close(); } document.querySelector("#complain").onclick = e => { myPort.postMessage({ invoke_contact_finder: currentReport }); close(); } document.querySelector("#open-options").onclick = e => { browser.runtime.openOptionsPage(); close(); } document.body.addEventListener("click", async e => { if (!e.target.matches(".reload")) return; let { tabId } = currentReport; if (tabId) { await browser.tabs.reload(tabId); myPort.postMessage({ "update": true, tabId }); } }); /* * Takes in the [[file_id, reason],...] array and the group name for one group * of scripts found in this tab, rendering it as a list with management buttons. * Groups are "unknown", "blacklisted", "whitelisted", "accepted", and "blocked". */ function createList(data, group) { var { url } = data; let entries = data[group]; let container = document.getElementById(group); let heading = container.querySelector("h2"); var list = container.querySelector("ul"); list.classList.toggle(group, true); if (Array.isArray(entries) && entries.length) { heading.innerHTML = `${group} scripts in ${url}:`; container.classList.remove("empty"); } else { // default message list.innerHTML = `
  • No ${group} scripts on this page.
  • ` entries = data[group] = []; container.classList.add("empty"); } // generate list let viewSourceToHuman = /^view-source:(.*)#line(\d+)\(([^)]*)\)[^]*/; for (let entry of entries) { let [scriptId, reason] = entry; let li = liTemplate.cloneNode(true); let a = li.querySelector("a"); a.href = scriptId.split("(")[0]; if (scriptId.startsWith("view-source:")) { a.target = "LibreJS-ViewSource"; let source = scriptId.match(/\n([^]*)/); if (source) { li.querySelector(".source").textContent = source[1]; li.querySelector(".toggle-source").style.display = "inline"; } scriptId = scriptId.replace(viewSourceToHuman, "$3 at line $2 of $1"); } a.textContent = scriptId; li.querySelector(".reason").textContent = reason; let bySite = !!reason.match(/https?:\/\/[^/]+\/\*/); li.classList.toggle("by-site", bySite); if (bySite) { let domain = li.querySelector(".forget .domain"); if (domain) domain.textContent = RegExp.lastMatch; } li._scriptEntry = entry; list.appendChild(li); } } /** * Updates scripts lists and buttons to act on them. * If return_HTML is true, it returns the HTML of the popup window without updating it. * example report argument: * { * "accepted": [["FILENAME 1","REASON 1"],["FILENAME 2","REASON 2"]], * "blocked": [["FILENAME 1","REASON 1"],["FILENAME 2","REASON 2"]], * "whitelisted": [["FILENAME 1","REASON 1"],["FILENAME 2","REASON 2"]], * "blacklisted": [["FILENAME 1","REASON 1"],["FILENAME 2","REASON 2"]], * "unknown": [["FILENAME 1","REASON 1"],["FILENAME 2","REASON 2"]], * "url":"example.com" * }; * */ function refreshUI(report) { currentReport = report; let { siteStatus, listedSite } = report; document.querySelector("#site").className = siteStatus || ""; document.querySelector("#site h2").textContent = `This site ${report.site}`; for (let toBeErased of document.querySelectorAll("#info h2:not(.site) > *, #info ul > *")) { toBeErased.remove(); } let scriptsCount = 0; for (let group of ["unknown", "accepted", "whitelisted", "blocked", "blacklisted"]) { if (group in report) createList(report, group); scriptsCount += report[group].length; } for (let b of document.querySelectorAll(`.forget, .whitelist, .blacklist`)) { b.disabled = false; } for (let b of document.querySelectorAll( `.unknown .forget, .accepted .forget, .blocked .forget, .whitelisted .whitelist, .blacklisted .blacklist` )) { b.disabled = true; } if (siteStatus && siteStatus !== "unknown") { let siteContainer = document.querySelector("#site"); let statusLabel = siteStatus; if (listedSite && listedSite !== report.site) { statusLabel += ` via ${listedSite}`; siteContainer.querySelector(".forget").disabled = true; } let status = siteContainer.querySelector(".status"); status.classList.add(siteStatus); status.textContent = statusLabel; } else { document.querySelector("#site .status").textContent = ""; } let noscript = scriptsCount === 0; document.body.classList.toggle("empty", noscript); } myPort.onMessage.addListener(m => { if (m.show_info) { refreshUI(m.show_info); } }); function print_local_storage() { myPort.postMessage({ "printlocalstorage": true }); } function delete_local_storage() { myPort.postMessage({ "deletelocalstorage": true }); }