Backed out 2 changesets (bug 1443027) for breacking Gmail on OSX r=pascalc a=backout
authorNoemi Erli <nerli@mozilla.com>
Fri, 30 Mar 2018 19:08:30 +0300
changeset 410763 0405f6006f3a3f653dd42d587c3eefe08cffa37d
parent 410762 44aceee694bec3dc6a7757395d25553ae87ab166
child 410795 10c662d8416e84b44931d767ea1be2f4d0cc92ce
child 410833 5eaadafebee8490143d9f6e55386d9ca69ffeb5e
push id33738
push usernerli@mozilla.com
push dateFri, 30 Mar 2018 16:08:52 +0000
treeherdermozilla-central@0405f6006f3a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspascalc, backout
bugs1443027
milestone61.0a1
backs out5deb310542a943a654533cbfb593f62587647b09
e04979dd66be1709557e2aa09deb3f838d449a29
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out 2 changesets (bug 1443027) for breacking Gmail on OSX r=pascalc a=backout Backed out changeset 5deb310542a9 (bug 1443027) Backed out changeset e04979dd66be (bug 1443027)
layout/generic/nsFrame.cpp
layout/painting/RetainedDisplayListBuilder.cpp
layout/painting/RetainedDisplayListBuilder.h
layout/painting/RetainedDisplayListHelpers.h
layout/painting/moz.build
layout/painting/nsDisplayList.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
layout/reftests/display-list/reftest.list
layout/reftests/position-dynamic-changes/relative/reftest.list
layout/reftests/w3c-css/submitted/ruby/reftest.list
--- 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,290 +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)
-{
-  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
+ * 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.
  *
- * 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.
+ * 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
  */
-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);
-  }
-
-  MergedListIndex ProcessItemFromNewList(nsDisplayItem* aNewItem, const Maybe<MergedListIndex>& aPreviousItem) {
-    OldListIndex oldIndex;
-    if (mOldKeyLookup.Get({ aNewItem->Frame(), aNewItem->GetPerFrameKey() }, &oldIndex.val)) {
-      bool changed = IsChanged(aNewItem);
-      if (!changed || HasSameSinglePredecessor(oldIndex, aPreviousItem)) {
-        MOZ_ASSERT(!mOldItems[oldIndex.val].IsUsed());
-        if (changed) {
-          mResultIsModified = true;
-        } else if (aNewItem->GetChildren()) {
-          Maybe<const ActiveScrolledRoot*> containerASRForChildren;
-          if (mBuilder->MergeDisplayLists(aNewItem->GetChildren(),
-                                          mOldItems[oldIndex.val].mItem->GetChildren(),
-                                          aNewItem->GetChildren(),
-                                          containerASRForChildren)) {
-            mResultIsModified = true;
+bool
+RetainedDisplayListBuilder::MergeDisplayLists(nsDisplayList* aNewList,
+                                              nsDisplayList* aOldList,
+                                              nsDisplayList* aOutList,
+                                              Maybe<const ActiveScrolledRoot*>& aOutContainerASR)
+{
+  bool modified = false;
 
-          }
-          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);
-  }
-
-  bool HasSameSinglePredecessor(OldListIndex aNode,
-                                const Maybe<MergedListIndex>& aNewItemPredecessor)
-  {
-    if (!aNewItemPredecessor) {
-      return false;
-    }
-
-    Span<OldListIndex> directPredecessors = mOldDAG.GetDirectPredecessors(aNode);
-    if (directPredecessors.Length() != 1) {
-      return false;
-    }
-
-    OldItemInfo& oldItem = mOldItems[directPredecessors[0].val];
-    if (oldItem.IsUsed() && !oldItem.IsDiscarded()) {
-      return oldItem.mIndex == aNewItemPredecessor.value();
-    }
-    return false;
-  }
-
-  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)
 {
@@ -996,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);
       }
     }
 
@@ -1032,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;
@@ -1085,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>
 
@@ -1089,21 +1088,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;
     }
@@ -1733,17 +1732,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.
@@ -2009,17 +2007,16 @@ private:
   bool                           mIsBuilding;
   bool                           mInInvalidSubtree;
   bool                           mBuildCompositorHitTestInfo;
   bool                           mLessEventRegionItems;
 };
 
 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.
@@ -2649,17 +2646,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)
   {
@@ -3320,69 +3317,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;
 
@@ -5052,17 +4996,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)
   {
@@ -5098,18 +5042,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.
@@ -6233,17 +6177,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);
@@ -6633,17 +6577,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