Bug 1526941 - Part 4: Remove mStoredList from nsDisplayTransform r=mattwoodrow
authorMiko Mynttinen <mikokm@gmail.com>
Thu, 07 Mar 2019 17:13:17 +0000
changeset 520853 c5a66f2c239f6bcadf8ec01954c31efcafa6b3c2
parent 520852 57c633e6cb449bcf77d27e38ed0301ea3fa04d86
child 520854 02fe822afa8c6252970f779dcbfd4a7b57fefd13
push id10862
push userffxbld-merge
push dateMon, 11 Mar 2019 13:01:11 +0000
treeherdermozilla-beta@a2e7f5c935da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow
bugs1526941
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 1526941 - Part 4: Remove mStoredList from nsDisplayTransform r=mattwoodrow Differential Revision: https://phabricator.services.mozilla.com/D21187
gfx/layers/wr/WebRenderCommandBuilder.cpp
gfx/layers/wr/WebRenderCommandBuilder.h
layout/painting/nsDisplayList.cpp
layout/painting/nsDisplayList.h
--- a/gfx/layers/wr/WebRenderCommandBuilder.cpp
+++ b/gfx/layers/wr/WebRenderCommandBuilder.cpp
@@ -1305,17 +1305,18 @@ void WebRenderCommandBuilder::DoGrouping
   int32_t appUnitsPerDevPixel =
       aWrappingItem->Frame()->PresContext()->AppUnitsPerDevPixel();
 
   g.mDisplayListBuilder = aDisplayListBuilder;
   RefPtr<WebRenderGroupData> groupData =
       CreateOrRecycleWebRenderUserData<WebRenderGroupData>(aWrappingItem);
 
   bool snapped;
