Bug 1205475 - Part 2. Prevent multiple EffectSet property look up. draft
authorcku <cku@mozilla.com>
Wed, 17 May 2017 15:13:12 +0800
changeset 579407 8fb8f4fe83a3724fc442ec55ff5486a1e5fdeb89
parent 579406 9450bbe584cebdf6583b0981a91f31c580082204
child 628998 36c3f1209ce1b9020864d856ae535648ae797c23
push id59242
push userbmo:cku@mozilla.com
push dateWed, 17 May 2017 09:43:26 +0000
bugs1205475
milestone55.0a1
Bug 1205475 - Part 2. Prevent multiple EffectSet property look up. Functions like BuildDisplayListForStackingContext or BuildDisplayListForChild look up EffectSet property several times in callees, such as IsTransformed() or HasOpacity(), which is time wasting. We sdould look up EffectSet just once, and pass the found one to all callees that need it. MozReview-Commit-ID: GZywm2UcpU7
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
layout/generic/nsFrame.cpp
layout/generic/nsIFrame.h
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -526,34 +526,40 @@ CheckOpacityAndTransformAnim(EffectSet* 
       !effects->MayHaveOpacityAnimation()) {
     return false;
   }
 
   return true;
 }
 
 bool
-nsLayoutUtils::HasAnimationOfProperty(const nsIFrame* aFrame,
+nsLayoutUtils::HasAnimationOfProperty(EffectSet* aEffectSet,
                                       nsCSSPropertyID aProperty)
 {
-  EffectSet* effects = EffectSet::GetEffectSet(aFrame);
-  if (!effects || !CheckOpacityAndTransformAnim(effects, aProperty)) {
+  if (!aEffectSet || !CheckOpacityAndTransformAnim(aEffectSet, aProperty)) {
     return false;
   }
 
-  return HasMatchingAnimations(effects,
+  return HasMatchingAnimations(aEffectSet,
     [&aProperty](KeyframeEffectReadOnly& aEffect)
     {
       return (aEffect.IsInEffect() || aEffect.IsCurrent()) &&
              aEffect.HasAnimationOfProperty(aProperty);
     }
   );
 }
 
 bool
+nsLayoutUtils::HasAnimationOfProperty(const nsIFrame* aFrame,
+                                      nsCSSPropertyID aProperty)
+{
+  return HasAnimationOfProperty(EffectSet::GetEffectSet(aFrame), aProperty);
+}
+
+bool
 nsLayoutUtils::HasEffectiveAnimation(const nsIFrame* aFrame,
                                      nsCSSPropertyID aProperty)
 {
   EffectSet* effects = EffectSet::GetEffectSet(aFrame);
   if (!effects || !CheckOpacityAndTransformAnim(effects, aProperty)) {
     return false;
   }
 
@@ -7142,17 +7148,19 @@ nsLayoutUtils::GetDisplayRootFrame(nsIFr
   }
 }
 
 /* static */ nsIFrame*
 nsLayoutUtils::GetReferenceFrame(nsIFrame* aFrame)
 {
   nsIFrame *f = aFrame;
   for (;;) {
-    if (f->IsTransformed() || f->IsPreserve3DLeaf() || IsPopup(f)) {
+    EffectSet* effectSet = EffectSet::GetEffectSet(aFrame);
+    if (f->IsTransformed(nullptr, effectSet) ||
+        f->IsPreserve3DLeaf(effectSet) || IsPopup(f)) {
       return f;
     }
     nsIFrame* parent = GetCrossDocParentFrame(f);
     if (!parent) {
       return f;
     }
     f = parent;
   }
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -66,16 +66,17 @@ struct nsOverflowAreas;
 namespace mozilla {
 enum class CSSPseudoElementType : uint8_t;
 class EventListenerManager;
 enum class LayoutFrameType : uint8_t;
 struct IntrinsicSize;
 struct ContainerLayerParameters;
 class WritingMode;
 class DisplayItemClip;
+class EffectSet;
 namespace dom {
 class CanvasRenderingContext2D;
 class DOMRectList;
 class Element;
 class HTMLImageElement;
 class HTMLCanvasElement;
 class HTMLVideoElement;
 class OffscreenCanvas;
@@ -2251,16 +2252,23 @@ public:
   /**
    * Returns true if |aFrame| has an animation of |aProperty| regardless of
    * whether the property is overridden by !important rule.
    */
   static bool HasAnimationOfProperty(const nsIFrame* aFrame,
                                      nsCSSPropertyID aProperty);
 
   /**
+   * Returns true if |aEffectSet| has an animation of |aProperty| regardless of
+   * whether the property is overridden by !important rule.
+   */
+  static bool HasAnimationOfProperty(mozilla::EffectSet* aEffectSet,
+                                     nsCSSPropertyID aProperty);
+
+  /**
    * Returns true if |aFrame| has an animation of |aProperty| which is
    * not overridden by !important rules.
    */
   static bool HasEffectiveAnimation(const nsIFrame* aFrame,
                                     nsCSSPropertyID aProperty);
 
   /**
    * Checks if off-main-thread animations are enabled.
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -1300,97 +1300,109 @@ nsIFrame::GetMarginRectRelativeToSelf() 
   nsMargin m = GetUsedMargin();
   m.ApplySkipSides(GetSkipSides());
   nsRect r(0, 0, mRect.width, mRect.height);
   r.Inflate(m);
   return r;
 }
 
 bool
-nsIFrame::IsTransformed(const nsStyleDisplay* aStyleDisplay) const
+nsIFrame::IsTransformed(const nsStyleDisplay* aStyleDisplay,
+                        EffectSet* aEffectSet) const
 {
   return ((mState & NS_FRAME_MAY_BE_TRANSFORMED) &&
           (StyleDisplayWithOptionalParam(aStyleDisplay)->HasTransform(this) ||
            IsSVGTransformed() ||
-           HasAnimationOfTransform()));
+           HasAnimationOfTransform(aEffectSet)));
 }
 
 bool
-nsIFrame::HasAnimationOfTransform() const
-{
+nsIFrame::HasAnimationOfTransform(EffectSet* aEffectSet) const
+{
+  EffectSet* effects =
+    aEffectSet ? aEffectSet : EffectSet::GetEffectSet(this);
+
   return mContent &&
-    nsLayoutUtils::HasAnimationOfProperty(this, eCSSProperty_transform) &&
+    nsLayoutUtils::HasAnimationOfProperty(effects, eCSSProperty_transform) &&
     IsFrameOfType(eSupportsCSSTransforms) &&
     mContent->GetPrimaryFrame() == this;
 }
 
 bool
-nsIFrame::HasOpacityInternal(float aThreshold) const
+nsIFrame::HasOpacityInternal(float aThreshold,
+                             EffectSet* aEffectSet) const
 {
   MOZ_ASSERT(0.0 <= aThreshold && aThreshold <= 1.0, "Invalid argument");
-  return StyleEffects()->mOpacity < aThreshold ||
-         (StyleDisplay()->mWillChangeBitField & NS_STYLE_WILL_CHANGE_OPACITY) ||
-         (mContent &&
-           nsLayoutUtils::HasAnimationOfProperty(this, eCSSProperty_opacity) &&
-           mContent->GetPrimaryFrame() == this);
+  if (StyleEffects()->mOpacity < aThreshold ||
+      (StyleDisplay()->mWillChangeBitField & NS_STYLE_WILL_CHANGE_OPACITY)) {
+    return true;
+  }
+
+  EffectSet* effects =
+    aEffectSet ? aEffectSet : EffectSet::GetEffectSet(this);
+  return (mContent && mContent->GetPrimaryFrame() == this &&
+          nsLayoutUtils::HasAnimationOfProperty(effects, eCSSProperty_opacity));
 }
 
 bool
 nsIFrame::IsSVGTransformed(gfx::Matrix *aOwnTransforms,
                            gfx::Matrix *aFromParentTransforms) const
 {
   return false;
 }
 
 bool
-nsIFrame::Extend3DContext() const
+nsIFrame::Extend3DContext(mozilla::EffectSet* aEffectSet) const
 {
   const nsStyleDisplay* disp = StyleDisplay();
   if (disp->mTransformStyle != NS_STYLE_TRANSFORM_STYLE_PRESERVE_3D ||
       !IsFrameOfType(nsIFrame::eSupportsCSSTransforms)) {
     return false;
   }
 
   // If we're all scroll frame, then all descendants will be clipped, so we can't preserve 3d.
   if (IsScrollFrame()) {
     return false;
   }
 
-  if (HasOpacity()) {
+  if (HasOpacity(aEffectSet)) {
     return false;
   }
 
   const nsStyleEffects* effects = StyleEffects();
   return !nsFrame::ShouldApplyOverflowClipping(this, disp) &&
          !GetClipPropClipRect(disp, effects, GetSize()) &&
          !nsSVGIntegrationUtils::UsingEffectsForFrame(this);
 }
 
 bool
-nsIFrame::Combines3DTransformWithAncestors(const nsStyleDisplay* aStyleDisplay) const
-{
-  if (!GetParent() || !GetParent()->Extend3DContext()) {
+nsIFrame::Combines3DTransformWithAncestors(const nsStyleDisplay* aStyleDisplay,
+                                           EffectSet* aEffectSet) const
+{
+  if (!GetParent() || !GetParent()->Extend3DContext(aEffectSet)) {
     return false;
   }
-  return IsTransformed(aStyleDisplay) || BackfaceIsHidden(aStyleDisplay);
+  return IsTransformed(aStyleDisplay,aEffectSet) ||
+         BackfaceIsHidden(aStyleDisplay);
 }
 
 bool
-nsIFrame::In3DContextAndBackfaceIsHidden() const
+nsIFrame::In3DContextAndBackfaceIsHidden(EffectSet* aEffectSet) const
 {
   // While both tests fail most of the time, test BackfaceIsHidden()
   // first since it's likely to fail faster.
   const nsStyleDisplay* disp = StyleDisplay();
-  return BackfaceIsHidden(disp) && Combines3DTransformWithAncestors(disp);
+  return BackfaceIsHidden(disp) &&
+         Combines3DTransformWithAncestors(disp, aEffectSet);
 }
 
 bool
-nsIFrame::HasPerspective() const
-{
-  if (!IsTransformed()) {
+nsIFrame::HasPerspective(EffectSet* aEffectSet) const
+{
+  if (!IsTransformed(nullptr, aEffectSet)) {
     return false;
   }
   nsIFrame* containingBlock = GetContainingBlock(SKIP_SCROLLED_FRAME);
   if (!containingBlock) {
     return false;
   }
   return containingBlock->ChildrenHavePerspective();
 }
@@ -2346,59 +2358,61 @@ nsIFrame::BuildDisplayListForStackingCon
 
   // Replaced elements have their visibility handled here, because
   // they're visually atomic
   if (IsFrameOfType(eReplaced) && !IsVisibleForPainting(aBuilder))
     return;
 
   const nsStyleDisplay* disp = StyleDisplay();
   const nsStyleEffects* effects = StyleEffects();
+  EffectSet* effectSet = EffectSet::GetEffectSet(this);
   // We can stop right away if this is a zero-opacity stacking context and
   // we're painting, and we're not animating opacity. Don't do this
   // if we're going to compute plugin geometry, since opacity-0 plugins
   // need to have display items built for them.
   bool needEventRegions =
     aBuilder->IsBuildingLayerEventRegions() &&
     StyleUserInterface()->GetEffectivePointerEvents(this) !=
       NS_STYLE_POINTER_EVENTS_NONE;
   bool opacityItemForEventsAndPluginsOnly = false;
   if (effects->mOpacity == 0.0 && aBuilder->IsForPainting() &&
       !(disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_OPACITY) &&
-      !nsLayoutUtils::HasAnimationOfProperty(this, eCSSProperty_opacity)) {
+      !nsLayoutUtils::HasAnimationOfProperty(effectSet, eCSSProperty_opacity)) {
     if (needEventRegions ||
         aBuilder->WillComputePluginGeometry()) {
       opacityItemForEventsAndPluginsOnly = true;
     } else {
       return;
     }
   }
 
   if (disp->mWillChangeBitField != 0) {
     aBuilder->AddToWillChangeBudget(this, GetSize());
   }
 
-  bool extend3DContext = Extend3DContext();
+
+  bool extend3DContext = Extend3DContext(effectSet);
   Maybe<nsDisplayListBuilder::AutoPreserves3DContext> autoPreserves3DContext;
   if (extend3DContext && !Combines3DTransformWithAncestors()) {
     // Start a new preserves3d context to keep informations on
     // nsDisplayListBuilder.
     autoPreserves3DContext.emplace(aBuilder);
     // Save dirty rect on the builder to avoid being distorted for
     // multiple transforms along the chain.
     aBuilder->SetPreserves3DDirtyRect(aDirtyRect);
   }
 
   // For preserves3d, use the dirty rect already installed on the
   // builder, since aDirtyRect maybe distorted for transforms along
   // the chain.
   nsRect dirtyRect = aDirtyRect;
 
   bool inTransform = aBuilder->IsInTransform();
-  bool isTransformed = IsTransformed(disp);
-  bool hasPerspective = HasPerspective();
+  bool isTransformed = IsTransformed(disp, effectSet);
+  bool hasPerspective = HasPerspective(effectSet);
   // reset blend mode so we can keep track if this stacking context needs have
   // a nsDisplayBlendContainer. Set the blend mode back when the routine exits
   // so we keep track if the parent stacking context needs a container too.
   AutoSaveRestoreContainsBlendMode autoRestoreBlendMode(*aBuilder);
   aBuilder->SetContainsBlendMode(false);
  
   nsRect dirtyRectOutsideTransform = dirtyRect;
   bool allowAsyncAnimation = false;
@@ -2449,17 +2463,18 @@ nsIFrame::BuildDisplayListForStackingCon
     aBuilder->EnterSVGEffectsContents(&hoistedScrollInfoItemsStorage);
   }
 
   // We build an opacity item if it's not going to be drawn by SVG content, or
   // SVG effects. SVG effects won't handle the opacity if we want an active
   // layer (for async animations), see
   // nsSVGIntegrationsUtils::PaintMaskAndClipPath or
   // nsSVGIntegrationsUtils::PaintFilter.
-  bool useOpacity = HasVisualOpacity() && !nsSVGUtils::CanOptimizeOpacity(this) &&
+  bool useOpacity = HasVisualOpacity(effectSet) &&
+                    !nsSVGUtils::CanOptimizeOpacity(this) &&
                     (!usingSVGEffects || nsDisplayOpacity::NeedsActiveLayer(aBuilder, this));
   bool useBlendMode = effects->mMixBlendMode != NS_STYLE_BLEND_NORMAL;
   bool useStickyPosition = disp->mPosition == NS_STYLE_POSITION_STICKY &&
     IsScrollFrameActive(aBuilder,
                         nsLayoutUtils::GetNearestScrollableFrame(GetParent(),
                         nsLayoutUtils::SCROLLABLE_SAME_DOC |
                         nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN));
   bool useFixedPosition = disp->mPosition == NS_STYLE_POSITION_FIXED &&
@@ -3047,17 +3062,16 @@ nsIFrame::BuildDisplayListForChild(nsDis
       (child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
     dirty = child->GetVisualOverflowRect();
     awayFromCommonPath = true;
   } else if (!DescendIntoChild(aBuilder, child, dirty)) {
     return;
   }
 
   // XXX need to have inline-block and inline-table set pseudoStackingContext
-  
   const nsStyleDisplay* ourDisp = StyleDisplay();
   // REVIEW: Taken from nsBoxFrame::Paint
   // Don't paint our children if the theme object is a leaf.
   if (IsThemed(ourDisp) &&
       !PresContext()->GetTheme()->WidgetIsContainer(ourDisp->UsedAppearance()))
     return;
 
   // Since we're now sure that we're adding this frame to the display list
@@ -3065,21 +3079,22 @@ nsIFrame::BuildDisplayListForChild(nsDis
   // within the displayport.
   if (aBuilder->IsPaintingToWindow() && child->TrackingVisibility()) {
     child->PresContext()->PresShell()->EnsureFrameInApproximatelyVisibleList(child);
     awayFromCommonPath = true;
   }
 
   // Child is composited if it's transformed, partially transparent, or has
   // SVG effects or a blend mode..
+  EffectSet* effectSet = EffectSet::GetEffectSet(this);
   const nsStyleDisplay* disp = child->StyleDisplay();
   const nsStyleEffects* effects = child->StyleEffects();
   const nsStylePosition* pos = child->StylePosition();
-  bool isVisuallyAtomic = child->HasOpacity()
-    || child->IsTransformed(disp)
+  bool isVisuallyAtomic = child->HasOpacity(effectSet)
+    || child->IsTransformed(disp, effectSet)
     // strictly speaking, 'perspective' doesn't require visual atomicity,
     // but the spec says it acts like the rest of these
     || disp->mChildPerspective.GetUnit() == eStyleUnit_Coord
     || effects->mMixBlendMode != NS_STYLE_BLEND_NORMAL
     || nsSVGIntegrationUtils::UsingEffectsForFrame(child);
 
   bool isPositioned = disp->IsAbsPosContainingBlock(child);
   bool isStackingContext =
@@ -8956,17 +8971,18 @@ ComputeAndIncludeOutlineArea(nsIFrame* a
 bool
 nsIFrame::FinishAndStoreOverflow(nsOverflowAreas& aOverflowAreas,
                                  nsSize aNewSize, nsSize* aOldSize)
 {
   MOZ_ASSERT(FrameMaintainsOverflow(),
              "Don't call - overflow rects not maintained on these SVG frames");
 
   const nsStyleDisplay* disp = StyleDisplay();
-  bool hasTransform = IsTransformed(disp);
+  EffectSet* effectSet = EffectSet::GetEffectSet(this);
+  bool hasTransform = IsTransformed(disp, effectSet);
 
   nsRect bounds(nsPoint(0, 0), aNewSize);
   // Store the passed in overflow area if we are a preserve-3d frame or we have
   // a transform, and it's not just the frame bounds.
   if (hasTransform || Combines3DTransformWithAncestors(disp)) {
     if (!aOverflowAreas.VisualOverflow().IsEqualEdges(bounds) ||
         !aOverflowAreas.ScrollableOverflow().IsEqualEdges(bounds)) {
       nsOverflowAreas* initial =
@@ -9069,17 +9085,17 @@ nsIFrame::FinishAndStoreOverflow(nsOverf
   /* Since our size might not actually have been computed yet, we need to make sure that we use the
    * correct dimensions by overriding the stored bounding rectangle with the value the caller has
    * ensured us we'll use.
    */
   SetSize(aNewSize);
 
   if (ChildrenHavePerspective() && sizeChanged) {
     nsRect newBounds(nsPoint(0, 0), aNewSize);
-    RecomputePerspectiveChildrenOverflow(this);
+    RecomputePerspectiveChildrenOverflow(this, effectSet);
   }
 
   if (hasTransform) {
     Properties().Set(nsIFrame::PreTransformOverflowAreasProperty(),
                      new nsOverflowAreas(aOverflowAreas));
 
     if (Combines3DTransformWithAncestors()) {
       /* If we're a preserve-3d leaf frame, then our pre-transform overflow should be correct. Our
@@ -9094,17 +9110,17 @@ nsIFrame::FinishAndStoreOverflow(nsOverf
         nsRect& o = aOverflowAreas.Overflow(otype);
         o = nsDisplayTransform::TransformRect(o, this);
       }
 
       /* If we're the root of the 3d context, then we want to include the overflow areas of all
        * the participants. This won't have happened yet as the code above set their overflow
        * area to empty. Manually collect these overflow areas now.
        */
-      if (Extend3DContext()) {
+      if (Extend3DContext(effectSet)) {
         ComputePreserve3DChildrenOverflow(aOverflowAreas);
       }
     }
   } else {
     Properties().Delete(nsIFrame::PreTransformOverflowAreasProperty());
   }
 
   /* Revert the size change in case some caller is depending on this. */
@@ -9119,27 +9135,28 @@ nsIFrame::FinishAndStoreOverflow(nsOverf
 
   if (anyOverflowChanged) {
     nsSVGEffects::InvalidateDirectRenderingObservers(this);
   }
   return anyOverflowChanged;
 }
 
 void
-nsIFrame::RecomputePerspectiveChildrenOverflow(const nsIFrame* aStartFrame)
+nsIFrame::RecomputePerspectiveChildrenOverflow(const nsIFrame* aStartFrame,
+                                               EffectSet* aEffectSet)
 {
   nsIFrame::ChildListIterator lists(this);
   for (; !lists.IsDone(); lists.Next()) {
     nsFrameList::Enumerator childFrames(lists.CurrentList());
     for (; !childFrames.AtEnd(); childFrames.Next()) {
       nsIFrame* child = childFrames.get();
       if (!child->FrameMaintainsOverflow()) {
         continue; // frame does not maintain overflow rects
       }
-      if (child->HasPerspective()) {
+      if (child->HasPerspective(aEffectSet)) {
         nsOverflowAreas* overflow = 
           child->Properties().Get(nsIFrame::InitialOverflowProperty());
         nsRect bounds(nsPoint(0, 0), child->GetSize());
         if (overflow) {
           nsOverflowAreas overflowCopy = *overflow;
           child->FinishAndStoreOverflow(overflowCopy, bounds.Size());
         } else {
           nsOverflowAreas boundsOverflow;
@@ -9147,17 +9164,17 @@ nsIFrame::RecomputePerspectiveChildrenOv
           child->FinishAndStoreOverflow(boundsOverflow, bounds.Size());
         }
       } else if (child->GetContainingBlock(SKIP_SCROLLED_FRAME) == aStartFrame) {
         // If a frame is using perspective, then the size used to compute
         // perspective-origin is the size of the frame belonging to its parent
         // style context. We must find any descendant frames using our size
         // (by recursing into frames that have the same containing block)
         // to update their overflow rects too.
-        child->RecomputePerspectiveChildrenOverflow(aStartFrame);
+        child->RecomputePerspectiveChildrenOverflow(aStartFrame, aEffectSet);
       }
     }
   }
 }
 
 void
 nsIFrame::ComputePreserve3DChildrenOverflow(nsOverflowAreas& aOverflowAreas)
 {
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -95,16 +95,17 @@ struct CharacterDataChangeInfo;
 namespace mozilla {
 
 enum class CSSPseudoElementType : uint8_t;
 class EventStates;
 struct ReflowInput;
 class ReflowOutput;
 class ServoStyleSet;
 class DisplayItemData;
+class EffectSet;
 
 namespace layers {
 class Layer;
 } // namespace layers
 
 namespace gfx {
 class Matrix;
 } // namespace gfx
@@ -1665,41 +1666,57 @@ public:
   /**
    * Returns true if this frame is transformed (e.g. has CSS or SVG transforms)
    * or if its parent is an SVG frame that has children-only transforms (e.g.
    * an SVG viewBox attribute) or if its transform-style is preserve-3d or
    * the frame has transform animations.
    *
    * @param aStyleDisplay:  If the caller has this->StyleDisplay(), providing
    *   it here will improve performance.
+   * @param aEffectSet: This function may need to look up EffectSet property.
+   *   If you alreasdy have one, pass it in can save property look up time;
+   *   otherwise, just left it as nullptr.
    */
-  bool IsTransformed(const nsStyleDisplay* aStyleDisplay = nullptr) const;
+  bool IsTransformed(const nsStyleDisplay* aStyleDisplay = nullptr,
+                     mozilla::EffectSet* aEffectSet = nullptr) const;
 
   /**
    * True if this frame has any animation of transform in effect.
+   *
+   * @param aEffectSet: This function may need to look up EffectSet property.
+   *   If you alreasdy have one, pass it in can save property look up time;
+   *   otherwise, just left it as nullptr.
    */
-  bool HasAnimationOfTransform() const;
+  bool HasAnimationOfTransform(mozilla::EffectSet* aEffectSet = nullptr) const;
 
   /**
    * Returns true if the frame is translucent or the frame has opacity
    * animations for the purposes of creating a stacking context.
+   *
+   * @param aEffectSet: This function may need to look up EffectSet property.
+   *   If you alreasdy have one, pass it in can save property look up time;
+   *   otherwise, just left it as nullptr.
    */
-  bool HasOpacity() const
+  bool HasOpacity(mozilla::EffectSet* aEffectSet = nullptr) const
   {
-    return HasOpacityInternal(1.0f);
+    return HasOpacityInternal(1.0f, aEffectSet);
   }
   /**
    * Returns true if the frame is translucent for display purposes.
+   *
+   * @param aEffectSet: This function may need to look up EffectSet property.
+   *   If you alreasdy have one, pass it in can save property look up time;
+   *   otherwise, just left it as nullptr.
    */
-  bool HasVisualOpacity() const
+  bool HasVisualOpacity(mozilla::EffectSet* aEffectSet = nullptr) const
   {
     // Treat an opacity value of 0.99 and above as opaque.  This is an
     // optimization aimed at Web content which use opacity:0.99 as a hint for
     // creating a stacking context only.
-    return HasOpacityInternal(0.99f);
+    return HasOpacityInternal(0.99f, aEffectSet);
   }
 
    /**
    * Return true if this frame might be using a transform getter.
    */
   virtual bool HasTransformGetter() const { return false; }
 
   /**
@@ -1713,52 +1730,65 @@ public:
    */
   virtual bool IsSVGTransformed(Matrix *aOwnTransforms = nullptr,
                                 Matrix *aFromParentTransforms = nullptr) const;
 
   /**
    * Returns whether this frame will attempt to extend the 3d transforms of its
    * children. This requires transform-style: preserve-3d, as well as no clipping
    * or svg effects.
+   *
+   * @param aEffectSet: This function may need to look up EffectSet property.
+   *   If you alreasdy have one, pass it in can save property look up time;
+   *   otherwise, just left it as nullptr.
    */
-  bool Extend3DContext() const;
+  bool Extend3DContext(mozilla::EffectSet* aEffectSet = nullptr) const;
 
   /**
    * Returns whether this frame has a parent that Extend3DContext() and has
    * its own transform (or hidden backface) to be combined with the parent's
    * transform.
    *
    * @param aStyleDisplay:  If the caller has this->StyleDisplay(), providing
    *   it here will improve performance.
+   * @param aEffectSet: This function may need to look up EffectSet property.
+   *   If you alreasdy have one, pass it in can save property look up time;
+   *   otherwise, just left it as nullptr.
    */
   bool Combines3DTransformWithAncestors(const nsStyleDisplay* aStyleDisplay
-                                          = nullptr) const;
+                                          = nullptr,
+                                        mozilla::EffectSet* aEffectSet = nullptr) const;
 
   /**
    * Returns whether this frame has a hidden backface and has a parent that
    * Extend3DContext(). This is useful because in some cases the hidden
    * backface can safely be ignored if it could not be visible anyway.
+   *
+   * @param aEffectSet: This function may need to look up EffectSet property.
+   *   If you alreasdy have one, pass it in can save property look up time;
+   *   otherwise, just left it as nullptr.
    */
-  bool In3DContextAndBackfaceIsHidden() const;
-
-  bool IsPreserve3DLeaf() const {
-    return Combines3DTransformWithAncestors() && !Extend3DContext();
+  bool In3DContextAndBackfaceIsHidden(mozilla::EffectSet* aEffectSet = nullptr) const;
+
+  bool IsPreserve3DLeaf(mozilla::EffectSet* aEffectSet = nullptr) const {
+    return Combines3DTransformWithAncestors() && !Extend3DContext(aEffectSet);
   }
 
-  bool HasPerspective() const;
+  bool HasPerspective(mozilla::EffectSet* aEffectSet = nullptr) const;
 
   bool ChildrenHavePerspective() const;
 
   /**
    * Includes the overflow area of all descendants that participate in the current
    * 3d context into aOverflowAreas.
    */
   void ComputePreserve3DChildrenOverflow(nsOverflowAreas& aOverflowAreas);
 
-  void RecomputePerspectiveChildrenOverflow(const nsIFrame* aStartFrame);
+  void RecomputePerspectiveChildrenOverflow(const nsIFrame* aStartFrame,
+                                            mozilla::EffectSet* aEffectSet = nullptr);
 
   /**
    * Returns the number of ancestors between this and the root of our frame tree
    */
   uint32_t GetDepthInFrameTree() const;
 
   /**
    * Event handling of GUI events.
@@ -3934,17 +3964,18 @@ private:
 
   // Helper-functions for SortFrameList():
   template<bool IsLessThanOrEqual(nsIFrame*, nsIFrame*)>
   static nsIFrame* SortedMerge(nsIFrame *aLeft, nsIFrame *aRight);
 
   template<bool IsLessThanOrEqual(nsIFrame*, nsIFrame*)>
   static nsIFrame* MergeSort(nsIFrame *aSource);
 
-  bool HasOpacityInternal(float aThreshold) const;
+  bool HasOpacityInternal(float aThreshold,
+                          mozilla::EffectSet* aEffectSet = nullptr) const;
 
 #ifdef DEBUG_FRAME_DUMP
 public:
   static void IndentBy(FILE* out, int32_t aIndent) {
     while (--aIndent >= 0) fputs("  ", out);
   }
   void ListTag(FILE* out) const {
     ListTag(out, this);