Bug 35168 (Part 2) - Allow relative positioning of internal table objects. r=dbaron

This commit is contained in:
Seth Fowler 2014-08-19 18:24:58 -07:00
parent 777c2b816e
commit a2cee49fc6
8 changed files with 285 additions and 139 deletions

View File

@ -410,16 +410,14 @@ RestyleManager::RecomputePosition(nsIFrame* aFrame)
// For relative positioning, we can simply update the frame rect
if (display->IsRelativelyPositionedStyle()) {
if (display->IsInnerTableStyle()) {
// We don't currently support relative positioning of inner table
// elements (bug 35168). If we apply offsets to things we haven't
// previously offset, we'll get confused. So bail.
return true;
}
// Move the frame
if (display->mPosition == NS_STYLE_POSITION_STICKY) {
if (display->IsInnerTableStyle()) {
// We don't currently support sticky positioning of inner table
// elements (bug 975644). Bail.
return true;
}
// Update sticky positioning for an entire element at once, starting with
// the first continuation or ib-split sibling.
// It's rare that the frame we already have isn't already the first

View File

@ -1873,7 +1873,7 @@ nsTableFrame::Reflow(nsPresContext* aPresContext,
// if there is an incomplete child, then set the desired height to include it but not the next one
nsMargin borderPadding = GetChildAreaOffset(&aReflowState);
aDesiredSize.Height() = borderPadding.bottom + GetCellSpacingY(GetRowCount()) +
lastChildReflowed->GetRect().YMost();
lastChildReflowed->GetNormalRect().YMost();
}
haveDesiredHeight = true;
@ -2677,6 +2677,7 @@ nsTableFrame::InitChildReflowState(nsHTMLReflowState& aReflowState)
// aKidRect is relative to the upper-left origin of our frame
void nsTableFrame::PlaceChild(nsTableReflowState& aReflowState,
nsIFrame* aKidFrame,
nsPoint aKidPosition,
nsHTMLReflowMetrics& aKidDesiredSize,
const nsRect& aOriginalKidRect,
const nsRect& aOriginalKidVisualOverflow)
@ -2686,7 +2687,7 @@ void nsTableFrame::PlaceChild(nsTableReflowState& aReflowState,
// Place and size the child
FinishReflowChild(aKidFrame, PresContext(), aKidDesiredSize, nullptr,
aReflowState.x, aReflowState.y, 0);
aKidPosition.x, aKidPosition.y, 0);
InvalidateTableFrame(aKidFrame, aOriginalKidRect, aOriginalKidVisualOverflow,
isFirstReflow);
@ -2870,7 +2871,10 @@ nsTableFrame::PlaceRepeatedFooter(nsTableReflowState& aReflowState,
desiredSize.ClearSize();
ReflowChild(aTfoot, presContext, desiredSize, footerReflowState,
aReflowState.x, aReflowState.y, 0, footerStatus);
PlaceChild(aReflowState, aTfoot, desiredSize, origTfootRect,
nsPoint kidPosition(aReflowState.x, aReflowState.y);
footerReflowState.ApplyRelativePositioning(&kidPosition);
PlaceChild(aReflowState, aTfoot, kidPosition, desiredSize, origTfootRect,
origTfootVisualOverflow);
}
@ -2990,7 +2994,7 @@ nsTableFrame::ReflowChildren(nsTableReflowState& aReflowState,
// We ignore a repeated head row group in this check to avoid causing
// infinite loops in some circumstances - see bug 344883.
if (childX > ((thead && IsRepeatedFrame(thead)) ? 1u : 0u) &&
(rowGroups[childX - 1]->GetRect().YMost() > 0)) {
(rowGroups[childX - 1]->GetNormalRect().YMost() > 0)) {
kidReflowState.mFlags.mIsTopOfPage = false;
}
aReflowState.y += cellSpacingY;
@ -3005,6 +3009,8 @@ nsTableFrame::ReflowChildren(nsTableReflowState& aReflowState,
ReflowChild(kidFrame, presContext, desiredSize, kidReflowState,
aReflowState.x, aReflowState.y, 0, aStatus);
nsPoint kidPosition(aReflowState.x, aReflowState.y);
kidReflowState.ApplyRelativePositioning(&kidPosition);
if (reorder) {
// reorder row groups the reflow may have changed the nextinflows
@ -3036,8 +3042,8 @@ nsTableFrame::ReflowChildren(nsTableReflowState& aReflowState,
if (childX+1 < rowGroups.Length()) {
nsIFrame* nextRowGroupFrame = rowGroups[childX + 1];
if (nextRowGroupFrame) {
PlaceChild(aReflowState, kidFrame, desiredSize, oldKidRect,
oldKidVisualOverflow);
PlaceChild(aReflowState, kidFrame, kidPosition, desiredSize,
oldKidRect, oldKidVisualOverflow);
if (allowRepeatedFooter) {
PlaceRepeatedFooter(aReflowState, tfoot, footerHeight);
}
@ -3065,8 +3071,8 @@ nsTableFrame::ReflowChildren(nsTableReflowState& aReflowState,
break;
}
else { // we can't push so lets make clear how much space we need
PlaceChild(aReflowState, kidFrame, desiredSize, oldKidRect,
oldKidVisualOverflow);
PlaceChild(aReflowState, kidFrame, kidPosition, desiredSize,
oldKidRect, oldKidVisualOverflow);
aLastChildReflowed = kidFrame;
if (allowRepeatedFooter) {
PlaceRepeatedFooter(aReflowState, tfoot, footerHeight);
@ -3089,7 +3095,7 @@ nsTableFrame::ReflowChildren(nsTableReflowState& aReflowState,
}
// Place the child
PlaceChild(aReflowState, kidFrame, desiredSize, oldKidRect,
PlaceChild(aReflowState, kidFrame, kidPosition, desiredSize, oldKidRect,
oldKidVisualOverflow);
// Remember where we just were in case we end up pushing children
@ -3136,12 +3142,12 @@ nsTableFrame::ReflowChildren(nsTableReflowState& aReflowState,
}
else { // it isn't being reflowed
aReflowState.y += cellSpacingY;
nsRect kidRect = kidFrame->GetRect();
nsRect kidRect = kidFrame->GetNormalRect();
if (kidRect.y != aReflowState.y) {
// invalidate the old position
kidFrame->InvalidateFrameSubtree();
kidRect.y = aReflowState.y;
kidFrame->SetRect(kidRect); // move to the new position
// move to the new position
kidFrame->MovePositionBy(nsPoint(0, aReflowState.y - kidRect.y));
RePositionViews(kidFrame);
// invalidate the new position
kidFrame->InvalidateFrameSubtree();
@ -3294,64 +3300,64 @@ nsTableFrame::DistributeHeightToRows(const nsHTMLReflowState& aReflowState,
nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
nscoord amountUsedByRG = 0;
nscoord yOriginRow = 0;
nsRect rgRect = rgFrame->GetRect();
nsRect rgNormalRect = rgFrame->GetNormalRect();
if (!rgFrame->HasStyleHeight()) {
nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
while (rowFrame) {
nsRect rowRect = rowFrame->GetRect();
nsRect rowNormalRect = rowFrame->GetNormalRect();
nscoord cellSpacingY = GetCellSpacingY(rowFrame->GetRowIndex());
if ((amountUsed < aAmount) && rowFrame->HasPctHeight()) {
nscoord pctHeight = rowFrame->GetHeight(pctBasis);
nscoord amountForRow = std::min(aAmount - amountUsed, pctHeight - rowRect.height);
nscoord amountForRow = std::min(aAmount - amountUsed,
pctHeight - rowNormalRect.height);
if (amountForRow > 0) {
nsRect oldRowRect = rowRect;
rowRect.height += amountForRow;
// XXXbz we don't need to change rowRect.y to be yOriginRow?
rowFrame->SetRect(rowRect);
yOriginRow += rowRect.height + cellSpacingY;
yEndRG += rowRect.height + cellSpacingY;
// XXXbz we don't need to move the row's y position to yOriginRow?
nsRect origRowRect = rowFrame->GetRect();
nscoord newRowHeight = rowNormalRect.height + amountForRow;
rowFrame->SetSize(nsSize(rowNormalRect.width, newRowHeight));
yOriginRow += newRowHeight + cellSpacingY;
yEndRG += newRowHeight + cellSpacingY;
amountUsed += amountForRow;
amountUsedByRG += amountForRow;
//rowFrame->DidResize();
nsTableFrame::RePositionViews(rowFrame);
rgFrame->InvalidateFrameWithRect(oldRowRect);
rgFrame->InvalidateFrameWithRect(origRowRect);
rgFrame->InvalidateFrame();
}
}
else {
if (amountUsed > 0 && yOriginRow != rowRect.y &&
if (amountUsed > 0 && yOriginRow != rowNormalRect.y &&
!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
rowFrame->InvalidateFrameSubtree();
rowFrame->SetPosition(nsPoint(rowRect.x, yOriginRow));
rowFrame->MovePositionBy(nsPoint(0, yOriginRow - rowNormalRect.y));
nsTableFrame::RePositionViews(rowFrame);
rowFrame->InvalidateFrameSubtree();
}
yOriginRow += rowRect.height + cellSpacingY;
yEndRG += rowRect.height + cellSpacingY;
yOriginRow += rowNormalRect.height + cellSpacingY;
yEndRG += rowNormalRect.height + cellSpacingY;
}
rowFrame = rowFrame->GetNextRow();
}
if (amountUsed > 0) {
if (rgRect.y != yOriginRG) {
if (rgNormalRect.y != yOriginRG) {
rgFrame->InvalidateFrameSubtree();
}
nsRect origRgRect = rgRect;
nsRect origRgNormalRect = rgFrame->GetRect();
nsRect origRgVisualOverflow = rgFrame->GetVisualOverflowRect();
rgRect.y = yOriginRG;
rgRect.height += amountUsedByRG;
rgFrame->MovePositionBy(nsPoint(0, yOriginRG - rgNormalRect.y));
rgFrame->SetSize(nsSize(rgNormalRect.width,
rgNormalRect.height + amountUsedByRG));
rgFrame->SetRect(rgRect);
nsTableFrame::InvalidateTableFrame(rgFrame, origRgRect,
nsTableFrame::InvalidateTableFrame(rgFrame, origRgNormalRect,
origRgVisualOverflow, false);
}
}
else if (amountUsed > 0 && yOriginRG != rgRect.y) {
else if (amountUsed > 0 && yOriginRG != rgNormalRect.y) {
rgFrame->InvalidateFrameSubtree();
rgFrame->SetPosition(nsPoint(rgRect.x, yOriginRG));
rgFrame->MovePositionBy(nsPoint(0, yOriginRG - rgNormalRect.y));
// Make sure child views are properly positioned
nsTableFrame::RePositionViews(rgFrame);
rgFrame->InvalidateFrameSubtree();
@ -3431,14 +3437,14 @@ nsTableFrame::DistributeHeightToRows(const nsHTMLReflowState& aReflowState,
nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
nscoord amountUsedByRG = 0;
nscoord yOriginRow = 0;
nsRect rgRect = rgFrame->GetRect();
nsRect rgNormalRect = rgFrame->GetNormalRect();
nsRect rgVisualOverflow = rgFrame->GetVisualOverflowRect();
// see if there is an eligible row group or we distribute to all rows
if (!firstUnStyledRG || !rgFrame->HasStyleHeight() || !eligibleRows) {
nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
while (rowFrame) {
nscoord cellSpacingY = GetCellSpacingY(rowFrame->GetRowIndex());
nsRect rowRect = rowFrame->GetRect();
nsRect rowNormalRect = rowFrame->GetNormalRect();
nsRect rowVisualOverflow = rowFrame->GetVisualOverflowRect();
// see if there is an eligible row or we distribute to all rows
if (!firstUnStyledRow || !rowFrame->HasStyleHeight() || !eligibleRows) {
@ -3447,7 +3453,7 @@ nsTableFrame::DistributeHeightToRows(const nsHTMLReflowState& aReflowState,
if (!expandEmptyRows) {
// The amount of additional space each row gets is proportional to
// its height
ratio = float(rowRect.height) / float(divisor);
ratio = float(rowNormalRect.height) / float(divisor);
} else {
// empty rows get all the same additional space
ratio = 1.0f / float(eligibleRows);
@ -3463,17 +3469,18 @@ nsTableFrame::DistributeHeightToRows(const nsHTMLReflowState& aReflowState,
? aAmount - amountUsed : NSToCoordRound(((float)(heightToDistribute)) * ratio);
amountForRow = std::min(amountForRow, aAmount - amountUsed);
if (yOriginRow != rowRect.y) {
if (yOriginRow != rowNormalRect.y) {
rowFrame->InvalidateFrameSubtree();
}
// update the row height
nsRect newRowRect(rowRect.x, yOriginRow, rowRect.width,
rowRect.height + amountForRow);
rowFrame->SetRect(newRowRect);
nsRect origRowRect = rowFrame->GetRect();
nscoord newRowHeight = rowNormalRect.height + amountForRow;
rowFrame->MovePositionBy(nsPoint(0, yOriginRow - rowNormalRect.y));
rowFrame->SetSize(nsSize(rowNormalRect.width, newRowHeight));
yOriginRow += newRowRect.height + cellSpacingY;
yEndRG += newRowRect.height + cellSpacingY;
yOriginRow += newRowHeight + cellSpacingY;
yEndRG += newRowHeight + cellSpacingY;
amountUsed += amountForRow;
amountUsedByRG += amountForRow;
@ -3481,37 +3488,39 @@ nsTableFrame::DistributeHeightToRows(const nsHTMLReflowState& aReflowState,
//rowFrame->DidResize();
nsTableFrame::RePositionViews(rowFrame);
nsTableFrame::InvalidateTableFrame(rowFrame, rowRect, rowVisualOverflow,
false);
nsTableFrame::InvalidateTableFrame(rowFrame, origRowRect,
rowVisualOverflow, false);
}
else {
if (amountUsed > 0 && yOriginRow != rowRect.y) {
if (amountUsed > 0 && yOriginRow != rowNormalRect.y) {
rowFrame->InvalidateFrameSubtree();
rowFrame->SetPosition(nsPoint(rowRect.x, yOriginRow));
rowFrame->MovePositionBy(nsPoint(0, yOriginRow - rowNormalRect.y));
nsTableFrame::RePositionViews(rowFrame);
rowFrame->InvalidateFrameSubtree();
}
yOriginRow += rowRect.height + cellSpacingY;
yEndRG += rowRect.height + cellSpacingY;
yOriginRow += rowNormalRect.height + cellSpacingY;
yEndRG += rowNormalRect.height + cellSpacingY;
}
rowFrame = rowFrame->GetNextRow();
}
if (amountUsed > 0) {
if (rgRect.y != yOriginRG) {
if (rgNormalRect.y != yOriginRG) {
rgFrame->InvalidateFrameSubtree();
}
rgFrame->SetRect(nsRect(rgRect.x, yOriginRG, rgRect.width,
rgRect.height + amountUsedByRG));
nsRect origRgNormalRect = rgFrame->GetRect();
rgFrame->MovePositionBy(nsPoint(0, yOriginRG - rgNormalRect.y));
rgFrame->SetSize(nsSize(rgNormalRect.width,
rgNormalRect.height + amountUsedByRG));
nsTableFrame::InvalidateTableFrame(rgFrame, rgRect, rgVisualOverflow,
false);
nsTableFrame::InvalidateTableFrame(rgFrame, origRgNormalRect,
rgVisualOverflow, false);
}
// Make sure child views are properly positioned
}
else if (amountUsed > 0 && yOriginRG != rgRect.y) {
else if (amountUsed > 0 && yOriginRG != rgNormalRect.y) {
rgFrame->InvalidateFrameSubtree();
rgFrame->SetPosition(nsPoint(rgRect.x, yOriginRG));
rgFrame->MovePositionBy(nsPoint(0, yOriginRG - rgNormalRect.y));
// Make sure child views are properly positioned
nsTableFrame::RePositionViews(rgFrame);
rgFrame->InvalidateFrameSubtree();
@ -3611,8 +3620,15 @@ nsTableFrame::GetLogicalBaseline(WritingMode aWritingMode) const
nsTableRowGroupFrame* rgFrame = orderedRowGroups[rgIndex];
if (rgFrame->GetRowCount()) {
firstRow = rgFrame->GetFirstRow();
ascent = rgFrame->BStart(aWritingMode, containerWidth) +
firstRow->BStart(aWritingMode, containerWidth) +
nscoord rgNormalBStart =
LogicalRect(aWritingMode, rgFrame->GetNormalRect(), containerWidth)
.Origin(aWritingMode).B(aWritingMode);
nscoord firstRowNormalBStart =
LogicalRect(aWritingMode, firstRow->GetNormalRect(), containerWidth)
.Origin(aWritingMode).B(aWritingMode);
ascent = rgNormalBStart + firstRowNormalBStart +
firstRow->GetRowBaseline(aWritingMode);
break;
}

View File

@ -679,6 +679,7 @@ protected:
void PlaceChild(nsTableReflowState& aReflowState,
nsIFrame* aKidFrame,
nsPoint aKidPosition,
nsHTMLReflowMetrics& aKidDesiredSize,
const nsRect& aOriginalKidRect,
const nsRect& aOriginalKidVisualOverflow);

View File

@ -92,11 +92,7 @@
Elements with stacking contexts set up their own painter to finish the
painting process, since they were skipped. They call the appropriate
sub-part of the loop (e.g. PaintRow) which will paint the frame and
descendants. Note that it is permissible according to CSS2.1 to ignore'
'position:relative' (and implicitly, 'opacity') on table parts so that
table parts can never create stacking contexts; if we want to, we can
implement that, and then we won't have to deal with TableBackgroundPainter
being used anywhere but from the nsTableFrame.
descendants.
XXX views are going
*/
@ -422,7 +418,15 @@ TableBackgroundPainter::PaintTable(nsTableFrame* aTableFrame,
// Need to compute the right rect via GetOffsetTo, since the row
// group may not be a child of the table.
mRowGroup.mRect.MoveTo(rg->GetOffsetTo(aTableFrame));
if (mRowGroup.mRect.Intersects(mDirtyRect - mRenderPt)) {
// We have to draw backgrounds not only within the overflow region of this
// row group, but also possibly (in the case of column / column group
// backgrounds) at its pre-relative-positioning location.
nsRect rgVisualOverflow = rg->GetVisualOverflowRectRelativeToSelf();
nsRect rgOverflowRect = rgVisualOverflow + rg->GetPosition();
nsRect rgNormalRect = rgVisualOverflow + rg->GetNormalPosition();
if (rgOverflowRect.Union(rgNormalRect).Intersects(mDirtyRect - mRenderPt)) {
nsresult rv = PaintRowGroup(rg, rg->IsPseudoStackingContextFromStyle());
if (NS_FAILED(rv)) return rv;
}
@ -470,16 +474,12 @@ TableBackgroundPainter::PaintRowGroup(nsTableRowGroupFrame* aFrame,
mRowGroup.mRect.MoveTo(0, 0);
/* Find the right row to start with */
nscoord ignored; // We don't care about overflow above, since what we really
// care about are backgrounds and overflow above doesn't
// correspond to backgrounds, since cells can't span up from
// their originating row. We do care about overflow below,
// however, since that can be due to rowspans.
// Note that mDirtyRect - mRenderPt is guaranteed to be in the row
// group's coordinate system here, so passing its .y to
// GetFirstRowContaining is ok.
nsIFrame* cursor = aFrame->GetFirstRowContaining(mDirtyRect.y - mRenderPt.y, &ignored);
nscoord overflowAbove;
nsIFrame* cursor = aFrame->GetFirstRowContaining(mDirtyRect.y - mRenderPt.y, &overflowAbove);
// Sadly, it seems like there may be non-row frames in there... or something?
// There are certainly null-checks in GetFirstRow() and GetNextRow(). :(
@ -500,9 +500,13 @@ TableBackgroundPainter::PaintRowGroup(nsTableRowGroupFrame* aFrame,
/* Finally paint */
for (; row; row = row->GetNextRow()) {
mRow.SetFrame(row);
if (mDirtyRect.YMost() - mRenderPt.y < mRow.mRect.y) { // Intersect wouldn't handle
// rowspans.
// Be sure to consider our positions both pre- and post-relative
// positioning, since we potentially need to paint at both places.
nscoord rowY = std::min(mRow.mRect.y, row->GetNormalPosition().y);
// Intersect wouldn't handle rowspans.
if (cursor &&
(mDirtyRect.YMost() - mRenderPt.y) <= (rowY - overflowAbove)) {
// All done; cells originating in later rows can't intersect mDirtyRect.
break;
}
@ -564,10 +568,20 @@ TableBackgroundPainter::PaintRow(nsTableRowFrame* aFrame,
//else: Use row group's coord system -> no translation necessary
for (nsTableCellFrame* cell = aFrame->GetFirstCell(); cell; cell = cell->GetNextCell()) {
//Translate to use the same coord system as mRow.
mCellRect = cell->GetRect() + mRow.mRect.TopLeft() + mRenderPt;
if (mCellRect.Intersects(mDirtyRect)) {
nsresult rv = PaintCell(cell, aPassThrough || cell->IsPseudoStackingContextFromStyle());
nsRect cellBGRect, rowBGRect, rowGroupBGRect, colBGRect;
ComputeCellBackgrounds(cell, cellBGRect, rowBGRect,
rowGroupBGRect, colBGRect);
// Find the union of all the cell background layers.
nsRect combinedRect(cellBGRect);
combinedRect.UnionRect(combinedRect, rowBGRect);
combinedRect.UnionRect(combinedRect, rowGroupBGRect);
combinedRect.UnionRect(combinedRect, colBGRect);
if (combinedRect.Intersects(mDirtyRect)) {
bool passCell = aPassThrough || cell->IsPseudoStackingContextFromStyle();
nsresult rv = PaintCell(cell, cellBGRect, rowBGRect, rowGroupBGRect,
colBGRect, passCell);
if (NS_FAILED(rv)) return rv;
}
}
@ -579,7 +593,11 @@ TableBackgroundPainter::PaintRow(nsTableRowFrame* aFrame,
nsresult
TableBackgroundPainter::PaintCell(nsTableCellFrame* aCell,
bool aPassSelf)
nsRect& aCellBGRect,
nsRect& aRowBGRect,
nsRect& aRowGroupBGRect,
nsRect& aColBGRect,
bool aPassSelf)
{
NS_PRECONDITION(aCell, "null frame");
@ -603,7 +621,7 @@ TableBackgroundPainter::PaintCell(nsTableCellFrame* aCell,
mCols[colIndex].mColGroup->mRect + mRenderPt,
mCols[colIndex].mColGroup->mFrame->StyleContext(),
*mCols[colIndex].mColGroup->mBorder,
mBGPaintFlags, &mCellRect);
mBGPaintFlags, &aColBGRect);
}
//Paint column background
@ -613,7 +631,7 @@ TableBackgroundPainter::PaintCell(nsTableCellFrame* aCell,
mCols[colIndex].mCol.mRect + mRenderPt,
mCols[colIndex].mCol.mFrame->StyleContext(),
*mCols[colIndex].mCol.mBorder,
mBGPaintFlags, &mCellRect);
mBGPaintFlags, &aColBGRect);
}
//Paint row group background
@ -623,7 +641,7 @@ TableBackgroundPainter::PaintCell(nsTableCellFrame* aCell,
mRowGroup.mRect + mRenderPt,
mRowGroup.mFrame->StyleContext(),
*mRowGroup.mBorder,
mBGPaintFlags, &mCellRect);
mBGPaintFlags, &aRowGroupBGRect);
}
//Paint row background
@ -633,14 +651,69 @@ TableBackgroundPainter::PaintCell(nsTableCellFrame* aCell,
mRow.mRect + mRenderPt,
mRow.mFrame->StyleContext(),
*mRow.mBorder,
mBGPaintFlags, &mCellRect);
mBGPaintFlags, &aRowBGRect);
}
//Paint cell background in border-collapse unless we're just passing
if (mIsBorderCollapse && !aPassSelf) {
aCell->PaintCellBackground(mRenderingContext, mDirtyRect,
mCellRect.TopLeft(), mBGPaintFlags);
aCellBGRect.TopLeft(), mBGPaintFlags);
}
return NS_OK;
}
void
TableBackgroundPainter::ComputeCellBackgrounds(nsTableCellFrame* aCell,
nsRect& aCellBGRect,
nsRect& aRowBGRect,
nsRect& aRowGroupBGRect,
nsRect& aColBGRect)
{
// We need to compute table background layer rects for this cell space,
// adjusted for possible relative positioning. This behavior is not specified
// at the time of this writing, but the approach below should be web
// compatible.
//
// Our goal is that relative positioning of a table part should leave
// backgrounds *under* that part unchanged. ("Under" being defined by CSS 2.1
// Section 17.5.1.) If a cell is positioned, we do not expect the row
// background to move. On the other hand, the backgrounds of layers *above*
// the positioned part are taken along for the ride -- for example,
// positioning a row group will also cause the row background to be drawn in
// the new location, unless it has further positioning applied.
//
// Each table part layer has its position stored in the coordinate space of
// the layer below (which is to say, its geometric parent), and the stored
// position is the post-relative-positioning one. The position of each
// background layer rect is thus determined by peeling off successive table
// part layers, removing the contribution of each layer's positioning one by
// one. Every rect we generate will be the same size, the size of the cell
// space.
// We cannot rely on the row group background data to be available, since some
// callers enter through PaintRow.
nsIFrame* rowGroupFrame =
mRowGroup.mFrame ? mRowGroup.mFrame : mRow.mFrame->GetParent();
// The cell background goes at the cell's position, translated to use the same
// coordinate system as mRow.
aCellBGRect = aCell->GetRect() + mRow.mRect.TopLeft() + mRenderPt;
// The row background goes at the normal position of the cell, which is to say
// the position without relative positioning applied.
aRowBGRect = aCellBGRect + (aCell->GetNormalPosition() - aCell->GetPosition());
// The row group background goes at the position we'd find the cell if neither
// the cell's relative positioning nor the row's were applied.
aRowGroupBGRect = aRowBGRect +
(mRow.mFrame->GetNormalPosition() - mRow.mFrame->GetPosition());
// The column and column group backgrounds (they're always at the same
// location, since relative positioning doesn't apply to columns or column
// groups) are drawn at the position we'd find the cell if none of the cell's,
// row's, or row group's relative positioning were applied.
aColBGRect = aRowGroupBGRect +
(rowGroupFrame->GetNormalPosition() - rowGroupFrame->GetPosition());
}

View File

@ -131,12 +131,34 @@ class TableBackgroundPainter
/** Paint table background layers for this cell space
* Also paints cell's own background in border-collapse mode
* @param aFrame - the cell
* @param aPassSelf - pass this cell; i.e. paint only underlying layers
* @param aCell - the cell
* @param aCellBGRect - background rect for the cell
* @param aRowBGRect - background rect for the row
* @param aRowGroupBGRect - background rect for the row group
* @param aColBGRect - background rect for the column and column group
* @param aPassSelf - pass this cell; i.e. paint only underlying layers
*/
nsresult PaintCell(nsTableCellFrame* aFrame,
nsresult PaintCell(nsTableCellFrame* aCell,
nsRect& aCellBGRect,
nsRect& aRowBGRect,
nsRect& aRowGroupBGRect,
nsRect& aColBGRect,
bool aPassSelf);
/** Compute table background layer positions for this cell space
* @param aCell - the cell
* @param aCellBGRectOut - outparam: background rect for the cell
* @param aRowBGRectOut - outparam: background rect for the row
* @param aRowGroupBGRectOut - outparam: background rect for the row group
* @param aColBGRectOut - outparam: background rect for the column
and column group
*/
void ComputeCellBackgrounds(nsTableCellFrame* aCell,
nsRect& aCellBGRect,
nsRect& aRowBGRect,
nsRect& aRowGroupBGRect,
nsRect& aColBGRect);
/** Translate mRenderingContext, mDirtyRect, and mCols' column and
* colgroup coords
* @param aDX - origin's x-coord change
@ -214,7 +236,6 @@ class TableBackgroundPainter
uint32_t mNumCols;
TableBackgroundData mRowGroup; //current row group
TableBackgroundData mRow; //current row
nsRect mCellRect; //current cell's rect
nsStyleBorder mZeroBorder; //cached zero-width border
uint32_t mBGPaintFlags;

View File

@ -2,6 +2,9 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/Maybe.h"
#include "nsTableRowFrame.h"
#include "nsTableRowGroupFrame.h"
#include "nsIPresShell.h"
@ -395,7 +398,7 @@ nscoord nsTableRowFrame::GetRowBaseline(WritingMode aWritingMode)
while (childFrame) {
if (IS_TABLE_CELL(childFrame->GetType())) {
nsIFrame* firstKid = childFrame->GetFirstPrincipalChild();
ascent = std::max(ascent, firstKid->GetRect().YMost());
ascent = std::max(ascent, firstKid->GetNormalRect().YMost());
}
// Get the next child
childFrame = iter.Next();
@ -861,7 +864,10 @@ nsTableRowFrame::ReflowChildren(nsPresContext* aPresContext,
// Reflow the child frame
nsRect kidRect = kidFrame->GetRect();
nsPoint origKidNormalPosition = kidFrame->GetNormalPosition();
MOZ_ASSERT(origKidNormalPosition.y == 0);
nsRect kidVisualOverflow = kidFrame->GetVisualOverflowRect();
nsPoint kidPosition(x, 0);
bool firstReflow =
(kidFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0;
@ -870,6 +876,7 @@ nsTableRowFrame::ReflowChildren(nsPresContext* aPresContext,
nscoord availCellWidth =
CalcAvailWidth(aTableFrame, *cellFrame);
Maybe<nsTableCellReflowState> kidReflowState;
nsHTMLReflowMetrics desiredSize(aReflowState);
// If the avail width is not the same as last time we reflowed the cell or
@ -890,19 +897,19 @@ nsTableRowFrame::ReflowChildren(nsPresContext* aPresContext,
HasPctHeight()) {
// Reflow the cell to fit the available width, height
// XXX The old IR_ChildIsDirty code used availCellWidth here.
nsSize kidAvailSize(availCellWidth, aReflowState.AvailableHeight());
nsSize kidAvailSize(availCellWidth, aReflowState.AvailableHeight());
// Reflow the child
nsTableCellReflowState
kidReflowState(aPresContext, aReflowState, kidFrame,
LogicalSize(kidFrame->GetWritingMode(),
kidAvailSize),
nsHTMLReflowState::CALLER_WILL_INIT);
kidReflowState.emplace(aPresContext, aReflowState, kidFrame,
LogicalSize(kidFrame->GetWritingMode(),
kidAvailSize),
// Cast needed for gcc 4.4.
uint32_t(nsHTMLReflowState::CALLER_WILL_INIT));
InitChildReflowState(*aPresContext, kidAvailSize, borderCollapse,
kidReflowState);
*kidReflowState);
nsReflowStatus status;
ReflowChild(kidFrame, aPresContext, desiredSize, kidReflowState,
ReflowChild(kidFrame, aPresContext, desiredSize, *kidReflowState,
x, 0, 0, status);
// allow the table to determine if/how the table needs to be rebalanced
@ -912,7 +919,7 @@ nsTableRowFrame::ReflowChildren(nsPresContext* aPresContext,
}
}
else {
if (x != kidRect.x) {
if (x != origKidNormalPosition.x) {
kidFrame->InvalidateFrameSubtree();
}
@ -956,7 +963,18 @@ nsTableRowFrame::ReflowChildren(nsPresContext* aPresContext,
// Place the child
desiredSize.ISize(rowWM) = availCellWidth;
FinishReflowChild(kidFrame, aPresContext, desiredSize, nullptr, x, 0, 0);
if (kidReflowState) {
// We reflowed. Apply relative positioning in the normal way.
kidReflowState->ApplyRelativePositioning(&kidPosition);
} else {
// We didn't reflow. To take relative positioning into account,
// translate the new position by the vector from the previous 'normal'
// position to the previous position.
// XXX(seth): This doesn't work for 'position: sticky'.
kidPosition += kidRect.TopLeft() - origKidNormalPosition;
}
FinishReflowChild(kidFrame, aPresContext, desiredSize, nullptr,
kidPosition.x, kidPosition.y, 0);
nsTableFrame::InvalidateTableFrame(kidFrame, kidRect, kidVisualOverflow,
firstReflow);
@ -964,11 +982,12 @@ nsTableRowFrame::ReflowChildren(nsPresContext* aPresContext,
x += desiredSize.Width();
}
else {
if (kidRect.x != x) {
if (x != origKidNormalPosition.x) {
// Invalidate the old position
kidFrame->InvalidateFrameSubtree();
// move to the new position
kidFrame->SetPosition(nsPoint(x, kidRect.y));
// Move to the new position. As above, we need to account for relative
// positioning.
kidFrame->MovePositionBy(nsPoint(x - origKidNormalPosition.x, 0));
nsTableFrame::RePositionViews(kidFrame);
// invalidate the new position
kidFrame->InvalidateFrameSubtree();
@ -1263,14 +1282,16 @@ nsTableRowFrame::CollapseRowIfNecessary(nscoord aRowOffset,
}
nsRect oldCellRect = cellFrame->GetRect();
nsPoint oldCellNormalPos = cellFrame->GetNormalPosition();
nsRect oldCellVisualOverflow = cellFrame->GetVisualOverflowRect();
if (aRowOffset == 0 && cRect.TopLeft() != oldCellRect.TopLeft()) {
if (aRowOffset == 0 && cRect.TopLeft() != oldCellNormalPos) {
// We're moving the cell. Invalidate the old overflow area
cellFrame->InvalidateFrameSubtree();
}
cellFrame->SetRect(cRect);
cellFrame->MovePositionBy(cRect.TopLeft() - oldCellNormalPos);
cellFrame->SetSize(cRect.Size());
// XXXbz This looks completely bogus in the cases when we didn't
// collapse the cell!

View File

@ -205,7 +205,8 @@ DisplayRows(nsDisplayListBuilder* aBuilder, nsFrame* aFrame,
if (kid) {
// have a cursor, use it
while (kid) {
if (kid->GetRect().y - overflowAbove >= aDirtyRect.YMost())
if (kid->GetRect().y - overflowAbove >= aDirtyRect.YMost() &&
kid->GetNormalRect().y - overflowAbove >= aDirtyRect.YMost())
break;
f->BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists);
kid = kid->GetNextSibling();
@ -277,6 +278,7 @@ void
nsTableRowGroupFrame::PlaceChild(nsPresContext* aPresContext,
nsRowGroupReflowState& aReflowState,
nsIFrame* aKidFrame,
nsPoint aKidPosition,
nsHTMLReflowMetrics& aDesiredSize,
const nsRect& aOriginalKidRect,
const nsRect& aOriginalKidVisualOverflow)
@ -285,8 +287,8 @@ nsTableRowGroupFrame::PlaceChild(nsPresContext* aPresContext,
(aKidFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0;
// Place and size the child
FinishReflowChild(aKidFrame, aPresContext, aDesiredSize, nullptr, 0,
aReflowState.y, 0);
FinishReflowChild(aKidFrame, aPresContext, aDesiredSize, nullptr,
aKidPosition.x, aKidPosition.y, 0);
nsTableFrame::InvalidateTableFrame(aKidFrame, aOriginalKidRect,
aOriginalKidVisualOverflow, isFirstReflow);
@ -400,16 +402,18 @@ nsTableRowGroupFrame::ReflowChildren(nsPresContext* aPresContext,
"If we're not on the first frame, we should have a "
"previous sibling...");
// If prev row has nonzero YMost, then we can't be at the top of the page
if (prevKidFrame && prevKidFrame->GetRect().YMost() > 0) {
if (prevKidFrame && prevKidFrame->GetNormalRect().YMost() > 0) {
kidReflowState.mFlags.mIsTopOfPage = false;
}
ReflowChild(kidFrame, aPresContext, desiredSize, kidReflowState,
0, aReflowState.y, 0, aStatus);
nsPoint kidPosition(0, aReflowState.y);
kidReflowState.ApplyRelativePositioning(&kidPosition);
// Place the child
PlaceChild(aPresContext, aReflowState, kidFrame, desiredSize,
oldKidRect, oldKidVisualOverflow);
PlaceChild(aPresContext, aReflowState, kidFrame, kidPosition,
desiredSize, oldKidRect, oldKidVisualOverflow);
aReflowState.y += cellSpacingY;
if (!reflowAllKids) {
@ -423,8 +427,6 @@ nsTableRowGroupFrame::ReflowChildren(nsPresContext* aPresContext,
unit != eStyleUnit_Coord) {
// Because other cells in the row may need to be aligned
// differently, repaint the entire row
nsRect kidRect(0, aReflowState.y,
desiredSize.Width(), desiredSize.Height());
InvalidateFrame();
}
else if (oldKidRect.height != desiredSize.Height())
@ -549,7 +551,7 @@ nsTableRowGroupFrame::CalculateRowHeights(nsPresContext* aPresContext,
if (!startRowFrame) return;
// the current row group height is the y origin of the 1st row we are about to calculated a height for
nscoord startRowGroupHeight = startRowFrame->GetPosition().y;
nscoord startRowGroupHeight = startRowFrame->GetNormalPosition().y;
int32_t numRows = GetRowCount() - (startRowFrame->GetRowIndex() - GetStartRowIndex());
// collect the current height of each row. nscoord* rowHeights = nullptr;
@ -778,25 +780,26 @@ nsTableRowGroupFrame::CalculateRowHeights(nsPresContext* aPresContext,
for (rowFrame = startRowFrame, rowIndex = 0; rowFrame; rowFrame = rowFrame->GetNextRow(), rowIndex++) {
nsRect rowBounds = rowFrame->GetRect();
nsRect rowVisualOverflow = rowFrame->GetVisualOverflowRect();
nscoord deltaY = yOrigin - rowFrame->GetNormalPosition().y;
bool movedFrame = (rowBounds.y != yOrigin);
nscoord rowHeight = (rowInfo[rowIndex].height > 0) ? rowInfo[rowIndex].height : 0;
if (movedFrame || (rowHeight != rowBounds.height)) {
if (deltaY != 0 || (rowHeight != rowBounds.height)) {
// Resize/move the row to its final size and position
if (movedFrame) {
if (deltaY != 0) {
rowFrame->InvalidateFrameSubtree();
}
rowFrame->SetRect(nsRect(rowBounds.x, yOrigin, rowBounds.width,
rowHeight));
rowFrame->MovePositionBy(nsPoint(0, deltaY));
rowFrame->SetSize(nsSize(rowBounds.width, rowHeight));
nsTableFrame::InvalidateTableFrame(rowFrame, rowBounds, rowVisualOverflow,
false);
}
if (movedFrame) {
nsTableFrame::RePositionViews(rowFrame);
// XXXbz we don't need to update our overflow area?
if (deltaY != 0) {
nsTableFrame::RePositionViews(rowFrame);
// XXXbz we don't need to update our overflow area?
}
}
yOrigin += rowHeight + tableFrame->GetCellSpacingY(startRowIndex + rowIndex);
}
@ -869,11 +872,12 @@ nsTableRowGroupFrame::SlideChild(nsRowGroupReflowState& aReflowState,
nsIFrame* aKidFrame)
{
// Move the frame if we need to
nsPoint oldPosition = aKidFrame->GetPosition();
nsPoint oldPosition = aKidFrame->GetNormalPosition();
nsPoint newPosition = oldPosition;
newPosition.y = aReflowState.y;
if (oldPosition.y != newPosition.y) {
aKidFrame->InvalidateFrameSubtree();
aReflowState.reflowState.ApplyRelativePositioning(&newPosition);
aKidFrame->SetPosition(newPosition);
nsTableFrame::RePositionViews(aKidFrame);
aKidFrame->InvalidateFrameSubtree();
@ -927,7 +931,7 @@ nsTableRowGroupFrame::SplitSpanningCells(nsPresContext& aPresContext,
for (nsTableRowFrame* row = &aFirstRow; !wasLast; row = row->GetNextRow()) {
wasLast = (row == &aLastRow);
int32_t rowIndex = row->GetRowIndex();
nsPoint rowPos = row->GetPosition();
nsPoint rowPos = row->GetNormalPosition();
// Iterate the cells looking for those that have rowspan > 1
for (nsTableCellFrame* cell = row->GetFirstCell(); cell; cell = cell->GetNextCell()) {
int32_t rowSpan = aTable.GetEffectiveRowSpan(rowIndex, *cell);
@ -942,7 +946,7 @@ nsTableRowGroupFrame::SplitSpanningCells(nsPresContext& aPresContext,
NS_ASSERTION(cellAvailHeight >= 0, "No space for cell?");
bool isTopOfPage = (row == &aFirstRow) && aFirstRowIsTopOfPage;
nsRect rowRect = row->GetRect();
nsRect rowRect = row->GetNormalRect();
nsSize rowAvailSize(aReflowState.AvailableWidth(),
std::max(aReflowState.AvailableHeight() - rowRect.y,
0));
@ -992,7 +996,7 @@ nsTableRowGroupFrame::SplitSpanningCells(nsPresContext& aPresContext,
}
}
if (!haveRowSpan) {
aDesiredHeight = aLastRow.GetRect().YMost();
aDesiredHeight = aLastRow.GetNormalRect().YMost();
}
}
@ -1073,7 +1077,7 @@ nsTableRowGroupFrame::SplitRowGroup(nsPresContext* aPresContext,
for (nsTableRowFrame* rowFrame = firstRowThisPage; rowFrame; rowFrame = rowFrame->GetNextRow()) {
bool rowIsOnPage = true;
nscoord cellSpacingY = aTableFrame->GetCellSpacingY(rowFrame->GetRowIndex());
nsRect rowRect = rowFrame->GetRect();
nsRect rowRect = rowFrame->GetNormalRect();
// See if the row fits on this page
if (rowRect.YMost() > availHeight) {
nsTableRowFrame* contRow = nullptr;
@ -1179,7 +1183,7 @@ nsTableRowGroupFrame::SplitRowGroup(nsPresContext* aPresContext,
break;
}
if (prevRowFrame) {
spanningRowBottom = prevRowFrame->GetRect().YMost();
spanningRowBottom = prevRowFrame->GetNormalRect().YMost();
lastRowThisPage = prevRowFrame;
isTopOfPage = (lastRowThisPage == firstRowThisPage) && aReflowState.mFlags.mIsTopOfPage;
aStatus = NS_FRAME_NOT_COMPLETE;
@ -1216,7 +1220,7 @@ nsTableRowGroupFrame::SplitRowGroup(nsPresContext* aPresContext,
// Try to put firstTruncateRow on the next page
nsTableRowFrame* rowBefore = ::GetRowBefore(*firstRowThisPage, *firstTruncatedRow);
nscoord oldSpanningRowBottom = spanningRowBottom;
spanningRowBottom = rowBefore->GetRect().YMost();
spanningRowBottom = rowBefore->GetNormalRect().YMost();
UndoContinuedRow(aPresContext, contRow);
contRow = nullptr;
@ -1895,12 +1899,12 @@ nsTableRowGroupFrame::GetFirstRowContaining(nscoord aY, nscoord* aOverflowAbove)
// encountering a row whose overflowArea.YMost() is <= aY but which has
// a row above it containing cell(s) that span to include aY.
while (cursorIndex > 0 &&
cursorFrame->GetRect().YMost() + property->mOverflowBelow > aY) {
cursorFrame->GetNormalRect().YMost() + property->mOverflowBelow > aY) {
--cursorIndex;
cursorFrame = property->mFrames[cursorIndex];
}
while (cursorIndex + 1 < frameCount &&
cursorFrame->GetRect().YMost() + property->mOverflowBelow <= aY) {
cursorFrame->GetNormalRect().YMost() + property->mOverflowBelow <= aY) {
++cursorIndex;
cursorFrame = property->mFrames[cursorIndex];
}
@ -1913,7 +1917,18 @@ nsTableRowGroupFrame::GetFirstRowContaining(nscoord aY, nscoord* aOverflowAbove)
bool
nsTableRowGroupFrame::FrameCursorData::AppendFrame(nsIFrame* aFrame)
{
nsRect overflowRect = aFrame->GetVisualOverflowRect();
// Relative positioning can cause table parts to move, but we will still paint
// the backgrounds for the parts under them at their 'normal' position. That
// means that we must consider the overflow rects at both positions. For
// example, if we use relative positioning to move a row-spanning cell, we
// will still paint the row background for that cell at its normal position,
// which will overflow the row.
// XXX(seth): This probably isn't correct in the presence of transforms.
nsRect positionedOverflowRect = aFrame->GetVisualOverflowRect();
nsPoint positionedToNormal = aFrame->GetNormalPosition() - aFrame->GetPosition();
nsRect normalOverflowRect = positionedOverflowRect + positionedToNormal;
nsRect overflowRect = positionedOverflowRect.Union(normalOverflowRect);
if (overflowRect.IsEmpty())
return true;
nscoord overflowAbove = -overflowRect.y;

View File

@ -337,6 +337,7 @@ protected:
void PlaceChild(nsPresContext* aPresContext,
nsRowGroupReflowState& aReflowState,
nsIFrame* aKidFrame,
nsPoint aKidPosition,
nsHTMLReflowMetrics& aDesiredSize,
const nsRect& aOriginalKidRect,
const nsRect& aOriginalKidVisualOverflow);