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