diff --git a/README.md b/README.md
index e69de29..1ea5935 100644
--- a/README.md
+++ b/README.md
@@ -0,0 +1,15 @@
+This add-on allows users to prevent websites from using the Javascript <canvas> API to fingerprint them. Users can choose to block the <canvas> API entirely on some or all websites (which may break some websites) or just block or fake its fingerprinting-friendly readout API. More information on <canvas> fingerprinting can be found at http://www.browserleaks.com/canvas.
+
+The different block modes are:
+
+- block readout API: All websites not on the white list or black list can use the <canvas> API to display something on the page, but the readout API is not allowed to return values to the website.
+- fake readout API: Canvas Blocker's default setting, and my favorite! All websites not on the white list or black list can use the <canvas> API to display something on the page, but the readout API is forced to return a new random value each time it is called.
+- ask for readout API permission: All websites not on the white list or black list can use the <canvas> API to display something on the page, but the user will be asked if the website should be allowed to use the readout API each time it is called.
+- block everything: Ignore all lists and block the <canvas> API on all websites.
+- allow only white list: Only websites in the white list are allowed to use the <canvas> API.
+- ask for permission: If a website is not listed on the white list or black list, the user will be asked if the website should be allowed to use the <canvas> API each time it is called.
+- block only black list: Block the <canvas> API only for websites on the black list.
+- allow everything: Ignore all lists and allow the <canvas> API on all websites.
+
+
+At present, only my domain (kkapsner.de) is whitelisted by default.
diff --git a/canvasblocker.xpi b/canvasblocker.xpi
index 87d6c1d..3ede9fb 100644
Binary files a/canvasblocker.xpi and b/canvasblocker.xpi differ
diff --git a/data/options.css b/data/options.css
index b219665..389cb42 100644
--- a/data/options.css
+++ b/data/options.css
@@ -13,4 +13,8 @@ setting[pref-name="showCallingFile"]{
setting[pref-name="showCompleteCallingStack"]{
border-top: 0px transparent none;
padding-bottom: 0.5em;
+}
+setting[pref-name="stackList"]{
+ border-top: 0px transparent none;
+ padding-bottom: 0.5em;
}
\ No newline at end of file
diff --git a/install.rdf b/install.rdf
index 60661a5..fe025dd 100644
--- a/install.rdf
+++ b/install.rdf
@@ -1,19 +1,24 @@
-
+
+
CanvasBlocker@kkapsner.de
- 0.2.0-Development
2
true
false
+ 0.2.3-Development
+ CanvasBlocker
+ Changes the JS-API for modifying <canvas> to prevent Canvas-Fingerprinting.
+ Korbinian Kapsner
+ https://github.com/kkapsner/CanvasBlocker/
+ data:text/xml,<placeholder/>
+ 2
{ec8030f7-c20a-464f-9b0e-13a3a9e97384}
- 38.0
- 40.*
+ 40.0
+ 46.*
@@ -39,18 +44,9 @@
{aa3c5121-dab2-40e2-81ca-7ea25febc110}
- 26.0
- 30.0a1
+ 40.0
+ 46.*
-
-
-
- CanvasBlocker
- Changes the JS-API for modifying <canvas> to prevent Canvas-Fingerprinting.
- Korbinian Kapsner
- https://github.com/kkapsner/CanvasBlocker/
- 2
-
\ No newline at end of file
diff --git a/lib/lists.js b/lib/lists.js
index 895022a..49592f0 100644
--- a/lib/lists.js
+++ b/lib/lists.js
@@ -51,7 +51,7 @@ function getDomainRegExpList(domainList){
var lists = {
white: [],
- ignore: [],
+ "ignore": [],
black: []
};
@@ -69,6 +69,32 @@ Object.keys(lists).forEach(function(type){
updateList(type);
});
+function updateStackList(){
+ try {
+ var data = JSON.parse(prefs.stackList);
+ if (!Array.isArray(data)){
+ data = [data];
+ }
+ var list = data.filter(function(entry){
+ return typeof entry === "object" && typeof entry.url === "string";
+ });
+ }
+ catch(e){
+ var list = [];
+ }
+ list.match = function(stack){
+ return this.some(function(stackRule){
+ return stack.match(stackRule);
+ });
+ };
+ lists.stack = list;
+}
+lists.stack = [];
+preferences.on("stackList", function(){
+ updateStackList();
+});
+updateStackList();
+
exports.get = function getList(type){
"use strict";
@@ -78,7 +104,7 @@ exports.appendTo = function appendToList(type, entry){
"use strict";
prefs[type + "List"] += (prefs[type + "List"]? ",": "") + entry;
- prefService.set("extensions.CanvasBlocker@kkapsner.de.whiteList", prefs[type + "List"]);
+ prefService.set("extensions.CanvasBlocker@kkapsner.de." + type + "List", 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 505f1e7..35426b2 100644
--- a/lib/main.js
+++ b/lib/main.js
@@ -18,8 +18,8 @@
const preferences = require("sdk/simple-prefs");
const prefs = preferences.prefs;
- function checkURL(url){
- var match = sharedFunctions.checkURL(url, prefs.blockMode).match(/^(block|allow|fake|ask)(|Readout|Everything|Context)$/);
+ function check(callingStack, url){
+ var match = sharedFunctions.check(callingStack, url, prefs.blockMode).match(/^(block|allow|fake|ask)(|Readout|Everything|Context)$/);
if (match){
return {
type: (match[2] === "Everything" || match[2] === "")?
@@ -53,17 +53,17 @@
enumerable: true,
configureable: false,
get: function(){
- var status = checkURL(window.location);
+ var callingStack = sharedFunctions.errorToCallingStack(new Error());
+ var status = check(callingStack, window.location);
if (status.type.indexOf(changedFunction.type) !== -1){
- var callingStackMsg = sharedFunctions.errorToCallingStackMsg(new Error());
if (status.mode === "ask"){
- status.mode = ask(window, changedFunction.type, this, callingStackMsg);
+ status.mode = ask(window, changedFunction.type, this, callingStack);
}
switch (status.mode){
case "allow":
return original;
case "fake":
- notify(window, callingStackMsg);
+ notify(window, callingStack);
return changedFunction.fake || undef;
//case "block":
default:
diff --git a/lib/notifications.js b/lib/notifications.js
index 8537b16..6c1e297 100644
--- a/lib/notifications.js
+++ b/lib/notifications.js
@@ -55,21 +55,40 @@ exports.notify = function(window, callingStackMsg){
label: _("ignorelistDomain"),
accessKey: "",
callback: function(){
- lists.appendTo("ignore", notification.domain);
+ var domain = browser.contentWindow.prompt(
+ _("inputIgnoreDomain"),
+ notification.domain
+ );
+ if (domain){
+ lists.appendTo("ignore", domain);
+ }
}
},
{
label: _("whitelistURL"),
accessKey: "",
callback: function(){
- lists.appendTo("white", "^" + notification.url.replace(/([\\\+\*\?\[\^\]\$\(\)\{\}\=\!\|\.])/g, "\\$1") + "$");
+ var url = browser.contentWindow.prompt(
+ _("inputWhitelistDomain"),
+ "^" + notification.url.replace(/([\\\+\*\?\[\^\]\$\(\)\{\}\=\!\|\.])/g, "\\$1") + "$"
+ );
+ if (url){
+ lists.appendTo("white", url);
+ }
}
},
{
label: _("whitelistDomain"),
accessKey: "",
callback: function(){
- lists.appendTo("white", notification.domain);
+ var domain = browser.contentWindow.prompt(
+ _("inputWhitelistURL"),
+ notification.domain
+ );
+ if (domain){
+ lists.appendTo("white", domain);
+ }
+
}
},
{
diff --git a/lib/sharedFunctions.js b/lib/sharedFunctions.js
index cb2cb98..bf2a27d 100644
--- a/lib/sharedFunctions.js
+++ b/lib/sharedFunctions.js
@@ -23,12 +23,25 @@ var _ = function(name, replace){
return str;
};
+function check(stack, url, blockMode){
+ if (prefs.enableStackList && checkStack(stack)){
+ return "allow";
+ }
+ else {
+ return checkURL(url, blockMode);
+ }
+}
function checkURL(url, blockMode){
"use strict";
- if (url.protocol === "about:" || url.protocol === "chrome:") {
- return "allow";
+ switch (url.protocol){
+ case "about:":
+ if (url.href === "about:blank"){
+ break;
+ }
+ case "chrome:":
+ return "allow";
}
var mode = "block";
@@ -67,6 +80,11 @@ function checkURL(url, blockMode){
return mode;
}
+function checkStack(stack){
+ "use strict";
+
+ return lists.get("stack").match(stack);
+}
// Stack parsing
function parseStackEntry(entry){
@@ -75,17 +93,32 @@ function parseStackEntry(entry){
var m = /@(.*):(\d*):(\d*)$/.exec(entry) || ["", entry, "--", "--"];
return {
url: m[1],
- line: m[2],
- column: m[3],
+ line: parseInt(m[2], 10),
+ column: parseInt(m[3], 10),
raw: entry
};
}
+function stackRuleMatch(stackEntry, stackRule){
+ if (!stackEntry){
+ return false;
+ }
+ if (stackEntry.url !== stackRule.url){
+ return false;
+ }
+ if ((typeof stackRule.line) !== "undefined" && stackEntry.line !== stackRule.line){
+ return false;
+ }
+ if ((typeof stackRule.column) !== "undefined" && stackEntry.column !== stackRule.column){
+ return false;
+ }
+ return true;
+}
+
// parse calling stack
-function errorToCallingStackMsg(error){
+function errorToCallingStack(error){
"use strict";
- var msg = "";
var callers = error.stack.trim().split("\n");
//console.log(callers);
var findme = callers.shift(); // Remove us from the stack
@@ -96,20 +129,39 @@ function errorToCallingStackMsg(error){
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;
+ }).map(parseStackEntry);
+ return {
+ toString: function(){
+ var msg = "";
+ msg += "\n\n" + _("sourceOutput") + ": ";
+ if (prefs.showCompleteCallingStack){
+ msg += callers.reduce(function(stack, c){
+ return stack + "\n\t" + _("stackEntryOutput", c);
+ }, "");
+ }
+ else{
+ msg += _("stackEntryOutput", callers[0]);
+ }
+
+ return msg;
+ },
+ match: function(stackRule){
+ if (typeof stackRule.stackPosition !== "undefined"){
+ var pos = stackRule.stackPosition;
+ if (pos < 0){
+ pos += callers.length;
+ }
+ return stackRuleMatch(callers[pos], stackRule);
+ }
+ else {
+ return callers.some(function(stackEntry){
+ return stackRuleMatch(stackEntry, stackRule);
+ });
+ }
+ }
+ };
}
-exports.checkURL = checkURL;
+exports.check = check;
exports.parseStackEntry = parseStackEntry;
-exports.errorToCallingStackMsg = errorToCallingStackMsg;
\ No newline at end of file
+exports.errorToCallingStack = errorToCallingStack;
\ No newline at end of file
diff --git a/locale/de-DE.json b/locale/de-DE.json
new file mode 100644
index 0000000..bfffa8d
--- /dev/null
+++ b/locale/de-DE.json
@@ -0,0 +1,52 @@
+{
+ "allowPDFCanvas_description": "Die native pdf.js verwendet