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
) {
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 bsize should always be border-box.
585 // https://quirks.spec.whatwg.org/#the-table-cell-height-box-sizing-quirk
586 specifiedBSize
= bsizeStyleCoord
.ToLength();
587 if (PresContext()->CompatibilityMode() != eCompatibility_NavQuirks
&&
588 position
->mBoxSizing
== StyleBoxSizing::Content
) {
590 aCellFrame
->GetLogicalUsedBorderAndPadding(aWM
).BStartEnd(aWM
);
594 SetFixedBSize(specifiedBSize
);
596 } else if (bsizeStyleCoord
.ConvertsToPercentage()) {
598 SetPctBSize(bsizeStyleCoord
.ToPercentage());
602 // If the specified bsize is greater than the desired bsize,
603 // then use the specified bsize
604 return std::max(specifiedBSize
, aDesiredBSize
);
607 // Calculates the available isize for the table cell based on the known
608 // column isizes taking into account column spans and column spacing
609 static nscoord
CalcAvailISize(nsTableFrame
& aTableFrame
,
610 nsTableCellFrame
& aCellFrame
) {
611 nscoord cellAvailISize
= 0;
612 uint32_t colIndex
= aCellFrame
.ColIndex();
613 int32_t colspan
= aTableFrame
.GetEffectiveColSpan(aCellFrame
);
614 NS_ASSERTION(colspan
> 0, "effective colspan should be positive");
615 nsTableFrame
* fifTable
=
616 static_cast<nsTableFrame
*>(aTableFrame
.FirstInFlow());
618 for (int32_t spanX
= 0; spanX
< colspan
; spanX
++) {
619 cellAvailISize
+= fifTable
->GetColumnISizeFromFirstInFlow(colIndex
+ spanX
);
620 if (spanX
> 0 && aTableFrame
.ColumnHasCellSpacingBefore(colIndex
+ spanX
)) {
621 cellAvailISize
+= aTableFrame
.GetColSpacing(colIndex
+ spanX
- 1);
624 return cellAvailISize
;
627 static nscoord
GetSpaceBetween(int32_t aPrevColIndex
, int32_t aColIndex
,
628 int32_t aColSpan
, nsTableFrame
& aTableFrame
,
629 bool aCheckVisibility
) {
632 nsTableFrame
* fifTable
=
633 static_cast<nsTableFrame
*>(aTableFrame
.FirstInFlow());
634 for (colIdx
= aPrevColIndex
+ 1; aColIndex
> colIdx
; colIdx
++) {
635 bool isCollapsed
= false;
636 if (!aCheckVisibility
) {
637 space
+= fifTable
->GetColumnISizeFromFirstInFlow(colIdx
);
639 nsTableColFrame
* colFrame
= aTableFrame
.GetColFrame(colIdx
);
640 const nsStyleVisibility
* colVis
= colFrame
->StyleVisibility();
641 bool collapseCol
= StyleVisibility::Collapse
== colVis
->mVisible
;
642 nsIFrame
* cgFrame
= colFrame
->GetParent();
643 const nsStyleVisibility
* groupVis
= cgFrame
->StyleVisibility();
644 bool collapseGroup
= StyleVisibility::Collapse
== groupVis
->mVisible
;
645 isCollapsed
= collapseCol
|| collapseGroup
;
647 space
+= fifTable
->GetColumnISizeFromFirstInFlow(colIdx
);
649 if (!isCollapsed
&& aTableFrame
.ColumnHasCellSpacingBefore(colIdx
)) {
650 space
+= aTableFrame
.GetColSpacing(colIdx
- 1);
656 // subtract the bsizes of aRow's prev in flows from the unpaginated bsize
657 static nscoord
CalcBSizeFromUnpaginatedBSize(nsTableRowFrame
& aRow
,
660 nsTableRowFrame
* firstInFlow
=
661 static_cast<nsTableRowFrame
*>(aRow
.FirstInFlow());
662 if (firstInFlow
->HasUnpaginatedBSize()) {
663 bsize
= firstInFlow
->GetUnpaginatedBSize();
664 for (nsIFrame
* prevInFlow
= aRow
.GetPrevInFlow(); prevInFlow
;
665 prevInFlow
= prevInFlow
->GetPrevInFlow()) {
666 bsize
-= prevInFlow
->BSize(aWM
);
669 return std::max(bsize
, 0);
672 void nsTableRowFrame::ReflowChildren(nsPresContext
* aPresContext
,
673 ReflowOutput
& aDesiredSize
,
674 const ReflowInput
& aReflowInput
,
675 nsTableFrame
& aTableFrame
,
676 nsReflowStatus
& aStatus
) {
679 // XXXldb Should we be checking constrained bsize instead?
680 const bool isPaginated
= aPresContext
->IsPaginated();
681 const bool borderCollapse
= aTableFrame
.IsBorderCollapse();
683 int32_t cellColSpan
=
684 1; // must be defined here so it's set properly for non-cell kids
686 // remember the col index of the previous cell to handle rowspans into this
688 int32_t prevColIndex
= -1;
689 nscoord iCoord
= 0; // running total of children inline-coord offset
691 // This computes the max of all cell bsizes
692 nscoord cellMaxBSize
= 0;
694 // Reflow each of our existing cell frames
695 WritingMode wm
= aReflowInput
.GetWritingMode();
696 nsSize containerSize
= aReflowInput
.ComputedSizeAsContainerIfConstrained();
698 for (nsTableCellFrame
* kidFrame
= GetFirstCell(); kidFrame
;
699 kidFrame
= kidFrame
->GetNextCell()) {
700 // See if we should only reflow the dirty child frames
701 bool doReflowChild
= true;
702 if (!aReflowInput
.ShouldReflowAllKids() && !aTableFrame
.IsGeometryDirty() &&
703 !kidFrame
->IsSubtreeDirty()) {
704 if (!aReflowInput
.mFlags
.mSpecialBSizeReflow
) {
705 doReflowChild
= false;
707 } else if (NS_UNCONSTRAINEDSIZE
!= aReflowInput
.AvailableBSize()) {
708 // We don't reflow a rowspan >1 cell here with a constrained bsize.
709 // That happens in nsTableRowGroupFrame::SplitSpanningCells.
710 if (aTableFrame
.GetEffectiveRowSpan(*kidFrame
) > 1) {
711 doReflowChild
= false;
714 if (aReflowInput
.mFlags
.mSpecialBSizeReflow
&& !isPaginated
&&
715 !kidFrame
->HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE
)) {
719 uint32_t cellColIndex
= kidFrame
->ColIndex();
720 cellColSpan
= aTableFrame
.GetEffectiveColSpan(*kidFrame
);
722 // If the adjacent cell is in a prior row (because of a rowspan) add in the
723 // space NOTE: prevColIndex can be -1 here.
724 if (prevColIndex
!= (static_cast<int32_t>(cellColIndex
) - 1)) {
725 iCoord
+= GetSpaceBetween(prevColIndex
, cellColIndex
, cellColSpan
,
729 // remember the rightmost (ltr) or leftmost (rtl) column this cell spans
731 prevColIndex
= cellColIndex
+ (cellColSpan
- 1);
733 // Reflow the child frame
734 nsRect kidRect
= kidFrame
->GetRect();
735 LogicalPoint origKidNormalPosition
=
736 kidFrame
->GetLogicalNormalPosition(wm
, containerSize
);
738 nsRect kidInkOverflow
= kidFrame
->InkOverflowRect();
739 LogicalPoint
kidPosition(wm
, iCoord
, 0);
740 bool firstReflow
= kidFrame
->HasAnyStateBits(NS_FRAME_FIRST_REFLOW
);
743 // Calculate the available isize for the table cell using the known
745 nscoord availCellISize
= CalcAvailISize(aTableFrame
, *kidFrame
);
747 Maybe
<TableCellReflowInput
> kidReflowInput
;
748 ReflowOutput
desiredSize(aReflowInput
);
750 // If the avail isize is not the same as last time we reflowed the cell or
751 // the cell wants to be bigger than what was available last time or
752 // it is a style change reflow or we are printing, then we must reflow the
753 // cell. Otherwise we can skip the reflow.
754 // XXXldb Why is this condition distinct from doReflowChild above?
755 NS_ASSERTION(kidFrame
->GetWritingMode() == wm
,
756 "expected consistent writing-mode within table");
757 LogicalSize cellDesiredSize
= kidFrame
->GetDesiredSize();
758 if ((availCellISize
!= kidFrame
->GetPriorAvailISize()) ||
759 (cellDesiredSize
.ISize(wm
) > kidFrame
->GetPriorAvailISize()) ||
760 HasAnyStateBits(NS_FRAME_IS_DIRTY
) || isPaginated
||
761 kidFrame
->IsSubtreeDirty() ||
762 // See if it needs a special reflow, or if it had one that we need to
764 kidFrame
->HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE
) ||
766 // Reflow the cell to fit the available isize, bsize
767 // XXX The old IR_ChildIsDirty code used availCellISize here.
768 LogicalSize
kidAvailSize(wm
, availCellISize
,
769 aReflowInput
.AvailableBSize());
772 kidReflowInput
.emplace(aPresContext
, aReflowInput
, kidFrame
,
774 ReflowInput::InitFlag::CallerWillInit
);
775 InitChildReflowInput(*aPresContext
, kidAvailSize
, borderCollapse
,
778 nsReflowStatus status
;
779 ReflowChild(kidFrame
, aPresContext
, desiredSize
, *kidReflowInput
, wm
,
780 kidPosition
, containerSize
, ReflowChildFlags::Default
,
783 // allow the table to determine if/how the table needs to be rebalanced
784 // If any of the cells are not complete, then we're not complete
785 if (status
.IsIncomplete()) {
787 aStatus
.SetIncomplete();
790 if (iCoord
!= origKidNormalPosition
.I(wm
)) {
791 kidFrame
->InvalidateFrameSubtree();
794 desiredSize
.SetSize(wm
, cellDesiredSize
);
795 desiredSize
.mOverflowAreas
= kidFrame
->GetOverflowAreas();
797 // if we are in a floated table, our position is not yet established, so
798 // we cannot reposition our views the containing block will do this for
799 // us after positioning the table
800 if (!aTableFrame
.IsFloating()) {
801 // Because we may have moved the frame we need to make sure any views
802 // are positioned properly. We have to do this, because any one of our
803 // parent frames could have moved and we have no way of knowing...
804 nsTableFrame::RePositionViews(kidFrame
);
808 if (NS_UNCONSTRAINEDSIZE
== aReflowInput
.AvailableBSize()) {
809 if (!GetPrevInFlow()) {
810 desiredSize
.BSize(wm
) =
811 CalcCellActualBSize(kidFrame
, desiredSize
.BSize(wm
), wm
);
813 // bsize may have changed, adjust descent to absorb any excess
816 if (!kidFrame
->PrincipalChildList()
818 ->PrincipalChildList()
820 ascent
= desiredSize
.BSize(wm
);
822 ascent
= kidFrame
->GetCellBaseline();
824 nscoord descent
= desiredSize
.BSize(wm
) - ascent
;
825 UpdateBSize(desiredSize
.BSize(wm
), ascent
, descent
, &aTableFrame
,
828 cellMaxBSize
= std::max(cellMaxBSize
, desiredSize
.BSize(wm
));
829 int32_t rowSpan
= aTableFrame
.GetEffectiveRowSpan(*kidFrame
);
831 SetContentBSize(cellMaxBSize
);
836 desiredSize
.ISize(wm
) = availCellISize
;
838 ReflowChildFlags flags
= ReflowChildFlags::Default
;
840 if (kidReflowInput
) {
841 // We reflowed. Apply relative positioning in the normal way.
842 flags
= ReflowChildFlags::ApplyRelativePositioning
;
843 } else if (kidFrame
->IsRelativelyOrStickyPositioned()) {
844 // We didn't reflow. Do the positioning part of what
845 // MovePositionBy does internally. (This codepath should really
846 // be merged into the else below if we can.)
847 nsMargin
* computedOffsetProp
=
848 kidFrame
->GetProperty(nsIFrame::ComputedOffsetProperty());
850 // On our fist reflow sticky children may not have the property yet (we
851 // need to reflow the children first to size the scroll frame).
852 LogicalMargin
computedOffsets(
853 wm
, computedOffsetProp
? *computedOffsetProp
: nsMargin());
854 ReflowInput::ApplyRelativePositioning(kidFrame
, wm
, computedOffsets
,
855 &kidPosition
, containerSize
);
858 // In vertical-rl mode, we are likely to have containerSize.width = 0
859 // because ComputedWidth() was NS_UNCONSTRAINEDSIZE.
860 // For cases where that's wrong, we will fix up the position later.
861 FinishReflowChild(kidFrame
, aPresContext
, desiredSize
,
862 kidReflowInput
.ptrOr(nullptr), wm
, kidPosition
,
863 containerSize
, flags
);
865 nsTableFrame
* tableFrame
= GetTableFrame();
866 if (tableFrame
->IsBorderCollapse()) {
867 nsTableFrame::InvalidateTableFrame(kidFrame
, kidRect
, kidInkOverflow
,
871 iCoord
+= desiredSize
.ISize(wm
);
873 if (iCoord
!= origKidNormalPosition
.I(wm
)) {
874 // Invalidate the old position
875 kidFrame
->InvalidateFrameSubtree();
876 // Move to the new position. As above, we need to account for relative
878 kidFrame
->MovePositionBy(
879 wm
, LogicalPoint(wm
, iCoord
- origKidNormalPosition
.I(wm
), 0));
880 nsTableFrame::RePositionViews(kidFrame
);
881 // invalidate the new position
882 kidFrame
->InvalidateFrameSubtree();
884 // we need to account for the cell's isize even if it isn't reflowed
885 iCoord
+= kidFrame
->ISize(wm
);
887 if (kidFrame
->GetNextInFlow()) {
889 aStatus
.SetIncomplete();
892 ConsiderChildOverflow(aDesiredSize
.mOverflowAreas
, kidFrame
);
893 iCoord
+= aTableFrame
.GetColSpacing(cellColIndex
);
896 // Just set our isize to what was available.
897 // The table will calculate the isize and not use our value.
898 aDesiredSize
.ISize(wm
) = aReflowInput
.AvailableISize();
900 if (aReflowInput
.mFlags
.mSpecialBSizeReflow
) {
901 aDesiredSize
.BSize(wm
) = BSize(wm
);
902 } else if (NS_UNCONSTRAINEDSIZE
== aReflowInput
.AvailableBSize()) {
903 aDesiredSize
.BSize(wm
) = CalcBSize(aReflowInput
);
904 if (GetPrevInFlow()) {
905 nscoord bsize
= CalcBSizeFromUnpaginatedBSize(*this, wm
);
906 aDesiredSize
.BSize(wm
) = std::max(aDesiredSize
.BSize(wm
), bsize
);
908 if (isPaginated
&& HasStyleBSize()) {
909 // set the unpaginated bsize so next in flows can try to honor it
910 SetUnpaginatedBSize(aDesiredSize
.BSize(wm
));
912 if (isPaginated
&& HasUnpaginatedBSize()) {
913 aDesiredSize
.BSize(wm
) =
914 std::max(aDesiredSize
.BSize(wm
), GetUnpaginatedBSize());
917 } else { // constrained bsize, paginated
918 // Compute the bsize we should have from style (subtracting the
919 // bsize from our prev-in-flows from the style bsize)
920 nscoord styleBSize
= CalcBSizeFromUnpaginatedBSize(*this, wm
);
921 if (styleBSize
> aReflowInput
.AvailableBSize()) {
922 styleBSize
= aReflowInput
.AvailableBSize();
923 aStatus
.SetIncomplete();
925 aDesiredSize
.BSize(wm
) = std::max(cellMaxBSize
, styleBSize
);
928 if (wm
.IsVerticalRL()) {
929 // Any children whose width was not the same as our final
930 // aDesiredSize.BSize will have been misplaced earlier at the
931 // FinishReflowChild stage. So fix them up now.
932 for (nsIFrame
* kidFrame
: mFrames
) {
933 if (kidFrame
->BSize(wm
) != aDesiredSize
.BSize(wm
)) {
934 kidFrame
->MovePositionBy(
936 LogicalPoint(wm
, 0, kidFrame
->BSize(wm
) - aDesiredSize
.BSize(wm
)));
937 nsTableFrame::RePositionViews(kidFrame
);
938 // Do we need to InvalidateFrameSubtree() here?
943 aDesiredSize
.UnionOverflowAreasWithDesiredBounds();
944 FinishAndStoreOverflow(&aDesiredSize
);
947 /** Layout the entire row.
948 * This method stacks cells in the inline dir according to HTML 4.0 rules.
950 void nsTableRowFrame::Reflow(nsPresContext
* aPresContext
,
951 ReflowOutput
& aDesiredSize
,
952 const ReflowInput
& aReflowInput
,
953 nsReflowStatus
& aStatus
) {
955 DO_GLOBAL_REFLOW_COUNT("nsTableRowFrame");
956 MOZ_ASSERT(aStatus
.IsEmpty(), "Caller should pass a fresh reflow status!");
958 WritingMode wm
= aReflowInput
.GetWritingMode();
960 nsTableFrame
* tableFrame
= GetTableFrame();
961 const nsStyleVisibility
* rowVis
= StyleVisibility();
962 bool collapseRow
= StyleVisibility::Collapse
== rowVis
->mVisible
;
964 tableFrame
->SetNeedToCollapse(true);
967 // see if a special bsize reflow needs to occur due to having a pct bsize
968 nsTableFrame::CheckRequestSpecialBSizeReflow(aReflowInput
);
970 // See if we have a cell with specified/pct bsize
971 InitHasCellWithStyleBSize(tableFrame
);
973 ReflowChildren(aPresContext
, aDesiredSize
, aReflowInput
, *tableFrame
,
976 if (aPresContext
->IsPaginated() && !aStatus
.IsFullyComplete() &&
977 ShouldAvoidBreakInside(aReflowInput
)) {
978 aStatus
.SetInlineLineBreakBeforeAndReset();
981 // Just set our isize to what was available.
982 // The table will calculate the isize and not use our value.
983 aDesiredSize
.ISize(wm
) = aReflowInput
.AvailableISize();
985 // If our parent is in initial reflow, it'll handle invalidating our
986 // entire overflow rect.
987 if (!GetParent()->HasAnyStateBits(NS_FRAME_FIRST_REFLOW
) &&
988 nsSize(aDesiredSize
.Width(), aDesiredSize
.Height()) != mRect
.Size()) {
992 // Any absolutely-positioned children will get reflowed in
993 // nsIFrame::FixupPositionedTableParts in another pass, so propagate our
994 // dirtiness to them before our parent clears our dirty bits.
995 PushDirtyBitToAbsoluteFrames();
998 nscoord
nsTableRowFrame::ReflowCellFrame(nsPresContext
* aPresContext
,
999 const ReflowInput
& aReflowInput
,
1001 nsTableCellFrame
* aCellFrame
,
1002 nscoord aAvailableBSize
,
1003 nsReflowStatus
& aStatus
) {
1004 MOZ_ASSERT(aPresContext
->IsPaginated(),
1005 "ReflowCellFrame currently supports only paged media!");
1006 MOZ_ASSERT(aAvailableBSize
!= NS_UNCONSTRAINEDSIZE
,
1007 "Why split cell frame if available bsize is unconstrained?");
1008 WritingMode wm
= aReflowInput
.GetWritingMode();
1010 // Reflow the cell frame with the specified height. Use the existing width
1011 nsSize containerSize
= aCellFrame
->GetSize();
1012 LogicalRect cellRect
= aCellFrame
->GetLogicalRect(wm
, containerSize
);
1013 nsRect cellInkOverflow
= aCellFrame
->InkOverflowRect();
1015 LogicalSize cellSize
= cellRect
.Size(wm
);
1016 LogicalSize
availSize(wm
, cellRect
.ISize(wm
), aAvailableBSize
);
1017 bool borderCollapse
= GetTableFrame()->IsBorderCollapse();
1018 NS_ASSERTION(aCellFrame
->GetWritingMode() == wm
,
1019 "expected consistent writing-mode within table");
1020 TableCellReflowInput
cellReflowInput(aPresContext
, aReflowInput
, aCellFrame
,
1022 ReflowInput::InitFlag::CallerWillInit
);
1023 InitChildReflowInput(*aPresContext
, availSize
, borderCollapse
,
1025 cellReflowInput
.mFlags
.mIsTopOfPage
= aIsTopOfPage
;
1027 ReflowOutput
desiredSize(aReflowInput
);
1029 ReflowChild(aCellFrame
, aPresContext
, desiredSize
, cellReflowInput
, 0, 0,
1030 ReflowChildFlags::NoMoveFrame
, aStatus
);
1031 const bool isTruncated
=
1032 aAvailableBSize
< desiredSize
.BSize(wm
) &&
1033 !aIsTopOfPage
; // XXX Is !aIsTopOfPage check really necessary?
1034 const bool isCompleteAndNotTruncated
= aStatus
.IsComplete() && !isTruncated
;
1035 if (isCompleteAndNotTruncated
) {
1036 desiredSize
.BSize(wm
) = aAvailableBSize
;
1038 aCellFrame
->SetSize(
1039 wm
, LogicalSize(wm
, cellSize
.ISize(wm
), desiredSize
.BSize(wm
)));
1041 // Note: BlockDirAlignChild can affect the overflow rect.
1042 // XXX What happens if this cell has 'vertical-align: baseline' ?
1043 // XXX Why is it assumed that the cell's ascent hasn't changed ?
1044 if (isCompleteAndNotTruncated
) {
1045 aCellFrame
->BlockDirAlignChild(wm
, mMaxCellAscent
,
1046 ForceAlignTopForTableCell::Yes
);
1049 nsTableFrame::InvalidateTableFrame(
1050 aCellFrame
, cellRect
.GetPhysicalRect(wm
, containerSize
), cellInkOverflow
,
1051 aCellFrame
->HasAnyStateBits(NS_FRAME_FIRST_REFLOW
));
1053 aCellFrame
->DidReflow(aPresContext
, nullptr);
1055 return desiredSize
.BSize(wm
);
1058 nscoord
nsTableRowFrame::CollapseRowIfNecessary(nscoord aRowOffset
,
1060 bool aCollapseGroup
,
1061 bool& aDidCollapse
) {
1062 const nsStyleVisibility
* rowVis
= StyleVisibility();
1063 bool collapseRow
= StyleVisibility::Collapse
== rowVis
->mVisible
;
1064 nsTableFrame
* tableFrame
=
1065 static_cast<nsTableFrame
*>(GetTableFrame()->FirstInFlow());
1067 tableFrame
->SetNeedToCollapse(true);
1070 if (aRowOffset
!= 0) {
1071 // We're moving, so invalidate our old position
1072 InvalidateFrameSubtree();
1075 WritingMode wm
= GetWritingMode();
1077 nsSize parentSize
= GetParent()->GetSize();
1078 LogicalRect rowRect
= GetLogicalRect(wm
, parentSize
);
1079 nsRect oldRect
= mRect
;
1080 nsRect oldInkOverflow
= InkOverflowRect();
1082 rowRect
.BStart(wm
) -= aRowOffset
;
1083 rowRect
.ISize(wm
) = aISize
;
1084 OverflowAreas overflow
;
1086 nsSize containerSize
= mRect
.Size();
1088 if (aCollapseGroup
|| collapseRow
) {
1089 aDidCollapse
= true;
1090 shift
= rowRect
.BSize(wm
);
1091 nsTableCellFrame
* cellFrame
= GetFirstCell();
1093 uint32_t rowIndex
= cellFrame
->RowIndex();
1094 shift
+= tableFrame
->GetRowSpacing(rowIndex
);
1096 LogicalRect cRect
= cellFrame
->GetLogicalRect(wm
, containerSize
);
1097 // If aRowOffset != 0, there's no point in invalidating the cells, since
1098 // we've already invalidated our overflow area. Note that we _do_ still
1099 // need to invalidate if our row is not moving, because the cell might
1100 // span out of this row, so invalidating our row rect won't do enough.
1101 if (aRowOffset
== 0) {
1104 cRect
.BSize(wm
) = 0;
1105 cellFrame
->SetRect(wm
, cRect
, containerSize
);
1106 cellFrame
= cellFrame
->GetNextCell();
1109 shift
+= tableFrame
->GetRowSpacing(GetRowIndex());
1111 rowRect
.BSize(wm
) = 0;
1112 } else { // row is not collapsed
1113 // remember the col index of the previous cell to handle rowspans into this
1115 int32_t prevColIndex
= -1;
1116 nscoord iPos
= 0; // running total of children inline-axis offset
1117 nsTableFrame
* fifTable
=
1118 static_cast<nsTableFrame
*>(tableFrame
->FirstInFlow());
1120 for (nsTableCellFrame
* cellFrame
= GetFirstCell(); cellFrame
;
1121 cellFrame
= cellFrame
->GetNextCell()) {
1122 uint32_t cellColIndex
= cellFrame
->ColIndex();
1123 int32_t cellColSpan
= tableFrame
->GetEffectiveColSpan(*cellFrame
);
1125 // If the adjacent cell is in a prior row (because of a rowspan) add in
1127 // NOTE: prevColIndex can be -1 here.
1128 if (prevColIndex
!= (static_cast<int32_t>(cellColIndex
) - 1)) {
1129 iPos
+= GetSpaceBetween(prevColIndex
, cellColIndex
, cellColSpan
,
1132 LogicalRect
cRect(wm
, iPos
, 0, 0, rowRect
.BSize(wm
));
1134 // remember the last (iend-wards-most) column this cell spans into
1135 prevColIndex
= cellColIndex
+ cellColSpan
- 1;
1136 int32_t actualColSpan
= cellColSpan
;
1137 bool isVisible
= false;
1138 for (int32_t colIdx
= cellColIndex
; actualColSpan
> 0;
1139 colIdx
++, actualColSpan
--) {
1140 nsTableColFrame
* colFrame
= tableFrame
->GetColFrame(colIdx
);
1141 const nsStyleVisibility
* colVis
= colFrame
->StyleVisibility();
1142 bool collapseCol
= StyleVisibility::Collapse
== colVis
->mVisible
;
1143 nsIFrame
* cgFrame
= colFrame
->GetParent();
1144 const nsStyleVisibility
* groupVis
= cgFrame
->StyleVisibility();
1145 bool collapseGroup
= StyleVisibility::Collapse
== groupVis
->mVisible
;
1146 bool isCollapsed
= collapseCol
|| collapseGroup
;
1148 cRect
.ISize(wm
) += fifTable
->GetColumnISizeFromFirstInFlow(colIdx
);
1150 if ((actualColSpan
> 1)) {
1151 nsTableColFrame
* nextColFrame
= tableFrame
->GetColFrame(colIdx
+ 1);
1152 const nsStyleVisibility
* nextColVis
=
1153 nextColFrame
->StyleVisibility();
1154 if (StyleVisibility::Collapse
!= nextColVis
->mVisible
&&
1155 tableFrame
->ColumnHasCellSpacingBefore(colIdx
+ 1)) {
1156 cRect
.ISize(wm
) += tableFrame
->GetColSpacing(cellColIndex
);
1161 iPos
+= cRect
.ISize(wm
);
1163 iPos
+= tableFrame
->GetColSpacing(cellColIndex
);
1165 int32_t actualRowSpan
= tableFrame
->GetEffectiveRowSpan(*cellFrame
);
1166 nsTableRowFrame
* rowFrame
= GetNextRow();
1167 for (actualRowSpan
--; actualRowSpan
> 0 && rowFrame
; actualRowSpan
--) {
1168 const nsStyleVisibility
* nextRowVis
= rowFrame
->StyleVisibility();
1169 bool collapseNextRow
=
1170 StyleVisibility::Collapse
== nextRowVis
->mVisible
;
1171 if (!collapseNextRow
) {
1172 LogicalRect nextRect
= rowFrame
->GetLogicalRect(wm
, containerSize
);
1173 cRect
.BSize(wm
) += nextRect
.BSize(wm
) +
1174 tableFrame
->GetRowSpacing(rowFrame
->GetRowIndex());
1176 rowFrame
= rowFrame
->GetNextRow();
1179 nsRect oldCellRect
= cellFrame
->GetRect();
1180 LogicalPoint oldCellNormalPos
=
1181 cellFrame
->GetLogicalNormalPosition(wm
, containerSize
);
1183 nsRect oldCellInkOverflow
= cellFrame
->InkOverflowRect();
1185 if (aRowOffset
== 0 && cRect
.Origin(wm
) != oldCellNormalPos
) {
1186 // We're moving the cell. Invalidate the old overflow area
1187 cellFrame
->InvalidateFrameSubtree();
1190 cellFrame
->MovePositionBy(wm
, cRect
.Origin(wm
) - oldCellNormalPos
);
1191 cellFrame
->SetSize(wm
, cRect
.Size(wm
));
1193 // XXXbz This looks completely bogus in the cases when we didn't
1194 // collapse the cell!
1195 LogicalRect
cellBounds(wm
, 0, 0, cRect
.ISize(wm
), cRect
.BSize(wm
));
1196 nsRect cellPhysicalBounds
= cellBounds
.GetPhysicalRect(wm
, containerSize
);
1197 OverflowAreas
cellOverflow(cellPhysicalBounds
, cellPhysicalBounds
);
1198 cellFrame
->FinishAndStoreOverflow(cellOverflow
,
1199 cRect
.Size(wm
).GetPhysicalSize(wm
));
1200 nsTableFrame::RePositionViews(cellFrame
);
1201 ConsiderChildOverflow(overflow
, cellFrame
);
1203 if (aRowOffset
== 0) {
1204 nsTableFrame::InvalidateTableFrame(cellFrame
, oldCellRect
,
1205 oldCellInkOverflow
, false);
1210 SetRect(wm
, rowRect
, containerSize
);
1211 overflow
.UnionAllWith(nsRect(0, 0, rowRect
.Width(wm
), rowRect
.Height(wm
)));
1212 FinishAndStoreOverflow(overflow
, rowRect
.Size(wm
).GetPhysicalSize(wm
));
1214 nsTableFrame::RePositionViews(this);
1215 nsTableFrame::InvalidateTableFrame(this, oldRect
, oldInkOverflow
, false);
1220 * The following method is called by the row group frame's SplitRowGroup()
1221 * when it creates a continuing cell frame and wants to insert it into the
1224 void nsTableRowFrame::InsertCellFrame(nsTableCellFrame
* aFrame
,
1225 int32_t aColIndex
) {
1226 // Find the cell frame where col index < aColIndex
1227 nsTableCellFrame
* priorCell
= nullptr;
1229 for (nsTableCellFrame
* cellFrame
= GetFirstCell(); cellFrame
;
1230 cellFrame
= cellFrame
->GetNextCell()) {
1231 uint32_t colIndex
= cellFrame
->ColIndex();
1232 // Can aColIndex be -1 here? Let's assume it can for now.
1233 if (static_cast<int32_t>(colIndex
) < aColIndex
) {
1234 priorCell
= cellFrame
;
1239 mFrames
.InsertFrame(this, priorCell
, aFrame
);
1242 nsTableRowFrame
* nsTableRowFrame::GetPrevRow() const {
1243 nsIFrame
* prevSibling
= GetPrevSibling();
1245 !prevSibling
|| static_cast<nsTableRowFrame
*>(do_QueryFrame(prevSibling
)),
1246 "How do we have a non-row sibling?");
1247 return static_cast<nsTableRowFrame
*>(prevSibling
);
1250 nsTableRowFrame
* nsTableRowFrame::GetNextRow() const {
1251 nsIFrame
* nextSibling
= GetNextSibling();
1253 !nextSibling
|| static_cast<nsTableRowFrame
*>(do_QueryFrame(nextSibling
)),
1254 "How do we have a non-row sibling?");
1255 return static_cast<nsTableRowFrame
*>(nextSibling
);
1258 // This property is only set on the first-in-flow of nsTableRowFrame.
1259 NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(TableRowUnpaginatedBSizeProperty
, nscoord
)
1261 void nsTableRowFrame::SetUnpaginatedBSize(nscoord aValue
) {
1262 MOZ_ASSERT(!GetPrevInFlow(),
1263 "TableRowUnpaginatedBSizeProperty should only be set on the "
1265 AddStateBits(NS_TABLE_ROW_HAS_UNPAGINATED_BSIZE
);
1266 SetProperty(TableRowUnpaginatedBSizeProperty(), aValue
);
1269 nscoord
nsTableRowFrame::GetUnpaginatedBSize() const {
1270 return GetProperty(TableRowUnpaginatedBSizeProperty());
1273 #ifdef ACCESSIBILITY
1274 a11y::AccType
nsTableRowFrame::AccessibleType() {
1275 return a11y::eHTMLTableRowType
;
1279 * Sets the NS_ROW_HAS_CELL_WITH_STYLE_BSIZE bit to indicate whether
1280 * this row has any cells that have non-auto-bsize. (Row-spanning
1281 * cells are ignored.)
1283 void nsTableRowFrame::InitHasCellWithStyleBSize(nsTableFrame
* aTableFrame
) {
1284 WritingMode wm
= GetWritingMode();
1286 for (nsTableCellFrame
* cellFrame
= GetFirstCell(); cellFrame
;
1287 cellFrame
= cellFrame
->GetNextCell()) {
1288 // Ignore row-spanning cells
1289 const auto& cellBSize
= cellFrame
->StylePosition()->BSize(wm
);
1290 if (aTableFrame
->GetEffectiveRowSpan(*cellFrame
) == 1 &&
1291 !cellBSize
.IsAuto() &&
1292 /* calc() with both percentages and lengths treated like 'auto' */
1293 (cellBSize
.ConvertsToLength() || cellBSize
.ConvertsToPercentage())) {
1294 AddStateBits(NS_ROW_HAS_CELL_WITH_STYLE_BSIZE
);
1298 RemoveStateBits(NS_ROW_HAS_CELL_WITH_STYLE_BSIZE
);
1301 void nsTableRowFrame::InvalidateFrame(uint32_t aDisplayItemKey
,
1302 bool aRebuildDisplayItems
) {
1303 nsIFrame::InvalidateFrame(aDisplayItemKey
, aRebuildDisplayItems
);
1304 if (GetTableFrame()->IsBorderCollapse()) {
1305 const bool rebuild
= StaticPrefs::layout_display_list_retain_sc();
1306 GetParent()->InvalidateFrameWithRect(InkOverflowRect() + GetPosition(),
1307 aDisplayItemKey
, rebuild
);
1311 void nsTableRowFrame::InvalidateFrameWithRect(const nsRect
& aRect
,
1312 uint32_t aDisplayItemKey
,
1313 bool aRebuildDisplayItems
) {
1314 nsIFrame::InvalidateFrameWithRect(aRect
, aDisplayItemKey
,
1315 aRebuildDisplayItems
);
1316 // If we have filters applied that would affects our bounds, then
1317 // we get an inactive layer created and this is computed
1318 // within FrameLayerBuilder
1319 GetParent()->InvalidateFrameWithRect(aRect
+ GetPosition(), aDisplayItemKey
,
1320 aRebuildDisplayItems
);
1323 /* ----- global methods ----- */
1325 nsTableRowFrame
* NS_NewTableRowFrame(PresShell
* aPresShell
,
1326 ComputedStyle
* aStyle
) {
1327 return new (aPresShell
) nsTableRowFrame(aStyle
, aPresShell
->GetPresContext());
1330 NS_IMPL_FRAMEARENA_HELPERS(nsTableRowFrame
)
1332 #ifdef DEBUG_FRAME_DUMP
1333 nsresult
nsTableRowFrame::GetFrameName(nsAString
& aResult
) const {
1334 return MakeFrameName(u
"TableRow"_ns
, aResult
);