dom/animation/test/css-integration/test_element-get-animation-players.html
author Brian Birtles <birtles@gmail.com>
Thu, 02 Oct 2014 15:14:14 +0900
changeset 208352 35e518128ca302874769f17abcee7f754dae9d30
parent 208345 41476731392e5071854bd36eb52faa7a40e8fde7
child 211173 3e5016be92f475efa67a98e67c6e42ccc52bec97
permissions -rw-r--r--
Bug 1074054 part 2 - Make Element::GetAnimationPlayers return in-effect animations too; r=dbaron This patch makes Element::GetAnimationPlayers return not only current animations but also animations that have finished but are filling forwards. This brings the implementation into line with recent changes to the Web Animations spec and allows querying all the animations that are currently affecting an element or which are scheduled to do so in the future.

<!doctype html>
<meta charset=utf-8>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<div id="log"></div>
<style>
@keyframes anim1 {
  to { left: 100px }
}
@keyframes anim2 {
  to { top: 100px }
}
@keyframes multiPropAnim {
  to { background: green, opacity: 0.5, left: 100px, top: 100px }
}
@keyframes empty { }
</style>
<script>
'use strict';

function addDiv() {
  var div = document.createElement('div');
  document.body.appendChild(div);
  return div;
}

test(function() {
  var div = addDiv();
  assert_equals(div.getAnimationPlayers().length, 0,
    'getAnimationPlayers returns an empty sequence for an element'
    + ' with no animations');
  div.remove();
}, 'getAnimationPlayers for non-animated content');

async_test(function(t) {
  var div = addDiv();

  // Add an animation
  div.style.animation = 'anim1 100s';
  var players = div.getAnimationPlayers();
  assert_equals(players.length, 1,
    'getAnimationPlayers returns a player running CSS Animations');
  var startTime = players[0].startTime;
  assert_true(startTime > 0 && startTime <= document.timeline.currentTime,
    'CSS animation has a sensible start time');

  // Wait a moment then add a second animation.
  //
  // We wait for the next frame so that we can test that the start times of
  // the animations differ.
  window.requestAnimationFrame(t.step_func(function() {
    div.style.animation = 'anim1 100s, anim2 100s';
    players = div.getAnimationPlayers();
    assert_equals(players.length, 2,
      'getAnimationPlayers returns one player for each value of'
      + ' animation-name');
    assert_true(players[0].startTime < players[1].startTime,
      'Additional players for CSS animations start after the original'
      + ' animation and appear later in the list');
    div.remove();
    t.done();
  }));
}, 'getAnimationPlayers for CSS Animations');

test(function() {
  var div = addDiv();

  // Add an animation that targets multiple properties
  div.style.animation = 'multiPropAnim 100s';
  assert_equals(div.getAnimationPlayers().length, 1,
    'getAnimationPlayers returns only one player for a CSS Animation'
    + ' that targets multiple properties');
  div.remove();
}, 'getAnimationPlayers for multi-property animations');

async_test(function(t) {
  var div = addDiv();

  // Add a couple of transitions
  div.style.left = '0px';
  div.style.top = '0px';
  window.getComputedStyle(div).transitionProperty;

  div.style.transition = 'all 100s';
  div.style.left = '100px';
  div.style.top = '100px';

  var players = div.getAnimationPlayers();
  assert_equals(players.length, 2,
    'getAnimationPlayers() returns one player per transitioning property');
  var startTime = players[0].startTime;
  assert_true(startTime > 0 && startTime <= document.timeline.currentTime,
    'CSS transitions have sensible start times');
  assert_equals(players[0].startTime, players[1].startTime,
    'CSS transitions started together have the same start time');

  // Wait a moment then add a third transition
  window.requestAnimationFrame(t.step_func(function() {
    div.style.backgroundColor = 'green';
    players = div.getAnimationPlayers();
    assert_equals(players.length, 3,
      'getAnimationPlayers returns players for all running CSS Transitions');
    assert_true(players[1].startTime < players[2].startTime,
      'Player for additional CSS transition starts after the original'
      + ' transitions and appears later in the list');
    div.remove();
    t.done();
  }));
}, 'getAnimationPlayers for CSS Transitions');

