Bug 1413073 - Part 3: Recompute ASR and clip-chains on wrap lists when merging. r=mstange
authorMatt Woodrow <mwoodrow@mozilla.com>
Fri, 10 Nov 2017 13:16:55 +1300
changeset 391142 9d9e227c3a5e054cbcafeae96bdab938723389cb
parent 391141 d5a89d7d8163a04e47f71c6d43e404b17da4c3f3
child 391143 864174ac0707207f7fc698ed6c3c47c043e99395
child 391251 05a3c8d0c61a4a41bc735364082930d56b9eb18b
push id32860
push userebalazs@mozilla.com
push dateFri, 10 Nov 2017 09:56:38 +0000
treeherdermozilla-central@864174ac0707 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmstange
bugs1413073
milestone58.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1413073 - Part 3: Recompute ASR and clip-chains on wrap lists when merging. r=mstange
layout/generic/nsCanvasFrame.cpp
layout/generic/nsFrame.cpp
layout/painting/DisplayListClipState.cpp
layout/painting/DisplayListClipState.h
layout/painting/RetainedDisplayListBuilder.cpp
layout/painting/RetainedDisplayListBuilder.h
layout/painting/nsDisplayList.cpp
layout/painting/nsDisplayList.h
layout/reftests/display-list/1413073-ref.html
layout/reftests/display-list/1413073.html
layout/reftests/display-list/reftest.list
layout/xul/nsBoxFrame.cpp
--- a/layout/generic/nsCanvasFrame.cpp
+++ b/layout/generic/nsCanvasFrame.cpp
@@ -577,29 +577,27 @@ nsCanvasFrame::BuildDisplayList(nsDispla
       } else {
         nsDisplayCanvasBackgroundImage* bgItem = new (aBuilder) nsDisplayCanvasBackgroundImage(bgData);
         bgItem->SetDependentFrame(aBuilder, dependentFrame);
         thisItemList.AppendNewToTop(bgItem);
       }
 
       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,
                                             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(),
                                                               containerASR));
     }
   }
 
   for (nsIFrame* kid : PrincipalChildList()) {
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -3093,17 +3093,16 @@ nsIFrame::BuildDisplayListForStackingCon
    * 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.ClearUpToASR(containerItemASR);
     resultList.AppendNewToTop(
       nsDisplayBlendContainer::CreateForMixBlendMode(aBuilder, this, &resultList,
                                                      containerItemASR));
     if (aCreatedContainerItem) {
       *aCreatedContainerItem = true;
     }
   }
 
@@ -3132,17 +3131,16 @@ 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.ClearUpToASR(containerItemASR);
       // The mask should move with aBuilder->CurrentActiveScrolledRoot(), so
       // that's the ASR we prefer to use for the mask item. However, we can
       // only do this if the mask if clipped with respect to that ASR, because
       // an item always needs to have finite bounds with respect to its ASR.
       // If we weren't able to compute a clip for the mask, we fall back to
       // using containerItemASR, which is the lowest common ancestor clip of
       // the mask's contents. That's not entirely crrect, but it satisfies
       // the base requirement of the ASR system (that items have finite bounds
@@ -3168,17 +3166,16 @@ nsIFrame::BuildDisplayListForStackingCon
   /* If the list is non-empty and there is CSS group opacity without SVG
    * effects, wrap it up in an opacity item.
    */
   if (useOpacity) {
     // 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.ClearUpToASR(containerItemASR);
     resultList.AppendNewToTop(
         new (aBuilder) nsDisplayOpacity(aBuilder, this, &resultList,
                                         containerItemASR,
                                         opacityItemForEventsAndPluginsOnly));
     if (aCreatedContainerItem) {
       *aCreatedContainerItem = true;
     }
   }
@@ -3311,17 +3308,16 @@ nsIFrame::BuildDisplayListForStackingCon
 
   /* 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) {
     DisplayListClipState::AutoSaveRestore blendModeClipState(aBuilder);
-    blendModeClipState.ClearUpToASR(containerItemASR);
     resultList.AppendNewToTop(
         new (aBuilder) nsDisplayBlendMode(aBuilder, this, &resultList,
                                           effects->mMixBlendMode,
                                           containerItemASR));
     if (aCreatedContainerItem) {
       *aCreatedContainerItem = true;
     }
   }
@@ -3343,17 +3339,19 @@ WrapInWrapList(nsDisplayListBuilder* aBu
   }
 
   if (aCanSkipWrapList) {
     MOZ_ASSERT(!item->GetAbove());
     aList->RemoveBottom();
     return item;
   }
 
-  return new (aBuilder) nsDisplayWrapList(aBuilder, aFrame, aList, aContainerASR);
+  // 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.
+  return new (aBuilder) nsDisplayWrapList(aBuilder, aFrame, aList, aContainerASR, true);
 }
 
 /**
  * Check if a frame should be visited for building display list.
  */
 static bool
 DescendIntoChild(nsDisplayListBuilder* aBuilder, nsIFrame *aChild,
                  const nsRect& aVisible, const nsRect& aDirty)
