Merge branch 'DOMRect'

Fixes #236
This commit is contained in:
kkapsner 2018-09-06 20:24:37 +02:00
commit ca6e5d83e1
18 changed files with 850 additions and 39 deletions

View File

@ -20,6 +20,7 @@ Geschützte "Fingerprinting"-APIs:
<li>audio</li>
<li>history</li>
<li>window (standardmäßig deaktiviert)</li>
<li>DOMRect</li>
</ul>
Falls Sie Fehler finden oder Verbesserungsvorschläge haben, teilen Sie mir das bitte auf https://github.com/kkapsner/CanvasBlocker/issues mit.

View File

@ -20,6 +20,7 @@ Protected "fingerprinting" APIs:
<li>audio</li>
<li>history</li>
<li>window (disabled by default)</li>
<li>DOMRect</li>
</ul>
Please report issues and feature requests at https://github.com/kkapsner/CanvasBlocker/issues

View File

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

View File

@ -13,6 +13,14 @@ The different block modes are:
<li>allow everything: Ignore all lists and allow the &lt;canvas&gt; API on all websites.</li>
</ul>
Protected "fingerprinting" APIs:
* canvas 2d
* webGL
* audio
* history
* window (disabled by default)
* DOMRect
Special thanks to:
* spodermenpls for finding all the typos
* Thorin-Oakenpants for the icon idea

View File

