Bug 1529422 - Part 1: Add all transform-like properties into ActiveLayerTracker. r=hiro,mattwoodrow
authorBoris Chiou <boris.chiou@gmail.com>
Sat, 23 Feb 2019 00:21:45 +0000
changeset 521635 6ee7f3988e84a856b8a07756ca25f7bf519dc6a9
parent 521634 c0389cee5b1fc50764eaabf24c25e3380990e7b5
child 521636 a92c6acc3a946e43d935d6dffc4cf4314667706b
push id2032
push userffxbld-merge
push dateMon, 13 May 2019 09:36:57 +0000
treeherdermozilla-release@455c1065dcbe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewershiro, mattwoodrow
bugs1529422
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1529422 - Part 1: Add all transform-like properties into ActiveLayerTracker. r=hiro,mattwoodrow Let ActiveLayerTracker track individual transforms. (Will add motion-path in the future.) Besides, using a property set for transform and opacity is more efficient, so let's change it. For background position, we use a different code path, so we can have more restrictions in IsStyleAnimated. Differential Revision: https://phabricator.services.mozilla.com/D19631
dom/animation/KeyframeEffect.cpp
dom/animation/KeyframeEffect.h
dom/base/nsDOMWindowUtils.cpp
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
layout/painting/ActiveLayerTracker.cpp
layout/painting/ActiveLayerTracker.h
layout/painting/FrameLayerBuilder.cpp
layout/painting/nsDisplayList.cpp
--- a/dom/animation/KeyframeEffect.cpp
+++ b/dom/animation/KeyframeEffect.cpp
@@ -253,16 +253,36 @@ const AnimationProperty* KeyframeEffect:
     if (IsEffectiveProperty(aEffects, property.mProperty)) {
       result = &property;
     }
     return result;
   }
   return nullptr;
 }
 
