Bug 1404181 - Part 14: Add a bunch of new helpers to display list builder and display item for retaining and reusing items. r?mstange draft
authorMatt Woodrow <mwoodrow@mozilla.com>, Miko Mynttinen <mikokm@gmail.com>, Timothy Nikkel <tnikkel@gmail.com>
Thu, 28 Sep 2017 14:07:39 +1300
changeset 684525 b53ea3c0f90b46169a0c015cbfe4be45e9d501d6
parent 684524 1bb1dd71822d4151fcf79ad4a9cbd137d2e56ce1
child 684526 e4727c154f4902e1a50fc16de0e8e7549cbc1438
push id85633
push usermwoodrow@mozilla.com
push dateSun, 22 Oct 2017 23:03:02 +0000
reviewersmstange
bugs1404181
milestone58.0a1
Bug 1404181 - Part 14: Add a bunch of new helpers to display list builder and display item for retaining and reusing items. r?mstange MozReview-Commit-ID: AyiVFa6kofu
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
layout/painting/nsDisplayList.cpp
layout/painting/nsDisplayList.h
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -7220,27 +7220,50 @@ nsLayoutUtils::IsPopup(nsIFrame* aFrame)
 nsLayoutUtils::GetDisplayRootFrame(nsIFrame* aFrame)
 {
   // We could use GetRootPresContext() here if the
   // NS_FRAME_IN_POPUP frame bit is set.
   nsIFrame* f = aFrame;
   for (;;) {
     if (!f->HasAnyStateBits(NS_FRAME_IN_POPUP)) {
       f = f->PresContext()->FrameManager()->GetRootFrame();
+      if (!f) {
+        return aFrame;
+      }
     } else if (IsPopup(f)) {
       return f;
     }
     nsIFrame* parent = GetCrossDocParentFrame(f);
     if (!parent)
       return f;
     f = parent;
   }
 }
 
 /* static */ nsIFrame*
