Coverage Report

Created: 2018-09-25 14:53

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