First DOMRect draft

For #236
This commit is contained in:
kkapsner 2018-09-04 23:29:58 +02:00
parent 9d7c801c2b
commit f00c3b674e
8 changed files with 467 additions and 1 deletions

View File

@ -8,6 +8,8 @@
"Nachfrageverweigerungsmodus",
"Oakenpants",
"PDFs",
"Rect",
"Rects",
"Spoofer",
"Thorin",
"Vortäuschaktion",

View File

@ -105,6 +105,10 @@
"message": "Window API",
"description": ""
},
"section_DOMRect-api":{
"message": "DOMRect API",
"description": ""
},
"displayAdvancedSettings_title": {
"message": "Expert mode",
@ -213,6 +217,18 @@
"message": "Do you want to allow window readout?",
"description": ""
},
"askForDOMRectPermission": {
"message": "Do you want to allow the DOMRect API?",
"description": ""
},
"askForDOMRectInputPermission": {
"message": "Do you want to allow DOMRect-API input?",
"description": ""
},
"askForDOMRectReadoutPermission": {
"message": "Do you want to allow DOMRect readout?",
"description": ""
},
"askOnlyOnce_title": {
"message": "Ask only once",
"description": ""
@ -544,6 +560,10 @@
"message": "Faked window readout on {url}",
"description": ""
},
"fakedDOMRectReadout": {
"message": "Faked DOMRect readout on {url}",
"description": ""
},
"fakedInput": {
"message": "Faked at input on {url}",
"description": ""
@ -869,6 +889,15 @@
"description": ""
},
"protectDOMRect_title": {
"message": "Protect DOMRect API",
"description": ""
},
"protectDOMRect_description": {
"message": "This protects against the \"getClientRects()\" fingerprinting and several similar methods.",
"description": ""
},
"theme_title": {
"message": "Theme",
"description": ""

View File

@ -325,7 +325,7 @@
const getter = changedGetter.getterGenerator(checker);
descriptor.get = exportFunction(getter, window);
if (changedGetter.setterGenerator){
if (descriptor.hasOwnProperty("set") && changedGetter.setterGenerator){
const setter = changedGetter.setterGenerator(window, descriptor.set);
descriptor.set = exportFunction(setter, window);
}

View File

@ -19,6 +19,7 @@
const getWrapped = require("sdk/getWrapped");
const {hasType, checkerWrapper} = require("./modifiedAPIFunctions");
const modifiedAudioAPI = require("./modifiedAudioAPI");
const modifiedDOMRectAPI = require("./modifiedDOMRectAPI");
var randomSupply = null;
@ -155,6 +156,7 @@
scope.setRandomSupply = function(supply){
randomSupply = supply;
modifiedAudioAPI.setRandomSupply(supply);
modifiedDOMRectAPI.setRandomSupply(supply);
};
var canvasContextType = new WeakMap();
// changed functions and their fakes
@ -488,4 +490,5 @@
appendModified(modifiedAudioAPI);
appendModified(require("./modifiedHistoryAPI"));
appendModified(require("./modifiedWindowAPI"));
appendModified(modifiedDOMRectAPI);
}());

409
lib/modifiedDOMRectAPI.js Normal file
View File