-  nsRect groupBounds = aWrappingItem->GetBounds(aDisplayListBuilder, &snapped);
+  nsRect groupBounds =
+      aWrappingItem->GetUntransformedBounds(aDisplayListBuilder, &snapped);
   // We don't want to restrict the size of the blob to the building rect of the
   // display item, since that will change when we scroll and trigger a resize
   // invalidation of the blob (will be fixed by blob recoordination).
   // Instead we retrieve the bounds of the overflow clip on the <svg> and use
   // that to restrict our size and prevent invisible content from affecting
   // our bounds.
   if (mClippedGroupBounds) {
     groupBounds = groupBounds.Intersect(mClippedGroupBounds.value());
@@ -1377,22 +1378,25 @@ void WebRenderCommandBuilder::DoGrouping
   group.mGroupBounds = groupBounds;
   group.mAppUnitsPerDevPixel = appUnitsPerDevPixel;
   group.mLayerBounds = LayerIntRect::FromUnknownRect(
       ScaleToOutsidePixelsOffset(group.mGroupBounds, scale.width, scale.height,
                                  group.mAppUnitsPerDevPixel, residualOffset));
   group.mImageBounds =
       IntRect(0, 0, group.mLayerBounds.width, group.mLayerBounds.height);
   group.mClippedImageBounds = group.mImageBounds;
-  group.mPaintRect =
-      LayerIntRect::FromUnknownRect(
-          ScaleToOutsidePixelsOffset(aWrappingItem->GetPaintRect(), scale.width,
-                                     scale.height, group.mAppUnitsPerDevPixel,
-                                     residualOffset))
-          .Intersect(group.mLayerBounds);
+
+  const nsRect& untransformedPaintRect =
+      aWrappingItem->GetUntransformedPaintRect();
+
+  group.mPaintRect = LayerIntRect::FromUnknownRect(
+                         ScaleToOutsidePixelsOffset(
+                             untransformedPaintRect, scale.width, scale.height,
+                             group.mAppUnitsPerDevPixel, residualOffset))
+                         .Intersect(group.mLayerBounds);
   // XXX: Make the paint rect relative to the layer bounds. After we include
   // mLayerBounds.TopLeft() in the blob image we want to stop doing this
   // adjustment.
   group.mPaintRect = group.mPaintRect - group.mLayerBounds.TopLeft();
   g.mTransform = Matrix::Scaling(scale.width, scale.height)
                      .PostTranslate(residualOffset.x, residualOffset.y);
   group.mScale = scale;
   group.mScrollId = scrollId;
@@ -1495,18 +1499,17 @@ bool WebRenderCommandBuilder::ShouldDump
   return aBuilder != nullptr && aBuilder->IsInActiveDocShell() &&
          ((XRE_IsParentProcess() && gfxPrefs::WebRenderDLDumpParent()) ||
           (XRE_IsContentProcess() && gfxPrefs::WebRenderDLDumpContent()));
 }
 
 void WebRenderCommandBuilder::CreateWebRenderCommandsFromDisplayList(
     nsDisplayList* aDisplayList, nsDisplayItem* aWrappingItem,
     nsDisplayListBuilder* aDisplayListBuilder, const StackingContextHelper& aSc,
-    wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
-    nsDisplayItem* aOuterItem) {
+    wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources) {
   if (mDoGrouping) {
     MOZ_RELEASE_ASSERT(
         aWrappingItem,
         "Only the root list should have a null wrapping item, and mDoGrouping "
         "should never be true for the root list.");
     GP("actually entering the grouping code\n");
     DoGroupingForDisplayList(aDisplayList, aWrappingItem, aDisplayListBuilder,
                              aSc, aBuilder, aResources);
@@ -1606,25 +1609,25 @@ void WebRenderCommandBuilder::CreateWebR
     {  // scope restoreDoGrouping
       AutoRestore<bool> restoreDoGrouping(mDoGrouping);
       if (itemType == DisplayItemType::TYPE_SVG_WRAPPER) {
         // Inside an <svg>, all display items that are not LAYER_ACTIVE wrapper
         // display items (like animated transforms / opacity) share the same
         // animated geometry root, so we can combine subsequent items of that
         // type into the same image.
         mContainsSVGGroup = mDoGrouping = true;
-        if (aOuterItem &&
-            aOuterItem->GetType() == DisplayItemType::TYPE_TRANSFORM) {
+        if (aWrappingItem &&
+            aWrappingItem->GetType() == DisplayItemType::TYPE_TRANSFORM) {
           // Inline <svg> should always have an overflow clip, but it gets put
           // outside the nsDisplayTransform we create for scaling the svg
           // viewport. Converting the clip into inner coordinates lets us
           // restrict the size of the blob images and prevents unnecessary
           // resizes.
           nsDisplayTransform* transform =
-              static_cast<nsDisplayTransform*>(aOuterItem);
+              static_cast<nsDisplayTransform*>(aWrappingItem);
 
           nsRect clippedBounds =
               transform->GetClippedBounds(aDisplayListBuilder);
           nsRect innerClippedBounds;
           DebugOnly<bool> result = transform->UntransformRect(
               aDisplayListBuilder, clippedBounds, &innerClippedBounds);
           MOZ_ASSERT(result);
 
--- a/gfx/layers/wr/WebRenderCommandBuilder.h
+++ b/gfx/layers/wr/WebRenderCommandBuilder.h
@@ -90,18 +90,17 @@ class WebRenderCommandBuilder {
                        wr::IpcResourceUpdateQueue& aResources,
                        const StackingContextHelper& aSc,
                        nsDisplayListBuilder* aDisplayListBuilder);
 
   void CreateWebRenderCommandsFromDisplayList(
       nsDisplayList* aDisplayList, nsDisplayItem* aWrappingItem,
       nsDisplayListBuilder* aDisplayListBuilder,
       const StackingContextHelper& aSc, wr::DisplayListBuilder& aBuilder,
-      wr::IpcResourceUpdateQueue& aResources,
-      nsDisplayItem* aOuterItem = nullptr);
+      wr::IpcResourceUpdateQueue& aResources);
 
   // aWrappingItem has to be non-null.
   void DoGroupingForDisplayList(nsDisplayList* aDisplayList,
                                 nsDisplayItem* aWrappingItem,
                                 nsDisplayListBuilder* aDisplayListBuilder,
                                 const StackingContextHelper& aSc,
                                 wr::DisplayListBuilder& aBuilder,
                                 wr::IpcResourceUpdateQueue& aResources);
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -7305,73 +7305,74 @@ already_AddRefed<Layer> nsDisplayAsyncZo
 
   return layer.forget();
 }
 
 ///////////////////////////////////////////////////
 // nsDisplayTransform Implementation
 //
 
+#ifndef DEBUG
+static_assert(sizeof(nsDisplayTransform) < 512, "nsDisplayTransform has grown");
+#endif
+
 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
                                        nsIFrame* aFrame, nsDisplayList* aList,
                                        const nsRect& aChildrenBuildingRect,
                                        uint32_t aIndex)
     : nsDisplayHitTestInfoItem(aBuilder, aFrame),
-      mStoredList(aBuilder, aFrame, aList),
       mTransform(Some(Matrix4x4())),
       mTransformGetter(nullptr),
       mAnimatedGeometryRootForChildren(mAnimatedGeometryRoot),
       mAnimatedGeometryRootForScrollMetadata(mAnimatedGeometryRoot),
       mChildrenBuildingRect(aChildrenBuildingRect),
       mIndex(aIndex),
       mIsTransformSeparator(true),
       mAllowAsyncAnimation(false) {
   MOZ_COUNT_CTOR(nsDisplayTransform);
   MOZ_ASSERT(aFrame, "Must have a frame!");
-  Init(aBuilder);
+  Init(aBuilder, aList);
   UpdateBoundsFor3D(aBuilder);
 }
 
 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
                                        nsIFrame* aFrame, nsDisplayList* aList,
                                        const nsRect& aChildrenBuildingRect,
                                        uint32_t aIndex,
                                        bool aAllowAsyncAnimation)
     : nsDisplayHitTestInfoItem(aBuilder, aFrame),
-      mStoredList(aBuilder, aFrame, aList),
       mTransformGetter(nullptr),
       mAnimatedGeometryRootForChildren(mAnimatedGeometryRoot),
       mAnimatedGeometryRootForScrollMetadata(mAnimatedGeometryRoot),
       mChildrenBuildingRect(aChildrenBuildingRect),
       mIndex(aIndex),
       mIsTransformSeparator(false),
       mAllowAsyncAnimation(aAllowAsyncAnimation) {
   MOZ_COUNT_CTOR(nsDisplayTransform);
   MOZ_ASSERT(aFrame, "Must have a frame!");
   SetReferenceFrameToAncestor(aBuilder);
-  Init(aBuilder);
+  Init(aBuilder, aList);
   UpdateBoundsFor3D(aBuilder);
 }
 
 nsDisplayTransform::nsDisplayTransform(
     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
     const nsRect& aChildrenBuildingRect, uint32_t aIndex,
     ComputeTransformFunction aTransformGetter)
     : nsDisplayHitTestInfoItem(aBuilder, aFrame),
-      mStoredList(aBuilder, aFrame, aList),
       mTransformGetter(aTransformGetter),
       mAnimatedGeometryRootForChildren(mAnimatedGeometryRoot),
       mAnimatedGeometryRootForScrollMetadata(mAnimatedGeometryRoot),
       mChildrenBuildingRect(aChildrenBuildingRect),
       mIndex(aIndex),
       mIsTransformSeparator(false),
       mAllowAsyncAnimation(false) {
   MOZ_COUNT_CTOR(nsDisplayTransform);
   MOZ_ASSERT(aFrame, "Must have a frame!");
-  Init(aBuilder);
+  Init(aBuilder, aList);
 }
 
 void nsDisplayTransform::SetReferenceFrameToAncestor(
     nsDisplayListBuilder* aBuilder) {
   if (mFrame == aBuilder->RootReferenceFrame()) {
     return;
   }
   nsIFrame* outerFrame = nsLayoutUtils::GetCrossDocParentFrame(mFrame);
@@ -7400,21 +7401,22 @@ void nsDisplayTransform::SetReferenceFra
       // PaintedLayer as surrounding content.
       mAnimatedGeometryRoot = mAnimatedGeometryRoot->mParentAGR;
     }
   }
 
   SetBuildingRect(aBuilder->GetVisibleRect() + mToReferenceFrame);
 }
 
