Bug 1926512 - Cache first-continuation/first-in-flow pointers directly in nsSplittableFrame. r=jwatt

Differential Revision: https://phabricator.services.mozilla.com/D226607
This commit is contained in:
Jonathan Kew
2024-10-23 11:38:48 +00:00
parent 6bd05b9603
commit 5abc524db9
2 changed files with 28 additions and 60 deletions

View File

@@ -20,12 +20,6 @@ NS_QUERYFRAME_HEAD(nsSplittableFrame)
NS_QUERYFRAME_ENTRY(nsSplittableFrame)
NS_QUERYFRAME_TAIL_INHERITING(nsIFrame)
// These frame properties cache the first-continuation and first-in-flow frame
// pointers. All nsSplittableFrames other than the first one in the continuation
// chain will have these properties set.
NS_DECLARE_FRAME_PROPERTY_WITHOUT_DTOR(FirstContinuationProperty, nsIFrame);
NS_DECLARE_FRAME_PROPERTY_WITHOUT_DTOR(FirstInFlowProperty, nsIFrame);
void nsSplittableFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
nsIFrame* aPrevInFlow) {
if (aPrevInFlow) {
@@ -75,23 +69,9 @@ void nsSplittableFrame::SetNextContinuation(nsIFrame* aFrame) {
}
}
nsIFrame* nsSplittableFrame::GetFirstContinuationIfCached() const {
if (!GetPrevContinuation()) {
MOZ_ASSERT(
!HasProperty(FirstContinuationProperty()),
"The property shouldn't be present on first-continuation itself!");
return const_cast<nsSplittableFrame*>(this);
}
nsIFrame* firstContinuation = GetProperty(FirstContinuationProperty());
MOZ_ASSERT(!firstContinuation || !firstContinuation->GetPrevContinuation(),
"First continuation shouldn't have a prev continuation!");
return firstContinuation;
}
nsIFrame* nsSplittableFrame::FirstContinuation() const {
if (nsIFrame* firstContinuation = GetFirstContinuationIfCached()) {
return firstContinuation;
if (mFirstContinuation) {
return mFirstContinuation;
}
// We fall back to the slow path during the frame destruction where our
@@ -173,22 +153,9 @@ void nsSplittableFrame::SetNextInFlow(nsIFrame* aFrame) {
}
}
nsIFrame* nsSplittableFrame::GetFirstInFlowIfCached() const {
if (!GetPrevInFlow()) {
MOZ_ASSERT(!HasProperty(FirstInFlowProperty()),
"The property shouldn't be present on first-in-flow itself!");
return const_cast<nsSplittableFrame*>(this);
}
nsIFrame* firstInFlow = GetProperty(FirstInFlowProperty());
MOZ_ASSERT(!firstInFlow || !firstInFlow->GetPrevInFlow(),
"First-in-flow shouldn't have a prev-in-flow!");
return firstInFlow;
}
nsIFrame* nsSplittableFrame::FirstInFlow() const {
if (nsIFrame* firstInFlow = GetFirstInFlowIfCached()) {
return firstInFlow;
if (mFirstInFlow) {
return mFirstInFlow;
}
// We fall back to the slow path during the frame destruction where our
@@ -241,14 +208,14 @@ void nsSplittableFrame::RemoveFromFlow(nsIFrame* aFrame) {
}
void nsSplittableFrame::UpdateFirstContinuationAndFirstInFlowCache() {
nsIFrame* oldCachedFirstContinuation =
GetProperty(FirstContinuationProperty());
nsIFrame* oldCachedFirstContinuation = mFirstContinuation;
if (nsIFrame* prevContinuation = GetPrevContinuation()) {
nsIFrame* newFirstContinuation = prevContinuation->FirstContinuation();
if (oldCachedFirstContinuation != newFirstContinuation) {
// Update the first-continuation cache for us and our next-continuations.
for (nsIFrame* f = this; f; f = f->GetNextContinuation()) {
f->SetProperty(FirstContinuationProperty(), newFirstContinuation);
for (nsSplittableFrame* f = this; f;
f = reinterpret_cast<nsSplittableFrame*>(f->GetNextContinuation())) {
f->mFirstContinuation = newFirstContinuation;
}
}
} else {
@@ -259,30 +226,33 @@ void nsSplittableFrame::UpdateFirstContinuationAndFirstInFlowCache() {
// next-continuations here, but that would result in overall O(n^2)
// behavior when a frame list is destroyed from the front. To avoid that
// pathological behavior, we simply purge the cached values.
for (nsIFrame* f = this; f; f = f->GetNextContinuation()) {
f->RemoveProperty(FirstContinuationProperty());
for (nsSplittableFrame* f = this; f;
f = reinterpret_cast<nsSplittableFrame*>(f->GetNextContinuation())) {
f->mFirstContinuation = nullptr;
}
}
}
nsIFrame* oldCachedFirstInFlow = GetProperty(FirstInFlowProperty());
nsIFrame* oldCachedFirstInFlow = mFirstInFlow;
if (nsIFrame* prevInFlow = GetPrevInFlow()) {
nsIFrame* newFirstInFlow = prevInFlow->FirstInFlow();
if (oldCachedFirstInFlow != newFirstInFlow) {
// Update the first-in-flow cache for us and our next-in-flows.
for (nsIFrame* f = this; f; f = f->GetNextInFlow()) {
f->SetProperty(FirstInFlowProperty(), newFirstInFlow);
for (nsSplittableFrame* f = this; f;
f = reinterpret_cast<nsSplittableFrame*>(f->GetNextInFlow())) {
f->mFirstInFlow = newFirstInFlow;
}
}
} else {
// We become the new first-in-flow due to our prev-in-flow being removed.
if (oldCachedFirstInFlow) {
// It's tempting to update the first-continuation cache for our
// next-continuations here, but that would result in overall O(n^2)
// It's tempting to update the first-in-flow cache for our
// next-in-flows here, but that would result in overall O(n^2)
// behavior when a frame list is destroyed from the front. To avoid that
// pathological behavior, we simply purge the cached values.
for (nsIFrame* f = this; f; f = f->GetNextInFlow()) {
f->RemoveProperty(FirstInFlowProperty());
for (nsSplittableFrame* f = this; f;
f = reinterpret_cast<nsSplittableFrame*>(f->GetNextInFlow())) {
f->mFirstInFlow = nullptr;
}
}
}

View File

@@ -93,16 +93,6 @@ class nsSplittableFrame : public nsIFrame {
ClassID aID)
: nsIFrame(aStyle, aPresContext, aID) {}
// Return the first-continuation for this frame if this frame is the
// first-continuation in the chain or if it has a cached first-continuation.
// Otherwise, return nullptr.
nsIFrame* GetFirstContinuationIfCached() const;
// Return the first-in-flow for this frame if this frame is the first-in-flow
// in the chain or if it has a cached first-in-flow. Otherwise, return
// nullptr.
nsIFrame* GetFirstInFlowIfCached() const;
// Update the first-continuation and first-in-flow cache for this frame and
// the next-continuations in the chain.
//
@@ -171,6 +161,14 @@ class nsSplittableFrame : public nsIFrame {
nsIFrame* mPrevContinuation = nullptr;
nsIFrame* mNextContinuation = nullptr;
/**
* Cached pointers to the first-continuation and first-in-flow, if currently
* known. These may be null, in which case the first-* will need to be found
* by following the chain.
*/
nsIFrame* mFirstContinuation = nullptr;
nsIFrame* mFirstInFlow = nullptr;
};
#endif /* nsSplittableFrame_h___ */