<!DOCTYPE HTML>
<html>
<head>
  <meta charset="utf-8">
  <title>Test for Bug 1382545</title>
<script>
  function waitForCondition(aCond, aCallback, aErrorMsg) {
    var tries = 0;
    var interval = setInterval(() => {
      if (tries >= 30) {
        opener.ok(false, aErrorMsg);
        moveOn();
        return;
      }
      var conditionPassed;
      try {
        conditionPassed = aCond();
      } catch (e) {
        opener.ok(false, `${e}\n${e.stack}`);
        conditionPassed = false;
      }
      if (conditionPassed) {
        moveOn();
      }
      tries++;
    }, 100);
    var moveOn = () => { clearInterval(interval); aCallback(); };
  }

  function runTest() {
    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.
      // 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;
        }
      }

      // We are temporarily disabling this extra debugging failure because we expect to return false in some instances
      // When we correct things we will re-enable it for debugging assistance
      // opener.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;
    };
    const testDiv = document.getElementById("testDiv");
    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, 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 true;
            return value;
          };

          opener.ok(maybeAcceptEverything(isRounded(animation.startTime)),
             "pref: " + opener.prefName + " - animation.startTime with precision " + expectedPrecision + " is not rounded: " + animation.startTime);
          opener.ok(maybeAcceptEverything(isRounded(animation.currentTime)),
             "pref: " + opener.prefName + " - animation.currentTime with precision " + expectedPrecision + " is not rounded: " + animation.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(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>
</head>
<body onload="runTest();">
<div id="testDiv">test</div>
</body>
</html>
