Bug 563259: Fix shark/dtrace enabled combo. (r=me)
[mozilla-central.git] / layout / tables / nsTableRowFrame.cpp
blobc3e4595c6cb68786f9f543819f25108cb4d52350
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
24 * Alternatively, the contents of this file may be used under the terms of
25 * either of the GNU General Public License Version 2 or later (the "GPL"),
26 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
37 #include "nsTableRowFrame.h"
38 #include "nsTableRowGroupFrame.h"
39 #include "nsIRenderingContext.h"
40 #include "nsIPresShell.h"
41 #include "nsPresContext.h"
42 #include "nsStyleContext.h"
43 #include "nsStyleConsts.h"
44 #include "nsGkAtoms.h"
45 #include "nsIContent.h"
46 #include "nsTableFrame.h"
47 #include "nsTableCellFrame.h"
48 #include "nsCSSRendering.h"
49 #include "nsHTMLParts.h"
50 #include "nsTableColGroupFrame.h"
51 #include "nsTableColFrame.h"
52 #include "nsCOMPtr.h"
53 #include "nsDisplayList.h"
55 using namespace mozilla;
57 struct nsTableCellReflowState : public nsHTMLReflowState
59 nsTableCellReflowState(nsPresContext* aPresContext,
60 const nsHTMLReflowState& aParentReflowState,
61 nsIFrame* aFrame,
62 const nsSize& aAvailableSpace,
63 PRBool aInit = PR_TRUE)
64 : nsHTMLReflowState(aPresContext, aParentReflowState, aFrame,
65 aAvailableSpace, -1, -1, aInit)
69 void FixUp(const nsSize& aAvailSpace);
72 void nsTableCellReflowState::FixUp(const nsSize& aAvailSpace)
74 // fix the mComputed values during a pass 2 reflow since the cell can be a percentage base
75 NS_WARN_IF_FALSE(NS_UNCONSTRAINEDSIZE != aAvailSpace.width,
76 "have unconstrained width; this should only result from "
77 "very large sizes, not attempts at intrinsic width "
78 "calculation");
79 if (NS_UNCONSTRAINEDSIZE != ComputedWidth()) {
80 nscoord computedWidth =
81 aAvailSpace.width - mComputedBorderPadding.LeftRight();
82 computedWidth = NS_MAX(0, computedWidth);
83 SetComputedWidth(computedWidth);
85 if (NS_UNCONSTRAINEDSIZE != ComputedHeight() &&
86 NS_UNCONSTRAINEDSIZE != aAvailSpace.height) {
87 nscoord computedHeight =
88 aAvailSpace.height - mComputedBorderPadding.TopBottom();
89 computedHeight = NS_MAX(0, computedHeight);
90 SetComputedHeight(computedHeight);
94 void
95 nsTableRowFrame::InitChildReflowState(nsPresContext& aPresContext,
96 const nsSize& aAvailSize,
97 PRBool aBorderCollapse,
98 nsTableCellReflowState& aReflowState)
100 nsMargin collapseBorder;
101 nsMargin* pCollapseBorder = nsnull;
102 if (aBorderCollapse) {
103 // we only reflow cells, so don't need to check frame type
104 nsBCTableCellFrame* bcCellFrame = (nsBCTableCellFrame*)aReflowState.frame;
105 if (bcCellFrame) {
106 pCollapseBorder = bcCellFrame->GetBorderWidth(collapseBorder);
109 aReflowState.Init(&aPresContext, -1, -1, pCollapseBorder);
110 aReflowState.FixUp(aAvailSize);
113 void
114 nsTableRowFrame::SetFixedHeight(nscoord aValue)
116 nscoord height = NS_MAX(0, aValue);
117 if (HasFixedHeight()) {
118 if (height > mStyleFixedHeight) {
119 mStyleFixedHeight = height;
122 else {
123 mStyleFixedHeight = height;
124 if (height > 0) {
125 SetHasFixedHeight(PR_TRUE);
130 void
131 nsTableRowFrame::SetPctHeight(float aPctValue,
132 PRBool aForce)
134 nscoord height = NS_MAX(0, NSToCoordRound(aPctValue * 100.0f));
135 if (HasPctHeight()) {
136 if ((height > mStylePctHeight) || aForce) {
137 mStylePctHeight = height;
140 else {
141 mStylePctHeight = height;
142 if (height > 0.0f) {
143 SetHasPctHeight(PR_TRUE);
148 /* ----------- nsTableRowFrame ---------- */
150 NS_QUERYFRAME_HEAD(nsTableRowFrame)
151 NS_QUERYFRAME_ENTRY(nsTableRowFrame)
152 NS_QUERYFRAME_TAIL_INHERITING(nsHTMLContainerFrame)
154 nsTableRowFrame::nsTableRowFrame(nsStyleContext* aContext)
155 : nsHTMLContainerFrame(aContext)
157 mBits.mRowIndex = mBits.mFirstInserted = 0;
158 ResetHeight(0);
161 nsTableRowFrame::~nsTableRowFrame()
165 NS_IMETHODIMP
166 nsTableRowFrame::Init(nsIContent* aContent,
167 nsIFrame* aParent,
168 nsIFrame* aPrevInFlow)
170 nsresult rv;
172 // Let the base class do its initialization
173 rv = nsHTMLContainerFrame::Init(aContent, aParent, aPrevInFlow);
175 NS_ASSERTION(NS_STYLE_DISPLAY_TABLE_ROW == GetStyleDisplay()->mDisplay,
176 "wrong display on table row frame");
178 if (aPrevInFlow) {
179 // Set the row index
180 nsTableRowFrame* rowFrame = (nsTableRowFrame*)aPrevInFlow;
182 SetRowIndex(rowFrame->GetRowIndex());
185 return rv;
188 /* virtual */ void
189 nsTableRowFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
191 if (!aOldStyleContext) //avoid this on init
192 return;
194 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
196 if (tableFrame->IsBorderCollapse() &&
197 tableFrame->BCRecalcNeeded(aOldStyleContext, GetStyleContext())) {
198 nsRect damageArea(0, GetRowIndex(), tableFrame->GetColCount(), 1);
199 tableFrame->SetBCDamageArea(damageArea);
201 return;
204 NS_IMETHODIMP
205 nsTableRowFrame::AppendFrames(nsIAtom* aListName,
206 nsFrameList& aFrameList)
208 NS_ASSERTION(!aListName, "unexpected child list");
210 // Append the frames
211 // XXXbz why do we append here first, then append to table, while
212 // for InsertFrames we do it in the other order? Bug 507419 covers this.
213 const nsFrameList::Slice& newCells = mFrames.AppendFrames(nsnull, aFrameList);
215 // Add the new cell frames to the table
216 nsTableFrame *tableFrame = nsTableFrame::GetTableFrame(this);
217 for (nsFrameList::Enumerator e(newCells) ; !e.AtEnd(); e.Next()) {
218 nsTableCellFrame *cellFrame = do_QueryFrame(e.get());
219 NS_ASSERTION(cellFrame, "Unexpected frame");
220 if (cellFrame) {
221 // Add the cell to the cell map
222 tableFrame->AppendCell(*cellFrame, GetRowIndex());
226 PresContext()->PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange,
227 NS_FRAME_HAS_DIRTY_CHILDREN);
228 tableFrame->SetGeometryDirty();
230 return NS_OK;
234 NS_IMETHODIMP
235 nsTableRowFrame::InsertFrames(nsIAtom* aListName,
236 nsIFrame* aPrevFrame,
237 nsFrameList& aFrameList)
239 NS_ASSERTION(!aListName, "unexpected child list");
240 NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
241 "inserting after sibling frame with different parent");
243 // Get the table frame
244 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
246 // gather the new frames (only those which are cells) into an array
247 // XXXbz there shouldn't be any other ones here... can we just put
248 // them all in the array and not do all this QI nonsense?
249 nsIAtom* cellFrameType = (tableFrame->IsBorderCollapse()) ? nsGkAtoms::bcTableCellFrame : nsGkAtoms::tableCellFrame;
250 nsTableCellFrame* prevCellFrame = (nsTableCellFrame *)nsTableFrame::GetFrameAtOrBefore(this, aPrevFrame, cellFrameType);
251 nsTArray<nsTableCellFrame*> cellChildren;
252 for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) {
253 nsTableCellFrame *cellFrame = do_QueryFrame(e.get());
254 NS_ASSERTION(cellFrame, "Unexpected frame");
255 if (cellFrame) {
256 cellChildren.AppendElement(cellFrame);
259 // insert the cells into the cell map
260 PRInt32 colIndex = -1;
261 if (prevCellFrame) {
262 prevCellFrame->GetColIndex(colIndex);
264 tableFrame->InsertCells(cellChildren, GetRowIndex(), colIndex);
266 // Insert the frames in the frame list
267 mFrames.InsertFrames(nsnull, aPrevFrame, aFrameList);
269 PresContext()->PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange,
270 NS_FRAME_HAS_DIRTY_CHILDREN);
271 tableFrame->SetGeometryDirty();
273 return NS_OK;
276 NS_IMETHODIMP
277 nsTableRowFrame::RemoveFrame(nsIAtom* aListName,
278 nsIFrame* aOldFrame)
280 NS_ASSERTION(!aListName, "unexpected child list");
282 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
283 if (tableFrame) {
284 nsTableCellFrame *cellFrame = do_QueryFrame(aOldFrame);
285 if (cellFrame) {
286 PRInt32 colIndex;
287 cellFrame->GetColIndex(colIndex);
288 // remove the cell from the cell map
289 tableFrame->RemoveCell(cellFrame, GetRowIndex());
291 // Remove the frame and destroy it
292 mFrames.DestroyFrame(aOldFrame);
294 PresContext()->PresShell()->
295 FrameNeedsReflow(this, nsIPresShell::eTreeChange,
296 NS_FRAME_HAS_DIRTY_CHILDREN);
297 tableFrame->SetGeometryDirty();
299 else {
300 NS_ERROR("unexpected frame type");
301 return NS_ERROR_INVALID_ARG;
305 return NS_OK;
308 /* virtual */ nsMargin
309 nsTableRowFrame::GetUsedMargin() const
311 return nsMargin(0,0,0,0);
314 /* virtual */ nsMargin
315 nsTableRowFrame::GetUsedBorder() const
317 return nsMargin(0,0,0,0);
320 /* virtual */ nsMargin
321 nsTableRowFrame::GetUsedPadding() const
323 return nsMargin(0,0,0,0);
326 nscoord
327 GetHeightOfRowsSpannedBelowFirst(nsTableCellFrame& aTableCellFrame,
328 nsTableFrame& aTableFrame)
330 nscoord height = 0;
331 nscoord cellSpacingY = aTableFrame.GetCellSpacingY();
332 PRInt32 rowSpan = aTableFrame.GetEffectiveRowSpan(aTableCellFrame);
333 // add in height of rows spanned beyond the 1st one
334 nsIFrame* nextRow = aTableCellFrame.GetParent()->GetNextSibling();
335 for (PRInt32 rowX = 1; ((rowX < rowSpan) && nextRow);) {
336 if (nsGkAtoms::tableRowFrame == nextRow->GetType()) {
337 height += nextRow->GetSize().height;
338 rowX++;
340 height += cellSpacingY;
341 nextRow = nextRow->GetNextSibling();
343 return height;
346 nsTableCellFrame*
347 nsTableRowFrame::GetFirstCell()
349 nsIFrame* childFrame = mFrames.FirstChild();
350 while (childFrame) {
351 nsTableCellFrame *cellFrame = do_QueryFrame(childFrame);
352 if (cellFrame) {
353 return cellFrame;
355 childFrame = childFrame->GetNextSibling();
357 return nsnull;
361 * Post-reflow hook. This is where the table row does its post-processing
363 void
364 nsTableRowFrame::DidResize()
366 // Resize and re-align the cell frames based on our row height
367 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
368 if (!tableFrame)
369 return;
371 nsTableIterator iter(*this);
372 nsIFrame* childFrame = iter.First();
374 nsHTMLReflowMetrics desiredSize;
375 desiredSize.width = mRect.width;
376 desiredSize.height = mRect.height;
377 desiredSize.mOverflowArea = nsRect(0, 0, desiredSize.width,
378 desiredSize.height);
380 while (childFrame) {
381 nsTableCellFrame *cellFrame = do_QueryFrame(childFrame);
382 if (cellFrame) {
383 nscoord cellHeight = mRect.height + GetHeightOfRowsSpannedBelowFirst(*cellFrame, *tableFrame);
385 // resize the cell's height
386 nsRect cellRect = cellFrame->GetRect();
387 nsRect cellOverflowRect = cellFrame->GetOverflowRect();
388 if (cellRect.height != cellHeight)
390 cellFrame->SetSize(nsSize(cellRect.width, cellHeight));
391 nsTableFrame::InvalidateFrame(cellFrame, cellRect, cellOverflowRect,
392 PR_FALSE);
395 // realign cell content based on the new height. We might be able to
396 // skip this if the height didn't change... maybe. Hard to tell.
397 cellFrame->VerticallyAlignChild(mMaxCellAscent);
399 // Always store the overflow, even if the height didn't change, since
400 // we'll lose part of our overflow area otherwise.
401 ConsiderChildOverflow(desiredSize.mOverflowArea, cellFrame);
403 // Note that if the cell's *content* needs to change in response
404 // to this height, it will get a special height reflow.
406 // Get the next child
407 childFrame = iter.Next();
409 FinishAndStoreOverflow(&desiredSize);
410 if (HasView()) {
411 nsContainerFrame::SyncFrameViewAfterReflow(PresContext(), this, GetView(), &desiredSize.mOverflowArea, 0);
413 // Let our base class do the usual work
416 // returns max-ascent amongst all cells that have 'vertical-align: baseline'
417 // *including* cells with rowspans
418 nscoord nsTableRowFrame::GetMaxCellAscent() const
420 return mMaxCellAscent;
423 nscoord nsTableRowFrame::GetRowBaseline()
425 if(mMaxCellAscent)
426 return mMaxCellAscent;
428 // If we don't have a baseline on any of the cells we go for the lowest
429 // content edge of the inner block frames.
430 // Every table cell has a cell frame with its border and padding. Inside
431 // the cell is a block frame. The cell is as high as the tallest cell in
432 // the parent row. As a consequence the block frame might not touch both
433 // the top and the bottom padding of it parent cell frame at the same time.
435 // bbbbbbbbbbbbbbbbbb cell border: b
436 // bppppppppppppppppb cell padding: p
437 // bpxxxxxxxxxxxxxxpb inner block: x
438 // bpx xpb
439 // bpx xpb
440 // bpx xpb
441 // bpxxxxxxxxxxxxxxpb base line
442 // bp pb
443 // bp pb
444 // bppppppppppppppppb
445 // bbbbbbbbbbbbbbbbbb
447 nsTableIterator iter(*this);
448 nsIFrame* childFrame = iter.First();
449 nscoord ascent = 0;
450 while (childFrame) {
451 if (IS_TABLE_CELL(childFrame->GetType())) {
452 nsIFrame* firstKid = childFrame->GetFirstChild(nsnull);
453 ascent = NS_MAX(ascent, firstKid->GetRect().YMost());
455 // Get the next child
456 childFrame = iter.Next();
458 return ascent;
460 nscoord
461 nsTableRowFrame::GetHeight(nscoord aPctBasis) const
463 nscoord height = 0;
464 if ((aPctBasis > 0) && HasPctHeight()) {
465 height = NSToCoordRound(GetPctHeight() * (float)aPctBasis);
467 if (HasFixedHeight()) {
468 height = NS_MAX(height, GetFixedHeight());
470 return NS_MAX(height, GetContentHeight());
473 void
474 nsTableRowFrame::ResetHeight(nscoord aFixedHeight)
476 SetHasFixedHeight(PR_FALSE);
477 SetHasPctHeight(PR_FALSE);
478 SetFixedHeight(0);
479 SetPctHeight(0);
480 SetContentHeight(0);
482 if (aFixedHeight > 0) {
483 SetFixedHeight(aFixedHeight);
486 mMaxCellAscent = 0;
487 mMaxCellDescent = 0;
490 void
491 nsTableRowFrame::UpdateHeight(nscoord aHeight,
492 nscoord aAscent,
493 nscoord aDescent,
494 nsTableFrame* aTableFrame,
495 nsTableCellFrame* aCellFrame)
497 if (!aTableFrame || !aCellFrame) {
498 NS_ASSERTION(PR_FALSE , "invalid call");
499 return;
502 if (aHeight != NS_UNCONSTRAINEDSIZE) {
503 if (!(aCellFrame->HasVerticalAlignBaseline())) { // only the cell's height matters
504 if (GetHeight() < aHeight) {
505 PRInt32 rowSpan = aTableFrame->GetEffectiveRowSpan(*aCellFrame);
506 if (rowSpan == 1) {
507 SetContentHeight(aHeight);
511 else { // the alignment on the baseline can change the height
512 NS_ASSERTION((aAscent != NS_UNCONSTRAINEDSIZE) && (aDescent != NS_UNCONSTRAINEDSIZE), "invalid call");
513 // see if this is a long ascender
514 if (mMaxCellAscent < aAscent) {
515 mMaxCellAscent = aAscent;
517 // see if this is a long descender and without rowspan
518 if (mMaxCellDescent < aDescent) {
519 PRInt32 rowSpan = aTableFrame->GetEffectiveRowSpan(*aCellFrame);
520 if (rowSpan == 1) {
521 mMaxCellDescent = aDescent;
524 // keep the tallest height in sync
525 if (GetHeight() < mMaxCellAscent + mMaxCellDescent) {
526 SetContentHeight(mMaxCellAscent + mMaxCellDescent);
532 nscoord
533 nsTableRowFrame::CalcHeight(const nsHTMLReflowState& aReflowState)
535 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
536 if (!tableFrame)
537 return 0;
539 nscoord computedHeight = (NS_UNCONSTRAINEDSIZE == aReflowState.ComputedHeight())
540 ? 0 : aReflowState.ComputedHeight();
541 ResetHeight(computedHeight);
543 const nsStylePosition* position = GetStylePosition();
544 if (eStyleUnit_Coord == position->mHeight.GetUnit()) {
545 SetFixedHeight(position->mHeight.GetCoordValue());
547 else if (eStyleUnit_Percent == position->mHeight.GetUnit()) {
548 SetPctHeight(position->mHeight.GetPercentValue());
551 for (nsIFrame* kidFrame = mFrames.FirstChild(); kidFrame;
552 kidFrame = kidFrame->GetNextSibling()) {
553 nsTableCellFrame *cellFrame = do_QueryFrame(kidFrame);
554 if (cellFrame) {
555 nsSize desSize = cellFrame->GetDesiredSize();
556 if ((NS_UNCONSTRAINEDSIZE == aReflowState.availableHeight) && !GetPrevInFlow()) {
557 CalculateCellActualHeight(cellFrame, desSize.height);
559 // height may have changed, adjust descent to absorb any excess difference
560 nscoord ascent;
561 if (!kidFrame->GetFirstChild(nsnull)->GetFirstChild(nsnull))
562 ascent = desSize.height;
563 else
564 ascent = cellFrame->GetCellBaseline();
565 nscoord descent = desSize.height - ascent;
566 UpdateHeight(desSize.height, ascent, descent, tableFrame, cellFrame);
569 return GetHeight();
573 * We need a custom display item for table row backgrounds. This is only used
574 * when the table row is the root of a stacking context (e.g., has 'opacity').
575 * Table row backgrounds can extend beyond the row frame bounds, when
576 * the row contains row-spanning cells.
578 class nsDisplayTableRowBackground : public nsDisplayTableItem {
579 public:
580 nsDisplayTableRowBackground(nsTableRowFrame* aFrame) : nsDisplayTableItem(aFrame) {
581 MOZ_COUNT_CTOR(nsDisplayTableRowBackground);
583 #ifdef NS_BUILD_REFCNT_LOGGING
584 virtual ~nsDisplayTableRowBackground() {
585 MOZ_COUNT_DTOR(nsDisplayTableRowBackground);
587 #endif
589 virtual void Paint(nsDisplayListBuilder* aBuilder,
590 nsIRenderingContext* aCtx);
591 NS_DISPLAY_DECL_NAME("TableRowBackground")
594 void
595 nsDisplayTableRowBackground::Paint(nsDisplayListBuilder* aBuilder,
596 nsIRenderingContext* aCtx) {
597 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(mFrame);
599 nsPoint pt = aBuilder->ToReferenceFrame(mFrame);
600 TableBackgroundPainter painter(tableFrame,
601 TableBackgroundPainter::eOrigin_TableRow,
602 mFrame->PresContext(), *aCtx,
603 mVisibleRect, pt,
604 aBuilder->GetBackgroundPaintFlags());
605 painter.PaintRow(static_cast<nsTableRowFrame*>(mFrame));
608 NS_IMETHODIMP
609 nsTableRowFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
610 const nsRect& aDirtyRect,
611 const nsDisplayListSet& aLists)
613 if (!IsVisibleInSelection(aBuilder))
614 return NS_OK;
616 PRBool isRoot = aBuilder->IsAtRootOfPseudoStackingContext();
617 nsDisplayTableItem* item = nsnull;
618 if (isRoot) {
619 // This background is created regardless of whether this frame is
620 // visible or not. Visibility decisions are delegated to the
621 // table background painter.
622 // We would use nsDisplayGeneric for this rare case except that we
623 // need the background to be larger than the row frame in some
624 // cases.
625 item = new (aBuilder) nsDisplayTableRowBackground(this);
626 nsresult rv = aLists.BorderBackground()->AppendNewToTop(item);
627 NS_ENSURE_SUCCESS(rv, rv);
630 return nsTableFrame::DisplayGenericTablePart(aBuilder, this, aDirtyRect, aLists, item);
633 PRIntn
634 nsTableRowFrame::GetSkipSides() const
636 PRIntn skip = 0;
637 if (nsnull != GetPrevInFlow()) {
638 skip |= 1 << NS_SIDE_TOP;
640 if (nsnull != GetNextInFlow()) {
641 skip |= 1 << NS_SIDE_BOTTOM;
643 return skip;
646 // Calculate the cell's actual height given its pass2 height.
647 // Takes into account the specified height (in the style).
648 // Modifies the desired height that is passed in.
649 nsresult
650 nsTableRowFrame::CalculateCellActualHeight(nsTableCellFrame* aCellFrame,
651 nscoord& aDesiredHeight)
653 nscoord specifiedHeight = 0;
655 // Get the height specified in the style information
656 const nsStylePosition* position = aCellFrame->GetStylePosition();
658 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
659 if (!tableFrame)
660 return NS_ERROR_NULL_POINTER;
662 PRInt32 rowSpan = tableFrame->GetEffectiveRowSpan(*aCellFrame);
664 switch (position->mHeight.GetUnit()) {
665 case eStyleUnit_Coord:
666 specifiedHeight = position->mHeight.GetCoordValue();
667 if (1 == rowSpan)
668 SetFixedHeight(specifiedHeight);
669 break;
670 case eStyleUnit_Percent: {
671 if (1 == rowSpan)
672 SetPctHeight(position->mHeight.GetPercentValue());
673 // pct heights are handled when all of the cells are finished, so don't set specifiedHeight
674 break;
676 case eStyleUnit_Auto:
677 default:
678 break;
681 // If the specified height is greater than the desired height, then use the specified height
682 if (specifiedHeight > aDesiredHeight)
683 aDesiredHeight = specifiedHeight;
685 return NS_OK;
688 // Calculates the available width for the table cell based on the known
689 // column widths taking into account column spans and column spacing
690 static nscoord
691 CalcAvailWidth(nsTableFrame& aTableFrame,
692 nsTableCellFrame& aCellFrame,
693 nscoord aCellSpacingX)
695 nscoord cellAvailWidth = 0;
696 PRInt32 colIndex;
697 aCellFrame.GetColIndex(colIndex);
698 PRInt32 colspan = aTableFrame.GetEffectiveColSpan(aCellFrame);
699 NS_ASSERTION(colspan > 0, "effective colspan should be positive");
701 for (PRInt32 spanX = 0; spanX < colspan; spanX++) {
702 cellAvailWidth += aTableFrame.GetColumnWidth(colIndex + spanX);
703 if (spanX > 0 &&
704 aTableFrame.ColumnHasCellSpacingBefore(colIndex + spanX)) {
705 cellAvailWidth += aCellSpacingX;
708 return cellAvailWidth;
711 nscoord
712 GetSpaceBetween(PRInt32 aPrevColIndex,
713 PRInt32 aColIndex,
714 PRInt32 aColSpan,
715 nsTableFrame& aTableFrame,
716 nscoord aCellSpacingX,
717 PRBool aIsLeftToRight,
718 PRBool aCheckVisibility)
720 nscoord space = 0;
721 PRInt32 colX;
722 if (aIsLeftToRight) {
723 for (colX = aPrevColIndex + 1; aColIndex > colX; colX++) {
724 PRBool isCollapsed = PR_FALSE;
725 if (!aCheckVisibility) {
726 space += aTableFrame.GetColumnWidth(colX);
728 else {
729 nsTableColFrame* colFrame = aTableFrame.GetColFrame(colX);
730 const nsStyleVisibility* colVis = colFrame->GetStyleVisibility();
731 PRBool collapseCol = (NS_STYLE_VISIBILITY_COLLAPSE == colVis->mVisible);
732 nsIFrame* cgFrame = colFrame->GetParent();
733 const nsStyleVisibility* groupVis = cgFrame->GetStyleVisibility();
734 PRBool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE ==
735 groupVis->mVisible);
736 isCollapsed = collapseCol || collapseGroup;
737 if (!isCollapsed)
738 space += aTableFrame.GetColumnWidth(colX);
740 if (!isCollapsed && aTableFrame.ColumnHasCellSpacingBefore(colX)) {
741 space += aCellSpacingX;
745 else {
746 PRInt32 lastCol = aColIndex + aColSpan - 1;
747 for (colX = aPrevColIndex - 1; colX > lastCol; colX--) {
748 PRBool isCollapsed = PR_FALSE;
749 if (!aCheckVisibility) {
750 space += aTableFrame.GetColumnWidth(colX);
752 else {
753 nsTableColFrame* colFrame = aTableFrame.GetColFrame(colX);
754 const nsStyleVisibility* colVis = colFrame->GetStyleVisibility();
755 PRBool collapseCol = (NS_STYLE_VISIBILITY_COLLAPSE == colVis->mVisible);
756 nsIFrame* cgFrame = colFrame->GetParent();
757 const nsStyleVisibility* groupVis = cgFrame->GetStyleVisibility();
758 PRBool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE ==
759 groupVis->mVisible);
760 isCollapsed = collapseCol || collapseGroup;
761 if (!isCollapsed)
762 space += aTableFrame.GetColumnWidth(colX);
764 if (!isCollapsed && aTableFrame.ColumnHasCellSpacingBefore(colX)) {
765 space += aCellSpacingX;
769 return space;
772 // subtract the heights of aRow's prev in flows from the unpaginated height
773 static
774 nscoord CalcHeightFromUnpaginatedHeight(nsPresContext* aPresContext,
775 nsTableRowFrame& aRow)
777 nscoord height = 0;
778 nsTableRowFrame* firstInFlow = (nsTableRowFrame*)aRow.GetFirstInFlow();
779 if (!firstInFlow) ABORT1(0);
780 if (firstInFlow->HasUnpaginatedHeight()) {
781 height = firstInFlow->GetUnpaginatedHeight(aPresContext);
782 for (nsIFrame* prevInFlow = aRow.GetPrevInFlow(); prevInFlow;
783 prevInFlow = prevInFlow->GetPrevInFlow()) {
784 height -= prevInFlow->GetSize().height;
787 return NS_MAX(height, 0);
790 NS_METHOD
791 nsTableRowFrame::ReflowChildren(nsPresContext* aPresContext,
792 nsHTMLReflowMetrics& aDesiredSize,
793 const nsHTMLReflowState& aReflowState,
794 nsTableFrame& aTableFrame,
795 nsReflowStatus& aStatus)
797 aStatus = NS_FRAME_COMPLETE;
799 PRBool borderCollapse = (((nsTableFrame*)aTableFrame.GetFirstInFlow())->IsBorderCollapse());
801 // XXXldb Should we be checking constrained height instead?
802 PRBool isPaginated = aPresContext->IsPaginated();
804 nsresult rv = NS_OK;
806 nscoord cellSpacingX = aTableFrame.GetCellSpacingX();
807 PRInt32 cellColSpan = 1; // must be defined here so it's set properly for non-cell kids
809 nsTableIterator iter(*this);
810 // remember the col index of the previous cell to handle rowspans into this row
811 PRInt32 firstPrevColIndex = (iter.IsLeftToRight()) ? -1 : aTableFrame.GetColCount();
812 PRInt32 prevColIndex = firstPrevColIndex;
813 nscoord x = 0; // running total of children x offset
815 // This computes the max of all cell heights
816 nscoord cellMaxHeight = 0;
818 // Reflow each of our existing cell frames
819 for (nsIFrame* kidFrame = iter.First(); kidFrame; kidFrame = iter.Next()) {
820 nsTableCellFrame *cellFrame = do_QueryFrame(kidFrame);
821 if (!cellFrame) {
822 // XXXldb nsCSSFrameConstructor needs to enforce this!
823 NS_NOTREACHED("yikes, a non-row child");
825 // it's an unknown frame type, give it a generic reflow and ignore the results
826 nsTableCellReflowState kidReflowState(aPresContext, aReflowState,
827 kidFrame, nsSize(0,0), PR_FALSE);
828 InitChildReflowState(*aPresContext, nsSize(0,0), PR_FALSE, kidReflowState);
829 nsHTMLReflowMetrics desiredSize;
830 nsReflowStatus status;
831 ReflowChild(kidFrame, aPresContext, desiredSize, kidReflowState, 0, 0, 0, status);
832 kidFrame->DidReflow(aPresContext, nsnull, NS_FRAME_REFLOW_FINISHED);
834 continue;
837 // See if we should only reflow the dirty child frames
838 PRBool doReflowChild = PR_TRUE;
839 if (!aReflowState.ShouldReflowAllKids() &&
840 !aTableFrame.IsGeometryDirty() &&
841 !NS_SUBTREE_DIRTY(kidFrame)) {
842 if (!aReflowState.mFlags.mSpecialHeightReflow)
843 doReflowChild = PR_FALSE;
845 else if ((NS_UNCONSTRAINEDSIZE != aReflowState.availableHeight)) {
846 // We don't reflow a rowspan >1 cell here with a constrained height.
847 // That happens in nsTableRowGroupFrame::SplitSpanningCells.
848 if (aTableFrame.GetEffectiveRowSpan(*cellFrame) > 1) {
849 doReflowChild = PR_FALSE;
852 if (aReflowState.mFlags.mSpecialHeightReflow) {
853 if (!isPaginated && !(cellFrame->GetStateBits() &
854 NS_FRAME_CONTAINS_RELATIVE_HEIGHT)) {
855 continue;
859 PRInt32 cellColIndex;
860 cellFrame->GetColIndex(cellColIndex);
861 cellColSpan = aTableFrame.GetEffectiveColSpan(*cellFrame);
863 // If the adjacent cell is in a prior row (because of a rowspan) add in the space
864 if ((iter.IsLeftToRight() && (prevColIndex != (cellColIndex - 1))) ||
865 (!iter.IsLeftToRight() && (prevColIndex != cellColIndex + cellColSpan))) {
866 x += GetSpaceBetween(prevColIndex, cellColIndex, cellColSpan, aTableFrame,
867 cellSpacingX, iter.IsLeftToRight(), PR_FALSE);
870 // remember the rightmost (ltr) or leftmost (rtl) column this cell spans into
871 prevColIndex = (iter.IsLeftToRight()) ? cellColIndex + (cellColSpan - 1) : cellColIndex;
873 // Reflow the child frame
874 nsRect kidRect = kidFrame->GetRect();
875 nsRect kidOverflowRect = kidFrame->GetOverflowRect();
876 PRBool firstReflow =
877 (kidFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0;
879 if (doReflowChild) {
880 // Calculate the available width for the table cell using the known column widths
881 nscoord availCellWidth =
882 CalcAvailWidth(aTableFrame, *cellFrame, cellSpacingX);
884 nsHTMLReflowMetrics desiredSize;
886 // If the avail width is not the same as last time we reflowed the cell or
887 // the cell wants to be bigger than what was available last time or
888 // it is a style change reflow or we are printing, then we must reflow the
889 // cell. Otherwise we can skip the reflow.
890 // XXXldb Why is this condition distinct from doReflowChild above?
891 nsSize cellDesiredSize = cellFrame->GetDesiredSize();
892 if ((availCellWidth != cellFrame->GetPriorAvailWidth()) ||
893 (cellDesiredSize.width > cellFrame->GetPriorAvailWidth()) ||
894 (GetStateBits() & NS_FRAME_IS_DIRTY) ||
895 isPaginated ||
896 NS_SUBTREE_DIRTY(cellFrame) ||
897 // See if it needs a special reflow, or if it had one that we need to undo.
898 (cellFrame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT) ||
899 HasPctHeight()) {
900 // Reflow the cell to fit the available width, height
901 // XXX The old IR_ChildIsDirty code used availCellWidth here.
902 nsSize kidAvailSize(availCellWidth, aReflowState.availableHeight);
904 // Reflow the child
905 nsTableCellReflowState kidReflowState(aPresContext, aReflowState,
906 kidFrame, kidAvailSize, PR_FALSE);
907 InitChildReflowState(*aPresContext, kidAvailSize, borderCollapse,
908 kidReflowState);
910 nsReflowStatus status;
911 rv = ReflowChild(kidFrame, aPresContext, desiredSize, kidReflowState,
912 x, 0, NS_FRAME_INVALIDATE_ON_MOVE, status);
914 // allow the table to determine if/how the table needs to be rebalanced
915 // If any of the cells are not complete, then we're not complete
916 if (NS_FRAME_IS_NOT_COMPLETE(status)) {
917 aStatus = NS_FRAME_NOT_COMPLETE;
920 else {
921 if (x != kidRect.x) {
922 kidFrame->InvalidateOverflowRect();
925 desiredSize.width = cellDesiredSize.width;
926 desiredSize.height = cellDesiredSize.height;
927 if (cellFrame->HasOverflowRect())
928 desiredSize.mOverflowArea = cellFrame->GetOverflowRect();
929 else
930 desiredSize.mOverflowArea.SetRect(0, 0, cellDesiredSize.width,
931 cellDesiredSize.height);
933 // if we are in a floated table, our position is not yet established, so we cannot reposition our views
934 // the containing block will do this for us after positioning the table
935 if (!aTableFrame.GetStyleDisplay()->IsFloating()) {
936 // Because we may have moved the frame we need to make sure any views are
937 // positioned properly. We have to do this, because any one of our parent
938 // frames could have moved and we have no way of knowing...
939 nsTableFrame::RePositionViews(kidFrame);
943 if (NS_UNCONSTRAINEDSIZE == aReflowState.availableHeight) {
944 if (!GetPrevInFlow()) {
945 // Calculate the cell's actual height given its pass2 height. This
946 // function takes into account the specified height (in the style)
947 CalculateCellActualHeight(cellFrame, desiredSize.height);
949 // height may have changed, adjust descent to absorb any excess difference
950 nscoord ascent;
951 if (!kidFrame->GetFirstChild(nsnull)->GetFirstChild(nsnull))
952 ascent = desiredSize.height;
953 else
954 ascent = ((nsTableCellFrame *)kidFrame)->GetCellBaseline();
955 nscoord descent = desiredSize.height - ascent;
956 UpdateHeight(desiredSize.height, ascent, descent, &aTableFrame, cellFrame);
958 else {
959 cellMaxHeight = NS_MAX(cellMaxHeight, desiredSize.height);
960 PRInt32 rowSpan = aTableFrame.GetEffectiveRowSpan((nsTableCellFrame&)*kidFrame);
961 if (1 == rowSpan) {
962 SetContentHeight(cellMaxHeight);
966 // Place the child
967 desiredSize.width = availCellWidth;
969 FinishReflowChild(kidFrame, aPresContext, nsnull, desiredSize, x, 0, 0);
971 nsTableFrame::InvalidateFrame(kidFrame, kidRect, kidOverflowRect,
972 firstReflow);
974 x += desiredSize.width;
976 else {
977 if (kidRect.x != x) {
978 // Invalidate the old position
979 kidFrame->InvalidateOverflowRect();
980 // move to the new position
981 kidFrame->SetPosition(nsPoint(x, kidRect.y));
982 nsTableFrame::RePositionViews(kidFrame);
983 // invalidate the new position
984 kidFrame->InvalidateOverflowRect();
986 // we need to account for the cell's width even if it isn't reflowed
987 x += kidRect.width;
989 if (kidFrame->GetNextInFlow()) {
990 aStatus = NS_FRAME_NOT_COMPLETE;
993 ConsiderChildOverflow(aDesiredSize.mOverflowArea, kidFrame);
994 x += cellSpacingX;
997 // just set our width to what was available. The table will calculate the width and not use our value.
998 aDesiredSize.width = aReflowState.availableWidth;
1000 if (aReflowState.mFlags.mSpecialHeightReflow) {
1001 aDesiredSize.height = mRect.height;
1003 else if (NS_UNCONSTRAINEDSIZE == aReflowState.availableHeight) {
1004 aDesiredSize.height = CalcHeight(aReflowState);
1005 if (GetPrevInFlow()) {
1006 nscoord height = CalcHeightFromUnpaginatedHeight(aPresContext, *this);
1007 aDesiredSize.height = NS_MAX(aDesiredSize.height, height);
1009 else {
1010 if (isPaginated && HasStyleHeight()) {
1011 // set the unpaginated height so next in flows can try to honor it
1012 SetHasUnpaginatedHeight(PR_TRUE);
1013 SetUnpaginatedHeight(aPresContext, aDesiredSize.height);
1015 if (isPaginated && HasUnpaginatedHeight()) {
1016 aDesiredSize.height = NS_MAX(aDesiredSize.height, GetUnpaginatedHeight(aPresContext));
1020 else { // constrained height, paginated
1021 // Compute the height we should have from style (subtracting the
1022 // height from our prev-in-flows from the style height)
1023 nscoord styleHeight = CalcHeightFromUnpaginatedHeight(aPresContext, *this);
1024 if (styleHeight > aReflowState.availableHeight) {
1025 styleHeight = aReflowState.availableHeight;
1026 NS_FRAME_SET_INCOMPLETE(aStatus);
1028 aDesiredSize.height = NS_MAX(cellMaxHeight, styleHeight);
1030 nsRect rowRect(0, 0, aDesiredSize.width, aDesiredSize.height);
1031 aDesiredSize.mOverflowArea.UnionRect(aDesiredSize.mOverflowArea, rowRect);
1032 FinishAndStoreOverflow(&aDesiredSize);
1033 return rv;
1036 /** Layout the entire row.
1037 * This method stacks cells horizontally according to HTML 4.0 rules.
1039 NS_METHOD
1040 nsTableRowFrame::Reflow(nsPresContext* aPresContext,
1041 nsHTMLReflowMetrics& aDesiredSize,
1042 const nsHTMLReflowState& aReflowState,
1043 nsReflowStatus& aStatus)
1045 DO_GLOBAL_REFLOW_COUNT("nsTableRowFrame");
1046 DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
1047 nsresult rv = NS_OK;
1049 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
1050 if (!tableFrame)
1051 return NS_ERROR_NULL_POINTER;
1053 const nsStyleVisibility* rowVis = GetStyleVisibility();
1054 PRBool collapseRow = (NS_STYLE_VISIBILITY_COLLAPSE == rowVis->mVisible);
1055 if (collapseRow) {
1056 tableFrame->SetNeedToCollapse(PR_TRUE);
1059 // see if a special height reflow needs to occur due to having a pct height
1060 nsTableFrame::CheckRequestSpecialHeightReflow(aReflowState);
1062 // See if we have a cell with specified/pct height
1063 InitHasCellWithStyleHeight(tableFrame);
1065 rv = ReflowChildren(aPresContext, aDesiredSize, aReflowState, *tableFrame,
1066 aStatus);
1068 // just set our width to what was available. The table will calculate the width and not use our value.
1069 aDesiredSize.width = aReflowState.availableWidth;
1071 // If our parent is in initial reflow, it'll handle invalidating our
1072 // entire overflow rect.
1073 if (!(GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
1074 CheckInvalidateSizeChange(aDesiredSize);
1077 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
1078 return rv;
1082 * This function is called by the row group frame's SplitRowGroup() code when
1083 * pushing a row frame that has cell frames that span into it. The cell frame
1084 * should be reflowed with the specified height
1086 nscoord
1087 nsTableRowFrame::ReflowCellFrame(nsPresContext* aPresContext,
1088 const nsHTMLReflowState& aReflowState,
1089 PRBool aIsTopOfPage,
1090 nsTableCellFrame* aCellFrame,
1091 nscoord aAvailableHeight,
1092 nsReflowStatus& aStatus)
1094 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
1095 if (!tableFrame)
1096 ABORT1(NS_ERROR_NULL_POINTER);
1098 // Reflow the cell frame with the specified height. Use the existing width
1099 nsRect cellRect = aCellFrame->GetRect();
1100 nsRect cellOverflowRect = aCellFrame->GetOverflowRect();
1102 nsSize availSize(cellRect.width, aAvailableHeight);
1103 PRBool borderCollapse = ((nsTableFrame*)tableFrame->GetFirstInFlow())->IsBorderCollapse();
1104 nsTableCellReflowState cellReflowState(aPresContext, aReflowState,
1105 aCellFrame, availSize, PR_FALSE);
1106 InitChildReflowState(*aPresContext, availSize, borderCollapse, cellReflowState);
1107 cellReflowState.mFlags.mIsTopOfPage = aIsTopOfPage;
1109 nsHTMLReflowMetrics desiredSize;
1111 ReflowChild(aCellFrame, aPresContext, desiredSize, cellReflowState,
1112 0, 0, NS_FRAME_NO_MOVE_FRAME, aStatus);
1113 PRBool fullyComplete = NS_FRAME_IS_COMPLETE(aStatus) && !NS_FRAME_IS_TRUNCATED(aStatus);
1114 if (fullyComplete) {
1115 desiredSize.height = aAvailableHeight;
1117 aCellFrame->SetSize(nsSize(cellRect.width, desiredSize.height));
1119 // Note: VerticallyAlignChild can affect the overflow rect.
1120 // XXX What happens if this cell has 'vertical-align: baseline' ?
1121 // XXX Why is it assumed that the cell's ascent hasn't changed ?
1122 if (fullyComplete) {
1123 aCellFrame->VerticallyAlignChild(mMaxCellAscent);
1126 nsTableFrame::InvalidateFrame(aCellFrame, cellRect,
1127 cellOverflowRect,
1128 (aCellFrame->GetStateBits() &
1129 NS_FRAME_FIRST_REFLOW) != 0);
1131 aCellFrame->DidReflow(aPresContext, nsnull, NS_FRAME_REFLOW_FINISHED);
1133 return desiredSize.height;
1136 nscoord
1137 nsTableRowFrame::CollapseRowIfNecessary(nscoord aRowOffset,
1138 nscoord aWidth,
1139 PRBool aCollapseGroup,
1140 PRBool& aDidCollapse)
1142 const nsStyleVisibility* rowVis = GetStyleVisibility();
1143 PRBool collapseRow = (NS_STYLE_VISIBILITY_COLLAPSE == rowVis->mVisible);
1144 nsTableFrame* tableFrame = static_cast<nsTableFrame*>(nsTableFrame::GetTableFrame(this)->GetFirstInFlow());
1145 if (!tableFrame)
1146 return 0;
1147 if (collapseRow) {
1148 tableFrame->SetNeedToCollapse(PR_TRUE);
1151 if (aRowOffset != 0) {
1152 // We're moving, so invalidate our old position
1153 InvalidateOverflowRect();
1156 nsRect rowRect = GetRect();
1157 nsRect oldRect = rowRect;
1158 nsRect oldOverflowRect = GetOverflowRect();
1160 rowRect.y -= aRowOffset;
1161 rowRect.width = aWidth;
1162 nsRect overflowArea(0, 0, 0, 0);
1163 nscoord shift = 0;
1164 nscoord cellSpacingX = tableFrame->GetCellSpacingX();
1165 nscoord cellSpacingY = tableFrame->GetCellSpacingY();
1167 if (aCollapseGroup || collapseRow) {
1168 nsTableCellFrame* cellFrame = GetFirstCell();
1169 aDidCollapse = PR_TRUE;
1170 shift = rowRect.height + cellSpacingY;
1171 while (cellFrame) {
1172 nsRect cRect = cellFrame->GetRect();
1173 // If aRowOffset != 0, there's no point in invalidating the cells, since
1174 // we've already invalidated our overflow area. Note that we _do_ still
1175 // need to invalidate if our row is not moving, because the cell might
1176 // span out of this row, so invalidating our row rect won't do enough.
1177 if (aRowOffset == 0) {
1178 Invalidate(cRect);
1180 cRect.height = 0;
1181 cellFrame->SetRect(cRect);
1182 cellFrame = cellFrame->GetNextCell();
1184 rowRect.height = 0;
1186 else { // row is not collapsed
1187 nsTableIterator iter(*this);
1188 // remember the col index of the previous cell to handle rowspans into this
1189 // row
1190 PRInt32 firstPrevColIndex = (iter.IsLeftToRight()) ? -1 :
1191 tableFrame->GetColCount();
1192 PRInt32 prevColIndex = firstPrevColIndex;
1193 nscoord x = 0; // running total of children x offset
1195 PRInt32 colIncrement = iter.IsLeftToRight() ? 1 : -1;
1197 //nscoord x = cellSpacingX;
1199 nsIFrame* kidFrame = iter.First();
1200 while (kidFrame) {
1201 nsTableCellFrame *cellFrame = do_QueryFrame(kidFrame);
1202 if (cellFrame) {
1203 PRInt32 cellColIndex;
1204 cellFrame->GetColIndex(cellColIndex);
1205 PRInt32 cellColSpan = tableFrame->GetEffectiveColSpan(*cellFrame);
1207 // If the adjacent cell is in a prior row (because of a rowspan) add in
1208 // the space
1209 if ((iter.IsLeftToRight() && (prevColIndex != (cellColIndex - 1))) ||
1210 (!iter.IsLeftToRight() &&
1211 (prevColIndex != cellColIndex + cellColSpan))) {
1212 x += GetSpaceBetween(prevColIndex, cellColIndex, cellColSpan,
1213 *tableFrame, cellSpacingX, iter.IsLeftToRight(),
1214 PR_TRUE);
1216 nsRect cRect(x, 0, 0, rowRect.height);
1218 // remember the rightmost (ltr) or leftmost (rtl) column this cell
1219 // spans into
1220 prevColIndex = (iter.IsLeftToRight()) ?
1221 cellColIndex + (cellColSpan - 1) : cellColIndex;
1222 PRInt32 startIndex = (iter.IsLeftToRight()) ?
1223 cellColIndex : cellColIndex + (cellColSpan - 1);
1224 PRInt32 actualColSpan = cellColSpan;
1225 PRBool isVisible = PR_FALSE;
1226 for (PRInt32 colX = startIndex; actualColSpan > 0;
1227 colX += colIncrement, actualColSpan--) {
1229 nsTableColFrame* colFrame = tableFrame->GetColFrame(colX);
1230 const nsStyleVisibility* colVis = colFrame->GetStyleVisibility();
1231 PRBool collapseCol = (NS_STYLE_VISIBILITY_COLLAPSE ==
1232 colVis->mVisible);
1233 nsIFrame* cgFrame = colFrame->GetParent();
1234 const nsStyleVisibility* groupVis = cgFrame->GetStyleVisibility();
1235 PRBool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE ==
1236 groupVis->mVisible);
1237 PRBool isCollapsed = collapseCol || collapseGroup;
1238 if (!isCollapsed) {
1239 cRect.width += tableFrame->GetColumnWidth(colX);
1240 isVisible = PR_TRUE;
1241 if ((actualColSpan > 1)) {
1242 nsTableColFrame* nextColFrame =
1243 tableFrame->GetColFrame(colX + colIncrement);
1244 const nsStyleVisibility* nextColVis =
1245 nextColFrame->GetStyleVisibility();
1246 if ( (NS_STYLE_VISIBILITY_COLLAPSE != nextColVis->mVisible) &&
1247 tableFrame->ColumnHasCellSpacingBefore(colX + colIncrement)) {
1248 cRect.width += cellSpacingX;
1253 x += cRect.width;
1254 if (isVisible)
1255 x += cellSpacingX;
1256 PRInt32 actualRowSpan = tableFrame->GetEffectiveRowSpan(*cellFrame);
1257 nsTableRowFrame* rowFrame = GetNextRow();
1258 for (actualRowSpan--; actualRowSpan > 0 && rowFrame; actualRowSpan--) {
1259 const nsStyleVisibility* nextRowVis = rowFrame->GetStyleVisibility();
1260 PRBool collapseNextRow = (NS_STYLE_VISIBILITY_COLLAPSE ==
1261 nextRowVis->mVisible);
1262 if (!collapseNextRow) {
1263 nsRect nextRect = rowFrame->GetRect();
1264 cRect.height += nextRect.height + cellSpacingY;
1266 rowFrame = rowFrame->GetNextRow();
1269 nsRect oldCellRect = cellFrame->GetRect();
1270 nsRect oldCellOverflowRect = cellFrame->GetOverflowRect();
1272 if (aRowOffset == 0 && cRect.TopLeft() != oldCellRect.TopLeft()) {
1273 // We're moving the cell. Invalidate the old overflow area
1274 cellFrame->InvalidateOverflowRect();
1277 cellFrame->SetRect(cRect);
1279 // XXXbz This looks completely bogus in the cases when we didn't
1280 // collapse the cell!
1281 nsRect cellOverflow = nsRect(0, 0, cRect.width, cRect.height);
1282 cellFrame->FinishAndStoreOverflow(&cellOverflow, nsSize(cRect.width,
1283 cRect.height));
1284 nsTableFrame::RePositionViews(cellFrame);
1285 ConsiderChildOverflow(overflowArea, cellFrame);
1287 if (aRowOffset == 0) {
1288 nsTableFrame::InvalidateFrame(cellFrame, oldCellRect,
1289 oldCellOverflowRect, PR_FALSE);
1292 kidFrame = iter.Next(); // Get the next child
1296 SetRect(rowRect);
1297 overflowArea.UnionRect(nsRect(0,0,rowRect.width, rowRect.height),
1298 overflowArea);
1299 FinishAndStoreOverflow(&overflowArea, nsSize(rowRect.width,
1300 rowRect.height));
1302 nsTableFrame::RePositionViews(this);
1303 nsTableFrame::InvalidateFrame(this, oldRect, oldOverflowRect, PR_FALSE);
1304 return shift;
1308 * The following method is called by the row group frame's SplitRowGroup()
1309 * when it creates a continuing cell frame and wants to insert it into the
1310 * row's child list.
1312 void
1313 nsTableRowFrame::InsertCellFrame(nsTableCellFrame* aFrame,
1314 PRInt32 aColIndex)
1316 // Find the cell frame where col index < aColIndex
1317 nsTableCellFrame* priorCell = nsnull;
1318 for (nsIFrame* child = mFrames.FirstChild(); child;
1319 child = child->GetNextSibling()) {
1320 nsTableCellFrame *cellFrame = do_QueryFrame(child);
1321 if (cellFrame) {
1322 PRInt32 colIndex;
1323 cellFrame->GetColIndex(colIndex);
1324 if (colIndex < aColIndex) {
1325 priorCell = cellFrame;
1327 else break;
1330 mFrames.InsertFrame(this, priorCell, aFrame);
1333 nsIAtom*
1334 nsTableRowFrame::GetType() const
1336 return nsGkAtoms::tableRowFrame;
1339 nsTableRowFrame*
1340 nsTableRowFrame::GetNextRow() const
1342 nsIFrame* childFrame = GetNextSibling();
1343 while (childFrame) {
1344 nsTableRowFrame *rowFrame = do_QueryFrame(childFrame);
1345 if (rowFrame) {
1346 NS_ASSERTION(NS_STYLE_DISPLAY_TABLE_ROW == childFrame->GetStyleDisplay()->mDisplay, "wrong display type on rowframe");
1347 return rowFrame;
1349 childFrame = childFrame->GetNextSibling();
1351 return nsnull;
1354 NS_DECLARE_FRAME_PROPERTY(RowUnpaginatedHeightProperty, nsnull)
1356 void
1357 nsTableRowFrame::SetUnpaginatedHeight(nsPresContext* aPresContext,
1358 nscoord aValue)
1360 NS_ASSERTION(!GetPrevInFlow(), "program error");
1361 // Get the property
1362 aPresContext->PropertyTable()->
1363 Set(this, RowUnpaginatedHeightProperty(), NS_INT32_TO_PTR(aValue));
1366 nscoord
1367 nsTableRowFrame::GetUnpaginatedHeight(nsPresContext* aPresContext)
1369 FrameProperties props = GetFirstInFlow()->Properties();
1370 return NS_PTR_TO_INT32(props.Get(RowUnpaginatedHeightProperty()));
1373 void nsTableRowFrame::SetContinuousBCBorderWidth(PRUint8 aForSide,
1374 BCPixelSize aPixelValue)
1376 switch (aForSide) {
1377 case NS_SIDE_RIGHT:
1378 mRightContBorderWidth = aPixelValue;
1379 return;
1380 case NS_SIDE_TOP:
1381 mTopContBorderWidth = aPixelValue;
1382 return;
1383 case NS_SIDE_LEFT:
1384 mLeftContBorderWidth = aPixelValue;
1385 return;
1386 default:
1387 NS_ERROR("invalid NS_SIDE arg");
1392 * Sets the NS_ROW_HAS_CELL_WITH_STYLE_HEIGHT bit to indicate whether
1393 * this row has any cells that have non-auto-height. (Row-spanning
1394 * cells are ignored.)
1396 void nsTableRowFrame::InitHasCellWithStyleHeight(nsTableFrame* aTableFrame)
1398 nsTableIterator iter(*this);
1400 for (nsIFrame* kidFrame = iter.First(); kidFrame; kidFrame = iter.Next()) {
1401 nsTableCellFrame *cellFrame = do_QueryFrame(kidFrame);
1402 if (!cellFrame) {
1403 NS_NOTREACHED("Table row has a non-cell child.");
1404 continue;
1406 // Ignore row-spanning cells
1407 if (aTableFrame->GetEffectiveRowSpan(*cellFrame) == 1 &&
1408 cellFrame->GetStylePosition()->mHeight.GetUnit() != eStyleUnit_Auto) {
1409 AddStateBits(NS_ROW_HAS_CELL_WITH_STYLE_HEIGHT);
1410 return;
1413 RemoveStateBits(NS_ROW_HAS_CELL_WITH_STYLE_HEIGHT);
1416 /* ----- global methods ----- */
1418 nsIFrame*
1419 NS_NewTableRowFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
1421 return new (aPresShell) nsTableRowFrame(aContext);
1424 NS_IMPL_FRAMEARENA_HELPERS(nsTableRowFrame)
1426 #ifdef DEBUG
1427 NS_IMETHODIMP
1428 nsTableRowFrame::GetFrameName(nsAString& aResult) const
1430 return MakeFrameName(NS_LITERAL_STRING("TableRow"), aResult);
1432 #endif