Bug 1279403 - Part 1: Force to apply corresponding change hint if there is no corresponding layer to generate display item even if animation's segment is transform:none or 100% opacity. r?birtles draft
authorHiroyuki Ikezoe <hiikezoe@mozilla-japan.org>
Mon, 11 Jul 2016 08:27:02 +0900
changeset 386059 55b93c1809cbf3c2cfd23094d61d79389a996700
parent 386010 679118259e91f40d4a8f968f03ec4cff066cdb5b
child 386060 179f6ebe5d409c0f3285b59219f9c1572de55690
push id22611
push userhiikezoe@mozilla-japan.org
push dateMon, 11 Jul 2016 02:42:29 +0000
reviewersbirtles
bugs1279403
milestone50.0a1
Bug 1279403 - Part 1: Force to apply corresponding change hint if there is no corresponding layer to generate display item even if animation's segment is transform:none or 100% opacity. r?birtles To create a stacking context for animations on transform:none segment, we need to set NS_FRAME_MAY_BE_TRANSFORMED. The fix is comming in part 2. Note that in case of animations which has properties preventing running on the compositor, e.g., width or height, corresponding layer is not created at all, but even in such cases, we normally set valid change hint for such animations in each tick, i.e. restyles in each tick. For example: div.animate([{ opacity: 1, width: '100px' }, { opacity: 0, width: '200px' }], 1000); This animation causes restyles in every ticks without this patch, this patch does not affect such animations at all. The only animations which will be affected by this patch are animations which has opacity/transform but did not have those properies. e.g, setting transform by setKeyframes or changing target element from other target which prevents running on the compositor, etc. MozReview-Commit-ID: 78fYqyX8uDX
dom/animation/test/chrome/test_running_on_compositor.html
layout/base/RestyleManager.cpp
layout/reftests/web-animations/reftest.list
layout/reftests/web-animations/stacking-context-animation-changing-target-ref.html
layout/reftests/web-animations/stacking-context-opacity-changing-keyframe.html
layout/reftests/web-animations/stacking-context-opacity-changing-target.html
--- a/dom/animation/test/chrome/test_running_on_compositor.html
+++ b/dom/animation/test/chrome/test_running_on_compositor.html
@@ -465,10 +465,55 @@ promise_test(function(t) {
  }).then(function() {
     assert_equals(animation.isRunningOnCompositor, omtaEnabled,
                   'Animation with fill:backwards in delay phase reports ' +
                   'that it is running on the compositor after delay phase');
   });
 }, 'animation with fill:backwards in delay phase is running on the ' +
    ' main-thread while it is in delay phase');
 
+promise_test(function(t) {
+  var div = addDiv(t);
+  var animation = div.animate([{ opacity: 1, offset: 0 },
+                               { opacity: 1, offset: 0.99 },
+                               { opacity: 0, offset: 1 }], 100 * MS_PER_SEC);
+
+  var another = addDiv(t);
+
+  return animation.ready.then(function() {
+    assert_equals(animation.isRunningOnCompositor, omtaEnabled,
+                  'Opacity animation on a 100% opacity keyframe reports ' +
+                  'that it is running on the compositor from the begining');
+
+    animation.effect.target = another;
+    return waitForFrame();
+  }).then(function() {
+    assert_equals(animation.isRunningOnCompositor, omtaEnabled,
+                  'Opacity animation on a 100% opacity keyframe keeps ' +
+                  'running on the compositor after changing the target ' +
+                  'element');
+  });
+}, '100% opacity animations with keeps running on the ' +
+   'compositor after changing the target element');
+
+promise_test(function(t) {
+  var div = addDiv(t);
+  var animation = div.animate({ color: ['red', 'black'] }, 100 * MS_PER_SEC);
+
+  return animation.ready.then(function() {
+    assert_equals(animation.isRunningOnCompositor, false,
+                  'Color animation reports that it is not running on the ' +
+                  'compositor');
+
+    animation.effect.setKeyframes([{ opacity: 1, offset: 0 },
+                                   { opacity: 1, offset: 0.99 },
+                                   { opacity: 0, offset: 1 }]);
+    return waitForFrame();
+  }).then(function() {
+    assert_equals(animation.isRunningOnCompositor, omtaEnabled,
+                  '100% opacity animation set by using setKeyframes reports ' +
+                  'that it is running on the compositor');
+  });
+}, '100% opacity animation set up by converting an existing animation with ' +
+   'cannot be run on the compositor, is running on the compositor');
+
 </script>
 </body>
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -2749,16 +2749,34 @@ ElementRestyler::AddLayerChangesForAnima
       // nsChangeHint_UpdateTransformLayer, ApplyRenderingChangeToTree would
       // complain that we're updating a transform layer without a transform).
       if (layerInfo.mLayerType == nsDisplayItem::TYPE_TRANSFORM &&
           !mFrame->StyleDisplay()->HasTransformStyle()) {
         continue;
       }
       hint |= layerInfo.mChangeHint;
     }
