Bug 1073396 - Make Element.getAnimationPlayers flush style; r=dbaron
authorBrian Birtles <birtles@gmail.com>
Thu, 02 Oct 2014 15:14:13 +0900
changeset 208345 41476731392e5071854bd36eb52faa7a40e8fde7
parent 208344 79ea9a38b446e5e46b66fec7365d2ed5e50891a8
child 208346 723e749323c64d9aa7ee4ffeb51314da2b7e8d45
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersdbaron
bugs1073396
milestone35.0a1
Bug 1073396 - Make Element.getAnimationPlayers flush style; r=dbaron
content/base/src/Element.cpp
dom/animation/test/chrome/test_running_on_compositor.html
dom/animation/test/css-integration/test_animation-effect-name.html
dom/animation/test/css-integration/test_animations-dynamic-changes.html
dom/animation/test/css-integration/test_element-get-animation-players.html
--- a/content/base/src/Element.cpp
+++ b/content/base/src/Element.cpp
@@ -2934,16 +2934,21 @@ void
 Element::MozRequestPointerLock()
 {
   OwnerDoc()->RequestPointerLock(this);
 }
 
 void
 Element::GetAnimationPlayers(nsTArray<nsRefPtr<AnimationPlayer> >& aPlayers)
 {
+  nsIDocument* doc = GetComposedDoc();
+  if (doc) {
+    doc->FlushPendingNotifications(Flush_Style);
+  }
+
   nsIAtom* properties[] = { nsGkAtoms::transitionsProperty,
                             nsGkAtoms::animationsProperty };
   for (size_t propIdx = 0; propIdx < MOZ_ARRAY_LENGTH(properties);
        propIdx++) {
     AnimationPlayerCollection* collection =
       static_cast<AnimationPlayerCollection*>(
         GetProperty(properties[propIdx]));
     if (!collection) {
--- a/dom/animation/test/chrome/test_running_on_compositor.html
+++ b/dom/animation/test/chrome/test_running_on_compositor.html
@@ -37,17 +37,16 @@ var div = document.querySelector('div.ta
 
 const OMTAPrefKey = 'layers.offmainthreadcomposition.async-animations';
 var omtaEnabled = SpecialPowers.DOMWindowUtils.layerManagerRemote &&
                   SpecialPowers.getBoolPref(OMTAPrefKey);
 
 // FIXME: When we implement Element.animate, use that here instead of CSS
 // so that we remove any dependency on the CSS mapping.
 div.style.animation = 'anim 100s';
-window.getComputedStyle(div).animationName;
 var player = div.getAnimationPlayers()[0];
 
 // Wait so that animation can be set up.
 // FIXME: When we implement the AnimationPlayer.ready promise we should wait
 // on that here.
 window.requestAnimationFrame(function() {
   is(player.isRunningOnCompositor, omtaEnabled,
      'AnimationPlayer reports that it is running on the compositor'
--- a/dom/animation/test/css-integration/test_animation-effect-name.html
+++ b/dom/animation/test/css-integration/test_animation-effect-name.html
@@ -15,51 +15,44 @@ function addDiv() {
   var div = document.createElement('div');
   document.body.appendChild(div);
   return div;
 }
 
 test(function() {
   var div = addDiv();
   div.style.animation = 'xyz 100s';
-  window.getComputedStyle(div).animationName;
-
   assert_equals(div.getAnimationPlayers()[0].source.effect.name, 'xyz',
                 'Animation effect name matches keyframes rule name');
   div.remove();
 }, 'Effect name makes keyframe rule');
 
 test(function() {
   var div = addDiv();
   div.style.animation = 'x\\yz 100s';
-  dump(window.getComputedStyle(div).animationName + "\n");
-
   assert_equals(div.getAnimationPlayers()[0].source.effect.name, 'xyz',
                 'Escaped animation effect name matches keyframes rule name');
   div.remove();
 }, 'Escaped animation name');
 
 test(function() {
   var div = addDiv();
   div.style.animation = 'x\\79 z 100s';
-  window.getComputedStyle(div).animationName;
-
   assert_equals(div.getAnimationPlayers()[0].source.effect.name, 'xyz',
                 'Hex-escaped animation effect name matches keyframes rule'
                 + ' name');
   div.remove();
 }, 'Animation name with hex-escape');
 
 test(function() {
   var div = addDiv();
 
   // Add a transition
   div.style.left = '0px';
   window.getComputedStyle(div).transitionProperty;
   div.style.transition = 'all 100s';
   div.style.left = '100px';
-  window.getComputedStyle(div).left;
 
   assert_equals(div.getAnimationPlayers()[0].source.effect.name, '',
                 'Animation effects for transitions have an empty name');
   div.remove();
 }, 'Effect name for transitions');
 </script>
--- a/dom/animation/test/css-integration/test_animations-dynamic-changes.html
+++ b/dom/animation/test/css-integration/test_animations-dynamic-changes.html
@@ -16,27 +16,25 @@ function addDiv() {
   var div = document.createElement('div');
   document.body.appendChild(div);
   return div;
 }
 
 async_test(function(t) {
   var div = addDiv();
   div.style.animation = 'anim1 100s';
-  window.getComputedStyle(div).animationName;
 
   var originalPlayer = div.getAnimationPlayers()[0];
   var originalStartTime = originalPlayer.startTime;
   var originalCurrentTime = originalPlayer.currentTime;
 
   // Wait a moment so we can confirm the startTime doesn't change (and doesn't
   // simply reflect the current time).
   window.requestAnimationFrame(t.step_func(function() {
     div.style.animationDuration = '200s';
-    window.getComputedStyle(div).animationDuration;
     var player = div.getAnimationPlayers()[0];
     assert_equals(player, originalPlayer,
                   'The same AnimationPlayer is returned after updating'
                   + ' animation duration');
     assert_equals(player.startTime, originalStartTime,
                   'AnimationPlayers returned by getAnimationPlayers preserve'
                   + ' their startTime even when they are updated');
     // Sanity check
@@ -46,49 +44,45 @@ async_test(function(t) {
     div.remove();
     t.done();
   }));
 }, 'AnimationPlayers preserve their startTime when changed');
 
 test(function() {
   var div = addDiv();
   div.style.animation = 'anim1 100s, anim1 100s';
-  window.getComputedStyle(div).animationName;
 
   // Store original state
   var players = div.getAnimationPlayers();
   var player1 = players[0];
   var player2 = players[1];
 
   // Update first in list
   div.style.animationDuration = '200s, 100s';
-  window.getComputedStyle(div).animationDuration;
   players = div.getAnimationPlayers();
   assert_equals(players[0], player1,
                 'First player is in same position after update');
   assert_equals(players[1], player2,
                 'Second player is in same position after update');
 }, 'Updated AnimationPlayers maintain their order in the list');
 
 async_test(function(t) {
   var div = addDiv();
   div.style.animation = 'anim1 200s, anim1 100s';
-  window.getComputedStyle(div).animationName;
 
   // Store original state
   var players = div.getAnimationPlayers();
   var player1 = players[0];
   var player2 = players[1];
 
   // Wait before continuing so we can compare start times
   window.requestAnimationFrame(t.step_func(function() {
     // Swap duration of first and second in list and prepend animation at the
     // same time
     div.style.animation = 'anim1 100s, anim1 100s, anim1 200s';
-    window.getComputedStyle(div).animationName;
     players = div.getAnimationPlayers();
     assert_true(players[0] !== player1 && players[0] !== player2,
                 'New player is prepended to start of list');
     assert_equals(players[1], player1,
                   'First player is in second position after update');
     assert_equals(players[2], player2,
                   'Second player is in third position after update');
     assert_equals(players[1].startTime, players[2].startTime,
@@ -98,53 +92,47 @@ async_test(function(t) {
     div.remove();
     t.done();
   }));
 }, 'Only the startTimes of existing animations are preserved');
 
 async_test(function(t) {
   var div = addDiv();
   div.style.animation = 'anim1 100s, anim1 100s';
-  window.getComputedStyle(div).animationName;
-
   var secondPlayer = div.getAnimationPlayers()[1];
 
   // Wait before continuing so we can compare start times
   window.requestAnimationFrame(t.step_func(function() {
     // Trim list of animations
     div.style.animationName = 'anim1';
-    window.getComputedStyle(div).animationName;
     var players = div.getAnimationPlayers();
     assert_equals(players.length, 1, 'List of players was trimmed');
     assert_equals(players[0], secondPlayer,
                   'Remaining player is the second one in the list');
     assert_true(players[0].startTime < players[0].timeline.currentTime,
                 'Remaining player preserves startTime');
     div.remove();
     t.done();
   }));
 }, 'Animations are removed from the start of the list while preserving'
    + ' the state of existing players');
 
 async_test(function(t) {
   var div = addDiv();
   div.style.animation = 'anim1 100s';
-  window.getComputedStyle(div).animationName;
   var firstAddedPlayer = div.getAnimationPlayers()[0];
 
   // Wait and add second player
   window.requestAnimationFrame(t.step_func(function() {
     div.style.animation = 'anim1 100s, anim1 100s';
-    window.getComputedStyle(div).animationName;
     var secondAddedPlayer = div.getAnimationPlayers()[0];
 
     // Wait again and add another player
     window.requestAnimationFrame(t.step_func(function() {
       div.style.animation = 'anim1 100s, anim2 100s, anim1 100s';
-      window.getComputedStyle(div).animationName;
       var players = div.getAnimationPlayers();
       assert_not_equals(firstAddedPlayer, secondAddedPlayer,
                         'New players are added to start of the list');
       assert_equals(players[0], secondAddedPlayer,
                     'Second player remains in same position after'
                     + ' interleaving');
       assert_equals(players[2], firstAddedPlayer,
                     'First player remains in same position after'
--- a/dom/animation/test/css-integration/test_element-get-animation-players.html
+++ b/dom/animation/test/css-integration/test_element-get-animation-players.html
@@ -32,31 +32,29 @@ test(function() {
   div.remove();
 }, 'getAnimationPlayers for non-animated content');
 
 async_test(function(t) {
   var div = addDiv();
 
   // Add an animation
   div.style.animation = 'anim1 100s';
-  window.getComputedStyle(div).animationName;
   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';
-    window.getComputedStyle(div).animationName;
     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();
@@ -64,48 +62,46 @@ async_test(function(t) {
   }));
 }, 'getAnimationPlayers for CSS Animations');
 
 test(function() {
   var div = addDiv();
 
   // Add an animation that targets multiple properties
   div.style.animation = 'multiPropAnim 100s';
-  window.getComputedStyle(div).animationName;
   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';
-  window.getComputedStyle(div).left;
 
   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';
-    window.getComputedStyle(div).backgroundColor;
     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();
@@ -119,17 +115,16 @@ async_test(function(t) {
   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';
-    window.getComputedStyle(div).backgroundColor;
 
     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');
@@ -147,205 +142,183 @@ async_test(function(t) {
       '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';
-  window.getComputedStyle(div).animationName;
 }, 'getAnimationPlayers for CSS Animations that have finished');
 
 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';
-  window.getComputedStyle(div).animationName;
 
   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';
-  window.getComputedStyle(div).animationName;
   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';
-  window.getComputedStyle(div).animationName;
-
   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';
-  window.getComputedStyle(div).animationName;
   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';
-  window.getComputedStyle(div).animationName;
-
   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);
-    window.getComputedStyle(div).animationName;
-
     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';
-  window.getComputedStyle(div).animationName;
-
   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';
-  window.getComputedStyle(div).animationName;
-
   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';
-  window.getComputedStyle(div).animationName;
-
   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';
-  window.getComputedStyle(div).animationName;
-
   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';
-  window.getComputedStyle(div).left;
 
   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', '');
-  window.getComputedStyle(div).getPropertyValue('-vendor-unsupported');
 
   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';
-  window.getComputedStyle(div).animationName;
-
   var originalPlayer = div.getAnimationPlayers()[0];
 
   // Update pause state (an AnimationPlayer change)
   div.style.animationPlayState = 'paused';
-  window.getComputedStyle(div).animationPlayState;
   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';
-  window.getComputedStyle(div).animationDuration
   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();