@@ -3729,20 +3727,16 @@ nsIFrame::BuildDisplayListForChild(nsDis
     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.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, wrapListASR, canSkipWrapList);
       if (isSVG) {
         aLists.Content()->AppendNewToTop(item);
--- a/layout/painting/DisplayListClipState.cpp
+++ b/layout/painting/DisplayListClipState.cpp
@@ -5,30 +5,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "DisplayListClipState.h"
 
 #include "nsDisplayList.h"
 
 namespace mozilla {
 
-void
-DisplayListClipState::ClearUpToASR(const ActiveScrolledRoot* aASR)
-{
-  while (mClipChainContentDescendants &&
-         ActiveScrolledRoot::IsAncestor(aASR, mClipChainContentDescendants->mASR)) {
-    mClipChainContentDescendants = mClipChainContentDescendants->mParent;
-  }
-  while (mClipChainContainingBlockDescendants &&
-         ActiveScrolledRoot::IsAncestor(aASR, mClipChainContainingBlockDescendants->mASR)) {
-    mClipChainContainingBlockDescendants = mClipChainContainingBlockDescendants->mParent;
-  }
-  InvalidateCurrentCombinedClipChain(aASR);
-}
-
 const DisplayItemClipChain*
 DisplayListClipState::GetCurrentCombinedClipChain(nsDisplayListBuilder* aBuilder)
 {
   if (mCurrentCombinedClipChainIsValid) {
     return mCurrentCombinedClipChain;
   }
   if (!mClipChainContentDescendants && !mClipChainContainingBlockDescendants) {
     mCurrentCombinedClipChain = nullptr;
--- a/layout/painting/DisplayListClipState.h
+++ b/layout/painting/DisplayListClipState.h
@@ -69,18 +69,16 @@ private:
   void Clear()
   {
     mClipChainContentDescendants = nullptr;
     mClipChainContainingBlockDescendants = nullptr;
     mCurrentCombinedClipChain = nullptr;
     mCurrentCombinedClipChainIsValid = false;
   }
 
-  void ClearUpToASR(const ActiveScrolledRoot* aASR);
-
   void SetClipChainForContainingBlockDescendants(const DisplayItemClipChain* aClipChain)
   {
     mClipChainContainingBlockDescendants = aClipChain;
     InvalidateCurrentCombinedClipChain(aClipChain ? aClipChain->mASR : nullptr);
   }
 
   /**
    * Intersects the given clip rect (with optional aRadii) with the current
@@ -167,25 +165,16 @@ public:
   {
     NS_ASSERTION(!mRestored, "Already restored!");
     mState.Clear();
 #ifdef DEBUG
     mClipUsed = false;
 #endif
   }
 
-  void ClearUpToASR(const ActiveScrolledRoot* aASR)
-  {
-    NS_ASSERTION(!mRestored, "Already restored!");
-    mState.ClearUpToASR(aASR);
-#ifdef DEBUG
-    mClipUsed = false;
-#endif
-  }
-
   void SetClipChainForContainingBlockDescendants(const DisplayItemClipChain* aClipChain)
   {
     mState.SetClipChainForContainingBlockDescendants(aClipChain);
   }
 
   /**
    * Intersects the given clip rect (with optional aRadii) with the current
    * mClipContainingBlockDescendants and sets mClipContainingBlockDescendants to
--- a/layout/painting/RetainedDisplayListBuilder.cpp
+++ b/layout/painting/RetainedDisplayListBuilder.cpp
@@ -277,16 +277,30 @@ RetainedDisplayListBuilder::IncrementSub
   MOZ_ASSERT(subDocFrame);
 
   nsIPresShell* presShell = subDocFrame->GetSubdocumentPresShellForPainting(0);
   MOZ_ASSERT(presShell);
 
   mBuilder.IncrementPresShellPaintCount(presShell);
 }
 
+void UpdateASR(nsDisplayItem* aItem,
+               const ActiveScrolledRoot* aContainerASR)
+{
+  nsDisplayWrapList* wrapList = aItem->AsDisplayWrapList();
+  if (!wrapList) {
+    aItem->SetActiveScrolledRoot(aContainerASR);
+    return;
+  }
+
+  wrapList->SetActiveScrolledRoot(
+    ActiveScrolledRoot::PickAncestor(wrapList->GetFrameActiveScrolledRoot(),
+                                     aContainerASR));
+}
+
 /**
  * Takes two display lists and merges them into an output list.
  *
  * The basic algorithm is:
  *
  * For-each item in the new list:
  *     If the item has a matching item in the old list:
  *         Remove items from the bottom of the old list until we reach the matching item:
@@ -327,21 +341,35 @@ RetainedDisplayListBuilder::IncrementSub
  * would have invalidated A), so we detect it and just destroy the new
  * instance of A.
  *
  * Merged List: A, B
  */
 void
 RetainedDisplayListBuilder::MergeDisplayLists(nsDisplayList* aNewList,
                                               nsDisplayList* aOldList,
-                                              nsDisplayList* aOutList)
+                                              nsDisplayList* aOutList,
+                                              const ActiveScrolledRoot** aOutContainerASR)
 {
   nsDisplayList merged(&mBuilder);
+  const ActiveScrolledRoot* containerASR = nullptr;
 
   const auto ReuseItem = [&](nsDisplayItem* aItem) {
+    const ActiveScrolledRoot* itemClipASR =
+      aItem->GetClipChain() ? aItem->GetClipChain()->mASR : nullptr;
+
+    const ActiveScrolledRoot* finiteBoundsASR = ActiveScrolledRoot::PickDescendant(
+      itemClipASR, aItem->GetActiveScrolledRoot());
+    if (merged.IsEmpty()) {
+      containerASR = finiteBoundsASR;
+    } else {
+      containerASR =
+        ActiveScrolledRoot::PickAncestor(containerASR, finiteBoundsASR);
+    }
+
     merged.AppendToTop(aItem);
     aItem->SetReused(true);
 
     if (aItem->GetType() == DisplayItemType::TYPE_SUBDOCUMENT) {
       IncrementSubDocPresShellPaintCount(aItem);
     }
   };
 
@@ -375,35 +403,40 @@ RetainedDisplayListBuilder::MergeDisplay
     if (nsDisplayItem* oldItem = oldListLookup.Get({ newItem->Frame(), newItem->GetPerFrameKey() })) {
       if (oldItem->IsReused()) {
         // If there's a matching item in the old list, but we've already put it into the
         // merged list then stick with that one. Merge any child lists, and then delete the
         // new item. This solves example 2 from above.
 
         if (oldItem->GetChildren()) {
           MOZ_ASSERT(newItem->GetChildren());
-          MergeDisplayLists(newItem->GetChildren(), oldItem->GetChildren(), oldItem->GetChildren());
+          const ActiveScrolledRoot* containerASRForChildren;
+          MergeDisplayLists(newItem->GetChildren(), oldItem->GetChildren(),
+                            oldItem->GetChildren(), &containerASRForChildren);
+          UpdateASR(oldItem, containerASRForChildren);
           oldItem->UpdateBounds(&mBuilder);
         }
         if (oldItem->GetType() == DisplayItemType::TYPE_LAYER_EVENT_REGIONS) {
           MergeLayerEventRegions(oldItem, newItem);
         }
         newItem->Destroy(&mBuilder);
       } else {
         // The new item has a matching counterpart in the old list, so copy all valid
         // items from the old list into the merged list until we get to the matched item.
         nsDisplayItem* old = nullptr;
         while ((old = aOldList->RemoveBottom()) && !IsSameItem(newItem, old)) {
           if (!IsAnyAncestorModified(old->FrameForInvalidation())) {
             // Recurse into the child list (without a matching new list) to
             // ensure that we find and remove any invalidated items.
             if (old->GetChildren()) {
               nsDisplayList empty(&mBuilder);
+              const ActiveScrolledRoot* containerASRForChildren;
               MergeDisplayLists(&empty, old->GetChildren(),
-                                old->GetChildren());
+                                old->GetChildren(), &containerASRForChildren);
+              UpdateASR(old, containerASRForChildren);
               old->UpdateBounds(&mBuilder);
             }
             ReuseItem(old);
           } else {
             oldListLookup.Remove({ old->Frame(), old->GetPerFrameKey() });
             old->Destroy(&mBuilder);
           }
         }
@@ -420,17 +453,20 @@ RetainedDisplayListBuilder::MergeDisplay
           // likely to have the bigger lists and merging will be quicker.
           MergeLayerEventRegions(old, newItem);
           ReuseItem(old);
           newItem->Destroy(&mBuilder);
         } else {
           if (!IsAnyAncestorModified(old->FrameForInvalidation()) &&
               old->GetChildren()) {
             MOZ_ASSERT(newItem->GetChildren());
-            MergeDisplayLists(newItem->GetChildren(), old->GetChildren(), newItem->GetChildren());
+            const ActiveScrolledRoot* containerASRForChildren;
+            MergeDisplayLists(newItem->GetChildren(), old->GetChildren(),
+                              newItem->GetChildren(), &containerASRForChildren);
+            UpdateASR(newItem, containerASRForChildren);
             newItem->UpdateBounds(&mBuilder);
           }
 
           old->Destroy(&mBuilder);
           merged.AppendToTop(newItem);
         }
       }
     } else {
@@ -438,37 +474,42 @@ RetainedDisplayListBuilder::MergeDisplay
       // add the new item to the merged list.
       merged.AppendToTop(newItem);
     }
   }
 
   // Reuse the remaining valid items from the old display list.
   while (nsDisplayItem* old = aOldList->RemoveBottom()) {
     if (!IsAnyAncestorModified(old->FrameForInvalidation())) {
-      ReuseItem(old);
-
       if (old->GetChildren()) {
         // We are calling MergeDisplayLists() to ensure that the display items
         // with modified or deleted children will be correctly handled.
         // Passing an empty new display list as an argument skips the merging
         // loop above and jumps back here.
         nsDisplayList empty(&mBuilder);
+        const ActiveScrolledRoot* containerASRForChildren;
 
-        MergeDisplayLists(&empty, old->GetChildren(), old->GetChildren());
+        MergeDisplayLists(&empty, old->GetChildren(),
+                          old->GetChildren(), &containerASRForChildren);
+        UpdateASR(old, containerASRForChildren);
         old->UpdateBounds(&mBuilder);
       }
       if (old->GetType() == DisplayItemType::TYPE_LAYER_EVENT_REGIONS) {
         MergeLayerEventRegions(old, nullptr);
       }
+      ReuseItem(old);
     } else {
       old->Destroy(&mBuilder);
     }
   }
 
   aOutList->AppendToTop(&merged);
+  if (aOutContainerASR) {
+    *aOutContainerASR = containerASR;
+  }
 }
 
 static void
 TakeAndAddModifiedFramesFromRootFrame(nsTArray<nsIFrame*>& aFrames,
                                       nsIFrame* aRootFrame)
 {
   MOZ_ASSERT(aRootFrame);
 
@@ -782,17 +823,17 @@ RetainedDisplayListBuilder::AttemptParti
       // printf_stderr("Skipping display list building since nothing needed to be done\n");
     }
 
     // |modifiedDL| can sometimes be empty here. We still perform the
     // display list merging to prune unused items (for example, items that
     // are not visible anymore) from the old list.
     // TODO: Optimization opportunity. In this case, MergeDisplayLists()
     // unnecessarily creates a hashtable of the old items.
