1
0
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:
kkapsner 2016-10-23 22:12:12 +02:00
parent b6d08459a5
commit a2fa00fb98
5 changed files with 188 additions and 59 deletions

View File

@ -8,7 +8,8 @@
const {utils: Cu} = Components; const {utils: Cu} = Components;
const COMMONJS_URI = "resource://gre/modules/commonjs"; const COMMONJS_URI = "resource://gre/modules/commonjs";
const {require} = Cu.import(COMMONJS_URI + "/toolkit/require.js", {}); 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"); const {ask} = require("../lib/askForPermission.js");
// Variable to "unload" the script // Variable to "unload" the script

View File

@ -59,7 +59,6 @@ function parseErrorStack(errorStack){
"use strict"; "use strict";
var callers = errorStack.trim().split("\n"); var callers = errorStack.trim().split("\n");
//console.log(callers);
var findme = callers.shift(); // Remove us from the stack var findme = callers.shift(); // Remove us from the stack
findme = findme.replace(/(:[0-9]+){1,2}$/, ""); // rm line & column findme = findme.replace(/(:[0-9]+){1,2}$/, ""); // rm line & column
// Eliminate squashed stack. stack may contain 2+ stacks, but why... // Eliminate squashed stack. stack may contain 2+ stacks, but why...

View File

@ -10,6 +10,7 @@
setRandomSupply(randomSupplies.nonPersistent); setRandomSupply(randomSupplies.nonPersistent);
var apiNames = Object.keys(changedFunctions); var apiNames = Object.keys(changedFunctions);
var undef; var undef;
var exportFunction;
function setRandomSupplyByType(type){ function setRandomSupplyByType(type){
switch (type){ switch (type){
@ -38,28 +39,27 @@
} }
var error = new Error(); var error = new Error();
var status = check({url: window.location.href, errorStack: error.stack}); var status = check({url: window.location.href, errorStack: error.stack});
if (status.type.indexOf(changedFunction.type) !== -1){ var funcStatus = changedFunction.getStatus(this, status);
if (status.mode === "ask"){
status.mode = ask({window: window, type: changedFunction.type, canvas: this, errorStack: error.stack}); 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": case "allow":
return original; return original;
case "fake": case "fake":
setRandomSupplyByType(prefs("rng")); setRandomSupplyByType(prefs("rng"));
if (changedFunction.fake){ var fake = changedFunction.fakeGenerator(prefs, function(messageId){
notify({url: window.location.href, errorStack: error.stack}, window); notify({url: window.location.href, errorStack: error.stack, messageId});
return changedFunction.fake; });
} switch (fake){
else { case true:
if (changedFunction.fakeGenerator) { return original;
return changedFunction.fakeGenerator(prefs, function(){ case false:
notify({url: window.location.href, errorStack: error.stack}, window);
});
}
else {
return undef; return undef;
} default:
return exportFunction(fake, window.wrappedJSObject);
} }
//case "block": //case "block":
default: default:
@ -74,4 +74,7 @@
); );
}); });
}; };
exports.setExportFunction = function(eFunc){
exportFunction = eFunc;
};
}()); }());

View File

