Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / layout / tables / nsTableRowGroupFrame.cpp
blob2108a63731533595eb444f92d44985d81ffb061c
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):
23 * Mats Palmgren <mats.palmgren@bredband.net>
25 * Alternatively, the contents of this file may be used under the terms of
26 * either of the GNU General Public License Version 2 or later (the "GPL"),
27 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
38 #include "nsCOMPtr.h"
39 #include "nsTableRowGroupFrame.h"
40 #include "nsTableRowFrame.h"
41 #include "nsTableFrame.h"
42 #include "nsTableCellFrame.h"
43 #include "nsIRenderingContext.h"
44 #include "nsPresContext.h"
45 #include "nsStyleContext.h"
46 #include "nsStyleConsts.h"
47 #include "nsIContent.h"
48 #include "nsGkAtoms.h"
49 #include "nsIPresShell.h"
50 #include "nsCSSRendering.h"
51 #include "nsHTMLParts.h"
52 #include "nsCSSFrameConstructor.h"
53 #include "nsDisplayList.h"
55 #include "nsCellMap.h"//table cell navigation
57 nsTableRowGroupFrame::nsTableRowGroupFrame(nsStyleContext* aContext):
58 nsHTMLContainerFrame(aContext)
60 SetRepeatable(PR_FALSE);
63 nsTableRowGroupFrame::~nsTableRowGroupFrame()
67 NS_IMETHODIMP
68 nsTableRowGroupFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr)
70 NS_PRECONDITION(aInstancePtr, "null out param");
72 static NS_DEFINE_IID(kITableRowGroupIID, NS_ITABLEROWGROUPFRAME_IID);
73 if (aIID.Equals(kITableRowGroupIID)) {
74 *aInstancePtr = (void*)this;
75 return NS_OK;
78 return nsHTMLContainerFrame::QueryInterface(aIID, aInstancePtr);
81 /* virtual */ PRBool
82 nsTableRowGroupFrame::IsContainingBlock() const
84 return PR_TRUE;
87 PRInt32
88 nsTableRowGroupFrame::GetRowCount()
90 PRInt32 count = 0; // init return
92 // loop through children, adding one to aCount for every legit row
93 nsIFrame* childFrame = GetFirstFrame();
94 while (PR_TRUE) {
95 if (!childFrame)
96 break;
97 if (NS_STYLE_DISPLAY_TABLE_ROW == childFrame->GetStyleDisplay()->mDisplay)
98 count++;
99 GetNextFrame(childFrame, &childFrame);
101 return count;
104 PRInt32 nsTableRowGroupFrame::GetStartRowIndex()
106 PRInt32 result = -1;
107 nsIFrame* childFrame = GetFirstFrame();
108 while (PR_TRUE) {
109 if (!childFrame)
110 break;
111 if (NS_STYLE_DISPLAY_TABLE_ROW == childFrame->GetStyleDisplay()->mDisplay) {
112 result = ((nsTableRowFrame *)childFrame)->GetRowIndex();
113 break;
115 GetNextFrame(childFrame, &childFrame);
117 // if the row group doesn't have any children, get it the hard way
118 if (-1 == result) {
119 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
120 if (tableFrame) {
121 return tableFrame->GetStartRowIndex(*this);
125 return result;
128 void nsTableRowGroupFrame::AdjustRowIndices(PRInt32 aRowIndex,
129 PRInt32 anAdjustment)
131 nsIFrame* rowFrame = GetFirstChild(nsnull);
132 for ( ; rowFrame; rowFrame = rowFrame->GetNextSibling()) {
133 if (NS_STYLE_DISPLAY_TABLE_ROW==rowFrame->GetStyleDisplay()->mDisplay) {
134 PRInt32 index = ((nsTableRowFrame*)rowFrame)->GetRowIndex();
135 if (index >= aRowIndex)
136 ((nsTableRowFrame *)rowFrame)->SetRowIndex(index+anAdjustment);
140 nsresult
141 nsTableRowGroupFrame::InitRepeatedFrame(nsPresContext* aPresContext,
142 nsTableRowGroupFrame* aHeaderFooterFrame)
144 nsTableRowFrame* copyRowFrame = GetFirstRow();
145 nsTableRowFrame* originalRowFrame = aHeaderFooterFrame->GetFirstRow();
146 AddStateBits(NS_REPEATED_ROW_OR_ROWGROUP);
147 while (copyRowFrame && originalRowFrame) {
148 copyRowFrame->AddStateBits(NS_REPEATED_ROW_OR_ROWGROUP);
149 int rowIndex = originalRowFrame->GetRowIndex();
150 copyRowFrame->SetRowIndex(rowIndex);
152 // For each table cell frame set its column index
153 nsTableCellFrame* originalCellFrame = originalRowFrame->GetFirstCell();
154 nsTableCellFrame* copyCellFrame = copyRowFrame->GetFirstCell();
155 while (copyCellFrame && originalCellFrame) {
156 NS_ASSERTION(originalCellFrame->GetContent() == copyCellFrame->GetContent(),
157 "cell frames have different content");
158 PRInt32 colIndex;
159 originalCellFrame->GetColIndex(colIndex);
160 copyCellFrame->SetColIndex(colIndex);
162 // Move to the next cell frame
163 copyCellFrame = copyCellFrame->GetNextCell();
164 originalCellFrame = originalCellFrame->GetNextCell();
167 // Move to the next row frame
168 originalRowFrame = originalRowFrame->GetNextRow();
169 copyRowFrame = copyRowFrame->GetNextRow();
172 return NS_OK;
176 * We need a custom display item for table row backgrounds. This is only used
177 * when the table row is the root of a stacking context (e.g., has 'opacity').
178 * Table row backgrounds can extend beyond the row frame bounds, when
179 * the row contains row-spanning cells.
181 class nsDisplayTableRowGroupBackground : public nsDisplayTableItem {
182 public:
183 nsDisplayTableRowGroupBackground(nsTableRowGroupFrame* aFrame) : nsDisplayTableItem(aFrame) {
184 MOZ_COUNT_CTOR(nsDisplayTableRowGroupBackground);
186 #ifdef NS_BUILD_REFCNT_LOGGING
187 virtual ~nsDisplayTableRowGroupBackground() {
188 MOZ_COUNT_DTOR(nsDisplayTableRowGroupBackground);
190 #endif
192 virtual void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx,
193 const nsRect& aDirtyRect);
195 NS_DISPLAY_DECL_NAME("TableRowGroupBackground")
198 void
199 nsDisplayTableRowGroupBackground::Paint(nsDisplayListBuilder* aBuilder,
200 nsIRenderingContext* aCtx, const nsRect& aDirtyRect) {
201 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(mFrame);
203 nsPoint pt = aBuilder->ToReferenceFrame(mFrame);
204 TableBackgroundPainter painter(tableFrame,
205 TableBackgroundPainter::eOrigin_TableRowGroup,
206 mFrame->PresContext(), *aCtx,
207 aDirtyRect, pt);
208 painter.PaintRowGroup(static_cast<nsTableRowGroupFrame*>(mFrame));
211 // Handle the child-traversal part of DisplayGenericTablePart
212 static nsresult
213 DisplayRows(nsDisplayListBuilder* aBuilder, nsFrame* aFrame,
214 const nsRect& aDirtyRect, const nsDisplayListSet& aLists)
216 nscoord overflowAbove;
217 nsTableRowGroupFrame* f = static_cast<nsTableRowGroupFrame*>(aFrame);
218 // Don't try to use the row cursor if we have to descend into placeholders;
219 // we might have rows containing placeholders, where the row's overflow
220 // area doesn't intersect the dirty rect but we need to descend into the row
221 // to see out of flows
222 nsIFrame* kid = f->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO
223 ? nsnull : f->GetFirstRowContaining(aDirtyRect.y, &overflowAbove);
225 if (kid) {
226 // have a cursor, use it
227 while (kid) {
228 if (kid->GetRect().y - overflowAbove >= aDirtyRect.YMost())
229 break;
230 nsresult rv = f->BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists);
231 NS_ENSURE_SUCCESS(rv, rv);
232 kid = kid->GetNextSibling();
234 return NS_OK;
237 // No cursor. Traverse children the hard way and build a cursor while we're at it
238 nsTableRowGroupFrame::FrameCursorData* cursor = f->SetupRowCursor();
239 kid = f->GetFirstChild(nsnull);
240 while (kid) {
241 nsresult rv = f->BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists);
242 if (NS_FAILED(rv)) {
243 f->ClearRowCursor();
244 return rv;
247 if (cursor) {
248 if (!cursor->AppendFrame(kid)) {
249 f->ClearRowCursor();
250 return NS_ERROR_OUT_OF_MEMORY;
254 kid = kid->GetNextSibling();
256 if (cursor) {
257 cursor->FinishBuildingCursor();
260 return NS_OK;
263 NS_IMETHODIMP
264 nsTableRowGroupFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
265 const nsRect& aDirtyRect,
266 const nsDisplayListSet& aLists)
268 if (!IsVisibleInSelection(aBuilder))
269 return NS_OK;
271 PRBool isRoot = aBuilder->IsAtRootOfPseudoStackingContext() || IsScrolled();
272 nsDisplayTableItem* item = nsnull;
273 if (isRoot) {
274 // This background is created regardless of whether this frame is
275 // visible or not. Visibility decisions are delegated to the
276 // table background painter.
277 item = new (aBuilder) nsDisplayTableRowGroupBackground(this);
278 nsresult rv = aLists.BorderBackground()->AppendNewToTop(item);
279 NS_ENSURE_SUCCESS(rv, rv);
282 return nsTableFrame::DisplayGenericTablePart(aBuilder, this, aDirtyRect,
283 aLists, item, DisplayRows);
286 PRIntn
287 nsTableRowGroupFrame::GetSkipSides() const
289 PRIntn skip = 0;
290 if (nsnull != GetPrevInFlow()) {
291 skip |= 1 << NS_SIDE_TOP;
293 if (nsnull != GetNextInFlow()) {
294 skip |= 1 << NS_SIDE_BOTTOM;
296 return skip;
299 // Position and size aKidFrame and update our reflow state. The origin of
300 // aKidRect is relative to the upper-left origin of our frame
301 void
302 nsTableRowGroupFrame::PlaceChild(nsPresContext* aPresContext,
303 nsRowGroupReflowState& aReflowState,
304 nsIFrame* aKidFrame,
305 nsHTMLReflowMetrics& aDesiredSize,
306 const nsRect& aOriginalKidRect,
307 const nsRect& aOriginalKidOverflowRect)
309 PRBool isFirstReflow =
310 (aKidFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0;
312 // Place and size the child
313 FinishReflowChild(aKidFrame, aPresContext, nsnull, aDesiredSize, 0,
314 aReflowState.y, 0);
316 nsTableFrame::InvalidateFrame(aKidFrame, aOriginalKidRect,
317 aOriginalKidOverflowRect, isFirstReflow);
319 // Adjust the running y-offset
320 aReflowState.y += aDesiredSize.height;
322 // If our height is constrained then update the available height
323 if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height) {
324 aReflowState.availSize.height -= aDesiredSize.height;
328 void
329 nsTableRowGroupFrame::InitChildReflowState(nsPresContext& aPresContext,
330 PRBool aBorderCollapse,
331 nsHTMLReflowState& aReflowState)
333 nsMargin collapseBorder;
334 nsMargin padding(0,0,0,0);
335 nsMargin* pCollapseBorder = nsnull;
336 if (aBorderCollapse) {
337 if (aReflowState.frame) {
338 if (nsGkAtoms::tableRowFrame == aReflowState.frame->GetType()) {
339 nsTableRowFrame* rowFrame = (nsTableRowFrame*)aReflowState.frame;
340 pCollapseBorder = rowFrame->GetBCBorderWidth(collapseBorder);
344 aReflowState.Init(&aPresContext, -1, -1, pCollapseBorder, &padding);
347 static void
348 CacheRowHeightsForPrinting(nsPresContext* aPresContext,
349 nsTableRowFrame* aFirstRow)
351 for (nsTableRowFrame* row = aFirstRow; row; row = row->GetNextRow()) {
352 if (!row->GetPrevInFlow()) {
353 row->SetHasUnpaginatedHeight(PR_TRUE);
354 row->SetUnpaginatedHeight(aPresContext, row->GetSize().height);
359 NS_METHOD
360 nsTableRowGroupFrame::ReflowChildren(nsPresContext* aPresContext,
361 nsHTMLReflowMetrics& aDesiredSize,
362 nsRowGroupReflowState& aReflowState,
363 nsReflowStatus& aStatus,
364 PRBool* aPageBreakBeforeEnd)
366 if (aPageBreakBeforeEnd)
367 *aPageBreakBeforeEnd = PR_FALSE;
369 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
370 if (!tableFrame)
371 ABORT1(NS_ERROR_NULL_POINTER);
373 nsresult rv = NS_OK;
375 PRBool borderCollapse = tableFrame->IsBorderCollapse();
377 nscoord cellSpacingY = tableFrame->GetCellSpacingY();
379 // XXXldb Should we really be checking this rather than available height?
380 // (Think about multi-column layout!)
381 PRBool isPaginated = aPresContext->IsPaginated();
383 PRBool haveRow = PR_FALSE;
384 PRBool reflowAllKids = aReflowState.reflowState.ShouldReflowAllKids() ||
385 tableFrame->IsGeometryDirty();
386 PRBool needToCalcRowHeights = reflowAllKids;
388 nsIFrame *prevKidFrame = nsnull;
389 for (nsIFrame* kidFrame = GetFirstFrame(); kidFrame;
390 prevKidFrame = kidFrame, kidFrame = kidFrame->GetNextSibling()) {
391 if (kidFrame->GetType() != nsGkAtoms::tableRowFrame) {
392 // XXXldb nsCSSFrameConstructor needs to enforce this!
393 NS_NOTREACHED("yikes, a non-row child");
394 continue;
397 haveRow = PR_TRUE;
399 // Reflow the row frame
400 if (reflowAllKids ||
401 NS_SUBTREE_DIRTY(kidFrame) ||
402 (aReflowState.reflowState.mFlags.mSpecialHeightReflow &&
403 (isPaginated || (kidFrame->GetStateBits() &
404 NS_FRAME_CONTAINS_RELATIVE_HEIGHT)))) {
405 nsRect oldKidRect = kidFrame->GetRect();
406 nsRect oldKidOverflowRect = kidFrame->GetOverflowRect();
408 // XXXldb We used to only pass aDesiredSize.mFlags through for the
409 // incremental reflow codepath.
410 nsHTMLReflowMetrics desiredSize(aDesiredSize.mFlags);
411 desiredSize.width = desiredSize.height = 0;
413 // Reflow the child into the available space, giving it as much height as
414 // it wants. We'll deal with splitting later after we've computed the row
415 // heights, taking into account cells with row spans...
416 nsSize kidAvailSize(aReflowState.availSize.width, NS_UNCONSTRAINEDSIZE);
417 nsHTMLReflowState kidReflowState(aPresContext, aReflowState.reflowState,
418 kidFrame, kidAvailSize,
419 -1, -1, PR_FALSE);
420 InitChildReflowState(*aPresContext, borderCollapse, kidReflowState);
422 // This can indicate that columns were resized.
423 if (aReflowState.reflowState.mFlags.mHResize)
424 kidReflowState.mFlags.mHResize = PR_TRUE;
426 NS_ASSERTION(kidFrame == GetFirstFrame() || prevKidFrame,
427 "If we're not on the first frame, we should have a "
428 "previous sibling...");
429 // If prev row has nonzero YMost, then we can't be at the top of the page
430 if (prevKidFrame && prevKidFrame->GetRect().YMost() > 0) {
431 kidReflowState.mFlags.mIsTopOfPage = PR_FALSE;
434 rv = ReflowChild(kidFrame, aPresContext, desiredSize, kidReflowState,
435 0, aReflowState.y, NS_FRAME_INVALIDATE_ON_MOVE,
436 aStatus);
438 // Place the child
439 PlaceChild(aPresContext, aReflowState, kidFrame, desiredSize,
440 oldKidRect, oldKidOverflowRect);
441 aReflowState.y += cellSpacingY;
443 if (!reflowAllKids) {
444 if (IsSimpleRowFrame(aReflowState.tableFrame, kidFrame)) {
445 // Inform the row of its new height.
446 ((nsTableRowFrame*)kidFrame)->DidResize();
447 // the overflow area may have changed inflate the overflow area
448 const nsStylePosition *stylePos = GetStylePosition();
449 nsStyleUnit unit = stylePos->mHeight.GetUnit();
450 if (aReflowState.tableFrame->IsAutoHeight() &&
451 unit != eStyleUnit_Coord) {
452 // Because other cells in the row may need to be aligned
453 // differently, repaint the entire row
454 nsRect kidRect(0, aReflowState.y,
455 desiredSize.width, desiredSize.height);
456 Invalidate(kidRect);
458 // Invalidate the area we're offseting. Note that we only
459 // repaint within our existing frame bounds.
460 if (kidRect.YMost() < mRect.height) {
461 nsRect dirtyRect(0, kidRect.YMost(),
462 mRect.width, mRect.height - kidRect.YMost());
463 Invalidate(dirtyRect);
466 else if (oldKidRect.height != desiredSize.height)
467 needToCalcRowHeights = PR_TRUE;
468 } else {
469 needToCalcRowHeights = PR_TRUE;
473 if (isPaginated && aPageBreakBeforeEnd && !*aPageBreakBeforeEnd) {
474 nsTableRowFrame* nextRow = ((nsTableRowFrame*)kidFrame)->GetNextRow();
475 if (nextRow) {
476 *aPageBreakBeforeEnd = nsTableFrame::PageBreakAfter(*kidFrame, nextRow);
479 } else {
480 SlideChild(aReflowState, kidFrame);
482 // Adjust the running y-offset so we know where the next row should be placed
483 nscoord height = kidFrame->GetSize().height + cellSpacingY;
484 aReflowState.y += height;
486 if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height) {
487 aReflowState.availSize.height -= height;
490 ConsiderChildOverflow(aDesiredSize.mOverflowArea, kidFrame);
493 if (haveRow)
494 aReflowState.y -= cellSpacingY;
496 // Return our desired rect
497 aDesiredSize.width = aReflowState.reflowState.availableWidth;
498 aDesiredSize.height = aReflowState.y;
500 if (aReflowState.reflowState.mFlags.mSpecialHeightReflow) {
501 DidResizeRows(aDesiredSize);
502 if (isPaginated) {
503 CacheRowHeightsForPrinting(aPresContext, GetFirstRow());
506 else if (needToCalcRowHeights) {
507 CalculateRowHeights(aPresContext, aDesiredSize, aReflowState.reflowState);
508 if (!reflowAllKids) {
509 // Because we don't know what changed repaint everything.
510 // XXX We should change CalculateRowHeights() to return the bounding
511 // rect of what changed. Or whether anything moved or changed size...
512 nsRect dirtyRect(0, 0, mRect.width, mRect.height);
513 Invalidate(dirtyRect);
517 return rv;
520 nsTableRowFrame*
521 nsTableRowGroupFrame::GetFirstRow()
523 for (nsIFrame* childFrame = GetFirstFrame(); childFrame;
524 childFrame = childFrame->GetNextSibling()) {
525 if (nsGkAtoms::tableRowFrame == childFrame->GetType()) {
526 return (nsTableRowFrame*)childFrame;
529 return nsnull;
533 struct RowInfo {
534 RowInfo() { height = pctHeight = hasStyleHeight = hasPctHeight = isSpecial = 0; }
535 unsigned height; // content height or fixed height, excluding pct height
536 unsigned pctHeight:29; // pct height
537 unsigned hasStyleHeight:1;
538 unsigned hasPctHeight:1;
539 unsigned isSpecial:1; // there is no cell originating in the row with rowspan=1 and there are at
540 // least 2 cells spanning the row and there is no style height on the row
543 static void
544 UpdateHeights(RowInfo& aRowInfo,
545 nscoord aAdditionalHeight,
546 nscoord& aTotal,
547 nscoord& aUnconstrainedTotal)
549 aRowInfo.height += aAdditionalHeight;
550 aTotal += aAdditionalHeight;
551 if (!aRowInfo.hasStyleHeight) {
552 aUnconstrainedTotal += aAdditionalHeight;
556 void
557 nsTableRowGroupFrame::DidResizeRows(nsHTMLReflowMetrics& aDesiredSize)
559 // update the cells spanning rows with their new heights
560 // this is the place where all of the cells in the row get set to the height of the row
561 // Reset the overflow area
562 aDesiredSize.mOverflowArea = nsRect(0, 0, 0, 0);
563 for (nsTableRowFrame* rowFrame = GetFirstRow();
564 rowFrame; rowFrame = rowFrame->GetNextRow()) {
565 rowFrame->DidResize();
566 ConsiderChildOverflow(aDesiredSize.mOverflowArea, rowFrame);
570 // This calculates the height of all the rows and takes into account
571 // style height on the row group, style heights on rows and cells, style heights on rowspans.
572 // Actual row heights will be adjusted later if the table has a style height.
573 // Even if rows don't change height, this method must be called to set the heights of each
574 // cell in the row to the height of its row.
575 void
576 nsTableRowGroupFrame::CalculateRowHeights(nsPresContext* aPresContext,
577 nsHTMLReflowMetrics& aDesiredSize,
578 const nsHTMLReflowState& aReflowState)
580 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
581 if (!tableFrame) return;
583 PRBool isPaginated = aPresContext->IsPaginated();
585 // all table cells have the same top and bottom margins, namely cellSpacingY
586 nscoord cellSpacingY = tableFrame->GetCellSpacingY();
588 PRInt32 numEffCols = tableFrame->GetEffectiveColCount();
590 PRInt32 startRowIndex = GetStartRowIndex();
591 // find the row corresponding to the row index we just found
592 nsTableRowFrame* startRowFrame = GetFirstRow();
594 if (!startRowFrame) return;
596 // the current row group height is the y origin of the 1st row we are about to calculated a height for
597 nscoord startRowGroupHeight = startRowFrame->GetPosition().y;
599 PRInt32 numRows = GetRowCount() - (startRowFrame->GetRowIndex() - GetStartRowIndex());
600 // collect the current height of each row. nscoord* rowHeights = nsnull;
601 if (numRows <= 0)
602 return;
604 nsTArray<RowInfo> rowInfo;
605 if (!rowInfo.AppendElements(numRows)) {
606 return;
609 PRBool hasRowSpanningCell = PR_FALSE;
610 nscoord heightOfRows = 0;
611 nscoord heightOfUnStyledRows = 0;
612 // Get the height of each row without considering rowspans. This will be the max of
613 // the largest desired height of each cell, the largest style height of each cell,
614 // the style height of the row.
615 nscoord pctHeightBasis = GetHeightBasis(aReflowState);
616 PRInt32 rowIndex; // the index in rowInfo, not among the rows in the row group
617 nsTableRowFrame* rowFrame;
618 for (rowFrame = startRowFrame, rowIndex = 0; rowFrame; rowFrame = rowFrame->GetNextRow(), rowIndex++) {
619 nscoord nonPctHeight = rowFrame->GetContentHeight();
620 if (isPaginated) {
621 nonPctHeight = PR_MAX(nonPctHeight, rowFrame->GetSize().height);
623 if (!rowFrame->GetPrevInFlow()) {
624 if (rowFrame->HasPctHeight()) {
625 rowInfo[rowIndex].hasPctHeight = PR_TRUE;
626 rowInfo[rowIndex].pctHeight = rowFrame->GetHeight(pctHeightBasis);
628 rowInfo[rowIndex].hasStyleHeight = rowFrame->HasStyleHeight();
629 nonPctHeight = PR_MAX(nonPctHeight, rowFrame->GetFixedHeight());
631 UpdateHeights(rowInfo[rowIndex], nonPctHeight, heightOfRows, heightOfUnStyledRows);
633 if (!rowInfo[rowIndex].hasStyleHeight) {
634 if (isPaginated || tableFrame->HasMoreThanOneCell(rowIndex + startRowIndex)) {
635 rowInfo[rowIndex].isSpecial = PR_TRUE;
636 // iteratate the row's cell frames to see if any do not have rowspan > 1
637 nsTableCellFrame* cellFrame = rowFrame->GetFirstCell();
638 while (cellFrame) {
639 PRInt32 rowSpan = tableFrame->GetEffectiveRowSpan(rowIndex + startRowIndex, *cellFrame);
640 if (1 == rowSpan) {
641 rowInfo[rowIndex].isSpecial = PR_FALSE;
642 break;
644 cellFrame = cellFrame->GetNextCell();
648 // See if a cell spans into the row. If so we'll have to do the next step
649 if (!hasRowSpanningCell) {
650 if (tableFrame->RowIsSpannedInto(rowIndex + startRowIndex, numEffCols)) {
651 hasRowSpanningCell = PR_TRUE;
656 if (hasRowSpanningCell) {
657 // Get the height of cells with rowspans and allocate any extra space to the rows they span
658 // iteratate the child frames and process the row frames among them
659 for (rowFrame = startRowFrame, rowIndex = 0; rowFrame; rowFrame = rowFrame->GetNextRow(), rowIndex++) {
660 // See if the row has an originating cell with rowspan > 1. We cannot determine this for a row in a
661 // continued row group by calling RowHasSpanningCells, because the row's fif may not have any originating
662 // cells yet the row may have a continued cell which originates in it.
663 if (GetPrevInFlow() || tableFrame->RowHasSpanningCells(startRowIndex + rowIndex, numEffCols)) {
664 nsTableCellFrame* cellFrame = rowFrame->GetFirstCell();
665 // iteratate the row's cell frames
666 while (cellFrame) {
667 PRInt32 rowSpan = tableFrame->GetEffectiveRowSpan(rowIndex + startRowIndex, *cellFrame);
668 if ((rowIndex + rowSpan) > numRows) {
669 // there might be rows pushed already to the nextInFlow
670 rowSpan = numRows - rowIndex;
672 if (rowSpan > 1) { // a cell with rowspan > 1, determine the height of the rows it spans
673 nscoord heightOfRowsSpanned = 0;
674 nscoord heightOfUnStyledRowsSpanned = 0;
675 nscoord numSpecialRowsSpanned = 0;
676 nscoord cellSpacingTotal = 0;
677 PRInt32 spanX;
678 for (spanX = 0; spanX < rowSpan; spanX++) {
679 heightOfRowsSpanned += rowInfo[rowIndex + spanX].height;
680 if (!rowInfo[rowIndex + spanX].hasStyleHeight) {
681 heightOfUnStyledRowsSpanned += rowInfo[rowIndex + spanX].height;
683 if (0 != spanX) {
684 cellSpacingTotal += cellSpacingY;
686 if (rowInfo[rowIndex + spanX].isSpecial) {
687 numSpecialRowsSpanned++;
690 nscoord heightOfAreaSpanned = heightOfRowsSpanned + cellSpacingTotal;
691 // get the height of the cell
692 nsSize cellFrameSize = cellFrame->GetSize();
693 nsSize cellDesSize = cellFrame->GetDesiredSize();
694 rowFrame->CalculateCellActualSize(cellFrame, cellDesSize.width,
695 cellDesSize.height, cellDesSize.width);
696 cellFrameSize.height = cellDesSize.height;
697 if (cellFrame->HasVerticalAlignBaseline()) {
698 // to ensure that a spanning cell with a long descender doesn't
699 // collide with the next row, we need to take into account the shift
700 // that will be done to align the cell on the baseline of the row.
701 cellFrameSize.height += rowFrame->GetMaxCellAscent() -
702 cellFrame->GetCellBaseline();
705 if (heightOfAreaSpanned < cellFrameSize.height) {
706 // the cell's height is larger than the available space of the rows it
707 // spans so distribute the excess height to the rows affected
708 nscoord extra = cellFrameSize.height - heightOfAreaSpanned;
709 nscoord extraUsed = 0;
710 if (0 == numSpecialRowsSpanned) {
711 //NS_ASSERTION(heightOfRowsSpanned > 0, "invalid row span situation");
712 PRBool haveUnStyledRowsSpanned = (heightOfUnStyledRowsSpanned > 0);
713 nscoord divisor = (haveUnStyledRowsSpanned)
714 ? heightOfUnStyledRowsSpanned : heightOfRowsSpanned;
715 if (divisor > 0) {
716 for (spanX = rowSpan - 1; spanX >= 0; spanX--) {
717 if (!haveUnStyledRowsSpanned || !rowInfo[rowIndex + spanX].hasStyleHeight) {
718 // The amount of additional space each row gets is proportional to its height
719 float percent = ((float)rowInfo[rowIndex + spanX].height) / ((float)divisor);
721 // give rows their percentage, except for the first row which gets the remainder
722 nscoord extraForRow = (0 == spanX) ? extra - extraUsed
723 : NSToCoordRound(((float)(extra)) * percent);
724 extraForRow = PR_MIN(extraForRow, extra - extraUsed);
725 // update the row height
726 UpdateHeights(rowInfo[rowIndex + spanX], extraForRow, heightOfRows, heightOfUnStyledRows);
727 extraUsed += extraForRow;
728 if (extraUsed >= extra) {
729 NS_ASSERTION((extraUsed == extra), "invalid row height calculation");
730 break;
735 else {
736 // put everything in the last row
737 UpdateHeights(rowInfo[rowIndex + rowSpan - 1], extra, heightOfRows, heightOfUnStyledRows);
740 else {
741 // give the extra to the special rows
742 nscoord numSpecialRowsAllocated = 0;
743 for (spanX = rowSpan - 1; spanX >= 0; spanX--) {
744 if (rowInfo[rowIndex + spanX].isSpecial) {
745 // The amount of additional space each degenerate row gets is proportional to the number of them
746 float percent = 1.0f / ((float)numSpecialRowsSpanned);
748 // give rows their percentage, except for the first row which gets the remainder
749 nscoord extraForRow = (numSpecialRowsSpanned - 1 == numSpecialRowsAllocated)
750 ? extra - extraUsed
751 : NSToCoordRound(((float)(extra)) * percent);
752 extraForRow = PR_MIN(extraForRow, extra - extraUsed);
753 // update the row height
754 UpdateHeights(rowInfo[rowIndex + spanX], extraForRow, heightOfRows, heightOfUnStyledRows);
755 extraUsed += extraForRow;
756 if (extraUsed >= extra) {
757 NS_ASSERTION((extraUsed == extra), "invalid row height calculation");
758 break;
764 } // if (rowSpan > 1)
765 cellFrame = cellFrame->GetNextCell();
766 } // while (cellFrame)
767 } // if (tableFrame->RowHasSpanningCells(startRowIndex + rowIndex) {
768 } // while (rowFrame)
771 // pct height rows have already got their content heights. Give them their pct heights up to pctHeightBasis
772 nscoord extra = pctHeightBasis - heightOfRows;
773 for (rowFrame = startRowFrame, rowIndex = 0; rowFrame && (extra > 0); rowFrame = rowFrame->GetNextRow(), rowIndex++) {
774 RowInfo& rInfo = rowInfo[rowIndex];
775 if (rInfo.hasPctHeight) {
776 nscoord rowExtra = (rInfo.pctHeight > rInfo.height)
777 ? rInfo.pctHeight - rInfo.height: 0;
778 rowExtra = PR_MIN(rowExtra, extra);
779 UpdateHeights(rInfo, rowExtra, heightOfRows, heightOfUnStyledRows);
780 extra -= rowExtra;
784 PRBool styleHeightAllocation = PR_FALSE;
785 nscoord rowGroupHeight = startRowGroupHeight + heightOfRows + ((numRows - 1) * cellSpacingY);
786 // if we have a style height, allocate the extra height to unconstrained rows
787 if ((aReflowState.ComputedHeight() > rowGroupHeight) &&
788 (NS_UNCONSTRAINEDSIZE != aReflowState.ComputedHeight())) {
789 nscoord extraComputedHeight = aReflowState.ComputedHeight() - rowGroupHeight;
790 nscoord extraUsed = 0;
791 PRBool haveUnStyledRows = (heightOfUnStyledRows > 0);
792 nscoord divisor = (haveUnStyledRows)
793 ? heightOfUnStyledRows : heightOfRows;
794 if (divisor > 0) {
795 styleHeightAllocation = PR_TRUE;
796 for (rowIndex = 0; rowIndex < numRows; rowIndex++) {
797 if (!haveUnStyledRows || !rowInfo[rowIndex].hasStyleHeight) {
798 // The amount of additional space each row gets is based on the
799 // percentage of space it occupies
800 float percent = ((float)rowInfo[rowIndex].height) / ((float)divisor);
801 // give rows their percentage, except for the last row which gets the remainder
802 nscoord extraForRow = (numRows - 1 == rowIndex)
803 ? extraComputedHeight - extraUsed
804 : NSToCoordRound(((float)extraComputedHeight) * percent);
805 extraForRow = PR_MIN(extraForRow, extraComputedHeight - extraUsed);
806 // update the row height
807 UpdateHeights(rowInfo[rowIndex], extraForRow, heightOfRows, heightOfUnStyledRows);
808 extraUsed += extraForRow;
809 if (extraUsed >= extraComputedHeight) {
810 NS_ASSERTION((extraUsed == extraComputedHeight), "invalid row height calculation");
811 break;
816 rowGroupHeight = aReflowState.ComputedHeight();
819 nscoord yOrigin = startRowGroupHeight;
820 // update the rows with their (potentially) new heights
821 for (rowFrame = startRowFrame, rowIndex = 0; rowFrame; rowFrame = rowFrame->GetNextRow(), rowIndex++) {
822 nsRect rowBounds = rowFrame->GetRect();
823 nsRect rowOverflowRect = rowFrame->GetOverflowRect();
825 PRBool movedFrame = (rowBounds.y != yOrigin);
826 nscoord rowHeight = (rowInfo[rowIndex].height > 0) ? rowInfo[rowIndex].height : 0;
828 if (movedFrame || (rowHeight != rowBounds.height)) {
829 // Resize/move the row to its final size and position
830 if (movedFrame) {
831 rowFrame->InvalidateOverflowRect();
834 rowFrame->SetRect(nsRect(rowBounds.x, yOrigin, rowBounds.width,
835 rowHeight));
837 nsTableFrame::InvalidateFrame(rowFrame, rowBounds, rowOverflowRect,
838 PR_FALSE);
840 if (movedFrame) {
841 nsTableFrame::RePositionViews(rowFrame);
842 // XXXbz we don't need to update our overflow area?
844 yOrigin += rowHeight + cellSpacingY;
847 if (isPaginated && styleHeightAllocation) {
848 // since the row group has a style height, cache the row heights, so next in flows can honor them
849 CacheRowHeightsForPrinting(aPresContext, GetFirstRow());
852 DidResizeRows(aDesiredSize);
854 aDesiredSize.height = rowGroupHeight; // Adjust our desired size
857 nscoord
858 nsTableRowGroupFrame::CollapseRowGroupIfNecessary(nscoord aYTotalOffset,
859 nscoord aWidth)
861 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
863 const nsStyleVisibility* groupVis = GetStyleVisibility();
864 PRBool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE == groupVis->mVisible);
865 if (collapseGroup) {
866 tableFrame->SetNeedToCollapse(PR_TRUE);
869 nsRect overflowArea(0, 0, 0, 0);
871 nsTableRowFrame* rowFrame= GetFirstRow();
872 PRBool didCollapse = PR_FALSE;
873 nscoord yGroupOffset = 0;
874 while (rowFrame) {
875 yGroupOffset += rowFrame->CollapseRowIfNecessary(yGroupOffset,
876 aWidth, collapseGroup,
877 didCollapse);
878 ConsiderChildOverflow(overflowArea, rowFrame);
879 rowFrame = rowFrame->GetNextRow();
882 nsRect groupRect = GetRect();
883 nsRect oldGroupRect = groupRect;
884 nsRect oldGroupOverflowRect = GetOverflowRect();
886 groupRect.height -= yGroupOffset;
887 if (didCollapse) {
888 // add back the cellspacing between rowgroups
889 groupRect.height += tableFrame->GetCellSpacingY();
892 groupRect.y -= aYTotalOffset;
893 groupRect.width = aWidth;
895 if (aYTotalOffset != 0) {
896 InvalidateOverflowRect();
899 SetRect(groupRect);
900 overflowArea.UnionRect(nsRect(0, 0, groupRect.width, groupRect.height),
901 overflowArea);
902 FinishAndStoreOverflow(&overflowArea, nsSize(groupRect.width,
903 groupRect.height));
904 nsTableFrame::RePositionViews(this);
905 nsTableFrame::InvalidateFrame(this, oldGroupRect, oldGroupOverflowRect,
906 PR_FALSE);
908 return yGroupOffset;
911 // Move a child that was skipped during a reflow.
912 void
913 nsTableRowGroupFrame::SlideChild(nsRowGroupReflowState& aReflowState,
914 nsIFrame* aKidFrame)
916 // Move the frame if we need to
917 nsPoint oldPosition = aKidFrame->GetPosition();
918 nsPoint newPosition = oldPosition;
919 newPosition.y = aReflowState.y;
920 if (oldPosition.y != newPosition.y) {
921 aKidFrame->InvalidateOverflowRect();
922 aKidFrame->SetPosition(newPosition);
923 nsTableFrame::RePositionViews(aKidFrame);
924 aKidFrame->InvalidateOverflowRect();
928 // Create a continuing frame, add it to the child list, and then push it
929 // and the frames that follow
930 void
931 nsTableRowGroupFrame::CreateContinuingRowFrame(nsPresContext& aPresContext,
932 nsIFrame& aRowFrame,
933 nsIFrame** aContRowFrame)
935 // XXX what is the row index?
936 if (!aContRowFrame) {NS_ASSERTION(PR_FALSE, "bad call"); return;}
937 // create the continuing frame which will create continuing cell frames
938 nsresult rv = aPresContext.PresShell()->FrameConstructor()->
939 CreateContinuingFrame(&aPresContext, &aRowFrame, this, aContRowFrame);
940 if (NS_FAILED(rv)) {
941 *aContRowFrame = nsnull;
942 return;
945 // Add the continuing row frame to the child list
946 nsIFrame* nextRow;
947 GetNextFrame(&aRowFrame, &nextRow);
948 (*aContRowFrame)->SetNextSibling(nextRow);
949 aRowFrame.SetNextSibling(*aContRowFrame);
951 // Push the continuing row frame and the frames that follow
952 PushChildren(&aPresContext, *aContRowFrame, &aRowFrame);
955 // Reflow the cells with rowspan > 1 which originate between aFirstRow
956 // and end on or after aLastRow. aFirstTruncatedRow is the highest row on the
957 // page that contains a cell which cannot split on this page
958 void
959 nsTableRowGroupFrame::SplitSpanningCells(nsPresContext& aPresContext,
960 const nsHTMLReflowState& aReflowState,
961 nsTableFrame& aTable,
962 nsTableRowFrame& aFirstRow,
963 nsTableRowFrame& aLastRow,
964 PRBool aFirstRowIsTopOfPage,
965 nscoord aSpanningRowBottom,
966 nsTableRowFrame*& aContRow,
967 nsTableRowFrame*& aFirstTruncatedRow,
968 nscoord& aDesiredHeight)
970 NS_ASSERTION(aSpanningRowBottom >= 0, "Can't split negative heights");
971 aFirstTruncatedRow = nsnull;
972 aDesiredHeight = 0;
974 PRInt32 lastRowIndex = aLastRow.GetRowIndex();
975 PRBool wasLast = PR_FALSE;
976 // Iterate the rows between aFirstRow and aLastRow
977 for (nsTableRowFrame* row = &aFirstRow; !wasLast; row = row->GetNextRow()) {
978 wasLast = (row == &aLastRow);
979 PRInt32 rowIndex = row->GetRowIndex();
980 nsPoint rowPos = row->GetPosition();
981 // Iterate the cells looking for those that have rowspan > 1
982 for (nsTableCellFrame* cell = row->GetFirstCell(); cell; cell = cell->GetNextCell()) {
983 PRInt32 rowSpan = aTable.GetEffectiveRowSpan(rowIndex, *cell);
984 // Only reflow rowspan > 1 cells which span aLastRow. Those which don't span aLastRow
985 // were reflowed correctly during the unconstrained height reflow.
986 if ((rowSpan > 1) && (rowIndex + rowSpan > lastRowIndex)) {
987 nsReflowStatus status;
988 // Ask the row to reflow the cell to the height of all the rows it spans up through aLastRow
989 // aAvailHeight is the space between the row group start and the end of the page
990 nscoord cellAvailHeight = aSpanningRowBottom - rowPos.y;
991 NS_ASSERTION(cellAvailHeight >= 0, "No space for cell?");
992 PRBool isTopOfPage = (row == &aFirstRow) && aFirstRowIsTopOfPage;
993 nscoord cellHeight = row->ReflowCellFrame(&aPresContext, aReflowState,
994 isTopOfPage, cell,
995 cellAvailHeight, status);
996 aDesiredHeight = PR_MAX(aDesiredHeight, rowPos.y + cellHeight);
997 if (NS_FRAME_IS_COMPLETE(status)) {
998 if (cellHeight > cellAvailHeight) {
999 aFirstTruncatedRow = row;
1000 if ((row != &aFirstRow) || !aFirstRowIsTopOfPage) {
1001 // return now, since we will be getting another reflow after either (1) row is
1002 // moved to the next page or (2) the row group is moved to the next page
1003 return;
1007 else {
1008 if (!aContRow) {
1009 CreateContinuingRowFrame(aPresContext, aLastRow, (nsIFrame**)&aContRow);
1011 if (aContRow) {
1012 if (row != &aLastRow) {
1013 // aContRow needs a continuation for cell, since cell spanned into aLastRow
1014 // but does not originate there
1015 nsTableCellFrame* contCell = nsnull;
1016 aPresContext.PresShell()->FrameConstructor()->
1017 CreateContinuingFrame(&aPresContext, cell, &aLastRow,
1018 (nsIFrame**)&contCell);
1019 PRInt32 colIndex;
1020 cell->GetColIndex(colIndex);
1021 aContRow->InsertCellFrame(contCell, colIndex);
1030 // Remove the next-in-flow of the row, its cells and their cell blocks. This
1031 // is necessary in case the row doesn't need a continuation later on or needs
1032 // a continuation which doesn't have the same number of cells that now exist.
1033 void
1034 nsTableRowGroupFrame::UndoContinuedRow(nsPresContext* aPresContext,
1035 nsTableRowFrame* aRow)
1037 if (!aRow) return; // allow null aRow to avoid callers doing null checks
1039 // rowBefore was the prev-sibling of aRow's next-sibling before aRow was created
1040 nsTableRowFrame* rowBefore = (nsTableRowFrame*)aRow->GetPrevInFlow();
1042 nsIFrame* firstOverflow = GetOverflowFrames(aPresContext, PR_TRUE);
1043 if (!rowBefore || !firstOverflow || (firstOverflow != aRow)) {
1044 NS_ASSERTION(PR_FALSE, "invalid continued row");
1045 return;
1048 // Remove aRow from the sibling chain and hook its next-sibling up with rowBefore
1049 rowBefore->SetNextSibling(aRow->GetNextSibling());
1051 // Destroy the row, its cells, and their cell blocks. Cell blocks that have split
1052 // will not have reflowed yet to pick up content from any overflow lines.
1053 aRow->Destroy();
1056 static nsTableRowFrame*
1057 GetRowBefore(nsTableRowFrame& aStartRow,
1058 nsTableRowFrame& aRow)
1060 nsTableRowFrame* rowBefore = nsnull;
1061 for (nsTableRowFrame* sib = &aStartRow; sib && (sib != &aRow); sib = sib->GetNextRow()) {
1062 rowBefore = sib;
1064 return rowBefore;
1067 nsresult
1068 nsTableRowGroupFrame::SplitRowGroup(nsPresContext* aPresContext,
1069 nsHTMLReflowMetrics& aDesiredSize,
1070 const nsHTMLReflowState& aReflowState,
1071 nsTableFrame* aTableFrame,
1072 nsReflowStatus& aStatus)
1074 NS_PRECONDITION(aPresContext->IsPaginated(), "SplitRowGroup currently supports only paged media");
1076 nsresult rv = NS_OK;
1077 nsTableRowFrame* prevRowFrame = nsnull;
1078 aDesiredSize.height = 0;
1080 nscoord availWidth = aReflowState.availableWidth;
1081 nscoord availHeight = aReflowState.availableHeight;
1083 PRBool borderCollapse = ((nsTableFrame*)aTableFrame->GetFirstInFlow())->IsBorderCollapse();
1084 nscoord cellSpacingY = aTableFrame->GetCellSpacingY();
1086 // get the page height
1087 nscoord pageHeight = aPresContext->GetPageSize().height;
1088 NS_ASSERTION(pageHeight != NS_UNCONSTRAINEDSIZE,
1089 "The table shouldn't be split when there should be space");
1091 PRBool isTopOfPage = aReflowState.mFlags.mIsTopOfPage;
1092 nsTableRowFrame* firstRowThisPage = GetFirstRow();
1094 // Need to dirty the table's geometry, or else the row might skip
1095 // reflowing its cell as an optimization.
1096 aTableFrame->SetGeometryDirty();
1098 // Walk each of the row frames looking for the first row frame that doesn't fit
1099 // in the available space
1100 for (nsTableRowFrame* rowFrame = firstRowThisPage; rowFrame; rowFrame = rowFrame->GetNextRow()) {
1101 PRBool rowIsOnPage = PR_TRUE;
1102 nsRect rowRect = rowFrame->GetRect();
1103 // See if the row fits on this page
1104 if (rowRect.YMost() > availHeight) {
1105 nsTableRowFrame* contRow = nsnull;
1106 // Reflow the row in the availabe space and have it split if it is the 1st
1107 // row (on the page) or there is at least 5% of the current page available
1108 // XXX this 5% should be made a preference
1109 if (!prevRowFrame || (availHeight - aDesiredSize.height > pageHeight / 20)) {
1110 nsSize availSize(availWidth, PR_MAX(availHeight - rowRect.y, 0));
1111 // don't let the available height exceed what CalculateRowHeights set for it
1112 availSize.height = PR_MIN(availSize.height, rowRect.height);
1114 nsHTMLReflowState rowReflowState(aPresContext, aReflowState,
1115 rowFrame, availSize,
1116 -1, -1, PR_FALSE);
1118 InitChildReflowState(*aPresContext, borderCollapse, rowReflowState);
1119 rowReflowState.mFlags.mIsTopOfPage = isTopOfPage; // set top of page
1120 nsHTMLReflowMetrics rowMetrics;
1122 // Get the old size before we reflow.
1123 nsRect oldRowRect = rowFrame->GetRect();
1124 nsRect oldRowOverflowRect = rowFrame->GetOverflowRect();
1126 // Reflow the cell with the constrained height. A cell with rowspan >1 will get this
1127 // reflow later during SplitSpanningCells.
1128 rv = ReflowChild(rowFrame, aPresContext, rowMetrics, rowReflowState,
1129 0, 0, NS_FRAME_NO_MOVE_FRAME, aStatus);
1130 if (NS_FAILED(rv)) return rv;
1131 rowFrame->SetSize(nsSize(rowMetrics.width, rowMetrics.height));
1132 rowFrame->DidReflow(aPresContext, nsnull, NS_FRAME_REFLOW_FINISHED);
1133 rowFrame->DidResize();
1135 nsTableFrame::InvalidateFrame(rowFrame, oldRowRect, oldRowOverflowRect,
1136 PR_FALSE);
1138 if (NS_FRAME_IS_NOT_COMPLETE(aStatus)) {
1139 // The row frame is incomplete and all of the rowspan 1 cells' block frames split
1140 if ((rowMetrics.height <= rowReflowState.availableHeight) || isTopOfPage) {
1141 // The row stays on this page because either it split ok or we're on the top of page.
1142 // If top of page and the height exceeded the avail height, then there will be data loss
1143 NS_ASSERTION(rowMetrics.height <= rowReflowState.availableHeight,
1144 "data loss - incomplete row needed more height than available, on top of page");
1145 CreateContinuingRowFrame(*aPresContext, *rowFrame, (nsIFrame**)&contRow);
1146 if (contRow) {
1147 aDesiredSize.height += rowMetrics.height;
1148 if (prevRowFrame)
1149 aDesiredSize.height += cellSpacingY;
1151 else return NS_ERROR_NULL_POINTER;
1153 else {
1154 // Put the row on the next page to give it more height
1155 rowIsOnPage = PR_FALSE;
1158 else {
1159 // The row frame is complete because either (1) its minimum height is greater than the
1160 // available height we gave it, or (2) it may have been given a larger height through
1161 // style than its content, or (3) it contains a rowspan >1 cell which hasn't been
1162 // reflowed with a constrained height yet (we will find out when SplitSpanningCells is
1163 // called below)
1164 if (rowMetrics.height > availSize.height) {
1165 // cases (1) and (2)
1166 if (isTopOfPage) {
1167 // We're on top of the page, so keep the row on this page. There will be data loss.
1168 // Push the row frame that follows
1169 nsTableRowFrame* nextRowFrame = rowFrame->GetNextRow();
1170 if (nextRowFrame) {
1171 aStatus = NS_FRAME_NOT_COMPLETE;
1173 aDesiredSize.height += rowMetrics.height;
1174 if (prevRowFrame)
1175 aDesiredSize.height += cellSpacingY;
1176 NS_WARNING("data loss - complete row needed more height than available, on top of page");
1178 else {
1179 // We're not on top of the page, so put the row on the next page to give it more height
1180 rowIsOnPage = PR_FALSE;
1184 } //if (!prevRowFrame || (availHeight - aDesiredSize.height > pageHeight / 20))
1185 else {
1186 // put the row on the next page to give it more height
1187 rowIsOnPage = PR_FALSE;
1190 nsTableRowFrame* lastRowThisPage = rowFrame;
1191 nscoord spanningRowBottom = availHeight;
1192 if (!rowIsOnPage) {
1193 NS_ASSERTION(!contRow, "We should not have created a continuation if none of this row fits");
1194 if (prevRowFrame) {
1195 spanningRowBottom = prevRowFrame->GetRect().YMost();
1196 lastRowThisPage = prevRowFrame;
1197 isTopOfPage = (lastRowThisPage == firstRowThisPage) && aReflowState.mFlags.mIsTopOfPage;
1198 aStatus = NS_FRAME_NOT_COMPLETE;
1200 else {
1201 // We can't push children, so let our parent reflow us again with more space
1202 aDesiredSize.height = rowRect.YMost();
1203 aStatus = NS_FRAME_COMPLETE;
1204 break;
1207 // reflow the cells with rowspan >1 that occur on the page
1209 nsTableRowFrame* firstTruncatedRow;
1210 nscoord yMost;
1211 SplitSpanningCells(*aPresContext, aReflowState, *aTableFrame, *firstRowThisPage,
1212 *lastRowThisPage, aReflowState.mFlags.mIsTopOfPage, spanningRowBottom, contRow,
1213 firstTruncatedRow, yMost);
1214 if (firstTruncatedRow) {
1215 // A rowspan >1 cell did not fit (and could not split) in the space we gave it
1216 if (firstTruncatedRow == firstRowThisPage) {
1217 if (aReflowState.mFlags.mIsTopOfPage) {
1218 NS_WARNING("data loss in a row spanned cell");
1220 else {
1221 // We can't push children, so let our parent reflow us again with more space
1222 aDesiredSize.height = rowRect.YMost();
1223 aStatus = NS_FRAME_COMPLETE;
1224 UndoContinuedRow(aPresContext, contRow);
1225 contRow = nsnull;
1228 else { // (firstTruncatedRow != firstRowThisPage)
1229 // Try to put firstTruncateRow on the next page
1230 nsTableRowFrame* rowBefore = ::GetRowBefore(*firstRowThisPage, *firstTruncatedRow);
1231 nscoord oldSpanningRowBottom = spanningRowBottom;
1232 spanningRowBottom = rowBefore->GetRect().YMost();
1234 UndoContinuedRow(aPresContext, contRow);
1235 contRow = nsnull;
1236 nsTableRowFrame* oldLastRowThisPage = lastRowThisPage;
1237 lastRowThisPage = firstTruncatedRow;
1238 aStatus = NS_FRAME_NOT_COMPLETE;
1240 // Call SplitSpanningCells again with rowBefore as the last row on the page
1241 SplitSpanningCells(*aPresContext, aReflowState, *aTableFrame,
1242 *firstRowThisPage, *rowBefore, aReflowState.mFlags.mIsTopOfPage,
1243 spanningRowBottom, contRow, firstTruncatedRow, aDesiredSize.height);
1244 if (firstTruncatedRow) {
1245 if (aReflowState.mFlags.mIsTopOfPage) {
1246 // We were better off with the 1st call to SplitSpanningCells, do it again
1247 UndoContinuedRow(aPresContext, contRow);
1248 contRow = nsnull;
1249 lastRowThisPage = oldLastRowThisPage;
1250 spanningRowBottom = oldSpanningRowBottom;
1251 SplitSpanningCells(*aPresContext, aReflowState, *aTableFrame, *firstRowThisPage,
1252 *lastRowThisPage, aReflowState.mFlags.mIsTopOfPage, spanningRowBottom, contRow,
1253 firstTruncatedRow, aDesiredSize.height);
1254 NS_WARNING("data loss in a row spanned cell");
1256 else {
1257 // Let our parent reflow us again with more space
1258 aDesiredSize.height = rowRect.YMost();
1259 aStatus = NS_FRAME_COMPLETE;
1260 UndoContinuedRow(aPresContext, contRow);
1261 contRow = nsnull;
1264 } // if (firstTruncatedRow == firstRowThisPage)
1265 } // if (firstTruncatedRow)
1266 else {
1267 aDesiredSize.height = PR_MAX(aDesiredSize.height, yMost);
1268 if (contRow) {
1269 aStatus = NS_FRAME_NOT_COMPLETE;
1272 if (NS_FRAME_IS_NOT_COMPLETE(aStatus) && !contRow) {
1273 nsTableRowFrame* nextRow = lastRowThisPage->GetNextRow();
1274 if (nextRow) {
1275 PushChildren(aPresContext, nextRow, lastRowThisPage);
1278 break;
1279 } // if (rowRect.YMost() > availHeight)
1280 else {
1281 aDesiredSize.height = rowRect.YMost();
1282 prevRowFrame = rowFrame;
1283 // see if there is a page break after the row
1284 nsTableRowFrame* nextRow = rowFrame->GetNextRow();
1285 if (nextRow && nsTableFrame::PageBreakAfter(*rowFrame, nextRow)) {
1286 PushChildren(aPresContext, nextRow, rowFrame);
1287 aStatus = NS_FRAME_NOT_COMPLETE;
1288 break;
1291 // after the 1st row that has a height, we can't be on top
1292 // of the page anymore.
1293 isTopOfPage = isTopOfPage && rowRect.YMost() == 0;
1295 return NS_OK;
1298 /** Layout the entire row group.
1299 * This method stacks rows vertically according to HTML 4.0 rules.
1300 * Rows are responsible for layout of their children.
1302 NS_METHOD
1303 nsTableRowGroupFrame::Reflow(nsPresContext* aPresContext,
1304 nsHTMLReflowMetrics& aDesiredSize,
1305 const nsHTMLReflowState& aReflowState,
1306 nsReflowStatus& aStatus)
1308 DO_GLOBAL_REFLOW_COUNT("nsTableRowGroupFrame");
1309 DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
1311 nsresult rv = NS_OK;
1312 aStatus = NS_FRAME_COMPLETE;
1314 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
1315 if (!tableFrame) return NS_ERROR_NULL_POINTER;
1317 // Row geometry may be going to change so we need to invalidate any row cursor.
1318 ClearRowCursor();
1320 // see if a special height reflow needs to occur due to having a pct height
1321 nsTableFrame::CheckRequestSpecialHeightReflow(aReflowState);
1323 nsRowGroupReflowState state(aReflowState, tableFrame);
1324 const nsStyleVisibility* groupVis = GetStyleVisibility();
1325 PRBool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE == groupVis->mVisible);
1326 if (collapseGroup) {
1327 tableFrame->SetNeedToCollapse(PR_TRUE);
1330 // Check for an overflow list
1331 MoveOverflowToChildList(aPresContext);
1333 // Reflow the existing frames.
1334 PRBool splitDueToPageBreak = PR_FALSE;
1335 rv = ReflowChildren(aPresContext, aDesiredSize, state, aStatus,
1336 &splitDueToPageBreak);
1338 // See if all the frames fit. Do not try to split anything if we're
1339 // not paginated ... we can't split across columns yet.
1340 if (aReflowState.mFlags.mTableIsSplittable &&
1341 (NS_FRAME_NOT_COMPLETE == aStatus || splitDueToPageBreak ||
1342 (NS_UNCONSTRAINEDSIZE != aReflowState.availableHeight &&
1343 aDesiredSize.height > aReflowState.availableHeight))) {
1344 // Nope, find a place to split the row group
1345 PRBool specialReflow = (PRBool)aReflowState.mFlags.mSpecialHeightReflow;
1346 ((nsHTMLReflowState::ReflowStateFlags&)aReflowState.mFlags).mSpecialHeightReflow = PR_FALSE;
1348 SplitRowGroup(aPresContext, aDesiredSize, aReflowState, tableFrame, aStatus);
1350 ((nsHTMLReflowState::ReflowStateFlags&)aReflowState.mFlags).mSpecialHeightReflow = specialReflow;
1353 // If we have a next-in-flow, then we're not complete
1354 // XXXldb This used to be done only for the incremental reflow codepath.
1355 if (GetNextInFlow()) {
1356 aStatus = NS_FRAME_NOT_COMPLETE;
1359 SetHasStyleHeight((NS_UNCONSTRAINEDSIZE != aReflowState.ComputedHeight()) &&
1360 (aReflowState.ComputedHeight() > 0));
1362 // just set our width to what was available. The table will calculate the width and not use our value.
1363 aDesiredSize.width = aReflowState.availableWidth;
1365 aDesiredSize.mOverflowArea.UnionRect(aDesiredSize.mOverflowArea, nsRect(0, 0, aDesiredSize.width,
1366 aDesiredSize.height));
1368 // If our parent is in initial reflow, it'll handle invalidating our
1369 // entire overflow rect.
1370 if (!(GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
1371 CheckInvalidateSizeChange(aDesiredSize);
1374 FinishAndStoreOverflow(&aDesiredSize);
1375 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
1376 return rv;
1379 /* virtual */ void
1380 nsTableRowGroupFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
1382 if (!aOldStyleContext) //avoid this on init
1383 return;
1385 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
1387 if (tableFrame->IsBorderCollapse() &&
1388 tableFrame->BCRecalcNeeded(aOldStyleContext, GetStyleContext())) {
1389 nsRect damageArea(0, GetStartRowIndex(), tableFrame->GetColCount(),
1390 GetRowCount());
1391 tableFrame->SetBCDamageArea(damageArea);
1393 return;
1396 NS_IMETHODIMP
1397 nsTableRowGroupFrame::AppendFrames(nsIAtom* aListName,
1398 nsIFrame* aFrameList)
1400 NS_ASSERTION(!aListName, "unexpected child list");
1402 ClearRowCursor();
1404 // collect the new row frames in an array
1405 nsAutoVoidArray rows;
1406 for (nsIFrame* rowFrame = aFrameList; rowFrame;
1407 rowFrame = rowFrame->GetNextSibling()) {
1408 if (nsGkAtoms::tableRowFrame == rowFrame->GetType()) {
1409 NS_ASSERTION(NS_STYLE_DISPLAY_TABLE_ROW ==
1410 rowFrame->GetStyleDisplay()->mDisplay,
1411 "wrong display type on rowframe");
1412 rows.AppendElement(rowFrame);
1416 PRInt32 rowIndex = GetRowCount();
1417 // Append the frames to the sibling chain
1418 mFrames.AppendFrames(nsnull, aFrameList);
1420 if (rows.Count() > 0) {
1421 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
1422 if (tableFrame) {
1423 tableFrame->AppendRows(*this, rowIndex, rows);
1424 PresContext()->PresShell()->
1425 FrameNeedsReflow(this, nsIPresShell::eTreeChange,
1426 NS_FRAME_HAS_DIRTY_CHILDREN);
1427 tableFrame->SetGeometryDirty();
1431 return NS_OK;
1434 NS_IMETHODIMP
1435 nsTableRowGroupFrame::InsertFrames(nsIAtom* aListName,
1436 nsIFrame* aPrevFrame,
1437 nsIFrame* aFrameList)
1439 NS_ASSERTION(!aListName, "unexpected child list");
1440 NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
1441 "inserting after sibling frame with different parent");
1443 ClearRowCursor();
1445 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
1446 if (!tableFrame)
1447 return NS_ERROR_NULL_POINTER;
1449 // collect the new row frames in an array
1450 nsVoidArray rows;
1451 PRBool gotFirstRow = PR_FALSE;
1452 for (nsIFrame* rowFrame = aFrameList; rowFrame;
1453 rowFrame = rowFrame->GetNextSibling()) {
1454 if (nsGkAtoms::tableRowFrame == rowFrame->GetType()) {
1455 NS_ASSERTION(NS_STYLE_DISPLAY_TABLE_ROW ==
1456 rowFrame->GetStyleDisplay()->mDisplay,
1457 "wrong display type on rowframe");
1458 rows.AppendElement(rowFrame);
1459 if (!gotFirstRow) {
1460 ((nsTableRowFrame*)rowFrame)->SetFirstInserted(PR_TRUE);
1461 gotFirstRow = PR_TRUE;
1462 tableFrame->SetRowInserted(PR_TRUE);
1467 PRInt32 startRowIndex = GetStartRowIndex();
1468 // Insert the frames in the sibling chain
1469 mFrames.InsertFrames(nsnull, aPrevFrame, aFrameList);
1471 PRInt32 numRows = rows.Count();
1472 if (numRows > 0) {
1473 nsTableRowFrame* prevRow = (nsTableRowFrame *)nsTableFrame::GetFrameAtOrBefore(this, aPrevFrame, nsGkAtoms::tableRowFrame);
1474 PRInt32 rowIndex = (prevRow) ? prevRow->GetRowIndex() + 1 : startRowIndex;
1475 tableFrame->InsertRows(*this, rows, rowIndex, PR_TRUE);
1477 PresContext()->PresShell()->
1478 FrameNeedsReflow(this, nsIPresShell::eTreeChange,
1479 NS_FRAME_HAS_DIRTY_CHILDREN);
1480 tableFrame->SetGeometryDirty();
1482 return NS_OK;
1485 NS_IMETHODIMP
1486 nsTableRowGroupFrame::RemoveFrame(nsIAtom* aListName,
1487 nsIFrame* aOldFrame)
1489 NS_ASSERTION(!aListName, "unexpected child list");
1491 ClearRowCursor();
1493 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
1494 if (tableFrame) {
1495 if (nsGkAtoms::tableRowFrame == aOldFrame->GetType()) {
1496 // remove the rows from the table (and flag a rebalance)
1497 tableFrame->RemoveRows((nsTableRowFrame &)*aOldFrame, 1, PR_TRUE);
1499 PresContext()->PresShell()->
1500 FrameNeedsReflow(this, nsIPresShell::eTreeChange,
1501 NS_FRAME_HAS_DIRTY_CHILDREN);
1502 tableFrame->SetGeometryDirty();
1505 mFrames.DestroyFrame(aOldFrame);
1507 return NS_OK;
1510 /* virtual */ nsMargin
1511 nsTableRowGroupFrame::GetUsedMargin() const
1513 return nsMargin(0,0,0,0);
1516 /* virtual */ nsMargin
1517 nsTableRowGroupFrame::GetUsedBorder() const
1519 return nsMargin(0,0,0,0);
1522 /* virtual */ nsMargin
1523 nsTableRowGroupFrame::GetUsedPadding() const
1525 return nsMargin(0,0,0,0);
1528 nscoord
1529 nsTableRowGroupFrame::GetHeightBasis(const nsHTMLReflowState& aReflowState)
1531 nscoord result = 0;
1532 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
1533 if (tableFrame) {
1534 if ((aReflowState.ComputedHeight() > 0) && (aReflowState.ComputedHeight() < NS_UNCONSTRAINEDSIZE)) {
1535 nscoord cellSpacing = PR_MAX(0, GetRowCount() - 1) * tableFrame->GetCellSpacingY();
1536 result = aReflowState.ComputedHeight() - cellSpacing;
1538 else {
1539 const nsHTMLReflowState* parentRS = aReflowState.parentReflowState;
1540 if (parentRS && (tableFrame != parentRS->frame)) {
1541 parentRS = parentRS->parentReflowState;
1543 if (parentRS && (tableFrame == parentRS->frame) &&
1544 (parentRS->ComputedHeight() > 0) && (parentRS->ComputedHeight() < NS_UNCONSTRAINEDSIZE)) {
1545 nscoord cellSpacing = PR_MAX(0, tableFrame->GetRowCount() + 1) * tableFrame->GetCellSpacingY();
1546 result = parentRS->ComputedHeight() - cellSpacing;
1551 return result;
1554 PRBool
1555 nsTableRowGroupFrame::IsSimpleRowFrame(nsTableFrame* aTableFrame,
1556 nsIFrame* aFrame)
1558 // Make sure it's a row frame and not a row group frame
1559 if (aFrame->GetType() == nsGkAtoms::tableRowFrame) {
1560 PRInt32 rowIndex = ((nsTableRowFrame*)aFrame)->GetRowIndex();
1562 // It's a simple row frame if there are no cells that span into or
1563 // across the row
1564 PRInt32 numEffCols = aTableFrame->GetEffectiveColCount();
1565 if (!aTableFrame->RowIsSpannedInto(rowIndex, numEffCols) &&
1566 !aTableFrame->RowHasSpanningCells(rowIndex, numEffCols)) {
1567 return PR_TRUE;
1571 return PR_FALSE;
1574 nsIAtom*
1575 nsTableRowGroupFrame::GetType() const
1577 return nsGkAtoms::tableRowGroupFrame;
1581 /* ----- global methods ----- */
1583 nsIFrame*
1584 NS_NewTableRowGroupFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
1586 return new (aPresShell) nsTableRowGroupFrame(aContext);
1589 #ifdef DEBUG
1590 NS_IMETHODIMP
1591 nsTableRowGroupFrame::GetFrameName(nsAString& aResult) const
1593 return MakeFrameName(NS_LITERAL_STRING("TableRowGroup"), aResult);
1595 #endif
1597 nsMargin*
1598 nsTableRowGroupFrame::GetBCBorderWidth(nsMargin& aBorder)
1600 aBorder.left = aBorder.right = aBorder.top = aBorder.bottom = 0;
1602 nsTableRowFrame* firstRowFrame = nsnull;
1603 nsTableRowFrame* lastRowFrame = nsnull;
1604 for (nsTableRowFrame* rowFrame = GetFirstRow(); rowFrame; rowFrame = rowFrame->GetNextRow()) {
1605 if (!firstRowFrame) {
1606 firstRowFrame = rowFrame;
1608 lastRowFrame = rowFrame;
1610 if (firstRowFrame) {
1611 aBorder.top = nsPresContext::CSSPixelsToAppUnits(firstRowFrame->GetTopBCBorderWidth());
1612 aBorder.bottom = nsPresContext::CSSPixelsToAppUnits(lastRowFrame->GetBottomBCBorderWidth());
1615 return &aBorder;
1618 void nsTableRowGroupFrame::SetContinuousBCBorderWidth(PRUint8 aForSide,
1619 BCPixelSize aPixelValue)
1621 switch (aForSide) {
1622 case NS_SIDE_RIGHT:
1623 mRightContBorderWidth = aPixelValue;
1624 return;
1625 case NS_SIDE_BOTTOM:
1626 mBottomContBorderWidth = aPixelValue;
1627 return;
1628 case NS_SIDE_LEFT:
1629 mLeftContBorderWidth = aPixelValue;
1630 return;
1631 default:
1632 NS_ERROR("invalid NS_SIDE argument");
1636 //nsILineIterator methods
1637 PRInt32
1638 nsTableRowGroupFrame::GetNumLines()
1640 return GetRowCount();
1643 PRBool
1644 nsTableRowGroupFrame::GetDirection()
1646 nsTableFrame* table = nsTableFrame::GetTableFrame(this);
1647 return (NS_STYLE_DIRECTION_RTL ==
1648 table->GetStyleVisibility()->mDirection);
1651 NS_IMETHODIMP
1652 nsTableRowGroupFrame::GetLine(PRInt32 aLineNumber,
1653 nsIFrame** aFirstFrameOnLine,
1654 PRInt32* aNumFramesOnLine,
1655 nsRect& aLineBounds,
1656 PRUint32* aLineFlags)
1658 NS_ENSURE_ARG_POINTER(aFirstFrameOnLine);
1659 NS_ENSURE_ARG_POINTER(aNumFramesOnLine);
1660 NS_ENSURE_ARG_POINTER(aLineFlags);
1662 nsTableFrame* table = nsTableFrame::GetTableFrame(this);
1663 nsTableCellMap* cellMap = table->GetCellMap();
1665 *aLineFlags = 0;
1666 *aFirstFrameOnLine = nsnull;
1667 *aNumFramesOnLine = 0;
1668 aLineBounds.SetRect(0, 0, 0, 0);
1670 if ((aLineNumber < 0) || (aLineNumber >= GetRowCount())) {
1671 return NS_OK;
1673 aLineNumber += GetStartRowIndex();
1675 *aNumFramesOnLine = cellMap->GetNumCellsOriginatingInRow(aLineNumber);
1676 if (*aNumFramesOnLine == 0) {
1677 return NS_OK;
1679 PRInt32 colCount = table->GetColCount();
1680 for (PRInt32 i = 0; i < colCount; i++) {
1681 CellData* data = cellMap->GetDataAt(aLineNumber, i);
1682 if (data && data->IsOrig()) {
1683 *aFirstFrameOnLine = (nsIFrame*)data->GetCellFrame();
1684 nsIFrame* parent = (*aFirstFrameOnLine)->GetParent();
1685 aLineBounds = parent->GetRect();
1686 return NS_OK;
1689 NS_ERROR("cellmap is lying");
1690 return NS_ERROR_FAILURE;
1693 PRInt32
1694 nsTableRowGroupFrame::FindLineContaining(nsIFrame* aFrame)
1696 NS_ENSURE_ARG_POINTER(aFrame);
1698 NS_ASSERTION((aFrame->GetType() == nsGkAtoms::tableRowFrame),
1699 "RowGroup contains a frame that is not a row");
1701 nsTableRowFrame* rowFrame = (nsTableRowFrame*)aFrame;
1702 return rowFrame->GetRowIndex() - GetStartRowIndex();
1705 PRInt32
1706 nsTableRowGroupFrame::FindLineAt(nscoord aY)
1708 NS_NOTREACHED("Not implemented");
1709 return NS_ERROR_NOT_IMPLEMENTED;
1712 #ifdef IBMBIDI
1713 NS_IMETHODIMP
1714 nsTableRowGroupFrame::CheckLineOrder(PRInt32 aLine,
1715 PRBool *aIsReordered,
1716 nsIFrame **aFirstVisual,
1717 nsIFrame **aLastVisual)
1719 *aIsReordered = PR_FALSE;
1720 *aFirstVisual = nsnull;
1721 *aLastVisual = nsnull;
1722 return NS_OK;
1724 #endif // IBMBIDI
1726 NS_IMETHODIMP
1727 nsTableRowGroupFrame::FindFrameAt(PRInt32 aLineNumber,
1728 nscoord aX,
1729 nsIFrame** aFrameFound,
1730 PRBool* aXIsBeforeFirstFrame,
1731 PRBool* aXIsAfterLastFrame)
1733 nsTableFrame* table = nsTableFrame::GetTableFrame(this);
1734 nsTableCellMap* cellMap = table->GetCellMap();
1736 *aFrameFound = nsnull;
1737 *aXIsBeforeFirstFrame = PR_TRUE;
1738 *aXIsAfterLastFrame = PR_FALSE;
1740 aLineNumber += GetStartRowIndex();
1741 PRInt32 numCells = cellMap->GetNumCellsOriginatingInRow(aLineNumber);
1742 if (numCells == 0) {
1743 return NS_OK;
1746 nsIFrame* frame = nsnull;
1747 PRInt32 colCount = table->GetColCount();
1748 for (PRInt32 i = 0; i < colCount; i++) {
1749 CellData* data = cellMap->GetDataAt(aLineNumber, i);
1750 if (data && data->IsOrig()) {
1751 frame = (nsIFrame*)data->GetCellFrame();
1752 break;
1755 NS_ASSERTION(frame, "cellmap is lying");
1756 PRBool isRTL = (NS_STYLE_DIRECTION_RTL ==
1757 table->GetStyleVisibility()->mDirection);
1759 nsIFrame* closestFromLeft = nsnull;
1760 nsIFrame* closestFromRight = nsnull;
1761 PRInt32 n = numCells;
1762 nsIFrame* firstFrame = frame;
1763 while (n--) {
1764 nsRect rect = frame->GetRect();
1765 if (rect.width > 0) {
1766 // If aX is inside this frame - this is it
1767 if (rect.x <= aX && rect.XMost() > aX) {
1768 closestFromLeft = closestFromRight = frame;
1769 break;
1771 if (rect.x < aX) {
1772 if (!closestFromLeft ||
1773 rect.XMost() > closestFromLeft->GetRect().XMost())
1774 closestFromLeft = frame;
1776 else {
1777 if (!closestFromRight ||
1778 rect.x < closestFromRight->GetRect().x)
1779 closestFromRight = frame;
1782 frame = frame->GetNextSibling();
1784 if (!closestFromLeft && !closestFromRight) {
1785 // All frames were zero-width. Just take the first one.
1786 closestFromLeft = closestFromRight = firstFrame;
1788 *aXIsBeforeFirstFrame = isRTL ? !closestFromRight : !closestFromLeft;
1789 *aXIsAfterLastFrame = isRTL ? !closestFromLeft : !closestFromRight;
1790 if (closestFromLeft == closestFromRight) {
1791 *aFrameFound = closestFromLeft;
1793 else if (!closestFromLeft) {
1794 *aFrameFound = closestFromRight;
1796 else if (!closestFromRight) {
1797 *aFrameFound = closestFromLeft;
1799 else { // we're between two frames
1800 nscoord delta = closestFromRight->GetRect().x -
1801 closestFromLeft->GetRect().XMost();
1802 if (aX < closestFromLeft->GetRect().XMost() + delta/2)
1803 *aFrameFound = closestFromLeft;
1804 else
1805 *aFrameFound = closestFromRight;
1807 return NS_OK;
1810 NS_IMETHODIMP
1811 nsTableRowGroupFrame::GetNextSiblingOnLine(nsIFrame*& aFrame,
1812 PRInt32 aLineNumber)
1814 NS_ENSURE_ARG_POINTER(aFrame);
1815 aFrame = aFrame->GetNextSibling();
1816 return NS_OK;
1819 //end nsLineIterator methods
1821 static void
1822 DestroyFrameCursorData(void* aObject, nsIAtom* aPropertyName,
1823 void* aPropertyValue, void* aData)
1825 delete static_cast<nsTableRowGroupFrame::FrameCursorData*>(aPropertyValue);
1828 void
1829 nsTableRowGroupFrame::ClearRowCursor()
1831 if (!(GetStateBits() & NS_ROWGROUP_HAS_ROW_CURSOR))
1832 return;
1834 RemoveStateBits(NS_ROWGROUP_HAS_ROW_CURSOR);
1835 DeleteProperty(nsGkAtoms::rowCursorProperty);
1838 nsTableRowGroupFrame::FrameCursorData*
1839 nsTableRowGroupFrame::SetupRowCursor()
1841 if (GetStateBits() & NS_ROWGROUP_HAS_ROW_CURSOR) {
1842 // We already have a valid row cursor. Don't waste time rebuilding it.
1843 return nsnull;
1846 nsIFrame* f = mFrames.FirstChild();
1847 PRInt32 count;
1848 for (count = 0; f && count < MIN_ROWS_NEEDING_CURSOR; ++count) {
1849 f = f->GetNextSibling();
1851 if (!f) {
1852 // Less than MIN_ROWS_NEEDING_CURSOR rows, so just don't bother
1853 return nsnull;
1856 FrameCursorData* data = new FrameCursorData();
1857 if (!data)
1858 return nsnull;
1859 nsresult rv = SetProperty(nsGkAtoms::rowCursorProperty, data,
1860 DestroyFrameCursorData);
1861 if (NS_FAILED(rv)) {
1862 delete data;
1863 return nsnull;
1865 AddStateBits(NS_ROWGROUP_HAS_ROW_CURSOR);
1866 return data;
1869 nsIFrame*
1870 nsTableRowGroupFrame::GetFirstRowContaining(nscoord aY, nscoord* aOverflowAbove)
1872 if (!(GetStateBits() & NS_ROWGROUP_HAS_ROW_CURSOR))
1873 return nsnull;
1875 FrameCursorData* property = static_cast<FrameCursorData*>
1876 (GetProperty(nsGkAtoms::rowCursorProperty));
1877 PRUint32 cursorIndex = property->mCursorIndex;
1878 PRUint32 frameCount = property->mFrames.Length();
1879 if (cursorIndex >= frameCount)
1880 return nsnull;
1881 nsIFrame* cursorFrame = property->mFrames[cursorIndex];
1883 // The cursor's frame list excludes frames with empty overflow-area, so
1884 // we don't need to check that here.
1886 // We use property->mOverflowBelow here instead of computing the frame's
1887 // true overflowArea.YMost(), because it is essential for the thresholds
1888 // to form a monotonically increasing sequence. Otherwise we would break
1889 // encountering a row whose overflowArea.YMost() is <= aY but which has
1890 // a row above it containing cell(s) that span to include aY.
1891 while (cursorIndex > 0 &&
1892 cursorFrame->GetRect().YMost() + property->mOverflowBelow > aY) {
1893 --cursorIndex;
1894 cursorFrame = property->mFrames[cursorIndex];
1896 while (cursorIndex + 1 < frameCount &&
1897 cursorFrame->GetRect().YMost() + property->mOverflowBelow <= aY) {
1898 ++cursorIndex;
1899 cursorFrame = property->mFrames[cursorIndex];
1902 property->mCursorIndex = cursorIndex;
1903 *aOverflowAbove = property->mOverflowAbove;
1904 return cursorFrame;
1907 PRBool
1908 nsTableRowGroupFrame::FrameCursorData::AppendFrame(nsIFrame* aFrame)
1910 nsRect overflowRect = aFrame->GetOverflowRect();
1911 if (overflowRect.IsEmpty())
1912 return PR_TRUE;
1913 nscoord overflowAbove = -overflowRect.y;
1914 nscoord overflowBelow = overflowRect.YMost() - aFrame->GetSize().height;
1915 mOverflowAbove = PR_MAX(mOverflowAbove, overflowAbove);
1916 mOverflowBelow = PR_MAX(mOverflowBelow, overflowBelow);
1917 return mFrames.AppendElement(aFrame) != nsnull;