// the computed bsize for the cell, which descendants use for percent bsize calculations // it is the bsize (minus border, padding) of the cell's first in flow during its final // reflow without an unconstrained bsize. static nscoord CalcUnpaginatedBSize(nsPresContext* aPresContext, nsTableCellFrame& aCellFrame, nsTableFrame& aTableFrame, nscoord aBlockDirBorderPadding) { const nsTableCellFrame* firstCellInFlow = static_cast<nsTableCellFrame*>(aCellFrame.FirstInFlow()); nsTableFrame* firstTableInFlow = static_cast<nsTableFrame*>(aTableFrame.FirstInFlow()); nsTableRowFrame* row = static_cast<nsTableRowFrame*>(firstCellInFlow->GetParent()); nsTableRowGroupFrame* firstRGInFlow = static_cast<nsTableRowGroupFrame*>(row->GetParent()); int32_t rowIndex; firstCellInFlow->GetRowIndex(rowIndex); int32_t rowSpan = aTableFrame.GetEffectiveRowSpan(*firstCellInFlow); nscoord computedBSize = firstTableInFlow->GetRowSpacing(rowIndex, rowIndex + rowSpan - 1); computedBSize -= aBlockDirBorderPadding; int32_t rowX; for (row = firstRGInFlow->GetFirstRow(), rowX = 0; row; row = row->GetNextRow(), rowX++) { if (rowX > rowIndex + rowSpan - 1) { break; } else if (rowX >= rowIndex) { computedBSize += row->GetUnpaginatedBSize(aPresContext); } } return computedBSize; }
// the computed height for the cell, which descendants use for percent height calculations // it is the height (minus border, padding) of the cell's first in flow during its final // reflow without an unconstrained height. static nscoord CalcUnpaginagedHeight(nsPresContext* aPresContext, nsTableCellFrame& aCellFrame, nsTableFrame& aTableFrame, nscoord aVerticalBorderPadding) { const nsTableCellFrame* firstCellInFlow = (nsTableCellFrame*)aCellFrame.GetFirstInFlow(); nsTableFrame* firstTableInFlow = (nsTableFrame*)aTableFrame.GetFirstInFlow(); nsTableRowFrame* row = static_cast<nsTableRowFrame*>(firstCellInFlow->GetParent()); nsTableRowGroupFrame* firstRGInFlow = static_cast<nsTableRowGroupFrame*>(row->GetParent()); int32_t rowIndex; firstCellInFlow->GetRowIndex(rowIndex); int32_t rowSpan = aTableFrame.GetEffectiveRowSpan(*firstCellInFlow); nscoord cellSpacing = firstTableInFlow->GetCellSpacingX(); nscoord computedHeight = ((rowSpan - 1) * cellSpacing) - aVerticalBorderPadding; int32_t rowX; for (row = firstRGInFlow->GetFirstRow(), rowX = 0; row; row = row->GetNextRow(), rowX++) { if (rowX > rowIndex + rowSpan - 1) { break; } else if (rowX >= rowIndex) { computedHeight += row->GetUnpaginatedHeight(aPresContext); } } return computedHeight; }
nscoord GetSpaceBetween(PRInt32 aPrevColIndex, PRInt32 aColIndex, PRInt32 aColSpan, nsTableFrame& aTableFrame, nscoord aCellSpacingX, bool aIsLeftToRight, bool aCheckVisibility) { nscoord space = 0; PRInt32 colX; if (aIsLeftToRight) { for (colX = aPrevColIndex + 1; aColIndex > colX; colX++) { bool isCollapsed = false; if (!aCheckVisibility) { space += aTableFrame.GetColumnWidth(colX); } else { nsTableColFrame* colFrame = aTableFrame.GetColFrame(colX); const nsStyleVisibility* colVis = colFrame->GetStyleVisibility(); bool collapseCol = (NS_STYLE_VISIBILITY_COLLAPSE == colVis->mVisible); nsIFrame* cgFrame = colFrame->GetParent(); const nsStyleVisibility* groupVis = cgFrame->GetStyleVisibility(); bool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE == groupVis->mVisible); isCollapsed = collapseCol || collapseGroup; if (!isCollapsed) space += aTableFrame.GetColumnWidth(colX); } if (!isCollapsed && aTableFrame.ColumnHasCellSpacingBefore(colX)) { space += aCellSpacingX; } } } else { PRInt32 lastCol = aColIndex + aColSpan - 1; for (colX = aPrevColIndex - 1; colX > lastCol; colX--) { bool isCollapsed = false; if (!aCheckVisibility) { space += aTableFrame.GetColumnWidth(colX); } else { nsTableColFrame* colFrame = aTableFrame.GetColFrame(colX); const nsStyleVisibility* colVis = colFrame->GetStyleVisibility(); bool collapseCol = (NS_STYLE_VISIBILITY_COLLAPSE == colVis->mVisible); nsIFrame* cgFrame = colFrame->GetParent(); const nsStyleVisibility* groupVis = cgFrame->GetStyleVisibility(); bool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE == groupVis->mVisible); isCollapsed = collapseCol || collapseGroup; if (!isCollapsed) space += aTableFrame.GetColumnWidth(colX); } if (!isCollapsed && aTableFrame.ColumnHasCellSpacingBefore(colX)) { space += aCellSpacingX; } } } return space; }
nscoord GetHeightOfRowsSpannedBelowFirst(nsTableCellFrame& aTableCellFrame, nsTableFrame& aTableFrame) { nscoord height = 0; nscoord cellSpacingY = aTableFrame.GetCellSpacingY(); PRInt32 rowSpan = aTableFrame.GetEffectiveRowSpan(aTableCellFrame); // add in height of rows spanned beyond the 1st one nsIFrame* nextRow = aTableCellFrame.GetParent()->GetNextSibling(); for (PRInt32 rowX = 1; ((rowX < rowSpan) && nextRow);) { if (nsGkAtoms::tableRowFrame == nextRow->GetType()) { height += nextRow->GetSize().height; rowX++; } height += cellSpacingY; nextRow = nextRow->GetNextSibling(); } return height; }
// Calculates the available width for the table cell based on the known // column widths taking into account column spans and column spacing static nscoord CalcAvailWidth(nsTableFrame& aTableFrame, nsTableCellFrame& aCellFrame, nscoord aCellSpacingX) { nscoord cellAvailWidth = 0; PRInt32 colIndex; aCellFrame.GetColIndex(colIndex); PRInt32 colspan = aTableFrame.GetEffectiveColSpan(aCellFrame); NS_ASSERTION(colspan > 0, "effective colspan should be positive"); for (PRInt32 spanX = 0; spanX < colspan; spanX++) { cellAvailWidth += aTableFrame.GetColumnWidth(colIndex + spanX); if (spanX > 0 && aTableFrame.ColumnHasCellSpacingBefore(colIndex + spanX)) { cellAvailWidth += aCellSpacingX; } } return cellAvailWidth; }
nsresult nsTableRowFrame::ReflowChildren(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsTableFrame& aTableFrame, nsReflowStatus& aStatus) { aStatus = NS_FRAME_COMPLETE; // XXXldb Should we be checking constrained height instead? const bool isPaginated = aPresContext->IsPaginated(); const bool borderCollapse = aTableFrame.IsBorderCollapse(); nsresult rv = NS_OK; nscoord cellSpacingX = aTableFrame.GetCellSpacingX(); PRInt32 cellColSpan = 1; // must be defined here so it's set properly for non-cell kids nsTableIterator iter(*this); // remember the col index of the previous cell to handle rowspans into this row PRInt32 firstPrevColIndex = (iter.IsLeftToRight()) ? -1 : aTableFrame.GetColCount(); PRInt32 prevColIndex = firstPrevColIndex; nscoord x = 0; // running total of children x offset // This computes the max of all cell heights nscoord cellMaxHeight = 0; // Reflow each of our existing cell frames for (nsIFrame* kidFrame = iter.First(); kidFrame; kidFrame = iter.Next()) { nsTableCellFrame *cellFrame = do_QueryFrame(kidFrame); if (!cellFrame) { // XXXldb nsCSSFrameConstructor needs to enforce this! NS_NOTREACHED("yikes, a non-row child"); // it's an unknown frame type, give it a generic reflow and ignore the results nsTableCellReflowState kidReflowState(aPresContext, aReflowState, kidFrame, nsSize(0,0), false); InitChildReflowState(*aPresContext, nsSize(0,0), false, kidReflowState); nsHTMLReflowMetrics desiredSize; nsReflowStatus status; ReflowChild(kidFrame, aPresContext, desiredSize, kidReflowState, 0, 0, 0, status); kidFrame->DidReflow(aPresContext, nullptr, NS_FRAME_REFLOW_FINISHED); continue; } // See if we should only reflow the dirty child frames bool doReflowChild = true; if (!aReflowState.ShouldReflowAllKids() && !aTableFrame.IsGeometryDirty() && !NS_SUBTREE_DIRTY(kidFrame)) { if (!aReflowState.mFlags.mSpecialHeightReflow) doReflowChild = false; } else if ((NS_UNCONSTRAINEDSIZE != aReflowState.availableHeight)) { // We don't reflow a rowspan >1 cell here with a constrained height. // That happens in nsTableRowGroupFrame::SplitSpanningCells. if (aTableFrame.GetEffectiveRowSpan(*cellFrame) > 1) { doReflowChild = false; } } if (aReflowState.mFlags.mSpecialHeightReflow) { if (!isPaginated && !(cellFrame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT)) { continue; } } PRInt32 cellColIndex; cellFrame->GetColIndex(cellColIndex); cellColSpan = aTableFrame.GetEffectiveColSpan(*cellFrame); // If the adjacent cell is in a prior row (because of a rowspan) add in the space if ((iter.IsLeftToRight() && (prevColIndex != (cellColIndex - 1))) || (!iter.IsLeftToRight() && (prevColIndex != cellColIndex + cellColSpan))) { x += GetSpaceBetween(prevColIndex, cellColIndex, cellColSpan, aTableFrame, cellSpacingX, iter.IsLeftToRight(), false); } // remember the rightmost (ltr) or leftmost (rtl) column this cell spans into prevColIndex = (iter.IsLeftToRight()) ? cellColIndex + (cellColSpan - 1) : cellColIndex; // Reflow the child frame nsRect kidRect = kidFrame->GetRect(); nsRect kidVisualOverflow = kidFrame->GetVisualOverflowRect(); bool firstReflow = (kidFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0; if (doReflowChild) { // Calculate the available width for the table cell using the known column widths nscoord availCellWidth = CalcAvailWidth(aTableFrame, *cellFrame, cellSpacingX); nsHTMLReflowMetrics desiredSize; // If the avail width is not the same as last time we reflowed the cell or // the cell wants to be bigger than what was available last time or // it is a style change reflow or we are printing, then we must reflow the // cell. Otherwise we can skip the reflow. // XXXldb Why is this condition distinct from doReflowChild above? nsSize cellDesiredSize = cellFrame->GetDesiredSize(); if ((availCellWidth != cellFrame->GetPriorAvailWidth()) || (cellDesiredSize.width > cellFrame->GetPriorAvailWidth()) || (GetStateBits() & NS_FRAME_IS_DIRTY) || isPaginated || NS_SUBTREE_DIRTY(cellFrame) || // See if it needs a special reflow, or if it had one that we need to undo. (cellFrame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT) || HasPctHeight()) { // Reflow the cell to fit the available width, height // XXX The old IR_ChildIsDirty code used availCellWidth here. nsSize kidAvailSize(availCellWidth, aReflowState.availableHeight); // Reflow the child nsTableCellReflowState kidReflowState(aPresContext, aReflowState, kidFrame, kidAvailSize, false); InitChildReflowState(*aPresContext, kidAvailSize, borderCollapse, kidReflowState); nsReflowStatus status; rv = ReflowChild(kidFrame, aPresContext, desiredSize, kidReflowState, x, 0, NS_FRAME_INVALIDATE_ON_MOVE, status); // allow the table to determine if/how the table needs to be rebalanced // If any of the cells are not complete, then we're not complete if (NS_FRAME_IS_NOT_COMPLETE(status)) { aStatus = NS_FRAME_NOT_COMPLETE; } } else { if (x != kidRect.x) { kidFrame->InvalidateFrameSubtree(); } desiredSize.width = cellDesiredSize.width; desiredSize.height = cellDesiredSize.height; desiredSize.mOverflowAreas = cellFrame->GetOverflowAreas(); // if we are in a floated table, our position is not yet established, so we cannot reposition our views // the containing block will do this for us after positioning the table if (!aTableFrame.IsFloating()) { // Because we may have moved the frame we need to make sure any views are // positioned properly. We have to do this, because any one of our parent // frames could have moved and we have no way of knowing... nsTableFrame::RePositionViews(kidFrame); } } if (NS_UNCONSTRAINEDSIZE == aReflowState.availableHeight) { if (!GetPrevInFlow()) { // Calculate the cell's actual height given its pass2 height. This // function takes into account the specified height (in the style) CalculateCellActualHeight(cellFrame, desiredSize.height); } // height may have changed, adjust descent to absorb any excess difference nscoord ascent; if (!kidFrame->GetFirstPrincipalChild()->GetFirstPrincipalChild()) ascent = desiredSize.height; else ascent = ((nsTableCellFrame *)kidFrame)->GetCellBaseline(); nscoord descent = desiredSize.height - ascent; UpdateHeight(desiredSize.height, ascent, descent, &aTableFrame, cellFrame); } else { cellMaxHeight = NS_MAX(cellMaxHeight, desiredSize.height); PRInt32 rowSpan = aTableFrame.GetEffectiveRowSpan((nsTableCellFrame&)*kidFrame); if (1 == rowSpan) { SetContentHeight(cellMaxHeight); } } // Place the child desiredSize.width = availCellWidth; FinishReflowChild(kidFrame, aPresContext, nullptr, desiredSize, x, 0, 0); nsTableFrame::InvalidateFrame(kidFrame, kidRect, kidVisualOverflow, firstReflow); x += desiredSize.width; } else { if (kidRect.x != x) { // Invalidate the old position kidFrame->InvalidateFrameSubtree(); // move to the new position kidFrame->SetPosition(nsPoint(x, kidRect.y)); nsTableFrame::RePositionViews(kidFrame); // invalidate the new position kidFrame->InvalidateFrameSubtree(); } // we need to account for the cell's width even if it isn't reflowed x += kidRect.width; if (kidFrame->GetNextInFlow()) { aStatus = NS_FRAME_NOT_COMPLETE; } } ConsiderChildOverflow(aDesiredSize.mOverflowAreas, kidFrame); x += cellSpacingX; } // just set our width to what was available. The table will calculate the width and not use our value. aDesiredSize.width = aReflowState.availableWidth; if (aReflowState.mFlags.mSpecialHeightReflow) { aDesiredSize.height = mRect.height; } else if (NS_UNCONSTRAINEDSIZE == aReflowState.availableHeight) { aDesiredSize.height = CalcHeight(aReflowState); if (GetPrevInFlow()) { nscoord height = CalcHeightFromUnpaginatedHeight(aPresContext, *this); aDesiredSize.height = NS_MAX(aDesiredSize.height, height); } else { if (isPaginated && HasStyleHeight()) { // set the unpaginated height so next in flows can try to honor it SetHasUnpaginatedHeight(true); SetUnpaginatedHeight(aPresContext, aDesiredSize.height); } if (isPaginated && HasUnpaginatedHeight()) { aDesiredSize.height = NS_MAX(aDesiredSize.height, GetUnpaginatedHeight(aPresContext)); } } } else { // constrained height, paginated // Compute the height we should have from style (subtracting the // height from our prev-in-flows from the style height) nscoord styleHeight = CalcHeightFromUnpaginatedHeight(aPresContext, *this); if (styleHeight > aReflowState.availableHeight) { styleHeight = aReflowState.availableHeight; NS_FRAME_SET_INCOMPLETE(aStatus); } aDesiredSize.height = NS_MAX(cellMaxHeight, styleHeight); } aDesiredSize.UnionOverflowAreasWithDesiredBounds(); FinishAndStoreOverflow(&aDesiredSize); return rv; }