From 97e0c6b9cdf56bb866e68ba90bc4969ebf0cbeee Mon Sep 17 00:00:00 2001 From: kkapsner Date: Sun, 6 Sep 2015 12:26:50 +0200 Subject: [PATCH] Removed pageMod and many not needed features. Notifications not working. --- data/inject.js | 320 --------------------------- lib/disableWithoutDocumentElement.js | 49 ---- lib/lists.js | 35 +++ lib/main.js | 264 ++++------------------ lib/modifiedAPI.js | 101 +++++++++ lib/notifications.js | 100 +++++++++ lib/sharedFunctions.js | 92 +++++--- package.json | 48 ---- 8 files changed, 348 insertions(+), 661 deletions(-) delete mode 100644 data/inject.js delete mode 100644 lib/disableWithoutDocumentElement.js create mode 100644 lib/lists.js create mode 100644 lib/modifiedAPI.js create mode 100644 lib/notifications.js diff --git a/data/inject.js b/data/inject.js deleted file mode 100644 index 7de1453..0000000 --- a/data/inject.js +++ /dev/null @@ -1,320 +0,0 @@ -/* global self, window, CanvasRenderingContext2D, WebGLRenderingContext, console, unsafeWindow, exportFunction, cloneInto, checkURL, getDomainRegExpList */ -/* 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 settings = { - showCallingFile: false, - showCompleteCallingStack: false - }; - var blockMode = { - getContext: { - name: "getContext", - status: "block", - askText: { - visible: "askForVisiblePermission", - invisible: "askForInvisiblePermission", - nocanvas: "askForPermission" - }, - askStatus: { - askOnce: false, - alreadyAsked: {}, - answer: {} - } - }, - readAPI: { - name: "readAPI", - status: "allow", - askText: { - visible: "askForVisibleReadoutPermission", - invisible: "askForInvisibleReadoutPermission", - nocanvas: "askForReadoutPermission" - }, - askStatus: { - askOnce: false, - alreadyAsked: {}, - answer: {} - } - } - }; - - var undef; - - - // Stack parsing - function parseStackEntry(entry){ - var m = /@(.*):(\d*):(\d*)$/.exec(entry) || ["", entry, "--", "--"]; - return { - url: m[1], - line: m[2], - column: m[3], - raw: entry - }; - } - - // parse calling stack - function errorToCallingStackMsg(error){ - var msg = ""; - var callers = error.stack.trim().split("\n"); - //console.log(callers); - var findme = callers.shift(); // Remove us from the stack - findme = findme.replace(/(:[0-9]+){1,2}$/, ""); // rm line & column - // Eliminate squashed stack. stack may contain 2+ stacks, but why... - var inDoubleStack = false; - callers = callers.filter(function(caller){ - var doubleStackStart = caller.search(findme) !== -1; - inDoubleStack = inDoubleStack || doubleStackStart; - return !inDoubleStack; - }); - msg += "\n\n" + _("sourceOutput") + ": "; - if (settings.showCompleteCallingStack){ - msg += callers.reduce(function(stack, c){ - return stack + "\n\t" + _("stackEntryOutput", parseStackEntry(c)); - }, ""); - } - else{ - msg += _("stackEntryOutput", parseStackEntry(callers[0])); - } - - return msg; - } - - // Check canvas appearance - function canvasAppearance(context){ - var oldBorder = false; - var canvas = false; - var inDOM = null; - if (context){ - if (context.nodeName === "CANVAS"){ - canvas = context; - } - else if ( - context instanceof CanvasRenderingContext2D || - context instanceof WebGLRenderingContext - ){ - canvas = context.canvas; - } - } - if (canvas){ - oldBorder = canvas.style.border; - canvas.style.border = "2px solid red"; - inDOM = canvas.ownerDocument.contains(canvas); - } - return { - canvas: canvas, - askCategory: canvas? (inDOM? "visible": "invisible"): "nocanvas", - get text(){ - var text = canvas? (this.visible? "visible": "invisible"): "nocanvas"; - Object.defineProperty(this, "text", {value: text}); - return text; - }, - inDom: inDOM, - get visible(){ - var visible = inDOM; - if (inDOM){ - canvas.scrollIntoView(); - var rect = canvas.getBoundingClientRect(); - var foundEl = document.elementFromPoint(rect.left + rect.width / 2, rect.top + rect.height / 2); - visible = (foundEl === canvas); - } - Object.defineProperty(this, "visible", {value: visible}); - return visible; - }, - reset: function(){ - if (canvas){ - canvas.style.border = oldBorder; - } - } - }; - } - - function getFakeCanvas(original){ - var imageData = changedFunctions.getImageData.fake(0, 0, this.width, this.height); - var canvas = this.cloneNode(true); - var context = canvas.getContext("2d"); - context.putImageData(imageData, 0, 0); - return canvas; - } - // changed functions and their fakes - var changedFunctions = { - getContext: { - mode: blockMode.getContext, - object: unsafeWindow.HTMLCanvasElement - }, - toDataURL: { - mode: blockMode.readAPI, - object: unsafeWindow.HTMLCanvasElement, - fake: function toDataURL(){ - return window.HTMLCanvasElement.prototype.toDataURL.apply(getFakeCanvas(this), arguments); - } - }, - toBlob: { - mode: blockMode.readAPI, - object: unsafeWindow.HTMLCanvasElement, - fake: function toBlob(callback){ - window.HTMLCanvasElement.prototype.toBlob.apply(getFakeCanvas(this), arguments); - }, - exportOptions: {allowCallbacks: true} - }, - mozGetAsFile: { - mode: blockMode.readAPI, - object: unsafeWindow.HTMLCanvasElement - }, - getImageData: { - mode: blockMode.readAPI, - object: unsafeWindow.CanvasRenderingContext2D, - fake: function getImageData(sx, sy, sw, sh){ - sw = parseInt(sw, 10); - sh = parseInt(sh, 10); - var l = sw * sh * 4; - var data = new Uint8ClampedArray(l); - for (var i = 0; i < l; i += 1){ - data[i] = Math.floor( - Math.random() * 256 - ); - } - var imageData = new window.ImageData(sw, sh); - imageData.data.set(cloneInto(data, unsafeWindow)); - return imageData; - } - }, - readPixels: { - mode: blockMode.readAPI, - object: unsafeWindow.WebGLRenderingContext, - fake: function readPixels(x, y, width, height, format, type, pixels){ - // fake not working due to XRay copy restrictions... - // for (var i = 0; i < pixels.length; i += 1){ - // pixels[i] = Math.floor( - // Math.random() * 256 - // ); - // } - } - } - }; - - // do the replacements - Object.keys(changedFunctions).forEach(function(name){ - var changedFunction = changedFunctions[name]; - var original = changedFunction.object.prototype[name]; - var fake = changedFunction.fake? - exportFunction( - changedFunction.fake, - unsafeWindow, - changedFunction.exportOptions - ): - undef; - Object.defineProperty( - changedFunction.object.prototype, - name, - { - enumerable: true, - configureable: false, - get: exportFunction(function(){ - var status = changedFunction.mode.status; - var callingStackMsg = errorToCallingStackMsg(new Error()); - if (status === "ask"){ - var askStatus = changedFunction.mode.askStatus; - var appearance = canvasAppearance(this); - if (askStatus.askOnce && askStatus.alreadyAsked[appearance.askCategory]){ - // already asked - status = askStatus.answer[appearance.askCategory]; - } - else { - // asking - var msg = _(changedFunction.mode.askText[appearance.text]); - if (settings.showCallingFile){ - msg += callingStackMsg; - } - status = window.confirm(msg) ? "allow": "block"; - askStatus.alreadyAsked[appearance.text] = true; - askStatus.answer[appearance.text] = status; - appearance.reset(); - } - } - self.port.emit("accessed " + changedFunction.mode.name, status, callingStackMsg); - switch (status){ - case "allow": - return original; - case "fake": - return fake; - //case "block": - default: - return undef; - } - }, unsafeWindow) - } - ); - }); - - // Translation - var _ = function(name, replace){ - var str = self.options.translations[name] || name; - if (replace){ - // replace generic content in the transation by given parameter - Object.keys(replace).forEach(function(name){ - str = str.replace(new RegExp("{" + name + "}", "g"), replace[name]); - }); - } - return str; - }; - - // Communication with main.js - - function setStatus(mode, askOnce){ - switch (mode){ - case "block": - blockMode.getContext.status = "block"; - blockMode.readAPI.status = "block"; - break; - case "ask": - blockMode.getContext.status = "ask"; - blockMode.getContext.askStatus.askOnce = askOnce; - blockMode.readAPI.status = "allow"; - break; - case "blockReadout": - blockMode.getContext.status = "allow"; - blockMode.readAPI.status = "block"; - break; - case "fakeReadout": - blockMode.getContext.status = "allow"; - blockMode.readAPI.status = "fake"; - break; - case "askReadout": - blockMode.getContext.status = "allow"; - blockMode.readAPI.status = "ask"; - blockMode.readAPI.askStatus.askOnce = askOnce; - break; - case "unblock": - blockMode.getContext.status = "allow"; - blockMode.readAPI.status = "allow"; - break; - case "detach": - blockMode.getContext.status = "allow"; - blockMode.readAPI.status = "allow"; - break; - } - } - ["block", "ask", "blockReadout", "fakeReadout", "askReadout", "unblock", "detach"].forEach(function(mode){ - self.port.on(mode, function(askOnce){ - setStatus(mode, askOnce); - }); - }); - - // initial status setting - setStatus( - checkURL( - location, - self.options.blockMode, - getDomainRegExpList(self.options.whiteList), - getDomainRegExpList(self.options.blackList) - ), - self.options.askOnce - ); - - // settings passthrough - self.port.on("set", function(name, value){ - settings[name] = value; - }); -}()); diff --git a/lib/disableWithoutDocumentElement.js b/lib/disableWithoutDocumentElement.js deleted file mode 100644 index 9fdedfb..0000000 --- a/lib/disableWithoutDocumentElement.js +++ /dev/null @@ -1,49 +0,0 @@ -/* 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"; - - const observers = require("sdk/system/events"); - const { when: unload } = require("sdk/system/unload"); - - var classes = { - HTMLCanvasElement: ["getContext", "toDataURL", "toBlob", "mozGetAsFile"], - CanvasRenderingContext2D: ["getImageData"], - WebGLRenderingContext: ["readPixels"] - }; - - var classNames = Object.keys(classes); - - var originalProperties = new WeakMap(); - - function disable({subject: window}){ - var oldProperties = {}; - classNames.forEach(function(className){ - oldProperties[className] = {}; - classes[className].forEach(function(funcName){ - oldProperties[className][funcName] = window.wrappedJSObject[className].prototype[funcName]; - window.wrappedJSObject[className].prototype[funcName] = function(){}; - }); - }); - originalProperties.set(window, oldProperties); - } - - function reset({subject: document}){ - var window = document.defaultView; - var oldProperties = originalProperties.get(window); - if (oldProperties){ - originalProperties.delete(window); - classNames.forEach(function(className){ - classes[className].forEach(function(funcName){ - window.wrappedJSObject[className].prototype[funcName] = oldProperties[className][funcName]; - }); - }); - } - } - - observers.on("content-document-global-created", disable); - unload(() => observers.off("content-document-global-created", disable)); - observers.on("document-element-inserted", reset); - unload(() => observers.off("document-element-inserted", reset)); -}()); \ No newline at end of file diff --git a/lib/lists.js b/lib/lists.js new file mode 100644 index 0000000..5f2fd0d --- /dev/null +++ b/lib/lists.js @@ -0,0 +1,35 @@ +/* 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/. */ + + +var preferences = require("sdk/simple-prefs"); +var prefService = require("sdk/preferences/service"); +var prefs = preferences.prefs; +var sharedFunctions = require("./sharedFunctions"); + +var lists = { + white: [], + ignore: [], + black: [] +}; + +function updateList(type){ + lists[type] = sharedFunctions.getDomainRegExpList(prefs[type + "List"]); +} +Object.keys(lists).forEach(function(type){ + preferences.on(type + "List", function(){ + updateList(type); + }); + updateList(type); +}); + +exports.get = function getList(type){ + return lists[type]; +} +exports.appendTo = function appendToList(type, entry){ + prefs[type + "List"] += (prefs[type + "List"]? ",": "") + entry; + prefService.set("extensions.CanvasBlocker@kkapsner.de.whiteList", prefs[type + "List"]); + updateList(type); +} +exports.update = updateList; \ No newline at end of file diff --git a/lib/main.js b/lib/main.js index 81ac91a..00e703a 100644 --- a/lib/main.js +++ b/lib/main.js @@ -5,234 +5,62 @@ (function(){ "use strict"; require("./stylePreferencePane"); - require("./disableWithoutDocumentElement"); - - var self = require("sdk/self"); - var pageMod = require("sdk/page-mod"); - var array = require("sdk/util/array"); - var preferences = require("sdk/simple-prefs"); - var prefService = require("sdk/preferences/service"); - var prefs = preferences.prefs; - var URL = require("sdk/url").URL; - var _ = require("sdk/l10n").get; - var tabUtils = require("sdk/tabs/utils"); + // require("./disableWithoutDocumentElement"); + const {changedFunctions} = require("./modifiedAPI"); + const {notify} = require("./notifications"); + const lists = require("./lists"); - var sharedFunctions = require("./sharedFunctions"); - var getDomainRegExpList = sharedFunctions.getDomainRegExpList; + const sharedFunctions = require("./sharedFunctions"); // preferences - Object.keys(prefs).forEach(function(pref){ - preferences.on(pref, function(){ - workers.forEach(checkWorker); - }); - }); - var whiteList; - function updateWhiteList(){ - whiteList = getDomainRegExpList(prefs.whiteList); - } - updateWhiteList(); - preferences.on("whiteList", function(){ - updateWhiteList(); - }); - - var blackList; - function updateBlackList(){ - blackList = getDomainRegExpList(prefs.blackList); - } - updateBlackList(); - preferences.on("blackList", function(){ - updateBlackList(); - }); - - var ignoreList; - function updateIgnoreList(){ - ignoreList = getDomainRegExpList(prefs.ignoreList); - } - updateIgnoreList(); - preferences.on("ignoreList", function(){ - updateIgnoreList(); - }); - // preferences for injected file - var preferencesForInjected = ["showCallingFile", "showCompleteCallingStack"]; - preferencesForInjected.forEach(function(name){ - preferences.on(name, function(){ - workers.forEach(function(worker){ - worker.port.emit("set", name, prefs[name]); - }); - }); - }); + const observers = require("sdk/system/events"); + const { when: unload } = require("sdk/system/unload"); + + const preferences = require("sdk/simple-prefs"); + const prefs = preferences.prefs; function checkURL(url){ - return sharedFunctions.checkURL(url, prefs.blockMode, whiteList, blackList); + return sharedFunctions.checkURL(url, prefs.blockMode); } - function checkWorker(worker){ - try { - var mode; - var url = new URL(worker.url); - if ( - (url.protocol === "about:") || - (prefs.allowPDFCanvas && worker.tab && worker.tab.contentType.match(/\/pdf$/i)) - ){ - mode = "unblock"; - } - else { - mode = checkURL(url); - } - worker.port.emit(mode, prefs.askOnlyOnce); - } - catch (e){ - console.log("Error updating " + worker.url + ": " + e.message); - } - } - - var workers = []; - var workerTranslations = { - sourceOutput: _("sourceOutput"), - stackEntryOutput: _("stackEntryOutput") - }; - ["", "Readout"].forEach(function(type){ - ["", "Visible", "Invisible"].forEach(function(visibility){ - var text = "askFor" + visibility + type + "Permission"; - workerTranslations[text] = _(text); - }); - }); + var apiNames = Object.keys(changedFunctions); + var undef; + + function intercept({subject: window}){ + apiNames.forEach(function(name){ + var changedFunction = changedFunctions[name]; + var original = window.wrappedJSObject[changedFunction.object].prototype[name]; - var workerOptions = { - blockMode: checkURL(), - whiteList: prefs.whiteList, - blackList: prefs.blackList, - askOnce: prefs.askOnce, - translations: workerTranslations - }; - preferences.on("blockMode", function(){ - workerOptions.blockMode = checkURL(); - }); - ["whiteList", "blackList", "askOnce"].forEach(function(prefName){ - preferences.on(prefName, function(){ - workerOptions[prefName] = prefs[prefName]; - }); - }); - pageMod.PageMod({ - include: "*", - contentScriptWhen: "start", - contentScriptFile: [ - self.data.url("sharedFunctions.js").replace("/data/", "/lib/"), - self.data.url("inject.js"), - ], - contentScriptOptions: workerOptions, - onAttach: function(worker){ - - 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); - }); - - preferencesForInjected.forEach(function(name){ - worker.port.emit("set", name, prefs[name]); - }); - - checkWorker(worker); - - // display notifications - worker.port.on("accessed readAPI", function(status, callingStackMsg){ - switch (status){ - case "fake": - - var contentURL = new URL(worker.contentURL); - if (prefs.showNotifications && !ignoreList.match(contentURL)){ - var url = contentURL.href; - var domain = contentURL.hostname; - var message = _("fakedReadout").replace(/\{url\}/g, domain); - - var tab = tabUtils.getTabForId(worker.tab.id); - var tabBrowser = tabUtils.getTabBrowserForTab(tab); - var browser = tabUtils.getBrowserForTab(tab); - - var notifyBox = tabBrowser.getNotificationBox(browser); - var notification = notifyBox.getNotificationWithValue("fake-readout"); - if (notification){ - notification.label = message; - } - else { - var buttons = [ - { - label: _("displayFullURL"), - accessKey: "", - callback: function(){ - browser.contentWindow.alert(url); - // only way to prevent closing... see https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XUL/Method/appendNotification#Notification_box_events - throw new Error("Do not close notification."); - } - }, - { - label: _("displayCallingStack"), - accessKey: "", - callback: function(){ - browser.contentWindow.alert(callingStackMsg); - // only way to prevent closing... see https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XUL/Method/appendNotification#Notification_box_events - throw new Error("Do not close notification."); - } - }, - { - label: _("ignorelistDomain"), - accessKey: "", - callback: function(){ - prefs.ignoreList += (prefs.ignoreList? ",": "") + domain; - prefService.set("extensions.CanvasBlocker@kkapsner.de.ignoreList", prefs.ignoreList); - updateIgnoreList(); - } - }, - { - label: _("whitelistURL"), - accessKey: "", - callback: function(){ - prefs.whiteList += (prefs.whiteList? ",": "") + "^" + url.replace(/([\\\+\*\?\[\^\]\$\(\)\{\}\=\!\|\.])/g, "\\$1") + "$"; - prefService.set("extensions.CanvasBlocker@kkapsner.de.whiteList", prefs.whiteList); - updateWhiteList(); - workers.forEach(checkWorker); - } - }, - { - label: _("whitelistDomain"), - accessKey: "", - callback: function(){ - prefs.whiteList += (prefs.whiteList? ",": "") + domain; - prefService.set("extensions.CanvasBlocker@kkapsner.de.whiteList", prefs.whiteList); - updateWhiteList(); - workers.forEach(checkWorker); - } - }, - { - label: _("disableNotifications"), - accessKey: "", - callback: function(){ - prefs.showNotifications = false; - prefService.set("extensions.CanvasBlocker@kkapsner.de.showNotifications", prefs.showNotifications); - } - } - ]; - - var priority = notifyBox.PRIORITY_WARNING_MEDIUM; - notification = notifyBox.appendNotification( - message, - "fake-readout", - "chrome://browser/skin/Info.png", - priority, - buttons - ); + Object.defineProperty( + window.wrappedJSObject[changedFunction.object].prototype, + name, + { + enumerable: true, + configureable: false, + get: function(){ + var status = checkURL(window.location); + if (status === changedFunction.mode){ + var callingStackMsg = sharedFunctions.errorToCallingStackMsg(new Error()); + switch (status){ + case "fakeReadout": + // notify(window, callingStackMsg); + return changedFunction.fake; + //case "block": + default: + return undef; } } - break; + else { + return original; + } + } } - }); - }, + ); + }); + } + + observers.on("content-document-global-created", intercept); + unload(function(){ + observers.off("content-document-global-created", intercept); }); - }()); \ No newline at end of file diff --git a/lib/modifiedAPI.js b/lib/modifiedAPI.js new file mode 100644 index 0000000..04fb640 --- /dev/null +++ b/lib/modifiedAPI.js @@ -0,0 +1,101 @@ +/* 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"; + + function getFakeCanvas(window, original){ + var canvas = original.cloneNode(true); + var context = window.HTMLCanvasElement.prototype.getContext.call(canvas, "2d"); + var imageData = window.CanvasRenderingContext2D.prototype.getImageData.call(context, 0, 0, canvas.width, canvas.height); + var data = imageData.data; + for (var i = 0, l = data.length; i < l; i += 1){ + var value = 0xFF - data[i]; + if (value > 0x80){ + value = value ^ Math.floor(Math.random() * 0x40); + } + else if (value > 0x40){ + value = value ^ Math.floor(Math.random() * 0x20); + } + else if (value > 0x20){ + value = value ^ Math.floor(Math.random() * 0x10); + } + else if (value > 0x10){ + value = value ^ Math.floor(Math.random() * 0x08); + } + else if (value > 0x08){ + value = value ^ Math.floor(Math.random() * 0x04); + } + else if (value > 0x04){ + value = value ^ Math.floor(Math.random() * 0x02); + } + else if (value > 0x02){ + value = value ^ Math.floor(Math.random() * 0x01); + } + data[i] = 0xFF - value; + } + context.putImageData(imageData, 0, 0); + return canvas; + } + function getWindow(canvas){ + console.log(canvas); + return canvas.ownerDocument.defaultView; + } + // changed functions and their fakes + exports.changedFunctions = { + getContext: { + mode: "block", + object: "HTMLCanvasElement" + }, + toDataURL: { + mode: "fakeReadout", + object: "HTMLCanvasElement", + fake: function toDataURL(){ + var window = getWindow(this); + return window.HTMLCanvasElement.prototype.toDataURL.apply(getFakeCanvas(window, this), arguments); + } + }, + toBlob: { + mode: "fakeReadout", + object: "HTMLCanvasElement", + fake: function toBlob(callback){ + var window = getWindow(this); + return window.HTMLCanvasElement.prototype.toBlob.apply(getFakeCanvas(window, this), arguments); + }, + exportOptions: {allowCallbacks: true} + }, + mozGetAsFile: { + mode: "fakeReadout", + object: "HTMLCanvasElement", + mozGetAsFile: function mozGetAsFile(callbak){ + var window = getWindow(this); + return window.HTMLCanvasElement.prototype.mozGetAsFile.apply(getFakeCanvas(window, this), arguments); + } + }, + getImageData: { + mode: "fakeReadout", + object: "CanvasRenderingContext2D", + fake: function getImageData(sx, sy, sw, sh){ + var window = getWindow(this.canvas); + var context = window.HTMLCanvasElement.prototype.getContext.call(getFakeCanvas(window, this.canvas), "2d"); + var data = window.CanvasRenderingContext2D.prototype.getImageData.apply(context, arguments).data; + + var imageData = new window.wrappedJSObject.ImageData(sw, sh); + for (var i = 0, l = data.length; i < l; i += 1){ + imageData.data[i] = data[i]; + } + return imageData; + } + }, + readPixels: { + mode: "fakeReadout", + object: "WebGLRenderingContext", + fake: function readPixels(x, y, width, height, format, type, pixels){ + var window = getWindow(this.canvas); + var context = window.HTMLCanvasElement.prototype.getContext.call(getFakeCanvas(window, this.canvas), "webGL"); + return window.WebGLRenderingContext.prototype.readPixels.apply(context, arguments); + + } + } + }; +}()); \ No newline at end of file diff --git a/lib/notifications.js b/lib/notifications.js new file mode 100644 index 0000000..0265f15 --- /dev/null +++ b/lib/notifications.js @@ -0,0 +1,100 @@ +/* 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/. */ + +var _ = require("sdk/l10n").get; +var preferences = require("sdk/simple-prefs"); +var prefService = require("sdk/preferences/service"); +var prefs = preferences.prefs; +var tabUtils = require("sdk/tabs/utils"); +var windowUtils = require("sdk/window/utils"); +var lists = require("./lists"); +var URL = require("sdk/url").URL; + +exports.notify = function(window, callingStackMsg){ + var contentURL = new URL(window.location); + if (prefs.showNotifications && !lists.get("ignore").match(contentURL)){ + var url = contentURL.href; + var domain = contentURL.hostname; + var message = _("fakedReadout").replace(/\{url\}/g, domain); + + // var tab = tabUtils.getTabForId(worker.tab.id); + // var tabBrowser = tabUtils.getTabBrowserForTab(tab); + // var browser = tabUtils.getBrowserForTab(tab); + var tabBrowser = tabUtils.getTabBrowser(window); + var browser = windowUtils.getOwnerBrowserWindow(window); + + var notifyBox = tabBrowser.getNotificationBox(browser); + var notification = notifyBox.getNotificationWithValue("fake-readout"); + if (notification){ + notification.label = message; + notification.url = url; + notification.domain = domain; + notification.callingStackMsg = callingStackMsg; + } + else { + var buttons = [ + { + label: _("displayFullURL"), + accessKey: "", + callback: function(){ + browser.contentWindow.alert(notification.url); + // only way to prevent closing... see https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XUL/Method/appendNotification#Notification_box_events + throw new Error("Do not close notification."); + } + }, + { + label: _("displayCallingStack"), + accessKey: "", + callback: function(){ + browser.contentWindow.alert(notification.callingStackMsg); + // only way to prevent closing... see https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XUL/Method/appendNotification#Notification_box_events + throw new Error("Do not close notification."); + } + }, + { + label: _("ignorelistDomain"), + accessKey: "", + callback: function(){ + lists.appendTo("ignore", notification.domain); + } + }, + { + label: _("whitelistURL"), + accessKey: "", + callback: function(){ + lists.appendTo("white", "^" + notification.url.replace(/([\\\+\*\?\[\^\]\$\(\)\{\}\=\!\|\.])/g, "\\$1") + "$"); + } + }, + { + label: _("whitelistDomain"), + accessKey: "", + callback: function(){ + lists.appendTo("white", notification.domain); + } + }, + { + label: _("disableNotifications"), + accessKey: "", + callback: function(){ + prefs.showNotifications = false; + prefService.set("extensions.CanvasBlocker@kkapsner.de.showNotifications", prefs.showNotifications); + } + } + ]; + + var priority = notifyBox.PRIORITY_WARNING_MEDIUM; + notification = notifyBox.appendNotification( + message, + "fake-readout", + "chrome://browser/skin/Info.png", + priority, + buttons + ); + notification.url = url; + notification.domain = domain; + notification.callingStackMsg = callingStackMsg; + } + } + +}; \ No newline at end of file diff --git a/lib/sharedFunctions.js b/lib/sharedFunctions.js index 8ae3ba6..ce1ac64 100644 --- a/lib/sharedFunctions.js +++ b/lib/sharedFunctions.js @@ -3,6 +3,23 @@ * 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/. */ +const lists = require("./lists"); +const preferences = require("sdk/simple-prefs"); +const prefs = preferences.prefs; + +// Translation +var translate = require("sdk/l10n").get; +var _ = function(name, replace){ + var str = translate(name) || name; + if (replace){ + // replace generic content in the transation by given parameter + Object.keys(replace).forEach(function(name){ + str = str.replace(new RegExp("{" + name + "}", "g"), replace[name]); + }); + } + return str; +}; + function getDomainRegExpList(domainList){ "use strict"; @@ -44,44 +61,28 @@ function getDomainRegExpList(domainList){ return list; } -function checkURL(url, blockMode, whiteList, blackList){ +function checkURL(url, blockMode){ "use strict"; + if (url.protocol === "about:") { + return "unblock"; + } var mode = "block"; switch (blockMode){ case "blockEverything": mode = "block"; break; - case "allowOnlyWhiteList": - if (url && whiteList.match(url)){ - mode = "unblock"; - } - else { - mode = "block"; - } - break; - case "ask": - case "blockReadout": case "fakeReadout": - case "askReadout": - if (url && whiteList.match(url)){ + if (url && lists.get("white").match(url)){ mode = "unblock"; } - else if (url && blackList.match(url)){ + else if (url && lists.get("black").match(url)){ mode = "block"; } else { mode = blockMode; } break; - case "blockOnlyBlackList": - if (url && blackList.match(url)){ - mode = "block"; - } - else { - mode = "unblock"; - } - break; case "allowEverything": mode = "unblock"; break; @@ -91,8 +92,47 @@ function checkURL(url, blockMode, whiteList, blackList){ return mode; } -try { - exports.getDomainRegExpList = getDomainRegExpList; - exports.checkURL = checkURL; + +// Stack parsing +function parseStackEntry(entry){ + var m = /@(.*):(\d*):(\d*)$/.exec(entry) || ["", entry, "--", "--"]; + return { + url: m[1], + line: m[2], + column: m[3], + raw: entry + }; } -catch(e){} \ No newline at end of file + +// parse calling stack +function errorToCallingStackMsg(error){ + var msg = ""; + var callers = error.stack.trim().split("\n"); + //console.log(callers); + var findme = callers.shift(); // Remove us from the stack + findme = findme.replace(/(:[0-9]+){1,2}$/, ""); // rm line & column + // Eliminate squashed stack. stack may contain 2+ stacks, but why... + var inDoubleStack = false; + callers = callers.filter(function(caller){ + var doubleStackStart = caller.search(findme) !== -1; + inDoubleStack = inDoubleStack || doubleStackStart; + return !inDoubleStack; + }); + msg += "\n\n" + _("sourceOutput") + ": "; + if (prefs.showCompleteCallingStack){ + msg += callers.reduce(function(stack, c){ + return stack + "\n\t" + _("stackEntryOutput", parseStackEntry(c)); + }, ""); + } + else{ + msg += _("stackEntryOutput", parseStackEntry(callers[0])); + } + + return msg; +} + + +exports.getDomainRegExpList = getDomainRegExpList; +exports.checkURL = checkURL; +exports.parseStackEntry = parseStackEntry; +exports.errorToCallingStackMsg = errorToCallingStackMsg; \ No newline at end of file diff --git a/package.json b/package.json index 1713a56..6e158d0 100644 --- a/package.json +++ b/package.json @@ -30,42 +30,12 @@ "value": "fakeReadout", "label": "fake readout API" }, - { - "value": "askReadout", - "label": "ask for readout API permission" - }, - { - "value": "", - "label": "" - }, - { - "value": "blockEverything", - "label": "block everything" - }, - { - "value": "allowOnlyWhiteList", - "label": "allow only white list" - }, - { - "value": "ask", - "label": "ask for permission" - }, - { - "value": "blockOnlyBlackList", - "label": "block only black list" - }, { "value": "allowEverything", "label": "allow everything" } ] }, - { - "name": "askOnlyOnce", - "title": "Ask only once", - "type": "bool", - "value": true - }, { "name": "showNotifications", "title": "Show notifications", @@ -77,24 +47,6 @@ "title": "Ignore list", "type": "string", "value": "" - }, - { - "name": "showCallingFile", - "title": "Display calling file", - "type": "bool", - "value": false - }, - { - "name": "showCompleteCallingStack", - "title": "Display complete calling stack", - "type": "bool", - "value": false - }, - { - "name": "allowPDFCanvas", - "title": "Allow canvas in PDFs", - "type": "bool", - "value": false } ], "main": "lib/main.js",