bug 558574 - fix issues with table footer placement and missed page breaks inside of row groups r=roc
--- a/layout/tables/nsTableFrame.cpp
+++ b/layout/tables/nsTableFrame.cpp
@@ -281,30 +281,35 @@ static PRBool
IsRepeatedFrame(nsIFrame* kidFrame)
{
return (kidFrame->GetType() == nsGkAtoms::tableRowFrame ||
kidFrame->GetType() == nsGkAtoms::tableRowGroupFrame) &&
(kidFrame->GetStateBits() & NS_REPEATED_ROW_OR_ROWGROUP);
}
PRBool
-nsTableFrame::PageBreakAfter(nsIFrame& aSourceFrame,
+nsTableFrame::PageBreakAfter(nsIFrame* aSourceFrame,
nsIFrame* aNextFrame)
{
- const nsStyleDisplay* display = aSourceFrame.GetStyleDisplay();
+ const nsStyleDisplay* display = aSourceFrame->GetStyleDisplay();
+ nsTableRowGroupFrame* prevRg = do_QueryFrame(aSourceFrame);
// don't allow a page break after a repeated element ...
- if (display->mBreakAfter && !IsRepeatedFrame(&aSourceFrame)) {
+ if (display->mBreakAfter || (prevRg && prevRg->HasInternalBreakAfter()) &&
+ !IsRepeatedFrame(aSourceFrame)) {
return !(aNextFrame && IsRepeatedFrame(aNextFrame)); // or before
}
if (aNextFrame) {
display = aNextFrame->GetStyleDisplay();
// don't allow a page break before a repeated element ...
- if (display->mBreakBefore && !IsRepeatedFrame(aNextFrame)) {
- return !IsRepeatedFrame(&aSourceFrame); // or after
+ nsTableRowGroupFrame* nextRg = do_QueryFrame(aNextFrame);
+ if (display->mBreakBefore ||
+ (nextRg && nextRg->HasInternalBreakBefore()) &&
+ !IsRepeatedFrame(aNextFrame)) {
+ return !IsRepeatedFrame(aSourceFrame); // or after
}
}
return PR_FALSE;
}
// XXX this needs to be cleaned up so that the frame constructor breaks out col group
// frames into a separate child list, bug 343048.
NS_IMETHODIMP
@@ -2608,16 +2613,44 @@ nsTableFrame::SetupHeaderFooterChild(con
NS_ENSURE_SUCCESS(rv, rv);
// The child will be reflowed again "for real" so no need to place it now
aFrame->SetRepeatable(IsRepeatable(desiredSize.height, pageHeight));
*aDesiredHeight = desiredSize.height;
return NS_OK;
}
+void
+nsTableFrame::PlaceRepeatedFooter(nsTableReflowState& aReflowState,
+ nsTableRowGroupFrame *aTfoot,
+ nscoord aFooterHeight)
+{
+ nsPresContext* presContext = PresContext();
+ nsSize kidAvailSize(aReflowState.availSize);
+ kidAvailSize.height = aFooterHeight;
+ nsHTMLReflowState footerReflowState(presContext,
+ aReflowState.reflowState,
+ aTfoot, kidAvailSize,
+ -1, -1, PR_FALSE);
+ InitChildReflowState(footerReflowState);
+ aReflowState.y += GetCellSpacingY();
+
+ nsRect origTfootRect = aTfoot->GetRect();
+ nsRect origTfootOverflowRect = aTfoot->GetOverflowRect();
+
+ nsReflowStatus footerStatus;
+ nsHTMLReflowMetrics desiredSize;
+ desiredSize.width = desiredSize.height = 0;
+ ReflowChild(aTfoot, presContext, desiredSize, footerReflowState,
+ aReflowState.x, aReflowState.y,
+ NS_FRAME_INVALIDATE_ON_MOVE, footerStatus);
+ PlaceChild(aReflowState, aTfoot, desiredSize, origTfootRect,
+ origTfootOverflowRect);
+}
+
// Reflow the children based on the avail size and reason in aReflowState
// update aReflowMetrics a aStatus
NS_METHOD
nsTableFrame::ReflowChildren(nsTableReflowState& aReflowState,
nsReflowStatus& aStatus,
nsIFrame*& aLastChildReflowed,
nsRect& aOverflowArea)
{
@@ -2625,17 +2658,18 @@ nsTableFrame::ReflowChildren(nsTableRefl
aLastChildReflowed = nsnull;
nsIFrame* prevKidFrame = nsnull;
nsresult rv = NS_OK;
nscoord cellSpacingY = GetCellSpacingY();
nsPresContext* presContext = PresContext();
// XXXldb Should we be checking constrained height instead?
- PRBool isPaginated = presContext->IsPaginated();
+ PRBool isPaginated = presContext->IsPaginated() &&
+ NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height;
aOverflowArea = nsRect (0, 0, 0, 0);
PRBool reflowAllKids = aReflowState.reflowState.ShouldReflowAllKids() ||
mBits.mResizedColumns ||
IsGeometryDirty();
RowGroupArray rowGroups;
@@ -2660,35 +2694,38 @@ nsTableFrame::ReflowChildren(nsTableRefl
return rv;
}
if (tfoot) {
rv = SetupHeaderFooterChild(aReflowState, tfoot, &footerHeight);
if (NS_FAILED(rv))
return rv;
}
}
-
+ // if the child is a tbody in paginated mode reduce the height by a repeated footer
+ PRBool allowRepeatedFooter = PR_FALSE;
for (PRUint32 childX = 0; childX < rowGroups.Length(); childX++) {
nsIFrame* kidFrame = rowGroups[childX];
// Get the frame state bits
// See if we should only reflow the dirty child frames
if (reflowAllKids ||
NS_SUBTREE_DIRTY(kidFrame) ||
(aReflowState.reflowState.mFlags.mSpecialHeightReflow &&
(isPaginated || (kidFrame->GetStateBits() &
NS_FRAME_CONTAINS_RELATIVE_HEIGHT)))) {
if (pageBreak) {
PushChildren(rowGroups, childX);
+ if (allowRepeatedFooter) {
+ PlaceRepeatedFooter(aReflowState, tfoot, footerHeight);
+ }
aStatus = NS_FRAME_NOT_COMPLETE;
break;
}
nsSize kidAvailSize(aReflowState.availSize);
- // if the child is a tbody in paginated mode reduce the height by a repeated footer
- PRBool allowRepeatedFooter = PR_FALSE;
+ allowRepeatedFooter = PR_FALSE;
if (isPaginated && (NS_UNCONSTRAINEDSIZE != kidAvailSize.height)) {
nsTableRowGroupFrame* kidRG =
static_cast<nsTableRowGroupFrame*>(kidFrame);
if (kidRG != thead && kidRG != tfoot && tfoot && tfoot->IsRepeatable()) {
// the child is a tbody and there is a repeatable footer
NS_ASSERTION(tfoot == rowGroups[rowGroups.Length() - 1], "Missing footer!");
if (footerHeight + cellSpacingY < kidAvailSize.height) {
allowRepeatedFooter = PR_TRUE;
@@ -2749,43 +2786,48 @@ nsTableFrame::ReflowChildren(nsTableRefl
kidReflowState.availableHeight < desiredSize.height) {
// if we are on top of the page place with dataloss
if (kidReflowState.mFlags.mIsTopOfPage) {
if (childX+1 < rowGroups.Length()) {
nsIFrame* nextRowGroupFrame = rowGroups[childX + 1];
if (nextRowGroupFrame) {
PlaceChild(aReflowState, kidFrame, desiredSize, oldKidRect,
oldKidOverflowRect);
+ if (allowRepeatedFooter) {
+ PlaceRepeatedFooter(aReflowState, tfoot, footerHeight);
+ }
aStatus = NS_FRAME_NOT_COMPLETE;
PushChildren(rowGroups, childX + 1);
aLastChildReflowed = kidFrame;
break;
}
}
}
else { // we are not on top, push this rowgroup onto the next page
if (prevKidFrame) { // we had a rowgroup before so push this
- // XXXroc shouldn't we add a repeated footer here?
+ if (allowRepeatedFooter) {
+ PlaceRepeatedFooter(aReflowState, tfoot, footerHeight);
+ }
aStatus = NS_FRAME_NOT_COMPLETE;
PushChildren(rowGroups, childX);
aLastChildReflowed = prevKidFrame;
break;
}
}
}
aLastChildReflowed = kidFrame;
pageBreak = PR_FALSE;
// see if there is a page break after this row group or before the next one
if (NS_FRAME_IS_COMPLETE(aStatus) && isPaginated &&
(NS_UNCONSTRAINEDSIZE != kidReflowState.availableHeight)) {
nsIFrame* nextKid =
(childX + 1 < rowGroups.Length()) ? rowGroups[childX + 1] : nsnull;
- pageBreak = PageBreakAfter(*kidFrame, nextKid);
+ pageBreak = PageBreakAfter(kidFrame, nextKid);
}
// Place the child
PlaceChild(aReflowState, kidFrame, desiredSize, oldKidRect,
oldKidOverflowRect);
// Remember where we just were in case we end up pushing children
prevKidFrame = kidFrame;
@@ -2818,33 +2860,17 @@ nsTableFrame::ReflowChildren(nsTableRefl
// We've used up all of our available space so push the remaining
// children to the next-in-flow
nsIFrame* nextSibling = kidFrame->GetNextSibling();
if (nsnull != nextSibling) {
PushChildren(rowGroups, childX + 1);
}
if (allowRepeatedFooter) {
- kidAvailSize.height = footerHeight;
- nsHTMLReflowState footerReflowState(presContext,
- aReflowState.reflowState,
- tfoot, kidAvailSize,
- -1, -1, PR_FALSE);
- InitChildReflowState(footerReflowState);
- aReflowState.y += cellSpacingY;
-
- nsRect origTfootRect = tfoot->GetRect();
- nsRect origTfootOverflowRect = tfoot->GetOverflowRect();
-
- nsReflowStatus footerStatus;
- rv = ReflowChild(tfoot, presContext, desiredSize, footerReflowState,
- aReflowState.x, aReflowState.y,
- NS_FRAME_INVALIDATE_ON_MOVE, footerStatus);
- PlaceChild(aReflowState, tfoot, desiredSize, origTfootRect,
- origTfootOverflowRect);
+ PlaceRepeatedFooter(aReflowState, tfoot, footerHeight);
}
break;
}
}
else { // it isn't being reflowed
aReflowState.y += cellSpacingY;
nsRect kidRect = kidFrame->GetRect();
if (kidRect.y != aReflowState.y) {
--- a/layout/tables/nsTableFrame.h
+++ b/layout/tables/nsTableFrame.h
@@ -172,17 +172,17 @@ public:
// Notify the frame and its ancestors (up to the containing table) that a special
// height reflow will occur.
static void RequestSpecialHeightReflow(const nsHTMLReflowState& aReflowState);
virtual PRBool IsContainingBlock() const;
static void RePositionViews(nsIFrame* aFrame);
- static PRBool PageBreakAfter(nsIFrame& aSourceFrame,
+ static PRBool PageBreakAfter(nsIFrame* aSourceFrame,
nsIFrame* aNextFrame);
nsPoint GetFirstSectionOrigin(const nsHTMLReflowState& aReflowState) const;
/*
* Notification that aAttribute has changed for content inside a table (cell, row, etc)
*/
void AttributeChangedFor(nsIFrame* aFrame,
nsIContent* aContent,
@@ -602,16 +602,19 @@ protected:
void DistributeHeightToRows(const nsHTMLReflowState& aReflowState,
nscoord aAmount);
void PlaceChild(nsTableReflowState& aReflowState,
nsIFrame* aKidFrame,
nsHTMLReflowMetrics& aKidDesiredSize,
const nsRect& aOriginalKidRect,
const nsRect& aOriginalKidOverflowRect);
+ void PlaceRepeatedFooter(nsTableReflowState& aReflowState,
+ nsTableRowGroupFrame *aTfoot,
+ nscoord aFooterHeight);
nsIFrame* GetFirstBodyRowGroupFrame();
public:
typedef nsAutoTPtrArray<nsTableRowGroupFrame, 8> RowGroupArray;
/**
* Push all our child frames from the aRowGroups array, in order, starting
* from the frame at aPushFrom to the end of the array. The frames are put on
* our overflow list or moved directly to our next-in-flow if one exists.
--- a/layout/tables/nsTableRowGroupFrame.cpp
+++ b/layout/tables/nsTableRowGroupFrame.cpp
@@ -358,17 +358,18 @@ nsTableRowGroupFrame::ReflowChildren(nsP
nsresult rv = NS_OK;
PRBool borderCollapse = tableFrame->IsBorderCollapse();
nscoord cellSpacingY = tableFrame->GetCellSpacingY();
// XXXldb Should we really be checking this rather than available height?
// (Think about multi-column layout!)
- PRBool isPaginated = aPresContext->IsPaginated();
+ PRBool isPaginated = aPresContext->IsPaginated() &&
+ NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height;
PRBool haveRow = PR_FALSE;
PRBool reflowAllKids = aReflowState.reflowState.ShouldReflowAllKids() ||
tableFrame->IsGeometryDirty();
PRBool needToCalcRowHeights = reflowAllKids;
nsIFrame *prevKidFrame = nsnull;
for (nsIFrame* kidFrame = mFrames.FirstChild(); kidFrame;
@@ -454,17 +455,17 @@ nsTableRowGroupFrame::ReflowChildren(nsP
} else {
needToCalcRowHeights = PR_TRUE;
}
}
if (isPaginated && aPageBreakBeforeEnd && !*aPageBreakBeforeEnd) {
nsTableRowFrame* nextRow = rowFrame->GetNextRow();
if (nextRow) {
- *aPageBreakBeforeEnd = nsTableFrame::PageBreakAfter(*kidFrame, nextRow);
+ *aPageBreakBeforeEnd = nsTableFrame::PageBreakAfter(kidFrame, nextRow);
}
}
} else {
SlideChild(aReflowState, kidFrame);
// Adjust the running y-offset so we know where the next row should be placed
nscoord height = kidFrame->GetSize().height + cellSpacingY;
aReflowState.y += height;
@@ -1263,17 +1264,17 @@ nsTableRowGroupFrame::SplitRowGroup(nsPr
}
break;
} // if (rowRect.YMost() > availHeight)
else {
aDesiredSize.height = rowRect.YMost();
prevRowFrame = rowFrame;
// see if there is a page break after the row
nsTableRowFrame* nextRow = rowFrame->GetNextRow();
- if (nextRow && nsTableFrame::PageBreakAfter(*rowFrame, nextRow)) {
+ if (nextRow && nsTableFrame::PageBreakAfter(rowFrame, nextRow)) {
PushChildren(aPresContext, nextRow, rowFrame);
aStatus = NS_FRAME_NOT_COMPLETE;
break;
}
}
// after the 1st row that has a height, we can't be on top
// of the page anymore.
isTopOfPage = isTopOfPage && rowRect.YMost() == 0;
@@ -1564,17 +1565,35 @@ nsTableRowGroupFrame::IsSimpleRowFrame(n
}
nsIAtom*
nsTableRowGroupFrame::GetType() const
{
return nsGkAtoms::tableRowGroupFrame;
}
+/** find page break before the first row **/
+PRBool
+nsTableRowGroupFrame::HasInternalBreakBefore() const
+{
+ nsIFrame* firstChild = mFrames.FirstChild();
+ if (!firstChild)
+ return PR_FALSE;
+ return firstChild->GetStyleDisplay()->mBreakBefore;
+}
+/** find page break after the last row **/
+PRBool
+nsTableRowGroupFrame::HasInternalBreakAfter() const
+{
+ nsIFrame* lastChild = mFrames.LastChild();
+ if (!lastChild)
+ return PR_FALSE;
+ return lastChild->GetStyleDisplay()->mBreakAfter;
+}
/* ----- global methods ----- */
nsIFrame*
NS_NewTableRowGroupFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
{
return new (aPresShell) nsTableRowGroupFrame(aContext);
}
--- a/layout/tables/nsTableRowGroupFrame.h
+++ b/layout/tables/nsTableRowGroupFrame.h
@@ -431,16 +431,18 @@ private:
BCPixelSize mBottomContBorderWidth;
BCPixelSize mLeftContBorderWidth;
public:
PRBool IsRepeatable() const;
void SetRepeatable(PRBool aRepeatable);
PRBool HasStyleHeight() const;
void SetHasStyleHeight(PRBool aValue);
+ PRBool HasInternalBreakBefore() const;
+ PRBool HasInternalBreakAfter() const;
};
inline PRBool nsTableRowGroupFrame::IsRepeatable() const
{
return (mState & NS_ROWGROUP_REPEATABLE) == NS_ROWGROUP_REPEATABLE;
}