Bug 1464568 - Set the shadow base transform value for the case where opacity animations' calculation was skipped. r?kats draft
authorHiroyuki Ikezoe <hikezoe@mozilla.com>
Tue, 05 Jun 2018 12:50:39 +0900
changeset 803971 5f10edbd6784b167178841a79d8c86309b349757
parent 803970 eba3e2c3fc0458b54d545b2ca80de8f63b1c52ab
push id112247
push userhikezoe@mozilla.com
push dateTue, 05 Jun 2018 05:13:07 +0000
reviewerskats
bugs1464568
milestone62.0a1
Bug 1464568 - Set the shadow base transform value for the case where opacity animations' calculation was skipped. r?kats And make DOMWindowUtils.getOMTCTransform work for opacity animations' layer. MozReview-Commit-ID: 7P99WjYqPr0
dom/base/nsDOMWindowUtils.cpp
dom/interfaces/base/nsIDOMWindowUtils.idl
gfx/layers/apz/test/mochitest/helper_bug1464568_opacity.html
gfx/layers/apz/test/mochitest/mochitest.ini
gfx/layers/apz/test/mochitest/test_bug1464568.html
gfx/layers/composite/AsyncCompositionManager.cpp
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -3764,19 +3764,23 @@ nsDOMWindowUtils::GetOMTCTransform(Eleme
   }
 
   nsIFrame* frame = frameOrError.unwrap();
   aResult.Truncate();
   if (!frame) {
     return NS_OK;
   }
 
