mirror of
https://github.com/kkapsner/CanvasBlocker
synced 2024-12-22 12:50:36 +01:00
parent
692b4616e2
commit
ec128796e3
@ -13,6 +13,7 @@ Beschützte "Fingerprinting"-APIs:
|
|||||||
<li>history</li>
|
<li>history</li>
|
||||||
<li>window (standardmäßig deaktiviert)</li>
|
<li>window (standardmäßig deaktiviert)</li>
|
||||||
<li>DOMRect</li>
|
<li>DOMRect</li>
|
||||||
|
<li>TextMetrics</li>
|
||||||
<li>navigator (standardmäßig deaktiviert)</li>
|
<li>navigator (standardmäßig deaktiviert)</li>
|
||||||
<li>screen</li>
|
<li>screen</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -13,6 +13,7 @@ Protected "fingerprinting" APIs:
|
|||||||
<li>history</li>
|
<li>history</li>
|
||||||
<li>window (disabled by default)</li>
|
<li>window (disabled by default)</li>
|
||||||
<li>DOMRect</li>
|
<li>DOMRect</li>
|
||||||
|
<li>TextMetrics</li>
|
||||||
<li>navigator (disabled by default)</li>
|
<li>navigator (disabled by default)</li>
|
||||||
<li>screen</li>
|
<li>screen</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
@ -42,6 +42,7 @@
|
|||||||
"mediump",
|
"mediump",
|
||||||
"micrococo",
|
"micrococo",
|
||||||
"monero",
|
"monero",
|
||||||
|
"monospace",
|
||||||
"nocanvas",
|
"nocanvas",
|
||||||
"onedrive",
|
"onedrive",
|
||||||
"onloaded",
|
"onloaded",
|
||||||
|
@ -15,6 +15,7 @@ Protected "fingerprinting" APIs:
|
|||||||
* history
|
* history
|
||||||
* window (disabled by default)
|
* window (disabled by default)
|
||||||
* DOMRect
|
* DOMRect
|
||||||
|
* TextMetrics
|
||||||
* navigator (disabled by default)
|
* navigator (disabled by default)
|
||||||
* screen
|
* screen
|
||||||
|
|
||||||
|
@ -154,6 +154,10 @@
|
|||||||
"message": "DOMRect API",
|
"message": "DOMRect API",
|
||||||
"description": ""
|
"description": ""
|
||||||
},
|
},
|
||||||
|
"section_TextMetrics-api": {
|
||||||
|
"message": "TextMetrics API",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
"section_Navigator-api": {
|
"section_Navigator-api": {
|
||||||
"message": "Navigator API",
|
"message": "Navigator API",
|
||||||
"description": ""
|
"description": ""
|
||||||
@ -279,6 +283,18 @@
|
|||||||
"message": "Do you want to allow DOMRect API readout?",
|
"message": "Do you want to allow DOMRect API readout?",
|
||||||
"description": ""
|
"description": ""
|
||||||
},
|
},
|
||||||
|
"askForTextMetricsPermission": {
|
||||||
|
"message": "Do you want to allow the TextMetrics API?",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"askForTextMetricsInputPermission": {
|
||||||
|
"message": "Do you want to allow TextMetrics API input?",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"askForTextMetricsReadoutPermission": {
|
||||||
|
"message": "Do you want to allow TextMetrics API readout?",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
"askForNavigatorPermission": {
|
"askForNavigatorPermission": {
|
||||||
"message": "Do you want to allow the navigator API?",
|
"message": "Do you want to allow the navigator API?",
|
||||||
"description": ""
|
"description": ""
|
||||||
@ -705,6 +721,10 @@
|
|||||||
"message": "Faked DOMRect readout on {url}",
|
"message": "Faked DOMRect readout on {url}",
|
||||||
"description": ""
|
"description": ""
|
||||||
},
|
},
|
||||||
|
"fakedTextMetricsReadout": {
|
||||||
|
"message": "Faked TextMetrics readout on {url}",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
"fakedNavigatorReadout": {
|
"fakedNavigatorReadout": {
|
||||||
"message": "Faked navigator readout on {url}",
|
"message": "Faked navigator readout on {url}",
|
||||||
"description": ""
|
"description": ""
|
||||||
@ -1154,6 +1174,19 @@
|
|||||||
"description": ""
|
"description": ""
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"protectTextMetrics_title": {
|
||||||
|
"message": "Protect TextMetrics API",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"protectTextMetrics_description": {
|
||||||
|
"message": "This protects against the \"measureText()\" fingerprinting which can be used to cross validate DOMRect values.",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"protectTextMetrics_urlSpecific": {
|
||||||
|
"message": "To exclude specific websites from this protection, click on the black arrow to open the menu, add the domain or URL by clicking on \"+\" and remove its checkmark.",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
|
||||||
"protectNavigator_title": {
|
"protectNavigator_title": {
|
||||||
"message": "Protect navigator API",
|
"message": "Protect navigator API",
|
||||||
"description": ""
|
"description": ""
|
||||||
|
@ -39,6 +39,7 @@
|
|||||||
appendModified(require("./modifiedHistoryAPI"));
|
appendModified(require("./modifiedHistoryAPI"));
|
||||||
appendModified(require("./modifiedWindowAPI"));
|
appendModified(require("./modifiedWindowAPI"));
|
||||||
appendModified(require("./modifiedDOMRectAPI"));
|
appendModified(require("./modifiedDOMRectAPI"));
|
||||||
|
appendModified(require("./modifiedTextMetricsAPI"));
|
||||||
appendModified(require("./modifiedNavigatorAPI"));
|
appendModified(require("./modifiedNavigatorAPI"));
|
||||||
appendModified(require("./modifiedScreenAPI"));
|
appendModified(require("./modifiedScreenAPI"));
|
||||||
}());
|
}());
|
@ -50,7 +50,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const cache = {};
|
const cache = {};
|
||||||
const valueCache = [{}, {}, {}, {}];
|
const valueCache = [{}, {}, {}, {}, {}];
|
||||||
|
scope.cache = {
|
||||||
|
valueCache,
|
||||||
|
X: 0,
|
||||||
|
Y: 1,
|
||||||
|
WIDTH: 2,
|
||||||
|
HEIGHT: 3,
|
||||||
|
OTHER: 4
|
||||||
|
};
|
||||||
function getFakeDomRect(window, domRect, prefs, notify){
|
function getFakeDomRect(window, domRect, prefs, notify){
|
||||||
const hash = getHash(domRect);
|
const hash = getHash(domRect);
|
||||||
let cached = cache[hash];
|
let cached = cache[hash];
|
||||||
|
96
lib/modifiedTextMetricsAPI.js
Normal file
96
lib/modifiedTextMetricsAPI.js
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
/* 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("./modifiedTextMetricsAPI", {});
|
||||||
|
}
|
||||||
|
|
||||||
|
const {checkerWrapper, setProperties, getStatusByFlag} = require("./modifiedAPIFunctions");
|
||||||
|
const {byteArrayToString: hash} = require("./hash");
|
||||||
|
const {cache} = require("./modifiedDOMRectAPI");
|
||||||
|
const valueCache = cache.valueCache;
|
||||||
|
|
||||||
|
function getValueHash(value){
|
||||||
|
return hash(new Float32Array([value]));
|
||||||
|
}
|
||||||
|
|
||||||
|
let randomSupply = null;
|
||||||
|
scope.setRandomSupply = function(supply){
|
||||||
|
randomSupply = supply;
|
||||||
|
};
|
||||||
|
|
||||||
|
function getFakeValue(window, value, i, prefs){
|
||||||
|
const valueHash = getValueHash(value);
|
||||||
|
const cache = valueCache[i];
|
||||||
|
let cachedValue = cache[valueHash];
|
||||||
|
if (typeof cachedValue === "number"){
|
||||||
|
return cachedValue;
|
||||||
|
}
|
||||||
|
if ((value * prefs("domRectIntegerFactor", window.location)) % 1 === 0){
|
||||||
|
cache[valueHash] = value;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const rng = randomSupply.getRng(5, window);
|
||||||
|
const fakedValue = value + 0.01 * (rng(i) / 0xffffffff - 0.5);
|
||||||
|
const fakedHash = getValueHash(fakedValue);
|
||||||
|
cache[valueHash] = fakedValue;
|
||||||
|
cache[fakedHash] = fakedValue;
|
||||||
|
return fakedValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateChangedTextMetricsPropertyGetter(property, cacheIndex){
|
||||||
|
const changedGetter = {
|
||||||
|
objectGetters: [
|
||||||
|
function(window){return window.TextMetrics && window.TextMetrics.prototype;}
|
||||||
|
],
|
||||||
|
name: property,
|
||||||
|
getterGenerator: function(checker){
|
||||||
|
const temp = {
|
||||||
|
get [property](){
|
||||||
|
return checkerWrapper(checker, this, arguments, function(args, check){
|
||||||
|
const {prefs, notify, window, original} = check;
|
||||||
|
const originalValue = original.call(this, ...args);
|
||||||
|
const returnValue = getFakeValue(window, originalValue, cacheIndex, prefs);
|
||||||
|
if (originalValue !== returnValue){
|
||||||
|
notify("fakedTextMetricsReadout");
|
||||||
|
}
|
||||||
|
return returnValue;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return Object.getOwnPropertyDescriptor(temp, property).get;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return changedGetter;
|
||||||
|
}
|
||||||
|
|
||||||
|
scope.changedGetters = [
|
||||||
|
generateChangedTextMetricsPropertyGetter("width", cache.WIDTH),
|
||||||
|
generateChangedTextMetricsPropertyGetter("actualBoundingBoxAscent", cache.OTHER),
|
||||||
|
generateChangedTextMetricsPropertyGetter("actualBoundingBoxDescent", cache.OTHER),
|
||||||
|
generateChangedTextMetricsPropertyGetter("actualBoundingBoxLeft", cache.OTHER),
|
||||||
|
generateChangedTextMetricsPropertyGetter("actualBoundingBoxRight", cache.OTHER),
|
||||||
|
generateChangedTextMetricsPropertyGetter("alphabeticBaseline", cache.OTHER),
|
||||||
|
generateChangedTextMetricsPropertyGetter("emHeightAscent", cache.OTHER),
|
||||||
|
generateChangedTextMetricsPropertyGetter("emHeightDescent", cache.OTHER),
|
||||||
|
generateChangedTextMetricsPropertyGetter("fontBoundingBoxAscent", cache.OTHER),
|
||||||
|
generateChangedTextMetricsPropertyGetter("fontBoundingBoxDescent", cache.OTHER),
|
||||||
|
generateChangedTextMetricsPropertyGetter("hangingBaseline", cache.OTHER),
|
||||||
|
generateChangedTextMetricsPropertyGetter("ideographicBaseline", cache.OTHER),
|
||||||
|
];
|
||||||
|
|
||||||
|
setProperties({}, scope.changedGetters, {
|
||||||
|
type: "readout",
|
||||||
|
getStatus: getStatusByFlag("protectTextMetrics"),
|
||||||
|
api: "textMetrics"
|
||||||
|
});
|
||||||
|
}());
|
@ -117,7 +117,20 @@
|
|||||||
"getExtentOfChar @ domRect",
|
"getExtentOfChar @ domRect",
|
||||||
"intersectionRect @ domRect",
|
"intersectionRect @ domRect",
|
||||||
"boundingClientRect @ domRect",
|
"boundingClientRect @ domRect",
|
||||||
"rootBounds",
|
"rootBounds @ domRect",
|
||||||
|
{name: "TextMetrics-API", level: 1},
|
||||||
|
"width @ textMetrics",
|
||||||
|
"actualBoundingBoxAscent @ textMetrics",
|
||||||
|
"actualBoundingBoxDescent @ textMetrics",
|
||||||
|
"actualBoundingBoxLeft @ textMetrics",
|
||||||
|
"actualBoundingBoxRight @ textMetrics",
|
||||||
|
"alphabeticBaseline @ textMetrics",
|
||||||
|
"emHeightAscent @ textMetrics",
|
||||||
|
"emHeightDescent @ textMetrics",
|
||||||
|
"fontBoundingBoxAscent @ textMetrics",
|
||||||
|
"fontBoundingBoxDescent @ textMetrics",
|
||||||
|
"hangingBaseline @ textMetrics",
|
||||||
|
"ideographicBaseline @ textMetrics",
|
||||||
{name: "Navigator-API", level: 1},
|
{name: "Navigator-API", level: 1},
|
||||||
"appCodeName @ navigator",
|
"appCodeName @ navigator",
|
||||||
"appName @ navigator",
|
"appName @ navigator",
|
||||||
@ -335,6 +348,11 @@
|
|||||||
name: "domRectIntegerFactor",
|
name: "domRectIntegerFactor",
|
||||||
defaultValue: 4
|
defaultValue: 4
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "protectTextMetrics",
|
||||||
|
defaultValue: true,
|
||||||
|
urlSpecific: true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "blockDataURLs",
|
name: "blockDataURLs",
|
||||||
defaultValue: true,
|
defaultValue: true,
|
||||||
|
@ -50,6 +50,7 @@
|
|||||||
"lib/modifiedHistoryAPI.js",
|
"lib/modifiedHistoryAPI.js",
|
||||||
"lib/modifiedWindowAPI.js",
|
"lib/modifiedWindowAPI.js",
|
||||||
"lib/modifiedDOMRectAPI.js",
|
"lib/modifiedDOMRectAPI.js",
|
||||||
|
"lib/modifiedTextMetricsAPI.js",
|
||||||
"lib/navigator.js",
|
"lib/navigator.js",
|
||||||
"lib/modifiedNavigatorAPI.js",
|
"lib/modifiedNavigatorAPI.js",
|
||||||
"lib/modifiedScreenAPI.js",
|
"lib/modifiedScreenAPI.js",
|
||||||
|
@ -83,6 +83,7 @@
|
|||||||
{mainFlag: "protectAudio", section: "Audio-API"},
|
{mainFlag: "protectAudio", section: "Audio-API"},
|
||||||
{mainFlag: "protectWindow", section: "Window-API"},
|
{mainFlag: "protectWindow", section: "Window-API"},
|
||||||
{mainFlag: "protectDOMRect", section: "DOMRect-API"},
|
{mainFlag: "protectDOMRect", section: "DOMRect-API"},
|
||||||
|
{mainFlag: "protectTextMetrics", section: "TextMetrics-API"},
|
||||||
{mainFlag: "protectNavigator", section: "Navigator-API"},
|
{mainFlag: "protectNavigator", section: "Navigator-API"},
|
||||||
{mainFlag: "protectScreen", section: "Screen-API"},
|
{mainFlag: "protectScreen", section: "Screen-API"},
|
||||||
].forEach(function(api){
|
].forEach(function(api){
|
||||||
|
@ -619,6 +619,25 @@
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "TextMetrics-API",
|
||||||
|
settings: [
|
||||||
|
{
|
||||||
|
"name": "protectTextMetrics"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "protectedAPIFeatures",
|
||||||
|
"replaceKeyPattern": / @ .+$/,
|
||||||
|
"displayedSection": "TextMetrics-API",
|
||||||
|
"displayDependencies": [
|
||||||
|
{
|
||||||
|
"protectTextMetrics": [true],
|
||||||
|
"displayAdvancedSettings": [true]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "Navigator-API",
|
name: "Navigator-API",
|
||||||
settings: [
|
settings: [
|
||||||
|
@ -5,6 +5,7 @@ Version 1.2:
|
|||||||
|
|
||||||
new features:
|
new features:
|
||||||
- added warning if some features of a API are disabled
|
- added warning if some features of a API are disabled
|
||||||
|
- added TextMetrics protection
|
||||||
|
|
||||||
fixes:
|
fixes:
|
||||||
-
|
-
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
<li><a href="dataUrlTest.php">Data-URL 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="audioTest.html">Audio Fingerprint test</a></li>
|
||||||
<li><a href="domRectTest.html">DOMRect Fingerprint test</a></li>
|
<li><a href="domRectTest.html">DOMRect Fingerprint test</a></li>
|
||||||
|
<li><a href="textMetricsTest.html">TextMetrics test</a></li>
|
||||||
<li><a href="detectionTest.html">Detection test</a></li>
|
<li><a href="detectionTest.html">Detection test</a></li>
|
||||||
<li><a href="performanceTest.html">Performance test</a></li>
|
<li><a href="performanceTest.html">Performance test</a></li>
|
||||||
<li><a href="webGL-Test.html">Support for webGL</a></li>
|
<li><a href="webGL-Test.html">Support for webGL</a></li>
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
const testAPI = function(){
|
const testAPI = function(){
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
const digest = crypto.subtle? crypto.subtle.digest.bind(crypto.subtle, "SHA-256"): function(buffer){
|
||||||
|
return new Uint32Array(buffer.buffer);
|
||||||
|
};
|
||||||
|
|
||||||
function bufferToString(hash){
|
function bufferToString(hash){
|
||||||
const chunks = [];
|
const chunks = [];
|
||||||
(new Uint32Array(hash)).forEach(function(num){
|
(new Uint32Array(hash)).forEach(function(num){
|
||||||
@ -18,7 +22,7 @@ const testAPI = function(){
|
|||||||
const buffer = ((typeof input) === "string")?
|
const buffer = ((typeof input) === "string")?
|
||||||
new TextEncoder("utf-8").encode(input):
|
new TextEncoder("utf-8").encode(input):
|
||||||
input;
|
input;
|
||||||
const hash = await crypto.subtle.digest("SHA-256", buffer);
|
const hash = await digest(buffer);
|
||||||
return bufferToString(hash);
|
return bufferToString(hash);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
40
test/textMetricsTest.html
Normal file
40
test/textMetricsTest.html
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<!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">
|
||||||
|
<style>
|
||||||
|
.hash {
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>TextMetrics test</h1>
|
||||||
|
<h2>Expected result</h2>
|
||||||
|
<ul>
|
||||||
|
<li>the hashes are different to the hashes when CanvasBlocker is disabled</li>
|
||||||
|
<li>the number of differences stays the same when CanvasBlocker is disabled</li>
|
||||||
|
<li>if "refresh" is clicked nothing must change</li>
|
||||||
|
<li>upon page reload the hashes change (depending on CanvasBlocker settings - e.g. not in the stealth preset)</li>
|
||||||
|
</ul>
|
||||||
|
<h2>Tests</h2>
|
||||||
|
<div id="tests">
|
||||||
|
<div class="test" id="measureText">
|
||||||
|
<h3 class="title">measureText</h3>
|
||||||
|
Hashes: <span class="hashes"><table>
|
||||||
|
<tr>
|
||||||
|
<td>all:</td>
|
||||||
|
<td class="hash all"></td>
|
||||||
|
</tr>
|
||||||
|
</table></span><br>
|
||||||
|
Number of differences: <span class="differences"></span><br>
|
||||||
|
<button class="refresh">refresh</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script src="testAPI.js"></script>
|
||||||
|
<script src="textMetricsTest.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
94
test/textMetricsTest.js
Normal file
94
test/textMetricsTest.js
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
/* globals testAPI */
|
||||||
|
(function(){
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const fonts = ["none", "sans-serif", "serif", "monospace", "cursive", "fantasy"];
|
||||||
|
const charCodePoints = [
|
||||||
|
0x20B9, 0x2581, 0x20BA, 0xA73D, 0xFFFD, 0x20B8, 0x05C6,
|
||||||
|
0x1E9E, 0x097F, 0xF003, 0x1CDA, 0x17DD, 0x23AE, 0x0D02, 0x0B82, 0x115A,
|
||||||
|
0x2425, 0x302E, 0xA830, 0x2B06, 0x21E4, 0x20BD, 0x2C7B, 0x20B0, 0xFBEE,
|
||||||
|
0xF810, 0xFFFF, 0x007F, 0x10A0, 0x1D790, 0x0700, 0x1950, 0x3095, 0x532D,
|
||||||
|
0x061C, 0x20E3, 0xFFF9, 0x0218, 0x058F, 0x08E4, 0x09B3, 0x1C50, 0x2619
|
||||||
|
];
|
||||||
|
const textMetricsProperties = [
|
||||||
|
"width",
|
||||||
|
"actualBoundingBoxAscent",
|
||||||
|
"actualBoundingBoxDescent",
|
||||||
|
"actualBoundingBoxLeft",
|
||||||
|
"actualBoundingBoxRight",
|
||||||
|
"alphabeticBaseline",
|
||||||
|
"emHeightAscent",
|
||||||
|
"emHeightDescent",
|
||||||
|
"fontBoundingBoxAscent",
|
||||||
|
"fontBoundingBoxDescent",
|
||||||
|
"hangingBaseline",
|
||||||
|
"ideographicBaseline",
|
||||||
|
].filter(function(property){
|
||||||
|
return TextMetrics.prototype.hasOwnProperty(property);
|
||||||
|
});
|
||||||
|
|
||||||
|
const hashTable = document.querySelector("#measureText .hashes table");
|
||||||
|
textMetricsProperties.forEach(function(property){
|
||||||
|
const row = document.createElement("tr");
|
||||||
|
hashTable.appendChild(row);
|
||||||
|
|
||||||
|
const name = document.createElement("td");
|
||||||
|
name.textContent = property + ": ";
|
||||||
|
row.appendChild(name);
|
||||||
|
|
||||||
|
const hash = document.createElement("td");
|
||||||
|
hash.className = "hash " + property;
|
||||||
|
row.appendChild(hash);
|
||||||
|
});
|
||||||
|
|
||||||
|
async function testMeasureText(){
|
||||||
|
const canvas = document.createElement("canvas");
|
||||||
|
const node = document.createElement("span");
|
||||||
|
document.body.appendChild(node);
|
||||||
|
const context = canvas.getContext("2d");
|
||||||
|
|
||||||
|
const data = new Float64Array(fonts.length * charCodePoints.length * textMetricsProperties.length);
|
||||||
|
let dataIndex = 0;
|
||||||
|
const propertyData = {};
|
||||||
|
textMetricsProperties.forEach(function(property){
|
||||||
|
propertyData[property] = new Float64Array(fonts.length * charCodePoints.length);
|
||||||
|
});
|
||||||
|
let propertyDataIndex = 0;
|
||||||
|
|
||||||
|
let differences = 0;
|
||||||
|
|
||||||
|
fonts.forEach(function(font){
|
||||||
|
context.font = node.style.font = "22000px " + font;
|
||||||
|
|
||||||
|
charCodePoints.forEach(function(charCodePoint){
|
||||||
|
const char = String.fromCodePoint(charCodePoint);
|
||||||
|
node.textContent = char;
|
||||||
|
|
||||||
|
const textMetric = context.measureText(char);
|
||||||
|
const domRect = node.getBoundingClientRect();
|
||||||
|
textMetricsProperties.forEach(function(property){
|
||||||
|
data[dataIndex] = textMetric[property];
|
||||||
|
propertyData[property][propertyDataIndex] = textMetric[property];
|
||||||
|
dataIndex += 1;
|
||||||
|
});
|
||||||
|
propertyDataIndex += 1;
|
||||||
|
if (textMetric.width !== domRect.width){
|
||||||
|
differences += 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
document.body.removeChild(node);
|
||||||
|
|
||||||
|
document.querySelector("#measureText .differences").textContent =
|
||||||
|
differences + " of " + fonts.length * charCodePoints.length;
|
||||||
|
textMetricsProperties.forEach(async function(property){
|
||||||
|
document.querySelector("#measureText .hash." + property).textContent =
|
||||||
|
await testAPI.hash(propertyData[property]);
|
||||||
|
});
|
||||||
|
document.querySelector("#measureText .hash.all").textContent = await testAPI.hash(data);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
testMeasureText();
|
||||||
|
document.querySelector("#measureText .refresh").addEventListener("click", testMeasureText);
|
||||||
|
}());
|
@ -129,6 +129,10 @@
|
|||||||
{
|
{
|
||||||
"version": "1.2Alpha20200224",
|
"version": "1.2Alpha20200224",
|
||||||
"update_link": "https://canvasblocker.kkapsner.de/versions/canvasblocker_beta-1.2Alpha20200224-an+fx.xpi"
|
"update_link": "https://canvasblocker.kkapsner.de/versions/canvasblocker_beta-1.2Alpha20200224-an+fx.xpi"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "1.2Alpha20200314",
|
||||||
|
"update_link": "https://canvasblocker.kkapsner.de/versions/canvasblocker_beta-1.2Alpha20200314-an+fx.xpi"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user