-    MergeDisplayLists(&modifiedDL, &mList, &mList);
+    MergeDisplayLists(&modifiedDL, &mList, &mList, nullptr);
 
     //printf_stderr("Painting --- Merged list:\n");
     //nsFrame::PrintDisplayList(&mBuilder, mList);
 
     merged = true;
   }
 
   mBuilder.LeavePresShell(mBuilder.RootReferenceFrame(), &mList);
--- a/layout/painting/RetainedDisplayListBuilder.h
+++ b/layout/painting/RetainedDisplayListBuilder.h
@@ -29,17 +29,18 @@ struct RetainedDisplayListBuilder {
 
   NS_DECLARE_FRAME_PROPERTY_DELETABLE(Cached, RetainedDisplayListBuilder)
 
 private:
   void PreProcessDisplayList(nsDisplayList* aList, AnimatedGeometryRoot* aAGR);
 
   void MergeDisplayLists(nsDisplayList* aNewList,
                          nsDisplayList* aOldList,
-                         nsDisplayList* aOutList);
+                         nsDisplayList* aOutList,
+                         const mozilla::ActiveScrolledRoot** aOutContainerASR = nullptr);
 
   bool ComputeRebuildRegion(nsTArray<nsIFrame*>& aModifiedFrames,
                             nsRect* aOutDirty,
                             AnimatedGeometryRoot** aOutModifiedAGR,
                             nsTArray<nsIFrame*>* aOutFramesWithProps);
 
   void IncrementSubDocPresShellPaintCount(nsDisplayItem* aItem);
 
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -3674,31 +3674,29 @@ nsDisplayBackgroundImage::AppendBackgrou
         bgItem = new (aBuilder) nsDisplayBackgroundImage(bgData);
       }
       bgItem->SetDependentFrame(aBuilder, dependentFrame);
       thisItemList.AppendNewToTop(bgItem);
     }
 
     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,
                                           asr, i + 1));
     }
     bgItemList.AppendToTop(&thisItemList);
   }
 
   if (needBlendContainer) {
     DisplayListClipState::AutoSaveRestore blendContainerClip(aBuilder);
-    blendContainerClip.ClearUpToASR(asr);
     bgItemList.AppendNewToTop(
       nsDisplayBlendContainer::CreateForBackgroundBlendMode(aBuilder, aFrame, &bgItemList, asr));
   }
 
   aList->AppendToTop(&bgItemList);
   return false;
 }
 
