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 "RenderTableSection.h"
29 #include "HitTestResult.h"
30 #include "HTMLNames.h"
31 #include "PaintInfo.h"
32 #include "RenderNamedFlowFragment.h"
33 #include "RenderTableCell.h"
34 #include "RenderTableCol.h"
35 #include "RenderTableRow.h"
36 #include "RenderView.h"
37 #include "StyleInheritedData.h"
39 #include <wtf/HashSet.h>
40 #include <wtf/StackStats.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 setRowLogicalHeightToRowStyleLogicalHeightIfNotRelative(RenderTableSection::RowStruct& row)
52 ASSERT(row.rowRenderer);
53 row.logicalHeight = row.rowRenderer->style().logicalHeight();
54 if (row.logicalHeight.isRelative())
55 row.logicalHeight = Length();
58 static inline void updateLogicalHeightForCell(RenderTableSection::RowStruct& row, const RenderTableCell* cell)
60 // We ignore height settings on rowspan cells.
61 if (cell->rowSpan() != 1)
64 Length logicalHeight = cell->style().logicalHeight();
65 if (logicalHeight.isPositive() || (logicalHeight.isRelative() && logicalHeight.value() >= 0)) {
66 Length cRowLogicalHeight = row.logicalHeight;
67 switch (logicalHeight.type()) {
69 if (!cRowLogicalHeight.isPercentNotCalculated() || cRowLogicalHeight.percent() < logicalHeight.percent())
70 row.logicalHeight = logicalHeight;
73 if (cRowLogicalHeight.isAuto() || cRowLogicalHeight.isRelative()
74 || (cRowLogicalHeight.isFixed() && cRowLogicalHeight.value() < logicalHeight.value()))
75 row.logicalHeight = logicalHeight;
84 RenderTableSection::RenderTableSection(Element& element, PassRef<RenderStyle> style)
85 : RenderBox(element, std::move(style), 0)
88 , m_outerBorderStart(0)
90 , m_outerBorderBefore(0)
91 , m_outerBorderAfter(0)
92 , m_needsCellRecalc(false)
93 , m_hasMultipleCellLevels(false)
98 RenderTableSection::RenderTableSection(Document& document, PassRef<RenderStyle> style)
99 : RenderBox(document, std::move(style), 0)
102 , m_outerBorderStart(0)
103 , m_outerBorderEnd(0)
104 , m_outerBorderBefore(0)
105 , m_outerBorderAfter(0)
106 , m_needsCellRecalc(false)
107 , m_hasMultipleCellLevels(false)
112 RenderTableSection::~RenderTableSection()
116 void RenderTableSection::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
118 RenderBox::styleDidChange(diff, oldStyle);
119 propagateStyleToAnonymousChildren(PropagateToAllChildren);
121 // If border was changed, notify table.
122 RenderTable* table = this->table();
123 if (table && !table->selfNeedsLayout() && !table->normalChildNeedsLayout() && oldStyle && oldStyle->border() != style().border())
124 table->invalidateCollapsedBorders();
127 void RenderTableSection::willBeRemovedFromTree()
129 RenderBox::willBeRemovedFromTree();
131 // Preventively invalidate our cells as we may be re-inserted into
132 // a new table which would require us to rebuild our structure.
133 setNeedsCellRecalc();
136 void RenderTableSection::addChild(RenderObject* child, RenderObject* beforeChild)
138 if (!child->isTableRow()) {
139 RenderObject* last = beforeChild;
142 if (last && last->isAnonymous() && !last->isBeforeOrAfterContent()) {
143 RenderTableRow* row = toRenderTableRow(last);
144 if (beforeChild == row)
145 beforeChild = row->firstCell();
146 row->addChild(child, beforeChild);
150 if (beforeChild && !beforeChild->isAnonymous() && beforeChild->parent() == this) {
151 RenderObject* row = beforeChild->previousSibling();
152 if (row && row->isTableRow() && row->isAnonymous()) {
153 toRenderTableRow(row)->addChild(child);
158 // If beforeChild is inside an anonymous cell/row, insert into the cell or into
159 // the anonymous row containing it, if there is one.
160 RenderObject* lastBox = last;
161 while (lastBox && lastBox->parent()->isAnonymous() && !lastBox->isTableRow())
162 lastBox = lastBox->parent();
163 if (lastBox && lastBox->isAnonymous() && !lastBox->isBeforeOrAfterContent()) {
164 toRenderTableRow(lastBox)->addChild(child, beforeChild);
168 RenderTableRow* row = RenderTableRow::createAnonymousWithParentRenderer(this);
169 addChild(row, beforeChild);
170 row->addChild(child);
175 setNeedsCellRecalc();
177 unsigned insertionRow = m_cRow;
183 RenderTableRow* row = toRenderTableRow(child);
184 m_grid[insertionRow].rowRenderer = row;
185 row->setRowIndex(insertionRow);
188 setRowLogicalHeightToRowStyleLogicalHeightIfNotRelative(m_grid[insertionRow]);
190 if (beforeChild && beforeChild->parent() != this)
191 beforeChild = splitAnonymousBoxesAroundChild(beforeChild);
193 ASSERT(!beforeChild || beforeChild->isTableRow());
194 RenderBox::addChild(child, beforeChild);
197 void RenderTableSection::ensureRows(unsigned numRows)
199 if (numRows <= m_grid.size())
202 unsigned oldSize = m_grid.size();
203 m_grid.grow(numRows);
205 unsigned effectiveColumnCount = std::max(1u, table()->numEffCols());
206 for (unsigned row = oldSize; row < m_grid.size(); ++row)
207 m_grid[row].row.resizeToFit(effectiveColumnCount);
210 void RenderTableSection::addCell(RenderTableCell* cell, RenderTableRow* row)
212 // We don't insert the cell if we need cell recalc as our internal columns' representation
213 // will have drifted from the table's representation. Also recalcCells will call addCell
214 // at a later time after sync'ing our columns' with the table's.
215 if (needsCellRecalc())
218 unsigned rSpan = cell->rowSpan();
219 unsigned cSpan = cell->colSpan();
220 const Vector<RenderTable::ColumnStruct>& columns = table()->columns();
221 unsigned nCols = columns.size();
222 unsigned insertionRow = row->rowIndex();
224 // ### mozilla still seems to do the old HTML way, even for strict DTD
225 // (see the annotation on table cell layouting in the CSS specs and the testcase below:
227 // <TR><TD>1 <TD rowspan="2">2 <TD>3 <TD>4
228 // <TR><TD colspan="2">5
230 while (m_cCol < nCols && (cellAt(insertionRow, m_cCol).hasCells() || cellAt(insertionRow, m_cCol).inColSpan))
233 updateLogicalHeightForCell(m_grid[insertionRow], cell);
235 ensureRows(insertionRow + rSpan);
237 m_grid[insertionRow].rowRenderer = row;
239 unsigned col = m_cCol;
240 // tell the cell where it is
241 bool inColSpan = false;
243 unsigned currentSpan;
244 if (m_cCol >= nCols) {
245 table()->appendColumn(cSpan);
248 if (cSpan < columns[m_cCol].span)
249 table()->splitColumn(m_cCol, cSpan);
250 currentSpan = columns[m_cCol].span;
252 for (unsigned r = 0; r < rSpan; r++) {
253 CellStruct& c = cellAt(insertionRow + r, m_cCol);
255 c.cells.append(cell);
256 // If cells overlap then we take the slow path for painting.
257 if (c.cells.size() > 1)
258 m_hasMultipleCellLevels = true;
263 cSpan -= currentSpan;
266 cell->setCol(table()->effColToCol(col));
269 int RenderTableSection::calcRowLogicalHeight()
272 SetLayoutNeededForbiddenScope layoutForbiddenScope(this);
275 ASSERT(!needsLayout());
277 RenderTableCell* cell;
279 // We ignore the border-spacing on any non-top section as it is already included in the previous section's last row position.
281 if (this == table()->topSection())
282 spacing = table()->vBorderSpacing();
284 LayoutStateMaintainer statePusher(view());
286 m_rowPos.resize(m_grid.size() + 1);
287 m_rowPos[0] = spacing;
289 unsigned totalRows = m_grid.size();
291 for (unsigned r = 0; r < totalRows; r++) {
292 m_grid[r].baseline = 0;
293 LayoutUnit baselineDescent = 0;
295 // Our base size is the biggest logical height from our cells' styles (excluding row spanning cells).
296 m_rowPos[r + 1] = std::max(m_rowPos[r] + minimumValueForLength(m_grid[r].logicalHeight, 0).round(), 0);
298 Row& row = m_grid[r].row;
299 unsigned totalCols = row.size();
301 for (unsigned c = 0; c < totalCols; c++) {
302 CellStruct& current = cellAt(r, c);
303 for (unsigned i = 0; i < current.cells.size(); i++) {
304 cell = current.cells[i];
305 if (current.inColSpan && cell->rowSpan() == 1)
308 // FIXME: We are always adding the height of a rowspan to the last rows which doesn't match
309 // other browsers. See webkit.org/b/52185 for example.
310 if ((cell->rowIndex() + cell->rowSpan() - 1) != r) {
311 // We will apply the height of the rowspan to the current row if next row is not valid.
312 if ((r + 1) < totalRows) {
314 CellStruct nextRowCell = cellAt(r + 1, col);
316 // We are trying to find that next row is valid or not.
317 while (nextRowCell.cells.size() && nextRowCell.cells[0]->rowSpan() > 1 && nextRowCell.cells[0]->rowIndex() < (r + 1)) {
320 nextRowCell = cellAt(r + 1, col);
325 // We are adding the height of the rowspan to the current row if next row is not valid.
326 if (col < totalCols && nextRowCell.cells.size())
331 // For row spanning cells, |r| is the last row in the span.
332 unsigned cellStartRow = cell->rowIndex();
334 if (cell->hasOverrideHeight()) {
335 if (!statePusher.didPush()) {
336 // Technically, we should also push state for the row, but since
337 // rows don't push a coordinate transform, that's not necessary.
338 statePusher.push(*this, locationOffset());
340 cell->clearIntrinsicPadding();
341 cell->clearOverrideSize();
342 cell->setChildNeedsLayout(MarkOnlyThis);
343 cell->layoutIfNeeded();
346 int cellLogicalHeight = cell->logicalHeightForRowSizing();
347 m_rowPos[r + 1] = std::max(m_rowPos[r + 1], m_rowPos[cellStartRow] + cellLogicalHeight);
349 // Find out the baseline. The baseline is set on the first row in a rowspan.
350 if (cell->isBaselineAligned()) {
351 LayoutUnit baselinePosition = cell->cellBaselinePosition();
352 if (baselinePosition > cell->borderAndPaddingBefore()) {
353 m_grid[cellStartRow].baseline = std::max(m_grid[cellStartRow].baseline, baselinePosition);
354 // 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
355 // become the baseline descent applied to the rest of the row. Also we don't account for the baseline descent of
356 // non-spanning cells when computing a spanning cell's extent.
357 int cellStartRowBaselineDescent = 0;
358 if (cell->rowSpan() == 1) {
359 baselineDescent = std::max(baselineDescent, cellLogicalHeight - (baselinePosition - cell->intrinsicPaddingBefore()));
360 cellStartRowBaselineDescent = baselineDescent;
362 m_rowPos[cellStartRow + 1] = std::max<int>(m_rowPos[cellStartRow + 1], m_rowPos[cellStartRow] + m_grid[cellStartRow].baseline + cellStartRowBaselineDescent);
368 // Add the border-spacing to our final position.
369 // Use table border-spacing even in non-top sections
370 spacing = table()->vBorderSpacing();
371 m_rowPos[r + 1] += m_grid[r].rowRenderer ? spacing : 0;
372 m_rowPos[r + 1] = std::max(m_rowPos[r + 1], m_rowPos[r]);
375 ASSERT(!needsLayout());
379 return m_rowPos[m_grid.size()];
382 void RenderTableSection::layout()
384 StackStats::LayoutCheckPoint layoutCheckPoint;
385 ASSERT(needsLayout());
386 ASSERT(!needsCellRecalc());
387 ASSERT(!table()->needsSectionRecalc());
389 // addChild may over-grow m_grid but we don't want to throw away the memory too early as addChild
390 // can be called in a loop (e.g during parsing). Doing it now ensures we have a stable-enough structure.
391 m_grid.shrinkToFit();
393 LayoutStateMaintainer statePusher(view(), *this, locationOffset(), hasTransform() || hasReflection() || style().isFlippedBlocksWritingMode());
395 const Vector<int>& columnPos = table()->columnPositions();
397 for (unsigned r = 0; r < m_grid.size(); ++r) {
398 Row& row = m_grid[r].row;
399 unsigned cols = row.size();
400 // First, propagate our table layout's information to the cells. This will mark the row as needing layout
401 // if there was a column logical width change.
402 for (unsigned startColumn = 0; startColumn < cols; ++startColumn) {
403 CellStruct& current = row[startColumn];
404 RenderTableCell* cell = current.primaryCell();
405 if (!cell || current.inColSpan)
408 unsigned endCol = startColumn;
409 unsigned cspan = cell->colSpan();
410 while (cspan && endCol < cols) {
411 ASSERT(endCol < table()->columns().size());
412 cspan -= table()->columns()[endCol].span;
415 int tableLayoutLogicalWidth = columnPos[endCol] - columnPos[startColumn] - table()->hBorderSpacing();
416 cell->setCellLogicalWidth(tableLayoutLogicalWidth);
419 if (RenderTableRow* rowRenderer = m_grid[r].rowRenderer)
420 rowRenderer->layoutIfNeeded();
427 void RenderTableSection::distributeExtraLogicalHeightToPercentRows(int& extraLogicalHeight, int totalPercent)
432 unsigned totalRows = m_grid.size();
433 int totalHeight = m_rowPos[totalRows] + extraLogicalHeight;
434 int totalLogicalHeightAdded = 0;
435 totalPercent = std::min(totalPercent, 100);
436 int rowHeight = m_rowPos[1] - m_rowPos[0];
437 for (unsigned r = 0; r < totalRows; ++r) {
438 if (totalPercent > 0 && m_grid[r].logicalHeight.isPercentNotCalculated()) {
439 int toAdd = std::min<int>(extraLogicalHeight, (totalHeight * m_grid[r].logicalHeight.percent() / 100) - rowHeight);
440 // If toAdd is negative, then we don't want to shrink the row (this bug
441 // affected Outlook Web Access).
442 toAdd = std::max(0, toAdd);
443 totalLogicalHeightAdded += toAdd;
444 extraLogicalHeight -= toAdd;
445 totalPercent -= m_grid[r].logicalHeight.percent();
447 ASSERT(totalRows >= 1);
448 if (r < totalRows - 1)
449 rowHeight = m_rowPos[r + 2] - m_rowPos[r + 1];
450 m_rowPos[r + 1] += totalLogicalHeightAdded;
454 void RenderTableSection::distributeExtraLogicalHeightToAutoRows(int& extraLogicalHeight, unsigned autoRowsCount)
459 int totalLogicalHeightAdded = 0;
460 for (unsigned r = 0; r < m_grid.size(); ++r) {
461 if (autoRowsCount > 0 && m_grid[r].logicalHeight.isAuto()) {
462 // Recomputing |extraLogicalHeightForRow| guarantees that we properly ditribute round |extraLogicalHeight|.
463 int extraLogicalHeightForRow = extraLogicalHeight / autoRowsCount;
464 totalLogicalHeightAdded += extraLogicalHeightForRow;
465 extraLogicalHeight -= extraLogicalHeightForRow;
468 m_rowPos[r + 1] += totalLogicalHeightAdded;
472 void RenderTableSection::distributeRemainingExtraLogicalHeight(int& extraLogicalHeight)
474 unsigned totalRows = m_grid.size();
476 if (extraLogicalHeight <= 0 || !m_rowPos[totalRows])
479 // FIXME: m_rowPos[totalRows] - m_rowPos[0] is the total rows' size.
480 int totalRowSize = m_rowPos[totalRows];
481 int totalLogicalHeightAdded = 0;
482 int previousRowPosition = m_rowPos[0];
483 for (unsigned r = 0; r < totalRows; r++) {
484 // weight with the original height
485 totalLogicalHeightAdded += extraLogicalHeight * (m_rowPos[r + 1] - previousRowPosition) / totalRowSize;
486 previousRowPosition = m_rowPos[r + 1];
487 m_rowPos[r + 1] += totalLogicalHeightAdded;
490 extraLogicalHeight -= totalLogicalHeightAdded;
493 int RenderTableSection::distributeExtraLogicalHeightToRows(int extraLogicalHeight)
495 if (!extraLogicalHeight)
496 return extraLogicalHeight;
498 unsigned totalRows = m_grid.size();
500 return extraLogicalHeight;
502 if (!m_rowPos[totalRows] && nextSibling())
503 return extraLogicalHeight;
505 unsigned autoRowsCount = 0;
506 int totalPercent = 0;
507 for (unsigned r = 0; r < totalRows; r++) {
508 if (m_grid[r].logicalHeight.isAuto())
510 else if (m_grid[r].logicalHeight.isPercentNotCalculated())
511 totalPercent += m_grid[r].logicalHeight.percent();
514 int remainingExtraLogicalHeight = extraLogicalHeight;
515 distributeExtraLogicalHeightToPercentRows(remainingExtraLogicalHeight, totalPercent);
516 distributeExtraLogicalHeightToAutoRows(remainingExtraLogicalHeight, autoRowsCount);
517 distributeRemainingExtraLogicalHeight(remainingExtraLogicalHeight);
518 return extraLogicalHeight - remainingExtraLogicalHeight;
521 void RenderTableSection::layoutRows()
524 SetLayoutNeededForbiddenScope layoutForbiddenScope(this);
527 ASSERT(!needsLayout());
529 unsigned totalRows = m_grid.size();
531 // Set the width of our section now. The rows will also be this width.
532 setLogicalWidth(table()->contentLogicalWidth());
533 m_forceSlowPaintPathWithOverflowingCell = false;
535 int vspacing = table()->vBorderSpacing();
536 unsigned nEffCols = table()->numEffCols();
538 LayoutStateMaintainer statePusher(view(), *this, locationOffset(), hasTransform() || style().isFlippedBlocksWritingMode());
540 for (unsigned r = 0; r < totalRows; r++) {
541 // Set the row's x/y position and width/height.
542 if (RenderTableRow* rowRenderer = m_grid[r].rowRenderer) {
543 // FIXME: the x() position of the row should be table()->hBorderSpacing() so that it can
544 // report the correct offsetLeft. However, that will require a lot of rebaselining of test results.
545 rowRenderer->setLocation(LayoutPoint(0, m_rowPos[r]));
546 rowRenderer->setLogicalWidth(logicalWidth());
547 rowRenderer->setLogicalHeight(m_rowPos[r + 1] - m_rowPos[r] - vspacing);
548 rowRenderer->updateLayerTransform();
551 int rowHeightIncreaseForPagination = 0;
553 for (unsigned c = 0; c < nEffCols; c++) {
554 CellStruct& cs = cellAt(r, c);
555 RenderTableCell* cell = cs.primaryCell();
557 if (!cell || cs.inColSpan)
560 int rowIndex = cell->rowIndex();
561 int rHeight = m_rowPos[rowIndex + cell->rowSpan()] - m_rowPos[rowIndex] - vspacing;
563 // Force percent height children to lay themselves out again.
564 // This will cause these children to grow to fill the cell.
565 // FIXME: There is still more work to do here to fully match WinIE (should
566 // it become necessary to do so). In quirks mode, WinIE behaves like we
567 // do, but it will clip the cells that spill out of the table section. In
568 // strict mode, Mozilla and WinIE both regrow the table to accommodate the
569 // new height of the cell (thus letting the percentages cause growth one
570 // time only). We may also not be handling row-spanning cells correctly.
572 // Note also the oddity where replaced elements always flex, and yet blocks/tables do
573 // not necessarily flex. WinIE is crazy and inconsistent, and we can't hope to
574 // match the behavior perfectly, but we'll continue to refine it as we discover new
576 bool cellChildrenFlex = false;
577 bool flexAllChildren = cell->style().logicalHeight().isFixed()
578 || (!table()->style().logicalHeight().isAuto() && rHeight != cell->logicalHeight());
580 for (RenderObject* o = cell->firstChild(); o; o = o->nextSibling()) {
581 if (!o->isText() && o->style().logicalHeight().isPercent() && (flexAllChildren || ((o->isReplaced() || (o->isBox() && toRenderBox(o)->scrollsOverflow())) && !o->isTextControl()))) {
582 // Tables with no sections do not flex.
583 if (!o->isTable() || toRenderTable(o)->hasSections()) {
584 o->setNeedsLayout(MarkOnlyThis);
585 cellChildrenFlex = true;
590 if (TrackedRendererListHashSet* percentHeightDescendants = cell->percentHeightDescendants()) {
591 TrackedRendererListHashSet::iterator end = percentHeightDescendants->end();
592 for (TrackedRendererListHashSet::iterator it = percentHeightDescendants->begin(); it != end; ++it) {
593 RenderBox* box = *it;
594 if (!box->isReplaced() && !box->scrollsOverflow() && !flexAllChildren)
597 while (box != cell) {
598 if (box->normalChildNeedsLayout())
600 box->setChildNeedsLayout(MarkOnlyThis);
601 box = box->containingBlock();
606 cellChildrenFlex = true;
610 if (cellChildrenFlex) {
611 cell->setChildNeedsLayout(MarkOnlyThis);
612 // Alignment within a cell is based off the calculated
613 // height, which becomes irrelevant once the cell has
614 // been resized based off its percentage.
615 cell->setOverrideLogicalContentHeightFromRowHeight(rHeight);
616 cell->layoutIfNeeded();
618 // If the baseline moved, we may have to update the data for our row. Find out the new baseline.
619 if (cell->isBaselineAligned()) {
620 LayoutUnit baseline = cell->cellBaselinePosition();
621 if (baseline > cell->borderAndPaddingBefore())
622 m_grid[r].baseline = std::max(m_grid[r].baseline, baseline);
626 cell->computeIntrinsicPadding(rHeight);
628 LayoutRect oldCellRect = cell->frameRect();
630 setLogicalPositionForCell(cell, c);
632 if (!cell->needsLayout() && view().layoutState()->pageLogicalHeight() && view().layoutState()->pageLogicalOffset(cell, cell->logicalTop()) != cell->pageLogicalOffset())
633 cell->setChildNeedsLayout(MarkOnlyThis);
635 cell->layoutIfNeeded();
637 // FIXME: Make pagination work with vertical tables.
638 if (view().layoutState()->pageLogicalHeight() && cell->logicalHeight() != rHeight) {
639 // FIXME: Pagination might have made us change size. For now just shrink or grow the cell to fit without doing a relayout.
640 // 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
641 // either. It's at least stable though and won't result in an infinite # of relayouts that may never stabilize.
642 if (cell->logicalHeight() > rHeight)
643 rowHeightIncreaseForPagination = std::max<int>(rowHeightIncreaseForPagination, cell->logicalHeight() - rHeight);
644 cell->setLogicalHeight(rHeight);
647 LayoutSize childOffset(cell->location() - oldCellRect.location());
648 if (childOffset.width() || childOffset.height()) {
649 view().addLayoutDelta(childOffset);
651 // If the child moved, we have to repaint it as well as any floating/positioned
652 // descendants. An exception is if we need a layout. In this case, we know we're going to
653 // repaint ourselves (and the child) anyway.
654 if (!table()->selfNeedsLayout() && cell->checkForRepaintDuringLayout())
655 cell->repaintDuringLayoutIfMoved(oldCellRect);
658 if (rowHeightIncreaseForPagination) {
659 for (unsigned rowIndex = r + 1; rowIndex <= totalRows; rowIndex++)
660 m_rowPos[rowIndex] += rowHeightIncreaseForPagination;
661 for (unsigned c = 0; c < nEffCols; ++c) {
662 Vector<RenderTableCell*, 1>& cells = cellAt(r, c).cells;
663 for (size_t i = 0; i < cells.size(); ++i)
664 cells[i]->setLogicalHeight(cells[i]->logicalHeight() + rowHeightIncreaseForPagination);
669 ASSERT(!needsLayout());
671 setLogicalHeight(m_rowPos[totalRows]);
673 computeOverflowFromCells(totalRows, nEffCols);
678 void RenderTableSection::computeOverflowFromCells()
680 unsigned totalRows = m_grid.size();
681 unsigned nEffCols = table()->numEffCols();
682 computeOverflowFromCells(totalRows, nEffCols);
685 void RenderTableSection::computeOverflowFromCells(unsigned totalRows, unsigned nEffCols)
688 m_overflowingCells.clear();
689 unsigned totalCellsCount = nEffCols * totalRows;
690 int maxAllowedOverflowingCellsCount = totalCellsCount < gMinTableSizeToUseFastPaintPathWithOverflowingCell ? 0 : gMaxAllowedOverflowingCellRatioForFastPaintPath * totalCellsCount;
693 bool hasOverflowingCell = false;
695 // Now that our height has been determined, add in overflow from cells.
696 for (unsigned r = 0; r < totalRows; r++) {
697 for (unsigned c = 0; c < nEffCols; c++) {
698 CellStruct& cs = cellAt(r, c);
699 RenderTableCell* cell = cs.primaryCell();
700 if (!cell || cs.inColSpan)
702 if (r < totalRows - 1 && cell == primaryCellAt(r + 1, c))
704 addOverflowFromChild(cell);
706 hasOverflowingCell |= cell->hasVisualOverflow();
708 if (cell->hasVisualOverflow() && !m_forceSlowPaintPathWithOverflowingCell) {
709 m_overflowingCells.add(cell);
710 if (m_overflowingCells.size() > maxAllowedOverflowingCellsCount) {
711 // We need to set m_forcesSlowPaintPath only if there is a least one overflowing cells as the hit testing code rely on this information.
712 m_forceSlowPaintPathWithOverflowingCell = true;
713 // The slow path does not make any use of the overflowing cells info, don't hold on to the memory.
714 m_overflowingCells.clear();
720 ASSERT(hasOverflowingCell == this->hasOverflowingCell());
723 int RenderTableSection::calcOuterBorderBefore() const
725 unsigned totalCols = table()->numEffCols();
726 if (!m_grid.size() || !totalCols)
729 unsigned borderWidth = 0;
731 const BorderValue& sb = style().borderBefore();
732 if (sb.style() == BHIDDEN)
734 if (sb.style() > BHIDDEN)
735 borderWidth = sb.width();
737 const BorderValue& rb = firstRow()->style().borderBefore();
738 if (rb.style() == BHIDDEN)
740 if (rb.style() > BHIDDEN && rb.width() > borderWidth)
741 borderWidth = rb.width();
743 bool allHidden = true;
744 for (unsigned c = 0; c < totalCols; c++) {
745 const CellStruct& current = cellAt(0, c);
746 if (current.inColSpan || !current.hasCells())
748 const BorderValue& cb = current.primaryCell()->style().borderBefore(); // FIXME: Make this work with perpendicular and flipped cells.
749 // FIXME: Don't repeat for the same col group
750 RenderTableCol* colGroup = table()->colElement(c);
752 const BorderValue& gb = colGroup->style().borderBefore();
753 if (gb.style() == BHIDDEN || cb.style() == BHIDDEN)
756 if (gb.style() > BHIDDEN && gb.width() > borderWidth)
757 borderWidth = gb.width();
758 if (cb.style() > BHIDDEN && cb.width() > borderWidth)
759 borderWidth = cb.width();
761 if (cb.style() == BHIDDEN)
764 if (cb.style() > BHIDDEN && cb.width() > borderWidth)
765 borderWidth = cb.width();
771 return borderWidth / 2;
774 int RenderTableSection::calcOuterBorderAfter() const
776 unsigned totalCols = table()->numEffCols();
777 if (!m_grid.size() || !totalCols)
780 unsigned borderWidth = 0;
782 const BorderValue& sb = style().borderAfter();
783 if (sb.style() == BHIDDEN)
785 if (sb.style() > BHIDDEN)
786 borderWidth = sb.width();
788 const BorderValue& rb = lastRow()->style().borderAfter();
789 if (rb.style() == BHIDDEN)
791 if (rb.style() > BHIDDEN && rb.width() > borderWidth)
792 borderWidth = rb.width();
794 bool allHidden = true;
795 for (unsigned c = 0; c < totalCols; c++) {
796 const CellStruct& current = cellAt(m_grid.size() - 1, c);
797 if (current.inColSpan || !current.hasCells())
799 const BorderValue& cb = current.primaryCell()->style().borderAfter(); // FIXME: Make this work with perpendicular and flipped cells.
800 // FIXME: Don't repeat for the same col group
801 RenderTableCol* colGroup = table()->colElement(c);
803 const BorderValue& gb = colGroup->style().borderAfter();
804 if (gb.style() == BHIDDEN || cb.style() == BHIDDEN)
807 if (gb.style() > BHIDDEN && gb.width() > borderWidth)
808 borderWidth = gb.width();
809 if (cb.style() > BHIDDEN && cb.width() > borderWidth)
810 borderWidth = cb.width();
812 if (cb.style() == BHIDDEN)
815 if (cb.style() > BHIDDEN && cb.width() > borderWidth)
816 borderWidth = cb.width();
822 return (borderWidth + 1) / 2;
825 int RenderTableSection::calcOuterBorderStart() const
827 unsigned totalCols = table()->numEffCols();
828 if (!m_grid.size() || !totalCols)
831 unsigned borderWidth = 0;
833 const BorderValue& sb = style().borderStart();
834 if (sb.style() == BHIDDEN)
836 if (sb.style() > BHIDDEN)
837 borderWidth = sb.width();
839 if (RenderTableCol* colGroup = table()->colElement(0)) {
840 const BorderValue& gb = colGroup->style().borderStart();
841 if (gb.style() == BHIDDEN)
843 if (gb.style() > BHIDDEN && gb.width() > borderWidth)
844 borderWidth = gb.width();
847 bool allHidden = true;
848 for (unsigned r = 0; r < m_grid.size(); r++) {
849 const CellStruct& current = cellAt(r, 0);
850 if (!current.hasCells())
852 // FIXME: Don't repeat for the same cell
853 const BorderValue& cb = current.primaryCell()->style().borderStart(); // FIXME: Make this work with perpendicular and flipped cells.
854 const BorderValue& rb = current.primaryCell()->parent()->style().borderStart();
855 if (cb.style() == BHIDDEN || rb.style() == BHIDDEN)
858 if (cb.style() > BHIDDEN && cb.width() > borderWidth)
859 borderWidth = cb.width();
860 if (rb.style() > BHIDDEN && rb.width() > borderWidth)
861 borderWidth = rb.width();
866 return (borderWidth + (table()->style().isLeftToRightDirection() ? 0 : 1)) / 2;
869 int RenderTableSection::calcOuterBorderEnd() const
871 unsigned totalCols = table()->numEffCols();
872 if (!m_grid.size() || !totalCols)
875 unsigned borderWidth = 0;
877 const BorderValue& sb = style().borderEnd();
878 if (sb.style() == BHIDDEN)
880 if (sb.style() > BHIDDEN)
881 borderWidth = sb.width();
883 if (RenderTableCol* colGroup = table()->colElement(totalCols - 1)) {
884 const BorderValue& gb = colGroup->style().borderEnd();
885 if (gb.style() == BHIDDEN)
887 if (gb.style() > BHIDDEN && gb.width() > borderWidth)
888 borderWidth = gb.width();
891 bool allHidden = true;
892 for (unsigned r = 0; r < m_grid.size(); r++) {
893 const CellStruct& current = cellAt(r, totalCols - 1);
894 if (!current.hasCells())
896 // FIXME: Don't repeat for the same cell
897 const BorderValue& cb = current.primaryCell()->style().borderEnd(); // FIXME: Make this work with perpendicular and flipped cells.
898 const BorderValue& rb = current.primaryCell()->parent()->style().borderEnd();
899 if (cb.style() == BHIDDEN || rb.style() == BHIDDEN)
902 if (cb.style() > BHIDDEN && cb.width() > borderWidth)
903 borderWidth = cb.width();
904 if (rb.style() > BHIDDEN && rb.width() > borderWidth)
905 borderWidth = rb.width();
910 return (borderWidth + (table()->style().isLeftToRightDirection() ? 1 : 0)) / 2;
913 void RenderTableSection::recalcOuterBorder()
915 m_outerBorderBefore = calcOuterBorderBefore();
916 m_outerBorderAfter = calcOuterBorderAfter();
917 m_outerBorderStart = calcOuterBorderStart();
918 m_outerBorderEnd = calcOuterBorderEnd();
921 int RenderTableSection::firstLineBaseline() const
926 int firstLineBaseline = m_grid[0].baseline;
927 if (firstLineBaseline)
928 return firstLineBaseline + m_rowPos[0];
930 firstLineBaseline = -1;
931 const Row& firstRow = m_grid[0].row;
932 for (size_t i = 0; i < firstRow.size(); ++i) {
933 const CellStruct& cs = firstRow.at(i);
934 const RenderTableCell* cell = cs.primaryCell();
935 // Only cells with content have a baseline
936 if (cell && cell->contentLogicalHeight())
937 firstLineBaseline = std::max<int>(firstLineBaseline, cell->logicalTop() + cell->borderAndPaddingBefore() + cell->contentLogicalHeight());
940 return firstLineBaseline;
943 void RenderTableSection::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
945 // put this back in when all layout tests can handle it
946 // ASSERT(!needsLayout());
947 // avoid crashing on bugs that cause us to paint with dirty layout
951 unsigned totalRows = m_grid.size();
952 unsigned totalCols = table()->columns().size();
954 if (!totalRows || !totalCols)
957 LayoutPoint adjustedPaintOffset = paintOffset + location();
959 PaintPhase phase = paintInfo.phase;
960 bool pushedClip = pushContentsClip(paintInfo, adjustedPaintOffset);
961 paintObject(paintInfo, adjustedPaintOffset);
963 popContentsClip(paintInfo, phase, adjustedPaintOffset);
965 if ((phase == PaintPhaseOutline || phase == PaintPhaseSelfOutline) && style().visibility() == VISIBLE)
966 paintOutline(paintInfo, LayoutRect(adjustedPaintOffset, size()));
969 static inline bool compareCellPositions(RenderTableCell* elem1, RenderTableCell* elem2)
971 return elem1->rowIndex() < elem2->rowIndex();
974 // This comparison is used only when we have overflowing cells as we have an unsorted array to sort. We thus need
975 // to sort both on rows and columns to properly repaint.
976 static inline bool compareCellPositionsWithOverflowingCells(RenderTableCell* elem1, RenderTableCell* elem2)
978 if (elem1->rowIndex() != elem2->rowIndex())
979 return elem1->rowIndex() < elem2->rowIndex();
981 return elem1->col() < elem2->col();
984 void RenderTableSection::paintCell(RenderTableCell* cell, PaintInfo& paintInfo, const LayoutPoint& paintOffset)
986 LayoutPoint cellPoint = flipForWritingModeForChild(cell, paintOffset);
987 PaintPhase paintPhase = paintInfo.phase;
988 RenderTableRow* row = toRenderTableRow(cell->parent());
990 if (paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChildBlockBackground) {
991 // We need to handle painting a stack of backgrounds. This stack (from bottom to top) consists of
992 // the column group, column, row group, row, and then the cell.
993 RenderTableCol* column = table()->colElement(cell->col());
994 RenderTableCol* columnGroup = column ? column->enclosingColumnGroup() : 0;
996 // Column groups and columns first.
997 // FIXME: Columns and column groups do not currently support opacity, and they are being painted "too late" in
998 // the stack, since we have already opened a transparency layer (potentially) for the table row group.
999 // Note that we deliberately ignore whether or not the cell has a layer, since these backgrounds paint "behind" the
1001 cell->paintBackgroundsBehindCell(paintInfo, cellPoint, columnGroup);
1002 cell->paintBackgroundsBehindCell(paintInfo, cellPoint, column);
1004 // Paint the row group next.
1005 cell->paintBackgroundsBehindCell(paintInfo, cellPoint, this);
1007 // Paint the row next, but only if it doesn't have a layer. If a row has a layer, it will be responsible for
1008 // painting the row background for the cell.
1009 if (!row->hasSelfPaintingLayer())
1010 cell->paintBackgroundsBehindCell(paintInfo, cellPoint, row);
1012 if ((!cell->hasSelfPaintingLayer() && !row->hasSelfPaintingLayer()))
1013 cell->paint(paintInfo, cellPoint);
1016 LayoutRect RenderTableSection::logicalRectForWritingModeAndDirection(const LayoutRect& rect) const
1018 LayoutRect tableAlignedRect(rect);
1020 flipForWritingMode(tableAlignedRect);
1022 if (!style().isHorizontalWritingMode())
1023 tableAlignedRect = tableAlignedRect.transposedRect();
1025 const Vector<int>& columnPos = table()->columnPositions();
1026 // FIXME: The table's direction should determine our row's direction, not the section's (see bug 96691).
1027 if (!style().isLeftToRightDirection())
1028 tableAlignedRect.setX(columnPos[columnPos.size() - 1] - tableAlignedRect.maxX());
1030 return tableAlignedRect;
1033 CellSpan RenderTableSection::dirtiedRows(const LayoutRect& damageRect) const
1035 if (m_forceSlowPaintPathWithOverflowingCell)
1036 return fullTableRowSpan();
1038 CellSpan coveredRows = spannedRows(damageRect, IncludeAllIntersectingCells);
1040 // To repaint the border we might need to repaint first or last row even if they are not spanned themselves.
1041 if (coveredRows.start() >= m_rowPos.size() - 1 && m_rowPos[m_rowPos.size() - 1] + table()->outerBorderAfter() >= damageRect.y())
1042 --coveredRows.start();
1044 if (!coveredRows.end() && m_rowPos[0] - table()->outerBorderBefore() <= damageRect.maxY())
1045 ++coveredRows.end();
1050 CellSpan RenderTableSection::dirtiedColumns(const LayoutRect& damageRect) const
1052 if (m_forceSlowPaintPathWithOverflowingCell)
1053 return fullTableColumnSpan();
1055 CellSpan coveredColumns = spannedColumns(damageRect, IncludeAllIntersectingCells);
1057 const Vector<int>& columnPos = table()->columnPositions();
1058 // To repaint the border we might need to repaint first or last column even if they are not spanned themselves.
1059 if (coveredColumns.start() >= columnPos.size() - 1 && columnPos[columnPos.size() - 1] + table()->outerBorderEnd() >= damageRect.x())
1060 --coveredColumns.start();
1062 if (!coveredColumns.end() && columnPos[0] - table()->outerBorderStart() <= damageRect.maxX())
1063 ++coveredColumns.end();
1065 return coveredColumns;
1068 CellSpan RenderTableSection::spannedRows(const LayoutRect& flippedRect, ShouldIncludeAllIntersectingCells shouldIncludeAllIntersectionCells) const
1070 // Find the first row that starts after rect top.
1071 unsigned nextRow = std::upper_bound(m_rowPos.begin(), m_rowPos.end(), flippedRect.y()) - m_rowPos.begin();
1072 if (shouldIncludeAllIntersectionCells == IncludeAllIntersectingCells && nextRow && m_rowPos[nextRow - 1] == flippedRect.y())
1075 if (nextRow == m_rowPos.size())
1076 return CellSpan(m_rowPos.size() - 1, m_rowPos.size() - 1); // After all rows.
1078 unsigned startRow = nextRow > 0 ? nextRow - 1 : 0;
1080 // Find the first row that starts after rect bottom.
1082 if (m_rowPos[nextRow] >= flippedRect.maxY())
1085 endRow = std::upper_bound(m_rowPos.begin() + nextRow, m_rowPos.end(), flippedRect.maxY()) - m_rowPos.begin();
1086 if (endRow == m_rowPos.size())
1087 endRow = m_rowPos.size() - 1;
1090 return CellSpan(startRow, endRow);
1093 CellSpan RenderTableSection::spannedColumns(const LayoutRect& flippedRect, ShouldIncludeAllIntersectingCells shouldIncludeAllIntersectionCells) const
1095 const Vector<int>& columnPos = table()->columnPositions();
1097 // Find the first column that starts after rect left.
1098 // lower_bound doesn't handle the edge between two cells properly as it would wrongly return the
1099 // cell on the logical top/left.
1100 // upper_bound on the other hand properly returns the cell on the logical bottom/right, which also
1101 // matches the behavior of other browsers.
1102 unsigned nextColumn = std::upper_bound(columnPos.begin(), columnPos.end(), flippedRect.x()) - columnPos.begin();
1103 if (shouldIncludeAllIntersectionCells == IncludeAllIntersectingCells && nextColumn && columnPos[nextColumn - 1] == flippedRect.x())
1106 if (nextColumn == columnPos.size())
1107 return CellSpan(columnPos.size() - 1, columnPos.size() - 1); // After all columns.
1109 unsigned startColumn = nextColumn > 0 ? nextColumn - 1 : 0;
1111 // Find the first column that starts after rect right.
1113 if (columnPos[nextColumn] >= flippedRect.maxX())
1114 endColumn = nextColumn;
1116 endColumn = std::upper_bound(columnPos.begin() + nextColumn, columnPos.end(), flippedRect.maxX()) - columnPos.begin();
1117 if (endColumn == columnPos.size())
1118 endColumn = columnPos.size() - 1;
1121 return CellSpan(startColumn, endColumn);
1124 void RenderTableSection::paintRowGroupBorder(const PaintInfo& paintInfo, bool antialias, LayoutRect rect, BoxSide side, CSSPropertyID borderColor, EBorderStyle borderStyle, EBorderStyle tableBorderStyle)
1126 if (tableBorderStyle == BHIDDEN)
1128 rect.intersect(paintInfo.rect);
1131 drawLineForBoxSide(paintInfo.context, rect.x(), rect.y(), rect.x() + rect.width(), rect.y() + rect.height(), side, style().visitedDependentColor(borderColor), borderStyle, 0, 0, antialias);
1134 int RenderTableSection::offsetLeftForRowGroupBorder(RenderTableCell* cell, const LayoutRect& rowGroupRect, unsigned row)
1136 if (style().isHorizontalWritingMode()) {
1137 if (style().isLeftToRightDirection())
1138 return cell ? cell->x().toInt() + cell->width().toInt() : 0;
1139 return -outerBorderLeft(&style());
1141 bool isLastRow = row + 1 == m_grid.size();
1142 return rowGroupRect.width().toInt() - m_rowPos[row + 1] + (isLastRow ? -outerBorderLeft(&style()) : 0);
1145 int RenderTableSection::offsetTopForRowGroupBorder(RenderTableCell* cell, BoxSide borderSide, unsigned row)
1147 bool isLastRow = row + 1 == m_grid.size();
1148 if (style().isHorizontalWritingMode())
1149 return m_rowPos[row] + (!row && borderSide == BSRight ? -outerBorderTop(&style()) : isLastRow && borderSide == BSLeft ? outerBorderTop(&style()) : 0);
1150 if (style().isLeftToRightDirection())
1151 return (cell ? cell->y().toInt() + cell->height().toInt() : 0) + (borderSide == BSLeft ? outerBorderTop(&style()) : 0);
1152 return borderSide == BSRight ? -outerBorderTop(&style()) : 0;
1155 int RenderTableSection::verticalRowGroupBorderHeight(RenderTableCell* cell, const LayoutRect& rowGroupRect, unsigned row)
1157 bool isLastRow = row + 1 == m_grid.size();
1158 if (style().isHorizontalWritingMode())
1159 return m_rowPos[row + 1] - m_rowPos[row] + (!row ? outerBorderTop(&style()) : isLastRow ? outerBorderBottom(&style()) : 0);
1160 if (style().isLeftToRightDirection())
1161 return rowGroupRect.height().toInt() - (cell ? cell->y().toInt() + cell->height().toInt() : 0) + outerBorderBottom(&style());
1162 return cell ? rowGroupRect.height().toInt() - (cell->y().toInt() - cell->height().toInt()) : 0;
1165 int RenderTableSection::horizontalRowGroupBorderWidth(RenderTableCell* cell, const LayoutRect& rowGroupRect, unsigned row, unsigned column)
1167 if (style().isHorizontalWritingMode()) {
1168 if (style().isLeftToRightDirection())
1169 return rowGroupRect.width().toInt() - (cell ? cell->x().toInt() + cell->width().toInt() : 0) + (!column ? outerBorderLeft(&style()) : column == table()->numEffCols() ? outerBorderRight(&style()) : 0);
1170 return cell ? rowGroupRect.width().toInt() - (cell->x().toInt() - cell->width().toInt()) : 0;
1172 bool isLastRow = row + 1 == m_grid.size();
1173 return m_rowPos[row + 1] - m_rowPos[row] + (isLastRow ? outerBorderLeft(&style()) : !row ? outerBorderRight(&style()) : 0);
1176 void RenderTableSection::paintRowGroupBorderIfRequired(const PaintInfo& paintInfo, const LayoutPoint& paintOffset, unsigned row, unsigned column, BoxSide borderSide, RenderTableCell* cell)
1178 if (table()->currentBorderValue()->precedence() > BROWGROUP)
1180 if (paintInfo.context->paintingDisabled())
1183 const RenderStyle& style = this->style();
1184 bool antialias = shouldAntialiasLines(paintInfo.context);
1185 LayoutRect rowGroupRect = LayoutRect(paintOffset, size());
1186 rowGroupRect.moveBy(-LayoutPoint(outerBorderLeft(&style), (borderSide == BSRight) ? 0 : outerBorderTop(&style)));
1188 switch (borderSide) {
1190 paintRowGroupBorder(paintInfo, antialias, LayoutRect(paintOffset.x() + offsetLeftForRowGroupBorder(cell, rowGroupRect, row), rowGroupRect.y(),
1191 horizontalRowGroupBorderWidth(cell, rowGroupRect, row, column), style.borderTop().width()), BSTop, CSSPropertyBorderTopColor, style.borderTopStyle(), table()->style().borderTopStyle());
1194 paintRowGroupBorder(paintInfo, antialias, LayoutRect(paintOffset.x() + offsetLeftForRowGroupBorder(cell, rowGroupRect, row), rowGroupRect.y() + rowGroupRect.height(),
1195 horizontalRowGroupBorderWidth(cell, rowGroupRect, row, column), style.borderBottom().width()), BSBottom, CSSPropertyBorderBottomColor, style.borderBottomStyle(), table()->style().borderBottomStyle());
1198 paintRowGroupBorder(paintInfo, antialias, LayoutRect(rowGroupRect.x(), rowGroupRect.y() + offsetTopForRowGroupBorder(cell, borderSide, row), style.borderLeft().width(),
1199 verticalRowGroupBorderHeight(cell, rowGroupRect, row)), BSLeft, CSSPropertyBorderLeftColor, style.borderLeftStyle(), table()->style().borderLeftStyle());
1202 paintRowGroupBorder(paintInfo, antialias, LayoutRect(rowGroupRect.x() + rowGroupRect.width(), rowGroupRect.y() + offsetTopForRowGroupBorder(cell, borderSide, row), style.borderRight().width(),
1203 verticalRowGroupBorderHeight(cell, rowGroupRect, row)), BSRight, CSSPropertyBorderRightColor, style.borderRightStyle(), table()->style().borderRightStyle());
1211 static BoxSide physicalBorderForDirection(RenderStyle* styleForCellFlow, CollapsedBorderSide side)
1216 if (styleForCellFlow->isHorizontalWritingMode())
1217 return styleForCellFlow->isLeftToRightDirection() ? BSLeft : BSRight;
1218 return styleForCellFlow->isLeftToRightDirection() ? BSTop : BSBottom;
1220 if (styleForCellFlow->isHorizontalWritingMode())
1221 return styleForCellFlow->isLeftToRightDirection() ? BSRight : BSLeft;
1222 return styleForCellFlow->isLeftToRightDirection() ? BSBottom : BSTop;
1224 if (styleForCellFlow->isHorizontalWritingMode())
1226 return styleForCellFlow->isLeftToRightDirection() ? BSRight : BSLeft;
1228 if (styleForCellFlow->isHorizontalWritingMode())
1230 return styleForCellFlow->isLeftToRightDirection() ? BSLeft : BSRight;
1232 ASSERT_NOT_REACHED();
1237 void RenderTableSection::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1239 PaintPhase paintPhase = paintInfo.phase;
1241 LayoutRect localRepaintRect = paintInfo.rect;
1242 localRepaintRect.moveBy(-paintOffset);
1243 localRepaintRect.inflate(maximalOutlineSize(paintPhase));
1245 LayoutRect tableAlignedRect = logicalRectForWritingModeAndDirection(localRepaintRect);
1247 CellSpan dirtiedRows = this->dirtiedRows(tableAlignedRect);
1248 CellSpan dirtiedColumns = this->dirtiedColumns(tableAlignedRect);
1250 if (dirtiedColumns.start() < dirtiedColumns.end()) {
1251 if (!m_hasMultipleCellLevels && !m_overflowingCells.size()) {
1252 if (paintInfo.phase == PaintPhaseCollapsedTableBorders) {
1253 // Collapsed borders are painted from the bottom right to the top left so that precedence
1254 // due to cell position is respected. We need to paint one row beyond the topmost dirtied
1255 // row to calculate its collapsed border value.
1256 unsigned startRow = dirtiedRows.start() ? dirtiedRows.start() - 1 : 0;
1257 for (unsigned r = dirtiedRows.end(); r > startRow; r--) {
1258 unsigned row = r - 1;
1259 bool shouldPaintRowGroupBorder = false;
1260 for (unsigned c = dirtiedColumns.end(); c > dirtiedColumns.start(); c--) {
1261 unsigned col = c - 1;
1262 CellStruct& current = cellAt(row, col);
1263 RenderTableCell* cell = current.primaryCell();
1266 paintRowGroupBorderIfRequired(paintInfo, paintOffset, row, col, physicalBorderForDirection(&style(), CBSStart));
1267 else if (c == table()->numEffCols())
1268 paintRowGroupBorderIfRequired(paintInfo, paintOffset, row, col, physicalBorderForDirection(&style(), CBSEnd));
1269 shouldPaintRowGroupBorder = true;
1272 if ((row > dirtiedRows.start() && primaryCellAt(row - 1, col) == cell) || (col > dirtiedColumns.start() && primaryCellAt(row, col - 1) == cell))
1275 // If we had a run of null cells paint their corresponding section of the row group's border if necessary. Note that
1276 // this will only happen once within a row as the null cells will always be clustered together on one end of the row.
1277 if (shouldPaintRowGroupBorder) {
1278 if (r == m_grid.size())
1279 paintRowGroupBorderIfRequired(paintInfo, paintOffset, row, col, physicalBorderForDirection(&style(), CBSAfter), cell);
1280 else if (!row && !table()->sectionAbove(this))
1281 paintRowGroupBorderIfRequired(paintInfo, paintOffset, row, col, physicalBorderForDirection(&style(), CBSBefore), cell);
1282 shouldPaintRowGroupBorder = false;
1285 LayoutPoint cellPoint = flipForWritingModeForChild(cell, paintOffset);
1286 cell->paintCollapsedBorders(paintInfo, cellPoint);
1290 // Draw the dirty cells in the order that they appear.
1291 for (unsigned r = dirtiedRows.start(); r < dirtiedRows.end(); r++) {
1292 RenderTableRow* row = m_grid[r].rowRenderer;
1293 if (row && !row->hasSelfPaintingLayer())
1294 row->paintOutlineForRowIfNeeded(paintInfo, paintOffset);
1295 for (unsigned c = dirtiedColumns.start(); c < dirtiedColumns.end(); c++) {
1296 CellStruct& current = cellAt(r, c);
1297 RenderTableCell* cell = current.primaryCell();
1298 if (!cell || (r > dirtiedRows.start() && primaryCellAt(r - 1, c) == cell) || (c > dirtiedColumns.start() && primaryCellAt(r, c - 1) == cell))
1300 paintCell(cell, paintInfo, paintOffset);
1305 // The overflowing cells should be scarce to avoid adding a lot of cells to the HashSet.
1307 unsigned totalRows = m_grid.size();
1308 unsigned totalCols = table()->columns().size();
1309 ASSERT(m_overflowingCells.size() < totalRows * totalCols * gMaxAllowedOverflowingCellRatioForFastPaintPath);
1312 // To make sure we properly repaint the section, we repaint all the overflowing cells that we collected.
1313 Vector<RenderTableCell*> cells;
1314 copyToVector(m_overflowingCells, cells);
1316 HashSet<RenderTableCell*> spanningCells;
1318 for (unsigned r = dirtiedRows.start(); r < dirtiedRows.end(); r++) {
1319 RenderTableRow* row = m_grid[r].rowRenderer;
1320 if (row && !row->hasSelfPaintingLayer())
1321 row->paintOutlineForRowIfNeeded(paintInfo, paintOffset);
1322 for (unsigned c = dirtiedColumns.start(); c < dirtiedColumns.end(); c++) {
1323 CellStruct& current = cellAt(r, c);
1324 if (!current.hasCells())
1326 for (unsigned i = 0; i < current.cells.size(); ++i) {
1327 if (m_overflowingCells.contains(current.cells[i]))
1330 if (current.cells[i]->rowSpan() > 1 || current.cells[i]->colSpan() > 1) {
1331 if (!spanningCells.add(current.cells[i]).isNewEntry)
1335 cells.append(current.cells[i]);
1340 // Sort the dirty cells by paint order.
1341 if (!m_overflowingCells.size())
1342 std::stable_sort(cells.begin(), cells.end(), compareCellPositions);
1344 std::sort(cells.begin(), cells.end(), compareCellPositionsWithOverflowingCells);
1346 if (paintInfo.phase == PaintPhaseCollapsedTableBorders) {
1347 for (unsigned i = cells.size(); i > 0; --i) {
1348 LayoutPoint cellPoint = flipForWritingModeForChild(cells[i - 1], paintOffset);
1349 cells[i - 1]->paintCollapsedBorders(paintInfo, cellPoint);
1352 for (unsigned i = 0; i < cells.size(); ++i)
1353 paintCell(cells[i], paintInfo, paintOffset);
1359 void RenderTableSection::imageChanged(WrappedImagePtr, const IntRect*)
1361 // FIXME: Examine cells and repaint only the rect the image paints in.
1365 void RenderTableSection::recalcCells()
1367 ASSERT(m_needsCellRecalc);
1368 // We reset the flag here to ensure that addCell() works. This is safe to do because we clear the grid
1369 // and update its dimensions to be consistent with the table's column representation before we rebuild
1370 // the grid using addCell().
1371 m_needsCellRecalc = false;
1377 for (RenderTableRow* row = firstRow(); row; row = row->nextRow()) {
1378 unsigned insertionRow = m_cRow;
1383 m_grid[insertionRow].rowRenderer = row;
1384 row->setRowIndex(insertionRow);
1385 setRowLogicalHeightToRowStyleLogicalHeightIfNotRelative(m_grid[insertionRow]);
1387 for (RenderTableCell* cell = row->firstCell(); cell; cell = cell->nextCell())
1391 m_grid.shrinkToFit();
1395 // FIXME: This function could be made O(1) in certain cases (like for the non-most-constrainive cells' case).
1396 void RenderTableSection::rowLogicalHeightChanged(unsigned rowIndex)
1398 if (needsCellRecalc())
1401 setRowLogicalHeightToRowStyleLogicalHeightIfNotRelative(m_grid[rowIndex]);
1403 for (RenderTableCell* cell = m_grid[rowIndex].rowRenderer->firstCell(); cell; cell = cell->nextCell())
1404 updateLogicalHeightForCell(m_grid[rowIndex], cell);
1407 void RenderTableSection::setNeedsCellRecalc()
1409 m_needsCellRecalc = true;
1411 // Clear the grid now to ensure that we don't hold onto any stale pointers (e.g. a cell renderer that is being removed).
1414 if (RenderTable* t = table())
1415 t->setNeedsSectionRecalc();
1418 unsigned RenderTableSection::numColumns() const
1420 ASSERT(!m_needsCellRecalc);
1421 unsigned result = 0;
1423 for (unsigned r = 0; r < m_grid.size(); ++r) {
1424 for (unsigned c = result; c < table()->numEffCols(); ++c) {
1425 const CellStruct& cell = cellAt(r, c);
1426 if (cell.hasCells() || cell.inColSpan)
1434 const BorderValue& RenderTableSection::borderAdjoiningStartCell(const RenderTableCell* cell) const
1436 ASSERT(cell->isFirstOrLastCellInRow());
1437 return hasSameDirectionAs(cell) ? style().borderStart() : style().borderEnd();
1440 const BorderValue& RenderTableSection::borderAdjoiningEndCell(const RenderTableCell* cell) const
1442 ASSERT(cell->isFirstOrLastCellInRow());
1443 return hasSameDirectionAs(cell) ? style().borderEnd() : style().borderStart();
1446 const RenderTableCell* RenderTableSection::firstRowCellAdjoiningTableStart() const
1448 unsigned adjoiningStartCellColumnIndex = hasSameDirectionAs(table()) ? 0 : table()->lastColumnIndex();
1449 return cellAt(0, adjoiningStartCellColumnIndex).primaryCell();
1452 const RenderTableCell* RenderTableSection::firstRowCellAdjoiningTableEnd() const
1454 unsigned adjoiningEndCellColumnIndex = hasSameDirectionAs(table()) ? table()->lastColumnIndex() : 0;
1455 return cellAt(0, adjoiningEndCellColumnIndex).primaryCell();
1458 void RenderTableSection::appendColumn(unsigned pos)
1460 ASSERT(!m_needsCellRecalc);
1462 for (unsigned row = 0; row < m_grid.size(); ++row)
1463 m_grid[row].row.resize(pos + 1);
1466 void RenderTableSection::splitColumn(unsigned pos, unsigned first)
1468 ASSERT(!m_needsCellRecalc);
1472 for (unsigned row = 0; row < m_grid.size(); ++row) {
1473 Row& r = m_grid[row].row;
1474 r.insert(pos + 1, CellStruct());
1475 if (r[pos].hasCells()) {
1476 r[pos + 1].cells.appendVector(r[pos].cells);
1477 RenderTableCell* cell = r[pos].primaryCell();
1479 ASSERT(cell->colSpan() >= (r[pos].inColSpan ? 1u : 0));
1480 unsigned colleft = cell->colSpan() - r[pos].inColSpan;
1481 if (first > colleft)
1482 r[pos + 1].inColSpan = 0;
1484 r[pos + 1].inColSpan = first + r[pos].inColSpan;
1486 r[pos + 1].inColSpan = 0;
1492 bool RenderTableSection::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction action)
1494 // If we have no children then we have nothing to do.
1498 // Table sections cannot ever be hit tested. Effectively they do not exist.
1499 // Just forward to our children always.
1500 LayoutPoint adjustedLocation = accumulatedOffset + location();
1502 if (hasOverflowClip() && !locationInContainer.intersects(overflowClipRect(adjustedLocation, currentRenderNamedFlowFragment())))
1505 if (hasOverflowingCell()) {
1506 for (RenderTableRow* row = lastRow(); row; row = row->previousRow()) {
1507 // FIXME: We have to skip over inline flows, since they can show up inside table rows
1508 // at the moment (a demoted inline <form> for example). If we ever implement a
1509 // table-specific hit-test method (which we should do for performance reasons anyway),
1510 // then we can remove this check.
1511 if (!row->hasSelfPaintingLayer()) {
1512 LayoutPoint childPoint = flipForWritingModeForChild(row, adjustedLocation);
1513 if (row->nodeAtPoint(request, result, locationInContainer, childPoint, action)) {
1514 updateHitTestResult(result, toLayoutPoint(locationInContainer.point() - childPoint));
1522 recalcCellsIfNeeded();
1524 LayoutRect hitTestRect = locationInContainer.boundingBox();
1525 hitTestRect.moveBy(-adjustedLocation);
1527 LayoutRect tableAlignedRect = logicalRectForWritingModeAndDirection(hitTestRect);
1528 CellSpan rowSpan = spannedRows(tableAlignedRect, DoNotIncludeAllIntersectingCells);
1529 CellSpan columnSpan = spannedColumns(tableAlignedRect, DoNotIncludeAllIntersectingCells);
1531 // Now iterate over the spanned rows and columns.
1532 for (unsigned hitRow = rowSpan.start(); hitRow < rowSpan.end(); ++hitRow) {
1533 for (unsigned hitColumn = columnSpan.start(); hitColumn < columnSpan.end(); ++hitColumn) {
1534 CellStruct& current = cellAt(hitRow, hitColumn);
1536 // If the cell is empty, there's nothing to do
1537 if (!current.hasCells())
1540 for (unsigned i = current.cells.size() ; i; ) {
1542 RenderTableCell* cell = current.cells[i];
1543 LayoutPoint cellPoint = flipForWritingModeForChild(cell, adjustedLocation);
1544 if (static_cast<RenderObject*>(cell)->nodeAtPoint(request, result, locationInContainer, cellPoint, action)) {
1545 updateHitTestResult(result, locationInContainer.point() - toLayoutSize(cellPoint));
1549 if (!result.isRectBasedTest())
1552 if (!result.isRectBasedTest())
1559 void RenderTableSection::removeCachedCollapsedBorders(const RenderTableCell* cell)
1561 if (!table()->collapseBorders())
1564 for (int side = CBSBefore; side <= CBSEnd; ++side)
1565 m_cellsCollapsedBorders.remove(std::make_pair(cell, side));
1568 void RenderTableSection::setCachedCollapsedBorder(const RenderTableCell* cell, CollapsedBorderSide side, CollapsedBorderValue border)
1570 ASSERT(table()->collapseBorders());
1571 m_cellsCollapsedBorders.set(std::make_pair(cell, side), border);
1574 CollapsedBorderValue& RenderTableSection::cachedCollapsedBorder(const RenderTableCell* cell, CollapsedBorderSide side)
1576 ASSERT(table()->collapseBorders());
1577 HashMap<std::pair<const RenderTableCell*, int>, CollapsedBorderValue>::iterator it = m_cellsCollapsedBorders.find(std::make_pair(cell, side));
1578 ASSERT(it != m_cellsCollapsedBorders.end());
1582 RenderTableSection* RenderTableSection::createAnonymousWithParentRenderer(const RenderObject* parent)
1584 auto section = new RenderTableSection(parent->document(), RenderStyle::createAnonymousStyleWithDisplay(&parent->style(), TABLE_ROW_GROUP));
1585 section->initializeStyle();
1589 void RenderTableSection::setLogicalPositionForCell(RenderTableCell* cell, unsigned effectiveColumn) const
1591 LayoutPoint oldCellLocation = cell->location();
1593 LayoutPoint cellLocation(0, m_rowPos[cell->rowIndex()]);
1594 int horizontalBorderSpacing = table()->hBorderSpacing();
1596 // FIXME: The table's direction should determine our row's direction, not the section's (see bug 96691).
1597 if (!style().isLeftToRightDirection())
1598 cellLocation.setX(table()->columnPositions()[table()->numEffCols()] - table()->columnPositions()[table()->colToEffCol(cell->col() + cell->colSpan())] + horizontalBorderSpacing);
1600 cellLocation.setX(table()->columnPositions()[effectiveColumn] + horizontalBorderSpacing);
1602 cell->setLogicalLocation(cellLocation);
1603 view().addLayoutDelta(oldCellLocation - cell->location());
1606 } // namespace WebCore