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() {
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
);
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
) {
446 if (!aCellFrame
->HasVerticalAlignBaseline()) {
447 // only the cell's height matters
448 if (GetInitialBSize() < aBSize
) {
449 int32_t rowSpan
= aTableFrame
->GetEffectiveRowSpan(*aCellFrame
);
451 SetContentBSize(aBSize
);
454 } else { // the alignment on the baseline can change the bsize
456 aAscent
!= NS_UNCONSTRAINEDSIZE
&& aDescent
!= NS_UNCONSTRAINEDSIZE
,
458 // see if this is a long ascender
459 if (mMaxCellAscent
< aAscent
) {
460 mMaxCellAscent
= aAscent
;
462 // see if this is a long descender and without rowspan
463 if (mMaxCellDescent
< aDescent
) {
464 int32_t rowSpan
= aTableFrame
->GetEffectiveRowSpan(*aCellFrame
);
466 mMaxCellDescent
= aDescent
;
469 // keep the tallest bsize in sync
470 if (GetInitialBSize() < mMaxCellAscent
+ mMaxCellDescent
) {
471 SetContentBSize(mMaxCellAscent
+ mMaxCellDescent
);
476 nscoord
nsTableRowFrame::CalcBSize(const ReflowInput
& aReflowInput
) {
477 nsTableFrame
* tableFrame
= GetTableFrame();
480 const nscoord computedBSize
= aReflowInput
.ComputedBSize();
481 if (computedBSize
!= NS_UNCONSTRAINEDSIZE
&& computedBSize
> 0) {
482 SetFixedBSize(computedBSize
);
485 WritingMode wm
= aReflowInput
.GetWritingMode();
486 const nsStylePosition
* position
= StylePosition();
487 const auto& bsizeStyleCoord
= position
->BSize(wm
);
488 if (bsizeStyleCoord
.ConvertsToLength()) {
489 SetFixedBSize(bsizeStyleCoord
.ToLength());
490 } else if (bsizeStyleCoord
.ConvertsToPercentage()) {
491 SetPctBSize(bsizeStyleCoord
.ToPercentage());
494 for (nsTableCellFrame
* kidFrame
= GetFirstCell(); kidFrame
;
495 kidFrame
= kidFrame
->GetNextCell()) {
496 MOZ_ASSERT(kidFrame
->GetWritingMode() == wm
);
497 LogicalSize desSize
= kidFrame
->GetDesiredSize();
498 if (NS_UNCONSTRAINEDSIZE
== aReflowInput
.AvailableBSize() &&
500 desSize
.BSize(wm
) = CalcCellActualBSize(kidFrame
, desSize
.BSize(wm
), wm
);
502 // bsize may have changed, adjust descent to absorb any excess difference
504 if (!kidFrame
->PrincipalChildList()
506 ->PrincipalChildList()
508 ascent
= desSize
.BSize(wm
);
510 ascent
= kidFrame
->GetCellBaseline();
512 nscoord descent
= desSize
.BSize(wm
) - ascent
;
513 UpdateBSize(desSize
.BSize(wm
), ascent
, descent
, tableFrame
, kidFrame
);
515 return GetInitialBSize();
518 void nsTableRowFrame::PaintCellBackgroundsForFrame(
519 nsIFrame
* aFrame
, nsDisplayListBuilder
* aBuilder
,
520 const nsDisplayListSet
& aLists
, const nsPoint
& aOffset
) {
521 // Compute background rect by iterating all cell frame.
522 const nsPoint toReferenceFrame
= aBuilder
->ToReferenceFrame(aFrame
);
523 for (nsTableCellFrame
* cell
= GetFirstCell(); cell
;
524 cell
= cell
->GetNextCell()) {
525 if (!cell
->ShouldPaintBackground(aBuilder
)) {
530 cell
->GetRectRelativeToSelf() + cell
->GetNormalPosition() + aOffset
;
531 if (!aBuilder
->GetDirtyRect().Intersects(cellRect
)) {
534 cellRect
+= toReferenceFrame
;
535 nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
536 aBuilder
, aFrame
, cellRect
, aLists
.BorderBackground(), true,
537 aFrame
->GetRectRelativeToSelf() + toReferenceFrame
, cell
);
541 void nsTableRowFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
542 const nsDisplayListSet
& aLists
) {
543 DisplayOutsetBoxShadow(aBuilder
, aLists
.BorderBackground());
545 PaintCellBackgroundsForFrame(this, aBuilder
, aLists
);
547 DisplayInsetBoxShadow(aBuilder
, aLists
.BorderBackground());
549 DisplayOutline(aBuilder
, aLists
);
551 for (nsIFrame
* kid
: PrincipalChildList()) {
552 BuildDisplayListForChild(aBuilder
, kid
, aLists
);
556 LogicalSides
nsTableRowFrame::GetLogicalSkipSides() const {
557 LogicalSides
skip(mWritingMode
);
558 if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak
==
559 StyleBoxDecorationBreak::Clone
)) {
563 if (GetPrevInFlow()) {
564 skip
|= eLogicalSideBitsBStart
;
566 if (GetNextInFlow()) {
567 skip
|= eLogicalSideBitsBEnd
;
572 nscoord
nsTableRowFrame::CalcCellActualBSize(nsTableCellFrame
* aCellFrame
,
573 const nscoord
& aDesiredBSize
,
575 nscoord specifiedBSize
= 0;
577 // Get the bsize specified in the style information
578 const nsStylePosition
* position
= aCellFrame
->StylePosition();
580 int32_t rowSpan
= GetTableFrame()->GetEffectiveRowSpan(*aCellFrame
);
582 const auto& bsizeStyleCoord
= position
->BSize(aWM
);
583 if (bsizeStyleCoord
.ConvertsToLength()) {
584 // In quirks mode, table cell isize should be content-box, but bsize
585 // should be border-box.
586 // Because of this historic anomaly, we do not use quirk.css
587 // (since we can't specify one value of box-sizing for isize and another
589 specifiedBSize
= bsizeStyleCoord
.ToLength();
590 if (PresContext()->CompatibilityMode() != eCompatibility_NavQuirks
&&
591 position
->mBoxSizing
== StyleBoxSizing::Content
) {
593 aCellFrame
->GetLogicalUsedBorderAndPadding(aWM
).BStartEnd(aWM
);
597 SetFixedBSize(specifiedBSize
);
599 } else if (bsizeStyleCoord
.ConvertsToPercentage()) {
601 SetPctBSize(bsizeStyleCoord
.ToPercentage());
605 // If the specified bsize is greater than the desired bsize,
606 // then use the specified bsize
607 return std::max(specifiedBSize
, aDesiredBSize
);
610 // Calculates the available isize for the table cell based on the known
611 // column isizes taking into account column spans and column spacing
612 static nscoord
CalcAvailISize(nsTableFrame
& aTableFrame
,
613 nsTableCellFrame
& aCellFrame
) {
614 nscoord cellAvailISize
= 0;
615 uint32_t colIndex
= aCellFrame
.ColIndex();
616 int32_t colspan
= aTableFrame
.GetEffectiveColSpan(aCellFrame
);
617 NS_ASSERTION(colspan
> 0, "effective colspan should be positive");
618 nsTableFrame
* fifTable
=
619 static_cast<nsTableFrame
*>(aTableFrame
.FirstInFlow());
621 for (int32_t spanX
= 0; spanX
< colspan
; spanX
++) {
622 cellAvailISize
+= fifTable
->GetColumnISizeFromFirstInFlow(colIndex
+ spanX
);
623 if (spanX
> 0 && aTableFrame
.ColumnHasCellSpacingBefore(colIndex
+ spanX
)) {
624 cellAvailISize
+= aTableFrame
.GetColSpacing(colIndex
+ spanX
- 1);
627 return cellAvailISize
;
630 static nscoord
GetSpaceBetween(int32_t aPrevColIndex
, int32_t aColIndex
,
631 int32_t aColSpan
, nsTableFrame
& aTableFrame
,
632 bool aCheckVisibility
) {
635 nsTableFrame
* fifTable
=
636 static_cast<nsTableFrame
*>(aTableFrame
.FirstInFlow());
637 for (colIdx
= aPrevColIndex
+ 1; aColIndex
> colIdx
; colIdx
++) {
638 bool isCollapsed
= false;
639 if (!aCheckVisibility
) {
640 space
+= fifTable
->GetColumnISizeFromFirstInFlow(colIdx
);
642 nsTableColFrame
* colFrame
= aTableFrame
.GetColFrame(colIdx
);
643 const nsStyleVisibility
* colVis
= colFrame
->StyleVisibility();
644 bool collapseCol
= StyleVisibility::Collapse
== colVis
->mVisible
;
645 nsIFrame
* cgFrame
= colFrame
->GetParent();
646 const nsStyleVisibility
* groupVis
= cgFrame
->StyleVisibility();
647 bool collapseGroup
= StyleVisibility::Collapse
== groupVis
->mVisible
;
648 isCollapsed
= collapseCol
|| collapseGroup
;
650 space
+= fifTable
->GetColumnISizeFromFirstInFlow(colIdx
);
652 if (!isCollapsed
&& aTableFrame
.ColumnHasCellSpacingBefore(colIdx
)) {
653 space
+= aTableFrame
.GetColSpacing(colIdx
- 1);
659 // subtract the bsizes of aRow's prev in flows from the unpaginated bsize
660 static nscoord
CalcBSizeFromUnpaginatedBSize(nsTableRowFrame
& aRow
,
663 nsTableRowFrame
* firstInFlow
=
664 static_cast<nsTableRowFrame
*>(aRow
.FirstInFlow());
665 if (firstInFlow
->HasUnpaginatedBSize()) {
666 bsize
= firstInFlow
->GetUnpaginatedBSize();
667 for (nsIFrame
* prevInFlow
= aRow
.GetPrevInFlow(); prevInFlow
;
668 prevInFlow
= prevInFlow
->GetPrevInFlow()) {
669 bsize
-= prevInFlow
->BSize(aWM
);
672 return std::max(bsize
, 0);
675 void nsTableRowFrame::ReflowChildren(nsPresContext
* aPresContext
,
676 ReflowOutput
& aDesiredSize
,
677 const ReflowInput
& aReflowInput
,
678 nsTableFrame
& aTableFrame
,
679 nsReflowStatus
& aStatus
) {
682 // XXXldb Should we be checking constrained bsize instead?
683 const bool isPaginated
= aPresContext
->IsPaginated();
684 const bool borderCollapse
= aTableFrame
.IsBorderCollapse();
686 int32_t cellColSpan
=
687 1; // must be defined here so it's set properly for non-cell kids
689 // remember the col index of the previous cell to handle rowspans into this
691 int32_t prevColIndex
= -1;
692 nscoord iCoord
= 0; // running total of children inline-coord offset
694 // This computes the max of all cell bsizes
695 nscoord cellMaxBSize
= 0;
697 // Reflow each of our existing cell frames
698 WritingMode wm
= aReflowInput
.GetWritingMode();
699 nsSize containerSize
= aReflowInput
.ComputedSizeAsContainerIfConstrained();
701 for (nsTableCellFrame
* kidFrame
= GetFirstCell(); kidFrame
;
702 kidFrame
= kidFrame
->GetNextCell()) {
703 // See if we should only reflow the dirty child frames
704 bool doReflowChild
= true;
705 if (!aReflowInput
.ShouldReflowAllKids() && !aTableFrame
.IsGeometryDirty() &&
706 !kidFrame
->IsSubtreeDirty()) {
707 if (!aReflowInput
.mFlags
.mSpecialBSizeReflow
) {
708 doReflowChild
= false;
710 } else if (NS_UNCONSTRAINEDSIZE
!= aReflowInput
.AvailableBSize()) {
711 // We don't reflow a rowspan >1 cell here with a constrained bsize.
712 // That happens in nsTableRowGroupFrame::SplitSpanningCells.
713 if (aTableFrame
.GetEffectiveRowSpan(*kidFrame
) > 1) {
714 doReflowChild
= false;
717 if (aReflowInput
.mFlags
.mSpecialBSizeReflow
&& !isPaginated
&&
718 !kidFrame
->HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE
)) {
722 uint32_t cellColIndex
= kidFrame
->ColIndex();
723 cellColSpan
= aTableFrame
.GetEffectiveColSpan(*kidFrame
);
725 // If the adjacent cell is in a prior row (because of a rowspan) add in the
726 // space NOTE: prevColIndex can be -1 here.
727 if (prevColIndex
!= (static_cast<int32_t>(cellColIndex
) - 1)) {
728 iCoord
+= GetSpaceBetween(prevColIndex
, cellColIndex
, cellColSpan
,
732 // remember the rightmost (ltr) or leftmost (rtl) column this cell spans
734 prevColIndex
= cellColIndex
+ (cellColSpan
- 1);
736 // Reflow the child frame
737 nsRect kidRect
= kidFrame
->GetRect();
738 LogicalPoint origKidNormalPosition
=
739 kidFrame
->GetLogicalNormalPosition(wm
, containerSize
);
741 nsRect kidInkOverflow
= kidFrame
->InkOverflowRect();
742 LogicalPoint
kidPosition(wm
, iCoord
, 0);
743 bool firstReflow
= kidFrame
->HasAnyStateBits(NS_FRAME_FIRST_REFLOW
);
746 // Calculate the available isize for the table cell using the known
748 nscoord availCellISize
= CalcAvailISize(aTableFrame
, *kidFrame
);
750 Maybe
<TableCellReflowInput
> kidReflowInput
;
751 ReflowOutput
desiredSize(aReflowInput
);
753 // If the avail isize is not the same as last time we reflowed the cell or
754 // the cell wants to be bigger than what was available last time or
755 // it is a style change reflow or we are printing, then we must reflow the
756 // cell. Otherwise we can skip the reflow.
757 // XXXldb Why is this condition distinct from doReflowChild above?
758 NS_ASSERTION(kidFrame
->GetWritingMode() == wm
,
759 "expected consistent writing-mode within table");
760 LogicalSize cellDesiredSize
= kidFrame
->GetDesiredSize();
761 if ((availCellISize
!= kidFrame
->GetPriorAvailISize()) ||
762 (cellDesiredSize
.ISize(wm
) > kidFrame
->GetPriorAvailISize()) ||
763 HasAnyStateBits(NS_FRAME_IS_DIRTY
) || isPaginated
||
764 kidFrame
->IsSubtreeDirty() ||
765 // See if it needs a special reflow, or if it had one that we need to
767 kidFrame
->HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE
) ||
769 // Reflow the cell to fit the available isize, bsize
770 // XXX The old IR_ChildIsDirty code used availCellISize here.
771 LogicalSize
kidAvailSize(wm
, availCellISize
,
772 aReflowInput
.AvailableBSize());
775 kidReflowInput
.emplace(aPresContext
, aReflowInput
, kidFrame
,
777 ReflowInput::InitFlag::CallerWillInit
);
778 InitChildReflowInput(*aPresContext
, kidAvailSize
, borderCollapse
,
781 nsReflowStatus status
;
782 ReflowChild(kidFrame
, aPresContext
, desiredSize
, *kidReflowInput
, wm
,
783 kidPosition
, containerSize
, ReflowChildFlags::Default
,
786 // allow the table to determine if/how the table needs to be rebalanced
787 // If any of the cells are not complete, then we're not complete
788 if (status
.IsIncomplete()) {
790 aStatus
.SetIncomplete();
793 if (iCoord
!= origKidNormalPosition
.I(wm
)) {
794 kidFrame
->InvalidateFrameSubtree();
797 desiredSize
.SetSize(wm
, cellDesiredSize
);
798 desiredSize
.mOverflowAreas
= kidFrame
->GetOverflowAreas();
800 // if we are in a floated table, our position is not yet established, so
801 // we cannot reposition our views the containing block will do this for
802 // us after positioning the table
803 if (!aTableFrame
.IsFloating()) {
804 // Because we may have moved the frame we need to make sure any views
805 // are positioned properly. We have to do this, because any one of our
806 // parent frames could have moved and we have no way of knowing...
807 nsTableFrame::RePositionViews(kidFrame
);
811 if (NS_UNCONSTRAINEDSIZE
== aReflowInput
.AvailableBSize()) {
812 if (!GetPrevInFlow()) {
813 desiredSize
.BSize(wm
) =
814 CalcCellActualBSize(kidFrame
, desiredSize
.BSize(wm
), wm
);
816 // bsize may have changed, adjust descent to absorb any excess
819 if (!kidFrame
->PrincipalChildList()
821 ->PrincipalChildList()
823 ascent
= desiredSize
.BSize(wm
);
825 ascent
= kidFrame
->GetCellBaseline();
827 nscoord descent
= desiredSize
.BSize(wm
) - ascent
;
828 UpdateBSize(desiredSize
.BSize(wm
), ascent
, descent
, &aTableFrame
,
831 cellMaxBSize
= std::max(cellMaxBSize
, desiredSize
.BSize(wm
));
832 int32_t rowSpan
= aTableFrame
.GetEffectiveRowSpan(*kidFrame
);
834 SetContentBSize(cellMaxBSize
);
839 desiredSize
.ISize(wm
) = availCellISize
;
841 ReflowChildFlags flags
= ReflowChildFlags::Default
;
843 if (kidReflowInput
) {
844 // We reflowed. Apply relative positioning in the normal way.
845 flags
= ReflowChildFlags::ApplyRelativePositioning
;
846 } else if (kidFrame
->IsRelativelyOrStickyPositioned()) {
847 // We didn't reflow. Do the positioning part of what
848 // MovePositionBy does internally. (This codepath should really
849 // be merged into the else below if we can.)
850 nsMargin
* computedOffsetProp
=
851 kidFrame
->GetProperty(nsIFrame::ComputedOffsetProperty());
853 // On our fist reflow sticky children may not have the property yet (we
854 // need to reflow the children first to size the scroll frame).
855 LogicalMargin
computedOffsets(
856 wm
, computedOffsetProp
? *computedOffsetProp
: nsMargin());
857 ReflowInput::ApplyRelativePositioning(kidFrame
, wm
, computedOffsets
,
858 &kidPosition
, containerSize
);
861 // In vertical-rl mode, we are likely to have containerSize.width = 0
862 // because ComputedWidth() was NS_UNCONSTRAINEDSIZE.
863 // For cases where that's wrong, we will fix up the position later.
864 FinishReflowChild(kidFrame
, aPresContext
, desiredSize
,
865 kidReflowInput
.ptrOr(nullptr), wm
, kidPosition
,
866 containerSize
, flags
);
868 nsTableFrame
* tableFrame
= GetTableFrame();
869 if (tableFrame
->IsBorderCollapse()) {
870 nsTableFrame::InvalidateTableFrame(kidFrame
, kidRect
, kidInkOverflow
,
874 iCoord
+= desiredSize
.ISize(wm
);
876 if (iCoord
!= origKidNormalPosition
.I(wm
)) {
877 // Invalidate the old position
878 kidFrame
->InvalidateFrameSubtree();
879 // Move to the new position. As above, we need to account for relative
881 kidFrame
->MovePositionBy(
882 wm
, LogicalPoint(wm
, iCoord
- origKidNormalPosition
.I(wm
), 0));
883 nsTableFrame::RePositionViews(kidFrame
);
884 // invalidate the new position
885 kidFrame
->InvalidateFrameSubtree();
887 // we need to account for the cell's isize even if it isn't reflowed
888 iCoord
+= kidFrame
->ISize(wm
);
890 if (kidFrame
->GetNextInFlow()) {
892 aStatus
.SetIncomplete();
895 ConsiderChildOverflow(aDesiredSize
.mOverflowAreas
, kidFrame
);
896 iCoord
+= aTableFrame
.GetColSpacing(cellColIndex
);
899 // Just set our isize to what was available.
900 // The table will calculate the isize and not use our value.
901 aDesiredSize
.ISize(wm
) = aReflowInput
.AvailableISize();
903 if (aReflowInput
.mFlags
.mSpecialBSizeReflow
) {
904 aDesiredSize
.BSize(wm
) = BSize(wm
);
905 } else if (NS_UNCONSTRAINEDSIZE
== aReflowInput
.AvailableBSize()) {
906 aDesiredSize
.BSize(wm
) = CalcBSize(aReflowInput
);
907 if (GetPrevInFlow()) {
908 nscoord bsize
= CalcBSizeFromUnpaginatedBSize(*this, wm
);
909 aDesiredSize
.BSize(wm
) = std::max(aDesiredSize
.BSize(wm
), bsize
);
911 if (isPaginated
&& HasStyleBSize()) {
912 // set the unpaginated bsize so next in flows can try to honor it
913 SetUnpaginatedBSize(aDesiredSize
.BSize(wm
));
915 if (isPaginated
&& HasUnpaginatedBSize()) {
916 aDesiredSize
.BSize(wm
) =
917 std::max(aDesiredSize
.BSize(wm
), GetUnpaginatedBSize());
920 } else { // constrained bsize, paginated
921 // Compute the bsize we should have from style (subtracting the
922 // bsize from our prev-in-flows from the style bsize)
923 nscoord styleBSize
= CalcBSizeFromUnpaginatedBSize(*this, wm
);
924 if (styleBSize
> aReflowInput
.AvailableBSize()) {
925 styleBSize
= aReflowInput
.AvailableBSize();
926 aStatus
.SetIncomplete();
928 aDesiredSize
.BSize(wm
) = std::max(cellMaxBSize
, styleBSize
);
931 if (wm
.IsVerticalRL()) {
932 // Any children whose width was not the same as our final
933 // aDesiredSize.BSize will have been misplaced earlier at the
934 // FinishReflowChild stage. So fix them up now.
935 for (nsIFrame
* kidFrame
: mFrames
) {
936 if (kidFrame
->BSize(wm
) != aDesiredSize
.BSize(wm
)) {
937 kidFrame
->MovePositionBy(
939 LogicalPoint(wm
, 0, kidFrame
->BSize(wm
) - aDesiredSize
.BSize(wm
)));
940 nsTableFrame::RePositionViews(kidFrame
);
941 // Do we need to InvalidateFrameSubtree() here?
946 aDesiredSize
.UnionOverflowAreasWithDesiredBounds();
947 FinishAndStoreOverflow(&aDesiredSize
);
950 /** Layout the entire row.
951 * This method stacks cells in the inline dir according to HTML 4.0 rules.
953 void nsTableRowFrame::Reflow(nsPresContext
* aPresContext
,
954 ReflowOutput
& aDesiredSize
,
955 const ReflowInput
& aReflowInput
,
956 nsReflowStatus
& aStatus
) {
958 DO_GLOBAL_REFLOW_COUNT("nsTableRowFrame");
959 DISPLAY_REFLOW(aPresContext
, this, aReflowInput
, aDesiredSize
, aStatus
);
960 MOZ_ASSERT(aStatus
.IsEmpty(), "Caller should pass a fresh reflow status!");
962 WritingMode wm
= aReflowInput
.GetWritingMode();
964 nsTableFrame
* tableFrame
= GetTableFrame();
965 const nsStyleVisibility
* rowVis
= StyleVisibility();
966 bool collapseRow
= StyleVisibility::Collapse
== rowVis
->mVisible
;
968 tableFrame
->SetNeedToCollapse(true);
971 // see if a special bsize reflow needs to occur due to having a pct bsize
972 nsTableFrame::CheckRequestSpecialBSizeReflow(aReflowInput
);
974 // See if we have a cell with specified/pct bsize
975 InitHasCellWithStyleBSize(tableFrame
);
977 ReflowChildren(aPresContext
, aDesiredSize
, aReflowInput
, *tableFrame
,
980 if (aPresContext
->IsPaginated() && !aStatus
.IsFullyComplete() &&
981 ShouldAvoidBreakInside(aReflowInput
)) {
982 aStatus
.SetInlineLineBreakBeforeAndReset();
985 // Just set our isize to what was available.
986 // The table will calculate the isize and not use our value.
987 aDesiredSize
.ISize(wm
) = aReflowInput
.AvailableISize();
989 // If our parent is in initial reflow, it'll handle invalidating our
990 // entire overflow rect.
991 if (!GetParent()->HasAnyStateBits(NS_FRAME_FIRST_REFLOW
) &&
992 nsSize(aDesiredSize
.Width(), aDesiredSize
.Height()) != mRect
.Size()) {
996 // Any absolutely-positioned children will get reflowed in
997 // nsIFrame::FixupPositionedTableParts in another pass, so propagate our
998 // dirtiness to them before our parent clears our dirty bits.
999 PushDirtyBitToAbsoluteFrames();
1003 * This function is called by the row group frame's SplitRowGroup() code when
1004 * pushing a row frame that has cell frames that span into it. The cell frame
1005 * should be reflowed with the specified height
1007 nscoord
nsTableRowFrame::ReflowCellFrame(nsPresContext
* aPresContext
,
1008 const ReflowInput
& aReflowInput
,
1010 nsTableCellFrame
* aCellFrame
,
1011 nscoord aAvailableBSize
,
1012 nsReflowStatus
& aStatus
) {
1013 MOZ_ASSERT(aAvailableBSize
!= NS_UNCONSTRAINEDSIZE
,
1014 "Why split cell frame if available bsize is unconstrained?");
1015 WritingMode wm
= aReflowInput
.GetWritingMode();
1017 // Reflow the cell frame with the specified height. Use the existing width
1018 nsSize containerSize
= aCellFrame
->GetSize();
1019 LogicalRect cellRect
= aCellFrame
->GetLogicalRect(wm
, containerSize
);
1020 nsRect cellInkOverflow
= aCellFrame
->InkOverflowRect();
1022 LogicalSize cellSize
= cellRect
.Size(wm
);
1023 LogicalSize
availSize(wm
, cellRect
.ISize(wm
), aAvailableBSize
);
1024 bool borderCollapse
= GetTableFrame()->IsBorderCollapse();
1025 NS_ASSERTION(aCellFrame
->GetWritingMode() == wm
,
1026 "expected consistent writing-mode within table");
1027 TableCellReflowInput
cellReflowInput(aPresContext
, aReflowInput
, aCellFrame
,
1029 ReflowInput::InitFlag::CallerWillInit
);
1030 InitChildReflowInput(*aPresContext
, availSize
, borderCollapse
,
1032 cellReflowInput
.mFlags
.mIsTopOfPage
= aIsTopOfPage
;
1034 ReflowOutput
desiredSize(aReflowInput
);
1036 ReflowChild(aCellFrame
, aPresContext
, desiredSize
, cellReflowInput
, 0, 0,
1037 ReflowChildFlags::NoMoveFrame
, aStatus
);
1038 const bool isTruncated
=
1039 aAvailableBSize
< desiredSize
.BSize(wm
) &&
1040 !aIsTopOfPage
; // XXX Is !aIsTopOfPage check really necessary?
1041 const bool isCompleteAndNotTruncated
= aStatus
.IsComplete() && !isTruncated
;
1042 if (isCompleteAndNotTruncated
) {
1043 desiredSize
.BSize(wm
) = aAvailableBSize
;
1045 aCellFrame
->SetSize(
1046 wm
, LogicalSize(wm
, cellSize
.ISize(wm
), desiredSize
.BSize(wm
)));
1048 // Note: BlockDirAlignChild can affect the overflow rect.
1049 // XXX What happens if this cell has 'vertical-align: baseline' ?
1050 // XXX Why is it assumed that the cell's ascent hasn't changed ?
1051 if (isCompleteAndNotTruncated
) {
1052 aCellFrame
->BlockDirAlignChild(wm
, mMaxCellAscent
);
1055 nsTableFrame::InvalidateTableFrame(
1056 aCellFrame
, cellRect
.GetPhysicalRect(wm
, containerSize
), cellInkOverflow
,
1057 aCellFrame
->HasAnyStateBits(NS_FRAME_FIRST_REFLOW
));
1059 aCellFrame
->DidReflow(aPresContext
, nullptr);
1061 return desiredSize
.BSize(wm
);
1064 nscoord
nsTableRowFrame::CollapseRowIfNecessary(nscoord aRowOffset
,
1066 bool aCollapseGroup
,
1067 bool& aDidCollapse
) {
1068 const nsStyleVisibility
* rowVis
= StyleVisibility();
1069 bool collapseRow
= StyleVisibility::Collapse
== rowVis
->mVisible
;
1070 nsTableFrame
* tableFrame
=
1071 static_cast<nsTableFrame
*>(GetTableFrame()->FirstInFlow());
1073 tableFrame
->SetNeedToCollapse(true);
1076 if (aRowOffset
!= 0) {
1077 // We're moving, so invalidate our old position
1078 InvalidateFrameSubtree();
1081 WritingMode wm
= GetWritingMode();
1083 nsSize parentSize
= GetParent()->GetSize();
1084 LogicalRect rowRect
= GetLogicalRect(wm
, parentSize
);
1085 nsRect oldRect
= mRect
;
1086 nsRect oldInkOverflow
= InkOverflowRect();
1088 rowRect
.BStart(wm
) -= aRowOffset
;
1089 rowRect
.ISize(wm
) = aISize
;
1090 OverflowAreas overflow
;
1092 nsSize containerSize
= mRect
.Size();
1094 if (aCollapseGroup
|| collapseRow
) {
1095 aDidCollapse
= true;
1096 shift
= rowRect
.BSize(wm
);
1097 nsTableCellFrame
* cellFrame
= GetFirstCell();
1099 uint32_t rowIndex
= cellFrame
->RowIndex();
1100 shift
+= tableFrame
->GetRowSpacing(rowIndex
);
1102 LogicalRect cRect
= cellFrame
->GetLogicalRect(wm
, containerSize
);
1103 // If aRowOffset != 0, there's no point in invalidating the cells, since
1104 // we've already invalidated our overflow area. Note that we _do_ still
1105 // need to invalidate if our row is not moving, because the cell might
1106 // span out of this row, so invalidating our row rect won't do enough.
1107 if (aRowOffset
== 0) {
1110 cRect
.BSize(wm
) = 0;
1111 cellFrame
->SetRect(wm
, cRect
, containerSize
);
1112 cellFrame
= cellFrame
->GetNextCell();
1115 shift
+= tableFrame
->GetRowSpacing(GetRowIndex());
1117 rowRect
.BSize(wm
) = 0;
1118 } else { // row is not collapsed
1119 // remember the col index of the previous cell to handle rowspans into this
1121 int32_t prevColIndex
= -1;
1122 nscoord iPos
= 0; // running total of children inline-axis offset
1123 nsTableFrame
* fifTable
=
1124 static_cast<nsTableFrame
*>(tableFrame
->FirstInFlow());
1126 for (nsTableCellFrame
* cellFrame
= GetFirstCell(); cellFrame
;
1127 cellFrame
= cellFrame
->GetNextCell()) {
1128 uint32_t cellColIndex
= cellFrame
->ColIndex();
1129 int32_t cellColSpan
= tableFrame
->GetEffectiveColSpan(*cellFrame
);
1131 // If the adjacent cell is in a prior row (because of a rowspan) add in
1133 // NOTE: prevColIndex can be -1 here.
1134 if (prevColIndex
!= (static_cast<int32_t>(cellColIndex
) - 1)) {
1135 iPos
+= GetSpaceBetween(prevColIndex
, cellColIndex
, cellColSpan
,
1138 LogicalRect
cRect(wm
, iPos
, 0, 0, rowRect
.BSize(wm
));
1140 // remember the last (iend-wards-most) column this cell spans into
1141 prevColIndex
= cellColIndex
+ cellColSpan
- 1;
1142 int32_t actualColSpan
= cellColSpan
;
1143 bool isVisible
= false;
1144 for (int32_t colIdx
= cellColIndex
; actualColSpan
> 0;
1145 colIdx
++, actualColSpan
--) {
1146 nsTableColFrame
* colFrame
= tableFrame
->GetColFrame(colIdx
);
1147 const nsStyleVisibility
* colVis
= colFrame
->StyleVisibility();
1148 bool collapseCol
= StyleVisibility::Collapse
== colVis
->mVisible
;
1149 nsIFrame
* cgFrame
= colFrame
->GetParent();
1150 const nsStyleVisibility
* groupVis
= cgFrame
->StyleVisibility();
1151 bool collapseGroup
= StyleVisibility::Collapse
== groupVis
->mVisible
;
1152 bool isCollapsed
= collapseCol
|| collapseGroup
;
1154 cRect
.ISize(wm
) += fifTable
->GetColumnISizeFromFirstInFlow(colIdx
);
1156 if ((actualColSpan
> 1)) {
1157 nsTableColFrame
* nextColFrame
= tableFrame
->GetColFrame(colIdx
+ 1);
1158 const nsStyleVisibility
* nextColVis
=
1159 nextColFrame
->StyleVisibility();
1160 if (StyleVisibility::Collapse
!= nextColVis
->mVisible
&&
1161 tableFrame
->ColumnHasCellSpacingBefore(colIdx
+ 1)) {
1162 cRect
.ISize(wm
) += tableFrame
->GetColSpacing(cellColIndex
);
1167 iPos
+= cRect
.ISize(wm
);
1169 iPos
+= tableFrame
->GetColSpacing(cellColIndex
);
1171 int32_t actualRowSpan
= tableFrame
->GetEffectiveRowSpan(*cellFrame
);
1172 nsTableRowFrame
* rowFrame
= GetNextRow();
1173 for (actualRowSpan
--; actualRowSpan
> 0 && rowFrame
; actualRowSpan
--) {
1174 const nsStyleVisibility
* nextRowVis
= rowFrame
->StyleVisibility();
1175 bool collapseNextRow
=
1176 StyleVisibility::Collapse
== nextRowVis
->mVisible
;
1177 if (!collapseNextRow
) {
1178 LogicalRect nextRect
= rowFrame
->GetLogicalRect(wm
, containerSize
);
1179 cRect
.BSize(wm
) += nextRect
.BSize(wm
) +
1180 tableFrame
->GetRowSpacing(rowFrame
->GetRowIndex());
1182 rowFrame
= rowFrame
->GetNextRow();
1185 nsRect oldCellRect
= cellFrame
->GetRect();
1186 LogicalPoint oldCellNormalPos
=
1187 cellFrame
->GetLogicalNormalPosition(wm
, containerSize
);
1189 nsRect oldCellInkOverflow
= cellFrame
->InkOverflowRect();
1191 if (aRowOffset
== 0 && cRect
.Origin(wm
) != oldCellNormalPos
) {
1192 // We're moving the cell. Invalidate the old overflow area
1193 cellFrame
->InvalidateFrameSubtree();
1196 cellFrame
->MovePositionBy(wm
, cRect
.Origin(wm
) - oldCellNormalPos
);
1197 cellFrame
->SetSize(wm
, cRect
.Size(wm
));
1199 // XXXbz This looks completely bogus in the cases when we didn't
1200 // collapse the cell!
1201 LogicalRect
cellBounds(wm
, 0, 0, cRect
.ISize(wm
), cRect
.BSize(wm
));
1202 nsRect cellPhysicalBounds
= cellBounds
.GetPhysicalRect(wm
, containerSize
);
1203 OverflowAreas
cellOverflow(cellPhysicalBounds
, cellPhysicalBounds
);
1204 cellFrame
->FinishAndStoreOverflow(cellOverflow
,
1205 cRect
.Size(wm
).GetPhysicalSize(wm
));
1206 nsTableFrame::RePositionViews(cellFrame
);
1207 ConsiderChildOverflow(overflow
, cellFrame
);
1209 if (aRowOffset
== 0) {
1210 nsTableFrame::InvalidateTableFrame(cellFrame
, oldCellRect
,
1211 oldCellInkOverflow
, false);
1216 SetRect(wm
, rowRect
, containerSize
);
1217 overflow
.UnionAllWith(nsRect(0, 0, rowRect
.Width(wm
), rowRect
.Height(wm
)));
1218 FinishAndStoreOverflow(overflow
, rowRect
.Size(wm
).GetPhysicalSize(wm
));
1220 nsTableFrame::RePositionViews(this);
1221 nsTableFrame::InvalidateTableFrame(this, oldRect
, oldInkOverflow
, false);
1226 * The following method is called by the row group frame's SplitRowGroup()
1227 * when it creates a continuing cell frame and wants to insert it into the
1230 void nsTableRowFrame::InsertCellFrame(nsTableCellFrame
* aFrame
,
1231 int32_t aColIndex
) {
1232 // Find the cell frame where col index < aColIndex
1233 nsTableCellFrame
* priorCell
= nullptr;
1235 for (nsTableCellFrame
* cellFrame
= GetFirstCell(); cellFrame
;
1236 cellFrame
= cellFrame
->GetNextCell()) {
1237 uint32_t colIndex
= cellFrame
->ColIndex();
1238 // Can aColIndex be -1 here? Let's assume it can for now.
1239 if (static_cast<int32_t>(colIndex
) < aColIndex
) {
1240 priorCell
= cellFrame
;
1245 mFrames
.InsertFrame(this, priorCell
, aFrame
);
1248 nsTableRowFrame
* nsTableRowFrame::GetPrevRow() const {
1249 nsIFrame
* prevSibling
= GetPrevSibling();
1251 !prevSibling
|| static_cast<nsTableRowFrame
*>(do_QueryFrame(prevSibling
)),
1252 "How do we have a non-row sibling?");
1253 return static_cast<nsTableRowFrame
*>(prevSibling
);
1256 nsTableRowFrame
* nsTableRowFrame::GetNextRow() const {
1257 nsIFrame
* nextSibling
= GetNextSibling();
1259 !nextSibling
|| static_cast<nsTableRowFrame
*>(do_QueryFrame(nextSibling
)),
1260 "How do we have a non-row sibling?");
1261 return static_cast<nsTableRowFrame
*>(nextSibling
);
1264 // This property is only set on the first-in-flow of nsTableRowFrame.
1265 NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(TableRowUnpaginatedBSizeProperty
, nscoord
)
1267 void nsTableRowFrame::SetUnpaginatedBSize(nscoord aValue
) {
1268 MOZ_ASSERT(!GetPrevInFlow(),
1269 "TableRowUnpaginatedBSizeProperty should only be set on the "
1271 AddStateBits(NS_TABLE_ROW_HAS_UNPAGINATED_BSIZE
);
1272 SetProperty(TableRowUnpaginatedBSizeProperty(), aValue
);
1275 nscoord
nsTableRowFrame::GetUnpaginatedBSize() const {
1276 return GetProperty(TableRowUnpaginatedBSizeProperty());
1279 #ifdef ACCESSIBILITY
1280 a11y::AccType
nsTableRowFrame::AccessibleType() {
1281 return a11y::eHTMLTableRowType
;
1285 * Sets the NS_ROW_HAS_CELL_WITH_STYLE_BSIZE bit to indicate whether
1286 * this row has any cells that have non-auto-bsize. (Row-spanning
1287 * cells are ignored.)
1289 void nsTableRowFrame::InitHasCellWithStyleBSize(nsTableFrame
* aTableFrame
) {
1290 WritingMode wm
= GetWritingMode();
1292 for (nsTableCellFrame
* cellFrame
= GetFirstCell(); cellFrame
;
1293 cellFrame
= cellFrame
->GetNextCell()) {
1294 // Ignore row-spanning cells
1295 const auto& cellBSize
= cellFrame
->StylePosition()->BSize(wm
);
1296 if (aTableFrame
->GetEffectiveRowSpan(*cellFrame
) == 1 &&
1297 !cellBSize
.IsAuto() &&
1298 /* calc() with both percentages and lengths treated like 'auto' */
1299 (cellBSize
.ConvertsToLength() || cellBSize
.ConvertsToPercentage())) {
1300 AddStateBits(NS_ROW_HAS_CELL_WITH_STYLE_BSIZE
);
1304 RemoveStateBits(NS_ROW_HAS_CELL_WITH_STYLE_BSIZE
);
1307 void nsTableRowFrame::InvalidateFrame(uint32_t aDisplayItemKey
,
1308 bool aRebuildDisplayItems
) {
1309 nsIFrame::InvalidateFrame(aDisplayItemKey
, aRebuildDisplayItems
);
1310 if (GetTableFrame()->IsBorderCollapse()) {
1311 const bool rebuild
= StaticPrefs::layout_display_list_retain_sc();
1312 GetParent()->InvalidateFrameWithRect(InkOverflowRect() + GetPosition(),
1313 aDisplayItemKey
, rebuild
);
1317 void nsTableRowFrame::InvalidateFrameWithRect(const nsRect
& aRect
,
1318 uint32_t aDisplayItemKey
,
1319 bool aRebuildDisplayItems
) {
1320 nsIFrame::InvalidateFrameWithRect(aRect
, aDisplayItemKey
,
1321 aRebuildDisplayItems
);
1322 // If we have filters applied that would affects our bounds, then
1323 // we get an inactive layer created and this is computed
1324 // within FrameLayerBuilder
1325 GetParent()->InvalidateFrameWithRect(aRect
+ GetPosition(), aDisplayItemKey
,
1326 aRebuildDisplayItems
);
1329 /* ----- global methods ----- */
1331 nsTableRowFrame
* NS_NewTableRowFrame(PresShell
* aPresShell
,
1332 ComputedStyle
* aStyle
) {
1333 return new (aPresShell
) nsTableRowFrame(aStyle
, aPresShell
->GetPresContext());
1336 NS_IMPL_FRAMEARENA_HELPERS(nsTableRowFrame
)
1338 #ifdef DEBUG_FRAME_DUMP
1339 nsresult
nsTableRowFrame::GetFrameName(nsAString
& aResult
) const {
1340 return MakeFrameName(u
"TableRow"_ns
, aResult
);