From d30758e3fc193a255d6d2ba5b3dc1aac220039b3 Mon Sep 17 00:00:00 2001 From: "karnaze%netscape.com" Date: Mon, 5 Nov 2001 00:15:51 +0000 Subject: [PATCH] bug 97138 - rework of table, row group, row, cell height code. sr=attinasi, r=alexsavulov git-svn-id: svn://10.0.0.236/trunk@107299 18797224-902f-48f8-a5cc-f745e15eee43 --- .../layout/base/public/nsHTMLReflowState.h | 9 +- mozilla/layout/generic/nsHTMLReflowState.cpp | 12 +- mozilla/layout/generic/nsHTMLReflowState.h | 9 +- .../html/base/src/nsHTMLReflowState.cpp | 12 +- mozilla/layout/html/table/src/nsCellMap.cpp | 22 +- .../html/table/src/nsTableCellFrame.cpp | 38 +- .../layout/html/table/src/nsTableCellFrame.h | 30 +- .../layout/html/table/src/nsTableColFrame.cpp | 1 + .../layout/html/table/src/nsTableFrame.cpp | 441 ++++++++---- mozilla/layout/html/table/src/nsTableFrame.h | 55 +- .../layout/html/table/src/nsTableRowFrame.cpp | 280 ++++---- .../layout/html/table/src/nsTableRowFrame.h | 148 +++- .../html/table/src/nsTableRowGroupFrame.cpp | 653 +++++++++--------- .../html/table/src/nsTableRowGroupFrame.h | 53 +- mozilla/layout/tables/nsCellMap.cpp | 22 +- mozilla/layout/tables/nsTableCellFrame.cpp | 38 +- mozilla/layout/tables/nsTableCellFrame.h | 30 +- mozilla/layout/tables/nsTableColFrame.cpp | 1 + mozilla/layout/tables/nsTableFrame.cpp | 441 ++++++++---- mozilla/layout/tables/nsTableFrame.h | 55 +- mozilla/layout/tables/nsTableRowFrame.cpp | 280 ++++---- mozilla/layout/tables/nsTableRowFrame.h | 148 +++- .../layout/tables/nsTableRowGroupFrame.cpp | 653 +++++++++--------- mozilla/layout/tables/nsTableRowGroupFrame.h | 53 +- 24 files changed, 2180 insertions(+), 1304 deletions(-) diff --git a/mozilla/layout/base/public/nsHTMLReflowState.h b/mozilla/layout/base/public/nsHTMLReflowState.h index ede29e88aa8..85c0d7aff11 100644 --- a/mozilla/layout/base/public/nsHTMLReflowState.h +++ b/mozilla/layout/base/public/nsHTMLReflowState.h @@ -239,7 +239,14 @@ struct nsHTMLReflowState { // This value keeps track of how deeply nested a given reflow state // is from the top of the frame tree. - PRInt32 mReflowDepth; + PRInt16 mReflowDepth; + + struct ReflowStateFlags { + //unsigned mUseAlignCharOffset:1; // ditto + //unsigned isTopOfPage:1; // ditto + PRUint16 mSpecialTableReflow:1; // used by tables to communicate special reflow in process + PRUint16 mUnused:15; + } mFlags; #ifdef IBMBIDI nscoord mRightEdge; diff --git a/mozilla/layout/generic/nsHTMLReflowState.cpp b/mozilla/layout/generic/nsHTMLReflowState.cpp index 356eb54d8e4..e5bc6a49be1 100644 --- a/mozilla/layout/generic/nsHTMLReflowState.cpp +++ b/mozilla/layout/generic/nsHTMLReflowState.cpp @@ -86,7 +86,7 @@ nsHTMLReflowState::nsHTMLReflowState(nsIPresContext* aPresContext, : mReflowDepth(0) { NS_PRECONDITION(nsnull != aRenderingContext, "no rendering context"); - + mFlags.mSpecialTableReflow = mFlags.mUnused = 0; parentReflowState = nsnull; frame = aFrame; reason = aReason; @@ -114,6 +114,7 @@ nsHTMLReflowState::nsHTMLReflowState(nsIPresContext* aPresContext, { NS_PRECONDITION(nsnull != aRenderingContext, "no rendering context"); + mFlags.mSpecialTableReflow = mFlags.mUnused = 0; reason = eReflowReason_Incremental; parentReflowState = nsnull; frame = aFrame; @@ -138,7 +139,8 @@ nsHTMLReflowState::nsHTMLReflowState(nsIPresContext* aPresContext, nsIFrame* aFrame, const nsSize& aAvailableSpace, nsReflowReason aReason) - : mReflowDepth(aParentReflowState.mReflowDepth + 1) + : mReflowDepth(aParentReflowState.mReflowDepth + 1), + mFlags(aParentReflowState.mFlags) { parentReflowState = &aParentReflowState; frame = aFrame; @@ -167,7 +169,8 @@ nsHTMLReflowState::nsHTMLReflowState(nsIPresContext* aPresContext, const nsHTMLReflowState& aParentReflowState, nsIFrame* aFrame, const nsSize& aAvailableSpace) - : mReflowDepth(aParentReflowState.mReflowDepth + 1) + : mReflowDepth(aParentReflowState.mReflowDepth + 1), + mFlags(aParentReflowState.mFlags) { parentReflowState = &aParentReflowState; frame = aFrame; @@ -195,7 +198,8 @@ nsHTMLReflowState::nsHTMLReflowState(nsIPresContext* aPresContext, const nsSize& aAvailableSpace, nscoord aContainingBlockWidth, nscoord aContainingBlockHeight) - : mReflowDepth(aParentReflowState.mReflowDepth + 1) + : mReflowDepth(aParentReflowState.mReflowDepth + 1), + mFlags(aParentReflowState.mFlags) { parentReflowState = &aParentReflowState; frame = aFrame; diff --git a/mozilla/layout/generic/nsHTMLReflowState.h b/mozilla/layout/generic/nsHTMLReflowState.h index ede29e88aa8..85c0d7aff11 100644 --- a/mozilla/layout/generic/nsHTMLReflowState.h +++ b/mozilla/layout/generic/nsHTMLReflowState.h @@ -239,7 +239,14 @@ struct nsHTMLReflowState { // This value keeps track of how deeply nested a given reflow state // is from the top of the frame tree. - PRInt32 mReflowDepth; + PRInt16 mReflowDepth; + + struct ReflowStateFlags { + //unsigned mUseAlignCharOffset:1; // ditto + //unsigned isTopOfPage:1; // ditto + PRUint16 mSpecialTableReflow:1; // used by tables to communicate special reflow in process + PRUint16 mUnused:15; + } mFlags; #ifdef IBMBIDI nscoord mRightEdge; diff --git a/mozilla/layout/html/base/src/nsHTMLReflowState.cpp b/mozilla/layout/html/base/src/nsHTMLReflowState.cpp index 356eb54d8e4..e5bc6a49be1 100644 --- a/mozilla/layout/html/base/src/nsHTMLReflowState.cpp +++ b/mozilla/layout/html/base/src/nsHTMLReflowState.cpp @@ -86,7 +86,7 @@ nsHTMLReflowState::nsHTMLReflowState(nsIPresContext* aPresContext, : mReflowDepth(0) { NS_PRECONDITION(nsnull != aRenderingContext, "no rendering context"); - + mFlags.mSpecialTableReflow = mFlags.mUnused = 0; parentReflowState = nsnull; frame = aFrame; reason = aReason; @@ -114,6 +114,7 @@ nsHTMLReflowState::nsHTMLReflowState(nsIPresContext* aPresContext, { NS_PRECONDITION(nsnull != aRenderingContext, "no rendering context"); + mFlags.mSpecialTableReflow = mFlags.mUnused = 0; reason = eReflowReason_Incremental; parentReflowState = nsnull; frame = aFrame; @@ -138,7 +139,8 @@ nsHTMLReflowState::nsHTMLReflowState(nsIPresContext* aPresContext, nsIFrame* aFrame, const nsSize& aAvailableSpace, nsReflowReason aReason) - : mReflowDepth(aParentReflowState.mReflowDepth + 1) + : mReflowDepth(aParentReflowState.mReflowDepth + 1), + mFlags(aParentReflowState.mFlags) { parentReflowState = &aParentReflowState; frame = aFrame; @@ -167,7 +169,8 @@ nsHTMLReflowState::nsHTMLReflowState(nsIPresContext* aPresContext, const nsHTMLReflowState& aParentReflowState, nsIFrame* aFrame, const nsSize& aAvailableSpace) - : mReflowDepth(aParentReflowState.mReflowDepth + 1) + : mReflowDepth(aParentReflowState.mReflowDepth + 1), + mFlags(aParentReflowState.mFlags) { parentReflowState = &aParentReflowState; frame = aFrame; @@ -195,7 +198,8 @@ nsHTMLReflowState::nsHTMLReflowState(nsIPresContext* aPresContext, const nsSize& aAvailableSpace, nscoord aContainingBlockWidth, nscoord aContainingBlockHeight) - : mReflowDepth(aParentReflowState.mReflowDepth + 1) + : mReflowDepth(aParentReflowState.mReflowDepth + 1), + mFlags(aParentReflowState.mFlags) { parentReflowState = &aParentReflowState; frame = aFrame; diff --git a/mozilla/layout/html/table/src/nsCellMap.cpp b/mozilla/layout/html/table/src/nsCellMap.cpp index 1aea50ec8d7..41ad8dfa13a 100644 --- a/mozilla/layout/html/table/src/nsCellMap.cpp +++ b/mozilla/layout/html/table/src/nsCellMap.cpp @@ -337,15 +337,19 @@ nsTableCellMap::RemoveRows(nsIPresContext* aPresContext, PRInt32 nsTableCellMap::GetNumCellsOriginatingInRow(PRInt32 aRowIndex) { - PRInt32 cellCount = 0; - CellData* tempCell; - do - { - tempCell = GetCellAt(aRowIndex,cellCount); - if (tempCell) - cellCount++; - }while(tempCell); - return cellCount; + PRInt32 originCount = 0; + + CellData* cellData; + PRInt32 colIndex = 0; + + do { + cellData = GetCellAt(aRowIndex, colIndex); + if (cellData && cellData->GetCellFrame()) + originCount++; + colIndex++; + } while(cellData); + + return originCount; } PRInt32 diff --git a/mozilla/layout/html/table/src/nsTableCellFrame.cpp b/mozilla/layout/html/table/src/nsTableCellFrame.cpp index 1eec30abe4b..523dcb2637b 100644 --- a/mozilla/layout/html/table/src/nsTableCellFrame.cpp +++ b/mozilla/layout/html/table/src/nsTableCellFrame.cpp @@ -91,6 +91,22 @@ nsTableCellFrame::~nsTableCellFrame() #endif } +nsTableCellFrame* +nsTableCellFrame::GetNextCell() const +{ + nsIFrame* childFrame; + GetNextSibling(&childFrame); + while (childFrame) { + nsCOMPtr frameType; + childFrame->GetFrameType(getter_AddRefs(frameType)); + if (nsLayoutAtoms::tableCellFrame == frameType.get()) { + return (nsTableCellFrame*)childFrame; + } + childFrame->GetNextSibling(&childFrame); + } + return nsnull; +} + NS_IMETHODIMP nsTableCellFrame::Init(nsIPresContext* aPresContext, nsIContent* aContent, @@ -694,6 +710,15 @@ NS_METHOD nsTableCellFrame::Reflow(nsIPresContext* aPresContext, } nsresult rv = NS_OK; + + if ((NS_UNCONSTRAINEDSIZE != aReflowState.availableWidth) && + ((NS_UNCONSTRAINEDSIZE == aReflowState.mComputedHeight) || + (0 == aReflowState.mComputedHeight)) && + !mPrevInFlow && + nsTableFrame::IsPctHeight(mStyleContext)) { + nsTableFrame::NotifyAncestorsOfSpecialReflow(aReflowState); + SetNeedSpecialReflow(PR_TRUE); + } // this should probably be cached somewhere nsCompatibility compatMode; aPresContext->GetCompatibilityMode(&compatMode); @@ -714,6 +739,8 @@ NS_METHOD nsTableCellFrame::Reflow(nsIPresContext* aPresContext, /* XXX: remove tableFrame when border-collapse inherits */ nsTableFrame* tableFrame=nsnull; rv = nsTableFrame::GetTableFrame(this, tableFrame); + nsTableFrame* tableFrameFirstInFlow = (nsTableFrame*)tableFrame->GetFirstInFlow(); + nsMargin borderPadding = aReflowState.mComputedPadding; nsMargin border; GetCellBorder(border, tableFrame); @@ -768,6 +795,10 @@ NS_METHOD nsTableCellFrame::Reflow(nsIPresContext* aPresContext, kidSize.width=kidSize.height=kidSize.ascent=kidSize.descent=0; SetPriorAvailWidth(aReflowState.availableWidth); nsIFrame* firstKid = mFrames.FirstChild(); + + if (aReflowState.mFlags.mSpecialTableReflow) { + ((nsHTMLReflowState&)aReflowState).mComputedHeight = mRect.height - topInset - bottomInset; + } nsHTMLReflowState kidReflowState(aPresContext, aReflowState, firstKid, availSize); @@ -898,8 +929,7 @@ NS_METHOD nsTableCellFrame::Reflow(nsIPresContext* aPresContext, // if the table allocated extra vertical space to row groups, rows, cells in pagination mode // then use that height as the desired height unless the cell needs to split. - nsTableFrame* tableFrameFirstInFlow = (nsTableFrame*)tableFrame->GetFirstInFlow(); - if ((NS_FRAME_COMPLETE == aStatus) && tableFrameFirstInFlow->IsThirdPassReflow()) { + if ((NS_FRAME_COMPLETE == aStatus) && aReflowState.mFlags.mSpecialTableReflow) { cellHeight = PR_MAX(cellHeight, mRect.height); } // next determine the cell's width @@ -942,6 +972,10 @@ NS_METHOD nsTableCellFrame::Reflow(nsIPresContext* aPresContext, // remember my desired size for this reflow SetDesiredSize(aDesiredSize); + if (aReflowState.mFlags.mSpecialTableReflow) { + SetNeedSpecialReflow(PR_FALSE); + } + #if defined DEBUG_TABLE_REFLOW_TIMING nsTableFrame::DebugReflow(this, (nsHTMLReflowState&)aReflowState, &aDesiredSize, aStatus); #endif diff --git a/mozilla/layout/html/table/src/nsTableCellFrame.h b/mozilla/layout/html/table/src/nsTableCellFrame.h index 04077abfafb..e6cf741b938 100644 --- a/mozilla/layout/html/table/src/nsTableCellFrame.h +++ b/mozilla/layout/html/table/src/nsTableCellFrame.h @@ -49,7 +49,8 @@ class nsHTMLValue; /** * Additional frame-state bits */ -#define NS_TABLE_CELL_FRAME_CONTENT_EMPTY 0x80000000 +#define NS_TABLE_CELL_CONTENT_EMPTY 0x80000000 +#define NS_TABLE_CELL_NEED_SPECIAL_REFLOW 0x40000000 /** * nsTableCellFrame @@ -239,11 +240,16 @@ public: PRBool GetContentEmpty(); void SetContentEmpty(PRBool aContentEmpty); + PRBool NeedSpecialReflow(); + void SetNeedSpecialReflow(PRBool aContentEmpty); + // The collapse offset is (0,0) except for cells originating in a row/col which is collapsed void SetCollapseOffsetX(nsIPresContext* aPresContext, nscoord aXOffset); void SetCollapseOffsetY(nsIPresContext* aPresContext, nscoord aYOffset); void GetCollapseOffset(nsIPresContext* aPresContext, nsPoint& aOffset); + nsTableCellFrame* GetNextCell() const; + protected: /** implement abstract method on nsHTMLContainerFrame */ virtual PRIntn GetSkipSides() const; @@ -332,19 +338,33 @@ inline nsSize nsTableCellFrame::GetPass1MaxElementSize() const inline PRBool nsTableCellFrame::GetContentEmpty() { - return (mState & NS_TABLE_CELL_FRAME_CONTENT_EMPTY) == - NS_TABLE_CELL_FRAME_CONTENT_EMPTY; + return (mState & NS_TABLE_CELL_CONTENT_EMPTY) == + NS_TABLE_CELL_CONTENT_EMPTY; } inline void nsTableCellFrame::SetContentEmpty(PRBool aContentEmpty) { if (aContentEmpty) { - mState |= NS_TABLE_CELL_FRAME_CONTENT_EMPTY; + mState |= NS_TABLE_CELL_CONTENT_EMPTY; } else { - mState &= ~NS_TABLE_CELL_FRAME_CONTENT_EMPTY; + mState &= ~NS_TABLE_CELL_CONTENT_EMPTY; } } +inline PRBool nsTableCellFrame::NeedSpecialReflow() +{ + return (mState & NS_TABLE_CELL_NEED_SPECIAL_REFLOW) == + NS_TABLE_CELL_NEED_SPECIAL_REFLOW; +} + +inline void nsTableCellFrame::SetNeedSpecialReflow(PRBool aValue) +{ + if (aValue) { + mState |= NS_TABLE_CELL_NEED_SPECIAL_REFLOW; + } else { + mState &= ~NS_TABLE_CELL_NEED_SPECIAL_REFLOW; + } +} #endif diff --git a/mozilla/layout/html/table/src/nsTableColFrame.cpp b/mozilla/layout/html/table/src/nsTableColFrame.cpp index 419eafe1c84..53a5b035632 100644 --- a/mozilla/layout/html/table/src/nsTableColFrame.cpp +++ b/mozilla/layout/html/table/src/nsTableColFrame.cpp @@ -84,6 +84,7 @@ void nsTableColFrame::SetType(nsTableColType aType) { mBits.mType = aType - eColContent; } + // XXX what about other style besides width nsStyleCoord nsTableColFrame::GetStyleWidth() const { diff --git a/mozilla/layout/html/table/src/nsTableFrame.cpp b/mozilla/layout/html/table/src/nsTableFrame.cpp index 61053481929..57e0e38dc59 100644 --- a/mozilla/layout/html/table/src/nsTableFrame.cpp +++ b/mozilla/layout/html/table/src/nsTableFrame.cpp @@ -1580,8 +1580,15 @@ nsTableFrame::GetSkipSides() const PRBool nsTableFrame::NeedsReflow(const nsHTMLReflowState& aReflowState) { PRBool result = PR_TRUE; - if ((eReflowReason_Incremental == aReflowState.reason) && - (NS_UNCONSTRAINEDSIZE == aReflowState.availableHeight)) { + if (eReflowReason_Resize == aReflowState.reason) { + if (aReflowState.mFlags.mSpecialTableReflow && + !NeedSpecialReflow() && + !NeedToInitiateSpecialReflow()) { + result = PR_FALSE; + } + } + else if ((eReflowReason_Incremental == aReflowState.reason) && + (NS_UNCONSTRAINEDSIZE == aReflowState.availableHeight)) { // It's an incremental reflow and we're in galley mode. Only // do a full reflow if we need to. #ifndef TABLE_REFLOW_COALESCING_OFF @@ -1809,6 +1816,30 @@ ProcessRowInserted(nsIPresContext* aPresContext, } } +void +nsTableFrame::NotifyAncestorsOfSpecialReflow(const nsHTMLReflowState& aReflowState) +{ + const nsHTMLReflowState* rs = aReflowState.parentReflowState; + while (rs) { + nsCOMPtr frameType; + rs->frame->GetFrameType(getter_AddRefs(frameType)); + if (nsLayoutAtoms::tableCellFrame == frameType.get()) { + ((nsTableCellFrame*)rs->frame)->SetNeedSpecialReflow(PR_TRUE); + } + else if (nsLayoutAtoms::tableRowFrame == frameType.get()) { + ((nsTableRowFrame*)rs->frame)->SetNeedSpecialReflow(PR_TRUE); + } + else if (nsLayoutAtoms::tableRowGroupFrame == frameType.get()) { + ((nsTableRowGroupFrame*)rs->frame)->SetNeedSpecialReflow(PR_TRUE); + } + else if (nsLayoutAtoms::tableFrame == frameType.get()) { + ((nsTableFrame*)rs->frame)->SetNeedToInitiateSpecialReflow(PR_TRUE); + break; + } + rs = rs->parentReflowState; + } +} + /* overview: if mFirstPassValid is false, this is our first time through since content was last changed do pass 1 @@ -1904,36 +1935,57 @@ NS_METHOD nsTableFrame::Reflow(nsIPresContext* aPresContext, if (NS_FAILED(rv)) return rv; - PRBool needPass3Reflow = PR_FALSE; + PRBool haveDesiredHeight = PR_FALSE; PRBool balanced = PR_FALSE; // Reflow the entire table. This phase is necessary during a constrained initial reflow // and other reflows which require either a strategy init or balance. This isn't done // during an unconstrained reflow because another reflow will be processed later. if (NeedsReflow(aReflowState) && (NS_UNCONSTRAINEDSIZE != aReflowState.availableWidth)) { - // see if an extra (3rd) reflow will be necessary in pagination mode when there is a specified table height - if (isPaginated && !mPrevInFlow && (NS_UNCONSTRAINEDSIZE != aReflowState.availableHeight)) { - nscoord tableSpecifiedHeight = CalcBorderBoxHeight(aReflowState); - if ((tableSpecifiedHeight > 0) && - (tableSpecifiedHeight != NS_UNCONSTRAINEDSIZE)) { - needPass3Reflow = PR_TRUE; + if (!mPrevInFlow) { + // see if an extra reflow will be necessary when there is a pct height but no height on the parent + if ( ((NS_UNCONSTRAINEDSIZE == aReflowState.mComputedHeight) || + (0 == aReflowState.mComputedHeight)) && IsPctHeight(mStyleContext)) { + NotifyAncestorsOfSpecialReflow(aReflowState); + SetNeedSpecialReflow(PR_TRUE); + } + // see if an extra reflow will be necessary in pagination mode when there is a specified table height + else if (isPaginated && (NS_UNCONSTRAINEDSIZE != aReflowState.availableHeight)) { + nscoord tableSpecifiedHeight = CalcBorderBoxHeight(aReflowState); + if ((tableSpecifiedHeight > 0) && + (tableSpecifiedHeight != NS_UNCONSTRAINEDSIZE)) { + SetNeedToInitiateSpecialReflow(PR_TRUE); + } } } // if we need to reflow the table an extra time, then don't constrain the height of the previous reflow - nscoord availHeight = (needPass3Reflow) ? NS_UNCONSTRAINEDSIZE : aReflowState.availableHeight; + nscoord availHeight = !aReflowState.mFlags.mSpecialTableReflow && + (NeedSpecialReflow() | NeedToInitiateSpecialReflow()) + ? NS_UNCONSTRAINEDSIZE : aReflowState.availableHeight; ReflowTable(aPresContext, aDesiredSize, aReflowState, availHeight, nextReason, doCollapse, balanced, aStatus); - if (needPass3Reflow) { + if (!aReflowState.mFlags.mSpecialTableReflow && NeedToInitiateSpecialReflow() && !NeedSpecialReflow()) { aDesiredSize.height = CalcDesiredHeight(aPresContext, aReflowState); // distributes extra vertical space to rows - SetThirdPassReflow(PR_TRUE); // set it and leave it set for frames that may split + ((nsHTMLReflowState::ReflowStateFlags&)aReflowState.mFlags).mSpecialTableReflow = PR_TRUE; ReflowTable(aPresContext, aDesiredSize, aReflowState, aReflowState.availableHeight, nextReason, doCollapse, balanced, aStatus); + haveDesiredHeight = PR_TRUE;; } } + else if (aReflowState.mFlags.mSpecialTableReflow) { + aDesiredSize.width = mRect.width; + aDesiredSize.height = mRect.height; +#if defined DEBUG_TABLE_REFLOW_TIMING + nsTableFrame::DebugReflow(this, (nsHTMLReflowState&)aReflowState, &aDesiredSize, aStatus); +#endif + SetNeedSpecialReflow(PR_FALSE); + SetNeedToInitiateSpecialReflow(PR_FALSE); + return NS_OK; + } - aDesiredSize.width = GetDesiredWidth(); - if (!needPass3Reflow) { + aDesiredSize.width = GetDesiredWidth(); + if (!haveDesiredHeight) { aDesiredSize.height = CalcDesiredHeight(aPresContext, aReflowState); } @@ -1986,6 +2038,10 @@ NS_METHOD nsTableFrame::Reflow(nsIPresContext* aPresContext, } } + if (aReflowState.mFlags.mSpecialTableReflow) { + SetNeedSpecialReflow(PR_FALSE); + SetNeedToInitiateSpecialReflow(PR_FALSE); + } #if defined DEBUG_TABLE_REFLOW_TIMING nsTableFrame::DebugReflow(this, (nsHTMLReflowState&)aReflowState, &aDesiredSize, aStatus); #endif @@ -3253,81 +3309,6 @@ nsTableFrame::CalcDesiredWidth(const nsHTMLReflowState& aReflowState) return tableWidth; } -/** - get the table height attribute - if it is auto, table height = SUM(height of rowgroups) - else if (resolved table height attribute > SUM(height of rowgroups)) - proportionately distribute extra height to each row - we assume we are passed in the default table height==the sum of the heights of the table's rowgroups - in aDesiredSize.height. - */ -void -nsTableFrame::DistributeSpaceToCells(nsIPresContext* aPresContext, - const nsHTMLReflowState& aReflowState, - nsIFrame* aRowGroupFrame) -{ - // now that all of the rows have been resized, resize the cells - nsTableRowGroupFrame* rowGroupFrame = (nsTableRowGroupFrame*)aRowGroupFrame; - nsIFrame * rowFrame = rowGroupFrame->GetFirstFrame(); - while (rowFrame) { - const nsStyleDisplay *rowDisplay; - rowFrame->GetStyleData(eStyleStruct_Display, ((const nsStyleStruct *&)rowDisplay)); - if (NS_STYLE_DISPLAY_TABLE_ROW == rowDisplay->mDisplay) { - ((nsTableRowFrame *)rowFrame)->DidResize(aPresContext, aReflowState); - } - rowGroupFrame->GetNextFrame(rowFrame, &rowFrame); - } -} - -void -nsTableFrame::DistributeSpaceToRows(nsIPresContext* aPresContext, - const nsHTMLReflowState& aReflowState, - nsIFrame* aRowGroupFrame, - nscoord aSumOfRowHeights, - nscoord aExcess, - nscoord& aExcessAllocated, - nscoord& aRowGroupYPos) -{ - // the rows in rowGroupFrame need to be expanded by rowHeightDelta[i] - // and the rowgroup itself needs to be expanded by SUM(row height deltas) - nscoord cellSpacingY = GetCellSpacingY(); - nsTableRowGroupFrame* rowGroupFrame = (nsTableRowGroupFrame*)aRowGroupFrame; - nsIFrame* rowFrame = rowGroupFrame->GetFirstFrame(); - nscoord excessForRowGroup = 0; - nscoord y = 0; - float p2t; - aPresContext->GetPixelsToTwips(&p2t); - while (rowFrame) { - const nsStyleDisplay *rowDisplay; - rowFrame->GetStyleData(eStyleStruct_Display, ((const nsStyleStruct *&)rowDisplay)); - if (NS_STYLE_DISPLAY_TABLE_ROW == rowDisplay->mDisplay) { - // the row needs to be expanded by the proportion this row contributed to the original height - nsRect rowRect; - rowFrame->GetRect(rowRect); - float percent = ((float)(rowRect.height)) / (float)aSumOfRowHeights; - nscoord excessForRow = RoundToPixel(NSToCoordRound((float)aExcess * percent), p2t); - excessForRow = PR_MIN(excessForRow, aExcess - aExcessAllocated); - - nsRect newRowRect(rowRect.x, y, rowRect.width, excessForRow + rowRect.height); - rowFrame->SetRect(aPresContext, newRowRect); - y = newRowRect.YMost() + cellSpacingY; - - aExcessAllocated += excessForRow; - excessForRowGroup += excessForRow; - } - - rowGroupFrame->GetNextFrame(rowFrame, &rowFrame); - } - - nsRect rowGroupRect; - aRowGroupFrame->GetRect(rowGroupRect); - nsRect newRowGroupRect(rowGroupRect.x, aRowGroupYPos, rowGroupRect.width, - excessForRowGroup + rowGroupRect.height); - aRowGroupFrame->SetRect(aPresContext, newRowGroupRect); - aRowGroupYPos = newRowGroupRect.YMost(); - - DistributeSpaceToCells(aPresContext, aReflowState, aRowGroupFrame); -} nscoord nsTableFrame::CalcDesiredHeight(nsIPresContext* aPresContext, @@ -3347,65 +3328,239 @@ nsTableFrame::CalcDesiredHeight(nsIPresContext* aPresContext, OrderRowGroups(rowGroups, numRowGroups, nsnull); if (numRowGroups <= 0) return 0; - nscoord naturalHeight = borderPadding.top + cellSpacingY + borderPadding.bottom; + nscoord desiredHeight = borderPadding.top + cellSpacingY + borderPadding.bottom; for (PRUint32 rgX = 0; rgX < numRowGroups; rgX++) { nsIFrame* rg = (nsIFrame*)rowGroups.ElementAt(rgX); if (rg) { nsRect rgRect; rg->GetRect(rgRect); - naturalHeight += rgRect.height + cellSpacingY; + desiredHeight += rgRect.height + cellSpacingY; } } - nscoord desiredHeight = naturalHeight; - // see if a specified table height requires dividing additional space to rows if (!mPrevInFlow) { nscoord tableSpecifiedHeight = CalcBorderBoxHeight(aReflowState); if ((tableSpecifiedHeight > 0) && (tableSpecifiedHeight != NS_UNCONSTRAINEDSIZE) && - (tableSpecifiedHeight > naturalHeight)) { - desiredHeight = tableSpecifiedHeight; - + (tableSpecifiedHeight > desiredHeight)) { + // proportionately distribute the excess height to unconstrained rows in each + // unconstrained row group.We don't need to do this if it's an unconstrained reflow if (NS_UNCONSTRAINEDSIZE != aReflowState.availableWidth) { - // proportionately distribute the excess height to each row. Note that we - // don't need to do this if it's an unconstrained reflow - nscoord excess = tableSpecifiedHeight - naturalHeight; - nscoord sumOfRowHeights = 0; - nscoord rowGroupYPos = aReflowState.mComputedBorderPadding.top + cellSpacingY; - - nsAutoVoidArray rowGroups; - PRUint32 numRowGroups; - OrderRowGroups(rowGroups, numRowGroups, nsnull); - - PRUint32 rgX; - for (rgX = 0; rgX < numRowGroups; rgX++) { - nsTableRowGroupFrame* rgFrame = - GetRowGroupFrame((nsIFrame*)rowGroups.ElementAt(rgX)); - if (rgFrame) { - sumOfRowHeights += rgFrame->GetHeightOfRows(aPresContext); - } - } - nscoord excessAllocated = 0; - for (rgX = 0; rgX < numRowGroups; rgX++) { - nsTableRowGroupFrame* rgFrame = - GetRowGroupFrame((nsIFrame*)rowGroups.ElementAt(rgX)); - if (rgFrame) { - DistributeSpaceToRows(aPresContext, aReflowState, rgFrame, sumOfRowHeights, - excess, excessAllocated, rowGroupYPos); - - // Make sure child views are properly positioned - // XXX what happens if childFrame is a scroll frame and this gets skipped? - nsTableFrame::RePositionViews(aPresContext, rgFrame); - } - rowGroupYPos += cellSpacingY; - } + DistributeHeightToRows(aPresContext, aReflowState, tableSpecifiedHeight - desiredHeight); } + desiredHeight = tableSpecifiedHeight; } } return desiredHeight; } +static +void ResizeCells(nsTableFrame& aTableFrame, + nsIPresContext* aPresContext, + const nsHTMLReflowState& aReflowState) +{ + nsAutoVoidArray rowGroups; + PRUint32 numRowGroups; + aTableFrame.OrderRowGroups(rowGroups, numRowGroups, nsnull); + + for (PRUint32 rgX = 0; (rgX < numRowGroups); rgX++) { + nsTableRowGroupFrame* rgFrame = aTableFrame.GetRowGroupFrame((nsIFrame*)rowGroups.ElementAt(rgX)); + nsTableRowFrame* rowFrame = rgFrame->GetFirstRow(); + while (rowFrame) { + rowFrame->DidResize(aPresContext, aReflowState); + rowFrame = rowFrame->GetNextRow(); + } + } +} + +void +nsTableFrame::DistributeHeightToRows(nsIPresContext* aPresContext, + const nsHTMLReflowState& aReflowState, + nscoord aAmount) +{ + float p2t; + aPresContext->GetPixelsToTwips(&p2t); + + nscoord cellSpacingY = GetCellSpacingY(); + + nscoord sumOfRowHeights = 0; + nscoord rowGroupYPos = aReflowState.mComputedBorderPadding.top + cellSpacingY; + + nsVoidArray rowGroups; + PRUint32 numRowGroups; + OrderRowGroups(rowGroups, numRowGroups, nsnull); + + nscoord amountUsed = 0; + // distribute space to each pct height row whose row group doesn't have a computed + // height, and base the pct on the table height. If the row group had a computed + // height, then this was already done in nsTableRowGroupFrame::CalculateRowHeights + nscoord pctBasis = aReflowState.mComputedHeight - (GetCellSpacingY() * (GetRowCount() + 1)); + nscoord yOriginRG = aReflowState.mComputedBorderPadding.top + GetCellSpacingY(); + nscoord yEndRG = yOriginRG; + PRUint32 rgX; + for (rgX = 0; (rgX < numRowGroups); rgX++) { + nsTableRowGroupFrame* rgFrame = GetRowGroupFrame((nsIFrame*)rowGroups.ElementAt(rgX)); + nscoord amountUsedByRG = 0; + nscoord yOriginRow = 0; + nsRect rgRect; + rgFrame->GetRect(rgRect); + if (rgFrame && !rgFrame->HasStyleHeight()) { + nsTableRowFrame* rowFrame = rgFrame->GetFirstRow(); + while (rowFrame) { + nsRect rowRect; + rowFrame->GetRect(rowRect); + if ((amountUsed < aAmount) && rowFrame->HasPctHeight()) { + nscoord pctHeight = nsTableFrame::RoundToPixel(rowFrame->GetHeight(pctBasis), p2t); + nscoord amountForRow = PR_MIN(aAmount - amountUsed, pctHeight - rowRect.height); + if (amountForRow > 0) { + rowRect.height += amountForRow; + rowFrame->SetRect(aPresContext, rowRect); + yOriginRow += rowRect.height + cellSpacingY; + yEndRG += rowRect.height + cellSpacingY; + amountUsed += amountForRow; + amountUsedByRG += amountForRow; + //rowFrame->DidResize(aPresContext, aReflowState); + nsTableFrame::RePositionViews(aPresContext, rowFrame); + } + } + else { + if (amountUsed > 0) { + rowFrame->MoveTo(aPresContext, rowRect.x, yOriginRow); + nsTableFrame::RePositionViews(aPresContext, rowFrame); + } + yOriginRow += rowRect.height + cellSpacingY; + yEndRG += rowRect.height + cellSpacingY; + } + rowFrame = rowFrame->GetNextRow(); + } + if (amountUsed > 0) { + rgRect.y = yOriginRG; + rgRect.height += amountUsedByRG; + rgFrame->SetRect(aPresContext, rgRect); + } + } + else if (amountUsed > 0) { + rgFrame->MoveTo(aPresContext, 0, yOriginRG); + // Make sure child views are properly positioned + nsTableFrame::RePositionViews(aPresContext, rgFrame); + } + yOriginRG = yEndRG; + } + + if (amountUsed >= aAmount) { + ResizeCells(*this, aPresContext, aReflowState); + return; + } + + // get the first row without a style height where its row group has an unconstrianed height + nsTableRowGroupFrame* firstUnStyledRG = nsnull; + nsTableRowFrame* firstUnStyledRow = nsnull; + for (rgX = 0; (rgX < numRowGroups) && !firstUnStyledRG; rgX++) { + nsTableRowGroupFrame* rgFrame = GetRowGroupFrame((nsIFrame*)rowGroups.ElementAt(rgX)); + if (rgFrame && !rgFrame->HasStyleHeight()) { + nsTableRowFrame* rowFrame = rgFrame->GetFirstRow(); + while (rowFrame) { + if (!rowFrame->HasStyleHeight()) { + firstUnStyledRG = rgFrame; + firstUnStyledRow = rowFrame; + break; + } + rowFrame = rowFrame->GetNextRow(); + } + } + } + + nsTableRowFrame* lastElligibleRow = nsnull; + // accumulate the correct divisor. This will be the total of all unstyled rows inside + // unstyled row groups, unless there are none, in which case, it will be all rows + nscoord divisor = 0; + for (rgX = 0; rgX < numRowGroups; rgX++) { + nsTableRowGroupFrame* rgFrame = GetRowGroupFrame((nsIFrame*)rowGroups.ElementAt(rgX)); + if (rgFrame && (!firstUnStyledRG || !rgFrame->HasStyleHeight())) { + nsTableRowFrame* rowFrame = rgFrame->GetFirstRow(); + while (rowFrame) { + if (!firstUnStyledRG || !rowFrame->HasStyleHeight()) { + nsRect rowRect; + rowFrame->GetRect(rowRect); + divisor += rowRect.height; + lastElligibleRow = rowFrame; + } + rowFrame = rowFrame->GetNextRow(); + } + } + } + if (divisor <= 0) { + NS_ASSERTION(PR_FALSE, "invlaid divisor"); + return; + } + + // allocate the extra height to the unstyled row groups and rows + pctBasis = aAmount - amountUsed; + yOriginRG = aReflowState.mComputedBorderPadding.top + cellSpacingY; + yEndRG = yOriginRG; + for (rgX = 0; rgX < numRowGroups; rgX++) { + nsTableRowGroupFrame* rgFrame = GetRowGroupFrame((nsIFrame*)rowGroups.ElementAt(rgX)); + if (!rgFrame) continue; + nscoord amountUsedByRG = 0; + nscoord yOriginRow = 0; + nsRect rgRect; + rgFrame->GetRect(rgRect); + // see if there is an eligible row group + if (!firstUnStyledRG || !rgFrame->HasStyleHeight()) { + nsTableRowFrame* rowFrame = rgFrame->GetFirstRow(); + while (rowFrame) { + nsRect rowRect; + rowFrame->GetRect(rowRect); + // see if there is an eligible row + if (!firstUnStyledRow || !rowFrame->HasStyleHeight()) { + // The amount of additional space each row gets is proportional to its height + float percent = rowRect.height / ((float)divisor); + // give rows their percentage, except for the last row which gets the remainder + nscoord amountForRow = (rowFrame == lastElligibleRow) + ? aAmount - amountUsed : NSToCoordRound(((float)(pctBasis)) * percent); + amountForRow = PR_MIN(nsTableFrame::RoundToPixel(amountForRow, p2t), aAmount - amountUsed); + // update the row height + nsRect newRowRect(rowRect.x, yOriginRow, rowRect.width, rowRect.height + amountForRow); + rowFrame->SetRect(aPresContext, newRowRect); + yOriginRow += newRowRect.height + cellSpacingY; + yEndRG += newRowRect.height + cellSpacingY; + + amountUsed += amountForRow; + amountUsedByRG += amountForRow; + NS_ASSERTION((amountUsed <= aAmount), "invalid row allocation"); + //rowFrame->DidResize(aPresContext, aReflowState); + nsTableFrame::RePositionViews(aPresContext, rowFrame); + } + else { + if (amountUsed > 0) { + rowFrame->MoveTo(aPresContext, rowRect.x, yOriginRow); + nsTableFrame::RePositionViews(aPresContext, rowFrame); + } + yOriginRow += rowRect.height + cellSpacingY; + yEndRG += rowRect.height + cellSpacingY; + } + rowFrame = rowFrame->GetNextRow(); + } + if (amountUsed > 0) { + rgRect.y = yOriginRG; + rgRect.height += amountUsedByRG; + rgFrame->SetRect(aPresContext, rgRect); + } + // Make sure child views are properly positioned + // XXX what happens if childFrame is a scroll frame and this gets skipped? see also below + } + else if (amountUsed > 0) { + rgFrame->MoveTo(aPresContext, 0, yOriginRG); + // Make sure child views are properly positioned + nsTableFrame::RePositionViews(aPresContext, rgFrame); + } + yOriginRG = yEndRG; + } + + ResizeCells(*this, aPresContext, aReflowState); +} + static void UpdateCol(nsTableFrame& aTableFrame, nsTableColFrame& aColFrame, @@ -3435,6 +3590,17 @@ UpdateCol(nsTableFrame& aTableFrame, } } +PRBool +nsTableFrame::IsPctHeight(nsIStyleContext* aStyleContext) +{ + PRBool result = PR_FALSE; + if (aStyleContext) { + nsStylePosition* position = (nsStylePosition*)aStyleContext->GetStyleData(eStyleStruct_Position); + result = (eStyleUnit_Percent == position->mHeight.GetUnit()); + } + return result; +} + PRBool nsTableFrame::CellChangedWidth(const nsTableCellFrame& aCellFrame, nscoord aPrevCellMin, @@ -4370,9 +4536,20 @@ NS_IMETHODIMP nsTableFrame::GetTableSize(PRInt32& aRowCount, PRInt32& aColCount) PRInt32 nsTableFrame::GetNumCellsOriginatingInCol(PRInt32 aColIndex) const { nsTableCellMap* cellMap = GetCellMap(); - return cellMap->GetNumCellsOriginatingInCol(aColIndex); + if (cellMap) + return cellMap->GetNumCellsOriginatingInCol(aColIndex); + else + return 0; } +PRInt32 nsTableFrame::GetNumCellsOriginatingInRow(PRInt32 aRowIndex) const +{ + nsTableCellMap* cellMap = GetCellMap(); + if (cellMap) + return cellMap->GetNumCellsOriginatingInRow(aRowIndex); + else + return 0; +} /******************************************************************************** @@ -4477,7 +4654,11 @@ void nsTableFrame::DebugReflow(nsIFrame* aFrame, if (!aMetrics) { // start PrettyUC(aState.availableWidth, width); PrettyUC(aState.availableHeight, height); - printf("r=%d a=%s,%s ", aState.reason, width, height); + printf("r=%d ", aState.reason); + if (aState.mFlags.mSpecialTableReflow) { + printf("special "); + } + printf("a=%s,%s ", width, height); PrettyUC(aState.mComputedWidth, width); PrettyUC(aState.mComputedHeight, height); printf("c=%s,%s ", width, height); diff --git a/mozilla/layout/html/table/src/nsTableFrame.h b/mozilla/layout/html/table/src/nsTableFrame.h index 7b462dc77ce..3d0efabf345 100644 --- a/mozilla/layout/html/table/src/nsTableFrame.h +++ b/mozilla/layout/html/table/src/nsTableFrame.h @@ -207,6 +207,8 @@ public: float aPixelToTwips, nsPixelRound aRound= eAlwaysRoundUp); + static void NotifyAncestorsOfSpecialReflow(const nsHTMLReflowState& aReflowState); + NS_IMETHOD IsPercentageBase(PRBool& aBase) const; static nsresult AppendDirtyReflowCommand(nsIPresShell* aPresShell, @@ -258,6 +260,7 @@ public: nsIAtom* aChildType); PRBool IsAutoWidth(PRBool* aIsPctWidth = nsnull); PRBool IsAutoHeight(); + static PRBool IsPctHeight(nsIStyleContext* aStyleContext); /** @return PR_TRUE if aDisplayType represents a rowgroup of any sort * (header, footer, or body) @@ -491,14 +494,18 @@ public: PRInt32* aColSpan = nsnull); PRInt32 GetNumCellsOriginatingInCol(PRInt32 aColIndex) const; + PRInt32 GetNumCellsOriginatingInRow(PRInt32 aRowIndex) const; PRBool HasPctCol() const; void SetHasPctCol(PRBool aValue); PRBool HasCellSpanningPctCol() const; void SetHasCellSpanningPctCol(PRBool aValue); - // is this the 3rd reflow due to a height on a table in pagination mode. - PRBool IsThirdPassReflow() const; + + PRBool NeedSpecialReflow() const; + void SetNeedSpecialReflow(PRBool aValue); + PRBool NeedToInitiateSpecialReflow() const; + void SetNeedToInitiateSpecialReflow(PRBool aValue); protected: @@ -527,7 +534,6 @@ protected: void SetDescendantReflowedNotTimeout(PRBool aValue); PRBool RequestedTimeoutReflow() const; void SetRequestedTimeoutReflow(PRBool aValue); - void SetThirdPassReflow(PRBool aValue); void InterruptNotification(nsIPresContext* aPresContext, PRBool aIsRequest); @@ -652,18 +658,12 @@ protected: // reflow state, and for the table attributes and parent nscoord CalcDesiredHeight(nsIPresContext* aPresContext, const nsHTMLReflowState& aReflowState); - // The following two functions are helpers for CalcDesiredHeight + + // The following is a helper for CalcDesiredHeight - void DistributeSpaceToCells(nsIPresContext* aPresContext, + void DistributeHeightToRows(nsIPresContext* aPresContext, const nsHTMLReflowState& aReflowState, - nsIFrame* aRowGroupFrame); - void DistributeSpaceToRows(nsIPresContext* aPresContext, - const nsHTMLReflowState& aReflowState, - nsIFrame* aRowGroupFrame, - nscoord aSumOfRowHeights, - nscoord aExcess, - nscoord& aExcessAllocated, - nscoord& aRowGroupYPos); + nscoord aAmount); void PlaceChild(nsIPresContext* aPresContext, nsTableReflowState& aReflowState, @@ -830,8 +830,6 @@ public: /* ----- Cell Map public methods ----- */ // percentage height cells void ComputePercentBasisForRows(const nsHTMLReflowState& aReflowState); - nscoord GetPercentBasisForRows(); - nscoord GetMinWidth() const; void SetMinWidth(nscoord aWidth); @@ -895,8 +893,9 @@ protected: // targeted at us, as an optimization. unsigned mRequestedTimeoutReflow:1; unsigned mRowInserted:1; - unsigned mThirdPassReflow:1; - int : 21; // unused + unsigned mNeedSpecialReflow:1; + unsigned mNeedToInitiateSpecialReflow:1; + int : 19; // unused } mBits; nsTableCellMap* mCellMap; // maintains the relationships between rows, cols, and cells @@ -941,11 +940,6 @@ inline PRBool nsTableFrame::IsRowGroup(PRInt32 aDisplayType) const (NS_STYLE_DISPLAY_TABLE_ROW_GROUP == aDisplayType)); } -inline nscoord nsTableFrame::GetPercentBasisForRows() -{ - return mPercentBasisForRows; -} - inline void nsTableFrame::SetHadInitialReflow(PRBool aValue) { mBits.mHadInitialReflow = aValue; @@ -1006,16 +1000,25 @@ inline void nsTableFrame::SetRequestedTimeoutReflow(PRBool aValue) mBits.mRequestedTimeoutReflow = (unsigned)aValue; } -inline PRBool nsTableFrame::IsThirdPassReflow() const +inline PRBool nsTableFrame::NeedSpecialReflow() const { - return (PRBool)mBits.mThirdPassReflow; + return (PRBool)mBits.mNeedSpecialReflow; } -inline void nsTableFrame::SetThirdPassReflow(PRBool aValue) +inline void nsTableFrame::SetNeedSpecialReflow(PRBool aValue) { - mBits.mThirdPassReflow = (unsigned)aValue; + mBits.mNeedSpecialReflow = (unsigned)aValue; } +inline PRBool nsTableFrame::NeedToInitiateSpecialReflow() const +{ + return (PRBool)mBits.mNeedToInitiateSpecialReflow; +} + +inline void nsTableFrame::SetNeedToInitiateSpecialReflow(PRBool aValue) +{ + mBits.mNeedToInitiateSpecialReflow = (unsigned)aValue; +} inline PRBool nsTableFrame::IsRowInserted() const { return (PRBool)mBits.mRowInserted; diff --git a/mozilla/layout/html/table/src/nsTableRowFrame.cpp b/mozilla/layout/html/table/src/nsTableRowFrame.cpp index 0ddd271299a..caaa317ead8 100644 --- a/mozilla/layout/html/table/src/nsTableRowFrame.cpp +++ b/mozilla/layout/html/table/src/nsTableRowFrame.cpp @@ -94,6 +94,42 @@ nsTableCellReflowState::nsTableCellReflowState(nsIPresContext* aPresCon FixUp(aAvailSpace); } +void +nsTableRowFrame::SetFixedHeight(nscoord aValue) +{ + if (!HasPctHeight()) { + nscoord height = PR_MAX(0, aValue); + if (HasFixedHeight()) { + if (height > mStyleHeight) { + mStyleHeight = height; + } + } + else { + mStyleHeight = height; + if (height > 0) { + SetHasFixedHeight(PR_TRUE); + } + } + } +} + +void +nsTableRowFrame::SetPctHeight(float aPctValue) +{ + nscoord height = PR_MAX(0, NSToCoordRound(aPctValue * 100.0f)); + if (HasPctHeight()) { + if (height > mStyleHeight) { + mStyleHeight = height; + } + } + else { + mStyleHeight = height; + if (height > 0.0f) { + SetHasPctHeight(PR_TRUE); + } + } +} + void nsTableCellReflowState::FixUp(const nsSize& aAvailSpace) { // fix the mComputed values during a pass 2 reflow since the cell can be a percentage base @@ -121,15 +157,13 @@ TallestCellGotShorter(nscoord aOld, return ((aNew < aOld) && (aOld == aTallest)); } -/* ----------- nsTableRowpFrame ---------- */ +/* ----------- nsTableRowFrame ---------- */ nsTableRowFrame::nsTableRowFrame() - : nsHTMLContainerFrame(), - mAllBits(0) + : nsHTMLContainerFrame() { - mBits.mMinRowSpan = 1; - mBits.mRowIndex = 0; - ResetTallestCell(0); + mBits.mRowIndex = mBits.mFirstInserted = 0; + ResetHeight(0); #ifdef DEBUG_TABLE_REFLOW_TIMING mTimer = new nsReflowTimer(this); #endif @@ -329,6 +363,20 @@ GetHeightOfRowsSpannedBelowFirst(nsTableCellFrame& aTableCellFrame, return height; } +nsTableCellFrame* +nsTableRowFrame::GetFirstCell() +{ + nsIFrame* childFrame = mFrames.FirstChild(); + while (childFrame) { + nsCOMPtr frameType; + childFrame->GetFrameType(getter_AddRefs(frameType)); + if (nsLayoutAtoms::tableCellFrame == frameType.get()) { + return (nsTableCellFrame*)childFrame; + } + childFrame->GetNextSibling(&childFrame); + } + return nsnull; +} /** * Post-reflow hook. This is where the table row does its post-processing @@ -390,43 +438,53 @@ nscoord nsTableRowFrame::GetMaxCellAscent() const return mMaxCellAscent; } -#if 0 // nobody uses this -// returns max-descent amongst all cells that have 'vertical-align: baseline' -// does *not* include cells with rowspans -nscoord nsTableRowFrame::GetMaxCellDescent() const +nscoord +nsTableRowFrame::GetHeight(nscoord aPctBasis) const { - return mMaxCellDescent; -} -#endif - -/** returns the height of the tallest child in this row (ignoring any cell with rowspans) */ -nscoord nsTableRowFrame::GetTallestCell() const -{ - return mTallestCell; + nscoord height = 0; + if ((aPctBasis > 0) && HasPctHeight()) { + height = NSToCoordRound(GetPctHeight() * (float)aPctBasis); + } + else if (HasFixedHeight()) { + height = GetFixedHeight(); + } + return PR_MAX(height, GetContentHeight()); } void -nsTableRowFrame::ResetTallestCell(nscoord aRowStyleHeight) +nsTableRowFrame::ResetHeight(nscoord aFixedHeight) { - mTallestCell = (NS_UNCONSTRAINEDSIZE == aRowStyleHeight) ? 0 : aRowStyleHeight; + SetHasFixedHeight(PR_FALSE); + SetHasPctHeight(PR_FALSE); + SetFixedHeight(0); + SetContentHeight(0); + + if (aFixedHeight > 0) { + SetFixedHeight(aFixedHeight); + } + mMaxCellAscent = 0; mMaxCellDescent = 0; } void -nsTableRowFrame::SetTallestCell(nscoord aHeight, - nscoord aAscent, - nscoord aDescent, - nsTableFrame* aTableFrame, - nsTableCellFrame* aCellFrame) +nsTableRowFrame::UpdateHeight(nscoord aHeight, + nscoord aAscent, + nscoord aDescent, + nsTableFrame* aTableFrame, + nsTableCellFrame* aCellFrame) { - NS_ASSERTION((aTableFrame && aCellFrame) , "invalid call"); + if (!aTableFrame || !aCellFrame) { + NS_ASSERTION(PR_FALSE , "invalid call"); + return; + } + if (aHeight != NS_UNCONSTRAINEDSIZE) { if (!(aCellFrame->HasVerticalAlignBaseline())) { // only the cell's height matters - if (mTallestCell < aHeight) { + if (GetHeight() < aHeight) { PRInt32 rowSpan = aTableFrame->GetEffectiveRowSpan(*aCellFrame); if (rowSpan == 1) { - mTallestCell = aHeight; + SetContentHeight(aHeight); } } } @@ -444,21 +502,23 @@ nsTableRowFrame::SetTallestCell(nscoord aHeight, } } // keep the tallest height in sync - if (mTallestCell < mMaxCellAscent + mMaxCellDescent) { - mTallestCell = mMaxCellAscent + mMaxCellDescent; + if (GetHeight() < mMaxCellAscent + mMaxCellDescent) { + SetContentHeight(mMaxCellAscent + mMaxCellDescent); } } } } -void -nsTableRowFrame::CalcTallestCell() +nscoord +nsTableRowFrame::CalcHeight(const nsHTMLReflowState& aReflowState) { nsTableFrame* tableFrame = nsnull; nsresult rv = nsTableFrame::GetTableFrame(this, tableFrame); - if (NS_FAILED(rv)) return; + if (!tableFrame) return 0; - ResetTallestCell(0); + nscoord computedHeight = (NS_UNCONSTRAINEDSIZE == aReflowState.mComputedHeight) + ? 0 : aReflowState.mComputedHeight; + ResetHeight(computedHeight); for (nsIFrame* kidFrame = mFrames.FirstChild(); kidFrame; kidFrame->GetNextSibling(&kidFrame)) { nsCOMPtr frameType; @@ -470,9 +530,10 @@ nsTableRowFrame::CalcTallestCell() // height may have changed, adjust descent to absorb any excess difference nscoord ascent = ((nsTableCellFrame *)kidFrame)->GetDesiredAscent(); nscoord descent = desSize.height - ascent; - SetTallestCell(desSize.height, ascent, descent, tableFrame, (nsTableCellFrame*)kidFrame); + UpdateHeight(desSize.height, ascent, descent, tableFrame, (nsTableCellFrame*)kidFrame); } } + return GetHeight(); } #if 0 @@ -648,49 +709,6 @@ nsTableRowFrame::GetFrameForPoint(nsIPresContext* aPresContext, return NS_ERROR_FAILURE; } -/* GetMinRowSpan is needed for deviant cases where every cell in a row has a rowspan > 1. - * It sets mMinRowSpan, which is used in FixMinCellHeight - */ -void nsTableRowFrame::GetMinRowSpan(nsTableFrame *aTableFrame) -{ - PRInt32 minRowSpan=-1; - nsIFrame* frame = mFrames.FirstChild(); - while (frame) - { - const nsStyleDisplay *kidDisplay; - frame->GetStyleData(eStyleStruct_Display, ((const nsStyleStruct *&)kidDisplay)); - if (NS_STYLE_DISPLAY_TABLE_CELL == kidDisplay->mDisplay) - { - PRInt32 rowSpan = aTableFrame->GetEffectiveRowSpan((nsTableCellFrame &)*frame); - if (-1==minRowSpan) - minRowSpan = rowSpan; - else if (minRowSpan>rowSpan) - minRowSpan = rowSpan; - } - frame->GetNextSibling(&frame); - } - mBits.mMinRowSpan = unsigned(minRowSpan); -} - -void nsTableRowFrame::FixMinCellHeight(nsTableFrame *aTableFrame) -{ - nsIFrame* frame = mFrames.FirstChild(); - while (frame) { - nsCOMPtr frameType; - frame->GetFrameType(getter_AddRefs(frameType)); - if (nsLayoutAtoms::tableCellFrame == frameType.get()) { - PRInt32 rowSpan = aTableFrame->GetEffectiveRowSpan((nsTableCellFrame &)*frame); - if (PRInt32(mBits.mMinRowSpan) == rowSpan) { - nsRect rect; - frame->GetRect(rect); - if (rect.height > mTallestCell) - mTallestCell = rect.height; - } - } - frame->GetNextSibling(&frame); - } -} - // Calculate the cell's actual size given its pass2 desired width and height. // Takes into account the specified height (in the style), and any special logic // needed for backwards compatibility. @@ -701,37 +719,37 @@ nsTableRowFrame::CalculateCellActualSize(nsIFrame* aCellFrame, nscoord& aDesiredHeight, nscoord aAvailWidth) { - nscoord specifiedHeight = 0; - const nsStylePosition* position; + nscoord specifiedHeight = 0; // Get the height specified in the style information + const nsStylePosition* position; aCellFrame->GetStyleData(eStyleStruct_Position, (const nsStyleStruct*&)position); + + nsTableFrame* tableFrame = nsnull; + nsTableFrame::GetTableFrame(this, tableFrame); + if (!tableFrame) return NS_ERROR_NULL_POINTER; + + PRInt32 rowSpan = tableFrame->GetEffectiveRowSpan((nsTableCellFrame&)*aCellFrame); switch (position->mHeight.GetUnit()) { case eStyleUnit_Coord: specifiedHeight = position->mHeight.GetCoordValue(); + if (1 == rowSpan) + SetFixedHeight(specifiedHeight); break; case eStyleUnit_Percent: { - nsTableFrame* table = nsnull; - nsTableFrame::GetTableFrame(this, table); - if (table) { - nscoord basis = table->GetPercentBasisForRows(); - if (basis > 0) { - float percent = position->mHeight.GetPercentValue(); - specifiedHeight = NSToCoordRound(percent * ((float)basis)); - } - } + if (1 == rowSpan) + SetPctHeight(position->mHeight.GetPercentValue()); + // pct heights are handled when all of the cells are finished, so don't set specifiedHeight break; } case eStyleUnit_Inherit: - // XXX for now, do nothing case eStyleUnit_Auto: default: break; } - // If the specified height is greater than the desired height, then use the - // specified height + // If the specified height is greater than the desired height, then use the specified height if (specifiedHeight > aDesiredHeight) aDesiredHeight = specifiedHeight; @@ -865,6 +883,7 @@ nsTableRowFrame::ReflowChildren(nsIPresContext* aPresContext, PRInt32 prevColIndex = firstPrevColIndex; nscoord x = 0; // running total of children x offset + nsTableFrame* tableFirstInFlow = (nsTableFrame*)tableFrame->GetFirstInFlow(); PRBool isAutoLayout = tableFrame->IsAutoLayout(); PRBool needToNotifyTable = PR_TRUE; // Reflow each of our existing cell frames @@ -874,14 +893,21 @@ nsTableRowFrame::ReflowChildren(nsIPresContext* aPresContext, nsFrameState frameState; kidFrame->GetFrameState(&frameState); + nsCOMPtr frameType; + kidFrame->GetFrameType(getter_AddRefs(frameType)); + // See if we should only reflow the dirty child frames PRBool doReflowChild = PR_TRUE; if (aDirtyOnly && ((frameState & NS_FRAME_IS_DIRTY) == 0)) { doReflowChild = PR_FALSE; } - - nsCOMPtr frameType; - kidFrame->GetFrameType(getter_AddRefs(frameType)); + if (aReflowState.mFlags.mSpecialTableReflow) { + if (!isPaginated && (nsLayoutAtoms::tableCellFrame == frameType.get() && + !((nsTableCellFrame*)kidFrame)->NeedSpecialReflow())) { + kidFrame = iter.Next(); + continue; + } + } // Reflow the child frame if (doReflowChild) { @@ -919,7 +945,9 @@ nsTableRowFrame::ReflowChildren(nsIPresContext* aPresContext, if ((availCellWidth != cellFrame->GetPriorAvailWidth()) || (cellDesiredSize.width > cellFrame->GetPriorAvailWidth()) || (eReflowReason_StyleChange == aReflowState.reason) || - isPaginated) { + isPaginated || + (aReflowState.mFlags.mSpecialTableReflow && cellFrame->NeedSpecialReflow()) || + HasPctHeight()) { // Reflow the cell to fit the available width, height nsSize kidAvailSize(availColWidth, aReflowState.availableHeight); nsReflowReason reason = eReflowReason_Resize; @@ -1002,7 +1030,7 @@ nsTableRowFrame::ReflowChildren(nsIPresContext* aPresContext, // height may have changed, adjust descent to absorb any excess difference nscoord ascent = cellFrame->GetDesiredAscent(); nscoord descent = desiredSize.height - ascent; - SetTallestCell(desiredSize.height, ascent, descent, tableFrame, cellFrame); + UpdateHeight(desiredSize.height, ascent, descent, tableFrame, cellFrame); // Place the child if (NS_UNCONSTRAINEDSIZE != availColWidth) { @@ -1037,8 +1065,7 @@ nsTableRowFrame::ReflowChildren(nsIPresContext* aPresContext, // just set our width to what was available. The table will calculate the width and not use our value. aDesiredSize.width = aReflowState.availableWidth; - CalcTallestCell(); - aDesiredSize.height = GetTallestCell(); + aDesiredSize.height = CalcHeight(aReflowState); return rv; } @@ -1050,7 +1077,7 @@ NS_METHOD nsTableRowFrame::IncrementalReflow(nsIPresContext* aPresConte nsReflowStatus& aStatus) { nsresult rv = NS_OK; - CalcTallestCell(); // need to recalculate it based on last reflow sizes + CalcHeight(aReflowState); // need to recalculate it based on last reflow sizes // determine if this frame is the target or not nsIFrame* target = nsnull; @@ -1193,7 +1220,7 @@ nsTableRowFrame::IR_TargetIsChild(nsIPresContext* aPresContext, if (!hasVerticalAlignBaseline) { // only the height matters tallestCellGotShorter = - TallestCellGotShorter(oldCellDesSize.height, cellMet.height, mTallestCell); + TallestCellGotShorter(oldCellDesSize.height, cellMet.height, GetHeight()); } else { // the ascent matters @@ -1209,10 +1236,10 @@ nsTableRowFrame::IR_TargetIsChild(nsIPresContext* aPresContext, } } if (tallestCellGotShorter) { - CalcTallestCell(); + CalcHeight(aReflowState); } else { - SetTallestCell(cellMet.height, cellMet.ascent, cellMet.descent, &aTableFrame, cellFrame); + UpdateHeight(cellMet.height, cellMet.ascent, cellMet.descent, &aTableFrame, cellFrame); } // if the cell's desired size didn't changed, our height is unchanged @@ -1232,7 +1259,7 @@ nsTableRowFrame::IR_TargetIsChild(nsIPresContext* aPresContext, } } } - aDesiredSize.height = (aDesiredSize.mNothingChanged) ? mRect.height : GetTallestCell(); + aDesiredSize.height = (aDesiredSize.mNothingChanged) ? mRect.height : GetHeight(); if (1 == rowSpan) { cellMet.height = aDesiredSize.height; } @@ -1308,25 +1335,19 @@ nsTableRowFrame::Reflow(nsIPresContext* aPresContext, rv = nsTableFrame::GetTableFrame(this, tableFrame); if (!tableFrame) return NS_ERROR_NULL_POINTER; + if ((NS_UNCONSTRAINEDSIZE != aReflowState.availableWidth) && !mPrevInFlow) { + // see if an extra reflow will be necessary when there is a pct height but no height on the parent + if ( ((NS_UNCONSTRAINEDSIZE == aReflowState.mComputedHeight) || + (0 == aReflowState.mComputedHeight)) && + nsTableFrame::IsPctHeight(mStyleContext)) { + nsTableFrame::NotifyAncestorsOfSpecialReflow(aReflowState); + SetNeedSpecialReflow(PR_TRUE); + } + } + switch (aReflowState.reason) { case eReflowReason_Initial: rv = ReflowChildren(aPresContext, aDesiredSize, aReflowState, *tableFrame, aStatus, PR_FALSE); -#ifdef WHY - if (!tableFrame->IsAutoLayout()) { - // this resize reflow is necessary to place the cells correctly in the case of rowspans and colspans. - // It is very efficient. It does not actually need to pass a reflow down to the cells. - nsSize availSpace(aReflowState.availableWidth, aReflowState.availableHeight); - nsHTMLReflowState resizeReflowState(aPresContext, - (const nsHTMLReflowState&)(*(aReflowState.parentReflowState)), - (nsIFrame *)this, - availSpace, - eReflowReason_Resize); - RowReflowState rowResizeReflowState(resizeReflowState, tableFrame); - rv = ReflowChildren(aPresContext, aDesiredSize, rowResizeReflowState, aStatus); - } -#endif - //GetMinRowSpan(tableFrame); - //FixMinCellHeight(tableFrame); aStatus = NS_FRAME_COMPLETE; break; @@ -1343,6 +1364,10 @@ nsTableRowFrame::Reflow(nsIPresContext* aPresContext, // just set our width to what was available. The table will calculate the width and not use our value. aDesiredSize.width = aReflowState.availableWidth; + if (aReflowState.mFlags.mSpecialTableReflow) { + SetNeedSpecialReflow(PR_FALSE); + } + #if defined DEBUG_TABLE_REFLOW_TIMING nsTableFrame::DebugReflow(this, (nsHTMLReflowState&)aReflowState, &aDesiredSize, aStatus); #endif @@ -1463,6 +1488,21 @@ nsTableRowFrame::GetFrameType(nsIAtom** aType) const return NS_OK; } +nsTableRowFrame* +nsTableRowFrame::GetNextRow() const +{ + nsIFrame* childFrame; + GetNextSibling(&childFrame); + while (childFrame) { + nsCOMPtr frameType; + childFrame->GetFrameType(getter_AddRefs(frameType)); + if (nsLayoutAtoms::tableRowFrame == frameType.get()) { + return (nsTableRowFrame*)childFrame; + } + childFrame->GetNextSibling(&childFrame); + } + return nsnull; +} /* ----- global methods ----- */ diff --git a/mozilla/layout/html/table/src/nsTableRowFrame.h b/mozilla/layout/html/table/src/nsTableRowFrame.h index 05d6467479a..a697d4e0902 100644 --- a/mozilla/layout/html/table/src/nsTableRowFrame.h +++ b/mozilla/layout/html/table/src/nsTableRowFrame.h @@ -47,7 +47,7 @@ class nsTableCellFrame; class nsReflowTimer; #endif -#define NS_TABLE_MAX_ROW_INDEX (1<<19) +#define NS_ROW_NEED_SPECIAL_REFLOW 0x20000000 #define NS_ROW_FRAME_PAINT_SKIP_ROW 0x00000001 #define NS_ROW_FRAME_PAINT_SKIP_CELLS 0x00000002 @@ -121,6 +121,8 @@ public: nsFramePaintLayer aWhichLayer, nsIFrame** aFrame); + nsTableCellFrame* GetFirstCell() ; + /** calls Reflow for all of its child cells. * Cells with rowspan=1 are all set to the same height and stacked horizontally. *

