Bug 1438953 Increase the epsilon on timer reduction tests r?froydnj
In Javascript, we re-clamp timers to ensure they stay the same. Because of double imprecision
sometimes they don't stay the same, and are clamped downwards. If that happens we detect it,
and if we were originally off by an epsilon from a clamped value, we accept the value in the
name of double imprecision.
However, the epsilons were originally chosen somewhat arbitrarily. They worked for small
numbers, where imprecision from doubles were very small. But large doubles have much less
precise fractional parts. So the epsilons were too small for large numbers where the
imprecision was larger.
In
Bug 1435296 we stopped reducing the precision of CSS Animations by default, and changed
the test to throw an error if they WERE rounded. After we increased the epsilon in this
patch, we began to see false positives - we were getting values that were not rounded, but
happened to be within the epsilon window and thus appeared rounded. We change the check so
instead of seeing if an Animation is not rounded, and erroring if it is, to accepting any
value.
MozReview-Commit-ID: HnYYo4cuv96
--- a/browser/components/resistfingerprinting/test/browser/browser_performanceAPI.js
+++ b/browser/components/resistfingerprinting/test/browser/browser_performanceAPI.js
@@ -32,20 +32,25 @@ const PERFORMANCE_TIMINGS = [
let isRounded = (x, expectedPrecision) => {
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
- if (Math.abs(rounded - x + expectedPrecision) < .0000001) {
+ // 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) < .0000001) {
+ } 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) {
--- a/browser/components/resistfingerprinting/test/browser/file_workerPerformance.js
+++ b/browser/components/resistfingerprinting/test/browser/file_workerPerformance.js
@@ -12,20 +12,25 @@ function finish() {
let isRounded = (x, expectedPrecision) => {
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
- if (Math.abs(rounded - x + expectedPrecision) < .0000001) {
+ // 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) < .0000001) {
+ } 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) {
--- a/browser/components/resistfingerprinting/test/mochitest/file_animation_api.html
+++ b/browser/components/resistfingerprinting/test/mochitest/file_animation_api.html
@@ -31,20 +31,25 @@
let expectedPrecision = opener.expectedPrecision / 1000;
let isRounded = (x) => {
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
- if (Math.abs(rounded - x + expectedPrecision) < .0000001) {
+ // 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) < .0000001) {
+ } 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) {
@@ -64,32 +69,32 @@
const animation = testDiv.animate({ opacity: [0, 1] }, 100000);
animation.play();
waitForCondition(
() => animation.currentTime > 100,
() => {
// We have disabled Time Precision Reduction for CSS Animations, so we expect those tests to fail.
- // If we are testing that preference, turn failures into successes and successes into failures
- var maybeInvert = function(value) {
+ // If we are testing that preference, we accept either rounded or not rounded values as A-OK.
+ var maybeAcceptEverything = function(value) {
if (opener.prefName.includes("privacy.reduceTimerPrecision") &&
!opener.prefName.includes("privacy.resistFingerprinting"))
- return !value;
+ return true;
return value;
};
- opener.ok(maybeInvert(isRounded(animation.startTime)),
+ opener.ok(maybeAcceptEverything(isRounded(animation.startTime)),
"pref: " + opener.prefName + " - animation.startTime with precision " + expectedPrecision + " is not rounded: " + animation.startTime);
- opener.ok(maybeInvert(isRounded(animation.currentTime)),
+ opener.ok(maybeAcceptEverything(isRounded(animation.currentTime)),
"pref: " + opener.prefName + " - animation.currentTime with precision " + expectedPrecision + " is not rounded: " + animation.currentTime);
- opener.ok(maybeInvert(isRounded(animation.timeline.currentTime)),
+ opener.ok(maybeAcceptEverything(isRounded(animation.timeline.currentTime)),
"pref: " + opener.prefName + " - animation.timeline.currentTime with precision " + expectedPrecision + " is not rounded: " + animation.timeline.currentTime);
if (document.timeline) {
- opener.ok(maybeInvert(isRounded(document.timeline.currentTime)),
+ opener.ok(maybeAcceptEverything(isRounded(document.timeline.currentTime)),
"pref: " + opener.prefName + " - document.timeline.currentTime with precision " + expectedPrecision + " is not rounded: " + document.timeline.currentTime);
}
opener.done();
window.close();
},
"animation failed to start");
}
</script>
--- a/browser/components/resistfingerprinting/test/mochitest/test_reduce_time_precision.html
+++ b/browser/components/resistfingerprinting/test/mochitest/test_reduce_time_precision.html
@@ -45,23 +45,28 @@ https://trac.torproject.org/projects/tor
]);
let isRounded = (x, expectedPrecision) => {
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
- if (Math.abs(rounded - x + expectedPrecision) < .0000001) {
- return true;
- } else if (Math.abs(rounded - x) < .0000001) {
- 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;
}