+nsLayoutUtils::GetViewportFrame(nsIFrame* aFrame)
+{
+  nsIFrame* f = aFrame;
+
+  for (;;) {
+    MOZ_ASSERT(f);
+    if (f->Type() == LayoutFrameType::Viewport) {
+      return f;
+    }
+
+    nsIFrame* parent = GetCrossDocParentFrame(f);
+    if (!parent) {
+      return f;
+    }
+
+    f = parent;
+  }
+}
+
+/* static */ nsIFrame*
 nsLayoutUtils::GetReferenceFrame(nsIFrame* aFrame)
 {
   nsIFrame *f = aFrame;
   for (;;) {
     const nsStyleDisplay* disp = f->StyleDisplay();
     if (f->IsTransformed(disp) || f->IsPreserve3DLeaf(disp) || IsPopup(f)) {
       return f;
     }
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -2021,16 +2021,21 @@ public:
 
   /**
    * Find the nearest "display root". This is the nearest enclosing
    * popup frame or the root prescontext's root frame.
    */
   static nsIFrame* GetDisplayRootFrame(nsIFrame* aFrame);
 
   /**
+   * Find the nearest viewport frame that is an ancestor of the given frame.
+   */
+  static nsIFrame* GetViewportFrame(nsIFrame* aFrame);
+
+  /**
    * Get the reference frame that would be used when constructing a
    * display item for this frame.  Rather than using their own frame
    * as a reference frame.)
    *
    * This duplicates some of the logic of GetDisplayRootFrame above and
    * of nsDisplayListBuilder::FindReferenceFrameFor.
    *
    * If you have an nsDisplayListBuilder, you should get the reference
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -904,43 +904,44 @@ nsDisplayListBuilder::AutoCurrentActiveS
       }
     }
   }
 
   mUsed = true;
 }
 
 nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
-    nsDisplayListBuilderMode aMode, bool aBuildCaret)
+    nsDisplayListBuilderMode aMode, bool aBuildCaret, bool aRetainingDisplayList)
     : mReferenceFrame(aReferenceFrame),
       mIgnoreScrollFrame(nullptr),
       mLayerEventRegions(nullptr),
       mCurrentTableItem(nullptr),
       mCurrentActiveScrolledRoot(nullptr),
       mCurrentContainerASR(nullptr),
       mCurrentFrame(aReferenceFrame),
       mCurrentReferenceFrame(aReferenceFrame),
-      mRootAGR(AnimatedGeometryRoot::CreateAGRForFrame(aReferenceFrame, nullptr, true, false)),
+      mRootAGR(AnimatedGeometryRoot::CreateAGRForFrame(aReferenceFrame, nullptr, true, aRetainingDisplayList)),
       mCurrentAGR(mRootAGR),
       mUsedAGRBudget(0),
       mDirtyRect(-1,-1,-1,-1),
       mGlassDisplayItem(nullptr),
       mScrollInfoItemsForHoisting(nullptr),
       mActiveScrolledRootForRootScrollframe(nullptr),
       mMode(aMode),
       mCurrentScrollParentId(FrameMetrics::NULL_SCROLL_ID),
       mCurrentScrollbarTarget(FrameMetrics::NULL_SCROLL_ID),
       mCurrentScrollbarFlags(0),
       mPerspectiveItemIndex(0),
       mSVGEffectsBuildingDepth(0),
       mContainsBlendMode(false),
       mIsBuildingScrollbar(false),
       mCurrentScrollbarWillHaveLayer(false),
       mBuildCaret(aBuildCaret),
-      mRetainingDisplayList(false),
+      mRetainingDisplayList(aRetainingDisplayList),
+      mPartialUpdate(false),
       mIgnoreSuppression(false),
       mIsAtRootOfPseudoStackingContext(false),
       mIncludeAllOutOfFlows(false),
       mDescendIntoSubdocuments(true),
       mSelectedFramesOnly(false),
       mAllowMergingAndFlattening(true),
       mWillComputePluginGeometry(false),
       mInTransform(false),
@@ -951,17 +952,18 @@ nsDisplayListBuilder::nsDisplayListBuild
       mContainsPluginItem(false),
       mAncestorHasApzAwareEventHandler(false),
       mHaveScrollableDisplayPort(false),
       mWindowDraggingAllowed(false),
       mIsBuildingForPopup(nsLayoutUtils::IsPopup(aReferenceFrame)),
       mForceLayerForScrollParent(false),
       mAsyncPanZoomEnabled(nsLayoutUtils::AsyncPanZoomEnabled(aReferenceFrame)),
       mBuildingInvisibleItems(false),
-      mHitTestShouldStopAtFirstOpaque(false)
+      mHitTestShouldStopAtFirstOpaque(false),
+      mIsBuilding(false)
 {
   MOZ_COUNT_CTOR(nsDisplayListBuilder);
 
   nsPresContext* pc = aReferenceFrame->PresContext();
   nsIPresShell *shell = pc->PresShell();
   if (pc->IsRenderingOnlySelection()) {
     nsCOMPtr<nsISelectionController> selcon(do_QueryInterface(shell));
     if (selcon) {
@@ -2461,33 +2463,30 @@ already_AddRefed<LayerManager> nsDisplay
     }
   }
 
   layerManager->SetUserData(&gLayerManagerLayerBuilder, oldBuilder);
   return layerManager.forget();
 }
 
 uint32_t nsDisplayList::Count() const {
-  uint32_t count = 0;
-  for (nsDisplayItem* i = GetBottom(); i; i = i->GetAbove()) {
-    ++count;
-  }
-  return count;
+  return mLength;
 }
 
 nsDisplayItem* nsDisplayList::RemoveBottom() {
   nsDisplayItem* item = mSentinel.mAbove;
   if (!item)
     return nullptr;
   mSentinel.mAbove = item->mAbove;
   if (item == mTop) {
     // must have been the only item
     mTop = &mSentinel;
   }
   item->mAbove = nullptr;
+  mLength--;
   return item;
 }
 
 void nsDisplayList::DeleteAll(nsDisplayListBuilder* aBuilder) {
   MOZ_DIAGNOSTIC_ASSERT(aBuilder == mBuilder);
   nsDisplayItem* item;
   while ((item = RemoveBottom()) != nullptr) {
     MOZ_DIAGNOSTIC_ASSERT(aBuilder->DebugContains(item));
@@ -2730,16 +2729,17 @@ nsDisplayItem::nsDisplayItem(nsDisplayLi
 
 nsDisplayItem::nsDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                              const ActiveScrolledRoot* aActiveScrolledRoot)
   : mFrame(aFrame)
   , mActiveScrolledRoot(aActiveScrolledRoot)
   , mAnimatedGeometryRoot(nullptr)
   , mForceNotVisible(aBuilder->IsBuildingInvisibleItems())
   , mDisableSubpixelAA(false)
+  , mReusedItem(false)
 #ifdef MOZ_DUMP_PAINTING
   , mPainted(false)
 #endif
 {
   MOZ_COUNT_CTOR(nsDisplayItem);
   if (aBuilder->IsRetainingDisplayList()) {
     mFrame->AddDisplayItem(this);
   }
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -407,17 +407,18 @@ public:
    * @param aReferenceFrame the frame at the root of the subtree; its origin
    * is the origin of the reference coordinate system for this display list
    * @param aMode encodes what the builder is being used for.
    * @param aBuildCaret whether or not we should include the caret in any
    * display lists that we make.
    */
   nsDisplayListBuilder(nsIFrame* aReferenceFrame,
                        nsDisplayListBuilderMode aMode,
-                       bool aBuildCaret);
+                       bool aBuildCaret,
+                       bool aRetainingDisplayList = false);
   ~nsDisplayListBuilder();
 
   void BeginFrame();
   void EndFrame();
 
   void AddTemporaryItem(nsDisplayItem* aItem)
   {
     mTemporaryItems.AppendElement(aItem);
@@ -601,20 +602,29 @@ public:
    * Calling this setter makes us compute accurate visible regions at the cost
    * of performance if regions get very complex.
    */
   bool GetAccurateVisibleRegions() { return mMode == nsDisplayListBuilderMode::PLUGIN_GEOMETRY; }
   /**
    * @return Returns true if we should include the caret in any display lists
    * that we make.
    */
-  bool IsBuildingCaret() { return mBuildCaret; }
+  bool IsBuildingCaret() const { return mBuildCaret; }
 
   bool IsRetainingDisplayList() const { return mRetainingDisplayList; }
 
+  bool IsPartialUpdate() const { return mPartialUpdate; }
+  void SetPartialUpdate(bool aPartial) { mPartialUpdate = aPartial; }
+
+  bool IsBuilding() const { return mIsBuilding; }
+  void SetIsBuilding(bool aIsBuilding)
+  {
+    mIsBuilding = aIsBuilding;
+  }
+
   /**
    * Allows callers to selectively override the regular paint suppression checks,
    * so that methods like GetFrameForPoint work when painting is suppressed.
    */
   void IgnorePaintSuppression() { mIgnoreSuppression = true; }
   /**
    * @return Returns if this builder will ignore paint suppression.
    */
@@ -1705,16 +1715,17 @@ private:
   Preserves3DContext             mPreserves3DCtx;
   uint32_t                       mPerspectiveItemIndex;
   int32_t                        mSVGEffectsBuildingDepth;
   bool                           mContainsBlendMode;
   bool                           mIsBuildingScrollbar;
   bool                           mCurrentScrollbarWillHaveLayer;
   bool                           mBuildCaret;
   bool                           mRetainingDisplayList;
+  bool                           mPartialUpdate;
   bool                           mIgnoreSuppression;
   bool                           mIsAtRootOfPseudoStackingContext;
   bool                           mIncludeAllOutOfFlows;
   bool                           mDescendIntoSubdocuments;
   bool                           mSelectedFramesOnly;
   bool                           mAllowMergingAndFlattening;
   bool                           mWillComputePluginGeometry;
   // True when we're building a display list that's directly or indirectly
@@ -1731,16 +1742,17 @@ private:
   // which WantsAsyncScroll().
   bool                           mHaveScrollableDisplayPort;
   bool                           mWindowDraggingAllowed;
   bool                           mIsBuildingForPopup;
   bool                           mForceLayerForScrollParent;
   bool                           mAsyncPanZoomEnabled;
   bool                           mBuildingInvisibleItems;
   bool                           mHitTestShouldStopAtFirstOpaque;
+  bool                           mIsBuilding;
 };
 
 class nsDisplayItem;
 class nsDisplayList;
 /**
  * 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.
@@ -1806,16 +1818,17 @@ public:
     : mFrame(aFrame)
     , mClipChain(nullptr)
     , mClip(nullptr)
     , mActiveScrolledRoot(nullptr)
     , mReferenceFrame(nullptr)
     , mAnimatedGeometryRoot(nullptr)
     , mForceNotVisible(false)
     , mDisableSubpixelAA(false)
+    , mReusedItem(false)
 #ifdef MOZ_DUMP_PAINTING
     , mPainted(false)
 #endif
   {
     MOZ_COUNT_CTOR(nsDisplayItem);
   }
 
 protected:
@@ -1875,16 +1888,17 @@ public:
     , mClip(aOther.mClip)
     , mActiveScrolledRoot(aOther.mActiveScrolledRoot)
     , mReferenceFrame(aOther.mReferenceFrame)
     , mAnimatedGeometryRoot(aOther.mAnimatedGeometryRoot)
     , mToReferenceFrame(aOther.mToReferenceFrame)
     , mVisibleRect(aOther.mVisibleRect)
     , mForceNotVisible(aOther.mForceNotVisible)
     , mDisableSubpixelAA(aOther.mDisableSubpixelAA)
+    , mReusedItem(false)
 #ifdef MOZ_DUMP_PAINTING
     , mPainted(false)
 #endif
   {
     MOZ_COUNT_CTOR(nsDisplayItem);
     if (aBuilder->IsRetainingDisplayList()) {
       mFrame->AddDisplayItem(this);
     }
@@ -1912,16 +1926,18 @@ public:
   virtual DisplayItemType GetType() const = 0;
   /**
    * Pairing this with the GetUnderlyingFrame() pointer gives a key that
    * uniquely identifies this display item in the display item tree.
    * XXX check nsOptionEventGrabberWrapper/nsXULEventRedirectorWrapper
    */
   virtual uint32_t GetPerFrameKey() const { return uint32_t(GetType()); }
 
+  uint8_t GetFlags() { return GetDisplayItemFlagsForType(GetType()); }
+
   /**
    * This is called after we've constructed a display list for event handling.
    * When this is called, we've already ensured that aRect intersects the
    * item's bounds and that clipping has been taking into account.
    *
    * @param aRect the point or rect being tested, relative to the reference
    * frame. If the width and height are both 1 app unit, it indicates we're
    * hit testing a point, not a rect.
@@ -1932,17 +1948,24 @@ public:
    */
   virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
                        HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) {}
   /**
    * @return the frame that this display item is based on. This is used to sort
    * items by z-index and content order and for some other uses. Never
    * returns null.
    */
