mirror of
https://github.com/kkapsner/CanvasBlocker
synced 2025-01-03 10:31:54 +01:00
First implementation of the fakeInput mode. (And other improvements.)
This commit is contained in:
parent
b6d08459a5
commit
a2fa00fb98
@ -8,7 +8,8 @@
|
||||
const {utils: Cu} = Components;
|
||||
const COMMONJS_URI = "resource://gre/modules/commonjs";
|
||||
const {require} = Cu.import(COMMONJS_URI + "/toolkit/require.js", {});
|
||||
const {intercept} = require("../lib/intercept.js");
|
||||
const {intercept, setExportFunction} = require("../lib/intercept.js");
|
||||
setExportFunction(Cu.exportFunction);
|
||||
const {ask} = require("../lib/askForPermission.js");
|
||||
|
||||
// Variable to "unload" the script
|
||||
|
@ -59,7 +59,6 @@ function parseErrorStack(errorStack){
|
||||
"use strict";
|
||||
|
||||
var callers = errorStack.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...
|
||||
|
@ -10,6 +10,7 @@
|
||||
setRandomSupply(randomSupplies.nonPersistent);
|
||||
var apiNames = Object.keys(changedFunctions);
|
||||
var undef;
|
||||
var exportFunction;
|
||||
|
||||
function setRandomSupplyByType(type){
|
||||
switch (type){
|
||||
@ -38,28 +39,27 @@
|
||||
}
|
||||
var error = new Error();
|
||||
var status = check({url: window.location.href, errorStack: error.stack});
|
||||
if (status.type.indexOf(changedFunction.type) !== -1){
|
||||
if (status.mode === "ask"){
|
||||
status.mode = ask({window: window, type: changedFunction.type, canvas: this, errorStack: error.stack});
|
||||
var funcStatus = changedFunction.getStatus(this, status);
|
||||
|
||||
if (funcStatus.active){
|
||||
if (funcStatus.mode === "ask"){
|
||||
funcStatus.mode = ask({window: window, type: changedFunction.type, canvas: this, errorStack: error.stack});
|
||||
}
|
||||
switch (status.mode){
|
||||
switch (funcStatus.mode){
|
||||
case "allow":
|
||||
return original;
|
||||
case "fake":
|
||||
setRandomSupplyByType(prefs("rng"));
|
||||
if (changedFunction.fake){
|
||||
notify({url: window.location.href, errorStack: error.stack}, window);
|
||||
return changedFunction.fake;
|
||||
}
|
||||
else {
|
||||
if (changedFunction.fakeGenerator) {
|
||||
return changedFunction.fakeGenerator(prefs, function(){
|
||||
notify({url: window.location.href, errorStack: error.stack}, window);
|
||||
});
|
||||
}
|
||||
else {
|
||||
var fake = changedFunction.fakeGenerator(prefs, function(messageId){
|
||||
notify({url: window.location.href, errorStack: error.stack, messageId});
|
||||
});
|
||||
switch (fake){
|
||||
case true:
|
||||
return original;
|
||||
case false:
|
||||
return undef;
|
||||
}
|
||||
default:
|
||||
return exportFunction(fake, window.wrappedJSObject);
|
||||
}
|
||||
//case "block":
|
||||
default:
|
||||
@ -74,4 +74,7 @@
|
||||
);
|
||||
});
|
||||
};
|
||||
exports.setExportFunction = function(eFunc){
|
||||
exportFunction = eFunc;
|
||||
};
|
||||
}());
|
@ -7,80 +7,165 @@
|
||||
|
||||
var randomSupply = null;
|
||||
|
||||
function getFakeCanvas(window, original){
|
||||
var context = window.HTMLCanvasElement.prototype.getContext.call(original, "2d");
|
||||
var imageData, data, source;
|
||||
if (context){
|
||||
imageData = window.CanvasRenderingContext2D.prototype.getImageData.call(context, 0, 0, original.width, original.height);
|
||||
source = imageData.data;
|
||||
function getContext(window, canvas){
|
||||
return window.HTMLCanvasElement.prototype.getContext.call(canvas, "2d") ||
|
||||
window.HTMLCanvasElement.prototype.getContext.call(canvas, "webgl") ||
|
||||
window.HTMLCanvasElement.prototype.getContext.call(canvas, "experimental-webgl") ||
|
||||
window.HTMLCanvasElement.prototype.getContext.call(canvas, "webgl2") ||
|
||||
window.HTMLCanvasElement.prototype.getContext.call(canvas, "experimental-webgl2");
|
||||
}
|
||||
function getImageData(window, context){
|
||||
if (context instanceof window.CanvasRenderingContext2D){
|
||||
return window.CanvasRenderingContext2D.prototype.getImageData.call(context, 0, 0, context.canvas.width, context.canvas.height);
|
||||
}
|
||||
else {
|
||||
context =
|
||||
window.HTMLCanvasElement.prototype.getContext.call(original, "webgl") ||
|
||||
window.HTMLCanvasElement.prototype.getContext.call(original, "experimental-webgl") ||
|
||||
window.HTMLCanvasElement.prototype.getContext.call(original, "webgl2") ||
|
||||
window.HTMLCanvasElement.prototype.getContext.call(original, "experimental-webgl2");
|
||||
imageData = new window.wrappedJSObject.ImageData(original.width, original.height);
|
||||
source = new window.wrappedJSObject.Uint8Array(imageData.data.length);
|
||||
var imageData = new window.wrappedJSObject.ImageData(context.canvas.width, context.canvas.height);
|
||||
window.WebGLRenderingContext.prototype.readPixels.call(
|
||||
context,
|
||||
0, 0, original.width, original.height,
|
||||
0, 0, context.canvas.width, context.canvas.height,
|
||||
context.RGBA, context.UNSIGNED_BYTE,
|
||||
source
|
||||
imageData.data
|
||||
);
|
||||
return imageData;
|
||||
}
|
||||
data = imageData.data;
|
||||
}
|
||||
|
||||
function getFakeCanvas(window, original){
|
||||
var context = getContext(window, original);
|
||||
var imageData = getImageData(window, context);
|
||||
var data = imageData.data;
|
||||
var l = data.length;
|
||||
var rng = randomSupply.getRng(l, window);
|
||||
|
||||
for (var i = 0; i < l; i += 1){
|
||||
data[i] = rng(source[i], i);
|
||||
data[i] = rng(data[i], i);
|
||||
}
|
||||
var canvas = original.cloneNode(true);
|
||||
context = window.HTMLCanvasElement.prototype.getContext.call(canvas, "2d");
|
||||
context.putImageData(imageData, 0, 0);
|
||||
return canvas;
|
||||
}
|
||||
function randomMixImageData(window, imageData1, imageData2){
|
||||
var data1 = imageData1.data;
|
||||
var data2 = imageData2.data;
|
||||
var l = data1.length;
|
||||
if (l === data2.length){
|
||||
var rng = randomSupply.getRng(l, window);
|
||||
|
||||
for (var i = 0; i < l; i += 1){
|
||||
if (data1[i] > data2[i]){
|
||||
data2[i] = data1[i] - rng(data1[i] - data2[i], i);
|
||||
}
|
||||
else if (data1[i] < data2[i]){
|
||||
data2[i] = data1[i] + rng(data2[i] - data1[i], i);
|
||||
}
|
||||
}
|
||||
}
|
||||
return imageData2;
|
||||
}
|
||||
|
||||
function getWindow(canvas){
|
||||
return canvas.ownerDocument.defaultView;
|
||||
}
|
||||
function hasType(status, type){
|
||||
return status.type.indexOf(type) !== -1;
|
||||
}
|
||||
|
||||
exports.setRandomSupply = function(supply){
|
||||
randomSupply = supply;
|
||||
};
|
||||
var canvasContextType = new WeakMap();
|
||||
// changed functions and their fakes
|
||||
exports.changedFunctions = {
|
||||
getContext: {
|
||||
type: "context",
|
||||
object: "HTMLCanvasElement"
|
||||
getStatus: function(obj, status){
|
||||
return {
|
||||
mode: hasType(status, "block")? "block": "fake",
|
||||
type: status.type,
|
||||
active: true
|
||||
};
|
||||
},
|
||||
object: "HTMLCanvasElement",
|
||||
fakeGenerator: function(prefs, notify){
|
||||
return function(context, contextAttributes){
|
||||
var window = getWindow(this);
|
||||
canvasContextType.set(this, context);
|
||||
return window.HTMLCanvasElement.prototype.getContext.apply(this, arguments);
|
||||
};
|
||||
}
|
||||
},
|
||||
toDataURL: {
|
||||
type: "readout",
|
||||
getStatus: function(obj, status){
|
||||
if (hasType(status, "input")){
|
||||
var contextType = canvasContextType.get(obj);
|
||||
status.active = contextType && contextType !== "2d";
|
||||
}
|
||||
else {
|
||||
status.active = hasType(status, "readout");
|
||||
}
|
||||
return status;
|
||||
},
|
||||
object: "HTMLCanvasElement",
|
||||
fake: function toDataURL(){
|
||||
var window = getWindow(this);
|
||||
return window.HTMLCanvasElement.prototype.toDataURL.apply(getFakeCanvas(window, this), arguments);
|
||||
fakeGenerator: function(prefs, notify){
|
||||
return function toDataURL(){
|
||||
notify("fakedReadout");
|
||||
var window = getWindow(this);
|
||||
return window.HTMLCanvasElement.prototype.toDataURL.apply(getFakeCanvas(window, this), arguments);
|
||||
};
|
||||
}
|
||||
},
|
||||
toBlob: {
|
||||
type: "readout",
|
||||
getStatus: function(obj, status){
|
||||
if (hasType(status, "input")){
|
||||
var contextType = canvasContextType.get(obj);
|
||||
status.active = contextType && contextType !== "2d";
|
||||
}
|
||||
else {
|
||||
status.active = hasType(status, "readout");
|
||||
}
|
||||
return status;
|
||||
},
|
||||
object: "HTMLCanvasElement",
|
||||
fake: function toBlob(callback){
|
||||
var window = getWindow(this);
|
||||
return window.HTMLCanvasElement.prototype.toBlob.apply(getFakeCanvas(window, this), arguments);
|
||||
fakeGenerator: function(prefs, notify){
|
||||
return function toBlob(callback){
|
||||
notify("fakedReadout");
|
||||
var window = getWindow(this);
|
||||
return window.HTMLCanvasElement.prototype.toBlob.apply(getFakeCanvas(window, this), arguments);
|
||||
};
|
||||
},
|
||||
exportOptions: {allowCallbacks: true}
|
||||
},
|
||||
mozGetAsFile: {
|
||||
type: "readout",
|
||||
getStatus: function(obj, status){
|
||||
if (hasType(status, "input")){
|
||||
var contextType = canvasContextType.get(obj);
|
||||
status.active = contextType && contextType !== "2d";
|
||||
}
|
||||
else {
|
||||
status.active = hasType(status, "readout");
|
||||
}
|
||||
return status;
|
||||
},
|
||||
object: "HTMLCanvasElement",
|
||||
mozGetAsFile: function mozGetAsFile(callback){
|
||||
var window = getWindow(this);
|
||||
return window.HTMLCanvasElement.prototype.mozGetAsFile.apply(getFakeCanvas(window, this), arguments);
|
||||
fakeGenerator: function(prefs, notify){
|
||||
return function mozGetAsFile(callback){
|
||||
notify("fakedReadout");
|
||||
var window = getWindow(this);
|
||||
return window.HTMLCanvasElement.prototype.mozGetAsFile.apply(getFakeCanvas(window, this), arguments);
|
||||
};
|
||||
}
|
||||
},
|
||||
getImageData: {
|
||||
type: "readout",
|
||||
getStatus: function(obj, status){
|
||||
if (hasType(status, "input")){
|
||||
var contextType = canvasContextType.get(obj);
|
||||
status.active = contextType && contextType !== "2d";
|
||||
}
|
||||
else {
|
||||
status.active = hasType(status, "readout");
|
||||
}
|
||||
return status;
|
||||
},
|
||||
object: "CanvasRenderingContext2D",
|
||||
fakeGenerator: function(prefs, notify){
|
||||
var maxSize = prefs("maxFakeSize") || Number.POSITIVE_INFINITY;
|
||||
@ -91,7 +176,7 @@
|
||||
context = this;
|
||||
}
|
||||
else {
|
||||
notify();
|
||||
notify("fakedReadout");
|
||||
context = window.HTMLCanvasElement.prototype.getContext.call(
|
||||
getFakeCanvas(window, this.canvas),
|
||||
"2d"
|
||||
@ -107,14 +192,55 @@
|
||||
};
|
||||
}
|
||||
},
|
||||
fillText: {
|
||||
getStatus: function(obj, status){
|
||||
status.active = hasType(status, "input");
|
||||
return status;
|
||||
},
|
||||
object: "CanvasRenderingContext2D",
|
||||
fakeGenerator: function(prefs, notify){
|
||||
return function fillText(str, x, y){
|
||||
notify("fakedInput");
|
||||
var window = getWindow(this.canvas);
|
||||
var oldImageData = getImageData(window, this);
|
||||
var ret = window.CanvasRenderingContext2D.prototype.fillText.apply(this, arguments);
|
||||
var newImageData = getImageData(window, this);
|
||||
this.putImageData(randomMixImageData(window, oldImageData, newImageData), 0, 0);
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
},
|
||||
strokeText: {
|
||||
getStatus: function(obj, status){
|
||||
status.active = hasType(status, "input");
|
||||
return status;
|
||||
},
|
||||
object: "CanvasRenderingContext2D",
|
||||
fakeGenerator: function(prefs, notify){
|
||||
return function strokeText(str, x, y){
|
||||
notify("fakedInput");
|
||||
var window = getWindow(this.canvas);
|
||||
var oldImageData = getImageData(window, this);
|
||||
var ret = window.CanvasRenderingContext2D.prototype.strokeText.apply(this, arguments);
|
||||
var newImageData = getImageData(window, this);
|
||||
this.putImageData(randomMixImageData(window, oldImageData, newImageData), 0, 0);
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
},
|
||||
readPixels: {
|
||||
type: "readout",
|
||||
getStatus: function(obj, status){
|
||||
status.active = hasType(status, "readout") || hasType(status, "input");
|
||||
return status;
|
||||
},
|
||||
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);
|
||||
|
||||
fakeGenerator: function(prefs, notify){
|
||||
return function readPixels(x, y, width, height, format, type, pixels){
|
||||
notify("fakedReadout");
|
||||
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);
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -8,7 +8,7 @@ var URL = require("sdk/url").URL;
|
||||
const {parseErrorStack} = require("./callingStack");
|
||||
|
||||
var tabUtils = require("sdk/tabs/utils");
|
||||
exports.notify = function({url, errorStack}, {lists, notificationPref, _, browser, window}){
|
||||
exports.notify = function({url, errorStack, messageId}, {lists, notificationPref, _, browser, window}){
|
||||
"use strict";
|
||||
var callingStackMsg = parseErrorStack(errorStack);
|
||||
|
||||
@ -16,7 +16,7 @@ exports.notify = function({url, errorStack}, {lists, notificationPref, _, browse
|
||||
if (notificationPref.doShow() && !lists.get("ignore").match(contentURL)){
|
||||
url = contentURL.href;
|
||||
var domain = contentURL.hostname;
|
||||
var message = _("fakedReadout").replace(/\{url\}/g, domain || url);
|
||||
var message = _(messageId).replace(/\{url\}/g, domain || url);
|
||||
|
||||
var tab, tabBrowser;
|
||||
if (browser){
|
||||
|
Loading…
x
Reference in New Issue
Block a user