-void nsDisplayTransform::Init(nsDisplayListBuilder* aBuilder) {
+void nsDisplayTransform::Init(nsDisplayListBuilder* aBuilder,
+                              nsDisplayList* aChildren) {
   mShouldFlatten = false;
   mHasBounds = false;
-  mStoredList.SetClipChain(nullptr, true);
-  mStoredList.SetBuildingRect(mChildrenBuildingRect);
+  mChildren.AppendToTop(aChildren);
+  UpdateUntransformedBounds(aBuilder);
 }
 
 bool nsDisplayTransform::ShouldFlattenAway(nsDisplayListBuilder* aBuilder) {
   if (gfxVars::UseWebRender() || !gfxPrefs::LayoutFlattenTransform()) {
     return false;
   }
 
   MOZ_ASSERT(!mShouldFlatten);
@@ -8010,18 +8012,17 @@ bool nsDisplayTransform::CreateWebRender
   params.clip =
       wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
 
   StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
                            params,
                            LayoutDeviceRect(position, LayoutDeviceSize()));
 
   aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
-      mStoredList.GetChildren(), &mStoredList, aDisplayListBuilder, sc,
-      aBuilder, aResources, this);
+      GetChildren(), this, aDisplayListBuilder, sc, aBuilder, aResources);
   return true;
 }
 
 bool nsDisplayTransform::UpdateScrollData(
     mozilla::layers::WebRenderScrollData* aData,
     mozilla::layers::WebRenderLayerScrollData* aLayerData) {
   if (!mFrame->ChildrenHavePerspective()) {
     // This case is handled in CreateWebRenderCommands by stashing the transform
@@ -8053,18 +8054,18 @@ already_AddRefed<Layer> nsDisplayTransfo
    * function of nsDisplayTransform.
    */
   const Matrix4x4 newTransformMatrix =
       shouldSkipTransform ? Matrix4x4() : GetTransformForRendering();
 
   uint32_t flags = FrameLayerBuilder::CONTAINER_ALLOW_PULL_BACKGROUND_COLOR;
   RefPtr<ContainerLayer> container =
       aManager->GetLayerBuilder()->BuildContainerLayerFor(
-          aBuilder, aManager, mFrame, this, mStoredList.GetChildren(),
-          aContainerParameters, &newTransformMatrix, flags);
+          aBuilder, aManager, mFrame, this, GetChildren(), aContainerParameters,
+          &newTransformMatrix, flags);
 
   if (!container) {
     return nullptr;
   }
 
   // Add the preserve-3d flag for this layer, BuildContainerLayerFor clears all
   // flags, so we never need to explicitly unset this flag.
   if (mFrame->Extend3DContext() && !mIsTransformSeparator) {
@@ -8134,17 +8135,17 @@ nsDisplayItem::LayerState nsDisplayTrans
     return LAYER_ACTIVE_FORCE;
   }
 
   // Expect the child display items to have this frame as their animated
   // geometry root (since it will be their reference frame). If they have a
   // different animated geometry root, we'll make this an active layer so the
   // animation can be accelerated.
   return RequiredLayerStateForChildren(aBuilder, aManager, aParameters,
-                                       *mStoredList.GetChildren(),
+                                       *GetChildren(),
                                        mAnimatedGeometryRootForChildren);
 }
 
 bool nsDisplayTransform::ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                            nsRegion* aVisibleRegion) {
   // nsDisplayTransform::GetBounds() returns an empty rect in nested 3d context.
   // Calling mStoredList.RecomputeVisibility below for such transform causes the
   // child display items to end up with empty visible rect.
@@ -8156,34 +8157,67 @@ bool nsDisplayTransform::ComputeVisibili
   /* As we do this, we need to be sure to
    * untransform the visible rect, since we want everything that's painting to
    * think that it's painting in its original rectangular coordinate space.
    * If we can't untransform, take the entire overflow rect */
   nsRect untransformedVisibleRect;
   if (!UntransformPaintRect(aBuilder, &untransformedVisibleRect)) {
     untransformedVisibleRect = mFrame->GetVisualOverflowRectRelativeToSelf();
   }
-  nsRegion untransformedVisible = untransformedVisibleRect;
-  // Call RecomputeVisiblity instead of ComputeVisibility since
-  // nsDisplayItem::ComputeVisibility should only be called from
-  // nsDisplayList::ComputeVisibility (which sets mVisibleRect on the item)
-  mStoredList.RecomputeVisibility(aBuilder, &untransformedVisible);
+
+  bool snap;
+  const nsRect bounds = GetUntransformedBounds(aBuilder, &snap);
+  nsRegion visibleRegion;
+  visibleRegion.And(bounds, untransformedVisibleRect);
+  GetChildren()->ComputeVisibilityForSublist(aBuilder, &visibleRegion,
+                                             visibleRegion.GetBounds());
+
   return true;
 }
 