+
+    // We consider it's the first paint for the frame if we have an animation
+    // for the property but have no layer.
+    // Note that in case of animations which has properties preventing running
+    // on the compositor, e.g., width or height, corresponding layer is not
+    // created at all, but even in such cases, we normally set valid change
+    // hint for such animations in each tick, i.e. restyles in each tick. As
+    // a result, we usually do restyles for such animations in every tick on
+    // the main-thread.  The only animations which will be affected by this
+    // explicit change hint are animations that have opacity/transform but did
+    // not have those properies just before. e.g,  setting transform by
+    // setKeyframes or changing target element from other target which prevents
+    // running on the compositor, etc.
+    if (!layer &&
+        nsLayoutUtils::HasRelevantAnimationOfProperty(mFrame,
+                                                      layerInfo.mProperty)) {
+      hint |= layerInfo.mChangeHint;
+    }
   }
   if (hint) {
     mChangeList->AppendChange(mFrame, mContent, hint);
   }
 }
 
 void
 ElementRestyler::CaptureChange(nsStyleContext* aOldContext,
--- a/layout/reftests/web-animations/reftest.list
+++ b/layout/reftests/web-animations/reftest.list
@@ -1,3 +1,5 @@
 test-pref(dom.animations-api.core.enabled,true) == 1246046-1.html green-box.html
 test-pref(dom.animations-api.core.enabled,true) == 1267937-1.html 1267937-ref.html
 test-pref(dom.animations-api.core.enabled,true) == stacking-context-transform-none-animation-before-appending-element.html stacking-context-animation-ref.html
+test-pref(dom.animations-api.core.enabled,true) == stacking-context-opacity-changing-keyframe.html stacking-context-animation-ref.html
+test-pref(dom.animations-api.core.enabled,true) == stacking-context-opacity-changing-target.html stacking-context-animation-changing-target-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/web-animations/stacking-context-animation-changing-target-ref.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<title>Reference of testcases for bug 1279403</title>
+<style>
+span {
+  height: 100px;
+  width: 100px;
+  background: green;
+  position: fixed;
+  top: 50px;
+}
+div {
+  height: 100px;
+  width: 100px;
+  background: blue;
+}
+#first {
+}
+#second {
+  position: fixed;
+}
+</style>
+<span></span>
+<div id="first"></div>
+<div id="second"></div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/web-animations/stacking-context-opacity-changing-keyframe.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html class="reftest-wait reftest-no-flush">
+<title>Changing keyframes to opacity frames creates a stacking context</title>
+<style>
+span {
+  height: 100px;
+  width: 100px;
+  position: fixed;
+  background: green;
+  top: 50px;
+}
+#test {
+  width: 100px; height: 100px;
+  background: blue;
+}
+</style>
+<span></span>
+<div id="test"></div>
+<script>
+  var anim = document.getElementById("test")
+    .animate({ }, { duration: 100000 });
+  anim.ready.then(function() {
+    anim.effect.setKeyframes({ opacity: [1, 1] });
+    requestAnimationFrame(function() {
+      document.documentElement.classList.remove("reftest-wait");
+    });
+  });
+</script>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/web-animations/stacking-context-opacity-changing-target.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html class="reftest-wait reftest-no-flush">
+<title>
+Opacity animation creates a stacking context when changing its target
+</title>
+<style>
+span {
+  height: 100px;
+  width: 100px;
+  position: fixed;
+  background: green;
+  top: 50px;
+}
+div {
+  width: 100px; height: 100px;
+  background: blue;
+}
+</style>
+<span></span>
+<div id="test"></div>
+<div id="another"></div>
+<script>
+  var anim = document.getElementById("test")
+    .animate({ opacity: [1, 1] }, { duration: 100000 });
+  anim.ready.then(function() {
+    anim.effect.target = document.getElementById("another");
+    requestAnimationFrame(function() {
+      document.documentElement.classList.remove("reftest-wait");
+    });
+  });
+</script>