diff --git a/canvasblocker.xpi b/canvasblocker.xpi index eca7d16..20f48ae 100644 Binary files a/canvasblocker.xpi and b/canvasblocker.xpi differ diff --git a/data/inject.js b/data/inject.js index 4c80cfa..37c39d5 100644 --- a/data/inject.js +++ b/data/inject.js @@ -2,215 +2,94 @@ (function(){ "use strict"; - var originalGetContext = unsafeWindow.HTMLCanvasElement.prototype.getContext; - var originalToDataURL = unsafeWindow.HTMLCanvasElement.prototype.toDataURL; - var originalGetImageData = unsafeWindow.CanvasRenderingContext2D.prototype.getImageData; - var askFunctionName = Math.random().toString(16); + var blockMode = { + getContext: { + status: "block", + askStatus: { + askOnce: false, + alreadyAsked: false, + answer: null + } + }, + readAPI: { + status: "allow" + } + }; - function transformFunctionToUnsafeWindow(func, name){ - var funcString = func.toString(); - var parameter = funcString.match(/^function\s*\(([^\)]*)\)\s*\{/)[1]; - funcString = funcString - .replace(/^function\s*\(([^\)]*)\)\s*\{|\}\s*$/g, "") - .replace(/(^|\n|\r)\t{3}/g, "$1") - .replace(/askFunctionName/g, JSON.stringify(askFunctionName)) - .replace(/askForPermission/g, _("askForPermission")) - .replace(/askForInvisiblePermission/g, _("askForInvisiblePermission")); - var unsafeFunction = new unsafeWindow.Function(parameter, funcString); - unsafeFunction.toString = - unsafeFunction.toLocaleString = - unsafeFunction.toSource = new unsafeWindow.Function( - "return \"function " + name + "() {\\n [native code]\\n}\";" - ); - return unsafeFunction; - } - function setAPIFunctions(getContext, toDataURL, getImageData){ - delete unsafeWindow.HTMLCanvasElement.prototype[askFunctionName]; - if (getContext){ - if (getContext === true){ - getContext = originalGetContext; - } - else { - var secretObject = new unsafeWindow.Object(); - Object.defineProperties( - secretObject, - { - getContext: {value: originalGetContext}, - confirm: {value: unsafeWindow.confirm} - } - ); - Object.defineProperty( - unsafeWindow.HTMLCanvasElement.prototype, - askFunctionName, - { - value: secretObject, - configurable: true, - enumerable: false - } - ); - getContext = transformFunctionToUnsafeWindow(getContext, "getContext"); - } - } - unsafeWindow.HTMLCanvasElement.prototype.getContext = getContext; - - if (toDataURL){ - if (toDataURL === true){ - toDataURL = originalToDataURL; - } - else { - toDataURL = transformFunctionToUnsafeWindow(toDataURL, "toDataURL"); - } - } - unsafeWindow.HTMLCanvasElement.prototype.toDataURL = toDataURL; - - if (getImageData){ - if (getImageData === true){ - getImageData = originalGetImageData; - } - else { - getImageData = transformFunctionToUnsafeWindow(getImageData, "getImageData"); - } - } - unsafeWindow.CanvasRenderingContext2D.prototype.getImageData = getImageData; - } - - function checkPDF(blocking){ - if (document.contentType.match(/\/pdf$/i)){ - self.port.emit("isPDF", blocking); - return true; - } - return false; - } - - function block(force){ - if (force || !checkPDF("block")){ - setAPIFunctions(null, null, null); - } - } - - function askVisible(force, askOnlyOnce){ - if (force || !checkPDF("askVisible")){ - setAPIFunctions( - askOnlyOnce? - function(){ - if (this.parentNode){ - var oldBorder = this.style.border; - this.style.border = "2px dashed red"; - var confirmText = "askForPermission"; - // try {throw new Error();} - // catch (e){ - // console.log(e.stack.split(/\s*(?:-?>|@)\s*/)); - // } - var allow = this[askFunctionName].confirm.call(window, confirmText); - this.style.border = oldBorder; - if (allow){ - HTMLCanvasElement.prototype.getContext = this[askFunctionName].getContext; - delete HTMLCanvasElement.prototype[askFunctionName]; - return this.getContext.apply(this, arguments); + var undef; + var originalGetContext = unsafeWindow.HTMLCanvasElement.prototype.getContext; + Object.defineProperty( + unsafeWindow.HTMLCanvasElement.prototype, + "getContext", + { + enumerable: true, + configureable: false, + get: exportFunction(function(){ + switch (blockMode.getContext.status){ + case "allow": + // console.log("allow"); + return originalGetContext; + case "ask": + // console.log("ask"); + var status = blockMode.getContext.askStatus; + var allow; + if (status.askOnce && status.alreadyAsked){ + // console.log("already asked"); + allow = status.answer; } else { - HTMLCanvasElement.prototype.getContext = null; - delete HTMLCanvasElement.prototype[askFunctionName]; - return null; + // console.log("asking"); + allow = window.confirm(_("askForPermission")); + status.alreadyAsked = true; + status.answer = allow; } - } - else { - return null; - } - }: - function(){ - if (this.parentNode){ - var oldBorder = this.style.border; - this.style.border = "2px dashed red"; - var confirmText = "askForPermission"; - // try {throw new Error();} - // catch (e){ - // console.log(e.stack.split(/\s*(?:-?>|@)\s*/)); - // } - var allow = this[askFunctionName].confirm.call(window, confirmText); - this.style.border = oldBorder; - if (allow){ - this.getContext = this[askFunctionName].getContext; - return this.getContext.apply(this, arguments); - } - else { - this.getContext = null; - return null; - } - } - else { - return null; - } - }, - true, - true - ); - } - } - function askInvisible(force, askOnlyOnce){ - if (force || !checkPDF("askInvisible")){ - setAPIFunctions( - askOnlyOnce? - function(){ - var oldBorder = this.style.border; - this.style.border = "2px dashed red"; - var confirmText = - this.parentNode? - "askForPermission": - "askForInvisiblePermission"; - var allow = this[askFunctionName].confirm.call(window, confirmText); - this.style.border = oldBorder; - if (allow){ - HTMLCanvasElement.prototype.getContext = this[askFunctionName].getContext; - delete HTMLCanvasElement.prototype[askFunctionName]; - return this.getContext.apply(this, arguments); - } - else { - HTMLCanvasElement.prototype.getContext = null; - delete HTMLCanvasElement.prototype[askFunctionName]; - return null; - } - }: - function(){ - var oldBorder = this.style.border; - this.style.border = "2px dashed red"; - var confirmText = - this.parentNode? - "askForPermission": - "askForInvisiblePermission"; - var allow = this[askFunctionName].confirm.call(window, confirmText); - this.style.border = oldBorder; - if (allow){ - this.getContext = this[askFunctionName].getContext; - return this.getContext.apply(this, arguments); - } - else { - this.getContext = null; - return null; - } - }, - true, - true - ); - } - } - function blockReadout(force){ - if (force || !checkPDF("blockReadout")){ - setAPIFunctions( - true, - function(){ - return "data:image/png;base64"; - }, - function(sx, sy, sw, sh){ - var imageData = this.createImageData(sw, sh); - return imageData; + return allow? originalGetContext: undef; + case "block": + default: + // console.log("block"); + return undef; } - ); + }, unsafeWindow) } - } - function unblock(){ - setAPIFunctions(true, true, true); - } + ); + + var originalToDataURL = unsafeWindow.HTMLCanvasElement.prototype.toDataURL; + Object.defineProperty( + unsafeWindow.HTMLCanvasElement.prototype, + "toDataURL", + { + enumerable: true, + configureable: false, + get: exportFunction(function(){ + switch (blockMode.readAPI.status){ + case "allow": + return originalToDataURL; + case "block": + default: + return undef; + } + }, unsafeWindow) + } + ); + + var originalGetImageData = unsafeWindow.CanvasRenderingContext2D.prototype.getImageData; + Object.defineProperty( + unsafeWindow.CanvasRenderingContext2D.prototype, + "getImageData", + { + enumerable: true, + configureable: false, + get: exportFunction(function(){ + switch (blockMode.readAPI.status){ + case "allow": + return originalGetImageData; + case "block": + default: + return undef; + } + }, unsafeWindow) + } + ); var _ = function(name){ return _[name] || name; @@ -218,12 +97,40 @@ self.port.on("setTranslation", function(name, translation){ _[name] = translation; }); - - block(); - self.port.on("block", block); - self.port.on("askVisible", askVisible); - self.port.on("askInvisible", askInvisible); - self.port.on("blockReadout", blockReadout); - self.port.on("unblock", unblock); - //self.port.on("detach", unblock); // produces memory leak due to the reference to unsafeWindow + + function checkPDF(blocking){ + if (document.contentType.match(/\/pdf$/i)){ + self.port.emit("isPDF", blocking); + return true; + } + return false; + } + + self.port.on("block", function(force){ + if (force || !checkPDF("block")){ + blockMode.getContext.status = "block"; + blockMode.readAPI.status = "allow"; + } + }); + self.port.on("ask", function(force, askOnce){ + if (force || !checkPDF("askVisible")){ + blockMode.getContext.status = "ask"; + blockMode.getContext.askStatus.askOnce = askOnce; + blockMode.readAPI.status = "allow"; + } + }); + self.port.on("blockReadout", function(force){ + if (force || !checkPDF("blockReadout")){ + blockMode.getContext.status = "allow"; + blockMode.readAPI.status = "block"; + } + }); + self.port.on("unblock", function(){ + blockMode.getContext.status = "allow"; + blockMode.readAPI.status = "allow"; + }); + self.port.on("detach", function(force){ + blockMode.getContext.status = "allow"; + blockMode.readAPI.status = "allow"; + }); }()); diff --git a/lib/main.js b/lib/main.js index 622907e..8da65a6 100644 --- a/lib/main.js +++ b/lib/main.js @@ -42,12 +42,18 @@ var self = require("sdk/self"); var pageMod = require("sdk/page-mod"); + var array = require("sdk/util/array"); var preferences = require("sdk/simple-prefs"); var prefs = preferences.prefs; var {URL} = require("sdk/url"); var _ = require("sdk/l10n").get; // preferences + Object.keys(prefs).forEach(function(pref){ + preferences.on(pref, function(){ + workers.forEach(checkWorker); + }); + }); var whiteList; function updateWhiteList(){ whiteList = getDomainRegExpList(prefs.whiteList); @@ -55,7 +61,6 @@ updateWhiteList(); preferences.on("whiteList", function(){ updateWhiteList(); - // workers.forEach(checkWorker); }); var blackList; @@ -65,100 +70,84 @@ updateBlackList(); preferences.on("blackList", function(){ updateBlackList(); - // workers.forEach(checkWorker); }); - - - // preferences.on("blockMode", function(){ - // workers.forEach(checkWorker); - // }); - // preferences.on("allowPDFCanvas", function(){ - // workers.forEach(checkWorker); - // }); - // var workers = []; - // function detachWorker(worker, workerArray) { - // var index = workerArray.indexOf(worker); - // if (index !== -1){ - // workerArray.splice(index, 1); - // } - // } function checkWorker(worker){ - var url = new URL(worker.url); - var mode = "block"; - switch (prefs.blockMode){ - case "blockEverything": - mode = "block"; - break; - case "allowOnlyWhiteList": - if (whiteList.match(url)){ - mode = "unblock"; - } - else { + try { + var url = new URL(worker.url); + var mode = "block"; + switch (prefs.blockMode){ + case "blockEverything": mode = "block"; - } - break; - case "askVisible": - if (whiteList.match(url)){ + break; + case "allowOnlyWhiteList": + if (whiteList.match(url)){ + mode = "unblock"; + } + else { + mode = "block"; + } + break; + case "ask": + if (whiteList.match(url)){ + mode = "unblock"; + } + else if (blackList.match(url)){ + mode = "block"; + } + else { + mode = "ask"; + } + break; + case "blockReadout": + if (whiteList.match(url)){ + mode = "unblock"; + } + else if (blackList.match(url)){ + mode = "block"; + } + else { + mode = "blockReadout"; + } + break; + case "blockOnlyBlackList": + if (blackList.match(url)){ + mode = "block"; + } + else { + mode = "unblock"; + } + break; + case "allowEverything": mode = "unblock"; - } - else if (blackList.match(url)){ - mode = "block"; - } - else { - mode = "askVisible"; - } - break; - case "askInvisible": - if (whiteList.match(url)){ - mode = "unblock"; - } - else if (blackList.match(url)){ - mode = "block"; - } - else { - mode = "askInvisible"; - } - break; - case "blockReadout": - if (whiteList.match(url)){ - mode = "unblock"; - } - else if (blackList.match(url)){ - mode = "block"; - } - else { - mode = "blockReadout"; - } - break; - case "blockOnlyBlackList": - if (blackList.match(url)){ - mode = "block"; - } - else { - mode = "unblock"; - } - break; - case "allowEverything": - mode = "unblock"; - break; - default: - console.log("Unknown blocking mode. Default to block everything."); + break; + default: + console.log("Unknown blocking mode. Default to block everything."); + } + worker.port.emit(mode, false, prefs.askOnlyOnce); + } + catch (e){ + console.log("Error updating " + worker.url + ": " + e.message); } - worker.port.emit(mode, false, prefs.askOnlyOnce); } - + var workers = []; pageMod.PageMod({ include: "*", contentScriptWhen: "start", contentScriptFile: self.data.url("inject.js"), onAttach: function(worker){ - // workers.push(worker); - // worker.on("detach", function(){ - // detachWorker(this, workers); - // }); + array.add(workers, worker); + worker.on("pageshow", function(){ + array.add(workers, this); + }); + worker.on("pagehide", function(){ + array.remove(workers, this); + }); + worker.on("detach", function(){ + array.remove(workers, this); + }); worker.port.on("isPDF", function(blocking){ if (prefs.allowPDFCanvas){ this.emit("unblock"); @@ -168,7 +157,6 @@ } }); worker.port.emit("setTranslation", "askForPermission", _("askForPermission")); - worker.port.emit("setTranslation", "askForInvisiblePermission", _("askForInvisiblePermission")); checkWorker(worker); }, }); diff --git a/locale/de-DE.properties b/locale/de-DE.properties index 2f1dd5c..6d0ff55 100644 --- a/locale/de-DE.properties +++ b/locale/de-DE.properties @@ -9,8 +9,7 @@ blockMode_description= blockMode_options.block everything= alles blockieren blockMode_options.allow only white list= nur Einträge der Whitelist erlauben -blockMode_options.ask for permission for visible = bei sichtbaren um Erlaubnis fragen -blockMode_options.ask for permision for invisible = bei unsichtbaren um Erlaubnis fragen +blockMode_options.ask for permission= um Erlaubnis fragen blockMode_options.block readout API= Auslese-API blockieren blockMode_options.block only black list= nur Einträge der Blacklist blockieren blockMode_options.allow everything= alles erlauben @@ -21,5 +20,4 @@ askOnlyOnce_description= Wenn eine Seite öfters versucht, die -API abzu allowPDFCanvas_title= in PDFs erlauben allowPDFCanvas_description= Die native pdf.js verwendet um den Inhalt von PDFs anzuzeigen. Wenn dies nicht markiert ist, werden viele Nachfragedialoge erscheinen oder die PDF Ansicht nicht funktionieren. -askForPermission= Wollen Sie das rot umrandete erlauben? -askForInvisiblePermission= Wollen Sie ein verstecktes erlauben? \ No newline at end of file +askForPermission= Wollen Sie erlauben? \ No newline at end of file diff --git a/locale/en-US.properties b/locale/en-US.properties index 2b7bb73..8ba708e 100644 --- a/locale/en-US.properties +++ b/locale/en-US.properties @@ -9,8 +9,7 @@ blockMode_description= blockMode_options.block everything= block everything blockMode_options.allow only white list= allow only white list -blockMode_options.ask for permission for visible = ask for permission for visible -blockMode_options.ask for permision for invisible = ask for permision for invisible +blockMode_options.ask for permission= ask for permission blockMode_options.block readout API= block readout API blockMode_options.block only black list= block only black list blockMode_options.allow everything= allow everything @@ -21,5 +20,4 @@ askOnlyOnce_description= If a page tries to access the -API several time allowPDFCanvas_title= Allow canvas in PDFs allowPDFCanvas_description= The native pdf.js uses to display the PDF content. If this is unchecked there will lots of annoying ask dialogs or the PDF display will not work. -askForPermission= Do you want to allow the red bordered ? -askForInvisiblePermission= Do you want to allow an invisibe ? \ No newline at end of file +askForPermission= Do you want to allow ? \ No newline at end of file diff --git a/package.json b/package.json index 33ff05b..497dbd1 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "name": "blockMode", "title": "block mode", "type": "menulist", - "value": "askVisible", + "value": "ask", "options": [ { "value": "blockEverything", @@ -31,12 +31,8 @@ "label": "allow only white list" }, { - "value": "askVisible", - "label": "ask for permission for visible " - }, - { - "value": "askInvisible", - "label": "ask for permision for invisible " + "value": "ask", + "label": "ask for permission" }, { "value": "blockReadout", @@ -56,7 +52,7 @@ "name": "askOnlyOnce", "title": "Ask only once", "type": "bool", - "value": false + "value": true }, { "name": "allowPDFCanvas",