-  inline nsIFrame* Frame() const { return mFrame; }
+  inline nsIFrame* Frame() const
+  {
+    MOZ_ASSERT(mFrame, "Trying to use display item after deletion!");
+    return mFrame;
+  }
+
+  bool HasDeletedFrame() const { return !mFrame; }
+
   /**
    * Compute the used z-index of our frame; returns zero for elements to which
    * z-index does not apply, and for z-index:auto.
    * @note This can be overridden, @see nsDisplayWrapList::SetOverrideZIndex.
    */
   virtual int32_t ZIndex() const;
   /**
    * The default bounds is the frame border rect.
@@ -2485,32 +2508,53 @@ public:
    * Intersect all clips in our clip chain up to (and including) aASR and set
    * set the intersection as this item's clip.
    */
   void FuseClipChainUpTo(nsDisplayListBuilder* aBuilder,
                          const ActiveScrolledRoot* aASR);
 
   bool BackfaceIsHidden() const { return mFrame->BackfaceIsHidden(); }
 
-  bool In3DContextAndBackfaceIsHidden() const
-  {
-    return Frame()->In3DContextAndBackfaceIsHidden();
+  bool In3DContextAndBackfaceIsHidden()
+  {
+    if (mBackfaceHidden) {
+      return *mBackfaceHidden;
+    }
+
+    // We never need to invalidate this cached value since we're
+    // guaranteed to rebuild the display item entirely if it changes.
+    bool backfaceHidden = Frame()->In3DContextAndBackfaceIsHidden();
+    mBackfaceHidden.emplace(backfaceHidden);
+
+    return backfaceHidden;
   }
 
   bool HasSameTypeAndClip(const nsDisplayItem* aOther) const
   {
     return GetType() == aOther->GetType() &&
            GetClipChain() == aOther->GetClipChain();
   }
 
   bool HasSameContent(const nsDisplayItem* aOther) const
   {
     return mFrame->GetContent() == aOther->Frame()->GetContent();
   }
 
+  bool IsReused() const
+  {
+    return mReusedItem;
+  }
+
+  void SetReused(bool aReused)
+  {
+    mReusedItem = aReused;
+  }
+
+  virtual bool CanBeReused() const { return true; }
+
   virtual nsIFrame* GetDependentFrame()
   {
     return nullptr;
   }
 
 protected:
   nsDisplayItem() = delete;
 
@@ -2531,20 +2575,22 @@ protected:
   // Display item construction sets this to the dirty rect.
   // nsDisplayList::ComputeVisibility sets this to the visible region
   // of the item by intersecting the current visible region with the bounds
   // of the item. Paint implementations can use this to limit their drawing.
   // Guaranteed to be contained in GetBounds().
   nsRect    mVisibleRect;
   bool      mForceNotVisible;
   bool      mDisableSubpixelAA;
+  bool      mReusedItem;
 #ifdef MOZ_DUMP_PAINTING
   // True if this frame has been painted.
   bool      mPainted;
 #endif
+  mozilla::Maybe<bool> mBackfaceHidden;
 
   struct {
     nsRect mVisibleRect;
     RefPtr<const DisplayItemClipChain> mClipChain;
     const DisplayItemClip* mClip;
   } mState;
 };
 
