Backed out 10 changesets (bug 1714138, bug 1542929, bug 1728232, bug 1729236, bug 1728258, bug 1728251, bug 1728050) for causing bug 1424348 a=backout
authorcriss <ccozmuta@mozilla.com>
Sun, 12 Sep 2021 12:00:30 +0300
changeset 591686 e8e345cec68d8e71ba6f6f3c5ff2fe33605c670f
parent 591685 64212702d9f0d6410362b209dcf0348cb3acb452
child 591689 5b94b4dcc30548cee96599f85c5b6d377e7b1955
push id38781
push userccozmuta@mozilla.com
push dateSun, 12 Sep 2021 09:01:01 +0000
treeherdermozilla-central@e8e345cec68d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbackout
bugs1714138, 1542929, 1728232, 1729236, 1728258, 1728251, 1728050, 1424348
milestone94.0a1
backs outc5b71e6ce0e5a9711f5cb682022e6d549a063257
c6bcc4ed3d2e89ab094502c17070cf9ab2554f19
7e292895282a6e0253ea6c15295a147e95d4eccb
d9ddd915e0c245d32e3b4abda28a5a4ae2edd632
82b98d2f0dcf08b5a245a8f35d4a18bc74bd342a
9a84a36b9dc48c27bb9ca7a4e26fd0fbf0051648
96be978630ff0fede5e558468fdf13d16ff8c4a9
d7a8bf19d8494c321a4bac10e9a4229b10a6c029
cce0c53b439fee638aa821a1e44547719807d9b1
3afd6aee7849b2a2fcc0feea0d2dd4257b2202ee
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
Backed out 10 changesets (bug 1714138, bug 1542929, bug 1728232, bug 1729236, bug 1728258, bug 1728251, bug 1728050) for causing bug 1424348 a=backout Backed out changeset c5b71e6ce0e5 (bug 1729236) Backed out changeset c6bcc4ed3d2e (bug 1729236) Backed out changeset 7e292895282a (bug 1729236) Backed out changeset d9ddd915e0c2 (bug 1714138) Backed out changeset 82b98d2f0dcf (bug 1728258) Backed out changeset 9a84a36b9dc4 (bug 1542929) Backed out changeset 96be978630ff (bug 1728251) Backed out changeset d7a8bf19d849 (bug 1728251) Backed out changeset cce0c53b439f (bug 1728232) Backed out changeset 3afd6aee7849 (bug 1728050)
gfx/layers/wr/ClipManager.cpp
gfx/layers/wr/DisplayItemCache.cpp
gfx/layers/wr/DisplayItemCache.h
gfx/layers/wr/WebRenderCommandBuilder.cpp
gfx/layers/wr/WebRenderCommandBuilder.h
gfx/layers/wr/WebRenderScrollData.cpp
gfx/webrender_bindings/WebRenderAPI.h
layout/base/nsLayoutDebugger.cpp
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
layout/forms/nsButtonFrameRenderer.cpp
layout/forms/nsSelectsAreaFrame.cpp
layout/generic/TextDrawTarget.h
layout/generic/TextOverflow.cpp
layout/generic/nsColumnSetFrame.cpp
layout/generic/nsGfxScrollFrame.cpp
layout/generic/nsGfxScrollFrame.h
layout/generic/nsIScrollableFrame.h
layout/generic/nsPageFrame.cpp
layout/painting/RetainedDisplayListBuilder.cpp
layout/painting/RetainedDisplayListBuilder.h
layout/painting/nsCSSRendering.cpp
layout/painting/nsDisplayList.cpp
layout/painting/nsDisplayList.h
layout/painting/nsDisplayListInvalidation.cpp
layout/painting/nsDisplayListInvalidation.h
layout/reftests/box-shadow/reftest.list
layout/reftests/bugs/reftest.list
layout/reftests/display-list/reftest.list
layout/reftests/table-background/reftest.list
layout/svg/SVGIntegrationUtils.h
layout/svg/SVGTextFrame.cpp
layout/tables/nsTableFrame.cpp
layout/xul/nsTextBoxFrame.cpp
layout/xul/tree/nsTreeBodyFrame.cpp
--- a/gfx/layers/wr/ClipManager.cpp
+++ b/gfx/layers/wr/ClipManager.cpp
@@ -269,33 +269,32 @@ Maybe<wr::WrSpatialId> ClipManager::Defi
     // If we've already defined this scroll layer before, we can early-exit
     return space;
   }
   // Recurse to define the ancestors
   Maybe<wr::WrSpatialId> ancestorSpace =
       DefineScrollLayers(aASR->mParent, aItem);
 
   Maybe<ScrollMetadata> metadata =
-      aASR->mScrollableFrame->ComputeScrollMetadata(mManager, aItem->Frame(),
-                                                    aItem->ToReferenceFrame());
+      aASR->mScrollableFrame->ComputeScrollMetadata(mManager,
+                                                    aItem->ReferenceFrame());
   if (!metadata) {
     MOZ_ASSERT_UNREACHABLE("Expected scroll metadata to be available!");
     return ancestorSpace;
   }
 
   FrameMetrics& metrics = metadata->GetMetrics();
   if (!metrics.IsScrollable()) {
     // This item is a scrolling no-op, skip over it in the ASR chain.
     return ancestorSpace;
   }
 
   nsIScrollableFrame* scrollableFrame = aASR->mScrollableFrame;
   nsIFrame* scrollFrame = do_QueryFrame(scrollableFrame);
-  nsPoint offset = scrollFrame->GetOffsetToCrossDoc(aItem->Frame()) +
-                   aItem->ToReferenceFrame();
+  nsPoint offset = scrollFrame->GetOffsetToCrossDoc(aItem->ReferenceFrame());
   float auPerDevPixel = aItem->Frame()->PresContext()->AppUnitsPerDevPixel();
   nsRect scrollPort = scrollableFrame->GetScrollPortRect() + offset;
   LayoutDeviceRect clipBounds =
       LayoutDeviceRect::FromAppUnits(scrollPort, auPerDevPixel);
 
   // The content rect that we hand to PushScrollLayer should be relative to
   // the same origin as the clipBounds that we hand to PushScrollLayer -
   // that is, both of them should be relative to the stacking context `aSc`.
--- a/gfx/layers/wr/DisplayItemCache.cpp
+++ b/gfx/layers/wr/DisplayItemCache.cpp
@@ -175,22 +175,16 @@ Maybe<uint16_t> DisplayItemCache::CanReu
   MOZ_ASSERT(slotIndex && CurrentSize() > *slotIndex);
 
   auto& slot = mSlots[*slotIndex];
   if (!slot.mOccupied) {
     // The display item has a stale cache slot. Recache the item.
     return Nothing();
   }
 
-  if (mSuppressed) {
-    slot.mOccupied = false;
-    slotIndex = Nothing();
-    return Nothing();
-  }
-
   if (!(aSpaceAndClip == slot.mSpaceAndClip)) {
     // Spatial id and clip id can change between display lists, if items that
     // generate them change their order.
     slot.mOccupied = false;
     aItem->SetCantBeCached();
     slotIndex = Nothing();
   } else {
     slot.mUsed = true;
--- a/gfx/layers/wr/DisplayItemCache.h
+++ b/gfx/layers/wr/DisplayItemCache.h
@@ -95,19 +95,19 @@ class DisplayItemCache final {
   /**
    * Returns true if display item caching is enabled, otherwise false.
    */
   bool IsEnabled() const { return !mSuppressed && mMaximumSize > 0; }
 
   /**
    * Suppress display item caching. This doesn't clear any existing cached
    * items or change the underlying capacity, it just makes IsEnabled() return
-   * false.
-   * It will also make CanReuseItem return false for the duration of the
-   * suppression.
+   * false. It is not meant to be flipped in the middle of a display list build,
+   * but rather set before the display list build starts to suppress use of the
+   * cache for that display list build.
    */
   bool SetSuppressed(bool aSuppressed) {
     if (aSuppressed == mSuppressed) {
       return mSuppressed;
     }
     mSuppressed = aSuppressed;
     return !mSuppressed;
   }
--- a/gfx/layers/wr/WebRenderCommandBuilder.cpp
+++ b/gfx/layers/wr/WebRenderCommandBuilder.cpp
@@ -1689,18 +1689,17 @@ void WebRenderCommandBuilder::CreateWebR
   if (!createdWRCommands) {
     PushItemAsImage(aItem, aBuilder, aResources, aSc, aDisplayListBuilder);
   }
 }
 
 void WebRenderCommandBuilder::CreateWebRenderCommandsFromDisplayList(
     nsDisplayList* aDisplayList, nsDisplayItem* aWrappingItem,
     nsDisplayListBuilder* aDisplayListBuilder, const StackingContextHelper& aSc,
-    wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
-    bool aNewClipList) {
+    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);
@@ -1711,19 +1710,17 @@ void WebRenderCommandBuilder::CreateWebR
   if (dumpEnabled) {
     // If we're inside a nested display list, print the WR DL items from the
     // wrapper item before we start processing the nested items.
     mBuilderDumpIndex =
         aBuilder.Dump(mDumpIndent + 1, Some(mBuilderDumpIndex), Nothing());
   }
 
   mDumpIndent++;
-  if (aNewClipList) {
-    mClipManager.BeginList(aSc);
-  }
+  mClipManager.BeginList(aSc);
 
   bool apzEnabled = mManager->AsyncPanZoomEnabled();
 
   FlattenedDisplayListIterator iter(aDisplayListBuilder, aDisplayList);
   while (iter.HasNext()) {
     nsDisplayItem* item = iter.GetNextItem();
 
     DisplayItemType itemType = item->GetType();
@@ -1910,19 +1907,17 @@ void WebRenderCommandBuilder::CreateWebR
               deferred ? aSc.GetDeferredTransformMatrix() : Nothing(),
               deferredId);
         }
       }
     }
   }
 
   mDumpIndent--;
-  if (aNewClipList) {
-    mClipManager.EndList(aSc);
-  }
+  mClipManager.EndList(aSc);
 }
 
 void WebRenderCommandBuilder::PushOverrideForASR(
     const ActiveScrolledRoot* aASR, const wr::WrSpatialId& aSpatialId) {
   mClipManager.PushOverrideForASR(aASR, aSpatialId);
 }
 
 void WebRenderCommandBuilder::PopOverrideForASR(
@@ -2484,26 +2479,24 @@ Maybe<wr::ImageMask> WebRenderCommandBui
     return Nothing();
   }
 
   LayoutDeviceToLayerScale2D layerScale(scale.width, scale.height);
   LayoutDeviceRect imageRect = LayerRect(visibleRect) / layerScale;
 
   nsPoint maskOffset = aMaskItem->ToReferenceFrame() - bounds.TopLeft();
 
-  bool shouldHandleOpacity = aBuilder.GetInheritedOpacity() != 1.0f;
-
   nsRect dirtyRect;
   // If this mask item is being painted for the first time, some members of
   // WebRenderMaskData are still default initialized. This is intentional.
   if (aMaskItem->IsInvalid(dirtyRect) ||
       !itemRect.IsEqualInterior(maskData->mItemRect) ||
       !(aMaskItem->Frame()->StyleSVGReset()->mMask == maskData->mMaskStyle) ||
       maskOffset != maskData->mMaskOffset || !sameScale ||
-      shouldHandleOpacity != maskData->mShouldHandleOpacity) {
+      aMaskItem->ShouldHandleOpacity() != maskData->mShouldHandleOpacity) {
     IntSize size = itemRect.Size().ToUnknownSize();
 
     if (!Factory::AllowedSurfaceSize(size)) {
       return Nothing();
     }
 
     std::vector<RefPtr<ScaledFont>> fonts;
     bool validFonts = true;
@@ -2537,18 +2530,18 @@ Maybe<wr::ImageMask> WebRenderCommandBui
     RefPtr<gfxContext> context = gfxContext::CreateOrNull(dt);
     MOZ_ASSERT(context);
 
     context->SetMatrix(context->CurrentMatrix()
                            .PreTranslate(-itemRect.x, -itemRect.y)
                            .PreScale(scale.width, scale.height));
 
     bool maskPainted = false;
-    bool maskIsComplete = aMaskItem->PaintMask(
-        aDisplayListBuilder, context, shouldHandleOpacity, &maskPainted);
+    bool maskIsComplete =
+        aMaskItem->PaintMask(aDisplayListBuilder, context, &maskPainted);
     if (!maskPainted) {
       return Nothing();
     }
 
     // If a mask is incomplete or missing (e.g. it's display: none) the proper
     // behaviour depends on the masked frame being html or svg.
     //
     // For an HTML frame:
@@ -2589,17 +2582,17 @@ Maybe<wr::ImageMask> WebRenderCommandBui
     maskData->mFonts = fonts;
     TakeExternalSurfaces(recorder, maskData->mExternalSurfaces,
                          mManager->GetRenderRootStateManager(), aResources);
     if (maskIsComplete) {
       maskData->mItemRect = itemRect;
       maskData->mMaskOffset = maskOffset;
       maskData->mScale = scale;
       maskData->mMaskStyle = aMaskItem->Frame()->StyleSVGReset()->mMask;
-      maskData->mShouldHandleOpacity = shouldHandleOpacity;
+      maskData->mShouldHandleOpacity = aMaskItem->ShouldHandleOpacity();
     }
   }
 
   aResources.SetBlobImageVisibleArea(
       maskData->mBlobKey.value(),
       ViewAs<ImagePixel>(visibleRect - itemRect.TopLeft(),
                          PixelCastJustification::LayerIsImage));
 
--- a/gfx/layers/wr/WebRenderCommandBuilder.h
+++ b/gfx/layers/wr/WebRenderCommandBuilder.h
@@ -96,17 +96,17 @@ class WebRenderCommandBuilder final {
                        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, bool aNewClipList = true);
+      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/gfx/layers/wr/WebRenderScrollData.cpp
+++ b/gfx/layers/wr/WebRenderScrollData.cpp
@@ -62,18 +62,18 @@ void WebRenderLayerScrollData::Initializ
 
   while (asr && asr != aStopAtAsr) {
     MOZ_ASSERT(aOwner.GetManager());
     ScrollableLayerGuid::ViewID scrollId = asr->GetViewId();
     if (Maybe<size_t> index = aOwner.HasMetadataFor(scrollId)) {
       mScrollIds.AppendElement(index.ref());
     } else {
       Maybe<ScrollMetadata> metadata =
-          asr->mScrollableFrame->ComputeScrollMetadata(
-              aOwner.GetManager(), aItem->Frame(), aItem->ToReferenceFrame());
+          asr->mScrollableFrame->ComputeScrollMetadata(aOwner.GetManager(),
+                                                       aItem->ReferenceFrame());
       aOwner.GetBuilder()->AddScrollFrameToNotify(asr->mScrollableFrame);
       if (metadata) {
         MOZ_ASSERT(metadata->GetMetrics().GetScrollId() == scrollId);
         mScrollIds.AppendElement(aOwner.AddMetadata(metadata.ref()));
       } else {
         MOZ_ASSERT_UNREACHABLE("Expected scroll metadata to be available!");
       }
     }
--- a/gfx/webrender_bindings/WebRenderAPI.h
+++ b/gfx/webrender_bindings/WebRenderAPI.h
@@ -679,27 +679,16 @@ class DisplayListBuilder final {
 
   // Try to avoid using this when possible.
   wr::WrState* Raw() { return mWrState; }
 
   void SetClipChainLeaf(const Maybe<wr::LayoutRect>& aClipRect) {
     mClipChainLeaf = aClipRect;
   }
 
-  // Used for opacity flattening. When we flatten away an opacity item,
-  // we push the opacity value onto the builder.
-  // Descendant items should pull the inherited opacity during
-  // their CreateWebRenderCommands implementation. This can only happen if all
-  // descendant items reported supporting this functionality, via
-  // nsDisplayItem::CanApplyOpacity.
-  float GetInheritedOpacity() { return mInheritedOpacity; }
-  void SetInheritedOpacity(float aOpacity) { mInheritedOpacity = aOpacity; }
-
-  layers::DisplayItemCache* GetDisplayItemCache() { return mDisplayItemCache; }
-
   // A chain of RAII objects, each holding a (ASR, ViewID, SideBits) tuple of
   // data. The topmost object is pointed to by the mActiveFixedPosTracker
   // pointer in the wr::DisplayListBuilder.
   class MOZ_RAII FixedPosScrollTargetTracker final {
    public:
     FixedPosScrollTargetTracker(DisplayListBuilder& aBuilder,
                                 const ActiveScrolledRoot* aAsr,
                                 layers::ScrollableLayerGuid::ViewID aScrollId,
@@ -756,17 +745,16 @@ class DisplayListBuilder final {
 
   wr::PipelineId mPipelineId;
   layers::WebRenderBackend mBackend;
 
   nsTArray<wr::PipelineId> mRemotePipelineIds;
 
   layers::DisplayItemCache* mDisplayItemCache;
   Maybe<uint16_t> mCurrentCacheSlot;
-  float mInheritedOpacity = 1.0f;
 
   friend class WebRenderAPI;
   friend class SpaceAndClipChainHelper;
 };
 
 // This is a RAII class that overrides the current Wr's SpatialId and
 // ClipChainId.
 class MOZ_RAII SpaceAndClipChainHelper final {
--- a/layout/base/nsLayoutDebugger.cpp
+++ b/layout/base/nsLayoutDebugger.cpp
@@ -55,16 +55,18 @@ static void PrintDisplayItemTo(nsDisplay
     if (classes) {
       classes->ToString(tmp);
       contentData.AppendLiteral(" class:");
       contentData.Append(tmp);
     }
   }
   bool snap;
   nsRect rect = aItem->GetBounds(aBuilder, &snap);
+  nsRect layerRect = rect - (*aItem->GetAnimatedGeometryRoot())
+                                ->GetOffsetToCrossDoc(aItem->ReferenceFrame());
   nsRect component = aItem->GetComponentAlphaBounds(aBuilder);
   nsDisplayList* list = aItem->GetChildren();
   const DisplayItemClip& clip = aItem->GetClip();
   nsRegion opaque = aItem->GetOpaqueRegion(aBuilder, &snap);
 
 #ifdef MOZ_DUMP_PAINTING
   if (aDumpHtml && aItem->Painted()) {
     nsCString string(aItem->Name());
@@ -72,25 +74,29 @@ static void PrintDisplayItemTo(nsDisplay
     string.AppendInt((uint64_t)aItem);
     aStream << nsPrintfCString("<a href=\"javascript:ViewImage('%s')\">",
                                string.BeginReading());
   }
 #endif
 
   aStream << nsPrintfCString(
       "%s p=0x%p f=0x%p(%s) key=%d %sbounds(%d,%d,%d,%d) "
-      "componentAlpha(%d,%d,%d,%d) clip(%s) asr(%s) clipChain(%s)%s ",
+      "layerBounds(%d,%d,%d,%d) "
+      "componentAlpha(%d,%d,%d,%d) clip(%s) asr(%s) clipChain(%s)%s ref=0x%p "
+      "agr=0x%p",
       aItem->Name(), aItem, (void*)f, NS_ConvertUTF16toUTF8(contentData).get(),
       aItem->GetPerFrameKey(),
       (aItem->ZIndex() ? nsPrintfCString("z=%d ", aItem->ZIndex()).get() : ""),
-      rect.x, rect.y, rect.width, rect.height, component.x, component.y,
+      rect.x, rect.y, rect.width, rect.height, layerRect.x, layerRect.y,
+      layerRect.width, layerRect.height, component.x, component.y,
       component.width, component.height, clip.ToString().get(),
       ActiveScrolledRoot::ToString(aItem->GetActiveScrolledRoot()).get(),
       DisplayItemClipChain::ToString(aItem->GetClipChain()).get(),
-      aItem->IsUniform(aBuilder) ? " uniform" : "");
+      aItem->IsUniform(aBuilder) ? " uniform" : "", aItem->ReferenceFrame(),
+      aItem->GetAnimatedGeometryRoot()->mFrame);
 
   for (auto iter = opaque.RectIter(); !iter.Done(); iter.Next()) {
     const nsRect& r = iter.Get();
     aStream << nsPrintfCString(" (opaque %d,%d,%d,%d)", r.x, r.y, r.width,
                                r.height);
   }
 
   const auto& willChange = aItem->Frame()->StyleDisplay()->mWillChange;
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -8537,18 +8537,17 @@ bool nsLayoutUtils::CanScrollOriginClobb
     default:
       return true;
   }
 }
 
 /* static */
 ScrollMetadata nsLayoutUtils::ComputeScrollMetadata(
     const nsIFrame* aForFrame, const nsIFrame* aScrollFrame,
-    nsIContent* aContent, const nsIFrame* aItemFrame,
-    const nsPoint& aOffsetToReferenceFrame,
+    nsIContent* aContent, const nsIFrame* aReferenceFrame,
     WebRenderLayerManager* aLayerManager, ViewID aScrollParentId,
     const nsSize& aScrollPortSize, bool aIsRootContent) {
   const nsPresContext* presContext = aForFrame->PresContext();
   int32_t auPerDevPixel = presContext->AppUnitsPerDevPixel();
 
   PresShell* presShell = presContext->GetPresShell();
   ScrollMetadata metadata;
   FrameMetrics& metrics = metadata.GetMetrics();
@@ -8795,18 +8794,18 @@ ScrollMetadata nsLayoutUtils::ComputeScr
 
   // Calculate the composition bounds as the size of the scroll frame and
   // its origin relative to the reference frame.
   // If aScrollFrame is null, we are in a document without a root scroll frame,
   // so it's a xul document. In this case, use the size of the viewport frame.
   const nsIFrame* frameForCompositionBoundsCalculation =
       aScrollFrame ? aScrollFrame : aForFrame;
   nsRect compositionBounds(
-      frameForCompositionBoundsCalculation->GetOffsetToCrossDoc(aItemFrame) +
-          aOffsetToReferenceFrame,
+      frameForCompositionBoundsCalculation->GetOffsetToCrossDoc(
+          aReferenceFrame),
       frameForCompositionBoundsCalculation->GetSize());
   if (scrollableFrame) {
     // If we have a scrollable frame, restrict the composition bounds to its
     // scroll port. The scroll port excludes the frame borders and the scroll
     // bars, which we don't want to be part of the composition bounds.
     nsRect scrollPort = scrollableFrame->GetScrollPortRect();
     compositionBounds = nsRect(
         compositionBounds.TopLeft() + scrollPort.TopLeft(), scrollPort.Size());
@@ -8952,19 +8951,19 @@ Maybe<ScrollMetadata> nsLayoutUtils::Get
 
     nsSize scrollPortSize = frame->GetSize();
     if (isRootContent && rootScrollFrame) {
       nsIScrollableFrame* scrollableFrame =
           rootScrollFrame->GetScrollTargetFrame();
       scrollPortSize = scrollableFrame->GetScrollPortRect().Size();
     }
     return Some(nsLayoutUtils::ComputeScrollMetadata(
-        frame, rootScrollFrame, content, frame,
-        aBuilder->ToReferenceFrame(frame), aLayerManager,
-        ScrollableLayerGuid::NULL_SCROLL_ID, scrollPortSize, isRootContent));
+        frame, rootScrollFrame, content, aBuilder->FindReferenceFrameFor(frame),
+        aLayerManager, ScrollableLayerGuid::NULL_SCROLL_ID, scrollPortSize,
+        isRootContent));
   }
 
   return Nothing();
 }
 
 /* static */
 bool nsLayoutUtils::ContainsMetricsWithId(const Layer* aLayer,
                                           const ViewID& aScrollId) {
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -2755,18 +2755,17 @@ class nsLayoutUtils {
    * higher priority, and take precedence over APZ scrolling. This function
    * returns true for those, and returns false for other origins like APZ
    * itself, or scroll position updates from the history restore code.
    */
   static bool CanScrollOriginClobberApz(ScrollOrigin aScrollOrigin);
 
   static ScrollMetadata ComputeScrollMetadata(
       const nsIFrame* aForFrame, const nsIFrame* aScrollFrame,
-      nsIContent* aContent, const nsIFrame* aItemFrame,
-      const nsPoint& aOffsetToReferenceFrame,
+      nsIContent* aContent, const nsIFrame* aReferenceFrame,
       mozilla::layers::WebRenderLayerManager* aLayerManager,
       ViewID aScrollParentId, const nsSize& aScrollPortSize, bool aIsRoot);
 
   /**
    * Returns the metadata to put onto the root layer of a layer tree, if one is
    * needed. The last argument is a callback function to check if the caller
    * already has a metadata for a given scroll id.
    */
--- a/layout/forms/nsButtonFrameRenderer.cpp
+++ b/layout/forms/nsButtonFrameRenderer.cpp
@@ -282,19 +282,18 @@ bool nsDisplayButtonBoxShadowOuter::Crea
     return false;
   }
   int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
   nsRect shadowRect = nsRect(ToReferenceFrame(), mFrame->GetSize());
   LayoutDeviceRect deviceBox =
       LayoutDeviceRect::FromAppUnits(shadowRect, appUnitsPerDevPixel);
   wr::LayoutRect deviceBoxRect = wr::ToLayoutRect(deviceBox);
 
-  bool dummy;
-  LayoutDeviceRect clipRect = LayoutDeviceRect::FromAppUnits(
-      GetBounds(aDisplayListBuilder, &dummy), appUnitsPerDevPixel);
+  LayoutDeviceRect clipRect =
+      LayoutDeviceRect::FromAppUnits(GetPaintRect(), appUnitsPerDevPixel);
   wr::LayoutRect deviceClipRect = wr::ToLayoutRect(clipRect);
 
   bool hasBorderRadius;
   Unused << nsCSSRendering::HasBoxShadowNativeTheme(mFrame, hasBorderRadius);
 
   LayoutDeviceSize zeroSize;
   wr::BorderRadius borderRadius =
       wr::ToBorderRadius(zeroSize, zeroSize, zeroSize, zeroSize);
@@ -507,21 +506,20 @@ void nsDisplayButtonForeground::Paint(ns
 bool nsDisplayButtonForeground::CreateWebRenderCommands(
     mozilla::wr::DisplayListBuilder& aBuilder,
     mozilla::wr::IpcResourceUpdateQueue& aResources,
     const StackingContextHelper& aSc,
     mozilla::layers::RenderRootStateManager* aManager,
     nsDisplayListBuilder* aDisplayListBuilder) {
   Maybe<nsCSSBorderRenderer> br;
   bool borderIsEmpty = false;
-  bool dummy;
   nsRect r = nsRect(ToReferenceFrame(), mFrame->GetSize());
-  br = mBFR->CreateInnerFocusBorderRenderer(
-      aDisplayListBuilder, mFrame->PresContext(), nullptr,
-      GetBounds(aDisplayListBuilder, &dummy), r, &borderIsEmpty);
+  br = mBFR->CreateInnerFocusBorderRenderer(aDisplayListBuilder,
+                                            mFrame->PresContext(), nullptr,
+                                            GetPaintRect(), r, &borderIsEmpty);
 
   if (!br) {
     return borderIsEmpty;
   }
 
   aBuilder.StartGroup(this);
   br->CreateWebRenderCommands(this, aBuilder, aResources, aSc);
   aBuilder.FinishGroup();
--- a/layout/forms/nsSelectsAreaFrame.cpp
+++ b/layout/forms/nsSelectsAreaFrame.cpp
@@ -161,25 +161,24 @@ class nsDisplayListFocus : public nsPain
 
   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
                            bool* aSnap) const override {
     *aSnap = false;
     // override bounds because the list item focus ring may extend outside
     // the nsSelectsAreaFrame
     nsListControlFrame* listFrame = GetEnclosingListFrame(Frame());
     return listFrame->InkOverflowRectRelativeToSelf() +
-           listFrame->GetOffsetToCrossDoc(Frame()) + ToReferenceFrame();
+           listFrame->GetOffsetToCrossDoc(ReferenceFrame());
   }
   virtual void Paint(nsDisplayListBuilder* aBuilder,
                      gfxContext* aCtx) override {
     nsListControlFrame* listFrame = GetEnclosingListFrame(Frame());
     // listFrame must be non-null or we wouldn't get called.
-    listFrame->PaintFocus(
-        aCtx->GetDrawTarget(),
-        listFrame->GetOffsetToCrossDoc(Frame()) + ToReferenceFrame());
+    listFrame->PaintFocus(aCtx->GetDrawTarget(),
+                          aBuilder->ToReferenceFrame(listFrame));
   }
   NS_DISPLAY_DECL_NAME("ListFocus", TYPE_LIST_FOCUS)
 };
 
 }  // namespace mozilla
 
 void nsSelectsAreaFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
                                           const nsDisplayListSet& aLists) {
--- a/layout/generic/TextDrawTarget.h
+++ b/layout/generic/TextDrawTarget.h
@@ -71,17 +71,17 @@ class TextDrawTarget : public DrawTarget
                     layers::RenderRootStateManager* aManager,
                     nsDisplayItem* aItem, nsRect& aBounds) {
     mResources = &aResources;
     mSc = &aSc;
     mManager = aManager;
     mHasUnsupportedFeatures = false;
     mHasShadows = false;
 
-    SetPermitSubpixelAA(true);
+    SetPermitSubpixelAA(!aItem->IsSubpixelAADisabled());
 
     // Compute clip/bounds
     auto appUnitsPerDevPixel =
         aItem->Frame()->PresContext()->AppUnitsPerDevPixel();
     LayoutDeviceRect layoutBoundsRect =
         LayoutDeviceRect::FromAppUnits(aBounds, appUnitsPerDevPixel);
     LayoutDeviceRect layoutClipRect = layoutBoundsRect;
     mBoundsRect = wr::ToLayoutRect(layoutBoundsRect);
--- a/layout/generic/TextOverflow.cpp
+++ b/layout/generic/TextOverflow.cpp
@@ -204,16 +204,19 @@ class nsDisplayTextOverflowMarker final 
 static void PaintTextShadowCallback(gfxContext* aCtx, nsPoint aShadowOffset,
                                     const nscolor& aShadowColor, void* aData) {
   reinterpret_cast<nsDisplayTextOverflowMarker*>(aData)->PaintTextToContext(
       aCtx, aShadowOffset);
 }
 
 void nsDisplayTextOverflowMarker::Paint(nsDisplayListBuilder* aBuilder,
                                         gfxContext* aCtx) {
+  DrawTargetAutoDisableSubpixelAntialiasing disable(aCtx->GetDrawTarget(),
+                                                    IsSubpixelAADisabled());
+
   nscolor foregroundColor =
       nsLayoutUtils::GetColor(mFrame, &nsStyleText::mWebkitTextFillColor);
 
   // Paint the text-shadows for the overflow marker
   nsLayoutUtils::PaintTextShadow(mFrame, aCtx, mRect,
                                  GetPaintRect(aBuilder, aCtx), foregroundColor,
                                  PaintTextShadowCallback, (void*)this);
   aCtx->SetColor(gfx::sRGBColor::FromABGR(foregroundColor));
--- a/layout/generic/nsColumnSetFrame.cpp
+++ b/layout/generic/nsColumnSetFrame.cpp
@@ -69,20 +69,18 @@ bool nsDisplayColumnRule::CreateWebRende
     mozilla::wr::DisplayListBuilder& aBuilder,
     mozilla::wr::IpcResourceUpdateQueue& aResources,
     const StackingContextHelper& aSc,
     mozilla::layers::RenderRootStateManager* aManager,
     nsDisplayListBuilder* aDisplayListBuilder) {
   RefPtr<gfxContext> screenRefCtx = gfxContext::CreateOrNull(
       gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget().get());
 
-  bool dummy;
   static_cast<nsColumnSetFrame*>(mFrame)->CreateBorderRenderers(
-      mBorderRenderers, screenRefCtx, GetBounds(aDisplayListBuilder, &dummy),
-      ToReferenceFrame());
+      mBorderRenderers, screenRefCtx, GetPaintRect(), ToReferenceFrame());
 
   if (mBorderRenderers.IsEmpty()) {
     return true;
   }
 
   for (auto& renderer : mBorderRenderers) {
     renderer.CreateWebRenderCommands(this, aBuilder, aResources, aSc);
   }
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -4339,16 +4339,19 @@ nsRect ScrollFrameHelper::RestrictToRoot
             (StaticPrefs::apz_nonwr_activate_all_scroll_frames_when_fission() &&
              FissionAutostart()));
   }
 }
 
 bool ScrollFrameHelper::DecideScrollableLayer(
     nsDisplayListBuilder* aBuilder, nsRect* aVisibleRect, nsRect* aDirtyRect,
     bool aSetBase, bool* aDirtyRectHasBeenOverriden) {
+  // Save and check if this changes so we can recompute the current agr.
+  bool oldWillBuildScrollableLayer = mWillBuildScrollableLayer;
+
   nsIContent* content = mOuter->GetContent();
   // For hit testing purposes with fission we want to create a
   // minimal display port for every scroll frame that could be active. (We only
   // do this when aSetBase is true because we only want to do this the first
   // time this function is called for the same scroll frame.)
   if (ShouldActivateAllScrollFrames() &&
       !DisplayPortUtils::HasDisplayPort(content) &&
       nsLayoutUtils::AsyncPanZoomEnabled(mOuter) && WantAsyncScroll() &&
@@ -4459,45 +4462,51 @@ bool ScrollFrameHelper::DecideScrollable
   // for some scroll frames.
   // When a displayport is being used, force building of a layer so that
   // the compositor can find the scrollable layer for async scrolling.
   // If the element is marked 'scrollgrab', also force building of a layer
   // so that APZ can implement scroll grabbing.
   mWillBuildScrollableLayer = usingDisplayPort ||
                               nsContentUtils::HasScrollgrab(content) ||
                               mZoomableByAPZ;
+
+  // The cached animated geometry root for the display builder is out of
+  // date if we just introduced a new animated geometry root.
+  if (oldWillBuildScrollableLayer != mWillBuildScrollableLayer) {
+    aBuilder->RecomputeCurrentAnimatedGeometryRoot();
+  }
+
   return mWillBuildScrollableLayer;
 }
 
 void ScrollFrameHelper::NotifyApzTransaction() {
   mAllowScrollOriginDowngrade = true;
   mApzScrollPos = GetScrollPosition();
   mApzAnimationRequested = IsLastScrollUpdateAnimating();
   mScrollUpdates.Clear();
   if (mIsRoot) {
     mOuter->PresShell()->SetResolutionUpdated(false);
   }
 }
 
 Maybe<ScrollMetadata> ScrollFrameHelper::ComputeScrollMetadata(
-    WebRenderLayerManager* aLayerManager, const nsIFrame* aItemFrame,
-    const nsPoint& aOffsetToReferenceFrame) const {
+    WebRenderLayerManager* aLayerManager,
+    const nsIFrame* aContainerReferenceFrame) const {
   if (!mWillBuildScrollableLayer) {
     return Nothing();
   }
 
   bool isRootContent =
       mIsRoot && mOuter->PresContext()->IsRootContentDocumentCrossProcess();
 
   MOZ_ASSERT(mScrolledFrame->GetContent());
 
   return Some(nsLayoutUtils::ComputeScrollMetadata(
-      mScrolledFrame, mOuter, mOuter->GetContent(), aItemFrame,
-      aOffsetToReferenceFrame, aLayerManager, mScrollParentID,
-      mScrollPort.Size(), isRootContent));
+      mScrolledFrame, mOuter, mOuter->GetContent(), aContainerReferenceFrame,
+      aLayerManager, mScrollParentID, mScrollPort.Size(), isRootContent));
 }
 
 bool ScrollFrameHelper::IsRectNearlyVisible(const nsRect& aRect) const {
   // Use the right rect depending on if a display port is set.
   nsRect displayPort;
   bool usingDisplayport = DisplayPortUtils::GetDisplayPort(
       mOuter->GetContent(), &displayPort,
       DisplayPortOptions().With(DisplayportRelativeTo::ScrollFrame));
--- a/layout/generic/nsGfxScrollFrame.h
+++ b/layout/generic/nsGfxScrollFrame.h
@@ -479,18 +479,18 @@ class ScrollFrameHelper : public nsIRefl
   bool IsLastScrollUpdateAnimating() const;
   using IncludeApzAnimation = nsIScrollableFrame::IncludeApzAnimation;
   bool IsScrollAnimating(IncludeApzAnimation = IncludeApzAnimation::Yes) const;
 
   void ResetScrollInfoIfNeeded(const ScrollGeneration& aGeneration,
                                bool aApzAnimationInProgress);
   bool WantAsyncScroll() const;
   Maybe<mozilla::layers::ScrollMetadata> ComputeScrollMetadata(
-      WebRenderLayerManager* aLayerManager, const nsIFrame* aItemFrame,
-      const nsPoint& aOffsetToReferenceFrame) const;
+      WebRenderLayerManager* aLayerManager,
+      const nsIFrame* aContainerReferenceFrame) const;
   // nsIScrollbarMediator
   void ScrollByPage(nsScrollbarFrame* aScrollbar, int32_t aDirection,
                     nsIScrollbarMediator::ScrollSnapMode aSnap =
                         nsIScrollbarMediator::DISABLE_SNAP);
   void ScrollByWhole(nsScrollbarFrame* aScrollbar, int32_t aDirection,
                      nsIScrollbarMediator::ScrollSnapMode aSnap =
                          nsIScrollbarMediator::DISABLE_SNAP);
   void ScrollByLine(nsScrollbarFrame* aScrollbar, int32_t aDirection,
@@ -1075,20 +1075,19 @@ class nsHTMLScrollFrame : public nsConta
   bool HasScrollUpdates() const final { return mHelper.HasScrollUpdates(); }
   void ResetScrollInfoIfNeeded(const mozilla::ScrollGeneration& aGeneration,
                                bool aApzAnimationInProgress) final {
     mHelper.ResetScrollInfoIfNeeded(aGeneration, aApzAnimationInProgress);
   }
   bool WantAsyncScroll() const final { return mHelper.WantAsyncScroll(); }
   mozilla::Maybe<mozilla::layers::ScrollMetadata> ComputeScrollMetadata(
       mozilla::layers::WebRenderLayerManager* aLayerManager,
-      const nsIFrame* aItemFrame,
-      const nsPoint& aOffsetToReferenceFrame) const final {
-    return mHelper.ComputeScrollMetadata(aLayerManager, aItemFrame,
-                                         aOffsetToReferenceFrame);
+      const nsIFrame* aContainerReferenceFrame) const final {
+    return mHelper.ComputeScrollMetadata(aLayerManager,
+                                         aContainerReferenceFrame);
   }
   void MarkScrollbarsDirtyForReflow() const final {
     mHelper.MarkScrollbarsDirtyForReflow();
   }
   void InvalidateVerticalScrollbar() const final {
     mHelper.InvalidateVerticalScrollbar();
   }
 
@@ -1553,20 +1552,19 @@ class nsXULScrollFrame final : public ns
   bool HasScrollUpdates() const final { return mHelper.HasScrollUpdates(); }
   void ResetScrollInfoIfNeeded(const mozilla::ScrollGeneration& aGeneration,
                                bool aApzAnimationInProgress) final {
     mHelper.ResetScrollInfoIfNeeded(aGeneration, aApzAnimationInProgress);
   }
   bool WantAsyncScroll() const final { return mHelper.WantAsyncScroll(); }
   mozilla::Maybe<mozilla::layers::ScrollMetadata> ComputeScrollMetadata(
       mozilla::layers::WebRenderLayerManager* aLayerManager,
-      const nsIFrame* aItemFrame,
-      const nsPoint& aOffsetToReferenceFrame) const final {
-    return mHelper.ComputeScrollMetadata(aLayerManager, aItemFrame,
-                                         aOffsetToReferenceFrame);
+      const nsIFrame* aContainerReferenceFrame) const final {
+    return mHelper.ComputeScrollMetadata(aLayerManager,
+                                         aContainerReferenceFrame);
   }
   void MarkScrollbarsDirtyForReflow() const final {
     mHelper.MarkScrollbarsDirtyForReflow();
   }
   void InvalidateVerticalScrollbar() const final {
     mHelper.InvalidateVerticalScrollbar();
   }
   void UpdateScrollbarPosition() final { mHelper.UpdateScrollbarPosition(); }
--- a/layout/generic/nsIScrollableFrame.h
+++ b/layout/generic/nsIScrollableFrame.h
@@ -471,18 +471,17 @@ class nsIScrollableFrame : public nsIScr
    * scroll frame.
    */
   virtual bool WantAsyncScroll() const = 0;
   /**
    * Returns the ScrollMetadata contributed by this frame, if there is one.
    */
   virtual mozilla::Maybe<mozilla::layers::ScrollMetadata> ComputeScrollMetadata(
       mozilla::layers::WebRenderLayerManager* aLayerManager,
-      const nsIFrame* aItemFrame,
-      const nsPoint& aOffsetToReferenceFrame) const = 0;
+      const nsIFrame* aContainerReferenceFrame) const = 0;
 
   /**
    * Mark the scrollbar frames for reflow.
    */
   virtual void MarkScrollbarsDirtyForReflow() const = 0;
 
   /**
    * Invalidate the scrollbar after the marks have been changed.
--- a/layout/generic/nsPageFrame.cpp
+++ b/layout/generic/nsPageFrame.cpp
@@ -402,17 +402,17 @@ class nsDisplayHeaderFooter final : publ
 
   virtual void Paint(nsDisplayListBuilder* aBuilder,
                      gfxContext* aCtx) override {
 #ifdef DEBUG
     nsPageFrame* pageFrame = do_QueryFrame(mFrame);
     MOZ_ASSERT(pageFrame, "We should have an nsPageFrame");
 #endif
     static_cast<nsPageFrame*>(mFrame)->PaintHeaderFooter(
-        *aCtx, ToReferenceFrame(), false);
+        *aCtx, ToReferenceFrame(), IsSubpixelAADisabled());
   }
   NS_DISPLAY_DECL_NAME("HeaderFooter", TYPE_HEADER_FOOTER)
 
   virtual nsRect GetComponentAlphaBounds(
       nsDisplayListBuilder* aBuilder) const override {
     bool snap;
     return GetBounds(aBuilder, &snap);
   }
--- a/layout/painting/RetainedDisplayListBuilder.cpp
+++ b/layout/painting/RetainedDisplayListBuilder.cpp
@@ -38,39 +38,16 @@
  * items. We then build a display list just for this subset of the screen and
  * merge it into the display list from last paint.
  *
  * Any items that exist in one list and not the other must not have a defined
  * ordering in the DAG, since they need to intersect to have an ordering and
  * we would have built both in the new list if they intersected. Given that, we
  * can align items that appear in both lists, and any items that appear between
  * matched items can be inserted into the merged list in any order.
- *
- * Frames that are a stacking context, containing blocks for position:fixed
- * descendants, and don't have any continuations (see
- * CanStoreDisplayListBuildingRect) trigger recursion into the algorithm with
- * separate retaining decisions made.
- *
- * RDL defines the concept of an AnimatedGeometryRoot (AGR), the nearest
- * ancestor frame which can be moved asynchronously on the compositor thread.
- * These are currently nsDisplayItems which return true from CanMoveAsync
- * (animated nsDisplayTransform and nsDisplayStickyPosition) and
- * ActiveScrolledRoots.
- *
- * For each context that we run the retaining algorithm, there can only be
- * mutations to one AnimatedGeometryRoot. This is because we are unable to
- * reason about intersections of items that might then move relative to each
- * other without RDL running again. If there are mutations to multiple
- * AnimatedGeometryRoots, then we bail out and rebuild all the items in the
- * context.
- *
- * Otherwise, when mutations are restricted to a single AGR, we pre-process the
- * old display list and mark the frames for all existing (unmodified!) items
- * that belong to a different AGR and ensure that we rebuild those items for
- * correct sorting with the modified ones.
  */
 
 using mozilla::dom::Document;
 
 namespace mozilla {
 
 void RetainedDisplayListData::AddModifiedFrame(nsIFrame* aFrame) {
   MOZ_ASSERT(!aFrame->IsFrameModified());
@@ -119,29 +96,30 @@ static void MarkFramesWithItemsAndImages
       }
     }
     if (i->GetChildren()) {
       MarkFramesWithItemsAndImagesModified(i->GetChildren());
     }
   }
 }
 