@@ -5924,21 +5922,24 @@ nsDisplayBoxShadowInner::ComputeVisibili
 nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder* aBuilder,
                                      nsIFrame* aFrame, nsDisplayList* aList)
   : nsDisplayWrapList(aBuilder, aFrame, aList,
                       aBuilder->CurrentActiveScrolledRoot())
 {}
 
 nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder* aBuilder,
                                      nsIFrame* aFrame, nsDisplayList* aList,
-                                     const ActiveScrolledRoot* aActiveScrolledRoot)
+                                     const ActiveScrolledRoot* aActiveScrolledRoot,
+                                     bool aClearClipChain)
   : nsDisplayItem(aBuilder, aFrame, aActiveScrolledRoot)
   , mList(aBuilder)
+  , mFrameActiveScrolledRoot(aBuilder->CurrentActiveScrolledRoot())
   , mOverrideZIndex(0)
   , mHasZIndexOverride(false)
+  , mClearingClipChain(aClearClipChain)
 {
   MOZ_COUNT_CTOR(nsDisplayWrapList);
 
   mBaseVisibleRect = mVisibleRect;
 
   mListPtr = &mList;
   mListPtr->AppendToTop(aList);
   UpdateBounds(aBuilder);
@@ -6246,17 +6247,17 @@ nsresult nsDisplayWrapper::WrapListsInPl
   // The outlines may not be in-flow
   return WrapEachDisplayItem(aBuilder, aLists.Outlines(), this);
 }
 
 nsDisplayOpacity::nsDisplayOpacity(nsDisplayListBuilder* aBuilder,
                                    nsIFrame* aFrame, nsDisplayList* aList,
                                    const ActiveScrolledRoot* aActiveScrolledRoot,
                                    bool aForEventsAndPluginsOnly)