@ -105,6 +105,10 @@
"message": "Window API",
"description": ""
},
"section_DOMRect-api":{
"message": "DOMRect API",
"description": ""
},
"displayAdvancedSettings_title": {
"message": "Expertenmodus",
@ -213,6 +217,18 @@
"message": "Wollen Sie das Auslesen über die Window-API erlauben?",
"description": ""
},
"askForDOMRectPermission": {
"message": "Wollen Sie die DOMRect-API erlauben?",
"description": ""
},
"askForDOMRectInputPermission": {
"message": "Wollen Sie das Schreiben über die DOMRect-API erlauben?",
"description": ""
},
"askForDOMRectReadoutPermission": {
"message": "Wollen Sie das Auslesen über die DOMRect-API erlauben?",
"description": ""
},
"askOnlyOnce_title": {
"message": "Nur einmal nachfragen",
"description": ""
@ -544,6 +560,10 @@
"message": "Window-Auslese vorgetäuscht auf {url}",
"description": ""
},
"fakedDOMRectReadout": {
"message": "DOMRect-Auslese vorgetäuscht auf {url}",
"description": ""
},
"fakedInput": {
"message": "Bei Ausgabe vorgetäuscht auf {url}",
"description": ""
@ -695,14 +715,14 @@
"message": "Dateispezifische Whitelist",
"description": ""
},
"whiteList_description": {
"message": "Domänen oder URLs, die die <canvas>-API verwenden dürfen. Mehrere Einträge müssen durch ein Komma getrennt werden.",
"description": ""
},
"whiteList_title": {
"message": "Whitelist",
"description": ""
},
"whiteList_description": {
"message": "Domänen oder URLs, die die <canvas>-API verwenden dürfen. Mehrere Einträge müssen durch ein Komma getrennt werden.",
"description": ""
},
"whitelist": {
"message": "erlauben",
"description": ""
@ -716,14 +736,14 @@
"description": ""
},
"sessionWhiteList_description": {
"message": "Domänen oder URLs, die die <canvas>-API in der aktuellen Sitzung verwenden dürfen. Mehrere Einträge müssen durch ein Komma getrennt werden.",
"description": ""
},
"sessionWhiteList_title": {
"message": "Sitzungs-Whitelist",
"description": ""
},
"sessionWhiteList_description": {
"message": "Domänen oder URLs, die die <canvas>-API in der aktuellen Sitzung verwenden dürfen. Mehrere Einträge müssen durch ein Komma getrennt werden.",
"description": ""
},
"whitelistDomainTemporarily": {
"message": "erlaube Domain übergangsweise",
@ -870,6 +890,24 @@
"description": ""
},
"protectDOMRect_title": {
"message": "DOMRect-API beschützen",
"description": ""
},
"protectDOMRect_description": {
"message": "Beschützt vor dem \"getClientRects()\" Fingerprint und einigen anderen ähnlichen Methoden.",
"description": ""
},
"domRectIntegerFactor_title": {
"message": "DOMRect Ganzzahlfaktor",
"description": ""
},
"domRectIntegerFactor_description": {
"message": "Ein Bruchteil eines Pixels kann durch CSS kontrolliert werden. Eigenschaften eines DOMRect, die multipliziert mit diesem Faktor eine ganze Zahl ergeben, dürfen nicht verändert werden um eine Detektion zu verhindern.",
"description": ""
},
"theme_title": {
"message": "Theme",
"description": ""

View File

@ -105,6 +105,10 @@
"message": "Window API",
"description": ""
},
"section_DOMRect-api":{
"message": "DOMRect API",
"description": ""
},
"displayAdvancedSettings_title": {
"message": "Expert mode",
@ -182,11 +186,11 @@
"description": ""
},
"askForAudioInputPermission": {
"message": "Do you want to allow audio-API input?",
"message": "Do you want to allow audio API input?",
"description": ""
},
"askForAudioReadoutPermission": {
"message": "Do you want to allow audio readout?",
"message": "Do you want to allow audio API readout?",
"description": ""
},
"askForHistoryPermission": {
@ -194,11 +198,11 @@
"description": ""
},
"askForHistoryInputPermission": {
"message": "Do you want to allow history-API input?",
"message": "Do you want to allow history API input?",
"description": ""
},
"askForHistoryReadoutPermission": {
"message": "Do you want to allow history readout?",
"message": "Do you want to allow history API readout?",
"description": ""
},
"askForWindowPermission": {
@ -206,11 +210,23 @@
"description": ""
},
"askForWindowInputPermission": {
"message": "Do you want to allow window-API input?",
"message": "Do you want to allow window API input?",
"description": ""
},
"askForWindowReadoutPermission": {
"message": "Do you want to allow window readout?",
"message": "Do you want to allow window API 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 API readout?",
"description": ""
},
"askOnlyOnce_title": {
@ -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,24 @@
"description": ""
},
"protectDOMRect_title": {
"message": "Protect DOMRect API",
"description": ""
},
"protectDOMRect_description": {
"message": "This protects against the \"getClientRects()\" fingerprinting and several similar methods.",
"description": ""
},
"domRectIntegerFactor_title": {
"message": "DOMRect integer factor",
"description": ""
},
"domRectIntegerFactor_description": {
"message": "Some fraction of a pixel can be controlled by CSS. To prevent detection values of a DOMRect that multiplied with this factor are integers will not be altered.",
"description": ""
},
"theme_title": {
"message": "Theme",
"description": ""

View File

@ -100,7 +100,13 @@
forEachFunction(function({name, object}){
var map = originalPropertyDescriptors[name] || new WeakMap();
originalPropertyDescriptors[name] = map;
map.set(object, Object.getOwnPropertyDescriptor(object, name));
const originalPropertyDescriptor = Object.getOwnPropertyDescriptor(object, name);
if (!originalPropertyDescriptor){
return;
}
map.set(object, originalPropertyDescriptor);
Object.defineProperty(
object,
name,
@ -145,11 +151,14 @@
if (preIntercepted){
preIntercepted = false;
forEachFunction(function({name, object}){
Object.defineProperty(
object,
name,
originalPropertyDescriptors[name].get(object)
);
const originalPropertyDescriptor = originalPropertyDescriptors[name].get(object);
if (originalPropertyDescriptor){
Object.defineProperty(
object,
name,
originalPropertyDescriptor
);
}
});
}
};
@ -286,26 +295,28 @@
var original = constructor.prototype[name];
const checker = generateChecker(name, changedFunction, siteStatus, original);
var descriptor = Object.getOwnPropertyDescriptor(constructor.prototype, name);
if (descriptor.hasOwnProperty("value")){
if (changedFunction.fakeGenerator){
descriptor.value = exportFunction(
changedFunction.fakeGenerator(checker),
window
);
if (descriptor){
if (descriptor.hasOwnProperty("value")){
if (changedFunction.fakeGenerator){
descriptor.value = exportFunction(
changedFunction.fakeGenerator(checker),
window
);
}
else {
descriptor.value = null;
}
}
else {
descriptor.value = null;
descriptor.get = exportFunction(function(){
return exportFunction(
changedFunction.fakeGenerator(checker),
window
);
}, window);
}
Object.defineProperty(constructor.prototype, name, descriptor);
}
else {
descriptor.get = exportFunction(function(){
return exportFunction(
changedFunction.fakeGenerator(checker),
window
);
}, window);
}
Object.defineProperty(constructor.prototype, name, descriptor);
}
});
}
@ -319,14 +330,14 @@
const object = objectGetter(getWrapped(window));
if (object){
const descriptor = Object.getOwnPropertyDescriptor(object, name);
if (descriptor.hasOwnProperty("get")){
if (descriptor && descriptor.hasOwnProperty("get")){
var original = descriptor.get;
const checker = generateChecker(name, changedGetter, siteStatus, original);
const getter = changedGetter.getterGenerator(checker);
descriptor.get = exportFunction(getter, window);
if (changedGetter.setterGenerator){
const setter = changedGetter.setterGenerator(window, descriptor.set);
if (descriptor.hasOwnProperty("set") && changedGetter.setterGenerator){
const setter = changedGetter.setterGenerator(window, descriptor.set, prefs);
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);
}());

414
lib/modifiedDOMRectAPI.js Normal file
View File

@ -0,0 +1,414 @@
/* 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, prefs, notify){
var rng = randomSupply.getRng(4, window);
function getFakeValue(value, i){
if ((value * prefs("domRectIntegerFactor", window.location)) % 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: "readout",
api: "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: "readout",
api: "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: "readout",
api: "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: "readout",
api: "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: "readout",
api: "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, prefs, registration.notify)[property];
}
}
return originalValue;
};
}
function setProperty(domRect, window, original, newValue, property, prefs){ // eslint-disable-line max-params
const registration = getDOMRectRegistration(domRect);
if (registration){
const fakeDomRect = getFakeDomRect(window, domRect, prefs, 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, prefs){
const temp = {
set x(x){
setProperty(this, window, original, x, "x", prefs);
}
};
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, prefs){
const temp = {
set y(y){
setProperty(this, window, original, y, "y", prefs);
}
};
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, prefs){
const temp = {
set width(width){
setProperty(this, window, original, width, "width", prefs);
}
};
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, prefs){
const temp = {
set height(height){
setProperty(this, window, original, height, "height", prefs);
}
};
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, prefs){
status = Object.create(status);
status.active = prefs("protectDOMRect", status.url) && 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,15 @@
defaultValue: false,
urlSpecific: true
},
{
name: "protectDOMRect",
defaultValue: true,
urlSpecific: true
},
{
name: "domRectIntegerFactor",
defaultValue: 4
},
{
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,20 @@
"displayAdvancedSettings": [true]
}
},
"DOMRect-API",
{
"name": "protectDOMRect",
"displayDependencies": {
"displayAdvancedSettings": [true]
}
},
{
"name": "domRectIntegerFactor",
"displayDependencies": {
"protectDOMRect": [true],
"displayAdvancedSettings": [true]
}
},
"misc",
{
"name": "theme"

View File

@ -4,6 +4,7 @@ Version 0.5.4:
new features:
- added save/load directly to/from file option
- added protection for DOMRect (getClientRects)
fixes:
- window and audio API were always blocked when using any of the "block ..." modes

View File

@ -177,6 +177,16 @@ addTest("property descriptor", function(log){
configurable: true
}
},
{
object: Element.prototype,
name: "getClientRects",
descriptor: {
value: function getClientRects(){},
writable: true,
enumerable: true,
configurable: true
}
},
];
return properties.reduce(function(pass, property){
@ -419,4 +429,91 @@ addTest("window name change", function(log){
return true;
}
return false;
});
function checkDOMRectData(rect, data, log){
"use strict";
var detected = false;
["x", "y", "width", "height"].forEach(function(property){
if (data[property] !== rect[property]){
log("Wrong value for", property, ":", data[property], "!=", rect[property]);
detected = true;
}
});
return detected;
}
function getRectByData(data){
"use strict";
var el = document.createElement("div");
el.style.cssText = "position: fixed;" +
"left: " + data.x + "px; " +
"top: " + data.y + "px; " +
"width: " + data.width + "px; " +
"height: " + data.height + "px;";
document.body.appendChild(el);
var rect = el.getBoundingClientRect();
document.body.removeChild(el);
return rect;
}
addTest("self created DOMRect", function(log){
"use strict";
var data = {
x: Math.PI,
y: Math.E,
width: Math.LOG10E,
height: Math.LOG2E
};
var rect = new DOMRect(data.x, data.y, data.width, data.height);
return checkDOMRectData(rect, data, log);
});
addTest("known DOMRect", function(log){
"use strict";
var data = {
x: 1 + 1/4,
y: 2,
width: 3,
height: 4
};
var rect = getRectByData(data);
return checkDOMRectData(rect, data, log);
});
addTest("changed DOMRect", function(log){
"use strict";
var data = {
x: Math.PI,
y: 2,
width: 3,
height: 4
};
var rect = getRectByData(data);
rect.x = Math.PI;
return checkDOMRectData(rect, data, log);
});
addTest("recreated DOMRect", function(log){
"use strict";
var data = {
x: Math.PI,
y: Math.E,
width: Math.LOG10E,
height: Math.LOG2E
};
var rect = getRectByData(data);
var rect2 = getRectByData(rect);
return checkDOMRectData(rect2, rect, log);
});

42
test/domRectIFrame.html Normal file
View File

@ -0,0 +1,42 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style>
#inside{
white-space: nowrap;
color:#5B5B5B;
position: absolute;
padding: 1.3333px;
left: 10.5555px;
top: 28.4444px;
font-size: 24.5555px;
-ms-transform: scale(1.31123) matrix3d(0.373513, -0.0440105, 0, -0.000202461, -0.0851682, 0.616234, 0, -0.00123197, 2.17, 0.21, 1, 0.02, 13.81, 2.11, 0, 0.98);
-moz-transform: scale(1.31123) matrix3d(0.373513, -0.0440105, 0, -0.000202461, -0.0851682, 0.616234, 0, -0.00123197, 2.17, 0.21, 1, 0.02, 13.81, 2.11, 0, 0.98);
-webkit-transform: scale(1.31123) matrix3d(0.373513, -0.0440105, 0, -0.000202461, -0.0851682, 0.616234, 0, -0.00123197, 2.17, 0.21, 1, 0.02, 13.81, 2.11, 0, 0.98);
transform: scale(1.31123) matrix3d(0.373513, -0.0440105, 0, -0.000202461, -0.0851682, 0.616234, 0, -0.00123197, 2.17, 0.21, 1, 0.02, 13.81, 2.11, 0, 0.98);
-ms-transform-origin: 0.1111px 0.2222px 0.3333px;
-moz-transform-origin: 0.1111px 0.2222px 0.3333px;
-webkit-transform-origin: 0.1111px 0.2222px 0.3333px;
transform-origin: 0.1111px 0.2222px 0.3333px;
}
</style>
</head>
<body>
<div id="inside">
<h1 id="rect0">https://browserleaks.com<i>/rects</i></h4>
<span id="rect1"><strong>Element.getClientRects (̿▀̿&thinsp;̿Ĺ̯̿̿▀̿ ̿)̄ </strong></span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<span id="rect2">F i n g e r p r i n t i n g ?</span>
</div>
</body>
</html>

42
test/domRectTest.html Normal file
View File

@ -0,0 +1,42 @@
<!DOCTYPE html>
<html>
<head>
<title>DOMRect test</title>
<style>
#iframe {
position: absolute;
top: -2000%;
}
.template {
display: none;
}
.test {
display: inline-block;
margin: 1em;
}
.test .data table {
border-collapse: collapse;
}
.test .data th {
padding: 0.4em;
}
.test .data td{
border: 1px solid #c7c7c7;
padding: 0.4em;
}
</style>
</head>
<body>
<h1>DOMRect test</h1>
<iframe id="iframe" src="domRectIFrame.html"></iframe>
<div id="tests">
<div class="test">
<h2 class="title"></h2>
Hash: <span class="hash"></span><br>
Data: <span class="data"></span><br>
<button>refresh</button>
</div>
</div>
<script src="domRectTest.js"></script>
</body>
</html>

78
test/domRectTest.js Normal file
View File

@ -0,0 +1,78 @@
(function(){
"use strict";
function byteArrayToHex(arrayBuffer){
var chunks = [];
(new Uint32Array(arrayBuffer)).forEach(function(num){
chunks.push(num.toString(16));
});
return chunks.map(function(chunk){
return "0".repeat(8 - chunk.length) + chunk;
}).join("");
}
const container = document.getElementById("tests");
const iframe = document.getElementById("iframe");
const template = document.querySelector(".test");
template.parentElement.removeChild(template);
function getElements(){
const doc = iframe.contentDocument;
return Array.from(doc.querySelectorAll("*[id^=rect]"));
}
function createTest(title, callback){
const properties = ["x", "y", "width", "height", "top", "left", "right", "bottom"];
function performTest(){
const rects = getElements().map(callback);
const data = new Float64Array(rects.length * properties.length);
rects.forEach(function(rect, i){
properties.forEach(function(property, j){
data[i * properties.length + j] = rect[property];
});
});
crypto.subtle.digest("SHA-256", data)
.then(function(hash){
output.querySelector(".hash").textContent = byteArrayToHex(hash);
});
output.querySelector(".data").innerHTML = "<table><tr><th></th>" +
rects.map(function(rect, i){
return "<th>rect " + (i + 1) + "</th>";
}).join("") +
"</tr>" +
properties.map(function(property){
return "<tr><th>" + property + "</th>" + rects.map(function(rect, i){
return "<td>" + rect[property] + "</td>";
}).join("") + "</tr>";
}).join("") +
"</table>";
}
const output = template.cloneNode(true);
output.querySelector(".title").textContent = title;
output.querySelector("button").addEventListener("click", performTest);
container.appendChild(output);
performTest();
}
iframe.addEventListener("load", function(){
createTest("Element.getClientRects", function(element){
return element.getClientRects()[0];
});
createTest("Element.getBoundingClientRect", function(element){
return element.getBoundingClientRect();
});
createTest("Range.getClientRects", function(element){
var range = document.createRange();
range.selectNode(element);
return range.getClientRects()[0];
});
createTest("Range.getBoundingClientRect", function(element){
var range = document.createRange();
range.selectNode(element);
return range.getBoundingClientRect();
});
});
}());

View File

@ -10,6 +10,7 @@
<li><a href="test.html">Fingerprinting test</a></li>
<li><a href="dataUrlTest.php">Data-URL test</a></li>
<li><a href="audioTest.html">Audio Fingerprint test</a></li>
<li><a href="domRectTest.html">DOMRect Fingerprint test</a></li>
<li><a href="detectionTest.html">Detection test</a></li>
<li><a href="performanceTest.html">Performance test</a></li>
<li><a href="webGL-Test.html">Support for webGL</a></li>