2 * Copyright (C) 1997 Martin Jones (mjones@kde.org)
3 * (C) 1997 Torben Weis (weis@kde.org)
4 * (C) 1998 Waldo Bastian (bastian@kde.org)
5 * (C) 1999 Lars Knoll (knoll@kde.org)
6 * (C) 1999 Antti Koivisto (koivisto@kde.org)
7 * Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009, 2010 Apple Inc. All rights reserved.
8 * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Library General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Library General Public License for more details.
20 * You should have received a copy of the GNU Library General Public License
21 * along with this library; see the file COPYING.LIB. If not, write to
22 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23 * Boston, MA 02110-1301, USA.
27 #include "core/rendering/RenderTableSection.h"
30 #include "core/rendering/GraphicsContextAnnotator.h"
31 #include "core/rendering/HitTestResult.h"
32 #include "core/rendering/PaintInfo.h"
33 #include "core/rendering/RenderTableCell.h"
34 #include "core/rendering/RenderTableCol.h"
35 #include "core/rendering/RenderTableRow.h"
36 #include "core/rendering/RenderView.h"
37 #include "core/rendering/SubtreeLayoutScope.h"
38 #include "wtf/HashSet.h"
44 using namespace HTMLNames;
46 // Those 2 variables are used to balance the memory consumption vs the repaint time on big tables.
47 static unsigned gMinTableSizeToUseFastPaintPathWithOverflowingCell = 75 * 75;
48 static float gMaxAllowedOverflowingCellRatioForFastPaintPath = 0.1f;
50 static inline void setRowLogicalHeightToRowStyleLogicalHeight(RenderTableSection::RowStruct& row)
52 ASSERT(row.rowRenderer);
53 row.logicalHeight = row.rowRenderer->style()->logicalHeight();
56 static inline void updateLogicalHeightForCell(RenderTableSection::RowStruct& row, const RenderTableCell* cell)
58 // We ignore height settings on rowspan cells.
59 if (cell->rowSpan() != 1)
62 Length logicalHeight = cell->style()->logicalHeight();
63 if (logicalHeight.isPositive()) {
64 Length cRowLogicalHeight = row.logicalHeight;
65 switch (logicalHeight.type()) {
67 if (!(cRowLogicalHeight.isPercent())
68 || (cRowLogicalHeight.isPercent() && cRowLogicalHeight.percent() < logicalHeight.percent()))
69 row.logicalHeight = logicalHeight;
72 if (cRowLogicalHeight.type() < Percent
73 || (cRowLogicalHeight.isFixed() && cRowLogicalHeight.value() < logicalHeight.value()))
74 row.logicalHeight = logicalHeight;
83 RenderTableSection::RenderTableSection(Element* element)
87 , m_outerBorderStart(0)
89 , m_outerBorderBefore(0)
90 , m_outerBorderAfter(0)
91 , m_needsCellRecalc(false)
92 , m_hasMultipleCellLevels(false)
94 // init RenderObject attributes
95 setInline(false); // our object is not Inline
98 RenderTableSection::~RenderTableSection()
102 void RenderTableSection::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
104 RenderBox::styleDidChange(diff, oldStyle);
105 propagateStyleToAnonymousChildren();
107 // If border was changed, notify table.
108 RenderTable* table = this->table();
109 if (table && !table->selfNeedsLayout() && !table->normalChildNeedsLayout() && oldStyle && oldStyle->border() != style()->border())
110 table->invalidateCollapsedBorders();
113 void RenderTableSection::willBeRemovedFromTree()
115 RenderBox::willBeRemovedFromTree();
117 // Preventively invalidate our cells as we may be re-inserted into
118 // a new table which would require us to rebuild our structure.
119 setNeedsCellRecalc();
122 void RenderTableSection::addChild(RenderObject* child, RenderObject* beforeChild)
124 if (!child->isTableRow()) {
125 RenderObject* last = beforeChild;
128 if (last && last->isAnonymous() && !last->isBeforeOrAfterContent()) {
129 if (beforeChild == last)
130 beforeChild = last->firstChild();
131 last->addChild(child, beforeChild);
135 if (beforeChild && !beforeChild->isAnonymous() && beforeChild->parent() == this) {
136 RenderObject* row = beforeChild->previousSibling();
137 if (row && row->isTableRow() && row->isAnonymous()) {
138 row->addChild(child);
143 // If beforeChild is inside an anonymous cell/row, insert into the cell or into
144 // the anonymous row containing it, if there is one.
145 RenderObject* lastBox = last;
146 while (lastBox && lastBox->parent()->isAnonymous() && !lastBox->isTableRow())
147 lastBox = lastBox->parent();
148 if (lastBox && lastBox->isAnonymous() && !lastBox->isBeforeOrAfterContent()) {
149 lastBox->addChild(child, beforeChild);
153 RenderObject* row = RenderTableRow::createAnonymousWithParentRenderer(this);
154 addChild(row, beforeChild);
155 row->addChild(child);
160 setNeedsCellRecalc();
162 unsigned insertionRow = m_cRow;
168 RenderTableRow* row = toRenderTableRow(child);
169 m_grid[insertionRow].rowRenderer = row;
170 row->setRowIndex(insertionRow);
173 setRowLogicalHeightToRowStyleLogicalHeight(m_grid[insertionRow]);
175 if (beforeChild && beforeChild->parent() != this)
176 beforeChild = splitAnonymousBoxesAroundChild(beforeChild);
178 ASSERT(!beforeChild || beforeChild->isTableRow());
179 RenderBox::addChild(child, beforeChild);
182 void RenderTableSection::ensureRows(unsigned numRows)
184 if (numRows <= m_grid.size())
187 unsigned oldSize = m_grid.size();
188 m_grid.grow(numRows);
190 unsigned effectiveColumnCount = max(1u, table()->numEffCols());
191 for (unsigned row = oldSize; row < m_grid.size(); ++row)
192 m_grid[row].row.grow(effectiveColumnCount);
195 void RenderTableSection::addCell(RenderTableCell* cell, RenderTableRow* row)
197 // We don't insert the cell if we need cell recalc as our internal columns' representation
198 // will have drifted from the table's representation. Also recalcCells will call addCell
199 // at a later time after sync'ing our columns' with the table's.
200 if (needsCellRecalc())
203 unsigned rSpan = cell->rowSpan();
204 unsigned cSpan = cell->colSpan();
205 const Vector<RenderTable::ColumnStruct>& columns = table()->columns();
206 unsigned nCols = columns.size();
207 unsigned insertionRow = row->rowIndex();
209 // ### mozilla still seems to do the old HTML way, even for strict DTD
210 // (see the annotation on table cell layouting in the CSS specs and the testcase below:
212 // <TR><TD>1 <TD rowspan="2">2 <TD>3 <TD>4
213 // <TR><TD colspan="2">5
215 while (m_cCol < nCols && (cellAt(insertionRow, m_cCol).hasCells() || cellAt(insertionRow, m_cCol).inColSpan))
218 updateLogicalHeightForCell(m_grid[insertionRow], cell);
220 ensureRows(insertionRow + rSpan);
222 m_grid[insertionRow].rowRenderer = row;
224 unsigned col = m_cCol;
225 // tell the cell where it is
226 bool inColSpan = false;
228 unsigned currentSpan;
229 if (m_cCol >= nCols) {
230 table()->appendColumn(cSpan);
233 if (cSpan < columns[m_cCol].span)
234 table()->splitColumn(m_cCol, cSpan);
235 currentSpan = columns[m_cCol].span;
237 for (unsigned r = 0; r < rSpan; r++) {
238 CellStruct& c = cellAt(insertionRow + r, m_cCol);
240 c.cells.append(cell);
241 // If cells overlap then we take the slow path for painting.
242 if (c.cells.size() > 1)
243 m_hasMultipleCellLevels = true;
248 cSpan -= currentSpan;
251 cell->setCol(table()->effColToCol(col));
254 bool RenderTableSection::rowHasOnlySpanningCells(unsigned row)
256 unsigned totalCols = m_grid[row].row.size();
261 for (unsigned col = 0; col < totalCols; col++) {
262 const CellStruct& rowSpanCell = cellAt(row, col);
264 // Empty cell is not a valid cell so it is not a rowspan cell.
265 if (rowSpanCell.cells.isEmpty())
268 if (rowSpanCell.cells[0]->rowSpan() == 1)
275 void RenderTableSection::populateSpanningRowsHeightFromCell(RenderTableCell* cell, struct SpanningRowsHeight& spanningRowsHeight)
277 const unsigned rowSpan = cell->rowSpan();
278 const unsigned rowIndex = cell->rowIndex();
280 spanningRowsHeight.spanningCellHeightIgnoringBorderSpacing = cell->logicalHeightForRowSizing();
282 spanningRowsHeight.rowHeight.resize(rowSpan);
283 spanningRowsHeight.totalRowsHeight = 0;
284 for (unsigned row = 0; row < rowSpan; row++) {
285 unsigned actualRow = row + rowIndex;
287 spanningRowsHeight.rowHeight[row] = m_rowPos[actualRow + 1] - m_rowPos[actualRow] - borderSpacingForRow(actualRow);
288 if (!spanningRowsHeight.rowHeight[row])
289 spanningRowsHeight.isAnyRowWithOnlySpanningCells |= rowHasOnlySpanningCells(actualRow);
291 spanningRowsHeight.totalRowsHeight += spanningRowsHeight.rowHeight[row];
292 spanningRowsHeight.spanningCellHeightIgnoringBorderSpacing -= borderSpacingForRow(actualRow);
294 // We don't span the following row so its border-spacing (if any) should be included.
295 spanningRowsHeight.spanningCellHeightIgnoringBorderSpacing += borderSpacingForRow(rowIndex + rowSpan - 1);
298 void RenderTableSection::distributeExtraRowSpanHeightToPercentRows(RenderTableCell* cell, int totalPercent, int& extraRowSpanningHeight, Vector<int>& rowsHeight)
300 if (!extraRowSpanningHeight || !totalPercent)
303 const unsigned rowSpan = cell->rowSpan();
304 const unsigned rowIndex = cell->rowIndex();
305 int percent = min(totalPercent, 100);
306 const int tableHeight = m_rowPos[m_grid.size()] + extraRowSpanningHeight;
308 // Our algorithm matches Firefox. Extra spanning height would be distributed Only in first percent height rows
309 // those total percent is 100. Other percent rows would be uneffected even extra spanning height is remain.
310 int accumulatedPositionIncrease = 0;
311 for (unsigned row = rowIndex; row < (rowIndex + rowSpan); row++) {
312 if (percent > 0 && extraRowSpanningHeight > 0) {
313 if (m_grid[row].logicalHeight.isPercent()) {
314 int toAdd = (tableHeight * m_grid[row].logicalHeight.percent() / 100) - rowsHeight[row - rowIndex];
315 // FIXME: Note that this is wrong if we have a percentage above 100% and may make us grow
316 // above the available space.
318 toAdd = min(toAdd, extraRowSpanningHeight);
319 accumulatedPositionIncrease += toAdd;
320 extraRowSpanningHeight -= toAdd;
321 percent -= m_grid[row].logicalHeight.percent();
324 m_rowPos[row + 1] += accumulatedPositionIncrease;
328 // Sometimes the multiplication of the 2 values below will overflow an integer.
329 // So we convert the parameters to 'long long' instead of 'int' to avoid the
330 // problem in this function.
331 static void updatePositionIncreasedWithRowHeight(long long extraHeight, long long rowHeight, long long totalHeight, int& accumulatedPositionIncrease, int& remainder)
333 COMPILE_ASSERT(sizeof(long long int) > sizeof(int), int_should_be_less_than_longlong);
335 accumulatedPositionIncrease += (extraHeight * rowHeight) / totalHeight;
336 remainder += (extraHeight * rowHeight) % totalHeight;
339 void RenderTableSection::distributeExtraRowSpanHeightToAutoRows(RenderTableCell* cell, int totalAutoRowsHeight, int& extraRowSpanningHeight, Vector<int>& rowsHeight)
341 if (!extraRowSpanningHeight || !totalAutoRowsHeight)
344 const unsigned rowSpan = cell->rowSpan();
345 const unsigned rowIndex = cell->rowIndex();
346 int accumulatedPositionIncrease = 0;
349 // Aspect ratios of auto rows should not change otherwise table may look different than user expected.
350 // So extra height distributed in auto spanning rows based on their weight in spanning cell.
351 for (unsigned row = rowIndex; row < (rowIndex + rowSpan); row++) {
352 if (m_grid[row].logicalHeight.isAuto()) {
353 updatePositionIncreasedWithRowHeight(extraRowSpanningHeight, rowsHeight[row - rowIndex], totalAutoRowsHeight, accumulatedPositionIncrease, remainder);
355 // While whole extra spanning height is distributing in auto spanning rows, rational parts remains
356 // in every integer division. So accumulating all remainder part in integer division and when total remainder
357 // is equvalent to divisor then 1 unit increased in row position.
358 // Note that this algorithm is biased towards adding more space towards the lower rows.
359 if (remainder >= totalAutoRowsHeight) {
360 remainder -= totalAutoRowsHeight;
361 accumulatedPositionIncrease++;
364 m_rowPos[row + 1] += accumulatedPositionIncrease;
369 extraRowSpanningHeight -= accumulatedPositionIncrease;
372 void RenderTableSection::distributeExtraRowSpanHeightToRemainingRows(RenderTableCell* cell, int totalRemainingRowsHeight, int& extraRowSpanningHeight, Vector<int>& rowsHeight)
374 if (!extraRowSpanningHeight || !totalRemainingRowsHeight)
377 const unsigned rowSpan = cell->rowSpan();
378 const unsigned rowIndex = cell->rowIndex();
379 int accumulatedPositionIncrease = 0;
382 // Aspect ratios of the rows should not change otherwise table may look different than user expected.
383 // So extra height distribution in remaining spanning rows based on their weight in spanning cell.
384 for (unsigned row = rowIndex; row < (rowIndex + rowSpan); row++) {
385 if (!m_grid[row].logicalHeight.isPercent()) {
386 updatePositionIncreasedWithRowHeight(extraRowSpanningHeight, rowsHeight[row - rowIndex], totalRemainingRowsHeight, accumulatedPositionIncrease, remainder);
388 // While whole extra spanning height is distributing in remaining spanning rows, rational parts remains
389 // in every integer division. So accumulating all remainder part in integer division and when total remainder
390 // is equvalent to divisor then 1 unit increased in row position.
391 // Note that this algorithm is biased towards adding more space towards the lower rows.
392 if (remainder >= totalRemainingRowsHeight) {
393 remainder -= totalRemainingRowsHeight;
394 accumulatedPositionIncrease++;
397 m_rowPos[row + 1] += accumulatedPositionIncrease;
402 extraRowSpanningHeight -= accumulatedPositionIncrease;
405 static bool cellIsFullyIncludedInOtherCell(const RenderTableCell* cell1, const RenderTableCell* cell2)
407 return (cell1->rowIndex() >= cell2->rowIndex() && (cell1->rowIndex() + cell1->rowSpan()) <= (cell2->rowIndex() + cell2->rowSpan()));
410 // To avoid unneeded extra height distributions, we apply the following sorting algorithm:
411 static bool compareRowSpanCellsInHeightDistributionOrder(const RenderTableCell* cell1, const RenderTableCell* cell2)
413 // Sorting bigger height cell first if cells are at same index with same span because we will skip smaller
414 // height cell to distribute it's extra height.
415 if (cell1->rowIndex() == cell2->rowIndex() && cell1->rowSpan() == cell2->rowSpan())
416 return (cell1->logicalHeightForRowSizing() > cell2->logicalHeightForRowSizing());
417 // Sorting inner most cell first because if inner spanning cell'e extra height is distributed then outer
418 // spanning cell's extra height will adjust accordingly. In reverse order, there is more chances that outer
419 // spanning cell's height will exceed than defined by user.
420 if (cellIsFullyIncludedInOtherCell(cell1, cell2))
422 // Sorting lower row index first because first we need to apply the extra height of spanning cell which
423 // comes first in the table so lower rows's position would increment in sequence.
424 if (!cellIsFullyIncludedInOtherCell(cell2, cell1))
425 return (cell1->rowIndex() < cell2->rowIndex());
430 bool RenderTableSection::isHeightNeededForRowHavingOnlySpanningCells(unsigned row)
432 unsigned totalCols = m_grid[row].row.size();
437 for (unsigned col = 0; col < totalCols; col++) {
438 const CellStruct& rowSpanCell = cellAt(row, col);
440 if (rowSpanCell.cells.size()) {
441 RenderTableCell* cell = rowSpanCell.cells[0];
442 const unsigned rowIndex = cell->rowIndex();
443 const unsigned rowSpan = cell->rowSpan();
444 int totalRowSpanCellHeight = 0;
446 for (unsigned row = 0; row < rowSpan; row++) {
447 unsigned actualRow = row + rowIndex;
448 totalRowSpanCellHeight += m_rowPos[actualRow + 1] - m_rowPos[actualRow];
450 totalRowSpanCellHeight -= borderSpacingForRow(rowIndex + rowSpan - 1);
452 if (totalRowSpanCellHeight < cell->logicalHeightForRowSizing())
460 unsigned RenderTableSection::calcRowHeightHavingOnlySpanningCells(unsigned row)
462 ASSERT(rowHasOnlySpanningCells(row));
464 unsigned totalCols = m_grid[row].row.size();
469 unsigned rowHeight = 0;
471 for (unsigned col = 0; col < totalCols; col++) {
472 const CellStruct& rowSpanCell = cellAt(row, col);
473 if (rowSpanCell.cells.size() && rowSpanCell.cells[0]->rowSpan() > 1)
474 rowHeight = max(rowHeight, rowSpanCell.cells[0]->logicalHeightForRowSizing() / rowSpanCell.cells[0]->rowSpan());
480 void RenderTableSection::updateRowsHeightHavingOnlySpanningCells(RenderTableCell* cell, struct SpanningRowsHeight& spanningRowsHeight)
482 ASSERT(spanningRowsHeight.rowHeight.size());
484 int accumulatedPositionIncrease = 0;
485 const unsigned rowSpan = cell->rowSpan();
486 const unsigned rowIndex = cell->rowIndex();
488 ASSERT_UNUSED(rowSpan, rowSpan == spanningRowsHeight.rowHeight.size());
490 for (unsigned row = 0; row < spanningRowsHeight.rowHeight.size(); row++) {
491 unsigned actualRow = row + rowIndex;
492 if (!spanningRowsHeight.rowHeight[row] && rowHasOnlySpanningCells(actualRow) && isHeightNeededForRowHavingOnlySpanningCells(actualRow)) {
493 spanningRowsHeight.rowHeight[row] = calcRowHeightHavingOnlySpanningCells(actualRow);
494 accumulatedPositionIncrease += spanningRowsHeight.rowHeight[row];
496 m_rowPos[actualRow + 1] += accumulatedPositionIncrease;
499 spanningRowsHeight.totalRowsHeight += accumulatedPositionIncrease;
502 // Distribute rowSpan cell height in rows those comes in rowSpan cell based on the ratio of row's height if
503 // 1. RowSpan cell height is greater then the total height of rows in rowSpan cell
504 void RenderTableSection::distributeRowSpanHeightToRows(SpanningRenderTableCells& rowSpanCells)
506 ASSERT(rowSpanCells.size());
508 // 'rowSpanCells' list is already sorted based on the cells rowIndex in ascending order
509 // Arrange row spanning cell in the order in which we need to process first.
510 std::sort(rowSpanCells.begin(), rowSpanCells.end(), compareRowSpanCellsInHeightDistributionOrder);
512 unsigned extraHeightToPropagate = 0;
513 unsigned lastRowIndex = 0;
514 unsigned lastRowSpan = 0;
516 for (unsigned i = 0; i < rowSpanCells.size(); i++) {
517 RenderTableCell* cell = rowSpanCells[i];
519 unsigned rowIndex = cell->rowIndex();
521 unsigned rowSpan = cell->rowSpan();
523 unsigned spanningCellEndIndex = rowIndex + rowSpan;
524 unsigned lastSpanningCellEndIndex = lastRowIndex + lastRowSpan;
526 // Only heightest spanning cell will distribute it's extra height in row if more then one spanning cells
527 // present at same level.
528 if (rowIndex == lastRowIndex && rowSpan == lastRowSpan)
531 int originalBeforePosition = m_rowPos[spanningCellEndIndex];
533 // When 2 spanning cells are ending at same row index then while extra height distribution of first spanning
534 // cell updates position of the last row so getting the original position of the last row in second spanning
535 // cell need to reduce the height changed by first spanning cell.
536 if (spanningCellEndIndex == lastSpanningCellEndIndex)
537 originalBeforePosition -= extraHeightToPropagate;
539 if (extraHeightToPropagate) {
540 for (unsigned row = lastSpanningCellEndIndex + 1; row <= spanningCellEndIndex; row++)
541 m_rowPos[row] += extraHeightToPropagate;
544 lastRowIndex = rowIndex;
545 lastRowSpan = rowSpan;
547 struct SpanningRowsHeight spanningRowsHeight;
549 populateSpanningRowsHeightFromCell(cell, spanningRowsHeight);
551 // Here we are handling only row(s) who have only rowspanning cells and do not have any empty cell.
552 if (spanningRowsHeight.isAnyRowWithOnlySpanningCells)
553 updateRowsHeightHavingOnlySpanningCells(cell, spanningRowsHeight);
555 // This code handle row(s) that have rowspanning cell(s) and at least one empty cell.
556 // Such rows are not handled below and end up having a height of 0. That would mean
557 // content overlapping if one of their cells has any content. To avoid the problem, we
558 // add all the remaining spanning cells' height to the last spanned row.
559 // This means that we could grow a row past its 'height' or break percentage spreading
560 // however this is better than overlapping content.
561 // FIXME: Is there a better algorithm?
562 if (!spanningRowsHeight.totalRowsHeight) {
563 if (spanningRowsHeight.spanningCellHeightIgnoringBorderSpacing)
564 m_rowPos[spanningCellEndIndex] += spanningRowsHeight.spanningCellHeightIgnoringBorderSpacing + borderSpacingForRow(spanningCellEndIndex - 1);
566 extraHeightToPropagate = m_rowPos[spanningCellEndIndex] - originalBeforePosition;
570 if (spanningRowsHeight.spanningCellHeightIgnoringBorderSpacing <= spanningRowsHeight.totalRowsHeight) {
571 extraHeightToPropagate = m_rowPos[rowIndex + rowSpan] - originalBeforePosition;
575 // Below we are handling only row(s) who have at least one visible cell without rowspan value.
576 int totalPercent = 0;
577 int totalAutoRowsHeight = 0;
578 int totalRemainingRowsHeight = spanningRowsHeight.totalRowsHeight;
580 // FIXME: Inner spanning cell height should not change if it have fixed height when it's parent spanning cell
581 // is distributing it's extra height in rows.
583 // Calculate total percentage, total auto rows height and total rows height except percent rows.
584 for (unsigned row = rowIndex; row < spanningCellEndIndex; row++) {
585 if (m_grid[row].logicalHeight.isPercent()) {
586 totalPercent += m_grid[row].logicalHeight.percent();
587 totalRemainingRowsHeight -= spanningRowsHeight.rowHeight[row - rowIndex];
588 } else if (m_grid[row].logicalHeight.isAuto()) {
589 totalAutoRowsHeight += spanningRowsHeight.rowHeight[row - rowIndex];
593 int extraRowSpanningHeight = spanningRowsHeight.spanningCellHeightIgnoringBorderSpacing - spanningRowsHeight.totalRowsHeight;
595 distributeExtraRowSpanHeightToPercentRows(cell, totalPercent, extraRowSpanningHeight, spanningRowsHeight.rowHeight);
596 distributeExtraRowSpanHeightToAutoRows(cell, totalAutoRowsHeight, extraRowSpanningHeight, spanningRowsHeight.rowHeight);
597 distributeExtraRowSpanHeightToRemainingRows(cell, totalRemainingRowsHeight, extraRowSpanningHeight, spanningRowsHeight.rowHeight);
599 ASSERT(!extraRowSpanningHeight);
601 // Getting total changed height in the table
602 extraHeightToPropagate = m_rowPos[spanningCellEndIndex] - originalBeforePosition;
605 if (extraHeightToPropagate) {
606 // Apply changed height by rowSpan cells to rows present at the end of the table
607 for (unsigned row = lastRowIndex + lastRowSpan + 1; row <= m_grid.size(); row++)
608 m_rowPos[row] += extraHeightToPropagate;
612 // Find out the baseline of the cell
613 // If the cell's baseline is more then the row's baseline then the cell's baseline become the row's baseline
614 // and if the row's baseline goes out of the row's boundries then adjust row height accordingly.
615 void RenderTableSection::updateBaselineForCell(RenderTableCell* cell, unsigned row, LayoutUnit& baselineDescent)
617 if (!cell->isBaselineAligned())
620 // Ignoring the intrinsic padding as it depends on knowing the row's baseline, which won't be accurate
621 // until the end of this function.
622 LayoutUnit baselinePosition = cell->cellBaselinePosition() - cell->intrinsicPaddingBefore();
623 if (baselinePosition > cell->borderBefore() + (cell->paddingBefore() - cell->intrinsicPaddingBefore())) {
624 m_grid[row].baseline = max(m_grid[row].baseline, baselinePosition);
626 int cellStartRowBaselineDescent = 0;
627 if (cell->rowSpan() == 1) {
628 baselineDescent = max(baselineDescent, cell->logicalHeightForRowSizing() - baselinePosition);
629 cellStartRowBaselineDescent = baselineDescent;
631 m_rowPos[row + 1] = max<int>(m_rowPos[row + 1], m_rowPos[row] + m_grid[row].baseline + cellStartRowBaselineDescent);
635 int RenderTableSection::calcRowLogicalHeight()
638 SetLayoutNeededForbiddenScope layoutForbiddenScope(*this);
641 ASSERT(!needsLayout());
643 RenderTableCell* cell;
645 LayoutStateMaintainer statePusher(*this);
647 m_rowPos.resize(m_grid.size() + 1);
649 // We ignore the border-spacing on any non-top section as it is already included in the previous section's last row position.
650 if (this == table()->topSection())
651 m_rowPos[0] = table()->vBorderSpacing();
655 SpanningRenderTableCells rowSpanCells;
657 HashSet<const RenderTableCell*> uniqueCells;
660 for (unsigned r = 0; r < m_grid.size(); r++) {
661 m_grid[r].baseline = 0;
662 LayoutUnit baselineDescent = 0;
664 // Our base size is the biggest logical height from our cells' styles (excluding row spanning cells).
665 m_rowPos[r + 1] = max(m_rowPos[r] + minimumValueForLength(m_grid[r].logicalHeight, 0).round(), 0);
667 Row& row = m_grid[r].row;
668 unsigned totalCols = row.size();
669 RenderTableCell* lastRowSpanCell = 0;
671 for (unsigned c = 0; c < totalCols; c++) {
672 CellStruct& current = cellAt(r, c);
673 for (unsigned i = 0; i < current.cells.size(); i++) {
674 cell = current.cells[i];
675 if (current.inColSpan && cell->rowSpan() == 1)
678 if (cell->rowSpan() > 1) {
679 // For row spanning cells, we only handle them for the first row they span. This ensures we take their baseline into account.
680 if (lastRowSpanCell != cell && cell->rowIndex() == r) {
682 ASSERT(!uniqueCells.contains(cell));
683 uniqueCells.add(cell);
686 rowSpanCells.append(cell);
687 lastRowSpanCell = cell;
689 // Find out the baseline. The baseline is set on the first row in a rowSpan.
690 updateBaselineForCell(cell, r, baselineDescent);
695 ASSERT(cell->rowSpan() == 1);
697 if (cell->hasOverrideHeight()) {
698 if (!statePusher.didPush()) {
699 // Technically, we should also push state for the row, but since
700 // rows don't push a coordinate transform, that's not necessary.
701 statePusher.push(*this, locationOffset());
703 cell->clearIntrinsicPadding();
704 cell->clearOverrideSize();
705 cell->forceChildLayout();
708 m_rowPos[r + 1] = max(m_rowPos[r + 1], m_rowPos[r] + cell->logicalHeightForRowSizing());
710 // Find out the baseline.
711 updateBaselineForCell(cell, r, baselineDescent);
715 // Add the border-spacing to our final position.
716 m_rowPos[r + 1] += borderSpacingForRow(r);
717 m_rowPos[r + 1] = max(m_rowPos[r + 1], m_rowPos[r]);
720 if (!rowSpanCells.isEmpty())
721 distributeRowSpanHeightToRows(rowSpanCells);
723 ASSERT(!needsLayout());
725 return m_rowPos[m_grid.size()];
728 void RenderTableSection::layout()
730 ASSERT(needsLayout());
731 ASSERT(!needsCellRecalc());
732 ASSERT(!table()->needsSectionRecalc());
734 // addChild may over-grow m_grid but we don't want to throw away the memory too early as addChild
735 // can be called in a loop (e.g during parsing). Doing it now ensures we have a stable-enough structure.
736 m_grid.shrinkToFit();
738 LayoutStateMaintainer statePusher(*this, locationOffset());
740 const Vector<int>& columnPos = table()->columnPositions();
742 SubtreeLayoutScope layouter(*this);
743 for (unsigned r = 0; r < m_grid.size(); ++r) {
744 Row& row = m_grid[r].row;
745 unsigned cols = row.size();
746 // First, propagate our table layout's information to the cells. This will mark the row as needing layout
747 // if there was a column logical width change.
748 for (unsigned startColumn = 0; startColumn < cols; ++startColumn) {
749 CellStruct& current = row[startColumn];
750 RenderTableCell* cell = current.primaryCell();
751 if (!cell || current.inColSpan)
754 unsigned endCol = startColumn;
755 unsigned cspan = cell->colSpan();
756 while (cspan && endCol < cols) {
757 ASSERT(endCol < table()->columns().size());
758 cspan -= table()->columns()[endCol].span;
761 int tableLayoutLogicalWidth = columnPos[endCol] - columnPos[startColumn] - table()->hBorderSpacing();
762 cell->setCellLogicalWidth(tableLayoutLogicalWidth, layouter);
765 if (RenderTableRow* rowRenderer = m_grid[r].rowRenderer) {
766 if (!rowRenderer->needsLayout())
767 rowRenderer->markForPaginationRelayoutIfNeeded(layouter);
768 rowRenderer->layoutIfNeeded();
775 void RenderTableSection::distributeExtraLogicalHeightToPercentRows(int& extraLogicalHeight, int totalPercent)
780 unsigned totalRows = m_grid.size();
781 int totalHeight = m_rowPos[totalRows] + extraLogicalHeight;
782 int totalLogicalHeightAdded = 0;
783 totalPercent = min(totalPercent, 100);
784 int rowHeight = m_rowPos[1] - m_rowPos[0];
785 for (unsigned r = 0; r < totalRows; ++r) {
786 if (totalPercent > 0 && m_grid[r].logicalHeight.isPercent()) {
787 int toAdd = min<int>(extraLogicalHeight, (totalHeight * m_grid[r].logicalHeight.percent() / 100) - rowHeight);
788 // If toAdd is negative, then we don't want to shrink the row (this bug
789 // affected Outlook Web Access).
790 toAdd = max(0, toAdd);
791 totalLogicalHeightAdded += toAdd;
792 extraLogicalHeight -= toAdd;
793 totalPercent -= m_grid[r].logicalHeight.percent();
795 ASSERT(totalRows >= 1);
796 if (r < totalRows - 1)
797 rowHeight = m_rowPos[r + 2] - m_rowPos[r + 1];
798 m_rowPos[r + 1] += totalLogicalHeightAdded;
802 void RenderTableSection::distributeExtraLogicalHeightToAutoRows(int& extraLogicalHeight, unsigned autoRowsCount)
807 int totalLogicalHeightAdded = 0;
808 for (unsigned r = 0; r < m_grid.size(); ++r) {
809 if (autoRowsCount > 0 && m_grid[r].logicalHeight.isAuto()) {
810 // Recomputing |extraLogicalHeightForRow| guarantees that we properly ditribute round |extraLogicalHeight|.
811 int extraLogicalHeightForRow = extraLogicalHeight / autoRowsCount;
812 totalLogicalHeightAdded += extraLogicalHeightForRow;
813 extraLogicalHeight -= extraLogicalHeightForRow;
816 m_rowPos[r + 1] += totalLogicalHeightAdded;
820 void RenderTableSection::distributeRemainingExtraLogicalHeight(int& extraLogicalHeight)
822 unsigned totalRows = m_grid.size();
824 if (extraLogicalHeight <= 0 || !m_rowPos[totalRows])
827 // FIXME: m_rowPos[totalRows] - m_rowPos[0] is the total rows' size.
828 int totalRowSize = m_rowPos[totalRows];
829 int totalLogicalHeightAdded = 0;
830 int previousRowPosition = m_rowPos[0];
831 for (unsigned r = 0; r < totalRows; r++) {
832 // weight with the original height
833 totalLogicalHeightAdded += extraLogicalHeight * (m_rowPos[r + 1] - previousRowPosition) / totalRowSize;
834 previousRowPosition = m_rowPos[r + 1];
835 m_rowPos[r + 1] += totalLogicalHeightAdded;
838 extraLogicalHeight -= totalLogicalHeightAdded;
841 int RenderTableSection::distributeExtraLogicalHeightToRows(int extraLogicalHeight)
843 if (!extraLogicalHeight)
844 return extraLogicalHeight;
846 unsigned totalRows = m_grid.size();
848 return extraLogicalHeight;
850 if (!m_rowPos[totalRows] && nextSibling())
851 return extraLogicalHeight;
853 unsigned autoRowsCount = 0;
854 int totalPercent = 0;
855 for (unsigned r = 0; r < totalRows; r++) {
856 if (m_grid[r].logicalHeight.isAuto())
858 else if (m_grid[r].logicalHeight.isPercent())
859 totalPercent += m_grid[r].logicalHeight.percent();
862 int remainingExtraLogicalHeight = extraLogicalHeight;
863 distributeExtraLogicalHeightToPercentRows(remainingExtraLogicalHeight, totalPercent);
864 distributeExtraLogicalHeightToAutoRows(remainingExtraLogicalHeight, autoRowsCount);
865 distributeRemainingExtraLogicalHeight(remainingExtraLogicalHeight);
866 return extraLogicalHeight - remainingExtraLogicalHeight;
869 static bool shouldFlexCellChild(RenderObject* cellDescendant)
871 return cellDescendant->isReplaced() || (cellDescendant->isBox() && toRenderBox(cellDescendant)->scrollsOverflow());
874 void RenderTableSection::layoutRows()
877 SetLayoutNeededForbiddenScope layoutForbiddenScope(*this);
880 ASSERT(!needsLayout());
882 // FIXME: Changing the height without a layout can change the overflow so it seems wrong.
884 unsigned totalRows = m_grid.size();
886 // Set the width of our section now. The rows will also be this width.
887 setLogicalWidth(table()->contentLogicalWidth());
889 m_overflowingCells.clear();
890 m_forceSlowPaintPathWithOverflowingCell = false;
892 int vspacing = table()->vBorderSpacing();
893 unsigned nEffCols = table()->numEffCols();
895 LayoutStateMaintainer statePusher(*this, locationOffset());
897 for (unsigned r = 0; r < totalRows; r++) {
898 // Set the row's x/y position and width/height.
899 if (RenderTableRow* rowRenderer = m_grid[r].rowRenderer) {
900 rowRenderer->setLocation(LayoutPoint(0, m_rowPos[r]));
901 rowRenderer->setLogicalWidth(logicalWidth());
902 rowRenderer->setLogicalHeight(m_rowPos[r + 1] - m_rowPos[r] - vspacing);
903 rowRenderer->updateLayerTransform();
904 rowRenderer->clearAllOverflows();
905 rowRenderer->addVisualEffectOverflow();
908 int rowHeightIncreaseForPagination = 0;
910 for (unsigned c = 0; c < nEffCols; c++) {
911 CellStruct& cs = cellAt(r, c);
912 RenderTableCell* cell = cs.primaryCell();
914 if (!cell || cs.inColSpan)
917 int rowIndex = cell->rowIndex();
918 int rHeight = m_rowPos[rowIndex + cell->rowSpan()] - m_rowPos[rowIndex] - vspacing;
920 // Force percent height children to lay themselves out again.
921 // This will cause these children to grow to fill the cell.
922 // FIXME: There is still more work to do here to fully match WinIE (should
923 // it become necessary to do so). In quirks mode, WinIE behaves like we
924 // do, but it will clip the cells that spill out of the table section. In
925 // strict mode, Mozilla and WinIE both regrow the table to accommodate the
926 // new height of the cell (thus letting the percentages cause growth one
927 // time only). We may also not be handling row-spanning cells correctly.
929 // Note also the oddity where replaced elements always flex, and yet blocks/tables do
930 // not necessarily flex. WinIE is crazy and inconsistent, and we can't hope to
931 // match the behavior perfectly, but we'll continue to refine it as we discover new
933 bool cellChildrenFlex = false;
934 bool flexAllChildren = cell->style()->logicalHeight().isFixed()
935 || (!table()->style()->logicalHeight().isAuto() && rHeight != cell->logicalHeight());
937 for (RenderObject* child = cell->firstChild(); child; child = child->nextSibling()) {
938 if (!child->isText() && child->style()->logicalHeight().isPercent()
939 && (flexAllChildren || shouldFlexCellChild(child))
940 && (!child->isTable() || toRenderTable(child)->hasSections())) {
941 cellChildrenFlex = true;
946 if (!cellChildrenFlex) {
947 if (TrackedRendererListHashSet* percentHeightDescendants = cell->percentHeightDescendants()) {
948 TrackedRendererListHashSet::iterator end = percentHeightDescendants->end();
949 for (TrackedRendererListHashSet::iterator it = percentHeightDescendants->begin(); it != end; ++it) {
950 if (flexAllChildren || shouldFlexCellChild(*it)) {
951 cellChildrenFlex = true;
958 if (cellChildrenFlex) {
959 // Alignment within a cell is based off the calculated
960 // height, which becomes irrelevant once the cell has
961 // been resized based off its percentage.
962 cell->setOverrideLogicalContentHeightFromRowHeight(rHeight);
963 cell->forceChildLayout();
965 // If the baseline moved, we may have to update the data for our row. Find out the new baseline.
966 if (cell->isBaselineAligned()) {
967 LayoutUnit baseline = cell->cellBaselinePosition();
968 if (baseline > cell->borderBefore() + cell->paddingBefore())
969 m_grid[r].baseline = max(m_grid[r].baseline, baseline);
973 SubtreeLayoutScope layouter(*cell);
974 cell->computeIntrinsicPadding(rHeight, layouter);
976 LayoutRect oldCellRect = cell->frameRect();
978 setLogicalPositionForCell(cell, c);
980 if (!cell->needsLayout())
981 cell->markForPaginationRelayoutIfNeeded(layouter);
983 cell->layoutIfNeeded();
985 // FIXME: Make pagination work with vertical tables.
986 if (view()->layoutState()->pageLogicalHeight() && cell->logicalHeight() != rHeight) {
987 // FIXME: Pagination might have made us change size. For now just shrink or grow the cell to fit without doing a relayout.
988 // 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
989 // either. It's at least stable though and won't result in an infinite # of relayouts that may never stabilize.
990 LayoutUnit oldLogicalHeight = cell->logicalHeight();
991 if (oldLogicalHeight > rHeight)
992 rowHeightIncreaseForPagination = max<int>(rowHeightIncreaseForPagination, oldLogicalHeight - rHeight);
993 cell->setLogicalHeight(rHeight);
994 cell->computeOverflow(oldLogicalHeight, false);
997 LayoutSize childOffset(cell->location() - oldCellRect.location());
998 if (childOffset.width() || childOffset.height()) {
999 if (!RuntimeEnabledFeatures::repaintAfterLayoutEnabled())
1000 view()->addLayoutDelta(childOffset);
1002 // If the child moved, we have to repaint it as well as any floating/positioned
1003 // descendants. An exception is if we need a layout. In this case, we know we're going to
1004 // repaint ourselves (and the child) anyway.
1005 if (!table()->selfNeedsLayout() && cell->checkForRepaintDuringLayout())
1006 cell->repaintDuringLayoutIfMoved(oldCellRect);
1009 if (rowHeightIncreaseForPagination) {
1010 for (unsigned rowIndex = r + 1; rowIndex <= totalRows; rowIndex++)
1011 m_rowPos[rowIndex] += rowHeightIncreaseForPagination;
1012 for (unsigned c = 0; c < nEffCols; ++c) {
1013 Vector<RenderTableCell*, 1>& cells = cellAt(r, c).cells;
1014 for (size_t i = 0; i < cells.size(); ++i) {
1015 LayoutUnit oldLogicalHeight = cells[i]->logicalHeight();
1016 cells[i]->setLogicalHeight(oldLogicalHeight + rowHeightIncreaseForPagination);
1017 cells[i]->computeOverflow(oldLogicalHeight, false);
1023 ASSERT(!needsLayout());
1025 setLogicalHeight(m_rowPos[totalRows]);
1027 computeOverflowFromCells(totalRows, nEffCols);
1030 void RenderTableSection::computeOverflowFromCells()
1032 unsigned totalRows = m_grid.size();
1033 unsigned nEffCols = table()->numEffCols();
1034 computeOverflowFromCells(totalRows, nEffCols);
1037 void RenderTableSection::computeOverflowFromCells(unsigned totalRows, unsigned nEffCols)
1039 unsigned totalCellsCount = nEffCols * totalRows;
1040 unsigned maxAllowedOverflowingCellsCount = totalCellsCount < gMinTableSizeToUseFastPaintPathWithOverflowingCell ? 0 : gMaxAllowedOverflowingCellRatioForFastPaintPath * totalCellsCount;
1043 bool hasOverflowingCell = false;
1045 // Now that our height has been determined, add in overflow from cells.
1046 for (unsigned r = 0; r < totalRows; r++) {
1047 for (unsigned c = 0; c < nEffCols; c++) {
1048 CellStruct& cs = cellAt(r, c);
1049 RenderTableCell* cell = cs.primaryCell();
1050 if (!cell || cs.inColSpan)
1052 if (r < totalRows - 1 && cell == primaryCellAt(r + 1, c))
1054 addOverflowFromChild(cell);
1056 hasOverflowingCell |= cell->hasVisualOverflow();
1058 if (cell->hasVisualOverflow() && !m_forceSlowPaintPathWithOverflowingCell) {
1059 m_overflowingCells.add(cell);
1060 if (m_overflowingCells.size() > maxAllowedOverflowingCellsCount) {
1061 // We need to set m_forcesSlowPaintPath only if there is a least one overflowing cells as the hit testing code rely on this information.
1062 m_forceSlowPaintPathWithOverflowingCell = true;
1063 // The slow path does not make any use of the overflowing cells info, don't hold on to the memory.
1064 m_overflowingCells.clear();
1070 ASSERT(hasOverflowingCell == this->hasOverflowingCell());
1073 int RenderTableSection::calcBlockDirectionOuterBorder(BlockBorderSide side) const
1075 unsigned totalCols = table()->numEffCols();
1076 if (!m_grid.size() || !totalCols)
1079 unsigned borderWidth = 0;
1081 const BorderValue& sb = side == BorderBefore ? style()->borderBefore() : style()->borderAfter();
1082 if (sb.style() == BHIDDEN)
1084 if (sb.style() > BHIDDEN)
1085 borderWidth = sb.width();
1087 const BorderValue& rb = side == BorderBefore ? firstChild()->style()->borderBefore() : lastChild()->style()->borderAfter();
1088 if (rb.style() == BHIDDEN)
1090 if (rb.style() > BHIDDEN && rb.width() > borderWidth)
1091 borderWidth = rb.width();
1093 bool allHidden = true;
1094 for (unsigned c = 0; c < totalCols; c++) {
1095 const CellStruct& current = cellAt(side == BorderBefore ? 0 : m_grid.size() - 1, c);
1096 if (current.inColSpan || !current.hasCells())
1098 const RenderStyle* primaryCellStyle = current.primaryCell()->style();
1099 const BorderValue& cb = side == BorderBefore ? primaryCellStyle->borderBefore() : primaryCellStyle->borderAfter(); // FIXME: Make this work with perpendicular and flipped cells.
1100 // FIXME: Don't repeat for the same col group
1101 RenderTableCol* colGroup = table()->colElement(c);
1103 const BorderValue& gb = side == BorderBefore ? colGroup->style()->borderBefore() : colGroup->style()->borderAfter();
1104 if (gb.style() == BHIDDEN || cb.style() == BHIDDEN)
1107 if (gb.style() > BHIDDEN && gb.width() > borderWidth)
1108 borderWidth = gb.width();
1109 if (cb.style() > BHIDDEN && cb.width() > borderWidth)
1110 borderWidth = cb.width();
1112 if (cb.style() == BHIDDEN)
1115 if (cb.style() > BHIDDEN && cb.width() > borderWidth)
1116 borderWidth = cb.width();
1122 if (side == BorderAfter)
1123 borderWidth++; // Distribute rounding error
1124 return borderWidth / 2;
1127 int RenderTableSection::calcInlineDirectionOuterBorder(InlineBorderSide side) const
1129 unsigned totalCols = table()->numEffCols();
1130 if (!m_grid.size() || !totalCols)
1132 unsigned colIndex = side == BorderStart ? 0 : totalCols - 1;
1134 unsigned borderWidth = 0;
1136 const BorderValue& sb = side == BorderStart ? style()->borderStart() : style()->borderEnd();
1137 if (sb.style() == BHIDDEN)
1139 if (sb.style() > BHIDDEN)
1140 borderWidth = sb.width();
1142 if (RenderTableCol* colGroup = table()->colElement(colIndex)) {
1143 const BorderValue& gb = side == BorderStart ? colGroup->style()->borderStart() : colGroup->style()->borderEnd();
1144 if (gb.style() == BHIDDEN)
1146 if (gb.style() > BHIDDEN && gb.width() > borderWidth)
1147 borderWidth = gb.width();
1150 bool allHidden = true;
1151 for (unsigned r = 0; r < m_grid.size(); r++) {
1152 const CellStruct& current = cellAt(r, colIndex);
1153 if (!current.hasCells())
1155 // FIXME: Don't repeat for the same cell
1156 const RenderStyle* primaryCellStyle = current.primaryCell()->style();
1157 const RenderStyle* primaryCellParentStyle = current.primaryCell()->parent()->style();
1158 const BorderValue& cb = side == BorderStart ? primaryCellStyle->borderStart() : primaryCellStyle->borderEnd(); // FIXME: Make this work with perpendicular and flipped cells.
1159 const BorderValue& rb = side == BorderStart ? primaryCellParentStyle->borderStart() : primaryCellParentStyle->borderEnd();
1160 if (cb.style() == BHIDDEN || rb.style() == BHIDDEN)
1163 if (cb.style() > BHIDDEN && cb.width() > borderWidth)
1164 borderWidth = cb.width();
1165 if (rb.style() > BHIDDEN && rb.width() > borderWidth)
1166 borderWidth = rb.width();
1171 if ((side == BorderStart) != table()->style()->isLeftToRightDirection())
1172 borderWidth++; // Distribute rounding error
1173 return borderWidth / 2;
1176 void RenderTableSection::recalcOuterBorder()
1178 m_outerBorderBefore = calcBlockDirectionOuterBorder(BorderBefore);
1179 m_outerBorderAfter = calcBlockDirectionOuterBorder(BorderAfter);
1180 m_outerBorderStart = calcInlineDirectionOuterBorder(BorderStart);
1181 m_outerBorderEnd = calcInlineDirectionOuterBorder(BorderEnd);
1184 int RenderTableSection::firstLineBoxBaseline() const
1189 int firstLineBaseline = m_grid[0].baseline;
1190 if (firstLineBaseline)
1191 return firstLineBaseline + m_rowPos[0];
1193 firstLineBaseline = -1;
1194 const Row& firstRow = m_grid[0].row;
1195 for (size_t i = 0; i < firstRow.size(); ++i) {
1196 const CellStruct& cs = firstRow.at(i);
1197 const RenderTableCell* cell = cs.primaryCell();
1198 // Only cells with content have a baseline
1199 if (cell && cell->contentLogicalHeight())
1200 firstLineBaseline = max<int>(firstLineBaseline, cell->logicalTop() + cell->paddingBefore() + cell->borderBefore() + cell->contentLogicalHeight());
1203 return firstLineBaseline;
1206 void RenderTableSection::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1208 ANNOTATE_GRAPHICS_CONTEXT(paintInfo, this);
1210 ASSERT(!needsLayout());
1211 // avoid crashing on bugs that cause us to paint with dirty layout
1215 unsigned totalRows = m_grid.size();
1216 unsigned totalCols = table()->columns().size();
1218 if (!totalRows || !totalCols)
1221 LayoutPoint adjustedPaintOffset = paintOffset + location();
1223 PaintPhase phase = paintInfo.phase;
1224 bool pushedClip = pushContentsClip(paintInfo, adjustedPaintOffset, ForceContentsClip);
1225 paintObject(paintInfo, adjustedPaintOffset);
1227 popContentsClip(paintInfo, phase, adjustedPaintOffset);
1229 if ((phase == PaintPhaseOutline || phase == PaintPhaseSelfOutline) && style()->visibility() == VISIBLE)
1230 paintOutline(paintInfo, LayoutRect(adjustedPaintOffset, size()));
1233 static inline bool compareCellPositions(RenderTableCell* elem1, RenderTableCell* elem2)
1235 return elem1->rowIndex() < elem2->rowIndex();
1238 // This comparison is used only when we have overflowing cells as we have an unsorted array to sort. We thus need
1239 // to sort both on rows and columns to properly repaint.
1240 static inline bool compareCellPositionsWithOverflowingCells(RenderTableCell* elem1, RenderTableCell* elem2)
1242 if (elem1->rowIndex() != elem2->rowIndex())
1243 return elem1->rowIndex() < elem2->rowIndex();
1245 return elem1->col() < elem2->col();
1248 void RenderTableSection::paintCell(RenderTableCell* cell, PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1250 LayoutPoint cellPoint = flipForWritingModeForChild(cell, paintOffset);
1251 PaintPhase paintPhase = paintInfo.phase;
1252 RenderTableRow* row = toRenderTableRow(cell->parent());
1254 if (paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChildBlockBackground) {
1255 // We need to handle painting a stack of backgrounds. This stack (from bottom to top) consists of
1256 // the column group, column, row group, row, and then the cell.
1257 RenderTableCol* column = table()->colElement(cell->col());
1258 RenderTableCol* columnGroup = column ? column->enclosingColumnGroup() : 0;
1260 // Column groups and columns first.
1261 // FIXME: Columns and column groups do not currently support opacity, and they are being painted "too late" in
1262 // the stack, since we have already opened a transparency layer (potentially) for the table row group.
1263 // Note that we deliberately ignore whether or not the cell has a layer, since these backgrounds paint "behind" the
1265 cell->paintBackgroundsBehindCell(paintInfo, cellPoint, columnGroup);
1266 cell->paintBackgroundsBehindCell(paintInfo, cellPoint, column);
1268 // Paint the row group next.
1269 cell->paintBackgroundsBehindCell(paintInfo, cellPoint, this);
1271 // Paint the row next, but only if it doesn't have a layer. If a row has a layer, it will be responsible for
1272 // painting the row background for the cell.
1273 if (!row->hasSelfPaintingLayer())
1274 cell->paintBackgroundsBehindCell(paintInfo, cellPoint, row);
1276 if ((!cell->hasSelfPaintingLayer() && !row->hasSelfPaintingLayer()))
1277 cell->paint(paintInfo, cellPoint);
1280 LayoutRect RenderTableSection::logicalRectForWritingModeAndDirection(const LayoutRect& rect) const
1282 LayoutRect tableAlignedRect(rect);
1284 flipForWritingMode(tableAlignedRect);
1286 if (!style()->isHorizontalWritingMode())
1287 tableAlignedRect = tableAlignedRect.transposedRect();
1289 const Vector<int>& columnPos = table()->columnPositions();
1290 // FIXME: The table's direction should determine our row's direction, not the section's (see bug 96691).
1291 if (!style()->isLeftToRightDirection())
1292 tableAlignedRect.setX(columnPos[columnPos.size() - 1] - tableAlignedRect.maxX());
1294 return tableAlignedRect;
1297 CellSpan RenderTableSection::dirtiedRows(const LayoutRect& damageRect) const
1299 if (m_forceSlowPaintPathWithOverflowingCell)
1300 return fullTableRowSpan();
1302 CellSpan coveredRows = spannedRows(damageRect);
1304 // To repaint the border we might need to repaint first or last row even if they are not spanned themselves.
1305 if (coveredRows.start() >= m_rowPos.size() - 1 && m_rowPos[m_rowPos.size() - 1] + table()->outerBorderAfter() >= damageRect.y())
1306 --coveredRows.start();
1308 if (!coveredRows.end() && m_rowPos[0] - table()->outerBorderBefore() <= damageRect.maxY())
1309 ++coveredRows.end();
1314 CellSpan RenderTableSection::dirtiedColumns(const LayoutRect& damageRect) const
1316 if (m_forceSlowPaintPathWithOverflowingCell)
1317 return fullTableColumnSpan();
1319 CellSpan coveredColumns = spannedColumns(damageRect);
1321 const Vector<int>& columnPos = table()->columnPositions();
1322 // To repaint the border we might need to repaint first or last column even if they are not spanned themselves.
1323 if (coveredColumns.start() >= columnPos.size() - 1 && columnPos[columnPos.size() - 1] + table()->outerBorderEnd() >= damageRect.x())
1324 --coveredColumns.start();
1326 if (!coveredColumns.end() && columnPos[0] - table()->outerBorderStart() <= damageRect.maxX())
1327 ++coveredColumns.end();
1329 return coveredColumns;
1332 CellSpan RenderTableSection::spannedRows(const LayoutRect& flippedRect) const
1334 // Find the first row that starts after rect top.
1335 unsigned nextRow = std::upper_bound(m_rowPos.begin(), m_rowPos.end(), flippedRect.y()) - m_rowPos.begin();
1337 if (nextRow == m_rowPos.size())
1338 return CellSpan(m_rowPos.size() - 1, m_rowPos.size() - 1); // After all rows.
1340 unsigned startRow = nextRow > 0 ? nextRow - 1 : 0;
1342 // Find the first row that starts after rect bottom.
1344 if (m_rowPos[nextRow] >= flippedRect.maxY())
1347 endRow = std::upper_bound(m_rowPos.begin() + nextRow, m_rowPos.end(), flippedRect.maxY()) - m_rowPos.begin();
1348 if (endRow == m_rowPos.size())
1349 endRow = m_rowPos.size() - 1;
1352 return CellSpan(startRow, endRow);
1355 CellSpan RenderTableSection::spannedColumns(const LayoutRect& flippedRect) const
1357 const Vector<int>& columnPos = table()->columnPositions();
1359 // Find the first column that starts after rect left.
1360 // lower_bound doesn't handle the edge between two cells properly as it would wrongly return the
1361 // cell on the logical top/left.
1362 // upper_bound on the other hand properly returns the cell on the logical bottom/right, which also
1363 // matches the behavior of other browsers.
1364 unsigned nextColumn = std::upper_bound(columnPos.begin(), columnPos.end(), flippedRect.x()) - columnPos.begin();
1366 if (nextColumn == columnPos.size())
1367 return CellSpan(columnPos.size() - 1, columnPos.size() - 1); // After all columns.
1369 unsigned startColumn = nextColumn > 0 ? nextColumn - 1 : 0;
1371 // Find the first column that starts after rect right.
1373 if (columnPos[nextColumn] >= flippedRect.maxX())
1374 endColumn = nextColumn;
1376 endColumn = std::upper_bound(columnPos.begin() + nextColumn, columnPos.end(), flippedRect.maxX()) - columnPos.begin();
1377 if (endColumn == columnPos.size())
1378 endColumn = columnPos.size() - 1;
1381 return CellSpan(startColumn, endColumn);
1385 void RenderTableSection::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1387 LayoutRect localRepaintRect = paintInfo.rect;
1388 localRepaintRect.moveBy(-paintOffset);
1390 LayoutRect tableAlignedRect = logicalRectForWritingModeAndDirection(localRepaintRect);
1392 CellSpan dirtiedRows = this->dirtiedRows(tableAlignedRect);
1393 CellSpan dirtiedColumns = this->dirtiedColumns(tableAlignedRect);
1395 if (dirtiedColumns.start() < dirtiedColumns.end()) {
1396 if (!m_hasMultipleCellLevels && !m_overflowingCells.size()) {
1397 if (paintInfo.phase == PaintPhaseCollapsedTableBorders) {
1398 // Collapsed borders are painted from the bottom right to the top left so that precedence
1399 // due to cell position is respected.
1400 for (unsigned r = dirtiedRows.end(); r > dirtiedRows.start(); r--) {
1401 unsigned row = r - 1;
1402 for (unsigned c = dirtiedColumns.end(); c > dirtiedColumns.start(); c--) {
1403 unsigned col = c - 1;
1404 CellStruct& current = cellAt(row, col);
1405 RenderTableCell* cell = current.primaryCell();
1406 if (!cell || (row > dirtiedRows.start() && primaryCellAt(row - 1, col) == cell) || (col > dirtiedColumns.start() && primaryCellAt(row, col - 1) == cell))
1408 LayoutPoint cellPoint = flipForWritingModeForChild(cell, paintOffset);
1409 cell->paintCollapsedBorders(paintInfo, cellPoint);
1413 // Draw the dirty cells in the order that they appear.
1414 for (unsigned r = dirtiedRows.start(); r < dirtiedRows.end(); r++) {
1415 RenderTableRow* row = m_grid[r].rowRenderer;
1416 if (row && !row->hasSelfPaintingLayer())
1417 row->paintOutlineForRowIfNeeded(paintInfo, paintOffset);
1418 for (unsigned c = dirtiedColumns.start(); c < dirtiedColumns.end(); c++) {
1419 CellStruct& current = cellAt(r, c);
1420 RenderTableCell* cell = current.primaryCell();
1421 if (!cell || (r > dirtiedRows.start() && primaryCellAt(r - 1, c) == cell) || (c > dirtiedColumns.start() && primaryCellAt(r, c - 1) == cell))
1423 paintCell(cell, paintInfo, paintOffset);
1428 // The overflowing cells should be scarce to avoid adding a lot of cells to the HashSet.
1430 unsigned totalRows = m_grid.size();
1431 unsigned totalCols = table()->columns().size();
1432 ASSERT(m_overflowingCells.size() < totalRows * totalCols * gMaxAllowedOverflowingCellRatioForFastPaintPath);
1435 // To make sure we properly repaint the section, we repaint all the overflowing cells that we collected.
1436 Vector<RenderTableCell*> cells;
1437 copyToVector(m_overflowingCells, cells);
1439 HashSet<RenderTableCell*> spanningCells;
1441 for (unsigned r = dirtiedRows.start(); r < dirtiedRows.end(); r++) {
1442 RenderTableRow* row = m_grid[r].rowRenderer;
1443 if (row && !row->hasSelfPaintingLayer())
1444 row->paintOutlineForRowIfNeeded(paintInfo, paintOffset);
1445 for (unsigned c = dirtiedColumns.start(); c < dirtiedColumns.end(); c++) {
1446 CellStruct& current = cellAt(r, c);
1447 if (!current.hasCells())
1449 for (unsigned i = 0; i < current.cells.size(); ++i) {
1450 if (m_overflowingCells.contains(current.cells[i]))
1453 if (current.cells[i]->rowSpan() > 1 || current.cells[i]->colSpan() > 1) {
1454 if (!spanningCells.add(current.cells[i]).isNewEntry)
1458 cells.append(current.cells[i]);
1463 // Sort the dirty cells by paint order.
1464 if (!m_overflowingCells.size())
1465 std::stable_sort(cells.begin(), cells.end(), compareCellPositions);
1467 std::sort(cells.begin(), cells.end(), compareCellPositionsWithOverflowingCells);
1469 if (paintInfo.phase == PaintPhaseCollapsedTableBorders) {
1470 for (unsigned i = cells.size(); i > 0; --i) {
1471 LayoutPoint cellPoint = flipForWritingModeForChild(cells[i - 1], paintOffset);
1472 cells[i - 1]->paintCollapsedBorders(paintInfo, cellPoint);
1475 for (unsigned i = 0; i < cells.size(); ++i)
1476 paintCell(cells[i], paintInfo, paintOffset);
1482 void RenderTableSection::imageChanged(WrappedImagePtr, const IntRect*)
1484 // FIXME: Examine cells and repaint only the rect the image paints in.
1488 void RenderTableSection::recalcCells()
1490 ASSERT(m_needsCellRecalc);
1491 // We reset the flag here to ensure that |addCell| works. This is safe to do as
1492 // fillRowsWithDefaultStartingAtPosition makes sure we match the table's columns
1494 m_needsCellRecalc = false;
1500 for (RenderObject* row = firstChild(); row; row = row->nextSibling()) {
1501 if (row->isTableRow()) {
1502 unsigned insertionRow = m_cRow;
1507 RenderTableRow* tableRow = toRenderTableRow(row);
1508 m_grid[insertionRow].rowRenderer = tableRow;
1509 tableRow->setRowIndex(insertionRow);
1510 setRowLogicalHeightToRowStyleLogicalHeight(m_grid[insertionRow]);
1512 for (RenderObject* cell = row->firstChild(); cell; cell = cell->nextSibling()) {
1513 if (!cell->isTableCell())
1516 RenderTableCell* tableCell = toRenderTableCell(cell);
1517 addCell(tableCell, tableRow);
1522 m_grid.shrinkToFit();
1526 // FIXME: This function could be made O(1) in certain cases (like for the non-most-constrainive cells' case).
1527 void RenderTableSection::rowLogicalHeightChanged(unsigned rowIndex)
1529 if (needsCellRecalc())
1532 setRowLogicalHeightToRowStyleLogicalHeight(m_grid[rowIndex]);
1534 for (RenderObject* cell = m_grid[rowIndex].rowRenderer->firstChild(); cell; cell = cell->nextSibling()) {
1535 if (!cell->isTableCell())
1538 updateLogicalHeightForCell(m_grid[rowIndex], toRenderTableCell(cell));
1542 void RenderTableSection::setNeedsCellRecalc()
1544 m_needsCellRecalc = true;
1545 if (RenderTable* t = table())
1546 t->setNeedsSectionRecalc();
1549 unsigned RenderTableSection::numColumns() const
1551 unsigned result = 0;
1553 for (unsigned r = 0; r < m_grid.size(); ++r) {
1554 for (unsigned c = result; c < table()->numEffCols(); ++c) {
1555 const CellStruct& cell = cellAt(r, c);
1556 if (cell.hasCells() || cell.inColSpan)
1564 const BorderValue& RenderTableSection::borderAdjoiningStartCell(const RenderTableCell* cell) const
1566 ASSERT(cell->isFirstOrLastCellInRow());
1567 return hasSameDirectionAs(cell) ? style()->borderStart() : style()->borderEnd();
1570 const BorderValue& RenderTableSection::borderAdjoiningEndCell(const RenderTableCell* cell) const
1572 ASSERT(cell->isFirstOrLastCellInRow());
1573 return hasSameDirectionAs(cell) ? style()->borderEnd() : style()->borderStart();
1576 const RenderTableCell* RenderTableSection::firstRowCellAdjoiningTableStart() const
1578 unsigned adjoiningStartCellColumnIndex = hasSameDirectionAs(table()) ? 0 : table()->lastColumnIndex();
1579 return cellAt(0, adjoiningStartCellColumnIndex).primaryCell();
1582 const RenderTableCell* RenderTableSection::firstRowCellAdjoiningTableEnd() const
1584 unsigned adjoiningEndCellColumnIndex = hasSameDirectionAs(table()) ? table()->lastColumnIndex() : 0;
1585 return cellAt(0, adjoiningEndCellColumnIndex).primaryCell();
1588 void RenderTableSection::appendColumn(unsigned pos)
1590 ASSERT(!m_needsCellRecalc);
1592 for (unsigned row = 0; row < m_grid.size(); ++row)
1593 m_grid[row].row.resize(pos + 1);
1596 void RenderTableSection::splitColumn(unsigned pos, unsigned first)
1598 ASSERT(!m_needsCellRecalc);
1602 for (unsigned row = 0; row < m_grid.size(); ++row) {
1603 Row& r = m_grid[row].row;
1604 r.insert(pos + 1, CellStruct());
1605 if (r[pos].hasCells()) {
1606 r[pos + 1].cells.appendVector(r[pos].cells);
1607 RenderTableCell* cell = r[pos].primaryCell();
1609 ASSERT(cell->colSpan() >= (r[pos].inColSpan ? 1u : 0));
1610 unsigned colleft = cell->colSpan() - r[pos].inColSpan;
1611 if (first > colleft)
1612 r[pos + 1].inColSpan = 0;
1614 r[pos + 1].inColSpan = first + r[pos].inColSpan;
1616 r[pos + 1].inColSpan = 0;
1622 bool RenderTableSection::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction action)
1624 // If we have no children then we have nothing to do.
1628 // Table sections cannot ever be hit tested. Effectively they do not exist.
1629 // Just forward to our children always.
1630 LayoutPoint adjustedLocation = accumulatedOffset + location();
1632 if (hasOverflowClip() && !locationInContainer.intersects(overflowClipRect(adjustedLocation)))
1635 if (hasOverflowingCell()) {
1636 for (RenderObject* child = lastChild(); child; child = child->previousSibling()) {
1637 // FIXME: We have to skip over inline flows, since they can show up inside table rows
1638 // at the moment (a demoted inline <form> for example). If we ever implement a
1639 // table-specific hit-test method (which we should do for performance reasons anyway),
1640 // then we can remove this check.
1641 if (child->isBox() && !toRenderBox(child)->hasSelfPaintingLayer()) {
1642 LayoutPoint childPoint = flipForWritingModeForChild(toRenderBox(child), adjustedLocation);
1643 if (child->nodeAtPoint(request, result, locationInContainer, childPoint, action)) {
1644 updateHitTestResult(result, toLayoutPoint(locationInContainer.point() - childPoint));
1652 recalcCellsIfNeeded();
1654 LayoutRect hitTestRect = locationInContainer.boundingBox();
1655 hitTestRect.moveBy(-adjustedLocation);
1657 LayoutRect tableAlignedRect = logicalRectForWritingModeAndDirection(hitTestRect);
1658 CellSpan rowSpan = spannedRows(tableAlignedRect);
1659 CellSpan columnSpan = spannedColumns(tableAlignedRect);
1661 // Now iterate over the spanned rows and columns.
1662 for (unsigned hitRow = rowSpan.start(); hitRow < rowSpan.end(); ++hitRow) {
1663 for (unsigned hitColumn = columnSpan.start(); hitColumn < columnSpan.end(); ++hitColumn) {
1664 CellStruct& current = cellAt(hitRow, hitColumn);
1666 // If the cell is empty, there's nothing to do
1667 if (!current.hasCells())
1670 for (unsigned i = current.cells.size() ; i; ) {
1672 RenderTableCell* cell = current.cells[i];
1673 LayoutPoint cellPoint = flipForWritingModeForChild(cell, adjustedLocation);
1674 if (static_cast<RenderObject*>(cell)->nodeAtPoint(request, result, locationInContainer, cellPoint, action)) {
1675 updateHitTestResult(result, locationInContainer.point() - toLayoutSize(cellPoint));
1679 if (!result.isRectBasedTest())
1682 if (!result.isRectBasedTest())
1689 void RenderTableSection::removeCachedCollapsedBorders(const RenderTableCell* cell)
1691 if (!table()->collapseBorders())
1694 for (int side = CBSBefore; side <= CBSEnd; ++side)
1695 m_cellsCollapsedBorders.remove(make_pair(cell, side));
1698 void RenderTableSection::setCachedCollapsedBorder(const RenderTableCell* cell, CollapsedBorderSide side, CollapsedBorderValue border)
1700 ASSERT(table()->collapseBorders());
1701 m_cellsCollapsedBorders.set(make_pair(cell, side), border);
1704 CollapsedBorderValue& RenderTableSection::cachedCollapsedBorder(const RenderTableCell* cell, CollapsedBorderSide side)
1706 ASSERT(table()->collapseBorders());
1707 HashMap<pair<const RenderTableCell*, int>, CollapsedBorderValue>::iterator it = m_cellsCollapsedBorders.find(make_pair(cell, side));
1708 ASSERT_WITH_SECURITY_IMPLICATION(it != m_cellsCollapsedBorders.end());
1712 RenderTableSection* RenderTableSection::createAnonymousWithParentRenderer(const RenderObject* parent)
1714 RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyleWithDisplay(parent->style(), TABLE_ROW_GROUP);
1715 RenderTableSection* newSection = new RenderTableSection(0);
1716 newSection->setDocumentForAnonymous(&parent->document());
1717 newSection->setStyle(newStyle.release());
1721 void RenderTableSection::setLogicalPositionForCell(RenderTableCell* cell, unsigned effectiveColumn) const
1723 LayoutPoint oldCellLocation = cell->location();
1725 LayoutPoint cellLocation(0, m_rowPos[cell->rowIndex()]);
1726 int horizontalBorderSpacing = table()->hBorderSpacing();
1728 // FIXME: The table's direction should determine our row's direction, not the section's (see bug 96691).
1729 if (!style()->isLeftToRightDirection())
1730 cellLocation.setX(table()->columnPositions()[table()->numEffCols()] - table()->columnPositions()[table()->colToEffCol(cell->col() + cell->colSpan())] + horizontalBorderSpacing);
1732 cellLocation.setX(table()->columnPositions()[effectiveColumn] + horizontalBorderSpacing);
1734 cell->setLogicalLocation(cellLocation);
1736 if (!RuntimeEnabledFeatures::repaintAfterLayoutEnabled())
1737 view()->addLayoutDelta(oldCellLocation - cell->location());
1740 } // namespace WebCore