Bug 1298218 - Use DisplayItemClipChain for tracking clips on display items. r=mattwoodrow,tnikkel
authorMarkus Stange <mstange@themasta.com>
Tue, 31 Jan 2017 17:07:35 -0500
changeset 332161 2023dadca59c6eef58385af514d349b7d3c79ab2
parent 332160 a97b00d08fbd0705812120c3b2c35ded3812a16d
child 332162 faefb3b2598889fd770e616c8daacc0017565054
push id86472
push usercbook@mozilla.com
push dateThu, 02 Feb 2017 13:28:39 +0000
treeherdermozilla-inbound@8a44f5ca07bb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow, tnikkel
bugs1298218
milestone54.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1298218 - Use DisplayItemClipChain for tracking clips on display items. r=mattwoodrow,tnikkel This is the bulk of the changes. - DisplayItemScrollClip is removed. Instead, we will have 1) ActiveScrolledRoot and 2) DisplayItemClipChain. - ActiveScrolledRoot points to a scroll frame and allows traversing up the scroll frame chain. - DisplayItemClipChain is a linked list of clips, each clip being associated with the ActiveScrolledRoot that moves this clip. - Each display item has an ActiveScrolledRoot and a clip chain. - nsDisplayItem::GetClip returns the item of the clip chain that scrolls with the item's ASR. The separation between "regular clip" and "scroll clips" mostly goes away. - Tracking clips in the display list builder's clip state happens very similarly to how regular clips used to be tracked - there's a clip chain for content descendants and a clip chain for containing block descendants. These clip chains are intersected to create the combined clip chain. - There are strict rules for the ASR of a container item: A container item's ASR should be the innermost ASR which the item has finite clipped bounds with respect to. - At some point in the future, ASRs and AGRs should be reunified, but I haven't done that yet, because I needed to limit the scope of the change. MozReview-Commit-ID: KYEpWY7qgf2
layout/base/PresShell.cpp
layout/base/nsLayoutDebugger.cpp
layout/generic/ViewportFrame.cpp
layout/generic/nsCanvasFrame.cpp
layout/generic/nsFrame.cpp
layout/generic/nsGfxScrollFrame.cpp
layout/generic/nsPluginFrame.cpp
layout/generic/nsSubDocumentFrame.cpp
layout/generic/nsTextFrame.cpp
layout/painting/DisplayListClipState.cpp
layout/painting/DisplayListClipState.h
layout/painting/FrameLayerBuilder.cpp
layout/painting/FrameLayerBuilder.h
layout/painting/nsDisplayList.cpp
layout/painting/nsDisplayList.h
layout/printing/crashtests/crashtests.list
layout/xul/nsBoxFrame.cpp
layout/xul/nsSliderFrame.cpp
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -4852,18 +4852,18 @@ PresShell::ClipListToRange(nsDisplayList
             nscoord x = std::min(startPoint.x, endPoint.x);
             textRect.x += x;
             textRect.width = std::max(startPoint.x, endPoint.x) - x;
           }
           surfaceRect.UnionRect(surfaceRect, textRect);
 
           DisplayItemClip newClip;
           newClip.SetTo(textRect);
-          newClip.IntersectWith(i->GetClip());
-          i->SetClip(aBuilder, newClip);
+          DisplayItemClipChain newClipChain = { newClip, i->GetActiveScrolledRoot(), nullptr };
+          i->IntersectClip(aBuilder, &newClipChain);
           itemToInsert = i;
         }
       }
       // Don't try to descend into subdocuments.
       // If this ever changes we'd need to add handling for subdocuments with
       // different zoom levels.
       else if (content->GetUncomposedDoc() ==
                  aRange->GetStartParent()->GetUncomposedDoc()) {
--- a/layout/base/nsLayoutDebugger.cpp
+++ b/layout/base/nsLayoutDebugger.cpp
@@ -10,17 +10,16 @@
 
 #include "nsILayoutDebugger.h"
 
 #include "nsAttrValue.h"
 #include "nsFrame.h"
 #include "nsDisplayList.h"
 #include "FrameLayerBuilder.h"
 #include "nsPrintfCString.h"
-#include "DisplayItemScrollClip.h"
 
 #include <iostream>
 #include <stdio.h>
 
 using namespace mozilla;
 using namespace mozilla::layers;
 
 #ifdef DEBUG
@@ -145,25 +144,26 @@ PrintDisplayItemTo(nsDisplayListBuilder*
   if (aDumpHtml && aItem->Painted()) {
     nsCString string(aItem->Name());
     string.Append('-');
     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) %sbounds(%d,%d,%d,%d) layerBounds(%d,%d,%d,%d) visible(%d,%d,%d,%d) componentAlpha(%d,%d,%d,%d) clip(%s) scrollClip(%s)%s ref=0x%p agr=0x%p",
+  aStream << nsPrintfCString("%s p=0x%p f=0x%p(%s) %sbounds(%d,%d,%d,%d) layerBounds(%d,%d,%d,%d) visible(%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->ZIndex() ? nsPrintfCString("z=%d ", aItem->ZIndex()).get() : ""),
           rect.x, rect.y, rect.width, rect.height,
           layerRect.x, layerRect.y, layerRect.width, layerRect.height,
           vis.x, vis.y, vis.width, vis.height,
           component.x, component.y, component.width, component.height,
           clip.ToString().get(),
-          DisplayItemScrollClip::ToString(aItem->ScrollClip()).get(),
+          ActiveScrolledRoot::ToString(aItem->GetActiveScrolledRoot()).get(),
+          DisplayItemClipChain::ToString(aItem->GetClipChain()).get(),
           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);
   }
 
--- a/layout/generic/ViewportFrame.cpp
+++ b/layout/generic/ViewportFrame.cpp
@@ -97,24 +97,27 @@ ShouldInTopLayerForFullscreen(Element* a
 
 static void
 BuildDisplayListForTopLayerFrame(nsDisplayListBuilder* aBuilder,
                                  nsIFrame* aFrame,
                                  nsDisplayList* aList)
 {
   nsRect dirty;
   DisplayListClipState::AutoClipMultiple clipState(aBuilder);
+  nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter asrSetter(aBuilder);
   nsDisplayListBuilder::OutOfFlowDisplayData*
     savedOutOfFlowData = nsDisplayListBuilder::GetOutOfFlowData(aFrame);
   if (savedOutOfFlowData) {
     dirty = savedOutOfFlowData->mDirtyRect;
-    clipState.SetClipForContainingBlockDescendants(
-      &savedOutOfFlowData->mContainingBlockClip);
-    clipState.SetScrollClipForContainingBlockDescendants(
-      savedOutOfFlowData->mContainingBlockScrollClip);
+    clipState.SetClipChainForContainingBlockDescendants(
+      savedOutOfFlowData->mContainingBlockClipChain);
+    clipState.ClipContainingBlockDescendantsExtra(
+      dirty + aBuilder->ToReferenceFrame(aFrame), nullptr);
+    asrSetter.SetCurrentActiveScrolledRoot(
+      savedOutOfFlowData->mContainingBlockActiveScrolledRoot);
   }
   nsDisplayList list;
   aFrame->BuildDisplayListForStackingContext(aBuilder, dirty, &list);
   aList->AppendToTop(&list);
 }
 
 void
 ViewportFrame::BuildDisplayListForTopLayer(nsDisplayListBuilder* aBuilder,
--- a/layout/generic/nsCanvasFrame.cpp
+++ b/layout/generic/nsCanvasFrame.cpp
@@ -472,60 +472,89 @@ nsCanvasFrame::BuildDisplayList(nsDispla
         new (aBuilder) nsDisplayCanvasThemedBackground(aBuilder, this));
       return;
     }
 
     if (!bg) {
       return;
     }
 
-    const DisplayItemScrollClip* scrollClip =
-      aBuilder->ClipState().GetCurrentInnermostScrollClip();
+    const ActiveScrolledRoot* asr =
+      aBuilder->CurrentActiveScrolledRoot();
 
     bool needBlendContainer = false;
+    nsDisplayListBuilder::AutoContainerASRTracker contASRTracker(aBuilder);
 
     // Create separate items for each background layer.
     const nsStyleImageLayers& layers = bg->mImage;
     NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, layers) {
       if (layers.mLayers[i].mImage.IsEmpty()) {
         continue;
       }
       if (layers.mLayers[i].mBlendMode != NS_STYLE_BLEND_NORMAL) {
         needBlendContainer = true;
       }
 
       nsRect bgRect = GetRectRelativeToSelf() + aBuilder->ToReferenceFrame(this);
+
+      const ActiveScrolledRoot* thisItemASR = asr;
+      nsDisplayList thisItemList;
       nsDisplayBackgroundImage::InitData bgData =
         nsDisplayBackgroundImage::GetInitData(aBuilder, this, i, bgRect, bg,
                                               nsDisplayBackgroundImage::LayerizeFixed::ALWAYS_LAYERIZE_FIXED_BACKGROUND);
 
-      nsDisplayList thisItemList;
       if (bgData.shouldFixToViewport) {
-        nsDisplayCanvasBackgroundImage* bgItem =
-          new (aBuilder) nsDisplayCanvasBackgroundImage(bgData);
+
+        auto* displayData = aBuilder->GetCurrentFixedBackgroundDisplayData();
+        nsDisplayListBuilder::AutoBuildingDisplayList
+          buildingDisplayList(aBuilder, this, aBuilder->GetDirtyRect(), false);
+
+        DisplayListClipState::AutoSaveRestore clipState(aBuilder);
+        nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter asrSetter(aBuilder);
+        if (displayData) {
+          nsRect dirtyRect = displayData->mDirtyRect + GetOffsetTo(PresContext()->GetPresShell()->GetRootFrame());
+          buildingDisplayList.SetDirtyRect(dirtyRect);
+          clipState.SetClipChainForContainingBlockDescendants(
+            displayData->mContainingBlockClipChain);
+          asrSetter.SetCurrentActiveScrolledRoot(
+            displayData->mContainingBlockActiveScrolledRoot);
+          thisItemASR = displayData->mContainingBlockActiveScrolledRoot;
+        }
+        nsDisplayCanvasBackgroundImage* bgItem = nullptr;
+        {
+          DisplayListClipState::AutoSaveRestore bgImageClip(aBuilder);
+          bgImageClip.Clear();
+          bgItem = new (aBuilder) nsDisplayCanvasBackgroundImage(bgData);
+        }
         thisItemList.AppendNewToTop(
           nsDisplayFixedPosition::CreateForFixedBackground(aBuilder, this, bgItem, i));
+
       } else {
         thisItemList.AppendNewToTop(new (aBuilder) nsDisplayCanvasBackgroundImage(bgData));
       }
 
       if (layers.mLayers[i].mBlendMode != NS_STYLE_BLEND_NORMAL) {
+        DisplayListClipState::AutoSaveRestore blendClip(aBuilder);
+        blendClip.ClearUpToASR(thisItemASR);
         thisItemList.AppendNewToTop(
           new (aBuilder) nsDisplayBlendMode(aBuilder, this, &thisItemList,
                                             layers.mLayers[i].mBlendMode,
-                                            scrollClip, i + 1));
+                                            thisItemASR, i + 1));
       }
       aLists.BorderBackground()->AppendToTop(&thisItemList);
     }
 
     if (needBlendContainer) {
+      const ActiveScrolledRoot* containerASR = contASRTracker.GetContainerASR();
+      DisplayListClipState::AutoSaveRestore blendContainerClip(aBuilder);
+      blendContainerClip.ClearUpToASR(containerASR);
       aLists.BorderBackground()->AppendNewToTop(
         nsDisplayBlendContainer::CreateForBackgroundBlendMode(aBuilder, this,
                                                               aLists.BorderBackground(),
-                                                              scrollClip));
+                                                              containerASR));
     }
   }
 
   for (nsIFrame* kid : PrincipalChildList()) {
     // Put our child into its own pseudo-stack.
     BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists);
   }
 
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -80,17 +80,16 @@
 #include "nsChangeHint.h"
 #include "nsDeckFrame.h"
 #include "nsSubDocumentFrame.h"
 #include "SVGTextFrame.h"
 
 #include "gfxContext.h"
 #include "nsRenderingContext.h"
 #include "nsAbsoluteContainingBlock.h"
-#include "DisplayItemScrollClip.h"
 #include "StickyScrollContainer.h"
 #include "nsFontInflationData.h"
 #include "nsRegion.h"
 #include "nsIFrameInlines.h"
 
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/EffectCompositor.h"
 #include "mozilla/EffectSet.h"
@@ -2271,85 +2270,81 @@ nsIFrame::BuildDisplayListForStackingCon
   // multiple container display items and wrap them around our contents.
   // This enum lists all the potential container display items, in the order
   // outside to inside.
   enum class ContainerItemType : uint8_t {
     eNone = 0,
     eOwnLayerIfNeeded,
     eBlendMode,
     eFixedPosition,
-    eStickyPosition,
     eOwnLayerForTransformWithRoundedClip,
     ePerspective,
     eTransform,
     eSeparatorTransforms,
     eOpacity,
     eFilter,
     eBlendContainer
   };
 