+bool KeyframeEffect::HasEffectiveAnimationOfPropertySet(
+    const nsCSSPropertyIDSet& aPropertySet, const EffectSet& aEffect) const {
+  bool ret = false;
+  for (const AnimationProperty& property : mProperties) {
+    if (!aPropertySet.HasProperty(property.mProperty)) {
+      continue;
+    }
+
+    // Only consider the property if it is not overridden by !important rules in
+    // the transitions level. If one of the properties is overridden by
+    // !important rules, we return false. This is especially for transform-like
+    // properties because all of them should be running on the same thread.
+    if (!IsEffectiveProperty(aEffect, property.mProperty)) {
+      return false;
+    }
+    ret = true;
+  }
+  return ret;
+}
+
 nsCSSPropertyIDSet KeyframeEffect::GetPropertiesForCompositor(
     EffectSet& aEffects, const nsIFrame* aFrame) const {
   MOZ_ASSERT(&aEffects ==
              EffectSet::GetEffectSet(mTarget->mElement, mTarget->mPseudoType));
 
   nsCSSPropertyIDSet properties;
 
   if (!IsInEffect() && !IsCurrent()) {
--- a/dom/animation/KeyframeEffect.h
+++ b/dom/animation/KeyframeEffect.h
@@ -196,16 +196,23 @@ class KeyframeEffect : public AnimationE
   // can't run on the compositor.
   bool HasEffectiveAnimationOfProperty(nsCSSPropertyID aProperty,
                                        const EffectSet& aEffect) const {
     return GetEffectiveAnimationOfProperty(aProperty, aEffect) != nullptr;
   }
   const AnimationProperty* GetEffectiveAnimationOfProperty(
       nsCSSPropertyID aProperty, const EffectSet& aEffect) const;
 
+  // This is a similar version as the above function, but for a
+  // nsCSSPropertyIDSet, and this returns true if this keyframe effect has
+  // properties in |aPropertySet| and if the properties are not overridden by
+  // !important rule or transition level.
+  bool HasEffectiveAnimationOfPropertySet(
+      const nsCSSPropertyIDSet& aPropertySet, const EffectSet& aEffect) const;
+
   // Returns all the effective animated CSS properties that can be animated on
   // the compositor and are not overridden by a higher cascade level.
   //
   // NOTE: This function is basically called for all KeyframeEffects on an
   // element thus it takes |aEffects| to avoid multiple calls of
   // EffectSet::GetEffect().
   //
   // NOTE(2): This function does NOT check that animations are permitted on
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -3450,17 +3450,18 @@ nsDOMWindowUtils::GetOMTCTransform(Eleme
 
   nsIFrame* frame = frameOrError.unwrap();
   aResult.Truncate();
   if (!frame) {
     return NS_OK;
   }
 
   DisplayItemType itemType = DisplayItemType::TYPE_TRANSFORM;
-  if (nsLayoutUtils::HasEffectiveAnimation(frame, eCSSProperty_opacity) &&
+  if (nsLayoutUtils::HasEffectiveAnimation(
+          frame, nsCSSPropertyIDSet::OpacityProperties()) &&
       !frame->IsTransformed()) {
     itemType = DisplayItemType::TYPE_OPACITY;
   }
 
   Layer* layer = FrameLayerBuilder::GetDedicatedLayer(frame, itemType);
   if (!layer) {
     return NS_OK;
   }
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -240,66 +240,70 @@ static bool MayHaveAnimationOfPropertySe
     return aTarget->MayHaveOpacityAnimation();
   }
 
   MOZ_ASSERT(aPropertySet.Equals(nsCSSPropertyIDSet::TransformLikeProperties()),
              "Should equal to transform-like properties at this branch");
   return aTarget->MayHaveTransformAnimation();
 }
 
-bool nsLayoutUtils::HasAnimationOfPropertySet(
-    EffectSet* aEffectSet, const nsCSSPropertyIDSet& aPropertySet) {
-  if (!aEffectSet || !MayHaveAnimationOfPropertySet(aEffectSet, aPropertySet)) {
+template <typename EffectSetOrFrame>
+static bool HasAnimationOfPropertySetImpl(
+    EffectSetOrFrame* aTarget, const nsCSSPropertyIDSet& aPropertySet) {
+  if (!aTarget || !MayHaveAnimationOfPropertySet(aTarget, aPropertySet)) {
     return false;
   }
 
   return HasMatchingAnimations(
-      aEffectSet, [&aPropertySet](KeyframeEffect& aEffect) {
+      aTarget, [&aPropertySet](KeyframeEffect& aEffect) {
         return (aEffect.IsInEffect() || aEffect.IsCurrent()) &&
                aEffect.HasAnimationOfPropertySet(aPropertySet);
       });
 }
 
 bool nsLayoutUtils::HasAnimationOfPropertySet(
+    EffectSet* aEffectSet, const nsCSSPropertyIDSet& aPropertySet) {
+  return HasAnimationOfPropertySetImpl(aEffectSet, aPropertySet);
+}
+
+bool nsLayoutUtils::HasAnimationOfPropertySet(
     const nsIFrame* aFrame, const nsCSSPropertyIDSet& aPropertySet) {
-  if (!MayHaveAnimationOfPropertySet(aFrame, aPropertySet)) {
-    return false;
-  }
-
-  return HasMatchingAnimations(
-      aFrame, [&aPropertySet](KeyframeEffect& aEffect) {
-        return (aEffect.IsInEffect() || aEffect.IsCurrent()) &&
-               aEffect.HasAnimationOfPropertySet(aPropertySet);
-      });
+  return HasAnimationOfPropertySetImpl(aFrame, aPropertySet);
 }
 
 bool nsLayoutUtils::HasEffectiveAnimation(const nsIFrame* aFrame,
                                           nsCSSPropertyID aProperty) {
   EffectSet* effects = EffectSet::GetEffectSet(aFrame);
+  // This function isn't called by opacity or transform, so we don't have to
+  // check MayHaveAnimationOfPropertySet.
   if (!effects) {
     return false;
   }
 
-  if (nsCSSPropertyIDSet::TransformLikeProperties().HasProperty(aProperty) &&
-      !effects->MayHaveTransformAnimation()) {
-    return false;
-  }
-
-  if (aProperty == eCSSProperty_opacity &&
-      !effects->MayHaveOpacityAnimation()) {
-    return false;
-  }
-
   return HasMatchingAnimations(
       effects, [&aProperty, &effects](KeyframeEffect& aEffect) {
         return (aEffect.IsInEffect() || aEffect.IsCurrent()) &&
                aEffect.HasEffectiveAnimationOfProperty(aProperty, *effects);
       });
 }
 
+bool nsLayoutUtils::HasEffectiveAnimation(
+    const nsIFrame* aFrame, const nsCSSPropertyIDSet& aPropertySet) {
+  EffectSet* effects = EffectSet::GetEffectSet(aFrame);
+  if (!effects || !MayHaveAnimationOfPropertySet(aFrame, aPropertySet)) {
+    return false;
+  }
+
+  return HasMatchingAnimations(effects, [&aPropertySet,
+                                         &effects](KeyframeEffect& aEffect) {
+    return (aEffect.IsInEffect() || aEffect.IsCurrent()) &&
+           aEffect.HasEffectiveAnimationOfPropertySet(aPropertySet, *effects);
+  });
+}
+
 /* static */ nsCSSPropertyIDSet
 nsLayoutUtils::GetAnimationPropertiesForCompositor(const nsIFrame* aFrame) {
   nsCSSPropertyIDSet properties;
 
   EffectSet* effects = EffectSet::GetEffectSet(aFrame);
   if (!effects) {
     return properties;
   }
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -16,17 +16,17 @@
 #include "nsBoundingMetrics.h"
 #include "nsChangeHint.h"
 #include "nsFrameList.h"
 #include "mozilla/layout/FrameChildList.h"
 #include "mozilla/layers/ScrollableLayerGuid.h"
 #include "nsThreadUtils.h"
 #include "nsIPrincipal.h"
 #include "nsIWidget.h"
-#include "nsCSSPropertyID.h"
+#include "nsCSSPropertyIDSet.h"
 #include "nsStyleCoord.h"
 #include "nsStyleConsts.h"
 #include "nsGkAtoms.h"
 #include "imgIContainer.h"
 #include "mozilla/gfx/2D.h"
 #include "Units.h"
 #include "mozilla/ToString.h"
 #include "mozilla/ReflowOutput.h"
@@ -2281,31 +2281,37 @@ class nsLayoutUtils {
    * Returns true if |aFrame| has an animation of a property in |aPropertySet|
    * regardless of whether any property in the set is overridden by !important
    * rule.
    */
   static bool HasAnimationOfPropertySet(const nsIFrame* aFrame,
                                         const nsCSSPropertyIDSet& aPropertySet);
 
   /**
-   * Returns true if |aEffectSet| has an animation of a property |aPropertySet|
-   * regardless of whether any property in the set is overridden by !important
-   * rule.
+   * Returns true if |aEffectSet| has an animation of a property in
+   * |aPropertySet| regardless of whether any property in the set is overridden
+   * by !important rule.
    */
   static bool HasAnimationOfPropertySet(mozilla::EffectSet* aEffectSet,
                                         const nsCSSPropertyIDSet& aPropertySet);
-
   /**
    * Returns true if |aFrame| has an animation of |aProperty| which is
    * not overridden by !important rules.
    */
   static bool HasEffectiveAnimation(const nsIFrame* aFrame,
                                     nsCSSPropertyID aProperty);
 
   /**
+   * Returns true if |aFrame| has animations of properties in |aPropertySet|,
+   * and all of these properties are not overridden by !important rules.
+   */
+  static bool HasEffectiveAnimation(const nsIFrame* aFrame,
+                                    const nsCSSPropertyIDSet& aPropertySet);
+
+  /**
    * Returns all effective animated CSS properties on |aFrame|. That means
    * properties that can be animated on the compositor and are not overridden by
    * a higher cascade level.
    */
   static nsCSSPropertyIDSet GetAnimationPropertiesForCompositor(
       const nsIFrame* aFrame);
 
   /**
--- a/layout/painting/ActiveLayerTracker.cpp
+++ b/layout/painting/ActiveLayerTracker.cpp
@@ -66,16 +66,22 @@ class LayerActivity {
     return mRestyleCounts[GetActivityIndexForProperty(aProperty)];
   }
 
   static ActivityIndex GetActivityIndexForProperty(nsCSSPropertyID aProperty) {
     switch (aProperty) {
       case eCSSProperty_opacity:
         return ACTIVITY_OPACITY;
       case eCSSProperty_transform:
+      case eCSSProperty_translate:
+      case eCSSProperty_rotate:
+      case eCSSProperty_scale:
+        // TODO: Bug 1186329: Add motion-path into ActiveLayerTracker.
+        // Note: All transform-like properties are mapping to the same activity
+        // index.
         return ACTIVITY_TRANSFORM;
       case eCSSProperty_left:
         return ACTIVITY_LEFT;
       case eCSSProperty_top:
         return ACTIVITY_TOP;
       case eCSSProperty_right:
         return ACTIVITY_RIGHT;
       case eCSSProperty_bottom:
@@ -87,16 +93,27 @@ class LayerActivity {
       case eCSSProperty_background_position_y:
         return ACTIVITY_BACKGROUND_POSITION;
       default:
         MOZ_ASSERT(false);
         return ACTIVITY_OPACITY;
     }
   }
 
+  static ActivityIndex GetActivityIndexForPropertySet(
+      const nsCSSPropertyIDSet& aPropertySet) {
+    if (aPropertySet.Intersect(nsCSSPropertyIDSet::TransformLikeProperties())
+            .Equals(aPropertySet)) {
+      return ACTIVITY_TRANSFORM;
+    }
+    MOZ_ASSERT(aPropertySet.Intersect(nsCSSPropertyIDSet::OpacityProperties())
+                   .Equals(aPropertySet));
+    return ACTIVITY_OPACITY;
+  }
+
   // While tracked, exactly one of mFrame or mContent is non-null, depending
   // on whether this property is stored on a frame or on a content node.
   // When this property is expired by the layer activity tracker, both mFrame
   // and mContent are nulled-out and the property is deleted.
   nsIFrame* mFrame;
   nsIContent* mContent;
 
   nsExpirationState mState;
@@ -283,17 +300,17 @@ static void IncrementScaleRestyleCountIf
 }
 
 /* static */ void ActiveLayerTracker::NotifyRestyle(nsIFrame* aFrame,
                                                     nsCSSPropertyID aProperty) {
   LayerActivity* layerActivity = GetLayerActivityForUpdate(aFrame);
   uint8_t& mutationCount = layerActivity->RestyleCountForProperty(aProperty);
   IncrementMutationCount(&mutationCount);
 
-  if (aProperty == eCSSProperty_transform) {
+  if (nsCSSPropertyIDSet::TransformLikeProperties().HasProperty(aProperty)) {
     IncrementScaleRestyleCountIfNeeded(aFrame, layerActivity);
   }
 }
 
 /* static */ void ActiveLayerTracker::NotifyOffsetRestyle(nsIFrame* aFrame) {
   LayerActivity* layerActivity = GetLayerActivityForUpdate(aFrame);
   IncrementMutationCount(
       &layerActivity->mRestyleCounts[LayerActivity::ACTIVITY_LEFT]);
@@ -374,28 +391,16 @@ static bool IsPresContextInScriptAnimati
         0xFF;
   } else {
     IncrementMutationCount(
         &layerActivity
              ->mRestyleCounts[LayerActivity::ACTIVITY_TRIGGERED_REPAINT]);
   }
 }
 
-/* static */ bool ActiveLayerTracker::IsStyleMaybeAnimated(
-    nsIFrame* aFrame, nsCSSPropertyID aProperty) {
-  return IsStyleAnimated(nullptr, aFrame, aProperty);
-}
-
-/* static */ bool ActiveLayerTracker::IsBackgroundPositionAnimated(
-    nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) {
-  return IsStyleAnimated(aBuilder, aFrame,
-                         eCSSProperty_background_position_x) ||
-         IsStyleAnimated(aBuilder, aFrame, eCSSProperty_background_position_y);
-}
-
 static bool CheckScrollInducedActivity(
     LayerActivity* aLayerActivity, LayerActivity::ActivityIndex aActivityIndex,
     nsDisplayListBuilder* aBuilder) {
   if (!aLayerActivity->mScrollHandlerInducedActivity.contains(aActivityIndex) ||
       !aLayerActivity->mAnimatingScrollHandlerFrame.IsAlive()) {
     return false;
   }
 
@@ -407,62 +412,109 @@ static bool CheckScrollInducedActivity(
 
   // The scroll frame has been destroyed or has become inactive. Clear it from
   // the layer activity so that it can expire.
   aLayerActivity->mAnimatingScrollHandlerFrame = nullptr;
   aLayerActivity->mScrollHandlerInducedActivity.clear();
   return false;
 }
 
+/* static */ bool ActiveLayerTracker::IsBackgroundPositionAnimated(
+    nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) {
+  LayerActivity* layerActivity = GetLayerActivity(aFrame);
+  if (layerActivity) {
+    LayerActivity::ActivityIndex activityIndex =
+        LayerActivity::ActivityIndex::ACTIVITY_BACKGROUND_POSITION;
+    if (layerActivity->mRestyleCounts[activityIndex] >= 2) {
+      // If the frame needs to be repainted frequently, we probably don't get
+      // much from treating the property as animated, *unless* this frame's
+      // 'scale' (which includes the bounds changes of a rotation) is changing.
+      // Marking a scaling transform as animating allows us to avoid resizing
+      // the texture, even if we have to repaint the contents of that texture.
+      if (layerActivity
+              ->mRestyleCounts[LayerActivity::ACTIVITY_TRIGGERED_REPAINT] < 2) {
+        return true;
+      }
+    }
+    if (CheckScrollInducedActivity(layerActivity, activityIndex, aBuilder)) {
+      return true;
+    }
+  }
+  return nsLayoutUtils::HasEffectiveAnimation(
+             aFrame, eCSSProperty_background_position_x) ||
+         nsLayoutUtils::HasEffectiveAnimation(
+             aFrame, eCSSProperty_background_position_y);
+}
+
+/* static */ bool ActiveLayerTracker::IsTransformAnimated(
+    nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) {
+  return IsStyleAnimated(aBuilder, aFrame,
+                         nsCSSPropertyIDSet::TransformLikeProperties());
+}
+
+/* static */ bool ActiveLayerTracker::IsTransformMaybeAnimated(
+    nsIFrame* aFrame) {
+  return IsStyleAnimated(nullptr, aFrame,
+                         nsCSSPropertyIDSet::TransformLikeProperties());
+}
+
 /* static */ bool ActiveLayerTracker::IsStyleAnimated(
     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
-    nsCSSPropertyID aProperty) {
-  // TODO: Add some abuse restrictions
+    const nsCSSPropertyIDSet& aPropertySet) {
+  MOZ_ASSERT(
+      aPropertySet.Intersect(nsCSSPropertyIDSet::TransformLikeProperties())
+              .Equals(aPropertySet) ||
+          aPropertySet.Intersect(nsCSSPropertyIDSet::OpacityProperties())
+              .Equals(aPropertySet),
+      "Only subset of opacity or transform-like properties set calls this");
+
+  const nsCSSPropertyIDSet transformSet =
+      nsCSSPropertyIDSet::TransformLikeProperties();
   if ((aFrame->StyleDisplay()->mWillChangeBitField &
        NS_STYLE_WILL_CHANGE_TRANSFORM) &&
-      aProperty == eCSSProperty_transform &&
+      aPropertySet.Intersects(transformSet) &&
       (!aBuilder ||
        aBuilder->IsInWillChangeBudget(aFrame, aFrame->GetSize()))) {
     return true;
   }
   if ((aFrame->StyleDisplay()->mWillChangeBitField &
        NS_STYLE_WILL_CHANGE_OPACITY) &&
-      aProperty == eCSSProperty_opacity &&
+      aPropertySet.Intersects(nsCSSPropertyIDSet::OpacityProperties()) &&
       (!aBuilder ||
        aBuilder->IsInWillChangeBudget(aFrame, aFrame->GetSize()))) {
     return true;
   }
 
   LayerActivity* layerActivity = GetLayerActivity(aFrame);
   if (layerActivity) {
     LayerActivity::ActivityIndex activityIndex =
-        LayerActivity::GetActivityIndexForProperty(aProperty);
+        LayerActivity::GetActivityIndexForPropertySet(aPropertySet);
     if (layerActivity->mRestyleCounts[activityIndex] >= 2) {
       // If the frame needs to be repainted frequently, we probably don't get
       // much from treating the property as animated, *unless* this frame's
       // 'scale' (which includes the bounds changes of a rotation) is changing.
       // Marking a scaling transform as animating allows us to avoid resizing
       // the texture, even if we have to repaint the contents of that texture.
       if (layerActivity
                   ->mRestyleCounts[LayerActivity::ACTIVITY_TRIGGERED_REPAINT] <
               2 ||
-          (aProperty == eCSSProperty_transform &&
+          (aPropertySet.Intersects(transformSet) &&
            IsScaleSubjectToAnimation(aFrame))) {
         return true;
       }
     }
     if (CheckScrollInducedActivity(layerActivity, activityIndex, aBuilder)) {
       return true;
     }
   }
-  if (aProperty == eCSSProperty_transform &&
+  if (aPropertySet.Intersects(transformSet) &&
       aFrame->Combines3DTransformWithAncestors()) {
-    return IsStyleAnimated(aBuilder, aFrame->GetParent(), aProperty);
+    return IsStyleAnimated(aBuilder, aFrame->GetParent(), aPropertySet);
   }
-  return nsLayoutUtils::HasEffectiveAnimation(aFrame, aProperty);
+  return nsLayoutUtils::HasEffectiveAnimation(aFrame, aPropertySet);
 }
 
 /* static */ bool ActiveLayerTracker::IsOffsetStyleAnimated(nsIFrame* aFrame) {
   LayerActivity* layerActivity = GetLayerActivity(aFrame);
   if (layerActivity) {
     if (layerActivity->mRestyleCounts[LayerActivity::ACTIVITY_LEFT] >= 2 ||
         layerActivity->mRestyleCounts[LayerActivity::ACTIVITY_TOP] >= 2 ||
         layerActivity->mRestyleCounts[LayerActivity::ACTIVITY_RIGHT] >= 2 ||
--- a/layout/painting/ActiveLayerTracker.h
+++ b/layout/painting/ActiveLayerTracker.h
@@ -6,16 +6,17 @@
 
 #ifndef ACTIVELAYERTRACKER_H_
 #define ACTIVELAYERTRACKER_H_
 
 #include "nsCSSPropertyID.h"
 
 class nsIFrame;
 class nsIContent;
+class nsCSSPropertyIDSet;
 class nsDisplayListBuilder;
 class nsDOMCSSDeclaration;
 
 namespace mozilla {
 
 /**
  * This class receives various notifications about style changes and content
  * changes that affect layerization decisions, and implements the heuristics
@@ -23,17 +24,18 @@ namespace mozilla {
  * heuristics.
  */
 class ActiveLayerTracker {
  public:
   static void Shutdown();
 
   /*
    * We track style changes to selected styles:
-   *   eCSSProperty_transform
+   *   eCSSProperty_transform, eCSSProperty_translate,
+   *   eCSSProperty_rotate, eCSSProperty_scale
    *   eCSSProperty_opacity
    *   eCSSProperty_left, eCSSProperty_top,
    *   eCSSProperty_right, eCSSProperty_bottom
    * and use that information to guess whether style changes are animated.
    */
 
   /**
    * Notify aFrame's style property as having changed due to a restyle,
@@ -79,38 +81,44 @@ class ActiveLayerTracker {
   /**
    * Notify that a frame needs to be repainted. This is important for layering
    * decisions where, say, aFrame's transform is updated from JS, but we need
    * to repaint aFrame anyway, so we get no benefit from giving it its own
    * layer.
    */
   static void NotifyNeedsRepaint(nsIFrame* aFrame);
   /**
-   * Return true if aFrame's aProperty style should be considered as being
-   * animated for pre-rendering.
-   */
-  static bool IsStyleMaybeAnimated(nsIFrame* aFrame, nsCSSPropertyID aProperty);
-  /**
-   * Return true if aFrame's aProperty style should be considered as being
-   * animated for constructing active layers.
+   * Return true if aFrame's property style in |aPropertySet| should be
+   * considered as being animated for constructing active layers.
    */
   static bool IsStyleAnimated(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
-                              nsCSSPropertyID aProperty);
+                              const nsCSSPropertyIDSet& aPropertySet);
   /**
    * Return true if any of aFrame's offset property styles should be considered
    * as being animated for constructing active layers.
    */
   static bool IsOffsetStyleAnimated(nsIFrame* aFrame);
   /**
    * Return true if aFrame's background-position-x or background-position-y
    * property is animated.
    */
   static bool IsBackgroundPositionAnimated(nsDisplayListBuilder* aBuilder,
                                            nsIFrame* aFrame);
   /**
+   * Return true if aFrame's transform-like property,
+   * i.e. transform/translate/rotate/scale, is animated.
+   */
+  static bool IsTransformAnimated(nsDisplayListBuilder* aBuilder,
+                                  nsIFrame* aFrame);
+  /**
+   * Return true if aFrame's transform style should be considered as being
+   * animated for pre-rendering.
+   */
+  static bool IsTransformMaybeAnimated(nsIFrame* aFrame);
+  /**
    * Return true if aFrame either has an animated scale now, or is likely to
    * have one in the future because it has a CSS animation or transition
    * (which may not be playing right now) that affects its scale.
    */
   static bool IsScaleSubjectToAnimation(nsIFrame* aFrame);
 
   /**
    * Transfer the LayerActivity property to the frame's content node when the
--- a/layout/painting/FrameLayerBuilder.cpp
+++ b/layout/painting/FrameLayerBuilder.cpp
@@ -6157,18 +6157,18 @@ static bool ChooseScaleAndSetTransform(
   aLayer->SetBaseTransform(transform);
   aLayer->SetPreScale(1.0f / scale.width, 1.0f / scale.height);
   aLayer->SetInheritedScale(aIncomingScale.mXScale, aIncomingScale.mYScale);
 
   aOutgoingScale = ContainerLayerParameters(scale.width, scale.height, -offset,
                                             aIncomingScale);
   if (aTransform) {
     aOutgoingScale.mInTransformedSubtree = true;
-    if (ActiveLayerTracker::IsStyleAnimated(
-            aDisplayListBuilder, aContainerFrame, eCSSProperty_transform)) {
+    if (ActiveLayerTracker::IsTransformAnimated(aDisplayListBuilder,
+                                                aContainerFrame)) {
       aOutgoingScale.mInActiveTransformedSubtree = true;
     }
   }
   if ((aLayerBuilder->IsBuildingRetainedLayers() &&
        (!canDraw2D || transform2d.HasNonIntegerTranslation())) ||
       aContainerFrame->Extend3DContext() ||
       aContainerFrame->Combines3DTransformWithAncestors() ||
       // For async transform animation, the value would be changed at
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -5824,18 +5824,18 @@ static bool IsItemTooSmallForActiveLayer
                    gfxPrefs::LayoutMinActiveLayerSize());
 }
 
 /* static */ bool nsDisplayOpacity::NeedsActiveLayer(
     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
     bool aEnforceMinimumSize) {
   if (EffectCompositor::HasAnimationsForCompositor(aFrame,
                                                    eCSSProperty_opacity) ||
-      (ActiveLayerTracker::IsStyleAnimated(aBuilder, aFrame,
-                                           eCSSProperty_opacity) &&
+      (ActiveLayerTracker::IsStyleAnimated(
+           aBuilder, aFrame, nsCSSPropertyIDSet::OpacityProperties()) &&
        !(aEnforceMinimumSize && IsItemTooSmallForActiveLayer(aFrame)))) {
     return true;
   }
   return false;
 }
 
 void nsDisplayOpacity::ApplyOpacity(nsDisplayListBuilder* aBuilder,
                                     float aOpacity,
@@ -7597,18 +7597,18 @@ Matrix4x4 nsDisplayTransform::GetResulti
     nsLayoutUtils::PostTranslate(result, aOrigin, aAppUnitsPerPixel,
                                  !hasSVGTransforms);
   }
 
   return result;
 }
 
 bool nsDisplayOpacity::CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) {
-  if (ActiveLayerTracker::IsStyleAnimated(aBuilder, mFrame,
-                                          eCSSProperty_opacity)) {
+  if (ActiveLayerTracker::IsStyleAnimated(
+          aBuilder, mFrame, nsCSSPropertyIDSet::OpacityProperties())) {
     return true;
   }
 
   EffectCompositor::SetPerformanceWarning(
       mFrame, eCSSProperty_opacity,
       AnimationPerformanceWarning(
           AnimationPerformanceWarning::Type::OpacityFrameInactive));
 
@@ -7628,18 +7628,17 @@ bool nsDisplayBackgroundColor::CanUseAsy
 
 /* static */ auto nsDisplayTransform::ShouldPrerenderTransformedContent(
     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsRect* aDirtyRect)
     -> PrerenderDecision {
   // Elements whose transform has been modified recently, or which
   // have a compositor-animated transform, can be prerendered. An element
   // might have only just had its transform animated in which case
   // the ActiveLayerManager may not have been notified yet.
-  if (!ActiveLayerTracker::IsStyleMaybeAnimated(aFrame,
-                                                eCSSProperty_transform) &&
+  if (!ActiveLayerTracker::IsTransformMaybeAnimated(aFrame) &&
       !EffectCompositor::HasAnimationsForCompositor(aFrame,
                                                     eCSSProperty_transform)) {
     EffectCompositor::SetPerformanceWarning(
         aFrame, eCSSProperty_transform,
         AnimationPerformanceWarning(
             AnimationPerformanceWarning::Type::TransformFrameInactive));
 
     return NoPrerender;
@@ -7904,18 +7903,17 @@ bool nsDisplayTransform::CreateWebRender
     // UpdateScrollData call because that scenario is more complex. Otherwise
     // we can just stash the transform on the StackingContextHelper and
     // apply it to any scroll data that are created inside this
     // nsDisplayTransform.
     deferredTransformItem = Some(this);
   }
 
   // Determine if we're possibly animated (= would need an active layer in FLB).
-  bool animated =
-      ActiveLayerTracker::IsStyleMaybeAnimated(Frame(), eCSSProperty_transform);
+  bool animated = ActiveLayerTracker::IsTransformMaybeAnimated(Frame());
 
   wr::StackingContextParams params;
   params.mBoundTransform = &newTransformMatrix;
   params.animation = animationsId ? &prop : nullptr;
   params.mTransformPtr = transformForSC;
   params.is_backface_visible = !BackfaceIsHidden();
   params.mDeferredTransformItem = deferredTransformItem;
   params.mAnimated = animated;
@@ -8017,24 +8015,23 @@ already_AddRefed<Layer> nsDisplayTransfo
   return container.forget();
 }
 
 bool nsDisplayTransform::MayBeAnimated(nsDisplayListBuilder* aBuilder,
                                        bool aEnforceMinimumSize) const {
   // If EffectCompositor::HasAnimationsForCompositor() is true then we can
   // completely bypass the main thread for this animation, so it is always
   // worthwhile.
-  // For ActiveLayerTracker::IsStyleAnimated() cases the main thread is
+  // For ActiveLayerTracker::IsTransformAnimated() cases the main thread is
   // already involved so there is less to be gained.
   // Therefore we check that the *post-transform* bounds of this item are
   // big enough to justify an active layer.
   if (EffectCompositor::HasAnimationsForCompositor(mFrame,
                                                    eCSSProperty_transform) ||
-      (ActiveLayerTracker::IsStyleAnimated(aBuilder, mFrame,
-                                           eCSSProperty_transform) &&
+      (ActiveLayerTracker::IsTransformAnimated(aBuilder, mFrame) &&
        !(aEnforceMinimumSize && IsItemTooSmallForActiveLayer(mFrame)))) {
     return true;
   }
   return false;
 }
 
 nsDisplayItem::LayerState nsDisplayTransform::GetLayerState(
     nsDisplayListBuilder* aBuilder, LayerManager* aManager,