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