mirror of
https://github.com/kkapsner/CanvasBlocker
synced 2025-01-03 10:31:54 +01:00
Removed pageMod and many not needed features.
Notifications not working.
This commit is contained in:
parent
195d780bf8
commit
97e0c6b9cd
320
data/inject.js
320
data/inject.js
@ -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;
|
|
||||||
});
|
|
||||||
}());
|
|
@ -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
35
lib/lists.js
Normal 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;
|
264
lib/main.js
264
lib/main.js
@ -5,234 +5,62 @@
|
|||||||
(function(){
|
(function(){
|
||||||
"use strict";
|
"use strict";
|
||||||
require("./stylePreferencePane");
|
require("./stylePreferencePane");
|
||||||
require("./disableWithoutDocumentElement");
|
// require("./disableWithoutDocumentElement");
|
||||||
|
const {changedFunctions} = require("./modifiedAPI");
|
||||||
var self = require("sdk/self");
|
const {notify} = require("./notifications");
|
||||||
var pageMod = require("sdk/page-mod");
|
const lists = require("./lists");
|
||||||
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");
|
|
||||||
|
|
||||||
var sharedFunctions = require("./sharedFunctions");
|
const sharedFunctions = require("./sharedFunctions");
|
||||||
var getDomainRegExpList = sharedFunctions.getDomainRegExpList;
|
|
||||||
// preferences
|
// 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
|
const observers = require("sdk/system/events");
|
||||||
var preferencesForInjected = ["showCallingFile", "showCompleteCallingStack"];
|
const { when: unload } = require("sdk/system/unload");
|
||||||
preferencesForInjected.forEach(function(name){
|
|
||||||
preferences.on(name, function(){
|
const preferences = require("sdk/simple-prefs");
|
||||||
workers.forEach(function(worker){
|
const prefs = preferences.prefs;
|
||||||
worker.port.emit("set", name, prefs[name]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
function checkURL(url){
|
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){
|
var apiNames = Object.keys(changedFunctions);
|
||||||
["", "Visible", "Invisible"].forEach(function(visibility){
|
var undef;
|
||||||
var text = "askFor" + visibility + type + "Permission";
|
|
||||||
workerTranslations[text] = _(text);
|
function intercept({subject: window}){
|
||||||
});
|
apiNames.forEach(function(name){
|
||||||
});
|
var changedFunction = changedFunctions[name];
|
||||||
|
var original = window.wrappedJSObject[changedFunction.object].prototype[name];
|
||||||
|
|
||||||
var workerOptions = {
|
Object.defineProperty(
|
||||||
blockMode: checkURL(),
|
window.wrappedJSObject[changedFunction.object].prototype,
|
||||||
whiteList: prefs.whiteList,
|
name,
|
||||||
blackList: prefs.blackList,
|
{
|
||||||
askOnce: prefs.askOnce,
|
enumerable: true,
|
||||||
translations: workerTranslations
|
configureable: false,
|
||||||
};
|
get: function(){
|
||||||
preferences.on("blockMode", function(){
|
var status = checkURL(window.location);
|
||||||
workerOptions.blockMode = checkURL();
|
if (status === changedFunction.mode){
|
||||||
});
|
var callingStackMsg = sharedFunctions.errorToCallingStackMsg(new Error());
|
||||||
["whiteList", "blackList", "askOnce"].forEach(function(prefName){
|
switch (status){
|
||||||
preferences.on(prefName, function(){
|
case "fakeReadout":
|
||||||
workerOptions[prefName] = prefs[prefName];
|
// notify(window, callingStackMsg);
|
||||||
});
|
return changedFunction.fake;
|
||||||
});
|
//case "block":
|
||||||
pageMod.PageMod({
|
default:
|
||||||
include: "*",
|
return undef;
|
||||||
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
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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
101
lib/modifiedAPI.js
Normal 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
100
lib/notifications.js
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
@ -3,6 +3,23 @@
|
|||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* 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){
|
function getDomainRegExpList(domainList){
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
@ -44,44 +61,28 @@ function getDomainRegExpList(domainList){
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkURL(url, blockMode, whiteList, blackList){
|
function checkURL(url, blockMode){
|
||||||
"use strict";
|
"use strict";
|
||||||
|
if (url.protocol === "about:") {
|
||||||
|
return "unblock";
|
||||||
|
}
|
||||||
|
|
||||||
var mode = "block";
|
var mode = "block";
|
||||||
switch (blockMode){
|
switch (blockMode){
|
||||||
case "blockEverything":
|
case "blockEverything":
|
||||||
mode = "block";
|
mode = "block";
|
||||||
break;
|
break;
|
||||||
case "allowOnlyWhiteList":
|
|
||||||
if (url && whiteList.match(url)){
|
|
||||||
mode = "unblock";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
mode = "block";
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "ask":
|
|
||||||
case "blockReadout":
|
|
||||||
case "fakeReadout":
|
case "fakeReadout":
|
||||||
case "askReadout":
|
if (url && lists.get("white").match(url)){
|
||||||
if (url && whiteList.match(url)){
|
|
||||||
mode = "unblock";
|
mode = "unblock";
|
||||||
}
|
}
|
||||||
else if (url && blackList.match(url)){
|
else if (url && lists.get("black").match(url)){
|
||||||
mode = "block";
|
mode = "block";
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
mode = blockMode;
|
mode = blockMode;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "blockOnlyBlackList":
|
|
||||||
if (url && blackList.match(url)){
|
|
||||||
mode = "block";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
mode = "unblock";
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "allowEverything":
|
case "allowEverything":
|
||||||
mode = "unblock";
|
mode = "unblock";
|
||||||
break;
|
break;
|
||||||
@ -91,8 +92,47 @@ function checkURL(url, blockMode, whiteList, blackList){
|
|||||||
return mode;
|
return mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
exports.getDomainRegExpList = getDomainRegExpList;
|
// Stack parsing
|
||||||
exports.checkURL = checkURL;
|
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;
|
48
package.json
48
package.json
@ -30,42 +30,12 @@
|
|||||||
"value": "fakeReadout",
|
"value": "fakeReadout",
|
||||||
"label": "fake readout API"
|
"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",
|
"value": "allowEverything",
|
||||||
"label": "allow everything"
|
"label": "allow everything"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "askOnlyOnce",
|
|
||||||
"title": "Ask only once",
|
|
||||||
"type": "bool",
|
|
||||||
"value": true
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "showNotifications",
|
"name": "showNotifications",
|
||||||
"title": "Show notifications",
|
"title": "Show notifications",
|
||||||
@ -77,24 +47,6 @@
|
|||||||
"title": "Ignore list",
|
"title": "Ignore list",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"value": ""
|
"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",
|
"main": "lib/main.js",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user