-    : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot)
+    : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot, true)
     , mOpacity(aFrame->StyleEffects()->mOpacity)
     , mForEventsAndPluginsOnly(aForEventsAndPluginsOnly)
 {
   MOZ_COUNT_CTOR(nsDisplayOpacity);
   mState.mOpacity = mOpacity;
 }
 
 #ifdef NS_BUILD_REFCNT_LOGGING
@@ -6549,17 +6550,17 @@ nsDisplayOpacity::CreateWebRenderCommand
   return true;
 }
 
 nsDisplayBlendMode::nsDisplayBlendMode(nsDisplayListBuilder* aBuilder,
                                              nsIFrame* aFrame, nsDisplayList* aList,
                                              uint8_t aBlendMode,
                                              const ActiveScrolledRoot* aActiveScrolledRoot,
                                              uint32_t aIndex)
-  : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot)
+  : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot, true)
   , mBlendMode(aBlendMode)
   , mIndex(aIndex)
 {
   MOZ_COUNT_CTOR(nsDisplayBlendMode);
 }
 
 #ifdef NS_BUILD_REFCNT_LOGGING
 nsDisplayBlendMode::~nsDisplayBlendMode() {
@@ -6667,17 +6668,17 @@ nsDisplayBlendContainer::CreateForBackgr
 {
   return new (aBuilder) nsDisplayBlendContainer(aBuilder, aFrame, aList, aActiveScrolledRoot, true);
 }
 
 nsDisplayBlendContainer::nsDisplayBlendContainer(nsDisplayListBuilder* aBuilder,
                                                  nsIFrame* aFrame, nsDisplayList* aList,
                                                  const ActiveScrolledRoot* aActiveScrolledRoot,
                                                  bool aIsForBackground)
-    : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot)
+    : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot, true)
     , mIsForBackground(aIsForBackground)
 {
   MOZ_COUNT_CTOR(nsDisplayBlendContainer);
 }
 
 #ifdef NS_BUILD_REFCNT_LOGGING
 nsDisplayBlendContainer::~nsDisplayBlendContainer() {
   MOZ_COUNT_DTOR(nsDisplayBlendContainer);
@@ -6726,18 +6727,19 @@ nsDisplayBlendContainer::CreateWebRender
                                                     aManager, aDisplayListBuilder);
 }
 
 nsDisplayOwnLayer::nsDisplayOwnLayer(nsDisplayListBuilder* aBuilder,
                                      nsIFrame* aFrame, nsDisplayList* aList,
                                      const ActiveScrolledRoot* aActiveScrolledRoot,
                                      uint32_t aFlags, ViewID aScrollTarget,
                                      const ScrollThumbData& aThumbData,
-                                     bool aForceActive)
-    : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot)
+                                     bool aForceActive,
+                                     bool aClearClipChain)
+    : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot, aClearClipChain)
     , mFlags(aFlags)
     , mScrollTarget(aScrollTarget)
     , mThumbData(aThumbData)
     , mForceActive(aForceActive)
     , mWrAnimationId(0)
 {
   MOZ_COUNT_CTOR(nsDisplayOwnLayer);
 
@@ -9021,18 +9023,19 @@ nsCharClipDisplayItem::ComputeInvalidati
       !geometry->mBorderRect.IsEqualInterior(GetBorderRect())) {
     aInvalidRegion->Or(oldRect, newRect);
   }
 }
 
 nsDisplaySVGEffects::nsDisplaySVGEffects(nsDisplayListBuilder* aBuilder,
                                          nsIFrame* aFrame, nsDisplayList* aList,
                                          bool aHandleOpacity,
-                                         const ActiveScrolledRoot* aActiveScrolledRoot)
-  : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot)
+                                         const ActiveScrolledRoot* aActiveScrolledRoot,
+                                         bool aClearClipChain)
+  : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot, aClearClipChain)
   , mHandleOpacity(aHandleOpacity)
 {
   MOZ_COUNT_CTOR(nsDisplaySVGEffects);
 }
 
 nsDisplaySVGEffects::nsDisplaySVGEffects(nsDisplayListBuilder* aBuilder,
                                          nsIFrame* aFrame, nsDisplayList* aList,
                                          bool aHandleOpacity)
@@ -9205,17 +9208,17 @@ ComputeMaskGeometry(PaintFramesParams& a
   IntRect result = ComputeClipExtsInDeviceSpace(ctx);
   aParams.maskRect = result;
 }
 
 nsDisplayMask::nsDisplayMask(nsDisplayListBuilder* aBuilder,
                              nsIFrame* aFrame, nsDisplayList* aList,
                              bool aHandleOpacity,
                              const ActiveScrolledRoot* aActiveScrolledRoot)
-  : nsDisplaySVGEffects(aBuilder, aFrame, aList, aHandleOpacity, aActiveScrolledRoot)
+  : nsDisplaySVGEffects(aBuilder, aFrame, aList, aHandleOpacity, aActiveScrolledRoot, true)
 {
   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) {
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -1936,16 +1936,17 @@ public:
       mFrame = nullptr;
     }
   }
 
   /**
    * Downcasts this item to nsDisplayWrapList, if possible.
    */
   virtual const nsDisplayWrapList* AsDisplayWrapList() const { return nullptr; }
