bug 558574 - fix issues with table footer placement and missed page breaks inside of row groups r=roc
authorBernd <bmlk@gmx.de>
Thu, 13 May 2010 16:15:49 +0200
changeset 42270 fcff7673a6ae09ae763fc094a55b8141ee100deb
parent 42269 f0206483a592390840b01eb5146a6a13a14d0002
child 42271 c4c451f9b36be263d6e714d781701b6247f55e27
push idunknown
push userunknown
push dateunknown
reviewersroc
bugs558574
milestone1.9.3a5pre
bug 558574 - fix issues with table footer placement and missed page breaks inside of row groups r=roc
layout/tables/nsTableFrame.cpp
layout/tables/nsTableFrame.h
layout/tables/nsTableRowGroupFrame.cpp
layout/tables/nsTableRowGroupFrame.h
--- 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;
 }