-  Layer* layer =
-    FrameLayerBuilder::GetDedicatedLayer(frame,
-                                         DisplayItemType::TYPE_TRANSFORM);
+  DisplayItemType itemType = DisplayItemType::TYPE_TRANSFORM;
+  if (nsLayoutUtils::HasEffectiveAnimation(frame, eCSSProperty_opacity) &&
+      !frame->IsTransformed()) {
+    itemType = DisplayItemType::TYPE_OPACITY;
+  }
+
+  Layer* layer = FrameLayerBuilder::GetDedicatedLayer(frame, itemType);
   if (!layer) {
     return NS_OK;
   }
 
   ShadowLayerForwarder* forwarder = layer->Manager()->AsShadowForwarder();
   if (!forwarder || !forwarder->HasShadowManager()) {
     return NS_OK;
   }
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -1696,17 +1696,18 @@ interface nsIDOMWindowUtils : nsISupport
    */
   AString getOMTAStyle(in Element aElement, in AString aProperty,
                        [optional] in AString aPseudoElement);
 
   /*
    * Returns the value of the transform value on the compositor thread.
    * Unlike the above getOMTAStyle, the transform value returned by this
    * includes both of animating and APZ values.
-   * Note: This function doesn't work on WebRender at all.
+   * Note: This function doesn't work on WebRender at all.  Also this function
+   * does work only for transform layer and opacity layer with animations.
    */
   AString getOMTCTransform(in Element aElement,
                            [optional] in AString aPseudoElement);
 
   /**
    * If aHandlingInput is true, this informs the event state manager that
    * we're handling user input. Otherwise, this is a no-op (as by default
    * we're not handling user input).
new file mode 100644
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/helper_bug1464568_opacity.html
@@ -0,0 +1,60 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test that opacity animation is correctly placed during asynchronous scrolling</title>
+  <script src="apz_test_utils.js"></script>
+  <script src="/tests/SimpleTest/paint_listener.js"></script>
+  <meta name="viewport" content="width=device-width"/>
+  <style>
+    #anim {
+      background: green;
+      width: 100px;
+      height: 100px;
+      animation: anim 100s step-start;
+    }
+    @keyframes anim {
+      from { opacity: 0; }
+      to { opacity: 1; }
+    }
+  </style>
+</head>
+<body>
+ <!--
+  This height should be smaller than window height, otherwise the animation
+  followed by this element will be out of view, thus the animation doesn't run
+  on the compositor.
+  -->
+ <div style="height: 500px"></div>
+ <div id="anim"></div>
+</body>
+<script>
+'use strict';
+
+const utils = SpecialPowers.getDOMWindowUtils(window);
+
+async function test_opacity() {
+  utils.setDisplayPortForElement(0, 0, 300, 1000, document.documentElement, 1);
+  await promiseAllPaintsDone();
+
+  let transform = utils.getOMTCTransform(anim);
+  is(transform, "matrix(1, 0, 0, 1, 0, 0)",
+     "The element shouldn't be moved before scrolling");
+
+  utils.setAsyncScrollOffset(document.documentElement, 0, 300);
+
+  await new Promise(resolve => waitForApzFlushedRepaints(resolve));
+
+  transform = utils.getOMTCTransform(anim);
+  is(transform, "matrix(1, 0, 0, 1, 0, -300)",
+     "Element should have been moved by the offset");
+}
+
+if (utils.layerManagerType == 'WebRender') {
+  ok(true, "This test doesn't need to run on WebRender");
+  subtestDone();
+} else {
+  waitUntilApzStable().then(test_opacity).then(subtestDone);
+}
+
+</script>
+</html>
--- a/gfx/layers/apz/test/mochitest/mochitest.ini
+++ b/gfx/layers/apz/test/mochitest/mochitest.ini
@@ -10,16 +10,17 @@
     helper_bug1162771.html
     helper_bug1271432.html
     helper_bug1280013.html
     helper_bug1285070.html
     helper_bug1299195.html
     helper_bug1346632.html
     helper_bug1414336.html
     helper_bug1464568_transform.html
+    helper_bug1464568_opacity.html
     helper_click.html
     helper_div_pan.html
     helper_drag_click.html
     helper_drag_scroll.html
     helper_iframe_pan.html
     helper_iframe1.html
     helper_iframe2.html
     helper_hittest_backface_hidden.html
--- a/gfx/layers/apz/test/mochitest/test_bug1464568.html
+++ b/gfx/layers/apz/test/mochitest/test_bug1464568.html
@@ -7,16 +7,18 @@
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="application/javascript">
     if (isApzEnabled()) {
       SimpleTest.waitForExplicitFinish();
 
       const subtests = [
         { file: 'helper_bug1464568_transform.html',
           prefs: [["apz.test.logging_enabled", true]] },
+        { file: 'helper_bug1464568_opacity.html',
+          prefs: [["apz.test.logging_enabled", true]] },
       ];
       // Run the actual test in its own window, because it requires that the
       // root APZC be scrollable. Mochitest pages themselves often run
       // inside an iframe which means we have no control over the root APZC.
       window.onload = () => {
         runSubtestsSeriallyInFreshWindows(subtests)
         .then(SimpleTest.finish, SimpleTest.finish);
       };
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -705,27 +705,35 @@ SampleAnimations(Layer* aLayer,
                                aStorage,
                                animation.property(),
                                animation.data(),
                                animationValue);
             break;
           }
           case AnimationHelper::SampleResult::Skipped:
             switch (animations[0].property()) {
-              case eCSSProperty_opacity:
+              case eCSSProperty_opacity: {
                 MOZ_ASSERT(
                   layer->AsHostLayer()->GetShadowOpacitySetByAnimation());
 #ifdef DEBUG
                 // Disable this assertion until the root cause is fixed in bug
                 // 1459775.
                 // MOZ_ASSERT(FuzzyEqualsMultiplicative(
                 //   Servo_AnimationValue_GetOpacity(animationValue),
                 //   *(aStorage->GetAnimationOpacity(layer->GetCompositorAnimationsId()))));
 #endif
+                // Even if opacity animation value has unchanged, we have to set
+                // the shadow base transform value here since the value might
+                // have been changed by APZC.
+                HostLayer* layerCompositor = layer->AsHostLayer();
+                layerCompositor->SetShadowBaseTransform(
+                  layer->GetBaseTransform());
+                layerCompositor->SetShadowTransformSetByAnimation(false);
                 break;
+              }
               case eCSSProperty_transform: {
                 MOZ_ASSERT(
                   layer->AsHostLayer()->GetShadowTransformSetByAnimation());
                 MOZ_ASSERT(previousValue);
 #ifdef DEBUG
                 const TransformData& transformData =
                   animations[0].data().get_TransformData();
                 Matrix4x4 frameTransform =