Backed out 2 changesets (bug 1488599) for bustages at /layout/painting/nsDisplayList.cpp on a CLOSED TREE
authorGurzau Raul <rgurzau@mozilla.com>
Mon, 17 Sep 2018 16:57:58 +0300
changeset 436724 88b67d6b6a46325c652c3cde93dc6f21107638da
parent 436723 961cfd2ee957fdba7a86f1e7881f8fcde0158da8
child 436725 3e53a951924221b067fbd77a147dccd96faf7a52
push id69394
push userrgurzau@mozilla.com
push dateMon, 17 Sep 2018 13:58:43 +0000
treeherderautoland@88b67d6b6a46 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1488599
milestone64.0a1
backs outed780c8fd413d4d80975713854d49b4356a16577
0f40b48ed3b456def7891587e1a6db4c8b68f79b
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 1488599) for bustages at /layout/painting/nsDisplayList.cpp on a CLOSED TREE Backed out changeset ed780c8fd413 (bug 1488599) Backed out changeset 0f40b48ed3b4 (bug 1488599)
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
layout/generic/nsFrame.cpp
layout/generic/nsIFrame.h
layout/painting/RetainedDisplayListBuilder.cpp
layout/painting/RetainedDisplayListBuilder.h
layout/painting/nsDisplayList.cpp
layout/painting/nsDisplayList.h
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -475,24 +475,16 @@ nsLayoutUtils::AreRetainedDisplayListsEn
     return gfxPrefs::LayoutRetainDisplayListChrome();
   }
 
   // Retained display lists require e10s.
   return false;
 }
 
 bool
