Bug 1377329 - Part 1. Cache the length of frames in nsFrameList. draft
authorcku <cku@mozilla.com>
Fri, 14 Jul 2017 12:47:12 +0800
changeset 608883 bff909d3dcba24b09d88e815ab79f200fd92e9fa
parent 607967 30ea2905130e85f9e1d8d56fa3097901eec6514b
child 608884 3ba829dddc087cddf76a4591e7746557a3cb1d12
child 608886 105804994e32827ff87ea7fcacecf942df41a051
push id68435
push userbmo:cku@mozilla.com
push dateFri, 14 Jul 2017 08:45:34 +0000
bugs1377329
milestone56.0a1
Bug 1377329 - Part 1. Cache the length of frames in nsFrameList. nsTableRowGroupFrame::GetRowCount() also uses nsFrameList::GetLength(). In this scenario, since using nsFrameList::GetLength() is not preventable, we can cache the length of frames inside nsFrameList to reduce time spend on tracing frame link list. MozReview-Commit-ID: HiDK7mF2AdL
layout/generic/nsFrameList.cpp
layout/generic/nsFrameList.h
layout/generic/nsIFrame.h
--- a/layout/generic/nsFrameList.cpp
+++ b/layout/generic/nsFrameList.cpp
@@ -41,27 +41,29 @@ nsFrameList::Delete(nsIPresShell* aPresS
 
 void
 nsFrameList::DestroyFrames()
 {
   while (nsIFrame* frame = RemoveFirstChild()) {
     frame->Destroy();
   }
   mLastChild = nullptr;
+  mLength.reset();
 }
 
 void
 nsFrameList::DestroyFramesFrom(nsIFrame* aDestructRoot)
 {
   NS_PRECONDITION(aDestructRoot, "Missing destruct root");
 
   while (nsIFrame* frame = RemoveFirstChild()) {
     frame->DestroyFrom(aDestructRoot);
   }
   mLastChild = nullptr;
+  mLength.reset();
 }
 
 void
 nsFrameList::SetFrames(nsIFrame* aFrameList)
 {
   NS_PRECONDITION(!mFirstChild, "Losing frames");
 
   mFirstChild = aFrameList;
@@ -90,16 +92,18 @@ nsFrameList::RemoveFrame(nsIFrame* aFram
     NS_ASSERTION(prevSibling && prevSibling->GetNextSibling() == aFrame,
                  "Broken frame linkage");
     prevSibling->SetNextSibling(nextFrame);
     aFrame->SetNextSibling(nullptr);
     if (!nextFrame) {
       mLastChild = prevSibling;
     }
   }
+
+  mLength.reset();
 }
 
 nsFrameList
 nsFrameList::RemoveFramesAfter(nsIFrame* aAfterFrame)
 {
   if (!aAfterFrame) {
     nsFrameList result;
     result.InsertFrames(nullptr, nullptr, *this);
@@ -111,16 +115,17 @@ nsFrameList::RemoveFramesAfter(nsIFrame*
   NS_PRECONDITION(ContainsFrame(aAfterFrame), "wrong list");
 #endif
 
   nsIFrame* tail = aAfterFrame->GetNextSibling();
   // if (!tail) return EmptyList();  -- worth optimizing this case?
   nsIFrame* oldLastChild = mLastChild;
   mLastChild = aAfterFrame;
   aAfterFrame->SetNextSibling(nullptr);
+  mLength.reset();
   return nsFrameList(tail, tail ? oldLastChild : nullptr);
 }
 
 nsIFrame*
 nsFrameList::RemoveFirstChild()
 {
   if (mFirstChild) {
     nsIFrame* firstChild = mFirstChild;
@@ -175,16 +180,17 @@ nsFrameList::InsertFrames(nsContainerFra
   lastNewFrame->SetNextSibling(nextSibling);
   if (!nextSibling) {
     mLastChild = lastNewFrame;
   }
 
   VerifyList();
 
   aFrameList.Clear();
+  mLength.reset();
   return Slice(*this, firstNewFrame, nextSibling);
 }
 
 nsFrameList
 nsFrameList::ExtractHead(FrameLinkEnumerator& aLink)
 {
   NS_PRECONDITION(&aLink.List() == this, "Unexpected list");
   NS_PRECONDITION(!aLink.PrevFrame() ||
@@ -208,16 +214,17 @@ nsFrameList::ExtractHead(FrameLinkEnumer
     newFirstFrame = mFirstChild;
     mFirstChild = aLink.NextFrame();
     if (!mFirstChild) { // we handed over the whole list
       mLastChild = nullptr;
     }
 
     // Now make sure aLink doesn't point to a frame we no longer have.
     aLink.mPrev = nullptr;
+    mLength.reset();
   }
   // else aLink is pointing to before our first frame.  Nothing to do.
 
   return nsFrameList(newFirstFrame, prev);
 }
 
 nsFrameList
 nsFrameList::ExtractTail(FrameLinkEnumerator& aLink)
@@ -247,16 +254,18 @@ nsFrameList::ExtractTail(FrameLinkEnumer
     mLastChild = prev;
   } else {
     // Hand the whole list over to our new list
     newFirstFrame = mFirstChild;
     newLastFrame = mLastChild;
     Clear();
   }
 
+  mLength.reset();
+
   // Now make sure aLink doesn't point to a frame we no longer have.
   aLink.mFrame = nullptr;
 
   NS_POSTCONDITION(aLink.AtEnd(), "What's going on here?");
 
   return nsFrameList(newFirstFrame, newLastFrame);
 }
 
@@ -297,16 +306,28 @@ nsFrameList::ContainsFrame(const nsIFram
     frame = frame->GetNextSibling();
   }
   return false;
 }
 
 int32_t
 nsFrameList::GetLength() const
 {
+  MOZ_ASSERT_IF(mLength, mLength.ref() == ComputeLength());
+
+  if (!mLength) {
+    mLength = Some(ComputeLength());
+  }
+
+  return mLength.ref();
+}
+
+int32_t
+nsFrameList::ComputeLength() const
+{
   int32_t count = 0;
   nsIFrame* frame = mFirstChild;
   while (frame) {
     count++;
     frame = frame->GetNextSibling();
   }
   return count;
 }
--- a/layout/generic/nsFrameList.h
+++ b/layout/generic/nsFrameList.h
@@ -5,16 +5,17 @@
 
 #ifndef nsFrameList_h___
 #define nsFrameList_h___
 
 #include <stdio.h> /* for FILE* */
 #include "nsDebug.h"
 #include "nsTArrayForwardDeclare.h"
 #include "mozilla/ReverseIterator.h"
+#include "mozilla/Maybe.h"
 
 #if defined(DEBUG) || defined(MOZ_DUMP_PAINTING)
 // DEBUG_FRAME_DUMP enables nsIFrame::List and related methods.
 // You can also define this in a non-DEBUG build if you need frame dumps.
 #define DEBUG_FRAME_DUMP 1
 #endif
 
 class nsContainerFrame;
@@ -496,16 +497,17 @@ public:
   const_iterator cend() const { return end(); }
   reverse_iterator rbegin() const { return reverse_iterator(end()); }
   const_reverse_iterator crbegin() const { return rbegin(); }
   reverse_iterator rend() const { return reverse_iterator(begin()); }
   const_reverse_iterator crend() const { return rend(); }
 
 private:
   void operator delete(void*) = delete;
+  int32_t ComputeLength() const;
 
 #ifdef DEBUG_FRAME_LIST
   void VerifyList() const;
 #else
   void VerifyList() const {}
 #endif
 
 protected:
@@ -514,16 +516,17 @@ protected:
    * is NOT the first or last sibling, because otherwise its nsFrameList will
    * have a stale mFirst/LastChild pointer.  This precondition is asserted.
    * This function is O(1).
    */
   static void UnhookFrameFromSiblings(nsIFrame* aFrame);
 
   nsIFrame* mFirstChild;
   nsIFrame* mLastChild;
+  mutable mozilla::Maybe<int32_t> mLength;
 };
 
 inline bool
 operator==(const nsFrameList::Iterator& aIter1,
            const nsFrameList::Iterator& aIter2)
 {
   MOZ_ASSERT(&aIter1.mList == &aIter2.mList,
              "must not compare iterator from different list");
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -4518,16 +4518,18 @@ nsFrameList::ContinueRemoveFrame(nsIFram
     return true;
   }
   return false;
 }
 
 inline bool
 nsFrameList::StartRemoveFrame(nsIFrame* aFrame)
 {
+  mLength.reset();
+
   if (aFrame->GetPrevSibling() && aFrame->GetNextSibling()) {
     UnhookFrameFromSiblings(aFrame);
     return true;
   }
   return ContinueRemoveFrame(aFrame);
 }
 
 inline void