aboutsummaryrefslogtreecommitdiff
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
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
-rw-r--r--bg/ExternalLicenses.js18
-rw-r--r--bg/ListManager.js52
-rw-r--r--bg/ResponseMetaData.js6
-rw-r--r--bg/ResponseProcessor.js32
-rw-r--r--common/Storage.js16
-rw-r--r--common/Test.js6
-rw-r--r--content/contactFinder.js312
-rw-r--r--content/externalLicenseChecker.js24
-rw-r--r--evaluation test/test.js1934
-rw-r--r--html/display_panel/content/main_panel.js126
-rw-r--r--html/fastclick.js1680
-rw-r--r--html/preferences_panel/pref.js570
-rw-r--r--legacy_license_check.js110
-rw-r--r--license_definitions.js500
-rw-r--r--main_background.js1900
-rw-r--r--pattern_utils.js36
-rw-r--r--test.js34
-rw-r--r--test/spec/LibreJSSpec.js26
18 files changed, 3692 insertions, 3690 deletions
diff --git a/bg/ExternalLicenses.js b/bg/ExternalLicenses.js
index 758b92d..0e09b6d 100644
--- a/bg/ExternalLicenses.js
+++ b/bg/ExternalLicenses.js
@@ -28,10 +28,10 @@
let licensesByLabel = new Map();
let licensesByUrl = new Map();
{
- let {licenses} = require("../license_definitions");
+ let { licenses } = require("../license_definitions");
let mapByLabel = (label, license) => licensesByLabel.set(label.toUpperCase(), license);
for (let [id, l] of Object.entries(licenses)) {
- let {identifier, canonicalUrl, licenseName} = l;
+ let { identifier, canonicalUrl, licenseName } = l;
if (identifier) {
mapByLabel(identifier, l);
} else {
@@ -59,7 +59,7 @@ var ExternalLicenses = {
},
async check(script) {
- let {url, tabId, frameId, documentUrl} = script;
+ let { url, tabId, frameId, documentUrl } = script;
let tabCache = cachedHrefs.get(tabId);
let frameCache = tabCache && tabCache.get(frameId);
let cache = frameCache && frameCache.get(documentUrl);
@@ -67,7 +67,7 @@ var ExternalLicenses = {
action: "checkLicensedScript",
url,
cache,
- }, {frameId});
+ }, { frameId });
if (!(scriptInfo && scriptInfo.licenseLinks.length)) {
return null;
@@ -76,8 +76,8 @@ var ExternalLicenses = {
scriptInfo.toString = function() {
let licenseIds = [...this.licenses].map(l => l.identifier).sort().join(", ");
return licenseIds
- ? `Free license${this.licenses.size > 1 ? "s" : ""} (${licenseIds})`
- : "Unknown license(s)";
+ ? `Free license${this.licenses.size > 1 ? "s" : ""} (${licenseIds})`
+ : "Unknown license(s)";
}
let match = (map, key) => {
if (map.has(key)) {
@@ -87,7 +87,7 @@ var ExternalLicenses = {
return false;
};
- for (let {label, url} of scriptInfo.licenseLinks) {
+ for (let { label, url } of scriptInfo.licenseLinks) {
match(licensesByLabel, label = label.trim().toUpperCase()) ||
match(licensesByUrl, url) ||
match(licensesByLabel, label.replace(/^GNU-|-(?:OR-LATER|ONLY)$/, ''));
@@ -105,7 +105,7 @@ var ExternalLicenses = {
*/
optimizeDocument(doc, cachePointer) {
let cache = {};
- let {tabId, frameId, documentUrl} = cachePointer;
+ let { tabId, frameId, documentUrl } = cachePointer;
let frameCache = cachedHrefs.get(tabId);
if (!frameCache) {
cachedHrefs.set(tabId, frameCache = new Map());
@@ -115,7 +115,7 @@ var ExternalLicenses = {
let link = doc.querySelector(`link[rel="jslicense"], link[data-jslicense="1"], a[rel="jslicense"], a[data-jslicense="1"]`);
if (link) {
let href = link.getAttribute("href");
- cache.webLabels = {href};
+ cache.webLabels = { href };
let move = () => !!doc.head.insertBefore(link, doc.head.firstChild);
if (link.parentNode === doc.head) {
for (let node = link; node = node.previousElementSibling;) {
diff --git a/bg/ListManager.js b/bg/ListManager.js
index 745e599..f712356 100644
--- a/bg/ListManager.js
+++ b/bg/ListManager.js
@@ -23,11 +23,11 @@
A class to manage whitelist/blacklist operations
*/
-let {ListStore} = require("../common/Storage");
+let { ListStore } = require("../common/Storage");
class ListManager {
constructor(whitelist, blacklist, builtInHashes) {
- this.lists = {whitelist, blacklist};
+ this.lists = { whitelist, blacklist };
this.builtInHashes = new Set(builtInHashes);
}
@@ -49,13 +49,13 @@ class ListManager {
Returns "blacklisted", "whitelisted" or defValue
*/
getStatus(key, defValue = "unknown") {
- let {blacklist, whitelist} = this.lists;
+ let { blacklist, whitelist } = this.lists;
let inline = ListStore.inlineItem(key);
if (inline) {
return blacklist.contains(inline)
? "blacklisted"
: whitelist.contains(inline) ? "whitelisted"
- : defValue;
+ : defValue;
}
let match = key.match(/\(([^)]+)\)(?=[^()]*$)/);
@@ -65,38 +65,38 @@ class ListManager {
return (blacklist.contains(url) || ListManager.siteMatch(site, blacklist)
? "blacklisted"
: whitelist.contains(url) || ListManager.siteMatch(site, whitelist)
- ? "whitelisted" : defValue
+ ? "whitelisted" : defValue
);
}
- let [hashItem, srcHash] = match; // (hash), hash
- return blacklist.contains(hashItem) ? "blacklisted"
- : this.builtInHashes.has(srcHash) || whitelist.contains(hashItem)
+ let [hashItem, srcHash] = match; // (hash), hash
+ return blacklist.contains(hashItem) ? "blacklisted"
+ : this.builtInHashes.has(srcHash) || whitelist.contains(hashItem)
? "whitelisted"
- : defValue;
- }
+ : defValue;
+ }
- /*
- Matches by whole site ("http://some.domain.com/*") supporting also
- wildcarded subdomains ("https://*.domain.com/*").
- */
- static siteMatch(url, list) {
- let site = ListStore.siteItem(url);
+ /*
+ Matches by whole site ("http://some.domain.com/*") supporting also
+ wildcarded subdomains ("https://*.domain.com/*").
+ */
+ static siteMatch(url, list) {
+ let site = ListStore.siteItem(url);
+ if (list.contains(site)) {
+ return site;
+ }
+ site = site.replace(/^([\w-]+:\/\/)?(\w)/, "$1*.$2");
+ for (; ;) {
if (list.contains(site)) {
return site;
}
- site = site.replace(/^([\w-]+:\/\/)?(\w)/, "$1*.$2");
- for (;;) {
- if (list.contains(site)) {
- return site;
- }
- let oldKey = site;
- site = site.replace(/(?:\*\.)*\w+(?=\.)/, "*");
- if (site === oldKey) {
- return null;
- }
+ let oldKey = site;
+ site = site.replace(/(?:\*\.)*\w+(?=\.)/, "*");
+ if (site === oldKey) {
+ return null;
}
}
+ }
}
module.exports = { ListManager };
diff --git a/bg/ResponseMetaData.js b/bg/ResponseMetaData.js
index 0f768fe..4570120 100644
--- a/bg/ResponseMetaData.js
+++ b/bg/ResponseMetaData.js
@@ -26,15 +26,15 @@
*/
const BOM = [0xEF, 0xBB, 0xBF];
-const DECODER_PARAMS = {stream: true};
+const DECODER_PARAMS = { stream: true };
class ResponseMetaData {
constructor(request) {
- let {responseHeaders} = request;
+ let { responseHeaders } = request;
this.headers = {};
for (let h of responseHeaders) {
if (/^\s*Content-(Type|Disposition)\s*$/i.test(h.name)) {
- let propertyName = h.name.split("-")[1].trim();
+ let propertyName = h.name.split("-")[1].trim();
propertyName = `content${propertyName.charAt(0).toUpperCase()}${propertyName.substring(1).toLowerCase()}`;
this[propertyName] = h.value;
this.headers[propertyName] = h;
diff --git a/bg/ResponseProcessor.js b/bg/ResponseProcessor.js
index 2c613c9..078f38a 100644
--- a/bg/ResponseProcessor.js
+++ b/bg/ResponseProcessor.js
@@ -24,7 +24,7 @@
only the "interesting" HTML and script requests and leaving the other alone
*/
-let {ResponseMetaData} = require("./ResponseMetaData");
+let { ResponseMetaData } = require("./ResponseMetaData");
let listeners = new WeakMap();
let webRequestEvent = browser.webRequest.onHeadersReceived;
@@ -34,13 +34,13 @@ class ResponseProcessor {
static install(handler, types = ["main_frame", "sub_frame", "script"]) {
if (listeners.has(handler)) return false;
let listener =
- async request => await new ResponseTextFilter(request).process(handler);
+ async request => await new ResponseTextFilter(request).process(handler);
listeners.set(handler, listener);
webRequestEvent.addListener(
- listener,
- {urls: ["<all_urls>"], types},
- ["blocking", "responseHeaders"]
- );
+ listener,
+ { urls: ["<all_urls>"], types },
+ ["blocking", "responseHeaders"]
+ );
return true;
}
@@ -54,15 +54,15 @@ class ResponseProcessor {
Object.assign(ResponseProcessor, {
// control flow values to be returned by handler.pre() callbacks
- ACCEPT: {},
- REJECT: {cancel: true},
- CONTINUE: null
+ ACCEPT: {},
+ REJECT: { cancel: true },
+ CONTINUE: null
});
class ResponseTextFilter {
constructor(request) {
this.request = request;
- let {type, statusCode} = request;
+ let { type, statusCode } = request;
let md = this.metaData = new ResponseMetaData(request);
this.canProcess = // we want to process html documents and scripts only
(statusCode < 300 || statusCode >= 400) && // skip redirections
@@ -72,8 +72,8 @@ class ResponseTextFilter {
async process(handler) {
if (!this.canProcess) return ResponseProcessor.ACCEPT;
- let {metaData, request} = this;
- let response = {request, metaData}; // we keep it around allowing callbacks to store state
+ let { metaData, request } = this;
+ let response = { request, metaData }; // we keep it around allowing callbacks to store state
if (typeof handler.pre === "function") {
let res = await handler.pre(response);
if (res) return res;
@@ -81,7 +81,7 @@ class ResponseTextFilter {
if (typeof handler !== "function") return ResponseProcessor.ACCEPT;
}
- let {requestId, responseHeaders} = request;
+ let { requestId, responseHeaders } = request;
let filter = browser.webRequest.filterResponseData(requestId);
let buffer = [];
@@ -103,10 +103,10 @@ class ResponseTextFilter {
console.debug("Warning: zeroes in bytestream, probable cached encoding mismatch.", request);
if (request.type === "script") {
console.debug("It's a script, trying to refetch it.");
- response.text = await (await fetch(request.url, {cache: "reload", credentials: "include"})).text();
+ response.text = await (await fetch(request.url, { cache: "reload", credentials: "include" })).text();
} else {
console.debug("It's a %s, trying to decode it as UTF-16.", request.type);
- response.text = new TextDecoder("utf-16be").decode(allBytes, {stream: true});
+ response.text = new TextDecoder("utf-16be").decode(allBytes, { stream: true });
}
} else {
response.text = metaData.decode(allBytes);
@@ -114,7 +114,7 @@ class ResponseTextFilter {
let editedText = null;
try {
editedText = await handler(response);
- } catch(e) {
+ } catch (e) {
console.error(e);
}
if (editedText !== null) {
diff --git a/common/Storage.js b/common/Storage.js
index 62b7b19..47261c5 100644
--- a/common/Storage.js
+++ b/common/Storage.js
@@ -34,7 +34,7 @@ var Storage = {
return array ? new Set(array) : new Set();
},
async save(key, list) {
- return await browser.storage.local.set({[key]: [...list]});
+ return await browser.storage.local.set({ [key]: [...list] });
},
},
@@ -45,7 +45,7 @@ var Storage = {
},
async save(key, list) {
- return await browser.storage.local.set({[key]: [...list].join(",")});
+ return await browser.storage.local.set({ [key]: [...list].join(",") });
}
}
};
@@ -70,8 +70,8 @@ class ListStore {
// here we simplify and hash inline script references
return url.startsWith("inline:") ? url
: url.startsWith("view-source:")
- && url.replace(/^view-source:[\w-+]+:\/+([^/]+).*#line\d+/,"inline://$1#")
- .replace(/\n[^]*/, s => s.replace(/\s+/g, ' ').substring(0, 16) + "…" + hash(s.trim()));
+ && url.replace(/^view-source:[\w-+]+:\/+([^/]+).*#line\d+/, "inline://$1#")
+ .replace(/\n[^]*/, s => s.replace(/\s+/g, ' ').substring(0, 16) + "…" + hash(s.trim()));
}
static hashItem(hash) {
return hash.startsWith("(") ? hash : `(${hash})`;
@@ -133,10 +133,10 @@ class ListStore {
}
}
-function hash(source){
- var shaObj = new jssha("SHA-256","TEXT")
- shaObj.update(source);
- return shaObj.getHash("HEX");
+function hash(source) {
+ var shaObj = new jssha("SHA-256", "TEXT")
+ shaObj.update(source);
+ return shaObj.getHash("HEX");
}
if (typeof module === "object") {
diff --git a/common/Test.js b/common/Test.js
index e272d1e..7acbfa0 100644
--- a/common/Test.js
+++ b/common/Test.js
@@ -40,11 +40,11 @@ var Test = (() => {
async getTab(activate = false) {
let url = await this.getURL();
- let tab = url ? (await browser.tabs.query({url}))[0] ||
- (await browser.tabs.create({url}))
+ let tab = url ? (await browser.tabs.query({ url }))[0] ||
+ (await browser.tabs.create({ url }))
: null;
if (tab && activate) {
- await browser.tabs.update(tab.id, {active: true});
+ await browser.tabs.update(tab.id, { active: true });
}
return tab;
}
diff --git a/content/contactFinder.js b/content/contactFinder.js
index 22bf2fc..6b6cc44 100644
--- a/content/contactFinder.js
+++ b/content/contactFinder.js
@@ -25,7 +25,7 @@
// - add a comma after the closing bracket of the key "background"
// - Copy and paste this after it:
/*
- "content_scripts": [{
+ "content_scripts": [{
"matches": ["<all_urls>"],
"js": ["/content/contactFinder.js"],
"css": ["/content/contactFinder.css"]
@@ -64,64 +64,64 @@ var reIdentiCa = /identi\.ca\/(?!notice\/)[a-z0-9]*/i;
* and by degree of certainty.
*/
var contactStr = {
- 'da': {
- 'certain': [
- '^[\\s]*Kontakt os[\\s]*$',
- '^[\\s]*Email Os[\\s]*$',
- '^[\\s]*Kontakt[\\s]*$'
- ],
- 'probable': ['^[\\s]Kontakt', '^[\\s]*Email'],
- 'uncertain': [
- '^[\\s]*Om Us',
- '^[\\s]*Om',
- 'Hvem vi er'
- ]
- },
- 'en': {
- 'certain': [
- '^[\\s]*Contact Us[\\s]*$',
- '^[\\s]*Email Us[\\s]*$',
- '^[\\s]*Contact[\\s]*$',
- '^[\\s]*Feedback[\\s]*$',
- '^[\\s]*Web.?site Feedback[\\s]*$'
- ],
- 'probable': ['^[\\s]Contact', '^[\\s]*Email'],
- 'uncertain': [
- '^[\\s]*About Us',
- '^[\\s]*About',
- 'Who we are',
- 'Who I am',
- 'Company Info',
- 'Customer Service'
- ]
- },
- 'es': {
- 'certain': [
- '^[\\s]*contáctenos[\\s]*$',
- '^[\\s]*Email[\\s]*$'
- ],
- 'probable': ['^[\\s]contáctenos', '^[\\s]*Email'],
- 'uncertain': [
- 'Acerca de nosotros'
- ]
- },
- 'fr': {
- 'certain': [
- '^[\\s]*Contactez nous[\\s]*$',
- '^[\\s]*(Nous )?contacter[\\s]*$',
- '^[\\s]*Email[\\s]*$',
- '^[\\s]*Contact[\\s]*$',
- '^[\\s]*Commentaires[\\s]*$'
- ],
- 'probable': ['^[\\s]Contact', '^[\\s]*Email'],
- 'uncertain': [
- '^[\\s]*(A|À) propos',
- 'Qui nous sommes',
- 'Qui suis(-| )?je',
- 'Info',
- 'Service Client(e|è)le'
- ]
- }
+ 'da': {
+ 'certain': [
+ '^[\\s]*Kontakt os[\\s]*$',
+ '^[\\s]*Email Os[\\s]*$',
+ '^[\\s]*Kontakt[\\s]*$'
+ ],
+ 'probable': ['^[\\s]Kontakt', '^[\\s]*Email'],
+ 'uncertain': [
+ '^[\\s]*Om Us',
+ '^[\\s]*Om',
+ 'Hvem vi er'
+ ]
+ },
+ 'en': {
+ 'certain': [
+ '^[\\s]*Contact Us[\\s]*$',
+ '^[\\s]*Email Us[\\s]*$',
+ '^[\\s]*Contact[\\s]*$',
+ '^[\\s]*Feedback[\\s]*$',
+ '^[\\s]*Web.?site Feedback[\\s]*$'
+ ],
+ 'probable': ['^[\\s]Contact', '^[\\s]*Email'],
+ 'uncertain': [
+ '^[\\s]*About Us',
+ '^[\\s]*About',
+ 'Who we are',
+ 'Who I am',
+ 'Company Info',
+ 'Customer Service'
+ ]
+ },
+ 'es': {
+ 'certain': [
+ '^[\\s]*contáctenos[\\s]*$',
+ '^[\\s]*Email[\\s]*$'
+ ],
+ 'probable': ['^[\\s]contáctenos', '^[\\s]*Email'],
+ 'uncertain': [
+ 'Acerca de nosotros'
+ ]
+ },
+ 'fr': {
+ 'certain': [
+ '^[\\s]*Contactez nous[\\s]*$',
+ '^[\\s]*(Nous )?contacter[\\s]*$',
+ '^[\\s]*Email[\\s]*$',
+ '^[\\s]*Contact[\\s]*$',
+ '^[\\s]*Commentaires[\\s]*$'
+ ],
+ 'probable': ['^[\\s]Contact', '^[\\s]*Email'],
+ 'uncertain': [
+ '^[\\s]*(A|À) propos',
+ 'Qui nous sommes',
+ 'Qui suis(-| )?je',
+ 'Info',
+ 'Service Client(e|è)le'
+ ]
+ }
};
var usaPhoneNumber = new RegExp(/(?:\+ ?1 ?)?\(?[2-9]{1}[0-9]{2}\)?(?:\-|\.| )?[0-9]{3}(?:\-|\.| )[0-9]{4}(?:[^0-9])/mg);
@@ -134,14 +134,14 @@ var prefs;
/**
* returns input with all elements not of type string removed
*/
-function remove_not_str(a){
- var new_a = [];
- for(var i in a){
- if(typeof(a[i]) == "string"){
- new_a.push(a[i])
- }
- }
- return new_a;
+function remove_not_str(a) {
+ var new_a = [];
+ for (var i in a) {
+ if (typeof (a[i]) == "string") {
+ new_a.push(a[i])
+ }
+ }
+ return new_a;
}
/**
* Tests all links on the page for regexes under a certain certainty level.
@@ -151,87 +151,87 @@ function remove_not_str(a){
*
* certainty_lvl can be "certain" > "probable" > "uncertain"
*/
-function attempt(certainty_lvl, first=true){
- // There needs to be some kind of max so that people can't troll by for example leaving a comment with a bunch of emails
- // to cause LibreJS users to slow down.
- var fail_flag = true;
- var flag;
- var matches = [];
- var result = [];
- var str_under_test = "";
- for(var i in document.links){
- if( typeof(document.links[i].innerText) != "string" || typeof(document.links[i].href) != "string"){
- continue;
- }
- str_under_test = document.links[i].innerText + " " + document.links[i].href;
- flag = true;
- for(var j in contactStr){
- for(var k in contactStr[j][certainty_lvl]){
- if(flag){
- result = [];
- result = str_under_test.match(new RegExp(contactStr[j][certainty_lvl][k],"g"));
- result = remove_not_str(result);
- if(result !== undefined && typeof(result[0]) == "string" ){
- if(first){
- return {"fail":false,"result":document.links[i]};
- } else{
- //console.log(document.links[i].href + " matched " + contactStr[j][certainty_lvl][k]);
- matches.push(document.links[i]);
- fail_flag = false;
- flag = false;
- }
- }
- }
- }
- }
- }
- return {"fail":fail_flag,"result":matches};
+function attempt(certainty_lvl, first = true) {
+ // There needs to be some kind of max so that people can't troll by for example leaving a comment with a bunch of emails
+ // to cause LibreJS users to slow down.
+ var fail_flag = true;
+ var flag;
+ var matches = [];
+ var result = [];
+ var str_under_test = "";
+ for (var i in document.links) {
+ if (typeof (document.links[i].innerText) != "string" || typeof (document.links[i].href) != "string") {
+ continue;
+ }
+ str_under_test = document.links[i].innerText + " " + document.links[i].href;
+ flag = true;
+ for (var j in contactStr) {
+ for (var k in contactStr[j][certainty_lvl]) {
+ if (flag) {
+ result = [];
+ result = str_under_test.match(new RegExp(contactStr[j][certainty_lvl][k], "g"));
+ result = remove_not_str(result);
+ if (result !== undefined && typeof (result[0]) == "string") {
+ if (first) {
+ return { "fail": false, "result": document.links[i] };
+ } else {
+ //console.log(document.links[i].href + " matched " + contactStr[j][certainty_lvl][k]);
+ matches.push(document.links[i]);
+ fail_flag = false;
+ flag = false;
+ }
+ }
+ }
+ }
+ }
+ }
+ return { "fail": fail_flag, "result": matches };
}
/**
* "LibreJS detects contact pages, email addresses that are likely to be owned by the
* maintainer of the site, Twitter and identi.ca links, and phone numbers."
*/
-function find_contacts(){
- var all = document.documentElement.innerText;
- var phone_num = [];
- var twitlinks = [];
- var identi = [];
- var contact_pages = [];
- var res = attempt("certain");
- var flag = true;
- var type = "";
- if(res["fail"] == false){
- type = "certain";
- res = res["result"];
- flag = false;
- }
- if(flag){
- res = attempt("probable");
- if(res["fail"] == false){
- type = "probable";
- res = res["result"];
- flag = false;
- }
- }
- if(flag){
- res = attempt("uncertain");
- if(res["fail"] == false){
- type = "uncertain";
- res = res["result"];
- flag = false;
- }
- }
- if(flag){
- return res;
- }
- return [type,res];
+function find_contacts() {
+ var all = document.documentElement.innerText;
+ var phone_num = [];
+ var twitlinks = [];
+ var identi = [];
+ var contact_pages = [];
+ var res = attempt("certain");
+ var flag = true;
+ var type = "";
+ if (res["fail"] == false) {
+ type = "certain";
+ res = res["result"];
+ flag = false;
+ }
+ if (flag) {
+ res = attempt("probable");
+ if (res["fail"] == false) {
+ type = "probable";
+ res = res["result"];
+ flag = false;
+ }
+ }
+ if (flag) {
+ res = attempt("uncertain");
+ if (res["fail"] == false) {
+ type = "uncertain";
+ res = res["result"];
+ flag = false;
+ }
+ }
+ if (flag) {
+ return res;
+ }
+ return [type, res];
}
function createWidget(id, tag, parent = document.body) {
let widget = document.getElementById(id);
- if (widget) widget.remove();
+ if (widget) widget.remove();
widget = parent.appendChild(document.createElement(tag));
widget.id = id;
return widget;
@@ -272,7 +272,7 @@ function main() {
debug("initFrame");
let res = find_contacts();
let contentDoc = frame.contentWindow.document;
- let {body} = contentDoc;
+ let { body } = contentDoc;
body.id = "_LibreJS_dialog";
body.innerHTML = `<h1>LibreJS Complaint</h1><button class='close'>x</button>`;
contentDoc.documentElement.appendChild(contentDoc.createElement("base")).target = "_top";
@@ -281,35 +281,33 @@ function main() {
let addHTML = s => content.insertAdjacentHTML("beforeend", s);
if ("fail" in res) {
content.classList.toggle("_LibreJS_fail", true)
- addHTML("<div>Could not guess any contact page for this site.</div>");
- } else {
+ addHTML("<div>Could not guess any contact page for this site.</div>");
+ } else {
addHTML("<h3>Contact info guessed for this site</h3>");
- if(typeof(res[1]) === "string") {
+ if (typeof (res[1]) === "string") {
let a = contentDoc.createElement("a");
a.href = a.textContent = res[1];
content.appendChild(a);
- } else if (typeof(res[1]) === "object"){
- addHTML(`${res[0]}: ${res[1].outerHTML}`);
- }
- }
+ } else if (typeof (res[1]) === "object") {
+ addHTML(`${res[0]}: ${res[1].outerHTML}`);
+ }
+ }
- let emails = document.documentElement.textContent.match(email_regex);
- if (emails && (emails = Array.filter(emails, e => !!e)).length) {
+ let emails = document.documentElement.textContent.match(email_regex);
+ if (emails && (emails = Array.filter(emails, e => !!e)).length) {
addHTML("<h5>Possible email addresses:</h5>");
let list = contentDoc.createElement("ul");
- for (let i = 0, max = Math.min(emails.length, 10); i < max; i++) {
+ for (let i = 0, max = Math.min(emails.length, 10); i < max; i++) {
let recipient = emails[i];
let a = contentDoc.createElement("a");
- a.href = `mailto:${recipient}?subject${
- encodeURIComponent(prefs["pref_subject"])
- }&body=${
- encodeURIComponent(prefs["pref_body"])
+ a.href = `mailto:${recipient}?subject${encodeURIComponent(prefs["pref_subject"])
+ }&body=${encodeURIComponent(prefs["pref_body"])
}`;
a.textContent = recipient;
list.appendChild(contentDoc.createElement("li")).appendChild(a);
- }
+ }
content.appendChild(list);
- }
+ }
Array.forEach(contentDoc.querySelectorAll(".close, a"), makeCloser);
debug("frame initialized");
}
@@ -318,9 +316,9 @@ function main() {
frame.addEventListener("load", e => {
debug("frame loaded");
- myPort = browser.runtime.connect({name: "contact_finder"}).onMessage.addListener(m => {
- prefs = m;
- initFrame();
+ myPort = browser.runtime.connect({ name: "contact_finder" }).onMessage.addListener(m => {
+ prefs = m;
+ initFrame();
});
});
}
diff --git a/content/externalLicenseChecker.js b/content/externalLicenseChecker.js
index 982ce04..600a501 100644
--- a/content/externalLicenseChecker.js
+++ b/content/externalLicenseChecker.js
@@ -24,15 +24,15 @@
let fetchWebLabels = async args => {
// see https://www.gnu.org/software/librejs/free-your-javascript.html#step3
- let {map, cache} = args;
+ let { map, cache } = args;
let link = document.querySelector(`link[rel="jslicense"], link[data-jslicense="1"], a[rel="jslicense"], a[data-jslicense="1"]`);
let baseURL = link ? link.href : cache.webLabels && new URL(cache.webLabels.href, document.baseURI);
if (baseURL) try {
let response = await fetch(baseURL);
if (!response.ok) throw `${response.status} ${response.statusText}`;
let doc = new DOMParser().parseFromString(
- await response.text(),
- "text/html"
+ await response.text(),
+ "text/html"
);
let base = doc.querySelector("base");
if (base) {
@@ -49,9 +49,9 @@
let script = firstLink(cols[0]);
let licenseLinks = allLinks(cols[1]);
let sources = cols[2] ? allLinks(cols[2]) : [];
- map.set(script.url, {script, licenseLinks, sources});
+ map.set(script.url, { script, licenseLinks, sources });
} catch (e) {
- console.error("LibreJS: error parsing Web Labels at %s, row %s", baseURL, row.innerHTML, e);
+ console.error("LibreJS: error parsing Web Labels at %s, row %s", baseURL, row.innerHTML, e);
}
}
} catch (e) {
@@ -62,22 +62,22 @@
let fetchLicenseInfo = async cache => {
let map = new Map();
- let args = {map, cache};
+ let args = { map, cache };
// in the fetchXxx methods we add to a map whatever license(s)
// URLs and source code references we can find in various formats
// (WebLabels is currently the only implementation), keyed by script URLs.
await Promise.all([
- fetchWebLabels(args),
- // fetchXmlSpdx(args),
- // fetchTxtSpdx(args),
- // ...
+ fetchWebLabels(args),
+ // fetchXmlSpdx(args),
+ // fetchTxtSpdx(args),
+ // ...
]);
return map;
}
let handlers = {
async checkLicensedScript(m) {
- let {url, cache} = m;
+ let { url, cache } = m;
if (!licensedScripts) licensedScripts = await fetchLicenseInfo(cache);
return licensedScripts.get(url) || licensedScripts.get(url.replace(/\?.*/, ''));
}
@@ -86,7 +86,7 @@
browser.runtime.onMessage.addListener(async m => {
if (m.action in handlers) try {
debug("Received message", m);
- let result = await handlers[m.action](m);
+ let result = await handlers[m.action](m);
return result;
} catch (e) {
console.error(e);
diff --git a/evaluation test/test.js b/evaluation test/test.js
index 091c47c..d6dbd4a 100644
--- a/evaluation test/test.js
+++ b/evaluation test/test.js
@@ -2,834 +2,834 @@ var acornLoose = require('acorn-loose');
var acorn = require("acorn");
var fname_data = {
- "WebGLShader": true,
- "WebGLShaderPrecisionFormat": true,
- "WebGLQuery": true,
- "WebGLRenderbuffer": true,
- "WebGLSampler": true,
- "WebGLUniformLocation": true,
- "WebGLFramebuffer": true,
- "WebGLProgram": true,
- "WebGLContextEvent": true,
- "WebGL2RenderingContext": true,
- "WebGLTexture": true,
- "WebGLRenderingContext": true,
- "WebGLVertexArrayObject": true,
- "WebGLActiveInfo": true,
- "WebGLTransformFeedback": true,
- "WebGLSync": true,
- "WebGLBuffer": true,
- "cat_svg": true,
- "SVGPoint": true,
- "SVGEllipseElement": true,
- "SVGRadialGradientElement": true,
- "SVGComponentTransferFunctionElement": true,
- "SVGPathSegCurvetoQuadraticAbs": true,
- "SVGAnimatedNumberList": true,
- "SVGPathSegCurvetoQuadraticSmoothRel": true,
- "SVGFEColorMatrixElement": true,
- "SVGPathSegLinetoHorizontalAbs": true,
- "SVGLinearGradientElement": true,
- "SVGStyleElement": true,
- "SVGPathSegMovetoRel": true,
- "SVGStopElement": true,
- "SVGPathSegLinetoRel": true,
- "SVGFEConvolveMatrixElement": true,
- "SVGAnimatedAngle": true,
- "SVGPathSegLinetoAbs": true,
- "SVGPreserveAspectRatio": true,
- "SVGFEOffsetElement": true,
- "SVGFEImageElement": true,
- "SVGFEDiffuseLightingElement": true,
- "SVGAnimatedNumber": true,
- "SVGTextElement": true,
- "SVGFESpotLightElement": true,
- "SVGFEMorphologyElement": true,
- "SVGAngle": true,
- "SVGScriptElement": true,
- "SVGFEDropShadowElement": true,
- "SVGPathSegArcRel": true,
- "SVGNumber": true,
- "SVGPathSegLinetoHorizontalRel": true,
- "SVGFEFuncBElement": true,
- "SVGClipPathElement": true,
- "SVGPathSeg": true,
- "SVGUseElement": true,
- "SVGPathSegArcAbs": true,
- "SVGPathSegCurvetoQuadraticSmoothAbs": true,
- "SVGRect": true,
- "SVGAnimatedPreserveAspectRatio": true,
- "SVGImageElement": true,
- "SVGAnimatedEnumeration": true,
- "SVGAnimatedLengthList": true,
- "SVGFEFloodElement": true,
- "SVGFECompositeElement": true,
- "SVGAElement": true,
- "SVGAnimatedBoolean": true,
- "SVGMaskElement": true,
- "SVGFilterElement": true,
- "SVGPathSegLinetoVerticalRel": true,
- "SVGAnimatedInteger": true,
- "SVGTSpanElement": true,
- "SVGMarkerElement": true,
- "SVGStringList": true,
- "SVGTransform": true,
- "SVGTitleElement": true,
- "SVGFEBlendElement": true,
- "SVGTextPositioningElement": true,
- "SVGFEFuncGElement": true,
- "SVGFEPointLightElement": true,
- "SVGAnimateElement": true,
- "SVGPolylineElement": true,
- "SVGDefsElement": true,
- "SVGPathSegList": true,
- "SVGAnimatedTransformList": true,
- "SVGPathSegClosePath": true,
- "SVGGradientElement": true,
- "SVGSwitchElement": true,
- "SVGViewElement": true,
- "SVGUnitTypes": true,
- "SVGPathSegMovetoAbs": true,
- "SVGSymbolElement": true,
- "SVGFEFuncAElement": true,
- "SVGAnimatedString": true,
- "SVGFEMergeElement": true,
- "SVGPathSegLinetoVerticalAbs": true,
- "SVGAnimationElement": true,
- "SVGPathSegCurvetoCubicAbs": true,
- "SVGLength": true,
- "SVGTextPathElement": true,
- "SVGPolygonElement": true,
- "SVGAnimatedRect": true,
- "SVGPathSegCurvetoCubicRel": true,
- "SVGFEFuncRElement": true,
- "SVGLengthList": true,
- "SVGTextContentElement": true,
- "SVGFETurbulenceElement": true,
- "SVGMatrix": true,
- "SVGZoomAndPan": true,
- "SVGMetadataElement": true,
- "SVGFEDistantLightElement": true,
- "SVGAnimateMotionElement": true,
- "SVGDescElement": true,
- "SVGPathSegCurvetoCubicSmoothRel": true,
- "SVGFESpecularLightingElement": true,
- "SVGFEGaussianBlurElement": true,
- "SVGFEComponentTransferElement": true,
- "SVGNumberList": true,
- "SVGTransformList": true,
- "SVGForeignObjectElement": true,
- "SVGRectElement": true,
- "SVGFEDisplacementMapElement": true,
- "SVGAnimateTransformElement": true,
- "SVGAnimatedLength": true,
- "SVGPointList": true,
- "SVGPatternElement": true,
- "SVGPathSegCurvetoCubicSmoothAbs": true,
- "SVGCircleElement": true,
- "SVGSetElement": true,
- "SVGFETileElement": true,
- "SVGMPathElement": true,
- "SVGFEMergeNodeElement": true,
- "SVGPathSegCurvetoQuadraticRel": true,
- "SVGElement": true,
- "SVGGraphicsElement": true,
- "SVGSVGElement": true,
- "SVGGElement": true,
- "SVGGeometryElement": true,
- "SVGPathElement": true,
- "SVGLineElement": true,
- "cat_html": true,
- "HTMLTimeElement": true,
- "HTMLPictureElement": true,
- "HTMLMenuItemElement": true,
- "HTMLFormElement": true,
- "HTMLOptionElement": true,
- "HTMLCanvasElement": true,
- "HTMLTableSectionElement": true,
- "HTMLSelectElement": true,
- "HTMLUListElement": true,
- "HTMLMetaElement": true,
- "HTMLLinkElement": true,
- "HTMLBaseElement": true,
- "HTMLDataListElement": true,
- "HTMLInputElement": true,
- "HTMLMeterElement": true,
- "HTMLSourceElement": true,
- "HTMLTrackElement": true,
- "HTMLTableColElement": true,
- "HTMLFieldSetElement": true,
- "HTMLDirectoryElement": true,
- "HTMLTableCellElement": true,
- "HTMLStyleElement": true,
- "HTMLAudioElement": true,
- "HTMLLegendElement": true,
- "HTMLOListElement": true,
- "HTMLEmbedElement": true,
- "HTMLQuoteElement": true,
- "HTMLMenuElement": true,
- "HTMLHeadElement": true,
- "HTMLUnknownElement": true,
- "HTMLBRElement": true,
- "HTMLProgressElement": true,
- "HTMLMediaElement": true,
- "HTMLFormControlsCollection": true,
- "HTMLCollection": true,
- "HTMLLIElement": true,
- "HTMLDetailsElement": true,
- "HTMLObjectElement": true,
- "HTMLHeadingElement": true,
- "HTMLTableCaptionElement": true,
- "HTMLPreElement": true,
- "HTMLAllCollection": true,
- "HTMLFrameSetElement": true,
- "HTMLFontElement": true,
- "HTMLFrameElement": true,
- "HTMLAnchorElement": true,
- "HTMLOptGroupElement": true,
- "HTMLVideoElement": true,
- "HTMLModElement": true,
- "HTMLBodyElement": true,
- "HTMLTableElement": true,
- "HTMLButtonElement": true,
- "HTMLTableRowElement": true,
- "HTMLAreaElement": true,
- "HTMLDataElement": true,
- "HTMLParamElement": true,
- "HTMLLabelElement": true,
- "HTMLTemplateElement": true,
- "HTMLOptionsCollection": true,
- "HTMLIFrameElement": true,
- "HTMLTitleElement": true,
- "HTMLMapElement": true,
- "HTMLOutputElement": true,
- "HTMLDListElement": true,
- "HTMLParagraphElement": true,
- "HTMLHRElement": true,
- "HTMLImageElement": true,
- "HTMLDocument": true,
- "HTMLElement": true,
- "HTMLScriptElement": true,
- "HTMLHtmlElement": true,
- "HTMLTextAreaElement": true,
- "HTMLDivElement": true,
- "HTMLSpanElement": true,
- "cat_css": true,
- "CSSStyleRule": true,
- "CSSFontFaceRule": true,
- "CSSPrimitiveValue": true,
- "CSSStyleDeclaration": true,
- "CSSStyleSheet": true,
- "CSSPageRule": true,
- "CSSSupportsRule": true,
- "CSSMozDocumentRule": true,
- "CSSKeyframeRule": true,
- "CSSGroupingRule": true,
- "CSS2Properties": true,
- "CSSFontFeatureValuesRule": true,
- "CSSRuleList": true,
- "CSSPseudoElement": true,
- "CSSMediaRule": true,
- "CSSCounterStyleRule": true,
- "CSSImportRule": true,
- "CSSTransition": true,
- "CSSAnimation": true,
- "CSSValue": true,
- "CSSNamespaceRule": true,
- "CSSRule": true,
- "CSS": true,
- "CSSKeyframesRule": true,
- "CSSConditionRule": true,
- "CSSValueList": true,
- "cat_event": true,
- "ondevicemotion": true,
- "ondeviceorientation": true,
- "onabsolutedeviceorientation": true,
- "ondeviceproximity": true,
- "onuserproximity": true,
- "ondevicelight": true,
- "onvrdisplayconnect": true,
- "onvrdisplaydisconnect": true,
- "onvrdisplayactivate": true,
- "onvrdisplaydeactivate": true,
- "onvrdisplaypresentchange": true,
- "onabort": true,
- "onblur": true,
- "onfocus": true,
- "onauxclick": true,
- "oncanplay": true,
- "oncanplaythrough": true,
- "onchange": true,
- "onclick": true,
- "onclose": true,
- "oncontextmenu": true,
- "ondblclick": true,
- "ondrag": true,
- "ondragend": true,
- "ondragenter": true,
- "ondragexit": true,
- "ondragleave": true,
- "ondragover": true,
- "ondragstart": true,
- "ondrop": true,
- "ondurationchange": true,
- "onemptied": true,
- "onended": true,
- "oninput": true,
- "oninvalid": true,
- "onkeydown": true,
- "onkeypress": true,
- "onkeyup": true,
- "onload": true,
- "onloadeddata": true,
- "onloadedmetadata": true,
- "onloadend": true,
- "onloadstart": true,
- "onmousedown": true,
- "onmouseenter": true,
- "onmouseleave": true,
- "onmousemove": true,
- "onmouseout": true,
- "onmouseover": true,
- "onmouseup": true,
- "onwheel": true,
- "onpause": true,
- "onplay": true,
- "onplaying": true,
- "onprogress": true,
- "onratechange": true,
- "onreset": true,
- "onresize": true,
- "onscroll": true,
- "onseeked": true,
- "onseeking": true,
- "onselect": true,
- "onshow": true,
- "onstalled": true,
- "onsubmit": true,
- "onsuspend": true,
- "ontimeupdate": true,
- "onvolumechange": true,
- "onwaiting": true,
- "onselectstart": true,
- "ontoggle": true,
- "onpointercancel": true,
- "onpointerdown": true,
- "onpointerup": true,
- "onpointermove": true,
- "onpointerout": true,
- "onpointerover": true,
- "onpointerenter": true,
- "onpointerleave": true,
- "ongotpointercapture": true,
- "onlostpointercapture": true,
- "onmozfullscreenchange": true,
- "onmozfullscreenerror": true,
- "onanimationcancel": true,
- "onanimationend": true,
- "onanimationiteration": true,
- "onanimationstart": true,
- "ontransitioncancel": true,
- "ontransitionend": true,
- "ontransitionrun": true,
- "ontransitionstart": true,
- "onwebkitanimationend": true,
- "onwebkitanimationiteration": true,
- "onwebkitanimationstart": true,
- "onwebkittransitionend": true,
- "onerror": true,
- "onafterprint": true,
- "onbeforeprint": true,
- "onbeforeunload": true,
- "onhashchange": true,
- "onlanguagechange": true,
- "onmessage": true,
- "onmessageerror": true,
- "onoffline": true,
- "ononline": true,
- "onpagehide": true,
- "onpageshow": true,
- "onpopstate": true,
- "onstorage": true,
- "onunload": true,
- "cat_rtc": true,
- "RTCDTMFSender": true,
- "RTCStatsReport": true,
- "RTCTrackEvent": true,
- "RTCDataChannelEvent": true,
- "RTCPeerConnectionIceEvent": true,
- "RTCCertificate": true,
- "RTCDTMFToneChangeEvent": true,
- "RTCPeerConnection": true,
- "RTCIceCandidate": true,
- "RTCRtpReceiver": true,
- "RTCRtpSender": true,
- "RTCSessionDescription": true,
- "cat_vr": true,
- "VRStageParameters": true,
- "VRFrameData": true,
- "VRDisplay": true,
- "VRDisplayEvent": true,
- "VRFieldOfView": true,
- "VRDisplayCapabilities": true,
- "VREyeParameters": true,
- "VRPose": true,
- "cat_dom": true,
- "DOMStringMap": true,
- "DOMRectReadOnly": true,
- "DOMException": true,
- "DOMRect": true,
- "DOMMatrix": true,
- "DOMMatrixReadOnly": true,
- "DOMPointReadOnly": true,
- "DOMPoint": true,
- "DOMQuad": true,
- "DOMRequest": true,
- "DOMParser": true,
- "DOMTokenList": true,
- "DOMStringList": true,
- "DOMImplementation": true,
- "DOMError": true,
- "DOMRectList": true,
- "DOMCursor": true,
- "cat_idb": true,
- "IDBFileRequest": true,
- "IDBTransaction": true,
- "IDBCursor": true,
- "IDBFileHandle": true,
- "IDBMutableFile": true,
- "IDBKeyRange": true,
- "IDBVersionChangeEvent": true,
- "IDBObjectStore": true,
- "IDBFactory": true,
- "IDBCursorWithValue": true,
- "IDBOpenDBRequest": true,
- "IDBRequest": true,
- "IDBIndex": true,
- "IDBDatabase": true,
- "cat_audio": true,
- "AudioContext": true,
- "AudioBuffer": true,
- "AudioBufferSourceNode": true,
- "Audio": true,
- "MediaElementAudioSourceNode": true,
- "AudioNode": true,
- "BaseAudioContext": true,
- "AudioListener": true,
- "MediaStreamAudioSourceNode": true,
- "OfflineAudioContext": true,
- "AudioDestinationNode": true,
- "AudioParam": true,
- "MediaStreamAudioDestinationNode": true,
- "OfflineAudioCompletionEvent": true,
- "AudioStreamTrack": true,
- "AudioScheduledSourceNode": true,
- "AudioProcessingEvent": true,
- "cat_gamepad": true,
- "GamepadButton": true,
- "GamepadHapticActuator": true,
- "GamepadAxisMoveEvent": true,
- "GamepadPose": true,
- "GamepadEvent": true,
- "Gamepad": true,
- "GamepadButtonEvent": true,
- "cat_media": true,
- "MediaKeys": true,
- "MediaKeyError": true,
- "MediaSource": true,
- "MediaDevices": true,
- "MediaKeyStatusMap": true,
- "MediaStreamTrackEvent": true,
- "MediaRecorder": true,
- "MediaQueryListEvent": true,
- "MediaStream": true,
- "MediaEncryptedEvent": true,
- "MediaStreamTrack": true,
- "MediaError": true,
- "MediaStreamEvent": true,
- "MediaQueryList": true,
- "MediaKeySystemAccess": true,
- "MediaDeviceInfo": true,
- "MediaKeySession": true,
- "MediaList": true,
- "MediaRecorderErrorEvent": true,
- "MediaKeyMessageEvent": true,
- "cat_event2": true,
- "SpeechSynthesisErrorEvent": true,
- "BeforeUnloadEvent": true,
- "CustomEvent": true,
- "PageTransitionEvent": true,
- "PopupBlockedEvent": true,
- "CloseEvent": true,
- "ProgressEvent": true,
- "MutationEvent": true,
- "MessageEvent": true,
- "FocusEvent": true,
- "TrackEvent": true,
- "DeviceMotionEvent": true,
- "TimeEvent": true,
- "PointerEvent": true,
- "UserProximityEvent": true,
- "StorageEvent": true,
- "DragEvent": true,
- "MouseScrollEvent": true,
- "EventSource": true,
- "PopStateEvent": true,
- "DeviceProximityEvent": true,
- "SpeechSynthesisEvent": true,
- "XMLHttpRequestEventTarget": true,
- "ClipboardEvent": true,
- "AnimationPlaybackEvent": true,
- "DeviceLightEvent": true,
- "BlobEvent": true,
- "MouseEvent": true,
- "WheelEvent": true,
- "InputEvent": true,
- "HashChangeEvent": true,
- "DeviceOrientationEvent": true,
- "CompositionEvent": true,
- "KeyEvent": true,
- "ScrollAreaEvent": true,
- "KeyboardEvent": true,
- "TransitionEvent": true,
- "ErrorEvent": true,
- "AnimationEvent": true,
- "FontFaceSetLoadEvent": true,
- "EventTarget": true,
- "captureEvents": true,
- "releaseEvents": true,
- "Event": true,
- "UIEvent": true,
- "cat_other": false,
- "undefined": false,
- "Array": false,
- "Boolean": false,
- "JSON": false,
- "Date": false,
- "Math": false,
- "Number": false,
- "String": false,
- "RegExp": false,
- "Error": false,
- "InternalError": false,
- "EvalError": false,
- "RangeError": false,
- "ReferenceError": false,
- "SyntaxError": false,
- "TypeError": false,
- "URIError": false,
- "ArrayBuffer": true,
- "Int8Array": true,
- "Uint8Array": true,
- "Int16Array": true,
- "Uint16Array": true,
- "Int32Array": true,
- "Uint32Array": true,
- "Float32Array": true,
- "Float64Array": true,
- "Uint8ClampedArray": true,
- "Proxy": true,
- "WeakMap": true,
- "Map": true,
- "Set": true,
- "DataView": false,
- "Symbol": false,
- "SharedArrayBuffer": true,
- "Intl": false,
- "TypedObject": true,
- "Reflect": true,
- "SIMD": true,
- "WeakSet": true,
- "Atomics": true,
- "Promise": true,
- "WebAssembly": true,
- "NaN": false,
- "Infinity": false,
- "isNaN": false,
- "isFinite": false,
- "parseFloat": false,
- "parseInt": false,
- "escape": false,
- "unescape": false,
- "decodeURI": false,
- "encodeURI": false,
- "decodeURIComponent": false,
- "encodeURIComponent": false,
- "uneval": false,
- "BatteryManager": true,
- "CanvasGradient": true,
- "TextDecoder": true,
- "Plugin": true,
- "PushManager": true,
- "ChannelMergerNode": true,
- "PerformanceResourceTiming": true,
- "ServiceWorker": true,
- "TextTrackCueList": true,
- "PerformanceEntry": true,
- "TextTrackList": true,
- "StyleSheet": true,
- "PerformanceMeasure": true,
- "DesktopNotificationCenter": true,
- "Comment": true,
- "DelayNode": true,
- "XPathResult": true,
- "CDATASection": true,
- "MessageChannel": true,
- "BiquadFilterNode": true,
- "SpeechSynthesisUtterance": true,
- "Crypto": true,
- "Navigator": true,
- "FileList": true,
- "URLSearchParams": false,
- "ServiceWorkerContainer": true,
- "ValidityState": true,
- "ProcessingInstruction": true,
- "AbortSignal": true,
- "FontFace": true,
- "FileReader": true,
- "Worker": true,
- "External": true,
- "ImageBitmap": true,
- "TimeRanges": true,
- "Option": true,
- "TextTrack": true,
- "Image": true,
- "AnimationTimeline": true,
- "VideoPlaybackQuality": true,
- "VTTCue": true,
- "Storage": true,
- "XPathExpression": true,
- "CharacterData": false,
- "TextMetrics": true,
- "AnimationEffectReadOnly": true,
- "PerformanceTiming": false,
- "PerformanceMark": true,
- "ImageBitmapRenderingContext": true,
- "Headers": true,
- "Range": false,
- "Rect": true,
- "AnimationEffectTimingReadOnly": true,
- "KeyframeEffect": true,
- "Permissions": true,
- "TextEncoder": true,
- "ImageData": true,
- "SpeechSynthesisVoice": true,
- "StorageManager": true,
- "TextTrackCue": true,
- "WebSocket": true,
- "DocumentType": true,
- "XPathEvaluator": true,
- "PerformanceNavigationTiming": true,
- "IdleDeadline": true,
- "FileSystem": true,
- "FileSystemFileEntry": true,
- "CacheStorage": true,
- "MimeType": true,
- "PannerNode": true,
- "NodeFilter": true,
- "StereoPannerNode": true,
- "console": false,
- "DynamicsCompressorNode": true,
- "PaintRequest": true,
- "RGBColor": true,
- "FontFaceSet": false,
- "PaintRequestList": true,
- "FileSystemEntry": true,
- "XMLDocument": false,
- "SourceBuffer": false,
- "Screen": true,
- "NamedNodeMap": false,
- "History": true,
- "Response": true,
- "AnimationEffectTiming": true,
- "ServiceWorkerRegistration": true,
- "CanvasRenderingContext2D": true,
- "ScriptProcessorNode": true,
- "FileSystemDirectoryReader": true,
- "MimeTypeArray": true,
- "CanvasCaptureMediaStream": true,
- "Directory": true,
- "mozRTCPeerConnection": true,
- "PerformanceObserverEntryList": true,
- "PushSubscriptionOptions": true,
- "Text": false,
- "IntersectionObserverEntry": true,
- "SubtleCrypto": true,
- "Animation": true,
- "DataTransfer": true,
- "TreeWalker": true,
- "XMLHttpRequest": true,
- "LocalMediaStream": true,
- "ConvolverNode": true,
- "WaveShaperNode": true,
- "DataTransferItemList": false,
- "Request": true,
- "SourceBufferList": false,
- "XSLTProcessor": true,
- "XMLHttpRequestUpload": true,
- "SharedWorker": true,
- "Notification": false,
- "DataTransferItem": true,
- "AnalyserNode": true,
- "mozRTCIceCandidate": true,
- "PerformanceObserver": true,
- "OfflineResourceList": true,
- "FileSystemDirectoryEntry": true,
- "DesktopNotification": false,
- "DataChannel": true,
- "IIRFilterNode": true,
- "ChannelSplitterNode": true,
- "File": true,
- "ConstantSourceNode": true,
- "CryptoKey": true,
- "GainNode": true,
- "AbortController": true,
- "Attr": true,
- "SpeechSynthesis": true,
- "PushSubscription": false,
- "XMLStylesheetProcessingInstruction": false,
- "NodeIterator": true,
- "VideoStreamTrack": true,
- "XMLSerializer": true,
- "CaretPosition": true,
- "FormData": true,
- "CanvasPattern": true,
- "mozRTCSessionDescription": true,
- "Path2D": true,
- "PerformanceNavigation": true,
- "URL": false,
- "PluginArray": true,
- "MutationRecord": true,
- "WebKitCSSMatrix": true,
- "PeriodicWave": true,
- "DocumentFragment": true,
- "DocumentTimeline": false,
- "ScreenOrientation": true,
- "BroadcastChannel": true,
- "PermissionStatus": true,
- "IntersectionObserver": true,
- "Blob": true,
- "MessagePort": true,
- "BarProp": true,
- "OscillatorNode": true,
- "Cache": true,
- "RadioNodeList": true,
- "KeyframeEffectReadOnly": true,
- "InstallTrigger": true,
- "Function": false,
- "Object": false,
- "eval": true,
- "Window": false,
- "close": false,
- "stop": false,
- "focus": false,
- "blur": false,
- "open": true,
- "alert": false,
- "confirm": false,
- "prompt": false,
- "print": false,
- "postMessage": true,
- "getSelection": true,
- "getComputedStyle": true,
- "matchMedia": true,
- "moveTo": false,
- "moveBy": false,
- "resizeTo": false,
- "resizeBy": false,
- "scroll": false,
- "scrollTo": false,
- "scrollBy": false,
- "requestAnimationFrame": true,
- "cancelAnimationFrame": true,
- "getDefaultComputedStyle": false,
- "scrollByLines": false,
- "scrollByPages": false,
- "sizeToContent": false,
- "updateCommands": true,
- "find": false,
- "dump": true,
- "setResizable": false,
- "requestIdleCallback": false,
- "cancelIdleCallback": false,
- "btoa": true,
- "atob": true,
- "setTimeout": true,
- "clearTimeout": true,
- "setInterval": true,
- "clearInterval": true,
- "createImageBitmap": true,
- "fetch": true,
- "self": true,
- "name": false,
- "history": true,
- "locationbar": true,
- "menubar": true,
- "personalbar": true,
- "scrollbars": true,
- "statusbar": true,
- "toolbar": true,
- "status": true,
- "closed": true,
- "frames": true,
- "length": false,
- "opener": true,
- "parent": true,
- "frameElement": true,
- "navigator": true,
- "external": true,
- "applicationCache": true,
- "screen": true,
- "innerWidth": true,
- "innerHeight": true,
- "scrollX": true,
- "pageXOffset": true,
- "scrollY": true,
- "pageYOffset": true,
- "screenX": true,
- "screenY": true,
- "outerWidth": true,
- "outerHeight": true,
- "performance": true,
- "mozInnerScreenX": true,
- "mozInnerScreenY": true,
- "devicePixelRatio": true,
- "scrollMaxX": true,
- "scrollMaxY": true,
- "fullScreen": false,
- "mozPaintCount": true,
- "sidebar": false,
- "crypto": true,
- "speechSynthesis": true,
- "localStorage": true,
- "origin": true,
- "isSecureContext": false,
- "indexedDB": true,
- "caches": true,
- "sessionStorage": true,
- "window": false,
- "document": true,
- "location": false,
- "top": true,
- "netscape": true,
- "Node": true,
- "Document": true,
- "Performance": false,
- "startProfiling": true,
- "stopProfiling": true,
- "pauseProfilers": true,
- "resumeProfilers": true,
- "dumpProfile": true,
- "getMaxGCPauseSinceClear": true,
- "clearMaxGCPauseAccumulator": true,
- "Location": true,
- "StyleSheetList": false,
- "Selection": false,
- "Element": true,
- "AnonymousContent": false,
- "MutationObserver": true,
- "NodeList": true,
- "StopIteration": true
+ "WebGLShader": true,
+ "WebGLShaderPrecisionFormat": true,
+ "WebGLQuery": true,
+ "WebGLRenderbuffer": true,
+ "WebGLSampler": true,
+ "WebGLUniformLocation": true,
+ "WebGLFramebuffer": true,
+ "WebGLProgram": true,
+ "WebGLContextEvent": true,
+ "WebGL2RenderingContext": true,
+ "WebGLTexture": true,
+ "WebGLRenderingContext": true,
+ "WebGLVertexArrayObject": true,
+ "WebGLActiveInfo": true,
+ "WebGLTransformFeedback": true,
+ "WebGLSync": true,
+ "WebGLBuffer": true,
+ "cat_svg": true,
+ "SVGPoint": true,
+ "SVGEllipseElement": true,
+ "SVGRadialGradientElement": true,
+ "SVGComponentTransferFunctionElement": true,
+ "SVGPathSegCurvetoQuadraticAbs": true,
+ "SVGAnimatedNumberList": true,
+ "SVGPathSegCurvetoQuadraticSmoothRel": true,
+ "SVGFEColorMatrixElement": true,
+ "SVGPathSegLinetoHorizontalAbs": true,
+ "SVGLinearGradientElement": true,
+ "SVGStyleElement": true,
+ "SVGPathSegMovetoRel": true,
+ "SVGStopElement": true,
+ "SVGPathSegLinetoRel": true,
+ "SVGFEConvolveMatrixElement": true,
+ "SVGAnimatedAngle": true,
+ "SVGPathSegLinetoAbs": true,
+ "SVGPreserveAspectRatio": true,
+ "SVGFEOffsetElement": true,
+ "SVGFEImageElement": true,
+ "SVGFEDiffuseLightingElement": true,
+ "SVGAnimatedNumber": true,
+ "SVGTextElement": true,
+ "SVGFESpotLightElement": true,
+ "SVGFEMorphologyElement": true,
+ "SVGAngle": true,
+ "SVGScriptElement": true,
+ "SVGFEDropShadowElement": true,
+ "SVGPathSegArcRel": true,
+ "SVGNumber": true,
+ "SVGPathSegLinetoHorizontalRel": true,
+ "SVGFEFuncBElement": true,
+ "SVGClipPathElement": true,
+ "SVGPathSeg": true,
+ "SVGUseElement": true,
+ "SVGPathSegArcAbs": true,
+ "SVGPathSegCurvetoQuadraticSmoothAbs": true,
+ "SVGRect": true,
+ "SVGAnimatedPreserveAspectRatio": true,
+ "SVGImageElement": true,
+ "SVGAnimatedEnumeration": true,
+ "SVGAnimatedLengthList": true,
+ "SVGFEFloodElement": true,
+ "SVGFECompositeElement": true,
+ "SVGAElement": true,
+ "SVGAnimatedBoolean": true,
+ "SVGMaskElement": true,
+ "SVGFilterElement": true,
+ "SVGPathSegLinetoVerticalRel": true,
+ "SVGAnimatedInteger": true,
+ "SVGTSpanElement": true,
+ "SVGMarkerElement": true,
+ "SVGStringList": true,
+ "SVGTransform": true,
+ "SVGTitleElement": true,
+ "SVGFEBlendElement": true,
+ "SVGTextPositioningElement": true,
+ "SVGFEFuncGElement": true,
+ "SVGFEPointLightElement": true,
+ "SVGAnimateElement": true,
+ "SVGPolylineElement": true,
+ "SVGDefsElement": true,
+ "SVGPathSegList": true,
+ "SVGAnimatedTransformList": true,
+ "SVGPathSegClosePath": true,
+ "SVGGradientElement": true,
+ "SVGSwitchElement": true,
+ "SVGViewElement": true,
+ "SVGUnitTypes": true,
+ "SVGPathSegMovetoAbs": true,
+ "SVGSymbolElement": true,
+ "SVGFEFuncAElement": true,
+ "SVGAnimatedString": true,
+ "SVGFEMergeElement": true,
+ "SVGPathSegLinetoVerticalAbs": true,
+ "SVGAnimationElement": true,
+ "SVGPathSegCurvetoCubicAbs": true,
+ "SVGLength": true,
+ "SVGTextPathElement": true,
+ "SVGPolygonElement": true,
+ "SVGAnimatedRect": true,
+ "SVGPathSegCurvetoCubicRel": true,
+ "SVGFEFuncRElement": true,
+ "SVGLengthList": true,
+ "SVGTextContentElement": true,
+ "SVGFETurbulenceElement": true,
+ "SVGMatrix": true,
+ "SVGZoomAndPan": true,
+ "SVGMetadataElement": true,
+ "SVGFEDistantLightElement": true,
+ "SVGAnimateMotionElement": true,
+ "SVGDescElement": true,
+ "SVGPathSegCurvetoCubicSmoothRel": true,
+ "SVGFESpecularLightingElement": true,
+ "SVGFEGaussianBlurElement": true,
+ "SVGFEComponentTransferElement": true,
+ "SVGNumberList": true,
+ "SVGTransformList": true,
+ "SVGForeignObjectElement": true,
+ "SVGRectElement": true,
+ "SVGFEDisplacementMapElement": true,
+ "SVGAnimateTransformElement": true,
+ "SVGAnimatedLength": true,
+ "SVGPointList": true,
+ "SVGPatternElement": true,
+ "SVGPathSegCurvetoCubicSmoothAbs": true,
+ "SVGCircleElement": true,
+ "SVGSetElement": true,
+ "SVGFETileElement": true,
+ "SVGMPathElement": true,
+ "SVGFEMergeNodeElement": true,
+ "SVGPathSegCurvetoQuadraticRel": true,
+ "SVGElement": true,
+ "SVGGraphicsElement": true,
+ "SVGSVGElement": true,
+ "SVGGElement": true,
+ "SVGGeometryElement": true,
+ "SVGPathElement": true,
+ "SVGLineElement": true,
+ "cat_html": true,
+ "HTMLTimeElement": true,
+ "HTMLPictureElement": true,
+ "HTMLMenuItemElement": true,
+ "HTMLFormElement": true,
+ "HTMLOptionElement": true,
+ "HTMLCanvasElement": true,
+ "HTMLTableSectionElement": true,
+ "HTMLSelectElement": true,
+ "HTMLUListElement": true,
+ "HTMLMetaElement": true,
+ "HTMLLinkElement": true,
+ "HTMLBaseElement": true,
+ "HTMLDataListElement": true,
+ "HTMLInputElement": true,
+ "HTMLMeterElement": true,
+ "HTMLSourceElement": true,
+ "HTMLTrackElement": true,
+ "HTMLTableColElement": true,
+ "HTMLFieldSetElement": true,
+ "HTMLDirectoryElement": true,
+ "HTMLTableCellElement": true,
+ "HTMLStyleElement": true,
+ "HTMLAudioElement": true,
+ "HTMLLegendElement": true,
+ "HTMLOListElement": true,
+ "HTMLEmbedElement": true,
+ "HTMLQuoteElement": true,
+ "HTMLMenuElement": true,
+ "HTMLHeadElement": true,
+ "HTMLUnknownElement": true,
+ "HTMLBRElement": true,
+ "HTMLProgressElement": true,
+ "HTMLMediaElement": true,
+ "HTMLFormControlsCollection": true,
+ "HTMLCollection": true,
+ "HTMLLIElement": true,
+ "HTMLDetailsElement": true,
+ "HTMLObjectElement": true,
+ "HTMLHeadingElement": true,
+ "HTMLTableCaptionElement": true,
+ "HTMLPreElement": true,
+ "HTMLAllCollection": true,
+ "HTMLFrameSetElement": true,
+ "HTMLFontElement": true,
+ "HTMLFrameElement": true,
+ "HTMLAnchorElement": true,
+ "HTMLOptGroupElement": true,
+ "HTMLVideoElement": true,
+ "HTMLModElement": true,
+ "HTMLBodyElement": true,
+ "HTMLTableElement": true,
+ "HTMLButtonElement": true,
+ "HTMLTableRowElement": true,
+ "HTMLAreaElement": true,
+ "HTMLDataElement": true,
+ "HTMLParamElement": true,
+ "HTMLLabelElement": true,
+ "HTMLTemplateElement": true,
+ "HTMLOptionsCollection": true,
+ "HTMLIFrameElement": true,
+ "HTMLTitleElement": true,
+ "HTMLMapElement": true,
+ "HTMLOutputElement": true,
+ "HTMLDListElement": true,
+ "HTMLParagraphElement": true,
+ "HTMLHRElement": true,
+ "HTMLImageElement": true,
+ "HTMLDocument": true,
+ "HTMLElement": true,
+ "HTMLScriptElement": true,
+ "HTMLHtmlElement": true,
+ "HTMLTextAreaElement": true,
+ "HTMLDivElement": true,
+ "HTMLSpanElement": true,
+ "cat_css": true,
+ "CSSStyleRule": true,
+ "CSSFontFaceRule": true,
+ "CSSPrimitiveValue": true,
+ "CSSStyleDeclaration": true,
+ "CSSStyleSheet": true,
+ "CSSPageRule": true,
+ "CSSSupportsRule": true,
+ "CSSMozDocumentRule": true,
+ "CSSKeyframeRule": true,
+ "CSSGroupingRule": true,
+ "CSS2Properties": true,
+ "CSSFontFeatureValuesRule": true,
+ "CSSRuleList": true,
+ "CSSPseudoElement": true,
+ "CSSMediaRule": true,
+ "CSSCounterStyleRule": true,
+ "CSSImportRule": true,
+ "CSSTransition": true,
+ "CSSAnimation": true,
+ "CSSValue": true,
+ "CSSNamespaceRule": true,
+ "CSSRule": true,
+ "CSS": true,
+ "CSSKeyframesRule": true,
+ "CSSConditionRule": true,
+ "CSSValueList": true,
+ "cat_event": true,
+ "ondevicemotion": true,
+ "ondeviceorientation": true,
+ "onabsolutedeviceorientation": true,
+ "ondeviceproximity": true,
+ "onuserproximity": true,
+ "ondevicelight": true,
+ "onvrdisplayconnect": true,
+ "onvrdisplaydisconnect": true,
+ "onvrdisplayactivate": true,
+ "onvrdisplaydeactivate": true,
+ "onvrdisplaypresentchange": true,
+ "onabort": true,
+ "onblur": true,
+ "onfocus": true,
+ "onauxclick": true,
+ "oncanplay": true,
+ "oncanplaythrough": true,
+ "onchange": true,
+ "onclick": true,
+ "onclose": true,
+ "oncontextmenu": true,
+ "ondblclick": true,
+ "ondrag": true,
+ "ondragend": true,
+ "ondragenter": true,
+ "ondragexit": true,
+ "ondragleave": true,
+ "ondragover": true,
+ "ondragstart": true,
+ "ondrop": true,
+ "ondurationchange": true,
+ "onemptied": true,
+ "onended": true,
+ "oninput": true,
+ "oninvalid": true,
+ "onkeydown": true,
+ "onkeypress": true,
+ "onkeyup": true,
+ "onload": true,
+ "onloadeddata": true,
+ "onloadedmetadata": true,
+ "onloadend": true,
+ "onloadstart": true,
+ "onmousedown": true,
+ "onmouseenter": true,
+ "onmouseleave": true,
+ "onmousemove": true,
+ "onmouseout": true,
+ "onmouseover": true,
+ "onmouseup": true,
+ "onwheel": true,
+ "onpause": true,
+ "onplay": true,
+ "onplaying": true,
+ "onprogress": true,
+ "onratechange": true,
+ "onreset": true,
+ "onresize": true,
+ "onscroll": true,
+ "onseeked": true,
+ "onseeking": true,
+ "onselect": true,
+ "onshow": true,
+ "onstalled": true,
+ "onsubmit": true,
+ "onsuspend": true,
+ "ontimeupdate": true,
+ "onvolumechange": true,
+ "onwaiting": true,
+ "onselectstart": true,
+ "ontoggle": true,
+ "onpointercancel": true,
+ "onpointerdown": true,
+ "onpointerup": true,
+ "onpointermove": true,
+ "onpointerout": true,
+ "onpointerover": true,
+ "onpointerenter": true,
+ "onpointerleave": true,
+ "ongotpointercapture": true,
+ "onlostpointercapture": true,
+ "onmozfullscreenchange": true,
+ "onmozfullscreenerror": true,
+ "onanimationcancel": true,
+ "onanimationend": true,
+ "onanimationiteration": true,
+ "onanimationstart": true,
+ "ontransitioncancel": true,
+ "ontransitionend": true,
+ "ontransitionrun": true,
+ "ontransitionstart": true,
+ "onwebkitanimationend": true,
+ "onwebkitanimationiteration": true,
+ "onwebkitanimationstart": true,
+ "onwebkittransitionend": true,
+ "onerror": true,
+ "onafterprint": true,
+ "onbeforeprint": true,
+ "onbeforeunload": true,
+ "onhashchange": true,
+ "onlanguagechange": true,
+ "onmessage": true,
+ "onmessageerror": true,
+ "onoffline": true,
+ "ononline": true,
+ "onpagehide": true,
+ "onpageshow": true,
+ "onpopstate": true,
+ "onstorage": true,
+ "onunload": true,
+ "cat_rtc": true,
+ "RTCDTMFSender": true,
+ "RTCStatsReport": true,
+ "RTCTrackEvent": true,
+ "RTCDataChannelEvent": true,
+ "RTCPeerConnectionIceEvent": true,
+ "RTCCertificate": true,
+ "RTCDTMFToneChangeEvent": true,
+ "RTCPeerConnection": true,
+ "RTCIceCandidate": true,
+ "RTCRtpReceiver": true,
+ "RTCRtpSender": true,
+ "RTCSessionDescription": true,
+ "cat_vr": true,
+ "VRStageParameters": true,
+ "VRFrameData": true,
+ "VRDisplay": true,
+ "VRDisplayEvent": true,
+ "VRFieldOfView": true,
+ "VRDisplayCapabilities": true,
+ "VREyeParameters": true,
+ "VRPose": true,
+ "cat_dom": true,
+ "DOMStringMap": true,
+ "DOMRectReadOnly": true,
+ "DOMException": true,
+ "DOMRect": true,
+ "DOMMatrix": true,
+ "DOMMatrixReadOnly": true,
+ "DOMPointReadOnly": true,
+ "DOMPoint": true,
+ "DOMQuad": true,
+ "DOMRequest": true,
+ "DOMParser": true,
+ "DOMTokenList": true,
+ "DOMStringList": true,
+ "DOMImplementation": true,
+ "DOMError": true,
+ "DOMRectList": true,
+ "DOMCursor": true,
+ "cat_idb": true,
+ "IDBFileRequest": true,
+ "IDBTransaction": true,
+ "IDBCursor": true,
+ "IDBFileHandle": true,
+ "IDBMutableFile": true,
+ "IDBKeyRange": true,
+ "IDBVersionChangeEvent": true,
+ "IDBObjectStore": true,
+ "IDBFactory": true,
+ "IDBCursorWithValue": true,
+ "IDBOpenDBRequest": true,
+ "IDBRequest": true,
+ "IDBIndex": true,
+ "IDBDatabase": true,
+ "cat_audio": true,
+ "AudioContext": true,
+ "AudioBuffer": true,
+ "AudioBufferSourceNode": true,
+ "Audio": true,
+ "MediaElementAudioSourceNode": true,
+ "AudioNode": true,
+ "BaseAudioContext": true,
+ "AudioListener": true,
+ "MediaStreamAudioSourceNode": true,
+ "OfflineAudioContext": true,
+ "AudioDestinationNode": true,
+ "AudioParam": true,
+ "MediaStreamAudioDestinationNode": true,
+ "OfflineAudioCompletionEvent": true,
+ "AudioStreamTrack": true,
+ "AudioScheduledSourceNode": true,
+ "AudioProcessingEvent": true,
+ "cat_gamepad": true,
+ "GamepadButton": true,
+ "GamepadHapticActuator": true,
+ "GamepadAxisMoveEvent": true,
+ "GamepadPose": true,
+ "GamepadEvent": true,
+ "Gamepad": true,
+ "GamepadButtonEvent": true,
+ "cat_media": true,
+ "MediaKeys": true,
+ "MediaKeyError": true,
+ "MediaSource": true,
+ "MediaDevices": true,
+ "MediaKeyStatusMap": true,
+ "MediaStreamTrackEvent": true,
+ "MediaRecorder": true,
+ "MediaQueryListEvent": true,
+ "MediaStream": true,
+ "MediaEncryptedEvent": true,
+ "MediaStreamTrack": true,
+ "MediaError": true,
+ "MediaStreamEvent": true,
+ "MediaQueryList": true,
+ "MediaKeySystemAccess": true,
+ "MediaDeviceInfo": true,
+ "MediaKeySession": true,
+ "MediaList": true,
+ "MediaRecorderErrorEvent": true,
+ "MediaKeyMessageEvent": true,
+ "cat_event2": true,
+ "SpeechSynthesisErrorEvent": true,
+ "BeforeUnloadEvent": true,
+ "CustomEvent": true,
+ "PageTransitionEvent": true,
+ "PopupBlockedEvent": true,
+ "CloseEvent": true,
+ "ProgressEvent": true,
+ "MutationEvent": true,
+ "MessageEvent": true,
+ "FocusEvent": true,
+ "TrackEvent": true,
+ "DeviceMotionEvent": true,
+ "TimeEvent": true,
+ "PointerEvent": true,
+ "UserProximityEvent": true,
+ "StorageEvent": true,
+ "DragEvent": true,
+ "MouseScrollEvent": true,
+ "EventSource": true,
+ "PopStateEvent": true,
+ "DeviceProximityEvent": true,
+ "SpeechSynthesisEvent": true,
+ "XMLHttpRequestEventTarget": true,
+ "ClipboardEvent": true,
+ "AnimationPlaybackEvent": true,
+ "DeviceLightEvent": true,
+ "BlobEvent": true,
+ "MouseEvent": true,
+ "WheelEvent": true,
+ "InputEvent": true,
+ "HashChangeEvent": true,
+ "DeviceOrientationEvent": true,
+ "CompositionEvent": true,
+ "KeyEvent": true,
+ "ScrollAreaEvent": true,
+ "KeyboardEvent": true,
+ "TransitionEvent": true,
+ "ErrorEvent": true,
+ "AnimationEvent": true,
+ "FontFaceSetLoadEvent": true,
+ "EventTarget": true,
+ "captureEvents": true,
+ "releaseEvents": true,
+ "Event": true,
+ "UIEvent": true,
+ "cat_other": false,
+ "undefined": false,
+ "Array": false,
+ "Boolean": false,
+ "JSON": false,
+ "Date": false,
+ "Math": false,
+ "Number": false,
+ "String": false,
+ "RegExp": false,
+ "Error": false,
+ "InternalError": false,
+ "EvalError": false,
+ "RangeError": false,
+ "ReferenceError": false,
+ "SyntaxError": false,
+ "TypeError": false,
+ "URIError": false,
+ "ArrayBuffer": true,
+ "Int8Array": true,
+ "Uint8Array": true,
+ "Int16Array": true,
+ "Uint16Array": true,
+ "Int32Array": true,
+ "Uint32Array": true,
+ "Float32Array": true,
+ "Float64Array": true,
+ "Uint8ClampedArray": true,
+ "Proxy": true,
+ "WeakMap": true,
+ "Map": true,
+ "Set": true,
+ "DataView": false,
+ "Symbol": false,
+ "SharedArrayBuffer": true,
+ "Intl": false,
+ "TypedObject": true,
+ "Reflect": true,
+ "SIMD": true,
+ "WeakSet": true,
+ "Atomics": true,
+ "Promise": true,
+ "WebAssembly": true,
+ "NaN": false,
+ "Infinity": false,
+ "isNaN": false,
+ "isFinite": false,
+ "parseFloat": false,
+ "parseInt": false,
+ "escape": false,
+ "unescape": false,
+ "decodeURI": false,
+ "encodeURI": false,
+ "decodeURIComponent": false,
+ "encodeURIComponent": false,
+ "uneval": false,
+ "BatteryManager": true,
+ "CanvasGradient": true,
+ "TextDecoder": true,
+ "Plugin": true,
+ "PushManager": true,
+ "ChannelMergerNode": true,
+ "PerformanceResourceTiming": true,
+ "ServiceWorker": true,
+ "TextTrackCueList": true,
+ "PerformanceEntry": true,
+ "TextTrackList": true,
+ "StyleSheet": true,
+ "PerformanceMeasure": true,
+ "DesktopNotificationCenter": true,
+ "Comment": true,
+ "DelayNode": true,
+ "XPathResult": true,
+ "CDATASection": true,
+ "MessageChannel": true,
+ "BiquadFilterNode": true,
+ "SpeechSynthesisUtterance": true,
+ "Crypto": true,
+ "Navigator": true,
+ "FileList": true,
+ "URLSearchParams": false,
+ "ServiceWorkerContainer": true,
+ "ValidityState": true,
+ "ProcessingInstruction": true,
+ "AbortSignal": true,
+ "FontFace": true,
+ "FileReader": true,
+ "Worker": true,
+ "External": true,
+ "ImageBitmap": true,
+ "TimeRanges": true,
+ "Option": true,
+ "TextTrack": true,
+ "Image": true,
+ "AnimationTimeline": true,
+ "VideoPlaybackQuality": true,
+ "VTTCue": true,
+ "Storage": true,
+ "XPathExpression": true,
+ "CharacterData": false,
+ "TextMetrics": true,
+ "AnimationEffectReadOnly": true,
+ "PerformanceTiming": false,
+ "PerformanceMark": true,
+ "ImageBitmapRenderingContext": true,
+ "Headers": true,
+ "Range": false,
+ "Rect": true,
+ "AnimationEffectTimingReadOnly": true,
+ "KeyframeEffect": true,
+ "Permissions": true,
+ "TextEncoder": true,
+ "ImageData": true,
+ "SpeechSynthesisVoice": true,
+ "StorageManager": true,
+ "TextTrackCue": true,
+ "WebSocket": true,
+ "DocumentType": true,
+ "XPathEvaluator": true,
+ "PerformanceNavigationTiming": true,
+ "IdleDeadline": true,
+ "FileSystem": true,
+ "FileSystemFileEntry": true,
+ "CacheStorage": true,
+ "MimeType": true,
+ "PannerNode": true,
+ "NodeFilter": true,
+ "StereoPannerNode": true,
+ "console": false,
+ "DynamicsCompressorNode": true,
+ "PaintRequest": true,
+ "RGBColor": true,
+ "FontFaceSet": false,
+ "PaintRequestList": true,
+ "FileSystemEntry": true,
+ "XMLDocument": false,
+ "SourceBuffer": false,
+ "Screen": true,
+ "NamedNodeMap": false,
+ "History": true,
+ "Response": true,
+ "AnimationEffectTiming": true,
+ "ServiceWorkerRegistration": true,
+ "CanvasRenderingContext2D": true,
+ "ScriptProcessorNode": true,
+ "FileSystemDirectoryReader": true,
+ "MimeTypeArray": true,
+ "CanvasCaptureMediaStream": true,
+ "Directory": true,
+ "mozRTCPeerConnection": true,
+ "PerformanceObserverEntryList": true,
+ "PushSubscriptionOptions": true,
+ "Text": false,
+ "IntersectionObserverEntry": true,
+ "SubtleCrypto": true,
+ "Animation": true,
+ "DataTransfer": true,
+ "TreeWalker": true,
+ "XMLHttpRequest": true,
+ "LocalMediaStream": true,
+ "ConvolverNode": true,
+ "WaveShaperNode": true,
+ "DataTransferItemList": false,
+ "Request": true,
+ "SourceBufferList": false,
+ "XSLTProcessor": true,
+ "XMLHttpRequestUpload": true,
+ "SharedWorker": true,
+ "Notification": false,
+ "DataTransferItem": true,
+ "AnalyserNode": true,
+ "mozRTCIceCandidate": true,
+ "PerformanceObserver": true,
+ "OfflineResourceList": true,
+ "FileSystemDirectoryEntry": true,
+ "DesktopNotification": false,
+ "DataChannel": true,
+ "IIRFilterNode": true,
+ "ChannelSplitterNode": true,
+ "File": true,
+ "ConstantSourceNode": true,
+ "CryptoKey": true,
+ "GainNode": true,
+ "AbortController": true,
+ "Attr": true,
+ "SpeechSynthesis": true,
+ "PushSubscription": false,
+ "XMLStylesheetProcessingInstruction": false,
+ "NodeIterator": true,
+ "VideoStreamTrack": true,
+ "XMLSerializer": true,
+ "CaretPosition": true,
+ "FormData": true,
+ "CanvasPattern": true,
+ "mozRTCSessionDescription": true,
+ "Path2D": true,
+ "PerformanceNavigation": true,
+ "URL": false,
+ "PluginArray": true,
+ "MutationRecord": true,
+ "WebKitCSSMatrix": true,
+ "PeriodicWave": true,
+ "DocumentFragment": true,
+ "DocumentTimeline": false,
+ "ScreenOrientation": true,
+ "BroadcastChannel": true,
+ "PermissionStatus": true,
+ "IntersectionObserver": true,
+ "Blob": true,
+ "MessagePort": true,
+ "BarProp": true,
+ "OscillatorNode": true,
+ "Cache": true,
+ "RadioNodeList": true,
+ "KeyframeEffectReadOnly": true,
+ "InstallTrigger": true,
+ "Function": false,
+ "Object": false,
+ "eval": true,
+ "Window": false,
+ "close": false,
+ "stop": false,
+ "focus": false,
+ "blur": false,
+ "open": true,
+ "alert": false,
+ "confirm": false,
+ "prompt": false,
+ "print": false,
+ "postMessage": true,
+ "getSelection": true,
+ "getComputedStyle": true,
+ "matchMedia": true,
+ "moveTo": false,
+ "moveBy": false,
+ "resizeTo": false,
+ "resizeBy": false,
+ "scroll": false,
+ "scrollTo": false,
+ "scrollBy": false,
+ "requestAnimationFrame": true,
+ "cancelAnimationFrame": true,
+ "getDefaultComputedStyle": false,
+ "scrollByLines": false,
+ "scrollByPages": false,
+ "sizeToContent": false,
+ "updateCommands": true,
+ "find": false,
+ "dump": true,
+ "setResizable": false,
+ "requestIdleCallback": false,
+ "cancelIdleCallback": false,
+ "btoa": true,
+ "atob": true,
+ "setTimeout": true,
+ "clearTimeout": true,
+ "setInterval": true,
+ "clearInterval": true,
+ "createImageBitmap": true,
+ "fetch": true,
+ "self": true,
+ "name": false,
+ "history": true,
+ "locationbar": true,
+ "menubar": true,
+ "personalbar": true,
+ "scrollbars": true,
+ "statusbar": true,
+ "toolbar": true,
+ "status": true,
+ "closed": true,
+ "frames": true,
+ "length": false,
+ "opener": true,
+ "parent": true,
+ "frameElement": true,
+ "navigator": true,
+ "external": true,
+ "applicationCache": true,
+ "screen": true,
+ "innerWidth": true,
+ "innerHeight": true,
+ "scrollX": true,
+ "pageXOffset": true,
+ "scrollY": true,
+ "pageYOffset": true,
+ "screenX": true,
+ "screenY": true,
+ "outerWidth": true,
+ "outerHeight": true,
+ "performance": true,
+ "mozInnerScreenX": true,
+ "mozInnerScreenY": true,
+ "devicePixelRatio": true,
+ "scrollMaxX": true,
+ "scrollMaxY": true,
+ "fullScreen": false,
+ "mozPaintCount": true,
+ "sidebar": false,
+ "crypto": true,
+ "speechSynthesis": true,
+ "localStorage": true,
+ "origin": true,
+ "isSecureContext": false,
+ "indexedDB": true,
+ "caches": true,
+ "sessionStorage": true,
+ "window": false,
+ "document": true,
+ "location": false,
+ "top": true,
+ "netscape": true,
+ "Node": true,
+ "Document": true,
+ "Performance": false,
+ "startProfiling": true,
+ "stopProfiling": true,
+ "pauseProfilers": true,
+ "resumeProfilers": true,
+ "dumpProfile": true,
+ "getMaxGCPauseSinceClear": true,
+ "clearMaxGCPauseAccumulator": true,
+ "Location": true,
+ "StyleSheetList": false,
+ "Selection": false,
+ "Element": true,
+ "AnonymousContent": false,
+ "MutationObserver": true,
+ "NodeList": true,
+ "StopIteration": true
};
@@ -842,155 +842,155 @@ var fname_data = {
*
*/
var DEBUG = true;
-console.log("DEBUG:"+DEBUG);
+console.log("DEBUG:" + DEBUG);
-function dbg_print(a,b){
- if(DEBUG == true){
- //console.log(a,b)
- }
+function dbg_print(a, b) {
+ if (DEBUG == true) {
+ //console.log(a,b)
+ }
}
-function full_evaluate(script){
- var res = true;
- if(script === undefined || script == ""){
- return [true,"Harmless null script"];
- }
+function full_evaluate(script) {
+ var res = true;
+ if (script === undefined || script == "") {
+ return [true, "Harmless null script"];
+ }
- var ast = acornLoose.parse(script).body[0];
+ var ast = acornLoose.parse(script).body[0];
- var flag = false;
- var amtloops = 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.warn("couldn't get first token (probably invalid code)");
- console.warn("Continuing evaluation");
- }
+ 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.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;
- }
- }
- if(script.charAt(end+i) == "("){
- return true;
- }else{
- return false;
- }
- }
- /**
- * 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;
- }
- }
- if(script.charAt(end+i) == "["){
- return true;
- }else{
- return false;
- }
- }
- var error_count = 0;
- while(toke.type != acorn.tokTypes.eof){
- if(toke.type.keyword !== undefined){
- // This type of loop detection ignores functional loop alternatives and ternary operators
- //dbg_print("Keyword:"+toke.type.keyword);
- console.log(toke);
- if(toke.type.keyword == "function"){
- dbg_print("%c NONTRIVIAL: Function declaration.","color:red");
- if(DEBUG == false){
- return [false,"NONTRIVIAL: Function declaration."];
- }
- }
+ /**
+ * 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;
+ }
+ }
+ if (script.charAt(end + i) == "(") {
+ return true;
+ } else {
+ return false;
+ }
+ }
+ /**
+ * 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;
+ }
+ }
+ if (script.charAt(end + i) == "[") {
+ return true;
+ } else {
+ return false;
+ }
+ }
+ var error_count = 0;
+ while (toke.type != acorn.tokTypes.eof) {
+ if (toke.type.keyword !== undefined) {
+ // This type of loop detection ignores functional loop alternatives and ternary operators
+ //dbg_print("Keyword:"+toke.type.keyword);
+ console.log(toke);
+ if (toke.type.keyword == "function") {
+ dbg_print("%c NONTRIVIAL: Function declaration.", "color:red");
+ if (DEBUG == false) {
+ return [false, "NONTRIVIAL: Function declaration."];
+ }
+ }
- 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){
- 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(operators[toke.value] === undefined){
- 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?
- // Are arguments being passed to a user defined variable?
- if(being_called(toke.end)){
- dbg_print("%c NONTRIVIAL: User defined variable '"+toke.value+"' called as function","color:red");
- if(DEBUG == false){
- return [false,"NONTRIVIAL: User defined variable '"+toke.value+"' called as function"];
- }
- }
- // Is there bracket suffix notation?
- if(operators[toke.value] === undefined){
- 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."];
- console.warn("Continuing evaluation");
- error_count++;
- }
- }
+ 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) {
+ 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 (operators[toke.value] === undefined) {
+ 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?
+ // Are arguments being passed to a user defined variable?
+ if (being_called(toke.end)) {
+ dbg_print("%c NONTRIVIAL: User defined variable '" + toke.value + "' called as function", "color:red");
+ if (DEBUG == false) {
+ return [false, "NONTRIVIAL: User defined variable '" + toke.value + "' called as function"];
+ }
+ }
+ // Is there bracket suffix notation?
+ if (operators[toke.value] === undefined) {
+ 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."];
+ console.warn("Continuing evaluation");
+ error_count++;
+ }
+ }
- dbg_print("%cAppears to be trivial.","color:green;");
- return [true,"Script appears to be trivial."];
+ dbg_print("%cAppears to be trivial.", "color:green;");
+ return [true, "Script appears to be trivial."];
}
//****************************************************************************************************
-window.onload = function () {
- document.getElementById("parse").addEventListener("click",function(){
- var script = document.getElementById("input").value;
- var ast = acornLoose.parse(script).body[0];
- document.getElementById("output").innerHTML = JSON.stringify(ast, null, '\t'); // Indented with tab
- document.getElementById("output").innerHTML = full_evaluate(script) + "\n\n" + document.getElementById("output").innerHTML;
- });
+window.onload = function() {
+ document.getElementById("parse").addEventListener("click", function() {
+ var script = document.getElementById("input").value;
+ var ast = acornLoose.parse(script).body[0];
+ document.getElementById("output").innerHTML = JSON.stringify(ast, null, '\t'); // Indented with tab
+ document.getElementById("output").innerHTML = full_evaluate(script) + "\n\n" + document.getElementById("output").innerHTML;
+ });
}
diff --git a/html/display_panel/content/main_panel.js b/html/display_panel/content/main_panel.js
index b96143b..7a5e2c3 100644
--- a/html/display_panel/content/main_panel.js
+++ b/html/display_panel/content/main_panel.js
@@ -29,11 +29,11 @@ if (fromTab) {
document.documentElement.classList.add("tab");
}
-var myPort = browser.runtime.connect({name: "port-from-cs"});
+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) || ""});
+myPort.postMessage({ "update": true, tabId: parseInt(currentReport && currentReport.tabId || fromTab) || "" });
// Display the actual extension version Number
document.querySelector("#version").textContent = browser.runtime.getManifest().version;
@@ -54,7 +54,7 @@ var liTemplate = document.querySelector("#li-template");
liTemplate.remove();
document.querySelector("#info").addEventListener("click", e => {
- let button = e.target;
+ let button = e.target;
if (button.tagName === "A") {
setTimeout(close, 100);
return;
@@ -69,27 +69,27 @@ document.querySelector("#info").addEventListener("click", e => {
}
return;
}
- if (!button.matches(".buttons > button")) 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 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});
+ myPort.postMessage({ [action]: entry, site, tabId: currentReport.tabId });
});
document.querySelector("#report-tab").onclick = e => {
- myPort.postMessage({report_tab: currentReport});
+ myPort.postMessage({ report_tab: currentReport });
close();
}
document.querySelector("#complain").onclick = e => {
- myPort.postMessage({invoke_contact_finder: currentReport});
+ myPort.postMessage({ invoke_contact_finder: currentReport });
close();
}
@@ -100,10 +100,10 @@ document.querySelector("#open-options").onclick = e => {
document.body.addEventListener("click", async e => {
if (!e.target.matches(".reload")) return;
- let {tabId} = currentReport;
+ let { tabId } = currentReport;
if (tabId) {
await browser.tabs.reload(tabId);
- myPort.postMessage({"update": true, tabId});
+ myPort.postMessage({ "update": true, tabId });
}
});
@@ -112,49 +112,49 @@ document.body.addEventListener("click", async e => {
* 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 = `<span class="type-name">${group}</span> scripts in ${url}:`;
- container.classList.remove("empty");
- } else {
- // default message
- list.innerHTML = `<li>No <span class="type-name">${group}</span> scripts on this page.</li>`
- 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);
- }
+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 = `<span class="type-name">${group}</span> scripts in ${url}:`;
+ container.classList.remove("empty");
+ } else {
+ // default message
+ list.innerHTML = `<li>No <span class="type-name">${group}</span> scripts on this page.</li>`
+ 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);
+ }
}
@@ -174,18 +174,18 @@ function createList(data, group){
*/
function refreshUI(report) {
currentReport = report;
- let {siteStatus, listedSite} = 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();
+ toBeErased.remove();
}
let scriptsCount = 0;
for (let group of ["unknown", "accepted", "whitelisted", "blocked", "blacklisted"]) {
- if (group in report) createList(report, group);
+ if (group in report) createList(report, group);
scriptsCount += report[group].length;
}
@@ -195,7 +195,7 @@ function refreshUI(report) {
for (let b of document.querySelectorAll(
`.unknown .forget, .accepted .forget, .blocked .forget,
.whitelisted .whitelist, .blacklisted .blacklist`
- )) {
+ )) {
b.disabled = true;
}
@@ -223,9 +223,9 @@ myPort.onMessage.addListener(m => {
}
});
-function print_local_storage(){
- myPort.postMessage({"printlocalstorage": true});
+function print_local_storage() {
+ myPort.postMessage({ "printlocalstorage": true });
}
-function delete_local_storage(){
- myPort.postMessage({"deletelocalstorage":true});
+function delete_local_storage() {
+ myPort.postMessage({ "deletelocalstorage": true });
}
diff --git a/html/fastclick.js b/html/fastclick.js
index 86bf83e..f487372 100644
--- a/html/fastclick.js
+++ b/html/fastclick.js
@@ -1,841 +1,841 @@
-;(function () {
- 'use strict';
-
- /**
- * @preserve FastClick: polyfill to remove click delays on browsers with touch UIs.
- *
- * @codingstandard ftlabs-jsv2
- * @copyright The Financial Times Limited [All Rights Reserved]
- * @license MIT License (see LICENSE.txt)
- */
-
- /*jslint browser:true, node:true*/
- /*global define, Event, Node*/
-
-
- /**
- * Instantiate fast-clicking listeners on the specified layer.
- *
- * @constructor
- * @param {Element} layer The layer to listen on
- * @param {Object} [options={}] The options to override the defaults
- */
- function FastClick(layer, options) {
- var oldOnClick;
-
- options = options || {};
-
- /**
- * Whether a click is currently being tracked.
- *
- * @type boolean
- */
- this.trackingClick = false;
-
-
- /**
- * Timestamp for when click tracking started.
- *
- * @type number
- */
- this.trackingClickStart = 0;
-
-
- /**
- * The element being tracked for a click.
- *
- * @type EventTarget
- */
- this.targetElement = null;
-
-
- /**
- * X-coordinate of touch start event.
- *
- * @type number
- */
- this.touchStartX = 0;
-
-
- /**
- * Y-coordinate of touch start event.
- *
- * @type number
- */
- this.touchStartY = 0;
-
-
- /**
- * ID of the last touch, retrieved from Touch.identifier.
- *
- * @type number
- */
- this.lastTouchIdentifier = 0;
-
-
- /**
- * Touchmove boundary, beyond which a click will be cancelled.
- *
- * @type number
- */
- this.touchBoundary = options.touchBoundary || 10;
-
-
- /**
- * The FastClick layer.
- *
- * @type Element
- */
- this.layer = layer;
-
- /**
- * The minimum time between tap(touchstart and touchend) events
- *
- * @type number
- */
- this.tapDelay = options.tapDelay || 200;
-
- /**
- * The maximum time for a tap
- *
- * @type number
- */
- this.tapTimeout = options.tapTimeout || 700;
-
- if (FastClick.notNeeded(layer)) {
- return;
- }
-
- // Some old versions of Android don't have Function.prototype.bind
- function bind(method, context) {
- return function() { return method.apply(context, arguments); };
- }
-
-
- var methods = ['onMouse', 'onClick', 'onTouchStart', 'onTouchMove', 'onTouchEnd', 'onTouchCancel'];
- var context = this;
- for (var i = 0, l = methods.length; i < l; i++) {
- context[methods[i]] = bind(context[methods[i]], context);
- }
-
- // Set up event handlers as required
- if (deviceIsAndroid) {
- layer.addEventListener('mouseover', this.onMouse, true);
- layer.addEventListener('mousedown', this.onMouse, true);
- layer.addEventListener('mouseup', this.onMouse, true);
- }
-
- layer.addEventListener('click', this.onClick, true);
- layer.addEventListener('touchstart', this.onTouchStart, false);
- layer.addEventListener('touchmove', this.onTouchMove, false);
- layer.addEventListener('touchend', this.onTouchEnd, false);
- layer.addEventListener('touchcancel', this.onTouchCancel, false);
-
- // Hack is required for browsers that don't support Event#stopImmediatePropagation (e.g. Android 2)
- // which is how FastClick normally stops click events bubbling to callbacks registered on the FastClick
- // layer when they are cancelled.
- if (!Event.prototype.stopImmediatePropagation) {
- layer.removeEventListener = function(type, callback, capture) {
- var rmv = Node.prototype.removeEventListener;
- if (type === 'click') {
- rmv.call(layer, type, callback.hijacked || callback, capture);
- } else {
- rmv.call(layer, type, callback, capture);
- }
- };
-
- layer.addEventListener = function(type, callback, capture) {
- var adv = Node.prototype.addEventListener;
- if (type === 'click') {
- adv.call(layer, type, callback.hijacked || (callback.hijacked = function(event) {
- if (!event.propagationStopped) {
- callback(event);
- }
- }), capture);
- } else {
- adv.call(layer, type, callback, capture);
- }
- };
- }
-
- // If a handler is already declared in the element's onclick attribute, it will be fired before
- // FastClick's onClick handler. Fix this by pulling out the user-defined handler function and
- // adding it as listener.
- if (typeof layer.onclick === 'function') {
-
- // Android browser on at least 3.2 requires a new reference to the function in layer.onclick
- // - the old one won't work if passed to addEventListener directly.
- oldOnClick = layer.onclick;
- layer.addEventListener('click', function(event) {
- oldOnClick(event);
- }, false);
- layer.onclick = null;
- }
- }
-
- /**
- * Windows Phone 8.1 fakes user agent string to look like Android and iPhone.
- *
- * @type boolean
- */
- var deviceIsWindowsPhone = navigator.userAgent.indexOf("Windows Phone") >= 0;
-
- /**
- * Android requires exceptions.
- *
- * @type boolean
- */
- var deviceIsAndroid = navigator.userAgent.indexOf('Android') > 0 && !deviceIsWindowsPhone;
-
-
- /**
- * iOS requires exceptions.
- *
- * @type boolean
- */
- var deviceIsIOS = /iP(ad|hone|od)/.test(navigator.userAgent) && !deviceIsWindowsPhone;
-
-
- /**
- * iOS 4 requires an exception for select elements.
- *
- * @type boolean
- */
- var deviceIsIOS4 = deviceIsIOS && (/OS 4_\d(_\d)?/).test(navigator.userAgent);
-
-
- /**
- * iOS 6.0-7.* requires the target element to be manually derived
- *
- * @type boolean
- */
- var deviceIsIOSWithBadTarget = deviceIsIOS && (/OS [6-7]_\d/).test(navigator.userAgent);
-
- /**
- * BlackBerry requires exceptions.
- *
- * @type boolean
- */
- var deviceIsBlackBerry10 = navigator.userAgent.indexOf('BB10') > 0;
-
- /**
- * Determine whether a given element requires a native click.
- *
- * @param {EventTarget|Element} target Target DOM element
- * @returns {boolean} Returns true if the element needs a native click
- */
- FastClick.prototype.needsClick = function(target) {
- switch (target.nodeName.toLowerCase()) {
-
- // Don't send a synthetic click to disabled inputs (issue #62)
- case 'button':
- case 'select':
- case 'textarea':
- if (target.disabled) {
- return true;
- }
-
- break;
- case 'input':
-
- // File inputs need real clicks on iOS 6 due to a browser bug (issue #68)
- if ((deviceIsIOS && target.type === 'file') || target.disabled) {
- return true;
- }
-
- break;
- case 'label':
- case 'iframe': // iOS8 homescreen apps can prevent events bubbling into frames
- case 'video':
- return true;
- }
-
- return (/\bneedsclick\b/).test(target.className);
- };
-
-
- /**
- * Determine whether a given element requires a call to focus to simulate click into element.
- *
- * @param {EventTarget|Element} target Target DOM element
- * @returns {boolean} Returns true if the element requires a call to focus to simulate native click.
- */
- FastClick.prototype.needsFocus = function(target) {
- switch (target.nodeName.toLowerCase()) {
- case 'textarea':
- return true;
- case 'select':
- return !deviceIsAndroid;
- case 'input':
- switch (target.type) {
- case 'button':
- case 'checkbox':
- case 'file':
- case 'image':
- case 'radio':
- case 'submit':
- return false;
- }
-
- // No point in attempting to focus disabled inputs
- return !target.disabled && !target.readOnly;
- default:
- return (/\bneedsfocus\b/).test(target.className);
- }
- };
-
-
- /**
- * Send a click event to the specified element.
- *
- * @param {EventTarget|Element} targetElement
- * @param {Event} event
- */
- FastClick.prototype.sendClick = function(targetElement, event) {
- var clickEvent, touch;
-
- // On some Android devices activeElement needs to be blurred otherwise the synthetic click will have no effect (#24)
- if (document.activeElement && document.activeElement !== targetElement) {
- document.activeElement.blur();
- }
-
- touch = event.changedTouches[0];
-
- // Synthesise a click event, with an extra attribute so it can be tracked
- clickEvent = document.createEvent('MouseEvents');
- clickEvent.initMouseEvent(this.determineEventType(targetElement), true, true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null);
- clickEvent.forwardedTouchEvent = true;
- targetElement.dispatchEvent(clickEvent);
- };
-
- FastClick.prototype.determineEventType = function(targetElement) {
-
- //Issue #159: Android Chrome Select Box does not open with a synthetic click event
- if (deviceIsAndroid && targetElement.tagName.toLowerCase() === 'select') {
- return 'mousedown';
- }
-
- return 'click';
- };
-
-
- /**
- * @param {EventTarget|Element} targetElement
- */
- FastClick.prototype.focus = function(targetElement) {
- var length;
-
- // Issue #160: on iOS 7, some input elements (e.g. date datetime month) throw a vague TypeError on setSelectionRange. These elements don't have an integer value for the selectionStart and selectionEnd properties, but unfortunately that can't be used for detection because accessing the properties also throws a TypeError. Just check the type instead. Filed as Apple bug #15122724.
- if (deviceIsIOS && targetElement.setSelectionRange && targetElement.type.indexOf('date') !== 0 && targetElement.type !== 'time' && targetElement.type !== 'month' && targetElement.type !== 'email') {
- length = targetElement.value.length;
- targetElement.setSelectionRange(length, length);
- } else {
- targetElement.focus();
- }
- };
-
-
- /**
- * Check whether the given target element is a child of a scrollable layer and if so, set a flag on it.
- *
- * @param {EventTarget|Element} targetElement
- */
- FastClick.prototype.updateScrollParent = function(targetElement) {
- var scrollParent, parentElement;
-
- scrollParent = targetElement.fastClickScrollParent;
-
- // Attempt to discover whether the target element is contained within a scrollable layer. Re-check if the
- // target element was moved to another parent.
- if (!scrollParent || !scrollParent.contains(targetElement)) {
- parentElement = targetElement;
- do {
- if (parentElement.scrollHeight > parentElement.offsetHeight) {
- scrollParent = parentElement;
- targetElement.fastClickScrollParent = parentElement;
- break;
- }
-
- parentElement = parentElement.parentElement;
- } while (parentElement);
- }
-
- // Always update the scroll top tracker if possible.
- if (scrollParent) {
- scrollParent.fastClickLastScrollTop = scrollParent.scrollTop;
- }
- };
-
-
- /**
- * @param {EventTarget} targetElement
- * @returns {Element|EventTarget}
- */
- FastClick.prototype.getTargetElementFromEventTarget = function(eventTarget) {
-
- // On some older browsers (notably Safari on iOS 4.1 - see issue #56) the event target may be a text node.
- if (eventTarget.nodeType === Node.TEXT_NODE) {
- return eventTarget.parentNode;
- }
-
- return eventTarget;
- };
-
-
- /**
- * On touch start, record the position and scroll offset.
- *
- * @param {Event} event
- * @returns {boolean}
- */
- FastClick.prototype.onTouchStart = function(event) {
- var targetElement, touch, selection;
-
- // Ignore multiple touches, otherwise pinch-to-zoom is prevented if both fingers are on the FastClick element (issue #111).
- if (event.targetTouches.length > 1) {
- return true;
- }
-
- targetElement = this.getTargetElementFromEventTarget(event.target);
- touch = event.targetTouches[0];
-
- if (deviceIsIOS) {
-
- // Only trusted events will deselect text on iOS (issue #49)
- selection = window.getSelection();
- if (selection.rangeCount && !selection.isCollapsed) {
- return true;
- }
-
- if (!deviceIsIOS4) {
-
- // Weird things happen on iOS when an alert or confirm dialog is opened from a click event callback (issue #23):
- // when the user next taps anywhere else on the page, new touchstart and touchend events are dispatched
- // with the same identifier as the touch event that previously triggered the click that triggered the alert.
- // Sadly, there is an issue on iOS 4 that causes some normal touch events to have the same identifier as an
- // immediately preceeding touch event (issue #52), so this fix is unavailable on that platform.
- // Issue 120: touch.identifier is 0 when Chrome dev tools 'Emulate touch events' is set with an iOS device UA string,
- // which causes all touch events to be ignored. As this block only applies to iOS, and iOS identifiers are always long,
- // random integers, it's safe to to continue if the identifier is 0 here.
- if (touch.identifier && touch.identifier === this.lastTouchIdentifier) {
- event.preventDefault();
- return false;
- }
-
- this.lastTouchIdentifier = touch.identifier;
-
- // If the target element is a child of a scrollable layer (using -webkit-overflow-scrolling: touch) and:
- // 1) the user does a fling scroll on the scrollable layer
- // 2) the user stops the fling scroll with another tap
- // then the event.target of the last 'touchend' event will be the element that was under the user's finger
- // when the fling scroll was started, causing FastClick to send a click event to that layer - unless a check
- // is made to ensure that a parent layer was not scrolled before sending a synthetic click (issue #42).
- this.updateScrollParent(targetElement);
- }
- }
-
- this.trackingClick = true;
- this.trackingClickStart = event.timeStamp;
- this.targetElement = targetElement;
-
- this.touchStartX = touch.pageX;
- this.touchStartY = touch.pageY;
-
- // Prevent phantom clicks on fast double-tap (issue #36)
- if ((event.timeStamp - this.lastClickTime) < this.tapDelay) {
- event.preventDefault();
- }
-
- return true;
- };
-
-
- /**
- * Based on a touchmove event object, check whether the touch has moved past a boundary since it started.
- *
- * @param {Event} event
- * @returns {boolean}
- */
- FastClick.prototype.touchHasMoved = function(event) {
- var touch = event.changedTouches[0], boundary = this.touchBoundary;
-
- if (Math.abs(touch.pageX - this.touchStartX) > boundary || Math.abs(touch.pageY - this.touchStartY) > boundary) {
- return true;
- }
-
- return false;
- };
-
-
- /**
- * Update the last position.
- *
- * @param {Event} event
- * @returns {boolean}
- */
- FastClick.prototype.onTouchMove = function(event) {
- if (!this.trackingClick) {
- return true;
- }
-
- // If the touch has moved, cancel the click tracking
- if (this.targetElement !== this.getTargetElementFromEventTarget(event.target) || this.touchHasMoved(event)) {
- this.trackingClick = false;
- this.targetElement = null;
- }
-
- return true;
- };
-
-
- /**
- * Attempt to find the labelled control for the given label element.
- *
- * @param {EventTarget|HTMLLabelElement} labelElement
- * @returns {Element|null}
- */
- FastClick.prototype.findControl = function(labelElement) {
-
- // Fast path for newer browsers supporting the HTML5 control attribute
- if (labelElement.control !== undefined) {
- return labelElement.control;
- }
-
- // All browsers under test that support touch events also support the HTML5 htmlFor attribute
- if (labelElement.htmlFor) {
- return document.getElementById(labelElement.htmlFor);
- }
-
- // If no for attribute exists, attempt to retrieve the first labellable descendant element
- // the list of which is defined here: http://www.w3.org/TR/html5/forms.html#category-label
- return labelElement.querySelector('button, input:not([type=hidden]), keygen, meter, output, progress, select, textarea');
- };
-
-
- /**
- * On touch end, determine whether to send a click event at once.
- *
- * @param {Event} event
- * @returns {boolean}
- */
- FastClick.prototype.onTouchEnd = function(event) {
- var forElement, trackingClickStart, targetTagName, scrollParent, touch, targetElement = this.targetElement;
-
- if (!this.trackingClick) {
- return true;
- }
-
- // Prevent phantom clicks on fast double-tap (issue #36)
- if ((event.timeStamp - this.lastClickTime) < this.tapDelay) {
- this.cancelNextClick = true;
- return true;
- }
-
- if ((event.timeStamp - this.trackingClickStart) > this.tapTimeout) {
- return true;
- }
-
- // Reset to prevent wrong click cancel on input (issue #156).
- this.cancelNextClick = false;
-
- this.lastClickTime = event.timeStamp;
-
- trackingClickStart = this.trackingClickStart;
- this.trackingClick = false;
- this.trackingClickStart = 0;
-
- // On some iOS devices, the targetElement supplied with the event is invalid if the layer
- // is performing a transition or scroll, and has to be re-detected manually. Note that
- // for this to function correctly, it must be called *after* the event target is checked!
- // See issue #57; also filed as rdar://13048589 .
- if (deviceIsIOSWithBadTarget) {
- touch = event.changedTouches[0];
-
- // In certain cases arguments of elementFromPoint can be negative, so prevent setting targetElement to null
- targetElement = document.elementFromPoint(touch.pageX - window.pageXOffset, touch.pageY - window.pageYOffset) || targetElement;
- targetElement.fastClickScrollParent = this.targetElement.fastClickScrollParent;
- }
-
- targetTagName = targetElement.tagName.toLowerCase();
- if (targetTagName === 'label') {
- forElement = this.findControl(targetElement);
- if (forElement) {
- this.focus(targetElement);
- if (deviceIsAndroid) {
- return false;
- }
-
- targetElement = forElement;
- }
- } else if (this.needsFocus(targetElement)) {
-
- // Case 1: If the touch started a while ago (best guess is 100ms based on tests for issue #36) then focus will be triggered anyway. Return early and unset the target element reference so that the subsequent click will be allowed through.
- // Case 2: Without this exception for input elements tapped when the document is contained in an iframe, then any inputted text won't be visible even though the value attribute is updated as the user types (issue #37).
- if ((event.timeStamp - trackingClickStart) > 100 || (deviceIsIOS && window.top !== window && targetTagName === 'input')) {
- this.targetElement = null;
- return false;
- }
-
- this.focus(targetElement);
- this.sendClick(targetElement, event);
-
- // Select elements need the event to go through on iOS 4, otherwise the selector menu won't open.
- // Also this breaks opening selects when VoiceOver is active on iOS6, iOS7 (and possibly others)
- if (!deviceIsIOS || targetTagName !== 'select') {
- this.targetElement = null;
- event.preventDefault();
- }
-
- return false;
- }
-
- if (deviceIsIOS && !deviceIsIOS4) {
-
- // Don't send a synthetic click event if the target element is contained within a parent layer that was scrolled
- // and this tap is being used to stop the scrolling (usually initiated by a fling - issue #42).
- scrollParent = targetElement.fastClickScrollParent;
- if (scrollParent && scrollParent.fastClickLastScrollTop !== scrollParent.scrollTop) {
- return true;
- }
- }
-
- // Prevent the actual click from going though - unless the target node is marked as requiring
- // real clicks or if it is in the whitelist in which case only non-programmatic clicks are permitted.
- if (!this.needsClick(targetElement)) {
- event.preventDefault();
- this.sendClick(targetElement, event);
- }
-
- return false;
- };
-
-
- /**
- * On touch cancel, stop tracking the click.
- *
- * @returns {void}
- */
- FastClick.prototype.onTouchCancel = function() {
- this.trackingClick = false;
- this.targetElement = null;
- };
-
-
- /**
- * Determine mouse events which should be permitted.
- *
- * @param {Event} event
- * @returns {boolean}
- */
- FastClick.prototype.onMouse = function(event) {
-
- // If a target element was never set (because a touch event was never fired) allow the event
- if (!this.targetElement) {
- return true;
- }
-
- if (event.forwardedTouchEvent) {
- return true;
- }
-
- // Programmatically generated events targeting a specific element should be permitted
- if (!event.cancelable) {
- return true;
- }
-
- // Derive and check the target element to see whether the mouse event needs to be permitted;
- // unless explicitly enabled, prevent non-touch click events from triggering actions,
- // to prevent ghost/doubleclicks.
- if (!this.needsClick(this.targetElement) || this.cancelNextClick) {
-
- // Prevent any user-added listeners declared on FastClick element from being fired.
- if (event.stopImmediatePropagation) {
- event.stopImmediatePropagation();
- } else {
-
- // Part of the hack for browsers that don't support Event#stopImmediatePropagation (e.g. Android 2)
- event.propagationStopped = true;
- }
-
- // Cancel the event
- event.stopPropagation();
- event.preventDefault();
-
- return false;
- }
-
- // If the mouse event is permitted, return true for the action to go through.
- return true;
- };
-
-
- /**
- * On actual clicks, determine whether this is a touch-generated click, a click action occurring
- * naturally after a delay after a touch (which needs to be cancelled to avoid duplication), or
- * an actual click which should be permitted.
- *
- * @param {Event} event
- * @returns {boolean}
- */
- FastClick.prototype.onClick = function(event) {
- var permitted;
-
- // It's possible for another FastClick-like library delivered with third-party code to fire a click event before FastClick does (issue #44). In that case, set the click-tracking flag back to false and return early. This will cause onTouchEnd to return early.
- if (this.trackingClick) {
- this.targetElement = null;
- this.trackingClick = false;
- return true;
- }
-
- // Very odd behaviour on iOS (issue #18): if a submit element is present inside a form and the user hits enter in the iOS simulator or clicks the Go button on the pop-up OS keyboard the a kind of 'fake' click event will be triggered with the submit-type input element as the target.
- if (event.target.type === 'submit' && event.detail === 0) {
- return true;
- }
-
- permitted = this.onMouse(event);
-
- // Only unset targetElement if the click is not permitted. This will ensure that the check for !targetElement in onMouse fails and the browser's click doesn't go through.
- if (!permitted) {
- this.targetElement = null;
- }
-
- // If clicks are permitted, return true for the action to go through.
- return permitted;
- };
-
-
- /**
- * Remove all FastClick's event listeners.
- *
- * @returns {void}
- */
- FastClick.prototype.destroy = function() {
- var layer = this.layer;
-
- if (deviceIsAndroid) {
- layer.removeEventListener('mouseover', this.onMouse, true);
- layer.removeEventListener('mousedown', this.onMouse, true);
- layer.removeEventListener('mouseup', this.onMouse, true);
- }
-
- layer.removeEventListener('click', this.onClick, true);
- layer.removeEventListener('touchstart', this.onTouchStart, false);
- layer.removeEventListener('touchmove', this.onTouchMove, false);
- layer.removeEventListener('touchend', this.onTouchEnd, false);
- layer.removeEventListener('touchcancel', this.onTouchCancel, false);
- };
-
-
- /**
- * Check whether FastClick is needed.
- *
- * @param {Element} layer The layer to listen on
- */
- FastClick.notNeeded = function(layer) {
- var metaViewport;
- var chromeVersion;
- var blackberryVersion;
- var firefoxVersion;
-
- // Devices that don't support touch don't need FastClick
- if (typeof window.ontouchstart === 'undefined') {
- return true;
- }
-
- // Chrome version - zero for other browsers
- chromeVersion = +(/Chrome\/([0-9]+)/.exec(navigator.userAgent) || [,0])[1];
-
- if (chromeVersion) {
-
- if (deviceIsAndroid) {
- metaViewport = document.querySelector('meta[name=viewport]');
-
- if (metaViewport) {
- // Chrome on Android with user-scalable="no" doesn't need FastClick (issue #89)
- if (metaViewport.content.indexOf('user-scalable=no') !== -1) {
- return true;
- }
- // Chrome 32 and above with width=device-width or less don't need FastClick
- if (chromeVersion > 31 && document.documentElement.scrollWidth <= window.outerWidth) {
- return true;
- }
- }
-
- // Chrome desktop doesn't need FastClick (issue #15)
- } else {
- return true;
- }
- }
-
- if (deviceIsBlackBerry10) {
- blackberryVersion = navigator.userAgent.match(/Version\/([0-9]*)\.([0-9]*)/);
-
- // BlackBerry 10.3+ does not require Fastclick library.
- // https://github.com/ftlabs/fastclick/issues/251
- if (blackberryVersion[1] >= 10 && blackberryVersion[2] >= 3) {
- metaViewport = document.querySelector('meta[name=viewport]');
-
- if (metaViewport) {
- // user-scalable=no eliminates click delay.
- if (metaViewport.content.indexOf('user-scalable=no') !== -1) {
- return true;
- }
- // width=device-width (or less than device-width) eliminates click delay.
- if (document.documentElement.scrollWidth <= window.outerWidth) {
- return true;
- }
- }
- }
- }
-
- // IE10 with -ms-touch-action: none or manipulation, which disables double-tap-to-zoom (issue #97)
- if (layer.style.msTouchAction === 'none' || layer.style.touchAction === 'manipulation') {
- return true;
- }
-
- // Firefox version - zero for other browsers
- firefoxVersion = +(/Firefox\/([0-9]+)/.exec(navigator.userAgent) || [,0])[1];
-
- if (firefoxVersion >= 27) {
- // Firefox 27+ does not have tap delay if the content is not zoomable - https://bugzilla.mozilla.org/show_bug.cgi?id=922896
-
- metaViewport = document.querySelector('meta[name=viewport]');
- if (metaViewport && (metaViewport.content.indexOf('user-scalable=no') !== -1 || document.documentElement.scrollWidth <= window.outerWidth)) {
- return true;
- }
- }
-
- // IE11: prefixed -ms-touch-action is no longer supported and it's recomended to use non-prefixed version
- // http://msdn.microsoft.com/en-us/library/windows/apps/Hh767313.aspx
- if (layer.style.touchAction === 'none' || layer.style.touchAction === 'manipulation') {
- return true;
- }
-
- return false;
- };
-
-
- /**
- * Factory method for creating a FastClick object
- *
- * @param {Element} layer The layer to listen on
- * @param {Object} [options={}] The options to override the defaults
- */
- FastClick.attach = function(layer, options) {
- return new FastClick(layer, options);
- };
-
-
- if (typeof define === 'function' && typeof define.amd === 'object' && define.amd) {
-
- // AMD. Register as an anonymous module.
- define(function() {
- return FastClick;
- });
- } else if (typeof module !== 'undefined' && module.exports) {
- module.exports = FastClick.attach;
- module.exports.FastClick = FastClick;
- } else {
- window.FastClick = FastClick;
- }
+; (function() {
+ 'use strict';
+
+ /**
+ * @preserve FastClick: polyfill to remove click delays on browsers with touch UIs.
+ *
+ * @codingstandard ftlabs-jsv2
+ * @copyright The Financial Times Limited [All Rights Reserved]
+ * @license MIT License (see LICENSE.txt)
+ */
+
+ /*jslint browser:true, node:true*/
+ /*global define, Event, Node*/
+
+
+ /**
+ * Instantiate fast-clicking listeners on the specified layer.
+ *
+ * @constructor
+ * @param {Element} layer The layer to listen on
+ * @param {Object} [options={}] The options to override the defaults
+ */
+ function FastClick(layer, options) {
+ var oldOnClick;
+
+ options = options || {};
+
+ /**
+ * Whether a click is currently being tracked.
+ *
+ * @type boolean
+ */
+ this.trackingClick = false;
+
+
+ /**
+ * Timestamp for when click tracking started.
+ *
+ * @type number
+ */
+ this.trackingClickStart = 0;
+
+
+ /**
+ * The element being tracked for a click.
+ *
+ * @type EventTarget
+ */
+ this.targetElement = null;
+
+
+ /**
+ * X-coordinate of touch start event.
+ *
+ * @type number
+ */
+ this.touchStartX = 0;
+
+
+ /**
+ * Y-coordinate of touch start event.
+ *
+ * @type number
+ */
+ this.touchStartY = 0;
+
+
+ /**
+ * ID of the last touch, retrieved from Touch.identifier.
+ *
+ * @type number
+ */
+ this.lastTouchIdentifier = 0;
+
+
+ /**
+ * Touchmove boundary, beyond which a click will be cancelled.
+ *
+ * @type number
+ */
+ this.touchBoundary = options.touchBoundary || 10;
+
+
+ /**
+ * The FastClick layer.
+ *
+ * @type Element
+ */
+ this.layer = layer;
+
+ /**
+ * The minimum time between tap(touchstart and touchend) events
+ *
+ * @type number
+ */
+ this.tapDelay = options.tapDelay || 200;
+
+ /**
+ * The maximum time for a tap
+ *
+ * @type number
+ */
+ this.tapTimeout = options.tapTimeout || 700;
+
+ if (FastClick.notNeeded(layer)) {
+ return;
+ }
+
+ // Some old versions of Android don't have Function.prototype.bind
+ function bind(method, context) {
+ return function() { return method.apply(context, arguments); };
+ }
+
+
+ var methods = ['onMouse', 'onClick', 'onTouchStart', 'onTouchMove', 'onTouchEnd', 'onTouchCancel'];
+ var context = this;
+ for (var i = 0, l = methods.length; i < l; i++) {
+ context[methods[i]] = bind(context[methods[i]], context);
+ }
+
+ // Set up event handlers as required
+ if (deviceIsAndroid) {
+ layer.addEventListener('mouseover', this.onMouse, true);
+ layer.addEventListener('mousedown', this.onMouse, true);
+ layer.addEventListener('mouseup', this.onMouse, true);
+ }
+
+ layer.addEventListener('click', this.onClick, true);
+ layer.addEventListener('touchstart', this.onTouchStart, false);
+ layer.addEventListener('touchmove', this.onTouchMove, false);
+ layer.addEventListener('touchend', this.onTouchEnd, false);
+ layer.addEventListener('touchcancel', this.onTouchCancel, false);
+
+ // Hack is required for browsers that don't support Event#stopImmediatePropagation (e.g. Android 2)
+ // which is how FastClick normally stops click events bubbling to callbacks registered on the FastClick
+ // layer when they are cancelled.
+ if (!Event.prototype.stopImmediatePropagation) {
+ layer.removeEventListener = function(type, callback, capture) {
+ var rmv = Node.prototype.removeEventListener;
+ if (type === 'click') {
+ rmv.call(layer, type, callback.hijacked || callback, capture);
+ } else {
+ rmv.call(layer, type, callback, capture);
+ }
+ };
+
+ layer.addEventListener = function(type, callback, capture) {
+ var adv = Node.prototype.addEventListener;
+ if (type === 'click') {
+ adv.call(layer, type, callback.hijacked || (callback.hijacked = function(event) {
+ if (!event.propagationStopped) {
+ callback(event);
+ }
+ }), capture);
+ } else {
+ adv.call(layer, type, callback, capture);
+ }
+ };
+ }
+
+ // If a handler is already declared in the element's onclick attribute, it will be fired before
+ // FastClick's onClick handler. Fix this by pulling out the user-defined handler function and
+ // adding it as listener.
+ if (typeof layer.onclick === 'function') {
+
+ // Android browser on at least 3.2 requires a new reference to the function in layer.onclick
+ // - the old one won't work if passed to addEventListener directly.
+ oldOnClick = layer.onclick;
+ layer.addEventListener('click', function(event) {
+ oldOnClick(event);
+ }, false);
+ layer.onclick = null;
+ }
+ }
+
+ /**
+ * Windows Phone 8.1 fakes user agent string to look like Android and iPhone.
+ *
+ * @type boolean
+ */
+ var deviceIsWindowsPhone = navigator.userAgent.indexOf("Windows Phone") >= 0;
+
+ /**
+ * Android requires exceptions.
+ *
+ * @type boolean
+ */
+ var deviceIsAndroid = navigator.userAgent.indexOf('Android') > 0 && !deviceIsWindowsPhone;
+
+
+ /**
+ * iOS requires exceptions.
+ *
+ * @type boolean
+ */
+ var deviceIsIOS = /iP(ad|hone|od)/.test(navigator.userAgent) && !deviceIsWindowsPhone;
+
+
+ /**
+ * iOS 4 requires an exception for select elements.
+ *
+ * @type boolean
+ */
+ var deviceIsIOS4 = deviceIsIOS && (/OS 4_\d(_\d)?/).test(navigator.userAgent);
+
+
+ /**
+ * iOS 6.0-7.* requires the target element to be manually derived
+ *
+ * @type boolean
+ */
+ var deviceIsIOSWithBadTarget = deviceIsIOS && (/OS [6-7]_\d/).test(navigator.userAgent);
+
+ /**
+ * BlackBerry requires exceptions.
+ *
+ * @type boolean
+ */
+ var deviceIsBlackBerry10 = navigator.userAgent.indexOf('BB10') > 0;
+
+ /**
+ * Determine whether a given element requires a native click.
+ *
+ * @param {EventTarget|Element} target Target DOM element
+ * @returns {boolean} Returns true if the element needs a native click
+ */
+ FastClick.prototype.needsClick = function(target) {
+ switch (target.nodeName.toLowerCase()) {
+
+ // Don't send a synthetic click to disabled inputs (issue #62)
+ case 'button':
+ case 'select':
+ case 'textarea':
+ if (target.disabled) {
+ return true;
+ }
+
+ break;
+ case 'input':
+
+ // File inputs need real clicks on iOS 6 due to a browser bug (issue #68)
+ if ((deviceIsIOS && target.type === 'file') || target.disabled) {
+ return true;
+ }
+
+ break;
+ case 'label':
+ case 'iframe': // iOS8 homescreen apps can prevent events bubbling into frames
+ case 'video':
+ return true;
+ }
+
+ return (/\bneedsclick\b/).test(target.className);
+ };
+
+
+ /**
+ * Determine whether a given element requires a call to focus to simulate click into element.
+ *
+ * @param {EventTarget|Element} target Target DOM element
+ * @returns {boolean} Returns true if the element requires a call to focus to simulate native click.
+ */
+ FastClick.prototype.needsFocus = function(target) {
+ switch (target.nodeName.toLowerCase()) {
+ case 'textarea':
+ return true;
+ case 'select':
+ return !deviceIsAndroid;
+ case 'input':
+ switch (target.type) {
+ case 'button':
+ case 'checkbox':
+ case 'file':
+ case 'image':
+ case 'radio':
+ case 'submit':
+ return false;
+ }
+
+ // No point in attempting to focus disabled inputs
+ return !target.disabled && !target.readOnly;
+ default:
+ return (/\bneedsfocus\b/).test(target.className);
+ }
+ };
+
+
+ /**
+ * Send a click event to the specified element.
+ *
+ * @param {EventTarget|Element} targetElement
+ * @param {Event} event
+ */
+ FastClick.prototype.sendClick = function(targetElement, event) {
+ var clickEvent, touch;
+
+ // On some Android devices activeElement needs to be blurred otherwise the synthetic click will have no effect (#24)
+ if (document.activeElement && document.activeElement !== targetElement) {
+ document.activeElement.blur();
+ }
+
+ touch = event.changedTouches[0];
+
+ // Synthesise a click event, with an extra attribute so it can be tracked
+ clickEvent = document.createEvent('MouseEvents');
+ clickEvent.initMouseEvent(this.determineEventType(targetElement), true, true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null);
+ clickEvent.forwardedTouchEvent = true;
+ targetElement.dispatchEvent(clickEvent);
+ };
+
+ FastClick.prototype.determineEventType = function(targetElement) {
+
+ //Issue #159: Android Chrome Select Box does not open with a synthetic click event
+ if (deviceIsAndroid && targetElement.tagName.toLowerCase() === 'select') {
+ return 'mousedown';
+ }
+
+ return 'click';
+ };
+
+
+ /**
+ * @param {EventTarget|Element} targetElement
+ */
+ FastClick.prototype.focus = function(targetElement) {
+ var length;
+
+ // Issue #160: on iOS 7, some input elements (e.g. date datetime month) throw a vague TypeError on setSelectionRange. These elements don't have an integer value for the selectionStart and selectionEnd properties, but unfortunately that can't be used for detection because accessing the properties also throws a TypeError. Just check the type instead. Filed as Apple bug #15122724.
+ if (deviceIsIOS && targetElement.setSelectionRange && targetElement.type.indexOf('date') !== 0 && targetElement.type !== 'time' && targetElement.type !== 'month' && targetElement.type !== 'email') {
+ length = targetElement.value.length;
+ targetElement.setSelectionRange(length, length);
+ } else {
+ targetElement.focus();
+ }
+ };
+
+
+ /**
+ * Check whether the given target element is a child of a scrollable layer and if so, set a flag on it.
+ *
+ * @param {EventTarget|Element} targetElement
+ */
+ FastClick.prototype.updateScrollParent = function(targetElement) {
+ var scrollParent, parentElement;
+
+ scrollParent = targetElement.fastClickScrollParent;
+
+ // Attempt to discover whether the target element is contained within a scrollable layer. Re-check if the
+ // target element was moved to another parent.
+ if (!scrollParent || !scrollParent.contains(targetElement)) {
+ parentElement = targetElement;
+ do {
+ if (parentElement.scrollHeight > parentElement.offsetHeight) {
+ scrollParent = parentElement;
+ targetElement.fastClickScrollParent = parentElement;
+ break;
+ }
+
+ parentElement = parentElement.parentElement;
+ } while (parentElement);
+ }
+
+ // Always update the scroll top tracker if possible.
+ if (scrollParent) {
+ scrollParent.fastClickLastScrollTop = scrollParent.scrollTop;
+ }
+ };
+
+
+ /**
+ * @param {EventTarget} targetElement
+ * @returns {Element|EventTarget}
+ */
+ FastClick.prototype.getTargetElementFromEventTarget = function(eventTarget) {
+
+ // On some older browsers (notably Safari on iOS 4.1 - see issue #56) the event target may be a text node.
+ if (eventTarget.nodeType === Node.TEXT_NODE) {
+ return eventTarget.parentNode;
+ }
+
+ return eventTarget;
+ };
+
+
+ /**
+ * On touch start, record the position and scroll offset.
+ *
+ * @param {Event} event
+ * @returns {boolean}
+ */
+ FastClick.prototype.onTouchStart = function(event) {
+ var targetElement, touch, selection;
+
+ // Ignore multiple touches, otherwise pinch-to-zoom is prevented if both fingers are on the FastClick element (issue #111).
+ if (event.targetTouches.length > 1) {
+ return true;
+ }
+
+ targetElement = this.getTargetElementFromEventTarget(event.target);
+ touch = event.targetTouches[0];
+
+ if (deviceIsIOS) {
+
+ // Only trusted events will deselect text on iOS (issue #49)
+ selection = window.getSelection();
+ if (selection.rangeCount && !selection.isCollapsed) {
+ return true;
+ }
+
+ if (!deviceIsIOS4) {
+
+ // Weird things happen on iOS when an alert or confirm dialog is opened from a click event callback (issue #23):
+ // when the user next taps anywhere else on the page, new touchstart and touchend events are dispatched
+ // with the same identifier as the touch event that previously triggered the click that triggered the alert.
+ // Sadly, there is an issue on iOS 4 that causes some normal touch events to have the same identifier as an
+ // immediately preceeding touch event (issue #52), so this fix is unavailable on that platform.
+ // Issue 120: touch.identifier is 0 when Chrome dev tools 'Emulate touch events' is set with an iOS device UA string,
+ // which causes all touch events to be ignored. As this block only applies to iOS, and iOS identifiers are always long,
+ // random integers, it's safe to to continue if the identifier is 0 here.
+ if (touch.identifier && touch.identifier === this.lastTouchIdentifier) {
+ event.preventDefault();
+ return false;
+ }
+
+ this.lastTouchIdentifier = touch.identifier;
+
+ // If the target element is a child of a scrollable layer (using -webkit-overflow-scrolling: touch) and:
+ // 1) the user does a fling scroll on the scrollable layer
+ // 2) the user stops the fling scroll with another tap
+ // then the event.target of the last 'touchend' event will be the element that was under the user's finger
+ // when the fling scroll was started, causing FastClick to send a click event to that layer - unless a check
+ // is made to ensure that a parent layer was not scrolled before sending a synthetic click (issue #42).
+ this.updateScrollParent(targetElement);
+ }
+ }
+
+ this.trackingClick = true;
+ this.trackingClickStart = event.timeStamp;
+ this.targetElement = targetElement;
+
+ this.touchStartX = touch.pageX;
+ this.touchStartY = touch.pageY;
+
+ // Prevent phantom clicks on fast double-tap (issue #36)
+ if ((event.timeStamp - this.lastClickTime) < this.tapDelay) {
+ event.preventDefault();
+ }
+
+ return true;
+ };
+
+
+ /**
+ * Based on a touchmove event object, check whether the touch has moved past a boundary since it started.
+ *
+ * @param {Event} event
+ * @returns {boolean}
+ */
+ FastClick.prototype.touchHasMoved = function(event) {
+ var touch = event.changedTouches[0], boundary = this.touchBoundary;
+
+ if (Math.abs(touch.pageX - this.touchStartX) > boundary || Math.abs(touch.pageY - this.touchStartY) > boundary) {
+ return true;
+ }
+
+ return false;
+ };
+
+
+ /**
+ * Update the last position.
+ *
+ * @param {Event} event
+ * @returns {boolean}
+ */
+ FastClick.prototype.onTouchMove = function(event) {
+ if (!this.trackingClick) {
+ return true;
+ }
+
+ // If the touch has moved, cancel the click tracking
+ if (this.targetElement !== this.getTargetElementFromEventTarget(event.target) || this.touchHasMoved(event)) {
+ this.trackingClick = false;
+ this.targetElement = null;
+ }
+
+ return true;
+ };
+
+
+ /**
+ * Attempt to find the labelled control for the given label element.
+ *
+ * @param {EventTarget|HTMLLabelElement} labelElement
+ * @returns {Element|null}
+ */
+ FastClick.prototype.findControl = function(labelElement) {
+
+ // Fast path for newer browsers supporting the HTML5 control attribute
+ if (labelElement.control !== undefined) {
+ return labelElement.control;
+ }
+
+ // All browsers under test that support touch events also support the HTML5 htmlFor attribute
+ if (labelElement.htmlFor) {
+ return document.getElementById(labelElement.htmlFor);
+ }
+
+ // If no for attribute exists, attempt to retrieve the first labellable descendant element
+ // the list of which is defined here: http://www.w3.org/TR/html5/forms.html#category-label
+ return labelElement.querySelector('button, input:not([type=hidden]), keygen, meter, output, progress, select, textarea');
+ };
+
+
+ /**
+ * On touch end, determine whether to send a click event at once.
+ *
+ * @param {Event} event
+ * @returns {boolean}
+ */
+ FastClick.prototype.onTouchEnd = function(event) {
+ var forElement, trackingClickStart, targetTagName, scrollParent, touch, targetElement = this.targetElement;
+
+ if (!this.trackingClick) {
+ return true;
+ }
+
+ // Prevent phantom clicks on fast double-tap (issue #36)
+ if ((event.timeStamp - this.lastClickTime) < this.tapDelay) {
+ this.cancelNextClick = true;
+ return true;
+ }
+
+ if ((event.timeStamp - this.trackingClickStart) > this.tapTimeout) {
+ return true;
+ }
+
+ // Reset to prevent wrong click cancel on input (issue #156).
+ this.cancelNextClick = false;
+
+ this.lastClickTime = event.timeStamp;
+
+ trackingClickStart = this.trackingClickStart;
+ this.trackingClick = false;
+ this.trackingClickStart = 0;
+
+ // On some iOS devices, the targetElement supplied with the event is invalid if the layer
+ // is performing a transition or scroll, and has to be re-detected manually. Note that
+ // for this to function correctly, it must be called *after* the event target is checked!
+ // See issue #57; also filed as rdar://13048589 .
+ if (deviceIsIOSWithBadTarget) {
+ touch = event.changedTouches[0];
+
+ // In certain cases arguments of elementFromPoint can be negative, so prevent setting targetElement to null
+ targetElement = document.elementFromPoint(touch.pageX - window.pageXOffset, touch.pageY - window.pageYOffset) || targetElement;
+ targetElement.fastClickScrollParent = this.targetElement.fastClickScrollParent;
+ }
+
+ targetTagName = targetElement.tagName.toLowerCase();
+ if (targetTagName === 'label') {
+ forElement = this.findControl(targetElement);
+ if (forElement) {
+ this.focus(targetElement);
+ if (deviceIsAndroid) {
+ return false;
+ }
+
+ targetElement = forElement;
+ }
+ } else if (this.needsFocus(targetElement)) {
+
+ // Case 1: If the touch started a while ago (best guess is 100ms based on tests for issue #36) then focus will be triggered anyway. Return early and unset the target element reference so that the subsequent click will be allowed through.
+ // Case 2: Without this exception for input elements tapped when the document is contained in an iframe, then any inputted text won't be visible even though the value attribute is updated as the user types (issue #37).
+ if ((event.timeStamp - trackingClickStart) > 100 || (deviceIsIOS && window.top !== window && targetTagName === 'input')) {
+ this.targetElement = null;
+ return false;
+ }
+
+ this.focus(targetElement);
+ this.sendClick(targetElement, event);
+
+ // Select elements need the event to go through on iOS 4, otherwise the selector menu won't open.
+ // Also this breaks opening selects when VoiceOver is active on iOS6, iOS7 (and possibly others)
+ if (!deviceIsIOS || targetTagName !== 'select') {
+ this.targetElement = null;
+ event.preventDefault();
+ }
+
+ return false;
+ }
+
+ if (deviceIsIOS && !deviceIsIOS4) {
+
+ // Don't send a synthetic click event if the target element is contained within a parent layer that was scrolled
+ // and this tap is being used to stop the scrolling (usually initiated by a fling - issue #42).
+ scrollParent = targetElement.fastClickScrollParent;
+ if (scrollParent && scrollParent.fastClickLastScrollTop !== scrollParent.scrollTop) {
+ return true;
+ }
+ }
+
+ // Prevent the actual click from going though - unless the target node is marked as requiring
+ // real clicks or if it is in the whitelist in which case only non-programmatic clicks are permitted.
+ if (!this.needsClick(targetElement)) {
+ event.preventDefault();
+ this.sendClick(targetElement, event);
+ }
+
+ return false;
+ };
+
+
+ /**
+ * On touch cancel, stop tracking the click.
+ *
+ * @returns {void}
+ */
+ FastClick.prototype.onTouchCancel = function() {
+ this.trackingClick = false;
+ this.targetElement = null;
+ };
+
+
+ /**
+ * Determine mouse events which should be permitted.
+ *
+ * @param {Event} event
+ * @returns {boolean}
+ */
+ FastClick.prototype.onMouse = function(event) {
+
+ // If a target element was never set (because a touch event was never fired) allow the event
+ if (!this.targetElement) {
+ return true;
+ }
+
+ if (event.forwardedTouchEvent) {
+ return true;
+ }
+
+ // Programmatically generated events targeting a specific element should be permitted
+ if (!event.cancelable) {
+ return true;
+ }
+
+ // Derive and check the target element to see whether the mouse event needs to be permitted;
+ // unless explicitly enabled, prevent non-touch click events from triggering actions,
+ // to prevent ghost/doubleclicks.
+ if (!this.needsClick(this.targetElement) || this.cancelNextClick) {
+
+ // Prevent any user-added listeners declared on FastClick element from being fired.
+ if (event.stopImmediatePropagation) {
+ event.stopImmediatePropagation();
+ } else {
+
+ // Part of the hack for browsers that don't support Event#stopImmediatePropagation (e.g. Android 2)
+ event.propagationStopped = true;
+ }
+
+ // Cancel the event
+ event.stopPropagation();
+ event.preventDefault();
+
+ return false;
+ }
+
+ // If the mouse event is permitted, return true for the action to go through.
+ return true;
+ };
+
+
+ /**
+ * On actual clicks, determine whether this is a touch-generated click, a click action occurring
+ * naturally after a delay after a touch (which needs to be cancelled to avoid duplication), or
+ * an actual click which should be permitted.
+ *
+ * @param {Event} event
+ * @returns {boolean}
+ */
+ FastClick.prototype.onClick = function(event) {
+ var permitted;
+
+ // It's possible for another FastClick-like library delivered with third-party code to fire a click event before FastClick does (issue #44). In that case, set the click-tracking flag back to false and return early. This will cause onTouchEnd to return early.
+ if (this.trackingClick) {
+ this.targetElement = null;
+ this.trackingClick = false;
+ return true;
+ }
+
+ // Very odd behaviour on iOS (issue #18): if a submit element is present inside a form and the user hits enter in the iOS simulator or clicks the Go button on the pop-up OS keyboard the a kind of 'fake' click event will be triggered with the submit-type input element as the target.
+ if (event.target.type === 'submit' && event.detail === 0) {
+ return true;
+ }
+
+ permitted = this.onMouse(event);
+
+ // Only unset targetElement if the click is not permitted. This will ensure that the check for !targetElement in onMouse fails and the browser's click doesn't go through.
+ if (!permitted) {
+ this.targetElement = null;
+ }
+
+ // If clicks are permitted, return true for the action to go through.
+ return permitted;
+ };
+
+
+ /**
+ * Remove all FastClick's event listeners.
+ *
+ * @returns {void}
+ */
+ FastClick.prototype.destroy = function() {
+ var layer = this.layer;
+
+ if (deviceIsAndroid) {
+ layer.removeEventListener('mouseover', this.onMouse, true);
+ layer.removeEventListener('mousedown', this.onMouse, true);
+ layer.removeEventListener('mouseup', this.onMouse, true);
+ }
+
+ layer.removeEventListener('click', this.onClick, true);
+ layer.removeEventListener('touchstart', this.onTouchStart, false);
+ layer.removeEventListener('touchmove', this.onTouchMove, false);
+ layer.removeEventListener('touchend', this.onTouchEnd, false);
+ layer.removeEventListener('touchcancel', this.onTouchCancel, false);
+ };
+
+
+ /**
+ * Check whether FastClick is needed.
+ *
+ * @param {Element} layer The layer to listen on
+ */
+ FastClick.notNeeded = function(layer) {
+ var metaViewport;
+ var chromeVersion;
+ var blackberryVersion;
+ var firefoxVersion;
+
+ // Devices that don't support touch don't need FastClick
+ if (typeof window.ontouchstart === 'undefined') {
+ return true;
+ }
+
+ // Chrome version - zero for other browsers
+ chromeVersion = +(/Chrome\/([0-9]+)/.exec(navigator.userAgent) || [, 0])[1];
+
+ if (chromeVersion) {
+
+ if (deviceIsAndroid) {
+ metaViewport = document.querySelector('meta[name=viewport]');
+
+ if (metaViewport) {
+ // Chrome on Android with user-scalable="no" doesn't need FastClick (issue #89)
+ if (metaViewport.content.indexOf('user-scalable=no') !== -1) {
+ return true;
+ }
+ // Chrome 32 and above with width=device-width or less don't need FastClick
+ if (chromeVersion > 31 && document.documentElement.scrollWidth <= window.outerWidth) {
+ return true;
+ }
+ }
+
+ // Chrome desktop doesn't need FastClick (issue #15)
+ } else {
+ return true;
+ }
+ }
+
+ if (deviceIsBlackBerry10) {
+ blackberryVersion = navigator.userAgent.match(/Version\/([0-9]*)\.([0-9]*)/);
+
+ // BlackBerry 10.3+ does not require Fastclick library.
+ // https://github.com/ftlabs/fastclick/issues/251
+ if (blackberryVersion[1] >= 10 && blackberryVersion[2] >= 3) {
+ metaViewport = document.querySelector('meta[name=viewport]');
+
+ if (metaViewport) {
+ // user-scalable=no eliminates click delay.
+ if (metaViewport.content.indexOf('user-scalable=no') !== -1) {
+ return true;
+ }
+ // width=device-width (or less than device-width) eliminates click delay.
+ if (document.documentElement.scrollWidth <= window.outerWidth) {
+ return true;
+ }
+ }
+ }
+ }
+
+ // IE10 with -ms-touch-action: none or manipulation, which disables double-tap-to-zoom (issue #97)
+ if (layer.style.msTouchAction === 'none' || layer.style.touchAction === 'manipulation') {
+ return true;
+ }
+
+ // Firefox version - zero for other browsers
+ firefoxVersion = +(/Firefox\/([0-9]+)/.exec(navigator.userAgent) || [, 0])[1];
+
+ if (firefoxVersion >= 27) {
+ // Firefox 27+ does not have tap delay if the content is not zoomable - https://bugzilla.mozilla.org/show_bug.cgi?id=922896
+
+ metaViewport = document.querySelector('meta[name=viewport]');
+ if (metaViewport && (metaViewport.content.indexOf('user-scalable=no') !== -1 || document.documentElement.scrollWidth <= window.outerWidth)) {
+ return true;
+ }
+ }
+
+ // IE11: prefixed -ms-touch-action is no longer supported and it's recomended to use non-prefixed version
+ // http://msdn.microsoft.com/en-us/library/windows/apps/Hh767313.aspx
+ if (layer.style.touchAction === 'none' || layer.style.touchAction === 'manipulation') {
+ return true;
+ }
+
+ return false;
+ };
+
+
+ /**
+ * Factory method for creating a FastClick object
+ *
+ * @param {Element} layer The layer to listen on
+ * @param {Object} [options={}] The options to override the defaults
+ */
+ FastClick.attach = function(layer, options) {
+ return new FastClick(layer, options);
+ };
+
+
+ if (typeof define === 'function' && typeof define.amd === 'object' && define.amd) {
+
+ // AMD. Register as an anonymous module.
+ define(function() {
+ return FastClick;
+ });
+ } else if (typeof module !== 'undefined' && module.exports) {
+ module.exports = FastClick.attach;
+ module.exports.FastClick = FastClick;
+ } else {
+ window.FastClick = FastClick;
+ }
}());
diff --git a/html/preferences_panel/pref.js b/html/preferences_panel/pref.js
index f8d8fdf..c3597e1 100644
--- a/html/preferences_panel/pref.js
+++ b/html/preferences_panel/pref.js
@@ -21,290 +21,290 @@
*/
(() => {
- "use strict";
-
- const LIST_NAMES = ["white", "black"];
-
- var Model = {
- lists: {},
- prefs: null,
-
- malformedUrl(url) {
- let error = null;
- try {
- let objUrl = new URL(url);
- url = objUrl.href;
- if (!objUrl.protocol.startsWith("http")) {
- error = "Please enter http:// or https:// URLs only";
- } else if (!/^[^*]+\*?$/.test(url)) {
- error = "Only one single trailing path wildcard (/*) allowed";
- }
- } catch (e) {
- if (/^https?:\/\/\*\./.test(url)) {
- return this.malformedUrl(url.replace("*.", ""));
- }
- error = "Invalid URL";
- if (url && !url.includes("://")) error += ": missing protocol, either http:// or https://";
- else if (url.endsWith("://")) error += ": missing domain name";
- }
- return error;
- },
-
- async save(prefs = this.prefs) {
- if (prefs !== this.prefs) {
- this.prefs = Object.assign(this.prefs, prefs);
- }
- this.saving = true;
- try {
- return await browser.storage.local.set(prefs);
- } finally {
- this.saving = false;
- }
- },
-
- async addToList(list, ...items) {
- let other = list === Model.lists.black ? Model.lists.white : Model.lists.black;
- this.saving = true;
- try {
- await Promise.all([
- other.remove(...items),
- list.store(...items)
- ]);
- } finally {
- this.saving = false;
- }
- }
- };
- Model.loading = (async () => {
- let prefsNames = [
- "whitelist",
- "blacklist",
- "subject",
- "body"
- ];
- Model.prefs = await browser.storage.local.get(prefsNames.map(name => `pref_${name}`));
-
- for (let listName of LIST_NAMES) {
- let prefName = `pref_${listName}list`;
- await (Model.lists[listName] = new ListStore(prefName, Storage.CSV))
- .load(Model.prefs[prefName]);
- }
- })();
-
- var Controller = {
- init() {
- let widgetsRoot = this.root = document.getElementById("widgets");
- for (let widget of widgetsRoot.querySelectorAll('[id^="pref_"]')) {
- if (widget.id in Model.lists) {
- populateListUI(widget);
- } else if (widget.id in Model.prefs) {
- widget.value = Model.prefs[widget.id];
- }
- }
-
- this.populateListUI();
- this.syncAll();
-
- for (let ev in Listeners) {
- widgetsRoot.addEventListener(ev, Listeners[ev]);
- }
- document.getElementById("site").onfocus = e => {
- if (!e.target.value.trim()) {
- e.target.value = "https://";
- }
- };
-
- browser.storage.onChanged.addListener(changes => {
- if (!Model.saving &&
- ("pref_whitelist" in changes || "pref_blacklist" in changes)) {
- setTimeout(() => {
- this.populateListUI();
- this.syncAll();
- }, 10);
- }
- });
- },
-
- async addSite(list) {
- let url = document.getElementById("site").value.trim();
-
- if (url && !Model.malformedUrl(url)) {
- await this.addToList(list, url);
- }
- },
- async addToList(list, ...items) {
- await Model.addToList(list, ...items);
- this.populateListUI();
- this.syncAll();
- },
- async swapSelection(list) {
- let origin = list === Model.lists.black ? "white" : "black";
- await this.addToList(list, ...Array.prototype.map.call(
- document.querySelectorAll(`select#${origin} option:checked`),
- option => option.value)
- );
- },
-
- syncAll() {
- this.syncListsUI();
- this.syncSiteUI();
- },
-
- syncSiteUI() {
- let widget = document.getElementById("site");
- let list2button = listName => document.getElementById(`cmd-${listName}list-site`);
-
- for (let bi of LIST_NAMES.map(list2button)) {
- bi.disabled = true;
- }
-
- let url = widget.value.trim();
- let malformedUrl = url && Model.malformedUrl(url);
- widget.classList.toggle("error", !!malformedUrl);
- document.getElementById("site-error").textContent = malformedUrl || "";
- if (!url) return;
- if (url !== widget.value) {
- widget.value = url;
- }
-
- for (let listName of LIST_NAMES) {
- let list = Model.lists[listName];
- if (!list.contains(url)) {
- list2button(listName).disabled = false;
- }
- }
- },
-
- syncListsUI() {
- let total = 0;
- for (let id of ["black", "white"]) {
- let selected = document.querySelectorAll(`select#${id} option:checked`).length;
- let other = id === "black" ? "white" : "black";
- document.getElementById(`cmd-${other}list`).disabled = selected === 0;
- total += selected;
- }
- document.getElementById("cmd-delete").disabled = total === 0;
- },
-
- async deleteSelection() {
- for (let id of ["black", "white"]) {
- let selection = document.querySelectorAll(`select#${id} option:checked`);
- await Model.lists[id].remove(...Array.prototype.map.call(selection, option => option.value));
- }
- this.populateListUI();
- this.syncAll();
- },
-
- populateListUI(widget) {
- if (!widget) {
- for(let id of ["white", "black"]) {
- this.populateListUI(document.getElementById(id));
- }
- return;
- }
- widget.innerHTML = "";
- let items = [...Model.lists[widget.id].items].sort();
- let options = new DocumentFragment();
- for (let item of items) {
- let option = document.createElement("option");
- option.value = option.textContent = option.title = item;
- options.appendChild(option);
- }
- widget.appendChild(options);
- }
- };
-
- var KeyEvents = {
- Delete(e) {
- if (e.target.matches("#lists select")) {
- Controller.deleteSelection();
- }
- },
- Enter(e) {
- if (e.target.id === "site") {
- e.target.parentElement.querySelector("button[default]").click();
- }
- },
- KeyA(e) {
- if (e.target.matches("select") && e.ctrlKey) {
- for (let o of e.target.options) {
- o.selected = true;
- }
- Controller.syncListsUI();
- }
- }
- }
-
- var Listeners = {
- async change(e) {
- let {target} = e;
- let {id} = target;
-
- if (id in Model.lists) {
- Controller.syncListsUI();
- let selection = target.querySelectorAll("option:checked");
- if (selection.length === 1) {
- document.getElementById("site").value = selection[0].value;
- }
- return;
- }
- },
-
- click(e) {
- let {target} = e;
-
- if (!/^cmd-(white|black|delete)(list-site)?/.test(target.id)) return;
- e.preventDefault();
- let cmd = RegExp.$1;
- if (cmd === "delete") {
- Controller.deleteSelection();
- return;
- }
- let list = Model.lists[cmd];
- if (list) {
- Controller[RegExp.$2 ? "addSite" : "swapSelection"](list);
- return;
- }
- },
-
- keypress(e) {
- let {code} = e;
- if (code && typeof KeyEvents[code] === "function") {
- if (KeyEvents[code](e) === false) {
- e.preventDefault();
- }
- return;
- }
- },
-
- async input(e) {
- let {target} = e;
- let {id} = target;
- if (!id) return;
-
- if (id === "site") {
- Controller.syncSiteUI();
- let url = target.value;
- if (url) {
- let o = document.querySelector(`#lists select option[value="${url}"]`);
- if (o) {
- o.scrollIntoView();
- o.selected = true;
- }
- }
- return;
- }
-
- if (id.startsWith("pref_")) {
- await Model.save({[id]: target.value});
- return;
- }
- }
- };
-
- window.addEventListener("DOMContentLoaded", async e => {
- await Model.loading;
- Controller.init();
- });
+ "use strict";
+
+ const LIST_NAMES = ["white", "black"];
+
+ var Model = {
+ lists: {},
+ prefs: null,
+
+ malformedUrl(url) {
+ let error = null;
+ try {
+ let objUrl = new URL(url);
+ url = objUrl.href;
+ if (!objUrl.protocol.startsWith("http")) {
+ error = "Please enter http:// or https:// URLs only";
+ } else if (!/^[^*]+\*?$/.test(url)) {
+ error = "Only one single trailing path wildcard (/*) allowed";
+ }
+ } catch (e) {
+ if (/^https?:\/\/\*\./.test(url)) {
+ return this.malformedUrl(url.replace("*.", ""));
+ }
+ error = "Invalid URL";
+ if (url && !url.includes("://")) error += ": missing protocol, either http:// or https://";
+ else if (url.endsWith("://")) error += ": missing domain name";
+ }
+ return error;
+ },
+
+ async save(prefs = this.prefs) {
+ if (prefs !== this.prefs) {
+ this.prefs = Object.assign(this.prefs, prefs);
+ }
+ this.saving = true;
+ try {
+ return await browser.storage.local.set(prefs);
+ } finally {
+ this.saving = false;
+ }
+ },
+
+ async addToList(list, ...items) {
+ let other = list === Model.lists.black ? Model.lists.white : Model.lists.black;
+ this.saving = true;
+ try {
+ await Promise.all([
+ other.remove(...items),
+ list.store(...items)
+ ]);
+ } finally {
+ this.saving = false;
+ }
+ }
+ };
+ Model.loading = (async () => {
+ let prefsNames = [
+ "whitelist",
+ "blacklist",
+ "subject",
+ "body"
+ ];
+ Model.prefs = await browser.storage.local.get(prefsNames.map(name => `pref_${name}`));
+
+ for (let listName of LIST_NAMES) {
+ let prefName = `pref_${listName}list`;
+ await (Model.lists[listName] = new ListStore(prefName, Storage.CSV))
+ .load(Model.prefs[prefName]);
+ }
+ })();
+
+ var Controller = {
+ init() {
+ let widgetsRoot = this.root = document.getElementById("widgets");
+ for (let widget of widgetsRoot.querySelectorAll('[id^="pref_"]')) {
+ if (widget.id in Model.lists) {
+ populateListUI(widget);
+ } else if (widget.id in Model.prefs) {
+ widget.value = Model.prefs[widget.id];
+ }
+ }
+
+ this.populateListUI();
+ this.syncAll();
+
+ for (let ev in Listeners) {
+ widgetsRoot.addEventListener(ev, Listeners[ev]);
+ }
+ document.getElementById("site").onfocus = e => {
+ if (!e.target.value.trim()) {
+ e.target.value = "https://";
+ }
+ };
+
+ browser.storage.onChanged.addListener(changes => {
+ if (!Model.saving &&
+ ("pref_whitelist" in changes || "pref_blacklist" in changes)) {
+ setTimeout(() => {
+ this.populateListUI();
+ this.syncAll();
+ }, 10);
+ }
+ });
+ },
+
+ async addSite(list) {
+ let url = document.getElementById("site").value.trim();
+
+ if (url && !Model.malformedUrl(url)) {
+ await this.addToList(list, url);
+ }
+ },
+ async addToList(list, ...items) {
+ await Model.addToList(list, ...items);
+ this.populateListUI();
+ this.syncAll();
+ },
+ async swapSelection(list) {
+ let origin = list === Model.lists.black ? "white" : "black";
+ await this.addToList(list, ...Array.prototype.map.call(
+ document.querySelectorAll(`select#${origin} option:checked`),
+ option => option.value)
+ );
+ },
+
+ syncAll() {
+ this.syncListsUI();
+ this.syncSiteUI();
+ },
+
+ syncSiteUI() {
+ let widget = document.getElementById("site");
+ let list2button = listName => document.getElementById(`cmd-${listName}list-site`);
+
+ for (let bi of LIST_NAMES.map(list2button)) {
+ bi.disabled = true;
+ }
+
+ let url = widget.value.trim();
+ let malformedUrl = url && Model.malformedUrl(url);
+ widget.classList.toggle("error", !!malformedUrl);
+ document.getElementById("site-error").textContent = malformedUrl || "";
+ if (!url) return;
+ if (url !== widget.value) {
+ widget.value = url;
+ }
+
+ for (let listName of LIST_NAMES) {
+ let list = Model.lists[listName];
+ if (!list.contains(url)) {
+ list2button(listName).disabled = false;
+ }
+ }
+ },
+
+ syncListsUI() {
+ let total = 0;
+ for (let id of ["black", "white"]) {
+ let selected = document.querySelectorAll(`select#${id} option:checked`).length;
+ let other = id === "black" ? "white" : "black";
+ document.getElementById(`cmd-${other}list`).disabled = selected === 0;
+ total += selected;
+ }
+ document.getElementById("cmd-delete").disabled = total === 0;
+ },
+
+ async deleteSelection() {
+ for (let id of ["black", "white"]) {
+ let selection = document.querySelectorAll(`select#${id} option:checked`);
+ await Model.lists[id].remove(...Array.prototype.map.call(selection, option => option.value));
+ }
+ this.populateListUI();
+ this.syncAll();
+ },
+
+ populateListUI(widget) {
+ if (!widget) {
+ for (let id of ["white", "black"]) {
+ this.populateListUI(document.getElementById(id));
+ }
+ return;
+ }
+ widget.innerHTML = "";
+ let items = [...Model.lists[widget.id].items].sort();
+ let options = new DocumentFragment();
+ for (let item of items) {
+ let option = document.createElement("option");
+ option.value = option.textContent = option.title = item;
+ options.appendChild(option);
+ }
+ widget.appendChild(options);
+ }
+ };
+
+ var KeyEvents = {
+ Delete(e) {
+ if (e.target.matches("#lists select")) {
+ Controller.deleteSelection();
+ }
+ },
+ Enter(e) {
+ if (e.target.id === "site") {
+ e.target.parentElement.querySelector("button[default]").click();
+ }
+ },
+ KeyA(e) {
+ if (e.target.matches("select") && e.ctrlKey) {
+ for (let o of e.target.options) {
+ o.selected = true;
+ }
+ Controller.syncListsUI();
+ }
+ }
+ }
+
+ var Listeners = {
+ async change(e) {
+ let { target } = e;
+ let { id } = target;
+
+ if (id in Model.lists) {
+ Controller.syncListsUI();
+ let selection = target.querySelectorAll("option:checked");
+ if (selection.length === 1) {
+ document.getElementById("site").value = selection[0].value;
+ }
+ return;
+ }
+ },
+
+ click(e) {
+ let { target } = e;
+
+ if (!/^cmd-(white|black|delete)(list-site)?/.test(target.id)) return;
+ e.preventDefault();
+ let cmd = RegExp.$1;
+ if (cmd === "delete") {
+ Controller.deleteSelection();
+ return;
+ }
+ let list = Model.lists[cmd];
+ if (list) {
+ Controller[RegExp.$2 ? "addSite" : "swapSelection"](list);
+ return;
+ }
+ },
+
+ keypress(e) {
+ let { code } = e;
+ if (code && typeof KeyEvents[code] === "function") {
+ if (KeyEvents[code](e) === false) {
+ e.preventDefault();
+ }
+ return;
+ }
+ },
+
+ async input(e) {
+ let { target } = e;
+ let { id } = target;
+ if (!id) return;
+
+ if (id === "site") {
+ Controller.syncSiteUI();
+ let url = target.value;
+ if (url) {
+ let o = document.querySelector(`#lists select option[value="${url}"]`);
+ if (o) {
+ o.scrollIntoView();
+ o.selected = true;
+ }
+ }
+ return;
+ }
+
+ if (id.startsWith("pref_")) {
+ await Model.save({ [id]: target.value });
+ return;
+ }
+ }
+ };
+
+ window.addEventListener("DOMContentLoaded", async e => {
+ await Model.loading;
+ Controller.init();
+ });
})();
diff --git a/legacy_license_check.js b/legacy_license_check.js
index 5068085..59097ab 100644
--- a/legacy_license_check.js
+++ b/legacy_license_check.js
@@ -33,26 +33,26 @@ var licStartLicEndRe = /@licstartThefollowingistheentirelicensenoticefortheJavaS
* hardcoded in license_definitions.js
*
*/
-var stripLicenseToRegexp = function (license) {
- var max = license.licenseFragments.length;
- var item;
- for (var i = 0; i < max; i++) {
- item = license.licenseFragments[i];
- item.regex = match_utils.removeNonalpha(item.text);
- item.regex = new RegExp(
- match_utils.replaceTokens(item.regex), '');
- }
- return license;
+var stripLicenseToRegexp = function(license) {
+ var max = license.licenseFragments.length;
+ var item;
+ for (var i = 0; i < max; i++) {
+ item = license.licenseFragments[i];
+ item.regex = match_utils.removeNonalpha(item.text);
+ item.regex = new RegExp(
+ match_utils.replaceTokens(item.regex), '');
+ }
+ return license;
};
-var license_regexes = [];
+var license_regexes = [];
-var init = function(){
- console.log("initializing regexes");
- for (var item in data.licenses) {
- license_regexes.push(stripLicenseToRegexp(data.licenses[item]));
- }
- //console.log(license_regexes);
+var init = function() {
+ console.log("initializing regexes");
+ for (var item in data.licenses) {
+ license_regexes.push(stripLicenseToRegexp(data.licenses[item]));
+ }
+ //console.log(license_regexes);
}
module.exports.init = init;
@@ -62,25 +62,25 @@ module.exports.init = init;
* Takes in the declaration that has been preprocessed and
* tests it against regexes in our table.
*/
-var search_table = function(stripped_comment){
- var stripped = match_utils.removeNonalpha(stripped_comment);
- //stripped = stripped.replaceTokens(stripped_comment);
-
- //console.log("Looking up license");
- //console.log(stripped);
-
- for (license in data.licenses) {
- frag = data.licenses[license].licenseFragments;
- max_i = data.licenses[license].licenseFragments.length;
- for (i = 0; i < max_i; i++) {
- if (frag[i].regex.test(stripped)) {
- //console.log(data.licenses[license].licenseName);
- return data.licenses[license].licenseName;
- }
- }
- }
- console.log("No global license found.");
- return false;
+var search_table = function(stripped_comment) {
+ var stripped = match_utils.removeNonalpha(stripped_comment);
+ //stripped = stripped.replaceTokens(stripped_comment);
+
+ //console.log("Looking up license");
+ //console.log(stripped);
+
+ for (license in data.licenses) {
+ frag = data.licenses[license].licenseFragments;
+ max_i = data.licenses[license].licenseFragments.length;
+ for (i = 0; i < max_i; i++) {
+ if (frag[i].regex.test(stripped)) {
+ //console.log(data.licenses[license].licenseName);
+ return data.licenses[license].licenseName;
+ }
+ }
+ }
+ console.log("No global license found.");
+ return false;
}
@@ -88,25 +88,25 @@ var search_table = function(stripped_comment){
* Takes the "first comment available on the page"
* returns true for "free" and false for anything else
*/
-var check = function(license_text){
- //console.log("checking...");
- //console.log(license_text);
-
- if(license_text === undefined || license_text === null || license_text == ""){
- //console.log("Was not an inline script");
- return false;
- }
- // remove whitespace
- var stripped = match_utils.removeWhitespace(license_text);
- // Search for @licstart/@licend
- // This assumes that there isn't anything before the comment
- var matches = stripped.match(licStartLicEndRe);
- if(matches == null){
- return false;
- }
- var declaration = matches[0];
-
- return search_table(declaration);
+var check = function(license_text) {
+ //console.log("checking...");
+ //console.log(license_text);
+
+ if (license_text === undefined || license_text === null || license_text == "") {
+ //console.log("Was not an inline script");
+ return false;
+ }
+ // remove whitespace
+ var stripped = match_utils.removeWhitespace(license_text);
+ // Search for @licstart/@licend
+ // This assumes that there isn't anything before the comment
+ var matches = stripped.match(licStartLicEndRe);
+ if (matches == null) {
+ return false;
+ }
+ var declaration = matches[0];
+
+ return search_table(declaration);
};
diff --git a/license_definitions.js b/license_definitions.js
index 1ea9fba..63721ea 100644
--- a/license_definitions.js
+++ b/license_definitions.js
@@ -20,9 +20,9 @@
* along with GNU LibreJS. If not, see <http://www.gnu.org/licenses/>.
*/
exports.types = {
- SHORT: 'short',
- LAZY: 'lazy',
- FULL: 'full'
+ SHORT: 'short',
+ LAZY: 'lazy',
+ FULL: 'full'
};
var type = exports.types;
@@ -36,275 +36,275 @@ var type = exports.types;
* https://spdx.org/licenses/
*/
exports.licenses = {
- 'CC0-1.0': {
- licenseName: 'Creative Commons CC0 1.0 Universal',
- identifier: 'CC0-1.0',
- canonicalUrl: [
- 'http://creativecommons.org/publicdomain/zero/1.0/legalcode',
- 'magnet:?xt=urn:btih:90dc5c0be029de84e523b9b3922520e79e0e6f08&dn=cc0.txt'
- ],
- licenseFragments: []
- },
+ 'CC0-1.0': {
+ licenseName: 'Creative Commons CC0 1.0 Universal',
+ identifier: 'CC0-1.0',
+ canonicalUrl: [
+ 'http://creativecommons.org/publicdomain/zero/1.0/legalcode',
+ 'magnet:?xt=urn:btih:90dc5c0be029de84e523b9b3922520e79e0e6f08&dn=cc0.txt'
+ ],
+ licenseFragments: []
+ },
- 'GPL-2.0': {
- licenseName: 'GNU General Public License (GPL) version 2',
- identifier: 'GPL-2.0',
- canonicalUrl: [
- 'http://www.gnu.org/licenses/gpl-2.0.html',
- 'magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt'
- ],
- licenseFragments: [{text: "<THISPROGRAM> 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 2 of the License, or (at your option) any later version.", type: type.SHORT},
- {text:"Alternatively, the contents of this file may be used under the terms of either the GNU General Public License Version 2 or later (the \"GPL\"), or the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"), in which case the provisions of the GPL or the LGPL are applicable instead of those above. If you wish to allow use of your version of this file only under the terms of either the GPL or the LGPL, and not to allow others to use your version of this file under the terms of the MPL, indicate your decision by deleting the provisions above and replace them with the notice and other provisions required by the GPL or the LGPL. If you do not delete the provisions above, a recipient may use your version of this file under the terms of any one of the MPL, the GPL or the LGPL.", type: type.SHORT}]
- },
+ 'GPL-2.0': {
+ licenseName: 'GNU General Public License (GPL) version 2',
+ identifier: 'GPL-2.0',
+ canonicalUrl: [
+ 'http://www.gnu.org/licenses/gpl-2.0.html',
+ 'magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt'
+ ],
+ licenseFragments: [{ text: "<THISPROGRAM> 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 2 of the License, or (at your option) any later version.", type: type.SHORT },
+ { text: "Alternatively, the contents of this file may be used under the terms of either the GNU General Public License Version 2 or later (the \"GPL\"), or the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"), in which case the provisions of the GPL or the LGPL are applicable instead of those above. If you wish to allow use of your version of this file only under the terms of either the GPL or the LGPL, and not to allow others to use your version of this file under the terms of the MPL, indicate your decision by deleting the provisions above and replace them with the notice and other provisions required by the GPL or the LGPL. If you do not delete the provisions above, a recipient may use your version of this file under the terms of any one of the MPL, the GPL or the LGPL.", type: type.SHORT }]
+ },
- 'GPL-3.0': {
- licenseName: 'GNU General Public License (GPL) version 3',
- identifier: 'GPL-3.0',
- canonicalUrl: [
- 'http://www.gnu.org/licenses/gpl-3.0.html',
- 'magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt'
- ],
- licenseFragments: [
- {text: "<THISPROGRAM> is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License (GNU GPL) as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The code is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU GPL for more details. As additional permission under GNU GPL version 3 section 7, you may distribute non-source (e.g., minimized or compacted) forms of that code without the copy of the GNU GPL normally required by section 4, provided you include this license notice and a URL through which recipients can access the Corresponding Source.", type: type.SHORT},
- {text: "<THISPROGRAM> 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.", type: type.SHORT}]
- },
+ 'GPL-3.0': {
+ licenseName: 'GNU General Public License (GPL) version 3',
+ identifier: 'GPL-3.0',
+ canonicalUrl: [
+ 'http://www.gnu.org/licenses/gpl-3.0.html',
+ 'magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt'
+ ],
+ licenseFragments: [
+ { text: "<THISPROGRAM> is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License (GNU GPL) as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The code is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU GPL for more details. As additional permission under GNU GPL version 3 section 7, you may distribute non-source (e.g., minimized or compacted) forms of that code without the copy of the GNU GPL normally required by section 4, provided you include this license notice and a URL through which recipients can access the Corresponding Source.", type: type.SHORT },
+ { text: "<THISPROGRAM> 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.", type: type.SHORT }]
+ },
- 'GNU-All-Permissive': {
- licenseName: 'GNU All-Permissive License',
- licenseFragments: [{text: "Copying and distribution of this file, with or without modification, are permitted in any medium without royalty provided the copyright notice and this notice are preserved. This file is offered as-is, without any warranty.", type: type.SHORT}]
- },
+ 'GNU-All-Permissive': {
+ licenseName: 'GNU All-Permissive License',
+ licenseFragments: [{ text: "Copying and distribution of this file, with or without modification, are permitted in any medium without royalty provided the copyright notice and this notice are preserved. This file is offered as-is, without any warranty.", type: type.SHORT }]
+ },
- 'Apache-2.0': {
- licenseName: 'Apache License, Version 2.0',
- identifier: 'Apache-2.0',
- canonicalUrl: [
- 'http://www.apache.org/licenses/LICENSE-2.0',
- 'magnet:?xt=urn:btih:8e4f440f4c65981c5bf93c76d35135ba5064d8b7&dn=apache-2.0.txt'
- ],
- licenseFragments: [{text: "Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0", type: type.SHORT}]
- },
+ 'Apache-2.0': {
+ licenseName: 'Apache License, Version 2.0',
+ identifier: 'Apache-2.0',
+ canonicalUrl: [
+ 'http://www.apache.org/licenses/LICENSE-2.0',
+ 'magnet:?xt=urn:btih:8e4f440f4c65981c5bf93c76d35135ba5064d8b7&dn=apache-2.0.txt'
+ ],
+ licenseFragments: [{ text: "Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0", type: type.SHORT }]
+ },
- 'LGPL-2.1': {
- licenseName: 'GNU Lesser General Public License, version 2.1',
- identifier: 'LGPL-2.1',
- canonicalUrl: [
- 'http://www.gnu.org/licenses/lgpl-2.1.html',
- 'magnet:?xt=urn:btih:5de60da917303dbfad4f93fb1b985ced5a89eac2&dn=lgpl-2.1.txt'
- ],
- licenseFragments: [{text: "<THISLIBRARY> is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version.", type: type.SHORT}]
- },
+ 'LGPL-2.1': {
+ licenseName: 'GNU Lesser General Public License, version 2.1',
+ identifier: 'LGPL-2.1',
+ canonicalUrl: [
+ 'http://www.gnu.org/licenses/lgpl-2.1.html',
+ 'magnet:?xt=urn:btih:5de60da917303dbfad4f93fb1b985ced5a89eac2&dn=lgpl-2.1.txt'
+ ],
+ licenseFragments: [{ text: "<THISLIBRARY> is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version.", type: type.SHORT }]
+ },
- 'LGPL-3.0': {
- licenseName: 'GNU Lesser General Public License, version 3',
- identifier: 'LGPL-3.0',
- canonicalUrl: [
- 'http://www.gnu.org/licenses/lgpl-3.0.html',
- 'magnet:?xt=urn:btih:0ef1b8170b3b615170ff270def6427c317705f85&dn=lgpl-3.0.txt'
- ],
- licenseFragments: [{text: "<THISPROGRAM> is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.", type: type.SHORT}]
- },
+ 'LGPL-3.0': {
+ licenseName: 'GNU Lesser General Public License, version 3',
+ identifier: 'LGPL-3.0',
+ canonicalUrl: [
+ 'http://www.gnu.org/licenses/lgpl-3.0.html',
+ 'magnet:?xt=urn:btih:0ef1b8170b3b615170ff270def6427c317705f85&dn=lgpl-3.0.txt'
+ ],
+ licenseFragments: [{ text: "<THISPROGRAM> is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.", type: type.SHORT }]
+ },
- 'AGPL-3.0': {
- licenseName: 'GNU AFFERO GENERAL PUBLIC LICENSE version 3',
- identifier: 'AGPL-3.0',
- canonicalUrl: [
- 'http://www.gnu.org/licenses/agpl-3.0.html',
- 'magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt'
- ],
+ 'AGPL-3.0': {
+ licenseName: 'GNU AFFERO GENERAL PUBLIC LICENSE version 3',
+ identifier: 'AGPL-3.0',
+ canonicalUrl: [
+ 'http://www.gnu.org/licenses/agpl-3.0.html',
+ 'magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt'
+ ],
- licenseFragments: [{text: "<THISPROGRAM> is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.", type: type.SHORT}]
- },
+ licenseFragments: [{ text: "<THISPROGRAM> is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.", type: type.SHORT }]
+ },
- 'BSL-1.0': {
- licenseName: 'Boost Software License 1.0',
- identifier: 'BSL-1.0',
- canonicalUrl: [
- 'http://www.boost.org/LICENSE_1_0.txt',
- 'magnet:?xt=urn:btih:89a97c535628232f2f3888c2b7b8ffd4c078cec0&dn=Boost-1.0.txt'
- ],
- licenseFragments: [{text: "Boost Software License <VERSION> <DATE> Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the \"Software\") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following", type: type.SHORT}]
- },
+ 'BSL-1.0': {
+ licenseName: 'Boost Software License 1.0',
+ identifier: 'BSL-1.0',
+ canonicalUrl: [
+ 'http://www.boost.org/LICENSE_1_0.txt',
+ 'magnet:?xt=urn:btih:89a97c535628232f2f3888c2b7b8ffd4c078cec0&dn=Boost-1.0.txt'
+ ],
+ licenseFragments: [{ text: "Boost Software License <VERSION> <DATE> Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the \"Software\") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following", type: type.SHORT }]
+ },
- 'BSD-3-Clause': {
- licenseName: "BSD 3-Clause License",
- identifier: 'BSD-3-Clause',
- canonicalUrl: [
- 'http://opensource.org/licenses/BSD-3-Clause',
- 'magnet:?xt=urn:btih:c80d50af7d3db9be66a4d0a86db0286e4fd33292&dn=bsd-3-clause.txt'
- ],
- licenseFragments: [{text: "Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.", type: type.SHORT}]
- },
+ 'BSD-3-Clause': {
+ licenseName: "BSD 3-Clause License",
+ identifier: 'BSD-3-Clause',
+ canonicalUrl: [
+ 'http://opensource.org/licenses/BSD-3-Clause',
+ 'magnet:?xt=urn:btih:c80d50af7d3db9be66a4d0a86db0286e4fd33292&dn=bsd-3-clause.txt'
+ ],
+ licenseFragments: [{ text: "Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.", type: type.SHORT }]
+ },
- 'BSD-2-Clause': {
- licenseName: "BSD 2-Clause License",
- identifier: 'BSD-2-Clause',
- licenseFragments: [{text: "Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.", type: type.SHORT}]
- },
+ 'BSD-2-Clause': {
+ licenseName: "BSD 2-Clause License",
+ identifier: 'BSD-2-Clause',
+ licenseFragments: [{ text: "Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.", type: type.SHORT }]
+ },
- 'EPL-1.0': {
- licenseName: "Eclipse Public License Version 1.0",
- identifier: "EPL-1.0",
- canonicalUrl: [
- "http://www.eclipse.org/legal/epl-v10.html",
- "magnet:?xt=urn:btih:4c6a2ad0018cd461e9b0fc44e1b340d2c1828b22&dn=epl-1.0.txt"
- ],
- licenseFragments: [
- {
- text: "THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE (\"AGREEMENT\"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.",
- type: type.SHORT
- }
- ]
- },
+ 'EPL-1.0': {
+ licenseName: "Eclipse Public License Version 1.0",
+ identifier: "EPL-1.0",
+ canonicalUrl: [
+ "http://www.eclipse.org/legal/epl-v10.html",
+ "magnet:?xt=urn:btih:4c6a2ad0018cd461e9b0fc44e1b340d2c1828b22&dn=epl-1.0.txt"
+ ],
+ licenseFragments: [
+ {
+ text: "THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE (\"AGREEMENT\"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.",
+ type: type.SHORT
+ }
+ ]
+ },
- 'MPL-2.0': {
- licenseName: 'Mozilla Public License Version 2.0',
- identifier: 'MPL-2.0',
- canonicalUrl: [
- 'http://www.mozilla.org/MPL/2.0',
- 'magnet:?xt=urn:btih:3877d6d54b3accd4bc32f8a48bf32ebc0901502a&dn=mpl-2.0.txt'
- ],
- licenseFragments: [{text: "This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.", type: type.SHORT }]
- },
+ 'MPL-2.0': {
+ licenseName: 'Mozilla Public License Version 2.0',
+ identifier: 'MPL-2.0',
+ canonicalUrl: [
+ 'http://www.mozilla.org/MPL/2.0',
+ 'magnet:?xt=urn:btih:3877d6d54b3accd4bc32f8a48bf32ebc0901502a&dn=mpl-2.0.txt'
+ ],
+ licenseFragments: [{ text: "This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.", type: type.SHORT }]
+ },
- 'Expat': {
- licenseName: 'Expat License (sometimes called MIT Licensed)',
- identifier: 'Expat',
- canonicalUrl: [
- 'http://www.jclark.com/xml/copying.txt',
- 'magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt'
- ],
- licenseFragments: [{text: "Copyright <YEAR> <NAME> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.", type: type.SHORT}]
- },
+ 'Expat': {
+ licenseName: 'Expat License (sometimes called MIT Licensed)',
+ identifier: 'Expat',
+ canonicalUrl: [
+ 'http://www.jclark.com/xml/copying.txt',
+ 'magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt'
+ ],
+ licenseFragments: [{ text: "Copyright <YEAR> <NAME> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.", type: type.SHORT }]
+ },
- 'UPL': {
- licenseName: 'Universal Permissive License',
- identifier: 'UPL-1.0',
- canonicalUrl: [
- 'magnet:?xt=urn:btih:5305d91886084f776adcf57509a648432709a7c7&dn=x11.txt'
- ],
- licenseFragments: [{
- text: "The Universal Permissive License (UPL), Version 1.0",
- type: type.SHORT
- }]
- },
+ 'UPL': {
+ licenseName: 'Universal Permissive License',
+ identifier: 'UPL-1.0',
+ canonicalUrl: [
+ 'magnet:?xt=urn:btih:5305d91886084f776adcf57509a648432709a7c7&dn=x11.txt'
+ ],
+ licenseFragments: [{
+ text: "The Universal Permissive License (UPL), Version 1.0",
+ type: type.SHORT
+ }]
+ },
- 'X11': {
- licenseName: 'X11 License',
- identifier: 'X11',
- canonicalUrl: [
- 'magnet:?xt=urn:btih:5305d91886084f776adcf57509a648432709a7c7&dn=x11.txt'
- ],
- licenseFragments: [{text: "Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.", type: type.SHORT}]
- },
+ 'X11': {
+ licenseName: 'X11 License',
+ identifier: 'X11',
+ canonicalUrl: [
+ 'magnet:?xt=urn:btih:5305d91886084f776adcf57509a648432709a7c7&dn=x11.txt'
+ ],
+ licenseFragments: [{ text: "Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.", type: type.SHORT }]
+ },
- 'XFree86-1.1': {
- licenseName: "XFree86 1.1 License",
- identifier: 'XFree86-1.1',
- canonicalUrl: [
- 'http://www.xfree86.org/3.3.6/COPYRIGHT2.html#3',
- 'http://www.xfree86.org/current/LICENSE4.html',
- 'magnet:?xt=urn:btih:12f2ec9e8de2a3b0002a33d518d6010cc8ab2ae9&dn=xfree86.txt'
- ],
- licenseFragments: [{text: "All rights reserved.\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n1. Redistributions of source code must retain the above copyright notice, this list of conditions, and the following disclaimer.\n2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution, and in the same place and form as other copyright, license and disclaimer information.\n3. The end-user documentation included with the redistribution, if any, must include the following acknowledgment: \"This product includes software developed by The XFree86 Project, Inc (http://www.xfree86.org/) and its contributors\", in the same place and form as other third-party acknowledgments. Alternately, this acknowledgment may appear in the software itself, in the same form and location as other such third-party acknowledgments.4. Except as contained in this notice, the name of The XFree86 Project, Inc shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from The XFree86 Project, Inc.", type: type.SHORT}
- ]
- },
+ 'XFree86-1.1': {
+ licenseName: "XFree86 1.1 License",
+ identifier: 'XFree86-1.1',
+ canonicalUrl: [
+ 'http://www.xfree86.org/3.3.6/COPYRIGHT2.html#3',
+ 'http://www.xfree86.org/current/LICENSE4.html',
+ 'magnet:?xt=urn:btih:12f2ec9e8de2a3b0002a33d518d6010cc8ab2ae9&dn=xfree86.txt'
+ ],
+ licenseFragments: [{ text: "All rights reserved.\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n1. Redistributions of source code must retain the above copyright notice, this list of conditions, and the following disclaimer.\n2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution, and in the same place and form as other copyright, license and disclaimer information.\n3. The end-user documentation included with the redistribution, if any, must include the following acknowledgment: \"This product includes software developed by The XFree86 Project, Inc (http://www.xfree86.org/) and its contributors\", in the same place and form as other third-party acknowledgments. Alternately, this acknowledgment may appear in the software itself, in the same form and location as other such third-party acknowledgments.4. Except as contained in this notice, the name of The XFree86 Project, Inc shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from The XFree86 Project, Inc.", type: type.SHORT }
+ ]
+ },
- 'FreeBSD': {
- licenseName: "FreeBSD License",
- identifier: 'FreeBSD',
- canonicalUrl: [
- 'http://www.freebsd.org/copyright/freebsd-license.html',
- 'magnet:?xt=urn:btih:87f119ba0b429ba17a44b4bffcab33165ebdacc0&dn=freebsd.txt'
- ],
- licenseFragments: [{text: "Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\nRedistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n\nRedistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.", type: type.SHORT}]
- },
+ 'FreeBSD': {
+ licenseName: "FreeBSD License",
+ identifier: 'FreeBSD',
+ canonicalUrl: [
+ 'http://www.freebsd.org/copyright/freebsd-license.html',
+ 'magnet:?xt=urn:btih:87f119ba0b429ba17a44b4bffcab33165ebdacc0&dn=freebsd.txt'
+ ],
+ licenseFragments: [{ text: "Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\nRedistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n\nRedistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.", type: type.SHORT }]
+ },
- 'ISC': {
- licenseName: "The ISC License",
- identifier: 'ISC',
- canonicalUrl: [
- 'https://www.isc.org/downloads/software-support-policy/isc-license/',
- 'magnet:?xt=urn:btih:b8999bbaf509c08d127678643c515b9ab0836bae&dn=ISC.txt'
- ],
- licenseFragments: [{text: "Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.", type: type.SHORT},
- {text: "Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.", type: type.SHORT}]
- },
+ 'ISC': {
+ licenseName: "The ISC License",
+ identifier: 'ISC',
+ canonicalUrl: [
+ 'https://www.isc.org/downloads/software-support-policy/isc-license/',
+ 'magnet:?xt=urn:btih:b8999bbaf509c08d127678643c515b9ab0836bae&dn=ISC.txt'
+ ],
+ licenseFragments: [{ text: "Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.", type: type.SHORT },
+ { text: "Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.", type: type.SHORT }]
+ },
- 'jQueryTools': {
- licenseName: "jQuery Tools",
- licenseFragments: [{
- text: 'NO COPYRIGHTS OR LICENSES. DO WHAT YOU LIKE.',
- type: type.SHORT
- }]
- },
+ 'jQueryTools': {
+ licenseName: "jQuery Tools",
+ licenseFragments: [{
+ text: 'NO COPYRIGHTS OR LICENSES. DO WHAT YOU LIKE.',
+ type: type.SHORT
+ }]
+ },
- 'Artistic-2.0': {
- licenseName: "Artistic License 2.0",
- identifier: 'Artistic-2.0',
- canonicalUrl: [
- "http://www.perlfoundation.org/artistic_license_2_0",
- "magnet:?xt=urn:btih:54fd2283f9dbdf29466d2df1a98bf8f65cafe314&dn=artistic-2.0.txt"
- ],
- licenseFragments: []
- },
+ 'Artistic-2.0': {
+ licenseName: "Artistic License 2.0",
+ identifier: 'Artistic-2.0',
+ canonicalUrl: [
+ "http://www.perlfoundation.org/artistic_license_2_0",
+ "magnet:?xt=urn:btih:54fd2283f9dbdf29466d2df1a98bf8f65cafe314&dn=artistic-2.0.txt"
+ ],
+ licenseFragments: []
+ },
- 'PublicDomain': {
- licenseName: "Public Domain",
- canonicalUrl: [
- 'magnet:?xt=urn:btih:e95b018ef3580986a04669f1b5879592219e2a7a&dn=public-domain.txt'
- ],
- licenseFragments: []
- },
+ 'PublicDomain': {
+ licenseName: "Public Domain",
+ canonicalUrl: [
+ 'magnet:?xt=urn:btih:e95b018ef3580986a04669f1b5879592219e2a7a&dn=public-domain.txt'
+ ],
+ licenseFragments: []
+ },
- 'CPAL-1.0': {
- licenseName: 'Common Public Attribution License Version 1.0 (CPAL)',
- identifier: 'CPAL-1.0',
- canonicalUrl: [
- 'http://opensource.org/licenses/cpal_1.0',
- 'magnet:?xt=urn:btih:84143bc45939fc8fa42921d619a95462c2031c5c&dn=cpal-1.0.txt'
- ],
- licenseFragments: [
- {
- text: 'The contents of this file are subject to the Common Public Attribution License Version 1.0',
- type: type.SHORT
- },
- {
- text: 'The term "External Deployment" means the use, distribution, or communication of the Original Code or Modifications in any way such that the Original Code or Modifications may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Code or Modifications as a distribution under section 3.1 and make Source Code available under Section 3.2.',
- type: type.SHORT
- }
- ]
- },
- 'WTFPL': {
- licenseName: 'Do What The F*ck You Want To Public License (WTFPL)',
- identifier: 'WTFPL',
- canonicalUrl: [
- 'http://www.wtfpl.net/txt/copying/',
- 'magnet:?xt=urn:btih:723febf9f6185544f57f0660a41489c7d6b4931b&dn=wtfpl.txt'
- ],
- licenseFragments: [
- {
- text: 'DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE',
- type: type.SHORT
- },
- {
- text: '0. You just DO WHAT THE FUCK YOU WANT TO.',
- type: type.SHORT
- }
- ]
- },
- 'Unlicense': {
- licenseName: 'Unlicense',
- identifier: 'Unlicense',
- canonicalUrl: [
- 'http://unlicense.org/UNLICENSE',
- 'magnet:?xt=urn:btih:5ac446d35272cc2e4e85e4325b146d0b7ca8f50c&dn=unlicense.txt'
- ],
- licenseFragments: [
- {
- text: 'This is free and unencumbered software released into the public domain.',
- type: type.SHORT
- },
- ]
- }
+ 'CPAL-1.0': {
+ licenseName: 'Common Public Attribution License Version 1.0 (CPAL)',
+ identifier: 'CPAL-1.0',
+ canonicalUrl: [
+ 'http://opensource.org/licenses/cpal_1.0',
+ 'magnet:?xt=urn:btih:84143bc45939fc8fa42921d619a95462c2031c5c&dn=cpal-1.0.txt'
+ ],
+ licenseFragments: [
+ {
+ text: 'The contents of this file are subject to the Common Public Attribution License Version 1.0',
+ type: type.SHORT
+ },
+ {
+ text: 'The term "External Deployment" means the use, distribution, or communication of the Original Code or Modifications in any way such that the Original Code or Modifications may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Code or Modifications as a distribution under section 3.1 and make Source Code available under Section 3.2.',
+ type: type.SHORT
+ }
+ ]
+ },
+ 'WTFPL': {
+ licenseName: 'Do What The F*ck You Want To Public License (WTFPL)',
+ identifier: 'WTFPL',
+ canonicalUrl: [
+ 'http://www.wtfpl.net/txt/copying/',
+ 'magnet:?xt=urn:btih:723febf9f6185544f57f0660a41489c7d6b4931b&dn=wtfpl.txt'
+ ],
+ licenseFragments: [
+ {
+ text: 'DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE',
+ type: type.SHORT
+ },
+ {
+ text: '0. You just DO WHAT THE FUCK YOU WANT TO.',
+ type: type.SHORT
+ }
+ ]
+ },
+ 'Unlicense': {
+ licenseName: 'Unlicense',
+ identifier: 'Unlicense',
+ canonicalUrl: [
+ 'http://unlicense.org/UNLICENSE',
+ 'magnet:?xt=urn:btih:5ac446d35272cc2e4e85e4325b146d0b7ca8f50c&dn=unlicense.txt'
+ ],
+ licenseFragments: [
+ {
+ text: 'This is free and unencumbered software released into the public domain.',
+ type: type.SHORT
+ },
+ ]
+ }
};
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();
diff --git a/pattern_utils.js b/pattern_utils.js
index ce20842..3702e07 100644
--- a/pattern_utils.js
+++ b/pattern_utils.js
@@ -21,22 +21,22 @@
*/
exports.patternUtils = {
- /**
- * removeNonalpha
- *
- * Remove all nonalphanumeric values, except for
- * < and >, since they are what we use for tokens.
- *
- */
- removeNonalpha: function (str) {
- var regex = /[^a-z0-9<>@]+/gi;
- return str.replace(regex, '');
- },
- removeWhitespace: function (str) {
- return str.replace(/\/\//gmi, '').replace(/\*/gmi, '').replace(/\s+/gmi, '');
- },
- replaceTokens: function (str) {
- var regex = /<.*?>/gi;
- return str.replace(regex, '.*?');
- }
+ /**
+ * removeNonalpha
+ *
+ * Remove all nonalphanumeric values, except for
+ * < and >, since they are what we use for tokens.
+ *
+ */
+ removeNonalpha: function(str) {
+ var regex = /[^a-z0-9<>@]+/gi;
+ return str.replace(regex, '');
+ },
+ removeWhitespace: function(str) {
+ return str.replace(/\/\//gmi, '').replace(/\*/gmi, '').replace(/\s+/gmi, '');
+ },
+ replaceTokens: function(str) {
+ var regex = /<.*?>/gi;
+ return str.replace(regex, '.*?');
+ }
};
diff --git a/test.js b/test.js
index d87353b..d695262 100644
--- a/test.js
+++ b/test.js
@@ -30,23 +30,23 @@
const firefox = require('selenium-webdriver/firefox');
new webdriver.Builder().forBrowser('firefox')
.setFirefoxOptions(new firefox.Options()
- // Uncomment this line to test using icecat
-// .setBinary("/usr/bin/icecat")
- .headless()).build()
+ // Uncomment this line to test using icecat
+ // .setBinary("/usr/bin/icecat")
+ .headless()).build()
.then(driver =>
driver.installAddon("./librejs.xpi", /*isTemporary=*/true)
- .then(driver.get("about:debugging#/runtime/this-firefox"))
- .then(_ => driver.findElements(webdriver.By.css('.fieldpair dd')))
- .then(es => es[2].getText())
- .then(uuid =>
- driver.get('moz-extension://'
- + uuid + '/test/SpecRunner.html'
- + (process.argv[2] ? '?seed=' + process.argv[2] : '') ))
- .then(_ => driver.wait(_ =>
- driver.findElement(webdriver.By.css('.jasmine-alert'))
- .then(e => e.getText()), 10000))
- .then(_ => driver.findElement(webdriver.By.css('.jasmine-alert')))
- .then(e => e.getText())
- .then(console.log)
- .then(_ => driver.quit()));
+ .then(driver.get("about:debugging#/runtime/this-firefox"))
+ .then(_ => driver.findElements(webdriver.By.css('.fieldpair dd')))
+ .then(es => es[2].getText())
+ .then(uuid =>
+ driver.get('moz-extension://'
+ + uuid + '/test/SpecRunner.html'
+ + (process.argv[2] ? '?seed=' + process.argv[2] : '')))
+ .then(_ => driver.wait(_ =>
+ driver.findElement(webdriver.By.css('.jasmine-alert'))
+ .then(e => e.getText()), 10000))
+ .then(_ => driver.findElement(webdriver.By.css('.jasmine-alert')))
+ .then(e => e.getText())
+ .then(console.log)
+ .then(_ => driver.quit()));
})();
diff --git a/test/spec/LibreJSSpec.js b/test/spec/LibreJSSpec.js
index 8d4aebe..1320a2a 100644
--- a/test/spec/LibreJSSpec.js
+++ b/test/spec/LibreJSSpec.js
@@ -42,13 +42,13 @@ describe("LibreJS' components", () => {
beforeAll(async () => {
let url = browser.extension.getURL("/test/resources/index.html");
- tab = (await browser.tabs.query({url}))[0] || (await browser.tabs.create({url}));
+ tab = (await browser.tabs.query({ url }))[0] || (await browser.tabs.create({ url }));
documentUrl = url;
});
describe("The whitelist/blacklist manager", () => {
- let {ListManager, ListStore, Storage} = LibreJS;
+ let { ListManager, ListStore, Storage } = LibreJS;
let lm = new ListManager(new ListStore("_test.whitelist", Storage.CSV), new ListStore("_test.blacklist", Storage.CSV), new Set());
let forgot = ["http://formerly.whitelist.ed/", "http://formerly.blacklist.ed/"];
@@ -58,14 +58,14 @@ describe("LibreJS' components", () => {
});
it("Should handle basic CRUD operations", async () => {
- expect(lm.getStatus(forgot[0])).toBe("whitelisted");
- expect(lm.getStatus(forgot[1])).toBe("blacklisted");
+ expect(lm.getStatus(forgot[0])).toBe("whitelisted");
+ expect(lm.getStatus(forgot[1])).toBe("blacklisted");
- await lm.forget(...forgot);
+ await lm.forget(...forgot);
- for (let url of forgot) {
- expect(lm.getStatus(url)).toBe("unknown");
- }
+ for (let url of forgot) {
+ expect(lm.getStatus(url)).toBe("unknown");
+ }
});
it("Should support full path wildcards", () => {
@@ -91,7 +91,7 @@ describe("LibreJS' components", () => {
let processScript = async (source, whitelisted = false) =>
await LibreJS.handle_script({
text: source,
- request: {url, tabId: tab.id, documentUrl, frameId: 0},
+ request: { url, tabId: tab.id, documentUrl, frameId: 0 },
}, whitelisted);
it("should accept whitelisted scripts", async () => {
@@ -249,13 +249,13 @@ describe("LibreJS' components", () => {
});
describe("The external (Web Labels) license checker", () => {
- let {ExternalLicenses} = LibreJS;
+ let { ExternalLicenses } = LibreJS;
let check;
beforeAll(async () => {
- let args = {tabId: tab.id, frameId: 0, documentUrl};
+ let args = { tabId: tab.id, frameId: 0, documentUrl };
let resolve = url => new URL(url, documentUrl).href;
- check = async url => await ExternalLicenses.check(Object.assign({url: resolve(url)}, args));
+ check = async url => await ExternalLicenses.check(Object.assign({ url: resolve(url) }, args));
await browser.tabs.executeScript(tab.id, {
file: "/content/externalLicenseChecker.js"
});
@@ -284,6 +284,6 @@ describe("LibreJS' components", () => {
});
afterAll(async () => {
await browser.tabs.remove(tab.id);
- browser.tabs.update((await browser.tabs.getCurrent()).id, {active: true});
+ browser.tabs.update((await browser.tabs.getCurrent()).id, { active: true });
});
});