Cells are not split unless absolutely necessary. @@ -155,19 +157,17 @@ public: PRUint32* aResult) const; #endif - void SetTallestCell(nscoord aHeight, - nscoord aAscent, - nscoord aDescent, - nsTableFrame* aTableFrame = nsnull, - nsTableCellFrame* aCellFrame = nsnull); + void UpdateHeight(nscoord aHeight, + nscoord aAscent, + nscoord aDescent, + nsTableFrame* aTableFrame = nsnull, + nsTableCellFrame* aCellFrame = nsnull); - void ResetTallestCell(nscoord aRowStyleHeight); + void ResetHeight(nscoord aRowStyleHeight); - // calculate the tallest child when the previous tallest child gets shorter - void CalcTallestCell(); - - /** returns the tallest child in this row (ignoring any cell with rowspans) */ - nscoord GetTallestCell() const; + // calculate the height, considering content height of the + // cells and the style height of the row and cells, excluding pct heights + nscoord CalcHeight(const nsHTMLReflowState& aReflowState); // Support for cells with 'vertical-align: baseline'. @@ -210,14 +210,38 @@ public: void RemoveCellFrame(nsTableCellFrame* aFrame); - nsresult CalculateCellActualSize(nsIFrame* aRowFrame, - nscoord& aDesiredWidth, - nscoord& aDesiredHeight, - nscoord aAvailWidth); + nsresult CalculateCellActualSize(nsIFrame* aRowFrame, + nscoord& aDesiredWidth, + nscoord& aDesiredHeight, + nscoord aAvailWidth); PRBool IsFirstInserted() const; void SetFirstInserted(PRBool aValue); + PRBool NeedSpecialReflow() const; + void SetNeedSpecialReflow(PRBool aValue); + + PRBool GetContentHeight() const; + void SetContentHeight(nscoord aTwipValue); + + PRBool HasStyleHeight() const; + + PRBool HasFixedHeight() const; + void SetHasFixedHeight(PRBool aValue); + + PRBool HasPctHeight() const; + void SetHasPctHeight(PRBool aValue); + + nscoord GetFixedHeight() const; + void SetFixedHeight(nscoord aValue); + + float GetPctHeight() const; + void SetPctHeight(float aPctValue); + + nscoord GetHeight(nscoord aBasis = 0) const; + + nsTableRowFrame* GetNextRow() const; + protected: /** protected constructor. @@ -262,10 +286,6 @@ protected: // row-specific methods - void GetMinRowSpan(nsTableFrame *aTableFrame); - - void FixMinCellHeight(nsTableFrame *aTableFrame); - nscoord ComputeCellXOffset(const nsHTMLReflowState& aState, nsIFrame* aKidFrame, const nsMargin& aKidMargin) const; @@ -280,19 +300,18 @@ protected: nsReflowStatus& aStatus, PRBool aDirtyOnly = PR_FALSE); -public: - struct RowBits { - int mRowIndex:20; - unsigned mMinRowSpan:11; // the smallest row span among all my child cells - unsigned mFirstInserted; // if true, then it was the top most newly inserted row - }; - private: - union { - PRUint32 mAllBits; - RowBits mBits; - }; - nscoord mTallestCell; // not my height, but the height of my tallest child + struct RowBits { + unsigned mRowIndex:29; + unsigned mHasFixedHeight:1; // set if the dominating style height on the row or any cell is pixel based + unsigned mHasPctHeight:1; // set if the dominating style height on the row or any cell is pct based + unsigned mFirstInserted:1; // if true, then it was the top most newly inserted row + } mBits; + + nscoord mContentHeight; // the desired height based on the content of the tallest cell in the row + nscoord mStyleHeight; // the height based on a style pct on either the row or any cell if mHasPctHeight + // is set, otherwise the height based on a style pixel height on the row or any + // cell if mHasFixedHeight is set // max-ascent and max-descent amongst all cells that have 'vertical-align: baseline' nscoord mMaxCellAscent; // does include cells with rowspan > 1 @@ -311,7 +330,6 @@ inline PRInt32 nsTableRowFrame::GetRowIndex() const inline void nsTableRowFrame::SetRowIndex (int aRowIndex) { - NS_PRECONDITION(aRowIndex < NS_TABLE_MAX_ROW_INDEX, "unexpected row index"); mBits.mRowIndex = aRowIndex; } @@ -325,4 +343,68 @@ inline void nsTableRowFrame::SetFirstInserted(PRBool aValue) mBits.mFirstInserted = aValue; } +inline PRBool nsTableRowFrame::HasStyleHeight() const +{ + return (PRBool)mBits.mHasFixedHeight || (PRBool)mBits.mHasPctHeight; +} + +inline PRBool nsTableRowFrame::HasFixedHeight() const +{ + return (PRBool)mBits.mHasFixedHeight; +} + +inline void nsTableRowFrame::SetHasFixedHeight(PRBool aValue) +{ + mBits.mHasFixedHeight = aValue; +} + +inline PRBool nsTableRowFrame::HasPctHeight() const +{ + return (PRBool)mBits.mHasPctHeight; +} + +inline void nsTableRowFrame::SetHasPctHeight(PRBool aValue) +{ + mBits.mHasPctHeight = aValue; +} + +inline nscoord nsTableRowFrame::GetContentHeight() const +{ + return mContentHeight; +} + +inline void nsTableRowFrame::SetContentHeight(nscoord aValue) +{ + mContentHeight = aValue; +} + +inline nscoord nsTableRowFrame::GetFixedHeight() const +{ + if (mBits.mHasFixedHeight && !mBits.mHasPctHeight) + return mStyleHeight; + else + return 0; +} + +inline float nsTableRowFrame::GetPctHeight() const +{ + if (mBits.mHasPctHeight) + return (float)mStyleHeight / 100.0f; + else + return 0.0f; +} + +inline PRBool nsTableRowFrame::NeedSpecialReflow() const +{ + return (mState & NS_ROW_NEED_SPECIAL_REFLOW) == NS_ROW_NEED_SPECIAL_REFLOW; +} + +inline void nsTableRowFrame::SetNeedSpecialReflow(PRBool aValue) +{ + if (aValue) { + mState |= NS_ROW_NEED_SPECIAL_REFLOW; + } else { + mState &= ~NS_ROW_NEED_SPECIAL_REFLOW; + } +} #endif diff --git a/mozilla/layout/html/table/src/nsTableRowGroupFrame.cpp b/mozilla/layout/html/table/src/nsTableRowGroupFrame.cpp index f863ef3047a..fca38c08835 100644 --- a/mozilla/layout/html/table/src/nsTableRowGroupFrame.cpp +++ b/mozilla/layout/html/table/src/nsTableRowGroupFrame.cpp @@ -105,6 +105,13 @@ nsTableRowGroupFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr) } } +NS_IMETHODIMP +nsTableRowGroupFrame::IsPercentageBase(PRBool& aBase) const +{ + aBase = PR_TRUE; + return NS_OK; +} + PRInt32 nsTableRowGroupFrame::GetRowCount() { @@ -362,6 +369,9 @@ nsTableRowGroupFrame::ReflowChildren(nsIPresContext* aPresContext, nscoord cellSpacingY = tableFrame->GetCellSpacingY(); + PRBool isPaginated; + aPresContext->IsPaginated(&isPaginated); + if (aFirstRowReflowed) { *aFirstRowReflowed = nsnull; } @@ -369,8 +379,11 @@ nsTableRowGroupFrame::ReflowChildren(nsIPresContext* aPresContext, PRBool adjustSiblings = PR_TRUE; nsIFrame* kidFrame = (aStartFrame) ? aStartFrame : mFrames.FirstChild(); - for ( ; kidFrame; ) { + nsTableFrame* tableFirstInFlow = (nsTableFrame*)tableFrame->GetFirstInFlow(); + for ( ; kidFrame; kidFrame->GetNextSibling(&kidFrame)) { // Get the frame state bits + nsCOMPtr kidType; + kidFrame->GetFrameType(getter_AddRefs(kidType)); nsFrameState frameState; kidFrame->GetFrameState(&frameState); @@ -379,6 +392,12 @@ nsTableRowGroupFrame::ReflowChildren(nsIPresContext* aPresContext, if (aDirtyOnly && ((frameState & NS_FRAME_IS_DIRTY) == 0)) { doReflowChild = PR_FALSE; } + if (aReflowState.reflowState.mFlags.mSpecialTableReflow) { + if (!isPaginated && (nsLayoutAtoms::tableRowFrame == kidType.get() && + !((nsTableRowFrame*)kidFrame)->NeedSpecialReflow())) { + doReflowChild = PR_FALSE; + } + } // Reflow the row frame if (doReflowChild) { @@ -413,9 +432,7 @@ nsTableRowGroupFrame::ReflowChildren(nsIPresContext* aPresContext, lastReflowedRow = kidFrame; if (aFirstRowReflowed && !*aFirstRowReflowed) { - nsCOMPtr fType; - kidFrame->GetFrameType(getter_AddRefs(fType)); - if (nsLayoutAtoms::tableRowFrame == fType.get()) { + if (nsLayoutAtoms::tableRowFrame == kidType.get()) { *aFirstRowReflowed = (nsTableRowFrame*)kidFrame; } } @@ -432,8 +449,6 @@ nsTableRowGroupFrame::ReflowChildren(nsIPresContext* aPresContext, kidFrame->GetSize(kidSize); aReflowState.y += kidSize.height + cellSpacingY; } - - kidFrame->GetNextSibling(&kidFrame); // Get the next child } // adjust the rows after the ones that were reflowed @@ -454,56 +469,82 @@ nsTableRowGroupFrame::ReflowChildren(nsIPresContext* aPresContext, return rv; } -void -nsTableRowGroupFrame::GetNextRowSibling(nsIFrame** aRowFrame) +nsTableRowFrame* +nsTableRowGroupFrame::GetFirstRow() { - if (!*aRowFrame) return; - GetNextFrame(*aRowFrame, aRowFrame); - while(*aRowFrame) { - const nsStyleDisplay *display; - (*aRowFrame)->GetStyleData(eStyleStruct_Display, ((const nsStyleStruct *&)display)); - if (NS_STYLE_DISPLAY_TABLE_ROW == display->mDisplay) { - return; + nsIFrame* childFrame = GetFirstFrame(); + while (childFrame) { + nsCOMPtr frameType; + childFrame->GetFrameType(getter_AddRefs(frameType)); + if (nsLayoutAtoms::tableRowFrame == frameType.get()) { + return (nsTableRowFrame*)childFrame; } - GetNextFrame(*aRowFrame, aRowFrame); + childFrame->GetNextSibling(&childFrame); + } + return nsnull; +} + + +struct RowInfo { + unsigned height:30; + unsigned hasStyleHeight:1; + unsigned isSpecial:1; // there is no cell originating in the row with rowspan=1 and there are at + // least 2 cells spanning the row and there is no style height on the row +}; + +static void +UpdateHeights(RowInfo& aRowInfo, + nscoord aAdditionalHeight, + nscoord& aTotal, + nscoord& aUnconstrainedTotal) +{ + aRowInfo.height += aAdditionalHeight; + aTotal += aAdditionalHeight; + if (!aRowInfo.hasStyleHeight) { + aUnconstrainedTotal += aAdditionalHeight; } } -// allocate the height of rows which have no cells originating in them -// except with cells with rowspan > 1. Store the height as negative -// to distinguish them from regular rows. void -AllocateSpecialHeight(nsIPresContext* aPresContext, - nsTableFrame* aTableFrame, - nsIFrame* aRowFrame, - nscoord& aHeight) +nsTableRowGroupFrame::DidResizeRows(nsIPresContext& aPresContext, + const nsHTMLReflowState& aReflowState, + nsTableRowFrame* aStartRowFrameIn) { - nsIFrame* cellFrame; - aRowFrame->FirstChild(aPresContext, nsnull, &cellFrame); - while (cellFrame) { - nsCOMPtr cellType; - cellFrame->GetFrameType(getter_AddRefs(cellType)); - if (nsLayoutAtoms::tableCellFrame == cellType.get()) { - PRInt32 rowSpan = aTableFrame->GetEffectiveRowSpan((nsTableCellFrame&)*cellFrame); - if (rowSpan > 1) { - // use a simple average to allocate the special row. This is not exact, - // but much better than nothing. - nsSize cellDesSize = ((nsTableCellFrame*)cellFrame)->GetDesiredSize(); - ((nsTableRowFrame*)aRowFrame)->CalculateCellActualSize(cellFrame, cellDesSize.width, - cellDesSize.height, cellDesSize.width); - PRInt32 propHeight = NSToCoordRound((float)cellDesSize.height / (float)rowSpan); - // special rows store the largest negative value - aHeight = PR_MIN(aHeight, -propHeight); - } - } - cellFrame->GetNextSibling(&cellFrame); + // update the cells spanning rows with their new heights + // this is the place where all of the cells in the row get set to the height of the row + PRInt32 rowIndex; + nsTableRowFrame* rowFrame; + nsTableRowFrame* startRowFrame = (aStartRowFrameIn) ? aStartRowFrameIn: GetFirstRow(); + for (rowFrame = startRowFrame, rowIndex = 0; rowFrame; rowFrame = rowFrame->GetNextRow(), rowIndex++) { + rowFrame->DidResize(&aPresContext, aReflowState); } } -/* CalculateRowHeights provides default heights for all rows in the rowgroup. - * Actual row heights are ultimately determined by the table, when the table - * height attribute is factored in. - */ +static PRBool +HasMoreThanOneCell(nsTableCellMap* aCellMap, + PRInt32 aRowIndex) +{ + if (aCellMap) { + CellData* cellData; + PRInt32 colIndex = 0; + PRInt32 count = 0; + do { + cellData = aCellMap->GetCellAt(aRowIndex, colIndex); + if (cellData && (cellData->GetCellFrame() || cellData->IsRowSpan())) + count++; + if (count > 1) + return PR_TRUE; + colIndex++; + } while(cellData); + } + return PR_FALSE; +} + +// This calculates the height of rows starting at aStartRowFrameIn and takes into account +// style height on the row group, style heights on rows and cells, style heights on rowspans. +// Actual row heights will be adjusted later if the table has a style height. +// Even if rows don't change height, this method must be called to set the heights of each +// cell in the row to the height of its row. void nsTableRowGroupFrame::CalculateRowHeights(nsIPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, @@ -511,280 +552,259 @@ nsTableRowGroupFrame::CalculateRowHeights(nsIPresContext* aPresContext, nsTableRowFrame* aStartRowFrameIn) { nsTableFrame* tableFrame = nsnull; - nsresult rv = nsTableFrame::GetTableFrame(this, tableFrame); - if (NS_FAILED(rv) || nsnull==tableFrame) return; + nsTableFrame::GetTableFrame(this, tableFrame); + if (!aPresContext || !tableFrame) return; // all table cells have the same top and bottom margins, namely cellSpacingY nscoord cellSpacingY = tableFrame->GetCellSpacingY(); + float p2t; + aPresContext->GetPixelsToTwips(&p2t); - // find the nearest row to the starting row (including the starting row) that isn't spanned into - nsTableRowFrame* startRowFrame = nsnull; - nsIFrame* childFrame = GetFirstFrame(); - PRBool foundFirstRow = PR_FALSE; - while (childFrame) { - nsCOMPtr frameType; - childFrame->GetFrameType(getter_AddRefs(frameType)); - if (nsLayoutAtoms::tableRowFrame == frameType.get()) { - PRInt32 rowIndex = ((nsTableRowFrame*)childFrame)->GetRowIndex(); - if (!foundFirstRow || !tableFrame->RowIsSpannedInto(rowIndex)) { - startRowFrame = (nsTableRowFrame*)childFrame; - if (!aStartRowFrameIn || (aStartRowFrameIn == startRowFrame)) break; + // find the nearest row at or before aStartRowFrameIn that isn't spanned into. + // If we have a computed height, then we can't compute the heights + // incrementally from aStartRowFrameIn, and we must start at the first row. + nsTableRowFrame* startRowFrame = GetFirstRow(); + if (aStartRowFrameIn && (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedHeight) + && (aReflowState.mComputedHeight > 0)) { + nsTableRowFrame* rowFrame = startRowFrame; + while (rowFrame) { + PRInt32 rowIndex = rowFrame->GetRowIndex(); + if (!tableFrame->RowIsSpannedInto(rowIndex)) { + startRowFrame = rowFrame; + if (aStartRowFrameIn == startRowFrame) + break; } - else if (aStartRowFrameIn == (nsTableRowFrame*)childFrame) break; - foundFirstRow = PR_TRUE; + else if (aStartRowFrameIn == rowFrame) + break; + rowFrame = rowFrame->GetNextRow(); } - childFrame->GetNextSibling(&childFrame); } if (!startRowFrame) return; + PRInt32 startRowIndex = startRowFrame->GetRowIndex(); + nsRect startRowRect; startRowFrame->GetRect(startRowRect); - nscoord rowGroupHeight = startRowRect.y; + // the current row group height is the y origin of the 1st row we are about to calculated a height for + nscoord startRowGroupHeight = startRowRect.y; + + PRInt32 numRows = GetRowCount() - (startRowFrame->GetRowIndex() - GetStartRowIndex()); + // collect the current height of each row. nscoord* rowHeights = nsnull; + RowInfo* rowInfo; + if (numRows > 0) { + rowInfo = new RowInfo[numRows]; + if (!rowInfo) return; + nsCRT::memset (rowInfo, 0, numRows*sizeof(RowInfo)); + } + else return; PRBool hasRowSpanningCell = PR_FALSE; - PRInt32 numRows = GetRowCount() - (startRowFrame->GetRowIndex() - GetStartRowIndex()); - // collect the current height of each row. rows which have 0 height because - // they have no cells originating in them without rowspans > 1, are referred to as - // special rows. The current height of a special row will be a negative number until - // it comes time to actually resize frames. - nscoord* rowHeights = nsnull; - if (numRows > 0) { - rowHeights = new nscoord[numRows]; - if (!rowHeights) return; - nsCRT::memset (rowHeights, 0, numRows*sizeof(nscoord)); - } // else - tree row groups need not have rows directly beneath them + nscoord heightOfRows = 0; + nscoord heightOfUnStyledRows = 0; + // Get the height of each row without considering rowspans. This will be the max of + // the largest desired height of each cell, the largest style height of each cell, + // the style height of the row. + nscoord pctHeightBasis = GetHeightBasis(aReflowState); + nsTableRowFrame* rowFrame; + PRInt32 rowIndex; // the index in rowInfo, not among the rows in the row group + for (rowFrame = startRowFrame, rowIndex = 0; rowFrame; rowFrame = rowFrame->GetNextRow(), rowIndex++) { + UpdateHeights(rowInfo[rowIndex], nsTableFrame::RoundToPixel(rowFrame->GetHeight(pctHeightBasis), p2t), + heightOfRows, heightOfUnStyledRows); + rowInfo[rowIndex].hasStyleHeight = rowFrame->HasStyleHeight(); - // Step 1: get the height of the tallest cell in the row and save it for - // pass 2. This height is for table cells that originate in this - // row and that don't span into the rows that follow - nsIFrame* rowFrame = startRowFrame; - PRInt32 rowIndex = 0; - - // For row groups that are split across pages, the first row frame won't - // necessarily be index 0 - PRInt32 startRowIndex = -1; - while (rowFrame) { - nsCOMPtr frameType; - rowFrame->GetFrameType(getter_AddRefs(frameType)); - if (nsLayoutAtoms::tableRowFrame == frameType.get()) { - if (startRowIndex == -1) { - startRowIndex = ((nsTableRowFrame*)rowFrame)->GetRowIndex(); - } - // get the height of the tallest cell in the row (excluding cells that span rows) - rowHeights[rowIndex] = ((nsTableRowFrame*)rowFrame)->GetTallestCell(); - - // See if a cell spans into the row. If so we'll have to do step 2 - if (!hasRowSpanningCell) { - if (tableFrame->RowIsSpannedInto(rowIndex + startRowIndex)) { - hasRowSpanningCell = PR_TRUE; + if (!rowInfo[rowIndex].hasStyleHeight) { + if (HasMoreThanOneCell(tableFrame->GetCellMap(), rowIndex)) { + rowInfo[rowIndex].isSpecial = PR_TRUE; + // iteratate the row's cell frames to see if any do not have rowspan > 1 + nsTableCellFrame* cellFrame = rowFrame->GetFirstCell(); + while (cellFrame) { + PRInt32 rowSpan = tableFrame->GetEffectiveRowSpan(rowIndex + startRowIndex, *cellFrame); + if (1 == rowSpan) { + rowInfo[rowIndex].isSpecial = PR_FALSE; + break; + } + cellFrame = cellFrame->GetNextCell(); } } - // special rows need to have some values, so they will get allocations - // later. If left at 0, they would get nothing. - if (0 == rowHeights[rowIndex]) { - AllocateSpecialHeight(aPresContext, tableFrame, rowFrame, rowHeights[rowIndex]); - } - rowIndex++; } - GetNextFrame(rowFrame, &rowFrame); // Get the next row + // See if a cell spans into the row. If so we'll have to do the next step + if (!hasRowSpanningCell) { + if (tableFrame->RowIsSpannedInto(rowIndex + startRowIndex)) { + hasRowSpanningCell = PR_TRUE; + } + } } - // Step 2: Now account for cells that span rows. A spanning cell's height is the sum of the heights of the - // rows it spans, or it's own desired height, whichever is greater. - // If the cell's desired height is the larger value, resize the rows and contained - // cells by an equal percentage of the additional space. - // We go through this loop twice. The first time, we are adjusting cell heights - // on the fly. The second time through the loop, we're ensuring that subsequent - // row-spanning cells didn't change prior calculations. Since we are guaranteed - // to have found the max height spanners the first time through, we know we only - // need two passes, not an arbitrary number. - nscoord yOrigin = rowGroupHeight; - nscoord lastCount = (hasRowSpanningCell) ? 2 : 1; - for (PRInt32 counter = 0; counter <= lastCount; counter++) { - rowFrame = startRowFrame; - rowIndex = 0; - while (rowFrame) { - nsCOMPtr rowType; - rowFrame->GetFrameType(getter_AddRefs(rowType)); - if (nsLayoutAtoms::tableRowFrame == rowType.get()) { - if (hasRowSpanningCell) { - // check this row for a cell with rowspans - nsIFrame* cellFrame; - rowFrame->FirstChild(aPresContext, nsnull, &cellFrame); - while (cellFrame) { - nsCOMPtr cellType; - cellFrame->GetFrameType(getter_AddRefs(cellType)); - if (nsLayoutAtoms::tableCellFrame == cellType.get()) { - PRInt32 rowSpan = tableFrame->GetEffectiveRowSpan(rowIndex + startRowIndex, - (nsTableCellFrame&)*cellFrame); - if (rowSpan > 1) { // found a cell with rowspan > 1, determine the height - // of the rows it spans - nscoord heightOfRowsSpanned = 0; - nscoord cellSpacingOfRowsSpanned = 0; - PRInt32 spanX; - PRBool cellsOrigInSpan = PR_FALSE; // do any cells originate in the spanned rows - for (spanX = 0; spanX < rowSpan; spanX++) { - PRInt32 rIndex = rowIndex + spanX; - if (rowHeights[rIndex] > 0) { - // don't consider negative values of special rows - heightOfRowsSpanned += rowHeights[rowIndex + spanX]; - cellsOrigInSpan = PR_TRUE; - } - if (0 != spanX) { - cellSpacingOfRowsSpanned += cellSpacingY; - } - } - nscoord availHeightOfRowsSpanned = heightOfRowsSpanned + cellSpacingOfRowsSpanned; + if (hasRowSpanningCell) { + // Get the height of cells with rowspans and allocate any extra space to the rows they span + // iteratate the child frames and process the row frames among them + for (rowFrame = startRowFrame, rowIndex = 0; rowFrame; rowFrame = rowFrame->GetNextRow(), rowIndex++) { + if (tableFrame->RowHasSpanningCells(startRowIndex + rowIndex)) { + nsTableCellFrame* cellFrame = rowFrame->GetFirstCell(); + // iteratate the row's cell frames + while (cellFrame) { + PRInt32 rowSpan = tableFrame->GetEffectiveRowSpan(rowIndex + startRowIndex, *cellFrame); + if (rowSpan > 1) { // a cell with rowspan > 1, determine the height of the rows it spans + nscoord heightOfRowsSpanned = 0; + nscoord heightOfUnStyledRowsSpanned = 0; + nscoord numSpecialRowsSpanned = 0; + nscoord cellSpacingTotal = 0; + PRInt32 spanX; + for (spanX = 0; spanX < rowSpan; spanX++) { + heightOfRowsSpanned += rowInfo[rowIndex + spanX].height; + if (!rowInfo[rowIndex + spanX].hasStyleHeight) { + heightOfUnStyledRowsSpanned += rowInfo[rowIndex + spanX].height; + } + if (0 != spanX) { + cellSpacingTotal += cellSpacingY; + } + if (rowInfo[rowIndex + spanX].isSpecial) { + numSpecialRowsSpanned++; + } + } + nscoord heightOfAreaSpanned = heightOfRowsSpanned + cellSpacingTotal; + // get the height of the cell + nsSize cellFrameSize; + cellFrame->GetSize(cellFrameSize); + nsSize cellDesSize = cellFrame->GetDesiredSize(); + rowFrame->CalculateCellActualSize(cellFrame, cellDesSize.width, + cellDesSize.height, cellDesSize.width); + cellFrameSize.height = cellDesSize.height; + if (cellFrame->HasVerticalAlignBaseline()) { + // to ensure that a spanning cell with a long descender doesn't + // collide with the next row, we need to take into account the shift + // that will be done to align the cell on the baseline of the row. + cellFrameSize.height += rowFrame->GetMaxCellAscent() - cellFrame->GetDesiredAscent(); + } - // see if the cell's height fits within the rows it spans. If this is - // pass 1 then use the cell's desired height and not the current height - // of its frame. That way this works for incremental reflow, too - nsSize cellFrameSize; - cellFrame->GetSize(cellFrameSize); - if (0 == counter) { - nsSize cellDesSize = ((nsTableCellFrame*)cellFrame)->GetDesiredSize(); - ((nsTableRowFrame*)rowFrame)->CalculateCellActualSize(cellFrame, cellDesSize.width, - cellDesSize.height, cellDesSize.width); - cellFrameSize.height = cellDesSize.height; - // see if the cell has 'vertical-align: baseline' - if (((nsTableCellFrame*)cellFrame)->HasVerticalAlignBaseline()) { - // to ensure that a spanning cell with a long descender doesn't - // collide with the next row, we need to take into account the shift - // that will be done to align the cell on the baseline of the row. - cellFrameSize.height += ((nsTableRowFrame*)rowFrame)->GetMaxCellAscent() - - ((nsTableCellFrame*)cellFrame)->GetDesiredAscent(); - } - } - - if (availHeightOfRowsSpanned >= cellFrameSize.height) { - // the cell's height fits with the available space of the rows it - // spans. Set the cell frame's height - cellFrame->SizeTo(aPresContext, cellFrameSize.width, availHeightOfRowsSpanned); - // Realign cell content based on new height - ((nsTableCellFrame*)cellFrame)->VerticallyAlignChild(aPresContext, aReflowState, ((nsTableRowFrame*)rowFrame)->GetMaxCellAscent()); - } - else { - // the cell's height is larger than the available space of the rows it - // spans so distribute the excess height to the rows affected - nscoord excessAvail = cellFrameSize.height - availHeightOfRowsSpanned; - nscoord excessBasis = excessAvail; - - nsTableRowFrame* rowFrameToBeResized = (nsTableRowFrame *)rowFrame; - // iterate every row starting at last row spanned and up to the row with - // the spanning cell. do this bottom up so that special rows can get a full - // allocation before other rows. - PRInt32 beginRowIndex = rowIndex + rowSpan - 1; - for (PRInt32 rowX = beginRowIndex; (rowX >= rowIndex) && (excessAvail > 0); rowX--) { - nscoord excessForRow = 0; - // special rows gets as much as they can - if (rowHeights[rowX] <= 0) { - if ((rowX == beginRowIndex) || (!cellsOrigInSpan)) { - if (0 == rowHeights[rowX]) { - // give it all since no cell originates in the row - excessForRow = excessBasis; - } - else { // don't let the allocation excced what it needs - excessForRow = (excessBasis > -rowHeights[rowX]) ? -rowHeights[rowX] : excessBasis; - } - rowHeights[rowX] = excessForRow; - excessBasis -= excessForRow; - excessAvail -= excessForRow; + if (heightOfAreaSpanned < cellFrameSize.height) { + // the cell's height is larger than the available space of the rows it + // spans so distribute the excess height to the rows affected + nscoord extra = cellFrameSize.height - heightOfAreaSpanned; + nscoord extraUsed = 0; + if (0 == numSpecialRowsSpanned) { + //NS_ASSERTION(heightOfRowsSpanned > 0, "invalid row span situation"); + PRBool haveUnStyledRowsSpanned = (heightOfUnStyledRowsSpanned > 0); + nscoord divisor = (haveUnStyledRowsSpanned) + ? heightOfUnStyledRowsSpanned : heightOfRowsSpanned; + if (divisor > 0) { + for (spanX = rowSpan - 1; spanX >= 0; spanX--) { + if (!haveUnStyledRowsSpanned || !rowInfo[rowIndex + spanX].hasStyleHeight) { + // The amount of additional space each row gets is proportional to its height + float percent = ((float)rowInfo[rowIndex + spanX].height) / ((float)divisor); + + // give rows their percentage, except for the first row which gets the remainder + nscoord extraForRow = (0 == spanX) ? extra - extraUsed + : NSToCoordRound(((float)(extra)) * percent); + extraForRow = PR_MIN(nsTableFrame::RoundToPixel(extraForRow, p2t), extra - extraUsed); + // update the row height + UpdateHeights(rowInfo[rowIndex + spanX], extraForRow, heightOfRows, heightOfUnStyledRows); + extraUsed += extraForRow; + if (extraUsed >= extra) { + NS_ASSERTION((extraUsed == extra), "invalid row height calculation"); + break; } } - else if (cellsOrigInSpan) { // normal rows - // The amount of additional space each normal row gets is based on the - // percentage of space it occupies, i.e. they don't all get the - // same amount of available space - float percent = ((float)rowHeights[rowX]) / ((float)heightOfRowsSpanned); - - // give rows their percentage, except for the first row which gets - // the remainder - excessForRow = (rowX == rowIndex) - ? excessAvail - : NSToCoordRound(((float)(excessBasis)) * percent); - // update the row height - rowHeights[rowX] += excessForRow; - excessAvail -= excessForRow; - } - // Get the next row frame - GetNextRowSibling((nsIFrame**)&rowFrameToBeResized); } - // if excessAvail is > 0 it is because !cellsOrigInSpan and the - // allocation involving special rows couldn't allocate everything. - // just give the remainder to the last row spanned. - if (excessAvail > 0) { - if (rowHeights[beginRowIndex] >= 0) { - rowHeights[beginRowIndex] += excessAvail; - } - else { - rowHeights[beginRowIndex] = excessAvail; + } + else { + // put everything in the last row + UpdateHeights(rowInfo[rowIndex + rowSpan - 1], extra, heightOfRows, heightOfUnStyledRows); + } + } + else { + // give the extra to the special rows + nscoord numSpecialRowsAllocated = 0; + for (spanX = rowSpan - 1; spanX >= 0; spanX--) { + if (rowInfo[rowIndex + spanX].isSpecial) { + // The amount of additional space each degenerate row gets is proportional to the number of them + float percent = 1.0f / ((float)numSpecialRowsSpanned); + + // give rows their percentage, except for the first row which gets the remainder + nscoord extraForRow = (numSpecialRowsSpanned - 1 == numSpecialRowsAllocated) + ? extra - extraUsed + : NSToCoordRound(((float)(extra)) * percent); + extraForRow = PR_MIN(nsTableFrame::RoundToPixel(extraForRow, p2t), extra - extraUsed); + // update the row height + UpdateHeights(rowInfo[rowIndex + spanX], extraForRow, heightOfRows, heightOfUnStyledRows); + extraUsed += extraForRow; + if (extraUsed >= extra) { + NS_ASSERTION((extraUsed == extra), "invalid row height calculation"); + break; } } } } - } - cellFrame->GetNextSibling(&cellFrame); // Get the next row child (cell frame) - } - } - - // If this is the last pass then resize the row to its final size and move the - // row's position if the previous rows have caused a shift - if (lastCount == counter) { - nsRect rowBounds; - rowFrame->GetRect(rowBounds); - - PRBool movedFrame = (rowBounds.y != yOrigin); - nscoord rowHeight = (rowHeights[rowIndex] > 0) ? rowHeights[rowIndex] : 0; - - // Resize the row to its final size and position - rowBounds.y = yOrigin; - rowBounds.height = rowHeight; - rowFrame->SetRect(aPresContext, rowBounds); - - if (movedFrame) { - nsTableFrame::RePositionViews(aPresContext, rowFrame); - } - // set the origin of the next row. - yOrigin += rowHeight + cellSpacingY; - } - - rowIndex++; - } - GetNextFrame(rowFrame, &rowFrame); // Get the next rowgroup child (row frame) - } + } + } // if (rowSpan > 1) + cellFrame = cellFrame->GetNextCell(); + } // while (cellFrame) + } // if (tableFrame->RowHasSpanningCells(startRowIndex + rowIndex) { + } // while (rowFrame) } - // step 3: notify the rows of their new heights - rowFrame = startRowFrame; - - rowIndex = 0; - while (rowFrame) { - if (NS_UNCONSTRAINEDSIZE != aReflowState.availableWidth) { - nsCOMPtr rowType; - rowFrame->GetFrameType(getter_AddRefs(rowType)); - if (nsLayoutAtoms::tableRowFrame == rowType.get()) { - // Notify the row of the new size - ((nsTableRowFrame *)rowFrame)->DidResize(aPresContext, aReflowState); + nscoord rowGroupHeight = startRowGroupHeight + heightOfRows + ((numRows - 1) * cellSpacingY); + nscoord extraComputedHeight = 0; + // if we have a style height, allocate the extra height to unconstrained rows + if ((aReflowState.mComputedHeight > rowGroupHeight) && + (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedHeight)) { + extraComputedHeight = aReflowState.mComputedHeight - rowGroupHeight; + nscoord extraUsed = 0; + PRBool haveUnStyledRows = (heightOfUnStyledRows > 0); + nscoord divisor = (haveUnStyledRows) + ? heightOfUnStyledRows : heightOfRows; + if (divisor > 0) { + for (rowIndex = 0; rowIndex < numRows; rowIndex++) { + if (!haveUnStyledRows || !rowInfo[rowIndex].hasStyleHeight) { + // The amount of additional space each row gets is based on the + // percentage of space it occupies + float percent = ((float)rowInfo[rowIndex].height) / ((float)divisor); + // give rows their percentage, except for the last row which gets the remainder + nscoord extraForRow = (numRows - 1 == rowIndex) + ? extraComputedHeight - extraUsed + : NSToCoordRound(((float)extraComputedHeight) * percent); + extraForRow = PR_MIN(nsTableFrame::RoundToPixel(extraForRow, p2t), extraComputedHeight - extraUsed); + // update the row height + UpdateHeights(rowInfo[rowIndex], extraForRow, heightOfRows, heightOfUnStyledRows); + extraUsed += extraForRow; + if (extraUsed >= extraComputedHeight) { + NS_ASSERTION((extraUsed == extraComputedHeight), "invalid row height calculation"); + break; + } + } } } - - // Update the running row group height. The height includes frames that - // aren't rows as well - nsSize rowSize; - rowFrame->GetSize(rowSize); - rowGroupHeight += rowSize.height; - if (0 != rowIndex) { - rowGroupHeight += cellSpacingY; - } - - GetNextFrame(rowFrame, &rowFrame); // Get the next row - rowIndex++; + rowGroupHeight = aReflowState.mComputedHeight; } + nscoord yOrigin = startRowGroupHeight; + // update the rows with their (potentially) new heights + for (rowFrame = startRowFrame, rowIndex = 0; rowFrame; rowFrame = rowFrame->GetNextRow(), rowIndex++) { + nsRect rowBounds; + rowFrame->GetRect(rowBounds); + + PRBool movedFrame = (rowBounds.y != yOrigin); + nscoord rowHeight = (rowInfo[rowIndex].height > 0) ? rowInfo[rowIndex].height : 0; + + if (movedFrame || (rowHeight != rowBounds.height)) { + // Resize the row to its final size and position + rowBounds.y = yOrigin; + rowBounds.height = rowHeight; + rowFrame->SetRect(aPresContext, rowBounds); + } + if (movedFrame) { + nsTableFrame::RePositionViews(aPresContext, rowFrame); + } + yOrigin += rowHeight + cellSpacingY; + } + + DidResizeRows(*aPresContext, aReflowState, startRowFrame); + aDesiredSize.height = rowGroupHeight; // Adjust our desired size - delete [] rowHeights; // cleanup + delete [] rowInfo; // cleanup } + // Called by IR_TargetIsChild() to adjust the sibling frames that follow // after an incremental reflow of aKidFrame. // This function is not used for paginated mode so we don't need to deal @@ -918,31 +938,6 @@ nsTableRowGroupFrame::SplitSpanningCells(nsIPresContext& aPresContext, return tallestCell; } -nsIFrame* GetNextRow(nsIFrame& aFrame) -{ - nsIFrame* rowFrame; - for (aFrame.GetNextSibling(&rowFrame); rowFrame; rowFrame->GetNextSibling(&rowFrame)) { - nsCOMPtr fType; - rowFrame->GetFrameType(getter_AddRefs(fType)); - if (nsLayoutAtoms::tableRowFrame == fType.get()) { - return rowFrame; - } - } - return nsnull; -} - -nsIFrame* GetFirstRow(nsTableRowGroupFrame& aRowGroupFrame) -{ - for (nsIFrame* rowFrame = aRowGroupFrame.GetFirstFrame(); rowFrame; rowFrame = GetNextRow(*rowFrame)) { - nsCOMPtr fType; - rowFrame->GetFrameType(getter_AddRefs(fType)); - if (nsLayoutAtoms::tableRowFrame == fType.get()) { - return rowFrame; - } - } - return nsnull; -} - nsresult nsTableRowGroupFrame::SplitRowGroup(nsIPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, @@ -971,7 +966,7 @@ nsTableRowGroupFrame::SplitRowGroup(nsIPresContext* aPresContext, // Walk each of the row frames looking for the first row frame that // doesn't fit in the available space - for (nsIFrame* rowFrame = GetFirstRow(*this); rowFrame; rowFrame = GetNextRow(*rowFrame)) { + for (nsTableRowFrame* rowFrame = GetFirstRow(); rowFrame; rowFrame = rowFrame->GetNextRow()) { PRBool rowIsOnCurrentPage = PR_TRUE; PRBool degenerateRow = PR_FALSE; nsRect bounds; @@ -995,7 +990,7 @@ nsTableRowGroupFrame::SplitRowGroup(nsIPresContext* aPresContext, 0, 0, NS_FRAME_NO_MOVE_FRAME, aStatus); rowFrame->SizeTo(aPresContext, desiredSize.width, desiredSize.height); rowFrame->DidReflow(aPresContext, NS_FRAME_REFLOW_FINISHED); - ((nsTableRowFrame *)rowFrame)->DidResize(aPresContext, aReflowState); + rowFrame->DidResize(aPresContext, aReflowState); if (NS_FRAME_IS_NOT_COMPLETE(aStatus)) { // the row frame is incomplete and all of the cells' block frames have split @@ -1043,7 +1038,7 @@ nsTableRowGroupFrame::SplitRowGroup(nsIPresContext* aPresContext, if (prevRowFrame) { nscoord tallestCell = SplitSpanningCells(*aPresContext, aReflowState, *styleSet, *aTableFrame, - *(nsTableRowFrame*)rowFrame, aDesiredSize.height, (nsTableRowFrame*)contRowFrame); + *rowFrame, aDesiredSize.height, (nsTableRowFrame*)contRowFrame); if (degenerateRow) { aDesiredSize.height = lastDesiredHeight + aTableFrame->GetCellSpacingY() + tallestCell; } @@ -1077,10 +1072,10 @@ nsTableRowGroupFrame::Reflow(nsIPresContext* aPresContext, nsresult rv=NS_OK; aStatus = NS_FRAME_COMPLETE; - + nsTableFrame* tableFrame = nsnull; rv = nsTableFrame::GetTableFrame(this, tableFrame); - if (!tableFrame) return NS_ERROR_NULL_POINTER; + if (!aPresContext || !tableFrame) return NS_ERROR_NULL_POINTER; nsRowGroupReflowState state(aReflowState, tableFrame); PRBool haveDesiredHeight = PR_FALSE; @@ -1111,9 +1106,12 @@ nsTableRowGroupFrame::Reflow(nsIPresContext* aPresContext, // reflow, then we need to do this because the table will skip the pass 2 reflow, // but we need to correctly calculate the row group height and we can't if there // are row spans unless we do this step - if ((eReflowReason_Initial != aReflowState.reason) || - isTableUnconstrainedReflow || - isPaginated) { + if (aReflowState.mFlags.mSpecialTableReflow) { + DidResizeRows(*aPresContext, aReflowState); + } + else if ((eReflowReason_Initial != aReflowState.reason) || + isTableUnconstrainedReflow || + isPaginated) { CalculateRowHeights(aPresContext, aDesiredSize, aReflowState); haveDesiredHeight = PR_TRUE; } @@ -1124,6 +1122,12 @@ nsTableRowGroupFrame::Reflow(nsIPresContext* aPresContext, SplitRowGroup(aPresContext, aDesiredSize, aReflowState, tableFrame, aStatus); } } + SetHasStyleHeight((NS_UNCONSTRAINEDSIZE != aReflowState.mComputedHeight) && + (aReflowState.mComputedHeight > 0)); + + if (aReflowState.mFlags.mSpecialTableReflow) { + SetNeedSpecialReflow(PR_FALSE); + } // just set our width to what was available. The table will calculate the width and not use our value. aDesiredSize.width = aReflowState.availableWidth; @@ -1318,6 +1322,21 @@ nsTableRowGroupFrame::IR_TargetIsMe(nsIPresContext* aPresContext, return rv; } +nscoord +nsTableRowGroupFrame::GetHeightBasis(const nsHTMLReflowState& aReflowState) +{ + nscoord result = 0; + if ((aReflowState.mComputedHeight > 0) && (aReflowState.mComputedHeight < NS_UNCONSTRAINEDSIZE)) { + nsTableFrame* tableFrame = nsnull; + nsTableFrame::GetTableFrame((nsIFrame*)this, tableFrame); + if (tableFrame) { + nscoord cellSpacing = PR_MAX(0, GetRowCount() - 1) * tableFrame->GetCellSpacingY(); + result -= cellSpacing; + } + } + return result; +} + nscoord nsTableRowGroupFrame::GetHeightOfRows(nsIPresContext* aPresContext) { diff --git a/mozilla/layout/html/table/src/nsTableRowGroupFrame.h b/mozilla/layout/html/table/src/nsTableRowGroupFrame.h index 0b12000af33..164c7aa1f19 100644 --- a/mozilla/layout/html/table/src/nsTableRowGroupFrame.h +++ b/mozilla/layout/html/table/src/nsTableRowGroupFrame.h @@ -79,9 +79,13 @@ struct nsRowGroupReflowState { { 0xe940e7bc, 0xb534, 0x11d2, \ { 0x95, 0xa2, 0x0, 0x60, 0xb0, 0xc3, 0x44, 0x14 } } -// use a bit from nsFrame's frame state bits to determine whether a +// use the following bits from nsFrame's frame state + // thead or tfoot should be repeated on every printed page -#define NS_ROWGROUP_REPEATABLE 0x80000000 +#define NS_ROWGROUP_REPEATABLE 0x80000000 +#define NS_ROWGROUP_HAS_STYLE_HEIGHT 0x40000000 +// we need a 3rd pass reflow to deal with pct height nested tables +#define NS_ROWGROUP_NEED_SPECIAL_REFLOW 0x20000000 /** * nsTableRowGroupFrame is the frame that maps row groups * (HTML tags THEAD, TFOOT, and TBODY). This class cannot be reused @@ -176,6 +180,10 @@ public: */ NS_IMETHOD GetFrameType(nsIAtom** aType) const; + NS_IMETHOD IsPercentageBase(PRBool& aBase) const; + + nsTableRowFrame* GetFirstRow(); + #ifdef DEBUG NS_IMETHOD GetFrameName(nsString& aResult) const; NS_IMETHOD SizeOf(nsISizeOfHandler* aSizer, PRUint32* aResult) const; @@ -206,6 +214,7 @@ public: * Get the total height of all the row rects */ nscoord GetHeightOfRows(nsIPresContext* aPresContext); + nscoord GetHeightBasis(const nsHTMLReflowState& aReflowState); // nsILineIterator methods public: @@ -255,6 +264,9 @@ protected: const nsHTMLReflowState& aReflowState, nsTableRowFrame* aStartRowFrameIn = nsnull); + void DidResizeRows(nsIPresContext& aPresContext, + const nsHTMLReflowState& aReflowState, + nsTableRowFrame* aStartRowFrameIn = nsnull); /** Incremental Reflow attempts to do column balancing with the minimum number of reflow * commands to child elements. This is done by processing the reflow command, @@ -337,8 +349,12 @@ public: virtual nsIFrame* GetLastFrame() { return mFrames.LastChild(); }; virtual void GetNextFrame(nsIFrame* aFrame, nsIFrame** aResult) { aFrame->GetNextSibling(aResult); }; - PRBool IsRepeatable(); - void SetRepeatable(PRBool aRepeatable); + PRBool IsRepeatable() const; + void SetRepeatable(PRBool aRepeatable); + PRBool HasStyleHeight() const; + void SetHasStyleHeight(PRBool aValue); + PRBool NeedSpecialReflow() const; + void SetNeedSpecialReflow(PRBool aValue); #ifdef DEBUG_TABLE_REFLOW_TIMING public: @@ -347,7 +363,7 @@ public: }; -inline PRBool nsTableRowGroupFrame::IsRepeatable() +inline PRBool nsTableRowGroupFrame::IsRepeatable() const { return (mState & NS_ROWGROUP_REPEATABLE) == NS_ROWGROUP_REPEATABLE; } @@ -361,4 +377,31 @@ inline void nsTableRowGroupFrame::SetRepeatable(PRBool aRepeatable) } } +inline PRBool nsTableRowGroupFrame::NeedSpecialReflow() const +{ + return (mState & NS_ROWGROUP_NEED_SPECIAL_REFLOW) == NS_ROWGROUP_NEED_SPECIAL_REFLOW; +} + +inline void nsTableRowGroupFrame::SetNeedSpecialReflow(PRBool aValue) +{ + if (aValue) { + mState |= NS_ROWGROUP_NEED_SPECIAL_REFLOW; + } else { + mState &= ~NS_ROWGROUP_NEED_SPECIAL_REFLOW; + } +} + +inline PRBool nsTableRowGroupFrame::HasStyleHeight() const +{ + return (mState & NS_ROWGROUP_HAS_STYLE_HEIGHT) == NS_ROWGROUP_HAS_STYLE_HEIGHT; +} + +inline void nsTableRowGroupFrame::SetHasStyleHeight(PRBool aValue) +{ + if (aValue) { + mState |= NS_ROWGROUP_HAS_STYLE_HEIGHT; + } else { + mState &= ~NS_ROWGROUP_HAS_STYLE_HEIGHT; + } +} #endif diff --git a/mozilla/layout/tables/nsCellMap.cpp b/mozilla/layout/tables/nsCellMap.cpp index 1aea50ec8d7..41ad8dfa13a 100644 --- a/mozilla/layout/tables/nsCellMap.cpp +++ b/mozilla/layout/tables/nsCellMap.cpp @@ -337,15 +337,19 @@ nsTableCellMap::RemoveRows(nsIPresContext* aPresContext, PRInt32 nsTableCellMap::GetNumCellsOriginatingInRow(PRInt32 aRowIndex) { - PRInt32 cellCount = 0; - CellData* tempCell; - do - { - tempCell = GetCellAt(aRowIndex,cellCount); - if (tempCell) - cellCount++; - }while(tempCell); - return cellCount; + PRInt32 originCount = 0; + + CellData* cellData; + PRInt32 colIndex = 0; + + do { + cellData = GetCellAt(aRowIndex, colIndex); + if (cellData && cellData->GetCellFrame()) + originCount++; + colIndex++; + } while(cellData); + + return originCount; } PRInt32 diff --git a/mozilla/layout/tables/nsTableCellFrame.cpp b/mozilla/layout/tables/nsTableCellFrame.cpp index 1eec30abe4b..523dcb2637b 100644 --- a/mozilla/layout/tables/nsTableCellFrame.cpp +++ b/mozilla/layout/tables/nsTableCellFrame.cpp @@ -91,6 +91,22 @@ nsTableCellFrame::~nsTableCellFrame() #endif } +nsTableCellFrame* +nsTableCellFrame::GetNextCell() const +{ + nsIFrame* childFrame; + GetNextSibling(&childFrame); + while (childFrame) { + nsCOMPtr frameType; + childFrame->GetFrameType(getter_AddRefs(frameType)); + if (nsLayoutAtoms::tableCellFrame == frameType.get()) { + return (nsTableCellFrame*)childFrame; + } + childFrame->GetNextSibling(&childFrame); + } + return nsnull; +} + NS_IMETHODIMP nsTableCellFrame::Init(nsIPresContext* aPresContext, nsIContent* aContent, @@ -694,6 +710,15 @@ NS_METHOD nsTableCellFrame::Reflow(nsIPresContext* aPresContext, } nsresult rv = NS_OK; + + if ((NS_UNCONSTRAINEDSIZE != aReflowState.availableWidth) && + ((NS_UNCONSTRAINEDSIZE == aReflowState.mComputedHeight) || + (0 == aReflowState.mComputedHeight)) && + !mPrevInFlow && + nsTableFrame::IsPctHeight(mStyleContext)) { + nsTableFrame::NotifyAncestorsOfSpecialReflow(aReflowState); + SetNeedSpecialReflow(PR_TRUE); + } // this should probably be cached somewhere nsCompatibility compatMode; aPresContext->GetCompatibilityMode(&compatMode); @@ -714,6 +739,8 @@ NS_METHOD nsTableCellFrame::Reflow(nsIPresContext* aPresContext, /* XXX: remove tableFrame when border-collapse inherits */ nsTableFrame* tableFrame=nsnull; rv = nsTableFrame::GetTableFrame(this, tableFrame); + nsTableFrame* tableFrameFirstInFlow = (nsTableFrame*)tableFrame->GetFirstInFlow(); + nsMargin borderPadding = aReflowState.mComputedPadding; nsMargin border; GetCellBorder(border, tableFrame); @@ -768,6 +795,10 @@ NS_METHOD nsTableCellFrame::Reflow(nsIPresContext* aPresContext, kidSize.width=kidSize.height=kidSize.ascent=kidSize.descent=0; SetPriorAvailWidth(aReflowState.availableWidth); nsIFrame* firstKid = mFrames.FirstChild(); + + if (aReflowState.mFlags.mSpecialTableReflow) { + ((nsHTMLReflowState&)aReflowState).mComputedHeight = mRect.height - topInset - bottomInset; + } nsHTMLReflowState kidReflowState(aPresContext, aReflowState, firstKid, availSize); @@ -898,8 +929,7 @@ NS_METHOD nsTableCellFrame::Reflow(nsIPresContext* aPresContext, // if the table allocated extra vertical space to row groups, rows, cells in pagination mode // then use that height as the desired height unless the cell needs to split. - nsTableFrame* tableFrameFirstInFlow = (nsTableFrame*)tableFrame->GetFirstInFlow(); - if ((NS_FRAME_COMPLETE == aStatus) && tableFrameFirstInFlow->IsThirdPassReflow()) { + if ((NS_FRAME_COMPLETE == aStatus) && aReflowState.mFlags.mSpecialTableReflow) { cellHeight = PR_MAX(cellHeight, mRect.height); } // next determine the cell's width @@ -942,6 +972,10 @@ NS_METHOD nsTableCellFrame::Reflow(nsIPresContext* aPresContext, // remember my desired size for this reflow SetDesiredSize(aDesiredSize); + if (aReflowState.mFlags.mSpecialTableReflow) { + SetNeedSpecialReflow(PR_FALSE); + } + #if defined DEBUG_TABLE_REFLOW_TIMING nsTableFrame::DebugReflow(this, (nsHTMLReflowState&)aReflowState, &aDesiredSize, aStatus); #endif diff --git a/mozilla/layout/tables/nsTableCellFrame.h b/mozilla/layout/tables/nsTableCellFrame.h index 04077abfafb..e6cf741b938 100644 --- a/mozilla/layout/tables/nsTableCellFrame.h +++ b/mozilla/layout/tables/nsTableCellFrame.h @@ -49,7 +49,8 @@ class nsHTMLValue; /** * Additional frame-state bits */ -#define NS_TABLE_CELL_FRAME_CONTENT_EMPTY 0x80000000 +#define NS_TABLE_CELL_CONTENT_EMPTY 0x80000000 +#define NS_TABLE_CELL_NEED_SPECIAL_REFLOW 0x40000000 /** * nsTableCellFrame @@ -239,11 +240,16 @@ public: PRBool GetContentEmpty(); void SetContentEmpty(PRBool aContentEmpty); + PRBool NeedSpecialReflow(); + void SetNeedSpecialReflow(PRBool aContentEmpty); + // The collapse offset is (0,0) except for cells originating in a row/col which is collapsed void SetCollapseOffsetX(nsIPresContext* aPresContext, nscoord aXOffset); void SetCollapseOffsetY(nsIPresContext* aPresContext, nscoord aYOffset); void GetCollapseOffset(nsIPresContext* aPresContext, nsPoint& aOffset); + nsTableCellFrame* GetNextCell() const; + protected: /** implement abstract method on nsHTMLContainerFrame */ virtual PRIntn GetSkipSides() const; @@ -332,19 +338,33 @@ inline nsSize nsTableCellFrame::GetPass1MaxElementSize() const inline PRBool nsTableCellFrame::GetContentEmpty() { - return (mState & NS_TABLE_CELL_FRAME_CONTENT_EMPTY) == - NS_TABLE_CELL_FRAME_CONTENT_EMPTY; + return (mState & NS_TABLE_CELL_CONTENT_EMPTY) == + NS_TABLE_CELL_CONTENT_EMPTY; } inline void nsTableCellFrame::SetContentEmpty(PRBool aContentEmpty) { if (aContentEmpty) { - mState |= NS_TABLE_CELL_FRAME_CONTENT_EMPTY; + mState |= NS_TABLE_CELL_CONTENT_EMPTY; } else { - mState &= ~NS_TABLE_CELL_FRAME_CONTENT_EMPTY; + mState &= ~NS_TABLE_CELL_CONTENT_EMPTY; } } +inline PRBool nsTableCellFrame::NeedSpecialReflow() +{ + return (mState & NS_TABLE_CELL_NEED_SPECIAL_REFLOW) == + NS_TABLE_CELL_NEED_SPECIAL_REFLOW; +} + +inline void nsTableCellFrame::SetNeedSpecialReflow(PRBool aValue) +{ + if (aValue) { + mState |= NS_TABLE_CELL_NEED_SPECIAL_REFLOW; + } else { + mState &= ~NS_TABLE_CELL_NEED_SPECIAL_REFLOW; + } +} #endif diff --git a/mozilla/layout/tables/nsTableColFrame.cpp b/mozilla/layout/tables/nsTableColFrame.cpp index 419eafe1c84..53a5b035632 100644 --- a/mozilla/layout/tables/nsTableColFrame.cpp +++ b/mozilla/layout/tables/nsTableColFrame.cpp @@ -84,6 +84,7 @@ void nsTableColFrame::SetType(nsTableColType aType) { mBits.mType = aType - eColContent; } + // XXX what about other style besides width nsStyleCoord nsTableColFrame::GetStyleWidth() const { diff --git a/mozilla/layout/tables/nsTableFrame.cpp b/mozilla/layout/tables/nsTableFrame.cpp index 61053481929..57e0e38dc59 100644 --- a/mozilla/layout/tables/nsTableFrame.cpp +++ b/mozilla/layout/tables/nsTableFrame.cpp @@ -1580,8 +1580,15 @@ nsTableFrame::GetSkipSides() const PRBool nsTableFrame::NeedsReflow(const nsHTMLReflowState& aReflowState) { PRBool result = PR_TRUE; - if ((eReflowReason_Incremental == aReflowState.reason) && - (NS_UNCONSTRAINEDSIZE == aReflowState.availableHeight)) { + if (eReflowReason_Resize == aReflowState.reason) { + if (aReflowState.mFlags.mSpecialTableReflow && + !NeedSpecialReflow() && + !NeedToInitiateSpecialReflow()) { + result = PR_FALSE; + } + } + else if ((eReflowReason_Incremental == aReflowState.reason) && + (NS_UNCONSTRAINEDSIZE == aReflowState.availableHeight)) { // It's an incremental reflow and we're in galley mode. Only // do a full reflow if we need to. #ifndef TABLE_REFLOW_COALESCING_OFF @@ -1809,6 +1816,30 @@ ProcessRowInserted(nsIPresContext* aPresContext, } } +void +nsTableFrame::NotifyAncestorsOfSpecialReflow(const nsHTMLReflowState& aReflowState) +{ + const nsHTMLReflowState* rs = aReflowState.parentReflowState; + while (rs) { + nsCOMPtr frameType; + rs->frame->GetFrameType(getter_AddRefs(frameType)); + if (nsLayoutAtoms::tableCellFrame == frameType.get()) { + ((nsTableCellFrame*)rs->frame)->SetNeedSpecialReflow(PR_TRUE); + } + else if (nsLayoutAtoms::tableRowFrame == frameType.get()) { + ((nsTableRowFrame*)rs->frame)->SetNeedSpecialReflow(PR_TRUE); + } + else if (nsLayoutAtoms::tableRowGroupFrame == frameType.get()) { + ((nsTableRowGroupFrame*)rs->frame)->SetNeedSpecialReflow(PR_TRUE); + } + else if (nsLayoutAtoms::tableFrame == frameType.get()) { + ((nsTableFrame*)rs->frame)->SetNeedToInitiateSpecialReflow(PR_TRUE); + break; + } + rs = rs->parentReflowState; + } +} + /* overview: if mFirstPassValid is false, this is our first time through since content was last changed do pass 1 @@ -1904,36 +1935,57 @@ NS_METHOD nsTableFrame::Reflow(nsIPresContext* aPresContext, if (NS_FAILED(rv)) return rv; - PRBool needPass3Reflow = PR_FALSE; + PRBool haveDesiredHeight = PR_FALSE; PRBool balanced = PR_FALSE; // Reflow the entire table. This phase is necessary during a constrained initial reflow // and other reflows which require either a strategy init or balance. This isn't done // during an unconstrained reflow because another reflow will be processed later. if (NeedsReflow(aReflowState) && (NS_UNCONSTRAINEDSIZE != aReflowState.availableWidth)) { - // see if an extra (3rd) reflow will be necessary in pagination mode when there is a specified table height - if (isPaginated && !mPrevInFlow && (NS_UNCONSTRAINEDSIZE != aReflowState.availableHeight)) { - nscoord tableSpecifiedHeight = CalcBorderBoxHeight(aReflowState); - if ((tableSpecifiedHeight > 0) && - (tableSpecifiedHeight != NS_UNCONSTRAINEDSIZE)) { - needPass3Reflow = PR_TRUE; + if (!mPrevInFlow) { + // see if an extra reflow will be necessary when there is a pct height but no height on the parent + if ( ((NS_UNCONSTRAINEDSIZE == aReflowState.mComputedHeight) || + (0 == aReflowState.mComputedHeight)) && IsPctHeight(mStyleContext)) { + NotifyAncestorsOfSpecialReflow(aReflowState); + SetNeedSpecialReflow(PR_TRUE); + } + // see if an extra reflow will be necessary in pagination mode when there is a specified table height + else if (isPaginated && (NS_UNCONSTRAINEDSIZE != aReflowState.availableHeight)) { + nscoord tableSpecifiedHeight = CalcBorderBoxHeight(aReflowState); + if ((tableSpecifiedHeight > 0) && + (tableSpecifiedHeight != NS_UNCONSTRAINEDSIZE)) { + SetNeedToInitiateSpecialReflow(PR_TRUE); + } } } // if we need to reflow the table an extra time, then don't constrain the height of the previous reflow - nscoord availHeight = (needPass3Reflow) ? NS_UNCONSTRAINEDSIZE : aReflowState.availableHeight; + nscoord availHeight = !aReflowState.mFlags.mSpecialTableReflow && + (NeedSpecialReflow() | NeedToInitiateSpecialReflow()) + ? NS_UNCONSTRAINEDSIZE : aReflowState.availableHeight; ReflowTable(aPresContext, aDesiredSize, aReflowState, availHeight, nextReason, doCollapse, balanced, aStatus); - if (needPass3Reflow) { + if (!aReflowState.mFlags.mSpecialTableReflow && NeedToInitiateSpecialReflow() && !NeedSpecialReflow()) { aDesiredSize.height = CalcDesiredHeight(aPresContext, aReflowState); // distributes extra vertical space to rows - SetThirdPassReflow(PR_TRUE); // set it and leave it set for frames that may split + ((nsHTMLReflowState::ReflowStateFlags&)aReflowState.mFlags).mSpecialTableReflow = PR_TRUE; ReflowTable(aPresContext, aDesiredSize, aReflowState, aReflowState.availableHeight, nextReason, doCollapse, balanced, aStatus); + haveDesiredHeight = PR_TRUE;; } } + else if (aReflowState.mFlags.mSpecialTableReflow) { + aDesiredSize.width = mRect.width; + aDesiredSize.height = mRect.height; +#if defined DEBUG_TABLE_REFLOW_TIMING + nsTableFrame::DebugReflow(this, (nsHTMLReflowState&)aReflowState, &aDesiredSize, aStatus); +#endif + SetNeedSpecialReflow(PR_FALSE); + SetNeedToInitiateSpecialReflow(PR_FALSE); + return NS_OK; + } - aDesiredSize.width = GetDesiredWidth(); - if (!needPass3Reflow) { + aDesiredSize.width = GetDesiredWidth(); + if (!haveDesiredHeight) { aDesiredSize.height = CalcDesiredHeight(aPresContext, aReflowState); } @@ -1986,6 +2038,10 @@ NS_METHOD nsTableFrame::Reflow(nsIPresContext* aPresContext, } } + if (aReflowState.mFlags.mSpecialTableReflow) { + SetNeedSpecialReflow(PR_FALSE); + SetNeedToInitiateSpecialReflow(PR_FALSE); + } #if defined DEBUG_TABLE_REFLOW_TIMING nsTableFrame::DebugReflow(this, (nsHTMLReflowState&)aReflowState, &aDesiredSize, aStatus); #endif @@ -3253,81 +3309,6 @@ nsTableFrame::CalcDesiredWidth(const nsHTMLReflowState& aReflowState) return tableWidth; } -/** - get the table height attribute - if it is auto, table height = SUM(height of rowgroups) - else if (resolved table height attribute > SUM(height of rowgroups)) - proportionately distribute extra height to each row - we assume we are passed in the default table height==the sum of the heights of the table's rowgroups - in aDesiredSize.height. - */ -void -nsTableFrame::DistributeSpaceToCells(nsIPresContext* aPresContext, - const nsHTMLReflowState& aReflowState, - nsIFrame* aRowGroupFrame) -{ - // now that all of the rows have been resized, resize the cells - nsTableRowGroupFrame* rowGroupFrame = (nsTableRowGroupFrame*)aRowGroupFrame; - nsIFrame * rowFrame = rowGroupFrame->GetFirstFrame(); - while (rowFrame) { - const nsStyleDisplay *rowDisplay; - rowFrame->GetStyleData(eStyleStruct_Display, ((const nsStyleStruct *&)rowDisplay)); - if (NS_STYLE_DISPLAY_TABLE_ROW == rowDisplay->mDisplay) { - ((nsTableRowFrame *)rowFrame)->DidResize(aPresContext, aReflowState); - } - rowGroupFrame->GetNextFrame(rowFrame, &rowFrame); - } -} - -void -nsTableFrame::DistributeSpaceToRows(nsIPresContext* aPresContext, - const nsHTMLReflowState& aReflowState, - nsIFrame* aRowGroupFrame, - nscoord aSumOfRowHeights, - nscoord aExcess, - nscoord& aExcessAllocated, - nscoord& aRowGroupYPos) -{ - // the rows in rowGroupFrame need to be expanded by rowHeightDelta[i] - // and the rowgroup itself needs to be expanded by SUM(row height deltas) - nscoord cellSpacingY = GetCellSpacingY(); - nsTableRowGroupFrame* rowGroupFrame = (nsTableRowGroupFrame*)aRowGroupFrame; - nsIFrame* rowFrame = rowGroupFrame->GetFirstFrame(); - nscoord excessForRowGroup = 0; - nscoord y = 0; - float p2t; - aPresContext->GetPixelsToTwips(&p2t); - while (rowFrame) { - const nsStyleDisplay *rowDisplay; - rowFrame->GetStyleData(eStyleStruct_Display, ((const nsStyleStruct *&)rowDisplay)); - if (NS_STYLE_DISPLAY_TABLE_ROW == rowDisplay->mDisplay) { - // the row needs to be expanded by the proportion this row contributed to the original height - nsRect rowRect; - rowFrame->GetRect(rowRect); - float percent = ((float)(rowRect.height)) / (float)aSumOfRowHeights; - nscoord excessForRow = RoundToPixel(NSToCoordRound((float)aExcess * percent), p2t); - excessForRow = PR_MIN(excessForRow, aExcess - aExcessAllocated); - - nsRect newRowRect(rowRect.x, y, rowRect.width, excessForRow + rowRect.height); - rowFrame->SetRect(aPresContext, newRowRect); - y = newRowRect.YMost() + cellSpacingY; - - aExcessAllocated += excessForRow; - excessForRowGroup += excessForRow; - } - - rowGroupFrame->GetNextFrame(rowFrame, &rowFrame); - } - - nsRect rowGroupRect; - aRowGroupFrame->GetRect(rowGroupRect); - nsRect newRowGroupRect(rowGroupRect.x, aRowGroupYPos, rowGroupRect.width, - excessForRowGroup + rowGroupRect.height); - aRowGroupFrame->SetRect(aPresContext, newRowGroupRect); - aRowGroupYPos = newRowGroupRect.YMost(); - - DistributeSpaceToCells(aPresContext, aReflowState, aRowGroupFrame); -} nscoord nsTableFrame::CalcDesiredHeight(nsIPresContext* aPresContext, @@ -3347,65 +3328,239 @@ nsTableFrame::CalcDesiredHeight(nsIPresContext* aPresContext, OrderRowGroups(rowGroups, numRowGroups, nsnull); if (numRowGroups <= 0) return 0; - nscoord naturalHeight = borderPadding.top + cellSpacingY + borderPadding.bottom; + nscoord desiredHeight = borderPadding.top + cellSpacingY + borderPadding.bottom; for (PRUint32 rgX = 0; rgX < numRowGroups; rgX++) { nsIFrame* rg = (nsIFrame*)rowGroups.ElementAt(rgX); if (rg) { nsRect rgRect; rg->GetRect(rgRect); - naturalHeight += rgRect.height + cellSpacingY; + desiredHeight += rgRect.height + cellSpacingY; } } - nscoord desiredHeight = naturalHeight; - // see if a specified table height requires dividing additional space to rows if (!mPrevInFlow) { nscoord tableSpecifiedHeight = CalcBorderBoxHeight(aReflowState); if ((tableSpecifiedHeight > 0) && (tableSpecifiedHeight != NS_UNCONSTRAINEDSIZE) && - (tableSpecifiedHeight > naturalHeight)) { - desiredHeight = tableSpecifiedHeight; - + (tableSpecifiedHeight > desiredHeight)) { + // proportionately distribute the excess height to unconstrained rows in each + // unconstrained row group.We don't need to do this if it's an unconstrained reflow if (NS_UNCONSTRAINEDSIZE != aReflowState.availableWidth) { - // proportionately distribute the excess height to each row. Note that we - // don't need to do this if it's an unconstrained reflow - nscoord excess = tableSpecifiedHeight - naturalHeight; - nscoord sumOfRowHeights = 0; - nscoord rowGroupYPos = aReflowState.mComputedBorderPadding.top + cellSpacingY; - - nsAutoVoidArray rowGroups; - PRUint32 numRowGroups; - OrderRowGroups(rowGroups, numRowGroups, nsnull); - - PRUint32 rgX; - for (rgX = 0; rgX < numRowGroups; rgX++) { - nsTableRowGroupFrame* rgFrame = - GetRowGroupFrame((nsIFrame*)rowGroups.ElementAt(rgX)); - if (rgFrame) { - sumOfRowHeights += rgFrame->GetHeightOfRows(aPresContext); - } - } - nscoord excessAllocated = 0; - for (rgX = 0; rgX < numRowGroups; rgX++) { - nsTableRowGroupFrame* rgFrame = - GetRowGroupFrame((nsIFrame*)rowGroups.ElementAt(rgX)); - if (rgFrame) { - DistributeSpaceToRows(aPresContext, aReflowState, rgFrame, sumOfRowHeights, - excess, excessAllocated, rowGroupYPos); - - // Make sure child views are properly positioned - // XXX what happens if childFrame is a scroll frame and this gets skipped? - nsTableFrame::RePositionViews(aPresContext, rgFrame); - } - rowGroupYPos += cellSpacingY; - } + DistributeHeightToRows(aPresContext, aReflowState, tableSpecifiedHeight - desiredHeight); } + desiredHeight = tableSpecifiedHeight; } } return desiredHeight; } +static +void ResizeCells(nsTableFrame& aTableFrame, + nsIPresContext* aPresContext, + const nsHTMLReflowState& aReflowState) +{ + nsAutoVoidArray rowGroups; + PRUint32 numRowGroups; + aTableFrame.OrderRowGroups(rowGroups, numRowGroups, nsnull); + + for (PRUint32 rgX = 0; (rgX < numRowGroups); rgX++) { + nsTableRowGroupFrame* rgFrame = aTableFrame.GetRowGroupFrame((nsIFrame*)rowGroups.ElementAt(rgX)); + nsTableRowFrame* rowFrame = rgFrame->GetFirstRow(); + while (rowFrame) { + rowFrame->DidResize(aPresContext, aReflowState); + rowFrame = rowFrame->GetNextRow(); + } + } +} + +void +nsTableFrame::DistributeHeightToRows(nsIPresContext* aPresContext, + const nsHTMLReflowState& aReflowState, + nscoord aAmount) +{ + float p2t; + aPresContext->GetPixelsToTwips(&p2t); + + nscoord cellSpacingY = GetCellSpacingY(); + + nscoord sumOfRowHeights = 0; + nscoord rowGroupYPos = aReflowState.mComputedBorderPadding.top + cellSpacingY; + + nsVoidArray rowGroups; + PRUint32 numRowGroups; + OrderRowGroups(rowGroups, numRowGroups, nsnull); + + nscoord amountUsed = 0; + // distribute space to each pct height row whose row group doesn't have a computed + // height, and base the pct on the table height. If the row group had a computed + // height, then this was already done in nsTableRowGroupFrame::CalculateRowHeights + nscoord pctBasis = aReflowState.mComputedHeight - (GetCellSpacingY() * (GetRowCount() + 1)); + nscoord yOriginRG = aReflowState.mComputedBorderPadding.top + GetCellSpacingY(); + nscoord yEndRG = yOriginRG; + PRUint32 rgX; + for (rgX = 0; (rgX < numRowGroups); rgX++) { + nsTableRowGroupFrame* rgFrame = GetRowGroupFrame((nsIFrame*)rowGroups.ElementAt(rgX)); + nscoord amountUsedByRG = 0; + nscoord yOriginRow = 0; + nsRect rgRect; + rgFrame->GetRect(rgRect); + if (rgFrame && !rgFrame->HasStyleHeight()) { + nsTableRowFrame* rowFrame = rgFrame->GetFirstRow(); + while (rowFrame) { + nsRect rowRect; + rowFrame->GetRect(rowRect); + if ((amountUsed < aAmount) && rowFrame->HasPctHeight()) { + nscoord pctHeight = nsTableFrame::RoundToPixel(rowFrame->GetHeight(pctBasis), p2t); + nscoord amountForRow = PR_MIN(aAmount - amountUsed, pctHeight - rowRect.height); + if (amountForRow > 0) { + rowRect.height += amountForRow; + rowFrame->SetRect(aPresContext, rowRect); + yOriginRow += rowRect.height + cellSpacingY; + yEndRG += rowRect.height + cellSpacingY; + amountUsed += amountForRow; + amountUsedByRG += amountForRow; + //rowFrame->DidResize(aPresContext, aReflowState); + nsTableFrame::RePositionViews(aPresContext, rowFrame); + } + } + else { + if (amountUsed > 0) { + rowFrame->MoveTo(aPresContext, rowRect.x, yOriginRow); + nsTableFrame::RePositionViews(aPresContext, rowFrame); + } + yOriginRow += rowRect.height + cellSpacingY; + yEndRG += rowRect.height + cellSpacingY; + } + rowFrame = rowFrame->GetNextRow(); + } + if (amountUsed > 0) { + rgRect.y = yOriginRG; + rgRect.height += amountUsedByRG; + rgFrame->SetRect(aPresContext, rgRect); + } + } + else if (amountUsed > 0) { + rgFrame->MoveTo(aPresContext, 0, yOriginRG); + // Make sure child views are properly positioned + nsTableFrame::RePositionViews(aPresContext, rgFrame); + } + yOriginRG = yEndRG; + } + + if (amountUsed >= aAmount) { + ResizeCells(*this, aPresContext, aReflowState); + return; + } + + // get the first row without a style height where its row group has an unconstrianed height + nsTableRowGroupFrame* firstUnStyledRG = nsnull; + nsTableRowFrame* firstUnStyledRow = nsnull; + for (rgX = 0; (rgX < numRowGroups) && !firstUnStyledRG; rgX++) { + nsTableRowGroupFrame* rgFrame = GetRowGroupFrame((nsIFrame*)rowGroups.ElementAt(rgX)); + if (rgFrame && !rgFrame->HasStyleHeight()) { + nsTableRowFrame* rowFrame = rgFrame->GetFirstRow(); + while (rowFrame) { + if (!rowFrame->HasStyleHeight()) { + firstUnStyledRG = rgFrame; + firstUnStyledRow = rowFrame; + break; + } + rowFrame = rowFrame->GetNextRow(); + } + } + } + + nsTableRowFrame* lastElligibleRow = nsnull; + // accumulate the correct divisor. This will be the total of all unstyled rows inside + // unstyled row groups, unless there are none, in which case, it will be all rows + nscoord divisor = 0; + for (rgX = 0; rgX < numRowGroups; rgX++) { + nsTableRowGroupFrame* rgFrame = GetRowGroupFrame((nsIFrame*)rowGroups.ElementAt(rgX)); + if (rgFrame && (!firstUnStyledRG || !rgFrame->HasStyleHeight())) { + nsTableRowFrame* rowFrame = rgFrame->GetFirstRow(); + while (rowFrame) { + if (!firstUnStyledRG || !rowFrame->HasStyleHeight()) { + nsRect rowRect; + rowFrame->GetRect(rowRect); + divisor += rowRect.height; + lastElligibleRow = rowFrame; + } + rowFrame = rowFrame->GetNextRow(); + } + } + } + if (divisor <= 0) { + NS_ASSERTION(PR_FALSE, "invlaid divisor"); + return; + } + + // allocate the extra height to the unstyled row groups and rows + pctBasis = aAmount - amountUsed; + yOriginRG = aReflowState.mComputedBorderPadding.top + cellSpacingY; + yEndRG = yOriginRG; + for (rgX = 0; rgX < numRowGroups; rgX++) { + nsTableRowGroupFrame* rgFrame = GetRowGroupFrame((nsIFrame*)rowGroups.ElementAt(rgX)); + if (!rgFrame) continue; + nscoord amountUsedByRG = 0; + nscoord yOriginRow = 0; + nsRect rgRect; + rgFrame->GetRect(rgRect); + // see if there is an eligible row group + if (!firstUnStyledRG || !rgFrame->HasStyleHeight()) { + nsTableRowFrame* rowFrame = rgFrame->GetFirstRow(); + while (rowFrame) { + nsRect rowRect; + rowFrame->GetRect(rowRect); + // see if there is an eligible row + if (!firstUnStyledRow || !rowFrame->HasStyleHeight()) { + // The amount of additional space each row gets is proportional to its height + float percent = rowRect.height / ((float)divisor); + // give rows their percentage, except for the last row which gets the remainder + nscoord amountForRow = (rowFrame == lastElligibleRow) + ? aAmount - amountUsed : NSToCoordRound(((float)(pctBasis)) * percent); + amountForRow = PR_MIN(nsTableFrame::RoundToPixel(amountForRow, p2t), aAmount - amountUsed); + // update the row height + nsRect newRowRect(rowRect.x, yOriginRow, rowRect.width, rowRect.height + amountForRow); + rowFrame->SetRect(aPresContext, newRowRect); + yOriginRow += newRowRect.height + cellSpacingY; + yEndRG += newRowRect.height + cellSpacingY; + + amountUsed += amountForRow; + amountUsedByRG += amountForRow; + NS_ASSERTION((amountUsed <= aAmount), "invalid row allocation"); + //rowFrame->DidResize(aPresContext, aReflowState); + nsTableFrame::RePositionViews(aPresContext, rowFrame); + } + else { + if (amountUsed > 0) { + rowFrame->MoveTo(aPresContext, rowRect.x, yOriginRow); + nsTableFrame::RePositionViews(aPresContext, rowFrame); + } + yOriginRow += rowRect.height + cellSpacingY; + yEndRG += rowRect.height + cellSpacingY; + } + rowFrame = rowFrame->GetNextRow(); + } + if (amountUsed > 0) { + rgRect.y = yOriginRG; + rgRect.height += amountUsedByRG; + rgFrame->SetRect(aPresContext, rgRect); + } + // Make sure child views are properly positioned + // XXX what happens if childFrame is a scroll frame and this gets skipped? see also below + } + else if (amountUsed > 0) { + rgFrame->MoveTo(aPresContext, 0, yOriginRG); + // Make sure child views are properly positioned + nsTableFrame::RePositionViews(aPresContext, rgFrame); + } + yOriginRG = yEndRG; + } + + ResizeCells(*this, aPresContext, aReflowState); +} + static void UpdateCol(nsTableFrame& aTableFrame, nsTableColFrame& aColFrame, @@ -3435,6 +3590,17 @@ UpdateCol(nsTableFrame& aTableFrame, } } +PRBool +nsTableFrame::IsPctHeight(nsIStyleContext* aStyleContext) +{ + PRBool result = PR_FALSE; + if (aStyleContext) { + nsStylePosition* position = (nsStylePosition*)aStyleContext->GetStyleData(eStyleStruct_Position); + result = (eStyleUnit_Percent == position->mHeight.GetUnit()); + } + return result; +} + PRBool nsTableFrame::CellChangedWidth(const nsTableCellFrame& aCellFrame, nscoord aPrevCellMin, @@ -4370,9 +4536,20 @@ NS_IMETHODIMP nsTableFrame::GetTableSize(PRInt32& aRowCount, PRInt32& aColCount) PRInt32 nsTableFrame::GetNumCellsOriginatingInCol(PRInt32 aColIndex) const { nsTableCellMap* cellMap = GetCellMap(); - return cellMap->GetNumCellsOriginatingInCol(aColIndex); + if (cellMap) + return cellMap->GetNumCellsOriginatingInCol(aColIndex); + else + return 0; } +PRInt32 nsTableFrame::GetNumCellsOriginatingInRow(PRInt32 aRowIndex) const +{ + nsTableCellMap* cellMap = GetCellMap(); + if (cellMap) + return cellMap->GetNumCellsOriginatingInRow(aRowIndex); + else + return 0; +} /******************************************************************************** @@ -4477,7 +4654,11 @@ void nsTableFrame::DebugReflow(nsIFrame* aFrame, if (!aMetrics) { // start PrettyUC(aState.availableWidth, width); PrettyUC(aState.availableHeight, height); - printf("r=%d a=%s,%s ", aState.reason, width, height); + printf("r=%d ", aState.reason); + if (aState.mFlags.mSpecialTableReflow) { + printf("special "); + } + printf("a=%s,%s ", width, height); PrettyUC(aState.mComputedWidth, width); PrettyUC(aState.mComputedHeight, height); printf("c=%s,%s ", width, height); diff --git a/mozilla/layout/tables/nsTableFrame.h b/mozilla/layout/tables/nsTableFrame.h index 7b462dc77ce..3d0efabf345 100644 --- a/mozilla/layout/tables/nsTableFrame.h +++ b/mozilla/layout/tables/nsTableFrame.h @@ -207,6 +207,8 @@ public: float aPixelToTwips, nsPixelRound aRound= eAlwaysRoundUp); + static void NotifyAncestorsOfSpecialReflow(const nsHTMLReflowState& aReflowState); + NS_IMETHOD IsPercentageBase(PRBool& aBase) const; static nsresult AppendDirtyReflowCommand(nsIPresShell* aPresShell, @@ -258,6 +260,7 @@ public: nsIAtom* aChildType); PRBool IsAutoWidth(PRBool* aIsPctWidth = nsnull); PRBool IsAutoHeight(); + static PRBool IsPctHeight(nsIStyleContext* aStyleContext); /** @return PR_TRUE if aDisplayType represents a rowgroup of any sort * (header, footer, or body) @@ -491,14 +494,18 @@ public: PRInt32* aColSpan = nsnull); PRInt32 GetNumCellsOriginatingInCol(PRInt32 aColIndex) const; + PRInt32 GetNumCellsOriginatingInRow(PRInt32 aRowIndex) const; PRBool HasPctCol() const; void SetHasPctCol(PRBool aValue); PRBool HasCellSpanningPctCol() const; void SetHasCellSpanningPctCol(PRBool aValue); - // is this the 3rd reflow due to a height on a table in pagination mode. - PRBool IsThirdPassReflow() const; + + PRBool NeedSpecialReflow() const; + void SetNeedSpecialReflow(PRBool aValue); + PRBool NeedToInitiateSpecialReflow() const; + void SetNeedToInitiateSpecialReflow(PRBool aValue); protected: @@ -527,7 +534,6 @@ protected: void SetDescendantReflowedNotTimeout(PRBool aValue); PRBool RequestedTimeoutReflow() const; void SetRequestedTimeoutReflow(PRBool aValue); - void SetThirdPassReflow(PRBool aValue); void InterruptNotification(nsIPresContext* aPresContext, PRBool aIsRequest); @@ -652,18 +658,12 @@ protected: // reflow state, and for the table attributes and parent nscoord CalcDesiredHeight(nsIPresContext* aPresContext, const nsHTMLReflowState& aReflowState); - // The following two functions are helpers for CalcDesiredHeight + + // The following is a helper for CalcDesiredHeight - void DistributeSpaceToCells(nsIPresContext* aPresContext, + void DistributeHeightToRows(nsIPresContext* aPresContext, const nsHTMLReflowState& aReflowState, - nsIFrame* aRowGroupFrame); - void DistributeSpaceToRows(nsIPresContext* aPresContext, - const nsHTMLReflowState& aReflowState, - nsIFrame* aRowGroupFrame, - nscoord aSumOfRowHeights, - nscoord aExcess, - nscoord& aExcessAllocated, - nscoord& aRowGroupYPos); + nscoord aAmount); void PlaceChild(nsIPresContext* aPresContext, nsTableReflowState& aReflowState, @@ -830,8 +830,6 @@ public: /* ----- Cell Map public methods ----- */ // percentage height cells void ComputePercentBasisForRows(const nsHTMLReflowState& aReflowState); - nscoord GetPercentBasisForRows(); - nscoord GetMinWidth() const; void SetMinWidth(nscoord aWidth); @@ -895,8 +893,9 @@ protected: // targeted at us, as an optimization. unsigned mRequestedTimeoutReflow:1; unsigned mRowInserted:1; - unsigned mThirdPassReflow:1; - int : 21; // unused + unsigned mNeedSpecialReflow:1; + unsigned mNeedToInitiateSpecialReflow:1; + int : 19; // unused } mBits; nsTableCellMap* mCellMap; // maintains the relationships between rows, cols, and cells @@ -941,11 +940,6 @@ inline PRBool nsTableFrame::IsRowGroup(PRInt32 aDisplayType) const (NS_STYLE_DISPLAY_TABLE_ROW_GROUP == aDisplayType)); } -inline nscoord nsTableFrame::GetPercentBasisForRows() -{ - return mPercentBasisForRows; -} - inline void nsTableFrame::SetHadInitialReflow(PRBool aValue) { mBits.mHadInitialReflow = aValue; @@ -1006,16 +1000,25 @@ inline void nsTableFrame::SetRequestedTimeoutReflow(PRBool aValue) mBits.mRequestedTimeoutReflow = (unsigned)aValue; } -inline PRBool nsTableFrame::IsThirdPassReflow() const +inline PRBool nsTableFrame::NeedSpecialReflow() const { - return (PRBool)mBits.mThirdPassReflow; + return (PRBool)mBits.mNeedSpecialReflow; } -inline void nsTableFrame::SetThirdPassReflow(PRBool aValue) +inline void nsTableFrame::SetNeedSpecialReflow(PRBool aValue) { - mBits.mThirdPassReflow = (unsigned)aValue; + mBits.mNeedSpecialReflow = (unsigned)aValue; } +inline PRBool nsTableFrame::NeedToInitiateSpecialReflow() const +{ + return (PRBool)mBits.mNeedToInitiateSpecialReflow; +} + +inline void nsTableFrame::SetNeedToInitiateSpecialReflow(PRBool aValue) +{ + mBits.mNeedToInitiateSpecialReflow = (unsigned)aValue; +} inline PRBool nsTableFrame::IsRowInserted() const { return (PRBool)mBits.mRowInserted; diff --git a/mozilla/layout/tables/nsTableRowFrame.cpp b/mozilla/layout/tables/nsTableRowFrame.cpp index 0ddd271299a..caaa317ead8 100644 --- a/mozilla/layout/tables/nsTableRowFrame.cpp +++ b/mozilla/layout/tables/nsTableRowFrame.cpp @@ -94,6 +94,42 @@ nsTableCellReflowState::nsTableCellReflowState(nsIPresContext* aPresCon FixUp(aAvailSpace); } +void +nsTableRowFrame::SetFixedHeight(nscoord aValue) +{ + if (!HasPctHeight()) { + nscoord height = PR_MAX(0, aValue); + if (HasFixedHeight()) { + if (height > mStyleHeight) { + mStyleHeight = height; + } + } + else { + mStyleHeight = height; + if (height > 0) { + SetHasFixedHeight(PR_TRUE); + } + } + } +} + +void +nsTableRowFrame::SetPctHeight(float aPctValue) +{ + nscoord height = PR_MAX(0, NSToCoordRound(aPctValue * 100.0f)); + if (HasPctHeight()) { + if (height > mStyleHeight) { + mStyleHeight = height; + } + } + else { + mStyleHeight = height; + if (height > 0.0f) { + SetHasPctHeight(PR_TRUE); + } + } +} + void nsTableCellReflowState::FixUp(const nsSize& aAvailSpace) { // fix the mComputed values during a pass 2 reflow since the cell can be a percentage base @@ -121,15 +157,13 @@ TallestCellGotShorter(nscoord aOld, return ((aNew < aOld) && (aOld == aTallest)); } -/* ----------- nsTableRowpFrame ---------- */ +/* ----------- nsTableRowFrame ---------- */ nsTableRowFrame::nsTableRowFrame() - : nsHTMLContainerFrame(), - mAllBits(0) + : nsHTMLContainerFrame() { - mBits.mMinRowSpan = 1; - mBits.mRowIndex = 0; - ResetTallestCell(0); + mBits.mRowIndex = mBits.mFirstInserted = 0; + ResetHeight(0); #ifdef DEBUG_TABLE_REFLOW_TIMING mTimer = new nsReflowTimer(this); #endif @@ -329,6 +363,20 @@ GetHeightOfRowsSpannedBelowFirst(nsTableCellFrame& aTableCellFrame, return height; } +nsTableCellFrame* +nsTableRowFrame::GetFirstCell() +{ + nsIFrame* childFrame = mFrames.FirstChild(); + while (childFrame) { + nsCOMPtr frameType; + childFrame->GetFrameType(getter_AddRefs(frameType)); + if (nsLayoutAtoms::tableCellFrame == frameType.get()) { + return (nsTableCellFrame*)childFrame; + } + childFrame->GetNextSibling(&childFrame); + } + return nsnull; +} /** * Post-reflow hook. This is where the table row does its post-processing @@ -390,43 +438,53 @@ nscoord nsTableRowFrame::GetMaxCellAscent() const return mMaxCellAscent; } -#if 0 // nobody uses this -// returns max-descent amongst all cells that have 'vertical-align: baseline' -// does *not* include cells with rowspans -nscoord nsTableRowFrame::GetMaxCellDescent() const +nscoord +nsTableRowFrame::GetHeight(nscoord aPctBasis) const { - return mMaxCellDescent; -} -#endif - -/** returns the height of the tallest child in this row (ignoring any cell with rowspans) */ -nscoord nsTableRowFrame::GetTallestCell() const -{ - return mTallestCell; + nscoord height = 0; + if ((aPctBasis > 0) && HasPctHeight()) { + height = NSToCoordRound(GetPctHeight() * (float)aPctBasis); + } + else if (HasFixedHeight()) { + height = GetFixedHeight(); + } + return PR_MAX(height, GetContentHeight()); } void -nsTableRowFrame::ResetTallestCell(nscoord aRowStyleHeight) +nsTableRowFrame::ResetHeight(nscoord aFixedHeight) { - mTallestCell = (NS_UNCONSTRAINEDSIZE == aRowStyleHeight) ? 0 : aRowStyleHeight; + SetHasFixedHeight(PR_FALSE); + SetHasPctHeight(PR_FALSE); + SetFixedHeight(0); + SetContentHeight(0); + + if (aFixedHeight > 0) { + SetFixedHeight(aFixedHeight); + } + mMaxCellAscent = 0; mMaxCellDescent = 0; } void -nsTableRowFrame::SetTallestCell(nscoord aHeight, - nscoord aAscent, - nscoord aDescent, - nsTableFrame* aTableFrame, - nsTableCellFrame* aCellFrame) +nsTableRowFrame::UpdateHeight(nscoord aHeight, + nscoord aAscent, + nscoord aDescent, + nsTableFrame* aTableFrame, + nsTableCellFrame* aCellFrame) { - NS_ASSERTION((aTableFrame && aCellFrame) , "invalid call"); + if (!aTableFrame || !aCellFrame) { + NS_ASSERTION(PR_FALSE , "invalid call"); + return; + } + if (aHeight != NS_UNCONSTRAINEDSIZE) { if (!(aCellFrame->HasVerticalAlignBaseline())) { // only the cell's height matters - if (mTallestCell < aHeight) { + if (GetHeight() < aHeight) { PRInt32 rowSpan = aTableFrame->GetEffectiveRowSpan(*aCellFrame); if (rowSpan == 1) { - mTallestCell = aHeight; + SetContentHeight(aHeight); } } } @@ -444,21 +502,23 @@ nsTableRowFrame::SetTallestCell(nscoord aHeight, } } // keep the tallest height in sync - if (mTallestCell < mMaxCellAscent + mMaxCellDescent) { - mTallestCell = mMaxCellAscent + mMaxCellDescent; + if (GetHeight() < mMaxCellAscent + mMaxCellDescent) { + SetContentHeight(mMaxCellAscent + mMaxCellDescent); } } } } -void -nsTableRowFrame::CalcTallestCell() +nscoord +nsTableRowFrame::CalcHeight(const nsHTMLReflowState& aReflowState) { nsTableFrame* tableFrame = nsnull; nsresult rv = nsTableFrame::GetTableFrame(this, tableFrame); - if (NS_FAILED(rv)) return; + if (!tableFrame) return 0; - ResetTallestCell(0); + nscoord computedHeight = (NS_UNCONSTRAINEDSIZE == aReflowState.mComputedHeight) + ? 0 : aReflowState.mComputedHeight; + ResetHeight(computedHeight); for (nsIFrame* kidFrame = mFrames.FirstChild(); kidFrame; kidFrame->GetNextSibling(&kidFrame)) { nsCOMPtr frameType; @@ -470,9 +530,10 @@ nsTableRowFrame::CalcTallestCell() // height may have changed, adjust descent to absorb any excess difference nscoord ascent = ((nsTableCellFrame *)kidFrame)->GetDesiredAscent(); nscoord descent = desSize.height - ascent; - SetTallestCell(desSize.height, ascent, descent, tableFrame, (nsTableCellFrame*)kidFrame); + UpdateHeight(desSize.height, ascent, descent, tableFrame, (nsTableCellFrame*)kidFrame); } } + return GetHeight(); } #if 0 @@ -648,49 +709,6 @@ nsTableRowFrame::GetFrameForPoint(nsIPresContext* aPresContext, return NS_ERROR_FAILURE; } -/* GetMinRowSpan is needed for deviant cases where every cell in a row has a rowspan > 1. - * It sets mMinRowSpan, which is used in FixMinCellHeight - */ -void nsTableRowFrame::GetMinRowSpan(nsTableFrame *aTableFrame) -{ - PRInt32 minRowSpan=-1; - nsIFrame* frame = mFrames.FirstChild(); - while (frame) - { - const nsStyleDisplay *kidDisplay; - frame->GetStyleData(eStyleStruct_Display, ((const nsStyleStruct *&)kidDisplay)); - if (NS_STYLE_DISPLAY_TABLE_CELL == kidDisplay->mDisplay) - { - PRInt32 rowSpan = aTableFrame->GetEffectiveRowSpan((nsTableCellFrame &)*frame); - if (-1==minRowSpan) - minRowSpan = rowSpan; - else if (minRowSpan>rowSpan) - minRowSpan = rowSpan; - } - frame->GetNextSibling(&frame); - } - mBits.mMinRowSpan = unsigned(minRowSpan); -} - -void nsTableRowFrame::FixMinCellHeight(nsTableFrame *aTableFrame) -{ - nsIFrame* frame = mFrames.FirstChild(); - while (frame) { - nsCOMPtr frameType; - frame->GetFrameType(getter_AddRefs(frameType)); - if (nsLayoutAtoms::tableCellFrame == frameType.get()) { - PRInt32 rowSpan = aTableFrame->GetEffectiveRowSpan((nsTableCellFrame &)*frame); - if (PRInt32(mBits.mMinRowSpan) == rowSpan) { - nsRect rect; - frame->GetRect(rect); - if (rect.height > mTallestCell) - mTallestCell = rect.height; - } - } - frame->GetNextSibling(&frame); - } -} - // Calculate the cell's actual size given its pass2 desired width and height. // Takes into account the specified height (in the style), and any special logic // needed for backwards compatibility. @@ -701,37 +719,37 @@ nsTableRowFrame::CalculateCellActualSize(nsIFrame* aCellFrame, nscoord& aDesiredHeight, nscoord aAvailWidth) { - nscoord specifiedHeight = 0; - const nsStylePosition* position; + nscoord specifiedHeight = 0; // Get the height specified in the style information + const nsStylePosition* position; aCellFrame->GetStyleData(eStyleStruct_Position, (const nsStyleStruct*&)position); + + nsTableFrame* tableFrame = nsnull; + nsTableFrame::GetTableFrame(this, tableFrame); + if (!tableFrame) return NS_ERROR_NULL_POINTER; + + PRInt32 rowSpan = tableFrame->GetEffectiveRowSpan((nsTableCellFrame&)*aCellFrame); switch (position->mHeight.GetUnit()) { case eStyleUnit_Coord: specifiedHeight = position->mHeight.GetCoordValue(); + if (1 == rowSpan) + SetFixedHeight(specifiedHeight); break; case eStyleUnit_Percent: { - nsTableFrame* table = nsnull; - nsTableFrame::GetTableFrame(this, table); - if (table) { - nscoord basis = table->GetPercentBasisForRows(); - if (basis > 0) { - float percent = position->mHeight.GetPercentValue(); - specifiedHeight = NSToCoordRound(percent * ((float)basis)); - } - } + if (1 == rowSpan) + SetPctHeight(position->mHeight.GetPercentValue()); + // pct heights are handled when all of the cells are finished, so don't set specifiedHeight break; } case eStyleUnit_Inherit: - // XXX for now, do nothing case eStyleUnit_Auto: default: break; } - // If the specified height is greater than the desired height, then use the - // specified height + // If the specified height is greater than the desired height, then use the specified height if (specifiedHeight > aDesiredHeight) aDesiredHeight = specifiedHeight; @@ -865,6 +883,7 @@ nsTableRowFrame::ReflowChildren(nsIPresContext* aPresContext, PRInt32 prevColIndex = firstPrevColIndex; nscoord x = 0; // running total of children x offset + nsTableFrame* tableFirstInFlow = (nsTableFrame*)tableFrame->GetFirstInFlow(); PRBool isAutoLayout = tableFrame->IsAutoLayout(); PRBool needToNotifyTable = PR_TRUE; // Reflow each of our existing cell frames @@ -874,14 +893,21 @@ nsTableRowFrame::ReflowChildren(nsIPresContext* aPresContext, nsFrameState frameState; kidFrame->GetFrameState(&frameState); + nsCOMPtr frameType; + kidFrame->GetFrameType(getter_AddRefs(frameType)); + // See if we should only reflow the dirty child frames PRBool doReflowChild = PR_TRUE; if (aDirtyOnly && ((frameState & NS_FRAME_IS_DIRTY) == 0)) { doReflowChild = PR_FALSE; } - - nsCOMPtr frameType; - kidFrame->GetFrameType(getter_AddRefs(frameType)); + if (aReflowState.mFlags.mSpecialTableReflow) { + if (!isPaginated && (nsLayoutAtoms::tableCellFrame == frameType.get() && + !((nsTableCellFrame*)kidFrame)->NeedSpecialReflow())) { + kidFrame = iter.Next(); + continue; + } + } // Reflow the child frame if (doReflowChild) { @@ -919,7 +945,9 @@ nsTableRowFrame::ReflowChildren(nsIPresContext* aPresContext, if ((availCellWidth != cellFrame->GetPriorAvailWidth()) || (cellDesiredSize.width > cellFrame->GetPriorAvailWidth()) || (eReflowReason_StyleChange == aReflowState.reason) || - isPaginated) { + isPaginated || + (aReflowState.mFlags.mSpecialTableReflow && cellFrame->NeedSpecialReflow()) || + HasPctHeight()) { // Reflow the cell to fit the available width, height nsSize kidAvailSize(availColWidth, aReflowState.availableHeight); nsReflowReason reason = eReflowReason_Resize; @@ -1002,7 +1030,7 @@ nsTableRowFrame::ReflowChildren(nsIPresContext* aPresContext, // height may have changed, adjust descent to absorb any excess difference nscoord ascent = cellFrame->GetDesiredAscent(); nscoord descent = desiredSize.height - ascent; - SetTallestCell(desiredSize.height, ascent, descent, tableFrame, cellFrame); + UpdateHeight(desiredSize.height, ascent, descent, tableFrame, cellFrame); // Place the child if (NS_UNCONSTRAINEDSIZE != availColWidth) { @@ -1037,8 +1065,7 @@ nsTableRowFrame::ReflowChildren(nsIPresContext* aPresContext, // just set our width to what was available. The table will calculate the width and not use our value. aDesiredSize.width = aReflowState.availableWidth; - CalcTallestCell(); - aDesiredSize.height = GetTallestCell(); + aDesiredSize.height = CalcHeight(aReflowState); return rv; } @@ -1050,7 +1077,7 @@ NS_METHOD nsTableRowFrame::IncrementalReflow(nsIPresContext* aPresConte nsReflowStatus& aStatus) { nsresult rv = NS_OK; - CalcTallestCell(); // need to recalculate it based on last reflow sizes + CalcHeight(aReflowState); // need to recalculate it based on last reflow sizes // determine if this frame is the target or not nsIFrame* target = nsnull; @@ -1193,7 +1220,7 @@ nsTableRowFrame::IR_TargetIsChild(nsIPresContext* aPresContext, if (!hasVerticalAlignBaseline) { // only the height matters tallestCellGotShorter = - TallestCellGotShorter(oldCellDesSize.height, cellMet.height, mTallestCell); + TallestCellGotShorter(oldCellDesSize.height, cellMet.height, GetHeight()); } else { // the ascent matters @@ -1209,10 +1236,10 @@ nsTableRowFrame::IR_TargetIsChild(nsIPresContext* aPresContext, } } if (tallestCellGotShorter) { - CalcTallestCell(); + CalcHeight(aReflowState); } else { - SetTallestCell(cellMet.height, cellMet.ascent, cellMet.descent, &aTableFrame, cellFrame); + UpdateHeight(cellMet.height, cellMet.ascent, cellMet.descent, &aTableFrame, cellFrame); } // if the cell's desired size didn't changed, our height is unchanged @@ -1232,7 +1259,7 @@ nsTableRowFrame::IR_TargetIsChild(nsIPresContext* aPresContext, } } } - aDesiredSize.height = (aDesiredSize.mNothingChanged) ? mRect.height : GetTallestCell(); + aDesiredSize.height = (aDesiredSize.mNothingChanged) ? mRect.height : GetHeight(); if (1 == rowSpan) { cellMet.height = aDesiredSize.height; } @@ -1308,25 +1335,19 @@ nsTableRowFrame::Reflow(nsIPresContext* aPresContext, rv = nsTableFrame::GetTableFrame(this, tableFrame); if (!tableFrame) return NS_ERROR_NULL_POINTER; + if ((NS_UNCONSTRAINEDSIZE != aReflowState.availableWidth) && !mPrevInFlow) { + // see if an extra reflow will be necessary when there is a pct height but no height on the parent + if ( ((NS_UNCONSTRAINEDSIZE == aReflowState.mComputedHeight) || + (0 == aReflowState.mComputedHeight)) && + nsTableFrame::IsPctHeight(mStyleContext)) { + nsTableFrame::NotifyAncestorsOfSpecialReflow(aReflowState); + SetNeedSpecialReflow(PR_TRUE); + } + } + switch (aReflowState.reason) { case eReflowReason_Initial: rv = ReflowChildren(aPresContext, aDesiredSize, aReflowState, *tableFrame, aStatus, PR_FALSE); -#ifdef WHY - if (!tableFrame->IsAutoLayout()) { - // this resize reflow is necessary to place the cells correctly in the case of rowspans and colspans. - // It is very efficient. It does not actually need to pass a reflow down to the cells. - nsSize availSpace(aReflowState.availableWidth, aReflowState.availableHeight); - nsHTMLReflowState resizeReflowState(aPresContext, - (const nsHTMLReflowState&)(*(aReflowState.parentReflowState)), - (nsIFrame *)this, - availSpace, - eReflowReason_Resize); - RowReflowState rowResizeReflowState(resizeReflowState, tableFrame); - rv = ReflowChildren(aPresContext, aDesiredSize, rowResizeReflowState, aStatus); - } -#endif - //GetMinRowSpan(tableFrame); - //FixMinCellHeight(tableFrame); aStatus = NS_FRAME_COMPLETE; break; @@ -1343,6 +1364,10 @@ nsTableRowFrame::Reflow(nsIPresContext* aPresContext, // just set our width to what was available. The table will calculate the width and not use our value. aDesiredSize.width = aReflowState.availableWidth; + if (aReflowState.mFlags.mSpecialTableReflow) { + SetNeedSpecialReflow(PR_FALSE); + } + #if defined DEBUG_TABLE_REFLOW_TIMING nsTableFrame::DebugReflow(this, (nsHTMLReflowState&)aReflowState, &aDesiredSize, aStatus); #endif @@ -1463,6 +1488,21 @@ nsTableRowFrame::GetFrameType(nsIAtom** aType) const return NS_OK; } +nsTableRowFrame* +nsTableRowFrame::GetNextRow() const +{ + nsIFrame* childFrame; + GetNextSibling(&childFrame); + while (childFrame) { + nsCOMPtr frameType; + childFrame->GetFrameType(getter_AddRefs(frameType)); + if (nsLayoutAtoms::tableRowFrame == frameType.get()) { + return (nsTableRowFrame*)childFrame; + } + childFrame->GetNextSibling(&childFrame); + } + return nsnull; +} /* ----- global methods ----- */ diff --git a/mozilla/layout/tables/nsTableRowFrame.h b/mozilla/layout/tables/nsTableRowFrame.h index 05d6467479a..a697d4e0902 100644 --- a/mozilla/layout/tables/nsTableRowFrame.h +++ b/mozilla/layout/tables/nsTableRowFrame.h @@ -47,7 +47,7 @@ class nsTableCellFrame; class nsReflowTimer; #endif -#define NS_TABLE_MAX_ROW_INDEX (1<<19) +#define NS_ROW_NEED_SPECIAL_REFLOW 0x20000000 #define NS_ROW_FRAME_PAINT_SKIP_ROW 0x00000001 #define NS_ROW_FRAME_PAINT_SKIP_CELLS 0x00000002 @@ -121,6 +121,8 @@ public: nsFramePaintLayer aWhichLayer, nsIFrame** aFrame); + nsTableCellFrame* GetFirstCell() ; + /** calls Reflow for all of its child cells. * Cells with rowspan=1 are all set to the same height and stacked horizontally. *