@ -7,80 +7,165 @@
var randomSupply = null; var randomSupply = null;
function getFakeCanvas(window, original){ function getContext(window, canvas){
var context = window.HTMLCanvasElement.prototype.getContext.call(original, "2d"); return window.HTMLCanvasElement.prototype.getContext.call(canvas, "2d") ||
var imageData, data, source; window.HTMLCanvasElement.prototype.getContext.call(canvas, "webgl") ||
if (context){ window.HTMLCanvasElement.prototype.getContext.call(canvas, "experimental-webgl") ||
imageData = window.CanvasRenderingContext2D.prototype.getImageData.call(context, 0, 0, original.width, original.height); window.HTMLCanvasElement.prototype.getContext.call(canvas, "webgl2") ||
source = imageData.data; 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 { else {
context = var imageData = new window.wrappedJSObject.ImageData(context.canvas.width, context.canvas.height);
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);
window.WebGLRenderingContext.prototype.readPixels.call( window.WebGLRenderingContext.prototype.readPixels.call(
context, context,
0, 0, original.width, original.height, 0, 0, context.canvas.width, context.canvas.height,
context.RGBA, context.UNSIGNED_BYTE, 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 l = data.length;
var rng = randomSupply.getRng(l, window); var rng = randomSupply.getRng(l, window);
for (var i = 0; i < l; i += 1){ 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); var canvas = original.cloneNode(true);
context = window.HTMLCanvasElement.prototype.getContext.call(canvas, "2d"); context = window.HTMLCanvasElement.prototype.getContext.call(canvas, "2d");
context.putImageData(imageData, 0, 0); context.putImageData(imageData, 0, 0);
return canvas; 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){ function getWindow(canvas){
return canvas.ownerDocument.defaultView; return canvas.ownerDocument.defaultView;
} }
function hasType(status, type){
return status.type.indexOf(type) !== -1;
}
exports.setRandomSupply = function(supply){ exports.setRandomSupply = function(supply){
randomSupply = supply; randomSupply = supply;
}; };
var canvasContextType = new WeakMap();
// changed functions and their fakes // changed functions and their fakes
exports.changedFunctions = { exports.changedFunctions = {
getContext: { getContext: {
type: "context", getStatus: function(obj, status){
object: "HTMLCanvasElement" 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: { 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", object: "HTMLCanvasElement",
fake: function toDataURL(){ fakeGenerator: function(prefs, notify){
var window = getWindow(this); return function toDataURL(){
return window.HTMLCanvasElement.prototype.toDataURL.apply(getFakeCanvas(window, this), arguments); notify("fakedReadout");
var window = getWindow(this);
return window.HTMLCanvasElement.prototype.toDataURL.apply(getFakeCanvas(window, this), arguments);
};
} }
}, },
toBlob: { 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", object: "HTMLCanvasElement",
fake: function toBlob(callback){ fakeGenerator: function(prefs, notify){
var window = getWindow(this); return function toBlob(callback){
return window.HTMLCanvasElement.prototype.toBlob.apply(getFakeCanvas(window, this), arguments); notify("fakedReadout");
var window = getWindow(this);
return window.HTMLCanvasElement.prototype.toBlob.apply(getFakeCanvas(window, this), arguments);
};
}, },
exportOptions: {allowCallbacks: true} exportOptions: {allowCallbacks: true}
}, },
mozGetAsFile: { 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", object: "HTMLCanvasElement",
mozGetAsFile: function mozGetAsFile(callback){ fakeGenerator: function(prefs, notify){
var window = getWindow(this); return function mozGetAsFile(callback){
return window.HTMLCanvasElement.prototype.mozGetAsFile.apply(getFakeCanvas(window, this), arguments); notify("fakedReadout");
var window = getWindow(this);
return window.HTMLCanvasElement.prototype.mozGetAsFile.apply(getFakeCanvas(window, this), arguments);
};
} }
}, },
getImageData: { 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", object: "CanvasRenderingContext2D",
fakeGenerator: function(prefs, notify){ fakeGenerator: function(prefs, notify){
var maxSize = prefs("maxFakeSize") || Number.POSITIVE_INFINITY; var maxSize = prefs("maxFakeSize") || Number.POSITIVE_INFINITY;
@ -91,7 +176,7 @@
context = this; context = this;
} }
else { else {
notify(); notify("fakedReadout");
context = window.HTMLCanvasElement.prototype.getContext.call( context = window.HTMLCanvasElement.prototype.getContext.call(
getFakeCanvas(window, this.canvas), getFakeCanvas(window, this.canvas),
"2d" "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: { readPixels: {
type: "readout", getStatus: function(obj, status){
status.active = hasType(status, "readout") || hasType(status, "input");
return status;
},
object: "WebGLRenderingContext", object: "WebGLRenderingContext",
fake: function readPixels(x, y, width, height, format, type, pixels){ fakeGenerator: function(prefs, notify){
var window = getWindow(this.canvas); return function readPixels(x, y, width, height, format, type, pixels){
var context = window.HTMLCanvasElement.prototype.getContext.call(getFakeCanvas(window, this.canvas), "webGL"); notify("fakedReadout");
return window.WebGLRenderingContext.prototype.readPixels.apply(context, arguments); 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);
};
} }
} }
}; };

View File

@ -8,7 +8,7 @@ var URL = require("sdk/url").URL;
const {parseErrorStack} = require("./callingStack"); const {parseErrorStack} = require("./callingStack");
var tabUtils = require("sdk/tabs/utils"); 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"; "use strict";
var callingStackMsg = parseErrorStack(errorStack); var callingStackMsg = parseErrorStack(errorStack);
@ -16,7 +16,7 @@ exports.notify = function({url, errorStack}, {lists, notificationPref, _, browse
if (notificationPref.doShow() && !lists.get("ignore").match(contentURL)){ if (notificationPref.doShow() && !lists.get("ignore").match(contentURL)){
url = contentURL.href; url = contentURL.href;
var domain = contentURL.hostname; var domain = contentURL.hostname;
var message = _("fakedReadout").replace(/\{url\}/g, domain || url); var message = _(messageId).replace(/\{url\}/g, domain || url);
var tab, tabBrowser; var tab, tabBrowser;
if (browser){ if (browser){