-nsLayoutUtils::DisplayRootHasRetainedDisplayListBuilder(nsIFrame* aFrame)
-{
-  const nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(aFrame);
-  MOZ_ASSERT(displayRoot);
-  return displayRoot->HasProperty(RetainedDisplayListBuilder::Cached());
-}
-
-bool
 nsLayoutUtils::GPUImageScalingEnabled()
 {
   static bool sGPUImageScalingEnabled;
   static bool sGPUImageScalingPrefInitialised = false;
 
   if (!sGPUImageScalingPrefInitialised) {
     sGPUImageScalingPrefInitialised = true;
     sGPUImageScalingEnabled =
@@ -1191,35 +1183,48 @@ nsLayoutUtils::InvalidateForDisplayPortC
     frame = do_QueryFrame(frame->GetScrollTargetFrame());
   }
 
   if (changed && frame) {
     // It is important to call SchedulePaint on the same frame that we set the dirty
     // rect properties on so we can find the frame later to remove the properties.
     frame->SchedulePaint();
 
-    if (!nsLayoutUtils::AreRetainedDisplayListsEnabled() ||
-        !nsLayoutUtils::DisplayRootHasRetainedDisplayListBuilder(frame)) {
+    if (!nsLayoutUtils::AreRetainedDisplayListsEnabled()) {
+      return;
+    }
+
+    nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(frame);
+    RetainedDisplayListBuilder* retainedBuilder =
+      displayRoot->GetProperty(RetainedDisplayListBuilder::Cached());
+
+    if (!retainedBuilder) {
       return;
     }
 
     nsRect* rect =
       frame->GetProperty(nsDisplayListBuilder::DisplayListBuildingDisplayPortRect());
 
     if (!rect) {
       rect = new nsRect();
       frame->SetProperty(nsDisplayListBuilder::DisplayListBuildingDisplayPortRect(), rect);
       frame->SetHasOverrideDirtyRegion(true);
 
-      nsIFrame* rootFrame = frame->PresShell()->GetRootFrame();
+      nsIFrame* rootFrame = frame->PresContext()->PresShell()->GetRootFrame();
       MOZ_ASSERT(rootFrame);
 
-      RetainedDisplayListData* data =
-        GetOrSetRetainedDisplayListData(rootFrame);
-      data->Flags(frame) |= RetainedDisplayListData::FrameFlags::HasProps;
+      nsTArray<nsIFrame*>* frames =
+        rootFrame->GetProperty(nsIFrame::OverriddenDirtyRectFrameList());
+
+      if (!frames) {
+        frames = new nsTArray<nsIFrame*>();
+        rootFrame->SetProperty(nsIFrame::OverriddenDirtyRectFrameList(), frames);
+      }
+
+      frames->AppendElement(frame);
     }
 
     if (aHadDisplayPort) {
       // We only need to build a display list for any new areas added
       nsRegion newRegion(aNewDisplayPort);
       newRegion.SubOut(aOldDisplayPort);
       rect->UnionRect(*rect, newRegion.GetBounds());
     } else {
@@ -3695,25 +3700,21 @@ nsLayoutUtils::PaintFrame(gfxContext* aR
             }
           }
         }
         dlStart = TimeStamp::Now();
       }
 
       if (updateState == PartialUpdateResult::Failed) {
         list.DeleteAll(&builder);
-
-        builder.ClearRetainedWindowRegions();
-        builder.ClearWillChangeBudget();
-
         builder.EnterPresShell(aFrame);
         builder.SetDirtyRect(visibleRect);
+        builder.ClearRetainedWindowRegions();
         aFrame->BuildDisplayListForStackingContext(&builder, &list);
-        AddExtraBackgroundItems(
-          builder, list, aFrame, canvasArea, visibleRegion, aBackstop);
+        AddExtraBackgroundItems(builder, list, aFrame, canvasArea, visibleRegion, aBackstop);
 
         builder.LeavePresShell(aFrame, &list);
         updateState = PartialUpdateResult::Updated;
 
         if (afterMergeChecker) {
           DisplayListChecker nonRetainedChecker(&list, "NR");
           std::stringstream ss;
           ss << "**** Differences between retained-after-merged (AM) and "
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -2370,18 +2370,16 @@ public:
    */
   static bool IsAnimationLoggingEnabled();
 
   /**
    * Checks if retained display lists are enabled.
    */
   static bool AreRetainedDisplayListsEnabled();
 
-  static bool DisplayRootHasRetainedDisplayListBuilder(nsIFrame* aFrame);
-
   /**
    * Find a suitable scale for a element (aFrame's content) over the course of any
    * animations and transitions of the CSS transform property on the
    * element that run on the compositor thread.
    * It will check the maximum and minimum scale during the animations and
    * transitions and return a suitable value for performance and quality.
    * Will return scale(1,1) if there are no such animations.
    * Always returns a positive value.
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -824,24 +824,22 @@ nsFrame::DestroyFrom(nsIFrame* aDestruct
   nsQueryFrame::FrameIID id = GetFrameId();
   this->~nsFrame();
 
 #ifdef DEBUG
   {
     nsIFrame* rootFrame = shell->GetRootFrame();
     MOZ_ASSERT(rootFrame);
     if (this != rootFrame) {
-      const RetainedDisplayListData* data =
-        GetRetainedDisplayListData(rootFrame);
-
-      const bool inModifiedList = data &&
-        (data->GetFlags(this) & RetainedDisplayListData::FrameFlags::Modified);
-
-      MOZ_ASSERT(!inModifiedList,
-                 "A dtor added this frame to modified frames list!");
+      nsTArray<nsIFrame*>* modifiedFrames =
+        rootFrame->GetProperty(nsIFrame::ModifiedFrameList());
+      if (modifiedFrames) {
+        MOZ_ASSERT(!modifiedFrames->Contains(this),
+                   "A dtor added this frame to ModifiedFrameList");
+      }
     }
   }
 #endif
 
   // Now that we're totally cleaned out, we need to add ourselves to
   // the presshell's recycler.
   shell->FreeFrame(id, this);
 }
@@ -962,46 +960,46 @@ nsIFrame::RemoveDisplayItemDataForDeleti
           !i->HasDeletedFrame()) {
         i->Frame()->MarkNeedsDisplayItemRebuild();
       }
       i->RemoveFrame(this);
     }
     delete items;
   }
 
-  if (!nsLayoutUtils::AreRetainedDisplayListsEnabled()) {
-    // Retained display lists are disabled, no need to update
-    // RetainedDisplayListData.
-    return;
-  }
-
-  const bool updateData =
-    IsFrameModified() || HasOverrideDirtyRegion() || MayHaveWillChangeBudget();
-
-  if (!updateData) {
-    // No RetainedDisplayListData to update.
-    return;
-  }
-
-  nsIFrame* rootFrame = PresShell()->GetRootFrame();
-  MOZ_ASSERT(rootFrame);
-
-  RetainedDisplayListData* data = GetOrSetRetainedDisplayListData(rootFrame);
-
-  if (MayHaveWillChangeBudget()) {
-    // Keep the frame in list, so it can be removed from the will-change budget.
-    data->Flags(this) = RetainedDisplayListData::FrameFlags::HadWillChange;
-    return;
-  }
-
-  if (IsFrameModified() || HasOverrideDirtyRegion()) {
-    // Remove deleted frames from RetainedDisplayListData.
-    DebugOnly<bool> removed = data->Remove(this);
-    MOZ_ASSERT(removed,
-               "Frame had flags set, but it was not found in DisplayListData!");
+  if (IsFrameModified()) {
+    nsIFrame* rootFrame = PresShell()->GetRootFrame();
+    MOZ_ASSERT(rootFrame);
+
+    nsTArray<nsIFrame*>* modifiedFrames =
+      rootFrame->GetProperty(nsIFrame::ModifiedFrameList());
+    MOZ_ASSERT(modifiedFrames);
+
+    for (auto& frame : *modifiedFrames) {
+      if (frame == this) {
+        frame = nullptr;
+        break;
+      }
+    }
+  }
+
+  if (HasOverrideDirtyRegion()) {
+    nsIFrame* rootFrame = PresShell()->GetRootFrame();
+    MOZ_ASSERT(rootFrame);
+
+    nsTArray<nsIFrame*>* frames =
+      rootFrame->GetProperty(nsIFrame::OverriddenDirtyRectFrameList());
+    MOZ_ASSERT(frames);
+
+    for (auto& frame : *frames) {
+      if (frame == this) {
+        frame = nullptr;
+        break;
+      }
+    }
   }
 }
 
 void
 nsIFrame::MarkNeedsDisplayItemRebuild()
 {
   if (!nsLayoutUtils::AreRetainedDisplayListsEnabled() ||
       IsFrameModified() ||
@@ -1014,34 +1012,63 @@ nsIFrame::MarkNeedsDisplayItemRebuild()
     nsIFrame* oof = static_cast<nsPlaceholderFrame*>(this)->GetOutOfFlowFrame();
     if (oof) {
       oof->MarkNeedsDisplayItemRebuild();
     }
     // Do not mark placeholder frames modified.
     return;
   }
 
-  if (!nsLayoutUtils::DisplayRootHasRetainedDisplayListBuilder(this)) {
+  nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(this);
+  MOZ_ASSERT(displayRoot);
+
+  RetainedDisplayListBuilder* retainedBuilder =
+    displayRoot->GetProperty(RetainedDisplayListBuilder::Cached());
+
+  if (!retainedBuilder) {
     return;
   }
 
   nsIFrame* rootFrame = PresShell()->GetRootFrame();
   MOZ_ASSERT(rootFrame);
 
   if (rootFrame->IsFrameModified()) {
     return;
   }
 
-  RetainedDisplayListData* data = GetOrSetRetainedDisplayListData(rootFrame);
-  data->Flags(this) |= RetainedDisplayListData::FrameFlags::Modified;
+  nsTArray<nsIFrame*>* modifiedFrames =
+    rootFrame->GetProperty(nsIFrame::ModifiedFrameList());
+
+  if (!modifiedFrames) {
+    modifiedFrames = new nsTArray<nsIFrame*>();
+    rootFrame->SetProperty(nsIFrame::ModifiedFrameList(), modifiedFrames);
+  }
+
+  if (this == rootFrame) {
+    // If this is the root frame, then marking us as needing a display
+    // item rebuild implies the same for all our descendents. Clear them
+    // all out to reduce the number of modified frames we keep around.
+    for (nsIFrame* f : *modifiedFrames) {
+      if (f) {
+        f->SetFrameIsModified(false);
+      }
+    }
+    modifiedFrames->Clear();
+  } else if (modifiedFrames->Length() > gfxPrefs::LayoutRebuildFrameLimit()) {
+    // If the list starts getting too big, then just mark the root frame
+    // as needing a rebuild.
+    rootFrame->MarkNeedsDisplayItemRebuild();
+    return;
+  }
+
+  modifiedFrames->AppendElement(this);
+
+  MOZ_ASSERT(PresContext()->LayoutPhaseCount(eLayoutPhase_DisplayListBuilding) == 0);
   SetFrameIsModified(true);
 
-  MOZ_ASSERT(
-    PresContext()->LayoutPhaseCount(eLayoutPhase_DisplayListBuilding) == 0);
-
   // Hopefully this is cheap, but we could use a frame state bit to note
   // the presence of dependencies to speed it up.
   DisplayItemArray* items = GetProperty(DisplayItems());
   if (items) {
     for (nsDisplayItem* i : *items) {
       if (i->GetDependentFrame() == this &&
           !i->HasDeletedFrame()) {
         i->Frame()->MarkNeedsDisplayItemRebuild();
@@ -3487,17 +3514,17 @@ nsIFrame::BuildDisplayListForChild(nsDis
     }
   }
 
   nsIFrame* child = aChild;
   if (child->HasAnyStateBits(
        NS_FRAME_TOO_DEEP_IN_FRAME_TREE | NS_FRAME_IS_NONDISPLAY))
     return;
 
-  aBuilder->RemoveFromWillChangeBudget(child);
+  aBuilder->ClearWillChangeBudget(child);
 
   const bool shortcutPossible = aBuilder->IsPaintingToWindow() &&
      aBuilder->BuildCompositorHitTestInfo();
 
   const bool doingShortcut = shortcutPossible &&
     (child->GetStateBits() & NS_FRAME_SIMPLE_DISPLAYLIST) &&
     // Animations may change the value of |HasOpacity()|.
     !(child->GetContent() &&
@@ -3564,17 +3591,17 @@ nsIFrame::BuildDisplayListForChild(nsDis
   }
 
   nsDisplayListBuilder::OutOfFlowDisplayData* savedOutOfFlowData = nullptr;
   bool isPlaceholder = false;
   if (child->IsPlaceholderFrame()) {
     isPlaceholder = true;
     nsPlaceholderFrame* placeholder = static_cast<nsPlaceholderFrame*>(child);
     child = placeholder->GetOutOfFlowFrame();
-    aBuilder->RemoveFromWillChangeBudget(child);
+    aBuilder->ClearWillChangeBudget(child);
     NS_ASSERTION(child, "No out of flow frame?");
     // If 'child' is a pushed float then it's owned by a block that's not an
     // ancestor of the placeholder, and it will be painted by that block and
     // should not be painted through the placeholder.
     if (!child || nsLayoutUtils::IsPopup(child) ||
         (child->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT))
       return;
     MOZ_ASSERT(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW);
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -1246,16 +1246,18 @@ public:
   NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(FragStretchBSizeProperty, nscoord)
 
   // The block-axis margin-box size associated with eBClampMarginBoxMinSize.
   NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(BClampMarginBoxMinSizeProperty, nscoord)
 
   NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(IBaselinePadProperty, nscoord)
   NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(BBaselinePadProperty, nscoord)
 
+  NS_DECLARE_FRAME_PROPERTY_DELETABLE(ModifiedFrameList, nsTArray<nsIFrame*>)
+  NS_DECLARE_FRAME_PROPERTY_DELETABLE(OverriddenDirtyRectFrameList, nsTArray<nsIFrame*>)
   NS_DECLARE_FRAME_PROPERTY_DELETABLE(DisplayItems, DisplayItemArray)
 
   NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(BidiDataProperty, mozilla::FrameBidiData)
 
   NS_DECLARE_FRAME_PROPERTY_WITHOUT_DTOR(PlaceholderFrameProperty, nsPlaceholderFrame)
 
   mozilla::FrameBidiData GetBidiData() const
   {
--- a/layout/painting/RetainedDisplayListBuilder.cpp
+++ b/layout/painting/RetainedDisplayListBuilder.cpp
@@ -36,39 +36,16 @@
  * ordering in the DAG, since they need to intersect to have an ordering and
  * we would have built both in the new list if they intersected. Given that, we
  * can align items that appear in both lists, and any items that appear between
  * matched items can be inserted into the merged list in any order.
  */
 
 using namespace mozilla;
 
-RetainedDisplayListData*
-GetRetainedDisplayListData(nsIFrame* aRootFrame)
-{
-  RetainedDisplayListData* data =
-    aRootFrame->GetProperty(RetainedDisplayListData::DisplayListData());
-
-  return data;
-}
-
-RetainedDisplayListData*
-GetOrSetRetainedDisplayListData(nsIFrame* aRootFrame)
-{
-  RetainedDisplayListData* data = GetRetainedDisplayListData(aRootFrame);
-
-  if (!data) {
-    data = new RetainedDisplayListData();
-    aRootFrame->SetProperty(RetainedDisplayListData::DisplayListData(), data);
-  }
-
-  MOZ_ASSERT(data);
-  return data;
-}
-
 static void
 MarkFramesWithItemsAndImagesModified(nsDisplayList* aList)
 {
   for (nsDisplayItem* i = aList->GetBottom(); i != nullptr; i = i->GetAbove()) {
     if (!i->HasDeletedFrame() && i->CanBeReused() &&
         !i->Frame()->IsFrameModified()) {
       // If we have existing cached geometry for this item, then check that for
       // whether we need to invalidate for a sync decode. If we don't, then
@@ -655,47 +632,46 @@ RetainedDisplayListBuilder::MergeDisplay
 
   *aOutList = merge.Finalize();
   aOutContainerASR = merge.mContainerASR;
   return merge.mResultIsModified;
 }
 
 static void
 TakeAndAddModifiedAndFramesWithPropsFromRootFrame(
-  nsDisplayListBuilder* aBuilder,
   nsTArray<nsIFrame*>* aModifiedFrames,
   nsTArray<nsIFrame*>* aFramesWithProps,
   nsIFrame* aRootFrame)
 {
   MOZ_ASSERT(aRootFrame);
 
-  RetainedDisplayListData* data = GetRetainedDisplayListData(aRootFrame);
+  nsTArray<nsIFrame*>* frames =
+    aRootFrame->GetProperty(nsIFrame::ModifiedFrameList());
 
-  if (!data) {
-    return;
+  if (frames) {
+    for (nsIFrame* f : *frames) {
+      if (f) {
+        aModifiedFrames->AppendElement(f);
+      }
+    }
+
+    frames->Clear();
   }
 
-  for (auto it = data->Iterator(); !it.Done(); it.Next()) {
-    nsIFrame* frame = it.Key();
-    const RetainedDisplayListData::FrameFlags& flags = it.Data();
+  frames = aRootFrame->GetProperty(nsIFrame::OverriddenDirtyRectFrameList());
 
-    if (flags & RetainedDisplayListData::FrameFlags::Modified) {
-      aModifiedFrames->AppendElement(frame);
+  if (frames) {
+    for (nsIFrame* f : *frames) {
+      if (f) {
+        aFramesWithProps->AppendElement(f);
+      }
     }
 
-    if (flags & RetainedDisplayListData::FrameFlags::HasProps) {
-      aFramesWithProps->AppendElement(frame);
-    }
-
-    if (flags & RetainedDisplayListData::FrameFlags::HadWillChange) {
-      aBuilder->RemoveFromWillChangeBudget(frame);
-    }
+    frames->Clear();
   }
-
-  data->Clear();
 }
 
 struct CbData
 {
   nsDisplayListBuilder* builder;
   nsTArray<nsIFrame*>* modifiedFrames;
   nsTArray<nsIFrame*>* framesWithProps;
 };
@@ -748,38 +724,39 @@ SubDocEnumCb(nsIDocument* aDocument, voi
   MOZ_ASSERT(aDocument);
   MOZ_ASSERT(aData);
 
   CbData* data = static_cast<CbData*>(aData);
 
   nsIFrame* rootFrame = GetRootFrameForPainting(data->builder, aDocument);
   if (rootFrame) {
     TakeAndAddModifiedAndFramesWithPropsFromRootFrame(
-      data->builder, data->modifiedFrames, data->framesWithProps, rootFrame);
+      data->modifiedFrames, data->framesWithProps, rootFrame);
 
     nsIDocument* innerDoc = rootFrame->PresShell()->GetDocument();
     if (innerDoc) {
       innerDoc->EnumerateSubDocuments(SubDocEnumCb, aData);
     }
   }
   return true;
 }
 
 static void
 GetModifiedAndFramesWithProps(nsDisplayListBuilder* aBuilder,
                               nsTArray<nsIFrame*>* aOutModifiedFrames,
                               nsTArray<nsIFrame*>* aOutFramesWithProps)
 {
-  nsIFrame* rootFrame = aBuilder->RootReferenceFrame();
-  MOZ_ASSERT(rootFrame);
+  MOZ_ASSERT(aBuilder->RootReferenceFrame());
 
   TakeAndAddModifiedAndFramesWithPropsFromRootFrame(
-    aBuilder, aOutModifiedFrames, aOutFramesWithProps, rootFrame);
+    aOutModifiedFrames, aOutFramesWithProps, aBuilder->RootReferenceFrame());
 
-  nsIDocument* rootdoc = rootFrame->PresContext()->Document();
+  nsIDocument* rootdoc =
+    aBuilder->RootReferenceFrame()->PresContext()->Document();
+
   if (rootdoc) {
     CbData data = { aBuilder, aOutModifiedFrames, aOutFramesWithProps };
 
     rootdoc->EnumerateSubDocuments(SubDocEnumCb, &data);
   }
 }
 
 // ComputeRebuildRegion  debugging
@@ -1248,21 +1225,16 @@ ClearFrameProps(nsTArray<nsIFrame*>& aFr
 
     f->SetFrameIsModified(false);
   }
 }
 
 class AutoClearFramePropsArray
 {
 public:
-  explicit AutoClearFramePropsArray(size_t aCapacity)
-    : mFrames(aCapacity)
-  {
-  }
-
   AutoClearFramePropsArray() = default;
 
   ~AutoClearFramePropsArray() { ClearFrameProps(mFrames); }
 
   nsTArray<nsIFrame*>& Frames() { return mFrames; }
 
   bool IsEmpty() const { return mFrames.IsEmpty(); }
 
@@ -1291,17 +1263,17 @@ RetainedDisplayListBuilder::AttemptParti
     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(64);
+  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.
   bool shouldBuildPartial =
     !mList.IsEmpty() && ShouldBuildPartial(modifiedFrames.Frames());
--- a/layout/painting/RetainedDisplayListBuilder.h
+++ b/layout/painting/RetainedDisplayListBuilder.h
@@ -4,88 +4,21 @@
  * 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 RETAINEDDISPLAYLISTBUILDER_H_
 #define RETAINEDDISPLAYLISTBUILDER_H_
 
 #include "nsDisplayList.h"
 #include "mozilla/Maybe.h"
-#include "mozilla/TypedEnumBits.h"
 
 namespace mozilla {
 class DisplayListChecker;
 } // namespace mozilla
 
-
-/**
- * RetainedDisplayListData contains frame invalidation information. It is stored
- * in root frames, and used by RetainedDisplayListBuilder.
- * Currently this is implemented as a map of frame pointers to flags.
- */
-struct RetainedDisplayListData
-{
-  NS_DECLARE_FRAME_PROPERTY_DELETABLE(DisplayListData, RetainedDisplayListData)
-
-  enum class FrameFlags : uint8_t
-  {
-    None = 0,
-    Modified = 1 << 0,
-    HasProps = 1 << 1,
-    HadWillChange = 1 << 2
-  };
-
-  /**
-   * Removes all the frames from this RetainedDisplayListData.
-   */
-  void Clear() { mFrames.Clear(); }
-
-  /**
-   * Returns a mutable reference to flags set for the given |aFrame|. If the
-   * frame does not exist in this RetainedDisplayListData, it is added with
-   * default constructible flags FrameFlags::None.
-   */
-  FrameFlags& Flags(nsIFrame* aFrame) { return mFrames.GetOrInsert(aFrame); }
-
-  /**
-   * Returns flags set for the given |aFrame|, or FrameFlags::None if the frame
-   * is not in this RetainedDisplayListData.
-   */
-  FrameFlags GetFlags(nsIFrame* aFrame) const { return mFrames.Get(aFrame); }
-
-  /**
-   * Returns an iterator to the underlying frame storage.
-   */
-  auto Iterator() { return mFrames.Iter(); }
-
-  /**
-   * Removes the given |aFrame| from this RetainedDisplayListData.
-   */
-  bool Remove(nsIFrame* aFrame) { return mFrames.Remove(aFrame); }
-
-private:
-  nsDataHashtable<nsPtrHashKey<nsIFrame>, FrameFlags> mFrames;
-};
-
-MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(RetainedDisplayListData::FrameFlags)
-
-/**
- * Returns RetainedDisplayListData property for the given |aRootFrame|, or
- * nullptr if the property is not set.
- */
-RetainedDisplayListData*
-GetRetainedDisplayListData(nsIFrame* aRootFrame);
-
-/**
- * Returns RetainedDisplayListData property for the given |aRootFrame|. Creates
- * and sets a new RetainedDisplayListData property if it is not already set.
- */
-RetainedDisplayListData*
-GetOrSetRetainedDisplayListData(nsIFrame* aRootFrame);
-
 struct RetainedDisplayListBuilder
 {
   RetainedDisplayListBuilder(nsIFrame* aReferenceFrame,
                              nsDisplayListBuilderMode aMode,
                              bool aBuildCaret)
     : mBuilder(aReferenceFrame, aMode, aBuildCaret, true)
   {
   }
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -1055,17 +1055,16 @@ nsDisplayListBuilder::BeginFrame()
 }
 
 void
 nsDisplayListBuilder::EndFrame()
 {
   NS_ASSERTION(!mInInvalidSubtree,
                "Someone forgot to cleanup mInInvalidSubtree!");
   mFrameToAnimatedGeometryRootMap.Clear();
-  mAGRBudgetSet.Clear();
   mActiveScrolledRoots.Clear();
   FreeClipChains();
   FreeTemporaryItems();
   nsCSSRendering::EndFrameTreesLocked();
 
   MOZ_ASSERT(!mCompositorHitTestInfo);
 }
 
@@ -2116,30 +2115,39 @@ GetLayerizationCost(const nsSize& aSize)
 bool
 nsDisplayListBuilder::AddToWillChangeBudget(nsIFrame* aFrame,
                                             const nsSize& aSize)
 {
   if (mWillChangeBudgetSet.Get(aFrame, nullptr)) {
     return true; // Already accounted
   }
 
-  nsPresContext* presContext = aFrame->PresContext();
-  nsRect area = presContext->GetVisibleArea();
+  nsPresContext* key = aFrame->PresContext();
+  DocumentWillChangeBudget budget;
+  auto willChangeBudgetEntry = mWillChangeBudget.LookupForAdd(key);
+  if (willChangeBudgetEntry) {
+    // We have an existing entry.
+    budget = willChangeBudgetEntry.Data();
+  } else {
+    budget = DocumentWillChangeBudget();
+    willChangeBudgetEntry.OrInsert([&budget]() { return budget; });
+  }
+
+  nsRect area = aFrame->PresContext()->GetVisibleArea();
   uint32_t budgetLimit = nsPresContext::AppUnitsToIntCSSPixels(area.width) *
                          nsPresContext::AppUnitsToIntCSSPixels(area.height);
+
   uint32_t cost = GetLayerizationCost(aSize);
-
-  DocumentWillChangeBudget& budget = mWillChangeBudget.GetOrInsert(presContext);
-
   bool onBudget =
     (budget.mBudget + cost) / gWillChangeAreaMultiplier < budgetLimit;
 
   if (onBudget) {
     budget.mBudget += cost;
-    mWillChangeBudgetSet.Put(aFrame, FrameWillChangeBudget(presContext, cost));
+    willChangeBudgetEntry.Data() = budget;
+    mWillChangeBudgetSet.Put(aFrame, cost);
     aFrame->SetMayHaveWillChangeBudget(true);
   }
 
   return onBudget;
 }
 
 bool
 nsDisplayListBuilder::IsInWillChangeBudget(nsIFrame* aFrame,
@@ -2166,39 +2174,33 @@ nsDisplayListBuilder::IsInWillChangeBudg
       false,
       params,
       ArrayLength(params));
   }
   return onBudget;
 }
 
 void
-nsDisplayListBuilder::RemoveFromWillChangeBudget(nsIFrame* aFrame)
-{
-  FrameWillChangeBudget* frameBudget = mWillChangeBudgetSet.GetValue(aFrame);
-
-  if (!frameBudget) {
+nsDisplayListBuilder::ClearWillChangeBudget(nsIFrame* aFrame)
+{
+  if (!aFrame->MayHaveWillChangeBudget()) {
     return;
   }
-
+  aFrame->SetMayHaveWillChangeBudget(false);
+
+  uint32_t cost = 0;
+  if (!mWillChangeBudgetSet.Get(aFrame, &cost)) {
+    return;
+  }
   mWillChangeBudgetSet.Remove(aFrame);
 
-  DocumentWillChangeBudget* budget =
-    mWillChangeBudget.GetValue(frameBudget->mPresContext);
-  MOZ_ASSERT(budget);
-
-  budget->mBudget -= frameBudget->mUsage;
-  MOZ_ASSERT(budget->mBudget >= 0);
-}
-
-void
-nsDisplayListBuilder::ClearWillChangeBudget()
-{
-  mWillChangeBudgetSet.Clear();
-  mWillChangeBudget.Clear();
+  DocumentWillChangeBudget& budget =
+    mWillChangeBudget.GetOrInsert(aFrame->PresContext());
+  MOZ_ASSERT(budget.mBudget >= cost);
+  budget.mBudget -= cost;
 }
 
 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
 const float gAGRBudgetAreaMultiplier = 0.3;
 #else
 const float gAGRBudgetAreaMultiplier = 3.0;
 #endif
 
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -1810,19 +1810,17 @@ public:
   /**
    * This will add the current frame to the will-change budget the first
    * time it is seen. On subsequent calls this will return the same
    * answer. This effectively implements a first-come, first-served
    * allocation of the will-change budget.
    */
   bool IsInWillChangeBudget(nsIFrame* aFrame, const nsSize& aSize);
 
-  void RemoveFromWillChangeBudget(nsIFrame* aFrame);
-
-  void ClearWillChangeBudget();
+  void ClearWillChangeBudget(nsIFrame* aFrame);
 
   void EnterSVGEffectsContents(nsDisplayList* aHoistedItemsStorage);
   void ExitSVGEffectsContents();
 
   /**
    * Note: if changing the conditions under which scroll info layers
    * are created, make a corresponding change to
    * ScrollFrameWillBuildScrollInfoLayer() in nsSliderFrame.cpp.
@@ -2037,29 +2035,23 @@ private:
     {
     }
 
     uint32_t mBudget;
   };
 
   struct FrameWillChangeBudget
   {
-    FrameWillChangeBudget()
-      : mPresContext(nullptr)
-      , mUsage(0)
-    {
-    }
-
-    FrameWillChangeBudget(nsPresContext* aPresContext, uint32_t aUsage)
-      : mPresContext(aPresContext)
+    FrameWillChangeBudget(nsIFrame* aFrame, uint32_t aUsage)
+      : mFrame(aFrame)
       , mUsage(aUsage)
     {
     }
 
-    nsPresContext* mPresContext;
+    nsIFrame* mFrame;
     uint32_t mUsage;
   };
 
   nsIFrame* const mReferenceFrame;
   nsIFrame* mIgnoreScrollFrame;
   nsDisplayCompositorHitTestInfo* mCompositorHitTestInfo;
 
   nsPresArena mPool;
@@ -2087,18 +2079,17 @@ private:
   RefPtr<AnimatedGeometryRoot> mCurrentAGR;
 
   // will-change budget tracker
   nsDataHashtable<nsPtrHashKey<nsPresContext>, DocumentWillChangeBudget>
     mWillChangeBudget;
 
   // Any frame listed in this set is already counted in the budget
   // and thus is in-budget.
-  nsDataHashtable<nsPtrHashKey<nsIFrame>, FrameWillChangeBudget>
-    mWillChangeBudgetSet;
+  nsDataHashtable<nsPtrHashKey<nsIFrame>, uint32_t> mWillChangeBudgetSet;
 
   // Area of animated geometry root budget already allocated
   uint32_t mUsedAGRBudget;
   // Set of frames already counted in budget
   nsTHashtable<nsPtrHashKey<nsIFrame>> mAGRBudgetSet;
 
   nsTArray<nsIFrame*> mModifiedFramesDuringBuilding;
 
@@ -3363,17 +3354,17 @@ public:
   /**
    * Remove an item from the bottom of the list and return it.
    */
   nsDisplayItem* RemoveBottom();
 
   /**
    * Remove all items from the list and call their destructors.
    */
-  virtual void DeleteAll(nsDisplayListBuilder* aBuilder);
+  void DeleteAll(nsDisplayListBuilder* aBuilder);
 
   /**
    * @return the item at the top of the list, or null if the list is empty
    */
   nsDisplayItem* GetTop() const
   {
     return mTop != &mSentinel ? static_cast<nsDisplayItem*>(mTop) : nullptr;
   }
@@ -3758,17 +3749,17 @@ public:
   {
     MOZ_ASSERT(!Count(), "Can only move into an empty list!");
     MOZ_ASSERT(mOldItems.IsEmpty(), "Can only move into an empty list!");
     AppendToTop(&aOther);
     mDAG = std::move(aOther.mDAG);
     return *this;
   }
 
-  void DeleteAll(nsDisplayListBuilder* aBuilder) override
+  void DeleteAll(nsDisplayListBuilder* aBuilder)
   {
     for (OldItemInfo& i : mOldItems) {
       if (i.mItem) {
         i.mItem->Destroy(aBuilder);
       }
     }
     mOldItems.Clear();
     mDAG.Clear();