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
--- 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