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