+  virtual nsDisplayWrapList* AsDisplayWrapList() { return nullptr; }
 
   /**
    * Create a clone of this item.
    */
   virtual nsDisplayItem* Clone(nsDisplayListBuilder* aBuilder) const
   {
     return nullptr;
   }
@@ -2578,17 +2579,17 @@ public:
   virtual bool SupportsOptimizingToImage() const { return false; }
 
   const DisplayItemClip& GetClip() const
   {
     return mClip ? *mClip : DisplayItemClip::NoClip();
   }
   void IntersectClip(nsDisplayListBuilder* aBuilder, const DisplayItemClipChain* aOther, bool aStore);
 
-  void SetActiveScrolledRoot(const ActiveScrolledRoot* aActiveScrolledRoot) { mActiveScrolledRoot = aActiveScrolledRoot; }
+  virtual void SetActiveScrolledRoot(const ActiveScrolledRoot* aActiveScrolledRoot) { mActiveScrolledRoot = aActiveScrolledRoot; }
   const ActiveScrolledRoot* GetActiveScrolledRoot() const { return mActiveScrolledRoot; }
 
   virtual void SetClipChain(const DisplayItemClipChain* aClipChain,
                             bool aStore);
   const DisplayItemClipChain* GetClipChain() const { return mClipChain; }
 
   /**
    * Intersect all clips in our clip chain up to (and including) aASR and set
@@ -4566,22 +4567,24 @@ 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 ActiveScrolledRoot* aActiveScrolledRoot);
+                    const ActiveScrolledRoot* aActiveScrolledRoot,
+                    bool aClearClipChain = false);
   nsDisplayWrapList(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                     nsDisplayItem* aItem);
   nsDisplayWrapList(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
     : nsDisplayItem(aBuilder, aFrame)
     , mList(aBuilder)
+    , mFrameActiveScrolledRoot(aBuilder->CurrentActiveScrolledRoot())
     , mOverrideZIndex(0)
     , mHasZIndexOverride(false)
   {
     MOZ_COUNT_CTOR(nsDisplayWrapList);
     mBaseVisibleRect = mVisibleRect;
     mListPtr = &mList;
   }
 
@@ -4589,31 +4592,37 @@ public:
    * A custom copy-constructor that does not copy mList, as this would mutate
    * the other item.
    */
   nsDisplayWrapList(const nsDisplayWrapList& aOther) = delete;
   nsDisplayWrapList(nsDisplayListBuilder* aBuilder, const nsDisplayWrapList& aOther)
     : nsDisplayItem(aBuilder, aOther)
     , mList(aOther.mList.mBuilder)
     , mListPtr(&mList)
+    , mFrameActiveScrolledRoot(aOther.mFrameActiveScrolledRoot)
     , mMergedFrames(aOther.mMergedFrames)
     , mBounds(aOther.mBounds)
     , mBaseVisibleRect(aOther.mBaseVisibleRect)
     , mOverrideZIndex(aOther.mOverrideZIndex)
     , mHasZIndexOverride(aOther.mHasZIndexOverride)
+    , mClearingClipChain(aOther.mClearingClipChain)
   {
     MOZ_COUNT_CTOR(nsDisplayWrapList);
   }
 
   virtual ~nsDisplayWrapList();
 
   virtual const nsDisplayWrapList* AsDisplayWrapList() const override
   {
     return this;
   }
+  virtual nsDisplayWrapList* AsDisplayWrapList() override
+  {
+    return this;
+  }
 
   virtual void Destroy(nsDisplayListBuilder* aBuilder) override {
     mList.DeleteAll(aBuilder);
     nsDisplayItem::Destroy(aBuilder);
   }
 
   /**
    * Creates a new nsDisplayWrapList that holds a pointer to the display list
@@ -4623,16 +4632,26 @@ public:
   virtual void MergeDisplayListFromItem(nsDisplayListBuilder* aBuilder,
                                         const nsDisplayItem* aItem) override;
 
   /**
    * Call this if the wrapped list is changed.
    */
   virtual void UpdateBounds(nsDisplayListBuilder* aBuilder) override
   {
+    // Clear the clip chain up to the asr, but don't store it, so that we'll recover
+    // it when we reuse the item.
+    if (mClearingClipChain) {
+      const DisplayItemClipChain* clip = mState.mClipChain;
+      while (clip && ActiveScrolledRoot::IsAncestor(GetActiveScrolledRoot(), clip->mASR)) {
+        clip = clip->mParent;
+      }
+      SetClipChain(clip, false);
+    }
+
     nsRect visibleRect;
     mBounds =
       mListPtr->GetClippedBoundsWithRespectToASR(aBuilder, mActiveScrolledRoot, &visibleRect);
     // 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
@@ -4730,38 +4749,44 @@ public:
   }
 
   virtual bool CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
                                        mozilla::wr::IpcResourceUpdateQueue& aResources,
                                        const StackingContextHelper& aSc,
                                        mozilla::layers::WebRenderLayerManager* aManager,
                                        nsDisplayListBuilder* aDisplayListBuilder) override;
 
