aboutsummaryrefslogtreecommitdiff
path: root/main_background.js
diff options
context:
space:
mode:
authorYuchen Pei <hi@ypei.me>2022-04-07 12:17:38 +1000
committerYuchen Pei <hi@ypei.me>2022-04-07 12:17:38 +1000
commitae25ad78906179e1448ff7d97957810e2be40206 (patch)
treeca56b583f36ccd317900e6b51c4fcd86f161bc56 /main_background.js
parent52398eafaf99ebbba7ff5a28b832830be394bf1d (diff)
nop whitespace formatting change.
- ran eglot-format using typescript-language-server on all js files in the repo except those under /hash_script/ - verify only whitespace changed: git diff --word-diff-regex=. 62d6a71 62d6a71~1
Diffstat (limited to 'main_background.js')
-rw-r--r--main_background.js1900
1 files changed, 952 insertions, 948 deletions
diff --git a/main_background.js b/main_background.js
index 82f3453..0d52308 100644
--- a/main_background.js
+++ b/main_background.js
@@ -23,10 +23,10 @@
var acorn = require('acorn');
var acornLoose = require('acorn-loose');
var legacy_license_lib = require("./legacy_license_check.js");
-var {ResponseProcessor} = require("./bg/ResponseProcessor");
-var {Storage, ListStore, hash} = require("./common/Storage");
-var {ListManager} = require("./bg/ListManager");
-var {ExternalLicenses} = require("./bg/ExternalLicenses");
+var { ResponseProcessor } = require("./bg/ResponseProcessor");
+var { Storage, ListStore, hash } = require("./common/Storage");
+var { ListManager } = require("./bg/ListManager");
+var { ExternalLicenses } = require("./bg/ExternalLicenses");
console.log("main_background.js");
/**
@@ -39,47 +39,47 @@ var DEBUG = false; // debug the JS evaluation
var PRINT_DEBUG = false; // Everything else
var time = Date.now();
-function dbg_print(a,b){
- if(PRINT_DEBUG == true){
- console.log("Time spent so far: " + (Date.now() - time)/1000 + " seconds");
- if(b === undefined){
- console.log(a);
- } else{
- console.log(a,b);
- }
- }
+function dbg_print(a, b) {
+ if (PRINT_DEBUG == true) {
+ console.log("Time spent so far: " + (Date.now() - time) / 1000 + " seconds");
+ if (b === undefined) {
+ console.log(a);
+ } else {
+ console.log(a, b);
+ }
+ }
}
/*
- NONTRIVIAL THINGS:
- - Fetch
- - XMLhttpRequest
- - eval()
- - ?
- JAVASCRIPT CAN BE FOUND IN:
- - Event handlers (onclick, onload, onsubmit, etc.)
- - <script>JS</script>
- - <script src="/JS.js"></script>
- WAYS TO DETERMINE PASS/FAIL:
- - "// @license [magnet link] [identifier]" then "// @license-end" (may also use /* comments)
- - Automatic whitelist: (http://bzr.savannah.gnu.org/lh/librejs/dev/annotate/head:/data/script_libraries/script-libraries.json_
+ NONTRIVIAL THINGS:
+ - Fetch
+ - XMLhttpRequest
+ - eval()
+ - ?
+ JAVASCRIPT CAN BE FOUND IN:
+ - Event handlers (onclick, onload, onsubmit, etc.)
+ - <script>JS</script>
+ - <script src="/JS.js"></script>
+ WAYS TO DETERMINE PASS/FAIL:
+ - "// @license [magnet link] [identifier]" then "// @license-end" (may also use /* comments)
+ - Automatic whitelist: (http://bzr.savannah.gnu.org/lh/librejs/dev/annotate/head:/data/script_libraries/script-libraries.json_
*/
var licenses = require("./licenses.json").licenses;
// These are objects that it will search for in an initial regex pass over non-free scripts.
var reserved_objects = [
- //"document",
- //"window",
- "fetch",
- "XMLHttpRequest",
- "chrome", // only on chrome
- "browser", // only on firefox
- "eval"
+ //"document",
+ //"window",
+ "fetch",
+ "XMLHttpRequest",
+ "chrome", // only on chrome
+ "browser", // only on firefox
+ "eval"
];
// Generates JSON key for local storage
-function get_storage_key(script_name,src_hash){
- return script_name;
+function get_storage_key(script_name, src_hash) {
+ return script_name;
}
/*
@@ -94,24 +94,24 @@ function get_storage_key(script_name,src_hash){
* with its code to update accordingly
*
*/
-function options_listener(changes, area){
- // The cache must be flushed when settings are changed
- // TODO: See if this can be minimized
- function flushed(){
- dbg_print("cache flushed");
- }
- //var flushingCache = browser.webRequest.handlerBehaviorChanged(flushed);
+function options_listener(changes, area) {
+ // The cache must be flushed when settings are changed
+ // TODO: See if this can be minimized
+ function flushed() {
+ dbg_print("cache flushed");
+ }
+ //var flushingCache = browser.webRequest.handlerBehaviorChanged(flushed);
- dbg_print("Items updated in area" + area +": ");
+ dbg_print("Items updated in area" + area + ": ");
- var changedItems = Object.keys(changes);
- var changed_items = "";
- for (var i = 0; i < changedItems.length; i++){
- var item = changedItems[i];
- changed_items += item + ",";
- }
- dbg_print(changed_items);
+ var changedItems = Object.keys(changes);
+ var changed_items = "";
+ for (var i = 0; i < changedItems.length; i++) {
+ var item = changedItems[i];
+ changed_items += item + ",";
+ }
+ dbg_print(changed_items);
}
@@ -119,26 +119,26 @@ function options_listener(changes, area){
var activeMessagePorts = {};
var activityReports = {};
async function createReport(initializer) {
- if (!(initializer && (initializer.url || initializer.tabId))) {
- throw new Error("createReport() needs an URL or a tabId at least");
- }
- let template = {
- "accepted": [],
- "blocked": [],
- "blacklisted": [],
- "whitelisted": [],
- "unknown": [],
- };
- template = Object.assign(template, initializer);
- let [url] = (template.url || (await browser.tabs.get(initializer.tabId)).url).split("#");
- template.url = url;
- template.site = ListStore.siteItem(url);
- template.siteStatus = listManager.getStatus(template.site);
- let list = {"whitelisted": whitelist, "blacklisted": blacklist}[template.siteStatus];
- if (list) {
- template.listedSite = ListManager.siteMatch(template.site, list);
- }
- return template;
+ if (!(initializer && (initializer.url || initializer.tabId))) {
+ throw new Error("createReport() needs an URL or a tabId at least");
+ }
+ let template = {
+ "accepted": [],
+ "blocked": [],
+ "blacklisted": [],
+ "whitelisted": [],
+ "unknown": [],
+ };
+ template = Object.assign(template, initializer);
+ let [url] = (template.url || (await browser.tabs.get(initializer.tabId)).url).split("#");
+ template.url = url;
+ template.site = ListStore.siteItem(url);
+ template.siteStatus = listManager.getStatus(template.site);
+ let list = { "whitelisted": whitelist, "blacklisted": blacklist }[template.siteStatus];
+ if (list) {
+ template.listedSite = ListManager.siteMatch(template.site, list);
+ }
+ return template;
}
/**
@@ -147,9 +147,9 @@ async function createReport(initializer) {
* at the moment.
*/
async function openReportInTab(data) {
- let popupURL = await browser.browserAction.getPopup({});
- let tab = await browser.tabs.create({url: `${popupURL}#fromTab=${data.tabId}`});
- activityReports[tab.id] = await createReport(data);
+ let popupURL = await browser.browserAction.getPopup({});
+ let tab = await browser.tabs.create({ url: `${popupURL}#fromTab=${data.tabId}` });
+ activityReports[tab.id] = await createReport(data);
}
/**
@@ -157,9 +157,9 @@ async function openReportInTab(data) {
* Clears local storage (the persistent data)
*
*/
-function debug_delete_local(){
- browser.storage.local.clear();
- dbg_print("Local storage cleared");
+function debug_delete_local() {
+ browser.storage.local.clear();
+ dbg_print("Local storage cleared");
}
/**
@@ -167,16 +167,16 @@ function debug_delete_local(){
* Prints local storage (the persistent data) as well as the temporary popup object
*
*/
-function debug_print_local(){
- function storage_got(items){
- console.log("%c Local storage: ", 'color: red;');
- for(var i in items){
- console.log("%c "+i+" = "+items[i], 'color: blue;');
- }
- }
- console.log("%c Variable 'activityReports': ", 'color: red;');
- console.log(activityReports);
- browser.storage.local.get(storage_got);
+function debug_print_local() {
+ function storage_got(items) {
+ console.log("%c Local storage: ", 'color: red;');
+ for (var i in items) {
+ console.log("%c " + i + " = " + items[i], 'color: blue;');
+ }
+ }
+ console.log("%c Variable 'activityReports': ", 'color: red;');
+ console.log(activityReports);
+ browser.storage.local.get(storage_got);
}
/**
@@ -194,25 +194,25 @@ function debug_print_local(){
* Make sure it will use the right URL when refering to a certain script.
*
*/
-async function updateReport(tabId, oldReport, updateUI = false){
- let {url} = oldReport;
- let newReport = await createReport({url, tabId});
- for (let property of Object.keys(oldReport)) {
- let entries = oldReport[property];
- if (!Array.isArray(entries)) continue;
- let defValue = property === "accepted" || property === "blocked" ? property : "unknown";
- for (let script of entries) {
- let status = listManager.getStatus(script[0], defValue);
- if (Array.isArray(newReport[status])) newReport[status].push(script);
- }
- }
- activityReports[tabId] = newReport;
- if (browser.sessions) browser.sessions.setTabValue(tabId, url, newReport);
- dbg_print(newReport);
- if (updateUI && activeMessagePorts[tabId]) {
- dbg_print(`[TABID: ${tabId}] Sending script blocking report directly to browser action.`);
- activeMessagePorts[tabId].postMessage({show_info: newReport});
- }
+async function updateReport(tabId, oldReport, updateUI = false) {
+ let { url } = oldReport;
+ let newReport = await createReport({ url, tabId });
+ for (let property of Object.keys(oldReport)) {
+ let entries = oldReport[property];
+ if (!Array.isArray(entries)) continue;
+ let defValue = property === "accepted" || property === "blocked" ? property : "unknown";
+ for (let script of entries) {
+ let status = listManager.getStatus(script[0], defValue);
+ if (Array.isArray(newReport[status])) newReport[status].push(script);
+ }
+ }
+ activityReports[tabId] = newReport;
+ if (browser.sessions) browser.sessions.setTabValue(tabId, url, newReport);
+ dbg_print(newReport);
+ if (updateUI && activeMessagePorts[tabId]) {
+ dbg_print(`[TABID: ${tabId}] Sending script blocking report directly to browser action.`);
+ activeMessagePorts[tabId].postMessage({ show_info: newReport });
+ }
}
/**
@@ -236,69 +236,69 @@ async function updateReport(tabId, oldReport, updateUI = false){
*
*/
async function addReportEntry(tabId, scriptHashOrUrl, action) {
- let report = activityReports[tabId];
- if (!report) report = activityReports[tabId] =
- await createReport({tabId});
- let type, actionValue;
- for (type of ["accepted", "blocked", "whitelisted", "blacklisted"]) {
- if (type in action) {
- actionValue = action[type];
- break;
- }
- }
- if (!actionValue) {
- console.debug("Something wrong with action", action);
- return "";
- }
-
- // Search unused data for the given entry
- function isNew(entries, item) {
- for (let e of entries) {
- if (e[0] === item) return false;
- }
- return true;
- }
-
- let entryType;
- let scriptName = actionValue[0];
- try {
- entryType = listManager.getStatus(scriptName, type);
- let entries = report[entryType];
- if(isNew(entries, scriptName)){
- dbg_print(activityReports);
- dbg_print(activityReports[tabId]);
- dbg_print(entryType);
- entries.push(actionValue);
- }
- } catch (e) {
- console.error("action %o, type %s, entryType %s", action, type, entryType, e);
- entryType = "unknown";
- }
-
- if (activeMessagePorts[tabId]) {
- try {
- activeMessagePorts[tabId].postMessage({show_info: report});
- } catch(e) {
- }
- }
-
- if (browser.sessions) browser.sessions.setTabValue(tabId, report.url, report);
- updateBadge(tabId, report);
- return entryType;
+ let report = activityReports[tabId];
+ if (!report) report = activityReports[tabId] =
+ await createReport({ tabId });
+ let type, actionValue;
+ for (type of ["accepted", "blocked", "whitelisted", "blacklisted"]) {
+ if (type in action) {
+ actionValue = action[type];
+ break;
+ }
+ }
+ if (!actionValue) {
+ console.debug("Something wrong with action", action);
+ return "";
+ }
+
+ // Search unused data for the given entry
+ function isNew(entries, item) {
+ for (let e of entries) {
+ if (e[0] === item) return false;
+ }
+ return true;
+ }
+
+ let entryType;
+ let scriptName = actionValue[0];
+ try {
+ entryType = listManager.getStatus(scriptName, type);
+ let entries = report[entryType];
+ if (isNew(entries, scriptName)) {
+ dbg_print(activityReports);
+ dbg_print(activityReports[tabId]);
+ dbg_print(entryType);
+ entries.push(actionValue);
+ }
+ } catch (e) {
+ console.error("action %o, type %s, entryType %s", action, type, entryType, e);
+ entryType = "unknown";
+ }
+
+ if (activeMessagePorts[tabId]) {
+ try {
+ activeMessagePorts[tabId].postMessage({ show_info: report });
+ } catch (e) {
+ }
+ }
+
+ if (browser.sessions) browser.sessions.setTabValue(tabId, report.url, report);
+ updateBadge(tabId, report);
+ return entryType;
}
-function get_domain(url){
- var domain = url.replace('http://','').replace('https://','').split(/[/?#]/)[0];
- if(url.indexOf("http://") == 0){
- domain = "http://" + domain;
- }
- else if(url.indexOf("https://") == 0){
- domain = "https://" + domain;
- }
- domain = domain + "/";
- domain = domain.replace(/ /g,"");
- return domain;
+function get_domain(url) {
+ var domain = url.replace('http://', '').replace('https://', '').split(/[/?#]/)[0];
+ if (url.indexOf("http://") == 0) {
+ domain = "http://" + domain;
+ }
+ else if (url.indexOf("https://") == 0) {
+ domain = "https://" + domain;
+ }
+ domain = domain + "/";
+ domain = domain.replace(/ /g, "");
+ return domain;
}
/**
@@ -308,82 +308,82 @@ function get_domain(url){
*/
var portFromCS;
async function connected(p) {
- if(p.name === "contact_finder"){
- // style the contact finder panel
- await browser.tabs.insertCSS(p.sender.tab.id, {
- file: "/content/dialog.css",
- cssOrigin: "user",
- matchAboutBlank: true,
- allFrames: true
- });
-
- // Send a message back with the relevant settings
- p.postMessage(await browser.storage.local.get(["prefs_subject", "prefs_body"]));
- return;
- }
- p.onMessage.addListener(async function(m) {
- var update = false;
- var contact_finder = false;
-
- for (let action of ["whitelist", "blacklist", "forget"]) {
- if (m[action]) {
- let [key] = m[action];
- if (m.site) {
- key = ListStore.siteItem(m.site);
- } else {
- key = ListStore.inlineItem(key) || key;
- }
- await listManager[action](key);
- update = true;
- }
- }
-
- if(m.report_tab){
- openReportInTab(m.report_tab);
- }
- // a debug feature
- if(m["printlocalstorage"] !== undefined){
- console.log("Print local storage");
- debug_print_local();
- }
- // invoke_contact_finder
- if(m["invoke_contact_finder"] !== undefined){
- contact_finder = true;
- await injectContactFinder();
- }
- // a debug feature (maybe give the user an option to do this?)
- if(m["deletelocalstorage"] !== undefined){
- console.log("Delete local storage");
- debug_delete_local();
- }
-
- let tabs = await browser.tabs.query({active: true, currentWindow: true});
-
- if(contact_finder){
- let tab = tabs.pop();
- dbg_print(`[TABID:${tab.id}] Injecting contact finder`);
- //inject_contact_finder(tabs[0]["id"]);
- }
- if (update || m.update && activityReports[m.tabId]) {
- let tabId = "tabId" in m ? m.tabId : tabs.pop().id;
- dbg_print(`%c updating tab ${tabId}`, "color: red;");
- activeMessagePorts[tabId] = p;
- await updateReport(tabId, activityReports[tabId], true);
- } else {
- for(let tab of tabs) {
- if(activityReports[tab.id]){
- // If we have some data stored here for this tabID, send it
- dbg_print(`[TABID: ${tab.id}] Sending stored data associated with browser action'`);
- p.postMessage({"show_info": activityReports[tab.id]});
- } else{
- // create a new entry
- let report = activityReports[tab.id] = await createReport({"url": tab.url, tabId: tab.id});
- p.postMessage({show_info: report});
- dbg_print(`[TABID: ${tab.id}] No data found, creating a new entry for this window.`);
- }
- }
- }
- });
+ if (p.name === "contact_finder") {
+ // style the contact finder panel
+ await browser.tabs.insertCSS(p.sender.tab.id, {
+ file: "/content/dialog.css",
+ cssOrigin: "user",
+ matchAboutBlank: true,
+ allFrames: true
+ });
+
+ // Send a message back with the relevant settings
+ p.postMessage(await browser.storage.local.get(["prefs_subject", "prefs_body"]));
+ return;
+ }
+ p.onMessage.addListener(async function(m) {
+ var update = false;
+ var contact_finder = false;
+
+ for (let action of ["whitelist", "blacklist", "forget"]) {
+ if (m[action]) {
+ let [key] = m[action];
+ if (m.site) {
+ key = ListStore.siteItem(m.site);
+ } else {
+ key = ListStore.inlineItem(key) || key;
+ }
+ await listManager[action](key);
+ update = true;
+ }
+ }
+
+ if (m.report_tab) {
+ openReportInTab(m.report_tab);
+ }
+ // a debug feature
+ if (m["printlocalstorage"] !== undefined) {
+ console.log("Print local storage");
+ debug_print_local();
+ }
+ // invoke_contact_finder
+ if (m["invoke_contact_finder"] !== undefined) {
+ contact_finder = true;
+ await injectContactFinder();
+ }
+ // a debug feature (maybe give the user an option to do this?)
+ if (m["deletelocalstorage"] !== undefined) {
+ console.log("Delete local storage");
+ debug_delete_local();
+ }
+
+ let tabs = await browser.tabs.query({ active: true, currentWindow: true });
+
+ if (contact_finder) {
+ let tab = tabs.pop();
+ dbg_print(`[TABID:${tab.id}] Injecting contact finder`);
+ //inject_contact_finder(tabs[0]["id"]);
+ }
+ if (update || m.update && activityReports[m.tabId]) {
+ let tabId = "tabId" in m ? m.tabId : tabs.pop().id;
+ dbg_print(`%c updating tab ${tabId}`, "color: red;");
+ activeMessagePorts[tabId] = p;
+ await updateReport(tabId, activityReports[tabId], true);
+ } else {
+ for (let tab of tabs) {
+ if (activityReports[tab.id]) {
+ // If we have some data stored here for this tabID, send it
+ dbg_print(`[TABID: ${tab.id}] Sending stored data associated with browser action'`);
+ p.postMessage({ "show_info": activityReports[tab.id] });
+ } else {
+ // create a new entry
+ let report = activityReports[tab.id] = await createReport({ "url": tab.url, tabId: tab.id });
+ p.postMessage({ show_info: report });
+ dbg_print(`[TABID: ${tab.id}] No data found, creating a new entry for this window.`);
+ }
+ }
+ }
+ });
}
/**
@@ -392,15 +392,15 @@ async function connected(p) {
* Delete the info we are storing about this tab if there is any.
*
*/
-function delete_removed_tab_info(tab_id, remove_info){
- dbg_print("[TABID:"+tab_id+"]"+"Deleting stored info about closed tab");
- if(activityReports[tab_id] !== undefined){
- delete activityReports[tab_id];
- }
- if(activeMessagePorts[tab_id] !== undefined){
- delete activeMessagePorts[tab_id];
- }
- ExternalLicenses.purgeCache(tab_id);
+function delete_removed_tab_info(tab_id, remove_info) {
+ dbg_print("[TABID:" + tab_id + "]" + "Deleting stored info about closed tab");
+ if (activityReports[tab_id] !== undefined) {
+ delete activityReports[tab_id];
+ }
+ if (activeMessagePorts[tab_id] !== undefined) {
+ delete activeMessagePorts[tab_id];
+ }
+ ExternalLicenses.purgeCache(tab_id);
}
/**
@@ -412,19 +412,19 @@ function delete_removed_tab_info(tab_id, remove_info){
*/
async function onTabUpdated(tabId, changedInfo, tab) {
- let [url] = tab.url.split("#");
- let report = activityReports[tabId];
- if (!(report && report.url === url)) {
- let cache = browser.sessions &&
- await browser.sessions.getTabValue(tabId, url) || null;
- // on session restore tabIds may change
- if (cache && cache.tabId !== tabId) cache.tabId = tabId;
- updateBadge(tabId, activityReports[tabId] = cache);
- }
+ let [url] = tab.url.split("#");
+ let report = activityReports[tabId];
+ if (!(report && report.url === url)) {
+ let cache = browser.sessions &&
+ await browser.sessions.getTabValue(tabId, url) || null;
+ // on session restore tabIds may change
+ if (cache && cache.tabId !== tabId) cache.tabId = tabId;
+ updateBadge(tabId, activityReports[tabId] = cache);
+ }
}
-async function onTabActivated({tabId}) {
- await onTabUpdated(tabId, {}, await browser.tabs.get(tabId));
+async function onTabActivated({ tabId }) {
+ await onTabUpdated(tabId, {}, await browser.tabs.get(tabId));
}
/* *********************************************************************************************** */
@@ -433,128 +433,128 @@ var fname_data = require("./fname_data.json").fname_data;
//************************this part can be tested in the HTML file index.html's script test.js****************************
-function full_evaluate(script){
- var res = true;
- if(script === undefined || script == ""){
- return [true,"Harmless null script"];
- }
-
- var ast = acornLoose.parse(script).body[0];
-
- var flag = false;
- var amtloops = 0;
-
- var loopkeys = {"for":true,"if":true,"while":true,"switch":true};
- var operators = {"||":true,"&&":true,"=":true,"==":true,"++":true,"--":true,"+=":true,"-=":true,"*":true};
- try{
- var tokens = acorn.tokenizer(script);
- }catch(e){
- console.warn("Tokenizer could not be initiated (probably invalid code)");
- return [false,"Tokenizer could not be initiated (probably invalid code)"];
- }
- try{
- var toke = tokens.getToken();
- }catch(e){
- console.log(script);
- console.log(e);
- console.warn("couldn't get first token (probably invalid code)");
- console.warn("Continuing evaluation");
- }
-
- /**
- * Given the end of an identifer token, it tests for bracket suffix notation
- */
- function being_called(end){
- var i = 0;
- while(script.charAt(end+i).match(/\s/g) !== null){
- i++;
- if(i >= script.length-1){
- return false;
- }
- }
-
- return script.charAt(end+i) == "(";
- }
- /**
- * Given the end of an identifer token, it tests for parentheses
- */
- function is_bsn(end){
- var i = 0;
- while(script.charAt(end+i).match(/\s/g) !== null){
- i++;
- if(i >= script.length-1){
- return false;
- }
- }
- return script.charAt(end+i) == "[";
- }
- var error_count = 0;
- var defines_functions = false;
- while(toke !== undefined && toke.type != acorn.tokTypes.eof){
- if(toke.type.keyword !== undefined){
- //dbg_print("Keyword:");
- //dbg_print(toke);
-
- // This type of loop detection ignores functional loop alternatives and ternary operators
-
- if(toke.type.keyword == "function"){
- dbg_print("%c NOTICE: Function declaration.","color:green");
- defines_functions = true;
- }
-
- if(loopkeys[toke.type.keyword] !== undefined){
- amtloops++;
- if(amtloops > 3){
- dbg_print("%c NONTRIVIAL: Too many loops/conditionals.","color:red");
- if(DEBUG == false){
- return [false,"NONTRIVIAL: Too many loops/conditionals."];
- }
- }
- }
- }else if(toke.value !== undefined && operators[toke.value] !== undefined){
- // It's just an operator. Javascript doesn't have operator overloading so it must be some
- // kind of primitive (I.e. a number)
- }else if(toke.value !== undefined){
- var status = fname_data[toke.value];
- if(status === true){ // is the identifier banned?
- dbg_print("%c NONTRIVIAL: nontrivial token: '"+toke.value+"'","color:red");
- if(DEBUG == false){
- return [false,"NONTRIVIAL: nontrivial token: '"+toke.value+"'"];
- }
- }else if(status === false){// is the identifier not banned?
- // Is there bracket suffix notation?
- if(is_bsn(toke.end)){
- dbg_print("%c NONTRIVIAL: Bracket suffix notation on variable '"+toke.value+"'","color:red");
- if(DEBUG == false){
- return [false,"%c NONTRIVIAL: Bracket suffix notation on variable '"+toke.value+"'"];
- }
- }
- }else if(status === undefined){// is the identifier user defined?
- // Is there bracket suffix notation?
- if(is_bsn(toke.end)){
- dbg_print("%c NONTRIVIAL: Bracket suffix notation on variable '"+toke.value+"'","color:red");
- if(DEBUG == false){
- return [false,"NONTRIVIAL: Bracket suffix notation on variable '"+toke.value+"'"];
- }
- }
- }else{
- dbg_print("trivial token:"+toke.value);
- }
- }
- // If not a keyword or an identifier it's some kind of operator, field parenthesis, brackets
- try{
- toke = tokens.getToken();
- }catch(e){
- dbg_print("Denied script because it cannot be parsed.");
- return [false,"NONTRIVIAL: Cannot be parsed. This could mean it is a 404 error."];
- }
- }
-
- dbg_print("%cAppears to be trivial.","color:green;");
- if (defines_functions === true)
- return [true,"Script appears to be trivial but defines functions."];
- else
- return [true,"Script appears to be trivial."];
+function full_evaluate(script) {
+ var res = true;
+ if (script === undefined || script == "") {
+ return [true, "Harmless null script"];
+ }
+
+ var ast = acornLoose.parse(script).body[0];
+
+ var flag = false;
+ var amtloops = 0;
+
+ var loopkeys = { "for": true, "if": true, "while": true, "switch": true };
+ var operators = { "||": true, "&&": true, "=": true, "==": true, "++": true, "--": true, "+=": true, "-=": true, "*": true };
+ try {
+ var tokens = acorn.tokenizer(script);
+ } catch (e) {
+ console.warn("Tokenizer could not be initiated (probably invalid code)");
+ return [false, "Tokenizer could not be initiated (probably invalid code)"];
+ }
+ try {
+ var toke = tokens.getToken();
+ } catch (e) {
+ console.log(script);
+ console.log(e);
+ console.warn("couldn't get first token (probably invalid code)");
+ console.warn("Continuing evaluation");
+ }
+
+ /**
+ * Given the end of an identifer token, it tests for bracket suffix notation
+ */
+ function being_called(end) {
+ var i = 0;
+ while (script.charAt(end + i).match(/\s/g) !== null) {
+ i++;
+ if (i >= script.length - 1) {
+ return false;
+ }
+ }
+
+ return script.charAt(end + i) == "(";
+ }
+ /**
+ * Given the end of an identifer token, it tests for parentheses
+ */
+ function is_bsn(end) {
+ var i = 0;
+ while (script.charAt(end + i).match(/\s/g) !== null) {
+ i++;
+ if (i >= script.length - 1) {
+ return false;
+ }
+ }
+ return script.charAt(end + i) == "[";
+ }
+ var error_count = 0;
+ var defines_functions = false;
+ while (toke !== undefined && toke.type != acorn.tokTypes.eof) {
+ if (toke.type.keyword !== undefined) {
+ //dbg_print("Keyword:");
+ //dbg_print(toke);
+
+ // This type of loop detection ignores functional loop alternatives and ternary operators
+
+ if (toke.type.keyword == "function") {
+ dbg_print("%c NOTICE: Function declaration.", "color:green");
+ defines_functions = true;
+ }
+
+ if (loopkeys[toke.type.keyword] !== undefined) {
+ amtloops++;
+ if (amtloops > 3) {
+ dbg_print("%c NONTRIVIAL: Too many loops/conditionals.", "color:red");
+ if (DEBUG == false) {
+ return [false, "NONTRIVIAL: Too many loops/conditionals."];
+ }
+ }
+ }
+ } else if (toke.value !== undefined && operators[toke.value] !== undefined) {
+ // It's just an operator. Javascript doesn't have operator overloading so it must be some
+ // kind of primitive (I.e. a number)
+ } else if (toke.value !== undefined) {
+ var status = fname_data[toke.value];
+ if (status === true) { // is the identifier banned?
+ dbg_print("%c NONTRIVIAL: nontrivial token: '" + toke.value + "'", "color:red");
+ if (DEBUG == false) {
+ return [false, "NONTRIVIAL: nontrivial token: '" + toke.value + "'"];
+ }
+ } else if (status === false) {// is the identifier not banned?
+ // Is there bracket suffix notation?
+ if (is_bsn(toke.end)) {
+ dbg_print("%c NONTRIVIAL: Bracket suffix notation on variable '" + toke.value + "'", "color:red");
+ if (DEBUG == false) {
+ return [false, "%c NONTRIVIAL: Bracket suffix notation on variable '" + toke.value + "'"];
+ }
+ }
+ } else if (status === undefined) {// is the identifier user defined?
+ // Is there bracket suffix notation?
+ if (is_bsn(toke.end)) {
+ dbg_print("%c NONTRIVIAL: Bracket suffix notation on variable '" + toke.value + "'", "color:red");
+ if (DEBUG == false) {
+ return [false, "NONTRIVIAL: Bracket suffix notation on variable '" + toke.value + "'"];
+ }
+ }
+ } else {
+ dbg_print("trivial token:" + toke.value);
+ }
+ }
+ // If not a keyword or an identifier it's some kind of operator, field parenthesis, brackets
+ try {
+ toke = tokens.getToken();
+ } catch (e) {
+ dbg_print("Denied script because it cannot be parsed.");
+ return [false, "NONTRIVIAL: Cannot be parsed. This could mean it is a 404 error."];
+ }
+ }
+
+ dbg_print("%cAppears to be trivial.", "color:green;");
+ if (defines_functions === true)
+ return [true, "Script appears to be trivial but defines functions."];
+ else
+ return [true, "Script appears to be trivial."];
}
@@ -571,66 +571,66 @@ function full_evaluate(script){
* It returns an array of [flag (boolean, false if "bad"), reason (string, human readable report)]
*
*/
-function evaluate(script,name){
- function reserved_object_regex(object){
- var arith_operators = "\\+\\-\\*\\/\\%\\=";
- var scope_chars = "\{\}\]\[\(\)\,";
- var trailing_chars = "\s*"+"\(\.\[";
- return new RegExp("(?:[^\\w\\d]|^|(?:"+arith_operators+"))"+object+'(?:\\s*?(?:[\\;\\,\\.\\(\\[])\\s*?)',"g");
- }
- reserved_object_regex("window");
- var all_strings = new RegExp('".*?"'+"|'.*?'","gm");
- var ml_comment = /\/\*([\s\S]+?)\*\//g;
- var il_comment = /\/\/.+/gm;
- var bracket_pairs = /\[.+?\]/g;
- var temp = script.replace(/'.+?'+/gm,"'string'");
- temp = temp.replace(/".+?"+/gm,'"string"');
- temp = temp.replace(ml_comment,"");
- temp = temp.replace(il_comment,"");
- dbg_print("%c ------evaluation results for "+ name +"------","color:white");
- dbg_print("Script accesses reserved objects?");
- var flag = true;
- var reason = ""
- // This is where individual "passes" are made over the code
- for(var i = 0; i < reserved_objects.length; i++){
- var res = reserved_object_regex(reserved_objects[i]).exec(temp);
- if(res != null){
- dbg_print("%c fail","color:red;");
- flag = false;
- reason = "Script uses a reserved object (" + reserved_objects[i] + ")";
- }
- }
- if(flag){
- dbg_print("%c pass","color:green;");
- } else{
- return [flag,reason];
- }
-
- return full_evaluate(script);
+function evaluate(script, name) {
+ function reserved_object_regex(object) {
+ var arith_operators = "\\+\\-\\*\\/\\%\\=";
+ var scope_chars = "\{\}\]\[\(\)\,";
+ var trailing_chars = "\s*" + "\(\.\[";
+ return new RegExp("(?:[^\\w\\d]|^|(?:" + arith_operators + "))" + object + '(?:\\s*?(?:[\\;\\,\\.\\(\\[])\\s*?)', "g");
+ }
+ reserved_object_regex("window");
+ var all_strings = new RegExp('".*?"' + "|'.*?'", "gm");
+ var ml_comment = /\/\*([\s\S]+?)\*\//g;
+ var il_comment = /\/\/.+/gm;
+ var bracket_pairs = /\[.+?\]/g;
+ var temp = script.replace(/'.+?'+/gm, "'string'");
+ temp = temp.replace(/".+?"+/gm, '"string"');
+ temp = temp.replace(ml_comment, "");
+ temp = temp.replace(il_comment, "");
+ dbg_print("%c ------evaluation results for " + name + "------", "color:white");
+ dbg_print("Script accesses reserved objects?");
+ var flag = true;
+ var reason = ""
+ // This is where individual "passes" are made over the code
+ for (var i = 0; i < reserved_objects.length; i++) {
+ var res = reserved_object_regex(reserved_objects[i]).exec(temp);
+ if (res != null) {
+ dbg_print("%c fail", "color:red;");
+ flag = false;
+ reason = "Script uses a reserved object (" + reserved_objects[i] + ")";
+ }
+ }
+ if (flag) {
+ dbg_print("%c pass", "color:green;");
+ } else {
+ return [flag, reason];
+ }
+
+ return full_evaluate(script);
}
function validateLicense(matches) {
- if (!(Array.isArray(matches) && matches.length >= 4)){
- return [false, "Malformed or unrecognized license tag."];
- }
-
- let [all, tag, first, second] = matches;
-
- for (let key in licenses){
- // Match by id on first or second parameter, ignoring case
- if (key.toLowerCase() === first.toLowerCase() ||
- key.toLowerCase() === second.toLowerCase()) {
- return [true, `Recognized license: "${licenses[key]['Name']}" `];
- }
- // Match by link on first parameter (legacy)
- if (licenses[key]["Magnet link"] === first.replace("&amp;","&") ||
- licenses[key]["URL"] === first.replace("&amp;","&") ||
- licenses[key]["URL"].replace("http://", "https://") === first.replace("&amp;","&")) {
- return [true, `Recognized license: "${licenses[key]['Name']}".`];
- }
- }
- return [false, `Unrecognized license tag: "${all}"`];
+ if (!(Array.isArray(matches) && matches.length >= 4)) {
+ return [false, "Malformed or unrecognized license tag."];
+ }
+
+ let [all, tag, first, second] = matches;
+
+ for (let key in licenses) {
+ // Match by id on first or second parameter, ignoring case
+ if (key.toLowerCase() === first.toLowerCase() ||
+ key.toLowerCase() === second.toLowerCase()) {
+ return [true, `Recognized license: "${licenses[key]['Name']}" `];
+ }
+ // Match by link on first parameter (legacy)
+ if (licenses[key]["Magnet link"] === first.replace("&amp;", "&") ||
+ licenses[key]["URL"] === first.replace("&amp;", "&") ||
+ licenses[key]["URL"].replace("http://", "https://") === first.replace("&amp;", "&")) {
+ return [true, `Recognized license: "${licenses[key]['Name']}".`];
+ }
+ }
+ return [false, `Unrecognized license tag: "${all}"`];
}
@@ -645,92 +645,92 @@ function validateLicense(matches) {
* reason text
* ]
*/
-function license_read(scriptSrc, name, external = false){
-
- let license = legacy_license_lib.check(scriptSrc);
- if (license){
- return [true, scriptSrc, `Licensed under: ${license}`];
- }
- if (listManager.builtInHashes.has(hash(scriptSrc))){
- return [true, scriptSrc, "Common script known to be free software."];
- }
-
- let editedSrc = "";
- let uneditedSrc = scriptSrc.trim();
- let reason = uneditedSrc ? "" : "Empty source.";
- let partsDenied = false;
- let partsAccepted = false;
-
- function checkTriviality(s) {
- if (!s.trim()) {
- return true; // empty, ignore it
- }
- let [trivial, message] = external ?
- [false, "External script with no known license"]
- : evaluate(s, name);
- if (trivial) {
- partsAccepted = true;
- editedSrc += s;
- } else {
- partsDenied = true;
- if (s.startsWith("javascript:"))
- editedSrc += `# LIBREJS BLOCKED: ${message}`;
- else
- editedSrc += `/*\nLIBREJS BLOCKED: ${message}\n*/`;
- }
- reason += `\n${message}`;
- return trivial;
- }
-
- while (uneditedSrc) {
- let openingMatch = /\/[\/\*]\s*?(@license)\s+(\S+)\s+(\S+)\s*$/mi.exec(uneditedSrc);
- if (!openingMatch) { // no license found, check for triviality
- checkTriviality(uneditedSrc);
- break;
- }
-
- let openingIndex = openingMatch.index;
- if (openingIndex) {
- // let's check the triviality of the code before the license tag, if any
- checkTriviality(uneditedSrc.substring(0, openingIndex));
- }
- // let's check the actual license
- uneditedSrc = uneditedSrc.substring(openingIndex);
-
- let closureMatch = /\/([*/])\s*@license-end\b[^*/\n]*/i.exec(uneditedSrc);
- if (!closureMatch) {
- let msg = "ERROR: @license with no @license-end";
- return [false, `\n/*\n ${msg} \n*/\n`, msg];
- }
-
- let closureEndIndex = closureMatch.index + closureMatch[0].length;
- let commentEndOffset = uneditedSrc.substring(closureEndIndex).indexOf(closureMatch[1] === "*" ? "*/" : "\n");
- if (commentEndOffset !== -1) {
- closureEndIndex += commentEndOffset;
- }
-
- let [licenseOK, message] = validateLicense(openingMatch);
- if(licenseOK) {
- editedSrc += uneditedSrc.substr(0, closureEndIndex);
- partsAccepted = true;
- } else {
- editedSrc += `\n/*\n${message}\n*/\n`;
- partsDenied = true;
- }
- reason += `\n${message}`;
-
- // trim off everything we just evaluated
- uneditedSrc = uneditedSrc.substring(closureEndIndex).trim();
- }
-
- if(partsDenied) {
- if (partsAccepted) {
- reason = `Some parts of the script have been disabled (check the source for details).\n^--- ${reason}`;
- }
- return [false, editedSrc, reason];
- }
-
- return [true, scriptSrc, reason];
+function license_read(scriptSrc, name, external = false) {
+
+ let license = legacy_license_lib.check(scriptSrc);
+ if (license) {
+ return [true, scriptSrc, `Licensed under: ${license}`];
+ }
+ if (listManager.builtInHashes.has(hash(scriptSrc))) {
+ return [true, scriptSrc, "Common script known to be free software."];
+ }
+
+ let editedSrc = "";
+ let uneditedSrc = scriptSrc.trim();
+ let reason = uneditedSrc ? "" : "Empty source.";
+ let partsDenied = false;
+ let partsAccepted = false;
+
+ function checkTriviality(s) {
+ if (!s.trim()) {
+ return true; // empty, ignore it
+ }
+ let [trivial, message] = external ?
+ [false, "External script with no known license"]
+ : evaluate(s, name);
+ if (trivial) {
+ partsAccepted = true;
+ editedSrc += s;
+ } else {
+ partsDenied = true;
+ if (s.startsWith("javascript:"))
+ editedSrc += `# LIBREJS BLOCKED: ${message}`;
+ else
+ editedSrc += `/*\nLIBREJS BLOCKED: ${message}\n*/`;
+ }
+ reason += `\n${message}`;
+ return trivial;
+ }
+
+ while (uneditedSrc) {
+ let openingMatch = /\/[\/\*]\s*?(@license)\s+(\S+)\s+(\S+)\s*$/mi.exec(uneditedSrc);
+ if (!openingMatch) { // no license found, check for triviality
+ checkTriviality(uneditedSrc);
+ break;
+ }
+
+ let openingIndex = openingMatch.index;
+ if (openingIndex) {
+ // let's check the triviality of the code before the license tag, if any
+ checkTriviality(uneditedSrc.substring(0, openingIndex));
+ }
+ // let's check the actual license
+ uneditedSrc = uneditedSrc.substring(openingIndex);
+
+ let closureMatch = /\/([*/])\s*@license-end\b[^*/\n]*/i.exec(uneditedSrc);
+ if (!closureMatch) {
+ let msg = "ERROR: @license with no @license-end";
+ return [false, `\n/*\n ${msg} \n*/\n`, msg];
+ }
+
+ let closureEndIndex = closureMatch.index + closureMatch[0].length;
+ let commentEndOffset = uneditedSrc.substring(closureEndIndex).indexOf(closureMatch[1] === "*" ? "*/" : "\n");
+ if (commentEndOffset !== -1) {
+ closureEndIndex += commentEndOffset;
+ }
+
+ let [licenseOK, message] = validateLicense(openingMatch);
+ if (licenseOK) {
+ editedSrc += uneditedSrc.substr(0, closureEndIndex);
+ partsAccepted = true;
+ } else {
+ editedSrc += `\n/*\n${message}\n*/\n`;
+ partsDenied = true;
+ }
+ reason += `\n${message}`;
+
+ // trim off everything we just evaluated
+ uneditedSrc = uneditedSrc.substring(closureEndIndex).trim();
+ }
+
+ if (partsDenied) {
+ if (partsAccepted) {
+ reason = `Some parts of the script have been disabled (check the source for details).\n^--- ${reason}`;
+ }
+ return [false, editedSrc, reason];
+ }
+
+ return [true, scriptSrc, reason];
}
/* *********************************************************************************************** */
@@ -741,90 +741,92 @@ function license_read(scriptSrc, name, external = false){
* or an array containing it and the index, if the latter !== -1
*/
async function get_script(response, url, tabId = -1, whitelisted = false, index = -1) {
- function result(scriptSource) {
- return index === -1 ? scriptSource : [scriptSource, index];
- }
-
-
- let scriptName = url.split("/").pop();
- if (whitelisted) {
- if (tabId !== -1) {
- let site = ListManager.siteMatch(url, whitelist);
- // Accept without reading script, it was explicitly whitelisted
- let reason = site
- ? `All ${site} whitelisted by user`
- : "Address whitelisted by user";
- addReportEntry(tabId, url, {"whitelisted": [site || url, reason], url});
- }
- if (response.startsWith("javascript:"))
- return result(response);
- else
- return result(`/* LibreJS: script whitelisted by user preference. */\n${response}`);
- }
-
- let [verdict, editedSource, reason] = license_read(response, scriptName, index === -2);
-
- if (tabId < 0) {
- return result(verdict ? response : editedSource);
- }
-
- let sourceHash = hash(response);
- let domain = get_domain(url);
- let report = activityReports[tabId] || (activityReports[tabId] = await createReport({tabId}));
- updateBadge(tabId, report, !verdict);
- let category = await addReportEntry(tabId, sourceHash, {"url": domain, [verdict ? "accepted" : "blocked"]: [url, reason]});
- switch(category) {
- case "blacklisted":
- editedSource = `/* LibreJS: script ${category} by user. */`;
- return result(response.startsWith("javascript:")
- ? `javascript:void(${encodeURIComponent(editedSource)})` : editedSource);
- case "whitelisted":
- return result(response.startsWith("javascript:")
- ? response : `/* LibreJS: script ${category} by user. */\n${response}`);
- default:
- let scriptSource = verdict ? response : editedSource;
+ function result(scriptSource) {
+ return index === -1 ? scriptSource : [scriptSource, index];
+ }
+
+
+ let scriptName = url.split("/").pop();
+ if (whitelisted) {
+ if (tabId !== -1) {
+ let site = ListManager.siteMatch(url, whitelist);
+ // Accept without reading script, it was explicitly whitelisted
+ let reason = site
+ ? `All ${site} whitelisted by user`
+ : "Address whitelisted by user";
+ addReportEntry(tabId, url, { "whitelisted": [site || url, reason], url });
+ }
+ if (response.startsWith("javascript:"))
+ return result(response);
+ else
+ return result(`/* LibreJS: script whitelisted by user preference. */\n${response}`);
+ }
+
+ let [verdict, editedSource, reason] = license_read(response, scriptName, index === -2);
+
+ if (tabId < 0) {
+ return result(verdict ? response : editedSource);
+ }
+
+ let sourceHash = hash(response);
+ let domain = get_domain(url);
+ let report = activityReports[tabId] || (activityReports[tabId] = await createReport({ tabId }));
+ updateBadge(tabId, report, !verdict);
+ let category = await addReportEntry(tabId, sourceHash, { "url": domain, [verdict ? "accepted" : "blocked"]: [url, reason] });
+ switch (category) {
+ case "blacklisted":
+ editedSource = `/* LibreJS: script ${category} by user. */`;
+ return result(response.startsWith("javascript:")
+ ? `javascript:void(${encodeURIComponent(editedSource)})` : editedSource);
+ case "whitelisted":
+ return result(response.startsWith("javascript:")
+ ? response : `/* LibreJS: script ${category} by user. */\n${response}`);
+ default:
+ let scriptSource = verdict ? response : editedSource;
return result(response.startsWith("javascript:")
- ? (verdict ? scriptSource : `javascript:void(/* ${scriptSource} */)`)
- : `/* LibreJS: script ${category}. */\n${scriptSource}`
- );
- }
+ ? (verdict ? scriptSource : `javascript:void(/* ${scriptSource} */)`)
+ : `/* LibreJS: script ${category}. */\n${scriptSource}`
+ );
+ }
}
function updateBadge(tabId, report = null, forceRed = false) {
- let blockedCount = report ? report.blocked.length + report.blacklisted.length : 0;
- let [text, color] = blockedCount > 0 || forceRed
- ? [blockedCount && blockedCount.toString() || "!" , "red"] : ["✓", "green"]
- let {browserAction} = browser;
- if ("setBadgeText" in browserAction) {
- browserAction.setBadgeText({text, tabId});
- browserAction.setBadgeBackgroundColor({color, tabId});
- } else {
- // Mobile
- browserAction.setTitle({title: `LibreJS (${text})`, tabId});
- }
+ let blockedCount = report ? report.blocked.length + report.blacklisted.length : 0;
+ let [text, color] = blockedCount > 0 || forceRed
+ ? [blockedCount && blockedCount.toString() || "!", "red"] : ["✓", "green"]
+ let { browserAction } = browser;
+ if ("setBadgeText" in browserAction) {
+ browserAction.setBadgeText({ text, tabId });
+ browserAction.setBadgeBackgroundColor({ color, tabId });
+ } else {
+ // Mobile
+ browserAction.setTitle({ title: `LibreJS (${text})`, tabId });
+ }
}
function blockGoogleAnalytics(request) {
- let {url} = request;
- let res = {};
- if (url === 'https://www.google-analytics.com/analytics.js' ||
- /^https:\/\/www\.google\.com\/analytics\/[^#]/.test(url)
- ) {
- res.cancel = true;
- }
- return res;
+ let { url } = request;
+ let res = {};
+ if (url === 'https://www.google-analytics.com/analytics.js' ||
+ /^https:\/\/www\.google\.com\/analytics\/[^#]/.test(url)
+ ) {
+ res.cancel = true;
+ }
+ return res;
}
-async function blockBlacklistedScripts(request) {
- let {url, tabId, documentUrl} = request;
- url = ListStore.urlItem(url);
- let status = listManager.getStatus(url);
- if (status !== "blacklisted") return {};
- let blacklistedSite = ListManager.siteMatch(url, blacklist);
- await addReportEntry(tabId, url, {url: documentUrl,
- "blacklisted": [url, /\*/.test(blacklistedSite) ? `User blacklisted ${blacklistedSite}` : "Blacklisted by user"]});
- return {cancel: true};
+async function blockBlacklistedScripts(request) {
+ let { url, tabId, documentUrl } = request;
+ url = ListStore.urlItem(url);
+ let status = listManager.getStatus(url);
+ if (status !== "blacklisted") return {};
+ let blacklistedSite = ListManager.siteMatch(url, blacklist);
+ await addReportEntry(tabId, url, {
+ url: documentUrl,
+ "blacklisted": [url, /\*/.test(blacklistedSite) ? `User blacklisted ${blacklistedSite}` : "Blacklisted by user"]
+ });
+ return { cancel: true };
}
/**
@@ -834,91 +836,93 @@ async function blockBlacklistedScripts(request) {
*/
var ResponseHandler = {
- /**
- * Enforce white/black lists for url/site early (hashes will be handled later)
- */
- async pre(response) {
- let {request} = response;
- let {url, type, tabId, frameId, documentUrl} = request;
-
- let fullUrl = url;
- url = ListStore.urlItem(url);
- let site = ListStore.siteItem(url);
-
- let blacklistedSite = ListManager.siteMatch(site, blacklist);
- let blacklisted = blacklistedSite || blacklist.contains(url);
- let topUrl = type === "sub_frame" && request.frameAncestors && request.frameAncestors.pop() || documentUrl;
-
- if (blacklisted) {
- if (type === "script") {
- // this shouldn't happen, because we intercept earlier in blockBlacklistedScripts()
- return ResponseProcessor.REJECT;
- }
- if (type === "main_frame") { // we handle the page change here too, since we won't call edit_html()
- activityReports[tabId] = await createReport({url: fullUrl, tabId});
- // Go on without parsing the page: it was explicitly blacklisted
- let reason = blacklistedSite
- ? `All ${blacklistedSite} blacklisted by user`
- : "Address blacklisted by user";
- await addReportEntry(tabId, url, {"blacklisted": [blacklistedSite || url, reason], url: fullUrl});
- }
- // use CSP to restrict JavaScript execution in the page
- request.responseHeaders.unshift({
- name: `Content-security-policy`,
- value: `script-src 'none';`
- });
- return {responseHeaders: request.responseHeaders}; // let's skip the inline script parsing, since we block by CSP
- } else {
- let whitelistedSite = ListManager.siteMatch(site, whitelist);
- let whitelisted = response.whitelisted = whitelistedSite || whitelist.contains(url);
- if (type === "script") {
- if (whitelisted) {
- // accept the script and stop processing
- addReportEntry(tabId, url, {url: topUrl,
- "whitelisted": [url, whitelistedSite ? `User whitelisted ${whitelistedSite}` : "Whitelisted by user"]});
- return ResponseProcessor.ACCEPT;
- } else {
- let scriptInfo = await ExternalLicenses.check({url: fullUrl, tabId, frameId, documentUrl});
- if (scriptInfo) {
- let verdict, ret;
- let msg = scriptInfo.toString();
- if (scriptInfo.free) {
- verdict = "accepted";
- ret = ResponseProcessor.ACCEPT;
- } else {
- verdict = "blocked";
- ret = ResponseProcessor.REJECT;
- }
- addReportEntry(tabId, url, {url, [verdict]: [url, msg]});
- return ret;
- }
- }
- }
- }
- // it's a page (it's too early to report) or an unknown script:
- // let's keep processing
- return ResponseProcessor.CONTINUE;
- },
-
- /**
- * Here we do the heavylifting, analyzing unknown scripts
- */
- async post(response) {
- let {type} = response.request;
- let handle_it = type === "script" ? handle_script : handle_html;
- return await handle_it(response, response.whitelisted);
- }
+ /**
+ * Enforce white/black lists for url/site early (hashes will be handled later)
+ */
+ async pre(response) {
+ let { request } = response;
+ let { url, type, tabId, frameId, documentUrl } = request;
+
+ let fullUrl = url;
+ url = ListStore.urlItem(url);
+ let site = ListStore.siteItem(url);
+
+ let blacklistedSite = ListManager.siteMatch(site, blacklist);
+ let blacklisted = blacklistedSite || blacklist.contains(url);
+ let topUrl = type === "sub_frame" && request.frameAncestors && request.frameAncestors.pop() || documentUrl;
+
+ if (blacklisted) {
+ if (type === "script") {
+ // this shouldn't happen, because we intercept earlier in blockBlacklistedScripts()
+ return ResponseProcessor.REJECT;
+ }
+ if (type === "main_frame") { // we handle the page change here too, since we won't call edit_html()
+ activityReports[tabId] = await createReport({ url: fullUrl, tabId });
+ // Go on without parsing the page: it was explicitly blacklisted
+ let reason = blacklistedSite
+ ? `All ${blacklistedSite} blacklisted by user`
+ : "Address blacklisted by user";
+ await addReportEntry(tabId, url, { "blacklisted": [blacklistedSite || url, reason], url: fullUrl });
+ }
+ // use CSP to restrict JavaScript execution in the page
+ request.responseHeaders.unshift({
+ name: `Content-security-policy`,
+ value: `script-src 'none';`
+ });
+ return { responseHeaders: request.responseHeaders }; // let's skip the inline script parsing, since we block by CSP
+ } else {
+ let whitelistedSite = ListManager.siteMatch(site, whitelist);
+ let whitelisted = response.whitelisted = whitelistedSite || whitelist.contains(url);
+ if (type === "script") {
+ if (whitelisted) {
+ // accept the script and stop processing
+ addReportEntry(tabId, url, {
+ url: topUrl,
+ "whitelisted": [url, whitelistedSite ? `User whitelisted ${whitelistedSite}` : "Whitelisted by user"]
+ });
+ return ResponseProcessor.ACCEPT;
+ } else {
+ let scriptInfo = await ExternalLicenses.check({ url: fullUrl, tabId, frameId, documentUrl });
+ if (scriptInfo) {
+ let verdict, ret;
+ let msg = scriptInfo.toString();
+ if (scriptInfo.free) {
+ verdict = "accepted";
+ ret = ResponseProcessor.ACCEPT;
+ } else {
+ verdict = "blocked";
+ ret = ResponseProcessor.REJECT;
+ }
+ addReportEntry(tabId, url, { url, [verdict]: [url, msg] });
+ return ret;
+ }
+ }
+ }
+ }
+ // it's a page (it's too early to report) or an unknown script:
+ // let's keep processing
+ return ResponseProcessor.CONTINUE;
+ },
+
+ /**
+ * Here we do the heavylifting, analyzing unknown scripts
+ */
+ async post(response) {
+ let { type } = response.request;
+ let handle_it = type === "script" ? handle_script : handle_html;
+ return await handle_it(response, response.whitelisted);
+ }
}
/**
* Here we handle external script requests
*/
-async function handle_script(response, whitelisted){
- let {text, request} = response;
- let {url, tabId, frameId} = request;
- url = ListStore.urlItem(url);
+async function handle_script(response, whitelisted) {
+ let { text, request } = response;
+ let { url, tabId, frameId } = request;
+ url = ListStore.urlItem(url);
let edited = await get_script(text, url, tabId, whitelisted, -2);
- return Array.isArray(edited) ? edited[0] : edited;
+ return Array.isArray(edited) ? edited[0] : edited;
}
/**
@@ -926,15 +930,15 @@ async function handle_script(response, whitelisted){
* the DOCTYPE declaration
*/
function doc2HTML(doc) {
- let s = doc.documentElement.outerHTML;
- if (doc.doctype) {
- let dt = doc.doctype;
- let sDoctype = `<!DOCTYPE ${dt.name || "html"}`;
- if (dt.publicId) sDoctype += ` PUBLIC "${dt.publicId}"`;
- if (dt.systemId) sDoctype += ` "${dt.systemId}"`;
- s = `${sDoctype}>\n${s}`;
- }
- return s;
+ let s = doc.documentElement.outerHTML;
+ if (doc.doctype) {
+ let dt = doc.doctype;
+ let sDoctype = `<!DOCTYPE ${dt.name || "html"}`;
+ if (dt.publicId) sDoctype += ` PUBLIC "${dt.publicId}"`;
+ if (dt.systemId) sDoctype += ` "${dt.systemId}"`;
+ s = `${sDoctype}>\n${s}`;
+ }
+ return s;
}
/**
@@ -949,10 +953,10 @@ function createHTMLElement(doc, name) {
* NOSCRIPT elements to visible the same way as NoScript and uBlock do)
*/
function forceElement(doc, element) {
- let replacement = createHTMLElement(doc, "span");
- replacement.innerHTML = element.innerHTML;
- element.replaceWith(replacement);
- return replacement;
+ let replacement = createHTMLElement(doc, "span");
+ replacement.innerHTML = element.innerHTML;
+ element.replaceWith(replacement);
+ return replacement;
}
/**
@@ -961,9 +965,9 @@ function forceElement(doc, element) {
* they have the "data-librejs-nodisplay" attribute).
*/
function forceNoscriptElements(doc) {
- let shown = 0;
- // inspired by NoScript's onScriptDisabled.js
- for (let noscript of doc.querySelectorAll("noscript:not([data-librejs-nodisplay])")) {
+ let shown = 0;
+ // inspired by NoScript's onScriptDisabled.js
+ for (let noscript of doc.querySelectorAll("noscript:not([data-librejs-nodisplay])")) {
let replacement = forceElement(doc, noscript);
// emulate meta-refresh
let meta = replacement.querySelector('meta[http-equiv="refresh"]');
@@ -971,9 +975,9 @@ function forceNoscriptElements(doc) {
refresh = true;
doc.head.appendChild(meta);
}
- shown++;
+ shown++;
}
- return shown;
+ return shown;
}
/**
* Forces displaying any element having the "data-librejs-display" attribute and
@@ -981,232 +985,232 @@ function forceNoscriptElements(doc) {
* they have the "data-librejs-nodisplay" attribute).
*/
function showConditionalElements(doc) {
- let shown = 0;
- for (let element of document.querySelectorAll("[data-librejs-display]")) {
- forceElement(doc, element);
- shown++;
- }
- return shown;
+ let shown = 0;
+ for (let element of document.querySelectorAll("[data-librejs-display]")) {
+ forceElement(doc, element);
+ shown++;
+ }
+ return shown;
}
/**
* Tests to see if the intrinsic events on the page are free or not.
* returns true if they are, false if they're not
*/
-function read_metadata(meta_element){
-
- if(meta_element === undefined || meta_element === null){
- return;
- }
-
- console.log("metadata found");
-
- var metadata = {};
-
- try{
- metadata = JSON.parse(meta_element.innerHTML);
- }catch(error){
- console.log("Could not parse metadata on page.")
- return false;
- }
-
- var license_str = metadata["intrinsic-events"];
- if(license_str === undefined){
- console.log("No intrinsic events license");
- return false;
- }
- console.log(license_str);
-
- var parts = license_str.split(" ");
- if(parts.length != 2){
- console.log("invalid (>2 tokens)");
- return false;
- }
-
- // this should be adequete to escape the HTML escaping
- parts[0] = parts[0].replace(/&amp;/g, '&');
-
- try{
- if(licenses[parts[1]]["Magnet link"] == parts[0]){
- return true;
- }else{
- console.log("invalid (doesn't match licenses)");
- return false;
- }
- } catch(error){
- console.log("invalid (threw error, key didn't exist)");
- return false;
- }
+function read_metadata(meta_element) {
+
+ if (meta_element === undefined || meta_element === null) {
+ return;
+ }
+
+ console.log("metadata found");
+
+ var metadata = {};
+
+ try {
+ metadata = JSON.parse(meta_element.innerHTML);
+ } catch (error) {
+ console.log("Could not parse metadata on page.")
+ return false;
+ }
+
+ var license_str = metadata["intrinsic-events"];
+ if (license_str === undefined) {
+ console.log("No intrinsic events license");
+ return false;
+ }
+ console.log(license_str);
+
+ var parts = license_str.split(" ");
+ if (parts.length != 2) {
+ console.log("invalid (>2 tokens)");
+ return false;
+ }
+
+ // this should be adequete to escape the HTML escaping
+ parts[0] = parts[0].replace(/&amp;/g, '&');
+
+ try {
+ if (licenses[parts[1]]["Magnet link"] == parts[0]) {
+ return true;
+ } else {
+ console.log("invalid (doesn't match licenses)");
+ return false;
+ }
+ } catch (error) {
+ console.log("invalid (threw error, key didn't exist)");
+ return false;
+ }
}
/**
* Reads/changes the HTML of a page and the scripts within it.
*/
-async function editHtml(html, documentUrl, tabId, frameId, whitelisted){
-
- var parser = new DOMParser();
- var html_doc = parser.parseFromString(html, "text/html");
-
- // moves external licenses reference, if any, before any <SCRIPT> element
- ExternalLicenses.optimizeDocument(html_doc, {tabId, frameId, documentUrl});
-
- let url = ListStore.urlItem(documentUrl);
-
- if (whitelisted) { // don't bother rewriting
- await get_script(html, url, tabId, whitelisted); // generates whitelisted report
- return null;
- }
-
- var scripts = html_doc.scripts;
-
- var meta_element = html_doc.getElementById("LibreJS-info");
- var first_script_src = "";
-
- // get the potential inline source that can contain a license
- for (let script of scripts) {
- // The script must be in-line and exist
- if(script && !script.src) {
- first_script_src = script.textContent;
- break;
- }
- }
-
- let license = false;
- if (first_script_src != "") {
- license = legacy_license_lib.check(first_script_src);
- }
-
- let findLine = finder => finder.test(html) && html.substring(0, finder.lastIndex).split(/\n/).length || 0;
- if (read_metadata(meta_element) || license) {
- console.log("Valid license for intrinsic events found");
- let line, extras;
- if (meta_element) {
- line = findLine(/id\s*=\s*['"]?LibreJS-info\b/gi);
- extras = "(0)";
- } else if (license) {
- line = html.substring(0, html.indexOf(first_script_src)).split(/\n/).length;
- extras = "\n" + first_script_src;
- }
- let viewUrl = line ? `view-source:${documentUrl}#line${line}(<${meta_element ? meta_element.tagName : "SCRIPT"}>)${extras}` : url;
- addReportEntry(tabId, url, {url, "accepted":[viewUrl, `Global license for the page: ${license}`]});
- // Do not process inline scripts
- scripts = [];
- } else {
- let dejaVu = new Map(); // deduplication map & edited script cache
- let modified = false;
- // Deal with intrinsic events
- let intrinsecindex = 0;
- let intrinsicFinder = /<[a-z][^>]*\b(on\w+|href\s*=\s*['"]?javascript:)/gi;
- for (let element of html_doc.all) {
- let line = -1;
- for (let attr of element.attributes) {
- let {name, value} = attr;
- value = value.trim();
- if (name.startsWith("on") || (name === "href" && value.toLowerCase().startsWith("javascript:"))){
- intrinsecindex++;
- if (line === -1) {
- line = findLine(intrinsicFinder);
- }
- try {
- let key = `<${element.tagName} ${name}="${value}">`;
- let edited;
- if (dejaVu.has(key)) {
- edited = dejaVu.get(key);
- } else {
- let url = `view-source:${documentUrl}#line${line}(<${element.tagName} ${name}>)\n${value.trim()}`;
- if (name === "href") value = decodeURIComponent(value);
- edited = await get_script(value, url, tabId, whitelist.contains(url)); dejaVu.set(key, edited);
- }
- if (edited && edited !== value) {
- modified = true;
- attr.value = edited;
- }
- } catch (e) {
- console.error(e);
- }
- }
- }
- }
-
- let modifiedInline = false;
- let scriptFinder = /<script\b/ig;
- for(let i = 0, len = scripts.length; i < len; i++) {
- let script = scripts[i];
- let line = findLine(scriptFinder);
- if (!script.src && !(script.type && script.type !== "text/javascript")) {
- let source = script.textContent.trim();
- let editedSource;
- if (dejaVu.has(source)) {
- editedSource = dejaVu.get(source);
- } else {
- let url = `view-source:${documentUrl}#line${line}(<SCRIPT>)\n${source}`;
- let edited = await get_script(source, url, tabId, whitelisted, i);
- editedSource = edited && edited[0].trim();
- dejaVu.set(url, editedSource);
- }
- if (editedSource) {
- if (source !== editedSource) {
- script.textContent = editedSource;
- modified = modifiedInline = true;
- }
- }
- }
- }
-
- modified = showConditionalElements(html_doc) > 0 || modified;
- if (modified) {
- if (modifiedInline) {
- forceNoscriptElements(html_doc);
- }
- return doc2HTML(html_doc);
- }
- }
- return null;
+async function editHtml(html, documentUrl, tabId, frameId, whitelisted) {
+
+ var parser = new DOMParser();
+ var html_doc = parser.parseFromString(html, "text/html");
+
+ // moves external licenses reference, if any, before any <SCRIPT> element
+ ExternalLicenses.optimizeDocument(html_doc, { tabId, frameId, documentUrl });
+
+ let url = ListStore.urlItem(documentUrl);
+
+ if (whitelisted) { // don't bother rewriting
+ await get_script(html, url, tabId, whitelisted); // generates whitelisted report
+ return null;
+ }
+
+ var scripts = html_doc.scripts;
+
+ var meta_element = html_doc.getElementById("LibreJS-info");
+ var first_script_src = "";
+
+ // get the potential inline source that can contain a license
+ for (let script of scripts) {
+ // The script must be in-line and exist
+ if (script && !script.src) {
+ first_script_src = script.textContent;
+ break;
+ }
+ }
+
+ let license = false;
+ if (first_script_src != "") {
+ license = legacy_license_lib.check(first_script_src);
+ }
+
+ let findLine = finder => finder.test(html) && html.substring(0, finder.lastIndex).split(/\n/).length || 0;
+ if (read_metadata(meta_element) || license) {
+ console.log("Valid license for intrinsic events found");
+ let line, extras;
+ if (meta_element) {
+ line = findLine(/id\s*=\s*['"]?LibreJS-info\b/gi);
+ extras = "(0)";
+ } else if (license) {
+ line = html.substring(0, html.indexOf(first_script_src)).split(/\n/).length;
+ extras = "\n" + first_script_src;
+ }
+ let viewUrl = line ? `view-source:${documentUrl}#line${line}(<${meta_element ? meta_element.tagName : "SCRIPT"}>)${extras}` : url;
+ addReportEntry(tabId, url, { url, "accepted": [viewUrl, `Global license for the page: ${license}`] });
+ // Do not process inline scripts
+ scripts = [];
+ } else {
+ let dejaVu = new Map(); // deduplication map & edited script cache
+ let modified = false;
+ // Deal with intrinsic events
+ let intrinsecindex = 0;
+ let intrinsicFinder = /<[a-z][^>]*\b(on\w+|href\s*=\s*['"]?javascript:)/gi;
+ for (let element of html_doc.all) {
+ let line = -1;
+ for (let attr of element.attributes) {
+ let { name, value } = attr;
+ value = value.trim();
+ if (name.startsWith("on") || (name === "href" && value.toLowerCase().startsWith("javascript:"))) {
+ intrinsecindex++;
+ if (line === -1) {
+ line = findLine(intrinsicFinder);
+ }
+ try {
+ let key = `<${element.tagName} ${name}="${value}">`;
+ let edited;
+ if (dejaVu.has(key)) {
+ edited = dejaVu.get(key);
+ } else {
+ let url = `view-source:${documentUrl}#line${line}(<${element.tagName} ${name}>)\n${value.trim()}`;
+ if (name === "href") value = decodeURIComponent(value);
+ edited = await get_script(value, url, tabId, whitelist.contains(url)); dejaVu.set(key, edited);
+ }
+ if (edited && edited !== value) {
+ modified = true;
+ attr.value = edited;
+ }
+ } catch (e) {
+ console.error(e);
+ }
+ }
+ }
+ }
+
+ let modifiedInline = false;
+ let scriptFinder = /<script\b/ig;
+ for (let i = 0, len = scripts.length; i < len; i++) {
+ let script = scripts[i];
+ let line = findLine(scriptFinder);
+ if (!script.src && !(script.type && script.type !== "text/javascript")) {
+ let source = script.textContent.trim();
+ let editedSource;
+ if (dejaVu.has(source)) {
+ editedSource = dejaVu.get(source);
+ } else {
+ let url = `view-source:${documentUrl}#line${line}(<SCRIPT>)\n${source}`;
+ let edited = await get_script(source, url, tabId, whitelisted, i);
+ editedSource = edited && edited[0].trim();
+ dejaVu.set(url, editedSource);
+ }
+ if (editedSource) {
+ if (source !== editedSource) {
+ script.textContent = editedSource;
+ modified = modifiedInline = true;
+ }
+ }
+ }
+ }
+
+ modified = showConditionalElements(html_doc) > 0 || modified;
+ if (modified) {
+ if (modifiedInline) {
+ forceNoscriptElements(html_doc);
+ }
+ return doc2HTML(html_doc);
+ }
+ }
+ return null;
}
/**
* Here we handle html document responses
*/
async function handle_html(response, whitelisted) {
- let {text, request} = response;
- let {url, tabId, frameId, type} = request;
- if (type === "main_frame") {
- activityReports[tabId] = await createReport({url, tabId});
- updateBadge(tabId);
- }
- return await editHtml(text, url, tabId, frameId, whitelisted);
+ let { text, request } = response;
+ let { url, tabId, frameId, type } = request;
+ if (type === "main_frame") {
+ activityReports[tabId] = await createReport({ url, tabId });
+ updateBadge(tabId);
+ }
+ return await editHtml(text, url, tabId, frameId, whitelisted);
}
var whitelist = new ListStore("pref_whitelist", Storage.CSV);
var blacklist = new ListStore("pref_blacklist", Storage.CSV);
var listManager = new ListManager(whitelist, blacklist,
- // built-in whitelist of script hashes, e.g. jQuery
- Object.values(require("./hash_script/whitelist").whitelist)
- .reduce((a, b) => a.concat(b)) // as a flat array
- .map(script => script.hash)
- );
+ // built-in whitelist of script hashes, e.g. jQuery
+ Object.values(require("./hash_script/whitelist").whitelist)
+ .reduce((a, b) => a.concat(b)) // as a flat array
+ .map(script => script.hash)
+);
async function initDefaults() {
- let defaults = {
- pref_subject: "Issues with Javascript on your website",
- pref_body: `Please consider using a free license for the Javascript on your website.
+ let defaults = {
+ pref_subject: "Issues with Javascript on your website",
+ pref_body: `Please consider using a free license for the Javascript on your website.
[Message generated by LibreJS. See https://www.gnu.org/software/librejs/ for more information]
`
- };
- let keys = Object.keys(defaults);
- let prefs = await browser.storage.local.get(keys);
- let changed = false;
- for (let k of keys) {
- if (!(k in prefs)) {
- prefs[k] = defaults[k];
- changed = true;
- }
- }
- if (changed) {
- await browser.storage.local.set(prefs);
- }
+ };
+ let keys = Object.keys(defaults);
+ let prefs = await browser.storage.local.get(keys);
+ let changed = false;
+ for (let k of keys) {
+ if (!(k in prefs)) {
+ prefs[k] = defaults[k];
+ changed = true;
+ }
+ }
+ if (changed) {
+ await browser.storage.local.set(prefs);
+ }
}
/**
@@ -1214,67 +1218,67 @@ async function initDefaults() {
* only meant to be called once when the script starts
*/
async function init_addon() {
- await initDefaults();
- await whitelist.load();
- browser.runtime.onConnect.addListener(connected);
- browser.storage.onChanged.addListener(options_listener);
- browser.tabs.onRemoved.addListener(delete_removed_tab_info);
- browser.tabs.onUpdated.addListener(onTabUpdated);
- browser.tabs.onActivated.addListener(onTabActivated);
- // Prevents Google Analytics from being loaded from Google servers
- let all_types = [
- "beacon", "csp_report", "font", "image", "imageset", "main_frame", "media",
- "object", "object_subrequest", "ping", "script", "stylesheet", "sub_frame",
- "web_manifest", "websocket", "xbl", "xml_dtd", "xmlhttprequest", "xslt",
- "other"
- ];
- browser.webRequest.onBeforeRequest.addListener(blockGoogleAnalytics,
- {urls: ["<all_urls>"], types: all_types},
- ["blocking"]
- );
- browser.webRequest.onBeforeRequest.addListener(blockBlacklistedScripts,
- {urls: ["<all_urls>"], types: ["script"]},
- ["blocking"]
- );
- browser.webRequest.onResponseStarted.addListener(request => {
- let {tabId} = request;
- let report = activityReports[tabId];
- if (report) {
- updateBadge(tabId, activityReports[tabId]);
- }
- }, {urls: ["<all_urls>"], types: ["main_frame"]});
-
- // Analyzes all the html documents and external scripts as they're loaded
- ResponseProcessor.install(ResponseHandler);
-
- legacy_license_lib.init();
-
-
- let Test = require("./common/Test");
- if (Test.getURL()) {
- // export testable functions to the global scope
- this.LibreJS = {
- editHtml,
- handle_script,
- ExternalLicenses,
- ListManager, ListStore, Storage,
- };
- // create or focus the autotest tab if it's a debugging session
- if ((await browser.management.getSelf()).installType === "development") {
- Test.getTab(true);
- }
- }
+ await initDefaults();
+ await whitelist.load();
+ browser.runtime.onConnect.addListener(connected);
+ browser.storage.onChanged.addListener(options_listener);
+ browser.tabs.onRemoved.addListener(delete_removed_tab_info);
+ browser.tabs.onUpdated.addListener(onTabUpdated);
+ browser.tabs.onActivated.addListener(onTabActivated);
+ // Prevents Google Analytics from being loaded from Google servers
+ let all_types = [
+ "beacon", "csp_report", "font", "image", "imageset", "main_frame", "media",
+ "object", "object_subrequest", "ping", "script", "stylesheet", "sub_frame",
+ "web_manifest", "websocket", "xbl", "xml_dtd", "xmlhttprequest", "xslt",
+ "other"
+ ];
+ browser.webRequest.onBeforeRequest.addListener(blockGoogleAnalytics,
+ { urls: ["<all_urls>"], types: all_types },
+ ["blocking"]
+ );
+ browser.webRequest.onBeforeRequest.addListener(blockBlacklistedScripts,
+ { urls: ["<all_urls>"], types: ["script"] },
+ ["blocking"]
+ );
+ browser.webRequest.onResponseStarted.addListener(request => {
+ let { tabId } = request;
+ let report = activityReports[tabId];
+ if (report) {
+ updateBadge(tabId, activityReports[tabId]);
+ }
+ }, { urls: ["<all_urls>"], types: ["main_frame"] });
+
+ // Analyzes all the html documents and external scripts as they're loaded
+ ResponseProcessor.install(ResponseHandler);
+
+ legacy_license_lib.init();
+
+
+ let Test = require("./common/Test");
+ if (Test.getURL()) {
+ // export testable functions to the global scope
+ this.LibreJS = {
+ editHtml,
+ handle_script,
+ ExternalLicenses,
+ ListManager, ListStore, Storage,
+ };
+ // create or focus the autotest tab if it's a debugging session
+ if ((await browser.management.getSelf()).installType === "development") {
+ Test.getTab(true);
+ }
+ }
}
/**
* Loads the contact finder on the given tab ID.
*/
-async function injectContactFinder(tabId){
- await Promise.all([
- browser.tabs.insertCSS(tabId, {file: "/content/overlay.css", cssOrigin: "user"}),
- browser.tabs.executeScript(tabId, {file: "/content/contactFinder.js"}),
- ]);
+async function injectContactFinder(tabId) {
+ await Promise.all([
+ browser.tabs.insertCSS(tabId, { file: "/content/overlay.css", cssOrigin: "user" }),
+ browser.tabs.executeScript(tabId, { file: "/content/contactFinder.js" }),
+ ]);
}
init_addon();