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:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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___ */
|
||||
|
||||
Reference in New Issue
Block a user