merge mozilla-central to mozilla-inbound
authorSebastian Hengst <archaeopteryx@coole-files.de>
Tue, 03 Apr 2018 20:06:28 +0300
changeset 776788 409ba4966a52f261daf4aa0d642cb969bb611e98
parent 776787 b59e901d20ce0948ffcfa077e8deddd8aeddff00 (current diff)
parent 776767 a1fb8ffae378963b128deaaf3a76eff9dbb6be21 (diff)
child 776789 05669ce25b032bf83ca38e082e6f2c1bf683ed19
push id104994
push usermaglione.k@gmail.com
push dateTue, 03 Apr 2018 18:21:08 +0000
milestone61.0a1
merge mozilla-central to mozilla-inbound
layout/base/crashtests/1443027-1.html
layout/painting/RetainedDisplayListHelpers.h
layout/reftests/display-list/1443027-1.html
layout/reftests/display-list/1443027-2.html
layout/reftests/display-list/1443027-3-ref.html
layout/reftests/display-list/1443027-3.html
layout/reftests/display-list/1443027-ref.html
deleted file mode 100644
--- a/layout/base/crashtests/1443027-1.html
+++ /dev/null
@@ -1,20 +0,0 @@
-<!DOCTYPE HTML>
-<html class="reftest-wait">
-<body>
-  <div id="first" style="position:absolute; width: 200px; height: 200px; background-color:blue; z-index: 1"></div>
-  <div style="position:absolute; left: 400px; width: 200px; height: 200px; background-color:green; z-index: 2"></div>
-  <div id="overlay" style="position:absolute; top: 100px; width: 600px; height: 200px; background-color:orange; z-index: 10"></div>
-</body>
-<script>
-  function doTest2() {
-    document.getElementById("overlay").style.zIndex= 11;
-    document.documentElement.removeAttribute('class');
-  }
-  function doTest() {
-    document.getElementById("first").style.zIndex = 3;
-    setTimeout(doTest2, 1000);
-  }
-
-  window.addEventListener("MozReftestInvalidate", doTest);
-</script>
-</html>
--- a/layout/base/crashtests/crashtests.list
+++ b/layout/base/crashtests/crashtests.list
@@ -522,10 +522,9 @@ load 1425893.html
 load 1428353.html
 pref(dom.webcomponents.shadowdom.enabled,true) load 1429088.html
 load 1429961.html
 load 1435015.html
 load 1429962.html
 pref(dom.webcomponents.shadowdom.enabled,true) load 1439016.html
 load 1442506.html
 load 1437155.html
