Bug 1298218 - Use DisplayItemClipChain for tracking clips on display items. r?tnikkel, r?mattwoodrow draft
authorMarkus Stange <mstange@themasta.com>
Tue, 31 Jan 2017 17:07:35 -0500
changeset 469205 2369451d7c77c36339b9e5a3c42713d3c730ddae
parent 469204 c643997284847ec393a02139ff25ff4f200dc983
child 469206 a5e52ee4e4a2d6401dc98d958d40de9465f4b3ee
push id43652
push userbmo:mstange@themasta.com
push dateWed, 01 Feb 2017 21:18:30 +0000
reviewerstnikkel, mattwoodrow
bugs1298218
milestone54.0a1
Bug 1298218 - Use DisplayItemClipChain for tracking clips on display items. r?tnikkel, r?mattwoodrow 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,
@@ -10051,17 +10058,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);
 }