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