+  const ActiveScrolledRoot* GetFrameActiveScrolledRoot() { return mFrameActiveScrolledRoot; }
+
 protected:
   nsDisplayWrapList() = delete;
 
   void MergeFromTrackingMergedFrames(const nsDisplayWrapList* aOther)
   {
     mBounds.UnionRect(mBounds, aOther->mBounds);
     mVisibleRect.UnionRect(mVisibleRect, aOther->mVisibleRect);
     mMergedFrames.AppendElement(aOther->mFrame);
     mMergedFrames.AppendElements(aOther->mMergedFrames);
   }
 
   nsDisplayList mList;
   nsDisplayList* mListPtr;
+  // The active scrolled root for the frame that created this
+  // wrap list.
+  RefPtr<const ActiveScrolledRoot> mFrameActiveScrolledRoot;
   // The frames from items that have been merged into this item, excluding
   // this item's own frame.
   nsTArray<nsIFrame*> mMergedFrames;
   nsRect mBounds;
   // Visible rect contributed by this display item itself.
   // Our mVisibleRect may include the visible areas of children.
   nsRect mBaseVisibleRect;
   int32_t mOverrideZIndex;
   bool mHasZIndexOverride;
+  bool mClearingClipChain = false;
 };
 
 /**
  * We call WrapDisplayList on the in-flow lists: BorderBackground(),
  * BlockBorderBackgrounds() and Content().
  * We call WrapDisplayItem on each item of Outlines(), PositionedDescendants(),
  * and Floats(). This is done to support special wrapping processing for frames
  * that may not be in-flow descendants of the current frame.
@@ -5035,17 +5060,18 @@ public:
    * scrollable content this scrollbar is for.
    */
   nsDisplayOwnLayer(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                     nsDisplayList* aList,
                     const ActiveScrolledRoot* aActiveScrolledRoot,
                     uint32_t aFlags = 0,
                     ViewID aScrollTarget = mozilla::layers::FrameMetrics::NULL_SCROLL_ID,
                     const ScrollThumbData& aThumbData = ScrollThumbData{},
-                    bool aForceActive = true);
+                    bool aForceActive = true,
+                    bool aClearClipChain = false);
 #ifdef NS_BUILD_REFCNT_LOGGING
   virtual ~nsDisplayOwnLayer();
 #endif
   nsDisplayOwnLayer(nsDisplayListBuilder* aBuilder, const nsDisplayOwnLayer& aOther)
     : nsDisplayWrapList(aBuilder, aOther)
     , mFlags(aOther.mFlags)
     , mScrollTarget(aOther.mFlags)
     , mThumbData(aOther.mThumbData)
@@ -5428,17 +5454,18 @@ public:
 private:
   int32_t mAPD, mParentAPD;
 };
 
 class nsDisplaySVGEffects: public nsDisplayWrapList {
 public:
   nsDisplaySVGEffects(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                       nsDisplayList* aList, bool aHandleOpacity,
-                      const ActiveScrolledRoot* aActiveScrolledRoot);
+                      const ActiveScrolledRoot* aActiveScrolledRoot,
+                      bool aClearClipChain = false);
   nsDisplaySVGEffects(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                       nsDisplayList* aList, bool aHandleOpacity);
 #ifdef NS_BUILD_REFCNT_LOGGING
   virtual ~nsDisplaySVGEffects();
 #endif
 
   nsDisplaySVGEffects(nsDisplayListBuilder* aBuilder,
                       const nsDisplaySVGEffects& aOther)
@@ -5755,16 +5782,22 @@ public:
     return GetBounds(aBuilder, &snap);
   }
 
   virtual nsDisplayList* GetChildren() const override
   {
     return mStoredList.GetChildren();
   }
 
+  virtual void SetActiveScrolledRoot(const ActiveScrolledRoot* aActiveScrolledRoot) override
+  {
+    nsDisplayItem::SetActiveScrolledRoot(aActiveScrolledRoot);
+    mStoredList.SetActiveScrolledRoot(aActiveScrolledRoot);
+  }
+
   virtual void HitTest(nsDisplayListBuilder *aBuilder, const nsRect& aRect,
                        HitTestState *aState, nsTArray<nsIFrame*> *aOutFrames) override;
   virtual nsRect GetBounds(nsDisplayListBuilder *aBuilder,
                            bool* aSnap) const override;
   virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder *aBuilder,
                                    bool* aSnap) const override;
   virtual mozilla::Maybe<nscolor> IsUniform(nsDisplayListBuilder *aBuilder) const override;
   virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
@@ -6143,16 +6176,22 @@ public:
     return mList.GetChildren();
   }
 
   virtual nsDisplayList* GetChildren() const override
   {
     return mList.GetChildren();
   }
 
+  virtual void SetActiveScrolledRoot(const ActiveScrolledRoot* aActiveScrolledRoot) override
+  {
+    nsDisplayItem::SetActiveScrolledRoot(aActiveScrolledRoot);
+    mList.SetActiveScrolledRoot(aActiveScrolledRoot);
+  }
+
   virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) const override
   {
     return mList.GetComponentAlphaBounds(aBuilder);
   }
 
   nsIFrame* TransformFrame() { return mTransformFrame; }
 
   virtual int32_t ZIndex() const override;