+void nsDisplayTransform::DoUpdateBoundsPreserves3D(
+    nsDisplayListBuilder* aBuilder) {
+  MOZ_ASSERT(mFrame->Combines3DTransformWithAncestors() ||
+             IsTransformSeparator());
+  // Updating is not going through to child 3D context.
+  ComputeBounds(aBuilder);
+}
+
+void nsDisplayTransform::UpdateBounds(nsDisplayListBuilder* aBuilder) {
+  mHasBounds = false;
+  UpdateUntransformedBounds(aBuilder, true);
+  UpdateBoundsFor3D(aBuilder);
+}
+
+void nsDisplayTransform::UpdateBoundsFor3D(nsDisplayListBuilder* aBuilder) {
+  if (!mFrame->Extend3DContext() ||
+      mFrame->Combines3DTransformWithAncestors() || IsTransformSeparator()) {
+    // Not an establisher of a 3D rendering context.
+    return;
+  }
+
+  // Always start updating from an establisher of a 3D rendering context.
+  nsDisplayListBuilder::AutoAccumulateRect accRect(aBuilder);
+  nsDisplayListBuilder::AutoAccumulateTransform accTransform(aBuilder);
+  accTransform.StartRoot();
+  ComputeBounds(aBuilder);
+  mBounds = aBuilder->GetAccumulatedRect();
+  mHasBounds = true;
+}
+
 #ifdef DEBUG_HIT
 #  include <time.h>
 #endif
 
 /* HitTest does some fun stuff with matrix transforms to obtain the answer. */
 void nsDisplayTransform::HitTest(nsDisplayListBuilder* aBuilder,
                                  const nsRect& aRect, HitTestState* aState,
                                  nsTArray<nsIFrame*>* aOutFrames) {
   if (aState->mInPreserves3D) {
-    mStoredList.HitTest(aBuilder, aRect, aState, aOutFrames);
+    GetChildren()->HitTest(aBuilder, aRect, aState, aOutFrames);
     return;
   }
 
   /* Here's how this works:
    * 1. Get the matrix.  If it's singular, abort (clearly we didn't hit
    *    anything).
    * 2. Invert the matrix.
    * 3. Use it to transform the rect into the correct space.
@@ -8222,17 +8256,17 @@ void nsDisplayTransform::HitTest(nsDispl
 
   } else {
     Rect originalRect(NSAppUnitsToFloatPixels(aRect.x, factor),
                       NSAppUnitsToFloatPixels(aRect.y, factor),
                       NSAppUnitsToFloatPixels(aRect.width, factor),
                       NSAppUnitsToFloatPixels(aRect.height, factor));
 
     bool snap;
-    nsRect childBounds = mStoredList.GetBounds(aBuilder, &snap);
+    nsRect childBounds = GetUntransformedBounds(aBuilder, &snap);
     Rect childGfxBounds(NSAppUnitsToFloatPixels(childBounds.x, factor),
                         NSAppUnitsToFloatPixels(childBounds.y, factor),
                         NSAppUnitsToFloatPixels(childBounds.width, factor),
                         NSAppUnitsToFloatPixels(childBounds.height, factor));
 
     Rect rect = matrix.ProjectRectBounds(originalRect, childGfxBounds);
 
     resultingRect =
@@ -8248,17 +8282,17 @@ void nsDisplayTransform::HitTest(nsDispl
 
 #ifdef DEBUG_HIT
   printf("Frame: %p\n", dynamic_cast<void*>(mFrame));
   printf("  Untransformed point: (%f, %f)\n", resultingRect.X(),
          resultingRect.Y());
   uint32_t originalFrameCount = aOutFrames.Length();
 #endif
 
-  mStoredList.HitTest(aBuilder, resultingRect, aState, aOutFrames);
+  GetChildren()->HitTest(aBuilder, resultingRect, aState, aOutFrames);
 
 #ifdef DEBUG_HIT
   if (originalFrameCount != aOutFrames.Length())
     printf("  Hit! Time: %f, first frame: %p\n", static_cast<double>(clock()),
            dynamic_cast<void*>(aOutFrames.ElementAt(0)));
   printf("=== end of hit test ===\n");
 #endif
 }
@@ -8294,17 +8328,18 @@ nsRect nsDisplayTransform::GetBounds(nsD
   if (mHasBounds) {
     return mBounds;
   }
 
   if (mFrame->Extend3DContext() && !mIsTransformSeparator) {
     return nsRect();
   }
 
-  nsRect untransformedBounds = mStoredList.GetBounds(aBuilder, aSnap);
+  bool snap;
+  nsRect untransformedBounds = GetUntransformedBounds(aBuilder, &snap);
   // GetTransform always operates in dev pixels.
   float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
   mBounds = nsLayoutUtils::MatrixTransformRect(untransformedBounds,
                                                GetTransform(), factor);
   mHasBounds = true;
   return mBounds;
 }
 
@@ -8320,25 +8355,27 @@ void nsDisplayTransform::ComputeBounds(n
    * nsDisplayListBuilder.
    */
   nsDisplayListBuilder::AutoAccumulateTransform accTransform(aBuilder);
 
   accTransform.Accumulate(GetTransform().GetMatrix());
 
   if (!IsLeafOf3DContext()) {
     // Do not dive into another 3D context.
-    mStoredList.DoUpdateBoundsPreserves3D(aBuilder);
+    for (nsDisplayItem* i = GetChildren()->GetBottom(); i; i = i->GetAbove()) {
+      i->DoUpdateBoundsPreserves3D(aBuilder);
+    }
   }
 
   /* For Preserves3D, it is bounds of only children as leaf frames.
    * For non-leaf frames, their bounds are accumulated and kept at
    * nsDisplayListBuilder.
    */
   bool snap;
-  nsRect untransformedBounds = mStoredList.GetBounds(aBuilder, &snap);
+  nsRect untransformedBounds = GetUntransformedBounds(aBuilder, &snap);
   // GetTransform always operates in dev pixels.
   float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
   nsRect rect = nsLayoutUtils::MatrixTransformRect(
       untransformedBounds, accTransform.GetCurrentTransform(), factor);
 
   aBuilder->AccumulateRect(rect);
 }
 
