<!doctype html>
<head>
<meta charset=utf-8>
<title>Test animations in PendingAnimationTracker</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
"use strict";

promise_test(function waitForLoad() {
  return new Promise(resolve => {
    window.addEventListener("load", resolve, { once: true });
  });
});

promise_test(async t => {
  // See below, but we should ensure we are in a rAF callback before proceeding
  // or else we will get inconsistent results.
  await waitForNextFrame();

  const target = addDiv(t);
  const anim = target.animate(null, 100 * MS_PER_SEC);
  assert_true(SpecialPowers.DOMWindowUtils.isAnimationInPendingTracker(anim),
              'The animation should be tracked by tracker');

  anim.effect = null;
  await waitForNextFrame();

  assert_false(SpecialPowers.DOMWindowUtils.isAnimationInPendingTracker(anim),
               'The animation should NOT be tracked by the tracker');
}, 'An animation whose effect is made null while pending is subsequently'
   + ' removed from the tracker');

test(t => {
  const target = addDiv(t);
  const anim = target.animate(null, 100 * MS_PER_SEC);
  assert_true(SpecialPowers.DOMWindowUtils.isAnimationInPendingTracker(anim),
              'The animation should be tracked by tracker');

  const newEffect = new KeyframeEffect(target, null);
  anim.effect = newEffect;

  assert_true(SpecialPowers.DOMWindowUtils.isAnimationInPendingTracker(anim),
              'The animation should be still tracked by tracker');
}, 'Setting another effect keeps the pending animation in the tracker');

test(t => {
  const effect = new KeyframeEffect(null, null);
  const anim = new Animation(effect);
  anim.play();
  assert_false(SpecialPowers.DOMWindowUtils.isAnimationInPendingTracker(anim),
               'The orphaned animation should NOT be tracked by tracker');

  const target = addDiv(t);
  const newEffect = new KeyframeEffect(target, null);
  anim.effect = newEffect;

  assert_true(SpecialPowers.DOMWindowUtils.isAnimationInPendingTracker(anim),
              'The animation should be now tracked by tracker');
}, 'Setting effect having target element starts being tracked by the ' +
   'tracker');

test(t => {
  const target = addDiv(t);
  const anim = target.animate(null, 100 * MS_PER_SEC);
  assert_true(SpecialPowers.DOMWindowUtils.isAnimationInPendingTracker(anim),
              'The animation should be tracked by tracker');

  anim.cancel();

  assert_false(SpecialPowers.DOMWindowUtils.isAnimationInPendingTracker(anim),
               'The animation should NOT be tracked by the tracker');
}, 'Calling cancel() removes the animation from the tracker');

promise_test(async t => {
  // Before proceeding this test, make sure following code is _NOT_ processed
  // between paint and refresh driver's tick.  Otherwise, waitForNextFrame below
  // doesn't ensure that a paint process happens which means that there is
  // no chance to call TriggerPendingAnimationsOnNextTick to discard the
  // animation from the pending animation tracker.
  await waitForNextFrame();

  const target = addDiv(t);
  const anim = target.animate(null, 100 * MS_PER_SEC);

  assert_true(SpecialPowers.DOMWindowUtils.isAnimationInPendingTracker(anim),
              'The animation should be tracked by tracker');

  target.remove();

  assert_true(SpecialPowers.DOMWindowUtils.isAnimationInPendingTracker(anim),
              'The animation is still being tracked by the tracker');

  await waitForNextFrame();
  assert_false(SpecialPowers.DOMWindowUtils.isAnimationInPendingTracker(anim),
               'The animation should NOT be tracked by the tracker in the ' +
               'next frame');
}, 'Removing target element from the document removes the animation from ' +
   'the tracker in the next tick');

test(t => {
  const target = addDiv(t);
  const anotherTarget = addDiv(t);
  const anim = target.animate(null, 100 * MS_PER_SEC);
  assert_true(SpecialPowers.DOMWindowUtils.isAnimationInPendingTracker(anim),
              'The animation should be tracked by tracker');

  anim.effect.target = anotherTarget;

  assert_true(SpecialPowers.DOMWindowUtils.isAnimationInPendingTracker(anim),
              'The animation should be still tracked by tracker');
}, 'Setting another target keeps the pending animation in the tracker');

test(t => {
  const effect = new KeyframeEffect(null, null);
  const anim = new Animation(effect);
  anim.play();
  assert_false(SpecialPowers.DOMWindowUtils.isAnimationInPendingTracker(anim),
               'The orphaned animation should NOT be tracked by tracker');

  const target = addDiv(t);
  anim.effect.target = target;

  assert_true(SpecialPowers.DOMWindowUtils.isAnimationInPendingTracker(anim),
              'The animation should be now tracked by tracker');
}, 'Setting target element to the orphaned animation starts being tracked ' +
   'by the tracker');

</script>
</body>