-load 1443027-1.html
 load 1448841-1.html
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -3086,21 +3086,22 @@ nsIFrame::BuildDisplayListForStackingCon
     // repeating display list building if it changed.
 
     // If we changed whether we're going to build a blend mode item,
     // then we need to make sure we're marked as invalid and we've built
     // the full display list.
     if (aBuilder->ContainsBlendMode() != BuiltBlendContainer() &&
         aBuilder->IsRetainingDisplayList()) {
       SetBuiltBlendContainer(aBuilder->ContainsBlendMode());
+      aBuilder->MarkCurrentFrameModifiedDuringBuilding();
 
       // If we did a partial build then delete all the items we just built
       // and repeat building with the full area.
       if (!aBuilder->GetDirtyRect().Contains(aBuilder->GetVisibleRect())) {
-        aBuilder->MarkCurrentFrameModifiedDuringBuilding();
+        aBuilder->SetDirtyRect(aBuilder->GetVisibleRect());
         set.DeleteAll(aBuilder);
 
         if (eventRegions) {
           eventRegions->Destroy(aBuilder);
           eventRegions = MakeDisplayItem<nsDisplayLayerEventRegions>(aBuilder, this);
           eventRegions->AddFrame(aBuilder, this);
           aBuilder->SetLayerEventRegions(eventRegions);
         }
--- a/layout/painting/RetainedDisplayListBuilder.cpp
+++ b/layout/painting/RetainedDisplayListBuilder.cpp
@@ -104,85 +104,193 @@ SelectAGRForFrame(nsIFrame* aFrame, Anim
 
 // Removes any display items that belonged to a frame that was deleted,
 // and mark frames that belong to a different AGR so that get their
 // items built again.
 // TODO: We currently descend into all children even if we don't have an AGR
 // to mark, as child stacking contexts might. It would be nice if we could
 // jump into those immediately rather than walking the entire thing.
 bool
-RetainedDisplayListBuilder::PreProcessDisplayList(RetainedDisplayList* aList,
+RetainedDisplayListBuilder::PreProcessDisplayList(nsDisplayList* aList,
                                                   AnimatedGeometryRoot* aAGR)
 {
-  // The DAG merging algorithm does not have strong mechanisms in place to keep the
-  // complexity of the resulting DAG under control. In some cases we can build up
-  // edges very quickly. Detect those cases and force a full display list build if
-  // we hit them.
-  static const uint32_t kMaxEdgeRatio = 5;
-  bool initializeDAG = !aList->mDAG.Length();
-  if (!initializeDAG &&
-      aList->mDAG.mDirectPredecessorList.Length() >
-      (aList->mDAG.mNodesInfo.Length() * kMaxEdgeRatio)) {
-    return false;
-
-  }
-
+  bool modified = false;
   nsDisplayList saved;
-  aList->mOldItems.SetCapacity(aList->Count());
-  size_t i = 0;
-  while (nsDisplayItem* item = aList->RemoveBottom()) {
-    if (item->HasDeletedFrame() || !item->CanBeReused()) {
-      // If we haven't yet initialized the DAG, then we can
-      // just drop this item. Otherwise we need to keep it
-      // around to preserve indices, and merging will
-      // get rid of it.
-      if (initializeDAG) {
-        item->Destroy(&mBuilder);
-      } else {
-        aList->mOldItems.AppendElement(OldItemInfo(item));
-      }
+  while (nsDisplayItem* i = aList->RemoveBottom()) {
+    if (i->HasDeletedFrame() || !i->CanBeReused()) {
+      i->Destroy(&mBuilder);
+      modified = true;
       continue;
     }
 
-    aList->mOldItems.AppendElement(OldItemInfo(item));
-    if (initializeDAG) {
-      if (i == 0) {
-        aList->mDAG.AddNode(Span<const MergedListIndex>());
-      } else {
-        MergedListIndex previous(i - 1);
-        aList->mDAG.AddNode(Span<const MergedListIndex>(&previous, 1));
-      }
+    nsIFrame* f = i->Frame();
 
-      aList->mKeyLookup.Put({ item->Frame(), item->GetPerFrameKey() }, i);
-      i++;
-    }
-
-    nsIFrame* f = item->Frame();
-
-    if (item->GetChildren()) {
-      if (!PreProcessDisplayList(item->GetChildren(), SelectAGRForFrame(f, aAGR))) {
-        mBuilder.MarkFrameForDisplayIfVisible(f, mBuilder.RootReferenceFrame());
-        mBuilder.MarkFrameModifiedDuringBuilding(f);
+    if (i->GetChildren()) {
+      if (PreProcessDisplayList(i->GetChildren(), SelectAGRForFrame(f, aAGR))) {
+        modified = true;
       }
     }
 
     // TODO: We should be able to check the clipped bounds relative
     // to the common AGR (of both the existing item and the invalidated
     // frame) and determine if they can ever intersect.
-    if (aAGR && item->GetAnimatedGeometryRoot()->GetAsyncAGR() != aAGR) {
+    if (aAGR && i->GetAnimatedGeometryRoot()->GetAsyncAGR() != aAGR) {
       mBuilder.MarkFrameForDisplayIfVisible(f, mBuilder.RootReferenceFrame());
+      modified = true;
     }
 
     // TODO: This is here because we sometimes reuse the previous display list
     // completely. For optimization, we could only restore the state for reused
     // display items.
-    item->RestoreState();
+    i->RestoreState();
+
+    saved.AppendToTop(i);
+  }
+  aList->AppendToTop(&saved);
+  aList->RestoreState();
+  return modified;
+}
+
+struct DisplayItemKey
+{
+  bool operator ==(const DisplayItemKey& aOther) const {
+    return mFrame == aOther.mFrame &&
+           mPerFrameKey == aOther.mPerFrameKey;
+  }
+
+  nsIFrame* mFrame;
+  uint32_t mPerFrameKey;
+};
+
+class DisplayItemHashEntry : public PLDHashEntryHdr
+{
+public:
+  typedef DisplayItemKey KeyType;
+  typedef const DisplayItemKey* KeyTypePointer;
+
+  explicit DisplayItemHashEntry(KeyTypePointer aKey)
+    : mKey(*aKey) {}
+  explicit DisplayItemHashEntry(const DisplayItemHashEntry& aCopy)=default;
+
+  ~DisplayItemHashEntry() = default;
+
+  KeyType GetKey() const { return mKey; }
+  bool KeyEquals(KeyTypePointer aKey) const
+  {
+    return mKey == *aKey;
+  }
+
+  static KeyTypePointer KeyToPointer(KeyType& aKey) { return &aKey; }
+  static PLDHashNumber HashKey(KeyTypePointer aKey)
+  {
+    if (!aKey)
+      return 0;
+
+    return mozilla::HashGeneric(aKey->mFrame, aKey->mPerFrameKey);
+  }
+  enum { ALLOW_MEMMOVE = true };
+
+  DisplayItemKey mKey;
+};
+
+template<typename T>
+void SwapAndRemove(nsTArray<T>& aArray, uint32_t aIndex)
+{
+  if (aIndex != (aArray.Length() - 1)) {
+    T last = aArray.LastElement();
+    aArray.LastElement() = aArray[aIndex];
+    aArray[aIndex] = last;
   }
-  aList->RestoreState();
-  return true;
+
+  aArray.RemoveLastElement();
+}
+
+static bool
+MergeFrameRects(nsDisplayLayerEventRegions* aOldItem,
+                nsDisplayLayerEventRegions* aNewItem,
+                nsDisplayLayerEventRegions::FrameRects nsDisplayLayerEventRegions::*aRectList,
+                nsTArray<nsIFrame*>& aAddedFrames)
+{
+  bool modified = false;
+  // Go through the old item's rect list and remove any rectangles
+  // belonging to invalidated frames (deleted frames should
+  // already be gone at this point)
+  nsDisplayLayerEventRegions::FrameRects& oldRects = aOldItem->*aRectList;
+  uint32_t i = 0;
+  while (i < oldRects.mFrames.Length()) {
+    // TODO: As mentioned in nsDisplayLayerEventRegions, this
+    // operation might perform really poorly on a vector.
+    nsIFrame* f = oldRects.mFrames[i];
+    if (IsAnyAncestorModified(f)) {
+      MOZ_ASSERT(f != aOldItem->Frame());
+      f->RemoveDisplayItem(aOldItem);
+      SwapAndRemove(oldRects.mFrames, i);
+      SwapAndRemove(oldRects.mBoxes, i);
+      modified = true;
+    } else {
+      i++;
+    }
+  }
+  if (!aNewItem) {
+    return modified;
+  }
+
+  // Copy items from the source list to the dest list, but
+  // only if the dest doesn't already include them.
+  nsDisplayItem* destItem = aOldItem;
+  nsDisplayLayerEventRegions::FrameRects* destRects = &(aOldItem->*aRectList);
+  nsDisplayLayerEventRegions::FrameRects* srcRects = &(aNewItem->*aRectList);
+
+  for (uint32_t i = 0; i < srcRects->mFrames.Length(); i++) {
+    nsIFrame* f = srcRects->mFrames[i];
+    if (!f->HasDisplayItem(destItem)) {
+      // If this frame isn't already in the destination item,
+      // then add it!
+      destRects->Add(f, srcRects->mBoxes[i]);
+
+      // We also need to update RealDisplayItemData for 'f',
+      // but that'll mess up this check for the following
+      // FrameRects lists, so defer that until the end.
+      aAddedFrames.AppendElement(f);
+      MOZ_ASSERT(f != aOldItem->Frame());
+
+      modified = true;
+    }
+
+  }
+  return modified;
+}
+
+bool MergeLayerEventRegions(nsDisplayItem* aOldItem,
+                            nsDisplayItem* aNewItem)
+{
+  nsDisplayLayerEventRegions* oldItem =
+    static_cast<nsDisplayLayerEventRegions*>(aOldItem);
+  nsDisplayLayerEventRegions* newItem =
+    static_cast<nsDisplayLayerEventRegions*>(aNewItem);
+
+  nsTArray<nsIFrame*> addedFrames;
+
+  bool modified = false;
+  modified |= MergeFrameRects(oldItem, newItem, &nsDisplayLayerEventRegions::mHitRegion, addedFrames);
+  modified |= MergeFrameRects(oldItem, newItem, &nsDisplayLayerEventRegions::mMaybeHitRegion, addedFrames);
+  modified |= MergeFrameRects(oldItem, newItem, &nsDisplayLayerEventRegions::mDispatchToContentHitRegion, addedFrames);
+  modified |= MergeFrameRects(oldItem, newItem, &nsDisplayLayerEventRegions::mNoActionRegion, addedFrames);
+  modified |= MergeFrameRects(oldItem, newItem, &nsDisplayLayerEventRegions::mHorizontalPanRegion, addedFrames);
+  modified |= MergeFrameRects(oldItem, newItem, &nsDisplayLayerEventRegions::mVerticalPanRegion, addedFrames);
+
+  // MergeFrameRects deferred updating the display item data list during
+  // processing so that earlier calls didn't change the result of later
+  // ones. Fix that up now.
+  for (nsIFrame* f : addedFrames) {
+    if (!f->HasDisplayItem(aOldItem)) {
+      f->AddDisplayItem(aOldItem);
+    }
+  }
+  return modified;
 }
 
 void
 RetainedDisplayListBuilder::IncrementSubDocPresShellPaintCount(nsDisplayItem* aItem)
 {
   MOZ_ASSERT(aItem->GetType() == DisplayItemType::TYPE_SUBDOCUMENT);
 
   nsSubDocumentFrame* subDocFrame =
@@ -209,268 +317,276 @@ UpdateASR(nsDisplayItem* aItem,
     return;
   }
 
   wrapList->SetActiveScrolledRoot(
     ActiveScrolledRoot::PickAncestor(wrapList->GetFrameActiveScrolledRoot(),
                                      aContainerASR.value()));
 }
 
-void
-OldItemInfo::AddedMatchToMergedList(RetainedDisplayListBuilder* aBuilder,
-                                    MergedListIndex aIndex)
-{
-  mItem->Destroy(aBuilder->Builder());
-  AddedToMergedList(aIndex);
-}
-
-void
-OldItemInfo::Discard(RetainedDisplayListBuilder* aBuilder,
-                     nsTArray<MergedListIndex>&& aDirectPredecessors)
+/**
+ * Takes two display lists and merges them into an output list.
+ *
+ * The basic algorithm is:
+ *
+ * For-each item i in the new list:
+ *     If the item has a matching item in the old list:
+ *         Remove items from the start of the old list up until we reach an item that also exists in the new list (leaving the matched item in place):
+ *             Add valid items to the merged list, destroy invalid items.
+ *     Add i into the merged list.
+ *     If the start of the old list matches i, remove and destroy it, otherwise mark the old version of i as used.
+ * Add all remaining valid items from the old list into the merged list, skipping over (and destroying) any that are marked as used.
+ *
+ * If any item has a child display list, then we recurse into the merge
+ * algorithm once we match up the new/old versions (if present).
+ *
+ * Example 1:
+ *
+ * Old List: A,B,C,D
+ * Modified List: A,D
+ * Invalidations: C,D
+ *
+ * We first match the A items, and add the new one to the merged list.
+ * We then match the D items, copy B into the merged list, but not C
+ * (since it's invalid). We then add the new D to the list and we're
+ * finished.
+ *
+ * Merged List: A,B,D
+ *
+ * Example 2 (layout/reftests/retained-dl-zindex-1.html):
+ *
+ * Old List: A, B
+ * Modified List: B, A
+ * Invalidations: A
+ *
+ * In this example A has been explicitly moved to the back.
+ *
+ * We match the B items, but don't copy A since it's invalid, and then add the
+ * new B into the merged list. We then add A, and we're done.
+ *
+ * Merged List: B, A
+ *
+ * Example 3:
+ *
+ * Old List: A, B
+ * Modified List: B, A
+ * Invalidations: -
+ *
+ * This can happen because a prior merge might have changed the ordering
+ * for non-intersecting items.
+ *
+ * We match the B items, but don't copy A since it's also present in the new list
+ * and then add the new B into the merged list. We then add A, and we're done.
+ *
+ * Merged List: B, A
+ *
+ * Example 4 (layout/reftests/retained-dl-zindex-2.html):
+ *
+ * Element A has two elements covering it (B and C), that don't intersect each
+ * other. We then move C to the back.
+ *
+ * The correct initial ordering has B and C after A, in any order.
+ *
+ * Old List: A, B, C
+ * Modified List: C, A
+ * Invalidations: C
+ *
+ * We match the C items, but don't add anything from the old list because A is present
+ * in both lists. We add C to the merged list, and mark the old version of C as reused.
+ *
+ * We then match A, add the new version the merged list and delete the old version.
+ *
+ * We then process the remainder of the old list, B is added (since it is valid,
+ * and hasn't been mark as reused), C is destroyed since it's marked as reused and
+ * is already present in the merged list.
+ *
+ * Merged List: C, A, B
+ */
+bool
+RetainedDisplayListBuilder::MergeDisplayLists(nsDisplayList* aNewList,
+                                              nsDisplayList* aOldList,
+                                              nsDisplayList* aOutList,
+                                              Maybe<const ActiveScrolledRoot*>& aOutContainerASR)
 {
-  MOZ_ASSERT(!IsUsed());
-  mUsed = mDiscarded = true;
-  mDirectPredecessors = Move(aDirectPredecessors);
-  mItem->Destroy(aBuilder->Builder());
-  mItem = nullptr;
-}
-
-/**
- * A C++ implementation of Markus Stange's merge-dags algorthim.
- * https://github.com/mstange/merge-dags
- *
- * MergeState handles combining a new list of display items into an existing
- * DAG and computes the new DAG in a single pass.
- * Each time we add a new item, we resolve all dependencies for it, so that the resulting
- * list and DAG are built in topological ordering.
- */
-class MergeState {
-public:
-  MergeState(RetainedDisplayListBuilder* aBuilder, RetainedDisplayList&& aOldList)
-    : mBuilder(aBuilder)
-    , mOldItems(Move(aOldList.mOldItems))
-    , mOldDAG(Move(*reinterpret_cast<DirectedAcyclicGraph<OldListUnits>*>(&aOldList.mDAG)))
-    , mResultIsModified(false)
-  {
-    mOldKeyLookup.SwapElements(aOldList.mKeyLookup);
-    mMergedDAG.EnsureCapacityFor(mOldDAG);
-  }
+  bool modified = false;
 
-  MergedListIndex ProcessItemFromNewList(nsDisplayItem* aNewItem, const Maybe<MergedListIndex>& aPreviousItem) {
-    OldListIndex oldIndex;
-    if (mOldKeyLookup.Get({ aNewItem->Frame(), aNewItem->GetPerFrameKey() }, &oldIndex.val)) {
-      if (!IsChanged(aNewItem)) {
-        MOZ_ASSERT(!mOldItems[oldIndex.val].IsUsed());
-        if (aNewItem->GetChildren()) {
-          Maybe<const ActiveScrolledRoot*> containerASRForChildren;
-          if (mBuilder->MergeDisplayLists(aNewItem->GetChildren(),
-                                          mOldItems[oldIndex.val].mItem->GetChildren(),
-                                          aNewItem->GetChildren(),
-                                          containerASRForChildren)) {
-            mResultIsModified = true;
-
-          }
-          UpdateASR(aNewItem, containerASRForChildren);
-          aNewItem->UpdateBounds(mBuilder->Builder());
-        }
-
-        AutoTArray<MergedListIndex, 2> directPredecessors = ProcessPredecessorsOfOldNode(oldIndex);
-        MergedListIndex newIndex = AddNewNode(aNewItem, Some(oldIndex), directPredecessors, aPreviousItem);
-        mOldItems[oldIndex.val].AddedMatchToMergedList(mBuilder, newIndex);
-        return newIndex;
-      }
-    }
-    mResultIsModified = true;
-    return AddNewNode(aNewItem, Nothing(), Span<MergedListIndex>(), aPreviousItem);
-  }
-
-  RetainedDisplayList Finalize() {
-    for (size_t i = 0; i < mOldDAG.Length(); i++) {
-      if (mOldItems[i].IsUsed()) {
-        continue;
-      }
-
-      AutoTArray<MergedListIndex, 2> directPredecessors =
-        ResolveNodeIndexesOldToMerged(mOldDAG.GetDirectPredecessors(OldListIndex(i)));
-      ProcessOldNode(OldListIndex(i), Move(directPredecessors));
-    }
-
-    RetainedDisplayList result;
-    result.AppendToTop(&mMergedItems);
-    result.mDAG = Move(mMergedDAG);
-    result.mKeyLookup.SwapElements(mMergedKeyLookup);
-    return result;
-  }
-
-  bool IsChanged(nsDisplayItem* aItem) {
-    return aItem->HasDeletedFrame() || !aItem->CanBeReused() ||
-           IsAnyAncestorModified(aItem->FrameForInvalidation());
-  }
-
-  void UpdateContainerASR(nsDisplayItem* aItem)
-  {
+  nsDisplayList merged;
+  const auto UseItem = [&](nsDisplayItem* aItem) {
     const ActiveScrolledRoot* itemClipASR =
       aItem->GetClipChain() ? aItem->GetClipChain()->mASR : nullptr;
 
     const ActiveScrolledRoot* finiteBoundsASR = ActiveScrolledRoot::PickDescendant(
       itemClipASR, aItem->GetActiveScrolledRoot());
-    if (!mContainerASR) {
-      mContainerASR = Some(finiteBoundsASR);
+    if (!aOutContainerASR) {
+      aOutContainerASR = Some(finiteBoundsASR);
     } else {
-      mContainerASR = Some(ActiveScrolledRoot::PickAncestor(mContainerASR.value(), finiteBoundsASR));
+      aOutContainerASR =
+        Some(ActiveScrolledRoot::PickAncestor(aOutContainerASR.value(), finiteBoundsASR));
+    }
+
+    merged.AppendToTop(aItem);
+  };
+
+  const auto ReuseItem = [&](nsDisplayItem* aItem) {
+    UseItem(aItem);
+    aItem->SetReused(true);
+
+    if (aItem->GetType() == DisplayItemType::TYPE_SUBDOCUMENT) {
+      IncrementSubDocPresShellPaintCount(aItem);
+    }
+  };
+
+  const bool newListIsEmpty = aNewList->IsEmpty();
+  if (!newListIsEmpty) {
+    // Build a hashtable of items in the old list so we can look for them quickly.
+    // We have similar data in the nsIFrame DisplayItems() property, but it doesn't
+    // know which display list items are in, and we only want to match items in
+    // this list.
+    nsDataHashtable<DisplayItemHashEntry, nsDisplayItem*> oldListLookup(aOldList->Count());
+
+    for (nsDisplayItem* i = aOldList->GetBottom(); i != nullptr; i = i->GetAbove()) {
+      i->SetReused(false);
+      oldListLookup.Put({ i->Frame(), i->GetPerFrameKey() }, i);
+    }
+
+    nsDataHashtable<DisplayItemHashEntry, nsDisplayItem*> newListLookup(aNewList->Count());
+    for (nsDisplayItem* i = aNewList->GetBottom(); i != nullptr; i = i->GetAbove()) {
+#ifdef DEBUG
+      if (newListLookup.Get({ i->Frame(), i->GetPerFrameKey() }, nullptr)) {
+        MOZ_CRASH_UNSAFE_PRINTF("Duplicate display items detected!: %s(0x%p) type=%d key=%d",
+                                  i->Name(), i->Frame(),
+                                  static_cast<int>(i->GetType()), i->GetPerFrameKey());
+      }
+#endif
+      newListLookup.Put({ i->Frame(), i->GetPerFrameKey() }, i);
     }
 
-  }
-
-  MergedListIndex AddNewNode(nsDisplayItem* aItem,
-                             const Maybe<OldListIndex>& aOldIndex,
-                             Span<const MergedListIndex> aDirectPredecessors,
-                             const Maybe<MergedListIndex>& aExtraDirectPredecessor) {
-    UpdateContainerASR(aItem);
-    mMergedItems.AppendToTop(aItem);
-    MergedListIndex newIndex = mMergedDAG.AddNode(aDirectPredecessors, aExtraDirectPredecessor);
-    mMergedKeyLookup.Put({ aItem->Frame(), aItem->GetPerFrameKey() }, newIndex.val);
-    return newIndex;
-  }
-
-  void ProcessOldNode(OldListIndex aNode, nsTArray<MergedListIndex>&& aDirectPredecessors) {
-    nsDisplayItem* item = mOldItems[aNode.val].mItem;
-    if (IsChanged(item)) {
-      mOldItems[aNode.val].Discard(mBuilder, Move(aDirectPredecessors));
-      mResultIsModified = true;
-    } else {
-      if (item->GetChildren()) {
-        Maybe<const ActiveScrolledRoot*> containerASRForChildren;
-        nsDisplayList empty;
-        if (mBuilder->MergeDisplayLists(&empty, item->GetChildren(), item->GetChildren(),
-                                        containerASRForChildren)) {
-          mResultIsModified = true;
+    while (nsDisplayItem* newItem = aNewList->RemoveBottom()) {
+      if (nsDisplayItem* oldItem = oldListLookup.Get({ newItem->Frame(), newItem->GetPerFrameKey() })) {
+        // The new item has a matching counterpart in the old list that we haven't yet reached,
+        // 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->GetBottom()) && old != oldItem) {
+          if (IsAnyAncestorModified(old->FrameForInvalidation())) {
+            // The old item is invalid, discard it.
+            oldListLookup.Remove({ old->Frame(), old->GetPerFrameKey() });
+            aOldList->RemoveBottom();
+            old->Destroy(&mBuilder);
+            modified = true;
+          } else if (newListLookup.Get({ old->Frame(), old->GetPerFrameKey() })) {
+            // This old item is also in the new list, but we haven't got to it yet.
+            // Stop now, and we'll deal with it when we get to the new entry.
+            modified = true;
+            break;
+          } else {
+            // 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;
+              Maybe<const ActiveScrolledRoot*> containerASRForChildren;
+              if (MergeDisplayLists(&empty, old->GetChildren(),
+                                    old->GetChildren(), containerASRForChildren)) {
+                modified = true;
+              }
+              UpdateASR(old, containerASRForChildren);
+              old->UpdateBounds(&mBuilder);
+            }
+            aOldList->RemoveBottom();
+            ReuseItem(old);
+          }
         }
-        UpdateASR(item, containerASRForChildren);
-        item->UpdateBounds(mBuilder->Builder());
-      }
-      if (item->GetType() == DisplayItemType::TYPE_SUBDOCUMENT) {
-        mBuilder->IncrementSubDocPresShellPaintCount(item);
-      }
-      item->SetReused(true);
-      mOldItems[aNode.val].AddedToMergedList(
-        AddNewNode(item, Some(aNode), aDirectPredecessors, Nothing()));
-    }
-  }
-
-  struct PredecessorStackItem {
-    PredecessorStackItem(OldListIndex aNode, Span<OldListIndex> aPredecessors)
-     : mNode(aNode)
-     , mDirectPredecessors(aPredecessors)
-     , mCurrentPredecessorIndex(0)
-    {}
-
-    bool IsFinished() {
-      return mCurrentPredecessorIndex == mDirectPredecessors.Length();
-    }
+        bool destroy = false;
+        if (old == oldItem) {
+          // If we advanced the old list until the matching item then we can pop
+          // the matching item off the old list and make sure we clean it up.
+          aOldList->RemoveBottom();
+          destroy = true;
+        } else {
+          // If we didn't get to the matching item, then mark the old item
+          // as being reused (since we're adding the new version to the new
+          // list now) so that we don't add it twice at the end.
+          oldItem->SetReused(true);
+        }
 
-    OldListIndex GetAndIncrementCurrentPredecessor() { return mDirectPredecessors[mCurrentPredecessorIndex++]; }
-
-    OldListIndex mNode;
-    Span<OldListIndex> mDirectPredecessors;
-    size_t mCurrentPredecessorIndex;
-  };
-
-  AutoTArray<MergedListIndex, 2> ProcessPredecessorsOfOldNode(OldListIndex aNode) {
-    AutoTArray<PredecessorStackItem,256> mStack;
-    mStack.AppendElement(PredecessorStackItem(aNode, mOldDAG.GetDirectPredecessors(aNode)));
+        // Recursively merge any child lists, destroy the old item and add
+        // the new one to the list.
+        if (destroy &&
+            oldItem->GetType() == DisplayItemType::TYPE_LAYER_EVENT_REGIONS &&
+            !IsAnyAncestorModified(oldItem->FrameForInvalidation())) {
+          // Event regions items don't have anything interesting other than
+          // the lists of regions and frames, so we have no need to use the
+          // newer item. Always use the old item instead since we assume it's
+          // likely to have the bigger lists and merging will be quicker.
+          if (MergeLayerEventRegions(oldItem, newItem)) {
+            modified = true;
+          }
+          ReuseItem(oldItem);
+          newItem->Destroy(&mBuilder);
+        } else {
+          if (IsAnyAncestorModified(oldItem->FrameForInvalidation())) {
+            modified = true;
+          } else if (oldItem->GetChildren()) {
+            MOZ_ASSERT(newItem->GetChildren());
+            Maybe<const ActiveScrolledRoot*> containerASRForChildren;
+            if (MergeDisplayLists(newItem->GetChildren(), oldItem->GetChildren(),
+                                  newItem->GetChildren(), containerASRForChildren)) {
+              modified = true;
+            }
+            UpdateASR(newItem, containerASRForChildren);
+            newItem->UpdateBounds(&mBuilder);
+          }
 
-    while (true) {
-      if (mStack.LastElement().IsFinished()) {
-        // If we've finished processing all the entries in the current set, then pop
-        // it off the processing stack and process it.
-        PredecessorStackItem item = mStack.PopLastElement();
-        AutoTArray<MergedListIndex,2> result =
-          ResolveNodeIndexesOldToMerged(item.mDirectPredecessors);
-        if (mStack.IsEmpty()) {
-          return result;
-        } else {
-          ProcessOldNode(item.mNode, Move(result));
+          if (destroy) {
+            oldItem->Destroy(&mBuilder);
+          }
+          UseItem(newItem);
         }
       } else {
-        // Grab the current predecessor, push predecessors of that onto the processing
-        // stack (if it hasn't already been processed), and then advance to the next entry.
-        OldListIndex currentIndex = mStack.LastElement().GetAndIncrementCurrentPredecessor();
-        if (!mOldItems[currentIndex.val].IsUsed()) {
-          mStack.AppendElement(
-            PredecessorStackItem(currentIndex, mOldDAG.GetDirectPredecessors(currentIndex)));
-        }
+        // If there was no matching item in the old list, then we only need to
+        // add the new item to the merged list.
+        modified = true;
+        UseItem(newItem);
       }
     }
   }
 
-  AutoTArray<MergedListIndex, 2> ResolveNodeIndexesOldToMerged(Span<OldListIndex> aDirectPredecessors) {
-    AutoTArray<MergedListIndex, 2> result;
-    result.SetCapacity(aDirectPredecessors.Length());
-    for (OldListIndex index : aDirectPredecessors) {
-      OldItemInfo& oldItem = mOldItems[index.val];
-      if (oldItem.IsDiscarded()) {
-        for (MergedListIndex inner : oldItem.mDirectPredecessors) {
-          if (!result.Contains(inner)) {
-            result.AppendElement(inner);
-          }
+  // Reuse the remaining valid items from the old display list.
+  while (nsDisplayItem* old = aOldList->RemoveBottom()) {
+    if (!IsAnyAncestorModified(old->FrameForInvalidation()) &&
+        (!old->IsReused() || newListIsEmpty)) {
+      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;
+        Maybe<const ActiveScrolledRoot*> containerASRForChildren;
+
+        if (MergeDisplayLists(&empty, old->GetChildren(),
+                              old->GetChildren(), containerASRForChildren)) {
+          modified = true;
         }
-      } else {
-        result.AppendElement(oldItem.mIndex);
+        UpdateASR(old, containerASRForChildren);
+        old->UpdateBounds(&mBuilder);
       }
+      if (old->GetType() == DisplayItemType::TYPE_LAYER_EVENT_REGIONS) {
+        if (MergeLayerEventRegions(old, nullptr)) {
+          modified = true;
+        }
+      }
+      ReuseItem(old);
+    } else {
+      old->Destroy(&mBuilder);
+      modified = true;
     }
-    return result;
   }
 
-  RetainedDisplayListBuilder* mBuilder;
-  Maybe<const ActiveScrolledRoot*> mContainerASR;
-  nsTArray<OldItemInfo> mOldItems;
-  DirectedAcyclicGraph<OldListUnits> mOldDAG;
-  // Unfortunately we can't use strong typing for the hashtables
-  // since they internally encode the type with the mOps pointer,
-  // and assert when we try swap the contents
-  nsDataHashtable<DisplayItemHashEntry, size_t> mOldKeyLookup;
-  nsDisplayList mMergedItems;
-  DirectedAcyclicGraph<MergedListUnits> mMergedDAG;
-  nsDataHashtable<DisplayItemHashEntry, size_t> mMergedKeyLookup;
-  bool mResultIsModified;
-};
-
-void RetainedDisplayList::ClearDAG()
-{
-  mDAG.Clear();
-  mKeyLookup.Clear();
-}
-
-/**
- * Takes two display lists and merges them into an output list.
- *
- * Display lists wthout an explicit DAG are interpreted as linear DAGs (with a maximum
- * of one direct predecessor and one direct successor per node). We add the two DAGs
- * together, and then output the topological sorted ordering as the final display list.
- *
- * Once we've merged a list, we then retain the DAG (as part of the RetainedDisplayList
- * object) to use for future merges.
- */
-bool
-RetainedDisplayListBuilder::MergeDisplayLists(nsDisplayList* aNewList,
-                                              RetainedDisplayList* aOldList,
-                                              RetainedDisplayList* aOutList,
-                                              mozilla::Maybe<const mozilla::ActiveScrolledRoot*>& aOutContainerASR)
-{
-  MergeState merge(this, Move(*aOldList));
-
-  Maybe<MergedListIndex> previousItemIndex;
-  while (nsDisplayItem* item = aNewList->RemoveBottom()) {
-    previousItemIndex = Some(merge.ProcessItemFromNewList(item, previousItemIndex));
-  }
-
-  *aOutList = Move(merge.Finalize());
-  aOutContainerASR = merge.mContainerASR;
-  return merge.mResultIsModified;
+  aOutList->AppendToTop(&merged);
+  return modified;
 }
 
 static void
 TakeAndAddModifiedAndFramesWithPropsFromRootFrame(
   nsTArray<nsIFrame*>* aModifiedFrames,
   nsTArray<nsIFrame*>* aFramesWithProps,
   nsIFrame* aRootFrame)
 {
@@ -974,31 +1090,31 @@ auto
 RetainedDisplayListBuilder::AttemptPartialUpdate(
   nscolor aBackstop,
   mozilla::DisplayListChecker* aChecker) -> PartialUpdateResult
 {
   mBuilder.RemoveModifiedWindowRegions();
   mBuilder.ClearWindowOpaqueRegion();
 
   if (mBuilder.ShouldSyncDecodeImages()) {
-    MarkFramesWithItemsAndImagesModified(List());
+    MarkFramesWithItemsAndImagesModified(&mList);
   }
 
   mBuilder.EnterPresShell(mBuilder.RootReferenceFrame());
 
   // We set the override dirty regions during ComputeRebuildRegion or in
   // nsLayoutUtils::InvalidateForDisplayPortChange. The display port change also
   // marks the frame modified, so those regions are cleared here as well.
   AutoClearFramePropsArray modifiedFrames;
   AutoClearFramePropsArray framesWithProps;
   GetModifiedAndFramesWithProps(&mBuilder, &modifiedFrames.Frames(), &framesWithProps.Frames());
 
   // Do not allow partial builds if the retained display list is empty, or if
   // ShouldBuildPartial heuristic fails.
-  const bool shouldBuildPartial = !List()->IsEmpty() && ShouldBuildPartial(modifiedFrames.Frames());
+  const bool shouldBuildPartial = !mList.IsEmpty() && ShouldBuildPartial(modifiedFrames.Frames());
 
   if (mPreviousCaret != mBuilder.GetCaretFrame()) {
     if (mPreviousCaret) {
       if (mBuilder.MarkFrameModifiedDuringBuilding(mPreviousCaret)) {
         modifiedFrames.Frames().AppendElement(mPreviousCaret);
       }
     }
 
@@ -1010,27 +1126,26 @@ RetainedDisplayListBuilder::AttemptParti
 
     mPreviousCaret = mBuilder.GetCaretFrame();
   }
 
   nsRect modifiedDirty;
   AnimatedGeometryRoot* modifiedAGR = nullptr;
   if (!shouldBuildPartial ||
       !ComputeRebuildRegion(modifiedFrames.Frames(), &modifiedDirty,
-                           &modifiedAGR, framesWithProps.Frames()) ||
-      !PreProcessDisplayList(&mList, modifiedAGR)) {
-    mBuilder.LeavePresShell(mBuilder.RootReferenceFrame(), List());
-    mList.ClearDAG();
+                           &modifiedAGR, framesWithProps.Frames())) {
+    mBuilder.LeavePresShell(mBuilder.RootReferenceFrame(), &mList);
     return PartialUpdateResult::Failed;
   }
 
   modifiedDirty.IntersectRect(modifiedDirty, mBuilder.RootReferenceFrame()->GetVisualOverflowRectRelativeToSelf());
 
   PartialUpdateResult result = PartialUpdateResult::NoChange;
-  if (!modifiedDirty.IsEmpty() ||
+  if (PreProcessDisplayList(&mList, modifiedAGR) ||
+      !modifiedDirty.IsEmpty() ||
       !framesWithProps.IsEmpty()) {
     result = PartialUpdateResult::Updated;
   }
 
   mBuilder.SetDirtyRect(modifiedDirty);
   mBuilder.SetPartialUpdate(true);
 
   nsDisplayList modifiedDL;
@@ -1063,11 +1178,11 @@ RetainedDisplayListBuilder::AttemptParti
   Maybe<const ActiveScrolledRoot*> dummy;
   if (MergeDisplayLists(&modifiedDL, &mList, &mList, dummy)) {
     result = PartialUpdateResult::Updated;
   }
 
   //printf_stderr("Painting --- Merged list:\n");
   //nsFrame::PrintDisplayList(&mBuilder, mList);
 
-  mBuilder.LeavePresShell(mBuilder.RootReferenceFrame(), List());
+  mBuilder.LeavePresShell(mBuilder.RootReferenceFrame(), &mList);
   return result;
 }
--- a/layout/painting/RetainedDisplayListBuilder.h
+++ b/layout/painting/RetainedDisplayListBuilder.h
@@ -44,30 +44,29 @@ struct RetainedDisplayListBuilder {
    * Also clears the frame properties set by RetainedDisplayListBuilder for all
    * the frames in the modified frame lists.
    */
   void ClearFramesWithProps();
 
   NS_DECLARE_FRAME_PROPERTY_DELETABLE(Cached, RetainedDisplayListBuilder)
 
 private:
-  bool PreProcessDisplayList(RetainedDisplayList* aList, AnimatedGeometryRoot* aAGR);
+  bool PreProcessDisplayList(nsDisplayList* aList, AnimatedGeometryRoot* aAGR);
+
   bool MergeDisplayLists(nsDisplayList* aNewList,
-                         RetainedDisplayList* aOldList,
-                         RetainedDisplayList* aOutList,
+                         nsDisplayList* aOldList,
+                         nsDisplayList* aOutList,
                          mozilla::Maybe<const mozilla::ActiveScrolledRoot*>& aOutContainerASR);
 
   bool ComputeRebuildRegion(nsTArray<nsIFrame*>& aModifiedFrames,
                             nsRect* aOutDirty,
                             AnimatedGeometryRoot** aOutModifiedAGR,
                             nsTArray<nsIFrame*>& aOutFramesWithProps);
 
   void IncrementSubDocPresShellPaintCount(nsDisplayItem* aItem);
 
-  friend class MergeState;
-
   nsDisplayListBuilder mBuilder;
-  RetainedDisplayList mList;
+  nsDisplayList mList;
   WeakFrame mPreviousCaret;
 
 };
 
 #endif // RETAINEDDISPLAYLISTBUILDER_H_
deleted file mode 100644
--- a/layout/painting/RetainedDisplayListHelpers.h
+++ /dev/null
@@ -1,199 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* 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/. */
-
-#ifndef RETAINEDDISPLAYLISTHELPERS_H_
-#define RETAINEDDISPLAYLISTHELPERS_H_
-
-#include "PLDHashTable.h"
-
-struct DisplayItemKey
-{
-  bool operator ==(const DisplayItemKey& aOther) const {
-    return mFrame == aOther.mFrame &&
-           mPerFrameKey == aOther.mPerFrameKey;
-  }
-
-  nsIFrame* mFrame;
-  uint32_t mPerFrameKey;
-};
-
-class DisplayItemHashEntry : public PLDHashEntryHdr
-{
-public:
-  typedef DisplayItemKey KeyType;
-  typedef const DisplayItemKey* KeyTypePointer;
-
-  explicit DisplayItemHashEntry(KeyTypePointer aKey)
-    : mKey(*aKey) {}
-  explicit DisplayItemHashEntry(const DisplayItemHashEntry& aCopy)=default;
-
-  ~DisplayItemHashEntry() = default;
-
-  KeyType GetKey() const { return mKey; }
-  bool KeyEquals(KeyTypePointer aKey) const
-  {
-    return mKey == *aKey;
-  }
-
-  static KeyTypePointer KeyToPointer(KeyType& aKey) { return &aKey; }
-  static PLDHashNumber HashKey(KeyTypePointer aKey)
-  {
-    if (!aKey)
-      return 0;
-
-    return mozilla::HashGeneric(aKey->mFrame, aKey->mPerFrameKey);
-  }
-  enum { ALLOW_MEMMOVE = true };
-
-  DisplayItemKey mKey;
-};
-
-template <typename T>
-bool SpanContains(mozilla::Span<const T>& aSpan, T aItem)
-{
-  for (const T& i : aSpan) {
-    if (i == aItem) {
-      return true;
-    }
-  }
-  return false;
-}
-
-class OldListUnits {};
-class MergedListUnits {};
-
-template <typename Units>
-struct Index {
-  Index()
-    : val(0)
-  {}
-  explicit Index(size_t aVal)
-    : val(aVal)
-  {}
-
-  bool operator==(const Index<Units>& aOther) const
-  {
-    return val == aOther.val;
-  }
-
-  size_t val;
-};
-typedef Index<OldListUnits> OldListIndex;
-typedef Index<MergedListUnits> MergedListIndex;
-
-
-template <typename T>
-class DirectedAcyclicGraph {
-public:
-  DirectedAcyclicGraph() {}
-  DirectedAcyclicGraph(DirectedAcyclicGraph&& aOther)
-    : mNodesInfo(mozilla::Move(aOther.mNodesInfo))
-    , mDirectPredecessorList(mozilla::Move(aOther.mDirectPredecessorList))
-  {}
-
-  DirectedAcyclicGraph& operator=(DirectedAcyclicGraph&& aOther)
-  {
-    mNodesInfo = mozilla::Move(aOther.mNodesInfo);
-    mDirectPredecessorList = mozilla::Move(aOther.mDirectPredecessorList);
-    return *this;
-  }
-
-  Index<T> AddNode(mozilla::Span<const Index<T>> aDirectPredecessors,
-                   const mozilla::Maybe<Index<T>>& aExtraPredecessor = mozilla::Nothing())
-  {
-    size_t index = mNodesInfo.Length();
-    mNodesInfo.AppendElement(NodeInfo(mDirectPredecessorList.Length(), aDirectPredecessors.Length()));
-    if (aExtraPredecessor && !SpanContains(aDirectPredecessors, aExtraPredecessor.value())) {
-      mNodesInfo.LastElement().mDirectPredecessorCount++;
-      mDirectPredecessorList.SetCapacity(mDirectPredecessorList.Length() + aDirectPredecessors.Length() + 1);
-      mDirectPredecessorList.AppendElements(aDirectPredecessors);
-      mDirectPredecessorList.AppendElement(aExtraPredecessor.value());
-    } else {
-      mDirectPredecessorList.AppendElements(aDirectPredecessors);
-    }
-    return Index<T>(index);
-  }
-
-  size_t Length()
-  {
-    return mNodesInfo.Length();
-  }
-
-  mozilla::Span<Index<T>> GetDirectPredecessors(Index<T> aNodeIndex)
-  {
-    NodeInfo& node = mNodesInfo[aNodeIndex.val];
-    return mozilla::MakeSpan(mDirectPredecessorList).Subspan(node.mIndexInDirectPredecessorList,
-                                                             node.mDirectPredecessorCount);
-  }
-
-  template<typename OtherUnits>
-  void EnsureCapacityFor(const DirectedAcyclicGraph<OtherUnits>& aOther)
-  {
-    mNodesInfo.SetCapacity(aOther.mNodesInfo.Length());
-    mDirectPredecessorList.SetCapacity(aOther.mDirectPredecessorList.Length());
-  }
-
-  void Clear()
-  {
-    mNodesInfo.Clear();
-    mDirectPredecessorList.Clear();
-  }
-
-  struct NodeInfo {
-    NodeInfo(size_t aIndexInDirectPredecessorList,
-             size_t aDirectPredecessorCount)
-      : mIndexInDirectPredecessorList(aIndexInDirectPredecessorList)
-      , mDirectPredecessorCount(aDirectPredecessorCount)
-    {}
-    size_t mIndexInDirectPredecessorList;
-    size_t mDirectPredecessorCount;
-  };
-
-  nsTArray<NodeInfo> mNodesInfo;
-  nsTArray<Index<T>> mDirectPredecessorList;
-};
-
-struct RetainedDisplayListBuilder;
-class nsDisplayItem;
-
-struct OldItemInfo {
-  explicit OldItemInfo(nsDisplayItem* aItem)
-    : mItem(aItem)
-    , mUsed(false)
-    , mDiscarded(false)
-  {}
-
-  void AddedToMergedList(MergedListIndex aIndex)
-  {
-    MOZ_ASSERT(!IsUsed());
-    mUsed = true;
-    mIndex = aIndex;
-    mItem = nullptr;
-  }
-
-  void AddedMatchToMergedList(RetainedDisplayListBuilder* aBuilder,
-                              MergedListIndex aIndex);
-  void Discard(RetainedDisplayListBuilder* aBuilder,
-               nsTArray<MergedListIndex>&& aDirectPredecessors);
-  bool IsUsed()
-  {
-    return mUsed;
-  }
-
-  bool IsDiscarded()
-  {
-    MOZ_ASSERT(IsUsed());
-    return mDiscarded;
-  }
-
-  nsDisplayItem* mItem;
-  bool mUsed;
-  bool mDiscarded;
-  MergedListIndex mIndex;
-  nsTArray<MergedListIndex> mDirectPredecessors;
-};
-
-#endif // RETAINEDDISPLAYLISTHELPERS_H_
--- a/layout/painting/moz.build
+++ b/layout/painting/moz.build
@@ -17,17 +17,16 @@ EXPORTS += [
     'nsCSSRenderingBorders.h',
     'nsCSSRenderingGradients.h',
     'nsDisplayItemTypes.h',
     'nsDisplayItemTypesList.h',
     'nsDisplayList.h',
     'nsDisplayListInvalidation.h',
     'nsImageRenderer.h',
     'RetainedDisplayListBuilder.h',
-    'RetainedDisplayListHelpers.h',
 ]
 
 EXPORTS.mozilla += [
     'PaintTracker.h',
 ]
 
 UNIFIED_SOURCES += [
     'ActiveLayerTracker.cpp',
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -39,17 +39,16 @@
 #include "mozilla/UniquePtr.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/gfx/UserData.h"
 #include "mozilla/layers/LayerAttributes.h"
 #include "nsCSSRenderingBorders.h"
 #include "nsPresArena.h"
 #include "nsAutoLayoutPhase.h"
 #include "nsDisplayItemTypes.h"
-#include "RetainedDisplayListHelpers.h"
 
 #include <stdint.h>
 #include "nsTHashtable.h"
 
 #include <stdlib.h>
 #include <algorithm>
 #include <unordered_set>
 
@@ -1092,21 +1091,21 @@ public:
       if (aBuilder->mCurrentFrame == aForChild->GetParent()) {
         if (mCurrentAGRState == AGR_YES) {
           aBuilder->mCurrentAGR = aBuilder->WrapAGRForFrame(aForChild, isAsync, aBuilder->mCurrentAGR);
         }
       } else if (aForChild != aBuilder->mCurrentFrame) {
         aBuilder->mCurrentAGR = aBuilder->FindAnimatedGeometryRootFor(aForChild);
       }
       MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDoc(aBuilder->RootReferenceFrame(), *aBuilder->mCurrentAGR));
-      aBuilder->mInInvalidSubtree = aBuilder->mInInvalidSubtree || aForChild->IsFrameModified();
       aBuilder->mCurrentFrame = aForChild;
       aBuilder->mVisibleRect = aVisibleRect;
-      aBuilder->mDirtyRect = aBuilder->mInInvalidSubtree ? aVisibleRect : aDirtyRect;
+      aBuilder->mDirtyRect = aDirtyRect;
       aBuilder->mIsAtRootOfPseudoStackingContext = aIsRoot;
+      aBuilder->mInInvalidSubtree = aBuilder->mInInvalidSubtree || aForChild->IsFrameModified();
     }
     void SetReferenceFrameAndCurrentOffset(const nsIFrame* aFrame, const nsPoint& aOffset) {
       mBuilder->mCurrentReferenceFrame = aFrame;
       mBuilder->mCurrentOffsetToReferenceFrame = aOffset;
     }
     bool IsAnimatedGeometryRoot() const {
       return mCurrentAGRState == AGR_YES;
     }
@@ -1736,17 +1735,16 @@ public:
     }
     return false;
   }
 
   bool MarkCurrentFrameModifiedDuringBuilding()
   {
     if (MarkFrameModifiedDuringBuilding(const_cast<nsIFrame*>(mCurrentFrame))) {
       mInInvalidSubtree = true;
-      mDirtyRect = mVisibleRect;
       return true;
     }
     return false;
   }
 
   /**
    * This is a convenience function to ease the transition until AGRs and ASRs
    * are unified.
@@ -2013,17 +2011,16 @@ private:
   bool                           mInInvalidSubtree;
   bool                           mBuildCompositorHitTestInfo;
   bool                           mLessEventRegionItems;
   bool                           mBuiltOverlayScrollbars;
 };
 
 class nsDisplayItem;
 class nsDisplayList;
-class RetainedDisplayList;
 /**
  * nsDisplayItems are put in singly-linked lists rooted in an nsDisplayList.
  * nsDisplayItemLink holds the link. The lists are linked from lowest to
  * highest in z-order.
  */
 class nsDisplayItemLink {
   // This is never instantiated directly, so no need to count constructors and
   // destructors.
@@ -2653,17 +2650,17 @@ public:
    * the same 3d rendering context.
    */
   virtual void DoUpdateBoundsPreserves3D(nsDisplayListBuilder* aBuilder) {}
 
   /**
    * If this has a child list, return it, even if the children are in
    * a different coordinate system to this item.
    */
-  virtual RetainedDisplayList* GetChildren() const { return nullptr; }
+  virtual nsDisplayList* GetChildren() const { return nullptr; }
 
   /**
    * Returns the visible rect.
    */
   const nsRect& GetVisibleRect() const { return mVisibleRect; }
 
   void SetVisibleRect(const nsRect& aVisibleRect, bool aStore)
   {
@@ -3324,69 +3321,16 @@ struct nsDisplayListCollection : public 
 private:
   // This class is only used on stack, so we don't have to worry about leaking
   // it.  Don't let us be heap-allocated!
   void* operator new(size_t sz) CPP_THROW_NEW;
 
   nsDisplayList mLists[6];
 };
 
-/**
- * A display list that also retains the partial build
- * information (in the form of a DAG) used to create it.
- *
- * Display lists built from a partial list aren't necessarily
- * in the same order as a full build, and the DAG retains
- * the information needing to interpret the current
- * order correctly.
- */
-class RetainedDisplayList : public nsDisplayList {
-public:
-  RetainedDisplayList() {}
-  RetainedDisplayList(RetainedDisplayList&& aOther)
-  {
-    AppendToTop(&aOther);
-    mDAG = mozilla::Move(aOther.mDAG);
-    mKeyLookup.SwapElements(aOther.mKeyLookup);
-  }
-  ~RetainedDisplayList()
-  {
-    MOZ_ASSERT(mOldItems.IsEmpty(), "Must empty list before destroying");
-  }
-
-  RetainedDisplayList& operator=(RetainedDisplayList&& aOther)
-  {
-    MOZ_ASSERT(!Count(), "Can only move into an empty list!");
-    MOZ_ASSERT(mOldItems.IsEmpty(), "Can only move into an empty list!");
-    AppendToTop(&aOther);
-    mDAG = mozilla::Move(aOther.mDAG);
-    mKeyLookup.SwapElements(aOther.mKeyLookup);
-    return *this;
-  }
-
-  void DeleteAll(nsDisplayListBuilder* aBuilder)
-  {
-    for (OldItemInfo& i : mOldItems) {
-      if (i.mItem) {
-        i.mItem->Destroy(aBuilder);
-      }
-    }
-    mOldItems.Clear();
-    nsDisplayList::DeleteAll(aBuilder);
-  }
-
-  void ClearDAG();
-
-  DirectedAcyclicGraph<MergedListUnits> mDAG;
-  nsDataHashtable<DisplayItemHashEntry, size_t> mKeyLookup;
-
-  // Temporary state initialized during the preprocess pass
-  // of RetainedDisplayListBuilder and then used during merging.
-  nsTArray<OldItemInfo> mOldItems;
-};
 
 class nsDisplayImageContainer : public nsDisplayItem {
 public:
   typedef mozilla::LayerIntPoint LayerIntPoint;
   typedef mozilla::LayoutDeviceRect LayoutDeviceRect;
   typedef mozilla::layers::ImageContainer ImageContainer;
   typedef mozilla::layers::ImageLayer ImageLayer;
 
@@ -5056,17 +5000,17 @@ public:
   {
     NS_ASSERTION(mListPtr->IsEmpty() || !ReferenceFrame() ||
                  !mListPtr->GetBottom()->ReferenceFrame() ||
                  mListPtr->GetBottom()->ReferenceFrame() == ReferenceFrame(),
                  "Children must have same reference frame");
     return mListPtr;
   }
 
-  virtual RetainedDisplayList* GetChildren() const override { return mListPtr; }
+  virtual nsDisplayList* GetChildren() const override { return mListPtr; }
 
   virtual int32_t ZIndex() const override
   {
     return (mHasZIndexOverride) ? mOverrideZIndex : nsDisplayItem::ZIndex();
   }
 
   void SetOverrideZIndex(int32_t aZIndex)
   {
@@ -5102,18 +5046,18 @@ protected:
   void MergeFromTrackingMergedFrames(const nsDisplayWrapList* aOther)
   {
     mBounds.UnionRect(mBounds, aOther->mBounds);
     mVisibleRect.UnionRect(mVisibleRect, aOther->mVisibleRect);
     mMergedFrames.AppendElement(aOther->mFrame);
     mMergedFrames.AppendElements(aOther->mMergedFrames);
   }
 
-  RetainedDisplayList mList;
-  RetainedDisplayList* mListPtr;
+  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.
@@ -6237,17 +6181,17 @@ public:
   virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) const override
   {
     if (mStoredList.GetComponentAlphaBounds(aBuilder).IsEmpty())
       return nsRect();
     bool snap;
     return GetBounds(aBuilder, &snap);
   }
 
-  virtual RetainedDisplayList* GetChildren() const override
+  virtual nsDisplayList* GetChildren() const override
   {
     return mStoredList.GetChildren();
   }
 
   virtual void SetActiveScrolledRoot(const ActiveScrolledRoot* aActiveScrolledRoot) override
   {
     nsDisplayItem::SetActiveScrolledRoot(aActiveScrolledRoot);
     mStoredList.SetActiveScrolledRoot(aActiveScrolledRoot);
@@ -6637,17 +6581,17 @@ public:
     return true;
   }
 
   virtual nsDisplayList* GetSameCoordinateSystemChildren() const override
   {
     return mList.GetChildren();
   }
 
-  virtual RetainedDisplayList* GetChildren() const override
+  virtual nsDisplayList* GetChildren() const override
   {
     return mList.GetChildren();
   }
 
   virtual void SetActiveScrolledRoot(const ActiveScrolledRoot* aActiveScrolledRoot) override
   {
     nsDisplayItem::SetActiveScrolledRoot(aActiveScrolledRoot);
     mList.SetActiveScrolledRoot(aActiveScrolledRoot);
deleted file mode 100644
--- a/layout/reftests/display-list/1443027-1.html
+++ /dev/null
@@ -1,93 +0,0 @@
-<!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>Bug 1443027 - Test merging across multiple paints</title>
-
-<style>
-
-body { opacity: 0.9; }
-
-div {
-  position: absolute;
-}
-
-#A {
-  left: 250px;
-  top: 50px;
-  width: 100px;
-  height: 100px;
-  background-color: red;
-}
-
-#B {
-  left: 200px;
-  top: 0px;
-  width: 100px;
-  height: 100px;
-  background-color: yellow;
-}
-
-#C {
-  left: 0px;
-  top: 0px;
-  width: 100px;
-  height: 100px;
-  background-color: green;
-}
-
-#D {
-  left: 80px;
-  top: 20px;
-  width: 140px;
-  height: 100px;
-  background-color: blue;
-}
-
-</style>
-</head>
-<body>
-<div id="A"></div>
-<div id="B"></div>
-<div id="C"></div>
-<div id="D"></div>
-
-<script>
-
-var A = document.getElementById("A");
-var B = document.getElementById("B");
-var C = document.getElementById("C");
-var D = document.getElementById("D");
-
-A.style.visibility = "hidden";
-B.style.visibility = "hidden";
-C.style.visibility = "hidden";
-D.style.visibility = "hidden";
-
-window.addEventListener("MozReftestInvalidate", step1);
-
-function step1() {
-  C.style.visibility = "visible";
-  C.style.transform = "translatez(1px)";
-  D.style.visibility = "visible";
-  D.style.transform = "translatez(1px)";
-
-  window.requestAnimationFrame(function() { window.requestAnimationFrame(step2); });
-}
-
-function step2() {
-  A.style.visibility = "visible";
-  A.style.transform = "translatez(1px)";
-  B.style.visibility = "visible";
-  B.style.transform = "translatez(1px)";
-
-  window.requestAnimationFrame(step3);
-}
-
-function step3() {
-  D.style.visibility = "hidden";
-  D.style.transform = "";
-  document.documentElement.removeAttribute('class');
-}
-
-</script>
-</body></html>
deleted file mode 100644
--- a/layout/reftests/display-list/1443027-2.html
+++ /dev/null
@@ -1,93 +0,0 @@
-<!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>Bug 1443027 - Test merging across multiple paints</title>
-
-<style>
-
-body { opacity: 0.9; }
-
-div {
-  position: absolute;
-}
-
-#A {
-  left: 250px;
-  top: 50px;
-  width: 100px;
-  height: 100px;
-  background-color: red;
-}
-
-#B {
-  left: 200px;
-  top: 0px;
-  width: 100px;
-  height: 100px;
-  background-color: yellow;
-}
-
-#C {
-  left: 0px;
-  top: 0px;
-  width: 100px;
-  height: 100px;
-  background-color: green;
-}
-
-#D {
-  left: 80px;
-  top: 20px;
-  width: 140px;
-  height: 100px;
-  background-color: blue;
-}
-
-</style>
-</head>
-<body>
-<div id="A"></div>
-<div id="B"></div>
-<div id="C"></div>
-<div id="D"></div>
-
-<script>
-
-var A = document.getElementById("A");
-var B = document.getElementById("B");
-var C = document.getElementById("C");
-var D = document.getElementById("D");
-
-A.style.visibility = "hidden";
-B.style.visibility = "hidden";
-C.style.visibility = "hidden";
-D.style.visibility = "hidden";
-
-window.addEventListener("MozReftestInvalidate", step1);
-
-function step1() {
-  A.style.visibility = "visible";
-  A.style.transform = "translatez(1px)";
-  B.style.visibility = "visible";
-  B.style.transform = "translatez(1px)";
-  D.style.visibility = "visible";
-  D.style.transform = "translatez(1px)";
-
-  window.requestAnimationFrame(function() { window.requestAnimationFrame(step2); });
-}
-
-function step2() {
-  C.style.visibility = "visible";
-  C.style.transform = "translatez(1px)";
-
-  window.requestAnimationFrame(step3);
-}
-
-function step3() {
-  D.style.visibility = "hidden";
-  D.style.transform = "";
-  document.documentElement.removeAttribute('class');
-}
-
-</script>
-</body></html>
deleted file mode 100644
--- a/layout/reftests/display-list/1443027-3-ref.html
+++ /dev/null
@@ -1,54 +0,0 @@
-<!DOCTYPE html>
-<html lang="en"><head>
-<meta http-equiv="content-type" content="text/html; charset=UTF-8"><meta charset="utf-8">
-<title>Bug 1443027 - Test merging across multiple paints</title>
-
-<style>
-
-body { opacity: 0.9; }
-
-div {
-  position: absolute;
-  transform: translatez(1px);
-}
-
-#A {
-  left: 250px;
-  top: 50px;
-  width: 100px;
-  height: 100px;
-  background-color: red;
-}
-
-#B {
-  left: 200px;
-  top: 0px;
-  width: 100px;
-  height: 100px;
-  background-color: yellow;
-}
-
-#C {
-  left: 0px;
-  top: 0px;
-  width: 100px;
-  height: 100px;
-  background-color: green;
-}
-
-#D {
-  left: 80px;
-  top: 20px;
-  width: 140px;
-  height: 100px;
-  background-color: blue;
-}
-
-</style>
-</head>
-<body>
-<div id="A"></div>
-<div id="B"></div>
-<div id="D"></div>
-<div id="C"></div>
-</body></html>
deleted file mode 100644
--- a/layout/reftests/display-list/1443027-3.html
+++ /dev/null
@@ -1,91 +0,0 @@
-<!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>Bug 1443027 - Test merging across multiple paints</title>
-
-<style>
-
-body { opacity: 0.9; }
-
-div {
-  position: absolute;
-}
-
-#A {
-  left: 250px;
-  top: 50px;
-  width: 100px;
-  height: 100px;
-  background-color: red;
-}
-
-#B {
-  left: 200px;
-  top: 0px;
-  width: 100px;
-  height: 100px;
-  background-color: yellow;
-}
-
-#C {
-  left: 0px;
-  top: 0px;
-  width: 100px;
-  height: 100px;
-  background-color: green;
-}
-
-#D {
-  left: 80px;
-  top: 20px;
-  width: 140px;
-  height: 100px;
-  background-color: blue;
-}
-
-</style>
-</head>
-<body>
-<div id="A"></div>
-<div id="B"></div>
-<div id="D"></div>
-<div id="C"></div>
-
-<script>
-
-var A = document.getElementById("A");
-var B = document.getElementById("B");
-var C = document.getElementById("C");
-var D = document.getElementById("D");
-
-A.style.visibility = "hidden";
-B.style.visibility = "hidden";
-C.style.visibility = "hidden";
-D.style.visibility = "hidden";
-
-window.addEventListener("MozReftestInvalidate", step1);
-
-function step1() {
-  A.style.visibility = "visible";
-  A.style.transform = "translatez(1px)";
-
-  window.requestAnimationFrame(function() { window.requestAnimationFrame(step2); });
-}
-
-function step2() {
-  C.style.visibility = "visible";
-  C.style.transform = "translatez(1px)";
-  D.style.visibility = "visible";
-  D.style.transform = "translatez(1px)";
-
-  window.requestAnimationFrame(step3);
-}
-
-function step3() {
-  B.style.visibility = "visible";
-  B.style.transform = "translatez(1px)";
-  document.documentElement.removeAttribute('class');
-}
-
-</script>
-</body></html>
deleted file mode 100644
--- a/layout/reftests/display-list/1443027-ref.html
+++ /dev/null
@@ -1,45 +0,0 @@
-<!DOCTYPE html>
-<html lang="en"><head>
-<meta http-equiv="content-type" content="text/html; charset=UTF-8"><meta charset="utf-8">
-<title>Bug 1443027 - Test merging across multiple paints</title>
-
-<style>
-
-body { opacity: 0.9; }
-
-div {
-  position: absolute;
-  transform: translatez(1px);
-}
-
-#A {
-  left: 250px;
-  top: 50px;
-  width: 100px;
-  height: 100px;
-  background-color: red;
-}
-
-#B {
-  left: 200px;
-  top: 0px;
-  width: 100px;
-  height: 100px;
-  background-color: yellow;
-}
-
-#C {
-  left: 0px;
-  top: 0px;
-  width: 100px;
-  height: 100px;
-  background-color: green;
-}
-
-</style>
-</head>
-<body>
-<div id="A"></div>
-<div id="B"></div>
-<div id="C"></div>
-</body></html>
--- a/layout/reftests/display-list/reftest.list
+++ b/layout/reftests/display-list/reftest.list
@@ -18,11 +18,9 @@ skip-if(Android) == 1428993-1.html 14289
 == 1420480-1.html 1420480-1-ref.html
 == 1428993-2.html 1428993-2-ref.html
 needs-focus == 1429027-1.html 1429027-1-ref.html
 == 1432553-1.html 1432553-1-ref.html
 == 1432553-2.html 1432553-2-ref.html
 == 1436189-1.html 1436189-1-ref.html
 skip-if(!asyncPan) == 1437374-1.html 1437374-1-ref.html
 == 1439809-1.html 1439809-1-ref.html
