From b6d8f8d678282feb6709b128fef8f786da3c7aba Mon Sep 17 00:00:00 2001 From: "roc+%cs.cmu.edu" Date: Tue, 18 Sep 2007 03:00:16 +0000 Subject: [PATCH] Bug 385607. Ensure that whitespace is collapsed across text-run boundaries across incremental updates like adding/removing frames in the middle of the textrun. r+sr+a=dbaron git-svn-id: svn://10.0.0.236/trunk@236189 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/layout/generic/nsBlockFrame.cpp | 29 ++++++++++++++++++-- mozilla/layout/generic/nsIFrame.h | 7 ++++- mozilla/layout/generic/nsLineBox.h | 10 +++++++ mozilla/layout/generic/nsTextFrameThebes.cpp | 7 +++-- 4 files changed, 47 insertions(+), 6 deletions(-) diff --git a/mozilla/layout/generic/nsBlockFrame.cpp b/mozilla/layout/generic/nsBlockFrame.cpp index 08871532822..f73645e4d29 100644 --- a/mozilla/layout/generic/nsBlockFrame.cpp +++ b/mozilla/layout/generic/nsBlockFrame.cpp @@ -665,6 +665,7 @@ nsBlockFrame::GetMinWidth(nsIRenderingContext *aRenderingContext) } // XXX Bug NNNNNN Should probably handle percentage text-indent. + data.line = &line; nsIFrame *kid = line->mFirstChild; for (PRInt32 i = 0, i_end = line->GetChildCount(); i != i_end; ++i, kid = kid->GetNextSibling()) { @@ -736,6 +737,7 @@ nsBlockFrame::GetPrefWidth(nsIRenderingContext *aRenderingContext) } // XXX Bug NNNNNN Should probably handle percentage text-indent. + data.line = &line; nsIFrame *kid = line->mFirstChild; for (PRInt32 i = 0, i_end = line->GetChildCount(); i != i_end; ++i, kid = kid->GetNextSibling()) { @@ -4798,12 +4800,19 @@ nsBlockFrame::AddFrames(nsIFrame* aFrameList, prevSibLine->SetChildCount(prevSibLine->GetChildCount() - rem); prevSibLine->MarkDirty(); } + // Force the lines next to where we're inserting content to regenerate + // their textruns + prevSibLine->SetInvalidateTextRuns(PR_TRUE); + if (prevSibLine.next() != end_lines()) { + prevSibLine.next()->SetInvalidateTextRuns(PR_TRUE); + } // Now (partially) join the sibling lists together aPrevSibling->SetNextSibling(aFrameList); } else if (! mLines.empty()) { prevSiblingNextFrame = mLines.front()->mFirstChild; + mLines.front()->SetInvalidateTextRuns(PR_TRUE); } // Walk through the new frames being added and update the line data @@ -5212,6 +5221,10 @@ found_frame:; NS_ERROR("can't find deleted frame in lines"); return NS_ERROR_FAILURE; } + + if (line != mLines.front()) { + line.prev()->SetInvalidateTextRuns(PR_TRUE); + } if (prevSibling && !prevSibling->GetNextSibling()) { // We must have found the first frame in the overflow line list. So @@ -5224,6 +5237,8 @@ found_frame:; NS_ASSERTION(this == aDeletedFrame->GetParent(), "messed up delete code"); NS_ASSERTION(line->Contains(aDeletedFrame), "frame not in line"); + line->SetInvalidateTextRuns(PR_TRUE); + // If the frame being deleted is the last one on the line then // optimize away the line->Contains(next-in-flow) call below. PRBool isLastFrameOnLine = (1 == line->GetChildCount() || @@ -5370,6 +5385,10 @@ found_frame:; } } } + + if (line.next() != line_end) { + line.next()->SetInvalidateTextRuns(PR_TRUE); + } #ifdef DEBUG VerifyLines(PR_TRUE); @@ -6104,10 +6123,16 @@ nsBlockFrame::ChildIsDirty(nsIFrame* aChild) // otherwise we have an empty line list, and ReflowDirtyLines // will handle reflowing the bullet. } else { - // Mark the line containing the child frame dirty. + // Mark the line containing the child frame dirty. We would rather do this + // in MarkIntrinsicWidthsDirty but that currently won't tell us which + // child is being dirtied. line_iterator fline = FindLineFor(aChild); - if (fline != end_lines()) + if (fline != end_lines()) { + // An inline descendant might have been added or removed, so we should + // reconstruct textruns. + fline->SetInvalidateTextRuns(PR_TRUE); MarkLineDirty(fline); + } } nsBlockFrameSuper::ChildIsDirty(aChild); diff --git a/mozilla/layout/generic/nsIFrame.h b/mozilla/layout/generic/nsIFrame.h index 66f8ca36dde..a61c8a675d3 100644 --- a/mozilla/layout/generic/nsIFrame.h +++ b/mozilla/layout/generic/nsIFrame.h @@ -92,6 +92,7 @@ class nsDisplayListSet; class nsDisplayList; class gfxSkipChars; class gfxSkipCharsIterator; +class nsLineList_iterator; struct nsPeekOffsetStruct; struct nsPoint; @@ -1137,12 +1138,16 @@ public: */ struct InlineIntrinsicWidthData { InlineIntrinsicWidthData() - : prevLines(0) + : line(nsnull) + , prevLines(0) , currentLine(0) , skipWhitespace(PR_TRUE) , trailingWhitespace(0) {} + // The line. This may be null if the inlines are not associated with a block. + const nsLineList_iterator* line; + // The maximum intrinsic width for all previous lines. nscoord prevLines; diff --git a/mozilla/layout/generic/nsLineBox.h b/mozilla/layout/generic/nsLineBox.h index faa97e01ec2..bb032a4a0af 100644 --- a/mozilla/layout/generic/nsLineBox.h +++ b/mozilla/layout/generic/nsLineBox.h @@ -305,6 +305,15 @@ public: return mFlags.mLineWrapped; } + // mInvalidateTextRuns bit + void SetInvalidateTextRuns(PRBool aOn) { + NS_ASSERTION((PR_FALSE==aOn || PR_TRUE==aOn), "somebody is playing fast and loose with bools and bits!"); + mFlags.mInvalidateTextRuns = aOn; + } + PRBool GetInvalidateTextRuns() const { + return mFlags.mInvalidateTextRuns; + } + // mResizeReflowOptimizationDisabled bit void DisableResizeReflowOptimization() { mFlags.mResizeReflowOptimizationDisabled = PR_TRUE; @@ -487,6 +496,7 @@ public: PRUint32 mBlock : 1; PRUint32 mImpactedByFloat : 1; PRUint32 mLineWrapped: 1; + PRUint32 mInvalidateTextRuns : 1; PRUint32 mResizeReflowOptimizationDisabled: 1; // default 0 = means that the opt potentially applies to this line. 1 = never skip reflowing this line for a resize reflow PRUint32 mEmptyCacheValid: 1; PRUint32 mEmptyCacheState: 1; diff --git a/mozilla/layout/generic/nsTextFrameThebes.cpp b/mozilla/layout/generic/nsTextFrameThebes.cpp index 0b907023ba9..2b394ad6e7e 100644 --- a/mozilla/layout/generic/nsTextFrameThebes.cpp +++ b/mozilla/layout/generic/nsTextFrameThebes.cpp @@ -1206,6 +1206,7 @@ BuildTextRuns(nsIRenderingContext* aRC, nsTextFrame* aForFrame, line = forwardIterator.GetLine(); if (line->IsBlock()) break; + line->SetInvalidateTextRuns(PR_FALSE); scanner.SetAtStartOfLine(); scanner.SetCommonAncestorWithLastFrame(nsnull); nsIFrame* child = line->mFirstChild; @@ -1966,7 +1967,7 @@ nsTextFrame::EnsureTextRun(nsIRenderingContext* aRC, nsIFrame* aLineContainer, const nsLineList::iterator* aLine, PRUint32* aFlowEndInTextRun) { - if (mTextRun) { + if (mTextRun && (!aLine || !(*aLine)->GetInvalidateTextRuns())) { if (mTextRun->GetExpirationState()->IsTracked()) { gTextRuns->MarkUsed(mTextRun); } @@ -5001,7 +5002,7 @@ nsTextFrame::AddInlineMinWidthForFlow(nsIRenderingContext *aRenderingContext, { PRUint32 flowEndInTextRun; gfxSkipCharsIterator iter = - EnsureTextRun(aRenderingContext, nsnull, nsnull, &flowEndInTextRun); + EnsureTextRun(aRenderingContext, nsnull, aData->line, &flowEndInTextRun); if (!mTextRun) return; @@ -5099,7 +5100,7 @@ nsTextFrame::AddInlinePrefWidthForFlow(nsIRenderingContext *aRenderingContext, { PRUint32 flowEndInTextRun; gfxSkipCharsIterator iter = - EnsureTextRun(aRenderingContext, nsnull, nsnull, &flowEndInTextRun); + EnsureTextRun(aRenderingContext, nsnull, aData->line, &flowEndInTextRun); if (!mTextRun) return;