new file mode 100644
--- /dev/null
+++ b/layout/reftests/display-list/1413073-ref.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<meta charset="utf-8">
+<title>The ASR for the opacity item is the root scroll frame instead of the subframe.</title>
+
+<style>
+
+.outer {
+  /* avoid event regions messing with our demonstration */
+  pointer-events: none;
+  /* make sure the .outer opacity item has the root scroll frame as its ASR */
+  background: rgba(0, 0, 0, 0.1);
+}
+
+.opacity {
+  opacity: 0.8;
+}
+
+.scrollFrameWrapper {
+  /* clips off .scrollFrame's scrollbar */
+  margin: 100px;
+  overflow: hidden;
+}
+
+.scrollFrame {
+  height: 300px;
+  margin-right: -20px;
+  padding-right: 20px;
+  overflow: hidden;
+}
+
+.scrolledContents {
+  height: 1000px;
+  width: 200px;
+  border: 5px solid black;
+  pointer-events: auto;
+}
+
+</style>
+
+</head><body>
+
+<div class="opacity outer">
+  <div class="opacity inner">
+    <div class="scrollFrameWrapper">
+      <div class="scrollFrame">
+        <div class="scrolledContents"></div>
+      </div>
+    </div>
+  </div>
+</div>
+</body></html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/display-list/1413073.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<html lang="en" class="reftest-wait">
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<meta charset="utf-8">
+<title>The ASR for the opacity item is the root scroll frame instead of the subframe.</title>
+
+<style>
+
+.outer {
+  /* avoid event regions messing with our demonstration */
+  pointer-events: none;
+  /* make sure the .outer opacity item has the root scroll frame as its ASR */
+  background: rgba(0, 0, 0, 0.1);
+}
+
+.opacity {
+  opacity: 0.8;
+}
+
+.scrollFrameWrapper {
+  /* clips off .scrollFrame's scrollbar */
+  margin: 100px;
+  overflow: hidden;
+}
+
+.scrollFrame {
+  height: 300px;
+  margin-right: -20px;
+  padding-right: 20px;
+  overflow: auto;
+}
+
+.scrolledContents {
+  height: 1000px;
+  width: 200px;
+  border: 5px solid black;
+  pointer-events: auto;
+}
+
+</style>
+
+</head><body>
+
+<div class="opacity outer">
+  <div class="opacity inner">
+    <div class="scrollFrameWrapper">
+      <div class="scrollFrame">
+        <div class="scrolledContents"></div>
+      </div>
+    </div>
+  </div>
+</div>
+
+<script>
+
+function doTest()
+{
+  var scrollFrame = document.querySelector('.scrollFrame');
+  scrollFrame.scrollTop = 10;
+  scrollFrame.scrollTop = 20;
+  scrollFrame.scrollTop = 0;
+
+  document.documentElement.removeAttribute("class");
+}
+
+document.addEventListener("MozReftestInvalidate", doTest);
+</script>
+</body></html>
--- a/layout/reftests/display-list/reftest.list
+++ b/layout/reftests/display-list/reftest.list
@@ -3,8 +3,9 @@ skip-if(!retainedDisplayList) == retaine
 skip-if(!retainedDisplayList) == retained-dl-frame-created-1.html retained-dl-style-change-1-ref.html
 skip-if(!retainedDisplayList) == retained-dl-style-change-stacking-context-1.html retained-dl-style-change-stacking-context-1-ref.html
 skip-if(!retainedDisplayList||!asyncPan) == retained-dl-async-scrolled-1.html retained-dl-async-scrolled-1-ref.html
 skip-if(!retainedDisplayList) == retained-dl-remove-for-ancestor-change-1.html retained-dl-remove-for-ancestor-change-1-ref.html
 skip-if(!retainedDisplayList) == retained-dl-scroll-out-of-view-1.html retained-dl-scroll-out-of-view-1-ref.html
 skip-if(!retainedDisplayList) == retained-dl-displayport-1.html retained-dl-displayport-1-ref.html
 skip-if(!retainedDisplayList) == retained-dl-prerender-transform-1.html retained-dl-prerender-transform-1-ref.html
 == retained-dl-wrap-list.html retained-dl-wrap-list-ref.html
+fuzzy(1,235200) == 1413073.html 1413073-ref.html
--- a/layout/xul/nsBoxFrame.cpp
+++ b/layout/xul/nsBoxFrame.cpp
@@ -1362,21 +1362,22 @@ nsBoxFrame::BuildDisplayList(nsDisplayLi
     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, ownLayerASR));
+      nsDisplayOwnLayer(aBuilder, this, &masterList, ownLayerASR, 0,
+                        mozilla::layers::FrameMetrics::NULL_SCROLL_ID,
+                        mozilla::layers::ScrollThumbData{}, true, true));
   }
 }
 
 void
 nsBoxFrame::BuildDisplayListForChildren(nsDisplayListBuilder*   aBuilder,
                                         const nsDisplayListSet& aLists)
 {
   nsIFrame* kid = mFrames.FirstChild();