-static nsIFrame* SelectAGRForFrame(nsIFrame* aFrame, nsIFrame* aParentAGR) {
+static AnimatedGeometryRoot* SelectAGRForFrame(
+    nsIFrame* aFrame, AnimatedGeometryRoot* aParentAGR) {
   if (!aFrame->IsStackingContext() || !aFrame->IsFixedPosContainingBlock()) {
     return aParentAGR;
   }
 
   if (!aFrame->HasOverrideDirtyRegion()) {
     return nullptr;
   }
 
   nsDisplayListBuilder::DisplayListBuildingData* data =
       aFrame->GetProperty(nsDisplayListBuilder::DisplayListBuildingRect());
 
-  return data && data->mModifiedAGR ? data->mModifiedAGR : nullptr;
+  return data && data->mModifiedAGR ? data->mModifiedAGR.get() : nullptr;
 }
 
 void RetainedDisplayListBuilder::AddSizeOfIncludingThis(
     nsWindowSizes& aSizes) const {
   aSizes.mLayoutRetainedDisplayListSize += aSizes.mState.mMallocSizeOf(this);
   mBuilder.AddSizeOfExcludingThis(aSizes);
   mList.AddSizeOfExcludingThis(aSizes);
 }
@@ -165,20 +143,19 @@ bool AnyContentAncestorModified(nsIFrame
 
 // Removes any display items that belonged to a frame that was deleted,
 // and mark frames that belong to a different AGR so that get their
 // items built again.
 // TODO: We currently descend into all children even if we don't have an AGR
 // to mark, as child stacking contexts might. It would be nice if we could
 // jump into those immediately rather than walking the entire thing.
 bool RetainedDisplayListBuilder::PreProcessDisplayList(
-    RetainedDisplayList* aList, nsIFrame* aAGR, PartialUpdateResult& aUpdated,
-    nsIFrame* aAsyncAncestor, const ActiveScrolledRoot* aAsyncAncestorASR,
-    nsIFrame* aOuterFrame, uint32_t aCallerKey, uint32_t aNestingDepth,
-    bool aKeepLinked) {
+    RetainedDisplayList* aList, AnimatedGeometryRoot* aAGR,
+    PartialUpdateResult& aUpdated, nsIFrame* aOuterFrame, uint32_t aCallerKey,
+    uint32_t aNestingDepth, bool aKeepLinked) {
   // The DAG merging algorithm does not have strong mechanisms in place to keep
   // the complexity of the resulting DAG under control. In some cases we can
   // build up edges very quickly. Detect those cases and force a full display
   // list build if we hit them.
   static const uint32_t kMaxEdgeRatio = 5;
   const bool initializeDAG = !aList->mDAG.Length();
   if (!aKeepLinked && !initializeDAG &&
       aList->mDAG.mDirectPredecessorList.Length() >
@@ -276,59 +253,43 @@ bool RetainedDisplayListBuilder::PreProc
       // need to.
       bool keepLinked = aKeepLinked;
       nsIFrame* invalid = item->FrameForInvalidation();
       if (!invalid->ForceDescendIntoIfVisible() &&
           !invalid->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) {
         keepLinked = true;
       }
 
-      // If this item's frame is an AGR (can be moved asynchronously by the
-      // compositor), then use that frame for descendants. Also pass the ASR
-      // for that item, so that descendants can compare to see if any new
-      // ASRs have been pushed since.
-      nsIFrame* asyncAncestor = aAsyncAncestor;
-      const ActiveScrolledRoot* asyncAncestorASR = aAsyncAncestorASR;
-      if (item->CanMoveAsync()) {
-        asyncAncestor = item->Frame();
-        asyncAncestorASR = item->GetActiveScrolledRoot();
-      }
-
-      if (!PreProcessDisplayList(
-              item->GetChildren(), SelectAGRForFrame(f, aAGR), aUpdated,
-              asyncAncestor, asyncAncestorASR, item->Frame(),
-              item->GetPerFrameKey(), aNestingDepth + 1, keepLinked)) {
+      if (!PreProcessDisplayList(item->GetChildren(),
+                                 SelectAGRForFrame(f, aAGR), aUpdated,
+                                 item->Frame(), item->GetPerFrameKey(),
+                                 aNestingDepth + 1, keepLinked)) {
         MOZ_RELEASE_ASSERT(
             !aKeepLinked,
             "Can't early return since we need to move the out list back");
         return false;
       }
     }
 
     // TODO: We should be able to check the clipped bounds relative
     // to the common AGR (of both the existing item and the invalidated
     // frame) and determine if they can ever intersect.
     // TODO: We only really need to build the ancestor container item that is a
     // sibling of the changed thing to get correct ordering. The changed content
     // is a frame though, and it's hard to map that to container items in this
     // list.
-    // If an ancestor display item is an AGR, and our ASR matches the ASR
-    // of that item, then there can't have been any new ASRs pushed since that
-    // item, so that item is our AGR. Otherwise, our AGR is our ASR.
-    nsIFrame* agrFrame = nullptr;
-    if (aAsyncAncestorASR == item->GetActiveScrolledRoot()) {
-      agrFrame = aAsyncAncestor;
-    } else {
-      MOZ_ASSERT(item->GetActiveScrolledRoot());
-      agrFrame =
-          item->GetActiveScrolledRoot()->mScrollableFrame->GetScrolledFrame();
+    if (aAGR && item->GetAnimatedGeometryRoot()->GetAsyncAGR() != aAGR) {
+      mBuilder.MarkFrameForDisplayIfVisible(f, mBuilder.RootReferenceFrame());
     }
 
-    if (aAGR && agrFrame != aAGR) {
-      mBuilder.MarkFrameForDisplayIfVisible(f, mBuilder.RootReferenceFrame());
+    // TODO: This is here because we sometimes reuse the previous display list
+    // completely. For optimization, we could only restore the state for reused
+    // display items.
+    if (item->RestoreState()) {
+      item->InvalidateItemCacheEntry();
     }
 
     // If we're going to keep this linked list and not merge it, then mark the
     // item as used and put it back into the list.
     if (aKeepLinked) {
       item->SetReused(true);
       if (item->GetChildren()) {
         item->UpdateBounds(Builder());
@@ -975,17 +936,17 @@ static bool CanStoreDisplayListBuildingR
          aFrame->IsStackingContext() && aFrame->IsFixedPosContainingBlock() &&
          // Split frames might have placeholders for modified frames in their
          // unmodified continuation frame.
          !aFrame->GetPrevContinuation() && !aFrame->GetNextContinuation();
 }
 
 static bool ProcessFrameInternal(nsIFrame* aFrame,
                                  nsDisplayListBuilder* aBuilder,
-                                 nsIFrame** aAGR, nsRect& aOverflow,
+                                 AnimatedGeometryRoot** aAGR, nsRect& aOverflow,
                                  const nsIFrame* aStopAtFrame,
                                  nsTArray<nsIFrame*>& aOutFramesWithProps,
                                  const bool aStopAtStackingContext) {
   nsIFrame* currentFrame = aFrame;
 
   while (currentFrame != aStopAtFrame) {
     CRR_LOG("currentFrame: %p (placeholder=%d), aOverflow: %d %d %d %d\n",
             currentFrame, !aStopAtStackingContext, aOverflow.x, aOverflow.y,
@@ -1011,17 +972,17 @@ static bool ProcessFrameInternal(nsIFram
               currentFrame);
 
       CRR_LOG("OOF frame draw area: %d %d %d %d\n", placeholderOverflow.x,
               placeholderOverflow.y, placeholderOverflow.width,
               placeholderOverflow.height);
 
       // Tracking AGRs for the placeholder processing is not necessary, as the
       // goal is to only modify the DisplayListBuildingData rect.
-      nsIFrame* dummyAGR = nullptr;
+      AnimatedGeometryRoot* dummyAGR = nullptr;
 
       // Find a common ancestor frame to handle frame continuations.
       // TODO: It might be possible to write a more specific and efficient
       // function for this.
       const nsIFrame* ancestor = nsLayoutUtils::FindNearestCommonAncestorFrame(
           currentFrame->GetParent(), placeholder->GetParent());
 
       if (!ProcessFrameInternal(placeholder, aBuilder, &dummyAGR,
@@ -1119,18 +1080,22 @@ static bool ProcessFrameInternal(nsIFram
         // Continue ascending the frame tree until we reach aStopAtFrame.
         continue;
       }
 
       // Grab the visible (display list building) rect for children of this
       // wrapper item and convert into into coordinate relative to the current
       // frame.
       nsRect previousVisible = wrapperItem->GetBuildingRectForChildren();
-      if (wrapperItem->ReferenceFrameForChildren() != wrapperItem->Frame()) {
+      if (wrapperItem->ReferenceFrameForChildren() ==
+          wrapperItem->ReferenceFrame()) {
         previousVisible -= wrapperItem->ToReferenceFrame();
+      } else {
+        MOZ_ASSERT(wrapperItem->ReferenceFrameForChildren() ==
+                   wrapperItem->Frame());
       }
 
       if (!previousVisible.Contains(aOverflow)) {
         // If the overflow area of the changed frame isn't contained within the
         // old item, then we might change the size of the item and need to
         // update its sorting accordingly. Keep propagating the overflow area up
         // so that we build intersecting items for sorting.
         continue;
@@ -1153,28 +1118,29 @@ static bool ProcessFrameInternal(nsIFram
     }
   }
   return true;
 }
 
 bool RetainedDisplayListBuilder::ProcessFrame(
     nsIFrame* aFrame, nsDisplayListBuilder* aBuilder, nsIFrame* aStopAtFrame,
     nsTArray<nsIFrame*>& aOutFramesWithProps, const bool aStopAtStackingContext,
-    nsRect* aOutDirty, nsIFrame** aOutModifiedAGR) {
+    nsRect* aOutDirty, AnimatedGeometryRoot** aOutModifiedAGR) {
   if (aFrame->HasOverrideDirtyRegion()) {
     aOutFramesWithProps.AppendElement(aFrame);
   }
 
   if (aFrame->HasAnyStateBits(NS_FRAME_IN_POPUP)) {
     return true;
   }
 
   // TODO: There is almost certainly a faster way of doing this, probably can be
   // combined with the ancestor walk for TransformFrameRectToAncestor.
-  nsIFrame* agrFrame = aBuilder->FindAnimatedGeometryRootFrameFor(aFrame);
+  AnimatedGeometryRoot* agr =
+      aBuilder->FindAnimatedGeometryRootFor(aFrame)->GetAsyncAGR();
 
   CRR_LOG("Processing frame %p with agr %p\n", aFrame, agr->mFrame);
 
   // Convert the frame's overflow rect into the coordinate space
   // of the nearest stacking context that has an existing display item.
   // We store that as a dirty rect on that stacking context so that we build
   // all items that intersect the changed frame within the stacking context,
   // and then we use MarkFrameForDisplayIfVisible to make sure the stacking
@@ -1186,33 +1152,33 @@ bool RetainedDisplayListBuilder::Process
 
   // If the modified frame is also a caret frame, include the caret area.
   // This is needed because some frames (for example text frames without text)
   // might have an empty overflow rect.
   if (aFrame == aBuilder->GetCaretFrame()) {
     overflow.UnionRect(overflow, aBuilder->GetCaretRect());
   }
 
-  if (!ProcessFrameInternal(aFrame, aBuilder, &agrFrame, overflow, aStopAtFrame,
+  if (!ProcessFrameInternal(aFrame, aBuilder, &agr, overflow, aStopAtFrame,
                             aOutFramesWithProps, aStopAtStackingContext)) {
     return false;
   }
 
   if (!overflow.IsEmpty()) {
     aOutDirty->UnionRect(*aOutDirty, overflow);
     CRR_LOG("Adding area to root draw area: %d %d %d %d\n", overflow.x,
             overflow.y, overflow.width, overflow.height);
 
     // If we get changed frames from multiple AGRS, then just give up as it gets
     // really complex to track which items would need to be marked in
     // MarkFramesForDifferentAGR.
     if (!*aOutModifiedAGR) {
-      CRR_LOG("Setting %p as root stacking context AGR\n", agrFrame);
-      *aOutModifiedAGR = agrFrame;
-    } else if (agrFrame && *aOutModifiedAGR != agrFrame) {
+      CRR_LOG("Setting %p as root stacking context AGR\n", agr);
+      *aOutModifiedAGR = agr;
+    } else if (agr && *aOutModifiedAGR != agr) {
       CRR_LOG("Found multiple AGRs in root stacking context, giving up\n");
       return false;
     }
   }
   return true;
 }
 
 static void AddFramesForContainingBlock(nsIFrame* aBlock,
@@ -1276,17 +1242,18 @@ static void FindContainingBlocks(nsIFram
  * @param aOutFramesWithProps The list of frames to which we attached partial
  * build data so that it can be cleaned up.
  *
  * @return true if we succesfully computed a partial rebuild region, false if a
  * full build is required.
  */
 bool RetainedDisplayListBuilder::ComputeRebuildRegion(
     nsTArray<nsIFrame*>& aModifiedFrames, nsRect* aOutDirty,
-    nsIFrame** aOutModifiedAGR, nsTArray<nsIFrame*>& aOutFramesWithProps) {
+    AnimatedGeometryRoot** aOutModifiedAGR,
+    nsTArray<nsIFrame*>& aOutFramesWithProps) {
   CRR_LOG("Computing rebuild regions for %zu frames:\n",
           aModifiedFrames.Length());
   nsTArray<nsIFrame*> extraFrames;
   for (nsIFrame* f : aModifiedFrames) {
     MOZ_ASSERT(f);
 
     mBuilder.AddFrameMarkedForDisplayIfVisible(f);
     FindContainingBlocks(f, extraFrames);
@@ -1438,23 +1405,22 @@ PartialUpdateResult RetainedDisplayListB
   AutoClearFramePropsArray framesWithProps;
   GetModifiedAndFramesWithProps(&mBuilder, &modifiedFrames.Frames(),
                                 &framesWithProps.Frames());
 
   // Do not allow partial builds if the |ShouldBuildPartial()| heuristic fails.
   bool shouldBuildPartial = ShouldBuildPartial(modifiedFrames.Frames());
 
   nsRect modifiedDirty;
-  nsIFrame* modifiedAGR = nullptr;
+  AnimatedGeometryRoot* modifiedAGR = nullptr;
   PartialUpdateResult result = PartialUpdateResult::NoChange;
   if (!shouldBuildPartial ||
       !ComputeRebuildRegion(modifiedFrames.Frames(), &modifiedDirty,
                             &modifiedAGR, framesWithProps.Frames()) ||
-      !PreProcessDisplayList(&mList, modifiedAGR, result,
-                             mBuilder.RootReferenceFrame(), nullptr)) {
+      !PreProcessDisplayList(&mList, modifiedAGR, result)) {
     mBuilder.SetPartialBuildFailed(true);
     mBuilder.LeavePresShell(mBuilder.RootReferenceFrame(), nullptr);
     mList.DeleteAll(&mBuilder);
     return PartialUpdateResult::Failed;
   }
 
   // This is normally handled by EnterPresShell, but we skipped it so that we
   // didn't call MarkFrameForDisplayIfVisible before ComputeRebuildRegion.
--- a/layout/painting/RetainedDisplayListBuilder.h
+++ b/layout/painting/RetainedDisplayListBuilder.h
@@ -210,21 +210,23 @@ struct RetainedDisplayListBuilder {
    * new partial display lists, and serializes the old list into an array,
    * recording indices on items for fast lookup during merging. Builds an
    * initial linear DAG for the list if we don't have an existing one. Finds
    * items that have a different AGR from the specified one, and marks them to
    * also be built so that we get relative ordering correct. Passes
    * aKeepLinked=true internally for sub-lists that can't be changed to keep the
    * original list structure linked for fast re-use.
    */
-  bool PreProcessDisplayList(
-      RetainedDisplayList* aList, nsIFrame* aAGR, PartialUpdateResult& aUpdated,
-      nsIFrame* aAsyncAncestor, const ActiveScrolledRoot* aAsyncAncestorASR,
-      nsIFrame* aOuterFrame = nullptr, uint32_t aCallerKey = 0,
-      uint32_t aNestingDepth = 0, bool aKeepLinked = false);
+  bool PreProcessDisplayList(RetainedDisplayList* aList,
+                             AnimatedGeometryRoot* aAGR,
+                             PartialUpdateResult& aUpdated,
+                             nsIFrame* aOuterFrame = nullptr,
+                             uint32_t aCallerKey = 0,
+                             uint32_t aNestingDepth = 0,
+                             bool aKeepLinked = false);
 
   /**
    * Merges items from aNewList into non-invalidated items from aOldList and
    * stores the result in aOutList.
    *
    * aOuterItem is a pointer to an item that owns one of the lists, if
    * available. If both lists are populated, then both outer items must not be
    * invalidated, and identical, so either can be passed here.
@@ -234,24 +236,25 @@ struct RetainedDisplayListBuilder {
    */
   bool MergeDisplayLists(
       nsDisplayList* aNewList, RetainedDisplayList* aOldList,
       RetainedDisplayList* aOutList,
       mozilla::Maybe<const mozilla::ActiveScrolledRoot*>& aOutContainerASR,
       nsDisplayItem* aOuterItem = nullptr);
 
   bool ComputeRebuildRegion(nsTArray<nsIFrame*>& aModifiedFrames,
-                            nsRect* aOutDirty, nsIFrame** aOutModifiedAGR,
+                            nsRect* aOutDirty,
+                            AnimatedGeometryRoot** aOutModifiedAGR,
                             nsTArray<nsIFrame*>& aOutFramesWithProps);
 
   bool ProcessFrame(nsIFrame* aFrame, nsDisplayListBuilder* aBuilder,
                     nsIFrame* aStopAtFrame,
                     nsTArray<nsIFrame*>& aOutFramesWithProps,
                     const bool aStopAtStackingContext, nsRect* aOutDirty,
-                    nsIFrame** aOutModifiedAGR);
+                    AnimatedGeometryRoot** aOutModifiedAGR);
 
   friend class MergeState;
 
   nsDisplayListBuilder mBuilder;
   RetainedDisplayList mList;
   nsRect mPreviousVisibleRect;
   WeakFrame mPreviousCaret;
   RetainedDisplayListMetrics mMetrics;
--- a/layout/painting/nsCSSRendering.cpp
+++ b/layout/painting/nsCSSRendering.cpp
@@ -723,23 +723,21 @@ ImgDrawResult nsCSSRendering::CreateWebR
   uint32_t flags = 0;
   if (aDisplayListBuilder->IsPaintingToWindow()) {
     flags |= nsImageRenderer::FLAG_PAINTING_TO_WINDOW;
   }
   if (aDisplayListBuilder->ShouldSyncDecodeImages()) {
     flags |= nsImageRenderer::FLAG_SYNC_DECODE_IMAGES;
   }
 
-  bool dummy;
   image::ImgDrawResult result;
   Maybe<nsCSSBorderImageRenderer> bir =
       nsCSSBorderImageRenderer::CreateBorderImageRenderer(
           aForFrame->PresContext(), aForFrame, aBorderArea, aStyleBorder,
-          aItem->GetBounds(aDisplayListBuilder, &dummy),
-          aForFrame->GetSkipSides(), flags, &result);
+          aItem->GetPaintRect(), aForFrame->GetSkipSides(), flags, &result);
 
   if (!bir) {
     // We aren't ready. Try to fallback to the null border image if present but
     // return the draw result for the border image renderer.
     CreateWebRenderCommandsForNullBorder(
         aItem, aForFrame, aBorderArea, aBuilder, aResources, aSc, aStyleBorder);
     return result;
   }
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -614,17 +614,21 @@ nsDisplayListBuilder::nsDisplayListBuild
                                            bool aBuildCaret,
                                            bool aRetainingDisplayList)
     : mReferenceFrame(aReferenceFrame),
       mIgnoreScrollFrame(nullptr),
       mCurrentActiveScrolledRoot(nullptr),
       mCurrentContainerASR(nullptr),
       mCurrentFrame(aReferenceFrame),
       mCurrentReferenceFrame(aReferenceFrame),
+      mRootAGR(AnimatedGeometryRoot::CreateAGRForFrame(
+          aReferenceFrame, nullptr, true, aRetainingDisplayList)),
+      mCurrentAGR(mRootAGR),
       mBuildingExtraPagesForPageNum(0),
+      mUsedAGRBudget(0),
       mDirtyRect(-1, -1, -1, -1),
       mGlassDisplayItem(nullptr),
       mHasGlassItemDuringPartial(false),
       mCaretFrame(nullptr),
       mScrollInfoItemsForHoisting(nullptr),
       mFirstClipChainToDestroy(nullptr),
       mMode(aMode),
       mTableBackgroundSet(nullptr),
@@ -699,16 +703,19 @@ static PresShell* GetFocusedPresShell() 
     return nullptr;
   }
 
   return focusedDocShell->GetPresShell();
 }
 
 void nsDisplayListBuilder::BeginFrame() {
   nsCSSRendering::BeginFrameTreesLocked();
+  mCurrentAGR = mRootAGR;
+  mFrameToAnimatedGeometryRootMap.InsertOrUpdate(mReferenceFrame,
+                                                 RefPtr{mRootAGR});
 
   mIsPaintingToWindow = false;
   mUseHighQualityScaling = false;
   mIgnoreSuppression = false;
   mInTransform = false;
   mInFilter = false;
   mSyncDecodeImages = false;
 
@@ -731,16 +738,18 @@ void nsDisplayListBuilder::BeginFrame() 
       mCaretFrame = nullptr;
     }
   }
 }
 
 void nsDisplayListBuilder::EndFrame() {
   NS_ASSERTION(!mInInvalidSubtree,
                "Someone forgot to cleanup mInInvalidSubtree!");
+  mFrameToAnimatedGeometryRootMap.Clear();
+  mAGRBudgetSet.Clear();
   mActiveScrolledRoots.Clear();
   mEffectsUpdates.Clear();
   FreeClipChains();
   FreeTemporaryItems();
   nsCSSRendering::EndFrameTreesLocked();
   mCaretFrame = nullptr;
 }
 
@@ -814,16 +823,93 @@ void nsDisplayListBuilder::SetGlassDispl
   }
 }
 
 bool nsDisplayListBuilder::NeedToForceTransparentSurfaceForItem(
     nsDisplayItem* aItem) {
   return aItem == mGlassDisplayItem;
 }
 
+AnimatedGeometryRoot* nsDisplayListBuilder::WrapAGRForFrame(
+    nsIFrame* aAnimatedGeometryRoot, bool aIsAsync,
+    AnimatedGeometryRoot* aParent /* = nullptr */) {
+  DebugOnly<bool> dummy;
+  MOZ_ASSERT(IsAnimatedGeometryRoot(aAnimatedGeometryRoot, dummy) == AGR_YES);
+
+  RefPtr<AnimatedGeometryRoot> result =
+      mFrameToAnimatedGeometryRootMap.Get(aAnimatedGeometryRoot);
+  if (!result) {
+    MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDocInProcess(
+        RootReferenceFrame(), aAnimatedGeometryRoot));
+    RefPtr<AnimatedGeometryRoot> parent = aParent;
+    if (!parent) {
+      nsIFrame* parentFrame =
+          nsLayoutUtils::GetCrossDocParentFrameInProcess(aAnimatedGeometryRoot);
+      if (parentFrame) {
+        bool isAsync;
+        nsIFrame* parentAGRFrame =
+            FindAnimatedGeometryRootFrameFor(parentFrame, isAsync);
+        parent = WrapAGRForFrame(parentAGRFrame, isAsync);
+      }
+    }
+    result = AnimatedGeometryRoot::CreateAGRForFrame(
+        aAnimatedGeometryRoot, parent, aIsAsync, IsRetainingDisplayList());
+    mFrameToAnimatedGeometryRootMap.InsertOrUpdate(aAnimatedGeometryRoot,
+                                                   RefPtr{result});
+  }
+  MOZ_ASSERT(!aParent || result->mParentAGR == aParent);
+  return result;
+}
+
+AnimatedGeometryRoot* nsDisplayListBuilder::AnimatedGeometryRootForASR(
+    const ActiveScrolledRoot* aASR) {
+  if (!aASR) {
+    return GetRootAnimatedGeometryRoot();
+  }
+  nsIFrame* scrolledFrame = aASR->mScrollableFrame->GetScrolledFrame();
+  return FindAnimatedGeometryRootFor(scrolledFrame);
+}
+
+AnimatedGeometryRoot* nsDisplayListBuilder::FindAnimatedGeometryRootFor(
+    nsIFrame* aFrame) {
+  if (!IsPaintingToWindow()) {
+    return mRootAGR;
+  }
+  if (aFrame == mCurrentFrame) {
+    return mCurrentAGR;
+  }
+
+  RefPtr<AnimatedGeometryRoot> result =
+      mFrameToAnimatedGeometryRootMap.Get(aFrame);
+  if (result) {
+    return result;
+  }
+
+  bool isAsync;
+  nsIFrame* agrFrame = FindAnimatedGeometryRootFrameFor(aFrame, isAsync);
+  result = WrapAGRForFrame(agrFrame, isAsync);
+  return result;
+}
+
+AnimatedGeometryRoot* nsDisplayListBuilder::FindAnimatedGeometryRootFor(
+    nsDisplayItem* aItem) {
+  if (aItem->ShouldFixToViewport(this)) {
+    // Make its active scrolled root be the active scrolled root of
+    // the enclosing viewport, since it shouldn't be scrolled by scrolled
+    // frames in its document. InvalidateFixedBackgroundFramesFromList in
+    // nsGfxScrollFrame will not repaint this item when scrolling occurs.
+    nsIFrame* viewportFrame = nsLayoutUtils::GetClosestFrameOfType(
+        aItem->Frame(), LayoutFrameType::Viewport, RootReferenceFrame());
+    if (viewportFrame) {
+      return FindAnimatedGeometryRootFor(viewportFrame);
+    }
+  }
+  return FindAnimatedGeometryRootFor(aItem->Frame());
+}
+
 void nsDisplayListBuilder::SetIsRelativeToLayoutViewport() {
   mIsRelativeToLayoutViewport = true;
   UpdateShouldBuildAsyncZoomContainer();
 }
 
 void nsDisplayListBuilder::UpdateShouldBuildAsyncZoomContainer() {
   const Document* document = mReferenceFrame->PresContext()->Document();
   mBuildAsyncZoomContainer = !mIsRelativeToLayoutViewport &&
@@ -1175,16 +1261,18 @@ void nsDisplayListBuilder::LeavePresShel
   if (!mPresShellStates.IsEmpty()) {
     nsPresContext* pc = CurrentPresContext();
     nsIDocShell* docShell = pc->GetDocShell();
     if (docShell) {
       docShell->GetWindowDraggingAllowed(&mWindowDraggingAllowed);
     }
     mIsInChromePresContext = pc->IsChrome();
   } else {
+    mCurrentAGR = mRootAGR;
+
     for (uint32_t i = 0; i < mFramesMarkedForDisplayIfVisible.Length(); ++i) {
       UnmarkFrameForDisplayIfVisible(mFramesMarkedForDisplayIfVisible[i]);
     }
     mFramesMarkedForDisplayIfVisible.SetLength(0);
   }
 }
 
 void nsDisplayListBuilder::FreeClipChains() {
@@ -1394,40 +1482,16 @@ const DisplayItemClipChain* nsDisplayLis
   return c;
 }
 
 struct ClipChainItem {
   DisplayItemClip clip;
   const ActiveScrolledRoot* asr;
 };
 
-static const DisplayItemClipChain* FindCommonAncestorClipForIntersection(
-    const DisplayItemClipChain* aOne, const DisplayItemClipChain* aTwo) {
-  for (const ActiveScrolledRoot* asr =
-           ActiveScrolledRoot::PickDescendant(aOne->mASR, aTwo->mASR);
-       asr; asr = asr->mParent) {
-    if (aOne == aTwo) {
-      return aOne;
-    }
-    if (aOne->mASR == asr) {
-      aOne = aOne->mParent;
-    }
-    if (aTwo->mASR == asr) {
-      aTwo = aTwo->mParent;
-    }
-    if (!aOne) {
-      return aTwo;
-    }
-    if (!aTwo) {
-      return aOne;
-    }
-  }
-  return nullptr;
-}
-
 const DisplayItemClipChain* nsDisplayListBuilder::CreateClipChainIntersection(
     const DisplayItemClipChain* aAncestor,
     const DisplayItemClipChain* aLeafClip1,
     const DisplayItemClipChain* aLeafClip2) {
   AutoTArray<ClipChainItem, 8> intersectedClips;
 
   const DisplayItemClipChain* clip1 = aLeafClip1;
   const DisplayItemClipChain* clip2 = aLeafClip2;
@@ -1471,16 +1535,36 @@ const DisplayItemClipChain* nsDisplayLis
   return parentSC;
 }
 
 const DisplayItemClipChain* nsDisplayListBuilder::CopyWholeChain(
     const DisplayItemClipChain* aClipChain) {
   return CreateClipChainIntersection(nullptr, aClipChain, nullptr);
 }
 
+const DisplayItemClipChain* nsDisplayListBuilder::FuseClipChainUpTo(
+    const DisplayItemClipChain* aClipChain, const ActiveScrolledRoot* aASR) {
+  if (!aClipChain) {
+    return nullptr;
+  }
+
+  const DisplayItemClipChain* sc = aClipChain;
+  DisplayItemClip mergedClip;
+  while (sc && ActiveScrolledRoot::PickDescendant(aASR, sc->mASR) == sc->mASR) {
+    mergedClip.IntersectWith(sc->mClip);
+    sc = sc->mParent;
+  }
+
+  if (!mergedClip.HasClip()) {
+    return nullptr;
+  }
+
+  return AllocateDisplayItemClipChain(mergedClip, aASR, sc);
+}
+
 const nsIFrame* nsDisplayListBuilder::FindReferenceFrameFor(
     const nsIFrame* aFrame, nsPoint* aOffset) const {
   auto MaybeApplyAdditionalOffset = [&]() {
     if (auto offset = AdditionalOffset()) {
       *aOffset += *offset;
     }
   };
 
@@ -1521,75 +1605,147 @@ static bool IsStickyFrameActive(nsDispla
                                nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
   if (!sf) {
     return false;
   }
 
   return sf->IsScrollingActive();
 }
 
-bool nsDisplayListBuilder::IsAnimatedGeometryRoot(nsIFrame* aFrame,
-                                                  nsIFrame** aParent) {
+nsDisplayListBuilder::AGRState nsDisplayListBuilder::IsAnimatedGeometryRoot(
+    nsIFrame* aFrame, bool& aIsAsync, nsIFrame** aParent) {
+  // We can return once we know that this frame is an AGR, and we're either
+  // async, or sure that none of the later conditions might make us async.
+  // The exception to this is when IsPaintingToWindow() == false.
+  aIsAsync = false;
   if (aFrame == mReferenceFrame) {
-    return true;
+    aIsAsync = true;
+    return AGR_YES;
   }
 
   if (!IsPaintingToWindow()) {
     if (aParent) {
       *aParent = nsLayoutUtils::GetCrossDocParentFrameInProcess(aFrame);
     }
-    return false;
+    return AGR_NO;
   }
 
   nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(aFrame);
   if (!parent) {
-    return true;
-  }
-  *aParent = parent;
+    aIsAsync = true;
+    return AGR_YES;
+  }
 
   if (aFrame->StyleDisplay()->mPosition == StylePositionProperty::Sticky &&
       IsStickyFrameActive(this, aFrame, parent)) {
-    return true;
+    aIsAsync = true;
+    return AGR_YES;
   }
 
   if (aFrame->IsTransformed()) {
-    if (EffectCompositor::HasAnimationsForCompositor(
-            aFrame, DisplayItemType::TYPE_TRANSFORM)) {
-      return true;
-    }
+    aIsAsync = EffectCompositor::HasAnimationsForCompositor(
+        aFrame, DisplayItemType::TYPE_TRANSFORM);
+    return AGR_YES;
   }
 
   LayoutFrameType parentType = parent->Type();
   if (parentType == LayoutFrameType::Scroll ||
       parentType == LayoutFrameType::ListControl) {
     nsIScrollableFrame* sf = do_QueryFrame(parent);
     if (sf->GetScrolledFrame() == aFrame && sf->IsScrollingActive()) {
       MOZ_ASSERT(!aFrame->IsTransformed());
-      return sf->IsMaybeAsynchronouslyScrolled();
-    }
-  }
-
-  return false;
+      aIsAsync = sf->IsMaybeAsynchronouslyScrolled();
+      return AGR_YES;
+    }
+  }
+
+  // Treat the slider thumb as being as an active scrolled root when it wants
+  // its own layer so that it can move without repainting.
+  if (parentType == LayoutFrameType::Slider) {
+    auto* sf = static_cast<nsSliderFrame*>(parent)->GetScrollFrame();
+    if (sf && sf->IsScrollingActive()) {
+      return AGR_YES;
+    }
+  }
+
+  if (nsLayoutUtils::IsPopup(aFrame)) {
+    return AGR_YES;
+  }
+
+  if (ActiveLayerTracker::IsOffsetStyleAnimated(aFrame)) {
+    const bool inBudget = AddToAGRBudget(aFrame);
+    if (inBudget) {
+      return AGR_YES;
+    }
+  }
+
+  if (!aFrame->GetParent() &&
+      DisplayPortUtils::ViewportHasDisplayPort(aFrame->PresContext())) {
+    // Viewport frames in a display port need to be animated geometry roots
+    // for background-attachment:fixed elements.
+    return AGR_YES;
+  }
+
+  // Fixed-pos frames are parented by the viewport frame, which has no parent.
+  if (DisplayPortUtils::IsFixedPosFrameInDisplayPort(aFrame)) {
+    return AGR_YES;
+  }
+
+  if (aParent) {
+    *aParent = parent;
+  }
+
+  return AGR_NO;
 }
 
 nsIFrame* nsDisplayListBuilder::FindAnimatedGeometryRootFrameFor(
-    nsIFrame* aFrame) {
+    nsIFrame* aFrame, bool& aIsAsync) {
   MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDocInProcess(
       RootReferenceFrame(), aFrame));
   nsIFrame* cursor = aFrame;
   while (cursor != RootReferenceFrame()) {
     nsIFrame* next;
-    if (IsAnimatedGeometryRoot(cursor, &next)) {
+    if (IsAnimatedGeometryRoot(cursor, aIsAsync, &next) == AGR_YES) {
       return cursor;
     }
     cursor = next;
   }
+  // Root frame is always an async agr.
+  aIsAsync = true;
   return cursor;
 }
 
+void nsDisplayListBuilder::RecomputeCurrentAnimatedGeometryRoot() {
+  bool isAsync;
+  if (*mCurrentAGR != mCurrentFrame &&
+      IsAnimatedGeometryRoot(const_cast<nsIFrame*>(mCurrentFrame), isAsync) ==
+          AGR_YES) {
+    AnimatedGeometryRoot* oldAGR = mCurrentAGR;
+    mCurrentAGR = WrapAGRForFrame(const_cast<nsIFrame*>(mCurrentFrame), isAsync,
+                                  mCurrentAGR);
+
+    // Iterate the AGR cache and look for any objects that reference the old AGR
+    // and check to see if they need to be updated. AGRs can be in the cache
+    // multiple times, so we may end up doing the work multiple times for AGRs
+    // that don't change.
+    for (const RefPtr<AnimatedGeometryRoot>& cached :
+         mFrameToAnimatedGeometryRootMap.Values()) {
+      if (cached->mParentAGR == oldAGR && cached != mCurrentAGR) {
+        // It's possible that this cached AGR struct that has the old AGR as a
+        // parent should instead have mCurrentFrame has a parent.
+        nsIFrame* parent = FindAnimatedGeometryRootFrameFor(*cached, isAsync);
+        MOZ_ASSERT(parent == mCurrentFrame || parent == *oldAGR);
+        if (parent == mCurrentFrame) {
+          cached->mParentAGR = mCurrentAGR;
+        }
+      }
+    }
+  }
+}
+
 static nsRect ApplyAllClipNonRoundedIntersection(
     const DisplayItemClipChain* aClipChain, const nsRect& aRect) {
   nsRect result = aRect;
   while (aClipChain) {
     result = aClipChain->mClip.ApplyNonRoundedIntersection(result);
     aClipChain = aClipChain->mParent;
   }
   return result;
@@ -1711,16 +1867,17 @@ void nsDisplayTransform::AddSizeOfExclud
 
 void nsDisplayListBuilder::AddSizeOfExcludingThis(nsWindowSizes& aSizes) const {
   mPool.AddSizeOfExcludingThis(aSizes, Arena::ArenaKind::DisplayList);
 
   size_t n = 0;
   MallocSizeOf mallocSizeOf = aSizes.mState.mMallocSizeOf;
   n += mDocumentWillChangeBudgets.ShallowSizeOfExcludingThis(mallocSizeOf);
   n += mFrameWillChangeBudgets.ShallowSizeOfExcludingThis(mallocSizeOf);
+  n += mAGRBudgetSet.ShallowSizeOfExcludingThis(mallocSizeOf);
   n += mEffectsUpdates.ShallowSizeOfExcludingThis(mallocSizeOf);
   n += mWindowExcludeGlassRegion.SizeOfExcludingThis(mallocSizeOf);
   n += mRetainedWindowDraggingRegion.SizeOfExcludingThis(mallocSizeOf);
   n += mRetainedWindowNoDraggingRegion.SizeOfExcludingThis(mallocSizeOf);
   n += mRetainedWindowOpaqueRegion.SizeOfExcludingThis(mallocSizeOf);
   // XXX can't measure mClipDeduplicator since it uses std::unordered_set.
 
   aSizes.mLayoutRetainedDisplayListSize += n;
@@ -1905,16 +2062,50 @@ void nsDisplayListBuilder::RemoveFromWil
   }
 }
 
 void nsDisplayListBuilder::ClearWillChangeBudgets() {
   mFrameWillChangeBudgets.Clear();
   mDocumentWillChangeBudgets.Clear();
 }
 
+#ifdef MOZ_GFX_OPTIMIZE_MOBILE
+const float gAGRBudgetAreaMultiplier = 0.3;
+#else
+const float gAGRBudgetAreaMultiplier = 3.0;
+#endif
+
+bool nsDisplayListBuilder::AddToAGRBudget(nsIFrame* aFrame) {
+  if (mAGRBudgetSet.Contains(aFrame)) {
+    return true;
+  }
+
+  const nsPresContext* presContext =
+      aFrame->PresContext()->GetRootPresContext();
+  if (!presContext) {
+    return false;
+  }
+
+  const nsRect area = presContext->GetVisibleArea();
+  const uint32_t budgetLimit =
+      gAGRBudgetAreaMultiplier *
+      nsPresContext::AppUnitsToIntCSSPixels(area.width) *
+      nsPresContext::AppUnitsToIntCSSPixels(area.height);
+
+  const uint32_t cost = GetLayerizationCost(aFrame->GetSize());
+  const bool onBudget = mUsedAGRBudget + cost < budgetLimit;
+
+  if (onBudget) {
+    mUsedAGRBudget += cost;
+    mAGRBudgetSet.Insert(aFrame);
+  }
+
+  return onBudget;
+}
+
 void nsDisplayListBuilder::EnterSVGEffectsContents(
     nsIFrame* aEffectsFrame, nsDisplayList* aHoistedItemsStorage) {
   MOZ_ASSERT(aHoistedItemsStorage);
   if (mSVGEffectsFrames.IsEmpty()) {
     MOZ_ASSERT(!mScrollInfoItemsForHoisting);
     mScrollInfoItemsForHoisting = aHoistedItemsStorage;
   }
   mSVGEffectsFrames.AppendElement(aEffectsFrame);
@@ -2545,22 +2736,29 @@ nsDisplayItem::nsDisplayItem(nsDisplayLi
                              const ActiveScrolledRoot* aActiveScrolledRoot)
     : mFrame(aFrame), mActiveScrolledRoot(aActiveScrolledRoot) {
   MOZ_COUNT_CTOR(nsDisplayItem);
   MOZ_ASSERT(mFrame);
   if (aBuilder->IsRetainingDisplayList()) {
     mFrame->AddDisplayItem(this);
   }
 
-  aBuilder->FindReferenceFrameFor(aFrame, &mToReferenceFrame);
+  mReferenceFrame = aBuilder->FindReferenceFrameFor(aFrame, &mToReferenceFrame);
+  // This can return the wrong result if the item override
+  // ShouldFixToViewport(), the item needs to set it again in its constructor.
+  mAnimatedGeometryRoot = aBuilder->FindAnimatedGeometryRootFor(aFrame);
+  MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDocInProcess(
+                 aBuilder->RootReferenceFrame(), *mAnimatedGeometryRoot),
+             "Bad");
   NS_ASSERTION(
       aBuilder->GetVisibleRect().width >= 0 || !aBuilder->IsForPainting(),
       "visible rect not set");
 
-  mClipChain = aBuilder->ClipState().GetCurrentCombinedClipChain(aBuilder);
+  nsDisplayItem::SetClipChain(
+      aBuilder->ClipState().GetCurrentCombinedClipChain(aBuilder), true);
 
   // The visible rect is for mCurrentFrame, so we have to use
   // mCurrentOffsetToReferenceFrame
   nsRect visible = aBuilder->GetVisibleRect() +
                    aBuilder->GetCurrentFrameOffsetToReferenceFrame();
   SetBuildingRect(visible);
 
   const nsStyleDisplay* disp = mFrame->StyleDisplay();
@@ -2587,34 +2785,69 @@ bool nsDisplayItem::ForceActiveLayers() 
   return StaticPrefs::layers_force_active();
 }
 
 int32_t nsDisplayItem::ZIndex() const { return mFrame->ZIndex().valueOr(0); }
 
 void nsDisplayItem::SetClipChain(const DisplayItemClipChain* aClipChain,
                                  bool aStore) {
   mClipChain = aClipChain;
+  mClip = DisplayItemClipChain::ClipForASR(aClipChain, mActiveScrolledRoot);
+
+  if (aStore) {
+    mState.mClipChain = mClipChain;
+    mState.mClip = mClip;
+  }
 }
 
 Maybe<nsRect> nsDisplayItem::GetClipWithRespectToASR(
     nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR) const {
   if (const DisplayItemClip* clip =
           DisplayItemClipChain::ClipForASR(GetClipChain(), aASR)) {
     return Some(clip->GetClipRect());
   }
 #ifdef DEBUG
   MOZ_ASSERT(false, "item should have finite clip with respect to aASR");
 #endif
   return Nothing();
 }
 
-const DisplayItemClip& nsDisplayItem::GetClip() const {
-  const DisplayItemClip* clip =
-      DisplayItemClipChain::ClipForASR(mClipChain, mActiveScrolledRoot);
-  return clip ? *clip : DisplayItemClip::NoClip();
+void nsDisplayItem::FuseClipChainUpTo(nsDisplayListBuilder* aBuilder,
+                                      const ActiveScrolledRoot* aASR) {
+  mClipChain = aBuilder->FuseClipChainUpTo(mClipChain, aASR);
+
+  if (mClipChain) {
+    mClip = &mClipChain->mClip;
+  } else {
+    mClip = nullptr;
+  }
+}
+
+static const DisplayItemClipChain* FindCommonAncestorClipForIntersection(
+    const DisplayItemClipChain* aOne, const DisplayItemClipChain* aTwo) {
+  for (const ActiveScrolledRoot* asr =
+           ActiveScrolledRoot::PickDescendant(aOne->mASR, aTwo->mASR);
+       asr; asr = asr->mParent) {
+    if (aOne == aTwo) {
+      return aOne;
+    }
+    if (aOne->mASR == asr) {
+      aOne = aOne->mParent;
+    }
+    if (aTwo->mASR == asr) {
+      aTwo = aTwo->mParent;
+    }
+    if (!aOne) {
+      return aTwo;
+    }
+    if (!aTwo) {
+      return aOne;
+    }
+  }
+  return nullptr;
 }
 
 void nsDisplayItem::IntersectClip(nsDisplayListBuilder* aBuilder,
                                   const DisplayItemClipChain* aOther,
                                   bool aStore) {
   if (!aOther || mClipChain == aOther) {
     return;
   }
@@ -2861,29 +3094,34 @@ nsDisplayBackgroundImage::nsDisplayBackg
       mBackgroundStyle(aInitData.backgroundStyle),
       mImage(aInitData.image),
       mDependentFrame(nullptr),
       mBackgroundRect(aInitData.backgroundRect),
       mFillRect(aInitData.fillArea),
       mDestRect(aInitData.destArea),
       mLayer(aInitData.layer),
       mIsRasterImage(aInitData.isRasterImage),
-      mShouldFixToViewport(aInitData.shouldFixToViewport) {
+      mShouldFixToViewport(aInitData.shouldFixToViewport),
+      mImageFlags(0),
+      mOpacity(1.0f) {
   MOZ_COUNT_CTOR(nsDisplayBackgroundImage);
 #ifdef DEBUG
   if (mBackgroundStyle && mBackgroundStyle != mFrame->Style()) {
     // If this changes, then you also need to adjust css::ImageLoader to
     // invalidate mFrame as needed.
     MOZ_ASSERT(mFrame->IsCanvasFrame() ||
                mFrame->IsFrameOfType(nsIFrame::eTablePart));
   }
 #endif
 
   mBounds = GetBoundsInternal(aInitData.builder, aFrameForBounds);
   if (mShouldFixToViewport) {
+    mAnimatedGeometryRoot =
+        aInitData.builder->FindAnimatedGeometryRootFor(this);
+
     // Expand the item's visible rect to cover the entire bounds, limited to the
     // viewport rect. This is necessary because the background's clip can move
     // asynchronously.
     if (Maybe<nsRect> viewportRect = GetViewportRectRelativeToReferenceFrame(
             aInitData.builder, mFrame)) {
       SetBuildingRect(mBounds.Intersect(*viewportRect));
     }
   }
@@ -3345,42 +3583,42 @@ static void CheckForBorderItem(nsDisplay
       aFlags |= nsCSSRendering::PAINTBG_WILL_PAINT_BORDER;
     }
 
     break;
   }
 }
 
 bool nsDisplayBackgroundImage::CanBuildWebRenderDisplayItems(
-    WebRenderLayerManager* aManager, nsDisplayListBuilder* aBuilder) const {
+    WebRenderLayerManager* aManager, nsDisplayListBuilder* aBuilder) {
+  if (aBuilder) {
+    mImageFlags = aBuilder->GetBackgroundPaintFlags();
+  }
+
   return mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer].mClip !=
              StyleGeometryBox::Text &&
          nsCSSRendering::CanBuildWebRenderDisplayItemsForStyleImageLayer(
              aManager, *StyleFrame()->PresContext(), StyleFrame(),
-             mBackgroundStyle->StyleBackground(), mLayer,
-             aBuilder->GetBackgroundPaintFlags());
+             mBackgroundStyle->StyleBackground(), mLayer, mImageFlags);
 }
 
 bool nsDisplayBackgroundImage::CreateWebRenderCommands(
     wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
     const StackingContextHelper& aSc, RenderRootStateManager* aManager,
     nsDisplayListBuilder* aDisplayListBuilder) {
   if (!CanBuildWebRenderDisplayItems(aManager->LayerManager(),
                                      aDisplayListBuilder)) {
     return false;
   }
 
-  uint32_t paintFlags = aDisplayListBuilder->GetBackgroundPaintFlags();
-  bool dummy;
-  CheckForBorderItem(this, paintFlags);
+  CheckForBorderItem(this, mImageFlags);
   nsCSSRendering::PaintBGParams params =
       nsCSSRendering::PaintBGParams::ForSingleLayer(
-          *StyleFrame()->PresContext(), GetBounds(aDisplayListBuilder, &dummy),
-          mBackgroundRect, StyleFrame(), paintFlags, mLayer,
-          CompositionOp::OP_OVER, aBuilder.GetInheritedOpacity());
+          *StyleFrame()->PresContext(), GetPaintRect(), mBackgroundRect,
+          StyleFrame(), mImageFlags, mLayer, CompositionOp::OP_OVER, mOpacity);
   params.bgClipRect = &mBounds;
   ImgDrawResult result =
       nsCSSRendering::BuildWebRenderDisplayItemsForStyleImageLayer(
           params, aBuilder, aResources, aSc, aManager, this);
   if (result == ImgDrawResult::NOT_SUPPORTED) {
     return false;
   }
 
@@ -3423,17 +3661,17 @@ static nsRect GetInsideClipRect(const ns
   return clipRect.Intersect(aRect);
 }
 
 nsRegion nsDisplayBackgroundImage::GetOpaqueRegion(
     nsDisplayListBuilder* aBuilder, bool* aSnap) const {
   nsRegion result;
   *aSnap = false;
 
-  if (!mBackgroundStyle) {
+  if (!mBackgroundStyle || mOpacity != 1.0f) {
     return result;
   }
 
   *aSnap = true;
 
   // For StyleBoxDecorationBreak::Slice, don't try to optimize here, since
   // this could easily lead to O(N^2) behavior inside InlineBackgroundData,
   // which expects frames to be sent to it in content order, not reverse
@@ -3512,18 +3750,17 @@ void nsDisplayBackgroundImage::PaintInte
                                  aBuilder)) {
       return;
     }
   }
 
   nsCSSRendering::PaintBGParams params =
       nsCSSRendering::PaintBGParams::ForSingleLayer(
           *StyleFrame()->PresContext(), aBounds, mBackgroundRect, StyleFrame(),
-          aBuilder->GetBackgroundPaintFlags(), mLayer, CompositionOp::OP_OVER,
-          1.0f);
+          mImageFlags, mLayer, CompositionOp::OP_OVER, mOpacity);
   params.bgClipRect = aClipRect;
   ImgDrawResult result = nsCSSRendering::PaintStyleImageLayer(params, *aCtx);
 
   if (clip == StyleGeometryBox::Text) {
     ctx->PopGroupAndBlend();
   }
 
   nsDisplayBackgroundGeometry::UpdateDrawResult(this, result);
@@ -3770,32 +4007,36 @@ nsRect nsDisplayThemedBackground::GetBou
 #if defined(MOZ_REFLOW_PERF_DSP) && defined(MOZ_REFLOW_PERF)
 void nsDisplayReflowCount::Paint(nsDisplayListBuilder* aBuilder,
                                  gfxContext* aCtx) {
   mFrame->PresShell()->PaintCount(mFrameName, aCtx, mFrame->PresContext(),
                                   mFrame, ToReferenceFrame(), mColor);
 }
 #endif
 
-bool nsDisplayBackgroundColor::CanApplyOpacity(
-    WebRenderLayerManager* aManager, nsDisplayListBuilder* aBuilder) const {
+void nsDisplayBackgroundColor::ApplyOpacity(nsDisplayListBuilder* aBuilder,
+                                            float aOpacity,
+                                            const DisplayItemClipChain* aClip) {
+  NS_ASSERTION(CanApplyOpacity(), "ApplyOpacity should be allowed");
+  mColor.a = mColor.a * aOpacity;
+  IntersectClip(aBuilder, aClip, false);
+}
+
+bool nsDisplayBackgroundColor::CanApplyOpacity() const {
   // Don't apply opacity if the background color is animated since the color is
   // going to be changed on the compositor.
   return !EffectCompositor::HasAnimationsForCompositor(
       mFrame, DisplayItemType::TYPE_BACKGROUND_COLOR);
 }
 
 bool nsDisplayBackgroundColor::CreateWebRenderCommands(
     wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
     const StackingContextHelper& aSc, RenderRootStateManager* aManager,
     nsDisplayListBuilder* aDisplayListBuilder) {
-  gfx::sRGBColor color = mColor;
-  color.a *= aBuilder.GetInheritedOpacity();
-
-  if (color == sRGBColor() &&
+  if (mColor == sRGBColor() &&
       !EffectCompositor::HasAnimationsForCompositor(
           mFrame, DisplayItemType::TYPE_BACKGROUND_COLOR)) {
     return true;
   }
 
   if (HasBackgroundClipText()) {
     return false;
   }
@@ -3812,21 +4053,21 @@ bool nsDisplayBackgroundColor::CreateWeb
   wr::LayoutRect r = wr::ToLayoutRect(bounds);
 
   if (animationsId) {
     wr::WrAnimationProperty prop{
         wr::WrAnimationType::BackgroundColor,
         animationsId,
     };
     aBuilder.PushRectWithAnimation(r, r, !BackfaceIsHidden(),
-                                   wr::ToColorF(ToDeviceColor(color)), &prop);
+                                   wr::ToColorF(ToDeviceColor(mColor)), &prop);
   } else {
     aBuilder.StartGroup(this);
     aBuilder.PushRect(r, r, !BackfaceIsHidden(),
-                      wr::ToColorF(ToDeviceColor(color)));
+                      wr::ToColorF(ToDeviceColor(mColor)));
     aBuilder.FinishGroup();
   }
 
   return true;
 }
 
 void nsDisplayBackgroundColor::PaintWithClip(nsDisplayListBuilder* aBuilder,
                                              gfxContext* aCtx,
@@ -4025,21 +4266,20 @@ bool nsDisplayOutline::CreateWebRenderCo
   nsRect rect = GetInnerRect() + ToReferenceFrame();
   if (IsThemedOutline()) {
     rect.Inflate(mFrame->StyleOutline()->mOutlineOffset.ToAppUnits());
     return pc->Theme()->CreateWebRenderCommandsForWidget(
         aBuilder, aResources, aSc, aManager, mFrame,
         StyleAppearance::FocusOutline, rect);
   }
 
-  bool dummy;
   Maybe<nsCSSBorderRenderer> borderRenderer =
       nsCSSRendering::CreateBorderRendererForNonThemedOutline(
-          pc, /* aDrawTarget = */ nullptr, mFrame,
-          GetBounds(aDisplayListBuilder, &dummy), rect, mFrame->Style());
+          pc, /* aDrawTarget = */ nullptr, mFrame, GetPaintRect(), rect,
+          mFrame->Style());
 
   if (!borderRenderer) {
     // No border renderer means "there is no outline".
     // Paint nothing and return success.
     return true;
   }
 
   borderRenderer->CreateWebRenderCommands(this, aBuilder, aResources, aSc);
@@ -4247,17 +4487,17 @@ void nsDisplayBoxShadowOuter::Paint(nsDi
                                     gfxContext* aCtx) {
   nsPoint offset = ToReferenceFrame();
   nsRect borderRect = mFrame->VisualBorderRectRelativeToSelf() + offset;
   nsPresContext* presContext = mFrame->PresContext();
 
   AUTO_PROFILER_LABEL("nsDisplayBoxShadowOuter::Paint", GRAPHICS);
 
   nsCSSRendering::PaintBoxShadowOuter(presContext, *aCtx, mFrame, borderRect,
-                                      GetPaintRect(aBuilder, aCtx), 1.0f);
+                                      GetPaintRect(aBuilder, aCtx), mOpacity);
 }
 
 nsRect nsDisplayBoxShadowOuter::GetBounds(nsDisplayListBuilder* aBuilder,
                                           bool* aSnap) const {
   *aSnap = false;
   return mBounds;
 }
 
@@ -4279,17 +4519,17 @@ bool nsDisplayBoxShadowOuter::IsInvisibl
   bool hasBorderRadii = mFrame->GetBorderRadii(twipsRadii);
   if (!hasBorderRadii) {
     return true;
   }
 
   return RoundedRectContainsRect(frameRect, twipsRadii, aRect);
 }
 
-bool nsDisplayBoxShadowOuter::CanBuildWebRenderDisplayItems() const {
+bool nsDisplayBoxShadowOuter::CanBuildWebRenderDisplayItems() {
   auto shadows = mFrame->StyleEffects()->mBoxShadow.AsSpan();
   if (shadows.IsEmpty()) {
     return false;
   }
 
   bool hasBorderRadius;
   // We don't support native themed things yet like box shadows around
   // input buttons.
@@ -4338,18 +4578,18 @@ bool nsDisplayBoxShadowOuter::CreateWebR
 
   for (const auto& shadow : Reversed(shadows)) {
     if (shadow.inset) {
       continue;
     }
 
     float blurRadius =
         float(shadow.base.blur.ToAppUnits()) / float(appUnitsPerDevPixel);
-    gfx::sRGBColor shadowColor = nsCSSRendering::GetShadowColor(
-        shadow.base, mFrame, aBuilder.GetInheritedOpacity());
+    gfx::sRGBColor shadowColor =
+        nsCSSRendering::GetShadowColor(shadow.base, mFrame, mOpacity);
 
     // We don't move the shadow rect here since WR does it for us
     // Now translate everything to device pixels.
     const nsRect& shadowRect = frameRect;
     LayoutDevicePoint shadowOffset = LayoutDevicePoint::FromAppUnits(
         nsPoint(shadow.base.horizontal.ToAppUnits(),
                 shadow.base.vertical.ToAppUnits()),
         appUnitsPerDevPixel);
@@ -4382,20 +4622,21 @@ bool nsDisplayBoxShadowOuter::CreateWebR
 
   return true;
 }
 
 void nsDisplayBoxShadowOuter::ComputeInvalidationRegion(
     nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
     nsRegion* aInvalidRegion) const {
   const auto* geometry =
-      static_cast<const nsDisplayItemGenericGeometry*>(aGeometry);
+      static_cast<const nsDisplayBoxShadowOuterGeometry*>(aGeometry);
   bool snap;
   if (!geometry->mBounds.IsEqualInterior(GetBounds(aBuilder, &snap)) ||
-      !geometry->mBorderRect.IsEqualInterior(GetBorderRect())) {
+      !geometry->mBorderRect.IsEqualInterior(GetBorderRect()) ||
+      mOpacity != geometry->mOpacity) {
     nsRegion oldShadow, newShadow;
     nscoord dontCare[8];
     bool hasBorderRadius = mFrame->GetBorderRadii(dontCare);
     if (hasBorderRadius) {
       // If we have rounded corners then we need to invalidate the frame area
       // too since we paint into it.
       oldShadow = geometry->mBounds;
       newShadow = GetBounds(aBuilder, &snap);
@@ -4527,41 +4768,53 @@ nsDisplayWrapList::nsDisplayWrapList(
       mHasZIndexOverride(false),
       mClearingClipChain(aClearClipChain) {
   MOZ_COUNT_CTOR(nsDisplayWrapList);
 
   mBaseBuildingRect = GetBuildingRect();
 
   mListPtr = &mList;
   mListPtr->AppendToTop(aList);
-  mOriginalClipChain = mClipChain;
   nsDisplayWrapList::UpdateBounds(aBuilder);
+
+#ifdef DEBUG
+  if (!aFrame || !aFrame->IsTransformed()) {
+    return;
+  }
+
+  nsDisplayItem* item = mListPtr->GetBottom();
+  if (item && item->Frame() == mFrame &&
+      (mListPtr->Count() == 1 ||
+       item->GetType() == DisplayItemType::TYPE_TRANSFORM)) {
+    MOZ_ASSERT(mReferenceFrame == item->ReferenceFrame());
+  }
+#endif
 }
 
 nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder* aBuilder,
                                      nsIFrame* aFrame, nsDisplayItem* aItem)
     : nsPaintedDisplayItem(aBuilder, aFrame,
                            aBuilder->CurrentActiveScrolledRoot()),
       mOverrideZIndex(0),
       mHasZIndexOverride(false) {
   MOZ_COUNT_CTOR(nsDisplayWrapList);
 
   mBaseBuildingRect = GetBuildingRect();
 
   mListPtr = &mList;
   mListPtr->AppendToTop(aItem);
-  mOriginalClipChain = mClipChain;
   nsDisplayWrapList::UpdateBounds(aBuilder);
 
   if (!aFrame || !aFrame->IsTransformed()) {
     return;
   }
 
   // See the previous nsDisplayWrapList constructor
   if (aItem->Frame() == aFrame) {
+    mReferenceFrame = aItem->ReferenceFrame();
     mToReferenceFrame = aItem->ToReferenceFrame();
   }
 
   nsRect visible = aBuilder->GetVisibleRect() +
                    aBuilder->GetCurrentFrameOffsetToReferenceFrame();
 
   SetBuildingRect(visible);
 }
@@ -4712,16 +4965,17 @@ nsDisplayOpacity::nsDisplayOpacity(
     const ActiveScrolledRoot* aActiveScrolledRoot, bool aForEventsOnly,
     bool aNeedsActiveLayer)
     : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot, true),
       mOpacity(aFrame->StyleEffects()->mOpacity),
       mForEventsOnly(aForEventsOnly),
       mNeedsActiveLayer(aNeedsActiveLayer),
       mChildOpacityState(ChildOpacityState::Unknown) {
   MOZ_COUNT_CTOR(nsDisplayOpacity);
+  mState.mOpacity = mOpacity;
 }
 
 void nsDisplayOpacity::HitTest(nsDisplayListBuilder* aBuilder,
                                const nsRect& aRect,
                                nsDisplayItem::HitTestState* aState,
                                nsTArray<nsIFrame*>* aOutFrames) {
   AutoRestore<float> opacity(aState->mCurrentOpacity);
   aState->mCurrentOpacity *= mOpacity;
@@ -4782,18 +5036,25 @@ bool nsDisplayOpacity::NeedsActiveLayer(
                                         bool aEnforceMinimumSize) {
   return EffectCompositor::HasAnimationsForCompositor(
              aFrame, DisplayItemType::TYPE_OPACITY) ||
          (ActiveLayerTracker::IsStyleAnimated(
               aBuilder, aFrame, nsCSSPropertyIDSet::OpacityProperties()) &&
           !(aEnforceMinimumSize && IsItemTooSmallForActiveLayer(aFrame)));
 }
 
-bool nsDisplayOpacity::CanApplyOpacity(WebRenderLayerManager* aManager,
-                                       nsDisplayListBuilder* aBuilder) const {
+void nsDisplayOpacity::ApplyOpacity(nsDisplayListBuilder* aBuilder,
+                                    float aOpacity,
+                                    const DisplayItemClipChain* aClip) {
+  NS_ASSERTION(CanApplyOpacity(), "ApplyOpacity should be allowed");
+  mOpacity = mOpacity * aOpacity;
+  IntersectClip(aBuilder, aClip, false);
+}
+
+bool nsDisplayOpacity::CanApplyOpacity() const {
   return !EffectCompositor::HasAnimationsForCompositor(
       mFrame, DisplayItemType::TYPE_OPACITY);
 }
 
 // Only try folding our opacity down if we have at most |kOpacityMaxChildCount|
 // children that don't overlap and can all apply the opacity to themselves.
 static const size_t kOpacityMaxChildCount = 3;
 
@@ -4810,19 +5071,17 @@ static const size_t kOpacityMaxListSize 
  *
  * We need to do this recursively, because the child display items might contain
  * nested nsDisplayWrapLists.
  *
  * Returns false if there are more than |kOpacityMaxChildCount| items, or if an
  * item that returns false for CanApplyOpacity() is encountered.
  * Otherwise returns true.
  */
-static bool CollectItemsWithOpacity(WebRenderLayerManager* aManager,
-                                    nsDisplayListBuilder* aBuilder,
-                                    nsDisplayList* aList,
+static bool CollectItemsWithOpacity(nsDisplayList* aList,
                                     nsTArray<nsPaintedDisplayItem*>& aArray) {
   if (aList->Count() > kOpacityMaxListSize) {
     // Exit early, since |aList| will likely contain more than
     // |kOpacityMaxChildCount| items.
     return false;
   }
 
   for (nsDisplayItem* i : *aList) {
@@ -4831,49 +5090,47 @@ static bool CollectItemsWithOpacity(WebR
     if (type == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO) {
       continue;
     }
 
     // Descend only into wraplists.
     if (type == DisplayItemType::TYPE_WRAP_LIST ||
         type == DisplayItemType::TYPE_CONTAINER) {
       // The current display item has children, process them first.
-      if (!CollectItemsWithOpacity(aManager, aBuilder, i->GetChildren(),
-                                   aArray)) {
+      if (!CollectItemsWithOpacity(i->GetChildren(), aArray)) {
         return false;
       }
 
       continue;
     }
 
     if (aArray.Length() == kOpacityMaxChildCount) {
       return false;
     }
 
     auto* item = i->AsPaintedDisplayItem();
-    if (!item || !item->CanApplyOpacity(aManager, aBuilder)) {
+    if (!item || !item->CanApplyOpacity()) {
       return false;
     }
 
     aArray.AppendElement(item);
   }
 
   return true;
 }
 
-bool nsDisplayOpacity::CanApplyToChildren(WebRenderLayerManager* aManager,
-                                          nsDisplayListBuilder* aBuilder) {
+bool nsDisplayOpacity::ApplyToChildren(nsDisplayListBuilder* aBuilder) {
   if (mChildOpacityState == ChildOpacityState::Deferred) {
     return false;
   }
 
   // Iterate through the child display list and copy at most
   // |kOpacityMaxChildCount| child display item pointers to a temporary list.
   AutoTArray<nsPaintedDisplayItem*, kOpacityMaxChildCount> items;
-  if (!CollectItemsWithOpacity(aManager, aBuilder, &mList, items)) {
+  if (!CollectItemsWithOpacity(&mList, items)) {
     mChildOpacityState = ChildOpacityState::Deferred;
     return false;
   }
 
   struct {
     nsPaintedDisplayItem* item{};
     nsRect bounds;
   } children[kOpacityMaxChildCount];
@@ -4890,47 +5147,52 @@ bool nsDisplayOpacity::CanApplyToChildre
     for (size_t j = i + 1; j < childCount; j++) {
       if (children[i].bounds.Intersects(children[j].bounds)) {
         mChildOpacityState = ChildOpacityState::Deferred;
         return false;
       }
     }
   }
 
+  for (uint32_t i = 0; i < childCount; i++) {
+    children[i].item->ApplyOpacity(aBuilder, mOpacity, mClipChain);
+  }
+
   mChildOpacityState = ChildOpacityState::Applied;
   return true;
 }
 
 /**
  * Returns true if this nsDisplayOpacity contains only a filter or a mask item
  * that has the same frame as the opacity item, and that supports painting with
  * opacity. In this case the opacity item can be optimized away.
  */
-bool nsDisplayOpacity::ApplyToMask() {
+bool nsDisplayOpacity::ApplyToFilterOrMask(const bool aUsingLayers) {
   if (mList.Count() != 1) {
     return false;
   }
 
   nsDisplayItem* item = mList.GetBottom();
   if (item->Frame() != mFrame) {
     // The effect item needs to have the same frame as the opacity item.
     return false;
   }
 
   const DisplayItemType type = item->GetType();
-  if (type == DisplayItemType::TYPE_MASK) {
+  if (type == DisplayItemType::TYPE_MASK ||
+      type == DisplayItemType::TYPE_FILTER) {
+    auto* filterOrMaskItem = static_cast<nsDisplayEffectsBase*>(item);
+    filterOrMaskItem->SelectOpacityOptimization(aUsingLayers);
     return true;
   }
 
   return false;
 }
 
-bool nsDisplayOpacity::CanApplyOpacityToChildren(
-    WebRenderLayerManager* aManager, nsDisplayListBuilder* aBuilder,
-    float aInheritedOpacity) {
+bool nsDisplayOpacity::ShouldFlattenAway(nsDisplayListBuilder* aBuilder) {
   if (mFrame->GetPrevContinuation() || mFrame->GetNextContinuation() ||
       mFrame->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)) {
     // If we've been split, then we might need to merge, so
     // don't flatten us away.
     return false;
   }
 
   if (mNeedsActiveLayer || mOpacity == 0.0) {
@@ -4940,27 +5202,26 @@ bool nsDisplayOpacity::CanApplyOpacityTo
     // might trigger repainting).
     return false;
   }
 
   if (mList.IsEmpty()) {
     return false;
   }
 
-  // We can only flatten opacity items into a mask if we haven't
-  // already flattened an earlier ancestor, since the SVG code pulls the opacity
-  // from style directly, and won't know about the outer opacity value.
-  if (aInheritedOpacity == 1.0f && ApplyToMask()) {
+  const bool usingLayers = !aBuilder->IsPaintingForWebRender();
+
+  if (ApplyToFilterOrMask(usingLayers)) {
     MOZ_ASSERT(SVGIntegrationUtils::UsingEffectsForFrame(mFrame));
     mChildOpacityState = ChildOpacityState::Applied;
     return true;
   }
 
   // Return true if we successfully applied opacity to child items.
-  return CanApplyToChildren(aManager, aBuilder);
+  return ApplyToChildren(aBuilder);
 }
 
 void nsDisplayOpacity::ComputeInvalidationRegion(
     nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
     nsRegion* aInvalidRegion) const {
   const auto* geometry =
       static_cast<const nsDisplayOpacityGeometry*>(aGeometry);
 
@@ -4988,45 +5249,18 @@ void nsDisplayOpacity::WriteDebugInfo(st
 
   aStream << ")";
 }
 
 bool nsDisplayOpacity::CreateWebRenderCommands(
     wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
     const StackingContextHelper& aSc, RenderRootStateManager* aManager,
     nsDisplayListBuilder* aDisplayListBuilder) {
-  // If this is a new (not retained/reused) item, then we need to disable
-  // the display item cache for descendants, since it's possible that some of
-  // them got cached with a flattened opacity values., which may no longer be
-  // applied.
-  Maybe<AutoDisplayItemCacheSuppressor> cacheSuppressor;
-  if (!IsReused()) {
-    cacheSuppressor.emplace(aBuilder.GetDisplayItemCache());
-  }
-
-  if (CanApplyOpacityToChildren(aManager->LayerManager(), aDisplayListBuilder,
-                                aBuilder.GetInheritedOpacity())) {
-    // If all our children support handling the opacity directly, then push
-    // the opacity and clip onto the builder and skip creating a stacking
-    // context.
-    float oldOpacity = aBuilder.GetInheritedOpacity();
-    aBuilder.SetInheritedOpacity(oldOpacity * mOpacity);
-
-    aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
-        &mList, this, aDisplayListBuilder, aSc, aBuilder, aResources, false);
-
-    aBuilder.SetInheritedOpacity(oldOpacity);
-    return true;
-  }
-
   MOZ_ASSERT(mChildOpacityState != ChildOpacityState::Applied);
-  float oldOpacity = aBuilder.GetInheritedOpacity();
-  aBuilder.SetInheritedOpacity(1.0f);
-  float opacity = mOpacity * oldOpacity;
-  float* opacityForSC = &opacity;
+  float* opacityForSC = &mOpacity;
 
   uint64_t animationsId =
       AddAnimationsForWebRender(this, aManager, aDisplayListBuilder);
   wr::WrAnimationProperty prop{
       wr::WrAnimationType::Opacity,
       animationsId,
   };
 
@@ -5035,17 +5269,16 @@ bool nsDisplayOpacity::CreateWebRenderCo
   params.opacity = opacityForSC;
   params.clip =
       wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
   StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
                            params);
 
   aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
       &mList, this, aDisplayListBuilder, sc, aBuilder, aResources);
-  aBuilder.SetInheritedOpacity(oldOpacity);
   return true;
 }
 
 nsDisplayBlendMode::nsDisplayBlendMode(
     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
     StyleBlend aBlendMode, const ActiveScrolledRoot* aActiveScrolledRoot,
     const bool aIsForBackground)
     : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot, true),
@@ -5196,16 +5429,24 @@ nsDisplayOwnLayer::nsDisplayOwnLayer(
     bool aForceActive, bool aClearClipChain)
     : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot,
                         aClearClipChain),
       mFlags(aFlags),
       mScrollbarData(aScrollbarData),
       mForceActive(aForceActive),
       mWrAnimationId(0) {
   MOZ_COUNT_CTOR(nsDisplayOwnLayer);
+
+  // For scroll thumb layers, override the AGR to be the thumb's AGR rather
+  // than the AGR for mFrame (which is the slider frame).
+  if (IsScrollThumbLayer()) {
+    if (nsIFrame* thumbFrame = nsIFrame::GetChildXULBox(mFrame)) {
+      mAnimatedGeometryRoot = aBuilder->FindAnimatedGeometryRootFor(thumbFrame);
+    }
+  }
 }
 
 bool nsDisplayOwnLayer::IsScrollThumbLayer() const {
   return mScrollbarData.mScrollbarLayerType == ScrollbarLayerType::Thumb;
 }
 
 bool nsDisplayOwnLayer::IsScrollbarContainer() const {
   return mScrollbarData.mScrollbarLayerType == ScrollbarLayerType::Container;
@@ -5362,16 +5603,23 @@ nsDisplaySubDocument::nsDisplaySubDocume
                                            nsDisplayOwnLayerFlags aFlags)
     : nsDisplayOwnLayer(aBuilder, aFrame, aList,
                         aBuilder->CurrentActiveScrolledRoot(), aFlags),
       mScrollParentId(aBuilder->GetCurrentScrollParentId()),
       mShouldFlatten(false),
       mSubDocFrame(aSubDocFrame) {
   MOZ_COUNT_CTOR(nsDisplaySubDocument);
 
+  // The SubDocument display item is conceptually outside the viewport frame,
+  // so in cases where the viewport frame is an AGR, the SubDocument's AGR
+  // should be not the viewport frame itself, but its parent AGR.
+  if (*mAnimatedGeometryRoot == mFrame && mAnimatedGeometryRoot->mParentAGR) {
+    mAnimatedGeometryRoot = mAnimatedGeometryRoot->mParentAGR;
+  }
+
   if (mSubDocFrame && mSubDocFrame != mFrame) {
     mSubDocFrame->AddDisplayItem(this);
   }
 }
 
 nsDisplaySubDocument::~nsDisplaySubDocument() {
   MOZ_COUNT_DTOR(nsDisplaySubDocument);
   if (mSubDocFrame) {
@@ -5455,26 +5703,35 @@ nsDisplayFixedPosition* nsDisplayFixedPo
 nsDisplayFixedPosition::nsDisplayFixedPosition(
     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
     const ActiveScrolledRoot* aActiveScrolledRoot,
     const ActiveScrolledRoot* aContainerASR)
     : nsDisplayOwnLayer(aBuilder, aFrame, aList, aActiveScrolledRoot),
       mContainerASR(aContainerASR),
       mIsFixedBackground(false) {
   MOZ_COUNT_CTOR(nsDisplayFixedPosition);
+  Init(aBuilder);
 }
 
 nsDisplayFixedPosition::nsDisplayFixedPosition(nsDisplayListBuilder* aBuilder,
                                                nsIFrame* aFrame,
                                                nsDisplayList* aList)
     : nsDisplayOwnLayer(aBuilder, aFrame, aList,
                         aBuilder->CurrentActiveScrolledRoot()),
       mContainerASR(nullptr),  // XXX maybe this should be something?
       mIsFixedBackground(true) {
   MOZ_COUNT_CTOR(nsDisplayFixedPosition);
+  Init(aBuilder);
+}
+
+void nsDisplayFixedPosition::Init(nsDisplayListBuilder* aBuilder) {
+  mAnimatedGeometryRootForScrollMetadata = mAnimatedGeometryRoot;
+  if (ShouldFixToViewport(aBuilder)) {
+    mAnimatedGeometryRoot = aBuilder->FindAnimatedGeometryRootFor(this);
+  }
 }
 
 ScrollableLayerGuid::ViewID nsDisplayFixedPosition::GetScrollTargetId() {
   if (mContainerASR && !nsLayoutUtils::IsReallyFixedPos(mFrame)) {
     return mContainerASR->GetViewId();
   }
   return nsLayoutUtils::ScrollIdForRootScrollFrame(mFrame->PresContext());
 }
@@ -5562,16 +5819,31 @@ nsDisplayStickyPosition::nsDisplaySticky
     const ActiveScrolledRoot* aActiveScrolledRoot,
     const ActiveScrolledRoot* aContainerASR, bool aClippedToDisplayPort)
     : nsDisplayOwnLayer(aBuilder, aFrame, aList, aActiveScrolledRoot),
       mContainerASR(aContainerASR),
       mClippedToDisplayPort(aClippedToDisplayPort) {
   MOZ_COUNT_CTOR(nsDisplayStickyPosition);
 }
 
+void nsDisplayStickyPosition::SetClipChain(
+    const DisplayItemClipChain* aClipChain, bool aStore) {
+  mClipChain = aClipChain;
+  mClip = nullptr;
+
+  MOZ_ASSERT(!mClip,
+             "There should never be a clip on this item because no clip moves "
+             "with it.");
+
+  if (aStore) {
+    mState.mClipChain = aClipChain;
+    mState.mClip = mClip;
+  }
+}
+
 // Returns the smallest distance from "0" to the range [min, max] where
 // min <= max. Despite the name, the return value is actually a 1-D vector,
 // and so may be negative if max < 0.
 static nscoord DistanceToRange(nscoord min, nscoord max) {
   MOZ_ASSERT(min <= max);
   if (max < 0) {
     return max;
   }
@@ -5653,18 +5925,17 @@ bool nsDisplayStickyPosition::CreateWebR
     wr::StickyOffsetBounds hBounds = {0.0, 0.0};
     nsPoint appliedOffset;
 
     nsRectAbsolute outer;
     nsRectAbsolute inner;
     stickyScrollContainer->GetScrollRanges(mFrame, &outer, &inner);
 
     nsIFrame* scrollFrame = do_QueryFrame(stickyScrollContainer->ScrollFrame());
-    nsPoint offset =
-        scrollFrame->GetOffsetToCrossDoc(Frame()) + ToReferenceFrame();
+    nsPoint offset = scrollFrame->GetOffsetToCrossDoc(ReferenceFrame());
 
     // Adjust the scrollPort coordinates to be relative to the reference frame,
     // so that it is in the same space as everything else.
     nsRect scrollPort =
         stickyScrollContainer->ScrollFrame()->GetScrollPortRect();
     scrollPort += offset;
 
     // The following computations make more sense upon understanding the
@@ -5873,19 +6144,19 @@ nsDisplayScrollInfoLayer::nsDisplayScrol
 #ifdef NS_BUILD_REFCNT_LOGGING
   MOZ_COUNT_CTOR(nsDisplayScrollInfoLayer);
 #endif
 }
 
 UniquePtr<ScrollMetadata> nsDisplayScrollInfoLayer::ComputeScrollMetadata(
     nsDisplayListBuilder* aBuilder, WebRenderLayerManager* aLayerManager) {
   ScrollMetadata metadata = nsLayoutUtils::ComputeScrollMetadata(
-      mScrolledFrame, mScrollFrame, mScrollFrame->GetContent(), Frame(),
-      ToReferenceFrame(), aLayerManager, mScrollParentId,
-      mScrollFrame->GetSize(), false);
+      mScrolledFrame, mScrollFrame, mScrollFrame->GetContent(),
+      ReferenceFrame(), aLayerManager, mScrollParentId, mScrollFrame->GetSize(),
+      false);
   metadata.GetMetrics().SetIsScrollInfoLayer(true);
   nsIScrollableFrame* scrollableFrame = mScrollFrame->GetScrollTargetFrame();
   if (scrollableFrame) {
     aBuilder->AddScrollFrameToNotify(scrollableFrame);
   }
 
   return UniquePtr<ScrollMetadata>(new ScrollMetadata(metadata));
 }
@@ -6002,45 +6273,51 @@ static_assert(sizeof(nsDisplayTransform)
               "nsDisplayTransform has grown");
 #endif
 
 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
                                        nsIFrame* aFrame, nsDisplayList* aList,
                                        const nsRect& aChildrenBuildingRect)
     : nsPaintedDisplayItem(aBuilder, aFrame),
       mTransform(Some(Matrix4x4())),
+      mAnimatedGeometryRootForChildren(mAnimatedGeometryRoot),
+      mAnimatedGeometryRootForScrollMetadata(mAnimatedGeometryRoot),
       mChildrenBuildingRect(aChildrenBuildingRect),
       mPrerenderDecision(PrerenderDecision::No),
       mIsTransformSeparator(true),
       mHasTransformGetter(false) {
   MOZ_COUNT_CTOR(nsDisplayTransform);
   MOZ_ASSERT(aFrame, "Must have a frame!");
   Init(aBuilder, aList);
 }
 
 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
                                        nsIFrame* aFrame, nsDisplayList* aList,
                                        const nsRect& aChildrenBuildingRect,
                                        PrerenderDecision aPrerenderDecision)
     : nsPaintedDisplayItem(aBuilder, aFrame),
+      mAnimatedGeometryRootForChildren(mAnimatedGeometryRoot),
+      mAnimatedGeometryRootForScrollMetadata(mAnimatedGeometryRoot),
       mChildrenBuildingRect(aChildrenBuildingRect),
       mPrerenderDecision(aPrerenderDecision),
       mIsTransformSeparator(false),
       mHasTransformGetter(false) {
   MOZ_COUNT_CTOR(nsDisplayTransform);
   MOZ_ASSERT(aFrame, "Must have a frame!");
   SetReferenceFrameToAncestor(aBuilder);
   Init(aBuilder, aList);
 }
 
 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
                                        nsIFrame* aFrame, nsDisplayList* aList,
                                        const nsRect& aChildrenBuildingRect,
                                        decltype(WithTransformGetter))
     : nsPaintedDisplayItem(aBuilder, aFrame),
+      mAnimatedGeometryRootForChildren(mAnimatedGeometryRoot),
+      mAnimatedGeometryRootForScrollMetadata(mAnimatedGeometryRoot),
       mChildrenBuildingRect(aChildrenBuildingRect),
       mPrerenderDecision(PrerenderDecision::No),
       mIsTransformSeparator(false),
       mHasTransformGetter(true) {
   MOZ_COUNT_CTOR(nsDisplayTransform);
   MOZ_ASSERT(aFrame, "Must have a frame!");
   MOZ_ASSERT(aFrame->GetTransformGetter());
   Init(aBuilder, aList);
@@ -6058,23 +6335,52 @@ void nsDisplayTransform::SetReferenceFra
   // our visible/building rects, since those should still include the additional
   // offset.
   // TODO: Are there are things computed using our ToReferenceFrame that should
   // have the additional offset applied? Should we instead just manually remove
   // the offset from our transform instead of this more general value?
   // Can we instead apply the additional offset to us and not our children, like
   // we do for all other offsets (and how reference frames are supposed to
   // work)?
+#ifdef DEBUG
   nsIFrame* outerFrame = nsLayoutUtils::GetCrossDocParentFrameInProcess(mFrame);
-  const nsIFrame* referenceFrame = aBuilder->FindReferenceFrameFor(outerFrame);
-  mToReferenceFrame = mFrame->GetOffsetToCrossDoc(referenceFrame);
+  MOZ_ASSERT(mReferenceFrame == aBuilder->FindReferenceFrameFor(outerFrame));
+#endif
+  mToReferenceFrame = mFrame->GetOffsetToCrossDoc(mReferenceFrame);
+
+  if (DisplayPortUtils::IsFixedPosFrameInDisplayPort(mFrame)) {
+    // This is an odd special case. If we are both IsFixedPosFrameInDisplayPort
+    // and transformed that we are our own AGR parent.
+    // We want our frame to be our AGR because FrameLayerBuilder uses our AGR to
+    // determine if we are inside a fixed pos subtree. If we use the outer AGR
+    // from outside the fixed pos subtree FLB can't tell that we are fixed pos.
+    mAnimatedGeometryRoot = mAnimatedGeometryRootForChildren;
+  } else if (mFrame->StyleDisplay()->mPosition ==
+                 StylePositionProperty::Sticky &&
+             IsStickyFrameActive(aBuilder, mFrame, nullptr)) {
+    // Similar to the IsFixedPosFrameInDisplayPort case we are our own AGR.
+    // We are inside the sticky position, so our AGR is the sticky positioned
+    // frame, which is our AGR, not the parent AGR.
+    mAnimatedGeometryRoot = mAnimatedGeometryRootForChildren;
+  } else if (mAnimatedGeometryRoot->mParentAGR) {
+    mAnimatedGeometryRootForScrollMetadata = mAnimatedGeometryRoot->mParentAGR;
+    if (!MayBeAnimated(aBuilder)) {
+      // If we're an animated transform then we want the same AGR as our
+      // children so that FrameLayerBuilder knows that this layer moves with the
+      // transform and won't compute occlusions. If we're not animated then use
+      // our parent AGR so that inactive transform layers can go in the same
+      // PaintedLayer as surrounding content.
+      mAnimatedGeometryRoot = mAnimatedGeometryRoot->mParentAGR;
+    }
+  }
 }
 
 void nsDisplayTransform::Init(nsDisplayListBuilder* aBuilder,
                               nsDisplayList* aChildren) {
+  mShouldFlatten = false;
   mChildren.AppendToTop(aChildren);
   UpdateBounds(aBuilder);
 }
 
 bool nsDisplayTransform::ShouldFlattenAway(nsDisplayListBuilder* aBuilder) {
   return false;
 }
 
@@ -7329,16 +7635,18 @@ void nsDisplayTransform::WriteDebugInfo(
 
 nsDisplayPerspective::nsDisplayPerspective(nsDisplayListBuilder* aBuilder,
                                            nsIFrame* aFrame,
                                            nsDisplayList* aList)
     : nsPaintedDisplayItem(aBuilder, aFrame) {
   mList.AppendToTop(aList);
   MOZ_ASSERT(mList.Count() == 1);
   MOZ_ASSERT(mList.GetTop()->GetType() == DisplayItemType::TYPE_TRANSFORM);
+  mAnimatedGeometryRoot = aBuilder->FindAnimatedGeometryRootFor(
+      mFrame->GetClosestFlattenedTreeAncestorPrimaryFrame());
 }
 
 void nsDisplayPerspective::Paint(nsDisplayListBuilder* aBuilder,
                                  gfxContext* aCtx) {
   // Just directly recurse into children, since we'll include the persepctive
   // value in any nsDisplayTransform children.
   GetChildren()->Paint(aBuilder, aCtx,
                        mFrame->PresContext()->AppUnitsPerDevPixel());
@@ -7447,28 +7755,26 @@ bool nsDisplayPerspective::CreateWebRend
       GetChildren(), this, aDisplayListBuilder, sc, aBuilder, aResources);
 
   return true;
 }
 
 nsDisplayText::nsDisplayText(nsDisplayListBuilder* aBuilder,
                              nsTextFrame* aFrame)
     : nsPaintedDisplayItem(aBuilder, aFrame),
+      mOpacity(1.0f),
       mVisIStartEdge(0),
       mVisIEndEdge(0) {
   MOZ_COUNT_CTOR(nsDisplayText);
   mBounds = mFrame->InkOverflowRectRelativeToSelf() + ToReferenceFrame();
   // Bug 748228
   mBounds.Inflate(mFrame->PresContext()->AppUnitsPerDevPixel());
-  mVisibleRect = aBuilder->GetVisibleRect() +
-                 aBuilder->GetCurrentFrameOffsetToReferenceFrame();
-}
-
-bool nsDisplayText::CanApplyOpacity(WebRenderLayerManager* aManager,
-                                    nsDisplayListBuilder* aBuilder) const {
+}
+
+bool nsDisplayText::CanApplyOpacity() const {
   auto* f = static_cast<nsTextFrame*>(mFrame);
 
   if (f->IsSelected()) {
     return false;
   }
 
   const nsStyleText* textStyle = f->StyleText();
   if (textStyle->HasTextShadow()) {
@@ -7478,20 +7784,20 @@ bool nsDisplayText::CanApplyOpacity(WebR
   nsTextFrame::TextDecorations decorations;
   f->GetTextDecorations(f->PresContext(), nsTextFrame::eResolvedColors,
                         decorations);
   return !decorations.HasDecorationLines();
 }
 
 void nsDisplayText::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
   AUTO_PROFILER_LABEL("nsDisplayText::Paint", GRAPHICS);
-  // We don't pass mVisibleRect here, since this can be called from within
-  // the WebRender fallback painting path, and we don't want to issue
-  // recorded commands that are dependent on the visible/building rect.
-  RenderToContext(aCtx, aBuilder, GetPaintRect(aBuilder, aCtx));
+
+  DrawTargetAutoDisableSubpixelAntialiasing disable(aCtx->GetDrawTarget(),
+                                                    IsSubpixelAADisabled());
+  RenderToContext(aCtx, aBuilder);
 }
 
 bool nsDisplayText::CreateWebRenderCommands(
     wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
     const StackingContextHelper& aSc, RenderRootStateManager* aManager,
     nsDisplayListBuilder* aDisplayListBuilder) {
   auto* f = static_cast<nsTextFrame*>(mFrame);
   auto appUnitsPerDevPixel = f->PresContext()->AppUnitsPerDevPixel();
@@ -7531,52 +7837,50 @@ bool nsDisplayText::CreateWebRenderComma
 
   // Clipping the bounds to the PaintRect (factoring in what's covered by parent
   // frames) let's us early reject a bunch of things, but it can produce
   // incorrect results for shadows, because they can translate things back into
   // view. Also if we're selected we might have some shadows from the
   // ::selected and ::inctive-selected pseudo-selectors. So don't do this
   // optimization if we have shadows or a selection.
   if (!(f->IsSelected() || f->StyleText()->HasTextShadow())) {
-    nsRect visible = mVisibleRect;
+    nsRect visible = GetPaintRect();
     visible.Inflate(3 * appUnitsPerDevPixel);
     bounds = bounds.Intersect(visible);
   }
 
   RefPtr<gfxContext> textDrawer = aBuilder.GetTextContext(
       aResources, aSc, aManager, this, bounds, deviceOffset);
 
   aBuilder.StartGroup(this);
 
-  RenderToContext(textDrawer, aDisplayListBuilder, mVisibleRect,
-                  aBuilder.GetInheritedOpacity(), true);
+  RenderToContext(textDrawer, aDisplayListBuilder, true);
   const bool result = textDrawer->GetTextDrawer()->Finish();
 
   if (result) {
     aBuilder.FinishGroup();
   } else {
     aBuilder.CancelGroup(true);
   }
 
   return result;
 }
 
 void nsDisplayText::RenderToContext(gfxContext* aCtx,
                                     nsDisplayListBuilder* aBuilder,
-                                    const nsRect& aVisibleRect, float aOpacity,
                                     bool aIsRecording) {
   nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
 
   // Add 1 pixel of dirty area around mVisibleRect to allow us to paint
   // antialiased pixels beyond the measured text extents.
   // This is temporary until we do this in the actual calculation of text
   // extents.
   auto A2D = mFrame->PresContext()->AppUnitsPerDevPixel();
   LayoutDeviceRect extraVisible =
-      LayoutDeviceRect::FromAppUnits(aVisibleRect, A2D);
+      LayoutDeviceRect::FromAppUnits(GetPaintRect(), A2D);
   extraVisible.Inflate(1);
 
   gfxRect pixelVisible(extraVisible.x, extraVisible.y, extraVisible.width,
                        extraVisible.height);
   pixelVisible.Inflate(2);
   pixelVisible.RoundOut();
 
   bool willClip = !aBuilder->IsForGenerateGlyphMask() && !aIsRecording;
@@ -7618,43 +7922,45 @@ void nsDisplayText::RenderToContext(gfxC
 
   if (aBuilder->IsForGenerateGlyphMask()) {
     params.state = nsTextFrame::PaintTextParams::GenerateTextMask;
   } else {
     params.state = nsTextFrame::PaintTextParams::PaintText;
   }
 
   f->PaintText(params, mVisIStartEdge, mVisIEndEdge, ToReferenceFrame(),
-               f->IsSelected(), aOpacity);
+               f->IsSelected(), mOpacity);
 
   if (willClip) {
     aCtx->PopClip();
   }
 }
 
 // This could go to nsDisplayListInvalidation.h, but
 // |nsTextFrame::TextDecorations| requires including of nsTextFrame.h which
 // would produce circular dependencies.
 class nsDisplayTextGeometry : public nsDisplayItemGenericGeometry {
  public:
   nsDisplayTextGeometry(nsDisplayText* aItem, nsDisplayListBuilder* aBuilder)
       : nsDisplayItemGenericGeometry(aItem, aBuilder),
+        mOpacity(aItem->Opacity()),
         mVisIStartEdge(aItem->VisIStartEdge()),
         mVisIEndEdge(aItem->VisIEndEdge()) {
     nsTextFrame* f = static_cast<nsTextFrame*>(aItem->Frame());
     f->GetTextDecorations(f->PresContext(), nsTextFrame::eResolvedColors,
                           mDecorations);
   }
 
   /**
    * We store the computed text decorations here since they are
    * computed using style data from parent frames. Any changes to these
    * styles will only invalidate the parent frame and not this frame.
    */
   nsTextFrame::TextDecorations mDecorations;
+  float mOpacity;
   nscoord mVisIStartEdge;
   nscoord mVisIEndEdge;
 };
 
 nsDisplayItemGeometry* nsDisplayText::AllocateGeometry(
     nsDisplayListBuilder* aBuilder) {
   return new nsDisplayTextGeometry(this, aBuilder);
 }
@@ -7672,17 +7978,18 @@ void nsDisplayText::ComputeInvalidationR
 
   bool snap;
   const nsRect& newRect = geometry->mBounds;
   nsRect oldRect = GetBounds(aBuilder, &snap);
   if (decorations != geometry->mDecorations ||
       mVisIStartEdge != geometry->mVisIStartEdge ||
       mVisIEndEdge != geometry->mVisIEndEdge ||
       !oldRect.IsEqualInterior(newRect) ||
-      !geometry->mBorderRect.IsEqualInterior(GetBorderRect())) {
+      !geometry->mBorderRect.IsEqualInterior(GetBorderRect()) ||
+      mOpacity != geometry->mOpacity) {
     aInvalidRegion->Or(oldRect, newRect);
   }
 }
 
 void nsDisplayText::WriteDebugInfo(std::stringstream& aStream) {
 #ifdef DEBUG
   aStream << " (\"";
 
@@ -7694,24 +8001,25 @@ void nsDisplayText::WriteDebugInfo(std::
   aStream << buf.get() << "\")";
 #endif
 }
 
 nsDisplayEffectsBase::nsDisplayEffectsBase(
     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
     const ActiveScrolledRoot* aActiveScrolledRoot, bool aClearClipChain)
     : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot,
-                        aClearClipChain) {
+                        aClearClipChain),
+      mHandleOpacity(false) {
   MOZ_COUNT_CTOR(nsDisplayEffectsBase);
 }
 
 nsDisplayEffectsBase::nsDisplayEffectsBase(nsDisplayListBuilder* aBuilder,
                                            nsIFrame* aFrame,
                                            nsDisplayList* aList)
-    : nsDisplayWrapList(aBuilder, aFrame, aList) {
+    : nsDisplayWrapList(aBuilder, aFrame, aList), mHandleOpacity(false) {
   MOZ_COUNT_CTOR(nsDisplayEffectsBase);
 }
 
 nsRegion nsDisplayEffectsBase::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                                                bool* aSnap) const {
   *aSnap = false;
   return nsRegion();
 }
@@ -7738,17 +8046,19 @@ void nsDisplayEffectsBase::ComputeInvali
     nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
     nsRegion* aInvalidRegion) const {
   const auto* geometry =
       static_cast<const nsDisplaySVGEffectGeometry*>(aGeometry);
   bool snap;
   nsRect bounds = GetBounds(aBuilder, &snap);
   if (geometry->mFrameOffsetToReferenceFrame != ToReferenceFrame() ||
       geometry->mUserSpaceOffset != UserSpaceOffset() ||
-      !geometry->mBBox.IsEqualInterior(BBoxInUserSpace())) {
+      !geometry->mBBox.IsEqualInterior(BBoxInUserSpace()) ||
+      geometry->mOpacity != mFrame->StyleEffects()->mOpacity ||
+      geometry->mHandleOpacity != ShouldHandleOpacity()) {
     // Filter and mask output can depend on the location of the frame's user
     // space and on the frame's BBox. We need to invalidate if either of these
     // change relative to the reference frame.
     // Invalidations from our inactive layer manager are not enough to catch
     // some of these cases because filters can produce output even if there's
     // nothing in the filter input.
     aInvalidRegion->Or(bounds, geometry->mBounds);
   }
@@ -7832,17 +8142,18 @@ static void ComputeMaskGeometry(PaintFra
   } else {
     aParams.maskRect = Nothing();
   }
 }
 
 nsDisplayMasksAndClipPaths::nsDisplayMasksAndClipPaths(
     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
     const ActiveScrolledRoot* aActiveScrolledRoot)
-    : nsDisplayEffectsBase(aBuilder, aFrame, aList, aActiveScrolledRoot, true) {
+    : nsDisplayEffectsBase(aBuilder, aFrame, aList, aActiveScrolledRoot, true),
+      mApplyOpacityWithSimpleClipPath(false) {
   MOZ_COUNT_CTOR(nsDisplayMasksAndClipPaths);
 
   nsPresContext* presContext = mFrame->PresContext();
   uint32_t flags =
       aBuilder->GetBackgroundPaintFlags() | nsCSSRendering::PAINTBG_MASK_IMAGE;
   const nsStyleSVGReset* svgReset = aFrame->StyleSVGReset();
   NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, svgReset->mMask) {
     const auto& layer = svgReset->mMask.mLayers[i];
@@ -7889,36 +8200,39 @@ bool nsDisplayMasksAndClipPaths::CanMerg
          CanMergeDisplayMaskFrame(aItem->Frame());
 }
 
 bool nsDisplayMasksAndClipPaths::IsValidMask() {
   if (!ValidateSVGFrame()) {
     return false;
   }
 
+  if (mFrame->StyleEffects()->mOpacity == 0.0f && mHandleOpacity) {
+    return false;
+  }
+
   nsIFrame* firstFrame =
       nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame);
 
   return !(SVGObserverUtils::GetAndObserveClipPath(firstFrame, nullptr) ==
                SVGObserverUtils::eHasRefsSomeInvalid ||
            SVGObserverUtils::GetAndObserveMasks(firstFrame, nullptr) ==
                SVGObserverUtils::eHasRefsSomeInvalid);
 }
 
 bool nsDisplayMasksAndClipPaths::PaintMask(nsDisplayListBuilder* aBuilder,
                                            gfxContext* aMaskContext,
-                                           bool aHandleOpacity,
                                            bool* aMaskPainted) {
   MOZ_ASSERT(aMaskContext->GetDrawTarget()->GetFormat() == SurfaceFormat::A8);
 
   imgDrawingParams imgParams(aBuilder->GetImageDecodeFlags());
   nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
   SVGIntegrationUtils::PaintFramesParams params(*aMaskContext, mFrame, mBounds,
                                                 borderArea, aBuilder, nullptr,
-                                                aHandleOpacity, imgParams);
+                                                mHandleOpacity, imgParams);
   ComputeMaskGeometry(params);
   bool maskIsComplete = false;
   bool painted = SVGIntegrationUtils::PaintMask(params, maskIsComplete);
   if (aMaskPainted) {
     *aMaskPainted = painted;
   }
 
   nsDisplayMasksAndClipPathsGeometry::UpdateDrawResult(this, imgParams.result);
@@ -7988,26 +8302,26 @@ void nsDisplayMasksAndClipPaths::Compute
 
 void nsDisplayMasksAndClipPaths::PaintWithContentsPaintCallback(
     nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
     const std::function<void()>& aPaintChildren) {
   // Clip the drawing target by mVisibleRect, which contains the visible
   // region of the target frame and its out-of-flow and inflow descendants.
   gfxContext* context = aCtx;
 
-  Rect bounds = NSRectToRect(GetPaintRect(aBuilder, aCtx),
+  Rect bounds = NSRectToRect(GetPaintRect(),
                              mFrame->PresContext()->AppUnitsPerDevPixel());
   bounds.RoundOut();
   context->Clip(bounds);
 
   imgDrawingParams imgParams(aBuilder->GetImageDecodeFlags());
   nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
-  SVGIntegrationUtils::PaintFramesParams params(
-      *aCtx, mFrame, GetPaintRect(aBuilder, aCtx), borderArea, aBuilder,
-      nullptr, false, imgParams);
+  SVGIntegrationUtils::PaintFramesParams params(*aCtx, mFrame, GetPaintRect(),
+                                                borderArea, aBuilder, nullptr,
+                                                mHandleOpacity, imgParams);
 
   ComputeMaskGeometry(params);
 
   SVGIntegrationUtils::PaintMaskAndClipPath(params, aPaintChildren);
 
   context->PopClip();
 
   nsDisplayMasksAndClipPathsGeometry::UpdateDrawResult(this, imgParams.result);
@@ -8175,49 +8489,56 @@ bool nsDisplayMasksAndClipPaths::CreateW
   auto appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
   nsRect displayBounds = GetBounds(aDisplayListBuilder, &snap);
   LayoutDeviceRect bounds =
       LayoutDeviceRect::FromAppUnits(displayBounds, appUnitsPerDevPixel);
 
   Maybe<wr::WrClipId> clip = CreateWRClipPathAndMasks(
       this, bounds, aResources, aBuilder, aSc, aManager, aDisplayListBuilder);
 
-  float oldOpacity = aBuilder.GetInheritedOpacity();
-
   Maybe<StackingContextHelper> layer;
   const StackingContextHelper* sc = &aSc;
   if (clip) {
     // Create a new stacking context to attach the mask to, ensuring the mask is
     // applied to the aggregate, and not the individual elements.
 
     // The stacking context shouldn't have any offset.
     bounds.MoveTo(0, 0);
 
-    Maybe<float> opacity =
-        (SVGIntegrationUtils::UsingSimpleClipPathForFrame(mFrame) &&
-         aBuilder.GetInheritedOpacity() != 1.0f)
-            ? Some(aBuilder.GetInheritedOpacity())
-            : Nothing();
+    Maybe<float> opacity = mApplyOpacityWithSimpleClipPath
+                               ? Some(mFrame->StyleEffects()->mOpacity)
+                               : Nothing();
 
     wr::StackingContextParams params;
     params.clip = wr::WrStackingContextClip::ClipId(*clip);
     params.opacity = opacity.ptrOr(nullptr);
     layer.emplace(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder, params,
                   bounds);
     sc = layer.ptr();
   }
 
-  aBuilder.SetInheritedOpacity(1.0f);
   nsDisplayEffectsBase::CreateWebRenderCommands(aBuilder, aResources, *sc,
                                                 aManager, aDisplayListBuilder);
-  aBuilder.SetInheritedOpacity(oldOpacity);
 
   return true;
 }
 
+void nsDisplayMasksAndClipPaths::SelectOpacityOptimization(
+    const bool aUsingLayers) {
+  if (aUsingLayers ||
+      !SVGIntegrationUtils::UsingSimpleClipPathForFrame(mFrame)) {
+    // Handle opacity in mask and clip-path drawing code.
+    SetHandleOpacity();
+    MOZ_ASSERT(!mApplyOpacityWithSimpleClipPath);
+  } else {
+    // Allow WebRender simple clip paths to also handle opacity.
+    mApplyOpacityWithSimpleClipPath = true;
+  }
+}
+
 Maybe<nsRect> nsDisplayMasksAndClipPaths::GetClipWithRespectToASR(
     nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR) const {
   if (const DisplayItemClip* clip =
           DisplayItemClipChain::ClipForASR(GetClipChain(), aASR)) {
     return Some(clip->GetClipRect());
   }
   // This item does not have a clip with respect to |aASR|. However, we
   // might still have finite bounds with respect to |aASR|. Check our
@@ -8233,16 +8554,20 @@ Maybe<nsRect> nsDisplayMasksAndClipPaths
 }
 
 #ifdef MOZ_DUMP_PAINTING
 void nsDisplayMasksAndClipPaths::PrintEffects(nsACString& aTo) {
   nsIFrame* firstFrame =
       nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame);
   bool first = true;
   aTo += " effects=(";
+  if (mHandleOpacity) {
+    first = false;
+    aTo += nsPrintfCString("opacity(%f)", mFrame->StyleEffects()->mOpacity);
+  }
   SVGClipPathFrame* clipPathFrame;
   // XXX Check return value?
   SVGObserverUtils::GetAndObserveClipPath(firstFrame, &clipPathFrame);
   if (clipPathFrame) {
     if (!first) {
       aTo += ", ";
     }
     aTo += nsPrintfCString(
@@ -8349,18 +8674,16 @@ void nsDisplayBackdropFilters::Paint(nsD
 }
 
 /* static */
 nsDisplayFilters::nsDisplayFilters(nsDisplayListBuilder* aBuilder,
                                    nsIFrame* aFrame, nsDisplayList* aList)
     : nsDisplayEffectsBase(aBuilder, aFrame, aList),
       mEffectsBounds(aFrame->InkOverflowRectRelativeToSelf()) {
   MOZ_COUNT_CTOR(nsDisplayFilters);
-  mVisibleRect = aBuilder->GetVisibleRect() +
-                 aBuilder->GetCurrentFrameOffsetToReferenceFrame();
 }
 
 void nsDisplayFilters::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
   PaintWithContentsPaintCallback(aBuilder, aCtx, [&](gfxContext* aContext) {
     GetChildren()->Paint(aBuilder, aContext,
                          mFrame->PresContext()->AppUnitsPerDevPixel());
   });
 }
@@ -8382,36 +8705,36 @@ void nsDisplayFilters::ComputeInvalidati
   }
 }
 
 void nsDisplayFilters::PaintWithContentsPaintCallback(
     nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
     const std::function<void(gfxContext* aContext)>& aPaintChildren) {
   imgDrawingParams imgParams(aBuilder->GetImageDecodeFlags());
   nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
-  SVGIntegrationUtils::PaintFramesParams params(*aCtx, mFrame, mVisibleRect,
+  SVGIntegrationUtils::PaintFramesParams params(*aCtx, mFrame, GetPaintRect(),
                                                 borderArea, aBuilder, nullptr,
-                                                false, imgParams);
+                                                mHandleOpacity, imgParams);
 
   gfxPoint userSpaceToFrameSpaceOffset =
       SVGIntegrationUtils::GetOffsetToUserSpaceInDevPx(mFrame, params);
 
   SVGIntegrationUtils::PaintFilter(
       params,
       [&](gfxContext& aContext, nsIFrame* aTarget, const gfxMatrix& aTransform,
           const nsIntRect* aDirtyRect, imgDrawingParams& aImgParams) {
         gfxContextMatrixAutoSaveRestore autoSR(&aContext);
         aContext.SetMatrixDouble(aContext.CurrentMatrixDouble().PreTranslate(
             -userSpaceToFrameSpaceOffset));
         aPaintChildren(&aContext);
       });
   nsDisplayFiltersGeometry::UpdateDrawResult(this, imgParams.result);
 }
 
-bool nsDisplayFilters::CanCreateWebRenderCommands() const {
+bool nsDisplayFilters::CanCreateWebRenderCommands() {
   return SVGIntegrationUtils::CanCreateWebRenderFiltersForFrame(mFrame);
 }
 
 bool nsDisplayFilters::CreateWebRenderCommands(
     wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
     const StackingContextHelper& aSc, RenderRootStateManager* aManager,
     nsDisplayListBuilder* aDisplayListBuilder) {
   float auPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
@@ -8432,39 +8755,41 @@ bool nsDisplayFilters::CreateWebRenderCo
         filterClip.value() + ToReferenceFrame(), auPerDevPixel);
     wr::WrClipId clipId =
         aBuilder.DefineRectClip(Nothing(), wr::ToLayoutRect(devPxRect));
     clip = wr::WrStackingContextClip::ClipId(clipId);
   } else {
     clip = wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
   }
 
-  float opacity = aBuilder.GetInheritedOpacity();
-  aBuilder.SetInheritedOpacity(1.0f);
+  float opacity = mFrame->StyleEffects()->mOpacity;
   wr::StackingContextParams params;
   params.mFilters = std::move(wrFilters.filters);
   params.mFilterDatas = std::move(wrFilters.filter_datas);
-  params.opacity = opacity != 1.0f ? &opacity : nullptr;
+  params.opacity = opacity != 1.0f && mHandleOpacity ? &opacity : nullptr;
   params.clip = clip;
   StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
                            params);
 
   nsDisplayEffectsBase::CreateWebRenderCommands(aBuilder, aResources, sc,
                                                 aManager, aDisplayListBuilder);
-  aBuilder.SetInheritedOpacity(opacity);
 
   return true;
 }
 
 #ifdef MOZ_DUMP_PAINTING
 void nsDisplayFilters::PrintEffects(nsACString& aTo) {
   nsIFrame* firstFrame =
       nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame);
   bool first = true;
   aTo += " effects=(";
+  if (mHandleOpacity) {
+    first = false;
+    aTo += nsPrintfCString("opacity(%f)", mFrame->StyleEffects()->mOpacity);
+  }
   // We may exist for a mix of CSS filter functions and/or references to SVG
   // filters.  If we have invalid references to SVG filters then we paint
   // nothing, but otherwise we will apply one or more filters.
   if (SVGObserverUtils::GetAndObserveFilters(firstFrame, nullptr) !=
       SVGObserverUtils::eHasRefsSomeInvalid) {
     if (!first) {
       aTo += ", ";
     }
@@ -8631,32 +8956,48 @@ nsDisplayListBuilder::AutoBuildingDispla
     const bool aIsTransformed)
     : mBuilder(aBuilder),
       mPrevFrame(aBuilder->mCurrentFrame),
       mPrevReferenceFrame(aBuilder->mCurrentReferenceFrame),
       mPrevOffset(aBuilder->mCurrentOffsetToReferenceFrame),
       mPrevAdditionalOffset(aBuilder->mAdditionalOffset),
       mPrevVisibleRect(aBuilder->mVisibleRect),
       mPrevDirtyRect(aBuilder->mDirtyRect),
+      mPrevAGR(aBuilder->mCurrentAGR),
       mPrevCompositorHitTestInfo(aBuilder->mCompositorHitTestInfo),
       mPrevAncestorHasApzAwareEventHandler(
           aBuilder->mAncestorHasApzAwareEventHandler),
       mPrevBuildingInvisibleItems(aBuilder->mBuildingInvisibleItems),
       mPrevInInvalidSubtree(aBuilder->mInInvalidSubtree) {
   if (aIsTransformed) {
     aBuilder->mCurrentOffsetToReferenceFrame =
         aBuilder->AdditionalOffset().refOr(nsPoint());
     aBuilder->mCurrentReferenceFrame = aForChild;
   } else if (aBuilder->mCurrentFrame == aForChild->GetParent()) {
     aBuilder->mCurrentOffsetToReferenceFrame += aForChild->GetPosition();
   } else {
     aBuilder->mCurrentReferenceFrame = aBuilder->FindReferenceFrameFor(
         aForChild, &aBuilder->mCurrentOffsetToReferenceFrame);
   }
 
+  bool isAsync;
+  mCurrentAGRState = aBuilder->IsAnimatedGeometryRoot(aForChild, isAsync);
+
+  if (aBuilder->mCurrentFrame == aForChild->GetParent()) {
+    if (mCurrentAGRState == AGR_YES) {
+      aBuilder->mCurrentAGR =
+          aBuilder->WrapAGRForFrame(aForChild, isAsync, aBuilder->mCurrentAGR);
+    }
+  } else if (aBuilder->mCurrentFrame != aForChild) {
+    aBuilder->mCurrentAGR = aBuilder->FindAnimatedGeometryRootFor(aForChild);
+  }
+
+  MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDocInProcess(
+      aBuilder->RootReferenceFrame(), *aBuilder->mCurrentAGR));
+
   // If aForChild is being visited from a frame other than it's ancestor frame,
   // mInInvalidSubtree will need to be recalculated the slow way.
   if (aForChild == mPrevFrame || GetAncestorFor(aForChild) == mPrevFrame) {
     aBuilder->mInInvalidSubtree =
         aBuilder->mInInvalidSubtree || aForChild->IsFrameModified();
   } else {
     aBuilder->mInInvalidSubtree = AnyContentAncestorModified(aForChild);
   }
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -148,16 +148,107 @@ enum class DisplayListArenaObjectId {
  * the subdocuments.
  *
  * Display item's coordinates are relative to their nearest reference frame
  * ancestor. Both the display root and any frame with a transform act as a
  * reference frame for their frame subtrees.
  */
 
 /**
+ * Represents a frame that is considered to have (or will have) "animated
+ * geometry" for itself and descendant frames.
+ *
+ * For example the scrolled frames of scrollframes which are actively being
+ * scrolled fall into this category. Frames with certain CSS properties that are
+ * being animated (e.g. 'left'/'top' etc) are also placed in this category.
+ * Frames with different active geometry roots are in different PaintedLayers,
+ * so that we can animate the geometry root by changing its transform (either on
+ * the main thread or in the compositor).
+ *
+ * nsDisplayListBuilder constructs a tree of these (for fast traversals) and
+ * assigns one for each display item.
+ *
+ * The animated geometry root for a display item is required to be a descendant
+ * (or equal to) the item's ReferenceFrame(), which means that we will fall back
+ * to returning aItem->ReferenceFrame() when we can't find another animated
+ * geometry root.
+ *
+ * The animated geometry root isn't strongly defined for a frame as transforms
+ * and background-attachment:fixed can cause it to vary between display items
+ * for a given frame.
+ */
+struct AnimatedGeometryRoot {
+  static already_AddRefed<AnimatedGeometryRoot> CreateAGRForFrame(
+      nsIFrame* aFrame, AnimatedGeometryRoot* aParent, bool aIsAsync,
+      bool aIsRetained) {
+    RefPtr<AnimatedGeometryRoot> result;
+    if (aIsRetained) {
+      result = aFrame->GetProperty(AnimatedGeometryRootCache());
+    }
+
+    if (result) {
+      result->mParentAGR = aParent;
+      result->mIsAsync = aIsAsync;
+    } else {
+      result = new AnimatedGeometryRoot(aFrame, aParent, aIsAsync, aIsRetained);
+    }
+    return result.forget();
+  }
+
+  operator nsIFrame*() { return mFrame; }
+
+  nsIFrame* operator->() const { return mFrame; }
+
+  AnimatedGeometryRoot* GetAsyncAGR() {
+    AnimatedGeometryRoot* agr = this;
+    while (!agr->mIsAsync && agr->mParentAGR) {
+      agr = agr->mParentAGR;
+    }
+    return agr;
+  }
+
+  NS_INLINE_DECL_REFCOUNTING(AnimatedGeometryRoot)
+
+  nsIFrame* mFrame;
+  RefPtr<AnimatedGeometryRoot> mParentAGR;
+  bool mIsAsync;
+  bool mIsRetained;
+
+ protected:
+  static void DetachAGR(AnimatedGeometryRoot* aAGR) {
+    aAGR->mFrame = nullptr;
+    aAGR->mParentAGR = nullptr;
+    NS_RELEASE(aAGR);
+  }
+
+  NS_DECLARE_FRAME_PROPERTY_WITH_DTOR(AnimatedGeometryRootCache,
+                                      AnimatedGeometryRoot, DetachAGR)
+
+  AnimatedGeometryRoot(nsIFrame* aFrame, AnimatedGeometryRoot* aParent,
+                       bool aIsAsync, bool aIsRetained)
+      : mFrame(aFrame),
+        mParentAGR(aParent),
+        mIsAsync(aIsAsync),
+        mIsRetained(aIsRetained) {
+    MOZ_ASSERT(mParentAGR || mIsAsync,
+               "The root AGR should always be treated as an async AGR.");
+    if (mIsRetained) {
+      NS_ADDREF(this);
+      aFrame->SetProperty(AnimatedGeometryRootCache(), this);
+    }
+  }
+
+  ~AnimatedGeometryRoot() {
+    if (mFrame && mIsRetained) {
+      mFrame->RemoveProperty(AnimatedGeometryRootCache());
+    }
+  }
+};
+
+/**
  * An active scrolled root (ASR) is similar to an animated geometry root (AGR).
  * The differences are:
  *  - ASRs are only created for async-scrollable scroll frames. This is a
  *    (hopefully) temporary restriction. In the future we will want to create
  *    ASRs for all the things that are currently creating AGRs, and then
  *    replace AGRs with ASRs and rename them from "active scrolled root" to
  *    "animated geometry root".
  *  - ASR objects are created during display list construction by the nsIFrames
@@ -299,16 +390,23 @@ class nsDisplayListBuilder {
     nsRect mAccumulatedRect;
     // How far this frame is from the root of the current 3d context.
     int mAccumulatedRectLevels;
     nsRect mVisibleRect;
     // Allow async animation for this 3D context.
     bool mAllowAsyncAnimation;
   };
 
+  /**
+   * A frame can be in one of three states of AGR.
+   * AGR_NO     means the frame is not an AGR for now.
+   * AGR_YES    means the frame is an AGR for now.
+   */
+  enum AGRState { AGR_NO, AGR_YES };
+
  public:
   using ViewID = layers::ScrollableLayerGuid::ViewID;
 
   /**
    * @param aReferenceFrame the frame at the root of the subtree; its origin
    * is the origin of the reference coordinate system for this display list
    * @param aMode encodes what the builder is being used for.
    * @param aBuildCaret whether or not we should include the caret in any
@@ -552,16 +650,21 @@ class nsDisplayListBuilder {
 
   const nsIFrame* GetCurrentFrame() { return mCurrentFrame; }
   const nsIFrame* GetCurrentReferenceFrame() { return mCurrentReferenceFrame; }
 
   const nsPoint& GetCurrentFrameOffsetToReferenceFrame() const {
     return mCurrentOffsetToReferenceFrame;
   }
 
+  AnimatedGeometryRoot* GetCurrentAnimatedGeometryRoot() { return mCurrentAGR; }
+  AnimatedGeometryRoot* GetRootAnimatedGeometryRoot() { return mRootAGR; }
+
+  void RecomputeCurrentAnimatedGeometryRoot();
+
   void Check() { mPool.Check(); }
 
   /**
    * Returns true if merging and flattening of display lists should be
    * performed while computing visibility.
    */
   bool AllowMergingAndFlattening() { return mAllowMergingAndFlattening; }
   void SetAllowMergingAndFlattening(bool aAllow) {
@@ -930,16 +1033,24 @@ class nsDisplayListBuilder {
       const DisplayItemClipChain* aLeafClip2);
 
   /**
    * Clone the supplied clip chain's chain items into this builder's arena.
    */
   const DisplayItemClipChain* CopyWholeChain(
       const DisplayItemClipChain* aClipChain);
 
+  /**
+   * Returns a new clip chain containing an intersection of all clips of
+   * |aClipChain| up to and including |aASR|.
+   * If there is no clip, returns nullptr.
+   */
+  const DisplayItemClipChain* FuseClipChainUpTo(
+      const DisplayItemClipChain* aClipChain, const ActiveScrolledRoot* aASR);
+
   const ActiveScrolledRoot* GetFilterASR() const { return mFilterASR; }
 
   /**
    * Merges the display items in |aMergedItems| and returns a new temporary
    * display item.
    * The display items in |aMergedItems| have to be mergeable with each other.
    */
   nsDisplayWrapList* MergeItems(nsTArray<nsDisplayWrapList*>& aItems);
@@ -970,42 +1081,47 @@ class nsDisplayListBuilder {
 
     void SetAdditionalOffset(const nsPoint& aOffset) {
       MOZ_ASSERT(!mBuilder->mAdditionalOffset);
       mBuilder->mAdditionalOffset = Some(aOffset);
 
       mBuilder->mCurrentOffsetToReferenceFrame += aOffset;
     }
 
+    bool IsAnimatedGeometryRoot() const { return mCurrentAGRState == AGR_YES; }
+
     void RestoreBuildingInvisibleItemsValue() {
       mBuilder->mBuildingInvisibleItems = mPrevBuildingInvisibleItems;
     }
 
     ~AutoBuildingDisplayList() {
       mBuilder->mCurrentFrame = mPrevFrame;
       mBuilder->mCurrentReferenceFrame = mPrevReferenceFrame;
       mBuilder->mCurrentOffsetToReferenceFrame = mPrevOffset;
       mBuilder->mVisibleRect = mPrevVisibleRect;
       mBuilder->mDirtyRect = mPrevDirtyRect;
+      mBuilder->mCurrentAGR = mPrevAGR;
       mBuilder->mAncestorHasApzAwareEventHandler =
           mPrevAncestorHasApzAwareEventHandler;
       mBuilder->mBuildingInvisibleItems = mPrevBuildingInvisibleItems;
       mBuilder->mInInvalidSubtree = mPrevInInvalidSubtree;
       mBuilder->mAdditionalOffset = mPrevAdditionalOffset;
       mBuilder->mCompositorHitTestInfo = mPrevCompositorHitTestInfo;
     }
 
    private:
     nsDisplayListBuilder* mBuilder;
+    AGRState mCurrentAGRState;
     const nsIFrame* mPrevFrame;
     const nsIFrame* mPrevReferenceFrame;
     nsPoint mPrevOffset;
     Maybe<nsPoint> mPrevAdditionalOffset;
     nsRect mPrevVisibleRect;
     nsRect mPrevDirtyRect;
+    RefPtr<AnimatedGeometryRoot> mPrevAGR;
     gfx::CompositorHitTestInfo mPrevCompositorHitTestInfo;
     bool mPrevAncestorHasApzAwareEventHandler;
     bool mPrevBuildingInvisibleItems;
     bool mPrevInInvalidSubtree;
   };
 
   /**
    * A helper class to temporarily set the value of mInTransform.
@@ -1381,17 +1497,17 @@ class nsDisplayListBuilder {
                                         mDirtyRect, aDirtyRect);
     }
   };
 
   NS_DECLARE_FRAME_PROPERTY_DELETABLE(OutOfFlowDisplayDataProperty,
                                       OutOfFlowDisplayData)
 
   struct DisplayListBuildingData {
-    nsIFrame* mModifiedAGR = nullptr;
+    RefPtr<AnimatedGeometryRoot> mModifiedAGR = nullptr;
     nsRect mDirtyRect;
   };
   NS_DECLARE_FRAME_PROPERTY_DELETABLE(DisplayListBuildingRect,
                                       DisplayListBuildingData)
 
   NS_DECLARE_FRAME_PROPERTY_DELETABLE(DisplayListBuildingDisplayPortRect,
                                       nsRect)
 
@@ -1572,16 +1688,23 @@ class nsDisplayListBuilder {
 
   void SetBuildingExtraPagesForPageNum(uint8_t aPageNum) {
     mBuildingExtraPagesForPageNum = aPageNum;
   }
   uint8_t GetBuildingExtraPagesForPageNum() const {
     return mBuildingExtraPagesForPageNum;
   }
 
+  /**
+   * This is a convenience function to ease the transition until AGRs and ASRs
+   * are unified.
+   */
+  AnimatedGeometryRoot* AnimatedGeometryRootForASR(
+      const ActiveScrolledRoot* aASR);
+
   bool HitTestIsForVisibility() const { return mVisibleThreshold.isSome(); }
 
   float VisibilityThreshold() const {
     MOZ_DIAGNOSTIC_ASSERT(HitTestIsForVisibility());
     return mVisibleThreshold.valueOr(1.0f);
   }
 
   void SetHitTestIsForVisibility(float aVisibleThreshold) {
@@ -1670,35 +1793,58 @@ class nsDisplayListBuilder {
 
     void MaybeAppendLink(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame);
 
    private:
     nsDisplayListBuilder* mBuilderToReset = nullptr;
     nsDisplayList* mList;
   };
 
-  /**
-   * Returns the nearest ancestor frame to aFrame that is considered to have
-   * (or will have) animated geometry. This can return aFrame.
-   */
-  nsIFrame* FindAnimatedGeometryRootFrameFor(nsIFrame* aFrame);
-
  private:
   bool MarkOutOfFlowFrameForDisplay(nsIFrame* aDirtyFrame, nsIFrame* aFrame,
                                     const nsRect& aVisibleRect,
                                     const nsRect& aDirtyRect);
 
-  friend class nsDisplayBackgroundImage;
-  friend struct RetainedDisplayListBuilder;
-
   /**
    * Returns whether a frame acts as an animated geometry root, optionally
    * returning the next ancestor to check.
    */
-  bool IsAnimatedGeometryRoot(nsIFrame* aFrame, nsIFrame** aParent = nullptr);
+  AGRState IsAnimatedGeometryRoot(nsIFrame* aFrame, bool& aIsAsync,
+                                  nsIFrame** aParent = nullptr);
+
+  /**
+   * Returns the nearest ancestor frame to aFrame that is considered to have
+   * (or will have) animated geometry. This can return aFrame.
+   */
+  nsIFrame* FindAnimatedGeometryRootFrameFor(nsIFrame* aFrame, bool& aIsAsync);
+
+  friend class nsDisplayCanvasBackgroundImage;
+  friend class nsDisplayBackgroundImage;
+  friend class nsDisplayFixedPosition;
+  friend class nsDisplayPerspective;
+  AnimatedGeometryRoot* FindAnimatedGeometryRootFor(nsDisplayItem* aItem);
+
+  friend class nsDisplayItem;
+  friend class nsDisplayOwnLayer;
+  friend struct RetainedDisplayListBuilder;
+  AnimatedGeometryRoot* FindAnimatedGeometryRootFor(nsIFrame* aFrame);
+
+  AnimatedGeometryRoot* WrapAGRForFrame(
+      nsIFrame* aAnimatedGeometryRoot, bool aIsAsync,
+      AnimatedGeometryRoot* aParent = nullptr);
+
+  nsTHashMap<nsIFrame*, RefPtr<AnimatedGeometryRoot>>
+      mFrameToAnimatedGeometryRootMap;
+
+  /**
+   * Add the current frame to the AGR budget if possible and remember
+   * the outcome. Subsequent calls will return the same value as
+   * returned here.
+   */
+  bool AddToAGRBudget(nsIFrame* aFrame);
 
   struct PresShellState {
     PresShell* mPresShell;
 #ifdef DEBUG
     Maybe<nsAutoLayoutPhase> mAutoLayoutPhase;
 #endif
     Maybe<OutOfFlowDisplayData> mFixedBackgroundDisplayData;
     uint32_t mFirstFrameMarkedForDisplay;
@@ -1752,28 +1898,36 @@ class nsDisplayListBuilder {
   const nsIFrame* mCurrentFrame;
   // The reference frame for mCurrentFrame.
   const nsIFrame* mCurrentReferenceFrame;
   // The offset from mCurrentFrame to mCurrentReferenceFrame.
   nsPoint mCurrentOffsetToReferenceFrame;
 
   Maybe<nsPoint> mAdditionalOffset;
 
+  RefPtr<AnimatedGeometryRoot> mRootAGR;
+  RefPtr<AnimatedGeometryRoot> mCurrentAGR;
+
   // will-change budget tracker
   typedef uint32_t DocumentWillChangeBudget;
   nsTHashMap<nsPtrHashKey<const nsPresContext>, DocumentWillChangeBudget>
       mDocumentWillChangeBudgets;
 
   // Any frame listed in this set is already counted in the budget
   // and thus is in-budget.
   nsTHashMap<nsPtrHashKey<const nsIFrame>, FrameWillChangeBudget>
       mFrameWillChangeBudgets;
 
   uint8_t mBuildingExtraPagesForPageNum;
 
+  // Area of animated geometry root budget already allocated
+  uint32_t mUsedAGRBudget;
+  // Set of frames already counted in budget
+  nsTHashSet<nsIFrame*> mAGRBudgetSet;
+
   nsTHashMap<nsPtrHashKey<dom::RemoteBrowser>, dom::EffectsInfo>
       mEffectsUpdates;
 
   // Relative to mCurrentFrame.
   nsRect mVisibleRect;
   nsRect mDirtyRect;
 
   // Tracked regions used for retained display list.
@@ -2023,17 +2177,16 @@ class nsDisplayItemLink {
  *
  * Display items belong to a list at all times (except temporarily as they
  * move from one list to another).
  */
 class nsDisplayItem : public nsDisplayItemLink {
  public:
   using Layer = layers::Layer;
   using LayerManager = layers::LayerManager;
-  using WebRenderLayerManager = layers::WebRenderLayerManager;
   using StackingContextHelper = layers::StackingContextHelper;
   using ViewID = layers::ScrollableLayerGuid::ViewID;
 
   /**
    * Downcasts this item to nsPaintedDisplayItem, if possible.
    */
   virtual nsPaintedDisplayItem* AsPaintedDisplayItem() { return nullptr; }
   virtual const nsPaintedDisplayItem* AsPaintedDisplayItem() const {
@@ -2206,22 +2359,16 @@ class nsDisplayItem : public nsDisplayIt
    * display list iterator to descend into the child display list.
    */
   virtual bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) {
     return false;
   }
 
   virtual bool CreatesStackingContextHelper() { return false; }
 
-  /**
-   * Returns true if this item can be moved asynchronously on the compositor,
-   * see RetainedDisplayListBuilder.cpp comments.
-   */
-  virtual bool CanMoveAsync() { return false; }
-
  protected:
   // This is never instantiated directly (it has pure virtual methods), so no
   // need to count constructors and destructors.
   nsDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame);
   nsDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                 const ActiveScrolledRoot* aActiveScrolledRoot);
 
   /**
@@ -2231,24 +2378,30 @@ class nsDisplayItem : public nsDisplayIt
    */
   nsDisplayItem(nsDisplayListBuilder* aBuilder, const nsDisplayItem& aOther)
       : mFrame(aOther.mFrame),
         mItemFlags(aOther.mItemFlags),
         mType(aOther.mType),
         mExtraPageForPageNum(aOther.mExtraPageForPageNum),
         mPerFrameIndex(aOther.mPerFrameIndex),
         mBuildingRect(aOther.mBuildingRect),
+        mReferenceFrame(aOther.mReferenceFrame),
         mToReferenceFrame(aOther.mToReferenceFrame),
+        mAnimatedGeometryRoot(aOther.mAnimatedGeometryRoot),
         mActiveScrolledRoot(aOther.mActiveScrolledRoot),
-        mClipChain(aOther.mClipChain) {
+        mClipChain(aOther.mClipChain),
+        mClip(aOther.mClip) {
     MOZ_COUNT_CTOR(nsDisplayItem);
     // TODO: It might be better to remove the flags that aren't copied.
     if (aOther.ForceNotVisible()) {
       mItemFlags += ItemFlag::ForceNotVisible;
     }
+    if (aOther.IsSubpixelAADisabled()) {
+      mItemFlags += ItemFlag::DisableSubpixelAA;
+    }
     if (mFrame->In3DContextAndBackfaceIsHidden()) {
       mItemFlags += ItemFlag::BackfaceHidden;
     }
     if (aOther.Combines3DTransformWithAncestors()) {
       mItemFlags += ItemFlag::Combines3DTransformWithAncestors;
     }
   }
 
@@ -2274,16 +2427,34 @@ class nsDisplayItem : public nsDisplayIt
 
   void SetDeletedFrame();
 
  public:
   nsDisplayItem() = delete;
   nsDisplayItem(const nsDisplayItem&) = delete;
 
   /**
+   * Roll back side effects carried out by processing the display list.
+   *
+   * @return true if the rollback actually modified anything, to help the caller
+   * decide whether to invalidate cached information about this node.
+   */
+  virtual bool RestoreState() {
+    if (mClipChain == mState.mClipChain && mClip == mState.mClip &&
+        !mItemFlags.contains(ItemFlag::DisableSubpixelAA)) {
+      return false;
+    }
+
+    mClipChain = mState.mClipChain;
+    mClip = mState.mClip;
+    mItemFlags -= ItemFlag::DisableSubpixelAA;
+    return true;
+  }
+
+  /**
    * Invalidate cached information that depends on this node's contents, after
    * a mutation of those contents.
    *
    * Specifically, if you mutate an |nsDisplayItem| in a way that would change
    * the WebRender display list items generated for it, you should call this
    * method.
    *
    * If a |RestoreState| method exists to restore some piece of state, that's a
@@ -2643,57 +2814,94 @@ class nsDisplayItem : public nsDisplayIt
 
   /**
    * Returns the result of aBuilder->ToReferenceFrame(GetUnderlyingFrame())
    */
   const nsPoint& ToReferenceFrame() const {
     NS_ASSERTION(mFrame, "No frame?");
     return mToReferenceFrame;
   }
+  /**
+   * @return the root of the display list's frame (sub)tree, whose origin
+   * establishes the coordinate system for the display list
+   */
+  const nsIFrame* ReferenceFrame() const { return mReferenceFrame; }
 
   /**
    * Returns the reference frame for display item children of this item.
    */
-  virtual const nsIFrame* ReferenceFrameForChildren() const { return nullptr; }
+  virtual const nsIFrame* ReferenceFrameForChildren() const {
+    return mReferenceFrame;
+  }
+
+  AnimatedGeometryRoot* GetAnimatedGeometryRoot() const {
+    MOZ_ASSERT(mAnimatedGeometryRoot,
+               "Must have cached AGR before accessing it!");
+    return mAnimatedGeometryRoot;
+  }
+
+  virtual struct AnimatedGeometryRoot* AnimatedGeometryRootForScrollMetadata()
+      const {
+    return GetAnimatedGeometryRoot();
+  }
 
   /**
    * Checks if this display item (or any children) contains content that might
    * be rendered with component alpha (e.g. subpixel antialiasing). Returns the
    * bounds of the area that needs component alpha, or an empty rect if nothing
    * in the item does.
    */
   virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) const {
     return nsRect();
   }
 
   /**
+   * Disable usage of component alpha. Currently only relevant for items that
+   * have text.
+   */
+  void DisableComponentAlpha() { mItemFlags += ItemFlag::DisableSubpixelAA; }
+
+  bool IsSubpixelAADisabled() const {
+    return mItemFlags.contains(ItemFlag::DisableSubpixelAA);
+  }
+
+  /**
    * Check if we can add async animations to the layer for this display item.
    */
   virtual bool CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) {
     return false;
   }
 
   virtual bool SupportsOptimizingToImage() const { return false; }
 
-  virtual const DisplayItemClip& GetClip() const;
+  const DisplayItemClip& GetClip() const {
+    return mClip ? *mClip : DisplayItemClip::NoClip();
+  }
   void IntersectClip(nsDisplayListBuilder* aBuilder,
                      const DisplayItemClipChain* aOther, bool aStore);
 
   virtual void SetActiveScrolledRoot(
       const ActiveScrolledRoot* aActiveScrolledRoot) {
     mActiveScrolledRoot = aActiveScrolledRoot;
   }
   const ActiveScrolledRoot* GetActiveScrolledRoot() const {
     return mActiveScrolledRoot;
   }
 
   virtual void SetClipChain(const DisplayItemClipChain* aClipChain,
                             bool aStore);
   const DisplayItemClipChain* GetClipChain() const { return mClipChain; }
 
+  /**
+   * Intersect all clips in our clip chain up to (and including) aASR and set
+   * set the intersection as this item's clip.
+   */
+  void FuseClipChainUpTo(nsDisplayListBuilder* aBuilder,
+                         const ActiveScrolledRoot* aASR);
+
   bool BackfaceIsHidden() const {
     return mItemFlags.contains(ItemFlag::BackfaceHidden);
   }
 
   bool Combines3DTransformWithAncestors() const {
     return mItemFlags.contains(ItemFlag::Combines3DTransformWithAncestors);
   }
 
@@ -2723,18 +2931,20 @@ class nsDisplayItem : public nsDisplayIt
     return mFrame->GetContent() == aOther->Frame()->GetContent();
   }
 
   virtual void NotifyUsed(nsDisplayListBuilder* aBuilder) {}
 
   virtual Maybe<nsRect> GetClipWithRespectToASR(
       nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR) const;
 
+  const nsRect& GetPaintRect() const { return mBuildingRect; }
+
   virtual const nsRect& GetUntransformedPaintRect() const {
-    return GetBuildingRect();
+    return GetPaintRect();
   }
 
   nsRect GetPaintRect(nsDisplayListBuilder* aBuilder, gfxContext* aCtx);
 
   virtual const HitTestInfo& GetHitTestInfo() { return HitTestInfo::Empty(); }
 
   nsIFrame* mFrame;  // 8
 
@@ -2746,16 +2956,17 @@ class nsDisplayItem : public nsDisplayIt
     ModifiedFrame,
     ReusedItem,
 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
     MergedItem,
     PreProcessedItem,
 #endif
     BackfaceHidden,
     Combines3DTransformWithAncestors,
+    DisableSubpixelAA,
     ForceNotVisible,
     HasHitTestInfo,
     IsGlassItem,
 #ifdef MOZ_DUMP_PAINTING
     // True if this frame has been painted.
     Painted,
 #endif
   };
@@ -2779,20 +2990,28 @@ class nsDisplayItem : public nsDisplayIt
     } else {
       mItemFlags -= aFlag;
     }
   }
 
   void SetHasHitTestInfo() { mItemFlags += ItemFlag::HasHitTestInfo; }
 
   // Result of ToReferenceFrame(mFrame), if mFrame is non-null
+  const nsIFrame* mReferenceFrame;
   nsPoint mToReferenceFrame;
 
+  RefPtr<AnimatedGeometryRoot> mAnimatedGeometryRoot;
   RefPtr<const ActiveScrolledRoot> mActiveScrolledRoot;
   RefPtr<const DisplayItemClipChain> mClipChain;
+  const DisplayItemClip* mClip = nullptr;
+
+  struct {
+    RefPtr<const DisplayItemClipChain> mClipChain;
+    const DisplayItemClip* mClip;
+  } mState;
 
 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
  public:
   bool IsMergedItem() const {
     return mItemFlags.contains(ItemFlag::MergedItem);
   }
 
   bool IsPreProcessedItem() const {
@@ -2812,23 +3031,29 @@ class nsDisplayItem : public nsDisplayIt
 class nsPaintedDisplayItem : public nsDisplayItem {
  public:
   nsPaintedDisplayItem* AsPaintedDisplayItem() final { return this; }
   const nsPaintedDisplayItem* AsPaintedDisplayItem() const final {
     return this;
   }
 
   /**
+   * Stores the given opacity value to be applied when drawing. It is an error
+   * to call this if CanApplyOpacity returned false.
+   */
+  virtual void ApplyOpacity(nsDisplayListBuilder* aBuilder, float aOpacity,
+                            const DisplayItemClipChain* aClip) {
+    MOZ_ASSERT(CanApplyOpacity(), "ApplyOpacity is not supported on this type");
+  }
+
+  /**
    * Returns true if this display item would return true from ApplyOpacity
    * without actually applying the opacity. Otherwise returns false.
    */
-  virtual bool CanApplyOpacity(WebRenderLayerManager* aManager,
-                               nsDisplayListBuilder* aBuilder) const {
-    return false;
-  }
+  virtual bool CanApplyOpacity() const { return false; }
 
   /**
    * Returns true if this item supports PaintWithClip, where the clipping
    * is used directly as the primitive geometry instead of needing an explicit
    * clip.
    */
   virtual bool CanPaintWithClip(const DisplayItemClip& aClip) { return false; }
 
@@ -4004,16 +4229,23 @@ class nsDisplayBackgroundImage : public 
                               uint16_t aLayer, const nsRect& aBackgroundRect,
                               ComputedStyle* aBackgroundStyle);
 
   explicit nsDisplayBackgroundImage(nsDisplayListBuilder* aBuilder,
                                     nsIFrame* aFrame, const InitData& aInitData,
                                     nsIFrame* aFrameForBounds = nullptr);
   ~nsDisplayBackgroundImage() override;
 
+  bool RestoreState() override {
+    bool superChanged = nsPaintedDisplayItem::RestoreState();
+    bool opacityChanged = (mOpacity != 1.0f);
+    mOpacity = 1.0f;
+    return (superChanged || opacityChanged);
+  }
+
   NS_DISPLAY_DECL_NAME("Background", TYPE_BACKGROUND)
 
   /**
    * This will create and append new items for all the layers of the
    * background. Returns the type of background that was appended.
    * aAllowWillPaintBorderOptimization should usually be left at true, unless
    * aFrame has special border drawing that causes opaque borders to not
    * actually be opaque.
@@ -4034,19 +4266,21 @@ class nsDisplayBackgroundImage : public 
       layers::RenderRootStateManager* aManager,
       nsDisplayListBuilder* aDisplayListBuilder) override;
   void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
                HitTestState* aState, nsTArray<nsIFrame*>* aOutFrames) override;
   nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                            bool* aSnap) const override;
   Maybe<nscolor> IsUniform(nsDisplayListBuilder* aBuilder) const override;
 
-  bool CanApplyOpacity(WebRenderLayerManager* aManager,
-                       nsDisplayListBuilder* aBuilder) const override {
-    return CanBuildWebRenderDisplayItems(aManager, aBuilder);
+  bool CanApplyOpacity() const override { return true; }
+
+  void ApplyOpacity(nsDisplayListBuilder* aBuilder, float aOpacity,
+                    const DisplayItemClipChain* aClip) override {
+    mOpacity = aOpacity;
   }
 
   /**
    * GetBounds() returns the background painting area.
    */
   nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const override;
 
   void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;
@@ -4105,17 +4339,17 @@ class nsDisplayBackgroundImage : public 
     const auto& styleImage =
         mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer].mImage;
 
     return styleImage.IsSizeAvailable() && styleImage.FinalImage().IsUrl();
   }
 
  protected:
   bool CanBuildWebRenderDisplayItems(layers::WebRenderLayerManager* aManager,
-                                     nsDisplayListBuilder* aBuilder) const;
+                                     nsDisplayListBuilder* aBuilder);
   nsRect GetBoundsInternal(nsDisplayListBuilder* aBuilder,
                            nsIFrame* aFrameForBounds = nullptr);
 
   void PaintInternal(nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
                      const nsRect& aBounds, nsRect* aClipRect);
 
   // Cache the result of nsCSSRendering::FindBackground. Always null if
   // mIsThemed is true or if FindBackground returned false.
@@ -4126,16 +4360,18 @@ class nsDisplayBackgroundImage : public 
   nsRect mFillRect;
   nsRect mDestRect;
   /* Bounds of this display item */
   nsRect mBounds;
   uint16_t mLayer;
   bool mIsRasterImage;
   /* Whether the image should be treated as fixed to the viewport. */
   bool mShouldFixToViewport;
+  uint32_t mImageFlags;
+  float mOpacity;
 };
 
 /**
  * A display item to paint background image for table. For table parts, such
  * as row, row group, col, col group, when drawing its background, we'll
  * create separate background image display item for its containning cell.
  * Those background image display items will reference to same DisplayItemData
  * if we keep the mFrame point to cell's ancestor frame. We don't want to this
@@ -4290,32 +4526,43 @@ class nsDisplayBackgroundColor : public 
                            const nsRect& aBackgroundRect,
                            const ComputedStyle* aBackgroundStyle,
                            const nscolor& aColor)
       : nsPaintedDisplayItem(aBuilder, aFrame),
         mBackgroundRect(aBackgroundRect),
         mHasStyle(aBackgroundStyle),
         mDependentFrame(nullptr),
         mColor(gfx::sRGBColor::FromABGR(aColor)) {
+    mState.mColor = mColor;
+
     if (mHasStyle) {
       mBottomLayerClip =
           aBackgroundStyle->StyleBackground()->BottomLayer().mClip;
     } else {
       MOZ_ASSERT(aBuilder->IsForEventDelivery());
     }
   }
 
   ~nsDisplayBackgroundColor() override {
     if (mDependentFrame) {
       mDependentFrame->RemoveDisplayItem(this);
     }
   }
 
   NS_DISPLAY_DECL_NAME("BackgroundColor", TYPE_BACKGROUND_COLOR)
 
+  bool RestoreState() override {
+    if (!nsPaintedDisplayItem::RestoreState() && mColor == mState.mColor) {
+      return false;
+    }
+
+    mColor = mState.mColor;
+    return true;
+  }
+
   bool HasBackgroundClipText() const {
     MOZ_ASSERT(mHasStyle);
     return mBottomLayerClip == StyleGeometryBox::Text;
   }
 
   void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;
   void PaintWithClip(nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
                      const DisplayItemClip& aClip) override;
@@ -4324,18 +4571,20 @@ class nsDisplayBackgroundColor : public 
       const StackingContextHelper& aSc,
       layers::RenderRootStateManager* aManager,
       nsDisplayListBuilder* aDisplayListBuilder) override;
   nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                            bool* aSnap) const override;
   Maybe<nscolor> IsUniform(nsDisplayListBuilder* aBuilder) const override;
   void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
                HitTestState* aState, nsTArray<nsIFrame*>* aOutFrames) override;
-  bool CanApplyOpacity(WebRenderLayerManager* aManager,
-                       nsDisplayListBuilder* aBuilder) const override;
+  void ApplyOpacity(nsDisplayListBuilder* aBuilder, float aOpacity,
+                    const DisplayItemClipChain* aClip) override;
+
+  bool CanApplyOpacity() const override;
 
   float GetOpacity() const { return mColor.a; }
 
   nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const override {
     *aSnap = true;
     return mBackgroundRect;
   }
 
@@ -4395,16 +4644,20 @@ class nsDisplayBackgroundColor : public 
   bool CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) override;
 
  protected:
   const nsRect mBackgroundRect;
   const bool mHasStyle;
   StyleGeometryBox mBottomLayerClip;
   nsIFrame* mDependentFrame;
   gfx::sRGBColor mColor;
+
+  struct {
+    gfx::sRGBColor mColor;
+  } mState;
 };
 
 class nsDisplayTableBackgroundColor : public nsDisplayBackgroundColor {
  public:
   nsDisplayTableBackgroundColor(nsDisplayListBuilder* aBuilder,
                                 nsIFrame* aFrame, const nsRect& aBackgroundRect,
                                 const ComputedStyle* aBackgroundStyle,
                                 const nscolor& aColor, nsIFrame* aAncestorFrame)
@@ -4443,47 +4696,66 @@ class nsDisplayTableBackgroundColor : pu
 };
 
 /**
  * The standard display item to paint the outer CSS box-shadows of a frame.
  */
 class nsDisplayBoxShadowOuter final : public nsPaintedDisplayItem {
  public:
   nsDisplayBoxShadowOuter(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
-      : nsPaintedDisplayItem(aBuilder, aFrame) {
+      : nsPaintedDisplayItem(aBuilder, aFrame), mOpacity(1.0f) {
     MOZ_COUNT_CTOR(nsDisplayBoxShadowOuter);
     mBounds = GetBoundsInternal();
   }
 
   MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayBoxShadowOuter)
 
   NS_DISPLAY_DECL_NAME("BoxShadowOuter", TYPE_BOX_SHADOW_OUTER)
 
+  bool RestoreState() override {
+    if (!nsPaintedDisplayItem::RestoreState() && mOpacity == 1.0f) {
+      return false;
+    }
+
+    mOpacity = 1.0f;
+    return true;
+  }
+
   void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;
   nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const override;
   bool IsInvisibleInRect(const nsRect& aRect) const override;
   void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                  const nsDisplayItemGeometry* aGeometry,
                                  nsRegion* aInvalidRegion) const override;
 
-  bool CanApplyOpacity(WebRenderLayerManager* aManager,
-                       nsDisplayListBuilder* aBuilder) const override {
-    return CanBuildWebRenderDisplayItems();
-  }
-
-  bool CanBuildWebRenderDisplayItems() const;
+  void ApplyOpacity(nsDisplayListBuilder* aBuilder, float aOpacity,
+                    const DisplayItemClipChain* aClip) override {
+    NS_ASSERTION(CanApplyOpacity(), "ApplyOpacity should be allowed");
+    mOpacity = aOpacity;
+    IntersectClip(aBuilder, aClip, false);
+  }
+
+  bool CanApplyOpacity() const override { return true; }
+
+  nsDisplayItemGeometry* AllocateGeometry(
+      nsDisplayListBuilder* aBuilder) override {
+    return new nsDisplayBoxShadowOuterGeometry(this, aBuilder, mOpacity);
+  }
+
+  bool CanBuildWebRenderDisplayItems();
   bool CreateWebRenderCommands(
       wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
       const StackingContextHelper& aSc,
       layers::RenderRootStateManager* aManager,
       nsDisplayListBuilder* aDisplayListBuilder) override;
   nsRect GetBoundsInternal();
 
  private:
   nsRect mBounds;
+  float mOpacity;
 };
 
 /**
  * The standard display item to paint the inner CSS box-shadows of a frame.
  */
 class nsDisplayBoxShadowInner : public nsPaintedDisplayItem {
  public:
   nsDisplayBoxShadowInner(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
@@ -4666,17 +4938,16 @@ class nsDisplayWrapList : public nsPaint
   nsDisplayWrapList(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
       : nsPaintedDisplayItem(aBuilder, aFrame),
         mFrameActiveScrolledRoot(aBuilder->CurrentActiveScrolledRoot()),
         mOverrideZIndex(0),
         mHasZIndexOverride(false) {
     MOZ_COUNT_CTOR(nsDisplayWrapList);
     mBaseBuildingRect = GetBuildingRect();
     mListPtr = &mList;
-    mOriginalClipChain = mClipChain;
   }
 
   nsDisplayWrapList() = delete;
 
   /**
    * A custom copy-constructor that does not copy mList, as this would mutate
    * the other item.
    */
@@ -4684,17 +4955,16 @@ class nsDisplayWrapList : public nsPaint
   nsDisplayWrapList(nsDisplayListBuilder* aBuilder,
                     const nsDisplayWrapList& aOther)
       : nsPaintedDisplayItem(aBuilder, aOther),
         mListPtr(&mList),
         mFrameActiveScrolledRoot(aOther.mFrameActiveScrolledRoot),
         mMergedFrames(aOther.mMergedFrames.Clone()),
         mBounds(aOther.mBounds),
         mBaseBuildingRect(aOther.mBaseBuildingRect),
-        mOriginalClipChain(aOther.mClipChain),
         mOverrideZIndex(aOther.mOverrideZIndex),
         mHasZIndexOverride(aOther.mHasZIndexOverride),
         mClearingClipChain(aOther.mClearingClipChain) {
     MOZ_COUNT_CTOR(nsDisplayWrapList);
   }
 
   ~nsDisplayWrapList() override;
 
@@ -4716,17 +4986,17 @@ class nsDisplayWrapList : public nsPaint
 
   /**
    * Call this if the wrapped list is changed.
    */
   void UpdateBounds(nsDisplayListBuilder* aBuilder) override {
     // Clear the clip chain up to the asr, but don't store it, so that we'll
     // recover it when we reuse the item.
     if (mClearingClipChain) {
-      const DisplayItemClipChain* clip = mOriginalClipChain;
+      const DisplayItemClipChain* clip = mState.mClipChain;
       while (clip && ActiveScrolledRoot::IsAncestor(GetActiveScrolledRoot(),
                                                     clip->mASR)) {
         clip = clip->mParent;
       }
       SetClipChain(clip, false);
     }
 
     nsRect buildingRect;
@@ -4738,25 +5008,16 @@ class nsDisplayWrapList : public nsPaint
     // overflow rect of a frame for some reason (e.g. when setting up dirty
     // rects in nsDisplayListBuilder::MarkOutOfFlowFrameForDisplay), but that
     // frame contains placeholders for out-of-flows that aren't descendants of
     // the frame.
     buildingRect.UnionRect(mBaseBuildingRect, buildingRect);
     SetBuildingRect(buildingRect);
   }
 
-  void SetClipChain(const DisplayItemClipChain* aClipChain,
-                    bool aStore) override {
-    nsDisplayItem::SetClipChain(aClipChain, aStore);
-
-    if (aStore) {
-      mOriginalClipChain = mClipChain;
-    }
-  }
-
   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;
   Maybe<nscolor> IsUniform(nsDisplayListBuilder* aBuilder) const override;
 
   /**
@@ -4806,16 +5067,21 @@ class nsDisplayWrapList : public nsPaint
     }
     aRect += ToReferenceFrame();
     return !aRect.IsEmpty();
   }
 
   nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) const override;
 
   RetainedDisplayList* GetSameCoordinateSystemChildren() const override {
+    NS_ASSERTION(
+        mListPtr->IsEmpty() || !ReferenceFrame() ||
+            !mListPtr->GetBottom()->ReferenceFrame() ||
+            mListPtr->GetBottom()->ReferenceFrame() == ReferenceFrame(),
+        "Children must have same reference frame");
     return mListPtr;
   }
 
   RetainedDisplayList* GetChildren() const override { return mListPtr; }
 
   int32_t ZIndex() const override {
     return (mHasZIndexOverride) ? mOverrideZIndex
                                 : nsPaintedDisplayItem::ZIndex();
@@ -4865,17 +5131,16 @@ class nsDisplayWrapList : public nsPaint
   RefPtr<const ActiveScrolledRoot> mFrameActiveScrolledRoot;
   // The frames from items that have been merged into this item, excluding
   // this item's own frame.
   nsTArray<nsIFrame*> mMergedFrames;
   nsRect mBounds;
   // Displaylist building rect contributed by this display item itself.
   // Our mBuildingRect may include the visible areas of children.
   nsRect mBaseBuildingRect;
-  RefPtr<const DisplayItemClipChain> mOriginalClipChain;
   int32_t mOverrideZIndex;
   bool mHasZIndexOverride;
   bool mClearingClipChain = false;
 };
 
 class nsDisplayWrapper : public nsDisplayWrapList {
  public:
   NS_DISPLAY_DECL_NAME("WrapList", TYPE_WRAP_LIST)
@@ -4952,16 +5217,25 @@ class nsDisplayOpacity : public nsDispla
 
   void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
                HitTestState* aState, nsTArray<nsIFrame*>* aOutFrames) override;
 
   MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayOpacity)
 
   NS_DISPLAY_DECL_NAME("Opacity", TYPE_OPACITY)
 
+  bool RestoreState() override {
+    if (!nsDisplayWrapList::RestoreState() && mOpacity == mState.mOpacity) {
+      return false;
+    }
+
+    mOpacity = mState.mOpacity;
+    return true;
+  }
+
   void InvalidateCachedChildInfo(nsDisplayListBuilder* aBuilder) override {
     mChildOpacityState = ChildOpacityState::Unknown;
   }
 
   nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                            bool* aSnap) const override;
   void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;
 
@@ -4984,25 +5258,20 @@ class nsDisplayOpacity : public nsDispla
                                  nsRegion* aInvalidRegion) const override;
 
   bool IsInvalid(nsRect& aRect) const override {
     if (mForEventsOnly) {
       return false;
     }
     return nsDisplayWrapList::IsInvalid(aRect);
   }
-  bool CanApplyOpacity(WebRenderLayerManager* aManager,
-                       nsDisplayListBuilder* aBuilder) const override;
-  bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override {
-    return false;
-  }
-
-  bool CanApplyOpacityToChildren(WebRenderLayerManager* aManager,
-                                 nsDisplayListBuilder* aBuilder,
-                                 float aInheritedOpacity);
+  void ApplyOpacity(nsDisplayListBuilder* aBuilder, float aOpacity,
+                    const DisplayItemClipChain* aClip) override;
+  bool CanApplyOpacity() const override;
+  bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override;
 
   bool NeedsGeometryUpdates() const override {
     // For flattened nsDisplayOpacity items, ComputeInvalidationRegion() only
     // handles invalidation for changed |mOpacity|. In order to keep track of
     // the current bounds of the item for invalidation, nsDisplayOpacityGeometry
     // for the corresponding DisplayItemData needs to be updated, even if the
     // reported invalidation region is empty.
     return mChildOpacityState == ChildOpacityState::Deferred;
@@ -5027,19 +5296,18 @@ class nsDisplayOpacity : public nsDispla
 
   float GetOpacity() const { return mOpacity; }
 
   bool CreatesStackingContextHelper() override { return true; }
 
  private:
   NS_DISPLAY_ALLOW_CLONING()
 
-  bool CanApplyToChildren(WebRenderLayerManager* aManager,
-                          nsDisplayListBuilder* aBuilder);
-  bool ApplyToMask();
+  bool ApplyToChildren(nsDisplayListBuilder* aBuilder);
+  bool ApplyToFilterOrMask(const bool aUsingLayers);
 
   float mOpacity;
   bool mForEventsOnly : 1;
   enum class ChildOpacityState : uint8_t {
     // Our child list has changed since the last time ApplyToChildren was
     // called.
     Unknown,
     // Our children defer opacity handling to us.
@@ -5048,16 +5316,20 @@ class nsDisplayOpacity : public nsDispla
     Applied
   };
   bool mNeedsActiveLayer : 1;
 #ifndef __GNUC__
   ChildOpacityState mChildOpacityState : 2;
 #else
   ChildOpacityState mChildOpacityState;
 #endif
+
+  struct {
+    float mOpacity;
+  } mState{};
 };
 
 class nsDisplayBlendMode : public nsDisplayWrapList {
  public:
   nsDisplayBlendMode(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                      nsDisplayList* aList, StyleBlend aBlendMode,
                      const ActiveScrolledRoot* aActiveScrolledRoot,
                      const bool aIsForBackground);
@@ -5424,19 +5696,18 @@ class nsDisplayStickyPosition : public n
       : nsDisplayOwnLayer(aBuilder, aOther),
         mContainerASR(aOther.mContainerASR),
         mClippedToDisplayPort(aOther.mClippedToDisplayPort) {
     MOZ_COUNT_CTOR(nsDisplayStickyPosition);
   }
 
   MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayStickyPosition)
 
-  const DisplayItemClip& GetClip() const override {
-    return DisplayItemClip::NoClip();
-  }
+  void SetClipChain(const DisplayItemClipChain* aClipChain,
+                    bool aStore) override;
   bool IsClippedToDisplayPort() const { return mClippedToDisplayPort; }
 
   NS_DISPLAY_DECL_NAME("StickyPosition", TYPE_STICKY_POSITION)
   void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override {
     GetChildren()->Paint(aBuilder, aCtx,
                          mFrame->PresContext()->AppUnitsPerDevPixel());
   }
 
@@ -5448,18 +5719,16 @@ class nsDisplayStickyPosition : public n
 
   bool UpdateScrollData(layers::WebRenderScrollData* aData,
                         layers::WebRenderLayerScrollData* aLayerData) override;
 
   const ActiveScrolledRoot* GetContainerASR() const { return mContainerASR; }
 
   bool CreatesStackingContextHelper() override { return true; }
 
-  bool CanMoveAsync() override { return true; }
-
  private:
   NS_DISPLAY_ALLOW_CLONING()
 
   void CalculateLayerScrollRanges(StickyScrollContainer* aStickyScrollContainer,
                                   float aAppUnitsPerDevPixel, float aScaleX,
                                   float aScaleY,
                                   LayerRectAbsolute& aStickyOuter,
                                   LayerRectAbsolute& aStickyInner);
@@ -5487,16 +5756,18 @@ class nsDisplayFixedPosition : public ns
  public:
   nsDisplayFixedPosition(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                          nsDisplayList* aList,
                          const ActiveScrolledRoot* aActiveScrolledRoot,
                          const ActiveScrolledRoot* aContainerASR);
   nsDisplayFixedPosition(nsDisplayListBuilder* aBuilder,
                          const nsDisplayFixedPosition& aOther)
       : nsDisplayOwnLayer(aBuilder, aOther),
+        mAnimatedGeometryRootForScrollMetadata(
+            aOther.mAnimatedGeometryRootForScrollMetadata),
         mContainerASR(aOther.mContainerASR),
         mIsFixedBackground(aOther.mIsFixedBackground) {
     MOZ_COUNT_CTOR(nsDisplayFixedPosition);
   }
 
   static nsDisplayFixedPosition* CreateForFixedBackground(
       nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
       nsIFrame* aSecondaryFrame, nsDisplayBackgroundImage* aImage,
@@ -5510,31 +5781,37 @@ class nsDisplayFixedPosition : public ns
     GetChildren()->Paint(aBuilder, aCtx,
                          mFrame->PresContext()->AppUnitsPerDevPixel());
   }
 
   bool ShouldFixToViewport(nsDisplayListBuilder* aBuilder) const override {
     return mIsFixedBackground;
   }
 
+  AnimatedGeometryRoot* AnimatedGeometryRootForScrollMetadata() const override {
+    return mAnimatedGeometryRootForScrollMetadata;
+  }
+
   bool CreateWebRenderCommands(
       wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
       const StackingContextHelper& aSc,
       layers::RenderRootStateManager* aManager,
       nsDisplayListBuilder* aDisplayListBuilder) override;
   bool UpdateScrollData(layers::WebRenderScrollData* aData,
                         layers::WebRenderLayerScrollData* aLayerData) override;
   void WriteDebugInfo(std::stringstream& aStream) override;
 
  protected:
   // For background-attachment:fixed
   nsDisplayFixedPosition(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                          nsDisplayList* aList);
+  void Init(nsDisplayListBuilder* aBuilder);
   ViewID GetScrollTargetId();
 
+  RefPtr<AnimatedGeometryRoot> mAnimatedGeometryRootForScrollMetadata;
   RefPtr<const ActiveScrolledRoot> mContainerASR;
   bool mIsFixedBackground;
 
  private:
   NS_DISPLAY_ALLOW_CLONING()
 };
 
 class nsDisplayTableFixedPosition : public nsDisplayFixedPosition {
@@ -5703,43 +5980,62 @@ class nsDisplayEffectsBase : public nsDi
                        const ActiveScrolledRoot* aActiveScrolledRoot,
                        bool aClearClipChain = false);
   nsDisplayEffectsBase(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                        nsDisplayList* aList);
 
   nsDisplayEffectsBase(nsDisplayListBuilder* aBuilder,
                        const nsDisplayEffectsBase& aOther)
       : nsDisplayWrapList(aBuilder, aOther),
-        mEffectsBounds(aOther.mEffectsBounds) {
+        mEffectsBounds(aOther.mEffectsBounds),
+        mHandleOpacity(aOther.mHandleOpacity) {
     MOZ_COUNT_CTOR(nsDisplayEffectsBase);
   }
 
   MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayEffectsBase)
 
   nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                            bool* aSnap) const override;
   void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
                HitTestState* aState, nsTArray<nsIFrame*>* aOutFrames) override;
 
+  bool RestoreState() override {
+    if (!nsDisplayWrapList::RestoreState() && !mHandleOpacity) {
+      return false;
+    }
+
+    mHandleOpacity = false;
+    return true;
+  }
+
   bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override {
     return false;
   }
 
+  virtual void SelectOpacityOptimization(const bool /* aUsingLayers */) {
+    SetHandleOpacity();
+  }
+
+  bool ShouldHandleOpacity() const { return mHandleOpacity; }
+
   gfxRect BBoxInUserSpace() const;
   gfxPoint UserSpaceOffset() const;
 
   void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                  const nsDisplayItemGeometry* aGeometry,
                                  nsRegion* aInvalidRegion) const override;
 
  protected:
+  void SetHandleOpacity() { mHandleOpacity = true; }
   bool ValidateSVGFrame();
 
   // relative to mFrame
   nsRect mEffectsBounds;
+  // True if we need to handle css opacity in this display item.
+  bool mHandleOpacity;
 };
 
 /**
  * A display item to paint a stacking context with 'mask' and 'clip-path'
  * effects set by the stacking context root frame's style.  The 'mask' and
  * 'clip-path' properties may both contain multiple masks and clip paths,
  * respectively.
  *
@@ -5795,20 +6091,22 @@ class nsDisplayMasksAndClipPaths : publi
       nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
       const std::function<void()>& aPaintChildren);
 
   /*
    * Paint mask onto aMaskContext in mFrame's coordinate space and
    * return whether the mask layer was painted successfully.
    */
   bool PaintMask(nsDisplayListBuilder* aBuilder, gfxContext* aMaskContext,
-                 bool aHandleOpacity, bool* aMaskPainted = nullptr);
+                 bool* aMaskPainted = nullptr);
 
   const nsTArray<nsRect>& GetDestRects() { return mDestRects; }
 
+  void SelectOpacityOptimization(const bool aUsingLayers) override;
+
   bool CreateWebRenderCommands(
       wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
       const StackingContextHelper& aSc,
       layers::RenderRootStateManager* aManager,
       nsDisplayListBuilder* aDisplayListBuilder) override;
 
   Maybe<nsRect> GetClipWithRespectToASR(
       nsDisplayListBuilder* aBuilder,
@@ -5819,16 +6117,17 @@ class nsDisplayMasksAndClipPaths : publi
  private:
   NS_DISPLAY_ALLOW_CLONING()
 
   // According to mask property and the capability of aManager, determine
   // whether we can paint the mask onto a dedicate mask layer.
   bool CanPaintOnMaskLayer(LayerManager* aManager);
 
   nsTArray<nsRect> mDestRects;
+  bool mApplyOpacityWithSimpleClipPath;
 };
 
 class nsDisplayBackdropRootContainer : public nsDisplayWrapList {
  public:
   nsDisplayBackdropRootContainer(nsDisplayListBuilder* aBuilder,
                                  nsIFrame* aFrame, nsDisplayList* aList,
                                  const ActiveScrolledRoot* aActiveScrolledRoot)
       : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot, true) {
@@ -5949,31 +6248,25 @@ class nsDisplayFilters : public nsDispla
       nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
       const std::function<void(gfxContext* aContext)>& aPaintChildren);
 
   bool CreateWebRenderCommands(
       wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
       const StackingContextHelper& aSc,
       layers::RenderRootStateManager* aManager,
       nsDisplayListBuilder* aDisplayListBuilder) override;
-  bool CanCreateWebRenderCommands() const;
-
-  bool CanApplyOpacity(WebRenderLayerManager* aManager,
-                       nsDisplayListBuilder* aBuilder) const override {
-    return CanCreateWebRenderCommands();
-  }
+  bool CanCreateWebRenderCommands();
 
   bool CreatesStackingContextHelper() override { return true; }
 
  private:
   NS_DISPLAY_ALLOW_CLONING()
 
   // relative to mFrame
   nsRect mEffectsBounds;
-  nsRect mVisibleRect;
 };
 
 /* A display item that applies a transformation to all of its descendant
  * elements.  This wrapper should only be used if there is a transform applied
  * to the root element.
  *
  * The reason that a "bounds" rect is involved in transform calculations is
  * because CSS-transforms allow percentage values for the x and y components
@@ -6009,16 +6302,25 @@ class nsDisplayTransform : public nsPain
   nsDisplayTransform(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                      nsDisplayList* aList, const nsRect& aChildrenBuildingRect,
                      decltype(WithTransformGetter));
 
   MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayTransform)
 
   NS_DISPLAY_DECL_NAME("nsDisplayTransform", TYPE_TRANSFORM)
 
+  bool RestoreState() override {
+    if (!nsPaintedDisplayItem::RestoreState() && !mShouldFlatten) {
+      return false;
+    }
+
+    mShouldFlatten = false;
+    return true;
+  }
+
   void UpdateBounds(nsDisplayListBuilder* aBuilder) override;
 
   /**
    * This function updates bounds for items with a frame establishing
    * 3D rendering context.
    */
   void UpdateBoundsFor3D(nsDisplayListBuilder* aBuilder);
 
@@ -6078,26 +6380,32 @@ class nsDisplayTransform : public nsPain
     // Only check if the transform has changed. The bounds invalidation should
     // be handled by the children themselves.
     if (!geometry->mTransform.FuzzyEqual(GetTransformForRendering())) {
       bool snap;
       aInvalidRegion->Or(GetBounds(aBuilder, &snap), geometry->mBounds);
     }
   }
 
+  bool NeedsGeometryUpdates() const override { return mShouldFlatten; }
+
   const nsIFrame* ReferenceFrameForChildren() const override {
     // If we were created using a transform-getter, then we don't
     // belong to a transformed frame, and aren't a reference frame
     // for our children.
     if (!mHasTransformGetter) {
       return mFrame;
     }
     return nsPaintedDisplayItem::ReferenceFrameForChildren();
   }
 
+  AnimatedGeometryRoot* AnimatedGeometryRootForScrollMetadata() const override {
+    return mAnimatedGeometryRootForScrollMetadata;
+  }
+
   const nsRect& GetBuildingRectForChildren() const override {
     return mChildrenBuildingRect;
   }
 
   enum { INDEX_MAX = UINT32_MAX >> TYPE_BITS };
 
   /**
    * We include the perspective matrix from our containing block for the
@@ -6152,16 +6460,21 @@ class nsDisplayTransform : public nsPain
   bool UntransformRect(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
                        nsRect* aOutRect) const;
 
   bool UntransformBuildingRect(nsDisplayListBuilder* aBuilder,
                                nsRect* aOutRect) const {
     return UntransformRect(aBuilder, GetBuildingRect(), aOutRect);
   }
 
+  bool UntransformPaintRect(nsDisplayListBuilder* aBuilder,
+                            nsRect* aOutRect) const {
+    return UntransformRect(aBuilder, GetPaintRect(), aOutRect);
+  }
+
   static gfx::Point3D GetDeltaToTransformOrigin(const nsIFrame* aFrame,
                                                 TransformReferenceBox&,
                                                 float aAppUnitsPerPixel);
 
   /*
    * Returns true if aFrame has perspective applied from its containing
    * block.
    * Returns the matrix to append to apply the persective (taking
@@ -6262,21 +6575,16 @@ class nsDisplayTransform : public nsPain
 
   bool CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) override;
 
   bool MayBeAnimated(nsDisplayListBuilder* aBuilder,
                      bool aEnforceMinimumSize = true) const;
 
   void WriteDebugInfo(std::stringstream& aStream) override;
 
-  bool CanMoveAsync() override {
-    return EffectCompositor::HasAnimationsForCompositor(
-        mFrame, DisplayItemType::TYPE_TRANSFORM);
-  }
-
   /**
    * This item is an additional item as the boundary between parent
    * and child 3D rendering context.
    * \see nsIFrame::BuildDisplayListForStackingContext().
    */
   bool IsTransformSeparator() const { return mIsTransformSeparator; }
   /**
    * This item is the boundary between parent and child 3D rendering
@@ -6321,32 +6629,36 @@ class nsDisplayTransform : public nsPain
   using TransformPolygon = layers::BSPPolygon<nsDisplayTransform>;
   void CollectSorted3DTransformLeaves(nsDisplayListBuilder* aBuilder,
                                       nsTArray<TransformPolygon>& aLeaves);
 
   mutable Maybe<Matrix4x4Flagged> mTransform;
   mutable Maybe<Matrix4x4Flagged> mInverseTransform;
   // Accumulated transform of ancestors on the preserves-3d chain.
   UniquePtr<Matrix4x4> mTransformPreserves3D;
+  RefPtr<AnimatedGeometryRoot> mAnimatedGeometryRootForChildren;
+  RefPtr<AnimatedGeometryRoot> mAnimatedGeometryRootForScrollMetadata;
   nsRect mChildrenBuildingRect;
   mutable RetainedDisplayList mChildren;
 
   // The untransformed bounds of |mChildren|.
   nsRect mChildBounds;
   // The transformed bounds of this display item.
   nsRect mBounds;
   PrerenderDecision mPrerenderDecision : 8;
   // 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 : 1;
+  // True if this nsDisplayTransform should get flattened
+  bool mShouldFlatten : 1;
   // True if we have a transform getter.
   bool mHasTransformGetter : 1;
 };
 
 /* A display item that applies a perspective transformation to a single
  * nsDisplayTransform child item. We keep this as a separate item since the
  * perspective-origin is relative to an ancestor of the transformed frame, and
  * APZ can scroll the child separately.
@@ -6429,16 +6741,25 @@ class nsDisplayTextGeometry;
  */
 class nsDisplayText final : public nsPaintedDisplayItem {
  public:
   nsDisplayText(nsDisplayListBuilder* aBuilder, nsTextFrame* aFrame);
   MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayText)
 
   NS_DISPLAY_DECL_NAME("Text", TYPE_TEXT)
 
+  bool RestoreState() final {
+    if (!nsPaintedDisplayItem::RestoreState() && mOpacity == 1.0f) {
+      return false;
+    }
+
+    mOpacity = 1.0f;
+    return true;
+  }
+
   nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const final {
     *aSnap = false;
     return mBounds;
   }
 
   void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
                HitTestState* aState, nsTArray<nsIFrame*>* aOutFrames) final {
     if (nsRect(ToReferenceFrame(), mFrame->GetSize()).Intersects(aRect)) {
@@ -6468,21 +6789,26 @@ class nsDisplayText final : public nsPai
 
   nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) final;
 
   void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                  const nsDisplayItemGeometry* aGeometry,
                                  nsRegion* aInvalidRegion) const final;
 
   void RenderToContext(gfxContext* aCtx, nsDisplayListBuilder* aBuilder,
-                       const nsRect& aVisibleRect, float aOpacity = 1.0f,
                        bool aIsRecording = false);
 
-  bool CanApplyOpacity(WebRenderLayerManager* aManager,
-                       nsDisplayListBuilder* aBuilder) const final;
+  bool CanApplyOpacity() const final;
+
+  void ApplyOpacity(nsDisplayListBuilder* aBuilder, float aOpacity,
+                    const DisplayItemClipChain* aClip) final {
+    NS_ASSERTION(CanApplyOpacity(), "ApplyOpacity should be allowed");
+    mOpacity = aOpacity;
+    IntersectClip(aBuilder, aClip, false);
+  }
 
   void WriteDebugInfo(std::stringstream& aStream) final;
 
   static nsDisplayText* CheckCast(nsDisplayItem* aItem) {
     return (aItem->GetType() == DisplayItemType::TYPE_TEXT)
                ? static_cast<nsDisplayText*>(aItem)
                : nullptr;
   }
@@ -6511,20 +6837,21 @@ class nsDisplayText final : public nsPai
     }
 
     nscoord mVisIStart;
     nscoord mVisIEnd;
   };
 
   nscoord& VisIStartEdge() { return mVisIStartEdge; }
   nscoord& VisIEndEdge() { return mVisIEndEdge; }