@@ -2570,16 +2616,17 @@ public:
   typedef mozilla::layers::LayerManager LayerManager;
   typedef mozilla::layers::PaintedLayer PaintedLayer;
 
   /**
    * Create an empty list.
    */
   explicit nsDisplayList(nsDisplayListBuilder* aBuilder)
     : mBuilder(aBuilder)
+    , mLength(0)
     , mIsOpaque(false)
     , mForceTransparentSurface(false)
   {
     mTop = &mSentinel;
     mSentinel.mAbove = nullptr;
   }
   ~nsDisplayList() {
     if (mSentinel.mAbove) {
@@ -2592,16 +2639,17 @@ public:
    * be in a list and cannot be null.
    */
   void AppendToTop(nsDisplayItem* aItem) {
     NS_ASSERTION(aItem, "No item to append!");
     NS_ASSERTION(!aItem->mAbove, "Already in a list!");
     MOZ_DIAGNOSTIC_ASSERT(mBuilder->DebugContains(aItem));
     mTop->mAbove = aItem;
     mTop = aItem;
+    mLength++;
   }
 
   /**
    * Append a new item to the top of the list. The intended usage is
    * AppendNewToTop(new ...);
    */
   void AppendNewToTop(nsDisplayItem* aItem) {
     if (aItem) {
@@ -2627,28 +2675,31 @@ public:
     NS_ASSERTION(aItem, "No item to append!");
     NS_ASSERTION(!aItem->mAbove, "Already in a list!");
     MOZ_DIAGNOSTIC_ASSERT(mBuilder->DebugContains(aItem));
     aItem->mAbove = mSentinel.mAbove;
     mSentinel.mAbove = aItem;
     if (mTop == &mSentinel) {
       mTop = aItem;
     }
+    mLength++;
   }
 
   /**
    * Removes all items from aList and appends them to the top of this list
    */
   void AppendToTop(nsDisplayList* aList) {
     MOZ_DIAGNOSTIC_ASSERT(mBuilder == aList->mBuilder);
     if (aList->mSentinel.mAbove) {
       mTop->mAbove = aList->mSentinel.mAbove;
       mTop = aList->mTop;
       aList->mTop = &aList->mSentinel;
       aList->mSentinel.mAbove = nullptr;
+      mLength += aList->mLength;
+      aList->mLength = 0;
     }
   }
 
   /**
    * Removes all items from aList and prepends them to the bottom of this list
    */
   void AppendToBottom(nsDisplayList* aList) {
     MOZ_DIAGNOSTIC_ASSERT(mBuilder == aList->mBuilder);
@@ -2656,16 +2707,18 @@ public:
       aList->mTop->mAbove = mSentinel.mAbove;
       mSentinel.mAbove = aList->mSentinel.mAbove;
       if (mTop == &mSentinel) {
         mTop = aList->mTop;
       }
 
       aList->mTop = &aList->mSentinel;
       aList->mSentinel.mAbove = nullptr;
+      mLength += aList->mLength;
+      aList->mLength = 0;
     }
   }
 
   /**
    * Remove an item from the bottom of the list and return it.
    */
   nsDisplayItem* RemoveBottom();
 
@@ -2866,23 +2919,21 @@ public:
   nsDisplayListBuilder* mBuilder;
   
   void RestoreState() {
     mIsOpaque = false;
     mForceTransparentSurface = false;
   }
 
 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;
-
   nsDisplayItemLink  mSentinel;
   nsDisplayItemLink* mTop;
 
+  uint32_t mLength;
+
   // This is set to true by FrameLayerBuilder if the final visible region
   // is empty (i.e. everything that was visible is covered by some
   // opaque content in this list).
   bool mIsOpaque;
   // This is set to true by FrameLayerBuilder if any display item in this
   // list needs to force the surface containing this list to be transparent.
   bool mForceTransparentSurface;
 };