+  nsDisplayListBuilder::AutoContainerASRTracker contASRTracker(aBuilder);
+
   DisplayListClipState::AutoSaveRestore clipState(aBuilder);
 
   // If there is a current clip, then depending on the container items we
   // create, different things can happen to it. Some container items simply
   // propagate the clip to their children and aren't clipped themselves.
   // But other container items, especially those that establish a different
   // geometry for their contents (e.g. transforms), capture the clip on
   // themselves and unset the clip for their contents. If we create more than
   // one of those container items, the clip will be captured on the outermost
   // one and the inner container items will be unclipped.
   ContainerItemType clipCapturedBy = ContainerItemType::eNone;
   if (useFixedPosition) {
     clipCapturedBy = ContainerItemType::eFixedPosition;
-  } else if (useStickyPosition) {
-    clipCapturedBy = ContainerItemType::eStickyPosition;
   } else if (isTransformed) {
-    if ((hasPerspective || extend3DContext) && clipState.SavedStateHasRoundedCorners()) {
+    const DisplayItemClipChain* currentClip =
+      aBuilder->ClipState().GetCurrentCombinedClipChain(aBuilder);
+    if ((hasPerspective || extend3DContext) &&
+        (currentClip && currentClip->HasRoundedCorners())) {
       // If we're creating an nsDisplayTransform item that is going to combine
       // its transform with its children (preserve-3d or perspective), then we
       // can't have an intermediate surface. Mask layers force an intermediate
       // surface, so if we're going to need both then create a separate
       // wrapping layer for the mask.
       clipCapturedBy = ContainerItemType::eOwnLayerForTransformWithRoundedClip;
     } else if (hasPerspective) {
       clipCapturedBy = ContainerItemType::ePerspective;
     } else {
       clipCapturedBy = ContainerItemType::eTransform;
     }
   } else if (usingFilter) {
     clipCapturedBy = ContainerItemType::eFilter;
   }
 
-  bool clearClip = false;
   if (clipCapturedBy != ContainerItemType::eNone) {
-    // We don't need to pass ancestor clipping down to our children;
-    // everything goes inside a display item's child list, and the display
-    // item itself will be clipped.
-    // For transforms we also need to clear ancestor clipping because it's
-    // relative to the wrong display item reference frame anyway.
-    clearClip = true;
-  }
-
-  clipState.EnterStackingContextContents(clearClip);
+    clipState.Clear();
+  }
 
   nsDisplayListCollection set;
   {
     DisplayListClipState::AutoSaveRestore nestedClipState(aBuilder);
     nsDisplayListBuilder::AutoInTransformSetter
       inTransformSetter(aBuilder, inTransform);
     nsDisplayListBuilder::AutoSaveRestorePerspectiveIndex
       perspectiveIndex(aBuilder, this);
 
     CheckForApzAwareEventHandlers(aBuilder, this);
 
-    Maybe<nsRect> clipPropClip = GetClipPropClipRect(disp, effects, GetSize());
-    if (clipPropClip) {
-      dirtyRect.IntersectRect(dirtyRect, *clipPropClip);
-      nestedClipState.ClipContentDescendants(
-        *clipPropClip + aBuilder->ToReferenceFrame(this));
+    Maybe<nsRect> contentClip =
+      GetClipPropClipRect(disp, effects, GetSize());
+
+    if (contentClip) {
+      dirtyRect.IntersectRect(dirtyRect, *contentClip);
+      nestedClipState.ClipContentDescendants(*contentClip +
+                                             aBuilder->ToReferenceFrame(this));
     }
 
     // extend3DContext also guarantees that applyAbsPosClipping and usingSVGEffects are false
     // We only modify the preserve-3d rect if we are the top of a preserve-3d heirarchy
     if (extend3DContext) {
       // Mark these first so MarkAbsoluteFramesForDisplayList knows if we are
       // going to be forced to descend into frames.
       aBuilder->MarkPreserve3DFramesForDisplayList(this);
@@ -2429,50 +2424,45 @@ nsIFrame::BuildDisplayListForStackingCon
   }
 #ifdef DEBUG
   DisplayDebugBorders(aBuilder, this, set);
 #endif
   resultList.AppendToTop(set.Outlines());
   // 8, 9: non-negative z-index children
   resultList.AppendToTop(set.PositionedDescendants());
 
-  // Get the scroll clip to use for the container items that we create here.
-  // If we cleared the clip, and we create multiple container items, then the
-  // items we create before we restore the clip will have a different scroll
-  // clip from the items we create after we restore the clip.
-  const DisplayItemScrollClip* containerItemScrollClip =
-    aBuilder->ClipState().CurrentAncestorScrollClipForStackingContextContents();
+  // Get the ASR to use for the container items that we create here.
+  const ActiveScrolledRoot* containerItemASR = contASRTracker.GetContainerASR();
 
   /* If adding both a nsDisplayBlendContainer and a nsDisplayBlendMode to the
    * same list, the nsDisplayBlendContainer should be added first. This only
    * happens when the element creating this stacking context has mix-blend-mode
    * and also contains a child which has mix-blend-mode.
    * The nsDisplayBlendContainer must be added to the list first, so it does not
    * isolate the containing element blending as well.
    */
-
   if (aBuilder->ContainsBlendMode()) {
     DisplayListClipState::AutoSaveRestore blendContainerClipState(aBuilder);
-    blendContainerClipState.Clear();
+    blendContainerClipState.ClearUpToASR(containerItemASR);
     resultList.AppendNewToTop(
       nsDisplayBlendContainer::CreateForMixBlendMode(aBuilder, this, &resultList,
-                                                     containerItemScrollClip));
+                                                     containerItemASR));
   }
 
   /* If there are any SVG effects, wrap the list up in an SVG effects item
    * (which also handles CSS group opacity). Note that we create an SVG effects
    * item even if resultList is empty, since a filter can produce graphical
    * output even if the element being filtered wouldn't otherwise do so.
    */
   if (usingSVGEffects) {
     MOZ_ASSERT(usingFilter ||usingMask,
                "Beside filter & mask/clip-path, what else effect do we have?");
 
     if (clipCapturedBy == ContainerItemType::eFilter) {
-      clipState.ExitStackingContextContents(&containerItemScrollClip);
+      clipState.Restore();
     }
     // Revert to the post-filter dirty rect.
     buildingDisplayList.SetDirtyRect(dirtyRectOutsideSVGEffects);
 
     // Skip all filter effects while generating glyph mask.
     if (usingFilter && !aBuilder->IsForGenerateGlyphMask()) {
       // If we are going to create a mask display item, handle opacity effect
       // in that mask display item; Otherwise, take care of opacity in this
@@ -2482,41 +2472,42 @@ nsIFrame::BuildDisplayListForStackingCon
       /* List now emptied, so add the new list to the top. */
       resultList.AppendNewToTop(
         new (aBuilder) nsDisplayFilter(aBuilder, this, &resultList,
                                        handleOpacity));
     }
 
     if (usingMask) {
       DisplayListClipState::AutoSaveRestore maskClipState(aBuilder);
-      maskClipState.Clear();
+      maskClipState.ClearUpToASR(containerItemASR);
       /* List now emptied, so add the new list to the top. */
       resultList.AppendNewToTop(
           new (aBuilder) nsDisplayMask(aBuilder, this, &resultList,
-                                       !useOpacity, containerItemScrollClip));
+                                       !useOpacity, containerItemASR));
     }
 
     // Also add the hoisted scroll info items. We need those for APZ scrolling
     // because nsDisplayMask items can't build active layers.
     aBuilder->ExitSVGEffectsContents();
     resultList.AppendToTop(&hoistedScrollInfoItemsStorage);
   }
 
   /* If the list is non-empty and there is CSS group opacity without SVG
    * effects, wrap it up in an opacity item.
    */
   if (useOpacity && !resultList.IsEmpty()) {
     // Don't clip nsDisplayOpacity items. We clip their descendants instead.
     // The clip we would set on an element with opacity would clip
     // all descendant content, but some should not be clipped.
     DisplayListClipState::AutoSaveRestore opacityClipState(aBuilder);
-    opacityClipState.Clear();
+    opacityClipState.ClearUpToASR(containerItemASR);
     resultList.AppendNewToTop(
         new (aBuilder) nsDisplayOpacity(aBuilder, this, &resultList,
-                                        containerItemScrollClip, opacityItemForEventsAndPluginsOnly));
+                                        containerItemASR,
+                                        opacityItemForEventsAndPluginsOnly));
   }
 
   /* If we're going to apply a transformation and don't have preserve-3d set, wrap
    * everything in an nsDisplayTransform. If there's nothing in the list, don't add
    * anything.
    *
    * For the preserve-3d case we want to individually wrap every child in the list with
    * a separate nsDisplayTransform instead. When the child is already an nsDisplayTransform,
@@ -2552,17 +2543,17 @@ nsIFrame::BuildDisplayListForStackingCon
     WrapSeparatorTransform(aBuilder, this, dirtyRect,
                            &nonparticipants, &participants, index++);
     resultList.AppendToTop(&participants);
   }
 
   if (isTransformed && !resultList.IsEmpty()) {
     if (clipCapturedBy == ContainerItemType::eTransform) {
       // Restore clip state now so nsDisplayTransform is clipped properly.
-      clipState.ExitStackingContextContents(&containerItemScrollClip);
+      clipState.Restore();
     }
     // Revert to the dirtyrect coming in from the parent, without our transform
     // taken into account.
     buildingDisplayList.SetDirtyRect(dirtyRectOutsideTransform);
     // Revert to the outer reference frame and offset because all display
     // items we create from now on are outside the transform.
     nsPoint toOuterReferenceFrame;
     const nsIFrame* outerReferenceFrame = this;
@@ -2579,72 +2570,86 @@ nsIFrame::BuildDisplayListForStackingCon
         new (aBuilder) nsDisplayTransform(aBuilder, this,
                                           &resultList, dirtyRect, 0,
                                           allowAsyncAnimation);
       resultList.AppendNewToTop(transformItem);
     }
 
     if (hasPerspective) {
       if (clipCapturedBy == ContainerItemType::ePerspective) {
-        clipState.ExitStackingContextContents(&containerItemScrollClip);
+        clipState.Restore();
       }
       resultList.AppendNewToTop(
         new (aBuilder) nsDisplayPerspective(
           aBuilder, this,
           GetContainingBlock()->GetContent()->GetPrimaryFrame(), &resultList));
     }
   }
 
   if (clipCapturedBy == ContainerItemType::eOwnLayerForTransformWithRoundedClip) {
-    clipState.ExitStackingContextContents(&containerItemScrollClip);
+    clipState.Restore();
     resultList.AppendNewToTop(
-      new (aBuilder) nsDisplayOwnLayer(aBuilder, this, &resultList, 0,
+      new (aBuilder) nsDisplayOwnLayer(aBuilder, this, &resultList,
+                                       aBuilder->CurrentActiveScrolledRoot(), 0,
                                        mozilla::layers::FrameMetrics::NULL_SCROLL_ID,
                                        0.0f, /* aForceActive = */ false));
   }
 
   /* If we have sticky positioning, wrap it in a sticky position item.
    */
   if (useFixedPosition) {
     if (clipCapturedBy == ContainerItemType::eFixedPosition) {
-      clipState.ExitStackingContextContents(&containerItemScrollClip);
-    }
+      clipState.Restore();
+    }
+    // The ASR for the fixed item should be the ASR of our containing block,
+    // which has been set as the builder's current ASR, unless this frame is
+    // invisible and we hadn't saved display item data for it. In that case,
+    // we need to take the containerItemASR since we might have fixed children.
+    const ActiveScrolledRoot* fixedASR =
+      ActiveScrolledRoot::PickAncestor(containerItemASR, aBuilder->CurrentActiveScrolledRoot());
     resultList.AppendNewToTop(
-        new (aBuilder) nsDisplayFixedPosition(aBuilder, this, &resultList));
+        new (aBuilder) nsDisplayFixedPosition(aBuilder, this, &resultList, fixedASR));
   } else if (useStickyPosition) {
-    if (clipCapturedBy == ContainerItemType::eStickyPosition) {
-      clipState.ExitStackingContextContents(&containerItemScrollClip);
-    }
+    // For position:sticky, the clip needs to be applied both to the sticky
+    // container item and to the contents. The container item needs the clip
+    // because a scrolled clip needs to move independently from the sticky
+    // contents, and the contents need the clip so that they have finite
+    // clipped bounds with respect to the container item's ASR. The latter is
+    // a little tricky in the case where the sticky item has both fixed and
+    // non-fixed descendants, because that means that the sticky container
+    // item's ASR is the ASR of the fixed descendant.
+    const ActiveScrolledRoot* stickyASR =
+      ActiveScrolledRoot::PickAncestor(containerItemASR, aBuilder->CurrentActiveScrolledRoot());
     resultList.AppendNewToTop(
-        new (aBuilder) nsDisplayStickyPosition(aBuilder, this, &resultList));
+        new (aBuilder) nsDisplayStickyPosition(aBuilder, this, &resultList, stickyASR));
   }
 
   /* If there's blending, wrap up the list in a blend-mode item. Note
    * that opacity can be applied before blending as the blend color is
    * not affected by foreground opacity (only background alpha).
    */
 
   if (useBlendMode && !resultList.IsEmpty()) {
-    DisplayListClipState::AutoSaveRestore mixBlendClipState(aBuilder);
-    mixBlendClipState.Clear();
+    DisplayListClipState::AutoSaveRestore blendModeClipState(aBuilder);
+    blendModeClipState.ClearUpToASR(containerItemASR);
     resultList.AppendNewToTop(
         new (aBuilder) nsDisplayBlendMode(aBuilder, this, &resultList,
                                           effects->mMixBlendMode,
-                                          containerItemScrollClip));
+                                          containerItemASR));
   }
 
   CreateOwnLayerIfNeeded(aBuilder, &resultList);
 
   aList->AppendToTop(&resultList);
 }
 
 static nsDisplayItem*
 WrapInWrapList(nsDisplayListBuilder* aBuilder,
                nsIFrame* aFrame, nsDisplayList* aList,
-               const DisplayItemScrollClip* aScrollClip)
+               const ActiveScrolledRoot* aContainerASR)
 {
   nsDisplayItem* item = aList->GetBottom();
   if (!item) {
     return nullptr;
   }
 
   // For perspective items we want to treat the 'frame' as being the transform
   // frame that created it. This stops the transform frame from wrapping another
@@ -2652,17 +2657,17 @@ WrapInWrapList(nsDisplayListBuilder* aBu
   // makes the perspective frame create one (so we have an atomic entry for z-index
   // sorting).
   nsIFrame *itemFrame = item->Frame();
   if (item->GetType() == nsDisplayItem::TYPE_PERSPECTIVE) {
     itemFrame = static_cast<nsDisplayPerspective*>(item)->TransformFrame();
   }
 
   if (item->GetAbove() || itemFrame != aFrame) {
-    return new (aBuilder) nsDisplayWrapList(aBuilder, aFrame, aList, aScrollClip);
+    return new (aBuilder) nsDisplayWrapList(aBuilder, aFrame, aList, aContainerASR);
   }
   aList->RemoveBottom();
   return item;
 }
 
 void
 nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder*   aBuilder,
                                    nsIFrame*               aChild,
@@ -2829,39 +2834,39 @@ nsIFrame::BuildDisplayListForChild(nsDis
     pseudoStackingContext = true;
   }
   NS_ASSERTION(!isStackingContext || pseudoStackingContext,
                "Stacking contexts must also be pseudo-stacking-contexts");
 
   nsDisplayListBuilder::AutoBuildingDisplayList
     buildingForChild(aBuilder, child, dirty, pseudoStackingContext);
   DisplayListClipState::AutoClipMultiple clipState(aBuilder);
+  nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter asrSetter(aBuilder);
   CheckForApzAwareEventHandlers(aBuilder, child);
 
   if (savedOutOfFlowData) {
     aBuilder->SetBuildingInvisibleItems(false);
 
-    clipState.SetClipForContainingBlockDescendants(
-      &savedOutOfFlowData->mContainingBlockClip);
-    clipState.SetScrollClipForContainingBlockDescendants(
-      savedOutOfFlowData->mContainingBlockScrollClip);
+    clipState.SetClipChainForContainingBlockDescendants(
+      savedOutOfFlowData->mContainingBlockClipChain);
+    asrSetter.SetCurrentActiveScrolledRoot(
+      savedOutOfFlowData->mContainingBlockActiveScrolledRoot);
   } else if (GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO &&
              isPlaceholder) {
     NS_ASSERTION(dirty.IsEmpty(), "should have empty dirty rect");
     // Every item we build from now until we descent into an out of flow that
     // does have saved out of flow data should be invisible. This state gets
     // restored when AutoBuildingDisplayList gets out of scope.
     aBuilder->SetBuildingInvisibleItems(true);
 
     // If we have nested out-of-flow frames and the outer one isn't visible
     // then we won't have stored clip data for it. We can just clear the clip
     // instead since we know we won't render anything, and the inner out-of-flow
     // frame will setup the correct clip for itself.
-    clipState.SetClipForContainingBlockDescendants(nullptr);
-    clipState.SetScrollClipForContainingBlockDescendants(nullptr);
+    clipState.SetClipChainForContainingBlockDescendants(nullptr);
   }
 
   // Setup clipping for the parent's overflow:-moz-hidden-unscrollable,
   // or overflow:hidden on elements that don't support scrolling (and therefore
   // don't create nsHTML/XULScrollFrame). This clipping needs to not clip
   // anything directly rendered by the parent, only the rendering of its
   // children.
   // Don't use overflowClip to restrict the dirty rect, since some of the
@@ -2869,24 +2874,27 @@ nsIFrame::BuildDisplayListForChild(nsDis
   // display items, they'll be pruned during ComputeVisibility.
   nsIFrame* parent = child->GetParent();
   const nsStyleDisplay* parentDisp =
     parent == this ? ourDisp : parent->StyleDisplay();
   ApplyOverflowClipping(aBuilder, parent, parentDisp, clipState);
 
   nsDisplayList list;
   nsDisplayList extraPositionedDescendants;
+  const ActiveScrolledRoot* wrapListASR = aBuilder->CurrentActiveScrolledRoot();
   if (isStackingContext) {
     if (effects->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
       aBuilder->SetContainsBlendMode(true);
     }
     // True stacking context.
     // For stacking contexts, BuildDisplayListForStackingContext handles
     // clipping and MarkAbsoluteFramesForDisplayList.
+    nsDisplayListBuilder::AutoContainerASRTracker contASRTracker(aBuilder);
     child->BuildDisplayListForStackingContext(aBuilder, dirty, &list);
+    wrapListASR = contASRTracker.GetContainerASR();
     aBuilder->DisplayCaret(child, dirty, &list);
   } else {
     Maybe<nsRect> clipPropClip =
       child->GetClipPropClipRect(disp, effects, child->GetSize());
     if (clipPropClip) {
       dirty.IntersectRect(dirty, *clipPropClip);
       clipState.ClipContentDescendants(
         *clipPropClip + aBuilder->ToReferenceFrame(child));
@@ -2933,54 +2941,53 @@ nsIFrame::BuildDisplayListForChild(nsDis
     }
 
     // A pseudo-stacking context (e.g., a positioned element with z-index auto).
     // We allow positioned descendants of the child to escape to our parent
     // stacking context's positioned descendant list, because they might be
     // z-index:non-auto
     nsDisplayListCollection pseudoStack;
     aBuilder->AdjustWindowDraggingRegion(child);
+    nsDisplayListBuilder::AutoContainerASRTracker contASRTracker(aBuilder);
     child->BuildDisplayList(aBuilder, dirty, pseudoStack);
     aBuilder->DisplayCaret(child, dirty, pseudoStack.Content());
+    wrapListASR = contASRTracker.GetContainerASR();
 
     list.AppendToTop(pseudoStack.BorderBackground());
     list.AppendToTop(pseudoStack.BlockBorderBackgrounds());
     list.AppendToTop(pseudoStack.Floats());
     list.AppendToTop(pseudoStack.Content());
     list.AppendToTop(pseudoStack.Outlines());
     extraPositionedDescendants.AppendToTop(pseudoStack.PositionedDescendants());
 #ifdef DEBUG
     DisplayDebugBorders(aBuilder, child, aLists);
 #endif
   }
 
   buildingForChild.RestoreBuildingInvisibleItemsValue();
  
   // Clear clip rect for the construction of the items below. Since we're
   // clipping all their contents, they themselves don't need to be clipped.
-  clipState.Clear();
-
-  const DisplayItemScrollClip* containerItemScrollClip =
-    aBuilder->ClipState().CurrentAncestorScrollClipForStackingContextContents();
+  clipState.ClearUpToASR(wrapListASR);
 
   if (isPositioned || isVisuallyAtomic ||
       (aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT)) {
     // Genuine stacking contexts, and positioned pseudo-stacking-contexts,
     // go in this level.
     if (!list.IsEmpty()) {
-      nsDisplayItem* item = WrapInWrapList(aBuilder, child, &list, containerItemScrollClip);
+      nsDisplayItem* item = WrapInWrapList(aBuilder, child, &list, wrapListASR);
       if (isSVG) {
         aLists.Content()->AppendNewToTop(item);
       } else {
         aLists.PositionedDescendants()->AppendNewToTop(item);
       }
     }
   } else if (!isSVG && disp->IsFloating(child)) {
     if (!list.IsEmpty()) {
-      aLists.Floats()->AppendNewToTop(WrapInWrapList(aBuilder, child, &list, containerItemScrollClip));
+      aLists.Floats()->AppendNewToTop(WrapInWrapList(aBuilder, child, &list, wrapListASR));
     }
   } else {
     aLists.Content()->AppendToTop(&list);
   }
   // We delay placing the positioned descendants of positioned frames to here,
   // because in the absence of z-index this is the correct order for them.
   // This doesn't affect correctness because the positioned descendants list
   // is sorted by z-order and content in BuildDisplayListForStackingContext,
@@ -10064,17 +10071,17 @@ nsIFrame::SetParent(nsContainerFrame* aP
 void
 nsIFrame::CreateOwnLayerIfNeeded(nsDisplayListBuilder* aBuilder, 
                                  nsDisplayList* aList)
 {
   if (GetContent() &&
       GetContent()->IsXULElement() &&
       GetContent()->HasAttr(kNameSpaceID_None, nsGkAtoms::layer)) {
     aList->AppendNewToTop(new (aBuilder) 
-        nsDisplayOwnLayer(aBuilder, this, aList));
+        nsDisplayOwnLayer(aBuilder, this, aList, aBuilder->CurrentActiveScrolledRoot()));
   }
 }
 
 bool
 nsIFrame::IsSelected() const
 {
   return (GetContent() && GetContent()->IsSelectionDescendant()) ?
     IsFrameSelected() : false;
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -5,17 +5,16 @@
 
 /* rendering object to wrap rendering objects that should be scrollable */
 
 #include "nsGfxScrollFrame.h"
 
 #include "ActiveLayerTracker.h"
 #include "base/compiler_specific.h"
 #include "DisplayItemClip.h"
-#include "DisplayItemScrollClip.h"
 #include "nsCOMPtr.h"
 #include "nsIContentViewer.h"
 #include "nsPresContext.h"
 #include "nsView.h"
 #include "nsIScrollable.h"
 #include "nsContainerFrame.h"
 #include "nsGkAtoms.h"
 #include "nsNameSpaceManager.h"
@@ -2980,23 +2979,24 @@ static const uint32_t APPEND_SCROLLBAR_C
 static void
 AppendToTop(nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists,
             nsDisplayList* aSource, nsIFrame* aSourceFrame, uint32_t aFlags)
 {
   if (aSource->IsEmpty())
     return;
 
   nsDisplayWrapList* newItem;
+  const ActiveScrolledRoot* asr = aBuilder->CurrentActiveScrolledRoot();
   if (aFlags & APPEND_OWN_LAYER) {
     uint32_t flags = (aFlags & APPEND_SCROLLBAR_CONTAINER)
                      ? nsDisplayOwnLayer::SCROLLBAR_CONTAINER
                      : 0;
-    newItem = new (aBuilder) nsDisplayOwnLayer(aBuilder, aSourceFrame, aSource, flags);
+    newItem = new (aBuilder) nsDisplayOwnLayer(aBuilder, aSourceFrame, aSource, asr, flags);
   } else {
-    newItem = new (aBuilder) nsDisplayWrapList(aBuilder, aSourceFrame, aSource);
+    newItem = new (aBuilder) nsDisplayWrapList(aBuilder, aSourceFrame, aSource, asr);
   }
 
   if (aFlags & APPEND_POSITIONED) {
     // We want overlay scrollbars to always be on top of the scrolled content,
     // but we don't want them to unnecessarily cover overlapping elements from
     // outside our scroll frame.
     int32_t zIndex = MaxZIndexInList(aLists.PositionedDescendants(), aBuilder);
     AppendInternalItemToTop(aLists, newItem, zIndex);
@@ -3178,69 +3178,54 @@ ShouldBeClippedByFrame(nsIFrame* aClipFr
 {
   return nsLayoutUtils::IsProperAncestorFrame(aClipFrame, aClippedFrame);
 }
 
 static void
 ClipItemsExceptCaret(nsDisplayList* aList,
                      nsDisplayListBuilder* aBuilder,
                      nsIFrame* aClipFrame,
-                     const DisplayItemClip* aNonCaretClip,
-                     const DisplayItemScrollClip* aNonCaretScrollClip)
+                     const DisplayItemClipChain* aExtraClip,
+                     nsDataHashtable<nsPtrHashKey<const DisplayItemClipChain>, const DisplayItemClipChain*>& aCache)
 {
   for (nsDisplayItem* i = aList->GetBottom(); i; i = i->GetAbove()) {
     if (!ShouldBeClippedByFrame(aClipFrame, i->Frame())) {
       continue;
     }
 
     if (i->GetType() != nsDisplayItem::TYPE_CARET) {
-      bool unused;
-      nsRect bounds = i->GetBounds(aBuilder, &unused);
-      if (aNonCaretClip && aNonCaretClip->IsRectAffectedByClip(bounds)) {
-        DisplayItemClip newClip;
-        newClip.IntersectWith(i->GetClip());
-        newClip.IntersectWith(*aNonCaretClip);
-        i->SetClip(aBuilder, newClip);
-      }
-
-      if (aNonCaretScrollClip) {
-        const DisplayItemScrollClip* currentScrollClip = i->ScrollClip();
-        MOZ_ASSERT(DisplayItemScrollClip::IsAncestor(aNonCaretScrollClip->mParent,
-                                                     currentScrollClip));
-
-        // Overwrite the existing scroll clip with aNonCaretScrollClip, unless
-        // the current scroll clip is deeper than aNonCaretScrollClip (which
-        // means that the display item is nested inside another scroll frame).
-        if (!currentScrollClip ||
-            currentScrollClip->mParent == aNonCaretScrollClip->mParent) {
-          i->SetScrollClip(aNonCaretScrollClip);
-        }
+      const DisplayItemClipChain* clip = i->GetClipChain();
+      const DisplayItemClipChain* intersection = nullptr;
+      if (aCache.Get(clip, &intersection)) {
+        i->SetClipChain(intersection);
+      } else {
+        i->IntersectClip(aBuilder, aExtraClip);
+        aCache.Put(clip, i->GetClipChain());
       }
     }
     nsDisplayList* children = i->GetSameCoordinateSystemChildren();
     if (children) {
-      ClipItemsExceptCaret(children, aBuilder, aClipFrame, aNonCaretClip,
-                           aNonCaretScrollClip);
+      ClipItemsExceptCaret(children, aBuilder, aClipFrame, aExtraClip, aCache);
     }
   }
 }
 
 static void
 ClipListsExceptCaret(nsDisplayListCollection* aLists,
                      nsDisplayListBuilder* aBuilder,
                      nsIFrame* aClipFrame,
-                     const DisplayItemClip* aNonCaretClip,
-                     const DisplayItemScrollClip* aNonCaretScrollClip)
-{
-  ClipItemsExceptCaret(aLists->BorderBackground(), aBuilder, aClipFrame, aNonCaretClip, aNonCaretScrollClip);
-  ClipItemsExceptCaret(aLists->BlockBorderBackgrounds(), aBuilder, aClipFrame, aNonCaretClip, aNonCaretScrollClip);
-  ClipItemsExceptCaret(aLists->Floats(), aBuilder, aClipFrame, aNonCaretClip, aNonCaretScrollClip);
-  ClipItemsExceptCaret(aLists->PositionedDescendants(), aBuilder, aClipFrame, aNonCaretClip, aNonCaretScrollClip);
-  ClipItemsExceptCaret(aLists->Outlines(), aBuilder, aClipFrame, aNonCaretClip, aNonCaretScrollClip);
-  ClipItemsExceptCaret(aLists->Content(), aBuilder, aClipFrame, aNonCaretClip, aNonCaretScrollClip);
+                     const DisplayItemClipChain* aExtraClip)
+{
+  nsDataHashtable<nsPtrHashKey<const DisplayItemClipChain>, const DisplayItemClipChain*> cache;
+  ClipItemsExceptCaret(aLists->BorderBackground(), aBuilder, aClipFrame, aExtraClip, cache);
+  ClipItemsExceptCaret(aLists->BlockBorderBackgrounds(), aBuilder, aClipFrame, aExtraClip, cache);
+  ClipItemsExceptCaret(aLists->Floats(), aBuilder, aClipFrame, aExtraClip, cache);
+  ClipItemsExceptCaret(aLists->PositionedDescendants(), aBuilder, aClipFrame, aExtraClip, cache);
+  ClipItemsExceptCaret(aLists->Outlines(), aBuilder, aClipFrame, aExtraClip, cache);
+  ClipItemsExceptCaret(aLists->Content(), aBuilder, aClipFrame, aExtraClip, cache);
 }
 
 void
 ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                     const nsRect&           aDirtyRect,
                                     const nsDisplayListSet& aLists)
 {
   if (aBuilder->IsForFrameVisibility()) {
@@ -3375,37 +3360,44 @@ ScrollFrameHelper::BuildDisplayList(nsDi
 
   const nsStyleDisplay* disp = mOuter->StyleDisplay();
   if (disp && (disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_SCROLL)) {
     aBuilder->AddToWillChangeBudget(mOuter, GetScrollPositionClampingScrollPortSize());
   }
 
   mScrollParentID = aBuilder->GetCurrentScrollParentId();
 
-  Maybe<nsRect> contentBoxClipForCaret;
-  Maybe<nsRect> contentBoxClipForNonCaretContent;
+  Maybe<nsRect> contentBoxClip;
+  Maybe<DisplayItemClipChain> extraContentBoxClipForNonCaretContent;
   if (MOZ_UNLIKELY(mOuter->StyleDisplay()->mOverflowClipBox ==
                      NS_STYLE_OVERFLOW_CLIP_BOX_CONTENT_BOX)) {
     // We only clip if there is *scrollable* overflow, to avoid clipping
     // *visual* overflow unnecessarily.
     nsRect clipRect = mScrollPort + aBuilder->ToReferenceFrame(mOuter);
     nsRect so = mScrolledFrame->GetScrollableOverflowRect();
     if (clipRect.width != so.width || clipRect.height != so.height ||
         so.x < 0 || so.y < 0) {
       clipRect.Deflate(mOuter->GetUsedPadding());
-      contentBoxClipForNonCaretContent = Some(clipRect);
-      if (nsIFrame* caretFrame = aBuilder->GetCaretFrame()) {
-        // Avoid clipping it in a zero-height line box (heuristic only).
-        if (caretFrame->GetRect().height != 0) {
-          nsRect caretRect = aBuilder->GetCaretRect();
-          // Allow the caret to stick out of the content box clip by half the
-          // caret height on the top, and its full width on the right.
-          clipRect.Inflate(nsMargin(caretRect.height / 2, caretRect.width, 0, 0));
-          contentBoxClipForCaret = Some(clipRect);
-        }
+
+      // The non-inflated clip needs to be set on all non-caret items.
+      // We prepare an extra DisplayItemClipChain here that will be intersected
+      // with those items after they've been created.
+      const ActiveScrolledRoot* asr = aBuilder->CurrentActiveScrolledRoot();
+      extraContentBoxClipForNonCaretContent = Some(DisplayItemClipChain{ DisplayItemClip(), asr, nullptr });
+      extraContentBoxClipForNonCaretContent->mClip.SetTo(clipRect);
+
+      nsIFrame* caretFrame = aBuilder->GetCaretFrame();
+      // Avoid clipping it in a zero-height line box (heuristic only).
+      if (caretFrame && caretFrame->GetRect().height != 0) {
+        nsRect caretRect = aBuilder->GetCaretRect();
+        // Allow the caret to stick out of the content box clip by half the
+        // caret height on the top, and its full width on the right.
+        nsRect inflatedClip = clipRect;
+        inflatedClip.Inflate(nsMargin(caretRect.height / 2, caretRect.width, 0, 0));
+        contentBoxClip = Some(inflatedClip);
       }
     }
   }
 
   nsIScrollableFrame* sf = do_QueryFrame(mOuter);
   MOZ_ASSERT(sf);
 
   nsDisplayListCollection scrolledContent;
@@ -3437,45 +3429,32 @@ ScrollFrameHelper::BuildDisplayList(nsDi
 
     DisplayListClipState::AutoSaveRestore clipState(aBuilder);
     if (mClipAllDescendants) {
       clipState.ClipContentDescendants(clipRect, haveRadii ? radii : nullptr);
     } else {
       clipState.ClipContainingBlockDescendants(clipRect, haveRadii ? radii : nullptr);
     }
 
-    DisplayItemScrollClip* inactiveScrollClip = nullptr;
+    Maybe<DisplayListClipState::AutoSaveRestore> contentBoxClipState;;
+    if (contentBoxClip) {
+      contentBoxClipState.emplace(aBuilder);
+      if (mClipAllDescendants) {
+        contentBoxClipState->ClipContentDescendants(*contentBoxClip);
+      } else {
+        contentBoxClipState->ClipContainingBlockDescendants(*contentBoxClip);
+      }
+    }
+
+    nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter asrSetter(aBuilder);
+    if (mWillBuildScrollableLayer) {
+      asrSetter.EnterScrollFrame(sf);
+    }
 
     {
-      DisplayListClipState::AutoSaveRestore contentBoxClipState(aBuilder);
-      if (contentBoxClipForCaret) {
-        if (mClipAllDescendants) {
-          contentBoxClipState.ClipContentDescendants(*contentBoxClipForCaret);
-        } else {
-          contentBoxClipState.ClipContainingBlockDescendants(*contentBoxClipForCaret);
-        }
-      }
-
-      DisplayListClipState::AutoSaveRestore clipStateForScrollClip(aBuilder);
-      if (mWillBuildScrollableLayer) {
-        if (mClipAllDescendants) {
-          clipStateForScrollClip.TurnClipIntoScrollClipForContentDescendants(aBuilder, sf);
-        } else {
-          clipStateForScrollClip.TurnClipIntoScrollClipForContainingBlockDescendants(aBuilder, sf);
-        }
-      } else {
-        // Create a scroll clip anyway because we might need to activate for scroll handoff.
-        if (mClipAllDescendants) {
-          inactiveScrollClip = clipStateForScrollClip.InsertInactiveScrollClipForContentDescendants(aBuilder, sf);
-        } else {
-          inactiveScrollClip = clipStateForScrollClip.InsertInactiveScrollClipForContainingBlockDescendants(aBuilder, sf);
-        }
-        MOZ_ASSERT(!inactiveScrollClip->mIsAsyncScrollable);
-      }
-
       // Clip our contents to the unsnapped scrolled rect. This makes sure that
       // we don't have display items over the subpixel seam at the edge of the
       // scrolled area.
       DisplayListClipState::AutoSaveRestore scrolledRectClipState(aBuilder);
       nsRect scrolledRectClip =
         GetUnsnappedScrolledRectInternal(mScrolledFrame->GetScrollableOverflowRect(),
                                          mScrollPort.Size()) + mScrolledFrame->GetPosition();
       if (usingDisplayPort) {
@@ -3498,67 +3477,54 @@ ScrollFrameHelper::BuildDisplayList(nsDi
         scrolledRectClip = scrolledRectClip.Intersect(dirtyRect);
       }
       scrolledRectClipState.ClipContainingBlockDescendants(
         scrolledRectClip + aBuilder->ToReferenceFrame(mOuter));
 
       mOuter->BuildDisplayListForChild(aBuilder, mScrolledFrame, dirtyRect, scrolledContent);
     }
 
-    if (contentBoxClipForNonCaretContent) {
-      DisplayListClipState::AutoSaveRestore contentBoxClipState(aBuilder);
-      if (mClipAllDescendants) {
-        contentBoxClipState.ClipContentDescendants(*contentBoxClipForNonCaretContent);
-      } else {
-        contentBoxClipState.ClipContainingBlockDescendants(*contentBoxClipForNonCaretContent);
-      }
-
-      DisplayListClipState::AutoSaveRestore clipStateForScrollClip(aBuilder);
-      if (mWillBuildScrollableLayer) {
-        if (mClipAllDescendants) {
-          clipStateForScrollClip.TurnClipIntoScrollClipForContentDescendants(aBuilder, sf);
-        } else {
-          clipStateForScrollClip.TurnClipIntoScrollClipForContainingBlockDescendants(aBuilder, sf);
-        }
-      }
-      const DisplayItemClip* clipNonCaret = aBuilder->ClipState().GetCurrentCombinedClip(aBuilder);
-      const DisplayItemScrollClip* scrollClipNonCaret = aBuilder->ClipState().GetCurrentInnermostScrollClip();
+    if (extraContentBoxClipForNonCaretContent) {
+      // The items were built while the inflated content box clip was in
+      // effect, so that the caret wasn't clipped unnecessarily. We apply
+      // the non-inflated clip to the non-caret items now, by intersecting
+      // it with their existing clip.
       ClipListsExceptCaret(&scrolledContent, aBuilder, mScrolledFrame,
-                           clipNonCaret, scrollClipNonCaret);
+                           extraContentBoxClipForNonCaretContent.ptr());
     }
 
     if (aBuilder->IsPaintingToWindow()) {
       mIsScrollParent = idSetter.ShouldForceLayerForScrollParent();
     }
     if (idSetter.ShouldForceLayerForScrollParent() &&
         !gfxPrefs::LayoutUseContainersForRootFrames())
     {
       // Note that forcing layerization of scroll parents follows the scroll
       // handoff chain which is subject to the out-of-flow-frames caveat noted
       // above (where the idSetter variable is created).
       //
       // This is not compatible when using containes for root scrollframes.
       MOZ_ASSERT(couldBuildLayer && mScrolledFrame->GetContent());
-      if (inactiveScrollClip) {
-        inactiveScrollClip->mIsAsyncScrollable = true;
-      }
       if (!mWillBuildScrollableLayer) {
         // Set a displayport so next paint we don't have to force layerization
         // after the fact.
         nsLayoutUtils::SetDisplayPortMargins(mOuter->GetContent(),
                                              mOuter->PresContext()->PresShell(),
                                              ScreenMargin(),
                                              0,
                                              nsLayoutUtils::RepaintMode::DoNotRepaint);
         // Call DecideScrollableLayer to recompute mWillBuildScrollableLayer and
         // recompute the current animated geometry root if needed.
         // It's too late to change the dirty rect so pass a copy.
         nsRect copyOfDirtyRect = dirtyRect;
         Unused << DecideScrollableLayer(aBuilder, &copyOfDirtyRect,
                     /* aAllowCreateDisplayPort = */ false);
+        if (mWillBuildScrollableLayer) {
+          asrSetter.InsertScrollFrame(sf);
+        }
       }
     }
   }
 
   if (mWillBuildScrollableLayer) {
     aBuilder->ForceLayerForScrollParent();
   }
 
--- a/layout/generic/nsPluginFrame.cpp
+++ b/layout/generic/nsPluginFrame.cpp
@@ -49,17 +49,16 @@
 #include "ImageLayers.h"
 #include "nsPluginInstanceOwner.h"
 
 #ifdef XP_WIN
 #include "gfxWindowsNativeDrawing.h"
 #include "gfxWindowsSurface.h"
 #endif
 
-#include "DisplayItemScrollClip.h"
 #include "Layers.h"
 #include "ReadbackLayer.h"
 #include "ImageContainer.h"
 
 // accessibility support
 #ifdef ACCESSIBILITY
 #include "nsAccessibilityService.h"
 #endif
@@ -1005,20 +1004,18 @@ nsDisplayPlugin::Paint(nsDisplayListBuil
   f->PaintPlugin(aBuilder, *aCtx, mVisibleRect, GetBounds(aBuilder, &snap));
 }
 
 static nsRect
 GetClippedBoundsIncludingAllScrollClips(nsDisplayItem* aItem,
                                         nsDisplayListBuilder* aBuilder)
 {
   nsRect r = aItem->GetClippedBounds(aBuilder);
-  for (auto* sc = aItem->ScrollClip(); sc; sc = sc->mParent) {
-    if (sc->mClip) {
-      r = sc->mClip->ApplyNonRoundedIntersection(r);
-    }
+  for (auto* sc = aItem->GetClipChain(); sc; sc = sc->mParent) {
+    r = sc->mClip.ApplyNonRoundedIntersection(r);
   }
   return r;
 }
 
 bool
 nsDisplayPlugin::ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                    nsRegion* aVisibleRegion)
 {
--- a/layout/generic/nsSubDocumentFrame.cpp
+++ b/layout/generic/nsSubDocumentFrame.cpp
@@ -316,17 +316,17 @@ WrapBackgroundColorInOwnLayer(nsDisplayL
                               nsDisplayList* aList)
 {
   nsDisplayList tempItems;
   nsDisplayItem* item;
   while ((item = aList->RemoveBottom()) != nullptr) {
     if (item->GetType() == nsDisplayItem::TYPE_BACKGROUND_COLOR) {
       nsDisplayList tmpList;
       tmpList.AppendToTop(item);
-      item = new (aBuilder) nsDisplayOwnLayer(aBuilder, aFrame, &tmpList);
+      item = new (aBuilder) nsDisplayOwnLayer(aBuilder, aFrame, &tmpList, aBuilder->CurrentActiveScrolledRoot());
     }
     tempItems.AppendToTop(item);
   }
   aList->AppendToTop(&tempItems);
 }
 
 void
 nsSubDocumentFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
@@ -451,17 +451,17 @@ nsSubDocumentFrame::BuildDisplayList(nsD
 
   {
     DisplayListClipState::AutoSaveRestore nestedClipState(aBuilder);
     if (needsOwnLayer) {
       // Clear current clip. There's no point in propagating it down, since
       // the layer we will construct will be clipped by the current clip.
       // In fact for nsDisplayZoom propagating it down would be incorrect since
       // nsDisplayZoom changes the meaning of appunits.
-      nestedClipState.EnterStackingContextContents(true);
+      nestedClipState.Clear();
     }
 
     if (subdocRootFrame) {
       nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame();
       nsDisplayListBuilder::AutoCurrentScrollParentIdSetter idSetter(
           aBuilder,
           ignoreViewportScrolling && rootScrollFrame && rootScrollFrame->GetContent()
               ? nsLayoutUtils::FindOrCreateIDFor(rootScrollFrame->GetContent())
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -4821,23 +4821,21 @@ public:
       return false;
     }
 
     return true;
   }
 
   void ApplyOpacity(nsDisplayListBuilder* aBuilder,
                     float aOpacity,
-                    const DisplayItemClip* aClip) override
+                    const DisplayItemClipChain* aClip) override
   {
     NS_ASSERTION(CanApplyOpacity(), "ApplyOpacity should be allowed");
     mOpacity = aOpacity;
-    if (aClip) {
-      IntersectClip(aBuilder, *aClip);
-    }
+    IntersectClip(aBuilder, aClip);
   }
 
   void WriteDebugInfo(std::stringstream& aStream) override
   {
 #ifdef DEBUG
     aStream << " (\"";
 
     nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
@@ -4855,19 +4853,17 @@ public:
   void GetMergedFrames(nsTArray<nsIFrame*>* aFrames) override
   {
     aFrames->AppendElements(mMergedFrames);
   }
 
   bool TryMerge(nsDisplayItem* aItem) override {
     if (aItem->GetType() != TYPE_TEXT)
       return false;
-    if (aItem->GetClip() != GetClip())
-      return false;
-    if (aItem->ScrollClip() != ScrollClip())
+    if (aItem->GetClipChain() != GetClipChain())
       return false;
 
     nsDisplayText* other = static_cast<nsDisplayText*>(aItem);
     if (!mFont || !other->mFont || mFont != other->mFont) {
       return false;
     }
     if (mOpacity != other->mOpacity) {
       return false;
--- a/layout/painting/DisplayListClipState.cpp
+++ b/layout/painting/DisplayListClipState.cpp
@@ -1,204 +1,171 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "DisplayListClipState.h"
 
-#include "DisplayItemScrollClip.h"
 #include "nsDisplayList.h"
 
 namespace mozilla {
 
-const DisplayItemClip*
-DisplayListClipState::GetCurrentCombinedClip(nsDisplayListBuilder* aBuilder)
+void
+DisplayListClipState::ClearUpToASR(const ActiveScrolledRoot* aASR)
 {
-  if (mCurrentCombinedClip) {
-    return mCurrentCombinedClip;
+  while (mClipChainContentDescendants &&
+         ActiveScrolledRoot::IsAncestor(aASR, mClipChainContentDescendants->mASR)) {
+    mClipChainContentDescendants = mClipChainContentDescendants->mParent;
+  }
+  while (mClipChainContainingBlockDescendants &&
+         ActiveScrolledRoot::IsAncestor(aASR, mClipChainContainingBlockDescendants->mASR)) {
+    mClipChainContainingBlockDescendants = mClipChainContainingBlockDescendants->mParent;
   }
-  if (!mClipContentDescendants && !mClipContainingBlockDescendants) {
+  InvalidateCurrentCombinedClipChain(aASR);
+}
+
+const DisplayItemClipChain*
+DisplayListClipState::GetCurrentCombinedClipChain(nsDisplayListBuilder* aBuilder)
+{
+  if (mCurrentCombinedClipChainIsValid) {
+    return mCurrentCombinedClipChain;
+  }
+  if (!mClipChainContentDescendants && !mClipChainContainingBlockDescendants) {
+    mCurrentCombinedClipChain = nullptr;
+    mCurrentCombinedClipChainIsValid = true;
     return nullptr;
   }
-  if (mClipContentDescendants) {
-    if (mClipContainingBlockDescendants) {
-      DisplayItemClip intersection = *mClipContentDescendants;
-      intersection.IntersectWith(*mClipContainingBlockDescendants);
-      mCurrentCombinedClip = aBuilder->AllocateDisplayItemClip(intersection);
-    } else {
-      mCurrentCombinedClip =
-        aBuilder->AllocateDisplayItemClip(*mClipContentDescendants);
+
+  mCurrentCombinedClipChain =
+    aBuilder->CreateClipChainIntersection(mCurrentCombinedClipChain,
+                                             mClipChainContentDescendants,
+                                             mClipChainContainingBlockDescendants);
+  mCurrentCombinedClipChainIsValid = true;
+  return mCurrentCombinedClipChain;
+}
+
+static void
+ApplyClip(nsDisplayListBuilder* aBuilder,
+          const DisplayItemClipChain*& aClipToModify,
+          const ActiveScrolledRoot* aASR,
+          DisplayItemClipChain& aClipChainOnStack)
+{
+  aClipChainOnStack.mASR = aASR;
+  if (aClipToModify && aClipToModify->mASR == aASR) {
+    // Intersect with aClipToModify and replace the clip chain item.
+    aClipChainOnStack.mClip.IntersectWith(aClipToModify->mClip);
+    aClipChainOnStack.mParent = aClipToModify->mParent;
+    aClipToModify = &aClipChainOnStack;
+  } else if (!aClipToModify ||
+             ActiveScrolledRoot::IsAncestor(aClipToModify->mASR, aASR)) {
+    // Add a new clip chain item at the bottom.
+    aClipChainOnStack.mParent = aClipToModify;
+    aClipToModify = &aClipChainOnStack;
+  } else {
+    // We need to insert / intersect a DisplayItemClipChain in the middle of the
+    // aClipToModify chain. This is a very rare case.
+    // Find the common ancestor and have the builder create the DisplayItemClipChain
+    // intersection. This will create new DisplayItemClipChain objects for all
+    // descendants of ancestorSC and we will not hold on to a pointer to
+    // aClipChainOnStack.
+    const DisplayItemClipChain* ancestorSC = aClipToModify;
+    while (ancestorSC && ActiveScrolledRoot::IsAncestor(aASR, ancestorSC->mASR)) {
+      ancestorSC = ancestorSC->mParent;
     }
-  } else {
-    mCurrentCombinedClip =
-      aBuilder->AllocateDisplayItemClip(*mClipContainingBlockDescendants);
+    aClipChainOnStack.mParent = nullptr;
+    aClipToModify =
+      aBuilder->CreateClipChainIntersection(ancestorSC, aClipToModify, &aClipChainOnStack);
   }
-  return mCurrentCombinedClip;
 }
 
 void
-DisplayListClipState::ClipContainingBlockDescendants(const nsRect& aRect,
+DisplayListClipState::ClipContainingBlockDescendants(nsDisplayListBuilder* aBuilder,
+                                                     const nsRect& aRect,
                                                      const nscoord* aRadii,
-                                                     DisplayItemClip& aClipOnStack)
+                                                     DisplayItemClipChain& aClipChainOnStack)
 {
   if (aRadii) {
-    aClipOnStack.SetTo(aRect, aRadii);
+    aClipChainOnStack.mClip.SetTo(aRect, aRadii);
   } else {
-    aClipOnStack.SetTo(aRect);
+    aClipChainOnStack.mClip.SetTo(aRect);
   }
-  if (mClipContainingBlockDescendants) {
-    aClipOnStack.IntersectWith(*mClipContainingBlockDescendants);
-  }
-  mClipContainingBlockDescendants = &aClipOnStack;
-  mCurrentCombinedClip = nullptr;
+  const ActiveScrolledRoot* asr = aBuilder->CurrentActiveScrolledRoot();
+  ApplyClip(aBuilder, mClipChainContainingBlockDescendants, asr, aClipChainOnStack);
+  InvalidateCurrentCombinedClipChain(asr);
 }
 
 void
-DisplayListClipState::ClipContentDescendants(const nsRect& aRect,
+DisplayListClipState::ClipContentDescendants(nsDisplayListBuilder* aBuilder,
+                                             const nsRect& aRect,
                                              const nscoord* aRadii,
-                                             DisplayItemClip& aClipOnStack)
+                                             DisplayItemClipChain& aClipChainOnStack)
 {
   if (aRadii) {
-    aClipOnStack.SetTo(aRect, aRadii);
+    aClipChainOnStack.mClip.SetTo(aRect, aRadii);
   } else {
-    aClipOnStack.SetTo(aRect);
+    aClipChainOnStack.mClip.SetTo(aRect);
   }
-  if (mClipContentDescendants) {
-    aClipOnStack.IntersectWith(*mClipContentDescendants);
-  }
-  mClipContentDescendants = &aClipOnStack;
-  mCurrentCombinedClip = nullptr;
+  const ActiveScrolledRoot* asr = aBuilder->CurrentActiveScrolledRoot();
+  ApplyClip(aBuilder, mClipChainContentDescendants, asr, aClipChainOnStack);
+  InvalidateCurrentCombinedClipChain(asr);
 }
 
 void
-DisplayListClipState::ClipContentDescendants(const nsRect& aRect,
+DisplayListClipState::ClipContentDescendants(nsDisplayListBuilder* aBuilder,
+                                             const nsRect& aRect,
                                              const nsRect& aRoundedRect,
                                              const nscoord* aRadii,
-                                             DisplayItemClip& aClipOnStack)
+                                             DisplayItemClipChain& aClipChainOnStack)
 {
   if (aRadii) {
-    aClipOnStack.SetTo(aRect, aRoundedRect, aRadii);
+    aClipChainOnStack.mClip.SetTo(aRect, aRoundedRect, aRadii);
   } else {
     nsRect intersect = aRect.Intersect(aRoundedRect);
-    aClipOnStack.SetTo(intersect);
+    aClipChainOnStack.mClip.SetTo(intersect);
   }
-  if (mClipContentDescendants) {
-    aClipOnStack.IntersectWith(*mClipContentDescendants);
+  const ActiveScrolledRoot* asr = aBuilder->CurrentActiveScrolledRoot();
+  ApplyClip(aBuilder, mClipChainContentDescendants, asr, aClipChainOnStack);
+  InvalidateCurrentCombinedClipChain(asr);
+}
+
+
+void
+DisplayListClipState::InvalidateCurrentCombinedClipChain(const ActiveScrolledRoot* aInvalidateUpTo)
+{
+  mCurrentCombinedClipChainIsValid = false;
+  while (mCurrentCombinedClipChain &&
+         ActiveScrolledRoot::IsAncestor(aInvalidateUpTo, mCurrentCombinedClipChain->mASR)) {
+    mCurrentCombinedClipChain = mCurrentCombinedClipChain->mParent;
   }
-  mClipContentDescendants = &aClipOnStack;
-  mCurrentCombinedClip = nullptr;
 }
 
 void
 DisplayListClipState::ClipContainingBlockDescendantsToContentBox(nsDisplayListBuilder* aBuilder,
                                                                  nsIFrame* aFrame,
-                                                                 DisplayItemClip& aClipOnStack,
+                                                                 DisplayItemClipChain& aClipChainOnStack,
                                                                  uint32_t aFlags)
 {
   nscoord radii[8];
   bool hasBorderRadius = aFrame->GetContentBoxBorderRadii(radii);
   if (!hasBorderRadius && (aFlags & ASSUME_DRAWING_RESTRICTED_TO_CONTENT_RECT)) {
     return;
   }
 
   nsRect clipRect = aFrame->GetContentRectRelativeToSelf() +
     aBuilder->ToReferenceFrame(aFrame);
   // If we have a border-radius, we have to clip our content to that
   // radius.
-  ClipContainingBlockDescendants(clipRect, hasBorderRadius ? radii : nullptr,
-                                 aClipOnStack);
-}
-
-const DisplayItemScrollClip*
-DisplayListClipState::GetCurrentInnermostScrollClip()
-{
-  return DisplayItemScrollClip::PickDescendant(
-    mScrollClipContentDescendants, mScrollClipContainingBlockDescendants);
-}
-
-void
-DisplayListClipState::TurnClipIntoScrollClipForContentDescendants(
-    nsDisplayListBuilder* aBuilder, nsIScrollableFrame* aScrollableFrame)
-{
-  const DisplayItemScrollClip* parent = GetCurrentInnermostScrollClip();
-  mScrollClipContentDescendants =
-    aBuilder->AllocateDisplayItemScrollClip(parent,
-                                      aScrollableFrame,
-                                      GetCurrentCombinedClip(aBuilder), true);
-  Clear();
-}
-
-void
-DisplayListClipState::TurnClipIntoScrollClipForContainingBlockDescendants(
-    nsDisplayListBuilder* aBuilder, nsIScrollableFrame* aScrollableFrame)
-{
-  const DisplayItemScrollClip* parent = GetCurrentInnermostScrollClip();
-  mScrollClipContainingBlockDescendants =
-    aBuilder->AllocateDisplayItemScrollClip(parent,
-                                      aScrollableFrame,
-                                      GetCurrentCombinedClip(aBuilder), true);
-  Clear();
-}
-
-const DisplayItemClip*
-WithoutRoundedCorners(nsDisplayListBuilder* aBuilder, const DisplayItemClip* aClip)
-{
-  if (!aClip) {
-    return nullptr;
-  }
-  DisplayItemClip rectClip(*aClip);
-  rectClip.RemoveRoundedCorners();
-  return aBuilder->AllocateDisplayItemClip(rectClip);
-}
-
-DisplayItemScrollClip*
-DisplayListClipState::CreateInactiveScrollClip(
-    nsDisplayListBuilder* aBuilder, nsIScrollableFrame* aScrollableFrame)
-{
-  // We ignore the rounded corners on the current clip because we don't want
-  // them to be double-applied (as scroll clip and as regular clip).
-  // Double-applying rectangle clips doesn't make a visual difference so it's
-  // fine.
-  const DisplayItemClip* rectClip =
-    WithoutRoundedCorners(aBuilder, GetCurrentCombinedClip(aBuilder));
-
-  const DisplayItemScrollClip* parent = GetCurrentInnermostScrollClip();
-  DisplayItemScrollClip* scrollClip =
-    aBuilder->AllocateDisplayItemScrollClip(parent,
-                                            aScrollableFrame,
-                                            rectClip, false);
-  return scrollClip;
-}
-
-DisplayItemScrollClip*
-DisplayListClipState::InsertInactiveScrollClipForContentDescendants(
-    nsDisplayListBuilder* aBuilder, nsIScrollableFrame* aScrollableFrame)
-{
-  DisplayItemScrollClip* scrollClip =
-    CreateInactiveScrollClip(aBuilder, aScrollableFrame);
-  mScrollClipContentDescendants = scrollClip;
-  return scrollClip;
-}
-
-DisplayItemScrollClip*
-DisplayListClipState::InsertInactiveScrollClipForContainingBlockDescendants(
-    nsDisplayListBuilder* aBuilder, nsIScrollableFrame* aScrollableFrame)
-{
-  DisplayItemScrollClip* scrollClip =
-    CreateInactiveScrollClip(aBuilder, aScrollableFrame);
-  mScrollClipContainingBlockDescendants = scrollClip;
-  return scrollClip;
+  ClipContainingBlockDescendants(aBuilder, clipRect, hasBorderRadius ? radii : nullptr,
+                                 aClipChainOnStack);
 }
 
 DisplayListClipState::AutoSaveRestore::AutoSaveRestore(nsDisplayListBuilder* aBuilder)
-  : mState(aBuilder->ClipState())
+  : mBuilder(aBuilder)
+  , mState(aBuilder->ClipState())
   , mSavedState(aBuilder->ClipState())
 #ifdef DEBUG
   , mClipUsed(false)
   , mRestored(false)
 #endif
-  , mClearedForStackingContextContents(false)
-{
-  mState.mStackingContextAncestorSC = mState.GetCurrentInnermostScrollClip();
-}
+{}
 
 } // namespace mozilla
--- a/layout/painting/DisplayListClipState.h
+++ b/layout/painting/DisplayListClipState.h
@@ -3,218 +3,160 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef DISPLAYLISTCLIPSTATE_H_
 #define DISPLAYLISTCLIPSTATE_H_
 
 #include "DisplayItemClip.h"
 #include "DisplayItemClipChain.h"
-#include "DisplayItemScrollClip.h"
 
 #include "mozilla/DebugOnly.h"
 
 class nsIFrame;
 class nsIScrollableFrame;
 class nsDisplayListBuilder;
 
 namespace mozilla {
 
 /**
  * All clip coordinates are in appunits relative to the reference frame
  * for the display item we're building.
  */
 class DisplayListClipState {
 public:
   DisplayListClipState()
-    : mClipContentDescendants(nullptr)
-    , mClipContainingBlockDescendants(nullptr)
-    , mCurrentCombinedClip(nullptr)
-    , mScrollClipContentDescendants(nullptr)
-    , mScrollClipContainingBlockDescendants(nullptr)
-    , mStackingContextAncestorSC(nullptr)
+    : mClipChainContentDescendants(nullptr)
+    , mClipChainContainingBlockDescendants(nullptr)
+    , mCurrentCombinedClipChain(nullptr)
+    , mCurrentCombinedClipChainIsValid(false)
   {}
 
   /**
-   * Returns intersection of mClipContainingBlockDescendants and
-   * mClipContentDescendants, allocated on aBuilder's arena.
+   * Returns intersection of mClipChainContainingBlockDescendants and
+   * mClipChainContentDescendants, allocated on aBuilder's arena.
    */
-  const DisplayItemClip* GetCurrentCombinedClip(nsDisplayListBuilder* aBuilder);
+  const DisplayItemClipChain* GetCurrentCombinedClipChain(nsDisplayListBuilder* aBuilder);
 
-  const DisplayItemClip* GetClipForContainingBlockDescendants() const
+  const DisplayItemClipChain* GetClipChainForContainingBlockDescendants() const
   {
-    return mClipContainingBlockDescendants;
+    return mClipChainContainingBlockDescendants;
   }
-  const DisplayItemClip* GetClipForContentDescendants() const
+  const DisplayItemClipChain* GetClipChainForContentDescendants() const
   {
-    return mClipContentDescendants;
+    return mClipChainContentDescendants;
   }
 
-  const DisplayItemScrollClip* GetCurrentInnermostScrollClip();
-
-  const DisplayItemScrollClip* CurrentAncestorScrollClipForStackingContextContents()
+  const ActiveScrolledRoot* GetContentClipASR() const
   {
-    return mStackingContextAncestorSC;
+    return mClipChainContentDescendants ? mClipChainContentDescendants->mASR : nullptr;
   }
 
   class AutoSaveRestore;
   friend class AutoSaveRestore;
 
   class AutoClipContainingBlockDescendantsToContentBox;
   friend class AutoClipContainingBlockDescendantsToContentBox;
 
   class AutoClipMultiple;
   friend class AutoClipMultiple;
 
   enum {
     ASSUME_DRAWING_RESTRICTED_TO_CONTENT_RECT = 0x01
   };
 
 private:
-  void SetClipForContainingBlockDescendants(const DisplayItemClip* aClip)
-  {
-    mClipContainingBlockDescendants = aClip;
-    mCurrentCombinedClip = nullptr;
-  }
-
-  void SetScrollClipForContainingBlockDescendants(const DisplayItemScrollClip* aScrollClip)
-  {
-    mScrollClipContainingBlockDescendants = aScrollClip;
-    mStackingContextAncestorSC = DisplayItemScrollClip::PickAncestor(mStackingContextAncestorSC, aScrollClip);
-  }
 
   void Clear()
   {
-    mClipContentDescendants = nullptr;
-    mClipContainingBlockDescendants = nullptr;
-    mCurrentCombinedClip = nullptr;
-    // We do not clear scroll clips.
-  }
-
-  void EnterStackingContextContents(bool aClear)
-  {
-    if (aClear) {
-      mClipContentDescendants = nullptr;
-      mClipContainingBlockDescendants = nullptr;
-      mCurrentCombinedClip = nullptr;
-      mScrollClipContentDescendants = nullptr;
-      mScrollClipContainingBlockDescendants = nullptr;
-      mStackingContextAncestorSC = nullptr;
-    } else {
-      mStackingContextAncestorSC = GetCurrentInnermostScrollClip();
-    }
+    mClipChainContentDescendants = nullptr;
+    mClipChainContainingBlockDescendants = nullptr;
+    mCurrentCombinedClipChain = nullptr;
+    mCurrentCombinedClipChainIsValid = false;
   }
 
-  /**
-   * Clear the current clip, and instead add it as a scroll clip to the current
-   * scroll clip chain.
-   */
-  void TurnClipIntoScrollClipForContentDescendants(nsDisplayListBuilder* aBuilder,
-                                                   nsIScrollableFrame* aScrollableFrame);
-  void TurnClipIntoScrollClipForContainingBlockDescendants(nsDisplayListBuilder* aBuilder,
-                                                           nsIScrollableFrame* aScrollableFrame);
+  void ClearUpToASR(const ActiveScrolledRoot* aASR);
 
-  /**
-   * Insert a scroll clip without clearing the current clip.
-   * The returned DisplayItemScrollClip will have mIsAsyncScrollable == false,
-   * and it can be activated once the scroll frame knows that it needs to be
-   * async scrollable.
-   */
-  DisplayItemScrollClip* InsertInactiveScrollClipForContentDescendants(nsDisplayListBuilder* aBuilder,
-                                                                       nsIScrollableFrame* aScrollableFrame);
-  DisplayItemScrollClip* InsertInactiveScrollClipForContainingBlockDescendants(nsDisplayListBuilder* aBuilder,
-                                                                               nsIScrollableFrame* aScrollableFrame);
-
-  DisplayItemScrollClip* CreateInactiveScrollClip(nsDisplayListBuilder* aBuilder,
-                                                  nsIScrollableFrame* aScrollableFrame);
+  void SetClipChainForContainingBlockDescendants(const DisplayItemClipChain* aClipChain)
+  {
+    mClipChainContainingBlockDescendants = aClipChain;
+    InvalidateCurrentCombinedClipChain(aClipChain ? aClipChain->mASR : nullptr);
+  }
 
   /**
    * Intersects the given clip rect (with optional aRadii) with the current
    * mClipContainingBlockDescendants and sets mClipContainingBlockDescendants to
    * the result, stored in aClipOnStack.
    */
-  void ClipContainingBlockDescendants(const nsRect& aRect,
+  void ClipContainingBlockDescendants(nsDisplayListBuilder* aBuilder,
+                                      const nsRect& aRect,
                                       const nscoord* aRadii,
-                                      DisplayItemClip& aClipOnStack);
+                                      DisplayItemClipChain& aClipChainOnStack);
 
-  void ClipContentDescendants(const nsRect& aRect,
+  void ClipContentDescendants(nsDisplayListBuilder* aBuilder,
+                              const nsRect& aRect,
                               const nscoord* aRadii,
-                              DisplayItemClip& aClipOnStack);
-  void ClipContentDescendants(const nsRect& aRect,
+                              DisplayItemClipChain& aClipChainOnStack);
+  void ClipContentDescendants(nsDisplayListBuilder* aBuilder,
+                              const nsRect& aRect,
                               const nsRect& aRoundedRect,
                               const nscoord* aRadii,
-                              DisplayItemClip& aClipOnStack);
+                              DisplayItemClipChain& aClipChainOnStack);
+
+  void InvalidateCurrentCombinedClipChain(const ActiveScrolledRoot* aInvalidateUpTo);
 
   /**
    * Clips containing-block descendants to the frame's content-box,
    * taking border-radius into account.
    * If aFlags contains ASSUME_DRAWING_RESTRICTED_TO_CONTENT_RECT then
    * we assume display items will not draw outside the content rect, so
    * clipping is only required if there is a border-radius. This is an
    * optimization to reduce the amount of clipping required.
    */
   void ClipContainingBlockDescendantsToContentBox(nsDisplayListBuilder* aBuilder,
                                                   nsIFrame* aFrame,
-                                                  DisplayItemClip& aClipOnStack,
+                                                  DisplayItemClipChain& aClipChainOnStack,
                                                   uint32_t aFlags);
 
   /**
    * All content descendants (i.e. following placeholder frames to their
-   * out-of-flows if necessary) should be clipped by mClipContentDescendants.
+   * out-of-flows if necessary) should be clipped by mClipChainContentDescendants.
    * Null if no clipping applies.
    */
-  const DisplayItemClip* mClipContentDescendants;
+  const DisplayItemClipChain* mClipChainContentDescendants;
   /**
    * All containing-block descendants (i.e. frame descendants), including
    * display items for the current frame, should be clipped by
-   * mClipContainingBlockDescendants.
+   * mClipChainContainingBlockDescendants.
    * Null if no clipping applies.
    */
-  const DisplayItemClip* mClipContainingBlockDescendants;
+  const DisplayItemClipChain* mClipChainContainingBlockDescendants;
   /**
-   * The intersection of mClipContentDescendants and
-   * mClipContainingBlockDescendants.
+   * The intersection of mClipChainContentDescendants and
+   * mClipChainContainingBlockDescendants.
    * Allocated in the nsDisplayListBuilder arena. Null if none has been
-   * allocated or both mClipContentDescendants and mClipContainingBlockDescendants
+   * allocated or both mClipChainContentDescendants and mClipChainContainingBlockDescendants
    * are null.
    */
-  const DisplayItemClip* mCurrentCombinedClip;
-
-  /**
-   * The same for scroll clips.
-   */
-  const DisplayItemScrollClip* mScrollClipContentDescendants;
-  const DisplayItemScrollClip* mScrollClipContainingBlockDescendants;
-
-  /**
-   * A scroll clip that is an ancestor of all the scroll clips that were
-   * "current" on this clip state since EnterStackingContextContents was
-   * called.
-   */
-  const DisplayItemScrollClip* mStackingContextAncestorSC;
+  const DisplayItemClipChain* mCurrentCombinedClipChain;
+  bool mCurrentCombinedClipChainIsValid;
 };
 
 /**
  * A class to automatically save and restore the current clip state. Also
  * offers methods for modifying the clip state. Only one modification is allowed
  * to be in scope at a time using one of these objects; multiple modifications
  * require nested objects. The interface is written this way to prevent
  * dangling pointers to DisplayItemClips.
  */
 class DisplayListClipState::AutoSaveRestore {
 public:
   explicit AutoSaveRestore(nsDisplayListBuilder* aBuilder);
   void Restore()
   {
-    if (!mClearedForStackingContextContents) {
-      // Forward along the ancestor scroll clip to the original clip state.
-      mSavedState.mStackingContextAncestorSC =
-        DisplayItemScrollClip::PickAncestor(mSavedState.mStackingContextAncestorSC,
-                                            mState.mStackingContextAncestorSC);
-    }
     mState = mSavedState;
 #ifdef DEBUG
     mRestored = true;
 #endif
   }
   ~AutoSaveRestore()
   {
     Restore();
@@ -224,150 +166,67 @@ public:
   {
     NS_ASSERTION(!mRestored, "Already restored!");
     mState.Clear();
 #ifdef DEBUG
     mClipUsed = false;
 #endif
   }
 
-  void EnterStackingContextContents(bool aClear)
+  void ClearUpToASR(const ActiveScrolledRoot* aASR)
   {
     NS_ASSERTION(!mRestored, "Already restored!");
-    mState.EnterStackingContextContents(aClear);
-    mClearedForStackingContextContents = aClear;
-  }
-
-  void ExitStackingContextContents(const DisplayItemScrollClip** aOutContainerSC)
-  {
-    if (mClearedForStackingContextContents) {
-      // If we cleared the scroll clip, then the scroll clip that was current
-      // just before we cleared it is the one that needs to be set on the
-      // container item.
-      *aOutContainerSC = mSavedState.GetCurrentInnermostScrollClip();
-    } else {
-      // If we didn't clear the scroll clip, then the container item needs to
-      // get a scroll clip that's an ancestor of all its direct child items'
-      // scroll clips.
-      // The simplest way to satisfy this requirement would be to just take the
-      // root scroll clip (i.e. nullptr). However, this can cause the bounds of
-      // the container items to be enlarged unnecessarily, so instead we try to
-      // take the "deepest" scroll clip that satisfies the requirement.
-      // Usually this is the scroll clip that was current before we entered
-      // the stacking context contents (call that the "initial scroll clip").
-      // There are two cases in which the container scroll clip *won't* be the
-      // initial scroll clip (instead the container scroll clip will be a
-      // proper ancestor of the initial scroll clip):
-      //  (1) If SetScrollClipForContainingBlockDescendants was called with an
-      //      ancestor scroll clip of the initial scroll clip while we were
-      //      building our direct child items. This happens if we entered a
-      //      position:absolute or position:fixed element whose containing
-      //      block is an ancestor of the frame that generated the initial
-      //      scroll clip. Then the "ancestor scroll clip for stacking context
-      //      contents" will be set to that scroll clip.
-      //  (2) If one of our direct child items is a container item for which
-      //      (1) or (2) happened.
-      *aOutContainerSC = mState.CurrentAncestorScrollClipForStackingContextContents();
-    }
-    Restore();
-  }
-
-  bool SavedStateHasRoundedCorners()
-  {
-    const DisplayItemScrollClip* scrollClip = mSavedState.GetCurrentInnermostScrollClip();
-    if (scrollClip && scrollClip->HasRoundedCorners()) {
-      return true;
-    }
-    const DisplayItemClip* clip = mSavedState.GetClipForContainingBlockDescendants();
-    if (clip && clip->GetRoundedRectCount() > 0) {
-      return true;
-    }
-
-    clip = mSavedState.GetClipForContentDescendants();
-    if (clip && clip->GetRoundedRectCount() > 0) {
-      return true;
-    }
-    return false;
-  }
-
-  void TurnClipIntoScrollClipForContentDescendants(nsDisplayListBuilder* aBuilder, nsIScrollableFrame* aScrollableFrame)
-  {
-    NS_ASSERTION(!mRestored, "Already restored!");
-    mState.TurnClipIntoScrollClipForContentDescendants(aBuilder, aScrollableFrame);
+    mState.ClearUpToASR(aASR);
 #ifdef DEBUG
-    mClipUsed = true;
+    mClipUsed = false;
 #endif
   }
 
-  void TurnClipIntoScrollClipForContainingBlockDescendants(nsDisplayListBuilder* aBuilder, nsIScrollableFrame* aScrollableFrame)
-  {
-    NS_ASSERTION(!mRestored, "Already restored!");
-    mState.TurnClipIntoScrollClipForContainingBlockDescendants(aBuilder, aScrollableFrame);
-#ifdef DEBUG
-    mClipUsed = true;
-#endif
-  }
-
-  DisplayItemScrollClip* InsertInactiveScrollClipForContentDescendants(nsDisplayListBuilder* aBuilder, nsIScrollableFrame* aScrollableFrame)
+  void SetClipChainForContainingBlockDescendants(const DisplayItemClipChain* aClipChain)
   {
-    NS_ASSERTION(!mRestored, "Already restored!");
-    DisplayItemScrollClip* scrollClip = mState.InsertInactiveScrollClipForContentDescendants(aBuilder, aScrollableFrame);
-#ifdef DEBUG
-    mClipUsed = true;
-#endif
-    return scrollClip;
-  }
-
-  DisplayItemScrollClip* InsertInactiveScrollClipForContainingBlockDescendants(nsDisplayListBuilder* aBuilder, nsIScrollableFrame* aScrollableFrame)
-  {
-    NS_ASSERTION(!mRestored, "Already restored!");
-    DisplayItemScrollClip* scrollClip = mState.InsertInactiveScrollClipForContainingBlockDescendants(aBuilder, aScrollableFrame);
-#ifdef DEBUG
-    mClipUsed = true;
-#endif
-    return scrollClip;
+    mState.SetClipChainForContainingBlockDescendants(aClipChain);
   }
 
   /**
    * Intersects the given clip rect (with optional aRadii) with the current
    * mClipContainingBlockDescendants and sets mClipContainingBlockDescendants to
    * the result, stored in aClipOnStack.
    */
   void ClipContainingBlockDescendants(const nsRect& aRect,
                                       const nscoord* aRadii = nullptr)
   {
     NS_ASSERTION(!mRestored, "Already restored!");
     NS_ASSERTION(!mClipUsed, "mClip already used");
 #ifdef DEBUG
     mClipUsed = true;
 #endif
-    mState.ClipContainingBlockDescendants(aRect, aRadii, mClip);
+    mState.ClipContainingBlockDescendants(mBuilder, aRect, aRadii, mClipChain);
   }
 
   void ClipContentDescendants(const nsRect& aRect,
                               const nscoord* aRadii = nullptr)
   {
     NS_ASSERTION(!mRestored, "Already restored!");
     NS_ASSERTION(!mClipUsed, "mClip already used");
 #ifdef DEBUG
     mClipUsed = true;
 #endif
-    mState.ClipContentDescendants(aRect, aRadii, mClip);
+    mState.ClipContentDescendants(mBuilder, aRect, aRadii, mClipChain);
   }
 
   void ClipContentDescendants(const nsRect& aRect,
                               const nsRect& aRoundedRect,
                               const nscoord* aRadii = nullptr)
   {
     NS_ASSERTION(!mRestored, "Already restored!");
     NS_ASSERTION(!mClipUsed, "mClip already used");
 #ifdef DEBUG
     mClipUsed = true;
 #endif
-    mState.ClipContentDescendants(aRect, aRoundedRect, aRadii, mClip);
+    mState.ClipContentDescendants(mBuilder, aRect, aRoundedRect, aRadii, mClipChain);
   }
 
   /**
    * Clips containing-block descendants to the frame's content-box,
    * taking border-radius into account.
    * If aFlags contains ASSUME_DRAWING_RESTRICTED_TO_CONTENT_RECT then
    * we assume display items will not draw outside the content rect, so
    * clipping is only required if there is a border-radius. This is an
@@ -377,41 +236,41 @@ public:
                                                   nsIFrame* aFrame,
                                                   uint32_t aFlags = 0)
   {
     NS_ASSERTION(!mRestored, "Already restored!");
     NS_ASSERTION(!mClipUsed, "mClip already used");
 #ifdef DEBUG
     mClipUsed = true;
 #endif
-    mState.ClipContainingBlockDescendantsToContentBox(aBuilder, aFrame, mClip, aFlags);
+    mState.ClipContainingBlockDescendantsToContentBox(aBuilder, aFrame, mClipChain, aFlags);
   }
 
 protected:
+  nsDisplayListBuilder* mBuilder;
   DisplayListClipState& mState;
   DisplayListClipState mSavedState;
-  DisplayItemClip mClip;
+  DisplayItemClipChain mClipChain;
 #ifdef DEBUG
   bool mClipUsed;
   bool mRestored;
 #endif
-  bool mClearedForStackingContextContents;
 };
 
 class DisplayListClipState::AutoClipContainingBlockDescendantsToContentBox : public AutoSaveRestore {
 public:
   AutoClipContainingBlockDescendantsToContentBox(nsDisplayListBuilder* aBuilder,
                                                  nsIFrame* aFrame,
                                                  uint32_t aFlags = 0)
     : AutoSaveRestore(aBuilder)
   {
 #ifdef DEBUG
     mClipUsed = true;
 #endif
-    mState.ClipContainingBlockDescendantsToContentBox(aBuilder, aFrame, mClip, aFlags);
+    mState.ClipContainingBlockDescendantsToContentBox(aBuilder, aFrame, mClipChain, aFlags);
   }
 };
 
 /**
  * Do not use this outside of nsFrame::BuildDisplayListForChild, use
  * multiple AutoSaveRestores instead. We provide this class just to ensure
  * BuildDisplayListForChild is as efficient as possible.
  */
@@ -420,46 +279,33 @@ public:
   explicit AutoClipMultiple(nsDisplayListBuilder* aBuilder)
     : AutoSaveRestore(aBuilder)
 #ifdef DEBUG
     , mExtraClipUsed(false)
 #endif
   {}
 
   /**
-   * *aClip must survive longer than this object. Be careful!!!
-   */
-  void SetClipForContainingBlockDescendants(const DisplayItemClip* aClip)
-  {
-    mState.SetClipForContainingBlockDescendants(aClip);
-  }
-
-  void SetScrollClipForContainingBlockDescendants(const DisplayItemScrollClip* aScrollClip)
-  {
-    mState.SetScrollClipForContainingBlockDescendants(aScrollClip);
-  }
-
-  /**
    * Intersects the given clip rect (with optional aRadii) with the current
    * mClipContainingBlockDescendants and sets mClipContainingBlockDescendants to
    * the result, stored in aClipOnStack.
    */
   void ClipContainingBlockDescendantsExtra(const nsRect& aRect,
                                            const nscoord* aRadii)
   {
     NS_ASSERTION(!mRestored, "Already restored!");
     NS_ASSERTION(!mExtraClipUsed, "mExtraClip already used");
 #ifdef DEBUG
     mExtraClipUsed = true;
 #endif
-    mState.ClipContainingBlockDescendants(aRect, aRadii, mExtraClip);
+    mState.ClipContainingBlockDescendants(mBuilder, aRect, aRadii, mExtraClipChain);
   }
 
 protected:
-  DisplayItemClip mExtraClip;
+  DisplayItemClipChain mExtraClipChain;
 #ifdef DEBUG
   bool mExtraClipUsed;
 #endif
 };
 
 } // namespace mozilla
 
 #endif /* DISPLAYLISTCLIPSTATE_H_ */
--- a/layout/painting/FrameLayerBuilder.cpp
+++ b/layout/painting/FrameLayerBuilder.cpp
@@ -8,17 +8,16 @@
 #include "FrameLayerBuilder.h"
 
 #include "mozilla/LookAndFeel.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/dom/ProfileTimelineMarkerBinding.h"
 #include "mozilla/gfx/Matrix.h"
 #include "ActiveLayerTracker.h"
 #include "BasicLayers.h"
-#include "DisplayItemScrollClip.h"
 #include "ImageContainer.h"
 #include "ImageLayers.h"
 #include "LayerTreeInvalidation.h"
 #include "Layers.h"
 #include "LayerUserData.h"
 #include "MaskLayerImageCache.h"
 #include "UnitTransforms.h"
 #include "Units.h"
@@ -418,23 +417,22 @@ struct AssignedDisplayItem
  * PaintedLayer in z-order. This reduces the number of layers and
  * makes it more likely a display item will be rendered to an opaque
  * layer, giving us the best chance of getting subpixel AA.
  */
 class PaintedLayerData {
 public:
   PaintedLayerData() :
     mAnimatedGeometryRoot(nullptr),
-    mScrollClip(nullptr),
+    mASR(nullptr),
     mReferenceFrame(nullptr),
     mLayer(nullptr),
     mSolidColor(NS_RGBA(0, 0, 0, 0)),
     mIsSolidColorInVisibleRegion(false),
     mFontSmoothingBackgroundColor(NS_RGBA(0,0,0,0)),
-    mSingleItemFixedToViewport(false),
     mNeedComponentAlpha(false),
     mForceTransparentSurface(false),
     mHideAllLayersBelow(false),
     mOpaqueForAnimatedGeometryRootParent(false),
     mDisableFlattening(false),
     mBackfaceHidden(false),
     mImage(nullptr),
     mCommonClipCount(-1),
@@ -553,20 +551,21 @@ public:
   nsIntRect mScaledHitRegionBounds;
   nsIntRect mScaledMaybeHitRegionBounds;
   /**
    * The "active scrolled root" for all content in the layer. Must
    * be non-null; all content in a PaintedLayer must have the same
    * active scrolled root.
    */
   AnimatedGeometryRoot* mAnimatedGeometryRoot;
+  const ActiveScrolledRoot* mASR;
   /**
-   * The scroll clip for this layer.
+   * The chain of clips that should apply to this layer.
    */
-  const DisplayItemScrollClip* mScrollClip;
+  const DisplayItemClipChain* mClipChain;
   /**
    * The offset between mAnimatedGeometryRoot and the reference frame.
    */
   nsPoint mAnimatedGeometryRootOffset;
   /**
    * If non-null, the frame from which we'll extract "fixed positioning"
    * metadata for this layer. This can be a position:fixed frame or a viewport
    * frame; the latter case is used for background-attachment:fixed content.
@@ -583,21 +582,16 @@ public:
    */
   bool mIsSolidColorInVisibleRegion;
   /**
    * The target background color for smoothing fonts that are drawn on top of
    * transparent parts of the layer.
    */
   nscolor mFontSmoothingBackgroundColor;
   /**
-   * True if the layer contains exactly one item that returned true for
-   * ShouldFixToViewport.
-   */
-  bool mSingleItemFixedToViewport;
-  /**
    * True if there is any text visible in the layer that's over
    * transparent pixels in the layer.
    */
   bool mNeedComponentAlpha;
   /**
    * Set if the layer should be treated as transparent, even if its entire
    * area is covered by opaque display items. For example, this needs to
    * be set if something is going to "punch holes" in the layer by clearing
@@ -673,29 +667,34 @@ public:
    */
   nsTArray<AssignedDisplayItem> mAssignedDisplayItems;
 
 };
 
 struct NewLayerEntry {
   NewLayerEntry()
     : mAnimatedGeometryRoot(nullptr)
-    , mScrollClip(nullptr)
+    , mASR(nullptr)
+    , mClipChain(nullptr)
+    , mScrollMetadataASR(nullptr)
     , mLayerContentsVisibleRect(0, 0, -1, -1)
     , mLayerState(LAYER_INACTIVE)
     , mHideAllLayersBelow(false)
     , mOpaqueForAnimatedGeometryRootParent(false)
     , mPropagateComponentAlphaFlattening(true)
     , mUntransformedVisibleRegion(false)
+    , mIsFixedToRootScrollFrame(false)
   {}
   // mLayer is null if the previous entry is for a PaintedLayer that hasn't
   // been optimized to some other form (yet).
   RefPtr<Layer> mLayer;
   AnimatedGeometryRoot* mAnimatedGeometryRoot;
-  const DisplayItemScrollClip* mScrollClip;
+  const ActiveScrolledRoot* mASR;
+  const DisplayItemClipChain* mClipChain;
+  const ActiveScrolledRoot* mScrollMetadataASR;
   // If non-null, this ScrollMetadata is set to the be the first ScrollMetadata
   // on the layer.
   UniquePtr<ScrollMetadata> mBaseScrollMetadata;
   // The following are only used for retained layers (for occlusion
   // culling of those layers). These regions are all relative to the
   // container reference frame.
   nsIntRegion mVisibleRegion;
   nsIntRegion mOpaqueRegion;
@@ -715,16 +714,17 @@ struct NewLayerEntry {
   bool mOpaqueForAnimatedGeometryRootParent;
 
   // If true, then the content flags for this layer should contribute
   // to our decision to flatten component alpha layers, false otherwise.
   bool mPropagateComponentAlphaFlattening;
   // mVisibleRegion is relative to the associated frame before
   // transform.
   bool mUntransformedVisibleRegion;
+  bool mIsFixedToRootScrollFrame;
 };
 
 class PaintedLayerDataTree;
 
 /**
  * This is tree node type for PaintedLayerDataTree.
  * Each node corresponds to a different animated geometry root, and contains
  * a stack of PaintedLayerDatas, in bottom-to-top order.
@@ -770,17 +770,18 @@ public:
   /**
    * Find a PaintedLayerData in our mPaintedLayerDataStack that aItem can be
    * added to. Creates a new PaintedLayerData by calling
    * aNewPaintedLayerCallback if necessary.
    */
   template<typename NewPaintedLayerCallbackType>
   PaintedLayerData* FindPaintedLayerFor(const nsIntRect& aVisibleRect,
                                         bool aBackfaceHidden,
-                                        const DisplayItemScrollClip* aScrollClip,
+                                        const ActiveScrolledRoot* aASR,
+                                        const DisplayItemClipChain* aClipChain,
                                         NewPaintedLayerCallbackType aNewPaintedLayerCallback);
 
   /**
    * Find an opaque background color for aRegion. Pulls a color from the parent
    * geometry root if appropriate, but only if that color is present underneath
    * the whole clip of this node, so that this node's contents can animate or
    * move (possibly async) without having to change the background color.
    * @param aUnderIndex Searching will start in mPaintedLayerDataStack right
@@ -944,17 +945,18 @@ public:
 
   /**
    * Find a PaintedLayerData for aItem. This can either be an existing
    * PaintedLayerData from inside a node in our tree, or a new one that gets
    * created by a call out to aNewPaintedLayerCallback.
    */
   template<typename NewPaintedLayerCallbackType>
   PaintedLayerData* FindPaintedLayerFor(AnimatedGeometryRoot* aAnimatedGeometryRoot,
-                                        const DisplayItemScrollClip* aScrollClip,
+                                        const ActiveScrolledRoot* aASR,
+                                        const DisplayItemClipChain* aClipChain,
                                         const nsIntRect& aVisibleRect,
                                         bool aBackfaceidden,
                                         NewPaintedLayerCallbackType aNewPaintedLayerCallback);
 
   /**
    * Finish everything.
    */
   void Finish();
@@ -1043,24 +1045,27 @@ public:
                  FrameLayerBuilder* aLayerBuilder,
                  nsIFrame* aContainerFrame,
                  nsDisplayItem* aContainerItem,
                  const nsRect& aContainerBounds,
                  ContainerLayer* aContainerLayer,
                  const ContainerLayerParameters& aParameters,
                  bool aFlattenToSingleLayer,
                  nscolor aBackgroundColor,
-                 const DisplayItemScrollClip* aContainerScrollClip) :
+                 const ActiveScrolledRoot* aContainerASR,
+                 const ActiveScrolledRoot* aContainerScrollMetadataASR,
+                 const ActiveScrolledRoot* aContainerCompositorASR) :
     mBuilder(aBuilder), mManager(aManager),
     mLayerBuilder(aLayerBuilder),
     mContainerFrame(aContainerFrame),
     mContainerLayer(aContainerLayer),
     mContainerBounds(aContainerBounds),
-    mContainerScrollClip(aContainerScrollClip),
-    mScrollClipForPerspectiveChild(aParameters.mScrollClipForPerspectiveChild),
+    mContainerASR(aContainerASR),
+    mContainerScrollMetadataASR(aContainerScrollMetadataASR),
+    mContainerCompositorASR(aContainerCompositorASR),
     mParameters(aParameters),
     mPaintedLayerDataTree(*this, aBackgroundColor),
     mFlattenToSingleLayer(aFlattenToSingleLayer)
   {
     nsPresContext* presContext = aContainerFrame->PresContext();
     mAppUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
     mContainerReferenceFrame =
       const_cast<nsIFrame*>(aContainerItem ? aContainerItem->ReferenceFrameForChildren() :
@@ -1284,17 +1289,17 @@ protected:
                                 PaintedLayer* aNewLayer);
   /**
    * Returns true if aItem's opaque area (in aOpaque) covers the entire
    * scrollable area of its presshell.
    */
   bool ItemCoversScrollableArea(nsDisplayItem* aItem, const nsRegion& aOpaque);
 
   /**
-   * Set FrameMetrics and scroll-induced clipping on aEntry's layer.
+   * Set ScrollMetadata and scroll-induced clipping on aEntry's layer.
    */
   void SetupScrollingMetadata(NewLayerEntry* aEntry);
 
   /**
    * Applies occlusion culling.
    * For each layer in mNewChildLayers, remove from its visible region the
    * opaque regions of the layers at higher z-index, but only if they have
    * the same animated geometry root and fixed-pos frame ancestor.
@@ -1320,29 +1325,31 @@ protected:
                                 bool* aOpaqueForAnimatedGeometryRootParent);
 
   /**
    * Return a PaintedLayerData object that is initialized for a layer that
    * aItem will be assigned to.
    * @param  aItem                 The item that is going to be added.
    * @param  aVisibleRect          The visible rect of the item.
    * @param  aAnimatedGeometryRoot The item's animated geometry root.
-   * @param  aScrollClip           The scroll clip for this PaintedLayer.
+   * @param  aASR                  The active scrolled root that moves this PaintedLayer.
+   * @param  aClipChain            The clip chain that the compositor needs to
+   *                               apply to this layer.
+   * @param  aScrollMetadataASR    The leaf ASR for which scroll metadata needs to be
+   *                               set on the layer, because either the layer itself
+   *                               or its scrolled clip need to move with that ASR.
    * @param  aTopLeft              The offset between aAnimatedGeometryRoot and
    *                               the reference frame.
-   * @param aShouldFixToViewport   If true, aAnimatedGeometryRoot is the
-   *                               viewport and we will be adding fixed-pos
-   *                               metadata for this layer because the display
-   *                               item returned true from ShouldFixToViewport.
    */
   PaintedLayerData NewPaintedLayerData(nsDisplayItem* aItem,
                                        AnimatedGeometryRoot* aAnimatedGeometryRoot,
-                                       const DisplayItemScrollClip* aScrollClip,
-                                       const nsPoint& aTopLeft,
-                                       bool aShouldFixToViewport);
+                                       const ActiveScrolledRoot* aASR,
+                                       const DisplayItemClipChain* aClipChain,
+                                       const ActiveScrolledRoot* aScrollMetadataASR,
+                                       const nsPoint& aTopLeft);
 
   /* Build a mask layer to represent the clipping region. Will return null if
    * there is no clipping specified or a mask layer cannot be built.
    * Builds an ImageLayer for the appropriate backend; the mask is relative to
    * aLayer's visible region.
    * aLayer is the layer to be clipped.
    * relative to the container reference frame
    * aRoundedRectClipCount is used when building mask layers for PaintedLayers,
@@ -1369,28 +1376,40 @@ protected:
   void SetupMaskLayerForCSSMask(Layer* aLayer, nsDisplayMask* aMaskItem);
 
   already_AddRefed<Layer> CreateMaskLayer(
     Layer *aLayer, const DisplayItemClip& aClip,
     const Maybe<size_t>& aForAncestorMaskLayer,
     uint32_t aRoundedRectClipCount = UINT32_MAX);
 
   bool ChooseAnimatedGeometryRoot(const nsDisplayList& aList,
-                                  AnimatedGeometryRoot **aAnimatedGeometryRoot);
+                                  AnimatedGeometryRoot** aAnimatedGeometryRoot,
+                                  const ActiveScrolledRoot** aASR);
 
   nsDisplayListBuilder*            mBuilder;
   LayerManager*                    mManager;
   FrameLayerBuilder*               mLayerBuilder;
   nsIFrame*                        mContainerFrame;
   nsIFrame*                        mContainerReferenceFrame;
   AnimatedGeometryRoot*            mContainerAnimatedGeometryRoot;
   ContainerLayer*                  mContainerLayer;
   nsRect                           mContainerBounds;
-  const DisplayItemScrollClip*     mContainerScrollClip;
-  const DisplayItemScrollClip*     mScrollClipForPerspectiveChild;
+
+  // Due to the way we store scroll annotations in the layer tree, we need to
+  // keep track of three (possibly different) ASRs here.
+  // mContainerASR is the ASR of the container display item that this
+  // ContainerState was created for.
+  // mContainerScrollMetadataASR is the ASR of the leafmost scroll metadata
+  // that's in effect on mContainerLayer.
+  // mContainerCompositorASR is the ASR that mContainerLayer moves with on
+  // the compositor / APZ side, taking into account both the scroll meta data
+  // and the fixed position annotation on itself and its ancestors.
+  const ActiveScrolledRoot*        mContainerASR;
+  const ActiveScrolledRoot*        mContainerScrollMetadataASR;
+  const ActiveScrolledRoot*        mContainerCompositorASR;
 #ifdef DEBUG
   nsRect                           mAccumulatedChildBounds;
 #endif
   ContainerLayerParameters         mParameters;
   /**
    * The region of PaintedLayers that should be invalidated every time
    * we recycle one.
    */
@@ -2754,27 +2773,29 @@ PaintedLayerDataNode::AddChildNodeFor(An
   mChildren.AppendElement(Move(child));
   return mChildren.LastElement().get();
 }
 
 template<typename NewPaintedLayerCallbackType>
 PaintedLayerData*
 PaintedLayerDataNode::FindPaintedLayerFor(const nsIntRect& aVisibleRect,
                                           bool aBackfaceHidden,
-                                          const DisplayItemScrollClip* aScrollClip,
+                                          const ActiveScrolledRoot* aASR,
+                                          const DisplayItemClipChain* aClipChain,
                                           NewPaintedLayerCallbackType aNewPaintedLayerCallback)
 {
   if (!mPaintedLayerDataStack.IsEmpty()) {
     PaintedLayerData* lowestUsableLayer = nullptr;
     for (auto& data : Reversed(mPaintedLayerDataStack)) {
       if (data.mVisibleAboveRegion.Intersects(aVisibleRect)) {
         break;
       }
       if (data.mBackfaceHidden == aBackfaceHidden &&
-          data.mScrollClip == aScrollClip) {
+          data.mASR == aASR &&
+          DisplayItemClipChain::Equal(data.mClipChain, aClipChain)) {
         lowestUsableLayer = &data;
       }
       nsIntRegion visibleRegion = data.mVisibleRegion;
       // Also check whether the event-regions intersect the visible rect,
       // unless we're in an inactive layer, in which case the event-regions
       // will be hoisted out into their own layer.
       // For performance reasons, we check the intersection with the bounds
       // of the event-regions.
@@ -2919,27 +2940,28 @@ PaintedLayerDataTree::AddingOwnLayer(Ani
     }
     node->SetAllDrawingAbove();
   }
 }
 
 template<typename NewPaintedLayerCallbackType>
 PaintedLayerData*
 PaintedLayerDataTree::FindPaintedLayerFor(AnimatedGeometryRoot* aAnimatedGeometryRoot,
-                                          const DisplayItemScrollClip* aScrollClip,
+                                          const ActiveScrolledRoot* aASR,
+                                          const DisplayItemClipChain* aClipChain,
                                           const nsIntRect& aVisibleRect,
                                           bool aBackfaceHidden,
                                           NewPaintedLayerCallbackType aNewPaintedLayerCallback)
 {
   const nsIntRect* bounds = &aVisibleRect;
   FinishPotentiallyIntersectingNodes(aAnimatedGeometryRoot, bounds);
   PaintedLayerDataNode* node = EnsureNodeFor(aAnimatedGeometryRoot);
 
   PaintedLayerData* data =
-    node->FindPaintedLayerFor(aVisibleRect, aBackfaceHidden, aScrollClip,
+    node->FindPaintedLayerFor(aVisibleRect, aBackfaceHidden, aASR, aClipChain,
                               aNewPaintedLayerCallback);
   return data;
 }
 
 void
 PaintedLayerDataTree::FinishPotentiallyIntersectingNodes(AnimatedGeometryRoot* aAnimatedGeometryRoot,
                                                          const nsIntRect* aRect)
 {
@@ -3184,21 +3206,24 @@ void ContainerState::FinishPaintedLayerD
                                     : PrepareColorLayer(data);
 
     if (layer) {
       NS_ASSERTION(FindIndexOfLayerIn(mNewChildLayers, layer) < 0,
                    "Layer already in list???");
       NS_ASSERTION(newLayerEntry->mLayer == data->mLayer,
                    "Painted layer at wrong index");
       // Store optimized layer in reserved slot
+      NewLayerEntry* paintedLayerEntry = newLayerEntry;
       newLayerEntry = &mNewChildLayers[data->mNewChildLayersIndex + 1];
       NS_ASSERTION(!newLayerEntry->mLayer, "Slot already occupied?");
       newLayerEntry->mLayer = layer;
       newLayerEntry->mAnimatedGeometryRoot = data->mAnimatedGeometryRoot;
-      newLayerEntry->mScrollClip = data->mScrollClip;
+      newLayerEntry->mASR = paintedLayerEntry->mASR;
+      newLayerEntry->mClipChain = paintedLayerEntry->mClipChain;
+      newLayerEntry->mScrollMetadataASR = paintedLayerEntry->mScrollMetadataASR;
 
       // Hide the PaintedLayer. We leave it in the layer tree so that we
       // can find and recycle it later.
       ParentLayerIntRect emptyRect;
       data->mLayer->SetClipRect(Some(emptyRect));
       data->mLayer->SetVisibleRegion(LayerIntRegion());
       data->mLayer->InvalidateRegion(data->mLayer->GetValidRegion().GetBounds());
       data->mLayer->SetEventRegions(EventRegions());
@@ -3207,28 +3232,16 @@ void ContainerState::FinishPaintedLayerD
 
   if (!layer) {
     // We couldn't optimize to an image layer or a color layer above.
     layer = data->mLayer;
     layer->SetClipRect(Nothing());
     FLB_LOG_PAINTED_LAYER_DECISION(data, "  Selected painted layer=%p\n", layer.get());
   }
 
-  // If the layer is a fixed background layer, the clip on the fixed background
-  // display item was not applied to the opaque region in
-  // ContainerState::ComputeOpaqueRect(), but was saved in data->mItemClip.
-  // Apply it to the opaque region now. Note that it's important to do this
-  // before the opaque region is propagated to the NewLayerEntry below.
-  if (data->mSingleItemFixedToViewport && data->mItemClip.HasClip()) {
-    nsRect clipRect = data->mItemClip.GetClipRect();
-    nsRect insideRoundedCorners = data->mItemClip.ApproximateIntersectInward(clipRect);
-    nsIntRect insideRoundedCornersScaled = ScaleToInsidePixels(insideRoundedCorners);
-    data->mOpaqueRegion.AndWith(insideRoundedCornersScaled);
-  }
-
   if (mLayerBuilder->IsBuildingRetainedLayers()) {
     newLayerEntry->mVisibleRegion = data->mVisibleRegion;
     newLayerEntry->mOpaqueRegion = data->mOpaqueRegion;
     newLayerEntry->mHideAllLayersBelow = data->mHideAllLayersBelow;
     newLayerEntry->mOpaqueForAnimatedGeometryRootParent = data->mOpaqueForAnimatedGeometryRootParent;
   } else {
     SetOuterVisibleRegionForLayer(layer, data->mVisibleRegion);
   }
@@ -3283,39 +3296,18 @@ void ContainerState::FinishPaintedLayerD
     userData->mForcedBackgroundColor = backgroundColor;
 
     userData->mFontSmoothingBackgroundColor = data->mFontSmoothingBackgroundColor;
 
     // use a mask layer for rounded rect clipping.
     // data->mCommonClipCount may be -1 if we haven't put any actual
     // drawable items in this layer (i.e. it's only catching events).
     int32_t commonClipCount;
-    // If the layer contains a single item fixed to the viewport, we removed
-    // its clip in ProcessDisplayItems() and saved it to set on the layer instead.
-    // Set the clip on the layer now.
-    if (data->mSingleItemFixedToViewport && data->mItemClip.HasClip()) {
-      nsIntRect layerClipRect = ScaleToNearestPixels(data->mItemClip.GetClipRect());
-      layerClipRect.MoveBy(mParameters.mOffset);
-      // The clip from such an item becomes part of the layer's scrolled clip,
-      // and the associated mask layer one of the layer's "ancestor mask layers".
-      LayerClip scrolledClip;
-      scrolledClip.SetClipRect(ViewAs<ParentLayerPixel>(layerClipRect));
-      scrolledClip.SetMaskLayerIndex(
-          SetupMaskLayerForScrolledClip(data->mLayer, data->mItemClip));
-      data->mLayer->SetScrolledClip(Some(scrolledClip));
-      // There is only one item, so all of the clips are in common to all items.
-      // data->mCommonClipCount will be zero because we removed the clip from
-      // the display item. (It could also be -1 if we're inside an inactive
-      // layer tree in which we don't call UpdateCommonClipCount() at all.)
-      MOZ_ASSERT(data->mCommonClipCount == -1 || data->mCommonClipCount == 0);
-      commonClipCount = data->mItemClip.GetRoundedRectCount();
-    } else {
-      commonClipCount = std::max(0, data->mCommonClipCount);
-      SetupMaskLayer(layer, data->mItemClip, commonClipCount);
-    }
+    commonClipCount = std::max(0, data->mCommonClipCount);
+    SetupMaskLayer(layer, data->mItemClip, commonClipCount);
     // copy commonClipCount to the entry
     FrameLayerBuilder::PaintedLayerItemsEntry* entry = mLayerBuilder->
       GetPaintedLayerItemsEntry(static_cast<PaintedLayer*>(layer.get()));
     entry->mCommonClipCount = commonClipCount;
   } else {
     // mask layer for image and color layers
     SetupMaskLayer(layer, data->mItemClip);
   }
@@ -3626,32 +3618,35 @@ PaintedLayerData::AccumulateEventRegions
   // for quick access in FindPaintedLayerFor().
   mScaledHitRegionBounds = aState->ScaleToOutsidePixels(mHitRegion.GetBounds());
   mScaledMaybeHitRegionBounds = aState->ScaleToOutsidePixels(mMaybeHitRegion.GetBounds());
 }
 
 PaintedLayerData
 ContainerState::NewPaintedLayerData(nsDisplayItem* aItem,
                                     AnimatedGeometryRoot* aAnimatedGeometryRoot,
-                                    const DisplayItemScrollClip* aScrollClip,
-                                    const nsPoint& aTopLeft,
-                                    bool aShouldFixToViewport)
+                                    const ActiveScrolledRoot* aASR,
+                                    const DisplayItemClipChain* aClipChain,
+                                    const ActiveScrolledRoot* aScrollMetadataASR,
+                                    const nsPoint& aTopLeft)
 {
   PaintedLayerData data;
   data.mAnimatedGeometryRoot = aAnimatedGeometryRoot;
-  data.mScrollClip = aScrollClip;
+  data.mASR = aASR;
+  data.mClipChain = aClipChain,
   data.mAnimatedGeometryRootOffset = aTopLeft;
   data.mReferenceFrame = aItem->ReferenceFrame();
-  data.mSingleItemFixedToViewport = aShouldFixToViewport;
   data.mBackfaceHidden = aItem->Frame()->In3DContextAndBackfaceIsHidden();
 
   data.mNewChildLayersIndex = mNewChildLayers.Length();
   NewLayerEntry* newLayerEntry = mNewChildLayers.AppendElement();
   newLayerEntry->mAnimatedGeometryRoot = aAnimatedGeometryRoot;
-  newLayerEntry->mScrollClip = aScrollClip;
+  newLayerEntry->mASR = aASR;
+  newLayerEntry->mScrollMetadataASR = aScrollMetadataASR;
+  newLayerEntry->mClipChain = aClipChain,
   // newLayerEntry->mOpaqueRegion is filled in later from
   // paintedLayerData->mOpaqueRegion, if necessary.
 
   // Allocate another entry for this layer's optimization to ColorLayer/ImageLayer
   mNewChildLayers.AppendElement();
 
   return data;
 }
@@ -3741,29 +3736,31 @@ PaintInactiveLayer(nsDisplayListBuilder*
 }
 
 /**
  * Chooses a single active scrolled root for the entire display list, used
  * when we are flattening layers.
  */
 bool
 ContainerState::ChooseAnimatedGeometryRoot(const nsDisplayList& aList,
-                                           AnimatedGeometryRoot **aAnimatedGeometryRoot)
+                                           AnimatedGeometryRoot** aAnimatedGeometryRoot,
+                                           const ActiveScrolledRoot** aASR)
 {
   for (nsDisplayItem* item = aList.GetBottom(); item; item = item->GetAbove()) {
     LayerState layerState = item->GetLayerState(mBuilder, mManager, mParameters);
     // Don't use an item that won't be part of any PaintedLayers to pick the
     // active scrolled root.
     if (layerState == LAYER_ACTIVE_FORCE) {
       continue;
     }
 
     // Try using the actual active scrolled root of the backmost item, as that
     // should result in the least invalidation when scrolling.
     *aAnimatedGeometryRoot = item->GetAnimatedGeometryRoot();
+    *aASR = item->GetActiveScrolledRoot();
     return true;
   }
   return false;
 }
 
 nsIntRegion
 ContainerState::ComputeOpaqueRect(nsDisplayItem* aItem,
                                   AnimatedGeometryRoot* aAnimatedGeometryRoot,
@@ -3817,53 +3814,44 @@ ContainerState::ComputeOpaqueRect(nsDisp
     displayport += scrollFrame->GetOffsetToCrossDoc(mContainerReferenceFrame);
     if (opaquePixels.Contains(ScaleRegionToNearestPixels(displayport))) {
       *aOpaqueForAnimatedGeometryRootParent = true;
     }
   }
   return opaquePixels;
 }
 
-static const DisplayItemScrollClip*
-InnermostScrollClipApplicableToAGR(const DisplayItemScrollClip* aItemScrollClip,
-                                   AnimatedGeometryRoot* aAnimatedGeometryRoot)
-{
-  // "Applicable" scroll clips are those that are for nsIScrollableFrames
-  // that are ancestors of aAnimatedGeometryRoot or ancestors of aContainerScrollClip.
-  // They can be applied to all items sharing this animated geometry root, so
-  // instead of applying to the items individually, they can be applied to the
-  // whole layer.
-  for (const DisplayItemScrollClip* scrollClip = aItemScrollClip;
-       scrollClip;
-       scrollClip = scrollClip->mParent) {
-    nsIFrame* scrolledFrame = scrollClip->mScrollableFrame->GetScrolledFrame();
-    if (nsLayoutUtils::IsAncestorFrameCrossDoc(scrolledFrame, *aAnimatedGeometryRoot)) {
-      // scrollClip and all its ancestors are applicable.
-      return scrollClip;
-    }
-  }
-  return nullptr;
-}
-
 Maybe<size_t>
 ContainerState::SetupMaskLayerForScrolledClip(Layer* aLayer,
                                               const DisplayItemClip& aClip)
 {
   if (aClip.GetRoundedRectCount() > 0) {
     Maybe<size_t> maskLayerIndex = Some(aLayer->GetAncestorMaskLayerCount());
     if (RefPtr<Layer> maskLayer = CreateMaskLayer(aLayer, aClip, maskLayerIndex,
                                                   aClip.GetRoundedRectCount())) {
       aLayer->AddAncestorMaskLayer(maskLayer);
       return maskLayerIndex;
     }
     // Fall through to |return Nothing()|.
   }
   return Nothing();
 }
 
+static const ActiveScrolledRoot*
+GetASRForPerspective(const ActiveScrolledRoot* aASR, nsIFrame* aPerspectiveFrame)
+{
+  for (const ActiveScrolledRoot* asr = aASR; asr; asr = asr->mParent) {
+    nsIFrame* scrolledFrame = asr->mScrollableFrame->GetScrolledFrame();
+    if (nsLayoutUtils::IsAncestorFrameCrossDoc(scrolledFrame, aPerspectiveFrame)) {
+      return asr;
+    }
+  }
+  return nullptr;
+}
+
 void
 ContainerState::SetupMaskLayerForCSSMask(Layer* aLayer,
                                          nsDisplayMask* aMaskItem)
 {
   MOZ_ASSERT(mManager->IsCompositingCheap());
 
   RefPtr<ImageLayer> maskLayer =
     CreateOrRecycleMaskImageLayerFor(MaskLayerKey(aLayer, Nothing()),
@@ -3948,24 +3936,25 @@ ContainerState::SetupMaskLayerForCSSMask
  */
 void
 ContainerState::ProcessDisplayItems(nsDisplayList* aList)
 {
   PROFILER_LABEL("ContainerState", "ProcessDisplayItems",
     js::ProfileEntry::Category::GRAPHICS);
 
   AnimatedGeometryRoot* lastAnimatedGeometryRoot = mContainerAnimatedGeometryRoot;
+  const ActiveScrolledRoot* lastASR = mContainerASR;
   nsPoint lastAGRTopLeft;
   nsPoint topLeft(0,0);
 
   // When NO_COMPONENT_ALPHA is set, items will be flattened into a single
   // layer, so we need to choose which active scrolled root to use for all
   // items.
   if (mFlattenToSingleLayer) {
-    if (ChooseAnimatedGeometryRoot(*aList, &lastAnimatedGeometryRoot)) {
+    if (ChooseAnimatedGeometryRoot(*aList, &lastAnimatedGeometryRoot, &lastASR)) {
       lastAGRTopLeft = (*lastAnimatedGeometryRoot)->GetOffsetToCrossDoc(mContainerReferenceFrame);
     }
   }
 
   int32_t maxLayers = gfxPrefs::MaxActiveLayers();
   int layerCount = 0;
 
   nsDisplayList savedItems;
@@ -4024,76 +4013,48 @@ ContainerState::ProcessDisplayItems(nsDi
     LayerState layerState = item->GetLayerState(mBuilder, mManager, mParameters);
     if (layerState == LAYER_INACTIVE &&
         nsDisplayItem::ForceActiveLayers()) {
       layerState = LAYER_ACTIVE;
     }
 
     bool forceInactive;
     AnimatedGeometryRoot* animatedGeometryRoot;
-    AnimatedGeometryRoot* animatedGeometryRootForClip = nullptr;
+    const ActiveScrolledRoot* itemASR = nullptr;
+    const DisplayItemClipChain* layerClipChain = nullptr;
     if (mFlattenToSingleLayer && layerState != LAYER_ACTIVE_FORCE) {
       forceInactive = true;
       animatedGeometryRoot = lastAnimatedGeometryRoot;
+      itemASR = lastASR;
       topLeft = lastAGRTopLeft;
+      item->FuseClipChainUpTo(mBuilder, mContainerASR);
     } else {
       forceInactive = false;
       if (mManager->IsWidgetLayerManager()) {
         animatedGeometryRoot = item->GetAnimatedGeometryRoot();
-        animatedGeometryRootForClip = item->AnimatedGeometryRootForScrollMetadata();
+        itemASR = item->GetActiveScrolledRoot();
+        const DisplayItemClipChain* itemClipChain = item->GetClipChain();
+        if (itemClipChain && itemClipChain->mASR == itemASR) {
+          layerClipChain = itemClipChain->mParent;
+        } else {
+          layerClipChain = itemClipChain;
+        }
       } else {
         // For inactive layer subtrees, splitting content into PaintedLayers
         // based on animated geometry roots is pointless. It's more efficient
         // to build the minimum number of layers.
         animatedGeometryRoot = mContainerAnimatedGeometryRoot;
-
+        itemASR = mContainerASR;
+        item->FuseClipChainUpTo(mBuilder, mContainerASR);
       }
       topLeft = (*animatedGeometryRoot)->GetOffsetToCrossDoc(mContainerReferenceFrame);
     }
-    if (!animatedGeometryRootForClip) {
-      animatedGeometryRootForClip = animatedGeometryRoot;
-    }
-
-    const DisplayItemScrollClip* itemScrollClip = item->ScrollClip();
-    // Now we need to separate the item's scroll clip chain into those scroll
-    // clips that can  be applied to the whole layer (i.e. to all items
-    // sharing the item's animated geometry root), and those that need to be
-    // applied to the item itself.
-    const DisplayItemScrollClip* agrScrollClip =
-      InnermostScrollClipApplicableToAGR(itemScrollClip, animatedGeometryRootForClip);
-    MOZ_ASSERT(DisplayItemScrollClip::IsAncestor(agrScrollClip, itemScrollClip));
-
-    if (agrScrollClip != itemScrollClip) {
-      // Pick up any scroll clips that should apply to the item and apply them.
-      DisplayItemClip clip = item->GetClip();
-      for (const DisplayItemScrollClip* scrollClip = itemScrollClip;
-           scrollClip && scrollClip != agrScrollClip && scrollClip != mContainerScrollClip;
-           scrollClip = scrollClip->mParent) {
-        if (scrollClip->mClip) {
-          clip.IntersectWith(*scrollClip->mClip);
-        }
-      }
-      item->SetClip(mBuilder, clip);
-    }
-
-    bool clipMovesWithLayer = (animatedGeometryRoot == animatedGeometryRootForClip);
-
-    bool shouldFixToViewport = !clipMovesWithLayer &&
-        !(*animatedGeometryRoot)->GetParent() &&
-        item->ShouldFixToViewport(mBuilder);
-
-    // For items that are fixed to the viewport, remove their clip at the
-    // display item level because additional areas could be brought into
-    // view by async scrolling. Save the clip so we can set it on the layer
-    // instead later.
-    DisplayItemClip fixedToViewportClip = DisplayItemClip::NoClip();
-    if (shouldFixToViewport) {
-      fixedToViewportClip = item->GetClip();
-      item->SetClip(mBuilder, DisplayItemClip::NoClip());
-    }
+
+    const ActiveScrolledRoot* scrollMetadataASR =
+        layerClipChain ? ActiveScrolledRoot::PickDescendant(itemASR, layerClipChain->mASR) : itemASR;
 
     bool snap;
     nsRect itemContent = item->GetBounds(mBuilder, &snap);
     if (itemType == nsDisplayItem::TYPE_LAYER_EVENT_REGIONS) {
       nsDisplayLayerEventRegions* eventRegions =
         static_cast<nsDisplayLayerEventRegions*>(item);
       itemContent = eventRegions->GetHitRegionBounds(mBuilder, &snap);
     }
@@ -4114,27 +4075,22 @@ ContainerState::ProcessDisplayItems(nsDi
     nsRect bounds = itemContent;
     bool dummy;
     if (itemType == nsDisplayItem::TYPE_LAYER_EVENT_REGIONS) {
       bounds = item->GetBounds(mBuilder, &dummy);
       if (itemClip.HasClip()) {
         bounds.IntersectRect(bounds, itemClip.GetClipRect());
       }
     }
-    bounds = fixedToViewportClip.ApplyNonRoundedIntersection(bounds);
     if (!bounds.IsEmpty()) {
-      for (const DisplayItemScrollClip* scrollClip = itemScrollClip;
-           scrollClip && scrollClip != mContainerScrollClip;
-           scrollClip = scrollClip->mParent) {
-        if (scrollClip->mClip) {
-          if (scrollClip->mIsAsyncScrollable) {
-            bounds = scrollClip->mClip->GetClipRect();
-          } else {
-            bounds = scrollClip->mClip->ApplyNonRoundedIntersection(bounds);
-          }
+      if (itemASR != mContainerASR) {
+        const DisplayItemClip* clip = DisplayItemClipChain::ClipForASR(item->GetClipChain(), mContainerASR);
+        MOZ_ASSERT(clip, "the item should have finite bounds with respect to mContainerASR.");
+        if (clip) {
+          bounds = clip->GetClipRect();
         }
       }
     }
     ((nsRect&)mAccumulatedChildBounds).UnionRect(mAccumulatedChildBounds, bounds);
 #endif
 
     nsIntRect itemVisibleRect = itemDrawRect;
     // We haven't computed visibility at this point, so item->GetVisibleRect()
@@ -4172,32 +4128,16 @@ ContainerState::ProcessDisplayItems(nsDi
       // Note that items without their own layers can't be skipped this
       // way, since their PaintedLayer may decide it wants to draw them
       // into its buffer even if they're currently covered.
       if (itemVisibleRect.IsEmpty() &&
           !item->ShouldBuildLayerEvenIfInvisible(mBuilder)) {
         continue;
       }
 
-      if (mScrollClipForPerspectiveChild) {
-        // We are the single transform child item of an nsDisplayPerspective.
-        // Our parent forwarded a scroll clip to us. Pick it up.
-        // We do this after any clipping has been applied, because this
-        // forwarded scroll clip is only used for scrolling (in the form of
-        // APZ frame metrics), not for clipping - the clip still belongs on
-        // the perspective item.
-        MOZ_ASSERT(itemType == nsDisplayItem::TYPE_TRANSFORM);
-        MOZ_ASSERT(!itemScrollClip);
-        MOZ_ASSERT(!agrScrollClip);
-        MOZ_ASSERT(DisplayItemScrollClip::IsAncestor(mContainerScrollClip,
-                                                      mScrollClipForPerspectiveChild));
-        itemScrollClip = mScrollClipForPerspectiveChild;
-        agrScrollClip = mScrollClipForPerspectiveChild;
-      }
-
       // 3D-transformed layers don't necessarily draw in the order in which
       // they're added to their parent container layer.
       bool mayDrawOutOfOrder = itemType == nsDisplayItem::TYPE_TRANSFORM &&
         (item->Frame()->Combines3DTransformWithAncestors() ||
          item->Frame()->Extend3DContext());
 
       // Let mPaintedLayerDataTree know about this item, so that
       // FindPaintedLayerFor and FindOpaqueBackgroundColor are aware of this
@@ -4207,71 +4147,95 @@ ContainerState::ProcessDisplayItems(nsDi
       // geometry root that we give it, but it can't easily figure about
       // overflow:hidden clips on ancestors just by looking at the frame.
       // So we'll do a little hand holding and pass the clip instead of the
       // visible rect for the two important cases.
       nscolor uniformColor = NS_RGBA(0,0,0,0);
       nscolor* uniformColorPtr = (mayDrawOutOfOrder || IsInInactiveLayer()) ? nullptr :
                                                                               &uniformColor;
       nsIntRect clipRectUntyped;
-      const DisplayItemClip& layerClip = shouldFixToViewport ? fixedToViewportClip : itemClip;
-      ParentLayerIntRect layerClipRect;
       nsIntRect* clipPtr = nullptr;
-      if (layerClip.HasClip()) {
-        layerClipRect = ViewAs<ParentLayerPixel>(
-          ScaleToNearestPixels(layerClip.GetClipRect()) + mParameters.mOffset);
-        clipRectUntyped = layerClipRect.ToUnknownRect();
+      if (itemClip.HasClip()) {
+        clipRectUntyped = clipRect.ToUnknownRect();
         clipPtr = &clipRectUntyped;
       }
-      if (*animatedGeometryRoot == item->Frame() &&
-          *animatedGeometryRoot != mBuilder->RootReferenceFrame()) {
+
+      bool hasScrolledClip = layerClipChain && layerClipChain->mClip.HasClip() &&
+        !ActiveScrolledRoot::IsAncestor(layerClipChain->mASR, itemASR);
+
+      if (hasScrolledClip) {
+        // If the clip is scrolled, reserve just the area of the clip for
+        // layerization, so that elements outside the clip can still merge
+        // into the same layer.
+        const ActiveScrolledRoot* clipASR = layerClipChain->mASR;
+        AnimatedGeometryRoot* clipAGR = mBuilder->AnimatedGeometryRootForASR(clipASR);
+        nsIntRect scrolledClipRect =
+          ScaleToNearestPixels(layerClipChain->mClip.GetClipRect()) + mParameters.mOffset;
+        mPaintedLayerDataTree.AddingOwnLayer(clipAGR,
+                                             &scrolledClipRect,
+                                             uniformColorPtr);
+      } else if (item->ShouldFixToViewport(mBuilder) && itemClip.HasClip() &&
+                 item->AnimatedGeometryRootForScrollMetadata() != animatedGeometryRoot) {
+        // This is basically the same as the case above, but for the non-APZ
+        // case. At the moment, when APZ is off, there is only the root ASR
+        // (because scroll frames without display ports don't create ASRs) and
+        // the whole clip chain is always just one fused clip.
+        AnimatedGeometryRoot* clipAGR = item->AnimatedGeometryRootForScrollMetadata();
+        nsIntRect scrolledClipRect =
+          ScaleToNearestPixels(itemClip.GetClipRect()) + mParameters.mOffset;
+        mPaintedLayerDataTree.AddingOwnLayer(clipAGR,
+                                             &scrolledClipRect,
+                                             uniformColorPtr);
+      } else if (*animatedGeometryRoot == item->Frame() &&
+                 *animatedGeometryRoot != mBuilder->RootReferenceFrame()) {
         // This is the case for scrollbar thumbs, for example. In that case the
         // clip we care about is the overflow:hidden clip on the scrollbar.
         mPaintedLayerDataTree.AddingOwnLayer(animatedGeometryRoot->mParentAGR,
                                              clipPtr,
                                              uniformColorPtr);
       } else if (prerenderedTransform) {
         mPaintedLayerDataTree.AddingOwnLayer(animatedGeometryRoot,
                                              clipPtr,
                                              uniformColorPtr);
-      } else if (shouldFixToViewport) {
-        mPaintedLayerDataTree.AddingOwnLayer(animatedGeometryRootForClip,
-                                             clipPtr,
-                                             uniformColorPtr);
       } else {
         // Using itemVisibleRect here isn't perfect. itemVisibleRect can be
         // larger or smaller than the potential bounds of item's contents in
         // animatedGeometryRoot: It's too large if there's a clipped display
         // port somewhere among item's contents (see bug 1147673), and it can
         // be too small if the contents can move, because it only looks at the
         // contents' current bounds and doesn't anticipate any animations.
         // Time will tell whether this is good enough, or whether we need to do
         // something more sophisticated here.
         mPaintedLayerDataTree.AddingOwnLayer(animatedGeometryRoot,
                                              &itemVisibleRect, uniformColorPtr);
       }
 
       ContainerLayerParameters params = mParameters;
       params.mBackgroundColor = uniformColor;
       params.mLayerCreationHint = GetLayerCreationHint(animatedGeometryRoot);
-      params.mScrollClip = agrScrollClip;
-      params.mScrollClipForPerspectiveChild = nullptr;
+      params.mScrollMetadataASR = ActiveScrolledRoot::PickDescendant(mContainerScrollMetadataASR, scrollMetadataASR);
+      params.mCompositorASR = params.mScrollMetadataASR != mContainerScrollMetadataASR
+                                ? params.mScrollMetadataASR
+                                : mContainerCompositorASR;
+      if (itemType == nsDisplayItem::TYPE_FIXED_POSITION) {
+        params.mCompositorASR = itemASR;
+      }
 
       if (itemType == nsDisplayItem::TYPE_PERSPECTIVE) {
         // Perspective items have a single child item, an nsDisplayTransform.
         // If the perspective item is scrolled, but the perspective-inducing
-        // frame is outside the scroll frame (indicated by this items AGR
+        // frame is outside the scroll frame (indicated by item->Frame()
         // being outside that scroll frame), we have to take special care to
         // make APZ scrolling work properly. APZ needs us to put the scroll
         // frame's FrameMetrics on our child transform ContainerLayer instead.
-        // Our agrScrollClip is the scroll clip that's applicable to our
-        // perspective frame, so it won't be the scroll clip for the scrolled
-        // frame in the case that we care about, and we'll forward that scroll
-        // clip to our child.
-        params.mScrollClipForPerspectiveChild = itemScrollClip;
+        // It's worth investigating whether this ASR adjustment can be done at
+        // display item creation time.
+        scrollMetadataASR = GetASRForPerspective(scrollMetadataASR, item->Frame());
+        params.mScrollMetadataASR = scrollMetadataASR;
+        itemASR = scrollMetadataASR;
       }
 
       // Just use its layer.
       // Set layerContentsVisibleRect.width/height to -1 to indicate we
       // currently don't know. If BuildContainerLayerFor gets called by
       // item->BuildLayer, this will be set to a proper rect.
       nsIntRect layerContentsVisibleRect(0, 0, -1, -1);
       params.mLayerContentsVisibleRect = &layerContentsVisibleRect;
@@ -4302,47 +4266,48 @@ ContainerState::ProcessDisplayItems(nsDi
         ownLayer->SetPostScale(mParameters.mXScale,
                                mParameters.mYScale);
       }
 
       // Update that layer's clip and visible rects.
       NS_ASSERTION(ownLayer->Manager() == mManager, "Wrong manager");
       NS_ASSERTION(!ownLayer->HasUserData(&gLayerManagerUserData),
                    "We shouldn't have a FrameLayerBuilder-managed layer here!");
-      NS_ASSERTION(layerClip.HasClip() ||
-                   layerClip.GetRoundedRectCount() == 0,
+      NS_ASSERTION(itemClip.HasClip() ||
+                   itemClip.GetRoundedRectCount() == 0,
                    "If we have rounded rects, we must have a clip rect");
 
       // It has its own layer. Update that layer's clip and visible rects.
       ownLayer->SetClipRect(Nothing());
       ownLayer->SetScrolledClip(Nothing());
-      if (layerClip.HasClip()) {
-        // For layers fixed to the viewport, the clip becomes part of the
-        // layer's scrolled clip. Otherwise, it becomes part of the layer clip.
-        if (shouldFixToViewport) {
-          LayerClip scrolledClip;
-          scrolledClip.SetClipRect(layerClipRect);
-          if (layerClip.GetRoundedRectCount() > 0) {
-            scrolledClip.SetMaskLayerIndex(
-                SetupMaskLayerForScrolledClip(ownLayer.get(), layerClip));
-          }
-          ownLayer->SetScrolledClip(Some(scrolledClip));
-        } else {
-          ownLayer->SetClipRect(Some(layerClipRect));
-
-          // rounded rectangle clipping using mask layers
-          // (must be done after visible rect is set on layer)
-          if (layerClip.GetRoundedRectCount() > 0) {
-            SetupMaskLayer(ownLayer, layerClip);
-          }
+      ownLayer->SetAncestorMaskLayers({});
+      if (itemClip.HasClip()) {
+        ownLayer->SetClipRect(Some(clipRect));
+
+        // rounded rectangle clipping using mask layers
+        // (must be done after visible rect is set on layer)
+        if (itemClip.GetRoundedRectCount() > 0) {
+          SetupMaskLayer(ownLayer, itemClip);
         }
       }
 
+      if (hasScrolledClip) {
+        const DisplayItemClip& scrolledClip = layerClipChain->mClip;
+        LayerClip scrolledLayerClip;
+        scrolledLayerClip.SetClipRect(ViewAs<ParentLayerPixel>(
+          ScaleToNearestPixels(scrolledClip.GetClipRect()) + mParameters.mOffset));
+        if (scrolledClip.GetRoundedRectCount() > 0) {
+          scrolledLayerClip.SetMaskLayerIndex(
+              SetupMaskLayerForScrolledClip(ownLayer.get(), scrolledClip));
+        }
+        ownLayer->SetScrolledClip(Some(scrolledLayerClip));
+      }
+
       if (item->GetType() == nsDisplayItem::TYPE_MASK) {
-        MOZ_ASSERT(layerClip.GetRoundedRectCount() == 0);
+        MOZ_ASSERT(itemClip.GetRoundedRectCount() == 0);
 
         nsDisplayMask* maskItem = static_cast<nsDisplayMask*>(item);
         SetupMaskLayerForCSSMask(ownLayer, maskItem);
 
         nsDisplayItem* next = aList->GetBottom();
         if (next && next->GetType() == nsDisplayItem::TYPE_SCROLL_INFO_LAYER) {
           // Since we do build a layer for mask, there is no need for this
           // scroll info layer anymore.
@@ -4364,18 +4329,25 @@ ContainerState::ProcessDisplayItems(nsDi
         oldContainer->RemoveChild(ownLayer);
       }
       NS_ASSERTION(FindIndexOfLayerIn(mNewChildLayers, ownLayer) < 0,
                    "Layer already in list???");
 
       NewLayerEntry* newLayerEntry = mNewChildLayers.AppendElement();
       newLayerEntry->mLayer = ownLayer;
       newLayerEntry->mAnimatedGeometryRoot = animatedGeometryRoot;
-      newLayerEntry->mScrollClip = agrScrollClip;
+      newLayerEntry->mASR = itemASR;
+      newLayerEntry->mScrollMetadataASR = scrollMetadataASR;
+      newLayerEntry->mClipChain = layerClipChain;
       newLayerEntry->mLayerState = layerState;
+      if (itemType == nsDisplayItem::TYPE_FIXED_POSITION) {
+        newLayerEntry->mIsFixedToRootScrollFrame =
+          item->Frame()->StyleDisplay()->mPosition == NS_STYLE_POSITION_FIXED &&
+          nsLayoutUtils::IsReallyFixedPos(item->Frame());
+      }
 
       // Don't attempt to flatten compnent alpha layers that are within
       // a forced active layer, or an active transform;
       if (itemType == nsDisplayItem::TYPE_TRANSFORM ||
           layerState == LAYER_ACTIVE_FORCE) {
         newLayerEntry->mPropagateComponentAlphaFlattening = false;
       }
       // nsDisplayTransform::BuildLayer must set layerContentsVisibleRect.
@@ -4395,17 +4367,17 @@ ContainerState::ProcessDisplayItems(nsDi
           // to avoid failure caused by singular transforms.
           newLayerEntry->mUntransformedVisibleRegion = true;
           newLayerEntry->mVisibleRegion =
             item->GetVisibleRectForChildren().ToOutsidePixels(mAppUnitsPerDevPixel);
         } else {
           newLayerEntry->mVisibleRegion = itemVisibleRegion;
         }
         newLayerEntry->mOpaqueRegion = ComputeOpaqueRect(item,
-          animatedGeometryRoot, layerClip, aList,
+          animatedGeometryRoot, itemClip, aList,
           &newLayerEntry->mHideAllLayersBelow,
           &newLayerEntry->mOpaqueForAnimatedGeometryRootParent);
       } else {
         bool useChildrenVisible =
           itemType == nsDisplayItem::TYPE_TRANSFORM &&
           (item->Frame()->IsPreserve3DLeaf() ||
            item->Frame()->HasPerspective());
         const nsIntRegion &visible = useChildrenVisible ?
@@ -4432,22 +4404,22 @@ ContainerState::ProcessDisplayItems(nsDi
 
       /**
        * No need to allocate geometry for items that aren't
        * part of a PaintedLayer.
        */
       mLayerBuilder->AddLayerDisplayItem(ownLayer, item, layerState, nullptr);
     } else {
       PaintedLayerData* paintedLayerData =
-        mPaintedLayerDataTree.FindPaintedLayerFor(animatedGeometryRoot, agrScrollClip,
+        mPaintedLayerDataTree.FindPaintedLayerFor(animatedGeometryRoot, itemASR, layerClipChain,
                                                   itemVisibleRect,
                                                   item->Frame()->In3DContextAndBackfaceIsHidden(),
                                                   [&]() {
-          return NewPaintedLayerData(item, animatedGeometryRoot, agrScrollClip,
-                                     topLeft, shouldFixToViewport);
+          return NewPaintedLayerData(item, animatedGeometryRoot, itemASR, layerClipChain, scrollMetadataASR,
+                                     topLeft);
         });
 
       if (itemType == nsDisplayItem::TYPE_LAYER_EVENT_REGIONS) {
         nsDisplayLayerEventRegions* eventRegions =
             static_cast<nsDisplayLayerEventRegions*>(item);
         paintedLayerData->AccumulateEventRegions(this, eventRegions);
       } else {
         // check to see if the new item has rounded rect clips in common with
@@ -4459,23 +4431,16 @@ ContainerState::ProcessDisplayItems(nsDi
             animatedGeometryRoot, itemClip, aList,
             &paintedLayerData->mHideAllLayersBelow,
             &paintedLayerData->mOpaqueForAnimatedGeometryRootParent);
         MOZ_ASSERT(nsIntRegion(itemDrawRect).Contains(opaquePixels));
         opaquePixels.AndWith(itemVisibleRect);
         paintedLayerData->Accumulate(this, item, opaquePixels,
             itemVisibleRect, itemClip, layerState);
 
-        // If we removed the clip from the display item above because it's
-        // fixed to the viewport, save it on the PaintedLayerData so we can
-        // set it on the layer later.
-        if (fixedToViewportClip.HasClip()) {
-          paintedLayerData->mItemClip = fixedToViewportClip;
-        }
-
         if (!paintedLayerData->mLayer) {
           // Try to recycle the old layer of this display item.
           RefPtr<PaintedLayer> layer =
             AttemptToRecyclePaintedLayer(animatedGeometryRoot, item, topLeft);
           if (layer) {
             paintedLayerData->mLayer = layer;
 
             NS_ASSERTION(FindIndexOfLayerIn(mNewChildLayers, layer) < 0,
@@ -4955,61 +4920,187 @@ FindOpaqueRegionEntry(nsTArray<OpaqueReg
     OpaqueRegionEntry* d = &aEntries[i];
     if (d->mAnimatedGeometryRoot == aAnimatedGeometryRoot) {
       return d;
     }
   }
   return nullptr;
 }
 
+const ActiveScrolledRoot*
+FindDirectChildASR(const ActiveScrolledRoot* aParent, const ActiveScrolledRoot* aDescendant)
+{
+  MOZ_ASSERT(aDescendant, "can't start at the root when looking for a child");
+  MOZ_ASSERT(ActiveScrolledRoot::IsAncestor(aParent, aDescendant));
+  const ActiveScrolledRoot* directChild = aDescendant;
+  while (directChild->mParent != aParent) {
+    directChild = directChild->mParent;
+    MOZ_RELEASE_ASSERT(directChild, "this must not be null");
+  }
+  return directChild;
+}
+
+static FrameMetrics::ViewID
+ViewIDForASR(const ActiveScrolledRoot* aASR)
+{
+  nsIContent* content = aASR->mScrollableFrame->GetScrolledFrame()->GetContent();
+  return nsLayoutUtils::FindOrCreateIDFor(content);
+}
+
+static void
+FixUpFixedPositionLayer(Layer* aLayer,
+                        const ActiveScrolledRoot* aTargetASR,
+                        const ActiveScrolledRoot* aLeafScrollMetadataASR,
+                        const ActiveScrolledRoot* aContainerScrollMetadataASR,
+                        const ActiveScrolledRoot* aContainerCompositorASR,
+                        bool aIsFixedToRootScrollFrame)
+{
+  if (!aLayer->GetIsFixedPosition()) {
+    return;
+  }
+
+  // Analyze ASRs to figure out if we need to fix up fixedness annotations on
+  // the layer. Fixed annotations are required in multiple cases:
+  //  - Sometimes we set scroll metadata on a layer for a scroll frame that we
+  //    don't want the layer to be moved by. (We have to do this if there is a
+  //    scrolled clip that is moved by that scroll frame.) So we set the fixed
+  //    annotation so that the compositor knows that it should ignore that
+  //    scroll metadata when determining the layer's position.
+  //  - Sometimes there is a scroll meta data on aLayer's parent layer for a
+  //    scroll frame that we don't want aLayer to be moved by. The most common
+  //    way for this to happen is with containerful root scrolling, where the
+  //    scroll metadata for the root scroll frame is on a container layer that
+  //    wraps the whole document's contents.
+  //  - Sometimes it's just needed for hit testing, i.e. figuring out what
+  //    scroll frame should be scrolled by events over the layer.
+  // A fixed layer needs to be annotated with the scroll ID of the scroll frame
+  // that it is *fixed with respect to*, i.e. the outermost scroll frame which
+  // does not move the layer. nsDisplayFixedPosition only ever annotates layers
+  // with the scroll ID of the presshell's root scroll frame, which is
+  // sometimes the wrong thing to do, so we correct it here. Specifically,
+  // it's the wrong thing to do if the fixed frame's containing block is a
+  // transformed frame - in that case, the fixed frame needs to scroll along
+  // with the transformed frame instead of being fixed with respect to the rsf.
+  // (It would be nice to compute the annotation only in one place and get it
+  // right, instead of fixing it up after the fact like this, but this will
+  // need to do for now.)
+  // compositorASR is the ASR that the layer would move with on the compositor
+  // if there were no fixed annotation on it.
+  const ActiveScrolledRoot* compositorASR =
+    aLeafScrollMetadataASR == aContainerScrollMetadataASR
+      ? aContainerCompositorASR
+      : aLeafScrollMetadataASR;
+
+  // The goal of the annotation is to have the layer move with aTargetASR.
+  if (compositorASR && aTargetASR != compositorASR) {
+    // Mark this layer as fixed with respect to the child scroll frame of aTargetASR.
+    aLayer->SetFixedPositionData(
+      ViewIDForASR(FindDirectChildASR(aTargetASR, compositorASR)),
+      aLayer->GetFixedPositionAnchor(),
+      aLayer->GetFixedPositionSides());
+  } else {
+    // Remove the fixed annotation from the layer, unless this layers is fixed
+    // to the document's root scroll frame - in that case, the annotation is
+    // needed for hit testing, because fixed layers in iframes should scroll
+    // the iframe, even though their position is not affected by scrolling in
+    // the iframe. (The APZ hit testing code has a special case for this.)
+    // nsDisplayFixedPosition has annotated this layer with the document's
+    // root scroll frame's scroll id.
+    aLayer->SetIsFixedPosition(aIsFixedToRootScrollFrame);
+  }
+}
+
 void
 ContainerState::SetupScrollingMetadata(NewLayerEntry* aEntry)
 {
   if (mFlattenToSingleLayer) {
     // animated geometry roots are forced to all match, so we can't
     // use them and we don't get async scrolling.
     return;
   }
 
   if (!mBuilder->IsPaintingToWindow()) {
     // async scrolling not possible, and async scrolling info not computed
     // for this paint.
     return;
   }
 
+  const ActiveScrolledRoot* startASR = aEntry->mScrollMetadataASR;
+  const ActiveScrolledRoot* stopASR = mContainerScrollMetadataASR;
+  if (!ActiveScrolledRoot::IsAncestor(stopASR, startASR)) {
+    if (ActiveScrolledRoot::IsAncestor(startASR, stopASR)) {
+      // startASR and stopASR are in the same branch of the ASR tree, but
+      // startASR is closer to the root. Just start at stopASR so that the loop
+      // below doesn't actually do anything.
+      startASR = stopASR;
+    } else {
+      // startASR and stopASR are in different branches of the
+      // ASR tree. Find a common ancestor and make that the stopASR.
+      // This can happen when there's a scrollable frame inside a fixed layer
+      // which has a scrolled clip. As far as scroll metadata is concerned,
+      // the scroll frame's scroll metadata will be a child of the scroll ID
+      // that scrolls the clip on the fixed layer. But as far as ASRs are
+      // concerned, those two ASRs are siblings, parented to the ASR of the
+      // fixed layer.
+      do {
+        stopASR = stopASR->mParent;
+      } while (!ActiveScrolledRoot::IsAncestor(stopASR, startASR));
+    }
+  }
+
+  FixUpFixedPositionLayer(aEntry->mLayer, aEntry->mASR, startASR,
+                          mContainerScrollMetadataASR, mContainerCompositorASR,
+                          aEntry->mIsFixedToRootScrollFrame);
+
   AutoTArray<ScrollMetadata,2> metricsArray;
   if (aEntry->mBaseScrollMetadata) {
     metricsArray.AppendElement(*aEntry->mBaseScrollMetadata);
 
     // The base FrameMetrics was not computed by the nsIScrollableframe, so it
     // should not have a mask layer.
     MOZ_ASSERT(!aEntry->mBaseScrollMetadata->HasMaskLayer());
   }
 
   // Any extra mask layers we need to attach to ScrollMetadatas.
   // The list may already contain an entry added for the layer's scrolled clip
   // so add to it rather than overwriting it (we clear the list when recycling
   // a layer).
   nsTArray<RefPtr<Layer>> maskLayers(aEntry->mLayer->GetAllAncestorMaskLayers());
 
-  for (const DisplayItemScrollClip* scrollClip = aEntry->mScrollClip;
-       scrollClip && scrollClip != mContainerScrollClip;
-       scrollClip = scrollClip->mParent) {
-    if (!scrollClip->mIsAsyncScrollable) {
-      // This scroll clip was created for a scroll frame that didn't know
-      // whether it needs to be async scrollable for scroll handoff. It was
-      // not activated, so we don't need to create a frame metrics for it.
-      continue;
+  // Iterate over the ASR chain and create the corresponding scroll metadatas.
+  // This loop is slightly tricky because the scrollframe-to-clip relationship
+  // is reversed between DisplayItemClipChain and ScrollMetadata:
+  //  - DisplayItemClipChain associates the clip with the scroll frame that
+  //    this clip is *moved by*, i.e. the clip is moving inside the scroll
+  //    frame.
+  //  - ScrollMetaData associates the scroll frame with the clip that's
+  //    *just outside* the scroll frame, i.e. not moved by the scroll frame
+  //    itself.
+  // This discrepancy means that the leaf clip item of the clip chain is never
+  // applied to any scroll meta data. Instead, it was applied earlier as the
+  // layer's clip (or fused with the painted layer contents), or it was applied
+  // as a ScrolledClip on the layer.
+  const DisplayItemClipChain* clipChain = aEntry->mClipChain;
+
+  for (const ActiveScrolledRoot* asr = startASR; asr != stopASR; asr = asr->mParent) {
+    if (!asr) {
+      MOZ_ASSERT_UNREACHABLE("Should have encountered stopASR on the way up.");
+      break;
     }
-
-    nsIScrollableFrame* scrollFrame = scrollClip->mScrollableFrame;
-    const DisplayItemClip* clip = scrollClip->mClip;
+    if (clipChain && clipChain->mASR == asr) {
+      clipChain = clipChain->mParent;
+    }
+
+    nsIScrollableFrame* scrollFrame = asr->mScrollableFrame;
+    const DisplayItemClip* clip =
+      (clipChain && clipChain->mASR == asr->mParent) ? &clipChain->mClip : nullptr;
 
     Maybe<ScrollMetadata> metadata =
-      scrollFrame->ComputeScrollMetadata(aEntry->mLayer, mContainerReferenceFrame, mParameters, clip);
+      scrollFrame->ComputeScrollMetadata(aEntry->mLayer, mContainerReferenceFrame,
+                                         mParameters, clip);
     if (!metadata) {
       continue;
     }
 
     if (clip &&
         clip->HasClip() &&
         clip->GetRoundedRectCount() > 0)
     {
@@ -5095,19 +5186,19 @@ ContainerState::PostprocessRetainedLayer
         data->mAnimatedGeometryRoot = animatedGeometryRootToCover;
       }
 
       nsIntRegion clippedOpaque = e->mOpaqueRegion;
       Maybe<ParentLayerIntRect> clipRect = e->mLayer->GetCombinedClipRect();
       if (clipRect) {
         clippedOpaque.AndWith(clipRect->ToUnknownRect());
       }
-      if (e->mLayer->GetIsFixedPosition() && e->mLayer->GetScrolledClip()) {
+      if (e->mLayer->GetScrolledClip()) {
         // The clip can move asynchronously, so we can't rely on opaque parts
-        // staying in the same place.
+        // staying visible.
         clippedOpaque.SetEmpty();
       } else if (e->mHideAllLayersBelow) {
         hideAll = true;
       }
       data->mOpaqueRegion.Or(data->mOpaqueRegion, clippedOpaque);
     }
 
     if (e->mLayer->GetType() == Layer::TYPE_READBACK) {
@@ -5472,20 +5563,22 @@ FrameLayerBuilder::BuildContainerLayerFo
     // Empty layers only have metadata and should never have display items. We
     // early exit because later, invalidation will walk up the frame tree to
     // determine which painted layer gets invalidated. Since an empty layer
     // should never have anything to paint, it should never be invalidated.
     NS_ASSERTION(aChildren->IsEmpty(), "Should have no children");
     return containerLayer.forget();
   }
 
-  const DisplayItemScrollClip* containerScrollClip = aParameters.mScrollClip;
+  const ActiveScrolledRoot* containerASR = aContainerItem ? aContainerItem->GetActiveScrolledRoot() : nullptr;
+  const ActiveScrolledRoot* containerScrollMetadataASR = aParameters.mScrollMetadataASR;
+  const ActiveScrolledRoot* containerCompositorASR = aParameters.mCompositorASR;
 
   ContainerLayerParameters scaleParameters;
-  nsRect bounds = aChildren->GetScrollClippedBoundsUpTo(aBuilder, containerScrollClip);
+  nsRect bounds = aChildren->GetClippedBoundsWithRespectToASR(aBuilder, containerASR);
   nsRect childrenVisible =
       aContainerItem ? aContainerItem->GetVisibleRectForChildren() :
           aContainerFrame->GetVisualOverflowRectRelativeToSelf();
   if (!ChooseScaleAndSetTransform(this, aBuilder, aContainerFrame,
                                   aContainerItem,
                                   bounds.Intersect(childrenVisible),
                                   aTransform, aParameters,
                                   containerLayer, state, scaleParameters)) {
@@ -5522,17 +5615,18 @@ FrameLayerBuilder::BuildContainerLayerFo
     backgroundColor = aParameters.mBackgroundColor;
   }
 
   uint32_t flags;
   while (true) {
     ContainerState state(aBuilder, aManager, aManager->GetLayerBuilder(),
                          aContainerFrame, aContainerItem, bounds,
                          containerLayer, scaleParameters, flattenToSingleLayer,
-                         backgroundColor, containerScrollClip);
+                         backgroundColor, containerASR, containerScrollMetadataASR,
+                         containerCompositorASR);
 
     state.ProcessDisplayItems(aChildren);
 
     // Set CONTENT_COMPONENT_ALPHA if any of our children have it.
     // This is suboptimal ... a child could have text that's over transparent
     // pixels in its own layer, but over opaque parts of previous siblings.
     bool hasComponentAlphaChildren = false;
     bool mayFlatten =
--- a/layout/painting/FrameLayerBuilder.h
+++ b/layout/painting/FrameLayerBuilder.h
@@ -22,17 +22,18 @@
 class nsDisplayListBuilder;
 class nsDisplayList;
 class nsDisplayItem;
 class gfxContext;
 class nsDisplayItemGeometry;
 class nsDisplayMask;
 
 namespace mozilla {
-class DisplayItemScrollClip;
+struct ActiveScrolledRoot;
+struct DisplayItemClipChain;
 namespace layers {
 class ContainerLayer;
 class LayerManager;
 class BasicLayerManager;
 class PaintedLayer;
 class ImageLayer;
 } // namespace layers
 
@@ -53,49 +54,49 @@ public:
 };
 
 struct ContainerLayerParameters {
   ContainerLayerParameters()
     : mXScale(1)
     , mYScale(1)
     , mLayerContentsVisibleRect(nullptr)
     , mBackgroundColor(NS_RGBA(0,0,0,0))
-    , mScrollClip(nullptr)
-    , mScrollClipForPerspectiveChild(nullptr)
+    , mScrollMetadataASR(nullptr)
+    , mCompositorASR(nullptr)
     , mInTransformedSubtree(false)
     , mInActiveTransformedSubtree(false)
     , mDisableSubpixelAntialiasingInDescendants(false)
     , mInLowPrecisionDisplayPort(false)
     , mForEventsAndPluginsOnly(false)
     , mLayerCreationHint(layers::LayerManager::NONE)
   {}
   ContainerLayerParameters(float aXScale, float aYScale)
     : mXScale(aXScale)
     , mYScale(aYScale)
     , mLayerContentsVisibleRect(nullptr)
     , mBackgroundColor(NS_RGBA(0,0,0,0))
-    , mScrollClip(nullptr)
-    , mScrollClipForPerspectiveChild(nullptr)
+    , mScrollMetadataASR(nullptr)
+    , mCompositorASR(nullptr)
     , mInTransformedSubtree(false)
     , mInActiveTransformedSubtree(false)
     , mDisableSubpixelAntialiasingInDescendants(false)
     , mInLowPrecisionDisplayPort(false)
     , mForEventsAndPluginsOnly(false)
     , mLayerCreationHint(layers::LayerManager::NONE)
   {}
   ContainerLayerParameters(float aXScale, float aYScale,
                            const nsIntPoint& aOffset,
                            const ContainerLayerParameters& aParent)
     : mXScale(aXScale)
     , mYScale(aYScale)
     , mLayerContentsVisibleRect(nullptr)
     , mOffset(aOffset)
     , mBackgroundColor(aParent.mBackgroundColor)
-    , mScrollClip(aParent.mScrollClip)
-    , mScrollClipForPerspectiveChild(aParent.mScrollClipForPerspectiveChild)
+    , mScrollMetadataASR(aParent.mScrollMetadataASR)
+    , mCompositorASR(aParent.mCompositorASR)
     , mInTransformedSubtree(aParent.mInTransformedSubtree)
     , mInActiveTransformedSubtree(aParent.mInActiveTransformedSubtree)
     , mDisableSubpixelAntialiasingInDescendants(aParent.mDisableSubpixelAntialiasingInDescendants)
     , mInLowPrecisionDisplayPort(aParent.mInLowPrecisionDisplayPort)
     , mForEventsAndPluginsOnly(aParent.mForEventsAndPluginsOnly)
     , mLayerCreationHint(aParent.mLayerCreationHint)
   {}
 
@@ -116,20 +117,18 @@ struct ContainerLayerParameters {
    */
   nsIntPoint mOffset;
 
   LayerIntPoint Offset() const {
     return LayerIntPoint::FromUnknownPoint(mOffset);
   }
 
   nscolor mBackgroundColor;
-  const DisplayItemScrollClip* mScrollClip;
-
-  // usually nullptr, except when building children of an nsDisplayPerspective
-  const DisplayItemScrollClip* mScrollClipForPerspectiveChild;
+  const ActiveScrolledRoot* mScrollMetadataASR;
+  const ActiveScrolledRoot* mCompositorASR;
 
   bool mInTransformedSubtree;
   bool mInActiveTransformedSubtree;
   bool mDisableSubpixelAntialiasingInDescendants;
   bool mInLowPrecisionDisplayPort;
   bool mForEventsAndPluginsOnly;
   layers::LayerManager::PaintedLayerCreationHint mLayerCreationHint;
 
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -75,17 +75,16 @@
 #include "mozilla/EventStateManager.h"
 #include "mozilla/RestyleManager.h"
 #include "nsCaret.h"
 #include "nsISelection.h"
 #include "nsDOMTokenList.h"
 #include "mozilla/RuleNodeCacheConditions.h"
 #include "nsCSSProps.h"
 #include "nsPluginFrame.h"
-#include "DisplayItemScrollClip.h"
 #include "nsSVGMaskFrame.h"
 
 // GetCurrentTime is defined in winbase.h as zero argument macro forwarding to
 // GetTickCount().
 #ifdef GetCurrentTime
 #undef GetCurrentTime
 #endif
 
@@ -852,16 +851,17 @@ nsDisplayListBuilder::AutoCurrentActiveS
 
 nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
     nsDisplayListBuilderMode aMode, bool aBuildCaret)
     : mReferenceFrame(aReferenceFrame),
       mIgnoreScrollFrame(nullptr),
       mLayerEventRegions(nullptr),
       mCurrentTableItem(nullptr),
       mCurrentActiveScrolledRoot(nullptr),
+      mCurrentContainerASR(nullptr),
       mCurrentFrame(aReferenceFrame),
       mCurrentReferenceFrame(aReferenceFrame),
       mCurrentAGR(&mRootAGR),
       mRootAGR(aReferenceFrame, nullptr),
       mUsedAGRBudget(0),
       mDirtyRect(-1,-1,-1,-1),
       mGlassDisplayItem(nullptr),
       mScrollInfoItemsForHoisting(nullptr),
@@ -957,16 +957,26 @@ nsDisplayListBuilder::WrapAGRForFrame(ns
     result = new (this) AnimatedGeometryRoot(aAnimatedGeometryRoot, parent);
     mFrameToAnimatedGeometryRootMap.Put(aAnimatedGeometryRoot, 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;
   }
@@ -1032,19 +1042,22 @@ void nsDisplayListBuilder::MarkOutOfFlow
     overflowRect.Inflate(nsPresContext::CSSPixelsToAppUnits(32));
   }
 
   if (!dirty.IntersectRect(dirty, overflowRect) &&
       !(aFrame->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) {
     return;
   }
 
-  const DisplayItemClip* oldClip = mClipState.GetClipForContainingBlockDescendants();
-  const DisplayItemScrollClip* sc = mClipState.GetCurrentInnermostScrollClip();
-  OutOfFlowDisplayData* data = new OutOfFlowDisplayData(oldClip, sc, dirty);
+  // mClipState.GetClipChainForContainingBlockDescendants can return pointers
+  // to objects on the stack, so we need to clone the chain.
+  const DisplayItemClipChain* clipChain =
+    CopyWholeChain(mClipState.GetClipChainForContainingBlockDescendants());
+  const ActiveScrolledRoot* asr = mCurrentActiveScrolledRoot;
+  OutOfFlowDisplayData* data = new OutOfFlowDisplayData(clipChain, asr, dirty);
   aFrame->Properties().Set(nsDisplayListBuilder::OutOfFlowDisplayDataProperty(), data);
 
   MarkFrameForDisplay(aFrame, aDirtyFrame);
 }
 
 static void UnmarkFrameForDisplay(nsIFrame* aFrame) {
   nsPresContext* presContext = aFrame->PresContext();
   presContext->PropertyTable()->
@@ -1062,22 +1075,16 @@ nsDisplayListBuilder::~nsDisplayListBuil
   NS_ASSERTION(mFramesMarkedForDisplay.Length() == 0,
                "All frames should have been unmarked");
   NS_ASSERTION(mPresShellStates.Length() == 0,
                "All presshells should have been exited");
   NS_ASSERTION(!mCurrentTableItem, "No table item should be active");
 
   nsCSSRendering::EndFrameTreesLocked();
 
-  for (DisplayItemClip* c : mDisplayItemClipsToDestroy) {
-    c->DisplayItemClip::~DisplayItemClip();
-  }
-  for (DisplayItemScrollClip* c : mScrollClipsToDestroy) {
-    c->DisplayItemScrollClip::~DisplayItemScrollClip();
-  }
   for (ActiveScrolledRoot* asr : mActiveScrolledRoots) {
     asr->ActiveScrolledRoot::~ActiveScrolledRoot();
   }
   for (DisplayItemClipChain* c : mClipChainsToDestroy) {
     c->DisplayItemClipChain::~DisplayItemClipChain();
   }
 
   PL_FinishArenaPool(&mPool);
@@ -1250,16 +1257,33 @@ nsDisplayListBuilder::MarkFramesForDispl
         if (classList->Contains(NS_LITERAL_STRING("moz-accessiblecaret"))) {
           continue;
         }
       }
     }
     mFramesMarkedForDisplay.AppendElement(e);
     MarkOutOfFlowFrameForDisplay(aDirtyFrame, e, aDirtyRect);
   }
+
+  if (!aDirtyFrame->GetParent()) {
+    // This is the viewport frame of aDirtyFrame's presshell.
+    // Store the current display data so that it can be used for fixed
+    // background images.
+    NS_ASSERTION(CurrentPresShellState()->mPresShell ==
+        aDirtyFrame->PresContext()->PresShell(),
+        "Presshell mismatch");
+    MOZ_ASSERT(!CurrentPresShellState()->mFixedBackgroundDisplayData,
+               "already traversed this presshell's root frame?");
+
+    const DisplayItemClipChain* clipChain =
+      CopyWholeChain(mClipState.GetClipChainForContainingBlockDescendants());
+    const ActiveScrolledRoot* asr = mCurrentActiveScrolledRoot;
+    CurrentPresShellState()->mFixedBackgroundDisplayData.emplace(
+      clipChain, asr, aDirtyRect);
+  }
 }
 
 /**
  * Mark all preserve-3d children with
  * NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO to make sure
  * nsFrame::BuildDisplayListForChild() would visit them.  Also compute
  * dirty rect for preserve-3d children.
  *
@@ -1372,44 +1396,16 @@ nsDisplayListBuilder::CreateClipChainInt
 }
 
 const DisplayItemClipChain*
 nsDisplayListBuilder::CopyWholeChain(const DisplayItemClipChain* aClipChain)
 {
   return CreateClipChainIntersection(nullptr, aClipChain, nullptr);
 }
 
-const DisplayItemClip*
-nsDisplayListBuilder::AllocateDisplayItemClip(const DisplayItemClip& aOriginal)
-{
-  void* p = Allocate(sizeof(DisplayItemClip));
-  if (!aOriginal.GetRoundedRectCount()) {
-    memcpy(p, &aOriginal, sizeof(DisplayItemClip));
-    return static_cast<DisplayItemClip*>(p);
-  }
-
-  DisplayItemClip* c = new (KnownNotNull, p) DisplayItemClip(aOriginal);
-  mDisplayItemClipsToDestroy.AppendElement(c);
-  return c;
-}
-
-DisplayItemScrollClip*
-nsDisplayListBuilder::AllocateDisplayItemScrollClip(const DisplayItemScrollClip* aParent,
-                                                    nsIScrollableFrame* aScrollableFrame,
-                                                    const DisplayItemClip* aClip,
-                                                    bool aIsAsyncScrollable)
-{
-  void* p = Allocate(sizeof(DisplayItemScrollClip));
-  DisplayItemScrollClip* c =
-    new (KnownNotNull, p) DisplayItemScrollClip(aParent, aScrollableFrame,
-                                                aClip, aIsAsyncScrollable);
-  mScrollClipsToDestroy.AppendElement(c);
-  return c;
-}
-
 const nsIFrame*
 nsDisplayListBuilder::FindReferenceFrameFor(const nsIFrame *aFrame,
                                             nsPoint* aOffset)
 {
   if (aFrame == mCurrentFrame) {
     if (aOffset) {
       *aOffset = mCurrentOffsetToReferenceFrame;
     }
@@ -1554,16 +1550,27 @@ nsDisplayListBuilder::RecomputeCurrentAn
         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;
+}
+
 void
 nsDisplayListBuilder::AdjustWindowDraggingRegion(nsIFrame* aFrame)
 {
   if (!mWindowDraggingAllowed || !IsForPainting()) {
     return;
   }
 
   const nsStyleUIReset* styleUI = aFrame->StyleUIReset();
@@ -1605,20 +1612,18 @@ nsDisplayListBuilder::AdjustWindowDraggi
   // should not be allowed to interfere with the window dragging region. Using
   // just the current DisplayItemClip is not enough to cover this case
   // completely because clips are reset while building stacking context
   // contents, so for example we'd fail to clip frames that have a clip path
   // applied to them. But the current dirty rect doesn't get reset in that
   // case, so we use it to make this case work.
   nsRect borderBox = aFrame->GetRectRelativeToSelf().Intersect(mDirtyRect);
   borderBox += ToReferenceFrame(aFrame);
-  const DisplayItemClip* clip = ClipState().GetCurrentCombinedClip(this);
-  if (clip) {
-    borderBox = clip->ApplyNonRoundedIntersection(borderBox);
-  }
+  const DisplayItemClipChain* clip = ClipState().GetCurrentCombinedClipChain(this);
+  borderBox = ApplyAllClipNonRoundedIntersection(clip, borderBox);
   if (!borderBox.IsEmpty()) {
     LayoutDeviceRect devPixelBorderBox =
       LayoutDevicePixel::FromAppUnits(borderBox, aFrame->PresContext()->AppUnitsPerDevPixel());
     LayoutDeviceRect transformedDevPixelBorderBox =
       TransformBy(referenceFrameToRootReferenceFrame, devPixelBorderBox);
     transformedDevPixelBorderBox.Round();
     LayoutDeviceIntRect transformedDevPixelBorderBoxInt;
     if (transformedDevPixelBorderBox.ToIntRect(&transformedDevPixelBorderBoxInt)) {
@@ -1823,32 +1828,26 @@ nsDisplayList::GetBounds(nsDisplayListBu
   nsRect bounds;
   for (nsDisplayItem* i = GetBottom(); i != nullptr; i = i->GetAbove()) {
     bounds.UnionRect(bounds, i->GetClippedBounds(aBuilder));
   }
   return bounds;
 }
 
 nsRect
-nsDisplayList::GetScrollClippedBoundsUpTo(nsDisplayListBuilder* aBuilder,
-                                          const DisplayItemScrollClip* aIncludeScrollClipsUpTo) const {
+nsDisplayList::GetClippedBoundsWithRespectToASR(nsDisplayListBuilder* aBuilder,
+                                                const ActiveScrolledRoot* aASR) const {
   nsRect bounds;
   for (nsDisplayItem* i = GetBottom(); i != nullptr; i = i->GetAbove()) {
     nsRect r = i->GetClippedBounds(aBuilder);
-    if (r.IsEmpty()) {
-      continue;
-    }
-    for (auto* sc = i->ScrollClip(); sc && sc != aIncludeScrollClipsUpTo; sc = sc->mParent) {
-      if (sc->mClip && sc->mClip->HasClip()) {
-        if (sc->mIsAsyncScrollable) {
-          // Assume the item can move anywhere in the scroll clip's clip rect.
-          r = sc->mClip->GetClipRect();
-        } else {
-          r = sc->mClip->ApplyNonRoundedIntersection(r);
-        }
+    if (aASR != i->GetActiveScrolledRoot() && !r.IsEmpty()) {
+      const DisplayItemClip* clip = DisplayItemClipChain::ClipForASR(i->GetClipChain(), aASR);
+      MOZ_ASSERT(clip, "Need to be clipped wrt aASR. Do not call this function with an ASR that our child items don't have finite bounds wrt.");
+      if (clip) {
+        r = clip->GetClipRect();
       }
     }
     bounds.UnionRect(bounds, r);
   }
   return bounds;
 }
 
 nsRect
@@ -1862,17 +1861,17 @@ nsDisplayList::GetVisibleRect() const {
 
 bool
 nsDisplayList::ComputeVisibilityForRoot(nsDisplayListBuilder* aBuilder,
                                         nsRegion* aVisibleRegion) {
   PROFILER_LABEL("nsDisplayList", "ComputeVisibilityForRoot",
     js::ProfileEntry::Category::GRAPHICS);
 
   nsRegion r;
-  r.And(*aVisibleRegion, GetBounds(aBuilder));
+  r.And(*aVisibleRegion, GetClippedBoundsWithRespectToASR(aBuilder, nullptr));
   return ComputeVisibilityForSublist(aBuilder, aVisibleRegion, r.GetBounds());
 }
 
 static nsRegion
 TreatAsOpaque(nsDisplayItem* aItem, nsDisplayListBuilder* aBuilder)
 {
   bool snap;
   nsRegion opaque = aItem->GetOpaqueRegion(aBuilder, &snap);
@@ -2528,24 +2527,26 @@ void nsDisplayList::SortByContentOrder(n
   Sort(IsContentLEQ, aCommonAncestor);
 }
 
 void nsDisplayList::Sort(SortLEQ aCmp, void* aClosure) {
   ::Sort(this, Count(), aCmp, aClosure);
 }
 
 nsDisplayItem::nsDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
- : nsDisplayItem(aBuilder, aFrame, aBuilder->ClipState().GetCurrentInnermostScrollClip())
+ : nsDisplayItem(aBuilder, aFrame,
+                 aBuilder->CurrentActiveScrolledRoot())
 {}
 
 nsDisplayItem::nsDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
-                             const DisplayItemScrollClip* aScrollClip)
+                             const ActiveScrolledRoot* aActiveScrolledRoot)
   : mFrame(aFrame)
-  , mClip(aBuilder->ClipState().GetCurrentCombinedClip(aBuilder))
-  , mScrollClip(aScrollClip)
+  , mClipChain(aBuilder->ClipState().GetCurrentCombinedClipChain(aBuilder))
+  , mClip(DisplayItemClipChain::ClipForASR(mClipChain, aActiveScrolledRoot))
+  , mActiveScrolledRoot(aActiveScrolledRoot)
   , mAnimatedGeometryRoot(nullptr)
   , mForceNotVisible(aBuilder->IsBuildingInvisibleItems())
 #ifdef MOZ_DUMP_PAINTING
   , mPainted(false)
 #endif
 {
   mReferenceFrame = aBuilder->FindReferenceFrameFor(aFrame, &mToReferenceFrame);
   // This can return the wrong result if the item override ShouldFixToViewport(),
@@ -2627,16 +2628,86 @@ nsDisplayItem::RecomputeVisibility(nsDis
     return false;
   }
 
   nsRegion opaque = TreatAsOpaque(this, aBuilder);
   aBuilder->SubtractFromVisibleRegion(aVisibleRegion, opaque);
   return true;
 }
 
+void
+nsDisplayItem::SetClipChain(const DisplayItemClipChain* aClipChain)
+{
+  mClipChain = aClipChain;
+  mClip = DisplayItemClipChain::ClipForASR(aClipChain, mActiveScrolledRoot);
+}
+
+void
+nsDisplayItem::FuseClipChainUpTo(nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR)
+{
+  const DisplayItemClipChain* sc = mClipChain;
+  DisplayItemClip mergedClip;
+  while (sc && ActiveScrolledRoot::PickDescendant(aASR, sc->mASR) == sc->mASR) {
+    mergedClip.IntersectWith(sc->mClip);
+    sc = sc->mParent;
+  }
+  if (mergedClip.HasClip()) {
+    mClipChain = aBuilder->AllocateDisplayItemClipChain(mergedClip, aASR, sc);
+    mClip = &mClipChain->mClip;
+  } else {
+    mClipChain = nullptr;
+    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)
+{
+  if (!aOther) {
+    return;
+  }
+
+  // aOther might be a reference to a clip on the stack. We need to make sure
+  // that CreateClipChainIntersection will allocate the actual intersected
+  // clip in the builder's arena, so for the mClipChain == nullptr case,
+  // we supply nullptr as the common ancestor so that CreateClipChainIntersection
+  // clones the whole chain.
+  const DisplayItemClipChain* ancestorClip =
+    mClipChain ? FindCommonAncestorClipForIntersection(mClipChain, aOther) : nullptr;
+  SetClipChain(aBuilder->CreateClipChainIntersection(ancestorClip, mClipChain, aOther));
+}
+
 nsRect
 nsDisplayItem::GetClippedBounds(nsDisplayListBuilder* aBuilder)
 {
   bool snap;
   nsRect r = GetBounds(aBuilder, &snap);
   return GetClip().ApplyNonRoundedIntersection(r);
 }
 
@@ -2976,18 +3047,18 @@ nsDisplayBackgroundImage::AppendBackgrou
     return true;
   }
 
   if (!bg) {
     aList->AppendToTop(&bgItemList);
     return false;
   }
 
-  const DisplayItemScrollClip* scrollClip =
-    aBuilder->ClipState().GetCurrentInnermostScrollClip();
+  const ActiveScrolledRoot* asr =
+    aBuilder->CurrentActiveScrolledRoot();
 
   bool needBlendContainer = false;
 
   // Passing bg == nullptr in this macro will result in one iteration with
   // i = 0.
   NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, bg->mImage) {
     if (bg->mImage.mLayers[i].mImage.IsEmpty()) {
       continue;
@@ -2999,41 +3070,77 @@ nsDisplayBackgroundImage::AppendBackgrou
 
     DisplayListClipState::AutoSaveRestore clipState(aBuilder);
     if (!aBuilder->IsForEventDelivery()) {
       const nsStyleImageLayers::Layer& layer = bg->mImage.mLayers[i];
       SetBackgroundClipRegion(clipState, aFrame, toRef,
                               layer, bgRect, willPaintBorder);
     }
 
+    nsDisplayList thisItemList;
     nsDisplayBackgroundImage::InitData bgData =
       nsDisplayBackgroundImage::GetInitData(aBuilder, aFrame, i, bgRect, bg,
                                             LayerizeFixed::DO_NOT_LAYERIZE_FIXED_BACKGROUND_IF_AVOIDING_COMPONENT_ALPHA_LAYERS);
 
-    nsDisplayList thisItemList;
     if (bgData.shouldFixToViewport) {
-      nsDisplayBackgroundImage* bgItem = new (aBuilder) nsDisplayBackgroundImage(bgData);
+
+      auto* displayData = aBuilder->GetCurrentFixedBackgroundDisplayData();
+      nsDisplayListBuilder::AutoBuildingDisplayList
+        buildingDisplayList(aBuilder, aFrame, aBuilder->GetDirtyRect(), false);
+
+      nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter asrSetter(aBuilder);
+      if (displayData) {
+        asrSetter.SetCurrentActiveScrolledRoot(
+          displayData->mContainingBlockActiveScrolledRoot);
+        if (nsLayoutUtils::UsesAsyncScrolling(aFrame)) {
+          // Override the dirty rect on the builder to be the dirty rect of
+          // the viewport.
+          // displayData->mDirtyRect is relative to the presshell's viewport
+          // frame (the root frame), and we need it to be relative to aFrame.
+          nsIFrame* rootFrame = aBuilder->CurrentPresShellState()->mPresShell->GetRootFrame();
+          // There cannot be any transforms between aFrame and rootFrame
+          // because then bgData.shouldFixToViewport would have been false.
+          nsRect dirtyRect = displayData->mDirtyRect + aFrame->GetOffsetTo(rootFrame);
+          buildingDisplayList.SetDirtyRect(dirtyRect);
+        }
+      }
+      nsDisplayBackgroundImage* bgItem = nullptr;
+      {
+        // The clip is captured by the nsDisplayFixedPosition, so clear the
+        // clip for the nsDisplayBackgroundImage inside.
+        DisplayListClipState::AutoSaveRestore bgImageClip(aBuilder);
+        bgImageClip.Clear();
+        bgItem = new (aBuilder) nsDisplayBackgroundImage(bgData);
+      }
       thisItemList.AppendNewToTop(
         nsDisplayFixedPosition::CreateForFixedBackground(aBuilder, aFrame, bgItem, i));
+
     } else {
       thisItemList.AppendNewToTop(new (aBuilder) nsDisplayBackgroundImage(bgData));
     }
 
     if (bg->mImage.mLayers[i].mBlendMode != NS_STYLE_BLEND_NORMAL) {
+      DisplayListClipState::AutoSaveRestore blendClip(aBuilder);
+      blendClip.ClearUpToASR(asr);
+      // asr is scrolled. Even if we wrap a fixed background layer, that's
+      // fine, because the item will have a scrolled clip that limits the
+      // item with respect to asr.
       thisItemList.AppendNewToTop(
         new (aBuilder) nsDisplayBlendMode(aBuilder, aFrame, &thisItemList,
                                           bg->mImage.mLayers[i].mBlendMode,
-                                          scrollClip, i + 1));
+                                          asr, i + 1));
     }
     bgItemList.AppendToTop(&thisItemList);
   }
 
   if (needBlendContainer) {
+    DisplayListClipState::AutoSaveRestore blendContainerClip(aBuilder);
+    blendContainerClip.ClearUpToASR(asr);
     bgItemList.AppendNewToTop(
-      nsDisplayBlendContainer::CreateForBackgroundBlendMode(aBuilder, aFrame, &bgItemList, scrollClip));
+      nsDisplayBlendContainer::CreateForBackgroundBlendMode(aBuilder, aFrame, &bgItemList, asr));
   }
 
   aList->AppendToTop(&bgItemList);
   return false;
 }
 
 // Check that the rounded border of aFrame, added to aToReferenceFrame,
 // intersects aRect.  Assumes that the unrounded border has already
@@ -3453,23 +3560,16 @@ nsDisplayBackgroundImage::GetBoundsInter
   if (!mBackgroundStyle) {
     return nsRect();
   }
 
   nsRect clipRect = mBackgroundRect;
   if (mFrame->GetType() == nsGkAtoms::canvasFrame) {
     nsCanvasFrame* frame = static_cast<nsCanvasFrame*>(mFrame);
     clipRect = frame->CanvasArea() + ToReferenceFrame();
-  } else if (nsLayoutUtils::UsesAsyncScrolling(mFrame) && mShouldFixToViewport) {
-    // If this is a background-attachment:fixed image, and APZ is enabled,
-    // async scrolling could reveal additional areas of the image, so don't
-    // clip it beyond clipping to the document's viewport.
-    if (Maybe<nsRect> viewportRect = GetViewportRectRelativeToReferenceFrame(aBuilder, mFrame)) {
-      clipRect = clipRect.Union(*viewportRect);
-    }
   }
   const nsStyleImageLayers::Layer& layer = mBackgroundStyle->mImage.mLayers[mLayer];
   return nsCSSRendering::GetBackgroundLayerRect(presContext, mFrame,
                                                 mBackgroundRect, clipRect, layer,
                                                 aBuilder->GetBackgroundPaintFlags());
 }
 
 uint32_t
@@ -3749,23 +3849,21 @@ nsDisplayImageContainer::CanOptimizeToIm
   }
 
   return true;
 }
 
 void
 nsDisplayBackgroundColor::ApplyOpacity(nsDisplayListBuilder* aBuilder,
                                        float aOpacity,
-                                       const DisplayItemClip* aClip)
+                                       const DisplayItemClipChain* aClip)
 {
   NS_ASSERTION(CanApplyOpacity(), "ApplyOpacity should be allowed");
   mColor.a = mColor.a * aOpacity;
-  if (aClip) {
-    IntersectClip(aBuilder, *aClip);
-  }
+  IntersectClip(aBuilder, aClip);
 }
 
 bool
 nsDisplayBackgroundColor::CanApplyOpacity() const
 {
   return true;
 }
 
@@ -4031,17 +4129,19 @@ nsDisplayLayerEventRegions::AddFrame(nsD
   if (!simpleRegions) {
     if (nsLayoutUtils::HasNonZeroCorner(aFrame->StyleBorder()->mBorderRadius)) {
       borderBoxHasRoundedCorners = true;
     } else {
       aFrame->AddStateBits(NS_FRAME_SIMPLE_EVENT_REGIONS);
     }
   }
 
-  const DisplayItemClip* clip = aBuilder->ClipState().GetCurrentCombinedClip(aBuilder);
+  const DisplayItemClip* clip = DisplayItemClipChain::ClipForASR(
+    aBuilder->ClipState().GetCurrentCombinedClipChain(aBuilder),
+    aBuilder->CurrentActiveScrolledRoot());
   if (clip) {
     borderBox = clip->ApplyNonRoundedIntersection(borderBox);
     if (clip->GetRoundedRectCount() > 0) {
       borderBoxHasRoundedCorners = true;
     }
   }
 
   if (borderBoxHasRoundedCorners ||
@@ -4563,23 +4663,23 @@ nsDisplayBoxShadowInner::ComputeVisibili
   // Store the actual visible region
   mVisibleRegion.And(*aVisibleRegion, mVisibleRect);
   return true;
 }
 
 nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder* aBuilder,
                                      nsIFrame* aFrame, nsDisplayList* aList)
   : nsDisplayWrapList(aBuilder, aFrame, aList,
-                      aBuilder->ClipState().GetCurrentInnermostScrollClip())
+                      aBuilder->CurrentActiveScrolledRoot())
 {}
 
 nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder* aBuilder,
                                      nsIFrame* aFrame, nsDisplayList* aList,
-                                     const DisplayItemScrollClip* aScrollClip)
-  : nsDisplayItem(aBuilder, aFrame, aScrollClip)
+                                     const ActiveScrolledRoot* aActiveScrolledRoot)
+  : nsDisplayItem(aBuilder, aFrame, aActiveScrolledRoot)
   , mOverrideZIndex(0)
   , mHasZIndexOverride(false)
 {
   MOZ_COUNT_CTOR(nsDisplayWrapList);
 
   mBaseVisibleRect = mVisibleRect;
 
   mList.AppendToTop(aList);
@@ -4839,19 +4939,19 @@ nsresult nsDisplayWrapper::WrapListsInPl
   rv = WrapEachDisplayItem(aBuilder, aLists.PositionedDescendants(), this);
   NS_ENSURE_SUCCESS(rv, rv);
   // The outlines may not be in-flow
   return WrapEachDisplayItem(aBuilder, aLists.Outlines(), this);
 }
 
 nsDisplayOpacity::nsDisplayOpacity(nsDisplayListBuilder* aBuilder,
                                    nsIFrame* aFrame, nsDisplayList* aList,
-                                   const DisplayItemScrollClip* aScrollClip,
+                                   const ActiveScrolledRoot* aActiveScrolledRoot,
                                    bool aForEventsAndPluginsOnly)
-    : nsDisplayWrapList(aBuilder, aFrame, aList, aScrollClip)
+    : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot)
     , mOpacity(aFrame->StyleEffects()->mOpacity)
     , mForEventsAndPluginsOnly(aForEventsAndPluginsOnly)
 {
   MOZ_COUNT_CTOR(nsDisplayOpacity);
 }
 
 #ifdef NS_BUILD_REFCNT_LOGGING
 nsDisplayOpacity::~nsDisplayOpacity() {
@@ -4938,23 +5038,21 @@ nsDisplayOpacity::NeedsActiveLayer(nsDis
     SetAnimationPerformanceWarningForTooSmallItem(aFrame, eCSSProperty_opacity);
   }
   return false;
 }
 
 void
 nsDisplayOpacity::ApplyOpacity(nsDisplayListBuilder* aBuilder,
                              float aOpacity,
-                             const DisplayItemClip* aClip)
+                             const DisplayItemClipChain* aClip)
 {
   NS_ASSERTION(CanApplyOpacity(), "ApplyOpacity should be allowed");
   mOpacity = mOpacity * aOpacity;
-  if (aClip) {
-    IntersectClip(aBuilder, *aClip);
-  }
+  IntersectClip(aBuilder, aClip);
 }
 
 bool
 nsDisplayOpacity::CanApplyOpacity() const
 {
   return true;
 }
 
@@ -5000,18 +5098,26 @@ nsDisplayOpacity::ShouldFlattenAway(nsDi
   for (uint32_t i = 0; i < numChildren; i++) {
     for (uint32_t j = i+1; j < numChildren; j++) {
       if (children[i].bounds.Intersects(children[j].bounds)) {
         return false;
       }
     }
   }
 
+  // When intersecting the children's clip, only intersect with the clip for
+  // our ASR and not with the whole clip chain, because the rest of the clip
+  // chain is usually already set on the children. In fact, opacity items
+  // usually never have their own clip because during display item creation
+  // time we propagated the clip to our contents, so maybe we should just
+  // remove the clip parameter from ApplyOpacity completely.
+  DisplayItemClipChain clip = { GetClip(), mActiveScrolledRoot, nullptr };
+
   for (uint32_t i = 0; i < numChildren; i++) {
-    children[i].item->ApplyOpacity(aBuilder, mOpacity, mClip);
+    children[i].item->ApplyOpacity(aBuilder, mOpacity, mClip ? &clip : nullptr);
   }
   return true;
 }
 
 nsDisplayItem::LayerState
 nsDisplayOpacity::GetLayerState(nsDisplayListBuilder* aBuilder,
                                 LayerManager* aManager,
                                 const ContainerLayerParameters& aParameters) {
@@ -5050,36 +5156,34 @@ nsDisplayOpacity::ComputeVisibility(nsDi
 bool nsDisplayOpacity::TryMerge(nsDisplayItem* aItem) {
   if (aItem->GetType() != TYPE_OPACITY)
     return false;
   // items for the same content element should be merged into a single
   // compositing group
   // aItem->GetUnderlyingFrame() returns non-null because it's nsDisplayOpacity
   if (aItem->Frame()->GetContent() != mFrame->GetContent())
     return false;
-  if (aItem->GetClip() != GetClip())
-    return false;
-  if (aItem->ScrollClip() != ScrollClip())
+  if (aItem->GetClipChain() != GetClipChain())
     return false;
   MergeFromTrackingMergedFrames(static_cast<nsDisplayOpacity*>(aItem));
   return true;
 }
 
 void
 nsDisplayOpacity::WriteDebugInfo(std::stringstream& aStream)
 {
   aStream << " (opacity " << mOpacity << ")";
 }
 
 nsDisplayBlendMode::nsDisplayBlendMode(nsDisplayListBuilder* aBuilder,
                                              nsIFrame* aFrame, nsDisplayList* aList,
                                              uint8_t aBlendMode,
-                                             const DisplayItemScrollClip* aScrollClip,
+                                             const ActiveScrolledRoot* aActiveScrolledRoot,
                                              uint32_t aIndex)
-  : nsDisplayWrapList(aBuilder, aFrame, aList, aScrollClip)
+  : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot)
   , mBlendMode(aBlendMode)
   , mIndex(aIndex)
 {
   MOZ_COUNT_CTOR(nsDisplayBlendMode);
 }
 
 #ifdef NS_BUILD_REFCNT_LOGGING
 nsDisplayBlendMode::~nsDisplayBlendMode() {
@@ -5140,45 +5244,43 @@ bool nsDisplayBlendMode::TryMerge(nsDisp
     return false;
   nsDisplayBlendMode* item = static_cast<nsDisplayBlendMode*>(aItem);
   // items for the same content element should be merged into a single
   // compositing group
   if (item->Frame()->GetContent() != mFrame->GetContent())
     return false;
   if (item->mIndex != 0 || mIndex != 0)
     return false; // don't merge background-blend-mode items
-  if (item->GetClip() != GetClip())
-    return false;
-  if (item->ScrollClip() != ScrollClip())
+  if (item->GetClipChain() != GetClipChain())
     return false;
   MergeFromTrackingMergedFrames(item);
   return true;
 }
 
 /* static */ nsDisplayBlendContainer*
 nsDisplayBlendContainer::CreateForMixBlendMode(nsDisplayListBuilder* aBuilder,
                                                nsIFrame* aFrame, nsDisplayList* aList,
-                                               const DisplayItemScrollClip* aScrollClip)
-{
-  return new (aBuilder) nsDisplayBlendContainer(aBuilder, aFrame, aList, aScrollClip, false);
+                                               const ActiveScrolledRoot* aActiveScrolledRoot)
+{
+  return new (aBuilder) nsDisplayBlendContainer(aBuilder, aFrame, aList, aActiveScrolledRoot, false);
 }
 
 /* static */ nsDisplayBlendContainer*
 nsDisplayBlendContainer::CreateForBackgroundBlendMode(nsDisplayListBuilder* aBuilder,
                                                       nsIFrame* aFrame, nsDisplayList* aList,
-                                                      const DisplayItemScrollClip* aScrollClip)
-{
-  return new (aBuilder) nsDisplayBlendContainer(aBuilder, aFrame, aList, aScrollClip, true);
+                                                      const ActiveScrolledRoot* aActiveScrolledRoot)
+{
+  return new (aBuilder) nsDisplayBlendContainer(aBuilder, aFrame, aList, aActiveScrolledRoot, true);
 }
 
 nsDisplayBlendContainer::nsDisplayBlendContainer(nsDisplayListBuilder* aBuilder,
                                                  nsIFrame* aFrame, nsDisplayList* aList,
-                                                 const DisplayItemScrollClip* aScrollClip,
+                                                 const ActiveScrolledRoot* aActiveScrolledRoot,
                                                  bool aIsForBackground)
-    : nsDisplayWrapList(aBuilder, aFrame, aList, aScrollClip)
+    : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot)
     , mIsForBackground(aIsForBackground)
 {
   MOZ_COUNT_CTOR(nsDisplayBlendContainer);
 }
 
 #ifdef NS_BUILD_REFCNT_LOGGING
 nsDisplayBlendContainer::~nsDisplayBlendContainer() {
   MOZ_COUNT_DTOR(nsDisplayBlendContainer);
@@ -5217,30 +5319,29 @@ nsDisplayBlendContainer::GetLayerState(n
 bool nsDisplayBlendContainer::TryMerge(nsDisplayItem* aItem) {
   if (aItem->GetType() != TYPE_BLEND_CONTAINER)
     return false;
   // items for the same content element should be merged into a single
   // compositing group
   // aItem->GetUnderlyingFrame() returns non-null because it's nsDisplayOpacity
   if (aItem->Frame()->GetContent() != mFrame->GetContent())
     return false;
-  if (aItem->GetClip() != GetClip())
-    return false;
-  if (aItem->ScrollClip() != ScrollClip())
+  if (aItem->GetClipChain() != GetClipChain())
     return false;
   MergeFromTrackingMergedFrames(static_cast<nsDisplayBlendContainer*>(aItem));
   return true;
 }
 
 nsDisplayOwnLayer::nsDisplayOwnLayer(nsDisplayListBuilder* aBuilder,
                                      nsIFrame* aFrame, nsDisplayList* aList,
+                                     const ActiveScrolledRoot* aActiveScrolledRoot,
                                      uint32_t aFlags, ViewID aScrollTarget,
                                      float aScrollbarThumbRatio,
                                      bool aForceActive)
-    : nsDisplayWrapList(aBuilder, aFrame, aList)
+    : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot)
     , mFlags(aFlags)
     , mScrollTarget(aScrollTarget)
     , mScrollbarThumbRatio(aScrollbarThumbRatio)
     , mForceActive(aForceActive)
 {
   MOZ_COUNT_CTOR(nsDisplayOwnLayer);
 }
 
@@ -5286,17 +5387,17 @@ nsDisplayOwnLayer::BuildLayer(nsDisplayL
     mFrame->PresContext()->SetNotifySubDocInvalidationData(layer);
   }
   return layer.forget();
 }
 
 nsDisplaySubDocument::nsDisplaySubDocument(nsDisplayListBuilder* aBuilder,
                                            nsIFrame* aFrame, nsDisplayList* aList,
                                            uint32_t aFlags)
-    : nsDisplayOwnLayer(aBuilder, aFrame, aList, aFlags)
+    : nsDisplayOwnLayer(aBuilder, aFrame, aList, aBuilder->CurrentActiveScrolledRoot(), aFlags)
     , mScrollParentId(aBuilder->GetCurrentScrollParentId())
 {
   MOZ_COUNT_CTOR(nsDisplaySubDocument);
   mForceDispatchToContentRegion =
     aBuilder->IsBuildingLayerEventRegions() &&
     nsLayoutUtils::HasDocumentLevelListenersForApzAwareEvents(aFrame->PresContext()->PresShell());
 }
 
@@ -5397,17 +5498,18 @@ nsDisplaySubDocument::ComputeVisibility(
 
   nsRegion childVisibleRegion;
   // The visible region for the children may be much bigger than the hole we
   // are viewing the children from, so that the compositor process has enough
   // content to asynchronously pan while content is being refreshed.
   childVisibleRegion = displayport + mFrame->GetOffsetToCrossDoc(ReferenceFrame());
 
   nsRect boundedRect =
-    childVisibleRegion.GetBounds().Intersect(mList.GetBounds(aBuilder));
+    childVisibleRegion.GetBounds().Intersect(
+      mList.GetClippedBoundsWithRespectToASR(aBuilder, mActiveScrolledRoot));
   bool visible = mList.ComputeVisibilityForSublist(
     aBuilder, &childVisibleRegion, boundedRect);
 
   // If APZ is enabled then don't allow this computation to influence
   // aVisibleRegion, on the assumption that the layer can be asynchronously
   // scrolled so we'll definitely need all the content under it.
   if (!nsLayoutUtils::UsesAsyncScrolling(mFrame)) {
     bool snap;
@@ -5485,30 +5587,31 @@ nsDisplayResolution::BuildLayer(nsDispla
                       1.0f / presShell->GetResolution());
   layer->AsContainerLayer()->SetScaleToResolution(
       presShell->ScaleToResolution(), presShell->GetResolution());
   return layer.forget();
 }
 
 nsDisplayFixedPosition::nsDisplayFixedPosition(nsDisplayListBuilder* aBuilder,
                                                nsIFrame* aFrame,
-                                               nsDisplayList* aList)
-  : nsDisplayOwnLayer(aBuilder, aFrame, aList)
+                                               nsDisplayList* aList,
+                                               const ActiveScrolledRoot* aActiveScrolledRoot)
+  : nsDisplayOwnLayer(aBuilder, aFrame, aList, aActiveScrolledRoot)
   , mIndex(0)
   , mIsFixedBackground(false)
 {
   MOZ_COUNT_CTOR(nsDisplayFixedPosition);
   Init(aBuilder);
 }
 
 nsDisplayFixedPosition::nsDisplayFixedPosition(nsDisplayListBuilder* aBuilder,
                                                nsIFrame* aFrame,
                                                nsDisplayList* aList,
                                                uint32_t aIndex)
-  : nsDisplayOwnLayer(aBuilder, aFrame, aList)
+  : nsDisplayOwnLayer(aBuilder, aFrame, aList, aBuilder->CurrentActiveScrolledRoot())
   , mIndex(aIndex)
   , mIsFixedBackground(true)
 {
   MOZ_COUNT_CTOR(nsDisplayFixedPosition);
   Init(aBuilder);
 }
 
 void
@@ -5521,21 +5624,16 @@ nsDisplayFixedPosition::Init(nsDisplayLi
 }
 
 /* static */ nsDisplayFixedPosition*
 nsDisplayFixedPosition::CreateForFixedBackground(nsDisplayListBuilder* aBuilder,
                                                  nsIFrame* aFrame,
                                                  nsDisplayBackgroundImage* aImage,
                                                  uint32_t aIndex)
 {
-  // Clear clipping on the child item, since we will apply it to the
-  // fixed position item as well.
-  aImage->SetClip(aBuilder, DisplayItemClip());
-  aImage->SetScrollClip(nullptr);
-
   nsDisplayList temp;
   temp.AppendToTop(aImage);
 
   return new (aBuilder) nsDisplayFixedPosition(aBuilder, aFrame, &temp, aIndex + 1);
 }
 
 
 #ifdef NS_BUILD_REFCNT_LOGGING
@@ -5585,26 +5683,27 @@ nsDisplayFixedPosition::BuildLayer(nsDis
 
 bool nsDisplayFixedPosition::TryMerge(nsDisplayItem* aItem) {
   if (aItem->GetType() != TYPE_FIXED_POSITION)
     return false;
   // Items with the same fixed position frame can be merged.
   nsDisplayFixedPosition* other = static_cast<nsDisplayFixedPosition*>(aItem);
   if (other->mFrame != mFrame)
     return false;
-  if (aItem->GetClip() != GetClip())
+  if (aItem->GetClipChain() != GetClipChain())
     return false;
   MergeFromTrackingMergedFrames(other);
   return true;
 }
 
 nsDisplayStickyPosition::nsDisplayStickyPosition(nsDisplayListBuilder* aBuilder,
                                                  nsIFrame* aFrame,
-                                                 nsDisplayList* aList)
-  : nsDisplayOwnLayer(aBuilder, aFrame, aList)
+                                                 nsDisplayList* aList,
+                                                 const ActiveScrolledRoot* aActiveScrolledRoot)
+  : nsDisplayOwnLayer(aBuilder, aFrame, aList, aActiveScrolledRoot)
 {
   MOZ_COUNT_CTOR(nsDisplayStickyPosition);
 }
 
 #ifdef NS_BUILD_REFCNT_LOGGING
 nsDisplayStickyPosition::~nsDisplayStickyPosition() {
   MOZ_COUNT_DTOR(nsDisplayStickyPosition);
 }
@@ -5669,19 +5768,17 @@ nsDisplayStickyPosition::BuildLayer(nsDi
 
 bool nsDisplayStickyPosition::TryMerge(nsDisplayItem* aItem) {
   if (aItem->GetType() != TYPE_STICKY_POSITION)
     return false;
   // Items with the same fixed position frame can be merged.
   nsDisplayStickyPosition* other = static_cast<nsDisplayStickyPosition*>(aItem);
   if (other->mFrame != mFrame)
     return false;
-  if (aItem->GetClip() != GetClip())
-    return false;
-  if (aItem->ScrollClip() != ScrollClip())
+  if (aItem->GetClipChain() != GetClipChain())
     return false;
   MergeFromTrackingMergedFrames(other);
   return true;
 }
 
 nsDisplayScrollInfoLayer::nsDisplayScrollInfoLayer(
   nsDisplayListBuilder* aBuilder,
   nsIFrame* aScrolledFrame,
@@ -5916,17 +6013,17 @@ nsDisplayTransform::SetReferenceFrameToA
   }
   mVisibleRect = aBuilder->GetDirtyRect() + mToReferenceFrame;
 }
 
 void
 nsDisplayTransform::Init(nsDisplayListBuilder* aBuilder)
 {
   mHasBounds = false;
-  mStoredList.SetClip(aBuilder, DisplayItemClip::NoClip());
+  mStoredList.SetClipChain(nullptr);
   mStoredList.SetVisibleRect(mChildrenVisibleRect);
 }
 
 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
                                        nsIFrame *aFrame, nsDisplayList *aList,
                                        const nsRect& aChildrenVisibleRect,
                                        uint32_t aIndex,
                                        bool aAllowAsyncAnimation)
@@ -6897,20 +6994,17 @@ nsDisplayTransform::TryMerge(nsDisplayIt
   /* Make sure that we're dealing with two transforms. */
   if (aItem->GetType() != TYPE_TRANSFORM)
     return false;
 
   /* Check to see that both frames are part of the same content. */
   if (aItem->Frame()->GetContent() != mFrame->GetContent())
     return false;
 
-  if (aItem->GetClip() != GetClip())
-    return false;
-
-  if (aItem->ScrollClip() != ScrollClip())
+  if (aItem->GetClipChain() != GetClipChain())
     return false;
 
   /* Now, move everything over to this frame and signal that
    * we merged things!
    */
   mStoredList.MergeFromTrackingMergedFrames(&static_cast<nsDisplayTransform*>(aItem)->mStoredList);
   return true;
 }
@@ -7127,18 +7221,18 @@ nsCharClipDisplayItem::ComputeInvalidati
       !geometry->mBorderRect.IsEqualInterior(GetBorderRect())) {
     aInvalidRegion->Or(oldRect, newRect);
   }
 }
 
 nsDisplaySVGEffects::nsDisplaySVGEffects(nsDisplayListBuilder* aBuilder,
                                          nsIFrame* aFrame, nsDisplayList* aList,
                                          bool aHandleOpacity,
-                                         const DisplayItemScrollClip* aScrollClip)
-  : nsDisplayWrapList(aBuilder, aFrame, aList, aScrollClip)
+                                         const ActiveScrolledRoot* aActiveScrolledRoot)
+  : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot)
   , mEffectsBounds(aFrame->GetVisualOverflowRectRelativeToSelf())
   , mHandleOpacity(aHandleOpacity)
 {
   MOZ_COUNT_CTOR(nsDisplaySVGEffects);
 }
 
 nsDisplaySVGEffects::nsDisplaySVGEffects(nsDisplayListBuilder* aBuilder,
                                          nsIFrame* aFrame, nsDisplayList* aList,
@@ -7346,18 +7440,18 @@ ComputeMaskGeometry(PaintFramesParams& a
   ctx.Restore();
 
   aParams.maskRect = result;
 }
 
 nsDisplayMask::nsDisplayMask(nsDisplayListBuilder* aBuilder,
                              nsIFrame* aFrame, nsDisplayList* aList,
                              bool aHandleOpacity,
-                             const DisplayItemScrollClip* aScrollClip)
-  : nsDisplaySVGEffects(aBuilder, aFrame, aList, aHandleOpacity, aScrollClip)
+                             const ActiveScrolledRoot* aActiveScrolledRoot)
+  : nsDisplaySVGEffects(aBuilder, aFrame, aList, aHandleOpacity, aActiveScrolledRoot)
 {
   MOZ_COUNT_CTOR(nsDisplayMask);
 
   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) {
@@ -7385,20 +7479,17 @@ bool nsDisplayMask::TryMerge(nsDisplayIt
     return false;
 
   // items for the same content element should be merged into a single
   // compositing group
   // aItem->GetUnderlyingFrame() returns non-null because it's nsDisplaySVGEffects
   if (aItem->Frame()->GetContent() != mFrame->GetContent()) {
     return false;
   }
-  if (aItem->GetClip() != GetClip()) {
-    return false;
-  }
-  if (aItem->ScrollClip() != ScrollClip()) {
+  if (aItem->GetClipChain() != GetClipChain()) {
     return false;
   }
 
   // Do not merge if mFrame has mask. Continuation frames should apply mask
   // independently(just like nsDisplayBackgroundImage).
   const nsStyleSVGReset *style = mFrame->StyleSVGReset();
   if (style->mMask.HasLayerWithImage()) {
     return false;
@@ -7501,17 +7592,18 @@ bool nsDisplayMask::ShouldPaintOnMaskLay
 }
 
 bool nsDisplayMask::ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                       nsRegion* aVisibleRegion)
 {
   // Our children may be made translucent or arbitrarily deformed so we should
   // not allow them to subtract area from aVisibleRegion.
   nsRegion childrenVisible(mVisibleRect);
-  nsRect r = mVisibleRect.Intersect(mList.GetBounds(aBuilder));
+  nsRect r = mVisibleRect.Intersect(
+    mList.GetClippedBoundsWithRespectToASR(aBuilder, mActiveScrolledRoot));
   mList.ComputeVisibilityForSublist(aBuilder, &childrenVisible, r);
   return true;
 }
 
 void
 nsDisplayMask::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                          const nsDisplayItemGeometry* aGeometry,
                                          nsRegion* aInvalidRegion)
@@ -7678,20 +7770,17 @@ bool nsDisplayFilter::TryMerge(nsDisplay
   }
 
   // items for the same content element should be merged into a single
   // compositing group.
   // aItem->Frame() returns non-null because it's nsDisplayFilter
   if (aItem->Frame()->GetContent() != mFrame->GetContent()) {
     return false;
   }
-  if (aItem->GetClip() != GetClip()) {
-    return false;
-  }
-  if (aItem->ScrollClip() != ScrollClip()) {
+  if (aItem->GetClipChain() != GetClipChain()) {
     return false;
   }
 
   nsDisplayFilter* other = static_cast<nsDisplayFilter*>(aItem);
   MergeFromTrackingMergedFrames(other);
   mEffectsBounds.UnionRect(mEffectsBounds,
     other->mEffectsBounds + other->mFrame->GetOffsetTo(mFrame));
 
@@ -7713,17 +7802,18 @@ bool nsDisplayFilter::ComputeVisibility(
   nsRect dirtyRect =
     nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(mFrame,
                                                            mVisibleRect - offset) +
     offset;
 
   // Our children may be made translucent or arbitrarily deformed so we should
   // not allow them to subtract area from aVisibleRegion.
   nsRegion childrenVisible(dirtyRect);
-  nsRect r = dirtyRect.Intersect(mList.GetBounds(aBuilder));
+  nsRect r = dirtyRect.Intersect(
+    mList.GetClippedBoundsWithRespectToASR(aBuilder, mActiveScrolledRoot));
   mList.ComputeVisibilityForSublist(aBuilder, &childrenVisible, r);
   return true;
 }
 
 void
 nsDisplayFilter::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                            const nsDisplayItemGeometry* aGeometry,
                                            nsRegion* aInvalidRegion)
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -48,17 +48,16 @@ class nsDisplayTableItem;
 class nsISelection;
 class nsIScrollableFrame;
 class nsDisplayLayerEventRegions;
 class nsDisplayScrollInfoLayer;
 class nsCaret;
 
 namespace mozilla {
 class FrameLayerBuilder;
-class DisplayItemScrollClip;
 namespace layers {
 class Layer;
 class ImageLayer;
 class ImageContainer;
 } // namespace layers
 } // namespace mozilla
 
 // A set of blend modes, that never includes OP_OVER (since it's
@@ -276,17 +275,16 @@ class nsDisplayListBuilder {
     nsRect mDirtyRect;
   };
 
 public:
   typedef mozilla::FrameLayerBuilder FrameLayerBuilder;
   typedef mozilla::DisplayItemClip DisplayItemClip;
   typedef mozilla::DisplayItemClipChain DisplayItemClipChain;
   typedef mozilla::DisplayListClipState DisplayListClipState;
-  typedef mozilla::DisplayItemScrollClip DisplayItemScrollClip;
   typedef mozilla::ActiveScrolledRoot ActiveScrolledRoot;
   typedef nsIWidget::ThemeGeometry ThemeGeometry;
   typedef mozilla::layers::Layer Layer;
   typedef mozilla::layers::FrameMetrics FrameMetrics;
   typedef mozilla::layers::FrameMetrics::ViewID ViewID;
   typedef mozilla::gfx::Matrix4x4 Matrix4x4;
 
   /**
@@ -756,29 +754,22 @@ public:
                                                           const DisplayItemClipChain* aLeafClip2);
 
   /**
    * Clone the supplied clip chain's chain items into this builder's arena.
    */
   const DisplayItemClipChain* CopyWholeChain(const DisplayItemClipChain* aClipChain);
 
   /**
-   * Allocate a new DisplayItemClip in the arena. Will be cleaned up
-   * automatically when the arena goes away.
+   * Only used for containerful root scrolling. This is a workaround.
    */
-  const DisplayItemClip* AllocateDisplayItemClip(const DisplayItemClip& aOriginal);
-
-  /**
-   * Allocate a new DisplayItemScrollClip in the arena. Will be cleaned up
-   * automatically when the arena goes away.
-   */
-  DisplayItemScrollClip* AllocateDisplayItemScrollClip(const DisplayItemScrollClip* aParent,
-                                                 nsIScrollableFrame* aScrollableFrame,
-                                                 const DisplayItemClip* aClip,
-                                                 bool aIsAsyncScrollable);
+  void SetActiveScrolledRootForRootScrollframe(const ActiveScrolledRoot* aASR)
+  { mActiveScrolledRootForRootScrollframe = aASR; }
+  const ActiveScrolledRoot* ActiveScrolledRootForRootScrollframe() const
+  { return mActiveScrolledRootForRootScrollframe; }
 
   /**
    * Transfer off main thread animations to the layer.  May be called
    * with aBuilder and aItem both null, but only if the caller has
    * already checked that off main thread animations should be sent to
    * the layer.  When they are both null, the animations are added to
    * the layer as pending animations.
    */
@@ -974,50 +965,101 @@ public:
    */
   class AutoCurrentActiveScrolledRootSetter;
   friend class AutoCurrentActiveScrolledRootSetter;
   class AutoCurrentActiveScrolledRootSetter {
   public:
     explicit AutoCurrentActiveScrolledRootSetter(nsDisplayListBuilder* aBuilder)
       : mBuilder(aBuilder)
       , mSavedActiveScrolledRoot(aBuilder->mCurrentActiveScrolledRoot)
+      , mContentClipASR(aBuilder->ClipState().GetContentClipASR())
       , mDescendantsStartIndex(aBuilder->mActiveScrolledRoots.Length())
       , mUsed(false)
     {
     }
 
     ~AutoCurrentActiveScrolledRootSetter()
     {
       mBuilder->mCurrentActiveScrolledRoot = mSavedActiveScrolledRoot;
     }
 
     void SetCurrentActiveScrolledRoot(const ActiveScrolledRoot* aActiveScrolledRoot)
     {
       MOZ_ASSERT(!mUsed);
+
+      // Set the builder's mCurrentActiveScrolledRoot.
       mBuilder->mCurrentActiveScrolledRoot = aActiveScrolledRoot;
+
+      // We also need to adjust the builder's mCurrentContainerASR.
+      // mCurrentContainerASR needs to be an ASR that all the container's
+      // contents have finite bounds with respect to. If aActiveScrolledRoot
+      // is an ancestor ASR of mCurrentContainerASR, that means we need to
+      // set mCurrentContainerASR to aActiveScrolledRoot, because otherwise
+      // the items that will be created with aActiveScrolledRoot wouldn't
+      // have finite bounds with respect to mCurrentContainerASR. There's one
+      // exception, in the case where there's a content clip on the builder
+      // that is scrolled by a descendant ASR of aActiveScrolledRoot. This
+      // content clip will clip all items that are created while this
+      // AutoCurrentActiveScrolledRootSetter exists. This means that the items
+      // created during our lifetime will have finite bounds with respect to
+      // the content clip's ASR, even if the items' actual ASR is an ancestor
+      // of that. And it also means that mCurrentContainerASR only needs to be
+      // set to the content clip's ASR and not all the way to aActiveScrolledRoot.
+      // This case is tested by fixed-pos-scrolled-clip-opacity-layerize.html
+      // and fixed-pos-scrolled-clip-opacity-inside-layerize.html.
+
+      // finiteBoundsASR is the leafmost ASR that all items created during
+      // object's lifetime have finite bounds with respect to.
+      const ActiveScrolledRoot* finiteBoundsASR = ActiveScrolledRoot::PickDescendant(
+        mContentClipASR, aActiveScrolledRoot);
+
+      // mCurrentContainerASR is adjusted so that it's still an ancestor of
+      // finiteBoundsASR.
       mBuilder->mCurrentContainerASR = ActiveScrolledRoot::PickAncestor(
-        mBuilder->mCurrentContainerASR, aActiveScrolledRoot);
+        mBuilder->mCurrentContainerASR, finiteBoundsASR);
+
       mUsed = true;
     }
 
     void EnterScrollFrame(nsIScrollableFrame* aScrollableFrame)
     {
       MOZ_ASSERT(!mUsed);
       ActiveScrolledRoot* asr = mBuilder->AllocateActiveScrolledRoot(
         mBuilder->mCurrentActiveScrolledRoot, aScrollableFrame);
       mBuilder->mCurrentActiveScrolledRoot = asr;
       mUsed = true;
     }
 
     void InsertScrollFrame(nsIScrollableFrame* aScrollableFrame);
 
   private:
     nsDisplayListBuilder* mBuilder;
+    /**
+     * The builder's mCurrentActiveScrolledRoot at construction time which
+     * needs to be restored at destruction time.
+     */
     const ActiveScrolledRoot* mSavedActiveScrolledRoot;
+    /**
+     * If there's a content clip on the builder at construction time, then
+     * mContentClipASR is that content clip's ASR, otherwise null. The
+     * assumption is that the content clip doesn't get relaxed while this
+     * object is on the stack.
+     */
+    const ActiveScrolledRoot* mContentClipASR;
+    /**
+     * InsertScrollFrame needs to mutate existing ASRs (those that were
+     * created while this object was on the stack), and mDescendantsStartIndex
+     * makes it easier to skip ASRs that were created in the past.
+     */
     size_t mDescendantsStartIndex;
+    /**
+     * Flag to make sure that only one of SetCurrentActiveScrolledRoot /
+     * EnterScrollFrame / InsertScrollFrame is called per instance of this
+     * class.
+     */
     bool mUsed;
   };
 
   /**
    * Keeps track of the innermost ASR that can be used as the ASR for a
    * container item that wraps all items that were created while this
    * object was on the stack.
    * The rule is: all child items of the container item need to have
@@ -1026,17 +1068,19 @@ public:
   class AutoContainerASRTracker;
   friend class AutoContainerASRTracker;
   class AutoContainerASRTracker {
   public:
     explicit AutoContainerASRTracker(nsDisplayListBuilder* aBuilder)
       : mBuilder(aBuilder)
       , mSavedContainerASR(aBuilder->mCurrentContainerASR)
     {
-      mBuilder->mCurrentContainerASR = mBuilder->mCurrentActiveScrolledRoot;
+      mBuilder->mCurrentContainerASR = ActiveScrolledRoot::PickDescendant(
+        mBuilder->ClipState().GetContentClipASR(),
+        mBuilder->mCurrentActiveScrolledRoot);
     }
 
     const ActiveScrolledRoot* GetContainerASR()
     {
       return mBuilder->mCurrentContainerASR;
     }
 
     ~AutoContainerASRTracker()
@@ -1161,40 +1205,46 @@ public:
     return mPreserves3DCtx.mAccumulatedRectLevels;
   }
 
   // Helpers for tables
   nsDisplayTableItem* GetCurrentTableItem() { return mCurrentTableItem; }
   void SetCurrentTableItem(nsDisplayTableItem* aTableItem) { mCurrentTableItem = aTableItem; }
 
   struct OutOfFlowDisplayData {
-    OutOfFlowDisplayData(const DisplayItemClip* aContainingBlockClip,
-                         const DisplayItemScrollClip* aContainingBlockScrollClip,
+    OutOfFlowDisplayData(const DisplayItemClipChain* aContainingBlockClipChain,
+                         const ActiveScrolledRoot* aContainingBlockActiveScrolledRoot,
                          const nsRect &aDirtyRect)
-      : mContainingBlockClip(aContainingBlockClip ? *aContainingBlockClip : DisplayItemClip())
-      , mContainingBlockScrollClip(aContainingBlockScrollClip)
+      : mContainingBlockClipChain(aContainingBlockClipChain)
+      , mContainingBlockActiveScrolledRoot(aContainingBlockActiveScrolledRoot)
       , mDirtyRect(aDirtyRect)
     {}
-    DisplayItemClip mContainingBlockClip;
-    const DisplayItemScrollClip* mContainingBlockScrollClip;
+    const DisplayItemClipChain* mContainingBlockClipChain;
+    const ActiveScrolledRoot* mContainingBlockActiveScrolledRoot;
     nsRect mDirtyRect;
   };
 
   NS_DECLARE_FRAME_PROPERTY_DELETABLE(OutOfFlowDisplayDataProperty,
                                       OutOfFlowDisplayData)
 
   static OutOfFlowDisplayData* GetOutOfFlowData(nsIFrame* aFrame)
   {
     return aFrame->Properties().Get(OutOfFlowDisplayDataProperty());
   }
 
   nsPresContext* CurrentPresContext() {
     return CurrentPresShellState()->mPresShell->GetPresContext();
   }
 
+  OutOfFlowDisplayData* GetCurrentFixedBackgroundDisplayData()
+  {
+    auto& displayData = CurrentPresShellState()->mFixedBackgroundDisplayData;
+    return displayData ? displayData.ptr() : nullptr;
+  }
+
   /**
    * Accumulates the bounds of box frames that have moz-appearance
    * -moz-win-exclude-glass style. Used in setting glass margins on
    * Windows.
    *
    * We set the window opaque region (from which glass margins are computed)
    * to the intersection of the glass region specified here and the opaque
    * region computed during painting. So the excluded glass region actually
@@ -1314,16 +1364,22 @@ public:
     mPreserves3DCtx.mDirtyRect = aDirtyRect;
   }
 
   bool IsBuildingInvisibleItems() const { return mBuildingInvisibleItems; }
   void SetBuildingInvisibleItems(bool aBuildingInvisibleItems) {
     mBuildingInvisibleItems = aBuildingInvisibleItems;
   }
 
+  /**
+   * This is a convenience function to ease the transition until AGRs and ASRs
+   * are unified.
+   */
+  AnimatedGeometryRoot* AnimatedGeometryRootForASR(const ActiveScrolledRoot* aASR);
+
   bool HitTestShouldStopAtFirstOpaque() const {
     return mHitTestShouldStopAtFirstOpaque;
   }
   void SetHitTestShouldStopAtFirstOpaque(bool aHitTestShouldStopAtFirstOpaque) {
     mHitTestShouldStopAtFirstOpaque = aHitTestShouldStopAtFirstOpaque;
   }
 
 private:
@@ -1342,35 +1398,36 @@ private:
    */
   nsIFrame* FindAnimatedGeometryRootFrameFor(nsIFrame* aFrame);
 
   friend class nsDisplayCanvasBackgroundImage;
   friend class nsDisplayBackgroundImage;
   friend class nsDisplayFixedPosition;
   AnimatedGeometryRoot* FindAnimatedGeometryRootFor(nsDisplayItem* aItem);
 
+  friend class nsDisplayItem;
+  AnimatedGeometryRoot* FindAnimatedGeometryRootFor(nsIFrame* aFrame);
+
   AnimatedGeometryRoot* WrapAGRForFrame(nsIFrame* aAnimatedGeometryRoot,
                                         AnimatedGeometryRoot* aParent = nullptr);
 
-  friend class nsDisplayItem;
-  AnimatedGeometryRoot* FindAnimatedGeometryRootFor(nsIFrame* aFrame);
-
   nsDataHashtable<nsPtrHashKey<nsIFrame>, 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 {
     nsIPresShell* mPresShell;
     nsIFrame*     mCaretFrame;
     nsRect        mCaretRect;
+    mozilla::Maybe<OutOfFlowDisplayData> mFixedBackgroundDisplayData;
     uint32_t      mFirstFrameMarkedForDisplay;
     bool          mIsBackgroundOnly;
     // This is a per-document flag turning off event handling for all content
     // in the document, and is set when we enter a subdocument for a pointer-
     // events:none frame.
     bool          mInsidePointerEventsNoneDoc;
   };
 
@@ -1433,20 +1490,19 @@ private:
   // The display item for the Windows window glass background, if any
   nsDisplayItem*                 mGlassDisplayItem;
   // A temporary list that we append scroll info items to while building
   // display items for the contents of frames with SVG effects.
   // Only non-null when ShouldBuildScrollInfoItemsForHoisting() is true.
   // This is a pointer and not a real nsDisplayList value because the
   // nsDisplayList class is defined below this class, so we can't use it here.
   nsDisplayList*                 mScrollInfoItemsForHoisting;
-  nsTArray<DisplayItemScrollClip*> mScrollClipsToDestroy;
-  nsTArray<DisplayItemClip*>     mDisplayItemClipsToDestroy;
   nsTArray<ActiveScrolledRoot*>  mActiveScrolledRoots;
   nsTArray<DisplayItemClipChain*> mClipChainsToDestroy;
+  const ActiveScrolledRoot*      mActiveScrolledRootForRootScrollframe;
   nsDisplayListBuilderMode       mMode;
   ViewID                         mCurrentScrollParentId;
   ViewID                         mCurrentScrollbarTarget;
   uint32_t                       mCurrentScrollbarFlags;
   Preserves3DContext             mPreserves3DCtx;
   uint32_t                       mPerspectiveItemIndex;
   int32_t                        mSVGEffectsBuildingDepth;
   bool                           mContainsBlendMode;
@@ -1513,39 +1569,39 @@ protected:
  * 
  * Display items belong to a list at all times (except temporarily as they
  * move from one list to another).
  */
 class nsDisplayItem : public nsDisplayItemLink {
 public:
   typedef mozilla::ContainerLayerParameters ContainerLayerParameters;
   typedef mozilla::DisplayItemClip DisplayItemClip;
-  typedef mozilla::DisplayItemScrollClip DisplayItemScrollClip;
   typedef mozilla::DisplayItemClipChain DisplayItemClipChain;
   typedef mozilla::ActiveScrolledRoot ActiveScrolledRoot;
   typedef mozilla::layers::FrameMetrics FrameMetrics;
   typedef mozilla::layers::ScrollMetadata ScrollMetadata;
   typedef mozilla::layers::FrameMetrics::ViewID ViewID;
   typedef mozilla::layers::Layer Layer;
   typedef mozilla::layers::LayerManager LayerManager;
   typedef mozilla::LayerState LayerState;
 
   // 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 DisplayItemScrollClip* aScrollClip);
+                const ActiveScrolledRoot* aActiveScrolledRoot);
   /**
    * This constructor is only used in rare cases when we need to construct
    * temporary items.
    */
   explicit nsDisplayItem(nsIFrame* aFrame)
     : mFrame(aFrame)
+    , mClipChain(nullptr)
     , mClip(nullptr)
-    , mScrollClip(nullptr)
+    , mActiveScrolledRoot(nullptr)
     , mReferenceFrame(nullptr)
     , mAnimatedGeometryRoot(nullptr)
     , mForceNotVisible(false)
 #ifdef MOZ_DUMP_PAINTING
     , mPainted(false)
 #endif
   {
   }
@@ -1954,17 +2010,17 @@ public:
   virtual const nsRect& GetVisibleRectForChildren() const { return mVisibleRect; }
 
   /**
    * 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 DisplayItemClip* aClip) {
+                            const DisplayItemClipChain* aClip) {
     NS_ASSERTION(CanApplyOpacity(), "ApplyOpacity 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() const {
     return false;
@@ -2037,51 +2093,44 @@ public:
   }
   
   virtual bool SupportsOptimizingToImage() { return false; }
 
   const DisplayItemClip& GetClip()
   {
     return mClip ? *mClip : DisplayItemClip::NoClip();
   }
-  void SetClip(nsDisplayListBuilder* aBuilder, const DisplayItemClip& aClip)
-  {
-    if (!aClip.HasClip()) {
-      mClip = nullptr;
-      return;
-    }
-    mClip = aBuilder->AllocateDisplayItemClip(aClip);
-  }
-
-  void IntersectClip(nsDisplayListBuilder* aBuilder, const DisplayItemClip& aClip)
-  {
-    if (mClip) {
-      DisplayItemClip temp = *mClip;
-      temp.IntersectWith(aClip);
-      SetClip(aBuilder, temp);
-    } else {
-      SetClip(aBuilder, aClip);
-    }
-  }
-
-  void SetScrollClip(const DisplayItemScrollClip* aScrollClip) { mScrollClip = aScrollClip; }
-  const DisplayItemScrollClip* ScrollClip() const { return mScrollClip; }
+  void IntersectClip(nsDisplayListBuilder* aBuilder, const DisplayItemClipChain* aOther);
+
+  void SetActiveScrolledRoot(const ActiveScrolledRoot* aActiveScrolledRoot) { mActiveScrolledRoot = aActiveScrolledRoot; }
+  const ActiveScrolledRoot* GetActiveScrolledRoot() const { return mActiveScrolledRoot; }
+
+  virtual void SetClipChain(const DisplayItemClipChain* aClipChain);
+  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() {
     return mFrame->BackfaceIsHidden();
   }
 
 protected:
   friend class nsDisplayList;
 
   nsDisplayItem() { mAbove = nullptr; }
 
   nsIFrame* mFrame;
+  const DisplayItemClipChain* mClipChain;
   const DisplayItemClip* mClip;
-  const DisplayItemScrollClip* mScrollClip;
+  const ActiveScrolledRoot* mActiveScrolledRoot;
   // Result of FindReferenceFrameFor(mFrame), if mFrame is non-null
   const nsIFrame* mReferenceFrame;
   struct AnimatedGeometryRoot* mAnimatedGeometryRoot;
   // Result of ToReferenceFrame(mFrame), if mFrame is non-null
   nsPoint   mToReferenceFrame;
   // This is the rectangle that needs to be painted.
   // Display item construction sets this to the dirty rect.
   // nsDisplayList::ComputeVisibility sets this to the visible region
@@ -2108,17 +2157,17 @@ protected:
  * slow so we don't support it. The methods that need to step downward
  * (HitTest(), ComputeVisibility()) internally build a temporary array of all
  * the items while they do the downward traversal, so overall they're still
  * linear time. We have optimized for efficient AppendToTop() of both
  * items and lists, with minimal codesize. AppendToBottom() is efficient too.
  */
 class nsDisplayList {
 public:
-  typedef mozilla::DisplayItemScrollClip DisplayItemScrollClip;
+  typedef mozilla::ActiveScrolledRoot ActiveScrolledRoot;
   typedef mozilla::layers::Layer Layer;
   typedef mozilla::layers::LayerManager LayerManager;
   typedef mozilla::layers::PaintedLayer PaintedLayer;
 
   /**
    * Create an empty list.
    */
   nsDisplayList()
@@ -2346,28 +2395,30 @@ public:
   already_AddRefed<LayerManager> PaintRoot(nsDisplayListBuilder* aBuilder,
                                            nsRenderingContext* aCtx,
                                            uint32_t aFlags);
   /**
    * Get the bounds. Takes the union of the bounds of all children.
    * The result is not cached.
    */
   nsRect GetBounds(nsDisplayListBuilder* aBuilder) const;
+
   /**
-   * Return the union of the scroll clipped bounds of all children. To get the
-   * scroll clipped bounds of a child item, we start with the item's clipped
-   * bounds and walk its scroll clip chain up to (but not including)
-   * aIncludeScrollClipsUpTo, and take each scroll clip into account. For
-   * scroll clips from async scrollable frames we assume that the item can move
-   * anywhere inside that scroll frame.
-   * In other words, the return value from this method includes all pixels that
-   * could potentially be covered by items in this list under async scrolling.
+   * Get this list's bounds, respecting clips relative to aASR. The result is
+   * the union of each item's clipped bounds with respect to aASR. That means
+   * that if an item can move asynchronously with an ASR that is a descendant
+   * of aASR, then the clipped bounds with respect to aASR will be the clip of
+   * that item for aASR, because the item can move anywhere inside that clip.
+   * If there is an item in this list which is not bounded with respect to
+   * aASR (i.e. which does not have "finite bounds" with respect to aASR),
+   * then this method trigger an assertion failure.
    */
-  nsRect GetScrollClippedBoundsUpTo(nsDisplayListBuilder* aBuilder,
-                                    const DisplayItemScrollClip* aIncludeScrollClipsUpTo) const;
+  nsRect GetClippedBoundsWithRespectToASR(nsDisplayListBuilder* aBuilder,
+                                          const ActiveScrolledRoot* aASR) const;
+
   /**
    * Find the topmost display item that returns a non-null frame, and return
    * the frame.
    */
   void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
                nsDisplayItem::HitTestState* aState,
                nsTArray<nsIFrame*> *aOutFrames) const;
   /**
@@ -3166,17 +3217,17 @@ public:
   virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                                    bool* aSnap) override;
   virtual mozilla::Maybe<nscolor> IsUniform(nsDisplayListBuilder* aBuilder) override;
   virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
                        HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) override;
 
   virtual void ApplyOpacity(nsDisplayListBuilder* aBuilder,
                             float aOpacity,
-                            const DisplayItemClip* aClip) override;
+                            const DisplayItemClipChain* aClip) override;
   virtual bool CanApplyOpacity() const override;
 
   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) override
   {
     *aSnap = true;
     return mBackgroundRect;
   }
 
@@ -3275,23 +3326,21 @@ public:
   NS_DISPLAY_DECL_NAME("BoxShadowOuter", TYPE_BOX_SHADOW_OUTER)
   
   virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                          const nsDisplayItemGeometry* aGeometry,
                                          nsRegion* aInvalidRegion) override;
   
   virtual void ApplyOpacity(nsDisplayListBuilder* aBuilder,
                             float aOpacity,
-                            const DisplayItemClip* aClip) override
+                            const DisplayItemClipChain* aClip) override
   {
     NS_ASSERTION(CanApplyOpacity(), "ApplyOpacity should be allowed");
     mOpacity = aOpacity;
-    if (aClip) {
-      IntersectClip(aBuilder, *aClip);
-    }
+    IntersectClip(aBuilder, aClip);
   }
   virtual bool CanApplyOpacity() const override
   {
     return true;
   }
 
   virtual nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) override
   {
@@ -3428,17 +3477,17 @@ public:
   nsRect GetHitRegionBounds(nsDisplayListBuilder* aBuilder, bool* aSnap)
   {
     *aSnap = false;
     return mHitRegion.GetBounds().Union(mMaybeHitRegion.GetBounds());
   }
 
   virtual void ApplyOpacity(nsDisplayListBuilder* aBuilder,
                             float aOpacity,
-                            const DisplayItemClip* aClip) override
+                            const DisplayItemClipChain* aClip) override
   {
     NS_ASSERTION(CanApplyOpacity(), "ApplyOpacity should be allowed");
   }
   virtual bool CanApplyOpacity() const override
   {
     return true;
   }
 
@@ -3512,32 +3561,32 @@ class nsDisplayWrapList : public nsDispl
 public:
   /**
    * Takes all the items from aList and puts them in our list.
    */
   nsDisplayWrapList(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                     nsDisplayList* aList);
   nsDisplayWrapList(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                     nsDisplayList* aList,
-                    const DisplayItemScrollClip* aScrollClip);
+                    const ActiveScrolledRoot* aActiveScrolledRoot);
   nsDisplayWrapList(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                     nsDisplayItem* aItem);
   nsDisplayWrapList(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
     : nsDisplayItem(aBuilder, aFrame), mOverrideZIndex(0), mHasZIndexOverride(false)
   {
     MOZ_COUNT_CTOR(nsDisplayWrapList);
     mBaseVisibleRect = mVisibleRect;
   }
   virtual ~nsDisplayWrapList();
   /**
    * Call this if the wrapped list is changed.
    */
   virtual void UpdateBounds(nsDisplayListBuilder* aBuilder) override
   {
-    mBounds = mList.GetScrollClippedBoundsUpTo(aBuilder, mScrollClip);
+    mBounds = mList.GetClippedBoundsWithRespectToASR(aBuilder, mActiveScrolledRoot);
     // The display list may contain content that's visible outside the visible
     // rect (i.e. the current dirty rect) passed in when the item was created.
     // This happens when the dirty rect has been restricted to the visual
     // 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.
     mVisibleRect.UnionRect(mBaseVisibleRect, mList.GetVisibleRect());
@@ -3671,17 +3720,17 @@ protected:
 /**
  * The standard display item to paint a stacking context with translucency
  * set by the stacking context root frame's 'opacity' style.
  */
 class nsDisplayOpacity : public nsDisplayWrapList {
 public:
   nsDisplayOpacity(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                    nsDisplayList* aList,
-                   const DisplayItemScrollClip* aScrollClip,
+                   const ActiveScrolledRoot* aActiveScrolledRoot,
                    bool aForEventsAndPluginsOnly);
 #ifdef NS_BUILD_REFCNT_LOGGING
   virtual ~nsDisplayOpacity();
 #endif
 
   virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                                    bool* aSnap) override;
   virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
@@ -3703,17 +3752,17 @@ public:
   {
     if (mForEventsAndPluginsOnly) {
       return false;
     }
     return nsDisplayWrapList::IsInvalid(aRect);
   }
   virtual void ApplyOpacity(nsDisplayListBuilder* aBuilder,
                             float aOpacity,
-                            const DisplayItemClip* aClip) override;
+                            const DisplayItemClipChain* aClip) override;
   virtual bool CanApplyOpacity() const override;
   virtual bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override;
   static bool NeedsActiveLayer(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame);
   NS_DISPLAY_DECL_NAME("Opacity", TYPE_OPACITY)
   virtual void WriteDebugInfo(std::stringstream& aStream) override;
 
   bool CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) override;
 
@@ -3721,17 +3770,17 @@ private:
   float mOpacity;
   bool mForEventsAndPluginsOnly;
 };
 
 class nsDisplayBlendMode : public nsDisplayWrapList {
 public:
   nsDisplayBlendMode(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                         nsDisplayList* aList, uint8_t aBlendMode,
-                        const DisplayItemScrollClip* aScrollClip,
+                        const ActiveScrolledRoot* aActiveScrolledRoot,
                         uint32_t aIndex = 0);
 #ifdef NS_BUILD_REFCNT_LOGGING
   virtual ~nsDisplayBlendMode();
 #endif
 
   nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                            bool* aSnap) override;
 
@@ -3763,21 +3812,23 @@ private:
   uint8_t mBlendMode;
   uint32_t mIndex;
 };
 
 class nsDisplayBlendContainer : public nsDisplayWrapList {
 public:
     static nsDisplayBlendContainer*
     CreateForMixBlendMode(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
-                          nsDisplayList* aList, const DisplayItemScrollClip* aScrollClip);
+                          nsDisplayList* aList,
+                          const ActiveScrolledRoot* aActiveScrolledRoot);
 
     static nsDisplayBlendContainer*
     CreateForBackgroundBlendMode(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
-                                 nsDisplayList* aList, const DisplayItemScrollClip* aScrollClip);
+                                 nsDisplayList* aList,
+                                 const ActiveScrolledRoot* aActiveScrolledRoot);
 
 #ifdef NS_BUILD_REFCNT_LOGGING
     virtual ~nsDisplayBlendContainer();
 #endif
     
     virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
                                                LayerManager* aManager,
                                                const ContainerLayerParameters& aContainerParameters) override;
@@ -3792,17 +3843,17 @@ public:
       return (mIsForBackground ? 1 << nsDisplayItem::TYPE_BITS : 0) |
         nsDisplayItem::GetPerFrameKey();
     }
     NS_DISPLAY_DECL_NAME("BlendContainer", TYPE_BLEND_CONTAINER)
 
 private:
     nsDisplayBlendContainer(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                             nsDisplayList* aList,
-                            const DisplayItemScrollClip* aScrollClip,
+                            const ActiveScrolledRoot* aActiveScrolledRoot,
                             bool aIsForBackground);
 
     // Used to distinguish containers created at building stacking
     // context or appending background.
     bool mIsForBackground;
 };
 
 /**
@@ -3830,17 +3881,19 @@ public:
    * GENERATE_SCROLLABLE_LAYER : only valid on nsDisplaySubDocument (and
    * subclasses), indicates this layer is to be a scrollable layer, so call
    * ComputeFrameMetrics, etc.
    * @param aScrollTarget when VERTICAL_SCROLLBAR or HORIZONTAL_SCROLLBAR
    * is set in the flags, this parameter should be the ViewID of the
    * scrollable content this scrollbar is for.
    */
   nsDisplayOwnLayer(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
-                    nsDisplayList* aList, uint32_t aFlags = 0,
+                    nsDisplayList* aList,
+                    const ActiveScrolledRoot* aActiveScrolledRoot,
+                    uint32_t aFlags = 0,
                     ViewID aScrollTarget = mozilla::layers::FrameMetrics::NULL_SCROLL_ID,
                     float aScrollbarThumbRatio = 0.0f,
                     bool aForceActive = true);
 #ifdef NS_BUILD_REFCNT_LOGGING
   virtual ~nsDisplayOwnLayer();
 #endif
   
   virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
@@ -3927,17 +3980,18 @@ public:
 /**
  * A display item used to represent sticky position elements. The contents
  * gets its own layer and creates a stacking context, and the layer will have
  * position-related metadata set on it.
  */
 class nsDisplayStickyPosition : public nsDisplayOwnLayer {
 public:
   nsDisplayStickyPosition(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
-                          nsDisplayList* aList);
+                          nsDisplayList* aList,
+                          const ActiveScrolledRoot* aActiveScrolledRoot);
 #ifdef NS_BUILD_REFCNT_LOGGING
   virtual ~nsDisplayStickyPosition();
 #endif
 
   virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
                                              LayerManager* aManager,
                                              const ContainerLayerParameters& aContainerParameters) override;
   NS_DISPLAY_DECL_NAME("StickyPosition", TYPE_STICKY_POSITION)
@@ -3948,17 +4002,18 @@ public:
     return mozilla::LAYER_ACTIVE;
   }
   virtual bool TryMerge(nsDisplayItem* aItem) override;
 };
 
 class nsDisplayFixedPosition : public nsDisplayOwnLayer {
 public:
   nsDisplayFixedPosition(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
-                          nsDisplayList* aList);
+                         nsDisplayList* aList,
+                         const ActiveScrolledRoot* aActiveScrolledRoot);
 
   static nsDisplayFixedPosition* CreateForFixedBackground(nsDisplayListBuilder* aBuilder,
                                                           nsIFrame* aFrame,
                                                           nsDisplayBackgroundImage* aImage,
                                                           uint32_t aIndex);
 
 
 #ifdef NS_BUILD_REFCNT_LOGGING
@@ -4089,17 +4144,17 @@ public:
 private:
   int32_t mAPD, mParentAPD;
 };
 
 class nsDisplaySVGEffects: public nsDisplayWrapList {
 public:
   nsDisplaySVGEffects(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                       nsDisplayList* aList, bool aHandleOpacity,
-                      const DisplayItemScrollClip* aScrollClip);
+                      const ActiveScrolledRoot* aActiveScrolledRoot);
   nsDisplaySVGEffects(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                       nsDisplayList* aList, bool aHandleOpacity);
 #ifdef NS_BUILD_REFCNT_LOGGING
   virtual ~nsDisplaySVGEffects();
 #endif
 
   virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                                    bool* aSnap) override;
@@ -4132,17 +4187,17 @@ protected:
  */
 class nsDisplayMask : public nsDisplaySVGEffects {
 public:
   typedef mozilla::layers::ImageLayer ImageLayer;
   typedef class mozilla::gfx::DrawTarget DrawTarget;
 
   nsDisplayMask(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                 nsDisplayList* aList, bool aHandleOpacity,
-                const DisplayItemScrollClip* aScrollClip);
+                const ActiveScrolledRoot* aActiveScrolledRoot);
 #ifdef NS_BUILD_REFCNT_LOGGING
   virtual ~nsDisplayMask();
 #endif
 
   NS_DISPLAY_DECL_NAME("Mask", TYPE_MASK)
 
   virtual bool TryMerge(nsDisplayItem* aItem) override;
   virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
--- a/layout/printing/crashtests/crashtests.list
+++ b/layout/printing/crashtests/crashtests.list
@@ -1,4 +1,4 @@
 load 509839-1.html
 load 509839-2.html
-asserts-if(browserIsRemote,4) asserts-if(stylo,2) load 576878.xhtml # bug 1324645
+load 576878.xhtml
 load 793844.html
--- a/layout/xul/nsBoxFrame.cpp
+++ b/layout/xul/nsBoxFrame.cpp
@@ -1339,16 +1339,21 @@ nsBoxFrame::BuildDisplayList(nsDisplayLi
     destination.BorderBackground()->AppendNewToTop(new (aBuilder)
       nsDisplayGeneric(aBuilder, this, PaintXULDebugBackground,
                        "XULDebugBackground"));
     destination.Outlines()->AppendNewToTop(new (aBuilder)
       nsDisplayXULDebug(aBuilder, this));
   }
 #endif
 
+  Maybe<nsDisplayListBuilder::AutoContainerASRTracker> contASRTracker;
+  if (forceLayer) {
+    contASRTracker.emplace(aBuilder);
+  }
+
   BuildDisplayListForChildren(aBuilder, aDirtyRect, destination);
 
   // see if we have to draw a selection frame around this container
   DisplaySelectionOverlay(aBuilder, destination.Content());
 
   if (forceLayer) {
     // This is a bit of a hack. Collect up all descendant display items
     // and merge them into a single Content() list. This can cause us
@@ -1357,19 +1362,24 @@ nsBoxFrame::BuildDisplayList(nsDisplayLi
     nsDisplayList masterList;
     masterList.AppendToTop(tempLists.BorderBackground());
     masterList.AppendToTop(tempLists.BlockBorderBackgrounds());
     masterList.AppendToTop(tempLists.Floats());
     masterList.AppendToTop(tempLists.Content());
     masterList.AppendToTop(tempLists.PositionedDescendants());
     masterList.AppendToTop(tempLists.Outlines());
 
+    const ActiveScrolledRoot* ownLayerASR = contASRTracker->GetContainerASR();
+
+    DisplayListClipState::AutoSaveRestore ownLayerClipState(aBuilder);
+    ownLayerClipState.ClearUpToASR(ownLayerASR);
+
     // Wrap the list to make it its own layer
     aLists.Content()->AppendNewToTop(new (aBuilder)
-      nsDisplayOwnLayer(aBuilder, this, &masterList));
+      nsDisplayOwnLayer(aBuilder, this, &masterList, ownLayerASR));
   }
 }
 
 void
 nsBoxFrame::BuildDisplayListForChildren(nsDisplayListBuilder*   aBuilder,
                                         const nsRect&           aDirtyRect,
                                         const nsDisplayListSet& aLists)
 {
--- a/layout/xul/nsSliderFrame.cpp
+++ b/layout/xul/nsSliderFrame.cpp
@@ -358,32 +358,37 @@ nsSliderFrame::BuildDisplayListForChildr
     uint32_t flags = 0;
     mozilla::layers::FrameMetrics::ViewID scrollTargetId =
       mozilla::layers::FrameMetrics::NULL_SCROLL_ID;
     aBuilder->GetScrollbarInfo(&scrollTargetId, &flags);
     bool thumbGetsLayer = (scrollTargetId != layers::FrameMetrics::NULL_SCROLL_ID);
     nsLayoutUtils::SetScrollbarThumbLayerization(thumb, thumbGetsLayer);
 
     if (thumbGetsLayer) {
+      nsDisplayListBuilder::AutoContainerASRTracker contASRTracker(aBuilder);
       nsDisplayListCollection tempLists;
       nsBoxFrame::BuildDisplayListForChildren(aBuilder, aDirtyRect, tempLists);
 
       // This is a bit of a hack. Collect up all descendant display items
       // and merge them into a single Content() list.
       nsDisplayList masterList;
       masterList.AppendToTop(tempLists.BorderBackground());
       masterList.AppendToTop(tempLists.BlockBorderBackgrounds());
       masterList.AppendToTop(tempLists.Floats());
       masterList.AppendToTop(tempLists.Content());
       masterList.AppendToTop(tempLists.PositionedDescendants());
       masterList.AppendToTop(tempLists.Outlines());
 
       // Wrap the list to make it its own layer.
+      const ActiveScrolledRoot* ownLayerASR = contASRTracker.GetContainerASR();
+      DisplayListClipState::AutoSaveRestore ownLayerClipState(aBuilder);
+      ownLayerClipState.ClearUpToASR(ownLayerASR);
       aLists.Content()->AppendNewToTop(new (aBuilder)
-        nsDisplayOwnLayer(aBuilder, this, &masterList, flags, scrollTargetId,
+        nsDisplayOwnLayer(aBuilder, this, &masterList, ownLayerASR,
+                          flags, scrollTargetId,
                           GetThumbRatio()));
 
       return;
     }
   }
   
   nsBoxFrame::BuildDisplayListForChildren(aBuilder, aDirtyRect, aLists);
 }