Bug 1919083 - [ci] Enable os-integration variant for more suites, r=jmaher
[gecko.git] / layout / tables / nsTableRowFrame.cpp
blobaae027298dc1ed29113af546819558c1159c0f18
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsTableRowFrame.h"
8 #include "mozilla/Baseline.h"
9 #include "mozilla/Maybe.h"
10 #include "mozilla/PresShell.h"
11 #include "nsTableRowGroupFrame.h"
12 #include "nsPresContext.h"
13 #include "mozilla/ComputedStyle.h"
14 #include "mozilla/StaticPrefs_layout.h"
15 #include "nsStyleConsts.h"
16 #include "nsIContent.h"
17 #include "nsIFrame.h"
18 #include "nsIFrameInlines.h"
19 #include "nsTableFrame.h"
20 #include "nsTableCellFrame.h"
21 #include "nsCSSRendering.h"
22 #include "nsHTMLParts.h"
23 #include "nsTableColGroupFrame.h"
24 #include "nsTableColFrame.h"
25 #include "nsDisplayList.h"
26 #include <algorithm>
28 #ifdef ACCESSIBILITY
29 # include "nsAccessibilityService.h"
30 #endif
32 using namespace mozilla;
34 namespace mozilla {
36 struct TableCellReflowInput : public ReflowInput {
37 TableCellReflowInput(nsPresContext* aPresContext,
38 const ReflowInput& aParentReflowInput, nsIFrame* aFrame,
39 const LogicalSize& aAvailableSpace,
40 ReflowInput::InitFlags aFlags = {})
41 : ReflowInput(aPresContext, aParentReflowInput, aFrame, aAvailableSpace,
42 Nothing(), aFlags) {}
44 void FixUp(const LogicalSize& aAvailSpace);
47 } // namespace mozilla
49 void TableCellReflowInput::FixUp(const LogicalSize& aAvailSpace) {
50 // fix the mComputed values during a pass 2 reflow since the cell can be a
51 // percentage base
52 NS_WARNING_ASSERTION(
53 NS_UNCONSTRAINEDSIZE != aAvailSpace.ISize(mWritingMode),
54 "have unconstrained inline-size; this should only result from very large "
55 "sizes, not attempts at intrinsic inline size calculation");
56 if (NS_UNCONSTRAINEDSIZE != ComputedISize()) {
57 nscoord computedISize =
58 aAvailSpace.ISize(mWritingMode) -
59 ComputedLogicalBorderPadding(mWritingMode).IStartEnd(mWritingMode);
60 computedISize = std::max(0, computedISize);
61 SetComputedISize(computedISize);
63 if (NS_UNCONSTRAINEDSIZE != ComputedBSize() &&
64 NS_UNCONSTRAINEDSIZE != aAvailSpace.BSize(mWritingMode)) {
65 nscoord computedBSize =
66 aAvailSpace.BSize(mWritingMode) -
67 ComputedLogicalBorderPadding(mWritingMode).BStartEnd(mWritingMode);
68 computedBSize = std::max(0, computedBSize);
69 SetComputedBSize(computedBSize);
73 void nsTableRowFrame::InitChildReflowInput(nsPresContext& aPresContext,
74 const LogicalSize& aAvailSize,
75 bool aBorderCollapse,
76 TableCellReflowInput& aReflowInput) {
77 Maybe<LogicalMargin> collapseBorder;
78 if (aBorderCollapse) {
79 // we only reflow cells, so don't need to check frame type
80 nsBCTableCellFrame* bcCellFrame = (nsBCTableCellFrame*)aReflowInput.mFrame;
81 if (bcCellFrame) {
82 collapseBorder.emplace(
83 bcCellFrame->GetBorderWidth(aReflowInput.GetWritingMode()));
86 aReflowInput.Init(&aPresContext, Nothing(), collapseBorder);
87 aReflowInput.FixUp(aAvailSize);
90 void nsTableRowFrame::SetFixedBSize(nscoord aValue) {
91 nscoord bsize = std::max(0, aValue);
92 if (HasFixedBSize()) {
93 if (bsize > mStyleFixedBSize) {
94 mStyleFixedBSize = bsize;
96 } else {
97 mStyleFixedBSize = bsize;
98 if (bsize > 0) {
99 SetHasFixedBSize(true);
104 void nsTableRowFrame::SetPctBSize(float aPctValue, bool aForce) {
105 nscoord bsize = std::max(0, NSToCoordRound(aPctValue * 100.0f));
106 if (HasPctBSize()) {
107 if ((bsize > mStylePctBSize) || aForce) {
108 mStylePctBSize = bsize;
110 } else {
111 mStylePctBSize = bsize;
112 if (bsize > 0) {
113 SetHasPctBSize(true);
118 /* ----------- nsTableRowFrame ---------- */
120 NS_QUERYFRAME_HEAD(nsTableRowFrame)
121 NS_QUERYFRAME_ENTRY(nsTableRowFrame)
122 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
124 nsTableRowFrame::nsTableRowFrame(ComputedStyle* aStyle,
125 nsPresContext* aPresContext, ClassID aID)
126 : nsContainerFrame(aStyle, aPresContext, aID) {
127 mBits.mRowIndex = 0;
128 mBits.mHasFixedBSize = 0;
129 mBits.mHasPctBSize = 0;
130 mBits.mFirstInserted = 0;
131 ResetBSize();
134 nsTableRowFrame::~nsTableRowFrame() = default;
136 void nsTableRowFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
137 nsIFrame* aPrevInFlow) {
138 // Let the base class do its initialization
139 nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
141 NS_ASSERTION(mozilla::StyleDisplay::TableRow == StyleDisplay()->mDisplay,
142 "wrong display on table row frame");
144 if (aPrevInFlow) {
145 // Set the row index
146 nsTableRowFrame* rowFrame = (nsTableRowFrame*)aPrevInFlow;
148 SetRowIndex(rowFrame->GetRowIndex());
149 } else {
150 mWritingMode = GetTableFrame()->GetWritingMode();
154 void nsTableRowFrame::Destroy(DestroyContext& aContext) {
155 nsTableFrame::MaybeUnregisterPositionedTablePart(this);
156 nsContainerFrame::Destroy(aContext);
159 /* virtual */
160 void nsTableRowFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle) {
161 nsContainerFrame::DidSetComputedStyle(aOldComputedStyle);
162 nsTableFrame::PositionedTablePartMaybeChanged(this, aOldComputedStyle);
164 if (!aOldComputedStyle) {
165 return; // avoid the following on init
168 #ifdef ACCESSIBILITY
169 if (nsAccessibilityService* accService = GetAccService()) {
170 // If a table row's background color is now different from
171 // the background color of its previous row, it is possible our
172 // table now has alternating row colors. This changes whether or not
173 // the table is classified as a layout table or data table.
174 // We invalidate on every background color change to avoid
175 // walking the tree in search of the nearest row.
176 if (StyleBackground()->BackgroundColor(this) !=
177 aOldComputedStyle->StyleBackground()->BackgroundColor(
178 aOldComputedStyle)) {
179 // We send a notification here to invalidate the a11y cache on the
180 // table so the next fetch of IsProbablyLayoutTable() is accurate.
181 accService->TableLayoutGuessMaybeChanged(PresShell(), mContent);
184 #endif
186 nsTableFrame* tableFrame = GetTableFrame();
187 if (tableFrame->IsBorderCollapse() &&
188 tableFrame->BCRecalcNeeded(aOldComputedStyle, Style())) {
189 TableArea damageArea(0, GetRowIndex(), tableFrame->GetColCount(), 1);
190 tableFrame->AddBCDamageArea(damageArea);
194 void nsTableRowFrame::AppendFrames(ChildListID aListID,
195 nsFrameList&& aFrameList) {
196 NS_ASSERTION(aListID == FrameChildListID::Principal, "unexpected child list");
198 DrainSelfOverflowList(); // ensure the last frame is in mFrames
199 const nsFrameList::Slice& newCells =
200 mFrames.AppendFrames(nullptr, std::move(aFrameList));
202 // Add the new cell frames to the table
203 nsTableFrame* tableFrame = GetTableFrame();
204 for (nsIFrame* childFrame : newCells) {
205 NS_ASSERTION(childFrame->IsTableCellFrame(),
206 "Not a table cell frame/pseudo frame construction failure");
207 tableFrame->AppendCell(static_cast<nsTableCellFrame&>(*childFrame),
208 GetRowIndex());
211 PresShell()->FrameNeedsReflow(this, IntrinsicDirty::FrameAndAncestors,
212 NS_FRAME_HAS_DIRTY_CHILDREN);
213 tableFrame->SetGeometryDirty();
216 void nsTableRowFrame::InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame,
217 const nsLineList::iterator* aPrevFrameLine,
218 nsFrameList&& aFrameList) {
219 NS_ASSERTION(aListID == FrameChildListID::Principal, "unexpected child list");
220 NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
221 "inserting after sibling frame with different parent");
222 if (mFrames.IsEmpty() || (aPrevFrame && !aPrevFrame->GetNextSibling())) {
223 // This is actually an append (though our caller didn't figure that out),
224 // and our append codepath is both simpler/faster _and_ less buggy.
225 // https://bugzilla.mozilla.org/show_bug.cgi?id=1388898 tracks the bugginess
226 AppendFrames(aListID, std::move(aFrameList));
227 return;
230 DrainSelfOverflowList(); // ensure aPrevFrame is in mFrames
231 // Insert Frames in the frame list
232 const nsFrameList::Slice& newCells =
233 mFrames.InsertFrames(nullptr, aPrevFrame, std::move(aFrameList));
235 nsTableCellFrame* prevCellFrame =
236 static_cast<nsTableCellFrame*>(nsTableFrame::GetFrameAtOrBefore(
237 this, aPrevFrame, LayoutFrameType::TableCell));
238 nsTArray<nsTableCellFrame*> cellChildren;
239 for (nsIFrame* childFrame : newCells) {
240 NS_ASSERTION(childFrame->IsTableCellFrame(),
241 "Not a table cell frame/pseudo frame construction failure");
242 cellChildren.AppendElement(static_cast<nsTableCellFrame*>(childFrame));
244 // insert the cells into the cell map
245 int32_t colIndex = -1;
246 if (prevCellFrame) {
247 colIndex = prevCellFrame->ColIndex();
249 nsTableFrame* tableFrame = GetTableFrame();
250 tableFrame->InsertCells(cellChildren, GetRowIndex(), colIndex);
252 PresShell()->FrameNeedsReflow(this, IntrinsicDirty::FrameAndAncestors,
253 NS_FRAME_HAS_DIRTY_CHILDREN);
254 tableFrame->SetGeometryDirty();
257 void nsTableRowFrame::RemoveFrame(DestroyContext& aContext, ChildListID aListID,
258 nsIFrame* aOldFrame) {
259 NS_ASSERTION(aListID == FrameChildListID::Principal, "unexpected child list");
260 MOZ_ASSERT((nsTableCellFrame*)do_QueryFrame(aOldFrame));
262 auto* cellFrame = static_cast<nsTableCellFrame*>(aOldFrame);
263 // remove the cell from the cell map
264 nsTableFrame* tableFrame = GetTableFrame();
265 tableFrame->RemoveCell(cellFrame, GetRowIndex());
267 // Remove the frame and destroy it
268 mFrames.DestroyFrame(aContext, aOldFrame);
270 PresShell()->FrameNeedsReflow(this, IntrinsicDirty::FrameAndAncestors,
271 NS_FRAME_HAS_DIRTY_CHILDREN);
273 tableFrame->SetGeometryDirty();
276 /* virtual */
277 nsMargin nsTableRowFrame::GetUsedMargin() const { return nsMargin(0, 0, 0, 0); }
279 /* virtual */
280 nsMargin nsTableRowFrame::GetUsedBorder() const { return nsMargin(0, 0, 0, 0); }
282 /* virtual */
283 nsMargin nsTableRowFrame::GetUsedPadding() const {
284 return nsMargin(0, 0, 0, 0);
287 static nscoord GetBSizeOfRowsSpannedBelowFirst(
288 nsTableCellFrame& aTableCellFrame, nsTableFrame& aTableFrame,
289 const WritingMode aWM) {
290 nscoord bsize = 0;
291 int32_t rowSpan = aTableFrame.GetEffectiveRowSpan(aTableCellFrame);
292 // add in bsize of rows spanned beyond the 1st one
293 nsIFrame* nextRow = aTableCellFrame.GetParent()->GetNextSibling();
294 for (int32_t rowX = 1; ((rowX < rowSpan) && nextRow);) {
295 if (nextRow->IsTableRowFrame()) {
296 bsize += nextRow->BSize(aWM);
297 rowX++;
299 bsize += aTableFrame.GetRowSpacing(rowX);
300 nextRow = nextRow->GetNextSibling();
302 return bsize;
306 * Post-reflow hook. This is where the table row does its post-processing
308 void nsTableRowFrame::DidResize(ForceAlignTopForTableCell aForceAlignTop) {
309 // Resize and re-align the cell frames based on our row bsize
310 nsTableFrame* tableFrame = GetTableFrame();
312 WritingMode wm = GetWritingMode();
313 ReflowOutput desiredSize(wm);
314 desiredSize.SetSize(wm, GetLogicalSize(wm));
315 desiredSize.SetOverflowAreasToDesiredBounds();
317 nsSize containerSize = mRect.Size();
319 for (nsTableCellFrame* cellFrame = GetFirstCell(); cellFrame;
320 cellFrame = cellFrame->GetNextCell()) {
321 nscoord cellBSize = BSize(wm) + GetBSizeOfRowsSpannedBelowFirst(
322 *cellFrame, *tableFrame, wm);
324 // If the bsize for the cell has changed, we need to reset it;
325 // and in vertical-rl mode, we need to update the cell's block position
326 // to account for the containerSize, which may not have been known
327 // earlier, so we always apply it here.
328 LogicalSize cellSize = cellFrame->GetLogicalSize(wm);
329 if (cellSize.BSize(wm) != cellBSize || wm.IsVerticalRL()) {
330 nsRect cellOldRect = cellFrame->GetRect();
331 nsRect cellInkOverflow = cellFrame->InkOverflowRect();
333 if (wm.IsVerticalRL()) {
334 // Get the old position of the cell, as we want to preserve its
335 // inline coordinate.
336 LogicalPoint oldPos = cellFrame->GetLogicalPosition(wm, containerSize);
338 // The cell should normally be aligned with the row's block-start,
339 // so set the B component of the position to zero:
340 LogicalPoint newPos(wm, oldPos.I(wm), 0);
342 // ...unless relative positioning is in effect, in which case the
343 // cell may have been moved away from the row's block-start
344 if (cellFrame->IsRelativelyOrStickyPositioned()) {
345 // Find out where the cell would have been without relative
346 // positioning.
347 LogicalPoint oldNormalPos =
348 cellFrame->GetLogicalNormalPosition(wm, containerSize);
349 // The difference (if any) between oldPos and oldNormalPos reflects
350 // relative positioning that was applied to the cell, and which we
351 // need to incorporate when resetting the position.
352 newPos.B(wm) = oldPos.B(wm) - oldNormalPos.B(wm);
355 if (oldPos != newPos) {
356 cellFrame->SetPosition(wm, newPos, containerSize);
357 nsTableFrame::RePositionViews(cellFrame);
361 cellSize.BSize(wm) = cellBSize;
362 cellFrame->SetSize(wm, cellSize);
364 if (tableFrame->IsBorderCollapse()) {
365 nsTableFrame::InvalidateTableFrame(cellFrame, cellOldRect,
366 cellInkOverflow, false);
370 // realign cell content based on the new bsize. We might be able to
371 // skip this if the bsize didn't change... maybe. Hard to tell.
372 cellFrame->BlockDirAlignChild(wm, mMaxCellAscent, aForceAlignTop);
374 // Always store the overflow, even if the height didn't change, since
375 // we'll lose part of our overflow area otherwise.
376 ConsiderChildOverflow(desiredSize.mOverflowAreas, cellFrame);
378 // Note that if the cell's *content* needs to change in response
379 // to this height, it will get a special bsize reflow.
381 FinishAndStoreOverflow(&desiredSize);
382 if (HasView()) {
383 nsContainerFrame::SyncFrameViewAfterReflow(PresContext(), this, GetView(),
384 desiredSize.InkOverflow(),
385 ReflowChildFlags::Default);
387 // Let our base class do the usual work
390 // returns max-ascent amongst all cells that have 'vertical-align: baseline'
391 // *including* cells with rowspans
392 nscoord nsTableRowFrame::GetMaxCellAscent() const { return mMaxCellAscent; }
394 Maybe<nscoord> nsTableRowFrame::GetRowBaseline(WritingMode aWM) {
395 if (mMaxCellAscent) {
396 return Some(mMaxCellAscent);
399 // If we get here, we don't have a baseline on any of the cells in this row.
400 if (aWM.IsCentralBaseline()) {
401 return Nothing{};
403 nscoord ascent = 0;
404 for (nsIFrame* childFrame : mFrames) {
405 MOZ_ASSERT(childFrame->IsTableCellFrame());
406 nscoord s = Baseline::SynthesizeBOffsetFromContentBox(
407 childFrame, aWM, BaselineSharingGroup::First);
408 ascent = std::max(ascent, s);
410 return Some(ascent);
413 nscoord nsTableRowFrame::GetInitialBSize(nscoord aPctBasis) const {
414 nscoord bsize = 0;
415 if ((aPctBasis > 0) && HasPctBSize()) {
416 bsize = NSToCoordRound(GetPctBSize() * (float)aPctBasis);
418 if (HasFixedBSize()) {
419 bsize = std::max(bsize, GetFixedBSize());
421 return std::max(bsize, GetContentBSize());
424 void nsTableRowFrame::ResetBSize() {
425 SetHasFixedBSize(false);
426 SetHasPctBSize(false);
427 SetFixedBSize(0);
428 SetPctBSize(0);
429 SetContentBSize(0);
431 mMaxCellAscent = 0;
432 mMaxCellDescent = 0;
435 void nsTableRowFrame::UpdateBSize(nscoord aBSize, nscoord aAscent,
436 nscoord aDescent, nsTableFrame* aTableFrame,
437 nsTableCellFrame* aCellFrame) {
438 if (!aTableFrame || !aCellFrame) {
439 NS_ASSERTION(false, "invalid call");
440 return;
443 if (aBSize == NS_UNCONSTRAINEDSIZE) {
444 return;
447 if (GetInitialBSize() < aBSize &&
448 aTableFrame->GetEffectiveRowSpan(*aCellFrame) == 1) {
449 SetContentBSize(aBSize);
452 if (aCellFrame->HasVerticalAlignBaseline()) {
453 NS_ASSERTION(
454 aAscent != NS_UNCONSTRAINEDSIZE && aDescent != NS_UNCONSTRAINEDSIZE,
455 "invalid call");
456 // see if this is a long ascender
457 if (mMaxCellAscent < aAscent) {
458 mMaxCellAscent = aAscent;
460 // see if this is a long descender and without rowspan
461 if (mMaxCellDescent < aDescent) {
462 if (aTableFrame->GetEffectiveRowSpan(*aCellFrame) == 1) {
463 mMaxCellDescent = aDescent;
469 nscoord nsTableRowFrame::CalcBSize(const ReflowInput& aReflowInput) {
470 nsTableFrame* tableFrame = GetTableFrame();
472 ResetBSize();
473 const nscoord computedBSize = aReflowInput.ComputedBSize();
474 if (computedBSize != NS_UNCONSTRAINEDSIZE && computedBSize > 0) {
475 SetFixedBSize(computedBSize);
478 WritingMode wm = aReflowInput.GetWritingMode();
479 const nsStylePosition* position = StylePosition();
480 const auto& bsizeStyleCoord = position->BSize(wm);
481 if (bsizeStyleCoord.ConvertsToLength()) {
482 SetFixedBSize(bsizeStyleCoord.ToLength());
483 } else if (bsizeStyleCoord.ConvertsToPercentage()) {
484 SetPctBSize(bsizeStyleCoord.ToPercentage());
487 for (nsTableCellFrame* kidFrame = GetFirstCell(); kidFrame;
488 kidFrame = kidFrame->GetNextCell()) {
489 MOZ_ASSERT(kidFrame->GetWritingMode() == wm);
490 LogicalSize desSize = kidFrame->GetDesiredSize();
491 if (NS_UNCONSTRAINEDSIZE == aReflowInput.AvailableBSize() &&
492 !GetPrevInFlow()) {
493 desSize.BSize(wm) = CalcCellActualBSize(kidFrame, desSize.BSize(wm), wm);
495 // bsize may have changed, adjust descent to absorb any excess difference
496 nscoord ascent;
497 if (!kidFrame->PrincipalChildList()
498 .FirstChild()
499 ->PrincipalChildList()
500 .FirstChild()) {
501 ascent = desSize.BSize(wm);
502 } else {
503 ascent = kidFrame->GetCellBaseline();
505 nscoord descent = desSize.BSize(wm) - ascent;
506 UpdateBSize(desSize.BSize(wm), ascent, descent, tableFrame, kidFrame);
508 return GetInitialBSize();
511 void nsTableRowFrame::PaintCellBackgroundsForFrame(
512 nsIFrame* aFrame, nsDisplayListBuilder* aBuilder,
513 const nsDisplayListSet& aLists, const nsPoint& aOffset) {
514 // Compute background rect by iterating all cell frame.
515 const nsPoint toReferenceFrame = aBuilder->ToReferenceFrame(aFrame);
516 for (nsTableCellFrame* cell = GetFirstCell(); cell;
517 cell = cell->GetNextCell()) {
518 if (!cell->ShouldPaintBackground(aBuilder)) {
519 continue;
522 auto cellRect =
523 cell->GetRectRelativeToSelf() + cell->GetNormalPosition() + aOffset;
524 if (!aBuilder->GetDirtyRect().Intersects(cellRect)) {
525 continue;
527 cellRect += toReferenceFrame;
528 nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
529 aBuilder, aFrame, cellRect, aLists.BorderBackground(), true,
530 aFrame->GetRectRelativeToSelf() + toReferenceFrame, cell);
534 void nsTableRowFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
535 const nsDisplayListSet& aLists) {
536 DisplayOutsetBoxShadow(aBuilder, aLists.BorderBackground());
538 PaintCellBackgroundsForFrame(this, aBuilder, aLists);
540 DisplayInsetBoxShadow(aBuilder, aLists.BorderBackground());
542 DisplayOutline(aBuilder, aLists);
544 for (nsIFrame* kid : PrincipalChildList()) {
545 BuildDisplayListForChild(aBuilder, kid, aLists);
549 LogicalSides nsTableRowFrame::GetLogicalSkipSides() const {
550 LogicalSides skip(mWritingMode);
551 if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==
552 StyleBoxDecorationBreak::Clone)) {
553 return skip;
556 if (GetPrevInFlow()) {
557 skip += LogicalSide::BStart;
559 if (GetNextInFlow()) {
560 skip += LogicalSide::BEnd;
562 return skip;
565 nscoord nsTableRowFrame::CalcCellActualBSize(nsTableCellFrame* aCellFrame,
566 const nscoord& aDesiredBSize,
567 WritingMode aWM) {
568 nscoord specifiedBSize = 0;
570 // Get the bsize specified in the style information
571 const nsStylePosition* position = aCellFrame->StylePosition();
573 int32_t rowSpan = GetTableFrame()->GetEffectiveRowSpan(*aCellFrame);
575 const auto& bsizeStyleCoord = position->BSize(aWM);
576 if (bsizeStyleCoord.ConvertsToLength()) {
577 // In quirks mode, table cell bsize should always be border-box.
578 // https://quirks.spec.whatwg.org/#the-table-cell-height-box-sizing-quirk
579 specifiedBSize = bsizeStyleCoord.ToLength();
580 if (PresContext()->CompatibilityMode() != eCompatibility_NavQuirks &&
581 position->mBoxSizing == StyleBoxSizing::Content) {
582 specifiedBSize +=
583 aCellFrame->GetLogicalUsedBorderAndPadding(aWM).BStartEnd(aWM);
586 if (1 == rowSpan) {
587 SetFixedBSize(specifiedBSize);
589 } else if (bsizeStyleCoord.ConvertsToPercentage()) {
590 if (1 == rowSpan) {
591 SetPctBSize(bsizeStyleCoord.ToPercentage());
595 // If the specified bsize is greater than the desired bsize,
596 // then use the specified bsize
597 return std::max(specifiedBSize, aDesiredBSize);
600 // Calculates the available isize for the table cell based on the known
601 // column isizes taking into account column spans and column spacing
602 static nscoord CalcAvailISize(nsTableFrame& aTableFrame,
603 nsTableCellFrame& aCellFrame) {
604 nscoord cellAvailISize = 0;
605 uint32_t colIndex = aCellFrame.ColIndex();
606 int32_t colspan = aTableFrame.GetEffectiveColSpan(aCellFrame);
607 NS_ASSERTION(colspan > 0, "effective colspan should be positive");
608 nsTableFrame* fifTable =
609 static_cast<nsTableFrame*>(aTableFrame.FirstInFlow());
611 for (int32_t spanX = 0; spanX < colspan; spanX++) {
612 cellAvailISize += fifTable->GetColumnISizeFromFirstInFlow(colIndex + spanX);
613 if (spanX > 0 && aTableFrame.ColumnHasCellSpacingBefore(colIndex + spanX)) {
614 cellAvailISize += aTableFrame.GetColSpacing(colIndex + spanX - 1);
617 return cellAvailISize;
620 static nscoord GetSpaceBetween(int32_t aPrevColIndex, int32_t aColIndex,
621 int32_t aColSpan, nsTableFrame& aTableFrame,
622 bool aCheckVisibility) {
623 nscoord space = 0;
624 int32_t colIdx;
625 nsTableFrame* fifTable =
626 static_cast<nsTableFrame*>(aTableFrame.FirstInFlow());
627 for (colIdx = aPrevColIndex + 1; aColIndex > colIdx; colIdx++) {
628 bool isCollapsed = false;
629 if (!aCheckVisibility) {
630 space += fifTable->GetColumnISizeFromFirstInFlow(colIdx);
631 } else {
632 nsTableColFrame* colFrame = aTableFrame.GetColFrame(colIdx);
633 const nsStyleVisibility* colVis = colFrame->StyleVisibility();
634 bool collapseCol = StyleVisibility::Collapse == colVis->mVisible;
635 nsIFrame* cgFrame = colFrame->GetParent();
636 const nsStyleVisibility* groupVis = cgFrame->StyleVisibility();
637 bool collapseGroup = StyleVisibility::Collapse == groupVis->mVisible;
638 isCollapsed = collapseCol || collapseGroup;
639 if (!isCollapsed) {
640 space += fifTable->GetColumnISizeFromFirstInFlow(colIdx);
643 if (!isCollapsed && aTableFrame.ColumnHasCellSpacingBefore(colIdx)) {
644 space += aTableFrame.GetColSpacing(colIdx - 1);
647 return space;
650 // subtract the bsizes of aRow's prev in flows from the unpaginated bsize
651 static nscoord CalcBSizeFromUnpaginatedBSize(nsTableRowFrame& aRow,
652 WritingMode aWM) {
653 nscoord bsize = 0;
654 nsTableRowFrame* firstInFlow =
655 static_cast<nsTableRowFrame*>(aRow.FirstInFlow());
656 if (firstInFlow->HasUnpaginatedBSize()) {
657 bsize = firstInFlow->GetUnpaginatedBSize();
658 for (nsIFrame* prevInFlow = aRow.GetPrevInFlow(); prevInFlow;
659 prevInFlow = prevInFlow->GetPrevInFlow()) {
660 bsize -= prevInFlow->BSize(aWM);
663 return std::max(bsize, 0);
666 void nsTableRowFrame::ReflowChildren(nsPresContext* aPresContext,
667 ReflowOutput& aDesiredSize,
668 const ReflowInput& aReflowInput,
669 nsTableFrame& aTableFrame,
670 nsReflowStatus& aStatus) {
671 aStatus.Reset();
673 // XXXldb Should we be checking constrained bsize instead?
674 const bool isPaginated = aPresContext->IsPaginated();
675 const bool borderCollapse = aTableFrame.IsBorderCollapse();
677 int32_t cellColSpan =
678 1; // must be defined here so it's set properly for non-cell kids
680 // remember the col index of the previous cell to handle rowspans into this
681 // row
682 int32_t prevColIndex = -1;
683 nscoord iCoord = 0; // running total of children inline-coord offset
685 // This computes the max of all cell bsizes
686 nscoord cellMaxBSize = 0;
688 // Reflow each of our existing cell frames
689 WritingMode wm = aReflowInput.GetWritingMode();
690 nsSize containerSize = aReflowInput.ComputedSizeAsContainerIfConstrained();
692 for (nsTableCellFrame* kidFrame = GetFirstCell(); kidFrame;
693 kidFrame = kidFrame->GetNextCell()) {
694 // See if we should only reflow the dirty child frames
695 bool doReflowChild = true;
696 if (!aReflowInput.ShouldReflowAllKids() && !aTableFrame.IsGeometryDirty() &&
697 !kidFrame->IsSubtreeDirty()) {
698 if (!aReflowInput.mFlags.mSpecialBSizeReflow) {
699 doReflowChild = false;
701 } else if (NS_UNCONSTRAINEDSIZE != aReflowInput.AvailableBSize()) {
702 // We don't reflow a rowspan >1 cell here with a constrained bsize.
703 // That happens in nsTableRowGroupFrame::SplitSpanningCells.
704 if (aTableFrame.GetEffectiveRowSpan(*kidFrame) > 1) {
705 doReflowChild = false;
708 if (aReflowInput.mFlags.mSpecialBSizeReflow && !isPaginated &&
709 !kidFrame->HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE)) {
710 continue;
713 uint32_t cellColIndex = kidFrame->ColIndex();
714 cellColSpan = aTableFrame.GetEffectiveColSpan(*kidFrame);
716 // If the adjacent cell is in a prior row (because of a rowspan) add in the
717 // space NOTE: prevColIndex can be -1 here.
718 if (prevColIndex != (static_cast<int32_t>(cellColIndex) - 1)) {
719 iCoord += GetSpaceBetween(prevColIndex, cellColIndex, cellColSpan,
720 aTableFrame, false);
723 // remember the rightmost (ltr) or leftmost (rtl) column this cell spans
724 // into
725 prevColIndex = cellColIndex + (cellColSpan - 1);
727 // Reflow the child frame
728 nsRect kidRect = kidFrame->GetRect();
729 LogicalPoint origKidNormalPosition =
730 kidFrame->GetLogicalNormalPosition(wm, containerSize);
732 nsRect kidInkOverflow = kidFrame->InkOverflowRect();
733 LogicalPoint kidPosition(wm, iCoord, 0);
734 bool firstReflow = kidFrame->HasAnyStateBits(NS_FRAME_FIRST_REFLOW);
736 if (doReflowChild) {
737 // Calculate the available isize for the table cell using the known
738 // column isizes
739 nscoord availCellISize = CalcAvailISize(aTableFrame, *kidFrame);
741 Maybe<TableCellReflowInput> kidReflowInput;
742 ReflowOutput desiredSize(aReflowInput);
744 // If the avail isize is not the same as last time we reflowed the cell or
745 // the cell wants to be bigger than what was available last time or
746 // it is a style change reflow or we are printing, then we must reflow the
747 // cell. Otherwise we can skip the reflow.
748 // XXXldb Why is this condition distinct from doReflowChild above?
749 NS_ASSERTION(kidFrame->GetWritingMode() == wm,
750 "expected consistent writing-mode within table");
751 LogicalSize cellDesiredSize = kidFrame->GetDesiredSize();
752 if (availCellISize != kidFrame->GetPriorAvailISize() ||
753 cellDesiredSize.ISize(wm) > kidFrame->GetPriorAvailISize() ||
754 HasAnyStateBits(NS_FRAME_IS_DIRTY) || isPaginated ||
755 kidFrame->IsSubtreeDirty() ||
756 // See if it needs a special reflow, or if it had one that we need to
757 // undo.
758 kidFrame->HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE) ||
759 kidFrame->BCBordersChanged() || HasPctBSize()) {
760 // Reflow the cell to fit the available isize, bsize
761 // XXX The old IR_ChildIsDirty code used availCellISize here.
762 LogicalSize kidAvailSize(wm, availCellISize,
763 aReflowInput.AvailableBSize());
765 // Reflow the child
766 kidReflowInput.emplace(aPresContext, aReflowInput, kidFrame,
767 kidAvailSize,
768 ReflowInput::InitFlag::CallerWillInit);
769 InitChildReflowInput(*aPresContext, kidAvailSize, borderCollapse,
770 *kidReflowInput);
772 nsReflowStatus status;
773 ReflowChild(kidFrame, aPresContext, desiredSize, *kidReflowInput, wm,
774 kidPosition, containerSize, ReflowChildFlags::Default,
775 status);
777 // allow the table to determine if/how the table needs to be rebalanced
778 // If any of the cells are not complete, then we're not complete
779 if (status.IsIncomplete()) {
780 aStatus.Reset();
781 aStatus.SetIncomplete();
783 } else {
784 if (iCoord != origKidNormalPosition.I(wm)) {
785 kidFrame->InvalidateFrameSubtree();
788 desiredSize.SetSize(wm, cellDesiredSize);
789 desiredSize.mOverflowAreas = kidFrame->GetOverflowAreas();
791 // if we are in a floated table, our position is not yet established, so
792 // we cannot reposition our views the containing block will do this for
793 // us after positioning the table
794 if (!aTableFrame.IsFloating()) {
795 // Because we may have moved the frame we need to make sure any views
796 // are positioned properly. We have to do this, because any one of our
797 // parent frames could have moved and we have no way of knowing...
798 nsTableFrame::RePositionViews(kidFrame);
802 if (NS_UNCONSTRAINEDSIZE == aReflowInput.AvailableBSize()) {
803 if (!GetPrevInFlow()) {
804 desiredSize.BSize(wm) =
805 CalcCellActualBSize(kidFrame, desiredSize.BSize(wm), wm);
807 // bsize may have changed, adjust descent to absorb any excess
808 // difference
809 nscoord ascent;
810 if (!kidFrame->PrincipalChildList()
811 .FirstChild()
812 ->PrincipalChildList()
813 .FirstChild()) {
814 ascent = desiredSize.BSize(wm);
815 } else {
816 ascent = kidFrame->GetCellBaseline();
818 nscoord descent = desiredSize.BSize(wm) - ascent;
819 UpdateBSize(desiredSize.BSize(wm), ascent, descent, &aTableFrame,
820 kidFrame);
821 } else {
822 cellMaxBSize = std::max(cellMaxBSize, desiredSize.BSize(wm));
823 int32_t rowSpan = aTableFrame.GetEffectiveRowSpan(*kidFrame);
824 if (1 == rowSpan) {
825 SetContentBSize(cellMaxBSize);
829 // Place the child
830 desiredSize.ISize(wm) = availCellISize;
832 ReflowChildFlags flags = ReflowChildFlags::Default;
834 if (kidReflowInput) {
835 // We reflowed. Apply relative positioning in the normal way.
836 flags = ReflowChildFlags::ApplyRelativePositioning;
837 } else if (kidFrame->IsRelativelyOrStickyPositioned()) {
838 // We didn't reflow. Do the positioning part of what
839 // MovePositionBy does internally. (This codepath should really
840 // be merged into the else below if we can.)
841 nsMargin* computedOffsetProp =
842 kidFrame->GetProperty(nsIFrame::ComputedOffsetProperty());
844 // On our fist reflow sticky children may not have the property yet (we
845 // need to reflow the children first to size the scroll frame).
846 LogicalMargin computedOffsets(
847 wm, computedOffsetProp ? *computedOffsetProp : nsMargin());
848 ReflowInput::ApplyRelativePositioning(kidFrame, wm, computedOffsets,
849 &kidPosition, containerSize);
852 // In vertical-rl mode, we are likely to have containerSize.width = 0
853 // because ComputedWidth() was NS_UNCONSTRAINEDSIZE.
854 // For cases where that's wrong, we will fix up the position later.
855 FinishReflowChild(kidFrame, aPresContext, desiredSize,
856 kidReflowInput.ptrOr(nullptr), wm, kidPosition,
857 containerSize, flags);
859 nsTableFrame* tableFrame = GetTableFrame();
860 if (tableFrame->IsBorderCollapse()) {
861 nsTableFrame::InvalidateTableFrame(kidFrame, kidRect, kidInkOverflow,
862 firstReflow);
865 iCoord += desiredSize.ISize(wm);
866 } else {
867 if (iCoord != origKidNormalPosition.I(wm)) {
868 // Invalidate the old position
869 kidFrame->InvalidateFrameSubtree();
870 // Move to the new position. As above, we need to account for relative
871 // positioning.
872 kidFrame->MovePositionBy(
873 wm, LogicalPoint(wm, iCoord - origKidNormalPosition.I(wm), 0));
874 nsTableFrame::RePositionViews(kidFrame);
875 // invalidate the new position
876 kidFrame->InvalidateFrameSubtree();
878 // we need to account for the cell's isize even if it isn't reflowed
879 iCoord += kidFrame->ISize(wm);
881 if (kidFrame->GetNextInFlow()) {
882 aStatus.Reset();
883 aStatus.SetIncomplete();
886 ConsiderChildOverflow(aDesiredSize.mOverflowAreas, kidFrame);
887 iCoord += aTableFrame.GetColSpacing(cellColIndex);
890 // Just set our isize to what was available.
891 // The table will calculate the isize and not use our value.
892 aDesiredSize.ISize(wm) = aReflowInput.AvailableISize();
894 if (aReflowInput.mFlags.mSpecialBSizeReflow) {
895 aDesiredSize.BSize(wm) = BSize(wm);
896 } else if (NS_UNCONSTRAINEDSIZE == aReflowInput.AvailableBSize()) {
897 aDesiredSize.BSize(wm) = CalcBSize(aReflowInput);
898 if (GetPrevInFlow()) {
899 nscoord bsize = CalcBSizeFromUnpaginatedBSize(*this, wm);
900 aDesiredSize.BSize(wm) = std::max(aDesiredSize.BSize(wm), bsize);
901 } else {
902 if (isPaginated && HasStyleBSize()) {
903 // set the unpaginated bsize so next in flows can try to honor it
904 SetUnpaginatedBSize(aDesiredSize.BSize(wm));
906 if (isPaginated && HasUnpaginatedBSize()) {
907 aDesiredSize.BSize(wm) =
908 std::max(aDesiredSize.BSize(wm), GetUnpaginatedBSize());
911 } else { // constrained bsize, paginated
912 // Compute the bsize we should have from style (subtracting the
913 // bsize from our prev-in-flows from the style bsize)
914 nscoord styleBSize = CalcBSizeFromUnpaginatedBSize(*this, wm);
915 if (styleBSize > aReflowInput.AvailableBSize()) {
916 styleBSize = aReflowInput.AvailableBSize();
917 aStatus.SetIncomplete();
919 aDesiredSize.BSize(wm) = std::max(cellMaxBSize, styleBSize);
922 if (wm.IsVerticalRL()) {
923 // Any children whose width was not the same as our final
924 // aDesiredSize.BSize will have been misplaced earlier at the
925 // FinishReflowChild stage. So fix them up now.
926 for (nsIFrame* kidFrame : mFrames) {
927 if (kidFrame->BSize(wm) != aDesiredSize.BSize(wm)) {
928 kidFrame->MovePositionBy(
930 LogicalPoint(wm, 0, kidFrame->BSize(wm) - aDesiredSize.BSize(wm)));
931 nsTableFrame::RePositionViews(kidFrame);
932 // Do we need to InvalidateFrameSubtree() here?
937 aDesiredSize.UnionOverflowAreasWithDesiredBounds();
938 FinishAndStoreOverflow(&aDesiredSize);
941 /** Layout the entire row.
942 * This method stacks cells in the inline dir according to HTML 4.0 rules.
944 void nsTableRowFrame::Reflow(nsPresContext* aPresContext,
945 ReflowOutput& aDesiredSize,
946 const ReflowInput& aReflowInput,
947 nsReflowStatus& aStatus) {
948 MarkInReflow();
949 DO_GLOBAL_REFLOW_COUNT("nsTableRowFrame");
950 MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
952 WritingMode wm = aReflowInput.GetWritingMode();
954 nsTableFrame* tableFrame = GetTableFrame();
955 const nsStyleVisibility* rowVis = StyleVisibility();
956 bool collapseRow = StyleVisibility::Collapse == rowVis->mVisible;
957 if (collapseRow) {
958 tableFrame->SetNeedToCollapse(true);
961 // see if a special bsize reflow needs to occur due to having a pct bsize
962 nsTableFrame::CheckRequestSpecialBSizeReflow(aReflowInput);
964 // See if we have a cell with specified/pct bsize
965 InitHasCellWithStyleBSize(tableFrame);
967 ReflowChildren(aPresContext, aDesiredSize, aReflowInput, *tableFrame,
968 aStatus);
970 if (aPresContext->IsPaginated() && !aStatus.IsFullyComplete() &&
971 ShouldAvoidBreakInside(aReflowInput)) {
972 aStatus.SetInlineLineBreakBeforeAndReset();
975 // Just set our isize to what was available.
976 // The table will calculate the isize and not use our value.
977 aDesiredSize.ISize(wm) = aReflowInput.AvailableISize();
979 // If our parent is in initial reflow, it'll handle invalidating our
980 // entire overflow rect.
981 if (!GetParent()->HasAnyStateBits(NS_FRAME_FIRST_REFLOW) &&
982 nsSize(aDesiredSize.Width(), aDesiredSize.Height()) != mRect.Size()) {
983 InvalidateFrame();
986 // Any absolutely-positioned children will get reflowed in
987 // nsIFrame::FixupPositionedTableParts in another pass, so propagate our
988 // dirtiness to them before our parent clears our dirty bits.
989 PushDirtyBitToAbsoluteFrames();
992 nscoord nsTableRowFrame::ReflowCellFrame(nsPresContext* aPresContext,
993 const ReflowInput& aReflowInput,
994 bool aIsTopOfPage,
995 nsTableCellFrame* aCellFrame,
996 nscoord aAvailableBSize,
997 nsReflowStatus& aStatus) {
998 MOZ_ASSERT(aPresContext->IsPaginated(),
999 "ReflowCellFrame currently supports only paged media!");
1000 MOZ_ASSERT(aAvailableBSize != NS_UNCONSTRAINEDSIZE,
1001 "Why split cell frame if available bsize is unconstrained?");
1002 WritingMode wm = aReflowInput.GetWritingMode();
1004 // Reflow the cell frame with the specified height. Use the existing width
1005 nsSize containerSize = aCellFrame->GetSize();
1006 LogicalRect cellRect = aCellFrame->GetLogicalRect(wm, containerSize);
1007 nsRect cellInkOverflow = aCellFrame->InkOverflowRect();
1009 LogicalSize cellSize = cellRect.Size(wm);
1010 LogicalSize availSize(wm, cellRect.ISize(wm), aAvailableBSize);
1011 bool borderCollapse = GetTableFrame()->IsBorderCollapse();
1012 NS_ASSERTION(aCellFrame->GetWritingMode() == wm,
1013 "expected consistent writing-mode within table");
1014 TableCellReflowInput cellReflowInput(aPresContext, aReflowInput, aCellFrame,
1015 availSize,
1016 ReflowInput::InitFlag::CallerWillInit);
1017 InitChildReflowInput(*aPresContext, availSize, borderCollapse,
1018 cellReflowInput);
1019 cellReflowInput.mFlags.mIsTopOfPage = aIsTopOfPage;
1021 ReflowOutput desiredSize(aReflowInput);
1023 ReflowChild(aCellFrame, aPresContext, desiredSize, cellReflowInput, 0, 0,
1024 ReflowChildFlags::NoMoveFrame, aStatus);
1025 const bool isTruncated =
1026 aAvailableBSize < desiredSize.BSize(wm) &&
1027 !aIsTopOfPage; // XXX Is !aIsTopOfPage check really necessary?
1028 const bool isCompleteAndNotTruncated = aStatus.IsComplete() && !isTruncated;
1029 if (isCompleteAndNotTruncated) {
1030 desiredSize.BSize(wm) = aAvailableBSize;
1032 aCellFrame->SetSize(
1033 wm, LogicalSize(wm, cellSize.ISize(wm), desiredSize.BSize(wm)));
1035 // Note: BlockDirAlignChild can affect the overflow rect.
1036 // XXX What happens if this cell has 'vertical-align: baseline' ?
1037 // XXX Why is it assumed that the cell's ascent hasn't changed ?
1038 if (isCompleteAndNotTruncated) {
1039 aCellFrame->BlockDirAlignChild(wm, mMaxCellAscent,
1040 ForceAlignTopForTableCell::Yes);
1043 nsTableFrame::InvalidateTableFrame(
1044 aCellFrame, cellRect.GetPhysicalRect(wm, containerSize), cellInkOverflow,
1045 aCellFrame->HasAnyStateBits(NS_FRAME_FIRST_REFLOW));
1047 aCellFrame->DidReflow(aPresContext, nullptr);
1049 return desiredSize.BSize(wm);
1052 nscoord nsTableRowFrame::CollapseRowIfNecessary(nscoord aRowOffset,
1053 nscoord aISize,
1054 bool aCollapseGroup,
1055 bool& aDidCollapse) {
1056 const nsStyleVisibility* rowVis = StyleVisibility();
1057 bool collapseRow = StyleVisibility::Collapse == rowVis->mVisible;
1058 nsTableFrame* tableFrame =
1059 static_cast<nsTableFrame*>(GetTableFrame()->FirstInFlow());
1060 if (collapseRow) {
1061 tableFrame->SetNeedToCollapse(true);
1064 if (aRowOffset != 0) {
1065 // We're moving, so invalidate our old position
1066 InvalidateFrameSubtree();
1069 WritingMode wm = GetWritingMode();
1071 nsSize parentSize = GetParent()->GetSize();
1072 LogicalRect rowRect = GetLogicalRect(wm, parentSize);
1073 nsRect oldRect = mRect;
1074 nsRect oldInkOverflow = InkOverflowRect();
1076 rowRect.BStart(wm) -= aRowOffset;
1077 rowRect.ISize(wm) = aISize;
1078 OverflowAreas overflow;
1079 nscoord shift = 0;
1080 nsSize containerSize = mRect.Size();
1082 if (aCollapseGroup || collapseRow) {
1083 aDidCollapse = true;
1084 shift = rowRect.BSize(wm);
1085 nsTableCellFrame* cellFrame = GetFirstCell();
1086 if (cellFrame) {
1087 uint32_t rowIndex = cellFrame->RowIndex();
1088 shift += tableFrame->GetRowSpacing(rowIndex);
1089 while (cellFrame) {
1090 LogicalRect cRect = cellFrame->GetLogicalRect(wm, containerSize);
1091 // If aRowOffset != 0, there's no point in invalidating the cells, since
1092 // we've already invalidated our overflow area. Note that we _do_ still
1093 // need to invalidate if our row is not moving, because the cell might
1094 // span out of this row, so invalidating our row rect won't do enough.
1095 if (aRowOffset == 0) {
1096 InvalidateFrame();
1098 cRect.BSize(wm) = 0;
1099 cellFrame->SetRect(wm, cRect, containerSize);
1100 cellFrame = cellFrame->GetNextCell();
1102 } else {
1103 shift += tableFrame->GetRowSpacing(GetRowIndex());
1105 rowRect.BSize(wm) = 0;
1106 } else { // row is not collapsed
1107 // remember the col index of the previous cell to handle rowspans into this
1108 // row
1109 int32_t prevColIndex = -1;
1110 nscoord iPos = 0; // running total of children inline-axis offset
1111 nsTableFrame* fifTable =
1112 static_cast<nsTableFrame*>(tableFrame->FirstInFlow());
1114 for (nsTableCellFrame* cellFrame = GetFirstCell(); cellFrame;
1115 cellFrame = cellFrame->GetNextCell()) {
1116 uint32_t cellColIndex = cellFrame->ColIndex();
1117 int32_t cellColSpan = tableFrame->GetEffectiveColSpan(*cellFrame);
1119 // If the adjacent cell is in a prior row (because of a rowspan) add in
1120 // the space
1121 // NOTE: prevColIndex can be -1 here.
1122 if (prevColIndex != (static_cast<int32_t>(cellColIndex) - 1)) {
1123 iPos += GetSpaceBetween(prevColIndex, cellColIndex, cellColSpan,
1124 *tableFrame, true);
1126 LogicalRect cRect(wm, iPos, 0, 0, rowRect.BSize(wm));
1128 // remember the last (iend-wards-most) column this cell spans into
1129 prevColIndex = cellColIndex + cellColSpan - 1;
1130 int32_t actualColSpan = cellColSpan;
1131 bool isVisible = false;
1132 for (int32_t colIdx = cellColIndex; actualColSpan > 0;
1133 colIdx++, actualColSpan--) {
1134 nsTableColFrame* colFrame = tableFrame->GetColFrame(colIdx);
1135 const nsStyleVisibility* colVis = colFrame->StyleVisibility();
1136 bool collapseCol = StyleVisibility::Collapse == colVis->mVisible;
1137 nsIFrame* cgFrame = colFrame->GetParent();
1138 const nsStyleVisibility* groupVis = cgFrame->StyleVisibility();
1139 bool collapseGroup = StyleVisibility::Collapse == groupVis->mVisible;
1140 bool isCollapsed = collapseCol || collapseGroup;
1141 if (!isCollapsed) {
1142 cRect.ISize(wm) += fifTable->GetColumnISizeFromFirstInFlow(colIdx);
1143 isVisible = true;
1144 if ((actualColSpan > 1)) {
1145 nsTableColFrame* nextColFrame = tableFrame->GetColFrame(colIdx + 1);
1146 const nsStyleVisibility* nextColVis =
1147 nextColFrame->StyleVisibility();
1148 if (StyleVisibility::Collapse != nextColVis->mVisible &&
1149 tableFrame->ColumnHasCellSpacingBefore(colIdx + 1)) {
1150 cRect.ISize(wm) += tableFrame->GetColSpacing(cellColIndex);
1155 iPos += cRect.ISize(wm);
1156 if (isVisible) {
1157 iPos += tableFrame->GetColSpacing(cellColIndex);
1159 int32_t actualRowSpan = tableFrame->GetEffectiveRowSpan(*cellFrame);
1160 nsTableRowFrame* rowFrame = GetNextRow();
1161 for (actualRowSpan--; actualRowSpan > 0 && rowFrame; actualRowSpan--) {
1162 const nsStyleVisibility* nextRowVis = rowFrame->StyleVisibility();
1163 bool collapseNextRow =
1164 StyleVisibility::Collapse == nextRowVis->mVisible;
1165 if (!collapseNextRow) {
1166 LogicalRect nextRect = rowFrame->GetLogicalRect(wm, containerSize);
1167 cRect.BSize(wm) += nextRect.BSize(wm) +
1168 tableFrame->GetRowSpacing(rowFrame->GetRowIndex());
1170 rowFrame = rowFrame->GetNextRow();
1173 nsRect oldCellRect = cellFrame->GetRect();
1174 LogicalPoint oldCellNormalPos =
1175 cellFrame->GetLogicalNormalPosition(wm, containerSize);
1177 nsRect oldCellInkOverflow = cellFrame->InkOverflowRect();
1179 if (aRowOffset == 0 && cRect.Origin(wm) != oldCellNormalPos) {
1180 // We're moving the cell. Invalidate the old overflow area
1181 cellFrame->InvalidateFrameSubtree();
1184 cellFrame->MovePositionBy(wm, cRect.Origin(wm) - oldCellNormalPos);
1185 cellFrame->SetSize(wm, cRect.Size(wm));
1187 // XXXbz This looks completely bogus in the cases when we didn't
1188 // collapse the cell!
1189 LogicalRect cellBounds(wm, 0, 0, cRect.ISize(wm), cRect.BSize(wm));
1190 nsRect cellPhysicalBounds = cellBounds.GetPhysicalRect(wm, containerSize);
1191 OverflowAreas cellOverflow(cellPhysicalBounds, cellPhysicalBounds);
1192 cellFrame->FinishAndStoreOverflow(cellOverflow,
1193 cRect.Size(wm).GetPhysicalSize(wm));
1194 nsTableFrame::RePositionViews(cellFrame);
1195 ConsiderChildOverflow(overflow, cellFrame);
1197 if (aRowOffset == 0) {
1198 nsTableFrame::InvalidateTableFrame(cellFrame, oldCellRect,
1199 oldCellInkOverflow, false);
1204 SetRect(wm, rowRect, containerSize);
1205 overflow.UnionAllWith(nsRect(0, 0, rowRect.Width(wm), rowRect.Height(wm)));
1206 FinishAndStoreOverflow(overflow, rowRect.Size(wm).GetPhysicalSize(wm));
1208 nsTableFrame::RePositionViews(this);
1209 nsTableFrame::InvalidateTableFrame(this, oldRect, oldInkOverflow, false);
1210 return shift;
1214 * The following method is called by the row group frame's SplitRowGroup()
1215 * when it creates a continuing cell frame and wants to insert it into the
1216 * row's child list.
1218 void nsTableRowFrame::InsertCellFrame(nsTableCellFrame* aFrame,
1219 int32_t aColIndex) {
1220 // Find the cell frame where col index < aColIndex
1221 nsTableCellFrame* priorCell = nullptr;
1223 for (nsTableCellFrame* cellFrame = GetFirstCell(); cellFrame;
1224 cellFrame = cellFrame->GetNextCell()) {
1225 uint32_t colIndex = cellFrame->ColIndex();
1226 // Can aColIndex be -1 here? Let's assume it can for now.
1227 if (static_cast<int32_t>(colIndex) < aColIndex) {
1228 priorCell = cellFrame;
1229 } else {
1230 break;
1233 mFrames.InsertFrame(this, priorCell, aFrame);
1236 nsTableRowFrame* nsTableRowFrame::GetPrevRow() const {
1237 nsIFrame* prevSibling = GetPrevSibling();
1238 MOZ_ASSERT(
1239 !prevSibling || static_cast<nsTableRowFrame*>(do_QueryFrame(prevSibling)),
1240 "How do we have a non-row sibling?");
1241 return static_cast<nsTableRowFrame*>(prevSibling);
1244 nsTableRowFrame* nsTableRowFrame::GetNextRow() const {
1245 nsIFrame* nextSibling = GetNextSibling();
1246 MOZ_ASSERT(
1247 !nextSibling || static_cast<nsTableRowFrame*>(do_QueryFrame(nextSibling)),
1248 "How do we have a non-row sibling?");
1249 return static_cast<nsTableRowFrame*>(nextSibling);
1252 // This property is only set on the first-in-flow of nsTableRowFrame.
1253 NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(TableRowUnpaginatedBSizeProperty, nscoord)
1255 void nsTableRowFrame::SetUnpaginatedBSize(nscoord aValue) {
1256 MOZ_ASSERT(!GetPrevInFlow(),
1257 "TableRowUnpaginatedBSizeProperty should only be set on the "
1258 "first-in-flow!");
1259 AddStateBits(NS_TABLE_ROW_HAS_UNPAGINATED_BSIZE);
1260 SetProperty(TableRowUnpaginatedBSizeProperty(), aValue);
1263 nscoord nsTableRowFrame::GetUnpaginatedBSize() const {
1264 return GetProperty(TableRowUnpaginatedBSizeProperty());
1267 #ifdef ACCESSIBILITY
1268 a11y::AccType nsTableRowFrame::AccessibleType() {
1269 return a11y::eHTMLTableRowType;
1271 #endif
1273 * Sets the NS_ROW_HAS_CELL_WITH_STYLE_BSIZE bit to indicate whether
1274 * this row has any cells that have non-auto-bsize. (Row-spanning
1275 * cells are ignored.)
1277 void nsTableRowFrame::InitHasCellWithStyleBSize(nsTableFrame* aTableFrame) {
1278 WritingMode wm = GetWritingMode();
1280 for (nsTableCellFrame* cellFrame = GetFirstCell(); cellFrame;
1281 cellFrame = cellFrame->GetNextCell()) {
1282 // Ignore row-spanning cells
1283 const auto& cellBSize = cellFrame->StylePosition()->BSize(wm);
1284 if (aTableFrame->GetEffectiveRowSpan(*cellFrame) == 1 &&
1285 !cellBSize.IsAuto() &&
1286 /* calc() with both percentages and lengths treated like 'auto' */
1287 (cellBSize.ConvertsToLength() || cellBSize.ConvertsToPercentage())) {
1288 AddStateBits(NS_ROW_HAS_CELL_WITH_STYLE_BSIZE);
1289 return;
1292 RemoveStateBits(NS_ROW_HAS_CELL_WITH_STYLE_BSIZE);
1295 void nsTableRowFrame::InvalidateFrame(uint32_t aDisplayItemKey,
1296 bool aRebuildDisplayItems) {
1297 nsIFrame::InvalidateFrame(aDisplayItemKey, aRebuildDisplayItems);
1298 if (GetTableFrame()->IsBorderCollapse()) {
1299 const bool rebuild = StaticPrefs::layout_display_list_retain_sc();
1300 GetParent()->InvalidateFrameWithRect(InkOverflowRect() + GetPosition(),
1301 aDisplayItemKey, rebuild);
1305 void nsTableRowFrame::InvalidateFrameWithRect(const nsRect& aRect,
1306 uint32_t aDisplayItemKey,
1307 bool aRebuildDisplayItems) {
1308 nsIFrame::InvalidateFrameWithRect(aRect, aDisplayItemKey,
1309 aRebuildDisplayItems);
1310 // If we have filters applied that would affects our bounds, then
1311 // we get an inactive layer created and this is computed
1312 // within FrameLayerBuilder
1313 GetParent()->InvalidateFrameWithRect(aRect + GetPosition(), aDisplayItemKey,
1314 aRebuildDisplayItems);
1317 /* ----- global methods ----- */
1319 nsTableRowFrame* NS_NewTableRowFrame(PresShell* aPresShell,
1320 ComputedStyle* aStyle) {
1321 return new (aPresShell) nsTableRowFrame(aStyle, aPresShell->GetPresContext());
1324 NS_IMPL_FRAMEARENA_HELPERS(nsTableRowFrame)
1326 #ifdef DEBUG_FRAME_DUMP
1327 nsresult nsTableRowFrame::GetFrameName(nsAString& aResult) const {
1328 return MakeFrameName(u"TableRow"_ns, aResult);
1330 #endif