Wed, 31 Dec 2014 13:27:57 +0100
Ignore runtime configuration files generated during quality assurance.
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "nsCOMPtr.h"
6 #include "nsTableRowGroupFrame.h"
7 #include "nsTableRowFrame.h"
8 #include "nsTableFrame.h"
9 #include "nsTableCellFrame.h"
10 #include "nsPresContext.h"
11 #include "nsStyleContext.h"
12 #include "nsStyleConsts.h"
13 #include "nsIContent.h"
14 #include "nsGkAtoms.h"
15 #include "nsIPresShell.h"
16 #include "nsCSSRendering.h"
17 #include "nsHTMLParts.h"
18 #include "nsCSSFrameConstructor.h"
19 #include "nsDisplayList.h"
21 #include "nsCellMap.h"//table cell navigation
22 #include <algorithm>
24 using namespace mozilla;
25 using namespace mozilla::layout;
27 nsTableRowGroupFrame::nsTableRowGroupFrame(nsStyleContext* aContext):
28 nsContainerFrame(aContext)
29 {
30 SetRepeatable(false);
31 }
33 nsTableRowGroupFrame::~nsTableRowGroupFrame()
34 {
35 }
37 void
38 nsTableRowGroupFrame::DestroyFrom(nsIFrame* aDestructRoot)
39 {
40 if (GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN) {
41 nsTableFrame::UnregisterPositionedTablePart(this, aDestructRoot);
42 }
44 nsContainerFrame::DestroyFrom(aDestructRoot);
45 }
47 NS_QUERYFRAME_HEAD(nsTableRowGroupFrame)
48 NS_QUERYFRAME_ENTRY(nsTableRowGroupFrame)
49 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
51 int32_t
52 nsTableRowGroupFrame::GetRowCount()
53 {
54 #ifdef DEBUG
55 for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) {
56 NS_ASSERTION(e.get()->StyleDisplay()->mDisplay ==
57 NS_STYLE_DISPLAY_TABLE_ROW,
58 "Unexpected display");
59 NS_ASSERTION(e.get()->GetType() == nsGkAtoms::tableRowFrame,
60 "Unexpected frame type");
61 }
62 #endif
64 return mFrames.GetLength();
65 }
67 int32_t nsTableRowGroupFrame::GetStartRowIndex()
68 {
69 int32_t result = -1;
70 if (mFrames.NotEmpty()) {
71 NS_ASSERTION(mFrames.FirstChild()->GetType() == nsGkAtoms::tableRowFrame,
72 "Unexpected frame type");
73 result = static_cast<nsTableRowFrame*>(mFrames.FirstChild())->GetRowIndex();
74 }
75 // if the row group doesn't have any children, get it the hard way
76 if (-1 == result) {
77 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
78 return tableFrame->GetStartRowIndex(this);
79 }
81 return result;
82 }
84 void nsTableRowGroupFrame::AdjustRowIndices(int32_t aRowIndex,
85 int32_t anAdjustment)
86 {
87 nsIFrame* rowFrame = GetFirstPrincipalChild();
88 for ( ; rowFrame; rowFrame = rowFrame->GetNextSibling()) {
89 if (NS_STYLE_DISPLAY_TABLE_ROW==rowFrame->StyleDisplay()->mDisplay) {
90 int32_t index = ((nsTableRowFrame*)rowFrame)->GetRowIndex();
91 if (index >= aRowIndex)
92 ((nsTableRowFrame *)rowFrame)->SetRowIndex(index+anAdjustment);
93 }
94 }
95 }
96 nsresult
97 nsTableRowGroupFrame::InitRepeatedFrame(nsPresContext* aPresContext,
98 nsTableRowGroupFrame* aHeaderFooterFrame)
99 {
100 nsTableRowFrame* copyRowFrame = GetFirstRow();
101 nsTableRowFrame* originalRowFrame = aHeaderFooterFrame->GetFirstRow();
102 AddStateBits(NS_REPEATED_ROW_OR_ROWGROUP);
103 while (copyRowFrame && originalRowFrame) {
104 copyRowFrame->AddStateBits(NS_REPEATED_ROW_OR_ROWGROUP);
105 int rowIndex = originalRowFrame->GetRowIndex();
106 copyRowFrame->SetRowIndex(rowIndex);
108 // For each table cell frame set its column index
109 nsTableCellFrame* originalCellFrame = originalRowFrame->GetFirstCell();
110 nsTableCellFrame* copyCellFrame = copyRowFrame->GetFirstCell();
111 while (copyCellFrame && originalCellFrame) {
112 NS_ASSERTION(originalCellFrame->GetContent() == copyCellFrame->GetContent(),
113 "cell frames have different content");
114 int32_t colIndex;
115 originalCellFrame->GetColIndex(colIndex);
116 copyCellFrame->SetColIndex(colIndex);
118 // Move to the next cell frame
119 copyCellFrame = copyCellFrame->GetNextCell();
120 originalCellFrame = originalCellFrame->GetNextCell();
121 }
123 // Move to the next row frame
124 originalRowFrame = originalRowFrame->GetNextRow();
125 copyRowFrame = copyRowFrame->GetNextRow();
126 }
128 return NS_OK;
129 }
131 /**
132 * We need a custom display item for table row backgrounds. This is only used
133 * when the table row is the root of a stacking context (e.g., has 'opacity').
134 * Table row backgrounds can extend beyond the row frame bounds, when
135 * the row contains row-spanning cells.
136 */
137 class nsDisplayTableRowGroupBackground : public nsDisplayTableItem {
138 public:
139 nsDisplayTableRowGroupBackground(nsDisplayListBuilder* aBuilder,
140 nsTableRowGroupFrame* aFrame) :
141 nsDisplayTableItem(aBuilder, aFrame) {
142 MOZ_COUNT_CTOR(nsDisplayTableRowGroupBackground);
143 }
144 #ifdef NS_BUILD_REFCNT_LOGGING
145 virtual ~nsDisplayTableRowGroupBackground() {
146 MOZ_COUNT_DTOR(nsDisplayTableRowGroupBackground);
147 }
148 #endif
150 virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
151 const nsDisplayItemGeometry* aGeometry,
152 nsRegion *aInvalidRegion) MOZ_OVERRIDE;
153 virtual void Paint(nsDisplayListBuilder* aBuilder,
154 nsRenderingContext* aCtx) MOZ_OVERRIDE;
156 NS_DISPLAY_DECL_NAME("TableRowGroupBackground", TYPE_TABLE_ROW_GROUP_BACKGROUND)
157 };
159 void
160 nsDisplayTableRowGroupBackground::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
161 const nsDisplayItemGeometry* aGeometry,
162 nsRegion *aInvalidRegion)
163 {
164 if (aBuilder->ShouldSyncDecodeImages()) {
165 if (nsTableFrame::AnyTablePartHasUndecodedBackgroundImage(mFrame, mFrame->GetNextSibling())) {
166 bool snap;
167 aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
168 }
169 }
171 nsDisplayTableItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
172 }
174 void
175 nsDisplayTableRowGroupBackground::Paint(nsDisplayListBuilder* aBuilder,
176 nsRenderingContext* aCtx)
177 {
178 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(mFrame);
179 TableBackgroundPainter painter(tableFrame,
180 TableBackgroundPainter::eOrigin_TableRowGroup,
181 mFrame->PresContext(), *aCtx,
182 mVisibleRect, ToReferenceFrame(),
183 aBuilder->GetBackgroundPaintFlags());
184 painter.PaintRowGroup(static_cast<nsTableRowGroupFrame*>(mFrame));
185 }
187 // Handle the child-traversal part of DisplayGenericTablePart
188 static void
189 DisplayRows(nsDisplayListBuilder* aBuilder, nsFrame* aFrame,
190 const nsRect& aDirtyRect, const nsDisplayListSet& aLists)
191 {
192 nscoord overflowAbove;
193 nsTableRowGroupFrame* f = static_cast<nsTableRowGroupFrame*>(aFrame);
194 // Don't try to use the row cursor if we have to descend into placeholders;
195 // we might have rows containing placeholders, where the row's overflow
196 // area doesn't intersect the dirty rect but we need to descend into the row
197 // to see out of flows.
198 // Note that we really want to check ShouldDescendIntoFrame for all
199 // the rows in |f|, but that's exactly what we're trying to avoid, so we
200 // approximate it by checking it for |f|: if it's true for any row
201 // in |f| then it's true for |f| itself.
202 nsIFrame* kid = aBuilder->ShouldDescendIntoFrame(f) ?
203 nullptr : f->GetFirstRowContaining(aDirtyRect.y, &overflowAbove);
205 if (kid) {
206 // have a cursor, use it
207 while (kid) {
208 if (kid->GetRect().y - overflowAbove >= aDirtyRect.YMost())
209 break;
210 f->BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists);
211 kid = kid->GetNextSibling();
212 }
213 return;
214 }
216 // No cursor. Traverse children the hard way and build a cursor while we're at it
217 nsTableRowGroupFrame::FrameCursorData* cursor = f->SetupRowCursor();
218 kid = f->GetFirstPrincipalChild();
219 while (kid) {
220 f->BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists);
222 if (cursor) {
223 if (!cursor->AppendFrame(kid)) {
224 f->ClearRowCursor();
225 return;
226 }
227 }
229 kid = kid->GetNextSibling();
230 }
231 if (cursor) {
232 cursor->FinishBuildingCursor();
233 }
234 }
236 void
237 nsTableRowGroupFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
238 const nsRect& aDirtyRect,
239 const nsDisplayListSet& aLists)
240 {
241 nsDisplayTableItem* item = nullptr;
242 if (IsVisibleInSelection(aBuilder)) {
243 bool isRoot = aBuilder->IsAtRootOfPseudoStackingContext();
244 if (isRoot) {
245 // This background is created regardless of whether this frame is
246 // visible or not. Visibility decisions are delegated to the
247 // table background painter.
248 item = new (aBuilder) nsDisplayTableRowGroupBackground(aBuilder, this);
249 aLists.BorderBackground()->AppendNewToTop(item);
250 }
251 }
252 nsTableFrame::DisplayGenericTablePart(aBuilder, this, aDirtyRect,
253 aLists, item, DisplayRows);
254 }
256 int
257 nsTableRowGroupFrame::GetLogicalSkipSides(const nsHTMLReflowState* aReflowState) const
258 {
259 int skip = 0;
260 if (nullptr != GetPrevInFlow()) {
261 skip |= LOGICAL_SIDE_B_START;
262 }
263 if (nullptr != GetNextInFlow()) {
264 skip |= LOGICAL_SIDE_B_END;
265 }
266 return skip;
267 }
269 // Position and size aKidFrame and update our reflow state. The origin of
270 // aKidRect is relative to the upper-left origin of our frame
271 void
272 nsTableRowGroupFrame::PlaceChild(nsPresContext* aPresContext,
273 nsRowGroupReflowState& aReflowState,
274 nsIFrame* aKidFrame,
275 nsHTMLReflowMetrics& aDesiredSize,
276 const nsRect& aOriginalKidRect,
277 const nsRect& aOriginalKidVisualOverflow)
278 {
279 bool isFirstReflow =
280 (aKidFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0;
282 // Place and size the child
283 FinishReflowChild(aKidFrame, aPresContext, aDesiredSize, nullptr, 0,
284 aReflowState.y, 0);
286 nsTableFrame::InvalidateTableFrame(aKidFrame, aOriginalKidRect,
287 aOriginalKidVisualOverflow, isFirstReflow);
289 // Adjust the running y-offset
290 aReflowState.y += aDesiredSize.Height();
292 // If our height is constrained then update the available height
293 if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height) {
294 aReflowState.availSize.height -= aDesiredSize.Height();
295 }
296 }
298 void
299 nsTableRowGroupFrame::InitChildReflowState(nsPresContext& aPresContext,
300 bool aBorderCollapse,
301 nsHTMLReflowState& aReflowState)
302 {
303 nsMargin collapseBorder;
304 nsMargin padding(0,0,0,0);
305 nsMargin* pCollapseBorder = nullptr;
306 if (aBorderCollapse) {
307 nsTableRowFrame *rowFrame = do_QueryFrame(aReflowState.frame);
308 if (rowFrame) {
309 pCollapseBorder = rowFrame->GetBCBorderWidth(collapseBorder);
310 }
311 }
312 aReflowState.Init(&aPresContext, -1, -1, pCollapseBorder, &padding);
313 }
315 static void
316 CacheRowHeightsForPrinting(nsPresContext* aPresContext,
317 nsTableRowFrame* aFirstRow)
318 {
319 for (nsTableRowFrame* row = aFirstRow; row; row = row->GetNextRow()) {
320 if (!row->GetPrevInFlow()) {
321 row->SetHasUnpaginatedHeight(true);
322 row->SetUnpaginatedHeight(aPresContext, row->GetSize().height);
323 }
324 }
325 }
327 nsresult
328 nsTableRowGroupFrame::ReflowChildren(nsPresContext* aPresContext,
329 nsHTMLReflowMetrics& aDesiredSize,
330 nsRowGroupReflowState& aReflowState,
331 nsReflowStatus& aStatus,
332 bool* aPageBreakBeforeEnd)
333 {
334 if (aPageBreakBeforeEnd)
335 *aPageBreakBeforeEnd = false;
337 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
338 nsresult rv = NS_OK;
339 const bool borderCollapse = tableFrame->IsBorderCollapse();
340 nscoord cellSpacingY = tableFrame->GetCellSpacingY();
342 // XXXldb Should we really be checking this rather than available height?
343 // (Think about multi-column layout!)
344 bool isPaginated = aPresContext->IsPaginated() &&
345 NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height;
347 bool haveRow = false;
348 bool reflowAllKids = aReflowState.reflowState.ShouldReflowAllKids() ||
349 tableFrame->IsGeometryDirty();
350 bool needToCalcRowHeights = reflowAllKids;
352 nsIFrame *prevKidFrame = nullptr;
353 for (nsIFrame* kidFrame = mFrames.FirstChild(); kidFrame;
354 prevKidFrame = kidFrame, kidFrame = kidFrame->GetNextSibling()) {
355 nsTableRowFrame *rowFrame = do_QueryFrame(kidFrame);
356 if (!rowFrame) {
357 // XXXldb nsCSSFrameConstructor needs to enforce this!
358 NS_NOTREACHED("yikes, a non-row child");
359 continue;
360 }
362 haveRow = true;
364 // Reflow the row frame
365 if (reflowAllKids ||
366 NS_SUBTREE_DIRTY(kidFrame) ||
367 (aReflowState.reflowState.mFlags.mSpecialHeightReflow &&
368 (isPaginated || (kidFrame->GetStateBits() &
369 NS_FRAME_CONTAINS_RELATIVE_HEIGHT)))) {
370 nsRect oldKidRect = kidFrame->GetRect();
371 nsRect oldKidVisualOverflow = kidFrame->GetVisualOverflowRect();
373 // XXXldb We used to only pass aDesiredSize.mFlags through for the
374 // incremental reflow codepath.
375 nsHTMLReflowMetrics desiredSize(aReflowState.reflowState,
376 aDesiredSize.mFlags);
377 desiredSize.Width() = desiredSize.Height() = 0;
379 // Reflow the child into the available space, giving it as much height as
380 // it wants. We'll deal with splitting later after we've computed the row
381 // heights, taking into account cells with row spans...
382 nsSize kidAvailSize(aReflowState.availSize.width, NS_UNCONSTRAINEDSIZE);
383 nsHTMLReflowState kidReflowState(aPresContext, aReflowState.reflowState,
384 kidFrame, kidAvailSize,
385 -1, -1,
386 nsHTMLReflowState::CALLER_WILL_INIT);
387 InitChildReflowState(*aPresContext, borderCollapse, kidReflowState);
389 // This can indicate that columns were resized.
390 if (aReflowState.reflowState.mFlags.mHResize)
391 kidReflowState.mFlags.mHResize = true;
393 NS_ASSERTION(kidFrame == mFrames.FirstChild() || prevKidFrame,
394 "If we're not on the first frame, we should have a "
395 "previous sibling...");
396 // If prev row has nonzero YMost, then we can't be at the top of the page
397 if (prevKidFrame && prevKidFrame->GetRect().YMost() > 0) {
398 kidReflowState.mFlags.mIsTopOfPage = false;
399 }
401 rv = ReflowChild(kidFrame, aPresContext, desiredSize, kidReflowState,
402 0, aReflowState.y, 0, aStatus);
404 // Place the child
405 PlaceChild(aPresContext, aReflowState, kidFrame, desiredSize,
406 oldKidRect, oldKidVisualOverflow);
407 aReflowState.y += cellSpacingY;
409 if (!reflowAllKids) {
410 if (IsSimpleRowFrame(aReflowState.tableFrame, kidFrame)) {
411 // Inform the row of its new height.
412 rowFrame->DidResize();
413 // the overflow area may have changed inflate the overflow area
414 const nsStylePosition *stylePos = StylePosition();
415 nsStyleUnit unit = stylePos->mHeight.GetUnit();
416 if (aReflowState.tableFrame->IsAutoHeight() &&
417 unit != eStyleUnit_Coord) {
418 // Because other cells in the row may need to be aligned
419 // differently, repaint the entire row
420 nsRect kidRect(0, aReflowState.y,
421 desiredSize.Width(), desiredSize.Height());
422 InvalidateFrame();
423 }
424 else if (oldKidRect.height != desiredSize.Height())
425 needToCalcRowHeights = true;
426 } else {
427 needToCalcRowHeights = true;
428 }
429 }
431 if (isPaginated && aPageBreakBeforeEnd && !*aPageBreakBeforeEnd) {
432 nsTableRowFrame* nextRow = rowFrame->GetNextRow();
433 if (nextRow) {
434 *aPageBreakBeforeEnd = nsTableFrame::PageBreakAfter(kidFrame, nextRow);
435 }
436 }
437 } else {
438 SlideChild(aReflowState, kidFrame);
440 // Adjust the running y-offset so we know where the next row should be placed
441 nscoord height = kidFrame->GetSize().height + cellSpacingY;
442 aReflowState.y += height;
444 if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height) {
445 aReflowState.availSize.height -= height;
446 }
447 }
448 ConsiderChildOverflow(aDesiredSize.mOverflowAreas, kidFrame);
449 }
451 if (haveRow)
452 aReflowState.y -= cellSpacingY;
454 // Return our desired rect
455 aDesiredSize.Width() = aReflowState.reflowState.AvailableWidth();
456 aDesiredSize.Height() = aReflowState.y;
458 if (aReflowState.reflowState.mFlags.mSpecialHeightReflow) {
459 DidResizeRows(aDesiredSize);
460 if (isPaginated) {
461 CacheRowHeightsForPrinting(aPresContext, GetFirstRow());
462 }
463 }
464 else if (needToCalcRowHeights) {
465 CalculateRowHeights(aPresContext, aDesiredSize, aReflowState.reflowState);
466 if (!reflowAllKids) {
467 InvalidateFrame();
468 }
469 }
471 return rv;
472 }
474 nsTableRowFrame*
475 nsTableRowGroupFrame::GetFirstRow()
476 {
477 for (nsIFrame* childFrame = mFrames.FirstChild(); childFrame;
478 childFrame = childFrame->GetNextSibling()) {
479 nsTableRowFrame *rowFrame = do_QueryFrame(childFrame);
480 if (rowFrame) {
481 return rowFrame;
482 }
483 }
484 return nullptr;
485 }
488 struct RowInfo {
489 RowInfo() { height = pctHeight = hasStyleHeight = hasPctHeight = isSpecial = 0; }
490 unsigned height; // content height or fixed height, excluding pct height
491 unsigned pctHeight:29; // pct height
492 unsigned hasStyleHeight:1;
493 unsigned hasPctHeight:1;
494 unsigned isSpecial:1; // there is no cell originating in the row with rowspan=1 and there are at
495 // least 2 cells spanning the row and there is no style height on the row
496 };
498 static void
499 UpdateHeights(RowInfo& aRowInfo,
500 nscoord aAdditionalHeight,
501 nscoord& aTotal,
502 nscoord& aUnconstrainedTotal)
503 {
504 aRowInfo.height += aAdditionalHeight;
505 aTotal += aAdditionalHeight;
506 if (!aRowInfo.hasStyleHeight) {
507 aUnconstrainedTotal += aAdditionalHeight;
508 }
509 }
511 void
512 nsTableRowGroupFrame::DidResizeRows(nsHTMLReflowMetrics& aDesiredSize)
513 {
514 // update the cells spanning rows with their new heights
515 // this is the place where all of the cells in the row get set to the height of the row
516 // Reset the overflow area
517 aDesiredSize.mOverflowAreas.Clear();
518 for (nsTableRowFrame* rowFrame = GetFirstRow();
519 rowFrame; rowFrame = rowFrame->GetNextRow()) {
520 rowFrame->DidResize();
521 ConsiderChildOverflow(aDesiredSize.mOverflowAreas, rowFrame);
522 }
523 }
525 // This calculates the height of all the rows and takes into account
526 // style height on the row group, style heights on rows and cells, style heights on rowspans.
527 // Actual row heights will be adjusted later if the table has a style height.
528 // Even if rows don't change height, this method must be called to set the heights of each
529 // cell in the row to the height of its row.
530 void
531 nsTableRowGroupFrame::CalculateRowHeights(nsPresContext* aPresContext,
532 nsHTMLReflowMetrics& aDesiredSize,
533 const nsHTMLReflowState& aReflowState)
534 {
535 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
536 const bool isPaginated = aPresContext->IsPaginated();
538 // all table cells have the same top and bottom margins, namely cellSpacingY
539 nscoord cellSpacingY = tableFrame->GetCellSpacingY();
541 int32_t numEffCols = tableFrame->GetEffectiveColCount();
543 int32_t startRowIndex = GetStartRowIndex();
544 // find the row corresponding to the row index we just found
545 nsTableRowFrame* startRowFrame = GetFirstRow();
547 if (!startRowFrame) return;
549 // the current row group height is the y origin of the 1st row we are about to calculated a height for
550 nscoord startRowGroupHeight = startRowFrame->GetPosition().y;
552 int32_t numRows = GetRowCount() - (startRowFrame->GetRowIndex() - GetStartRowIndex());
553 // collect the current height of each row. nscoord* rowHeights = nullptr;
554 if (numRows <= 0)
555 return;
557 nsTArray<RowInfo> rowInfo;
558 if (!rowInfo.AppendElements(numRows)) {
559 return;
560 }
562 bool hasRowSpanningCell = false;
563 nscoord heightOfRows = 0;
564 nscoord heightOfUnStyledRows = 0;
565 // Get the height of each row without considering rowspans. This will be the max of
566 // the largest desired height of each cell, the largest style height of each cell,
567 // the style height of the row.
568 nscoord pctHeightBasis = GetHeightBasis(aReflowState);
569 int32_t rowIndex; // the index in rowInfo, not among the rows in the row group
570 nsTableRowFrame* rowFrame;
571 for (rowFrame = startRowFrame, rowIndex = 0; rowFrame; rowFrame = rowFrame->GetNextRow(), rowIndex++) {
572 nscoord nonPctHeight = rowFrame->GetContentHeight();
573 if (isPaginated) {
574 nonPctHeight = std::max(nonPctHeight, rowFrame->GetSize().height);
575 }
576 if (!rowFrame->GetPrevInFlow()) {
577 if (rowFrame->HasPctHeight()) {
578 rowInfo[rowIndex].hasPctHeight = true;
579 rowInfo[rowIndex].pctHeight = rowFrame->GetHeight(pctHeightBasis);
580 }
581 rowInfo[rowIndex].hasStyleHeight = rowFrame->HasStyleHeight();
582 nonPctHeight = std::max(nonPctHeight, rowFrame->GetFixedHeight());
583 }
584 UpdateHeights(rowInfo[rowIndex], nonPctHeight, heightOfRows, heightOfUnStyledRows);
586 if (!rowInfo[rowIndex].hasStyleHeight) {
587 if (isPaginated || tableFrame->HasMoreThanOneCell(rowIndex + startRowIndex)) {
588 rowInfo[rowIndex].isSpecial = true;
589 // iteratate the row's cell frames to see if any do not have rowspan > 1
590 nsTableCellFrame* cellFrame = rowFrame->GetFirstCell();
591 while (cellFrame) {
592 int32_t rowSpan = tableFrame->GetEffectiveRowSpan(rowIndex + startRowIndex, *cellFrame);
593 if (1 == rowSpan) {
594 rowInfo[rowIndex].isSpecial = false;
595 break;
596 }
597 cellFrame = cellFrame->GetNextCell();
598 }
599 }
600 }
601 // See if a cell spans into the row. If so we'll have to do the next step
602 if (!hasRowSpanningCell) {
603 if (tableFrame->RowIsSpannedInto(rowIndex + startRowIndex, numEffCols)) {
604 hasRowSpanningCell = true;
605 }
606 }
607 }
609 if (hasRowSpanningCell) {
610 // Get the height of cells with rowspans and allocate any extra space to the rows they span
611 // iteratate the child frames and process the row frames among them
612 for (rowFrame = startRowFrame, rowIndex = 0; rowFrame; rowFrame = rowFrame->GetNextRow(), rowIndex++) {
613 // See if the row has an originating cell with rowspan > 1. We cannot determine this for a row in a
614 // continued row group by calling RowHasSpanningCells, because the row's fif may not have any originating
615 // cells yet the row may have a continued cell which originates in it.
616 if (GetPrevInFlow() || tableFrame->RowHasSpanningCells(startRowIndex + rowIndex, numEffCols)) {
617 nsTableCellFrame* cellFrame = rowFrame->GetFirstCell();
618 // iteratate the row's cell frames
619 while (cellFrame) {
620 int32_t rowSpan = tableFrame->GetEffectiveRowSpan(rowIndex + startRowIndex, *cellFrame);
621 if ((rowIndex + rowSpan) > numRows) {
622 // there might be rows pushed already to the nextInFlow
623 rowSpan = numRows - rowIndex;
624 }
625 if (rowSpan > 1) { // a cell with rowspan > 1, determine the height of the rows it spans
626 nscoord heightOfRowsSpanned = 0;
627 nscoord heightOfUnStyledRowsSpanned = 0;
628 nscoord numSpecialRowsSpanned = 0;
629 nscoord cellSpacingTotal = 0;
630 int32_t spanX;
631 for (spanX = 0; spanX < rowSpan; spanX++) {
632 heightOfRowsSpanned += rowInfo[rowIndex + spanX].height;
633 if (!rowInfo[rowIndex + spanX].hasStyleHeight) {
634 heightOfUnStyledRowsSpanned += rowInfo[rowIndex + spanX].height;
635 }
636 if (0 != spanX) {
637 cellSpacingTotal += cellSpacingY;
638 }
639 if (rowInfo[rowIndex + spanX].isSpecial) {
640 numSpecialRowsSpanned++;
641 }
642 }
643 nscoord heightOfAreaSpanned = heightOfRowsSpanned + cellSpacingTotal;
644 // get the height of the cell
645 nsSize cellFrameSize = cellFrame->GetSize();
646 nsSize cellDesSize = cellFrame->GetDesiredSize();
647 rowFrame->CalculateCellActualHeight(cellFrame, cellDesSize.height);
648 cellFrameSize.height = cellDesSize.height;
649 if (cellFrame->HasVerticalAlignBaseline()) {
650 // to ensure that a spanning cell with a long descender doesn't
651 // collide with the next row, we need to take into account the shift
652 // that will be done to align the cell on the baseline of the row.
653 cellFrameSize.height += rowFrame->GetMaxCellAscent() -
654 cellFrame->GetCellBaseline();
655 }
657 if (heightOfAreaSpanned < cellFrameSize.height) {
658 // the cell's height is larger than the available space of the rows it
659 // spans so distribute the excess height to the rows affected
660 nscoord extra = cellFrameSize.height - heightOfAreaSpanned;
661 nscoord extraUsed = 0;
662 if (0 == numSpecialRowsSpanned) {
663 //NS_ASSERTION(heightOfRowsSpanned > 0, "invalid row span situation");
664 bool haveUnStyledRowsSpanned = (heightOfUnStyledRowsSpanned > 0);
665 nscoord divisor = (haveUnStyledRowsSpanned)
666 ? heightOfUnStyledRowsSpanned : heightOfRowsSpanned;
667 if (divisor > 0) {
668 for (spanX = rowSpan - 1; spanX >= 0; spanX--) {
669 if (!haveUnStyledRowsSpanned || !rowInfo[rowIndex + spanX].hasStyleHeight) {
670 // The amount of additional space each row gets is proportional to its height
671 float percent = ((float)rowInfo[rowIndex + spanX].height) / ((float)divisor);
673 // give rows their percentage, except for the first row which gets the remainder
674 nscoord extraForRow = (0 == spanX) ? extra - extraUsed
675 : NSToCoordRound(((float)(extra)) * percent);
676 extraForRow = std::min(extraForRow, extra - extraUsed);
677 // update the row height
678 UpdateHeights(rowInfo[rowIndex + spanX], extraForRow, heightOfRows, heightOfUnStyledRows);
679 extraUsed += extraForRow;
680 if (extraUsed >= extra) {
681 NS_ASSERTION((extraUsed == extra), "invalid row height calculation");
682 break;
683 }
684 }
685 }
686 }
687 else {
688 // put everything in the last row
689 UpdateHeights(rowInfo[rowIndex + rowSpan - 1], extra, heightOfRows, heightOfUnStyledRows);
690 }
691 }
692 else {
693 // give the extra to the special rows
694 nscoord numSpecialRowsAllocated = 0;
695 for (spanX = rowSpan - 1; spanX >= 0; spanX--) {
696 if (rowInfo[rowIndex + spanX].isSpecial) {
697 // The amount of additional space each degenerate row gets is proportional to the number of them
698 float percent = 1.0f / ((float)numSpecialRowsSpanned);
700 // give rows their percentage, except for the first row which gets the remainder
701 nscoord extraForRow = (numSpecialRowsSpanned - 1 == numSpecialRowsAllocated)
702 ? extra - extraUsed
703 : NSToCoordRound(((float)(extra)) * percent);
704 extraForRow = std::min(extraForRow, extra - extraUsed);
705 // update the row height
706 UpdateHeights(rowInfo[rowIndex + spanX], extraForRow, heightOfRows, heightOfUnStyledRows);
707 extraUsed += extraForRow;
708 if (extraUsed >= extra) {
709 NS_ASSERTION((extraUsed == extra), "invalid row height calculation");
710 break;
711 }
712 }
713 }
714 }
715 }
716 } // if (rowSpan > 1)
717 cellFrame = cellFrame->GetNextCell();
718 } // while (cellFrame)
719 } // if (tableFrame->RowHasSpanningCells(startRowIndex + rowIndex) {
720 } // while (rowFrame)
721 }
723 // pct height rows have already got their content heights. Give them their pct heights up to pctHeightBasis
724 nscoord extra = pctHeightBasis - heightOfRows;
725 for (rowFrame = startRowFrame, rowIndex = 0; rowFrame && (extra > 0); rowFrame = rowFrame->GetNextRow(), rowIndex++) {
726 RowInfo& rInfo = rowInfo[rowIndex];
727 if (rInfo.hasPctHeight) {
728 nscoord rowExtra = (rInfo.pctHeight > rInfo.height)
729 ? rInfo.pctHeight - rInfo.height: 0;
730 rowExtra = std::min(rowExtra, extra);
731 UpdateHeights(rInfo, rowExtra, heightOfRows, heightOfUnStyledRows);
732 extra -= rowExtra;
733 }
734 }
736 bool styleHeightAllocation = false;
737 nscoord rowGroupHeight = startRowGroupHeight + heightOfRows + ((numRows - 1) * cellSpacingY);
738 // if we have a style height, allocate the extra height to unconstrained rows
739 if ((aReflowState.ComputedHeight() > rowGroupHeight) &&
740 (NS_UNCONSTRAINEDSIZE != aReflowState.ComputedHeight())) {
741 nscoord extraComputedHeight = aReflowState.ComputedHeight() - rowGroupHeight;
742 nscoord extraUsed = 0;
743 bool haveUnStyledRows = (heightOfUnStyledRows > 0);
744 nscoord divisor = (haveUnStyledRows)
745 ? heightOfUnStyledRows : heightOfRows;
746 if (divisor > 0) {
747 styleHeightAllocation = true;
748 for (rowIndex = 0; rowIndex < numRows; rowIndex++) {
749 if (!haveUnStyledRows || !rowInfo[rowIndex].hasStyleHeight) {
750 // The amount of additional space each row gets is based on the
751 // percentage of space it occupies
752 float percent = ((float)rowInfo[rowIndex].height) / ((float)divisor);
753 // give rows their percentage, except for the last row which gets the remainder
754 nscoord extraForRow = (numRows - 1 == rowIndex)
755 ? extraComputedHeight - extraUsed
756 : NSToCoordRound(((float)extraComputedHeight) * percent);
757 extraForRow = std::min(extraForRow, extraComputedHeight - extraUsed);
758 // update the row height
759 UpdateHeights(rowInfo[rowIndex], extraForRow, heightOfRows, heightOfUnStyledRows);
760 extraUsed += extraForRow;
761 if (extraUsed >= extraComputedHeight) {
762 NS_ASSERTION((extraUsed == extraComputedHeight), "invalid row height calculation");
763 break;
764 }
765 }
766 }
767 }
768 rowGroupHeight = aReflowState.ComputedHeight();
769 }
771 nscoord yOrigin = startRowGroupHeight;
772 // update the rows with their (potentially) new heights
773 for (rowFrame = startRowFrame, rowIndex = 0; rowFrame; rowFrame = rowFrame->GetNextRow(), rowIndex++) {
774 nsRect rowBounds = rowFrame->GetRect();
775 nsRect rowVisualOverflow = rowFrame->GetVisualOverflowRect();
777 bool movedFrame = (rowBounds.y != yOrigin);
778 nscoord rowHeight = (rowInfo[rowIndex].height > 0) ? rowInfo[rowIndex].height : 0;
780 if (movedFrame || (rowHeight != rowBounds.height)) {
781 // Resize/move the row to its final size and position
782 if (movedFrame) {
783 rowFrame->InvalidateFrameSubtree();
784 }
786 rowFrame->SetRect(nsRect(rowBounds.x, yOrigin, rowBounds.width,
787 rowHeight));
789 nsTableFrame::InvalidateTableFrame(rowFrame, rowBounds, rowVisualOverflow,
790 false);
791 }
792 if (movedFrame) {
793 nsTableFrame::RePositionViews(rowFrame);
794 // XXXbz we don't need to update our overflow area?
795 }
796 yOrigin += rowHeight + cellSpacingY;
797 }
799 if (isPaginated && styleHeightAllocation) {
800 // since the row group has a style height, cache the row heights, so next in flows can honor them
801 CacheRowHeightsForPrinting(aPresContext, GetFirstRow());
802 }
804 DidResizeRows(aDesiredSize);
806 aDesiredSize.Height() = rowGroupHeight; // Adjust our desired size
807 }
809 nscoord
810 nsTableRowGroupFrame::CollapseRowGroupIfNecessary(nscoord aYTotalOffset,
811 nscoord aWidth)
812 {
813 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
814 const nsStyleVisibility* groupVis = StyleVisibility();
815 bool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE == groupVis->mVisible);
816 if (collapseGroup) {
817 tableFrame->SetNeedToCollapse(true);
818 }
820 nsOverflowAreas overflow;
822 nsTableRowFrame* rowFrame= GetFirstRow();
823 bool didCollapse = false;
824 nscoord yGroupOffset = 0;
825 while (rowFrame) {
826 yGroupOffset += rowFrame->CollapseRowIfNecessary(yGroupOffset,
827 aWidth, collapseGroup,
828 didCollapse);
829 ConsiderChildOverflow(overflow, rowFrame);
830 rowFrame = rowFrame->GetNextRow();
831 }
833 nsRect groupRect = GetRect();
834 nsRect oldGroupRect = groupRect;
835 nsRect oldGroupVisualOverflow = GetVisualOverflowRect();
837 groupRect.height -= yGroupOffset;
838 if (didCollapse) {
839 // add back the cellspacing between rowgroups
840 groupRect.height += tableFrame->GetCellSpacingY();
841 }
843 groupRect.y -= aYTotalOffset;
844 groupRect.width = aWidth;
846 if (aYTotalOffset != 0) {
847 InvalidateFrameSubtree();
848 }
850 SetRect(groupRect);
851 overflow.UnionAllWith(nsRect(0, 0, groupRect.width, groupRect.height));
852 FinishAndStoreOverflow(overflow, groupRect.Size());
853 nsTableFrame::RePositionViews(this);
854 nsTableFrame::InvalidateTableFrame(this, oldGroupRect, oldGroupVisualOverflow,
855 false);
857 return yGroupOffset;
858 }
860 // Move a child that was skipped during a reflow.
861 void
862 nsTableRowGroupFrame::SlideChild(nsRowGroupReflowState& aReflowState,
863 nsIFrame* aKidFrame)
864 {
865 // Move the frame if we need to
866 nsPoint oldPosition = aKidFrame->GetPosition();
867 nsPoint newPosition = oldPosition;
868 newPosition.y = aReflowState.y;
869 if (oldPosition.y != newPosition.y) {
870 aKidFrame->InvalidateFrameSubtree();
871 aKidFrame->SetPosition(newPosition);
872 nsTableFrame::RePositionViews(aKidFrame);
873 aKidFrame->InvalidateFrameSubtree();
874 }
875 }
877 // Create a continuing frame, add it to the child list, and then push it
878 // and the frames that follow
879 void
880 nsTableRowGroupFrame::CreateContinuingRowFrame(nsPresContext& aPresContext,
881 nsIFrame& aRowFrame,
882 nsIFrame** aContRowFrame)
883 {
884 // XXX what is the row index?
885 if (!aContRowFrame) {NS_ASSERTION(false, "bad call"); return;}
886 // create the continuing frame which will create continuing cell frames
887 *aContRowFrame = aPresContext.PresShell()->FrameConstructor()->
888 CreateContinuingFrame(&aPresContext, &aRowFrame, this);
890 // Add the continuing row frame to the child list
891 mFrames.InsertFrame(nullptr, &aRowFrame, *aContRowFrame);
893 // Push the continuing row frame and the frames that follow
894 PushChildren(*aContRowFrame, &aRowFrame);
895 }
897 // Reflow the cells with rowspan > 1 which originate between aFirstRow
898 // and end on or after aLastRow. aFirstTruncatedRow is the highest row on the
899 // page that contains a cell which cannot split on this page
900 void
901 nsTableRowGroupFrame::SplitSpanningCells(nsPresContext& aPresContext,
902 const nsHTMLReflowState& aReflowState,
903 nsTableFrame& aTable,
904 nsTableRowFrame& aFirstRow,
905 nsTableRowFrame& aLastRow,
906 bool aFirstRowIsTopOfPage,
907 nscoord aSpanningRowBottom,
908 nsTableRowFrame*& aContRow,
909 nsTableRowFrame*& aFirstTruncatedRow,
910 nscoord& aDesiredHeight)
911 {
912 NS_ASSERTION(aSpanningRowBottom >= 0, "Can't split negative heights");
913 aFirstTruncatedRow = nullptr;
914 aDesiredHeight = 0;
916 const bool borderCollapse = aTable.IsBorderCollapse();
917 int32_t lastRowIndex = aLastRow.GetRowIndex();
918 bool wasLast = false;
919 bool haveRowSpan = false;
920 // Iterate the rows between aFirstRow and aLastRow
921 for (nsTableRowFrame* row = &aFirstRow; !wasLast; row = row->GetNextRow()) {
922 wasLast = (row == &aLastRow);
923 int32_t rowIndex = row->GetRowIndex();
924 nsPoint rowPos = row->GetPosition();
925 // Iterate the cells looking for those that have rowspan > 1
926 for (nsTableCellFrame* cell = row->GetFirstCell(); cell; cell = cell->GetNextCell()) {
927 int32_t rowSpan = aTable.GetEffectiveRowSpan(rowIndex, *cell);
928 // Only reflow rowspan > 1 cells which span aLastRow. Those which don't span aLastRow
929 // were reflowed correctly during the unconstrained height reflow.
930 if ((rowSpan > 1) && (rowIndex + rowSpan > lastRowIndex)) {
931 haveRowSpan = true;
932 nsReflowStatus status;
933 // Ask the row to reflow the cell to the height of all the rows it spans up through aLastRow
934 // aAvailHeight is the space between the row group start and the end of the page
935 nscoord cellAvailHeight = aSpanningRowBottom - rowPos.y;
936 NS_ASSERTION(cellAvailHeight >= 0, "No space for cell?");
937 bool isTopOfPage = (row == &aFirstRow) && aFirstRowIsTopOfPage;
939 nsRect rowRect = row->GetRect();
940 nsSize rowAvailSize(aReflowState.AvailableWidth(),
941 std::max(aReflowState.AvailableHeight() - rowRect.y,
942 0));
943 // don't let the available height exceed what
944 // CalculateRowHeights set for it
945 rowAvailSize.height = std::min(rowAvailSize.height, rowRect.height);
946 nsHTMLReflowState rowReflowState(&aPresContext, aReflowState,
947 row, rowAvailSize,
948 -1, -1,
949 nsHTMLReflowState::CALLER_WILL_INIT);
950 InitChildReflowState(aPresContext, borderCollapse, rowReflowState);
951 rowReflowState.mFlags.mIsTopOfPage = isTopOfPage; // set top of page
953 nscoord cellHeight = row->ReflowCellFrame(&aPresContext, rowReflowState,
954 isTopOfPage, cell,
955 cellAvailHeight, status);
956 aDesiredHeight = std::max(aDesiredHeight, rowPos.y + cellHeight);
957 if (NS_FRAME_IS_COMPLETE(status)) {
958 if (cellHeight > cellAvailHeight) {
959 aFirstTruncatedRow = row;
960 if ((row != &aFirstRow) || !aFirstRowIsTopOfPage) {
961 // return now, since we will be getting another reflow after either (1) row is
962 // moved to the next page or (2) the row group is moved to the next page
963 return;
964 }
965 }
966 }
967 else {
968 if (!aContRow) {
969 CreateContinuingRowFrame(aPresContext, aLastRow, (nsIFrame**)&aContRow);
970 }
971 if (aContRow) {
972 if (row != &aLastRow) {
973 // aContRow needs a continuation for cell, since cell spanned into aLastRow
974 // but does not originate there
975 nsTableCellFrame* contCell = static_cast<nsTableCellFrame*>(
976 aPresContext.PresShell()->FrameConstructor()->
977 CreateContinuingFrame(&aPresContext, cell, &aLastRow));
978 int32_t colIndex;
979 cell->GetColIndex(colIndex);
980 aContRow->InsertCellFrame(contCell, colIndex);
981 }
982 }
983 }
984 }
985 }
986 }
987 if (!haveRowSpan) {
988 aDesiredHeight = aLastRow.GetRect().YMost();
989 }
990 }
992 // Remove the next-in-flow of the row, its cells and their cell blocks. This
993 // is necessary in case the row doesn't need a continuation later on or needs
994 // a continuation which doesn't have the same number of cells that now exist.
995 void
996 nsTableRowGroupFrame::UndoContinuedRow(nsPresContext* aPresContext,
997 nsTableRowFrame* aRow)
998 {
999 if (!aRow) return; // allow null aRow to avoid callers doing null checks
1001 // rowBefore was the prev-sibling of aRow's next-sibling before aRow was created
1002 nsTableRowFrame* rowBefore = (nsTableRowFrame*)aRow->GetPrevInFlow();
1003 NS_PRECONDITION(mFrames.ContainsFrame(rowBefore),
1004 "rowBefore not in our frame list?");
1006 AutoFrameListPtr overflows(aPresContext, StealOverflowFrames());
1007 if (!rowBefore || !overflows || overflows->IsEmpty() ||
1008 overflows->FirstChild() != aRow) {
1009 NS_ERROR("invalid continued row");
1010 return;
1011 }
1013 // Destroy aRow, its cells, and their cell blocks. Cell blocks that have split
1014 // will not have reflowed yet to pick up content from any overflow lines.
1015 overflows->DestroyFrame(aRow);
1017 // Put the overflow rows into our child list
1018 if (!overflows->IsEmpty()) {
1019 mFrames.InsertFrames(nullptr, rowBefore, *overflows);
1020 }
1021 }
1023 static nsTableRowFrame*
1024 GetRowBefore(nsTableRowFrame& aStartRow,
1025 nsTableRowFrame& aRow)
1026 {
1027 nsTableRowFrame* rowBefore = nullptr;
1028 for (nsTableRowFrame* sib = &aStartRow; sib && (sib != &aRow); sib = sib->GetNextRow()) {
1029 rowBefore = sib;
1030 }
1031 return rowBefore;
1032 }
1034 nsresult
1035 nsTableRowGroupFrame::SplitRowGroup(nsPresContext* aPresContext,
1036 nsHTMLReflowMetrics& aDesiredSize,
1037 const nsHTMLReflowState& aReflowState,
1038 nsTableFrame* aTableFrame,
1039 nsReflowStatus& aStatus,
1040 bool aRowForcedPageBreak)
1041 {
1042 NS_PRECONDITION(aPresContext->IsPaginated(), "SplitRowGroup currently supports only paged media");
1044 nsresult rv = NS_OK;
1045 nsTableRowFrame* prevRowFrame = nullptr;
1046 aDesiredSize.Height() = 0;
1048 nscoord availWidth = aReflowState.AvailableWidth();
1049 nscoord availHeight = aReflowState.AvailableHeight();
1051 const bool borderCollapse = aTableFrame->IsBorderCollapse();
1052 nscoord cellSpacingY = aTableFrame->GetCellSpacingY();
1054 // get the page height
1055 nscoord pageHeight = aPresContext->GetPageSize().height;
1056 NS_ASSERTION(pageHeight != NS_UNCONSTRAINEDSIZE,
1057 "The table shouldn't be split when there should be space");
1059 bool isTopOfPage = aReflowState.mFlags.mIsTopOfPage;
1060 nsTableRowFrame* firstRowThisPage = GetFirstRow();
1062 // Need to dirty the table's geometry, or else the row might skip
1063 // reflowing its cell as an optimization.
1064 aTableFrame->SetGeometryDirty();
1066 // Walk each of the row frames looking for the first row frame that doesn't fit
1067 // in the available space
1068 for (nsTableRowFrame* rowFrame = firstRowThisPage; rowFrame; rowFrame = rowFrame->GetNextRow()) {
1069 bool rowIsOnPage = true;
1070 nsRect rowRect = rowFrame->GetRect();
1071 // See if the row fits on this page
1072 if (rowRect.YMost() > availHeight) {
1073 nsTableRowFrame* contRow = nullptr;
1074 // Reflow the row in the availabe space and have it split if it is the 1st
1075 // row (on the page) or there is at least 5% of the current page available
1076 // XXX this 5% should be made a preference
1077 if (!prevRowFrame || (availHeight - aDesiredSize.Height() > pageHeight / 20)) {
1078 nsSize availSize(availWidth, std::max(availHeight - rowRect.y, 0));
1079 // don't let the available height exceed what CalculateRowHeights set for it
1080 availSize.height = std::min(availSize.height, rowRect.height);
1082 nsHTMLReflowState rowReflowState(aPresContext, aReflowState,
1083 rowFrame, availSize,
1084 -1, -1,
1085 nsHTMLReflowState::CALLER_WILL_INIT);
1087 InitChildReflowState(*aPresContext, borderCollapse, rowReflowState);
1088 rowReflowState.mFlags.mIsTopOfPage = isTopOfPage; // set top of page
1089 nsHTMLReflowMetrics rowMetrics(aReflowState);
1091 // Get the old size before we reflow.
1092 nsRect oldRowRect = rowFrame->GetRect();
1093 nsRect oldRowVisualOverflow = rowFrame->GetVisualOverflowRect();
1095 // Reflow the cell with the constrained height. A cell with rowspan >1 will get this
1096 // reflow later during SplitSpanningCells.
1097 rv = ReflowChild(rowFrame, aPresContext, rowMetrics, rowReflowState,
1098 0, 0, NS_FRAME_NO_MOVE_FRAME, aStatus);
1099 if (NS_FAILED(rv)) return rv;
1100 rowFrame->SetSize(nsSize(rowMetrics.Width(), rowMetrics.Height()));
1101 rowFrame->DidReflow(aPresContext, nullptr, nsDidReflowStatus::FINISHED);
1102 rowFrame->DidResize();
1104 if (!aRowForcedPageBreak && !NS_FRAME_IS_FULLY_COMPLETE(aStatus) &&
1105 ShouldAvoidBreakInside(aReflowState)) {
1106 aStatus = NS_INLINE_LINE_BREAK_BEFORE();
1107 break;
1108 }
1110 nsTableFrame::InvalidateTableFrame(rowFrame, oldRowRect,
1111 oldRowVisualOverflow,
1112 false);
1114 if (NS_FRAME_IS_NOT_COMPLETE(aStatus)) {
1115 // The row frame is incomplete and all of the rowspan 1 cells' block frames split
1116 if ((rowMetrics.Height() <= rowReflowState.AvailableHeight()) || isTopOfPage) {
1117 // The row stays on this page because either it split ok or we're on the top of page.
1118 // If top of page and the height exceeded the avail height, then there will be data loss
1119 NS_ASSERTION(rowMetrics.Height() <= rowReflowState.AvailableHeight(),
1120 "data loss - incomplete row needed more height than available, on top of page");
1121 CreateContinuingRowFrame(*aPresContext, *rowFrame, (nsIFrame**)&contRow);
1122 if (contRow) {
1123 aDesiredSize.Height() += rowMetrics.Height();
1124 if (prevRowFrame)
1125 aDesiredSize.Height() += cellSpacingY;
1126 }
1127 else return NS_ERROR_NULL_POINTER;
1128 }
1129 else {
1130 // Put the row on the next page to give it more height
1131 rowIsOnPage = false;
1132 }
1133 }
1134 else {
1135 // The row frame is complete because either (1) its minimum height is greater than the
1136 // available height we gave it, or (2) it may have been given a larger height through
1137 // style than its content, or (3) it contains a rowspan >1 cell which hasn't been
1138 // reflowed with a constrained height yet (we will find out when SplitSpanningCells is
1139 // called below)
1140 if (rowMetrics.Height() > availSize.height ||
1141 (NS_INLINE_IS_BREAK_BEFORE(aStatus) && !aRowForcedPageBreak)) {
1142 // cases (1) and (2)
1143 if (isTopOfPage) {
1144 // We're on top of the page, so keep the row on this page. There will be data loss.
1145 // Push the row frame that follows
1146 nsTableRowFrame* nextRowFrame = rowFrame->GetNextRow();
1147 if (nextRowFrame) {
1148 aStatus = NS_FRAME_NOT_COMPLETE;
1149 }
1150 aDesiredSize.Height() += rowMetrics.Height();
1151 if (prevRowFrame)
1152 aDesiredSize.Height() += cellSpacingY;
1153 NS_WARNING("data loss - complete row needed more height than available, on top of page");
1154 }
1155 else {
1156 // We're not on top of the page, so put the row on the next page to give it more height
1157 rowIsOnPage = false;
1158 }
1159 }
1160 }
1161 } //if (!prevRowFrame || (availHeight - aDesiredSize.Height() > pageHeight / 20))
1162 else {
1163 // put the row on the next page to give it more height
1164 rowIsOnPage = false;
1165 }
1167 nsTableRowFrame* lastRowThisPage = rowFrame;
1168 nscoord spanningRowBottom = availHeight;
1169 if (!rowIsOnPage) {
1170 NS_ASSERTION(!contRow, "We should not have created a continuation if none of this row fits");
1171 if (!aRowForcedPageBreak && ShouldAvoidBreakInside(aReflowState)) {
1172 aStatus = NS_INLINE_LINE_BREAK_BEFORE();
1173 break;
1174 }
1175 if (prevRowFrame) {
1176 spanningRowBottom = prevRowFrame->GetRect().YMost();
1177 lastRowThisPage = prevRowFrame;
1178 isTopOfPage = (lastRowThisPage == firstRowThisPage) && aReflowState.mFlags.mIsTopOfPage;
1179 aStatus = NS_FRAME_NOT_COMPLETE;
1180 }
1181 else {
1182 // We can't push children, so let our parent reflow us again with more space
1183 aDesiredSize.Height() = rowRect.YMost();
1184 aStatus = NS_FRAME_COMPLETE;
1185 break;
1186 }
1187 }
1188 // reflow the cells with rowspan >1 that occur on the page
1190 nsTableRowFrame* firstTruncatedRow;
1191 nscoord yMost;
1192 SplitSpanningCells(*aPresContext, aReflowState, *aTableFrame, *firstRowThisPage,
1193 *lastRowThisPage, aReflowState.mFlags.mIsTopOfPage, spanningRowBottom, contRow,
1194 firstTruncatedRow, yMost);
1195 if (firstTruncatedRow) {
1196 // A rowspan >1 cell did not fit (and could not split) in the space we gave it
1197 if (firstTruncatedRow == firstRowThisPage) {
1198 if (aReflowState.mFlags.mIsTopOfPage) {
1199 NS_WARNING("data loss in a row spanned cell");
1200 }
1201 else {
1202 // We can't push children, so let our parent reflow us again with more space
1203 aDesiredSize.Height() = rowRect.YMost();
1204 aStatus = NS_FRAME_COMPLETE;
1205 UndoContinuedRow(aPresContext, contRow);
1206 contRow = nullptr;
1207 }
1208 }
1209 else { // (firstTruncatedRow != firstRowThisPage)
1210 // Try to put firstTruncateRow on the next page
1211 nsTableRowFrame* rowBefore = ::GetRowBefore(*firstRowThisPage, *firstTruncatedRow);
1212 nscoord oldSpanningRowBottom = spanningRowBottom;
1213 spanningRowBottom = rowBefore->GetRect().YMost();
1215 UndoContinuedRow(aPresContext, contRow);
1216 contRow = nullptr;
1217 nsTableRowFrame* oldLastRowThisPage = lastRowThisPage;
1218 lastRowThisPage = rowBefore;
1219 aStatus = NS_FRAME_NOT_COMPLETE;
1221 // Call SplitSpanningCells again with rowBefore as the last row on the page
1222 SplitSpanningCells(*aPresContext, aReflowState, *aTableFrame,
1223 *firstRowThisPage, *rowBefore, aReflowState.mFlags.mIsTopOfPage,
1224 spanningRowBottom, contRow, firstTruncatedRow, aDesiredSize.Height());
1225 if (firstTruncatedRow) {
1226 if (aReflowState.mFlags.mIsTopOfPage) {
1227 // We were better off with the 1st call to SplitSpanningCells, do it again
1228 UndoContinuedRow(aPresContext, contRow);
1229 contRow = nullptr;
1230 lastRowThisPage = oldLastRowThisPage;
1231 spanningRowBottom = oldSpanningRowBottom;
1232 SplitSpanningCells(*aPresContext, aReflowState, *aTableFrame, *firstRowThisPage,
1233 *lastRowThisPage, aReflowState.mFlags.mIsTopOfPage, spanningRowBottom, contRow,
1234 firstTruncatedRow, aDesiredSize.Height());
1235 NS_WARNING("data loss in a row spanned cell");
1236 }
1237 else {
1238 // Let our parent reflow us again with more space
1239 aDesiredSize.Height() = rowRect.YMost();
1240 aStatus = NS_FRAME_COMPLETE;
1241 UndoContinuedRow(aPresContext, contRow);
1242 contRow = nullptr;
1243 }
1244 }
1245 } // if (firstTruncatedRow == firstRowThisPage)
1246 } // if (firstTruncatedRow)
1247 else {
1248 aDesiredSize.Height() = std::max(aDesiredSize.Height(), yMost);
1249 if (contRow) {
1250 aStatus = NS_FRAME_NOT_COMPLETE;
1251 }
1252 }
1253 if (NS_FRAME_IS_NOT_COMPLETE(aStatus) && !contRow) {
1254 nsTableRowFrame* nextRow = lastRowThisPage->GetNextRow();
1255 if (nextRow) {
1256 PushChildren(nextRow, lastRowThisPage);
1257 }
1258 }
1259 break;
1260 } // if (rowRect.YMost() > availHeight)
1261 else {
1262 aDesiredSize.Height() = rowRect.YMost();
1263 prevRowFrame = rowFrame;
1264 // see if there is a page break after the row
1265 nsTableRowFrame* nextRow = rowFrame->GetNextRow();
1266 if (nextRow && nsTableFrame::PageBreakAfter(rowFrame, nextRow)) {
1267 PushChildren(nextRow, rowFrame);
1268 aStatus = NS_FRAME_NOT_COMPLETE;
1269 break;
1270 }
1271 }
1272 // after the 1st row that has a height, we can't be on top
1273 // of the page anymore.
1274 isTopOfPage = isTopOfPage && rowRect.YMost() == 0;
1275 }
1276 return NS_OK;
1277 }
1279 /** Layout the entire row group.
1280 * This method stacks rows vertically according to HTML 4.0 rules.
1281 * Rows are responsible for layout of their children.
1282 */
1283 nsresult
1284 nsTableRowGroupFrame::Reflow(nsPresContext* aPresContext,
1285 nsHTMLReflowMetrics& aDesiredSize,
1286 const nsHTMLReflowState& aReflowState,
1287 nsReflowStatus& aStatus)
1288 {
1289 DO_GLOBAL_REFLOW_COUNT("nsTableRowGroupFrame");
1290 DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
1292 nsresult rv = NS_OK;
1293 aStatus = NS_FRAME_COMPLETE;
1295 // Row geometry may be going to change so we need to invalidate any row cursor.
1296 ClearRowCursor();
1298 // see if a special height reflow needs to occur due to having a pct height
1299 nsTableFrame::CheckRequestSpecialHeightReflow(aReflowState);
1301 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
1302 nsRowGroupReflowState state(aReflowState, tableFrame);
1303 const nsStyleVisibility* groupVis = StyleVisibility();
1304 bool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE == groupVis->mVisible);
1305 if (collapseGroup) {
1306 tableFrame->SetNeedToCollapse(true);
1307 }
1309 // Check for an overflow list
1310 MoveOverflowToChildList();
1312 // Reflow the existing frames.
1313 bool splitDueToPageBreak = false;
1314 rv = ReflowChildren(aPresContext, aDesiredSize, state, aStatus,
1315 &splitDueToPageBreak);
1317 // See if all the frames fit. Do not try to split anything if we're
1318 // not paginated ... we can't split across columns yet.
1319 if (aReflowState.mFlags.mTableIsSplittable &&
1320 NS_UNCONSTRAINEDSIZE != aReflowState.AvailableHeight() &&
1321 (NS_FRAME_NOT_COMPLETE == aStatus || splitDueToPageBreak ||
1322 aDesiredSize.Height() > aReflowState.AvailableHeight())) {
1323 // Nope, find a place to split the row group
1324 bool specialReflow = (bool)aReflowState.mFlags.mSpecialHeightReflow;
1325 ((nsHTMLReflowState::ReflowStateFlags&)aReflowState.mFlags).mSpecialHeightReflow = false;
1327 SplitRowGroup(aPresContext, aDesiredSize, aReflowState, tableFrame, aStatus,
1328 splitDueToPageBreak);
1330 ((nsHTMLReflowState::ReflowStateFlags&)aReflowState.mFlags).mSpecialHeightReflow = specialReflow;
1331 }
1333 // XXXmats The following is just bogus. We leave it here for now because
1334 // ReflowChildren should pull up rows from our next-in-flow before returning
1335 // a Complete status, but doesn't (bug 804888).
1336 if (GetNextInFlow() && GetNextInFlow()->GetFirstPrincipalChild()) {
1337 NS_FRAME_SET_INCOMPLETE(aStatus);
1338 }
1340 SetHasStyleHeight((NS_UNCONSTRAINEDSIZE != aReflowState.ComputedHeight()) &&
1341 (aReflowState.ComputedHeight() > 0));
1343 // just set our width to what was available. The table will calculate the width and not use our value.
1344 aDesiredSize.Width() = aReflowState.AvailableWidth();
1346 aDesiredSize.UnionOverflowAreasWithDesiredBounds();
1348 // If our parent is in initial reflow, it'll handle invalidating our
1349 // entire overflow rect.
1350 if (!(GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW) &&
1351 nsSize(aDesiredSize.Width(), aDesiredSize.Height()) != mRect.Size()) {
1352 InvalidateFrame();
1353 }
1355 FinishAndStoreOverflow(&aDesiredSize);
1356 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
1357 return rv;
1358 }
1360 bool
1361 nsTableRowGroupFrame::UpdateOverflow()
1362 {
1363 // Row cursor invariants depend on the visual overflow area of the rows,
1364 // which may have changed, so we need to clear the cursor now.
1365 ClearRowCursor();
1366 return nsContainerFrame::UpdateOverflow();
1367 }
1369 /* virtual */ void
1370 nsTableRowGroupFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
1371 {
1372 nsContainerFrame::DidSetStyleContext(aOldStyleContext);
1374 if (!aOldStyleContext) //avoid this on init
1375 return;
1377 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
1378 if (tableFrame->IsBorderCollapse() &&
1379 tableFrame->BCRecalcNeeded(aOldStyleContext, StyleContext())) {
1380 nsIntRect damageArea(0, GetStartRowIndex(), tableFrame->GetColCount(),
1381 GetRowCount());
1382 tableFrame->AddBCDamageArea(damageArea);
1383 }
1384 }
1386 nsresult
1387 nsTableRowGroupFrame::AppendFrames(ChildListID aListID,
1388 nsFrameList& aFrameList)
1389 {
1390 NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
1392 ClearRowCursor();
1394 // collect the new row frames in an array
1395 // XXXbz why are we doing the QI stuff? There shouldn't be any non-rows here.
1396 nsAutoTArray<nsTableRowFrame*, 8> rows;
1397 for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) {
1398 nsTableRowFrame *rowFrame = do_QueryFrame(e.get());
1399 NS_ASSERTION(rowFrame, "Unexpected frame; frame constructor screwed up");
1400 if (rowFrame) {
1401 NS_ASSERTION(NS_STYLE_DISPLAY_TABLE_ROW ==
1402 e.get()->StyleDisplay()->mDisplay,
1403 "wrong display type on rowframe");
1404 rows.AppendElement(rowFrame);
1405 }
1406 }
1408 int32_t rowIndex = GetRowCount();
1409 // Append the frames to the sibling chain
1410 mFrames.AppendFrames(nullptr, aFrameList);
1412 if (rows.Length() > 0) {
1413 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
1414 tableFrame->AppendRows(this, rowIndex, rows);
1415 PresContext()->PresShell()->
1416 FrameNeedsReflow(this, nsIPresShell::eTreeChange,
1417 NS_FRAME_HAS_DIRTY_CHILDREN);
1418 tableFrame->SetGeometryDirty();
1419 }
1421 return NS_OK;
1422 }
1424 nsresult
1425 nsTableRowGroupFrame::InsertFrames(ChildListID aListID,
1426 nsIFrame* aPrevFrame,
1427 nsFrameList& aFrameList)
1428 {
1429 NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
1430 NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
1431 "inserting after sibling frame with different parent");
1433 ClearRowCursor();
1435 // collect the new row frames in an array
1436 // XXXbz why are we doing the QI stuff? There shouldn't be any non-rows here.
1437 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
1438 nsTArray<nsTableRowFrame*> rows;
1439 bool gotFirstRow = false;
1440 for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) {
1441 nsTableRowFrame *rowFrame = do_QueryFrame(e.get());
1442 NS_ASSERTION(rowFrame, "Unexpected frame; frame constructor screwed up");
1443 if (rowFrame) {
1444 NS_ASSERTION(NS_STYLE_DISPLAY_TABLE_ROW ==
1445 e.get()->StyleDisplay()->mDisplay,
1446 "wrong display type on rowframe");
1447 rows.AppendElement(rowFrame);
1448 if (!gotFirstRow) {
1449 rowFrame->SetFirstInserted(true);
1450 gotFirstRow = true;
1451 tableFrame->SetRowInserted(true);
1452 }
1453 }
1454 }
1456 int32_t startRowIndex = GetStartRowIndex();
1457 // Insert the frames in the sibling chain
1458 mFrames.InsertFrames(nullptr, aPrevFrame, aFrameList);
1460 int32_t numRows = rows.Length();
1461 if (numRows > 0) {
1462 nsTableRowFrame* prevRow = (nsTableRowFrame *)nsTableFrame::GetFrameAtOrBefore(this, aPrevFrame, nsGkAtoms::tableRowFrame);
1463 int32_t rowIndex = (prevRow) ? prevRow->GetRowIndex() + 1 : startRowIndex;
1464 tableFrame->InsertRows(this, rows, rowIndex, true);
1466 PresContext()->PresShell()->
1467 FrameNeedsReflow(this, nsIPresShell::eTreeChange,
1468 NS_FRAME_HAS_DIRTY_CHILDREN);
1469 tableFrame->SetGeometryDirty();
1470 }
1471 return NS_OK;
1472 }
1474 nsresult
1475 nsTableRowGroupFrame::RemoveFrame(ChildListID aListID,
1476 nsIFrame* aOldFrame)
1477 {
1478 NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
1480 ClearRowCursor();
1482 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
1483 // XXX why are we doing the QI stuff? There shouldn't be any non-rows here.
1484 nsTableRowFrame* rowFrame = do_QueryFrame(aOldFrame);
1485 if (rowFrame) {
1486 // remove the rows from the table (and flag a rebalance)
1487 tableFrame->RemoveRows(*rowFrame, 1, true);
1489 PresContext()->PresShell()->
1490 FrameNeedsReflow(this, nsIPresShell::eTreeChange,
1491 NS_FRAME_HAS_DIRTY_CHILDREN);
1492 tableFrame->SetGeometryDirty();
1493 }
1494 mFrames.DestroyFrame(aOldFrame);
1496 return NS_OK;
1497 }
1499 /* virtual */ nsMargin
1500 nsTableRowGroupFrame::GetUsedMargin() const
1501 {
1502 return nsMargin(0,0,0,0);
1503 }
1505 /* virtual */ nsMargin
1506 nsTableRowGroupFrame::GetUsedBorder() const
1507 {
1508 return nsMargin(0,0,0,0);
1509 }
1511 /* virtual */ nsMargin
1512 nsTableRowGroupFrame::GetUsedPadding() const
1513 {
1514 return nsMargin(0,0,0,0);
1515 }
1517 nscoord
1518 nsTableRowGroupFrame::GetHeightBasis(const nsHTMLReflowState& aReflowState)
1519 {
1520 nscoord result = 0;
1521 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
1522 if ((aReflowState.ComputedHeight() > 0) && (aReflowState.ComputedHeight() < NS_UNCONSTRAINEDSIZE)) {
1523 nscoord cellSpacing = std::max(0, GetRowCount() - 1) * tableFrame->GetCellSpacingY();
1524 result = aReflowState.ComputedHeight() - cellSpacing;
1525 }
1526 else {
1527 const nsHTMLReflowState* parentRS = aReflowState.parentReflowState;
1528 if (parentRS && (tableFrame != parentRS->frame)) {
1529 parentRS = parentRS->parentReflowState;
1530 }
1531 if (parentRS && (tableFrame == parentRS->frame) &&
1532 (parentRS->ComputedHeight() > 0) && (parentRS->ComputedHeight() < NS_UNCONSTRAINEDSIZE)) {
1533 nscoord cellSpacing = std::max(0, tableFrame->GetRowCount() + 1) * tableFrame->GetCellSpacingY();
1534 result = parentRS->ComputedHeight() - cellSpacing;
1535 }
1536 }
1538 return result;
1539 }
1541 bool
1542 nsTableRowGroupFrame::IsSimpleRowFrame(nsTableFrame* aTableFrame,
1543 nsIFrame* aFrame)
1544 {
1545 // Make sure it's a row frame and not a row group frame
1546 nsTableRowFrame *rowFrame = do_QueryFrame(aFrame);
1547 if (rowFrame) {
1548 int32_t rowIndex = rowFrame->GetRowIndex();
1550 // It's a simple row frame if there are no cells that span into or
1551 // across the row
1552 int32_t numEffCols = aTableFrame->GetEffectiveColCount();
1553 if (!aTableFrame->RowIsSpannedInto(rowIndex, numEffCols) &&
1554 !aTableFrame->RowHasSpanningCells(rowIndex, numEffCols)) {
1555 return true;
1556 }
1557 }
1559 return false;
1560 }
1562 nsIAtom*
1563 nsTableRowGroupFrame::GetType() const
1564 {
1565 return nsGkAtoms::tableRowGroupFrame;
1566 }
1568 /** find page break before the first row **/
1569 bool
1570 nsTableRowGroupFrame::HasInternalBreakBefore() const
1571 {
1572 nsIFrame* firstChild = mFrames.FirstChild();
1573 if (!firstChild)
1574 return false;
1575 return firstChild->StyleDisplay()->mBreakBefore;
1576 }
1578 /** find page break after the last row **/
1579 bool
1580 nsTableRowGroupFrame::HasInternalBreakAfter() const
1581 {
1582 nsIFrame* lastChild = mFrames.LastChild();
1583 if (!lastChild)
1584 return false;
1585 return lastChild->StyleDisplay()->mBreakAfter;
1586 }
1587 /* ----- global methods ----- */
1589 nsIFrame*
1590 NS_NewTableRowGroupFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
1591 {
1592 return new (aPresShell) nsTableRowGroupFrame(aContext);
1593 }
1595 NS_IMPL_FRAMEARENA_HELPERS(nsTableRowGroupFrame)
1597 #ifdef DEBUG_FRAME_DUMP
1598 nsresult
1599 nsTableRowGroupFrame::GetFrameName(nsAString& aResult) const
1600 {
1601 return MakeFrameName(NS_LITERAL_STRING("TableRowGroup"), aResult);
1602 }
1603 #endif
1605 nsMargin*
1606 nsTableRowGroupFrame::GetBCBorderWidth(nsMargin& aBorder)
1607 {
1608 aBorder.left = aBorder.right = aBorder.top = aBorder.bottom = 0;
1610 nsTableRowFrame* firstRowFrame = nullptr;
1611 nsTableRowFrame* lastRowFrame = nullptr;
1612 for (nsTableRowFrame* rowFrame = GetFirstRow(); rowFrame; rowFrame = rowFrame->GetNextRow()) {
1613 if (!firstRowFrame) {
1614 firstRowFrame = rowFrame;
1615 }
1616 lastRowFrame = rowFrame;
1617 }
1618 if (firstRowFrame) {
1619 aBorder.top = nsPresContext::CSSPixelsToAppUnits(firstRowFrame->GetTopBCBorderWidth());
1620 aBorder.bottom = nsPresContext::CSSPixelsToAppUnits(lastRowFrame->GetBottomBCBorderWidth());
1621 }
1623 return &aBorder;
1624 }
1626 void nsTableRowGroupFrame::SetContinuousBCBorderWidth(uint8_t aForSide,
1627 BCPixelSize aPixelValue)
1628 {
1629 switch (aForSide) {
1630 case NS_SIDE_RIGHT:
1631 mRightContBorderWidth = aPixelValue;
1632 return;
1633 case NS_SIDE_BOTTOM:
1634 mBottomContBorderWidth = aPixelValue;
1635 return;
1636 case NS_SIDE_LEFT:
1637 mLeftContBorderWidth = aPixelValue;
1638 return;
1639 default:
1640 NS_ERROR("invalid NS_SIDE argument");
1641 }
1642 }
1644 //nsILineIterator methods
1645 int32_t
1646 nsTableRowGroupFrame::GetNumLines()
1647 {
1648 return GetRowCount();
1649 }
1651 bool
1652 nsTableRowGroupFrame::GetDirection()
1653 {
1654 nsTableFrame* table = nsTableFrame::GetTableFrame(this);
1655 return (NS_STYLE_DIRECTION_RTL ==
1656 table->StyleVisibility()->mDirection);
1657 }
1659 NS_IMETHODIMP
1660 nsTableRowGroupFrame::GetLine(int32_t aLineNumber,
1661 nsIFrame** aFirstFrameOnLine,
1662 int32_t* aNumFramesOnLine,
1663 nsRect& aLineBounds,
1664 uint32_t* aLineFlags)
1665 {
1666 NS_ENSURE_ARG_POINTER(aFirstFrameOnLine);
1667 NS_ENSURE_ARG_POINTER(aNumFramesOnLine);
1668 NS_ENSURE_ARG_POINTER(aLineFlags);
1670 nsTableFrame* table = nsTableFrame::GetTableFrame(this);
1671 nsTableCellMap* cellMap = table->GetCellMap();
1673 *aLineFlags = 0;
1674 *aFirstFrameOnLine = nullptr;
1675 *aNumFramesOnLine = 0;
1676 aLineBounds.SetRect(0, 0, 0, 0);
1678 if ((aLineNumber < 0) || (aLineNumber >= GetRowCount())) {
1679 return NS_OK;
1680 }
1681 aLineNumber += GetStartRowIndex();
1683 *aNumFramesOnLine = cellMap->GetNumCellsOriginatingInRow(aLineNumber);
1684 if (*aNumFramesOnLine == 0) {
1685 return NS_OK;
1686 }
1687 int32_t colCount = table->GetColCount();
1688 for (int32_t i = 0; i < colCount; i++) {
1689 CellData* data = cellMap->GetDataAt(aLineNumber, i);
1690 if (data && data->IsOrig()) {
1691 *aFirstFrameOnLine = (nsIFrame*)data->GetCellFrame();
1692 nsIFrame* parent = (*aFirstFrameOnLine)->GetParent();
1693 aLineBounds = parent->GetRect();
1694 return NS_OK;
1695 }
1696 }
1697 NS_ERROR("cellmap is lying");
1698 return NS_ERROR_FAILURE;
1699 }
1701 int32_t
1702 nsTableRowGroupFrame::FindLineContaining(nsIFrame* aFrame, int32_t aStartLine)
1703 {
1704 NS_ENSURE_TRUE(aFrame, -1);
1706 nsTableRowFrame *rowFrame = do_QueryFrame(aFrame);
1707 NS_ASSERTION(rowFrame, "RowGroup contains a frame that is not a row");
1709 int32_t rowIndexInGroup = rowFrame->GetRowIndex() - GetStartRowIndex();
1711 return rowIndexInGroup >= aStartLine ? rowIndexInGroup : -1;
1712 }
1714 NS_IMETHODIMP
1715 nsTableRowGroupFrame::CheckLineOrder(int32_t aLine,
1716 bool *aIsReordered,
1717 nsIFrame **aFirstVisual,
1718 nsIFrame **aLastVisual)
1719 {
1720 *aIsReordered = false;
1721 *aFirstVisual = nullptr;
1722 *aLastVisual = nullptr;
1723 return NS_OK;
1724 }
1726 NS_IMETHODIMP
1727 nsTableRowGroupFrame::FindFrameAt(int32_t aLineNumber,
1728 nscoord aX,
1729 nsIFrame** aFrameFound,
1730 bool* aXIsBeforeFirstFrame,
1731 bool* aXIsAfterLastFrame)
1732 {
1733 nsTableFrame* table = nsTableFrame::GetTableFrame(this);
1734 nsTableCellMap* cellMap = table->GetCellMap();
1736 *aFrameFound = nullptr;
1737 *aXIsBeforeFirstFrame = true;
1738 *aXIsAfterLastFrame = false;
1740 aLineNumber += GetStartRowIndex();
1741 int32_t numCells = cellMap->GetNumCellsOriginatingInRow(aLineNumber);
1742 if (numCells == 0) {
1743 return NS_OK;
1744 }
1746 nsIFrame* frame = nullptr;
1747 int32_t colCount = table->GetColCount();
1748 for (int32_t i = 0; i < colCount; i++) {
1749 CellData* data = cellMap->GetDataAt(aLineNumber, i);
1750 if (data && data->IsOrig()) {
1751 frame = (nsIFrame*)data->GetCellFrame();
1752 break;
1753 }
1754 }
1755 NS_ASSERTION(frame, "cellmap is lying");
1756 bool isRTL = (NS_STYLE_DIRECTION_RTL ==
1757 table->StyleVisibility()->mDirection);
1759 nsIFrame* closestFromLeft = nullptr;
1760 nsIFrame* closestFromRight = nullptr;
1761 int32_t 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;
1770 }
1771 if (rect.x < aX) {
1772 if (!closestFromLeft ||
1773 rect.XMost() > closestFromLeft->GetRect().XMost())
1774 closestFromLeft = frame;
1775 }
1776 else {
1777 if (!closestFromRight ||
1778 rect.x < closestFromRight->GetRect().x)
1779 closestFromRight = frame;
1780 }
1781 }
1782 frame = frame->GetNextSibling();
1783 }
1784 if (!closestFromLeft && !closestFromRight) {
1785 // All frames were zero-width. Just take the first one.
1786 closestFromLeft = closestFromRight = firstFrame;
1787 }
1788 *aXIsBeforeFirstFrame = isRTL ? !closestFromRight : !closestFromLeft;
1789 *aXIsAfterLastFrame = isRTL ? !closestFromLeft : !closestFromRight;
1790 if (closestFromLeft == closestFromRight) {
1791 *aFrameFound = closestFromLeft;
1792 }
1793 else if (!closestFromLeft) {
1794 *aFrameFound = closestFromRight;
1795 }
1796 else if (!closestFromRight) {
1797 *aFrameFound = closestFromLeft;
1798 }
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;
1806 }
1807 return NS_OK;
1808 }
1810 NS_IMETHODIMP
1811 nsTableRowGroupFrame::GetNextSiblingOnLine(nsIFrame*& aFrame,
1812 int32_t aLineNumber)
1813 {
1814 NS_ENSURE_ARG_POINTER(aFrame);
1815 aFrame = aFrame->GetNextSibling();
1816 return NS_OK;
1817 }
1819 //end nsLineIterator methods
1821 static void
1822 DestroyFrameCursorData(void* aPropertyValue)
1823 {
1824 delete static_cast<nsTableRowGroupFrame::FrameCursorData*>(aPropertyValue);
1825 }
1827 NS_DECLARE_FRAME_PROPERTY(RowCursorProperty, DestroyFrameCursorData)
1829 void
1830 nsTableRowGroupFrame::ClearRowCursor()
1831 {
1832 if (!(GetStateBits() & NS_ROWGROUP_HAS_ROW_CURSOR))
1833 return;
1835 RemoveStateBits(NS_ROWGROUP_HAS_ROW_CURSOR);
1836 Properties().Delete(RowCursorProperty());
1837 }
1839 nsTableRowGroupFrame::FrameCursorData*
1840 nsTableRowGroupFrame::SetupRowCursor()
1841 {
1842 if (GetStateBits() & NS_ROWGROUP_HAS_ROW_CURSOR) {
1843 // We already have a valid row cursor. Don't waste time rebuilding it.
1844 return nullptr;
1845 }
1847 nsIFrame* f = mFrames.FirstChild();
1848 int32_t count;
1849 for (count = 0; f && count < MIN_ROWS_NEEDING_CURSOR; ++count) {
1850 f = f->GetNextSibling();
1851 }
1852 if (!f) {
1853 // Less than MIN_ROWS_NEEDING_CURSOR rows, so just don't bother
1854 return nullptr;
1855 }
1857 FrameCursorData* data = new FrameCursorData();
1858 if (!data)
1859 return nullptr;
1860 Properties().Set(RowCursorProperty(), data);
1861 AddStateBits(NS_ROWGROUP_HAS_ROW_CURSOR);
1862 return data;
1863 }
1865 nsIFrame*
1866 nsTableRowGroupFrame::GetFirstRowContaining(nscoord aY, nscoord* aOverflowAbove)
1867 {
1868 if (!(GetStateBits() & NS_ROWGROUP_HAS_ROW_CURSOR))
1869 return nullptr;
1871 FrameCursorData* property = static_cast<FrameCursorData*>
1872 (Properties().Get(RowCursorProperty()));
1873 uint32_t cursorIndex = property->mCursorIndex;
1874 uint32_t frameCount = property->mFrames.Length();
1875 if (cursorIndex >= frameCount)
1876 return nullptr;
1877 nsIFrame* cursorFrame = property->mFrames[cursorIndex];
1879 // The cursor's frame list excludes frames with empty overflow-area, so
1880 // we don't need to check that here.
1882 // We use property->mOverflowBelow here instead of computing the frame's
1883 // true overflowArea.YMost(), because it is essential for the thresholds
1884 // to form a monotonically increasing sequence. Otherwise we would break
1885 // encountering a row whose overflowArea.YMost() is <= aY but which has
1886 // a row above it containing cell(s) that span to include aY.
1887 while (cursorIndex > 0 &&
1888 cursorFrame->GetRect().YMost() + property->mOverflowBelow > aY) {
1889 --cursorIndex;
1890 cursorFrame = property->mFrames[cursorIndex];
1891 }
1892 while (cursorIndex + 1 < frameCount &&
1893 cursorFrame->GetRect().YMost() + property->mOverflowBelow <= aY) {
1894 ++cursorIndex;
1895 cursorFrame = property->mFrames[cursorIndex];
1896 }
1898 property->mCursorIndex = cursorIndex;
1899 *aOverflowAbove = property->mOverflowAbove;
1900 return cursorFrame;
1901 }
1903 bool
1904 nsTableRowGroupFrame::FrameCursorData::AppendFrame(nsIFrame* aFrame)
1905 {
1906 nsRect overflowRect = aFrame->GetVisualOverflowRect();
1907 if (overflowRect.IsEmpty())
1908 return true;
1909 nscoord overflowAbove = -overflowRect.y;
1910 nscoord overflowBelow = overflowRect.YMost() - aFrame->GetSize().height;
1911 mOverflowAbove = std::max(mOverflowAbove, overflowAbove);
1912 mOverflowBelow = std::max(mOverflowBelow, overflowBelow);
1913 return mFrames.AppendElement(aFrame) != nullptr;
1914 }
1916 void
1917 nsTableRowGroupFrame::InvalidateFrame(uint32_t aDisplayItemKey)
1918 {
1919 nsIFrame::InvalidateFrame(aDisplayItemKey);
1920 GetParent()->InvalidateFrameWithRect(GetVisualOverflowRect() + GetPosition(), aDisplayItemKey);
1921 }
1923 void
1924 nsTableRowGroupFrame::InvalidateFrameWithRect(const nsRect& aRect, uint32_t aDisplayItemKey)
1925 {
1926 nsIFrame::InvalidateFrameWithRect(aRect, aDisplayItemKey);
1927 // If we have filters applied that would affects our bounds, then
1928 // we get an inactive layer created and this is computed
1929 // within FrameLayerBuilder
1930 GetParent()->InvalidateFrameWithRect(aRect + GetPosition(), aDisplayItemKey);
1931 }