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