+  float Opacity() const { return mOpacity; }
 
  private:
   nsRect mBounds;
-  nsRect mVisibleRect;
+  float mOpacity;
 
   // Lengths measured from the visual inline start and end sides
   // (i.e. left and right respectively in horizontal writing modes,
   // regardless of bidi directionality; top and bottom in vertical modes).
   nscoord mVisIStartEdge;
   nscoord mVisIEndEdge;
 };
 
--- a/layout/painting/nsDisplayListInvalidation.cpp
+++ b/layout/painting/nsDisplayListInvalidation.cpp
@@ -86,27 +86,33 @@ nsDisplayBoxShadowInnerGeometry::nsDispl
     : nsDisplayItemGeometry(aItem, aBuilder),
       mPaddingRect(aItem->GetPaddingRect()) {}
 
 void nsDisplayBoxShadowInnerGeometry::MoveBy(const nsPoint& aOffset) {
   nsDisplayItemGeometry::MoveBy(aOffset);
   mPaddingRect.MoveBy(aOffset);
 }
 
+nsDisplayBoxShadowOuterGeometry::nsDisplayBoxShadowOuterGeometry(
+    nsDisplayItem* aItem, nsDisplayListBuilder* aBuilder, float aOpacity)
+    : nsDisplayItemGenericGeometry(aItem, aBuilder), mOpacity(aOpacity) {}
+
 void nsDisplaySolidColorRegionGeometry::MoveBy(const nsPoint& aOffset) {
   nsDisplayItemGeometry::MoveBy(aOffset);
   mRegion.MoveBy(aOffset);
 }
 
 nsDisplaySVGEffectGeometry::nsDisplaySVGEffectGeometry(
     nsDisplayEffectsBase* aItem, nsDisplayListBuilder* aBuilder)
     : nsDisplayItemGeometry(aItem, aBuilder),
       mBBox(aItem->BBoxInUserSpace()),
       mUserSpaceOffset(aItem->UserSpaceOffset()),
