Bumping manifests a=b2g-bump
[gecko.git] / layout / tables / nsTableRowFrame.cpp
blob287c9316c1b9cf7402a3f4ab59325a131a6fcc76
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/. */
5 #include "nsTableRowFrame.h"
6 #include "nsTableRowGroupFrame.h"
7 #include "nsIPresShell.h"
8 #include "nsPresContext.h"
9 #include "nsStyleContext.h"
10 #include "nsStyleConsts.h"
11 #include "nsGkAtoms.h"
12 #include "nsIContent.h"
13 #include "nsTableFrame.h"
14 #include "nsTableCellFrame.h"
15 #include "nsCSSRendering.h"
16 #include "nsHTMLParts.h"
17 #include "nsTableColGroupFrame.h"
18 #include "nsTableColFrame.h"
19 #include "nsCOMPtr.h"
20 #include "nsDisplayList.h"
21 #include "nsIFrameInlines.h"
22 #include <algorithm>
24 using namespace mozilla;
26 struct nsTableCellReflowState : public nsHTMLReflowState
28 nsTableCellReflowState(nsPresContext* aPresContext,
29 const nsHTMLReflowState& aParentReflowState,
30 nsIFrame* aFrame,
31 const LogicalSize& aAvailableSpace,
32 uint32_t aFlags = 0)
33 : nsHTMLReflowState(aPresContext, aParentReflowState, aFrame,
34 aAvailableSpace, -1, -1, aFlags)
38 void FixUp(const nsSize& aAvailSpace);
41 void nsTableCellReflowState::FixUp(const nsSize& aAvailSpace)
43 // fix the mComputed values during a pass 2 reflow since the cell can be a percentage base
44 NS_WARN_IF_FALSE(NS_UNCONSTRAINEDSIZE != aAvailSpace.width,
45 "have unconstrained width; this should only result from "
46 "very large sizes, not attempts at intrinsic width "
47 "calculation");
48 if (NS_UNCONSTRAINEDSIZE != ComputedWidth()) {
49 nscoord computedWidth =
50 aAvailSpace.width - mComputedBorderPadding.LeftRight();
51 computedWidth = std::max(0, computedWidth);
52 SetComputedWidth(computedWidth);
54 if (NS_UNCONSTRAINEDSIZE != ComputedHeight() &&
55 NS_UNCONSTRAINEDSIZE != aAvailSpace.height) {
56 nscoord computedHeight =
57 aAvailSpace.height - mComputedBorderPadding.TopBottom();
58 computedHeight = std::max(0, computedHeight);
59 SetComputedHeight(computedHeight);
63 void
64 nsTableRowFrame::InitChildReflowState(nsPresContext& aPresContext,
65 const nsSize& aAvailSize,
66 bool aBorderCollapse,
67 nsTableCellReflowState& aReflowState)
69 nsMargin collapseBorder;
70 nsMargin* pCollapseBorder = nullptr;
71 if (aBorderCollapse) {
72 // we only reflow cells, so don't need to check frame type
73 nsBCTableCellFrame* bcCellFrame = (nsBCTableCellFrame*)aReflowState.frame;
74 if (bcCellFrame) {
75 pCollapseBorder = bcCellFrame->GetBorderWidth(collapseBorder);
78 aReflowState.Init(&aPresContext, -1, -1, pCollapseBorder);
79 aReflowState.FixUp(aAvailSize);
82 void
83 nsTableRowFrame::SetFixedHeight(nscoord aValue)
85 nscoord height = std::max(0, aValue);
86 if (HasFixedHeight()) {
87 if (height > mStyleFixedHeight) {
88 mStyleFixedHeight = height;
91 else {
92 mStyleFixedHeight = height;
93 if (height > 0) {
94 SetHasFixedHeight(true);
99 void
100 nsTableRowFrame::SetPctHeight(float aPctValue,
101 bool aForce)
103 nscoord height = std::max(0, NSToCoordRound(aPctValue * 100.0f));
104 if (HasPctHeight()) {
105 if ((height > mStylePctHeight) || aForce) {
106 mStylePctHeight = height;
109 else {
110 mStylePctHeight = height;
111 if (height > 0) {
112 SetHasPctHeight(true);
117 /* ----------- nsTableRowFrame ---------- */
119 NS_QUERYFRAME_HEAD(nsTableRowFrame)
120 NS_QUERYFRAME_ENTRY(nsTableRowFrame)
121 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
123 nsTableRowFrame::nsTableRowFrame(nsStyleContext* aContext)
124 : nsContainerFrame(aContext)
126 mBits.mRowIndex = mBits.mFirstInserted = 0;
127 ResetHeight(0);
130 nsTableRowFrame::~nsTableRowFrame()
134 void
135 nsTableRowFrame::Init(nsIContent* aContent,
136 nsContainerFrame* aParent,
137 nsIFrame* aPrevInFlow)
139 // Let the base class do its initialization
140 nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
142 NS_ASSERTION(NS_STYLE_DISPLAY_TABLE_ROW == StyleDisplay()->mDisplay,
143 "wrong display on table row frame");
145 if (aPrevInFlow) {
146 // Set the row index
147 nsTableRowFrame* rowFrame = (nsTableRowFrame*)aPrevInFlow;
149 SetRowIndex(rowFrame->GetRowIndex());
153 void
154 nsTableRowFrame::DestroyFrom(nsIFrame* aDestructRoot)
156 if (GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN) {
157 nsTableFrame::UnregisterPositionedTablePart(this, aDestructRoot);
160 nsContainerFrame::DestroyFrom(aDestructRoot);
163 /* virtual */ void
164 nsTableRowFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
166 nsContainerFrame::DidSetStyleContext(aOldStyleContext);
168 if (!aOldStyleContext) //avoid this on init
169 return;
171 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
172 if (tableFrame->IsBorderCollapse() &&
173 tableFrame->BCRecalcNeeded(aOldStyleContext, StyleContext())) {
174 nsIntRect damageArea(0, GetRowIndex(), tableFrame->GetColCount(), 1);
175 tableFrame->AddBCDamageArea(damageArea);
179 void
180 nsTableRowFrame::AppendFrames(ChildListID aListID,
181 nsFrameList& aFrameList)
183 NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
185 DrainSelfOverflowList(); // ensure the last frame is in mFrames
186 const nsFrameList::Slice& newCells = mFrames.AppendFrames(nullptr, aFrameList);
188 // Add the new cell frames to the table
189 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
190 for (nsFrameList::Enumerator e(newCells) ; !e.AtEnd(); e.Next()) {
191 nsIFrame *childFrame = e.get();
192 NS_ASSERTION(IS_TABLE_CELL(childFrame->GetType()),"Not a table cell frame/pseudo frame construction failure");
193 tableFrame->AppendCell(static_cast<nsTableCellFrame&>(*childFrame), GetRowIndex());
196 PresContext()->PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange,
197 NS_FRAME_HAS_DIRTY_CHILDREN);
198 tableFrame->SetGeometryDirty();
202 void
203 nsTableRowFrame::InsertFrames(ChildListID aListID,
204 nsIFrame* aPrevFrame,
205 nsFrameList& aFrameList)
207 NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
208 NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
209 "inserting after sibling frame with different parent");
210 DrainSelfOverflowList(); // ensure aPrevFrame is in mFrames
211 //Insert Frames in the frame list
212 const nsFrameList::Slice& newCells = mFrames.InsertFrames(nullptr, aPrevFrame, aFrameList);
214 // Get the table frame
215 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
216 nsIAtom* cellFrameType = tableFrame->IsBorderCollapse() ? nsGkAtoms::bcTableCellFrame : nsGkAtoms::tableCellFrame;
217 nsTableCellFrame* prevCellFrame = (nsTableCellFrame *)nsTableFrame::GetFrameAtOrBefore(this, aPrevFrame, cellFrameType);
218 nsTArray<nsTableCellFrame*> cellChildren;
219 for (nsFrameList::Enumerator e(newCells); !e.AtEnd(); e.Next()) {
220 nsIFrame *childFrame = e.get();
221 NS_ASSERTION(IS_TABLE_CELL(childFrame->GetType()),"Not a table cell frame/pseudo frame construction failure");
222 cellChildren.AppendElement(static_cast<nsTableCellFrame*>(childFrame));
224 // insert the cells into the cell map
225 int32_t colIndex = -1;
226 if (prevCellFrame) {
227 prevCellFrame->GetColIndex(colIndex);
229 tableFrame->InsertCells(cellChildren, GetRowIndex(), colIndex);
231 PresContext()->PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange,
232 NS_FRAME_HAS_DIRTY_CHILDREN);
233 tableFrame->SetGeometryDirty();
236 void
237 nsTableRowFrame::RemoveFrame(ChildListID aListID,
238 nsIFrame* aOldFrame)
240 NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
242 MOZ_ASSERT((nsTableCellFrame*)do_QueryFrame(aOldFrame));
243 nsTableCellFrame* cellFrame = static_cast<nsTableCellFrame*>(aOldFrame);
244 // remove the cell from the cell map
245 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
246 tableFrame->RemoveCell(cellFrame, GetRowIndex());
248 // Remove the frame and destroy it
249 mFrames.DestroyFrame(aOldFrame);
251 PresContext()->PresShell()->
252 FrameNeedsReflow(this, nsIPresShell::eTreeChange,
253 NS_FRAME_HAS_DIRTY_CHILDREN);
255 tableFrame->SetGeometryDirty();
258 /* virtual */ nsMargin
259 nsTableRowFrame::GetUsedMargin() const
261 return nsMargin(0,0,0,0);
264 /* virtual */ nsMargin
265 nsTableRowFrame::GetUsedBorder() const
267 return nsMargin(0,0,0,0);
270 /* virtual */ nsMargin
271 nsTableRowFrame::GetUsedPadding() const
273 return nsMargin(0,0,0,0);
276 nscoord
277 GetHeightOfRowsSpannedBelowFirst(nsTableCellFrame& aTableCellFrame,
278 nsTableFrame& aTableFrame)
280 nscoord height = 0;
281 int32_t rowSpan = aTableFrame.GetEffectiveRowSpan(aTableCellFrame);
282 // add in height of rows spanned beyond the 1st one
283 nsIFrame* nextRow = aTableCellFrame.GetParent()->GetNextSibling();
284 for (int32_t rowX = 1; ((rowX < rowSpan) && nextRow);) {
285 if (nsGkAtoms::tableRowFrame == nextRow->GetType()) {
286 height += nextRow->GetSize().height;
287 rowX++;
289 height += aTableFrame.GetCellSpacingY(rowX);
290 nextRow = nextRow->GetNextSibling();
292 return height;
295 nsTableCellFrame*
296 nsTableRowFrame::GetFirstCell()
298 nsIFrame* childFrame = mFrames.FirstChild();
299 while (childFrame) {
300 nsTableCellFrame *cellFrame = do_QueryFrame(childFrame);
301 if (cellFrame) {
302 return cellFrame;
304 childFrame = childFrame->GetNextSibling();
306 return nullptr;
310 * Post-reflow hook. This is where the table row does its post-processing
312 void
313 nsTableRowFrame::DidResize()
315 // Resize and re-align the cell frames based on our row height
316 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
317 nsTableIterator iter(*this);
318 nsIFrame* childFrame = iter.First();
320 WritingMode wm = GetWritingMode();
321 nsHTMLReflowMetrics desiredSize(wm);
322 desiredSize.SetSize(wm, GetLogicalSize(wm));
323 desiredSize.SetOverflowAreasToDesiredBounds();
325 while (childFrame) {
326 nsTableCellFrame *cellFrame = do_QueryFrame(childFrame);
327 if (cellFrame) {
328 nscoord cellHeight = mRect.height + GetHeightOfRowsSpannedBelowFirst(*cellFrame, *tableFrame);
330 // resize the cell's height
331 nsRect cellRect = cellFrame->GetRect();
332 nsRect cellVisualOverflow = cellFrame->GetVisualOverflowRect();
333 if (cellRect.height != cellHeight)
335 cellFrame->SetSize(nsSize(cellRect.width, cellHeight));
336 nsTableFrame::InvalidateTableFrame(cellFrame, cellRect,
337 cellVisualOverflow,
338 false);
341 // realign cell content based on the new height. We might be able to
342 // skip this if the height didn't change... maybe. Hard to tell.
343 cellFrame->VerticallyAlignChild(mMaxCellAscent);
345 // Always store the overflow, even if the height didn't change, since
346 // we'll lose part of our overflow area otherwise.
347 ConsiderChildOverflow(desiredSize.mOverflowAreas, cellFrame);
349 // Note that if the cell's *content* needs to change in response
350 // to this height, it will get a special height reflow.
352 // Get the next child
353 childFrame = iter.Next();
355 FinishAndStoreOverflow(&desiredSize);
356 if (HasView()) {
357 nsContainerFrame::SyncFrameViewAfterReflow(PresContext(), this, GetView(),
358 desiredSize.VisualOverflow(), 0);
360 // Let our base class do the usual work
363 // returns max-ascent amongst all cells that have 'vertical-align: baseline'
364 // *including* cells with rowspans
365 nscoord nsTableRowFrame::GetMaxCellAscent() const
367 return mMaxCellAscent;
370 nscoord nsTableRowFrame::GetRowBaseline(WritingMode aWritingMode)
372 if(mMaxCellAscent)
373 return mMaxCellAscent;
375 // If we don't have a baseline on any of the cells we go for the lowest
376 // content edge of the inner block frames.
377 // Every table cell has a cell frame with its border and padding. Inside
378 // the cell is a block frame. The cell is as high as the tallest cell in
379 // the parent row. As a consequence the block frame might not touch both
380 // the top and the bottom padding of it parent cell frame at the same time.
382 // bbbbbbbbbbbbbbbbbb cell border: b
383 // bppppppppppppppppb cell padding: p
384 // bpxxxxxxxxxxxxxxpb inner block: x
385 // bpx xpb
386 // bpx xpb
387 // bpx xpb
388 // bpxxxxxxxxxxxxxxpb base line
389 // bp pb
390 // bp pb
391 // bppppppppppppppppb
392 // bbbbbbbbbbbbbbbbbb
394 nsTableIterator iter(*this);
395 nsIFrame* childFrame = iter.First();
396 nscoord ascent = 0;
397 while (childFrame) {
398 if (IS_TABLE_CELL(childFrame->GetType())) {
399 nsIFrame* firstKid = childFrame->GetFirstPrincipalChild();
400 ascent = std::max(ascent, firstKid->GetRect().YMost());
402 // Get the next child
403 childFrame = iter.Next();
405 return ascent;
407 nscoord
408 nsTableRowFrame::GetHeight(nscoord aPctBasis) const
410 nscoord height = 0;
411 if ((aPctBasis > 0) && HasPctHeight()) {
412 height = NSToCoordRound(GetPctHeight() * (float)aPctBasis);
414 if (HasFixedHeight()) {
415 height = std::max(height, GetFixedHeight());
417 return std::max(height, GetContentHeight());
420 void
421 nsTableRowFrame::ResetHeight(nscoord aFixedHeight)
423 SetHasFixedHeight(false);
424 SetHasPctHeight(false);
425 SetFixedHeight(0);
426 SetPctHeight(0);
427 SetContentHeight(0);
429 if (aFixedHeight > 0) {
430 SetFixedHeight(aFixedHeight);
433 mMaxCellAscent = 0;
434 mMaxCellDescent = 0;
437 void
438 nsTableRowFrame::UpdateHeight(nscoord aHeight,
439 nscoord aAscent,
440 nscoord aDescent,
441 nsTableFrame* aTableFrame,
442 nsTableCellFrame* aCellFrame)
444 if (!aTableFrame || !aCellFrame) {
445 NS_ASSERTION(false , "invalid call");
446 return;
449 if (aHeight != NS_UNCONSTRAINEDSIZE) {
450 if (!(aCellFrame->HasVerticalAlignBaseline())) { // only the cell's height matters
451 if (GetHeight() < aHeight) {
452 int32_t rowSpan = aTableFrame->GetEffectiveRowSpan(*aCellFrame);
453 if (rowSpan == 1) {
454 SetContentHeight(aHeight);
458 else { // the alignment on the baseline can change the height
459 NS_ASSERTION((aAscent != NS_UNCONSTRAINEDSIZE) && (aDescent != NS_UNCONSTRAINEDSIZE), "invalid call");
460 // see if this is a long ascender
461 if (mMaxCellAscent < aAscent) {
462 mMaxCellAscent = aAscent;
464 // see if this is a long descender and without rowspan
465 if (mMaxCellDescent < aDescent) {
466 int32_t rowSpan = aTableFrame->GetEffectiveRowSpan(*aCellFrame);
467 if (rowSpan == 1) {
468 mMaxCellDescent = aDescent;
471 // keep the tallest height in sync
472 if (GetHeight() < mMaxCellAscent + mMaxCellDescent) {
473 SetContentHeight(mMaxCellAscent + mMaxCellDescent);
479 nscoord
480 nsTableRowFrame::CalcHeight(const nsHTMLReflowState& aReflowState)
482 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
483 nscoord computedHeight = (NS_UNCONSTRAINEDSIZE == aReflowState.ComputedHeight())
484 ? 0 : aReflowState.ComputedHeight();
485 ResetHeight(computedHeight);
487 const nsStylePosition* position = StylePosition();
488 if (position->mHeight.ConvertsToLength()) {
489 SetFixedHeight(nsRuleNode::ComputeCoordPercentCalc(position->mHeight, 0));
491 else if (eStyleUnit_Percent == position->mHeight.GetUnit()) {
492 SetPctHeight(position->mHeight.GetPercentValue());
494 // calc() with percentages is treated like 'auto' on table rows.
496 for (nsIFrame* kidFrame = mFrames.FirstChild(); kidFrame;
497 kidFrame = kidFrame->GetNextSibling()) {
498 nsTableCellFrame *cellFrame = do_QueryFrame(kidFrame);
499 if (cellFrame) {
500 WritingMode wm = cellFrame->GetWritingMode();
501 LogicalSize desSize = cellFrame->GetDesiredSize();
502 if ((NS_UNCONSTRAINEDSIZE == aReflowState.AvailableHeight()) && !GetPrevInFlow()) {
503 CalculateCellActualHeight(cellFrame, desSize.BSize(wm));
505 // height may have changed, adjust descent to absorb any excess difference
506 nscoord ascent;
507 if (!kidFrame->GetFirstPrincipalChild()->GetFirstPrincipalChild())
508 ascent = desSize.BSize(wm);
509 else
510 ascent = cellFrame->GetCellBaseline();
511 nscoord descent = desSize.BSize(wm) - ascent;
512 UpdateHeight(desSize.BSize(wm), ascent, descent, tableFrame, cellFrame);
515 return GetHeight();
519 * We need a custom display item for table row backgrounds. This is only used
520 * when the table row is the root of a stacking context (e.g., has 'opacity').
521 * Table row backgrounds can extend beyond the row frame bounds, when
522 * the row contains row-spanning cells.
524 class nsDisplayTableRowBackground : public nsDisplayTableItem {
525 public:
526 nsDisplayTableRowBackground(nsDisplayListBuilder* aBuilder,
527 nsTableRowFrame* aFrame) :
528 nsDisplayTableItem(aBuilder, aFrame) {
529 MOZ_COUNT_CTOR(nsDisplayTableRowBackground);
531 #ifdef NS_BUILD_REFCNT_LOGGING
532 virtual ~nsDisplayTableRowBackground() {
533 MOZ_COUNT_DTOR(nsDisplayTableRowBackground);
535 #endif
537 virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
538 const nsDisplayItemGeometry* aGeometry,
539 nsRegion *aInvalidRegion) MOZ_OVERRIDE;
540 virtual void Paint(nsDisplayListBuilder* aBuilder,
541 nsRenderingContext* aCtx) MOZ_OVERRIDE;
542 NS_DISPLAY_DECL_NAME("TableRowBackground", TYPE_TABLE_ROW_BACKGROUND)
545 void
546 nsDisplayTableRowBackground::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
547 const nsDisplayItemGeometry* aGeometry,
548 nsRegion *aInvalidRegion)
550 if (aBuilder->ShouldSyncDecodeImages()) {
551 if (nsTableFrame::AnyTablePartHasUndecodedBackgroundImage(mFrame, mFrame->GetNextSibling())) {
552 bool snap;
553 aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
557 nsDisplayTableItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
560 void
561 nsDisplayTableRowBackground::Paint(nsDisplayListBuilder* aBuilder,
562 nsRenderingContext* aCtx)
564 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(mFrame);
565 TableBackgroundPainter painter(tableFrame,
566 TableBackgroundPainter::eOrigin_TableRow,
567 mFrame->PresContext(), *aCtx,
568 mVisibleRect, ToReferenceFrame(),
569 aBuilder->GetBackgroundPaintFlags());
570 painter.PaintRow(static_cast<nsTableRowFrame*>(mFrame));
573 void
574 nsTableRowFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
575 const nsRect& aDirtyRect,
576 const nsDisplayListSet& aLists)
578 nsDisplayTableItem* item = nullptr;
579 if (IsVisibleInSelection(aBuilder)) {
580 bool isRoot = aBuilder->IsAtRootOfPseudoStackingContext();
581 if (isRoot) {
582 // This background is created regardless of whether this frame is
583 // visible or not. Visibility decisions are delegated to the
584 // table background painter.
585 // We would use nsDisplayGeneric for this rare case except that we
586 // need the background to be larger than the row frame in some
587 // cases.
588 item = new (aBuilder) nsDisplayTableRowBackground(aBuilder, this);
589 aLists.BorderBackground()->AppendNewToTop(item);
592 nsTableFrame::DisplayGenericTablePart(aBuilder, this, aDirtyRect, aLists, item);
595 nsIFrame::LogicalSides
596 nsTableRowFrame::GetLogicalSkipSides(const nsHTMLReflowState* aReflowState) const
598 if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==
599 NS_STYLE_BOX_DECORATION_BREAK_CLONE)) {
600 return LogicalSides();
603 LogicalSides skip;
604 if (nullptr != GetPrevInFlow()) {
605 skip |= eLogicalSideBitsBStart;
607 if (nullptr != GetNextInFlow()) {
608 skip |= eLogicalSideBitsBEnd;
610 return skip;
613 // Calculate the cell's actual height given its pass2 height.
614 // Takes into account the specified height (in the style).
615 // Modifies the desired height that is passed in.
616 nsresult
617 nsTableRowFrame::CalculateCellActualHeight(nsTableCellFrame* aCellFrame,
618 nscoord& aDesiredHeight)
620 nscoord specifiedHeight = 0;
622 // Get the height specified in the style information
623 const nsStylePosition* position = aCellFrame->StylePosition();
625 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
626 int32_t rowSpan = tableFrame->GetEffectiveRowSpan(*aCellFrame);
628 switch (position->mHeight.GetUnit()) {
629 case eStyleUnit_Calc: {
630 if (position->mHeight.CalcHasPercent()) {
631 // Treat this like "auto"
632 break;
634 // Fall through to the coord case
636 case eStyleUnit_Coord: {
637 nscoord outsideBoxSizing = 0;
638 // In quirks mode, table cell width should be content-box, but height
639 // should be border-box.
640 // Because of this historic anomaly, we do not use quirk.css
641 // (since we can't specify one value of box-sizing for width and another
642 // for height)
643 if (PresContext()->CompatibilityMode() != eCompatibility_NavQuirks) {
644 switch (position->mBoxSizing) {
645 case NS_STYLE_BOX_SIZING_CONTENT:
646 outsideBoxSizing = aCellFrame->GetUsedBorderAndPadding().TopBottom();
647 break;
648 case NS_STYLE_BOX_SIZING_PADDING:
649 outsideBoxSizing = aCellFrame->GetUsedBorder().TopBottom();
650 break;
651 default:
652 // NS_STYLE_BOX_SIZING_BORDER
653 break;
657 specifiedHeight =
658 nsRuleNode::ComputeCoordPercentCalc(position->mHeight, 0) +
659 outsideBoxSizing;
661 if (1 == rowSpan)
662 SetFixedHeight(specifiedHeight);
663 break;
665 case eStyleUnit_Percent: {
666 if (1 == rowSpan)
667 SetPctHeight(position->mHeight.GetPercentValue());
668 // pct heights are handled when all of the cells are finished, so don't set specifiedHeight
669 break;
671 case eStyleUnit_Auto:
672 default:
673 break;
676 // If the specified height is greater than the desired height, then use the specified height
677 if (specifiedHeight > aDesiredHeight)
678 aDesiredHeight = specifiedHeight;
680 return NS_OK;
683 // Calculates the available width for the table cell based on the known
684 // column widths taking into account column spans and column spacing
685 static nscoord
686 CalcAvailWidth(nsTableFrame& aTableFrame,
687 nsTableCellFrame& aCellFrame)
689 nscoord cellAvailWidth = 0;
690 int32_t colIndex;
691 aCellFrame.GetColIndex(colIndex);
692 int32_t colspan = aTableFrame.GetEffectiveColSpan(aCellFrame);
693 NS_ASSERTION(colspan > 0, "effective colspan should be positive");
695 for (int32_t spanX = 0; spanX < colspan; spanX++) {
696 cellAvailWidth += aTableFrame.GetColumnWidth(colIndex + spanX);
697 if (spanX > 0 &&
698 aTableFrame.ColumnHasCellSpacingBefore(colIndex + spanX)) {
699 cellAvailWidth += aTableFrame.GetCellSpacingX(colIndex + spanX - 1);
702 return cellAvailWidth;
705 nscoord
706 GetSpaceBetween(int32_t aPrevColIndex,
707 int32_t aColIndex,
708 int32_t aColSpan,
709 nsTableFrame& aTableFrame,
710 bool aIsLeftToRight,
711 bool aCheckVisibility)
713 nscoord space = 0;
714 int32_t colX;
715 if (aIsLeftToRight) {
716 for (colX = aPrevColIndex + 1; aColIndex > colX; colX++) {
717 bool isCollapsed = false;
718 if (!aCheckVisibility) {
719 space += aTableFrame.GetColumnWidth(colX);
721 else {
722 nsTableColFrame* colFrame = aTableFrame.GetColFrame(colX);
723 const nsStyleVisibility* colVis = colFrame->StyleVisibility();
724 bool collapseCol = (NS_STYLE_VISIBILITY_COLLAPSE == colVis->mVisible);
725 nsIFrame* cgFrame = colFrame->GetParent();
726 const nsStyleVisibility* groupVis = cgFrame->StyleVisibility();
727 bool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE ==
728 groupVis->mVisible);
729 isCollapsed = collapseCol || collapseGroup;
730 if (!isCollapsed)
731 space += aTableFrame.GetColumnWidth(colX);
733 if (!isCollapsed && aTableFrame.ColumnHasCellSpacingBefore(colX)) {
734 space += aTableFrame.GetCellSpacingX(colX - 1);
738 else {
739 int32_t lastCol = aColIndex + aColSpan - 1;
740 for (colX = aPrevColIndex - 1; colX > lastCol; colX--) {
741 bool isCollapsed = false;
742 if (!aCheckVisibility) {
743 space += aTableFrame.GetColumnWidth(colX);
745 else {
746 nsTableColFrame* colFrame = aTableFrame.GetColFrame(colX);
747 const nsStyleVisibility* colVis = colFrame->StyleVisibility();
748 bool collapseCol = (NS_STYLE_VISIBILITY_COLLAPSE == colVis->mVisible);
749 nsIFrame* cgFrame = colFrame->GetParent();
750 const nsStyleVisibility* groupVis = cgFrame->StyleVisibility();
751 bool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE ==
752 groupVis->mVisible);
753 isCollapsed = collapseCol || collapseGroup;
754 if (!isCollapsed)
755 space += aTableFrame.GetColumnWidth(colX);
757 if (!isCollapsed && aTableFrame.ColumnHasCellSpacingBefore(colX)) {
758 space += aTableFrame.GetCellSpacingX(colX - 1);
762 return space;
765 // subtract the heights of aRow's prev in flows from the unpaginated height
766 static
767 nscoord CalcHeightFromUnpaginatedHeight(nsPresContext* aPresContext,
768 nsTableRowFrame& aRow)
770 nscoord height = 0;
771 nsTableRowFrame* firstInFlow =
772 static_cast<nsTableRowFrame*>(aRow.FirstInFlow());
773 if (firstInFlow->HasUnpaginatedHeight()) {
774 height = firstInFlow->GetUnpaginatedHeight(aPresContext);
775 for (nsIFrame* prevInFlow = aRow.GetPrevInFlow(); prevInFlow;
776 prevInFlow = prevInFlow->GetPrevInFlow()) {
777 height -= prevInFlow->GetSize().height;
780 return std::max(height, 0);
783 void
784 nsTableRowFrame::ReflowChildren(nsPresContext* aPresContext,
785 nsHTMLReflowMetrics& aDesiredSize,
786 const nsHTMLReflowState& aReflowState,
787 nsTableFrame& aTableFrame,
788 nsReflowStatus& aStatus)
790 aStatus = NS_FRAME_COMPLETE;
792 // XXXldb Should we be checking constrained height instead?
793 const bool isPaginated = aPresContext->IsPaginated();
794 const bool borderCollapse = aTableFrame.IsBorderCollapse();
796 int32_t cellColSpan = 1; // must be defined here so it's set properly for non-cell kids
798 nsTableIterator iter(*this);
799 // remember the col index of the previous cell to handle rowspans into this row
800 int32_t firstPrevColIndex = (iter.IsLeftToRight()) ? -1 : aTableFrame.GetColCount();
801 int32_t prevColIndex = firstPrevColIndex;
802 nscoord x = 0; // running total of children x offset
804 // This computes the max of all cell heights
805 nscoord cellMaxHeight = 0;
807 // Reflow each of our existing cell frames
808 for (nsIFrame* kidFrame = iter.First(); kidFrame; kidFrame = iter.Next()) {
809 nsTableCellFrame *cellFrame = do_QueryFrame(kidFrame);
810 if (!cellFrame) {
811 // XXXldb nsCSSFrameConstructor needs to enforce this!
812 NS_NOTREACHED("yikes, a non-row child");
814 // it's an unknown frame type, give it a generic reflow and ignore the results
815 nsTableCellReflowState
816 kidReflowState(aPresContext, aReflowState, kidFrame,
817 LogicalSize(kidFrame->GetWritingMode(), 0, 0),
818 nsHTMLReflowState::CALLER_WILL_INIT);
819 InitChildReflowState(*aPresContext, nsSize(0,0), false, kidReflowState);
820 nsHTMLReflowMetrics desiredSize(aReflowState);
821 nsReflowStatus status;
822 ReflowChild(kidFrame, aPresContext, desiredSize, kidReflowState, 0, 0, 0, status);
823 kidFrame->DidReflow(aPresContext, nullptr, nsDidReflowStatus::FINISHED);
825 continue;
828 // See if we should only reflow the dirty child frames
829 bool doReflowChild = true;
830 if (!aReflowState.ShouldReflowAllKids() &&
831 !aTableFrame.IsGeometryDirty() &&
832 !NS_SUBTREE_DIRTY(kidFrame)) {
833 if (!aReflowState.mFlags.mSpecialHeightReflow)
834 doReflowChild = false;
836 else if ((NS_UNCONSTRAINEDSIZE != aReflowState.AvailableHeight())) {
837 // We don't reflow a rowspan >1 cell here with a constrained height.
838 // That happens in nsTableRowGroupFrame::SplitSpanningCells.
839 if (aTableFrame.GetEffectiveRowSpan(*cellFrame) > 1) {
840 doReflowChild = false;
843 if (aReflowState.mFlags.mSpecialHeightReflow) {
844 if (!isPaginated && !(cellFrame->GetStateBits() &
845 NS_FRAME_CONTAINS_RELATIVE_HEIGHT)) {
846 continue;
850 int32_t cellColIndex;
851 cellFrame->GetColIndex(cellColIndex);
852 cellColSpan = aTableFrame.GetEffectiveColSpan(*cellFrame);
854 // If the adjacent cell is in a prior row (because of a rowspan) add in the space
855 if ((iter.IsLeftToRight() && (prevColIndex != (cellColIndex - 1))) ||
856 (!iter.IsLeftToRight() && (prevColIndex != cellColIndex + cellColSpan))) {
857 x += GetSpaceBetween(prevColIndex, cellColIndex, cellColSpan, aTableFrame,
858 iter.IsLeftToRight(), false);
861 // remember the rightmost (ltr) or leftmost (rtl) column this cell spans into
862 prevColIndex = (iter.IsLeftToRight()) ? cellColIndex + (cellColSpan - 1) : cellColIndex;
864 // Reflow the child frame
865 nsRect kidRect = kidFrame->GetRect();
866 nsRect kidVisualOverflow = kidFrame->GetVisualOverflowRect();
867 bool firstReflow =
868 (kidFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0;
870 if (doReflowChild) {
871 // Calculate the available width for the table cell using the known column widths
872 nscoord availCellWidth =
873 CalcAvailWidth(aTableFrame, *cellFrame);
875 nsHTMLReflowMetrics desiredSize(aReflowState);
877 // If the avail width is not the same as last time we reflowed the cell or
878 // the cell wants to be bigger than what was available last time or
879 // it is a style change reflow or we are printing, then we must reflow the
880 // cell. Otherwise we can skip the reflow.
881 // XXXldb Why is this condition distinct from doReflowChild above?
882 WritingMode rowWM = aReflowState.GetWritingMode();
883 WritingMode cellWM = cellFrame->GetWritingMode();
884 LogicalSize cellDesiredSize = cellFrame->GetDesiredSize();
885 if ((availCellWidth != cellFrame->GetPriorAvailWidth()) ||
886 (cellDesiredSize.ISize(cellWM) > cellFrame->GetPriorAvailWidth()) ||
887 (GetStateBits() & NS_FRAME_IS_DIRTY) ||
888 isPaginated ||
889 NS_SUBTREE_DIRTY(cellFrame) ||
890 // See if it needs a special reflow, or if it had one that we need to undo.
891 (cellFrame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT) ||
892 HasPctHeight()) {
893 // Reflow the cell to fit the available width, height
894 // XXX The old IR_ChildIsDirty code used availCellWidth here.
895 nsSize kidAvailSize(availCellWidth, aReflowState.AvailableHeight());
897 // Reflow the child
898 nsTableCellReflowState
899 kidReflowState(aPresContext, aReflowState, kidFrame,
900 LogicalSize(kidFrame->GetWritingMode(),
901 kidAvailSize),
902 nsHTMLReflowState::CALLER_WILL_INIT);
903 InitChildReflowState(*aPresContext, kidAvailSize, borderCollapse,
904 kidReflowState);
906 nsReflowStatus status;
907 ReflowChild(kidFrame, aPresContext, desiredSize, kidReflowState,
908 x, 0, 0, status);
910 // allow the table to determine if/how the table needs to be rebalanced
911 // If any of the cells are not complete, then we're not complete
912 if (NS_FRAME_IS_NOT_COMPLETE(status)) {
913 aStatus = NS_FRAME_NOT_COMPLETE;
916 else {
917 if (x != kidRect.x) {
918 kidFrame->InvalidateFrameSubtree();
921 desiredSize.SetSize(cellWM, cellDesiredSize);
922 desiredSize.mOverflowAreas = cellFrame->GetOverflowAreas();
924 // if we are in a floated table, our position is not yet established, so we cannot reposition our views
925 // the containing block will do this for us after positioning the table
926 if (!aTableFrame.IsFloating()) {
927 // Because we may have moved the frame we need to make sure any views are
928 // positioned properly. We have to do this, because any one of our parent
929 // frames could have moved and we have no way of knowing...
930 nsTableFrame::RePositionViews(kidFrame);
934 if (NS_UNCONSTRAINEDSIZE == aReflowState.AvailableHeight()) {
935 if (!GetPrevInFlow()) {
936 // Calculate the cell's actual height given its pass2 height. This
937 // function takes into account the specified height (in the style)
938 CalculateCellActualHeight(cellFrame, desiredSize.Height());
940 // height may have changed, adjust descent to absorb any excess difference
941 nscoord ascent;
942 if (!kidFrame->GetFirstPrincipalChild()->GetFirstPrincipalChild()) {
943 ascent = desiredSize.BSize(rowWM);
944 } else {
945 ascent = ((nsTableCellFrame *)kidFrame)->GetCellBaseline();
947 nscoord descent = desiredSize.BSize(rowWM) - ascent;
948 UpdateHeight(desiredSize.BSize(rowWM), ascent, descent, &aTableFrame, cellFrame);
950 else {
951 cellMaxHeight = std::max(cellMaxHeight, desiredSize.Height());
952 int32_t rowSpan = aTableFrame.GetEffectiveRowSpan((nsTableCellFrame&)*kidFrame);
953 if (1 == rowSpan) {
954 SetContentHeight(cellMaxHeight);
958 // Place the child
959 desiredSize.ISize(rowWM) = availCellWidth;
961 FinishReflowChild(kidFrame, aPresContext, desiredSize, nullptr, x, 0, 0);
963 nsTableFrame::InvalidateTableFrame(kidFrame, kidRect, kidVisualOverflow,
964 firstReflow);
966 x += desiredSize.Width();
968 else {
969 if (kidRect.x != x) {
970 // Invalidate the old position
971 kidFrame->InvalidateFrameSubtree();
972 // move to the new position
973 kidFrame->SetPosition(nsPoint(x, kidRect.y));
974 nsTableFrame::RePositionViews(kidFrame);
975 // invalidate the new position
976 kidFrame->InvalidateFrameSubtree();
978 // we need to account for the cell's width even if it isn't reflowed
979 x += kidRect.width;
981 if (kidFrame->GetNextInFlow()) {
982 aStatus = NS_FRAME_NOT_COMPLETE;
985 ConsiderChildOverflow(aDesiredSize.mOverflowAreas, kidFrame);
986 x += aTableFrame.GetCellSpacingX(cellColIndex);
989 // just set our width to what was available. The table will calculate the width and not use our value.
990 aDesiredSize.Width() = aReflowState.AvailableWidth();
992 if (aReflowState.mFlags.mSpecialHeightReflow) {
993 aDesiredSize.Height() = mRect.height;
995 else if (NS_UNCONSTRAINEDSIZE == aReflowState.AvailableHeight()) {
996 aDesiredSize.Height() = CalcHeight(aReflowState);
997 if (GetPrevInFlow()) {
998 nscoord height = CalcHeightFromUnpaginatedHeight(aPresContext, *this);
999 aDesiredSize.Height() = std::max(aDesiredSize.Height(), height);
1001 else {
1002 if (isPaginated && HasStyleHeight()) {
1003 // set the unpaginated height so next in flows can try to honor it
1004 SetHasUnpaginatedHeight(true);
1005 SetUnpaginatedHeight(aPresContext, aDesiredSize.Height());
1007 if (isPaginated && HasUnpaginatedHeight()) {
1008 aDesiredSize.Height() = std::max(aDesiredSize.Height(), GetUnpaginatedHeight(aPresContext));
1012 else { // constrained height, paginated
1013 // Compute the height we should have from style (subtracting the
1014 // height from our prev-in-flows from the style height)
1015 nscoord styleHeight = CalcHeightFromUnpaginatedHeight(aPresContext, *this);
1016 if (styleHeight > aReflowState.AvailableHeight()) {
1017 styleHeight = aReflowState.AvailableHeight();
1018 NS_FRAME_SET_INCOMPLETE(aStatus);
1020 aDesiredSize.Height() = std::max(cellMaxHeight, styleHeight);
1022 aDesiredSize.UnionOverflowAreasWithDesiredBounds();
1023 FinishAndStoreOverflow(&aDesiredSize);
1026 /** Layout the entire row.
1027 * This method stacks cells horizontally according to HTML 4.0 rules.
1029 void
1030 nsTableRowFrame::Reflow(nsPresContext* aPresContext,
1031 nsHTMLReflowMetrics& aDesiredSize,
1032 const nsHTMLReflowState& aReflowState,
1033 nsReflowStatus& aStatus)
1035 DO_GLOBAL_REFLOW_COUNT("nsTableRowFrame");
1036 DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
1038 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
1039 const nsStyleVisibility* rowVis = StyleVisibility();
1040 bool collapseRow = (NS_STYLE_VISIBILITY_COLLAPSE == rowVis->mVisible);
1041 if (collapseRow) {
1042 tableFrame->SetNeedToCollapse(true);
1045 // see if a special height reflow needs to occur due to having a pct height
1046 nsTableFrame::CheckRequestSpecialHeightReflow(aReflowState);
1048 // See if we have a cell with specified/pct height
1049 InitHasCellWithStyleHeight(tableFrame);
1051 ReflowChildren(aPresContext, aDesiredSize, aReflowState, *tableFrame, aStatus);
1053 if (aPresContext->IsPaginated() && !NS_FRAME_IS_FULLY_COMPLETE(aStatus) &&
1054 ShouldAvoidBreakInside(aReflowState)) {
1055 aStatus = NS_INLINE_LINE_BREAK_BEFORE();
1058 // just set our width to what was available. The table will calculate the width and not use our value.
1059 aDesiredSize.Width() = aReflowState.AvailableWidth();
1061 // If our parent is in initial reflow, it'll handle invalidating our
1062 // entire overflow rect.
1063 if (!(GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW) &&
1064 nsSize(aDesiredSize.Width(), aDesiredSize.Height()) != mRect.Size()) {
1065 InvalidateFrame();
1068 // Any absolutely-positioned children will get reflowed in
1069 // nsFrame::FixupPositionedTableParts in another pass, so propagate our
1070 // dirtiness to them before our parent clears our dirty bits.
1071 PushDirtyBitToAbsoluteFrames();
1073 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
1077 * This function is called by the row group frame's SplitRowGroup() code when
1078 * pushing a row frame that has cell frames that span into it. The cell frame
1079 * should be reflowed with the specified height
1081 nscoord
1082 nsTableRowFrame::ReflowCellFrame(nsPresContext* aPresContext,
1083 const nsHTMLReflowState& aReflowState,
1084 bool aIsTopOfPage,
1085 nsTableCellFrame* aCellFrame,
1086 nscoord aAvailableHeight,
1087 nsReflowStatus& aStatus)
1089 // Reflow the cell frame with the specified height. Use the existing width
1090 nsRect cellRect = aCellFrame->GetRect();
1091 nsRect cellVisualOverflow = aCellFrame->GetVisualOverflowRect();
1093 nsSize availSize(cellRect.width, aAvailableHeight);
1094 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
1095 bool borderCollapse = tableFrame->IsBorderCollapse();
1096 nsTableCellReflowState
1097 cellReflowState(aPresContext, aReflowState, aCellFrame,
1098 LogicalSize(aCellFrame->GetWritingMode(),
1099 availSize),
1100 nsHTMLReflowState::CALLER_WILL_INIT);
1101 InitChildReflowState(*aPresContext, availSize, borderCollapse, cellReflowState);
1102 cellReflowState.mFlags.mIsTopOfPage = aIsTopOfPage;
1104 nsHTMLReflowMetrics desiredSize(aReflowState);
1106 ReflowChild(aCellFrame, aPresContext, desiredSize, cellReflowState,
1107 0, 0, NS_FRAME_NO_MOVE_FRAME, aStatus);
1108 bool fullyComplete = NS_FRAME_IS_COMPLETE(aStatus) && !NS_FRAME_IS_TRUNCATED(aStatus);
1109 if (fullyComplete) {
1110 desiredSize.Height() = aAvailableHeight;
1112 aCellFrame->SetSize(nsSize(cellRect.width, desiredSize.Height()));
1114 // Note: VerticallyAlignChild can affect the overflow rect.
1115 // XXX What happens if this cell has 'vertical-align: baseline' ?
1116 // XXX Why is it assumed that the cell's ascent hasn't changed ?
1117 if (fullyComplete) {
1118 aCellFrame->VerticallyAlignChild(mMaxCellAscent);
1121 nsTableFrame::InvalidateTableFrame(aCellFrame, cellRect,
1122 cellVisualOverflow,
1123 (aCellFrame->GetStateBits() &
1124 NS_FRAME_FIRST_REFLOW) != 0);
1126 aCellFrame->DidReflow(aPresContext, nullptr, nsDidReflowStatus::FINISHED);
1128 return desiredSize.Height();
1131 nscoord
1132 nsTableRowFrame::CollapseRowIfNecessary(nscoord aRowOffset,
1133 nscoord aWidth,
1134 bool aCollapseGroup,
1135 bool& aDidCollapse)
1137 const nsStyleVisibility* rowVis = StyleVisibility();
1138 bool collapseRow = (NS_STYLE_VISIBILITY_COLLAPSE == rowVis->mVisible);
1139 nsTableFrame* tableFrame = static_cast<nsTableFrame*>(
1140 nsTableFrame::GetTableFrame(this)->FirstInFlow());
1141 if (collapseRow) {
1142 tableFrame->SetNeedToCollapse(true);
1145 if (aRowOffset != 0) {
1146 // We're moving, so invalidate our old position
1147 InvalidateFrameSubtree();
1150 nsRect rowRect = GetRect();
1151 nsRect oldRect = rowRect;
1152 nsRect oldVisualOverflow = GetVisualOverflowRect();
1154 rowRect.y -= aRowOffset;
1155 rowRect.width = aWidth;
1156 nsOverflowAreas overflow;
1157 nscoord shift = 0;
1159 if (aCollapseGroup || collapseRow) {
1160 aDidCollapse = true;
1161 shift = rowRect.height;
1162 nsTableCellFrame* cellFrame = GetFirstCell();
1163 if (cellFrame) {
1164 int32_t rowIndex;
1165 cellFrame->GetRowIndex(rowIndex);
1166 shift += tableFrame->GetCellSpacingY(rowIndex);
1167 while (cellFrame) {
1168 nsRect cRect = cellFrame->GetRect();
1169 // If aRowOffset != 0, there's no point in invalidating the cells, since
1170 // we've already invalidated our overflow area. Note that we _do_ still
1171 // need to invalidate if our row is not moving, because the cell might
1172 // span out of this row, so invalidating our row rect won't do enough.
1173 if (aRowOffset == 0) {
1174 InvalidateFrame();
1176 cRect.height = 0;
1177 cellFrame->SetRect(cRect);
1178 cellFrame = cellFrame->GetNextCell();
1180 } else {
1181 shift += tableFrame->GetCellSpacingY(GetRowIndex());
1183 rowRect.height = 0;
1185 else { // row is not collapsed
1186 nsTableIterator iter(*this);
1187 // remember the col index of the previous cell to handle rowspans into this
1188 // row
1189 int32_t firstPrevColIndex = (iter.IsLeftToRight()) ? -1 :
1190 tableFrame->GetColCount();
1191 int32_t prevColIndex = firstPrevColIndex;
1192 nscoord x = 0; // running total of children x offset
1194 int32_t colIncrement = iter.IsLeftToRight() ? 1 : -1;
1196 nsIFrame* kidFrame = iter.First();
1197 while (kidFrame) {
1198 nsTableCellFrame *cellFrame = do_QueryFrame(kidFrame);
1199 if (cellFrame) {
1200 int32_t cellColIndex;
1201 cellFrame->GetColIndex(cellColIndex);
1202 int32_t cellColSpan = tableFrame->GetEffectiveColSpan(*cellFrame);
1204 // If the adjacent cell is in a prior row (because of a rowspan) add in
1205 // the space
1206 if ((iter.IsLeftToRight() && (prevColIndex != (cellColIndex - 1))) ||
1207 (!iter.IsLeftToRight() &&
1208 (prevColIndex != cellColIndex + cellColSpan))) {
1209 x += GetSpaceBetween(prevColIndex, cellColIndex, cellColSpan,
1210 *tableFrame, iter.IsLeftToRight(),
1211 true);
1213 nsRect cRect(x, 0, 0, rowRect.height);
1215 // remember the rightmost (ltr) or leftmost (rtl) column this cell
1216 // spans into
1217 prevColIndex = (iter.IsLeftToRight()) ?
1218 cellColIndex + (cellColSpan - 1) : cellColIndex;
1219 int32_t startIndex = (iter.IsLeftToRight()) ?
1220 cellColIndex : cellColIndex + (cellColSpan - 1);
1221 int32_t actualColSpan = cellColSpan;
1222 bool isVisible = false;
1223 for (int32_t colX = startIndex; actualColSpan > 0;
1224 colX += colIncrement, actualColSpan--) {
1226 nsTableColFrame* colFrame = tableFrame->GetColFrame(colX);
1227 const nsStyleVisibility* colVis = colFrame->StyleVisibility();
1228 bool collapseCol = (NS_STYLE_VISIBILITY_COLLAPSE ==
1229 colVis->mVisible);
1230 nsIFrame* cgFrame = colFrame->GetParent();
1231 const nsStyleVisibility* groupVis = cgFrame->StyleVisibility();
1232 bool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE ==
1233 groupVis->mVisible);
1234 bool isCollapsed = collapseCol || collapseGroup;
1235 if (!isCollapsed) {
1236 cRect.width += tableFrame->GetColumnWidth(colX);
1237 isVisible = true;
1238 if ((actualColSpan > 1)) {
1239 nsTableColFrame* nextColFrame =
1240 tableFrame->GetColFrame(colX + colIncrement);
1241 const nsStyleVisibility* nextColVis =
1242 nextColFrame->StyleVisibility();
1243 if ( (NS_STYLE_VISIBILITY_COLLAPSE != nextColVis->mVisible) &&
1244 tableFrame->ColumnHasCellSpacingBefore(colX + colIncrement)) {
1245 cRect.width += tableFrame->GetCellSpacingX(cellColIndex);
1250 x += cRect.width;
1251 if (isVisible)
1252 x += tableFrame->GetCellSpacingX(cellColIndex);
1253 int32_t actualRowSpan = tableFrame->GetEffectiveRowSpan(*cellFrame);
1254 nsTableRowFrame* rowFrame = GetNextRow();
1255 for (actualRowSpan--; actualRowSpan > 0 && rowFrame; actualRowSpan--) {
1256 const nsStyleVisibility* nextRowVis = rowFrame->StyleVisibility();
1257 bool collapseNextRow = (NS_STYLE_VISIBILITY_COLLAPSE ==
1258 nextRowVis->mVisible);
1259 if (!collapseNextRow) {
1260 nsRect nextRect = rowFrame->GetRect();
1261 cRect.height += nextRect.height +
1262 tableFrame->GetCellSpacingY(rowFrame->GetRowIndex());
1264 rowFrame = rowFrame->GetNextRow();
1267 nsRect oldCellRect = cellFrame->GetRect();
1268 nsRect oldCellVisualOverflow = cellFrame->GetVisualOverflowRect();
1270 if (aRowOffset == 0 && cRect.TopLeft() != oldCellRect.TopLeft()) {
1271 // We're moving the cell. Invalidate the old overflow area
1272 cellFrame->InvalidateFrameSubtree();
1275 cellFrame->SetRect(cRect);
1277 // XXXbz This looks completely bogus in the cases when we didn't
1278 // collapse the cell!
1279 nsRect cellBounds(0, 0, cRect.width, cRect.height);
1280 nsOverflowAreas cellOverflow(cellBounds, cellBounds);
1281 cellFrame->FinishAndStoreOverflow(cellOverflow, cRect.Size());
1282 nsTableFrame::RePositionViews(cellFrame);
1283 ConsiderChildOverflow(overflow, cellFrame);
1285 if (aRowOffset == 0) {
1286 nsTableFrame::InvalidateTableFrame(cellFrame, oldCellRect,
1287 oldCellVisualOverflow,
1288 false);
1291 kidFrame = iter.Next(); // Get the next child
1295 SetRect(rowRect);
1296 overflow.UnionAllWith(nsRect(0, 0, rowRect.width, rowRect.height));
1297 FinishAndStoreOverflow(overflow, rowRect.Size());
1299 nsTableFrame::RePositionViews(this);
1300 nsTableFrame::InvalidateTableFrame(this, oldRect, oldVisualOverflow, false);
1301 return shift;
1305 * The following method is called by the row group frame's SplitRowGroup()
1306 * when it creates a continuing cell frame and wants to insert it into the
1307 * row's child list.
1309 void
1310 nsTableRowFrame::InsertCellFrame(nsTableCellFrame* aFrame,
1311 int32_t aColIndex)
1313 // Find the cell frame where col index < aColIndex
1314 nsTableCellFrame* priorCell = nullptr;
1315 for (nsIFrame* child = mFrames.FirstChild(); child;
1316 child = child->GetNextSibling()) {
1317 nsTableCellFrame *cellFrame = do_QueryFrame(child);
1318 if (cellFrame) {
1319 int32_t colIndex;
1320 cellFrame->GetColIndex(colIndex);
1321 if (colIndex < aColIndex) {
1322 priorCell = cellFrame;
1324 else break;
1327 mFrames.InsertFrame(this, priorCell, aFrame);
1330 nsIAtom*
1331 nsTableRowFrame::GetType() const
1333 return nsGkAtoms::tableRowFrame;
1336 nsTableRowFrame*
1337 nsTableRowFrame::GetNextRow() const
1339 nsIFrame* childFrame = GetNextSibling();
1340 while (childFrame) {
1341 nsTableRowFrame *rowFrame = do_QueryFrame(childFrame);
1342 if (rowFrame) {
1343 NS_ASSERTION(NS_STYLE_DISPLAY_TABLE_ROW == childFrame->StyleDisplay()->mDisplay, "wrong display type on rowframe");
1344 return rowFrame;
1346 childFrame = childFrame->GetNextSibling();
1348 return nullptr;
1351 NS_DECLARE_FRAME_PROPERTY(RowUnpaginatedHeightProperty, nullptr)
1353 void
1354 nsTableRowFrame::SetUnpaginatedHeight(nsPresContext* aPresContext,
1355 nscoord aValue)
1357 NS_ASSERTION(!GetPrevInFlow(), "program error");
1358 // Get the property
1359 aPresContext->PropertyTable()->
1360 Set(this, RowUnpaginatedHeightProperty(), NS_INT32_TO_PTR(aValue));
1363 nscoord
1364 nsTableRowFrame::GetUnpaginatedHeight(nsPresContext* aPresContext)
1366 FrameProperties props = FirstInFlow()->Properties();
1367 return NS_PTR_TO_INT32(props.Get(RowUnpaginatedHeightProperty()));
1370 void nsTableRowFrame::SetContinuousBCBorderWidth(uint8_t aForSide,
1371 BCPixelSize aPixelValue)
1373 switch (aForSide) {
1374 case NS_SIDE_RIGHT:
1375 mRightContBorderWidth = aPixelValue;
1376 return;
1377 case NS_SIDE_TOP:
1378 mTopContBorderWidth = aPixelValue;
1379 return;
1380 case NS_SIDE_LEFT:
1381 mLeftContBorderWidth = aPixelValue;
1382 return;
1383 default:
1384 NS_ERROR("invalid NS_SIDE arg");
1387 #ifdef ACCESSIBILITY
1388 a11y::AccType
1389 nsTableRowFrame::AccessibleType()
1391 return a11y::eHTMLTableRowType;
1393 #endif
1395 * Sets the NS_ROW_HAS_CELL_WITH_STYLE_HEIGHT bit to indicate whether
1396 * this row has any cells that have non-auto-height. (Row-spanning
1397 * cells are ignored.)
1399 void nsTableRowFrame::InitHasCellWithStyleHeight(nsTableFrame* aTableFrame)
1401 nsTableIterator iter(*this);
1403 for (nsIFrame* kidFrame = iter.First(); kidFrame; kidFrame = iter.Next()) {
1404 nsTableCellFrame *cellFrame = do_QueryFrame(kidFrame);
1405 if (!cellFrame) {
1406 NS_NOTREACHED("Table row has a non-cell child.");
1407 continue;
1409 // Ignore row-spanning cells
1410 const nsStyleCoord &cellHeight = cellFrame->StylePosition()->mHeight;
1411 if (aTableFrame->GetEffectiveRowSpan(*cellFrame) == 1 &&
1412 cellHeight.GetUnit() != eStyleUnit_Auto &&
1413 /* calc() with percentages treated like 'auto' */
1414 (!cellHeight.IsCalcUnit() || !cellHeight.HasPercent())) {
1415 AddStateBits(NS_ROW_HAS_CELL_WITH_STYLE_HEIGHT);
1416 return;
1419 RemoveStateBits(NS_ROW_HAS_CELL_WITH_STYLE_HEIGHT);
1422 void
1423 nsTableRowFrame::InvalidateFrame(uint32_t aDisplayItemKey)
1425 nsIFrame::InvalidateFrame(aDisplayItemKey);
1426 GetParent()->InvalidateFrameWithRect(GetVisualOverflowRect() + GetPosition(), aDisplayItemKey);
1429 void
1430 nsTableRowFrame::InvalidateFrameWithRect(const nsRect& aRect, uint32_t aDisplayItemKey)
1432 nsIFrame::InvalidateFrameWithRect(aRect, aDisplayItemKey);
1433 // If we have filters applied that would affects our bounds, then
1434 // we get an inactive layer created and this is computed
1435 // within FrameLayerBuilder
1436 GetParent()->InvalidateFrameWithRect(aRect + GetPosition(), aDisplayItemKey);
1439 /* ----- global methods ----- */
1441 nsTableRowFrame*
1442 NS_NewTableRowFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
1444 return new (aPresShell) nsTableRowFrame(aContext);
1447 NS_IMPL_FRAMEARENA_HELPERS(nsTableRowFrame)
1449 #ifdef DEBUG_FRAME_DUMP
1450 nsresult
1451 nsTableRowFrame::GetFrameName(nsAString& aResult) const
1453 return MakeFrameName(NS_LITERAL_STRING("TableRow"), aResult);
1455 #endif