Cells are not split unless absolutely necessary. @@ -155,19 +157,17 @@ public: PRUint32* aResult) const; #endif - void SetTallestCell(nscoord aHeight, - nscoord aAscent, - nscoord aDescent, - nsTableFrame* aTableFrame = nsnull, - nsTableCellFrame* aCellFrame = nsnull); + void UpdateHeight(nscoord aHeight, + nscoord aAscent, + nscoord aDescent, + nsTableFrame* aTableFrame = nsnull, + nsTableCellFrame* aCellFrame = nsnull); - void ResetTallestCell(nscoord aRowStyleHeight); + void ResetHeight(nscoord aRowStyleHeight); - // calculate the tallest child when the previous tallest child gets shorter - void CalcTallestCell(); - - /** returns the tallest child in this row (ignoring any cell with rowspans) */ - nscoord GetTallestCell() const; + // calculate the height, considering content height of the + // cells and the style height of the row and cells, excluding pct heights + nscoord CalcHeight(const nsHTMLReflowState& aReflowState); // Support for cells with 'vertical-align: baseline'. @@ -210,14 +210,38 @@ public: void RemoveCellFrame(nsTableCellFrame* aFrame); - nsresult CalculateCellActualSize(nsIFrame* aRowFrame, - nscoord& aDesiredWidth, - nscoord& aDesiredHeight, - nscoord aAvailWidth); + nsresult CalculateCellActualSize(nsIFrame* aRowFrame, + nscoord& aDesiredWidth, + nscoord& aDesiredHeight, + nscoord aAvailWidth); PRBool IsFirstInserted() const; void SetFirstInserted(PRBool aValue); + PRBool NeedSpecialReflow() const; + void SetNeedSpecialReflow(PRBool aValue); + + PRBool GetContentHeight() const; + void SetContentHeight(nscoord aTwipValue); + + PRBool HasStyleHeight() const; + + PRBool HasFixedHeight() const; + void SetHasFixedHeight(PRBool aValue); + + PRBool HasPctHeight() const; + void SetHasPctHeight(PRBool aValue); + + nscoord GetFixedHeight() const; + void SetFixedHeight(nscoord aValue); + + float GetPctHeight() const; + void SetPctHeight(float aPctValue); + + nscoord GetHeight(nscoord aBasis = 0) const; + + nsTableRowFrame* GetNextRow() const; + protected: /** protected constructor. @@ -262,10 +286,6 @@ protected: // row-specific methods - void GetMinRowSpan(nsTableFrame *aTableFrame); - - void FixMinCellHeight(nsTableFrame *aTableFrame); - nscoord ComputeCellXOffset(const nsHTMLReflowState& aState, nsIFrame* aKidFrame, const nsMargin& aKidMargin) const; @@ -280,19 +300,18 @@ protected: nsReflowStatus& aStatus, PRBool aDirtyOnly = PR_FALSE); -public: - struct RowBits { - int mRowIndex:20; - unsigned mMinRowSpan:11; // the smallest row span among all my child cells - unsigned mFirstInserted; // if true, then it was the top most newly inserted row - }; - private: - union { - PRUint32 mAllBits; - RowBits mBits; - }; - nscoord mTallestCell; // not my height, but the height of my tallest child + struct RowBits { + unsigned mRowIndex:29; + unsigned mHasFixedHeight:1; // set if the dominating style height on the row or any cell is pixel based + unsigned mHasPctHeight:1; // set if the dominating style height on the row or any cell is pct based + unsigned mFirstInserted:1; // if true, then it was the top most newly inserted row + } mBits; + + nscoord mContentHeight; // the desired height based on the content of the tallest cell in the row + nscoord mStyleHeight; // the height based on a style pct on either the row or any cell if mHasPctHeight + // is set, otherwise the height based on a style pixel height on the row or any + // cell if mHasFixedHeight is set // max-ascent and max-descent amongst all cells that have 'vertical-align: baseline' nscoord mMaxCellAscent; // does include cells with rowspan > 1 @@ -311,7 +330,6 @@ inline PRInt32 nsTableRowFrame::GetRowIndex() const inline void nsTableRowFrame::SetRowIndex (int aRowIndex) { - NS_PRECONDITION(aRowIndex < NS_TABLE_MAX_ROW_INDEX, "unexpected row index"); mBits.mRowIndex = aRowIndex; } @@ -325,4 +343,68 @@ inline void nsTableRowFrame::SetFirstInserted(PRBool aValue) mBits.mFirstInserted = aValue; } +inline PRBool nsTableRowFrame::HasStyleHeight() const +{ + return (PRBool)mBits.mHasFixedHeight || (PRBool)mBits.mHasPctHeight; +} + +inline PRBool nsTableRowFrame::HasFixedHeight() const +{ + return (PRBool)mBits.mHasFixedHeight; +} + +inline void nsTableRowFrame::SetHasFixedHeight(PRBool aValue) +{ + mBits.mHasFixedHeight = aValue; +} + +inline PRBool nsTableRowFrame::HasPctHeight() const +{ + return (PRBool)mBits.mHasPctHeight; +} + +inline void nsTableRowFrame::SetHasPctHeight(PRBool aValue) +{ + mBits.mHasPctHeight = aValue; +} + +inline nscoord nsTableRowFrame::GetContentHeight() const +{ + return mContentHeight; +} + +inline void nsTableRowFrame::SetContentHeight(nscoord aValue) +{ + mContentHeight = aValue; +} + +inline nscoord nsTableRowFrame::GetFixedHeight() const +{ + if (mBits.mHasFixedHeight && !mBits.mHasPctHeight) + return mStyleHeight; + else + return 0; +} + +inline float nsTableRowFrame::GetPctHeight() const +{ + if (mBits.mHasPctHeight) + return (float)mStyleHeight / 100.0f; + else + return 0.0f; +} + +inline PRBool nsTableRowFrame::NeedSpecialReflow() const +{ + return (mState & NS_ROW_NEED_SPECIAL_REFLOW) == NS_ROW_NEED_SPECIAL_REFLOW; +} + +inline void nsTableRowFrame::SetNeedSpecialReflow(PRBool aValue) +{ + if (aValue) { + mState |= NS_ROW_NEED_SPECIAL_REFLOW; + } else { + mState &= ~NS_ROW_NEED_SPECIAL_REFLOW; + } +} #endif diff --git a/mozilla/layout/tables/nsTableRowGroupFrame.cpp b/mozilla/layout/tables/nsTableRowGroupFrame.cpp index f863ef3047a..fca38c08835 100644 --- a/mozilla/layout/tables/nsTableRowGroupFrame.cpp +++ b/mozilla/layout/tables/nsTableRowGroupFrame.cpp @@ -105,6 +105,13 @@ nsTableRowGroupFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr) } } +NS_IMETHODIMP +nsTableRowGroupFrame::IsPercentageBase(PRBool& aBase) const +{ + aBase = PR_TRUE; + return NS_OK; +} + PRInt32 nsTableRowGroupFrame::GetRowCount() { @@ -362,6 +369,9 @@ nsTableRowGroupFrame::ReflowChildren(nsIPresContext* aPresContext, nscoord cellSpacingY = tableFrame->GetCellSpacingY(); + PRBool isPaginated; + aPresContext->IsPaginated(&isPaginated); + if (aFirstRowReflowed) { *aFirstRowReflowed = nsnull; } @@ -369,8 +379,11 @@ nsTableRowGroupFrame::ReflowChildren(nsIPresContext* aPresContext, PRBool adjustSiblings = PR_TRUE; nsIFrame* kidFrame = (aStartFrame) ? aStartFrame : mFrames.FirstChild(); - for ( ; kidFrame; ) { + nsTableFrame* tableFirstInFlow = (nsTableFrame*)tableFrame->GetFirstInFlow(); + for ( ; kidFrame; kidFrame->GetNextSibling(&kidFrame)) { // Get the frame state bits + nsCOMPtr kidType; + kidFrame->GetFrameType(getter_AddRefs(kidType)); nsFrameState frameState; kidFrame->GetFrameState(&frameState); @@ -379,6 +392,12 @@ nsTableRowGroupFrame::ReflowChildren(nsIPresContext* aPresContext, if (aDirtyOnly && ((frameState & NS_FRAME_IS_DIRTY) == 0)) { doReflowChild = PR_FALSE; } + if (aReflowState.reflowState.mFlags.mSpecialTableReflow) { + if (!isPaginated && (nsLayoutAtoms::tableRowFrame == kidType.get() && + !((nsTableRowFrame*)kidFrame)->NeedSpecialReflow())) { + doReflowChild = PR_FALSE; + } + } // Reflow the row frame if (doReflowChild) { @@ -413,9 +432,7 @@ nsTableRowGroupFrame::ReflowChildren(nsIPresContext* aPresContext, lastReflowedRow = kidFrame; if (aFirstRowReflowed && !*aFirstRowReflowed) { - nsCOMPtr fType; - kidFrame->GetFrameType(getter_AddRefs(fType)); - if (nsLayoutAtoms::tableRowFrame == fType.get()) { + if (nsLayoutAtoms::tableRowFrame == kidType.get()) { *aFirstRowReflowed = (nsTableRowFrame*)kidFrame; } } @@ -432,8 +449,6 @@ nsTableRowGroupFrame::ReflowChildren(nsIPresContext* aPresContext, kidFrame->GetSize(kidSize); aReflowState.y += kidSize.height + cellSpacingY; } - - kidFrame->GetNextSibling(&kidFrame); // Get the next child } // adjust the rows after the ones that were reflowed @@ -454,56 +469,82 @@ nsTableRowGroupFrame::ReflowChildren(nsIPresContext* aPresContext, return rv; } -void -nsTableRowGroupFrame::GetNextRowSibling(nsIFrame** aRowFrame) +nsTableRowFrame* +nsTableRowGroupFrame::GetFirstRow() { - if (!*aRowFrame) return; - GetNextFrame(*aRowFrame, aRowFrame); - while(*aRowFrame) { - const nsStyleDisplay *display; - (*aRowFrame)->GetStyleData(eStyleStruct_Display, ((const nsStyleStruct *&)display)); - if (NS_STYLE_DISPLAY_TABLE_ROW == display->mDisplay) { - return; + nsIFrame* childFrame = GetFirstFrame(); + while (childFrame) { + nsCOMPtr frameType; + childFrame->GetFrameType(getter_AddRefs(frameType)); + if (nsLayoutAtoms::tableRowFrame == frameType.get()) { + return (nsTableRowFrame*)childFrame; } - GetNextFrame(*aRowFrame, aRowFrame); + childFrame->GetNextSibling(&childFrame); + } + return nsnull; +} + + +struct RowInfo { + unsigned height:30; + unsigned hasStyleHeight:1; + unsigned isSpecial:1; // there is no cell originating in the row with rowspan=1 and there are at + // least 2 cells spanning the row and there is no style height on the row +}; + +static void +UpdateHeights(RowInfo& aRowInfo, + nscoord aAdditionalHeight, + nscoord& aTotal, + nscoord& aUnconstrainedTotal) +{ + aRowInfo.height += aAdditionalHeight; + aTotal += aAdditionalHeight; + if (!aRowInfo.hasStyleHeight) { + aUnconstrainedTotal += aAdditionalHeight; } } -// allocate the height of rows which have no cells originating in them -// except with cells with rowspan > 1. Store the height as negative -// to distinguish them from regular rows. void -AllocateSpecialHeight(nsIPresContext* aPresContext, - nsTableFrame* aTableFrame, - nsIFrame* aRowFrame, - nscoord& aHeight) +nsTableRowGroupFrame::DidResizeRows(nsIPresContext& aPresContext, + const nsHTMLReflowState& aReflowState, + nsTableRowFrame* aStartRowFrameIn) { - nsIFrame* cellFrame; - aRowFrame->FirstChild(aPresContext, nsnull, &cellFrame); - while (cellFrame) { - nsCOMPtr cellType; - cellFrame->GetFrameType(getter_AddRefs(cellType)); - if (nsLayoutAtoms::tableCellFrame == cellType.get()) { - PRInt32 rowSpan = aTableFrame->GetEffectiveRowSpan((nsTableCellFrame&)*cellFrame); - if (rowSpan > 1) { - // use a simple average to allocate the special row. This is not exact, - // but much better than nothing. - nsSize cellDesSize = ((nsTableCellFrame*)cellFrame)->GetDesiredSize(); - ((nsTableRowFrame*)aRowFrame)->CalculateCellActualSize(cellFrame, cellDesSize.width, - cellDesSize.height, cellDesSize.width); - PRInt32 propHeight = NSToCoordRound((float)cellDesSize.height / (float)rowSpan); - // special rows store the largest negative value - aHeight = PR_MIN(aHeight, -propHeight); - } - } - cellFrame->GetNextSibling(&cellFrame); + // update the cells spanning rows with their new heights + // this is the place where all of the cells in the row get set to the height of the row + PRInt32 rowIndex; + nsTableRowFrame* rowFrame; + nsTableRowFrame* startRowFrame = (aStartRowFrameIn) ? aStartRowFrameIn: GetFirstRow(); + for (rowFrame = startRowFrame, rowIndex = 0; rowFrame; rowFrame = rowFrame->GetNextRow(), rowIndex++) { + rowFrame->DidResize(&aPresContext, aReflowState); } } -/* CalculateRowHeights provides default heights for all rows in the rowgroup. - * Actual row heights are ultimately determined by the table, when the table - * height attribute is factored in. - */ +static PRBool +HasMoreThanOneCell(nsTableCellMap* aCellMap, + PRInt32 aRowIndex) +{ + if (aCellMap) { + CellData* cellData; + PRInt32 colIndex = 0; + PRInt32 count = 0; + do { + cellData = aCellMap->GetCellAt(aRowIndex, colIndex); + if (cellData && (cellData->GetCellFrame() || cellData->IsRowSpan())) + count++; + if (count > 1) + return PR_TRUE; + colIndex++; + } while(cellData); + } + return PR_FALSE; +} + +// This calculates the height of rows starting at aStartRowFrameIn and takes into account +// style height on the row group, style heights on rows and cells, style heights on rowspans. +// Actual row heights will be adjusted later if the table has a style height. +// Even if rows don't change height, this method must be called to set the heights of each +// cell in the row to the height of its row. void nsTableRowGroupFrame::CalculateRowHeights(nsIPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, @@ -511,280 +552,259 @@ nsTableRowGroupFrame::CalculateRowHeights(nsIPresContext* aPresContext, nsTableRowFrame* aStartRowFrameIn) { nsTableFrame* tableFrame = nsnull; - nsresult rv = nsTableFrame::GetTableFrame(this, tableFrame); - if (NS_FAILED(rv) || nsnull==tableFrame) return; + nsTableFrame::GetTableFrame(this, tableFrame); + if (!aPresContext || !tableFrame) return; // all table cells have the same top and bottom margins, namely cellSpacingY nscoord cellSpacingY = tableFrame->GetCellSpacingY(); + float p2t; + aPresContext->GetPixelsToTwips(&p2t); - // find the nearest row to the starting row (including the starting row) that isn't spanned into - nsTableRowFrame* startRowFrame = nsnull; - nsIFrame* childFrame = GetFirstFrame(); - PRBool foundFirstRow = PR_FALSE; - while (childFrame) { - nsCOMPtr frameType; - childFrame->GetFrameType(getter_AddRefs(frameType)); - if (nsLayoutAtoms::tableRowFrame == frameType.get()) { - PRInt32 rowIndex = ((nsTableRowFrame*)childFrame)->GetRowIndex(); - if (!foundFirstRow || !tableFrame->RowIsSpannedInto(rowIndex)) { - startRowFrame = (nsTableRowFrame*)childFrame; - if (!aStartRowFrameIn || (aStartRowFrameIn == startRowFrame)) break; + // find the nearest row at or before aStartRowFrameIn that isn't spanned into. + // If we have a computed height, then we can't compute the heights + // incrementally from aStartRowFrameIn, and we must start at the first row. + nsTableRowFrame* startRowFrame = GetFirstRow(); + if (aStartRowFrameIn && (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedHeight) + && (aReflowState.mComputedHeight > 0)) { + nsTableRowFrame* rowFrame = startRowFrame; + while (rowFrame) { + PRInt32 rowIndex = rowFrame->GetRowIndex(); + if (!tableFrame->RowIsSpannedInto(rowIndex)) { + startRowFrame = rowFrame; + if (aStartRowFrameIn == startRowFrame) + break; } - else if (aStartRowFrameIn == (nsTableRowFrame*)childFrame) break; - foundFirstRow = PR_TRUE; + else if (aStartRowFrameIn == rowFrame) + break; + rowFrame = rowFrame->GetNextRow(); } - childFrame->GetNextSibling(&childFrame); } if (!startRowFrame) return; + PRInt32 startRowIndex = startRowFrame->GetRowIndex(); + nsRect startRowRect; startRowFrame->GetRect(startRowRect); - nscoord rowGroupHeight = startRowRect.y; + // the current row group height is the y origin of the 1st row we are about to calculated a height for + nscoord startRowGroupHeight = startRowRect.y; + + PRInt32 numRows = GetRowCount() - (startRowFrame->GetRowIndex() - GetStartRowIndex()); + // collect the current height of each row. nscoord* rowHeights = nsnull; + RowInfo* rowInfo; + if (numRows > 0) { + rowInfo = new RowInfo[numRows]; + if (!rowInfo) return; + nsCRT::memset (rowInfo, 0, numRows*sizeof(RowInfo)); + } + else return; PRBool hasRowSpanningCell = PR_FALSE; - PRInt32 numRows = GetRowCount() - (startRowFrame->GetRowIndex() - GetStartRowIndex()); - // collect the current height of each row. rows which have 0 height because - // they have no cells originating in them without rowspans > 1, are referred to as - // special rows. The current height of a special row will be a negative number until - // it comes time to actually resize frames. - nscoord* rowHeights = nsnull; - if (numRows > 0) { - rowHeights = new nscoord[numRows]; - if (!rowHeights) return; - nsCRT::memset (rowHeights, 0, numRows*sizeof(nscoord)); - } // else - tree row groups need not have rows directly beneath them + nscoord heightOfRows = 0; + nscoord heightOfUnStyledRows = 0; + // Get the height of each row without considering rowspans. This will be the max of + // the largest desired height of each cell, the largest style height of each cell, + // the style height of the row. + nscoord pctHeightBasis = GetHeightBasis(aReflowState); + nsTableRowFrame* rowFrame; + PRInt32 rowIndex; // the index in rowInfo, not among the rows in the row group + for (rowFrame = startRowFrame, rowIndex = 0; rowFrame; rowFrame = rowFrame->GetNextRow(), rowIndex++) { + UpdateHeights(rowInfo[rowIndex], nsTableFrame::RoundToPixel(rowFrame->GetHeight(pctHeightBasis), p2t), + heightOfRows, heightOfUnStyledRows); + rowInfo[rowIndex].hasStyleHeight = rowFrame->HasStyleHeight(); - // Step 1: get the height of the tallest cell in the row and save it for - // pass 2. This height is for table cells that originate in this - // row and that don't span into the rows that follow - nsIFrame* rowFrame = startRowFrame; - PRInt32 rowIndex = 0; - - // For row groups that are split across pages, the first row frame won't - // necessarily be index 0 - PRInt32 startRowIndex = -1; - while (rowFrame) { - nsCOMPtr frameType; - rowFrame->GetFrameType(getter_AddRefs(frameType)); - if (nsLayoutAtoms::tableRowFrame == frameType.get()) { - if (startRowIndex == -1) { - startRowIndex = ((nsTableRowFrame*)rowFrame)->GetRowIndex(); - } - // get the height of the tallest cell in the row (excluding cells that span rows) - rowHeights[rowIndex] = ((nsTableRowFrame*)rowFrame)->GetTallestCell(); - - // See if a cell spans into the row. If so we'll have to do step 2 - if (!hasRowSpanningCell) { - if (tableFrame->RowIsSpannedInto(rowIndex + startRowIndex)) { - hasRowSpanningCell = PR_TRUE; + if (!rowInfo[rowIndex].hasStyleHeight) { + if (HasMoreThanOneCell(tableFrame->GetCellMap(), rowIndex)) { + rowInfo[rowIndex].isSpecial = PR_TRUE; + // iteratate the row's cell frames to see if any do not have rowspan > 1 + nsTableCellFrame* cellFrame = rowFrame->GetFirstCell(); + while (cellFrame) { + PRInt32 rowSpan = tableFrame->GetEffectiveRowSpan(rowIndex + startRowIndex, *cellFrame); + if (1 == rowSpan) { + rowInfo[rowIndex].isSpecial = PR_FALSE; + break; + } + cellFrame = cellFrame->GetNextCell(); } } - // special rows need to have some values, so they will get allocations - // later. If left at 0, they would get nothing. - if (0 == rowHeights[rowIndex]) { - AllocateSpecialHeight(aPresContext, tableFrame, rowFrame, rowHeights[rowIndex]); - } - rowIndex++; } - GetNextFrame(rowFrame, &rowFrame); // Get the next row + // See if a cell spans into the row. If so we'll have to do the next step + if (!hasRowSpanningCell) { + if (tableFrame->RowIsSpannedInto(rowIndex + startRowIndex)) { + hasRowSpanningCell = PR_TRUE; + } + } } - // Step 2: Now account for cells that span rows. A spanning cell's height is the sum of the heights of the - // rows it spans, or it's own desired height, whichever is greater. - // If the cell's desired height is the larger value, resize the rows and contained - // cells by an equal percentage of the additional space. - // We go through this loop twice. The first time, we are adjusting cell heights - // on the fly. The second time through the loop, we're ensuring that subsequent - // row-spanning cells didn't change prior calculations. Since we are guaranteed - // to have found the max height spanners the first time through, we know we only - // need two passes, not an arbitrary number. - nscoord yOrigin = rowGroupHeight; - nscoord lastCount = (hasRowSpanningCell) ? 2 : 1; - for (PRInt32 counter = 0; counter <= lastCount; counter++) { - rowFrame = startRowFrame; - rowIndex = 0; - while (rowFrame) { - nsCOMPtr rowType; - rowFrame->GetFrameType(getter_AddRefs(rowType)); - if (nsLayoutAtoms::tableRowFrame == rowType.get()) { - if (hasRowSpanningCell) { - // check this row for a cell with rowspans - nsIFrame* cellFrame; - rowFrame->FirstChild(aPresContext, nsnull, &cellFrame); - while (cellFrame) { - nsCOMPtr cellType; - cellFrame->GetFrameType(getter_AddRefs(cellType)); - if (nsLayoutAtoms::tableCellFrame == cellType.get()) { - PRInt32 rowSpan = tableFrame->GetEffectiveRowSpan(rowIndex + startRowIndex, - (nsTableCellFrame&)*cellFrame); - if (rowSpan > 1) { // found a cell with rowspan > 1, determine the height - // of the rows it spans - nscoord heightOfRowsSpanned = 0; - nscoord cellSpacingOfRowsSpanned = 0; - PRInt32 spanX; - PRBool cellsOrigInSpan = PR_FALSE; // do any cells originate in the spanned rows - for (spanX = 0; spanX < rowSpan; spanX++) { - PRInt32 rIndex = rowIndex + spanX; - if (rowHeights[rIndex] > 0) { - // don't consider negative values of special rows - heightOfRowsSpanned += rowHeights[rowIndex + spanX]; - cellsOrigInSpan = PR_TRUE; - } - if (0 != spanX) { - cellSpacingOfRowsSpanned += cellSpacingY; - } - } - nscoord availHeightOfRowsSpanned = heightOfRowsSpanned + cellSpacingOfRowsSpanned; + if (hasRowSpanningCell) { + // Get the height of cells with rowspans and allocate any extra space to the rows they span + // iteratate the child frames and process the row frames among them + for (rowFrame = startRowFrame, rowIndex = 0; rowFrame; rowFrame = rowFrame->GetNextRow(), rowIndex++) { + if (tableFrame->RowHasSpanningCells(startRowIndex + rowIndex)) { + nsTableCellFrame* cellFrame = rowFrame->GetFirstCell(); + // iteratate the row's cell frames + while (cellFrame) { + PRInt32 rowSpan = tableFrame->GetEffectiveRowSpan(rowIndex + startRowIndex, *cellFrame); + if (rowSpan > 1) { // a cell with rowspan > 1, determine the height of the rows it spans + nscoord heightOfRowsSpanned = 0; + nscoord heightOfUnStyledRowsSpanned = 0; + nscoord numSpecialRowsSpanned = 0; + nscoord cellSpacingTotal = 0; + PRInt32 spanX; + for (spanX = 0; spanX < rowSpan; spanX++) { + heightOfRowsSpanned += rowInfo[rowIndex + spanX].height; + if (!rowInfo[rowIndex + spanX].hasStyleHeight) { + heightOfUnStyledRowsSpanned += rowInfo[rowIndex + spanX].height; + } + if (0 != spanX) { + cellSpacingTotal += cellSpacingY; + } + if (rowInfo[rowIndex + spanX].isSpecial) { + numSpecialRowsSpanned++; + } + } + nscoord heightOfAreaSpanned = heightOfRowsSpanned + cellSpacingTotal; + // get the height of the cell + nsSize cellFrameSize; + cellFrame->GetSize(cellFrameSize); + nsSize cellDesSize = cellFrame->GetDesiredSize(); + rowFrame->CalculateCellActualSize(cellFrame, cellDesSize.width, + cellDesSize.height, cellDesSize.width); + cellFrameSize.height = cellDesSize.height; + if (cellFrame->HasVerticalAlignBaseline()) { + // to ensure that a spanning cell with a long descender doesn't + // collide with the next row, we need to take into account the shift + // that will be done to align the cell on the baseline of the row. + cellFrameSize.height += rowFrame->GetMaxCellAscent() - cellFrame->GetDesiredAscent(); + } - // see if the cell's height fits within the rows it spans. If this is - // pass 1 then use the cell's desired height and not the current height - // of its frame. That way this works for incremental reflow, too - nsSize cellFrameSize; - cellFrame->GetSize(cellFrameSize); - if (0 == counter) { - nsSize cellDesSize = ((nsTableCellFrame*)cellFrame)->GetDesiredSize(); - ((nsTableRowFrame*)rowFrame)->CalculateCellActualSize(cellFrame, cellDesSize.width, - cellDesSize.height, cellDesSize.width); - cellFrameSize.height = cellDesSize.height; - // see if the cell has 'vertical-align: baseline' - if (((nsTableCellFrame*)cellFrame)->HasVerticalAlignBaseline()) { - // to ensure that a spanning cell with a long descender doesn't - // collide with the next row, we need to take into account the shift - // that will be done to align the cell on the baseline of the row. - cellFrameSize.height += ((nsTableRowFrame*)rowFrame)->GetMaxCellAscent() - - ((nsTableCellFrame*)cellFrame)->GetDesiredAscent(); - } - } - - if (availHeightOfRowsSpanned >= cellFrameSize.height) { - // the cell's height fits with the available space of the rows it - // spans. Set the cell frame's height - cellFrame->SizeTo(aPresContext, cellFrameSize.width, availHeightOfRowsSpanned); - // Realign cell content based on new height - ((nsTableCellFrame*)cellFrame)->VerticallyAlignChild(aPresContext, aReflowState, ((nsTableRowFrame*)rowFrame)->GetMaxCellAscent()); - } - else { - // the cell's height is larger than the available space of the rows it - // spans so distribute the excess height to the rows affected - nscoord excessAvail = cellFrameSize.height - availHeightOfRowsSpanned; - nscoord excessBasis = excessAvail; - - nsTableRowFrame* rowFrameToBeResized = (nsTableRowFrame *)rowFrame; - // iterate every row starting at last row spanned and up to the row with - // the spanning cell. do this bottom up so that special rows can get a full - // allocation before other rows. - PRInt32 beginRowIndex = rowIndex + rowSpan - 1; - for (PRInt32 rowX = beginRowIndex; (rowX >= rowIndex) && (excessAvail > 0); rowX--) { - nscoord excessForRow = 0; - // special rows gets as much as they can - if (rowHeights[rowX] <= 0) { - if ((rowX == beginRowIndex) || (!cellsOrigInSpan)) { - if (0 == rowHeights[rowX]) { - // give it all since no cell originates in the row - excessForRow = excessBasis; - } - else { // don't let the allocation excced what it needs - excessForRow = (excessBasis > -rowHeights[rowX]) ? -rowHeights[rowX] : excessBasis; - } - rowHeights[rowX] = excessForRow; - excessBasis -= excessForRow; - excessAvail -= excessForRow; + if (heightOfAreaSpanned < cellFrameSize.height) { + // the cell's height is larger than the available space of the rows it + // spans so distribute the excess height to the rows affected + nscoord extra = cellFrameSize.height - heightOfAreaSpanned; + nscoord extraUsed = 0; + if (0 == numSpecialRowsSpanned) { + //NS_ASSERTION(heightOfRowsSpanned > 0, "invalid row span situation"); + PRBool haveUnStyledRowsSpanned = (heightOfUnStyledRowsSpanned > 0); + nscoord divisor = (haveUnStyledRowsSpanned) + ? heightOfUnStyledRowsSpanned : heightOfRowsSpanned; + if (divisor > 0) { + for (spanX = rowSpan - 1; spanX >= 0; spanX--) { + if (!haveUnStyledRowsSpanned || !rowInfo[rowIndex + spanX].hasStyleHeight) { + // The amount of additional space each row gets is proportional to its height + float percent = ((float)rowInfo[rowIndex + spanX].height) / ((float)divisor); + + // give rows their percentage, except for the first row which gets the remainder + nscoord extraForRow = (0 == spanX) ? extra - extraUsed + : NSToCoordRound(((float)(extra)) * percent); + extraForRow = PR_MIN(nsTableFrame::RoundToPixel(extraForRow, p2t), extra - extraUsed); + // update the row height + UpdateHeights(rowInfo[rowIndex + spanX], extraForRow, heightOfRows, heightOfUnStyledRows); + extraUsed += extraForRow; + if (extraUsed >= extra) { + NS_ASSERTION((extraUsed == extra), "invalid row height calculation"); + break; } } - else if (cellsOrigInSpan) { // normal rows - // The amount of additional space each normal row gets is based on the - // percentage of space it occupies, i.e. they don't all get the - // same amount of available space - float percent = ((float)rowHeights[rowX]) / ((float)heightOfRowsSpanned); - - // give rows their percentage, except for the first row which gets - // the remainder - excessForRow = (rowX == rowIndex) - ? excessAvail - : NSToCoordRound(((float)(excessBasis)) * percent); - // update the row height - rowHeights[rowX] += excessForRow; - excessAvail -= excessForRow; - } - // Get the next row frame - GetNextRowSibling((nsIFrame**)&rowFrameToBeResized); } - // if excessAvail is > 0 it is because !cellsOrigInSpan and the - // allocation involving special rows couldn't allocate everything. - // just give the remainder to the last row spanned. - if (excessAvail > 0) { - if (rowHeights[beginRowIndex] >= 0) { - rowHeights[beginRowIndex] += excessAvail; - } - else { - rowHeights[beginRowIndex] = excessAvail; + } + else { + // put everything in the last row + UpdateHeights(rowInfo[rowIndex + rowSpan - 1], extra, heightOfRows, heightOfUnStyledRows); + } + } + else { + // give the extra to the special rows + nscoord numSpecialRowsAllocated = 0; + for (spanX = rowSpan - 1; spanX >= 0; spanX--) { + if (rowInfo[rowIndex + spanX].isSpecial) { + // The amount of additional space each degenerate row gets is proportional to the number of them + float percent = 1.0f / ((float)numSpecialRowsSpanned); + + // give rows their percentage, except for the first row which gets the remainder + nscoord extraForRow = (numSpecialRowsSpanned - 1 == numSpecialRowsAllocated) + ? extra - extraUsed + : NSToCoordRound(((float)(extra)) * percent); + extraForRow = PR_MIN(nsTableFrame::RoundToPixel(extraForRow, p2t), extra - extraUsed); + // update the row height + UpdateHeights(rowInfo[rowIndex + spanX], extraForRow, heightOfRows, heightOfUnStyledRows); + extraUsed += extraForRow; + if (extraUsed >= extra) { + NS_ASSERTION((extraUsed == extra), "invalid row height calculation"); + break; } } } } - } - cellFrame->GetNextSibling(&cellFrame); // Get the next row child (cell frame) - } - } - - // If this is the last pass then resize the row to its final size and move the - // row's position if the previous rows have caused a shift - if (lastCount == counter) { - nsRect rowBounds; - rowFrame->GetRect(rowBounds); - - PRBool movedFrame = (rowBounds.y != yOrigin); - nscoord rowHeight = (rowHeights[rowIndex] > 0) ? rowHeights[rowIndex] : 0; - - // Resize the row to its final size and position - rowBounds.y = yOrigin; - rowBounds.height = rowHeight; - rowFrame->SetRect(aPresContext, rowBounds); - - if (movedFrame) { - nsTableFrame::RePositionViews(aPresContext, rowFrame); - } - // set the origin of the next row. - yOrigin += rowHeight + cellSpacingY; - } - - rowIndex++; - } - GetNextFrame(rowFrame, &rowFrame); // Get the next rowgroup child (row frame) - } + } + } // if (rowSpan > 1) + cellFrame = cellFrame->GetNextCell(); + } // while (cellFrame) + } // if (tableFrame->RowHasSpanningCells(startRowIndex + rowIndex) { + } // while (rowFrame) } - // step 3: notify the rows of their new heights - rowFrame = startRowFrame; - - rowIndex = 0; - while (rowFrame) { - if (NS_UNCONSTRAINEDSIZE != aReflowState.availableWidth) { - nsCOMPtr rowType; - rowFrame->GetFrameType(getter_AddRefs(rowType)); - if (nsLayoutAtoms::tableRowFrame == rowType.get()) { - // Notify the row of the new size - ((nsTableRowFrame *)rowFrame)->DidResize(aPresContext, aReflowState); + nscoord rowGroupHeight = startRowGroupHeight + heightOfRows + ((numRows - 1) * cellSpacingY); + nscoord extraComputedHeight = 0; + // if we have a style height, allocate the extra height to unconstrained rows + if ((aReflowState.mComputedHeight > rowGroupHeight) && + (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedHeight)) { + extraComputedHeight = aReflowState.mComputedHeight - rowGroupHeight; + nscoord extraUsed = 0; + PRBool haveUnStyledRows = (heightOfUnStyledRows > 0); + nscoord divisor = (haveUnStyledRows) + ? heightOfUnStyledRows : heightOfRows; + if (divisor > 0) { + for (rowIndex = 0; rowIndex < numRows; rowIndex++) { + if (!haveUnStyledRows || !rowInfo[rowIndex].hasStyleHeight) { + // The amount of additional space each row gets is based on the + // percentage of space it occupies + float percent = ((float)rowInfo[rowIndex].height) / ((float)divisor); + // give rows their percentage, except for the last row which gets the remainder + nscoord extraForRow = (numRows - 1 == rowIndex) + ? extraComputedHeight - extraUsed + : NSToCoordRound(((float)extraComputedHeight) * percent); + extraForRow = PR_MIN(nsTableFrame::RoundToPixel(extraForRow, p2t), extraComputedHeight - extraUsed); + // update the row height + UpdateHeights(rowInfo[rowIndex], extraForRow, heightOfRows, heightOfUnStyledRows); + extraUsed += extraForRow; + if (extraUsed >= extraComputedHeight) { + NS_ASSERTION((extraUsed == extraComputedHeight), "invalid row height calculation"); + break; + } + } } } - - // Update the running row group height. The height includes frames that - // aren't rows as well - nsSize rowSize; - rowFrame->GetSize(rowSize); - rowGroupHeight += rowSize.height; - if (0 != rowIndex) { - rowGroupHeight += cellSpacingY; - } - - GetNextFrame(rowFrame, &rowFrame); // Get the next row - rowIndex++; + rowGroupHeight = aReflowState.mComputedHeight; } + nscoord yOrigin = startRowGroupHeight; + // update the rows with their (potentially) new heights + for (rowFrame = startRowFrame, rowIndex = 0; rowFrame; rowFrame = rowFrame->GetNextRow(), rowIndex++) { + nsRect rowBounds; + rowFrame->GetRect(rowBounds); + + PRBool movedFrame = (rowBounds.y != yOrigin); + nscoord rowHeight = (rowInfo[rowIndex].height > 0) ? rowInfo[rowIndex].height : 0; + + if (movedFrame || (rowHeight != rowBounds.height)) { + // Resize the row to its final size and position + rowBounds.y = yOrigin; + rowBounds.height = rowHeight; + rowFrame->SetRect(aPresContext, rowBounds); + } + if (movedFrame) { + nsTableFrame::RePositionViews(aPresContext, rowFrame); + } + yOrigin += rowHeight + cellSpacingY; + } + + DidResizeRows(*aPresContext, aReflowState, startRowFrame); + aDesiredSize.height = rowGroupHeight; // Adjust our desired size - delete [] rowHeights; // cleanup + delete [] rowInfo; // cleanup } + // Called by IR_TargetIsChild() to adjust the sibling frames that follow // after an incremental reflow of aKidFrame. // This function is not used for paginated mode so we don't need to deal @@ -918,31 +938,6 @@ nsTableRowGroupFrame::SplitSpanningCells(nsIPresContext& aPresContext, return tallestCell; } -nsIFrame* GetNextRow(nsIFrame& aFrame) -{ - nsIFrame* rowFrame; - for (aFrame.GetNextSibling(&rowFrame); rowFrame; rowFrame->GetNextSibling(&rowFrame)) { - nsCOMPtr fType; - rowFrame->GetFrameType(getter_AddRefs(fType)); - if (nsLayoutAtoms::tableRowFrame == fType.get()) { - return rowFrame; - } - } - return nsnull; -} - -nsIFrame* GetFirstRow(nsTableRowGroupFrame& aRowGroupFrame) -{ - for (nsIFrame* rowFrame = aRowGroupFrame.GetFirstFrame(); rowFrame; rowFrame = GetNextRow(*rowFrame)) { - nsCOMPtr fType; - rowFrame->GetFrameType(getter_AddRefs(fType)); - if (nsLayoutAtoms::tableRowFrame == fType.get()) { - return rowFrame; - } - } - return nsnull; -} - nsresult nsTableRowGroupFrame::SplitRowGroup(nsIPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, @@ -971,7 +966,7 @@ nsTableRowGroupFrame::SplitRowGroup(nsIPresContext* aPresContext, // Walk each of the row frames looking for the first row frame that // doesn't fit in the available space - for (nsIFrame* rowFrame = GetFirstRow(*this); rowFrame; rowFrame = GetNextRow(*rowFrame)) { + for (nsTableRowFrame* rowFrame = GetFirstRow(); rowFrame; rowFrame = rowFrame->GetNextRow()) { PRBool rowIsOnCurrentPage = PR_TRUE; PRBool degenerateRow = PR_FALSE; nsRect bounds; @@ -995,7 +990,7 @@ nsTableRowGroupFrame::SplitRowGroup(nsIPresContext* aPresContext, 0, 0, NS_FRAME_NO_MOVE_FRAME, aStatus); rowFrame->SizeTo(aPresContext, desiredSize.width, desiredSize.height); rowFrame->DidReflow(aPresContext, NS_FRAME_REFLOW_FINISHED); - ((nsTableRowFrame *)rowFrame)->DidResize(aPresContext, aReflowState); + rowFrame->DidResize(aPresContext, aReflowState); if (NS_FRAME_IS_NOT_COMPLETE(aStatus)) { // the row frame is incomplete and all of the cells' block frames have split @@ -1043,7 +1038,7 @@ nsTableRowGroupFrame::SplitRowGroup(nsIPresContext* aPresContext, if (prevRowFrame) { nscoord tallestCell = SplitSpanningCells(*aPresContext, aReflowState, *styleSet, *aTableFrame, - *(nsTableRowFrame*)rowFrame, aDesiredSize.height, (nsTableRowFrame*)contRowFrame); + *rowFrame, aDesiredSize.height, (nsTableRowFrame*)contRowFrame); if (degenerateRow) { aDesiredSize.height = lastDesiredHeight + aTableFrame->GetCellSpacingY() + tallestCell; } @@ -1077,10 +1072,10 @@ nsTableRowGroupFrame::Reflow(nsIPresContext* aPresContext, nsresult rv=NS_OK; aStatus = NS_FRAME_COMPLETE; - + nsTableFrame* tableFrame = nsnull; rv = nsTableFrame::GetTableFrame(this, tableFrame); - if (!tableFrame) return NS_ERROR_NULL_POINTER; + if (!aPresContext || !tableFrame) return NS_ERROR_NULL_POINTER; nsRowGroupReflowState state(aReflowState, tableFrame); PRBool haveDesiredHeight = PR_FALSE; @@ -1111,9 +1106,12 @@ nsTableRowGroupFrame::Reflow(nsIPresContext* aPresContext, // reflow, then we need to do this because the table will skip the pass 2 reflow, // but we need to correctly calculate the row group height and we can't if there // are row spans unless we do this step - if ((eReflowReason_Initial != aReflowState.reason) || - isTableUnconstrainedReflow || - isPaginated) { + if (aReflowState.mFlags.mSpecialTableReflow) { + DidResizeRows(*aPresContext, aReflowState); + } + else if ((eReflowReason_Initial != aReflowState.reason) || + isTableUnconstrainedReflow || + isPaginated) { CalculateRowHeights(aPresContext, aDesiredSize, aReflowState); haveDesiredHeight = PR_TRUE; } @@ -1124,6 +1122,12 @@ nsTableRowGroupFrame::Reflow(nsIPresContext* aPresContext, SplitRowGroup(aPresContext, aDesiredSize, aReflowState, tableFrame, aStatus); } } + SetHasStyleHeight((NS_UNCONSTRAINEDSIZE != aReflowState.mComputedHeight) && + (aReflowState.mComputedHeight > 0)); + + if (aReflowState.mFlags.mSpecialTableReflow) { + SetNeedSpecialReflow(PR_FALSE); + } // just set our width to what was available. The table will calculate the width and not use our value. aDesiredSize.width = aReflowState.availableWidth; @@ -1318,6 +1322,21 @@ nsTableRowGroupFrame::IR_TargetIsMe(nsIPresContext* aPresContext, return rv; } +nscoord +nsTableRowGroupFrame::GetHeightBasis(const nsHTMLReflowState& aReflowState) +{ + nscoord result = 0; + if ((aReflowState.mComputedHeight > 0) && (aReflowState.mComputedHeight < NS_UNCONSTRAINEDSIZE)) { + nsTableFrame* tableFrame = nsnull; + nsTableFrame::GetTableFrame((nsIFrame*)this, tableFrame); + if (tableFrame) { + nscoord cellSpacing = PR_MAX(0, GetRowCount() - 1) * tableFrame->GetCellSpacingY(); + result -= cellSpacing; + } + } + return result; +} + nscoord nsTableRowGroupFrame::GetHeightOfRows(nsIPresContext* aPresContext) { diff --git a/mozilla/layout/tables/nsTableRowGroupFrame.h b/mozilla/layout/tables/nsTableRowGroupFrame.h index 0b12000af33..164c7aa1f19 100644 --- a/mozilla/layout/tables/nsTableRowGroupFrame.h +++ b/mozilla/layout/tables/nsTableRowGroupFrame.h @@ -79,9 +79,13 @@ struct nsRowGroupReflowState { { 0xe940e7bc, 0xb534, 0x11d2, \ { 0x95, 0xa2, 0x0, 0x60, 0xb0, 0xc3, 0x44, 0x14 } } -// use a bit from nsFrame's frame state bits to determine whether a +// use the following bits from nsFrame's frame state + // thead or tfoot should be repeated on every printed page -#define NS_ROWGROUP_REPEATABLE 0x80000000 +#define NS_ROWGROUP_REPEATABLE 0x80000000 +#define NS_ROWGROUP_HAS_STYLE_HEIGHT 0x40000000 +// we need a 3rd pass reflow to deal with pct height nested tables +#define NS_ROWGROUP_NEED_SPECIAL_REFLOW 0x20000000 /** * nsTableRowGroupFrame is the frame that maps row groups * (HTML tags THEAD, TFOOT, and TBODY). This class cannot be reused @@ -176,6 +180,10 @@ public: */ NS_IMETHOD GetFrameType(nsIAtom** aType) const; + NS_IMETHOD IsPercentageBase(PRBool& aBase) const; + + nsTableRowFrame* GetFirstRow(); + #ifdef DEBUG NS_IMETHOD GetFrameName(nsString& aResult) const; NS_IMETHOD SizeOf(nsISizeOfHandler* aSizer, PRUint32* aResult) const; @@ -206,6 +214,7 @@ public: * Get the total height of all the row rects */ nscoord GetHeightOfRows(nsIPresContext* aPresContext); + nscoord GetHeightBasis(const nsHTMLReflowState& aReflowState); // nsILineIterator methods public: @@ -255,6 +264,9 @@ protected: const nsHTMLReflowState& aReflowState, nsTableRowFrame* aStartRowFrameIn = nsnull); + void DidResizeRows(nsIPresContext& aPresContext, + const nsHTMLReflowState& aReflowState, + nsTableRowFrame* aStartRowFrameIn = nsnull); /** Incremental Reflow attempts to do column balancing with the minimum number of reflow * commands to child elements. This is done by processing the reflow command, @@ -337,8 +349,12 @@ public: virtual nsIFrame* GetLastFrame() { return mFrames.LastChild(); }; virtual void GetNextFrame(nsIFrame* aFrame, nsIFrame** aResult) { aFrame->GetNextSibling(aResult); }; - PRBool IsRepeatable(); - void SetRepeatable(PRBool aRepeatable); + PRBool IsRepeatable() const; + void SetRepeatable(PRBool aRepeatable); + PRBool HasStyleHeight() const; + void SetHasStyleHeight(PRBool aValue); + PRBool NeedSpecialReflow() const; + void SetNeedSpecialReflow(PRBool aValue); #ifdef DEBUG_TABLE_REFLOW_TIMING public: @@ -347,7 +363,7 @@ public: }; -inline PRBool nsTableRowGroupFrame::IsRepeatable() +inline PRBool nsTableRowGroupFrame::IsRepeatable() const { return (mState & NS_ROWGROUP_REPEATABLE) == NS_ROWGROUP_REPEATABLE; } @@ -361,4 +377,31 @@ inline void nsTableRowGroupFrame::SetRepeatable(PRBool aRepeatable) } } +inline PRBool nsTableRowGroupFrame::NeedSpecialReflow() const +{ + return (mState & NS_ROWGROUP_NEED_SPECIAL_REFLOW) == NS_ROWGROUP_NEED_SPECIAL_REFLOW; +} + +inline void nsTableRowGroupFrame::SetNeedSpecialReflow(PRBool aValue) +{ + if (aValue) { + mState |= NS_ROWGROUP_NEED_SPECIAL_REFLOW; + } else { + mState &= ~NS_ROWGROUP_NEED_SPECIAL_REFLOW; + } +} + +inline PRBool nsTableRowGroupFrame::HasStyleHeight() const +{ + return (mState & NS_ROWGROUP_HAS_STYLE_HEIGHT) == NS_ROWGROUP_HAS_STYLE_HEIGHT; +} + +inline void nsTableRowGroupFrame::SetHasStyleHeight(PRBool aValue) +{ + if (aValue) { + mState |= NS_ROWGROUP_HAS_STYLE_HEIGHT; + } else { + mState &= ~NS_ROWGROUP_HAS_STYLE_HEIGHT; + } +} #endif