Bug 1395151 - Incorporate ancestors opacity values into the animation opacity value in each keyframe before sending the animation to the compositor if the ancestors were flattened. r?birtles,mattwoodrow draft
authorHiroyuki Ikezoe <hikezoe@mozilla.com>
Thu, 19 Oct 2017 17:56:19 +0900
changeset 683161 dda4f09d1ddf61d716a889252cbf537327504158
parent 682898 a04860cd9c8895aaaadaa32efec5e8b2cdcd24e8
child 736545 0f1f44bd60ff329c050083a5e8742287c941a16b
push id85269
push userhikezoe@mozilla.com
push dateThu, 19 Oct 2017 08:57:06 +0000
reviewersbirtles, mattwoodrow
bugs1395151
milestone58.0a1
Bug 1395151 - Incorporate ancestors opacity values into the animation opacity value in each keyframe before sending the animation to the compositor if the ancestors were flattened. r?birtles,mattwoodrow Once ancestor display items are flattened into an nsDisplayOpacity with animations, the flattened opacity values are hard to extract from nsDisplayOpacity::mOpacity since the mOpacity includes animating value at *this* moment. Also the mOpacity is used if the opacity animation is not able to be sent to the compositor. So in this patch, we introduce a new variable, mFlattenedOpacity to store the flattened opacity values for the ancestors along with mOpacity. MozReview-Commit-ID: KsxukpGrH6P
layout/painting/nsDisplayList.cpp
layout/painting/nsDisplayList.h
layout/reftests/css-animations/opacity-animation-in-fixed-opacity-parent-ref.html
layout/reftests/css-animations/opacity-animation-in-fixed-opacity-parent.html
layout/reftests/css-animations/reftest.list
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -449,30 +449,35 @@ ToTimingFunction(const Maybe<ComputedTim
   uint32_t type = aCTF->GetType() == nsTimingFunction::Type::StepStart ? 1 : 2;
   return TimingFunction(StepFunction(aCTF->GetSteps(), type));
 }
 
 static void
 SetAnimatable(nsCSSPropertyID aProperty,
               const AnimationValue& aAnimationValue,
               nsIFrame* aFrame,
+              const nsDisplayItem* aItem,
               TransformReferenceBox& aRefBox,
               layers::Animatable& aAnimatable)
 {
   MOZ_ASSERT(aFrame);
 
   if (aAnimationValue.IsNull()) {
     aAnimatable = null_t();
     return;
   }
 
   switch (aProperty) {
-    case eCSSProperty_opacity:
-      aAnimatable = aAnimationValue.GetOpacity();
+    case eCSSProperty_opacity: {
+      // Incorporate flattened opacity value into the animation value.
+      aAnimatable =
+        aAnimationValue.GetOpacity() *
+          static_cast<const nsDisplayOpacity*>(aItem)->FlattenedOpacity();
       break;
+    }
     case eCSSProperty_transform: {
       aAnimatable = InfallibleTArray<TransformFunction>();
       if (aAnimationValue.mServo) {
         RefPtr<nsCSSValueSharedList> list;
         Servo_AnimationValue_GetTransform(aAnimationValue.mServo, &list);
         AddTransformFunctions(list, aFrame, aRefBox, aAnimatable);
       } else {
         nsCSSValueSharedList* list =
@@ -482,19 +487,23 @@ SetAnimatable(nsCSSPropertyID aProperty,
       break;
     }
     default:
       MOZ_ASSERT_UNREACHABLE("Unsupported property");
   }
 }
 
 static void
-AddAnimationForProperty(nsIFrame* aFrame, const AnimationProperty& aProperty,
-                        dom::Animation* aAnimation, AnimationInfo& aAnimationInfo,
-                        AnimationData& aData, bool aPending)
+AddAnimationForProperty(nsIFrame* aFrame,
+                        const nsDisplayItem* aItem,
+                        const AnimationProperty& aProperty,
+                        dom::Animation* aAnimation,
+                        AnimationInfo& aAnimationInfo,
+                        AnimationData& aData,
+                        bool aPending)
 {
   MOZ_ASSERT(aAnimation->GetEffect(),
              "Should not be adding an animation without an effect");
   MOZ_ASSERT(!aAnimation->GetCurrentOrPendingStartTime().IsNull() ||
              !aAnimation->IsPlaying() ||
              (aAnimation->GetTimeline() &&
               aAnimation->GetTimeline()->TracksWallclockTime()),
              "If the animation has an unresolved start time it should either"
@@ -568,33 +577,39 @@ AddAnimationForProperty(nsIFrame* aFrame
   // If the animation is additive or accumulates, we need to pass its base value
   // to the compositor.
 
   AnimationValue baseStyle =
     aAnimation->GetEffect()->AsKeyframeEffect()->BaseStyle(aProperty.mProperty);
   if (!baseStyle.IsNull()) {
     SetAnimatable(aProperty.mProperty,
                   baseStyle,
-                  aFrame, refBox,
+                  aFrame,
+                  aItem,
+                  refBox,
                   animation->baseStyle());
   } else {
     animation->baseStyle() = null_t();
   }
 
   for (uint32_t segIdx = 0; segIdx < aProperty.mSegments.Length(); segIdx++) {
     const AnimationPropertySegment& segment = aProperty.mSegments[segIdx];
 
     AnimationSegment* animSegment = animation->segments().AppendElement();
     SetAnimatable(aProperty.mProperty,
                   segment.mFromValue,
-                  aFrame, refBox,
+                  aFrame,
+                  aItem,
+                  refBox,
                   animSegment->startState());
     SetAnimatable(aProperty.mProperty,
                   segment.mToValue,
-                  aFrame, refBox,
+                  aFrame,
+                  aItem,
+                  refBox,
                   animSegment->endState());
 
     animSegment->startPortion() = segment.mFromKey;
     animSegment->endPortion() = segment.mToKey;
     animSegment->startComposite() =
       static_cast<uint8_t>(segment.mFromComposite);
     animSegment->endComposite() =
       static_cast<uint8_t>(segment.mToComposite);
@@ -731,17 +746,23 @@ AddAnimationsForProperty(nsIFrame* aFram
     // driver under test control. In this case, the next time the refresh
     // driver is advanced it will trigger any pending animations.
     if (anim->PlayState() == AnimationPlayState::Pending &&
         (anim->GetTimeline() &&
          !anim->GetTimeline()->TracksWallclockTime())) {
       continue;
     }
 
-    AddAnimationForProperty(aFrame, *property, anim, aAnimationInfo, data, aPending);
+    AddAnimationForProperty(aFrame,
+                            aItem,
+                            *property,
+                            anim,
+                            aAnimationInfo,
+                            data,
+                            aPending);
     keyframeEffect->SetIsRunningOnCompositor(aProperty, true);
     sentAnimations = true;
   }
 
   if (sentAnimations && aProperty == eCSSProperty_transform) {
     TimeStamp now = aFrame->PresContext()->RefreshDriver()->MostRecentRefresh();
     effects->UpdateLastTransformSyncTime(now);
   }
@@ -6004,16 +6025,17 @@ nsresult nsDisplayWrapper::WrapListsInPl
 }
 
 nsDisplayOpacity::nsDisplayOpacity(nsDisplayListBuilder* aBuilder,
                                    nsIFrame* aFrame, nsDisplayList* aList,
                                    const ActiveScrolledRoot* aActiveScrolledRoot,
                                    bool aForEventsAndPluginsOnly)
     : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot)
     , mOpacity(aFrame->StyleEffects()->mOpacity)
+    , mFlattenedOpacity(1.0)
     , mForEventsAndPluginsOnly(aForEventsAndPluginsOnly)
 {
   MOZ_COUNT_CTOR(nsDisplayOpacity);
 }
 
 #ifdef NS_BUILD_REFCNT_LOGGING
 nsDisplayOpacity::~nsDisplayOpacity() {
   MOZ_COUNT_DTOR(nsDisplayOpacity);
@@ -6081,16 +6103,17 @@ nsDisplayOpacity::NeedsActiveLayer(nsDis
 
 void
 nsDisplayOpacity::ApplyOpacity(nsDisplayListBuilder* aBuilder,
                              float aOpacity,
                              const DisplayItemClipChain* aClip)
 {
   NS_ASSERTION(CanApplyOpacity(), "ApplyOpacity should be allowed");
   mOpacity = mOpacity * aOpacity;
+  mFlattenedOpacity = mFlattenedOpacity * aOpacity;
   IntersectClip(aBuilder, aClip);
 }
 
 bool
 nsDisplayOpacity::CanApplyOpacity() const
 {
   return true;
 }
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -4278,18 +4278,27 @@ public:
   bool CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) override;
 
   virtual bool CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
                                        mozilla::wr::IpcResourceUpdateQueue& aResources,
                                        const StackingContextHelper& aSc,
                                        mozilla::layers::WebRenderLayerManager* aManager,
                                        nsDisplayListBuilder* aDisplayListBuilder) override;
 
+  float FlattenedOpacity() const
+  {
+    return mFlattenedOpacity;
+  }
 private:
   float mOpacity;
+  // Opacity value for all flattened ancestors.
+  // This value is incorporated into the animation parameters we set up when we
+  // send animations to the compositor, since the animation values does not have
+  // this flattened opacity value.
+  float mFlattenedOpacity;
   bool mForEventsAndPluginsOnly;
 };
 
 class nsDisplayBlendMode : public nsDisplayWrapList {
 public:
   nsDisplayBlendMode(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                         nsDisplayList* aList, uint8_t aBlendMode,
                         const ActiveScrolledRoot* aActiveScrolledRoot,
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-animations/opacity-animation-in-fixed-opacity-parent-ref.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset=utf-8>
+  <title>Testcase, bug 1395151</title>
+  <style type="text/css">
+.ok {
+  background : #0f0;
+  height : 100px;
+  width : 100px;
+}
+.error {
+  opacity : .001;
+  height : 100px;
+  width : 100px;
+}
+.error > span {
+  background : #f00;
+  display : block;
+  height : 100px;
+  width : 100px;
+}
+  </style>
+</head>
+<body>
+<div class="ok">
+  <div class="error"><span></span></div>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-animations/opacity-animation-in-fixed-opacity-parent.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html class="reftest-wait reftest-no-flush">
+<head>
+  <meta charset=utf-8>
+  <title>Testcase, bug 1395151</title>
+  <style type="text/css">
+@keyframes test-anim {
+  from { opacity : 1 }
+  to { opacity : 1 }
+}
+
+.ok {
+  background : #0f0;
+  height : 100px;
+  width : 100px;
+}
+
+.error {
+  opacity : .001;
+  height : 100px;
+  width : 100px;
+}
+
+.error > span {
+  animation  : test-anim 1s linear infinite;
+  background : #f00;
+  display : block;
+  height : 100px;
+  width : 100px;
+}
+  </style>
+</head>
+<body>
+<div class="ok">
+  <div class="error"><span></span></div>
+</div>
+<script>
+document.addEventListener('MozReftestInvalidate', () => {
+  requestAnimationFrame(() => {
+    requestAnimationFrame(() => {
+      document.documentElement.classList.remove('reftest-wait');
+    });
+  });
+}, false);
+</script>
+</body>
+</html>
--- a/layout/reftests/css-animations/reftest.list
+++ b/layout/reftests/css-animations/reftest.list
@@ -60,8 +60,10 @@ fails-if(layerChecksEnabled) == backgrou
 == reframe-and-animation-starts-at-the-same-time.html reframe-and-animation-starts-at-the-same-time-ref.html
 == change-animation-name-to-none-in-rule.html change-animation-name-in-rule-ref.html
 == change-animation-name-to-other-in-rule.html change-animation-name-in-rule-ref.html
 == change-animation-name-to-non-existent-in-rule.html change-animation-name-in-rule-ref.html
 == no-style-sharing-with-animations.html no-style-sharing-with-animations-ref.html
 
 == continuation-opacity.html continuation-opacity-ref.html
 == ib-split-sibling-opacity.html about:blank
+
+== opacity-animation-in-fixed-opacity-parent.html opacity-animation-in-fixed-opacity-parent-ref.html