-== 1443027-1.html 1443027-ref.html
-== 1443027-2.html 1443027-ref.html
-== 1443027-3.html 1443027-3-ref.html
+
--- a/layout/reftests/position-dynamic-changes/relative/reftest.list
+++ b/layout/reftests/position-dynamic-changes/relative/reftest.list
@@ -1,5 +1,5 @@
-fuzzy-if(cocoaWidget,1,2) fuzzy-if(d2d,47,26) fuzzy-if(asyncPan&&!layersGPUAccelerated,169,970) == move-right-bottom.html move-right-bottom-ref.html
-fuzzy-if(cocoaWidget,1,2) fuzzy-if(asyncPan&&!layersGPUAccelerated,169,970) == move-top-left.html move-top-left-ref.html # Bug 688545
+fuzzy-if(cocoaWidget,1,2) fuzzy-if(d2d,47,26) fuzzy-if(asyncPan&&!layersGPUAccelerated,149,716) == move-right-bottom.html move-right-bottom-ref.html
+fuzzy-if(cocoaWidget,1,2) fuzzy-if(asyncPan&&!layersGPUAccelerated,149,716) == move-top-left.html move-top-left-ref.html # Bug 688545
 fuzzy-if(cocoaWidget,1,3) fuzzy-if(asyncPan&&!layersGPUAccelerated,144,580) == move-right-bottom-table.html move-right-bottom-table-ref.html
 fuzzy-if(cocoaWidget,1,3) fuzzy-if(asyncPan&&!layersGPUAccelerated,144,580) == move-top-left-table.html move-top-left-table-ref.html # Bug 688545
 == percent.html percent-ref.html
--- a/layout/reftests/w3c-css/submitted/ruby/reftest.list
+++ b/layout/reftests/w3c-css/submitted/ruby/reftest.list
@@ -1,11 +1,11 @@
 # Tests for inlinizing block-level boxes
 == ruby-inlinize-blocks-001.html ruby-inlinize-blocks-001-ref.html
-fuzzy-if(winWidget,144,1) == ruby-inlinize-blocks-002.html ruby-inlinize-blocks-002-ref.html
+== ruby-inlinize-blocks-002.html ruby-inlinize-blocks-002-ref.html
 == ruby-inlinize-blocks-003.html ruby-inlinize-blocks-003-ref.html
 == ruby-inlinize-blocks-004.html ruby-inlinize-blocks-004-ref.html
 == ruby-inlinize-blocks-005.html ruby-inlinize-blocks-005-ref.html
 
 # Tests for autohiding base-identical annotations
 == ruby-autohide-001.html ruby-autohide-001-ref.html
 == ruby-autohide-002.html ruby-autohide-002-ref.html
 == ruby-autohide-003.html ruby-autohide-003-ref.html