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