async_test(function(t) {
  var div = addDiv();

  // Add an animation
  div.style.backgroundColor = 'red';
  div.style.animation = 'anim1 100s';
  window.getComputedStyle(div).backgroundColor;

  // Wait a moment then add a transition
  window.requestAnimationFrame(t.step_func(function() {
    div.style.transition = 'all 100s';
    div.style.backgroundColor = 'green';

    var players = div.getAnimationPlayers();
    assert_equals(players.length, 2,
      'getAnimationPlayers returns players for both animations and '
      + ' transitions that run simultaneously');
    assert_true(players[0].startTime > players[1].startTime,
      'players for transitions appear before animations even if they '
      + ' start later');
    div.remove();
    t.done();
  }));
}, 'getAnimationPlayers for both CSS Animations and Transitions at once');

async_test(function(t) {
  var div = addDiv();

  // Set up event listener
  div.addEventListener('animationend', t.step_func(function() {
    assert_equals(div.getAnimationPlayers().length, 0,
      'getAnimationPlayers does not return players for finished '
      + ' (and non-forwards-filling) CSS Animations');
    div.remove();
    t.done();
  }));

  // Add a very short animation
  div.style.animation = 'anim1 0.01s';
}, 'getAnimationPlayers for CSS Animations that have finished');

async_test(function(t) {
  var div = addDiv();

  // Set up event listener
  div.addEventListener('animationend', t.step_func(function() {
    assert_equals(div.getAnimationPlayers().length, 1,
      'getAnimationPlayers returns players for CSS Animations that have'
      + ' finished but are filling forwards');
    div.remove();
    t.done();
  }));

  // Add a very short animation
  div.style.animation = 'anim1 0.01s forwards';
}, 'getAnimationPlayers for CSS Animations that have finished but are'
   + ' forwards filling');

async_test(function(t) {
  var div = addDiv();

  // Set up event listener
  div.addEventListener('transitionend', t.step_func(function() {
    assert_equals(div.getAnimationPlayers().length, 0,
      'getAnimationPlayers does not return finished CSS Transitions');
    div.remove();
    t.done();
  }));

  // Add a very short transition
  div.style.left = '0px';
  window.getComputedStyle(div).left;

  div.style.transition = 'all 0.01s';
  div.style.left = '100px';
  window.getComputedStyle(div).left;
}, 'getAnimationPlayers for CSS Transitions that have finished');

test(function() {
  var div = addDiv();
  div.style.animation = 'none 100s';

  var players = div.getAnimationPlayers();
  assert_equals(players.length, 0,
    'getAnimationPlayers returns an empty sequence for an element'
    + ' with animation-name: none');

  div.style.animation = 'none 100s, anim1 100s';
  players = div.getAnimationPlayers();
  assert_equals(players.length, 1,
    'getAnimationPlayers returns players only for those CSS Animations whose'
    + ' animation-name is not none');

  div.remove();
}, 'getAnimationPlayers for CSS Animations with animation-name: none');

test(function() {
  var div = addDiv();
  div.style.animation = 'missing 100s';
  var players = div.getAnimationPlayers();
  assert_equals(players.length, 0,
    'getAnimationPlayers returns an empty sequence for an element'
    + ' with animation-name: missing');

  div.style.animation = 'anim1 100s, missing 100s';
  players = div.getAnimationPlayers();
  assert_equals(players.length, 1,
    'getAnimationPlayers returns players only for those CSS Animations whose'
    + ' animation-name is found');

  div.remove();
}, 'getAnimationPlayers for CSS Animations with animation-name: missing');

