layout/tables/nsTableRowGroupFrame.cpp

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 #include "nsCOMPtr.h"
     6 #include "nsTableRowGroupFrame.h"
     7 #include "nsTableRowFrame.h"
     8 #include "nsTableFrame.h"
     9 #include "nsTableCellFrame.h"
    10 #include "nsPresContext.h"
    11 #include "nsStyleContext.h"
    12 #include "nsStyleConsts.h"
    13 #include "nsIContent.h"
    14 #include "nsGkAtoms.h"
    15 #include "nsIPresShell.h"
    16 #include "nsCSSRendering.h"
    17 #include "nsHTMLParts.h"
    18 #include "nsCSSFrameConstructor.h"
    19 #include "nsDisplayList.h"
    21 #include "nsCellMap.h"//table cell navigation
    22 #include <algorithm>
    24 using namespace mozilla;
    25 using namespace mozilla::layout;
    27 nsTableRowGroupFrame::nsTableRowGroupFrame(nsStyleContext* aContext):
    28   nsContainerFrame(aContext)
    29 {
    30   SetRepeatable(false);
    31 }
    33 nsTableRowGroupFrame::~nsTableRowGroupFrame()
    34 {
    35 }
    37 void
    38 nsTableRowGroupFrame::DestroyFrom(nsIFrame* aDestructRoot)
    39 {
    40   if (GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN) {
    41     nsTableFrame::UnregisterPositionedTablePart(this, aDestructRoot);
    42   }
    44   nsContainerFrame::DestroyFrom(aDestructRoot);
    45 }
    47 NS_QUERYFRAME_HEAD(nsTableRowGroupFrame)
    48   NS_QUERYFRAME_ENTRY(nsTableRowGroupFrame)
    49 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
    51 int32_t
    52 nsTableRowGroupFrame::GetRowCount()
    53 {
    54 #ifdef DEBUG
    55   for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) {
    56     NS_ASSERTION(e.get()->StyleDisplay()->mDisplay ==
    57                    NS_STYLE_DISPLAY_TABLE_ROW,
    58                  "Unexpected display");
    59     NS_ASSERTION(e.get()->GetType() == nsGkAtoms::tableRowFrame,
    60                  "Unexpected frame type");
    61   }
    62 #endif
    64   return mFrames.GetLength();
    65 }
    67 int32_t nsTableRowGroupFrame::GetStartRowIndex()
    68 {
    69   int32_t result = -1;
    70   if (mFrames.NotEmpty()) {
    71     NS_ASSERTION(mFrames.FirstChild()->GetType() == nsGkAtoms::tableRowFrame,
    72                  "Unexpected frame type");
    73     result = static_cast<nsTableRowFrame*>(mFrames.FirstChild())->GetRowIndex();
    74   }
    75   // if the row group doesn't have any children, get it the hard way
    76   if (-1 == result) {
    77     nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
    78     return tableFrame->GetStartRowIndex(this);
    79   }
    81   return result;
    82 }
    84 void  nsTableRowGroupFrame::AdjustRowIndices(int32_t aRowIndex,
    85                                              int32_t anAdjustment)
    86 {
    87   nsIFrame* rowFrame = GetFirstPrincipalChild();
    88   for ( ; rowFrame; rowFrame = rowFrame->GetNextSibling()) {
    89     if (NS_STYLE_DISPLAY_TABLE_ROW==rowFrame->StyleDisplay()->mDisplay) {
    90       int32_t index = ((nsTableRowFrame*)rowFrame)->GetRowIndex();
    91       if (index >= aRowIndex)
    92         ((nsTableRowFrame *)rowFrame)->SetRowIndex(index+anAdjustment);
    93     }
    94   }
    95 }
    96 nsresult
    97 nsTableRowGroupFrame::InitRepeatedFrame(nsPresContext*        aPresContext,
    98                                         nsTableRowGroupFrame* aHeaderFooterFrame)
    99 {
   100   nsTableRowFrame* copyRowFrame = GetFirstRow();
   101   nsTableRowFrame* originalRowFrame = aHeaderFooterFrame->GetFirstRow();
   102   AddStateBits(NS_REPEATED_ROW_OR_ROWGROUP);
   103   while (copyRowFrame && originalRowFrame) {
   104     copyRowFrame->AddStateBits(NS_REPEATED_ROW_OR_ROWGROUP);
   105     int rowIndex = originalRowFrame->GetRowIndex();
   106     copyRowFrame->SetRowIndex(rowIndex);
   108     // For each table cell frame set its column index
   109     nsTableCellFrame* originalCellFrame = originalRowFrame->GetFirstCell();
   110     nsTableCellFrame* copyCellFrame     = copyRowFrame->GetFirstCell();
   111     while (copyCellFrame && originalCellFrame) {
   112       NS_ASSERTION(originalCellFrame->GetContent() == copyCellFrame->GetContent(),
   113                    "cell frames have different content");
   114       int32_t colIndex;
   115       originalCellFrame->GetColIndex(colIndex);
   116       copyCellFrame->SetColIndex(colIndex);
   118       // Move to the next cell frame
   119       copyCellFrame     = copyCellFrame->GetNextCell();
   120       originalCellFrame = originalCellFrame->GetNextCell();
   121     }
   123     // Move to the next row frame
   124     originalRowFrame = originalRowFrame->GetNextRow();
   125     copyRowFrame = copyRowFrame->GetNextRow();
   126   }
   128   return NS_OK;
   129 }
   131 /**
   132  * We need a custom display item for table row backgrounds. This is only used
   133  * when the table row is the root of a stacking context (e.g., has 'opacity').
   134  * Table row backgrounds can extend beyond the row frame bounds, when
   135  * the row contains row-spanning cells.
   136  */
   137 class nsDisplayTableRowGroupBackground : public nsDisplayTableItem {
   138 public:
   139   nsDisplayTableRowGroupBackground(nsDisplayListBuilder* aBuilder,
   140                                    nsTableRowGroupFrame* aFrame) :
   141     nsDisplayTableItem(aBuilder, aFrame) {
   142     MOZ_COUNT_CTOR(nsDisplayTableRowGroupBackground);
   143   }
   144 #ifdef NS_BUILD_REFCNT_LOGGING
   145   virtual ~nsDisplayTableRowGroupBackground() {
   146     MOZ_COUNT_DTOR(nsDisplayTableRowGroupBackground);
   147   }
   148 #endif
   150   virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
   151                                          const nsDisplayItemGeometry* aGeometry,
   152                                          nsRegion *aInvalidRegion) MOZ_OVERRIDE;
   153   virtual void Paint(nsDisplayListBuilder* aBuilder,
   154                      nsRenderingContext* aCtx) MOZ_OVERRIDE;
   156   NS_DISPLAY_DECL_NAME("TableRowGroupBackground", TYPE_TABLE_ROW_GROUP_BACKGROUND)
   157 };
   159 void
   160 nsDisplayTableRowGroupBackground::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
   161                                                             const nsDisplayItemGeometry* aGeometry,
   162                                                             nsRegion *aInvalidRegion)
   163 {
   164   if (aBuilder->ShouldSyncDecodeImages()) {
   165     if (nsTableFrame::AnyTablePartHasUndecodedBackgroundImage(mFrame, mFrame->GetNextSibling())) {
   166       bool snap;
   167       aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
   168     }
   169   }
   171   nsDisplayTableItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
   172 }
   174 void
   175 nsDisplayTableRowGroupBackground::Paint(nsDisplayListBuilder* aBuilder,
   176                                         nsRenderingContext* aCtx)
   177 {
   178   nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(mFrame);
   179   TableBackgroundPainter painter(tableFrame,
   180                                  TableBackgroundPainter::eOrigin_TableRowGroup,
   181                                  mFrame->PresContext(), *aCtx,
   182                                  mVisibleRect, ToReferenceFrame(),
   183                                  aBuilder->GetBackgroundPaintFlags());
   184   painter.PaintRowGroup(static_cast<nsTableRowGroupFrame*>(mFrame));
   185 }
   187 // Handle the child-traversal part of DisplayGenericTablePart
   188 static void
   189 DisplayRows(nsDisplayListBuilder* aBuilder, nsFrame* aFrame,
   190             const nsRect& aDirtyRect, const nsDisplayListSet& aLists)
   191 {
   192   nscoord overflowAbove;
   193   nsTableRowGroupFrame* f = static_cast<nsTableRowGroupFrame*>(aFrame);
   194   // Don't try to use the row cursor if we have to descend into placeholders;
   195   // we might have rows containing placeholders, where the row's overflow
   196   // area doesn't intersect the dirty rect but we need to descend into the row
   197   // to see out of flows.
   198   // Note that we really want to check ShouldDescendIntoFrame for all
   199   // the rows in |f|, but that's exactly what we're trying to avoid, so we
   200   // approximate it by checking it for |f|: if it's true for any row
   201   // in |f| then it's true for |f| itself.
   202   nsIFrame* kid = aBuilder->ShouldDescendIntoFrame(f) ?
   203     nullptr : f->GetFirstRowContaining(aDirtyRect.y, &overflowAbove);
   205   if (kid) {
   206     // have a cursor, use it
   207     while (kid) {
   208       if (kid->GetRect().y - overflowAbove >= aDirtyRect.YMost())
   209         break;
   210       f->BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists);
   211       kid = kid->GetNextSibling();
   212     }
   213     return;
   214   }
   216   // No cursor. Traverse children the hard way and build a cursor while we're at it
   217   nsTableRowGroupFrame::FrameCursorData* cursor = f->SetupRowCursor();
   218   kid = f->GetFirstPrincipalChild();
   219   while (kid) {
   220     f->BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists);
   222     if (cursor) {
   223       if (!cursor->AppendFrame(kid)) {
   224         f->ClearRowCursor();
   225         return;
   226       }
   227     }
   229     kid = kid->GetNextSibling();
   230   }
   231   if (cursor) {
   232     cursor->FinishBuildingCursor();
   233   }
   234 }
   236 void
   237 nsTableRowGroupFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
   238                                        const nsRect&           aDirtyRect,
   239                                        const nsDisplayListSet& aLists)
   240 {
   241   nsDisplayTableItem* item = nullptr;
   242   if (IsVisibleInSelection(aBuilder)) {
   243     bool isRoot = aBuilder->IsAtRootOfPseudoStackingContext();
   244     if (isRoot) {
   245       // This background is created regardless of whether this frame is
   246       // visible or not. Visibility decisions are delegated to the
   247       // table background painter.
   248       item = new (aBuilder) nsDisplayTableRowGroupBackground(aBuilder, this);
   249       aLists.BorderBackground()->AppendNewToTop(item);
   250     }
   251   }  
   252   nsTableFrame::DisplayGenericTablePart(aBuilder, this, aDirtyRect,
   253                                         aLists, item, DisplayRows);
   254 }
   256 int
   257 nsTableRowGroupFrame::GetLogicalSkipSides(const nsHTMLReflowState* aReflowState) const
   258 {
   259   int skip = 0;
   260   if (nullptr != GetPrevInFlow()) {
   261     skip |= LOGICAL_SIDE_B_START;
   262   }
   263   if (nullptr != GetNextInFlow()) {
   264     skip |= LOGICAL_SIDE_B_END;
   265   }
   266   return skip;
   267 }
   269 // Position and size aKidFrame and update our reflow state. The origin of
   270 // aKidRect is relative to the upper-left origin of our frame
   271 void 
   272 nsTableRowGroupFrame::PlaceChild(nsPresContext*         aPresContext,
   273                                  nsRowGroupReflowState& aReflowState,
   274                                  nsIFrame*              aKidFrame,
   275                                  nsHTMLReflowMetrics&   aDesiredSize,
   276                                  const nsRect&          aOriginalKidRect,
   277                                  const nsRect&          aOriginalKidVisualOverflow)
   278 {
   279   bool isFirstReflow =
   280     (aKidFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0;
   282   // Place and size the child
   283   FinishReflowChild(aKidFrame, aPresContext, aDesiredSize, nullptr, 0,
   284                     aReflowState.y, 0);
   286   nsTableFrame::InvalidateTableFrame(aKidFrame, aOriginalKidRect,
   287                                      aOriginalKidVisualOverflow, isFirstReflow);
   289   // Adjust the running y-offset
   290   aReflowState.y += aDesiredSize.Height();
   292   // If our height is constrained then update the available height
   293   if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height) {
   294     aReflowState.availSize.height -= aDesiredSize.Height();
   295   }
   296 }
   298 void
   299 nsTableRowGroupFrame::InitChildReflowState(nsPresContext&     aPresContext, 
   300                                            bool               aBorderCollapse,
   301                                            nsHTMLReflowState& aReflowState)                                    
   302 {
   303   nsMargin collapseBorder;
   304   nsMargin padding(0,0,0,0);
   305   nsMargin* pCollapseBorder = nullptr;
   306   if (aBorderCollapse) {
   307     nsTableRowFrame *rowFrame = do_QueryFrame(aReflowState.frame);
   308     if (rowFrame) {
   309       pCollapseBorder = rowFrame->GetBCBorderWidth(collapseBorder);
   310     }
   311   }
   312   aReflowState.Init(&aPresContext, -1, -1, pCollapseBorder, &padding);
   313 }
   315 static void
   316 CacheRowHeightsForPrinting(nsPresContext*   aPresContext,
   317                            nsTableRowFrame* aFirstRow)
   318 {
   319   for (nsTableRowFrame* row = aFirstRow; row; row = row->GetNextRow()) {
   320     if (!row->GetPrevInFlow()) {
   321       row->SetHasUnpaginatedHeight(true);
   322       row->SetUnpaginatedHeight(aPresContext, row->GetSize().height);
   323     }
   324   }
   325 }
   327 nsresult
   328 nsTableRowGroupFrame::ReflowChildren(nsPresContext*         aPresContext,
   329                                      nsHTMLReflowMetrics&   aDesiredSize,
   330                                      nsRowGroupReflowState& aReflowState,
   331                                      nsReflowStatus&        aStatus,
   332                                      bool*                aPageBreakBeforeEnd)
   333 {
   334   if (aPageBreakBeforeEnd) 
   335     *aPageBreakBeforeEnd = false;
   337   nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
   338   nsresult rv = NS_OK;
   339   const bool borderCollapse = tableFrame->IsBorderCollapse();
   340   nscoord cellSpacingY = tableFrame->GetCellSpacingY();
   342   // XXXldb Should we really be checking this rather than available height?
   343   // (Think about multi-column layout!)
   344   bool isPaginated = aPresContext->IsPaginated() && 
   345                        NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height;
   347   bool haveRow = false;
   348   bool reflowAllKids = aReflowState.reflowState.ShouldReflowAllKids() ||
   349                          tableFrame->IsGeometryDirty();
   350   bool needToCalcRowHeights = reflowAllKids;
   352   nsIFrame *prevKidFrame = nullptr;
   353   for (nsIFrame* kidFrame = mFrames.FirstChild(); kidFrame;
   354        prevKidFrame = kidFrame, kidFrame = kidFrame->GetNextSibling()) {
   355     nsTableRowFrame *rowFrame = do_QueryFrame(kidFrame);
   356     if (!rowFrame) {
   357       // XXXldb nsCSSFrameConstructor needs to enforce this!
   358       NS_NOTREACHED("yikes, a non-row child");
   359       continue;
   360     }
   362     haveRow = true;
   364     // Reflow the row frame
   365     if (reflowAllKids ||
   366         NS_SUBTREE_DIRTY(kidFrame) ||
   367         (aReflowState.reflowState.mFlags.mSpecialHeightReflow &&
   368          (isPaginated || (kidFrame->GetStateBits() &
   369                           NS_FRAME_CONTAINS_RELATIVE_HEIGHT)))) {
   370       nsRect oldKidRect = kidFrame->GetRect();
   371       nsRect oldKidVisualOverflow = kidFrame->GetVisualOverflowRect();
   373       // XXXldb We used to only pass aDesiredSize.mFlags through for the
   374       // incremental reflow codepath.
   375       nsHTMLReflowMetrics desiredSize(aReflowState.reflowState,
   376                                       aDesiredSize.mFlags);
   377       desiredSize.Width() = desiredSize.Height() = 0;
   379       // Reflow the child into the available space, giving it as much height as
   380       // it wants. We'll deal with splitting later after we've computed the row
   381       // heights, taking into account cells with row spans...
   382       nsSize kidAvailSize(aReflowState.availSize.width, NS_UNCONSTRAINEDSIZE);
   383       nsHTMLReflowState kidReflowState(aPresContext, aReflowState.reflowState,
   384                                        kidFrame, kidAvailSize,
   385                                        -1, -1,
   386                                        nsHTMLReflowState::CALLER_WILL_INIT);
   387       InitChildReflowState(*aPresContext, borderCollapse, kidReflowState);
   389       // This can indicate that columns were resized.
   390       if (aReflowState.reflowState.mFlags.mHResize)
   391         kidReflowState.mFlags.mHResize = true;
   393       NS_ASSERTION(kidFrame == mFrames.FirstChild() || prevKidFrame, 
   394                    "If we're not on the first frame, we should have a "
   395                    "previous sibling...");
   396       // If prev row has nonzero YMost, then we can't be at the top of the page
   397       if (prevKidFrame && prevKidFrame->GetRect().YMost() > 0) {
   398         kidReflowState.mFlags.mIsTopOfPage = false;
   399       }
   401       rv = ReflowChild(kidFrame, aPresContext, desiredSize, kidReflowState,
   402                        0, aReflowState.y, 0, aStatus);
   404       // Place the child
   405       PlaceChild(aPresContext, aReflowState, kidFrame, desiredSize,
   406                  oldKidRect, oldKidVisualOverflow);
   407       aReflowState.y += cellSpacingY;
   409       if (!reflowAllKids) {
   410         if (IsSimpleRowFrame(aReflowState.tableFrame, kidFrame)) {
   411           // Inform the row of its new height.
   412           rowFrame->DidResize();
   413           // the overflow area may have changed inflate the overflow area
   414           const nsStylePosition *stylePos = StylePosition();
   415           nsStyleUnit unit = stylePos->mHeight.GetUnit();
   416           if (aReflowState.tableFrame->IsAutoHeight() &&
   417               unit != eStyleUnit_Coord) {
   418             // Because other cells in the row may need to be aligned
   419             // differently, repaint the entire row
   420             nsRect kidRect(0, aReflowState.y,
   421                            desiredSize.Width(), desiredSize.Height());
   422             InvalidateFrame();
   423           }
   424           else if (oldKidRect.height != desiredSize.Height())
   425             needToCalcRowHeights = true;
   426         } else {
   427           needToCalcRowHeights = true;
   428         }
   429       }
   431       if (isPaginated && aPageBreakBeforeEnd && !*aPageBreakBeforeEnd) {
   432         nsTableRowFrame* nextRow = rowFrame->GetNextRow();
   433         if (nextRow) {
   434           *aPageBreakBeforeEnd = nsTableFrame::PageBreakAfter(kidFrame, nextRow);
   435         }
   436       }
   437     } else {
   438       SlideChild(aReflowState, kidFrame);
   440       // Adjust the running y-offset so we know where the next row should be placed
   441       nscoord height = kidFrame->GetSize().height + cellSpacingY;
   442       aReflowState.y += height;
   444       if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height) {
   445         aReflowState.availSize.height -= height;
   446       }
   447     }
   448     ConsiderChildOverflow(aDesiredSize.mOverflowAreas, kidFrame);
   449   }
   451   if (haveRow)
   452     aReflowState.y -= cellSpacingY;
   454   // Return our desired rect
   455   aDesiredSize.Width() = aReflowState.reflowState.AvailableWidth();
   456   aDesiredSize.Height() = aReflowState.y;
   458   if (aReflowState.reflowState.mFlags.mSpecialHeightReflow) {
   459     DidResizeRows(aDesiredSize);
   460     if (isPaginated) {
   461       CacheRowHeightsForPrinting(aPresContext, GetFirstRow());
   462     }
   463   }
   464   else if (needToCalcRowHeights) {
   465     CalculateRowHeights(aPresContext, aDesiredSize, aReflowState.reflowState);
   466     if (!reflowAllKids) {
   467       InvalidateFrame();
   468     }
   469   }
   471   return rv;
   472 }
   474 nsTableRowFrame*  
   475 nsTableRowGroupFrame::GetFirstRow() 
   476 {
   477   for (nsIFrame* childFrame = mFrames.FirstChild(); childFrame;
   478        childFrame = childFrame->GetNextSibling()) {
   479     nsTableRowFrame *rowFrame = do_QueryFrame(childFrame);
   480     if (rowFrame) {
   481       return rowFrame;
   482     }
   483   }
   484   return nullptr;
   485 }
   488 struct RowInfo {
   489   RowInfo() { height = pctHeight = hasStyleHeight = hasPctHeight = isSpecial = 0; }
   490   unsigned height;       // content height or fixed height, excluding pct height
   491   unsigned pctHeight:29; // pct height
   492   unsigned hasStyleHeight:1; 
   493   unsigned hasPctHeight:1; 
   494   unsigned isSpecial:1; // there is no cell originating in the row with rowspan=1 and there are at
   495                         // least 2 cells spanning the row and there is no style height on the row
   496 };
   498 static void
   499 UpdateHeights(RowInfo& aRowInfo,
   500               nscoord  aAdditionalHeight,
   501               nscoord& aTotal,
   502               nscoord& aUnconstrainedTotal)
   503 {
   504   aRowInfo.height += aAdditionalHeight;
   505   aTotal          += aAdditionalHeight;
   506   if (!aRowInfo.hasStyleHeight) {
   507     aUnconstrainedTotal += aAdditionalHeight;
   508   }
   509 }
   511 void 
   512 nsTableRowGroupFrame::DidResizeRows(nsHTMLReflowMetrics& aDesiredSize)
   513 {
   514   // update the cells spanning rows with their new heights
   515   // this is the place where all of the cells in the row get set to the height of the row
   516   // Reset the overflow area
   517   aDesiredSize.mOverflowAreas.Clear();
   518   for (nsTableRowFrame* rowFrame = GetFirstRow();
   519        rowFrame; rowFrame = rowFrame->GetNextRow()) {
   520     rowFrame->DidResize();
   521     ConsiderChildOverflow(aDesiredSize.mOverflowAreas, rowFrame);
   522   }
   523 }
   525 // This calculates the height of all the rows and takes into account 
   526 // style height on the row group, style heights on rows and cells, style heights on rowspans. 
   527 // Actual row heights will be adjusted later if the table has a style height.
   528 // Even if rows don't change height, this method must be called to set the heights of each
   529 // cell in the row to the height of its row.
   530 void 
   531 nsTableRowGroupFrame::CalculateRowHeights(nsPresContext*           aPresContext, 
   532                                           nsHTMLReflowMetrics&     aDesiredSize,
   533                                           const nsHTMLReflowState& aReflowState)
   534 {
   535   nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
   536   const bool isPaginated = aPresContext->IsPaginated();
   538   // all table cells have the same top and bottom margins, namely cellSpacingY
   539   nscoord cellSpacingY = tableFrame->GetCellSpacingY();
   541   int32_t numEffCols = tableFrame->GetEffectiveColCount();
   543   int32_t startRowIndex = GetStartRowIndex();
   544   // find the row corresponding to the row index we just found
   545   nsTableRowFrame* startRowFrame = GetFirstRow();
   547   if (!startRowFrame) return;
   549   // the current row group height is the y origin of the 1st row we are about to calculated a height for
   550   nscoord startRowGroupHeight = startRowFrame->GetPosition().y;
   552   int32_t numRows = GetRowCount() - (startRowFrame->GetRowIndex() - GetStartRowIndex());
   553   // collect the current height of each row.  nscoord* rowHeights = nullptr;
   554   if (numRows <= 0)
   555     return;
   557   nsTArray<RowInfo> rowInfo;
   558   if (!rowInfo.AppendElements(numRows)) {
   559     return;
   560   }
   562   bool    hasRowSpanningCell = false;
   563   nscoord heightOfRows = 0;
   564   nscoord heightOfUnStyledRows = 0;
   565   // Get the height of each row without considering rowspans. This will be the max of 
   566   // the largest desired height of each cell, the largest style height of each cell, 
   567   // the style height of the row.
   568   nscoord pctHeightBasis = GetHeightBasis(aReflowState);
   569   int32_t rowIndex; // the index in rowInfo, not among the rows in the row group
   570   nsTableRowFrame* rowFrame;
   571   for (rowFrame = startRowFrame, rowIndex = 0; rowFrame; rowFrame = rowFrame->GetNextRow(), rowIndex++) {
   572     nscoord nonPctHeight = rowFrame->GetContentHeight();
   573     if (isPaginated) {
   574       nonPctHeight = std::max(nonPctHeight, rowFrame->GetSize().height);
   575     }
   576     if (!rowFrame->GetPrevInFlow()) {
   577       if (rowFrame->HasPctHeight()) {
   578         rowInfo[rowIndex].hasPctHeight = true;
   579         rowInfo[rowIndex].pctHeight = rowFrame->GetHeight(pctHeightBasis);
   580       }
   581       rowInfo[rowIndex].hasStyleHeight = rowFrame->HasStyleHeight();
   582       nonPctHeight = std::max(nonPctHeight, rowFrame->GetFixedHeight());
   583     }
   584     UpdateHeights(rowInfo[rowIndex], nonPctHeight, heightOfRows, heightOfUnStyledRows);
   586     if (!rowInfo[rowIndex].hasStyleHeight) {
   587       if (isPaginated || tableFrame->HasMoreThanOneCell(rowIndex + startRowIndex)) {
   588         rowInfo[rowIndex].isSpecial = true;
   589         // iteratate the row's cell frames to see if any do not have rowspan > 1
   590         nsTableCellFrame* cellFrame = rowFrame->GetFirstCell();
   591         while (cellFrame) {
   592           int32_t rowSpan = tableFrame->GetEffectiveRowSpan(rowIndex + startRowIndex, *cellFrame);
   593           if (1 == rowSpan) { 
   594             rowInfo[rowIndex].isSpecial = false;
   595             break;
   596           }
   597           cellFrame = cellFrame->GetNextCell(); 
   598         }
   599       }
   600     }
   601     // See if a cell spans into the row. If so we'll have to do the next step
   602     if (!hasRowSpanningCell) {
   603       if (tableFrame->RowIsSpannedInto(rowIndex + startRowIndex, numEffCols)) {
   604         hasRowSpanningCell = true;
   605       }
   606     }
   607   }
   609   if (hasRowSpanningCell) {
   610     // Get the height of cells with rowspans and allocate any extra space to the rows they span 
   611     // iteratate the child frames and process the row frames among them
   612     for (rowFrame = startRowFrame, rowIndex = 0; rowFrame; rowFrame = rowFrame->GetNextRow(), rowIndex++) {
   613       // See if the row has an originating cell with rowspan > 1. We cannot determine this for a row in a 
   614       // continued row group by calling RowHasSpanningCells, because the row's fif may not have any originating
   615       // cells yet the row may have a continued cell which originates in it.
   616       if (GetPrevInFlow() || tableFrame->RowHasSpanningCells(startRowIndex + rowIndex, numEffCols)) {
   617         nsTableCellFrame* cellFrame = rowFrame->GetFirstCell();
   618         // iteratate the row's cell frames 
   619         while (cellFrame) {
   620           int32_t rowSpan = tableFrame->GetEffectiveRowSpan(rowIndex + startRowIndex, *cellFrame);
   621           if ((rowIndex + rowSpan) > numRows) {
   622             // there might be rows pushed already to the nextInFlow
   623             rowSpan = numRows - rowIndex;
   624           }
   625           if (rowSpan > 1) { // a cell with rowspan > 1, determine the height of the rows it spans
   626             nscoord heightOfRowsSpanned = 0;
   627             nscoord heightOfUnStyledRowsSpanned = 0;
   628             nscoord numSpecialRowsSpanned = 0; 
   629             nscoord cellSpacingTotal = 0;
   630             int32_t spanX;
   631             for (spanX = 0; spanX < rowSpan; spanX++) {
   632               heightOfRowsSpanned += rowInfo[rowIndex + spanX].height;
   633               if (!rowInfo[rowIndex + spanX].hasStyleHeight) {
   634                 heightOfUnStyledRowsSpanned += rowInfo[rowIndex + spanX].height;
   635               }
   636               if (0 != spanX) {
   637                 cellSpacingTotal += cellSpacingY;
   638               }
   639               if (rowInfo[rowIndex + spanX].isSpecial) {
   640                 numSpecialRowsSpanned++;
   641               }
   642             } 
   643             nscoord heightOfAreaSpanned = heightOfRowsSpanned + cellSpacingTotal;
   644             // get the height of the cell 
   645             nsSize cellFrameSize = cellFrame->GetSize();
   646             nsSize cellDesSize = cellFrame->GetDesiredSize();
   647             rowFrame->CalculateCellActualHeight(cellFrame, cellDesSize.height);
   648             cellFrameSize.height = cellDesSize.height;
   649             if (cellFrame->HasVerticalAlignBaseline()) {
   650               // to ensure that a spanning cell with a long descender doesn't
   651               // collide with the next row, we need to take into account the shift
   652               // that will be done to align the cell on the baseline of the row.
   653               cellFrameSize.height += rowFrame->GetMaxCellAscent() -
   654                                       cellFrame->GetCellBaseline();
   655             }
   657             if (heightOfAreaSpanned < cellFrameSize.height) {
   658               // the cell's height is larger than the available space of the rows it
   659               // spans so distribute the excess height to the rows affected
   660               nscoord extra     = cellFrameSize.height - heightOfAreaSpanned;
   661               nscoord extraUsed = 0;
   662               if (0 == numSpecialRowsSpanned) {
   663                 //NS_ASSERTION(heightOfRowsSpanned > 0, "invalid row span situation");
   664                 bool haveUnStyledRowsSpanned = (heightOfUnStyledRowsSpanned > 0);
   665                 nscoord divisor = (haveUnStyledRowsSpanned) 
   666                                   ? heightOfUnStyledRowsSpanned : heightOfRowsSpanned;
   667                 if (divisor > 0) {
   668                   for (spanX = rowSpan - 1; spanX >= 0; spanX--) {
   669                     if (!haveUnStyledRowsSpanned || !rowInfo[rowIndex + spanX].hasStyleHeight) {
   670                       // The amount of additional space each row gets is proportional to its height
   671                       float percent = ((float)rowInfo[rowIndex + spanX].height) / ((float)divisor);
   673                       // give rows their percentage, except for the first row which gets the remainder
   674                       nscoord extraForRow = (0 == spanX) ? extra - extraUsed  
   675                                                          : NSToCoordRound(((float)(extra)) * percent);
   676                       extraForRow = std::min(extraForRow, extra - extraUsed);
   677                       // update the row height
   678                       UpdateHeights(rowInfo[rowIndex + spanX], extraForRow, heightOfRows, heightOfUnStyledRows);
   679                       extraUsed += extraForRow;
   680                       if (extraUsed >= extra) {
   681                         NS_ASSERTION((extraUsed == extra), "invalid row height calculation");
   682                         break;
   683                       }
   684                     }
   685                   }
   686                 }
   687                 else {
   688                   // put everything in the last row
   689                   UpdateHeights(rowInfo[rowIndex + rowSpan - 1], extra, heightOfRows, heightOfUnStyledRows);
   690                 }
   691               }
   692               else {
   693                 // give the extra to the special rows
   694                 nscoord numSpecialRowsAllocated = 0;
   695                 for (spanX = rowSpan - 1; spanX >= 0; spanX--) {
   696                   if (rowInfo[rowIndex + spanX].isSpecial) {
   697                     // The amount of additional space each degenerate row gets is proportional to the number of them
   698                     float percent = 1.0f / ((float)numSpecialRowsSpanned);
   700                     // give rows their percentage, except for the first row which gets the remainder
   701                     nscoord extraForRow = (numSpecialRowsSpanned - 1 == numSpecialRowsAllocated) 
   702                                           ? extra - extraUsed  
   703                                           : NSToCoordRound(((float)(extra)) * percent);
   704                     extraForRow = std::min(extraForRow, extra - extraUsed);
   705                     // update the row height
   706                     UpdateHeights(rowInfo[rowIndex + spanX], extraForRow, heightOfRows, heightOfUnStyledRows);
   707                     extraUsed += extraForRow;
   708                     if (extraUsed >= extra) {
   709                       NS_ASSERTION((extraUsed == extra), "invalid row height calculation");
   710                       break;
   711                     }
   712                   }
   713                 }
   714               }
   715             } 
   716           } // if (rowSpan > 1)
   717           cellFrame = cellFrame->GetNextCell(); 
   718         } // while (cellFrame)
   719       } // if (tableFrame->RowHasSpanningCells(startRowIndex + rowIndex) {
   720     } // while (rowFrame)
   721   }
   723   // pct height rows have already got their content heights. Give them their pct heights up to pctHeightBasis
   724   nscoord extra = pctHeightBasis - heightOfRows;
   725   for (rowFrame = startRowFrame, rowIndex = 0; rowFrame && (extra > 0); rowFrame = rowFrame->GetNextRow(), rowIndex++) {
   726     RowInfo& rInfo = rowInfo[rowIndex];
   727     if (rInfo.hasPctHeight) {
   728       nscoord rowExtra = (rInfo.pctHeight > rInfo.height)  
   729                          ? rInfo.pctHeight - rInfo.height: 0;
   730       rowExtra = std::min(rowExtra, extra);
   731       UpdateHeights(rInfo, rowExtra, heightOfRows, heightOfUnStyledRows);
   732       extra -= rowExtra;
   733     }
   734   }
   736   bool styleHeightAllocation = false;
   737   nscoord rowGroupHeight = startRowGroupHeight + heightOfRows + ((numRows - 1) * cellSpacingY);
   738   // if we have a style height, allocate the extra height to unconstrained rows
   739   if ((aReflowState.ComputedHeight() > rowGroupHeight) && 
   740       (NS_UNCONSTRAINEDSIZE != aReflowState.ComputedHeight())) {
   741     nscoord extraComputedHeight = aReflowState.ComputedHeight() - rowGroupHeight;
   742     nscoord extraUsed = 0;
   743     bool haveUnStyledRows = (heightOfUnStyledRows > 0);
   744     nscoord divisor = (haveUnStyledRows) 
   745                       ? heightOfUnStyledRows : heightOfRows;
   746     if (divisor > 0) {
   747       styleHeightAllocation = true;
   748       for (rowIndex = 0; rowIndex < numRows; rowIndex++) {
   749         if (!haveUnStyledRows || !rowInfo[rowIndex].hasStyleHeight) {
   750           // The amount of additional space each row gets is based on the
   751           // percentage of space it occupies
   752           float percent = ((float)rowInfo[rowIndex].height) / ((float)divisor);
   753           // give rows their percentage, except for the last row which gets the remainder
   754           nscoord extraForRow = (numRows - 1 == rowIndex) 
   755                                 ? extraComputedHeight - extraUsed  
   756                                 : NSToCoordRound(((float)extraComputedHeight) * percent);
   757           extraForRow = std::min(extraForRow, extraComputedHeight - extraUsed);
   758           // update the row height
   759           UpdateHeights(rowInfo[rowIndex], extraForRow, heightOfRows, heightOfUnStyledRows);
   760           extraUsed += extraForRow;
   761           if (extraUsed >= extraComputedHeight) {
   762             NS_ASSERTION((extraUsed == extraComputedHeight), "invalid row height calculation");
   763             break;
   764           }
   765         }
   766       }
   767     }
   768     rowGroupHeight = aReflowState.ComputedHeight();
   769   }
   771   nscoord yOrigin = startRowGroupHeight;
   772   // update the rows with their (potentially) new heights
   773   for (rowFrame = startRowFrame, rowIndex = 0; rowFrame; rowFrame = rowFrame->GetNextRow(), rowIndex++) {
   774     nsRect rowBounds = rowFrame->GetRect();
   775     nsRect rowVisualOverflow = rowFrame->GetVisualOverflowRect();
   777     bool movedFrame = (rowBounds.y != yOrigin);  
   778     nscoord rowHeight = (rowInfo[rowIndex].height > 0) ? rowInfo[rowIndex].height : 0;
   780     if (movedFrame || (rowHeight != rowBounds.height)) {
   781       // Resize/move the row to its final size and position
   782       if (movedFrame) {
   783         rowFrame->InvalidateFrameSubtree();
   784       }
   786       rowFrame->SetRect(nsRect(rowBounds.x, yOrigin, rowBounds.width,
   787                                rowHeight));
   789       nsTableFrame::InvalidateTableFrame(rowFrame, rowBounds, rowVisualOverflow,
   790                                          false);
   791     }
   792     if (movedFrame) {
   793       nsTableFrame::RePositionViews(rowFrame);
   794       // XXXbz we don't need to update our overflow area?
   795     }
   796     yOrigin += rowHeight + cellSpacingY;
   797   }
   799   if (isPaginated && styleHeightAllocation) {
   800     // since the row group has a style height, cache the row heights, so next in flows can honor them 
   801     CacheRowHeightsForPrinting(aPresContext, GetFirstRow());
   802   }
   804   DidResizeRows(aDesiredSize);
   806   aDesiredSize.Height() = rowGroupHeight; // Adjust our desired size
   807 }
   809 nscoord
   810 nsTableRowGroupFrame::CollapseRowGroupIfNecessary(nscoord aYTotalOffset,
   811                                                   nscoord aWidth)
   812 {
   813   nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
   814   const nsStyleVisibility* groupVis = StyleVisibility();
   815   bool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE == groupVis->mVisible);
   816   if (collapseGroup) {
   817     tableFrame->SetNeedToCollapse(true);
   818   }
   820   nsOverflowAreas overflow;
   822   nsTableRowFrame* rowFrame= GetFirstRow();
   823   bool didCollapse = false;
   824   nscoord yGroupOffset = 0;
   825   while (rowFrame) {
   826     yGroupOffset += rowFrame->CollapseRowIfNecessary(yGroupOffset,
   827                                                      aWidth, collapseGroup,
   828                                                      didCollapse);
   829     ConsiderChildOverflow(overflow, rowFrame);
   830     rowFrame = rowFrame->GetNextRow();
   831   }
   833   nsRect groupRect = GetRect();
   834   nsRect oldGroupRect = groupRect;
   835   nsRect oldGroupVisualOverflow = GetVisualOverflowRect();
   837   groupRect.height -= yGroupOffset;
   838   if (didCollapse) {
   839     // add back the cellspacing between rowgroups
   840     groupRect.height += tableFrame->GetCellSpacingY();
   841   }
   843   groupRect.y -= aYTotalOffset;
   844   groupRect.width = aWidth;
   846   if (aYTotalOffset != 0) {
   847     InvalidateFrameSubtree();
   848   }
   850   SetRect(groupRect);
   851   overflow.UnionAllWith(nsRect(0, 0, groupRect.width, groupRect.height));
   852   FinishAndStoreOverflow(overflow, groupRect.Size());
   853   nsTableFrame::RePositionViews(this);
   854   nsTableFrame::InvalidateTableFrame(this, oldGroupRect, oldGroupVisualOverflow,
   855                                      false);
   857   return yGroupOffset;
   858 }
   860 // Move a child that was skipped during a reflow.
   861 void
   862 nsTableRowGroupFrame::SlideChild(nsRowGroupReflowState& aReflowState,
   863                                  nsIFrame*              aKidFrame)
   864 {
   865   // Move the frame if we need to
   866   nsPoint oldPosition = aKidFrame->GetPosition();
   867   nsPoint newPosition = oldPosition;
   868   newPosition.y = aReflowState.y;
   869   if (oldPosition.y != newPosition.y) {
   870     aKidFrame->InvalidateFrameSubtree();
   871     aKidFrame->SetPosition(newPosition);
   872     nsTableFrame::RePositionViews(aKidFrame);
   873     aKidFrame->InvalidateFrameSubtree();
   874   }
   875 }
   877 // Create a continuing frame, add it to the child list, and then push it
   878 // and the frames that follow
   879 void 
   880 nsTableRowGroupFrame::CreateContinuingRowFrame(nsPresContext& aPresContext,
   881                                                nsIFrame&      aRowFrame,
   882                                                nsIFrame**     aContRowFrame)
   883 {
   884   // XXX what is the row index?
   885   if (!aContRowFrame) {NS_ASSERTION(false, "bad call"); return;}
   886   // create the continuing frame which will create continuing cell frames
   887   *aContRowFrame = aPresContext.PresShell()->FrameConstructor()->
   888     CreateContinuingFrame(&aPresContext, &aRowFrame, this);
   890   // Add the continuing row frame to the child list
   891   mFrames.InsertFrame(nullptr, &aRowFrame, *aContRowFrame);
   893   // Push the continuing row frame and the frames that follow
   894   PushChildren(*aContRowFrame, &aRowFrame);
   895 }
   897 // Reflow the cells with rowspan > 1 which originate between aFirstRow
   898 // and end on or after aLastRow. aFirstTruncatedRow is the highest row on the
   899 // page that contains a cell which cannot split on this page 
   900 void
   901 nsTableRowGroupFrame::SplitSpanningCells(nsPresContext&           aPresContext,
   902                                          const nsHTMLReflowState& aReflowState,
   903                                          nsTableFrame&            aTable,
   904                                          nsTableRowFrame&         aFirstRow, 
   905                                          nsTableRowFrame&         aLastRow,  
   906                                          bool                     aFirstRowIsTopOfPage,
   907                                          nscoord                  aSpanningRowBottom,
   908                                          nsTableRowFrame*&        aContRow,
   909                                          nsTableRowFrame*&        aFirstTruncatedRow,
   910                                          nscoord&                 aDesiredHeight)
   911 {
   912   NS_ASSERTION(aSpanningRowBottom >= 0, "Can't split negative heights");
   913   aFirstTruncatedRow = nullptr;
   914   aDesiredHeight     = 0;
   916   const bool borderCollapse = aTable.IsBorderCollapse();
   917   int32_t lastRowIndex = aLastRow.GetRowIndex();
   918   bool wasLast = false;
   919   bool haveRowSpan = false;
   920   // Iterate the rows between aFirstRow and aLastRow
   921   for (nsTableRowFrame* row = &aFirstRow; !wasLast; row = row->GetNextRow()) {
   922     wasLast = (row == &aLastRow);
   923     int32_t rowIndex = row->GetRowIndex();
   924     nsPoint rowPos = row->GetPosition();
   925     // Iterate the cells looking for those that have rowspan > 1
   926     for (nsTableCellFrame* cell = row->GetFirstCell(); cell; cell = cell->GetNextCell()) {
   927       int32_t rowSpan = aTable.GetEffectiveRowSpan(rowIndex, *cell);
   928       // Only reflow rowspan > 1 cells which span aLastRow. Those which don't span aLastRow
   929       // were reflowed correctly during the unconstrained height reflow. 
   930       if ((rowSpan > 1) && (rowIndex + rowSpan > lastRowIndex)) {
   931         haveRowSpan = true;
   932         nsReflowStatus status;
   933         // Ask the row to reflow the cell to the height of all the rows it spans up through aLastRow
   934         // aAvailHeight is the space between the row group start and the end of the page
   935         nscoord cellAvailHeight = aSpanningRowBottom - rowPos.y;
   936         NS_ASSERTION(cellAvailHeight >= 0, "No space for cell?");
   937         bool isTopOfPage = (row == &aFirstRow) && aFirstRowIsTopOfPage;
   939         nsRect rowRect = row->GetRect();
   940         nsSize rowAvailSize(aReflowState.AvailableWidth(),
   941                             std::max(aReflowState.AvailableHeight() - rowRect.y,
   942                                    0));
   943         // don't let the available height exceed what
   944         // CalculateRowHeights set for it
   945         rowAvailSize.height = std::min(rowAvailSize.height, rowRect.height);
   946         nsHTMLReflowState rowReflowState(&aPresContext, aReflowState,
   947                                          row, rowAvailSize,
   948                                          -1, -1,
   949                                          nsHTMLReflowState::CALLER_WILL_INIT);
   950         InitChildReflowState(aPresContext, borderCollapse, rowReflowState);
   951         rowReflowState.mFlags.mIsTopOfPage = isTopOfPage; // set top of page
   953         nscoord cellHeight = row->ReflowCellFrame(&aPresContext, rowReflowState,
   954                                                   isTopOfPage, cell,
   955                                                   cellAvailHeight, status);
   956         aDesiredHeight = std::max(aDesiredHeight, rowPos.y + cellHeight);
   957         if (NS_FRAME_IS_COMPLETE(status)) {
   958           if (cellHeight > cellAvailHeight) {
   959             aFirstTruncatedRow = row;
   960             if ((row != &aFirstRow) || !aFirstRowIsTopOfPage) {
   961               // return now, since we will be getting another reflow after either (1) row is 
   962               // moved to the next page or (2) the row group is moved to the next page
   963               return;
   964             }
   965           }
   966         }
   967         else {
   968           if (!aContRow) {
   969             CreateContinuingRowFrame(aPresContext, aLastRow, (nsIFrame**)&aContRow);
   970           }
   971           if (aContRow) {
   972             if (row != &aLastRow) {
   973               // aContRow needs a continuation for cell, since cell spanned into aLastRow 
   974               // but does not originate there
   975               nsTableCellFrame* contCell = static_cast<nsTableCellFrame*>(
   976                 aPresContext.PresShell()->FrameConstructor()->
   977                   CreateContinuingFrame(&aPresContext, cell, &aLastRow));
   978               int32_t colIndex;
   979               cell->GetColIndex(colIndex);
   980               aContRow->InsertCellFrame(contCell, colIndex);
   981             }
   982           }
   983         }
   984       }
   985     }
   986   }
   987   if (!haveRowSpan) {
   988     aDesiredHeight = aLastRow.GetRect().YMost();
   989   }
   990 }
   992 // Remove the next-in-flow of the row, its cells and their cell blocks. This 
   993 // is necessary in case the row doesn't need a continuation later on or needs 
   994 // a continuation which doesn't have the same number of cells that now exist. 
   995 void
   996 nsTableRowGroupFrame::UndoContinuedRow(nsPresContext*   aPresContext,
   997                                        nsTableRowFrame* aRow)
   998 {
   999   if (!aRow) return; // allow null aRow to avoid callers doing null checks
  1001   // rowBefore was the prev-sibling of aRow's next-sibling before aRow was created
  1002   nsTableRowFrame* rowBefore = (nsTableRowFrame*)aRow->GetPrevInFlow();
  1003   NS_PRECONDITION(mFrames.ContainsFrame(rowBefore),
  1004                   "rowBefore not in our frame list?");
  1006   AutoFrameListPtr overflows(aPresContext, StealOverflowFrames());
  1007   if (!rowBefore || !overflows || overflows->IsEmpty() ||
  1008       overflows->FirstChild() != aRow) {
  1009     NS_ERROR("invalid continued row");
  1010     return;
  1013   // Destroy aRow, its cells, and their cell blocks. Cell blocks that have split
  1014   // will not have reflowed yet to pick up content from any overflow lines.
  1015   overflows->DestroyFrame(aRow);
  1017   // Put the overflow rows into our child list
  1018   if (!overflows->IsEmpty()) {
  1019     mFrames.InsertFrames(nullptr, rowBefore, *overflows);
  1023 static nsTableRowFrame* 
  1024 GetRowBefore(nsTableRowFrame& aStartRow,
  1025              nsTableRowFrame& aRow)
  1027   nsTableRowFrame* rowBefore = nullptr;
  1028   for (nsTableRowFrame* sib = &aStartRow; sib && (sib != &aRow); sib = sib->GetNextRow()) {
  1029     rowBefore = sib;
  1031   return rowBefore;
  1034 nsresult
  1035 nsTableRowGroupFrame::SplitRowGroup(nsPresContext*           aPresContext,
  1036                                     nsHTMLReflowMetrics&     aDesiredSize,
  1037                                     const nsHTMLReflowState& aReflowState,
  1038                                     nsTableFrame*            aTableFrame,
  1039                                     nsReflowStatus&          aStatus,
  1040                                     bool                     aRowForcedPageBreak)
  1042   NS_PRECONDITION(aPresContext->IsPaginated(), "SplitRowGroup currently supports only paged media"); 
  1044   nsresult rv = NS_OK;
  1045   nsTableRowFrame* prevRowFrame = nullptr;
  1046   aDesiredSize.Height() = 0;
  1048   nscoord availWidth  = aReflowState.AvailableWidth();
  1049   nscoord availHeight = aReflowState.AvailableHeight();
  1051   const bool borderCollapse = aTableFrame->IsBorderCollapse();
  1052   nscoord cellSpacingY = aTableFrame->GetCellSpacingY();
  1054   // get the page height
  1055   nscoord pageHeight = aPresContext->GetPageSize().height;
  1056   NS_ASSERTION(pageHeight != NS_UNCONSTRAINEDSIZE, 
  1057                "The table shouldn't be split when there should be space");
  1059   bool isTopOfPage = aReflowState.mFlags.mIsTopOfPage;
  1060   nsTableRowFrame* firstRowThisPage = GetFirstRow();
  1062   // Need to dirty the table's geometry, or else the row might skip
  1063   // reflowing its cell as an optimization.
  1064   aTableFrame->SetGeometryDirty();
  1066   // Walk each of the row frames looking for the first row frame that doesn't fit 
  1067   // in the available space
  1068   for (nsTableRowFrame* rowFrame = firstRowThisPage; rowFrame; rowFrame = rowFrame->GetNextRow()) {
  1069     bool rowIsOnPage = true;
  1070     nsRect rowRect = rowFrame->GetRect();
  1071     // See if the row fits on this page
  1072     if (rowRect.YMost() > availHeight) {
  1073       nsTableRowFrame* contRow = nullptr;
  1074       // Reflow the row in the availabe space and have it split if it is the 1st
  1075       // row (on the page) or there is at least 5% of the current page available 
  1076       // XXX this 5% should be made a preference 
  1077       if (!prevRowFrame || (availHeight - aDesiredSize.Height() > pageHeight / 20)) { 
  1078         nsSize availSize(availWidth, std::max(availHeight - rowRect.y, 0));
  1079         // don't let the available height exceed what CalculateRowHeights set for it
  1080         availSize.height = std::min(availSize.height, rowRect.height);
  1082         nsHTMLReflowState rowReflowState(aPresContext, aReflowState,
  1083                                          rowFrame, availSize,
  1084                                          -1, -1,
  1085                                          nsHTMLReflowState::CALLER_WILL_INIT);
  1087         InitChildReflowState(*aPresContext, borderCollapse, rowReflowState);
  1088         rowReflowState.mFlags.mIsTopOfPage = isTopOfPage; // set top of page
  1089         nsHTMLReflowMetrics rowMetrics(aReflowState);
  1091         // Get the old size before we reflow.
  1092         nsRect oldRowRect = rowFrame->GetRect();
  1093         nsRect oldRowVisualOverflow = rowFrame->GetVisualOverflowRect();
  1095         // Reflow the cell with the constrained height. A cell with rowspan >1 will get this
  1096         // reflow later during SplitSpanningCells.
  1097         rv = ReflowChild(rowFrame, aPresContext, rowMetrics, rowReflowState,
  1098                          0, 0, NS_FRAME_NO_MOVE_FRAME, aStatus);
  1099         if (NS_FAILED(rv)) return rv;
  1100         rowFrame->SetSize(nsSize(rowMetrics.Width(), rowMetrics.Height()));
  1101         rowFrame->DidReflow(aPresContext, nullptr, nsDidReflowStatus::FINISHED);
  1102         rowFrame->DidResize();
  1104         if (!aRowForcedPageBreak && !NS_FRAME_IS_FULLY_COMPLETE(aStatus) &&
  1105             ShouldAvoidBreakInside(aReflowState)) {
  1106           aStatus = NS_INLINE_LINE_BREAK_BEFORE();
  1107           break;
  1110         nsTableFrame::InvalidateTableFrame(rowFrame, oldRowRect,
  1111                                            oldRowVisualOverflow,
  1112                                            false);
  1114         if (NS_FRAME_IS_NOT_COMPLETE(aStatus)) {
  1115           // The row frame is incomplete and all of the rowspan 1 cells' block frames split
  1116           if ((rowMetrics.Height() <= rowReflowState.AvailableHeight()) || isTopOfPage) {
  1117             // The row stays on this page because either it split ok or we're on the top of page.
  1118             // If top of page and the height exceeded the avail height, then there will be data loss
  1119             NS_ASSERTION(rowMetrics.Height() <= rowReflowState.AvailableHeight(), 
  1120                          "data loss - incomplete row needed more height than available, on top of page");
  1121             CreateContinuingRowFrame(*aPresContext, *rowFrame, (nsIFrame**)&contRow);
  1122             if (contRow) {
  1123               aDesiredSize.Height() += rowMetrics.Height();
  1124               if (prevRowFrame) 
  1125                 aDesiredSize.Height() += cellSpacingY;
  1127             else return NS_ERROR_NULL_POINTER;
  1129           else {
  1130             // Put the row on the next page to give it more height 
  1131             rowIsOnPage = false;
  1134         else {
  1135           // The row frame is complete because either (1) its minimum height is greater than the 
  1136           // available height we gave it, or (2) it may have been given a larger height through 
  1137           // style than its content, or (3) it contains a rowspan >1 cell which hasn't been
  1138           // reflowed with a constrained height yet (we will find out when SplitSpanningCells is
  1139           // called below)
  1140           if (rowMetrics.Height() > availSize.height ||
  1141               (NS_INLINE_IS_BREAK_BEFORE(aStatus) && !aRowForcedPageBreak)) {
  1142             // cases (1) and (2)
  1143             if (isTopOfPage) { 
  1144               // We're on top of the page, so keep the row on this page. There will be data loss.
  1145               // Push the row frame that follows
  1146               nsTableRowFrame* nextRowFrame = rowFrame->GetNextRow();
  1147               if (nextRowFrame) {
  1148                 aStatus = NS_FRAME_NOT_COMPLETE;
  1150               aDesiredSize.Height() += rowMetrics.Height();
  1151               if (prevRowFrame) 
  1152                 aDesiredSize.Height() += cellSpacingY;
  1153               NS_WARNING("data loss - complete row needed more height than available, on top of page");
  1155             else {
  1156               // We're not on top of the page, so put the row on the next page to give it more height 
  1157               rowIsOnPage = false;
  1161       } //if (!prevRowFrame || (availHeight - aDesiredSize.Height() > pageHeight / 20))
  1162       else { 
  1163         // put the row on the next page to give it more height
  1164         rowIsOnPage = false;
  1167       nsTableRowFrame* lastRowThisPage = rowFrame;
  1168       nscoord spanningRowBottom = availHeight;
  1169       if (!rowIsOnPage) {
  1170         NS_ASSERTION(!contRow, "We should not have created a continuation if none of this row fits");
  1171         if (!aRowForcedPageBreak && ShouldAvoidBreakInside(aReflowState)) {
  1172           aStatus = NS_INLINE_LINE_BREAK_BEFORE();
  1173           break;
  1175         if (prevRowFrame) {
  1176           spanningRowBottom = prevRowFrame->GetRect().YMost();
  1177           lastRowThisPage = prevRowFrame;
  1178           isTopOfPage = (lastRowThisPage == firstRowThisPage) && aReflowState.mFlags.mIsTopOfPage;
  1179           aStatus = NS_FRAME_NOT_COMPLETE;
  1181         else {
  1182           // We can't push children, so let our parent reflow us again with more space
  1183           aDesiredSize.Height() = rowRect.YMost();
  1184           aStatus = NS_FRAME_COMPLETE;
  1185           break;
  1188       // reflow the cells with rowspan >1 that occur on the page
  1190       nsTableRowFrame* firstTruncatedRow;
  1191       nscoord yMost;
  1192       SplitSpanningCells(*aPresContext, aReflowState, *aTableFrame, *firstRowThisPage,
  1193                          *lastRowThisPage, aReflowState.mFlags.mIsTopOfPage, spanningRowBottom, contRow, 
  1194                          firstTruncatedRow, yMost);
  1195       if (firstTruncatedRow) {
  1196         // A rowspan >1 cell did not fit (and could not split) in the space we gave it
  1197         if (firstTruncatedRow == firstRowThisPage) {
  1198           if (aReflowState.mFlags.mIsTopOfPage) {
  1199             NS_WARNING("data loss in a row spanned cell");
  1201           else {
  1202             // We can't push children, so let our parent reflow us again with more space
  1203             aDesiredSize.Height() = rowRect.YMost();
  1204             aStatus = NS_FRAME_COMPLETE;
  1205             UndoContinuedRow(aPresContext, contRow);
  1206             contRow = nullptr;
  1209         else { // (firstTruncatedRow != firstRowThisPage)
  1210           // Try to put firstTruncateRow on the next page 
  1211           nsTableRowFrame* rowBefore = ::GetRowBefore(*firstRowThisPage, *firstTruncatedRow);
  1212           nscoord oldSpanningRowBottom = spanningRowBottom;
  1213           spanningRowBottom = rowBefore->GetRect().YMost();
  1215           UndoContinuedRow(aPresContext, contRow);
  1216           contRow = nullptr;
  1217           nsTableRowFrame* oldLastRowThisPage = lastRowThisPage;
  1218           lastRowThisPage = rowBefore;
  1219           aStatus = NS_FRAME_NOT_COMPLETE;
  1221           // Call SplitSpanningCells again with rowBefore as the last row on the page
  1222           SplitSpanningCells(*aPresContext, aReflowState, *aTableFrame, 
  1223                              *firstRowThisPage, *rowBefore, aReflowState.mFlags.mIsTopOfPage, 
  1224                              spanningRowBottom, contRow, firstTruncatedRow, aDesiredSize.Height());
  1225           if (firstTruncatedRow) {
  1226             if (aReflowState.mFlags.mIsTopOfPage) {
  1227               // We were better off with the 1st call to SplitSpanningCells, do it again
  1228               UndoContinuedRow(aPresContext, contRow);
  1229               contRow = nullptr;
  1230               lastRowThisPage = oldLastRowThisPage;
  1231               spanningRowBottom = oldSpanningRowBottom;
  1232               SplitSpanningCells(*aPresContext, aReflowState, *aTableFrame, *firstRowThisPage,
  1233                                  *lastRowThisPage, aReflowState.mFlags.mIsTopOfPage, spanningRowBottom, contRow, 
  1234                                  firstTruncatedRow, aDesiredSize.Height());
  1235               NS_WARNING("data loss in a row spanned cell");
  1237             else {
  1238               // Let our parent reflow us again with more space
  1239               aDesiredSize.Height() = rowRect.YMost();
  1240               aStatus = NS_FRAME_COMPLETE;
  1241               UndoContinuedRow(aPresContext, contRow);
  1242               contRow = nullptr;
  1245         } // if (firstTruncatedRow == firstRowThisPage)
  1246       } // if (firstTruncatedRow)
  1247       else {
  1248         aDesiredSize.Height() = std::max(aDesiredSize.Height(), yMost);
  1249         if (contRow) {
  1250           aStatus = NS_FRAME_NOT_COMPLETE;
  1253       if (NS_FRAME_IS_NOT_COMPLETE(aStatus) && !contRow) {
  1254         nsTableRowFrame* nextRow = lastRowThisPage->GetNextRow();
  1255         if (nextRow) {
  1256           PushChildren(nextRow, lastRowThisPage);
  1259       break;
  1260     } // if (rowRect.YMost() > availHeight)
  1261     else { 
  1262       aDesiredSize.Height() = rowRect.YMost();
  1263       prevRowFrame = rowFrame;
  1264       // see if there is a page break after the row
  1265       nsTableRowFrame* nextRow = rowFrame->GetNextRow();
  1266       if (nextRow && nsTableFrame::PageBreakAfter(rowFrame, nextRow)) {
  1267         PushChildren(nextRow, rowFrame);
  1268         aStatus = NS_FRAME_NOT_COMPLETE;
  1269         break;
  1272     // after the 1st row that has a height, we can't be on top
  1273     // of the page anymore.
  1274     isTopOfPage = isTopOfPage && rowRect.YMost() == 0;
  1276   return NS_OK;
  1279 /** Layout the entire row group.
  1280   * This method stacks rows vertically according to HTML 4.0 rules.
  1281   * Rows are responsible for layout of their children.
  1282   */
  1283 nsresult
  1284 nsTableRowGroupFrame::Reflow(nsPresContext*           aPresContext,
  1285                              nsHTMLReflowMetrics&     aDesiredSize,
  1286                              const nsHTMLReflowState& aReflowState,
  1287                              nsReflowStatus&          aStatus)
  1289   DO_GLOBAL_REFLOW_COUNT("nsTableRowGroupFrame");
  1290   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
  1292   nsresult rv = NS_OK;
  1293   aStatus     = NS_FRAME_COMPLETE;
  1295   // Row geometry may be going to change so we need to invalidate any row cursor.
  1296   ClearRowCursor();
  1298   // see if a special height reflow needs to occur due to having a pct height
  1299   nsTableFrame::CheckRequestSpecialHeightReflow(aReflowState);
  1301   nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
  1302   nsRowGroupReflowState state(aReflowState, tableFrame);
  1303   const nsStyleVisibility* groupVis = StyleVisibility();
  1304   bool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE == groupVis->mVisible);
  1305   if (collapseGroup) {
  1306     tableFrame->SetNeedToCollapse(true);
  1309   // Check for an overflow list
  1310   MoveOverflowToChildList();
  1312   // Reflow the existing frames. 
  1313   bool splitDueToPageBreak = false;
  1314   rv = ReflowChildren(aPresContext, aDesiredSize, state, aStatus,
  1315                       &splitDueToPageBreak);
  1317   // See if all the frames fit. Do not try to split anything if we're
  1318   // not paginated ... we can't split across columns yet.
  1319   if (aReflowState.mFlags.mTableIsSplittable &&
  1320       NS_UNCONSTRAINEDSIZE != aReflowState.AvailableHeight() &&
  1321       (NS_FRAME_NOT_COMPLETE == aStatus || splitDueToPageBreak || 
  1322        aDesiredSize.Height() > aReflowState.AvailableHeight())) {
  1323     // Nope, find a place to split the row group 
  1324     bool specialReflow = (bool)aReflowState.mFlags.mSpecialHeightReflow;
  1325     ((nsHTMLReflowState::ReflowStateFlags&)aReflowState.mFlags).mSpecialHeightReflow = false;
  1327     SplitRowGroup(aPresContext, aDesiredSize, aReflowState, tableFrame, aStatus,
  1328                   splitDueToPageBreak);
  1330     ((nsHTMLReflowState::ReflowStateFlags&)aReflowState.mFlags).mSpecialHeightReflow = specialReflow;
  1333   // XXXmats The following is just bogus.  We leave it here for now because
  1334   // ReflowChildren should pull up rows from our next-in-flow before returning
  1335   // a Complete status, but doesn't (bug 804888).
  1336   if (GetNextInFlow() && GetNextInFlow()->GetFirstPrincipalChild()) {
  1337     NS_FRAME_SET_INCOMPLETE(aStatus);
  1340   SetHasStyleHeight((NS_UNCONSTRAINEDSIZE != aReflowState.ComputedHeight()) &&
  1341                     (aReflowState.ComputedHeight() > 0)); 
  1343   // just set our width to what was available. The table will calculate the width and not use our value.
  1344   aDesiredSize.Width() = aReflowState.AvailableWidth();
  1346   aDesiredSize.UnionOverflowAreasWithDesiredBounds();
  1348   // If our parent is in initial reflow, it'll handle invalidating our
  1349   // entire overflow rect.
  1350   if (!(GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW) &&
  1351       nsSize(aDesiredSize.Width(), aDesiredSize.Height()) != mRect.Size()) {
  1352     InvalidateFrame();
  1355   FinishAndStoreOverflow(&aDesiredSize);
  1356   NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
  1357   return rv;
  1360 bool
  1361 nsTableRowGroupFrame::UpdateOverflow()
  1363   // Row cursor invariants depend on the visual overflow area of the rows,
  1364   // which may have changed, so we need to clear the cursor now.
  1365   ClearRowCursor();
  1366   return nsContainerFrame::UpdateOverflow();
  1369 /* virtual */ void
  1370 nsTableRowGroupFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
  1372   nsContainerFrame::DidSetStyleContext(aOldStyleContext);
  1374   if (!aOldStyleContext) //avoid this on init
  1375     return;
  1377   nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
  1378   if (tableFrame->IsBorderCollapse() &&
  1379       tableFrame->BCRecalcNeeded(aOldStyleContext, StyleContext())) {
  1380     nsIntRect damageArea(0, GetStartRowIndex(), tableFrame->GetColCount(),
  1381                          GetRowCount());
  1382     tableFrame->AddBCDamageArea(damageArea);
  1386 nsresult
  1387 nsTableRowGroupFrame::AppendFrames(ChildListID     aListID,
  1388                                    nsFrameList&    aFrameList)
  1390   NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
  1392   ClearRowCursor();
  1394   // collect the new row frames in an array
  1395   // XXXbz why are we doing the QI stuff?  There shouldn't be any non-rows here.
  1396   nsAutoTArray<nsTableRowFrame*, 8> rows;
  1397   for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) {
  1398     nsTableRowFrame *rowFrame = do_QueryFrame(e.get());
  1399     NS_ASSERTION(rowFrame, "Unexpected frame; frame constructor screwed up");
  1400     if (rowFrame) {
  1401       NS_ASSERTION(NS_STYLE_DISPLAY_TABLE_ROW ==
  1402                      e.get()->StyleDisplay()->mDisplay,
  1403                    "wrong display type on rowframe");      
  1404       rows.AppendElement(rowFrame);
  1408   int32_t rowIndex = GetRowCount();
  1409   // Append the frames to the sibling chain
  1410   mFrames.AppendFrames(nullptr, aFrameList);
  1412   if (rows.Length() > 0) {
  1413     nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
  1414     tableFrame->AppendRows(this, rowIndex, rows);
  1415     PresContext()->PresShell()->
  1416       FrameNeedsReflow(this, nsIPresShell::eTreeChange,
  1417                        NS_FRAME_HAS_DIRTY_CHILDREN);
  1418     tableFrame->SetGeometryDirty();
  1421   return NS_OK;
  1424 nsresult
  1425 nsTableRowGroupFrame::InsertFrames(ChildListID     aListID,
  1426                                    nsIFrame*       aPrevFrame,
  1427                                    nsFrameList&    aFrameList)
  1429   NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
  1430   NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
  1431                "inserting after sibling frame with different parent");
  1433   ClearRowCursor();
  1435   // collect the new row frames in an array
  1436   // XXXbz why are we doing the QI stuff?  There shouldn't be any non-rows here.
  1437   nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
  1438   nsTArray<nsTableRowFrame*> rows;
  1439   bool gotFirstRow = false;
  1440   for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) {
  1441     nsTableRowFrame *rowFrame = do_QueryFrame(e.get());
  1442     NS_ASSERTION(rowFrame, "Unexpected frame; frame constructor screwed up");
  1443     if (rowFrame) {
  1444       NS_ASSERTION(NS_STYLE_DISPLAY_TABLE_ROW ==
  1445                      e.get()->StyleDisplay()->mDisplay,
  1446                    "wrong display type on rowframe");      
  1447       rows.AppendElement(rowFrame);
  1448       if (!gotFirstRow) {
  1449         rowFrame->SetFirstInserted(true);
  1450         gotFirstRow = true;
  1451         tableFrame->SetRowInserted(true);
  1456   int32_t startRowIndex = GetStartRowIndex();
  1457   // Insert the frames in the sibling chain
  1458   mFrames.InsertFrames(nullptr, aPrevFrame, aFrameList);
  1460   int32_t numRows = rows.Length();
  1461   if (numRows > 0) {
  1462     nsTableRowFrame* prevRow = (nsTableRowFrame *)nsTableFrame::GetFrameAtOrBefore(this, aPrevFrame, nsGkAtoms::tableRowFrame);
  1463     int32_t rowIndex = (prevRow) ? prevRow->GetRowIndex() + 1 : startRowIndex;
  1464     tableFrame->InsertRows(this, rows, rowIndex, true);
  1466     PresContext()->PresShell()->
  1467       FrameNeedsReflow(this, nsIPresShell::eTreeChange,
  1468                        NS_FRAME_HAS_DIRTY_CHILDREN);
  1469     tableFrame->SetGeometryDirty();
  1471   return NS_OK;
  1474 nsresult
  1475 nsTableRowGroupFrame::RemoveFrame(ChildListID     aListID,
  1476                                   nsIFrame*       aOldFrame)
  1478   NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
  1480   ClearRowCursor();
  1482   nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
  1483   // XXX why are we doing the QI stuff?  There shouldn't be any non-rows here.
  1484   nsTableRowFrame* rowFrame = do_QueryFrame(aOldFrame);
  1485   if (rowFrame) {
  1486     // remove the rows from the table (and flag a rebalance)
  1487     tableFrame->RemoveRows(*rowFrame, 1, true);
  1489     PresContext()->PresShell()->
  1490       FrameNeedsReflow(this, nsIPresShell::eTreeChange,
  1491                        NS_FRAME_HAS_DIRTY_CHILDREN);
  1492     tableFrame->SetGeometryDirty();
  1494   mFrames.DestroyFrame(aOldFrame);
  1496   return NS_OK;
  1499 /* virtual */ nsMargin
  1500 nsTableRowGroupFrame::GetUsedMargin() const
  1502   return nsMargin(0,0,0,0);
  1505 /* virtual */ nsMargin
  1506 nsTableRowGroupFrame::GetUsedBorder() const
  1508   return nsMargin(0,0,0,0);
  1511 /* virtual */ nsMargin
  1512 nsTableRowGroupFrame::GetUsedPadding() const
  1514   return nsMargin(0,0,0,0);
  1517 nscoord 
  1518 nsTableRowGroupFrame::GetHeightBasis(const nsHTMLReflowState& aReflowState)
  1520   nscoord result = 0;
  1521   nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
  1522   if ((aReflowState.ComputedHeight() > 0) && (aReflowState.ComputedHeight() < NS_UNCONSTRAINEDSIZE)) {
  1523     nscoord cellSpacing = std::max(0, GetRowCount() - 1) * tableFrame->GetCellSpacingY();
  1524     result = aReflowState.ComputedHeight() - cellSpacing;
  1526   else {
  1527     const nsHTMLReflowState* parentRS = aReflowState.parentReflowState;
  1528     if (parentRS && (tableFrame != parentRS->frame)) {
  1529       parentRS = parentRS->parentReflowState;
  1531     if (parentRS && (tableFrame == parentRS->frame) && 
  1532         (parentRS->ComputedHeight() > 0) && (parentRS->ComputedHeight() < NS_UNCONSTRAINEDSIZE)) {
  1533       nscoord cellSpacing = std::max(0, tableFrame->GetRowCount() + 1) * tableFrame->GetCellSpacingY();
  1534       result = parentRS->ComputedHeight() - cellSpacing;
  1538   return result;
  1541 bool
  1542 nsTableRowGroupFrame::IsSimpleRowFrame(nsTableFrame* aTableFrame,
  1543                                        nsIFrame*     aFrame)
  1545   // Make sure it's a row frame and not a row group frame
  1546   nsTableRowFrame *rowFrame = do_QueryFrame(aFrame);
  1547   if (rowFrame) {
  1548     int32_t rowIndex = rowFrame->GetRowIndex();
  1550     // It's a simple row frame if there are no cells that span into or
  1551     // across the row
  1552     int32_t numEffCols = aTableFrame->GetEffectiveColCount();
  1553     if (!aTableFrame->RowIsSpannedInto(rowIndex, numEffCols) &&
  1554         !aTableFrame->RowHasSpanningCells(rowIndex, numEffCols)) {
  1555       return true;
  1559   return false;
  1562 nsIAtom*
  1563 nsTableRowGroupFrame::GetType() const
  1565   return nsGkAtoms::tableRowGroupFrame;
  1568 /** find page break before the first row **/
  1569 bool 
  1570 nsTableRowGroupFrame::HasInternalBreakBefore() const
  1572  nsIFrame* firstChild = mFrames.FirstChild(); 
  1573   if (!firstChild)
  1574     return false;
  1575   return firstChild->StyleDisplay()->mBreakBefore;
  1578 /** find page break after the last row **/
  1579 bool 
  1580 nsTableRowGroupFrame::HasInternalBreakAfter() const
  1582   nsIFrame* lastChild = mFrames.LastChild(); 
  1583   if (!lastChild)
  1584     return false;
  1585   return lastChild->StyleDisplay()->mBreakAfter;
  1587 /* ----- global methods ----- */
  1589 nsIFrame*
  1590 NS_NewTableRowGroupFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
  1592   return new (aPresShell) nsTableRowGroupFrame(aContext);
  1595 NS_IMPL_FRAMEARENA_HELPERS(nsTableRowGroupFrame)
  1597 #ifdef DEBUG_FRAME_DUMP
  1598 nsresult
  1599 nsTableRowGroupFrame::GetFrameName(nsAString& aResult) const
  1601   return MakeFrameName(NS_LITERAL_STRING("TableRowGroup"), aResult);
  1603 #endif
  1605 nsMargin* 
  1606 nsTableRowGroupFrame::GetBCBorderWidth(nsMargin& aBorder)
  1608   aBorder.left = aBorder.right = aBorder.top = aBorder.bottom = 0;
  1610   nsTableRowFrame* firstRowFrame = nullptr;
  1611   nsTableRowFrame* lastRowFrame = nullptr;
  1612   for (nsTableRowFrame* rowFrame = GetFirstRow(); rowFrame; rowFrame = rowFrame->GetNextRow()) {
  1613     if (!firstRowFrame) {
  1614       firstRowFrame = rowFrame;
  1616     lastRowFrame = rowFrame;
  1618   if (firstRowFrame) {
  1619     aBorder.top    = nsPresContext::CSSPixelsToAppUnits(firstRowFrame->GetTopBCBorderWidth());
  1620     aBorder.bottom = nsPresContext::CSSPixelsToAppUnits(lastRowFrame->GetBottomBCBorderWidth());
  1623   return &aBorder;
  1626 void nsTableRowGroupFrame::SetContinuousBCBorderWidth(uint8_t     aForSide,
  1627                                                       BCPixelSize aPixelValue)
  1629   switch (aForSide) {
  1630     case NS_SIDE_RIGHT:
  1631       mRightContBorderWidth = aPixelValue;
  1632       return;
  1633     case NS_SIDE_BOTTOM:
  1634       mBottomContBorderWidth = aPixelValue;
  1635       return;
  1636     case NS_SIDE_LEFT:
  1637       mLeftContBorderWidth = aPixelValue;
  1638       return;
  1639     default:
  1640       NS_ERROR("invalid NS_SIDE argument");
  1644 //nsILineIterator methods
  1645 int32_t
  1646 nsTableRowGroupFrame::GetNumLines()
  1648   return GetRowCount();
  1651 bool
  1652 nsTableRowGroupFrame::GetDirection()
  1654   nsTableFrame* table = nsTableFrame::GetTableFrame(this);
  1655   return (NS_STYLE_DIRECTION_RTL ==
  1656           table->StyleVisibility()->mDirection);
  1659 NS_IMETHODIMP
  1660 nsTableRowGroupFrame::GetLine(int32_t    aLineNumber, 
  1661                               nsIFrame** aFirstFrameOnLine, 
  1662                               int32_t*   aNumFramesOnLine,
  1663                               nsRect&    aLineBounds, 
  1664                               uint32_t*  aLineFlags)
  1666   NS_ENSURE_ARG_POINTER(aFirstFrameOnLine);
  1667   NS_ENSURE_ARG_POINTER(aNumFramesOnLine);
  1668   NS_ENSURE_ARG_POINTER(aLineFlags);
  1670   nsTableFrame* table = nsTableFrame::GetTableFrame(this);
  1671   nsTableCellMap* cellMap = table->GetCellMap();
  1673   *aLineFlags = 0;
  1674   *aFirstFrameOnLine = nullptr;
  1675   *aNumFramesOnLine = 0;
  1676   aLineBounds.SetRect(0, 0, 0, 0);
  1678   if ((aLineNumber < 0) || (aLineNumber >=  GetRowCount())) {
  1679     return NS_OK;
  1681   aLineNumber += GetStartRowIndex(); 
  1683   *aNumFramesOnLine = cellMap->GetNumCellsOriginatingInRow(aLineNumber);
  1684   if (*aNumFramesOnLine == 0) {
  1685     return NS_OK;
  1687   int32_t colCount = table->GetColCount();
  1688   for (int32_t i = 0; i < colCount; i++) {
  1689     CellData* data = cellMap->GetDataAt(aLineNumber, i);
  1690     if (data && data->IsOrig()) {
  1691       *aFirstFrameOnLine = (nsIFrame*)data->GetCellFrame();
  1692       nsIFrame* parent = (*aFirstFrameOnLine)->GetParent();
  1693       aLineBounds = parent->GetRect();
  1694       return NS_OK;
  1697   NS_ERROR("cellmap is lying");
  1698   return NS_ERROR_FAILURE;
  1701 int32_t
  1702 nsTableRowGroupFrame::FindLineContaining(nsIFrame* aFrame, int32_t aStartLine)
  1704   NS_ENSURE_TRUE(aFrame, -1);
  1706   nsTableRowFrame *rowFrame = do_QueryFrame(aFrame);
  1707   NS_ASSERTION(rowFrame, "RowGroup contains a frame that is not a row");
  1709   int32_t rowIndexInGroup = rowFrame->GetRowIndex() - GetStartRowIndex();
  1711   return rowIndexInGroup >= aStartLine ? rowIndexInGroup : -1;
  1714 NS_IMETHODIMP
  1715 nsTableRowGroupFrame::CheckLineOrder(int32_t                  aLine,
  1716                                      bool                     *aIsReordered,
  1717                                      nsIFrame                 **aFirstVisual,
  1718                                      nsIFrame                 **aLastVisual)
  1720   *aIsReordered = false;
  1721   *aFirstVisual = nullptr;
  1722   *aLastVisual = nullptr;
  1723   return NS_OK;
  1726 NS_IMETHODIMP
  1727 nsTableRowGroupFrame::FindFrameAt(int32_t    aLineNumber, 
  1728                                   nscoord    aX, 
  1729                                   nsIFrame** aFrameFound,
  1730                                   bool*    aXIsBeforeFirstFrame, 
  1731                                   bool*    aXIsAfterLastFrame)
  1733    nsTableFrame* table = nsTableFrame::GetTableFrame(this);
  1734    nsTableCellMap* cellMap = table->GetCellMap();
  1736    *aFrameFound = nullptr;
  1737    *aXIsBeforeFirstFrame = true;
  1738    *aXIsAfterLastFrame = false;
  1740    aLineNumber += GetStartRowIndex();
  1741    int32_t numCells = cellMap->GetNumCellsOriginatingInRow(aLineNumber);
  1742    if (numCells == 0) {
  1743      return NS_OK;
  1746    nsIFrame* frame = nullptr;
  1747    int32_t colCount = table->GetColCount();
  1748    for (int32_t i = 0; i < colCount; i++) {
  1749      CellData* data = cellMap->GetDataAt(aLineNumber, i);
  1750      if (data && data->IsOrig()) {
  1751        frame = (nsIFrame*)data->GetCellFrame();
  1752        break;
  1755    NS_ASSERTION(frame, "cellmap is lying");
  1756    bool isRTL = (NS_STYLE_DIRECTION_RTL ==
  1757                    table->StyleVisibility()->mDirection);
  1759    nsIFrame* closestFromLeft = nullptr;
  1760    nsIFrame* closestFromRight = nullptr;
  1761    int32_t n = numCells;
  1762    nsIFrame* firstFrame = frame;
  1763    while (n--) {
  1764      nsRect rect = frame->GetRect();
  1765      if (rect.width > 0) {
  1766        // If aX is inside this frame - this is it
  1767        if (rect.x <= aX && rect.XMost() > aX) {
  1768          closestFromLeft = closestFromRight = frame;
  1769          break;
  1771        if (rect.x < aX) {
  1772          if (!closestFromLeft ||
  1773              rect.XMost() > closestFromLeft->GetRect().XMost())
  1774            closestFromLeft = frame;
  1776        else {
  1777          if (!closestFromRight ||
  1778              rect.x < closestFromRight->GetRect().x)
  1779            closestFromRight = frame;
  1782      frame = frame->GetNextSibling();
  1784    if (!closestFromLeft && !closestFromRight) {
  1785      // All frames were zero-width. Just take the first one.
  1786      closestFromLeft = closestFromRight = firstFrame;
  1788    *aXIsBeforeFirstFrame = isRTL ? !closestFromRight : !closestFromLeft;
  1789    *aXIsAfterLastFrame =   isRTL ? !closestFromLeft : !closestFromRight;
  1790    if (closestFromLeft == closestFromRight) {
  1791      *aFrameFound = closestFromLeft;
  1793    else if (!closestFromLeft) {
  1794      *aFrameFound = closestFromRight;
  1796    else if (!closestFromRight) {
  1797      *aFrameFound = closestFromLeft;
  1799    else { // we're between two frames
  1800      nscoord delta = closestFromRight->GetRect().x -
  1801                      closestFromLeft->GetRect().XMost();
  1802      if (aX < closestFromLeft->GetRect().XMost() + delta/2)
  1803        *aFrameFound = closestFromLeft;
  1804      else
  1805        *aFrameFound = closestFromRight;
  1807   return NS_OK;
  1810 NS_IMETHODIMP
  1811 nsTableRowGroupFrame::GetNextSiblingOnLine(nsIFrame*& aFrame, 
  1812                                            int32_t    aLineNumber)
  1814   NS_ENSURE_ARG_POINTER(aFrame);
  1815   aFrame = aFrame->GetNextSibling();
  1816   return NS_OK;
  1819 //end nsLineIterator methods
  1821 static void
  1822 DestroyFrameCursorData(void* aPropertyValue)
  1824   delete static_cast<nsTableRowGroupFrame::FrameCursorData*>(aPropertyValue);
  1827 NS_DECLARE_FRAME_PROPERTY(RowCursorProperty, DestroyFrameCursorData)
  1829 void
  1830 nsTableRowGroupFrame::ClearRowCursor()
  1832   if (!(GetStateBits() & NS_ROWGROUP_HAS_ROW_CURSOR))
  1833     return;
  1835   RemoveStateBits(NS_ROWGROUP_HAS_ROW_CURSOR);
  1836   Properties().Delete(RowCursorProperty());
  1839 nsTableRowGroupFrame::FrameCursorData*
  1840 nsTableRowGroupFrame::SetupRowCursor()
  1842   if (GetStateBits() & NS_ROWGROUP_HAS_ROW_CURSOR) {
  1843     // We already have a valid row cursor. Don't waste time rebuilding it.
  1844     return nullptr;
  1847   nsIFrame* f = mFrames.FirstChild();
  1848   int32_t count;
  1849   for (count = 0; f && count < MIN_ROWS_NEEDING_CURSOR; ++count) {
  1850     f = f->GetNextSibling();
  1852   if (!f) {
  1853     // Less than MIN_ROWS_NEEDING_CURSOR rows, so just don't bother
  1854     return nullptr;
  1857   FrameCursorData* data = new FrameCursorData();
  1858   if (!data)
  1859     return nullptr;
  1860   Properties().Set(RowCursorProperty(), data);
  1861   AddStateBits(NS_ROWGROUP_HAS_ROW_CURSOR);
  1862   return data;
  1865 nsIFrame*
  1866 nsTableRowGroupFrame::GetFirstRowContaining(nscoord aY, nscoord* aOverflowAbove)
  1868   if (!(GetStateBits() & NS_ROWGROUP_HAS_ROW_CURSOR))
  1869     return nullptr;
  1871   FrameCursorData* property = static_cast<FrameCursorData*>
  1872     (Properties().Get(RowCursorProperty()));
  1873   uint32_t cursorIndex = property->mCursorIndex;
  1874   uint32_t frameCount = property->mFrames.Length();
  1875   if (cursorIndex >= frameCount)
  1876     return nullptr;
  1877   nsIFrame* cursorFrame = property->mFrames[cursorIndex];
  1879   // The cursor's frame list excludes frames with empty overflow-area, so
  1880   // we don't need to check that here.
  1882   // We use property->mOverflowBelow here instead of computing the frame's
  1883   // true overflowArea.YMost(), because it is essential for the thresholds
  1884   // to form a monotonically increasing sequence. Otherwise we would break
  1885   // encountering a row whose overflowArea.YMost() is <= aY but which has
  1886   // a row above it containing cell(s) that span to include aY.
  1887   while (cursorIndex > 0 &&
  1888          cursorFrame->GetRect().YMost() + property->mOverflowBelow > aY) {
  1889     --cursorIndex;
  1890     cursorFrame = property->mFrames[cursorIndex];
  1892   while (cursorIndex + 1 < frameCount &&
  1893          cursorFrame->GetRect().YMost() + property->mOverflowBelow <= aY) {
  1894     ++cursorIndex;
  1895     cursorFrame = property->mFrames[cursorIndex];
  1898   property->mCursorIndex = cursorIndex;
  1899   *aOverflowAbove = property->mOverflowAbove;
  1900   return cursorFrame;
  1903 bool
  1904 nsTableRowGroupFrame::FrameCursorData::AppendFrame(nsIFrame* aFrame)
  1906   nsRect overflowRect = aFrame->GetVisualOverflowRect();
  1907   if (overflowRect.IsEmpty())
  1908     return true;
  1909   nscoord overflowAbove = -overflowRect.y;
  1910   nscoord overflowBelow = overflowRect.YMost() - aFrame->GetSize().height;
  1911   mOverflowAbove = std::max(mOverflowAbove, overflowAbove);
  1912   mOverflowBelow = std::max(mOverflowBelow, overflowBelow);
  1913   return mFrames.AppendElement(aFrame) != nullptr;
  1916 void 
  1917 nsTableRowGroupFrame::InvalidateFrame(uint32_t aDisplayItemKey)
  1919   nsIFrame::InvalidateFrame(aDisplayItemKey);
  1920   GetParent()->InvalidateFrameWithRect(GetVisualOverflowRect() + GetPosition(), aDisplayItemKey);
  1923 void 
  1924 nsTableRowGroupFrame::InvalidateFrameWithRect(const nsRect& aRect, uint32_t aDisplayItemKey)
  1926   nsIFrame::InvalidateFrameWithRect(aRect, aDisplayItemKey);
  1927   // If we have filters applied that would affects our bounds, then
  1928   // we get an inactive layer created and this is computed
  1929   // within FrameLayerBuilder
  1930   GetParent()->InvalidateFrameWithRect(aRect + GetPosition(), aDisplayItemKey);

mercurial