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::DestroyFrom(nsIFrame
* aDestructRoot
,
158 PostDestroyData
& aPostDestroyData
) {
159 nsTableFrame::MaybeUnregisterPositionedTablePart(this, aDestructRoot
);
160 nsContainerFrame::DestroyFrom(aDestructRoot
, aPostDestroyData
);
164 void nsTableRowFrame::DidSetComputedStyle(ComputedStyle
* aOldComputedStyle
) {
165 nsContainerFrame::DidSetComputedStyle(aOldComputedStyle
);
166 nsTableFrame::PositionedTablePartMaybeChanged(this, aOldComputedStyle
);
168 if (!aOldComputedStyle
) {
169 return; // avoid the following on init
173 if (nsAccessibilityService
* accService
= GetAccService()) {
174 // If a table row's background color is now different from
175 // the background color of its previous row, it is possible our
176 // table now has alternating row colors. This changes whether or not
177 // the table is classified as a layout table or data table.
178 // We invalidate on every background color change to avoid
179 // walking the tree in search of the nearest row.
180 if (StyleBackground()->BackgroundColor(this) !=
181 aOldComputedStyle
->StyleBackground()->BackgroundColor(
182 aOldComputedStyle
)) {
183 // We send a notification here to invalidate the a11y cache on the
184 // table so the next fetch of IsProbablyLayoutTable() is accurate.
185 accService
->TableLayoutGuessMaybeChanged(PresShell(), mContent
);
190 nsTableFrame
* tableFrame
= GetTableFrame();
191 if (tableFrame
->IsBorderCollapse() &&
192 tableFrame
->BCRecalcNeeded(aOldComputedStyle
, Style())) {
193 TableArea
damageArea(0, GetRowIndex(), tableFrame
->GetColCount(), 1);
194 tableFrame
->AddBCDamageArea(damageArea
);
198 void nsTableRowFrame::AppendFrames(ChildListID aListID
,
199 nsFrameList
&& aFrameList
) {
200 NS_ASSERTION(aListID
== FrameChildListID::Principal
, "unexpected child list");
202 DrainSelfOverflowList(); // ensure the last frame is in mFrames
203 const nsFrameList::Slice
& newCells
=
204 mFrames
.AppendFrames(nullptr, std::move(aFrameList
));
206 // Add the new cell frames to the table
207 nsTableFrame
* tableFrame
= GetTableFrame();
208 for (nsIFrame
* childFrame
: newCells
) {
209 NS_ASSERTION(childFrame
->IsTableCellFrame(),
210 "Not a table cell frame/pseudo frame construction failure");
211 tableFrame
->AppendCell(static_cast<nsTableCellFrame
&>(*childFrame
),
215 PresShell()->FrameNeedsReflow(this, IntrinsicDirty::FrameAndAncestors
,
216 NS_FRAME_HAS_DIRTY_CHILDREN
);
217 tableFrame
->SetGeometryDirty();
220 void nsTableRowFrame::InsertFrames(ChildListID aListID
, nsIFrame
* aPrevFrame
,
221 const nsLineList::iterator
* aPrevFrameLine
,
222 nsFrameList
&& aFrameList
) {
223 NS_ASSERTION(aListID
== FrameChildListID::Principal
, "unexpected child list");
224 NS_ASSERTION(!aPrevFrame
|| aPrevFrame
->GetParent() == this,
225 "inserting after sibling frame with different parent");
226 if (mFrames
.IsEmpty() || (aPrevFrame
&& !aPrevFrame
->GetNextSibling())) {
227 // This is actually an append (though our caller didn't figure that out),
228 // and our append codepath is both simpler/faster _and_ less buggy.
229 // https://bugzilla.mozilla.org/show_bug.cgi?id=1388898 tracks the bugginess
230 AppendFrames(aListID
, std::move(aFrameList
));
234 DrainSelfOverflowList(); // ensure aPrevFrame is in mFrames
235 // Insert Frames in the frame list
236 const nsFrameList::Slice
& newCells
=
237 mFrames
.InsertFrames(nullptr, aPrevFrame
, std::move(aFrameList
));
239 nsTableCellFrame
* prevCellFrame
=
240 static_cast<nsTableCellFrame
*>(nsTableFrame::GetFrameAtOrBefore(
241 this, aPrevFrame
, LayoutFrameType::TableCell
));
242 nsTArray
<nsTableCellFrame
*> cellChildren
;
243 for (nsIFrame
* childFrame
: newCells
) {
244 NS_ASSERTION(childFrame
->IsTableCellFrame(),
245 "Not a table cell frame/pseudo frame construction failure");
246 cellChildren
.AppendElement(static_cast<nsTableCellFrame
*>(childFrame
));
248 // insert the cells into the cell map
249 int32_t colIndex
= -1;
251 colIndex
= prevCellFrame
->ColIndex();
253 nsTableFrame
* tableFrame
= GetTableFrame();
254 tableFrame
->InsertCells(cellChildren
, GetRowIndex(), colIndex
);
256 PresShell()->FrameNeedsReflow(this, IntrinsicDirty::FrameAndAncestors
,
257 NS_FRAME_HAS_DIRTY_CHILDREN
);
258 tableFrame
->SetGeometryDirty();
261 void nsTableRowFrame::RemoveFrame(ChildListID aListID
, nsIFrame
* aOldFrame
) {
262 NS_ASSERTION(aListID
== FrameChildListID::Principal
, "unexpected child list");
264 MOZ_ASSERT((nsTableCellFrame
*)do_QueryFrame(aOldFrame
));
265 nsTableCellFrame
* 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(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 (nsIFrame
* childFrame
: mFrames
) {
323 nsTableCellFrame
* cellFrame
= do_QueryFrame(childFrame
);
325 nscoord cellBSize
= BSize(wm
) + GetBSizeOfRowsSpannedBelowFirst(
326 *cellFrame
, *tableFrame
, wm
);
328 // If the bsize for the cell has changed, we need to reset it;
329 // and in vertical-rl mode, we need to update the cell's block position
330 // to account for the containerSize, which may not have been known
331 // earlier, so we always apply it here.
332 LogicalSize cellSize
= cellFrame
->GetLogicalSize(wm
);
333 if (cellSize
.BSize(wm
) != cellBSize
|| wm
.IsVerticalRL()) {
334 nsRect cellOldRect
= cellFrame
->GetRect();
335 nsRect cellInkOverflow
= cellFrame
->InkOverflowRect();
337 if (wm
.IsVerticalRL()) {
338 // Get the old position of the cell, as we want to preserve its
339 // inline coordinate.
340 LogicalPoint oldPos
=
341 cellFrame
->GetLogicalPosition(wm
, containerSize
);
343 // The cell should normally be aligned with the row's block-start,
344 // so set the B component of the position to zero:
345 LogicalPoint
newPos(wm
, oldPos
.I(wm
), 0);
347 // ...unless relative positioning is in effect, in which case the
348 // cell may have been moved away from the row's block-start
349 if (cellFrame
->IsRelativelyOrStickyPositioned()) {
350 // Find out where the cell would have been without relative
352 LogicalPoint oldNormalPos
=
353 cellFrame
->GetLogicalNormalPosition(wm
, containerSize
);
354 // The difference (if any) between oldPos and oldNormalPos reflects
355 // relative positioning that was applied to the cell, and which we
356 // need to incorporate when resetting the position.
357 newPos
.B(wm
) = oldPos
.B(wm
) - oldNormalPos
.B(wm
);
360 if (oldPos
!= newPos
) {
361 cellFrame
->SetPosition(wm
, newPos
, containerSize
);
362 nsTableFrame::RePositionViews(cellFrame
);
366 cellSize
.BSize(wm
) = cellBSize
;
367 cellFrame
->SetSize(wm
, cellSize
);
369 nsTableFrame
* tableFrame
= GetTableFrame();
370 if (tableFrame
->IsBorderCollapse()) {
371 nsTableFrame::InvalidateTableFrame(cellFrame
, cellOldRect
,
372 cellInkOverflow
, false);
376 // realign cell content based on the new bsize. We might be able to
377 // skip this if the bsize didn't change... maybe. Hard to tell.
378 cellFrame
->BlockDirAlignChild(wm
, mMaxCellAscent
);
380 // Always store the overflow, even if the height didn't change, since
381 // we'll lose part of our overflow area otherwise.
382 ConsiderChildOverflow(desiredSize
.mOverflowAreas
, cellFrame
);
384 // Note that if the cell's *content* needs to change in response
385 // to this height, it will get a special bsize reflow.
388 FinishAndStoreOverflow(&desiredSize
);
390 nsContainerFrame::SyncFrameViewAfterReflow(PresContext(), this, GetView(),
391 desiredSize
.InkOverflow(),
392 ReflowChildFlags::Default
);
394 // Let our base class do the usual work
397 // returns max-ascent amongst all cells that have 'vertical-align: baseline'
398 // *including* cells with rowspans
399 nscoord
nsTableRowFrame::GetMaxCellAscent() const { return mMaxCellAscent
; }
401 nscoord
nsTableRowFrame::GetRowBaseline(WritingMode aWM
) {
402 if (mMaxCellAscent
) {
403 return mMaxCellAscent
;
406 // If we get here, we don't have a baseline on any of the cells in this row.
409 for (nsIFrame
* childFrame
: mFrames
) {
410 MOZ_ASSERT(childFrame
->IsTableCellFrame());
411 nscoord s
= Baseline::SynthesizeBOffsetFromContentBox(
412 childFrame
, aWM
, BaselineSharingGroup::First
);
413 ascent
= std::max(ascent
, s
);
418 nscoord
nsTableRowFrame::GetInitialBSize(nscoord aPctBasis
) const {
420 if ((aPctBasis
> 0) && HasPctBSize()) {
421 bsize
= NSToCoordRound(GetPctBSize() * (float)aPctBasis
);
423 if (HasFixedBSize()) {
424 bsize
= std::max(bsize
, GetFixedBSize());
426 return std::max(bsize
, GetContentBSize());
429 void nsTableRowFrame::ResetBSize(nscoord aFixedBSize
) {
430 SetHasFixedBSize(false);
431 SetHasPctBSize(false);
436 if (aFixedBSize
> 0) {
437 SetFixedBSize(aFixedBSize
);
444 void nsTableRowFrame::UpdateBSize(nscoord aBSize
, nscoord aAscent
,
445 nscoord aDescent
, nsTableFrame
* aTableFrame
,
446 nsTableCellFrame
* aCellFrame
) {
447 if (!aTableFrame
|| !aCellFrame
) {
448 NS_ASSERTION(false, "invalid call");
452 if (aBSize
!= NS_UNCONSTRAINEDSIZE
) {
453 if (!(aCellFrame
->HasVerticalAlignBaseline())) { // only the cell's height
455 if (GetInitialBSize() < aBSize
) {
456 int32_t rowSpan
= aTableFrame
->GetEffectiveRowSpan(*aCellFrame
);
458 SetContentBSize(aBSize
);
461 } else { // the alignment on the baseline can change the bsize
462 NS_ASSERTION((aAscent
!= NS_UNCONSTRAINEDSIZE
) &&
463 (aDescent
!= NS_UNCONSTRAINEDSIZE
),
465 // see if this is a long ascender
466 if (mMaxCellAscent
< aAscent
) {
467 mMaxCellAscent
= aAscent
;
469 // see if this is a long descender and without rowspan
470 if (mMaxCellDescent
< aDescent
) {
471 int32_t rowSpan
= aTableFrame
->GetEffectiveRowSpan(*aCellFrame
);
473 mMaxCellDescent
= aDescent
;
476 // keep the tallest bsize in sync
477 if (GetInitialBSize() < mMaxCellAscent
+ mMaxCellDescent
) {
478 SetContentBSize(mMaxCellAscent
+ mMaxCellDescent
);
484 nscoord
nsTableRowFrame::CalcBSize(const ReflowInput
& aReflowInput
) {
485 nsTableFrame
* tableFrame
= GetTableFrame();
486 nscoord computedBSize
= (NS_UNCONSTRAINEDSIZE
== aReflowInput
.ComputedBSize())
488 : aReflowInput
.ComputedBSize();
489 ResetBSize(computedBSize
);
491 WritingMode wm
= aReflowInput
.GetWritingMode();
492 const nsStylePosition
* position
= StylePosition();
493 const auto& bsizeStyleCoord
= position
->BSize(wm
);
494 if (bsizeStyleCoord
.ConvertsToLength()) {
495 SetFixedBSize(bsizeStyleCoord
.ToLength());
496 } else if (bsizeStyleCoord
.ConvertsToPercentage()) {
497 SetPctBSize(bsizeStyleCoord
.ToPercentage());
500 for (nsIFrame
* kidFrame
: mFrames
) {
501 nsTableCellFrame
* cellFrame
= do_QueryFrame(kidFrame
);
503 MOZ_ASSERT(cellFrame
->GetWritingMode() == wm
);
504 LogicalSize desSize
= cellFrame
->GetDesiredSize();
505 if ((NS_UNCONSTRAINEDSIZE
== aReflowInput
.AvailableBSize()) &&
507 CalculateCellActualBSize(cellFrame
, desSize
.BSize(wm
), wm
);
509 // bsize may have changed, adjust descent to absorb any excess difference
511 if (!kidFrame
->PrincipalChildList()
513 ->PrincipalChildList()
515 ascent
= desSize
.BSize(wm
);
517 ascent
= cellFrame
->GetCellBaseline();
518 nscoord descent
= desSize
.BSize(wm
) - ascent
;
519 UpdateBSize(desSize
.BSize(wm
), ascent
, descent
, tableFrame
, cellFrame
);
522 return GetInitialBSize();
525 void nsTableRowFrame::PaintCellBackgroundsForFrame(
526 nsIFrame
* aFrame
, nsDisplayListBuilder
* aBuilder
,
527 const nsDisplayListSet
& aLists
, const nsPoint
& aOffset
) {
528 // Compute background rect by iterating all cell frame.
529 const nsPoint toReferenceFrame
= aBuilder
->ToReferenceFrame(aFrame
);
530 for (nsTableCellFrame
* cell
= GetFirstCell(); cell
;
531 cell
= cell
->GetNextCell()) {
532 if (!cell
->ShouldPaintBackground(aBuilder
)) {
537 cell
->GetRectRelativeToSelf() + cell
->GetNormalPosition() + aOffset
;
538 if (!aBuilder
->GetDirtyRect().Intersects(cellRect
)) {
541 cellRect
+= toReferenceFrame
;
542 nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
543 aBuilder
, aFrame
, cellRect
, aLists
.BorderBackground(), true,
544 aFrame
->GetRectRelativeToSelf() + toReferenceFrame
, cell
);
548 void nsTableRowFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
549 const nsDisplayListSet
& aLists
) {
550 DisplayOutsetBoxShadow(aBuilder
, aLists
.BorderBackground());
552 PaintCellBackgroundsForFrame(this, aBuilder
, aLists
);
554 DisplayInsetBoxShadow(aBuilder
, aLists
.BorderBackground());
556 DisplayOutline(aBuilder
, aLists
);
558 for (nsIFrame
* kid
: PrincipalChildList()) {
559 BuildDisplayListForChild(aBuilder
, kid
, aLists
);
563 LogicalSides
nsTableRowFrame::GetLogicalSkipSides() const {
564 LogicalSides
skip(mWritingMode
);
565 if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak
==
566 StyleBoxDecorationBreak::Clone
)) {
570 if (GetPrevInFlow()) {
571 skip
|= eLogicalSideBitsBStart
;
573 if (GetNextInFlow()) {
574 skip
|= eLogicalSideBitsBEnd
;
579 // Calculate the cell's actual bsize given its pass2 bsize.
580 // Takes into account the specified bsize (in the style).
581 // Modifies the desired bsize that is passed in.
582 nsresult
nsTableRowFrame::CalculateCellActualBSize(nsTableCellFrame
* aCellFrame
,
583 nscoord
& aDesiredBSize
,
585 nscoord specifiedBSize
= 0;
587 // Get the bsize specified in the style information
588 const nsStylePosition
* position
= aCellFrame
->StylePosition();
590 int32_t rowSpan
= GetTableFrame()->GetEffectiveRowSpan(*aCellFrame
);
592 const auto& bsizeStyleCoord
= position
->BSize(aWM
);
593 if (bsizeStyleCoord
.ConvertsToLength()) {
594 // In quirks mode, table cell isize should be content-box, but bsize
595 // should be border-box.
596 // Because of this historic anomaly, we do not use quirk.css
597 // (since we can't specify one value of box-sizing for isize and another
599 specifiedBSize
= bsizeStyleCoord
.ToLength();
600 if (PresContext()->CompatibilityMode() != eCompatibility_NavQuirks
&&
601 position
->mBoxSizing
== StyleBoxSizing::Content
) {
603 aCellFrame
->GetLogicalUsedBorderAndPadding(aWM
).BStartEnd(aWM
);
607 SetFixedBSize(specifiedBSize
);
609 } else if (bsizeStyleCoord
.ConvertsToPercentage()) {
611 SetPctBSize(bsizeStyleCoord
.ToPercentage());
615 // If the specified bsize is greater than the desired bsize,
616 // then use the specified bsize
617 if (specifiedBSize
> aDesiredBSize
) {
618 aDesiredBSize
= specifiedBSize
;
624 // Calculates the available isize for the table cell based on the known
625 // column isizes taking into account column spans and column spacing
626 static nscoord
CalcAvailISize(nsTableFrame
& aTableFrame
,
627 nsTableCellFrame
& aCellFrame
) {
628 nscoord cellAvailISize
= 0;
629 uint32_t colIndex
= aCellFrame
.ColIndex();
630 int32_t colspan
= aTableFrame
.GetEffectiveColSpan(aCellFrame
);
631 NS_ASSERTION(colspan
> 0, "effective colspan should be positive");
632 nsTableFrame
* fifTable
=
633 static_cast<nsTableFrame
*>(aTableFrame
.FirstInFlow());
635 for (int32_t spanX
= 0; spanX
< colspan
; spanX
++) {
636 cellAvailISize
+= fifTable
->GetColumnISizeFromFirstInFlow(colIndex
+ spanX
);
637 if (spanX
> 0 && aTableFrame
.ColumnHasCellSpacingBefore(colIndex
+ spanX
)) {
638 cellAvailISize
+= aTableFrame
.GetColSpacing(colIndex
+ spanX
- 1);
641 return cellAvailISize
;
644 static nscoord
GetSpaceBetween(int32_t aPrevColIndex
, int32_t aColIndex
,
645 int32_t aColSpan
, nsTableFrame
& aTableFrame
,
646 bool aCheckVisibility
) {
649 nsTableFrame
* fifTable
=
650 static_cast<nsTableFrame
*>(aTableFrame
.FirstInFlow());
651 for (colIdx
= aPrevColIndex
+ 1; aColIndex
> colIdx
; colIdx
++) {
652 bool isCollapsed
= false;
653 if (!aCheckVisibility
) {
654 space
+= fifTable
->GetColumnISizeFromFirstInFlow(colIdx
);
656 nsTableColFrame
* colFrame
= aTableFrame
.GetColFrame(colIdx
);
657 const nsStyleVisibility
* colVis
= colFrame
->StyleVisibility();
658 bool collapseCol
= StyleVisibility::Collapse
== colVis
->mVisible
;
659 nsIFrame
* cgFrame
= colFrame
->GetParent();
660 const nsStyleVisibility
* groupVis
= cgFrame
->StyleVisibility();
661 bool collapseGroup
= StyleVisibility::Collapse
== groupVis
->mVisible
;
662 isCollapsed
= collapseCol
|| collapseGroup
;
664 space
+= fifTable
->GetColumnISizeFromFirstInFlow(colIdx
);
666 if (!isCollapsed
&& aTableFrame
.ColumnHasCellSpacingBefore(colIdx
)) {
667 space
+= aTableFrame
.GetColSpacing(colIdx
- 1);
673 // subtract the bsizes of aRow's prev in flows from the unpaginated bsize
674 static nscoord
CalcBSizeFromUnpaginatedBSize(nsTableRowFrame
& aRow
,
677 nsTableRowFrame
* firstInFlow
=
678 static_cast<nsTableRowFrame
*>(aRow
.FirstInFlow());
679 if (firstInFlow
->HasUnpaginatedBSize()) {
680 bsize
= firstInFlow
->GetUnpaginatedBSize();
681 for (nsIFrame
* prevInFlow
= aRow
.GetPrevInFlow(); prevInFlow
;
682 prevInFlow
= prevInFlow
->GetPrevInFlow()) {
683 bsize
-= prevInFlow
->BSize(aWM
);
686 return std::max(bsize
, 0);
689 void nsTableRowFrame::ReflowChildren(nsPresContext
* aPresContext
,
690 ReflowOutput
& aDesiredSize
,
691 const ReflowInput
& aReflowInput
,
692 nsTableFrame
& aTableFrame
,
693 nsReflowStatus
& aStatus
) {
696 // XXXldb Should we be checking constrained bsize instead?
697 const bool isPaginated
= aPresContext
->IsPaginated();
698 const bool borderCollapse
= aTableFrame
.IsBorderCollapse();
700 int32_t cellColSpan
=
701 1; // must be defined here so it's set properly for non-cell kids
703 // remember the col index of the previous cell to handle rowspans into this
705 int32_t prevColIndex
= -1;
706 nscoord iCoord
= 0; // running total of children inline-coord offset
708 // This computes the max of all cell bsizes
709 nscoord cellMaxBSize
= 0;
711 // Reflow each of our existing cell frames
712 WritingMode wm
= aReflowInput
.GetWritingMode();
713 nsSize containerSize
= aReflowInput
.ComputedSizeAsContainerIfConstrained();
715 for (nsIFrame
* kidFrame
: mFrames
) {
716 nsTableCellFrame
* cellFrame
= do_QueryFrame(kidFrame
);
718 // XXXldb nsCSSFrameConstructor needs to enforce this!
719 MOZ_ASSERT_UNREACHABLE("yikes, a non-row child");
721 // it's an unknown frame type, give it a generic reflow and ignore the
723 TableCellReflowInput
kidReflowInput(
724 aPresContext
, aReflowInput
, kidFrame
,
725 LogicalSize(kidFrame
->GetWritingMode(), 0, 0),
726 ReflowInput::InitFlag::CallerWillInit
);
727 InitChildReflowInput(*aPresContext
, LogicalSize(wm
), false,
729 ReflowOutput
desiredSize(aReflowInput
);
730 nsReflowStatus status
;
731 ReflowChild(kidFrame
, aPresContext
, desiredSize
, kidReflowInput
, 0, 0,
732 ReflowChildFlags::Default
, status
);
733 kidFrame
->DidReflow(aPresContext
, nullptr);
738 // See if we should only reflow the dirty child frames
739 bool doReflowChild
= true;
740 if (!aReflowInput
.ShouldReflowAllKids() && !aTableFrame
.IsGeometryDirty() &&
741 !kidFrame
->IsSubtreeDirty()) {
742 if (!aReflowInput
.mFlags
.mSpecialBSizeReflow
) doReflowChild
= false;
743 } else if ((NS_UNCONSTRAINEDSIZE
!= aReflowInput
.AvailableBSize())) {
744 // We don't reflow a rowspan >1 cell here with a constrained bsize.
745 // That happens in nsTableRowGroupFrame::SplitSpanningCells.
746 if (aTableFrame
.GetEffectiveRowSpan(*cellFrame
) > 1) {
747 doReflowChild
= false;
750 if (aReflowInput
.mFlags
.mSpecialBSizeReflow
) {
752 !cellFrame
->HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE
)) {
757 uint32_t cellColIndex
= cellFrame
->ColIndex();
758 cellColSpan
= aTableFrame
.GetEffectiveColSpan(*cellFrame
);
760 // If the adjacent cell is in a prior row (because of a rowspan) add in the
761 // space NOTE: prevColIndex can be -1 here.
762 if (prevColIndex
!= (static_cast<int32_t>(cellColIndex
) - 1)) {
763 iCoord
+= GetSpaceBetween(prevColIndex
, cellColIndex
, cellColSpan
,
767 // remember the rightmost (ltr) or leftmost (rtl) column this cell spans
769 prevColIndex
= cellColIndex
+ (cellColSpan
- 1);
771 // Reflow the child frame
772 nsRect kidRect
= kidFrame
->GetRect();
773 LogicalPoint origKidNormalPosition
=
774 kidFrame
->GetLogicalNormalPosition(wm
, containerSize
);
776 nsRect kidInkOverflow
= kidFrame
->InkOverflowRect();
777 LogicalPoint
kidPosition(wm
, iCoord
, 0);
778 bool firstReflow
= kidFrame
->HasAnyStateBits(NS_FRAME_FIRST_REFLOW
);
781 // Calculate the available isize for the table cell using the known
783 nscoord availCellISize
= CalcAvailISize(aTableFrame
, *cellFrame
);
785 Maybe
<TableCellReflowInput
> kidReflowInput
;
786 ReflowOutput
desiredSize(aReflowInput
);
788 // If the avail isize is not the same as last time we reflowed the cell or
789 // the cell wants to be bigger than what was available last time or
790 // it is a style change reflow or we are printing, then we must reflow the
791 // cell. Otherwise we can skip the reflow.
792 // XXXldb Why is this condition distinct from doReflowChild above?
793 WritingMode wm
= aReflowInput
.GetWritingMode();
794 NS_ASSERTION(cellFrame
->GetWritingMode() == wm
,
795 "expected consistent writing-mode within table");
796 LogicalSize cellDesiredSize
= cellFrame
->GetDesiredSize();
797 if ((availCellISize
!= cellFrame
->GetPriorAvailISize()) ||
798 (cellDesiredSize
.ISize(wm
) > cellFrame
->GetPriorAvailISize()) ||
799 HasAnyStateBits(NS_FRAME_IS_DIRTY
) || isPaginated
||
800 cellFrame
->IsSubtreeDirty() ||
801 // See if it needs a special reflow, or if it had one that we need to
803 cellFrame
->HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE
) ||
805 // Reflow the cell to fit the available isize, bsize
806 // XXX The old IR_ChildIsDirty code used availCellISize here.
807 LogicalSize
kidAvailSize(wm
, availCellISize
,
808 aReflowInput
.AvailableBSize());
811 kidReflowInput
.emplace(aPresContext
, aReflowInput
, kidFrame
,
813 ReflowInput::InitFlag::CallerWillInit
);
814 InitChildReflowInput(*aPresContext
, kidAvailSize
, borderCollapse
,
817 nsReflowStatus status
;
818 ReflowChild(kidFrame
, aPresContext
, desiredSize
, *kidReflowInput
, wm
,
819 kidPosition
, containerSize
, ReflowChildFlags::Default
,
822 // allow the table to determine if/how the table needs to be rebalanced
823 // If any of the cells are not complete, then we're not complete
824 if (status
.IsIncomplete()) {
826 aStatus
.SetIncomplete();
829 if (iCoord
!= origKidNormalPosition
.I(wm
)) {
830 kidFrame
->InvalidateFrameSubtree();
833 desiredSize
.SetSize(wm
, cellDesiredSize
);
834 desiredSize
.mOverflowAreas
= cellFrame
->GetOverflowAreas();
836 // if we are in a floated table, our position is not yet established, so
837 // we cannot reposition our views the containing block will do this for
838 // us after positioning the table
839 if (!aTableFrame
.IsFloating()) {
840 // Because we may have moved the frame we need to make sure any views
841 // are positioned properly. We have to do this, because any one of our
842 // parent frames could have moved and we have no way of knowing...
843 nsTableFrame::RePositionViews(kidFrame
);
847 if (NS_UNCONSTRAINEDSIZE
== aReflowInput
.AvailableBSize()) {
848 if (!GetPrevInFlow()) {
849 // Calculate the cell's actual bsize given its pass2 bsize. This
850 // function takes into account the specified bsize (in the style)
851 CalculateCellActualBSize(cellFrame
, desiredSize
.BSize(wm
), wm
);
853 // bsize may have changed, adjust descent to absorb any excess
856 if (!kidFrame
->PrincipalChildList()
858 ->PrincipalChildList()
860 ascent
= desiredSize
.BSize(wm
);
862 ascent
= ((nsTableCellFrame
*)kidFrame
)->GetCellBaseline();
864 nscoord descent
= desiredSize
.BSize(wm
) - ascent
;
865 UpdateBSize(desiredSize
.BSize(wm
), ascent
, descent
, &aTableFrame
,
868 cellMaxBSize
= std::max(cellMaxBSize
, desiredSize
.BSize(wm
));
870 aTableFrame
.GetEffectiveRowSpan((nsTableCellFrame
&)*kidFrame
);
872 SetContentBSize(cellMaxBSize
);
877 desiredSize
.ISize(wm
) = availCellISize
;
879 ReflowChildFlags flags
= ReflowChildFlags::Default
;
881 if (kidReflowInput
) {
882 // We reflowed. Apply relative positioning in the normal way.
883 flags
= ReflowChildFlags::ApplyRelativePositioning
;
884 } else if (kidFrame
->IsRelativelyOrStickyPositioned()) {
885 // We didn't reflow. Do the positioning part of what
886 // MovePositionBy does internally. (This codepath should really
887 // be merged into the else below if we can.)
888 nsMargin
* computedOffsetProp
=
889 kidFrame
->GetProperty(nsIFrame::ComputedOffsetProperty());
891 // On our fist reflow sticky children may not have the property yet (we
892 // need to reflow the children first to size the scroll frame).
893 LogicalMargin
computedOffsets(
894 wm
, computedOffsetProp
? *computedOffsetProp
: nsMargin());
895 ReflowInput::ApplyRelativePositioning(kidFrame
, wm
, computedOffsets
,
896 &kidPosition
, containerSize
);
899 // In vertical-rl mode, we are likely to have containerSize.width = 0
900 // because ComputedWidth() was NS_UNCONSTRAINEDSIZE.
901 // For cases where that's wrong, we will fix up the position later.
902 FinishReflowChild(kidFrame
, aPresContext
, desiredSize
,
903 kidReflowInput
.ptrOr(nullptr), wm
, kidPosition
,
904 containerSize
, flags
);
906 nsTableFrame
* tableFrame
= GetTableFrame();
907 if (tableFrame
->IsBorderCollapse()) {
908 nsTableFrame::InvalidateTableFrame(kidFrame
, kidRect
, kidInkOverflow
,
912 iCoord
+= desiredSize
.ISize(wm
);
914 if (iCoord
!= origKidNormalPosition
.I(wm
)) {
915 // Invalidate the old position
916 kidFrame
->InvalidateFrameSubtree();
917 // Move to the new position. As above, we need to account for relative
919 kidFrame
->MovePositionBy(
920 wm
, LogicalPoint(wm
, iCoord
- origKidNormalPosition
.I(wm
), 0));
921 nsTableFrame::RePositionViews(kidFrame
);
922 // invalidate the new position
923 kidFrame
->InvalidateFrameSubtree();
925 // we need to account for the cell's isize even if it isn't reflowed
926 iCoord
+= kidFrame
->ISize(wm
);
928 if (kidFrame
->GetNextInFlow()) {
930 aStatus
.SetIncomplete();
933 ConsiderChildOverflow(aDesiredSize
.mOverflowAreas
, kidFrame
);
934 iCoord
+= aTableFrame
.GetColSpacing(cellColIndex
);
937 // Just set our isize to what was available.
938 // The table will calculate the isize and not use our value.
939 aDesiredSize
.ISize(wm
) = aReflowInput
.AvailableISize();
941 if (aReflowInput
.mFlags
.mSpecialBSizeReflow
) {
942 aDesiredSize
.BSize(wm
) = BSize(wm
);
943 } else if (NS_UNCONSTRAINEDSIZE
== aReflowInput
.AvailableBSize()) {
944 aDesiredSize
.BSize(wm
) = CalcBSize(aReflowInput
);
945 if (GetPrevInFlow()) {
946 nscoord bsize
= CalcBSizeFromUnpaginatedBSize(*this, wm
);
947 aDesiredSize
.BSize(wm
) = std::max(aDesiredSize
.BSize(wm
), bsize
);
949 if (isPaginated
&& HasStyleBSize()) {
950 // set the unpaginated bsize so next in flows can try to honor it
951 SetHasUnpaginatedBSize(true);
952 SetUnpaginatedBSize(aPresContext
, aDesiredSize
.BSize(wm
));
954 if (isPaginated
&& HasUnpaginatedBSize()) {
955 aDesiredSize
.BSize(wm
) =
956 std::max(aDesiredSize
.BSize(wm
), GetUnpaginatedBSize());
959 } else { // constrained bsize, paginated
960 // Compute the bsize we should have from style (subtracting the
961 // bsize from our prev-in-flows from the style bsize)
962 nscoord styleBSize
= CalcBSizeFromUnpaginatedBSize(*this, wm
);
963 if (styleBSize
> aReflowInput
.AvailableBSize()) {
964 styleBSize
= aReflowInput
.AvailableBSize();
965 aStatus
.SetIncomplete();
967 aDesiredSize
.BSize(wm
) = std::max(cellMaxBSize
, styleBSize
);
970 if (wm
.IsVerticalRL()) {
971 // Any children whose width was not the same as our final
972 // aDesiredSize.BSize will have been misplaced earlier at the
973 // FinishReflowChild stage. So fix them up now.
974 for (nsIFrame
* kidFrame
: mFrames
) {
975 nsTableCellFrame
* cellFrame
= do_QueryFrame(kidFrame
);
979 if (kidFrame
->BSize(wm
) != aDesiredSize
.BSize(wm
)) {
980 kidFrame
->MovePositionBy(
982 LogicalPoint(wm
, 0, kidFrame
->BSize(wm
) - aDesiredSize
.BSize(wm
)));
983 nsTableFrame::RePositionViews(kidFrame
);
984 // Do we need to InvalidateFrameSubtree() here?
989 aDesiredSize
.UnionOverflowAreasWithDesiredBounds();
990 FinishAndStoreOverflow(&aDesiredSize
);
993 /** Layout the entire row.
994 * This method stacks cells in the inline dir according to HTML 4.0 rules.
996 void nsTableRowFrame::Reflow(nsPresContext
* aPresContext
,
997 ReflowOutput
& aDesiredSize
,
998 const ReflowInput
& aReflowInput
,
999 nsReflowStatus
& aStatus
) {
1001 DO_GLOBAL_REFLOW_COUNT("nsTableRowFrame");
1002 DISPLAY_REFLOW(aPresContext
, this, aReflowInput
, aDesiredSize
, aStatus
);
1003 MOZ_ASSERT(aStatus
.IsEmpty(), "Caller should pass a fresh reflow status!");
1005 WritingMode wm
= aReflowInput
.GetWritingMode();
1007 nsTableFrame
* tableFrame
= GetTableFrame();
1008 const nsStyleVisibility
* rowVis
= StyleVisibility();
1009 bool collapseRow
= StyleVisibility::Collapse
== rowVis
->mVisible
;
1011 tableFrame
->SetNeedToCollapse(true);
1014 // see if a special bsize reflow needs to occur due to having a pct bsize
1015 nsTableFrame::CheckRequestSpecialBSizeReflow(aReflowInput
);
1017 // See if we have a cell with specified/pct bsize
1018 InitHasCellWithStyleBSize(tableFrame
);
1020 ReflowChildren(aPresContext
, aDesiredSize
, aReflowInput
, *tableFrame
,
1023 if (aPresContext
->IsPaginated() && !aStatus
.IsFullyComplete() &&
1024 ShouldAvoidBreakInside(aReflowInput
)) {
1025 aStatus
.SetInlineLineBreakBeforeAndReset();
1028 // Just set our isize to what was available.
1029 // The table will calculate the isize and not use our value.
1030 aDesiredSize
.ISize(wm
) = aReflowInput
.AvailableISize();
1032 // If our parent is in initial reflow, it'll handle invalidating our
1033 // entire overflow rect.
1034 if (!GetParent()->HasAnyStateBits(NS_FRAME_FIRST_REFLOW
) &&
1035 nsSize(aDesiredSize
.Width(), aDesiredSize
.Height()) != mRect
.Size()) {
1039 // Any absolutely-positioned children will get reflowed in
1040 // nsIFrame::FixupPositionedTableParts in another pass, so propagate our
1041 // dirtiness to them before our parent clears our dirty bits.
1042 PushDirtyBitToAbsoluteFrames();
1046 * This function is called by the row group frame's SplitRowGroup() code when
1047 * pushing a row frame that has cell frames that span into it. The cell frame
1048 * should be reflowed with the specified height
1050 nscoord
nsTableRowFrame::ReflowCellFrame(nsPresContext
* aPresContext
,
1051 const ReflowInput
& aReflowInput
,
1053 nsTableCellFrame
* aCellFrame
,
1054 nscoord aAvailableBSize
,
1055 nsReflowStatus
& aStatus
) {
1056 MOZ_ASSERT(aAvailableBSize
!= NS_UNCONSTRAINEDSIZE
,
1057 "Why split cell frame if available bsize is unconstrained?");
1058 WritingMode wm
= aReflowInput
.GetWritingMode();
1060 // Reflow the cell frame with the specified height. Use the existing width
1061 nsSize containerSize
= aCellFrame
->GetSize();
1062 LogicalRect cellRect
= aCellFrame
->GetLogicalRect(wm
, containerSize
);
1063 nsRect cellInkOverflow
= aCellFrame
->InkOverflowRect();
1065 LogicalSize cellSize
= cellRect
.Size(wm
);
1066 LogicalSize
availSize(wm
, cellRect
.ISize(wm
), aAvailableBSize
);
1067 bool borderCollapse
= GetTableFrame()->IsBorderCollapse();
1068 NS_ASSERTION(aCellFrame
->GetWritingMode() == wm
,
1069 "expected consistent writing-mode within table");
1070 TableCellReflowInput
cellReflowInput(aPresContext
, aReflowInput
, aCellFrame
,
1072 ReflowInput::InitFlag::CallerWillInit
);
1073 InitChildReflowInput(*aPresContext
, availSize
, borderCollapse
,
1075 cellReflowInput
.mFlags
.mIsTopOfPage
= aIsTopOfPage
;
1077 ReflowOutput
desiredSize(aReflowInput
);
1079 ReflowChild(aCellFrame
, aPresContext
, desiredSize
, cellReflowInput
, 0, 0,
1080 ReflowChildFlags::NoMoveFrame
, aStatus
);
1081 const bool isTruncated
=
1082 aAvailableBSize
< desiredSize
.BSize(wm
) &&
1083 !aIsTopOfPage
; // XXX Is !aIsTopOfPage check really necessary?
1084 const bool isCompleteAndNotTruncated
= aStatus
.IsComplete() && !isTruncated
;
1085 if (isCompleteAndNotTruncated
) {
1086 desiredSize
.BSize(wm
) = aAvailableBSize
;
1088 aCellFrame
->SetSize(
1089 wm
, LogicalSize(wm
, cellSize
.ISize(wm
), desiredSize
.BSize(wm
)));
1091 // Note: BlockDirAlignChild can affect the overflow rect.
1092 // XXX What happens if this cell has 'vertical-align: baseline' ?
1093 // XXX Why is it assumed that the cell's ascent hasn't changed ?
1094 if (isCompleteAndNotTruncated
) {
1095 aCellFrame
->BlockDirAlignChild(wm
, mMaxCellAscent
);
1098 nsTableFrame::InvalidateTableFrame(
1099 aCellFrame
, cellRect
.GetPhysicalRect(wm
, containerSize
), cellInkOverflow
,
1100 aCellFrame
->HasAnyStateBits(NS_FRAME_FIRST_REFLOW
));
1102 aCellFrame
->DidReflow(aPresContext
, nullptr);
1104 return desiredSize
.BSize(wm
);
1107 nscoord
nsTableRowFrame::CollapseRowIfNecessary(nscoord aRowOffset
,
1109 bool aCollapseGroup
,
1110 bool& aDidCollapse
) {
1111 const nsStyleVisibility
* rowVis
= StyleVisibility();
1112 bool collapseRow
= StyleVisibility::Collapse
== rowVis
->mVisible
;
1113 nsTableFrame
* tableFrame
=
1114 static_cast<nsTableFrame
*>(GetTableFrame()->FirstInFlow());
1116 tableFrame
->SetNeedToCollapse(true);
1119 if (aRowOffset
!= 0) {
1120 // We're moving, so invalidate our old position
1121 InvalidateFrameSubtree();
1124 WritingMode wm
= GetWritingMode();
1126 nsSize parentSize
= GetParent()->GetSize();
1127 LogicalRect rowRect
= GetLogicalRect(wm
, parentSize
);
1128 nsRect oldRect
= mRect
;
1129 nsRect oldInkOverflow
= InkOverflowRect();
1131 rowRect
.BStart(wm
) -= aRowOffset
;
1132 rowRect
.ISize(wm
) = aISize
;
1133 OverflowAreas overflow
;
1135 nsSize containerSize
= mRect
.Size();
1137 if (aCollapseGroup
|| collapseRow
) {
1138 aDidCollapse
= true;
1139 shift
= rowRect
.BSize(wm
);
1140 nsTableCellFrame
* cellFrame
= GetFirstCell();
1142 uint32_t rowIndex
= cellFrame
->RowIndex();
1143 shift
+= tableFrame
->GetRowSpacing(rowIndex
);
1145 LogicalRect cRect
= cellFrame
->GetLogicalRect(wm
, containerSize
);
1146 // If aRowOffset != 0, there's no point in invalidating the cells, since
1147 // we've already invalidated our overflow area. Note that we _do_ still
1148 // need to invalidate if our row is not moving, because the cell might
1149 // span out of this row, so invalidating our row rect won't do enough.
1150 if (aRowOffset
== 0) {
1153 cRect
.BSize(wm
) = 0;
1154 cellFrame
->SetRect(wm
, cRect
, containerSize
);
1155 cellFrame
= cellFrame
->GetNextCell();
1158 shift
+= tableFrame
->GetRowSpacing(GetRowIndex());
1160 rowRect
.BSize(wm
) = 0;
1161 } else { // row is not collapsed
1162 // remember the col index of the previous cell to handle rowspans into this
1164 int32_t prevColIndex
= -1;
1165 nscoord iPos
= 0; // running total of children inline-axis offset
1166 nsTableFrame
* fifTable
=
1167 static_cast<nsTableFrame
*>(tableFrame
->FirstInFlow());
1169 for (nsIFrame
* kidFrame
: mFrames
) {
1170 nsTableCellFrame
* cellFrame
= do_QueryFrame(kidFrame
);
1172 uint32_t cellColIndex
= cellFrame
->ColIndex();
1173 int32_t cellColSpan
= tableFrame
->GetEffectiveColSpan(*cellFrame
);
1175 // If the adjacent cell is in a prior row (because of a rowspan) add in
1177 // NOTE: prevColIndex can be -1 here.
1178 if (prevColIndex
!= (static_cast<int32_t>(cellColIndex
) - 1)) {
1179 iPos
+= GetSpaceBetween(prevColIndex
, cellColIndex
, cellColSpan
,
1182 LogicalRect
cRect(wm
, iPos
, 0, 0, rowRect
.BSize(wm
));
1184 // remember the last (iend-wards-most) column this cell spans into
1185 prevColIndex
= cellColIndex
+ cellColSpan
- 1;
1186 int32_t actualColSpan
= cellColSpan
;
1187 bool isVisible
= false;
1188 for (int32_t colIdx
= cellColIndex
; actualColSpan
> 0;
1189 colIdx
++, actualColSpan
--) {
1190 nsTableColFrame
* colFrame
= tableFrame
->GetColFrame(colIdx
);
1191 const nsStyleVisibility
* colVis
= colFrame
->StyleVisibility();
1192 bool collapseCol
= StyleVisibility::Collapse
== colVis
->mVisible
;
1193 nsIFrame
* cgFrame
= colFrame
->GetParent();
1194 const nsStyleVisibility
* groupVis
= cgFrame
->StyleVisibility();
1195 bool collapseGroup
= StyleVisibility::Collapse
== groupVis
->mVisible
;
1196 bool isCollapsed
= collapseCol
|| collapseGroup
;
1198 cRect
.ISize(wm
) += fifTable
->GetColumnISizeFromFirstInFlow(colIdx
);
1200 if ((actualColSpan
> 1)) {
1201 nsTableColFrame
* nextColFrame
=
1202 tableFrame
->GetColFrame(colIdx
+ 1);
1203 const nsStyleVisibility
* nextColVis
=
1204 nextColFrame
->StyleVisibility();
1205 if (StyleVisibility::Collapse
!= nextColVis
->mVisible
&&
1206 tableFrame
->ColumnHasCellSpacingBefore(colIdx
+ 1)) {
1207 cRect
.ISize(wm
) += tableFrame
->GetColSpacing(cellColIndex
);
1212 iPos
+= cRect
.ISize(wm
);
1214 iPos
+= tableFrame
->GetColSpacing(cellColIndex
);
1216 int32_t actualRowSpan
= tableFrame
->GetEffectiveRowSpan(*cellFrame
);
1217 nsTableRowFrame
* rowFrame
= GetNextRow();
1218 for (actualRowSpan
--; actualRowSpan
> 0 && rowFrame
; actualRowSpan
--) {
1219 const nsStyleVisibility
* nextRowVis
= rowFrame
->StyleVisibility();
1220 bool collapseNextRow
=
1221 StyleVisibility::Collapse
== nextRowVis
->mVisible
;
1222 if (!collapseNextRow
) {
1223 LogicalRect nextRect
= rowFrame
->GetLogicalRect(wm
, containerSize
);
1225 nextRect
.BSize(wm
) +
1226 tableFrame
->GetRowSpacing(rowFrame
->GetRowIndex());
1228 rowFrame
= rowFrame
->GetNextRow();
1231 nsRect oldCellRect
= cellFrame
->GetRect();
1232 LogicalPoint oldCellNormalPos
=
1233 cellFrame
->GetLogicalNormalPosition(wm
, containerSize
);
1235 nsRect oldCellInkOverflow
= cellFrame
->InkOverflowRect();
1237 if (aRowOffset
== 0 && cRect
.Origin(wm
) != oldCellNormalPos
) {
1238 // We're moving the cell. Invalidate the old overflow area
1239 cellFrame
->InvalidateFrameSubtree();
1242 cellFrame
->MovePositionBy(wm
, cRect
.Origin(wm
) - oldCellNormalPos
);
1243 cellFrame
->SetSize(wm
, cRect
.Size(wm
));
1245 // XXXbz This looks completely bogus in the cases when we didn't
1246 // collapse the cell!
1247 LogicalRect
cellBounds(wm
, 0, 0, cRect
.ISize(wm
), cRect
.BSize(wm
));
1248 nsRect cellPhysicalBounds
=
1249 cellBounds
.GetPhysicalRect(wm
, containerSize
);
1250 OverflowAreas
cellOverflow(cellPhysicalBounds
, cellPhysicalBounds
);
1251 cellFrame
->FinishAndStoreOverflow(cellOverflow
,
1252 cRect
.Size(wm
).GetPhysicalSize(wm
));
1253 nsTableFrame::RePositionViews(cellFrame
);
1254 ConsiderChildOverflow(overflow
, cellFrame
);
1256 if (aRowOffset
== 0) {
1257 nsTableFrame::InvalidateTableFrame(cellFrame
, oldCellRect
,
1258 oldCellInkOverflow
, false);
1264 SetRect(wm
, rowRect
, containerSize
);
1265 overflow
.UnionAllWith(nsRect(0, 0, rowRect
.Width(wm
), rowRect
.Height(wm
)));
1266 FinishAndStoreOverflow(overflow
, rowRect
.Size(wm
).GetPhysicalSize(wm
));
1268 nsTableFrame::RePositionViews(this);
1269 nsTableFrame::InvalidateTableFrame(this, oldRect
, oldInkOverflow
, false);
1274 * The following method is called by the row group frame's SplitRowGroup()
1275 * when it creates a continuing cell frame and wants to insert it into the
1278 void nsTableRowFrame::InsertCellFrame(nsTableCellFrame
* aFrame
,
1279 int32_t aColIndex
) {
1280 // Find the cell frame where col index < aColIndex
1281 nsTableCellFrame
* priorCell
= nullptr;
1282 for (nsIFrame
* child
: mFrames
) {
1283 nsTableCellFrame
* cellFrame
= do_QueryFrame(child
);
1285 uint32_t colIndex
= cellFrame
->ColIndex();
1286 // Can aColIndex be -1 here? Let's assume it can for now.
1287 if (static_cast<int32_t>(colIndex
) < aColIndex
) {
1288 priorCell
= cellFrame
;
1293 mFrames
.InsertFrame(this, priorCell
, aFrame
);
1296 nsTableRowFrame
* nsTableRowFrame::GetNextRow() const {
1297 nsIFrame
* childFrame
= GetNextSibling();
1298 while (childFrame
) {
1299 nsTableRowFrame
* rowFrame
= do_QueryFrame(childFrame
);
1301 NS_ASSERTION(mozilla::StyleDisplay::TableRow
==
1302 childFrame
->StyleDisplay()->mDisplay
,
1303 "wrong display type on rowframe");
1306 childFrame
= childFrame
->GetNextSibling();
1311 NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(RowUnpaginatedHeightProperty
, nscoord
)
1313 void nsTableRowFrame::SetUnpaginatedBSize(nsPresContext
* aPresContext
,
1315 NS_ASSERTION(!GetPrevInFlow(), "program error");
1317 SetProperty(RowUnpaginatedHeightProperty(), aValue
);
1320 nscoord
nsTableRowFrame::GetUnpaginatedBSize() {
1321 return GetProperty(RowUnpaginatedHeightProperty());
1324 void nsTableRowFrame::SetContinuousBCBorderWidth(LogicalSide aForSide
,
1325 BCPixelSize aPixelValue
) {
1327 case eLogicalSideIEnd
:
1328 mIEndContBorderWidth
= aPixelValue
;
1330 case eLogicalSideBStart
:
1331 mBStartContBorderWidth
= aPixelValue
;
1333 case eLogicalSideIStart
:
1334 mIStartContBorderWidth
= aPixelValue
;
1337 NS_ERROR("invalid LogicalSide arg");
1340 #ifdef ACCESSIBILITY
1341 a11y::AccType
nsTableRowFrame::AccessibleType() {
1342 return a11y::eHTMLTableRowType
;
1346 * Sets the NS_ROW_HAS_CELL_WITH_STYLE_BSIZE bit to indicate whether
1347 * this row has any cells that have non-auto-bsize. (Row-spanning
1348 * cells are ignored.)
1350 void nsTableRowFrame::InitHasCellWithStyleBSize(nsTableFrame
* aTableFrame
) {
1351 WritingMode wm
= GetWritingMode();
1353 for (nsIFrame
* kidFrame
: mFrames
) {
1354 nsTableCellFrame
* cellFrame
= do_QueryFrame(kidFrame
);
1356 MOZ_ASSERT_UNREACHABLE("Table row has a non-cell child.");
1359 // Ignore row-spanning cells
1360 const auto& cellBSize
= cellFrame
->StylePosition()->BSize(wm
);
1361 if (aTableFrame
->GetEffectiveRowSpan(*cellFrame
) == 1 &&
1362 !cellBSize
.IsAuto() &&
1363 /* calc() with both percentages and lengths treated like 'auto' */
1364 (cellBSize
.ConvertsToLength() || cellBSize
.ConvertsToPercentage())) {
1365 AddStateBits(NS_ROW_HAS_CELL_WITH_STYLE_BSIZE
);
1369 RemoveStateBits(NS_ROW_HAS_CELL_WITH_STYLE_BSIZE
);
1372 void nsTableRowFrame::InvalidateFrame(uint32_t aDisplayItemKey
,
1373 bool aRebuildDisplayItems
) {
1374 nsIFrame::InvalidateFrame(aDisplayItemKey
, aRebuildDisplayItems
);
1375 if (GetTableFrame()->IsBorderCollapse()) {
1376 const bool rebuild
= StaticPrefs::layout_display_list_retain_sc();
1377 GetParent()->InvalidateFrameWithRect(InkOverflowRect() + GetPosition(),
1378 aDisplayItemKey
, rebuild
);
1382 void nsTableRowFrame::InvalidateFrameWithRect(const nsRect
& aRect
,
1383 uint32_t aDisplayItemKey
,
1384 bool aRebuildDisplayItems
) {
1385 nsIFrame::InvalidateFrameWithRect(aRect
, aDisplayItemKey
,
1386 aRebuildDisplayItems
);
1387 // If we have filters applied that would affects our bounds, then
1388 // we get an inactive layer created and this is computed
1389 // within FrameLayerBuilder
1390 GetParent()->InvalidateFrameWithRect(aRect
+ GetPosition(), aDisplayItemKey
,
1391 aRebuildDisplayItems
);
1394 /* ----- global methods ----- */
1396 nsTableRowFrame
* NS_NewTableRowFrame(PresShell
* aPresShell
,
1397 ComputedStyle
* aStyle
) {
1398 return new (aPresShell
) nsTableRowFrame(aStyle
, aPresShell
->GetPresContext());
1401 NS_IMPL_FRAMEARENA_HELPERS(nsTableRowFrame
)
1403 #ifdef DEBUG_FRAME_DUMP
1404 nsresult
nsTableRowFrame::GetFrameName(nsAString
& aResult
) const {
1405 return MakeFrameName(u
"TableRow"_ns
, aResult
);