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 "nsGkAtoms.h"
17 #include "nsIContent.h"
19 #include "nsIFrameInlines.h"
20 #include "nsTableFrame.h"
21 #include "nsTableCellFrame.h"
22 #include "nsCSSRendering.h"
23 #include "nsHTMLParts.h"
24 #include "nsTableColGroupFrame.h"
25 #include "nsTableColFrame.h"
27 #include "nsDisplayList.h"
28 #include "nsIFrameInlines.h"
32 # include "nsAccessibilityService.h"
35 using namespace mozilla
;
39 struct TableCellReflowInput
: public ReflowInput
{
40 TableCellReflowInput(nsPresContext
* aPresContext
,
41 const ReflowInput
& aParentReflowInput
, nsIFrame
* aFrame
,
42 const LogicalSize
& aAvailableSpace
,
43 ReflowInput::InitFlags aFlags
= {})
44 : ReflowInput(aPresContext
, aParentReflowInput
, aFrame
, aAvailableSpace
,
47 void FixUp(const LogicalSize
& aAvailSpace
);
50 } // namespace mozilla
52 void TableCellReflowInput::FixUp(const LogicalSize
& aAvailSpace
) {
53 // fix the mComputed values during a pass 2 reflow since the cell can be a
56 NS_UNCONSTRAINEDSIZE
!= aAvailSpace
.ISize(mWritingMode
),
57 "have unconstrained inline-size; this should only result from very large "
58 "sizes, not attempts at intrinsic inline size calculation");
59 if (NS_UNCONSTRAINEDSIZE
!= ComputedISize()) {
60 nscoord computedISize
=
61 aAvailSpace
.ISize(mWritingMode
) -
62 ComputedLogicalBorderPadding(mWritingMode
).IStartEnd(mWritingMode
);
63 computedISize
= std::max(0, computedISize
);
64 SetComputedISize(computedISize
);
66 if (NS_UNCONSTRAINEDSIZE
!= ComputedBSize() &&
67 NS_UNCONSTRAINEDSIZE
!= aAvailSpace
.BSize(mWritingMode
)) {
68 nscoord computedBSize
=
69 aAvailSpace
.BSize(mWritingMode
) -
70 ComputedLogicalBorderPadding(mWritingMode
).BStartEnd(mWritingMode
);
71 computedBSize
= std::max(0, computedBSize
);
72 SetComputedBSize(computedBSize
);
76 void nsTableRowFrame::InitChildReflowInput(nsPresContext
& aPresContext
,
77 const LogicalSize
& aAvailSize
,
79 TableCellReflowInput
& aReflowInput
) {
80 Maybe
<LogicalMargin
> collapseBorder
;
81 if (aBorderCollapse
) {
82 // we only reflow cells, so don't need to check frame type
83 nsBCTableCellFrame
* bcCellFrame
= (nsBCTableCellFrame
*)aReflowInput
.mFrame
;
85 collapseBorder
.emplace(
86 bcCellFrame
->GetBorderWidth(aReflowInput
.GetWritingMode()));
89 aReflowInput
.Init(&aPresContext
, Nothing(), collapseBorder
);
90 aReflowInput
.FixUp(aAvailSize
);
93 void nsTableRowFrame::SetFixedBSize(nscoord aValue
) {
94 nscoord bsize
= std::max(0, aValue
);
95 if (HasFixedBSize()) {
96 if (bsize
> mStyleFixedBSize
) {
97 mStyleFixedBSize
= bsize
;
100 mStyleFixedBSize
= bsize
;
102 SetHasFixedBSize(true);
107 void nsTableRowFrame::SetPctBSize(float aPctValue
, bool aForce
) {
108 nscoord bsize
= std::max(0, NSToCoordRound(aPctValue
* 100.0f
));
110 if ((bsize
> mStylePctBSize
) || aForce
) {
111 mStylePctBSize
= bsize
;
114 mStylePctBSize
= bsize
;
116 SetHasPctBSize(true);
121 /* ----------- nsTableRowFrame ---------- */
123 NS_QUERYFRAME_HEAD(nsTableRowFrame
)
124 NS_QUERYFRAME_ENTRY(nsTableRowFrame
)
125 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame
)
127 nsTableRowFrame::nsTableRowFrame(ComputedStyle
* aStyle
,
128 nsPresContext
* aPresContext
, ClassID aID
)
129 : nsContainerFrame(aStyle
, aPresContext
, aID
) {
131 mBits
.mHasFixedBSize
= 0;
132 mBits
.mHasPctBSize
= 0;
133 mBits
.mFirstInserted
= 0;
137 nsTableRowFrame::~nsTableRowFrame() = default;
139 void nsTableRowFrame::Init(nsIContent
* aContent
, nsContainerFrame
* aParent
,
140 nsIFrame
* aPrevInFlow
) {
141 // Let the base class do its initialization
142 nsContainerFrame::Init(aContent
, aParent
, aPrevInFlow
);
144 NS_ASSERTION(mozilla::StyleDisplay::TableRow
== StyleDisplay()->mDisplay
,
145 "wrong display on table row frame");
149 nsTableRowFrame
* rowFrame
= (nsTableRowFrame
*)aPrevInFlow
;
151 SetRowIndex(rowFrame
->GetRowIndex());
153 mWritingMode
= GetTableFrame()->GetWritingMode();
157 void nsTableRowFrame::Destroy(DestroyContext
& aContext
) {
158 nsTableFrame::MaybeUnregisterPositionedTablePart(this);
159 nsContainerFrame::Destroy(aContext
);
163 void nsTableRowFrame::DidSetComputedStyle(ComputedStyle
* aOldComputedStyle
) {
164 nsContainerFrame::DidSetComputedStyle(aOldComputedStyle
);
165 nsTableFrame::PositionedTablePartMaybeChanged(this, aOldComputedStyle
);
167 if (!aOldComputedStyle
) {
168 return; // avoid the following on init
172 if (nsAccessibilityService
* accService
= GetAccService()) {
173 // If a table row's background color is now different from
174 // the background color of its previous row, it is possible our
175 // table now has alternating row colors. This changes whether or not
176 // the table is classified as a layout table or data table.
177 // We invalidate on every background color change to avoid
178 // walking the tree in search of the nearest row.
179 if (StyleBackground()->BackgroundColor(this) !=
180 aOldComputedStyle
->StyleBackground()->BackgroundColor(
181 aOldComputedStyle
)) {
182 // We send a notification here to invalidate the a11y cache on the
183 // table so the next fetch of IsProbablyLayoutTable() is accurate.
184 accService
->TableLayoutGuessMaybeChanged(PresShell(), mContent
);
189 nsTableFrame
* tableFrame
= GetTableFrame();
190 if (tableFrame
->IsBorderCollapse() &&
191 tableFrame
->BCRecalcNeeded(aOldComputedStyle
, Style())) {
192 TableArea
damageArea(0, GetRowIndex(), tableFrame
->GetColCount(), 1);
193 tableFrame
->AddBCDamageArea(damageArea
);
197 void nsTableRowFrame::AppendFrames(ChildListID aListID
,
198 nsFrameList
&& aFrameList
) {
199 NS_ASSERTION(aListID
== FrameChildListID::Principal
, "unexpected child list");
201 DrainSelfOverflowList(); // ensure the last frame is in mFrames
202 const nsFrameList::Slice
& newCells
=
203 mFrames
.AppendFrames(nullptr, std::move(aFrameList
));
205 // Add the new cell frames to the table
206 nsTableFrame
* tableFrame
= GetTableFrame();
207 for (nsIFrame
* childFrame
: newCells
) {
208 NS_ASSERTION(childFrame
->IsTableCellFrame(),
209 "Not a table cell frame/pseudo frame construction failure");
210 tableFrame
->AppendCell(static_cast<nsTableCellFrame
&>(*childFrame
),
214 PresShell()->FrameNeedsReflow(this, IntrinsicDirty::FrameAndAncestors
,
215 NS_FRAME_HAS_DIRTY_CHILDREN
);
216 tableFrame
->SetGeometryDirty();
219 void nsTableRowFrame::InsertFrames(ChildListID aListID
, nsIFrame
* aPrevFrame
,
220 const nsLineList::iterator
* aPrevFrameLine
,
221 nsFrameList
&& aFrameList
) {
222 NS_ASSERTION(aListID
== FrameChildListID::Principal
, "unexpected child list");
223 NS_ASSERTION(!aPrevFrame
|| aPrevFrame
->GetParent() == this,
224 "inserting after sibling frame with different parent");
225 if (mFrames
.IsEmpty() || (aPrevFrame
&& !aPrevFrame
->GetNextSibling())) {
226 // This is actually an append (though our caller didn't figure that out),
227 // and our append codepath is both simpler/faster _and_ less buggy.
228 // https://bugzilla.mozilla.org/show_bug.cgi?id=1388898 tracks the bugginess
229 AppendFrames(aListID
, std::move(aFrameList
));
233 DrainSelfOverflowList(); // ensure aPrevFrame is in mFrames
234 // Insert Frames in the frame list
235 const nsFrameList::Slice
& newCells
=
236 mFrames
.InsertFrames(nullptr, aPrevFrame
, std::move(aFrameList
));
238 nsTableCellFrame
* prevCellFrame
=
239 static_cast<nsTableCellFrame
*>(nsTableFrame::GetFrameAtOrBefore(
240 this, aPrevFrame
, LayoutFrameType::TableCell
));
241 nsTArray
<nsTableCellFrame
*> cellChildren
;
242 for (nsIFrame
* childFrame
: newCells
) {
243 NS_ASSERTION(childFrame
->IsTableCellFrame(),
244 "Not a table cell frame/pseudo frame construction failure");
245 cellChildren
.AppendElement(static_cast<nsTableCellFrame
*>(childFrame
));
247 // insert the cells into the cell map
248 int32_t colIndex
= -1;
250 colIndex
= prevCellFrame
->ColIndex();
252 nsTableFrame
* tableFrame
= GetTableFrame();
253 tableFrame
->InsertCells(cellChildren
, GetRowIndex(), colIndex
);
255 PresShell()->FrameNeedsReflow(this, IntrinsicDirty::FrameAndAncestors
,
256 NS_FRAME_HAS_DIRTY_CHILDREN
);
257 tableFrame
->SetGeometryDirty();
260 void nsTableRowFrame::RemoveFrame(DestroyContext
& aContext
, ChildListID aListID
,
261 nsIFrame
* aOldFrame
) {
262 NS_ASSERTION(aListID
== FrameChildListID::Principal
, "unexpected child list");
263 MOZ_ASSERT((nsTableCellFrame
*)do_QueryFrame(aOldFrame
));
265 auto* cellFrame
= static_cast<nsTableCellFrame
*>(aOldFrame
);
266 // remove the cell from the cell map
267 nsTableFrame
* tableFrame
= GetTableFrame();
268 tableFrame
->RemoveCell(cellFrame
, GetRowIndex());
270 // Remove the frame and destroy it
271 mFrames
.DestroyFrame(aContext
, aOldFrame
);
273 PresShell()->FrameNeedsReflow(this, IntrinsicDirty::FrameAndAncestors
,
274 NS_FRAME_HAS_DIRTY_CHILDREN
);
276 tableFrame
->SetGeometryDirty();
280 nsMargin
nsTableRowFrame::GetUsedMargin() const { return nsMargin(0, 0, 0, 0); }
283 nsMargin
nsTableRowFrame::GetUsedBorder() const { return nsMargin(0, 0, 0, 0); }
286 nsMargin
nsTableRowFrame::GetUsedPadding() const {
287 return nsMargin(0, 0, 0, 0);
290 static nscoord
GetBSizeOfRowsSpannedBelowFirst(
291 nsTableCellFrame
& aTableCellFrame
, nsTableFrame
& aTableFrame
,
292 const WritingMode aWM
) {
294 int32_t rowSpan
= aTableFrame
.GetEffectiveRowSpan(aTableCellFrame
);
295 // add in bsize of rows spanned beyond the 1st one
296 nsIFrame
* nextRow
= aTableCellFrame
.GetParent()->GetNextSibling();
297 for (int32_t rowX
= 1; ((rowX
< rowSpan
) && nextRow
);) {
298 if (nextRow
->IsTableRowFrame()) {
299 bsize
+= nextRow
->BSize(aWM
);
302 bsize
+= aTableFrame
.GetRowSpacing(rowX
);
303 nextRow
= nextRow
->GetNextSibling();
309 * Post-reflow hook. This is where the table row does its post-processing
311 void nsTableRowFrame::DidResize() {
312 // Resize and re-align the cell frames based on our row bsize
313 nsTableFrame
* tableFrame
= GetTableFrame();
315 WritingMode wm
= GetWritingMode();
316 ReflowOutput
desiredSize(wm
);
317 desiredSize
.SetSize(wm
, GetLogicalSize(wm
));
318 desiredSize
.SetOverflowAreasToDesiredBounds();
320 nsSize containerSize
= mRect
.Size();
322 for (nsTableCellFrame
* cellFrame
= GetFirstCell(); cellFrame
;
323 cellFrame
= cellFrame
->GetNextCell()) {
324 nscoord cellBSize
= BSize(wm
) + GetBSizeOfRowsSpannedBelowFirst(
325 *cellFrame
, *tableFrame
, wm
);
327 // If the bsize for the cell has changed, we need to reset it;
328 // and in vertical-rl mode, we need to update the cell's block position
329 // to account for the containerSize, which may not have been known
330 // earlier, so we always apply it here.
331 LogicalSize cellSize
= cellFrame
->GetLogicalSize(wm
);
332 if (cellSize
.BSize(wm
) != cellBSize
|| wm
.IsVerticalRL()) {
333 nsRect cellOldRect
= cellFrame
->GetRect();
334 nsRect cellInkOverflow
= cellFrame
->InkOverflowRect();
336 if (wm
.IsVerticalRL()) {
337 // Get the old position of the cell, as we want to preserve its
338 // inline coordinate.
339 LogicalPoint oldPos
= cellFrame
->GetLogicalPosition(wm
, containerSize
);
341 // The cell should normally be aligned with the row's block-start,
342 // so set the B component of the position to zero:
343 LogicalPoint
newPos(wm
, oldPos
.I(wm
), 0);
345 // ...unless relative positioning is in effect, in which case the
346 // cell may have been moved away from the row's block-start
347 if (cellFrame
->IsRelativelyOrStickyPositioned()) {
348 // Find out where the cell would have been without relative
350 LogicalPoint oldNormalPos
=
351 cellFrame
->GetLogicalNormalPosition(wm
, containerSize
);
352 // The difference (if any) between oldPos and oldNormalPos reflects
353 // relative positioning that was applied to the cell, and which we
354 // need to incorporate when resetting the position.
355 newPos
.B(wm
) = oldPos
.B(wm
) - oldNormalPos
.B(wm
);
358 if (oldPos
!= newPos
) {
359 cellFrame
->SetPosition(wm
, newPos
, containerSize
);
360 nsTableFrame::RePositionViews(cellFrame
);
364 cellSize
.BSize(wm
) = cellBSize
;
365 cellFrame
->SetSize(wm
, cellSize
);
367 if (tableFrame
->IsBorderCollapse()) {
368 nsTableFrame::InvalidateTableFrame(cellFrame
, cellOldRect
,
369 cellInkOverflow
, false);
373 // realign cell content based on the new bsize. We might be able to
374 // skip this if the bsize didn't change... maybe. Hard to tell.
375 cellFrame
->BlockDirAlignChild(wm
, mMaxCellAscent
);
377 // Always store the overflow, even if the height didn't change, since
378 // we'll lose part of our overflow area otherwise.
379 ConsiderChildOverflow(desiredSize
.mOverflowAreas
, cellFrame
);
381 // Note that if the cell's *content* needs to change in response
382 // to this height, it will get a special bsize reflow.
384 FinishAndStoreOverflow(&desiredSize
);
386 nsContainerFrame::SyncFrameViewAfterReflow(PresContext(), this, GetView(),
387 desiredSize
.InkOverflow(),
388 ReflowChildFlags::Default
);
390 // Let our base class do the usual work
393 // returns max-ascent amongst all cells that have 'vertical-align: baseline'
394 // *including* cells with rowspans
395 nscoord
nsTableRowFrame::GetMaxCellAscent() const { return mMaxCellAscent
; }
397 Maybe
<nscoord
> nsTableRowFrame::GetRowBaseline(WritingMode aWM
) {
398 if (mMaxCellAscent
) {
399 return Some(mMaxCellAscent
);
402 // If we get here, we don't have a baseline on any of the cells in this row.
403 if (aWM
.IsCentralBaseline()) {
407 for (nsIFrame
* childFrame
: mFrames
) {
408 MOZ_ASSERT(childFrame
->IsTableCellFrame());
409 nscoord s
= Baseline::SynthesizeBOffsetFromContentBox(
410 childFrame
, aWM
, BaselineSharingGroup::First
);
411 ascent
= std::max(ascent
, s
);
416 nscoord
nsTableRowFrame::GetInitialBSize(nscoord aPctBasis
) const {
418 if ((aPctBasis
> 0) && HasPctBSize()) {
419 bsize
= NSToCoordRound(GetPctBSize() * (float)aPctBasis
);
421 if (HasFixedBSize()) {
422 bsize
= std::max(bsize
, GetFixedBSize());
424 return std::max(bsize
, GetContentBSize());
427 void nsTableRowFrame::ResetBSize() {
428 SetHasFixedBSize(false);
429 SetHasPctBSize(false);
438 void nsTableRowFrame::UpdateBSize(nscoord aBSize
, nscoord aAscent
,
439 nscoord aDescent
, nsTableFrame
* aTableFrame
,
440 nsTableCellFrame
* aCellFrame
) {
441 if (!aTableFrame
|| !aCellFrame
) {
442 NS_ASSERTION(false, "invalid call");
446 if (aBSize
!= NS_UNCONSTRAINEDSIZE
) {
447 if (!(aCellFrame
->HasVerticalAlignBaseline())) { // only the cell's height
449 if (GetInitialBSize() < aBSize
) {
450 int32_t rowSpan
= aTableFrame
->GetEffectiveRowSpan(*aCellFrame
);
452 SetContentBSize(aBSize
);
455 } else { // the alignment on the baseline can change the bsize
456 NS_ASSERTION((aAscent
!= NS_UNCONSTRAINEDSIZE
) &&
457 (aDescent
!= NS_UNCONSTRAINEDSIZE
),
459 // see if this is a long ascender
460 if (mMaxCellAscent
< aAscent
) {
461 mMaxCellAscent
= aAscent
;
463 // see if this is a long descender and without rowspan
464 if (mMaxCellDescent
< aDescent
) {
465 int32_t rowSpan
= aTableFrame
->GetEffectiveRowSpan(*aCellFrame
);
467 mMaxCellDescent
= aDescent
;
470 // keep the tallest bsize in sync
471 if (GetInitialBSize() < mMaxCellAscent
+ mMaxCellDescent
) {
472 SetContentBSize(mMaxCellAscent
+ mMaxCellDescent
);
478 nscoord
nsTableRowFrame::CalcBSize(const ReflowInput
& aReflowInput
) {
479 nsTableFrame
* tableFrame
= GetTableFrame();
482 const nscoord computedBSize
= aReflowInput
.ComputedBSize();
483 if (computedBSize
!= NS_UNCONSTRAINEDSIZE
&& computedBSize
> 0) {
484 SetFixedBSize(computedBSize
);
487 WritingMode wm
= aReflowInput
.GetWritingMode();
488 const nsStylePosition
* position
= StylePosition();
489 const auto& bsizeStyleCoord
= position
->BSize(wm
);
490 if (bsizeStyleCoord
.ConvertsToLength()) {
491 SetFixedBSize(bsizeStyleCoord
.ToLength());
492 } else if (bsizeStyleCoord
.ConvertsToPercentage()) {
493 SetPctBSize(bsizeStyleCoord
.ToPercentage());
496 for (nsTableCellFrame
* kidFrame
= GetFirstCell(); kidFrame
;
497 kidFrame
= kidFrame
->GetNextCell()) {
498 MOZ_ASSERT(kidFrame
->GetWritingMode() == wm
);
499 LogicalSize desSize
= kidFrame
->GetDesiredSize();
500 if ((NS_UNCONSTRAINEDSIZE
== aReflowInput
.AvailableBSize()) &&
502 desSize
.BSize(wm
) = CalcCellActualBSize(kidFrame
, desSize
.BSize(wm
), wm
);
504 // bsize may have changed, adjust descent to absorb any excess difference
506 if (!kidFrame
->PrincipalChildList()
508 ->PrincipalChildList()
510 ascent
= desSize
.BSize(wm
);
512 ascent
= kidFrame
->GetCellBaseline();
514 nscoord descent
= desSize
.BSize(wm
) - ascent
;
515 UpdateBSize(desSize
.BSize(wm
), ascent
, descent
, tableFrame
, kidFrame
);
517 return GetInitialBSize();
520 void nsTableRowFrame::PaintCellBackgroundsForFrame(
521 nsIFrame
* aFrame
, nsDisplayListBuilder
* aBuilder
,
522 const nsDisplayListSet
& aLists
, const nsPoint
& aOffset
) {
523 // Compute background rect by iterating all cell frame.
524 const nsPoint toReferenceFrame
= aBuilder
->ToReferenceFrame(aFrame
);
525 for (nsTableCellFrame
* cell
= GetFirstCell(); cell
;
526 cell
= cell
->GetNextCell()) {
527 if (!cell
->ShouldPaintBackground(aBuilder
)) {
532 cell
->GetRectRelativeToSelf() + cell
->GetNormalPosition() + aOffset
;
533 if (!aBuilder
->GetDirtyRect().Intersects(cellRect
)) {
536 cellRect
+= toReferenceFrame
;
537 nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
538 aBuilder
, aFrame
, cellRect
, aLists
.BorderBackground(), true,
539 aFrame
->GetRectRelativeToSelf() + toReferenceFrame
, cell
);
543 void nsTableRowFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
544 const nsDisplayListSet
& aLists
) {
545 DisplayOutsetBoxShadow(aBuilder
, aLists
.BorderBackground());
547 PaintCellBackgroundsForFrame(this, aBuilder
, aLists
);
549 DisplayInsetBoxShadow(aBuilder
, aLists
.BorderBackground());
551 DisplayOutline(aBuilder
, aLists
);
553 for (nsIFrame
* kid
: PrincipalChildList()) {
554 BuildDisplayListForChild(aBuilder
, kid
, aLists
);
558 LogicalSides
nsTableRowFrame::GetLogicalSkipSides() const {
559 LogicalSides
skip(mWritingMode
);
560 if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak
==
561 StyleBoxDecorationBreak::Clone
)) {
565 if (GetPrevInFlow()) {
566 skip
|= eLogicalSideBitsBStart
;
568 if (GetNextInFlow()) {
569 skip
|= eLogicalSideBitsBEnd
;
574 nscoord
nsTableRowFrame::CalcCellActualBSize(nsTableCellFrame
* aCellFrame
,
575 const nscoord
& aDesiredBSize
,
577 nscoord specifiedBSize
= 0;
579 // Get the bsize specified in the style information
580 const nsStylePosition
* position
= aCellFrame
->StylePosition();
582 int32_t rowSpan
= GetTableFrame()->GetEffectiveRowSpan(*aCellFrame
);
584 const auto& bsizeStyleCoord
= position
->BSize(aWM
);
585 if (bsizeStyleCoord
.ConvertsToLength()) {
586 // In quirks mode, table cell isize should be content-box, but bsize
587 // should be border-box.
588 // Because of this historic anomaly, we do not use quirk.css
589 // (since we can't specify one value of box-sizing for isize and another
591 specifiedBSize
= bsizeStyleCoord
.ToLength();
592 if (PresContext()->CompatibilityMode() != eCompatibility_NavQuirks
&&
593 position
->mBoxSizing
== StyleBoxSizing::Content
) {
595 aCellFrame
->GetLogicalUsedBorderAndPadding(aWM
).BStartEnd(aWM
);
599 SetFixedBSize(specifiedBSize
);
601 } else if (bsizeStyleCoord
.ConvertsToPercentage()) {
603 SetPctBSize(bsizeStyleCoord
.ToPercentage());
607 // If the specified bsize is greater than the desired bsize,
608 // then use the specified bsize
609 return std::max(specifiedBSize
, aDesiredBSize
);
612 // Calculates the available isize for the table cell based on the known
613 // column isizes taking into account column spans and column spacing
614 static nscoord
CalcAvailISize(nsTableFrame
& aTableFrame
,
615 nsTableCellFrame
& aCellFrame
) {
616 nscoord cellAvailISize
= 0;
617 uint32_t colIndex
= aCellFrame
.ColIndex();
618 int32_t colspan
= aTableFrame
.GetEffectiveColSpan(aCellFrame
);
619 NS_ASSERTION(colspan
> 0, "effective colspan should be positive");
620 nsTableFrame
* fifTable
=
621 static_cast<nsTableFrame
*>(aTableFrame
.FirstInFlow());
623 for (int32_t spanX
= 0; spanX
< colspan
; spanX
++) {
624 cellAvailISize
+= fifTable
->GetColumnISizeFromFirstInFlow(colIndex
+ spanX
);
625 if (spanX
> 0 && aTableFrame
.ColumnHasCellSpacingBefore(colIndex
+ spanX
)) {
626 cellAvailISize
+= aTableFrame
.GetColSpacing(colIndex
+ spanX
- 1);
629 return cellAvailISize
;
632 static nscoord
GetSpaceBetween(int32_t aPrevColIndex
, int32_t aColIndex
,
633 int32_t aColSpan
, nsTableFrame
& aTableFrame
,
634 bool aCheckVisibility
) {
637 nsTableFrame
* fifTable
=
638 static_cast<nsTableFrame
*>(aTableFrame
.FirstInFlow());
639 for (colIdx
= aPrevColIndex
+ 1; aColIndex
> colIdx
; colIdx
++) {
640 bool isCollapsed
= false;
641 if (!aCheckVisibility
) {
642 space
+= fifTable
->GetColumnISizeFromFirstInFlow(colIdx
);
644 nsTableColFrame
* colFrame
= aTableFrame
.GetColFrame(colIdx
);
645 const nsStyleVisibility
* colVis
= colFrame
->StyleVisibility();
646 bool collapseCol
= StyleVisibility::Collapse
== colVis
->mVisible
;
647 nsIFrame
* cgFrame
= colFrame
->GetParent();
648 const nsStyleVisibility
* groupVis
= cgFrame
->StyleVisibility();
649 bool collapseGroup
= StyleVisibility::Collapse
== groupVis
->mVisible
;
650 isCollapsed
= collapseCol
|| collapseGroup
;
652 space
+= fifTable
->GetColumnISizeFromFirstInFlow(colIdx
);
654 if (!isCollapsed
&& aTableFrame
.ColumnHasCellSpacingBefore(colIdx
)) {
655 space
+= aTableFrame
.GetColSpacing(colIdx
- 1);
661 // subtract the bsizes of aRow's prev in flows from the unpaginated bsize
662 static nscoord
CalcBSizeFromUnpaginatedBSize(nsTableRowFrame
& aRow
,
665 nsTableRowFrame
* firstInFlow
=
666 static_cast<nsTableRowFrame
*>(aRow
.FirstInFlow());
667 if (firstInFlow
->HasUnpaginatedBSize()) {
668 bsize
= firstInFlow
->GetUnpaginatedBSize();
669 for (nsIFrame
* prevInFlow
= aRow
.GetPrevInFlow(); prevInFlow
;
670 prevInFlow
= prevInFlow
->GetPrevInFlow()) {
671 bsize
-= prevInFlow
->BSize(aWM
);
674 return std::max(bsize
, 0);
677 void nsTableRowFrame::ReflowChildren(nsPresContext
* aPresContext
,
678 ReflowOutput
& aDesiredSize
,
679 const ReflowInput
& aReflowInput
,
680 nsTableFrame
& aTableFrame
,
681 nsReflowStatus
& aStatus
) {
684 // XXXldb Should we be checking constrained bsize instead?
685 const bool isPaginated
= aPresContext
->IsPaginated();
686 const bool borderCollapse
= aTableFrame
.IsBorderCollapse();
688 int32_t cellColSpan
=
689 1; // must be defined here so it's set properly for non-cell kids
691 // remember the col index of the previous cell to handle rowspans into this
693 int32_t prevColIndex
= -1;
694 nscoord iCoord
= 0; // running total of children inline-coord offset
696 // This computes the max of all cell bsizes
697 nscoord cellMaxBSize
= 0;
699 // Reflow each of our existing cell frames
700 WritingMode wm
= aReflowInput
.GetWritingMode();
701 nsSize containerSize
= aReflowInput
.ComputedSizeAsContainerIfConstrained();
703 for (nsTableCellFrame
* kidFrame
= GetFirstCell(); kidFrame
;
704 kidFrame
= kidFrame
->GetNextCell()) {
705 // See if we should only reflow the dirty child frames
706 bool doReflowChild
= true;
707 if (!aReflowInput
.ShouldReflowAllKids() && !aTableFrame
.IsGeometryDirty() &&
708 !kidFrame
->IsSubtreeDirty()) {
709 if (!aReflowInput
.mFlags
.mSpecialBSizeReflow
) 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
) {
719 !kidFrame
->HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE
)) {
724 uint32_t cellColIndex
= kidFrame
->ColIndex();
725 cellColSpan
= aTableFrame
.GetEffectiveColSpan(*kidFrame
);
727 // If the adjacent cell is in a prior row (because of a rowspan) add in the
728 // space NOTE: prevColIndex can be -1 here.
729 if (prevColIndex
!= (static_cast<int32_t>(cellColIndex
) - 1)) {
730 iCoord
+= GetSpaceBetween(prevColIndex
, cellColIndex
, cellColSpan
,
734 // remember the rightmost (ltr) or leftmost (rtl) column this cell spans
736 prevColIndex
= cellColIndex
+ (cellColSpan
- 1);
738 // Reflow the child frame
739 nsRect kidRect
= kidFrame
->GetRect();
740 LogicalPoint origKidNormalPosition
=
741 kidFrame
->GetLogicalNormalPosition(wm
, containerSize
);
743 nsRect kidInkOverflow
= kidFrame
->InkOverflowRect();
744 LogicalPoint
kidPosition(wm
, iCoord
, 0);
745 bool firstReflow
= kidFrame
->HasAnyStateBits(NS_FRAME_FIRST_REFLOW
);
748 // Calculate the available isize for the table cell using the known
750 nscoord availCellISize
= CalcAvailISize(aTableFrame
, *kidFrame
);
752 Maybe
<TableCellReflowInput
> kidReflowInput
;
753 ReflowOutput
desiredSize(aReflowInput
);
755 // If the avail isize is not the same as last time we reflowed the cell or
756 // the cell wants to be bigger than what was available last time or
757 // it is a style change reflow or we are printing, then we must reflow the
758 // cell. Otherwise we can skip the reflow.
759 // XXXldb Why is this condition distinct from doReflowChild above?
760 NS_ASSERTION(kidFrame
->GetWritingMode() == wm
,
761 "expected consistent writing-mode within table");
762 LogicalSize cellDesiredSize
= kidFrame
->GetDesiredSize();
763 if ((availCellISize
!= kidFrame
->GetPriorAvailISize()) ||
764 (cellDesiredSize
.ISize(wm
) > kidFrame
->GetPriorAvailISize()) ||
765 HasAnyStateBits(NS_FRAME_IS_DIRTY
) || isPaginated
||
766 kidFrame
->IsSubtreeDirty() ||
767 // See if it needs a special reflow, or if it had one that we need to
769 kidFrame
->HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE
) ||
771 // Reflow the cell to fit the available isize, bsize
772 // XXX The old IR_ChildIsDirty code used availCellISize here.
773 LogicalSize
kidAvailSize(wm
, availCellISize
,
774 aReflowInput
.AvailableBSize());
777 kidReflowInput
.emplace(aPresContext
, aReflowInput
, kidFrame
,
779 ReflowInput::InitFlag::CallerWillInit
);
780 InitChildReflowInput(*aPresContext
, kidAvailSize
, borderCollapse
,
783 nsReflowStatus status
;
784 ReflowChild(kidFrame
, aPresContext
, desiredSize
, *kidReflowInput
, wm
,
785 kidPosition
, containerSize
, ReflowChildFlags::Default
,
788 // allow the table to determine if/how the table needs to be rebalanced
789 // If any of the cells are not complete, then we're not complete
790 if (status
.IsIncomplete()) {
792 aStatus
.SetIncomplete();
795 if (iCoord
!= origKidNormalPosition
.I(wm
)) {
796 kidFrame
->InvalidateFrameSubtree();
799 desiredSize
.SetSize(wm
, cellDesiredSize
);
800 desiredSize
.mOverflowAreas
= kidFrame
->GetOverflowAreas();
802 // if we are in a floated table, our position is not yet established, so
803 // we cannot reposition our views the containing block will do this for
804 // us after positioning the table
805 if (!aTableFrame
.IsFloating()) {
806 // Because we may have moved the frame we need to make sure any views
807 // are positioned properly. We have to do this, because any one of our
808 // parent frames could have moved and we have no way of knowing...
809 nsTableFrame::RePositionViews(kidFrame
);
813 if (NS_UNCONSTRAINEDSIZE
== aReflowInput
.AvailableBSize()) {
814 if (!GetPrevInFlow()) {
815 desiredSize
.BSize(wm
) =
816 CalcCellActualBSize(kidFrame
, desiredSize
.BSize(wm
), wm
);
818 // bsize may have changed, adjust descent to absorb any excess
821 if (!kidFrame
->PrincipalChildList()
823 ->PrincipalChildList()
825 ascent
= desiredSize
.BSize(wm
);
827 ascent
= kidFrame
->GetCellBaseline();
829 nscoord descent
= desiredSize
.BSize(wm
) - ascent
;
830 UpdateBSize(desiredSize
.BSize(wm
), ascent
, descent
, &aTableFrame
,
833 cellMaxBSize
= std::max(cellMaxBSize
, desiredSize
.BSize(wm
));
834 int32_t rowSpan
= aTableFrame
.GetEffectiveRowSpan(*kidFrame
);
836 SetContentBSize(cellMaxBSize
);
841 desiredSize
.ISize(wm
) = availCellISize
;
843 ReflowChildFlags flags
= ReflowChildFlags::Default
;
845 if (kidReflowInput
) {
846 // We reflowed. Apply relative positioning in the normal way.
847 flags
= ReflowChildFlags::ApplyRelativePositioning
;
848 } else if (kidFrame
->IsRelativelyOrStickyPositioned()) {
849 // We didn't reflow. Do the positioning part of what
850 // MovePositionBy does internally. (This codepath should really
851 // be merged into the else below if we can.)
852 nsMargin
* computedOffsetProp
=
853 kidFrame
->GetProperty(nsIFrame::ComputedOffsetProperty());
855 // On our fist reflow sticky children may not have the property yet (we
856 // need to reflow the children first to size the scroll frame).
857 LogicalMargin
computedOffsets(
858 wm
, computedOffsetProp
? *computedOffsetProp
: nsMargin());
859 ReflowInput::ApplyRelativePositioning(kidFrame
, wm
, computedOffsets
,
860 &kidPosition
, containerSize
);
863 // In vertical-rl mode, we are likely to have containerSize.width = 0
864 // because ComputedWidth() was NS_UNCONSTRAINEDSIZE.
865 // For cases where that's wrong, we will fix up the position later.
866 FinishReflowChild(kidFrame
, aPresContext
, desiredSize
,
867 kidReflowInput
.ptrOr(nullptr), wm
, kidPosition
,
868 containerSize
, flags
);
870 nsTableFrame
* tableFrame
= GetTableFrame();
871 if (tableFrame
->IsBorderCollapse()) {
872 nsTableFrame::InvalidateTableFrame(kidFrame
, kidRect
, kidInkOverflow
,
876 iCoord
+= desiredSize
.ISize(wm
);
878 if (iCoord
!= origKidNormalPosition
.I(wm
)) {
879 // Invalidate the old position
880 kidFrame
->InvalidateFrameSubtree();
881 // Move to the new position. As above, we need to account for relative
883 kidFrame
->MovePositionBy(
884 wm
, LogicalPoint(wm
, iCoord
- origKidNormalPosition
.I(wm
), 0));
885 nsTableFrame::RePositionViews(kidFrame
);
886 // invalidate the new position
887 kidFrame
->InvalidateFrameSubtree();
889 // we need to account for the cell's isize even if it isn't reflowed
890 iCoord
+= kidFrame
->ISize(wm
);
892 if (kidFrame
->GetNextInFlow()) {
894 aStatus
.SetIncomplete();
897 ConsiderChildOverflow(aDesiredSize
.mOverflowAreas
, kidFrame
);
898 iCoord
+= aTableFrame
.GetColSpacing(cellColIndex
);
901 // Just set our isize to what was available.
902 // The table will calculate the isize and not use our value.
903 aDesiredSize
.ISize(wm
) = aReflowInput
.AvailableISize();
905 if (aReflowInput
.mFlags
.mSpecialBSizeReflow
) {
906 aDesiredSize
.BSize(wm
) = BSize(wm
);
907 } else if (NS_UNCONSTRAINEDSIZE
== aReflowInput
.AvailableBSize()) {
908 aDesiredSize
.BSize(wm
) = CalcBSize(aReflowInput
);
909 if (GetPrevInFlow()) {
910 nscoord bsize
= CalcBSizeFromUnpaginatedBSize(*this, wm
);
911 aDesiredSize
.BSize(wm
) = std::max(aDesiredSize
.BSize(wm
), bsize
);
913 if (isPaginated
&& HasStyleBSize()) {
914 // set the unpaginated bsize so next in flows can try to honor it
915 SetUnpaginatedBSize(aDesiredSize
.BSize(wm
));
917 if (isPaginated
&& HasUnpaginatedBSize()) {
918 aDesiredSize
.BSize(wm
) =
919 std::max(aDesiredSize
.BSize(wm
), GetUnpaginatedBSize());
922 } else { // constrained bsize, paginated
923 // Compute the bsize we should have from style (subtracting the
924 // bsize from our prev-in-flows from the style bsize)
925 nscoord styleBSize
= CalcBSizeFromUnpaginatedBSize(*this, wm
);
926 if (styleBSize
> aReflowInput
.AvailableBSize()) {
927 styleBSize
= aReflowInput
.AvailableBSize();
928 aStatus
.SetIncomplete();
930 aDesiredSize
.BSize(wm
) = std::max(cellMaxBSize
, styleBSize
);
933 if (wm
.IsVerticalRL()) {
934 // Any children whose width was not the same as our final
935 // aDesiredSize.BSize will have been misplaced earlier at the
936 // FinishReflowChild stage. So fix them up now.
937 for (nsIFrame
* kidFrame
: mFrames
) {
938 if (kidFrame
->BSize(wm
) != aDesiredSize
.BSize(wm
)) {
939 kidFrame
->MovePositionBy(
941 LogicalPoint(wm
, 0, kidFrame
->BSize(wm
) - aDesiredSize
.BSize(wm
)));
942 nsTableFrame::RePositionViews(kidFrame
);
943 // Do we need to InvalidateFrameSubtree() here?
948 aDesiredSize
.UnionOverflowAreasWithDesiredBounds();
949 FinishAndStoreOverflow(&aDesiredSize
);
952 /** Layout the entire row.
953 * This method stacks cells in the inline dir according to HTML 4.0 rules.
955 void nsTableRowFrame::Reflow(nsPresContext
* aPresContext
,
956 ReflowOutput
& aDesiredSize
,
957 const ReflowInput
& aReflowInput
,
958 nsReflowStatus
& aStatus
) {
960 DO_GLOBAL_REFLOW_COUNT("nsTableRowFrame");
961 DISPLAY_REFLOW(aPresContext
, this, aReflowInput
, aDesiredSize
, aStatus
);
962 MOZ_ASSERT(aStatus
.IsEmpty(), "Caller should pass a fresh reflow status!");
964 WritingMode wm
= aReflowInput
.GetWritingMode();
966 nsTableFrame
* tableFrame
= GetTableFrame();
967 const nsStyleVisibility
* rowVis
= StyleVisibility();
968 bool collapseRow
= StyleVisibility::Collapse
== rowVis
->mVisible
;
970 tableFrame
->SetNeedToCollapse(true);
973 // see if a special bsize reflow needs to occur due to having a pct bsize
974 nsTableFrame::CheckRequestSpecialBSizeReflow(aReflowInput
);
976 // See if we have a cell with specified/pct bsize
977 InitHasCellWithStyleBSize(tableFrame
);
979 ReflowChildren(aPresContext
, aDesiredSize
, aReflowInput
, *tableFrame
,
982 if (aPresContext
->IsPaginated() && !aStatus
.IsFullyComplete() &&
983 ShouldAvoidBreakInside(aReflowInput
)) {
984 aStatus
.SetInlineLineBreakBeforeAndReset();
987 // Just set our isize to what was available.
988 // The table will calculate the isize and not use our value.
989 aDesiredSize
.ISize(wm
) = aReflowInput
.AvailableISize();
991 // If our parent is in initial reflow, it'll handle invalidating our
992 // entire overflow rect.
993 if (!GetParent()->HasAnyStateBits(NS_FRAME_FIRST_REFLOW
) &&
994 nsSize(aDesiredSize
.Width(), aDesiredSize
.Height()) != mRect
.Size()) {
998 // Any absolutely-positioned children will get reflowed in
999 // nsIFrame::FixupPositionedTableParts in another pass, so propagate our
1000 // dirtiness to them before our parent clears our dirty bits.
1001 PushDirtyBitToAbsoluteFrames();
1005 * This function is called by the row group frame's SplitRowGroup() code when
1006 * pushing a row frame that has cell frames that span into it. The cell frame
1007 * should be reflowed with the specified height
1009 nscoord
nsTableRowFrame::ReflowCellFrame(nsPresContext
* aPresContext
,
1010 const ReflowInput
& aReflowInput
,
1012 nsTableCellFrame
* aCellFrame
,
1013 nscoord aAvailableBSize
,
1014 nsReflowStatus
& aStatus
) {
1015 MOZ_ASSERT(aAvailableBSize
!= NS_UNCONSTRAINEDSIZE
,
1016 "Why split cell frame if available bsize is unconstrained?");
1017 WritingMode wm
= aReflowInput
.GetWritingMode();
1019 // Reflow the cell frame with the specified height. Use the existing width
1020 nsSize containerSize
= aCellFrame
->GetSize();
1021 LogicalRect cellRect
= aCellFrame
->GetLogicalRect(wm
, containerSize
);
1022 nsRect cellInkOverflow
= aCellFrame
->InkOverflowRect();
1024 LogicalSize cellSize
= cellRect
.Size(wm
);
1025 LogicalSize
availSize(wm
, cellRect
.ISize(wm
), aAvailableBSize
);
1026 bool borderCollapse
= GetTableFrame()->IsBorderCollapse();
1027 NS_ASSERTION(aCellFrame
->GetWritingMode() == wm
,
1028 "expected consistent writing-mode within table");
1029 TableCellReflowInput
cellReflowInput(aPresContext
, aReflowInput
, aCellFrame
,
1031 ReflowInput::InitFlag::CallerWillInit
);
1032 InitChildReflowInput(*aPresContext
, availSize
, borderCollapse
,
1034 cellReflowInput
.mFlags
.mIsTopOfPage
= aIsTopOfPage
;
1036 ReflowOutput
desiredSize(aReflowInput
);
1038 ReflowChild(aCellFrame
, aPresContext
, desiredSize
, cellReflowInput
, 0, 0,
1039 ReflowChildFlags::NoMoveFrame
, aStatus
);
1040 const bool isTruncated
=
1041 aAvailableBSize
< desiredSize
.BSize(wm
) &&
1042 !aIsTopOfPage
; // XXX Is !aIsTopOfPage check really necessary?
1043 const bool isCompleteAndNotTruncated
= aStatus
.IsComplete() && !isTruncated
;
1044 if (isCompleteAndNotTruncated
) {
1045 desiredSize
.BSize(wm
) = aAvailableBSize
;
1047 aCellFrame
->SetSize(
1048 wm
, LogicalSize(wm
, cellSize
.ISize(wm
), desiredSize
.BSize(wm
)));
1050 // Note: BlockDirAlignChild can affect the overflow rect.
1051 // XXX What happens if this cell has 'vertical-align: baseline' ?
1052 // XXX Why is it assumed that the cell's ascent hasn't changed ?
1053 if (isCompleteAndNotTruncated
) {
1054 aCellFrame
->BlockDirAlignChild(wm
, mMaxCellAscent
);
1057 nsTableFrame::InvalidateTableFrame(
1058 aCellFrame
, cellRect
.GetPhysicalRect(wm
, containerSize
), cellInkOverflow
,
1059 aCellFrame
->HasAnyStateBits(NS_FRAME_FIRST_REFLOW
));
1061 aCellFrame
->DidReflow(aPresContext
, nullptr);
1063 return desiredSize
.BSize(wm
);
1066 nscoord
nsTableRowFrame::CollapseRowIfNecessary(nscoord aRowOffset
,
1068 bool aCollapseGroup
,
1069 bool& aDidCollapse
) {
1070 const nsStyleVisibility
* rowVis
= StyleVisibility();
1071 bool collapseRow
= StyleVisibility::Collapse
== rowVis
->mVisible
;
1072 nsTableFrame
* tableFrame
=
1073 static_cast<nsTableFrame
*>(GetTableFrame()->FirstInFlow());
1075 tableFrame
->SetNeedToCollapse(true);
1078 if (aRowOffset
!= 0) {
1079 // We're moving, so invalidate our old position
1080 InvalidateFrameSubtree();
1083 WritingMode wm
= GetWritingMode();
1085 nsSize parentSize
= GetParent()->GetSize();
1086 LogicalRect rowRect
= GetLogicalRect(wm
, parentSize
);
1087 nsRect oldRect
= mRect
;
1088 nsRect oldInkOverflow
= InkOverflowRect();
1090 rowRect
.BStart(wm
) -= aRowOffset
;
1091 rowRect
.ISize(wm
) = aISize
;
1092 OverflowAreas overflow
;
1094 nsSize containerSize
= mRect
.Size();
1096 if (aCollapseGroup
|| collapseRow
) {
1097 aDidCollapse
= true;
1098 shift
= rowRect
.BSize(wm
);
1099 nsTableCellFrame
* cellFrame
= GetFirstCell();
1101 uint32_t rowIndex
= cellFrame
->RowIndex();
1102 shift
+= tableFrame
->GetRowSpacing(rowIndex
);
1104 LogicalRect cRect
= cellFrame
->GetLogicalRect(wm
, containerSize
);
1105 // If aRowOffset != 0, there's no point in invalidating the cells, since
1106 // we've already invalidated our overflow area. Note that we _do_ still
1107 // need to invalidate if our row is not moving, because the cell might
1108 // span out of this row, so invalidating our row rect won't do enough.
1109 if (aRowOffset
== 0) {
1112 cRect
.BSize(wm
) = 0;
1113 cellFrame
->SetRect(wm
, cRect
, containerSize
);
1114 cellFrame
= cellFrame
->GetNextCell();
1117 shift
+= tableFrame
->GetRowSpacing(GetRowIndex());
1119 rowRect
.BSize(wm
) = 0;
1120 } else { // row is not collapsed
1121 // remember the col index of the previous cell to handle rowspans into this
1123 int32_t prevColIndex
= -1;
1124 nscoord iPos
= 0; // running total of children inline-axis offset
1125 nsTableFrame
* fifTable
=
1126 static_cast<nsTableFrame
*>(tableFrame
->FirstInFlow());
1128 for (nsTableCellFrame
* cellFrame
= GetFirstCell(); cellFrame
;
1129 cellFrame
= cellFrame
->GetNextCell()) {
1130 uint32_t cellColIndex
= cellFrame
->ColIndex();
1131 int32_t cellColSpan
= tableFrame
->GetEffectiveColSpan(*cellFrame
);
1133 // If the adjacent cell is in a prior row (because of a rowspan) add in
1135 // NOTE: prevColIndex can be -1 here.
1136 if (prevColIndex
!= (static_cast<int32_t>(cellColIndex
) - 1)) {
1137 iPos
+= GetSpaceBetween(prevColIndex
, cellColIndex
, cellColSpan
,
1140 LogicalRect
cRect(wm
, iPos
, 0, 0, rowRect
.BSize(wm
));
1142 // remember the last (iend-wards-most) column this cell spans into
1143 prevColIndex
= cellColIndex
+ cellColSpan
- 1;
1144 int32_t actualColSpan
= cellColSpan
;
1145 bool isVisible
= false;
1146 for (int32_t colIdx
= cellColIndex
; actualColSpan
> 0;
1147 colIdx
++, actualColSpan
--) {
1148 nsTableColFrame
* colFrame
= tableFrame
->GetColFrame(colIdx
);
1149 const nsStyleVisibility
* colVis
= colFrame
->StyleVisibility();
1150 bool collapseCol
= StyleVisibility::Collapse
== colVis
->mVisible
;
1151 nsIFrame
* cgFrame
= colFrame
->GetParent();
1152 const nsStyleVisibility
* groupVis
= cgFrame
->StyleVisibility();
1153 bool collapseGroup
= StyleVisibility::Collapse
== groupVis
->mVisible
;
1154 bool isCollapsed
= collapseCol
|| collapseGroup
;
1156 cRect
.ISize(wm
) += fifTable
->GetColumnISizeFromFirstInFlow(colIdx
);
1158 if ((actualColSpan
> 1)) {
1159 nsTableColFrame
* nextColFrame
= tableFrame
->GetColFrame(colIdx
+ 1);
1160 const nsStyleVisibility
* nextColVis
=
1161 nextColFrame
->StyleVisibility();
1162 if (StyleVisibility::Collapse
!= nextColVis
->mVisible
&&
1163 tableFrame
->ColumnHasCellSpacingBefore(colIdx
+ 1)) {
1164 cRect
.ISize(wm
) += tableFrame
->GetColSpacing(cellColIndex
);
1169 iPos
+= cRect
.ISize(wm
);
1171 iPos
+= tableFrame
->GetColSpacing(cellColIndex
);
1173 int32_t actualRowSpan
= tableFrame
->GetEffectiveRowSpan(*cellFrame
);
1174 nsTableRowFrame
* rowFrame
= GetNextRow();
1175 for (actualRowSpan
--; actualRowSpan
> 0 && rowFrame
; actualRowSpan
--) {
1176 const nsStyleVisibility
* nextRowVis
= rowFrame
->StyleVisibility();
1177 bool collapseNextRow
=
1178 StyleVisibility::Collapse
== nextRowVis
->mVisible
;
1179 if (!collapseNextRow
) {
1180 LogicalRect nextRect
= rowFrame
->GetLogicalRect(wm
, containerSize
);
1181 cRect
.BSize(wm
) += nextRect
.BSize(wm
) +
1182 tableFrame
->GetRowSpacing(rowFrame
->GetRowIndex());
1184 rowFrame
= rowFrame
->GetNextRow();
1187 nsRect oldCellRect
= cellFrame
->GetRect();
1188 LogicalPoint oldCellNormalPos
=
1189 cellFrame
->GetLogicalNormalPosition(wm
, containerSize
);
1191 nsRect oldCellInkOverflow
= cellFrame
->InkOverflowRect();
1193 if (aRowOffset
== 0 && cRect
.Origin(wm
) != oldCellNormalPos
) {
1194 // We're moving the cell. Invalidate the old overflow area
1195 cellFrame
->InvalidateFrameSubtree();
1198 cellFrame
->MovePositionBy(wm
, cRect
.Origin(wm
) - oldCellNormalPos
);
1199 cellFrame
->SetSize(wm
, cRect
.Size(wm
));
1201 // XXXbz This looks completely bogus in the cases when we didn't
1202 // collapse the cell!
1203 LogicalRect
cellBounds(wm
, 0, 0, cRect
.ISize(wm
), cRect
.BSize(wm
));
1204 nsRect cellPhysicalBounds
= cellBounds
.GetPhysicalRect(wm
, containerSize
);
1205 OverflowAreas
cellOverflow(cellPhysicalBounds
, cellPhysicalBounds
);
1206 cellFrame
->FinishAndStoreOverflow(cellOverflow
,
1207 cRect
.Size(wm
).GetPhysicalSize(wm
));
1208 nsTableFrame::RePositionViews(cellFrame
);
1209 ConsiderChildOverflow(overflow
, cellFrame
);
1211 if (aRowOffset
== 0) {
1212 nsTableFrame::InvalidateTableFrame(cellFrame
, oldCellRect
,
1213 oldCellInkOverflow
, false);
1218 SetRect(wm
, rowRect
, containerSize
);
1219 overflow
.UnionAllWith(nsRect(0, 0, rowRect
.Width(wm
), rowRect
.Height(wm
)));
1220 FinishAndStoreOverflow(overflow
, rowRect
.Size(wm
).GetPhysicalSize(wm
));
1222 nsTableFrame::RePositionViews(this);
1223 nsTableFrame::InvalidateTableFrame(this, oldRect
, oldInkOverflow
, false);
1228 * The following method is called by the row group frame's SplitRowGroup()
1229 * when it creates a continuing cell frame and wants to insert it into the
1232 void nsTableRowFrame::InsertCellFrame(nsTableCellFrame
* aFrame
,
1233 int32_t aColIndex
) {
1234 // Find the cell frame where col index < aColIndex
1235 nsTableCellFrame
* priorCell
= nullptr;
1237 for (nsTableCellFrame
* cellFrame
= GetFirstCell(); cellFrame
;
1238 cellFrame
= cellFrame
->GetNextCell()) {
1239 uint32_t colIndex
= cellFrame
->ColIndex();
1240 // Can aColIndex be -1 here? Let's assume it can for now.
1241 if (static_cast<int32_t>(colIndex
) < aColIndex
) {
1242 priorCell
= cellFrame
;
1247 mFrames
.InsertFrame(this, priorCell
, aFrame
);
1250 nsTableRowFrame
* nsTableRowFrame::GetPrevRow() const {
1251 nsIFrame
* prevSibling
= GetPrevSibling();
1253 !prevSibling
|| static_cast<nsTableRowFrame
*>(do_QueryFrame(prevSibling
)),
1254 "How do we have a non-row sibling?");
1255 return static_cast<nsTableRowFrame
*>(prevSibling
);
1258 nsTableRowFrame
* nsTableRowFrame::GetNextRow() const {
1259 nsIFrame
* nextSibling
= GetNextSibling();
1261 !nextSibling
|| static_cast<nsTableRowFrame
*>(do_QueryFrame(nextSibling
)),
1262 "How do we have a non-row sibling?");
1263 return static_cast<nsTableRowFrame
*>(nextSibling
);
1266 // This property is only set on the first-in-flow of nsTableRowFrame.
1267 NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(TableRowUnpaginatedBSizeProperty
, nscoord
)
1269 void nsTableRowFrame::SetUnpaginatedBSize(nscoord aValue
) {
1270 MOZ_ASSERT(!GetPrevInFlow(),
1271 "TableRowUnpaginatedBSizeProperty should only be set on the "
1273 AddStateBits(NS_TABLE_ROW_HAS_UNPAGINATED_BSIZE
);
1274 SetProperty(TableRowUnpaginatedBSizeProperty(), aValue
);
1277 nscoord
nsTableRowFrame::GetUnpaginatedBSize() const {
1278 return GetProperty(TableRowUnpaginatedBSizeProperty());
1281 #ifdef ACCESSIBILITY
1282 a11y::AccType
nsTableRowFrame::AccessibleType() {
1283 return a11y::eHTMLTableRowType
;
1287 * Sets the NS_ROW_HAS_CELL_WITH_STYLE_BSIZE bit to indicate whether
1288 * this row has any cells that have non-auto-bsize. (Row-spanning
1289 * cells are ignored.)
1291 void nsTableRowFrame::InitHasCellWithStyleBSize(nsTableFrame
* aTableFrame
) {
1292 WritingMode wm
= GetWritingMode();
1294 for (nsTableCellFrame
* cellFrame
= GetFirstCell(); cellFrame
;
1295 cellFrame
= cellFrame
->GetNextCell()) {
1296 // Ignore row-spanning cells
1297 const auto& cellBSize
= cellFrame
->StylePosition()->BSize(wm
);
1298 if (aTableFrame
->GetEffectiveRowSpan(*cellFrame
) == 1 &&
1299 !cellBSize
.IsAuto() &&
1300 /* calc() with both percentages and lengths treated like 'auto' */
1301 (cellBSize
.ConvertsToLength() || cellBSize
.ConvertsToPercentage())) {
1302 AddStateBits(NS_ROW_HAS_CELL_WITH_STYLE_BSIZE
);
1306 RemoveStateBits(NS_ROW_HAS_CELL_WITH_STYLE_BSIZE
);
1309 void nsTableRowFrame::InvalidateFrame(uint32_t aDisplayItemKey
,
1310 bool aRebuildDisplayItems
) {
1311 nsIFrame::InvalidateFrame(aDisplayItemKey
, aRebuildDisplayItems
);
1312 if (GetTableFrame()->IsBorderCollapse()) {
1313 const bool rebuild
= StaticPrefs::layout_display_list_retain_sc();
1314 GetParent()->InvalidateFrameWithRect(InkOverflowRect() + GetPosition(),
1315 aDisplayItemKey
, rebuild
);
1319 void nsTableRowFrame::InvalidateFrameWithRect(const nsRect
& aRect
,
1320 uint32_t aDisplayItemKey
,
1321 bool aRebuildDisplayItems
) {
1322 nsIFrame::InvalidateFrameWithRect(aRect
, aDisplayItemKey
,
1323 aRebuildDisplayItems
);
1324 // If we have filters applied that would affects our bounds, then
1325 // we get an inactive layer created and this is computed
1326 // within FrameLayerBuilder
1327 GetParent()->InvalidateFrameWithRect(aRect
+ GetPosition(), aDisplayItemKey
,
1328 aRebuildDisplayItems
);
1331 /* ----- global methods ----- */
1333 nsTableRowFrame
* NS_NewTableRowFrame(PresShell
* aPresShell
,
1334 ComputedStyle
* aStyle
) {
1335 return new (aPresShell
) nsTableRowFrame(aStyle
, aPresShell
->GetPresContext());
1338 NS_IMPL_FRAMEARENA_HELPERS(nsTableRowFrame
)
1340 #ifdef DEBUG_FRAME_DUMP
1341 nsresult
nsTableRowFrame::GetFrameName(nsAString
& aResult
) const {
1342 return MakeFrameName(u
"TableRow"_ns
, aResult
);