2014-12-04 23:34:41 +01:00
|
|
|
/* global self, window, console, unsafeWindow, exportFunction, cloneInto */
|
2015-01-16 13:01:01 +01:00
|
|
|
/* 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/. */
|
2014-08-20 10:21:38 +02:00
|
|
|
(function(){
|
|
|
|
"use strict";
|
|
|
|
|
2014-12-15 18:43:47 +01:00
|
|
|
var settings = {
|
|
|
|
showCallingFile: false
|
|
|
|
};
|
2014-10-09 00:26:36 +02:00
|
|
|
var blockMode = {
|
|
|
|
getContext: {
|
2015-04-14 01:26:04 +02:00
|
|
|
name: "getContext",
|
2014-10-09 00:26:36 +02:00
|
|
|
status: "block",
|
2014-12-04 23:34:41 +01:00
|
|
|
askText: {
|
|
|
|
visible: "askForVisiblePermission",
|
|
|
|
invisible: "askForInvisiblePermission",
|
|
|
|
nocanvas: "askForPermission"
|
|
|
|
},
|
2014-10-09 00:26:36 +02:00
|
|
|
askStatus: {
|
|
|
|
askOnce: false,
|
2014-12-15 22:01:36 +01:00
|
|
|
alreadyAsked: {},
|
|
|
|
answer: {}
|
2014-10-08 03:16:23 +02:00
|
|
|
}
|
2014-10-09 00:26:36 +02:00
|
|
|
},
|
|
|
|
readAPI: {
|
2015-04-14 01:26:04 +02:00
|
|
|
name: "readAPI",
|
2014-10-11 01:46:47 +02:00
|
|
|
status: "allow",
|
2014-12-04 23:34:41 +01:00
|
|
|
askText: {
|
|
|
|
visible: "askForVisibleReadoutPermission",
|
|
|
|
invisible: "askForInvisibleReadoutPermission",
|
|
|
|
nocanvas: "askForReadoutPermission"
|
|
|
|
},
|
2014-10-11 01:46:47 +02:00
|
|
|
askStatus: {
|
|
|
|
askOnce: false,
|
2014-12-15 22:01:36 +01:00
|
|
|
alreadyAsked: {},
|
|
|
|
answer: {}
|
2014-10-11 01:46:47 +02:00
|
|
|
}
|
2014-10-08 03:16:23 +02:00
|
|
|
}
|
2014-10-09 00:26:36 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
var undef;
|
2014-10-11 01:17:49 +02:00
|
|
|
var randomImage = (function(){
|
|
|
|
var length = Math.floor(20 + Math.random() * 100);
|
|
|
|
var bytes = "";
|
|
|
|
for (var i = 0; i < length; i += 1){
|
|
|
|
bytes += String.fromCharCode(Math.floor(Math.random() * 256));
|
|
|
|
}
|
|
|
|
return bytes;
|
|
|
|
}());
|
|
|
|
|
2015-04-15 12:02:21 +02:00
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
2014-12-04 23:34:41 +01:00
|
|
|
// Check canvas appearance
|
|
|
|
function canvasAppearance(context){
|
|
|
|
var oldBorder = false;
|
|
|
|
var canvas = false;
|
2014-12-15 22:01:36 +01:00
|
|
|
var inDOM = null;
|
2014-12-04 23:34:41 +01:00
|
|
|
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";
|
2014-12-15 22:01:36 +01:00
|
|
|
inDOM = canvas.ownerDocument.contains(canvas);
|
2014-12-04 23:34:41 +01:00
|
|
|
}
|
|
|
|
return {
|
|
|
|
canvas: canvas,
|
2014-12-15 22:01:36 +01:00
|
|
|
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;
|
|
|
|
},
|
2014-12-04 23:34:41 +01:00
|
|
|
reset: function(){
|
|
|
|
if (canvas){
|
|
|
|
canvas.style.border = oldBorder;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2014-10-13 09:31:04 +02:00
|
|
|
// changed functions
|
|
|
|
var changedFunctions = {
|
|
|
|
getContext: {
|
|
|
|
mode: blockMode.getContext,
|
|
|
|
object: unsafeWindow.HTMLCanvasElement
|
|
|
|
},
|
2014-10-11 01:46:47 +02:00
|
|
|
toDataURL: {
|
2014-10-13 09:31:04 +02:00
|
|
|
mode: blockMode.readAPI,
|
2014-10-11 01:46:47 +02:00
|
|
|
object: unsafeWindow.HTMLCanvasElement,
|
2014-10-13 09:31:04 +02:00
|
|
|
fake: function(){
|
2014-10-11 01:46:47 +02:00
|
|
|
var type = arguments[0] || "image/png";
|
|
|
|
return "data:" + type + ";base64," + btoa(randomImage);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
toBlob: {
|
2014-10-13 09:31:04 +02:00
|
|
|
mode: blockMode.readAPI,
|
2014-10-11 01:46:47 +02:00
|
|
|
object: unsafeWindow.HTMLCanvasElement,
|
2014-10-13 09:31:04 +02:00
|
|
|
fake: function(callback){
|
2014-10-11 01:46:47 +02:00
|
|
|
var type = arguments[0] || "image/png";
|
|
|
|
var blob = new window.Blob(randomImage, {type: type});
|
|
|
|
callback(blob);
|
|
|
|
},
|
|
|
|
exportOptions: {allowCallbacks: true}
|
|
|
|
},
|
|
|
|
mozGetAsFile: {
|
2014-10-13 09:31:04 +02:00
|
|
|
mode: blockMode.readAPI,
|
|
|
|
object: unsafeWindow.HTMLCanvasElement
|
2014-10-11 01:46:47 +02:00
|
|
|
},
|
|
|
|
getImageData: {
|
2014-10-13 09:31:04 +02:00
|
|
|
mode: blockMode.readAPI,
|
2014-10-11 01:46:47 +02:00
|
|
|
object: unsafeWindow.CanvasRenderingContext2D,
|
2014-10-13 09:31:04 +02:00
|
|
|
fake: function(sx, sy, sw, sh){
|
2014-10-11 01:46:47 +02:00
|
|
|
var l = sw * sh * 4;
|
2014-11-16 11:14:15 +01:00
|
|
|
var data = new Uint8ClampedArray(l);
|
2014-10-11 01:46:47 +02:00
|
|
|
for (var i = 0; i < l; i += 1){
|
2014-11-16 11:14:15 +01:00
|
|
|
data[i] = Math.floor(
|
2014-10-11 01:46:47 +02:00
|
|
|
Math.random() * 256
|
|
|
|
);
|
2014-10-11 01:17:49 +02:00
|
|
|
}
|
2014-11-16 11:14:15 +01:00
|
|
|
var imageData = new window.ImageData(sw, sh);
|
|
|
|
imageData.data.set(cloneInto(data, unsafeWindow));
|
2014-10-11 01:46:47 +02:00
|
|
|
return imageData;
|
|
|
|
}
|
2014-10-14 23:52:08 +02:00
|
|
|
},
|
|
|
|
readPixels: {
|
|
|
|
mode: blockMode.readAPI,
|
|
|
|
object: unsafeWindow.WebGLRenderingContext
|
2014-10-11 01:17:49 +02:00
|
|
|
}
|
2014-10-11 01:46:47 +02:00
|
|
|
};
|
2014-10-11 01:17:49 +02:00
|
|
|
|
2014-10-13 09:31:04 +02:00
|
|
|
Object.keys(changedFunctions).forEach(function(name){
|
|
|
|
var changedFunction = changedFunctions[name];
|
|
|
|
var original = changedFunction.object.prototype[name];
|
2014-10-11 01:46:47 +02:00
|
|
|
Object.defineProperty(
|
2014-10-13 09:31:04 +02:00
|
|
|
changedFunction.object.prototype,
|
2014-10-11 01:46:47 +02:00
|
|
|
name,
|
|
|
|
{
|
|
|
|
enumerable: true,
|
|
|
|
configureable: false,
|
|
|
|
get: exportFunction(function(){
|
2014-10-13 09:31:04 +02:00
|
|
|
var status = changedFunction.mode.status;
|
2015-04-15 12:02:21 +02:00
|
|
|
var callingStackMsg = errorToCallingStackMsg(new Error());
|
2014-10-11 01:46:47 +02:00
|
|
|
if (status === "ask"){
|
2014-10-13 09:31:04 +02:00
|
|
|
var askStatus = changedFunction.mode.askStatus;
|
2014-12-15 22:01:36 +01:00
|
|
|
var appearance = canvasAppearance(this);
|
|
|
|
if (askStatus.askOnce && askStatus.alreadyAsked[appearance.askCategory]){
|
2014-10-11 01:46:47 +02:00
|
|
|
// console.log("already asked");
|
2014-12-15 22:01:36 +01:00
|
|
|
status = askStatus.answer[appearance.askCategory];
|
2014-10-11 01:46:47 +02:00
|
|
|
}
|
|
|
|
else {
|
2014-12-10 15:58:26 +01:00
|
|
|
//console.log("asking");
|
2014-12-15 19:32:02 +01:00
|
|
|
var msg = _(changedFunction.mode.askText[appearance.text]);
|
2014-12-15 18:43:47 +01:00
|
|
|
if (settings.showCallingFile){
|
2015-04-15 12:02:21 +02:00
|
|
|
msg += callingStackMsg;
|
2014-12-06 22:38:07 -05:00
|
|
|
}
|
|
|
|
status = window.confirm(msg) ? "allow": "block";
|
2014-12-15 22:01:36 +01:00
|
|
|
askStatus.alreadyAsked[appearance.text] = true;
|
|
|
|
askStatus.answer[appearance.text] = status;
|
2014-12-10 15:58:26 +01:00
|
|
|
//console.log("asking (done)");
|
2014-12-04 23:34:41 +01:00
|
|
|
appearance.reset();
|
2014-10-11 01:46:47 +02:00
|
|
|
}
|
|
|
|
}
|
2015-04-15 12:02:21 +02:00
|
|
|
self.port.emit("accessed " + changedFunction.mode.name, status, callingStackMsg);
|
2014-10-11 01:46:47 +02:00
|
|
|
switch (status){
|
|
|
|
case "allow":
|
|
|
|
return original;
|
2014-10-13 01:37:13 +02:00
|
|
|
case "fake":
|
2014-10-13 09:31:04 +02:00
|
|
|
return changedFunction.fake? exportFunction(
|
|
|
|
changedFunction.fake,
|
2014-10-11 01:46:47 +02:00
|
|
|
unsafeWindow,
|
2014-10-13 09:31:04 +02:00
|
|
|
changedFunction.exportOptions
|
2014-10-13 01:37:13 +02:00
|
|
|
): undef;
|
|
|
|
case "block":
|
|
|
|
default:
|
|
|
|
return undef;
|
2014-10-11 01:46:47 +02:00
|
|
|
}
|
|
|
|
}, unsafeWindow)
|
|
|
|
}
|
|
|
|
);
|
|
|
|
});
|
2014-10-09 00:26:36 +02:00
|
|
|
|
2014-12-15 18:43:47 +01:00
|
|
|
// Stack parsing
|
|
|
|
function parseStackEntry(entry){
|
|
|
|
var m = /@(.*):(\d*):(\d*)$/.exec(entry) || ["", entry, "--", "--"];
|
|
|
|
return {
|
|
|
|
url: m[1],
|
|
|
|
line: m[2],
|
|
|
|
column: m[3],
|
|
|
|
raw: entry
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2014-10-11 01:46:47 +02:00
|
|
|
// Translation
|
2014-12-15 18:43:47 +01:00
|
|
|
var _ = function(name, replace){
|
|
|
|
var str = _[name] || name;
|
|
|
|
if (replace){
|
|
|
|
Object.keys(replace).forEach(function(name){
|
|
|
|
str = str.replace(new RegExp("{" + name + "}", "g"), replace[name]);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
return str;
|
2014-10-09 00:26:36 +02:00
|
|
|
};
|
|
|
|
self.port.on("setTranslation", function(name, translation){
|
|
|
|
_[name] = translation;
|
|
|
|
});
|
|
|
|
|
2014-10-11 01:46:47 +02:00
|
|
|
// Communication with main.js
|
|
|
|
|
2015-04-09 11:08:31 +02:00
|
|
|
function checkAbout(){
|
2015-04-14 01:26:04 +02:00
|
|
|
return document.location.protocol === "about:";
|
2015-04-09 11:08:31 +02:00
|
|
|
}
|
|
|
|
|
2014-08-20 10:21:38 +02:00
|
|
|
function checkPDF(blocking){
|
|
|
|
if (document.contentType.match(/\/pdf$/i)){
|
|
|
|
self.port.emit("isPDF", blocking);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
2014-08-04 17:02:42 +02:00
|
|
|
}
|
2014-10-09 00:26:36 +02:00
|
|
|
|
|
|
|
self.port.on("block", function(force){
|
2015-04-09 11:08:31 +02:00
|
|
|
if (force || !checkAbout() || !checkPDF("block")){
|
2014-10-09 00:26:36 +02:00
|
|
|
blockMode.getContext.status = "block";
|
|
|
|
blockMode.readAPI.status = "allow";
|
2014-08-20 10:21:38 +02:00
|
|
|
}
|
2014-10-09 00:26:36 +02:00
|
|
|
});
|
|
|
|
self.port.on("ask", function(force, askOnce){
|
2015-04-09 11:08:31 +02:00
|
|
|
if (force || !checkAbout() || !checkPDF("askVisible")){
|
2014-10-09 00:26:36 +02:00
|
|
|
blockMode.getContext.status = "ask";
|
|
|
|
blockMode.getContext.askStatus.askOnce = askOnce;
|
|
|
|
blockMode.readAPI.status = "allow";
|
2014-10-08 03:16:23 +02:00
|
|
|
}
|
2014-10-09 00:26:36 +02:00
|
|
|
});
|
|
|
|
self.port.on("blockReadout", function(force){
|
2015-04-09 11:08:31 +02:00
|
|
|
if (force || !checkAbout() || !checkPDF("blockReadout")){
|
2014-10-09 00:26:36 +02:00
|
|
|
blockMode.getContext.status = "allow";
|
|
|
|
blockMode.readAPI.status = "block";
|
2014-08-20 10:21:38 +02:00
|
|
|
}
|
|
|
|
});
|
2014-10-13 01:37:13 +02:00
|
|
|
self.port.on("fakeReadout", function(force){
|
2015-04-09 11:08:31 +02:00
|
|
|
if (force || !checkAbout() || !checkPDF("fakeReadout")){
|
2014-10-13 01:37:13 +02:00
|
|
|
blockMode.getContext.status = "allow";
|
|
|
|
blockMode.readAPI.status = "fake";
|
|
|
|
}
|
|
|
|
});
|
2014-10-11 01:46:47 +02:00
|
|
|
self.port.on("askReadout", function(force, askOnce){
|
2015-04-09 11:08:31 +02:00
|
|
|
if (force || !checkAbout() || !checkPDF("askReadout")){
|
2014-10-11 01:46:47 +02:00
|
|
|
blockMode.getContext.status = "allow";
|
|
|
|
blockMode.readAPI.status = "ask";
|
|
|
|
blockMode.readAPI.askStatus.askOnce = askOnce;
|
|
|
|
}
|
|
|
|
});
|
2014-10-09 00:26:36 +02:00
|
|
|
self.port.on("unblock", function(){
|
|
|
|
blockMode.getContext.status = "allow";
|
|
|
|
blockMode.readAPI.status = "allow";
|
|
|
|
});
|
2014-10-13 09:31:04 +02:00
|
|
|
self.port.on("detach", function(){
|
2014-10-09 00:26:36 +02:00
|
|
|
blockMode.getContext.status = "allow";
|
|
|
|
blockMode.readAPI.status = "allow";
|
|
|
|
});
|
2014-12-15 18:43:47 +01:00
|
|
|
|
|
|
|
// settings passthrough
|
|
|
|
self.port.on("set", function(name, value){
|
|
|
|
settings[name] = value;
|
|
|
|
});
|
2014-09-30 13:15:30 +02:00
|
|
|
}());
|