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/. */
6 #include "nsTableRowFrame.h"
8 #include "mozilla/Baseline.h"
9 #include "mozilla/Maybe.h"
10 #include "mozilla/PresShell.h"
11 #include "nsTableRowGroupFrame.h"
12 #include "nsPresContext.h"
13 #include "mozilla/ComputedStyle.h"
14 #include "mozilla/StaticPrefs_layout.h"
15 #include "nsStyleConsts.h"
16 #include "nsIContent.h"
18 #include "nsIFrameInlines.h"
19 #include "nsTableFrame.h"
20 #include "nsTableCellFrame.h"
21 #include "nsCSSRendering.h"
22 #include "nsHTMLParts.h"
23 #include "nsTableColGroupFrame.h"
24 #include "nsTableColFrame.h"
25 #include "nsDisplayList.h"
29 # include "nsAccessibilityService.h"
32 using namespace mozilla
;
36 struct TableCellReflowInput
: public ReflowInput
{
37 TableCellReflowInput(nsPresContext
* aPresContext
,
38 const ReflowInput
& aParentReflowInput
, nsIFrame
* aFrame
,
39 const LogicalSize
& aAvailableSpace
,
40 ReflowInput::InitFlags aFlags
= {})
41 : ReflowInput(aPresContext
, aParentReflowInput
, aFrame
, aAvailableSpace
,
44 void FixUp(const LogicalSize
& aAvailSpace
);
47 } // namespace mozilla
49 void TableCellReflowInput::FixUp(const LogicalSize
& aAvailSpace
) {
50 // fix the mComputed values during a pass 2 reflow since the cell can be a
53 NS_UNCONSTRAINEDSIZE
!= aAvailSpace
.ISize(mWritingMode
),
54 "have unconstrained inline-size; this should only result from very large "
55 "sizes, not attempts at intrinsic inline size calculation");
56 if (NS_UNCONSTRAINEDSIZE
!= ComputedISize()) {
57 nscoord computedISize
=
58 aAvailSpace
.ISize(mWritingMode
) -
59 ComputedLogicalBorderPadding(mWritingMode
).IStartEnd(mWritingMode
);
60 computedISize
= std::max(0, computedISize
);
61 SetComputedISize(computedISize
);
63 if (NS_UNCONSTRAINEDSIZE
!= ComputedBSize() &&
64 NS_UNCONSTRAINEDSIZE
!= aAvailSpace
.BSize(mWritingMode
)) {
65 nscoord computedBSize
=
66 aAvailSpace
.BSize(mWritingMode
) -
67 ComputedLogicalBorderPadding(mWritingMode
).BStartEnd(mWritingMode
);
68 computedBSize
= std::max(0, computedBSize
);
69 SetComputedBSize(computedBSize
);
73 void nsTableRowFrame::InitChildReflowInput(nsPresContext
& aPresContext
,
74 const LogicalSize
& aAvailSize
,
76 TableCellReflowInput
& aReflowInput
) {
77 Maybe
<LogicalMargin
> collapseBorder
;
78 if (aBorderCollapse
) {
79 // we only reflow cells, so don't need to check frame type
80 nsBCTableCellFrame
* bcCellFrame
= (nsBCTableCellFrame
*)aReflowInput
.mFrame
;
82 collapseBorder
.emplace(
83 bcCellFrame
->GetBorderWidth(aReflowInput
.GetWritingMode()));
86 aReflowInput
.Init(&aPresContext
, Nothing(), collapseBorder
);
87 aReflowInput
.FixUp(aAvailSize
);
90 void nsTableRowFrame::SetFixedBSize(nscoord aValue
) {
91 nscoord bsize
= std::max(0, aValue
);
92 if (HasFixedBSize()) {
93 if (bsize
> mStyleFixedBSize
) {
94 mStyleFixedBSize
= bsize
;
97 mStyleFixedBSize
= bsize
;
99 SetHasFixedBSize(true);
104 void nsTableRowFrame::SetPctBSize(float aPctValue
, bool aForce
) {
105 nscoord bsize
= std::max(0, NSToCoordRound(aPctValue
* 100.0f
));
107 if ((bsize
> mStylePctBSize
) || aForce
) {
108 mStylePctBSize
= bsize
;
111 mStylePctBSize
= bsize
;
113 SetHasPctBSize(true);
118 /* ----------- nsTableRowFrame ---------- */
120 NS_QUERYFRAME_HEAD(nsTableRowFrame
)
121 NS_QUERYFRAME_ENTRY(nsTableRowFrame
)
122 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame
)
124 nsTableRowFrame::nsTableRowFrame(ComputedStyle
* aStyle
,
125 nsPresContext
* aPresContext
, ClassID aID
)
126 : nsContainerFrame(aStyle
, aPresContext
, aID
) {
128 mBits
.mHasFixedBSize
= 0;
129 mBits
.mHasPctBSize
= 0;
130 mBits
.mFirstInserted
= 0;
134 nsTableRowFrame::~nsTableRowFrame() = default;
136 void nsTableRowFrame::Init(nsIContent
* aContent
, nsContainerFrame
* aParent
,
137 nsIFrame
* aPrevInFlow
) {
138 // Let the base class do its initialization
139 nsContainerFrame::Init(aContent
, aParent
, aPrevInFlow
);
141 NS_ASSERTION(mozilla::StyleDisplay::TableRow
== StyleDisplay()->mDisplay
,
142 "wrong display on table row frame");
146 nsTableRowFrame
* rowFrame
= (nsTableRowFrame
*)aPrevInFlow
;
148 SetRowIndex(rowFrame
->GetRowIndex());
150 mWritingMode
= GetTableFrame()->GetWritingMode();
154 void nsTableRowFrame::Destroy(DestroyContext
& aContext
) {
155 nsTableFrame::MaybeUnregisterPositionedTablePart(this);
156 nsContainerFrame::Destroy(aContext
);
160 void nsTableRowFrame::DidSetComputedStyle(ComputedStyle
* aOldComputedStyle
) {
161 nsContainerFrame::DidSetComputedStyle(aOldComputedStyle
);
162 nsTableFrame::PositionedTablePartMaybeChanged(this, aOldComputedStyle
);
164 if (!aOldComputedStyle
) {
165 return; // avoid the following on init
169 if (nsAccessibilityService
* accService
= GetAccService()) {
170 // If a table row's background color is now different from
171 // the background color of its previous row, it is possible our
172 // table now has alternating row colors. This changes whether or not
173 // the table is classified as a layout table or data table.
174 // We invalidate on every background color change to avoid
175 // walking the tree in search of the nearest row.
176 if (StyleBackground()->BackgroundColor(this) !=
177 aOldComputedStyle
->StyleBackground()->BackgroundColor(
178 aOldComputedStyle
)) {
179 // We send a notification here to invalidate the a11y cache on the
180 // table so the next fetch of IsProbablyLayoutTable() is accurate.
181 accService
->TableLayoutGuessMaybeChanged(PresShell(), mContent
);
186 nsTableFrame
* tableFrame
= GetTableFrame();
187 if (tableFrame
->IsBorderCollapse() &&
188 tableFrame
->BCRecalcNeeded(aOldComputedStyle
, Style())) {
189 TableArea
damageArea(0, GetRowIndex(), tableFrame
->GetColCount(), 1);
190 tableFrame
->AddBCDamageArea(damageArea
);
194 void nsTableRowFrame::AppendFrames(ChildListID aListID
,
195 nsFrameList
&& aFrameList
) {
196 NS_ASSERTION(aListID
== FrameChildListID::Principal
, "unexpected child list");
198 DrainSelfOverflowList(); // ensure the last frame is in mFrames
199 const nsFrameList::Slice
& newCells
=
200 mFrames
.AppendFrames(nullptr, std::move(aFrameList
));
202 // Add the new cell frames to the table
203 nsTableFrame
* tableFrame
= GetTableFrame();
204 for (nsIFrame
* childFrame
: newCells
) {
205 NS_ASSERTION(childFrame
->IsTableCellFrame(),
206 "Not a table cell frame/pseudo frame construction failure");
207 tableFrame
->AppendCell(static_cast<nsTableCellFrame
&>(*childFrame
),
211 PresShell()->FrameNeedsReflow(this, IntrinsicDirty::FrameAndAncestors
,
212 NS_FRAME_HAS_DIRTY_CHILDREN
);
213 tableFrame
->SetGeometryDirty();
216 void nsTableRowFrame::InsertFrames(ChildListID aListID
, nsIFrame
* aPrevFrame
,
217 const nsLineList::iterator
* aPrevFrameLine
,
218 nsFrameList
&& aFrameList
) {
219 NS_ASSERTION(aListID
== FrameChildListID::Principal
, "unexpected child list");
220 NS_ASSERTION(!aPrevFrame
|| aPrevFrame
->GetParent() == this,
221 "inserting after sibling frame with different parent");
222 if (mFrames
.IsEmpty() || (aPrevFrame
&& !aPrevFrame
->GetNextSibling())) {
223 // This is actually an append (though our caller didn't figure that out),
224 // and our append codepath is both simpler/faster _and_ less buggy.
225 // https://bugzilla.mozilla.org/show_bug.cgi?id=1388898 tracks the bugginess
226 AppendFrames(aListID
, std::move(aFrameList
));
230 DrainSelfOverflowList(); // ensure aPrevFrame is in mFrames
231 // Insert Frames in the frame list
232 const nsFrameList::Slice
& newCells
=
233 mFrames
.InsertFrames(nullptr, aPrevFrame
, std::move(aFrameList
));
235 nsTableCellFrame
* prevCellFrame
=
236 static_cast<nsTableCellFrame
*>(nsTableFrame::GetFrameAtOrBefore(
237 this, aPrevFrame
, LayoutFrameType::TableCell
));
238 nsTArray
<nsTableCellFrame
*> cellChildren
;
239 for (nsIFrame
* childFrame
: newCells
) {
240 NS_ASSERTION(childFrame
->IsTableCellFrame(),
241 "Not a table cell frame/pseudo frame construction failure");
242 cellChildren
.AppendElement(static_cast<nsTableCellFrame
*>(childFrame
));
244 // insert the cells into the cell map
245 int32_t colIndex
= -1;
247 colIndex
= prevCellFrame
->ColIndex();
249 nsTableFrame
* tableFrame
= GetTableFrame();
250 tableFrame
->InsertCells(cellChildren
, GetRowIndex(), colIndex
);
252 PresShell()->FrameNeedsReflow(this, IntrinsicDirty::FrameAndAncestors
,
253 NS_FRAME_HAS_DIRTY_CHILDREN
);
254 tableFrame
->SetGeometryDirty();
257 void nsTableRowFrame::RemoveFrame(DestroyContext
& aContext
, ChildListID aListID
,
258 nsIFrame
* aOldFrame
) {
259 NS_ASSERTION(aListID
== FrameChildListID::Principal
, "unexpected child list");
260 MOZ_ASSERT((nsTableCellFrame
*)do_QueryFrame(aOldFrame
));
262 auto* cellFrame
= static_cast<nsTableCellFrame
*>(aOldFrame
);
263 // remove the cell from the cell map
264 nsTableFrame
* tableFrame
= GetTableFrame();
265 tableFrame
->RemoveCell(cellFrame
, GetRowIndex());
267 // Remove the frame and destroy it
268 mFrames
.DestroyFrame(aContext
, aOldFrame
);
270 PresShell()->FrameNeedsReflow(this, IntrinsicDirty::FrameAndAncestors
,
271 NS_FRAME_HAS_DIRTY_CHILDREN
);
273 tableFrame
->SetGeometryDirty();
277 nsMargin
nsTableRowFrame::GetUsedMargin() const { return nsMargin(0, 0, 0, 0); }
280 nsMargin
nsTableRowFrame::GetUsedBorder() const { return nsMargin(0, 0, 0, 0); }
283 nsMargin
nsTableRowFrame::GetUsedPadding() const {
284 return nsMargin(0, 0, 0, 0);
287 static nscoord
GetBSizeOfRowsSpannedBelowFirst(
288 nsTableCellFrame
& aTableCellFrame
, nsTableFrame
& aTableFrame
,
289 const WritingMode aWM
) {
291 int32_t rowSpan
= aTableFrame
.GetEffectiveRowSpan(aTableCellFrame
);
292 // add in bsize of rows spanned beyond the 1st one
293 nsIFrame
* nextRow
= aTableCellFrame
.GetParent()->GetNextSibling();
294 for (int32_t rowX
= 1; ((rowX
< rowSpan
) && nextRow
);) {
295 if (nextRow
->IsTableRowFrame()) {
296 bsize
+= nextRow
->BSize(aWM
);
299 bsize
+= aTableFrame
.GetRowSpacing(rowX
);
300 nextRow
= nextRow
->GetNextSibling();
306 * Post-reflow hook. This is where the table row does its post-processing
308 void nsTableRowFrame::DidResize(ForceAlignTopForTableCell aForceAlignTop
) {
309 // Resize and re-align the cell frames based on our row bsize
310 nsTableFrame
* tableFrame
= GetTableFrame();
312 WritingMode wm
= GetWritingMode();
313 ReflowOutput
desiredSize(wm
);
314 desiredSize
.SetSize(wm
, GetLogicalSize(wm
));
315 desiredSize
.SetOverflowAreasToDesiredBounds();
317 nsSize containerSize
= mRect
.Size();
319 for (nsTableCellFrame
* cellFrame
= GetFirstCell(); cellFrame
;
320 cellFrame
= cellFrame
->GetNextCell()) {
321 nscoord cellBSize
= BSize(wm
) + GetBSizeOfRowsSpannedBelowFirst(
322 *cellFrame
, *tableFrame
, wm
);
324 // If the bsize for the cell has changed, we need to reset it;
325 // and in vertical-rl mode, we need to update the cell's block position
326 // to account for the containerSize, which may not have been known
327 // earlier, so we always apply it here.
328 LogicalSize cellSize
= cellFrame
->GetLogicalSize(wm
);
329 if (cellSize
.BSize(wm
) != cellBSize
|| wm
.IsVerticalRL()) {
330 nsRect cellOldRect
= cellFrame
->GetRect();
331 nsRect cellInkOverflow
= cellFrame
->InkOverflowRect();
333 if (wm
.IsVerticalRL()) {
334 // Get the old position of the cell, as we want to preserve its
335 // inline coordinate.
336 LogicalPoint oldPos
= cellFrame
->GetLogicalPosition(wm
, containerSize
);
338 // The cell should normally be aligned with the row's block-start,
339 // so set the B component of the position to zero:
340 LogicalPoint
newPos(wm
, oldPos
.I(wm
), 0);
342 // ...unless relative positioning is in effect, in which case the
343 // cell may have been moved away from the row's block-start
344 if (cellFrame
->IsRelativelyOrStickyPositioned()) {
345 // Find out where the cell would have been without relative
347 LogicalPoint oldNormalPos
=
348 cellFrame
->GetLogicalNormalPosition(wm
, containerSize
);
349 // The difference (if any) between oldPos and oldNormalPos reflects
350 // relative positioning that was applied to the cell, and which we
351 // need to incorporate when resetting the position.
352 newPos
.B(wm
) = oldPos
.B(wm
) - oldNormalPos
.B(wm
);
355 if (oldPos
!= newPos
) {
356 cellFrame
->SetPosition(wm
, newPos
, containerSize
);
357 nsTableFrame::RePositionViews(cellFrame
);
361 cellSize
.BSize(wm
) = cellBSize
;
362 cellFrame
->SetSize(wm
, cellSize
);
364 if (tableFrame
->IsBorderCollapse()) {
365 nsTableFrame::InvalidateTableFrame(cellFrame
, cellOldRect
,
366 cellInkOverflow
, false);
370 // realign cell content based on the new bsize. We might be able to
371 // skip this if the bsize didn't change... maybe. Hard to tell.
372 cellFrame
->BlockDirAlignChild(wm
, mMaxCellAscent
, aForceAlignTop
);
374 // Always store the overflow, even if the height didn't change, since
375 // we'll lose part of our overflow area otherwise.
376 ConsiderChildOverflow(desiredSize
.mOverflowAreas
, cellFrame
);
378 // Note that if the cell's *content* needs to change in response
379 // to this height, it will get a special bsize reflow.
381 FinishAndStoreOverflow(&desiredSize
);
383 nsContainerFrame::SyncFrameViewAfterReflow(PresContext(), this, GetView(),
384 desiredSize
.InkOverflow(),
385 ReflowChildFlags::Default
);
387 // Let our base class do the usual work
390 // returns max-ascent amongst all cells that have 'vertical-align: baseline'
391 // *including* cells with rowspans
392 nscoord
nsTableRowFrame::GetMaxCellAscent() const { return mMaxCellAscent
; }
394 Maybe
<nscoord
> nsTableRowFrame::GetRowBaseline(WritingMode aWM
) {
395 if (mMaxCellAscent
) {
396 return Some(mMaxCellAscent
);
399 // If we get here, we don't have a baseline on any of the cells in this row.
400 if (aWM
.IsCentralBaseline()) {
404 for (nsIFrame
* childFrame
: mFrames
) {
405 MOZ_ASSERT(childFrame
->IsTableCellFrame());
406 nscoord s
= Baseline::SynthesizeBOffsetFromContentBox(
407 childFrame
, aWM
, BaselineSharingGroup::First
);
408 ascent
= std::max(ascent
, s
);
413 nscoord
nsTableRowFrame::GetInitialBSize(nscoord aPctBasis
) const {
415 if ((aPctBasis
> 0) && HasPctBSize()) {
416 bsize
= NSToCoordRound(GetPctBSize() * (float)aPctBasis
);
418 if (HasFixedBSize()) {
419 bsize
= std::max(bsize
, GetFixedBSize());
421 return std::max(bsize
, GetContentBSize());
424 void nsTableRowFrame::ResetBSize() {
425 SetHasFixedBSize(false);
426 SetHasPctBSize(false);
435 void nsTableRowFrame::UpdateBSize(nscoord aBSize
, nscoord aAscent
,
436 nscoord aDescent
, nsTableFrame
* aTableFrame
,
437 nsTableCellFrame
* aCellFrame
) {
438 if (!aTableFrame
|| !aCellFrame
) {
439 NS_ASSERTION(false, "invalid call");
443 if (aBSize
== NS_UNCONSTRAINEDSIZE
) {
447 if (GetInitialBSize() < aBSize
&&
448 aTableFrame
->GetEffectiveRowSpan(*aCellFrame
) == 1) {
449 SetContentBSize(aBSize
);
452 if (aCellFrame
->HasVerticalAlignBaseline()) {
454 aAscent
!= NS_UNCONSTRAINEDSIZE
&& aDescent
!= NS_UNCONSTRAINEDSIZE
,
456 // see if this is a long ascender
457 if (mMaxCellAscent
< aAscent
) {
458 mMaxCellAscent
= aAscent
;
460 // see if this is a long descender and without rowspan
461 if (mMaxCellDescent
< aDescent
) {
462 if (aTableFrame
->GetEffectiveRowSpan(*aCellFrame
) == 1) {
463 mMaxCellDescent
= aDescent
;
469 nscoord
nsTableRowFrame::CalcBSize(const ReflowInput
& aReflowInput
) {
470 nsTableFrame
* tableFrame
= GetTableFrame();
473 const nscoord computedBSize
= aReflowInput
.ComputedBSize();
474 if (computedBSize
!= NS_UNCONSTRAINEDSIZE
&& computedBSize
> 0) {
475 SetFixedBSize(computedBSize
);
478 WritingMode wm
= aReflowInput
.GetWritingMode();
479 const nsStylePosition
* position
= StylePosition();
480 const auto& bsizeStyleCoord
= position
->BSize(wm
);
481 if (bsizeStyleCoord
.ConvertsToLength()) {
482 SetFixedBSize(bsizeStyleCoord
.ToLength());
483 } else if (bsizeStyleCoord
.ConvertsToPercentage()) {
484 SetPctBSize(bsizeStyleCoord
.ToPercentage());
487 for (nsTableCellFrame
* kidFrame
= GetFirstCell(); kidFrame
;
488 kidFrame
= kidFrame
->GetNextCell()) {
489 MOZ_ASSERT(kidFrame
->GetWritingMode() == wm
);
490 LogicalSize desSize
= kidFrame
->GetDesiredSize();
491 if (NS_UNCONSTRAINEDSIZE
== aReflowInput
.AvailableBSize() &&
493 desSize
.BSize(wm
) = CalcCellActualBSize(kidFrame
, desSize
.BSize(wm
), wm
);
495 // bsize may have changed, adjust descent to absorb any excess difference
497 if (!kidFrame
->PrincipalChildList()
499 ->PrincipalChildList()
501 ascent
= desSize
.BSize(wm
);
503 ascent
= kidFrame
->GetCellBaseline();
505 nscoord descent
= desSize
.BSize(wm
) - ascent
;
506 UpdateBSize(desSize
.BSize(wm
), ascent
, descent
, tableFrame
, kidFrame
);
508 return GetInitialBSize();
511 void nsTableRowFrame::PaintCellBackgroundsForFrame(
512 nsIFrame
* aFrame
, nsDisplayListBuilder
* aBuilder
,
513 const nsDisplayListSet
& aLists
, const nsPoint
& aOffset
) {
514 // Compute background rect by iterating all cell frame.
515 const nsPoint toReferenceFrame
= aBuilder
->ToReferenceFrame(aFrame
);
516 for (nsTableCellFrame
* cell
= GetFirstCell(); cell
;
517 cell
= cell
->GetNextCell()) {
518 if (!cell
->ShouldPaintBackground(aBuilder
)) {
523 cell
->GetRectRelativeToSelf() + cell
->GetNormalPosition() + aOffset
;
524 if (!aBuilder
->GetDirtyRect().Intersects(cellRect
)) {
527 cellRect
+= toReferenceFrame
;
528 nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
529 aBuilder
, aFrame
, cellRect
, aLists
.BorderBackground(), true,
530 aFrame
->GetRectRelativeToSelf() + toReferenceFrame
, cell
);
534 void nsTableRowFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
535 const nsDisplayListSet
& aLists
) {
536 DisplayOutsetBoxShadow(aBuilder
, aLists
.BorderBackground());
538 PaintCellBackgroundsForFrame(this, aBuilder
, aLists
);
540 DisplayInsetBoxShadow(aBuilder
, aLists
.BorderBackground());
542 DisplayOutline(aBuilder
, aLists
);
544 for (nsIFrame
* kid
: PrincipalChildList()) {
545 BuildDisplayListForChild(aBuilder
, kid
, aLists
);
549 LogicalSides
nsTableRowFrame::GetLogicalSkipSides() const {
550 LogicalSides
skip(mWritingMode
);
551 if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak
==
552 StyleBoxDecorationBreak::Clone
)) {
556 if (GetPrevInFlow()) {
557 skip
+= LogicalSide::BStart
;
559 if (GetNextInFlow()) {
560 skip
+= LogicalSide::BEnd
;
565 nscoord
nsTableRowFrame::CalcCellActualBSize(nsTableCellFrame
* aCellFrame
,
566 const nscoord
& aDesiredBSize
,
568 nscoord specifiedBSize
= 0;
570 // Get the bsize specified in the style information
571 const nsStylePosition
* position
= aCellFrame
->StylePosition();
573 int32_t rowSpan
= GetTableFrame()->GetEffectiveRowSpan(*aCellFrame
);
575 const auto& bsizeStyleCoord
= position
->BSize(aWM
);
576 if (bsizeStyleCoord
.ConvertsToLength()) {
577 // In quirks mode, table cell bsize should always be border-box.
578 // https://quirks.spec.whatwg.org/#the-table-cell-height-box-sizing-quirk
579 specifiedBSize
= bsizeStyleCoord
.ToLength();
580 if (PresContext()->CompatibilityMode() != eCompatibility_NavQuirks
&&
581 position
->mBoxSizing
== StyleBoxSizing::Content
) {
583 aCellFrame
->GetLogicalUsedBorderAndPadding(aWM
).BStartEnd(aWM
);
587 SetFixedBSize(specifiedBSize
);
589 } else if (bsizeStyleCoord
.ConvertsToPercentage()) {
591 SetPctBSize(bsizeStyleCoord
.ToPercentage());
595 // If the specified bsize is greater than the desired bsize,
596 // then use the specified bsize
597 return std::max(specifiedBSize
, aDesiredBSize
);
600 // Calculates the available isize for the table cell based on the known
601 // column isizes taking into account column spans and column spacing
602 static nscoord
CalcAvailISize(nsTableFrame
& aTableFrame
,
603 nsTableCellFrame
& aCellFrame
) {
604 nscoord cellAvailISize
= 0;
605 uint32_t colIndex
= aCellFrame
.ColIndex();
606 int32_t colspan
= aTableFrame
.GetEffectiveColSpan(aCellFrame
);
607 NS_ASSERTION(colspan
> 0, "effective colspan should be positive");
608 nsTableFrame
* fifTable
=
609 static_cast<nsTableFrame
*>(aTableFrame
.FirstInFlow());
611 for (int32_t spanX
= 0; spanX
< colspan
; spanX
++) {
612 cellAvailISize
+= fifTable
->GetColumnISizeFromFirstInFlow(colIndex
+ spanX
);
613 if (spanX
> 0 && aTableFrame
.ColumnHasCellSpacingBefore(colIndex
+ spanX
)) {
614 cellAvailISize
+= aTableFrame
.GetColSpacing(colIndex
+ spanX
- 1);
617 return cellAvailISize
;
620 static nscoord
GetSpaceBetween(int32_t aPrevColIndex
, int32_t aColIndex
,
621 int32_t aColSpan
, nsTableFrame
& aTableFrame
,
622 bool aCheckVisibility
) {
625 nsTableFrame
* fifTable
=
626 static_cast<nsTableFrame
*>(aTableFrame
.FirstInFlow());
627 for (colIdx
= aPrevColIndex
+ 1; aColIndex
> colIdx
; colIdx
++) {
628 bool isCollapsed
= false;
629 if (!aCheckVisibility
) {
630 space
+= fifTable
->GetColumnISizeFromFirstInFlow(colIdx
);
632 nsTableColFrame
* colFrame
= aTableFrame
.GetColFrame(colIdx
);
633 const nsStyleVisibility
* colVis
= colFrame
->StyleVisibility();
634 bool collapseCol
= StyleVisibility::Collapse
== colVis
->mVisible
;
635 nsIFrame
* cgFrame
= colFrame
->GetParent();
636 const nsStyleVisibility
* groupVis
= cgFrame
->StyleVisibility();
637 bool collapseGroup
= StyleVisibility::Collapse
== groupVis
->mVisible
;
638 isCollapsed
= collapseCol
|| collapseGroup
;
640 space
+= fifTable
->GetColumnISizeFromFirstInFlow(colIdx
);
643 if (!isCollapsed
&& aTableFrame
.ColumnHasCellSpacingBefore(colIdx
)) {
644 space
+= aTableFrame
.GetColSpacing(colIdx
- 1);
650 // subtract the bsizes of aRow's prev in flows from the unpaginated bsize
651 static nscoord
CalcBSizeFromUnpaginatedBSize(nsTableRowFrame
& aRow
,
654 nsTableRowFrame
* firstInFlow
=
655 static_cast<nsTableRowFrame
*>(aRow
.FirstInFlow());
656 if (firstInFlow
->HasUnpaginatedBSize()) {
657 bsize
= firstInFlow
->GetUnpaginatedBSize();
658 for (nsIFrame
* prevInFlow
= aRow
.GetPrevInFlow(); prevInFlow
;
659 prevInFlow
= prevInFlow
->GetPrevInFlow()) {
660 bsize
-= prevInFlow
->BSize(aWM
);
663 return std::max(bsize
, 0);
666 void nsTableRowFrame::ReflowChildren(nsPresContext
* aPresContext
,
667 ReflowOutput
& aDesiredSize
,
668 const ReflowInput
& aReflowInput
,
669 nsTableFrame
& aTableFrame
,
670 nsReflowStatus
& aStatus
) {
673 // XXXldb Should we be checking constrained bsize instead?
674 const bool isPaginated
= aPresContext
->IsPaginated();
675 const bool borderCollapse
= aTableFrame
.IsBorderCollapse();
677 int32_t cellColSpan
=
678 1; // must be defined here so it's set properly for non-cell kids
680 // remember the col index of the previous cell to handle rowspans into this
682 int32_t prevColIndex
= -1;
683 nscoord iCoord
= 0; // running total of children inline-coord offset
685 // This computes the max of all cell bsizes
686 nscoord cellMaxBSize
= 0;
688 // Reflow each of our existing cell frames
689 WritingMode wm
= aReflowInput
.GetWritingMode();
690 nsSize containerSize
= aReflowInput
.ComputedSizeAsContainerIfConstrained();
692 for (nsTableCellFrame
* kidFrame
= GetFirstCell(); kidFrame
;
693 kidFrame
= kidFrame
->GetNextCell()) {
694 // See if we should only reflow the dirty child frames
695 bool doReflowChild
= true;
696 if (!aReflowInput
.ShouldReflowAllKids() && !aTableFrame
.IsGeometryDirty() &&
697 !kidFrame
->IsSubtreeDirty()) {
698 if (!aReflowInput
.mFlags
.mSpecialBSizeReflow
) {
699 doReflowChild
= false;
701 } else if (NS_UNCONSTRAINEDSIZE
!= aReflowInput
.AvailableBSize()) {
702 // We don't reflow a rowspan >1 cell here with a constrained bsize.
703 // That happens in nsTableRowGroupFrame::SplitSpanningCells.
704 if (aTableFrame
.GetEffectiveRowSpan(*kidFrame
) > 1) {
705 doReflowChild
= false;
708 if (aReflowInput
.mFlags
.mSpecialBSizeReflow
&& !isPaginated
&&
709 !kidFrame
->HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE
)) {
713 uint32_t cellColIndex
= kidFrame
->ColIndex();
714 cellColSpan
= aTableFrame
.GetEffectiveColSpan(*kidFrame
);
716 // If the adjacent cell is in a prior row (because of a rowspan) add in the
717 // space NOTE: prevColIndex can be -1 here.
718 if (prevColIndex
!= (static_cast<int32_t>(cellColIndex
) - 1)) {
719 iCoord
+= GetSpaceBetween(prevColIndex
, cellColIndex
, cellColSpan
,
723 // remember the rightmost (ltr) or leftmost (rtl) column this cell spans
725 prevColIndex
= cellColIndex
+ (cellColSpan
- 1);
727 // Reflow the child frame
728 nsRect kidRect
= kidFrame
->GetRect();
729 LogicalPoint origKidNormalPosition
=
730 kidFrame
->GetLogicalNormalPosition(wm
, containerSize
);
732 nsRect kidInkOverflow
= kidFrame
->InkOverflowRect();
733 LogicalPoint
kidPosition(wm
, iCoord
, 0);
734 bool firstReflow
= kidFrame
->HasAnyStateBits(NS_FRAME_FIRST_REFLOW
);
737 // Calculate the available isize for the table cell using the known
739 nscoord availCellISize
= CalcAvailISize(aTableFrame
, *kidFrame
);
741 Maybe
<TableCellReflowInput
> kidReflowInput
;
742 ReflowOutput
desiredSize(aReflowInput
);
744 // If the avail isize is not the same as last time we reflowed the cell or
745 // the cell wants to be bigger than what was available last time or
746 // it is a style change reflow or we are printing, then we must reflow the
747 // cell. Otherwise we can skip the reflow.
748 // XXXldb Why is this condition distinct from doReflowChild above?
749 NS_ASSERTION(kidFrame
->GetWritingMode() == wm
,
750 "expected consistent writing-mode within table");
751 LogicalSize cellDesiredSize
= kidFrame
->GetDesiredSize();
752 if (availCellISize
!= kidFrame
->GetPriorAvailISize() ||
753 cellDesiredSize
.ISize(wm
) > kidFrame
->GetPriorAvailISize() ||
754 HasAnyStateBits(NS_FRAME_IS_DIRTY
) || isPaginated
||
755 kidFrame
->IsSubtreeDirty() ||
756 // See if it needs a special reflow, or if it had one that we need to
758 kidFrame
->HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE
) ||
759 kidFrame
->BCBordersChanged() || HasPctBSize()) {
760 // Reflow the cell to fit the available isize, bsize
761 // XXX The old IR_ChildIsDirty code used availCellISize here.
762 LogicalSize
kidAvailSize(wm
, availCellISize
,
763 aReflowInput
.AvailableBSize());
766 kidReflowInput
.emplace(aPresContext
, aReflowInput
, kidFrame
,
768 ReflowInput::InitFlag::CallerWillInit
);
769 InitChildReflowInput(*aPresContext
, kidAvailSize
, borderCollapse
,
772 nsReflowStatus status
;
773 ReflowChild(kidFrame
, aPresContext
, desiredSize
, *kidReflowInput
, wm
,
774 kidPosition
, containerSize
, ReflowChildFlags::Default
,
777 // allow the table to determine if/how the table needs to be rebalanced
778 // If any of the cells are not complete, then we're not complete
779 if (status
.IsIncomplete()) {
781 aStatus
.SetIncomplete();
784 if (iCoord
!= origKidNormalPosition
.I(wm
)) {
785 kidFrame
->InvalidateFrameSubtree();
788 desiredSize
.SetSize(wm
, cellDesiredSize
);
789 desiredSize
.mOverflowAreas
= kidFrame
->GetOverflowAreas();
791 // if we are in a floated table, our position is not yet established, so
792 // we cannot reposition our views the containing block will do this for
793 // us after positioning the table
794 if (!aTableFrame
.IsFloating()) {
795 // Because we may have moved the frame we need to make sure any views
796 // are positioned properly. We have to do this, because any one of our
797 // parent frames could have moved and we have no way of knowing...
798 nsTableFrame::RePositionViews(kidFrame
);
802 if (NS_UNCONSTRAINEDSIZE
== aReflowInput
.AvailableBSize()) {
803 if (!GetPrevInFlow()) {
804 desiredSize
.BSize(wm
) =
805 CalcCellActualBSize(kidFrame
, desiredSize
.BSize(wm
), wm
);
807 // bsize may have changed, adjust descent to absorb any excess
810 if (!kidFrame
->PrincipalChildList()
812 ->PrincipalChildList()
814 ascent
= desiredSize
.BSize(wm
);
816 ascent
= kidFrame
->GetCellBaseline();
818 nscoord descent
= desiredSize
.BSize(wm
) - ascent
;
819 UpdateBSize(desiredSize
.BSize(wm
), ascent
, descent
, &aTableFrame
,
822 cellMaxBSize
= std::max(cellMaxBSize
, desiredSize
.BSize(wm
));
823 int32_t rowSpan
= aTableFrame
.GetEffectiveRowSpan(*kidFrame
);
825 SetContentBSize(cellMaxBSize
);
830 desiredSize
.ISize(wm
) = availCellISize
;
832 ReflowChildFlags flags
= ReflowChildFlags::Default
;
834 if (kidReflowInput
) {
835 // We reflowed. Apply relative positioning in the normal way.
836 flags
= ReflowChildFlags::ApplyRelativePositioning
;
837 } else if (kidFrame
->IsRelativelyOrStickyPositioned()) {
838 // We didn't reflow. Do the positioning part of what
839 // MovePositionBy does internally. (This codepath should really
840 // be merged into the else below if we can.)
841 nsMargin
* computedOffsetProp
=
842 kidFrame
->GetProperty(nsIFrame::ComputedOffsetProperty());
844 // On our fist reflow sticky children may not have the property yet (we
845 // need to reflow the children first to size the scroll frame).
846 LogicalMargin
computedOffsets(
847 wm
, computedOffsetProp
? *computedOffsetProp
: nsMargin());
848 ReflowInput::ApplyRelativePositioning(kidFrame
, wm
, computedOffsets
,
849 &kidPosition
, containerSize
);
852 // In vertical-rl mode, we are likely to have containerSize.width = 0
853 // because ComputedWidth() was NS_UNCONSTRAINEDSIZE.
854 // For cases where that's wrong, we will fix up the position later.
855 FinishReflowChild(kidFrame
, aPresContext
, desiredSize
,
856 kidReflowInput
.ptrOr(nullptr), wm
, kidPosition
,
857 containerSize
, flags
);
859 nsTableFrame
* tableFrame
= GetTableFrame();
860 if (tableFrame
->IsBorderCollapse()) {
861 nsTableFrame::InvalidateTableFrame(kidFrame
, kidRect
, kidInkOverflow
,
865 iCoord
+= desiredSize
.ISize(wm
);
867 if (iCoord
!= origKidNormalPosition
.I(wm
)) {
868 // Invalidate the old position
869 kidFrame
->InvalidateFrameSubtree();
870 // Move to the new position. As above, we need to account for relative
872 kidFrame
->MovePositionBy(
873 wm
, LogicalPoint(wm
, iCoord
- origKidNormalPosition
.I(wm
), 0));
874 nsTableFrame::RePositionViews(kidFrame
);
875 // invalidate the new position
876 kidFrame
->InvalidateFrameSubtree();
878 // we need to account for the cell's isize even if it isn't reflowed
879 iCoord
+= kidFrame
->ISize(wm
);
881 if (kidFrame
->GetNextInFlow()) {
883 aStatus
.SetIncomplete();
886 ConsiderChildOverflow(aDesiredSize
.mOverflowAreas
, kidFrame
);
887 iCoord
+= aTableFrame
.GetColSpacing(cellColIndex
);
890 // Just set our isize to what was available.
891 // The table will calculate the isize and not use our value.
892 aDesiredSize
.ISize(wm
) = aReflowInput
.AvailableISize();
894 if (aReflowInput
.mFlags
.mSpecialBSizeReflow
) {
895 aDesiredSize
.BSize(wm
) = BSize(wm
);
896 } else if (NS_UNCONSTRAINEDSIZE
== aReflowInput
.AvailableBSize()) {
897 aDesiredSize
.BSize(wm
) = CalcBSize(aReflowInput
);
898 if (GetPrevInFlow()) {
899 nscoord bsize
= CalcBSizeFromUnpaginatedBSize(*this, wm
);
900 aDesiredSize
.BSize(wm
) = std::max(aDesiredSize
.BSize(wm
), bsize
);
902 if (isPaginated
&& HasStyleBSize()) {
903 // set the unpaginated bsize so next in flows can try to honor it
904 SetUnpaginatedBSize(aDesiredSize
.BSize(wm
));
906 if (isPaginated
&& HasUnpaginatedBSize()) {
907 aDesiredSize
.BSize(wm
) =
908 std::max(aDesiredSize
.BSize(wm
), GetUnpaginatedBSize());
911 } else { // constrained bsize, paginated
912 // Compute the bsize we should have from style (subtracting the
913 // bsize from our prev-in-flows from the style bsize)
914 nscoord styleBSize
= CalcBSizeFromUnpaginatedBSize(*this, wm
);
915 if (styleBSize
> aReflowInput
.AvailableBSize()) {
916 styleBSize
= aReflowInput
.AvailableBSize();
917 aStatus
.SetIncomplete();
919 aDesiredSize
.BSize(wm
) = std::max(cellMaxBSize
, styleBSize
);
922 if (wm
.IsVerticalRL()) {
923 // Any children whose width was not the same as our final
924 // aDesiredSize.BSize will have been misplaced earlier at the
925 // FinishReflowChild stage. So fix them up now.
926 for (nsIFrame
* kidFrame
: mFrames
) {
927 if (kidFrame
->BSize(wm
) != aDesiredSize
.BSize(wm
)) {
928 kidFrame
->MovePositionBy(
930 LogicalPoint(wm
, 0, kidFrame
->BSize(wm
) - aDesiredSize
.BSize(wm
)));
931 nsTableFrame::RePositionViews(kidFrame
);
932 // Do we need to InvalidateFrameSubtree() here?
937 aDesiredSize
.UnionOverflowAreasWithDesiredBounds();
938 FinishAndStoreOverflow(&aDesiredSize
);
941 /** Layout the entire row.
942 * This method stacks cells in the inline dir according to HTML 4.0 rules.
944 void nsTableRowFrame::Reflow(nsPresContext
* aPresContext
,
945 ReflowOutput
& aDesiredSize
,
946 const ReflowInput
& aReflowInput
,
947 nsReflowStatus
& aStatus
) {
949 DO_GLOBAL_REFLOW_COUNT("nsTableRowFrame");
950 MOZ_ASSERT(aStatus
.IsEmpty(), "Caller should pass a fresh reflow status!");
952 WritingMode wm
= aReflowInput
.GetWritingMode();
954 nsTableFrame
* tableFrame
= GetTableFrame();
955 const nsStyleVisibility
* rowVis
= StyleVisibility();
956 bool collapseRow
= StyleVisibility::Collapse
== rowVis
->mVisible
;
958 tableFrame
->SetNeedToCollapse(true);
961 // see if a special bsize reflow needs to occur due to having a pct bsize
962 nsTableFrame::CheckRequestSpecialBSizeReflow(aReflowInput
);
964 // See if we have a cell with specified/pct bsize
965 InitHasCellWithStyleBSize(tableFrame
);
967 ReflowChildren(aPresContext
, aDesiredSize
, aReflowInput
, *tableFrame
,
970 if (aPresContext
->IsPaginated() && !aStatus
.IsFullyComplete() &&
971 ShouldAvoidBreakInside(aReflowInput
)) {
972 aStatus
.SetInlineLineBreakBeforeAndReset();
975 // Just set our isize to what was available.
976 // The table will calculate the isize and not use our value.
977 aDesiredSize
.ISize(wm
) = aReflowInput
.AvailableISize();
979 // If our parent is in initial reflow, it'll handle invalidating our
980 // entire overflow rect.
981 if (!GetParent()->HasAnyStateBits(NS_FRAME_FIRST_REFLOW
) &&
982 nsSize(aDesiredSize
.Width(), aDesiredSize
.Height()) != mRect
.Size()) {
986 // Any absolutely-positioned children will get reflowed in
987 // nsIFrame::FixupPositionedTableParts in another pass, so propagate our
988 // dirtiness to them before our parent clears our dirty bits.
989 PushDirtyBitToAbsoluteFrames();
992 nscoord
nsTableRowFrame::ReflowCellFrame(nsPresContext
* aPresContext
,
993 const ReflowInput
& aReflowInput
,
995 nsTableCellFrame
* aCellFrame
,
996 nscoord aAvailableBSize
,
997 nsReflowStatus
& aStatus
) {
998 MOZ_ASSERT(aPresContext
->IsPaginated(),
999 "ReflowCellFrame currently supports only paged media!");
1000 MOZ_ASSERT(aAvailableBSize
!= NS_UNCONSTRAINEDSIZE
,
1001 "Why split cell frame if available bsize is unconstrained?");
1002 WritingMode wm
= aReflowInput
.GetWritingMode();
1004 // Reflow the cell frame with the specified height. Use the existing width
1005 nsSize containerSize
= aCellFrame
->GetSize();
1006 LogicalRect cellRect
= aCellFrame
->GetLogicalRect(wm
, containerSize
);
1007 nsRect cellInkOverflow
= aCellFrame
->InkOverflowRect();
1009 LogicalSize cellSize
= cellRect
.Size(wm
);
1010 LogicalSize
availSize(wm
, cellRect
.ISize(wm
), aAvailableBSize
);
1011 bool borderCollapse
= GetTableFrame()->IsBorderCollapse();
1012 NS_ASSERTION(aCellFrame
->GetWritingMode() == wm
,
1013 "expected consistent writing-mode within table");
1014 TableCellReflowInput
cellReflowInput(aPresContext
, aReflowInput
, aCellFrame
,
1016 ReflowInput::InitFlag::CallerWillInit
);
1017 InitChildReflowInput(*aPresContext
, availSize
, borderCollapse
,
1019 cellReflowInput
.mFlags
.mIsTopOfPage
= aIsTopOfPage
;
1021 ReflowOutput
desiredSize(aReflowInput
);
1023 ReflowChild(aCellFrame
, aPresContext
, desiredSize
, cellReflowInput
, 0, 0,
1024 ReflowChildFlags::NoMoveFrame
, aStatus
);
1025 const bool isTruncated
=
1026 aAvailableBSize
< desiredSize
.BSize(wm
) &&
1027 !aIsTopOfPage
; // XXX Is !aIsTopOfPage check really necessary?
1028 const bool isCompleteAndNotTruncated
= aStatus
.IsComplete() && !isTruncated
;
1029 if (isCompleteAndNotTruncated
) {
1030 desiredSize
.BSize(wm
) = aAvailableBSize
;
1032 aCellFrame
->SetSize(
1033 wm
, LogicalSize(wm
, cellSize
.ISize(wm
), desiredSize
.BSize(wm
)));
1035 // Note: BlockDirAlignChild can affect the overflow rect.
1036 // XXX What happens if this cell has 'vertical-align: baseline' ?
1037 // XXX Why is it assumed that the cell's ascent hasn't changed ?
1038 if (isCompleteAndNotTruncated
) {
1039 aCellFrame
->BlockDirAlignChild(wm
, mMaxCellAscent
,
1040 ForceAlignTopForTableCell::Yes
);
1043 nsTableFrame::InvalidateTableFrame(
1044 aCellFrame
, cellRect
.GetPhysicalRect(wm
, containerSize
), cellInkOverflow
,
1045 aCellFrame
->HasAnyStateBits(NS_FRAME_FIRST_REFLOW
));
1047 aCellFrame
->DidReflow(aPresContext
, nullptr);
1049 return desiredSize
.BSize(wm
);
1052 nscoord
nsTableRowFrame::CollapseRowIfNecessary(nscoord aRowOffset
,
1054 bool aCollapseGroup
,
1055 bool& aDidCollapse
) {
1056 const nsStyleVisibility
* rowVis
= StyleVisibility();
1057 bool collapseRow
= StyleVisibility::Collapse
== rowVis
->mVisible
;
1058 nsTableFrame
* tableFrame
=
1059 static_cast<nsTableFrame
*>(GetTableFrame()->FirstInFlow());
1061 tableFrame
->SetNeedToCollapse(true);
1064 if (aRowOffset
!= 0) {
1065 // We're moving, so invalidate our old position
1066 InvalidateFrameSubtree();
1069 WritingMode wm
= GetWritingMode();
1071 nsSize parentSize
= GetParent()->GetSize();
1072 LogicalRect rowRect
= GetLogicalRect(wm
, parentSize
);
1073 nsRect oldRect
= mRect
;
1074 nsRect oldInkOverflow
= InkOverflowRect();
1076 rowRect
.BStart(wm
) -= aRowOffset
;
1077 rowRect
.ISize(wm
) = aISize
;
1078 OverflowAreas overflow
;
1080 nsSize containerSize
= mRect
.Size();
1082 if (aCollapseGroup
|| collapseRow
) {
1083 aDidCollapse
= true;
1084 shift
= rowRect
.BSize(wm
);
1085 nsTableCellFrame
* cellFrame
= GetFirstCell();
1087 uint32_t rowIndex
= cellFrame
->RowIndex();
1088 shift
+= tableFrame
->GetRowSpacing(rowIndex
);
1090 LogicalRect cRect
= cellFrame
->GetLogicalRect(wm
, containerSize
);
1091 // If aRowOffset != 0, there's no point in invalidating the cells, since
1092 // we've already invalidated our overflow area. Note that we _do_ still
1093 // need to invalidate if our row is not moving, because the cell might
1094 // span out of this row, so invalidating our row rect won't do enough.
1095 if (aRowOffset
== 0) {
1098 cRect
.BSize(wm
) = 0;
1099 cellFrame
->SetRect(wm
, cRect
, containerSize
);
1100 cellFrame
= cellFrame
->GetNextCell();
1103 shift
+= tableFrame
->GetRowSpacing(GetRowIndex());
1105 rowRect
.BSize(wm
) = 0;
1106 } else { // row is not collapsed
1107 // remember the col index of the previous cell to handle rowspans into this
1109 int32_t prevColIndex
= -1;
1110 nscoord iPos
= 0; // running total of children inline-axis offset
1111 nsTableFrame
* fifTable
=
1112 static_cast<nsTableFrame
*>(tableFrame
->FirstInFlow());
1114 for (nsTableCellFrame
* cellFrame
= GetFirstCell(); cellFrame
;
1115 cellFrame
= cellFrame
->GetNextCell()) {
1116 uint32_t cellColIndex
= cellFrame
->ColIndex();
1117 int32_t cellColSpan
= tableFrame
->GetEffectiveColSpan(*cellFrame
);
1119 // If the adjacent cell is in a prior row (because of a rowspan) add in
1121 // NOTE: prevColIndex can be -1 here.
1122 if (prevColIndex
!= (static_cast<int32_t>(cellColIndex
) - 1)) {
1123 iPos
+= GetSpaceBetween(prevColIndex
, cellColIndex
, cellColSpan
,
1126 LogicalRect
cRect(wm
, iPos
, 0, 0, rowRect
.BSize(wm
));
1128 // remember the last (iend-wards-most) column this cell spans into
1129 prevColIndex
= cellColIndex
+ cellColSpan
- 1;
1130 int32_t actualColSpan
= cellColSpan
;
1131 bool isVisible
= false;
1132 for (int32_t colIdx
= cellColIndex
; actualColSpan
> 0;
1133 colIdx
++, actualColSpan
--) {
1134 nsTableColFrame
* colFrame
= tableFrame
->GetColFrame(colIdx
);
1135 const nsStyleVisibility
* colVis
= colFrame
->StyleVisibility();
1136 bool collapseCol
= StyleVisibility::Collapse
== colVis
->mVisible
;
1137 nsIFrame
* cgFrame
= colFrame
->GetParent();
1138 const nsStyleVisibility
* groupVis
= cgFrame
->StyleVisibility();
1139 bool collapseGroup
= StyleVisibility::Collapse
== groupVis
->mVisible
;
1140 bool isCollapsed
= collapseCol
|| collapseGroup
;
1142 cRect
.ISize(wm
) += fifTable
->GetColumnISizeFromFirstInFlow(colIdx
);
1144 if ((actualColSpan
> 1)) {
1145 nsTableColFrame
* nextColFrame
= tableFrame
->GetColFrame(colIdx
+ 1);
1146 const nsStyleVisibility
* nextColVis
=
1147 nextColFrame
->StyleVisibility();
1148 if (StyleVisibility::Collapse
!= nextColVis
->mVisible
&&
1149 tableFrame
->ColumnHasCellSpacingBefore(colIdx
+ 1)) {
1150 cRect
.ISize(wm
) += tableFrame
->GetColSpacing(cellColIndex
);
1155 iPos
+= cRect
.ISize(wm
);
1157 iPos
+= tableFrame
->GetColSpacing(cellColIndex
);
1159 int32_t actualRowSpan
= tableFrame
->GetEffectiveRowSpan(*cellFrame
);
1160 nsTableRowFrame
* rowFrame
= GetNextRow();
1161 for (actualRowSpan
--; actualRowSpan
> 0 && rowFrame
; actualRowSpan
--) {
1162 const nsStyleVisibility
* nextRowVis
= rowFrame
->StyleVisibility();
1163 bool collapseNextRow
=
1164 StyleVisibility::Collapse
== nextRowVis
->mVisible
;
1165 if (!collapseNextRow
) {
1166 LogicalRect nextRect
= rowFrame
->GetLogicalRect(wm
, containerSize
);
1167 cRect
.BSize(wm
) += nextRect
.BSize(wm
) +
1168 tableFrame
->GetRowSpacing(rowFrame
->GetRowIndex());
1170 rowFrame
= rowFrame
->GetNextRow();
1173 nsRect oldCellRect
= cellFrame
->GetRect();
1174 LogicalPoint oldCellNormalPos
=
1175 cellFrame
->GetLogicalNormalPosition(wm
, containerSize
);
1177 nsRect oldCellInkOverflow
= cellFrame
->InkOverflowRect();
1179 if (aRowOffset
== 0 && cRect
.Origin(wm
) != oldCellNormalPos
) {
1180 // We're moving the cell. Invalidate the old overflow area
1181 cellFrame
->InvalidateFrameSubtree();
1184 cellFrame
->MovePositionBy(wm
, cRect
.Origin(wm
) - oldCellNormalPos
);
1185 cellFrame
->SetSize(wm
, cRect
.Size(wm
));
1187 // XXXbz This looks completely bogus in the cases when we didn't
1188 // collapse the cell!
1189 LogicalRect
cellBounds(wm
, 0, 0, cRect
.ISize(wm
), cRect
.BSize(wm
));
1190 nsRect cellPhysicalBounds
= cellBounds
.GetPhysicalRect(wm
, containerSize
);
1191 OverflowAreas
cellOverflow(cellPhysicalBounds
, cellPhysicalBounds
);
1192 cellFrame
->FinishAndStoreOverflow(cellOverflow
,
1193 cRect
.Size(wm
).GetPhysicalSize(wm
));
1194 nsTableFrame::RePositionViews(cellFrame
);
1195 ConsiderChildOverflow(overflow
, cellFrame
);
1197 if (aRowOffset
== 0) {
1198 nsTableFrame::InvalidateTableFrame(cellFrame
, oldCellRect
,
1199 oldCellInkOverflow
, false);
1204 SetRect(wm
, rowRect
, containerSize
);
1205 overflow
.UnionAllWith(nsRect(0, 0, rowRect
.Width(wm
), rowRect
.Height(wm
)));
1206 FinishAndStoreOverflow(overflow
, rowRect
.Size(wm
).GetPhysicalSize(wm
));
1208 nsTableFrame::RePositionViews(this);
1209 nsTableFrame::InvalidateTableFrame(this, oldRect
, oldInkOverflow
, false);
1214 * The following method is called by the row group frame's SplitRowGroup()
1215 * when it creates a continuing cell frame and wants to insert it into the
1218 void nsTableRowFrame::InsertCellFrame(nsTableCellFrame
* aFrame
,
1219 int32_t aColIndex
) {
1220 // Find the cell frame where col index < aColIndex
1221 nsTableCellFrame
* priorCell
= nullptr;
1223 for (nsTableCellFrame
* cellFrame
= GetFirstCell(); cellFrame
;
1224 cellFrame
= cellFrame
->GetNextCell()) {
1225 uint32_t colIndex
= cellFrame
->ColIndex();
1226 // Can aColIndex be -1 here? Let's assume it can for now.
1227 if (static_cast<int32_t>(colIndex
) < aColIndex
) {
1228 priorCell
= cellFrame
;
1233 mFrames
.InsertFrame(this, priorCell
, aFrame
);
1236 nsTableRowFrame
* nsTableRowFrame::GetPrevRow() const {
1237 nsIFrame
* prevSibling
= GetPrevSibling();
1239 !prevSibling
|| static_cast<nsTableRowFrame
*>(do_QueryFrame(prevSibling
)),
1240 "How do we have a non-row sibling?");
1241 return static_cast<nsTableRowFrame
*>(prevSibling
);
1244 nsTableRowFrame
* nsTableRowFrame::GetNextRow() const {
1245 nsIFrame
* nextSibling
= GetNextSibling();
1247 !nextSibling
|| static_cast<nsTableRowFrame
*>(do_QueryFrame(nextSibling
)),
1248 "How do we have a non-row sibling?");
1249 return static_cast<nsTableRowFrame
*>(nextSibling
);
1252 // This property is only set on the first-in-flow of nsTableRowFrame.
1253 NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(TableRowUnpaginatedBSizeProperty
, nscoord
)
1255 void nsTableRowFrame::SetUnpaginatedBSize(nscoord aValue
) {
1256 MOZ_ASSERT(!GetPrevInFlow(),
1257 "TableRowUnpaginatedBSizeProperty should only be set on the "
1259 AddStateBits(NS_TABLE_ROW_HAS_UNPAGINATED_BSIZE
);
1260 SetProperty(TableRowUnpaginatedBSizeProperty(), aValue
);
1263 nscoord
nsTableRowFrame::GetUnpaginatedBSize() const {
1264 return GetProperty(TableRowUnpaginatedBSizeProperty());
1267 #ifdef ACCESSIBILITY
1268 a11y::AccType
nsTableRowFrame::AccessibleType() {
1269 return a11y::eHTMLTableRowType
;
1273 * Sets the NS_ROW_HAS_CELL_WITH_STYLE_BSIZE bit to indicate whether
1274 * this row has any cells that have non-auto-bsize. (Row-spanning
1275 * cells are ignored.)
1277 void nsTableRowFrame::InitHasCellWithStyleBSize(nsTableFrame
* aTableFrame
) {
1278 WritingMode wm
= GetWritingMode();
1280 for (nsTableCellFrame
* cellFrame
= GetFirstCell(); cellFrame
;
1281 cellFrame
= cellFrame
->GetNextCell()) {
1282 // Ignore row-spanning cells
1283 const auto& cellBSize
= cellFrame
->StylePosition()->BSize(wm
);
1284 if (aTableFrame
->GetEffectiveRowSpan(*cellFrame
) == 1 &&
1285 !cellBSize
.IsAuto() &&
1286 /* calc() with both percentages and lengths treated like 'auto' */
1287 (cellBSize
.ConvertsToLength() || cellBSize
.ConvertsToPercentage())) {
1288 AddStateBits(NS_ROW_HAS_CELL_WITH_STYLE_BSIZE
);
1292 RemoveStateBits(NS_ROW_HAS_CELL_WITH_STYLE_BSIZE
);
1295 void nsTableRowFrame::InvalidateFrame(uint32_t aDisplayItemKey
,
1296 bool aRebuildDisplayItems
) {
1297 nsIFrame::InvalidateFrame(aDisplayItemKey
, aRebuildDisplayItems
);
1298 if (GetTableFrame()->IsBorderCollapse()) {
1299 const bool rebuild
= StaticPrefs::layout_display_list_retain_sc();
1300 GetParent()->InvalidateFrameWithRect(InkOverflowRect() + GetPosition(),
1301 aDisplayItemKey
, rebuild
);
1305 void nsTableRowFrame::InvalidateFrameWithRect(const nsRect
& aRect
,
1306 uint32_t aDisplayItemKey
,
1307 bool aRebuildDisplayItems
) {
1308 nsIFrame::InvalidateFrameWithRect(aRect
, aDisplayItemKey
,
1309 aRebuildDisplayItems
);
1310 // If we have filters applied that would affects our bounds, then
1311 // we get an inactive layer created and this is computed
1312 // within FrameLayerBuilder
1313 GetParent()->InvalidateFrameWithRect(aRect
+ GetPosition(), aDisplayItemKey
,
1314 aRebuildDisplayItems
);
1317 /* ----- global methods ----- */
1319 nsTableRowFrame
* NS_NewTableRowFrame(PresShell
* aPresShell
,
1320 ComputedStyle
* aStyle
) {
1321 return new (aPresShell
) nsTableRowFrame(aStyle
, aPresShell
->GetPresContext());
1324 NS_IMPL_FRAMEARENA_HELPERS(nsTableRowFrame
)
1326 #ifdef DEBUG_FRAME_DUMP
1327 nsresult
nsTableRowFrame::GetFrameName(nsAString
& aResult
) const {
1328 return MakeFrameName(u
"TableRow"_ns
, aResult
);