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/Maybe.h"
9 #include "mozilla/PresShell.h"
10 #include "nsTableRowGroupFrame.h"
11 #include "nsPresContext.h"
12 #include "mozilla/ComputedStyle.h"
13 #include "nsStyleConsts.h"
14 #include "nsGkAtoms.h"
15 #include "nsIContent.h"
17 #include "nsIFrameInlines.h"
18 #include "nsTableFrame.h"
19 #include "nsTableCellFrame.h"
20 #include "nsCSSRendering.h"
21 #include "nsHTMLParts.h"
22 #include "nsTableColGroupFrame.h"
23 #include "nsTableColFrame.h"
25 #include "nsDisplayList.h"
26 #include "nsIFrameInlines.h"
30 # include "nsAccessibilityService.h"
33 using namespace mozilla
;
37 struct TableCellReflowInput
: public ReflowInput
{
38 TableCellReflowInput(nsPresContext
* aPresContext
,
39 const ReflowInput
& aParentReflowInput
, nsIFrame
* aFrame
,
40 const LogicalSize
& aAvailableSpace
,
41 ReflowInput::InitFlags aFlags
= {})
42 : ReflowInput(aPresContext
, aParentReflowInput
, aFrame
, aAvailableSpace
,
45 void FixUp(const LogicalSize
& aAvailSpace
);
48 } // namespace mozilla
50 void TableCellReflowInput::FixUp(const LogicalSize
& aAvailSpace
) {
51 // fix the mComputed values during a pass 2 reflow since the cell can be a
54 NS_UNCONSTRAINEDSIZE
!= aAvailSpace
.ISize(mWritingMode
),
55 "have unconstrained inline-size; this should only result from very large "
56 "sizes, not attempts at intrinsic inline size calculation");
57 if (NS_UNCONSTRAINEDSIZE
!= ComputedISize()) {
58 nscoord computedISize
=
59 aAvailSpace
.ISize(mWritingMode
) -
60 ComputedLogicalBorderPadding(mWritingMode
).IStartEnd(mWritingMode
);
61 computedISize
= std::max(0, computedISize
);
62 SetComputedISize(computedISize
);
64 if (NS_UNCONSTRAINEDSIZE
!= ComputedBSize() &&
65 NS_UNCONSTRAINEDSIZE
!= aAvailSpace
.BSize(mWritingMode
)) {
66 nscoord computedBSize
=
67 aAvailSpace
.BSize(mWritingMode
) -
68 ComputedLogicalBorderPadding(mWritingMode
).BStartEnd(mWritingMode
);
69 computedBSize
= std::max(0, computedBSize
);
70 SetComputedBSize(computedBSize
);
74 void nsTableRowFrame::InitChildReflowInput(nsPresContext
& aPresContext
,
75 const LogicalSize
& aAvailSize
,
77 TableCellReflowInput
& aReflowInput
) {
78 Maybe
<LogicalMargin
> collapseBorder
;
79 if (aBorderCollapse
) {
80 // we only reflow cells, so don't need to check frame type
81 nsBCTableCellFrame
* bcCellFrame
= (nsBCTableCellFrame
*)aReflowInput
.mFrame
;
83 collapseBorder
.emplace(
84 bcCellFrame
->GetBorderWidth(aReflowInput
.GetWritingMode()));
87 aReflowInput
.Init(&aPresContext
, Nothing(), collapseBorder
);
88 aReflowInput
.FixUp(aAvailSize
);
91 void nsTableRowFrame::SetFixedBSize(nscoord aValue
) {
92 nscoord bsize
= std::max(0, aValue
);
93 if (HasFixedBSize()) {
94 if (bsize
> mStyleFixedBSize
) {
95 mStyleFixedBSize
= bsize
;
98 mStyleFixedBSize
= bsize
;
100 SetHasFixedBSize(true);
105 void nsTableRowFrame::SetPctBSize(float aPctValue
, bool aForce
) {
106 nscoord bsize
= std::max(0, NSToCoordRound(aPctValue
* 100.0f
));
108 if ((bsize
> mStylePctBSize
) || aForce
) {
109 mStylePctBSize
= bsize
;
112 mStylePctBSize
= bsize
;
114 SetHasPctBSize(true);
119 /* ----------- nsTableRowFrame ---------- */
121 NS_QUERYFRAME_HEAD(nsTableRowFrame
)
122 NS_QUERYFRAME_ENTRY(nsTableRowFrame
)
123 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame
)
125 nsTableRowFrame::nsTableRowFrame(ComputedStyle
* aStyle
,
126 nsPresContext
* aPresContext
, ClassID aID
)
127 : nsContainerFrame(aStyle
, aPresContext
, aID
),
133 mBStartBorderWidth(0),
135 mIEndContBorderWidth(0),
136 mBStartContBorderWidth(0),
137 mIStartContBorderWidth(0) {
139 mBits
.mHasFixedBSize
= 0;
140 mBits
.mHasPctBSize
= 0;
141 mBits
.mFirstInserted
= 0;
145 nsTableRowFrame::~nsTableRowFrame() = default;
147 void nsTableRowFrame::Init(nsIContent
* aContent
, nsContainerFrame
* aParent
,
148 nsIFrame
* aPrevInFlow
) {
149 // Let the base class do its initialization
150 nsContainerFrame::Init(aContent
, aParent
, aPrevInFlow
);
152 NS_ASSERTION(mozilla::StyleDisplay::TableRow
== StyleDisplay()->mDisplay
,
153 "wrong display on table row frame");
157 nsTableRowFrame
* rowFrame
= (nsTableRowFrame
*)aPrevInFlow
;
159 SetRowIndex(rowFrame
->GetRowIndex());
161 mWritingMode
= GetTableFrame()->GetWritingMode();
165 void nsTableRowFrame::DestroyFrom(nsIFrame
* aDestructRoot
,
166 PostDestroyData
& aPostDestroyData
) {
167 if (HasAnyStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN
)) {
168 nsTableFrame::UnregisterPositionedTablePart(this, aDestructRoot
);
171 nsContainerFrame::DestroyFrom(aDestructRoot
, aPostDestroyData
);
175 void nsTableRowFrame::DidSetComputedStyle(ComputedStyle
* aOldComputedStyle
) {
176 nsContainerFrame::DidSetComputedStyle(aOldComputedStyle
);
178 if (!aOldComputedStyle
) // avoid this on init
182 if (nsAccessibilityService
* accService
= GetAccService()) {
183 // If a table row's background color is now different from
184 // the background color of its previous row, it is possible our
185 // table now has alternating row colors. This changes whether or not
186 // the table is classified as a layout table or data table.
187 // We invalidate on every background color change to avoid
188 // walking the tree in search of the nearest row.
189 if (StyleBackground()->BackgroundColor(this) !=
190 aOldComputedStyle
->StyleBackground()->BackgroundColor(
191 aOldComputedStyle
)) {
192 // We send a notification here to invalidate the a11y cache on the
193 // table so the next fetch of IsProbablyLayoutTable() is accurate.
194 accService
->TableLayoutGuessMaybeChanged(PresShell(), mContent
);
199 nsTableFrame
* tableFrame
= GetTableFrame();
200 if (tableFrame
->IsBorderCollapse() &&
201 tableFrame
->BCRecalcNeeded(aOldComputedStyle
, Style())) {
202 TableArea
damageArea(0, GetRowIndex(), tableFrame
->GetColCount(), 1);
203 tableFrame
->AddBCDamageArea(damageArea
);
207 void nsTableRowFrame::AppendFrames(ChildListID aListID
,
208 nsFrameList
& aFrameList
) {
209 NS_ASSERTION(aListID
== kPrincipalList
, "unexpected child list");
211 DrainSelfOverflowList(); // ensure the last frame is in mFrames
212 const nsFrameList::Slice
& newCells
=
213 mFrames
.AppendFrames(nullptr, aFrameList
);
215 // Add the new cell frames to the table
216 nsTableFrame
* tableFrame
= GetTableFrame();
217 for (nsFrameList::Enumerator
e(newCells
); !e
.AtEnd(); e
.Next()) {
218 nsIFrame
* childFrame
= e
.get();
219 NS_ASSERTION(childFrame
->IsTableCellFrame(),
220 "Not a table cell frame/pseudo frame construction failure");
221 tableFrame
->AppendCell(static_cast<nsTableCellFrame
&>(*childFrame
),
225 PresShell()->FrameNeedsReflow(this, IntrinsicDirty::TreeChange
,
226 NS_FRAME_HAS_DIRTY_CHILDREN
);
227 tableFrame
->SetGeometryDirty();
230 void nsTableRowFrame::InsertFrames(ChildListID aListID
, nsIFrame
* aPrevFrame
,
231 const nsLineList::iterator
* aPrevFrameLine
,
232 nsFrameList
& aFrameList
) {
233 NS_ASSERTION(aListID
== kPrincipalList
, "unexpected child list");
234 NS_ASSERTION(!aPrevFrame
|| aPrevFrame
->GetParent() == this,
235 "inserting after sibling frame with different parent");
236 if (mFrames
.IsEmpty() || (aPrevFrame
&& !aPrevFrame
->GetNextSibling())) {
237 // This is actually an append (though our caller didn't figure that out),
238 // and our append codepath is both simpler/faster _and_ less buggy.
239 // https://bugzilla.mozilla.org/show_bug.cgi?id=1388898 tracks the bugginess
240 AppendFrames(aListID
, aFrameList
);
244 DrainSelfOverflowList(); // ensure aPrevFrame is in mFrames
245 // Insert Frames in the frame list
246 const nsFrameList::Slice
& newCells
=
247 mFrames
.InsertFrames(nullptr, aPrevFrame
, aFrameList
);
249 nsTableCellFrame
* prevCellFrame
=
250 static_cast<nsTableCellFrame
*>(nsTableFrame::GetFrameAtOrBefore(
251 this, aPrevFrame
, LayoutFrameType::TableCell
));
252 nsTArray
<nsTableCellFrame
*> cellChildren
;
253 for (nsFrameList::Enumerator
e(newCells
); !e
.AtEnd(); e
.Next()) {
254 nsIFrame
* childFrame
= e
.get();
255 NS_ASSERTION(childFrame
->IsTableCellFrame(),
256 "Not a table cell frame/pseudo frame construction failure");
257 cellChildren
.AppendElement(static_cast<nsTableCellFrame
*>(childFrame
));
259 // insert the cells into the cell map
260 int32_t colIndex
= -1;
262 colIndex
= prevCellFrame
->ColIndex();
264 nsTableFrame
* tableFrame
= GetTableFrame();
265 tableFrame
->InsertCells(cellChildren
, GetRowIndex(), colIndex
);
267 PresShell()->FrameNeedsReflow(this, IntrinsicDirty::TreeChange
,
268 NS_FRAME_HAS_DIRTY_CHILDREN
);
269 tableFrame
->SetGeometryDirty();
272 void nsTableRowFrame::RemoveFrame(ChildListID aListID
, nsIFrame
* aOldFrame
) {
273 NS_ASSERTION(aListID
== kPrincipalList
, "unexpected child list");
275 MOZ_ASSERT((nsTableCellFrame
*)do_QueryFrame(aOldFrame
));
276 nsTableCellFrame
* cellFrame
= static_cast<nsTableCellFrame
*>(aOldFrame
);
277 // remove the cell from the cell map
278 nsTableFrame
* tableFrame
= GetTableFrame();
279 tableFrame
->RemoveCell(cellFrame
, GetRowIndex());
281 // Remove the frame and destroy it
282 mFrames
.DestroyFrame(aOldFrame
);
284 PresShell()->FrameNeedsReflow(this, IntrinsicDirty::TreeChange
,
285 NS_FRAME_HAS_DIRTY_CHILDREN
);
287 tableFrame
->SetGeometryDirty();
291 nsMargin
nsTableRowFrame::GetUsedMargin() const { return nsMargin(0, 0, 0, 0); }
294 nsMargin
nsTableRowFrame::GetUsedBorder() const { return nsMargin(0, 0, 0, 0); }
297 nsMargin
nsTableRowFrame::GetUsedPadding() const {
298 return nsMargin(0, 0, 0, 0);
301 static nscoord
GetBSizeOfRowsSpannedBelowFirst(
302 nsTableCellFrame
& aTableCellFrame
, nsTableFrame
& aTableFrame
,
303 const WritingMode aWM
) {
305 int32_t rowSpan
= aTableFrame
.GetEffectiveRowSpan(aTableCellFrame
);
306 // add in bsize of rows spanned beyond the 1st one
307 nsIFrame
* nextRow
= aTableCellFrame
.GetParent()->GetNextSibling();
308 for (int32_t rowX
= 1; ((rowX
< rowSpan
) && nextRow
);) {
309 if (nextRow
->IsTableRowFrame()) {
310 bsize
+= nextRow
->BSize(aWM
);
313 bsize
+= aTableFrame
.GetRowSpacing(rowX
);
314 nextRow
= nextRow
->GetNextSibling();
320 * Post-reflow hook. This is where the table row does its post-processing
322 void nsTableRowFrame::DidResize() {
323 // Resize and re-align the cell frames based on our row bsize
324 nsTableFrame
* tableFrame
= GetTableFrame();
326 WritingMode wm
= GetWritingMode();
327 ReflowOutput
desiredSize(wm
);
328 desiredSize
.SetSize(wm
, GetLogicalSize(wm
));
329 desiredSize
.SetOverflowAreasToDesiredBounds();
331 nsSize containerSize
= mRect
.Size();
333 for (nsIFrame
* childFrame
: mFrames
) {
334 nsTableCellFrame
* cellFrame
= do_QueryFrame(childFrame
);
336 nscoord cellBSize
= BSize(wm
) + GetBSizeOfRowsSpannedBelowFirst(
337 *cellFrame
, *tableFrame
, wm
);
339 // If the bsize for the cell has changed, we need to reset it;
340 // and in vertical-rl mode, we need to update the cell's block position
341 // to account for the containerSize, which may not have been known
342 // earlier, so we always apply it here.
343 LogicalSize cellSize
= cellFrame
->GetLogicalSize(wm
);
344 if (cellSize
.BSize(wm
) != cellBSize
|| wm
.IsVerticalRL()) {
345 nsRect cellOldRect
= cellFrame
->GetRect();
346 nsRect cellInkOverflow
= cellFrame
->InkOverflowRect();
348 if (wm
.IsVerticalRL()) {
349 // Get the old position of the cell, as we want to preserve its
350 // inline coordinate.
351 LogicalPoint oldPos
=
352 cellFrame
->GetLogicalPosition(wm
, containerSize
);
354 // The cell should normally be aligned with the row's block-start,
355 // so set the B component of the position to zero:
356 LogicalPoint
newPos(wm
, oldPos
.I(wm
), 0);
358 // ...unless relative positioning is in effect, in which case the
359 // cell may have been moved away from the row's block-start
360 if (cellFrame
->IsRelativelyOrStickyPositioned()) {
361 // Find out where the cell would have been without relative
363 LogicalPoint oldNormalPos
=
364 cellFrame
->GetLogicalNormalPosition(wm
, containerSize
);
365 // The difference (if any) between oldPos and oldNormalPos reflects
366 // relative positioning that was applied to the cell, and which we
367 // need to incorporate when resetting the position.
368 newPos
.B(wm
) = oldPos
.B(wm
) - oldNormalPos
.B(wm
);
371 if (oldPos
!= newPos
) {
372 cellFrame
->SetPosition(wm
, newPos
, containerSize
);
373 nsTableFrame::RePositionViews(cellFrame
);
377 cellSize
.BSize(wm
) = cellBSize
;
378 cellFrame
->SetSize(wm
, cellSize
);
380 nsTableFrame
* tableFrame
= GetTableFrame();
381 if (tableFrame
->IsBorderCollapse()) {
382 nsTableFrame::InvalidateTableFrame(cellFrame
, cellOldRect
,
383 cellInkOverflow
, false);
387 // realign cell content based on the new bsize. We might be able to
388 // skip this if the bsize didn't change... maybe. Hard to tell.
389 cellFrame
->BlockDirAlignChild(wm
, mMaxCellAscent
);
391 // Always store the overflow, even if the height didn't change, since
392 // we'll lose part of our overflow area otherwise.
393 ConsiderChildOverflow(desiredSize
.mOverflowAreas
, cellFrame
);
395 // Note that if the cell's *content* needs to change in response
396 // to this height, it will get a special bsize reflow.
399 FinishAndStoreOverflow(&desiredSize
);
401 nsContainerFrame::SyncFrameViewAfterReflow(PresContext(), this, GetView(),
402 desiredSize
.InkOverflow(),
403 ReflowChildFlags::Default
);
405 // Let our base class do the usual work
408 // returns max-ascent amongst all cells that have 'vertical-align: baseline'
409 // *including* cells with rowspans
410 nscoord
nsTableRowFrame::GetMaxCellAscent() const { return mMaxCellAscent
; }
412 nscoord
nsTableRowFrame::GetRowBaseline(WritingMode aWM
) {
413 if (mMaxCellAscent
) {
414 return mMaxCellAscent
;
417 // If we get here, we don't have a baseline on any of the cells in this row.
420 for (nsIFrame
* childFrame
: mFrames
) {
421 MOZ_ASSERT(childFrame
->IsTableCellFrame());
422 nscoord s
= childFrame
->SynthesizeBaselineBOffsetFromContentBox(
423 aWM
, BaselineSharingGroup::First
);
424 ascent
= std::max(ascent
, s
);
429 nscoord
nsTableRowFrame::GetInitialBSize(nscoord aPctBasis
) const {
431 if ((aPctBasis
> 0) && HasPctBSize()) {
432 bsize
= NSToCoordRound(GetPctBSize() * (float)aPctBasis
);
434 if (HasFixedBSize()) {
435 bsize
= std::max(bsize
, GetFixedBSize());
437 return std::max(bsize
, GetContentBSize());
440 void nsTableRowFrame::ResetBSize(nscoord aFixedBSize
) {
441 SetHasFixedBSize(false);
442 SetHasPctBSize(false);
447 if (aFixedBSize
> 0) {
448 SetFixedBSize(aFixedBSize
);
455 void nsTableRowFrame::UpdateBSize(nscoord aBSize
, nscoord aAscent
,
456 nscoord aDescent
, nsTableFrame
* aTableFrame
,
457 nsTableCellFrame
* aCellFrame
) {
458 if (!aTableFrame
|| !aCellFrame
) {
459 NS_ASSERTION(false, "invalid call");
463 if (aBSize
!= NS_UNCONSTRAINEDSIZE
) {
464 if (!(aCellFrame
->HasVerticalAlignBaseline())) { // only the cell's height
466 if (GetInitialBSize() < aBSize
) {
467 int32_t rowSpan
= aTableFrame
->GetEffectiveRowSpan(*aCellFrame
);
469 SetContentBSize(aBSize
);
472 } else { // the alignment on the baseline can change the bsize
473 NS_ASSERTION((aAscent
!= NS_UNCONSTRAINEDSIZE
) &&
474 (aDescent
!= NS_UNCONSTRAINEDSIZE
),
476 // see if this is a long ascender
477 if (mMaxCellAscent
< aAscent
) {
478 mMaxCellAscent
= aAscent
;
480 // see if this is a long descender and without rowspan
481 if (mMaxCellDescent
< aDescent
) {
482 int32_t rowSpan
= aTableFrame
->GetEffectiveRowSpan(*aCellFrame
);
484 mMaxCellDescent
= aDescent
;
487 // keep the tallest bsize in sync
488 if (GetInitialBSize() < mMaxCellAscent
+ mMaxCellDescent
) {
489 SetContentBSize(mMaxCellAscent
+ mMaxCellDescent
);
495 nscoord
nsTableRowFrame::CalcBSize(const ReflowInput
& aReflowInput
) {
496 nsTableFrame
* tableFrame
= GetTableFrame();
497 nscoord computedBSize
= (NS_UNCONSTRAINEDSIZE
== aReflowInput
.ComputedBSize())
499 : aReflowInput
.ComputedBSize();
500 ResetBSize(computedBSize
);
502 WritingMode wm
= aReflowInput
.GetWritingMode();
503 const nsStylePosition
* position
= StylePosition();
504 const auto& bsizeStyleCoord
= position
->BSize(wm
);
505 if (bsizeStyleCoord
.ConvertsToLength()) {
506 SetFixedBSize(bsizeStyleCoord
.ToLength());
507 } else if (bsizeStyleCoord
.ConvertsToPercentage()) {
508 SetPctBSize(bsizeStyleCoord
.ToPercentage());
511 for (nsIFrame
* kidFrame
: mFrames
) {
512 nsTableCellFrame
* cellFrame
= do_QueryFrame(kidFrame
);
514 MOZ_ASSERT(cellFrame
->GetWritingMode() == wm
);
515 LogicalSize desSize
= cellFrame
->GetDesiredSize();
516 if ((NS_UNCONSTRAINEDSIZE
== aReflowInput
.AvailableBSize()) &&
518 CalculateCellActualBSize(cellFrame
, desSize
.BSize(wm
), wm
);
520 // bsize may have changed, adjust descent to absorb any excess difference
522 if (!kidFrame
->PrincipalChildList()
524 ->PrincipalChildList()
526 ascent
= desSize
.BSize(wm
);
528 ascent
= cellFrame
->GetCellBaseline();
529 nscoord descent
= desSize
.BSize(wm
) - ascent
;
530 UpdateBSize(desSize
.BSize(wm
), ascent
, descent
, tableFrame
, cellFrame
);
533 return GetInitialBSize();
536 void nsTableRowFrame::PaintCellBackgroundsForFrame(
537 nsIFrame
* aFrame
, nsDisplayListBuilder
* aBuilder
,
538 const nsDisplayListSet
& aLists
, const nsPoint
& aOffset
) {
539 // Compute background rect by iterating all cell frame.
540 const nsPoint toReferenceFrame
= aBuilder
->ToReferenceFrame(aFrame
);
541 for (nsTableCellFrame
* cell
= GetFirstCell(); cell
;
542 cell
= cell
->GetNextCell()) {
543 if (!cell
->ShouldPaintBackground(aBuilder
)) {
548 cell
->GetRectRelativeToSelf() + cell
->GetNormalPosition() + aOffset
;
549 if (!aBuilder
->GetDirtyRect().Intersects(cellRect
)) {
552 cellRect
+= toReferenceFrame
;
553 nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
554 aBuilder
, aFrame
, cellRect
, aLists
.BorderBackground(), true, nullptr,
555 aFrame
->GetRectRelativeToSelf() + toReferenceFrame
, cell
);
559 void nsTableRowFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
560 const nsDisplayListSet
& aLists
) {
561 DisplayOutsetBoxShadow(aBuilder
, aLists
.BorderBackground());
563 PaintCellBackgroundsForFrame(this, aBuilder
, aLists
);
565 DisplayInsetBoxShadow(aBuilder
, aLists
.BorderBackground());
567 DisplayOutline(aBuilder
, aLists
);
569 for (nsIFrame
* kid
: PrincipalChildList()) {
570 BuildDisplayListForChild(aBuilder
, kid
, aLists
);
574 LogicalSides
nsTableRowFrame::GetLogicalSkipSides() const {
575 LogicalSides
skip(mWritingMode
);
576 if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak
==
577 StyleBoxDecorationBreak::Clone
)) {
581 if (GetPrevInFlow()) {
582 skip
|= eLogicalSideBitsBStart
;
584 if (GetNextInFlow()) {
585 skip
|= eLogicalSideBitsBEnd
;
590 // Calculate the cell's actual bsize given its pass2 bsize.
591 // Takes into account the specified bsize (in the style).
592 // Modifies the desired bsize that is passed in.
593 nsresult
nsTableRowFrame::CalculateCellActualBSize(nsTableCellFrame
* aCellFrame
,
594 nscoord
& aDesiredBSize
,
596 nscoord specifiedBSize
= 0;
598 // Get the bsize specified in the style information
599 const nsStylePosition
* position
= aCellFrame
->StylePosition();
601 int32_t rowSpan
= GetTableFrame()->GetEffectiveRowSpan(*aCellFrame
);
603 const auto& bsizeStyleCoord
= position
->BSize(aWM
);
604 if (bsizeStyleCoord
.ConvertsToLength()) {
605 // In quirks mode, table cell isize should be content-box, but bsize
606 // should be border-box.
607 // Because of this historic anomaly, we do not use quirk.css
608 // (since we can't specify one value of box-sizing for isize and another
610 specifiedBSize
= bsizeStyleCoord
.ToLength();
611 if (PresContext()->CompatibilityMode() != eCompatibility_NavQuirks
&&
612 position
->mBoxSizing
== StyleBoxSizing::Content
) {
614 aCellFrame
->GetLogicalUsedBorderAndPadding(aWM
).BStartEnd(aWM
);
618 SetFixedBSize(specifiedBSize
);
620 } else if (bsizeStyleCoord
.ConvertsToPercentage()) {
622 SetPctBSize(bsizeStyleCoord
.ToPercentage());
626 // If the specified bsize is greater than the desired bsize,
627 // then use the specified bsize
628 if (specifiedBSize
> aDesiredBSize
) {
629 aDesiredBSize
= specifiedBSize
;
635 // Calculates the available isize for the table cell based on the known
636 // column isizes taking into account column spans and column spacing
637 static nscoord
CalcAvailISize(nsTableFrame
& aTableFrame
,
638 nsTableCellFrame
& aCellFrame
) {
639 nscoord cellAvailISize
= 0;
640 uint32_t colIndex
= aCellFrame
.ColIndex();
641 int32_t colspan
= aTableFrame
.GetEffectiveColSpan(aCellFrame
);
642 NS_ASSERTION(colspan
> 0, "effective colspan should be positive");
643 nsTableFrame
* fifTable
=
644 static_cast<nsTableFrame
*>(aTableFrame
.FirstInFlow());
646 for (int32_t spanX
= 0; spanX
< colspan
; spanX
++) {
647 cellAvailISize
+= fifTable
->GetColumnISizeFromFirstInFlow(colIndex
+ spanX
);
648 if (spanX
> 0 && aTableFrame
.ColumnHasCellSpacingBefore(colIndex
+ spanX
)) {
649 cellAvailISize
+= aTableFrame
.GetColSpacing(colIndex
+ spanX
- 1);
652 return cellAvailISize
;
655 static nscoord
GetSpaceBetween(int32_t aPrevColIndex
, int32_t aColIndex
,
656 int32_t aColSpan
, nsTableFrame
& aTableFrame
,
657 bool aCheckVisibility
) {
660 nsTableFrame
* fifTable
=
661 static_cast<nsTableFrame
*>(aTableFrame
.FirstInFlow());
662 for (colIdx
= aPrevColIndex
+ 1; aColIndex
> colIdx
; colIdx
++) {
663 bool isCollapsed
= false;
664 if (!aCheckVisibility
) {
665 space
+= fifTable
->GetColumnISizeFromFirstInFlow(colIdx
);
667 nsTableColFrame
* colFrame
= aTableFrame
.GetColFrame(colIdx
);
668 const nsStyleVisibility
* colVis
= colFrame
->StyleVisibility();
669 bool collapseCol
= StyleVisibility::Collapse
== colVis
->mVisible
;
670 nsIFrame
* cgFrame
= colFrame
->GetParent();
671 const nsStyleVisibility
* groupVis
= cgFrame
->StyleVisibility();
672 bool collapseGroup
= StyleVisibility::Collapse
== groupVis
->mVisible
;
673 isCollapsed
= collapseCol
|| collapseGroup
;
675 space
+= fifTable
->GetColumnISizeFromFirstInFlow(colIdx
);
677 if (!isCollapsed
&& aTableFrame
.ColumnHasCellSpacingBefore(colIdx
)) {
678 space
+= aTableFrame
.GetColSpacing(colIdx
- 1);
684 // subtract the bsizes of aRow's prev in flows from the unpaginated bsize
685 static nscoord
CalcBSizeFromUnpaginatedBSize(nsTableRowFrame
& aRow
,
688 nsTableRowFrame
* firstInFlow
=
689 static_cast<nsTableRowFrame
*>(aRow
.FirstInFlow());
690 if (firstInFlow
->HasUnpaginatedBSize()) {
691 bsize
= firstInFlow
->GetUnpaginatedBSize();
692 for (nsIFrame
* prevInFlow
= aRow
.GetPrevInFlow(); prevInFlow
;
693 prevInFlow
= prevInFlow
->GetPrevInFlow()) {
694 bsize
-= prevInFlow
->BSize(aWM
);
697 return std::max(bsize
, 0);
700 void nsTableRowFrame::ReflowChildren(nsPresContext
* aPresContext
,
701 ReflowOutput
& aDesiredSize
,
702 const ReflowInput
& aReflowInput
,
703 nsTableFrame
& aTableFrame
,
704 nsReflowStatus
& aStatus
) {
707 // XXXldb Should we be checking constrained bsize instead?
708 const bool isPaginated
= aPresContext
->IsPaginated();
709 const bool borderCollapse
= aTableFrame
.IsBorderCollapse();
711 int32_t cellColSpan
=
712 1; // must be defined here so it's set properly for non-cell kids
714 // remember the col index of the previous cell to handle rowspans into this
716 int32_t prevColIndex
= -1;
717 nscoord iCoord
= 0; // running total of children inline-coord offset
719 // This computes the max of all cell bsizes
720 nscoord cellMaxBSize
= 0;
722 // Reflow each of our existing cell frames
723 WritingMode wm
= aReflowInput
.GetWritingMode();
724 nsSize containerSize
= aReflowInput
.ComputedSizeAsContainerIfConstrained();
726 for (nsIFrame
* kidFrame
: mFrames
) {
727 nsTableCellFrame
* cellFrame
= do_QueryFrame(kidFrame
);
729 // XXXldb nsCSSFrameConstructor needs to enforce this!
730 MOZ_ASSERT_UNREACHABLE("yikes, a non-row child");
732 // it's an unknown frame type, give it a generic reflow and ignore the
734 TableCellReflowInput
kidReflowInput(
735 aPresContext
, aReflowInput
, kidFrame
,
736 LogicalSize(kidFrame
->GetWritingMode(), 0, 0),
737 ReflowInput::InitFlag::CallerWillInit
);
738 InitChildReflowInput(*aPresContext
, LogicalSize(wm
), false,
740 ReflowOutput
desiredSize(aReflowInput
);
741 nsReflowStatus status
;
742 ReflowChild(kidFrame
, aPresContext
, desiredSize
, kidReflowInput
, 0, 0,
743 ReflowChildFlags::Default
, status
);
744 kidFrame
->DidReflow(aPresContext
, nullptr);
749 // See if we should only reflow the dirty child frames
750 bool doReflowChild
= true;
751 if (!aReflowInput
.ShouldReflowAllKids() && !aTableFrame
.IsGeometryDirty() &&
752 !kidFrame
->IsSubtreeDirty()) {
753 if (!aReflowInput
.mFlags
.mSpecialBSizeReflow
) doReflowChild
= false;
754 } else if ((NS_UNCONSTRAINEDSIZE
!= aReflowInput
.AvailableBSize())) {
755 // We don't reflow a rowspan >1 cell here with a constrained bsize.
756 // That happens in nsTableRowGroupFrame::SplitSpanningCells.
757 if (aTableFrame
.GetEffectiveRowSpan(*cellFrame
) > 1) {
758 doReflowChild
= false;
761 if (aReflowInput
.mFlags
.mSpecialBSizeReflow
) {
763 !cellFrame
->HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE
)) {
768 uint32_t cellColIndex
= cellFrame
->ColIndex();
769 cellColSpan
= aTableFrame
.GetEffectiveColSpan(*cellFrame
);
771 // If the adjacent cell is in a prior row (because of a rowspan) add in the
772 // space NOTE: prevColIndex can be -1 here.
773 if (prevColIndex
!= (static_cast<int32_t>(cellColIndex
) - 1)) {
774 iCoord
+= GetSpaceBetween(prevColIndex
, cellColIndex
, cellColSpan
,
778 // remember the rightmost (ltr) or leftmost (rtl) column this cell spans
780 prevColIndex
= cellColIndex
+ (cellColSpan
- 1);
782 // Reflow the child frame
783 nsRect kidRect
= kidFrame
->GetRect();
784 LogicalPoint origKidNormalPosition
=
785 kidFrame
->GetLogicalNormalPosition(wm
, containerSize
);
786 // All cells' no-relative-positioning position should be snapped to the
787 // row's bstart edge.
788 // This doesn't hold in vertical-rl mode, where we don't yet know the
789 // correct containerSize for the row frame. In that case, we'll have to
790 // fix up child positions later, after determining our desiredSize.
791 NS_ASSERTION(origKidNormalPosition
.B(wm
) == 0 || wm
.IsVerticalRL(),
792 "unexpected kid position");
794 nsRect kidInkOverflow
= kidFrame
->InkOverflowRect();
795 LogicalPoint
kidPosition(wm
, iCoord
, 0);
796 bool firstReflow
= kidFrame
->HasAnyStateBits(NS_FRAME_FIRST_REFLOW
);
799 // Calculate the available isize for the table cell using the known
801 nscoord availCellISize
= CalcAvailISize(aTableFrame
, *cellFrame
);
803 Maybe
<TableCellReflowInput
> kidReflowInput
;
804 ReflowOutput
desiredSize(aReflowInput
);
806 // If the avail isize is not the same as last time we reflowed the cell or
807 // the cell wants to be bigger than what was available last time or
808 // it is a style change reflow or we are printing, then we must reflow the
809 // cell. Otherwise we can skip the reflow.
810 // XXXldb Why is this condition distinct from doReflowChild above?
811 WritingMode wm
= aReflowInput
.GetWritingMode();
812 NS_ASSERTION(cellFrame
->GetWritingMode() == wm
,
813 "expected consistent writing-mode within table");
814 LogicalSize cellDesiredSize
= cellFrame
->GetDesiredSize();
815 if ((availCellISize
!= cellFrame
->GetPriorAvailISize()) ||
816 (cellDesiredSize
.ISize(wm
) > cellFrame
->GetPriorAvailISize()) ||
817 HasAnyStateBits(NS_FRAME_IS_DIRTY
) || isPaginated
||
818 cellFrame
->IsSubtreeDirty() ||
819 // See if it needs a special reflow, or if it had one that we need to
821 cellFrame
->HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE
) ||
823 // Reflow the cell to fit the available isize, bsize
824 // XXX The old IR_ChildIsDirty code used availCellISize here.
825 LogicalSize
kidAvailSize(wm
, availCellISize
,
826 aReflowInput
.AvailableBSize());
829 kidReflowInput
.emplace(aPresContext
, aReflowInput
, kidFrame
,
831 ReflowInput::InitFlag::CallerWillInit
);
832 InitChildReflowInput(*aPresContext
, kidAvailSize
, borderCollapse
,
835 nsReflowStatus status
;
836 ReflowChild(kidFrame
, aPresContext
, desiredSize
, *kidReflowInput
, wm
,
837 kidPosition
, containerSize
, ReflowChildFlags::Default
,
840 // allow the table to determine if/how the table needs to be rebalanced
841 // If any of the cells are not complete, then we're not complete
842 if (status
.IsIncomplete()) {
844 aStatus
.SetIncomplete();
847 if (iCoord
!= origKidNormalPosition
.I(wm
)) {
848 kidFrame
->InvalidateFrameSubtree();
851 desiredSize
.SetSize(wm
, cellDesiredSize
);
852 desiredSize
.mOverflowAreas
= cellFrame
->GetOverflowAreas();
854 // if we are in a floated table, our position is not yet established, so
855 // we cannot reposition our views the containing block will do this for
856 // us after positioning the table
857 if (!aTableFrame
.IsFloating()) {
858 // Because we may have moved the frame we need to make sure any views
859 // are positioned properly. We have to do this, because any one of our
860 // parent frames could have moved and we have no way of knowing...
861 nsTableFrame::RePositionViews(kidFrame
);
865 if (NS_UNCONSTRAINEDSIZE
== aReflowInput
.AvailableBSize()) {
866 if (!GetPrevInFlow()) {
867 // Calculate the cell's actual bsize given its pass2 bsize. This
868 // function takes into account the specified bsize (in the style)
869 CalculateCellActualBSize(cellFrame
, desiredSize
.BSize(wm
), wm
);
871 // bsize may have changed, adjust descent to absorb any excess
874 if (!kidFrame
->PrincipalChildList()
876 ->PrincipalChildList()
878 ascent
= desiredSize
.BSize(wm
);
880 ascent
= ((nsTableCellFrame
*)kidFrame
)->GetCellBaseline();
882 nscoord descent
= desiredSize
.BSize(wm
) - ascent
;
883 UpdateBSize(desiredSize
.BSize(wm
), ascent
, descent
, &aTableFrame
,
886 cellMaxBSize
= std::max(cellMaxBSize
, desiredSize
.BSize(wm
));
888 aTableFrame
.GetEffectiveRowSpan((nsTableCellFrame
&)*kidFrame
);
890 SetContentBSize(cellMaxBSize
);
895 desiredSize
.ISize(wm
) = availCellISize
;
897 ReflowChildFlags flags
= ReflowChildFlags::Default
;
899 if (kidReflowInput
) {
900 // We reflowed. Apply relative positioning in the normal way.
901 flags
= ReflowChildFlags::ApplyRelativePositioning
;
902 } else if (kidFrame
->IsRelativelyOrStickyPositioned()) {
903 // We didn't reflow. Do the positioning part of what
904 // MovePositionBy does internally. (This codepath should really
905 // be merged into the else below if we can.)
906 nsMargin
* computedOffsetProp
=
907 kidFrame
->GetProperty(nsIFrame::ComputedOffsetProperty());
909 // On our fist reflow sticky children may not have the property yet (we
910 // need to reflow the children first to size the scroll frame).
911 LogicalMargin
computedOffsets(
912 wm
, computedOffsetProp
? *computedOffsetProp
: nsMargin());
913 ReflowInput::ApplyRelativePositioning(kidFrame
, wm
, computedOffsets
,
914 &kidPosition
, containerSize
);
917 // In vertical-rl mode, we are likely to have containerSize.width = 0
918 // because ComputedWidth() was NS_UNCONSTRAINEDSIZE.
919 // For cases where that's wrong, we will fix up the position later.
920 FinishReflowChild(kidFrame
, aPresContext
, desiredSize
,
921 kidReflowInput
.ptrOr(nullptr), wm
, kidPosition
,
922 containerSize
, flags
);
924 nsTableFrame
* tableFrame
= GetTableFrame();
925 if (tableFrame
->IsBorderCollapse()) {
926 nsTableFrame::InvalidateTableFrame(kidFrame
, kidRect
, kidInkOverflow
,
930 iCoord
+= desiredSize
.ISize(wm
);
932 if (iCoord
!= origKidNormalPosition
.I(wm
)) {
933 // Invalidate the old position
934 kidFrame
->InvalidateFrameSubtree();
935 // Move to the new position. As above, we need to account for relative
937 kidFrame
->MovePositionBy(
938 wm
, LogicalPoint(wm
, iCoord
- origKidNormalPosition
.I(wm
), 0));
939 nsTableFrame::RePositionViews(kidFrame
);
940 // invalidate the new position
941 kidFrame
->InvalidateFrameSubtree();
943 // we need to account for the cell's isize even if it isn't reflowed
944 iCoord
+= kidFrame
->ISize(wm
);
946 if (kidFrame
->GetNextInFlow()) {
948 aStatus
.SetIncomplete();
951 ConsiderChildOverflow(aDesiredSize
.mOverflowAreas
, kidFrame
);
952 iCoord
+= aTableFrame
.GetColSpacing(cellColIndex
);
955 // Just set our isize to what was available.
956 // The table will calculate the isize and not use our value.
957 aDesiredSize
.ISize(wm
) = aReflowInput
.AvailableISize();
959 if (aReflowInput
.mFlags
.mSpecialBSizeReflow
) {
960 aDesiredSize
.BSize(wm
) = BSize(wm
);
961 } else if (NS_UNCONSTRAINEDSIZE
== aReflowInput
.AvailableBSize()) {
962 aDesiredSize
.BSize(wm
) = CalcBSize(aReflowInput
);
963 if (GetPrevInFlow()) {
964 nscoord bsize
= CalcBSizeFromUnpaginatedBSize(*this, wm
);
965 aDesiredSize
.BSize(wm
) = std::max(aDesiredSize
.BSize(wm
), bsize
);
967 if (isPaginated
&& HasStyleBSize()) {
968 // set the unpaginated bsize so next in flows can try to honor it
969 SetHasUnpaginatedBSize(true);
970 SetUnpaginatedBSize(aPresContext
, aDesiredSize
.BSize(wm
));
972 if (isPaginated
&& HasUnpaginatedBSize()) {
973 aDesiredSize
.BSize(wm
) =
974 std::max(aDesiredSize
.BSize(wm
), GetUnpaginatedBSize());
977 } else { // constrained bsize, paginated
978 // Compute the bsize we should have from style (subtracting the
979 // bsize from our prev-in-flows from the style bsize)
980 nscoord styleBSize
= CalcBSizeFromUnpaginatedBSize(*this, wm
);
981 if (styleBSize
> aReflowInput
.AvailableBSize()) {
982 styleBSize
= aReflowInput
.AvailableBSize();
983 aStatus
.SetIncomplete();
985 aDesiredSize
.BSize(wm
) = std::max(cellMaxBSize
, styleBSize
);
988 if (wm
.IsVerticalRL()) {
989 // Any children whose width was not the same as our final
990 // aDesiredSize.BSize will have been misplaced earlier at the
991 // FinishReflowChild stage. So fix them up now.
992 for (nsIFrame
* kidFrame
: mFrames
) {
993 nsTableCellFrame
* cellFrame
= do_QueryFrame(kidFrame
);
997 if (kidFrame
->BSize(wm
) != aDesiredSize
.BSize(wm
)) {
998 kidFrame
->MovePositionBy(
1000 LogicalPoint(wm
, 0, kidFrame
->BSize(wm
) - aDesiredSize
.BSize(wm
)));
1001 nsTableFrame::RePositionViews(kidFrame
);
1002 // Do we need to InvalidateFrameSubtree() here?
1007 aDesiredSize
.UnionOverflowAreasWithDesiredBounds();
1008 FinishAndStoreOverflow(&aDesiredSize
);
1011 /** Layout the entire row.
1012 * This method stacks cells in the inline dir according to HTML 4.0 rules.
1014 void nsTableRowFrame::Reflow(nsPresContext
* aPresContext
,
1015 ReflowOutput
& aDesiredSize
,
1016 const ReflowInput
& aReflowInput
,
1017 nsReflowStatus
& aStatus
) {
1019 DO_GLOBAL_REFLOW_COUNT("nsTableRowFrame");
1020 DISPLAY_REFLOW(aPresContext
, this, aReflowInput
, aDesiredSize
, aStatus
);
1021 MOZ_ASSERT(aStatus
.IsEmpty(), "Caller should pass a fresh reflow status!");
1023 WritingMode wm
= aReflowInput
.GetWritingMode();
1025 nsTableFrame
* tableFrame
= GetTableFrame();
1026 const nsStyleVisibility
* rowVis
= StyleVisibility();
1027 bool collapseRow
= StyleVisibility::Collapse
== rowVis
->mVisible
;
1029 tableFrame
->SetNeedToCollapse(true);
1032 // see if a special bsize reflow needs to occur due to having a pct bsize
1033 nsTableFrame::CheckRequestSpecialBSizeReflow(aReflowInput
);
1035 // See if we have a cell with specified/pct bsize
1036 InitHasCellWithStyleBSize(tableFrame
);
1038 ReflowChildren(aPresContext
, aDesiredSize
, aReflowInput
, *tableFrame
,
1041 if (aPresContext
->IsPaginated() && !aStatus
.IsFullyComplete() &&
1042 ShouldAvoidBreakInside(aReflowInput
)) {
1043 aStatus
.SetInlineLineBreakBeforeAndReset();
1046 // Just set our isize to what was available.
1047 // The table will calculate the isize and not use our value.
1048 aDesiredSize
.ISize(wm
) = aReflowInput
.AvailableISize();
1050 // If our parent is in initial reflow, it'll handle invalidating our
1051 // entire overflow rect.
1052 if (!GetParent()->HasAnyStateBits(NS_FRAME_FIRST_REFLOW
) &&
1053 nsSize(aDesiredSize
.Width(), aDesiredSize
.Height()) != mRect
.Size()) {
1057 // Any absolutely-positioned children will get reflowed in
1058 // nsIFrame::FixupPositionedTableParts in another pass, so propagate our
1059 // dirtiness to them before our parent clears our dirty bits.
1060 PushDirtyBitToAbsoluteFrames();
1062 NS_FRAME_SET_TRUNCATION(aStatus
, aReflowInput
, aDesiredSize
);
1066 * This function is called by the row group frame's SplitRowGroup() code when
1067 * pushing a row frame that has cell frames that span into it. The cell frame
1068 * should be reflowed with the specified height
1070 nscoord
nsTableRowFrame::ReflowCellFrame(nsPresContext
* aPresContext
,
1071 const ReflowInput
& aReflowInput
,
1073 nsTableCellFrame
* aCellFrame
,
1074 nscoord aAvailableBSize
,
1075 nsReflowStatus
& aStatus
) {
1076 WritingMode wm
= aReflowInput
.GetWritingMode();
1078 // Reflow the cell frame with the specified height. Use the existing width
1079 nsSize containerSize
= aCellFrame
->GetSize();
1080 LogicalRect cellRect
= aCellFrame
->GetLogicalRect(wm
, containerSize
);
1081 nsRect cellInkOverflow
= aCellFrame
->InkOverflowRect();
1083 LogicalSize cellSize
= cellRect
.Size(wm
);
1084 LogicalSize
availSize(wm
, cellRect
.ISize(wm
), aAvailableBSize
);
1085 bool borderCollapse
= GetTableFrame()->IsBorderCollapse();
1086 NS_ASSERTION(aCellFrame
->GetWritingMode() == wm
,
1087 "expected consistent writing-mode within table");
1088 TableCellReflowInput
cellReflowInput(aPresContext
, aReflowInput
, aCellFrame
,
1090 ReflowInput::InitFlag::CallerWillInit
);
1091 InitChildReflowInput(*aPresContext
, availSize
, borderCollapse
,
1093 cellReflowInput
.mFlags
.mIsTopOfPage
= aIsTopOfPage
;
1095 ReflowOutput
desiredSize(aReflowInput
);
1097 ReflowChild(aCellFrame
, aPresContext
, desiredSize
, cellReflowInput
, 0, 0,
1098 ReflowChildFlags::NoMoveFrame
, aStatus
);
1099 bool fullyComplete
= aStatus
.IsComplete() && !aStatus
.IsTruncated();
1100 if (fullyComplete
) {
1101 desiredSize
.BSize(wm
) = aAvailableBSize
;
1103 aCellFrame
->SetSize(
1104 wm
, LogicalSize(wm
, cellSize
.ISize(wm
), desiredSize
.BSize(wm
)));
1106 // Note: BlockDirAlignChild can affect the overflow rect.
1107 // XXX What happens if this cell has 'vertical-align: baseline' ?
1108 // XXX Why is it assumed that the cell's ascent hasn't changed ?
1109 if (fullyComplete
) {
1110 aCellFrame
->BlockDirAlignChild(wm
, mMaxCellAscent
);
1113 nsTableFrame::InvalidateTableFrame(
1114 aCellFrame
, cellRect
.GetPhysicalRect(wm
, containerSize
), cellInkOverflow
,
1115 aCellFrame
->HasAnyStateBits(NS_FRAME_FIRST_REFLOW
));
1117 aCellFrame
->DidReflow(aPresContext
, nullptr);
1119 return desiredSize
.BSize(wm
);
1122 nscoord
nsTableRowFrame::CollapseRowIfNecessary(nscoord aRowOffset
,
1124 bool aCollapseGroup
,
1125 bool& aDidCollapse
) {
1126 const nsStyleVisibility
* rowVis
= StyleVisibility();
1127 bool collapseRow
= StyleVisibility::Collapse
== rowVis
->mVisible
;
1128 nsTableFrame
* tableFrame
=
1129 static_cast<nsTableFrame
*>(GetTableFrame()->FirstInFlow());
1131 tableFrame
->SetNeedToCollapse(true);
1134 if (aRowOffset
!= 0) {
1135 // We're moving, so invalidate our old position
1136 InvalidateFrameSubtree();
1139 WritingMode wm
= GetWritingMode();
1141 nsSize parentSize
= GetParent()->GetSize();
1142 LogicalRect rowRect
= GetLogicalRect(wm
, parentSize
);
1143 nsRect oldRect
= mRect
;
1144 nsRect oldInkOverflow
= InkOverflowRect();
1146 rowRect
.BStart(wm
) -= aRowOffset
;
1147 rowRect
.ISize(wm
) = aISize
;
1148 OverflowAreas overflow
;
1150 nsSize containerSize
= mRect
.Size();
1152 if (aCollapseGroup
|| collapseRow
) {
1153 aDidCollapse
= true;
1154 shift
= rowRect
.BSize(wm
);
1155 nsTableCellFrame
* cellFrame
= GetFirstCell();
1157 uint32_t rowIndex
= cellFrame
->RowIndex();
1158 shift
+= tableFrame
->GetRowSpacing(rowIndex
);
1160 LogicalRect cRect
= cellFrame
->GetLogicalRect(wm
, containerSize
);
1161 // If aRowOffset != 0, there's no point in invalidating the cells, since
1162 // we've already invalidated our overflow area. Note that we _do_ still
1163 // need to invalidate if our row is not moving, because the cell might
1164 // span out of this row, so invalidating our row rect won't do enough.
1165 if (aRowOffset
== 0) {
1168 cRect
.BSize(wm
) = 0;
1169 cellFrame
->SetRect(wm
, cRect
, containerSize
);
1170 cellFrame
= cellFrame
->GetNextCell();
1173 shift
+= tableFrame
->GetRowSpacing(GetRowIndex());
1175 rowRect
.BSize(wm
) = 0;
1176 } else { // row is not collapsed
1177 // remember the col index of the previous cell to handle rowspans into this
1179 int32_t prevColIndex
= -1;
1180 nscoord iPos
= 0; // running total of children inline-axis offset
1181 nsTableFrame
* fifTable
=
1182 static_cast<nsTableFrame
*>(tableFrame
->FirstInFlow());
1184 for (nsIFrame
* kidFrame
: mFrames
) {
1185 nsTableCellFrame
* cellFrame
= do_QueryFrame(kidFrame
);
1187 uint32_t cellColIndex
= cellFrame
->ColIndex();
1188 int32_t cellColSpan
= tableFrame
->GetEffectiveColSpan(*cellFrame
);
1190 // If the adjacent cell is in a prior row (because of a rowspan) add in
1192 // NOTE: prevColIndex can be -1 here.
1193 if (prevColIndex
!= (static_cast<int32_t>(cellColIndex
) - 1)) {
1194 iPos
+= GetSpaceBetween(prevColIndex
, cellColIndex
, cellColSpan
,
1197 LogicalRect
cRect(wm
, iPos
, 0, 0, rowRect
.BSize(wm
));
1199 // remember the last (iend-wards-most) column this cell spans into
1200 prevColIndex
= cellColIndex
+ cellColSpan
- 1;
1201 int32_t actualColSpan
= cellColSpan
;
1202 bool isVisible
= false;
1203 for (int32_t colIdx
= cellColIndex
; actualColSpan
> 0;
1204 colIdx
++, actualColSpan
--) {
1205 nsTableColFrame
* colFrame
= tableFrame
->GetColFrame(colIdx
);
1206 const nsStyleVisibility
* colVis
= colFrame
->StyleVisibility();
1207 bool collapseCol
= StyleVisibility::Collapse
== colVis
->mVisible
;
1208 nsIFrame
* cgFrame
= colFrame
->GetParent();
1209 const nsStyleVisibility
* groupVis
= cgFrame
->StyleVisibility();
1210 bool collapseGroup
= StyleVisibility::Collapse
== groupVis
->mVisible
;
1211 bool isCollapsed
= collapseCol
|| collapseGroup
;
1213 cRect
.ISize(wm
) += fifTable
->GetColumnISizeFromFirstInFlow(colIdx
);
1215 if ((actualColSpan
> 1)) {
1216 nsTableColFrame
* nextColFrame
=
1217 tableFrame
->GetColFrame(colIdx
+ 1);
1218 const nsStyleVisibility
* nextColVis
=
1219 nextColFrame
->StyleVisibility();
1220 if (StyleVisibility::Collapse
!= nextColVis
->mVisible
&&
1221 tableFrame
->ColumnHasCellSpacingBefore(colIdx
+ 1)) {
1222 cRect
.ISize(wm
) += tableFrame
->GetColSpacing(cellColIndex
);
1227 iPos
+= cRect
.ISize(wm
);
1229 iPos
+= tableFrame
->GetColSpacing(cellColIndex
);
1231 int32_t actualRowSpan
= tableFrame
->GetEffectiveRowSpan(*cellFrame
);
1232 nsTableRowFrame
* rowFrame
= GetNextRow();
1233 for (actualRowSpan
--; actualRowSpan
> 0 && rowFrame
; actualRowSpan
--) {
1234 const nsStyleVisibility
* nextRowVis
= rowFrame
->StyleVisibility();
1235 bool collapseNextRow
=
1236 StyleVisibility::Collapse
== nextRowVis
->mVisible
;
1237 if (!collapseNextRow
) {
1238 LogicalRect nextRect
= rowFrame
->GetLogicalRect(wm
, containerSize
);
1240 nextRect
.BSize(wm
) +
1241 tableFrame
->GetRowSpacing(rowFrame
->GetRowIndex());
1243 rowFrame
= rowFrame
->GetNextRow();
1246 nsRect oldCellRect
= cellFrame
->GetRect();
1247 LogicalPoint oldCellNormalPos
=
1248 cellFrame
->GetLogicalNormalPosition(wm
, containerSize
);
1250 nsRect oldCellInkOverflow
= cellFrame
->InkOverflowRect();
1252 if (aRowOffset
== 0 && cRect
.Origin(wm
) != oldCellNormalPos
) {
1253 // We're moving the cell. Invalidate the old overflow area
1254 cellFrame
->InvalidateFrameSubtree();
1257 cellFrame
->MovePositionBy(wm
, cRect
.Origin(wm
) - oldCellNormalPos
);
1258 cellFrame
->SetSize(wm
, cRect
.Size(wm
));
1260 // XXXbz This looks completely bogus in the cases when we didn't
1261 // collapse the cell!
1262 LogicalRect
cellBounds(wm
, 0, 0, cRect
.ISize(wm
), cRect
.BSize(wm
));
1263 nsRect cellPhysicalBounds
=
1264 cellBounds
.GetPhysicalRect(wm
, containerSize
);
1265 OverflowAreas
cellOverflow(cellPhysicalBounds
, cellPhysicalBounds
);
1266 cellFrame
->FinishAndStoreOverflow(cellOverflow
,
1267 cRect
.Size(wm
).GetPhysicalSize(wm
));
1268 nsTableFrame::RePositionViews(cellFrame
);
1269 ConsiderChildOverflow(overflow
, cellFrame
);
1271 if (aRowOffset
== 0) {
1272 nsTableFrame::InvalidateTableFrame(cellFrame
, oldCellRect
,
1273 oldCellInkOverflow
, false);
1279 SetRect(wm
, rowRect
, containerSize
);
1280 overflow
.UnionAllWith(nsRect(0, 0, rowRect
.Width(wm
), rowRect
.Height(wm
)));
1281 FinishAndStoreOverflow(overflow
, rowRect
.Size(wm
).GetPhysicalSize(wm
));
1283 nsTableFrame::RePositionViews(this);
1284 nsTableFrame::InvalidateTableFrame(this, oldRect
, oldInkOverflow
, false);
1289 * The following method is called by the row group frame's SplitRowGroup()
1290 * when it creates a continuing cell frame and wants to insert it into the
1293 void nsTableRowFrame::InsertCellFrame(nsTableCellFrame
* aFrame
,
1294 int32_t aColIndex
) {
1295 // Find the cell frame where col index < aColIndex
1296 nsTableCellFrame
* priorCell
= nullptr;
1297 for (nsIFrame
* child
: mFrames
) {
1298 nsTableCellFrame
* cellFrame
= do_QueryFrame(child
);
1300 uint32_t colIndex
= cellFrame
->ColIndex();
1301 // Can aColIndex be -1 here? Let's assume it can for now.
1302 if (static_cast<int32_t>(colIndex
) < aColIndex
) {
1303 priorCell
= cellFrame
;
1308 mFrames
.InsertFrame(this, priorCell
, aFrame
);
1311 nsTableRowFrame
* nsTableRowFrame::GetNextRow() const {
1312 nsIFrame
* childFrame
= GetNextSibling();
1313 while (childFrame
) {
1314 nsTableRowFrame
* rowFrame
= do_QueryFrame(childFrame
);
1316 NS_ASSERTION(mozilla::StyleDisplay::TableRow
==
1317 childFrame
->StyleDisplay()->mDisplay
,
1318 "wrong display type on rowframe");
1321 childFrame
= childFrame
->GetNextSibling();
1326 NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(RowUnpaginatedHeightProperty
, nscoord
)
1328 void nsTableRowFrame::SetUnpaginatedBSize(nsPresContext
* aPresContext
,
1330 NS_ASSERTION(!GetPrevInFlow(), "program error");
1332 SetProperty(RowUnpaginatedHeightProperty(), aValue
);
1335 nscoord
nsTableRowFrame::GetUnpaginatedBSize() {
1336 return GetProperty(RowUnpaginatedHeightProperty());
1339 void nsTableRowFrame::SetContinuousBCBorderWidth(LogicalSide aForSide
,
1340 BCPixelSize aPixelValue
) {
1342 case eLogicalSideIEnd
:
1343 mIEndContBorderWidth
= aPixelValue
;
1345 case eLogicalSideBStart
:
1346 mBStartContBorderWidth
= aPixelValue
;
1348 case eLogicalSideIStart
:
1349 mIStartContBorderWidth
= aPixelValue
;
1352 NS_ERROR("invalid LogicalSide arg");
1355 #ifdef ACCESSIBILITY
1356 a11y::AccType
nsTableRowFrame::AccessibleType() {
1357 return a11y::eHTMLTableRowType
;
1361 * Sets the NS_ROW_HAS_CELL_WITH_STYLE_BSIZE bit to indicate whether
1362 * this row has any cells that have non-auto-bsize. (Row-spanning
1363 * cells are ignored.)
1365 void nsTableRowFrame::InitHasCellWithStyleBSize(nsTableFrame
* aTableFrame
) {
1366 WritingMode wm
= GetWritingMode();
1368 for (nsIFrame
* kidFrame
: mFrames
) {
1369 nsTableCellFrame
* cellFrame
= do_QueryFrame(kidFrame
);
1371 MOZ_ASSERT_UNREACHABLE("Table row has a non-cell child.");
1374 // Ignore row-spanning cells
1375 const auto& cellBSize
= cellFrame
->StylePosition()->BSize(wm
);
1376 if (aTableFrame
->GetEffectiveRowSpan(*cellFrame
) == 1 &&
1377 !cellBSize
.IsAuto() &&
1378 /* calc() with both percentages and lengths treated like 'auto' */
1379 (cellBSize
.ConvertsToLength() || cellBSize
.ConvertsToPercentage())) {
1380 AddStateBits(NS_ROW_HAS_CELL_WITH_STYLE_BSIZE
);
1384 RemoveStateBits(NS_ROW_HAS_CELL_WITH_STYLE_BSIZE
);
1387 void nsTableRowFrame::InvalidateFrame(uint32_t aDisplayItemKey
,
1388 bool aRebuildDisplayItems
) {
1389 nsIFrame::InvalidateFrame(aDisplayItemKey
, aRebuildDisplayItems
);
1390 if (GetTableFrame()->IsBorderCollapse()) {
1391 const bool rebuild
= StaticPrefs::layout_display_list_retain_sc();
1392 GetParent()->InvalidateFrameWithRect(InkOverflowRect() + GetPosition(),
1393 aDisplayItemKey
, rebuild
);
1397 void nsTableRowFrame::InvalidateFrameWithRect(const nsRect
& aRect
,
1398 uint32_t aDisplayItemKey
,
1399 bool aRebuildDisplayItems
) {
1400 nsIFrame::InvalidateFrameWithRect(aRect
, aDisplayItemKey
,
1401 aRebuildDisplayItems
);
1402 // If we have filters applied that would affects our bounds, then
1403 // we get an inactive layer created and this is computed
1404 // within FrameLayerBuilder
1405 GetParent()->InvalidateFrameWithRect(aRect
+ GetPosition(), aDisplayItemKey
,
1406 aRebuildDisplayItems
);
1409 /* ----- global methods ----- */
1411 nsTableRowFrame
* NS_NewTableRowFrame(PresShell
* aPresShell
,
1412 ComputedStyle
* aStyle
) {
1413 return new (aPresShell
) nsTableRowFrame(aStyle
, aPresShell
->GetPresContext());
1416 NS_IMPL_FRAMEARENA_HELPERS(nsTableRowFrame
)
1418 #ifdef DEBUG_FRAME_DUMP
1419 nsresult
nsTableRowFrame::GetFrameName(nsAString
& aResult
) const {
1420 return MakeFrameName(u
"TableRow"_ns
, aResult
);