@@ -8356,34 +8393,50 @@ void nsDisplayTransform::ComputeBounds(n
  * our visible rect. We use UntransformRect which expands to the axis-aligned
  * bounding rect, but that's OK since if
  * mStoredList.GetVisibleRect().Contains(untransformedVisible), then it
  * certainly contains the actual (non-axis-aligned) untransformed rect.
  */
 nsRegion nsDisplayTransform::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                                              bool* aSnap) const {
   *aSnap = false;
+
   nsRect untransformedVisible;
   if (!UntransformBuildingRect(aBuilder, &untransformedVisible)) {
     return nsRegion();
   }
 
   const Matrix4x4Flagged& matrix = GetTransform();
+  Matrix matrix2d;
+  if (!matrix.Is2D(&matrix2d) || !matrix2d.PreservesAxisAlignedRectangles()) {
+    return nsRegion();
+  }
 
   nsRegion result;
-  Matrix matrix2d;
+
   bool tmpSnap;
-  if (matrix.Is2D(&matrix2d) && matrix2d.PreservesAxisAlignedRectangles() &&
-      mStoredList.GetOpaqueRegion(aBuilder, &tmpSnap)
-          .Contains(untransformedVisible)) {
+  const nsRect bounds = GetUntransformedBounds(aBuilder, &tmpSnap);
+  const nsRegion opaque = ::GetOpaqueRegion(aBuilder, GetChildren(), bounds);
+
+  if (opaque.Contains(untransformedVisible)) {
     result = GetBuildingRect().Intersect(GetBounds(aBuilder, &tmpSnap));
   }
   return result;
 }
 
+nsRect nsDisplayTransform::GetComponentAlphaBounds(
+    nsDisplayListBuilder* aBuilder) const {
+  if (GetChildren()->GetComponentAlphaBounds(aBuilder).IsEmpty()) {
+    return nsRect();
+  }
+
+  bool snap;
+  return GetBounds(aBuilder, &snap);
+}
+
 /* TransformRect takes in as parameters a rectangle (in app space) and returns
  * the smallest rectangle (in app space) containing the transformed image of
  * that rectangle.  That is, it takes the four corners of the rectangle,
  * transforms them according to the matrix associated with the specified frame,
  * then returns the smallest rectangle containing the four transformed points.
  *
  * @param aUntransformedBounds The rectangle (in app units) to transform.
  * @param aFrame The frame whose transformation should be applied.
@@ -8452,17 +8505,17 @@ bool nsDisplayTransform::UntransformRect
   // GetTransform always operates in dev pixels.
   float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
   RectDouble result(NSAppUnitsToFloatPixels(aRect.x, factor),
                     NSAppUnitsToFloatPixels(aRect.y, factor),
                     NSAppUnitsToFloatPixels(aRect.width, factor),
                     NSAppUnitsToFloatPixels(aRect.height, factor));
 
   bool snap;
-  nsRect childBounds = mStoredList.GetBounds(aBuilder, &snap);
+  nsRect childBounds = GetUntransformedBounds(aBuilder, &snap);
   RectDouble childGfxBounds(
       NSAppUnitsToFloatPixels(childBounds.x, factor),
       NSAppUnitsToFloatPixels(childBounds.y, factor),
       NSAppUnitsToFloatPixels(childBounds.width, factor),
       NSAppUnitsToFloatPixels(childBounds.height, factor));
 
   /* We want to untransform the matrix, so invert the transformation first! */
   result = GetInverseTransform().ProjectRectBounds(result, childGfxBounds);
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -2266,16 +2266,24 @@ class nsDisplayItem : public nsDisplayIt
    * @return a rectangle relative to aBuilder->ReferenceFrame() that
    * contains the area drawn by this display item
    */
   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const {
     *aSnap = false;
     return nsRect(ToReferenceFrame(), Frame()->GetSize());
   }
 
+  /**
+   * Returns the untransformed bounds of this display item.
+   */
+  virtual nsRect GetUntransformedBounds(nsDisplayListBuilder* aBuilder,
+                                        bool* aSnap) const {
+    return GetBounds(aBuilder, aSnap);
+  }
+
   virtual nsRegion GetTightBounds(nsDisplayListBuilder* aBuilder,
                                   bool* aSnap) const {
     *aSnap = false;
     return nsRegion();
   }
 
   /**
    * Returns true if nothing will be rendered inside aRect, false if uncertain.
@@ -2889,16 +2897,20 @@ class nsDisplayItem : public nsDisplayIt
       return false;
     }
     *aOutIndex = mOldListIndex;
     return true;
   }
 
   const nsRect& GetPaintRect() const { return mPaintRect; }
 
+  virtual const nsRect& GetUntransformedPaintRect() const {
+    return GetPaintRect();
+  }
+
   virtual bool HasHitTestInfo() const { return false; }
 
 #ifdef DEBUG
   virtual bool IsHitTestItem() const { return false; }
 #endif
 
  protected:
   typedef bool (*PrefFunc)(void);
@@ -6437,47 +6449,16 @@ class nsDisplayFilters : public nsDispla
  * function.
  * INVARIANT: The wrapped frame is non-null.
  */
 class nsDisplayTransform : public nsDisplayHitTestInfoItem {
   typedef mozilla::gfx::Matrix4x4 Matrix4x4;
   typedef mozilla::gfx::Matrix4x4Flagged Matrix4x4Flagged;
   typedef mozilla::gfx::Point3D Point3D;
 
-  /*
-   * Avoid doing UpdateBounds() during construction.
-   */
-  class StoreList : public nsDisplayWrapList {
-   public:
-    StoreList(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
-              nsDisplayList* aList)
-        : nsDisplayWrapList(aBuilder, aFrame, aList, true) {}
-
-    ~StoreList() override = default;
-
-    void UpdateBounds(nsDisplayListBuilder* aBuilder) override {
-      // For extending 3d rendering context, the bounds would be
-      // updated by DoUpdateBoundsPreserves3D(), not here.
-      if (!mFrame->Extend3DContext()) {
-        nsDisplayWrapList::UpdateBounds(aBuilder);
-      }
-    }
-
-    void ForceUpdateBounds(nsDisplayListBuilder* aBuilder) {
-      nsDisplayWrapList::UpdateBounds(aBuilder);
-    }
-
-    void DoUpdateBoundsPreserves3D(nsDisplayListBuilder* aBuilder) override {
-      for (nsDisplayItem* i = mList.GetBottom(); i; i = i->GetAbove()) {
-        i->DoUpdateBoundsPreserves3D(aBuilder);
-      }
-      nsDisplayWrapList::UpdateBounds(aBuilder);
-    }
-  };
-
  public:
   enum PrerenderDecision { NoPrerender, FullPrerender, PartialPrerender };
 
   /**
    * Returns a matrix (in pixels) for the current frame. The matrix should be
    * relative to the current frame's coordinate space.
    *
    * @param aFrame The frame to compute the transform for.
@@ -6505,51 +6486,56 @@ class nsDisplayTransform : public nsDisp
 #ifdef NS_BUILD_REFCNT_LOGGING
   ~nsDisplayTransform() override { MOZ_COUNT_DTOR(nsDisplayTransform); }
 #endif
 
   NS_DISPLAY_DECL_NAME("nsDisplayTransform", TYPE_TRANSFORM)
 
   void RestoreState() override { mShouldFlatten = false; }
 
-  void UpdateBounds(nsDisplayListBuilder* aBuilder) override {
-    mHasBounds = false;
-    if (IsTransformSeparator()) {
-      mStoredList.ForceUpdateBounds(aBuilder);
-      return;
-    }
-    mStoredList.UpdateBounds(aBuilder);
-    UpdateBoundsFor3D(aBuilder);
-  }
+  void UpdateBounds(nsDisplayListBuilder* aBuilder) override;
+
+  /**
+   * This function updates bounds for items with a frame establishing
+   * 3D rendering context.
+   */
+  void UpdateBoundsFor3D(nsDisplayListBuilder* aBuilder);
+
+  void DoUpdateBoundsPreserves3D(nsDisplayListBuilder* aBuilder) override;
 
   void Destroy(nsDisplayListBuilder* aBuilder) override {
-    mStoredList.GetChildren()->DeleteAll(aBuilder);
+    GetChildren()->DeleteAll(aBuilder);
     nsDisplayItem::Destroy(aBuilder);
   }
 
-  nsRect GetComponentAlphaBounds(
-      nsDisplayListBuilder* aBuilder) const override {
-    if (mStoredList.GetComponentAlphaBounds(aBuilder).IsEmpty())
-      return nsRect();
-    bool snap;
-    return GetBounds(aBuilder, &snap);
-  }
-
-  RetainedDisplayList* GetChildren() const override {
-    return mStoredList.GetChildren();
+  nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) const override;
+
+  RetainedDisplayList* GetChildren() const override { return &mChildren; }
+
+  nsRect GetUntransformedBounds(nsDisplayListBuilder* aBuilder,
+                                bool* aSnap) const override {
+    *aSnap = false;
+    return *mChildBounds;
+  }
+
+  const nsRect& GetUntransformedPaintRect() const override {
+    return mChildrenBuildingRect;
+  }
+
+  void UpdateUntransformedBounds(nsDisplayListBuilder* aBuilder,
+                                 bool aForce = false) {
+    if (!mChildBounds || aForce) {
+      mChildBounds =
+          mozilla::Some(GetChildren()->GetClippedBoundsWithRespectToASR(
+              aBuilder, mActiveScrolledRoot));
+    }
   }
 
   bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override;
 
-  void SetActiveScrolledRoot(
-      const ActiveScrolledRoot* aActiveScrolledRoot) override {
-    nsDisplayHitTestInfoItem::SetActiveScrolledRoot(aActiveScrolledRoot);
-    mStoredList.SetActiveScrolledRoot(aActiveScrolledRoot);
-  }
-
   void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
                HitTestState* aState, nsTArray<nsIFrame*>* aOutFrames) override;
   nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const override;
   nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                            bool* aSnap) const override;
   LayerState GetLayerState(
       nsDisplayListBuilder* aBuilder, LayerManager* aManager,
       const ContainerLayerParameters& aParameters) override;