@ -0,0 +1,409 @@
/* 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 scope;
if ((typeof exports) !== "undefined"){
scope = exports;
}
else {
window.scope.modifiedDOMRectAPI = {};
scope = window.scope.modifiedDOMRectAPI;
}
const {hasType, checkerWrapper} = require("./modifiedAPIFunctions");
const {md5String: hash} = require("./hash");
const getWrapped = require("sdk/getWrapped");
var randomSupply = null;
scope.setRandomSupply = function(supply){
randomSupply = supply;
};
const cache = new Map();
function getHash(domRect){
return hash(new Float64Array([domRect.x, domRect.y, domRect.width, domRect.height]));
}
const registeredRects = new WeakMap();
function registerDOMRect(domRect, notify){
registeredRects.set(getWrapped(domRect), {
notify: function(){
let done = false;
return function(message){
if (!done){
done = true;
notify(message);
}
};
}()
});
}
function getDOMRectRegistration(domRect){
return registeredRects.get(getWrapped(domRect));
}
function getFakeDomRect(window, domRect, notify){
var rng = randomSupply.getRng(4, window);
function getFakeValue(value, i){
if (value % 1 === 0){
return value;
}
else {
return value + 0.01 * (rng(i) / 0xffffffff - 0.5);
}
}
const hash = getHash(domRect);
let cached = cache.get(hash);
if (!cached){
notify("fakedDOMRectReadout");
cached = new domRect.constructor(
getFakeValue(domRect.x, 0),
getFakeValue(domRect.y, 1),
getFakeValue(domRect.width, 2),
getFakeValue(domRect.height, 3)
);
cache.set(getHash(cached), cached);
}
return cached;
}
scope.changedFunctions = {
getClientRects: {
type: "domRect",
getStatus: getStatus,
object: ["Range", "Element"],
fakeGenerator: function(checker){
return function getClientRects(){
return checkerWrapper(checker, this, arguments, function(args, check){
var {prefs, notify, window, original} = check;
var ret = original.apply(this, window.Array.from(args));
for (let i = 0; i < ret.length; i += 1){
registerDOMRect(ret[i], notify);
}
return ret;
});
};
}
},
getBoundingClientRect: {
type: "domRect",
getStatus: getStatus,
object: ["Range", "Element"],
fakeGenerator: function(checker){
return function getBoundingClientRect(){
return checkerWrapper(checker, this, arguments, function(args, check){
var {prefs, notify, window, original} = check;
var ret = original.apply(this, window.Array.from(args));
registerDOMRect(ret, notify);
return ret;
});
};
}
},
getBounds: {
type: "domRect",
getStatus: getStatus,
object: ["DOMQuad"],
fakeGenerator: function(checker){
return function getBounds(){
return checkerWrapper(checker, this, arguments, function(args, check){
var {prefs, notify, window, original} = check;
var ret = original.apply(this, window.Array.from(args));
registerDOMRect(ret, notify);
return ret;
});
};
}
},
getBBox: {
type: "domRect",
getStatus: getStatus,
object: ["SVGGraphicsElement"],
fakeGenerator: function(checker){
return function getBBox(){
return checkerWrapper(checker, this, arguments, function(args, check){
var {prefs, notify, window, original} = check;
var ret = original.apply(this, window.Array.from(args));
registerDOMRect(ret, notify);
return ret;
});
};
}
},
getExtentOfChar: {
type: "domRect",
getStatus: getStatus,
object: ["SVGTextContentElement"],
fakeGenerator: function(checker){
return function getBBox(){
return checkerWrapper(checker, this, arguments, function(args, check){
var {prefs, notify, window, original} = check;
var ret = original.apply(this, window.Array.from(args));
registerDOMRect(ret, notify);
return ret;
});
};
}
},
};
function createCheckerCallback(property){
return function(args, check){
const {prefs, notify, window, original} = check;
const originalValue = original.apply(this, window.Array.from(args));
if (prefs("protectDOMRect", window.location)){
const registration = getDOMRectRegistration(this);
if (registration){
return getFakeDomRect(window, this, registration.notify)[property];
}
}
return originalValue;
};
}
function setProperty(domRect, window, original, newValue, property){ // eslint-disable-line max-params
const registration = getDOMRectRegistration(domRect);
if (registration){
const fakeDomRect = getFakeDomRect(window, domRect, registration.notify);
registeredRects.delete(getWrapped(domRect));
["x", "y", "width", "height"].forEach(function(prop){
if (prop === property){
domRect[prop] = newValue;
}
else {
domRect[prop] = fakeDomRect[prop];
}
});
}
else {
original.apply(domRect, window.Array.from(arguments));
}
}
scope.changedGetters = [
{
objectGetters: [
function(window){return window.DOMRect.prototype;},
function(window){return window.DOMRectReadOnly.prototype;}
],
name: "x",
getterGenerator: function(checker){
const temp = {
get x(){
return checkerWrapper(checker, this, arguments, createCheckerCallback("x"));
}
};
return Object.getOwnPropertyDescriptor(temp, "x").get;
},
setterGenerator: function(window, original){
const temp = {
set x(x){
setProperty(this, window, original, x, "x");
}
};
return Object.getOwnPropertyDescriptor(temp, "x").set;
}
},
{
objectGetters: [
function(window){return window.DOMRect.prototype;},
function(window){return window.DOMRectReadOnly.prototype;}
],
name: "y",
getterGenerator: function(checker){
const temp = {
get y(){
return checkerWrapper(checker, this, arguments, createCheckerCallback("y"));
}
};
return Object.getOwnPropertyDescriptor(temp, "y").get;
},
setterGenerator: function(window, original){
const temp = {
set y(y){
setProperty(this, window, original, y, "y");
}
};
return Object.getOwnPropertyDescriptor(temp, "y").set;
}
},
{
objectGetters: [
function(window){return window.DOMRect.prototype;},
function(window){return window.DOMRectReadOnly.prototype;}
],
name: "width",
getterGenerator: function(checker){
const temp = {
get width(){
return checkerWrapper(checker, this, arguments, createCheckerCallback("width"));
}
};
return Object.getOwnPropertyDescriptor(temp, "width").get;
},
setterGenerator: function(window, original){
const temp = {
set width(width){
setProperty(this, window, original, width, "width");
}
};
return Object.getOwnPropertyDescriptor(temp, "width").set;
}
},
{
objectGetters: [
function(window){return window.DOMRect.prototype;},
function(window){return window.DOMRectReadOnly.prototype;}
],
name: "height",
getterGenerator: function(checker){
const temp = {
get height(){
return checkerWrapper(checker, this, arguments, createCheckerCallback("height"));
}
};
return Object.getOwnPropertyDescriptor(temp, "height").get;
},
setterGenerator: function(window, original){
const temp = {
set height(height){
setProperty(this, window, original, height, "height");
}
};
return Object.getOwnPropertyDescriptor(temp, "height").set;
}
},
{
objectGetters: [
function(window){return window.DOMRectReadOnly.prototype;}
],
name: "left",
getterGenerator: function(checker){
const callback = createCheckerCallback("left");
const temp = {
get left(){
return checkerWrapper(checker, this, arguments, callback);
}
};
return Object.getOwnPropertyDescriptor(temp, "left").get;
}
},
{
objectGetters: [
function(window){return window.DOMRectReadOnly.prototype;}
],
name: "right",
getterGenerator: function(checker){
const temp = {
get right(){
return checkerWrapper(checker, this, arguments, createCheckerCallback("right"));
}
};
return Object.getOwnPropertyDescriptor(temp, "right").get;
}
},
{
objectGetters: [
function(window){return window.DOMRectReadOnly.prototype;}
],
name: "top",
getterGenerator: function(checker){
const temp = {
get top(){
return checkerWrapper(checker, this, arguments, createCheckerCallback("top"));
}
};
return Object.getOwnPropertyDescriptor(temp, "top").get;
}
},
{
objectGetters: [
function(window){return window.DOMRectReadOnly.prototype;}
],
name: "bottom",
getterGenerator: function(checker){
const temp = {
get bottom(){
return checkerWrapper(checker, this, arguments, createCheckerCallback("bottom"));
}
};
return Object.getOwnPropertyDescriptor(temp, "bottom").get;
}
},
{
objectGetters: [
function(window){return window.IntersectionObserverEntry.prototype;}
],
name: "intersectionRect",
getterGenerator: function(checker){
const temp = {
get intersectionRect(){
return checkerWrapper(checker, this, arguments, function(args, check){
const {prefs, notify, window, original} = check;
const originalValue = original.apply(this, window.Array.from(args));
registerDOMRect(originalValue, notify);
return originalValue;
});
}
};
return Object.getOwnPropertyDescriptor(temp, "intersectionRect").get;
}
},
{
objectGetters: [
function(window){return window.IntersectionObserverEntry.prototype;}
],
name: "boundingClientRect",
getterGenerator: function(checker){
const temp = {
get boundingClientRect(){
return checkerWrapper(checker, this, arguments, function(args, check){
const {prefs, notify, window, original} = check;
const originalValue = original.apply(this, window.Array.from(args));
registerDOMRect(originalValue, notify);
return originalValue;
});
}
};
return Object.getOwnPropertyDescriptor(temp, "boundingClientRect").get;
}
},
{
objectGetters: [
function(window){return window.IntersectionObserverEntry.prototype;}
],
name: "rootBounds",
getterGenerator: function(checker){
const temp = {
get rootBounds(){
return checkerWrapper(checker, this, arguments, function(args, check){
const {prefs, notify, window, original} = check;
const originalValue = original.apply(this, window.Array.from(args));
registerDOMRect(originalValue, notify);
return originalValue;
});
}
};
return Object.getOwnPropertyDescriptor(temp, "rootBounds").get;
}
}
];
function getStatus(obj, status){
status = Object.create(status);
status.active = hasType(status, "readout");
return status;
}
scope.changedGetters.forEach(function(changedGetter){
changedGetter.type = "readout";
changedGetter.getStatus = getStatus;
changedGetter.api = "domRect";
});
}());

View File

@ -95,6 +95,15 @@
{name: "Window-API", level: 1},
"opener",
"name",
{name: "DOMRect-API", level: 1},
"getClientRects",
"getBoundingClientRect",
"getBounds",
"getBBox",
"getExtentOfChar",
"intersectionRect",
"boundingClientRect",
"rootBounds",
],
defaultKeyValue: false
},
@ -195,6 +204,7 @@
"audio",
"history",
"window",
"DOMRect",
],
defaultKeyValue: false
},
@ -252,6 +262,11 @@
defaultValue: false,
urlSpecific: true
},
{
name: "protectDOMRect",
defaultValue: true,
urlSpecific: true
},
{
name: "blockDataURLs",
defaultValue: true

View File

@ -39,6 +39,7 @@
"lib/modifiedAudioAPI.js",
"lib/modifiedHistoryAPI.js",
"lib/modifiedWindowAPI.js",
"lib/modifiedDOMRectAPI.js",
"lib/modifiedAPI.js",
"lib/randomSupplies.js",
"lib/intercept.js",

View File

@ -404,6 +404,13 @@
"displayAdvancedSettings": [true]
}
},
"DOMRect-API",
{
"name": "protectDOMRect",
"displayDependencies": {
"displayAdvancedSettings": [true]
}
},
"misc",
{
"name": "theme"