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"
7 #include "mozilla/ComputedStyle.h"
8 #include "mozilla/PresShell.h"
9 #include "mozilla/StaticPrefs_layout.h"
12 #include "nsTableRowFrame.h"
13 #include "nsTableFrame.h"
14 #include "nsTableCellFrame.h"
15 #include "nsPresContext.h"
16 #include "nsStyleConsts.h"
17 #include "nsIContent.h"
19 #include "nsIFrameInlines.h"
20 #include "nsGkAtoms.h"
21 #include "nsCSSRendering.h"
22 #include "nsHTMLParts.h"
23 #include "nsCSSFrameConstructor.h"
24 #include "nsDisplayList.h"
26 #include "nsCellMap.h" //table cell navigation
29 using namespace mozilla
;
30 using namespace mozilla::layout
;
34 struct TableRowGroupReflowInput final
{
36 const ReflowInput
& mReflowInput
;
38 // The available size (computed from the parent)
39 LogicalSize mAvailSize
;
41 // Running block-offset
44 explicit TableRowGroupReflowInput(const ReflowInput
& aReflowInput
)
45 : mReflowInput(aReflowInput
), mAvailSize(aReflowInput
.AvailableSize()) {}
47 ~TableRowGroupReflowInput() = default;
50 } // namespace mozilla
52 nsTableRowGroupFrame::nsTableRowGroupFrame(ComputedStyle
* aStyle
,
53 nsPresContext
* aPresContext
)
54 : nsContainerFrame(aStyle
, aPresContext
, kClassID
) {
58 nsTableRowGroupFrame::~nsTableRowGroupFrame() = default;
60 void nsTableRowGroupFrame::Destroy(DestroyContext
& aContext
) {
61 nsTableFrame::MaybeUnregisterPositionedTablePart(this);
62 nsContainerFrame::Destroy(aContext
);
65 NS_QUERYFRAME_HEAD(nsTableRowGroupFrame
)
66 NS_QUERYFRAME_ENTRY(nsTableRowGroupFrame
)
67 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame
)
69 int32_t nsTableRowGroupFrame::GetRowCount() const {
71 for (nsIFrame
* f
: mFrames
) {
72 NS_ASSERTION(f
->StyleDisplay()->mDisplay
== mozilla::StyleDisplay::TableRow
,
73 "Unexpected display");
74 NS_ASSERTION(f
->IsTableRowFrame(), "Unexpected frame type");
78 return mFrames
.GetLength();
81 int32_t nsTableRowGroupFrame::GetStartRowIndex() const {
83 if (mFrames
.NotEmpty()) {
84 NS_ASSERTION(mFrames
.FirstChild()->IsTableRowFrame(),
85 "Unexpected frame type");
86 result
= static_cast<nsTableRowFrame
*>(mFrames
.FirstChild())->GetRowIndex();
88 // if the row group doesn't have any children, get it the hard way
90 return GetTableFrame()->GetStartRowIndex(this);
96 void nsTableRowGroupFrame::AdjustRowIndices(int32_t aRowIndex
,
97 int32_t anAdjustment
) {
98 for (nsIFrame
* rowFrame
: mFrames
) {
99 if (mozilla::StyleDisplay::TableRow
== rowFrame
->StyleDisplay()->mDisplay
) {
100 int32_t index
= ((nsTableRowFrame
*)rowFrame
)->GetRowIndex();
101 if (index
>= aRowIndex
) {
102 ((nsTableRowFrame
*)rowFrame
)->SetRowIndex(index
+ anAdjustment
);
108 int32_t nsTableRowGroupFrame::GetAdjustmentForStoredIndex(
109 int32_t aStoredIndex
) {
110 nsTableFrame
* tableFrame
= GetTableFrame();
111 return tableFrame
->GetAdjustmentForStoredIndex(aStoredIndex
);
114 void nsTableRowGroupFrame::MarkRowsAsDeleted(nsTableRowFrame
& aStartRowFrame
,
115 int32_t aNumRowsToDelete
) {
116 nsTableRowFrame
* currentRowFrame
= &aStartRowFrame
;
118 // XXXneerja - Instead of calling AddDeletedRowIndex() per row frame
119 // it is possible to change AddDeleteRowIndex to instead take
120 // <start row index> and <num of rows to mark for deletion> as arguments.
121 // The problem that emerges here is mDeletedRowIndexRanges only stores
122 // disjoint index ranges and since AddDeletedRowIndex() must operate on
123 // the "stored" index, in some cases it is possible that the range
124 // of indices to delete becomes overlapping EG: Deleting rows 9 - 11 and
125 // then from the remaining rows deleting the *new* rows 7 to 20.
126 // Handling these overlapping ranges is much more complicated to
127 // implement and so I opted to add the deleted row index of one row at a
128 // time and maintain the invariant that the range of deleted row indices
129 // is always disjoint.
130 currentRowFrame
->AddDeletedRowIndex();
131 if (--aNumRowsToDelete
== 0) {
134 currentRowFrame
= do_QueryFrame(currentRowFrame
->GetNextSibling());
135 if (!currentRowFrame
) {
136 MOZ_ASSERT_UNREACHABLE("expected another row frame");
142 void nsTableRowGroupFrame::AddDeletedRowIndex(int32_t aDeletedRowStoredIndex
) {
143 nsTableFrame
* tableFrame
= GetTableFrame();
144 return tableFrame
->AddDeletedRowIndex(aDeletedRowStoredIndex
);
147 void nsTableRowGroupFrame::InitRepeatedFrame(
148 nsTableRowGroupFrame
* aHeaderFooterFrame
) {
149 nsTableRowFrame
* copyRowFrame
= GetFirstRow();
150 nsTableRowFrame
* originalRowFrame
= aHeaderFooterFrame
->GetFirstRow();
151 AddStateBits(NS_REPEATED_ROW_OR_ROWGROUP
);
152 while (copyRowFrame
&& originalRowFrame
) {
153 copyRowFrame
->AddStateBits(NS_REPEATED_ROW_OR_ROWGROUP
);
154 int rowIndex
= originalRowFrame
->GetRowIndex();
155 copyRowFrame
->SetRowIndex(rowIndex
);
157 // For each table cell frame set its column index
158 nsTableCellFrame
* originalCellFrame
= originalRowFrame
->GetFirstCell();
159 nsTableCellFrame
* copyCellFrame
= copyRowFrame
->GetFirstCell();
160 while (copyCellFrame
&& originalCellFrame
) {
162 originalCellFrame
->GetContent() == copyCellFrame
->GetContent(),
163 "cell frames have different content");
164 uint32_t colIndex
= originalCellFrame
->ColIndex();
165 copyCellFrame
->SetColIndex(colIndex
);
167 // Move to the next cell frame
168 copyCellFrame
= copyCellFrame
->GetNextCell();
169 originalCellFrame
= originalCellFrame
->GetNextCell();
172 // Move to the next row frame
173 originalRowFrame
= originalRowFrame
->GetNextRow();
174 copyRowFrame
= copyRowFrame
->GetNextRow();
178 // Handle the child-traversal part of DisplayGenericTablePart
179 static void DisplayRows(nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
,
180 const nsDisplayListSet
& aLists
) {
181 nscoord overflowAbove
;
182 nsTableRowGroupFrame
* f
= static_cast<nsTableRowGroupFrame
*>(aFrame
);
183 // Don't try to use the row cursor if we have to descend into placeholders;
184 // we might have rows containing placeholders, where the row's overflow
185 // area doesn't intersect the dirty rect but we need to descend into the row
186 // to see out of flows.
187 // Note that we really want to check ShouldDescendIntoFrame for all
188 // the rows in |f|, but that's exactly what we're trying to avoid, so we
189 // approximate it by checking it for |f|: if it's true for any row
190 // in |f| then it's true for |f| itself.
191 nsIFrame
* kid
= aBuilder
->ShouldDescendIntoFrame(f
, true)
193 : f
->GetFirstRowContaining(aBuilder
->GetVisibleRect().y
,
197 // have a cursor, use it
199 if (kid
->GetRect().y
- overflowAbove
>=
200 aBuilder
->GetVisibleRect().YMost()) {
203 f
->BuildDisplayListForChild(aBuilder
, kid
, aLists
);
204 kid
= kid
->GetNextSibling();
209 // No cursor. Traverse children the hard way and build a cursor while we're at
211 nsTableRowGroupFrame::FrameCursorData
* cursor
= f
->SetupRowCursor();
212 kid
= f
->PrincipalChildList().FirstChild();
214 f
->BuildDisplayListForChild(aBuilder
, kid
, aLists
);
217 if (!cursor
->AppendFrame(kid
)) {
223 kid
= kid
->GetNextSibling();
226 cursor
->FinishBuildingCursor();
230 void nsTableRowGroupFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
231 const nsDisplayListSet
& aLists
) {
232 DisplayOutsetBoxShadow(aBuilder
, aLists
.BorderBackground());
234 for (nsTableRowFrame
* row
= GetFirstRow(); row
; row
= row
->GetNextRow()) {
235 if (!aBuilder
->GetDirtyRect().Intersects(row
->InkOverflowRect() +
236 row
->GetNormalPosition())) {
239 row
->PaintCellBackgroundsForFrame(this, aBuilder
, aLists
,
240 row
->GetNormalPosition());
243 DisplayInsetBoxShadow(aBuilder
, aLists
.BorderBackground());
245 DisplayOutline(aBuilder
, aLists
);
247 DisplayRows(aBuilder
, this, aLists
);
250 LogicalSides
nsTableRowGroupFrame::GetLogicalSkipSides() const {
251 LogicalSides
skip(mWritingMode
);
252 if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak
==
253 StyleBoxDecorationBreak::Clone
)) {
257 if (GetPrevInFlow()) {
258 skip
+= LogicalSide::BStart
;
260 if (GetNextInFlow()) {
261 skip
+= LogicalSide::BEnd
;
266 // Position and size aKidFrame and update our reflow input.
267 void nsTableRowGroupFrame::PlaceChild(
268 nsPresContext
* aPresContext
, TableRowGroupReflowInput
& aReflowInput
,
269 nsIFrame
* aKidFrame
, const ReflowInput
& aKidReflowInput
, WritingMode aWM
,
270 const LogicalPoint
& aKidPosition
, const nsSize
& aContainerSize
,
271 ReflowOutput
& aDesiredSize
, const nsRect
& aOriginalKidRect
,
272 const nsRect
& aOriginalKidInkOverflow
) {
273 bool isFirstReflow
= aKidFrame
->HasAnyStateBits(NS_FRAME_FIRST_REFLOW
);
275 // Place and size the child
276 FinishReflowChild(aKidFrame
, aPresContext
, aDesiredSize
, &aKidReflowInput
,
277 aWM
, aKidPosition
, aContainerSize
,
278 ReflowChildFlags::ApplyRelativePositioning
);
280 nsTableFrame
* tableFrame
= GetTableFrame();
281 if (tableFrame
->IsBorderCollapse()) {
282 nsTableFrame::InvalidateTableFrame(aKidFrame
, aOriginalKidRect
,
283 aOriginalKidInkOverflow
, isFirstReflow
);
286 // Adjust the running block-offset
287 aReflowInput
.mBCoord
+= aDesiredSize
.BSize(aWM
);
289 // If our block-size is constrained then update the available bsize
290 if (NS_UNCONSTRAINEDSIZE
!= aReflowInput
.mAvailSize
.BSize(aWM
)) {
291 aReflowInput
.mAvailSize
.BSize(aWM
) -= aDesiredSize
.BSize(aWM
);
295 void nsTableRowGroupFrame::InitChildReflowInput(nsPresContext
* aPresContext
,
296 bool aBorderCollapse
,
297 ReflowInput
& aReflowInput
) {
298 const auto childWM
= aReflowInput
.GetWritingMode();
299 LogicalMargin
border(childWM
);
300 if (aBorderCollapse
) {
301 auto* rowFrame
= static_cast<nsTableRowFrame
*>(aReflowInput
.mFrame
);
302 border
= rowFrame
->GetBCBorderWidth(childWM
);
304 const LogicalMargin
zeroPadding(childWM
);
305 aReflowInput
.Init(aPresContext
, Nothing(), Some(border
), Some(zeroPadding
));
308 static void CacheRowBSizesForPrinting(nsTableRowFrame
* aFirstRow
,
310 for (nsTableRowFrame
* row
= aFirstRow
; row
; row
= row
->GetNextRow()) {
311 if (!row
->GetPrevInFlow()) {
312 row
->SetUnpaginatedBSize(row
->BSize(aWM
));
317 void nsTableRowGroupFrame::ReflowChildren(
318 nsPresContext
* aPresContext
, ReflowOutput
& aDesiredSize
,
319 TableRowGroupReflowInput
& aReflowInput
, nsReflowStatus
& aStatus
,
320 bool* aPageBreakBeforeEnd
) {
321 if (aPageBreakBeforeEnd
) {
322 *aPageBreakBeforeEnd
= false;
325 WritingMode wm
= aReflowInput
.mReflowInput
.GetWritingMode();
326 nsTableFrame
* tableFrame
= GetTableFrame();
327 const bool borderCollapse
= tableFrame
->IsBorderCollapse();
329 // XXXldb Should we really be checking IsPaginated(),
330 // or should we *only* check available block-size?
331 // (Think about multi-column layout!)
332 bool isPaginated
= aPresContext
->IsPaginated() &&
333 NS_UNCONSTRAINEDSIZE
!= aReflowInput
.mAvailSize
.BSize(wm
);
335 bool reflowAllKids
= aReflowInput
.mReflowInput
.ShouldReflowAllKids() ||
336 tableFrame
->IsGeometryDirty() ||
337 tableFrame
->NeedToCollapse();
339 // in vertical-rl mode, we always need the row bsizes in order to
340 // get the necessary containerSize for placing our kids
341 bool needToCalcRowBSizes
= reflowAllKids
|| wm
.IsVerticalRL();
343 nsSize containerSize
=
344 aReflowInput
.mReflowInput
.ComputedSizeAsContainerIfConstrained();
346 nsIFrame
* prevKidFrame
= nullptr;
347 for (nsTableRowFrame
* kidFrame
= GetFirstRow(); kidFrame
;
348 prevKidFrame
= kidFrame
, kidFrame
= kidFrame
->GetNextRow()) {
349 const nscoord rowSpacing
=
350 tableFrame
->GetRowSpacing(kidFrame
->GetRowIndex());
352 // Reflow the row frame
353 if (reflowAllKids
|| kidFrame
->IsSubtreeDirty() ||
354 (aReflowInput
.mReflowInput
.mFlags
.mSpecialBSizeReflow
&&
356 kidFrame
->HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE
)))) {
357 LogicalRect oldKidRect
= kidFrame
->GetLogicalRect(wm
, containerSize
);
358 nsRect oldKidInkOverflow
= kidFrame
->InkOverflowRect();
360 ReflowOutput
kidDesiredSize(aReflowInput
.mReflowInput
);
362 // Reflow the child into the available space, giving it as much bsize as
363 // it wants. We'll deal with splitting later after we've computed the row
364 // bsizes, taking into account cells with row spans...
365 LogicalSize kidAvailSize
= aReflowInput
.mAvailSize
;
366 kidAvailSize
.BSize(wm
) = NS_UNCONSTRAINEDSIZE
;
367 ReflowInput
kidReflowInput(aPresContext
, aReflowInput
.mReflowInput
,
368 kidFrame
, kidAvailSize
, Nothing(),
369 ReflowInput::InitFlag::CallerWillInit
);
370 InitChildReflowInput(aPresContext
, borderCollapse
, kidReflowInput
);
372 // This can indicate that columns were resized.
373 if (aReflowInput
.mReflowInput
.IsIResize()) {
374 kidReflowInput
.SetIResize(true);
377 NS_ASSERTION(kidFrame
== mFrames
.FirstChild() || prevKidFrame
,
378 "If we're not on the first frame, we should have a "
379 "previous sibling...");
380 // If prev row has nonzero YMost, then we can't be at the top of the page
381 if (prevKidFrame
&& prevKidFrame
->GetNormalRect().YMost() > 0) {
382 kidReflowInput
.mFlags
.mIsTopOfPage
= false;
385 LogicalPoint
kidPosition(wm
, 0, aReflowInput
.mBCoord
);
386 ReflowChild(kidFrame
, aPresContext
, kidDesiredSize
, kidReflowInput
, wm
,
387 kidPosition
, containerSize
, ReflowChildFlags::Default
,
391 PlaceChild(aPresContext
, aReflowInput
, kidFrame
, kidReflowInput
, wm
,
392 kidPosition
, containerSize
, kidDesiredSize
,
393 oldKidRect
.GetPhysicalRect(wm
, containerSize
),
395 aReflowInput
.mBCoord
+= rowSpacing
;
397 if (!reflowAllKids
) {
398 if (IsSimpleRowFrame(tableFrame
, kidFrame
)) {
399 // Inform the row of its new bsize.
400 kidFrame
->DidResize();
401 // the overflow area may have changed inflate the overflow area
402 const nsStylePosition
* stylePos
= StylePosition();
403 if (tableFrame
->IsAutoBSize(wm
) &&
404 !stylePos
->BSize(wm
).ConvertsToLength()) {
405 // Because other cells in the row may need to be aligned
406 // differently, repaint the entire row
408 } else if (oldKidRect
.BSize(wm
) != kidDesiredSize
.BSize(wm
)) {
409 needToCalcRowBSizes
= true;
412 needToCalcRowBSizes
= true;
416 if (isPaginated
&& aPageBreakBeforeEnd
&& !*aPageBreakBeforeEnd
) {
417 nsTableRowFrame
* nextRow
= kidFrame
->GetNextRow();
419 *aPageBreakBeforeEnd
=
420 nsTableFrame::PageBreakAfter(kidFrame
, nextRow
);
424 // Move a child that was skipped during a reflow.
425 const LogicalPoint oldPosition
=
426 kidFrame
->GetLogicalNormalPosition(wm
, containerSize
);
427 if (oldPosition
.B(wm
) != aReflowInput
.mBCoord
) {
428 kidFrame
->InvalidateFrameSubtree();
429 const LogicalPoint
offset(wm
, 0,
430 aReflowInput
.mBCoord
- oldPosition
.B(wm
));
431 kidFrame
->MovePositionBy(wm
, offset
);
432 nsTableFrame::RePositionViews(kidFrame
);
433 kidFrame
->InvalidateFrameSubtree();
436 // Adjust the running b-offset so we know where the next row should be
438 nscoord bSize
= kidFrame
->BSize(wm
) + rowSpacing
;
439 aReflowInput
.mBCoord
+= bSize
;
441 if (NS_UNCONSTRAINEDSIZE
!= aReflowInput
.mAvailSize
.BSize(wm
)) {
442 aReflowInput
.mAvailSize
.BSize(wm
) -= bSize
;
445 ConsiderChildOverflow(aDesiredSize
.mOverflowAreas
, kidFrame
);
449 aReflowInput
.mBCoord
-=
450 tableFrame
->GetRowSpacing(GetStartRowIndex() + GetRowCount());
453 // Return our desired rect
454 aDesiredSize
.ISize(wm
) = aReflowInput
.mReflowInput
.AvailableISize();
455 aDesiredSize
.BSize(wm
) = aReflowInput
.mBCoord
;
457 if (aReflowInput
.mReflowInput
.mFlags
.mSpecialBSizeReflow
) {
458 DidResizeRows(aDesiredSize
);
460 CacheRowBSizesForPrinting(GetFirstRow(), wm
);
462 } else if (needToCalcRowBSizes
) {
463 CalculateRowBSizes(aPresContext
, aDesiredSize
, aReflowInput
.mReflowInput
);
464 if (!reflowAllKids
) {
470 nsTableRowFrame
* nsTableRowGroupFrame::GetFirstRow() const {
471 nsIFrame
* firstChild
= mFrames
.FirstChild();
473 !firstChild
|| static_cast<nsTableRowFrame
*>(do_QueryFrame(firstChild
)),
474 "How do we have a non-row child?");
475 return static_cast<nsTableRowFrame
*>(firstChild
);
478 nsTableRowFrame
* nsTableRowGroupFrame::GetLastRow() const {
479 nsIFrame
* lastChild
= mFrames
.LastChild();
481 !lastChild
|| static_cast<nsTableRowFrame
*>(do_QueryFrame(lastChild
)),
482 "How do we have a non-row child?");
483 return static_cast<nsTableRowFrame
*>(lastChild
);
487 RowInfo() { bSize
= pctBSize
= hasStyleBSize
= hasPctBSize
= isSpecial
= 0; }
488 unsigned bSize
; // content bsize or fixed bsize, excluding pct bsize
489 unsigned pctBSize
: 29; // pct bsize
490 unsigned hasStyleBSize
: 1;
491 unsigned hasPctBSize
: 1;
492 unsigned isSpecial
: 1; // there is no cell originating in the row with
493 // rowspan=1 and there are at least 2 cells spanning
494 // the row and there is no style bsize on the row
497 static void UpdateBSizes(RowInfo
& aRowInfo
, nscoord aAdditionalBSize
,
498 nscoord
& aTotal
, nscoord
& aUnconstrainedTotal
) {
499 aRowInfo
.bSize
+= aAdditionalBSize
;
500 aTotal
+= aAdditionalBSize
;
501 if (!aRowInfo
.hasStyleBSize
) {
502 aUnconstrainedTotal
+= aAdditionalBSize
;
506 void nsTableRowGroupFrame::DidResizeRows(ReflowOutput
& aDesiredSize
) {
507 // Update the cells spanning rows with their new bsizes.
508 // This is the place where all of the cells in the row get set to the bsize
510 // Reset the overflow area.
511 aDesiredSize
.mOverflowAreas
.Clear();
512 for (nsTableRowFrame
* rowFrame
= GetFirstRow(); rowFrame
;
513 rowFrame
= rowFrame
->GetNextRow()) {
514 rowFrame
->DidResize();
515 ConsiderChildOverflow(aDesiredSize
.mOverflowAreas
, rowFrame
);
519 // This calculates the bsize of all the rows and takes into account
520 // style bsize on the row group, style bsizes on rows and cells, style bsizes on
521 // rowspans. Actual row bsizes will be adjusted later if the table has a style
522 // bsize. Even if rows don't change bsize, this method must be called to set the
523 // bsizes of each cell in the row to the bsize of its row.
524 void nsTableRowGroupFrame::CalculateRowBSizes(nsPresContext
* aPresContext
,
525 ReflowOutput
& aDesiredSize
,
526 const ReflowInput
& aReflowInput
) {
527 nsTableFrame
* tableFrame
= GetTableFrame();
528 const bool isPaginated
= aPresContext
->IsPaginated();
530 int32_t numEffCols
= tableFrame
->GetEffectiveColCount();
532 int32_t startRowIndex
= GetStartRowIndex();
533 // find the row corresponding to the row index we just found
534 nsTableRowFrame
* startRowFrame
= GetFirstRow();
536 if (!startRowFrame
) {
540 // The current row group block-size is the block-origin of the 1st row
541 // we are about to calculate a block-size for.
542 WritingMode wm
= aReflowInput
.GetWritingMode();
543 nsSize containerSize
; // actual value is unimportant as we're initially
544 // computing sizes, not physical positions
545 nscoord startRowGroupBSize
=
546 startRowFrame
->GetLogicalNormalPosition(wm
, containerSize
).B(wm
);
549 GetRowCount() - (startRowFrame
->GetRowIndex() - GetStartRowIndex());
550 // Collect the current bsize of each row.
555 AutoTArray
<RowInfo
, 32> rowInfo
;
556 // XXX(Bug 1631371) Check if this should use a fallible operation as it
557 // pretended earlier.
558 rowInfo
.AppendElements(numRows
);
560 bool hasRowSpanningCell
= false;
561 nscoord bSizeOfRows
= 0;
562 nscoord bSizeOfUnStyledRows
= 0;
563 // Get the bsize of each row without considering rowspans. This will be the
564 // max of the largest desired bsize of each cell, the largest style bsize of
565 // each cell, the style bsize of the row.
566 nscoord pctBSizeBasis
= GetBSizeBasis(aReflowInput
);
568 rowIndex
; // the index in rowInfo, not among the rows in the row group
569 nsTableRowFrame
* rowFrame
;
570 for (rowFrame
= startRowFrame
, rowIndex
= 0; rowFrame
;
571 rowFrame
= rowFrame
->GetNextRow(), rowIndex
++) {
572 nscoord nonPctBSize
= rowFrame
->GetContentBSize();
574 nonPctBSize
= std::max(nonPctBSize
, rowFrame
->BSize(wm
));
576 if (!rowFrame
->GetPrevInFlow()) {
577 if (rowFrame
->HasPctBSize()) {
578 rowInfo
[rowIndex
].hasPctBSize
= true;
579 rowInfo
[rowIndex
].pctBSize
= rowFrame
->GetInitialBSize(pctBSizeBasis
);
581 rowInfo
[rowIndex
].hasStyleBSize
= rowFrame
->HasStyleBSize();
582 nonPctBSize
= std::max(nonPctBSize
, rowFrame
->GetFixedBSize());
584 UpdateBSizes(rowInfo
[rowIndex
], nonPctBSize
, bSizeOfRows
,
585 bSizeOfUnStyledRows
);
587 if (!rowInfo
[rowIndex
].hasStyleBSize
) {
589 tableFrame
->HasMoreThanOneCell(rowIndex
+ startRowIndex
)) {
590 rowInfo
[rowIndex
].isSpecial
= true;
591 // iteratate the row's cell frames to see if any do not have rowspan > 1
592 nsTableCellFrame
* cellFrame
= rowFrame
->GetFirstCell();
594 int32_t rowSpan
= tableFrame
->GetEffectiveRowSpan(
595 rowIndex
+ startRowIndex
, *cellFrame
);
597 rowInfo
[rowIndex
].isSpecial
= false;
600 cellFrame
= cellFrame
->GetNextCell();
604 // See if a cell spans into the row. If so we'll have to do the next step
605 if (!hasRowSpanningCell
) {
606 if (tableFrame
->RowIsSpannedInto(rowIndex
+ startRowIndex
, numEffCols
)) {
607 hasRowSpanningCell
= true;
612 if (hasRowSpanningCell
) {
613 // Get the bsize of cells with rowspans and allocate any extra space to the
614 // rows they span iteratate the child frames and process the row frames
616 for (rowFrame
= startRowFrame
, rowIndex
= 0; rowFrame
;
617 rowFrame
= rowFrame
->GetNextRow(), rowIndex
++) {
618 // See if the row has an originating cell with rowspan > 1. We cannot
619 // determine this for a row in a continued row group by calling
620 // RowHasSpanningCells, because the row's fif may not have any originating
621 // cells yet the row may have a continued cell which originates in it.
622 if (GetPrevInFlow() || tableFrame
->RowHasSpanningCells(
623 startRowIndex
+ rowIndex
, numEffCols
)) {
624 nsTableCellFrame
* cellFrame
= rowFrame
->GetFirstCell();
625 // iteratate the row's cell frames
627 const nscoord rowSpacing
=
628 tableFrame
->GetRowSpacing(startRowIndex
+ rowIndex
);
629 int32_t rowSpan
= tableFrame
->GetEffectiveRowSpan(
630 rowIndex
+ startRowIndex
, *cellFrame
);
631 if ((rowIndex
+ rowSpan
) > numRows
) {
632 // there might be rows pushed already to the nextInFlow
633 rowSpan
= numRows
- rowIndex
;
635 if (rowSpan
> 1) { // a cell with rowspan > 1, determine the bsize of
637 nscoord bsizeOfRowsSpanned
= 0;
638 nscoord bsizeOfUnStyledRowsSpanned
= 0;
639 nscoord numSpecialRowsSpanned
= 0;
640 nscoord cellSpacingTotal
= 0;
642 for (spanX
= 0; spanX
< rowSpan
; spanX
++) {
643 bsizeOfRowsSpanned
+= rowInfo
[rowIndex
+ spanX
].bSize
;
644 if (!rowInfo
[rowIndex
+ spanX
].hasStyleBSize
) {
645 bsizeOfUnStyledRowsSpanned
+= rowInfo
[rowIndex
+ spanX
].bSize
;
648 cellSpacingTotal
+= rowSpacing
;
650 if (rowInfo
[rowIndex
+ spanX
].isSpecial
) {
651 numSpecialRowsSpanned
++;
654 nscoord bsizeOfAreaSpanned
= bsizeOfRowsSpanned
+ cellSpacingTotal
;
655 // get the bsize of the cell
656 LogicalSize cellFrameSize
= cellFrame
->GetLogicalSize(wm
);
657 LogicalSize cellDesSize
= cellFrame
->GetDesiredSize();
658 cellDesSize
.BSize(wm
) = rowFrame
->CalcCellActualBSize(
659 cellFrame
, cellDesSize
.BSize(wm
), wm
);
660 cellFrameSize
.BSize(wm
) = cellDesSize
.BSize(wm
);
662 if (bsizeOfAreaSpanned
< cellFrameSize
.BSize(wm
)) {
663 // the cell's bsize is larger than the available space of the rows
664 // it spans so distribute the excess bsize to the rows affected
665 nscoord extra
= cellFrameSize
.BSize(wm
) - bsizeOfAreaSpanned
;
666 nscoord extraUsed
= 0;
667 if (0 == numSpecialRowsSpanned
) {
668 // NS_ASSERTION(bsizeOfRowsSpanned > 0, "invalid row span
670 bool haveUnStyledRowsSpanned
= (bsizeOfUnStyledRowsSpanned
> 0);
671 nscoord divisor
= (haveUnStyledRowsSpanned
)
672 ? bsizeOfUnStyledRowsSpanned
673 : bsizeOfRowsSpanned
;
675 for (spanX
= rowSpan
- 1; spanX
>= 0; spanX
--) {
676 if (!haveUnStyledRowsSpanned
||
677 !rowInfo
[rowIndex
+ spanX
].hasStyleBSize
) {
678 // The amount of additional space each row gets is
679 // proportional to its bsize
680 float percent
= ((float)rowInfo
[rowIndex
+ spanX
].bSize
) /
683 // give rows their percentage, except for the first row
684 // which gets the remainder
685 nscoord extraForRow
=
688 : NSToCoordRound(((float)(extra
)) * percent
);
689 extraForRow
= std::min(extraForRow
, extra
- extraUsed
);
690 // update the row bsize
691 UpdateBSizes(rowInfo
[rowIndex
+ spanX
], extraForRow
,
692 bSizeOfRows
, bSizeOfUnStyledRows
);
693 extraUsed
+= extraForRow
;
694 if (extraUsed
>= extra
) {
695 NS_ASSERTION((extraUsed
== extra
),
696 "invalid row bsize calculation");
702 // put everything in the last row
703 UpdateBSizes(rowInfo
[rowIndex
+ rowSpan
- 1], extra
,
704 bSizeOfRows
, bSizeOfUnStyledRows
);
707 // give the extra to the special rows
708 nscoord numSpecialRowsAllocated
= 0;
709 for (spanX
= rowSpan
- 1; spanX
>= 0; spanX
--) {
710 if (rowInfo
[rowIndex
+ spanX
].isSpecial
) {
711 // The amount of additional space each degenerate row gets
712 // is proportional to the number of them
713 float percent
= 1.0f
/ ((float)numSpecialRowsSpanned
);
715 // give rows their percentage, except for the first row
716 // which gets the remainder
717 nscoord extraForRow
=
718 (numSpecialRowsSpanned
- 1 == numSpecialRowsAllocated
)
720 : NSToCoordRound(((float)(extra
)) * percent
);
721 extraForRow
= std::min(extraForRow
, extra
- extraUsed
);
722 // update the row bsize
723 UpdateBSizes(rowInfo
[rowIndex
+ spanX
], extraForRow
,
724 bSizeOfRows
, bSizeOfUnStyledRows
);
725 extraUsed
+= extraForRow
;
726 if (extraUsed
>= extra
) {
727 NS_ASSERTION((extraUsed
== extra
),
728 "invalid row bsize calculation");
735 } // if (rowSpan > 1)
736 cellFrame
= cellFrame
->GetNextCell();
737 } // while (cellFrame)
738 } // if (tableFrame->RowHasSpanningCells(startRowIndex + rowIndex) {
739 } // while (rowFrame)
742 // pct bsize rows have already got their content bsizes.
743 // Give them their pct bsizes up to pctBSizeBasis
744 nscoord extra
= pctBSizeBasis
- bSizeOfRows
;
745 for (rowFrame
= startRowFrame
, rowIndex
= 0; rowFrame
&& (extra
> 0);
746 rowFrame
= rowFrame
->GetNextRow(), rowIndex
++) {
747 RowInfo
& rInfo
= rowInfo
[rowIndex
];
748 if (rInfo
.hasPctBSize
) {
750 (rInfo
.pctBSize
> rInfo
.bSize
) ? rInfo
.pctBSize
- rInfo
.bSize
: 0;
751 rowExtra
= std::min(rowExtra
, extra
);
752 UpdateBSizes(rInfo
, rowExtra
, bSizeOfRows
, bSizeOfUnStyledRows
);
757 bool styleBSizeAllocation
= false;
758 nscoord rowGroupBSize
= startRowGroupBSize
+ bSizeOfRows
+
759 tableFrame
->GetRowSpacing(0, numRows
- 1);
760 // if we have a style bsize, allocate the extra bsize to unconstrained rows
761 if ((aReflowInput
.ComputedBSize() > rowGroupBSize
) &&
762 (NS_UNCONSTRAINEDSIZE
!= aReflowInput
.ComputedBSize())) {
763 nscoord extraComputedBSize
= aReflowInput
.ComputedBSize() - rowGroupBSize
;
764 nscoord extraUsed
= 0;
765 bool haveUnStyledRows
= (bSizeOfUnStyledRows
> 0);
766 nscoord divisor
= (haveUnStyledRows
) ? bSizeOfUnStyledRows
: bSizeOfRows
;
768 styleBSizeAllocation
= true;
769 for (rowIndex
= 0; rowIndex
< numRows
; rowIndex
++) {
770 if (!haveUnStyledRows
|| !rowInfo
[rowIndex
].hasStyleBSize
) {
771 // The amount of additional space each row gets is based on the
772 // percentage of space it occupies
773 float percent
= ((float)rowInfo
[rowIndex
].bSize
) / ((float)divisor
);
774 // give rows their percentage, except for the last row which gets the
776 nscoord extraForRow
=
777 (numRows
- 1 == rowIndex
)
778 ? extraComputedBSize
- extraUsed
779 : NSToCoordRound(((float)extraComputedBSize
) * percent
);
780 extraForRow
= std::min(extraForRow
, extraComputedBSize
- extraUsed
);
781 // update the row bsize
782 UpdateBSizes(rowInfo
[rowIndex
], extraForRow
, bSizeOfRows
,
783 bSizeOfUnStyledRows
);
784 extraUsed
+= extraForRow
;
785 if (extraUsed
>= extraComputedBSize
) {
786 NS_ASSERTION((extraUsed
== extraComputedBSize
),
787 "invalid row bsize calculation");
793 rowGroupBSize
= aReflowInput
.ComputedBSize();
796 if (wm
.IsVertical()) {
797 // we need the correct containerSize below for block positioning in
798 // vertical-rl writing mode
799 containerSize
.width
= rowGroupBSize
;
802 nscoord bOrigin
= startRowGroupBSize
;
803 // update the rows with their (potentially) new bsizes
804 for (rowFrame
= startRowFrame
, rowIndex
= 0; rowFrame
;
805 rowFrame
= rowFrame
->GetNextRow(), rowIndex
++) {
806 nsRect rowBounds
= rowFrame
->GetRect();
807 LogicalSize
rowBoundsSize(wm
, rowBounds
.Size());
808 nsRect rowInkOverflow
= rowFrame
->InkOverflowRect();
810 bOrigin
- rowFrame
->GetLogicalNormalPosition(wm
, containerSize
).B(wm
);
813 (rowInfo
[rowIndex
].bSize
> 0) ? rowInfo
[rowIndex
].bSize
: 0;
815 if (deltaB
!= 0 || (rowBSize
!= rowBoundsSize
.BSize(wm
))) {
816 // Resize/move the row to its final size and position
818 rowFrame
->InvalidateFrameSubtree();
821 rowFrame
->MovePositionBy(wm
, LogicalPoint(wm
, 0, deltaB
));
822 rowFrame
->SetSize(LogicalSize(wm
, rowBoundsSize
.ISize(wm
), rowBSize
));
824 nsTableFrame::InvalidateTableFrame(rowFrame
, rowBounds
, rowInkOverflow
,
828 nsTableFrame::RePositionViews(rowFrame
);
829 // XXXbz we don't need to update our overflow area?
832 bOrigin
+= rowBSize
+ tableFrame
->GetRowSpacing(startRowIndex
+ rowIndex
);
835 if (isPaginated
&& styleBSizeAllocation
) {
836 // since the row group has a style bsize, cache the row bsizes,
837 // so next in flows can honor them
838 CacheRowBSizesForPrinting(GetFirstRow(), wm
);
841 DidResizeRows(aDesiredSize
);
843 aDesiredSize
.BSize(wm
) = rowGroupBSize
; // Adjust our desired size
846 nscoord
nsTableRowGroupFrame::CollapseRowGroupIfNecessary(nscoord aBTotalOffset
,
849 nsTableFrame
* tableFrame
= GetTableFrame();
850 nsSize containerSize
= tableFrame
->GetSize();
851 const nsStyleVisibility
* groupVis
= StyleVisibility();
852 bool collapseGroup
= StyleVisibility::Collapse
== groupVis
->mVisible
;
854 tableFrame
->SetNeedToCollapse(true);
857 OverflowAreas overflow
;
859 nsTableRowFrame
* rowFrame
= GetFirstRow();
860 bool didCollapse
= false;
861 nscoord bGroupOffset
= 0;
863 bGroupOffset
+= rowFrame
->CollapseRowIfNecessary(
864 bGroupOffset
, aISize
, collapseGroup
, didCollapse
);
865 ConsiderChildOverflow(overflow
, rowFrame
);
866 rowFrame
= rowFrame
->GetNextRow();
869 LogicalRect groupRect
= GetLogicalRect(aWM
, containerSize
);
870 nsRect oldGroupRect
= GetRect();
871 nsRect oldGroupInkOverflow
= InkOverflowRect();
873 groupRect
.BSize(aWM
) -= bGroupOffset
;
875 // add back the cellspacing between rowgroups
876 groupRect
.BSize(aWM
) +=
877 tableFrame
->GetRowSpacing(GetStartRowIndex() + GetRowCount());
880 groupRect
.BStart(aWM
) -= aBTotalOffset
;
881 groupRect
.ISize(aWM
) = aISize
;
883 if (aBTotalOffset
!= 0) {
884 InvalidateFrameSubtree();
887 SetRect(aWM
, groupRect
, containerSize
);
888 overflow
.UnionAllWith(
889 nsRect(0, 0, groupRect
.Width(aWM
), groupRect
.Height(aWM
)));
890 FinishAndStoreOverflow(overflow
, groupRect
.Size(aWM
).GetPhysicalSize(aWM
));
891 nsTableFrame::RePositionViews(this);
892 nsTableFrame::InvalidateTableFrame(this, oldGroupRect
, oldGroupInkOverflow
,
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));
904 // Add the continuing row frame to the child list.
905 mFrames
.InsertFrame(nullptr, aRowFrame
, contRowFrame
);
907 // Push the continuing row frame and the frames that follow.
908 // This needs to match `UndoContinuedRow`.
909 PushChildrenToOverflow(contRowFrame
, aRowFrame
);
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;
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
944 if ((rowSpan
> 1) && (rowIndex
+ rowSpan
> lastRowIndex
)) {
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
;
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
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
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
984 aContRow
= CreateContinuingRowFrame(aLastRow
);
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(
993 uint32_t colIndex
= cell
->ColIndex();
994 aContRow
->InsertCellFrame(contCell
, colIndex
);
1002 aDesiredBSize
= aLastRow
->GetLogicalNormalRect(wm
, aContainerSize
).BEnd(wm
);
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
) {
1012 return; // allow null aRow to avoid callers doing null checks
1015 // rowBefore was the prev-sibling of aRow's next-sibling before aRow was
1017 nsTableRowFrame
* rowBefore
= (nsTableRowFrame
*)aRow
->GetPrevInFlow();
1018 MOZ_ASSERT(mFrames
.ContainsFrame(rowBefore
),
1019 "rowBefore not in our frame list?");
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");
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
);
1035 // Put the overflow rows into our child list
1036 if (!overflows
->IsEmpty()) {
1037 mFrames
.InsertFrames(nullptr, rowBefore
, std::move(*overflows
));
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");
1050 const WritingMode wm
= aReflowInput
.GetWritingMode();
1051 nsTableRowFrame
* prevRowFrame
= nullptr;
1052 aDesiredSize
.BSize(wm
) = 0;
1053 aDesiredSize
.SetOverflowAreasToDesiredBounds();
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();
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");
1066 bool isTopOfPage
= aReflowInput
.mFlags
.mIsTopOfPage
;
1067 nsTableRowFrame
* firstRowThisPage
= GetFirstRow();
1069 // Need to dirty the table's geometry, or else the row might skip
1070 // reflowing its cell as an optimization.
1071 aTableFrame
->SetGeometryDirty();
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
1094 availSize
.BSize(wm
) = std::min(availSize
.BSize(wm
), rowRect
.BSize(wm
));
1096 ReflowInput
rowReflowInput(
1097 aPresContext
, aReflowInput
, rowFrame
,
1098 availSize
.ConvertTo(rowFrame
->GetWritingMode(), wm
), Nothing(),
1099 ReflowInput::InitFlag::CallerWillInit
);
1101 InitChildReflowInput(aPresContext
, borderCollapse
, rowReflowInput
);
1102 rowReflowInput
.mFlags
.mIsTopOfPage
= isTopOfPage
; // set top of page
1103 ReflowOutput
rowMetrics(aReflowInput
);
1105 // Get the old size before we reflow.
1106 nsRect oldRowRect
= rowFrame
->GetRect();
1107 nsRect oldRowInkOverflow
= rowFrame
->InkOverflowRect();
1109 // Reflow the cell with the constrained bsize. A cell with rowspan >1
1110 // will get this reflow later during SplitSpanningCells.
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
,
1119 FinishReflowChild(rowFrame
, aPresContext
, rowMetrics
, &rowReflowInput
,
1120 wm
, dummyPos
, dummyContainerSize
,
1121 ReflowChildFlags::NoMoveFrame
);
1122 rowFrame
->DidResize(ForceAlignTopForTableCell::Yes
);
1124 if (!aRowForcedPageBreak
&& !aStatus
.IsFullyComplete() &&
1125 ShouldAvoidBreakInside(aReflowInput
)) {
1126 aStatus
.SetInlineLineBreakBeforeAndReset();
1130 nsTableFrame::InvalidateTableFrame(rowFrame
, oldRowRect
,
1131 oldRowInkOverflow
, false);
1133 if (aStatus
.IsIncomplete()) {
1134 // The row frame is incomplete and all of the rowspan 1 cells' block
1136 if ((rowMetrics
.BSize(wm
) <= rowReflowInput
.AvailableBSize()) ||
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.
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
);
1148 aDesiredSize
.BSize(wm
) += rowSpacing
;
1151 // Put the row on the next page to give it more block-size.
1152 rowIsOnPage
= false;
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)
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();
1170 aStatus
.SetIncomplete();
1172 aDesiredSize
.BSize(wm
) += rowMetrics
.BSize(wm
);
1174 aDesiredSize
.BSize(wm
) += rowSpacing
;
1177 "Data loss - complete row needed more block-size than "
1178 "available, on top of page");
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;
1187 // Put the row on the next page to give it more block-size.
1188 rowIsOnPage
= false;
1191 nsTableRowFrame
* lastRowThisPage
= rowFrame
;
1192 nscoord spanningRowBEnd
= availBSize
;
1194 NS_ASSERTION(!contRow
,
1195 "We should not have created a continuation if none of "
1197 if (!prevRowFrame
||
1198 (!aRowForcedPageBreak
&& ShouldAvoidBreakInside(aReflowInput
))) {
1199 aStatus
.SetInlineLineBreakBeforeAndReset();
1203 prevRowFrame
->GetLogicalNormalRect(wm
, containerSize
).BEnd(wm
);
1204 lastRowThisPage
= prevRowFrame
;
1206 aStatus
.SetIncomplete();
1209 // reflow the cells with rowspan >1 that occur on the page
1210 nsTableRowFrame
* firstTruncatedRow
;
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
1219 if (firstTruncatedRow
== firstRowThisPage
) {
1220 if (aReflowInput
.mFlags
.mIsTopOfPage
) {
1221 NS_WARNING("data loss in a row spanned cell");
1223 // We can't push children, so let our parent reflow us again with
1225 aDesiredSize
.BSize(wm
) = rowRect
.BEnd(wm
);
1227 UndoContinuedRow(aPresContext
, contRow
);
1231 // Try to put firstTruncateRow on the next page
1232 nsTableRowFrame
* rowBefore
= firstTruncatedRow
->GetPrevRow();
1233 const nscoord oldSpanningRowBEnd
= spanningRowBEnd
;
1235 rowBefore
->GetLogicalNormalRect(wm
, containerSize
).BEnd(wm
);
1237 UndoContinuedRow(aPresContext
, contRow
);
1239 nsTableRowFrame
* oldLastRowThisPage
= lastRowThisPage
;
1240 lastRowThisPage
= rowBefore
;
1242 aStatus
.SetIncomplete();
1244 // Call SplitSpanningCells again with rowBefore as the last row on the
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
1255 UndoContinuedRow(aPresContext
, contRow
);
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");
1266 // Let our parent reflow us again with more space
1267 aDesiredSize
.BSize(wm
) = rowRect
.BEnd(wm
);
1269 UndoContinuedRow(aPresContext
, contRow
);
1275 aDesiredSize
.BSize(wm
) = std::max(aDesiredSize
.BSize(wm
), bMost
);
1278 aStatus
.SetIncomplete();
1281 if (aStatus
.IsIncomplete() && !contRow
) {
1282 if (nsTableRowFrame
* nextRow
= lastRowThisPage
->GetNextRow()) {
1283 PushChildrenToOverflow(nextRow
, lastRowThisPage
);
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()) {
1292 aStatus
.SetIncomplete();
1293 PushChildrenToOverflow(nextRow
, lastRowThisPage
);
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
);
1305 aStatus
.SetIncomplete();
1308 // After the 1st row that has a block-size, we can't be on top of the page
1310 isTopOfPage
= isTopOfPage
&& rowRect
.BEnd(wm
) == 0;
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.
1318 void nsTableRowGroupFrame::Reflow(nsPresContext
* aPresContext
,
1319 ReflowOutput
& aDesiredSize
,
1320 const ReflowInput
& aReflowInput
,
1321 nsReflowStatus
& aStatus
) {
1323 DO_GLOBAL_REFLOW_COUNT("nsTableRowGroupFrame");
1324 MOZ_ASSERT(aStatus
.IsEmpty(), "Caller should pass a fresh reflow status!");
1326 // Row geometry may be going to change so we need to invalidate any row
1330 // see if a special bsize reflow needs to occur due to having a pct bsize
1331 nsTableFrame::CheckRequestSpecialBSizeReflow(aReflowInput
);
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);
1341 // Check for an overflow list
1342 MoveOverflowToChildList();
1344 // Reflow the existing frames.
1345 bool splitDueToPageBreak
= false;
1346 ReflowChildren(aPresContext
, aDesiredSize
, state
, aStatus
,
1347 &splitDueToPageBreak
);
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;
1361 SplitRowGroup(aPresContext
, aDesiredSize
, aReflowInput
, tableFrame
, aStatus
,
1362 splitDueToPageBreak
);
1364 mutableRIFlags
.mSpecialBSizeReflow
= savedSpecialBSizeReflow
;
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();
1374 SetHasStyleBSize((NS_UNCONSTRAINEDSIZE
!= aReflowInput
.ComputedBSize()) &&
1375 (aReflowInput
.ComputedBSize() > 0));
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();
1381 aDesiredSize
.UnionOverflowAreasWithDesiredBounds();
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
)) {
1390 FinishAndStoreOverflow(&aDesiredSize
);
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();
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.
1403 return nsContainerFrame::ComputeCustomOverflow(aOverflowAreas
);
1407 void nsTableRowGroupFrame::DidSetComputedStyle(
1408 ComputedStyle
* aOldComputedStyle
) {
1409 nsContainerFrame::DidSetComputedStyle(aOldComputedStyle
);
1410 nsTableFrame::PositionedTablePartMaybeChanged(this, aOldComputedStyle
);
1412 if (!aOldComputedStyle
) {
1413 return; // avoid the following on init
1416 nsTableFrame
* tableFrame
= GetTableFrame();
1417 if (tableFrame
->IsBorderCollapse() &&
1418 tableFrame
->BCRecalcNeeded(aOldComputedStyle
, Style())) {
1419 TableArea
damageArea(0, GetStartRowIndex(), tableFrame
->GetColCount(),
1421 tableFrame
->AddBCDamageArea(damageArea
);
1425 void nsTableRowGroupFrame::AppendFrames(ChildListID aListID
,
1426 nsFrameList
&& aFrameList
) {
1427 NS_ASSERTION(aListID
== FrameChildListID::Principal
, "unexpected child list");
1429 DrainSelfOverflowList(); // ensure the last frame is in mFrames
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");
1440 mozilla::StyleDisplay::TableRow
== f
->StyleDisplay()->mDisplay
,
1441 "wrong display type on rowframe");
1442 rows
.AppendElement(rowFrame
);
1446 int32_t rowIndex
= GetRowCount();
1447 // Append the frames to the sibling chain
1448 mFrames
.AppendFrames(nullptr, std::move(aFrameList
));
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();
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");
1466 DrainSelfOverflowList(); // ensure aPrevFrame is in mFrames
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");
1479 mozilla::StyleDisplay::TableRow
== f
->StyleDisplay()->mDisplay
,
1480 "wrong display type on rowframe");
1481 rows
.AppendElement(rowFrame
);
1483 rowFrame
->SetFirstInserted(true);
1485 tableFrame
->SetRowInserted(true);
1490 int32_t startRowIndex
= GetStartRowIndex();
1491 // Insert the frames in the sibling chain
1492 mFrames
.InsertFrames(nullptr, aPrevFrame
, std::move(aFrameList
));
1494 int32_t numRows
= rows
.Length();
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);
1502 PresShell()->FrameNeedsReflow(this, IntrinsicDirty::FrameAndAncestors
,
1503 NS_FRAME_HAS_DIRTY_CHILDREN
);
1504 tableFrame
->SetGeometryDirty();
1508 void nsTableRowGroupFrame::RemoveFrame(DestroyContext
& aContext
,
1509 ChildListID aListID
,
1510 nsIFrame
* aOldFrame
) {
1511 NS_ASSERTION(aListID
== FrameChildListID::Principal
, "unexpected child list");
1515 // XXX why are we doing the QI stuff? There shouldn't be any non-rows here.
1516 nsTableRowFrame
* rowFrame
= do_QueryFrame(aOldFrame
);
1518 nsTableFrame
* tableFrame
= GetTableFrame();
1519 // remove the rows from the table (and flag a rebalance)
1520 tableFrame
->RemoveRows(*rowFrame
, 1, true);
1522 PresShell()->FrameNeedsReflow(this, IntrinsicDirty::FrameAndAncestors
,
1523 NS_FRAME_HAS_DIRTY_CHILDREN
);
1524 tableFrame
->SetGeometryDirty();
1526 mFrames
.DestroyFrame(aContext
, aOldFrame
);
1530 nsMargin
nsTableRowGroupFrame::GetUsedMargin() const {
1531 return nsMargin(0, 0, 0, 0);
1535 nsMargin
nsTableRowGroupFrame::GetUsedBorder() const {
1536 return nsMargin(0, 0, 0, 0);
1540 nsMargin
nsTableRowGroupFrame::GetUsedPadding() const {
1541 return nsMargin(0, 0, 0, 0);
1544 nscoord
nsTableRowGroupFrame::GetBSizeBasis(const ReflowInput
& aReflowInput
) {
1546 nsTableFrame
* tableFrame
= GetTableFrame();
1547 int32_t startRowIndex
= GetStartRowIndex();
1548 if ((aReflowInput
.ComputedBSize() > 0) &&
1549 (aReflowInput
.ComputedBSize() < NS_UNCONSTRAINEDSIZE
)) {
1550 nscoord cellSpacing
= tableFrame
->GetRowSpacing(
1552 std::max(startRowIndex
, startRowIndex
+ GetRowCount() - 1));
1553 result
= aReflowInput
.ComputedBSize() - cellSpacing
;
1555 const ReflowInput
* parentRI
= aReflowInput
.mParentReflowInput
;
1556 if (parentRI
&& (tableFrame
!= parentRI
->mFrame
)) {
1557 parentRI
= parentRI
->mParentReflowInput
;
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
;
1571 bool nsTableRowGroupFrame::IsSimpleRowFrame(nsTableFrame
* aTableFrame
,
1572 nsTableRowFrame
* aRowFrame
) {
1573 int32_t rowIndex
= aRowFrame
->GetRowIndex();
1575 // It's a simple row frame if there are no cells that span into or
1577 int32_t numEffCols
= aTableFrame
->GetEffectiveColCount();
1578 if (!aTableFrame
->RowIsSpannedInto(rowIndex
, numEffCols
) &&
1579 !aTableFrame
->RowHasSpanningCells(rowIndex
, numEffCols
)) {
1586 /** find page break before the first row **/
1587 bool nsTableRowGroupFrame::HasInternalBreakBefore() const {
1588 nsIFrame
* firstChild
= mFrames
.FirstChild();
1592 return firstChild
->StyleDisplay()->BreakBefore();
1595 /** find page break after the last row **/
1596 bool nsTableRowGroupFrame::HasInternalBreakAfter() const {
1597 nsIFrame
* lastChild
= mFrames
.LastChild();
1601 return lastChild
->StyleDisplay()->BreakAfter();
1603 /* ----- global methods ----- */
1605 nsTableRowGroupFrame
* NS_NewTableRowGroupFrame(PresShell
* aPresShell
,
1606 ComputedStyle
* aStyle
) {
1607 return new (aPresShell
)
1608 nsTableRowGroupFrame(aStyle
, aPresShell
->GetPresContext());
1611 NS_IMPL_FRAMEARENA_HELPERS(nsTableRowGroupFrame
)
1613 #ifdef DEBUG_FRAME_DUMP
1614 nsresult
nsTableRowGroupFrame::GetFrameName(nsAString
& aResult
) const {
1615 return MakeFrameName(u
"TableRowGroup"_ns
, aResult
);
1619 LogicalMargin
nsTableRowGroupFrame::GetBCBorderWidth(WritingMode aWM
) {
1620 LogicalMargin
border(aWM
);
1621 nsTableRowFrame
* firstRowFrame
= GetFirstRow();
1622 if (!firstRowFrame
) {
1625 nsTableRowFrame
* lastRowFrame
= firstRowFrame
;
1626 for (nsTableRowFrame
* rowFrame
= firstRowFrame
->GetNextRow(); rowFrame
;
1627 rowFrame
= rowFrame
->GetNextRow()) {
1628 lastRowFrame
= rowFrame
;
1630 border
.BStart(aWM
) = firstRowFrame
->GetBStartBCBorderWidth();
1631 border
.BEnd(aWM
) = lastRowFrame
->GetBEndBCBorderWidth();
1635 // nsILineIterator methods
1636 int32_t nsTableRowGroupFrame::GetNumLines() const { return GetRowCount(); }
1638 bool nsTableRowGroupFrame::IsLineIteratorFlowRTL() {
1639 return StyleDirection::Rtl
== GetTableFrame()->StyleVisibility()->mDirection
;
1642 Result
<nsILineIterator::LineInfo
, nsresult
> nsTableRowGroupFrame::GetLine(
1643 int32_t aLineNumber
) {
1644 if ((aLineNumber
< 0) || (aLineNumber
>= GetRowCount())) {
1645 return Err(NS_ERROR_FAILURE
);
1648 nsTableFrame
* table
= GetTableFrame();
1649 nsTableCellMap
* cellMap
= table
->GetCellMap();
1650 aLineNumber
+= GetStartRowIndex();
1652 structure
.mNumFramesOnLine
=
1653 cellMap
->GetNumCellsOriginatingInRow(aLineNumber
);
1654 if (structure
.mNumFramesOnLine
== 0) {
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();
1667 MOZ_ASSERT_UNREACHABLE("cellmap is lying");
1668 return Err(NS_ERROR_FAILURE
);
1671 int32_t nsTableRowGroupFrame::FindLineContaining(nsIFrame
* aFrame
,
1672 int32_t aStartLine
) {
1673 NS_ENSURE_TRUE(aFrame
, -1);
1675 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
1681 MOZ_ASSERT(aFrame
->GetParent() == this);
1682 MOZ_ASSERT(aFrame
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
));
1686 int32_t rowIndexInGroup
= rowFrame
->GetRowIndex() - GetStartRowIndex();
1688 return rowIndexInGroup
>= aStartLine
? rowIndexInGroup
: -1;
1692 nsTableRowGroupFrame::CheckLineOrder(int32_t aLine
, bool* aIsReordered
,
1693 nsIFrame
** aFirstVisual
,
1694 nsIFrame
** aLastVisual
) {
1695 *aIsReordered
= false;
1696 *aFirstVisual
= nullptr;
1697 *aLastVisual
= nullptr;
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();
1709 *aFrameFound
= nullptr;
1710 *aPosIsBeforeFirstFrame
= true;
1711 *aPosIsAfterLastFrame
= false;
1713 aLineNumber
+= GetStartRowIndex();
1714 int32_t numCells
= cellMap
->GetNumCellsOriginatingInRow(aLineNumber
);
1715 if (numCells
== 0) {
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();
1728 NS_ASSERTION(frame
, "cellmap is lying");
1729 bool isRTL
= StyleDirection::Rtl
== table
->StyleVisibility()->mDirection
;
1731 LineFrameFinder
finder(aPos
, table
->GetSize(), table
->GetWritingMode(),
1734 int32_t n
= numCells
;
1737 if (finder
.IsDone()) {
1740 frame
= frame
->GetNextSibling();
1742 finder
.Finish(aFrameFound
, aPosIsBeforeFirstFrame
, aPosIsAfterLastFrame
);
1746 // end nsLineIterator methods
1748 NS_DECLARE_FRAME_PROPERTY_DELETABLE(RowCursorProperty
,
1749 nsTableRowGroupFrame::FrameCursorData
)
1751 void nsTableRowGroupFrame::ClearRowCursor() {
1752 if (!HasAnyStateBits(NS_ROWGROUP_HAS_ROW_CURSOR
)) {
1756 RemoveStateBits(NS_ROWGROUP_HAS_ROW_CURSOR
);
1757 RemoveProperty(RowCursorProperty());
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.
1766 nsIFrame
* f
= mFrames
.FirstChild();
1768 for (count
= 0; f
&& count
< MIN_ROWS_NEEDING_CURSOR
; ++count
) {
1769 f
= f
->GetNextSibling();
1772 // Less than MIN_ROWS_NEEDING_CURSOR rows, so just don't bother
1776 FrameCursorData
* data
= new FrameCursorData();
1777 SetProperty(RowCursorProperty(), data
);
1778 AddStateBits(NS_ROWGROUP_HAS_ROW_CURSOR
);
1782 nsIFrame
* nsTableRowGroupFrame::GetFirstRowContaining(nscoord aY
,
1783 nscoord
* aOverflowAbove
) {
1784 if (!HasAnyStateBits(NS_ROWGROUP_HAS_ROW_CURSOR
)) {
1788 FrameCursorData
* property
= GetProperty(RowCursorProperty());
1789 uint32_t cursorIndex
= property
->mCursorIndex
;
1790 uint32_t frameCount
= property
->mFrames
.Length();
1791 if (cursorIndex
>= frameCount
) {
1794 nsIFrame
* cursorFrame
= property
->mFrames
[cursorIndex
];
1796 // The cursor's frame list excludes frames with empty overflow-area, so
1797 // we don't need to check that here.
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
) {
1807 cursorFrame
= property
->mFrames
[cursorIndex
];
1809 while (cursorIndex
+ 1 < frameCount
&&
1810 cursorFrame
->GetRect().YMost() + property
->mOverflowBelow
<= aY
) {
1812 cursorFrame
= property
->mFrames
[cursorIndex
];
1815 property
->mCursorIndex
= cursorIndex
;
1816 *aOverflowAbove
= property
->mOverflowAbove
;
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
;
1832 nsRect overflowRect
= positionedOverflowRect
.Union(normalOverflowRect
);
1833 if (overflowRect
.IsEmpty()) {
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
);
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
);
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
);