@@ -6772,45 +6758,16 @@ class nsDisplayTransform : public nsDisp
       nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsRect* aDirtyRect);
   bool CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) override;
 
   bool MayBeAnimated(nsDisplayListBuilder* aBuilder,
                      bool aEnforceMinimumSize = true) const;
 
   void WriteDebugInfo(std::stringstream& aStream) override;
 
-  void DoUpdateBoundsPreserves3D(nsDisplayListBuilder* aBuilder) override {
-    MOZ_ASSERT(mFrame->Combines3DTransformWithAncestors() ||
-               IsTransformSeparator());
-    // Updating is not going through to child 3D context.
-    ComputeBounds(aBuilder);
-  }
-
-  /**
-   * This function updates bounds for items with a frame establishing
-   * 3D rendering context.
-   *
-   * \see nsDisplayItem::DoUpdateBoundsPreserves3D()
-   */
-  void UpdateBoundsFor3D(nsDisplayListBuilder* aBuilder) {
-    if (!mFrame->Extend3DContext() ||
-        mFrame->Combines3DTransformWithAncestors() || IsTransformSeparator()) {
-      // Not an establisher of a 3D rendering context.
-      return;
-    }
-    // Always start updating from an establisher of a 3D rendering context.
-
-    nsDisplayListBuilder::AutoAccumulateRect accRect(aBuilder);
-    nsDisplayListBuilder::AutoAccumulateTransform accTransform(aBuilder);
-    accTransform.StartRoot();
-    ComputeBounds(aBuilder);
-    mBounds = aBuilder->GetAccumulatedRect();
-    mHasBounds = true;
-  }
-
   /**
    * This item is an additional item as the boundary between parent
    * and child 3D rendering context.
    * \see nsIFrame::BuildDisplayListForStackingContext().
    */
   bool IsTransformSeparator() { return mIsTransformSeparator; }
   /**
    * This item is the boundary between parent and child 3D rendering
@@ -6825,43 +6782,44 @@ class nsDisplayTransform : public nsDisp
    * The backing frame of this item participates a 3D rendering
    * context.
    */
   bool IsParticipating3DContext() {
     return mFrame->Extend3DContext() ||
            mFrame->Combines3DTransformWithAncestors();
   }
 
