tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsTableRowGroupFrame.cpp (74651B)


      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 "nsTableRowGroupFrame.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "mozilla/ComputedStyle.h"
     10 #include "mozilla/PresShell.h"
     11 #include "mozilla/StaticPrefs_layout.h"
     12 #include "nsCOMPtr.h"
     13 #include "nsCSSFrameConstructor.h"
     14 #include "nsCSSRendering.h"
     15 #include "nsCellMap.h"  //table cell navigation
     16 #include "nsDisplayList.h"
     17 #include "nsGkAtoms.h"
     18 #include "nsHTMLParts.h"
     19 #include "nsIContent.h"
     20 #include "nsIFrame.h"
     21 #include "nsIFrameInlines.h"
     22 #include "nsPresContext.h"
     23 #include "nsStyleConsts.h"
     24 #include "nsTableCellFrame.h"
     25 #include "nsTableFrame.h"
     26 #include "nsTableRowFrame.h"
     27 
     28 using namespace mozilla;
     29 using namespace mozilla::layout;
     30 
     31 namespace mozilla {
     32 
     33 struct TableRowGroupReflowInput final {
     34  // Our reflow input
     35  const ReflowInput& mReflowInput;
     36 
     37  // The available size (computed from the parent)
     38  LogicalSize mAvailSize;
     39 
     40  // Running block-offset
     41  nscoord mBCoord = 0;
     42 
     43  explicit TableRowGroupReflowInput(const ReflowInput& aReflowInput)
     44      : mReflowInput(aReflowInput), mAvailSize(aReflowInput.AvailableSize()) {}
     45 
     46  ~TableRowGroupReflowInput() = default;
     47 };
     48 
     49 }  // namespace mozilla
     50 
     51 nsTableRowGroupFrame::nsTableRowGroupFrame(ComputedStyle* aStyle,
     52                                           nsPresContext* aPresContext)
     53    : nsContainerFrame(aStyle, aPresContext, kClassID) {
     54  SetRepeatable(false);
     55 }
     56 
     57 nsTableRowGroupFrame::~nsTableRowGroupFrame() = default;
     58 
     59 void nsTableRowGroupFrame::Destroy(DestroyContext& aContext) {
     60  nsTableFrame::MaybeUnregisterPositionedTablePart(this);
     61  nsContainerFrame::Destroy(aContext);
     62 }
     63 
     64 NS_QUERYFRAME_HEAD(nsTableRowGroupFrame)
     65  NS_QUERYFRAME_ENTRY(nsTableRowGroupFrame)
     66 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
     67 
     68 int32_t nsTableRowGroupFrame::GetRowCount() const {
     69 #ifdef DEBUG
     70  for (nsIFrame* f : mFrames) {
     71    NS_ASSERTION(f->StyleDisplay()->mDisplay == mozilla::StyleDisplay::TableRow,
     72                 "Unexpected display");
     73    NS_ASSERTION(f->IsTableRowFrame(), "Unexpected frame type");
     74  }
     75 #endif
     76 
     77  return mFrames.GetLength();
     78 }
     79 
     80 int32_t nsTableRowGroupFrame::GetStartRowIndex() const {
     81  int32_t result = -1;
     82  if (mFrames.NotEmpty()) {
     83    NS_ASSERTION(mFrames.FirstChild()->IsTableRowFrame(),
     84                 "Unexpected frame type");
     85    result = static_cast<nsTableRowFrame*>(mFrames.FirstChild())->GetRowIndex();
     86  }
     87  // if the row group doesn't have any children, get it the hard way
     88  if (-1 == result) {
     89    return GetTableFrame()->GetStartRowIndex(this);
     90  }
     91 
     92  return result;
     93 }
     94 
     95 void nsTableRowGroupFrame::AdjustRowIndices(int32_t aRowIndex,
     96                                            int32_t anAdjustment) {
     97  for (nsIFrame* rowFrame : mFrames) {
     98    if (mozilla::StyleDisplay::TableRow == rowFrame->StyleDisplay()->mDisplay) {
     99      int32_t index = ((nsTableRowFrame*)rowFrame)->GetRowIndex();
    100      if (index >= aRowIndex) {
    101        ((nsTableRowFrame*)rowFrame)->SetRowIndex(index + anAdjustment);
    102      }
    103    }
    104  }
    105 }
    106 
    107 int32_t nsTableRowGroupFrame::GetAdjustmentForStoredIndex(
    108    int32_t aStoredIndex) {
    109  nsTableFrame* tableFrame = GetTableFrame();
    110  return tableFrame->GetAdjustmentForStoredIndex(aStoredIndex);
    111 }
    112 
    113 void nsTableRowGroupFrame::MarkRowsAsDeleted(nsTableRowFrame& aStartRowFrame,
    114                                             int32_t aNumRowsToDelete) {
    115  nsTableRowFrame* currentRowFrame = &aStartRowFrame;
    116  for (;;) {
    117    // XXXneerja - Instead of calling AddDeletedRowIndex() per row frame
    118    // it is possible to change AddDeleteRowIndex to instead take
    119    // <start row index> and <num of rows to mark for deletion> as arguments.
    120    // The problem that emerges here is mDeletedRowIndexRanges only stores
    121    // disjoint index ranges and since AddDeletedRowIndex() must operate on
    122    // the "stored" index, in some cases it is possible that the range
    123    // of indices to delete becomes overlapping EG: Deleting rows 9 - 11 and
    124    // then from the remaining rows deleting the *new* rows 7 to 20.
    125    // Handling these overlapping ranges is much more complicated to
    126    // implement and so I opted to add the deleted row index of one row at a
    127    // time and maintain the invariant that the range of deleted row indices
    128    // is always disjoint.
    129    currentRowFrame->AddDeletedRowIndex();
    130    if (--aNumRowsToDelete == 0) {
    131      break;
    132    }
    133    currentRowFrame = do_QueryFrame(currentRowFrame->GetNextSibling());
    134    if (!currentRowFrame) {
    135      MOZ_ASSERT_UNREACHABLE("expected another row frame");
    136      break;
    137    }
    138  }
    139 }
    140 
    141 void nsTableRowGroupFrame::AddDeletedRowIndex(int32_t aDeletedRowStoredIndex) {
    142  nsTableFrame* tableFrame = GetTableFrame();
    143  return tableFrame->AddDeletedRowIndex(aDeletedRowStoredIndex);
    144 }
    145 
    146 void nsTableRowGroupFrame::InitRepeatedFrame(
    147    nsTableRowGroupFrame* aHeaderFooterFrame) {
    148  nsTableRowFrame* copyRowFrame = GetFirstRow();
    149  nsTableRowFrame* originalRowFrame = aHeaderFooterFrame->GetFirstRow();
    150  AddStateBits(NS_REPEATED_ROW_OR_ROWGROUP);
    151  while (copyRowFrame && originalRowFrame) {
    152    copyRowFrame->AddStateBits(NS_REPEATED_ROW_OR_ROWGROUP);
    153    int rowIndex = originalRowFrame->GetRowIndex();
    154    copyRowFrame->SetRowIndex(rowIndex);
    155 
    156    // For each table cell frame set its column index
    157    nsTableCellFrame* originalCellFrame = originalRowFrame->GetFirstCell();
    158    nsTableCellFrame* copyCellFrame = copyRowFrame->GetFirstCell();
    159    while (copyCellFrame && originalCellFrame) {
    160      NS_ASSERTION(
    161          originalCellFrame->GetContent() == copyCellFrame->GetContent(),
    162          "cell frames have different content");
    163      uint32_t colIndex = originalCellFrame->ColIndex();
    164      copyCellFrame->SetColIndex(colIndex);
    165 
    166      // Move to the next cell frame
    167      copyCellFrame = copyCellFrame->GetNextCell();
    168      originalCellFrame = originalCellFrame->GetNextCell();
    169    }
    170 
    171    // Move to the next row frame
    172    originalRowFrame = originalRowFrame->GetNextRow();
    173    copyRowFrame = copyRowFrame->GetNextRow();
    174  }
    175 }
    176 
    177 // Handle the child-traversal part of DisplayGenericTablePart
    178 static void DisplayRows(nsDisplayListBuilder* aBuilder,
    179                        nsTableRowGroupFrame* aFrame,
    180                        const nsDisplayListSet& aLists) {
    181  if (aFrame->HidesContent()) {
    182    return;
    183  }
    184  nscoord overflowAbove;
    185  // Don't try to use the row cursor if we have to descend into placeholders;
    186  // we might have rows containing placeholders, where the row's overflow
    187  // area doesn't intersect the dirty rect but we need to descend into the row
    188  // to see out of flows.
    189  // Note that we really want to check ShouldDescendIntoFrame for all
    190  // the rows in |f|, but that's exactly what we're trying to avoid, so we
    191  // approximate it by checking it for |f|: if it's true for any row
    192  // in |f| then it's true for |f| itself.
    193  nsIFrame* kid = aBuilder->ShouldDescendIntoFrame(aFrame, true)
    194                      ? nullptr
    195                      : aFrame->GetFirstRowContaining(
    196                            aBuilder->GetVisibleRect().y, &overflowAbove);
    197 
    198  if (kid) {
    199    // have a cursor, use it
    200    while (kid) {
    201      if (kid->GetRect().y - overflowAbove >=
    202          aBuilder->GetVisibleRect().YMost()) {
    203        break;
    204      }
    205      aFrame->BuildDisplayListForChild(aBuilder, kid, aLists);
    206      kid = kid->GetNextSibling();
    207    }
    208    return;
    209  }
    210 
    211  // No cursor. Traverse children the hard way and build a cursor while we're at
    212  // it
    213  nsTableRowGroupFrame::FrameCursorData* cursor = aFrame->SetupRowCursor();
    214  kid = aFrame->PrincipalChildList().FirstChild();
    215  while (kid) {
    216    aFrame->BuildDisplayListForChild(aBuilder, kid, aLists);
    217 
    218    if (cursor) {
    219      if (!cursor->AppendFrame(kid)) {
    220        aFrame->ClearRowCursor();
    221        return;
    222      }
    223    }
    224 
    225    kid = kid->GetNextSibling();
    226  }
    227  if (cursor) {
    228    cursor->FinishBuildingCursor();
    229  }
    230 }
    231 
    232 void nsTableRowGroupFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
    233                                            const nsDisplayListSet& aLists) {
    234  DisplayOutsetBoxShadow(aBuilder, aLists.BorderBackground());
    235 
    236  for (nsTableRowFrame* row = GetFirstRow(); row; row = row->GetNextRow()) {
    237    if (!aBuilder->GetDirtyRect().Intersects(row->InkOverflowRect() +
    238                                             row->GetNormalPosition())) {
    239      continue;
    240    }
    241    row->PaintCellBackgroundsForFrame(this, aBuilder, aLists,
    242                                      row->GetNormalPosition());
    243  }
    244 
    245  DisplayInsetBoxShadow(aBuilder, aLists.BorderBackground());
    246 
    247  DisplayOutline(aBuilder, aLists);
    248 
    249  DisplayRows(aBuilder, this, aLists);
    250 }
    251 
    252 LogicalSides nsTableRowGroupFrame::GetLogicalSkipSides() const {
    253  LogicalSides skip(mWritingMode);
    254  if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==
    255                   StyleBoxDecorationBreak::Clone)) {
    256    return skip;
    257  }
    258 
    259  if (GetPrevInFlow()) {
    260    skip += LogicalSide::BStart;
    261  }
    262  if (GetNextInFlow()) {
    263    skip += LogicalSide::BEnd;
    264  }
    265  return skip;
    266 }
    267 
    268 // Position and size aKidFrame and update our reflow input.
    269 void nsTableRowGroupFrame::PlaceChild(
    270    nsPresContext* aPresContext, TableRowGroupReflowInput& aReflowInput,
    271    nsIFrame* aKidFrame, const ReflowInput& aKidReflowInput, WritingMode aWM,
    272    const LogicalPoint& aKidPosition, const nsSize& aContainerSize,
    273    ReflowOutput& aDesiredSize, const nsRect& aOriginalKidRect,
    274    const nsRect& aOriginalKidInkOverflow) {
    275  bool isFirstReflow = aKidFrame->HasAnyStateBits(NS_FRAME_FIRST_REFLOW);
    276 
    277  // Place and size the child
    278  FinishReflowChild(aKidFrame, aPresContext, aDesiredSize, &aKidReflowInput,
    279                    aWM, aKidPosition, aContainerSize,
    280                    ReflowChildFlags::ApplyRelativePositioning);
    281 
    282  nsTableFrame* tableFrame = GetTableFrame();
    283  if (tableFrame->IsBorderCollapse()) {
    284    nsTableFrame::InvalidateTableFrame(aKidFrame, aOriginalKidRect,
    285                                       aOriginalKidInkOverflow, isFirstReflow);
    286  }
    287 
    288  // Adjust the running block-offset
    289  aReflowInput.mBCoord += aDesiredSize.BSize(aWM);
    290 
    291  // If our block-size is constrained then update the available bsize
    292  if (NS_UNCONSTRAINEDSIZE != aReflowInput.mAvailSize.BSize(aWM)) {
    293    aReflowInput.mAvailSize.BSize(aWM) -= aDesiredSize.BSize(aWM);
    294  }
    295 }
    296 
    297 void nsTableRowGroupFrame::InitChildReflowInput(nsPresContext* aPresContext,
    298                                                bool aBorderCollapse,
    299                                                ReflowInput& aReflowInput) {
    300  const auto childWM = aReflowInput.GetWritingMode();
    301  LogicalMargin border(childWM);
    302  if (aBorderCollapse) {
    303    auto* rowFrame = static_cast<nsTableRowFrame*>(aReflowInput.mFrame);
    304    border = rowFrame->GetBCBorderWidth(childWM);
    305  }
    306  const LogicalMargin zeroPadding(childWM);
    307  aReflowInput.Init(aPresContext, Nothing(), Some(border), Some(zeroPadding));
    308 }
    309 
    310 static void CacheRowBSizesForPrinting(nsTableRowFrame* aFirstRow,
    311                                      WritingMode aWM) {
    312  for (nsTableRowFrame* row = aFirstRow; row; row = row->GetNextRow()) {
    313    if (!row->GetPrevInFlow()) {
    314      row->SetUnpaginatedBSize(row->BSize(aWM));
    315    }
    316  }
    317 }
    318 
    319 void nsTableRowGroupFrame::ReflowChildren(
    320    nsPresContext* aPresContext, ReflowOutput& aDesiredSize,
    321    TableRowGroupReflowInput& aReflowInput, nsReflowStatus& aStatus,
    322    bool* aPageBreakBeforeEnd) {
    323  if (aPageBreakBeforeEnd) {
    324    *aPageBreakBeforeEnd = false;
    325  }
    326 
    327  WritingMode wm = aReflowInput.mReflowInput.GetWritingMode();
    328  nsTableFrame* tableFrame = GetTableFrame();
    329  const bool borderCollapse = tableFrame->IsBorderCollapse();
    330 
    331  // XXXldb Should we really be checking IsPaginated(),
    332  // or should we *only* check available block-size?
    333  // (Think about multi-column layout!)
    334  bool isPaginated = aPresContext->IsPaginated() &&
    335                     NS_UNCONSTRAINEDSIZE != aReflowInput.mAvailSize.BSize(wm);
    336 
    337  bool reflowAllKids = aReflowInput.mReflowInput.ShouldReflowAllKids() ||
    338                       tableFrame->IsGeometryDirty() ||
    339                       tableFrame->NeedToCollapse();
    340 
    341  // in vertical-rl mode, we always need the row bsizes in order to
    342  // get the necessary containerSize for placing our kids
    343  bool needToCalcRowBSizes = reflowAllKids || wm.IsVerticalRL();
    344 
    345  nsSize containerSize =
    346      aReflowInput.mReflowInput.ComputedSizeAsContainerIfConstrained();
    347 
    348  nsIFrame* prevKidFrame = nullptr;
    349  for (nsTableRowFrame* kidFrame = GetFirstRow(); kidFrame;
    350       prevKidFrame = kidFrame, kidFrame = kidFrame->GetNextRow()) {
    351    const nscoord rowSpacing =
    352        tableFrame->GetRowSpacing(kidFrame->GetRowIndex());
    353 
    354    // Reflow the row frame
    355    if (reflowAllKids || kidFrame->IsSubtreeDirty() ||
    356        (aReflowInput.mReflowInput.mFlags.mSpecialBSizeReflow &&
    357         (isPaginated ||
    358          kidFrame->HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE)))) {
    359      LogicalRect oldKidRect = kidFrame->GetLogicalRect(wm, containerSize);
    360      nsRect oldKidInkOverflow = kidFrame->InkOverflowRect();
    361 
    362      ReflowOutput kidDesiredSize(aReflowInput.mReflowInput);
    363 
    364      // Reflow the child into the available space, giving it as much bsize as
    365      // it wants. We'll deal with splitting later after we've computed the row
    366      // bsizes, taking into account cells with row spans...
    367      LogicalSize kidAvailSize = aReflowInput.mAvailSize;
    368      kidAvailSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
    369      ReflowInput kidReflowInput(aPresContext, aReflowInput.mReflowInput,
    370                                 kidFrame, kidAvailSize, Nothing(),
    371                                 ReflowInput::InitFlag::CallerWillInit);
    372      InitChildReflowInput(aPresContext, borderCollapse, kidReflowInput);
    373 
    374      // This can indicate that columns were resized.
    375      if (aReflowInput.mReflowInput.IsIResize()) {
    376        kidReflowInput.SetIResize(true);
    377      }
    378 
    379      NS_ASSERTION(kidFrame == mFrames.FirstChild() || prevKidFrame,
    380                   "If we're not on the first frame, we should have a "
    381                   "previous sibling...");
    382      // If prev row has nonzero YMost, then we can't be at the top of the page
    383      if (prevKidFrame && prevKidFrame->GetNormalRect().YMost() > 0) {
    384        kidReflowInput.mFlags.mIsTopOfPage = false;
    385      }
    386 
    387      LogicalPoint kidPosition(wm, 0, aReflowInput.mBCoord);
    388      ReflowChild(kidFrame, aPresContext, kidDesiredSize, kidReflowInput, wm,
    389                  kidPosition, containerSize, ReflowChildFlags::Default,
    390                  aStatus);
    391 
    392      // Place the child
    393      PlaceChild(aPresContext, aReflowInput, kidFrame, kidReflowInput, wm,
    394                 kidPosition, containerSize, kidDesiredSize,
    395                 oldKidRect.GetPhysicalRect(wm, containerSize),
    396                 oldKidInkOverflow);
    397      aReflowInput.mBCoord += rowSpacing;
    398 
    399      if (!reflowAllKids) {
    400        if (IsSimpleRowFrame(tableFrame, kidFrame)) {
    401          // Inform the row of its new bsize.
    402          kidFrame->DidResize();
    403          // the overflow area may have changed inflate the overflow area
    404          const nsStylePosition* stylePos = StylePosition();
    405          if (tableFrame->IsAutoBSize(wm) &&
    406              !stylePos->BSize(wm, AnchorPosResolutionParams::From(this))
    407                   ->ConvertsToLength()) {
    408            // Because other cells in the row may need to be aligned
    409            // differently, repaint the entire row
    410            InvalidateFrame();
    411          } else if (oldKidRect.BSize(wm) != kidDesiredSize.BSize(wm)) {
    412            needToCalcRowBSizes = true;
    413          }
    414        } else {
    415          needToCalcRowBSizes = true;
    416        }
    417      }
    418 
    419      if (isPaginated && aPageBreakBeforeEnd && !*aPageBreakBeforeEnd) {
    420        nsTableRowFrame* nextRow = kidFrame->GetNextRow();
    421        if (nextRow) {
    422          *aPageBreakBeforeEnd =
    423              nsTableFrame::PageBreakAfter(kidFrame, nextRow);
    424        }
    425      }
    426    } else {
    427      // Move a child that was skipped during a reflow.
    428      const LogicalPoint oldPosition =
    429          kidFrame->GetLogicalNormalPosition(wm, containerSize);
    430      if (oldPosition.B(wm) != aReflowInput.mBCoord) {
    431        kidFrame->InvalidateFrameSubtree();
    432        const LogicalPoint offset(wm, 0,
    433                                  aReflowInput.mBCoord - oldPosition.B(wm));
    434        kidFrame->MovePositionBy(wm, offset);
    435        kidFrame->InvalidateFrameSubtree();
    436      }
    437 
    438      // Adjust the running b-offset so we know where the next row should be
    439      // placed
    440      nscoord bSize = kidFrame->BSize(wm) + rowSpacing;
    441      aReflowInput.mBCoord += bSize;
    442 
    443      if (NS_UNCONSTRAINEDSIZE != aReflowInput.mAvailSize.BSize(wm)) {
    444        aReflowInput.mAvailSize.BSize(wm) -= bSize;
    445      }
    446    }
    447    ConsiderChildOverflow(aDesiredSize.mOverflowAreas, kidFrame);
    448  }
    449 
    450  if (GetFirstRow()) {
    451    aReflowInput.mBCoord -=
    452        tableFrame->GetRowSpacing(GetStartRowIndex() + GetRowCount());
    453  }
    454 
    455  // Return our desired rect
    456  aDesiredSize.ISize(wm) = aReflowInput.mReflowInput.AvailableISize();
    457  aDesiredSize.BSize(wm) = aReflowInput.mBCoord;
    458 
    459  if (aReflowInput.mReflowInput.mFlags.mSpecialBSizeReflow) {
    460    DidResizeRows(aDesiredSize);
    461    if (isPaginated) {
    462      CacheRowBSizesForPrinting(GetFirstRow(), wm);
    463    }
    464  } else if (needToCalcRowBSizes) {
    465    CalculateRowBSizes(aPresContext, aDesiredSize, aReflowInput.mReflowInput);
    466    if (!reflowAllKids) {
    467      InvalidateFrame();
    468    }
    469  }
    470 }
    471 
    472 nsTableRowFrame* nsTableRowGroupFrame::GetFirstRow() const {
    473  nsIFrame* firstChild = mFrames.FirstChild();
    474  MOZ_ASSERT(
    475      !firstChild || static_cast<nsTableRowFrame*>(do_QueryFrame(firstChild)),
    476      "How do we have a non-row child?");
    477  return static_cast<nsTableRowFrame*>(firstChild);
    478 }
    479 
    480 nsTableRowFrame* nsTableRowGroupFrame::GetLastRow() const {
    481  nsIFrame* lastChild = mFrames.LastChild();
    482  MOZ_ASSERT(
    483      !lastChild || static_cast<nsTableRowFrame*>(do_QueryFrame(lastChild)),
    484      "How do we have a non-row child?");
    485  return static_cast<nsTableRowFrame*>(lastChild);
    486 }
    487 
    488 struct RowInfo {
    489  RowInfo() { bSize = pctBSize = hasStyleBSize = hasPctBSize = isSpecial = 0; }
    490  unsigned bSize;          // content bsize or fixed bsize, excluding pct bsize
    491  unsigned pctBSize : 29;  // pct bsize
    492  unsigned hasStyleBSize : 1;
    493  unsigned hasPctBSize : 1;
    494  unsigned isSpecial : 1;  // there is no cell originating in the row with
    495                           // rowspan=1 and there are at least 2 cells spanning
    496                           // the row and there is no style bsize on the row
    497 };
    498 
    499 static void UpdateBSizes(RowInfo& aRowInfo, nscoord aAdditionalBSize,
    500                         nscoord& aTotal, nscoord& aUnconstrainedTotal) {
    501  aRowInfo.bSize += aAdditionalBSize;
    502  aTotal += aAdditionalBSize;
    503  if (!aRowInfo.hasStyleBSize) {
    504    aUnconstrainedTotal += aAdditionalBSize;
    505  }
    506 }
    507 
    508 void nsTableRowGroupFrame::DidResizeRows(ReflowOutput& aDesiredSize) {
    509  // Update the cells spanning rows with their new bsizes.
    510  // This is the place where all of the cells in the row get set to the bsize
    511  // of the row.
    512  // Reset the overflow area.
    513  aDesiredSize.mOverflowAreas.Clear();
    514  for (nsTableRowFrame* rowFrame = GetFirstRow(); rowFrame;
    515       rowFrame = rowFrame->GetNextRow()) {
    516    rowFrame->DidResize();
    517    ConsiderChildOverflow(aDesiredSize.mOverflowAreas, rowFrame);
    518  }
    519 }
    520 
    521 // This calculates the bsize of all the rows and takes into account
    522 // style bsize on the row group, style bsizes on rows and cells, style bsizes on
    523 // rowspans. Actual row bsizes will be adjusted later if the table has a style
    524 // bsize. Even if rows don't change bsize, this method must be called to set the
    525 // bsizes of each cell in the row to the bsize of its row.
    526 void nsTableRowGroupFrame::CalculateRowBSizes(nsPresContext* aPresContext,
    527                                              ReflowOutput& aDesiredSize,
    528                                              const ReflowInput& aReflowInput) {
    529  nsTableFrame* tableFrame = GetTableFrame();
    530  const bool isPaginated = aPresContext->IsPaginated();
    531 
    532  int32_t numEffCols = tableFrame->GetEffectiveColCount();
    533 
    534  int32_t startRowIndex = GetStartRowIndex();
    535  // find the row corresponding to the row index we just found
    536  nsTableRowFrame* startRowFrame = GetFirstRow();
    537 
    538  if (!startRowFrame) {
    539    return;
    540  }
    541 
    542  // The current row group block-size is the block-origin of the 1st row
    543  // we are about to calculate a block-size for.
    544  WritingMode wm = aReflowInput.GetWritingMode();
    545  nsSize containerSize;  // actual value is unimportant as we're initially
    546                         // computing sizes, not physical positions
    547  nscoord startRowGroupBSize =
    548      startRowFrame->GetLogicalNormalPosition(wm, containerSize).B(wm);
    549 
    550  int32_t numRows =
    551      GetRowCount() - (startRowFrame->GetRowIndex() - GetStartRowIndex());
    552  // Collect the current bsize of each row.
    553  if (numRows <= 0) {
    554    return;
    555  }
    556 
    557  AutoTArray<RowInfo, 32> rowInfo;
    558  // XXX(Bug 1631371) Check if this should use a fallible operation as it
    559  // pretended earlier.
    560  rowInfo.AppendElements(numRows);
    561 
    562  bool hasRowSpanningCell = false;
    563  nscoord bSizeOfRows = 0;
    564  nscoord bSizeOfUnStyledRows = 0;
    565  // Get the bsize of each row without considering rowspans. This will be the
    566  // max of the largest desired bsize of each cell, the largest style bsize of
    567  // each cell, the style bsize of the row.
    568  nscoord pctBSizeBasis = GetBSizeBasis(aReflowInput);
    569  int32_t
    570      rowIndex;  // the index in rowInfo, not among the rows in the row group
    571  nsTableRowFrame* rowFrame;
    572  for (rowFrame = startRowFrame, rowIndex = 0; rowFrame;
    573       rowFrame = rowFrame->GetNextRow(), rowIndex++) {
    574    nscoord nonPctBSize = rowFrame->GetContentBSize();
    575    if (isPaginated) {
    576      nonPctBSize = std::max(nonPctBSize, rowFrame->BSize(wm));
    577    }
    578    if (!rowFrame->GetPrevInFlow()) {
    579      if (rowFrame->HasPctBSize()) {
    580        rowInfo[rowIndex].hasPctBSize = true;
    581        rowInfo[rowIndex].pctBSize = rowFrame->GetInitialBSize(pctBSizeBasis);
    582      }
    583      rowInfo[rowIndex].hasStyleBSize = rowFrame->HasStyleBSize();
    584      nonPctBSize = std::max(nonPctBSize, rowFrame->GetFixedBSize());
    585    }
    586    UpdateBSizes(rowInfo[rowIndex], nonPctBSize, bSizeOfRows,
    587                 bSizeOfUnStyledRows);
    588 
    589    if (!rowInfo[rowIndex].hasStyleBSize) {
    590      if (isPaginated ||
    591          tableFrame->HasMoreThanOneCell(rowIndex + startRowIndex)) {
    592        rowInfo[rowIndex].isSpecial = true;
    593        // iteratate the row's cell frames to see if any do not have rowspan > 1
    594        nsTableCellFrame* cellFrame = rowFrame->GetFirstCell();
    595        while (cellFrame) {
    596          int32_t rowSpan = tableFrame->GetEffectiveRowSpan(
    597              rowIndex + startRowIndex, *cellFrame);
    598          if (1 == rowSpan) {
    599            rowInfo[rowIndex].isSpecial = false;
    600            break;
    601          }
    602          cellFrame = cellFrame->GetNextCell();
    603        }
    604      }
    605    }
    606    // See if a cell spans into the row. If so we'll have to do the next step
    607    if (!hasRowSpanningCell) {
    608      if (tableFrame->RowIsSpannedInto(rowIndex + startRowIndex, numEffCols)) {
    609        hasRowSpanningCell = true;
    610      }
    611    }
    612  }
    613 
    614  if (hasRowSpanningCell) {
    615    // Get the bsize of cells with rowspans and allocate any extra space to the
    616    // rows they span iteratate the child frames and process the row frames
    617    // among them
    618    for (rowFrame = startRowFrame, rowIndex = 0; rowFrame;
    619         rowFrame = rowFrame->GetNextRow(), rowIndex++) {
    620      // See if the row has an originating cell with rowspan > 1. We cannot
    621      // determine this for a row in a continued row group by calling
    622      // RowHasSpanningCells, because the row's fif may not have any originating
    623      // cells yet the row may have a continued cell which originates in it.
    624      if (GetPrevInFlow() || tableFrame->RowHasSpanningCells(
    625                                 startRowIndex + rowIndex, numEffCols)) {
    626        nsTableCellFrame* cellFrame = rowFrame->GetFirstCell();
    627        // iteratate the row's cell frames
    628        while (cellFrame) {
    629          const nscoord rowSpacing =
    630              tableFrame->GetRowSpacing(startRowIndex + rowIndex);
    631          int32_t rowSpan = tableFrame->GetEffectiveRowSpan(
    632              rowIndex + startRowIndex, *cellFrame);
    633          if ((rowIndex + rowSpan) > numRows) {
    634            // there might be rows pushed already to the nextInFlow
    635            rowSpan = numRows - rowIndex;
    636          }
    637          if (rowSpan > 1) {  // a cell with rowspan > 1, determine the bsize of
    638                              // the rows it spans
    639            nscoord bsizeOfRowsSpanned = 0;
    640            nscoord bsizeOfUnStyledRowsSpanned = 0;
    641            nscoord numSpecialRowsSpanned = 0;
    642            nscoord cellSpacingTotal = 0;
    643            int32_t spanX;
    644            for (spanX = 0; spanX < rowSpan; spanX++) {
    645              bsizeOfRowsSpanned += rowInfo[rowIndex + spanX].bSize;
    646              if (!rowInfo[rowIndex + spanX].hasStyleBSize) {
    647                bsizeOfUnStyledRowsSpanned += rowInfo[rowIndex + spanX].bSize;
    648              }
    649              if (0 != spanX) {
    650                cellSpacingTotal += rowSpacing;
    651              }
    652              if (rowInfo[rowIndex + spanX].isSpecial) {
    653                numSpecialRowsSpanned++;
    654              }
    655            }
    656            nscoord bsizeOfAreaSpanned = bsizeOfRowsSpanned + cellSpacingTotal;
    657            // get the bsize of the cell
    658            LogicalSize cellFrameSize = cellFrame->GetLogicalSize(wm);
    659            LogicalSize cellDesSize = cellFrame->GetDesiredSize();
    660            cellDesSize.BSize(wm) = rowFrame->CalcCellActualBSize(
    661                cellFrame, cellDesSize.BSize(wm), wm);
    662            cellFrameSize.BSize(wm) = cellDesSize.BSize(wm);
    663 
    664            if (bsizeOfAreaSpanned < cellFrameSize.BSize(wm)) {
    665              // the cell's bsize is larger than the available space of the rows
    666              // it spans so distribute the excess bsize to the rows affected
    667              nscoord extra = cellFrameSize.BSize(wm) - bsizeOfAreaSpanned;
    668              nscoord extraUsed = 0;
    669              if (0 == numSpecialRowsSpanned) {
    670                // NS_ASSERTION(bsizeOfRowsSpanned > 0, "invalid row span
    671                // situation");
    672                bool haveUnStyledRowsSpanned = (bsizeOfUnStyledRowsSpanned > 0);
    673                nscoord divisor = (haveUnStyledRowsSpanned)
    674                                      ? bsizeOfUnStyledRowsSpanned
    675                                      : bsizeOfRowsSpanned;
    676                if (divisor > 0) {
    677                  for (spanX = rowSpan - 1; spanX >= 0; spanX--) {
    678                    if (!haveUnStyledRowsSpanned ||
    679                        !rowInfo[rowIndex + spanX].hasStyleBSize) {
    680                      // The amount of additional space each row gets is
    681                      // proportional to its bsize
    682                      float percent = ((float)rowInfo[rowIndex + spanX].bSize) /
    683                                      ((float)divisor);
    684 
    685                      // give rows their percentage, except for the first row
    686                      // which gets the remainder
    687                      nscoord extraForRow =
    688                          (0 == spanX)
    689                              ? extra - extraUsed
    690                              : NSToCoordRound(((float)(extra)) * percent);
    691                      extraForRow = std::min(extraForRow, extra - extraUsed);
    692                      // update the row bsize
    693                      UpdateBSizes(rowInfo[rowIndex + spanX], extraForRow,
    694                                   bSizeOfRows, bSizeOfUnStyledRows);
    695                      extraUsed += extraForRow;
    696                      if (extraUsed >= extra) {
    697                        NS_ASSERTION((extraUsed == extra),
    698                                     "invalid row bsize calculation");
    699                        break;
    700                      }
    701                    }
    702                  }
    703                } else {
    704                  // put everything in the last row
    705                  UpdateBSizes(rowInfo[rowIndex + rowSpan - 1], extra,
    706                               bSizeOfRows, bSizeOfUnStyledRows);
    707                }
    708              } else {
    709                // give the extra to the special rows
    710                nscoord numSpecialRowsAllocated = 0;
    711                for (spanX = rowSpan - 1; spanX >= 0; spanX--) {
    712                  if (rowInfo[rowIndex + spanX].isSpecial) {
    713                    // The amount of additional space each degenerate row gets
    714                    // is proportional to the number of them
    715                    float percent = 1.0f / ((float)numSpecialRowsSpanned);
    716 
    717                    // give rows their percentage, except for the first row
    718                    // which gets the remainder
    719                    nscoord extraForRow =
    720                        (numSpecialRowsSpanned - 1 == numSpecialRowsAllocated)
    721                            ? extra - extraUsed
    722                            : NSToCoordRound(((float)(extra)) * percent);
    723                    extraForRow = std::min(extraForRow, extra - extraUsed);
    724                    // update the row bsize
    725                    UpdateBSizes(rowInfo[rowIndex + spanX], extraForRow,
    726                                 bSizeOfRows, bSizeOfUnStyledRows);
    727                    extraUsed += extraForRow;
    728                    if (extraUsed >= extra) {
    729                      NS_ASSERTION((extraUsed == extra),
    730                                   "invalid row bsize calculation");
    731                      break;
    732                    }
    733                  }
    734                }
    735              }
    736            }
    737          }  // if (rowSpan > 1)
    738          cellFrame = cellFrame->GetNextCell();
    739        }  // while (cellFrame)
    740      }  // if (tableFrame->RowHasSpanningCells(startRowIndex + rowIndex) {
    741    }  // while (rowFrame)
    742  }
    743 
    744  // pct bsize rows have already got their content bsizes.
    745  // Give them their pct bsizes up to pctBSizeBasis
    746  nscoord extra = pctBSizeBasis - bSizeOfRows;
    747  for (rowFrame = startRowFrame, rowIndex = 0; rowFrame && (extra > 0);
    748       rowFrame = rowFrame->GetNextRow(), rowIndex++) {
    749    RowInfo& rInfo = rowInfo[rowIndex];
    750    if (rInfo.hasPctBSize) {
    751      nscoord rowExtra =
    752          (rInfo.pctBSize > rInfo.bSize) ? rInfo.pctBSize - rInfo.bSize : 0;
    753      rowExtra = std::min(rowExtra, extra);
    754      UpdateBSizes(rInfo, rowExtra, bSizeOfRows, bSizeOfUnStyledRows);
    755      extra -= rowExtra;
    756    }
    757  }
    758 
    759  bool styleBSizeAllocation = false;
    760  nscoord rowGroupBSize = startRowGroupBSize + bSizeOfRows +
    761                          tableFrame->GetRowSpacing(0, numRows - 1);
    762  // if we have a style bsize, allocate the extra bsize to unconstrained rows
    763  if ((aReflowInput.ComputedBSize() > rowGroupBSize) &&
    764      (NS_UNCONSTRAINEDSIZE != aReflowInput.ComputedBSize())) {
    765    nscoord extraComputedBSize = aReflowInput.ComputedBSize() - rowGroupBSize;
    766    nscoord extraUsed = 0;
    767    bool haveUnStyledRows = (bSizeOfUnStyledRows > 0);
    768    nscoord divisor = (haveUnStyledRows) ? bSizeOfUnStyledRows : bSizeOfRows;
    769    if (divisor > 0) {
    770      styleBSizeAllocation = true;
    771      for (rowIndex = 0; rowIndex < numRows; rowIndex++) {
    772        if (!haveUnStyledRows || !rowInfo[rowIndex].hasStyleBSize) {
    773          // The amount of additional space each row gets is based on the
    774          // percentage of space it occupies
    775          float percent = ((float)rowInfo[rowIndex].bSize) / ((float)divisor);
    776          // give rows their percentage, except for the last row which gets the
    777          // remainder
    778          nscoord extraForRow =
    779              (numRows - 1 == rowIndex)
    780                  ? extraComputedBSize - extraUsed
    781                  : NSToCoordRound(((float)extraComputedBSize) * percent);
    782          extraForRow = std::min(extraForRow, extraComputedBSize - extraUsed);
    783          // update the row bsize
    784          UpdateBSizes(rowInfo[rowIndex], extraForRow, bSizeOfRows,
    785                       bSizeOfUnStyledRows);
    786          extraUsed += extraForRow;
    787          if (extraUsed >= extraComputedBSize) {
    788            NS_ASSERTION((extraUsed == extraComputedBSize),
    789                         "invalid row bsize calculation");
    790            break;
    791          }
    792        }
    793      }
    794    }
    795    rowGroupBSize = aReflowInput.ComputedBSize();
    796  }
    797 
    798  if (wm.IsVertical()) {
    799    // we need the correct containerSize below for block positioning in
    800    // vertical-rl writing mode
    801    containerSize.width = rowGroupBSize;
    802  }
    803 
    804  nscoord bOrigin = startRowGroupBSize;
    805  // update the rows with their (potentially) new bsizes
    806  for (rowFrame = startRowFrame, rowIndex = 0; rowFrame;
    807       rowFrame = rowFrame->GetNextRow(), rowIndex++) {
    808    nsRect rowBounds = rowFrame->GetRect();
    809    LogicalSize rowBoundsSize(wm, rowBounds.Size());
    810    nsRect rowInkOverflow = rowFrame->InkOverflowRect();
    811    nscoord deltaB =
    812        bOrigin - rowFrame->GetLogicalNormalPosition(wm, containerSize).B(wm);
    813 
    814    nscoord rowBSize =
    815        (rowInfo[rowIndex].bSize > 0) ? rowInfo[rowIndex].bSize : 0;
    816 
    817    if (deltaB != 0 || (rowBSize != rowBoundsSize.BSize(wm))) {
    818      // Resize/move the row to its final size and position
    819      if (deltaB != 0) {
    820        rowFrame->InvalidateFrameSubtree();
    821      }
    822 
    823      rowFrame->MovePositionBy(wm, LogicalPoint(wm, 0, deltaB));
    824      rowFrame->SetSize(LogicalSize(wm, rowBoundsSize.ISize(wm), rowBSize));
    825 
    826      nsTableFrame::InvalidateTableFrame(rowFrame, rowBounds, rowInkOverflow,
    827                                         false);
    828 
    829      if (deltaB != 0) {
    830        // XXXbz we don't need to update our overflow area?
    831      }
    832    }
    833    bOrigin += rowBSize + tableFrame->GetRowSpacing(startRowIndex + rowIndex);
    834  }
    835 
    836  if (isPaginated && styleBSizeAllocation) {
    837    // since the row group has a style bsize, cache the row bsizes,
    838    // so next in flows can honor them
    839    CacheRowBSizesForPrinting(GetFirstRow(), wm);
    840  }
    841 
    842  DidResizeRows(aDesiredSize);
    843 
    844  aDesiredSize.BSize(wm) = rowGroupBSize;  // Adjust our desired size
    845 }
    846 
    847 nscoord nsTableRowGroupFrame::CollapseRowGroupIfNecessary(nscoord aBTotalOffset,
    848                                                          nscoord aISize,
    849                                                          WritingMode aWM) {
    850  nsTableFrame* tableFrame = GetTableFrame();
    851  nsSize containerSize = tableFrame->GetSize();
    852  const nsStyleVisibility* groupVis = StyleVisibility();
    853  bool collapseGroup = StyleVisibility::Collapse == groupVis->mVisible;
    854  if (collapseGroup) {
    855    tableFrame->SetNeedToCollapse(true);
    856  }
    857 
    858  OverflowAreas overflow;
    859 
    860  nsTableRowFrame* rowFrame = GetFirstRow();
    861  bool didCollapse = false;
    862  nscoord bGroupOffset = 0;
    863  while (rowFrame) {
    864    bGroupOffset += rowFrame->CollapseRowIfNecessary(
    865        bGroupOffset, aISize, collapseGroup, didCollapse);
    866    ConsiderChildOverflow(overflow, rowFrame);
    867    rowFrame = rowFrame->GetNextRow();
    868  }
    869 
    870  LogicalRect groupRect = GetLogicalRect(aWM, containerSize);
    871  nsRect oldGroupRect = GetRect();
    872  nsRect oldGroupInkOverflow = InkOverflowRect();
    873 
    874  groupRect.BSize(aWM) -= bGroupOffset;
    875  if (didCollapse) {
    876    // add back the cellspacing between rowgroups
    877    groupRect.BSize(aWM) +=
    878        tableFrame->GetRowSpacing(GetStartRowIndex() + GetRowCount());
    879  }
    880 
    881  groupRect.BStart(aWM) -= aBTotalOffset;
    882  groupRect.ISize(aWM) = aISize;
    883 
    884  if (aBTotalOffset != 0) {
    885    InvalidateFrameSubtree();
    886  }
    887 
    888  SetRect(aWM, groupRect, containerSize);
    889  overflow.UnionAllWith(
    890      nsRect(0, 0, groupRect.Width(aWM), groupRect.Height(aWM)));
    891  FinishAndStoreOverflow(overflow, groupRect.Size(aWM).GetPhysicalSize(aWM));
    892  nsTableFrame::InvalidateTableFrame(this, oldGroupRect, oldGroupInkOverflow,
    893                                     false);
    894 
    895  return bGroupOffset;
    896 }
    897 
    898 nsTableRowFrame* nsTableRowGroupFrame::CreateContinuingRowFrame(
    899    nsIFrame* aRowFrame) {
    900  // Create the continuing frame which will create continuing cell frames.
    901  auto* contRowFrame = static_cast<nsTableRowFrame*>(
    902      PresShell()->FrameConstructor()->CreateContinuingFrame(aRowFrame, this));
    903 
    904  // Add the continuing row frame to the child list.
    905  mFrames.InsertFrame(nullptr, aRowFrame, contRowFrame);
    906 
    907  // Push the continuing row frame and the frames that follow.
    908  // This needs to match `UndoContinuedRow`.
    909  PushChildrenToOverflow(contRowFrame, aRowFrame);
    910 
    911  return contRowFrame;
    912 }
    913 
    914 // Reflow the cells with rowspan > 1 which originate between aFirstRow
    915 // and end on or after aLastRow. aFirstTruncatedRow is the highest row on the
    916 // page that contains a cell which cannot split on this page
    917 void nsTableRowGroupFrame::SplitSpanningCells(
    918    nsPresContext* aPresContext, const ReflowInput& aReflowInput,
    919    nsTableFrame* aTable, nsTableRowFrame* aFirstRow, nsTableRowFrame* aLastRow,
    920    bool aFirstRowIsTopOfPage, nscoord aSpanningRowBEnd,
    921    const nsSize& aContainerSize, nsTableRowFrame*& aContRow,
    922    nsTableRowFrame*& aFirstTruncatedRow, nscoord& aDesiredBSize) {
    923  NS_ASSERTION(aSpanningRowBEnd >= 0, "Can't split negative bsizes");
    924  aFirstTruncatedRow = nullptr;
    925  aDesiredBSize = 0;
    926 
    927  const WritingMode wm = aReflowInput.GetWritingMode();
    928  const bool borderCollapse = aTable->IsBorderCollapse();
    929  int32_t lastRowIndex = aLastRow->GetRowIndex();
    930  bool wasLast = false;
    931  bool haveRowSpan = false;
    932  // Iterate the rows between aFirstRow and aLastRow
    933  for (nsTableRowFrame* row = aFirstRow; !wasLast; row = row->GetNextRow()) {
    934    wasLast = (row == aLastRow);
    935    int32_t rowIndex = row->GetRowIndex();
    936    const LogicalRect rowRect = row->GetLogicalNormalRect(wm, aContainerSize);
    937    // Iterate the cells looking for those that have rowspan > 1
    938    for (nsTableCellFrame* cell = row->GetFirstCell(); cell;
    939         cell = cell->GetNextCell()) {
    940      int32_t rowSpan = aTable->GetEffectiveRowSpan(rowIndex, *cell);
    941      // Only reflow rowspan > 1 cells which span aLastRow. Those which don't
    942      // span aLastRow were reflowed correctly during the unconstrained bsize
    943      // reflow.
    944      if ((rowSpan > 1) && (rowIndex + rowSpan > lastRowIndex)) {
    945        haveRowSpan = true;
    946        nsReflowStatus status;
    947        // Ask the row to reflow the cell to the bsize of all the rows it spans
    948        // up through aLastRow cellAvailBSize is the space between the row group
    949        // start and the end of the page
    950        const nscoord cellAvailBSize = aSpanningRowBEnd - rowRect.BStart(wm);
    951        NS_ASSERTION(cellAvailBSize >= 0, "No space for cell?");
    952        bool isTopOfPage = (row == aFirstRow) && aFirstRowIsTopOfPage;
    953 
    954        LogicalSize rowAvailSize(
    955            wm, aReflowInput.AvailableISize(),
    956            std::max(aReflowInput.AvailableBSize() - rowRect.BStart(wm), 0));
    957        // Don't let the available block-size exceed what CalculateRowBSizes set
    958        // for it.
    959        rowAvailSize.BSize(wm) =
    960            std::min(rowAvailSize.BSize(wm), rowRect.BSize(wm));
    961        ReflowInput rowReflowInput(
    962            aPresContext, aReflowInput, row,
    963            rowAvailSize.ConvertTo(row->GetWritingMode(), wm), Nothing(),
    964            ReflowInput::InitFlag::CallerWillInit);
    965        InitChildReflowInput(aPresContext, borderCollapse, rowReflowInput);
    966        rowReflowInput.mFlags.mIsTopOfPage = isTopOfPage;  // set top of page
    967 
    968        nscoord cellBSize =
    969            row->ReflowCellFrame(aPresContext, rowReflowInput, isTopOfPage,
    970                                 cell, cellAvailBSize, status);
    971        aDesiredBSize = std::max(aDesiredBSize, rowRect.BStart(wm) + cellBSize);
    972        if (status.IsComplete()) {
    973          if (cellBSize > cellAvailBSize) {
    974            aFirstTruncatedRow = row;
    975            if ((row != aFirstRow) || !aFirstRowIsTopOfPage) {
    976              // return now, since we will be getting another reflow after
    977              // either (1) row is moved to the next page or (2) the row group
    978              // is moved to the next page
    979              return;
    980            }
    981          }
    982        } else {
    983          if (!aContRow) {
    984            aContRow = CreateContinuingRowFrame(aLastRow);
    985          }
    986          if (aContRow) {
    987            if (row != aLastRow) {
    988              // aContRow needs a continuation for cell, since cell spanned into
    989              // aLastRow but does not originate there
    990              nsTableCellFrame* contCell = static_cast<nsTableCellFrame*>(
    991                  PresShell()->FrameConstructor()->CreateContinuingFrame(
    992                      cell, aLastRow));
    993              uint32_t colIndex = cell->ColIndex();
    994              aContRow->InsertCellFrame(contCell, colIndex);
    995            }
    996          }
    997        }
    998      }
    999    }
   1000  }
   1001  if (!haveRowSpan) {
   1002    aDesiredBSize = aLastRow->GetLogicalNormalRect(wm, aContainerSize).BEnd(wm);
   1003  }
   1004 }
   1005 
   1006 // Remove the next-in-flow of the row, its cells and their cell blocks. This
   1007 // is necessary in case the row doesn't need a continuation later on or needs
   1008 // a continuation which doesn't have the same number of cells that now exist.
   1009 void nsTableRowGroupFrame::UndoContinuedRow(nsPresContext* aPresContext,
   1010                                            nsTableRowFrame* aRow) {
   1011  if (!aRow) {
   1012    return;  // allow null aRow to avoid callers doing null checks
   1013  }
   1014 
   1015  // rowBefore was the prev-sibling of aRow's next-sibling before aRow was
   1016  // created
   1017  nsTableRowFrame* rowBefore = (nsTableRowFrame*)aRow->GetPrevInFlow();
   1018  MOZ_ASSERT(mFrames.ContainsFrame(rowBefore),
   1019             "rowBefore not in our frame list?");
   1020 
   1021  // Needs to match `CreateContinuingRowFrame` - we're assuming that continued
   1022  // frames always go into overflow frames list.
   1023  AutoFrameListPtr overflows(aPresContext, StealOverflowFrames());
   1024  if (!rowBefore || !overflows || overflows->IsEmpty() ||
   1025      overflows->FirstChild() != aRow) {
   1026    NS_ERROR("invalid continued row");
   1027    return;
   1028  }
   1029 
   1030  DestroyContext context(aPresContext->PresShell());
   1031  // Destroy aRow, its cells, and their cell blocks. Cell blocks that have split
   1032  // will not have reflowed yet to pick up content from any overflow lines.
   1033  overflows->DestroyFrame(context, aRow);
   1034 
   1035  // Put the overflow rows into our child list
   1036  if (!overflows->IsEmpty()) {
   1037    mFrames.InsertFrames(nullptr, rowBefore, std::move(*overflows));
   1038  }
   1039 }
   1040 
   1041 void nsTableRowGroupFrame::SplitRowGroup(nsPresContext* aPresContext,
   1042                                         ReflowOutput& aDesiredSize,
   1043                                         const ReflowInput& aReflowInput,
   1044                                         nsTableFrame* aTableFrame,
   1045                                         nsReflowStatus& aStatus,
   1046                                         bool aRowForcedPageBreak) {
   1047  MOZ_ASSERT(aPresContext->IsPaginated(),
   1048             "SplitRowGroup currently supports only paged media");
   1049 
   1050  const WritingMode wm = aReflowInput.GetWritingMode();
   1051  nsTableRowFrame* prevRowFrame = nullptr;
   1052  aDesiredSize.BSize(wm) = 0;
   1053  aDesiredSize.SetOverflowAreasToDesiredBounds();
   1054 
   1055  const nscoord availISize = aReflowInput.AvailableISize();
   1056  const nscoord availBSize = aReflowInput.AvailableBSize();
   1057  const nsSize containerSize =
   1058      aReflowInput.ComputedSizeAsContainerIfConstrained();
   1059  const bool borderCollapse = aTableFrame->IsBorderCollapse();
   1060 
   1061  const nscoord pageBSize =
   1062      LogicalSize(wm, aPresContext->GetPageSize()).BSize(wm);
   1063  NS_ASSERTION(pageBSize != NS_UNCONSTRAINEDSIZE,
   1064               "The table shouldn't be split when there should be space");
   1065 
   1066  bool isTopOfPage = aReflowInput.mFlags.mIsTopOfPage;
   1067  nsTableRowFrame* firstRowThisPage = GetFirstRow();
   1068 
   1069  // Need to dirty the table's geometry, or else the row might skip
   1070  // reflowing its cell as an optimization.
   1071  aTableFrame->SetGeometryDirty();
   1072 
   1073  // Walk each of the row frames looking for the first row frame that doesn't
   1074  // fit in the available space
   1075  for (nsTableRowFrame* rowFrame = firstRowThisPage; rowFrame;
   1076       rowFrame = rowFrame->GetNextRow()) {
   1077    bool rowIsOnPage = true;
   1078    const nscoord rowSpacing =
   1079        aTableFrame->GetRowSpacing(rowFrame->GetRowIndex());
   1080    const LogicalRect rowRect =
   1081        rowFrame->GetLogicalNormalRect(wm, containerSize);
   1082    // See if the row fits on this page
   1083    if (rowRect.BEnd(wm) > availBSize) {
   1084      nsTableRowFrame* contRow = nullptr;
   1085      // Reflow the row in the availabe space and have it split if it is the 1st
   1086      // row (on the page) or there is at least 5% of the current page available
   1087      // XXX this 5% should be made a preference
   1088      if (!prevRowFrame ||
   1089          (availBSize - aDesiredSize.BSize(wm) > pageBSize / 20)) {
   1090        LogicalSize availSize(wm, availISize,
   1091                              std::max(availBSize - rowRect.BStart(wm), 0));
   1092        // Don't let the available block-size exceed what CalculateRowBSizes set
   1093        // for it.
   1094        availSize.BSize(wm) = std::min(availSize.BSize(wm), rowRect.BSize(wm));
   1095 
   1096        ReflowInput rowReflowInput(
   1097            aPresContext, aReflowInput, rowFrame,
   1098            availSize.ConvertTo(rowFrame->GetWritingMode(), wm), Nothing(),
   1099            ReflowInput::InitFlag::CallerWillInit);
   1100 
   1101        InitChildReflowInput(aPresContext, borderCollapse, rowReflowInput);
   1102        rowReflowInput.mFlags.mIsTopOfPage = isTopOfPage;  // set top of page
   1103        ReflowOutput rowMetrics(aReflowInput);
   1104 
   1105        // Get the old size before we reflow.
   1106        nsRect oldRowRect = rowFrame->GetRect();
   1107        nsRect oldRowInkOverflow = rowFrame->InkOverflowRect();
   1108 
   1109        // Reflow the cell with the constrained bsize. A cell with rowspan >1
   1110        // will get this reflow later during SplitSpanningCells.
   1111        //
   1112        // Note: We just pass dummy aPos and aContainerSize since we are not
   1113        // moving the row frame.
   1114        const LogicalPoint dummyPos(wm);
   1115        const nsSize dummyContainerSize;
   1116        ReflowChild(rowFrame, aPresContext, rowMetrics, rowReflowInput, wm,
   1117                    dummyPos, dummyContainerSize, ReflowChildFlags::NoMoveFrame,
   1118                    aStatus);
   1119        FinishReflowChild(rowFrame, aPresContext, rowMetrics, &rowReflowInput,
   1120                          wm, dummyPos, dummyContainerSize,
   1121                          ReflowChildFlags::NoMoveFrame);
   1122        rowFrame->DidResize(ForceAlignTopForTableCell::Yes);
   1123 
   1124        if (!aRowForcedPageBreak && !aStatus.IsFullyComplete() &&
   1125            ShouldAvoidBreakInside(aReflowInput)) {
   1126          aStatus.SetInlineLineBreakBeforeAndReset();
   1127          break;
   1128        }
   1129 
   1130        nsTableFrame::InvalidateTableFrame(rowFrame, oldRowRect,
   1131                                           oldRowInkOverflow, false);
   1132 
   1133        if (aStatus.IsIncomplete()) {
   1134          // The row frame is incomplete and all of the rowspan 1 cells' block
   1135          // frames split
   1136          if ((rowMetrics.BSize(wm) <= rowReflowInput.AvailableBSize()) ||
   1137              isTopOfPage) {
   1138            // The row stays on this page because either it split ok or we're on
   1139            // the top of page. If top of page and the block-size exceeded the
   1140            // avail block-size, then there will be data loss.
   1141            NS_ASSERTION(
   1142                rowMetrics.BSize(wm) <= rowReflowInput.AvailableBSize(),
   1143                "Data loss - incomplete row needed more block-size than "
   1144                "available, on top of page!");
   1145            contRow = CreateContinuingRowFrame(rowFrame);
   1146            aDesiredSize.BSize(wm) += rowMetrics.BSize(wm);
   1147            if (prevRowFrame) {
   1148              aDesiredSize.BSize(wm) += rowSpacing;
   1149            }
   1150          } else {
   1151            // Put the row on the next page to give it more block-size.
   1152            rowIsOnPage = false;
   1153          }
   1154        } else {
   1155          // The row frame is complete because either (1) its minimum block-size
   1156          // is greater than the available block-size we gave it, or (2) it may
   1157          // have been given a larger block-size through style than its content,
   1158          // or (3) it contains a rowspan >1 cell which hasn't been reflowed
   1159          // with a constrained block-size yet (we will find out when
   1160          // SplitSpanningCells is called below)
   1161          if (rowMetrics.BSize(wm) > availSize.BSize(wm) ||
   1162              (aStatus.IsInlineBreakBefore() && !aRowForcedPageBreak)) {
   1163            // cases (1) and (2)
   1164            if (isTopOfPage) {
   1165              // We're on top of the page, so keep the row on this page. There
   1166              // will be data loss. Push the row frame that follows
   1167              nsTableRowFrame* nextRowFrame = rowFrame->GetNextRow();
   1168              if (nextRowFrame) {
   1169                aStatus.Reset();
   1170                aStatus.SetIncomplete();
   1171              }
   1172              aDesiredSize.BSize(wm) += rowMetrics.BSize(wm);
   1173              if (prevRowFrame) {
   1174                aDesiredSize.BSize(wm) += rowSpacing;
   1175              }
   1176              NS_WARNING(
   1177                  "Data loss - complete row needed more block-size than "
   1178                  "available, on top of page");
   1179            } else {
   1180              // We're not on top of the page, so put the row on the next page
   1181              // to give it more block-size.
   1182              rowIsOnPage = false;
   1183            }
   1184          }
   1185        }
   1186      } else {
   1187        // Put the row on the next page to give it more block-size.
   1188        rowIsOnPage = false;
   1189      }
   1190 
   1191      nsTableRowFrame* lastRowThisPage = rowFrame;
   1192      nscoord spanningRowBEnd = availBSize;
   1193      if (!rowIsOnPage) {
   1194        NS_ASSERTION(!contRow,
   1195                     "We should not have created a continuation if none of "
   1196                     "this row fits");
   1197        if (!prevRowFrame ||
   1198            (!aRowForcedPageBreak && ShouldAvoidBreakInside(aReflowInput))) {
   1199          aStatus.SetInlineLineBreakBeforeAndReset();
   1200          break;
   1201        }
   1202        spanningRowBEnd =
   1203            prevRowFrame->GetLogicalNormalRect(wm, containerSize).BEnd(wm);
   1204        lastRowThisPage = prevRowFrame;
   1205        aStatus.Reset();
   1206        aStatus.SetIncomplete();
   1207      }
   1208 
   1209      // reflow the cells with rowspan >1 that occur on the page
   1210      nsTableRowFrame* firstTruncatedRow;
   1211      nscoord bMost;
   1212      SplitSpanningCells(aPresContext, aReflowInput, aTableFrame,
   1213                         firstRowThisPage, lastRowThisPage,
   1214                         aReflowInput.mFlags.mIsTopOfPage, spanningRowBEnd,
   1215                         containerSize, contRow, firstTruncatedRow, bMost);
   1216      if (firstTruncatedRow) {
   1217        // A rowspan >1 cell did not fit (and could not split) in the space we
   1218        // gave it
   1219        if (firstTruncatedRow == firstRowThisPage) {
   1220          if (aReflowInput.mFlags.mIsTopOfPage) {
   1221            NS_WARNING("data loss in a row spanned cell");
   1222          } else {
   1223            // We can't push children, so let our parent reflow us again with
   1224            // more space
   1225            aDesiredSize.BSize(wm) = rowRect.BEnd(wm);
   1226            aStatus.Reset();
   1227            UndoContinuedRow(aPresContext, contRow);
   1228            contRow = nullptr;
   1229          }
   1230        } else {
   1231          // Try to put firstTruncateRow on the next page
   1232          nsTableRowFrame* rowBefore = firstTruncatedRow->GetPrevRow();
   1233          const nscoord oldSpanningRowBEnd = spanningRowBEnd;
   1234          spanningRowBEnd =
   1235              rowBefore->GetLogicalNormalRect(wm, containerSize).BEnd(wm);
   1236 
   1237          UndoContinuedRow(aPresContext, contRow);
   1238          contRow = nullptr;
   1239          nsTableRowFrame* oldLastRowThisPage = lastRowThisPage;
   1240          lastRowThisPage = rowBefore;
   1241          aStatus.Reset();
   1242          aStatus.SetIncomplete();
   1243 
   1244          // Call SplitSpanningCells again with rowBefore as the last row on the
   1245          // page
   1246          SplitSpanningCells(aPresContext, aReflowInput, aTableFrame,
   1247                             firstRowThisPage, rowBefore,
   1248                             aReflowInput.mFlags.mIsTopOfPage, spanningRowBEnd,
   1249                             containerSize, contRow, firstTruncatedRow,
   1250                             aDesiredSize.BSize(wm));
   1251          if (firstTruncatedRow) {
   1252            if (aReflowInput.mFlags.mIsTopOfPage) {
   1253              // We were better off with the 1st call to SplitSpanningCells, do
   1254              // it again
   1255              UndoContinuedRow(aPresContext, contRow);
   1256              contRow = nullptr;
   1257              lastRowThisPage = oldLastRowThisPage;
   1258              spanningRowBEnd = oldSpanningRowBEnd;
   1259              SplitSpanningCells(aPresContext, aReflowInput, aTableFrame,
   1260                                 firstRowThisPage, lastRowThisPage,
   1261                                 aReflowInput.mFlags.mIsTopOfPage,
   1262                                 spanningRowBEnd, containerSize, contRow,
   1263                                 firstTruncatedRow, aDesiredSize.BSize(wm));
   1264              NS_WARNING("data loss in a row spanned cell");
   1265            } else {
   1266              // Let our parent reflow us again with more space
   1267              aDesiredSize.BSize(wm) = rowRect.BEnd(wm);
   1268              aStatus.Reset();
   1269              UndoContinuedRow(aPresContext, contRow);
   1270              contRow = nullptr;
   1271            }
   1272          }
   1273        }
   1274      } else {
   1275        aDesiredSize.BSize(wm) = std::max(aDesiredSize.BSize(wm), bMost);
   1276        if (contRow) {
   1277          aStatus.Reset();
   1278          aStatus.SetIncomplete();
   1279        }
   1280      }
   1281      if (aStatus.IsIncomplete() && !contRow) {
   1282        if (nsTableRowFrame* nextRow = lastRowThisPage->GetNextRow()) {
   1283          PushChildrenToOverflow(nextRow, lastRowThisPage);
   1284        }
   1285      } else if (aStatus.IsComplete() && lastRowThisPage) {
   1286        // Our size from the unconstrained reflow exceeded the constrained
   1287        // available space but our size in the constrained reflow is Complete.
   1288        // This can happen when a non-zero block-end margin is suppressed in
   1289        // nsBlockFrame::ComputeFinalSize.
   1290        if (nsTableRowFrame* nextRow = lastRowThisPage->GetNextRow()) {
   1291          aStatus.Reset();
   1292          aStatus.SetIncomplete();
   1293          PushChildrenToOverflow(nextRow, lastRowThisPage);
   1294        }
   1295      }
   1296      break;
   1297    }
   1298    aDesiredSize.BSize(wm) = rowRect.BEnd(wm);
   1299    prevRowFrame = rowFrame;
   1300    // see if there is a page break after the row
   1301    nsTableRowFrame* nextRow = rowFrame->GetNextRow();
   1302    if (nextRow && nsTableFrame::PageBreakAfter(rowFrame, nextRow)) {
   1303      PushChildrenToOverflow(nextRow, rowFrame);
   1304      aStatus.Reset();
   1305      aStatus.SetIncomplete();
   1306      break;
   1307    }
   1308    // After the 1st row that has a block-size, we can't be on top of the page
   1309    // anymore.
   1310    isTopOfPage = isTopOfPage && rowRect.BEnd(wm) == 0;
   1311  }
   1312 }
   1313 
   1314 /** Layout the entire row group.
   1315 * This method stacks rows vertically according to HTML 4.0 rules.
   1316 * Rows are responsible for layout of their children.
   1317 */
   1318 void nsTableRowGroupFrame::Reflow(nsPresContext* aPresContext,
   1319                                  ReflowOutput& aDesiredSize,
   1320                                  const ReflowInput& aReflowInput,
   1321                                  nsReflowStatus& aStatus) {
   1322  MarkInReflow();
   1323  DO_GLOBAL_REFLOW_COUNT("nsTableRowGroupFrame");
   1324  MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
   1325 
   1326  // Row geometry may be going to change so we need to invalidate any row
   1327  // cursor.
   1328  ClearRowCursor();
   1329 
   1330  // see if a special bsize reflow needs to occur due to having a pct bsize
   1331  nsTableFrame::CheckRequestSpecialBSizeReflow(aReflowInput);
   1332 
   1333  nsTableFrame* tableFrame = GetTableFrame();
   1334  TableRowGroupReflowInput state(aReflowInput);
   1335  const nsStyleVisibility* groupVis = StyleVisibility();
   1336  bool collapseGroup = StyleVisibility::Collapse == groupVis->mVisible;
   1337  if (collapseGroup) {
   1338    tableFrame->SetNeedToCollapse(true);
   1339  }
   1340 
   1341  // Check for an overflow list
   1342  MoveOverflowToChildList();
   1343 
   1344  // Reflow the existing frames.
   1345  bool splitDueToPageBreak = false;
   1346  ReflowChildren(aPresContext, aDesiredSize, state, aStatus,
   1347                 &splitDueToPageBreak);
   1348 
   1349  // See if all the frames fit. Do not try to split anything if we're
   1350  // not paginated ... we can't split across columns yet.
   1351  WritingMode wm = aReflowInput.GetWritingMode();
   1352  if (aReflowInput.mFlags.mTableIsSplittable &&
   1353      aReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE &&
   1354      (aStatus.IsIncomplete() || splitDueToPageBreak ||
   1355       aDesiredSize.BSize(wm) > aReflowInput.AvailableBSize())) {
   1356    // Nope, find a place to split the row group
   1357    auto& mutableRIFlags = const_cast<ReflowInput::Flags&>(aReflowInput.mFlags);
   1358    const bool savedSpecialBSizeReflow = mutableRIFlags.mSpecialBSizeReflow;
   1359    mutableRIFlags.mSpecialBSizeReflow = false;
   1360 
   1361    SplitRowGroup(aPresContext, aDesiredSize, aReflowInput, tableFrame, aStatus,
   1362                  splitDueToPageBreak);
   1363 
   1364    mutableRIFlags.mSpecialBSizeReflow = savedSpecialBSizeReflow;
   1365  }
   1366 
   1367  // XXXmats The following is just bogus.  We leave it here for now because
   1368  // ReflowChildren should pull up rows from our next-in-flow before returning
   1369  // a Complete status, but doesn't (bug 804888).
   1370  if (GetNextInFlow() && GetNextInFlow()->PrincipalChildList().FirstChild()) {
   1371    aStatus.SetIncomplete();
   1372  }
   1373 
   1374  SetHasStyleBSize((NS_UNCONSTRAINEDSIZE != aReflowInput.ComputedBSize()) &&
   1375                   (aReflowInput.ComputedBSize() > 0));
   1376 
   1377  // Just set our isize to what was available.
   1378  // The table will calculate the isize and not use our value.
   1379  aDesiredSize.ISize(wm) = aReflowInput.AvailableISize();
   1380 
   1381  aDesiredSize.UnionOverflowAreasWithDesiredBounds();
   1382 
   1383  // If our parent is in initial reflow, it'll handle invalidating our
   1384  // entire overflow rect.
   1385  if (!GetParent()->HasAnyStateBits(NS_FRAME_FIRST_REFLOW) &&
   1386      aDesiredSize.Size(wm) != GetLogicalSize(wm)) {
   1387    InvalidateFrame();
   1388  }
   1389 
   1390  FinishAndStoreOverflow(&aDesiredSize);
   1391 
   1392  // Any absolutely-positioned children will get reflowed in
   1393  // nsIFrame::FixupPositionedTableParts in another pass, so propagate our
   1394  // dirtiness to them before our parent clears our dirty bits.
   1395  PushDirtyBitToAbsoluteFrames();
   1396 }
   1397 
   1398 bool nsTableRowGroupFrame::ComputeCustomOverflow(
   1399    OverflowAreas& aOverflowAreas) {
   1400  // Row cursor invariants depend on the ink overflow area of the rows,
   1401  // which may have changed, so we need to clear the cursor now.
   1402  ClearRowCursor();
   1403  return nsContainerFrame::ComputeCustomOverflow(aOverflowAreas);
   1404 }
   1405 
   1406 /* virtual */
   1407 void nsTableRowGroupFrame::DidSetComputedStyle(
   1408    ComputedStyle* aOldComputedStyle) {
   1409  nsContainerFrame::DidSetComputedStyle(aOldComputedStyle);
   1410  nsTableFrame::PositionedTablePartMaybeChanged(this, aOldComputedStyle);
   1411 
   1412  if (!aOldComputedStyle) {
   1413    return;  // avoid the following on init
   1414  }
   1415 
   1416  nsTableFrame* tableFrame = GetTableFrame();
   1417  if (tableFrame->IsBorderCollapse() &&
   1418      tableFrame->BCRecalcNeeded(aOldComputedStyle, Style())) {
   1419    TableArea damageArea(0, GetStartRowIndex(), tableFrame->GetColCount(),
   1420                         GetRowCount());
   1421    tableFrame->AddBCDamageArea(damageArea);
   1422  }
   1423 }
   1424 
   1425 void nsTableRowGroupFrame::AppendFrames(ChildListID aListID,
   1426                                        nsFrameList&& aFrameList) {
   1427  NS_ASSERTION(aListID == FrameChildListID::Principal, "unexpected child list");
   1428 
   1429  DrainSelfOverflowList();  // ensure the last frame is in mFrames
   1430  ClearRowCursor();
   1431 
   1432  // collect the new row frames in an array
   1433  // XXXbz why are we doing the QI stuff?  There shouldn't be any non-rows here.
   1434  AutoTArray<nsTableRowFrame*, 8> rows;
   1435  for (nsIFrame* f : aFrameList) {
   1436    nsTableRowFrame* rowFrame = do_QueryFrame(f);
   1437    NS_ASSERTION(rowFrame, "Unexpected frame; frame constructor screwed up");
   1438    if (rowFrame) {
   1439      NS_ASSERTION(
   1440          mozilla::StyleDisplay::TableRow == f->StyleDisplay()->mDisplay,
   1441          "wrong display type on rowframe");
   1442      rows.AppendElement(rowFrame);
   1443    }
   1444  }
   1445 
   1446  int32_t rowIndex = GetRowCount();
   1447  // Append the frames to the sibling chain
   1448  mFrames.AppendFrames(nullptr, std::move(aFrameList));
   1449 
   1450  if (rows.Length() > 0) {
   1451    nsTableFrame* tableFrame = GetTableFrame();
   1452    tableFrame->AppendRows(this, rowIndex, rows);
   1453    PresShell()->FrameNeedsReflow(this, IntrinsicDirty::FrameAndAncestors,
   1454                                  NS_FRAME_HAS_DIRTY_CHILDREN);
   1455    tableFrame->SetGeometryDirty();
   1456  }
   1457 }
   1458 
   1459 void nsTableRowGroupFrame::InsertFrames(
   1460    ChildListID aListID, nsIFrame* aPrevFrame,
   1461    const nsLineList::iterator* aPrevFrameLine, nsFrameList&& aFrameList) {
   1462  NS_ASSERTION(aListID == FrameChildListID::Principal, "unexpected child list");
   1463  NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
   1464               "inserting after sibling frame with different parent");
   1465 
   1466  DrainSelfOverflowList();  // ensure aPrevFrame is in mFrames
   1467  ClearRowCursor();
   1468 
   1469  // collect the new row frames in an array
   1470  // XXXbz why are we doing the QI stuff?  There shouldn't be any non-rows here.
   1471  nsTableFrame* tableFrame = GetTableFrame();
   1472  nsTArray<nsTableRowFrame*> rows;
   1473  bool gotFirstRow = false;
   1474  for (nsIFrame* f : aFrameList) {
   1475    nsTableRowFrame* rowFrame = do_QueryFrame(f);
   1476    NS_ASSERTION(rowFrame, "Unexpected frame; frame constructor screwed up");
   1477    if (rowFrame) {
   1478      NS_ASSERTION(
   1479          mozilla::StyleDisplay::TableRow == f->StyleDisplay()->mDisplay,
   1480          "wrong display type on rowframe");
   1481      rows.AppendElement(rowFrame);
   1482      if (!gotFirstRow) {
   1483        rowFrame->SetFirstInserted(true);
   1484        gotFirstRow = true;
   1485        tableFrame->SetRowInserted(true);
   1486      }
   1487    }
   1488  }
   1489 
   1490  int32_t startRowIndex = GetStartRowIndex();
   1491  // Insert the frames in the sibling chain
   1492  mFrames.InsertFrames(nullptr, aPrevFrame, std::move(aFrameList));
   1493 
   1494  int32_t numRows = rows.Length();
   1495  if (numRows > 0) {
   1496    nsTableRowFrame* prevRow =
   1497        (nsTableRowFrame*)nsTableFrame::GetFrameAtOrBefore(
   1498            this, aPrevFrame, LayoutFrameType::TableRow);
   1499    int32_t rowIndex = (prevRow) ? prevRow->GetRowIndex() + 1 : startRowIndex;
   1500    tableFrame->InsertRows(this, rows, rowIndex, true);
   1501 
   1502    PresShell()->FrameNeedsReflow(this, IntrinsicDirty::FrameAndAncestors,
   1503                                  NS_FRAME_HAS_DIRTY_CHILDREN);
   1504    tableFrame->SetGeometryDirty();
   1505  }
   1506 }
   1507 
   1508 void nsTableRowGroupFrame::RemoveFrame(DestroyContext& aContext,
   1509                                       ChildListID aListID,
   1510                                       nsIFrame* aOldFrame) {
   1511  NS_ASSERTION(aListID == FrameChildListID::Principal, "unexpected child list");
   1512 
   1513  ClearRowCursor();
   1514 
   1515  // XXX why are we doing the QI stuff?  There shouldn't be any non-rows here.
   1516  nsTableRowFrame* rowFrame = do_QueryFrame(aOldFrame);
   1517  if (rowFrame) {
   1518    nsTableFrame* tableFrame = GetTableFrame();
   1519    // remove the rows from the table (and flag a rebalance)
   1520    tableFrame->RemoveRows(*rowFrame, 1, true);
   1521 
   1522    PresShell()->FrameNeedsReflow(this, IntrinsicDirty::FrameAndAncestors,
   1523                                  NS_FRAME_HAS_DIRTY_CHILDREN);
   1524    tableFrame->SetGeometryDirty();
   1525  }
   1526  mFrames.DestroyFrame(aContext, aOldFrame);
   1527 }
   1528 
   1529 /* virtual */
   1530 nsMargin nsTableRowGroupFrame::GetUsedMargin() const {
   1531  return nsMargin(0, 0, 0, 0);
   1532 }
   1533 
   1534 /* virtual */
   1535 nsMargin nsTableRowGroupFrame::GetUsedBorder() const {
   1536  return nsMargin(0, 0, 0, 0);
   1537 }
   1538 
   1539 /* virtual */
   1540 nsMargin nsTableRowGroupFrame::GetUsedPadding() const {
   1541  return nsMargin(0, 0, 0, 0);
   1542 }
   1543 
   1544 nscoord nsTableRowGroupFrame::GetBSizeBasis(const ReflowInput& aReflowInput) {
   1545  nscoord result = 0;
   1546  nsTableFrame* tableFrame = GetTableFrame();
   1547  int32_t startRowIndex = GetStartRowIndex();
   1548  if ((aReflowInput.ComputedBSize() > 0) &&
   1549      (aReflowInput.ComputedBSize() < NS_UNCONSTRAINEDSIZE)) {
   1550    nscoord cellSpacing = tableFrame->GetRowSpacing(
   1551        startRowIndex,
   1552        std::max(startRowIndex, startRowIndex + GetRowCount() - 1));
   1553    result = aReflowInput.ComputedBSize() - cellSpacing;
   1554  } else {
   1555    const ReflowInput* parentRI = aReflowInput.mParentReflowInput;
   1556    if (parentRI && (tableFrame != parentRI->mFrame)) {
   1557      parentRI = parentRI->mParentReflowInput;
   1558    }
   1559    if (parentRI && (tableFrame == parentRI->mFrame) &&
   1560        (parentRI->ComputedBSize() > 0) &&
   1561        (parentRI->ComputedBSize() < NS_UNCONSTRAINEDSIZE)) {
   1562      nscoord cellSpacing =
   1563          tableFrame->GetRowSpacing(-1, tableFrame->GetRowCount());
   1564      result = parentRI->ComputedBSize() - cellSpacing;
   1565    }
   1566  }
   1567 
   1568  return result;
   1569 }
   1570 
   1571 bool nsTableRowGroupFrame::IsSimpleRowFrame(nsTableFrame* aTableFrame,
   1572                                            nsTableRowFrame* aRowFrame) {
   1573  int32_t rowIndex = aRowFrame->GetRowIndex();
   1574 
   1575  // It's a simple row frame if there are no cells that span into or
   1576  // across the row
   1577  int32_t numEffCols = aTableFrame->GetEffectiveColCount();
   1578  if (!aTableFrame->RowIsSpannedInto(rowIndex, numEffCols) &&
   1579      !aTableFrame->RowHasSpanningCells(rowIndex, numEffCols)) {
   1580    return true;
   1581  }
   1582 
   1583  return false;
   1584 }
   1585 
   1586 /** find page break before the first row **/
   1587 bool nsTableRowGroupFrame::HasInternalBreakBefore() const {
   1588  nsIFrame* firstChild = mFrames.FirstChild();
   1589  if (!firstChild) {
   1590    return false;
   1591  }
   1592  return firstChild->StyleDisplay()->BreakBefore();
   1593 }
   1594 
   1595 /** find page break after the last row **/
   1596 bool nsTableRowGroupFrame::HasInternalBreakAfter() const {
   1597  nsIFrame* lastChild = mFrames.LastChild();
   1598  if (!lastChild) {
   1599    return false;
   1600  }
   1601  return lastChild->StyleDisplay()->BreakAfter();
   1602 }
   1603 /* ----- global methods ----- */
   1604 
   1605 nsTableRowGroupFrame* NS_NewTableRowGroupFrame(PresShell* aPresShell,
   1606                                               ComputedStyle* aStyle) {
   1607  return new (aPresShell)
   1608      nsTableRowGroupFrame(aStyle, aPresShell->GetPresContext());
   1609 }
   1610 
   1611 NS_IMPL_FRAMEARENA_HELPERS(nsTableRowGroupFrame)
   1612 
   1613 #ifdef DEBUG_FRAME_DUMP
   1614 nsresult nsTableRowGroupFrame::GetFrameName(nsAString& aResult) const {
   1615  return MakeFrameName(u"TableRowGroup"_ns, aResult);
   1616 }
   1617 #endif
   1618 
   1619 LogicalMargin nsTableRowGroupFrame::GetBCBorderWidth(WritingMode aWM) {
   1620  LogicalMargin border(aWM);
   1621  nsTableRowFrame* firstRowFrame = GetFirstRow();
   1622  if (!firstRowFrame) {
   1623    return border;
   1624  }
   1625  nsTableRowFrame* lastRowFrame = firstRowFrame;
   1626  for (nsTableRowFrame* rowFrame = firstRowFrame->GetNextRow(); rowFrame;
   1627       rowFrame = rowFrame->GetNextRow()) {
   1628    lastRowFrame = rowFrame;
   1629  }
   1630  border.BStart(aWM) = firstRowFrame->GetBStartBCBorderWidth();
   1631  border.BEnd(aWM) = lastRowFrame->GetBEndBCBorderWidth();
   1632  return border;
   1633 }
   1634 
   1635 // nsILineIterator methods
   1636 int32_t nsTableRowGroupFrame::GetNumLines() const { return GetRowCount(); }
   1637 
   1638 bool nsTableRowGroupFrame::IsLineIteratorFlowRTL() {
   1639  return StyleDirection::Rtl == GetTableFrame()->StyleVisibility()->mDirection;
   1640 }
   1641 
   1642 Result<nsILineIterator::LineInfo, nsresult> nsTableRowGroupFrame::GetLine(
   1643    int32_t aLineNumber) {
   1644  if ((aLineNumber < 0) || (aLineNumber >= GetRowCount())) {
   1645    return Err(NS_ERROR_FAILURE);
   1646  }
   1647  LineInfo structure;
   1648  nsTableFrame* table = GetTableFrame();
   1649  nsTableCellMap* cellMap = table->GetCellMap();
   1650  aLineNumber += GetStartRowIndex();
   1651 
   1652  structure.mNumFramesOnLine =
   1653      cellMap->GetNumCellsOriginatingInRow(aLineNumber);
   1654  if (structure.mNumFramesOnLine == 0) {
   1655    return structure;
   1656  }
   1657  int32_t colCount = table->GetColCount();
   1658  for (int32_t i = 0; i < colCount; i++) {
   1659    CellData* data = cellMap->GetDataAt(aLineNumber, i);
   1660    if (data && data->IsOrig()) {
   1661      structure.mFirstFrameOnLine = (nsIFrame*)data->GetCellFrame();
   1662      nsIFrame* parent = structure.mFirstFrameOnLine->GetParent();
   1663      structure.mLineBounds = parent->GetRect();
   1664      return structure;
   1665    }
   1666  }
   1667  MOZ_ASSERT_UNREACHABLE("cellmap is lying");
   1668  return Err(NS_ERROR_FAILURE);
   1669 }
   1670 
   1671 int32_t nsTableRowGroupFrame::FindLineContaining(const nsIFrame* aFrame,
   1672                                                 int32_t aStartLine) {
   1673  NS_ENSURE_TRUE(aFrame, -1);
   1674 
   1675  const nsTableRowFrame* rowFrame = do_QueryFrame(aFrame);
   1676  if (MOZ_UNLIKELY(!rowFrame)) {
   1677    // When we do not have valid table structure in the DOM tree, somebody wants
   1678    // to check the line number with an out-of-flow child of this frame because
   1679    // its parent frame is set to this frame.  Otherwise, the caller must have
   1680    // a bug.
   1681    MOZ_ASSERT(aFrame->GetParent() == this);
   1682    MOZ_ASSERT(aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW));
   1683    return -1;
   1684  }
   1685 
   1686  int32_t rowIndexInGroup = rowFrame->GetRowIndex() - GetStartRowIndex();
   1687 
   1688  return rowIndexInGroup >= aStartLine ? rowIndexInGroup : -1;
   1689 }
   1690 
   1691 NS_IMETHODIMP
   1692 nsTableRowGroupFrame::CheckLineOrder(int32_t aLine, bool* aIsReordered,
   1693                                     nsIFrame** aFirstVisual,
   1694                                     nsIFrame** aLastVisual) {
   1695  *aIsReordered = false;
   1696  *aFirstVisual = nullptr;
   1697  *aLastVisual = nullptr;
   1698  return NS_OK;
   1699 }
   1700 
   1701 NS_IMETHODIMP
   1702 nsTableRowGroupFrame::FindFrameAt(int32_t aLineNumber, nsPoint aPos,
   1703                                  nsIFrame** aFrameFound,
   1704                                  bool* aPosIsBeforeFirstFrame,
   1705                                  bool* aPosIsAfterLastFrame) {
   1706  nsTableFrame* table = GetTableFrame();
   1707  nsTableCellMap* cellMap = table->GetCellMap();
   1708 
   1709  *aFrameFound = nullptr;
   1710  *aPosIsBeforeFirstFrame = true;
   1711  *aPosIsAfterLastFrame = false;
   1712 
   1713  aLineNumber += GetStartRowIndex();
   1714  int32_t numCells = cellMap->GetNumCellsOriginatingInRow(aLineNumber);
   1715  if (numCells == 0) {
   1716    return NS_OK;
   1717  }
   1718 
   1719  nsIFrame* frame = nullptr;
   1720  int32_t colCount = table->GetColCount();
   1721  for (int32_t i = 0; i < colCount; i++) {
   1722    CellData* data = cellMap->GetDataAt(aLineNumber, i);
   1723    if (data && data->IsOrig()) {
   1724      frame = (nsIFrame*)data->GetCellFrame();
   1725      break;
   1726    }
   1727  }
   1728  NS_ASSERTION(frame, "cellmap is lying");
   1729  bool isRTL = StyleDirection::Rtl == table->StyleVisibility()->mDirection;
   1730 
   1731  LineFrameFinder finder(aPos, table->GetSize(), table->GetWritingMode(),
   1732                         isRTL);
   1733 
   1734  int32_t n = numCells;
   1735  while (n--) {
   1736    finder.Scan(frame);
   1737    if (finder.IsDone()) {
   1738      break;
   1739    }
   1740    frame = frame->GetNextSibling();
   1741  }
   1742  finder.Finish(aFrameFound, aPosIsBeforeFirstFrame, aPosIsAfterLastFrame);
   1743  return NS_OK;
   1744 }
   1745 
   1746 // end nsLineIterator methods
   1747 
   1748 NS_DECLARE_FRAME_PROPERTY_DELETABLE(RowCursorProperty,
   1749                                    nsTableRowGroupFrame::FrameCursorData)
   1750 
   1751 void nsTableRowGroupFrame::ClearRowCursor() {
   1752  if (!HasAnyStateBits(NS_ROWGROUP_HAS_ROW_CURSOR)) {
   1753    return;
   1754  }
   1755 
   1756  RemoveStateBits(NS_ROWGROUP_HAS_ROW_CURSOR);
   1757  RemoveProperty(RowCursorProperty());
   1758 }
   1759 
   1760 nsTableRowGroupFrame::FrameCursorData* nsTableRowGroupFrame::SetupRowCursor() {
   1761  if (HasAnyStateBits(NS_ROWGROUP_HAS_ROW_CURSOR)) {
   1762    // We already have a valid row cursor. Don't waste time rebuilding it.
   1763    return nullptr;
   1764  }
   1765 
   1766  nsIFrame* f = mFrames.FirstChild();
   1767  int32_t count;
   1768  for (count = 0; f && count < MIN_ROWS_NEEDING_CURSOR; ++count) {
   1769    f = f->GetNextSibling();
   1770  }
   1771  if (!f) {
   1772    // Less than MIN_ROWS_NEEDING_CURSOR rows, so just don't bother
   1773    return nullptr;
   1774  }
   1775 
   1776  FrameCursorData* data = new FrameCursorData();
   1777  SetProperty(RowCursorProperty(), data);
   1778  AddStateBits(NS_ROWGROUP_HAS_ROW_CURSOR);
   1779  return data;
   1780 }
   1781 
   1782 nsIFrame* nsTableRowGroupFrame::GetFirstRowContaining(nscoord aY,
   1783                                                      nscoord* aOverflowAbove) {
   1784  if (!HasAnyStateBits(NS_ROWGROUP_HAS_ROW_CURSOR)) {
   1785    return nullptr;
   1786  }
   1787 
   1788  FrameCursorData* property = GetProperty(RowCursorProperty());
   1789  uint32_t cursorIndex = property->mCursorIndex;
   1790  uint32_t frameCount = property->mFrames.Length();
   1791  if (cursorIndex >= frameCount) {
   1792    return nullptr;
   1793  }
   1794  nsIFrame* cursorFrame = property->mFrames[cursorIndex];
   1795 
   1796  // The cursor's frame list excludes frames with empty overflow-area, so
   1797  // we don't need to check that here.
   1798 
   1799  // We use property->mOverflowBelow here instead of computing the frame's
   1800  // true overflowArea.YMost(), because it is essential for the thresholds
   1801  // to form a monotonically increasing sequence. Otherwise we would break
   1802  // encountering a row whose overflowArea.YMost() is <= aY but which has
   1803  // a row above it containing cell(s) that span to include aY.
   1804  while (cursorIndex > 0 &&
   1805         cursorFrame->GetRect().YMost() + property->mOverflowBelow > aY) {
   1806    --cursorIndex;
   1807    cursorFrame = property->mFrames[cursorIndex];
   1808  }
   1809  while (cursorIndex + 1 < frameCount &&
   1810         cursorFrame->GetRect().YMost() + property->mOverflowBelow <= aY) {
   1811    ++cursorIndex;
   1812    cursorFrame = property->mFrames[cursorIndex];
   1813  }
   1814 
   1815  property->mCursorIndex = cursorIndex;
   1816  *aOverflowAbove = property->mOverflowAbove;
   1817  return cursorFrame;
   1818 }
   1819 
   1820 bool nsTableRowGroupFrame::FrameCursorData::AppendFrame(nsIFrame* aFrame) {
   1821  // The cursor requires a monotonically increasing sequence in order to
   1822  // identify which rows can be skipped, and position:relative can move
   1823  // rows around such that the overflow areas don't provide this.
   1824  // We take the union of the overflow rect, and the frame's 'normal' position
   1825  // (excluding position:relative changes) and record the max difference between
   1826  // this combined overflow and the frame's rect.
   1827  nsRect positionedOverflowRect = aFrame->InkOverflowRect();
   1828  nsPoint positionedToNormal =
   1829      aFrame->GetNormalPosition() - aFrame->GetPosition();
   1830  nsRect normalOverflowRect = positionedOverflowRect + positionedToNormal;
   1831 
   1832  nsRect overflowRect = positionedOverflowRect.Union(normalOverflowRect);
   1833  if (overflowRect.IsEmpty()) {
   1834    return true;
   1835  }
   1836  nscoord overflowAbove = -overflowRect.y;
   1837  nscoord overflowBelow = overflowRect.YMost() - aFrame->GetSize().height;
   1838  mOverflowAbove = std::max(mOverflowAbove, overflowAbove);
   1839  mOverflowBelow = std::max(mOverflowBelow, overflowBelow);
   1840  // XXX(Bug 1631371) Check if this should use a fallible operation as it
   1841  // pretended earlier, or change the return type to void.
   1842  mFrames.AppendElement(aFrame);
   1843  return true;
   1844 }
   1845 
   1846 void nsTableRowGroupFrame::InvalidateFrame(uint32_t aDisplayItemKey,
   1847                                           bool aRebuildDisplayItems) {
   1848  nsIFrame::InvalidateFrame(aDisplayItemKey, aRebuildDisplayItems);
   1849  if (GetTableFrame()->IsBorderCollapse()) {
   1850    const bool rebuild = StaticPrefs::layout_display_list_retain_sc();
   1851    GetParent()->InvalidateFrameWithRect(InkOverflowRect() + GetPosition(),
   1852                                         aDisplayItemKey, rebuild);
   1853  }
   1854 }
   1855 
   1856 void nsTableRowGroupFrame::InvalidateFrameWithRect(const nsRect& aRect,
   1857                                                   uint32_t aDisplayItemKey,
   1858                                                   bool aRebuildDisplayItems) {
   1859  nsIFrame::InvalidateFrameWithRect(aRect, aDisplayItemKey,
   1860                                    aRebuildDisplayItems);
   1861  // If we have filters applied that would affects our bounds, then
   1862  // we get an inactive layer created and this is computed
   1863  // within FrameLayerBuilder
   1864  GetParent()->InvalidateFrameWithRect(aRect + GetPosition(), aDisplayItemKey,
   1865                                       aRebuildDisplayItems);
   1866 }