Bug 580167 part 1. Create an nsTextFrame::ReflowText that doesn't need a reflow state. r=dbaron

This commit is contained in:
Boris Zbarsky 2010-08-25 14:54:46 -04:00
parent 51fff79f07
commit 49fc0489a4
2 changed files with 66 additions and 42 deletions

View File

@ -367,6 +367,11 @@ public:
TrimmedOffsets GetTrimmedOffsets(const nsTextFragment* aFrag, TrimmedOffsets GetTrimmedOffsets(const nsTextFragment* aFrag,
PRBool aTrimAfter); PRBool aTrimAfter);
// Similar to Reflow(), but for use from nsLineLayout
void ReflowText(nsLineLayout& aLineLayout, nscoord aAvailableWidth,
nsIRenderingContext* aRenderingContext, PRBool aShouldBlink,
nsHTMLReflowMetrics& aMetrics, nsReflowStatus& aStatus);
protected: protected:
virtual ~nsTextFrame(); virtual ~nsTextFrame();

View File

@ -6195,12 +6195,38 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
{ {
DO_GLOBAL_REFLOW_COUNT("nsTextFrame"); DO_GLOBAL_REFLOW_COUNT("nsTextFrame");
DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus); DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
// XXX If there's no line layout, we shouldn't even have created this
// frame. This may happen if, for example, this is text inside a table
// but not inside a cell. For now, just don't reflow.
if (!aReflowState.mLineLayout) {
ClearMetrics(aMetrics);
aStatus = NS_FRAME_COMPLETE;
return NS_OK;
}
ReflowText(*aReflowState.mLineLayout, aReflowState.availableWidth,
aReflowState.rendContext, aReflowState.mFlags.mBlinks,
aMetrics, aStatus);
NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aMetrics);
return NS_OK;
}
void
nsTextFrame::ReflowText(nsLineLayout& aLineLayout, nscoord aAvailableWidth,
nsIRenderingContext* aRenderingContext,
PRBool aShouldBlink,
nsHTMLReflowMetrics& aMetrics,
nsReflowStatus& aStatus)
{
#ifdef NOISY_REFLOW #ifdef NOISY_REFLOW
ListTag(stdout); ListTag(stdout);
printf(": BeginReflow: availableSize=%d,%d\n", printf(": BeginReflow: availableWidth=%d\n", aAvailableWidth);
aReflowState.availableWidth, aReflowState.availableHeight);
#endif #endif
nsPresContext* presContext = PresContext();
///////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////
// Set up flags and clear out state // Set up flags and clear out state
///////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////
@ -6216,22 +6242,17 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
// content; gfxTextRun construction logic will ensure that we take priority. // content; gfxTextRun construction logic will ensure that we take priority.
PRInt32 maxContentLength = GetInFlowContentLength(); PRInt32 maxContentLength = GetInFlowContentLength();
// XXX If there's no line layout, we shouldn't even have created this // We don't need to reflow if there is no content.
// frame. This may happen if, for example, this is text inside a table if (!maxContentLength) {
// but not inside a cell. For now, just don't reflow. We also don't need to
// reflow if there is no content.
if (!aReflowState.mLineLayout || !maxContentLength) {
ClearMetrics(aMetrics); ClearMetrics(aMetrics);
aStatus = NS_FRAME_COMPLETE; aStatus = NS_FRAME_COMPLETE;
return NS_OK; return;
} }
nsLineLayout& lineLayout = *aReflowState.mLineLayout; if (aShouldBlink) {
if (aReflowState.mFlags.mBlinks) {
if (0 == (mState & TEXT_BLINK_ON)) { if (0 == (mState & TEXT_BLINK_ON)) {
mState |= TEXT_BLINK_ON; mState |= TEXT_BLINK_ON;
nsBlinkTimer::AddBlinkFrame(aPresContext, this); nsBlinkTimer::AddBlinkFrame(presContext, this);
} }
} }
else { else {
@ -6243,14 +6264,14 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
const nsStyleText* textStyle = GetStyleText(); const nsStyleText* textStyle = GetStyleText();
PRBool atStartOfLine = lineLayout.LineAtStart(); PRBool atStartOfLine = aLineLayout.LineAtStart();
if (atStartOfLine) { if (atStartOfLine) {
AddStateBits(TEXT_START_OF_LINE); AddStateBits(TEXT_START_OF_LINE);
} }
PRUint32 flowEndInTextRun; PRUint32 flowEndInTextRun;
nsIFrame* lineContainer = lineLayout.GetLineContainerFrame(); nsIFrame* lineContainer = aLineLayout.GetLineContainerFrame();
gfxContext* ctx = aReflowState.rendContext->ThebesContext(); gfxContext* ctx = aRenderingContext->ThebesContext();
const nsTextFragment* frag = mContent->GetText(); const nsTextFragment* frag = mContent->GetText();
// DOM offsets of the text range we need to measure, after trimming // DOM offsets of the text range we need to measure, after trimming
@ -6290,21 +6311,21 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
PRBool completedFirstLetter = PR_FALSE; PRBool completedFirstLetter = PR_FALSE;
// Layout dependent styles are a problem because we need to reconstruct // Layout dependent styles are a problem because we need to reconstruct
// the gfxTextRun based on our layout. // the gfxTextRun based on our layout.
if (lineLayout.GetInFirstLetter() || lineLayout.GetInFirstLine()) { if (aLineLayout.GetInFirstLetter() || aLineLayout.GetInFirstLine()) {
SetLength(maxContentLength, &lineLayout); SetLength(maxContentLength, &aLineLayout);
if (lineLayout.GetInFirstLetter()) { if (aLineLayout.GetInFirstLetter()) {
// floating first-letter boundaries are significant in textrun // floating first-letter boundaries are significant in textrun
// construction, so clear the textrun out every time we hit a first-letter // construction, so clear the textrun out every time we hit a first-letter
// and have changed our length (which controls the first-letter boundary) // and have changed our length (which controls the first-letter boundary)
ClearTextRun(); ClearTextRun();
// Find the length of the first-letter. We need a textrun for this. // Find the length of the first-letter. We need a textrun for this.
gfxSkipCharsIterator iter = gfxSkipCharsIterator iter =
EnsureTextRun(ctx, lineContainer, lineLayout.GetLine(), &flowEndInTextRun); EnsureTextRun(ctx, lineContainer, aLineLayout.GetLine(), &flowEndInTextRun);
if (mTextRun) { if (mTextRun) {
PRInt32 firstLetterLength = length; PRInt32 firstLetterLength = length;
if (lineLayout.GetFirstLetterStyleOK()) { if (aLineLayout.GetFirstLetterStyleOK()) {
completedFirstLetter = completedFirstLetter =
FindFirstLetterRange(frag, mTextRun, offset, iter, &firstLetterLength); FindFirstLetterRange(frag, mTextRun, offset, iter, &firstLetterLength);
if (newLineOffset >= 0) { if (newLineOffset >= 0) {
@ -6333,7 +6354,7 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
// Change this frame's length to the first-letter length right now // Change this frame's length to the first-letter length right now
// so that when we rebuild the textrun it will be built with the // so that when we rebuild the textrun it will be built with the
// right first-letter boundary // right first-letter boundary
SetLength(offset + length - GetContentOffset(), &lineLayout); SetLength(offset + length - GetContentOffset(), &aLineLayout);
// Ensure that the textrun will be rebuilt // Ensure that the textrun will be rebuilt
ClearTextRun(); ClearTextRun();
} }
@ -6341,7 +6362,7 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
} }
gfxSkipCharsIterator iter = gfxSkipCharsIterator iter =
EnsureTextRun(ctx, lineContainer, lineLayout.GetLine(), &flowEndInTextRun); EnsureTextRun(ctx, lineContainer, aLineLayout.GetLine(), &flowEndInTextRun);
if (mTextRun && iter.GetOriginalEnd() < offset + length) { if (mTextRun && iter.GetOriginalEnd() < offset + length) {
// The textrun does not map enough text for this frame. This can happen // The textrun does not map enough text for this frame. This can happen
@ -6350,13 +6371,13 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
// consumed all the text of the textrun. We need a new textrun. // consumed all the text of the textrun. We need a new textrun.
ClearTextRun(); ClearTextRun();
iter = EnsureTextRun(ctx, lineContainer, iter = EnsureTextRun(ctx, lineContainer,
lineLayout.GetLine(), &flowEndInTextRun); aLineLayout.GetLine(), &flowEndInTextRun);
} }
if (!mTextRun) { if (!mTextRun) {
ClearMetrics(aMetrics); ClearMetrics(aMetrics);
aStatus = NS_FRAME_COMPLETE; aStatus = NS_FRAME_COMPLETE;
return NS_OK; return;
} }
NS_ASSERTION(gfxSkipCharsIterator(iter).ConvertOriginalToSkipped(offset + length) NS_ASSERTION(gfxSkipCharsIterator(iter).ConvertOriginalToSkipped(offset + length)
@ -6369,7 +6390,7 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
iter.SetOriginalOffset(offset); iter.SetOriginalOffset(offset);
nscoord xOffsetForTabs = (mTextRun->GetFlags() & nsTextFrameUtils::TEXT_HAS_TAB) ? nscoord xOffsetForTabs = (mTextRun->GetFlags() & nsTextFrameUtils::TEXT_HAS_TAB) ?
(lineLayout.GetCurrentFrameXDistanceFromBlock() - (aLineLayout.GetCurrentFrameXDistanceFromBlock() -
lineContainer->GetUsedBorderAndPadding().left) lineContainer->GetUsedBorderAndPadding().left)
: -1; : -1;
PropertyProvider provider(mTextRun, textStyle, frag, this, iter, length, PropertyProvider provider(mTextRun, textStyle, frag, this, iter, length,
@ -6388,7 +6409,7 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
#endif #endif
PRInt32 limitLength = length; PRInt32 limitLength = length;
PRInt32 forceBreak = lineLayout.GetForcedBreakPosition(mContent); PRInt32 forceBreak = aLineLayout.GetForcedBreakPosition(mContent);
PRBool forceBreakAfter = PR_FALSE; PRBool forceBreakAfter = PR_FALSE;
if (forceBreak >= offset + length) { if (forceBreak >= offset + length) {
forceBreakAfter = forceBreak == offset + length; forceBreakAfter = forceBreak == offset + length;
@ -6418,16 +6439,16 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
PRUint32 transformedLastBreak = 0; PRUint32 transformedLastBreak = 0;
PRBool usedHyphenation; PRBool usedHyphenation;
gfxFloat trimmedWidth = 0; gfxFloat trimmedWidth = 0;
gfxFloat availWidth = aReflowState.availableWidth; gfxFloat availWidth = aAvailableWidth;
PRBool canTrimTrailingWhitespace = !textStyle->WhiteSpaceIsSignificant(); PRBool canTrimTrailingWhitespace = !textStyle->WhiteSpaceIsSignificant();
PRInt32 unusedOffset; PRInt32 unusedOffset;
gfxBreakPriority breakPriority; gfxBreakPriority breakPriority;
lineLayout.GetLastOptionalBreakPosition(&unusedOffset, &breakPriority); aLineLayout.GetLastOptionalBreakPosition(&unusedOffset, &breakPriority);
PRUint32 transformedCharsFit = PRUint32 transformedCharsFit =
mTextRun->BreakAndMeasureText(transformedOffset, transformedLength, mTextRun->BreakAndMeasureText(transformedOffset, transformedLength,
(GetStateBits() & TEXT_START_OF_LINE) != 0, (GetStateBits() & TEXT_START_OF_LINE) != 0,
availWidth, availWidth,
&provider, !lineLayout.LineIsBreakable(), &provider, !aLineLayout.LineIsBreakable(),
canTrimTrailingWhitespace ? &trimmedWidth : nsnull, canTrimTrailingWhitespace ? &trimmedWidth : nsnull,
&textMetrics, boundingBoxType, ctx, &textMetrics, boundingBoxType, ctx,
&usedHyphenation, &transformedLastBreak, &usedHyphenation, &transformedLastBreak,
@ -6501,9 +6522,9 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
if (!brokeText && lastBreak >= 0) { if (!brokeText && lastBreak >= 0) {
// Since everything fit and no break was forced, // Since everything fit and no break was forced,
// record the last break opportunity // record the last break opportunity
NS_ASSERTION(textMetrics.mAdvanceWidth - trimmableWidth <= aReflowState.availableWidth, NS_ASSERTION(textMetrics.mAdvanceWidth - trimmableWidth <= aAvailableWidth,
"If the text doesn't fit, and we have a break opportunity, why didn't MeasureText use it?"); "If the text doesn't fit, and we have a break opportunity, why didn't MeasureText use it?");
lineLayout.NotifyOptionalBreakPosition(mContent, lastBreak, PR_TRUE, breakPriority); aLineLayout.NotifyOptionalBreakPosition(mContent, lastBreak, PR_TRUE, breakPriority);
} }
PRInt32 contentLength = offset + charsFit - GetContentOffset(); PRInt32 contentLength = offset + charsFit - GetContentOffset();
@ -6553,7 +6574,7 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
aMetrics.mOverflowArea.UnionRect(boundingBox, aMetrics.mOverflowArea.UnionRect(boundingBox,
nsRect(0, 0, aMetrics.width, aMetrics.height)); nsRect(0, 0, aMetrics.width, aMetrics.height));
UnionTextDecorationOverflow(aPresContext, provider, &aMetrics.mOverflowArea); UnionTextDecorationOverflow(presContext, provider, &aMetrics.mOverflowArea);
///////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////
// Clean up, update state // Clean up, update state
@ -6565,13 +6586,13 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
// at most one space so there's no way for trimmable width from a previous // at most one space so there's no way for trimmable width from a previous
// frame to accumulate with trimmable width from this frame.) // frame to accumulate with trimmable width from this frame.)
if (transformedCharsFit > 0) { if (transformedCharsFit > 0) {
lineLayout.SetTrimmableWidth(NSToCoordFloor(trimmableWidth)); aLineLayout.SetTrimmableWidth(NSToCoordFloor(trimmableWidth));
AddStateBits(TEXT_HAS_NONCOLLAPSED_CHARACTERS); AddStateBits(TEXT_HAS_NONCOLLAPSED_CHARACTERS);
} }
if (charsFit > 0 && charsFit == length && if (charsFit > 0 && charsFit == length &&
HasSoftHyphenBefore(frag, mTextRun, offset, end)) { HasSoftHyphenBefore(frag, mTextRun, offset, end)) {
// Record a potential break after final soft hyphen // Record a potential break after final soft hyphen
lineLayout.NotifyOptionalBreakPosition(mContent, offset + length, aLineLayout.NotifyOptionalBreakPosition(mContent, offset + length,
textMetrics.mAdvanceWidth + provider.GetHyphenWidth() <= availWidth, textMetrics.mAdvanceWidth + provider.GetHyphenWidth() <= availWidth,
eNormalBreak); eNormalBreak);
} }
@ -6592,8 +6613,8 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
if (textMetrics.mAdvanceWidth - trimmableWidth > availWidth) { if (textMetrics.mAdvanceWidth - trimmableWidth > availWidth) {
breakAfter = PR_TRUE; breakAfter = PR_TRUE;
} else { } else {
lineLayout.NotifyOptionalBreakPosition(mContent, offset + length, PR_TRUE, aLineLayout.NotifyOptionalBreakPosition(mContent, offset + length,
eNormalBreak); PR_TRUE, eNormalBreak);
} }
} }
@ -6607,12 +6628,12 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
} else if (contentLength > 0 && mContentOffset + contentLength - 1 == newLineOffset) { } else if (contentLength > 0 && mContentOffset + contentLength - 1 == newLineOffset) {
// Ends in \n // Ends in \n
aStatus = NS_INLINE_LINE_BREAK_AFTER(aStatus); aStatus = NS_INLINE_LINE_BREAK_AFTER(aStatus);
lineLayout.SetLineEndsInBR(PR_TRUE); aLineLayout.SetLineEndsInBR(PR_TRUE);
} else if (breakAfter) { } else if (breakAfter) {
aStatus = NS_INLINE_LINE_BREAK_AFTER(aStatus); aStatus = NS_INLINE_LINE_BREAK_AFTER(aStatus);
} }
if (completedFirstLetter) { if (completedFirstLetter) {
lineLayout.SetFirstLetterStyleOK(PR_FALSE); aLineLayout.SetFirstLetterStyleOK(PR_FALSE);
aStatus |= NS_INLINE_BREAK_FIRST_LETTER_COMPLETE; aStatus |= NS_INLINE_BREAK_FIRST_LETTER_COMPLETE;
} }
@ -6648,11 +6669,11 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
NS_ASSERTION(numJustifiableCharacters <= charsFit, NS_ASSERTION(numJustifiableCharacters <= charsFit,
"Bad justifiable character count"); "Bad justifiable character count");
lineLayout.SetTextJustificationWeights(numJustifiableCharacters, aLineLayout.SetTextJustificationWeights(numJustifiableCharacters,
charsFit - numJustifiableCharacters); charsFit - numJustifiableCharacters);
} }
SetLength(contentLength, &lineLayout); SetLength(contentLength, &aLineLayout);
if (mContent->HasFlag(NS_TEXT_IN_SELECTION)) { if (mContent->HasFlag(NS_TEXT_IN_SELECTION)) {
SelectionDetails* details = GetSelectionDetails(); SelectionDetails* details = GetSelectionDetails();
@ -6672,8 +6693,6 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
aMetrics.width, aMetrics.height, aMetrics.ascent, aMetrics.width, aMetrics.height, aMetrics.ascent,
aStatus); aStatus);
#endif #endif
NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aMetrics);
return NS_OK;
} }
/* virtual */ PRBool /* virtual */ PRBool