async_test(function(t) {
  var div = addDiv();
  div.style.animation = 'anim1 100s, notyet 100s';
  var players = div.getAnimationPlayers();
  assert_equals(players.length, 1,
    'getAnimationPlayers initally only returns players for CSS Animations whose'
    + ' animation-name is found');

  window.requestAnimationFrame(t.step_func(function() {
    var keyframes = '@keyframes notyet { to { left: 100px; } }';
    document.styleSheets[0].insertRule(keyframes, 0);
    players = div.getAnimationPlayers();
    assert_equals(players.length, 2,
      'getAnimationPlayers includes player when @keyframes rule is added'
      + ' later');
    assert_true(players[0].startTime < players[1].startTime,
      'Newly added player has a later start time');
    document.styleSheets[0].deleteRule(0);
    div.remove();
    t.done();
  }));
}, 'getAnimationPlayers for CSS Animations where the @keyframes rule is added'
   + ' later');

test(function() {
  var div = addDiv();
  div.style.animation = 'anim1 100s, anim1 100s';
  assert_equals(div.getAnimationPlayers().length, 2,
    'getAnimationPlayers returns one player for each CSS animation-name'
    + ' even if the names are duplicated');
  div.remove();
}, 'getAnimationPlayers for CSS Animations with duplicated animation-name');

test(function() {
  var div = addDiv();
  div.style.animation = 'empty 100s';
  assert_equals(div.getAnimationPlayers().length, 1,
    'getAnimationPlayers returns players for CSS animations with an'
    + ' empty keyframes rule');
  div.remove();
}, 'getAnimationPlayers for CSS Animations with empty keyframes rule');

test(function() {
  var div = addDiv();
  div.style.animation = 'anim1 100s 100s';
  var players = div.getAnimationPlayers();
  assert_equals(players.length, 1,
    'getAnimationPlayers returns animations for CSS animations whose'
    + ' delay makes them start later');
  assert_true(players[0].startTime <= document.timeline.currentTime,
    'For CSS Animations in delay phase, the start time of the player is'
    + ' not in the future');
  div.remove();
}, 'getAnimationPlayers for CSS animations in delay phase');

test(function() {
  var div = addDiv();
  div.style.animation = 'anim1 0s 100s';
  assert_equals(div.getAnimationPlayers().length, 1,
    'getAnimationPlayers returns animations for CSS animations whose'
    + ' duration is zero');
  div.remove();
}, 'getAnimationPlayers for zero-duration CSS Animations');

test(function() {
  var div = addDiv();

  // Try to transition non-animatable property animation-duration
  div.style.animationDuration = '10s';
  window.getComputedStyle(div).animationDuration;

  div.style.transition = 'all 100s';
  div.style.animationDuration = '100s';

  assert_equals(div.getAnimationPlayers().length, 0,
    'getAnimationPlayers returns an empty sequence for a transition'
    + ' of a non-animatable property');
  div.remove();
}, 'getAnimationPlayers for transition on non-animatable property');

test(function() {
  var div = addDiv();

  div.style.setProperty('-vendor-unsupported', '0px', '');
  window.getComputedStyle(div).transitionProperty;
  div.style.transition = 'all 100s';
  div.style.setProperty('-vendor-unsupported', '100px', '');

  assert_equals(div.getAnimationPlayers().length, 0,
    'getAnimationPlayers returns an empty sequence for a transition'
    + ' of an unsupported property');
  div.remove();
}, 'getAnimationPlayers for transition on unsupported property');

test(function() {
  var div = addDiv();
  div.style.animation = 'anim1 100s';
  var originalPlayer = div.getAnimationPlayers()[0];

  // Update pause state (an AnimationPlayer change)
  div.style.animationPlayState = 'paused';
  var pausedPlayer = div.getAnimationPlayers()[0];
  // FIXME: Check pausedPlayer.playState has changed once the API is available
  // (bug 1037321)
  assert_equals(originalPlayer, pausedPlayer,
                'getAnimationPlayers returns the same objects even when their'
                + ' play state changes');

  // Update duration (an Animation change)
  div.style.animationDuration = '200s';
  var extendedPlayer = div.getAnimationPlayers()[0];
  // FIXME: Check extendedPlayer.source.timing.duration has changed once the
  // API is available
  assert_equals(originalPlayer, extendedPlayer,
                'getAnimationPlayers returns the same objects even when their'
                + ' duration changes');

  div.remove();
}, 'getAnimationPlayers returns objects with the same identity');

</script>