Added test for SVG

For #589 and #590
This commit is contained in:
kkapsner 2022-01-30 10:05:52 +01:00
parent 78183f9efc
commit 734e76180f
6 changed files with 283 additions and 0 deletions

88
lib/modifiedSVGAPI.js Normal file
View File

@ -0,0 +1,88 @@
/* 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";
let scope;
if ((typeof exports) !== "undefined"){
scope = exports;
}
else {
scope = require.register("./modifiedSVGAPI", {});
}
const {checkerWrapper, setProperties, getStatusByFlag} = require("./modifiedAPIFunctions");
const {byteArrayToString: hash} = require("./hash");
let randomSupply = null;
scope.setRandomSupply = function(supply){
randomSupply = supply;
};
function getValueHash(value){
return hash(new Float32Array([value]));
}
const cache = {};
function getFakeValue(value, window){
const valueHash = getValueHash(value);
let cachedValue = cache[valueHash];
if (typeof cachedValue === "number"){
return cachedValue;
}
else {
const rng = randomSupply.getRng(1, window);
const fakedValue = value + 0.01 * (rng(0) / 0xffffffff - 0.5);
const fakedHash = getValueHash(fakedValue);
cache[valueHash] = fakedValue;
cache[fakedHash] = fakedValue;
return fakedValue;
}
}
scope.getFakeValue = getFakeValue;
function getFakeValueCallback(args, check){
const {notify, window, original} = check;
const ret = args.length? original.call(this, ...args): original.call(this);
notify("fakedSVGReadout");
return getFakeValue(ret, window);
}
scope.changedFunctions = {
getTotalLength: {
object: ["SVGGeometryElement"],
fakeGenerator: function(checker){
return function getTotalLength(){
return checkerWrapper(checker, this, arguments, getFakeValueCallback);
};
}
},
getComputedTextLength: {
object: ["SVGTextContentElement"],
fakeGenerator: function(checker){
return function getComputedTextLength(){
return checkerWrapper(checker, this, arguments, getFakeValueCallback);
};
}
},
getSubStringLength: {
object: ["SVGTextContentElement"],
fakeGenerator: function(checker){
return function getSubStringLength(charnum, nchars){
return checkerWrapper(checker, this, arguments, getFakeValueCallback);
};
}
},
};
scope.changedGetters = [];
setProperties(scope.changedFunctions, scope.changedGetters, {
type: "readout",
getStatus: getStatusByFlag("protectSVG"),
api: "svg"
});
}());

View File

@ -16,4 +16,5 @@
Q 90,60 50,90
Q 10,60 10,30 z"/>
<text x="20" y="35" data-name="text" id="text" class="testRect">Text with Unicode &#x1D790;</text>
<text x="20" y="55" data-name="text2" id="text2" class="testRect">&#x73E9A;&#x9843;&#xB3D21;&#x123;&#1337;&#x314;&#xabcde;&#x4a110;&#x7F53A;&#x0815;</text>
</svg>

Before

Width:  |  Height:  |  Size: 470 B

After

Width:  |  Height:  |  Size: 628 B

View File

@ -18,6 +18,7 @@
<li><a href="audioTest.html">Audio Fingerprint test</a></li>
<li><a href="domRectTest.php">DOMRect Fingerprint test</a></li>
<li><a href="textMetricsTest.html">TextMetrics test</a></li>
<li><a href="svgTest.html">SVG 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>

43
test/svgTest.css Normal file
View File

@ -0,0 +1,43 @@
#svg {
position: fixed;
top: -2000%;
}
#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;
}
#test .data td.value {
text-align: right;
}
.small {
font-size: 0.8em;
color: gray;
}
.rectHash {
font-size: 4px;
}
.rectHash:hover {
font-size: 100%;
}
.content-hidable.content-hidden .content, .content-hidable .anti-content {
display: none;
}
.content-hidable .content, .content-hidable.content-hidden .anti-content {
display: initial;
}
.content-hidable .toggle {
cursor: pointer;
}

36
test/svgTest.html Normal file
View File

@ -0,0 +1,36 @@
<!DOCTYPE html>
<html>
<head>
<title>TextMetrics test</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<link href="testIcon.svg" type="image/png" rel="icon">
<link href="testIcon.svg" type="image/png" rel="shortcut icon">
<link rel="stylesheet" href="../default.css" type="text/css">
<link rel="stylesheet" href="./svgTest.css" type="text/css">
<style>
.hash {
font-family: monospace;
font-size: 70%;
}
</style>
</head>
<body>
<h1>SVG test</h1>
<h2>Expected result</h2>
<ol>
<li>the hashes are different to the hashes when CanvasBlocker is disabled</li>
<li>if "refresh" is clicked nothing must change</li>
<li>upon page reload the hash changes (depending on CanvasBlocker settings - e.g. not in the stealth preset)</li>
</ol>
<h2>Tests</h2>
<iframe id="svg" src="domRectSVG.svg"></iframe>
<div id="test" id="svg">
<h3 class="title">SVG</h3>
Hash: <span class="hash"></span><br>
<span class="content-hidable content-hidden">Data: <span class="toggle"><span class="anti-content">&plus;</span><span class="content">&minus;</span></span><span class="data content"></span></span><br>
<button class="refresh">refresh</button>
</div>
<script src="testAPI.js"></script>
<script src="svgTest.js"></script>
</body>
</html>

114
test/svgTest.js Normal file
View File

@ -0,0 +1,114 @@
/* globals testAPI */
(function(){
"use strict";
function byteArrayToHex(arrayBuffer){
const 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("");
}
function formatNumber(number){
const str = number.toString();
return "<span class=small>" + str.substring(0, str.length - 2) + "</span>" +
str.substring(str.length - 2);
}
const svg = document.getElementById("svg");
const output = document.getElementById("test");
function getElements(){
const doc = svg.contentDocument;
return Array.from(doc.querySelectorAll(".testRect"));
}
const tests = [];
function addTest(title, callback){
tests.push({title, callback});
}
async function performTests(){
const elements = getElements();
const results = await Promise.all(tests.map(async function(test){
return {
name: test.title,
data: await Promise.all(elements.map(async function(svgElement){
return await test.callback(svgElement);
}))
};
}));
const data = new Float64Array(elements.length * tests.length);
results.forEach(function(svgData, i){
svgData.data.forEach(function(testData, j){
if ((typeof testData) === "number"){
data[i * elements.length + j] = testData;
}
});
});
const hash = await crypto.subtle.digest("SHA-256", data);
output.querySelector(".hash").textContent = byteArrayToHex(hash);
const dataNode = output.querySelector(".data");
dataNode.innerHTML = "<table><tr><th></th>" +
elements.map(function(svgElement){
return "<th>" + svgElement.dataset.name + "</th>";
}).join("") +
results.map(function(result){
return "<tr><th>" + result.name + "</th>" + result.data.map(function(value){
if ((typeof value) === "number"){
return "<td class=\"value\">" +
formatNumber(value) +
"</td>";
}
else {
return "<td class=\"value unavailable\">--</td>";
}
}).join("") + "</tr>";
}).join("") +
"</table>";
}
svg.addEventListener("load", function(){
addTest("getTotalLength", function(element){
if (!element.getTotalLength){
return null;
}
return element.getTotalLength();
});
addTest("getComputedTextLength", function(element){
if (!element.getComputedTextLength){
return null;
}
return element.getComputedTextLength();
});
[{start: 3, end: 7}, {start: 7, end: 11}, {start: 3, end: 11}].forEach(function(substringDefinition){
addTest(
`getSubStringLength(${substringDefinition.start}, ${substringDefinition.end})`,
function(element){
if (!element.getSubStringLength){
return null;
}
return element.getSubStringLength(substringDefinition.start, substringDefinition.end);
}
);
});
test.querySelector(".refresh").addEventListener("click", function(){
performTests();
});
performTests();
document.querySelectorAll(".content-hidable").forEach(function(parentNode){
parentNode.querySelector(".toggle").addEventListener("click", function(){
parentNode.classList.toggle("content-hidden");
});
});
});
}());