-  void RemoveFrame(nsIFrame* aFrame) override {
-    nsDisplayItem::RemoveFrame(aFrame);
-    mStoredList.RemoveFrame(aFrame);
-  }
-
  private:
   void ComputeBounds(nsDisplayListBuilder* aBuilder);
   void SetReferenceFrameToAncestor(nsDisplayListBuilder* aBuilder);
-  void Init(nsDisplayListBuilder* aBuilder);
+  void Init(nsDisplayListBuilder* aBuilder, nsDisplayList* aChildren);
 
   static Matrix4x4 GetResultingTransformMatrixInternal(
       const FrameTransformProperties& aProperties, const nsPoint& aOrigin,
       float aAppUnitsPerPixel, uint32_t aFlags, const nsRect* aBoundsOverride);
 
-  StoreList mStoredList;
   mutable mozilla::Maybe<Matrix4x4Flagged> mTransform;
   mutable mozilla::Maybe<Matrix4x4Flagged> mInverseTransform;
   // Accumulated transform of ancestors on the preserves-3d chain.
   mozilla::UniquePtr<Matrix4x4> mTransformPreserves3D;
   ComputeTransformFunction mTransformGetter;
   RefPtr<AnimatedGeometryRoot> mAnimatedGeometryRootForChildren;
   RefPtr<AnimatedGeometryRoot> mAnimatedGeometryRootForScrollMetadata;
   nsRect mChildrenBuildingRect;
   uint32_t mIndex;
+
+  // The transformed bounds of this display item.
   mutable nsRect mBounds;
   // True for mBounds is valid.
   mutable bool mHasBounds;
+
+  mutable RetainedDisplayList mChildren;
+  // The untransformed bounds of |mChildren|.
+  mutable mozilla::Maybe<nsRect> mChildBounds;
+
   // This item is a separator between 3D rendering contexts, and
   // mTransform have been presetted by the constructor.
   // This also forces us not to extend the 3D context.  Since we don't create a
   // transform item, a container layer, for every frame in a preserves3d
   // context, the transform items of a child preserves3d context may extend the
   // parent context unintendedly if the root of the child preserves3d context
   // doesn't create a transform item.
   bool mIsTransformSeparator;