-      mFrameOffsetToReferenceFrame(aItem->ToReferenceFrame()) {}
+      mFrameOffsetToReferenceFrame(aItem->ToReferenceFrame()),
+      mOpacity(aItem->Frame()->StyleEffects()->mOpacity),
+      mHandleOpacity(aItem->ShouldHandleOpacity()) {}
 
 void nsDisplaySVGEffectGeometry::MoveBy(const nsPoint& aOffset) {
   mBounds.MoveBy(aOffset);
   mFrameOffsetToReferenceFrame += aOffset;
 }
 
 nsDisplayMasksAndClipPathsGeometry::nsDisplayMasksAndClipPathsGeometry(
     nsDisplayMasksAndClipPaths* aItem, nsDisplayListBuilder* aBuilder)
--- a/layout/painting/nsDisplayListInvalidation.h
+++ b/layout/painting/nsDisplayListInvalidation.h
@@ -240,16 +240,25 @@ class nsDisplayBoxShadowInnerGeometry : 
   nsDisplayBoxShadowInnerGeometry(nsDisplayItem* aItem,
                                   nsDisplayListBuilder* aBuilder);
 
   void MoveBy(const nsPoint& aOffset) override;
 
   nsRect mPaddingRect;
 };
 
+class nsDisplayBoxShadowOuterGeometry : public nsDisplayItemGenericGeometry {
+ public:
+  nsDisplayBoxShadowOuterGeometry(nsDisplayItem* aItem,
+                                  nsDisplayListBuilder* aBuilder,
+                                  float aOpacity);
+
+  float mOpacity;
+};
+
 class nsDisplaySolidColorGeometry : public nsDisplayItemBoundsGeometry {
  public:
   nsDisplaySolidColorGeometry(nsDisplayItem* aItem,
                               nsDisplayListBuilder* aBuilder, nscolor aColor)
       : nsDisplayItemBoundsGeometry(aItem, aBuilder), mColor(aColor) {}
 
   nscolor mColor;
 };
