1
0
Fork 0
mirror of https://github.com/kkapsner/CanvasBlocker synced 2025-07-03 20:16:33 +02:00

Removed pageMod and many not needed features.

Notifications not working.
This commit is contained in:
kkapsner 2015-09-06 12:26:50 +02:00
parent 195d780bf8
commit 97e0c6b9cd
8 changed files with 348 additions and 661 deletions

View file

@ -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));
}());

35
lib/lists.js Normal file
View file

@ -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;

View file

@ -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);
});
}());

101
lib/modifiedAPI.js Normal file
View file

@ -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);
}
}
};
}());

100
lib/notifications.js Normal file
View file

@ -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;
}
}
};

View file

@ -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){}
// 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;