From f40b76249a34485a3870768e1a86ee6d533b458c Mon Sep 17 00:00:00 2001 From: "buster%netscape.com" Date: Mon, 17 Apr 2000 14:40:46 +0000 Subject: [PATCH] bug 588 (text justification) for Robert O'Callahan r=buster bug 18545 ([FLOAT] Problem Centering with
tag) r=troy bugs 18827, 19579, 22327 24782, 26512, 30124, 31849, 32846 (floater behavior wrong) The primary change here is to determine if a block is impacted by a floater, and if so mark the block's lines dirty when appropriate. r=troy no bug number. performance work. reduced the size of some reflow data structures by collapsing multiple fields into a single bit field. r=troy --- layout/base/nsPresShell.cpp | 12 +- layout/generic/nsBlockBandData.cpp | 23 +- layout/generic/nsBlockBandData.h | 4 + layout/generic/nsBlockFrame.cpp | 313 +++++++++++------- layout/generic/nsBlockReflowContext.cpp | 8 +- layout/generic/nsBlockReflowState.cpp | 313 +++++++++++------- layout/generic/nsBlockReflowState.h | 313 +++++++++++------- layout/generic/nsImageFrame.cpp | 7 + layout/generic/nsLineLayout.cpp | 277 +++++++++++----- layout/generic/nsLineLayout.h | 148 +++++++-- layout/generic/nsTextFrame.cpp | 237 ++++++++++--- layout/html/base/src/nsBlockBandData.cpp | 23 +- layout/html/base/src/nsBlockBandData.h | 4 + layout/html/base/src/nsBlockFrame.cpp | 313 +++++++++++------- layout/html/base/src/nsBlockReflowContext.cpp | 8 +- layout/html/base/src/nsBlockReflowState.cpp | 313 +++++++++++------- layout/html/base/src/nsBlockReflowState.h | 313 +++++++++++------- layout/html/base/src/nsImageFrame.cpp | 7 + layout/html/base/src/nsLineLayout.cpp | 277 +++++++++++----- layout/html/base/src/nsLineLayout.h | 148 +++++++-- layout/html/base/src/nsPresShell.cpp | 12 +- layout/html/base/src/nsTextFrame.cpp | 237 ++++++++++--- 22 files changed, 2222 insertions(+), 1088 deletions(-) diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index 66d5d509ad98..3fa2bd8c49c9 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -3874,6 +3874,9 @@ FindTopFrame(nsIFrame* aRoot) PRBool PresShell::VerifyIncrementalReflow() { + if (VERIFY_REFLOW_NOISY & gVerifyReflowFlags) { + printf("Building Verification Tree...\n"); + } // All the stuff we are creating that needs releasing nsIPresContext* cx; nsIViewManager* vm; @@ -3976,6 +3979,9 @@ PresShell::VerifyIncrementalReflow() vm->SetViewObserver((nsIViewObserver *)((PresShell*)sh)); sh->InitialReflow(r.width, r.height); sh->SetVerifyReflowEnable(PR_TRUE); // turn on verify reflow again now that we're done reflowing the test frame tree + if (VERIFY_REFLOW_NOISY & gVerifyReflowFlags) { + printf("Verification Tree built, comparing...\n"); + } // Now that the document has been reflowed, use its frame tree to // compare against our frame tree. @@ -3999,15 +4005,15 @@ PresShell::VerifyIncrementalReflow() } } -// printf("Incremental reflow doomed view tree:\n"); -// view->List(stdout, 1); -// view->SetVisibility(nsViewVisibility_kHide); cx->Stop(); cx->SetContainer(nsnull); NS_RELEASE(cx); sh->EndObservingDocument(); NS_RELEASE(sh); NS_RELEASE(vm); + if (VERIFY_REFLOW_NOISY & gVerifyReflowFlags) { + printf("Finished Verifying Reflow...\n"); + } return ok; } diff --git a/layout/generic/nsBlockBandData.cpp b/layout/generic/nsBlockBandData.cpp index 32b93fc1478b..ef56e9c8666c 100644 --- a/layout/generic/nsBlockBandData.cpp +++ b/layout/generic/nsBlockBandData.cpp @@ -81,6 +81,10 @@ nsBlockBandData::GetAvailableSpace(nscoord aY, nsRect& aResult) // between any left and right floaters. ComputeAvailSpaceRect(); aResult = mAvailSpace; +#ifdef REALLY_NOISY_COMPUTEAVAILSPACERECT + printf("nsBBD %p GetAvailableSpace(%d) returing (%d, %d, %d, %d)\n", + this, aY, aResult.x, aResult.y, aResult.width, aResult.height); +#endif return NS_OK; } @@ -134,7 +138,7 @@ void nsBlockBandData::ComputeAvailSpaceRect() { #ifdef REALLY_NOISY_COMPUTEAVAILSPACERECT - printf("nsBlockBandData::ComputeAvailSpaceRect %p \n", this); + printf("nsBlockBandData::ComputeAvailSpaceRect %p with count %d\n", this, mCount); #endif if (0 == mCount) { mAvailSpace.x = 0; @@ -248,6 +252,11 @@ nsBlockBandData::ComputeAvailSpaceRect() if (NS_UNCONSTRAINEDSIZE == mSpace.width) { mAvailSpace.width = NS_UNCONSTRAINEDSIZE; } +#ifdef REALLY_NOISY_COMPUTEAVAILSPACERECT + printf(" ComputeAvailSpaceRect settting state mAvailSpace (%d,%d,%d,%d)\n", + mAvailSpace.x, mAvailSpace.y, mAvailSpace.width, mAvailSpace.height); +#endif + } /** @@ -471,3 +480,15 @@ nsBlockBandData::GetMaxElementSize(nsIPresContext* aPresContext, *aWidthResult = maxWidth; *aHeightResult = maxHeight; } + +#ifdef DEBUG +void nsBlockBandData::List() +{ + printf("nsBlockBandData %p sm=%p, sm coord = (%d,%d), mSpace = (%d,%d)\n", + this, mSpaceManager, mSpaceManagerX, mSpaceManagerY, + mSpace.width, mSpace.height); + printf(" availSpace=(%d, %d, %d, %d), floaters l=%d r=%d\n", + mAvailSpace.x, mAvailSpace.y, mAvailSpace.width, mAvailSpace.height, + mLeftFloaters, mRightFloaters); +} +#endif diff --git a/layout/generic/nsBlockBandData.h b/layout/generic/nsBlockBandData.h index 29ac776dd93a..7008a61593ac 100644 --- a/layout/generic/nsBlockBandData.h +++ b/layout/generic/nsBlockBandData.h @@ -89,6 +89,10 @@ public: nsIFrame* aFrame, nsSize* aResult); +#ifdef DEBUG + void List(); +#endif + protected: /** utility method to calculate the band data at aY. diff --git a/layout/generic/nsBlockFrame.cpp b/layout/generic/nsBlockFrame.cpp index 554b851c4d28..5f974deb204e 100644 --- a/layout/generic/nsBlockFrame.cpp +++ b/layout/generic/nsBlockFrame.cpp @@ -18,6 +18,8 @@ * Rights Reserved. * * Contributor(s): + * Steve Clark + * Robert O'Callahan * Pierre Phaneuf */ #include "nsCOMPtr.h" @@ -61,6 +63,8 @@ #ifdef DEBUG +//#define NOISY_BLOCK_INVALIDATE // DO NOT CHECK THIS IN TURNED ON! + static PRBool gLamePaintMetrics; static PRBool gLameReflowMetrics; static PRBool gNoisy; @@ -451,12 +455,6 @@ public: nscoord mBottomEdge; - PRPackedBool mUnconstrainedWidth; - PRPackedBool mUnconstrainedHeight; - PRPackedBool mShrinkWrapWidth; - PRPackedBool mNeedResizeReflow; - PRPackedBool mIsInlineIncrReflow; - // The content area to reflow child frames within. The x/y // coordinates are known to be mBorderPadding.left and // mBorderPadding.top. The width/height may be NS_UNCONSTRAINEDSIZE @@ -464,15 +462,6 @@ public: // unconstrained area. nsSize mContentArea; - // Our wrapping behavior - PRPackedBool mNoWrap; - - // Is this frame a root for top/bottom margin collapsing? - PRPackedBool mIsTopMarginRoot, mIsBottomMarginRoot; - - // See ShouldApplyTopMargin - PRPackedBool mApplyTopMargin; - //---------------------------------------- // This state is "running" state updated by the reflow of each line @@ -541,15 +530,48 @@ public: // being N^2. nsFloaterCacheFreeList mBelowCurrentLineFloaters; - PRPackedBool mComputeMaxElementSize; - PRPackedBool mComputeMaximumWidth; - nsSize mMaxElementSize; nscoord mMaximumWidth; nscoord mMinLineHeight; PRInt32 mLineNumber; + + // block reflow state flags +#define BRS_UNCONSTRAINEDWIDTH 0x00000001 +#define BRS_UNCONSTRAINEDHEIGHT 0x00000002 +#define BRS_SHRINKWRAPWIDTH 0x00000004 +#define BRS_NEEDRESIZEREFLOW 0x00000008 +#define BRS_ISINLINEINCRREFLOW 0x00000010 +#define BRS_NOWRAP 0x00000020 +#define BRS_ISTOPMARGINROOT 0x00000040 // Is this frame a root for top/bottom margin collapsing? +#define BRS_ISBOTTOMMARGINROOT 0x00000080 +#define BRS_APPLYTOPMARGIN 0x00000100 // See ShouldApplyTopMargin +#define BRS_COMPUTEMAXELEMENTSIZE 0x00000200 +#define BRS_COMPUTEMAXWIDTH 0x00000400 +#define BRS_LASTFLAG BRS_COMPUTEMAXWIDTH + + PRInt16 mFlags; + + void SetFlag(PRUint32 aFlag, PRBool aValue) + { + NS_ASSERTION(aFlag<=BRS_LASTFLAG, "bad flag"); + NS_ASSERTION(aValue==PR_FALSE || aValue==PR_TRUE, "bad value"); + if (aValue) { // set flag + mFlags |= aFlag; + } + else { // unset flag + mFlags &= ~aFlag; + } + } + + PRBool GetFlag(PRUint32 aFlag) const + { + NS_ASSERTION(aFlag<=BRS_LASTFLAG, "bad flag"); + PRBool result = (mFlags & aFlag); + if (result) return PR_TRUE; + return PR_FALSE; + } }; // XXX This is vile. Make it go away @@ -574,29 +596,25 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, : mBlock(aFrame), mPresContext(aPresContext), mReflowState(aReflowState), - mNeedResizeReflow(PR_FALSE), - mIsInlineIncrReflow(PR_FALSE), - mIsTopMarginRoot(PR_FALSE), - mIsBottomMarginRoot(PR_FALSE), - mApplyTopMargin(PR_FALSE), mNextRCFrame(nsnull), mPrevBottomMargin(0), - mLineNumber(0) + mLineNumber(0), + mFlags(0) { const nsMargin& borderPadding = BorderPadding(); if (aBlockMarginRoot) { - mIsTopMarginRoot = PR_TRUE; - mIsBottomMarginRoot = PR_TRUE; + SetFlag(BRS_ISTOPMARGINROOT, PR_TRUE); + SetFlag(BRS_ISBOTTOMMARGINROOT, PR_TRUE); } if (0 != aReflowState.mComputedBorderPadding.top) { - mIsTopMarginRoot = PR_TRUE; + SetFlag(BRS_ISTOPMARGINROOT, PR_TRUE); } if (0 != aReflowState.mComputedBorderPadding.bottom) { - mIsBottomMarginRoot = PR_TRUE; + SetFlag(BRS_ISBOTTOMMARGINROOT, PR_TRUE); } - if (mIsTopMarginRoot) { - mApplyTopMargin = PR_TRUE; + if (GetFlag(BRS_ISTOPMARGINROOT)) { + SetFlag(BRS_APPLYTOPMARGIN, PR_TRUE); } mSpaceManager = aReflowState.mSpaceManager; @@ -617,21 +635,19 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, // Compute content area width (the content area is inside the border // and padding) - mUnconstrainedWidth = PR_FALSE; - mShrinkWrapWidth = PR_FALSE; if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedWidth) { mContentArea.width = aReflowState.mComputedWidth; } else { if (NS_UNCONSTRAINEDSIZE == aReflowState.availableWidth) { mContentArea.width = NS_UNCONSTRAINEDSIZE; - mUnconstrainedWidth = PR_TRUE; + SetFlag(BRS_UNCONSTRAINEDWIDTH, PR_TRUE); } else if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMaxWidth) { // Choose a width based on the content (shrink wrap width) up // to the maximum width mContentArea.width = aReflowState.mComputedMaxWidth; - mShrinkWrapWidth = PR_TRUE; + SetFlag(BRS_SHRINKWRAPWIDTH, PR_TRUE); } else { nscoord lr = borderPadding.left + borderPadding.right; @@ -646,7 +662,6 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, // specified style height then we may end up limiting our height if // the availableHeight is constrained (this situation occurs when we // are paginated). - mUnconstrainedHeight = PR_FALSE; if (NS_UNCONSTRAINEDSIZE != aReflowState.availableHeight) { // We are in a paginated situation. The bottom edge is just inside // the bottom border and padding. The content area height doesn't @@ -657,7 +672,7 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, else { // When we are not in a paginated situation then we always use // an constrained height. - mUnconstrainedHeight = PR_TRUE; + SetFlag(BRS_UNCONSTRAINEDHEIGHT, PR_TRUE); mContentArea.height = mBottomEdge = NS_UNCONSTRAINEDSIZE; } @@ -674,16 +689,17 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, switch (styleText->mWhiteSpace) { case NS_STYLE_WHITESPACE_PRE: case NS_STYLE_WHITESPACE_NOWRAP: - mNoWrap = PR_TRUE; + SetFlag(BRS_NOWRAP, PR_TRUE); break; default: - mNoWrap = PR_FALSE; + SetFlag(BRS_NOWRAP, PR_FALSE); break; } - mComputeMaxElementSize = nsnull != aMetrics.maxElementSize; + SetFlag(BRS_COMPUTEMAXELEMENTSIZE, (nsnull != aMetrics.maxElementSize)); mMaxElementSize.SizeTo(0, 0); - mComputeMaximumWidth = NS_REFLOW_CALC_MAX_WIDTH == (aMetrics.mFlags & NS_REFLOW_CALC_MAX_WIDTH); + SetFlag(BRS_COMPUTEMAXWIDTH, + (NS_REFLOW_CALC_MAX_WIDTH == (aMetrics.mFlags & NS_REFLOW_CALC_MAX_WIDTH))); mMaximumWidth = 0; mMinLineHeight = nsHTMLReflowState::CalcLineHeight(mPresContext, @@ -729,8 +745,12 @@ nsBlockReflowState::ComputeBlockAvailSpace(nsIFrame* aFrame, const nsStyleDisplay* aDisplay, nsRect& aResult) { +#ifdef REALLY_NOISY_REFLOW + printf("CBAS frame=%p has floater count %d\n", aFrame, mBand.GetFloaterCount()); + mBand.List(); +#endif aResult.y = mY; - aResult.height = mUnconstrainedHeight + aResult.height = GetFlag(BRS_UNCONSTRAINEDHEIGHT) ? NS_UNCONSTRAINEDSIZE : mBottomEdge - mY; @@ -749,7 +769,7 @@ nsBlockReflowState::ComputeBlockAvailSpace(nsIFrame* aFrame, // The child block will flow around the floater. Therefore // give it all of the available space. aResult.x = borderPadding.left; - aResult.width = mUnconstrainedWidth + aResult.width = GetFlag(BRS_UNCONSTRAINEDWIDTH) ? NS_UNCONSTRAINEDSIZE : mContentArea.width; break; @@ -776,7 +796,7 @@ nsBlockReflowState::ComputeBlockAvailSpace(nsIFrame* aFrame, } // determine width - if (mUnconstrainedWidth) { + if (GetFlag(BRS_UNCONSTRAINEDWIDTH)) { aResult.width = NS_UNCONSTRAINEDSIZE; } else { @@ -810,7 +830,7 @@ nsBlockReflowState::ComputeBlockAvailSpace(nsIFrame* aFrame, // doesn't matter therefore give the block element all of the // available space since it will flow around the floater itself. aResult.x = borderPadding.left; - aResult.width = mUnconstrainedWidth + aResult.width = GetFlag(BRS_UNCONSTRAINEDWIDTH) ? NS_UNCONSTRAINEDSIZE : mContentArea.width; } @@ -957,12 +977,12 @@ nsBlockReflowState::RecoverStateFrom(nsLineBox* aLine, #endif mKidXMost = xmost; } - if (mComputeMaxElementSize) { + if (GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { UpdateMaxElementSize(nsSize(aLine->mMaxElementWidth, aLine->mBounds.height)); } // If computing the maximum width, then update mMaximumWidth - if (mComputeMaximumWidth) { + if (GetFlag(BRS_COMPUTEMAXWIDTH)) { UpdateMaximumWidth(aLine->mMaximumWidth); } @@ -1454,6 +1474,7 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, nsBlockReflowState state(aReflowState, aPresContext, this, aMetrics, NS_BLOCK_MARGIN_ROOT & mState); + PRInt32 sizeofBRS = sizeof nsBlockReflowState; if (eReflowReason_Resize != aReflowState.reason) { RenumberLists(aPresContext); @@ -1462,7 +1483,7 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, nsresult rv = NS_OK; PRBool isStyleChange = PR_FALSE; - state.mIsInlineIncrReflow = PR_FALSE; + state.SetFlag(BRS_ISINLINEINCRREFLOW, PR_FALSE); nsIFrame* target; switch (aReflowState.reason) { case eReflowReason_Initial: @@ -1535,7 +1556,7 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, // reflow the line containing the target of the incr. reflow // first mark the line dirty and set up the state object rv = PrepareChildIncrementalReflow(state); - state.mIsInlineIncrReflow = PR_TRUE; + state.SetFlag(BRS_ISINLINEINCRREFLOW, PR_TRUE); state.mPrevLine = prevLine; state.mCurrentLine = line; state.mNextRCFrame = state.mNextRCFrame; @@ -1681,6 +1702,10 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, if (isStyleChange) { // Lots of things could have changed so damage our entire // bounds +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 1 (%d, %d, %d, %d)\n", + this, 0, 0, mRect.width, mRect.height); +#endif Invalidate(aPresContext, nsRect(0, 0, mRect.width, mRect.height)); } else { @@ -1707,6 +1732,10 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, damageRect.y = 0; damageRect.height = mRect.height; } +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 2 (%d, %d, %d, %d)\n", + this, damageRect.x, damageRect.y, damageRect.width, damageRect.height); +#endif Invalidate(aPresContext, damageRect); } @@ -1730,6 +1759,10 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, damageRect.y = mRect.height - border.bottom; damageRect.height = border.bottom; } +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 3 (%d, %d, %d, %d)\n", + this, damageRect.x, damageRect.y, damageRect.width, damageRect.height); +#endif Invalidate(aPresContext, damageRect); } } @@ -1868,7 +1901,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, #ifdef NOISY_FINAL_SIZE ListTag(stdout); printf(": mY=%d mIsBottomMarginRoot=%s mPrevBottomMargin=%d bp=%d,%d\n", - aState.mY, aState.mIsBottomMarginRoot ? "yes" : "no", + aState.mY, aState.GetFlag(BRS_ISBOTTOMMARGINROOT) ? "yes" : "no", aState.mPrevBottomMargin, borderPadding.top, borderPadding.bottom); #endif @@ -1946,7 +1979,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, // contents or we fluff out to the maximum block width. Note: // We always shrink wrap when given an unconstrained width. if ((0 == (NS_BLOCK_SHRINK_WRAP & mState)) && - !aState.mUnconstrainedWidth && !aState.mShrinkWrapWidth && + !aState.GetFlag(BRS_UNCONSTRAINEDWIDTH) && !aState.GetFlag(BRS_SHRINKWRAPWIDTH) && !compact) { // Set our width to the max width if we aren't already that // wide. Note that the max-width has nothing to do with our @@ -1956,9 +1989,9 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, } // See if we should compute our max element size - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { // Adjust the computedWidth - if (aState.mNoWrap) { + if (aState.GetFlag(BRS_NOWRAP)) { // When no-wrap is true the max-element-size.width is the // width of the widest line plus the right border. Note that // aState.mKidXMost already has the left border factored in @@ -1996,7 +2029,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, // need to do horizontal alignment of the inline lines and make sure // blocks are correctly sized and positioned. Any lines that need // final adjustment will have been marked as dirty - if (aState.mShrinkWrapWidth && aState.mNeedResizeReflow) { + if (aState.GetFlag(BRS_SHRINKWRAPWIDTH) && aState.GetFlag(BRS_NEEDRESIZEREFLOW)) { // If the parent reflow state is also shrink wrap width, then // we don't need to do this, because it will reflow us after it // calculates the final width @@ -2025,7 +2058,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, } } - if (aState.mShrinkWrapWidth) { + if (aState.GetFlag(BRS_SHRINKWRAPWIDTH)) { PRBool parentIsShrinkWrapWidth = PR_FALSE; if (aReflowState.parentReflowState) { if (NS_SHRINKWRAPWIDTH == aReflowState.parentReflowState->mComputedWidth) { @@ -2047,7 +2080,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, // Don't carry out a bottom margin when our height is fixed // unless the bottom of the last line adjoins the bottom of our // content area. - if (!aState.mIsBottomMarginRoot) { + if (!aState.GetFlag(BRS_ISBOTTOMMARGINROOT)) { if (aState.mY + aState.mPrevBottomMargin != aMetrics.height) { aState.mPrevBottomMargin = 0; } @@ -2057,7 +2090,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, nscoord autoHeight = aState.mY; // Shrink wrap our height around our contents. - if (aState.mIsBottomMarginRoot) { + if (aState.GetFlag(BRS_ISBOTTOMMARGINROOT)) { // When we are a bottom-margin root make sure that our last // childs bottom margin is fully applied. // XXX check for a fit @@ -2082,7 +2115,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, } aMetrics.height = autoHeight; - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { maxHeight = aState.mMaxElementSize.height + borderPadding.top + borderPadding.bottom; } @@ -2090,7 +2123,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, aMetrics.ascent = aMetrics.height; aMetrics.descent = 0; - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { // Store away the final value aMetrics.maxElementSize->width = maxWidth; aMetrics.maxElementSize->height = maxHeight; @@ -2098,14 +2131,14 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, // Return bottom margin information aMetrics.mCarriedOutBottomMargin = - aState.mIsBottomMarginRoot ? 0 : aState.mPrevBottomMargin; + aState.GetFlag(BRS_ISBOTTOMMARGINROOT) ? 0 : aState.mPrevBottomMargin; #ifdef DEBUG if (CRAZY_WIDTH(aMetrics.width) || CRAZY_HEIGHT(aMetrics.height)) { ListTag(stdout); printf(": WARNING: desired:%d,%d\n", aMetrics.width, aMetrics.height); } - if (aState.mComputeMaxElementSize && + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE) && ((maxWidth > aMetrics.width) || (maxHeight > aMetrics.height))) { ListTag(stdout); printf(": WARNING: max-element-size:%d,%d desired:%d,%d maxSize:%d,%d\n", @@ -2115,7 +2148,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, } #endif #ifdef NOISY_MAX_ELEMENT_SIZE - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { IndentBy(stdout, GetDepth()); if (NS_UNCONSTRAINEDSIZE == aState.mReflowState.availableWidth) { printf("PASS1 "); @@ -2130,7 +2163,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, } // If we're requested to update our maximum width, then compute it - if (aState.mComputeMaximumWidth) { + if (aState.GetFlag(BRS_COMPUTEMAXWIDTH)) { // We need to add in for the right border/padding aMetrics.mMaximumWidth = aState.mMaximumWidth + borderPadding.right; } @@ -2337,8 +2370,13 @@ nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState) // See if we can try and avoid marking all the lines as dirty PRBool tryAndSkipLines = PR_FALSE; - // See if this is this a constrained resize reflow - if ((aState.mReflowState.reason == eReflowReason_Resize) && + // we need to calculate if any part of then block itself + // is impacted by a floater (bug 19579) + aState.GetAvailableSpace(); + + // See if this is this a constrained resize reflow that is not impacted by floaters + if ((PR_FALSE==aState.IsImpactedByFloater()) && + (aState.mReflowState.reason == eReflowReason_Resize) && (NS_UNCONSTRAINEDSIZE != aState.mReflowState.availableWidth)) { // If the text is left-aligned, then we try and avoid reflowing the lines @@ -2389,7 +2427,7 @@ nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState) } #endif - PRBool notWrapping = aState.mNoWrap; + PRBool notWrapping = aState.GetFlag(BRS_NOWRAP); while (nsnull != line) { if (line->IsBlock()) { @@ -2402,8 +2440,6 @@ nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState) printf("PrepareResizeReflow thinks line %p is %simpacted by floaters\n", line, line->IsImpactedByFloater() ? "" : "not "); #endif - - if (notWrapping) { // When no-wrap is set then the only line-breaking that // occurs for inline lines is triggered by BR elements or by @@ -2430,7 +2466,7 @@ nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState) printf("skipped: line=%p next=%p %s %s %s%s%s breakType=%d xmost=%d\n", line, line->mNext, line->IsBlock() ? "block" : "inline", - aState.mNoWrap ? "no-wrap" : "wrapping", + aState.GetFlag(BRS_NOWRAP) ? "no-wrap" : "wrapping", line->HasBreak() ? "has-break " : "", line->HasFloaters() ? "has-floaters " : "", line->IsImpactedByFloater() ? "impacted " : "", @@ -2628,7 +2664,7 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) ListTag(stdout); printf(": incrementally reflowing dirty lines: type=%s(%d) isInline=%s", kReflowCommandType[type], type, - aState.mIsInlineIncrReflow ? "true" : "false"); + aState.GetFlag(BRS_ISINLINEINCRREFLOW) ? "true" : "false"); } else { IndentBy(stdout, gNoiseIndent); @@ -2651,7 +2687,7 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) // Reflow the lines that are already ours aState.mPrevLine = nsnull; nsLineBox* line = mLines; - if (aState.mIsInlineIncrReflow && aState.mNextRCFrame) + if (aState.GetFlag(BRS_ISINLINEINCRREFLOW) && aState.mNextRCFrame) { const nsLineBox* incrTargetLine = aState.mCurrentLine; aState.mCurrentLine = line; @@ -2662,6 +2698,10 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) RecoverStateFrom(aState, line, deltaY, incrementalReflow ? &damageRect : 0); if (incrementalReflow && !damageRect.IsEmpty()) { +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 4 (%d, %d, %d, %d)\n", + this, damageRect.x, damageRect.y, damageRect.width, damageRect.height); +#endif Invalidate(aState.mPresContext, damageRect); } aState.mPrevLine = line; @@ -2689,7 +2729,7 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) // If we're supposed to update our maximum width, then we'll also need to // reflow this line if it's line wrapped and any of the continuing lines // are dirty - if (line->IsDirty() || (aState.mComputeMaximumWidth && ::WrappedLinesAreDirty(line))) { + if (line->IsDirty() || (aState.GetFlag(BRS_COMPUTEMAXWIDTH) && ::WrappedLinesAreDirty(line))) { // Compute the dirty lines "before" YMost, after factoring in // the running deltaY value - the running value is implicit in // aState.mY. @@ -2729,6 +2769,10 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) RecoverStateFrom(aState, line, deltaY, incrementalReflow ? &damageRect : 0); if (incrementalReflow && !damageRect.IsEmpty()) { +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 5 (%d, %d, %d, %d)\n", + this, damageRect.x, damageRect.y, damageRect.width, damageRect.height); +#endif Invalidate(aState.mPresContext, damageRect); } } @@ -2902,6 +2946,10 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, // XXX We need to improve on this... nsRect dirtyRect; dirtyRect.UnionRect(oldCombinedArea, lineCombinedArea); +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 6 (%d, %d, %d, %d)\n", + this, dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height); +#endif Invalidate(aState.mPresContext, dirtyRect); } else { @@ -2918,6 +2966,10 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, dirtyRect.x; dirtyRect.height = PR_MAX(oldCombinedArea.height, lineCombinedArea.height); +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 7 (%d, %d, %d, %d)\n", + this, dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height); +#endif Invalidate(aState.mPresContext, dirtyRect); } if (oldCombinedArea.height != lineCombinedArea.height) { @@ -2933,6 +2985,10 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, dirtyRect.height = PR_MAX(oldCombinedArea.YMost(), lineCombinedArea.YMost()) - dirtyRect.y; +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 8 (%d, %d, %d, %d)\n", + this, dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height); +#endif Invalidate(aState.mPresContext, dirtyRect); } } @@ -2951,21 +3007,21 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, // we'll either need to recover the floater state that applies to the // unconstrained reflow or keep it around in a separate space manager... PRBool isBeginningLine = !aState.mPrevLine || !aState.mPrevLine->IsLineWrapped(); - if (aState.mComputeMaximumWidth && isBeginningLine) { + if (aState.GetFlag(BRS_COMPUTEMAXWIDTH) && isBeginningLine) { nscoord oldY = aState.mY; nscoord oldPrevBottomMargin = aState.mPrevBottomMargin; - PRBool oldUnconstrainedWidth = aState.mUnconstrainedWidth; + PRBool oldUnconstrainedWidth = aState.GetFlag(BRS_UNCONSTRAINEDWIDTH); // First reflow the line with an unconstrained width. When doing this // we need to set the block reflow state's "mUnconstrainedWidth" variable // to PR_TRUE so if we encounter a placeholder and then reflow its // associated floater we don't end up resetting the line's right edge and // have it think the width is unconstrained... - aState.mUnconstrainedWidth = PR_TRUE; + aState.SetFlag(BRS_UNCONSTRAINEDWIDTH, PR_TRUE); ReflowInlineFrames(aState, aLine, aKeepReflowGoing, PR_TRUE); aState.mY = oldY; aState.mPrevBottomMargin = oldPrevBottomMargin; - aState.mUnconstrainedWidth = oldUnconstrainedWidth; + aState.SetFlag(BRS_UNCONSTRAINEDWIDTH, oldUnconstrainedWidth); // Update the line's maximum width aLine->mMaximumWidth = aLine->mBounds.XMost(); @@ -2980,14 +3036,14 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, // Note: we need to reset both member variables, because the inline // code examines mComputeMaxElementSize and if there is a placeholder // on this line the code to reflow the floater looks at both... - nscoord oldComputeMaxElementSize = aState.mComputeMaxElementSize; - nscoord oldComputeMaximumWidth = aState.mComputeMaximumWidth; + nscoord oldComputeMaxElementSize = aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE); + nscoord oldComputeMaximumWidth = aState.GetFlag(BRS_COMPUTEMAXWIDTH); - aState.mComputeMaxElementSize = PR_FALSE; - aState.mComputeMaximumWidth = PR_FALSE; + aState.SetFlag(BRS_COMPUTEMAXELEMENTSIZE, PR_FALSE); + aState.SetFlag(BRS_COMPUTEMAXWIDTH, PR_FALSE); rv = ReflowInlineFrames(aState, aLine, aKeepReflowGoing); - aState.mComputeMaxElementSize = oldComputeMaxElementSize; - aState.mComputeMaximumWidth = oldComputeMaximumWidth; + aState.SetFlag(BRS_COMPUTEMAXELEMENTSIZE, oldComputeMaxElementSize); + aState.SetFlag(BRS_COMPUTEMAXWIDTH, oldComputeMaximumWidth); } else { rv = ReflowInlineFrames(aState, aLine, aKeepReflowGoing); @@ -3001,6 +3057,10 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, nsRect dirtyRect; dirtyRect.UnionRect(oldCombinedArea, combinedArea); +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 9 (%d, %d, %d, %d)\n", + this, dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height); +#endif Invalidate(aState.mPresContext, dirtyRect); } } @@ -3368,7 +3428,7 @@ PRBool nsBlockFrame::ShouldApplyTopMargin(nsBlockReflowState& aState, nsLineBox* aLine) { - if (aState.mApplyTopMargin) { + if (aState.GetFlag(BRS_APPLYTOPMARGIN)) { // Apply short-circuit check to avoid searching the line list return PR_TRUE; } @@ -3377,7 +3437,7 @@ nsBlockFrame::ShouldApplyTopMargin(nsBlockReflowState& aState, // If we aren't at the top Y coordinate then something of non-zero // height must have been placed. Therefore the childs top-margin // applies. - aState.mApplyTopMargin = PR_TRUE; + aState.SetFlag(BRS_APPLYTOPMARGIN, PR_TRUE); return PR_TRUE; } @@ -3387,13 +3447,13 @@ nsBlockFrame::ShouldApplyTopMargin(nsBlockReflowState& aState, if (line->IsBlock()) { // A line which preceeds aLine contains a block; therefore the // top margin applies. - aState.mApplyTopMargin = PR_TRUE; + aState.SetFlag(BRS_APPLYTOPMARGIN, PR_TRUE); return PR_TRUE; } else if (line->HasFloaters()) { // A line which preceeds aLine is not empty therefore the top // margin applies. - aState.mApplyTopMargin = PR_TRUE; + aState.SetFlag(BRS_APPLYTOPMARGIN, PR_TRUE); return PR_TRUE; } line = line->mNext; @@ -3486,8 +3546,8 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, frame->GetStyleData(eStyleStruct_Display, (const nsStyleStruct*&) display); nsBlockReflowContext brc(aState.mPresContext, aState.mReflowState, - aState.mComputeMaxElementSize, - aState.mComputeMaximumWidth); + aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE), + aState.GetFlag(BRS_COMPUTEMAXWIDTH)); brc.SetNextRCFrame(aState.mNextRCFrame); // See if we should apply the top margin. If the block frame being @@ -3611,14 +3671,14 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, &collapsedBottomMargin, aLine->mBounds, combinedArea); - if (aState.mShrinkWrapWidth) { + if (aState.GetFlag(BRS_SHRINKWRAPWIDTH)) { // Mark the line as block so once we known the final shrink wrap width // we can reflow the block to the correct size // XXX We don't always need to do this... aLine->MarkDirty(); - aState.mNeedResizeReflow = PR_TRUE; + aState.SetFlag(BRS_NEEDRESIZEREFLOW, PR_TRUE); } - if (aState.mUnconstrainedWidth || aState.mShrinkWrapWidth) { + if (aState.GetFlag(BRS_UNCONSTRAINEDWIDTH) || aState.GetFlag(BRS_SHRINKWRAPWIDTH)) { // Add the right margin to the line's bounnds. That way it will be taken into // account when we compute our shrink wrap size nscoord marginRight = brc.GetMargin().right; @@ -3709,7 +3769,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, // Post-process the "line" nsSize maxElementSize(0, 0); - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { maxElementSize = brc.GetMaxElementSize(); if (aState.IsImpactedByFloater() && (NS_FRAME_SPLITTABLE_NON_RECTANGULAR != splitType)) { @@ -3721,7 +3781,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, } // If we asked the block to update its maximum width, then record the // updated value in the line, and update the current maximum width - if (aState.mComputeMaximumWidth) { + if (aState.GetFlag(BRS_COMPUTEMAXWIDTH)) { aLine->mMaximumWidth = brc.GetMaximumWidth(); aState.UpdateMaximumWidth(aLine->mMaximumWidth); @@ -3849,7 +3909,7 @@ nsBlockFrame::DoReflowInlineFramesMalloc(nsBlockReflowState& aState, nsLineLayout* ll = new nsLineLayout(aState.mPresContext, aState.mReflowState.mSpaceManager, &aState.mReflowState, - aState.mComputeMaxElementSize); + aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)); if (!ll) { return NS_ERROR_OUT_OF_MEMORY; } @@ -3872,7 +3932,7 @@ nsBlockFrame::DoReflowInlineFramesAuto(nsBlockReflowState& aState, nsLineLayout lineLayout(aState.mPresContext, aState.mReflowState.mSpaceManager, &aState.mReflowState, - aState.mComputeMaxElementSize); + aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)); lineLayout.Init(&aState, aState.mMinLineHeight, aState.mLineNumber); lineLayout.SetReflowTextRuns(mTextRuns); nsresult rv = DoReflowInlineFrames(aState, lineLayout, aLine, @@ -3910,7 +3970,7 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState, nscoord x = aState.mAvailSpaceRect.x + borderPadding.left; nscoord availWidth = aState.mAvailSpaceRect.width; nscoord availHeight; - if (aState.mUnconstrainedHeight) { + if (aState.GetFlag(BRS_UNCONSTRAINEDHEIGHT)) { availHeight = NS_UNCONSTRAINEDSIZE; } else { @@ -4359,7 +4419,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, nsSize maxElementSize; aLineLayout.VerticalAlignFrames(aLine, maxElementSize); // See if we're shrink wrapping the width - if (aState.mShrinkWrapWidth) { + if (aState.GetFlag(BRS_SHRINKWRAPWIDTH)) { // When determining the line's width we also need to include any // right floaters that impact us. This represents the shrink wrap // width of the line @@ -4388,25 +4448,20 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, // Only block frames horizontally align their children because // inline frames "shrink-wrap" around their children (therefore // there is no extra horizontal space). -#if XXX_fix_me - PRBool allowJustify = PR_TRUE; - if (NS_STYLE_TEXT_ALIGN_JUSTIFY == aState.mStyleText->mTextAlign) { - allowJustify = ShouldJustifyLine(aState, aLine); - } -#else - PRBool allowJustify = PR_FALSE; -#endif - - PRBool successful; - nsRect combinedArea; - successful = aLineLayout.HorizontalAlignFrames(aLine->mBounds, allowJustify, - aState.mShrinkWrapWidth); + const nsStyleText* styleText = (const nsStyleText*) + mStyleContext->GetStyleData(eStyleStruct_Text); + PRBool allowJustify = NS_STYLE_TEXT_ALIGN_JUSTIFY == styleText->mTextAlign + && !aLineLayout.GetLineEndsInBR() && ShouldJustifyLine(aState, aLine); + PRBool successful = aLineLayout.HorizontalAlignFrames(aLine->mBounds, allowJustify, + aState.GetFlag(BRS_SHRINKWRAPWIDTH)); if (!successful) { // Mark the line dirty and then later once we've determined the width // we can do the horizontal alignment aLine->MarkDirty(); - aState.mNeedResizeReflow = PR_TRUE; + aState.SetFlag(BRS_NEEDRESIZEREFLOW, PR_TRUE); } + + nsRect combinedArea; aLineLayout.RelativePositionFrames(combinedArea); aLine->SetCombinedArea(combinedArea); if (addedBullet) { @@ -4454,7 +4509,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, } aState.mY = newY; - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { #ifdef NOISY_MAX_ELEMENT_SIZE IndentBy(stdout, GetDepth()); if (NS_UNCONSTRAINEDSIZE == aState.mReflowState.availableWidth) { @@ -4478,7 +4533,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, // we don't want updated... if (aUpdateMaximumWidth) { // However, we do need to update the max-element-size if requested - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { aState.UpdateMaxElementSize(maxElementSize); // We also cache the max element width in the line. This is needed for // incremental reflow @@ -4519,7 +4574,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, CombineRects(aState.mFloaterCombinedArea, lineCombinedArea); if (aState.mHaveRightFloaters && - (aState.mUnconstrainedWidth || aState.mShrinkWrapWidth)) { + (aState.GetFlag(BRS_UNCONSTRAINEDWIDTH) || aState.GetFlag(BRS_SHRINKWRAPWIDTH))) { // We are reflowing in an unconstrained situation or shrink wrapping and // have some right floaters. They were placed at the infinite right edge // which will cause the combined area to be unusable. @@ -4540,11 +4595,11 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, aState.mRightFloaterCombinedArea.x = aLine->mBounds.XMost(); CombineRects(aState.mRightFloaterCombinedArea, lineCombinedArea); - if (aState.mShrinkWrapWidth) { + if (aState.GetFlag(BRS_SHRINKWRAPWIDTH)) { // Mark the line dirty so we come back and re-place the floater once // the shrink wrap width is determined aLine->MarkDirty(); - aState.mNeedResizeReflow = PR_TRUE; + aState.SetFlag(BRS_NEEDRESIZEREFLOW, PR_TRUE); } } aLine->SetCombinedArea(lineCombinedArea); @@ -4629,7 +4684,7 @@ nsBlockFrame::PostPlaceLine(nsBlockReflowState& aState, } // Update max-element-size - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { aState.UpdateMaxElementSize(aMaxElementSize); // We also cache the max element width in the line. This is needed for // incremental reflow @@ -4639,7 +4694,7 @@ nsBlockFrame::PostPlaceLine(nsBlockReflowState& aState, // If this is an unconstrained reflow, then cache the line width in the // line. We'll need this during incremental reflow if we're asked to // calculate the maximum width - if (aState.mUnconstrainedWidth) { + if (aState.GetFlag(BRS_UNCONSTRAINEDWIDTH)) { aLine->mMaximumWidth = aLine->mBounds.XMost(); } @@ -4653,7 +4708,7 @@ nsBlockFrame::PostPlaceLine(nsBlockReflowState& aState, #endif // If we're shrink wrapping our width and the line was wrapped, // then make sure we take up all of the available width - if (aState.mShrinkWrapWidth && aLine->IsLineWrapped()) { + if (aState.GetFlag(BRS_SHRINKWRAPWIDTH) && aLine->IsLineWrapped()) { aState.mKidXMost = aState.BorderPadding().left + aState.mContentArea.width; } @@ -5187,6 +5242,10 @@ nsBlockFrame::DoRemoveFrame(nsIPresContext* aPresContext, // cases... nsRect lineCombinedArea; line->GetCombinedArea(&lineCombinedArea); +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 10 (%d, %d, %d, %d)\n", + this, lineCombinedArea.x, lineCombinedArea.y, lineCombinedArea.width, lineCombinedArea.height); +#endif Invalidate(aPresContext, lineCombinedArea); line->Destroy(presShell); line = next; @@ -5282,8 +5341,8 @@ nsBlockFrame::ReflowFloater(nsBlockReflowState& aState, // Setup block reflow state to reflow the floater nsBlockReflowContext brc(aState.mPresContext, aState.mReflowState, - aState.mComputeMaxElementSize, - aState.mComputeMaximumWidth); + aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE), + aState.GetFlag(BRS_COMPUTEMAXWIDTH)); brc.SetNextRCFrame(aState.mNextRCFrame); // Reflow the floater @@ -5324,7 +5383,7 @@ nsBlockFrame::ReflowFloater(nsBlockReflowState& aState, floater->DidReflow(aState.mPresContext, NS_FRAME_REFLOW_FINISHED); // If we computed it, then stash away the max-element-size for later - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { aState.StoreMaxElementSize(floater, brc.GetMaxElementSize()); } @@ -5389,7 +5448,7 @@ nsBlockReflowState::AddFloater(nsLineLayout& aLineLayout, // Pass on updated available space to the current inline reflow engine GetAvailableSpace(); aLineLayout.UpdateBand(mAvailSpaceRect.x + BorderPadding().left, mY, - mUnconstrainedWidth ? NS_UNCONSTRAINEDSIZE : mAvailSpaceRect.width, + GetFlag(BRS_UNCONSTRAINEDWIDTH) ? NS_UNCONSTRAINEDSIZE : mAvailSpaceRect.width, mAvailSpaceRect.height, isLeftFloater, aPlaceholder->GetOutOfFlowFrame()); @@ -5615,7 +5674,12 @@ nsBlockReflowState::PlaceFloater(nsFloaterCache* aFloaterCache, } else { isLeftFloater = PR_FALSE; - region.x = mAvailSpaceRect.XMost() - region.width; + if (NS_UNCONSTRAINEDSIZE != mAvailSpaceRect.XMost()) + region.x = mAvailSpaceRect.XMost() - region.width; + else { + okToAddRectRegion = PR_FALSE; + region.x = mAvailSpaceRect.x; + } } *aIsLeftFloater = isLeftFloater; const nsMargin& borderPadding = BorderPadding(); @@ -5682,7 +5746,8 @@ nsBlockReflowState::PlaceFloater(nsFloaterCache* aFloaterCache, nsRect combinedArea = aFloaterCache->mCombinedArea; combinedArea.x += x; combinedArea.y += y; - if (!isLeftFloater && (mUnconstrainedWidth || mShrinkWrapWidth)) { + if (!isLeftFloater && + (GetFlag(BRS_UNCONSTRAINEDWIDTH) || GetFlag(BRS_SHRINKWRAPWIDTH))) { // When we are placing a right floater in an unconstrained situation or // when shrink wrapping, we don't apply it to the floater combined area // immediately. Otherwise we end up with an infinitely wide combined diff --git a/layout/generic/nsBlockReflowContext.cpp b/layout/generic/nsBlockReflowContext.cpp index 7ef2cdee0d8d..8b2d17b0da81 100644 --- a/layout/generic/nsBlockReflowContext.cpp +++ b/layout/generic/nsBlockReflowContext.cpp @@ -197,10 +197,12 @@ nsBlockReflowContext::AlignBlockHorizontally(nscoord aWidth, // compatability cases. switch (styleText->mTextAlign) { case NS_STYLE_TEXT_ALIGN_MOZ_RIGHT: + case NS_STYLE_TEXT_ALIGN_RIGHT: aAlign.mXOffset += remainingSpace; doCSS = PR_FALSE; break; case NS_STYLE_TEXT_ALIGN_MOZ_CENTER: + case NS_STYLE_TEXT_ALIGN_CENTER: aAlign.mXOffset += remainingSpace / 2; doCSS = PR_FALSE; break; @@ -359,7 +361,11 @@ nsBlockReflowContext::ReflowBlock(nsIFrame* aFrame, reflowState.mComputedBorderPadding.right; } - x = aSpace.XMost() - mMargin.right - frameWidth; + // if this is an unconstrained width reflow, then just place the floater at the left margin + if (NS_UNCONSTRAINEDSIZE == aSpace.width) + x = aSpace.x; + else + x = aSpace.XMost() - mMargin.right - frameWidth; } else { x = aSpace.x + mMargin.left; diff --git a/layout/generic/nsBlockReflowState.cpp b/layout/generic/nsBlockReflowState.cpp index 554b851c4d28..5f974deb204e 100644 --- a/layout/generic/nsBlockReflowState.cpp +++ b/layout/generic/nsBlockReflowState.cpp @@ -18,6 +18,8 @@ * Rights Reserved. * * Contributor(s): + * Steve Clark + * Robert O'Callahan * Pierre Phaneuf */ #include "nsCOMPtr.h" @@ -61,6 +63,8 @@ #ifdef DEBUG +//#define NOISY_BLOCK_INVALIDATE // DO NOT CHECK THIS IN TURNED ON! + static PRBool gLamePaintMetrics; static PRBool gLameReflowMetrics; static PRBool gNoisy; @@ -451,12 +455,6 @@ public: nscoord mBottomEdge; - PRPackedBool mUnconstrainedWidth; - PRPackedBool mUnconstrainedHeight; - PRPackedBool mShrinkWrapWidth; - PRPackedBool mNeedResizeReflow; - PRPackedBool mIsInlineIncrReflow; - // The content area to reflow child frames within. The x/y // coordinates are known to be mBorderPadding.left and // mBorderPadding.top. The width/height may be NS_UNCONSTRAINEDSIZE @@ -464,15 +462,6 @@ public: // unconstrained area. nsSize mContentArea; - // Our wrapping behavior - PRPackedBool mNoWrap; - - // Is this frame a root for top/bottom margin collapsing? - PRPackedBool mIsTopMarginRoot, mIsBottomMarginRoot; - - // See ShouldApplyTopMargin - PRPackedBool mApplyTopMargin; - //---------------------------------------- // This state is "running" state updated by the reflow of each line @@ -541,15 +530,48 @@ public: // being N^2. nsFloaterCacheFreeList mBelowCurrentLineFloaters; - PRPackedBool mComputeMaxElementSize; - PRPackedBool mComputeMaximumWidth; - nsSize mMaxElementSize; nscoord mMaximumWidth; nscoord mMinLineHeight; PRInt32 mLineNumber; + + // block reflow state flags +#define BRS_UNCONSTRAINEDWIDTH 0x00000001 +#define BRS_UNCONSTRAINEDHEIGHT 0x00000002 +#define BRS_SHRINKWRAPWIDTH 0x00000004 +#define BRS_NEEDRESIZEREFLOW 0x00000008 +#define BRS_ISINLINEINCRREFLOW 0x00000010 +#define BRS_NOWRAP 0x00000020 +#define BRS_ISTOPMARGINROOT 0x00000040 // Is this frame a root for top/bottom margin collapsing? +#define BRS_ISBOTTOMMARGINROOT 0x00000080 +#define BRS_APPLYTOPMARGIN 0x00000100 // See ShouldApplyTopMargin +#define BRS_COMPUTEMAXELEMENTSIZE 0x00000200 +#define BRS_COMPUTEMAXWIDTH 0x00000400 +#define BRS_LASTFLAG BRS_COMPUTEMAXWIDTH + + PRInt16 mFlags; + + void SetFlag(PRUint32 aFlag, PRBool aValue) + { + NS_ASSERTION(aFlag<=BRS_LASTFLAG, "bad flag"); + NS_ASSERTION(aValue==PR_FALSE || aValue==PR_TRUE, "bad value"); + if (aValue) { // set flag + mFlags |= aFlag; + } + else { // unset flag + mFlags &= ~aFlag; + } + } + + PRBool GetFlag(PRUint32 aFlag) const + { + NS_ASSERTION(aFlag<=BRS_LASTFLAG, "bad flag"); + PRBool result = (mFlags & aFlag); + if (result) return PR_TRUE; + return PR_FALSE; + } }; // XXX This is vile. Make it go away @@ -574,29 +596,25 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, : mBlock(aFrame), mPresContext(aPresContext), mReflowState(aReflowState), - mNeedResizeReflow(PR_FALSE), - mIsInlineIncrReflow(PR_FALSE), - mIsTopMarginRoot(PR_FALSE), - mIsBottomMarginRoot(PR_FALSE), - mApplyTopMargin(PR_FALSE), mNextRCFrame(nsnull), mPrevBottomMargin(0), - mLineNumber(0) + mLineNumber(0), + mFlags(0) { const nsMargin& borderPadding = BorderPadding(); if (aBlockMarginRoot) { - mIsTopMarginRoot = PR_TRUE; - mIsBottomMarginRoot = PR_TRUE; + SetFlag(BRS_ISTOPMARGINROOT, PR_TRUE); + SetFlag(BRS_ISBOTTOMMARGINROOT, PR_TRUE); } if (0 != aReflowState.mComputedBorderPadding.top) { - mIsTopMarginRoot = PR_TRUE; + SetFlag(BRS_ISTOPMARGINROOT, PR_TRUE); } if (0 != aReflowState.mComputedBorderPadding.bottom) { - mIsBottomMarginRoot = PR_TRUE; + SetFlag(BRS_ISBOTTOMMARGINROOT, PR_TRUE); } - if (mIsTopMarginRoot) { - mApplyTopMargin = PR_TRUE; + if (GetFlag(BRS_ISTOPMARGINROOT)) { + SetFlag(BRS_APPLYTOPMARGIN, PR_TRUE); } mSpaceManager = aReflowState.mSpaceManager; @@ -617,21 +635,19 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, // Compute content area width (the content area is inside the border // and padding) - mUnconstrainedWidth = PR_FALSE; - mShrinkWrapWidth = PR_FALSE; if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedWidth) { mContentArea.width = aReflowState.mComputedWidth; } else { if (NS_UNCONSTRAINEDSIZE == aReflowState.availableWidth) { mContentArea.width = NS_UNCONSTRAINEDSIZE; - mUnconstrainedWidth = PR_TRUE; + SetFlag(BRS_UNCONSTRAINEDWIDTH, PR_TRUE); } else if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMaxWidth) { // Choose a width based on the content (shrink wrap width) up // to the maximum width mContentArea.width = aReflowState.mComputedMaxWidth; - mShrinkWrapWidth = PR_TRUE; + SetFlag(BRS_SHRINKWRAPWIDTH, PR_TRUE); } else { nscoord lr = borderPadding.left + borderPadding.right; @@ -646,7 +662,6 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, // specified style height then we may end up limiting our height if // the availableHeight is constrained (this situation occurs when we // are paginated). - mUnconstrainedHeight = PR_FALSE; if (NS_UNCONSTRAINEDSIZE != aReflowState.availableHeight) { // We are in a paginated situation. The bottom edge is just inside // the bottom border and padding. The content area height doesn't @@ -657,7 +672,7 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, else { // When we are not in a paginated situation then we always use // an constrained height. - mUnconstrainedHeight = PR_TRUE; + SetFlag(BRS_UNCONSTRAINEDHEIGHT, PR_TRUE); mContentArea.height = mBottomEdge = NS_UNCONSTRAINEDSIZE; } @@ -674,16 +689,17 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, switch (styleText->mWhiteSpace) { case NS_STYLE_WHITESPACE_PRE: case NS_STYLE_WHITESPACE_NOWRAP: - mNoWrap = PR_TRUE; + SetFlag(BRS_NOWRAP, PR_TRUE); break; default: - mNoWrap = PR_FALSE; + SetFlag(BRS_NOWRAP, PR_FALSE); break; } - mComputeMaxElementSize = nsnull != aMetrics.maxElementSize; + SetFlag(BRS_COMPUTEMAXELEMENTSIZE, (nsnull != aMetrics.maxElementSize)); mMaxElementSize.SizeTo(0, 0); - mComputeMaximumWidth = NS_REFLOW_CALC_MAX_WIDTH == (aMetrics.mFlags & NS_REFLOW_CALC_MAX_WIDTH); + SetFlag(BRS_COMPUTEMAXWIDTH, + (NS_REFLOW_CALC_MAX_WIDTH == (aMetrics.mFlags & NS_REFLOW_CALC_MAX_WIDTH))); mMaximumWidth = 0; mMinLineHeight = nsHTMLReflowState::CalcLineHeight(mPresContext, @@ -729,8 +745,12 @@ nsBlockReflowState::ComputeBlockAvailSpace(nsIFrame* aFrame, const nsStyleDisplay* aDisplay, nsRect& aResult) { +#ifdef REALLY_NOISY_REFLOW + printf("CBAS frame=%p has floater count %d\n", aFrame, mBand.GetFloaterCount()); + mBand.List(); +#endif aResult.y = mY; - aResult.height = mUnconstrainedHeight + aResult.height = GetFlag(BRS_UNCONSTRAINEDHEIGHT) ? NS_UNCONSTRAINEDSIZE : mBottomEdge - mY; @@ -749,7 +769,7 @@ nsBlockReflowState::ComputeBlockAvailSpace(nsIFrame* aFrame, // The child block will flow around the floater. Therefore // give it all of the available space. aResult.x = borderPadding.left; - aResult.width = mUnconstrainedWidth + aResult.width = GetFlag(BRS_UNCONSTRAINEDWIDTH) ? NS_UNCONSTRAINEDSIZE : mContentArea.width; break; @@ -776,7 +796,7 @@ nsBlockReflowState::ComputeBlockAvailSpace(nsIFrame* aFrame, } // determine width - if (mUnconstrainedWidth) { + if (GetFlag(BRS_UNCONSTRAINEDWIDTH)) { aResult.width = NS_UNCONSTRAINEDSIZE; } else { @@ -810,7 +830,7 @@ nsBlockReflowState::ComputeBlockAvailSpace(nsIFrame* aFrame, // doesn't matter therefore give the block element all of the // available space since it will flow around the floater itself. aResult.x = borderPadding.left; - aResult.width = mUnconstrainedWidth + aResult.width = GetFlag(BRS_UNCONSTRAINEDWIDTH) ? NS_UNCONSTRAINEDSIZE : mContentArea.width; } @@ -957,12 +977,12 @@ nsBlockReflowState::RecoverStateFrom(nsLineBox* aLine, #endif mKidXMost = xmost; } - if (mComputeMaxElementSize) { + if (GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { UpdateMaxElementSize(nsSize(aLine->mMaxElementWidth, aLine->mBounds.height)); } // If computing the maximum width, then update mMaximumWidth - if (mComputeMaximumWidth) { + if (GetFlag(BRS_COMPUTEMAXWIDTH)) { UpdateMaximumWidth(aLine->mMaximumWidth); } @@ -1454,6 +1474,7 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, nsBlockReflowState state(aReflowState, aPresContext, this, aMetrics, NS_BLOCK_MARGIN_ROOT & mState); + PRInt32 sizeofBRS = sizeof nsBlockReflowState; if (eReflowReason_Resize != aReflowState.reason) { RenumberLists(aPresContext); @@ -1462,7 +1483,7 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, nsresult rv = NS_OK; PRBool isStyleChange = PR_FALSE; - state.mIsInlineIncrReflow = PR_FALSE; + state.SetFlag(BRS_ISINLINEINCRREFLOW, PR_FALSE); nsIFrame* target; switch (aReflowState.reason) { case eReflowReason_Initial: @@ -1535,7 +1556,7 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, // reflow the line containing the target of the incr. reflow // first mark the line dirty and set up the state object rv = PrepareChildIncrementalReflow(state); - state.mIsInlineIncrReflow = PR_TRUE; + state.SetFlag(BRS_ISINLINEINCRREFLOW, PR_TRUE); state.mPrevLine = prevLine; state.mCurrentLine = line; state.mNextRCFrame = state.mNextRCFrame; @@ -1681,6 +1702,10 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, if (isStyleChange) { // Lots of things could have changed so damage our entire // bounds +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 1 (%d, %d, %d, %d)\n", + this, 0, 0, mRect.width, mRect.height); +#endif Invalidate(aPresContext, nsRect(0, 0, mRect.width, mRect.height)); } else { @@ -1707,6 +1732,10 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, damageRect.y = 0; damageRect.height = mRect.height; } +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 2 (%d, %d, %d, %d)\n", + this, damageRect.x, damageRect.y, damageRect.width, damageRect.height); +#endif Invalidate(aPresContext, damageRect); } @@ -1730,6 +1759,10 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, damageRect.y = mRect.height - border.bottom; damageRect.height = border.bottom; } +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 3 (%d, %d, %d, %d)\n", + this, damageRect.x, damageRect.y, damageRect.width, damageRect.height); +#endif Invalidate(aPresContext, damageRect); } } @@ -1868,7 +1901,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, #ifdef NOISY_FINAL_SIZE ListTag(stdout); printf(": mY=%d mIsBottomMarginRoot=%s mPrevBottomMargin=%d bp=%d,%d\n", - aState.mY, aState.mIsBottomMarginRoot ? "yes" : "no", + aState.mY, aState.GetFlag(BRS_ISBOTTOMMARGINROOT) ? "yes" : "no", aState.mPrevBottomMargin, borderPadding.top, borderPadding.bottom); #endif @@ -1946,7 +1979,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, // contents or we fluff out to the maximum block width. Note: // We always shrink wrap when given an unconstrained width. if ((0 == (NS_BLOCK_SHRINK_WRAP & mState)) && - !aState.mUnconstrainedWidth && !aState.mShrinkWrapWidth && + !aState.GetFlag(BRS_UNCONSTRAINEDWIDTH) && !aState.GetFlag(BRS_SHRINKWRAPWIDTH) && !compact) { // Set our width to the max width if we aren't already that // wide. Note that the max-width has nothing to do with our @@ -1956,9 +1989,9 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, } // See if we should compute our max element size - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { // Adjust the computedWidth - if (aState.mNoWrap) { + if (aState.GetFlag(BRS_NOWRAP)) { // When no-wrap is true the max-element-size.width is the // width of the widest line plus the right border. Note that // aState.mKidXMost already has the left border factored in @@ -1996,7 +2029,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, // need to do horizontal alignment of the inline lines and make sure // blocks are correctly sized and positioned. Any lines that need // final adjustment will have been marked as dirty - if (aState.mShrinkWrapWidth && aState.mNeedResizeReflow) { + if (aState.GetFlag(BRS_SHRINKWRAPWIDTH) && aState.GetFlag(BRS_NEEDRESIZEREFLOW)) { // If the parent reflow state is also shrink wrap width, then // we don't need to do this, because it will reflow us after it // calculates the final width @@ -2025,7 +2058,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, } } - if (aState.mShrinkWrapWidth) { + if (aState.GetFlag(BRS_SHRINKWRAPWIDTH)) { PRBool parentIsShrinkWrapWidth = PR_FALSE; if (aReflowState.parentReflowState) { if (NS_SHRINKWRAPWIDTH == aReflowState.parentReflowState->mComputedWidth) { @@ -2047,7 +2080,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, // Don't carry out a bottom margin when our height is fixed // unless the bottom of the last line adjoins the bottom of our // content area. - if (!aState.mIsBottomMarginRoot) { + if (!aState.GetFlag(BRS_ISBOTTOMMARGINROOT)) { if (aState.mY + aState.mPrevBottomMargin != aMetrics.height) { aState.mPrevBottomMargin = 0; } @@ -2057,7 +2090,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, nscoord autoHeight = aState.mY; // Shrink wrap our height around our contents. - if (aState.mIsBottomMarginRoot) { + if (aState.GetFlag(BRS_ISBOTTOMMARGINROOT)) { // When we are a bottom-margin root make sure that our last // childs bottom margin is fully applied. // XXX check for a fit @@ -2082,7 +2115,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, } aMetrics.height = autoHeight; - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { maxHeight = aState.mMaxElementSize.height + borderPadding.top + borderPadding.bottom; } @@ -2090,7 +2123,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, aMetrics.ascent = aMetrics.height; aMetrics.descent = 0; - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { // Store away the final value aMetrics.maxElementSize->width = maxWidth; aMetrics.maxElementSize->height = maxHeight; @@ -2098,14 +2131,14 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, // Return bottom margin information aMetrics.mCarriedOutBottomMargin = - aState.mIsBottomMarginRoot ? 0 : aState.mPrevBottomMargin; + aState.GetFlag(BRS_ISBOTTOMMARGINROOT) ? 0 : aState.mPrevBottomMargin; #ifdef DEBUG if (CRAZY_WIDTH(aMetrics.width) || CRAZY_HEIGHT(aMetrics.height)) { ListTag(stdout); printf(": WARNING: desired:%d,%d\n", aMetrics.width, aMetrics.height); } - if (aState.mComputeMaxElementSize && + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE) && ((maxWidth > aMetrics.width) || (maxHeight > aMetrics.height))) { ListTag(stdout); printf(": WARNING: max-element-size:%d,%d desired:%d,%d maxSize:%d,%d\n", @@ -2115,7 +2148,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, } #endif #ifdef NOISY_MAX_ELEMENT_SIZE - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { IndentBy(stdout, GetDepth()); if (NS_UNCONSTRAINEDSIZE == aState.mReflowState.availableWidth) { printf("PASS1 "); @@ -2130,7 +2163,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, } // If we're requested to update our maximum width, then compute it - if (aState.mComputeMaximumWidth) { + if (aState.GetFlag(BRS_COMPUTEMAXWIDTH)) { // We need to add in for the right border/padding aMetrics.mMaximumWidth = aState.mMaximumWidth + borderPadding.right; } @@ -2337,8 +2370,13 @@ nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState) // See if we can try and avoid marking all the lines as dirty PRBool tryAndSkipLines = PR_FALSE; - // See if this is this a constrained resize reflow - if ((aState.mReflowState.reason == eReflowReason_Resize) && + // we need to calculate if any part of then block itself + // is impacted by a floater (bug 19579) + aState.GetAvailableSpace(); + + // See if this is this a constrained resize reflow that is not impacted by floaters + if ((PR_FALSE==aState.IsImpactedByFloater()) && + (aState.mReflowState.reason == eReflowReason_Resize) && (NS_UNCONSTRAINEDSIZE != aState.mReflowState.availableWidth)) { // If the text is left-aligned, then we try and avoid reflowing the lines @@ -2389,7 +2427,7 @@ nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState) } #endif - PRBool notWrapping = aState.mNoWrap; + PRBool notWrapping = aState.GetFlag(BRS_NOWRAP); while (nsnull != line) { if (line->IsBlock()) { @@ -2402,8 +2440,6 @@ nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState) printf("PrepareResizeReflow thinks line %p is %simpacted by floaters\n", line, line->IsImpactedByFloater() ? "" : "not "); #endif - - if (notWrapping) { // When no-wrap is set then the only line-breaking that // occurs for inline lines is triggered by BR elements or by @@ -2430,7 +2466,7 @@ nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState) printf("skipped: line=%p next=%p %s %s %s%s%s breakType=%d xmost=%d\n", line, line->mNext, line->IsBlock() ? "block" : "inline", - aState.mNoWrap ? "no-wrap" : "wrapping", + aState.GetFlag(BRS_NOWRAP) ? "no-wrap" : "wrapping", line->HasBreak() ? "has-break " : "", line->HasFloaters() ? "has-floaters " : "", line->IsImpactedByFloater() ? "impacted " : "", @@ -2628,7 +2664,7 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) ListTag(stdout); printf(": incrementally reflowing dirty lines: type=%s(%d) isInline=%s", kReflowCommandType[type], type, - aState.mIsInlineIncrReflow ? "true" : "false"); + aState.GetFlag(BRS_ISINLINEINCRREFLOW) ? "true" : "false"); } else { IndentBy(stdout, gNoiseIndent); @@ -2651,7 +2687,7 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) // Reflow the lines that are already ours aState.mPrevLine = nsnull; nsLineBox* line = mLines; - if (aState.mIsInlineIncrReflow && aState.mNextRCFrame) + if (aState.GetFlag(BRS_ISINLINEINCRREFLOW) && aState.mNextRCFrame) { const nsLineBox* incrTargetLine = aState.mCurrentLine; aState.mCurrentLine = line; @@ -2662,6 +2698,10 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) RecoverStateFrom(aState, line, deltaY, incrementalReflow ? &damageRect : 0); if (incrementalReflow && !damageRect.IsEmpty()) { +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 4 (%d, %d, %d, %d)\n", + this, damageRect.x, damageRect.y, damageRect.width, damageRect.height); +#endif Invalidate(aState.mPresContext, damageRect); } aState.mPrevLine = line; @@ -2689,7 +2729,7 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) // If we're supposed to update our maximum width, then we'll also need to // reflow this line if it's line wrapped and any of the continuing lines // are dirty - if (line->IsDirty() || (aState.mComputeMaximumWidth && ::WrappedLinesAreDirty(line))) { + if (line->IsDirty() || (aState.GetFlag(BRS_COMPUTEMAXWIDTH) && ::WrappedLinesAreDirty(line))) { // Compute the dirty lines "before" YMost, after factoring in // the running deltaY value - the running value is implicit in // aState.mY. @@ -2729,6 +2769,10 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) RecoverStateFrom(aState, line, deltaY, incrementalReflow ? &damageRect : 0); if (incrementalReflow && !damageRect.IsEmpty()) { +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 5 (%d, %d, %d, %d)\n", + this, damageRect.x, damageRect.y, damageRect.width, damageRect.height); +#endif Invalidate(aState.mPresContext, damageRect); } } @@ -2902,6 +2946,10 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, // XXX We need to improve on this... nsRect dirtyRect; dirtyRect.UnionRect(oldCombinedArea, lineCombinedArea); +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 6 (%d, %d, %d, %d)\n", + this, dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height); +#endif Invalidate(aState.mPresContext, dirtyRect); } else { @@ -2918,6 +2966,10 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, dirtyRect.x; dirtyRect.height = PR_MAX(oldCombinedArea.height, lineCombinedArea.height); +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 7 (%d, %d, %d, %d)\n", + this, dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height); +#endif Invalidate(aState.mPresContext, dirtyRect); } if (oldCombinedArea.height != lineCombinedArea.height) { @@ -2933,6 +2985,10 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, dirtyRect.height = PR_MAX(oldCombinedArea.YMost(), lineCombinedArea.YMost()) - dirtyRect.y; +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 8 (%d, %d, %d, %d)\n", + this, dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height); +#endif Invalidate(aState.mPresContext, dirtyRect); } } @@ -2951,21 +3007,21 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, // we'll either need to recover the floater state that applies to the // unconstrained reflow or keep it around in a separate space manager... PRBool isBeginningLine = !aState.mPrevLine || !aState.mPrevLine->IsLineWrapped(); - if (aState.mComputeMaximumWidth && isBeginningLine) { + if (aState.GetFlag(BRS_COMPUTEMAXWIDTH) && isBeginningLine) { nscoord oldY = aState.mY; nscoord oldPrevBottomMargin = aState.mPrevBottomMargin; - PRBool oldUnconstrainedWidth = aState.mUnconstrainedWidth; + PRBool oldUnconstrainedWidth = aState.GetFlag(BRS_UNCONSTRAINEDWIDTH); // First reflow the line with an unconstrained width. When doing this // we need to set the block reflow state's "mUnconstrainedWidth" variable // to PR_TRUE so if we encounter a placeholder and then reflow its // associated floater we don't end up resetting the line's right edge and // have it think the width is unconstrained... - aState.mUnconstrainedWidth = PR_TRUE; + aState.SetFlag(BRS_UNCONSTRAINEDWIDTH, PR_TRUE); ReflowInlineFrames(aState, aLine, aKeepReflowGoing, PR_TRUE); aState.mY = oldY; aState.mPrevBottomMargin = oldPrevBottomMargin; - aState.mUnconstrainedWidth = oldUnconstrainedWidth; + aState.SetFlag(BRS_UNCONSTRAINEDWIDTH, oldUnconstrainedWidth); // Update the line's maximum width aLine->mMaximumWidth = aLine->mBounds.XMost(); @@ -2980,14 +3036,14 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, // Note: we need to reset both member variables, because the inline // code examines mComputeMaxElementSize and if there is a placeholder // on this line the code to reflow the floater looks at both... - nscoord oldComputeMaxElementSize = aState.mComputeMaxElementSize; - nscoord oldComputeMaximumWidth = aState.mComputeMaximumWidth; + nscoord oldComputeMaxElementSize = aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE); + nscoord oldComputeMaximumWidth = aState.GetFlag(BRS_COMPUTEMAXWIDTH); - aState.mComputeMaxElementSize = PR_FALSE; - aState.mComputeMaximumWidth = PR_FALSE; + aState.SetFlag(BRS_COMPUTEMAXELEMENTSIZE, PR_FALSE); + aState.SetFlag(BRS_COMPUTEMAXWIDTH, PR_FALSE); rv = ReflowInlineFrames(aState, aLine, aKeepReflowGoing); - aState.mComputeMaxElementSize = oldComputeMaxElementSize; - aState.mComputeMaximumWidth = oldComputeMaximumWidth; + aState.SetFlag(BRS_COMPUTEMAXELEMENTSIZE, oldComputeMaxElementSize); + aState.SetFlag(BRS_COMPUTEMAXWIDTH, oldComputeMaximumWidth); } else { rv = ReflowInlineFrames(aState, aLine, aKeepReflowGoing); @@ -3001,6 +3057,10 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, nsRect dirtyRect; dirtyRect.UnionRect(oldCombinedArea, combinedArea); +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 9 (%d, %d, %d, %d)\n", + this, dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height); +#endif Invalidate(aState.mPresContext, dirtyRect); } } @@ -3368,7 +3428,7 @@ PRBool nsBlockFrame::ShouldApplyTopMargin(nsBlockReflowState& aState, nsLineBox* aLine) { - if (aState.mApplyTopMargin) { + if (aState.GetFlag(BRS_APPLYTOPMARGIN)) { // Apply short-circuit check to avoid searching the line list return PR_TRUE; } @@ -3377,7 +3437,7 @@ nsBlockFrame::ShouldApplyTopMargin(nsBlockReflowState& aState, // If we aren't at the top Y coordinate then something of non-zero // height must have been placed. Therefore the childs top-margin // applies. - aState.mApplyTopMargin = PR_TRUE; + aState.SetFlag(BRS_APPLYTOPMARGIN, PR_TRUE); return PR_TRUE; } @@ -3387,13 +3447,13 @@ nsBlockFrame::ShouldApplyTopMargin(nsBlockReflowState& aState, if (line->IsBlock()) { // A line which preceeds aLine contains a block; therefore the // top margin applies. - aState.mApplyTopMargin = PR_TRUE; + aState.SetFlag(BRS_APPLYTOPMARGIN, PR_TRUE); return PR_TRUE; } else if (line->HasFloaters()) { // A line which preceeds aLine is not empty therefore the top // margin applies. - aState.mApplyTopMargin = PR_TRUE; + aState.SetFlag(BRS_APPLYTOPMARGIN, PR_TRUE); return PR_TRUE; } line = line->mNext; @@ -3486,8 +3546,8 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, frame->GetStyleData(eStyleStruct_Display, (const nsStyleStruct*&) display); nsBlockReflowContext brc(aState.mPresContext, aState.mReflowState, - aState.mComputeMaxElementSize, - aState.mComputeMaximumWidth); + aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE), + aState.GetFlag(BRS_COMPUTEMAXWIDTH)); brc.SetNextRCFrame(aState.mNextRCFrame); // See if we should apply the top margin. If the block frame being @@ -3611,14 +3671,14 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, &collapsedBottomMargin, aLine->mBounds, combinedArea); - if (aState.mShrinkWrapWidth) { + if (aState.GetFlag(BRS_SHRINKWRAPWIDTH)) { // Mark the line as block so once we known the final shrink wrap width // we can reflow the block to the correct size // XXX We don't always need to do this... aLine->MarkDirty(); - aState.mNeedResizeReflow = PR_TRUE; + aState.SetFlag(BRS_NEEDRESIZEREFLOW, PR_TRUE); } - if (aState.mUnconstrainedWidth || aState.mShrinkWrapWidth) { + if (aState.GetFlag(BRS_UNCONSTRAINEDWIDTH) || aState.GetFlag(BRS_SHRINKWRAPWIDTH)) { // Add the right margin to the line's bounnds. That way it will be taken into // account when we compute our shrink wrap size nscoord marginRight = brc.GetMargin().right; @@ -3709,7 +3769,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, // Post-process the "line" nsSize maxElementSize(0, 0); - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { maxElementSize = brc.GetMaxElementSize(); if (aState.IsImpactedByFloater() && (NS_FRAME_SPLITTABLE_NON_RECTANGULAR != splitType)) { @@ -3721,7 +3781,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, } // If we asked the block to update its maximum width, then record the // updated value in the line, and update the current maximum width - if (aState.mComputeMaximumWidth) { + if (aState.GetFlag(BRS_COMPUTEMAXWIDTH)) { aLine->mMaximumWidth = brc.GetMaximumWidth(); aState.UpdateMaximumWidth(aLine->mMaximumWidth); @@ -3849,7 +3909,7 @@ nsBlockFrame::DoReflowInlineFramesMalloc(nsBlockReflowState& aState, nsLineLayout* ll = new nsLineLayout(aState.mPresContext, aState.mReflowState.mSpaceManager, &aState.mReflowState, - aState.mComputeMaxElementSize); + aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)); if (!ll) { return NS_ERROR_OUT_OF_MEMORY; } @@ -3872,7 +3932,7 @@ nsBlockFrame::DoReflowInlineFramesAuto(nsBlockReflowState& aState, nsLineLayout lineLayout(aState.mPresContext, aState.mReflowState.mSpaceManager, &aState.mReflowState, - aState.mComputeMaxElementSize); + aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)); lineLayout.Init(&aState, aState.mMinLineHeight, aState.mLineNumber); lineLayout.SetReflowTextRuns(mTextRuns); nsresult rv = DoReflowInlineFrames(aState, lineLayout, aLine, @@ -3910,7 +3970,7 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState, nscoord x = aState.mAvailSpaceRect.x + borderPadding.left; nscoord availWidth = aState.mAvailSpaceRect.width; nscoord availHeight; - if (aState.mUnconstrainedHeight) { + if (aState.GetFlag(BRS_UNCONSTRAINEDHEIGHT)) { availHeight = NS_UNCONSTRAINEDSIZE; } else { @@ -4359,7 +4419,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, nsSize maxElementSize; aLineLayout.VerticalAlignFrames(aLine, maxElementSize); // See if we're shrink wrapping the width - if (aState.mShrinkWrapWidth) { + if (aState.GetFlag(BRS_SHRINKWRAPWIDTH)) { // When determining the line's width we also need to include any // right floaters that impact us. This represents the shrink wrap // width of the line @@ -4388,25 +4448,20 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, // Only block frames horizontally align their children because // inline frames "shrink-wrap" around their children (therefore // there is no extra horizontal space). -#if XXX_fix_me - PRBool allowJustify = PR_TRUE; - if (NS_STYLE_TEXT_ALIGN_JUSTIFY == aState.mStyleText->mTextAlign) { - allowJustify = ShouldJustifyLine(aState, aLine); - } -#else - PRBool allowJustify = PR_FALSE; -#endif - - PRBool successful; - nsRect combinedArea; - successful = aLineLayout.HorizontalAlignFrames(aLine->mBounds, allowJustify, - aState.mShrinkWrapWidth); + const nsStyleText* styleText = (const nsStyleText*) + mStyleContext->GetStyleData(eStyleStruct_Text); + PRBool allowJustify = NS_STYLE_TEXT_ALIGN_JUSTIFY == styleText->mTextAlign + && !aLineLayout.GetLineEndsInBR() && ShouldJustifyLine(aState, aLine); + PRBool successful = aLineLayout.HorizontalAlignFrames(aLine->mBounds, allowJustify, + aState.GetFlag(BRS_SHRINKWRAPWIDTH)); if (!successful) { // Mark the line dirty and then later once we've determined the width // we can do the horizontal alignment aLine->MarkDirty(); - aState.mNeedResizeReflow = PR_TRUE; + aState.SetFlag(BRS_NEEDRESIZEREFLOW, PR_TRUE); } + + nsRect combinedArea; aLineLayout.RelativePositionFrames(combinedArea); aLine->SetCombinedArea(combinedArea); if (addedBullet) { @@ -4454,7 +4509,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, } aState.mY = newY; - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { #ifdef NOISY_MAX_ELEMENT_SIZE IndentBy(stdout, GetDepth()); if (NS_UNCONSTRAINEDSIZE == aState.mReflowState.availableWidth) { @@ -4478,7 +4533,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, // we don't want updated... if (aUpdateMaximumWidth) { // However, we do need to update the max-element-size if requested - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { aState.UpdateMaxElementSize(maxElementSize); // We also cache the max element width in the line. This is needed for // incremental reflow @@ -4519,7 +4574,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, CombineRects(aState.mFloaterCombinedArea, lineCombinedArea); if (aState.mHaveRightFloaters && - (aState.mUnconstrainedWidth || aState.mShrinkWrapWidth)) { + (aState.GetFlag(BRS_UNCONSTRAINEDWIDTH) || aState.GetFlag(BRS_SHRINKWRAPWIDTH))) { // We are reflowing in an unconstrained situation or shrink wrapping and // have some right floaters. They were placed at the infinite right edge // which will cause the combined area to be unusable. @@ -4540,11 +4595,11 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, aState.mRightFloaterCombinedArea.x = aLine->mBounds.XMost(); CombineRects(aState.mRightFloaterCombinedArea, lineCombinedArea); - if (aState.mShrinkWrapWidth) { + if (aState.GetFlag(BRS_SHRINKWRAPWIDTH)) { // Mark the line dirty so we come back and re-place the floater once // the shrink wrap width is determined aLine->MarkDirty(); - aState.mNeedResizeReflow = PR_TRUE; + aState.SetFlag(BRS_NEEDRESIZEREFLOW, PR_TRUE); } } aLine->SetCombinedArea(lineCombinedArea); @@ -4629,7 +4684,7 @@ nsBlockFrame::PostPlaceLine(nsBlockReflowState& aState, } // Update max-element-size - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { aState.UpdateMaxElementSize(aMaxElementSize); // We also cache the max element width in the line. This is needed for // incremental reflow @@ -4639,7 +4694,7 @@ nsBlockFrame::PostPlaceLine(nsBlockReflowState& aState, // If this is an unconstrained reflow, then cache the line width in the // line. We'll need this during incremental reflow if we're asked to // calculate the maximum width - if (aState.mUnconstrainedWidth) { + if (aState.GetFlag(BRS_UNCONSTRAINEDWIDTH)) { aLine->mMaximumWidth = aLine->mBounds.XMost(); } @@ -4653,7 +4708,7 @@ nsBlockFrame::PostPlaceLine(nsBlockReflowState& aState, #endif // If we're shrink wrapping our width and the line was wrapped, // then make sure we take up all of the available width - if (aState.mShrinkWrapWidth && aLine->IsLineWrapped()) { + if (aState.GetFlag(BRS_SHRINKWRAPWIDTH) && aLine->IsLineWrapped()) { aState.mKidXMost = aState.BorderPadding().left + aState.mContentArea.width; } @@ -5187,6 +5242,10 @@ nsBlockFrame::DoRemoveFrame(nsIPresContext* aPresContext, // cases... nsRect lineCombinedArea; line->GetCombinedArea(&lineCombinedArea); +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 10 (%d, %d, %d, %d)\n", + this, lineCombinedArea.x, lineCombinedArea.y, lineCombinedArea.width, lineCombinedArea.height); +#endif Invalidate(aPresContext, lineCombinedArea); line->Destroy(presShell); line = next; @@ -5282,8 +5341,8 @@ nsBlockFrame::ReflowFloater(nsBlockReflowState& aState, // Setup block reflow state to reflow the floater nsBlockReflowContext brc(aState.mPresContext, aState.mReflowState, - aState.mComputeMaxElementSize, - aState.mComputeMaximumWidth); + aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE), + aState.GetFlag(BRS_COMPUTEMAXWIDTH)); brc.SetNextRCFrame(aState.mNextRCFrame); // Reflow the floater @@ -5324,7 +5383,7 @@ nsBlockFrame::ReflowFloater(nsBlockReflowState& aState, floater->DidReflow(aState.mPresContext, NS_FRAME_REFLOW_FINISHED); // If we computed it, then stash away the max-element-size for later - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { aState.StoreMaxElementSize(floater, brc.GetMaxElementSize()); } @@ -5389,7 +5448,7 @@ nsBlockReflowState::AddFloater(nsLineLayout& aLineLayout, // Pass on updated available space to the current inline reflow engine GetAvailableSpace(); aLineLayout.UpdateBand(mAvailSpaceRect.x + BorderPadding().left, mY, - mUnconstrainedWidth ? NS_UNCONSTRAINEDSIZE : mAvailSpaceRect.width, + GetFlag(BRS_UNCONSTRAINEDWIDTH) ? NS_UNCONSTRAINEDSIZE : mAvailSpaceRect.width, mAvailSpaceRect.height, isLeftFloater, aPlaceholder->GetOutOfFlowFrame()); @@ -5615,7 +5674,12 @@ nsBlockReflowState::PlaceFloater(nsFloaterCache* aFloaterCache, } else { isLeftFloater = PR_FALSE; - region.x = mAvailSpaceRect.XMost() - region.width; + if (NS_UNCONSTRAINEDSIZE != mAvailSpaceRect.XMost()) + region.x = mAvailSpaceRect.XMost() - region.width; + else { + okToAddRectRegion = PR_FALSE; + region.x = mAvailSpaceRect.x; + } } *aIsLeftFloater = isLeftFloater; const nsMargin& borderPadding = BorderPadding(); @@ -5682,7 +5746,8 @@ nsBlockReflowState::PlaceFloater(nsFloaterCache* aFloaterCache, nsRect combinedArea = aFloaterCache->mCombinedArea; combinedArea.x += x; combinedArea.y += y; - if (!isLeftFloater && (mUnconstrainedWidth || mShrinkWrapWidth)) { + if (!isLeftFloater && + (GetFlag(BRS_UNCONSTRAINEDWIDTH) || GetFlag(BRS_SHRINKWRAPWIDTH))) { // When we are placing a right floater in an unconstrained situation or // when shrink wrapping, we don't apply it to the floater combined area // immediately. Otherwise we end up with an infinitely wide combined diff --git a/layout/generic/nsBlockReflowState.h b/layout/generic/nsBlockReflowState.h index 554b851c4d28..5f974deb204e 100644 --- a/layout/generic/nsBlockReflowState.h +++ b/layout/generic/nsBlockReflowState.h @@ -18,6 +18,8 @@ * Rights Reserved. * * Contributor(s): + * Steve Clark + * Robert O'Callahan * Pierre Phaneuf */ #include "nsCOMPtr.h" @@ -61,6 +63,8 @@ #ifdef DEBUG +//#define NOISY_BLOCK_INVALIDATE // DO NOT CHECK THIS IN TURNED ON! + static PRBool gLamePaintMetrics; static PRBool gLameReflowMetrics; static PRBool gNoisy; @@ -451,12 +455,6 @@ public: nscoord mBottomEdge; - PRPackedBool mUnconstrainedWidth; - PRPackedBool mUnconstrainedHeight; - PRPackedBool mShrinkWrapWidth; - PRPackedBool mNeedResizeReflow; - PRPackedBool mIsInlineIncrReflow; - // The content area to reflow child frames within. The x/y // coordinates are known to be mBorderPadding.left and // mBorderPadding.top. The width/height may be NS_UNCONSTRAINEDSIZE @@ -464,15 +462,6 @@ public: // unconstrained area. nsSize mContentArea; - // Our wrapping behavior - PRPackedBool mNoWrap; - - // Is this frame a root for top/bottom margin collapsing? - PRPackedBool mIsTopMarginRoot, mIsBottomMarginRoot; - - // See ShouldApplyTopMargin - PRPackedBool mApplyTopMargin; - //---------------------------------------- // This state is "running" state updated by the reflow of each line @@ -541,15 +530,48 @@ public: // being N^2. nsFloaterCacheFreeList mBelowCurrentLineFloaters; - PRPackedBool mComputeMaxElementSize; - PRPackedBool mComputeMaximumWidth; - nsSize mMaxElementSize; nscoord mMaximumWidth; nscoord mMinLineHeight; PRInt32 mLineNumber; + + // block reflow state flags +#define BRS_UNCONSTRAINEDWIDTH 0x00000001 +#define BRS_UNCONSTRAINEDHEIGHT 0x00000002 +#define BRS_SHRINKWRAPWIDTH 0x00000004 +#define BRS_NEEDRESIZEREFLOW 0x00000008 +#define BRS_ISINLINEINCRREFLOW 0x00000010 +#define BRS_NOWRAP 0x00000020 +#define BRS_ISTOPMARGINROOT 0x00000040 // Is this frame a root for top/bottom margin collapsing? +#define BRS_ISBOTTOMMARGINROOT 0x00000080 +#define BRS_APPLYTOPMARGIN 0x00000100 // See ShouldApplyTopMargin +#define BRS_COMPUTEMAXELEMENTSIZE 0x00000200 +#define BRS_COMPUTEMAXWIDTH 0x00000400 +#define BRS_LASTFLAG BRS_COMPUTEMAXWIDTH + + PRInt16 mFlags; + + void SetFlag(PRUint32 aFlag, PRBool aValue) + { + NS_ASSERTION(aFlag<=BRS_LASTFLAG, "bad flag"); + NS_ASSERTION(aValue==PR_FALSE || aValue==PR_TRUE, "bad value"); + if (aValue) { // set flag + mFlags |= aFlag; + } + else { // unset flag + mFlags &= ~aFlag; + } + } + + PRBool GetFlag(PRUint32 aFlag) const + { + NS_ASSERTION(aFlag<=BRS_LASTFLAG, "bad flag"); + PRBool result = (mFlags & aFlag); + if (result) return PR_TRUE; + return PR_FALSE; + } }; // XXX This is vile. Make it go away @@ -574,29 +596,25 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, : mBlock(aFrame), mPresContext(aPresContext), mReflowState(aReflowState), - mNeedResizeReflow(PR_FALSE), - mIsInlineIncrReflow(PR_FALSE), - mIsTopMarginRoot(PR_FALSE), - mIsBottomMarginRoot(PR_FALSE), - mApplyTopMargin(PR_FALSE), mNextRCFrame(nsnull), mPrevBottomMargin(0), - mLineNumber(0) + mLineNumber(0), + mFlags(0) { const nsMargin& borderPadding = BorderPadding(); if (aBlockMarginRoot) { - mIsTopMarginRoot = PR_TRUE; - mIsBottomMarginRoot = PR_TRUE; + SetFlag(BRS_ISTOPMARGINROOT, PR_TRUE); + SetFlag(BRS_ISBOTTOMMARGINROOT, PR_TRUE); } if (0 != aReflowState.mComputedBorderPadding.top) { - mIsTopMarginRoot = PR_TRUE; + SetFlag(BRS_ISTOPMARGINROOT, PR_TRUE); } if (0 != aReflowState.mComputedBorderPadding.bottom) { - mIsBottomMarginRoot = PR_TRUE; + SetFlag(BRS_ISBOTTOMMARGINROOT, PR_TRUE); } - if (mIsTopMarginRoot) { - mApplyTopMargin = PR_TRUE; + if (GetFlag(BRS_ISTOPMARGINROOT)) { + SetFlag(BRS_APPLYTOPMARGIN, PR_TRUE); } mSpaceManager = aReflowState.mSpaceManager; @@ -617,21 +635,19 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, // Compute content area width (the content area is inside the border // and padding) - mUnconstrainedWidth = PR_FALSE; - mShrinkWrapWidth = PR_FALSE; if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedWidth) { mContentArea.width = aReflowState.mComputedWidth; } else { if (NS_UNCONSTRAINEDSIZE == aReflowState.availableWidth) { mContentArea.width = NS_UNCONSTRAINEDSIZE; - mUnconstrainedWidth = PR_TRUE; + SetFlag(BRS_UNCONSTRAINEDWIDTH, PR_TRUE); } else if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMaxWidth) { // Choose a width based on the content (shrink wrap width) up // to the maximum width mContentArea.width = aReflowState.mComputedMaxWidth; - mShrinkWrapWidth = PR_TRUE; + SetFlag(BRS_SHRINKWRAPWIDTH, PR_TRUE); } else { nscoord lr = borderPadding.left + borderPadding.right; @@ -646,7 +662,6 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, // specified style height then we may end up limiting our height if // the availableHeight is constrained (this situation occurs when we // are paginated). - mUnconstrainedHeight = PR_FALSE; if (NS_UNCONSTRAINEDSIZE != aReflowState.availableHeight) { // We are in a paginated situation. The bottom edge is just inside // the bottom border and padding. The content area height doesn't @@ -657,7 +672,7 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, else { // When we are not in a paginated situation then we always use // an constrained height. - mUnconstrainedHeight = PR_TRUE; + SetFlag(BRS_UNCONSTRAINEDHEIGHT, PR_TRUE); mContentArea.height = mBottomEdge = NS_UNCONSTRAINEDSIZE; } @@ -674,16 +689,17 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, switch (styleText->mWhiteSpace) { case NS_STYLE_WHITESPACE_PRE: case NS_STYLE_WHITESPACE_NOWRAP: - mNoWrap = PR_TRUE; + SetFlag(BRS_NOWRAP, PR_TRUE); break; default: - mNoWrap = PR_FALSE; + SetFlag(BRS_NOWRAP, PR_FALSE); break; } - mComputeMaxElementSize = nsnull != aMetrics.maxElementSize; + SetFlag(BRS_COMPUTEMAXELEMENTSIZE, (nsnull != aMetrics.maxElementSize)); mMaxElementSize.SizeTo(0, 0); - mComputeMaximumWidth = NS_REFLOW_CALC_MAX_WIDTH == (aMetrics.mFlags & NS_REFLOW_CALC_MAX_WIDTH); + SetFlag(BRS_COMPUTEMAXWIDTH, + (NS_REFLOW_CALC_MAX_WIDTH == (aMetrics.mFlags & NS_REFLOW_CALC_MAX_WIDTH))); mMaximumWidth = 0; mMinLineHeight = nsHTMLReflowState::CalcLineHeight(mPresContext, @@ -729,8 +745,12 @@ nsBlockReflowState::ComputeBlockAvailSpace(nsIFrame* aFrame, const nsStyleDisplay* aDisplay, nsRect& aResult) { +#ifdef REALLY_NOISY_REFLOW + printf("CBAS frame=%p has floater count %d\n", aFrame, mBand.GetFloaterCount()); + mBand.List(); +#endif aResult.y = mY; - aResult.height = mUnconstrainedHeight + aResult.height = GetFlag(BRS_UNCONSTRAINEDHEIGHT) ? NS_UNCONSTRAINEDSIZE : mBottomEdge - mY; @@ -749,7 +769,7 @@ nsBlockReflowState::ComputeBlockAvailSpace(nsIFrame* aFrame, // The child block will flow around the floater. Therefore // give it all of the available space. aResult.x = borderPadding.left; - aResult.width = mUnconstrainedWidth + aResult.width = GetFlag(BRS_UNCONSTRAINEDWIDTH) ? NS_UNCONSTRAINEDSIZE : mContentArea.width; break; @@ -776,7 +796,7 @@ nsBlockReflowState::ComputeBlockAvailSpace(nsIFrame* aFrame, } // determine width - if (mUnconstrainedWidth) { + if (GetFlag(BRS_UNCONSTRAINEDWIDTH)) { aResult.width = NS_UNCONSTRAINEDSIZE; } else { @@ -810,7 +830,7 @@ nsBlockReflowState::ComputeBlockAvailSpace(nsIFrame* aFrame, // doesn't matter therefore give the block element all of the // available space since it will flow around the floater itself. aResult.x = borderPadding.left; - aResult.width = mUnconstrainedWidth + aResult.width = GetFlag(BRS_UNCONSTRAINEDWIDTH) ? NS_UNCONSTRAINEDSIZE : mContentArea.width; } @@ -957,12 +977,12 @@ nsBlockReflowState::RecoverStateFrom(nsLineBox* aLine, #endif mKidXMost = xmost; } - if (mComputeMaxElementSize) { + if (GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { UpdateMaxElementSize(nsSize(aLine->mMaxElementWidth, aLine->mBounds.height)); } // If computing the maximum width, then update mMaximumWidth - if (mComputeMaximumWidth) { + if (GetFlag(BRS_COMPUTEMAXWIDTH)) { UpdateMaximumWidth(aLine->mMaximumWidth); } @@ -1454,6 +1474,7 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, nsBlockReflowState state(aReflowState, aPresContext, this, aMetrics, NS_BLOCK_MARGIN_ROOT & mState); + PRInt32 sizeofBRS = sizeof nsBlockReflowState; if (eReflowReason_Resize != aReflowState.reason) { RenumberLists(aPresContext); @@ -1462,7 +1483,7 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, nsresult rv = NS_OK; PRBool isStyleChange = PR_FALSE; - state.mIsInlineIncrReflow = PR_FALSE; + state.SetFlag(BRS_ISINLINEINCRREFLOW, PR_FALSE); nsIFrame* target; switch (aReflowState.reason) { case eReflowReason_Initial: @@ -1535,7 +1556,7 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, // reflow the line containing the target of the incr. reflow // first mark the line dirty and set up the state object rv = PrepareChildIncrementalReflow(state); - state.mIsInlineIncrReflow = PR_TRUE; + state.SetFlag(BRS_ISINLINEINCRREFLOW, PR_TRUE); state.mPrevLine = prevLine; state.mCurrentLine = line; state.mNextRCFrame = state.mNextRCFrame; @@ -1681,6 +1702,10 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, if (isStyleChange) { // Lots of things could have changed so damage our entire // bounds +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 1 (%d, %d, %d, %d)\n", + this, 0, 0, mRect.width, mRect.height); +#endif Invalidate(aPresContext, nsRect(0, 0, mRect.width, mRect.height)); } else { @@ -1707,6 +1732,10 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, damageRect.y = 0; damageRect.height = mRect.height; } +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 2 (%d, %d, %d, %d)\n", + this, damageRect.x, damageRect.y, damageRect.width, damageRect.height); +#endif Invalidate(aPresContext, damageRect); } @@ -1730,6 +1759,10 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, damageRect.y = mRect.height - border.bottom; damageRect.height = border.bottom; } +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 3 (%d, %d, %d, %d)\n", + this, damageRect.x, damageRect.y, damageRect.width, damageRect.height); +#endif Invalidate(aPresContext, damageRect); } } @@ -1868,7 +1901,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, #ifdef NOISY_FINAL_SIZE ListTag(stdout); printf(": mY=%d mIsBottomMarginRoot=%s mPrevBottomMargin=%d bp=%d,%d\n", - aState.mY, aState.mIsBottomMarginRoot ? "yes" : "no", + aState.mY, aState.GetFlag(BRS_ISBOTTOMMARGINROOT) ? "yes" : "no", aState.mPrevBottomMargin, borderPadding.top, borderPadding.bottom); #endif @@ -1946,7 +1979,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, // contents or we fluff out to the maximum block width. Note: // We always shrink wrap when given an unconstrained width. if ((0 == (NS_BLOCK_SHRINK_WRAP & mState)) && - !aState.mUnconstrainedWidth && !aState.mShrinkWrapWidth && + !aState.GetFlag(BRS_UNCONSTRAINEDWIDTH) && !aState.GetFlag(BRS_SHRINKWRAPWIDTH) && !compact) { // Set our width to the max width if we aren't already that // wide. Note that the max-width has nothing to do with our @@ -1956,9 +1989,9 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, } // See if we should compute our max element size - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { // Adjust the computedWidth - if (aState.mNoWrap) { + if (aState.GetFlag(BRS_NOWRAP)) { // When no-wrap is true the max-element-size.width is the // width of the widest line plus the right border. Note that // aState.mKidXMost already has the left border factored in @@ -1996,7 +2029,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, // need to do horizontal alignment of the inline lines and make sure // blocks are correctly sized and positioned. Any lines that need // final adjustment will have been marked as dirty - if (aState.mShrinkWrapWidth && aState.mNeedResizeReflow) { + if (aState.GetFlag(BRS_SHRINKWRAPWIDTH) && aState.GetFlag(BRS_NEEDRESIZEREFLOW)) { // If the parent reflow state is also shrink wrap width, then // we don't need to do this, because it will reflow us after it // calculates the final width @@ -2025,7 +2058,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, } } - if (aState.mShrinkWrapWidth) { + if (aState.GetFlag(BRS_SHRINKWRAPWIDTH)) { PRBool parentIsShrinkWrapWidth = PR_FALSE; if (aReflowState.parentReflowState) { if (NS_SHRINKWRAPWIDTH == aReflowState.parentReflowState->mComputedWidth) { @@ -2047,7 +2080,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, // Don't carry out a bottom margin when our height is fixed // unless the bottom of the last line adjoins the bottom of our // content area. - if (!aState.mIsBottomMarginRoot) { + if (!aState.GetFlag(BRS_ISBOTTOMMARGINROOT)) { if (aState.mY + aState.mPrevBottomMargin != aMetrics.height) { aState.mPrevBottomMargin = 0; } @@ -2057,7 +2090,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, nscoord autoHeight = aState.mY; // Shrink wrap our height around our contents. - if (aState.mIsBottomMarginRoot) { + if (aState.GetFlag(BRS_ISBOTTOMMARGINROOT)) { // When we are a bottom-margin root make sure that our last // childs bottom margin is fully applied. // XXX check for a fit @@ -2082,7 +2115,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, } aMetrics.height = autoHeight; - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { maxHeight = aState.mMaxElementSize.height + borderPadding.top + borderPadding.bottom; } @@ -2090,7 +2123,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, aMetrics.ascent = aMetrics.height; aMetrics.descent = 0; - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { // Store away the final value aMetrics.maxElementSize->width = maxWidth; aMetrics.maxElementSize->height = maxHeight; @@ -2098,14 +2131,14 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, // Return bottom margin information aMetrics.mCarriedOutBottomMargin = - aState.mIsBottomMarginRoot ? 0 : aState.mPrevBottomMargin; + aState.GetFlag(BRS_ISBOTTOMMARGINROOT) ? 0 : aState.mPrevBottomMargin; #ifdef DEBUG if (CRAZY_WIDTH(aMetrics.width) || CRAZY_HEIGHT(aMetrics.height)) { ListTag(stdout); printf(": WARNING: desired:%d,%d\n", aMetrics.width, aMetrics.height); } - if (aState.mComputeMaxElementSize && + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE) && ((maxWidth > aMetrics.width) || (maxHeight > aMetrics.height))) { ListTag(stdout); printf(": WARNING: max-element-size:%d,%d desired:%d,%d maxSize:%d,%d\n", @@ -2115,7 +2148,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, } #endif #ifdef NOISY_MAX_ELEMENT_SIZE - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { IndentBy(stdout, GetDepth()); if (NS_UNCONSTRAINEDSIZE == aState.mReflowState.availableWidth) { printf("PASS1 "); @@ -2130,7 +2163,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, } // If we're requested to update our maximum width, then compute it - if (aState.mComputeMaximumWidth) { + if (aState.GetFlag(BRS_COMPUTEMAXWIDTH)) { // We need to add in for the right border/padding aMetrics.mMaximumWidth = aState.mMaximumWidth + borderPadding.right; } @@ -2337,8 +2370,13 @@ nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState) // See if we can try and avoid marking all the lines as dirty PRBool tryAndSkipLines = PR_FALSE; - // See if this is this a constrained resize reflow - if ((aState.mReflowState.reason == eReflowReason_Resize) && + // we need to calculate if any part of then block itself + // is impacted by a floater (bug 19579) + aState.GetAvailableSpace(); + + // See if this is this a constrained resize reflow that is not impacted by floaters + if ((PR_FALSE==aState.IsImpactedByFloater()) && + (aState.mReflowState.reason == eReflowReason_Resize) && (NS_UNCONSTRAINEDSIZE != aState.mReflowState.availableWidth)) { // If the text is left-aligned, then we try and avoid reflowing the lines @@ -2389,7 +2427,7 @@ nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState) } #endif - PRBool notWrapping = aState.mNoWrap; + PRBool notWrapping = aState.GetFlag(BRS_NOWRAP); while (nsnull != line) { if (line->IsBlock()) { @@ -2402,8 +2440,6 @@ nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState) printf("PrepareResizeReflow thinks line %p is %simpacted by floaters\n", line, line->IsImpactedByFloater() ? "" : "not "); #endif - - if (notWrapping) { // When no-wrap is set then the only line-breaking that // occurs for inline lines is triggered by BR elements or by @@ -2430,7 +2466,7 @@ nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState) printf("skipped: line=%p next=%p %s %s %s%s%s breakType=%d xmost=%d\n", line, line->mNext, line->IsBlock() ? "block" : "inline", - aState.mNoWrap ? "no-wrap" : "wrapping", + aState.GetFlag(BRS_NOWRAP) ? "no-wrap" : "wrapping", line->HasBreak() ? "has-break " : "", line->HasFloaters() ? "has-floaters " : "", line->IsImpactedByFloater() ? "impacted " : "", @@ -2628,7 +2664,7 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) ListTag(stdout); printf(": incrementally reflowing dirty lines: type=%s(%d) isInline=%s", kReflowCommandType[type], type, - aState.mIsInlineIncrReflow ? "true" : "false"); + aState.GetFlag(BRS_ISINLINEINCRREFLOW) ? "true" : "false"); } else { IndentBy(stdout, gNoiseIndent); @@ -2651,7 +2687,7 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) // Reflow the lines that are already ours aState.mPrevLine = nsnull; nsLineBox* line = mLines; - if (aState.mIsInlineIncrReflow && aState.mNextRCFrame) + if (aState.GetFlag(BRS_ISINLINEINCRREFLOW) && aState.mNextRCFrame) { const nsLineBox* incrTargetLine = aState.mCurrentLine; aState.mCurrentLine = line; @@ -2662,6 +2698,10 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) RecoverStateFrom(aState, line, deltaY, incrementalReflow ? &damageRect : 0); if (incrementalReflow && !damageRect.IsEmpty()) { +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 4 (%d, %d, %d, %d)\n", + this, damageRect.x, damageRect.y, damageRect.width, damageRect.height); +#endif Invalidate(aState.mPresContext, damageRect); } aState.mPrevLine = line; @@ -2689,7 +2729,7 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) // If we're supposed to update our maximum width, then we'll also need to // reflow this line if it's line wrapped and any of the continuing lines // are dirty - if (line->IsDirty() || (aState.mComputeMaximumWidth && ::WrappedLinesAreDirty(line))) { + if (line->IsDirty() || (aState.GetFlag(BRS_COMPUTEMAXWIDTH) && ::WrappedLinesAreDirty(line))) { // Compute the dirty lines "before" YMost, after factoring in // the running deltaY value - the running value is implicit in // aState.mY. @@ -2729,6 +2769,10 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) RecoverStateFrom(aState, line, deltaY, incrementalReflow ? &damageRect : 0); if (incrementalReflow && !damageRect.IsEmpty()) { +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 5 (%d, %d, %d, %d)\n", + this, damageRect.x, damageRect.y, damageRect.width, damageRect.height); +#endif Invalidate(aState.mPresContext, damageRect); } } @@ -2902,6 +2946,10 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, // XXX We need to improve on this... nsRect dirtyRect; dirtyRect.UnionRect(oldCombinedArea, lineCombinedArea); +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 6 (%d, %d, %d, %d)\n", + this, dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height); +#endif Invalidate(aState.mPresContext, dirtyRect); } else { @@ -2918,6 +2966,10 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, dirtyRect.x; dirtyRect.height = PR_MAX(oldCombinedArea.height, lineCombinedArea.height); +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 7 (%d, %d, %d, %d)\n", + this, dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height); +#endif Invalidate(aState.mPresContext, dirtyRect); } if (oldCombinedArea.height != lineCombinedArea.height) { @@ -2933,6 +2985,10 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, dirtyRect.height = PR_MAX(oldCombinedArea.YMost(), lineCombinedArea.YMost()) - dirtyRect.y; +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 8 (%d, %d, %d, %d)\n", + this, dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height); +#endif Invalidate(aState.mPresContext, dirtyRect); } } @@ -2951,21 +3007,21 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, // we'll either need to recover the floater state that applies to the // unconstrained reflow or keep it around in a separate space manager... PRBool isBeginningLine = !aState.mPrevLine || !aState.mPrevLine->IsLineWrapped(); - if (aState.mComputeMaximumWidth && isBeginningLine) { + if (aState.GetFlag(BRS_COMPUTEMAXWIDTH) && isBeginningLine) { nscoord oldY = aState.mY; nscoord oldPrevBottomMargin = aState.mPrevBottomMargin; - PRBool oldUnconstrainedWidth = aState.mUnconstrainedWidth; + PRBool oldUnconstrainedWidth = aState.GetFlag(BRS_UNCONSTRAINEDWIDTH); // First reflow the line with an unconstrained width. When doing this // we need to set the block reflow state's "mUnconstrainedWidth" variable // to PR_TRUE so if we encounter a placeholder and then reflow its // associated floater we don't end up resetting the line's right edge and // have it think the width is unconstrained... - aState.mUnconstrainedWidth = PR_TRUE; + aState.SetFlag(BRS_UNCONSTRAINEDWIDTH, PR_TRUE); ReflowInlineFrames(aState, aLine, aKeepReflowGoing, PR_TRUE); aState.mY = oldY; aState.mPrevBottomMargin = oldPrevBottomMargin; - aState.mUnconstrainedWidth = oldUnconstrainedWidth; + aState.SetFlag(BRS_UNCONSTRAINEDWIDTH, oldUnconstrainedWidth); // Update the line's maximum width aLine->mMaximumWidth = aLine->mBounds.XMost(); @@ -2980,14 +3036,14 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, // Note: we need to reset both member variables, because the inline // code examines mComputeMaxElementSize and if there is a placeholder // on this line the code to reflow the floater looks at both... - nscoord oldComputeMaxElementSize = aState.mComputeMaxElementSize; - nscoord oldComputeMaximumWidth = aState.mComputeMaximumWidth; + nscoord oldComputeMaxElementSize = aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE); + nscoord oldComputeMaximumWidth = aState.GetFlag(BRS_COMPUTEMAXWIDTH); - aState.mComputeMaxElementSize = PR_FALSE; - aState.mComputeMaximumWidth = PR_FALSE; + aState.SetFlag(BRS_COMPUTEMAXELEMENTSIZE, PR_FALSE); + aState.SetFlag(BRS_COMPUTEMAXWIDTH, PR_FALSE); rv = ReflowInlineFrames(aState, aLine, aKeepReflowGoing); - aState.mComputeMaxElementSize = oldComputeMaxElementSize; - aState.mComputeMaximumWidth = oldComputeMaximumWidth; + aState.SetFlag(BRS_COMPUTEMAXELEMENTSIZE, oldComputeMaxElementSize); + aState.SetFlag(BRS_COMPUTEMAXWIDTH, oldComputeMaximumWidth); } else { rv = ReflowInlineFrames(aState, aLine, aKeepReflowGoing); @@ -3001,6 +3057,10 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, nsRect dirtyRect; dirtyRect.UnionRect(oldCombinedArea, combinedArea); +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 9 (%d, %d, %d, %d)\n", + this, dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height); +#endif Invalidate(aState.mPresContext, dirtyRect); } } @@ -3368,7 +3428,7 @@ PRBool nsBlockFrame::ShouldApplyTopMargin(nsBlockReflowState& aState, nsLineBox* aLine) { - if (aState.mApplyTopMargin) { + if (aState.GetFlag(BRS_APPLYTOPMARGIN)) { // Apply short-circuit check to avoid searching the line list return PR_TRUE; } @@ -3377,7 +3437,7 @@ nsBlockFrame::ShouldApplyTopMargin(nsBlockReflowState& aState, // If we aren't at the top Y coordinate then something of non-zero // height must have been placed. Therefore the childs top-margin // applies. - aState.mApplyTopMargin = PR_TRUE; + aState.SetFlag(BRS_APPLYTOPMARGIN, PR_TRUE); return PR_TRUE; } @@ -3387,13 +3447,13 @@ nsBlockFrame::ShouldApplyTopMargin(nsBlockReflowState& aState, if (line->IsBlock()) { // A line which preceeds aLine contains a block; therefore the // top margin applies. - aState.mApplyTopMargin = PR_TRUE; + aState.SetFlag(BRS_APPLYTOPMARGIN, PR_TRUE); return PR_TRUE; } else if (line->HasFloaters()) { // A line which preceeds aLine is not empty therefore the top // margin applies. - aState.mApplyTopMargin = PR_TRUE; + aState.SetFlag(BRS_APPLYTOPMARGIN, PR_TRUE); return PR_TRUE; } line = line->mNext; @@ -3486,8 +3546,8 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, frame->GetStyleData(eStyleStruct_Display, (const nsStyleStruct*&) display); nsBlockReflowContext brc(aState.mPresContext, aState.mReflowState, - aState.mComputeMaxElementSize, - aState.mComputeMaximumWidth); + aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE), + aState.GetFlag(BRS_COMPUTEMAXWIDTH)); brc.SetNextRCFrame(aState.mNextRCFrame); // See if we should apply the top margin. If the block frame being @@ -3611,14 +3671,14 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, &collapsedBottomMargin, aLine->mBounds, combinedArea); - if (aState.mShrinkWrapWidth) { + if (aState.GetFlag(BRS_SHRINKWRAPWIDTH)) { // Mark the line as block so once we known the final shrink wrap width // we can reflow the block to the correct size // XXX We don't always need to do this... aLine->MarkDirty(); - aState.mNeedResizeReflow = PR_TRUE; + aState.SetFlag(BRS_NEEDRESIZEREFLOW, PR_TRUE); } - if (aState.mUnconstrainedWidth || aState.mShrinkWrapWidth) { + if (aState.GetFlag(BRS_UNCONSTRAINEDWIDTH) || aState.GetFlag(BRS_SHRINKWRAPWIDTH)) { // Add the right margin to the line's bounnds. That way it will be taken into // account when we compute our shrink wrap size nscoord marginRight = brc.GetMargin().right; @@ -3709,7 +3769,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, // Post-process the "line" nsSize maxElementSize(0, 0); - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { maxElementSize = brc.GetMaxElementSize(); if (aState.IsImpactedByFloater() && (NS_FRAME_SPLITTABLE_NON_RECTANGULAR != splitType)) { @@ -3721,7 +3781,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, } // If we asked the block to update its maximum width, then record the // updated value in the line, and update the current maximum width - if (aState.mComputeMaximumWidth) { + if (aState.GetFlag(BRS_COMPUTEMAXWIDTH)) { aLine->mMaximumWidth = brc.GetMaximumWidth(); aState.UpdateMaximumWidth(aLine->mMaximumWidth); @@ -3849,7 +3909,7 @@ nsBlockFrame::DoReflowInlineFramesMalloc(nsBlockReflowState& aState, nsLineLayout* ll = new nsLineLayout(aState.mPresContext, aState.mReflowState.mSpaceManager, &aState.mReflowState, - aState.mComputeMaxElementSize); + aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)); if (!ll) { return NS_ERROR_OUT_OF_MEMORY; } @@ -3872,7 +3932,7 @@ nsBlockFrame::DoReflowInlineFramesAuto(nsBlockReflowState& aState, nsLineLayout lineLayout(aState.mPresContext, aState.mReflowState.mSpaceManager, &aState.mReflowState, - aState.mComputeMaxElementSize); + aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)); lineLayout.Init(&aState, aState.mMinLineHeight, aState.mLineNumber); lineLayout.SetReflowTextRuns(mTextRuns); nsresult rv = DoReflowInlineFrames(aState, lineLayout, aLine, @@ -3910,7 +3970,7 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState, nscoord x = aState.mAvailSpaceRect.x + borderPadding.left; nscoord availWidth = aState.mAvailSpaceRect.width; nscoord availHeight; - if (aState.mUnconstrainedHeight) { + if (aState.GetFlag(BRS_UNCONSTRAINEDHEIGHT)) { availHeight = NS_UNCONSTRAINEDSIZE; } else { @@ -4359,7 +4419,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, nsSize maxElementSize; aLineLayout.VerticalAlignFrames(aLine, maxElementSize); // See if we're shrink wrapping the width - if (aState.mShrinkWrapWidth) { + if (aState.GetFlag(BRS_SHRINKWRAPWIDTH)) { // When determining the line's width we also need to include any // right floaters that impact us. This represents the shrink wrap // width of the line @@ -4388,25 +4448,20 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, // Only block frames horizontally align their children because // inline frames "shrink-wrap" around their children (therefore // there is no extra horizontal space). -#if XXX_fix_me - PRBool allowJustify = PR_TRUE; - if (NS_STYLE_TEXT_ALIGN_JUSTIFY == aState.mStyleText->mTextAlign) { - allowJustify = ShouldJustifyLine(aState, aLine); - } -#else - PRBool allowJustify = PR_FALSE; -#endif - - PRBool successful; - nsRect combinedArea; - successful = aLineLayout.HorizontalAlignFrames(aLine->mBounds, allowJustify, - aState.mShrinkWrapWidth); + const nsStyleText* styleText = (const nsStyleText*) + mStyleContext->GetStyleData(eStyleStruct_Text); + PRBool allowJustify = NS_STYLE_TEXT_ALIGN_JUSTIFY == styleText->mTextAlign + && !aLineLayout.GetLineEndsInBR() && ShouldJustifyLine(aState, aLine); + PRBool successful = aLineLayout.HorizontalAlignFrames(aLine->mBounds, allowJustify, + aState.GetFlag(BRS_SHRINKWRAPWIDTH)); if (!successful) { // Mark the line dirty and then later once we've determined the width // we can do the horizontal alignment aLine->MarkDirty(); - aState.mNeedResizeReflow = PR_TRUE; + aState.SetFlag(BRS_NEEDRESIZEREFLOW, PR_TRUE); } + + nsRect combinedArea; aLineLayout.RelativePositionFrames(combinedArea); aLine->SetCombinedArea(combinedArea); if (addedBullet) { @@ -4454,7 +4509,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, } aState.mY = newY; - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { #ifdef NOISY_MAX_ELEMENT_SIZE IndentBy(stdout, GetDepth()); if (NS_UNCONSTRAINEDSIZE == aState.mReflowState.availableWidth) { @@ -4478,7 +4533,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, // we don't want updated... if (aUpdateMaximumWidth) { // However, we do need to update the max-element-size if requested - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { aState.UpdateMaxElementSize(maxElementSize); // We also cache the max element width in the line. This is needed for // incremental reflow @@ -4519,7 +4574,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, CombineRects(aState.mFloaterCombinedArea, lineCombinedArea); if (aState.mHaveRightFloaters && - (aState.mUnconstrainedWidth || aState.mShrinkWrapWidth)) { + (aState.GetFlag(BRS_UNCONSTRAINEDWIDTH) || aState.GetFlag(BRS_SHRINKWRAPWIDTH))) { // We are reflowing in an unconstrained situation or shrink wrapping and // have some right floaters. They were placed at the infinite right edge // which will cause the combined area to be unusable. @@ -4540,11 +4595,11 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, aState.mRightFloaterCombinedArea.x = aLine->mBounds.XMost(); CombineRects(aState.mRightFloaterCombinedArea, lineCombinedArea); - if (aState.mShrinkWrapWidth) { + if (aState.GetFlag(BRS_SHRINKWRAPWIDTH)) { // Mark the line dirty so we come back and re-place the floater once // the shrink wrap width is determined aLine->MarkDirty(); - aState.mNeedResizeReflow = PR_TRUE; + aState.SetFlag(BRS_NEEDRESIZEREFLOW, PR_TRUE); } } aLine->SetCombinedArea(lineCombinedArea); @@ -4629,7 +4684,7 @@ nsBlockFrame::PostPlaceLine(nsBlockReflowState& aState, } // Update max-element-size - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { aState.UpdateMaxElementSize(aMaxElementSize); // We also cache the max element width in the line. This is needed for // incremental reflow @@ -4639,7 +4694,7 @@ nsBlockFrame::PostPlaceLine(nsBlockReflowState& aState, // If this is an unconstrained reflow, then cache the line width in the // line. We'll need this during incremental reflow if we're asked to // calculate the maximum width - if (aState.mUnconstrainedWidth) { + if (aState.GetFlag(BRS_UNCONSTRAINEDWIDTH)) { aLine->mMaximumWidth = aLine->mBounds.XMost(); } @@ -4653,7 +4708,7 @@ nsBlockFrame::PostPlaceLine(nsBlockReflowState& aState, #endif // If we're shrink wrapping our width and the line was wrapped, // then make sure we take up all of the available width - if (aState.mShrinkWrapWidth && aLine->IsLineWrapped()) { + if (aState.GetFlag(BRS_SHRINKWRAPWIDTH) && aLine->IsLineWrapped()) { aState.mKidXMost = aState.BorderPadding().left + aState.mContentArea.width; } @@ -5187,6 +5242,10 @@ nsBlockFrame::DoRemoveFrame(nsIPresContext* aPresContext, // cases... nsRect lineCombinedArea; line->GetCombinedArea(&lineCombinedArea); +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 10 (%d, %d, %d, %d)\n", + this, lineCombinedArea.x, lineCombinedArea.y, lineCombinedArea.width, lineCombinedArea.height); +#endif Invalidate(aPresContext, lineCombinedArea); line->Destroy(presShell); line = next; @@ -5282,8 +5341,8 @@ nsBlockFrame::ReflowFloater(nsBlockReflowState& aState, // Setup block reflow state to reflow the floater nsBlockReflowContext brc(aState.mPresContext, aState.mReflowState, - aState.mComputeMaxElementSize, - aState.mComputeMaximumWidth); + aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE), + aState.GetFlag(BRS_COMPUTEMAXWIDTH)); brc.SetNextRCFrame(aState.mNextRCFrame); // Reflow the floater @@ -5324,7 +5383,7 @@ nsBlockFrame::ReflowFloater(nsBlockReflowState& aState, floater->DidReflow(aState.mPresContext, NS_FRAME_REFLOW_FINISHED); // If we computed it, then stash away the max-element-size for later - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { aState.StoreMaxElementSize(floater, brc.GetMaxElementSize()); } @@ -5389,7 +5448,7 @@ nsBlockReflowState::AddFloater(nsLineLayout& aLineLayout, // Pass on updated available space to the current inline reflow engine GetAvailableSpace(); aLineLayout.UpdateBand(mAvailSpaceRect.x + BorderPadding().left, mY, - mUnconstrainedWidth ? NS_UNCONSTRAINEDSIZE : mAvailSpaceRect.width, + GetFlag(BRS_UNCONSTRAINEDWIDTH) ? NS_UNCONSTRAINEDSIZE : mAvailSpaceRect.width, mAvailSpaceRect.height, isLeftFloater, aPlaceholder->GetOutOfFlowFrame()); @@ -5615,7 +5674,12 @@ nsBlockReflowState::PlaceFloater(nsFloaterCache* aFloaterCache, } else { isLeftFloater = PR_FALSE; - region.x = mAvailSpaceRect.XMost() - region.width; + if (NS_UNCONSTRAINEDSIZE != mAvailSpaceRect.XMost()) + region.x = mAvailSpaceRect.XMost() - region.width; + else { + okToAddRectRegion = PR_FALSE; + region.x = mAvailSpaceRect.x; + } } *aIsLeftFloater = isLeftFloater; const nsMargin& borderPadding = BorderPadding(); @@ -5682,7 +5746,8 @@ nsBlockReflowState::PlaceFloater(nsFloaterCache* aFloaterCache, nsRect combinedArea = aFloaterCache->mCombinedArea; combinedArea.x += x; combinedArea.y += y; - if (!isLeftFloater && (mUnconstrainedWidth || mShrinkWrapWidth)) { + if (!isLeftFloater && + (GetFlag(BRS_UNCONSTRAINEDWIDTH) || GetFlag(BRS_SHRINKWRAPWIDTH))) { // When we are placing a right floater in an unconstrained situation or // when shrink wrapping, we don't apply it to the floater combined area // immediately. Otherwise we end up with an infinitely wide combined diff --git a/layout/generic/nsImageFrame.cpp b/layout/generic/nsImageFrame.cpp index b480a8105da8..2a1cfca6b5c4 100644 --- a/layout/generic/nsImageFrame.cpp +++ b/layout/generic/nsImageFrame.cpp @@ -879,6 +879,13 @@ nsImageFrame::AttributeChanged(nsIPresContext* aPresContext, } } } + else if (nsHTMLAtoms::width == aAttribute || nsHTMLAtoms::height == aAttribute) + { // XXX: could check for new width == old width, and make that a no-op + nsCOMPtr presShell; + aPresContext->GetShell(getter_AddRefs(presShell)); + mState |= NS_FRAME_IS_DIRTY; + mParent->ReflowDirtyChild(presShell, (nsIFrame*) this); + } return NS_OK; } diff --git a/layout/generic/nsLineLayout.cpp b/layout/generic/nsLineLayout.cpp index 6f5e6816e708..6a78a5c7f756 100644 --- a/layout/generic/nsLineLayout.cpp +++ b/layout/generic/nsLineLayout.cpp @@ -18,8 +18,10 @@ * Rights Reserved. * * Contributor(s): + * Steve Clark * Pierre Phaneuf * L. David Baron + * Robert O'Callahan */ #include "nsCOMPtr.h" #include "nsLineLayout.h" @@ -41,13 +43,14 @@ #include "nsIView.h" #include "nsIViewManager.h" #include "nsHTMLAtoms.h" +#include "nsTextFragment.h" #ifdef DEBUG #undef NOISY_HORIZONTAL_ALIGN #undef NOISY_VERTICAL_ALIGN #undef REALLY_NOISY_VERTICAL_ALIGN #undef NOISY_REFLOW -#undef REALLY_NOISY_REFLOW +#undef REALLY_NOISY_REFLOW #undef NOISY_PUSHING #undef REALLY_NOISY_PUSHING #undef DEBUG_ADD_TEXT @@ -120,15 +123,9 @@ nsLineLayout::nsLineLayout(nsIPresContext* aPresContext, mTextAlign = mStyleText->mTextAlign; mLineNumber = 0; mColumn = 0; - mEndsInWhiteSpace = PR_TRUE; - mUnderstandsWhiteSpace = PR_FALSE; - mTextStartsWithNBSP = PR_FALSE; - mFirstLetterStyleOK = PR_FALSE; - mIsTopOfPage = PR_FALSE; - mUpdatedBand = PR_FALSE; + mFlags = 0; // default all flags to false except those that follow here... + SetFlag(LL_ENDSINWHITESPACE, PR_TRUE); mPlacedFloaters = 0; - mImpactedByFloaters = PR_FALSE; - mLastFloaterWasLetterFrame = PR_FALSE; mTotalPlacedFrames = 0; mTopEdge = mBottomEdge = 0; mReflowTextRuns = nsnull; @@ -148,14 +145,17 @@ nsLineLayout::nsLineLayout(nsIPresContext* aPresContext, mTextRuns = nsnull; mTextRunP = &mTextRuns; mNewTextRun = nsnull; - mKnowStrictMode = PR_FALSE; + SetFlag(LL_KNOWSTRICTMODE, PR_FALSE); + PRInt32 size = sizeof nsLineLayout; + PRInt32 size_pfd = sizeof PerFrameData; + PRInt32 size_psd = sizeof PerSpanData; } nsLineLayout::nsLineLayout(nsIPresContext* aPresContext) : mPresContext(aPresContext) { MOZ_COUNT_CTOR(nsLineLayout); - + mTextRuns = nsnull; mTextRunP = &mTextRuns; mNewTextRun = nsnull; @@ -197,9 +197,9 @@ nsLineLayout::~nsLineLayout() PRBool nsLineLayout::InStrictMode() { - if (!mKnowStrictMode) { - mKnowStrictMode = PR_TRUE; - mInStrictMode = PR_TRUE; + if (!GetFlag(LL_KNOWSTRICTMODE)) { + SetFlag(LL_KNOWSTRICTMODE, PR_TRUE); + SetFlag(LL_INSTRICTMODE, PR_TRUE); // Get the compatabilty mode from pres context via the document and pres shell if (mBlockReflowState->frame) { @@ -217,7 +217,7 @@ nsLineLayout::InStrictMode() nsCompatibility mode; presContext->GetCompatibilityMode(&mode); if (eCompatibility_NavQuirks == mode) { - mInStrictMode = PR_FALSE; + SetFlag(LL_INSTRICTMODE, PR_FALSE); } } NS_RELEASE(shell); @@ -226,7 +226,7 @@ nsLineLayout::InStrictMode() } } } - return mInStrictMode; + return GetFlag(LL_INSTRICTMODE); } void @@ -235,10 +235,6 @@ nsLineLayout::BeginLineReflow(nscoord aX, nscoord aY, PRBool aImpactedByFloaters, PRBool aIsTopOfPage) { -#ifdef REALLY_NOISY_REFLOW - printf("nsLL::BeginLineReflow %d, %d, %d, %d, impacted=%s\n", - aX, aY, aWidth, aHeight, aImpactedByFloaters?"true":"false"); -#endif NS_ASSERTION(nsnull == mRootSpan, "bad linelayout user"); #ifdef DEBUG if ((aWidth != NS_UNCONSTRAINEDSIZE) && CRAZY_WIDTH(aWidth)) { @@ -256,8 +252,9 @@ nsLineLayout::BeginLineReflow(nscoord aX, nscoord aY, #endif #ifdef NOISY_REFLOW nsFrame::ListTag(stdout, mBlockReflowState->frame); - printf(": BeginLineReflow: %d,%d,%d,%d %s\n", + printf(": BeginLineReflow: %d,%d,%d,%d impacted=%s %s\n", aX, aY, aWidth, aHeight, + aImpactedByFloaters?"true":"false", aIsTopOfPage ? "top-of-page" : ""); #endif #ifdef DEBUG @@ -265,17 +262,18 @@ nsLineLayout::BeginLineReflow(nscoord aX, nscoord aY, #endif mColumn = 0; - mEndsInWhiteSpace = PR_TRUE; - mUnderstandsWhiteSpace = PR_FALSE; - mTextStartsWithNBSP = PR_FALSE; - mFirstLetterStyleOK = PR_FALSE; - mIsTopOfPage = aIsTopOfPage; - mUpdatedBand = PR_FALSE; + + SetFlag(LL_ENDSINWHITESPACE, PR_TRUE); + SetFlag(LL_UNDERSTANDSNWHITESPACE, PR_FALSE); + SetFlag(LL_TEXTSTARTSWITHNBSP, PR_FALSE); + SetFlag(LL_FIRSTLETTERSTYLEOK, PR_FALSE); + SetFlag(LL_ISTOPOFPAGE, aIsTopOfPage); + SetFlag(LL_UPDATEDBAND, PR_FALSE); mPlacedFloaters = 0; - mImpactedByFloaters = aImpactedByFloaters; + SetFlag(LL_IMPACTEDBYFLOATERS, aImpactedByFloaters); mTotalPlacedFrames = 0; - mCanPlaceFloater = PR_TRUE; - mLineEndsInBR = PR_FALSE; + SetFlag(LL_CANPLACEFLOATER, PR_TRUE); + SetFlag(LL_LINEENDSINBR, PR_FALSE); mSpanDepth = 0; mMaxTopBoxHeight = mMaxBottomBoxHeight = 0; @@ -355,7 +353,7 @@ nsLineLayout::UpdateBand(nscoord aX, nscoord aY, nsIFrame* aFloaterFrame) { #ifdef REALLY_NOISY_REFLOW - printf("nsLL::UpdateBand %d, %d, %d, %d, frame=%p placedLeft=%s\n will set mImpacted to PR_TRUE", + printf("nsLL::UpdateBand %d, %d, %d, %d, frame=%p placedLeft=%s\n will set mImpacted to PR_TRUE\n", aX, aY, aWidth, aHeight, aFloaterFrame, aPlacedLeftFloater?"true":"false"); #endif PerSpanData* psd = mRootSpan; @@ -404,13 +402,13 @@ nsLineLayout::UpdateBand(nscoord aX, nscoord aY, else { mBottomEdge = aY + aHeight; } - mUpdatedBand = PR_TRUE; + SetFlag(LL_UPDATEDBAND, PR_TRUE); mPlacedFloaters |= (aPlacedLeftFloater ? PLACED_LEFT : PLACED_RIGHT); - mImpactedByFloaters = PR_TRUE; + SetFlag(LL_IMPACTEDBYFLOATERS, PR_TRUE); nsCOMPtr frameType; aFloaterFrame->GetFrameType(getter_AddRefs(frameType)); - mLastFloaterWasLetterFrame = nsLayoutAtoms::letterFrame == frameType.get(); + SetFlag(LL_LASTFLOATERWASLETTERFRAME, (nsLayoutAtoms::letterFrame == frameType.get())); // Now update all of the open spans... mRootSpan->mContainsFloater = PR_TRUE; // make sure mRootSpan gets updated too @@ -762,17 +760,10 @@ nsLineLayout::NewPerFrameData(PerFrameData** aResult) pfd->mNext = nsnull; pfd->mPrev = nsnull; pfd->mFrame = nsnull; - pfd->mRelativePos = PR_FALSE; - pfd->mIsTextFrame = PR_FALSE; - pfd->mIsNonEmptyTextFrame = PR_FALSE; - pfd->mIsNonWhitespaceTextFrame = PR_FALSE; - pfd->mIsLetterFrame = PR_FALSE; - pfd->mIsSticky = PR_FALSE; - + pfd->mFlags = 0; // all flags default to false #ifdef DEBUG pfd->mVerticalAlign = 0xFF; - pfd->mRelativePos = PRBool(0xFF); mFramesAllocated++; #endif *aResult = pfd; @@ -782,7 +773,7 @@ nsLineLayout::NewPerFrameData(PerFrameData** aResult) PRBool nsLineLayout::CanPlaceFloaterNow() const { - return mCanPlaceFloater; + return GetFlag(LL_CANPLACEFLOATER); } PRBool @@ -794,7 +785,7 @@ nsLineLayout::LineIsEmpty() const PRBool nsLineLayout::LineIsBreakable() const { - if ((0 != mTotalPlacedFrames) || mImpactedByFloaters) { + if ((0 != mTotalPlacedFrames) || GetFlag(LL_IMPACTEDBYFLOATERS)) { return PR_TRUE; } return PR_FALSE; @@ -903,9 +894,11 @@ nsLineLayout::ReflowFrame(nsIFrame* aFrame, nsHTMLReflowState reflowState(mPresContext, *psd->mReflowState, aFrame, availSize, reason); reflowState.mLineLayout = this; - reflowState.isTopOfPage = mIsTopOfPage; - mUnderstandsWhiteSpace = PR_FALSE; - mTextStartsWithNBSP = PR_FALSE; + reflowState.isTopOfPage = GetFlag(LL_ISTOPOFPAGE); + SetFlag(LL_UNDERSTANDSNWHITESPACE, PR_FALSE); + SetFlag(LL_TEXTSTARTSWITHNBSP, PR_FALSE); + mTextJustificationNumSpaces = 0; + mTextJustificationNumLetters = 0; // Stash copies of some of the computed state away for later // (vertical alignment, for example) @@ -913,9 +906,9 @@ nsLineLayout::ReflowFrame(nsIFrame* aFrame, pfd->mMargin = reflowState.mComputedMargin; pfd->mBorderPadding = reflowState.mComputedBorderPadding; pfd->mFrameType = reflowState.mFrameType; - pfd->mRelativePos = - reflowState.mStylePosition->mPosition == NS_STYLE_POSITION_RELATIVE; - if (pfd->mRelativePos) { + pfd->SetFlag(PFD_RELATIVEPOS, + (reflowState.mStylePosition->mPosition == NS_STYLE_POSITION_RELATIVE)); + if (pfd->GetFlag(PFD_RELATIVEPOS)) { pfd->mOffsets = reflowState.mComputedOffsets; } @@ -969,14 +962,18 @@ nsLineLayout::ReflowFrame(nsIFrame* aFrame, nscoord ty = y - psd->mReflowState->mComputedBorderPadding.top; mSpaceManager->Translate(tx, ty); - pfd->mIsTextFrame = PR_FALSE; - pfd->mIsLetterFrame = PR_FALSE; - pfd->mIsNonEmptyTextFrame = PR_FALSE; - pfd->mIsNonWhitespaceTextFrame = PR_FALSE; - pfd->mIsSticky = PR_FALSE; + pfd->SetFlag(PFD_ISTEXTFRAME, PR_FALSE); + pfd->SetFlag(PFD_ISLETTERFRAME, PR_FALSE); + pfd->SetFlag(PFD_ISNONEMPTYTEXTFRAME, PR_FALSE); + pfd->SetFlag(PFD_ISNONWHITESPACETEXTFRAME, PR_FALSE); + pfd->SetFlag(PFD_ISSTICKY, PR_FALSE); + pfd->SetFlag(PFD_ISBULLET, PR_FALSE); aFrame->Reflow(mPresContext, metrics, reflowState, aReflowStatus); + pfd->mJustificationNumSpaces = mTextJustificationNumSpaces; + pfd->mJustificationNumLetters = mTextJustificationNumLetters; + // XXX See if the frame is a placeholderFrame and if it is process // the floater. nsIAtom* frameType; @@ -1000,7 +997,7 @@ nsLineLayout::ReflowFrame(nsIFrame* aFrame, outOfFlowFrame->GetFrameType(&oofft); if (oofft) { if (oofft == nsLayoutAtoms::letterFrame) { - mFirstLetterStyleOK = PR_FALSE; + SetFlag(LL_FIRSTLETTERSTYLEOK, PR_FALSE); } NS_RELEASE(oofft); } @@ -1009,11 +1006,11 @@ nsLineLayout::ReflowFrame(nsIFrame* aFrame, } else if (frameType == nsLayoutAtoms::textFrame) { // Note non-empty text-frames for inline frame compatability hackery - pfd->mIsTextFrame = PR_TRUE; + pfd->SetFlag(PFD_ISTEXTFRAME, PR_TRUE); // XXX An empty text frame at the end of the line seems not // to have zero width. if (metrics.width) { - pfd->mIsNonEmptyTextFrame = PR_TRUE; + pfd->SetFlag(PFD_ISNONEMPTYTEXTFRAME, PR_TRUE); nsCOMPtr content; nsresult result = pfd->mFrame->GetContent(getter_AddRefs(content)); if ((NS_SUCCEEDED(result)) && content) { @@ -1023,14 +1020,14 @@ nsLineLayout::ReflowFrame(nsIFrame* aFrame, PRBool isWhitespace; result = textContent->IsOnlyWhitespace(&isWhitespace); if (NS_SUCCEEDED(result)) { - pfd->mIsNonWhitespaceTextFrame = !isWhitespace; + pfd->SetFlag(PFD_ISNONWHITESPACETEXTFRAME, !isWhitespace); } } } } } else if (frameType == nsLayoutAtoms::letterFrame) { - pfd->mIsLetterFrame = PR_TRUE; + pfd->SetFlag(PFD_ISLETTERFRAME, PR_TRUE); } NS_RELEASE(frameType); } @@ -1170,7 +1167,7 @@ nsLineLayout::ReflowFrame(nsIFrame* aFrame, else { PushFrame(aFrame); } - mTextStartsWithNBSP = PR_FALSE; // reset for next time + SetFlag(LL_TEXTSTARTSWITHNBSP, PR_FALSE); // reset for next time #ifdef REALLY_NOISY_REFLOW nsFrame::IndentBy(stdout, mSpanDepth); @@ -1330,7 +1327,7 @@ nsLineLayout::CanPlaceFrame(PerFrameData* pfd, // There are no frames on the line or we are in the first word on // the line. If the line isn't impacted by a floater then the // current frame fits. - if (!mImpactedByFloaters) { + if (!GetFlag(LL_IMPACTEDBYFLOATERS)) { #ifdef NOISY_CAN_PLACE_FRAME printf(" ==> not-safe and not-impacted fits: "); while (nsnull != psd) { @@ -1341,28 +1338,28 @@ nsLineLayout::CanPlaceFrame(PerFrameData* pfd, #endif return PR_TRUE; } - else if (mLastFloaterWasLetterFrame) { + else if (GetFlag(LL_LASTFLOATERWASLETTERFRAME)) { // Another special case: see if the floater is a letter // frame. If it is, then allow the frame next to it to fit. - if (pfd->mIsNonEmptyTextFrame) { + if (pfd->GetFlag(PFD_ISNONEMPTYTEXTFRAME)) { // This must be the first piece of non-empty text (because // aNotSafeToBreak is true) or its a piece of text that is // part of a larger word. - pfd->mIsSticky = PR_TRUE; + pfd->SetFlag(PFD_ISSTICKY, PR_TRUE); } else if (pfd->mSpan) { PerFrameData* pf = pfd->mSpan->mFirstFrame; while (pf) { - if (pf->mIsSticky) { + if (pf->GetFlag(PFD_ISSTICKY)) { // If one of the spans children was sticky then the span // itself is sticky. - pfd->mIsSticky = PR_TRUE; + pfd->SetFlag(PFD_ISSTICKY, PR_TRUE); } pf = pf->mNext; } } - if (pfd->mIsSticky) { + if (pfd->GetFlag(PFD_ISSTICKY)) { #ifdef NOISY_CAN_PLACE_FRAME printf(" ==> last floater was letter frame && frame is sticky\n"); #endif @@ -1372,8 +1369,8 @@ nsLineLayout::CanPlaceFrame(PerFrameData* pfd, } // If this is a piece of text inside a letter frame... - if (pfd->mIsNonEmptyTextFrame) { - if (psd->mFrame && psd->mFrame->mIsLetterFrame) { + if (pfd->GetFlag(PFD_ISNONEMPTYTEXTFRAME)) { + if (psd->mFrame && psd->mFrame->GetFlag(PFD_ISLETTERFRAME)) { nsIFrame* prevInFlow; psd->mFrame->mFrame->GetPrevInFlow(&prevInFlow); if (prevInFlow) { @@ -1387,7 +1384,7 @@ nsLineLayout::CanPlaceFrame(PerFrameData* pfd, } } } - else if (pfd->mIsLetterFrame) { + else if (pfd->GetFlag(PFD_ISLETTERFRAME)) { // If this is the first continuation of the letter frame... nsIFrame* prevInFlow; pfd->mFrame->GetPrevInFlow(&prevInFlow); @@ -1433,7 +1430,7 @@ nsLineLayout::CanPlaceFrame(PerFrameData* pfd, // edge...Which means that whatever piece of text we just formatted // will be the piece that fits (the text frame logic knows to stop // when it runs out of room). - if (pfd->mIsNonEmptyTextFrame && mTextStartsWithNBSP) { + if (pfd->GetFlag(PFD_ISNONEMPTYTEXTFRAME) && GetFlag(LL_TEXTSTARTSWITHNBSP)) { return PR_TRUE; } @@ -1467,9 +1464,9 @@ nsLineLayout::PlaceFrame(PerFrameData* pfd, nsHTMLReflowMetrics& aMetrics) // If the band was updated during the reflow of that frame then we // need to adjust any prior frames that were reflowed. - if (mUpdatedBand && InBlockContext()) { + if (GetFlag(LL_UPDATEDBAND) && InBlockContext()) { UpdateFrames(); - mUpdatedBand = PR_FALSE; + SetFlag(LL_UPDATEDBAND, PR_FALSE); } // Advance to next X coordinate @@ -1478,8 +1475,8 @@ nsLineLayout::PlaceFrame(PerFrameData* pfd, nsHTMLReflowMetrics& aMetrics) // If the frame is a not aware of white-space and it takes up some // width, disable leading white-space compression for the next frame // to be reflowed. - if (!mUnderstandsWhiteSpace && pfd->mBounds.width) { - mEndsInWhiteSpace = PR_FALSE; + if ((!GetFlag(LL_UNDERSTANDSNWHITESPACE)) && pfd->mBounds.width) { + SetFlag(LL_ENDSINWHITESPACE, PR_FALSE); } // Count the number of frames on the line... @@ -1487,7 +1484,7 @@ nsLineLayout::PlaceFrame(PerFrameData* pfd, nsHTMLReflowMetrics& aMetrics) if (psd->mX != psd->mLeftEdge) { // As soon as a frame placed on the line advances an X coordinate // of any span we can no longer place a floater on the line. - mCanPlaceFloater = PR_FALSE; + SetFlag(LL_CANPLACEFLOATER, PR_FALSE); } } @@ -1505,14 +1502,10 @@ nsLineLayout::AddBulletFrame(nsIFrame* aFrame, pfd->mMargin.SizeTo(0, 0, 0, 0); pfd->mBorderPadding.SizeTo(0, 0, 0, 0); pfd->mFrameType = NS_CSS_FRAME_TYPE_INLINE|NS_FRAME_REPLACED_ELEMENT; - pfd->mRelativePos = PR_FALSE; + pfd->mFlags = 0; // all flags default to false + pfd->SetFlag(PFD_ISBULLET, PR_TRUE); pfd->mAscent = aMetrics.ascent; pfd->mDescent = aMetrics.descent; - pfd->mIsTextFrame = PR_FALSE; - pfd->mIsNonEmptyTextFrame = PR_FALSE; - pfd->mIsNonWhitespaceTextFrame = PR_FALSE; - pfd->mIsLetterFrame = PR_FALSE; - pfd->mIsSticky = PR_FALSE; // Note: y value will be updated during vertical alignment aFrame->GetRect(pfd->mBounds); @@ -1857,7 +1850,7 @@ nsLineLayout::VerticalAlignFrames(PerSpanData* psd) zeroEffectiveSpanBox = PR_TRUE; PerFrameData* pfd = psd->mFirstFrame; while (nsnull != pfd) { - if (preMode?pfd->mIsTextFrame:pfd->mIsNonWhitespaceTextFrame) { + if (preMode?pfd->GetFlag(PFD_ISTEXTFRAME):pfd->GetFlag(PFD_ISNONWHITESPACETEXTFRAME)) { zeroEffectiveSpanBox = PR_FALSE; break; } @@ -2154,7 +2147,7 @@ nsLineLayout::VerticalAlignFrames(PerSpanData* psd) if (pfd->mVerticalAlign == VALIGN_OTHER) { // Text frames do not contribute to the min/max Y values for the // line (instead their parent frame's font-size contributes). - if (!pfd->mIsTextFrame) { + if (!pfd->GetFlag(PFD_ISTEXTFRAME)) { nscoord yTop, yBottom; if (frameSpan) { // For spans that were are now placing, use their position @@ -2212,7 +2205,7 @@ nsLineLayout::VerticalAlignFrames(PerSpanData* psd) // BR) (NN4/IE5 quirk) PRBool applyMinLH = !(psd->mZeroEffectiveSpanBox); // (1) above PRBool isFirstLine = !mLineNumber; // if the line number is 0 - PRBool isLastLine = (!mLineBox->IsLineWrapped() && !mLineEndsInBR); + PRBool isLastLine = (!mLineBox->IsLineWrapped() && !GetFlag(LL_LINEENDSINBR)); //PRBool isLastLine = mBlockRS->mCurLine->IsLineWrapped(); if (!applyMinLH && (isFirstLine || isLastLine)) { nsCOMPtr blockContent; @@ -2412,13 +2405,13 @@ nsLineLayout::TrimTrailingWhiteSpaceIn(PerSpanData* psd, return PR_TRUE; } } - else if (!pfd->mIsTextFrame) { + else if (!pfd->GetFlag(PFD_ISTEXTFRAME)) { // If we hit a frame on the end that's not text, then there is // no trailing whitespace to trim. Stop the search. *aDeltaWidth = 0; return PR_TRUE; } - else if (pfd->mIsNonEmptyTextFrame) { + else if (pfd->GetFlag(PFD_ISNONEMPTYTEXTFRAME)) { nscoord deltaWidth = 0; pfd->mFrame->TrimTrailingWhiteSpace(mPresContext, *mBlockReflowState->rendContext, @@ -2432,6 +2425,10 @@ nsLineLayout::TrimTrailingWhiteSpaceIn(PerSpanData* psd, printf(" returned %d\n", deltaWidth); #endif if (deltaWidth) { + if (pfd->mJustificationNumSpaces > 0) { + pfd->mJustificationNumSpaces--; + } + pfd->mBounds.width -= deltaWidth; pfd->mCombinedArea.width -= deltaWidth; if (0 == pfd->mBounds.width) { @@ -2479,6 +2476,95 @@ nsLineLayout::TrimTrailingWhiteSpace() return 0 != deltaWidth; } +void +nsLineLayout::ComputeJustificationWeights(PerSpanData* aPSD, + PRInt32* aNumSpaces, + PRInt32* aNumLetters) +{ + NS_ASSERTION(aPSD, "null arg"); + NS_ASSERTION(aNumSpaces, "null arg"); + NS_ASSERTION(aNumLetters, "null arg"); + PRInt32 numSpaces = 0; + PRInt32 numLetters = 0; + + for (PerFrameData* pfd = aPSD->mFirstFrame; pfd != nsnull; pfd = pfd->mNext) { + nscoord dw = 0; + + if (PR_TRUE == pfd->GetFlag(PFD_ISTEXTFRAME)) { + numSpaces += pfd->mJustificationNumSpaces; + numLetters += pfd->mJustificationNumLetters; + } + else if (pfd->mSpan != nsnull) { + PRInt32 spanSpaces; + PRInt32 spanLetters; + + ComputeJustificationWeights(pfd->mSpan, &spanSpaces, &spanLetters); + + numSpaces += spanSpaces; + numLetters += spanLetters; + } + } + + *aNumSpaces = numSpaces; + *aNumLetters = numLetters; +} + +nscoord +nsLineLayout::ApplyFrameJustification(PerSpanData* aPSD, FrameJustificationState* aState) +{ + NS_ASSERTION(aPSD, "null arg"); + NS_ASSERTION(aState, "null arg"); + + nscoord deltaX = 0; + for (PerFrameData* pfd = aPSD->mFirstFrame; pfd != nsnull; pfd = pfd->mNext) { + // Don't reposition bullets (and other frames that occur out of X-order?) + if (!pfd->GetFlag(PFD_ISBULLET)) { + nscoord dw = 0; + + pfd->mBounds.x += deltaX; + + if (PR_TRUE == pfd->GetFlag(PFD_ISTEXTFRAME)) { + if (aState->mTotalWidthForSpaces > 0 && + aState->mTotalNumSpaces > 0 && // we divide by this value, so must be non-zero + aState->mTotalNumLetters >0 // we divide by this value, so must be non-zero + ) { + aState->mNumSpacesProcessed += pfd->mJustificationNumSpaces; + + nscoord newAllocatedWidthForSpaces = + (aState->mTotalWidthForSpaces*aState->mNumSpacesProcessed) + /aState->mTotalNumSpaces; + + dw += newAllocatedWidthForSpaces - aState->mWidthForSpacesProcessed; + + aState->mWidthForSpacesProcessed = newAllocatedWidthForSpaces; + } + + if (aState->mTotalWidthForLetters > 0) { + aState->mNumLettersProcessed += pfd->mJustificationNumLetters; + + nscoord newAllocatedWidthForLetters = + (aState->mTotalWidthForLetters*aState->mNumLettersProcessed) + /aState->mTotalNumLetters; + + dw += newAllocatedWidthForLetters - aState->mWidthForLettersProcessed; + + aState->mWidthForLettersProcessed = newAllocatedWidthForLetters; + } + } + else { + if (nsnull != pfd->mSpan) { + dw += ApplyFrameJustification(pfd->mSpan, aState); + } + } + + pfd->mBounds.width += dw; + deltaX += dw; + pfd->mFrame->SetRect(mPresContext, pfd->mBounds); + } + } + return deltaX; +} + PRBool nsLineLayout::HorizontalAlignFrames(nsRect& aLineBounds, PRBool aAllowJustify, @@ -2525,7 +2611,18 @@ nsLineLayout::HorizontalAlignFrames(nsRect& aLineBounds, // frames in the line. If it is the last line then if the // direction is right-to-left then we right-align the frames. if (aAllowJustify) { - break; + if (!aShrinkWrapWidth) { + PRInt32 numSpaces; + PRInt32 numLetters; + + ComputeJustificationWeights(psd, &numSpaces, &numLetters); + + if (numSpaces > 0) { + FrameJustificationState state = { numSpaces, numLetters, remainingWidth, 0, 0, 0, 0, 0 }; + + ApplyFrameJustification(psd, &state); + } + } } else if (NS_STYLE_DIRECTION_RTL == psd->mDirection) { // right align the frames @@ -2617,7 +2714,7 @@ nsLineLayout::RelativePositionFrames(PerSpanData* psd, nsRect& aCombinedArea) nscoord y = pfd->mBounds.y; // Adjust the origin of the frame - if (pfd->mRelativePos) { + if (pfd->GetFlag(PFD_RELATIVEPOS)) { nsIFrame* frame = pfd->mFrame; frame->GetOrigin(origin); // XXX what about right and bottom? diff --git a/layout/generic/nsLineLayout.h b/layout/generic/nsLineLayout.h index 2cb644817ba5..4bde711f609b 100644 --- a/layout/generic/nsLineLayout.h +++ b/layout/generic/nsLineLayout.h @@ -18,6 +18,8 @@ * Rights Reserved. * * Contributor(s): + * Steve Clark + * Robert O'Callahan */ #ifndef nsLineLayout_h___ #define nsLineLayout_h___ @@ -32,7 +34,7 @@ class nsBlockReflowState; class nsPlaceholderFrame; struct nsStyleText; -#define NS_LINELAYOUT_NUM_FRAMES 10 +#define NS_LINELAYOUT_NUM_FRAMES 5 #define NS_LINELAYOUT_NUM_SPANS 5 class nsLineLayout { @@ -122,23 +124,69 @@ public: //---------------------------------------- + // Supporting methods and data for flags +protected: +#define LL_ENDSINWHITESPACE 0x00000001 +#define LL_UNDERSTANDSNWHITESPACE 0x00000002 +#define LL_TEXTSTARTSWITHNBSP 0x00000004 +#define LL_FIRSTLETTERSTYLEOK 0x00000008 +#define LL_ISTOPOFPAGE 0x00000010 +#define LL_UPDATEDBAND 0x00000020 +#define LL_IMPACTEDBYFLOATERS 0x00000040 +#define LL_LASTFLOATERWASLETTERFRAME 0x00000080 +#define LL_CANPLACEFLOATER 0x00000100 +#define LL_KNOWSTRICTMODE 0x00000200 +#define LL_INSTRICTMODE 0x00000400 +#define LL_LINEENDSINBR 0x00000800 +#define LL_LASTFLAG LL_LINEENDSINBR + + PRUint16 mFlags; + + void SetFlag(PRUint32 aFlag, PRBool aValue) + { + NS_ASSERTION(aFlag<=LL_LASTFLAG, "bad flag"); + NS_ASSERTION(aValue==PR_FALSE || aValue==PR_TRUE, "bad value"); + if (aValue) { // set flag + mFlags |= aFlag; + } + else { // unset flag + mFlags &= ~aFlag; + } + } + + PRBool GetFlag(PRUint32 aFlag) const + { + NS_ASSERTION(aFlag<=LL_LASTFLAG, "bad flag"); + PRBool result = (mFlags & aFlag); + if (result) return PR_TRUE; + return PR_FALSE; + } + +public: + // Support methods for white-space compression and word-wrapping // during line reflow void SetEndsInWhiteSpace(PRBool aState) { - mEndsInWhiteSpace = aState; + SetFlag(LL_ENDSINWHITESPACE, aState); } PRBool GetEndsInWhiteSpace() const { - return mEndsInWhiteSpace; + return GetFlag(LL_ENDSINWHITESPACE); } void SetUnderstandsWhiteSpace(PRBool aSetting) { - mUnderstandsWhiteSpace = aSetting; + SetFlag(LL_UNDERSTANDSNWHITESPACE, aSetting); } + void SetTextJustificationWeights(PRInt32 aNumSpaces, PRInt32 aNumLetters) { + mTextJustificationNumSpaces = aNumSpaces; + mTextJustificationNumLetters = aNumLetters; + } + + void SetTextStartsWithNBSP(PRBool aYes) { - mTextStartsWithNBSP = aYes; + SetFlag(LL_TEXTSTARTSWITHNBSP, aYes); } void RecordWordFrame(nsIFrame* aWordFrame) { @@ -163,9 +211,15 @@ public: PRBool LineIsBreakable() const; - PRBool GetLineEndsInBR() const { return mLineEndsInBR; } + PRBool GetLineEndsInBR() const + { + return GetFlag(LL_LINEENDSINBR); + } - void SetLineEndsInBR(PRBool aOn) { mLineEndsInBR = aOn; } + void SetLineEndsInBR(PRBool aOn) + { + SetFlag(LL_LINEENDSINBR, aOn); + } //---------------------------------------- // Inform the line-layout about the presence of a floating frame @@ -176,11 +230,11 @@ public: //---------------------------------------- PRBool GetFirstLetterStyleOK() const { - return mFirstLetterStyleOK; + return GetFlag(LL_FIRSTLETTERSTYLEOK); } void SetFirstLetterStyleOK(PRBool aSetting) { - mFirstLetterStyleOK = aSetting; + SetFlag(LL_FIRSTLETTERSTYLEOK, aSetting); } void SetFirstLetterFrame(nsIFrame* aFrame) { @@ -233,19 +287,11 @@ protected: nsIFrame* mFirstLetterFrame; PRInt32 mLineNumber; PRInt32 mColumn; + PRInt32 mTextJustificationNumSpaces; + PRInt32 mTextJustificationNumLetters; + nsLineBox* mLineBox; - PRPackedBool mEndsInWhiteSpace; - PRPackedBool mUnderstandsWhiteSpace; - PRPackedBool mTextStartsWithNBSP; - PRPackedBool mFirstLetterStyleOK; - PRPackedBool mIsTopOfPage; - PRPackedBool mUpdatedBand; - PRPackedBool mImpactedByFloaters; - PRPackedBool mLastFloaterWasLetterFrame; - PRPackedBool mCanPlaceFloater; - PRPackedBool mKnowStrictMode; - PRPackedBool mInStrictMode; - PRPackedBool mLineEndsInBR; + PRUint8 mPlacedFloaters; PRInt32 mTotalPlacedFrames; nsVoidArray mWordFrames; @@ -294,15 +340,47 @@ protected: nsMargin mMargin; nsMargin mBorderPadding; nsMargin mOffsets; - PRPackedBool mRelativePos; // Other state we use PRUint8 mVerticalAlign; - PRPackedBool mIsTextFrame; - PRPackedBool mIsNonEmptyTextFrame; - PRPackedBool mIsNonWhitespaceTextFrame; - PRPackedBool mIsLetterFrame; - PRPackedBool mIsSticky; + + // state for text justification + PRInt32 mJustificationNumSpaces; + PRInt32 mJustificationNumLetters; + + +// PerFrameData flags +#define PFD_RELATIVEPOS 0x00000001 +#define PFD_ISTEXTFRAME 0x00000002 +#define PFD_ISNONEMPTYTEXTFRAME 0x00000004 +#define PFD_ISNONWHITESPACETEXTFRAME 0x00000008 +#define PFD_ISLETTERFRAME 0x00000010 +#define PFD_ISSTICKY 0x00000020 +#define PFD_ISBULLET 0x00000040 +#define PFD_LASTFLAG PFD_ISBULLET + + PRPackedBool mFlags; + + void SetFlag(PRUint32 aFlag, PRBool aValue) + { + NS_ASSERTION(aFlag<=PFD_LASTFLAG, "bad flag"); + NS_ASSERTION(aValue==PR_FALSE || aValue==PR_TRUE, "bad value"); + if (aValue) { // set flag + mFlags |= aFlag; + } + else { // unset flag + mFlags &= ~aFlag; + } + } + + PRBool GetFlag(PRUint32 aFlag) const + { + NS_ASSERTION(aFlag<=PFD_LASTFLAG, "bad flag"); + PRBool result = (mFlags & aFlag); + if (result) return PR_TRUE; + return PR_FALSE; + } + PerFrameData* Last() { PerFrameData* pfd = this; @@ -409,6 +487,22 @@ protected: PRBool TrimTrailingWhiteSpaceIn(PerSpanData* psd, nscoord* aDeltaWidth); + + void ComputeJustificationWeights(PerSpanData* psd, PRInt32* numSpaces, PRInt32* numLetters); + + struct FrameJustificationState { + PRInt32 mTotalNumSpaces; + PRInt32 mTotalNumLetters; + nscoord mTotalWidthForSpaces; + nscoord mTotalWidthForLetters; + PRInt32 mNumSpacesProcessed; + PRInt32 mNumLettersProcessed; + nscoord mWidthForSpacesProcessed; + nscoord mWidthForLettersProcessed; + }; + nscoord ApplyFrameJustification(PerSpanData* aPSD, FrameJustificationState* aState); + + #ifdef DEBUG void DumpPerSpanData(PerSpanData* psd, PRInt32 aIndent); #endif diff --git a/layout/generic/nsTextFrame.cpp b/layout/generic/nsTextFrame.cpp index 3b46283c23a5..886c84e3eafb 100644 --- a/layout/generic/nsTextFrame.cpp +++ b/layout/generic/nsTextFrame.cpp @@ -19,6 +19,7 @@ * * Contributor(s): * Pierre Phaneuf + * Robert O'Callahan */ #include "nsCOMPtr.h" #include "nsHTMLParts.h" @@ -458,9 +459,10 @@ public: nscoord mAveCharWidth; PRBool mJustifying; PRBool mPreformatted; - PRIntn mNumSpaces; + PRInt32 mNumSpacesToRender; + PRInt32 mNumSpacesToMeasure; nscoord mExtraSpacePerSpace; - nscoord mRemainingExtraSpace; + PRInt32 mNumSpacesReceivingExtraJot; TextStyle(nsIPresContext* aPresContext, nsIRenderingContext& aRenderingContext, @@ -521,20 +523,25 @@ public: // Get the word and letter spacing mWordSpacing = 0; - mLetterSpacing = 0; PRIntn unit = mText->mWordSpacing.GetUnit(); if (eStyleUnit_Coord == unit) { mWordSpacing = mText->mWordSpacing.GetCoordValue(); } + + mLetterSpacing = 0; unit = mText->mLetterSpacing.GetUnit(); if (eStyleUnit_Coord == unit) { mLetterSpacing = mText->mLetterSpacing.GetCoordValue(); } - mNumSpaces = 0; - mRemainingExtraSpace = 0; + mNumSpacesToRender = 0; + mNumSpacesToMeasure = 0; + mNumSpacesReceivingExtraJot = 0; mExtraSpacePerSpace = 0; mPreformatted = (NS_STYLE_WHITESPACE_PRE == mText->mWhiteSpace) || (NS_STYLE_WHITESPACE_MOZ_PRE_WRAP == mText->mWhiteSpace); + + mJustifying = (NS_STYLE_TEXT_ALIGN_JUSTIFY == mText->mTextAlign) && + !mPreformatted; } ~TextStyle() { @@ -590,6 +597,9 @@ public: nsAutoIndexBuffer* aIndexBuffer, nsAutoTextBuffer* aTextBuffer, PRInt32* aTextLen); + void ComputeExtraJustificationSpacing(nsIRenderingContext& aRenderingContext, + TextStyle& aTextStyle, + PRUnichar* aBuffer, PRInt32 aLength, PRInt32 aNumSpaces); void PaintTextDecorations(nsIRenderingContext& aRenderingContext, nsIStyleContext* aStyleContext, @@ -1192,7 +1202,8 @@ nsTextFrame::Paint(nsIPresContext* aPresContext, sc->GetStyleData(eStyleStruct_Display); if (disp->IsVisible()) { TextStyle ts(aPresContext, aRenderingContext, mStyleContext); - if (ts.mSmallCaps || (0 != ts.mWordSpacing) || (0 != ts.mLetterSpacing)) { + if (ts.mSmallCaps || (0 != ts.mWordSpacing) || (0 != ts.mLetterSpacing) + || ts.mJustifying) { PaintTextSlowly(aPresContext, aRenderingContext, sc, ts, 0, 0); } else { @@ -1261,7 +1272,9 @@ nsTextFrame::PrepareUnicodeText(nsTextTransformer& aTX, PRBool isWhitespace, wasTransformed; PRInt32 wordLen, contentLen; aTX.GetNextWord(PR_FALSE, &wordLen, &contentLen, &isWhitespace, &wasTransformed); - NS_ASSERTION(isWhitespace, "mState and content are out of sync"); + // we trip this assertion in bug 31053, but I think it's unnecessary + //NS_ASSERTION(isWhitespace, "mState and content are out of sync"); + if (isWhitespace) { if (nsnull != indexp) { // Point mapping indicies at the same content index since @@ -1301,7 +1314,6 @@ nsTextFrame::PrepareUnicodeText(nsTextTransformer& aTX, } inWord = PR_FALSE; if (isWhitespace) { - numSpaces++; if ('\t' == bp[0]) { PRInt32 spaces = 8 - (7 & column); PRUnichar* tp = bp; @@ -1339,20 +1351,30 @@ nsTextFrame::PrepareUnicodeText(nsTextTransformer& aTX, } } } + numSpaces += wordLen; } else { + PRInt32 i; if (nsnull != indexp) { // Point mapping indicies at each content index in the word - PRInt32 i = contentLen; + i = contentLen; while (--i >= 0) { *indexp++ = strInx++; } } + // Nonbreaking spaces count as spaces, not letters + PRUnichar* tp = bp; + i = wordLen; + while (--i >= 0) { + if (*tp++ == ' ') { + numSpaces++; + } + } } // Grow the buffer before we run out of room. The only time this // happens is because of tab expansion. - if (dstOffset + wordLen > aTextBuffer->mBufferLen) { + if (aTextBuffer != nsnull && dstOffset + wordLen > aTextBuffer->mBufferLen) { nsresult rv = aTextBuffer->GrowBy(wordLen); if (NS_FAILED(rv)) { break; @@ -1362,8 +1384,10 @@ nsTextFrame::PrepareUnicodeText(nsTextTransformer& aTX, column += wordLen; textLength += wordLen; n -= contentLen; - nsCRT::memcpy(aTextBuffer->mBuffer + dstOffset, bp, - sizeof(PRUnichar)*wordLen); + if (aTextBuffer != nsnull) { + nsCRT::memcpy(aTextBuffer->mBuffer + dstOffset, bp, + sizeof(PRUnichar)*wordLen); + } dstOffset += wordLen; } @@ -1372,19 +1396,24 @@ nsTextFrame::PrepareUnicodeText(nsTextTransformer& aTX, NS_ASSERTION(indexp <= aIndexBuffer->mBuffer + aIndexBuffer->mBufferLen, "yikes - we just overwrote memory"); } - NS_ASSERTION(dstOffset <= aTextBuffer->mBufferLen, - "yikes - we just overwrote memory"); + if (aTextBuffer) { + NS_ASSERTION(dstOffset <= aTextBuffer->mBufferLen, + "yikes - we just overwrote memory"); + } + #endif // Remove trailing whitespace if it was trimmed after reflow if (TEXT_TRIMMED_WS & mState) { + NS_ASSERTION(aTextBuffer != nsnull, + "Nonexistent text buffer should only occur during reflow, i.e. before whitespace is trimmed"); if (--dstOffset >= 0) { PRUnichar ch = aTextBuffer->mBuffer[dstOffset]; if (XP_IS_SPACE(ch)) { textLength--; + numSpaces--; } } - numSpaces--; } if (aIndexBuffer) { @@ -1716,6 +1745,7 @@ nsTextFrame::PaintUnicodeText(nsIPresContext* aPresContext, doc->GetLineBreaker(getter_AddRefs(lb)); nsTextTransformer tx(lb, nsnull); PRInt32 textLength; + // no need to worry about justification, that's always on the slow path PrepareUnicodeText(tx, (displaySelection ? &indexBuffer : nsnull), &paintBuffer, &textLength); @@ -1833,7 +1863,7 @@ nsTextFrame::GetPositionSlowly(nsIPresContext* aPresContext, } TextStyle ts(aPresContext, *aRendContext, mStyleContext); - if (!ts.mSmallCaps && !ts.mWordSpacing && !ts.mLetterSpacing) { + if (!ts.mSmallCaps && !ts.mWordSpacing && !ts.mLetterSpacing && !ts.mJustifying) { return NS_ERROR_INVALID_ARG; } nsIView * view; @@ -1861,12 +1891,16 @@ nsTextFrame::GetPositionSlowly(nsIPresContext* aPresContext, doc->GetLineBreaker(getter_AddRefs(lb)); nsTextTransformer tx(lb, nsnull); PRInt32 textLength; - PrepareUnicodeText(tx, &indexBuffer, &paintBuffer, &textLength); + PRInt32 numSpaces; + + numSpaces = PrepareUnicodeText(tx, &indexBuffer, &paintBuffer, &textLength); if (textLength <= 0) { return NS_ERROR_FAILURE; } -//IF STYLE SAYS TO SELCT TO END OF FRAME HERE... + ComputeExtraJustificationSpacing(*aRendContext, ts, paintBuffer.mBuffer, textLength, numSpaces); + +//IF STYLE SAYS TO SELECT TO END OF FRAME HERE... nsCOMPtr prefs; PRInt32 prefInt = 0; rv = nsServiceManager::GetService(kPrefCID, @@ -1929,7 +1963,7 @@ nsTextFrame::RenderString(nsIRenderingContext& aRenderingContext, PRUnichar* bp = bp0; PRBool spacing = (0 != aTextStyle.mLetterSpacing) || - (0 != aTextStyle.mWordSpacing); + (0 != aTextStyle.mWordSpacing) || aTextStyle.mJustifying; nscoord spacingMem[TEXT_BUF_SIZE]; PRIntn* sp0 = spacingMem; if (spacing && (aLength > TEXT_BUF_SIZE)) { @@ -1977,12 +2011,12 @@ nsTextFrame::RenderString(nsIRenderingContext& aRenderingContext, else if (ch == ' ') { nextFont = aTextStyle.mNormalFont; nextY = aY; - glyphWidth = aTextStyle.mSpaceWidth + aTextStyle.mWordSpacing; - nscoord extra = aTextStyle.mExtraSpacePerSpace; - if (--aTextStyle.mNumSpaces == 0) { - extra += aTextStyle.mRemainingExtraSpace; + glyphWidth = aTextStyle.mSpaceWidth + aTextStyle.mWordSpacing + + aTextStyle.mExtraSpacePerSpace; + if ((PRUint32)--aTextStyle.mNumSpacesToRender < + (PRUint32)aTextStyle.mNumSpacesReceivingExtraJot) { + glyphWidth++; } - glyphWidth += extra; } else { if (lastFont != aTextStyle.mNormalFont) { @@ -2096,12 +2130,12 @@ nsTextFrame::GetWidthOrLength(nsIRenderingContext& aRenderingContext, glyphWidth = charWidth + aStyle.mLetterSpacing; } else if (ch == ' ') { - glyphWidth = aStyle.mSpaceWidth + aStyle.mWordSpacing; - nscoord extra = aStyle.mExtraSpacePerSpace; - if (--aStyle.mNumSpaces == 0) { - extra += aStyle.mRemainingExtraSpace; + glyphWidth = aStyle.mSpaceWidth + aStyle.mWordSpacing + + aStyle.mExtraSpacePerSpace; + if ((PRUint32)--aStyle.mNumSpacesToMeasure + < (PRUint32)aStyle.mNumSpacesReceivingExtraJot) { + glyphWidth++; } - glyphWidth += extra; } else { if (lastFont != aStyle.mNormalFont) { @@ -2147,6 +2181,45 @@ nsTextFrame::GetLengthSlowly(nsIRenderingContext& aRenderingContext, return GetWidthOrLength(aRenderingContext,aStyle,aBuffer,aLength,&aWidth,PR_FALSE); } +void +nsTextFrame::ComputeExtraJustificationSpacing(nsIRenderingContext& aRenderingContext, + TextStyle& aTextStyle, + PRUnichar* aBuffer, PRInt32 aLength, + PRInt32 aNumSpaces) +{ + if (aTextStyle.mJustifying) { + nscoord trueWidth; + + // OK, so this is a bit ugly. The problem is that to get the right margin + // nice and clean, we have to apply a little extra space to *some* of the + // spaces. It has to be the same ones every time or things will go haywire. + // This implies that the GetWidthOrLength and RenderString functions depend + // on a little bit of secret state: which part of the prepared text they are + // looking at. It turns out that they get called in a regular way: they look + // at the text from the beginning to the end. So we just count which spaces + // we're up to, for each context. + // This is not a great solution, but a perfect solution requires much more + // widespread changes, to explicitly annotate all the transformed text fragments + // that are passed around with their position in the transformed text + // for the entire frame. + aTextStyle.mNumSpacesToMeasure = 0; + aTextStyle.mExtraSpacePerSpace = 0; + aTextStyle.mNumSpacesReceivingExtraJot = 0; + + GetWidth(aRenderingContext, aTextStyle, aBuffer, aLength, &trueWidth); + + aTextStyle.mNumSpacesToMeasure = aNumSpaces; + aTextStyle.mNumSpacesToRender = aNumSpaces; + + nscoord extraSpace = mRect.width - trueWidth; + + if (extraSpace > 0 && aNumSpaces > 0) { + aTextStyle.mExtraSpacePerSpace = extraSpace/aNumSpaces; + aTextStyle.mNumSpacesReceivingExtraJot = + extraSpace - aTextStyle.mExtraSpacePerSpace*aNumSpaces; + } + } +} void nsTextFrame::PaintTextSlowly(nsIPresContext* aPresContext, @@ -2173,10 +2246,11 @@ nsTextFrame::PaintTextSlowly(nsIPresContext* aPresContext, nsCOMPtr lb; doc->GetLineBreaker(getter_AddRefs(lb)); nsTextTransformer tx(lb, nsnull); - aTextStyle.mNumSpaces = PrepareUnicodeText(tx, - (displaySelection - ? &indexBuffer : nsnull), - &paintBuffer, &textLength); + PRInt32 numSpaces; + + numSpaces = PrepareUnicodeText(tx, (displaySelection ? &indexBuffer : nsnull), + &paintBuffer, &textLength); + PRInt32* ip = indexBuffer.mBuffer; PRUnichar* text = paintBuffer.mBuffer; @@ -2185,6 +2259,7 @@ nsTextFrame::PaintTextSlowly(nsIPresContext* aPresContext, GetFrameState(&frameState); isSelected = (frameState & NS_FRAME_SELECTED_CONTENT) == NS_FRAME_SELECTED_CONTENT; if (0 != textLength) { + ComputeExtraJustificationSpacing(aRenderingContext, aTextStyle, text, textLength, numSpaces); if (!displaySelection || !isSelected) { // When there is no selection showing, use the fastest and // simplest rendering approach @@ -2550,8 +2625,7 @@ nsTextFrame::GetPosition(nsIPresContext* aCX, rv = shell->CreateRenderingContext(this, getter_AddRefs(acx)); if (NS_SUCCEEDED(rv)) { TextStyle ts(aCX, *acx, mStyleContext); - if (ts.mSmallCaps || ts.mWordSpacing || ts.mLetterSpacing) { - + if (ts.mSmallCaps || ts.mWordSpacing || ts.mLetterSpacing || ts.mJustifying) { nsresult result = GetPositionSlowly(aCX, acx, aPoint, aNewContent, aContentOffset); aContentOffsetEnd = aContentOffset; @@ -2582,6 +2656,7 @@ nsTextFrame::GetPosition(nsIPresContext* aCX, doc->GetLineBreaker(getter_AddRefs(lb)); nsTextTransformer tx(lb, nsnull); PRInt32 textLength; + // no need to worry about justification, that's always on the slow path PrepareUnicodeText(tx, &indexBuffer, &paintBuffer, &textLength); if (textLength <=0) { @@ -2593,7 +2668,7 @@ nsTextFrame::GetPosition(nsIPresContext* aCX, nsIView * view; GetOffsetFromView(aCX, origin, &view); -//IF SYLE SAYS TO SELCT TO END OF FRAME HERE... +//IF STYLE SAYS TO SELECT TO END OF FRAME HERE... nsCOMPtr prefs; PRInt32 prefInt = 0; rv = nsServiceManager::GetService(kPrefCID, @@ -2880,7 +2955,12 @@ nsTextFrame::GetPointFromOffset(nsIPresContext* aPresContext, doc->GetLineBreaker(getter_AddRefs(lb)); nsTextTransformer tx(lb, nsnull); PRInt32 textLength; - PrepareUnicodeText(tx, &indexBuffer, &paintBuffer, &textLength); + PRInt32 numSpaces; + + numSpaces = PrepareUnicodeText(tx, &indexBuffer, &paintBuffer, &textLength); + + ComputeExtraJustificationSpacing(*inRendContext, ts, paintBuffer.mBuffer, textLength, numSpaces); + PRInt32* ip = indexBuffer.mBuffer; if (inOffset > mContentLength){ @@ -2889,7 +2969,7 @@ nsTextFrame::GetPointFromOffset(nsIPresContext* aPresContext, } nscoord width = mRect.width; - if (ts.mSmallCaps || (0 != ts.mWordSpacing) || (0 != ts.mLetterSpacing)) + if (ts.mSmallCaps || (0 != ts.mWordSpacing) || (0 != ts.mLetterSpacing) || ts.mJustifying) { GetWidth(*inRendContext, ts, paintBuffer.mBuffer, ip[inOffset]-mContentOffset, @@ -2907,7 +2987,8 @@ nsTextFrame::GetPointFromOffset(nsIPresContext* aPresContext, // to the total width, so the caret appears // in the proper place! // - width += ts.mSpaceWidth; + // NOTE: the trailing whitespace includes the word spacing!! + width += ts.mSpaceWidth + ts.mWordSpacing; } outPoint->x = width; @@ -3502,6 +3583,7 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext, #ifdef _WIN32 PRBool measureTextRuns = !aTextData.mComputeMaxWordWidth && !aTs.mPreformatted && !aTs.mSmallCaps && !aTs.mWordSpacing && !aTs.mLetterSpacing; + // Don't measure text runs with letter spacing active, it doesn't work #else PRBool measureTextRuns = PR_FALSE; #endif @@ -3579,10 +3661,17 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext, mState |= TEXT_SKIP_LEADING_WS; continue; } + + // NOTE: Even if the textRun absorbs the whitespace below, we still + // want to remember that we're breakable. + aTextData.mIsBreakable = PR_TRUE; + aTextData.mFirstLetterOK = PR_FALSE; + if ('\t' == firstChar) { // Expand tabs to the proper width wordLen = 8 - (7 & column); - width = aTs.mSpaceWidth * wordLen; + // Apply word spacing to every space derived from a tab + width = (aTs.mSpaceWidth + aTs.mWordSpacing)*wordLen; // Because we have to expand the tab when rendering consider that // a transformation of the text @@ -3594,10 +3683,9 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext, continue; } else { - width = (wordLen * aTs.mSpaceWidth) + aTs.mWordSpacing;// XXX simplistic + // Apply word spacing to every space, if there's more than one + width = wordLen*(aTs.mWordSpacing + aTs.mSpaceWidth);// XXX simplistic } - aTextData.mIsBreakable = PR_TRUE; - aTextData.mFirstLetterOK = PR_FALSE; if (aTextData.mMeasureText) { // See if there is room for the text @@ -3612,7 +3700,6 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext, endsInWhitespace = PR_TRUE; prevOffset = aTextData.mOffset; aTextData.mOffset += contentLen; - } else { // See if the first thing in the section of text is a // non-breaking space (html nbsp entity). If it is then make @@ -3704,6 +3791,7 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext, MeasureTextRun: #ifdef _WIN32 PRInt32 numCharsFit; + // These calls can return numCharsFit not positioned at a break in the textRun. Beware. if (aTx.TransformedTextIsAscii()) { aReflowState.rendContext->GetWidth((char*)aTx.GetWordBuffer(), textRun.mTotalNumChars, maxWidth - aTextData.mX, @@ -3722,22 +3810,43 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext, } // Find the index of the last segment that fit - PRInt32 lastSegment = textRun.mNumSegments - 1; - if (numCharsFit != textRun.mTotalNumChars) { + PRInt32 lastSegment; + if (numCharsFit == textRun.mTotalNumChars) { // fast path, normal case + lastSegment = textRun.mNumSegments - 1; + } else { for (lastSegment = 0; textRun.mBreaks[lastSegment] < numCharsFit; lastSegment++) ; NS_ASSERTION(lastSegment < textRun.mNumSegments, "failed to find segment"); + // now we have textRun.mBreaks[lastSegment] >= numCharsFit + /* O'Callahan XXX: This snippet together with the snippet below prevents mail from loading + Justification seems to work just fine without these changes. + We get into trouble in a case where lastSegment gets set to -1 + + if (textRun.mBreaks[lastSegment] > numCharsFit) { + // NOTE: this segment did not actually fit! + lastSegment--; + } + */ } + /* O'Callahan XXX: This snippet together with the snippet above prevents mail from loading + + if (lastSegment < 0) { + // no segments fit + break; + } else */ if (lastSegment == 0) { // Only one segment fit prevColumn = column; prevOffset = aTextData.mOffset; - } else { // The previous state is for the next to last word prevColumn = textRun.mBreaks[lastSegment - 1]; prevOffset = textRun.mSegments[lastSegment - 1].ContentLen(); + // NOTE: The textRun data are relative to the last updated column and offset! + prevColumn = column + textRun.mBreaks[lastSegment - 1]; + prevOffset = aTextData.mOffset + textRun.mSegments[lastSegment - 1].ContentLen(); } + aTextData.mX += width; column += numCharsFit; aTextData.mOffset += textRun.mSegments[lastSegment].ContentLen(); @@ -3775,7 +3884,8 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext, aTextData.mX = mRect.width; if (mState & TEXT_TRIMMED_WS) { // Add back in the width of a space since it was trimmed away last time - aTextData.mX += aTs.mSpaceWidth; + // NOTE: Trailing whitespace includes word spacing! + aTextData.mX += aTs.mSpaceWidth + aTs.mWordSpacing; } } @@ -4056,19 +4166,22 @@ nsTextFrame::Reflow(nsIPresContext* aPresContext, // current frame width -or- // we're not wrapping text and we're at the same column as before (this is // an issue for preformatted tabbed text only) + // - AND we aren't justified (in which case the frame width has already been tweaked and can't be used) if ((eReflowReason_Resize == aReflowState.reason) && (0 == (mState & NS_FRAME_IS_DIRTY))) { nscoord realWidth = mRect.width; if (mState & TEXT_TRIMMED_WS) { - realWidth += ts.mSpaceWidth; + // NOTE: Trailing whitespace includes word spacing! + realWidth += ts.mSpaceWidth + ts.mWordSpacing; } if (!mNextInFlow && (mState & TEXT_OPTIMIZE_RESIZE) && !aMetrics.maxElementSize && (lastTimeWeSkippedLeadingWS == skipWhitespace) && ((wrapping && (maxWidth >= realWidth)) || - (!wrapping && (prevColumn == column)))) { + (!wrapping && (prevColumn == column))) && + !ts.mJustifying) { // We can skip measuring of text and use the value from our // previous reflow measureText = PR_FALSE; @@ -4120,6 +4233,23 @@ nsTextFrame::Reflow(nsIPresContext* aPresContext, mContentOffset = startingOffset; mContentLength = textData.mOffset - startingOffset; + // Compute space and letter counts for justification, if required + if (ts.mJustifying) { + PRInt32 numSpaces; + PRInt32 textLength; + + // This will include a space for trailing whitespace, if any is present. + // This is corrected for in nsLineLayout::TrimWhiteSpaceIn. + + // This work could be done in MeasureText, but it's complex to do accurately + // there because of the need to repair counts when wrapped words are backed out. + // So I do it via PrepareUnicodeText ... a little slower perhaps, but a lot saner, + // and it localizes the counting logic to one place. + numSpaces = PrepareUnicodeText(tx, nsnull, nsnull, &textLength); + lineLayout.SetTextJustificationWeights(numSpaces, textLength - numSpaces); + } + + #ifdef MOZ_MATHML // Simple minded code to also return the bounding metrics if the caller wants it... // More consolidation is needed -- a better approach is to follow what is done by @@ -4226,6 +4356,11 @@ nsTextFrame::TrimTrailingWhiteSpace(nsIPresContext* aPresContext, mStyleContext->GetStyleData(eStyleStruct_Font); aRC.SetFont(fontStyle->mFont); aRC.GetWidth(' ', dw); + // NOTE: Trailing whitespace includes word spacing! + PRIntn unit = textStyle->mWordSpacing.GetUnit(); + if (eStyleUnit_Coord == unit) { + dw += textStyle->mWordSpacing.GetCoordValue(); + } } } } @@ -4406,6 +4541,8 @@ nsTextFrame::ComputeWordFragmentWidth(nsIPresContext* aPresContext, } else { rc.GetWidth(bp, wordLen, width); + // NOTE: Don't forget to add letter spacing for the word fragment! + width += wordLen*ts.mLetterSpacing; } rc.SetFont(oldfm); diff --git a/layout/html/base/src/nsBlockBandData.cpp b/layout/html/base/src/nsBlockBandData.cpp index 32b93fc1478b..ef56e9c8666c 100644 --- a/layout/html/base/src/nsBlockBandData.cpp +++ b/layout/html/base/src/nsBlockBandData.cpp @@ -81,6 +81,10 @@ nsBlockBandData::GetAvailableSpace(nscoord aY, nsRect& aResult) // between any left and right floaters. ComputeAvailSpaceRect(); aResult = mAvailSpace; +#ifdef REALLY_NOISY_COMPUTEAVAILSPACERECT + printf("nsBBD %p GetAvailableSpace(%d) returing (%d, %d, %d, %d)\n", + this, aY, aResult.x, aResult.y, aResult.width, aResult.height); +#endif return NS_OK; } @@ -134,7 +138,7 @@ void nsBlockBandData::ComputeAvailSpaceRect() { #ifdef REALLY_NOISY_COMPUTEAVAILSPACERECT - printf("nsBlockBandData::ComputeAvailSpaceRect %p \n", this); + printf("nsBlockBandData::ComputeAvailSpaceRect %p with count %d\n", this, mCount); #endif if (0 == mCount) { mAvailSpace.x = 0; @@ -248,6 +252,11 @@ nsBlockBandData::ComputeAvailSpaceRect() if (NS_UNCONSTRAINEDSIZE == mSpace.width) { mAvailSpace.width = NS_UNCONSTRAINEDSIZE; } +#ifdef REALLY_NOISY_COMPUTEAVAILSPACERECT + printf(" ComputeAvailSpaceRect settting state mAvailSpace (%d,%d,%d,%d)\n", + mAvailSpace.x, mAvailSpace.y, mAvailSpace.width, mAvailSpace.height); +#endif + } /** @@ -471,3 +480,15 @@ nsBlockBandData::GetMaxElementSize(nsIPresContext* aPresContext, *aWidthResult = maxWidth; *aHeightResult = maxHeight; } + +#ifdef DEBUG +void nsBlockBandData::List() +{ + printf("nsBlockBandData %p sm=%p, sm coord = (%d,%d), mSpace = (%d,%d)\n", + this, mSpaceManager, mSpaceManagerX, mSpaceManagerY, + mSpace.width, mSpace.height); + printf(" availSpace=(%d, %d, %d, %d), floaters l=%d r=%d\n", + mAvailSpace.x, mAvailSpace.y, mAvailSpace.width, mAvailSpace.height, + mLeftFloaters, mRightFloaters); +} +#endif diff --git a/layout/html/base/src/nsBlockBandData.h b/layout/html/base/src/nsBlockBandData.h index 29ac776dd93a..7008a61593ac 100644 --- a/layout/html/base/src/nsBlockBandData.h +++ b/layout/html/base/src/nsBlockBandData.h @@ -89,6 +89,10 @@ public: nsIFrame* aFrame, nsSize* aResult); +#ifdef DEBUG + void List(); +#endif + protected: /** utility method to calculate the band data at aY. diff --git a/layout/html/base/src/nsBlockFrame.cpp b/layout/html/base/src/nsBlockFrame.cpp index 554b851c4d28..5f974deb204e 100644 --- a/layout/html/base/src/nsBlockFrame.cpp +++ b/layout/html/base/src/nsBlockFrame.cpp @@ -18,6 +18,8 @@ * Rights Reserved. * * Contributor(s): + * Steve Clark + * Robert O'Callahan * Pierre Phaneuf */ #include "nsCOMPtr.h" @@ -61,6 +63,8 @@ #ifdef DEBUG +//#define NOISY_BLOCK_INVALIDATE // DO NOT CHECK THIS IN TURNED ON! + static PRBool gLamePaintMetrics; static PRBool gLameReflowMetrics; static PRBool gNoisy; @@ -451,12 +455,6 @@ public: nscoord mBottomEdge; - PRPackedBool mUnconstrainedWidth; - PRPackedBool mUnconstrainedHeight; - PRPackedBool mShrinkWrapWidth; - PRPackedBool mNeedResizeReflow; - PRPackedBool mIsInlineIncrReflow; - // The content area to reflow child frames within. The x/y // coordinates are known to be mBorderPadding.left and // mBorderPadding.top. The width/height may be NS_UNCONSTRAINEDSIZE @@ -464,15 +462,6 @@ public: // unconstrained area. nsSize mContentArea; - // Our wrapping behavior - PRPackedBool mNoWrap; - - // Is this frame a root for top/bottom margin collapsing? - PRPackedBool mIsTopMarginRoot, mIsBottomMarginRoot; - - // See ShouldApplyTopMargin - PRPackedBool mApplyTopMargin; - //---------------------------------------- // This state is "running" state updated by the reflow of each line @@ -541,15 +530,48 @@ public: // being N^2. nsFloaterCacheFreeList mBelowCurrentLineFloaters; - PRPackedBool mComputeMaxElementSize; - PRPackedBool mComputeMaximumWidth; - nsSize mMaxElementSize; nscoord mMaximumWidth; nscoord mMinLineHeight; PRInt32 mLineNumber; + + // block reflow state flags +#define BRS_UNCONSTRAINEDWIDTH 0x00000001 +#define BRS_UNCONSTRAINEDHEIGHT 0x00000002 +#define BRS_SHRINKWRAPWIDTH 0x00000004 +#define BRS_NEEDRESIZEREFLOW 0x00000008 +#define BRS_ISINLINEINCRREFLOW 0x00000010 +#define BRS_NOWRAP 0x00000020 +#define BRS_ISTOPMARGINROOT 0x00000040 // Is this frame a root for top/bottom margin collapsing? +#define BRS_ISBOTTOMMARGINROOT 0x00000080 +#define BRS_APPLYTOPMARGIN 0x00000100 // See ShouldApplyTopMargin +#define BRS_COMPUTEMAXELEMENTSIZE 0x00000200 +#define BRS_COMPUTEMAXWIDTH 0x00000400 +#define BRS_LASTFLAG BRS_COMPUTEMAXWIDTH + + PRInt16 mFlags; + + void SetFlag(PRUint32 aFlag, PRBool aValue) + { + NS_ASSERTION(aFlag<=BRS_LASTFLAG, "bad flag"); + NS_ASSERTION(aValue==PR_FALSE || aValue==PR_TRUE, "bad value"); + if (aValue) { // set flag + mFlags |= aFlag; + } + else { // unset flag + mFlags &= ~aFlag; + } + } + + PRBool GetFlag(PRUint32 aFlag) const + { + NS_ASSERTION(aFlag<=BRS_LASTFLAG, "bad flag"); + PRBool result = (mFlags & aFlag); + if (result) return PR_TRUE; + return PR_FALSE; + } }; // XXX This is vile. Make it go away @@ -574,29 +596,25 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, : mBlock(aFrame), mPresContext(aPresContext), mReflowState(aReflowState), - mNeedResizeReflow(PR_FALSE), - mIsInlineIncrReflow(PR_FALSE), - mIsTopMarginRoot(PR_FALSE), - mIsBottomMarginRoot(PR_FALSE), - mApplyTopMargin(PR_FALSE), mNextRCFrame(nsnull), mPrevBottomMargin(0), - mLineNumber(0) + mLineNumber(0), + mFlags(0) { const nsMargin& borderPadding = BorderPadding(); if (aBlockMarginRoot) { - mIsTopMarginRoot = PR_TRUE; - mIsBottomMarginRoot = PR_TRUE; + SetFlag(BRS_ISTOPMARGINROOT, PR_TRUE); + SetFlag(BRS_ISBOTTOMMARGINROOT, PR_TRUE); } if (0 != aReflowState.mComputedBorderPadding.top) { - mIsTopMarginRoot = PR_TRUE; + SetFlag(BRS_ISTOPMARGINROOT, PR_TRUE); } if (0 != aReflowState.mComputedBorderPadding.bottom) { - mIsBottomMarginRoot = PR_TRUE; + SetFlag(BRS_ISBOTTOMMARGINROOT, PR_TRUE); } - if (mIsTopMarginRoot) { - mApplyTopMargin = PR_TRUE; + if (GetFlag(BRS_ISTOPMARGINROOT)) { + SetFlag(BRS_APPLYTOPMARGIN, PR_TRUE); } mSpaceManager = aReflowState.mSpaceManager; @@ -617,21 +635,19 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, // Compute content area width (the content area is inside the border // and padding) - mUnconstrainedWidth = PR_FALSE; - mShrinkWrapWidth = PR_FALSE; if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedWidth) { mContentArea.width = aReflowState.mComputedWidth; } else { if (NS_UNCONSTRAINEDSIZE == aReflowState.availableWidth) { mContentArea.width = NS_UNCONSTRAINEDSIZE; - mUnconstrainedWidth = PR_TRUE; + SetFlag(BRS_UNCONSTRAINEDWIDTH, PR_TRUE); } else if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMaxWidth) { // Choose a width based on the content (shrink wrap width) up // to the maximum width mContentArea.width = aReflowState.mComputedMaxWidth; - mShrinkWrapWidth = PR_TRUE; + SetFlag(BRS_SHRINKWRAPWIDTH, PR_TRUE); } else { nscoord lr = borderPadding.left + borderPadding.right; @@ -646,7 +662,6 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, // specified style height then we may end up limiting our height if // the availableHeight is constrained (this situation occurs when we // are paginated). - mUnconstrainedHeight = PR_FALSE; if (NS_UNCONSTRAINEDSIZE != aReflowState.availableHeight) { // We are in a paginated situation. The bottom edge is just inside // the bottom border and padding. The content area height doesn't @@ -657,7 +672,7 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, else { // When we are not in a paginated situation then we always use // an constrained height. - mUnconstrainedHeight = PR_TRUE; + SetFlag(BRS_UNCONSTRAINEDHEIGHT, PR_TRUE); mContentArea.height = mBottomEdge = NS_UNCONSTRAINEDSIZE; } @@ -674,16 +689,17 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, switch (styleText->mWhiteSpace) { case NS_STYLE_WHITESPACE_PRE: case NS_STYLE_WHITESPACE_NOWRAP: - mNoWrap = PR_TRUE; + SetFlag(BRS_NOWRAP, PR_TRUE); break; default: - mNoWrap = PR_FALSE; + SetFlag(BRS_NOWRAP, PR_FALSE); break; } - mComputeMaxElementSize = nsnull != aMetrics.maxElementSize; + SetFlag(BRS_COMPUTEMAXELEMENTSIZE, (nsnull != aMetrics.maxElementSize)); mMaxElementSize.SizeTo(0, 0); - mComputeMaximumWidth = NS_REFLOW_CALC_MAX_WIDTH == (aMetrics.mFlags & NS_REFLOW_CALC_MAX_WIDTH); + SetFlag(BRS_COMPUTEMAXWIDTH, + (NS_REFLOW_CALC_MAX_WIDTH == (aMetrics.mFlags & NS_REFLOW_CALC_MAX_WIDTH))); mMaximumWidth = 0; mMinLineHeight = nsHTMLReflowState::CalcLineHeight(mPresContext, @@ -729,8 +745,12 @@ nsBlockReflowState::ComputeBlockAvailSpace(nsIFrame* aFrame, const nsStyleDisplay* aDisplay, nsRect& aResult) { +#ifdef REALLY_NOISY_REFLOW + printf("CBAS frame=%p has floater count %d\n", aFrame, mBand.GetFloaterCount()); + mBand.List(); +#endif aResult.y = mY; - aResult.height = mUnconstrainedHeight + aResult.height = GetFlag(BRS_UNCONSTRAINEDHEIGHT) ? NS_UNCONSTRAINEDSIZE : mBottomEdge - mY; @@ -749,7 +769,7 @@ nsBlockReflowState::ComputeBlockAvailSpace(nsIFrame* aFrame, // The child block will flow around the floater. Therefore // give it all of the available space. aResult.x = borderPadding.left; - aResult.width = mUnconstrainedWidth + aResult.width = GetFlag(BRS_UNCONSTRAINEDWIDTH) ? NS_UNCONSTRAINEDSIZE : mContentArea.width; break; @@ -776,7 +796,7 @@ nsBlockReflowState::ComputeBlockAvailSpace(nsIFrame* aFrame, } // determine width - if (mUnconstrainedWidth) { + if (GetFlag(BRS_UNCONSTRAINEDWIDTH)) { aResult.width = NS_UNCONSTRAINEDSIZE; } else { @@ -810,7 +830,7 @@ nsBlockReflowState::ComputeBlockAvailSpace(nsIFrame* aFrame, // doesn't matter therefore give the block element all of the // available space since it will flow around the floater itself. aResult.x = borderPadding.left; - aResult.width = mUnconstrainedWidth + aResult.width = GetFlag(BRS_UNCONSTRAINEDWIDTH) ? NS_UNCONSTRAINEDSIZE : mContentArea.width; } @@ -957,12 +977,12 @@ nsBlockReflowState::RecoverStateFrom(nsLineBox* aLine, #endif mKidXMost = xmost; } - if (mComputeMaxElementSize) { + if (GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { UpdateMaxElementSize(nsSize(aLine->mMaxElementWidth, aLine->mBounds.height)); } // If computing the maximum width, then update mMaximumWidth - if (mComputeMaximumWidth) { + if (GetFlag(BRS_COMPUTEMAXWIDTH)) { UpdateMaximumWidth(aLine->mMaximumWidth); } @@ -1454,6 +1474,7 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, nsBlockReflowState state(aReflowState, aPresContext, this, aMetrics, NS_BLOCK_MARGIN_ROOT & mState); + PRInt32 sizeofBRS = sizeof nsBlockReflowState; if (eReflowReason_Resize != aReflowState.reason) { RenumberLists(aPresContext); @@ -1462,7 +1483,7 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, nsresult rv = NS_OK; PRBool isStyleChange = PR_FALSE; - state.mIsInlineIncrReflow = PR_FALSE; + state.SetFlag(BRS_ISINLINEINCRREFLOW, PR_FALSE); nsIFrame* target; switch (aReflowState.reason) { case eReflowReason_Initial: @@ -1535,7 +1556,7 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, // reflow the line containing the target of the incr. reflow // first mark the line dirty and set up the state object rv = PrepareChildIncrementalReflow(state); - state.mIsInlineIncrReflow = PR_TRUE; + state.SetFlag(BRS_ISINLINEINCRREFLOW, PR_TRUE); state.mPrevLine = prevLine; state.mCurrentLine = line; state.mNextRCFrame = state.mNextRCFrame; @@ -1681,6 +1702,10 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, if (isStyleChange) { // Lots of things could have changed so damage our entire // bounds +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 1 (%d, %d, %d, %d)\n", + this, 0, 0, mRect.width, mRect.height); +#endif Invalidate(aPresContext, nsRect(0, 0, mRect.width, mRect.height)); } else { @@ -1707,6 +1732,10 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, damageRect.y = 0; damageRect.height = mRect.height; } +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 2 (%d, %d, %d, %d)\n", + this, damageRect.x, damageRect.y, damageRect.width, damageRect.height); +#endif Invalidate(aPresContext, damageRect); } @@ -1730,6 +1759,10 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, damageRect.y = mRect.height - border.bottom; damageRect.height = border.bottom; } +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 3 (%d, %d, %d, %d)\n", + this, damageRect.x, damageRect.y, damageRect.width, damageRect.height); +#endif Invalidate(aPresContext, damageRect); } } @@ -1868,7 +1901,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, #ifdef NOISY_FINAL_SIZE ListTag(stdout); printf(": mY=%d mIsBottomMarginRoot=%s mPrevBottomMargin=%d bp=%d,%d\n", - aState.mY, aState.mIsBottomMarginRoot ? "yes" : "no", + aState.mY, aState.GetFlag(BRS_ISBOTTOMMARGINROOT) ? "yes" : "no", aState.mPrevBottomMargin, borderPadding.top, borderPadding.bottom); #endif @@ -1946,7 +1979,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, // contents or we fluff out to the maximum block width. Note: // We always shrink wrap when given an unconstrained width. if ((0 == (NS_BLOCK_SHRINK_WRAP & mState)) && - !aState.mUnconstrainedWidth && !aState.mShrinkWrapWidth && + !aState.GetFlag(BRS_UNCONSTRAINEDWIDTH) && !aState.GetFlag(BRS_SHRINKWRAPWIDTH) && !compact) { // Set our width to the max width if we aren't already that // wide. Note that the max-width has nothing to do with our @@ -1956,9 +1989,9 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, } // See if we should compute our max element size - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { // Adjust the computedWidth - if (aState.mNoWrap) { + if (aState.GetFlag(BRS_NOWRAP)) { // When no-wrap is true the max-element-size.width is the // width of the widest line plus the right border. Note that // aState.mKidXMost already has the left border factored in @@ -1996,7 +2029,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, // need to do horizontal alignment of the inline lines and make sure // blocks are correctly sized and positioned. Any lines that need // final adjustment will have been marked as dirty - if (aState.mShrinkWrapWidth && aState.mNeedResizeReflow) { + if (aState.GetFlag(BRS_SHRINKWRAPWIDTH) && aState.GetFlag(BRS_NEEDRESIZEREFLOW)) { // If the parent reflow state is also shrink wrap width, then // we don't need to do this, because it will reflow us after it // calculates the final width @@ -2025,7 +2058,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, } } - if (aState.mShrinkWrapWidth) { + if (aState.GetFlag(BRS_SHRINKWRAPWIDTH)) { PRBool parentIsShrinkWrapWidth = PR_FALSE; if (aReflowState.parentReflowState) { if (NS_SHRINKWRAPWIDTH == aReflowState.parentReflowState->mComputedWidth) { @@ -2047,7 +2080,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, // Don't carry out a bottom margin when our height is fixed // unless the bottom of the last line adjoins the bottom of our // content area. - if (!aState.mIsBottomMarginRoot) { + if (!aState.GetFlag(BRS_ISBOTTOMMARGINROOT)) { if (aState.mY + aState.mPrevBottomMargin != aMetrics.height) { aState.mPrevBottomMargin = 0; } @@ -2057,7 +2090,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, nscoord autoHeight = aState.mY; // Shrink wrap our height around our contents. - if (aState.mIsBottomMarginRoot) { + if (aState.GetFlag(BRS_ISBOTTOMMARGINROOT)) { // When we are a bottom-margin root make sure that our last // childs bottom margin is fully applied. // XXX check for a fit @@ -2082,7 +2115,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, } aMetrics.height = autoHeight; - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { maxHeight = aState.mMaxElementSize.height + borderPadding.top + borderPadding.bottom; } @@ -2090,7 +2123,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, aMetrics.ascent = aMetrics.height; aMetrics.descent = 0; - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { // Store away the final value aMetrics.maxElementSize->width = maxWidth; aMetrics.maxElementSize->height = maxHeight; @@ -2098,14 +2131,14 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, // Return bottom margin information aMetrics.mCarriedOutBottomMargin = - aState.mIsBottomMarginRoot ? 0 : aState.mPrevBottomMargin; + aState.GetFlag(BRS_ISBOTTOMMARGINROOT) ? 0 : aState.mPrevBottomMargin; #ifdef DEBUG if (CRAZY_WIDTH(aMetrics.width) || CRAZY_HEIGHT(aMetrics.height)) { ListTag(stdout); printf(": WARNING: desired:%d,%d\n", aMetrics.width, aMetrics.height); } - if (aState.mComputeMaxElementSize && + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE) && ((maxWidth > aMetrics.width) || (maxHeight > aMetrics.height))) { ListTag(stdout); printf(": WARNING: max-element-size:%d,%d desired:%d,%d maxSize:%d,%d\n", @@ -2115,7 +2148,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, } #endif #ifdef NOISY_MAX_ELEMENT_SIZE - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { IndentBy(stdout, GetDepth()); if (NS_UNCONSTRAINEDSIZE == aState.mReflowState.availableWidth) { printf("PASS1 "); @@ -2130,7 +2163,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, } // If we're requested to update our maximum width, then compute it - if (aState.mComputeMaximumWidth) { + if (aState.GetFlag(BRS_COMPUTEMAXWIDTH)) { // We need to add in for the right border/padding aMetrics.mMaximumWidth = aState.mMaximumWidth + borderPadding.right; } @@ -2337,8 +2370,13 @@ nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState) // See if we can try and avoid marking all the lines as dirty PRBool tryAndSkipLines = PR_FALSE; - // See if this is this a constrained resize reflow - if ((aState.mReflowState.reason == eReflowReason_Resize) && + // we need to calculate if any part of then block itself + // is impacted by a floater (bug 19579) + aState.GetAvailableSpace(); + + // See if this is this a constrained resize reflow that is not impacted by floaters + if ((PR_FALSE==aState.IsImpactedByFloater()) && + (aState.mReflowState.reason == eReflowReason_Resize) && (NS_UNCONSTRAINEDSIZE != aState.mReflowState.availableWidth)) { // If the text is left-aligned, then we try and avoid reflowing the lines @@ -2389,7 +2427,7 @@ nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState) } #endif - PRBool notWrapping = aState.mNoWrap; + PRBool notWrapping = aState.GetFlag(BRS_NOWRAP); while (nsnull != line) { if (line->IsBlock()) { @@ -2402,8 +2440,6 @@ nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState) printf("PrepareResizeReflow thinks line %p is %simpacted by floaters\n", line, line->IsImpactedByFloater() ? "" : "not "); #endif - - if (notWrapping) { // When no-wrap is set then the only line-breaking that // occurs for inline lines is triggered by BR elements or by @@ -2430,7 +2466,7 @@ nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState) printf("skipped: line=%p next=%p %s %s %s%s%s breakType=%d xmost=%d\n", line, line->mNext, line->IsBlock() ? "block" : "inline", - aState.mNoWrap ? "no-wrap" : "wrapping", + aState.GetFlag(BRS_NOWRAP) ? "no-wrap" : "wrapping", line->HasBreak() ? "has-break " : "", line->HasFloaters() ? "has-floaters " : "", line->IsImpactedByFloater() ? "impacted " : "", @@ -2628,7 +2664,7 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) ListTag(stdout); printf(": incrementally reflowing dirty lines: type=%s(%d) isInline=%s", kReflowCommandType[type], type, - aState.mIsInlineIncrReflow ? "true" : "false"); + aState.GetFlag(BRS_ISINLINEINCRREFLOW) ? "true" : "false"); } else { IndentBy(stdout, gNoiseIndent); @@ -2651,7 +2687,7 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) // Reflow the lines that are already ours aState.mPrevLine = nsnull; nsLineBox* line = mLines; - if (aState.mIsInlineIncrReflow && aState.mNextRCFrame) + if (aState.GetFlag(BRS_ISINLINEINCRREFLOW) && aState.mNextRCFrame) { const nsLineBox* incrTargetLine = aState.mCurrentLine; aState.mCurrentLine = line; @@ -2662,6 +2698,10 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) RecoverStateFrom(aState, line, deltaY, incrementalReflow ? &damageRect : 0); if (incrementalReflow && !damageRect.IsEmpty()) { +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 4 (%d, %d, %d, %d)\n", + this, damageRect.x, damageRect.y, damageRect.width, damageRect.height); +#endif Invalidate(aState.mPresContext, damageRect); } aState.mPrevLine = line; @@ -2689,7 +2729,7 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) // If we're supposed to update our maximum width, then we'll also need to // reflow this line if it's line wrapped and any of the continuing lines // are dirty - if (line->IsDirty() || (aState.mComputeMaximumWidth && ::WrappedLinesAreDirty(line))) { + if (line->IsDirty() || (aState.GetFlag(BRS_COMPUTEMAXWIDTH) && ::WrappedLinesAreDirty(line))) { // Compute the dirty lines "before" YMost, after factoring in // the running deltaY value - the running value is implicit in // aState.mY. @@ -2729,6 +2769,10 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) RecoverStateFrom(aState, line, deltaY, incrementalReflow ? &damageRect : 0); if (incrementalReflow && !damageRect.IsEmpty()) { +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 5 (%d, %d, %d, %d)\n", + this, damageRect.x, damageRect.y, damageRect.width, damageRect.height); +#endif Invalidate(aState.mPresContext, damageRect); } } @@ -2902,6 +2946,10 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, // XXX We need to improve on this... nsRect dirtyRect; dirtyRect.UnionRect(oldCombinedArea, lineCombinedArea); +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 6 (%d, %d, %d, %d)\n", + this, dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height); +#endif Invalidate(aState.mPresContext, dirtyRect); } else { @@ -2918,6 +2966,10 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, dirtyRect.x; dirtyRect.height = PR_MAX(oldCombinedArea.height, lineCombinedArea.height); +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 7 (%d, %d, %d, %d)\n", + this, dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height); +#endif Invalidate(aState.mPresContext, dirtyRect); } if (oldCombinedArea.height != lineCombinedArea.height) { @@ -2933,6 +2985,10 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, dirtyRect.height = PR_MAX(oldCombinedArea.YMost(), lineCombinedArea.YMost()) - dirtyRect.y; +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 8 (%d, %d, %d, %d)\n", + this, dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height); +#endif Invalidate(aState.mPresContext, dirtyRect); } } @@ -2951,21 +3007,21 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, // we'll either need to recover the floater state that applies to the // unconstrained reflow or keep it around in a separate space manager... PRBool isBeginningLine = !aState.mPrevLine || !aState.mPrevLine->IsLineWrapped(); - if (aState.mComputeMaximumWidth && isBeginningLine) { + if (aState.GetFlag(BRS_COMPUTEMAXWIDTH) && isBeginningLine) { nscoord oldY = aState.mY; nscoord oldPrevBottomMargin = aState.mPrevBottomMargin; - PRBool oldUnconstrainedWidth = aState.mUnconstrainedWidth; + PRBool oldUnconstrainedWidth = aState.GetFlag(BRS_UNCONSTRAINEDWIDTH); // First reflow the line with an unconstrained width. When doing this // we need to set the block reflow state's "mUnconstrainedWidth" variable // to PR_TRUE so if we encounter a placeholder and then reflow its // associated floater we don't end up resetting the line's right edge and // have it think the width is unconstrained... - aState.mUnconstrainedWidth = PR_TRUE; + aState.SetFlag(BRS_UNCONSTRAINEDWIDTH, PR_TRUE); ReflowInlineFrames(aState, aLine, aKeepReflowGoing, PR_TRUE); aState.mY = oldY; aState.mPrevBottomMargin = oldPrevBottomMargin; - aState.mUnconstrainedWidth = oldUnconstrainedWidth; + aState.SetFlag(BRS_UNCONSTRAINEDWIDTH, oldUnconstrainedWidth); // Update the line's maximum width aLine->mMaximumWidth = aLine->mBounds.XMost(); @@ -2980,14 +3036,14 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, // Note: we need to reset both member variables, because the inline // code examines mComputeMaxElementSize and if there is a placeholder // on this line the code to reflow the floater looks at both... - nscoord oldComputeMaxElementSize = aState.mComputeMaxElementSize; - nscoord oldComputeMaximumWidth = aState.mComputeMaximumWidth; + nscoord oldComputeMaxElementSize = aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE); + nscoord oldComputeMaximumWidth = aState.GetFlag(BRS_COMPUTEMAXWIDTH); - aState.mComputeMaxElementSize = PR_FALSE; - aState.mComputeMaximumWidth = PR_FALSE; + aState.SetFlag(BRS_COMPUTEMAXELEMENTSIZE, PR_FALSE); + aState.SetFlag(BRS_COMPUTEMAXWIDTH, PR_FALSE); rv = ReflowInlineFrames(aState, aLine, aKeepReflowGoing); - aState.mComputeMaxElementSize = oldComputeMaxElementSize; - aState.mComputeMaximumWidth = oldComputeMaximumWidth; + aState.SetFlag(BRS_COMPUTEMAXELEMENTSIZE, oldComputeMaxElementSize); + aState.SetFlag(BRS_COMPUTEMAXWIDTH, oldComputeMaximumWidth); } else { rv = ReflowInlineFrames(aState, aLine, aKeepReflowGoing); @@ -3001,6 +3057,10 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, nsRect dirtyRect; dirtyRect.UnionRect(oldCombinedArea, combinedArea); +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 9 (%d, %d, %d, %d)\n", + this, dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height); +#endif Invalidate(aState.mPresContext, dirtyRect); } } @@ -3368,7 +3428,7 @@ PRBool nsBlockFrame::ShouldApplyTopMargin(nsBlockReflowState& aState, nsLineBox* aLine) { - if (aState.mApplyTopMargin) { + if (aState.GetFlag(BRS_APPLYTOPMARGIN)) { // Apply short-circuit check to avoid searching the line list return PR_TRUE; } @@ -3377,7 +3437,7 @@ nsBlockFrame::ShouldApplyTopMargin(nsBlockReflowState& aState, // If we aren't at the top Y coordinate then something of non-zero // height must have been placed. Therefore the childs top-margin // applies. - aState.mApplyTopMargin = PR_TRUE; + aState.SetFlag(BRS_APPLYTOPMARGIN, PR_TRUE); return PR_TRUE; } @@ -3387,13 +3447,13 @@ nsBlockFrame::ShouldApplyTopMargin(nsBlockReflowState& aState, if (line->IsBlock()) { // A line which preceeds aLine contains a block; therefore the // top margin applies. - aState.mApplyTopMargin = PR_TRUE; + aState.SetFlag(BRS_APPLYTOPMARGIN, PR_TRUE); return PR_TRUE; } else if (line->HasFloaters()) { // A line which preceeds aLine is not empty therefore the top // margin applies. - aState.mApplyTopMargin = PR_TRUE; + aState.SetFlag(BRS_APPLYTOPMARGIN, PR_TRUE); return PR_TRUE; } line = line->mNext; @@ -3486,8 +3546,8 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, frame->GetStyleData(eStyleStruct_Display, (const nsStyleStruct*&) display); nsBlockReflowContext brc(aState.mPresContext, aState.mReflowState, - aState.mComputeMaxElementSize, - aState.mComputeMaximumWidth); + aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE), + aState.GetFlag(BRS_COMPUTEMAXWIDTH)); brc.SetNextRCFrame(aState.mNextRCFrame); // See if we should apply the top margin. If the block frame being @@ -3611,14 +3671,14 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, &collapsedBottomMargin, aLine->mBounds, combinedArea); - if (aState.mShrinkWrapWidth) { + if (aState.GetFlag(BRS_SHRINKWRAPWIDTH)) { // Mark the line as block so once we known the final shrink wrap width // we can reflow the block to the correct size // XXX We don't always need to do this... aLine->MarkDirty(); - aState.mNeedResizeReflow = PR_TRUE; + aState.SetFlag(BRS_NEEDRESIZEREFLOW, PR_TRUE); } - if (aState.mUnconstrainedWidth || aState.mShrinkWrapWidth) { + if (aState.GetFlag(BRS_UNCONSTRAINEDWIDTH) || aState.GetFlag(BRS_SHRINKWRAPWIDTH)) { // Add the right margin to the line's bounnds. That way it will be taken into // account when we compute our shrink wrap size nscoord marginRight = brc.GetMargin().right; @@ -3709,7 +3769,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, // Post-process the "line" nsSize maxElementSize(0, 0); - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { maxElementSize = brc.GetMaxElementSize(); if (aState.IsImpactedByFloater() && (NS_FRAME_SPLITTABLE_NON_RECTANGULAR != splitType)) { @@ -3721,7 +3781,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, } // If we asked the block to update its maximum width, then record the // updated value in the line, and update the current maximum width - if (aState.mComputeMaximumWidth) { + if (aState.GetFlag(BRS_COMPUTEMAXWIDTH)) { aLine->mMaximumWidth = brc.GetMaximumWidth(); aState.UpdateMaximumWidth(aLine->mMaximumWidth); @@ -3849,7 +3909,7 @@ nsBlockFrame::DoReflowInlineFramesMalloc(nsBlockReflowState& aState, nsLineLayout* ll = new nsLineLayout(aState.mPresContext, aState.mReflowState.mSpaceManager, &aState.mReflowState, - aState.mComputeMaxElementSize); + aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)); if (!ll) { return NS_ERROR_OUT_OF_MEMORY; } @@ -3872,7 +3932,7 @@ nsBlockFrame::DoReflowInlineFramesAuto(nsBlockReflowState& aState, nsLineLayout lineLayout(aState.mPresContext, aState.mReflowState.mSpaceManager, &aState.mReflowState, - aState.mComputeMaxElementSize); + aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)); lineLayout.Init(&aState, aState.mMinLineHeight, aState.mLineNumber); lineLayout.SetReflowTextRuns(mTextRuns); nsresult rv = DoReflowInlineFrames(aState, lineLayout, aLine, @@ -3910,7 +3970,7 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState, nscoord x = aState.mAvailSpaceRect.x + borderPadding.left; nscoord availWidth = aState.mAvailSpaceRect.width; nscoord availHeight; - if (aState.mUnconstrainedHeight) { + if (aState.GetFlag(BRS_UNCONSTRAINEDHEIGHT)) { availHeight = NS_UNCONSTRAINEDSIZE; } else { @@ -4359,7 +4419,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, nsSize maxElementSize; aLineLayout.VerticalAlignFrames(aLine, maxElementSize); // See if we're shrink wrapping the width - if (aState.mShrinkWrapWidth) { + if (aState.GetFlag(BRS_SHRINKWRAPWIDTH)) { // When determining the line's width we also need to include any // right floaters that impact us. This represents the shrink wrap // width of the line @@ -4388,25 +4448,20 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, // Only block frames horizontally align their children because // inline frames "shrink-wrap" around their children (therefore // there is no extra horizontal space). -#if XXX_fix_me - PRBool allowJustify = PR_TRUE; - if (NS_STYLE_TEXT_ALIGN_JUSTIFY == aState.mStyleText->mTextAlign) { - allowJustify = ShouldJustifyLine(aState, aLine); - } -#else - PRBool allowJustify = PR_FALSE; -#endif - - PRBool successful; - nsRect combinedArea; - successful = aLineLayout.HorizontalAlignFrames(aLine->mBounds, allowJustify, - aState.mShrinkWrapWidth); + const nsStyleText* styleText = (const nsStyleText*) + mStyleContext->GetStyleData(eStyleStruct_Text); + PRBool allowJustify = NS_STYLE_TEXT_ALIGN_JUSTIFY == styleText->mTextAlign + && !aLineLayout.GetLineEndsInBR() && ShouldJustifyLine(aState, aLine); + PRBool successful = aLineLayout.HorizontalAlignFrames(aLine->mBounds, allowJustify, + aState.GetFlag(BRS_SHRINKWRAPWIDTH)); if (!successful) { // Mark the line dirty and then later once we've determined the width // we can do the horizontal alignment aLine->MarkDirty(); - aState.mNeedResizeReflow = PR_TRUE; + aState.SetFlag(BRS_NEEDRESIZEREFLOW, PR_TRUE); } + + nsRect combinedArea; aLineLayout.RelativePositionFrames(combinedArea); aLine->SetCombinedArea(combinedArea); if (addedBullet) { @@ -4454,7 +4509,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, } aState.mY = newY; - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { #ifdef NOISY_MAX_ELEMENT_SIZE IndentBy(stdout, GetDepth()); if (NS_UNCONSTRAINEDSIZE == aState.mReflowState.availableWidth) { @@ -4478,7 +4533,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, // we don't want updated... if (aUpdateMaximumWidth) { // However, we do need to update the max-element-size if requested - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { aState.UpdateMaxElementSize(maxElementSize); // We also cache the max element width in the line. This is needed for // incremental reflow @@ -4519,7 +4574,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, CombineRects(aState.mFloaterCombinedArea, lineCombinedArea); if (aState.mHaveRightFloaters && - (aState.mUnconstrainedWidth || aState.mShrinkWrapWidth)) { + (aState.GetFlag(BRS_UNCONSTRAINEDWIDTH) || aState.GetFlag(BRS_SHRINKWRAPWIDTH))) { // We are reflowing in an unconstrained situation or shrink wrapping and // have some right floaters. They were placed at the infinite right edge // which will cause the combined area to be unusable. @@ -4540,11 +4595,11 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, aState.mRightFloaterCombinedArea.x = aLine->mBounds.XMost(); CombineRects(aState.mRightFloaterCombinedArea, lineCombinedArea); - if (aState.mShrinkWrapWidth) { + if (aState.GetFlag(BRS_SHRINKWRAPWIDTH)) { // Mark the line dirty so we come back and re-place the floater once // the shrink wrap width is determined aLine->MarkDirty(); - aState.mNeedResizeReflow = PR_TRUE; + aState.SetFlag(BRS_NEEDRESIZEREFLOW, PR_TRUE); } } aLine->SetCombinedArea(lineCombinedArea); @@ -4629,7 +4684,7 @@ nsBlockFrame::PostPlaceLine(nsBlockReflowState& aState, } // Update max-element-size - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { aState.UpdateMaxElementSize(aMaxElementSize); // We also cache the max element width in the line. This is needed for // incremental reflow @@ -4639,7 +4694,7 @@ nsBlockFrame::PostPlaceLine(nsBlockReflowState& aState, // If this is an unconstrained reflow, then cache the line width in the // line. We'll need this during incremental reflow if we're asked to // calculate the maximum width - if (aState.mUnconstrainedWidth) { + if (aState.GetFlag(BRS_UNCONSTRAINEDWIDTH)) { aLine->mMaximumWidth = aLine->mBounds.XMost(); } @@ -4653,7 +4708,7 @@ nsBlockFrame::PostPlaceLine(nsBlockReflowState& aState, #endif // If we're shrink wrapping our width and the line was wrapped, // then make sure we take up all of the available width - if (aState.mShrinkWrapWidth && aLine->IsLineWrapped()) { + if (aState.GetFlag(BRS_SHRINKWRAPWIDTH) && aLine->IsLineWrapped()) { aState.mKidXMost = aState.BorderPadding().left + aState.mContentArea.width; } @@ -5187,6 +5242,10 @@ nsBlockFrame::DoRemoveFrame(nsIPresContext* aPresContext, // cases... nsRect lineCombinedArea; line->GetCombinedArea(&lineCombinedArea); +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 10 (%d, %d, %d, %d)\n", + this, lineCombinedArea.x, lineCombinedArea.y, lineCombinedArea.width, lineCombinedArea.height); +#endif Invalidate(aPresContext, lineCombinedArea); line->Destroy(presShell); line = next; @@ -5282,8 +5341,8 @@ nsBlockFrame::ReflowFloater(nsBlockReflowState& aState, // Setup block reflow state to reflow the floater nsBlockReflowContext brc(aState.mPresContext, aState.mReflowState, - aState.mComputeMaxElementSize, - aState.mComputeMaximumWidth); + aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE), + aState.GetFlag(BRS_COMPUTEMAXWIDTH)); brc.SetNextRCFrame(aState.mNextRCFrame); // Reflow the floater @@ -5324,7 +5383,7 @@ nsBlockFrame::ReflowFloater(nsBlockReflowState& aState, floater->DidReflow(aState.mPresContext, NS_FRAME_REFLOW_FINISHED); // If we computed it, then stash away the max-element-size for later - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { aState.StoreMaxElementSize(floater, brc.GetMaxElementSize()); } @@ -5389,7 +5448,7 @@ nsBlockReflowState::AddFloater(nsLineLayout& aLineLayout, // Pass on updated available space to the current inline reflow engine GetAvailableSpace(); aLineLayout.UpdateBand(mAvailSpaceRect.x + BorderPadding().left, mY, - mUnconstrainedWidth ? NS_UNCONSTRAINEDSIZE : mAvailSpaceRect.width, + GetFlag(BRS_UNCONSTRAINEDWIDTH) ? NS_UNCONSTRAINEDSIZE : mAvailSpaceRect.width, mAvailSpaceRect.height, isLeftFloater, aPlaceholder->GetOutOfFlowFrame()); @@ -5615,7 +5674,12 @@ nsBlockReflowState::PlaceFloater(nsFloaterCache* aFloaterCache, } else { isLeftFloater = PR_FALSE; - region.x = mAvailSpaceRect.XMost() - region.width; + if (NS_UNCONSTRAINEDSIZE != mAvailSpaceRect.XMost()) + region.x = mAvailSpaceRect.XMost() - region.width; + else { + okToAddRectRegion = PR_FALSE; + region.x = mAvailSpaceRect.x; + } } *aIsLeftFloater = isLeftFloater; const nsMargin& borderPadding = BorderPadding(); @@ -5682,7 +5746,8 @@ nsBlockReflowState::PlaceFloater(nsFloaterCache* aFloaterCache, nsRect combinedArea = aFloaterCache->mCombinedArea; combinedArea.x += x; combinedArea.y += y; - if (!isLeftFloater && (mUnconstrainedWidth || mShrinkWrapWidth)) { + if (!isLeftFloater && + (GetFlag(BRS_UNCONSTRAINEDWIDTH) || GetFlag(BRS_SHRINKWRAPWIDTH))) { // When we are placing a right floater in an unconstrained situation or // when shrink wrapping, we don't apply it to the floater combined area // immediately. Otherwise we end up with an infinitely wide combined diff --git a/layout/html/base/src/nsBlockReflowContext.cpp b/layout/html/base/src/nsBlockReflowContext.cpp index 7ef2cdee0d8d..8b2d17b0da81 100644 --- a/layout/html/base/src/nsBlockReflowContext.cpp +++ b/layout/html/base/src/nsBlockReflowContext.cpp @@ -197,10 +197,12 @@ nsBlockReflowContext::AlignBlockHorizontally(nscoord aWidth, // compatability cases. switch (styleText->mTextAlign) { case NS_STYLE_TEXT_ALIGN_MOZ_RIGHT: + case NS_STYLE_TEXT_ALIGN_RIGHT: aAlign.mXOffset += remainingSpace; doCSS = PR_FALSE; break; case NS_STYLE_TEXT_ALIGN_MOZ_CENTER: + case NS_STYLE_TEXT_ALIGN_CENTER: aAlign.mXOffset += remainingSpace / 2; doCSS = PR_FALSE; break; @@ -359,7 +361,11 @@ nsBlockReflowContext::ReflowBlock(nsIFrame* aFrame, reflowState.mComputedBorderPadding.right; } - x = aSpace.XMost() - mMargin.right - frameWidth; + // if this is an unconstrained width reflow, then just place the floater at the left margin + if (NS_UNCONSTRAINEDSIZE == aSpace.width) + x = aSpace.x; + else + x = aSpace.XMost() - mMargin.right - frameWidth; } else { x = aSpace.x + mMargin.left; diff --git a/layout/html/base/src/nsBlockReflowState.cpp b/layout/html/base/src/nsBlockReflowState.cpp index 554b851c4d28..5f974deb204e 100644 --- a/layout/html/base/src/nsBlockReflowState.cpp +++ b/layout/html/base/src/nsBlockReflowState.cpp @@ -18,6 +18,8 @@ * Rights Reserved. * * Contributor(s): + * Steve Clark + * Robert O'Callahan * Pierre Phaneuf */ #include "nsCOMPtr.h" @@ -61,6 +63,8 @@ #ifdef DEBUG +//#define NOISY_BLOCK_INVALIDATE // DO NOT CHECK THIS IN TURNED ON! + static PRBool gLamePaintMetrics; static PRBool gLameReflowMetrics; static PRBool gNoisy; @@ -451,12 +455,6 @@ public: nscoord mBottomEdge; - PRPackedBool mUnconstrainedWidth; - PRPackedBool mUnconstrainedHeight; - PRPackedBool mShrinkWrapWidth; - PRPackedBool mNeedResizeReflow; - PRPackedBool mIsInlineIncrReflow; - // The content area to reflow child frames within. The x/y // coordinates are known to be mBorderPadding.left and // mBorderPadding.top. The width/height may be NS_UNCONSTRAINEDSIZE @@ -464,15 +462,6 @@ public: // unconstrained area. nsSize mContentArea; - // Our wrapping behavior - PRPackedBool mNoWrap; - - // Is this frame a root for top/bottom margin collapsing? - PRPackedBool mIsTopMarginRoot, mIsBottomMarginRoot; - - // See ShouldApplyTopMargin - PRPackedBool mApplyTopMargin; - //---------------------------------------- // This state is "running" state updated by the reflow of each line @@ -541,15 +530,48 @@ public: // being N^2. nsFloaterCacheFreeList mBelowCurrentLineFloaters; - PRPackedBool mComputeMaxElementSize; - PRPackedBool mComputeMaximumWidth; - nsSize mMaxElementSize; nscoord mMaximumWidth; nscoord mMinLineHeight; PRInt32 mLineNumber; + + // block reflow state flags +#define BRS_UNCONSTRAINEDWIDTH 0x00000001 +#define BRS_UNCONSTRAINEDHEIGHT 0x00000002 +#define BRS_SHRINKWRAPWIDTH 0x00000004 +#define BRS_NEEDRESIZEREFLOW 0x00000008 +#define BRS_ISINLINEINCRREFLOW 0x00000010 +#define BRS_NOWRAP 0x00000020 +#define BRS_ISTOPMARGINROOT 0x00000040 // Is this frame a root for top/bottom margin collapsing? +#define BRS_ISBOTTOMMARGINROOT 0x00000080 +#define BRS_APPLYTOPMARGIN 0x00000100 // See ShouldApplyTopMargin +#define BRS_COMPUTEMAXELEMENTSIZE 0x00000200 +#define BRS_COMPUTEMAXWIDTH 0x00000400 +#define BRS_LASTFLAG BRS_COMPUTEMAXWIDTH + + PRInt16 mFlags; + + void SetFlag(PRUint32 aFlag, PRBool aValue) + { + NS_ASSERTION(aFlag<=BRS_LASTFLAG, "bad flag"); + NS_ASSERTION(aValue==PR_FALSE || aValue==PR_TRUE, "bad value"); + if (aValue) { // set flag + mFlags |= aFlag; + } + else { // unset flag + mFlags &= ~aFlag; + } + } + + PRBool GetFlag(PRUint32 aFlag) const + { + NS_ASSERTION(aFlag<=BRS_LASTFLAG, "bad flag"); + PRBool result = (mFlags & aFlag); + if (result) return PR_TRUE; + return PR_FALSE; + } }; // XXX This is vile. Make it go away @@ -574,29 +596,25 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, : mBlock(aFrame), mPresContext(aPresContext), mReflowState(aReflowState), - mNeedResizeReflow(PR_FALSE), - mIsInlineIncrReflow(PR_FALSE), - mIsTopMarginRoot(PR_FALSE), - mIsBottomMarginRoot(PR_FALSE), - mApplyTopMargin(PR_FALSE), mNextRCFrame(nsnull), mPrevBottomMargin(0), - mLineNumber(0) + mLineNumber(0), + mFlags(0) { const nsMargin& borderPadding = BorderPadding(); if (aBlockMarginRoot) { - mIsTopMarginRoot = PR_TRUE; - mIsBottomMarginRoot = PR_TRUE; + SetFlag(BRS_ISTOPMARGINROOT, PR_TRUE); + SetFlag(BRS_ISBOTTOMMARGINROOT, PR_TRUE); } if (0 != aReflowState.mComputedBorderPadding.top) { - mIsTopMarginRoot = PR_TRUE; + SetFlag(BRS_ISTOPMARGINROOT, PR_TRUE); } if (0 != aReflowState.mComputedBorderPadding.bottom) { - mIsBottomMarginRoot = PR_TRUE; + SetFlag(BRS_ISBOTTOMMARGINROOT, PR_TRUE); } - if (mIsTopMarginRoot) { - mApplyTopMargin = PR_TRUE; + if (GetFlag(BRS_ISTOPMARGINROOT)) { + SetFlag(BRS_APPLYTOPMARGIN, PR_TRUE); } mSpaceManager = aReflowState.mSpaceManager; @@ -617,21 +635,19 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, // Compute content area width (the content area is inside the border // and padding) - mUnconstrainedWidth = PR_FALSE; - mShrinkWrapWidth = PR_FALSE; if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedWidth) { mContentArea.width = aReflowState.mComputedWidth; } else { if (NS_UNCONSTRAINEDSIZE == aReflowState.availableWidth) { mContentArea.width = NS_UNCONSTRAINEDSIZE; - mUnconstrainedWidth = PR_TRUE; + SetFlag(BRS_UNCONSTRAINEDWIDTH, PR_TRUE); } else if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMaxWidth) { // Choose a width based on the content (shrink wrap width) up // to the maximum width mContentArea.width = aReflowState.mComputedMaxWidth; - mShrinkWrapWidth = PR_TRUE; + SetFlag(BRS_SHRINKWRAPWIDTH, PR_TRUE); } else { nscoord lr = borderPadding.left + borderPadding.right; @@ -646,7 +662,6 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, // specified style height then we may end up limiting our height if // the availableHeight is constrained (this situation occurs when we // are paginated). - mUnconstrainedHeight = PR_FALSE; if (NS_UNCONSTRAINEDSIZE != aReflowState.availableHeight) { // We are in a paginated situation. The bottom edge is just inside // the bottom border and padding. The content area height doesn't @@ -657,7 +672,7 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, else { // When we are not in a paginated situation then we always use // an constrained height. - mUnconstrainedHeight = PR_TRUE; + SetFlag(BRS_UNCONSTRAINEDHEIGHT, PR_TRUE); mContentArea.height = mBottomEdge = NS_UNCONSTRAINEDSIZE; } @@ -674,16 +689,17 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, switch (styleText->mWhiteSpace) { case NS_STYLE_WHITESPACE_PRE: case NS_STYLE_WHITESPACE_NOWRAP: - mNoWrap = PR_TRUE; + SetFlag(BRS_NOWRAP, PR_TRUE); break; default: - mNoWrap = PR_FALSE; + SetFlag(BRS_NOWRAP, PR_FALSE); break; } - mComputeMaxElementSize = nsnull != aMetrics.maxElementSize; + SetFlag(BRS_COMPUTEMAXELEMENTSIZE, (nsnull != aMetrics.maxElementSize)); mMaxElementSize.SizeTo(0, 0); - mComputeMaximumWidth = NS_REFLOW_CALC_MAX_WIDTH == (aMetrics.mFlags & NS_REFLOW_CALC_MAX_WIDTH); + SetFlag(BRS_COMPUTEMAXWIDTH, + (NS_REFLOW_CALC_MAX_WIDTH == (aMetrics.mFlags & NS_REFLOW_CALC_MAX_WIDTH))); mMaximumWidth = 0; mMinLineHeight = nsHTMLReflowState::CalcLineHeight(mPresContext, @@ -729,8 +745,12 @@ nsBlockReflowState::ComputeBlockAvailSpace(nsIFrame* aFrame, const nsStyleDisplay* aDisplay, nsRect& aResult) { +#ifdef REALLY_NOISY_REFLOW + printf("CBAS frame=%p has floater count %d\n", aFrame, mBand.GetFloaterCount()); + mBand.List(); +#endif aResult.y = mY; - aResult.height = mUnconstrainedHeight + aResult.height = GetFlag(BRS_UNCONSTRAINEDHEIGHT) ? NS_UNCONSTRAINEDSIZE : mBottomEdge - mY; @@ -749,7 +769,7 @@ nsBlockReflowState::ComputeBlockAvailSpace(nsIFrame* aFrame, // The child block will flow around the floater. Therefore // give it all of the available space. aResult.x = borderPadding.left; - aResult.width = mUnconstrainedWidth + aResult.width = GetFlag(BRS_UNCONSTRAINEDWIDTH) ? NS_UNCONSTRAINEDSIZE : mContentArea.width; break; @@ -776,7 +796,7 @@ nsBlockReflowState::ComputeBlockAvailSpace(nsIFrame* aFrame, } // determine width - if (mUnconstrainedWidth) { + if (GetFlag(BRS_UNCONSTRAINEDWIDTH)) { aResult.width = NS_UNCONSTRAINEDSIZE; } else { @@ -810,7 +830,7 @@ nsBlockReflowState::ComputeBlockAvailSpace(nsIFrame* aFrame, // doesn't matter therefore give the block element all of the // available space since it will flow around the floater itself. aResult.x = borderPadding.left; - aResult.width = mUnconstrainedWidth + aResult.width = GetFlag(BRS_UNCONSTRAINEDWIDTH) ? NS_UNCONSTRAINEDSIZE : mContentArea.width; } @@ -957,12 +977,12 @@ nsBlockReflowState::RecoverStateFrom(nsLineBox* aLine, #endif mKidXMost = xmost; } - if (mComputeMaxElementSize) { + if (GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { UpdateMaxElementSize(nsSize(aLine->mMaxElementWidth, aLine->mBounds.height)); } // If computing the maximum width, then update mMaximumWidth - if (mComputeMaximumWidth) { + if (GetFlag(BRS_COMPUTEMAXWIDTH)) { UpdateMaximumWidth(aLine->mMaximumWidth); } @@ -1454,6 +1474,7 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, nsBlockReflowState state(aReflowState, aPresContext, this, aMetrics, NS_BLOCK_MARGIN_ROOT & mState); + PRInt32 sizeofBRS = sizeof nsBlockReflowState; if (eReflowReason_Resize != aReflowState.reason) { RenumberLists(aPresContext); @@ -1462,7 +1483,7 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, nsresult rv = NS_OK; PRBool isStyleChange = PR_FALSE; - state.mIsInlineIncrReflow = PR_FALSE; + state.SetFlag(BRS_ISINLINEINCRREFLOW, PR_FALSE); nsIFrame* target; switch (aReflowState.reason) { case eReflowReason_Initial: @@ -1535,7 +1556,7 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, // reflow the line containing the target of the incr. reflow // first mark the line dirty and set up the state object rv = PrepareChildIncrementalReflow(state); - state.mIsInlineIncrReflow = PR_TRUE; + state.SetFlag(BRS_ISINLINEINCRREFLOW, PR_TRUE); state.mPrevLine = prevLine; state.mCurrentLine = line; state.mNextRCFrame = state.mNextRCFrame; @@ -1681,6 +1702,10 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, if (isStyleChange) { // Lots of things could have changed so damage our entire // bounds +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 1 (%d, %d, %d, %d)\n", + this, 0, 0, mRect.width, mRect.height); +#endif Invalidate(aPresContext, nsRect(0, 0, mRect.width, mRect.height)); } else { @@ -1707,6 +1732,10 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, damageRect.y = 0; damageRect.height = mRect.height; } +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 2 (%d, %d, %d, %d)\n", + this, damageRect.x, damageRect.y, damageRect.width, damageRect.height); +#endif Invalidate(aPresContext, damageRect); } @@ -1730,6 +1759,10 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, damageRect.y = mRect.height - border.bottom; damageRect.height = border.bottom; } +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 3 (%d, %d, %d, %d)\n", + this, damageRect.x, damageRect.y, damageRect.width, damageRect.height); +#endif Invalidate(aPresContext, damageRect); } } @@ -1868,7 +1901,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, #ifdef NOISY_FINAL_SIZE ListTag(stdout); printf(": mY=%d mIsBottomMarginRoot=%s mPrevBottomMargin=%d bp=%d,%d\n", - aState.mY, aState.mIsBottomMarginRoot ? "yes" : "no", + aState.mY, aState.GetFlag(BRS_ISBOTTOMMARGINROOT) ? "yes" : "no", aState.mPrevBottomMargin, borderPadding.top, borderPadding.bottom); #endif @@ -1946,7 +1979,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, // contents or we fluff out to the maximum block width. Note: // We always shrink wrap when given an unconstrained width. if ((0 == (NS_BLOCK_SHRINK_WRAP & mState)) && - !aState.mUnconstrainedWidth && !aState.mShrinkWrapWidth && + !aState.GetFlag(BRS_UNCONSTRAINEDWIDTH) && !aState.GetFlag(BRS_SHRINKWRAPWIDTH) && !compact) { // Set our width to the max width if we aren't already that // wide. Note that the max-width has nothing to do with our @@ -1956,9 +1989,9 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, } // See if we should compute our max element size - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { // Adjust the computedWidth - if (aState.mNoWrap) { + if (aState.GetFlag(BRS_NOWRAP)) { // When no-wrap is true the max-element-size.width is the // width of the widest line plus the right border. Note that // aState.mKidXMost already has the left border factored in @@ -1996,7 +2029,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, // need to do horizontal alignment of the inline lines and make sure // blocks are correctly sized and positioned. Any lines that need // final adjustment will have been marked as dirty - if (aState.mShrinkWrapWidth && aState.mNeedResizeReflow) { + if (aState.GetFlag(BRS_SHRINKWRAPWIDTH) && aState.GetFlag(BRS_NEEDRESIZEREFLOW)) { // If the parent reflow state is also shrink wrap width, then // we don't need to do this, because it will reflow us after it // calculates the final width @@ -2025,7 +2058,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, } } - if (aState.mShrinkWrapWidth) { + if (aState.GetFlag(BRS_SHRINKWRAPWIDTH)) { PRBool parentIsShrinkWrapWidth = PR_FALSE; if (aReflowState.parentReflowState) { if (NS_SHRINKWRAPWIDTH == aReflowState.parentReflowState->mComputedWidth) { @@ -2047,7 +2080,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, // Don't carry out a bottom margin when our height is fixed // unless the bottom of the last line adjoins the bottom of our // content area. - if (!aState.mIsBottomMarginRoot) { + if (!aState.GetFlag(BRS_ISBOTTOMMARGINROOT)) { if (aState.mY + aState.mPrevBottomMargin != aMetrics.height) { aState.mPrevBottomMargin = 0; } @@ -2057,7 +2090,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, nscoord autoHeight = aState.mY; // Shrink wrap our height around our contents. - if (aState.mIsBottomMarginRoot) { + if (aState.GetFlag(BRS_ISBOTTOMMARGINROOT)) { // When we are a bottom-margin root make sure that our last // childs bottom margin is fully applied. // XXX check for a fit @@ -2082,7 +2115,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, } aMetrics.height = autoHeight; - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { maxHeight = aState.mMaxElementSize.height + borderPadding.top + borderPadding.bottom; } @@ -2090,7 +2123,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, aMetrics.ascent = aMetrics.height; aMetrics.descent = 0; - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { // Store away the final value aMetrics.maxElementSize->width = maxWidth; aMetrics.maxElementSize->height = maxHeight; @@ -2098,14 +2131,14 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, // Return bottom margin information aMetrics.mCarriedOutBottomMargin = - aState.mIsBottomMarginRoot ? 0 : aState.mPrevBottomMargin; + aState.GetFlag(BRS_ISBOTTOMMARGINROOT) ? 0 : aState.mPrevBottomMargin; #ifdef DEBUG if (CRAZY_WIDTH(aMetrics.width) || CRAZY_HEIGHT(aMetrics.height)) { ListTag(stdout); printf(": WARNING: desired:%d,%d\n", aMetrics.width, aMetrics.height); } - if (aState.mComputeMaxElementSize && + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE) && ((maxWidth > aMetrics.width) || (maxHeight > aMetrics.height))) { ListTag(stdout); printf(": WARNING: max-element-size:%d,%d desired:%d,%d maxSize:%d,%d\n", @@ -2115,7 +2148,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, } #endif #ifdef NOISY_MAX_ELEMENT_SIZE - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { IndentBy(stdout, GetDepth()); if (NS_UNCONSTRAINEDSIZE == aState.mReflowState.availableWidth) { printf("PASS1 "); @@ -2130,7 +2163,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, } // If we're requested to update our maximum width, then compute it - if (aState.mComputeMaximumWidth) { + if (aState.GetFlag(BRS_COMPUTEMAXWIDTH)) { // We need to add in for the right border/padding aMetrics.mMaximumWidth = aState.mMaximumWidth + borderPadding.right; } @@ -2337,8 +2370,13 @@ nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState) // See if we can try and avoid marking all the lines as dirty PRBool tryAndSkipLines = PR_FALSE; - // See if this is this a constrained resize reflow - if ((aState.mReflowState.reason == eReflowReason_Resize) && + // we need to calculate if any part of then block itself + // is impacted by a floater (bug 19579) + aState.GetAvailableSpace(); + + // See if this is this a constrained resize reflow that is not impacted by floaters + if ((PR_FALSE==aState.IsImpactedByFloater()) && + (aState.mReflowState.reason == eReflowReason_Resize) && (NS_UNCONSTRAINEDSIZE != aState.mReflowState.availableWidth)) { // If the text is left-aligned, then we try and avoid reflowing the lines @@ -2389,7 +2427,7 @@ nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState) } #endif - PRBool notWrapping = aState.mNoWrap; + PRBool notWrapping = aState.GetFlag(BRS_NOWRAP); while (nsnull != line) { if (line->IsBlock()) { @@ -2402,8 +2440,6 @@ nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState) printf("PrepareResizeReflow thinks line %p is %simpacted by floaters\n", line, line->IsImpactedByFloater() ? "" : "not "); #endif - - if (notWrapping) { // When no-wrap is set then the only line-breaking that // occurs for inline lines is triggered by BR elements or by @@ -2430,7 +2466,7 @@ nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState) printf("skipped: line=%p next=%p %s %s %s%s%s breakType=%d xmost=%d\n", line, line->mNext, line->IsBlock() ? "block" : "inline", - aState.mNoWrap ? "no-wrap" : "wrapping", + aState.GetFlag(BRS_NOWRAP) ? "no-wrap" : "wrapping", line->HasBreak() ? "has-break " : "", line->HasFloaters() ? "has-floaters " : "", line->IsImpactedByFloater() ? "impacted " : "", @@ -2628,7 +2664,7 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) ListTag(stdout); printf(": incrementally reflowing dirty lines: type=%s(%d) isInline=%s", kReflowCommandType[type], type, - aState.mIsInlineIncrReflow ? "true" : "false"); + aState.GetFlag(BRS_ISINLINEINCRREFLOW) ? "true" : "false"); } else { IndentBy(stdout, gNoiseIndent); @@ -2651,7 +2687,7 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) // Reflow the lines that are already ours aState.mPrevLine = nsnull; nsLineBox* line = mLines; - if (aState.mIsInlineIncrReflow && aState.mNextRCFrame) + if (aState.GetFlag(BRS_ISINLINEINCRREFLOW) && aState.mNextRCFrame) { const nsLineBox* incrTargetLine = aState.mCurrentLine; aState.mCurrentLine = line; @@ -2662,6 +2698,10 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) RecoverStateFrom(aState, line, deltaY, incrementalReflow ? &damageRect : 0); if (incrementalReflow && !damageRect.IsEmpty()) { +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 4 (%d, %d, %d, %d)\n", + this, damageRect.x, damageRect.y, damageRect.width, damageRect.height); +#endif Invalidate(aState.mPresContext, damageRect); } aState.mPrevLine = line; @@ -2689,7 +2729,7 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) // If we're supposed to update our maximum width, then we'll also need to // reflow this line if it's line wrapped and any of the continuing lines // are dirty - if (line->IsDirty() || (aState.mComputeMaximumWidth && ::WrappedLinesAreDirty(line))) { + if (line->IsDirty() || (aState.GetFlag(BRS_COMPUTEMAXWIDTH) && ::WrappedLinesAreDirty(line))) { // Compute the dirty lines "before" YMost, after factoring in // the running deltaY value - the running value is implicit in // aState.mY. @@ -2729,6 +2769,10 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) RecoverStateFrom(aState, line, deltaY, incrementalReflow ? &damageRect : 0); if (incrementalReflow && !damageRect.IsEmpty()) { +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 5 (%d, %d, %d, %d)\n", + this, damageRect.x, damageRect.y, damageRect.width, damageRect.height); +#endif Invalidate(aState.mPresContext, damageRect); } } @@ -2902,6 +2946,10 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, // XXX We need to improve on this... nsRect dirtyRect; dirtyRect.UnionRect(oldCombinedArea, lineCombinedArea); +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 6 (%d, %d, %d, %d)\n", + this, dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height); +#endif Invalidate(aState.mPresContext, dirtyRect); } else { @@ -2918,6 +2966,10 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, dirtyRect.x; dirtyRect.height = PR_MAX(oldCombinedArea.height, lineCombinedArea.height); +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 7 (%d, %d, %d, %d)\n", + this, dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height); +#endif Invalidate(aState.mPresContext, dirtyRect); } if (oldCombinedArea.height != lineCombinedArea.height) { @@ -2933,6 +2985,10 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, dirtyRect.height = PR_MAX(oldCombinedArea.YMost(), lineCombinedArea.YMost()) - dirtyRect.y; +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 8 (%d, %d, %d, %d)\n", + this, dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height); +#endif Invalidate(aState.mPresContext, dirtyRect); } } @@ -2951,21 +3007,21 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, // we'll either need to recover the floater state that applies to the // unconstrained reflow or keep it around in a separate space manager... PRBool isBeginningLine = !aState.mPrevLine || !aState.mPrevLine->IsLineWrapped(); - if (aState.mComputeMaximumWidth && isBeginningLine) { + if (aState.GetFlag(BRS_COMPUTEMAXWIDTH) && isBeginningLine) { nscoord oldY = aState.mY; nscoord oldPrevBottomMargin = aState.mPrevBottomMargin; - PRBool oldUnconstrainedWidth = aState.mUnconstrainedWidth; + PRBool oldUnconstrainedWidth = aState.GetFlag(BRS_UNCONSTRAINEDWIDTH); // First reflow the line with an unconstrained width. When doing this // we need to set the block reflow state's "mUnconstrainedWidth" variable // to PR_TRUE so if we encounter a placeholder and then reflow its // associated floater we don't end up resetting the line's right edge and // have it think the width is unconstrained... - aState.mUnconstrainedWidth = PR_TRUE; + aState.SetFlag(BRS_UNCONSTRAINEDWIDTH, PR_TRUE); ReflowInlineFrames(aState, aLine, aKeepReflowGoing, PR_TRUE); aState.mY = oldY; aState.mPrevBottomMargin = oldPrevBottomMargin; - aState.mUnconstrainedWidth = oldUnconstrainedWidth; + aState.SetFlag(BRS_UNCONSTRAINEDWIDTH, oldUnconstrainedWidth); // Update the line's maximum width aLine->mMaximumWidth = aLine->mBounds.XMost(); @@ -2980,14 +3036,14 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, // Note: we need to reset both member variables, because the inline // code examines mComputeMaxElementSize and if there is a placeholder // on this line the code to reflow the floater looks at both... - nscoord oldComputeMaxElementSize = aState.mComputeMaxElementSize; - nscoord oldComputeMaximumWidth = aState.mComputeMaximumWidth; + nscoord oldComputeMaxElementSize = aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE); + nscoord oldComputeMaximumWidth = aState.GetFlag(BRS_COMPUTEMAXWIDTH); - aState.mComputeMaxElementSize = PR_FALSE; - aState.mComputeMaximumWidth = PR_FALSE; + aState.SetFlag(BRS_COMPUTEMAXELEMENTSIZE, PR_FALSE); + aState.SetFlag(BRS_COMPUTEMAXWIDTH, PR_FALSE); rv = ReflowInlineFrames(aState, aLine, aKeepReflowGoing); - aState.mComputeMaxElementSize = oldComputeMaxElementSize; - aState.mComputeMaximumWidth = oldComputeMaximumWidth; + aState.SetFlag(BRS_COMPUTEMAXELEMENTSIZE, oldComputeMaxElementSize); + aState.SetFlag(BRS_COMPUTEMAXWIDTH, oldComputeMaximumWidth); } else { rv = ReflowInlineFrames(aState, aLine, aKeepReflowGoing); @@ -3001,6 +3057,10 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, nsRect dirtyRect; dirtyRect.UnionRect(oldCombinedArea, combinedArea); +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 9 (%d, %d, %d, %d)\n", + this, dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height); +#endif Invalidate(aState.mPresContext, dirtyRect); } } @@ -3368,7 +3428,7 @@ PRBool nsBlockFrame::ShouldApplyTopMargin(nsBlockReflowState& aState, nsLineBox* aLine) { - if (aState.mApplyTopMargin) { + if (aState.GetFlag(BRS_APPLYTOPMARGIN)) { // Apply short-circuit check to avoid searching the line list return PR_TRUE; } @@ -3377,7 +3437,7 @@ nsBlockFrame::ShouldApplyTopMargin(nsBlockReflowState& aState, // If we aren't at the top Y coordinate then something of non-zero // height must have been placed. Therefore the childs top-margin // applies. - aState.mApplyTopMargin = PR_TRUE; + aState.SetFlag(BRS_APPLYTOPMARGIN, PR_TRUE); return PR_TRUE; } @@ -3387,13 +3447,13 @@ nsBlockFrame::ShouldApplyTopMargin(nsBlockReflowState& aState, if (line->IsBlock()) { // A line which preceeds aLine contains a block; therefore the // top margin applies. - aState.mApplyTopMargin = PR_TRUE; + aState.SetFlag(BRS_APPLYTOPMARGIN, PR_TRUE); return PR_TRUE; } else if (line->HasFloaters()) { // A line which preceeds aLine is not empty therefore the top // margin applies. - aState.mApplyTopMargin = PR_TRUE; + aState.SetFlag(BRS_APPLYTOPMARGIN, PR_TRUE); return PR_TRUE; } line = line->mNext; @@ -3486,8 +3546,8 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, frame->GetStyleData(eStyleStruct_Display, (const nsStyleStruct*&) display); nsBlockReflowContext brc(aState.mPresContext, aState.mReflowState, - aState.mComputeMaxElementSize, - aState.mComputeMaximumWidth); + aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE), + aState.GetFlag(BRS_COMPUTEMAXWIDTH)); brc.SetNextRCFrame(aState.mNextRCFrame); // See if we should apply the top margin. If the block frame being @@ -3611,14 +3671,14 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, &collapsedBottomMargin, aLine->mBounds, combinedArea); - if (aState.mShrinkWrapWidth) { + if (aState.GetFlag(BRS_SHRINKWRAPWIDTH)) { // Mark the line as block so once we known the final shrink wrap width // we can reflow the block to the correct size // XXX We don't always need to do this... aLine->MarkDirty(); - aState.mNeedResizeReflow = PR_TRUE; + aState.SetFlag(BRS_NEEDRESIZEREFLOW, PR_TRUE); } - if (aState.mUnconstrainedWidth || aState.mShrinkWrapWidth) { + if (aState.GetFlag(BRS_UNCONSTRAINEDWIDTH) || aState.GetFlag(BRS_SHRINKWRAPWIDTH)) { // Add the right margin to the line's bounnds. That way it will be taken into // account when we compute our shrink wrap size nscoord marginRight = brc.GetMargin().right; @@ -3709,7 +3769,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, // Post-process the "line" nsSize maxElementSize(0, 0); - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { maxElementSize = brc.GetMaxElementSize(); if (aState.IsImpactedByFloater() && (NS_FRAME_SPLITTABLE_NON_RECTANGULAR != splitType)) { @@ -3721,7 +3781,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, } // If we asked the block to update its maximum width, then record the // updated value in the line, and update the current maximum width - if (aState.mComputeMaximumWidth) { + if (aState.GetFlag(BRS_COMPUTEMAXWIDTH)) { aLine->mMaximumWidth = brc.GetMaximumWidth(); aState.UpdateMaximumWidth(aLine->mMaximumWidth); @@ -3849,7 +3909,7 @@ nsBlockFrame::DoReflowInlineFramesMalloc(nsBlockReflowState& aState, nsLineLayout* ll = new nsLineLayout(aState.mPresContext, aState.mReflowState.mSpaceManager, &aState.mReflowState, - aState.mComputeMaxElementSize); + aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)); if (!ll) { return NS_ERROR_OUT_OF_MEMORY; } @@ -3872,7 +3932,7 @@ nsBlockFrame::DoReflowInlineFramesAuto(nsBlockReflowState& aState, nsLineLayout lineLayout(aState.mPresContext, aState.mReflowState.mSpaceManager, &aState.mReflowState, - aState.mComputeMaxElementSize); + aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)); lineLayout.Init(&aState, aState.mMinLineHeight, aState.mLineNumber); lineLayout.SetReflowTextRuns(mTextRuns); nsresult rv = DoReflowInlineFrames(aState, lineLayout, aLine, @@ -3910,7 +3970,7 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState, nscoord x = aState.mAvailSpaceRect.x + borderPadding.left; nscoord availWidth = aState.mAvailSpaceRect.width; nscoord availHeight; - if (aState.mUnconstrainedHeight) { + if (aState.GetFlag(BRS_UNCONSTRAINEDHEIGHT)) { availHeight = NS_UNCONSTRAINEDSIZE; } else { @@ -4359,7 +4419,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, nsSize maxElementSize; aLineLayout.VerticalAlignFrames(aLine, maxElementSize); // See if we're shrink wrapping the width - if (aState.mShrinkWrapWidth) { + if (aState.GetFlag(BRS_SHRINKWRAPWIDTH)) { // When determining the line's width we also need to include any // right floaters that impact us. This represents the shrink wrap // width of the line @@ -4388,25 +4448,20 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, // Only block frames horizontally align their children because // inline frames "shrink-wrap" around their children (therefore // there is no extra horizontal space). -#if XXX_fix_me - PRBool allowJustify = PR_TRUE; - if (NS_STYLE_TEXT_ALIGN_JUSTIFY == aState.mStyleText->mTextAlign) { - allowJustify = ShouldJustifyLine(aState, aLine); - } -#else - PRBool allowJustify = PR_FALSE; -#endif - - PRBool successful; - nsRect combinedArea; - successful = aLineLayout.HorizontalAlignFrames(aLine->mBounds, allowJustify, - aState.mShrinkWrapWidth); + const nsStyleText* styleText = (const nsStyleText*) + mStyleContext->GetStyleData(eStyleStruct_Text); + PRBool allowJustify = NS_STYLE_TEXT_ALIGN_JUSTIFY == styleText->mTextAlign + && !aLineLayout.GetLineEndsInBR() && ShouldJustifyLine(aState, aLine); + PRBool successful = aLineLayout.HorizontalAlignFrames(aLine->mBounds, allowJustify, + aState.GetFlag(BRS_SHRINKWRAPWIDTH)); if (!successful) { // Mark the line dirty and then later once we've determined the width // we can do the horizontal alignment aLine->MarkDirty(); - aState.mNeedResizeReflow = PR_TRUE; + aState.SetFlag(BRS_NEEDRESIZEREFLOW, PR_TRUE); } + + nsRect combinedArea; aLineLayout.RelativePositionFrames(combinedArea); aLine->SetCombinedArea(combinedArea); if (addedBullet) { @@ -4454,7 +4509,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, } aState.mY = newY; - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { #ifdef NOISY_MAX_ELEMENT_SIZE IndentBy(stdout, GetDepth()); if (NS_UNCONSTRAINEDSIZE == aState.mReflowState.availableWidth) { @@ -4478,7 +4533,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, // we don't want updated... if (aUpdateMaximumWidth) { // However, we do need to update the max-element-size if requested - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { aState.UpdateMaxElementSize(maxElementSize); // We also cache the max element width in the line. This is needed for // incremental reflow @@ -4519,7 +4574,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, CombineRects(aState.mFloaterCombinedArea, lineCombinedArea); if (aState.mHaveRightFloaters && - (aState.mUnconstrainedWidth || aState.mShrinkWrapWidth)) { + (aState.GetFlag(BRS_UNCONSTRAINEDWIDTH) || aState.GetFlag(BRS_SHRINKWRAPWIDTH))) { // We are reflowing in an unconstrained situation or shrink wrapping and // have some right floaters. They were placed at the infinite right edge // which will cause the combined area to be unusable. @@ -4540,11 +4595,11 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, aState.mRightFloaterCombinedArea.x = aLine->mBounds.XMost(); CombineRects(aState.mRightFloaterCombinedArea, lineCombinedArea); - if (aState.mShrinkWrapWidth) { + if (aState.GetFlag(BRS_SHRINKWRAPWIDTH)) { // Mark the line dirty so we come back and re-place the floater once // the shrink wrap width is determined aLine->MarkDirty(); - aState.mNeedResizeReflow = PR_TRUE; + aState.SetFlag(BRS_NEEDRESIZEREFLOW, PR_TRUE); } } aLine->SetCombinedArea(lineCombinedArea); @@ -4629,7 +4684,7 @@ nsBlockFrame::PostPlaceLine(nsBlockReflowState& aState, } // Update max-element-size - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { aState.UpdateMaxElementSize(aMaxElementSize); // We also cache the max element width in the line. This is needed for // incremental reflow @@ -4639,7 +4694,7 @@ nsBlockFrame::PostPlaceLine(nsBlockReflowState& aState, // If this is an unconstrained reflow, then cache the line width in the // line. We'll need this during incremental reflow if we're asked to // calculate the maximum width - if (aState.mUnconstrainedWidth) { + if (aState.GetFlag(BRS_UNCONSTRAINEDWIDTH)) { aLine->mMaximumWidth = aLine->mBounds.XMost(); } @@ -4653,7 +4708,7 @@ nsBlockFrame::PostPlaceLine(nsBlockReflowState& aState, #endif // If we're shrink wrapping our width and the line was wrapped, // then make sure we take up all of the available width - if (aState.mShrinkWrapWidth && aLine->IsLineWrapped()) { + if (aState.GetFlag(BRS_SHRINKWRAPWIDTH) && aLine->IsLineWrapped()) { aState.mKidXMost = aState.BorderPadding().left + aState.mContentArea.width; } @@ -5187,6 +5242,10 @@ nsBlockFrame::DoRemoveFrame(nsIPresContext* aPresContext, // cases... nsRect lineCombinedArea; line->GetCombinedArea(&lineCombinedArea); +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 10 (%d, %d, %d, %d)\n", + this, lineCombinedArea.x, lineCombinedArea.y, lineCombinedArea.width, lineCombinedArea.height); +#endif Invalidate(aPresContext, lineCombinedArea); line->Destroy(presShell); line = next; @@ -5282,8 +5341,8 @@ nsBlockFrame::ReflowFloater(nsBlockReflowState& aState, // Setup block reflow state to reflow the floater nsBlockReflowContext brc(aState.mPresContext, aState.mReflowState, - aState.mComputeMaxElementSize, - aState.mComputeMaximumWidth); + aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE), + aState.GetFlag(BRS_COMPUTEMAXWIDTH)); brc.SetNextRCFrame(aState.mNextRCFrame); // Reflow the floater @@ -5324,7 +5383,7 @@ nsBlockFrame::ReflowFloater(nsBlockReflowState& aState, floater->DidReflow(aState.mPresContext, NS_FRAME_REFLOW_FINISHED); // If we computed it, then stash away the max-element-size for later - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { aState.StoreMaxElementSize(floater, brc.GetMaxElementSize()); } @@ -5389,7 +5448,7 @@ nsBlockReflowState::AddFloater(nsLineLayout& aLineLayout, // Pass on updated available space to the current inline reflow engine GetAvailableSpace(); aLineLayout.UpdateBand(mAvailSpaceRect.x + BorderPadding().left, mY, - mUnconstrainedWidth ? NS_UNCONSTRAINEDSIZE : mAvailSpaceRect.width, + GetFlag(BRS_UNCONSTRAINEDWIDTH) ? NS_UNCONSTRAINEDSIZE : mAvailSpaceRect.width, mAvailSpaceRect.height, isLeftFloater, aPlaceholder->GetOutOfFlowFrame()); @@ -5615,7 +5674,12 @@ nsBlockReflowState::PlaceFloater(nsFloaterCache* aFloaterCache, } else { isLeftFloater = PR_FALSE; - region.x = mAvailSpaceRect.XMost() - region.width; + if (NS_UNCONSTRAINEDSIZE != mAvailSpaceRect.XMost()) + region.x = mAvailSpaceRect.XMost() - region.width; + else { + okToAddRectRegion = PR_FALSE; + region.x = mAvailSpaceRect.x; + } } *aIsLeftFloater = isLeftFloater; const nsMargin& borderPadding = BorderPadding(); @@ -5682,7 +5746,8 @@ nsBlockReflowState::PlaceFloater(nsFloaterCache* aFloaterCache, nsRect combinedArea = aFloaterCache->mCombinedArea; combinedArea.x += x; combinedArea.y += y; - if (!isLeftFloater && (mUnconstrainedWidth || mShrinkWrapWidth)) { + if (!isLeftFloater && + (GetFlag(BRS_UNCONSTRAINEDWIDTH) || GetFlag(BRS_SHRINKWRAPWIDTH))) { // When we are placing a right floater in an unconstrained situation or // when shrink wrapping, we don't apply it to the floater combined area // immediately. Otherwise we end up with an infinitely wide combined diff --git a/layout/html/base/src/nsBlockReflowState.h b/layout/html/base/src/nsBlockReflowState.h index 554b851c4d28..5f974deb204e 100644 --- a/layout/html/base/src/nsBlockReflowState.h +++ b/layout/html/base/src/nsBlockReflowState.h @@ -18,6 +18,8 @@ * Rights Reserved. * * Contributor(s): + * Steve Clark + * Robert O'Callahan * Pierre Phaneuf */ #include "nsCOMPtr.h" @@ -61,6 +63,8 @@ #ifdef DEBUG +//#define NOISY_BLOCK_INVALIDATE // DO NOT CHECK THIS IN TURNED ON! + static PRBool gLamePaintMetrics; static PRBool gLameReflowMetrics; static PRBool gNoisy; @@ -451,12 +455,6 @@ public: nscoord mBottomEdge; - PRPackedBool mUnconstrainedWidth; - PRPackedBool mUnconstrainedHeight; - PRPackedBool mShrinkWrapWidth; - PRPackedBool mNeedResizeReflow; - PRPackedBool mIsInlineIncrReflow; - // The content area to reflow child frames within. The x/y // coordinates are known to be mBorderPadding.left and // mBorderPadding.top. The width/height may be NS_UNCONSTRAINEDSIZE @@ -464,15 +462,6 @@ public: // unconstrained area. nsSize mContentArea; - // Our wrapping behavior - PRPackedBool mNoWrap; - - // Is this frame a root for top/bottom margin collapsing? - PRPackedBool mIsTopMarginRoot, mIsBottomMarginRoot; - - // See ShouldApplyTopMargin - PRPackedBool mApplyTopMargin; - //---------------------------------------- // This state is "running" state updated by the reflow of each line @@ -541,15 +530,48 @@ public: // being N^2. nsFloaterCacheFreeList mBelowCurrentLineFloaters; - PRPackedBool mComputeMaxElementSize; - PRPackedBool mComputeMaximumWidth; - nsSize mMaxElementSize; nscoord mMaximumWidth; nscoord mMinLineHeight; PRInt32 mLineNumber; + + // block reflow state flags +#define BRS_UNCONSTRAINEDWIDTH 0x00000001 +#define BRS_UNCONSTRAINEDHEIGHT 0x00000002 +#define BRS_SHRINKWRAPWIDTH 0x00000004 +#define BRS_NEEDRESIZEREFLOW 0x00000008 +#define BRS_ISINLINEINCRREFLOW 0x00000010 +#define BRS_NOWRAP 0x00000020 +#define BRS_ISTOPMARGINROOT 0x00000040 // Is this frame a root for top/bottom margin collapsing? +#define BRS_ISBOTTOMMARGINROOT 0x00000080 +#define BRS_APPLYTOPMARGIN 0x00000100 // See ShouldApplyTopMargin +#define BRS_COMPUTEMAXELEMENTSIZE 0x00000200 +#define BRS_COMPUTEMAXWIDTH 0x00000400 +#define BRS_LASTFLAG BRS_COMPUTEMAXWIDTH + + PRInt16 mFlags; + + void SetFlag(PRUint32 aFlag, PRBool aValue) + { + NS_ASSERTION(aFlag<=BRS_LASTFLAG, "bad flag"); + NS_ASSERTION(aValue==PR_FALSE || aValue==PR_TRUE, "bad value"); + if (aValue) { // set flag + mFlags |= aFlag; + } + else { // unset flag + mFlags &= ~aFlag; + } + } + + PRBool GetFlag(PRUint32 aFlag) const + { + NS_ASSERTION(aFlag<=BRS_LASTFLAG, "bad flag"); + PRBool result = (mFlags & aFlag); + if (result) return PR_TRUE; + return PR_FALSE; + } }; // XXX This is vile. Make it go away @@ -574,29 +596,25 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, : mBlock(aFrame), mPresContext(aPresContext), mReflowState(aReflowState), - mNeedResizeReflow(PR_FALSE), - mIsInlineIncrReflow(PR_FALSE), - mIsTopMarginRoot(PR_FALSE), - mIsBottomMarginRoot(PR_FALSE), - mApplyTopMargin(PR_FALSE), mNextRCFrame(nsnull), mPrevBottomMargin(0), - mLineNumber(0) + mLineNumber(0), + mFlags(0) { const nsMargin& borderPadding = BorderPadding(); if (aBlockMarginRoot) { - mIsTopMarginRoot = PR_TRUE; - mIsBottomMarginRoot = PR_TRUE; + SetFlag(BRS_ISTOPMARGINROOT, PR_TRUE); + SetFlag(BRS_ISBOTTOMMARGINROOT, PR_TRUE); } if (0 != aReflowState.mComputedBorderPadding.top) { - mIsTopMarginRoot = PR_TRUE; + SetFlag(BRS_ISTOPMARGINROOT, PR_TRUE); } if (0 != aReflowState.mComputedBorderPadding.bottom) { - mIsBottomMarginRoot = PR_TRUE; + SetFlag(BRS_ISBOTTOMMARGINROOT, PR_TRUE); } - if (mIsTopMarginRoot) { - mApplyTopMargin = PR_TRUE; + if (GetFlag(BRS_ISTOPMARGINROOT)) { + SetFlag(BRS_APPLYTOPMARGIN, PR_TRUE); } mSpaceManager = aReflowState.mSpaceManager; @@ -617,21 +635,19 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, // Compute content area width (the content area is inside the border // and padding) - mUnconstrainedWidth = PR_FALSE; - mShrinkWrapWidth = PR_FALSE; if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedWidth) { mContentArea.width = aReflowState.mComputedWidth; } else { if (NS_UNCONSTRAINEDSIZE == aReflowState.availableWidth) { mContentArea.width = NS_UNCONSTRAINEDSIZE; - mUnconstrainedWidth = PR_TRUE; + SetFlag(BRS_UNCONSTRAINEDWIDTH, PR_TRUE); } else if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMaxWidth) { // Choose a width based on the content (shrink wrap width) up // to the maximum width mContentArea.width = aReflowState.mComputedMaxWidth; - mShrinkWrapWidth = PR_TRUE; + SetFlag(BRS_SHRINKWRAPWIDTH, PR_TRUE); } else { nscoord lr = borderPadding.left + borderPadding.right; @@ -646,7 +662,6 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, // specified style height then we may end up limiting our height if // the availableHeight is constrained (this situation occurs when we // are paginated). - mUnconstrainedHeight = PR_FALSE; if (NS_UNCONSTRAINEDSIZE != aReflowState.availableHeight) { // We are in a paginated situation. The bottom edge is just inside // the bottom border and padding. The content area height doesn't @@ -657,7 +672,7 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, else { // When we are not in a paginated situation then we always use // an constrained height. - mUnconstrainedHeight = PR_TRUE; + SetFlag(BRS_UNCONSTRAINEDHEIGHT, PR_TRUE); mContentArea.height = mBottomEdge = NS_UNCONSTRAINEDSIZE; } @@ -674,16 +689,17 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, switch (styleText->mWhiteSpace) { case NS_STYLE_WHITESPACE_PRE: case NS_STYLE_WHITESPACE_NOWRAP: - mNoWrap = PR_TRUE; + SetFlag(BRS_NOWRAP, PR_TRUE); break; default: - mNoWrap = PR_FALSE; + SetFlag(BRS_NOWRAP, PR_FALSE); break; } - mComputeMaxElementSize = nsnull != aMetrics.maxElementSize; + SetFlag(BRS_COMPUTEMAXELEMENTSIZE, (nsnull != aMetrics.maxElementSize)); mMaxElementSize.SizeTo(0, 0); - mComputeMaximumWidth = NS_REFLOW_CALC_MAX_WIDTH == (aMetrics.mFlags & NS_REFLOW_CALC_MAX_WIDTH); + SetFlag(BRS_COMPUTEMAXWIDTH, + (NS_REFLOW_CALC_MAX_WIDTH == (aMetrics.mFlags & NS_REFLOW_CALC_MAX_WIDTH))); mMaximumWidth = 0; mMinLineHeight = nsHTMLReflowState::CalcLineHeight(mPresContext, @@ -729,8 +745,12 @@ nsBlockReflowState::ComputeBlockAvailSpace(nsIFrame* aFrame, const nsStyleDisplay* aDisplay, nsRect& aResult) { +#ifdef REALLY_NOISY_REFLOW + printf("CBAS frame=%p has floater count %d\n", aFrame, mBand.GetFloaterCount()); + mBand.List(); +#endif aResult.y = mY; - aResult.height = mUnconstrainedHeight + aResult.height = GetFlag(BRS_UNCONSTRAINEDHEIGHT) ? NS_UNCONSTRAINEDSIZE : mBottomEdge - mY; @@ -749,7 +769,7 @@ nsBlockReflowState::ComputeBlockAvailSpace(nsIFrame* aFrame, // The child block will flow around the floater. Therefore // give it all of the available space. aResult.x = borderPadding.left; - aResult.width = mUnconstrainedWidth + aResult.width = GetFlag(BRS_UNCONSTRAINEDWIDTH) ? NS_UNCONSTRAINEDSIZE : mContentArea.width; break; @@ -776,7 +796,7 @@ nsBlockReflowState::ComputeBlockAvailSpace(nsIFrame* aFrame, } // determine width - if (mUnconstrainedWidth) { + if (GetFlag(BRS_UNCONSTRAINEDWIDTH)) { aResult.width = NS_UNCONSTRAINEDSIZE; } else { @@ -810,7 +830,7 @@ nsBlockReflowState::ComputeBlockAvailSpace(nsIFrame* aFrame, // doesn't matter therefore give the block element all of the // available space since it will flow around the floater itself. aResult.x = borderPadding.left; - aResult.width = mUnconstrainedWidth + aResult.width = GetFlag(BRS_UNCONSTRAINEDWIDTH) ? NS_UNCONSTRAINEDSIZE : mContentArea.width; } @@ -957,12 +977,12 @@ nsBlockReflowState::RecoverStateFrom(nsLineBox* aLine, #endif mKidXMost = xmost; } - if (mComputeMaxElementSize) { + if (GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { UpdateMaxElementSize(nsSize(aLine->mMaxElementWidth, aLine->mBounds.height)); } // If computing the maximum width, then update mMaximumWidth - if (mComputeMaximumWidth) { + if (GetFlag(BRS_COMPUTEMAXWIDTH)) { UpdateMaximumWidth(aLine->mMaximumWidth); } @@ -1454,6 +1474,7 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, nsBlockReflowState state(aReflowState, aPresContext, this, aMetrics, NS_BLOCK_MARGIN_ROOT & mState); + PRInt32 sizeofBRS = sizeof nsBlockReflowState; if (eReflowReason_Resize != aReflowState.reason) { RenumberLists(aPresContext); @@ -1462,7 +1483,7 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, nsresult rv = NS_OK; PRBool isStyleChange = PR_FALSE; - state.mIsInlineIncrReflow = PR_FALSE; + state.SetFlag(BRS_ISINLINEINCRREFLOW, PR_FALSE); nsIFrame* target; switch (aReflowState.reason) { case eReflowReason_Initial: @@ -1535,7 +1556,7 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, // reflow the line containing the target of the incr. reflow // first mark the line dirty and set up the state object rv = PrepareChildIncrementalReflow(state); - state.mIsInlineIncrReflow = PR_TRUE; + state.SetFlag(BRS_ISINLINEINCRREFLOW, PR_TRUE); state.mPrevLine = prevLine; state.mCurrentLine = line; state.mNextRCFrame = state.mNextRCFrame; @@ -1681,6 +1702,10 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, if (isStyleChange) { // Lots of things could have changed so damage our entire // bounds +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 1 (%d, %d, %d, %d)\n", + this, 0, 0, mRect.width, mRect.height); +#endif Invalidate(aPresContext, nsRect(0, 0, mRect.width, mRect.height)); } else { @@ -1707,6 +1732,10 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, damageRect.y = 0; damageRect.height = mRect.height; } +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 2 (%d, %d, %d, %d)\n", + this, damageRect.x, damageRect.y, damageRect.width, damageRect.height); +#endif Invalidate(aPresContext, damageRect); } @@ -1730,6 +1759,10 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, damageRect.y = mRect.height - border.bottom; damageRect.height = border.bottom; } +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 3 (%d, %d, %d, %d)\n", + this, damageRect.x, damageRect.y, damageRect.width, damageRect.height); +#endif Invalidate(aPresContext, damageRect); } } @@ -1868,7 +1901,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, #ifdef NOISY_FINAL_SIZE ListTag(stdout); printf(": mY=%d mIsBottomMarginRoot=%s mPrevBottomMargin=%d bp=%d,%d\n", - aState.mY, aState.mIsBottomMarginRoot ? "yes" : "no", + aState.mY, aState.GetFlag(BRS_ISBOTTOMMARGINROOT) ? "yes" : "no", aState.mPrevBottomMargin, borderPadding.top, borderPadding.bottom); #endif @@ -1946,7 +1979,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, // contents or we fluff out to the maximum block width. Note: // We always shrink wrap when given an unconstrained width. if ((0 == (NS_BLOCK_SHRINK_WRAP & mState)) && - !aState.mUnconstrainedWidth && !aState.mShrinkWrapWidth && + !aState.GetFlag(BRS_UNCONSTRAINEDWIDTH) && !aState.GetFlag(BRS_SHRINKWRAPWIDTH) && !compact) { // Set our width to the max width if we aren't already that // wide. Note that the max-width has nothing to do with our @@ -1956,9 +1989,9 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, } // See if we should compute our max element size - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { // Adjust the computedWidth - if (aState.mNoWrap) { + if (aState.GetFlag(BRS_NOWRAP)) { // When no-wrap is true the max-element-size.width is the // width of the widest line plus the right border. Note that // aState.mKidXMost already has the left border factored in @@ -1996,7 +2029,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, // need to do horizontal alignment of the inline lines and make sure // blocks are correctly sized and positioned. Any lines that need // final adjustment will have been marked as dirty - if (aState.mShrinkWrapWidth && aState.mNeedResizeReflow) { + if (aState.GetFlag(BRS_SHRINKWRAPWIDTH) && aState.GetFlag(BRS_NEEDRESIZEREFLOW)) { // If the parent reflow state is also shrink wrap width, then // we don't need to do this, because it will reflow us after it // calculates the final width @@ -2025,7 +2058,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, } } - if (aState.mShrinkWrapWidth) { + if (aState.GetFlag(BRS_SHRINKWRAPWIDTH)) { PRBool parentIsShrinkWrapWidth = PR_FALSE; if (aReflowState.parentReflowState) { if (NS_SHRINKWRAPWIDTH == aReflowState.parentReflowState->mComputedWidth) { @@ -2047,7 +2080,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, // Don't carry out a bottom margin when our height is fixed // unless the bottom of the last line adjoins the bottom of our // content area. - if (!aState.mIsBottomMarginRoot) { + if (!aState.GetFlag(BRS_ISBOTTOMMARGINROOT)) { if (aState.mY + aState.mPrevBottomMargin != aMetrics.height) { aState.mPrevBottomMargin = 0; } @@ -2057,7 +2090,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, nscoord autoHeight = aState.mY; // Shrink wrap our height around our contents. - if (aState.mIsBottomMarginRoot) { + if (aState.GetFlag(BRS_ISBOTTOMMARGINROOT)) { // When we are a bottom-margin root make sure that our last // childs bottom margin is fully applied. // XXX check for a fit @@ -2082,7 +2115,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, } aMetrics.height = autoHeight; - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { maxHeight = aState.mMaxElementSize.height + borderPadding.top + borderPadding.bottom; } @@ -2090,7 +2123,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, aMetrics.ascent = aMetrics.height; aMetrics.descent = 0; - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { // Store away the final value aMetrics.maxElementSize->width = maxWidth; aMetrics.maxElementSize->height = maxHeight; @@ -2098,14 +2131,14 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, // Return bottom margin information aMetrics.mCarriedOutBottomMargin = - aState.mIsBottomMarginRoot ? 0 : aState.mPrevBottomMargin; + aState.GetFlag(BRS_ISBOTTOMMARGINROOT) ? 0 : aState.mPrevBottomMargin; #ifdef DEBUG if (CRAZY_WIDTH(aMetrics.width) || CRAZY_HEIGHT(aMetrics.height)) { ListTag(stdout); printf(": WARNING: desired:%d,%d\n", aMetrics.width, aMetrics.height); } - if (aState.mComputeMaxElementSize && + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE) && ((maxWidth > aMetrics.width) || (maxHeight > aMetrics.height))) { ListTag(stdout); printf(": WARNING: max-element-size:%d,%d desired:%d,%d maxSize:%d,%d\n", @@ -2115,7 +2148,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, } #endif #ifdef NOISY_MAX_ELEMENT_SIZE - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { IndentBy(stdout, GetDepth()); if (NS_UNCONSTRAINEDSIZE == aState.mReflowState.availableWidth) { printf("PASS1 "); @@ -2130,7 +2163,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, } // If we're requested to update our maximum width, then compute it - if (aState.mComputeMaximumWidth) { + if (aState.GetFlag(BRS_COMPUTEMAXWIDTH)) { // We need to add in for the right border/padding aMetrics.mMaximumWidth = aState.mMaximumWidth + borderPadding.right; } @@ -2337,8 +2370,13 @@ nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState) // See if we can try and avoid marking all the lines as dirty PRBool tryAndSkipLines = PR_FALSE; - // See if this is this a constrained resize reflow - if ((aState.mReflowState.reason == eReflowReason_Resize) && + // we need to calculate if any part of then block itself + // is impacted by a floater (bug 19579) + aState.GetAvailableSpace(); + + // See if this is this a constrained resize reflow that is not impacted by floaters + if ((PR_FALSE==aState.IsImpactedByFloater()) && + (aState.mReflowState.reason == eReflowReason_Resize) && (NS_UNCONSTRAINEDSIZE != aState.mReflowState.availableWidth)) { // If the text is left-aligned, then we try and avoid reflowing the lines @@ -2389,7 +2427,7 @@ nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState) } #endif - PRBool notWrapping = aState.mNoWrap; + PRBool notWrapping = aState.GetFlag(BRS_NOWRAP); while (nsnull != line) { if (line->IsBlock()) { @@ -2402,8 +2440,6 @@ nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState) printf("PrepareResizeReflow thinks line %p is %simpacted by floaters\n", line, line->IsImpactedByFloater() ? "" : "not "); #endif - - if (notWrapping) { // When no-wrap is set then the only line-breaking that // occurs for inline lines is triggered by BR elements or by @@ -2430,7 +2466,7 @@ nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState) printf("skipped: line=%p next=%p %s %s %s%s%s breakType=%d xmost=%d\n", line, line->mNext, line->IsBlock() ? "block" : "inline", - aState.mNoWrap ? "no-wrap" : "wrapping", + aState.GetFlag(BRS_NOWRAP) ? "no-wrap" : "wrapping", line->HasBreak() ? "has-break " : "", line->HasFloaters() ? "has-floaters " : "", line->IsImpactedByFloater() ? "impacted " : "", @@ -2628,7 +2664,7 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) ListTag(stdout); printf(": incrementally reflowing dirty lines: type=%s(%d) isInline=%s", kReflowCommandType[type], type, - aState.mIsInlineIncrReflow ? "true" : "false"); + aState.GetFlag(BRS_ISINLINEINCRREFLOW) ? "true" : "false"); } else { IndentBy(stdout, gNoiseIndent); @@ -2651,7 +2687,7 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) // Reflow the lines that are already ours aState.mPrevLine = nsnull; nsLineBox* line = mLines; - if (aState.mIsInlineIncrReflow && aState.mNextRCFrame) + if (aState.GetFlag(BRS_ISINLINEINCRREFLOW) && aState.mNextRCFrame) { const nsLineBox* incrTargetLine = aState.mCurrentLine; aState.mCurrentLine = line; @@ -2662,6 +2698,10 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) RecoverStateFrom(aState, line, deltaY, incrementalReflow ? &damageRect : 0); if (incrementalReflow && !damageRect.IsEmpty()) { +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 4 (%d, %d, %d, %d)\n", + this, damageRect.x, damageRect.y, damageRect.width, damageRect.height); +#endif Invalidate(aState.mPresContext, damageRect); } aState.mPrevLine = line; @@ -2689,7 +2729,7 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) // If we're supposed to update our maximum width, then we'll also need to // reflow this line if it's line wrapped and any of the continuing lines // are dirty - if (line->IsDirty() || (aState.mComputeMaximumWidth && ::WrappedLinesAreDirty(line))) { + if (line->IsDirty() || (aState.GetFlag(BRS_COMPUTEMAXWIDTH) && ::WrappedLinesAreDirty(line))) { // Compute the dirty lines "before" YMost, after factoring in // the running deltaY value - the running value is implicit in // aState.mY. @@ -2729,6 +2769,10 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) RecoverStateFrom(aState, line, deltaY, incrementalReflow ? &damageRect : 0); if (incrementalReflow && !damageRect.IsEmpty()) { +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 5 (%d, %d, %d, %d)\n", + this, damageRect.x, damageRect.y, damageRect.width, damageRect.height); +#endif Invalidate(aState.mPresContext, damageRect); } } @@ -2902,6 +2946,10 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, // XXX We need to improve on this... nsRect dirtyRect; dirtyRect.UnionRect(oldCombinedArea, lineCombinedArea); +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 6 (%d, %d, %d, %d)\n", + this, dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height); +#endif Invalidate(aState.mPresContext, dirtyRect); } else { @@ -2918,6 +2966,10 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, dirtyRect.x; dirtyRect.height = PR_MAX(oldCombinedArea.height, lineCombinedArea.height); +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 7 (%d, %d, %d, %d)\n", + this, dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height); +#endif Invalidate(aState.mPresContext, dirtyRect); } if (oldCombinedArea.height != lineCombinedArea.height) { @@ -2933,6 +2985,10 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, dirtyRect.height = PR_MAX(oldCombinedArea.YMost(), lineCombinedArea.YMost()) - dirtyRect.y; +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 8 (%d, %d, %d, %d)\n", + this, dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height); +#endif Invalidate(aState.mPresContext, dirtyRect); } } @@ -2951,21 +3007,21 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, // we'll either need to recover the floater state that applies to the // unconstrained reflow or keep it around in a separate space manager... PRBool isBeginningLine = !aState.mPrevLine || !aState.mPrevLine->IsLineWrapped(); - if (aState.mComputeMaximumWidth && isBeginningLine) { + if (aState.GetFlag(BRS_COMPUTEMAXWIDTH) && isBeginningLine) { nscoord oldY = aState.mY; nscoord oldPrevBottomMargin = aState.mPrevBottomMargin; - PRBool oldUnconstrainedWidth = aState.mUnconstrainedWidth; + PRBool oldUnconstrainedWidth = aState.GetFlag(BRS_UNCONSTRAINEDWIDTH); // First reflow the line with an unconstrained width. When doing this // we need to set the block reflow state's "mUnconstrainedWidth" variable // to PR_TRUE so if we encounter a placeholder and then reflow its // associated floater we don't end up resetting the line's right edge and // have it think the width is unconstrained... - aState.mUnconstrainedWidth = PR_TRUE; + aState.SetFlag(BRS_UNCONSTRAINEDWIDTH, PR_TRUE); ReflowInlineFrames(aState, aLine, aKeepReflowGoing, PR_TRUE); aState.mY = oldY; aState.mPrevBottomMargin = oldPrevBottomMargin; - aState.mUnconstrainedWidth = oldUnconstrainedWidth; + aState.SetFlag(BRS_UNCONSTRAINEDWIDTH, oldUnconstrainedWidth); // Update the line's maximum width aLine->mMaximumWidth = aLine->mBounds.XMost(); @@ -2980,14 +3036,14 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, // Note: we need to reset both member variables, because the inline // code examines mComputeMaxElementSize and if there is a placeholder // on this line the code to reflow the floater looks at both... - nscoord oldComputeMaxElementSize = aState.mComputeMaxElementSize; - nscoord oldComputeMaximumWidth = aState.mComputeMaximumWidth; + nscoord oldComputeMaxElementSize = aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE); + nscoord oldComputeMaximumWidth = aState.GetFlag(BRS_COMPUTEMAXWIDTH); - aState.mComputeMaxElementSize = PR_FALSE; - aState.mComputeMaximumWidth = PR_FALSE; + aState.SetFlag(BRS_COMPUTEMAXELEMENTSIZE, PR_FALSE); + aState.SetFlag(BRS_COMPUTEMAXWIDTH, PR_FALSE); rv = ReflowInlineFrames(aState, aLine, aKeepReflowGoing); - aState.mComputeMaxElementSize = oldComputeMaxElementSize; - aState.mComputeMaximumWidth = oldComputeMaximumWidth; + aState.SetFlag(BRS_COMPUTEMAXELEMENTSIZE, oldComputeMaxElementSize); + aState.SetFlag(BRS_COMPUTEMAXWIDTH, oldComputeMaximumWidth); } else { rv = ReflowInlineFrames(aState, aLine, aKeepReflowGoing); @@ -3001,6 +3057,10 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, nsRect dirtyRect; dirtyRect.UnionRect(oldCombinedArea, combinedArea); +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 9 (%d, %d, %d, %d)\n", + this, dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height); +#endif Invalidate(aState.mPresContext, dirtyRect); } } @@ -3368,7 +3428,7 @@ PRBool nsBlockFrame::ShouldApplyTopMargin(nsBlockReflowState& aState, nsLineBox* aLine) { - if (aState.mApplyTopMargin) { + if (aState.GetFlag(BRS_APPLYTOPMARGIN)) { // Apply short-circuit check to avoid searching the line list return PR_TRUE; } @@ -3377,7 +3437,7 @@ nsBlockFrame::ShouldApplyTopMargin(nsBlockReflowState& aState, // If we aren't at the top Y coordinate then something of non-zero // height must have been placed. Therefore the childs top-margin // applies. - aState.mApplyTopMargin = PR_TRUE; + aState.SetFlag(BRS_APPLYTOPMARGIN, PR_TRUE); return PR_TRUE; } @@ -3387,13 +3447,13 @@ nsBlockFrame::ShouldApplyTopMargin(nsBlockReflowState& aState, if (line->IsBlock()) { // A line which preceeds aLine contains a block; therefore the // top margin applies. - aState.mApplyTopMargin = PR_TRUE; + aState.SetFlag(BRS_APPLYTOPMARGIN, PR_TRUE); return PR_TRUE; } else if (line->HasFloaters()) { // A line which preceeds aLine is not empty therefore the top // margin applies. - aState.mApplyTopMargin = PR_TRUE; + aState.SetFlag(BRS_APPLYTOPMARGIN, PR_TRUE); return PR_TRUE; } line = line->mNext; @@ -3486,8 +3546,8 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, frame->GetStyleData(eStyleStruct_Display, (const nsStyleStruct*&) display); nsBlockReflowContext brc(aState.mPresContext, aState.mReflowState, - aState.mComputeMaxElementSize, - aState.mComputeMaximumWidth); + aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE), + aState.GetFlag(BRS_COMPUTEMAXWIDTH)); brc.SetNextRCFrame(aState.mNextRCFrame); // See if we should apply the top margin. If the block frame being @@ -3611,14 +3671,14 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, &collapsedBottomMargin, aLine->mBounds, combinedArea); - if (aState.mShrinkWrapWidth) { + if (aState.GetFlag(BRS_SHRINKWRAPWIDTH)) { // Mark the line as block so once we known the final shrink wrap width // we can reflow the block to the correct size // XXX We don't always need to do this... aLine->MarkDirty(); - aState.mNeedResizeReflow = PR_TRUE; + aState.SetFlag(BRS_NEEDRESIZEREFLOW, PR_TRUE); } - if (aState.mUnconstrainedWidth || aState.mShrinkWrapWidth) { + if (aState.GetFlag(BRS_UNCONSTRAINEDWIDTH) || aState.GetFlag(BRS_SHRINKWRAPWIDTH)) { // Add the right margin to the line's bounnds. That way it will be taken into // account when we compute our shrink wrap size nscoord marginRight = brc.GetMargin().right; @@ -3709,7 +3769,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, // Post-process the "line" nsSize maxElementSize(0, 0); - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { maxElementSize = brc.GetMaxElementSize(); if (aState.IsImpactedByFloater() && (NS_FRAME_SPLITTABLE_NON_RECTANGULAR != splitType)) { @@ -3721,7 +3781,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, } // If we asked the block to update its maximum width, then record the // updated value in the line, and update the current maximum width - if (aState.mComputeMaximumWidth) { + if (aState.GetFlag(BRS_COMPUTEMAXWIDTH)) { aLine->mMaximumWidth = brc.GetMaximumWidth(); aState.UpdateMaximumWidth(aLine->mMaximumWidth); @@ -3849,7 +3909,7 @@ nsBlockFrame::DoReflowInlineFramesMalloc(nsBlockReflowState& aState, nsLineLayout* ll = new nsLineLayout(aState.mPresContext, aState.mReflowState.mSpaceManager, &aState.mReflowState, - aState.mComputeMaxElementSize); + aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)); if (!ll) { return NS_ERROR_OUT_OF_MEMORY; } @@ -3872,7 +3932,7 @@ nsBlockFrame::DoReflowInlineFramesAuto(nsBlockReflowState& aState, nsLineLayout lineLayout(aState.mPresContext, aState.mReflowState.mSpaceManager, &aState.mReflowState, - aState.mComputeMaxElementSize); + aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)); lineLayout.Init(&aState, aState.mMinLineHeight, aState.mLineNumber); lineLayout.SetReflowTextRuns(mTextRuns); nsresult rv = DoReflowInlineFrames(aState, lineLayout, aLine, @@ -3910,7 +3970,7 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState, nscoord x = aState.mAvailSpaceRect.x + borderPadding.left; nscoord availWidth = aState.mAvailSpaceRect.width; nscoord availHeight; - if (aState.mUnconstrainedHeight) { + if (aState.GetFlag(BRS_UNCONSTRAINEDHEIGHT)) { availHeight = NS_UNCONSTRAINEDSIZE; } else { @@ -4359,7 +4419,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, nsSize maxElementSize; aLineLayout.VerticalAlignFrames(aLine, maxElementSize); // See if we're shrink wrapping the width - if (aState.mShrinkWrapWidth) { + if (aState.GetFlag(BRS_SHRINKWRAPWIDTH)) { // When determining the line's width we also need to include any // right floaters that impact us. This represents the shrink wrap // width of the line @@ -4388,25 +4448,20 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, // Only block frames horizontally align their children because // inline frames "shrink-wrap" around their children (therefore // there is no extra horizontal space). -#if XXX_fix_me - PRBool allowJustify = PR_TRUE; - if (NS_STYLE_TEXT_ALIGN_JUSTIFY == aState.mStyleText->mTextAlign) { - allowJustify = ShouldJustifyLine(aState, aLine); - } -#else - PRBool allowJustify = PR_FALSE; -#endif - - PRBool successful; - nsRect combinedArea; - successful = aLineLayout.HorizontalAlignFrames(aLine->mBounds, allowJustify, - aState.mShrinkWrapWidth); + const nsStyleText* styleText = (const nsStyleText*) + mStyleContext->GetStyleData(eStyleStruct_Text); + PRBool allowJustify = NS_STYLE_TEXT_ALIGN_JUSTIFY == styleText->mTextAlign + && !aLineLayout.GetLineEndsInBR() && ShouldJustifyLine(aState, aLine); + PRBool successful = aLineLayout.HorizontalAlignFrames(aLine->mBounds, allowJustify, + aState.GetFlag(BRS_SHRINKWRAPWIDTH)); if (!successful) { // Mark the line dirty and then later once we've determined the width // we can do the horizontal alignment aLine->MarkDirty(); - aState.mNeedResizeReflow = PR_TRUE; + aState.SetFlag(BRS_NEEDRESIZEREFLOW, PR_TRUE); } + + nsRect combinedArea; aLineLayout.RelativePositionFrames(combinedArea); aLine->SetCombinedArea(combinedArea); if (addedBullet) { @@ -4454,7 +4509,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, } aState.mY = newY; - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { #ifdef NOISY_MAX_ELEMENT_SIZE IndentBy(stdout, GetDepth()); if (NS_UNCONSTRAINEDSIZE == aState.mReflowState.availableWidth) { @@ -4478,7 +4533,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, // we don't want updated... if (aUpdateMaximumWidth) { // However, we do need to update the max-element-size if requested - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { aState.UpdateMaxElementSize(maxElementSize); // We also cache the max element width in the line. This is needed for // incremental reflow @@ -4519,7 +4574,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, CombineRects(aState.mFloaterCombinedArea, lineCombinedArea); if (aState.mHaveRightFloaters && - (aState.mUnconstrainedWidth || aState.mShrinkWrapWidth)) { + (aState.GetFlag(BRS_UNCONSTRAINEDWIDTH) || aState.GetFlag(BRS_SHRINKWRAPWIDTH))) { // We are reflowing in an unconstrained situation or shrink wrapping and // have some right floaters. They were placed at the infinite right edge // which will cause the combined area to be unusable. @@ -4540,11 +4595,11 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, aState.mRightFloaterCombinedArea.x = aLine->mBounds.XMost(); CombineRects(aState.mRightFloaterCombinedArea, lineCombinedArea); - if (aState.mShrinkWrapWidth) { + if (aState.GetFlag(BRS_SHRINKWRAPWIDTH)) { // Mark the line dirty so we come back and re-place the floater once // the shrink wrap width is determined aLine->MarkDirty(); - aState.mNeedResizeReflow = PR_TRUE; + aState.SetFlag(BRS_NEEDRESIZEREFLOW, PR_TRUE); } } aLine->SetCombinedArea(lineCombinedArea); @@ -4629,7 +4684,7 @@ nsBlockFrame::PostPlaceLine(nsBlockReflowState& aState, } // Update max-element-size - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { aState.UpdateMaxElementSize(aMaxElementSize); // We also cache the max element width in the line. This is needed for // incremental reflow @@ -4639,7 +4694,7 @@ nsBlockFrame::PostPlaceLine(nsBlockReflowState& aState, // If this is an unconstrained reflow, then cache the line width in the // line. We'll need this during incremental reflow if we're asked to // calculate the maximum width - if (aState.mUnconstrainedWidth) { + if (aState.GetFlag(BRS_UNCONSTRAINEDWIDTH)) { aLine->mMaximumWidth = aLine->mBounds.XMost(); } @@ -4653,7 +4708,7 @@ nsBlockFrame::PostPlaceLine(nsBlockReflowState& aState, #endif // If we're shrink wrapping our width and the line was wrapped, // then make sure we take up all of the available width - if (aState.mShrinkWrapWidth && aLine->IsLineWrapped()) { + if (aState.GetFlag(BRS_SHRINKWRAPWIDTH) && aLine->IsLineWrapped()) { aState.mKidXMost = aState.BorderPadding().left + aState.mContentArea.width; } @@ -5187,6 +5242,10 @@ nsBlockFrame::DoRemoveFrame(nsIPresContext* aPresContext, // cases... nsRect lineCombinedArea; line->GetCombinedArea(&lineCombinedArea); +#ifdef NOISY_BLOCK_INVALIDATE + printf("%p invalidate 10 (%d, %d, %d, %d)\n", + this, lineCombinedArea.x, lineCombinedArea.y, lineCombinedArea.width, lineCombinedArea.height); +#endif Invalidate(aPresContext, lineCombinedArea); line->Destroy(presShell); line = next; @@ -5282,8 +5341,8 @@ nsBlockFrame::ReflowFloater(nsBlockReflowState& aState, // Setup block reflow state to reflow the floater nsBlockReflowContext brc(aState.mPresContext, aState.mReflowState, - aState.mComputeMaxElementSize, - aState.mComputeMaximumWidth); + aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE), + aState.GetFlag(BRS_COMPUTEMAXWIDTH)); brc.SetNextRCFrame(aState.mNextRCFrame); // Reflow the floater @@ -5324,7 +5383,7 @@ nsBlockFrame::ReflowFloater(nsBlockReflowState& aState, floater->DidReflow(aState.mPresContext, NS_FRAME_REFLOW_FINISHED); // If we computed it, then stash away the max-element-size for later - if (aState.mComputeMaxElementSize) { + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE)) { aState.StoreMaxElementSize(floater, brc.GetMaxElementSize()); } @@ -5389,7 +5448,7 @@ nsBlockReflowState::AddFloater(nsLineLayout& aLineLayout, // Pass on updated available space to the current inline reflow engine GetAvailableSpace(); aLineLayout.UpdateBand(mAvailSpaceRect.x + BorderPadding().left, mY, - mUnconstrainedWidth ? NS_UNCONSTRAINEDSIZE : mAvailSpaceRect.width, + GetFlag(BRS_UNCONSTRAINEDWIDTH) ? NS_UNCONSTRAINEDSIZE : mAvailSpaceRect.width, mAvailSpaceRect.height, isLeftFloater, aPlaceholder->GetOutOfFlowFrame()); @@ -5615,7 +5674,12 @@ nsBlockReflowState::PlaceFloater(nsFloaterCache* aFloaterCache, } else { isLeftFloater = PR_FALSE; - region.x = mAvailSpaceRect.XMost() - region.width; + if (NS_UNCONSTRAINEDSIZE != mAvailSpaceRect.XMost()) + region.x = mAvailSpaceRect.XMost() - region.width; + else { + okToAddRectRegion = PR_FALSE; + region.x = mAvailSpaceRect.x; + } } *aIsLeftFloater = isLeftFloater; const nsMargin& borderPadding = BorderPadding(); @@ -5682,7 +5746,8 @@ nsBlockReflowState::PlaceFloater(nsFloaterCache* aFloaterCache, nsRect combinedArea = aFloaterCache->mCombinedArea; combinedArea.x += x; combinedArea.y += y; - if (!isLeftFloater && (mUnconstrainedWidth || mShrinkWrapWidth)) { + if (!isLeftFloater && + (GetFlag(BRS_UNCONSTRAINEDWIDTH) || GetFlag(BRS_SHRINKWRAPWIDTH))) { // When we are placing a right floater in an unconstrained situation or // when shrink wrapping, we don't apply it to the floater combined area // immediately. Otherwise we end up with an infinitely wide combined diff --git a/layout/html/base/src/nsImageFrame.cpp b/layout/html/base/src/nsImageFrame.cpp index b480a8105da8..2a1cfca6b5c4 100644 --- a/layout/html/base/src/nsImageFrame.cpp +++ b/layout/html/base/src/nsImageFrame.cpp @@ -879,6 +879,13 @@ nsImageFrame::AttributeChanged(nsIPresContext* aPresContext, } } } + else if (nsHTMLAtoms::width == aAttribute || nsHTMLAtoms::height == aAttribute) + { // XXX: could check for new width == old width, and make that a no-op + nsCOMPtr presShell; + aPresContext->GetShell(getter_AddRefs(presShell)); + mState |= NS_FRAME_IS_DIRTY; + mParent->ReflowDirtyChild(presShell, (nsIFrame*) this); + } return NS_OK; } diff --git a/layout/html/base/src/nsLineLayout.cpp b/layout/html/base/src/nsLineLayout.cpp index 6f5e6816e708..6a78a5c7f756 100644 --- a/layout/html/base/src/nsLineLayout.cpp +++ b/layout/html/base/src/nsLineLayout.cpp @@ -18,8 +18,10 @@ * Rights Reserved. * * Contributor(s): + * Steve Clark * Pierre Phaneuf * L. David Baron + * Robert O'Callahan */ #include "nsCOMPtr.h" #include "nsLineLayout.h" @@ -41,13 +43,14 @@ #include "nsIView.h" #include "nsIViewManager.h" #include "nsHTMLAtoms.h" +#include "nsTextFragment.h" #ifdef DEBUG #undef NOISY_HORIZONTAL_ALIGN #undef NOISY_VERTICAL_ALIGN #undef REALLY_NOISY_VERTICAL_ALIGN #undef NOISY_REFLOW -#undef REALLY_NOISY_REFLOW +#undef REALLY_NOISY_REFLOW #undef NOISY_PUSHING #undef REALLY_NOISY_PUSHING #undef DEBUG_ADD_TEXT @@ -120,15 +123,9 @@ nsLineLayout::nsLineLayout(nsIPresContext* aPresContext, mTextAlign = mStyleText->mTextAlign; mLineNumber = 0; mColumn = 0; - mEndsInWhiteSpace = PR_TRUE; - mUnderstandsWhiteSpace = PR_FALSE; - mTextStartsWithNBSP = PR_FALSE; - mFirstLetterStyleOK = PR_FALSE; - mIsTopOfPage = PR_FALSE; - mUpdatedBand = PR_FALSE; + mFlags = 0; // default all flags to false except those that follow here... + SetFlag(LL_ENDSINWHITESPACE, PR_TRUE); mPlacedFloaters = 0; - mImpactedByFloaters = PR_FALSE; - mLastFloaterWasLetterFrame = PR_FALSE; mTotalPlacedFrames = 0; mTopEdge = mBottomEdge = 0; mReflowTextRuns = nsnull; @@ -148,14 +145,17 @@ nsLineLayout::nsLineLayout(nsIPresContext* aPresContext, mTextRuns = nsnull; mTextRunP = &mTextRuns; mNewTextRun = nsnull; - mKnowStrictMode = PR_FALSE; + SetFlag(LL_KNOWSTRICTMODE, PR_FALSE); + PRInt32 size = sizeof nsLineLayout; + PRInt32 size_pfd = sizeof PerFrameData; + PRInt32 size_psd = sizeof PerSpanData; } nsLineLayout::nsLineLayout(nsIPresContext* aPresContext) : mPresContext(aPresContext) { MOZ_COUNT_CTOR(nsLineLayout); - + mTextRuns = nsnull; mTextRunP = &mTextRuns; mNewTextRun = nsnull; @@ -197,9 +197,9 @@ nsLineLayout::~nsLineLayout() PRBool nsLineLayout::InStrictMode() { - if (!mKnowStrictMode) { - mKnowStrictMode = PR_TRUE; - mInStrictMode = PR_TRUE; + if (!GetFlag(LL_KNOWSTRICTMODE)) { + SetFlag(LL_KNOWSTRICTMODE, PR_TRUE); + SetFlag(LL_INSTRICTMODE, PR_TRUE); // Get the compatabilty mode from pres context via the document and pres shell if (mBlockReflowState->frame) { @@ -217,7 +217,7 @@ nsLineLayout::InStrictMode() nsCompatibility mode; presContext->GetCompatibilityMode(&mode); if (eCompatibility_NavQuirks == mode) { - mInStrictMode = PR_FALSE; + SetFlag(LL_INSTRICTMODE, PR_FALSE); } } NS_RELEASE(shell); @@ -226,7 +226,7 @@ nsLineLayout::InStrictMode() } } } - return mInStrictMode; + return GetFlag(LL_INSTRICTMODE); } void @@ -235,10 +235,6 @@ nsLineLayout::BeginLineReflow(nscoord aX, nscoord aY, PRBool aImpactedByFloaters, PRBool aIsTopOfPage) { -#ifdef REALLY_NOISY_REFLOW - printf("nsLL::BeginLineReflow %d, %d, %d, %d, impacted=%s\n", - aX, aY, aWidth, aHeight, aImpactedByFloaters?"true":"false"); -#endif NS_ASSERTION(nsnull == mRootSpan, "bad linelayout user"); #ifdef DEBUG if ((aWidth != NS_UNCONSTRAINEDSIZE) && CRAZY_WIDTH(aWidth)) { @@ -256,8 +252,9 @@ nsLineLayout::BeginLineReflow(nscoord aX, nscoord aY, #endif #ifdef NOISY_REFLOW nsFrame::ListTag(stdout, mBlockReflowState->frame); - printf(": BeginLineReflow: %d,%d,%d,%d %s\n", + printf(": BeginLineReflow: %d,%d,%d,%d impacted=%s %s\n", aX, aY, aWidth, aHeight, + aImpactedByFloaters?"true":"false", aIsTopOfPage ? "top-of-page" : ""); #endif #ifdef DEBUG @@ -265,17 +262,18 @@ nsLineLayout::BeginLineReflow(nscoord aX, nscoord aY, #endif mColumn = 0; - mEndsInWhiteSpace = PR_TRUE; - mUnderstandsWhiteSpace = PR_FALSE; - mTextStartsWithNBSP = PR_FALSE; - mFirstLetterStyleOK = PR_FALSE; - mIsTopOfPage = aIsTopOfPage; - mUpdatedBand = PR_FALSE; + + SetFlag(LL_ENDSINWHITESPACE, PR_TRUE); + SetFlag(LL_UNDERSTANDSNWHITESPACE, PR_FALSE); + SetFlag(LL_TEXTSTARTSWITHNBSP, PR_FALSE); + SetFlag(LL_FIRSTLETTERSTYLEOK, PR_FALSE); + SetFlag(LL_ISTOPOFPAGE, aIsTopOfPage); + SetFlag(LL_UPDATEDBAND, PR_FALSE); mPlacedFloaters = 0; - mImpactedByFloaters = aImpactedByFloaters; + SetFlag(LL_IMPACTEDBYFLOATERS, aImpactedByFloaters); mTotalPlacedFrames = 0; - mCanPlaceFloater = PR_TRUE; - mLineEndsInBR = PR_FALSE; + SetFlag(LL_CANPLACEFLOATER, PR_TRUE); + SetFlag(LL_LINEENDSINBR, PR_FALSE); mSpanDepth = 0; mMaxTopBoxHeight = mMaxBottomBoxHeight = 0; @@ -355,7 +353,7 @@ nsLineLayout::UpdateBand(nscoord aX, nscoord aY, nsIFrame* aFloaterFrame) { #ifdef REALLY_NOISY_REFLOW - printf("nsLL::UpdateBand %d, %d, %d, %d, frame=%p placedLeft=%s\n will set mImpacted to PR_TRUE", + printf("nsLL::UpdateBand %d, %d, %d, %d, frame=%p placedLeft=%s\n will set mImpacted to PR_TRUE\n", aX, aY, aWidth, aHeight, aFloaterFrame, aPlacedLeftFloater?"true":"false"); #endif PerSpanData* psd = mRootSpan; @@ -404,13 +402,13 @@ nsLineLayout::UpdateBand(nscoord aX, nscoord aY, else { mBottomEdge = aY + aHeight; } - mUpdatedBand = PR_TRUE; + SetFlag(LL_UPDATEDBAND, PR_TRUE); mPlacedFloaters |= (aPlacedLeftFloater ? PLACED_LEFT : PLACED_RIGHT); - mImpactedByFloaters = PR_TRUE; + SetFlag(LL_IMPACTEDBYFLOATERS, PR_TRUE); nsCOMPtr frameType; aFloaterFrame->GetFrameType(getter_AddRefs(frameType)); - mLastFloaterWasLetterFrame = nsLayoutAtoms::letterFrame == frameType.get(); + SetFlag(LL_LASTFLOATERWASLETTERFRAME, (nsLayoutAtoms::letterFrame == frameType.get())); // Now update all of the open spans... mRootSpan->mContainsFloater = PR_TRUE; // make sure mRootSpan gets updated too @@ -762,17 +760,10 @@ nsLineLayout::NewPerFrameData(PerFrameData** aResult) pfd->mNext = nsnull; pfd->mPrev = nsnull; pfd->mFrame = nsnull; - pfd->mRelativePos = PR_FALSE; - pfd->mIsTextFrame = PR_FALSE; - pfd->mIsNonEmptyTextFrame = PR_FALSE; - pfd->mIsNonWhitespaceTextFrame = PR_FALSE; - pfd->mIsLetterFrame = PR_FALSE; - pfd->mIsSticky = PR_FALSE; - + pfd->mFlags = 0; // all flags default to false #ifdef DEBUG pfd->mVerticalAlign = 0xFF; - pfd->mRelativePos = PRBool(0xFF); mFramesAllocated++; #endif *aResult = pfd; @@ -782,7 +773,7 @@ nsLineLayout::NewPerFrameData(PerFrameData** aResult) PRBool nsLineLayout::CanPlaceFloaterNow() const { - return mCanPlaceFloater; + return GetFlag(LL_CANPLACEFLOATER); } PRBool @@ -794,7 +785,7 @@ nsLineLayout::LineIsEmpty() const PRBool nsLineLayout::LineIsBreakable() const { - if ((0 != mTotalPlacedFrames) || mImpactedByFloaters) { + if ((0 != mTotalPlacedFrames) || GetFlag(LL_IMPACTEDBYFLOATERS)) { return PR_TRUE; } return PR_FALSE; @@ -903,9 +894,11 @@ nsLineLayout::ReflowFrame(nsIFrame* aFrame, nsHTMLReflowState reflowState(mPresContext, *psd->mReflowState, aFrame, availSize, reason); reflowState.mLineLayout = this; - reflowState.isTopOfPage = mIsTopOfPage; - mUnderstandsWhiteSpace = PR_FALSE; - mTextStartsWithNBSP = PR_FALSE; + reflowState.isTopOfPage = GetFlag(LL_ISTOPOFPAGE); + SetFlag(LL_UNDERSTANDSNWHITESPACE, PR_FALSE); + SetFlag(LL_TEXTSTARTSWITHNBSP, PR_FALSE); + mTextJustificationNumSpaces = 0; + mTextJustificationNumLetters = 0; // Stash copies of some of the computed state away for later // (vertical alignment, for example) @@ -913,9 +906,9 @@ nsLineLayout::ReflowFrame(nsIFrame* aFrame, pfd->mMargin = reflowState.mComputedMargin; pfd->mBorderPadding = reflowState.mComputedBorderPadding; pfd->mFrameType = reflowState.mFrameType; - pfd->mRelativePos = - reflowState.mStylePosition->mPosition == NS_STYLE_POSITION_RELATIVE; - if (pfd->mRelativePos) { + pfd->SetFlag(PFD_RELATIVEPOS, + (reflowState.mStylePosition->mPosition == NS_STYLE_POSITION_RELATIVE)); + if (pfd->GetFlag(PFD_RELATIVEPOS)) { pfd->mOffsets = reflowState.mComputedOffsets; } @@ -969,14 +962,18 @@ nsLineLayout::ReflowFrame(nsIFrame* aFrame, nscoord ty = y - psd->mReflowState->mComputedBorderPadding.top; mSpaceManager->Translate(tx, ty); - pfd->mIsTextFrame = PR_FALSE; - pfd->mIsLetterFrame = PR_FALSE; - pfd->mIsNonEmptyTextFrame = PR_FALSE; - pfd->mIsNonWhitespaceTextFrame = PR_FALSE; - pfd->mIsSticky = PR_FALSE; + pfd->SetFlag(PFD_ISTEXTFRAME, PR_FALSE); + pfd->SetFlag(PFD_ISLETTERFRAME, PR_FALSE); + pfd->SetFlag(PFD_ISNONEMPTYTEXTFRAME, PR_FALSE); + pfd->SetFlag(PFD_ISNONWHITESPACETEXTFRAME, PR_FALSE); + pfd->SetFlag(PFD_ISSTICKY, PR_FALSE); + pfd->SetFlag(PFD_ISBULLET, PR_FALSE); aFrame->Reflow(mPresContext, metrics, reflowState, aReflowStatus); + pfd->mJustificationNumSpaces = mTextJustificationNumSpaces; + pfd->mJustificationNumLetters = mTextJustificationNumLetters; + // XXX See if the frame is a placeholderFrame and if it is process // the floater. nsIAtom* frameType; @@ -1000,7 +997,7 @@ nsLineLayout::ReflowFrame(nsIFrame* aFrame, outOfFlowFrame->GetFrameType(&oofft); if (oofft) { if (oofft == nsLayoutAtoms::letterFrame) { - mFirstLetterStyleOK = PR_FALSE; + SetFlag(LL_FIRSTLETTERSTYLEOK, PR_FALSE); } NS_RELEASE(oofft); } @@ -1009,11 +1006,11 @@ nsLineLayout::ReflowFrame(nsIFrame* aFrame, } else if (frameType == nsLayoutAtoms::textFrame) { // Note non-empty text-frames for inline frame compatability hackery - pfd->mIsTextFrame = PR_TRUE; + pfd->SetFlag(PFD_ISTEXTFRAME, PR_TRUE); // XXX An empty text frame at the end of the line seems not // to have zero width. if (metrics.width) { - pfd->mIsNonEmptyTextFrame = PR_TRUE; + pfd->SetFlag(PFD_ISNONEMPTYTEXTFRAME, PR_TRUE); nsCOMPtr content; nsresult result = pfd->mFrame->GetContent(getter_AddRefs(content)); if ((NS_SUCCEEDED(result)) && content) { @@ -1023,14 +1020,14 @@ nsLineLayout::ReflowFrame(nsIFrame* aFrame, PRBool isWhitespace; result = textContent->IsOnlyWhitespace(&isWhitespace); if (NS_SUCCEEDED(result)) { - pfd->mIsNonWhitespaceTextFrame = !isWhitespace; + pfd->SetFlag(PFD_ISNONWHITESPACETEXTFRAME, !isWhitespace); } } } } } else if (frameType == nsLayoutAtoms::letterFrame) { - pfd->mIsLetterFrame = PR_TRUE; + pfd->SetFlag(PFD_ISLETTERFRAME, PR_TRUE); } NS_RELEASE(frameType); } @@ -1170,7 +1167,7 @@ nsLineLayout::ReflowFrame(nsIFrame* aFrame, else { PushFrame(aFrame); } - mTextStartsWithNBSP = PR_FALSE; // reset for next time + SetFlag(LL_TEXTSTARTSWITHNBSP, PR_FALSE); // reset for next time #ifdef REALLY_NOISY_REFLOW nsFrame::IndentBy(stdout, mSpanDepth); @@ -1330,7 +1327,7 @@ nsLineLayout::CanPlaceFrame(PerFrameData* pfd, // There are no frames on the line or we are in the first word on // the line. If the line isn't impacted by a floater then the // current frame fits. - if (!mImpactedByFloaters) { + if (!GetFlag(LL_IMPACTEDBYFLOATERS)) { #ifdef NOISY_CAN_PLACE_FRAME printf(" ==> not-safe and not-impacted fits: "); while (nsnull != psd) { @@ -1341,28 +1338,28 @@ nsLineLayout::CanPlaceFrame(PerFrameData* pfd, #endif return PR_TRUE; } - else if (mLastFloaterWasLetterFrame) { + else if (GetFlag(LL_LASTFLOATERWASLETTERFRAME)) { // Another special case: see if the floater is a letter // frame. If it is, then allow the frame next to it to fit. - if (pfd->mIsNonEmptyTextFrame) { + if (pfd->GetFlag(PFD_ISNONEMPTYTEXTFRAME)) { // This must be the first piece of non-empty text (because // aNotSafeToBreak is true) or its a piece of text that is // part of a larger word. - pfd->mIsSticky = PR_TRUE; + pfd->SetFlag(PFD_ISSTICKY, PR_TRUE); } else if (pfd->mSpan) { PerFrameData* pf = pfd->mSpan->mFirstFrame; while (pf) { - if (pf->mIsSticky) { + if (pf->GetFlag(PFD_ISSTICKY)) { // If one of the spans children was sticky then the span // itself is sticky. - pfd->mIsSticky = PR_TRUE; + pfd->SetFlag(PFD_ISSTICKY, PR_TRUE); } pf = pf->mNext; } } - if (pfd->mIsSticky) { + if (pfd->GetFlag(PFD_ISSTICKY)) { #ifdef NOISY_CAN_PLACE_FRAME printf(" ==> last floater was letter frame && frame is sticky\n"); #endif @@ -1372,8 +1369,8 @@ nsLineLayout::CanPlaceFrame(PerFrameData* pfd, } // If this is a piece of text inside a letter frame... - if (pfd->mIsNonEmptyTextFrame) { - if (psd->mFrame && psd->mFrame->mIsLetterFrame) { + if (pfd->GetFlag(PFD_ISNONEMPTYTEXTFRAME)) { + if (psd->mFrame && psd->mFrame->GetFlag(PFD_ISLETTERFRAME)) { nsIFrame* prevInFlow; psd->mFrame->mFrame->GetPrevInFlow(&prevInFlow); if (prevInFlow) { @@ -1387,7 +1384,7 @@ nsLineLayout::CanPlaceFrame(PerFrameData* pfd, } } } - else if (pfd->mIsLetterFrame) { + else if (pfd->GetFlag(PFD_ISLETTERFRAME)) { // If this is the first continuation of the letter frame... nsIFrame* prevInFlow; pfd->mFrame->GetPrevInFlow(&prevInFlow); @@ -1433,7 +1430,7 @@ nsLineLayout::CanPlaceFrame(PerFrameData* pfd, // edge...Which means that whatever piece of text we just formatted // will be the piece that fits (the text frame logic knows to stop // when it runs out of room). - if (pfd->mIsNonEmptyTextFrame && mTextStartsWithNBSP) { + if (pfd->GetFlag(PFD_ISNONEMPTYTEXTFRAME) && GetFlag(LL_TEXTSTARTSWITHNBSP)) { return PR_TRUE; } @@ -1467,9 +1464,9 @@ nsLineLayout::PlaceFrame(PerFrameData* pfd, nsHTMLReflowMetrics& aMetrics) // If the band was updated during the reflow of that frame then we // need to adjust any prior frames that were reflowed. - if (mUpdatedBand && InBlockContext()) { + if (GetFlag(LL_UPDATEDBAND) && InBlockContext()) { UpdateFrames(); - mUpdatedBand = PR_FALSE; + SetFlag(LL_UPDATEDBAND, PR_FALSE); } // Advance to next X coordinate @@ -1478,8 +1475,8 @@ nsLineLayout::PlaceFrame(PerFrameData* pfd, nsHTMLReflowMetrics& aMetrics) // If the frame is a not aware of white-space and it takes up some // width, disable leading white-space compression for the next frame // to be reflowed. - if (!mUnderstandsWhiteSpace && pfd->mBounds.width) { - mEndsInWhiteSpace = PR_FALSE; + if ((!GetFlag(LL_UNDERSTANDSNWHITESPACE)) && pfd->mBounds.width) { + SetFlag(LL_ENDSINWHITESPACE, PR_FALSE); } // Count the number of frames on the line... @@ -1487,7 +1484,7 @@ nsLineLayout::PlaceFrame(PerFrameData* pfd, nsHTMLReflowMetrics& aMetrics) if (psd->mX != psd->mLeftEdge) { // As soon as a frame placed on the line advances an X coordinate // of any span we can no longer place a floater on the line. - mCanPlaceFloater = PR_FALSE; + SetFlag(LL_CANPLACEFLOATER, PR_FALSE); } } @@ -1505,14 +1502,10 @@ nsLineLayout::AddBulletFrame(nsIFrame* aFrame, pfd->mMargin.SizeTo(0, 0, 0, 0); pfd->mBorderPadding.SizeTo(0, 0, 0, 0); pfd->mFrameType = NS_CSS_FRAME_TYPE_INLINE|NS_FRAME_REPLACED_ELEMENT; - pfd->mRelativePos = PR_FALSE; + pfd->mFlags = 0; // all flags default to false + pfd->SetFlag(PFD_ISBULLET, PR_TRUE); pfd->mAscent = aMetrics.ascent; pfd->mDescent = aMetrics.descent; - pfd->mIsTextFrame = PR_FALSE; - pfd->mIsNonEmptyTextFrame = PR_FALSE; - pfd->mIsNonWhitespaceTextFrame = PR_FALSE; - pfd->mIsLetterFrame = PR_FALSE; - pfd->mIsSticky = PR_FALSE; // Note: y value will be updated during vertical alignment aFrame->GetRect(pfd->mBounds); @@ -1857,7 +1850,7 @@ nsLineLayout::VerticalAlignFrames(PerSpanData* psd) zeroEffectiveSpanBox = PR_TRUE; PerFrameData* pfd = psd->mFirstFrame; while (nsnull != pfd) { - if (preMode?pfd->mIsTextFrame:pfd->mIsNonWhitespaceTextFrame) { + if (preMode?pfd->GetFlag(PFD_ISTEXTFRAME):pfd->GetFlag(PFD_ISNONWHITESPACETEXTFRAME)) { zeroEffectiveSpanBox = PR_FALSE; break; } @@ -2154,7 +2147,7 @@ nsLineLayout::VerticalAlignFrames(PerSpanData* psd) if (pfd->mVerticalAlign == VALIGN_OTHER) { // Text frames do not contribute to the min/max Y values for the // line (instead their parent frame's font-size contributes). - if (!pfd->mIsTextFrame) { + if (!pfd->GetFlag(PFD_ISTEXTFRAME)) { nscoord yTop, yBottom; if (frameSpan) { // For spans that were are now placing, use their position @@ -2212,7 +2205,7 @@ nsLineLayout::VerticalAlignFrames(PerSpanData* psd) // BR) (NN4/IE5 quirk) PRBool applyMinLH = !(psd->mZeroEffectiveSpanBox); // (1) above PRBool isFirstLine = !mLineNumber; // if the line number is 0 - PRBool isLastLine = (!mLineBox->IsLineWrapped() && !mLineEndsInBR); + PRBool isLastLine = (!mLineBox->IsLineWrapped() && !GetFlag(LL_LINEENDSINBR)); //PRBool isLastLine = mBlockRS->mCurLine->IsLineWrapped(); if (!applyMinLH && (isFirstLine || isLastLine)) { nsCOMPtr blockContent; @@ -2412,13 +2405,13 @@ nsLineLayout::TrimTrailingWhiteSpaceIn(PerSpanData* psd, return PR_TRUE; } } - else if (!pfd->mIsTextFrame) { + else if (!pfd->GetFlag(PFD_ISTEXTFRAME)) { // If we hit a frame on the end that's not text, then there is // no trailing whitespace to trim. Stop the search. *aDeltaWidth = 0; return PR_TRUE; } - else if (pfd->mIsNonEmptyTextFrame) { + else if (pfd->GetFlag(PFD_ISNONEMPTYTEXTFRAME)) { nscoord deltaWidth = 0; pfd->mFrame->TrimTrailingWhiteSpace(mPresContext, *mBlockReflowState->rendContext, @@ -2432,6 +2425,10 @@ nsLineLayout::TrimTrailingWhiteSpaceIn(PerSpanData* psd, printf(" returned %d\n", deltaWidth); #endif if (deltaWidth) { + if (pfd->mJustificationNumSpaces > 0) { + pfd->mJustificationNumSpaces--; + } + pfd->mBounds.width -= deltaWidth; pfd->mCombinedArea.width -= deltaWidth; if (0 == pfd->mBounds.width) { @@ -2479,6 +2476,95 @@ nsLineLayout::TrimTrailingWhiteSpace() return 0 != deltaWidth; } +void +nsLineLayout::ComputeJustificationWeights(PerSpanData* aPSD, + PRInt32* aNumSpaces, + PRInt32* aNumLetters) +{ + NS_ASSERTION(aPSD, "null arg"); + NS_ASSERTION(aNumSpaces, "null arg"); + NS_ASSERTION(aNumLetters, "null arg"); + PRInt32 numSpaces = 0; + PRInt32 numLetters = 0; + + for (PerFrameData* pfd = aPSD->mFirstFrame; pfd != nsnull; pfd = pfd->mNext) { + nscoord dw = 0; + + if (PR_TRUE == pfd->GetFlag(PFD_ISTEXTFRAME)) { + numSpaces += pfd->mJustificationNumSpaces; + numLetters += pfd->mJustificationNumLetters; + } + else if (pfd->mSpan != nsnull) { + PRInt32 spanSpaces; + PRInt32 spanLetters; + + ComputeJustificationWeights(pfd->mSpan, &spanSpaces, &spanLetters); + + numSpaces += spanSpaces; + numLetters += spanLetters; + } + } + + *aNumSpaces = numSpaces; + *aNumLetters = numLetters; +} + +nscoord +nsLineLayout::ApplyFrameJustification(PerSpanData* aPSD, FrameJustificationState* aState) +{ + NS_ASSERTION(aPSD, "null arg"); + NS_ASSERTION(aState, "null arg"); + + nscoord deltaX = 0; + for (PerFrameData* pfd = aPSD->mFirstFrame; pfd != nsnull; pfd = pfd->mNext) { + // Don't reposition bullets (and other frames that occur out of X-order?) + if (!pfd->GetFlag(PFD_ISBULLET)) { + nscoord dw = 0; + + pfd->mBounds.x += deltaX; + + if (PR_TRUE == pfd->GetFlag(PFD_ISTEXTFRAME)) { + if (aState->mTotalWidthForSpaces > 0 && + aState->mTotalNumSpaces > 0 && // we divide by this value, so must be non-zero + aState->mTotalNumLetters >0 // we divide by this value, so must be non-zero + ) { + aState->mNumSpacesProcessed += pfd->mJustificationNumSpaces; + + nscoord newAllocatedWidthForSpaces = + (aState->mTotalWidthForSpaces*aState->mNumSpacesProcessed) + /aState->mTotalNumSpaces; + + dw += newAllocatedWidthForSpaces - aState->mWidthForSpacesProcessed; + + aState->mWidthForSpacesProcessed = newAllocatedWidthForSpaces; + } + + if (aState->mTotalWidthForLetters > 0) { + aState->mNumLettersProcessed += pfd->mJustificationNumLetters; + + nscoord newAllocatedWidthForLetters = + (aState->mTotalWidthForLetters*aState->mNumLettersProcessed) + /aState->mTotalNumLetters; + + dw += newAllocatedWidthForLetters - aState->mWidthForLettersProcessed; + + aState->mWidthForLettersProcessed = newAllocatedWidthForLetters; + } + } + else { + if (nsnull != pfd->mSpan) { + dw += ApplyFrameJustification(pfd->mSpan, aState); + } + } + + pfd->mBounds.width += dw; + deltaX += dw; + pfd->mFrame->SetRect(mPresContext, pfd->mBounds); + } + } + return deltaX; +} + PRBool nsLineLayout::HorizontalAlignFrames(nsRect& aLineBounds, PRBool aAllowJustify, @@ -2525,7 +2611,18 @@ nsLineLayout::HorizontalAlignFrames(nsRect& aLineBounds, // frames in the line. If it is the last line then if the // direction is right-to-left then we right-align the frames. if (aAllowJustify) { - break; + if (!aShrinkWrapWidth) { + PRInt32 numSpaces; + PRInt32 numLetters; + + ComputeJustificationWeights(psd, &numSpaces, &numLetters); + + if (numSpaces > 0) { + FrameJustificationState state = { numSpaces, numLetters, remainingWidth, 0, 0, 0, 0, 0 }; + + ApplyFrameJustification(psd, &state); + } + } } else if (NS_STYLE_DIRECTION_RTL == psd->mDirection) { // right align the frames @@ -2617,7 +2714,7 @@ nsLineLayout::RelativePositionFrames(PerSpanData* psd, nsRect& aCombinedArea) nscoord y = pfd->mBounds.y; // Adjust the origin of the frame - if (pfd->mRelativePos) { + if (pfd->GetFlag(PFD_RELATIVEPOS)) { nsIFrame* frame = pfd->mFrame; frame->GetOrigin(origin); // XXX what about right and bottom? diff --git a/layout/html/base/src/nsLineLayout.h b/layout/html/base/src/nsLineLayout.h index 2cb644817ba5..4bde711f609b 100644 --- a/layout/html/base/src/nsLineLayout.h +++ b/layout/html/base/src/nsLineLayout.h @@ -18,6 +18,8 @@ * Rights Reserved. * * Contributor(s): + * Steve Clark + * Robert O'Callahan */ #ifndef nsLineLayout_h___ #define nsLineLayout_h___ @@ -32,7 +34,7 @@ class nsBlockReflowState; class nsPlaceholderFrame; struct nsStyleText; -#define NS_LINELAYOUT_NUM_FRAMES 10 +#define NS_LINELAYOUT_NUM_FRAMES 5 #define NS_LINELAYOUT_NUM_SPANS 5 class nsLineLayout { @@ -122,23 +124,69 @@ public: //---------------------------------------- + // Supporting methods and data for flags +protected: +#define LL_ENDSINWHITESPACE 0x00000001 +#define LL_UNDERSTANDSNWHITESPACE 0x00000002 +#define LL_TEXTSTARTSWITHNBSP 0x00000004 +#define LL_FIRSTLETTERSTYLEOK 0x00000008 +#define LL_ISTOPOFPAGE 0x00000010 +#define LL_UPDATEDBAND 0x00000020 +#define LL_IMPACTEDBYFLOATERS 0x00000040 +#define LL_LASTFLOATERWASLETTERFRAME 0x00000080 +#define LL_CANPLACEFLOATER 0x00000100 +#define LL_KNOWSTRICTMODE 0x00000200 +#define LL_INSTRICTMODE 0x00000400 +#define LL_LINEENDSINBR 0x00000800 +#define LL_LASTFLAG LL_LINEENDSINBR + + PRUint16 mFlags; + + void SetFlag(PRUint32 aFlag, PRBool aValue) + { + NS_ASSERTION(aFlag<=LL_LASTFLAG, "bad flag"); + NS_ASSERTION(aValue==PR_FALSE || aValue==PR_TRUE, "bad value"); + if (aValue) { // set flag + mFlags |= aFlag; + } + else { // unset flag + mFlags &= ~aFlag; + } + } + + PRBool GetFlag(PRUint32 aFlag) const + { + NS_ASSERTION(aFlag<=LL_LASTFLAG, "bad flag"); + PRBool result = (mFlags & aFlag); + if (result) return PR_TRUE; + return PR_FALSE; + } + +public: + // Support methods for white-space compression and word-wrapping // during line reflow void SetEndsInWhiteSpace(PRBool aState) { - mEndsInWhiteSpace = aState; + SetFlag(LL_ENDSINWHITESPACE, aState); } PRBool GetEndsInWhiteSpace() const { - return mEndsInWhiteSpace; + return GetFlag(LL_ENDSINWHITESPACE); } void SetUnderstandsWhiteSpace(PRBool aSetting) { - mUnderstandsWhiteSpace = aSetting; + SetFlag(LL_UNDERSTANDSNWHITESPACE, aSetting); } + void SetTextJustificationWeights(PRInt32 aNumSpaces, PRInt32 aNumLetters) { + mTextJustificationNumSpaces = aNumSpaces; + mTextJustificationNumLetters = aNumLetters; + } + + void SetTextStartsWithNBSP(PRBool aYes) { - mTextStartsWithNBSP = aYes; + SetFlag(LL_TEXTSTARTSWITHNBSP, aYes); } void RecordWordFrame(nsIFrame* aWordFrame) { @@ -163,9 +211,15 @@ public: PRBool LineIsBreakable() const; - PRBool GetLineEndsInBR() const { return mLineEndsInBR; } + PRBool GetLineEndsInBR() const + { + return GetFlag(LL_LINEENDSINBR); + } - void SetLineEndsInBR(PRBool aOn) { mLineEndsInBR = aOn; } + void SetLineEndsInBR(PRBool aOn) + { + SetFlag(LL_LINEENDSINBR, aOn); + } //---------------------------------------- // Inform the line-layout about the presence of a floating frame @@ -176,11 +230,11 @@ public: //---------------------------------------- PRBool GetFirstLetterStyleOK() const { - return mFirstLetterStyleOK; + return GetFlag(LL_FIRSTLETTERSTYLEOK); } void SetFirstLetterStyleOK(PRBool aSetting) { - mFirstLetterStyleOK = aSetting; + SetFlag(LL_FIRSTLETTERSTYLEOK, aSetting); } void SetFirstLetterFrame(nsIFrame* aFrame) { @@ -233,19 +287,11 @@ protected: nsIFrame* mFirstLetterFrame; PRInt32 mLineNumber; PRInt32 mColumn; + PRInt32 mTextJustificationNumSpaces; + PRInt32 mTextJustificationNumLetters; + nsLineBox* mLineBox; - PRPackedBool mEndsInWhiteSpace; - PRPackedBool mUnderstandsWhiteSpace; - PRPackedBool mTextStartsWithNBSP; - PRPackedBool mFirstLetterStyleOK; - PRPackedBool mIsTopOfPage; - PRPackedBool mUpdatedBand; - PRPackedBool mImpactedByFloaters; - PRPackedBool mLastFloaterWasLetterFrame; - PRPackedBool mCanPlaceFloater; - PRPackedBool mKnowStrictMode; - PRPackedBool mInStrictMode; - PRPackedBool mLineEndsInBR; + PRUint8 mPlacedFloaters; PRInt32 mTotalPlacedFrames; nsVoidArray mWordFrames; @@ -294,15 +340,47 @@ protected: nsMargin mMargin; nsMargin mBorderPadding; nsMargin mOffsets; - PRPackedBool mRelativePos; // Other state we use PRUint8 mVerticalAlign; - PRPackedBool mIsTextFrame; - PRPackedBool mIsNonEmptyTextFrame; - PRPackedBool mIsNonWhitespaceTextFrame; - PRPackedBool mIsLetterFrame; - PRPackedBool mIsSticky; + + // state for text justification + PRInt32 mJustificationNumSpaces; + PRInt32 mJustificationNumLetters; + + +// PerFrameData flags +#define PFD_RELATIVEPOS 0x00000001 +#define PFD_ISTEXTFRAME 0x00000002 +#define PFD_ISNONEMPTYTEXTFRAME 0x00000004 +#define PFD_ISNONWHITESPACETEXTFRAME 0x00000008 +#define PFD_ISLETTERFRAME 0x00000010 +#define PFD_ISSTICKY 0x00000020 +#define PFD_ISBULLET 0x00000040 +#define PFD_LASTFLAG PFD_ISBULLET + + PRPackedBool mFlags; + + void SetFlag(PRUint32 aFlag, PRBool aValue) + { + NS_ASSERTION(aFlag<=PFD_LASTFLAG, "bad flag"); + NS_ASSERTION(aValue==PR_FALSE || aValue==PR_TRUE, "bad value"); + if (aValue) { // set flag + mFlags |= aFlag; + } + else { // unset flag + mFlags &= ~aFlag; + } + } + + PRBool GetFlag(PRUint32 aFlag) const + { + NS_ASSERTION(aFlag<=PFD_LASTFLAG, "bad flag"); + PRBool result = (mFlags & aFlag); + if (result) return PR_TRUE; + return PR_FALSE; + } + PerFrameData* Last() { PerFrameData* pfd = this; @@ -409,6 +487,22 @@ protected: PRBool TrimTrailingWhiteSpaceIn(PerSpanData* psd, nscoord* aDeltaWidth); + + void ComputeJustificationWeights(PerSpanData* psd, PRInt32* numSpaces, PRInt32* numLetters); + + struct FrameJustificationState { + PRInt32 mTotalNumSpaces; + PRInt32 mTotalNumLetters; + nscoord mTotalWidthForSpaces; + nscoord mTotalWidthForLetters; + PRInt32 mNumSpacesProcessed; + PRInt32 mNumLettersProcessed; + nscoord mWidthForSpacesProcessed; + nscoord mWidthForLettersProcessed; + }; + nscoord ApplyFrameJustification(PerSpanData* aPSD, FrameJustificationState* aState); + + #ifdef DEBUG void DumpPerSpanData(PerSpanData* psd, PRInt32 aIndent); #endif diff --git a/layout/html/base/src/nsPresShell.cpp b/layout/html/base/src/nsPresShell.cpp index 66d5d509ad98..3fa2bd8c49c9 100644 --- a/layout/html/base/src/nsPresShell.cpp +++ b/layout/html/base/src/nsPresShell.cpp @@ -3874,6 +3874,9 @@ FindTopFrame(nsIFrame* aRoot) PRBool PresShell::VerifyIncrementalReflow() { + if (VERIFY_REFLOW_NOISY & gVerifyReflowFlags) { + printf("Building Verification Tree...\n"); + } // All the stuff we are creating that needs releasing nsIPresContext* cx; nsIViewManager* vm; @@ -3976,6 +3979,9 @@ PresShell::VerifyIncrementalReflow() vm->SetViewObserver((nsIViewObserver *)((PresShell*)sh)); sh->InitialReflow(r.width, r.height); sh->SetVerifyReflowEnable(PR_TRUE); // turn on verify reflow again now that we're done reflowing the test frame tree + if (VERIFY_REFLOW_NOISY & gVerifyReflowFlags) { + printf("Verification Tree built, comparing...\n"); + } // Now that the document has been reflowed, use its frame tree to // compare against our frame tree. @@ -3999,15 +4005,15 @@ PresShell::VerifyIncrementalReflow() } } -// printf("Incremental reflow doomed view tree:\n"); -// view->List(stdout, 1); -// view->SetVisibility(nsViewVisibility_kHide); cx->Stop(); cx->SetContainer(nsnull); NS_RELEASE(cx); sh->EndObservingDocument(); NS_RELEASE(sh); NS_RELEASE(vm); + if (VERIFY_REFLOW_NOISY & gVerifyReflowFlags) { + printf("Finished Verifying Reflow...\n"); + } return ok; } diff --git a/layout/html/base/src/nsTextFrame.cpp b/layout/html/base/src/nsTextFrame.cpp index 3b46283c23a5..886c84e3eafb 100644 --- a/layout/html/base/src/nsTextFrame.cpp +++ b/layout/html/base/src/nsTextFrame.cpp @@ -19,6 +19,7 @@ * * Contributor(s): * Pierre Phaneuf + * Robert O'Callahan */ #include "nsCOMPtr.h" #include "nsHTMLParts.h" @@ -458,9 +459,10 @@ public: nscoord mAveCharWidth; PRBool mJustifying; PRBool mPreformatted; - PRIntn mNumSpaces; + PRInt32 mNumSpacesToRender; + PRInt32 mNumSpacesToMeasure; nscoord mExtraSpacePerSpace; - nscoord mRemainingExtraSpace; + PRInt32 mNumSpacesReceivingExtraJot; TextStyle(nsIPresContext* aPresContext, nsIRenderingContext& aRenderingContext, @@ -521,20 +523,25 @@ public: // Get the word and letter spacing mWordSpacing = 0; - mLetterSpacing = 0; PRIntn unit = mText->mWordSpacing.GetUnit(); if (eStyleUnit_Coord == unit) { mWordSpacing = mText->mWordSpacing.GetCoordValue(); } + + mLetterSpacing = 0; unit = mText->mLetterSpacing.GetUnit(); if (eStyleUnit_Coord == unit) { mLetterSpacing = mText->mLetterSpacing.GetCoordValue(); } - mNumSpaces = 0; - mRemainingExtraSpace = 0; + mNumSpacesToRender = 0; + mNumSpacesToMeasure = 0; + mNumSpacesReceivingExtraJot = 0; mExtraSpacePerSpace = 0; mPreformatted = (NS_STYLE_WHITESPACE_PRE == mText->mWhiteSpace) || (NS_STYLE_WHITESPACE_MOZ_PRE_WRAP == mText->mWhiteSpace); + + mJustifying = (NS_STYLE_TEXT_ALIGN_JUSTIFY == mText->mTextAlign) && + !mPreformatted; } ~TextStyle() { @@ -590,6 +597,9 @@ public: nsAutoIndexBuffer* aIndexBuffer, nsAutoTextBuffer* aTextBuffer, PRInt32* aTextLen); + void ComputeExtraJustificationSpacing(nsIRenderingContext& aRenderingContext, + TextStyle& aTextStyle, + PRUnichar* aBuffer, PRInt32 aLength, PRInt32 aNumSpaces); void PaintTextDecorations(nsIRenderingContext& aRenderingContext, nsIStyleContext* aStyleContext, @@ -1192,7 +1202,8 @@ nsTextFrame::Paint(nsIPresContext* aPresContext, sc->GetStyleData(eStyleStruct_Display); if (disp->IsVisible()) { TextStyle ts(aPresContext, aRenderingContext, mStyleContext); - if (ts.mSmallCaps || (0 != ts.mWordSpacing) || (0 != ts.mLetterSpacing)) { + if (ts.mSmallCaps || (0 != ts.mWordSpacing) || (0 != ts.mLetterSpacing) + || ts.mJustifying) { PaintTextSlowly(aPresContext, aRenderingContext, sc, ts, 0, 0); } else { @@ -1261,7 +1272,9 @@ nsTextFrame::PrepareUnicodeText(nsTextTransformer& aTX, PRBool isWhitespace, wasTransformed; PRInt32 wordLen, contentLen; aTX.GetNextWord(PR_FALSE, &wordLen, &contentLen, &isWhitespace, &wasTransformed); - NS_ASSERTION(isWhitespace, "mState and content are out of sync"); + // we trip this assertion in bug 31053, but I think it's unnecessary + //NS_ASSERTION(isWhitespace, "mState and content are out of sync"); + if (isWhitespace) { if (nsnull != indexp) { // Point mapping indicies at the same content index since @@ -1301,7 +1314,6 @@ nsTextFrame::PrepareUnicodeText(nsTextTransformer& aTX, } inWord = PR_FALSE; if (isWhitespace) { - numSpaces++; if ('\t' == bp[0]) { PRInt32 spaces = 8 - (7 & column); PRUnichar* tp = bp; @@ -1339,20 +1351,30 @@ nsTextFrame::PrepareUnicodeText(nsTextTransformer& aTX, } } } + numSpaces += wordLen; } else { + PRInt32 i; if (nsnull != indexp) { // Point mapping indicies at each content index in the word - PRInt32 i = contentLen; + i = contentLen; while (--i >= 0) { *indexp++ = strInx++; } } + // Nonbreaking spaces count as spaces, not letters + PRUnichar* tp = bp; + i = wordLen; + while (--i >= 0) { + if (*tp++ == ' ') { + numSpaces++; + } + } } // Grow the buffer before we run out of room. The only time this // happens is because of tab expansion. - if (dstOffset + wordLen > aTextBuffer->mBufferLen) { + if (aTextBuffer != nsnull && dstOffset + wordLen > aTextBuffer->mBufferLen) { nsresult rv = aTextBuffer->GrowBy(wordLen); if (NS_FAILED(rv)) { break; @@ -1362,8 +1384,10 @@ nsTextFrame::PrepareUnicodeText(nsTextTransformer& aTX, column += wordLen; textLength += wordLen; n -= contentLen; - nsCRT::memcpy(aTextBuffer->mBuffer + dstOffset, bp, - sizeof(PRUnichar)*wordLen); + if (aTextBuffer != nsnull) { + nsCRT::memcpy(aTextBuffer->mBuffer + dstOffset, bp, + sizeof(PRUnichar)*wordLen); + } dstOffset += wordLen; } @@ -1372,19 +1396,24 @@ nsTextFrame::PrepareUnicodeText(nsTextTransformer& aTX, NS_ASSERTION(indexp <= aIndexBuffer->mBuffer + aIndexBuffer->mBufferLen, "yikes - we just overwrote memory"); } - NS_ASSERTION(dstOffset <= aTextBuffer->mBufferLen, - "yikes - we just overwrote memory"); + if (aTextBuffer) { + NS_ASSERTION(dstOffset <= aTextBuffer->mBufferLen, + "yikes - we just overwrote memory"); + } + #endif // Remove trailing whitespace if it was trimmed after reflow if (TEXT_TRIMMED_WS & mState) { + NS_ASSERTION(aTextBuffer != nsnull, + "Nonexistent text buffer should only occur during reflow, i.e. before whitespace is trimmed"); if (--dstOffset >= 0) { PRUnichar ch = aTextBuffer->mBuffer[dstOffset]; if (XP_IS_SPACE(ch)) { textLength--; + numSpaces--; } } - numSpaces--; } if (aIndexBuffer) { @@ -1716,6 +1745,7 @@ nsTextFrame::PaintUnicodeText(nsIPresContext* aPresContext, doc->GetLineBreaker(getter_AddRefs(lb)); nsTextTransformer tx(lb, nsnull); PRInt32 textLength; + // no need to worry about justification, that's always on the slow path PrepareUnicodeText(tx, (displaySelection ? &indexBuffer : nsnull), &paintBuffer, &textLength); @@ -1833,7 +1863,7 @@ nsTextFrame::GetPositionSlowly(nsIPresContext* aPresContext, } TextStyle ts(aPresContext, *aRendContext, mStyleContext); - if (!ts.mSmallCaps && !ts.mWordSpacing && !ts.mLetterSpacing) { + if (!ts.mSmallCaps && !ts.mWordSpacing && !ts.mLetterSpacing && !ts.mJustifying) { return NS_ERROR_INVALID_ARG; } nsIView * view; @@ -1861,12 +1891,16 @@ nsTextFrame::GetPositionSlowly(nsIPresContext* aPresContext, doc->GetLineBreaker(getter_AddRefs(lb)); nsTextTransformer tx(lb, nsnull); PRInt32 textLength; - PrepareUnicodeText(tx, &indexBuffer, &paintBuffer, &textLength); + PRInt32 numSpaces; + + numSpaces = PrepareUnicodeText(tx, &indexBuffer, &paintBuffer, &textLength); if (textLength <= 0) { return NS_ERROR_FAILURE; } -//IF STYLE SAYS TO SELCT TO END OF FRAME HERE... + ComputeExtraJustificationSpacing(*aRendContext, ts, paintBuffer.mBuffer, textLength, numSpaces); + +//IF STYLE SAYS TO SELECT TO END OF FRAME HERE... nsCOMPtr prefs; PRInt32 prefInt = 0; rv = nsServiceManager::GetService(kPrefCID, @@ -1929,7 +1963,7 @@ nsTextFrame::RenderString(nsIRenderingContext& aRenderingContext, PRUnichar* bp = bp0; PRBool spacing = (0 != aTextStyle.mLetterSpacing) || - (0 != aTextStyle.mWordSpacing); + (0 != aTextStyle.mWordSpacing) || aTextStyle.mJustifying; nscoord spacingMem[TEXT_BUF_SIZE]; PRIntn* sp0 = spacingMem; if (spacing && (aLength > TEXT_BUF_SIZE)) { @@ -1977,12 +2011,12 @@ nsTextFrame::RenderString(nsIRenderingContext& aRenderingContext, else if (ch == ' ') { nextFont = aTextStyle.mNormalFont; nextY = aY; - glyphWidth = aTextStyle.mSpaceWidth + aTextStyle.mWordSpacing; - nscoord extra = aTextStyle.mExtraSpacePerSpace; - if (--aTextStyle.mNumSpaces == 0) { - extra += aTextStyle.mRemainingExtraSpace; + glyphWidth = aTextStyle.mSpaceWidth + aTextStyle.mWordSpacing + + aTextStyle.mExtraSpacePerSpace; + if ((PRUint32)--aTextStyle.mNumSpacesToRender < + (PRUint32)aTextStyle.mNumSpacesReceivingExtraJot) { + glyphWidth++; } - glyphWidth += extra; } else { if (lastFont != aTextStyle.mNormalFont) { @@ -2096,12 +2130,12 @@ nsTextFrame::GetWidthOrLength(nsIRenderingContext& aRenderingContext, glyphWidth = charWidth + aStyle.mLetterSpacing; } else if (ch == ' ') { - glyphWidth = aStyle.mSpaceWidth + aStyle.mWordSpacing; - nscoord extra = aStyle.mExtraSpacePerSpace; - if (--aStyle.mNumSpaces == 0) { - extra += aStyle.mRemainingExtraSpace; + glyphWidth = aStyle.mSpaceWidth + aStyle.mWordSpacing + + aStyle.mExtraSpacePerSpace; + if ((PRUint32)--aStyle.mNumSpacesToMeasure + < (PRUint32)aStyle.mNumSpacesReceivingExtraJot) { + glyphWidth++; } - glyphWidth += extra; } else { if (lastFont != aStyle.mNormalFont) { @@ -2147,6 +2181,45 @@ nsTextFrame::GetLengthSlowly(nsIRenderingContext& aRenderingContext, return GetWidthOrLength(aRenderingContext,aStyle,aBuffer,aLength,&aWidth,PR_FALSE); } +void +nsTextFrame::ComputeExtraJustificationSpacing(nsIRenderingContext& aRenderingContext, + TextStyle& aTextStyle, + PRUnichar* aBuffer, PRInt32 aLength, + PRInt32 aNumSpaces) +{ + if (aTextStyle.mJustifying) { + nscoord trueWidth; + + // OK, so this is a bit ugly. The problem is that to get the right margin + // nice and clean, we have to apply a little extra space to *some* of the + // spaces. It has to be the same ones every time or things will go haywire. + // This implies that the GetWidthOrLength and RenderString functions depend + // on a little bit of secret state: which part of the prepared text they are + // looking at. It turns out that they get called in a regular way: they look + // at the text from the beginning to the end. So we just count which spaces + // we're up to, for each context. + // This is not a great solution, but a perfect solution requires much more + // widespread changes, to explicitly annotate all the transformed text fragments + // that are passed around with their position in the transformed text + // for the entire frame. + aTextStyle.mNumSpacesToMeasure = 0; + aTextStyle.mExtraSpacePerSpace = 0; + aTextStyle.mNumSpacesReceivingExtraJot = 0; + + GetWidth(aRenderingContext, aTextStyle, aBuffer, aLength, &trueWidth); + + aTextStyle.mNumSpacesToMeasure = aNumSpaces; + aTextStyle.mNumSpacesToRender = aNumSpaces; + + nscoord extraSpace = mRect.width - trueWidth; + + if (extraSpace > 0 && aNumSpaces > 0) { + aTextStyle.mExtraSpacePerSpace = extraSpace/aNumSpaces; + aTextStyle.mNumSpacesReceivingExtraJot = + extraSpace - aTextStyle.mExtraSpacePerSpace*aNumSpaces; + } + } +} void nsTextFrame::PaintTextSlowly(nsIPresContext* aPresContext, @@ -2173,10 +2246,11 @@ nsTextFrame::PaintTextSlowly(nsIPresContext* aPresContext, nsCOMPtr lb; doc->GetLineBreaker(getter_AddRefs(lb)); nsTextTransformer tx(lb, nsnull); - aTextStyle.mNumSpaces = PrepareUnicodeText(tx, - (displaySelection - ? &indexBuffer : nsnull), - &paintBuffer, &textLength); + PRInt32 numSpaces; + + numSpaces = PrepareUnicodeText(tx, (displaySelection ? &indexBuffer : nsnull), + &paintBuffer, &textLength); + PRInt32* ip = indexBuffer.mBuffer; PRUnichar* text = paintBuffer.mBuffer; @@ -2185,6 +2259,7 @@ nsTextFrame::PaintTextSlowly(nsIPresContext* aPresContext, GetFrameState(&frameState); isSelected = (frameState & NS_FRAME_SELECTED_CONTENT) == NS_FRAME_SELECTED_CONTENT; if (0 != textLength) { + ComputeExtraJustificationSpacing(aRenderingContext, aTextStyle, text, textLength, numSpaces); if (!displaySelection || !isSelected) { // When there is no selection showing, use the fastest and // simplest rendering approach @@ -2550,8 +2625,7 @@ nsTextFrame::GetPosition(nsIPresContext* aCX, rv = shell->CreateRenderingContext(this, getter_AddRefs(acx)); if (NS_SUCCEEDED(rv)) { TextStyle ts(aCX, *acx, mStyleContext); - if (ts.mSmallCaps || ts.mWordSpacing || ts.mLetterSpacing) { - + if (ts.mSmallCaps || ts.mWordSpacing || ts.mLetterSpacing || ts.mJustifying) { nsresult result = GetPositionSlowly(aCX, acx, aPoint, aNewContent, aContentOffset); aContentOffsetEnd = aContentOffset; @@ -2582,6 +2656,7 @@ nsTextFrame::GetPosition(nsIPresContext* aCX, doc->GetLineBreaker(getter_AddRefs(lb)); nsTextTransformer tx(lb, nsnull); PRInt32 textLength; + // no need to worry about justification, that's always on the slow path PrepareUnicodeText(tx, &indexBuffer, &paintBuffer, &textLength); if (textLength <=0) { @@ -2593,7 +2668,7 @@ nsTextFrame::GetPosition(nsIPresContext* aCX, nsIView * view; GetOffsetFromView(aCX, origin, &view); -//IF SYLE SAYS TO SELCT TO END OF FRAME HERE... +//IF STYLE SAYS TO SELECT TO END OF FRAME HERE... nsCOMPtr prefs; PRInt32 prefInt = 0; rv = nsServiceManager::GetService(kPrefCID, @@ -2880,7 +2955,12 @@ nsTextFrame::GetPointFromOffset(nsIPresContext* aPresContext, doc->GetLineBreaker(getter_AddRefs(lb)); nsTextTransformer tx(lb, nsnull); PRInt32 textLength; - PrepareUnicodeText(tx, &indexBuffer, &paintBuffer, &textLength); + PRInt32 numSpaces; + + numSpaces = PrepareUnicodeText(tx, &indexBuffer, &paintBuffer, &textLength); + + ComputeExtraJustificationSpacing(*inRendContext, ts, paintBuffer.mBuffer, textLength, numSpaces); + PRInt32* ip = indexBuffer.mBuffer; if (inOffset > mContentLength){ @@ -2889,7 +2969,7 @@ nsTextFrame::GetPointFromOffset(nsIPresContext* aPresContext, } nscoord width = mRect.width; - if (ts.mSmallCaps || (0 != ts.mWordSpacing) || (0 != ts.mLetterSpacing)) + if (ts.mSmallCaps || (0 != ts.mWordSpacing) || (0 != ts.mLetterSpacing) || ts.mJustifying) { GetWidth(*inRendContext, ts, paintBuffer.mBuffer, ip[inOffset]-mContentOffset, @@ -2907,7 +2987,8 @@ nsTextFrame::GetPointFromOffset(nsIPresContext* aPresContext, // to the total width, so the caret appears // in the proper place! // - width += ts.mSpaceWidth; + // NOTE: the trailing whitespace includes the word spacing!! + width += ts.mSpaceWidth + ts.mWordSpacing; } outPoint->x = width; @@ -3502,6 +3583,7 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext, #ifdef _WIN32 PRBool measureTextRuns = !aTextData.mComputeMaxWordWidth && !aTs.mPreformatted && !aTs.mSmallCaps && !aTs.mWordSpacing && !aTs.mLetterSpacing; + // Don't measure text runs with letter spacing active, it doesn't work #else PRBool measureTextRuns = PR_FALSE; #endif @@ -3579,10 +3661,17 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext, mState |= TEXT_SKIP_LEADING_WS; continue; } + + // NOTE: Even if the textRun absorbs the whitespace below, we still + // want to remember that we're breakable. + aTextData.mIsBreakable = PR_TRUE; + aTextData.mFirstLetterOK = PR_FALSE; + if ('\t' == firstChar) { // Expand tabs to the proper width wordLen = 8 - (7 & column); - width = aTs.mSpaceWidth * wordLen; + // Apply word spacing to every space derived from a tab + width = (aTs.mSpaceWidth + aTs.mWordSpacing)*wordLen; // Because we have to expand the tab when rendering consider that // a transformation of the text @@ -3594,10 +3683,9 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext, continue; } else { - width = (wordLen * aTs.mSpaceWidth) + aTs.mWordSpacing;// XXX simplistic + // Apply word spacing to every space, if there's more than one + width = wordLen*(aTs.mWordSpacing + aTs.mSpaceWidth);// XXX simplistic } - aTextData.mIsBreakable = PR_TRUE; - aTextData.mFirstLetterOK = PR_FALSE; if (aTextData.mMeasureText) { // See if there is room for the text @@ -3612,7 +3700,6 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext, endsInWhitespace = PR_TRUE; prevOffset = aTextData.mOffset; aTextData.mOffset += contentLen; - } else { // See if the first thing in the section of text is a // non-breaking space (html nbsp entity). If it is then make @@ -3704,6 +3791,7 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext, MeasureTextRun: #ifdef _WIN32 PRInt32 numCharsFit; + // These calls can return numCharsFit not positioned at a break in the textRun. Beware. if (aTx.TransformedTextIsAscii()) { aReflowState.rendContext->GetWidth((char*)aTx.GetWordBuffer(), textRun.mTotalNumChars, maxWidth - aTextData.mX, @@ -3722,22 +3810,43 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext, } // Find the index of the last segment that fit - PRInt32 lastSegment = textRun.mNumSegments - 1; - if (numCharsFit != textRun.mTotalNumChars) { + PRInt32 lastSegment; + if (numCharsFit == textRun.mTotalNumChars) { // fast path, normal case + lastSegment = textRun.mNumSegments - 1; + } else { for (lastSegment = 0; textRun.mBreaks[lastSegment] < numCharsFit; lastSegment++) ; NS_ASSERTION(lastSegment < textRun.mNumSegments, "failed to find segment"); + // now we have textRun.mBreaks[lastSegment] >= numCharsFit + /* O'Callahan XXX: This snippet together with the snippet below prevents mail from loading + Justification seems to work just fine without these changes. + We get into trouble in a case where lastSegment gets set to -1 + + if (textRun.mBreaks[lastSegment] > numCharsFit) { + // NOTE: this segment did not actually fit! + lastSegment--; + } + */ } + /* O'Callahan XXX: This snippet together with the snippet above prevents mail from loading + + if (lastSegment < 0) { + // no segments fit + break; + } else */ if (lastSegment == 0) { // Only one segment fit prevColumn = column; prevOffset = aTextData.mOffset; - } else { // The previous state is for the next to last word prevColumn = textRun.mBreaks[lastSegment - 1]; prevOffset = textRun.mSegments[lastSegment - 1].ContentLen(); + // NOTE: The textRun data are relative to the last updated column and offset! + prevColumn = column + textRun.mBreaks[lastSegment - 1]; + prevOffset = aTextData.mOffset + textRun.mSegments[lastSegment - 1].ContentLen(); } + aTextData.mX += width; column += numCharsFit; aTextData.mOffset += textRun.mSegments[lastSegment].ContentLen(); @@ -3775,7 +3884,8 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext, aTextData.mX = mRect.width; if (mState & TEXT_TRIMMED_WS) { // Add back in the width of a space since it was trimmed away last time - aTextData.mX += aTs.mSpaceWidth; + // NOTE: Trailing whitespace includes word spacing! + aTextData.mX += aTs.mSpaceWidth + aTs.mWordSpacing; } } @@ -4056,19 +4166,22 @@ nsTextFrame::Reflow(nsIPresContext* aPresContext, // current frame width -or- // we're not wrapping text and we're at the same column as before (this is // an issue for preformatted tabbed text only) + // - AND we aren't justified (in which case the frame width has already been tweaked and can't be used) if ((eReflowReason_Resize == aReflowState.reason) && (0 == (mState & NS_FRAME_IS_DIRTY))) { nscoord realWidth = mRect.width; if (mState & TEXT_TRIMMED_WS) { - realWidth += ts.mSpaceWidth; + // NOTE: Trailing whitespace includes word spacing! + realWidth += ts.mSpaceWidth + ts.mWordSpacing; } if (!mNextInFlow && (mState & TEXT_OPTIMIZE_RESIZE) && !aMetrics.maxElementSize && (lastTimeWeSkippedLeadingWS == skipWhitespace) && ((wrapping && (maxWidth >= realWidth)) || - (!wrapping && (prevColumn == column)))) { + (!wrapping && (prevColumn == column))) && + !ts.mJustifying) { // We can skip measuring of text and use the value from our // previous reflow measureText = PR_FALSE; @@ -4120,6 +4233,23 @@ nsTextFrame::Reflow(nsIPresContext* aPresContext, mContentOffset = startingOffset; mContentLength = textData.mOffset - startingOffset; + // Compute space and letter counts for justification, if required + if (ts.mJustifying) { + PRInt32 numSpaces; + PRInt32 textLength; + + // This will include a space for trailing whitespace, if any is present. + // This is corrected for in nsLineLayout::TrimWhiteSpaceIn. + + // This work could be done in MeasureText, but it's complex to do accurately + // there because of the need to repair counts when wrapped words are backed out. + // So I do it via PrepareUnicodeText ... a little slower perhaps, but a lot saner, + // and it localizes the counting logic to one place. + numSpaces = PrepareUnicodeText(tx, nsnull, nsnull, &textLength); + lineLayout.SetTextJustificationWeights(numSpaces, textLength - numSpaces); + } + + #ifdef MOZ_MATHML // Simple minded code to also return the bounding metrics if the caller wants it... // More consolidation is needed -- a better approach is to follow what is done by @@ -4226,6 +4356,11 @@ nsTextFrame::TrimTrailingWhiteSpace(nsIPresContext* aPresContext, mStyleContext->GetStyleData(eStyleStruct_Font); aRC.SetFont(fontStyle->mFont); aRC.GetWidth(' ', dw); + // NOTE: Trailing whitespace includes word spacing! + PRIntn unit = textStyle->mWordSpacing.GetUnit(); + if (eStyleUnit_Coord == unit) { + dw += textStyle->mWordSpacing.GetCoordValue(); + } } } } @@ -4406,6 +4541,8 @@ nsTextFrame::ComputeWordFragmentWidth(nsIPresContext* aPresContext, } else { rc.GetWidth(bp, wordLen, width); + // NOTE: Don't forget to add letter spacing for the word fragment! + width += wordLen*ts.mLetterSpacing; } rc.SetFont(oldfm);