From 49e32902e4f83fe7860e21ba40dc8baa13674a44 Mon Sep 17 00:00:00 2001 From: Date: Fri, 8 Feb 2008 01:36:32 -0800 Subject: [PATCH] Fix bug 416073: invalidate more things as needed during table reflow to prevent glitches. r+sr=roc --- layout/base/nsLayoutUtils.cpp | 18 ++++ layout/base/nsLayoutUtils.h | 9 ++ layout/generic/nsBlockFrame.cpp | 31 ++----- layout/generic/nsContainerFrame.cpp | 5 ++ layout/generic/nsContainerFrame.h | 3 + layout/generic/nsFrame.cpp | 15 ++++ layout/generic/nsIFrame.h | 15 ++++ layout/tables/nsTableCellFrame.cpp | 16 +++- layout/tables/nsTableFrame.cpp | 110 +++++++++++++++++++++---- layout/tables/nsTableFrame.h | 17 +++- layout/tables/nsTableRowFrame.cpp | 90 ++++++++++++++++---- layout/tables/nsTableRowGroupFrame.cpp | 51 +++++++++--- layout/tables/nsTableRowGroupFrame.h | 3 +- 13 files changed, 309 insertions(+), 74 deletions(-) diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 679e9f016348..f4e0e50c494f 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -2486,3 +2486,21 @@ nsLayoutUtils::GetTextRunFlagsForStyle(nsStyleContext* aStyleContext, #endif return result; } + +/* static */ void +nsLayoutUtils::GetRectDifferenceStrips(const nsRect& aR1, const nsRect& aR2, + nsRect* aHStrip, nsRect* aVStrip) { + NS_ASSERTION(aR1.TopLeft() == aR2.TopLeft(), + "expected rects at the same position"); + nsRect unionRect(aR1.x, aR1.y, PR_MAX(aR1.width, aR2.width), + PR_MAX(aR1.height, aR2.height)); + nscoord VStripStart = PR_MIN(aR1.width, aR2.width); + nscoord HStripStart = PR_MIN(aR1.height, aR2.height); + *aVStrip = unionRect; + aVStrip->x += VStripStart; + aVStrip->width -= VStripStart; + *aHStrip = unionRect; + aHStrip->y += HStripStart; + aHStrip->height -= HStripStart; +} + diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h index 73e642e98dfb..26d4e36270e7 100644 --- a/layout/base/nsLayoutUtils.h +++ b/layout/base/nsLayoutUtils.h @@ -754,6 +754,15 @@ public: const nsStyleText* aStyleText, const nsStyleFont* aStyleFont); + /** + * Takes two rectangles whose origins must be the same, and computes + * the difference between their union and their intersection as two + * rectangles. (This difference is a superset of the difference + * between the two rectangles.) + */ + static void GetRectDifferenceStrips(const nsRect& aR1, const nsRect& aR2, + nsRect* aHStrip, nsRect* aVStrip); + /** * Indicates if the nsIFrame::GetUsedXXX assertions in nsFrame.cpp should * disabled. diff --git a/layout/generic/nsBlockFrame.cpp b/layout/generic/nsBlockFrame.cpp index 8416ccc953e3..acc4833375f0 100644 --- a/layout/generic/nsBlockFrame.cpp +++ b/layout/generic/nsBlockFrame.cpp @@ -2234,28 +2234,6 @@ nsBlockFrame::DeleteLine(nsBlockReflowState& aState, } } -/** - * Takes two rectangles whose origins must be the same, and computes - * the difference between their union and their intersection as two - * rectangles. (This difference is a superset of the difference - * between the two rectangles.) - */ -static void GetRectDifferenceStrips(const nsRect& aR1, const nsRect& aR2, - nsRect* aHStrip, nsRect* aVStrip) { - NS_ASSERTION(aR1.TopLeft() == aR2.TopLeft(), - "expected rects at the same position"); - nsRect unionRect(aR1.x, aR1.y, PR_MAX(aR1.width, aR2.width), - PR_MAX(aR1.height, aR2.height)); - nscoord VStripStart = PR_MIN(aR1.width, aR2.width); - nscoord HStripStart = PR_MIN(aR1.height, aR2.height); - *aVStrip = unionRect; - aVStrip->x += VStripStart; - aVStrip->width -= VStripStart; - *aHStrip = unionRect; - aHStrip->y += HStripStart; - aHStrip->height -= HStripStart; -} - /** * Reflow a line. The line will either contain a single block frame * or contain 1 or more inline frames. aKeepReflowGoing indicates @@ -2305,10 +2283,11 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, } else { nsRect combinedAreaHStrip, combinedAreaVStrip; nsRect boundsHStrip, boundsVStrip; - GetRectDifferenceStrips(oldBounds, newBounds, - &boundsHStrip, &boundsVStrip); - GetRectDifferenceStrips(oldCombinedArea, lineCombinedArea, - &combinedAreaHStrip, &combinedAreaVStrip); + nsLayoutUtils::GetRectDifferenceStrips(oldBounds, newBounds, + &boundsHStrip, &boundsVStrip); + nsLayoutUtils::GetRectDifferenceStrips(oldCombinedArea, lineCombinedArea, + &combinedAreaHStrip, + &combinedAreaVStrip); #ifdef NOISY_BLOCK_INVALIDATE printf("%p invalidate boundsVStrip (%d, %d, %d, %d)\n", diff --git a/layout/generic/nsContainerFrame.cpp b/layout/generic/nsContainerFrame.cpp index 7455458c60b9..13b9fd0e3088 100644 --- a/layout/generic/nsContainerFrame.cpp +++ b/layout/generic/nsContainerFrame.cpp @@ -743,6 +743,11 @@ nsContainerFrame::ReflowChild(nsIFrame* aKidFrame, aKidFrame->WillReflow(aPresContext); if (0 == (aFlags & NS_FRAME_NO_MOVE_FRAME)) { + if ((aFlags & NS_FRAME_INVALIDATE_ON_MOVE) && + !(aKidFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) && + aKidFrame->GetPosition() != nsPoint(aX, aY)) { + aKidFrame->InvalidateOverflowRect(); + } aKidFrame->SetPosition(nsPoint(aX, aY)); } diff --git a/layout/generic/nsContainerFrame.h b/layout/generic/nsContainerFrame.h index f126ca18deb3..a259cea3f32d 100644 --- a/layout/generic/nsContainerFrame.h +++ b/layout/generic/nsContainerFrame.h @@ -60,6 +60,9 @@ #define NS_FRAME_NO_MOVE_FRAME (0x0002 | NS_FRAME_NO_MOVE_VIEW) #define NS_FRAME_NO_SIZE_VIEW 0x0004 #define NS_FRAME_NO_VISIBILITY 0x0008 +// Only applies to ReflowChild: if true, invalidate the child if it's +// being moved +#define NS_FRAME_INVALIDATE_ON_MOVE 0x0010 class nsOverflowContinuationTracker; diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 834f0a81fc14..0e448782f2f5 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -3600,6 +3600,21 @@ nsIFrame::InvalidateInternal(const nsRect& aDamageRect, nscoord aX, nscoord aY, InvalidateInternal(aDamageRect, aX + mRect.x, aY + mRect.y, this, aImmediate); } +void +nsIFrame::InvalidateRectDifference(const nsRect& aR1, const nsRect& aR2) +{ + nsRect sizeHStrip, sizeVStrip; + nsLayoutUtils::GetRectDifferenceStrips(aR1, aR2, &sizeHStrip, &sizeVStrip); + Invalidate(sizeVStrip); + Invalidate(sizeHStrip); +} + +void +nsIFrame::InvalidateOverflowRect() +{ + Invalidate(GetOverflowRect()); +} + void nsIFrame::InvalidateRoot(const nsRect& aDamageRect, nscoord aX, nscoord aY, PRBool aImmediate) diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h index 9272d5114c89..842562561106 100644 --- a/layout/generic/nsIFrame.h +++ b/layout/generic/nsIFrame.h @@ -1655,6 +1655,21 @@ public: nscoord aOffsetX, nscoord aOffsetY, nsIFrame* aForChild, PRBool aImmediate); + /** + * Take two rectangles in the coordinate system of this frame which + * have the same origin and invalidate the difference between them. + * This is a helper method to be used when a frame is being resized. + * + * @param aR1 the first rectangle + * @param aR2 the second rectangle + */ + void InvalidateRectDifference(const nsRect& aR1, const nsRect& aR2); + + /** + * Invalidate the overflow rect of this frame + */ + void InvalidateOverflowRect(); + /** * Computes a rect that encompasses everything that might be painted by * this frame. This includes this frame, all its descendent frames, this diff --git a/layout/tables/nsTableCellFrame.cpp b/layout/tables/nsTableCellFrame.cpp index a99e10a5726e..e0e589d986b2 100644 --- a/layout/tables/nsTableCellFrame.cpp +++ b/layout/tables/nsTableCellFrame.cpp @@ -593,6 +593,11 @@ void nsTableCellFrame::VerticallyAlignChild(nscoord aMaxAscent) // if the content is larger than the cell height align from top kidYTop = PR_MAX(0, kidYTop); + if (kidYTop != kidRect.y) { + // Invalidate at the old position first + firstKid->InvalidateOverflowRect(); + } + firstKid->SetPosition(nsPoint(kidRect.x, kidYTop)); nsHTMLReflowMetrics desiredSize; desiredSize.width = mRect.width; @@ -604,6 +609,9 @@ void nsTableCellFrame::VerticallyAlignChild(nscoord aMaxAscent) // Make sure any child views are correctly positioned. We know the inner table // cell won't have a view nsContainerFrame::PositionChildViews(firstKid); + + // Invalidate new overflow rect + firstKid->InvalidateOverflowRect(); } if (HasView()) { nsContainerFrame::SyncFrameViewAfterReflow(PresContext(), this, @@ -861,15 +869,19 @@ NS_METHOD nsTableCellFrame::Reflow(nsPresContext* aPresContext, } nsPoint kidOrigin(leftInset, topInset); + nsRect origRect = firstKid->GetRect(); + PRBool firstReflow = (firstKid->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0; ReflowChild(firstKid, aPresContext, kidSize, kidReflowState, - kidOrigin.x, kidOrigin.y, 0, aStatus); + kidOrigin.x, kidOrigin.y, NS_FRAME_INVALIDATE_ON_MOVE, aStatus); if (NS_FRAME_OVERFLOW_IS_INCOMPLETE(aStatus)) { // Don't pass OVERFLOW_INCOMPLETE through tables until they can actually handle it //XXX should paginate overflow as overflow, but not in this patch (bug 379349) NS_FRAME_SET_INCOMPLETE(aStatus); printf("Set table cell incomplete %p\n", this); } + + // XXXbz is this invalidate actually needed, really? if (GetStateBits() & NS_FRAME_IS_DIRTY) { Invalidate(GetOverflowRect(), PR_FALSE); } @@ -885,6 +897,8 @@ NS_METHOD nsTableCellFrame::Reflow(nsPresContext* aPresContext, // Place the child FinishReflowChild(firstKid, aPresContext, &kidReflowState, kidSize, kidOrigin.x, kidOrigin.y, 0); + + nsTableFrame::InvalidateFrame(firstKid, origRect, firstReflow); // first, compute the height which can be set w/o being restricted by aMaxSize.height nscoord cellHeight = kidSize.height; diff --git a/layout/tables/nsTableFrame.cpp b/layout/tables/nsTableFrame.cpp index 4bce9f2abc8e..e944c6ba8ce2 100644 --- a/layout/tables/nsTableFrame.cpp +++ b/layout/tables/nsTableFrame.cpp @@ -1957,6 +1957,11 @@ NS_METHOD nsTableFrame::Reflow(nsPresContext* aPresContext, } } + if (GetStateBits() & NS_FRAME_FIRST_REFLOW) { + // Fulfill the promise InvalidateFrame makes. + Invalidate(aDesiredSize.mOverflowArea); + } + FinishAndStoreOverflow(&aDesiredSize); NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); return rv; @@ -2578,13 +2583,18 @@ nsTableFrame::InitChildReflowState(nsHTMLReflowState& aReflowState) // aKidRect is relative to the upper-left origin of our frame void nsTableFrame::PlaceChild(nsTableReflowState& aReflowState, nsIFrame* aKidFrame, - nsHTMLReflowMetrics& aKidDesiredSize) + nsHTMLReflowMetrics& aKidDesiredSize, + const nsRect& aOriginalKidRect) { + PRBool isFirstReflow = + (aKidFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0; // Place and size the child FinishReflowChild(aKidFrame, PresContext(), nsnull, aKidDesiredSize, aReflowState.x, aReflowState.y, 0); + InvalidateFrame(aKidFrame, aOriginalKidRect, isFirstReflow); + // Adjust the running y-offset aReflowState.y += aKidDesiredSize.height; @@ -2934,9 +2944,10 @@ nsTableFrame::ReflowChildren(nsTableReflowState& aReflowState, PRBool reorder = PR_FALSE; if (kidFrame->GetNextInFlow()) reorder = PR_TRUE; - + rv = ReflowChild(kidFrame, presContext, desiredSize, kidReflowState, - aReflowState.x, aReflowState.y, 0, aStatus); + aReflowState.x, aReflowState.y, + NS_FRAME_INVALIDATE_ON_MOVE, aStatus); if (reorder) { // reorder row groups the reflow may have changed the nextinflows @@ -2957,7 +2968,7 @@ nsTableFrame::ReflowChildren(nsTableReflowState& aReflowState, if (childX+1 < rowGroups.Length()) { nsIFrame* nextRowGroupFrame = rowGroups[childX + 1]; if (nextRowGroupFrame) { - PlaceChild(aReflowState, kidFrame, desiredSize); + PlaceChild(aReflowState, kidFrame, desiredSize, oldKidRect); aStatus = NS_FRAME_NOT_COMPLETE; PushChildren(rowGroups, childX + 1); aLastChildReflowed = kidFrame; @@ -2988,7 +2999,7 @@ nsTableFrame::ReflowChildren(nsTableReflowState& aReflowState, } // Place the child - PlaceChild(aReflowState, kidFrame, desiredSize); + PlaceChild(aReflowState, kidFrame, desiredSize, oldKidRect); // Remember where we just were in case we end up pushing children prevKidFrame = kidFrame; @@ -3036,10 +3047,14 @@ nsTableFrame::ReflowChildren(nsTableReflowState& aReflowState, -1, -1, PR_FALSE); InitChildReflowState(footerReflowState); aReflowState.y += cellSpacingY; + + nsRect origTfootRect = tfoot->GetRect(); + nsReflowStatus footerStatus; rv = ReflowChild(tfoot, presContext, desiredSize, footerReflowState, - aReflowState.x, aReflowState.y, 0, footerStatus); - PlaceChild(aReflowState, tfoot, desiredSize); + aReflowState.x, aReflowState.y, + NS_FRAME_INVALIDATE_ON_MOVE, footerStatus); + PlaceChild(aReflowState, tfoot, desiredSize, origTfootRect); } break; } @@ -3048,11 +3063,13 @@ nsTableFrame::ReflowChildren(nsTableReflowState& aReflowState, aReflowState.y += cellSpacingY; nsRect kidRect = kidFrame->GetRect(); if (kidRect.y != aReflowState.y) { - Invalidate(kidRect); // invalidate the old position + // invalidate the old position + kidFrame->InvalidateOverflowRect(); kidRect.y = aReflowState.y; kidFrame->SetRect(kidRect); // move to the new position RePositionViews(kidFrame); - Invalidate(kidRect); // invalidate the new position + // invalidate the new position + kidFrame->InvalidateOverflowRect(); } aReflowState.y += kidRect.height; @@ -3226,7 +3243,9 @@ nsTableFrame::DistributeHeightToRows(const nsHTMLReflowState& aReflowState, nscoord pctHeight = rowFrame->GetHeight(pctBasis); nscoord amountForRow = PR_MIN(aAmount - amountUsed, pctHeight - rowRect.height); if (amountForRow > 0) { + nsRect oldRowRect = rowRect; rowRect.height += amountForRow; + // XXXbz we don't need to change rowRect.y to be yOriginRow? rowFrame->SetRect(rowRect); yOriginRow += rowRect.height + cellSpacingY; yEndRG += rowRect.height + cellSpacingY; @@ -3234,12 +3253,17 @@ nsTableFrame::DistributeHeightToRows(const nsHTMLReflowState& aReflowState, amountUsedByRG += amountForRow; //rowFrame->DidResize(); nsTableFrame::RePositionViews(rowFrame); + + rgFrame->InvalidateRectDifference(oldRowRect, rowRect); } } else { - if (amountUsed > 0) { + if (amountUsed > 0 && yOriginRow != rowRect.y && + !(GetStateBits() & NS_FRAME_FIRST_REFLOW)) { + rowFrame->InvalidateOverflowRect(); rowFrame->SetPosition(nsPoint(rowRect.x, yOriginRow)); nsTableFrame::RePositionViews(rowFrame); + rowFrame->InvalidateOverflowRect(); } yOriginRow += rowRect.height + cellSpacingY; yEndRG += rowRect.height + cellSpacingY; @@ -3247,15 +3271,27 @@ nsTableFrame::DistributeHeightToRows(const nsHTMLReflowState& aReflowState, rowFrame = rowFrame->GetNextRow(); } if (amountUsed > 0) { + if (rgRect.y != yOriginRG) { + rgFrame->InvalidateOverflowRect(); + } + + nsRect origRgRect = rgRect; + rgRect.y = yOriginRG; rgRect.height += amountUsedByRG; + rgFrame->SetRect(rgRect); + + nsTableFrame::InvalidateFrame(rgFrame, origRgRect, PR_FALSE); } } - else if (amountUsed > 0) { + else if (amountUsed > 0 && yOriginRG != rgFrame->GetPosition().y) { + NS_ASSERTION(rgFrame->GetPosition().x == 0, "Unexpected position"); + rgFrame->InvalidateOverflowRect(); rgFrame->SetPosition(nsPoint(0, yOriginRG)); // Make sure child views are properly positioned nsTableFrame::RePositionViews(rgFrame); + rgFrame->InvalidateOverflowRect(); } yOriginRG = yEndRG; } @@ -3360,9 +3396,16 @@ nsTableFrame::DistributeHeightToRows(const nsHTMLReflowState& aReflowState, nscoord amountForRow = (rowFrame == lastEligibleRow) ? aAmount - amountUsed : NSToCoordRound(((float)(heightToDistribute)) * ratio); amountForRow = PR_MIN(amountForRow, aAmount - amountUsed); + + if (yOriginRow != rowRect.y) { + rowFrame->InvalidateOverflowRect(); + } + // update the row height - nsRect newRowRect(rowRect.x, yOriginRow, rowRect.width, rowRect.height + amountForRow); + nsRect newRowRect(rowRect.x, yOriginRow, rowRect.width, + rowRect.height + amountForRow); rowFrame->SetRect(newRowRect); + yOriginRow += newRowRect.height + cellSpacingY; yEndRG += newRowRect.height + cellSpacingY; @@ -3371,11 +3414,15 @@ nsTableFrame::DistributeHeightToRows(const nsHTMLReflowState& aReflowState, NS_ASSERTION((amountUsed <= aAmount), "invalid row allocation"); //rowFrame->DidResize(); nsTableFrame::RePositionViews(rowFrame); + + nsTableFrame::InvalidateFrame(rowFrame, rowRect, PR_FALSE); } else { - if (amountUsed > 0) { + if (amountUsed > 0 && yOriginRow != rowRect.y) { + rowFrame->InvalidateOverflowRect(); rowFrame->SetPosition(nsPoint(rowRect.x, yOriginRow)); nsTableFrame::RePositionViews(rowFrame); + rowFrame->InvalidateOverflowRect(); } yOriginRow += rowRect.height + cellSpacingY; yEndRG += rowRect.height + cellSpacingY; @@ -3383,17 +3430,25 @@ nsTableFrame::DistributeHeightToRows(const nsHTMLReflowState& aReflowState, rowFrame = rowFrame->GetNextRow(); } if (amountUsed > 0) { - rgRect.y = yOriginRG; - rgRect.height += amountUsedByRG; - rgFrame->SetRect(rgRect); + if (rgRect.y != yOriginRG) { + rgFrame->InvalidateOverflowRect(); + } + + rgFrame->SetRect(nsRect(rgRect.x, yOriginRG, rgRect.width, + rgRect.height + amountUsedByRG)); + + nsTableFrame::InvalidateFrame(rgFrame, rgRect, PR_FALSE); } // 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) { + else if (amountUsed > 0 && yOriginRG != rgFrame->GetPosition().y) { + NS_ASSERTION(rgFrame->GetPosition().x == 0, "Unexpected position"); + rgFrame->InvalidateOverflowRect(); rgFrame->SetPosition(nsPoint(0, yOriginRG)); // Make sure child views are properly positioned nsTableFrame::RePositionViews(rgFrame); + rgFrame->InvalidateOverflowRect(); } yOriginRG = yEndRG; } @@ -6718,6 +6773,27 @@ nsTableFrame::GetProperty(nsIFrame* aFrame, return nsnull; } +/* static */ +void +nsTableFrame::InvalidateFrame(nsIFrame* aFrame, const nsRect& aOrigRect, + PRBool aIsFirstReflow) +{ + nsIFrame* parent = aFrame->GetParent(); + NS_ASSERTION(parent, "What happened here?"); + + if (parent->GetStateBits() & NS_FRAME_FIRST_REFLOW) { + // Don't bother; we'll invalidate the parent's overflow rect when + // we finish reflowing it. + return; + } + + if (aIsFirstReflow || aOrigRect.TopLeft() != aFrame->GetPosition()) { + aFrame->InvalidateOverflowRect(); + } else { + parent->InvalidateRectDifference(aOrigRect, aFrame->GetRect()); + } +} + #ifdef DEBUG #define MAX_SIZE 128 #define MIN_INDENT 30 diff --git a/layout/tables/nsTableFrame.h b/layout/tables/nsTableFrame.h index 54846ef28d9f..7f966e4fd69b 100644 --- a/layout/tables/nsTableFrame.h +++ b/layout/tables/nsTableFrame.h @@ -480,6 +480,20 @@ public: PRBool HasCellSpanningPctCol() const; void SetHasCellSpanningPctCol(PRBool aValue); + /** + * To be called on a frame by its parent after setting its size/position and + * calling DidReflow (possibly via FinishReflowChild()). This can also be + * used for child frames which are not being reflown but did have their size + * or position changed. + * + * @param aFrame The frame to invalidate + * @param aOrigRect The original rect of aFrame (before the change). + * @param aIsFirstReflow True if the size/position change is due to the + * first reflow of aFrame. + */ + static void InvalidateFrame(nsIFrame* aFrame, const nsRect& aOrigRect, + PRBool aIsFirstReflow); + protected: /** protected constructor. @@ -567,7 +581,8 @@ protected: void PlaceChild(nsTableReflowState& aReflowState, nsIFrame* aKidFrame, - nsHTMLReflowMetrics& aKidDesiredSize); + nsHTMLReflowMetrics& aKidDesiredSize, + const nsRect& aOriginalKidRect); nsIFrame* GetFirstBodyRowGroupFrame(); PRBool MoveOverflowToChildList(nsPresContext* aPresContext); diff --git a/layout/tables/nsTableRowFrame.cpp b/layout/tables/nsTableRowFrame.cpp index b8ec70ecfea8..ecdecb9fe11d 100644 --- a/layout/tables/nsTableRowFrame.cpp +++ b/layout/tables/nsTableRowFrame.cpp @@ -353,15 +353,23 @@ nsTableRowFrame::DidResize() nscoord cellHeight = mRect.height + GetHeightOfRowsSpannedBelowFirst(*cellFrame, *tableFrame); // resize the cell's height - //if (cellFrameSize.height!=cellHeight) + nsRect cellRect = cellFrame->GetRect(); + if (cellRect.height != cellHeight) { - cellFrame->SetSize(nsSize(cellFrame->GetSize().width, cellHeight)); - // realign cell content based on the new height - cellFrame->VerticallyAlignChild(mMaxCellAscent); - ConsiderChildOverflow(desiredSize.mOverflowArea, cellFrame); - // Note that if the cell's *content* needs to change in response - // to this height, it will get a special height reflow. + cellFrame->SetSize(nsSize(cellRect.width, cellHeight)); + nsTableFrame::InvalidateFrame(cellFrame, cellRect, PR_FALSE); } + + // realign cell content based on the new height. We might be able to + // skip this if the height didn't change... maybe. Hard to tell. + cellFrame->VerticallyAlignChild(mMaxCellAscent); + + // Always store the overflow, even if the height didn't change, since + // we'll lose part of our overflow area otherwise. + ConsiderChildOverflow(desiredSize.mOverflowArea, cellFrame); + + // Note that if the cell's *content* needs to change in response + // to this height, it will get a special height reflow. } // Get the next child childFrame = iter.Next(); @@ -855,6 +863,10 @@ nsTableRowFrame::ReflowChildren(nsPresContext* aPresContext, prevColIndex = (iter.IsLeftToRight()) ? cellColIndex + (cellColSpan - 1) : cellColIndex; // Reflow the child frame + nsRect kidRect = kidFrame->GetRect(); + PRBool firstReflow = + (kidFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0; + if (doReflowChild) { // Calculate the available width for the table cell using the known column widths nscoord availColWidth, availCellWidth; @@ -889,7 +901,7 @@ nsTableRowFrame::ReflowChildren(nsPresContext* aPresContext, nsReflowStatus status; rv = ReflowChild(kidFrame, aPresContext, desiredSize, kidReflowState, - x, 0, 0, status); + x, 0, NS_FRAME_INVALIDATE_ON_MOVE, status); // allow the table to determine if/how the table needs to be rebalanced // If any of the cells are not complete, then we're not complete @@ -898,6 +910,10 @@ nsTableRowFrame::ReflowChildren(nsPresContext* aPresContext, } } else { + if (x != kidRect.x) { + kidFrame->InvalidateOverflowRect(); + } + desiredSize.width = cellDesiredSize.width; desiredSize.height = cellDesiredSize.height; nsRect *overflowArea = @@ -949,17 +965,20 @@ nsTableRowFrame::ReflowChildren(nsPresContext* aPresContext, } FinishReflowChild(kidFrame, aPresContext, nsnull, desiredSize, x, 0, 0); + + nsTableFrame::InvalidateFrame(kidFrame, kidRect, firstReflow); x += desiredSize.width; } else { - nsRect kidRect = kidFrame->GetRect(); if (kidRect.x != x) { - Invalidate(kidRect); // invalidate the old position - kidRect.x = x; - kidFrame->SetRect(kidRect); // move to the new position + // Invalidate the old position + kidFrame->InvalidateOverflowRect(); + // move to the new position + kidFrame->SetPosition(nsPoint(x, kidRect.y)); nsTableFrame::RePositionViews(kidFrame); - Invalidate(kidRect); // invalidate the new position + // invalidate the new position + kidFrame->InvalidateOverflowRect(); } // we need to account for the cell's width even if it isn't reflowed x += kidRect.width; @@ -1066,9 +1085,9 @@ nsTableRowFrame::ReflowCellFrame(nsPresContext* aPresContext, ABORT1(NS_ERROR_NULL_POINTER); // Reflow the cell frame with the specified height. Use the existing width - nsSize cellSize = aCellFrame->GetSize(); + nsRect cellRect = aCellFrame->GetRect(); - nsSize availSize(cellSize.width, aAvailableHeight); + nsSize availSize(cellRect.width, aAvailableHeight); PRBool borderCollapse = ((nsTableFrame*)tableFrame->GetFirstInFlow())->IsBorderCollapse(); nsTableCellReflowState cellReflowState(aPresContext, aReflowState, aCellFrame, availSize, PR_FALSE); @@ -1083,8 +1102,12 @@ nsTableRowFrame::ReflowCellFrame(nsPresContext* aPresContext, if (fullyComplete) { desiredSize.height = aAvailableHeight; } - aCellFrame->SetSize(nsSize(cellSize.width, desiredSize.height)); + aCellFrame->SetSize(nsSize(cellRect.width, desiredSize.height)); + nsTableFrame::InvalidateFrame(aCellFrame, cellRect, + (aCellFrame->GetStateBits() & + NS_FRAME_FIRST_REFLOW) != 0); + // XXX What happens if this cell has 'vertical-align: baseline' ? // XXX Why is it assumed that the cell's ascent hasn't changed ? if (fullyComplete) { @@ -1110,7 +1133,14 @@ nsTableRowFrame::CollapseRowIfNecessary(nscoord aRowOffset, tableFrame->SetNeedToCollapse(PR_TRUE); } + if (aRowOffset != 0) { + // We're moving, so invalidate our old position + InvalidateOverflowRect(); + } + nsRect rowRect = GetRect(); + nsRect oldRect = rowRect; + rowRect.y -= aRowOffset; rowRect.width = aWidth; nsRect overflowArea(0, 0, 0, 0); @@ -1124,6 +1154,13 @@ nsTableRowFrame::CollapseRowIfNecessary(nscoord aRowOffset, shift = rowRect.height + cellSpacingY; while (cellFrame) { nsRect cRect = cellFrame->GetRect(); + // If aRowOffset != 0, there's no point in invalidating the cells, since + // we've already invalidated our overflow area. Note that we _do_ still + // need to invalidate if our row is not moving, because the cell might + // span out of this row, so invalidating our row rect won't do enough. + if (aRowOffset == 0) { + Invalidate(cRect); + } cRect.height = 0; cellFrame->SetRect(cRect); cellFrame = cellFrame->GetNextCell(); @@ -1161,7 +1198,7 @@ nsTableRowFrame::CollapseRowIfNecessary(nscoord aRowOffset, *tableFrame, cellSpacingX, iter.IsLeftToRight(), PR_TRUE); } - nsRect cRect(x, 0, 0,rowRect.height); + nsRect cRect(x, 0, 0, rowRect.height); // remember the rightmost (ltr) or leftmost (rtl) column this cell // spans into @@ -1216,23 +1253,40 @@ nsTableRowFrame::CollapseRowIfNecessary(nscoord aRowOffset, } rowFrame = rowFrame->GetNextRow(); } + + nsRect oldCellRect = cellFrame->GetRect(); + + if (aRowOffset == 0 && cRect.TopLeft() != oldCellRect.TopLeft()) { + // We're moving the cell. Invalidate the old overflow area + cellFrame->InvalidateOverflowRect(); + } + cellFrame->SetRect(cRect); + + // XXXbz This looks completely bogus in the cases when we didn't + // collapse the cell! nsRect cellOverflow = nsRect(0, 0, cRect.width, cRect.height); cellFrame->FinishAndStoreOverflow(&cellOverflow, nsSize(cRect.width, cRect.height)); nsTableFrame::RePositionViews(cellFrame); ConsiderChildOverflow(overflowArea, cellFrame); - + + if (aRowOffset == 0) { + nsTableFrame::InvalidateFrame(cellFrame, oldCellRect, PR_FALSE); + } } kidFrame = iter.Next(); // Get the next child } } + SetRect(rowRect); overflowArea.UnionRect(nsRect(0,0,rowRect.width, rowRect.height), overflowArea); FinishAndStoreOverflow(&overflowArea, nsSize(rowRect.width, rowRect.height)); + nsTableFrame::RePositionViews(this); + nsTableFrame::InvalidateFrame(this, oldRect, PR_FALSE); return shift; } diff --git a/layout/tables/nsTableRowGroupFrame.cpp b/layout/tables/nsTableRowGroupFrame.cpp index 3dfe28a3496e..eb32b585ea88 100644 --- a/layout/tables/nsTableRowGroupFrame.cpp +++ b/layout/tables/nsTableRowGroupFrame.cpp @@ -294,10 +294,17 @@ void nsTableRowGroupFrame::PlaceChild(nsPresContext* aPresContext, nsRowGroupReflowState& aReflowState, nsIFrame* aKidFrame, - nsHTMLReflowMetrics& aDesiredSize) + nsHTMLReflowMetrics& aDesiredSize, + const nsRect& aOriginalKidRect) { + PRBool isFirstReflow = + (aKidFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0; + // Place and size the child - FinishReflowChild(aKidFrame, aPresContext, nsnull, aDesiredSize, 0, aReflowState.y, 0); + FinishReflowChild(aKidFrame, aPresContext, nsnull, aDesiredSize, 0, + aReflowState.y, 0); + + nsTableFrame::InvalidateFrame(aKidFrame, aOriginalKidRect, isFirstReflow); // Adjust the running y-offset aReflowState.y += aDesiredSize.height; @@ -384,7 +391,7 @@ nsTableRowGroupFrame::ReflowChildren(nsPresContext* aPresContext, (aReflowState.reflowState.mFlags.mSpecialHeightReflow && (isPaginated || (kidFrame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT)))) { - nsSize oldKidSize = kidFrame->GetSize(); + nsRect oldKidRect = kidFrame->GetRect(); // XXXldb We used to only pass aDesiredSize.mFlags through for the // incremental reflow codepath. @@ -410,10 +417,12 @@ nsTableRowGroupFrame::ReflowChildren(nsPresContext* aPresContext, } rv = ReflowChild(kidFrame, aPresContext, desiredSize, kidReflowState, - 0, aReflowState.y, 0, aStatus); + 0, aReflowState.y, NS_FRAME_INVALIDATE_ON_MOVE, + aStatus); // Place the child - PlaceChild(aPresContext, aReflowState, kidFrame, desiredSize); + PlaceChild(aPresContext, aReflowState, kidFrame, desiredSize, + oldKidRect); aReflowState.y += cellSpacingY; if (!reflowAllKids) { @@ -436,7 +445,7 @@ nsTableRowGroupFrame::ReflowChildren(nsPresContext* aPresContext, Invalidate(dirtyRect); } } - else if (oldKidSize.height != desiredSize.height) + else if (oldKidRect.height != desiredSize.height) needToCalcRowHeights = PR_TRUE; } else { needToCalcRowHeights = PR_TRUE; @@ -798,13 +807,19 @@ nsTableRowGroupFrame::CalculateRowHeights(nsPresContext* aPresContext, 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(rowBounds); + // Resize/move the row to its final size and position + if (movedFrame) { + rowFrame->InvalidateOverflowRect(); + } + + rowFrame->SetRect(nsRect(rowBounds.x, yOrigin, rowBounds.width, + rowHeight)); + + nsTableFrame::InvalidateFrame(rowFrame, rowBounds, PR_FALSE); } if (movedFrame) { nsTableFrame::RePositionViews(rowFrame); + // XXXbz we don't need to update our overflow area? } yOrigin += rowHeight + cellSpacingY; } @@ -845,6 +860,8 @@ nsTableRowGroupFrame::CollapseRowGroupIfNecessary(nscoord aYTotalOffset, } nsRect groupRect = GetRect(); + nsRect oldGroupRect = groupRect; + groupRect.height -= yGroupOffset; if (didCollapse) { // add back the cellspacing between rowgroups @@ -853,12 +870,19 @@ nsTableRowGroupFrame::CollapseRowGroupIfNecessary(nscoord aYTotalOffset, groupRect.y -= aYTotalOffset; groupRect.width = aWidth; + + if (aYTotalOffset != 0) { + InvalidateOverflowRect(); + } + SetRect(groupRect); overflowArea.UnionRect(nsRect(0, 0, groupRect.width, groupRect.height), overflowArea); FinishAndStoreOverflow(&overflowArea, nsSize(groupRect.width, groupRect.height)); nsTableFrame::RePositionViews(this); + nsTableFrame::InvalidateFrame(this, oldGroupRect, PR_FALSE); + return yGroupOffset; } @@ -880,8 +904,10 @@ nsTableRowGroupFrame::SlideChild(nsRowGroupReflowState& aReflowState, nsPoint newPosition = oldPosition; newPosition.y = aReflowState.y; if (oldPosition.y != newPosition.y) { + aKidFrame->InvalidateOverflowRect(); aKidFrame->SetPosition(newPosition); nsTableFrame::RePositionViews(aKidFrame); + aKidFrame->InvalidateOverflowRect(); } } @@ -1079,6 +1105,9 @@ nsTableRowGroupFrame::SplitRowGroup(nsPresContext* aPresContext, rowReflowState.mFlags.mIsTopOfPage = isTopOfPage; // set top of page nsHTMLReflowMetrics rowMetrics; + // Get the old size before we reflow. + nsRect oldRowRect = rowFrame->GetRect(); + // Reflow the cell with the constrained height. A cell with rowspan >1 will get this // reflow later during SplitSpanningCells. rv = ReflowChild(rowFrame, aPresContext, rowMetrics, rowReflowState, @@ -1088,6 +1117,8 @@ nsTableRowGroupFrame::SplitRowGroup(nsPresContext* aPresContext, rowFrame->DidReflow(aPresContext, nsnull, NS_FRAME_REFLOW_FINISHED); rowFrame->DidResize(); + nsTableFrame::InvalidateFrame(rowFrame, oldRowRect, PR_FALSE); + if (NS_FRAME_IS_NOT_COMPLETE(aStatus)) { // The row frame is incomplete and all of the rowspan 1 cells' block frames split if ((rowMetrics.height <= rowReflowState.availableHeight) || isTopOfPage) { diff --git a/layout/tables/nsTableRowGroupFrame.h b/layout/tables/nsTableRowGroupFrame.h index 2fee470aebb9..8c62a8f1b478 100644 --- a/layout/tables/nsTableRowGroupFrame.h +++ b/layout/tables/nsTableRowGroupFrame.h @@ -313,7 +313,8 @@ protected: void PlaceChild(nsPresContext* aPresContext, nsRowGroupReflowState& aReflowState, nsIFrame* aKidFrame, - nsHTMLReflowMetrics& aDesiredSize); + nsHTMLReflowMetrics& aDesiredSize, + const nsRect& aOriginalKidRect); void CalculateRowHeights(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize,