/**
 * Bug 1621677 - A test for making sure getting the correct (higher) precision
 *   when it's cross-origin-isolated on animation APIs.
 */

// ================================================================================================
// ================================================================================================
// This test case is mostly copy-and-paste from the test case for window in
// test_animation_api.html. The main difference is this test case
// verifies animation has more precsion when it's in cross-origin-isolated and
// cross-origin-isolated doesn't affect RFP.
add_task(async function runRTPTestAnimation() {
  await SpecialPowers.pushPrefEnv({
    set: [["security.allow_eval_with_system_principal", true]],
  });

  let runTests = async function (data) {
    function waitForCondition(aCond, aCallback, aErrorMsg) {
      var tries = 0;
      var interval = content.setInterval(() => {
        if (tries >= 30) {
          ok(false, aErrorMsg);
          moveOn();
          return;
        }
        var conditionPassed;
        try {
          conditionPassed = aCond();
        } catch (e) {
          ok(false, `${e}\n${e.stack}`);
          conditionPassed = false;
        }
        if (conditionPassed) {
          moveOn();
        }
        tries++;
      }, 100);
      var moveOn = () => {
        content.clearInterval(interval);
        aCallback();
      };
    }

    let expectedPrecision = data.precision;
    // eslint beleives that isrounded is available in this scope, but if you
    // remove the assignment, you will see it is not
    // eslint-disable-next-line
    let isRounded = eval(data.isRoundedFunc);

    const testDiv = content.document.getElementById("testDiv");
    const animation = testDiv.animate({ opacity: [0, 1] }, 100000);
    animation.play();

    let done;
    let promise = new Promise(resolve => {
      done = resolve;
    });

    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 (
            data.options.reduceTimerPrecision &&
            !data.options.resistFingerprinting
          ) {
            return true;
          }

          return value;
        };

        ok(
          maybeAcceptEverything(
            isRounded(animation.startTime, expectedPrecision)
          ),
          `Animation.startTime with precision ${expectedPrecision} is not ` +
            `rounded: ${animation.startTime}`
        );
        ok(
          maybeAcceptEverything(
            isRounded(animation.currentTime, expectedPrecision)
          ),
          `Animation.currentTime with precision ${expectedPrecision} is ` +
            `not rounded: ${animation.currentTime}`
        );
        ok(
          maybeAcceptEverything(
            isRounded(animation.timeline.currentTime, expectedPrecision)
          ),
          `Animation.timeline.currentTime with precision ` +
            `${expectedPrecision} is not rounded: ` +
            `${animation.timeline.currentTime}`
        );
        if (content.document.timeline) {
          ok(
            maybeAcceptEverything(
              isRounded(
                content.document.timeline.currentTime,
                expectedPrecision
              )
            ),
            `Document.timeline.currentTime with precision ` +
              `${expectedPrecision} is not rounded: ` +
              `${content.document.timeline.currentTime}`
          );
        }
        done();
      },
      "animation failed to start"
    );

    await promise;
  };

  await setupAndRunCrossOriginIsolatedTest(
    {
      resistFingerprinting: true,
      reduceTimerPrecision: true,
      crossOriginIsolated: true,
    },
    100,
    runTests
  );
  await setupAndRunCrossOriginIsolatedTest(
    {
      resistFingerprinting: true,
      crossOriginIsolated: true,
    },
    50,
    runTests
  );
  await setupAndRunCrossOriginIsolatedTest(
    {
      resistFingerprinting: true,
      crossOriginIsolated: true,
    },
    0.1,
    runTests
  );
  await setupAndRunCrossOriginIsolatedTest(
    {
      resistFingerprinting: true,
      reduceTimerPrecision: true,
      crossOriginIsolated: true,
    },
    0.013,
    runTests
  );

  await setupAndRunCrossOriginIsolatedTest(
    {
      reduceTimerPrecision: true,
      crossOriginIsolated: true,
    },
    0.005,
    runTests
  );
});
