Bug 1873530 Part 3 - Unify continuation linking operations by removing SetPrevContinuation() and SetPrevInFlow(). r=jfkthame

SetNextContinuation() and SetPrevContinuation() are almost always called
together when setting up a continuation link, but the callers don't call them in
particular order. We should unify them as one method so that it's more
ergonomics and robust, especially when we do more complex work such as caching
continuations. Same reason for SetNextInFlow() and SetPrevInFlow().

We choose to merge the SetPrevContinuation() code into SetNextContinuation() for
the symmetry of SetNextSibling(). (Yes, we don't have SetPrevSibling().)

This patch doesn't change behavior.

Differential Revision: https://phabricator.services.mozilla.com/D197966
This commit is contained in:
Ting-Yu Lin
2024-01-11 19:50:58 +00:00
parent fd8f769590
commit 1c5fd05aca
8 changed files with 142 additions and 157 deletions

View File

@@ -4070,7 +4070,7 @@ void nsTextFrame::Destroy(DestroyContext& aContext) {
// type might be changing. Not clear whether it's worth it.
ClearTextRuns();
if (mNextContinuation) {
mNextContinuation->SetPrevInFlow(nullptr);
SetNextInFlow(nullptr);
}
// Let the base class destroy the frame
nsIFrame::Destroy(aContext);
@@ -4109,6 +4109,7 @@ class nsContinuingTextFrame final : public nsTextFrame {
public:
NS_DECL_FRAMEARENA_HELPERS(nsContinuingTextFrame)
friend class nsTextFrame;
friend nsIFrame* NS_NewContinuingTextFrame(mozilla::PresShell* aPresShell,
ComputedStyle* aStyle);
@@ -4119,33 +4120,11 @@ class nsContinuingTextFrame final : public nsTextFrame {
nsTextFrame* GetPrevContinuation() const final { return mPrevContinuation; }
void SetPrevContinuation(nsIFrame* aPrevContinuation) final {
NS_ASSERTION(!aPrevContinuation || Type() == aPrevContinuation->Type(),
"setting a prev continuation with incorrect type!");
NS_ASSERTION(
!nsSplittableFrame::IsInPrevContinuationChain(aPrevContinuation, this),
"creating a loop in continuation chain!");
mPrevContinuation = static_cast<nsTextFrame*>(aPrevContinuation);
RemoveStateBits(NS_FRAME_IS_FLUID_CONTINUATION);
UpdateCachedContinuations();
}
nsTextFrame* GetPrevInFlow() const final {
return HasAnyStateBits(NS_FRAME_IS_FLUID_CONTINUATION) ? mPrevContinuation
: nullptr;
}
void SetPrevInFlow(nsIFrame* aPrevInFlow) final {
NS_ASSERTION(!aPrevInFlow || Type() == aPrevInFlow->Type(),
"setting a prev in flow with incorrect type!");
NS_ASSERTION(
!nsSplittableFrame::IsInPrevContinuationChain(aPrevInFlow, this),
"creating a loop in continuation chain!");
mPrevContinuation = static_cast<nsTextFrame*>(aPrevInFlow);
AddStateBits(NS_FRAME_IS_FLUID_CONTINUATION);
UpdateCachedContinuations();
}
// Call this helper to update cache after mPrevContinuation is changed.
void UpdateCachedContinuations() {
nsTextFrame* prevFirst = mFirstContinuation;
@@ -4215,7 +4194,6 @@ void nsContinuingTextFrame::Init(nsIContent* aContent,
// Hook the frame into the flow
nsTextFrame* prev = static_cast<nsTextFrame*>(aPrevInFlow);
nsTextFrame* nextContinuation = prev->GetNextContinuation();
SetPrevInFlow(aPrevInFlow);
aPrevInFlow->SetNextInFlow(this);
// NOTE: bypassing nsTextFrame::Init!!!
@@ -4247,7 +4225,6 @@ void nsContinuingTextFrame::Init(nsIContent* aContent,
if (nextContinuation) {
SetNextContinuation(nextContinuation);
nextContinuation->SetPrevContinuation(this);
// Adjust next-continuations' content offset as needed.
while (nextContinuation &&
nextContinuation->GetContentOffset() < mContentOffset) {
@@ -4384,6 +4361,73 @@ Maybe<nsIFrame::Cursor> nsTextFrame::GetCursor(const nsPoint& aPoint) {
return Some(Cursor{kind, AllowCustomCursorImage::Yes});
}
void nsTextFrame::SetNextContinuation(nsIFrame* aFrame) {
MOZ_ASSERT(!aFrame || Type() == aFrame->Type(),
"Setting a next-continuation with an incorrect type!");
MOZ_ASSERT(!nsSplittableFrame::IsInNextContinuationChain(aFrame, this),
"We shouldn't be in aFrame's next-continuation chain!");
MOZ_ASSERT(!nsSplittableFrame::IsInPrevContinuationChain(this, aFrame),
"aFrame shouldn't be in our prev-continuation chain!");
if (mNextContinuation && mNextContinuation != aFrame) {
// We have an existing non-null next-continuation. Break the link.
MOZ_ASSERT(mNextContinuation->GetPrevContinuation() == this,
"The existing link is wrong!");
auto* next = static_cast<nsContinuingTextFrame*>(mNextContinuation);
next->mPrevContinuation = nullptr;
next->UpdateCachedContinuations();
}
auto* next = static_cast<nsContinuingTextFrame*>(aFrame);
mNextContinuation = next;
if (next) {
next->RemoveStateBits(NS_FRAME_IS_FLUID_CONTINUATION);
next->mPrevContinuation = this;
next->UpdateCachedContinuations();
}
// Setting a non-fluid continuation might affect our flow length (they're
// quite rare so we assume it always does) so we delete our cached value:
if (GetContent()->HasFlag(NS_HAS_FLOWLENGTH_PROPERTY)) {
GetContent()->RemoveProperty(nsGkAtoms::flowlength);
GetContent()->UnsetFlags(NS_HAS_FLOWLENGTH_PROPERTY);
}
}
void nsTextFrame::SetNextInFlow(nsIFrame* aFrame) {
MOZ_ASSERT(!aFrame || Type() == aFrame->Type(),
"Setting a next-in-flow with an incorrect type!");
MOZ_ASSERT(!nsSplittableFrame::IsInNextContinuationChain(aFrame, this),
"We shouldn't be in aFrame's next-continuation chain!");
MOZ_ASSERT(!nsSplittableFrame::IsInPrevContinuationChain(this, aFrame),
"aFrame shouldn't be in our prev-continuation chain!");
if (mNextContinuation && mNextContinuation != aFrame) {
// We have an existing non-null next-continuation. Break the link.
MOZ_ASSERT(mNextContinuation->GetPrevContinuation() == this,
"The existing link is wrong!");
auto* next = static_cast<nsContinuingTextFrame*>(mNextContinuation);
next->mPrevContinuation = nullptr;
next->UpdateCachedContinuations();
}
auto* next = static_cast<nsContinuingTextFrame*>(aFrame);
mNextContinuation = next;
if (next) {
if (!next->HasAnyStateBits(NS_FRAME_IS_FLUID_CONTINUATION)) {
// Changing from non-fluid to fluid continuation might affect our flow
// length, so we delete our cached value:
if (GetContent()->HasFlag(NS_HAS_FLOWLENGTH_PROPERTY)) {
GetContent()->RemoveProperty(nsGkAtoms::flowlength);
GetContent()->UnsetFlags(NS_HAS_FLOWLENGTH_PROPERTY);
}
}
next->AddStateBits(NS_FRAME_IS_FLUID_CONTINUATION);
next->mPrevContinuation = this;
next->UpdateCachedContinuations();
}
}
nsTextFrame* nsTextFrame::LastInFlow() const {
nsTextFrame* lastInFlow = const_cast<nsTextFrame*>(this);
while (lastInFlow->GetNextInFlow()) {
@@ -9041,16 +9085,13 @@ static void RemoveEmptyInFlows(nsTextFrame* aFrame,
}
}
prevContinuation->SetNextInFlow(aFirstToNotRemove);
aFirstToNotRemove->SetPrevInFlow(prevContinuation);
// **Note: it is important here that we clear the Next link from lastRemoved
// BEFORE clearing the Prev link from aFrame, because SetPrevInFlow() will
// follow the Next pointers, wiping out the cached mFirstContinuation field
// from each following frame in the list. We need this to stop when it
// reaches lastRemoved!
// Note: it is important here that we disconnect lastRemoved and its
// next-continuation BEFORE connecting prevContinuation and aFirstToNotRemove,
// because SetNextInFlow() will follow the Next pointers, wiping out the
// cached mFirstContinuation field from each following frame in the list. We
// need this to stop when it reaches lastRemoved!
lastRemoved->SetNextInFlow(nullptr);
aFrame->SetPrevInFlow(nullptr);
prevContinuation->SetNextInFlow(aFirstToNotRemove);
nsContainerFrame* parent = aFrame->GetParent();
nsIFrame::DestroyContext context(aFrame->PresShell());