diff --git a/.eslintrc.json b/.eslintrc.json index 0cddb96..3920238 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,40 +1,68 @@ { - "env": { - "browser": true, - "commonjs": true, - "es6": true, - "webextensions": true - }, - "parserOptions": { - "ecmaFeatures": { - "jsx": true - }, - "sourceType": "script" - }, - "extends": "eslint:recommended", - "globals": { - "exportFunction": false - }, - "rules": { - "brace-style": ["error", "stroustrup", {"allowSingleLine": true}], - "comma-spacing": ["error", { "before": false, "after": true }], - "constructor-super": "warn", - "eqeqeq": "error", - "max-len": ["warn", {"code": 120, "tabWidth": 4}], - "max-lines": ["warn", {"max": 500, "skipBlankLines": true, "skipComments": true}], - "max-params": ["warn", 4], - "no-const-assign": "warn", - "no-this-before-super": "warn", - "no-undef": "error", - "no-unreachable": "warn", - "no-unused-vars": "off", - "no-trailing-spaces": ["error", {"skipBlankLines": true}], - "no-mixed-spaces-and-tabs": ["error", "smart-tabs"], - "indent": ["error", "tab", {"SwitchCase": 1}], - "space-in-parens": ["error", "never"], - "valid-typeof": "warn", - "quotes": ["error", "double"], - "semi": ["error", "always"], - "strict": ["error", "function"] - } + "env": { + "browser": true, + "commonjs": true, + "es6": true, + "webextensions": true + }, + "parserOptions": { + "ecmaFeatures": { + "jsx": true + }, + "sourceType": "script" + }, + "plugins": ["promise", "eslint-comments"], + "extends": [ + "eslint:recommended", + "plugin:promise/recommended", + "plugin:eslint-comments/recommended" + ], + "globals": { + "exportFunction": false + }, + "rules": { + "brace-style": ["error", "stroustrup", {"allowSingleLine": true}], + "comma-spacing": ["error", { "before": false, "after": true }], + "complexity": ["warn", 20], + "constructor-super": "warn", + "eqeqeq": "error", + "eslint-comments/no-use": ["error", {"allow": ["eslint-disable-next-line"]}], + "indent": ["error", "tab", {"SwitchCase": 1}], + "max-depth": ["warn", 4], + "max-len": ["warn", {"code": 120, "tabWidth": 4}], + "max-lines-per-function": ["warn", {"max": 80,"skipBlankLines": true, "skipComments": true}], + "max-lines": ["warn", {"max": 500, "skipBlankLines": true, "skipComments": true}], + "max-params": ["warn", 4], + "no-console": "error", + "no-const-assign": "error", + "no-inner-declarations": "warn", + "no-mixed-spaces-and-tabs": ["error", "smart-tabs"], + "no-prototype-builtins": "off", + "no-this-before-super": "warn", + "no-trailing-spaces": ["error", {"skipBlankLines": true}], + "no-undef": "error", + "no-unreachable": "warn", + "no-unused-vars": "off", + "no-use-before-define": ["error", {"functions": false}], + "no-var": "error", + "quotes": ["error", "double"], + "semi": ["error", "always"], + "space-in-parens": ["error", "never"], + "strict": ["error", "function"], + "valid-typeof": "warn" + }, + "overrides": [ + { + "files": ["detectionTest.js", "modifiedCanvasAPI.js", "options.js", "settingsDisplay.js"], + "rules": { + "max-lines": "off" + } + }, + { + "files": ["test/*.js"], + "rules": { + "no-var": "off" + } + } + ] } \ No newline at end of file diff --git a/browserAction/browserAction.js b/browserAction/browserAction.js index 20169f2..d28cea3 100644 --- a/browserAction/browserAction.js +++ b/browserAction/browserAction.js @@ -11,7 +11,7 @@ logging.message("Opened browser action"); settings.onloaded(function(){ - var actions = document.getElementById("actions"); + const actions = document.getElementById("actions"); [ { @@ -54,10 +54,10 @@ logging.verbose("Hiding advanced action"); return; } - var actionButton = document.createElement("button"); + const actionButton = document.createElement("button"); actionButton.className = "action"; - var icon = document.createElement("span"); + const icon = document.createElement("span"); icon.className = "icon"; icon.style.maskImage = "url(" + action.icon + ")"; @@ -72,7 +72,7 @@ actions.appendChild(actionButton); }); - var search = document.createElement("input"); + const search = document.createElement("input"); search.placeholder = extension.getTranslation("search"); search.className = "search action"; actions.appendChild(search); diff --git a/lib/askForPermission.js b/lib/askForPermission.js index 0ea5a3d..fe7ae97 100644 --- a/lib/askForPermission.js +++ b/lib/askForPermission.js @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ (function(){ "use strict"; - var scope; + let scope; if ((typeof exports) !== "undefined"){ scope = exports; } @@ -15,15 +15,15 @@ // Check canvas appearance function canvasAppearance(window, api, context){ - var oldBorder = false; - var canvas = false; - var inDOM = null; + let oldBorder = false; + let canvas = false; + let inDOM = null; if (api === "canvas" && context){ - var nodeName; + let nodeName; try { nodeName = context.nodeName; } - catch (e){ + catch (error){ nodeName = ""; } if (nodeName === "CANVAS"){ @@ -45,17 +45,17 @@ canvas: canvas, askCategory: canvas? (inDOM? "visible": "invisible"): (api === "canvas"? "nocanvas": api), get text(){ - var text = canvas? (this.visible? "visible": "invisible"): (api === "canvas"? "nocanvas": api); + const text = canvas? (this.visible? "visible": "invisible"): (api === "canvas"? "nocanvas": api); Object.defineProperty(this, "text", {value: text}); return text; }, inDom: inDOM, get visible(){ - var visible = inDOM; + let visible = inDOM; if (inDOM){ canvas.scrollIntoView(); - var rect = canvas.getBoundingClientRect(); - var foundEl = window.document.elementFromPoint( + const rect = canvas.getBoundingClientRect(); + const foundEl = window.document.elementFromPoint( rect.left + rect.width / 2, rect.top + rect.height / 2 ); @@ -72,9 +72,9 @@ }; } - var modes = new WeakMap(); + const modes = new WeakMap(); function getAskMode(window, type, _){ - var mode = modes.get(window); + let mode = modes.get(window); if (mode){ return mode[type]; } @@ -132,11 +132,11 @@ } scope.ask = function({window, type, api, canvas, errorStack}, {_, prefs}){ - var answer; - var askMode = getAskMode(window, type, _); - var askStatus = askMode.askStatus; - var appearance = canvasAppearance(window, api, canvas); - var category = appearance.askCategory; + let answer; + const askMode = getAskMode(window, type, _); + const askStatus = askMode.askStatus; + const appearance = canvasAppearance(window, api, canvas); + let category = appearance.askCategory; if (prefs("askOnlyOnce") !== "no" && askStatus.alreadyAsked[category]){ // already asked appearance.reset(); @@ -146,7 +146,6 @@ let imgContainer = null; if (type === "readout" && prefs("showCanvasWhileAsking") && canvas){ try { - let content = canvas.toDataURL(); let document = window.top.document; imgContainer = document.createElement("div"); imgContainer.style.cssText = ` @@ -177,12 +176,12 @@ imgContainer.appendChild(img); document.body.appendChild(imgContainer); } - catch (e){ + catch (error){ // unable to read the canvas } } // asking - var msg = askMode.askText[appearance.text]; + let msg = askMode.askText[appearance.text]; // visible vs invisible is only calculated here correctly category = appearance.text; @@ -196,8 +195,8 @@ if (prefs("askOnlyOnce") === "combined"){ ["context", "readout", "input"].forEach(function(type){ - var askMode = getAskMode(window, type, _); - var askStatus = askMode.askStatus; + const askMode = getAskMode(window, type, _); + const askStatus = askMode.askStatus; askStatus.alreadyAsked[category] = true; askStatus.answer[category] = answer; }); diff --git a/lib/callingStack.js b/lib/callingStack.js index 8ff2893..04517c4 100644 --- a/lib/callingStack.js +++ b/lib/callingStack.js @@ -4,7 +4,7 @@ (function(){ "use strict"; - var scope; + let scope; if ((typeof exports) !== "undefined"){ scope = exports; } @@ -16,12 +16,12 @@ const extension = require("./extension"); // Translation - var _ = function(name, replace, translateAPI){ + const _ = function(name, replace, translateAPI){ if (!translateAPI){ translateAPI = extension.getTranslation; } - var str = translateAPI(name) || name; + let str = translateAPI(name) || name; if (replace){ // replace generic content in the translation by given parameter Object.keys(replace).forEach(function(name){ @@ -33,7 +33,7 @@ // Stack parsing function parseStackEntry(entry){ - var m = /@(.*):(\d*):(\d*)$/.exec(entry) || ["", entry, "--", "--"]; + const m = /@(.*):(\d*):(\d*)$/.exec(entry) || ["", entry, "--", "--"]; return { url: m[1], line: parseInt(m[2], 10), @@ -61,13 +61,12 @@ // parse calling stack const extensionID = extension.extensionID; function parseErrorStack(errorStack){ - var callers = errorStack.trim().split("\n"); - callers = callers.map(parseStackEntry).filter(function(caller){ + const callers = errorStack.trim().split("\n").map(parseStackEntry).filter(function(caller){ return !caller.url.startsWith(extensionID); }); return { toString: function(translateAPI){ - var msg = ""; + let msg = ""; msg += "\n\n" + _("sourceOutput", undefined, translateAPI) + ": "; if (settings.showCompleteCallingStack){ msg += callers.reduce(function(stack, c){ @@ -82,7 +81,7 @@ }, match: function(stackRule){ if (typeof stackRule.stackPosition !== "undefined"){ - var pos = stackRule.stackPosition; + let pos = stackRule.stackPosition; if (pos < 0){ pos += callers.length; } diff --git a/lib/check.js b/lib/check.js index 871dc91..8e77058 100644 --- a/lib/check.js +++ b/lib/check.js @@ -4,7 +4,7 @@ (function(){ "use strict"; - var scope; + let scope; if ((typeof exports) !== "undefined"){ scope = exports; } @@ -19,7 +19,7 @@ scope.check = function check({url, errorStack}){ url = new URL(url || "about:blank"); - var match = checkBoth(errorStack, url, settings.get("blockMode", url)).match( + const match = checkBoth(errorStack, url, settings.get("blockMode", url)).match( /^(block|allow|fake|ask)(|Everything|Internal)$/ ); if (match){ @@ -63,7 +63,7 @@ return "allowInternal"; } - var mode = "block"; + let mode = "block"; switch (blockMode){ case "blockEverything": mode = "block"; diff --git a/lib/colorStatistics.js b/lib/colorStatistics.js index 1438e55..2822c1e 100644 --- a/lib/colorStatistics.js +++ b/lib/colorStatistics.js @@ -5,7 +5,7 @@ "use strict"; - var scope; + let scope; if ((typeof exports) !== "undefined"){ scope = exports; } @@ -23,8 +23,8 @@ this.minBoundary.nextColor = this.maxBoundary; } addColor(r, g, b, a){ - var index = String.fromCharCode(r, g, b, a); - var color = this.colors[index]; + const index = String.fromCharCode(r, g, b, a); + let color = this.colors[index]; if (!color){ color = { index, @@ -42,10 +42,10 @@ if (color.count > color.nextColor.count){ // swap colors to remain in right order // a_ -> b_ -> c -> d becomes a_ -> c -> b_ -> d - var a_ = color.previousColor; - var b_ = color; - var c = color.nextColor; - var d = color.nextColor.nextColor; + const a_ = color.previousColor; + const b_ = color; + const c = color.nextColor; + const d = color.nextColor.nextColor; a_.nextColor = c; c.previousColor = a_; @@ -59,8 +59,8 @@ } getMaxColors(n){ n = Math.min(n, this.numberOfColors); - var colors = Object.create(null); - var current = this.maxBoundary; + const colors = Object.create(null); + let current = this.maxBoundary; for (;n && current;n -= 1){ current = current.previousColor; colors[current.index] = current; @@ -70,8 +70,8 @@ } scope.compute = function computeColorStatistics(rawData){ - var statistic = new Statistic(); - for (var i = 0, l = rawData.length; i < l; i += 4){ + const statistic = new Statistic(); + for (let i = 0, l = rawData.length; i < l; i += 4){ statistic.addColor( rawData[i + 0], rawData[i + 1], @@ -86,10 +86,10 @@ return statistic.numberOfColors > threshold; } else { - var colors = Object.create(null); - var count = 0; - for (var i = 0, l = rawData.length; i < l; i += 4){ - var index = String.fromCharCode( + const colors = Object.create(null); + let count = 0; + for (let i = 0, l = rawData.length; i < l; i += 4){ + const index = String.fromCharCode( rawData[i + 0], rawData[i + 1], rawData[i + 2], diff --git a/lib/dataUrls.js b/lib/dataUrls.js index ddb60dc..5f5dc7e 100644 --- a/lib/dataUrls.js +++ b/lib/dataUrls.js @@ -4,7 +4,7 @@ (function(){ "use strict"; - var scope; + let scope; if ((typeof exports) !== "undefined"){ scope = exports; } @@ -21,6 +21,10 @@ const mainVersion = parseInt(info.version.replace(/\..+/, ""), 10); canMergeHeader = mainVersion > 59; blockBlob = mainVersion < 60; + return canMergeHeader; + }).catch(function(){ + canMergeHeader = false; + blockBlob = true; }); function setHeader(headers, header){ if (canMergeHeader){ diff --git a/lib/extension.js b/lib/extension.js index 93b3108..ea11cbb 100644 --- a/lib/extension.js +++ b/lib/extension.js @@ -4,7 +4,7 @@ (function(){ "use strict"; - var scope; + let scope; if ((typeof exports) !== "undefined"){ scope = exports; } diff --git a/lib/frame.js b/lib/frame.js index 5ed6e30..6e5a376 100644 --- a/lib/frame.js +++ b/lib/frame.js @@ -14,13 +14,12 @@ const iframeProtection = require("./iframeProtection"); const logging = require("./logging"); - const {error, warning, message, notice, verbose, setPrefix: setLogPrefix} = logging; - setLogPrefix("frame script"); + logging.setPrefix("frame script"); // Variable to "unload" the script - var enabled = true; + let enabled = true; - message("starting", location.href); + logging.message("starting", location.href); function check(message){ if (enabled){ @@ -72,38 +71,38 @@ } computeExtensionSecret(); - message("open port to background script"); - var port = browser.runtime.connect(); + logging.message("open port to background script"); + const port = browser.runtime.connect(); if (window === window.top){ - message("Is top level window -> tab had navigation -> clear page action"); + logging.message("Is top level window -> tab had navigation -> clear page action"); port.postMessage({"canvasBlocker-clear-page-action": true}); } - var tabId; + let tabId; port.onMessage.addListener(function(data){ - message("Got data from port", data); + logging.message("Got data from port", data); if (data.hasOwnProperty("tabId")){ - notice("my tab id is", data.tabId); + logging.notice("my tab id is", data.tabId); tabId = data.tabId; } if (data.hasOwnProperty("cookieStoreId")){ - notice("my tab cookie store id is", data.cookieStoreId); + logging.notice("my tab cookie store id is", data.cookieStoreId); const {persistent: persistentRnd} = require("./randomSupplies"); persistentRnd.setCookieStoreId(data.cookieStoreId); } const persistentRndName = "persistent" + (extension.inIncognitoContext? "Incognito": "") + "Rnd"; if (data.hasOwnProperty(persistentRndName)){ const persistentRndValue = data[persistentRndName]; - notice("got persistent random data", persistentRndValue); + logging.notice("got persistent random data", persistentRndValue); const {persistent: persistentRnd} = require("./randomSupplies"); Object.keys(persistentRndValue).forEach(function(domain){ - verbose("random data for", domain, persistentRndValue[domain]); + logging.verbose("random data for", domain, persistentRndValue[domain]); persistentRnd.setDomainRnd(domain, persistentRndValue[domain]); }); } }); - var notifications = []; - var notificationCounter = {}; - var sentAPIs = {}; + const notifications = []; + const notificationCounter = {}; + const sentAPIs = {}; function notify(data){ if (!settings.ignoredAPIs[data.api]){ if (settings.storeNotificationData){ @@ -128,16 +127,18 @@ } - var interceptedWindows = new WeakMap(); + const interceptedWindows = new WeakMap(); function interceptWindow(window){ + let wrappedTry; try { - var href = window.location.href; - var wrappedTry = getWrapped(window); + // eslint-disable-next-line no-unused-vars + const href = window.location.href; + wrappedTry = getWrapped(window); } - catch (e){ + catch (error){ // we are unable to read the location due to SOP // therefore we also can not intercept anything. - notice("NOT intercepting window due to SOP", window); + logging.notice("NOT intercepting window due to SOP", window); return false; } const wrappedWindow = wrappedTry; @@ -150,12 +151,12 @@ return false; } - message("intercepting window", window); + logging.message("intercepting window", window); intercept( {subject: window}, {check, checkStack, ask: askWrapper, notify, prefs} ); - message("prepare to intercept (i)frames."); + logging.message("prepare to intercept (i)frames."); function interceptAllFrames(){ const currentLength = window.length; @@ -185,7 +186,7 @@ return true; } - message("register listener for messages from background script"); + logging.message("register listener for messages from background script"); extension.message.on(function(data){ if (data["canvasBlocker-unload"]){ enabled = false; @@ -194,14 +195,14 @@ data.hasOwnProperty("canvasBlocker-sendNotifications") && data["canvasBlocker-sendNotifications"] === tabId ){ - notice("sending notifications:", notifications); + logging.notice("sending notifications:", notifications); extension.message.send({ sender: tabId, url: window.location.href, "canvasBlocker-notificationCounter": notificationCounter, "canvasBlocker-notifications": notifications }); - notice("notifications sent"); + logging.notice("notifications sent"); } }); diff --git a/lib/hash.js b/lib/hash.js index c4958bf..d4d68dc 100644 --- a/lib/hash.js +++ b/lib/hash.js @@ -4,7 +4,7 @@ (function(){ "use strict"; - var scope; + let scope; if ((typeof exports) !== "undefined"){ scope = exports; } @@ -20,11 +20,11 @@ }; scope.sumXor = function(inputByteArray){ - var hash = new Uint32Array(4); - var sum = new Float64Array(hash.buffer, 8, 1); - var intView = new Uint32Array(inputByteArray.buffer); - var floatView = new Float32Array(inputByteArray.buffer); - var length = intView.length; + const hash = new Uint32Array(4); + // const sum = new Float64Array(hash.buffer, 8, 1); + const intView = new Uint32Array(inputByteArray.buffer); + // const floatView = new Float32Array(inputByteArray.buffer); + const length = intView.length; for (let i = 0; i < length; i += 1){ // sum[0] += floatView[i]; hash[0] ^= intView[i]; @@ -34,11 +34,11 @@ }; scope.hashCode = function(inputByteArray){ - var hash = new Uint32Array(1); - var intView = new Uint32Array(inputByteArray.buffer); - var length = intView.length; + const hash = new Uint32Array(1); + const intView = new Uint32Array(inputByteArray.buffer); + const length = intView.length; for (let i = 0; i < length; i += 1){ - var v = hash[0]; + const v = hash[0]; hash[0] = ((v << 5) - v) + intView[i]; } return hash; @@ -81,21 +81,21 @@ return function md5(inputByteArray){ h.set(hInitial); - var length = inputByteArray.buffer.byteLength; - var messageBitLength = length * 8; + const length = inputByteArray.buffer.byteLength; + const messageBitLength = length * 8; // create byte array with length dividable by 64 (512 bit) - var neededLength = Math.ceil((length + 1 + 8) / 64) * 64; - var messageByteArray = new Uint8Array(neededLength); + const neededLength = Math.ceil((length + 1 + 8) / 64) * 64; + const messageByteArray = new Uint8Array(neededLength); messageByteArray.set(new Uint8Array(inputByteArray.buffer)); - var view = new DataView(messageByteArray.buffer); + const view = new DataView(messageByteArray.buffer); // append 10...000000 messageByteArray[length] = 0x80; // append size in 64 bit big endian view.setUint32(neededLength - 8, messageBitLength, true); - for (var i = 0; i < neededLength; i += 64){ + for (let i = 0; i < neededLength; i += 64){ for (let j = 0; j < 64; j += 4){ w[j / 4] = view.getUint32(i + j, true); } @@ -118,7 +118,7 @@ temp[4] = (temp[2] ^ (temp[1] | (~ temp[3]))); temp[5] = (7*j) % 16; } - var temp_ = temp[3]; + const temp_ = temp[3]; temp[3] = temp[2]; temp[2] = temp[1]; temp[1] = (leftRotate(temp[0] + temp[4] + k[j] + w[temp[5]], r[j]) + temp[1]); @@ -165,14 +165,14 @@ h.set(hInitial); - var length = inputByteArray.buffer.byteLength; - var messageBitLength = length * 8; + const length = inputByteArray.buffer.byteLength; + const messageBitLength = length * 8; // create byte array with length dividable by 64 (512 bit) - var neededLength = Math.ceil((length + 1 + 8) / 64) * 64; - var messageByteArray = new Uint8Array(neededLength); + const neededLength = Math.ceil((length + 1 + 8) / 64) * 64; + const messageByteArray = new Uint8Array(neededLength); messageByteArray.set(new Uint8Array(inputByteArray.buffer)); - var view = new DataView(messageByteArray.buffer); + const view = new DataView(messageByteArray.buffer); // append 10...000000 messageByteArray[length] = 0x80; diff --git a/lib/iframeProtection.js b/lib/iframeProtection.js index 9064386..95d0278 100644 --- a/lib/iframeProtection.js +++ b/lib/iframeProtection.js @@ -19,8 +19,7 @@ return lists.get("white").match(url) || settings.get("blockMode", url).startsWith("allow"); } - scope.protect = function protect(window, wrappedWindow, singleCallback, allCallback){ - + function createChangeProperty(window){ function changeProperty(object, name, type, changed){ const descriptor = Object.getOwnPropertyDescriptor(object, name); const original = descriptor[type]; @@ -49,7 +48,10 @@ return; } } - + return changeProperty; + } + + function protectFrameProperties({window, wrappedWindow, changeProperty, singleCallback}){ ["HTMLIFrameElement", "HTMLFrameElement"].forEach(function(constructorName){ const constructor = window[constructorName]; const wrappedConstructor = wrappedWindow[constructorName]; @@ -61,7 +63,7 @@ const originalContentWindowGetter = contentWindowDescriptor.get; const contentWindowTemp = { get contentWindow(){ - var window = originalContentWindowGetter.call(this); + const window = originalContentWindowGetter.call(this); if (window){ singleCallback(window); } @@ -80,7 +82,7 @@ const originalContentDocumentGetter = contentDocumentDescriptor.get; const contentDocumentTemp = { get contentDocument(){ - var document = originalContentDocumentGetter.call(this); + const document = originalContentDocumentGetter.call(this); if (document){ singleCallback(document.defaultView); } @@ -92,6 +94,9 @@ window )); }); + } + + function protectDOMModifications({window, wrappedWindow, changeProperty, allCallback}){ [ // useless as length could be obtained before the iframe is created and window.frames === window // { @@ -161,31 +166,31 @@ )); }); }); - - // MutationObserver to intercept iFrames while generating the DOM. - const observe = function(){ - var observer = new MutationObserver(allCallback); - var observing = false; - function observe(){ - if ( - !observing && - window.document - ){ - observer.observe(window.document, {subtree: true, childList: true}); - observing = true; - } + } + + function enableMutationObserver({window, allCallback}){ + const observer = new MutationObserver(allCallback); + let observing = false; + function observe(){ + if ( + !observing && + window.document + ){ + observer.observe(window.document, {subtree: true, childList: true}); + observing = true; } - observe(); - window.document.addEventListener("DOMContentLoaded", function(){ - if (observing){ - observer.disconnect(); - observing = false; - } - }); - return observe; - }(); - - // MutationObserver does not trigger fast enough when document.write is used + } + observe(); + window.document.addEventListener("DOMContentLoaded", function(){ + if (observing){ + observer.disconnect(); + observing = false; + } + }); + return observe; + } + + function protectDocumentWrite({window, wrappedWindow, changeProperty, observe, allCallback}){ const documentWriteDescriptorOnHTMLDocument = Object.getOwnPropertyDescriptor( wrappedWindow.HTMLDocument.prototype, "write" @@ -199,6 +204,7 @@ documentWriteDescriptorOnHTMLDocument? wrappedWindow.HTMLDocument.prototype: wrappedWindow.Document.prototype, + // eslint-disable-next-line no-unused-vars "write", "value", exportFunction(function write(markup){ for (let i = 0, l = arguments.length; i < l; i += 1){ const str = "" + arguments[i]; @@ -234,6 +240,7 @@ wrappedWindow.HTMLDocument.prototype: wrappedWindow.Document.prototype, "writeln", "value", exportFunction( + // eslint-disable-next-line no-unused-vars function writeln(markup){ for (let i = 0, l = arguments.length; i < l; i += 1){ const str = "" + arguments[i]; @@ -253,5 +260,21 @@ window ) ); + } + + scope.protect = function protect(window, wrappedWindow, singleCallback, allCallback){ + const changeProperty = createChangeProperty(window); + + const api = {window, wrappedWindow, changeProperty, singleCallback, allCallback}; + + protectFrameProperties(api); + + protectDOMModifications(api); + + // MutationObserver to intercept iFrames while generating the DOM. + api.observe = enableMutationObserver(api); + + // MutationObserver does not trigger fast enough when document.write is used + protectDocumentWrite(api); }; }()); \ No newline at end of file diff --git a/lib/intercept.js b/lib/intercept.js index e966066..56dee6f 100644 --- a/lib/intercept.js +++ b/lib/intercept.js @@ -41,380 +41,394 @@ setRandomSupplyByType(settings.rng); }); - function getURL(window){ + function getURL(windowToProcess){ let href; try { - href = window.location.href; + href = windowToProcess.location.href; } - catch (e){ + catch (error){ // unable to read location due to SOP // since we are not able to do anything in that case we can allow everything return "about:SOP"; } if (!href || href === "about:blank"){ - if (window !== window.parent){ - return getURL(window.parent); + if (windowToProcess !== windowToProcess.parent){ + return getURL(windowToProcess.parent); } - else if (window.opener){ - return getURL(window.opener); + else if (windowToProcess.opener){ + return getURL(windowToProcess.opener); } } return href; } + const getAllFunctionObjects = function(windowToProcess, changedFunction){ + return ( + Array.isArray(changedFunction.object)? + changedFunction.object: + [changedFunction.object] + ).map(function(name){ + if (name){ + const constructor = getWrapped(windowToProcess)[name]; + if (constructor){ + return constructor.prototype; + } + } + return false; + }).concat( + changedFunction.objectGetters? + changedFunction.objectGetters.map(function(objectGetter){ + return objectGetter(getWrapped(windowToProcess)); + }): + [] + ); + }; + const forEachFunction = function(windowToProcess, callback){ + apiNames.forEach(function(name){ + const changedFunction = changedFunctions[name]; + getAllFunctionObjects(windowToProcess, changedFunction).forEach(function(object){ + if (object){ + callback({name, object: object, changedFunction}); + } + }); + }); + }; + const forEachGetter = function(windowToProcess, callback){ + changedGetters.forEach(function(changedGetter){ + const name = changedGetter.name; + changedGetter.objectGetters.forEach(function(changedGetter){ + const object = changedGetter(getWrapped(windowToProcess)); + if (object){ + callback({name, object, objectGetter: changedGetter}); + } + }); + }); + }; - scope.preIntercept = function preIntercept({subject: window}, apis){ - if (!settings.isStillDefault){ - logging.message("settings already loaded -> no need to pre intercept"); - scope.intercept({subject: window}, apis); + const forEach = function(windowToProcess, callback){ + forEachFunction(windowToProcess, callback); + forEachGetter(windowToProcess, callback); + }; + + const doRealIntercept = function(windowToProcess, apis, state){ + if (!state.intercepted){ + scope.intercept({subject: windowToProcess}, apis); + state.intercepted = true; } - else { - logging.message("settings not loaded -> need to pre intercept"); - let forceLoad = true; - let preIntercepted = false; - let intercepted = false; - const forEachFunction = function(callback){ - apiNames.forEach(function(name){ - const changedFunction = changedFunctions[name]; - ( - Array.isArray(changedFunction.object)? - changedFunction.object: - [changedFunction.object] - ).map(function(name){ - if (name){ - const constructor = getWrapped(window)[name]; - if (constructor){ - return constructor.prototype; - } - } - return false; - }).concat( - changedFunction.objectGetters? - changedFunction.objectGetters.map(function(objectGetter){ - return objectGetter(getWrapped(window)); - }): - [] - ).forEach(function(object){ - if (object){ - callback({name, object: object}); - } - }); - }); - changedGetters.forEach(function(changedGetter){ - const name = changedGetter.name; - changedGetter.objectGetters.forEach(function(objectGetter){ - const object = objectGetter(getWrapped(window)); - if (object){ - callback({name, object}); - } - }); - }); - }; - let originalPropertyDescriptors = {}; - const doPreIntercept = function(){ - if (!preIntercepted){ - forEachFunction(function({name, object}){ - const map = originalPropertyDescriptors[name] || new WeakMap(); - originalPropertyDescriptors[name] = map; - - const originalPropertyDescriptor = Object.getOwnPropertyDescriptor(object, name); - if (!originalPropertyDescriptor){ - return; - } - - map.set(object, originalPropertyDescriptor); + }; + const doPreIntercept = function(windowToProcess, apis, state){ + const forceLoad = true; + const originalPropertyDescriptors = {}; + const undoPreIntercept = function(){ + if (state.preIntercepted){ + state.preIntercepted = false; + forEach(windowToProcess, function({name, object}){ + const originalPropertyDescriptor = originalPropertyDescriptors[name].get(object); + if (originalPropertyDescriptor){ Object.defineProperty( object, name, - { - enumerable: true, - configurable: true, - get: exportFunction(function(){ - if (forceLoad){ - logging.warning("force load the settings. Calling stack:", (new Error()).stack); - undoPreIntercept(); - settings.forceLoad(); - doRealIntercept(); - const descriptor = Object.getOwnPropertyDescriptor(object, name); - return descriptor.value || descriptor.get.call(this); - } - else { - logging.notice("API blocked (%s)", name); - const url = getURL(window); - if (!url){ - return undef; - } - const error = new Error(); - apis.notify({ - url, - errorStack: error.stack, - messageId: "preBlock", - timestamp: new Date(), - functionName: name, - dataURL: false - }); - return; - } - }, window), - set: exportFunction(function(){}, window) - } + originalPropertyDescriptor ); - }); - preIntercepted = true; - } - }; - const undoPreIntercept = function(){ - if (preIntercepted){ - preIntercepted = false; - forEachFunction(function({name, object}){ - const originalPropertyDescriptor = originalPropertyDescriptors[name].get(object); - if (originalPropertyDescriptor){ - Object.defineProperty( - object, - name, - originalPropertyDescriptor - ); - } - }); - } - }; - const doRealIntercept = function(){ - if (!intercepted){ - scope.intercept({subject: window}, apis); - intercepted = true; + } + }); + } + }; + if (!state.preIntercepted){ + forEach(windowToProcess, function({name, object}){ + const map = originalPropertyDescriptors[name] || new WeakMap(); + originalPropertyDescriptors[name] = map; + + const originalPropertyDescriptor = Object.getOwnPropertyDescriptor(object, name); + if (!originalPropertyDescriptor){ + return; } + + map.set(object, originalPropertyDescriptor); + Object.defineProperty( + object, + name, + { + enumerable: true, + configurable: true, + get: exportFunction(function(){ + if (forceLoad){ + logging.warning("force load the settings. Calling stack:", (new Error()).stack); + undoPreIntercept(); + settings.forceLoad(); + doRealIntercept(windowToProcess, apis, state); + const descriptor = Object.getOwnPropertyDescriptor(object, name); + return descriptor.value || descriptor.get.call(this); + } + else { + logging.notice("API blocked (%s)", name); + const url = getURL(windowToProcess); + if (!url){ + return undef; + } + const error = new Error(); + apis.notify({ + url, + errorStack: error.stack, + messageId: "preBlock", + timestamp: new Date(), + functionName: name, + dataURL: false + }); + return; + } + }, windowToProcess), + set: exportFunction(function(){}, windowToProcess) + } + ); + }); + state.preIntercepted = true; + } + return undoPreIntercept; + }; + + scope.preIntercept = function preIntercept({subject: windowToProcess}, apis){ + if (!settings.isStillDefault){ + logging.message("settings already loaded -> no need to pre intercept"); + scope.intercept({subject: windowToProcess}, apis); + } + else { + logging.message("settings not loaded -> need to pre intercept"); + + const state = { + preIntercepted: false, + intercepted: false }; - doPreIntercept(); + const undoPreIntercept = doPreIntercept(windowToProcess, apis, state); settings.onloaded(function(){ undoPreIntercept(); - doRealIntercept(); + doRealIntercept(windowToProcess, apis, state); }); } }; + + function getDataURL(object, prefs){ + return ( + object && + prefs("storeImageForInspection") && + prefs("showNotifications")? + ( + object instanceof HTMLCanvasElement? + object.toDataURL(): + ( + object.canvas instanceof HTMLCanvasElement? + object.canvas.toDataURL(): + false + ) + ): + false + ); + } let extensionID = extension.extensionID; - scope.intercept = function intercept({subject: window}, {check, checkStack, ask, notify, prefs}){ - function getDataURL(object, prefs){ - return ( - object && - prefs("storeImageForInspection") && - prefs("showNotifications")? - ( - object instanceof HTMLCanvasElement? - object.toDataURL(): - ( - object.canvas instanceof HTMLCanvasElement? - object.canvas.toDataURL(): - false - ) - ): - false - ); - } - function generateChecker(name, changedFunction, siteStatus, original){ - return function checker(callingDepth = 2){ - const error = new Error(); - const errorStack = error.stack; - - try { - // return original if the extension itself requested the function - if ( - errorStack - .split("\n", callingDepth + 2)[callingDepth + 1] - .split("@", callingDepth + 1)[1] - .startsWith(extensionID) - ){ - return {allow: true, original, window}; - } + + function generateChecker({ + name, changedFunction, siteStatus, original, + window: windowToProcess, prefs, notify, checkStack, ask + }){ + return function checker(callingDepth = 2){ + const errorStack = (new Error()).stack; + + try { + // return original if the extension itself requested the function + if ( + errorStack + .split("\n", callingDepth + 2)[callingDepth + 1] + .split("@", callingDepth + 1)[1] + .startsWith(extensionID) + ){ + return {allow: true, original, window: windowToProcess}; } - catch (e) { - // stack had an unknown form - } - if (checkStack(errorStack)){ - return {allow: true, original, window}; - } - const funcStatus = changedFunction.getStatus(this, siteStatus, prefs); - - const This = this; - function notifyCallback(messageId){ - notify({ - url: getURL(window), - errorStack, - messageId, - timestamp: new Date(), - functionName: name, + } + catch (error) { + // stack had an unknown form + } + if (checkStack(errorStack)){ + return {allow: true, original, window: windowToProcess}; + } + const funcStatus = changedFunction.getStatus(this, siteStatus, prefs); + + const This = this; + function notifyCallback(messageId){ + notify({ + url: getURL(windowToProcess), + errorStack, + messageId, + timestamp: new Date(), + functionName: name, + api: changedFunction.api, + dataURL: getDataURL(This, prefs) + }); + } + const protectedAPIFeatures = prefs("protectedAPIFeatures"); + if ( + funcStatus.active && + ( + !protectedAPIFeatures.hasOwnProperty(name + " @ " + changedFunction.api) || + protectedAPIFeatures[name + " @ " + changedFunction.api] + ) + ){ + if (funcStatus.mode === "ask"){ + funcStatus.mode = ask({ + window: windowToProcess, + type: changedFunction.type, api: changedFunction.api, - dataURL: getDataURL(This, prefs) + canvas: this instanceof HTMLCanvasElement? + this: + ( + this && + (this.canvas instanceof HTMLCanvasElement)? + this.canvas: + false + ), + errorStack }); } - const protectedAPIFeatures = prefs("protectedAPIFeatures"); - if ( - funcStatus.active && - ( - !protectedAPIFeatures.hasOwnProperty(name + " @ " + changedFunction.api) || - protectedAPIFeatures[name + " @ " + changedFunction.api] - ) - ){ - if (funcStatus.mode === "ask"){ - funcStatus.mode = ask({ - window: window, - type: changedFunction.type, - api: changedFunction.api, - canvas: this instanceof HTMLCanvasElement? - this: - ( - this && - (this.canvas instanceof HTMLCanvasElement)? - this.canvas: - false - ), - errorStack + switch (funcStatus.mode){ + case "allow": + return {allow: true, original, window: windowToProcess}; + case "fake": + return { + allow: "fake", + prefs, + notify: notifyCallback, + window: windowToProcess, + original + }; + //case "block": + default: + return {allow: false, notify: notifyCallback}; + } + } + else { + return {allow: true, original, window: windowToProcess}; + } + }; + } + + function interceptFunctions(windowToProcess, siteStatus, {checkStack, ask, notify, prefs}){ + apiNames.forEach(function(name){ + const changedFunction = changedFunctions[name]; + const functionStatus = changedFunction.getStatus(undefined, siteStatus, prefs); + logging.verbose("status for", name, ":", functionStatus); + if (functionStatus.active){ + getAllFunctionObjects(windowToProcess, changedFunction).forEach(function(object){ + if (object){ + const original = object[name]; + const checker = generateChecker({ + name, changedFunction, siteStatus, original, + window: windowToProcess, prefs, checkStack, ask, notify }); - } - switch (funcStatus.mode){ - case "allow": - return {allow: true, original, window}; - case "fake": - return { - allow: "fake", - prefs, - notify: notifyCallback, - window, - original - }; - //case "block": - default: - return { - allow: false, - notify: notifyCallback - }; - } - } - else { - return {allow: true, original, window}; - } - }; - } - - const siteStatus = check({url: getURL(window)}); - logging.verbose("status for page", window, siteStatus); - if (siteStatus.mode !== "allow"){ - apiNames.forEach(function(name){ - const changedFunction = changedFunctions[name]; - const functionStatus = changedFunction.getStatus(undefined, siteStatus, prefs); - logging.verbose("status for", name, ":", functionStatus); - if (functionStatus.active){ - ( - Array.isArray(changedFunction.object)? - changedFunction.object: - [changedFunction.object] - ).map(function(name){ - if (name){ - const constructor = getWrapped(window)[name]; - if (constructor){ - return constructor.prototype; - } - } - return false; - }).concat( - changedFunction.objectGetters? - changedFunction.objectGetters.map(function(objectGetter){ - return objectGetter(getWrapped(window)); - }): - [] - ).forEach(function(object){ - if (object){ - const original = object[name]; - const checker = generateChecker(name, changedFunction, siteStatus, original); - const descriptor = Object.getOwnPropertyDescriptor(object, name); - if (descriptor){ - if (descriptor.hasOwnProperty("value")){ - if (changedFunction.fakeGenerator){ - descriptor.value = exportFunction( - changedFunction.fakeGenerator(checker, original, window), - window - ); - } - else { - descriptor.value = null; - } + const descriptor = Object.getOwnPropertyDescriptor(object, name); + if (descriptor){ + if (descriptor.hasOwnProperty("value")){ + if (changedFunction.fakeGenerator){ + descriptor.value = exportFunction( + changedFunction.fakeGenerator(checker, original, windowToProcess), + windowToProcess + ); } else { - descriptor.get = exportFunction(function(){ - return exportFunction( - changedFunction.fakeGenerator(checker), - window - ); - }, window); - } - Object.defineProperty(object, name, descriptor); - } - } - }); - } - }); - changedGetters.forEach(function(changedGetter){ - const name = changedGetter.name; - const functionStatus = changedGetter.getStatus(undefined, siteStatus, prefs); - logging.verbose("status for", changedGetter, ":", functionStatus); - if (functionStatus.active){ - changedGetter.objectGetters.forEach(function(objectGetter){ - const object = objectGetter(getWrapped(window)); - if (object){ - const descriptor = Object.getOwnPropertyDescriptor(object, name); - if (descriptor && descriptor.hasOwnProperty("get")){ - const original = descriptor.get; - const checker = generateChecker(name, changedGetter, siteStatus, original); - const getter = changedGetter.getterGenerator(checker, original, window); - descriptor.get = exportFunction(getter, window); - - if (descriptor.hasOwnProperty("set") && changedGetter.setterGenerator){ - const setter = changedGetter.setterGenerator(window, descriptor.set, prefs); - descriptor.set = exportFunction(setter, window); - } - - Object.defineProperty(object, name, descriptor); - } - else if ( - changedGetter.valueGenerator && - descriptor && descriptor.hasOwnProperty("value") - ){ - const protectedAPIFeatures = prefs("protectedAPIFeatures"); - if ( - functionStatus.active && - ( - !protectedAPIFeatures.hasOwnProperty(name + " @ " + changedGetter.api) || - protectedAPIFeatures[name + " @ " + changedGetter.api] - ) - ){ - switch (functionStatus.mode){ - case "ask": case "block": case "fake": - descriptor.value = changedGetter.valueGenerator({ - mode: functionStatus.mode, - original: descriptor.value, - notify: function notifyCallback(messageId){ - notify({ - url: getURL(window), - errorStack: (new Error()).stack, - messageId, - timestamp: new Date(), - functionName: name, - api: changedGetter.api - }); - } - }); - Object.defineProperty(object, name, descriptor); - break; - } + descriptor.value = null; } } else { - logging.error("Try to fake non getter property:", changedGetter); + descriptor.get = exportFunction(function(){ + return exportFunction( + changedFunction.fakeGenerator(checker), + windowToProcess + ); + }, windowToProcess); + } + Object.defineProperty(object, name, descriptor); + } + } + }); + } + }); + } + function interceptGetters(windowToProcess, siteStatus, {checkStack, ask, notify, prefs}){ + changedGetters.forEach(function(changedGetter){ + const name = changedGetter.name; + const functionStatus = changedGetter.getStatus(undefined, siteStatus, prefs); + logging.verbose("status for", changedGetter, ":", functionStatus); + if (functionStatus.active){ + changedGetter.objectGetters.forEach(function(objectGetter){ + const object = objectGetter(getWrapped(windowToProcess)); + if (object){ + const descriptor = Object.getOwnPropertyDescriptor(object, name); + if (descriptor && descriptor.hasOwnProperty("get")){ + const original = descriptor.get; + const checker = generateChecker({ + name, changedFunction: changedGetter, siteStatus, original, + window: windowToProcess, prefs, checkStack, ask, notify + }); + const getter = changedGetter.getterGenerator(checker, original, windowToProcess); + descriptor.get = exportFunction(getter, windowToProcess); + + if (descriptor.hasOwnProperty("set") && changedGetter.setterGenerator){ + const setter = changedGetter.setterGenerator( + windowToProcess, + descriptor.set, + prefs + ); + descriptor.set = exportFunction(setter, windowToProcess); + } + + Object.defineProperty(object, name, descriptor); + } + else if ( + changedGetter.valueGenerator && + descriptor && descriptor.hasOwnProperty("value") + ){ + const protectedAPIFeatures = prefs("protectedAPIFeatures"); + if ( + functionStatus.active && + ( + !protectedAPIFeatures.hasOwnProperty(name + " @ " + changedGetter.api) || + protectedAPIFeatures[name + " @ " + changedGetter.api] + ) + ){ + switch (functionStatus.mode){ + case "ask": case "block": case "fake": + descriptor.value = changedGetter.valueGenerator({ + mode: functionStatus.mode, + original: descriptor.value, + notify: function notifyCallback(messageId){ + notify({ + url: getURL(windowToProcess), + errorStack: (new Error()).stack, + messageId, + timestamp: new Date(), + functionName: name, + api: changedGetter.api + }); + } + }); + Object.defineProperty(object, name, descriptor); + break; + } } } - }); - } - }); + else { + logging.error("Try to fake non getter property:", changedGetter); + } + } + }); + } + }); + } + scope.intercept = function intercept({subject: windowToProcess}, apis){ + const siteStatus = apis.check({url: getURL(windowToProcess)}); + logging.verbose("status for page", windowToProcess, siteStatus); + if (siteStatus.mode !== "allow"){ + interceptFunctions(windowToProcess, siteStatus, apis); + interceptGetters(windowToProcess, siteStatus, apis); } }; }()); \ No newline at end of file diff --git a/lib/lists.js b/lib/lists.js index 2e263c5..f633776 100644 --- a/lib/lists.js +++ b/lib/lists.js @@ -6,7 +6,7 @@ "use strict"; - var scope; + let scope; if ((typeof exports) !== "undefined"){ scope = exports; } @@ -14,11 +14,11 @@ scope = require.register("./lists", {}); } - var settings = require("./settings"); + const settings = require("./settings"); function getDomainRegExpList(domainList){ - var list = domainList + const list = domainList .split(",") .map(function(entry){ return entry.replace(/^\s+|\s+$/g, ""); @@ -27,8 +27,8 @@ return !!entry.length; }) .map(function(entry){ - var regExp; - var domain = !!entry.match(/^[A-Za-z0-9_.-]+$/); + let regExp; + const domain = !!entry.match(/^[A-Za-z0-9_.-]+$/); if (domain){ regExp = new RegExp("(?:^|\\.)" + entry.replace(/([\\+*?[^\]$(){}=!|.])/g, "\\$1") + "\\.?$", "i"); } @@ -59,7 +59,7 @@ return list; } - var lists = { + const lists = { white: [], sessionWhite: [], "ignore": [], @@ -81,9 +81,9 @@ }); function updateStackList(value){ - var list; + let list; try { - var data = JSON.parse(value); + let data = JSON.parse(value); if (!Array.isArray(data)){ data = [data]; } @@ -91,7 +91,7 @@ return typeof entry === "object" && typeof entry.url === "string"; }); } - catch(e){ + catch(error){ list = []; } list.match = function(stack){ @@ -109,13 +109,13 @@ scope.get = function getList(type){ if (type === "white"){ - var combined = lists.white.slice().concat(lists.sessionWhite); + const combined = lists.white.slice().concat(lists.sessionWhite); return addMatchToList(combined); } return lists[type]; }; scope.appendTo = function appendToList(type, entry){ - var oldValue = settings[type + "List"]; + const oldValue = settings[type + "List"]; return settings.set(type + "List", oldValue + (oldValue? ",": "") + entry).then(function(){ return updateList(type); }); diff --git a/lib/logging.js b/lib/logging.js index c45be07..ac98d44 100644 --- a/lib/logging.js +++ b/lib/logging.js @@ -1,11 +1,10 @@ -/* eslint no-console: off */ /* 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/. */ (function(){ "use strict"; - var scope; + let scope; if ((typeof exports) !== "undefined"){ scope = exports; } @@ -17,21 +16,21 @@ scope.setSettings = function(realSettings){ if (!settings){ settings = realSettings; - settings.loaded.then(scope.clearQueue); + return settings.loaded.then(scope.clearQueue); } else { warning("logging: Settings can only be set once."); } }; - var prefix = ""; + let prefix = ""; function leftPad(str, char, pad){ str = "" + str; return char.repeat(pad - str.length) + str; } - var colors = { + const colors = { 1: "color: red", 25: "color: orange", 50: "", @@ -40,7 +39,7 @@ 999: "background-color: lightgray" }; - var queue = []; + let queue = []; function performLog(level, args, date){ if (!date){ date = new Date(); @@ -50,7 +49,7 @@ } else { if (settings.logLevel >= level){ - var pre = "%c[CanvasBlocker] "; + let pre = "%c[CanvasBlocker] "; if (prefix){ pre += prefix + ": "; } @@ -71,6 +70,7 @@ args.unshift(colors[level] || ""); args.unshift(pre); } + // eslint-disable-next-line no-console console.log.apply(console, args); } } @@ -94,7 +94,7 @@ scope.clearQueue = function(){ if (queue.length){ metaLog("clear logging queue"); - var tmp = queue; + const tmp = queue; queue = []; tmp.forEach(function(item){ performLog(item.level, item.args, item.date); diff --git a/lib/main.js b/lib/main.js index 2809271..b4ae4e5 100644 --- a/lib/main.js +++ b/lib/main.js @@ -6,25 +6,81 @@ const settings = require("./settings"); const logging = require("./logging"); - const {error, warning, message, notice, verbose, } = logging; logging.setPrefix("main script"); const persistentRndStorage = require("./persistentRndStorage"); const notification = require("./notification"); - message("start of background script"); - message("waiting for settings to be loaded"); + const registerSettingsContentScript = (function(){ + let unregisterSettingsContentScript = function(){}; + let lastRegistering; + return function registerSettingsContentScript(){ + logging.message("Register content script for the settings."); + logging.verbose("Unregister old content script, if present."); + unregisterSettingsContentScript(); + const data = {}; + settings.forEach(function(def){ + data[def.name] = def.get(); + }); + lastRegistering = data; + browser.contentScripts.register({ + matches: [""], + matchAboutBlank: true, + allFrames: true, + runAt: "document_start", + js: [{ + code: `(function(settingsData){ + if (typeof require !== "undefined"){ + const settings = require("./settings"); + const logging = require("./logging"); + if (settings.init(settingsData)){ + logging.message("Initialized settings by dynamic content script."); + } + else { + logging.warning("Dynamic content script was too late to provide settings."); + } + } + else { + if (!window.scope){ + window.scope = {}; + } + window.scope.settingsData = settingsData; + console.warn( + "[CanvasBlocker] invalid content script order: require not defined at", + window.location.href + ); + } + }(${JSON.stringify(data)}))` + }] + }).then(function(api){ + logging.verbose("Content script registered."); + if (data !== lastRegistering){ + logging.verbose("Multiple content scripts registered at once. Remove unnecessary one."); + api.unregister(); + } + else { + unregisterSettingsContentScript = api.unregister; + } + return; + }).catch(function(error){ + logging.warning("Unable to register content script:", error); + }); + }; + }()); + + logging.message("start of background script"); + logging.message("waiting for settings to be loaded"); settings.onloaded(function(){ - notice("everything loaded"); + logging.notice("everything loaded"); - message("perform startup reset"); + logging.message("perform startup reset"); settings.startupReset(); persistentRndStorage.init(); - message("register non port message listener"); + logging.message("register non port message listener"); browser.runtime.onMessage.addListener(function(data){ - notice("got data without port", data); - var keys = Object.keys(data); + logging.notice("got data without port", data); + const keys = Object.keys(data); if (data["canvasBlocker-new-domain-rnd"]){ persistentRndStorage.setDomainData( data["canvasBlocker-new-domain-rnd"].domain, @@ -47,31 +103,34 @@ return; } } - notice("pass the message to the tabs"); + logging.notice("pass the message to the tabs"); browser.tabs.query({}).then(function(tabs){ tabs.forEach(function(tab){ browser.tabs.sendMessage(tab.id, data); }); + return; + }).catch(function(error){ + logging.warning("Unable to get browser tabs:", error); }); }); - message("register port listener"); + logging.message("register port listener"); browser.runtime.onConnect.addListener(function(port){ - notice("got port", port); + logging.notice("got port", port); if (!port.sender.tab){ - notice("got port without tab = Firefox bug:", port); + logging.notice("got port without tab = Firefox bug:", port); return; } - verbose("send back the tab id", port.sender.tab.id); - verbose("send back the tab cookie store id", port.sender.tab.cookieStoreId); - verbose("send back the persistent random seeds", persistentRndStorage.persistentRnd); + logging.verbose("send back the tab id", port.sender.tab.id); + logging.verbose("send back the tab cookie store id", port.sender.tab.cookieStoreId); + logging.verbose("send back the persistent random seeds", persistentRndStorage.persistentRnd); port.postMessage({ tabId: port.sender.tab.id, cookieStoreId: port.sender.tab.cookieStoreId, persistentRnd: persistentRndStorage.persistentRnd, persistentIncognitoRnd: persistentRndStorage.persistentIncognitoRnd }); - var url = new URL(port.sender.url); + const url = new URL(port.sender.url); port.onMessage.addListener(function(data){ if (data.hasOwnProperty("canvasBlocker-notify")){ notification.show(port.sender.tab.id, url, data["canvasBlocker-notify"].api); @@ -79,76 +138,25 @@ if (data.hasOwnProperty("canvasBlocker-clear-page-action")){ notification.hide(port.sender.tab.id, url); } - verbose("got data", data, "from port", port); + logging.verbose("got data", data, "from port", port); }); }); - message("register storage change event listener"); + logging.message("register storage change event listener"); if (browser.contentScripts){ - let unregister = function(){}; - let lastRegistering; - const register = function register(){ - logging.message("Register content script for the settings."); - logging.verbose("Unregister old content script, if present."); - unregister(); - var data = {}; - settings.forEach(function(def){ - data[def.name] = def.get(); - }); - lastRegistering = data; - browser.contentScripts.register({ - matches: [""], - matchAboutBlank: true, - allFrames: true, - runAt: "document_start", - js: [{ - code: `(function(settingsData){ - if (typeof require !== "undefined"){ - const settings = require("./settings"); - const logging = require("./logging"); - if (settings.init(settingsData)){ - logging.message("Initialized settings by dynamic content script."); - } - else { - logging.warning("Dynamic content script was too late to provide settings."); - } - } - else { - if (!window.scope){ - window.scope = {}; - } - window.scope.settingsData = settingsData; - console.warn( - "[CanvasBlocker] invalid content script order: require not defined at", - window.location.href - ); - } - }(${JSON.stringify(data)}))` - }] - }).then(function(api){ - logging.verbose("Content script registered."); - if (data !== lastRegistering){ - logging.verbose("Multiple content scripts registered at once. Remove unnecessary one."); - api.unregister(); - } - else { - unregister = api.unregister; - } - }); - }; - register(); - settings.on("any", register); + registerSettingsContentScript(); + settings.on("any", registerSettingsContentScript); } else { logging.error("Old Firefox does not support browser.contentScript.register()"); } }); - message("Initialize data-URL workaround."); + logging.message("Initialize data-URL workaround."); require("./dataUrls").init(); - message("Initialize navigator HTTP header protection."); + logging.message("Initialize navigator HTTP header protection."); require("./navigator").init(); browser.runtime.onInstalled.addListener(function(details){ @@ -165,7 +173,7 @@ } switch (details.reason){ case "install": - message("CanvasBlocker installed"); + logging.message("CanvasBlocker installed"); openOptions(details.reason); browser.tabs.create({ url: browser.extension.getURL("options/presets.html?notice=" + details.reason) @@ -174,12 +182,12 @@ case "update": settings.onloaded(function(){ if (!settings.dontShowOptionsOnUpdate){ - message("CanvasBlocker updated"); + logging.message("CanvasBlocker updated"); openOptions(details.reason); } }); } }); - message("end"); + logging.message("end"); }()); diff --git a/lib/modal.js b/lib/modal.js index c011431..cd5a1c7 100644 --- a/lib/modal.js +++ b/lib/modal.js @@ -96,8 +96,7 @@ const parentTop = getGlobalOffsetTop(parent) - getGlobalScrollTop(parent); const parentHeight = parent.offsetHeight; const height = dialog.offsetHeight; - const top = Math.max( - 0, + const top = Math.max(0, Math.min( container.offsetHeight - height, parentTop + parentHeight / 2 - height / 2 diff --git a/lib/modifiedAPI.js b/lib/modifiedAPI.js index 329c46c..aa4a126 100644 --- a/lib/modifiedAPI.js +++ b/lib/modifiedAPI.js @@ -4,7 +4,7 @@ (function(){ "use strict"; - var scope; + let scope; if ((typeof exports) !== "undefined"){ scope = exports; } diff --git a/lib/modifiedAPIFunctions.js b/lib/modifiedAPIFunctions.js index edd5322..775cc05 100644 --- a/lib/modifiedAPIFunctions.js +++ b/lib/modifiedAPIFunctions.js @@ -4,7 +4,7 @@ (function(){ "use strict"; - var scope; + let scope; if ((typeof exports) !== "undefined"){ scope = exports; } @@ -32,7 +32,7 @@ scope.setFunctionProperties = function setFunctionProperties(functions, data){ Object.keys(functions).forEach(function(key){ - var func = functions[key]; + const func = functions[key]; ["type", "api", "getStatus"].forEach(function(property){ if (data[property] && !func[property]){ func[property] = data[property]; diff --git a/lib/modifiedAudioAPI.js b/lib/modifiedAudioAPI.js index 77a5509..ad48af2 100644 --- a/lib/modifiedAudioAPI.js +++ b/lib/modifiedAudioAPI.js @@ -4,7 +4,7 @@ (function(){ "use strict"; - var scope; + let scope; if ((typeof exports) !== "undefined"){ scope = exports; } @@ -12,25 +12,24 @@ scope = require.register("./modifiedAudioAPI", {}); } - const logging = require("./logging"); const {sha256String: hashing} = require("./hash"); const {checkerWrapper} = require("./modifiedAPIFunctions"); - var randomSupply = null; + let randomSupply = null; const getAudioFakeRate = function(){ const audioFakeRate = { - "1": function(array){return 1;}, - "10": function(array){return 10;}, - "100": function(array){return 100;}, - "1000": function(array){return 1000;}, + "1": function(){return 1;}, + "10": function(){return 10;}, + "100": function(){return 100;}, + "1000": function(){return 1000;}, "0.1%": function(array){return array.length / 1000;}, "1%": function(array){return array.length / 100;}, "10%": function(array){return array.length / 10;}, "100%": function(array){return array.length;}, }; return function getAudioFakeRate(array, prefs){ - var func = audioFakeRate[prefs("audioFakeRate")]; + const func = audioFakeRate[prefs("audioFakeRate")]; if (typeof func === "function"){ return func(array); } @@ -66,18 +65,18 @@ } function forEachIndex(array, prefs, callback){ - var length = array.length; - var rate = getAudioFakeRate(array, prefs); - var start = 0; + const length = array.length; + const rate = getAudioFakeRate(array, prefs); + let start = 0; forEachFixedIndex(prefs, function(index){ callback(index, start); start += 1; }); if (start < rate){ - var delta = Math.floor(length / (rate - start)); - var indexRng = randomSupply.getIndexRng(1, length - delta * (rate - start - 1), window); - var offset = indexRng(0); - for (var i = start; i < rate; i += 1){ + const delta = Math.floor(length / (rate - start)); + const indexRng = randomSupply.getIndexRng(1, length - delta * (rate - start - 1), window); + let offset = indexRng(0); + for (let i = start; i < rate; i += 1){ callback(offset, i); offset += delta; } @@ -88,7 +87,7 @@ const intCache = Object.create(null); function arrayHasAnyNonZero(array){ - for (var i = 0, l = array.length; i < l; i += 1){ + for (let i = 0, l = array.length; i < l; i += 1){ if (array[i]){ return true; } @@ -105,9 +104,9 @@ cached = floatCache[hash]; } if (!cached){ - var rate = getAudioFakeRate(array, prefs); - var noiseLevel = getAudioNoiseLevel(prefs); - var rng = randomSupply.getRng(rate, window); + const rate = getAudioFakeRate(array, prefs); + const noiseLevel = getAudioNoiseLevel(prefs); + const rng = randomSupply.getRng(rate, window); forEachIndex(array, prefs, function(index, i){ let value; if (array[index] !== 0){ @@ -137,8 +136,8 @@ cached = intCache[hash]; } if (!cached){ - var rate = getAudioFakeRate(array, prefs); - var rng = randomSupply.getValueRng(rate, window); + const rate = getAudioFakeRate(array, prefs); + const rng = randomSupply.getValueRng(rate, window); forEachIndex(array, prefs, function(index, i){ array[index] = rng(array[index], i); }); @@ -171,9 +170,9 @@ fakeGenerator: function(checker){ return function getFloatFrequencyData(array){ return checkerWrapper(checker, this, arguments, function(args, check){ - var {prefs, notify, window, original} = check; + const {prefs, notify, window, original} = check; notify("fakedAudioReadout"); - var ret = original.apply(this, window.Array.from(args)); + const ret = original.apply(this, window.Array.from(args)); fakeFloat32Array(array, window, prefs); return ret; }); @@ -185,9 +184,9 @@ fakeGenerator: function(checker){ return function getByteFrequencyData(array){ return checkerWrapper(checker, this, arguments, function(args, check){ - var {prefs, notify, window, original} = check; + const {prefs, notify, window, original} = check; notify("fakedAudioReadout"); - var ret = original.apply(this, window.Array.from(args)); + const ret = original.apply(this, window.Array.from(args)); fakeUint8Array(array, window, prefs); return ret; }); @@ -199,9 +198,9 @@ fakeGenerator: function(checker){ return function getFloatTimeDomainData(array){ return checkerWrapper(checker, this, arguments, function(args, check){ - var {prefs, notify, window, original} = check; + const {prefs, notify, window, original} = check; notify("fakedAudioReadout"); - var ret = original.apply(this, window.Array.from(args)); + const ret = original.apply(this, window.Array.from(args)); fakeFloat32Array(array, window, prefs); return ret; }); @@ -213,9 +212,9 @@ fakeGenerator: function(checker){ return function getByteTimeDomainData(array){ return checkerWrapper(checker, this, arguments, function(args, check){ - var {prefs, notify, window, original} = check; + const {prefs, notify, window, original} = check; notify("fakedAudioReadout"); - var ret = original.apply(this, window.Array.from(args)); + const ret = original.apply(this, window.Array.from(args)); fakeUint8Array(array, window, prefs); return ret; }); @@ -225,10 +224,11 @@ getChannelData: { object: ["AudioBuffer"], fakeGenerator: function(checker){ + // eslint-disable-next-line no-unused-vars return function getChannelData(channel){ return checkerWrapper(checker, this, arguments, function(args, check){ - var {prefs, notify, window, original} = check; - var ret = original.apply(this, window.Array.from(args)); + const {prefs, notify, window, original} = check; + const ret = original.apply(this, window.Array.from(args)); if (!getChannelDataAlreadyFakedArrays.get(ret)){ notify("fakedAudioReadout"); fakeFloat32Array(ret, window, prefs); @@ -242,16 +242,17 @@ copyFromChannel: { object: ["AudioBuffer"], fakeGenerator: function(checker){ + // eslint-disable-next-line no-unused-vars return function copyFromChannel(destination, channelNumber, startInChannel){ return checkerWrapper(checker, this, arguments, function(args, check){ - var {prefs, notify, window, original} = check; - var channelData = this.getChannelData(channelNumber); + const {prefs, notify, window, original} = check; + const channelData = this.getChannelData(channelNumber); if (!getChannelDataAlreadyFakedArrays.get(channelData)){ notify("fakedAudioReadout"); fakeFloat32Array(channelData, window, prefs); getChannelDataAlreadyFakedArrays.set(channelData, true); } - var ret = original.apply(this, window.Array.from(args)); + const ret = original.apply(this, window.Array.from(args)); return ret; }); }; @@ -260,11 +261,12 @@ getFrequencyResponse: { object: ["BiquadFilterNode", "IIRFilterNode"], fakeGenerator: function(checker){ + // eslint-disable-next-line no-unused-vars return function getFrequencyResponse(frequencyArray, magResponseOutput, phaseResponseOutput){ return checkerWrapper(checker, this, arguments, function(args, check){ - var {prefs, notify, window, original} = check; + const {prefs, notify, window, original} = check; notify("fakedAudioReadout"); - var ret = original.apply(this, window.Array.from(args)); + const ret = original.apply(this, window.Array.from(args)); fakeFloat32Array(magResponseOutput, window, prefs); fakeFloat32Array(phaseResponseOutput, window, prefs); return ret; diff --git a/lib/modifiedCanvasAPI.js b/lib/modifiedCanvasAPI.js index 8a9e153..f18c709 100644 --- a/lib/modifiedCanvasAPI.js +++ b/lib/modifiedCanvasAPI.js @@ -10,7 +10,7 @@ const {copyCanvasToWebgl} = require("./webgl"); const {getWrapped, checkerWrapper} = require("./modifiedAPIFunctions"); - var randomSupply = null; + let randomSupply = null; function getContext(window, canvas){ return window.HTMLCanvasElement.prototype.getContext.call(canvas, "2d") || @@ -20,8 +20,8 @@ window.HTMLCanvasElement.prototype.getContext.call(canvas, "experimental-webgl2"); } function getImageData(window, context){ - var imageData; - var source; + let imageData; + let source; if ((context.canvas.width || 0) * (context.canvas.height || 0) === 0){ imageData = new (getWrapped(window).ImageData)(0, 0); source = new (getWrapped(window).ImageData)(0, 0); @@ -54,24 +54,25 @@ }; } - var canvasCache = Object.create(null); + const canvasCache = Object.create(null); function getFakeCanvas(window, original, prefs){ try { + let originalDataURL; if (prefs("useCanvasCache")){ - var originalDataURL = original.toDataURL(); - var cached = canvasCache[originalDataURL]; + originalDataURL = original.toDataURL(); + const cached = canvasCache[originalDataURL]; if (cached){ return cached; } } // original may not be a canvas -> we must not leak an error - var context = getContext(window, original); - var {imageData, source} = getImageData(window, context); - var desc = imageData.data; - var l = desc.length; + let context = getContext(window, original); + const {imageData, source} = getImageData(window, context); + const desc = imageData.data; + const l = desc.length; - var ignoredColors = {}; - var statistic; + let ignoredColors = {}; + let statistic; if (prefs("ignoreFrequentColors")){ statistic = colorStatistics.compute(source); ignoredColors = statistic.getMaxColors(prefs("ignoreFrequentColors")); @@ -82,10 +83,10 @@ } } - var rng = randomSupply.getPixelRng(l, window, ignoredColors); - var fakeAlphaChannel = prefs("fakeAlphaChannel"); - for (var i = 0; i < l; i += 4){ - var [r, g, b, a] = rng( + const rng = randomSupply.getPixelRng(l, window, ignoredColors); + const fakeAlphaChannel = prefs("fakeAlphaChannel"); + for (let i = 0; i < l; i += 4){ + const [r, g, b, a] = rng( source[i + 0], source[i + 1], source[i + 2], @@ -97,7 +98,7 @@ desc[i + 2] = b; desc[i + 3] = fakeAlphaChannel? a: source[i + 3]; } - var canvas = original.cloneNode(true); + const canvas = original.cloneNode(true); context = window.HTMLCanvasElement.prototype.getContext.call(canvas, "2d"); context.putImageData(imageData, 0, 0); if (prefs("useCanvasCache")){ @@ -106,25 +107,25 @@ } return canvas; } - catch (e){ - logging.warning("Error while faking:", e); + catch (error){ + logging.warning("Error while faking:", error); return original; } } function randomMixImageData(window, imageData1, imageData2){ - var data1 = imageData1.data; - var data2 = imageData2.data; - var l = data1.length; + const data1 = imageData1.data; + const data2 = imageData2.data; + const l = data1.length; if (l === data2.length){ - var rng = randomSupply.getPixelRng(l, window, {}); + const rng = randomSupply.getPixelRng(l, window, {}); - for (var i = 0; i < l; i += 4){ + for (let i = 0; i < l; i += 4){ const signR = data1[i + 0] > data2[i + 0]? -1: 1; const signG = data1[i + 1] > data2[i + 1]? -1: 1; const signB = data1[i + 2] > data2[i + 2]? -1: 1; const signA = data1[i + 3] > data2[i + 3]? -1: 1; - var [deltaR, deltaG, deltaB, deltaA] = rng( + const [deltaR, deltaG, deltaB, deltaA] = rng( signR * (data2[i + 0] - data1[i + 0]), signG * (data2[i + 1] - data1[i + 1]), signB * (data2[i + 2] - data1[i + 2]), @@ -142,9 +143,9 @@ function canvasSizeShouldBeFaked(canvas, prefs){ if (canvas){ - var size = canvas.height * canvas.width; - var maxSize = prefs("maxFakeSize") || Number.POSITIVE_INFINITY; - var minSize = prefs("minFakeSize") || 0; + const size = canvas.height * canvas.width; + const maxSize = prefs("maxFakeSize") || Number.POSITIVE_INFINITY; + const minSize = prefs("minFakeSize") || 0; return size > minSize && size <= maxSize; } else { @@ -181,7 +182,98 @@ scope.setRandomSupply = function(supply){ randomSupply = supply; }; - var canvasContextType = new WeakMap(); + + + function getNumber(originalValue, max, index, window){ + const bitLength = Math.floor(Math.log2(max) + 1); + const rng = randomSupply.getBitRng(bitLength, window); + let value = 0; + for (let i = 0; i < bitLength; i += 1){ + value <<= 1; + value ^= rng(originalValue, index + i); + } + return value; + } + + const parameterFakeTypes = { + decimal: function(originalValue, definition, window){ + const int = Math.floor(originalValue); + if (int !== originalValue){ + const decimal = originalValue - int; + const rng = randomSupply.getRng(1, window); + const newDecimal = decimal * (rng(definition.pname) / 0xFFFFFFFF); + return int + newDecimal; + } + else { + return originalValue; + } + }, + shift: function(originalValue, definition, window){ + const value = getNumber(originalValue, definition.max, definition.pname, window); + return originalValue >>> value; + }, + "-": function(originalValue, definition, window){ + const value = getNumber(originalValue, definition.max, definition.pname, window) * + (definition.factor || 1); + if (value > originalValue){ + return 0; + } + return originalValue - value; + } + }; + const parameterChangeDefinition = { + 2928: {name: "DEPTH_RANGE", type: "decimal", isArray: true}, + 3379: {name: "MAX_TEXTURE_SIZE", type: "shift", max: 1}, + 3386: {name: "MAX_VIEWPORT_DIMS", type: "shift", max: 1, isArray: true}, + 32883: {name: "MAX_3D_TEXTURE_SIZE", type: "shift", max: 1}, + 33000: {name: "MAX_ELEMENTS_VERTICES", type: "-", max: 3, factor: 50}, + 33001: {name: "MAX_ELEMENTS_INDICES", type: "-", max: 3, factor: 50}, + 33901: {name: "ALIASED_POINT_SIZE_RANGE", type: "decimal", isArray: true}, + 33902: {name: "ALIASED_LINE_WIDTH_RANGE", type: "decimal", isArray: true}, + 34024: {name: "MAX_RENDERBUFFER_SIZE", type: "shift", max: 1}, + 34045: {name: "MAX_TEXTURE_LOD_BIAS", type: "-", max: 1, factor: 1}, + 34076: {name: "MAX_CUBE_MAP_TEXTURE_SIZE", type: "shift", max: 1}, + 34921: {name: "MAX_VERTEX_ATTRIBS", type: "shift", max: 1}, + 34930: {name: "MAX_TEXTURE_IMAGE_UNITS", type: "shift", max: 1}, + 35071: {name: "MAX_ARRAY_TEXTURE_LAYERS", type: "shift", max: 1}, + 35371: {name: "MAX_VERTEX_UNIFORM_BLOCKS", type: "-", max: 1, factor: 1}, + 35373: {name: "MAX_FRAGMENT_UNIFORM_BLOCKS", type: "-", max: 1, factor: 1}, + 35374: {name: "MAX_COMBINED_UNIFORM_BLOCKS", type: "-", max: 3, factor: 1}, + 35375: {name: "MAX_UNIFORM_BUFFER_BINDINGS", type: "-", max: 3, factor: 1}, + 35376: {name: "MAX_UNIFORM_BLOCK_SIZE", type: "shift", max: 1}, + 35377: {name: "MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS", type: "-", max: 7, factor: 10}, + 35379: {name: "MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS", type: "-", max: 7, factor: 10}, + 35657: {name: "MAX_FRAGMENT_UNIFORM_COMPONENTS", type: "shift", max: 1}, + 35658: {name: "MAX_VERTEX_UNIFORM_COMPONENTS", type: "shift", max: 1}, + 35659: {name: "MAX_VARYING_COMPONENTS", type: "shift", max: 1}, + 35660: {name: "MAX_VERTEX_TEXTURE_IMAGE_UNITS", type: "shift", max: 1}, + 35661: {name: "MAX_COMBINED_TEXTURE_IMAGE_UNITS", type: "-", max: 1, factor: 2}, + 35968: {name: "MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS", type: "shift", max: 1}, + 35978: {name: "MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS", type: "shift", max: 1}, + 36203: {name: "MAX_ELEMENT_INDEX", type: "-", max: 15, factor: 1}, + 36347: {name: "MAX_VERTEX_UNIFORM_VECTORS", type: "shift", max: 1}, + 36348: {name: "MAX_VARYING_VECTORS", type: "shift", max: 1}, + 36349: {name: "MAX_FRAGMENT_UNIFORM_VECTORS", type: "shift", max: 1}, + 37154: {name: "MAX_VERTEX_OUTPUT_COMPONENTS", type: "shift", max: 1}, + 37157: {name: "MAX_FRAGMENT_INPUT_COMPONENTS", type: "shift", max: 1}, + 7936: {name: "VENDOR", fake: function(originalValue, window, prefs){ + const settingValue = prefs("webGLVendor") || originalValue; + return {value: settingValue, faked: settingValue === originalValue}; + }}, + 7937: {name: "RENDERER", fake: function(originalValue, window, prefs){ + const settingValue = prefs("webGLRenderer") || originalValue; + return {value: settingValue, faked: settingValue === originalValue}; + }}, + 37445: {name: "UNMASKED_VENDOR_WEBGL", fake: function(originalValue, window, prefs){ + const settingValue = prefs("webGLUnmaskedVendor") || originalValue; + return {value: settingValue, faked: settingValue === originalValue}; + }}, + 37446: {name: "UNMASKED_RENDERER_WEBGL", fake: function(originalValue, window, prefs){ + const settingValue = prefs("webGLUnmaskedRenderer") || originalValue; + return {value: settingValue, faked: settingValue === originalValue}; + }} + }; + const canvasContextType = new WeakMap(); // changed functions and their fakes scope.changedFunctions = { getContext: { @@ -209,9 +301,10 @@ }, object: "HTMLCanvasElement", fakeGenerator: function(checker){ + // eslint-disable-next-line no-unused-vars return function(context, contextAttributes){ return checkerWrapper(checker, this, arguments, function(args, check){ - var {prefs, notify, window, original} = check; + const {window, original} = check; canvasContextType.set(this, context); return original.apply(this, window.Array.from(args)); }); @@ -225,7 +318,7 @@ status = Object.create(status); status.active = protectedPartChecker("readout"); if (!status.active && protectedPartChecker("input")){ - var contextType = canvasContextType.get(obj); + const contextType = canvasContextType.get(obj); status.active = contextType !== "2d"; } return status; @@ -234,9 +327,9 @@ fakeGenerator: function(checker){ return function toDataURL(){ return checkerWrapper(checker, this, arguments, function(args, check){ - var {prefs, notify, window, original} = check; + const {prefs, notify, window, original} = check; if (canvasSizeShouldBeFaked(this, prefs)){ - var fakeCanvas = getFakeCanvas(window, this, prefs); + const fakeCanvas = getFakeCanvas(window, this, prefs); if (fakeCanvas !== this){ notify("fakedReadout"); } @@ -256,18 +349,19 @@ status = Object.create(status); status.active = protectedPartChecker("readout"); if (!status.active && protectedPartChecker("input")){ - var contextType = canvasContextType.get(obj); + const contextType = canvasContextType.get(obj); status.active = contextType !== "2d"; } return status; }, object: "HTMLCanvasElement", fakeGenerator: function(checker){ + // eslint-disable-next-line no-unused-vars return function toBlob(callback){ return checkerWrapper(checker, this, arguments, function(args, check){ - var {prefs, notify, window, original} = check; + const {prefs, notify, window, original} = check; if (canvasSizeShouldBeFaked(this, prefs)){ - var fakeCanvas = getFakeCanvas(window, this, prefs); + const fakeCanvas = getFakeCanvas(window, this, prefs); if (fakeCanvas !== this){ notify("fakedReadout"); } @@ -288,18 +382,19 @@ status = Object.create(status); status.active = protectedPartChecker("readout"); if (!status.active && protectedPartChecker("input")){ - var contextType = canvasContextType.get(obj); + const contextType = canvasContextType.get(obj); status.active = contextType !== "2d"; } return status; }, object: "HTMLCanvasElement", fakeGenerator: function(checker){ + // eslint-disable-next-line no-unused-vars return function mozGetAsFile(callback){ return checkerWrapper(checker, this, arguments, function(args, check){ - var {prefs, notify, window, original} = check; + const {prefs, notify, window, original} = check; if (canvasSizeShouldBeFaked(this, prefs)){ - var fakeCanvas = getFakeCanvas(window, this, prefs); + const fakeCanvas = getFakeCanvas(window, this, prefs); if (fakeCanvas !== this){ notify("fakedReadout"); } @@ -322,12 +417,13 @@ }, object: "CanvasRenderingContext2D", fakeGenerator: function(checker){ + // eslint-disable-next-line no-unused-vars return function getImageData(sx, sy, sw, sh){ return checkerWrapper(checker, this, arguments, function(args, check){ - var {prefs, notify, window, original} = check; + const {prefs, notify, window, original} = check; if (!this || canvasSizeShouldBeFaked(this.canvas, prefs)){ - var fakeCanvas; - var context = this; + let fakeCanvas; + let context = this; if (this && this.canvas) { fakeCanvas = getFakeCanvas(window, this.canvas, prefs); } @@ -359,12 +455,12 @@ fakeGenerator: function(checker){ return function isPointInPath(x, y){ return checkerWrapper(checker, this, arguments, function(args, check){ - var {prefs, notify, window, original} = check; - var rng = randomSupply.getValueRng(1, window); - var originalValue = original.apply(this, window.Array.from(args)); + const {notify, window, original} = check; + const rng = randomSupply.getValueRng(1, window); + const originalValue = original.apply(this, window.Array.from(args)); if ((typeof originalValue) === "boolean"){ notify("fakedReadout"); - var index = x + this.width * y; + const index = x + this.width * y; return original.call(this, rng(x, index), rng(y, index), args[2]); } else { @@ -386,9 +482,9 @@ fakeGenerator: function(checker){ return function isPointInStroke(x, y){ return checkerWrapper(checker, this, arguments, function(args, check){ - var {prefs, notify, window, original} = check; - var rng = randomSupply.getValueRng(1, window); - var originalValue = original.apply(this, window.Array.from(args)); + const {notify, window, original} = check; + const rng = randomSupply.getValueRng(1, window); + const originalValue = original.apply(this, window.Array.from(args)); if ((typeof originalValue) === "boolean"){ notify("fakedReadout"); if (x instanceof window.Path2D){ @@ -420,22 +516,23 @@ }, object: "CanvasRenderingContext2D", fakeGenerator: function(checker){ + // eslint-disable-next-line no-unused-vars return function fillText(str, x, y){ return checkerWrapper(checker, this, arguments, function(args, check){ - var {prefs, notify, window, original} = check; + const {prefs, notify, window, original} = check; if (!this || canvasSizeShouldBeFaked(this.canvas, prefs)){ notify("fakedInput"); - var oldImageData; + let oldImageData; try { // "this" is not trustable - it may be not a context oldImageData = getImageData(window, this).imageData; } - catch (e){ + catch (error){ // nothing to do here } // if "this" is not a correct context the next line will throw an error - var ret = original.apply(this, window.Array.from(args)); - var newImageData = getImageData(window, this).imageData; + const ret = original.apply(this, window.Array.from(args)); + const newImageData = getImageData(window, this).imageData; this.putImageData(randomMixImageData(window, oldImageData, newImageData), 0, 0); return ret; } @@ -456,22 +553,23 @@ }, object: "CanvasRenderingContext2D", fakeGenerator: function(checker){ + // eslint-disable-next-line no-unused-vars return function strokeText(str, x, y){ return checkerWrapper(checker, this, arguments, function(args, check){ - var {prefs, notify, window, original} = check; + const {prefs, notify, window, original} = check; if (!this || canvasSizeShouldBeFaked(this.canvas, prefs)){ notify("fakedInput"); - var oldImageData; + let oldImageData; try { // "this" is not trustable - it may be not a context oldImageData = getImageData(window, this).imageData; } - catch (e){ + catch (error){ // nothing to do here } // if "this" is not a correct context the next line will throw an error - var ret = original.apply(this, window.Array.from(args)); - var newImageData = getImageData(window, this).imageData; + const ret = original.apply(this, window.Array.from(args)); + const newImageData = getImageData(window, this).imageData; this.putImageData(randomMixImageData(window, oldImageData, newImageData), 0, 0); return ret; } @@ -492,13 +590,14 @@ }, object: ["WebGLRenderingContext", "WebGL2RenderingContext"], fakeGenerator: function(checker){ - return function readPixels(x, y, width, height, format, type, pixels){ // eslint-disable-line max-params + // eslint-disable-next-line max-params, no-unused-vars + return function readPixels(x, y, width, height, format, type, pixels){ return checkerWrapper(checker, this, arguments, function(args, check){ - var {prefs, notify, window, original} = check; + const {prefs, notify, window, original} = check; if (!this || canvasSizeShouldBeFaked(this.canvas, prefs)){ notify("fakedReadout"); - var fakeCanvas = getFakeCanvas(window, this.canvas, prefs); - var {context} = copyCanvasToWebgl( + const fakeCanvas = getFakeCanvas(window, this.canvas, prefs); + const {context} = copyCanvasToWebgl( window, fakeCanvas, this instanceof window.WebGLRenderingContext? "webgl": "webgl2" @@ -522,111 +621,22 @@ }, object: ["WebGLRenderingContext", "WebGL2RenderingContext"], fakeGenerator: function(checker){ - function getNumber(originalValue, max, index, window){ - const bitLength = Math.floor(Math.log2(max) + 1); - const rng = randomSupply.getBitRng(bitLength, window); - let value = 0; - for (let i = 0; i < bitLength; i += 1){ - value <<= 1; - value ^= rng(originalValue, index + i); - } - return value; - } - const types = { - decimal: function(originalValue, definition, window){ - const int = Math.floor(originalValue); - if (int !== originalValue){ - const decimal = originalValue - int; - const rng = randomSupply.getRng(1, window); - const newDecimal = decimal * (rng(definition.pname) / 0xFFFFFFFF); - return int + newDecimal; - } - else { - return originalValue; - } - }, - shift: function(originalValue, definition, window){ - const value = getNumber(originalValue, definition.max, definition.pname, window); - return originalValue >>> value; - }, - "-": function(originalValue, definition, window){ - const value = getNumber(originalValue, definition.max, definition.pname, window) * - (definition.factor || 1); - if (value > originalValue){ - return 0; - } - return originalValue - value; - } - }; - const changeDefinition = { - 2928: {name: "DEPTH_RANGE", type: "decimal", isArray: true}, - 3379: {name: "MAX_TEXTURE_SIZE", type: "shift", max: 1}, - 3386: {name: "MAX_VIEWPORT_DIMS", type: "shift", max: 1, isArray: true}, - 32883: {name: "MAX_3D_TEXTURE_SIZE", type: "shift", max: 1}, - 33000: {name: "MAX_ELEMENTS_VERTICES", type: "-", max: 3, factor: 50}, - 33001: {name: "MAX_ELEMENTS_INDICES", type: "-", max: 3, factor: 50}, - 33901: {name: "ALIASED_POINT_SIZE_RANGE", type: "decimal", isArray: true}, - 33902: {name: "ALIASED_LINE_WIDTH_RANGE", type: "decimal", isArray: true}, - 34024: {name: "MAX_RENDERBUFFER_SIZE", type: "shift", max: 1}, - 34045: {name: "MAX_TEXTURE_LOD_BIAS", type: "-", max: 1, factor: 1}, - 34076: {name: "MAX_CUBE_MAP_TEXTURE_SIZE", type: "shift", max: 1}, - 34921: {name: "MAX_VERTEX_ATTRIBS", type: "shift", max: 1}, - 34930: {name: "MAX_TEXTURE_IMAGE_UNITS", type: "shift", max: 1}, - 35071: {name: "MAX_ARRAY_TEXTURE_LAYERS", type: "shift", max: 1}, - 35371: {name: "MAX_VERTEX_UNIFORM_BLOCKS", type: "-", max: 1, factor: 1}, - 35373: {name: "MAX_FRAGMENT_UNIFORM_BLOCKS", type: "-", max: 1, factor: 1}, - 35374: {name: "MAX_COMBINED_UNIFORM_BLOCKS", type: "-", max: 3, factor: 1}, - 35375: {name: "MAX_UNIFORM_BUFFER_BINDINGS", type: "-", max: 3, factor: 1}, - 35376: {name: "MAX_UNIFORM_BLOCK_SIZE", type: "shift", max: 1}, - 35377: {name: "MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS", type: "-", max: 7, factor: 10}, - 35379: {name: "MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS", type: "-", max: 7, factor: 10}, - 35657: {name: "MAX_FRAGMENT_UNIFORM_COMPONENTS", type: "shift", max: 1}, - 35658: {name: "MAX_VERTEX_UNIFORM_COMPONENTS", type: "shift", max: 1}, - 35659: {name: "MAX_VARYING_COMPONENTS", type: "shift", max: 1}, - 35660: {name: "MAX_VERTEX_TEXTURE_IMAGE_UNITS", type: "shift", max: 1}, - 35661: {name: "MAX_COMBINED_TEXTURE_IMAGE_UNITS", type: "-", max: 1, factor: 2}, - 35968: {name: "MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS", type: "shift", max: 1}, - 35978: {name: "MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS", type: "shift", max: 1}, - 36203: {name: "MAX_ELEMENT_INDEX", type: "-", max: 15, factor: 1}, - 36347: {name: "MAX_VERTEX_UNIFORM_VECTORS", type: "shift", max: 1}, - 36348: {name: "MAX_VARYING_VECTORS", type: "shift", max: 1}, - 36349: {name: "MAX_FRAGMENT_UNIFORM_VECTORS", type: "shift", max: 1}, - 37154: {name: "MAX_VERTEX_OUTPUT_COMPONENTS", type: "shift", max: 1}, - 37157: {name: "MAX_FRAGMENT_INPUT_COMPONENTS", type: "shift", max: 1}, - 7936: {name: "VENDOR", fake: function(originalValue, window, prefs){ - const settingValue = prefs("webGLVendor") || originalValue; - return {value: settingValue, faked: settingValue === originalValue}; - }}, - 7937: {name: "RENDERER", fake: function(originalValue, window, prefs){ - const settingValue = prefs("webGLRenderer") || originalValue; - return {value: settingValue, faked: settingValue === originalValue}; - }}, - 37445: {name: "UNMASKED_VENDOR_WEBGL", fake: function(originalValue, window, prefs){ - const settingValue = prefs("webGLUnmaskedVendor") || originalValue; - return {value: settingValue, faked: settingValue === originalValue}; - }}, - 37446: {name: "UNMASKED_RENDERER_WEBGL", fake: function(originalValue, window, prefs){ - const settingValue = prefs("webGLUnmaskedRenderer") || originalValue; - return {value: settingValue, faked: settingValue === originalValue}; - }} - }; - const parameterNames = Object.keys(changeDefinition); - parameterNames.forEach(function(parameterName){ - const definition = changeDefinition[parameterName]; + Object.keys(parameterChangeDefinition).forEach(function(parameterName){ + const definition = parameterChangeDefinition[parameterName]; definition.pname = parameterName; if (!definition.fake){ definition.fake = definition.isArray? function fake(originalValue, window){ let faked = false; let fakedValue = []; - for (var i = 0; i < originalValue.length; i += 1){ - fakedValue[i] = types[this.type](originalValue[i], this, window); + for (let i = 0; i < originalValue.length; i += 1){ + fakedValue[i] = parameterFakeTypes[this.type](originalValue[i], this, window); faked |= originalValue[i] === fakedValue[i]; originalValue[i] = fakedValue[i]; } this.fake = function(originalValue){ if (faked){ - for (var i = 0; i < originalValue.length; i += 1){ + for (let i = 0; i < originalValue.length; i += 1){ originalValue[i] = fakedValue[i]; } } @@ -641,7 +651,7 @@ }; }: function fake(originalValue, window){ - let value = types[this.type](originalValue, this, window); + let value = parameterFakeTypes[this.type](originalValue, this, window); let faked = value === originalValue; this.fake = function(){ return {value, faked}; @@ -652,10 +662,10 @@ }); return function getParameter(pname){ return checkerWrapper(checker, this, arguments, function(args, check){ - var {prefs, notify, window, original} = check; + const {prefs, notify, window, original} = check; const originalValue = original.apply(this, window.Array.from(args)); - if (changeDefinition[pname]){ - const definition = changeDefinition[pname]; + if (parameterChangeDefinition[pname]){ + const definition = parameterChangeDefinition[pname]; const {value, faked} = definition.fake(originalValue, window, prefs); if (faked){ notify("fakedReadout"); diff --git a/lib/modifiedHistoryAPI.js b/lib/modifiedHistoryAPI.js index 0632596..cd1e1ea 100644 --- a/lib/modifiedHistoryAPI.js +++ b/lib/modifiedHistoryAPI.js @@ -4,7 +4,7 @@ (function(){ "use strict"; - var scope; + let scope; if ((typeof exports) !== "undefined"){ scope = exports; } diff --git a/lib/modifiedNavigatorAPI.js b/lib/modifiedNavigatorAPI.js index a764c55..72b76a0 100644 --- a/lib/modifiedNavigatorAPI.js +++ b/lib/modifiedNavigatorAPI.js @@ -4,7 +4,7 @@ (function(){ "use strict"; - var scope; + let scope; if ((typeof exports) !== "undefined"){ scope = exports; } @@ -23,7 +23,7 @@ const temp = eval(`({ get ${property}(){ return checkerWrapper(checker, this, arguments, function(args, check){ - const {prefs, notify, window, original} = check; + const {notify, window, original} = check; const originalValue = original.apply(this, window.Array.from(args)); const returnValue = navigator.getNavigatorValue("${property}"); if (originalValue !== returnValue){ diff --git a/lib/modifiedScreenAPI.js b/lib/modifiedScreenAPI.js index f7e365e..9630ca2 100644 --- a/lib/modifiedScreenAPI.js +++ b/lib/modifiedScreenAPI.js @@ -4,7 +4,7 @@ (function(){ "use strict"; - var scope; + let scope; if ((typeof exports) !== "undefined"){ scope = exports; } @@ -177,7 +177,7 @@ const temp = { get availLeft(){ return checkerWrapper(checker, this, arguments, function(args, check){ - const {prefs, notify, window, original} = check; + const {notify, window, original} = check; const originalValue = original.apply(this, window.Array.from(args)); if (originalValue !== 0){ notify("fakedScreenReadout"); @@ -196,7 +196,7 @@ const temp = { get availTop(){ return checkerWrapper(checker, this, arguments, function(args, check){ - const {prefs, notify, window, original} = check; + const {notify, window, original} = check; const originalValue = original.apply(this, window.Array.from(args)); if (originalValue !== 0){ notify("fakedScreenReadout"); @@ -215,7 +215,7 @@ const temp = { get outerWidth(){ return checkerWrapper(checker, this, arguments, function(args, check){ - const {prefs, notify, window, original} = check; + const {notify, window, original} = check; const originalValue = original.apply(this, window.Array.from(args)); const returnValue = window.innerWidth; if (originalValue !== returnValue){ @@ -235,7 +235,7 @@ const temp = { get outerHeight(){ return checkerWrapper(checker, this, arguments, function(args, check){ - const {prefs, notify, window, original} = check; + const {notify, window, original} = check; const originalValue = original.apply(this, window.Array.from(args)); const returnValue = window.innerHeight; if (originalValue !== returnValue){ diff --git a/lib/modifiedWindowAPI.js b/lib/modifiedWindowAPI.js index 7cc6533..a388913 100644 --- a/lib/modifiedWindowAPI.js +++ b/lib/modifiedWindowAPI.js @@ -4,7 +4,7 @@ (function(){ "use strict"; - var scope; + let scope; if ((typeof exports) !== "undefined"){ scope = exports; } diff --git a/lib/navigator.js b/lib/navigator.js index daf56bc..6f61201 100644 --- a/lib/navigator.js +++ b/lib/navigator.js @@ -4,7 +4,7 @@ (function(){ "use strict"; - var scope; + let scope; if ((typeof exports) !== "undefined"){ scope = exports; } @@ -77,7 +77,7 @@ settings.protectedAPIFeatures["userAgent @ navigator"] ) ){ - for (var header of details.requestHeaders){ + for (let header of details.requestHeaders){ if (header.name.toLowerCase() === "user-agent"){ header.value = getValue("userAgent"); } diff --git a/lib/notification.js b/lib/notification.js index 92e995d..e68273b 100644 --- a/lib/notification.js +++ b/lib/notification.js @@ -4,7 +4,7 @@ (function(){ "use strict"; - var scope; + let scope; if ((typeof exports) !== "undefined"){ scope = exports; } @@ -170,6 +170,9 @@ tabs.forEach(function(tab){ browser.pageAction.hide(tab.id); }); + return; + }).catch(function(error){ + logging.warning("Unable to get browser tabs:", error); }); } }); @@ -188,6 +191,9 @@ text: "" }); }); + return; + }).catch(function(error){ + logging.warning("Unable to get browser tabs:", error); }); } } diff --git a/lib/persistentRndStorage.js b/lib/persistentRndStorage.js index 8ef0809..1434ad8 100644 --- a/lib/persistentRndStorage.js +++ b/lib/persistentRndStorage.js @@ -4,7 +4,7 @@ (function(){ "use strict"; - var scope; + let scope; if ((typeof exports) !== "undefined"){ scope = exports; } @@ -18,6 +18,7 @@ scope.persistentRnd = Object.create(null); scope.persistentIncognitoRnd = Object.create(null); + let clearTimeout; scope.init = function init(){ logging.message("initializing persistent rng storage"); @@ -26,8 +27,8 @@ if (settings.storePersistentRnd){ try { let storedData = JSON.parse(settings.persistentRndStorage); - for (var domain in storedData){ - var value = storedData[domain]; + for (let domain in storedData){ + const value = storedData[domain]; if ( Array.isArray(value) && value.length === 128 && @@ -39,7 +40,7 @@ } } } - catch (e){ + catch (error){ // JSON is not valid -> ignore it } } @@ -74,7 +75,7 @@ }; const getInterval = function(){ - var units = { + const units = { seconds: 1000, minutes: 60 * 1000, hours: 60 * 60 * 1000, @@ -95,18 +96,20 @@ })){ clearIncognito(); } + return; + }).catch(function(error){ + logging.warning("Unable to get browser windows:", error); }); }); - - let clearTimeout; + function registerTimeout(){ - var interval = getInterval(); + const interval = getInterval(); if (interval > 0){ - var timeout = settings.lastPersistentRndClearing + interval - Date.now(); + const timeout = settings.lastPersistentRndClearing + interval - Date.now(); logging.message("registering persistent rng data clearing timeout. Clearing in ", timeout, "ms"); if (timeout > 1073741824){ // window.setTimeout can only handle delays up to 32 bit. - // Therefore we repeat the registering afert 2^30 = 1073741824 seconds + // Therefore we repeat the registering after 2^30 = 1073741824 seconds clearTimeout = window.setTimeout(registerTimeout, 1073741824); } else { @@ -119,6 +122,9 @@ tabs.forEach(function(tab){ browser.tabs.sendMessage(tab.id, data); }); + return; + }).catch(function(error){ + logging.warning("Unable to get browser tabs:", error); }); } function clearIncognito(){ diff --git a/lib/randomSupplies.js b/lib/randomSupplies.js index d333e9d..e8e5570 100644 --- a/lib/randomSupplies.js +++ b/lib/randomSupplies.js @@ -4,7 +4,7 @@ (function(){ "use strict"; - var scope; + let scope; if ((typeof exports) !== "undefined"){ scope = exports; } @@ -15,7 +15,6 @@ const rngTemplate = { getBitRng: function(length, window){ const rng = this.getRng(Math.ceil(length / 32), window); - let bitIndex = 32; let rnd = 0; let mask = 0xffffffff * 2; return function(value, i){ @@ -38,20 +37,21 @@ getValueRng: function(length, window){ const rng = this.getBitRng(length, window); return function(value, i){ - var rnd = rng(value, i); + const rnd = rng(value, i); // XOR the last bit to alter it... or not return value ^ (rnd & 0x01); }; }, getPixelRng: function(length, window, ignoredColors){ - var rng = this.getValueRng(length, window); - return function(r, g, b, a, i){ // eslint-disable-line max-params - var index = String.fromCharCode(r, g, b, a); + const rng = this.getValueRng(length, window); + // eslint-disable-next-line max-params + return function(r, g, b, a, i){ + const index = String.fromCharCode(r, g, b, a); if (ignoredColors[index]){ return [r, g, b, a]; } - var baseIndex = i * 4; + const baseIndex = i * 4; return [ rng(r, baseIndex + 0), rng(g, baseIndex + 1), @@ -81,7 +81,7 @@ return window.location.host; } - var persistentRnd = Object.create(null); + let persistentRnd = Object.create(null); let cookieStoreId = false; settings.onloaded(function(){ try { @@ -90,8 +90,8 @@ settings.persistentIncognitoRndStorage: settings.persistentRndStorage ); - for (var domain in storedData){ - var value = storedData[domain]; + for (let domain in storedData){ + const value = storedData[domain]; if ( Array.isArray(value) && value.length === 128 && @@ -103,7 +103,7 @@ } } } - catch (e){ + catch (error){ // JSON is not valid -> ignore it } }); @@ -111,7 +111,7 @@ extension.message.on(function(data){ if (data["canvasBlocker-set-domain-rnd"]){ - var {domain, incognito, rnd} = data["canvasBlocker-set-domain-rnd"]; + const {domain, incognito, rnd} = data["canvasBlocker-set-domain-rnd"]; if (incognito === extension.inIncognitoContext){ persistentRnd[domain] = new Uint8Array(rnd); } @@ -130,11 +130,11 @@ xhr.send(); xhr = null; } - catch (e){ - logging.verbose("Error in XHR:", e); + catch (error){ + logging.verbose("Error in XHR:", error); } } - var domain = cookieStoreId + getDomain(window); + const domain = cookieStoreId + getDomain(window); if (!persistentRnd[domain]){ // create the (sub-)domains random numbers if not existing persistentRnd[domain] = new Uint8Array(128); @@ -165,26 +165,26 @@ } }; scope.persistent.getRng = function(length, window){ - var bitSet = new Uint32Array(getPersistentRnd(window).buffer); - var bitSetLength = bitSet.length; + const bitSet = new Uint32Array(getPersistentRnd(window).buffer); + const bitSetLength = bitSet.length; return function(i){ return bitSet[i % bitSetLength]; }; }; scope.persistent.getBitRng = function(length, window){ - var bitSet = getPersistentRnd(window); + const bitSet = getPersistentRnd(window); return function(value, i){ // use the last 7 bits from the value for the index of the // random number - var index = value & 0x7F; + const index = value & 0x7F; // use the last 3 bits from the position and the first bit from // from the value to get bit to use from the random number - var bitIndex = ((i & 0x03) << 1) | (value >>> 7); + const bitIndex = ((i & 0x03) << 1) | (value >>> 7); // extract the bit - var bit = (bitSet[index] >>> bitIndex) & 0x01; + const bit = (bitSet[index] >>> bitIndex) & 0x01; return bit; }; @@ -196,16 +196,17 @@ return scope.nonPersistent.getRng(length, window); }; scope.constant.getPixelRng = (function(){ - var colors = Object.create(null); + const colors = Object.create(null); return function getConstantPixelRng(length, window, ignoredColors){ - var rng = scope.nonPersistent.getValueRng(1024, window); + const rng = scope.nonPersistent.getValueRng(1024, window); - return function(r, g, b, a, i){ // eslint-disable-line max-params - var index = String.fromCharCode(r, g, b, a); + // eslint-disable-next-line max-params, no-unused-vars + return function(r, g, b, a, i){ + const index = String.fromCharCode(r, g, b, a); if (ignoredColors[index]){ return [r, g, b, a]; } - var color = colors[index]; + let color = colors[index]; if (!color){ color = [ rng(r, 0), @@ -224,8 +225,8 @@ scope.nonPersistent.name = "nonPersistent"; scope.nonPersistent.getRng = function(length, window){ const maxLength = 0x4000; - var randomI = maxLength; - var randomNumbers = new Uint32Array(Math.min(maxLength, length)); + let randomI = maxLength; + let randomNumbers = new Uint32Array(Math.min(maxLength, length)); return function(i){ if (randomI >= randomNumbers.length){ // refill the random number bucket if empty @@ -235,7 +236,7 @@ } window.crypto.getRandomValues(randomNumbers); } - var rnd = randomNumbers[randomI]; + const rnd = randomNumbers[randomI]; randomI += 1; return rnd; diff --git a/lib/require.js b/lib/require.js index 8844a7f..7053f24 100644 --- a/lib/require.js +++ b/lib/require.js @@ -10,14 +10,14 @@ const require = function(){ const scope = window.scope; function getScopeName(module){ - var scopeName = module.replace(/^\..*\//, "").replace(/\..+/, ""); + const scopeName = module.replace(/^\..*\//, "").replace(/\..+/, ""); // console.log(scopeName); return scopeName; } function require(module){ if (module.startsWith(".")){ - var scopeName = getScopeName(module); + const scopeName = getScopeName(module); return scope[scopeName]; } throw new ReferenceError("Unable to get non relative module " + module + "!"); diff --git a/lib/search.js b/lib/search.js index b63a6fb..556d342 100644 --- a/lib/search.js +++ b/lib/search.js @@ -4,7 +4,7 @@ (function(){ "use strict"; - var scope; + let scope; if ((typeof exports) !== "undefined"){ scope = exports; } @@ -35,7 +35,7 @@ if (resultSets.length){ return Array.from( resultSets.reduce(function(previousSet, set){ - var andSet = new Set(); + const andSet = new Set(); set.forEach(function(entry){ if (previousSet.has(entry)){ andSet.add(entry); diff --git a/lib/settingContainers.js b/lib/settingContainers.js index 326a113..67c4339 100644 --- a/lib/settingContainers.js +++ b/lib/settingContainers.js @@ -4,7 +4,7 @@ (function(){ "use strict"; - var scope; + let scope; if ((typeof exports) !== "undefined"){ scope = exports; } @@ -19,7 +19,7 @@ scope.expandContainer = null; scope.getUrlValueContainer = function(name, url){ - var matching = scope.urlContainer.get().filter(function(urlSetting){ + const matching = scope.urlContainer.get().filter(function(urlSetting){ return urlSetting.hasOwnProperty(name); }).filter(function(urlSetting){ return urlSetting.match(url); @@ -32,8 +32,8 @@ } }; scope.setUrlValue = function(name, value, url){ - var urlContainerValue = scope.urlContainer.get(); - var matching = urlContainerValue.filter(function(urlSetting){ + const urlContainerValue = scope.urlContainer.get(); + let matching = urlContainerValue.filter(function(urlSetting){ return urlSetting.match(url); }); if (!matching.length){ @@ -46,8 +46,8 @@ return scope.urlContainer.set(urlContainerValue); }; scope.resetUrlValue = function(name, url){ - var urlContainerValue = scope.urlContainer.get(); - var matching = urlContainerValue.filter(function(urlSetting){ + let urlContainerValue = scope.urlContainer.get(); + const matching = urlContainerValue.filter(function(urlSetting){ return urlSetting.match(url); }); if (matching.length){ @@ -61,6 +61,92 @@ } }; + function processHideContainer(settingDefinition){ + scope.hideContainer = settingDefinition; + let changeListeners = {}; + settingDefinition.setHideByName = function(name, value){ + logging.verbose("set hide of", name, "to", value); + const hideStore = settingDefinition.get(); + hideStore[name] = value; + settingDefinition.set(hideStore); + (changeListeners[name] || []).forEach(function(listener){ + listener(value); + }); + }; + settingDefinition.getHideByName = function(name){ + const hideStore = settingDefinition.get(); + return hideStore[name] || false; + }; + settingDefinition.onHideChange = function(name, listener){ + if (!changeListeners[name]){ + changeListeners[name] = []; + } + changeListeners[name].push(listener); + }; + settingDefinition.on(function(event){ + const value = event.newValue; + Object.keys(value).forEach(function(name){ + (changeListeners[name] || []).forEach(function(listener){ + listener(value[name]); + }); + }); + const oldValue = event.oldValue; + Object.keys(oldValue).filter(function(name){ + return !value.hasOwnProperty(name); + }).forEach(function(name){ + (changeListeners[name] || []).forEach(function(listener){ + listener(false); + }); + }); + }); + settingDefinition.hideAble = false; + } + + function processExpandContainer(settingDefinition){ + scope.expandContainer = settingDefinition; + let changeListeners = {}; + settingDefinition.setExpandByName = function(name, value){ + logging.verbose("set expand of", name, "to", value); + const expandStore = settingDefinition.get(); + expandStore[name] = value; + settingDefinition.set(expandStore); + (changeListeners[name] || []).forEach(function(listener){ + listener(value); + }); + }; + settingDefinition.getExpandByName = function(name, defaultValue = false){ + const expandStore = settingDefinition.get(); + if ((typeof expandStore[name]) !== "undefined"){ + return expandStore[name] || false; + } + else { + return defaultValue; + } + }; + settingDefinition.onExpandChange = function(name, listener){ + if (!changeListeners[name]){ + changeListeners[name] = []; + } + changeListeners[name].push(listener); + }; + settingDefinition.on(function(event){ + const value = event.newValue; + Object.keys(value).forEach(function(name){ + (changeListeners[name] || []).forEach(function(listener){ + listener(value[name]); + }); + }); + const oldValue = event.oldValue; + Object.keys(oldValue).filter(function(name){ + return !value.hasOwnProperty(name); + }).forEach(function(name){ + (changeListeners[name] || []).forEach(function(listener){ + listener(false); + }); + }); + }); + } + scope.check = function(settingDefinition){ if (settingDefinition.isUrlContainer){ scope.urlContainer = settingDefinition; @@ -70,89 +156,11 @@ } if (settingDefinition.isHideContainer){ - scope.hideContainer = settingDefinition; - let changeListeners = {}; - settingDefinition.setHideByName = function(name, value){ - logging.verbose("set hide of", name, "to", value); - const hideStore = settingDefinition.get(); - hideStore[name] = value; - settingDefinition.set(hideStore); - (changeListeners[name] || []).forEach(function(listener){ - listener(value); - }); - }; - settingDefinition.getHideByName = function(name){ - const hideStore = settingDefinition.get(); - return hideStore[name] || false; - }; - settingDefinition.onHideChange = function(name, listener){ - if (!changeListeners[name]){ - changeListeners[name] = []; - } - changeListeners[name].push(listener); - }; - settingDefinition.on(function(event){ - const value = event.newValue; - Object.keys(value).forEach(function(name){ - (changeListeners[name] || []).forEach(function(listener){ - listener(value[name]); - }); - }); - const oldValue = event.oldValue; - Object.keys(oldValue).filter(function(name){ - return !value.hasOwnProperty(name); - }).forEach(function(name){ - (changeListeners[name] || []).forEach(function(listener){ - listener(false); - }); - }); - }); - settingDefinition.hideAble = false; + processHideContainer(settingDefinition); } if (settingDefinition.isExpandContainer){ - scope.expandContainer = settingDefinition; - let changeListeners = {}; - settingDefinition.setExpandByName = function(name, value){ - logging.verbose("set expand of", name, "to", value); - const expandStore = settingDefinition.get(); - expandStore[name] = value; - settingDefinition.set(expandStore); - (changeListeners[name] || []).forEach(function(listener){ - listener(value); - }); - }; - settingDefinition.getExpandByName = function(name, defaultValue = false){ - const expandStore = settingDefinition.get(); - if ((typeof expandStore[name]) !== "undefined"){ - return expandStore[name] || false; - } - else { - return defaultValue; - } - }; - settingDefinition.onExpandChange = function(name, listener){ - if (!changeListeners[name]){ - changeListeners[name] = []; - } - changeListeners[name].push(listener); - }; - settingDefinition.on(function(event){ - const value = event.newValue; - Object.keys(value).forEach(function(name){ - (changeListeners[name] || []).forEach(function(listener){ - listener(value[name]); - }); - }); - const oldValue = event.oldValue; - Object.keys(oldValue).filter(function(name){ - return !value.hasOwnProperty(name); - }).forEach(function(name){ - (changeListeners[name] || []).forEach(function(listener){ - listener(false); - }); - }); - }); + processExpandContainer(settingDefinition); } }; @@ -160,8 +168,8 @@ if (scope.urlContainer){ scope.urlContainer.on(function({newValue, oldValue}){ newValue.forEach(function(urlSetting){ - var regExp; - var domain = !!urlSetting.url.match(/^[A-Za-z0-9_.-]+$/); + let regExp; + const domain = !!urlSetting.url.match(/^[A-Za-z0-9_.-]+$/); if (domain){ regExp = new RegExp( "(?:^|\\.)" + urlSetting.url.replace(/([\\+*?[^\]$(){}=!|.])/g, "\\$1") + "\\.?$", @@ -200,9 +208,9 @@ ); }); - var newUrls = newValue.map(function(entry){return entry.url;}); - var oldUrls = oldValue.map(function(entry){return entry.url;}); - var matching = {}; + const newUrls = newValue.map(function(entry){return entry.url;}); + const oldUrls = oldValue.map(function(entry){return entry.url;}); + const matching = {}; newUrls.forEach(function(url, i){ matching[url] = {new: i, old: oldUrls.indexOf(url)}; }); @@ -212,12 +220,12 @@ } }); Object.keys(matching).forEach(function(url){ - var oldEntry = oldValue[matching[url].old] || {}; - var newEntry = newValue[matching[url].new] || {}; + const oldEntry = oldValue[matching[url].old] || {}; + const newEntry = newValue[matching[url].new] || {}; scope.urlContainer.entries.forEach(function(settingDefinition){ - var name = settingDefinition.name; - var oldValue = oldEntry[name]; - var newValue = newEntry[name]; + const name = settingDefinition.name; + const oldValue = oldEntry[name]; + const newValue = newEntry[name]; if (oldValue !== newValue){ ((eventHandler[name] || {})[url] || []).forEach(function(callback){ diff --git a/lib/settingDefinitions.js b/lib/settingDefinitions.js index 2637de6..77e1f0d 100644 --- a/lib/settingDefinitions.js +++ b/lib/settingDefinitions.js @@ -4,7 +4,7 @@ (function(){ "use strict"; - var settingDefinitions = [ + const settingDefinitions = [ { name: "logLevel", defaultValue: 1, diff --git a/lib/settingStrings.js b/lib/settingStrings.js index ab5a673..9739608 100644 --- a/lib/settingStrings.js +++ b/lib/settingStrings.js @@ -4,7 +4,7 @@ (function(){ "use strict"; - var scope; + let scope; if ((typeof exports) !== "undefined"){ scope = exports; } diff --git a/lib/settings.js b/lib/settings.js index 9e77da4..a639586 100644 --- a/lib/settings.js +++ b/lib/settings.js @@ -4,7 +4,7 @@ (function(require){ "use strict"; - var scope; + let scope; if ((typeof exports) !== "undefined"){ scope = exports; } @@ -37,7 +37,7 @@ if (!Array.isArray(newValue)){ return "wrongType"; } - var entriesInvalid = newValue.reduce(function(v, entry){ + const entriesInvalid = newValue.reduce(function(v, entry){ v = v || settingDefinition.entries.reduce(function(v, entryDefinition){ return v || isDefinitionInvalid(entryDefinition, entry[entryDefinition.name]); }, false); @@ -74,7 +74,7 @@ else if (settingDefinition.urlSpecific){ return function getValue(url){ if (url){ - var match = settingContainers.getUrlValueContainer(settingDefinition.name, url); + const match = settingContainers.getUrlValueContainer(settingDefinition.name, url); if (match){ return match[settingDefinition.name]; } @@ -105,13 +105,13 @@ function createSetter(settingDefinition){ if (settingDefinition.dynamic){ return function setValue(newValue){ - settingDefinition.setter(scope); + settingDefinition.setter(scope, newValue); }; } else { const name = settingDefinition.name; const isValid = function isValid(newValue){ - var invalid = settingDefinition.invalid(newValue); + const invalid = settingDefinition.invalid(newValue); if (invalid){ if (invalid === "fixed"){ logging.warning("Trying to set the fixed setting", name, ":", newValue); @@ -133,13 +133,14 @@ logging.verbose("Trying to store new value for %s", name, newValue); settings[name] = newValue; if (!settingDefinition.transient){ - var storeObject = {}; + const storeObject = {}; storeObject[name] = newValue; - var promise = browser.storage.local.set(storeObject); + const promise = browser.storage.local.set(storeObject); promise.then(function(){ logging.verbose("New value stored for %s:", name, newValue); - }, function(err){ - logging.error("Unable to store new value for %s:", name, newValue, err); + return; + }).catch(function(error){ + logging.error("Unable to store new value for %s:", name, newValue, error); }); return promise; } @@ -227,7 +228,7 @@ }; settingDefinitions.forEach(function(settingDefinition){ - var name = settingDefinition.name; + const name = settingDefinition.name; definitionsByName[name] = settingDefinition; if (typeof settingDefinition.defaultValue === "function"){ settingDefinition.defaultValue = settingDefinition.defaultValue(); @@ -282,7 +283,7 @@ }); scope.getDefinition = function(name){ - var foundDefinition = definitionsByName[name]; + const foundDefinition = definitionsByName[name]; if (foundDefinition){ return Object.create(foundDefinition); } @@ -300,7 +301,7 @@ }; scope.set = function(name, ...args){ - var foundDefinition = definitionsByName[name]; + const foundDefinition = definitionsByName[name]; if (foundDefinition){ return foundDefinition.set(...args); } @@ -309,7 +310,7 @@ } }; scope.get = function(name, ...args){ - var foundDefinition = definitionsByName[name]; + const foundDefinition = definitionsByName[name]; if (foundDefinition){ return foundDefinition.get(...args); } @@ -328,9 +329,9 @@ const resetSymbol = Symbol("reset"); function changeValue(name, newValue){ - var settingDefinition = scope.getDefinition(name); + const settingDefinition = scope.getDefinition(name); if (settingDefinition){ - var oldValue = settings[name]; + const oldValue = settings[name]; if (newValue === resetSymbol){ newValue = getDefaultValue(settingDefinition); } @@ -355,7 +356,7 @@ browser.storage.onChanged.addListener(function(changes, area){ if (area === "local"){ logging.notice("settings changed", changes); - var delayedChange = []; + const delayedChange = []; Object.entries(changes).forEach(function(entry){ const [name, change] = entry; if (settingContainers.urlContainer && name === settingContainers.urlContainer.name){ @@ -407,7 +408,7 @@ {settings, logging, changeValue, urlContainer: settingContainers.urlContainer} ); } - var delayedChange = []; + const delayedChange = []; Object.entries(storage).forEach(function(entry){ const [name, value] = entry; if (settingContainers.urlContainer && name === settingContainers.urlContainer.name){ @@ -451,8 +452,8 @@ xhr.send(); xhr = null; } - catch (e){ - logging.verbose("Error in XHR:", e); + catch (error){ + logging.verbose("Error in XHR:", error); } logging.message("settings still default?", settings.isStillDefault); } diff --git a/lib/settingsMigration.js b/lib/settingsMigration.js index f44f54b..d541a40 100644 --- a/lib/settingsMigration.js +++ b/lib/settingsMigration.js @@ -5,7 +5,7 @@ (function(){ "use strict"; - var scope; + let scope; if ((typeof exports) !== "undefined"){ scope = exports; } @@ -17,13 +17,14 @@ scope.validVersions = [undefined, 0.1, 0.2, 0.3, 0.4, 0.5]; scope.transitions = { + // eslint-disable-next-line no-unused-vars "": function(oldStorage){ return { storageVersion: 0.5 }; }, 0.1: function(oldStorage){ - var newStorage = { + const newStorage = { storageVersion: 0.2 }; if (oldStorage.hasOwnProperty("askOnlyOnce")){ @@ -32,7 +33,7 @@ return newStorage; }, 0.2: function(oldStorage){ - var newStorage = { + const newStorage = { storageVersion: 0.3, urlSettings: ( oldStorage.urlSettings && @@ -40,13 +41,13 @@ )? oldStorage.urlSettings: [] }; - var urlSettings = {}; + const urlSettings = {}; (oldStorage.blackList || "").split(",") .map(function(url){return url.trim();}) .filter(function(url){return !!url;}) .forEach(function(url){ - var entry = urlSettings[url]; + let entry = urlSettings[url]; if (!entry){ entry = {url, blockMode: "block"}; urlSettings[url] = entry; @@ -57,7 +58,7 @@ .map(function(url){return url.trim();}) .filter(function(url){return !!url;}) .forEach(function(url){ - var entry = urlSettings[url]; + let entry = urlSettings[url]; if (!entry){ entry = {url, blockMode: "allow"}; urlSettings[url] = entry; @@ -68,7 +69,7 @@ .map(function(url){return url.trim();}) .filter(function(url){return !!url;}) .forEach(function(url){ - var entry = urlSettings[url]; + let entry = urlSettings[url]; if (!entry){ entry = {url, showNotifications: false}; urlSettings[url] = entry; @@ -87,7 +88,7 @@ return newStorage; }, 0.3: function(oldStorage){ - var newStorage = { + const newStorage = { storageVersion: 0.4 }; if (oldStorage.hasOwnProperty("apiWhiteList")){ @@ -100,7 +101,7 @@ return newStorage; }, 0.4: function(oldStorage){ - var newStorage = { + const newStorage = { storageVersion: 0.5 }; @@ -134,7 +135,7 @@ return newStorage; }, 0.5: function(oldStorage){ - var newStorage = { + const newStorage = { storageVersion: 0.6 }; @@ -159,7 +160,7 @@ }, }; - scope.check = function(storage, {settings, logging, changeValue, urlContainer}){ + scope.check = function(storage, {settings, logging}){ if (!storage.storageVersion){ logging.message("No storage version found. Initializing storage."); @@ -168,13 +169,13 @@ browser.storage.local.set(storage); } else if (storage.storageVersion !== settings.storageVersion){ - var toChange = {}; + const toChange = {}; while (storage.storageVersion !== settings.storageVersion){ logging.message("Old storage found (", storage.storageVersion, "expected", settings.storageVersion, ")"); if (scope.transitions[storage.storageVersion]){ - var changes = scope.transitions[storage.storageVersion](storage); + const changes = scope.transitions[storage.storageVersion](storage); Object.entries(changes).forEach(function(entry){ const [name, value] = entry; toChange[name] = value; diff --git a/lib/theme.js b/lib/theme.js index 08ada55..995912f 100644 --- a/lib/theme.js +++ b/lib/theme.js @@ -17,7 +17,7 @@ scope.init = function(page){ const basePath = browser.extension.getURL("themes"); - var baseLink = document.createElement("link"); + const baseLink = document.createElement("link"); baseLink.href = `${basePath}/base/layout.css`; baseLink.rel = "stylesheet"; baseLink.type = "text/css"; @@ -25,7 +25,7 @@ const links = ["layout", page].filter(function(file){ return file; }).map(function(file){ - var link = document.createElement("link"); + const link = document.createElement("link"); link.cbFile = file; link.rel = "alternative"; link.type = "text/css"; diff --git a/lib/webgl.js b/lib/webgl.js index 8818b20..ca2e17b 100644 --- a/lib/webgl.js +++ b/lib/webgl.js @@ -4,9 +4,9 @@ (function(){ "use strict"; - var logging = require("./logging"); + const logging = require("./logging"); - var scope; + let scope; if ((typeof exports) !== "undefined"){ scope = exports; } @@ -15,9 +15,8 @@ } scope.copyCanvasToWebgl = function copyCanvasToWebgl(window, canvas, webGLVersion = "webgl"){ - var webGlCanvas = canvas.cloneNode(true); - var success; - var context = + const webGlCanvas = canvas.cloneNode(true); + const context = window.HTMLCanvasElement.prototype.getContext.call(webGlCanvas, webGLVersion) || window.HTMLCanvasElement.prototype.getContext.call(webGlCanvas, "experimental-" + webGLVersion); if (!context){ @@ -27,38 +26,35 @@ } context.viewport(0, 0, webGlCanvas.width, webGlCanvas.height); - - var program = context.createProgram(); - - var shader = context.createShader(context.VERTEX_SHADER); - var vertex = "attribute vec4 a_position;\nattribute vec2 a_texCoord;\nvarying vec2 v_texCoord;\n" + + + const program = context.createProgram(); + + const vertexShader = context.createShader(context.VERTEX_SHADER); + const vertex = "attribute vec4 a_position;\nattribute vec2 a_texCoord;\nvarying vec2 v_texCoord;\n" + "void main(){\n\tgl_Position = a_position;\n\tv_texCoord = a_texCoord;\n}"; - context.shaderSource(shader, vertex); - context.compileShader(shader); - success = context.getShaderParameter(shader, context.COMPILE_STATUS); - if (!success){ - context.deleteShader(shader); + context.shaderSource(vertexShader, vertex); + context.compileShader(vertexShader); + if (!context.getShaderParameter(vertexShader, context.COMPILE_STATUS)){ + context.deleteShader(vertexShader); logging.warning("webgl: failed to compile vertex shader."); return {canvas: false, context: false}; } - context.attachShader(program, shader); + context.attachShader(program, vertexShader); - shader = context.createShader(context.FRAGMENT_SHADER); - var fragmenter = "precision mediump float;\nuniform sampler2D u_image;\nvarying vec2 v_texCoord;\n" + + const fragmentShader = context.createShader(context.FRAGMENT_SHADER); + const fragmenter = "precision mediump float;\nuniform sampler2D u_image;\nvarying vec2 v_texCoord;\n" + "void main(){\n\tgl_FragColor = texture2D(u_image, v_texCoord);\n}"; - context.shaderSource(shader, fragmenter); - context.compileShader(shader); - success = context.getShaderParameter(shader, context.COMPILE_STATUS); - if (!success){ - context.deleteShader(shader); + context.shaderSource(fragmentShader, fragmenter); + context.compileShader(fragmentShader); + if (!context.getShaderParameter(fragmentShader, context.COMPILE_STATUS)){ + context.deleteShader(fragmentShader); logging.warning("webgl: failed to compile fragmenter shader."); return {canvas: false, context: false}; } - context.attachShader(program, shader); + context.attachShader(program, fragmentShader); context.linkProgram(program); - success = context.getProgramParameter(program, context.LINK_STATUS); - if (!success){ + if (!context.getProgramParameter(program, context.LINK_STATUS)){ context.deleteProgram(program); logging.warning("webgl: failed to link program."); return {canvas: false, context: false}; @@ -66,8 +62,7 @@ context.useProgram(program); - var positionAttributeLocation = context.getAttribLocation(program, "a_position"); - var positionBuffer = context.createBuffer(); + const positionBuffer = context.createBuffer(); context.bindBuffer(context.ARRAY_BUFFER, positionBuffer); context.bufferData(context.ARRAY_BUFFER, new Float32Array([ -1, -1, @@ -76,21 +71,21 @@ 1, 1, -1, 1, 1, -1 - ]), context.STATIC_DRAW); + const positionAttributeLocation = context.getAttribLocation(program, "a_position"); context.enableVertexAttribArray(positionAttributeLocation); - var size = 2; // 2 components per iteration - var type = context.FLOAT; // the data is 32bit floats - var normalize = false; // don't normalize the data - var stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position - var offset = 0; // start at the beginning of the buffer + const size = 2; // 2 components per iteration + const type = context.FLOAT; // the data is 32bit floats + const normalize = false; // don't normalize the data + const stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position + const offset = 0; // start at the beginning of the buffer context.vertexAttribPointer(positionAttributeLocation, size, type, normalize, stride, offset); - var texCoordLocation = context.getAttribLocation(program, "a_texCoord"); + const texCoordLocation = context.getAttribLocation(program, "a_texCoord"); // provide texture coordinates for the rectangle. - var texCoordBuffer = context.createBuffer(); + const texCoordBuffer = context.createBuffer(); context.bindBuffer(context.ARRAY_BUFFER, texCoordBuffer); context.bufferData(context.ARRAY_BUFFER, new Float32Array([ 0, 1, @@ -102,19 +97,15 @@ ]), context.STATIC_DRAW); context.enableVertexAttribArray(texCoordLocation); context.vertexAttribPointer(texCoordLocation, 2, context.FLOAT, false, 0, 0); - - var texture = context.createTexture(); - context.bindTexture(context.TEXTURE_2D, texture); + + context.bindTexture(context.TEXTURE_2D, context.createTexture()); context.texParameteri(context.TEXTURE_2D, context.TEXTURE_WRAP_S, context.CLAMP_TO_EDGE); context.texParameteri(context.TEXTURE_2D, context.TEXTURE_WRAP_T, context.CLAMP_TO_EDGE); context.texParameteri(context.TEXTURE_2D, context.TEXTURE_MIN_FILTER, context.NEAREST); context.texParameteri(context.TEXTURE_2D, context.TEXTURE_MAG_FILTER, context.NEAREST); context.texImage2D(context.TEXTURE_2D, 0, context.RGBA, context.RGBA, context.UNSIGNED_BYTE, canvas); - - var primitiveType = context.TRIANGLES; - var triangleOffset = 0; - var count = 6; - context.drawArrays(primitiveType, triangleOffset, count); + + context.drawArrays(context.TRIANGLES /*primitiveType*/, 0 /*triangleOffset*/, 6 /*count*/); return {webGlCanvas, context}; }; diff --git a/options/export.js b/options/export.js index 504558c..4984da9 100644 --- a/options/export.js +++ b/options/export.js @@ -10,7 +10,7 @@ require("../lib/theme").init(); const input = document.getElementById("settings"); settings.onloaded(function(){ - var data = {}; + const data = {}; settings.forEach(function(def){ data[def.name] = def.get(); }); @@ -18,8 +18,8 @@ input.addEventListener("input", function(){ try { - var newSettings = JSON.parse(this.value); - var isValid = true; + let newSettings = JSON.parse(this.value); + let isValid = true; while (settingsMigration.transitions.hasOwnProperty(newSettings.storageVersion)){ let oldVersion = newSettings.storageVersion; @@ -57,14 +57,14 @@ this.classList.add("invalid"); } } - catch (e){ - logging.warning("Invalid JSON:", e); + catch (error){ + logging.warning("Invalid JSON:", error); this.classList.add("invalid"); } }); input.addEventListener("blur", function(){ if (!this.classList.contains("invalid")){ - var data = {}; + const data = {}; settings.forEach(function(def){ data[def.name] = def.get(); }); diff --git a/options/options.js b/options/options.js index 75c3382..cd09237 100644 --- a/options/options.js +++ b/options/options.js @@ -18,7 +18,7 @@ require("./theme").init("options"); const modal = require("../lib/modal"); - var callbacks = { + const callbacks = { openNavigatorSettings: function(){ logging.verbose("open navigator settings"); window.open("navigator.html", "_blank"); @@ -36,7 +36,7 @@ }, clearPersistentRndForContainer: function(){ browser.contextualIdentities.query({}).then(function(identities){ - modal.select( + return modal.select( extension.getTranslation("clearPersistentRndForContainer_title"), identities.map(function(identity){ return { @@ -44,9 +44,12 @@ object: identity }; }) - ).then(function(identity){ - extension.message.send({"canvasBlocker-clear-container-rnd": identity.cookieStoreId}); - }, ()=>{}); + ); + }).then(function(identity){ + extension.message.send({"canvasBlocker-clear-container-rnd": identity.cookieStoreId}); + return; + }).catch(function(error){ + logging.warning("Unable to clear persistent rnd for container:", error); }); }, inspectSettings: function(){ @@ -73,7 +76,7 @@ link.target = "_blank"; const now = new Date(); function format(number, digits){ - var str = number.toFixed(0); + const str = number.toFixed(0); return "0".repeat(digits - str.length) + str; } link.download = "CanvasBlocker-settings_" + @@ -99,13 +102,12 @@ input.type = "file"; input.addEventListener("change", function(){ if (this.files.length){ - var file = this.files[0]; - var reader = new FileReader(); - reader.onload = function(result){ + const reader = new FileReader(); + reader.onload = function(){ resolve(this.result); }; - reader.onerror = function(err){ - reject(err); + reader.onerror = function(error){ + reject(error); }; reader.readAsText(this.files[0]); } @@ -134,8 +136,9 @@ keys.forEach(function(key){ settings[key] = json[key]; }); - }).catch(function(err){ - alert(err); + return; + }).catch(function(error){ + alert(error); }); }, resetSettings: function(){ @@ -149,6 +152,9 @@ if (clear){ browser.storage.local.clear(); } + return; + }).catch(function(error){ + logging.warning("Unable to reset settings:", error); }); } }; @@ -224,22 +230,25 @@ linkDiv.appendChild(link); head.appendChild(linkDiv); } + return; + }).catch(function(error){ + logging.warning("Unable to identify tab:", error); }); - var groupTabs = document.createElement("div"); + const groupTabs = document.createElement("div"); groupTabs.classList = "groupTabs"; document.body.appendChild(groupTabs); - var groups = document.createElement("ul"); + const groups = document.createElement("ul"); groups.className = "groups"; groupTabs.appendChild(groups); - var table = document.createElement("table"); + const table = document.createElement("table"); table.className = "settings " + (settings.displayDescriptions? "display": "hide") + "Descriptions"; settings.on("displayDescriptions", function(){ table.className = "settings " + (settings.displayDescriptions? "display": "hide") + "Descriptions"; }); - var tableContainer = document.createElement("div"); + const tableContainer = document.createElement("div"); tableContainer.classList = "tabContents"; groupTabs.appendChild(tableContainer); @@ -277,41 +286,6 @@ const {hide: hideContainer, expand: expandContainer} = settings.getContainers(); - const addGroup = function addGroup(groupDefinition){ - const sections = []; - const group = { - select: function(){ - groups.querySelectorAll(".selected").forEach(function(group){ - group.classList.remove("selected"); - }); - table.querySelectorAll("tbody").forEach(function(section){ - section.classList.remove("selectedGroup"); - }); - sections.forEach(function(section){ - section.node.classList.add("selectedGroup"); - }); - name.classList.add("selected"); - }, - addSection: function(sectionDefinition){ - const section = addSection(sectionDefinition.name); - sections.push(section); - return section; - } - }; - - const groupIndex = groups.childNodes.length; - const name = document.createElement("li"); - name.textContent = extension.getTranslation("group_" + groupDefinition.name); - name.className = "groupName group" + groupIndex; - - - name.addEventListener("click", group.select); - - groups.appendChild(name); - - return group; - }; - const addSection = function addSection(name){ const body = document.createElement("tbody"); body.className = "sectionBody"; @@ -356,9 +330,9 @@ }, updateDisplay: function(){ const searchMode = document.body.classList.contains("searching"); - var anyVisible = false; + let anyVisible = false; rows.forEach(function(row){ - var isHidden = row.classList.contains("hidden"); + const isHidden = row.classList.contains("hidden"); if (!isHidden){ if (searchMode){ if (!row.classList.contains("found")){ @@ -382,13 +356,160 @@ return section; }; + const addGroup = function addGroup(groupDefinition){ + const sections = []; + + const groupIndex = groups.childNodes.length; + const name = document.createElement("li"); + name.textContent = extension.getTranslation("group_" + groupDefinition.name); + name.className = "groupName group" + groupIndex; + + const group = { + select: function(){ + groups.querySelectorAll(".selected").forEach(function(group){ + group.classList.remove("selected"); + }); + table.querySelectorAll("tbody").forEach(function(section){ + section.classList.remove("selectedGroup"); + }); + sections.forEach(function(section){ + section.node.classList.add("selectedGroup"); + }); + name.classList.add("selected"); + }, + addSection: function(sectionDefinition){ + const section = addSection(sectionDefinition.name); + sections.push(section); + return section; + } + }; + + + name.addEventListener("click", group.select); + + groups.appendChild(name); + + return group; + }; + const beforeChangeEventListeners = {}; + function setupSetterForDisplay(setting){ + let originalSet = setting.set; + setting.originalSet = originalSet; + if (originalSet){ + const eventListeners = []; + beforeChangeEventListeners[setting.name] = eventListeners; + setting.set = function(...args){ + if (eventListeners.every(function(listener){ + return listener.call(setting, ...args); + })){ + return originalSet.apply(this, args); + } + else { + return false; + } + }; + } + } + + function setupHideForDisplay(setting){ + const display = setting.display; + const hideChangeListeners = []; + setting.setHide = function setHide(value){ + if (hideContainer){ + hideContainer.setHideByName(display.name, value); + if (setting.computeDependencies){ + setting.computeDependencies(); + } + } + }; + setting.onHideChange = function(listener){ + hideChangeListeners.push(listener); + }; + setting.getHide = function getHide(){ + if (hideContainer){ + return hideContainer.getHideByName(display.name); + } + else { + return false; + } + }; + if (hideContainer){ + hideContainer.onHideChange(display.name, function(value){ + if (setting.computeDependencies){ + setting.computeDependencies(); + } + hideChangeListeners.forEach(function(listener){ + listener(value); + }); + }); + } + } + function setupExpandForDisplay(setting){ + const display = setting.display; + const expandChangeListeners = []; + setting.setExpand = function setExpand(value){ + if (expandContainer){ + expandContainer.setExpandByName(display.name, value); + } + }; + setting.onExpandChange = function(listener){ + expandChangeListeners.push(listener); + }; + setting.getExpand = function getExpand(){ + if (expandContainer){ + return expandContainer.getExpandByName(display.name); + } + else { + return false; + } + }; + if (expandContainer){ + expandContainer.onExpandChange(display.name, function(value){ + expandChangeListeners.forEach(function(listener){ + listener(value); + }); + }); + } + } + + function setupComputeDependenciesForDisplay(setting, section, row){ + let displayDependencies = setting.display.displayDependencies || [{}]; + displayDependencies = Array.isArray(displayDependencies)? + displayDependencies: + [displayDependencies]; + setting.computeDependencies = function computeDependencies(){ + logging.verbose("evaluate display dependencies for", setting); + row.classList[( + ( + displayHidden.get() || + !setting.getHide() + ) && + displayDependencies.some(function(displayDependency){ + return Object.keys(displayDependency).every(function(key){ + return displayDependency[key].indexOf(settings[key]) !== -1; + }); + }) + )? "remove": "add"]("hidden"); + section.updateDisplay(); + }; + + displayDependencies.forEach(function(displayDependency){ + Object.keys(displayDependency).forEach(function(name){ + settings.on(name, setting.computeDependencies); + }); + }); + + setting.computeDependencies(); + displayHidden.on(setting.computeDependencies); + } + settingsDisplay.map(function(groupDefinition){ const group = addGroup(groupDefinition); groupDefinition.sections.forEach(function(sectionDefinition){ const section = group.addSection(sectionDefinition); sectionDefinition.settings.forEach(function(display){ - var setting = settings.getDefinition(display.name); + let setting = settings.getDefinition(display.name); if (!setting){ if (display.inputs){ setting = { @@ -419,113 +540,17 @@ if (setting){ setting.display = display; - let originalSet = setting.set; - setting.originalSet = originalSet; - if (originalSet){ - const eventListeners = []; - beforeChangeEventListeners[setting.name] = eventListeners; - setting.set = function(...args){ - if (eventListeners.every(function(listener){ - return listener.call(setting, ...args); - })){ - return originalSet.apply(this, args); - } - else { - return false; - } - }; - } + setupSetterForDisplay(setting); + setupHideForDisplay(setting); + setupExpandForDisplay(setting); - let hideChangeListeners = []; - setting.setHide = function setHide(value){ - if (hideContainer){ - hideContainer.setHideByName(display.name, value); - if (computeDependencies){ - computeDependencies(); - } - } - }; - setting.onHideChange = function(listener){ - hideChangeListeners.push(listener); - }; - setting.getHide = function getHide(){ - if (hideContainer){ - return hideContainer.getHideByName(display.name); - } - else { - return false; - } - }; - if (hideContainer){ - hideContainer.onHideChange(display.name, function(value){ - if (computeDependencies){ - computeDependencies(); - } - hideChangeListeners.forEach(function(listener){ - listener(value); - }); - }); - } - - let expandChangeListeners = []; - setting.setExpand = function setExpand(value){ - if (expandContainer){ - expandContainer.setExpandByName(display.name, value); - } - }; - setting.onExpandChange = function(listener){ - expandChangeListeners.push(listener); - }; - setting.getExpand = function getExpand(){ - if (expandContainer){ - return expandContainer.getExpandByName(display.name); - } - else { - return false; - } - }; - if (expandContainer){ - expandContainer.onExpandChange(display.name, function(value){ - expandChangeListeners.forEach(function(listener){ - listener(value); - }); - }); - } - - var row = optionsGui.createSettingRow(setting); + const row = optionsGui.createSettingRow(setting); settingStrings.getStrings(setting).forEach(function(string){ search.register(string, row); }); section.addRow(row); - if (!display.displayDependencies){ - display.displayDependencies = {}; - } - var displayDependencies = display.displayDependencies; - displayDependencies = Array.isArray(displayDependencies)? - displayDependencies: - [displayDependencies]; - var computeDependencies = function(){ - logging.verbose("evaluate display dependencies for", setting); - row.classList[( - ( - displayHidden.get() || - !setting.getHide() - ) && - displayDependencies.some(function(displayDependency){ - return Object.keys(displayDependency).every(function(key){ - return displayDependency[key].indexOf(settings[key]) !== -1; - }); - }) - )? "remove": "add"]("hidden"); - section.updateDisplay(); - }; - computeDependencies(); - displayDependencies.forEach(function(displayDependency){ - Object.keys(displayDependency).forEach(function(name){ - settings.on(name, computeDependencies); - }); - }); - displayHidden.on(computeDependencies); + + setupComputeDependenciesForDisplay(setting, section, row); } }); }); @@ -538,6 +563,9 @@ return response.json(); }).then(function(manifest){ version.textContent = "Version " + manifest.version; + return; + }).catch(function(error){ + version.textContent = "Unable to get version: " + error; }); document.body.appendChild(version); @@ -567,6 +595,9 @@ if (addException){ settings.set("protectWindow", false, reCaptchaEntry); } + return; + }).catch(function(error){ + logging.warning("Error while adding reCaptcha exception:", error); }); } } @@ -581,8 +612,11 @@ } ).then((reallyShare) => { if (reallyShare){ - this.originalSet(value, ...args); + return this.originalSet(value, ...args); } + return; + }).catch(function(error){ + logging.warning("Unable to set sharePersistentRndBetweenDomains:", error); }); return false; } diff --git a/options/optionsGui.js b/options/optionsGui.js index 8358943..8edeaaa 100644 --- a/options/optionsGui.js +++ b/options/optionsGui.js @@ -4,7 +4,7 @@ (function(){ "use strict"; - var scope; + let scope; if ((typeof exports) !== "undefined"){ scope = exports; } @@ -16,15 +16,15 @@ const logging = require("../lib/logging"); function createDescription(setting){ - var c = document.createElement("div"); + const c = document.createElement("div"); c.className = "content"; - var title = document.createElement("span"); + const title = document.createElement("span"); title.className = "title"; title.textContent = extension.getTranslation(setting.name + "_title"); c.appendChild(title); - var descriptionText = extension.getTranslation(setting.name + "_description"); + let descriptionText = extension.getTranslation(setting.name + "_description"); if (setting.urlSpecific){ const urlSpecificDescription = extension.getTranslation(setting.name + "_urlSpecific"); if (urlSpecificDescription){ @@ -32,11 +32,11 @@ } } if (descriptionText){ - var info = document.createElement("div"); + const info = document.createElement("div"); info.className = "info"; c.appendChild(info); - var description = document.createElement("div"); + const description = document.createElement("div"); description.className = "description"; description.textContent = descriptionText; info.appendChild(description); @@ -45,10 +45,10 @@ } function createSelect(setting){ - var select = document.createElement("select"); + const select = document.createElement("select"); select.dataset.type = typeof setting.defaultValue; setting.options.forEach(function(value){ - var option = document.createElement("option"); + const option = document.createElement("option"); if (typeof value === typeof setting.defaultValue){ option.value = value; if (setting.defaultValue === value){ @@ -65,7 +65,7 @@ return select; } - var inputTypes = { + const inputTypes = { number: { input: function(value){ const input = document.createElement("input"); @@ -121,10 +121,192 @@ }, object: false }; + + function createKeyInput(setting, url){ + const input = document.createElement("table"); + let inSection = false; + setting.keys.forEach(function(key){ + if (setting.display.displayedSection){ + if (typeof key === "object"){ + if (key.level === 1){ + inSection = key.name === setting.display.displayedSection; + return; + } + } + if (!inSection){ + return; + } + } + let row = document.createElement("tr"); + if (typeof key === "object"){ + let cell = document.createElement("td"); + cell.colSpan = 2; + let h = document.createElement("h" + (2 + (key.level || 1))); + h.textContent = key.message? extension.getTranslation(key.message): key.name; + cell.appendChild(h); + row.appendChild(cell); + input.appendChild(row); + return; + } + + let nameCell = document.createElement("td"); + nameCell.textContent = setting.display.replaceKeyPattern? + key.replace(setting.display.replaceKeyPattern, ""): + key; + row.appendChild(nameCell); + + let keyType = inputTypes[typeof setting.defaultKeyValue]; + let keyInput = keyType.input(setting.defaultKeyValue); + + let inputCell = document.createElement("td"); + inputCell.appendChild(keyInput); + row.appendChild(inputCell); + + setting.on(function(){ + const container = setting.get(url); + keyType.updateCallback( + keyInput, + container && container.hasOwnProperty(key)? + container[key]: + setting.defaultKeyValue, + url + ); + }); + keyInput.addEventListener("change", function(){ + const value = keyType.getValue(keyInput); + let container = setting.get(url); + if (!container){ + container = setting.defaultValue; + } + container[key] = value; + if (setting.set(container, url)){ + logging.message("changed setting", setting.name, "(", key, "):", value); + } + else { + container = setting.get(url); + keyType.updateCallback( + keyInput, + container && container.hasOwnProperty(key)? + container[key]: + setting.defaultKeyValue, + url + ); + logging.message("setting", setting.name, "(", key, ") was not changed"); + } + }); + input.appendChild(row); + }); + return input; + } + + function getPopulateUrlTable(setting, type, body){ + return function populateUrlTable({newValue}){ + body.innerHTML = ""; + newValue.forEach(function(entry){ + let row = document.createElement("tr"); + let urlCell = document.createElement("td"); + urlCell.classList.add("url"); + urlCell.addEventListener("click", function(){ + const input = document.createElement("input"); + input.classList.add("urlInput"); + input.style.width = urlCell.clientWidth + "px"; + input.style.height = urlCell.clientHeight + "px"; + urlCell.innerHTML = ""; + urlCell.appendChild(input); + input.title = extension.getTranslation("inputURL"); + input.value = entry.url; + input.focus(); + input.addEventListener("blur", function(){ + const url = input.value.trim(); + if (url){ + entry.url = url; + setting.urlContainer.refresh(); + } + urlCell.removeChild(input); + urlCell.textContent = entry.url; + }); + }); + urlCell.textContent = entry.url; + row.appendChild(urlCell); + let input = createInput(setting, entry.url); + type.updateCallback(input, setting.get(entry.url)); + if (!entry.hasOwnProperty(setting.name)){ + input.classList.add("notSpecifiedForUrl"); + } + let inputCell = document.createElement("td"); + inputCell.appendChild(input); + row.appendChild(inputCell); + let clearCell = document.createElement("td"); + let clearButton = document.createElement("button"); + clearButton.className = "reset"; + clearButton.textContent = "\xD7"; + clearButton.addEventListener("click", function(){ + setting.reset(entry.url); + }); + clearCell.appendChild(clearButton); + row.appendChild(clearCell); + body.appendChild(row); + }); + }; + } + function createUrlSpecificInput(setting, input, type){ + const container = document.createElement("div"); + container.className = "urlValues " + (setting.getExpand()? "expanded": "collapsed"); + container.appendChild(input); + const collapser = document.createElement("button"); + collapser.classList.add("collapser"); + container.appendChild(collapser); + collapser.addEventListener("click", function(){ + setting.setExpand(!setting.getExpand()); + }); + setting.onExpandChange(function(value){ + container.classList[value? "remove": "add"]("collapsed"); + container.classList[value? "add": "remove"]("expanded"); + }); + let urlTable = document.createElement("table"); + let caption = document.createElement("caption"); + caption.textContent = extension.getTranslation(setting.urlContainer.name + "_title"); + urlTable.appendChild(caption); + let body = document.createElement("tbody"); + urlTable.appendChild(body); + let foot = document.createElement("tfoot"); + let footRow = document.createElement("tr"); + let footCell = document.createElement("td"); + footCell.colSpan = 3; + let newInput = document.createElement("input"); + newInput.className = "inputURL"; + newInput.title = extension.getTranslation("inputURL"); + const addURLSetting = function(){ + const url = newInput.value.trim(); + if (url){ + setting.set(setting.get(url), url); + newInput.value = ""; + newInput.focus(); + } + }; + newInput.addEventListener("keypress", function(event){ + if ([10, 13].indexOf(event.keyCode) !== -1){ + addURLSetting(); + } + }); + footCell.appendChild(newInput); + let footPlus = document.createElement("button"); + footPlus.classList.add("add"); + footPlus.textContent = "+"; + footPlus.addEventListener("click", addURLSetting); + footCell.appendChild(footPlus); + footRow.appendChild(footCell); + foot.appendChild(footRow); + urlTable.appendChild(foot); + container.appendChild(urlTable); + setting.urlContainer.on(getPopulateUrlTable(setting, type, body)); + return container; + } + function createInput(setting, url = ""){ - var type = inputTypes[typeof setting.defaultValue]; - var input; + const type = inputTypes[typeof setting.defaultValue]; + let input; if (setting.options){ input = createSelect(setting); } @@ -136,7 +318,7 @@ if (type){ setting.on(function(){type.updateCallback(input, setting.get(url));}, url); input.addEventListener("change", function(){ - var value = type.getValue(input); + const value = type.getValue(input); if (setting.set(value, url)){ logging.message("changed setting", setting.name, ":", value); } @@ -147,211 +329,41 @@ }); } else if (setting.keys){ - input = document.createElement("table"); - let inSection = false; - setting.keys.forEach(function(key){ - if (setting.display.displayedSection){ - if (typeof key === "object"){ - if (key.level === 1){ - inSection = key.name === setting.display.displayedSection; - return; - } - } - if (!inSection){ - return; - } - } - let row = document.createElement("tr"); - if (typeof key === "object"){ - let cell = document.createElement("td"); - cell.colSpan = 2; - let h = document.createElement("h" + (2 + (key.level || 1))); - h.textContent = key.message? extension.getTranslation(key.message): key.name; - cell.appendChild(h); - row.appendChild(cell); - input.appendChild(row); - return; - } - - let nameCell = document.createElement("td"); - nameCell.textContent = setting.display.replaceKeyPattern? - key.replace(setting.display.replaceKeyPattern, ""): - key; - row.appendChild(nameCell); - - let keyType = inputTypes[typeof setting.defaultKeyValue]; - let keyInput = keyType.input(setting.defaultKeyValue); - - let inputCell = document.createElement("td"); - inputCell.appendChild(keyInput); - row.appendChild(inputCell); - - setting.on(function(){ - var container = setting.get(url); - keyType.updateCallback( - keyInput, - container && container.hasOwnProperty(key)? - container[key]: - setting.defaultKeyValue, - url - ); - }); - keyInput.addEventListener("change", function(){ - var value = keyType.getValue(keyInput); - var container = setting.get(url); - if (!container){ - container = setting.defaultValue; - } - container[key] = value; - if (setting.set(container, url)){ - logging.message("changed setting", setting.name, "(", key, "):", value); - } - else { - container = setting.get(url); - keyType.updateCallback( - keyInput, - container && container.hasOwnProperty(key)? - container[key]: - setting.defaultKeyValue, - url - ); - logging.message("setting", setting.name, "(", key, ") was not changed"); - } - }); - input.appendChild(row); - }); + input = createKeyInput(setting, url); } if (setting.urlSpecific && url === ""){ - let container = document.createElement("div"); - container.className = "urlValues " + (setting.getExpand()? "expanded": "collapsed"); - container.appendChild(input); - var collapser = document.createElement("button"); - collapser.classList.add("collapser"); - container.appendChild(collapser); - collapser.addEventListener("click", function(){ - setting.setExpand(!setting.getExpand()); - }); - setting.onExpandChange(function(value){ - container.classList[value? "remove": "add"]("collapsed"); - container.classList[value? "add": "remove"]("expanded"); - }); - let urlTable = document.createElement("table"); - let caption = document.createElement("caption"); - caption.textContent = extension.getTranslation(setting.urlContainer.name + "_title"); - urlTable.appendChild(caption); - let body = document.createElement("tbody"); - urlTable.appendChild(body); - let foot = document.createElement("tfoot"); - let footRow = document.createElement("tr"); - let footCell = document.createElement("td"); - footCell.colSpan = 3; - let newInput = document.createElement("input"); - newInput.className = "inputURL"; - newInput.title = extension.getTranslation("inputURL"); - const addURLSetting = function(){ - var url = newInput.value.trim(); - if (url){ - setting.set(setting.get(url), url); - newInput.value = ""; - newInput.focus(); - } - }; - newInput.addEventListener("keypress", function(event){ - if ([10, 13].indexOf(event.keyCode) !== -1){ - addURLSetting(); - } - }); - footCell.appendChild(newInput); - let footPlus = document.createElement("button"); - footPlus.classList.add("add"); - footPlus.textContent = "+"; - footPlus.addEventListener("click", addURLSetting); - footCell.appendChild(footPlus); - footRow.appendChild(footCell); - foot.appendChild(footRow); - urlTable.appendChild(foot); - container.appendChild(urlTable); - - setting.urlContainer.on(function({newValue}){ - body.innerHTML = ""; - newValue.forEach(function(entry){ - let row = document.createElement("tr"); - let urlCell = document.createElement("td"); - urlCell.classList.add("url"); - urlCell.addEventListener("click", function(){ - var input = document.createElement("input"); - input.classList.add("urlInput"); - input.style.width = urlCell.clientWidth + "px"; - input.style.height = urlCell.clientHeight + "px"; - urlCell.innerHTML = ""; - urlCell.appendChild(input); - input.title = extension.getTranslation("inputURL"); - input.value = entry.url; - input.focus(); - input.addEventListener("blur", function(){ - var url = input.value.trim(); - if (url){ - entry.url = url; - setting.urlContainer.refresh(); - } - urlCell.removeChild(input); - urlCell.textContent = entry.url; - }); - }); - urlCell.textContent = entry.url; - row.appendChild(urlCell); - let input = createInput(setting, entry.url); - type.updateCallback(input, setting.get(entry.url)); - if (!entry.hasOwnProperty(setting.name)){ - input.classList.add("notSpecifiedForUrl"); - } - let inputCell = document.createElement("td"); - inputCell.appendChild(input); - row.appendChild(inputCell); - let clearCell = document.createElement("td"); - let clearButton = document.createElement("button"); - clearButton.className = "reset"; - clearButton.textContent = "\xD7"; - clearButton.addEventListener("click", function(){ - setting.reset(entry.url); - }); - clearCell.appendChild(clearButton); - row.appendChild(clearCell); - body.appendChild(row); - }); - }); - return container; + return createUrlSpecificInput(setting, input, type); } return input || document.createElement("span"); } function createButton(setting){ - var button = document.createElement("button"); + const button = document.createElement("button"); button.textContent = extension.getTranslation(setting.name + "_label"); button.addEventListener("click", setting.action); return button; } function createInteraction(setting){ - var c = document.createElement("div"); + const c = document.createElement("div"); c.className = "content"; - var interaction; + let interaction; if (setting.action){ interaction = createButton(setting); } else if (setting.actions){ interaction = document.createElement("span"); setting.actions.forEach(function(action){ - var button = createButton(action); + const button = createButton(action); interaction.appendChild(button); }); } else if (setting.inputs){ interaction = document.createElement("span"); setting.inputs.forEach(function(inputSetting){ - var input = createInput(inputSetting); + const input = createInput(inputSetting); input.classList.add("multiple" + setting.inputs.length); interaction.appendChild(input); }); @@ -369,10 +381,10 @@ } function createHide(setting){ - var label = document.createElement("label"); + const label = document.createElement("label"); label.className = "content hideContent"; label.title = extension.getTranslation("hideSetting"); - var input = document.createElement("input"); + const input = document.createElement("input"); input.type = "checkbox"; input.className = "hide"; input.checked = setting.getHide(); @@ -384,26 +396,26 @@ }); label.appendChild(input); - var display = document.createElement("span"); + const display = document.createElement("span"); display.className = "display"; label.appendChild(display); return label; } function createSettingRow(setting){ - var tr = document.createElement("tr"); + const tr = document.createElement("tr"); tr.className = "settingRow"; - var hide = document.createElement("td"); + const hide = document.createElement("td"); hide.className = "hideColumn"; hide.appendChild(createHide(setting)); tr.appendChild(hide); - var left = document.createElement("td"); + const left = document.createElement("td"); left.appendChild(createDescription(setting)); tr.appendChild(left); - var right = document.createElement("td"); + const right = document.createElement("td"); right.appendChild(createInteraction(setting)); tr.appendChild(right); @@ -434,7 +446,7 @@ displayHiddenDescription.appendChild(createDescription(displayHidden)); displayHiddenRow.appendChild(displayHiddenDescription); - var displayHiddenInteraction = document.createElement("td"); + const displayHiddenInteraction = document.createElement("td"); displayHiddenInteraction.appendChild(createInteraction(displayHidden)); displayHiddenRow.appendChild(displayHiddenInteraction); tHead.appendChild(displayHiddenRow); diff --git a/options/presets.js b/options/presets.js index e1dd46c..7cb1f18 100644 --- a/options/presets.js +++ b/options/presets.js @@ -12,93 +12,103 @@ const searchParameters = new URLSearchParams(window.location.search); require("./theme").init("presets"); + function buildPresetSettingGui(setting, value){ + function valueToText(value){ + switch (typeof value){ + case "string": + return extension.getTranslation(`${setting}_options.${value}`); + case "boolean": + return value? "\u2713": "\u00D7"; + default: + return value.toString(); + } + } + + const container = document.createElement("li"); + container.textContent = extension.getTranslation(`${setting}_title`) + ": "; + if ((typeof value) === "object"){ + const urlValues = document.createElement("ul"); + Object.keys(value).map(function(url){ + const container = document.createElement("li"); + container.className = "urlValue"; + container.textContent = url + ": " + + valueToText(value[url]) + + " (" + valueToText(settings.get(setting, url)) +")"; + return container; + + }).forEach(function(node){ + urlValues.appendChild(node); + }); + container.appendChild(urlValues); + } + else { + container.appendChild(document.createTextNode( + `${valueToText(value)} (${valueToText(settings.get(setting))})` + )); + } + + return container; + } + + function buildPresetGui(presetName, preset){ + const container = document.createElement("div"); + container.className = "preset " + presetName; + const title = document.createElement("h1"); + title.className = "title"; + title.textContent = extension.getTranslation(`preset_${presetName}_title`); + container.appendChild(title); + + const description = document.createElement("div"); + description.className = "description"; + description.textContent = extension.getTranslation(`preset_${presetName}_description`); + container.appendChild(description); + + const settingsList = document.createElement("ul"); + settingsList.className = "settings"; + container.appendChild(settingsList); + + Object.keys(preset).map(function(settingName){ + return buildPresetSettingGui(settingName, preset[settingName]); + }).forEach(function(node){ + settingsList.appendChild(node); + }); + + if (settingsList.childNodes.length){ + const button = document.createElement("button"); + button.textContent = extension.getTranslation("apply"); + button.addEventListener("click", function(){ + Promise.all(Object.keys(preset).map(function(settingName){ + const value = preset[settingName]; + if ((typeof value) === "object"){ + return Promise.all(Object.keys(value).map(function(url){ + return settings.set(settingName, value[url], url); + })); + } + else { + return settings.set(settingName, value); + } + })).then(function(){ + window.location.reload(); + return; + }).catch(function(error){ + logging.warning("Unable to apply preset:", error); + }); + }); + container.appendChild(button); + } + + return container; + } + Promise.all([ settings.loaded, fetch("presets.json").then(function(data){ return data.json(); }) + // eslint-disable-next-line no-unused-vars ]).then(function([settingsLoaded, presets]){ Object.keys(presets).map(function(presetName){ - const preset = presets[presetName]; - const container = document.createElement("div"); - container.className = "preset " + presetName; - const title = document.createElement("h1"); - title.className = "title"; - title.textContent = extension.getTranslation(`preset_${presetName}_title`); - container.appendChild(title); - - const description = document.createElement("div"); - description.className = "description"; - description.textContent = extension.getTranslation(`preset_${presetName}_description`); - container.appendChild(description); - - const settingsList = document.createElement("ul"); - settingsList.className = "settings"; - container.appendChild(settingsList); - - Object.keys(preset).map(function(settingName){ - function valueToText(value){ - switch (typeof value){ - case "string": - return extension.getTranslation(`${settingName}_options.${value}`); - case "boolean": - return value? "\u2713": "\u00D7"; - default: - return value.toString(); - } - } - - const value = preset[settingName]; - const container = document.createElement("li"); - container.textContent = extension.getTranslation(`${settingName}_title`) + ": "; - if ((typeof value) === "object"){ - const urlValues = document.createElement("ul"); - Object.keys(value).map(function(url){ - var container = document.createElement("li"); - container.className = "urlValue"; - container.textContent = url + ": " + - valueToText(value[url]) + - " (" + valueToText(settings.get(settingName, url)) +")"; - return container; - - }).forEach(function(node){ - urlValues.appendChild(node); - }); - container.appendChild(urlValues); - } - else { - container.appendChild(document.createTextNode( - `${valueToText(value)} (${valueToText(settings.get(settingName))})` - )); - } - - return container; - }).forEach(function(node){ - settingsList.appendChild(node); - }); - - if (settingsList.childNodes.length){ - const button = document.createElement("button"); - button.textContent = extension.getTranslation("apply"); - button.addEventListener("click", function(){ - Promise.all(Object.keys(preset).map(function(settingName){ - const value = preset[settingName]; - if ((typeof value) === "object"){ - return Promise.all(Object.keys(value).map(function(url){ - return settings.set(settingName, value[url], url); - })); - } - else { - return settings.set(settingName, value); - } - })).then(function(){ - window.location.reload(); - }); - }); - container.appendChild(button); - } - - return container; + return buildPresetGui(presetName, presets[presetName]); }).forEach(function(node){ document.body.appendChild(node); }); @@ -131,6 +141,9 @@ document.body.style.fontSize = fontSize + "px"; } } + return; + }).catch(function(error){ + logging.warning("Unable to setup presets:", error); }); document.querySelector("head title").textContent = extension.getTranslation("presets_title"); diff --git a/options/sanitationRules.js b/options/sanitationRules.js index c05da90..f522691 100644 --- a/options/sanitationRules.js +++ b/options/sanitationRules.js @@ -4,7 +4,7 @@ (function(){ "use strict"; - var scope; + let scope; if ((typeof exports) !== "undefined"){ scope = exports; } @@ -84,8 +84,6 @@ {mainFlag: "protectScreen", section: "Screen-API"}, ].forEach(function(api){ if (settings.get(api.mainFlag) !== (api.mainFlagDisabledValue || false)){ - let inSection = false; - let anyActive = false; if (getSectionKeys(api.section).every(function(key){ return protectedFeaturesValue.hasOwnProperty(key) && !protectedFeaturesValue[key]; diff --git a/options/sanitize.js b/options/sanitize.js index 6a3ce3f..7eeb65d 100644 --- a/options/sanitize.js +++ b/options/sanitize.js @@ -9,12 +9,12 @@ require("../lib/theme").init("sanitize"); const sanitationRules = require("./sanitationRules"); - var title = document.createElement("h1"); + const title = document.createElement("h1"); title.className = "title"; title.textContent = extension.getTranslation("sanitation_title"); document.body.appendChild(title); - var description = document.createElement("div"); + const description = document.createElement("div"); description.className = "description"; description.textContent = extension.getTranslation("sanitation_description"); document.body.appendChild(description); diff --git a/options/settingsDisplay.js b/options/settingsDisplay.js index 8b47c41..69c2484 100644 --- a/options/settingsDisplay.js +++ b/options/settingsDisplay.js @@ -1,10 +1,9 @@ /* 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/. */ -/* eslint max-lines: off*/ (function(){ "use strict"; - var settingsDisplay = [ + const settingsDisplay = [ { name: "general", sections: [ diff --git a/options/whitelist.js b/options/whitelist.js index 4f540d6..d238d8c 100644 --- a/options/whitelist.js +++ b/options/whitelist.js @@ -11,13 +11,59 @@ const searchParameters = new URLSearchParams(window.location.search); - var title = document.createElement("h1"); + const title = document.createElement("h1"); title.className = "title"; title.textContent = extension.getTranslation("whitelist_inspection_title"); document.body.appendChild(title); document.querySelector("head title").textContent = title.textContent; + + const whitelistSettings = [ + { + title: extension.getTranslation("whitelist_all_apis"), + name: "blockMode", + whitelistValue: "allow", + protectedValue: "fake" + }, + { + title: extension.getTranslation("section_canvas-api"), + name: "protectedCanvasPart", + whitelistValue: "nothing", + protectedValue: "readout" + }, + { + title: extension.getTranslation("section_audio-api"), + name: "protectAudio", + whitelistValue: false, + protectedValue: true + }, + { + title: extension.getTranslation("section_history-api"), + name: "historyLengthThreshold", + whitelistValue: 10000, + protectedValue: 2 + }, + { + title: extension.getTranslation("section_window-api"), + name: "protectWindow", + whitelistValue: false, + protectedValue: true + }, + { + title: extension.getTranslation("section_DOMRect-api"), + name: "protectDOMRect", + whitelistValue: false, + protectedValue: true + }, + { + title: extension.getTranslation("section_navigator-api"), + name: "protectNavigator", + whitelistValue: false, + protectedValue: true + }, + ]; + settings.onloaded(function(){ const sets = settingContainers.urlContainer.get(); @@ -47,51 +93,6 @@ } } - const whitelistSettings = [ - { - title: extension.getTranslation("whitelist_all_apis"), - name: "blockMode", - whitelistValue: "allow", - protectedValue: "fake" - }, - { - title: extension.getTranslation("section_canvas-api"), - name: "protectedCanvasPart", - whitelistValue: "nothing", - protectedValue: "readout" - }, - { - title: extension.getTranslation("section_audio-api"), - name: "protectAudio", - whitelistValue: false, - protectedValue: true - }, - { - title: extension.getTranslation("section_history-api"), - name: "historyLengthThreshold", - whitelistValue: 10000, - protectedValue: 2 - }, - { - title: extension.getTranslation("section_window-api"), - name: "protectWindow", - whitelistValue: false, - protectedValue: true - }, - { - title: extension.getTranslation("section_DOMRect-api"), - name: "protectDOMRect", - whitelistValue: false, - protectedValue: true - }, - { - title: extension.getTranslation("section_navigator-api"), - name: "protectNavigator", - whitelistValue: false, - protectedValue: true - }, - ]; - const table = document.createElement("table"); whitelistSettings.forEach(function(setting){ const row = document.createElement("tr"); diff --git a/pageAction/domainNotification.js b/pageAction/domainNotification.js index ea859b4..d57dac7 100644 --- a/pageAction/domainNotification.js +++ b/pageAction/domainNotification.js @@ -15,7 +15,7 @@ const addToContainer = function(){ const container = document.getElementById("prints"); container.querySelector("li").textContent = extension.getTranslation("pleaseWait"); - var first = true; + let first = true; return function addToContainer(domainNotification){ if (first){ @@ -105,10 +105,10 @@ this.textNode = function(){ return node; }; - var messageParts = extension.getTranslation(this.messageId).split(/\{url\}/g); + const messageParts = extension.getTranslation(this.messageId).split(/\{url\}/g); node.appendChild(document.createTextNode(messageParts.shift())); while (messageParts.length){ - var urlSpan = document.createElement("span"); + const urlSpan = document.createElement("span"); urlSpan.textContent = this.domain || extension.getTranslation("localFile"); urlSpan.className = "url hasHiddenActions"; urlSpan.appendChild(this.actionsNode()); @@ -116,7 +116,7 @@ node.appendChild(document.createTextNode(messageParts.shift())); } node.appendChild(document.createTextNode(" (")); - var countSpan = document.createElement("span"); + const countSpan = document.createElement("span"); countSpan.className = "count"; countSpan.textContent = "0"; node.appendChild(countSpan); @@ -168,7 +168,7 @@ const domains = new Map(); const domainNotification = function(url, messageId, count = 0, api = ""){ const domain = url.hostname; - var domainNotification = domains.get(domain + messageId); + let domainNotification = domains.get(domain + messageId); if (!domainNotification){ domainNotification = new DomainNotification(url, messageId, count, api); domains.set(domain + messageId, domainNotification); diff --git a/pageAction/gui.js b/pageAction/gui.js index a4a51ce..e485dda 100644 --- a/pageAction/gui.js +++ b/pageAction/gui.js @@ -13,7 +13,7 @@ scope = require.register("./gui", {}); } - const {error, warning, message, notice, verbose, setPrefix: setLogPrefix} = require("../lib/logging"); + const logging = require("../lib/logging"); const extension = require("../lib/extension"); scope.createCollapser = function(){ @@ -23,11 +23,11 @@ }; return function createCollapser(container){ - var collapser = document.createElement("span"); + const collapser = document.createElement("span"); collapser.className = "collapser"; ["more", "less"].forEach(function(type){ - var span = document.createElement("span"); + const span = document.createElement("span"); span.className = type; span.textContent = messages[type]; collapser.appendChild(span); @@ -44,13 +44,13 @@ scope.createActionButtons = function createActionButtons(container, actions, data, horizontal){ - actions.forEach(function(action, i){ - var button = document.createElement("button"); + actions.forEach(function(action){ + const button = document.createElement("button"); button.className = action.name + " action"; button.title = extension.getTranslation(action.name); if (action.isIcon || action.icon){ button.classList.add("isIcon"); - var img = document.createElement("img"); + const img = document.createElement("img"); button.appendChild(img); img.src = "../icons/" + (action.icon || `pageAction-${action.name}.svg`); } @@ -66,8 +66,8 @@ }; scope.modalChoice = function modalChoice(messageText, choices){ - message("open modal choice"); - return new Promise(function(resolve, reject){ + logging.message("open modal choice"); + return new Promise(function(resolve){ document.body.innerHTML = ""; document.body.className = "modal"; document.body.appendChild(document.createTextNode(messageText)); @@ -77,7 +77,7 @@ const button = document.createElement("button"); button.addEventListener("click", function(){ resolve(choice.value || choice); - message("modal choice closed with value", choice.value || choice); + logging.message("modal choice closed with value", choice.value || choice); }); button.appendChild(document.createTextNode(choice.text || choice)); stack.appendChild(button); @@ -87,8 +87,8 @@ }; scope.modalPrompt = function modalPrompt(messageText, defaultValue){ - message("open modal prompt"); - return new Promise(function(resolve, reject){ + logging.message("open modal prompt"); + return new Promise(function(resolve){ document.body.innerHTML = ""; document.body.className = "modal"; document.body.appendChild(document.createTextNode(messageText)); @@ -101,7 +101,7 @@ button.textContent = "OK"; button.addEventListener("click", function(){ resolve(input.value); - message("modal prompt closed with value", input.value); + logging.message("modal prompt closed with value", input.value); }); stack.appendChild(button); document.body.append(stack); diff --git a/pageAction/pageAction.js b/pageAction/pageAction.js index d15d860..37790e6 100644 --- a/pageAction/pageAction.js +++ b/pageAction/pageAction.js @@ -7,22 +7,17 @@ const extension = require("../lib/extension"); const settings = require("../lib/settings"); const {parseErrorStack} = require("../lib/callingStack"); - const {error, warning, message, notice, verbose, setPrefix: setLogPrefix} = require("../lib/logging"); - setLogPrefix("page action script"); + const logging = require("../lib/logging"); + logging.setPrefix("page action script"); const domainNotification = require("./domainNotification"); const Notification = require("./Notification"); const {createActionButtons, modalPrompt, modalChoice} = require("./gui"); const lists = require("../lib/lists"); require("../lib/theme").init("pageAction"); - - Promise.all([ - browser.tabs.query({active: true, currentWindow: true}), - settings.loaded - ]).then(function(values){ - const tabs = values[0]; - - notice("create global action buttons"); + + function registerActionButtons(){ + logging.notice("create global action buttons"); createActionButtons( document.getElementById("globalActions"), @@ -44,7 +39,9 @@ isIcon: true, callback: function(){ settings.set("showNotifications", false).then(function(){ - window.close(); + return window.close(); + }).catch(function(error){ + logging.warning("Unable to disable notifications:", error); }); } } @@ -52,164 +49,130 @@ undefined, true ); - - if (!tabs.length){ - throw new Error("noTabsFound"); - } - else if (tabs.length > 1){ - error(tabs); - throw new Error("tooManyTabsFound"); - } - - function domainOrUrlPicker(domain, urls, selectText, urlInputText){ - const choices = Array.from(urls).map(function(url){ - return { - text: url, - value: "^" + url.replace(/([\\+*?[^\]$(){}=!|.])/g, "\\$1") + "$" + } + + const domainActions = [ + { + name: "ignorelist", + isIcon: true, + callback: function({domain, urls}){ + return domainOrUrlPicker( + domain, + urls, + extension.getTranslation("selectIgnore"), + extension.getTranslation("inputIgnoreURL") + ).then(function(choice){ + if (choice){ + return settings.set("showNotifications", false, choice); + } + return; + }).then(function(){ + return window.close(); + }); + } + }, + { + name: "whitelist", + isIcon: true, + callback: function({domain, urls, api}){ + const whitelistingSettings = { + all: {name: "blockMode", value: "allow"}, + canvas: {name: "protectedCanvasPart", value: "nothing"}, + audio: {name: "protectAudio", value: false}, + domRect: {name: "protectDOMRect", value: false}, + history: {name: "historyLengthThreshold", value: 10000}, + navigator: {name: "protectNavigator", value: false}, + windows: {name: "protectWindow", value: false} + }; - }); - if (domain){ - choices.unshift(domain); + return domainOrUrlPicker( + domain, + urls, + extension.getTranslation("selectWhitelist"), + extension.getTranslation("inputWhitelistURL") + ).then(function(choice){ + if ( + api && + whitelistingSettings[api] + ){ + // eslint-disable-next-line promise/no-nesting + return modalChoice( + extension.getTranslation("selectWhitelistScope"), + [ + { + text: extension.getTranslation("whitelistOnlyAPI") + .replace( + /\{api\}/g, + extension.getTranslation("section_" + api + "-api") + ), + value: api + }, + { + text: extension.getTranslation("whitelistAllAPIs"), + value: "all" + } + ] + ).then(function(selection){ + return {choice, setting: whitelistingSettings[selection]}; + }); + } + else { + return {choice, setting: whitelistingSettings.all}; + } + }).then(function({choice, setting}){ + if (choice){ + return settings.set(setting.name, setting.value, choice); + } + return; + }).then(function(){ + return window.close(); + }); + } + }, + { + name: "whitelistTemporarily", + isIcon: true, + callback: function({domain, urls}){ + return domainOrUrlPicker( + domain, + urls, + extension.getTranslation("selectSessionWhitelist"), + extension.getTranslation("inputSessionWhitelistURL") + ).then(function(choice){ + if (choice){ + return lists.appendTo("sessionWhite", choice); + } + return; + }).then(function(){ + return window.close(); + }); + } + }, + { + name: "inspectWhitelist", + isIcon: true, + callback: function({domain, urls}){ + window.open( + browser.extension.getURL( + "options/whitelist.html?domain=" + + encodeURIComponent(domain) + + "&urls=" + + encodeURIComponent(JSON.stringify(Array.from(urls.values()))) + ), + "_blank" + ); } - return modalChoice( - selectText, - choices - ).then(function(choice){ - if (choice.startsWith("^")){ - return modalPrompt( - urlInputText, - choice - ); - } - else { - return choice; - } - }); } - - verbose("registering domain actions"); - [ - { - name: "ignorelist", - isIcon: true, - callback: function({domain, urls}){ - domainOrUrlPicker( - domain, - urls, - extension.getTranslation("selectIgnore"), - extension.getTranslation("inputIgnoreURL") - ).then(function(choice){ - if (choice){ - settings.set("showNotifications", false, choice).then(function(){ - window.close(); - }); - } - else { - window.close(); - } - }); - } - }, - { - name: "whitelist", - isIcon: true, - callback: function({domain, urls, api}){ - const whitelistingSettings = { - all: {name: "blockMode", value: "allow"}, - canvas: {name: "protectedCanvasPart", value: "nothing"}, - audio: {name: "protectAudio", value: false}, - domRect: {name: "protectDOMRect", value: false}, - history: {name: "historyLengthThreshold", value: 10000}, - navigator: {name: "protectNavigator", value: false}, - windows: {name: "protectWindow", value: false} - - }; - domainOrUrlPicker( - domain, - urls, - extension.getTranslation("selectWhitelist"), - extension.getTranslation("inputWhitelistURL") - ).then(function(choice){ - if ( - api && - whitelistingSettings[api] - ){ - return modalChoice( - extension.getTranslation("selectWhitelistScope"), - [ - { - text: extension.getTranslation("whitelistOnlyAPI") - .replace( - /\{api\}/g, - extension.getTranslation("section_" + api + "-api") - ), - value: api - }, - { - text: extension.getTranslation("whitelistAllAPIs"), - value: "all" - } - ] - ).then(function(selection){ - return {choice, setting: whitelistingSettings[selection]}; - }); - } - else { - return {choice, setting: whitelistingSettings.all}; - } - }).then(function({choice, setting}){ - if (choice){ - settings.set(setting.name, setting.value, choice).then(function(){ - window.close(); - }); - } - else { - window.close(); - } - }); - } - }, - { - name: "whitelistTemporarily", - isIcon: true, - callback: function({domain, urls}){ - domainOrUrlPicker( - domain, - urls, - extension.getTranslation("selectSessionWhitelist"), - extension.getTranslation("inputSessionWhitelistURL") - ).then(function(choice){ - if (choice){ - lists.appendTo("sessionWhite", choice).then(function(){ - window.close(); - }); - } - else { - window.close(); - } - }); - } - }, - { - name: "inspectWhitelist", - isIcon: true, - callback: function({domain, urls}){ - window.open( - browser.extension.getURL( - "options/whitelist.html?domain=" + - encodeURIComponent(domain) + - "&urls=" + - encodeURIComponent(JSON.stringify(Array.from(urls.values()))) - ), - "_blank" - ); - } - } - ].forEach(function(domainAction){ + ]; + function registerDomainActions(){ + logging.verbose("registering domain actions"); + domainActions.forEach(function(domainAction){ domainNotification.addAction(domainAction); }); - - verbose("registering notification actions"); + } + + function registerNotificationActions(){ + logging.verbose("registering notification actions"); [ { name: "displayFullURL", @@ -228,13 +191,60 @@ ].forEach(function(action){ Notification.addAction(action); }); + } + + function domainOrUrlPicker(domain, urls, selectText, urlInputText){ + const choices = Array.from(urls).map(function(url){ + return { + text: url, + value: "^" + url.replace(/([\\+*?[^\]$(){}=!|.])/g, "\\$1") + "$" + }; + }); + if (domain){ + choices.unshift(domain); + } + return modalChoice( + selectText, + choices + ).then(function(choice){ + if (choice.startsWith("^")){ + return modalPrompt( + urlInputText, + choice + ); + } + else { + return choice; + } + }); + } + + Promise.all([ + browser.tabs.query({active: true, currentWindow: true}), + settings.loaded + ]).then(function(values){ + const tabs = values[0]; - var tab = tabs[0]; + if (!tabs.length){ + throw new Error("noTabsFound"); + } + else if (tabs.length > 1){ + logging.error(tabs); + throw new Error("tooManyTabsFound"); + } + + registerActionButtons(); + + registerDomainActions(); + + registerNotificationActions(); + + const tab = tabs[0]; extension.message.on(function(data){ if (data["canvasBlocker-notificationCounter"]){ const url = new URL(data.url); Object.keys(data["canvasBlocker-notificationCounter"]).forEach(function(key){ - const notification = domainNotification( + domainNotification( url, key, data["canvasBlocker-notificationCounter"][key].count, @@ -246,7 +256,7 @@ Array.isArray(data["canvasBlocker-notifications"]) && data["canvasBlocker-notifications"].length ){ - message("got notifications"); + logging.message("got notifications"); const notifications = data["canvasBlocker-notifications"]; let i = 0; const length = notifications.length; @@ -255,13 +265,14 @@ window.clearInterval(tick); } else { - for (var delta = 0; delta < 20 && i + delta < length; delta += 1){ + let delta = 0; + for (; delta < 20 && i + delta < length; delta += 1){ let notification = notifications[i + delta]; - verbose(notification); + logging.verbose(notification); if (settings.ignoredAPIs[notification.api]){ continue; } - verbose(notification); + logging.verbose(notification); notification.url = new URL(notification.url); domainNotification( notification.url, @@ -275,15 +286,16 @@ }, 1); } }); - message("request notifications from tab", tab.id); + logging.message("request notifications from tab", tab.id); browser.tabs.sendMessage( tab.id, { "canvasBlocker-sendNotifications": tab.id } ); - notice("waiting for notifications"); - }).catch(function(e){ - error(e); + logging.notice("waiting for notifications"); + return; + }).catch(function(error){ + error(error); }); }()); \ No newline at end of file diff --git a/test/audioTest.js b/test/audioTest.js index 9bd303f..e691b09 100644 --- a/test/audioTest.js +++ b/test/audioTest.js @@ -43,7 +43,8 @@ crypto.subtle.digest("SHA-256", data).then(function(hash){ hashNode.textContent = byteArrayToHex(hash); - }, function(error){ + return; + }).catch(function(error){ hashNode.textContent = error; }); hashSets[set].appendChild(container); diff --git a/test/detectionTest.js b/test/detectionTest.js index 48a1df1..9b24e47 100644 --- a/test/detectionTest.js +++ b/test/detectionTest.js @@ -1,4 +1,3 @@ -/* eslint no-console: off, max-lines: off */ var addTest = (function(){ "use strict"; @@ -18,8 +17,9 @@ var addTest = (function(){ try { status = func(log)? 1: 2; } - catch (e){ - console.log(e); + catch (error){ + // eslint-disable-next-line no-console + console.log(error); status = 3; } var li = document.createElement("li"); @@ -214,6 +214,7 @@ addTest("property descriptor", function(log){ object: CanvasRenderingContext2D.prototype, name: "getImageData", descriptor: { + // eslint-disable-next-line no-unused-vars value: function getImageData(x, y, w, h){}, writable: true, enumerable: true, @@ -255,12 +256,12 @@ addTest("error provocation 1", function(log){ try{ ctx.getImageData(0, 0, 0, 0); } - catch (err){ + catch (error){ try { - log(err.name); - log(err.toString()); + log(error.name); + log(error.toString()); } - catch (e){ + catch (error){ canvasBlocker = true; } } @@ -277,12 +278,12 @@ addTest("error provocation 2", function(log){ ctx.getImageData(0, 0, 1, 1); log("no error provoked"); } - catch (err){ + catch (error){ try { - log(err.name); - log(err.toString()); + log(error.name); + log(error.toString()); } - catch (e){ + catch (error){ canvasBlocker = true; } } @@ -295,12 +296,12 @@ addTest("error provocation 3", function(log){ try{ CanvasRenderingContext2D.prototype.getImageData.apply(undefined, [0, 0, 1, 1]); } - catch (err){ + catch (error){ try { - log(err.name); - log(err.toString()); + log(error.name); + log(error.toString()); } - catch (e){ + catch (error){ canvasBlocker = true; } } @@ -313,26 +314,26 @@ addTest("error properties", function(log){ try{ CanvasRenderingContext2D.prototype.getImageData.apply(undefined, [0, 0, 1, 1]); } - catch (err){ + catch (error){ try { var name = "TypeError"; - if (err.name !== name && err instanceof TypeError){ - log("Error name wrong. Expected: ", name, "- got:", err.name); + if (error.name !== name && error instanceof TypeError){ + log("Error name wrong. Expected: ", name, "- got:", error.name); canvasBlocker = true; } var start = "@" + location.href.replace(/\.html$/, ".js"); - if (!err.stack.startsWith(start)){ - log("Error stack starts wrong. Expected:", start, "- got :", err.stack.split(/\n/g, 2)[0]); + if (!error.stack.startsWith(start)){ + log("Error stack starts wrong. Expected:", start, "- got :", error.stack.split(/\n/g, 2)[0]); canvasBlocker = true; } var message = "'getImageData' called on an object that " + "does not implement interface CanvasRenderingContext2D."; - if (err.message !== message){ - log("Error message wrong. Expected: ", message, "- got:", err.message); + if (error.message !== message){ + log("Error message wrong. Expected: ", message, "- got:", error.message); canvasBlocker = true; } } - catch (e){ + catch (error){ canvasBlocker = true; } } diff --git a/test/domRectTest.js b/test/domRectTest.js index 7319150..ac8da3d 100644 --- a/test/domRectTest.js +++ b/test/domRectTest.js @@ -21,66 +21,78 @@ return Array.from(doc.querySelectorAll(".testRect")); } + function formatNumber(number){ + const str = number.toString(); + return "" + str.substring(0, str.length - 2) + "" + + str.substring(str.length - 2); + } + + const properties = ["x", "y", "width", "height", "top", "left", "right", "bottom"]; + function performTest(output, callback){ + const rects = getElements().map(function(element){ + return { + name: element.dataset.name, + data: callback(element) + }; + }); + const data = new Float64Array(rects.length * properties.length); + rects.forEach(function(rect, i){ + properties.forEach(function(property, j){ + data[i * properties.length + j] = rect.data[property]; + }); + }); + + crypto.subtle.digest("SHA-256", data) + .then(function(hash){ + output.querySelector(".hash").textContent = byteArrayToHex(hash); + return; + }).catch(function(error){ + output.querySelector(".hash").textContent = "Unable to compute hash: " + error; + }); + + var dataNode = output.querySelector(".data"); + dataNode.innerHTML = "" + + rects.map(function(rect){ + return ""; + }).join("") + + "" + + rects.map(function(rect){ + return ""; + }).join("") + + "" + + properties.map(function(property){ + return "" + rects.map(function(rect){ + return ""; + }).join("") + ""; + }).join("") + + "
" + rect.name + "
hash
" + property + "" + + formatNumber(rect.data[property]) + + "
"; + rects.forEach(function(rect){ + const data = new Float64Array(properties.length); + properties.forEach(function(property, i){ + data[i] = rect.data[property]; + }); + + crypto.subtle.digest("SHA-256", data).then(function(hash){ + dataNode.querySelector( + ".rectHash[data-name=\"" + rect.name + "\"]" + ).textContent = byteArrayToHex(hash); + return; + }).catch(function(error){ + dataNode.querySelector( + ".rectHash[data-name=\"" + rect.name + "\"]" + ).textContent = "Unable to compute hash: " + error; + }); + }); + } + function createTest(title, callback){ - const properties = ["x", "y", "width", "height", "top", "left", "right", "bottom"]; - function performTest(){ - const rects = getElements().map(function(element){ - return { - name: element.dataset.name, - data: callback(element) - }; - }); - const data = new Float64Array(rects.length * properties.length); - rects.forEach(function(rect, i){ - properties.forEach(function(property, j){ - data[i * properties.length + j] = rect.data[property]; - }); - }); - - crypto.subtle.digest("SHA-256", data) - .then(function(hash){ - output.querySelector(".hash").textContent = byteArrayToHex(hash); - }); - - function formatNumber(number){ - const str = number.toString(); - return "" + str.substring(0, str.length - 2) + "" + - str.substring(str.length - 2); - } - var dataNode = output.querySelector(".data"); - dataNode.innerHTML = "" + - rects.map(function(rect){ - return ""; - }).join("") + - "" + - rects.map(function(rect){ - return ""; - }).join("") + - "" + - properties.map(function(property){ - return "" + rects.map(function(rect){ - return ""; - }).join("") + ""; - }).join("") + - "
" + rect.name + "
hash
" + property + "" + - formatNumber(rect.data[property]) + - "
"; - rects.forEach(function(rect){ - const data = new Float64Array(properties.length); - properties.forEach(function(property, i){ - data[i] = rect.data[property]; - }); - - crypto.subtle.digest("SHA-256", data).then(function(hash){ - dataNode.querySelector( - ".rectHash[data-name=\"" + rect.name + "\"]" - ).textContent = byteArrayToHex(hash); - }); - }); - } const output = template.cloneNode(true); output.querySelector(".title").textContent = title; - output.querySelector(".refresh").addEventListener("click", performTest); + output.querySelector(".refresh").addEventListener("click", function(){ + performTest(output, callback); + }); output.querySelector(".performance").addEventListener("click", function(){ let count = 200; let totalCount = 0; @@ -122,7 +134,7 @@ }()); container.appendChild(output); - performTest(); + performTest(output, callback); } iframe.addEventListener("load", function(){ createTest("Element.getClientRects", function(element){ diff --git a/test/firstPossibleCall.js b/test/firstPossibleCall.js index 4ccd42f..01585bc 100644 --- a/test/firstPossibleCall.js +++ b/test/firstPossibleCall.js @@ -1,2 +1,2 @@ -/* eslint no-console: off */ +// eslint-disable-next-line no-console console.log("first possible call"); \ No newline at end of file diff --git a/test/iframeTest.js b/test/iframeTest.js index 6b14935..5c9a63c 100644 --- a/test/iframeTest.js +++ b/test/iframeTest.js @@ -1,6 +1,14 @@ var log = function(){ "use strict"; return function log(...str){ + if (str[str.length - 1] === "match"){ + str.unshift("color: green"); + str.unshift("%cOK"); + } + else if (str[str.length - 1].substr(0, 9) === "missmatch"){ + str.unshift("color: red"); + str.unshift("%cX"); + } // eslint-disable-next-line no-console console.log(...str); }; @@ -32,7 +40,7 @@ function test(window){ // create window canvas var canvas = document.createElement("canvas"); // draw image in window canvas - var ctx = draw(canvas); + draw(canvas); return window.HTMLCanvasElement.prototype.toDataURL.call(canvas); } @@ -55,12 +63,13 @@ function hash(string){ function compare(string1, string2, alwaysOutputHashes){ "use strict"; function outputHashes(message){ - Promise.all([ + return Promise.all([ hash(string1), hash(string2) ]).then(function(hashes){ // eslint-disable-next-line no-console console.log(message, ...hashes); + return; }); } @@ -98,4 +107,9 @@ hash(reference).then(function(hash){ "use strict"; log("reference hash:", hash); + return; +}).catch(function(error){ + "use strict"; + + log("%cX", "color: red", "Unable to compute reference hash:", error); }); \ No newline at end of file diff --git a/test/performanceTest.js b/test/performanceTest.js index 3765653..90d4ec3 100644 --- a/test/performanceTest.js +++ b/test/performanceTest.js @@ -36,6 +36,7 @@ var performTest = function(){ var log = createLog(); log.createLine("test " + name, "h3"); var line = log.createLine(""); + var line2; var time = 0; var time2 = 0; var min = Number.POSITIVE_INFINITY; @@ -80,7 +81,7 @@ var performTest = function(){ } window.setTimeout(run, 10); }); - var line2 = log.createLine(""); + line2 = log.createLine(""); }; }(); diff --git a/test/screenSizeTest.js b/test/screenSizeTest.js index 21945a9..b8cb15f 100644 --- a/test/screenSizeTest.js +++ b/test/screenSizeTest.js @@ -34,6 +34,7 @@ function addConsistencyTest(title, callback){ consistent.textContent = "computing"; callback().then(function(value){ consistent.textContent = value? "OK": "not OK"; + return; }).catch(function(error){ consistent.classList.add("failed"); if (Array.isArray(error)){ @@ -141,6 +142,7 @@ function addResolutionTest(title, callback){ number.textContent = "computing"; callback(type).then(function(value){ number.textContent = value; + return; }).catch(function(error){ number.classList.add("failed"); number.textContent = error; @@ -214,15 +216,14 @@ function searchValue(tester){ return minValue; } else { + // eslint-disable-next-line promise/no-nesting return tester(maxValue).then(function(testResult){ if (testResult.isEqual){ return maxValue; } else { - return Promise.reject( - "Search could not find exact value." + - " It's between " + minValue + " and " + maxValue + "." - ); + throw "Search could not find exact value." + + " It's between " + minValue + " and " + maxValue + "."; } }); } diff --git a/test/settingsLoading.php b/test/settingsLoading.php index 4c92bc4..fd73855 100644 --- a/test/settingsLoading.php +++ b/test/settingsLoading.php @@ -42,8 +42,8 @@ try { var firstFingerprint = fingerPrint(); } - catch (e){ - console.log(new Date(), e); + catch (error){ + console.log(new Date(), error); var firstFingerprint = false; } diff --git a/test/test.js b/test/test.js index 9a4d342..08807ba 100644 --- a/test/test.js +++ b/test/test.js @@ -1,4 +1,3 @@ -/* eslint no-console: off */ (function(){ "use strict"; @@ -24,25 +23,35 @@ container.querySelector(".hash").textContent = hashToString(hashes[0]) + " / " + hashToString(hashes[1]); + return; + }).catch(function(error){ + container.querySelector(".hash").textContent = "Error while calculating hash: " + error; }); container.querySelector(".isPointInPath").textContent = isPointInPath; } if (location.search !== "?notInitial"){ try {show(document.getElementById("top"), topTest());} - catch (e){console.error(e);} + // eslint-disable-next-line no-console + catch (error){console.error(error);} try {show(document.getElementById("iframe"), iframeTest(document.querySelector("#iframe iframe")));} - catch (e){console.error(e);} + // eslint-disable-next-line no-console + catch (error){console.error(error);} try {show(document.getElementById("iframe2"), iframeTest(document.querySelector("#iframe2 iframe")));} - catch (e){console.error(e);} + // eslint-disable-next-line no-console + catch (error){console.error(error);} try {show(document.getElementById("iframe3"), iframeTest(document.querySelector("#iframe3 iframe")));} - catch (e){console.error(e);} + // eslint-disable-next-line no-console + catch (error){console.error(error);} try {show(document.getElementById("iframe4"), dynamicIframeTest1());} - catch (e){console.error(e);} + // eslint-disable-next-line no-console + catch (error){console.error(error);} try {show(document.getElementById("iframe5"), dynamicIframeTest2());} - catch (e){console.error(e);} + // eslint-disable-next-line no-console + catch (error){console.error(error);} try {show(document.getElementById("iframe6"), dynamicIframeTest3());} - catch (e){console.error(e);} + // eslint-disable-next-line no-console + catch (error){console.error(error);} } document.querySelector("#top button").addEventListener("click", function(){ show(document.getElementById("top"), topTest()); diff --git a/test/webGL-Test.js b/test/webGL-Test.js index 0834f12..0ecb9f0 100644 --- a/test/webGL-Test.js +++ b/test/webGL-Test.js @@ -1,6 +1,50 @@ (function(){ "use strict"; + function getParameters(context){ + const parameters = []; + for (var name in context){ + if (name.toUpperCase() === name){ + var value = context.getParameter(context[name]); + if (value !== null){ + parameters.push({name: name, value: value}); + } + } + } + const debugExtension = context.getExtension("WEBGL_debug_renderer_info"); + + for (name in debugExtension){ + if (name.toUpperCase() === name){ + value = context.getParameter(debugExtension[name]); + if (value !== null){ + parameters.push({name: name, value: value}); + } + } + } + var frontParameters = ["VENDOR", "RENDERER", "UNMASKED_VENDOR_WEBGL", "UNMASKED_RENDERER_WEBGL"]; + parameters.sort(function(a, b){ + var frontA = frontParameters.indexOf(a.name); + var frontB = frontParameters.indexOf(b.name); + if (frontA !== -1){ + if (frontB !== -1){ + return frontA - frontB; + } + else { + return -1; + } + } + else { + if (frontB !== -1){ + return 1; + } + else { + return a.name < b.name? -1: 1; + } + } + }); + return parameters; + } + ["webgl", "webgl2"].forEach(function(context, index){ var output = document.createElement("div"); document.getElementById("output").appendChild(output); @@ -22,46 +66,8 @@ values[pixels[i]] = (values[pixels[i]] || 0) + 1; max = Math.max(max, values[pixels[i]]); } - const parameters = []; - for (var name in gl){ - if (name.toUpperCase() === name){ - var value = gl.getParameter(gl[name]); - if (value !== null){ - parameters.push({name: name, value: value}); - } - } - } - const debugExtension = gl.getExtension("WEBGL_debug_renderer_info"); - for (name in debugExtension){ - if (name.toUpperCase() === name){ - value = gl.getParameter(debugExtension[name]); - if (value !== null){ - parameters.push({name: name, value: value}); - } - } - } - var frontParameters = ["VENDOR", "RENDERER", "UNMASKED_VENDOR_WEBGL", "UNMASKED_RENDERER_WEBGL"]; - parameters.sort(function(a, b){ - var frontA = frontParameters.indexOf(a.name); - var frontB = frontParameters.indexOf(b.name); - if (frontA !== -1){ - if (frontB !== -1){ - return frontA - frontB; - } - else { - return -1; - } - } - else { - if (frontB !== -1){ - return 1; - } - else { - return a.name < b.name? -1: 1; - } - } - }); + const parameters = getParameters(gl); if (context === "webgl2"){ var parameterOutput = document.createElement("table"); document.getElementById("parameters").appendChild(parameterOutput); @@ -87,11 +93,14 @@ (max !== 3 * values[255]? "": "not ") + "supported " + "(parameter hash: " + hash + ")"; output.title = JSON.stringify(values); + return; + }).catch(function(error){ + output.textContent = "Error while calculating hash: " + error; }); } - catch (e){ + catch (error){ output.textContent = context + ": ERROR"; - output.title = e; + output.title = error; } }); }()); \ No newline at end of file