diff --git a/canvasblocker.xpi b/canvasblocker.xpi index fe413c6..eca7d16 100644 Binary files a/canvasblocker.xpi and b/canvasblocker.xpi differ diff --git a/data/inject.js b/data/inject.js index 9ae094d..4c80cfa 100644 --- a/data/inject.js +++ b/data/inject.js @@ -1,8 +1,78 @@ +/* global self, window, console, unsafeWindow */ (function(){ "use strict"; - var getContext = unsafeWindow.HTMLCanvasElement.prototype.getContext; + var originalGetContext = unsafeWindow.HTMLCanvasElement.prototype.getContext; + var originalToDataURL = unsafeWindow.HTMLCanvasElement.prototype.toDataURL; + var originalGetImageData = unsafeWindow.CanvasRenderingContext2D.prototype.getImageData; var askFunctionName = Math.random().toString(16); + + 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)){ @@ -14,24 +84,14 @@ function block(force){ if (force || !checkPDF("block")){ - // consoe.log("block"); - delete unsafeWindow.HTMLCanvasElement.prototype[askFunctionName]; - unsafeWindow.HTMLCanvasElement.prototype.getContext = null; + setAPIFunctions(null, null, null); } } - function askVisible(force){ + function askVisible(force, askOnlyOnce){ if (force || !checkPDF("askVisible")){ - - Object.defineProperty( - unsafeWindow.HTMLCanvasElement.prototype, - askFunctionName, - { - value: getContext, - enumerable: false - } - ); - unsafeWindow.HTMLCanvasElement.prototype.getContext = new unsafeWindow.Function( + setAPIFunctions( + askOnlyOnce? function(){ if (this.parentNode){ var oldBorder = this.style.border; @@ -41,11 +101,37 @@ // catch (e){ // console.log(e.stack.split(/\s*(?:-?>|@)\s*/)); // } - var allow = confirm(confirmText); + var allow = this[askFunctionName].confirm.call(window, confirmText); this.style.border = oldBorder; if (allow){ - this.getContext = this["askFunctionName"]; - return this["askFunctionName"].apply(this, arguments); + 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; + } + } + 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; @@ -55,25 +141,16 @@ else { return null; } - }.toString() - .replace(/^function\s*\(\)\s*\{|\}\s*$/g, "") - .replace(/askFunctionName/g, askFunctionName) - .replace(/askForPermission/g, _("askForPermission")) + }, + true, + true ); } } - function askInvisible(force){ + function askInvisible(force, askOnlyOnce){ if (force || !checkPDF("askInvisible")){ - - Object.defineProperty( - unsafeWindow.HTMLCanvasElement.prototype, - askFunctionName, - { - value: getContext, - enumerable: false - } - ); - unsafeWindow.HTMLCanvasElement.prototype.getContext = new unsafeWindow.Function( + setAPIFunctions( + askOnlyOnce? function(){ var oldBorder = this.style.border; this.style.border = "2px dashed red"; @@ -81,32 +158,63 @@ this.parentNode? "askForPermission": "askForInvisiblePermission"; - var allow = confirm(confirmText); + var allow = this[askFunctionName].confirm.call(window, confirmText); this.style.border = oldBorder; if (allow){ - this.getContext = this["askFunctionName"]; - return this["askFunctionName"].apply(this, arguments); + 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; } - }.toString() - .replace(/^function\s*\(\)\s*\{|\}\s*$/g, "") - .replace(/askFunctionName/g, askFunctionName) - .replace(/askForPermission/g, _("askForPermission")) - .replace(/askForInvisiblePermission/g, _("askForInvisiblePermission")) + }, + 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; + } ); } } function unblock(){ - // console.log("unblock"); - unsafeWindow.HTMLCanvasElement.prototype.getContext = getContext; + setAPIFunctions(true, true, true); } var _ = function(name){ return _[name] || name; - } + }; self.port.on("setTranslation", function(name, translation){ _[name] = translation; }); @@ -115,6 +223,7 @@ 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 }()); diff --git a/lib/main.js b/lib/main.js index 1c3e0f3..622907e 100644 --- a/lib/main.js +++ b/lib/main.js @@ -85,54 +85,67 @@ // } function checkWorker(worker){ var url = new URL(worker.url); + var mode = "block"; switch (prefs.blockMode){ case "blockEverything": - worker.port.emit("block"); + mode = "block"; break; case "allowOnlyWhiteList": if (whiteList.match(url)){ - worker.port.emit("unblock"); + mode = "unblock"; } else { - worker.port.emit("block"); + mode = "block"; } break; case "askVisible": if (whiteList.match(url)){ - worker.port.emit("unblock"); + mode = "unblock"; } else if (blackList.match(url)){ - worker.port.emit("block"); + mode = "block"; } else { - worker.port.emit("askVisible"); + mode = "askVisible"; } break; case "askInvisible": if (whiteList.match(url)){ - worker.port.emit("unblock"); + mode = "unblock"; } else if (blackList.match(url)){ - worker.port.emit("block"); + mode = "block"; } else { - worker.port.emit("askInvisible"); + 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)){ - worker.port.emit("block"); + mode = "block"; } else { - worker.port.emit("unblock"); + mode = "unblock"; } break; case "allowEverything": - worker.port.emit("unblock"); + mode = "unblock"; break; default: - console.log("Unknown blocking mode."); + console.log("Unknown blocking mode. Default to block everything."); } + worker.port.emit(mode, false, prefs.askOnlyOnce); } @@ -151,7 +164,7 @@ this.emit("unblock"); } else { - this.emit(blocking, true); + this.emit(blocking, true, prefs.askOnlyOnce); } }); worker.port.emit("setTranslation", "askForPermission", _("askForPermission")); diff --git a/locale/de-DE.properties b/locale/de-DE.properties index 423830a..2f1dd5c 100644 --- a/locale/de-DE.properties +++ b/locale/de-DE.properties @@ -11,9 +11,13 @@ 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.block readout API= Auslese-API blockieren blockMode_options.block only black list= nur Einträge der Blacklist blockieren blockMode_options.allow everything= alles erlauben +askOnlyOnce_title= Nur einmal nachfragen +askOnlyOnce_description= Wenn eine Seite öfters versucht, die -API abzurufen, erscheint jedes mal eine Nachfrage. Mit diesem Schalter wird pro Seitenbesuch nur einmal nachgefragt. Bei manchen Seiten kann es trotzdem zu mehrmaligem Nachfragen kommen. + 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. diff --git a/locale/en-US.properties b/locale/en-US.properties index ba574d5..2b7bb73 100644 --- a/locale/en-US.properties +++ b/locale/en-US.properties @@ -11,9 +11,13 @@ 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.block readout API= block readout API blockMode_options.block only black list= block only black list blockMode_options.allow everything= allow everything +askOnlyOnce_title= Ask only once +askOnlyOnce_description= If a page tries to access the -API several times a confirm message will appear every time. This switch tries to make only one confirmation. Never the less on some pages there will be more. + 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. diff --git a/package.json b/package.json index 8911a93..33ff05b 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,10 @@ "value": "askInvisible", "label": "ask for permision for invisible " }, + { + "value": "blockReadout", + "label": "block readout API" + }, { "value": "blockOnlyBlackList", "label": "block only black list" @@ -48,6 +52,12 @@ } ] }, + { + "name": "askOnlyOnce", + "title": "Ask only once", + "type": "bool", + "value": false + }, { "name": "allowPDFCanvas", "title": "Allow canvas in PDFs", @@ -57,6 +67,6 @@ ], "author": "Korbinian Kapsner", "license": "MPL 2.0", - "version": "0.1.3", + "version": "0.1.4", "permissions": {"private-browsing": true} }