diff options
Diffstat (limited to 'Source/WebCore/rendering/RenderTableSection.cpp')
-rw-r--r-- | Source/WebCore/rendering/RenderTableSection.cpp | 607 |
1 files changed, 376 insertions, 231 deletions
diff --git a/Source/WebCore/rendering/RenderTableSection.cpp b/Source/WebCore/rendering/RenderTableSection.cpp index a415af583..1907657e9 100644 --- a/Source/WebCore/rendering/RenderTableSection.cpp +++ b/Source/WebCore/rendering/RenderTableSection.cpp @@ -29,29 +29,29 @@ #include "HitTestResult.h" #include "HTMLNames.h" #include "PaintInfo.h" +#include "RenderNamedFlowFragment.h" #include "RenderTableCell.h" #include "RenderTableCol.h" #include "RenderTableRow.h" +#include "RenderTextControl.h" #include "RenderView.h" #include "StyleInheritedData.h" #include <limits> #include <wtf/HashSet.h> #include <wtf/StackStats.h> -using namespace std; - namespace WebCore { using namespace HTMLNames; // Those 2 variables are used to balance the memory consumption vs the repaint time on big tables. -static unsigned gMinTableSizeToUseFastPaintPathWithOverflowingCell = 75 * 75; -static float gMaxAllowedOverflowingCellRatioForFastPaintPath = 0.1f; +static const unsigned gMinTableSizeToUseFastPaintPathWithOverflowingCell = 75 * 75; +static const float gMaxAllowedOverflowingCellRatioForFastPaintPath = 0.1f; static inline void setRowLogicalHeightToRowStyleLogicalHeightIfNotRelative(RenderTableSection::RowStruct& row) { ASSERT(row.rowRenderer); - row.logicalHeight = row.rowRenderer->style()->logicalHeight(); + row.logicalHeight = row.rowRenderer->style().logicalHeight(); if (row.logicalHeight.isRelative()) row.logicalHeight = Length(); } @@ -62,17 +62,16 @@ static inline void updateLogicalHeightForCell(RenderTableSection::RowStruct& row if (cell->rowSpan() != 1) return; - Length logicalHeight = cell->style()->logicalHeight(); + Length logicalHeight = cell->style().logicalHeight(); if (logicalHeight.isPositive() || (logicalHeight.isRelative() && logicalHeight.value() >= 0)) { Length cRowLogicalHeight = row.logicalHeight; switch (logicalHeight.type()) { case Percent: - if (!(cRowLogicalHeight.isPercent()) - || (cRowLogicalHeight.isPercent() && cRowLogicalHeight.percent() < logicalHeight.percent())) + if (!cRowLogicalHeight.isPercent() || cRowLogicalHeight.percent() < logicalHeight.percent()) row.logicalHeight = logicalHeight; break; case Fixed: - if (cRowLogicalHeight.type() < Percent + if (cRowLogicalHeight.isAuto() || cRowLogicalHeight.isRelative() || (cRowLogicalHeight.isFixed() && cRowLogicalHeight.value() < logicalHeight.value())) row.logicalHeight = logicalHeight; break; @@ -83,20 +82,16 @@ static inline void updateLogicalHeightForCell(RenderTableSection::RowStruct& row } } +RenderTableSection::RenderTableSection(Element& element, Ref<RenderStyle>&& style) + : RenderBox(element, WTFMove(style), 0) +{ + setInline(false); +} -RenderTableSection::RenderTableSection(Element* element) - : RenderBox(element) - , m_cCol(0) - , m_cRow(0) - , m_outerBorderStart(0) - , m_outerBorderEnd(0) - , m_outerBorderBefore(0) - , m_outerBorderAfter(0) - , m_needsCellRecalc(false) - , m_hasMultipleCellLevels(false) +RenderTableSection::RenderTableSection(Document& document, Ref<RenderStyle>&& style) + : RenderBox(document, WTFMove(style), 0) { - // init RenderObject attributes - setInline(false); // our object is not Inline + setInline(false); } RenderTableSection::~RenderTableSection() @@ -106,11 +101,11 @@ RenderTableSection::~RenderTableSection() void RenderTableSection::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { RenderBox::styleDidChange(diff, oldStyle); - propagateStyleToAnonymousChildren(); + propagateStyleToAnonymousChildren(PropagateToAllChildren); // If border was changed, notify table. RenderTable* table = this->table(); - if (table && !table->selfNeedsLayout() && !table->normalChildNeedsLayout() && oldStyle && oldStyle->border() != style()->border()) + if (table && oldStyle && oldStyle->border() != style().border()) table->invalidateCollapsedBorders(); } @@ -125,21 +120,22 @@ void RenderTableSection::willBeRemovedFromTree() void RenderTableSection::addChild(RenderObject* child, RenderObject* beforeChild) { - if (!child->isTableRow()) { + if (!is<RenderTableRow>(*child)) { RenderObject* last = beforeChild; if (!last) - last = lastChild(); + last = lastRow(); if (last && last->isAnonymous() && !last->isBeforeOrAfterContent()) { - if (beforeChild == last) - beforeChild = last->firstChild(); - last->addChild(child, beforeChild); + RenderTableRow& row = downcast<RenderTableRow>(*last); + if (beforeChild == &row) + beforeChild = row.firstCell(); + row.addChild(child, beforeChild); return; } if (beforeChild && !beforeChild->isAnonymous() && beforeChild->parent() == this) { RenderObject* row = beforeChild->previousSibling(); - if (row && row->isTableRow() && row->isAnonymous()) { - row->addChild(child); + if (is<RenderTableRow>(row) && row->isAnonymous()) { + downcast<RenderTableRow>(*row).addChild(child); return; } } @@ -147,14 +143,14 @@ void RenderTableSection::addChild(RenderObject* child, RenderObject* beforeChild // If beforeChild is inside an anonymous cell/row, insert into the cell or into // the anonymous row containing it, if there is one. RenderObject* lastBox = last; - while (lastBox && lastBox->parent()->isAnonymous() && !lastBox->isTableRow()) + while (lastBox && lastBox->parent()->isAnonymous() && !is<RenderTableRow>(*lastBox)) lastBox = lastBox->parent(); if (lastBox && lastBox->isAnonymous() && !lastBox->isBeforeOrAfterContent()) { - lastBox->addChild(child, beforeChild); + downcast<RenderTableRow>(*lastBox).addChild(child, beforeChild); return; } - RenderObject* row = RenderTableRow::createAnonymousWithParentRenderer(this); + RenderTableRow* row = RenderTableRow::createAnonymousWithParentRenderer(this); addChild(row, beforeChild); row->addChild(child); return; @@ -169,9 +165,9 @@ void RenderTableSection::addChild(RenderObject* child, RenderObject* beforeChild ensureRows(m_cRow); - RenderTableRow* row = toRenderTableRow(child); - m_grid[insertionRow].rowRenderer = row; - row->setRowIndex(insertionRow); + RenderTableRow& row = downcast<RenderTableRow>(*child); + m_grid[insertionRow].rowRenderer = &row; + row.setRowIndex(insertionRow); if (!beforeChild) setRowLogicalHeightToRowStyleLogicalHeightIfNotRelative(m_grid[insertionRow]); @@ -179,7 +175,7 @@ void RenderTableSection::addChild(RenderObject* child, RenderObject* beforeChild if (beforeChild && beforeChild->parent() != this) beforeChild = splitAnonymousBoxesAroundChild(beforeChild); - ASSERT(!beforeChild || beforeChild->isTableRow()); + ASSERT(!beforeChild || is<RenderTableRow>(*beforeChild)); RenderBox::addChild(child, beforeChild); } @@ -191,9 +187,9 @@ void RenderTableSection::ensureRows(unsigned numRows) unsigned oldSize = m_grid.size(); m_grid.grow(numRows); - unsigned effectiveColumnCount = max(1u, table()->numEffCols()); + unsigned effectiveColumnCount = std::max(1u, table()->numEffCols()); for (unsigned row = oldSize; row < m_grid.size(); ++row) - m_grid[row].row.grow(effectiveColumnCount); + m_grid[row].row.resizeToFit(effectiveColumnCount); } void RenderTableSection::addCell(RenderTableCell* cell, RenderTableRow* row) @@ -255,7 +251,7 @@ void RenderTableSection::addCell(RenderTableCell* cell, RenderTableRow* row) cell->setCol(table()->effColToCol(col)); } -int RenderTableSection::calcRowLogicalHeight() +LayoutUnit RenderTableSection::calcRowLogicalHeight() { #ifndef NDEBUG SetLayoutNeededForbiddenScope layoutForbiddenScope(this); @@ -265,10 +261,12 @@ int RenderTableSection::calcRowLogicalHeight() RenderTableCell* cell; - int spacing = table()->vBorderSpacing(); - - RenderView* viewRenderer = view(); - LayoutStateMaintainer statePusher(viewRenderer); + // We ignore the border-spacing on any non-top section as it is already included in the previous section's last row position. + LayoutUnit spacing = 0; + if (this == table()->topSection()) + spacing = table()->vBorderSpacing(); + + LayoutStateMaintainer statePusher(view()); m_rowPos.resize(m_grid.size() + 1); m_rowPos[0] = spacing; @@ -280,7 +278,7 @@ int RenderTableSection::calcRowLogicalHeight() LayoutUnit baselineDescent = 0; // Our base size is the biggest logical height from our cells' styles (excluding row spanning cells). - m_rowPos[r + 1] = max(m_rowPos[r] + minimumValueForLength(m_grid[r].logicalHeight, 0, viewRenderer).round(), 0); + m_rowPos[r + 1] = std::max(m_rowPos[r] + minimumValueForLength(m_grid[r].logicalHeight, 0), LayoutUnit::fromPixel(0)); Row& row = m_grid[r].row; unsigned totalCols = row.size(); @@ -318,43 +316,46 @@ int RenderTableSection::calcRowLogicalHeight() // For row spanning cells, |r| is the last row in the span. unsigned cellStartRow = cell->rowIndex(); - if (cell->hasOverrideHeight()) { + if (cell->hasOverrideLogicalContentHeight()) { if (!statePusher.didPush()) { // Technically, we should also push state for the row, but since // rows don't push a coordinate transform, that's not necessary. - statePusher.push(this, locationOffset()); + statePusher.push(*this, locationOffset()); } cell->clearIntrinsicPadding(); cell->clearOverrideSize(); - cell->setChildNeedsLayout(true, MarkOnlyThis); + cell->setChildNeedsLayout(MarkOnlyThis); cell->layoutIfNeeded(); } - int cellLogicalHeight = cell->logicalHeightForRowSizing(); - m_rowPos[r + 1] = max(m_rowPos[r + 1], m_rowPos[cellStartRow] + cellLogicalHeight); + LayoutUnit cellLogicalHeight = cell->logicalHeightForRowSizing(); + m_rowPos[r + 1] = std::max(m_rowPos[r + 1], m_rowPos[cellStartRow] + cellLogicalHeight); // Find out the baseline. The baseline is set on the first row in a rowspan. if (cell->isBaselineAligned()) { - LayoutUnit baselinePosition = cell->cellBaselinePosition(); - if (baselinePosition > cell->borderAndPaddingBefore()) { - m_grid[cellStartRow].baseline = max(m_grid[cellStartRow].baseline, baselinePosition); + LayoutUnit baselinePosition = cell->cellBaselinePosition() - cell->intrinsicPaddingBefore(); + LayoutUnit borderAndComputedPaddingBefore = cell->borderAndPaddingBefore() - cell->intrinsicPaddingBefore(); + if (baselinePosition > borderAndComputedPaddingBefore) { + m_grid[cellStartRow].baseline = std::max(m_grid[cellStartRow].baseline, baselinePosition); // The descent of a cell that spans multiple rows does not affect the height of the first row it spans, so don't let it // become the baseline descent applied to the rest of the row. Also we don't account for the baseline descent of // non-spanning cells when computing a spanning cell's extent. - int cellStartRowBaselineDescent = 0; + LayoutUnit cellStartRowBaselineDescent = 0; if (cell->rowSpan() == 1) { - baselineDescent = max(baselineDescent, cellLogicalHeight - (baselinePosition - cell->intrinsicPaddingBefore())); + baselineDescent = std::max(baselineDescent, cellLogicalHeight - baselinePosition); cellStartRowBaselineDescent = baselineDescent; } - m_rowPos[cellStartRow + 1] = max<int>(m_rowPos[cellStartRow + 1], m_rowPos[cellStartRow] + m_grid[cellStartRow].baseline + cellStartRowBaselineDescent); + m_rowPos[cellStartRow + 1] = std::max(m_rowPos[cellStartRow + 1], m_rowPos[cellStartRow] + m_grid[cellStartRow].baseline + cellStartRowBaselineDescent); } } } } // Add the border-spacing to our final position. - m_rowPos[r + 1] += m_grid[r].rowRenderer ? spacing : 0; - m_rowPos[r + 1] = max(m_rowPos[r + 1], m_rowPos[r]); + // Use table border-spacing even in non-top sections + spacing = table()->vBorderSpacing(); + m_rowPos[r + 1] += m_grid[r].rowRenderer ? spacing : LayoutUnit::fromPixel(0); + m_rowPos[r + 1] = std::max(m_rowPos[r + 1], m_rowPos[r]); } ASSERT(!needsLayout()); @@ -375,10 +376,11 @@ void RenderTableSection::layout() // can be called in a loop (e.g during parsing). Doing it now ensures we have a stable-enough structure. m_grid.shrinkToFit(); - LayoutStateMaintainer statePusher(view(), this, locationOffset(), hasTransform() || hasReflection() || style()->isFlippedBlocksWritingMode()); - - const Vector<int>& columnPos = table()->columnPositions(); - + LayoutStateMaintainer statePusher(view(), *this, locationOffset(), hasTransform() || hasReflection() || style().isFlippedBlocksWritingMode()); + bool paginated = view().layoutState()->isPaginated(); + + const Vector<LayoutUnit>& columnPos = table()->columnPositions(); + for (unsigned r = 0; r < m_grid.size(); ++r) { Row& row = m_grid[r].row; unsigned cols = row.size(); @@ -397,34 +399,38 @@ void RenderTableSection::layout() cspan -= table()->columns()[endCol].span; endCol++; } - int tableLayoutLogicalWidth = columnPos[endCol] - columnPos[startColumn] - table()->hBorderSpacing(); + LayoutUnit tableLayoutLogicalWidth = columnPos[endCol] - columnPos[startColumn] - table()->hBorderSpacing(); cell->setCellLogicalWidth(tableLayoutLogicalWidth); } - if (RenderTableRow* rowRenderer = m_grid[r].rowRenderer) + if (RenderTableRow* rowRenderer = m_grid[r].rowRenderer) { + if (!rowRenderer->needsLayout() && paginated && view().layoutState()->pageLogicalHeightChanged()) + rowRenderer->setChildNeedsLayout(MarkOnlyThis); + rowRenderer->layoutIfNeeded(); + } } statePusher.pop(); - setNeedsLayout(false); + clearNeedsLayout(); } -void RenderTableSection::distributeExtraLogicalHeightToPercentRows(int& extraLogicalHeight, int totalPercent) +void RenderTableSection::distributeExtraLogicalHeightToPercentRows(LayoutUnit& extraLogicalHeight, int totalPercent) { if (!totalPercent) return; unsigned totalRows = m_grid.size(); - int totalHeight = m_rowPos[totalRows] + extraLogicalHeight; - int totalLogicalHeightAdded = 0; - totalPercent = min(totalPercent, 100); - int rowHeight = m_rowPos[1] - m_rowPos[0]; + LayoutUnit totalHeight = m_rowPos[totalRows] + extraLogicalHeight; + LayoutUnit totalLogicalHeightAdded = 0; + totalPercent = std::min(totalPercent, 100); + LayoutUnit rowHeight = m_rowPos[1] - m_rowPos[0]; for (unsigned r = 0; r < totalRows; ++r) { if (totalPercent > 0 && m_grid[r].logicalHeight.isPercent()) { - int toAdd = min<int>(extraLogicalHeight, (totalHeight * m_grid[r].logicalHeight.percent() / 100) - rowHeight); + LayoutUnit toAdd = std::min<LayoutUnit>(extraLogicalHeight, (totalHeight * m_grid[r].logicalHeight.percent() / 100) - rowHeight); // If toAdd is negative, then we don't want to shrink the row (this bug // affected Outlook Web Access). - toAdd = max(0, toAdd); + toAdd = std::max(LayoutUnit::fromPixel(0), toAdd); totalLogicalHeightAdded += toAdd; extraLogicalHeight -= toAdd; totalPercent -= m_grid[r].logicalHeight.percent(); @@ -436,16 +442,16 @@ void RenderTableSection::distributeExtraLogicalHeightToPercentRows(int& extraLog } } -void RenderTableSection::distributeExtraLogicalHeightToAutoRows(int& extraLogicalHeight, unsigned autoRowsCount) +void RenderTableSection::distributeExtraLogicalHeightToAutoRows(LayoutUnit& extraLogicalHeight, unsigned autoRowsCount) { if (!autoRowsCount) return; - int totalLogicalHeightAdded = 0; + LayoutUnit totalLogicalHeightAdded = 0; for (unsigned r = 0; r < m_grid.size(); ++r) { if (autoRowsCount > 0 && m_grid[r].logicalHeight.isAuto()) { // Recomputing |extraLogicalHeightForRow| guarantees that we properly ditribute round |extraLogicalHeight|. - int extraLogicalHeightForRow = extraLogicalHeight / autoRowsCount; + LayoutUnit extraLogicalHeightForRow = extraLogicalHeight / autoRowsCount; totalLogicalHeightAdded += extraLogicalHeightForRow; extraLogicalHeight -= extraLogicalHeightForRow; --autoRowsCount; @@ -454,7 +460,7 @@ void RenderTableSection::distributeExtraLogicalHeightToAutoRows(int& extraLogica } } -void RenderTableSection::distributeRemainingExtraLogicalHeight(int& extraLogicalHeight) +void RenderTableSection::distributeRemainingExtraLogicalHeight(LayoutUnit& extraLogicalHeight) { unsigned totalRows = m_grid.size(); @@ -462,9 +468,9 @@ void RenderTableSection::distributeRemainingExtraLogicalHeight(int& extraLogical return; // FIXME: m_rowPos[totalRows] - m_rowPos[0] is the total rows' size. - int totalRowSize = m_rowPos[totalRows]; - int totalLogicalHeightAdded = 0; - int previousRowPosition = m_rowPos[0]; + LayoutUnit totalRowSize = m_rowPos[totalRows]; + LayoutUnit totalLogicalHeightAdded = 0; + LayoutUnit previousRowPosition = m_rowPos[0]; for (unsigned r = 0; r < totalRows; r++) { // weight with the original height totalLogicalHeightAdded += extraLogicalHeight * (m_rowPos[r + 1] - previousRowPosition) / totalRowSize; @@ -475,7 +481,7 @@ void RenderTableSection::distributeRemainingExtraLogicalHeight(int& extraLogical extraLogicalHeight -= totalLogicalHeightAdded; } -int RenderTableSection::distributeExtraLogicalHeightToRows(int extraLogicalHeight) +LayoutUnit RenderTableSection::distributeExtraLogicalHeightToRows(LayoutUnit extraLogicalHeight) { if (!extraLogicalHeight) return extraLogicalHeight; @@ -496,7 +502,7 @@ int RenderTableSection::distributeExtraLogicalHeightToRows(int extraLogicalHeigh totalPercent += m_grid[r].logicalHeight.percent(); } - int remainingExtraLogicalHeight = extraLogicalHeight; + LayoutUnit remainingExtraLogicalHeight = extraLogicalHeight; distributeExtraLogicalHeightToPercentRows(remainingExtraLogicalHeight, totalPercent); distributeExtraLogicalHeightToAutoRows(remainingExtraLogicalHeight, autoRowsCount); distributeRemainingExtraLogicalHeight(remainingExtraLogicalHeight); @@ -517,21 +523,25 @@ void RenderTableSection::layoutRows() setLogicalWidth(table()->contentLogicalWidth()); m_forceSlowPaintPathWithOverflowingCell = false; - int vspacing = table()->vBorderSpacing(); + LayoutUnit vspacing = table()->vBorderSpacing(); unsigned nEffCols = table()->numEffCols(); - LayoutStateMaintainer statePusher(view(), this, locationOffset(), hasTransform() || style()->isFlippedBlocksWritingMode()); + LayoutStateMaintainer statePusher(view(), *this, locationOffset(), hasTransform() || style().isFlippedBlocksWritingMode()); for (unsigned r = 0; r < totalRows; r++) { // Set the row's x/y position and width/height. if (RenderTableRow* rowRenderer = m_grid[r].rowRenderer) { + // FIXME: the x() position of the row should be table()->hBorderSpacing() so that it can + // report the correct offsetLeft. However, that will require a lot of rebaselining of test results. rowRenderer->setLocation(LayoutPoint(0, m_rowPos[r])); rowRenderer->setLogicalWidth(logicalWidth()); rowRenderer->setLogicalHeight(m_rowPos[r + 1] - m_rowPos[r] - vspacing); rowRenderer->updateLayerTransform(); + rowRenderer->clearOverflow(); + rowRenderer->addVisualEffectOverflow(); } - int rowHeightIncreaseForPagination = 0; + LayoutUnit rowHeightIncreaseForPagination = 0; for (unsigned c = 0; c < nEffCols; c++) { CellStruct& cs = cellAt(r, c); @@ -541,7 +551,7 @@ void RenderTableSection::layoutRows() continue; int rowIndex = cell->rowIndex(); - int rHeight = m_rowPos[rowIndex + cell->rowSpan()] - m_rowPos[rowIndex] - vspacing; + LayoutUnit rHeight = m_rowPos[rowIndex + cell->rowSpan()] - m_rowPos[rowIndex] - vspacing; // Force percent height children to lay themselves out again. // This will cause these children to grow to fill the cell. @@ -557,14 +567,14 @@ void RenderTableSection::layoutRows() // match the behavior perfectly, but we'll continue to refine it as we discover new // bugs. :) bool cellChildrenFlex = false; - bool flexAllChildren = cell->style()->logicalHeight().isFixed() - || (!table()->style()->logicalHeight().isAuto() && rHeight != cell->logicalHeight()); + bool flexAllChildren = cell->style().logicalHeight().isFixed() + || (!table()->style().logicalHeight().isAuto() && rHeight != cell->logicalHeight()); - for (RenderObject* o = cell->firstChild(); o; o = o->nextSibling()) { - if (!o->isText() && o->style()->logicalHeight().isPercent() && (flexAllChildren || ((o->isReplaced() || (o->isBox() && toRenderBox(o)->scrollsOverflow())) && !o->isTextControl()))) { + for (RenderObject* renderer = cell->firstChild(); renderer; renderer = renderer->nextSibling()) { + if (!is<RenderText>(*renderer) && renderer->style().logicalHeight().isPercentOrCalculated() && (flexAllChildren || ((renderer->isReplaced() || (is<RenderBox>(*renderer) && downcast<RenderBox>(*renderer).scrollsOverflow())) && !is<RenderTextControl>(*renderer)))) { // Tables with no sections do not flex. - if (!o->isTable() || toRenderTable(o)->hasSections()) { - o->setNeedsLayout(true, MarkOnlyThis); + if (!is<RenderTable>(*renderer) || downcast<RenderTable>(*renderer).hasSections()) { + renderer->setNeedsLayout(MarkOnlyThis); cellChildrenFlex = true; } } @@ -580,7 +590,7 @@ void RenderTableSection::layoutRows() while (box != cell) { if (box->normalChildNeedsLayout()) break; - box->setChildNeedsLayout(true, MarkOnlyThis); + box->setChildNeedsLayout(MarkOnlyThis); box = box->containingBlock(); ASSERT(box); if (!box) @@ -591,7 +601,7 @@ void RenderTableSection::layoutRows() } if (cellChildrenFlex) { - cell->setChildNeedsLayout(true, MarkOnlyThis); + cell->setChildNeedsLayout(MarkOnlyThis); // Alignment within a cell is based off the calculated // height, which becomes irrelevant once the cell has // been resized based off its percentage. @@ -602,7 +612,7 @@ void RenderTableSection::layoutRows() if (cell->isBaselineAligned()) { LayoutUnit baseline = cell->cellBaselinePosition(); if (baseline > cell->borderAndPaddingBefore()) - m_grid[r].baseline = max(m_grid[r].baseline, baseline); + m_grid[r].baseline = std::max(m_grid[r].baseline, baseline); } } @@ -612,24 +622,24 @@ void RenderTableSection::layoutRows() setLogicalPositionForCell(cell, c); - if (!cell->needsLayout() && view()->layoutState()->pageLogicalHeight() && view()->layoutState()->pageLogicalOffset(cell, cell->logicalTop()) != cell->pageLogicalOffset()) - cell->setChildNeedsLayout(true, MarkOnlyThis); + if (!cell->needsLayout() && view().layoutState()->pageLogicalHeight() && view().layoutState()->pageLogicalOffset(cell, cell->logicalTop()) != cell->pageLogicalOffset()) + cell->setChildNeedsLayout(MarkOnlyThis); cell->layoutIfNeeded(); // FIXME: Make pagination work with vertical tables. - if (view()->layoutState()->pageLogicalHeight() && cell->logicalHeight() != rHeight) { + if (view().layoutState()->pageLogicalHeight() && cell->logicalHeight() != rHeight) { // FIXME: Pagination might have made us change size. For now just shrink or grow the cell to fit without doing a relayout. // We'll also do a basic increase of the row height to accommodate the cell if it's bigger, but this isn't quite right // either. It's at least stable though and won't result in an infinite # of relayouts that may never stabilize. if (cell->logicalHeight() > rHeight) - rowHeightIncreaseForPagination = max<int>(rowHeightIncreaseForPagination, cell->logicalHeight() - rHeight); + rowHeightIncreaseForPagination = std::max(rowHeightIncreaseForPagination, cell->logicalHeight() - rHeight); cell->setLogicalHeight(rHeight); } LayoutSize childOffset(cell->location() - oldCellRect.location()); if (childOffset.width() || childOffset.height()) { - view()->addLayoutDelta(childOffset); + view().addLayoutDelta(childOffset); // If the child moved, we have to repaint it as well as any floating/positioned // descendants. An exception is if we need a layout. In this case, we know we're going to @@ -667,10 +677,10 @@ void RenderTableSection::computeOverflowFromCells() void RenderTableSection::computeOverflowFromCells(unsigned totalRows, unsigned nEffCols) { - m_overflow.clear(); + clearOverflow(); m_overflowingCells.clear(); unsigned totalCellsCount = nEffCols * totalRows; - int maxAllowedOverflowingCellsCount = totalCellsCount < gMinTableSizeToUseFastPaintPathWithOverflowingCell ? 0 : gMaxAllowedOverflowingCellRatioForFastPaintPath * totalCellsCount; + unsigned maxAllowedOverflowingCellsCount = totalCellsCount < gMinTableSizeToUseFastPaintPathWithOverflowingCell ? 0 : gMaxAllowedOverflowingCellRatioForFastPaintPath * totalCellsCount; #ifndef NDEBUG bool hasOverflowingCell = false; @@ -699,25 +709,24 @@ void RenderTableSection::computeOverflowFromCells(unsigned totalRows, unsigned n } } } - ASSERT(hasOverflowingCell == this->hasOverflowingCell()); } -int RenderTableSection::calcOuterBorderBefore() const +LayoutUnit RenderTableSection::calcOuterBorderBefore() const { unsigned totalCols = table()->numEffCols(); if (!m_grid.size() || !totalCols) return 0; - unsigned borderWidth = 0; + LayoutUnit borderWidth = 0; - const BorderValue& sb = style()->borderBefore(); + const BorderValue& sb = style().borderBefore(); if (sb.style() == BHIDDEN) return -1; if (sb.style() > BHIDDEN) borderWidth = sb.width(); - const BorderValue& rb = firstChild()->style()->borderBefore(); + const BorderValue& rb = firstRow()->style().borderBefore(); if (rb.style() == BHIDDEN) return -1; if (rb.style() > BHIDDEN && rb.width() > borderWidth) @@ -728,11 +737,11 @@ int RenderTableSection::calcOuterBorderBefore() const const CellStruct& current = cellAt(0, c); if (current.inColSpan || !current.hasCells()) continue; - const BorderValue& cb = current.primaryCell()->style()->borderBefore(); // FIXME: Make this work with perpendicular and flipped cells. + const BorderValue& cb = current.primaryCell()->style().borderBefore(); // FIXME: Make this work with perpendicular and flipped cells. // FIXME: Don't repeat for the same col group RenderTableCol* colGroup = table()->colElement(c); if (colGroup) { - const BorderValue& gb = colGroup->style()->borderBefore(); + const BorderValue& gb = colGroup->style().borderBefore(); if (gb.style() == BHIDDEN || cb.style() == BHIDDEN) continue; allHidden = false; @@ -751,24 +760,24 @@ int RenderTableSection::calcOuterBorderBefore() const if (allHidden) return -1; - return borderWidth / 2; + return floorToInt(borderWidth / 2); } -int RenderTableSection::calcOuterBorderAfter() const +LayoutUnit RenderTableSection::calcOuterBorderAfter() const { unsigned totalCols = table()->numEffCols(); if (!m_grid.size() || !totalCols) return 0; - unsigned borderWidth = 0; + LayoutUnit borderWidth = 0; - const BorderValue& sb = style()->borderAfter(); + const BorderValue& sb = style().borderAfter(); if (sb.style() == BHIDDEN) return -1; if (sb.style() > BHIDDEN) borderWidth = sb.width(); - const BorderValue& rb = lastChild()->style()->borderAfter(); + const BorderValue& rb = lastRow()->style().borderAfter(); if (rb.style() == BHIDDEN) return -1; if (rb.style() > BHIDDEN && rb.width() > borderWidth) @@ -779,11 +788,11 @@ int RenderTableSection::calcOuterBorderAfter() const const CellStruct& current = cellAt(m_grid.size() - 1, c); if (current.inColSpan || !current.hasCells()) continue; - const BorderValue& cb = current.primaryCell()->style()->borderAfter(); // FIXME: Make this work with perpendicular and flipped cells. + const BorderValue& cb = current.primaryCell()->style().borderAfter(); // FIXME: Make this work with perpendicular and flipped cells. // FIXME: Don't repeat for the same col group RenderTableCol* colGroup = table()->colElement(c); if (colGroup) { - const BorderValue& gb = colGroup->style()->borderAfter(); + const BorderValue& gb = colGroup->style().borderAfter(); if (gb.style() == BHIDDEN || cb.style() == BHIDDEN) continue; allHidden = false; @@ -802,25 +811,25 @@ int RenderTableSection::calcOuterBorderAfter() const if (allHidden) return -1; - return (borderWidth + 1) / 2; + return floorToInt((borderWidth + 1) / 2); } -int RenderTableSection::calcOuterBorderStart() const +LayoutUnit RenderTableSection::calcOuterBorderStart() const { unsigned totalCols = table()->numEffCols(); if (!m_grid.size() || !totalCols) return 0; - unsigned borderWidth = 0; + LayoutUnit borderWidth = 0; - const BorderValue& sb = style()->borderStart(); + const BorderValue& sb = style().borderStart(); if (sb.style() == BHIDDEN) return -1; if (sb.style() > BHIDDEN) borderWidth = sb.width(); if (RenderTableCol* colGroup = table()->colElement(0)) { - const BorderValue& gb = colGroup->style()->borderStart(); + const BorderValue& gb = colGroup->style().borderStart(); if (gb.style() == BHIDDEN) return -1; if (gb.style() > BHIDDEN && gb.width() > borderWidth) @@ -833,8 +842,8 @@ int RenderTableSection::calcOuterBorderStart() const if (!current.hasCells()) continue; // FIXME: Don't repeat for the same cell - const BorderValue& cb = current.primaryCell()->style()->borderStart(); // FIXME: Make this work with perpendicular and flipped cells. - const BorderValue& rb = current.primaryCell()->parent()->style()->borderStart(); + const BorderValue& cb = current.primaryCell()->style().borderStart(); // FIXME: Make this work with perpendicular and flipped cells. + const BorderValue& rb = current.primaryCell()->parent()->style().borderStart(); if (cb.style() == BHIDDEN || rb.style() == BHIDDEN) continue; allHidden = false; @@ -846,25 +855,25 @@ int RenderTableSection::calcOuterBorderStart() const if (allHidden) return -1; - return (borderWidth + (table()->style()->isLeftToRightDirection() ? 0 : 1)) / 2; + return floorToInt((borderWidth + (table()->style().isLeftToRightDirection() ? 0 : 1)) / 2); } -int RenderTableSection::calcOuterBorderEnd() const +LayoutUnit RenderTableSection::calcOuterBorderEnd() const { unsigned totalCols = table()->numEffCols(); if (!m_grid.size() || !totalCols) return 0; - unsigned borderWidth = 0; + LayoutUnit borderWidth = 0; - const BorderValue& sb = style()->borderEnd(); + const BorderValue& sb = style().borderEnd(); if (sb.style() == BHIDDEN) return -1; if (sb.style() > BHIDDEN) borderWidth = sb.width(); if (RenderTableCol* colGroup = table()->colElement(totalCols - 1)) { - const BorderValue& gb = colGroup->style()->borderEnd(); + const BorderValue& gb = colGroup->style().borderEnd(); if (gb.style() == BHIDDEN) return -1; if (gb.style() > BHIDDEN && gb.width() > borderWidth) @@ -877,8 +886,8 @@ int RenderTableSection::calcOuterBorderEnd() const if (!current.hasCells()) continue; // FIXME: Don't repeat for the same cell - const BorderValue& cb = current.primaryCell()->style()->borderEnd(); // FIXME: Make this work with perpendicular and flipped cells. - const BorderValue& rb = current.primaryCell()->parent()->style()->borderEnd(); + const BorderValue& cb = current.primaryCell()->style().borderEnd(); // FIXME: Make this work with perpendicular and flipped cells. + const BorderValue& rb = current.primaryCell()->parent()->style().borderEnd(); if (cb.style() == BHIDDEN || rb.style() == BHIDDEN) continue; allHidden = false; @@ -890,7 +899,7 @@ int RenderTableSection::calcOuterBorderEnd() const if (allHidden) return -1; - return (borderWidth + (table()->style()->isLeftToRightDirection() ? 1 : 0)) / 2; + return floorToInt((borderWidth + (table()->style().isLeftToRightDirection() ? 1 : 0)) / 2); } void RenderTableSection::recalcOuterBorder() @@ -901,26 +910,28 @@ void RenderTableSection::recalcOuterBorder() m_outerBorderEnd = calcOuterBorderEnd(); } -int RenderTableSection::firstLineBoxBaseline() const +Optional<int> RenderTableSection::firstLineBaseline() const { if (!m_grid.size()) - return -1; + return Optional<int>(); int firstLineBaseline = m_grid[0].baseline; if (firstLineBaseline) - return firstLineBaseline + m_rowPos[0]; + return firstLineBaseline + roundToInt(m_rowPos[0]); - firstLineBaseline = -1; + Optional<int> result; const Row& firstRow = m_grid[0].row; for (size_t i = 0; i < firstRow.size(); ++i) { const CellStruct& cs = firstRow.at(i); const RenderTableCell* cell = cs.primaryCell(); // Only cells with content have a baseline - if (cell && cell->contentLogicalHeight()) - firstLineBaseline = max<int>(firstLineBaseline, cell->logicalTop() + cell->borderAndPaddingBefore() + cell->contentLogicalHeight()); + if (cell && cell->contentLogicalHeight()) { + int candidate = roundToInt(cell->logicalTop() + cell->borderAndPaddingBefore() + cell->contentLogicalHeight()); + result = std::max(result.valueOr(candidate), candidate); + } } - return firstLineBaseline; + return result; } void RenderTableSection::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) @@ -945,7 +956,7 @@ void RenderTableSection::paint(PaintInfo& paintInfo, const LayoutPoint& paintOff if (pushedClip) popContentsClip(paintInfo, phase, adjustedPaintOffset); - if ((phase == PaintPhaseOutline || phase == PaintPhaseSelfOutline) && style()->visibility() == VISIBLE) + if ((phase == PaintPhaseOutline || phase == PaintPhaseSelfOutline) && style().visibility() == VISIBLE) paintOutline(paintInfo, LayoutRect(adjustedPaintOffset, size())); } @@ -968,13 +979,13 @@ void RenderTableSection::paintCell(RenderTableCell* cell, PaintInfo& paintInfo, { LayoutPoint cellPoint = flipForWritingModeForChild(cell, paintOffset); PaintPhase paintPhase = paintInfo.phase; - RenderTableRow* row = toRenderTableRow(cell->parent()); + RenderTableRow& row = downcast<RenderTableRow>(*cell->parent()); if (paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChildBlockBackground) { // We need to handle painting a stack of backgrounds. This stack (from bottom to top) consists of // the column group, column, row group, row, and then the cell. RenderTableCol* column = table()->colElement(cell->col()); - RenderTableCol* columnGroup = column ? column->enclosingColumnGroup() : 0; + RenderTableCol* columnGroup = column ? column->enclosingColumnGroup() : nullptr; // Column groups and columns first. // FIXME: Columns and column groups do not currently support opacity, and they are being painted "too late" in @@ -989,10 +1000,10 @@ void RenderTableSection::paintCell(RenderTableCell* cell, PaintInfo& paintInfo, // Paint the row next, but only if it doesn't have a layer. If a row has a layer, it will be responsible for // painting the row background for the cell. - if (!row->hasSelfPaintingLayer()) - cell->paintBackgroundsBehindCell(paintInfo, cellPoint, row); + if (!row.hasSelfPaintingLayer()) + cell->paintBackgroundsBehindCell(paintInfo, cellPoint, &row); } - if ((!cell->hasSelfPaintingLayer() && !row->hasSelfPaintingLayer())) + if ((!cell->hasSelfPaintingLayer() && !row.hasSelfPaintingLayer())) cell->paint(paintInfo, cellPoint); } @@ -1002,12 +1013,12 @@ LayoutRect RenderTableSection::logicalRectForWritingModeAndDirection(const Layou flipForWritingMode(tableAlignedRect); - if (!style()->isHorizontalWritingMode()) + if (!style().isHorizontalWritingMode()) tableAlignedRect = tableAlignedRect.transposedRect(); - const Vector<int>& columnPos = table()->columnPositions(); + const Vector<LayoutUnit>& columnPos = table()->columnPositions(); // FIXME: The table's direction should determine our row's direction, not the section's (see bug 96691). - if (!style()->isLeftToRightDirection()) + if (!style().isLeftToRightDirection()) tableAlignedRect.setX(columnPos[columnPos.size() - 1] - tableAlignedRect.maxX()); return tableAlignedRect; @@ -1018,14 +1029,14 @@ CellSpan RenderTableSection::dirtiedRows(const LayoutRect& damageRect) const if (m_forceSlowPaintPathWithOverflowingCell) return fullTableRowSpan(); - CellSpan coveredRows = spannedRows(damageRect); + CellSpan coveredRows = spannedRows(damageRect, IncludeAllIntersectingCells); // To repaint the border we might need to repaint first or last row even if they are not spanned themselves. - if (coveredRows.start() >= m_rowPos.size() - 1 && m_rowPos[m_rowPos.size() - 1] + table()->outerBorderAfter() >= damageRect.y()) - --coveredRows.start(); + if (coveredRows.start >= m_rowPos.size() - 1 && m_rowPos[m_rowPos.size() - 1] + table()->outerBorderAfter() >= damageRect.y()) + --coveredRows.start; - if (!coveredRows.end() && m_rowPos[0] - table()->outerBorderBefore() <= damageRect.maxY()) - ++coveredRows.end(); + if (!coveredRows.end && m_rowPos[0] - table()->outerBorderBefore() <= damageRect.maxY()) + ++coveredRows.end; return coveredRows; } @@ -1035,23 +1046,25 @@ CellSpan RenderTableSection::dirtiedColumns(const LayoutRect& damageRect) const if (m_forceSlowPaintPathWithOverflowingCell) return fullTableColumnSpan(); - CellSpan coveredColumns = spannedColumns(damageRect); + CellSpan coveredColumns = spannedColumns(damageRect, IncludeAllIntersectingCells); - const Vector<int>& columnPos = table()->columnPositions(); + const Vector<LayoutUnit>& columnPos = table()->columnPositions(); // To repaint the border we might need to repaint first or last column even if they are not spanned themselves. - if (coveredColumns.start() >= columnPos.size() - 1 && columnPos[columnPos.size() - 1] + table()->outerBorderEnd() >= damageRect.x()) - --coveredColumns.start(); + if (coveredColumns.start >= columnPos.size() - 1 && columnPos[columnPos.size() - 1] + table()->outerBorderEnd() >= damageRect.x()) + --coveredColumns.start; - if (!coveredColumns.end() && columnPos[0] - table()->outerBorderStart() <= damageRect.maxX()) - ++coveredColumns.end(); + if (!coveredColumns.end && columnPos[0] - table()->outerBorderStart() <= damageRect.maxX()) + ++coveredColumns.end; return coveredColumns; } -CellSpan RenderTableSection::spannedRows(const LayoutRect& flippedRect) const +CellSpan RenderTableSection::spannedRows(const LayoutRect& flippedRect, ShouldIncludeAllIntersectingCells shouldIncludeAllIntersectionCells) const { // Find the first row that starts after rect top. unsigned nextRow = std::upper_bound(m_rowPos.begin(), m_rowPos.end(), flippedRect.y()) - m_rowPos.begin(); + if (shouldIncludeAllIntersectionCells == IncludeAllIntersectingCells && nextRow && m_rowPos[nextRow - 1] == flippedRect.y()) + --nextRow; if (nextRow == m_rowPos.size()) return CellSpan(m_rowPos.size() - 1, m_rowPos.size() - 1); // After all rows. @@ -1063,7 +1076,7 @@ CellSpan RenderTableSection::spannedRows(const LayoutRect& flippedRect) const if (m_rowPos[nextRow] >= flippedRect.maxY()) endRow = nextRow; else { - endRow = std::upper_bound(m_rowPos.begin() + nextRow, m_rowPos.end(), flippedRect.maxY()) - m_rowPos.begin(); + endRow = std::upper_bound(m_rowPos.begin() + static_cast<int32_t>(nextRow), m_rowPos.end(), flippedRect.maxY()) - m_rowPos.begin(); if (endRow == m_rowPos.size()) endRow = m_rowPos.size() - 1; } @@ -1071,9 +1084,9 @@ CellSpan RenderTableSection::spannedRows(const LayoutRect& flippedRect) const return CellSpan(startRow, endRow); } -CellSpan RenderTableSection::spannedColumns(const LayoutRect& flippedRect) const +CellSpan RenderTableSection::spannedColumns(const LayoutRect& flippedRect, ShouldIncludeAllIntersectingCells shouldIncludeAllIntersectionCells) const { - const Vector<int>& columnPos = table()->columnPositions(); + const Vector<LayoutUnit>& columnPos = table()->columnPositions(); // Find the first column that starts after rect left. // lower_bound doesn't handle the edge between two cells properly as it would wrongly return the @@ -1081,6 +1094,8 @@ CellSpan RenderTableSection::spannedColumns(const LayoutRect& flippedRect) const // upper_bound on the other hand properly returns the cell on the logical bottom/right, which also // matches the behavior of other browsers. unsigned nextColumn = std::upper_bound(columnPos.begin(), columnPos.end(), flippedRect.x()) - columnPos.begin(); + if (shouldIncludeAllIntersectionCells == IncludeAllIntersectingCells && nextColumn && columnPos[nextColumn - 1] == flippedRect.x()) + --nextColumn; if (nextColumn == columnPos.size()) return CellSpan(columnPos.size() - 1, columnPos.size() - 1); // After all columns. @@ -1092,7 +1107,7 @@ CellSpan RenderTableSection::spannedColumns(const LayoutRect& flippedRect) const if (columnPos[nextColumn] >= flippedRect.maxX()) endColumn = nextColumn; else { - endColumn = std::upper_bound(columnPos.begin() + nextColumn, columnPos.end(), flippedRect.maxX()) - columnPos.begin(); + endColumn = std::upper_bound(columnPos.begin() + static_cast<int32_t>(nextColumn), columnPos.end(), flippedRect.maxX()) - columnPos.begin(); if (endColumn == columnPos.size()) endColumn = columnPos.size() - 1; } @@ -1100,49 +1115,178 @@ CellSpan RenderTableSection::spannedColumns(const LayoutRect& flippedRect) const return CellSpan(startColumn, endColumn); } +void RenderTableSection::paintRowGroupBorder(const PaintInfo& paintInfo, bool antialias, LayoutRect rect, BoxSide side, CSSPropertyID borderColor, EBorderStyle borderStyle, EBorderStyle tableBorderStyle) +{ + if (tableBorderStyle == BHIDDEN) + return; + rect.intersect(paintInfo.rect); + if (rect.isEmpty()) + return; + drawLineForBoxSide(paintInfo.context(), rect, side, style().visitedDependentColor(borderColor), borderStyle, 0, 0, antialias); +} + +LayoutUnit RenderTableSection::offsetLeftForRowGroupBorder(RenderTableCell* cell, const LayoutRect& rowGroupRect, unsigned row) +{ + if (style().isHorizontalWritingMode()) { + if (style().isLeftToRightDirection()) + return cell ? cell->x() + cell->width() : LayoutUnit::fromPixel(0); + return -outerBorderLeft(&style()); + } + bool isLastRow = row + 1 == m_grid.size(); + return rowGroupRect.width() - m_rowPos[row + 1] + (isLastRow ? -outerBorderLeft(&style()) : LayoutUnit::fromPixel(0)); +} -void RenderTableSection::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffset) +LayoutUnit RenderTableSection::offsetTopForRowGroupBorder(RenderTableCell* cell, BoxSide borderSide, unsigned row) { - PaintPhase paintPhase = paintInfo.phase; + bool isLastRow = row + 1 == m_grid.size(); + if (style().isHorizontalWritingMode()) + return m_rowPos[row] + (!row && borderSide == BSRight ? -outerBorderTop(&style()) : isLastRow && borderSide == BSLeft ? outerBorderTop(&style()) : LayoutUnit::fromPixel(0)); + if (style().isLeftToRightDirection()) + return (cell ? cell->y() + cell->height() : LayoutUnit::fromPixel(0)) + (borderSide == BSLeft ? outerBorderTop(&style()) : LayoutUnit::fromPixel(0)); + return borderSide == BSRight ? -outerBorderTop(&style()) : LayoutUnit::fromPixel(0); +} +LayoutUnit RenderTableSection::verticalRowGroupBorderHeight(RenderTableCell* cell, const LayoutRect& rowGroupRect, unsigned row) +{ + bool isLastRow = row + 1 == m_grid.size(); + if (style().isHorizontalWritingMode()) + return m_rowPos[row + 1] - m_rowPos[row] + (!row ? outerBorderTop(&style()) : isLastRow ? outerBorderBottom(&style()) : LayoutUnit::fromPixel(0)); + if (style().isLeftToRightDirection()) + return rowGroupRect.height() - (cell ? cell->y() + cell->height() : LayoutUnit::fromPixel(0)) + outerBorderBottom(&style()); + return cell ? rowGroupRect.height() - (cell->y() - cell->height()) : LayoutUnit::fromPixel(0); +} + +LayoutUnit RenderTableSection::horizontalRowGroupBorderWidth(RenderTableCell* cell, const LayoutRect& rowGroupRect, unsigned row, unsigned column) +{ + if (style().isHorizontalWritingMode()) { + if (style().isLeftToRightDirection()) + return rowGroupRect.width() - (cell ? cell->x() + cell->width() : LayoutUnit::fromPixel(0)) + (!column ? outerBorderLeft(&style()) : column == table()->numEffCols() ? outerBorderRight(&style()) : LayoutUnit::fromPixel(0)); + return cell ? rowGroupRect.width() - (cell->x() - cell->width()) : LayoutUnit::fromPixel(0); + } + bool isLastRow = row + 1 == m_grid.size(); + return m_rowPos[row + 1] - m_rowPos[row] + (isLastRow ? outerBorderLeft(&style()) : !row ? outerBorderRight(&style()) : LayoutUnit::fromPixel(0)); +} + +void RenderTableSection::paintRowGroupBorderIfRequired(const PaintInfo& paintInfo, const LayoutPoint& paintOffset, unsigned row, unsigned column, BoxSide borderSide, RenderTableCell* cell) +{ + if (table()->currentBorderValue()->precedence() > BROWGROUP) + return; + if (paintInfo.context().paintingDisabled()) + return; + + const RenderStyle& style = this->style(); + bool antialias = shouldAntialiasLines(paintInfo.context()); + LayoutRect rowGroupRect = LayoutRect(paintOffset, size()); + rowGroupRect.moveBy(-LayoutPoint(outerBorderLeft(&style), (borderSide == BSRight) ? LayoutUnit::fromPixel(0) : outerBorderTop(&style))); + + switch (borderSide) { + case BSTop: + paintRowGroupBorder(paintInfo, antialias, LayoutRect(paintOffset.x() + offsetLeftForRowGroupBorder(cell, rowGroupRect, row), rowGroupRect.y(), + horizontalRowGroupBorderWidth(cell, rowGroupRect, row, column), style.borderTop().width()), BSTop, CSSPropertyBorderTopColor, style.borderTopStyle(), table()->style().borderTopStyle()); + break; + case BSBottom: + paintRowGroupBorder(paintInfo, antialias, LayoutRect(paintOffset.x() + offsetLeftForRowGroupBorder(cell, rowGroupRect, row), rowGroupRect.y() + rowGroupRect.height(), + horizontalRowGroupBorderWidth(cell, rowGroupRect, row, column), style.borderBottom().width()), BSBottom, CSSPropertyBorderBottomColor, style.borderBottomStyle(), table()->style().borderBottomStyle()); + break; + case BSLeft: + paintRowGroupBorder(paintInfo, antialias, LayoutRect(rowGroupRect.x(), rowGroupRect.y() + offsetTopForRowGroupBorder(cell, borderSide, row), style.borderLeft().width(), + verticalRowGroupBorderHeight(cell, rowGroupRect, row)), BSLeft, CSSPropertyBorderLeftColor, style.borderLeftStyle(), table()->style().borderLeftStyle()); + break; + case BSRight: + paintRowGroupBorder(paintInfo, antialias, LayoutRect(rowGroupRect.x() + rowGroupRect.width(), rowGroupRect.y() + offsetTopForRowGroupBorder(cell, borderSide, row), style.borderRight().width(), + verticalRowGroupBorderHeight(cell, rowGroupRect, row)), BSRight, CSSPropertyBorderRightColor, style.borderRightStyle(), table()->style().borderRightStyle()); + break; + default: + break; + } + +} + +static BoxSide physicalBorderForDirection(RenderStyle* styleForCellFlow, CollapsedBorderSide side) +{ + + switch (side) { + case CBSStart: + if (styleForCellFlow->isHorizontalWritingMode()) + return styleForCellFlow->isLeftToRightDirection() ? BSLeft : BSRight; + return styleForCellFlow->isLeftToRightDirection() ? BSTop : BSBottom; + case CBSEnd: + if (styleForCellFlow->isHorizontalWritingMode()) + return styleForCellFlow->isLeftToRightDirection() ? BSRight : BSLeft; + return styleForCellFlow->isLeftToRightDirection() ? BSBottom : BSTop; + case CBSBefore: + if (styleForCellFlow->isHorizontalWritingMode()) + return BSTop; + return styleForCellFlow->isLeftToRightDirection() ? BSRight : BSLeft; + case CBSAfter: + if (styleForCellFlow->isHorizontalWritingMode()) + return BSBottom; + return styleForCellFlow->isLeftToRightDirection() ? BSLeft : BSRight; + default: + ASSERT_NOT_REACHED(); + return BSLeft; + } +} + +void RenderTableSection::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffset) +{ LayoutRect localRepaintRect = paintInfo.rect; localRepaintRect.moveBy(-paintOffset); - localRepaintRect.inflate(maximalOutlineSize(paintPhase)); LayoutRect tableAlignedRect = logicalRectForWritingModeAndDirection(localRepaintRect); CellSpan dirtiedRows = this->dirtiedRows(tableAlignedRect); CellSpan dirtiedColumns = this->dirtiedColumns(tableAlignedRect); - if (dirtiedColumns.start() < dirtiedColumns.end()) { + if (dirtiedColumns.start < dirtiedColumns.end) { if (!m_hasMultipleCellLevels && !m_overflowingCells.size()) { if (paintInfo.phase == PaintPhaseCollapsedTableBorders) { // Collapsed borders are painted from the bottom right to the top left so that precedence // due to cell position is respected. We need to paint one row beyond the topmost dirtied // row to calculate its collapsed border value. - unsigned startRow = dirtiedRows.start() ? dirtiedRows.start() - 1 : 0; - for (unsigned r = dirtiedRows.end(); r > startRow; r--) { + unsigned startRow = dirtiedRows.start ? dirtiedRows.start - 1 : 0; + for (unsigned r = dirtiedRows.end; r > startRow; r--) { unsigned row = r - 1; - for (unsigned c = dirtiedColumns.end(); c > dirtiedColumns.start(); c--) { + bool shouldPaintRowGroupBorder = false; + for (unsigned c = dirtiedColumns.end; c > dirtiedColumns.start; c--) { unsigned col = c - 1; CellStruct& current = cellAt(row, col); RenderTableCell* cell = current.primaryCell(); - if (!cell || (row > dirtiedRows.start() && primaryCellAt(row - 1, col) == cell) || (col > dirtiedColumns.start() && primaryCellAt(row, col - 1) == cell)) + if (!cell) { + if (!c) + paintRowGroupBorderIfRequired(paintInfo, paintOffset, row, col, physicalBorderForDirection(&style(), CBSStart)); + else if (c == table()->numEffCols()) + paintRowGroupBorderIfRequired(paintInfo, paintOffset, row, col, physicalBorderForDirection(&style(), CBSEnd)); + shouldPaintRowGroupBorder = true; continue; + } + if ((row > dirtiedRows.start && primaryCellAt(row - 1, col) == cell) || (col > dirtiedColumns.start && primaryCellAt(row, col - 1) == cell)) + continue; + + // If we had a run of null cells paint their corresponding section of the row group's border if necessary. Note that + // this will only happen once within a row as the null cells will always be clustered together on one end of the row. + if (shouldPaintRowGroupBorder) { + if (r == m_grid.size()) + paintRowGroupBorderIfRequired(paintInfo, paintOffset, row, col, physicalBorderForDirection(&style(), CBSAfter), cell); + else if (!row && !table()->sectionAbove(this)) + paintRowGroupBorderIfRequired(paintInfo, paintOffset, row, col, physicalBorderForDirection(&style(), CBSBefore), cell); + shouldPaintRowGroupBorder = false; + } + LayoutPoint cellPoint = flipForWritingModeForChild(cell, paintOffset); cell->paintCollapsedBorders(paintInfo, cellPoint); } } } else { // Draw the dirty cells in the order that they appear. - for (unsigned r = dirtiedRows.start(); r < dirtiedRows.end(); r++) { + for (unsigned r = dirtiedRows.start; r < dirtiedRows.end; r++) { RenderTableRow* row = m_grid[r].rowRenderer; if (row && !row->hasSelfPaintingLayer()) row->paintOutlineForRowIfNeeded(paintInfo, paintOffset); - for (unsigned c = dirtiedColumns.start(); c < dirtiedColumns.end(); c++) { + for (unsigned c = dirtiedColumns.start; c < dirtiedColumns.end; c++) { CellStruct& current = cellAt(r, c); RenderTableCell* cell = current.primaryCell(); - if (!cell || (r > dirtiedRows.start() && primaryCellAt(r - 1, c) == cell) || (c > dirtiedColumns.start() && primaryCellAt(r, c - 1) == cell)) + if (!cell || (r > dirtiedRows.start && primaryCellAt(r - 1, c) == cell) || (c > dirtiedColumns.start && primaryCellAt(r, c - 1) == cell)) continue; paintCell(cell, paintInfo, paintOffset); } @@ -1162,11 +1306,11 @@ void RenderTableSection::paintObject(PaintInfo& paintInfo, const LayoutPoint& pa HashSet<RenderTableCell*> spanningCells; - for (unsigned r = dirtiedRows.start(); r < dirtiedRows.end(); r++) { + for (unsigned r = dirtiedRows.start; r < dirtiedRows.end; r++) { RenderTableRow* row = m_grid[r].rowRenderer; if (row && !row->hasSelfPaintingLayer()) row->paintOutlineForRowIfNeeded(paintInfo, paintOffset); - for (unsigned c = dirtiedColumns.start(); c < dirtiedColumns.end(); c++) { + for (unsigned c = dirtiedColumns.start; c < dirtiedColumns.end; c++) { CellStruct& current = cellAt(r, c); if (!current.hasCells()) continue; @@ -1212,39 +1356,31 @@ void RenderTableSection::imageChanged(WrappedImagePtr, const IntRect*) void RenderTableSection::recalcCells() { ASSERT(m_needsCellRecalc); - // We reset the flag here to ensure that |addCell| works. This is safe to do as - // fillRowsWithDefaultStartingAtPosition makes sure we match the table's columns - // representation. + // We reset the flag here to ensure that addCell() works. This is safe to do because we clear the grid + // and update its dimensions to be consistent with the table's column representation before we rebuild + // the grid using addCell(). m_needsCellRecalc = false; m_cCol = 0; m_cRow = 0; m_grid.clear(); - for (RenderObject* row = firstChild(); row; row = row->nextSibling()) { - if (row->isTableRow()) { - unsigned insertionRow = m_cRow; - m_cRow++; - m_cCol = 0; - ensureRows(m_cRow); - - RenderTableRow* tableRow = toRenderTableRow(row); - m_grid[insertionRow].rowRenderer = tableRow; - tableRow->setRowIndex(insertionRow); - setRowLogicalHeightToRowStyleLogicalHeightIfNotRelative(m_grid[insertionRow]); + for (RenderTableRow* row = firstRow(); row; row = row->nextRow()) { + unsigned insertionRow = m_cRow; + m_cRow++; + m_cCol = 0; + ensureRows(m_cRow); - for (RenderObject* cell = row->firstChild(); cell; cell = cell->nextSibling()) { - if (!cell->isTableCell()) - continue; + m_grid[insertionRow].rowRenderer = row; + row->setRowIndex(insertionRow); + setRowLogicalHeightToRowStyleLogicalHeightIfNotRelative(m_grid[insertionRow]); - RenderTableCell* tableCell = toRenderTableCell(cell); - addCell(tableCell, tableRow); - } - } + for (RenderTableCell* cell = row->firstCell(); cell; cell = cell->nextCell()) + addCell(cell, row); } m_grid.shrinkToFit(); - setNeedsLayout(true); + setNeedsLayout(); } // FIXME: This function could be made O(1) in certain cases (like for the non-most-constrainive cells' case). @@ -1255,23 +1391,24 @@ void RenderTableSection::rowLogicalHeightChanged(unsigned rowIndex) setRowLogicalHeightToRowStyleLogicalHeightIfNotRelative(m_grid[rowIndex]); - for (RenderObject* cell = m_grid[rowIndex].rowRenderer->firstChild(); cell; cell = cell->nextSibling()) { - if (!cell->isTableCell()) - continue; - - updateLogicalHeightForCell(m_grid[rowIndex], toRenderTableCell(cell)); - } + for (RenderTableCell* cell = m_grid[rowIndex].rowRenderer->firstCell(); cell; cell = cell->nextCell()) + updateLogicalHeightForCell(m_grid[rowIndex], cell); } void RenderTableSection::setNeedsCellRecalc() { m_needsCellRecalc = true; + + // Clear the grid now to ensure that we don't hold onto any stale pointers (e.g. a cell renderer that is being removed). + m_grid.clear(); + if (RenderTable* t = table()) t->setNeedsSectionRecalc(); } unsigned RenderTableSection::numColumns() const { + ASSERT(!m_needsCellRecalc); unsigned result = 0; for (unsigned r = 0; r < m_grid.size(); ++r) { @@ -1288,13 +1425,13 @@ unsigned RenderTableSection::numColumns() const const BorderValue& RenderTableSection::borderAdjoiningStartCell(const RenderTableCell* cell) const { ASSERT(cell->isFirstOrLastCellInRow()); - return hasSameDirectionAs(cell) ? style()->borderStart() : style()->borderEnd(); + return hasSameDirectionAs(cell) ? style().borderStart() : style().borderEnd(); } const BorderValue& RenderTableSection::borderAdjoiningEndCell(const RenderTableCell* cell) const { ASSERT(cell->isFirstOrLastCellInRow()); - return hasSameDirectionAs(cell) ? style()->borderEnd() : style()->borderStart(); + return hasSameDirectionAs(cell) ? style().borderEnd() : style().borderStart(); } const RenderTableCell* RenderTableSection::firstRowCellAdjoiningTableStart() const @@ -1346,25 +1483,25 @@ void RenderTableSection::splitColumn(unsigned pos, unsigned first) bool RenderTableSection::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction action) { // If we have no children then we have nothing to do. - if (!firstChild()) + if (!firstRow()) return false; // Table sections cannot ever be hit tested. Effectively they do not exist. // Just forward to our children always. LayoutPoint adjustedLocation = accumulatedOffset + location(); - if (hasOverflowClip() && !locationInContainer.intersects(overflowClipRect(adjustedLocation, locationInContainer.region()))) + if (hasOverflowClip() && !locationInContainer.intersects(overflowClipRect(adjustedLocation, currentRenderNamedFlowFragment()))) return false; if (hasOverflowingCell()) { - for (RenderObject* child = lastChild(); child; child = child->previousSibling()) { + for (RenderTableRow* row = lastRow(); row; row = row->previousRow()) { // FIXME: We have to skip over inline flows, since they can show up inside table rows // at the moment (a demoted inline <form> for example). If we ever implement a // table-specific hit-test method (which we should do for performance reasons anyway), // then we can remove this check. - if (child->isBox() && !toRenderBox(child)->hasSelfPaintingLayer()) { - LayoutPoint childPoint = flipForWritingModeForChild(toRenderBox(child), adjustedLocation); - if (child->nodeAtPoint(request, result, locationInContainer, childPoint, action)) { + if (!row->hasSelfPaintingLayer()) { + LayoutPoint childPoint = flipForWritingModeForChild(row, adjustedLocation); + if (row->nodeAtPoint(request, result, locationInContainer, childPoint, action)) { updateHitTestResult(result, toLayoutPoint(locationInContainer.point() - childPoint)); return true; } @@ -1379,12 +1516,12 @@ bool RenderTableSection::nodeAtPoint(const HitTestRequest& request, HitTestResul hitTestRect.moveBy(-adjustedLocation); LayoutRect tableAlignedRect = logicalRectForWritingModeAndDirection(hitTestRect); - CellSpan rowSpan = spannedRows(tableAlignedRect); - CellSpan columnSpan = spannedColumns(tableAlignedRect); + CellSpan rowSpan = spannedRows(tableAlignedRect, DoNotIncludeAllIntersectingCells); + CellSpan columnSpan = spannedColumns(tableAlignedRect, DoNotIncludeAllIntersectingCells); // Now iterate over the spanned rows and columns. - for (unsigned hitRow = rowSpan.start(); hitRow < rowSpan.end(); ++hitRow) { - for (unsigned hitColumn = columnSpan.start(); hitColumn < columnSpan.end(); ++hitColumn) { + for (unsigned hitRow = rowSpan.start; hitRow < rowSpan.end; ++hitRow) { + for (unsigned hitColumn = columnSpan.start; hitColumn < columnSpan.end; ++hitColumn) { CellStruct& current = cellAt(hitRow, hitColumn); // If the cell is empty, there's nothing to do @@ -1410,36 +1547,44 @@ bool RenderTableSection::nodeAtPoint(const HitTestRequest& request, HitTestResul return false; } -void RenderTableSection::removeCachedCollapsedBorders(const RenderTableCell* cell) +void RenderTableSection::clearCachedCollapsedBorders() +{ + if (!table()->collapseBorders()) + return; + m_cellsCollapsedBorders.clear(); +} + +void RenderTableSection::removeCachedCollapsedBorders(const RenderTableCell& cell) { if (!table()->collapseBorders()) return; for (int side = CBSBefore; side <= CBSEnd; ++side) - m_cellsCollapsedBorders.remove(make_pair(cell, side)); + m_cellsCollapsedBorders.remove(std::make_pair(&cell, side)); } -void RenderTableSection::setCachedCollapsedBorder(const RenderTableCell* cell, CollapsedBorderSide side, CollapsedBorderValue border) +void RenderTableSection::setCachedCollapsedBorder(const RenderTableCell& cell, CollapsedBorderSide side, CollapsedBorderValue border) { ASSERT(table()->collapseBorders()); - m_cellsCollapsedBorders.set(make_pair(cell, side), border); + ASSERT(border.width()); + m_cellsCollapsedBorders.set(std::make_pair(&cell, side), border); } -CollapsedBorderValue& RenderTableSection::cachedCollapsedBorder(const RenderTableCell* cell, CollapsedBorderSide side) +CollapsedBorderValue RenderTableSection::cachedCollapsedBorder(const RenderTableCell& cell, CollapsedBorderSide side) { - ASSERT(table()->collapseBorders()); - HashMap<pair<const RenderTableCell*, int>, CollapsedBorderValue>::iterator it = m_cellsCollapsedBorders.find(make_pair(cell, side)); - ASSERT(it != m_cellsCollapsedBorders.end()); + ASSERT(table()->collapseBorders() && table()->collapsedBordersAreValid()); + auto it = m_cellsCollapsedBorders.find(std::make_pair(&cell, side)); + // Only non-empty collapsed borders are in the hashmap. + if (it == m_cellsCollapsedBorders.end()) + return CollapsedBorderValue(BorderValue(), Color(), BCELL); return it->value; } RenderTableSection* RenderTableSection::createAnonymousWithParentRenderer(const RenderObject* parent) { - RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyleWithDisplay(parent->style(), TABLE_ROW_GROUP); - RenderTableSection* newSection = new (parent->renderArena()) RenderTableSection(0); - newSection->setDocumentForAnonymous(parent->document()); - newSection->setStyle(newStyle.release()); - return newSection; + auto section = new RenderTableSection(parent->document(), RenderStyle::createAnonymousStyleWithDisplay(&parent->style(), TABLE_ROW_GROUP)); + section->initializeStyle(); + return section; } void RenderTableSection::setLogicalPositionForCell(RenderTableCell* cell, unsigned effectiveColumn) const @@ -1447,16 +1592,16 @@ void RenderTableSection::setLogicalPositionForCell(RenderTableCell* cell, unsign LayoutPoint oldCellLocation = cell->location(); LayoutPoint cellLocation(0, m_rowPos[cell->rowIndex()]); - int horizontalBorderSpacing = table()->hBorderSpacing(); + LayoutUnit horizontalBorderSpacing = table()->hBorderSpacing(); // FIXME: The table's direction should determine our row's direction, not the section's (see bug 96691). - if (!style()->isLeftToRightDirection()) + if (!style().isLeftToRightDirection()) cellLocation.setX(table()->columnPositions()[table()->numEffCols()] - table()->columnPositions()[table()->colToEffCol(cell->col() + cell->colSpan())] + horizontalBorderSpacing); else cellLocation.setX(table()->columnPositions()[effectiveColumn] + horizontalBorderSpacing); cell->setLogicalLocation(cellLocation); - view()->addLayoutDelta(oldCellLocation - cell->location()); + view().addLayoutDelta(oldCellLocation - cell->location()); } } // namespace WebCore |