@@ -275,16 +284,17 @@ class nsDisplaySVGEffectGeometry : publi
   nsDisplaySVGEffectGeometry(nsDisplayEffectsBase* aItem,
                              nsDisplayListBuilder* aBuilder);
 
   void MoveBy(const nsPoint& aOffset) override;
 
   gfxRect mBBox;
   gfxPoint mUserSpaceOffset;
   nsPoint mFrameOffsetToReferenceFrame;
+  float mOpacity;
   bool mHandleOpacity;
 };
 
 class nsDisplayMasksAndClipPathsGeometry
     : public nsDisplaySVGEffectGeometry,
       public nsImageGeometryMixin<nsDisplayMasksAndClipPathsGeometry> {
  public:
   nsDisplayMasksAndClipPathsGeometry(nsDisplayMasksAndClipPaths* aItem,
--- a/layout/reftests/box-shadow/reftest.list
+++ b/layout/reftests/box-shadow/reftest.list
@@ -18,17 +18,17 @@ fuzzy-if(/^Windows\x20NT\x2010\.0/.test(
 fuzzy-if(skiaContent,0-1,0-18) random-if(layersGPUAccelerated) == boxshadow-mixed.html boxshadow-mixed-ref.html
 fuzzy-if(skiaContent,0-1,0-17) == boxshadow-mixed-2.html boxshadow-mixed-2-ref.html
 random-if(d2d) fuzzy-if(skiaContent,0-1,0-212) fuzzy-if(webrender,0-127,0-3528) == boxshadow-rounded-spread.html boxshadow-rounded-spread-ref.html
 fuzzy-if(skiaContent,0-1,0-50) == chrome://reftest/content/box-shadow/boxshadow-dynamic.xhtml chrome://reftest/content/box-shadow/boxshadow-dynamic-ref.xhtml
 random-if(d2d) fuzzy-if(skiaContent,0-1,0-14) == boxshadow-onecorner.html boxshadow-onecorner-ref.html
 random-if(d2d) fuzzy-if(skiaContent,0-1,0-22) == boxshadow-twocorners.html boxshadow-twocorners-ref.html
 random-if(d2d) fuzzy-if(skiaContent,0-1,0-36) == boxshadow-threecorners.html boxshadow-threecorners-ref.html
 fuzzy(0-2,0-440) == boxshadow-skiprect.html boxshadow-skiprect-ref.html
-fuzzy-if(useDrawSnapshot,1-1,1197-1197) == boxshadow-opacity.html boxshadow-opacity-ref.html
+== boxshadow-opacity.html boxshadow-opacity-ref.html
 == boxshadow-color-rounding.html boxshadow-color-rounding-ref.html
 == boxshadow-color-rounding-middle.html boxshadow-color-rounding-middle-ref.html
 fuzzy(0-3,0-500) fuzzy-if(d2d,0-2,0-1080) == boxshadow-border-radius-int.html boxshadow-border-radius-int-ref.html
 == boxshadow-inset-neg-spread.html about:blank
 == boxshadow-inset-neg-spread2.html boxshadow-inset-neg-spread2-ref.html
 fuzzy(0-26,0-3610) fuzzy-if(d2d,0-26,0-5910) fuzzy-if(webrender&&!(swgl&&Android),0-6,378-4500) == boxshadow-rotated.html boxshadow-rotated-ref.html # Bug 1211264
 == boxshadow-inset-large-border-radius.html boxshadow-inset-large-border-radius-ref.html
 
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1686,17 +1686,17 @@ fuzzy-if(cocoaWidget,0-1,0-300000) fuzzy
 == 748692-1a.html 748692-1-ref.html
 == 748692-1b.html 748692-1-ref.html
 == 748803-1.html 748803-1-ref.html
 == 750551-1.html 750551-1-ref.html
 fuzzy-if(skiaContent,0-1,0-1) == 751012-1a.html 751012-1-ref.html
 fuzzy-if(skiaContent,0-1,0-1) == 751012-1b.html 751012-1-ref.html
 == 753329-1.html about:blank
 == 758561-1.html 758561-1-ref.html
-fuzzy-if(true,0-1,0-90) fuzzy-if(skiaContent,0-1,0-320) fuzzy-if(useDrawSnapshot,1-1,77595-77595) == 759036-1.html 759036-1-ref.html
+fuzzy-if(true,0-1,0-90) fuzzy-if(skiaContent,0-1,0-320) == 759036-1.html 759036-1-ref.html
 fuzzy-if(true,0-17,0-5886) fuzzy-if(skiaContent,0-9,0-5894) fuzzy-if(geckoview&&webrender&&device&&!swgl,3-3,5831-5855) == 759036-2.html 759036-2-ref.html
 == 776265-1a.html 776265-1-ref.html
 == 776265-1b.html 776265-1-ref.html
 == 776265-1c.html 776265-1-ref.html
 == 776265-1d.html 776265-1-ref.html
 == 776265-2a.html 776265-2-ref.html
 == 776265-2b.html 776265-2-ref.html
 == 776265-2c.html 776265-2-ref.html
@@ -2028,17 +2028,17 @@ test-pref(font.size.systemFontScale,200)
 fuzzy(0-255,0-4054) == 1415987-1.html 1415987-1-ref.html # this is a large fuzz, without the fix the test fails in the range 246-255,3237-25110, which overlaps the fuzz range, but would still catch regressing this bug some of the time which is better than not having a test.
 == 1419820-1.html 1419820-1-ref.html
 == 1420946-1.html 1420946-1-ref.html
 == 1422393.html 1422393-ref.html
 == 1424177.html 1424177-ref.html
 == 1424680.html 1424680-ref.html
 == 1424798-1.html 1424798-ref.html
 fuzzy-if(!webrender,0-74,0-2234) == 1425243-1.html 1425243-1-ref.html
-fuzzy-if(Android,0-66,0-574) fuzzy-if(d2d,0-89,0-777) fuzzy-if(!Android&&!d2d,0-1,0-31412) fuzzy-if(webrender&&winWidget,1-1,31240-31412) == 1425243-2.html 1425243-2-ref.html
+fuzzy-if(Android,0-66,0-574) fuzzy-if(d2d,0-89,0-777) fuzzy-if(!Android&&!d2d,0-1,0-31412) fuzzy-if(webrender&&winWidget,1-1,31240-31412) fails-if(useDrawSnapshot) == 1425243-2.html 1425243-2-ref.html
 == 1430869.html 1430869-ref.html
 == 1432541.html 1432541-ref.html
 == 1446470.html 1035091-ref.html
 fails-if(useDrawSnapshot&&!webrender) == 1456111-1.html about:blank
 # Note: The following test depends on having a pref-controllable CSS property,
 # and the property needs to have a non-initial value that causes it to create
 # a containing block for fixed-position descendants. It doesn't matter whether
 # the pref is enabled by default; we just need to be able to pref it off here.
@@ -2054,17 +2054,17 @@ test-pref(layout.css.visited_links_enabl
 pref(layout.css.supports-selector.enabled,true) == 1499386.html 1499386-ref.html
 pref(layout.css.supports-selector.enabled,false) != 1499386.html 1499386-ref.html
 == 1509425-1.html 1509425-1-ref.html
 == 1511570.html 1511570-ref.html
 fuzzy-if(winWidget&&!webrender,0-104,0-1423) == 1513423-1.html 1513423-1-ref.html
 fuzzy-if(winWidget&&!webrender,0-89,0-1420) == 1513423-2.html 1513423-2-ref.html
 == 1513423-3.html 1513423-3-ref.html
 pref(layout.accessiblecaret.enabled,true) == 1517385.html 1517385-ref.html
-fuzzy-if(!webrender,1-5,66-547) fuzzy-if(geckoview&&!webrender,1-2,64-141) fuzzy-if(winWidget&&swgl,1-1,12-16) fuzzy-if(cocoaWidget&&swgl,1-1,32-32) fuzzy-if(useDrawSnapshot&&webrender,2-2,209-209) == 1529992-1.html 1529992-1-ref.html
+fuzzy-if(!webrender,1-5,66-547) fuzzy-if(geckoview&&!webrender,1-2,64-141) fuzzy-if(winWidget&&swgl,1-1,12-16) fuzzy-if(cocoaWidget&&swgl,1-1,32-32) fuzzy-if(useDrawSnapshot&&webrender,3-3,459-459) == 1529992-1.html 1529992-1-ref.html
 fuzzy-if(!webrender,0-6,0-34) fuzzy-if(Android,9-14,44-60) fails-if(!useDrawSnapshot&&webrender) == 1529992-2.html 1529992-2-ref.html
 == 1535040-1.html 1535040-1-ref.html
 == 1545360-1.xhtml 1545360-1-ref.xhtml
 skip-if(!asyncPan) == 1544895.html 1544895-ref.html
 random-if(useDrawSnapshot) == 1546856-1.html 1546856-ref.html
 == 1546856-2.html 1546856-ref.html
 == 1547759-1.html 1547759-1-ref.html
 == 1548809.html 1548809-ref.html
--- a/layout/reftests/display-list/reftest.list
+++ b/layout/reftests/display-list/reftest.list
@@ -17,17 +17,17 @@ skip-if(!retainedDisplayList) == retaine
 == retained-dl-zindex-2.html retained-dl-zindex-2-ref.html
 == retained-dl-style-change-with-prerender-transform-1.html retained-dl-style-change-with-prerender-transform-1-ref.html
 == invalidated-blendmode-sorting.html invalidated-blendmode-sorting-ref.html
 fuzzy(0-1,0-235200) == 1413073.html 1413073-ref.html
 == 1416291.html 1416291-ref.html
 fuzzy-if(useDrawSnapshot&&!webrender,1-1,40000-40000) == 1417601-1.html 1417601-1-ref.html
 == 1418945-1.html 1418945-1-ref.html
 skip-if(Android) == 1428993-1.html 1428993-1-ref.html
-fuzzy-if(!Android&&!swgl&&!useDrawSnapshot,1-1,40000-40000) == 1420480-1.html 1420480-1-ref.html
+== 1420480-1.html 1420480-1-ref.html
 == 1428993-2.html 1428993-2-ref.html
 needs-focus fuzzy-if(!nativeThemePref,0-3,0-2) == 1429027-1.html 1429027-1-ref.html
 == 1432553-1.html 1432553-1-ref.html
 == 1432553-2.html 1432553-2-ref.html
 == 1436189-1.html 1436189-1-ref.html
 skip-if(!asyncPan) == 1437374-1.html 1437374-1-ref.html
 == 1439809-1.html 1439809-1-ref.html
 == 1443027-1.html 1443027-ref.html
--- a/layout/reftests/table-background/reftest.list
+++ b/layout/reftests/table-background/reftest.list
@@ -40,17 +40,17 @@ random-if(useDrawSnapshot&&webrender) ==
 random-if(useDrawSnapshot&&webrender) == border-collapse-table-row-group.html border-collapse-table-row-group-ref.html # Bug 1715400
 random-if(useDrawSnapshot&&webrender) == border-collapse-table-row.html border-collapse-table-row-ref.html # Bug 1715400
 random-if(useDrawSnapshot&&webrender) == border-collapse-table.html border-collapse-table-ref.html # Bug 1715400
 fuzzy-if(d2d,0-1,0-1083) fuzzy-if(skiaContent,0-1,0-2200) random-if(useDrawSnapshot&&webrender) == border-collapse-opacity-table-cell.html border-collapse-opacity-table-cell-ref.html
 fuzzy-if(d2d,0-1,0-33174) fuzzy-if(skiaContent,0-1,0-16863) random-if(useDrawSnapshot&&webrender) == border-collapse-opacity-table-column-group.html border-collapse-opacity-table-column-group-ref.html
 fuzzy-if(d2d,0-1,0-11058) fuzzy-if(skiaContent,0-1,0-5625) random-if(useDrawSnapshot&&webrender) == border-collapse-opacity-table-column.html border-collapse-opacity-table-column-ref.html
 fuzzy-if(d2d,0-1,0-24606) fuzzy-if(skiaContent,0-1,0-32718) random-if(useDrawSnapshot&&webrender) == border-collapse-opacity-table-row-group.html border-collapse-opacity-table-row-group-ref.html
 fuzzy-if(d2d,0-1,0-11000) fuzzy-if(skiaContent,0-1,0-11000) random-if(useDrawSnapshot&&webrender) == border-collapse-opacity-table-row.html border-collapse-opacity-table-row-ref.html
-fuzzy-if(d2d||skiaContent,0-1,0-60000) == border-collapse-opacity-table.html border-collapse-opacity-table-ref.html
+fuzzy-if(d2d||skiaContent,0-1,0-60000) fails-if(useDrawSnapshot&&webrender) == border-collapse-opacity-table.html border-collapse-opacity-table-ref.html
 fuzzy-if(d2d,0-1,0-2478) fuzzy-if(skiaContent,0-1,0-2500) random-if(useDrawSnapshot&&webrender) == border-separate-opacity-table-cell.html border-separate-opacity-table-cell-ref.html
 fuzzy-if(d2d,0-1,0-38000) fuzzy-if(webrender,0-1,0-4078) random-if(useDrawSnapshot&&webrender) == border-separate-opacity-table-column-group.html border-separate-opacity-table-column-group-ref.html
 fuzzy-if(d2d,0-1,0-13000) fuzzy-if(webrender,0-1,0-1329) random-if(useDrawSnapshot&&webrender) == border-separate-opacity-table-column.html border-separate-opacity-table-column-ref.html
 fuzzy-if(d2d,0-1,0-37170) fuzzy-if(skiaContent,0-1,0-38000) random-if(useDrawSnapshot&&webrender) == border-separate-opacity-table-row-group.html border-separate-opacity-table-row-group-ref.html
 fuzzy-if(d2d,0-1,0-12390) fuzzy-if(skiaContent,0-1,0-13000) random-if(useDrawSnapshot&&webrender) == border-separate-opacity-table-row.html border-separate-opacity-table-row-ref.html
 fuzzy-if(d2d||skiaContent,0-1,0-95000) random-if(useDrawSnapshot&&webrender) == border-separate-opacity-table.html border-separate-opacity-table-ref.html
 != scrollable-rowgroup-collapse-background.html scrollable-rowgroup-collapse-notref.html
 != scrollable-rowgroup-collapse-border.html scrollable-rowgroup-collapse-notref.html
--- a/layout/svg/SVGIntegrationUtils.h
+++ b/layout/svg/SVGIntegrationUtils.h
@@ -146,18 +146,18 @@ class SVGIntegrationUtils final {
    * Returns true if the given point is not clipped out by effects.
    * @param aPt in appunits relative to aFrame
    */
   static bool HitTestFrameForEffects(nsIFrame* aFrame, const nsPoint& aPt);
 
   struct MOZ_STACK_CLASS PaintFramesParams {
     gfxContext& ctx;
     nsIFrame* frame;
-    nsRect dirtyRect;
-    nsRect borderArea;
+    const nsRect& dirtyRect;
+    const nsRect& borderArea;
     nsDisplayListBuilder* builder;
     layers::LayerManager* layerManager;
     bool handleOpacity;  // If true, PaintMaskAndClipPath/ PaintFilter should
                          // apply css opacity.
     Maybe<gfx::Rect> maskRect;
     imgDrawingParams& imgParams;
 
     explicit PaintFramesParams(gfxContext& aCtx, nsIFrame* aFrame,
--- a/layout/svg/SVGTextFrame.cpp
+++ b/layout/svg/SVGTextFrame.cpp
@@ -2709,16 +2709,19 @@ void DisplaySVGText::HitTest(nsDisplayLi
 
   nsIFrame* target = frame->GetFrameForPoint(userSpacePt);
   if (target) {
     aOutFrames->AppendElement(target);
   }
 }
 
 void DisplaySVGText::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
+  DrawTargetAutoDisableSubpixelAntialiasing disable(aCtx->GetDrawTarget(),
+                                                    IsSubpixelAADisabled());
+
   uint32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
 
   // ToReferenceFrame includes our mRect offset, but painting takes
   // account of that too. To avoid double counting, we subtract that
   // here.
   nsPoint offset = ToReferenceFrame() - mFrame->GetPosition();
 
   gfxPoint devPixelOffset =
--- a/layout/tables/nsTableFrame.cpp
+++ b/layout/tables/nsTableFrame.cpp
@@ -7562,16 +7562,14 @@ void nsDisplayTableBorderCollapse::Paint
       *drawTarget, GetPaintRect(aBuilder, aCtx) - pt);
 }
 
 bool nsDisplayTableBorderCollapse::CreateWebRenderCommands(
     wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
     const StackingContextHelper& aSc,
     mozilla::layers::RenderRootStateManager* aManager,
     nsDisplayListBuilder* aDisplayListBuilder) {
-  bool dummy;
   static_cast<nsTableFrame*>(mFrame)->CreateWebRenderCommandsForBCBorders(
-      aBuilder, aSc, GetBounds(aDisplayListBuilder, &dummy),
-      ToReferenceFrame());
+      aBuilder, aSc, GetPaintRect(), ToReferenceFrame());
   return true;
 }
 
 }  // namespace mozilla
--- a/layout/xul/nsTextBoxFrame.cpp
+++ b/layout/xul/nsTextBoxFrame.cpp
@@ -258,32 +258,33 @@ class nsDisplayXULTextBox final : public
 static void PaintTextShadowCallback(gfxContext* aCtx, nsPoint aShadowOffset,
                                     const nscolor& aShadowColor, void* aData) {
   reinterpret_cast<nsDisplayXULTextBox*>(aData)->PaintTextToContext(
       aCtx, aShadowOffset, &aShadowColor);
 }
 
 void nsDisplayXULTextBox::Paint(nsDisplayListBuilder* aBuilder,
                                 gfxContext* aCtx) {
+  DrawTargetAutoDisableSubpixelAntialiasing disable(aCtx->GetDrawTarget(),
+                                                    IsSubpixelAADisabled());
+
   // Paint the text shadow before doing any foreground stuff
   nsRect drawRect =
       static_cast<nsTextBoxFrame*>(mFrame)->mTextDrawRect + ToReferenceFrame();
-  nsLayoutUtils::PaintTextShadow(mFrame, aCtx, drawRect,
-                                 GetPaintRect(aBuilder, aCtx),
+  nsLayoutUtils::PaintTextShadow(mFrame, aCtx, drawRect, GetPaintRect(),
                                  mFrame->StyleText()->mColor.ToColor(),
                                  PaintTextShadowCallback, (void*)this);
 
   PaintTextToContext(aCtx, nsPoint(0, 0), nullptr);
 }
 
 void nsDisplayXULTextBox::PaintTextToContext(gfxContext* aCtx, nsPoint aOffset,
                                              const nscolor* aColor) {
   static_cast<nsTextBoxFrame*>(mFrame)->PaintTitle(
-      *aCtx, mFrame->InkOverflowRectRelativeToSelf() + ToReferenceFrame(),
-      ToReferenceFrame() + aOffset, aColor);
+      *aCtx, GetPaintRect(), ToReferenceFrame() + aOffset, aColor);
 }
 
 bool nsDisplayXULTextBox::CreateWebRenderCommands(
     mozilla::wr::DisplayListBuilder& aBuilder,
     mozilla::wr::IpcResourceUpdateQueue& aResources,
     const StackingContextHelper& aSc,
     mozilla::layers::RenderRootStateManager* aManager,
     nsDisplayListBuilder* aDisplayListBuilder) {
--- a/layout/xul/tree/nsTreeBodyFrame.cpp
+++ b/layout/xul/tree/nsTreeBodyFrame.cpp
@@ -2520,16 +2520,19 @@ class nsDisplayTreeBody final : public n
 
     nsPaintedDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry,
                                                     aInvalidRegion);
   }
 
   virtual void Paint(nsDisplayListBuilder* aBuilder,
                      gfxContext* aCtx) override {
     MOZ_ASSERT(aBuilder);
+    DrawTargetAutoDisableSubpixelAntialiasing disable(aCtx->GetDrawTarget(),
+                                                      IsSubpixelAADisabled());
+
     ImgDrawResult result = static_cast<nsTreeBodyFrame*>(mFrame)->PaintTreeBody(
         *aCtx, GetPaintRect(aBuilder, aCtx), ToReferenceFrame(), aBuilder);
 
     nsDisplayTreeBodyGeometry::UpdateDrawResult(this, result);
   }
 
   NS_DISPLAY_DECL_NAME("XULTreeBody", TYPE_XUL_TREE_BODY)