• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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)
9  *
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.
14  *
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.
19  *
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.
24  */
25 
26 #include "config.h"
27 #include "core/rendering/RenderTableSection.h"
28 
29 // FIXME: Remove 'RuntimeEnabledFeatures.h' when http://crbug.com/78724 is closed.
30 #include "RuntimeEnabledFeatures.h"
31 #include <limits>
32 #include "core/rendering/GraphicsContextAnnotator.h"
33 #include "core/rendering/HitTestResult.h"
34 #include "core/rendering/LayoutRectRecorder.h"
35 #include "core/rendering/PaintInfo.h"
36 #include "core/rendering/RenderTableCell.h"
37 #include "core/rendering/RenderTableCol.h"
38 #include "core/rendering/RenderTableRow.h"
39 #include "core/rendering/RenderView.h"
40 #include "core/rendering/SubtreeLayoutScope.h"
41 #include "wtf/HashSet.h"
42 
43 using namespace std;
44 
45 namespace WebCore {
46 
47 using namespace HTMLNames;
48 
49 // Those 2 variables are used to balance the memory consumption vs the repaint time on big tables.
50 static unsigned gMinTableSizeToUseFastPaintPathWithOverflowingCell = 75 * 75;
51 static float gMaxAllowedOverflowingCellRatioForFastPaintPath = 0.1f;
52 
setRowLogicalHeightToRowStyleLogicalHeight(RenderTableSection::RowStruct & row)53 static inline void setRowLogicalHeightToRowStyleLogicalHeight(RenderTableSection::RowStruct& row)
54 {
55     ASSERT(row.rowRenderer);
56     row.logicalHeight = row.rowRenderer->style()->logicalHeight();
57 }
58 
updateLogicalHeightForCell(RenderTableSection::RowStruct & row,const RenderTableCell * cell)59 static inline void updateLogicalHeightForCell(RenderTableSection::RowStruct& row, const RenderTableCell* cell)
60 {
61     // We ignore height settings on rowspan cells.
62     if (cell->rowSpan() != 1)
63         return;
64 
65     Length logicalHeight = cell->style()->logicalHeight();
66     if (logicalHeight.isPositive()) {
67         Length cRowLogicalHeight = row.logicalHeight;
68         switch (logicalHeight.type()) {
69         case Percent:
70             if (!(cRowLogicalHeight.isPercent())
71                 || (cRowLogicalHeight.isPercent() && cRowLogicalHeight.percent() < logicalHeight.percent()))
72                 row.logicalHeight = logicalHeight;
73             break;
74         case Fixed:
75             if (cRowLogicalHeight.type() < Percent
76                 || (cRowLogicalHeight.isFixed() && cRowLogicalHeight.value() < logicalHeight.value()))
77                 row.logicalHeight = logicalHeight;
78             break;
79         default:
80             break;
81         }
82     }
83 }
84 
85 
RenderTableSection(Element * element)86 RenderTableSection::RenderTableSection(Element* element)
87     : RenderBox(element)
88     , m_cCol(0)
89     , m_cRow(0)
90     , m_outerBorderStart(0)
91     , m_outerBorderEnd(0)
92     , m_outerBorderBefore(0)
93     , m_outerBorderAfter(0)
94     , m_needsCellRecalc(false)
95     , m_hasMultipleCellLevels(false)
96 {
97     // init RenderObject attributes
98     setInline(false); // our object is not Inline
99 }
100 
~RenderTableSection()101 RenderTableSection::~RenderTableSection()
102 {
103 }
104 
styleDidChange(StyleDifference diff,const RenderStyle * oldStyle)105 void RenderTableSection::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
106 {
107     RenderBox::styleDidChange(diff, oldStyle);
108     propagateStyleToAnonymousChildren();
109 
110     // If border was changed, notify table.
111     RenderTable* table = this->table();
112     if (table && !table->selfNeedsLayout() && !table->normalChildNeedsLayout() && oldStyle && oldStyle->border() != style()->border())
113         table->invalidateCollapsedBorders();
114 }
115 
willBeRemovedFromTree()116 void RenderTableSection::willBeRemovedFromTree()
117 {
118     RenderBox::willBeRemovedFromTree();
119 
120     // Preventively invalidate our cells as we may be re-inserted into
121     // a new table which would require us to rebuild our structure.
122     setNeedsCellRecalc();
123 }
124 
addChild(RenderObject * child,RenderObject * beforeChild)125 void RenderTableSection::addChild(RenderObject* child, RenderObject* beforeChild)
126 {
127     if (!child->isTableRow()) {
128         RenderObject* last = beforeChild;
129         if (!last)
130             last = lastChild();
131         if (last && last->isAnonymous() && !last->isBeforeOrAfterContent()) {
132             if (beforeChild == last)
133                 beforeChild = last->firstChild();
134             last->addChild(child, beforeChild);
135             return;
136         }
137 
138         if (beforeChild && !beforeChild->isAnonymous() && beforeChild->parent() == this) {
139             RenderObject* row = beforeChild->previousSibling();
140             if (row && row->isTableRow() && row->isAnonymous()) {
141                 row->addChild(child);
142                 return;
143             }
144         }
145 
146         // If beforeChild is inside an anonymous cell/row, insert into the cell or into
147         // the anonymous row containing it, if there is one.
148         RenderObject* lastBox = last;
149         while (lastBox && lastBox->parent()->isAnonymous() && !lastBox->isTableRow())
150             lastBox = lastBox->parent();
151         if (lastBox && lastBox->isAnonymous() && !lastBox->isBeforeOrAfterContent()) {
152             lastBox->addChild(child, beforeChild);
153             return;
154         }
155 
156         RenderObject* row = RenderTableRow::createAnonymousWithParentRenderer(this);
157         addChild(row, beforeChild);
158         row->addChild(child);
159         return;
160     }
161 
162     if (beforeChild)
163         setNeedsCellRecalc();
164 
165     unsigned insertionRow = m_cRow;
166     ++m_cRow;
167     m_cCol = 0;
168 
169     ensureRows(m_cRow);
170 
171     RenderTableRow* row = toRenderTableRow(child);
172     m_grid[insertionRow].rowRenderer = row;
173     row->setRowIndex(insertionRow);
174 
175     if (!beforeChild)
176         setRowLogicalHeightToRowStyleLogicalHeight(m_grid[insertionRow]);
177 
178     if (beforeChild && beforeChild->parent() != this)
179         beforeChild = splitAnonymousBoxesAroundChild(beforeChild);
180 
181     ASSERT(!beforeChild || beforeChild->isTableRow());
182     RenderBox::addChild(child, beforeChild);
183 }
184 
ensureRows(unsigned numRows)185 void RenderTableSection::ensureRows(unsigned numRows)
186 {
187     if (numRows <= m_grid.size())
188         return;
189 
190     unsigned oldSize = m_grid.size();
191     m_grid.grow(numRows);
192 
193     unsigned effectiveColumnCount = max(1u, table()->numEffCols());
194     for (unsigned row = oldSize; row < m_grid.size(); ++row)
195         m_grid[row].row.grow(effectiveColumnCount);
196 }
197 
addCell(RenderTableCell * cell,RenderTableRow * row)198 void RenderTableSection::addCell(RenderTableCell* cell, RenderTableRow* row)
199 {
200     // We don't insert the cell if we need cell recalc as our internal columns' representation
201     // will have drifted from the table's representation. Also recalcCells will call addCell
202     // at a later time after sync'ing our columns' with the table's.
203     if (needsCellRecalc())
204         return;
205 
206     unsigned rSpan = cell->rowSpan();
207     unsigned cSpan = cell->colSpan();
208     const Vector<RenderTable::ColumnStruct>& columns = table()->columns();
209     unsigned nCols = columns.size();
210     unsigned insertionRow = row->rowIndex();
211 
212     // ### mozilla still seems to do the old HTML way, even for strict DTD
213     // (see the annotation on table cell layouting in the CSS specs and the testcase below:
214     // <TABLE border>
215     // <TR><TD>1 <TD rowspan="2">2 <TD>3 <TD>4
216     // <TR><TD colspan="2">5
217     // </TABLE>
218     while (m_cCol < nCols && (cellAt(insertionRow, m_cCol).hasCells() || cellAt(insertionRow, m_cCol).inColSpan))
219         m_cCol++;
220 
221     updateLogicalHeightForCell(m_grid[insertionRow], cell);
222 
223     ensureRows(insertionRow + rSpan);
224 
225     m_grid[insertionRow].rowRenderer = row;
226 
227     unsigned col = m_cCol;
228     // tell the cell where it is
229     bool inColSpan = false;
230     while (cSpan) {
231         unsigned currentSpan;
232         if (m_cCol >= nCols) {
233             table()->appendColumn(cSpan);
234             currentSpan = cSpan;
235         } else {
236             if (cSpan < columns[m_cCol].span)
237                 table()->splitColumn(m_cCol, cSpan);
238             currentSpan = columns[m_cCol].span;
239         }
240         for (unsigned r = 0; r < rSpan; r++) {
241             CellStruct& c = cellAt(insertionRow + r, m_cCol);
242             ASSERT(cell);
243             c.cells.append(cell);
244             // If cells overlap then we take the slow path for painting.
245             if (c.cells.size() > 1)
246                 m_hasMultipleCellLevels = true;
247             if (inColSpan)
248                 c.inColSpan = true;
249         }
250         m_cCol++;
251         cSpan -= currentSpan;
252         inColSpan = true;
253     }
254     cell->setCol(table()->effColToCol(col));
255 }
256 
rowHasOnlySpanningCells(unsigned row)257 bool RenderTableSection::rowHasOnlySpanningCells(unsigned row)
258 {
259     unsigned totalCols = m_grid[row].row.size();
260 
261     if (!totalCols)
262         return false;
263 
264     for (unsigned col = 0; col < totalCols; col++) {
265         const CellStruct& rowSpanCell = cellAt(row, col);
266 
267         // Empty cell is not a valid cell so it is not a rowspan cell.
268         if (rowSpanCell.cells.isEmpty())
269             return false;
270 
271         if (rowSpanCell.cells[0]->rowSpan() == 1)
272             return false;
273     }
274 
275     return true;
276 }
277 
populateSpanningRowsHeightFromCell(RenderTableCell * cell,struct SpanningRowsHeight & spanningRowsHeight)278 void RenderTableSection::populateSpanningRowsHeightFromCell(RenderTableCell* cell, struct SpanningRowsHeight& spanningRowsHeight)
279 {
280     const unsigned rowSpan = cell->rowSpan();
281     const unsigned rowIndex = cell->rowIndex();
282 
283     spanningRowsHeight.spanningCellHeightIgnoringBorderSpacing = cell->logicalHeightForRowSizing();
284 
285     spanningRowsHeight.rowHeight.resize(rowSpan);
286     spanningRowsHeight.totalRowsHeight = 0;
287     for (unsigned row = 0; row < rowSpan; row++) {
288         unsigned actualRow = row + rowIndex;
289 
290         spanningRowsHeight.rowHeight[row] = m_rowPos[actualRow + 1] - m_rowPos[actualRow] - borderSpacingForRow(actualRow);
291         if (!spanningRowsHeight.rowHeight[row])
292             spanningRowsHeight.rowWithOnlySpanningCells |= rowHasOnlySpanningCells(actualRow);
293 
294         spanningRowsHeight.totalRowsHeight += spanningRowsHeight.rowHeight[row];
295         spanningRowsHeight.spanningCellHeightIgnoringBorderSpacing -= borderSpacingForRow(actualRow);
296     }
297     // We don't span the following row so its border-spacing (if any) should be included.
298     spanningRowsHeight.spanningCellHeightIgnoringBorderSpacing += borderSpacingForRow(rowIndex + rowSpan - 1);
299 }
300 
distributeExtraRowSpanHeightToPercentRows(RenderTableCell * cell,int totalPercent,int & extraRowSpanningHeight,Vector<int> & rowsHeight)301 void RenderTableSection::distributeExtraRowSpanHeightToPercentRows(RenderTableCell* cell, int totalPercent, int& extraRowSpanningHeight, Vector<int>& rowsHeight)
302 {
303     if (!extraRowSpanningHeight || !totalPercent)
304         return;
305 
306     const unsigned rowSpan = cell->rowSpan();
307     const unsigned rowIndex = cell->rowIndex();
308     int percent = min(totalPercent, 100);
309     const int tableHeight = m_rowPos[m_grid.size()] + extraRowSpanningHeight;
310 
311     // Our algorithm matches Firefox. Extra spanning height would be distributed Only in first percent height rows
312     // those total percent is 100. Other percent rows would be uneffected even extra spanning height is remain.
313     int accumulatedPositionIncrease = 0;
314     for (unsigned row = rowIndex; row < (rowIndex + rowSpan); row++) {
315         if (percent > 0 && extraRowSpanningHeight > 0) {
316             if (m_grid[row].logicalHeight.isPercent()) {
317                 int toAdd = (tableHeight * m_grid[row].logicalHeight.percent() / 100) - rowsHeight[row - rowIndex];
318                 // FIXME: Note that this is wrong if we have a percentage above 100% and may make us grow
319                 // above the available space.
320 
321                 toAdd = min(toAdd, extraRowSpanningHeight);
322                 accumulatedPositionIncrease += toAdd;
323                 extraRowSpanningHeight -= toAdd;
324                 percent -= m_grid[row].logicalHeight.percent();
325             }
326         }
327         m_rowPos[row + 1] += accumulatedPositionIncrease;
328     }
329 }
330 
distributeExtraRowSpanHeightToAutoRows(RenderTableCell * cell,int totalAutoRowsHeight,int & extraRowSpanningHeight,Vector<int> & rowsHeight)331 void RenderTableSection::distributeExtraRowSpanHeightToAutoRows(RenderTableCell* cell, int totalAutoRowsHeight, int& extraRowSpanningHeight, Vector<int>& rowsHeight)
332 {
333     if (!extraRowSpanningHeight || !totalAutoRowsHeight)
334         return;
335 
336     const unsigned rowSpan = cell->rowSpan();
337     const unsigned rowIndex = cell->rowIndex();
338     int accumulatedPositionIncrease = 0;
339     int remainder = 0;
340 
341     // Aspect ratios of auto rows should not change otherwise table may look different than user expected.
342     // So extra height distributed in auto spanning rows based on their weight in spanning cell.
343     for (unsigned row = rowIndex; row < (rowIndex + rowSpan); row++) {
344         if (m_grid[row].logicalHeight.isAuto()) {
345             accumulatedPositionIncrease += (extraRowSpanningHeight * rowsHeight[row - rowIndex]) / totalAutoRowsHeight;
346             remainder += (extraRowSpanningHeight * rowsHeight[row - rowIndex]) % totalAutoRowsHeight;
347 
348             // While whole extra spanning height is distributing in auto spanning rows, rational parts remains
349             // in every integer division. So accumulating all remainder part in integer division and when total remainder
350             // is equvalent to divisor then 1 unit increased in row position.
351             // Note that this algorithm is biased towards adding more space towards the lower rows.
352             if (remainder >= totalAutoRowsHeight) {
353                 remainder -= totalAutoRowsHeight;
354                 accumulatedPositionIncrease++;
355             }
356         }
357         m_rowPos[row + 1] += accumulatedPositionIncrease;
358     }
359 
360     ASSERT(!remainder);
361 
362     extraRowSpanningHeight -= accumulatedPositionIncrease;
363 }
364 
distributeExtraRowSpanHeightToRemainingRows(RenderTableCell * cell,int totalRemainingRowsHeight,int & extraRowSpanningHeight,Vector<int> & rowsHeight)365 void RenderTableSection::distributeExtraRowSpanHeightToRemainingRows(RenderTableCell* cell, int totalRemainingRowsHeight, int& extraRowSpanningHeight, Vector<int>& rowsHeight)
366 {
367     if (!extraRowSpanningHeight || !totalRemainingRowsHeight)
368         return;
369 
370     const unsigned rowSpan = cell->rowSpan();
371     const unsigned rowIndex = cell->rowIndex();
372     int accumulatedPositionIncrease = 0;
373     int remainder = 0;
374 
375     // Aspect ratios of the rows should not change otherwise table may look different than user expected.
376     // So extra height distribution in remaining spanning rows based on their weight in spanning cell.
377     for (unsigned row = rowIndex; row < (rowIndex + rowSpan); row++) {
378         if (!m_grid[row].logicalHeight.isPercent()) {
379             accumulatedPositionIncrease += (extraRowSpanningHeight * rowsHeight[row - rowIndex]) / totalRemainingRowsHeight;
380             remainder += (extraRowSpanningHeight * rowsHeight[row - rowIndex]) % totalRemainingRowsHeight;
381 
382             // While whole extra spanning height is distributing in remaining spanning rows, rational parts remains
383             // in every integer division. So accumulating all remainder part in integer division and when total remainder
384             // is equvalent to divisor then 1 unit increased in row position.
385             // Note that this algorithm is biased towards adding more space towards the lower rows.
386             if (remainder >= totalRemainingRowsHeight) {
387                 remainder -= totalRemainingRowsHeight;
388                 accumulatedPositionIncrease++;
389             }
390         }
391         m_rowPos[row + 1] += accumulatedPositionIncrease;
392     }
393 
394     ASSERT(!remainder);
395 
396     extraRowSpanningHeight -= accumulatedPositionIncrease;
397 }
398 
cellIsFullyIncludedInOtherCell(const RenderTableCell * cell1,const RenderTableCell * cell2)399 static bool cellIsFullyIncludedInOtherCell(const RenderTableCell* cell1, const RenderTableCell* cell2)
400 {
401     return (cell1->rowIndex() >= cell2->rowIndex() && (cell1->rowIndex() + cell1->rowSpan()) <= (cell2->rowIndex() + cell2->rowSpan()));
402 }
403 
404 // To avoid unneeded extra height distributions, we apply the following sorting algorithm:
compareRowSpanCellsInHeightDistributionOrder(const RenderTableCell * cell1,const RenderTableCell * cell2)405 static bool compareRowSpanCellsInHeightDistributionOrder(const RenderTableCell* cell1, const RenderTableCell* cell2)
406 {
407     // Sorting bigger height cell first if cells are at same index with same span because we will skip smaller
408     // height cell to distribute it's extra height.
409     if (cell1->rowIndex() == cell2->rowIndex() && cell1->rowSpan() == cell2->rowSpan())
410         return (cell1->logicalHeightForRowSizing() > cell2->logicalHeightForRowSizing());
411     // Sorting inner most cell first because if inner spanning cell'e extra height is distributed then outer
412     // spanning cell's extra height will adjust accordingly. In reverse order, there is more chances that outer
413     // spanning cell's height will exceed than defined by user.
414     if (cellIsFullyIncludedInOtherCell(cell1, cell2))
415         return true;
416     // Sorting lower row index first because first we need to apply the extra height of spanning cell which
417     // comes first in the table so lower rows's position would increment in sequence.
418     if (!cellIsFullyIncludedInOtherCell(cell2, cell1))
419         return (cell1->rowIndex() < cell2->rowIndex());
420 
421     return false;
422 }
423 
isHeightNeededForRowHavingOnlySpanningCells(unsigned row)424 bool RenderTableSection::isHeightNeededForRowHavingOnlySpanningCells(unsigned row)
425 {
426     unsigned totalCols = m_grid[row].row.size();
427 
428     if (!totalCols)
429         return false;
430 
431     for (unsigned col = 0; col < totalCols; col++) {
432         const CellStruct& rowSpanCell = cellAt(row, col);
433 
434         if (rowSpanCell.cells.size()) {
435             RenderTableCell* cell = rowSpanCell.cells[0];
436             const unsigned rowIndex = cell->rowIndex();
437             const unsigned rowSpan = cell->rowSpan();
438             int totalRowSpanCellHeight = 0;
439 
440             for (unsigned row = 0; row < rowSpan; row++) {
441                 unsigned actualRow = row + rowIndex;
442                 totalRowSpanCellHeight += m_rowPos[actualRow + 1] - m_rowPos[actualRow];
443             }
444             totalRowSpanCellHeight -= borderSpacingForRow(rowIndex + rowSpan - 1);
445 
446             if (totalRowSpanCellHeight < cell->logicalHeightForRowSizing())
447                 return true;
448         }
449     }
450 
451     return false;
452 }
453 
calcRowHeightHavingOnlySpanningCells(unsigned row)454 unsigned RenderTableSection::calcRowHeightHavingOnlySpanningCells(unsigned row)
455 {
456     ASSERT(rowHasOnlySpanningCells(row));
457 
458     unsigned totalCols = m_grid[row].row.size();
459 
460     if (!totalCols)
461         return 0;
462 
463     unsigned rowHeight = 0;
464 
465     for (unsigned col = 0; col < totalCols; col++) {
466         const CellStruct& rowSpanCell = cellAt(row, col);
467         if (rowSpanCell.cells.size() && rowSpanCell.cells[0]->rowSpan() > 1)
468             rowHeight = max(rowHeight, rowSpanCell.cells[0]->logicalHeightForRowSizing() / rowSpanCell.cells[0]->rowSpan());
469     }
470 
471     return rowHeight;
472 }
473 
updateRowsHeightHavingOnlySpanningCells(RenderTableCell * cell,struct SpanningRowsHeight & spanningRowsHeight)474 void RenderTableSection::updateRowsHeightHavingOnlySpanningCells(RenderTableCell* cell, struct SpanningRowsHeight& spanningRowsHeight)
475 {
476     ASSERT(spanningRowsHeight.rowHeight.size());
477 
478     int accumulatedPositionIncrease = 0;
479     const unsigned rowSpan = cell->rowSpan();
480     const unsigned rowIndex = cell->rowIndex();
481 
482     ASSERT_UNUSED(rowSpan, rowSpan == spanningRowsHeight.rowHeight.size());
483 
484     for (unsigned row = 0; row < spanningRowsHeight.rowHeight.size(); row++) {
485         unsigned actualRow = row + rowIndex;
486         if (!spanningRowsHeight.rowHeight[row] && rowHasOnlySpanningCells(actualRow) && isHeightNeededForRowHavingOnlySpanningCells(actualRow)) {
487             spanningRowsHeight.rowHeight[row] = calcRowHeightHavingOnlySpanningCells(actualRow);
488             accumulatedPositionIncrease += spanningRowsHeight.rowHeight[row];
489         }
490         m_rowPos[actualRow + 1] += accumulatedPositionIncrease;
491     }
492 
493     spanningRowsHeight.totalRowsHeight += accumulatedPositionIncrease;
494 }
495 
496 // Distribute rowSpan cell height in rows those comes in rowSpan cell based on the ratio of row's height if
497 // 1. RowSpan cell height is greater then the total height of rows in rowSpan cell
distributeRowSpanHeightToRows(SpanningRenderTableCells & rowSpanCells)498 void RenderTableSection::distributeRowSpanHeightToRows(SpanningRenderTableCells& rowSpanCells)
499 {
500     ASSERT(rowSpanCells.size());
501 
502     // 'rowSpanCells' list is already sorted based on the cells rowIndex in ascending order
503     // Arrange row spanning cell in the order in which we need to process first.
504     std::sort(rowSpanCells.begin(), rowSpanCells.end(), compareRowSpanCellsInHeightDistributionOrder);
505 
506     unsigned extraHeightToPropagate = 0;
507     unsigned lastRowIndex = 0;
508     unsigned lastRowSpan = 0;
509 
510     for (unsigned i = 0; i < rowSpanCells.size(); i++) {
511         RenderTableCell* cell = rowSpanCells[i];
512 
513         unsigned rowIndex = cell->rowIndex();
514 
515         unsigned rowSpan = cell->rowSpan();
516 
517         unsigned spanningCellEndIndex = rowIndex + rowSpan;
518         unsigned lastSpanningCellEndIndex = lastRowIndex + lastRowSpan;
519 
520         // Only heightest spanning cell will distribute it's extra height in row if more then one spanning cells
521         // present at same level.
522         if (rowIndex == lastRowIndex && rowSpan == lastRowSpan)
523             continue;
524 
525         int originalBeforePosition = m_rowPos[spanningCellEndIndex];
526 
527         // When 2 spanning cells are ending at same row index then while extra height distribution of first spanning
528         // cell updates position of the last row so getting the original position of the last row in second spanning
529         // cell need to reduce the height changed by first spanning cell.
530         if (spanningCellEndIndex == lastSpanningCellEndIndex)
531             originalBeforePosition -= extraHeightToPropagate;
532 
533         if (extraHeightToPropagate) {
534             for (unsigned row = lastSpanningCellEndIndex + 1; row <= spanningCellEndIndex; row++)
535                 m_rowPos[row] += extraHeightToPropagate;
536         }
537 
538         lastRowIndex = rowIndex;
539         lastRowSpan = rowSpan;
540 
541         struct SpanningRowsHeight spanningRowsHeight;
542 
543         populateSpanningRowsHeightFromCell(cell, spanningRowsHeight);
544 
545         if (spanningRowsHeight.rowWithOnlySpanningCells)
546             updateRowsHeightHavingOnlySpanningCells(cell, spanningRowsHeight);
547 
548         if (!spanningRowsHeight.totalRowsHeight || spanningRowsHeight.spanningCellHeightIgnoringBorderSpacing <= spanningRowsHeight.totalRowsHeight) {
549             extraHeightToPropagate = m_rowPos[rowIndex + rowSpan] - originalBeforePosition;
550             continue;
551         }
552 
553         int totalPercent = 0;
554         int totalAutoRowsHeight = 0;
555         int totalRemainingRowsHeight = spanningRowsHeight.totalRowsHeight;
556 
557         // FIXME: Inner spanning cell height should not change if it have fixed height when it's parent spanning cell
558         // is distributing it's extra height in rows.
559 
560         // Calculate total percentage, total auto rows height and total rows height except percent rows.
561         for (unsigned row = rowIndex; row < spanningCellEndIndex; row++) {
562             if (m_grid[row].logicalHeight.isPercent()) {
563                 totalPercent += m_grid[row].logicalHeight.percent();
564                 totalRemainingRowsHeight -= spanningRowsHeight.rowHeight[row - rowIndex];
565             } else if (m_grid[row].logicalHeight.isAuto()) {
566                 totalAutoRowsHeight += spanningRowsHeight.rowHeight[row - rowIndex];
567             }
568         }
569 
570         int extraRowSpanningHeight = spanningRowsHeight.spanningCellHeightIgnoringBorderSpacing - spanningRowsHeight.totalRowsHeight;
571 
572         distributeExtraRowSpanHeightToPercentRows(cell, totalPercent, extraRowSpanningHeight, spanningRowsHeight.rowHeight);
573         distributeExtraRowSpanHeightToAutoRows(cell, totalAutoRowsHeight, extraRowSpanningHeight, spanningRowsHeight.rowHeight);
574         distributeExtraRowSpanHeightToRemainingRows(cell, totalRemainingRowsHeight, extraRowSpanningHeight, spanningRowsHeight.rowHeight);
575 
576         ASSERT(!extraRowSpanningHeight);
577 
578         // Getting total changed height in the table
579         extraHeightToPropagate = m_rowPos[spanningCellEndIndex] - originalBeforePosition;
580     }
581 
582     if (extraHeightToPropagate) {
583         // Apply changed height by rowSpan cells to rows present at the end of the table
584         for (unsigned row = lastRowIndex + lastRowSpan + 1; row <= m_grid.size(); row++)
585             m_rowPos[row] += extraHeightToPropagate;
586     }
587 }
588 
589 // Find out the baseline of the cell
590 // If the cell's baseline is more then the row's baseline then the cell's baseline become the row's baseline
591 // and if the row's baseline goes out of the row's boundries then adjust row height accordingly.
updateBaselineForCell(RenderTableCell * cell,unsigned row,LayoutUnit & baselineDescent)592 void RenderTableSection::updateBaselineForCell(RenderTableCell* cell, unsigned row, LayoutUnit& baselineDescent)
593 {
594     if (!cell->isBaselineAligned())
595         return;
596 
597     // Ignoring the intrinsic padding as it depends on knowing the row's baseline, which won't be accurate
598     // until the end of this function.
599     LayoutUnit baselinePosition = cell->cellBaselinePosition() - cell->intrinsicPaddingBefore();
600     if (baselinePosition > cell->borderBefore() + (cell->paddingBefore() - cell->intrinsicPaddingBefore())) {
601         m_grid[row].baseline = max(m_grid[row].baseline, baselinePosition);
602 
603         int cellStartRowBaselineDescent = 0;
604         if (cell->rowSpan() == 1) {
605             baselineDescent = max(baselineDescent, cell->logicalHeightForRowSizing() - baselinePosition);
606             cellStartRowBaselineDescent = baselineDescent;
607         }
608         m_rowPos[row + 1] = max<int>(m_rowPos[row + 1], m_rowPos[row] + m_grid[row].baseline + cellStartRowBaselineDescent);
609     }
610 }
611 
calcRowLogicalHeight()612 int RenderTableSection::calcRowLogicalHeight()
613 {
614 #ifndef NDEBUG
615     SetLayoutNeededForbiddenScope layoutForbiddenScope(this);
616 #endif
617 
618     ASSERT(!needsLayout());
619 
620     RenderTableCell* cell;
621 
622     RenderView* viewRenderer = view();
623     LayoutStateMaintainer statePusher(viewRenderer);
624 
625     m_rowPos.resize(m_grid.size() + 1);
626 
627     // We ignore the border-spacing on any non-top section as it is already included in the previous section's last row position.
628     if (this == table()->topSection())
629         m_rowPos[0] = table()->vBorderSpacing();
630     else
631         m_rowPos[0] = 0;
632 
633     SpanningRenderTableCells rowSpanCells;
634 #ifndef NDEBUG
635     HashSet<const RenderTableCell*> uniqueCells;
636 #endif
637 
638     for (unsigned r = 0; r < m_grid.size(); r++) {
639         m_grid[r].baseline = 0;
640         LayoutUnit baselineDescent = 0;
641 
642         // Our base size is the biggest logical height from our cells' styles (excluding row spanning cells).
643         m_rowPos[r + 1] = max(m_rowPos[r] + minimumValueForLength(m_grid[r].logicalHeight, 0, viewRenderer).round(), 0);
644 
645         Row& row = m_grid[r].row;
646         unsigned totalCols = row.size();
647         RenderTableCell* lastRowSpanCell = 0;
648 
649         for (unsigned c = 0; c < totalCols; c++) {
650             CellStruct& current = cellAt(r, c);
651             for (unsigned i = 0; i < current.cells.size(); i++) {
652                 cell = current.cells[i];
653                 if (current.inColSpan && cell->rowSpan() == 1)
654                     continue;
655 
656                 if (RuntimeEnabledFeatures::rowSpanLogicalHeightSpreadingEnabled()) {
657                     if (cell->rowSpan() > 1) {
658                         // For row spanning cells, we only handle them for the first row they span. This ensures we take their baseline into account.
659                         if (lastRowSpanCell != cell && cell->rowIndex() == r) {
660 #ifndef NDEBUG
661                             ASSERT(!uniqueCells.contains(cell));
662                             uniqueCells.add(cell);
663 #endif
664 
665                             rowSpanCells.append(cell);
666                             lastRowSpanCell = cell;
667 
668                             // Find out the baseline. The baseline is set on the first row in a rowSpan.
669                             updateBaselineForCell(cell, r, baselineDescent);
670                         }
671                         continue;
672                     }
673 
674                     ASSERT(cell->rowSpan() == 1);
675                 } else {
676                     // FIXME: We add all the logical row of a rowspan to the last rows
677                     // until crbug.com/78724 is fixed and the runtime flag removed.
678                     // This avoids propagating temporary regressions while we fix the bug.
679                     if ((cell->rowIndex() + cell->rowSpan() - 1) != r)
680                         continue;
681                 }
682 
683                 if (cell->hasOverrideHeight()) {
684                     if (!statePusher.didPush()) {
685                         // Technically, we should also push state for the row, but since
686                         // rows don't push a coordinate transform, that's not necessary.
687                         statePusher.push(this, locationOffset());
688                     }
689                     cell->clearIntrinsicPadding();
690                     cell->clearOverrideSize();
691                     cell->forceChildLayout();
692                 }
693 
694                 if (RuntimeEnabledFeatures::rowSpanLogicalHeightSpreadingEnabled()) {
695                     m_rowPos[r + 1] = max(m_rowPos[r + 1], m_rowPos[r] + cell->logicalHeightForRowSizing());
696 
697                     // Find out the baseline.
698                     updateBaselineForCell(cell, r, baselineDescent);
699                 } else {
700                     // For row spanning cells, |r| is the last row in the span.
701                     unsigned cellStartRow = cell->rowIndex();
702 
703                     m_rowPos[r + 1] = max(m_rowPos[r + 1], m_rowPos[cellStartRow] + cell->logicalHeightForRowSizing());
704 
705                     // Find out the baseline.
706                     updateBaselineForCell(cell, cellStartRow, baselineDescent);
707                 }
708             }
709         }
710 
711         // Add the border-spacing to our final position.
712         m_rowPos[r + 1] += borderSpacingForRow(r);
713         m_rowPos[r + 1] = max(m_rowPos[r + 1], m_rowPos[r]);
714     }
715 
716     if (!rowSpanCells.isEmpty()) {
717         ASSERT(RuntimeEnabledFeatures::rowSpanLogicalHeightSpreadingEnabled());
718         distributeRowSpanHeightToRows(rowSpanCells);
719     }
720 
721     ASSERT(!needsLayout());
722 
723     statePusher.pop();
724 
725     return m_rowPos[m_grid.size()];
726 }
727 
layout()728 void RenderTableSection::layout()
729 {
730     ASSERT(needsLayout());
731     ASSERT(!needsCellRecalc());
732     ASSERT(!table()->needsSectionRecalc());
733 
734     LayoutRectRecorder recorder(*this);
735 
736     // addChild may over-grow m_grid but we don't want to throw away the memory too early as addChild
737     // can be called in a loop (e.g during parsing). Doing it now ensures we have a stable-enough structure.
738     m_grid.shrinkToFit();
739 
740     LayoutStateMaintainer statePusher(view(), this, locationOffset(), style()->isFlippedBlocksWritingMode());
741 
742     const Vector<int>& columnPos = table()->columnPositions();
743 
744     SubtreeLayoutScope layouter(this);
745     for (unsigned r = 0; r < m_grid.size(); ++r) {
746         Row& row = m_grid[r].row;
747         unsigned cols = row.size();
748         // First, propagate our table layout's information to the cells. This will mark the row as needing layout
749         // if there was a column logical width change.
750         for (unsigned startColumn = 0; startColumn < cols; ++startColumn) {
751             CellStruct& current = row[startColumn];
752             RenderTableCell* cell = current.primaryCell();
753             if (!cell || current.inColSpan)
754                 continue;
755 
756             unsigned endCol = startColumn;
757             unsigned cspan = cell->colSpan();
758             while (cspan && endCol < cols) {
759                 ASSERT(endCol < table()->columns().size());
760                 cspan -= table()->columns()[endCol].span;
761                 endCol++;
762             }
763             int tableLayoutLogicalWidth = columnPos[endCol] - columnPos[startColumn] - table()->hBorderSpacing();
764             cell->setCellLogicalWidth(tableLayoutLogicalWidth, layouter);
765         }
766 
767         if (RenderTableRow* rowRenderer = m_grid[r].rowRenderer)
768             rowRenderer->layoutIfNeeded();
769     }
770 
771     statePusher.pop();
772     clearNeedsLayout();
773 }
774 
distributeExtraLogicalHeightToPercentRows(int & extraLogicalHeight,int totalPercent)775 void RenderTableSection::distributeExtraLogicalHeightToPercentRows(int& extraLogicalHeight, int totalPercent)
776 {
777     if (!totalPercent)
778         return;
779 
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();
794         }
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;
799     }
800 }
801 
distributeExtraLogicalHeightToAutoRows(int & extraLogicalHeight,unsigned autoRowsCount)802 void RenderTableSection::distributeExtraLogicalHeightToAutoRows(int& extraLogicalHeight, unsigned autoRowsCount)
803 {
804     if (!autoRowsCount)
805         return;
806 
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;
814             --autoRowsCount;
815         }
816         m_rowPos[r + 1] += totalLogicalHeightAdded;
817     }
818 }
819 
distributeRemainingExtraLogicalHeight(int & extraLogicalHeight)820 void RenderTableSection::distributeRemainingExtraLogicalHeight(int& extraLogicalHeight)
821 {
822     unsigned totalRows = m_grid.size();
823 
824     if (extraLogicalHeight <= 0 || !m_rowPos[totalRows])
825         return;
826 
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;
836     }
837 
838     extraLogicalHeight -= totalLogicalHeightAdded;
839 }
840 
distributeExtraLogicalHeightToRows(int extraLogicalHeight)841 int RenderTableSection::distributeExtraLogicalHeightToRows(int extraLogicalHeight)
842 {
843     if (!extraLogicalHeight)
844         return extraLogicalHeight;
845 
846     unsigned totalRows = m_grid.size();
847     if (!totalRows)
848         return extraLogicalHeight;
849 
850     if (!m_rowPos[totalRows] && nextSibling())
851         return extraLogicalHeight;
852 
853     unsigned autoRowsCount = 0;
854     int totalPercent = 0;
855     for (unsigned r = 0; r < totalRows; r++) {
856         if (m_grid[r].logicalHeight.isAuto())
857             ++autoRowsCount;
858         else if (m_grid[r].logicalHeight.isPercent())
859             totalPercent += m_grid[r].logicalHeight.percent();
860     }
861 
862     int remainingExtraLogicalHeight = extraLogicalHeight;
863     distributeExtraLogicalHeightToPercentRows(remainingExtraLogicalHeight, totalPercent);
864     distributeExtraLogicalHeightToAutoRows(remainingExtraLogicalHeight, autoRowsCount);
865     distributeRemainingExtraLogicalHeight(remainingExtraLogicalHeight);
866     return extraLogicalHeight - remainingExtraLogicalHeight;
867 }
868 
shouldFlexCellChild(RenderObject * cellDescendant)869 static bool shouldFlexCellChild(RenderObject* cellDescendant)
870 {
871     return cellDescendant->isReplaced() || (cellDescendant->isBox() && toRenderBox(cellDescendant)->scrollsOverflow());
872 }
873 
layoutRows()874 void RenderTableSection::layoutRows()
875 {
876 #ifndef NDEBUG
877     SetLayoutNeededForbiddenScope layoutForbiddenScope(this);
878 #endif
879 
880     ASSERT(!needsLayout());
881 
882     unsigned totalRows = m_grid.size();
883 
884     // Set the width of our section now.  The rows will also be this width.
885     setLogicalWidth(table()->contentLogicalWidth());
886     m_overflow.clear();
887     m_overflowingCells.clear();
888     m_forceSlowPaintPathWithOverflowingCell = false;
889 
890     int vspacing = table()->vBorderSpacing();
891     unsigned nEffCols = table()->numEffCols();
892 
893     LayoutStateMaintainer statePusher(view(), this, locationOffset(), style()->isFlippedBlocksWritingMode());
894 
895     for (unsigned r = 0; r < totalRows; r++) {
896         // Set the row's x/y position and width/height.
897         if (RenderTableRow* rowRenderer = m_grid[r].rowRenderer) {
898             rowRenderer->setLocation(LayoutPoint(0, m_rowPos[r]));
899             rowRenderer->setLogicalWidth(logicalWidth());
900             rowRenderer->setLogicalHeight(m_rowPos[r + 1] - m_rowPos[r] - vspacing);
901             rowRenderer->updateLayerTransform();
902         }
903 
904         int rowHeightIncreaseForPagination = 0;
905 
906         for (unsigned c = 0; c < nEffCols; c++) {
907             CellStruct& cs = cellAt(r, c);
908             RenderTableCell* cell = cs.primaryCell();
909 
910             if (!cell || cs.inColSpan)
911                 continue;
912 
913             int rowIndex = cell->rowIndex();
914             int rHeight = m_rowPos[rowIndex + cell->rowSpan()] - m_rowPos[rowIndex] - vspacing;
915 
916             // Force percent height children to lay themselves out again.
917             // This will cause these children to grow to fill the cell.
918             // FIXME: There is still more work to do here to fully match WinIE (should
919             // it become necessary to do so).  In quirks mode, WinIE behaves like we
920             // do, but it will clip the cells that spill out of the table section.  In
921             // strict mode, Mozilla and WinIE both regrow the table to accommodate the
922             // new height of the cell (thus letting the percentages cause growth one
923             // time only).  We may also not be handling row-spanning cells correctly.
924             //
925             // Note also the oddity where replaced elements always flex, and yet blocks/tables do
926             // not necessarily flex.  WinIE is crazy and inconsistent, and we can't hope to
927             // match the behavior perfectly, but we'll continue to refine it as we discover new
928             // bugs. :)
929             bool cellChildrenFlex = false;
930             bool flexAllChildren = cell->style()->logicalHeight().isFixed()
931                 || (!table()->style()->logicalHeight().isAuto() && rHeight != cell->logicalHeight());
932 
933             for (RenderObject* child = cell->firstChild(); child; child = child->nextSibling()) {
934                 if (!child->isText() && child->style()->logicalHeight().isPercent()
935                     && (flexAllChildren || shouldFlexCellChild(child))
936                     && (!child->isTable() || toRenderTable(child)->hasSections())) {
937                     cellChildrenFlex = true;
938                     break;
939                 }
940             }
941 
942             if (!cellChildrenFlex) {
943                 if (TrackedRendererListHashSet* percentHeightDescendants = cell->percentHeightDescendants()) {
944                     TrackedRendererListHashSet::iterator end = percentHeightDescendants->end();
945                     for (TrackedRendererListHashSet::iterator it = percentHeightDescendants->begin(); it != end; ++it) {
946                         if (flexAllChildren || shouldFlexCellChild(*it)) {
947                             cellChildrenFlex = true;
948                             break;
949                         }
950                     }
951                 }
952             }
953 
954             if (cellChildrenFlex) {
955                 // Alignment within a cell is based off the calculated
956                 // height, which becomes irrelevant once the cell has
957                 // been resized based off its percentage.
958                 cell->setOverrideLogicalContentHeightFromRowHeight(rHeight);
959                 cell->forceChildLayout();
960 
961                 // If the baseline moved, we may have to update the data for our row. Find out the new baseline.
962                 if (cell->isBaselineAligned()) {
963                     LayoutUnit baseline = cell->cellBaselinePosition();
964                     if (baseline > cell->borderBefore() + cell->paddingBefore())
965                         m_grid[r].baseline = max(m_grid[r].baseline, baseline);
966                 }
967             }
968 
969             SubtreeLayoutScope layouter(cell);
970             cell->computeIntrinsicPadding(rHeight, layouter);
971 
972             LayoutRect oldCellRect = cell->frameRect();
973 
974             setLogicalPositionForCell(cell, c);
975 
976             if (!cell->needsLayout() && view()->layoutState()->pageLogicalHeight() && view()->layoutState()->pageLogicalOffset(cell, cell->logicalTop()) != cell->pageLogicalOffset())
977                 layouter.setChildNeedsLayout(cell);
978 
979             cell->layoutIfNeeded();
980 
981             // FIXME: Make pagination work with vertical tables.
982             if (view()->layoutState()->pageLogicalHeight() && cell->logicalHeight() != rHeight) {
983                 // FIXME: Pagination might have made us change size. For now just shrink or grow the cell to fit without doing a relayout.
984                 // 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
985                 // either. It's at least stable though and won't result in an infinite # of relayouts that may never stabilize.
986                 if (cell->logicalHeight() > rHeight)
987                     rowHeightIncreaseForPagination = max<int>(rowHeightIncreaseForPagination, cell->logicalHeight() - rHeight);
988                 cell->setLogicalHeight(rHeight);
989             }
990 
991             LayoutSize childOffset(cell->location() - oldCellRect.location());
992             if (childOffset.width() || childOffset.height()) {
993                 view()->addLayoutDelta(childOffset);
994 
995                 // If the child moved, we have to repaint it as well as any floating/positioned
996                 // descendants.  An exception is if we need a layout.  In this case, we know we're going to
997                 // repaint ourselves (and the child) anyway.
998                 if (!table()->selfNeedsLayout() && cell->checkForRepaintDuringLayout())
999                     cell->repaintDuringLayoutIfMoved(oldCellRect);
1000             }
1001         }
1002         if (rowHeightIncreaseForPagination) {
1003             for (unsigned rowIndex = r + 1; rowIndex <= totalRows; rowIndex++)
1004                 m_rowPos[rowIndex] += rowHeightIncreaseForPagination;
1005             for (unsigned c = 0; c < nEffCols; ++c) {
1006                 Vector<RenderTableCell*, 1>& cells = cellAt(r, c).cells;
1007                 for (size_t i = 0; i < cells.size(); ++i)
1008                     cells[i]->setLogicalHeight(cells[i]->logicalHeight() + rowHeightIncreaseForPagination);
1009             }
1010         }
1011     }
1012 
1013     ASSERT(!needsLayout());
1014 
1015     setLogicalHeight(m_rowPos[totalRows]);
1016 
1017     computeOverflowFromCells(totalRows, nEffCols);
1018 
1019     statePusher.pop();
1020 }
1021 
computeOverflowFromCells()1022 void RenderTableSection::computeOverflowFromCells()
1023 {
1024     unsigned totalRows = m_grid.size();
1025     unsigned nEffCols = table()->numEffCols();
1026     computeOverflowFromCells(totalRows, nEffCols);
1027 }
1028 
computeOverflowFromCells(unsigned totalRows,unsigned nEffCols)1029 void RenderTableSection::computeOverflowFromCells(unsigned totalRows, unsigned nEffCols)
1030 {
1031     unsigned totalCellsCount = nEffCols * totalRows;
1032     unsigned maxAllowedOverflowingCellsCount = totalCellsCount < gMinTableSizeToUseFastPaintPathWithOverflowingCell ? 0 : gMaxAllowedOverflowingCellRatioForFastPaintPath * totalCellsCount;
1033 
1034 #ifndef NDEBUG
1035     bool hasOverflowingCell = false;
1036 #endif
1037     // Now that our height has been determined, add in overflow from cells.
1038     for (unsigned r = 0; r < totalRows; r++) {
1039         for (unsigned c = 0; c < nEffCols; c++) {
1040             CellStruct& cs = cellAt(r, c);
1041             RenderTableCell* cell = cs.primaryCell();
1042             if (!cell || cs.inColSpan)
1043                 continue;
1044             if (r < totalRows - 1 && cell == primaryCellAt(r + 1, c))
1045                 continue;
1046             addOverflowFromChild(cell);
1047 #ifndef NDEBUG
1048             hasOverflowingCell |= cell->hasVisualOverflow();
1049 #endif
1050             if (cell->hasVisualOverflow() && !m_forceSlowPaintPathWithOverflowingCell) {
1051                 m_overflowingCells.add(cell);
1052                 if (m_overflowingCells.size() > maxAllowedOverflowingCellsCount) {
1053                     // We need to set m_forcesSlowPaintPath only if there is a least one overflowing cells as the hit testing code rely on this information.
1054                     m_forceSlowPaintPathWithOverflowingCell = true;
1055                     // The slow path does not make any use of the overflowing cells info, don't hold on to the memory.
1056                     m_overflowingCells.clear();
1057                 }
1058             }
1059         }
1060     }
1061 
1062     ASSERT(hasOverflowingCell == this->hasOverflowingCell());
1063 }
1064 
calcOuterBorderBefore() const1065 int RenderTableSection::calcOuterBorderBefore() const
1066 {
1067     unsigned totalCols = table()->numEffCols();
1068     if (!m_grid.size() || !totalCols)
1069         return 0;
1070 
1071     unsigned borderWidth = 0;
1072 
1073     const BorderValue& sb = style()->borderBefore();
1074     if (sb.style() == BHIDDEN)
1075         return -1;
1076     if (sb.style() > BHIDDEN)
1077         borderWidth = sb.width();
1078 
1079     const BorderValue& rb = firstChild()->style()->borderBefore();
1080     if (rb.style() == BHIDDEN)
1081         return -1;
1082     if (rb.style() > BHIDDEN && rb.width() > borderWidth)
1083         borderWidth = rb.width();
1084 
1085     bool allHidden = true;
1086     for (unsigned c = 0; c < totalCols; c++) {
1087         const CellStruct& current = cellAt(0, c);
1088         if (current.inColSpan || !current.hasCells())
1089             continue;
1090         const BorderValue& cb = current.primaryCell()->style()->borderBefore(); // FIXME: Make this work with perpendicular and flipped cells.
1091         // FIXME: Don't repeat for the same col group
1092         RenderTableCol* colGroup = table()->colElement(c);
1093         if (colGroup) {
1094             const BorderValue& gb = colGroup->style()->borderBefore();
1095             if (gb.style() == BHIDDEN || cb.style() == BHIDDEN)
1096                 continue;
1097             allHidden = false;
1098             if (gb.style() > BHIDDEN && gb.width() > borderWidth)
1099                 borderWidth = gb.width();
1100             if (cb.style() > BHIDDEN && cb.width() > borderWidth)
1101                 borderWidth = cb.width();
1102         } else {
1103             if (cb.style() == BHIDDEN)
1104                 continue;
1105             allHidden = false;
1106             if (cb.style() > BHIDDEN && cb.width() > borderWidth)
1107                 borderWidth = cb.width();
1108         }
1109     }
1110     if (allHidden)
1111         return -1;
1112 
1113     return borderWidth / 2;
1114 }
1115 
calcOuterBorderAfter() const1116 int RenderTableSection::calcOuterBorderAfter() const
1117 {
1118     unsigned totalCols = table()->numEffCols();
1119     if (!m_grid.size() || !totalCols)
1120         return 0;
1121 
1122     unsigned borderWidth = 0;
1123 
1124     const BorderValue& sb = style()->borderAfter();
1125     if (sb.style() == BHIDDEN)
1126         return -1;
1127     if (sb.style() > BHIDDEN)
1128         borderWidth = sb.width();
1129 
1130     const BorderValue& rb = lastChild()->style()->borderAfter();
1131     if (rb.style() == BHIDDEN)
1132         return -1;
1133     if (rb.style() > BHIDDEN && rb.width() > borderWidth)
1134         borderWidth = rb.width();
1135 
1136     bool allHidden = true;
1137     for (unsigned c = 0; c < totalCols; c++) {
1138         const CellStruct& current = cellAt(m_grid.size() - 1, c);
1139         if (current.inColSpan || !current.hasCells())
1140             continue;
1141         const BorderValue& cb = current.primaryCell()->style()->borderAfter(); // FIXME: Make this work with perpendicular and flipped cells.
1142         // FIXME: Don't repeat for the same col group
1143         RenderTableCol* colGroup = table()->colElement(c);
1144         if (colGroup) {
1145             const BorderValue& gb = colGroup->style()->borderAfter();
1146             if (gb.style() == BHIDDEN || cb.style() == BHIDDEN)
1147                 continue;
1148             allHidden = false;
1149             if (gb.style() > BHIDDEN && gb.width() > borderWidth)
1150                 borderWidth = gb.width();
1151             if (cb.style() > BHIDDEN && cb.width() > borderWidth)
1152                 borderWidth = cb.width();
1153         } else {
1154             if (cb.style() == BHIDDEN)
1155                 continue;
1156             allHidden = false;
1157             if (cb.style() > BHIDDEN && cb.width() > borderWidth)
1158                 borderWidth = cb.width();
1159         }
1160     }
1161     if (allHidden)
1162         return -1;
1163 
1164     return (borderWidth + 1) / 2;
1165 }
1166 
calcOuterBorderStart() const1167 int RenderTableSection::calcOuterBorderStart() const
1168 {
1169     unsigned totalCols = table()->numEffCols();
1170     if (!m_grid.size() || !totalCols)
1171         return 0;
1172 
1173     unsigned borderWidth = 0;
1174 
1175     const BorderValue& sb = style()->borderStart();
1176     if (sb.style() == BHIDDEN)
1177         return -1;
1178     if (sb.style() > BHIDDEN)
1179         borderWidth = sb.width();
1180 
1181     if (RenderTableCol* colGroup = table()->colElement(0)) {
1182         const BorderValue& gb = colGroup->style()->borderStart();
1183         if (gb.style() == BHIDDEN)
1184             return -1;
1185         if (gb.style() > BHIDDEN && gb.width() > borderWidth)
1186             borderWidth = gb.width();
1187     }
1188 
1189     bool allHidden = true;
1190     for (unsigned r = 0; r < m_grid.size(); r++) {
1191         const CellStruct& current = cellAt(r, 0);
1192         if (!current.hasCells())
1193             continue;
1194         // FIXME: Don't repeat for the same cell
1195         const BorderValue& cb = current.primaryCell()->style()->borderStart(); // FIXME: Make this work with perpendicular and flipped cells.
1196         const BorderValue& rb = current.primaryCell()->parent()->style()->borderStart();
1197         if (cb.style() == BHIDDEN || rb.style() == BHIDDEN)
1198             continue;
1199         allHidden = false;
1200         if (cb.style() > BHIDDEN && cb.width() > borderWidth)
1201             borderWidth = cb.width();
1202         if (rb.style() > BHIDDEN && rb.width() > borderWidth)
1203             borderWidth = rb.width();
1204     }
1205     if (allHidden)
1206         return -1;
1207 
1208     return (borderWidth + (table()->style()->isLeftToRightDirection() ? 0 : 1)) / 2;
1209 }
1210 
calcOuterBorderEnd() const1211 int RenderTableSection::calcOuterBorderEnd() const
1212 {
1213     unsigned totalCols = table()->numEffCols();
1214     if (!m_grid.size() || !totalCols)
1215         return 0;
1216 
1217     unsigned borderWidth = 0;
1218 
1219     const BorderValue& sb = style()->borderEnd();
1220     if (sb.style() == BHIDDEN)
1221         return -1;
1222     if (sb.style() > BHIDDEN)
1223         borderWidth = sb.width();
1224 
1225     if (RenderTableCol* colGroup = table()->colElement(totalCols - 1)) {
1226         const BorderValue& gb = colGroup->style()->borderEnd();
1227         if (gb.style() == BHIDDEN)
1228             return -1;
1229         if (gb.style() > BHIDDEN && gb.width() > borderWidth)
1230             borderWidth = gb.width();
1231     }
1232 
1233     bool allHidden = true;
1234     for (unsigned r = 0; r < m_grid.size(); r++) {
1235         const CellStruct& current = cellAt(r, totalCols - 1);
1236         if (!current.hasCells())
1237             continue;
1238         // FIXME: Don't repeat for the same cell
1239         const BorderValue& cb = current.primaryCell()->style()->borderEnd(); // FIXME: Make this work with perpendicular and flipped cells.
1240         const BorderValue& rb = current.primaryCell()->parent()->style()->borderEnd();
1241         if (cb.style() == BHIDDEN || rb.style() == BHIDDEN)
1242             continue;
1243         allHidden = false;
1244         if (cb.style() > BHIDDEN && cb.width() > borderWidth)
1245             borderWidth = cb.width();
1246         if (rb.style() > BHIDDEN && rb.width() > borderWidth)
1247             borderWidth = rb.width();
1248     }
1249     if (allHidden)
1250         return -1;
1251 
1252     return (borderWidth + (table()->style()->isLeftToRightDirection() ? 1 : 0)) / 2;
1253 }
1254 
recalcOuterBorder()1255 void RenderTableSection::recalcOuterBorder()
1256 {
1257     m_outerBorderBefore = calcOuterBorderBefore();
1258     m_outerBorderAfter = calcOuterBorderAfter();
1259     m_outerBorderStart = calcOuterBorderStart();
1260     m_outerBorderEnd = calcOuterBorderEnd();
1261 }
1262 
firstLineBoxBaseline() const1263 int RenderTableSection::firstLineBoxBaseline() const
1264 {
1265     if (!m_grid.size())
1266         return -1;
1267 
1268     int firstLineBaseline = m_grid[0].baseline;
1269     if (firstLineBaseline)
1270         return firstLineBaseline + m_rowPos[0];
1271 
1272     firstLineBaseline = -1;
1273     const Row& firstRow = m_grid[0].row;
1274     for (size_t i = 0; i < firstRow.size(); ++i) {
1275         const CellStruct& cs = firstRow.at(i);
1276         const RenderTableCell* cell = cs.primaryCell();
1277         // Only cells with content have a baseline
1278         if (cell && cell->contentLogicalHeight())
1279             firstLineBaseline = max<int>(firstLineBaseline, cell->logicalTop() + cell->paddingBefore() + cell->borderBefore() + cell->contentLogicalHeight());
1280     }
1281 
1282     return firstLineBaseline;
1283 }
1284 
paint(PaintInfo & paintInfo,const LayoutPoint & paintOffset)1285 void RenderTableSection::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1286 {
1287     ANNOTATE_GRAPHICS_CONTEXT(paintInfo, this);
1288 
1289     ASSERT_WITH_SECURITY_IMPLICATION(!needsLayout());
1290     // avoid crashing on bugs that cause us to paint with dirty layout
1291     if (needsLayout())
1292         return;
1293 
1294     unsigned totalRows = m_grid.size();
1295     unsigned totalCols = table()->columns().size();
1296 
1297     if (!totalRows || !totalCols)
1298         return;
1299 
1300     LayoutPoint adjustedPaintOffset = paintOffset + location();
1301 
1302     PaintPhase phase = paintInfo.phase;
1303     bool pushedClip = pushContentsClip(paintInfo, adjustedPaintOffset, ForceContentsClip);
1304     paintObject(paintInfo, adjustedPaintOffset);
1305     if (pushedClip)
1306         popContentsClip(paintInfo, phase, adjustedPaintOffset);
1307 
1308     if ((phase == PaintPhaseOutline || phase == PaintPhaseSelfOutline) && style()->visibility() == VISIBLE)
1309         paintOutline(paintInfo, LayoutRect(adjustedPaintOffset, size()));
1310 }
1311 
compareCellPositions(RenderTableCell * elem1,RenderTableCell * elem2)1312 static inline bool compareCellPositions(RenderTableCell* elem1, RenderTableCell* elem2)
1313 {
1314     return elem1->rowIndex() < elem2->rowIndex();
1315 }
1316 
1317 // This comparison is used only when we have overflowing cells as we have an unsorted array to sort. We thus need
1318 // to sort both on rows and columns to properly repaint.
compareCellPositionsWithOverflowingCells(RenderTableCell * elem1,RenderTableCell * elem2)1319 static inline bool compareCellPositionsWithOverflowingCells(RenderTableCell* elem1, RenderTableCell* elem2)
1320 {
1321     if (elem1->rowIndex() != elem2->rowIndex())
1322         return elem1->rowIndex() < elem2->rowIndex();
1323 
1324     return elem1->col() < elem2->col();
1325 }
1326 
paintCell(RenderTableCell * cell,PaintInfo & paintInfo,const LayoutPoint & paintOffset)1327 void RenderTableSection::paintCell(RenderTableCell* cell, PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1328 {
1329     LayoutPoint cellPoint = flipForWritingModeForChild(cell, paintOffset);
1330     PaintPhase paintPhase = paintInfo.phase;
1331     RenderTableRow* row = toRenderTableRow(cell->parent());
1332 
1333     if (paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChildBlockBackground) {
1334         // We need to handle painting a stack of backgrounds.  This stack (from bottom to top) consists of
1335         // the column group, column, row group, row, and then the cell.
1336         RenderTableCol* column = table()->colElement(cell->col());
1337         RenderTableCol* columnGroup = column ? column->enclosingColumnGroup() : 0;
1338 
1339         // Column groups and columns first.
1340         // FIXME: Columns and column groups do not currently support opacity, and they are being painted "too late" in
1341         // the stack, since we have already opened a transparency layer (potentially) for the table row group.
1342         // Note that we deliberately ignore whether or not the cell has a layer, since these backgrounds paint "behind" the
1343         // cell.
1344         cell->paintBackgroundsBehindCell(paintInfo, cellPoint, columnGroup);
1345         cell->paintBackgroundsBehindCell(paintInfo, cellPoint, column);
1346 
1347         // Paint the row group next.
1348         cell->paintBackgroundsBehindCell(paintInfo, cellPoint, this);
1349 
1350         // Paint the row next, but only if it doesn't have a layer.  If a row has a layer, it will be responsible for
1351         // painting the row background for the cell.
1352         if (!row->hasSelfPaintingLayer())
1353             cell->paintBackgroundsBehindCell(paintInfo, cellPoint, row);
1354     }
1355     if ((!cell->hasSelfPaintingLayer() && !row->hasSelfPaintingLayer()))
1356         cell->paint(paintInfo, cellPoint);
1357 }
1358 
logicalRectForWritingModeAndDirection(const LayoutRect & rect) const1359 LayoutRect RenderTableSection::logicalRectForWritingModeAndDirection(const LayoutRect& rect) const
1360 {
1361     LayoutRect tableAlignedRect(rect);
1362 
1363     flipForWritingMode(tableAlignedRect);
1364 
1365     if (!style()->isHorizontalWritingMode())
1366         tableAlignedRect = tableAlignedRect.transposedRect();
1367 
1368     const Vector<int>& columnPos = table()->columnPositions();
1369     // FIXME: The table's direction should determine our row's direction, not the section's (see bug 96691).
1370     if (!style()->isLeftToRightDirection())
1371         tableAlignedRect.setX(columnPos[columnPos.size() - 1] - tableAlignedRect.maxX());
1372 
1373     return tableAlignedRect;
1374 }
1375 
dirtiedRows(const LayoutRect & damageRect) const1376 CellSpan RenderTableSection::dirtiedRows(const LayoutRect& damageRect) const
1377 {
1378     if (m_forceSlowPaintPathWithOverflowingCell)
1379         return fullTableRowSpan();
1380 
1381     CellSpan coveredRows = spannedRows(damageRect);
1382 
1383     // To repaint the border we might need to repaint first or last row even if they are not spanned themselves.
1384     if (coveredRows.start() >= m_rowPos.size() - 1 && m_rowPos[m_rowPos.size() - 1] + table()->outerBorderAfter() >= damageRect.y())
1385         --coveredRows.start();
1386 
1387     if (!coveredRows.end() && m_rowPos[0] - table()->outerBorderBefore() <= damageRect.maxY())
1388         ++coveredRows.end();
1389 
1390     return coveredRows;
1391 }
1392 
dirtiedColumns(const LayoutRect & damageRect) const1393 CellSpan RenderTableSection::dirtiedColumns(const LayoutRect& damageRect) const
1394 {
1395     if (m_forceSlowPaintPathWithOverflowingCell)
1396         return fullTableColumnSpan();
1397 
1398     CellSpan coveredColumns = spannedColumns(damageRect);
1399 
1400     const Vector<int>& columnPos = table()->columnPositions();
1401     // To repaint the border we might need to repaint first or last column even if they are not spanned themselves.
1402     if (coveredColumns.start() >= columnPos.size() - 1 && columnPos[columnPos.size() - 1] + table()->outerBorderEnd() >= damageRect.x())
1403         --coveredColumns.start();
1404 
1405     if (!coveredColumns.end() && columnPos[0] - table()->outerBorderStart() <= damageRect.maxX())
1406         ++coveredColumns.end();
1407 
1408     return coveredColumns;
1409 }
1410 
spannedRows(const LayoutRect & flippedRect) const1411 CellSpan RenderTableSection::spannedRows(const LayoutRect& flippedRect) const
1412 {
1413     // Find the first row that starts after rect top.
1414     unsigned nextRow = std::upper_bound(m_rowPos.begin(), m_rowPos.end(), flippedRect.y()) - m_rowPos.begin();
1415 
1416     if (nextRow == m_rowPos.size())
1417         return CellSpan(m_rowPos.size() - 1, m_rowPos.size() - 1); // After all rows.
1418 
1419     unsigned startRow = nextRow > 0 ? nextRow - 1 : 0;
1420 
1421     // Find the first row that starts after rect bottom.
1422     unsigned endRow;
1423     if (m_rowPos[nextRow] >= flippedRect.maxY())
1424         endRow = nextRow;
1425     else {
1426         endRow = std::upper_bound(m_rowPos.begin() + nextRow, m_rowPos.end(), flippedRect.maxY()) - m_rowPos.begin();
1427         if (endRow == m_rowPos.size())
1428             endRow = m_rowPos.size() - 1;
1429     }
1430 
1431     return CellSpan(startRow, endRow);
1432 }
1433 
spannedColumns(const LayoutRect & flippedRect) const1434 CellSpan RenderTableSection::spannedColumns(const LayoutRect& flippedRect) const
1435 {
1436     const Vector<int>& columnPos = table()->columnPositions();
1437 
1438     // Find the first column that starts after rect left.
1439     // lower_bound doesn't handle the edge between two cells properly as it would wrongly return the
1440     // cell on the logical top/left.
1441     // upper_bound on the other hand properly returns the cell on the logical bottom/right, which also
1442     // matches the behavior of other browsers.
1443     unsigned nextColumn = std::upper_bound(columnPos.begin(), columnPos.end(), flippedRect.x()) - columnPos.begin();
1444 
1445     if (nextColumn == columnPos.size())
1446         return CellSpan(columnPos.size() - 1, columnPos.size() - 1); // After all columns.
1447 
1448     unsigned startColumn = nextColumn > 0 ? nextColumn - 1 : 0;
1449 
1450     // Find the first column that starts after rect right.
1451     unsigned endColumn;
1452     if (columnPos[nextColumn] >= flippedRect.maxX())
1453         endColumn = nextColumn;
1454     else {
1455         endColumn = std::upper_bound(columnPos.begin() + nextColumn, columnPos.end(), flippedRect.maxX()) - columnPos.begin();
1456         if (endColumn == columnPos.size())
1457             endColumn = columnPos.size() - 1;
1458     }
1459 
1460     return CellSpan(startColumn, endColumn);
1461 }
1462 
1463 
paintObject(PaintInfo & paintInfo,const LayoutPoint & paintOffset)1464 void RenderTableSection::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1465 {
1466     PaintPhase paintPhase = paintInfo.phase;
1467 
1468     LayoutRect localRepaintRect = paintInfo.rect;
1469     localRepaintRect.moveBy(-paintOffset);
1470     localRepaintRect.inflate(maximalOutlineSize(paintPhase));
1471 
1472     LayoutRect tableAlignedRect = logicalRectForWritingModeAndDirection(localRepaintRect);
1473 
1474     CellSpan dirtiedRows = this->dirtiedRows(tableAlignedRect);
1475     CellSpan dirtiedColumns = this->dirtiedColumns(tableAlignedRect);
1476 
1477     if (dirtiedColumns.start() < dirtiedColumns.end()) {
1478         if (!m_hasMultipleCellLevels && !m_overflowingCells.size()) {
1479             if (paintInfo.phase == PaintPhaseCollapsedTableBorders) {
1480                 // Collapsed borders are painted from the bottom right to the top left so that precedence
1481                 // due to cell position is respected.
1482                 for (unsigned r = dirtiedRows.end(); r > dirtiedRows.start(); r--) {
1483                     unsigned row = r - 1;
1484                     for (unsigned c = dirtiedColumns.end(); c > dirtiedColumns.start(); c--) {
1485                         unsigned col = c - 1;
1486                         CellStruct& current = cellAt(row, col);
1487                         RenderTableCell* cell = current.primaryCell();
1488                         if (!cell || (row > dirtiedRows.start() && primaryCellAt(row - 1, col) == cell) || (col > dirtiedColumns.start() && primaryCellAt(row, col - 1) == cell))
1489                             continue;
1490                         LayoutPoint cellPoint = flipForWritingModeForChild(cell, paintOffset);
1491                         cell->paintCollapsedBorders(paintInfo, cellPoint);
1492                     }
1493                 }
1494             } else {
1495                 // Draw the dirty cells in the order that they appear.
1496                 for (unsigned r = dirtiedRows.start(); r < dirtiedRows.end(); r++) {
1497                     RenderTableRow* row = m_grid[r].rowRenderer;
1498                     if (row && !row->hasSelfPaintingLayer())
1499                         row->paintOutlineForRowIfNeeded(paintInfo, paintOffset);
1500                     for (unsigned c = dirtiedColumns.start(); c < dirtiedColumns.end(); c++) {
1501                         CellStruct& current = cellAt(r, c);
1502                         RenderTableCell* cell = current.primaryCell();
1503                         if (!cell || (r > dirtiedRows.start() && primaryCellAt(r - 1, c) == cell) || (c > dirtiedColumns.start() && primaryCellAt(r, c - 1) == cell))
1504                             continue;
1505                         paintCell(cell, paintInfo, paintOffset);
1506                     }
1507                 }
1508             }
1509         } else {
1510             // The overflowing cells should be scarce to avoid adding a lot of cells to the HashSet.
1511 #ifndef NDEBUG
1512             unsigned totalRows = m_grid.size();
1513             unsigned totalCols = table()->columns().size();
1514             ASSERT(m_overflowingCells.size() < totalRows * totalCols * gMaxAllowedOverflowingCellRatioForFastPaintPath);
1515 #endif
1516 
1517             // To make sure we properly repaint the section, we repaint all the overflowing cells that we collected.
1518             Vector<RenderTableCell*> cells;
1519             copyToVector(m_overflowingCells, cells);
1520 
1521             HashSet<RenderTableCell*> spanningCells;
1522 
1523             for (unsigned r = dirtiedRows.start(); r < dirtiedRows.end(); r++) {
1524                 RenderTableRow* row = m_grid[r].rowRenderer;
1525                 if (row && !row->hasSelfPaintingLayer())
1526                     row->paintOutlineForRowIfNeeded(paintInfo, paintOffset);
1527                 for (unsigned c = dirtiedColumns.start(); c < dirtiedColumns.end(); c++) {
1528                     CellStruct& current = cellAt(r, c);
1529                     if (!current.hasCells())
1530                         continue;
1531                     for (unsigned i = 0; i < current.cells.size(); ++i) {
1532                         if (m_overflowingCells.contains(current.cells[i]))
1533                             continue;
1534 
1535                         if (current.cells[i]->rowSpan() > 1 || current.cells[i]->colSpan() > 1) {
1536                             if (!spanningCells.add(current.cells[i]).isNewEntry)
1537                                 continue;
1538                         }
1539 
1540                         cells.append(current.cells[i]);
1541                     }
1542                 }
1543             }
1544 
1545             // Sort the dirty cells by paint order.
1546             if (!m_overflowingCells.size())
1547                 std::stable_sort(cells.begin(), cells.end(), compareCellPositions);
1548             else
1549                 std::sort(cells.begin(), cells.end(), compareCellPositionsWithOverflowingCells);
1550 
1551             if (paintInfo.phase == PaintPhaseCollapsedTableBorders) {
1552                 for (unsigned i = cells.size(); i > 0; --i) {
1553                     LayoutPoint cellPoint = flipForWritingModeForChild(cells[i - 1], paintOffset);
1554                     cells[i - 1]->paintCollapsedBorders(paintInfo, cellPoint);
1555                 }
1556             } else {
1557                 for (unsigned i = 0; i < cells.size(); ++i)
1558                     paintCell(cells[i], paintInfo, paintOffset);
1559             }
1560         }
1561     }
1562 }
1563 
imageChanged(WrappedImagePtr,const IntRect *)1564 void RenderTableSection::imageChanged(WrappedImagePtr, const IntRect*)
1565 {
1566     // FIXME: Examine cells and repaint only the rect the image paints in.
1567     repaint();
1568 }
1569 
recalcCells()1570 void RenderTableSection::recalcCells()
1571 {
1572     ASSERT(m_needsCellRecalc);
1573     // We reset the flag here to ensure that |addCell| works. This is safe to do as
1574     // fillRowsWithDefaultStartingAtPosition makes sure we match the table's columns
1575     // representation.
1576     m_needsCellRecalc = false;
1577 
1578     m_cCol = 0;
1579     m_cRow = 0;
1580     m_grid.clear();
1581 
1582     for (RenderObject* row = firstChild(); row; row = row->nextSibling()) {
1583         if (row->isTableRow()) {
1584             unsigned insertionRow = m_cRow;
1585             m_cRow++;
1586             m_cCol = 0;
1587             ensureRows(m_cRow);
1588 
1589             RenderTableRow* tableRow = toRenderTableRow(row);
1590             m_grid[insertionRow].rowRenderer = tableRow;
1591             tableRow->setRowIndex(insertionRow);
1592             setRowLogicalHeightToRowStyleLogicalHeight(m_grid[insertionRow]);
1593 
1594             for (RenderObject* cell = row->firstChild(); cell; cell = cell->nextSibling()) {
1595                 if (!cell->isTableCell())
1596                     continue;
1597 
1598                 RenderTableCell* tableCell = toRenderTableCell(cell);
1599                 addCell(tableCell, tableRow);
1600             }
1601         }
1602     }
1603 
1604     m_grid.shrinkToFit();
1605     setNeedsLayout();
1606 }
1607 
1608 // FIXME: This function could be made O(1) in certain cases (like for the non-most-constrainive cells' case).
rowLogicalHeightChanged(unsigned rowIndex)1609 void RenderTableSection::rowLogicalHeightChanged(unsigned rowIndex)
1610 {
1611     if (needsCellRecalc())
1612         return;
1613 
1614     setRowLogicalHeightToRowStyleLogicalHeight(m_grid[rowIndex]);
1615 
1616     for (RenderObject* cell = m_grid[rowIndex].rowRenderer->firstChild(); cell; cell = cell->nextSibling()) {
1617         if (!cell->isTableCell())
1618             continue;
1619 
1620         updateLogicalHeightForCell(m_grid[rowIndex], toRenderTableCell(cell));
1621     }
1622 }
1623 
setNeedsCellRecalc()1624 void RenderTableSection::setNeedsCellRecalc()
1625 {
1626     m_needsCellRecalc = true;
1627     if (RenderTable* t = table())
1628         t->setNeedsSectionRecalc();
1629 }
1630 
numColumns() const1631 unsigned RenderTableSection::numColumns() const
1632 {
1633     unsigned result = 0;
1634 
1635     for (unsigned r = 0; r < m_grid.size(); ++r) {
1636         for (unsigned c = result; c < table()->numEffCols(); ++c) {
1637             const CellStruct& cell = cellAt(r, c);
1638             if (cell.hasCells() || cell.inColSpan)
1639                 result = c;
1640         }
1641     }
1642 
1643     return result + 1;
1644 }
1645 
borderAdjoiningStartCell(const RenderTableCell * cell) const1646 const BorderValue& RenderTableSection::borderAdjoiningStartCell(const RenderTableCell* cell) const
1647 {
1648     ASSERT(cell->isFirstOrLastCellInRow());
1649     return hasSameDirectionAs(cell) ? style()->borderStart() : style()->borderEnd();
1650 }
1651 
borderAdjoiningEndCell(const RenderTableCell * cell) const1652 const BorderValue& RenderTableSection::borderAdjoiningEndCell(const RenderTableCell* cell) const
1653 {
1654     ASSERT(cell->isFirstOrLastCellInRow());
1655     return hasSameDirectionAs(cell) ? style()->borderEnd() : style()->borderStart();
1656 }
1657 
firstRowCellAdjoiningTableStart() const1658 const RenderTableCell* RenderTableSection::firstRowCellAdjoiningTableStart() const
1659 {
1660     unsigned adjoiningStartCellColumnIndex = hasSameDirectionAs(table()) ? 0 : table()->lastColumnIndex();
1661     return cellAt(0, adjoiningStartCellColumnIndex).primaryCell();
1662 }
1663 
firstRowCellAdjoiningTableEnd() const1664 const RenderTableCell* RenderTableSection::firstRowCellAdjoiningTableEnd() const
1665 {
1666     unsigned adjoiningEndCellColumnIndex = hasSameDirectionAs(table()) ? table()->lastColumnIndex() : 0;
1667     return cellAt(0, adjoiningEndCellColumnIndex).primaryCell();
1668 }
1669 
appendColumn(unsigned pos)1670 void RenderTableSection::appendColumn(unsigned pos)
1671 {
1672     ASSERT(!m_needsCellRecalc);
1673 
1674     for (unsigned row = 0; row < m_grid.size(); ++row)
1675         m_grid[row].row.resize(pos + 1);
1676 }
1677 
splitColumn(unsigned pos,unsigned first)1678 void RenderTableSection::splitColumn(unsigned pos, unsigned first)
1679 {
1680     ASSERT(!m_needsCellRecalc);
1681 
1682     if (m_cCol > pos)
1683         m_cCol++;
1684     for (unsigned row = 0; row < m_grid.size(); ++row) {
1685         Row& r = m_grid[row].row;
1686         r.insert(pos + 1, CellStruct());
1687         if (r[pos].hasCells()) {
1688             r[pos + 1].cells.append(r[pos].cells);
1689             RenderTableCell* cell = r[pos].primaryCell();
1690             ASSERT(cell);
1691             ASSERT(cell->colSpan() >= (r[pos].inColSpan ? 1u : 0));
1692             unsigned colleft = cell->colSpan() - r[pos].inColSpan;
1693             if (first > colleft)
1694               r[pos + 1].inColSpan = 0;
1695             else
1696               r[pos + 1].inColSpan = first + r[pos].inColSpan;
1697         } else {
1698             r[pos + 1].inColSpan = 0;
1699         }
1700     }
1701 }
1702 
1703 // Hit Testing
nodeAtPoint(const HitTestRequest & request,HitTestResult & result,const HitTestLocation & locationInContainer,const LayoutPoint & accumulatedOffset,HitTestAction action)1704 bool RenderTableSection::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction action)
1705 {
1706     // If we have no children then we have nothing to do.
1707     if (!firstChild())
1708         return false;
1709 
1710     // Table sections cannot ever be hit tested.  Effectively they do not exist.
1711     // Just forward to our children always.
1712     LayoutPoint adjustedLocation = accumulatedOffset + location();
1713 
1714     if (hasOverflowClip() && !locationInContainer.intersects(overflowClipRect(adjustedLocation, locationInContainer.region())))
1715         return false;
1716 
1717     if (hasOverflowingCell()) {
1718         for (RenderObject* child = lastChild(); child; child = child->previousSibling()) {
1719             // FIXME: We have to skip over inline flows, since they can show up inside table rows
1720             // at the moment (a demoted inline <form> for example). If we ever implement a
1721             // table-specific hit-test method (which we should do for performance reasons anyway),
1722             // then we can remove this check.
1723             if (child->isBox() && !toRenderBox(child)->hasSelfPaintingLayer()) {
1724                 LayoutPoint childPoint = flipForWritingModeForChild(toRenderBox(child), adjustedLocation);
1725                 if (child->nodeAtPoint(request, result, locationInContainer, childPoint, action)) {
1726                     updateHitTestResult(result, toLayoutPoint(locationInContainer.point() - childPoint));
1727                     return true;
1728                 }
1729             }
1730         }
1731         return false;
1732     }
1733 
1734     recalcCellsIfNeeded();
1735 
1736     LayoutRect hitTestRect = locationInContainer.boundingBox();
1737     hitTestRect.moveBy(-adjustedLocation);
1738 
1739     LayoutRect tableAlignedRect = logicalRectForWritingModeAndDirection(hitTestRect);
1740     CellSpan rowSpan = spannedRows(tableAlignedRect);
1741     CellSpan columnSpan = spannedColumns(tableAlignedRect);
1742 
1743     // Now iterate over the spanned rows and columns.
1744     for (unsigned hitRow = rowSpan.start(); hitRow < rowSpan.end(); ++hitRow) {
1745         for (unsigned hitColumn = columnSpan.start(); hitColumn < columnSpan.end(); ++hitColumn) {
1746             CellStruct& current = cellAt(hitRow, hitColumn);
1747 
1748             // If the cell is empty, there's nothing to do
1749             if (!current.hasCells())
1750                 continue;
1751 
1752             for (unsigned i = current.cells.size() ; i; ) {
1753                 --i;
1754                 RenderTableCell* cell = current.cells[i];
1755                 LayoutPoint cellPoint = flipForWritingModeForChild(cell, adjustedLocation);
1756                 if (static_cast<RenderObject*>(cell)->nodeAtPoint(request, result, locationInContainer, cellPoint, action)) {
1757                     updateHitTestResult(result, locationInContainer.point() - toLayoutSize(cellPoint));
1758                     return true;
1759                 }
1760             }
1761             if (!result.isRectBasedTest())
1762                 break;
1763         }
1764         if (!result.isRectBasedTest())
1765             break;
1766     }
1767 
1768     return false;
1769 }
1770 
removeCachedCollapsedBorders(const RenderTableCell * cell)1771 void RenderTableSection::removeCachedCollapsedBorders(const RenderTableCell* cell)
1772 {
1773     if (!table()->collapseBorders())
1774         return;
1775 
1776     for (int side = CBSBefore; side <= CBSEnd; ++side)
1777         m_cellsCollapsedBorders.remove(make_pair(cell, side));
1778 }
1779 
setCachedCollapsedBorder(const RenderTableCell * cell,CollapsedBorderSide side,CollapsedBorderValue border)1780 void RenderTableSection::setCachedCollapsedBorder(const RenderTableCell* cell, CollapsedBorderSide side, CollapsedBorderValue border)
1781 {
1782     ASSERT(table()->collapseBorders());
1783     m_cellsCollapsedBorders.set(make_pair(cell, side), border);
1784 }
1785 
cachedCollapsedBorder(const RenderTableCell * cell,CollapsedBorderSide side)1786 CollapsedBorderValue& RenderTableSection::cachedCollapsedBorder(const RenderTableCell* cell, CollapsedBorderSide side)
1787 {
1788     ASSERT(table()->collapseBorders());
1789     HashMap<pair<const RenderTableCell*, int>, CollapsedBorderValue>::iterator it = m_cellsCollapsedBorders.find(make_pair(cell, side));
1790     ASSERT_WITH_SECURITY_IMPLICATION(it != m_cellsCollapsedBorders.end());
1791     return it->value;
1792 }
1793 
createAnonymousWithParentRenderer(const RenderObject * parent)1794 RenderTableSection* RenderTableSection::createAnonymousWithParentRenderer(const RenderObject* parent)
1795 {
1796     RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyleWithDisplay(parent->style(), TABLE_ROW_GROUP);
1797     RenderTableSection* newSection = new RenderTableSection(0);
1798     newSection->setDocumentForAnonymous(&parent->document());
1799     newSection->setStyle(newStyle.release());
1800     return newSection;
1801 }
1802 
setLogicalPositionForCell(RenderTableCell * cell,unsigned effectiveColumn) const1803 void RenderTableSection::setLogicalPositionForCell(RenderTableCell* cell, unsigned effectiveColumn) const
1804 {
1805     LayoutPoint oldCellLocation = cell->location();
1806 
1807     LayoutPoint cellLocation(0, m_rowPos[cell->rowIndex()]);
1808     int horizontalBorderSpacing = table()->hBorderSpacing();
1809 
1810     // FIXME: The table's direction should determine our row's direction, not the section's (see bug 96691).
1811     if (!style()->isLeftToRightDirection())
1812         cellLocation.setX(table()->columnPositions()[table()->numEffCols()] - table()->columnPositions()[table()->colToEffCol(cell->col() + cell->colSpan())] + horizontalBorderSpacing);
1813     else
1814         cellLocation.setX(table()->columnPositions()[effectiveColumn] + horizontalBorderSpacing);
1815 
1816     cell->setLogicalLocation(cellLocation);
1817     view()->addLayoutDelta(oldCellLocation - cell->location());
1818 }
1819 
1820 } // namespace WebCore
1821