144 lines
5.0 KiB
HTML
144 lines
5.0 KiB
HTML
<!DOCTYPE HTML>
|
|
<html>
|
|
<!--
|
|
Tor bug
|
|
https://trac.torproject.org/projects/tor/ticket/1517
|
|
-->
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>Test for Tor Bug 1517 and Mozilla Bug 1424341</title>
|
|
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
|
<script type="application/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
|
|
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
|
</head>
|
|
<body>
|
|
<a target="_blank" href="https://trac.torproject.org/projects/tor/ticket/1517">Tor Bug 1517</a>
|
|
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1424341">Mozilla Bug 1424341</a>
|
|
|
|
<!-- Canvas for testing 'currentTime' -->
|
|
<canvas id="test-canvas" width="100" height="100"></canvas>
|
|
|
|
<!-- The main testing script -->
|
|
<script type="application/javascript">
|
|
SimpleTest.requestFlakyTimeout("testing JS time-based fingerprinting");
|
|
|
|
// Prepare for test of AudioContext.currentTime
|
|
let audioContext = new AudioContext();
|
|
// Prepare for test of CanvasStream.currentTime
|
|
let canvas = document.getElementById("test-canvas");
|
|
let context = canvas.getContext("2d");
|
|
context.fillText("test", 20, 20);
|
|
let canvasStream = canvas.captureStream(25);
|
|
|
|
// Known ways to generate time stamps, in milliseconds
|
|
const timeStampCodes = [
|
|
"performance.now()",
|
|
"new Date().getTime()",
|
|
"new Event(\"\").timeStamp",
|
|
"new File([], \"\").lastModified",
|
|
"new File([], \"\").lastModifiedDate.getTime()",
|
|
];
|
|
// These are measured in seconds, so we need to scale them up
|
|
var timeStampCodesDOM = timeStampCodes.concat([
|
|
"audioContext.currentTime * 1000",
|
|
"canvasStream.currentTime * 1000",
|
|
]);
|
|
|
|
let isRounded = (x) => {
|
|
expectedPrecision = 2;
|
|
let rounded = (Math.floor(x / expectedPrecision) * expectedPrecision);
|
|
// First we do the perfectly normal check that should work just fine
|
|
if (rounded === x || x === 0)
|
|
return true;
|
|
|
|
// When we're diving by non-whole numbers, we may not get perfect
|
|
// multiplication/division because of floating points.
|
|
// When dealing with ms since epoch, a double's precision is on the order
|
|
// of 1/5 of a microsecond, so we use a value a little higher than that as
|
|
// our epsilon.
|
|
// To be clear, this error is introduced in our re-calculation of 'rounded'
|
|
// above in JavaScript.
|
|
if (Math.abs(rounded - x + expectedPrecision) < .0005) {
|
|
return true;
|
|
} else if (Math.abs(rounded - x) < .0005) {
|
|
return true;
|
|
}
|
|
|
|
// Then we handle the case where you're sub-millisecond and the timer is not
|
|
// We check that the timer is not sub-millisecond by assuming it is not if it
|
|
// returns an even number of milliseconds
|
|
if (expectedPrecision < 1 && Math.round(x) == x) {
|
|
if (Math.round(rounded) == x) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
ok(false, "Looming Test Failure, Additional Debugging Info: Expected Precision: " + expectedPrecision + " Measured Value: " + x +
|
|
" Rounded Vaue: " + rounded + " Fuzzy1: " + Math.abs(rounded - x + expectedPrecision) +
|
|
" Fuzzy 2: " + Math.abs(rounded - x));
|
|
|
|
return false;
|
|
};
|
|
|
|
// ================================================================================================
|
|
// ================================================================================================
|
|
|
|
function* checkWorker(worker) {
|
|
// The child worker will send the results back.
|
|
let checkWorkerTimeStamps = () => new Promise(function(resolve) {
|
|
let onMessage = function(event) {
|
|
worker.removeEventListener("message", onMessage);
|
|
|
|
let timeStamps = event.data;
|
|
for (let i = 0; i < timeStampCodes.length; i++) {
|
|
let timeStamp = timeStamps[i];
|
|
ok(isRounded(timeStamp),
|
|
"'" + timeStampCodes[i] +
|
|
"' should be rounded to nearest 2 ms in workers; saw " +
|
|
timeStamp);
|
|
}
|
|
resolve();
|
|
};
|
|
worker.addEventListener("message", onMessage);
|
|
});
|
|
|
|
// Send the codes to its child worker.
|
|
worker.postMessage(timeStampCodes);
|
|
|
|
// First, check the child's results.
|
|
yield checkWorkerTimeStamps();
|
|
// Then, check the grandchild's results.
|
|
yield checkWorkerTimeStamps();
|
|
|
|
worker.terminate();
|
|
}
|
|
|
|
add_task(function*() {
|
|
let worker = new Worker("worker_child.js");
|
|
// Allow ~550 ms to elapse, so we can get non-zero
|
|
// time values for all elements.
|
|
yield new Promise(resolve => window.setTimeout(resolve, 550));
|
|
yield checkWorker(worker);
|
|
});
|
|
|
|
// ================================================================================================
|
|
// ================================================================================================
|
|
|
|
|
|
add_task(function*() {
|
|
// Loop through each timeStampCode, evaluate it,
|
|
// and check if it is rounded
|
|
for (let timeStampCode of timeStampCodesDOM) {
|
|
let timeStamp = eval(timeStampCode);
|
|
ok(isRounded(timeStamp),
|
|
"'" + timeStampCode + "' should be rounded to nearest 2ms" +
|
|
" saw " + timeStamp);
|
|
}
|
|
});
|
|
|
|
</script>
|
|
|
|
|
|
</body>
|
|
</html>
|